首页
社区
课程
招聘
[原创]如何开始学习Windows逆向(虽已是夕阳)
2022-8-17 23:09 19852

[原创]如何开始学习Windows逆向(虽已是夕阳)

2022-8-17 23:09
19852

如何开始学习Windows逆向(虽已是夕阳)

QQ群:935394656欢迎各位逆向手子们加入(钓鱼佬别来)

1. 熟练掌握相应工具(工欲善其事,必先利其器).x96dbg\IDA Pro\Windbg X\Cheats engine\...(还有很多)

工具的使用教程网上太多了,这里就不一一赘述了.

 

此处省略千言万语...

1.1 笔记真很重要

我们碰到问题去百度 google的时候搜出来的大部分都是别人的博客笔记.
养成记录学习的过程是一个好习惯.

2. 熟悉汇编指令与C/C++相关语法

第一步与第二步可以并行学习,可以自写自逆(自己编写Demo然后去使用IDA与x96dbg分析,前期不要加载(生成)PDB)的方式从而掌握相关工具的使用与汇编的初识.

先从C语言语法开始一边写一边看汇编对应代码,从而加深对代码与汇编印象.

最开始的学习过程中不要写C++代码与使用STL等等库

最开始的学习过程中不要写C++代码与使用STL等等库

最开始的学习过程中不要写C++代码与使用STL等等库

重要的事情说三遍!

为什么呢?如果在最开始就尝试去分析C++与STL大部分是要劝退了

分析C++最好是加载PDB从而更好的理解C++代码编译后的一些汇编特性.

3. 开始实战Crack Me 160案例或者任意单机游戏or网游(最好挑一个软柿子无保护的)

此阶段是从逆向分析角度去了解Windows API,数据结构,设计模式,加密解密.

为什么推荐选择游戏或者CrackMe呢,此类程序都可以实质性肉眼可见反馈成果出来提升学习动力"向银手镯更进一步".

逆向的学习没有速成.

学习过程较为漫长需要大量分析慢慢积累经验.

4.实战LostArk(失落的方舟)人物基础信息解密

前言:

分析游戏一般是选择可见值可推测状态等等相关回馈信息着手.不少游戏都会采取数值加密,尽可能的隐藏内存中可见值来妨碍肉眼分析过程.

因为LostArk是UE3Unreal Engine引擎开发,所以相关词汇采用UE引擎为准(其他引擎也是大同小异).

3D游戏的图形世界中我们可操控的对象(LocalPlayer)由两个部分组成CameraModelObject绑定共同构成.

其中相机是隐藏的不是真实存在的模型实体,只有模型会反馈给玩家从而绘制在屏幕上.

PS:我在BB些什么呢

开始实战讲解部分:

掏出咋们的万能工具Cheat Engine对着可见血量进行4字节一顿筛查搜索(CE的使用可以B站搜索视频学习有很多UP主都出过)最后筛查出最后两个.

从上面我可以看出来两个地址非常的近 很明显是当前血与最大血值的存储地址

???不会吧不会吧这么简单?

好了看一下有那些代码在访问此地址.

下访问断点后我们可以发现只有改变血量才会有访问代码

很明显两个访问地址在一个函数内一个在上一个在下.切到反汇编窗口过去看看.

从上面的汇编代码可以看出在与CALL_2142D00的返回值进行了一些比较,然后再将返回值写入R15+4F0(这里其实就一眼便知CALL_2142D00返回值才是真正的血量来源).

仔细观察代码与R15对象结构下的其他信息就能发现并不是人物自身对象,而是UI标签(血量是解密以后填充到UI标签中让我们可以在UI上看见真实血量值).

分析0x2142D00这个CALL,首先确定参数,从CALL外部来看有两个参数,RCX是一个人物对象,RDX是一个索引.然后继续观察CALL内部的过程.

进来CALL内部可以看到RCX+0x34紧接着将dl也就是索引拷贝给ebx.继续跟进去看CALL_EA7DA0.

这个CALL就是解密人物对象自身信息关键代码了,很明显是异或加密了对象信息数值.

省略掉基址的查找了,大家可以自己在CALL_2142D00往上跟一下就能看到基址来源.

我们打印一下当前对象所有信息看看都是什么.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void DumpData()
{
    uint64_t info = 0;
    uint64_t result = 0, address = 0; // rax
    uint64_t InfoTmp = mem.RPM<uint64_t>(mem.BaseAddrss + 0x447FE38);
    InfoTmp = mem.RPM<uint64_t>(InfoTmp + 0xDC) + 0x34;
    for (size_t i = 0; i < 0x98; i++)
    {
        //info = DecryptObjInfo(InfoTmp, i);
        if (mem.RPM<uint32_t>(InfoTmp + 0x5A8) > 1)
            result = mem.RPM<uint8_t>(i + InfoTmp + 0x510);
        info = mem.RPM<uint64_t>(InfoTmp + 8 * (int)result) ^ mem.RPM<uint64_t>(InfoTmp + 8 * i + 0x50);
        printf("Address:%llX\t Info:%-4d\r\n",
            InfoTmp + 8 * i + 0x50,
            info);
    }
}

其实想偷懒的话直接IDA F5就好了 F5的结果并不是一定准确只能作为伪代码参考(不过我是直接C+V舒服了)

然后修改一下代码成为写入加密数值.思密达的游戏特色很多数据放在本地判断.

修改攻速搭配蓄力技能让游戏体验加倍增长~~~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void DecryptObjInfo(int Index, int value)
{
    uint64_t result = 0, address = 0, info = 0; // rax
    uint64_t InfoTmp = mem.RPM<uint64_t>(mem.BaseAddrss + 0x447FE38);
    InfoTmp = mem.RPM<uint64_t>(InfoTmp + 0xDC) + 0x34;
    if (mem.RPM<uint32_t>(InfoTmp + 0x5A8) > 1)
        result = mem.RPM<uint8_t>(Index + InfoTmp + 0x510);
    info = mem.RPM<uint64_t>(InfoTmp + 8 * (int)result) ^ mem.RPM<uint64_t>(InfoTmp + 8 * Index + 0x50);
    if (info != value) {
        int Decvalue = mem.RPM<uint64_t>(InfoTmp + 8 * (int)result) ^ value;//取出当前加密值跟与写入的值进行异或
        mem.WPM<uint64_t>(InfoTmp + 8 * Index + 0x50, Decvalue);
    }
}
 
// 1是当前血
// 27是最大血
// 2是当前蓝
// 28是最大蓝
// 77是攻速
// 还有一些其他信息可以自行打印观看
DecryptObjInfo(77,500);//500 == 500%

此处结构只有人物的基础信息并非完整的存在.真正的人物对象在另外一个结构通过一个ID跟此结构进行关联.还是比较少见的分开存储的方式可能不止一种方式去获取其他的方法没有去深入观察.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//写法拉跨大家凑合看
int ARKObject::GetObjinfovalue(uint64_t UId, uint64_t Index)
{
    if (this->GetObjType() == ObjType::Moster || this->GetObjType() == ObjType::Role)
    {
        auto uBase = mem.RPM<uint64_t>(g_BaseModule + GlobalBaseInfo);
        auto size = mem.RPM<uint32_t>(uBase + 0x9C);
        uBase = mem.RPM<uint64_t>(uBase + 0x94);
        for (size_t i = 0; i < size; i++)
        {
            uint64_t EFUId = mem.RPM<uint64_t>(uBase + 0x18 * i);
            if (EFUId == UId) {
                //解密从上面的函数修改而来
                return DecryptObjInfo(mem.RPM<uint64_t>(uBase + 0x18 * i + 8), Index);
            }
        }
    }
    return 0;
}

5.Windows内核调试与逆向

VirtualKD-3.0

微软提供了非常强大的调试工具Windbg(新版本是WindbgX).不废话了直接上手搭建双机调试的环境吧.

推荐环境为:VMWare+VirtualKD-3.0(此工具需要去Github上下载一个最新版本才能支持最新VMWare16等新版本)这样搭建双机调试不容易出错,也很简单.

VMWare安装系统步骤就省略了.

系统安装完成以后将下载好的VirtualKD-3.0\target64或者VirtualKD-3.0\target32你的目标虚拟机系统是多少位就复制相对应到C盘根目录.

完成上述步骤以后就可以直接在虚拟机中运行vminstall.exe点击Install按钮.

系统会被强制重启,出现以下画面.

Win10或更高需要禁用强制驱动签名才能完整跑起来VirtualKD.这个暂时不确定是我个人问题还是此工具问题

然后我们再配置本机的VirtualKD-3.0的设置.本机系统是多少位就运行对应的vmmon32.exevmmon64.exe.

现在的新版本支持选择Windbg Preview也就是WindbgX.直接选择对应目录打开对应的exe就好了.

老版本的需要选择Curstom配置:你的工具目录\windbgx1.27DbgX.Shell.exe /k com:pipe,resets=0,reconnect,port=$(pipename) -y "cache*D:\localsymbols;srv*https://msdl.microsoft.com/download/symbols;"

还有你的symbols也就是PDB选择保存在那个目录上面.PDB的下载问题需要科学上网解决

可能有的时候会出现一个问题全部都配置好了.但是发现还是无法双机调试启动成功.

出现这个情况需要检查虚拟机的调试模式是否开启.

cmd中输入msconfig查看

强烈推荐巧妙运用VMware的快照功能,初始化环境后将需要的工具拷贝进虚拟机,然后可以备份一个快照.防止出情况后续再重新配置.

到这里基本配置就OK了.

接下就是IDA与WindbgX搭配进行静态分析与动态调试了.

我这边举例一个保护进程的功能,也是现在流传最为广泛的一个.

我们先把虚拟机的ntoskrnl.exe搞出来用IDA静态分析一手.

IDA也需要设置一下PDB的下载与加载问题.打开IDA的根目录找到IDA Pro\cfg\pdb.cfg打开后找到_NT_SYMBOL_PATH = "SRV*D:\\localsymbols*http://msdl.microsoft.com/download/symbols";进行设置即可.记得换成自己的目录

把ntos丢进IDA让他自动跑完分析保存成.i64文件即可.分析其他内核模块也是差不多这个步骤

开始我们的调试内核之旅:

在虚拟机中打开一个记事本,再使用WindbgX去暂停虚拟机.

再命令行输入:!process 0 0 notepad.exe

出现以下信息

PROCESS:为进程对应的内核EPROCESS对象首地址.

1
2
3
4
PROCESS ffffca83623e8080
SessionId: 1  Cid: 17d4    Peb: 4d6ad51000  ParentCid: 0ee8
DirBase: 3532e002  ObjectTable: ffff9e829eca2980  HandleCount: 282.
Image: notepad.exe

拿到EPROCESS以后呢.直接按照-0x30其实就是EPROCESS地址-sizeof(_OBJECT_HEADER)方法去拿_OBJECT_HEADER结构所在位置.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
0: kd> dt _object_header ffffca83623e8080-0x30
nt!_OBJECT_HEADER
+0x000 PointerCount     : 0n196247
+0x008 HandleCount      : 0n6
+0x008 NextToFree       : 0x00000000`00000006 Void
+0x010 Lock             : _EX_PUSH_LOCK
+0x018 TypeIndex        : 0xb4 ''
+0x019 TraceFlags       : 0 ''
+0x019 DbgRefTrace      : 0y0
+0x019 DbgTracePermanent : 0y0
+0x01a InfoMask         : 0x88 ''
+0x01b Flags            : 0 ''
+0x01b NewObject        : 0y0
+0x01b KernelObject     : 0y0
+0x01b KernelOnlyAccess : 0y0
+0x01b ExclusiveObject  : 0y0
+0x01b PermanentObject  : 0y0
+0x01b DefaultSecurityQuota : 0y0
+0x01b SingleHandleEntry : 0y0
+0x01b DeletedInline    : 0y0
+0x01c Reserved         : 0
+0x020 ObjectCreateInfo : 0xffffca83`605e58c0 _OBJECT_CREATE_INFORMATION
+0x020 QuotaBlockCharged : 0xffffca83`605e58c0 Void
+0x028 SecurityDescriptor : 0xffff9e82`a116906c Void
+0x030 Body             : _QUAD

+0x01b Flags关键就是这个flags了.现在普遍用的是写入4对应将+0x01b KernelObject : 0y0置为1.此方法源于ydark

还有一个就是看雪上发过的设置成退出标志位0x71.对应的即是如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
> 0: kd> eb ffffca83623e8080-0x30+0x1b 0x71
0: kd> dt _object_header ffffca83623e8080-0x30
nt!_OBJECT_HEADER
   ....
   +0x01b Flags            : 0x71 'q'
   +0x01b NewObject        : 0y1
   +0x01b KernelObject     : 0y0
   +0x01b KernelOnlyAccess : 0y0
   +0x01b ExclusiveObject  : 0y0
   +0x01b PermanentObject  : 0y1
   +0x01b DefaultSecurityQuota : 0y1
   +0x01b SingleHandleEntry : 0y1
   +0x01b DeletedInline    : 0y0
   ....

他们是怎么确定退出标志位是0x71呢?调试吧?

那我们也调试一下吧.

ffffca83623e8080-0x30+0x1b下写入断点

1
2
3
0: kd> ba w1 ffffca83623e8080-0x30+0x1b
0: kd> bl
     0 e Disable Clear  ffffca83`623e806b w 1 0001 (0001)

退出进程并没有任何反应???是我操作不对吗.我在尝试一次.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
> 0: kd> ba w4 ffffca8363233080-0x30+0x1b
Data breakpoint must be aligned
                                      ^ Syntax error in 'ba w4 ffffca8363233080-0x30+0x1b'
0: kd> ba w8 ffffca8363233080-0x30+0x1b
Data breakpoint must be aligned
                                      ^ Syntax error in 'ba w8 ffffca8363233080-0x30+0x1b'
 
0: kd> ba w8 ffffca8363233080-0x30+0x18
0: kd> g
 
 
//移除对象了从偏移来看也就这里有点关系了
nt!ObpRemoveObjectRoutine+0x70:
fffff803`6e283cec 804b1b80        or      byte ptr [rbx+1Bh],80h
fffff803`6e283cf0 488b8788000000  mov     rax,qword ptr [rdi+88h]
 
 
//看API的名字也知道释放对象了 偏移位置不对操作的也是byte
nt!ObpFreeObject+0x17d:
fffff803`6e283ed9 40887b18           mov     byte ptr [rbx+18h], dil:2
fffff803`6e283edd 4885ed             test    rbp, rbp
 
 
 
//到这个API出现基本就没我们目前什么事了说明已经被清理了
nt!memset+0xb3:
fffff803`6e01b433 0f2941e0        movaps  xmmword ptr [rcx-20h],xmm0
 
//到这个API出现基本就没我们目前什么事了说明已经被清理了
nt!MiClearPteAccessed+0x6de:
fffff803`6de5675e ff470c          inc     dword ptr [rdi+0Ch]

Data breakpoint must be aligned数据断点必须要对齐咯.那咱们就从0x18下8字节写入.

所以网上的0x71到底从何而来呢,目前还是不知道呢.估计是根据对象结构去自行测试出来的吧.

还有就是此方法只针对R3的Openprocess有效驱动直接用PsLookupProcessByProcessId获取EProcess完全没问题.

所以这个保护进程其实也就是和置位EProcess+0x87a Protection : _PS_PROTECTION差不多一个效果.

好了动态调试既然没办法找到0x71这个写入的来源,接下来就可以使用IDA了.我们可以打开IDA分析好的ntos.i64文件了.

然后在IDA函数名字列表搜索OBJECT_为什么要搜索这个呢?结构体就叫这个呀.别问,问就单词联动+肉眼观察法

我们可以看到有四个OBJECT_HEADER_TO什么什么的.对我们来说关键的是TO_PROCESS因为分析的这个功能是进程保护.

然后我们双击转到这个函数的反汇编窗口直接F5是的就这么直接.有PDB加载还看锤子的汇编代码不要学我,大家好好学汇编.这不是一个好习惯.

这个函数啥啊就这么点东西,a1推测(IDA F5出来的结果不要过度相信依赖.因为是静态分析别说太绝对给自己留个退路不然容易啪啪打脸)应该是OBJECT_HEADER对象目前不是我们所需要关心的.因为这是Windows句柄表相关的知识这里就不多废话啦.我们主要的目的是去分析他们的0x71到底从何而来.

点击函数名字按X键交叉引用(xrefs)找到上层调用函数此函数有四处调用,分别来自两个函数.其中有一个函数调用了三次.

我们先看第一个双击跳过去.然后拉到函数起始也就是头部,开始一点点分析.

v8就是OBJECT_HEADER对象因为它有一个明显的-0x30特征存在,a4就是EPROCESS.我没有去N键重命名

既然知道了v8就是OBJECT_HEADER对象 那这个v8+27(十进制的0x1b)就是我们要找的Flags我们将鼠标点到27上去高亮.然后可以发现当前函数中有五处操作了v8+27并且都&按位与某个数.不同的数值其实就是对应着要操作的Flags的不同bit位

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
union                                                                       // 0x001B; 2 elements; 0x0001 Bytes
{
    UINT8                   Flags;                                          // 0x001B; 0x0001 Bytes
    struct                                                                  // 0x001B; 8 elements; 0x0001 Bytes
    {
        UINT8               NewObject                                  : 1; // 0x001B; Bit:   0
        UINT8               KernelObject                               : 1; // 0x001B; Bit:   1
        UINT8               KernelOnlyAccess                           : 1; // 0x001B; Bit:   2
        UINT8               ExclusiveObject                            : 1; // 0x001B; Bit:   3
        UINT8               PermanentObject                            : 1; // 0x001B; Bit:   4
        UINT8               DefaultSecurityQuota                       : 1; // 0x001B; Bit:   5
        UINT8               SingleHandleEntry                          : 1; // 0x001B; Bit:   6
        UINT8               DeletedInline                              : 1; // 0x001B; Bit:   7
    };
};

我上图只分析了第一个NewObject大家在学习的过程中可以去把下面的也分析了 大家就能得出来为什么他们有修改成4或者0x71了.

通过修改对应bit位判断让此函数内部判断失效返回错误值.拼凑出来了这个"0x71"

最后我再把这个函数的调用层级带大家一起看一下.

NtOpenPorcess->PsOpenProcess->ObOpenObjectByPointer->ObpCreateHandle->ObpIncrementHandleCountEx(我们返回到的函数)

上述调用链中的函数大部分过程都是很繁琐的即是IDA F5以后观看起来也很头皮发麻.

NtOpenPorcess要打开进程就要在请求进程自身创建一个Process的Handle 来存储着被打开的进程一些信息.

通过修改了Flags bit位的判断实现了让来自NtOpenPorcess内部调用链出现错误返回.从而达到了保护的目的.(这种方法只能防住R3的调用,驱动有驱动的玩法了后面有机会在BB)

到此就暂时结束了.近期内应该不会再新增其他内容了.

文中出现错误的话请及时指出 .


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2022-8-19 14:09 被如梦而醉编辑 ,原因: 1111
收藏
点赞8
打赏
分享
最新回复 (22)
雪    币: 1451
活跃值: (14614)
能力值: ( LV12,RANK:380 )
在线值:
发帖
回帖
粉丝
SSH山水画 3 2022-8-18 08:39
2
3
如何学习Windows逆向  ×
如何学习外挂开发   √
雪    币: 164
活跃值: (86)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
June0165 2022-8-18 09:30
3
0
如何学习Windows逆向  ×
如何学习外挂开发   √ 
雪    币: 2107
活跃值: (1429)
能力值: ( LV8,RANK:126 )
在线值:
发帖
回帖
粉丝
binlmmhc 2022-8-18 09:34
4
0
如何学习Windows逆向  ×
如何学习外挂开发   √
雪    币: 3624
活跃值: (3783)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 2022-8-18 09:51
5
0

如何学习Windows逆向  ×
如何学习外挂开发   √
雪    币: 3764
活跃值: (5485)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
huangjw 2022-8-18 09:52
6
0
如何学习Windows逆向  ×
如何学习制作游戏外挂开发   √
雪    币: 229
活跃值: (213252)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
shinratensei 1 2022-8-18 10:44
7
0
如何学习Windows逆向  ×
如何走向越来越刑的生活   √
雪    币: 1019
活跃值: (593)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
一个懵懂的SB 2022-8-19 17:32
8
0
如何学习Windows逆向  ×
如何走向越来越刑的生活   √
雪    币: 721
活跃值: (1511)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
目露不羁 2022-9-6 11:31
9
0
如何学习Windows逆向  ×
如何走向越来越刑的生活   √
雪    币: 1278
活跃值: (1265)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10ngvv 2022-9-6 12:29
10
0

如何学习Windows逆向  ×
如何走向越来越刑的生活   √

最后于 2022-9-6 12:29 被10ngvv编辑 ,原因:
雪    币: 220
活跃值: (33)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
蒋道理 2022-9-6 15:27
11
0
如何学习Windows逆向  ×
如何走向越来越刑的生活   √
雪    币: 10
活跃值: (74)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
方形大脸猫 2022-9-7 00:59
12
0
寄~~~~
雪    币: 16016
活跃值: (2012)
能力值: ( LV9,RANK:147 )
在线值:
发帖
回帖
粉丝
orz1ruo 2022-9-7 09:02
13
0
如何学习Windows逆向  ×
如何从零基础入门到深度入狱捡肥皂   √
雪    币: 1904
活跃值: (3316)
能力值: ( LV6,RANK:81 )
在线值:
发帖
回帖
粉丝
KingSelyF 1 2022-9-7 09:57
14
0
如何学习Windows逆向  ×
如何走向越来越刑的生活   √
雪    币: 1062
活跃值: (1297)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
厌倦 2022-9-8 16:22
15
0
如何学习Windows逆向  ×
如何走向越来越刑的生活   √
雪    币: 9644
活跃值: (3805)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Willarcap 2022-9-8 16:27
16
0
可刑。感谢分享~
雪    币: 377
活跃值: (1383)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kira_yamato 2022-9-14 09:59
17
0
真刑啊
雪    币: 3019
活跃值: (1699)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
935 2022-9-14 11:56
18
0
如何学习Windows逆向  ×
如何走向越来越刑的生活   √
雪    币: 622
活跃值: (1211)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
绝望的皮卡丘 2022-9-15 10:47
19
0
如何学习Windows逆向  ×
如何走向踩缝纫机的生活   √
雪    币: 887
活跃值: (1260)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Hbruce 2023-1-31 16:15
20
0
如何学习Windows逆向  ×
如何走向越来越刑的生活   √
雪    币: 336
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_higyywgo 2023-2-20 12:29
21
0
很好阿  稳健的我感觉逆向真好玩  哈哈
雪    币: 313
活跃值: (364)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
losemao 2023-2-26 22:52
22
0
windows 还没到夕阳,加油!
雪    币: 21
活跃值: (75)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
zngsai 2023-5-31 16:05
23
0
QQ群号码是无效的
游客
登录 | 注册 方可回帖
返回