首页
社区
课程
招聘
Thigo's keygenme简单分析
2006-8-10 12:05 13406

Thigo's keygenme简单分析

2006-8-10 12:05
13406
【文章标题】: Thigo's keygenme简单分析
【下载地址】: 自己搜索下载
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
  呵呵,这个keygenMe有点意思.
  
  让我们开始吧,运行一下CrackMe,有如下提示信息:
  You should put a file named key.dat in this dir...
  哦,听作者的话吧,把CrackMe关掉,然后在同一个目录下建一个名字叫key.dat的文件
  里面随便输点东西,我写的信息是字符串"12345678".
  再次打开CrackMe,看到提示信息变成了:
  Are u sure it's a good keyfile ??
  呀,还是错的呢.
  
  
  W32Dasm反汇编,看看有没有什么有用的信息.
  
  W32DASM String Data References
  ------------
  " ?"
  ""
  ""
  "?"
  "]_^[?]?L$髁"
  "Are u sure it's a good keyfile "
  "key.dat"
  "U?? -"
  "You should put a file named key.dat "
  "竣??NK4?8?2?8?5C泪B?LE?G?H:?8?0R?
  ------------
  似乎都是提示keyfile的信息,没成功的提示信息.
  
  
  双击"Are u sure it's a good keyfile "这一行字符串,来到这里
  
  :004011EA 3DF48B2A00              cmp eax, 002A8BF4
  :004011EF 7407                    je 004011F8
  
  * Possible StringData Ref from Data Obj ->"Are u sure it's a good keyfile "
                                          ->"??"
                                    |
  :004011F1 68E4514000              push 004051E4
  :004011F6 EB60                    jmp 00401258
  
  
  一直往上看
  
  :00401142 50                      push eax
  :00401143 56                      push esi
  
  * Reference To: KERNEL32.ReadFile, Ord:0218h
                                    |
  :00401144 FF1518404000                   call dword ptr ds:[<&KERNEL32.ReadFile>]    ; 读取keyfile文件
  
  :0040114A 56                      push esi
  
  * Reference To: KERNEL32.CloseHandle, Ord:001Bh
                                    |
  :0040114B FF151C404000            call dword ptr ds:[<&KERNEL32.CloseHandle>] ; 关闭文件句炳
  :00401151 8B4DFC                  mov ecx, dword ptr [ebp-04]
  :00401154 33C0                    xor eax, eax
  :00401156 3BCB                    cmp ecx, ebx
  :00401158 760C                    jbe 00401166
  
  
  就从这里下手吧,用OD载入CrackME,在00401144处下个断点,Let's go
  
  0040115A    308C05 A8FEFFFF       xor byte ptr ss:[ebp+eax-158],cl            ; ---------
  00401161    40                    inc eax                                     ; key.dat内容逐字节异或文件的长度
  00401162    3BC1                  cmp eax,ecx
  00401164  ^ 72 F4                 jb short crackme.0040115A                   ; ---------
  00401166    80B5 A8FEFFFF 54      xor byte ptr ss:[ebp-158],54                ; 文件第1字节XOR 0x54
  0040116D    80B5 A9FEFFFF 4D      xor byte ptr ss:[ebp-157],4D                ; 文件第2字节XOR 0x4D
  00401174    80B5 AAFEFFFF 47      xor byte ptr ss:[ebp-156],47                ; 文件第3字节XOR 0x47
  0040117B    3BCF                  cmp ecx,edi                                 ; /
  0040117D    8BF7                  mov esi,edi                                 ; key.dat文件长度<=3则跳转
  0040117F    76 27                 jbe short crackme.004011A8                  ; \
  00401181    8A95 A8FEFFFF         mov dl,byte ptr ss:[ebp-158]                ; /
  00401187    8D8435 A9FEFFFF       lea eax,dword ptr ss:[ebp+esi-157]          ; n为循环计数器,n > 0
  0040118E    03F7                  add esi,edi
  00401190    3050 FF               xor byte ptr ds:[eax-1],dl                  ; 第1位XOR第3n+1位->第3n+1位
  00401193    8A95 A9FEFFFF         mov dl,byte ptr ss:[ebp-157]
  00401199    3010                  xor byte ptr ds:[eax],dl                    ; 第2位XOR第3n+2位->第3n+2位
  0040119B    8A95 AAFEFFFF         mov dl,byte ptr ss:[ebp-156]
  004011A1    3050 01               xor byte ptr ds:[eax+1],dl                  ; 第3位XOR第3(n+1)位->第3(n+1)位
  004011A4    3BF1                  cmp esi,ecx                                 ; 循环直到3(n+1) > length
  004011A6  ^ 72 D9                 jb short crackme.00401181                   ; \
  004011A8    BE 30504000           mov esi,crackme.00405030                   ; 要处理的数据开始地址(0405030)放到ESI
  004011AD    889C0D A8FEFFFF       mov byte ptr ss:[ebp+ecx-158],bl           ; 把0写入上面最后一次循环的3(n+1)位
  004011B4    33FF                  xor edi,edi
  004011B6    8BC6                  mov eax,esi
  004011B8    8A8C3D A8FEFFFF       mov cl,byte ptr ss:[ebp+edi-158]           ; /
  004011BF    3008                  xor byte ptr ds:[eax],cl
  004011C1    40                    inc eax                                    ; 用前面对key.dat内容变换后得到的数据
  004011C2    47                    inc edi
  004011C3    83FF 03               cmp edi,3                                  ; 的前3位循环XOR从地址0403050开始的数据,
  004011C6    75 02                 jnz short crackme.004011CA
  004011C8    33FF                  xor edi,edi                                ; 直到碰到要处理数据段的结束标志0xFF
  004011CA    8038 FF               cmp byte ptr ds:[eax],0FF
  004011CD  ^ 75 E9                 jnz short crackme.004011B8                 ; \
  004011CF    0FB605 32504000       movzx eax,byte ptr ds:[405032]             ; /
  004011D6    0FB60D 31504000       movzx ecx,byte ptr ds:[405031]
  004011DD    0FAFC1                imul eax,ecx
  004011E0    0FB60D 30504000       movzx ecx,byte ptr ds:[405030]             ; 0403050开始3位字节的乘积=0x2A8BF4 ?
  004011E7    0FAFC1                imul eax,ecx
  004011EA    3D F48B2A00           cmp eax,2A8BF4                             ; 如不相等则显示下面的错误信息
  004011EF    74 07                 je short crackme.004011F8                  ; \
  004011F1    68 E4514000           push crackme.004051E4                      ; ASCII "Are u sure it's a good keyfile ??"
  004011F6    EB 60                 jmp short crackme.00401258
  004011F8    8A8D A8FEFFFF         mov cl,byte ptr ss:[ebp-158]
  004011FE    33C0                  xor eax,eax
  00401200    884C05 D0             mov byte ptr ss:[ebp+eax-30],cl            ; /
  00401204    8A8C05 A9FEFFFF       mov cl,byte ptr ss:[ebp+eax-157]
  0040120B    40                    inc eax                                    ; 在处理后的key.dat中查找第一个空格字符(0x20)
  0040120C    80F9 20               cmp cl,20
  0040120F  ^ 75 EF                 jnz short crackme.00401200                 ; \
  00401211    885C05 D0             mov byte ptr ss:[ebp+eax-30],bl            ;
  00401215    40                    inc eax
  00401216    33D2                  xor edx,edx
  00401218    8D8C05 A8FEFFFF       lea ecx,dword ptr ss:[ebp+eax-158]
  0040121F    8A8405 A8FEFFFF       mov al,byte ptr ss:[ebp+eax-158]
  00401226    41                    inc ecx                                    ; /---------A-------
  00401227    884415 A8             mov byte ptr ss:[ebp+edx-58],al            ; 将key.dat文件中第一个空格字符
  0040122B    42                    inc edx                                    ; 后1位起一直到末尾的数据
  0040122C    8A01                  mov al,byte ptr ds:[ecx]                   ; COPY到某处内存
  0040122E    3AC3                  cmp al,bl                                  ; 这段数据最后要用到
  00401230  ^ 75 F4                 jnz short crackme.00401226                 ; \---------A-------
  00401232    8D45 F8               lea eax,dword ptr ss:[ebp-8]
  00401235    885C15 A8             mov byte ptr ss:[ebp+edx-58],bl
  00401239    50                    push eax
  0040123A    6A 40                 push 40
  0040123C    68 7F010000           push 17F
  00401241    56                    push esi
  00401242    FF15 68404000         call dword ptr ds:[<&KERNEL32.VirtualProte>; kernel32.VirtualProtect
  
  0012FA88   00401248      /CALL 到 VirtualProtect 来自 crackme.00401242
  0012FA8C   00405030      |Address = crackme.00405030
  0012FA90   0000017F      |Size = 17F (383.)
  0012FA94   00000040      |NewProtect = PAGE_EXECUTE_READWRITE
  0012FA98   0012FBF8      \pOldProtect = 0012FBF8
  
  修改地址为0403050,长度为0x17F内存区域的属性,从这里可以猜测到前面做变换的使用的数据段的长度
  
  
  00401248    8D45 D0               lea eax,dword ptr ss:[ebp-30]
  0040124B    50                    push eax
  0040124C    8D45 A8               lea eax,dword ptr ss:[ebp-58]
  0040124F    50                    push eax
  00401250    FFD6                  call esi                                   ; /*整个CrackMe的精彩部分*/ESI=403050
  00401252    59                    pop ecx
  00401253    8D45 D0               lea eax,dword ptr ss:[ebp-30]
  00401256    59                    pop ecx
  00401257    50                    push eax
  00401258    68 EC030000           push 3EC
  0040125D    FF75 08               push dword ptr ss:[ebp+8]
  00401260    FF15 C0404000         call dword ptr ds:[<&USER32.SetDlgItemText>; 根据CALL ESI的结果设置提示信息
  00401266    5F                    pop edi
  00401267    5E                    pop esi
  00401268    5B                    pop ebx
  00401269    C9                    leave
  
  
  
  
  我是用内容为字符串"12345678"的keyfile,通过把前面的两处跳转
  1:
  004011EA    3D F48B2A00           cmp eax,2A8BF4                             ;
  004011EF    74 07                 je short crackme.004011F8                  ;这里要修改
  
  2:
  0040120C    80F9 20               cmp cl,20
  0040120F  ^ 75 EF                 jnz short crackme.00401200                 ; 这里要修改
  
  
  修改后来到这里的:
  程序运行到这里的时候ESI=0403050
  00401250    FFD6                  call esi
  
  进入CALL
  
  00405030  - 73 C8                 jnb short crackme.00404FFA
  00405032    DE77 84               fidiv word ptr ds:[edi-7C]
  00405035  ^ 77 DA                 ja short crackme.00405011
  00405037    43                    inc ebx
  00405038    3226                  xor ah,byte ptr ds:[esi]
  0040503A    43                    inc ebx
  0040503B    B9 634FF426           mov ecx,26F44F63
  ......
  ......
  
  一大段的乱码,呵呵,这是怎么回事呀?
  
  这里正是整个CrackMe最精彩的部分,慢慢分析一下:
  地址0403050开始的这段数据在我们前面的分析中曾出现两次
  1:
  004011B8    8A8C3D A8FEFFFF       mov cl,byte ptr ss:[ebp+edi-158]           ; /
  004011BF    3008                  xor byte ptr ds:[eax],cl
  004011C1    40                    inc eax                                    ; 用前面对key.dat内容变换后得到的数据
  004011C2    47                    inc edi
  004011C3    83FF 03               cmp edi,3                                  ; 的前3位循环XOR从地址0403050开始的数据,
  004011C6    75 02                 jnz short crackme.004011CA
  004011C8    33FF                  xor edi,edi                                ; 直到碰到要处理数据段的结束标志0xFF
  004011CA    8038 FF               cmp byte ptr ds:[eax],0FF
  004011CD  ^ 75 E9                 jnz short crackme.004011B8                 ; \
  
  
  2:
  004011E0    0FB60D 30504000       movzx ecx,byte ptr ds:[405030]             ; 0403050开始3位字节的乘积=0x2A8BF4 ?
  004011E7    0FAFC1                imul eax,ecx
  004011EA    3D F48B2A00           cmp eax,2A8BF4                             ; 如不相等则显示下面的错误信息
  004011EF    74 07                 je short crackme.004011F8                  ; \
  004011F1    68 E4514000           push crackme.004051E4                      ; ASCII "Are u sure it's a good keyfile ??"
  
  
  而现在它又是一段CALL的代码,这里面一定有作者的某种提示.
  
  让我们看看一般的CALL开头是什么样的:
  kernel32.GetModuleHandleA
  
  7C80B529 >  8BFF                  mov edi,edi
  7C80B52B    55                    push ebp                                   ; ******
  7C80B52C    8BEC                  mov ebp,esp                                ; ******
  7C80B52E    837D 08 00            cmp dword ptr ss:[ebp+8],0
  7C80B532    74 18                 je short kernel32.7C80B54C
  7C80B534    FF75 08               push dword ptr ss:[ebp+8]
  7C80B537    E8 682D0000           call kernel32.7C80E2A4
  7C80B53C    85C0                  test eax,eax
  7C80B53E    74 08                 je short kernel32.7C80B548
  7C80B540    FF70 04               push dword ptr ds:[eax+4]
  7C80B543    E8 F4300000           call kernel32.GetModuleHandleW
  7C80B548    5D                    pop ebp
  7C80B549    C2 0400               retn 4
  
  
  
  kernel32.TerminateProcess
  
  
  7C801E16 >  8BFF                  mov edi,edi
  7C801E18    55                    push ebp                                   ; ******
  7C801E19    8BEC                  mov ebp,esp                                ; ******
  7C801E1B    837D 08 00            cmp dword ptr ss:[ebp+8],0
  7C801E1F    75 09                 jnz short kernel32.7C801E2A
  7C801E21    6A 06                 push 6
  7C801E23    E8 98740000           call kernel32.7C8092C0
  7C801E28    EB 1B                 jmp short kernel32.7C801E45
  7C801E2A    FF75 0C               push dword ptr ss:[ebp+C]
  7C801E2D    FF75 08               push dword ptr ss:[ebp+8]
  7C801E30    FF15 FC13807C         call dword ptr ds:[<&ntdll.NtTerminateProc>; ntdll.ZwTerminateProcess
  7C801E36    85C0                  test eax,eax
  7C801E38    7C 05                 jl short kernel32.7C801E3F
  7C801E3A    33C0                  xor eax,eax
  7C801E3C    40                    inc eax
  7C801E3D    EB 08                 jmp short kernel32.7C801E47
  7C801E3F    50                    push eax
  7C801E40    E8 36750000           call kernel32.7C80937B
  7C801E45    33C0                  xor eax,eax
  7C801E47    5D                    pop ebp
  7C801E48    C2 0800               retn 8
  
  注意打了'*'号的语句
  
  0x55 * 0x8B * 0xEC = 0x2A8BF4
  
  结合:
  1:
  004011B8    8A8C3D A8FEFFFF       mov cl,byte ptr ss:[ebp+edi-158]           ; /
  004011BF    3008                  xor byte ptr ds:[eax],cl
  004011C1    40                    inc eax                                    ; 用前面对key.dat内容变换后得到的数据
  004011C2    47                    inc edi
  004011C3    83FF 03               cmp edi,3                                  ; 的前3位循环XOR从地址0403050开始的数据,
  004011C6    75 02                 jnz short crackme.004011CA
  004011C8    33FF                  xor edi,edi                                ; 直到碰到要处理数据段的结束标志0xFF
  004011CA    8038 FF               cmp byte ptr ds:[eax],0FF
  004011CD  ^ 75 E9                 jnz short crackme.004011B8                 ; \
  
  
  2:
  004011E0    0FB60D 30504000       movzx ecx,byte ptr ds:[405030]             ; 0403050开始3位字节的乘积=0x2A8BF4 ?
  004011E7    0FAFC1                imul eax,ecx
  004011EA    3D F48B2A00           cmp eax,2A8BF4                             ; 如不相等则显示下面的错误信息
  004011EF    74 07                 je short crackme.004011F8                  ; \
  004011F1    68 E4514000           push crackme.004051E4                      ; ASCII "Are u sure it's a good keyfile ??"
  
  原来作者要告诉我们的就是:
  
  地址0403050开始的这段数据最后变换得到的结果应该是:
  7C801E18    55                    push ebp                                   
  7C801E19    8BEC                  mov ebp,esp                                
  这样的形式.
  
  因为异或运算满足:
  A XOR B = C
  A XOR C = B
  
  从而可以推导出前面key.dat变换后得到的前3位的结果,
  
  
  
  
  0403050处的原始数据(程序内置数据):
  ---------------------------
  00405030  1E BF A2 1A F3 0B B7 34 4E 4B 34 C5 0E 38 88 4B  竣??NK4?8?
  00405040  32 C5 06 38 88 0A 35 43 C0 61 42 8D 76 4C 45 BF  2?8?5C泪B?LE
  .....
  .....
  00405190  35 C7 0E C8 C5 06 3C 4D 06 C8 41 F5 25 CB 99 41  5?扰<M攘??A
  004051A0  97 C0 71 42 48 71 B2 8D 74 41 4B BF AB 16 F7 FF  ?qBHq?tAK揩?
  
  
  0x1E XOR 0x55 = 0x4B
  
  0xBF XOR 0x8B = 0x34
  
  0xA2 XOR 0xEC = 0x4E
  
  把得到的结果代替
  004011B8    8A8C3D A8FEFFFF       mov cl,byte ptr ss:[ebp+edi-158]
  处的key.dat数据,就可以把0403050处的原始数据还原成正确的指令代码了:
  
  还原后的指令:
  00405030    55                    push ebp
  00405031    8BEC                  mov ebp,esp
  00405033    51                    push ecx
  00405034    C745 FC 00000000      mov dword ptr ss:[ebp-4],0
  0040503B    8B45 0C               mov eax,dword ptr ss:[ebp+C]
  0040503E    C600 06               mov byte ptr ds:[eax],6
  
  ......
  
  ......
  
  前面一大段的指令做的只是很简单的变换:
  把固定的数据还原成以'\0'结尾的字符串:
  registed to:
  
  
  0040517A    8B45 0C               mov eax,dword ptr ss:[ebp+C]               ; /
  0040517D    0345 FC               add eax,dword ptr ss:[ebp-4]
  00405180    8B4D 08               mov ecx,dword ptr ss:[ebp+8]               ; 把前面标记为A出得到的数据
  00405183    034D FC               add ecx,dword ptr ss:[ebp-4]
  00405186    8A11                  mov dl,byte ptr ds:[ecx]
  00405188    8850 0F               mov byte ptr ds:[eax+F],dl
  0040518B    8B45 FC               mov eax,dword ptr ss:[ebp-4]               ; 连接在字符串"registed to:"后面
  0040518E    83C0 01               add eax,1
  00405191    8945 FC               mov dword ptr ss:[ebp-4],eax
  00405194    8B4D 08               mov ecx,dword ptr ss:[ebp+8]
  00405197    034D FC               add ecx,dword ptr ss:[ebp-4]               ; 得到registed to:XXXXX形势的字符串
  0040519A    0FBE11                movsx edx,byte ptr ds:[ecx]
  0040519D    85D2                  test edx,edx                               ; 做为提示信息
  0040519F  ^ 75 D9                 jnz short crackme.0040517A                 ; \
  
  这个CrackMe总的来说还是比较简单的,因为这个CrackMe本来就是for newbies,专门为我们菜鸟写的嘛:).
  
  算法大体过程如上所叙,根据用户名逆推回去就可以得到正确的keyFile了,说的不清楚的部分,请参看注册机代码(C语言).
  
  
  
  
  #include <iostream>
  
  #include <string>
  
  #include <fstream>
  
  using namespace std;
  
  int main()
  {
          string userName;
  
          string tmp;
  
  
          userName.push_back(0x4B ^ 0x54);
  
          userName.push_back(0x34 ^ 0x4D);
         
          userName.push_back(0x4E ^ 0x47);
  
          userName.push_back(0x4B ^ 0x20);
  
          userName.push_back(0x34 ^ 0x20);
  
          userName.push_back(0x4E ^ 0x20);
  
  
          cout << "Please Enter your name:" << endl;
  
          cin >> tmp;
  
          userName += tmp;
  
          int len = userName.length();
  
          if((userName.length() % 3)!= 0)
          {
                  int i  =3 * (userName.length() / 3 + 1);
  
                  for(;  i > len; i--)
                  {
                          userName.push_back(0x0);
                         
                  }
          }
  
          for(int i = 2;i < userName.length() / 3; i ++)
          {
                  userName[i * 3] ^= 0x4B;
  
                  userName[i * 3 + 1] ^= 0x34;
  
                  userName[i *3 + 2] ^= 0x4E;
          }
  
          for(int i = 0; i < userName.length(); i++)
          {
                  userName[i] ^= userName.length();
          }
  
  
  
          ofstream outFile;
  
          outFile.open("key.dat",ios_base::out | ios_base::binary);
  
          outFile << userName;
  
          outFile.close();
  
  
          system("pause");
  
          return 0;
  }
  
  
  
  
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!

                                                       2006年08月10日 12:01:57

阿里云助力开发者!2核2G 3M带宽不限流量!6.18限时价,开 发者可享99元/年,续费同价!

上传的附件:
收藏
点赞7
打赏
分享
最新回复 (13)
雪    币: 2256
活跃值: (941)
能力值: (RANK:2210 )
在线值:
发帖
回帖
粉丝
逍遥风 55 2006-8-10 12:18
2
0
好文章...学习了
雪    币: 267
活跃值: (44)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
hrbx 8 2006-8-10 14:34
3
0
精彩!!!
雪    币: 338
活跃值: (10)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
bfqyygy 1 2006-8-10 15:54
4
0
好文章!!学习!!
雪    币: 721
活跃值: (350)
能力值: ( LV9,RANK:1250 )
在线值:
发帖
回帖
粉丝
happytown 31 2006-8-10 20:07
5
0
分析得很精彩,以后多多出好文。
雪    币: 270
活跃值: (176)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
ikki 9 2006-8-11 13:28
6
0
谢谢各位的鼓励
雪    币: 716
活跃值: (162)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
elance 6 2006-8-14 09:57
7
0
分析的很好,支持!!!
因为这只是个CRACKME,所以作者没用什么加密算法,不过这种思想--数据与代码结合--很可怕,用这种思想如果把软件的关键功能代码加密,需要正确注册信息才能解密,那么这个软件几乎不能被破解!!!
雪    币: 461
活跃值: (93)
能力值: ( LV9,RANK:1170 )
在线值:
发帖
回帖
粉丝
bxm 29 2006-8-14 12:48
8
0
高,实在是高!
雪    币: 270
活跃值: (176)
能力值: ( LV12,RANK:370 )
在线值:
发帖
回帖
粉丝
ikki 9 2006-8-14 13:01
9
0
最初由 elance 发布
分析的很好,支持!!!
因为这只是个CRACKME,所以作者没用什么加密算法,不过这种思想--数据与代码结合--很可怕,用这种思想如果把软件的关键功能代码加密,需要正确注册信息才能解密,那么这个软件几乎不能被破解!!!

这样的应用也是有的,比如必须要key才能运行的ARM加壳后的软件,需要用注册文件解密特殊功能代码的软件等.
但如果有一个可用的正确的注册信息,也就和一般的破解没多大的区别了.
雪    币: 230
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
黄连 2006-8-19 11:02
10
0

适合菜鸟学习的好文!!
雪    币: 180
活跃值: (10)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
末日星魂 2010-5-5 23:15
11
0
注册码: 13 75 05 B8 18 01 35 59 21 2C 55 27

成功后显示: Registered to: Crackme

好文章,刷到第一页。
雪    币: 435
活跃值: (1147)
能力值: ( LV13,RANK:388 )
在线值:
发帖
回帖
粉丝
bitt 5 2010-5-6 00:55
12
0
晕,这不是那个考试题么,改了名字,改了资源,加了个壳,别的都没变
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kevinhook 2010-5-9 21:28
13
0
好文章!!学习!!
雪    币: 27
活跃值: (328)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tarequl 2021-8-12 17:21
14
0
Nice Tutorial 
游客
登录 | 注册 方可回帖
返回