好友
阅读权限30
听众
最后登录1970-1-1
|
本帖最后由 姐又寡闻了 于 2019-6-6 22:38 编辑
写一篇自己分析算法的一个过程,不对欢迎指正。
今天的程序是 Ahead DVD Ripper 年代好像有点久远,然而就从简单的算法来分析走起
PEID查壳一个VC++ 6.0,直接拖入OD 随意输入用户名与注册码 点击OK
是个MFC,到MFC42下按钮事件,来到关键算法CALL
[Asm] 纯文本查看 复制代码 00414156 . C74424 14 000>mov dword ptr ss:[esp+0x14],0x0
0041415E . E8 47D70500 call <jmp.&MFC42.#6334>
00414163 . E8 6AD60500 call <jmp.&MFC42.#1168>
00414168 . 8B48 04 mov ecx,dword ptr ds:[eax+0x4] ; Ahead_DV.00413FB0
0041416B . E8 0ADA0500 call <jmp.&MFC42.#1669>
00414170 . 8B46 64 mov eax,dword ptr ds:[esi+0x64]
00414173 . 8B4E 60 mov ecx,dword ptr ds:[esi+0x60]
00414176 . 50 push eax ; EAX 序列号
00414177 . 51 push ecx ; ECX 用户名
00414178 . C64424 18 01 mov byte ptr ss:[esp+0x18],0x1
0041417D . E8 EEFCFFFF call Ahead_DV.00413E70 ; 关键CALL
00414182 . 83C4 08 add esp,0x8
00414185 . 85C0 test eax,eax ; Ahead_DV.00478518
00414187 . 75 15 jnz short Ahead_DV.0041419E ; 关键跳。
00414189 . 6A 40 push 0x40
0041418B . 68 2C2D4A00 push Ahead_DV.004A2D2C ; ASCII "Sorry"
00414190 . 68 002D4A00 push Ahead_DV.004A2D00 ; ASCII "Invalid username or registration code "
00414195 . 8BCE mov ecx,esi ; mfc42.#4234
00414197 . E8 9CD60500 call <jmp.&MFC42.#4224>
0041419C . EB 54 jmp short Ahead_DV.004141F2
0041419E > 8B46 60 mov eax,dword ptr ds:[esi+0x60]
004141A1 . 8D4C24 04 lea ecx,dword ptr ss:[esp+0x4]
004141A5 . 50 push eax ; Ahead_DV.00478518
004141A6 . 68 E42C4A00 push Ahead_DV.004A2CE4 ; ASCII "License To:%s "
004141AB . 51 push ecx
004141AC . E8 57D60500 call <jmp.&MFC42.#2818>
004141B1 . 8B5424 10 mov edx,dword ptr ss:[esp+0x10] ; mfc42.73D322FD
004141B5 . 83C4 0C add esp,0xC
004141B8 . 8BCE mov ecx,esi ; mfc42.#4234
004141BA . 6A 40 push 0x40
004141BC . 68 D82C4A00 push Ahead_DV.004A2CD8 ; ASCII "Thank you"
004141C1 . 52 push edx
F7跟进关键CALL来到了这里:
[Asm] 纯文本查看 复制代码 00413E70 /$ 53 push ebx
00413E71 |. 55 push ebp
00413E72 |. 8B6C24 0C mov ebp,dword ptr ss:[esp+0xC] ; 用户名
00413E76 |. 56 push esi ; mfc42.#4234
00413E77 |. 57 push edi
00413E78 |. BE 2C2F4C00 mov esi,Ahead_DV.004C2F2C
00413E7D |. 8BC5 mov eax,ebp
00413E7F |> 8A10 /mov dl,byte ptr ds:[eax] ; 取用户名单字符
00413E81 |. 8A1E |mov bl,byte ptr ds:[esi]
00413E83 |. 8ACA |mov cl,dl ; 单字符给了CL
00413E85 |. 3AD3 |cmp dl,bl
00413E87 |. 75 1E |jnz short Ahead_DV.00413EA7 ; 不为空则跳出
00413E89 |. 84C9 |test cl,cl
00413E8B |. 74 16 |je short Ahead_DV.00413EA3
00413E8D |. 8A50 01 |mov dl,byte ptr ds:[eax+0x1]
00413E90 |. 8A5E 01 |mov bl,byte ptr ds:[esi+0x1]
00413E93 |. 8ACA |mov cl,dl
00413E95 |. 3AD3 |cmp dl,bl
00413E97 |. 75 0E |jnz short Ahead_DV.00413EA7
00413E99 |. 83C0 02 |add eax,0x2
00413E9C |. 83C6 02 |add esi,0x2
00413E9F |. 84C9 |test cl,cl
00413EA1 |.^ 75 DC \jnz short Ahead_DV.00413E7F
00413EA3 |> 33C0 xor eax,eax ; Ahead_DV.00478518
00413EA5 |. EB 05 jmp short Ahead_DV.00413EAC
00413EA7 |> 1BC0 sbb eax,eax ; Ahead_DV.00478518
00413EA9 |. 83D8 FF sbb eax,-0x1
00413EAC |> 85C0 test eax,eax ; Ahead_DV.00478518
00413EAE |. 74 51 je short Ahead_DV.00413F01
最后的00413EAE 跳到了下面这段代码, 从未进关键CALL之前,可以看到下面EAX为关键标志,如果EAX为0则注册失败,通过对上面汇编代码分析得出,这代码主要判断用户名是否为空
[Asm] 纯文本查看 复制代码 00413F01 |> \5F pop edi ; 这里EAX为0,注册失败
00413F02 |. 5E pop esi ; mfc42.73D323EB
00413F03 |. 5D pop ebp ; mfc42.73D323EB
00413F04 |. 33C0 xor eax,eax ; Ahead_DV.00478518
00413F06 |. 5B pop ebx ; mfc42.73D323EB
00413F07 \. C3 retn
以此类推,下面是注册码是否为空的判断代码:
[Asm] 纯文本查看 复制代码 00413EB0 |. 8B7C24 18 mov edi,dword ptr ss:[esp+0x18] ; 取序列号
00413EB4 |. BE 2C2F4C00 mov esi,Ahead_DV.004C2F2C
00413EB9 |. 8BC7 mov eax,edi
00413EBB |> 8A10 /mov dl,byte ptr ds:[eax] ; 与上面循环一样
00413EBD |. 8A1E |mov bl,byte ptr ds:[esi]
00413EBF |. 8ACA |mov cl,dl
00413EC1 |. 3AD3 |cmp dl,bl
00413EC3 |. 75 1E |jnz short Ahead_DV.00413EE3 ; 不为空则跳出
00413EC5 |. 84C9 |test cl,cl
00413EC7 |. 74 16 |je short Ahead_DV.00413EDF
00413EC9 |. 8A50 01 |mov dl,byte ptr ds:[eax+0x1]
00413ECC |. 8A5E 01 |mov bl,byte ptr ds:[esi+0x1]
00413ECF |. 8ACA |mov cl,dl
00413ED1 |. 3AD3 |cmp dl,bl
00413ED3 |. 75 0E |jnz short Ahead_DV.00413EE3
00413ED5 |. 83C0 02 |add eax,0x2
00413ED8 |. 83C6 02 |add esi,0x2
00413EDB |. 84C9 |test cl,cl
00413EDD |.^ 75 DC \jnz short Ahead_DV.00413EBB
00413EDF |> 33C0 xor eax,eax ; Ahead_DV.00478518
00413EE1 |. EB 05 jmp short Ahead_DV.00413EE8
00413EE3 |> 1BC0 sbb eax,eax ; Ahead_DV.00478518
00413EE5 |. 83D8 FF sbb eax,-0x1
00413EE8 |> 85C0 test eax,eax ; Ahead_DV.00478518
00413EEA |. 74 15 je short Ahead_DV.00413F01
注意这里,用户名和注册码都不为空时,这里又进入下一层CALL,此CALL也必须跟进,因为关键算法就在里面了。
[Asm] 纯文本查看 复制代码 00413C40 /$ 6A FF push -0x1
00413C42 |. 68 203B4700 push Ahead_DV.00473B20 ; SE 处理程序安装
00413C47 |. 64:A1 0000000>mov eax,dword ptr fs:[0]
00413C4D |. 50 push eax ; Ahead_DV.00478518
00413C4E |. 64:8925 00000>mov dword ptr fs:[0],esp
00413C55 |. 83EC 14 sub esp,0x14
00413C58 |. 8B4424 24 mov eax,dword ptr ss:[esp+0x24] ; 用户名
00413C5C |. 53 push ebx
00413C5D |. 55 push ebp
00413C5E |. 56 push esi ; mfc42.#4234
00413C5F |. 57 push edi
00413C60 |. 50 push eax ; Ahead_DV.00478518
00413C61 |. 8D4C24 18 lea ecx,dword ptr ss:[esp+0x18]
00413C65 |. E8 2EDC0500 call <jmp.&MFC42.#537> ; EAX存着用户名指针
00413C6A |. 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14]
00413C6E |. C74424 2C 000>mov dword ptr ss:[esp+0x2C],0x0
00413C76 |. E8 F3DE0500 call <jmp.&MFC42.#6282>
00413C7B |. 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14]
00413C7F |. E8 E4DE0500 call <jmp.&MFC42.#6283>
00413C84 |. 6A 20 push 0x20
00413C86 |. 8D4C24 18 lea ecx,dword ptr ss:[esp+0x18]
00413C8A |. E8 B5DB0500 call <jmp.&MFC42.#2915>
00413C8F |. 8B4C24 38 mov ecx,dword ptr ss:[esp+0x38]
00413C93 |. 8BD8 mov ebx,eax ; Ahead_DV.00478518
00413C95 |. 51 push ecx
00413C96 |. 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14]
00413C9A |. E8 F9DB0500 call <jmp.&MFC42.#537>
00413C9F |. 8D4C24 10 lea ecx,dword ptr ss:[esp+0x10] ; EAX存着序列号指针
00413CA3 |. C64424 2C 01 mov byte ptr ss:[esp+0x2C],0x1
00413CA8 |. E8 C1DE0500 call <jmp.&MFC42.#6282>
00413CAD |. 8D4C24 10 lea ecx,dword ptr ss:[esp+0x10]
00413CB1 |. E8 B2DE0500 call <jmp.&MFC42.#6283>
00413CB6 |. 6A 20 push 0x20
00413CB8 |. 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14]
00413CBC |. E8 83DB0500 call <jmp.&MFC42.#2915>
00413CC1 |. 8BD0 mov edx,eax ; EDX序列号
00413CC3 |. 83CE FF or esi,-0x1
00413CC6 |. 8BFA mov edi,edx
00413CC8 |. 8BCE mov ecx,esi ; mfc42.#4234
00413CCA |. 33C0 xor eax,eax ; Ahead_DV.00478518
00413CCC |. 895424 20 mov dword ptr ss:[esp+0x20],edx
00413CD0 |. F2:AE repne scas byte ptr es:[edi]
00413CD2 |. F7D1 not ecx
00413CD4 |. 49 dec ecx
00413CD5 |. 8BFB mov edi,ebx
00413CD7 |. 8BE9 mov ebp,ecx
00413CD9 |. 8BCE mov ecx,esi ; mfc42.#4234
00413CDB |. F2:AE repne scas byte ptr es:[edi]
00413CDD |. F7D1 not ecx
00413CDF |. 49 dec ecx
00413CE0 |. 3BCD cmp ecx,ebp ; 比较用户名与序列号的长度
00413CE2 |. 0F87 54010000 ja Ahead_DV.00413E3C ; 跳到注册失败
进入这个CALL后,通过上面的分析,可以看到,取了用户名与注册码的长度进行比较,若用户名长度大于注册码的长度则跳到00413E3C
如果跳到 00413E3C 这个位置明显是注册失败,从上图可以看到EAX又被赋值为0,而且可以看到这里有3个跳转来自.所以1处就是刚分析得到的,用户长度不能大于注册码长度,否则注册失败。
接着继续分析下面代码,这里可以看到,用户名的长度与注册码的长度分别做了比较,如果为空则跳到 00413E3C,那么刚才的2处错误跳转就来自于这2个比较。
[Asm] 纯文本查看 复制代码 00413CE8 |. 8BFB mov edi,ebx
00413CEA |. 8BCE mov ecx,esi ; mfc42.#4234
00413CEC |. F2:AE repne scas byte ptr es:[edi]
00413CEE |. F7D1 not ecx
00413CF0 |. 49 dec ecx
00413CF1 |. 0F84 45010000 je Ahead_DV.00413E3C ; 用户名是否为空,跳到注册失败
00413CF7 |. 8BFA mov edi,edx
00413CF9 |. 8BCE mov ecx,esi ; mfc42.#4234
00413CFB |. F2:AE repne scas byte ptr es:[edi]
00413CFD |. F7D1 not ecx
00413CFF |. 49 dec ecx
00413D00 |. 0F84 36010000 je Ahead_DV.00413E3C ; 序列号是否为空,跳到注册失败
接着就是关键的一些算法,我们一点一点来看。
[Asm] 纯文本查看 复制代码 00413D06 |. 894424 38 mov dword ptr ss:[esp+0x38],eax ; Ahead_DV.00478518
00413D0A |> 8B5424 38 /mov edx,dword ptr ss:[esp+0x38]
00413D0E |. 8D4C24 34 |lea ecx,dword ptr ss:[esp+0x34] ; ECX存放用户名指针
00413D12 |. 8A82 782C4A00 |mov al,byte ptr ds:[edx+0x4A2C78] ; EDX计数,常量ZQY
00413D18 |. 884424 18 |mov byte ptr ss:[esp+0x18],al
00413D1C |. E8 93DA0500 |call <jmp.&MFC42.#540>
00413D21 |. 8BFB |mov edi,ebx
00413D23 |. 83C9 FF |or ecx,-0x1
00413D26 |. 33C0 |xor eax,eax ; Ahead_DV.00478518
00413D28 |. 33ED |xor ebp,ebp
00413D2A |. F2:AE |repne scas byte ptr es:[edi]
00413D2C |. F7D1 |not ecx
00413D2E |. 49 |dec ecx
00413D2F |. C64424 2C 02 |mov byte ptr ss:[esp+0x2C],0x2
00413D34 |. 74 4B |je short Ahead_DV.00413D81 ; 用户名长度为8,没有跳,暂时不管
00413D36 |> 8A042B |/mov al,byte ptr ds:[ebx+ebp] ; 取用户名1字节
00413D39 |. 33F6 ||xor esi,esi ; mfc42.#4234
00413D3B |> 3A0475 102C4A>||/cmp al,byte ptr ds:[esi*2+0x4A2C10] ; 查表,比较
00413D42 |. 74 08 |||je short Ahead_DV.00413D4C
00413D44 |. 46 |||inc esi ; mfc42.#4234
00413D45 |. 83FE 34 |||cmp esi,0x34 ; 比较大小字母 总的52位
00413D48 |.^ 7C F1 ||\jl short Ahead_DV.00413D3B
00413D4A |. EB 11 ||jmp short Ahead_DV.00413D5D
00413D4C |> 8A0C75 112C4A>||mov cl,byte ptr ds:[esi*2+0x4A2C11] ; 对应的码表赋值cl
00413D53 |. 51 ||push ecx
00413D54 |. 8D4C24 38 ||lea ecx,dword ptr ss:[esp+0x38]
00413D58 |. E8 05DE0500 ||call <jmp.&MFC42.#940> ; EAX存放着算出来的字符串指针
00413D5D |> 83FE 34 ||cmp esi,0x34
00413D60 |. 75 0E ||jnz short Ahead_DV.00413D70
00413D62 |. 8B5424 18 ||mov edx,dword ptr ss:[esp+0x18]
00413D66 |. 8D4C24 34 ||lea ecx,dword ptr ss:[esp+0x34]
00413D6A |. 52 ||push edx
00413D6B |. E8 F2DD0500 ||call <jmp.&MFC42.#940>
00413D70 |> 8BFB ||mov edi,ebx
00413D72 |. 83C9 FF ||or ecx,-0x1
00413D75 |. 33C0 ||xor eax,eax ; Ahead_DV.00478518
00413D77 |. 45 ||inc ebp
00413D78 |. F2:AE ||repne scas byte ptr es:[edi]
00413D7A |. F7D1 ||not ecx
00413D7C |. 49 ||dec ecx
00413D7D |. 3BE9 ||cmp ebp,ecx ; ebp为计数
00413D7F |.^ 72 B5 |\jb short Ahead_DV.00413D36
00413D81 |> 8B4424 34 |mov eax,dword ptr ss:[esp+0x34] ; 用户名计算得出的字符串
00413D85 |. 8B48 F8 |mov ecx,dword ptr ds:[eax-0x8] ; Ahead_DV.00411CF0
00413D88 |. 83F9 10 |cmp ecx,0x10 ; 比较是否大于等于 10h
00413D8B |. 7D 3A |jge short Ahead_DV.00413DC7
00413D8D |. 8BC1 |mov eax,ecx
00413D8F |. B9 10000000 |mov ecx,0x10
00413D94 |. 2BC8 |sub ecx,eax ; Ahead_DV.00478518
00413D96 |. 8D5424 1C |lea edx,dword ptr ss:[esp+0x1C]
00413D9A |. 51 |push ecx
00413D9B |. 52 |push edx
00413D9C |. B9 1C044E00 |mov ecx,Ahead_DV.004E041C ; UNICODE "匀?"
00413DA1 |. E8 B6DD0500 |call <jmp.&MFC42.#4129> ; 截取10h-用户名长度字符串 "HLWsCbMErpAQOXWK"
00413DA6 |. 50 |push eax ; Ahead_DV.00478518
00413DA7 |. 8D4C24 38 |lea ecx,dword ptr ss:[esp+0x38]
00413DAB |. C64424 30 03 |mov byte ptr ss:[esp+0x30],0x3
00413DB0 |. E8 CBDA0500 |call <jmp.&MFC42.#939>
00413DB5 |. 8D4C24 1C |lea ecx,dword ptr ss:[esp+0x1C] ; 计算的字符串+截取字符
00413DB9 |. C64424 2C 02 |mov byte ptr ss:[esp+0x2C],0x2 ; ASCII "ZZCKAZBZHLWsCbME"
00413DBE |. E8 EFD80500 |call <jmp.&MFC42.#800>
00413DC3 |. 8B4424 34 |mov eax,dword ptr ss:[esp+0x34]
00413DC7 |> 8B4C24 20 |mov ecx,dword ptr ss:[esp+0x20] ; mfc42.73D491F4
00413DCB |. 51 |push ecx ; /s2 = "匞"
00413DCC |. 50 |push eax ; |s1 = "?G"
00413DCD |. FF15 6C574700 |call dword ptr ds:[<&MSVCRT._mbscmp>] ; \_mbscmp
00413DD3 |. 83C4 08 |add esp,0x8 ; 真假比较
00413DD6 |. 8D4C24 34 |lea ecx,dword ptr ss:[esp+0x34]
00413DDA |. 85C0 |test eax,eax ; Ahead_DV.00478518
00413DDC |. C64424 2C 01 |mov byte ptr ss:[esp+0x2C],0x1
00413DE1 |. 74 1B |je short Ahead_DV.00413DFE
00413DE3 |. 33F6 |xor esi,esi ; mfc42.#4234
00413DE5 |. E8 C8D80500 |call <jmp.&MFC42.#800>
00413DEA |. 8B4424 38 |mov eax,dword ptr ss:[esp+0x38]
00413DEE |. 40 |inc eax ; Ahead_DV.00478518
00413DEF |. 83F8 03 |cmp eax,0x3
00413DF2 |. 894424 38 |mov dword ptr ss:[esp+0x38],eax ; Ahead_DV.00478518
00413DF6 |.^ 0F8C 0EFFFFFF \jl Ahead_DV.00413D0A
00413DFC |. EB 0A jmp short Ahead_DV.00413E08
00413DFE |> BE 01000000 mov esi,0x1 ; 标志位赋值
00413E03 |. E8 AAD80500 call <jmp.&MFC42.#800>
00413E08 |> 8D4C24 10 lea ecx,dword ptr ss:[esp+0x10]
00413E0C |. C64424 2C 00 mov byte ptr ss:[esp+0x2C],0x0
00413E11 |. E8 9CD80500 call <jmp.&MFC42.#800>
00413E16 |. 8D4C24 14 lea ecx,dword ptr ss:[esp+0x14]
00413E1A |. C74424 2C FFF>mov dword ptr ss:[esp+0x2C],-0x1
00413E22 |. E8 8BD80500 call <jmp.&MFC42.#800>
00413E27 |. 8BC6 mov eax,esi ; mfc42.#4234
00413E29 |. 5F pop edi ; mfc42.73D323EB
00413E2A |. 5E pop esi ; mfc42.73D323EB
00413E2B |. 5D pop ebp ; mfc42.73D323EB
00413E2C |. 5B pop ebx ; mfc42.73D323EB
00413E2D |. 8B4C24 14 mov ecx,dword ptr ss:[esp+0x14]
00413E31 |. 64:890D 00000>mov dword ptr fs:[0],ecx
00413E38 |. 83C4 20 add esp,0x20
00413E3B |. C3 retn
这里有个可疑的常量,我们先记录一下,一会还要用到这个
这里AL为存放用户名的逐个取字符,EBP为计数,与esi*2+0x4A2C10 内存地址进行比较,我们数据跟随,可以看到一张表,34h == 52,这里进行循环比较,从这个内存地址我们可以看出,他就是取这张表来进行比较,总共比的是a-z与A-Z刚好是52个大小写字母
比较完若找到字符,则把esi*2+0x4A2C11 赋值给CL,没有找到就跳过赋值,通过跟踪分析得出,这里如果用户名的字符是字母则把表里的紧跟的字符赋值给了cl,如果不是字母,则利用之前的那个常量,还记得吗,ZQY的一个字符Z来代替,然后依次循环N*NAME长度,这样就获取到了由用户名通过码表计算出来的一串字符串。
[Asm] 纯文本查看 复制代码 00413D81 |> \8B4424 34 |mov eax,dword ptr ss:[esp+0x34] ; 用户名计算得出的字符串
00413D85 |. 8B48 F8 |mov ecx,dword ptr ds:[eax-0x8]
00413D88 |. 83F9 10 |cmp ecx,0x10 ; 比较是否大于等于 10h
00413D8B |. 7D 3A |jge short Ahead_DV.00413DC7
00413D8D |. 8BC1 |mov eax,ecx
00413D8F |. B9 10000000 |mov ecx,0x10
00413D94 |. 2BC8 |sub ecx,eax
00413D96 |. 8D5424 1C |lea edx,dword ptr ss:[esp+0x1C]
00413D9A |. 51 |push ecx
00413D9B |. 52 |push edx
00413D9C |. B9 1C044E00 |mov ecx,Ahead_DV.004E041C ; UNICODE "匀?"
00413DA1 |. E8 B6DD0500 |call <jmp.&MFC42.#4129> ; 截取10h-用户名长度字符串 "HLWsCbMErpAQOXWK"
00413DA6 |. 50 |push eax
00413DA7 |. 8D4C24 38 |lea ecx,dword ptr ss:[esp+0x38]
00413DAB |. C64424 30 03 |mov byte ptr ss:[esp+0x30],0x3
00413DB0 |. E8 CBDA0500 |call <jmp.&MFC42.#939>
00413DB5 |. 8D4C24 1C |lea ecx,dword ptr ss:[esp+0x1C] ; 计算的字符串+截取字符
取得计算出来的字符串长度判断是否够16位,若不够,则补到16位,而补的字符串取值来自0x4E041C ASCII ”HLWsCbMErpAQOXWK“,最后组合成新的注册码与我们之前的假码进行比较。
这里看到,当比较结果给了EAX,如果EAX为0则ESI就为1,最后通过ESI给EAX后,注册即可成功。所以大概的流程就是这样,现在我们来大概梳理一下,还有那个常量的问题,再次来分析一下。
从这边看到这里大循环3次,而且和这个常量有关,我们再走一遍分析一下,得出这一次计算出来的结果则是如果名字不是字母则用Q来代替,之前是用Z来代替,那么我们就可以总结出,如果NAME含有不是字母的其他字符,那么他就有3个KEY。
到这里已经很清晰了,通过上面的分析我们就可以来自己写KEYGEN了,首先名字不能为空,其次名字长度不能大于注册码的长度,然后通过码表依次计算得出新的字符串,不到16位则用第2个表的字符串进行补位,最后生成的就是正确的KEY。
用易语言写的KEYGEN,-.-# 凑合看看就好啦~ 附上成品注册机。
Ahead DVD Ripper KeyGen.rar
(298.68 KB, 下载次数: 86)
|
免费评分
-
查看全部评分
本帖被以下淘专辑推荐:
- · 学习及教程|主题: 1126, 订阅: 1119
- · 优秀逆向文|主题: 238, 订阅: 93
|