首页
社区
课程
招聘
[原创]****CHM 注册流程及算法分析(MD5+AES-256+ZLIB+RC4)(续)
发表于: 2006-12-4 18:08 18480

[原创]****CHM 注册流程及算法分析(MD5+AES-256+ZLIB+RC4)(续)

2006-12-4 18:08
18480
【破文标题】**CHM 3.50 注册流程及算法分析(续)
【破文作者】Ptero
【破解工具】FI,OllyDbg,Dede,IDA,MD5工具
【注册方式】序列号+KeyFile
【保护方式】花指令,自校检,进程检测,API断点检测
【加壳方式】UPX v0.94-1.90
【加密算法】MD5+AES-256(Rijndael)+ZLib+RC4
【软件限制】功能限制
【破解声明】初学Crack,只是感兴趣,没有其它目的。失误之处敬请诸位大侠赐教!

----------------------------------------------------

【破解分析】

有关该软件中的AES-256(Rijndael)和ZLib算法的分析,请参见拙作《****CHM 3.50 注册流程及算法分析》。

本文主要分析该软件中采用的作者自定义算法和RC4算法。
因为因为本文算法较少,再加上我也是初涉密码学,所以尽可能写详细一点,自己分析起来方便,大家看着也方便,呵呵。

在继续闯关之前,先简单介绍一下RC4算法:

RC4加密算法是大名鼎鼎的RSA三人组中的头号人物Ron Rivest在1987年设计的密钥长度可变的流加密算法簇。之所以称其为簇,是由于其核心部分的S-Box长度可为任意,但一般为256字节。该算法的速度可以达到DES加密的10倍左右。
在开始的7年中该算法享有专利,直到1994年,有人匿名发布了它的源码,此后它就不再是一个商业秘密了。
RC4属于对称算法中的序列密码,按字节逐个对明文加密。

(以下代码均用类C语言描述)

先看S-Box的初始化,共分为2步:

1. 线性填充S-Box(S[i])。代码如下:

for(i=0;i<256;i++)
        S[i]=i;

2. 根据密钥(K[i])扰乱S-Box。代码如下:

for(i=j=0;i<256;i++)
{
        j=(j+S[i]+K[i])%256;
        交换S[i]和S[j];
}

RC4的加密/解密也很简单,也分为2步:

1. 产生随机字节k,代码如下:

i=j=0;
while(明文未结束)
{
        i=(i+1)%256;
        j=(j+s[i])%256;
        交换S[i]和S[j];
        t=(S[i]+S[j])%256;
        k=S[t];
}

2. 将字节k与明文异或。

因为是异或运算,所以加密/解密算法相同。

下面言归正传,继续闯关。

++++++++++++++++++++++++++++++++++++++++ 第11关:检测KeyFile黑名单 ++++++++++++++++++++++++++++++++++++++++

话说Ptero闯过10关后,发现程序注册菜单消失,自以为注册成功,正沾沾自喜中,忽又发现被程序作者骗了,制作出的chm还是显示未注册版本。
于是又打开OD、Dede、IDA等等,继续分析,终于找到了隐藏的11关入口:

00503DF3    8D95 58FEFFFF  lea edx, [ebp-1A8]
00503DF9    8B45 F0        mov eax, [ebp-10]                ; KeyFile文件名
00503DFC    E8 0763FCFF    call ****CHM.004CA108        ; 计算KeyFile文件的MD5值
00503E01    8B85 58FEFFFF  mov eax, [ebp-1A8]                ; MD5值
00503E07    8B55 EC        mov edx, [ebp-14]
00503E0A    E8 25B5FCFF    call ****CHM.004CF334
00503E0F    84C0           test al, al
00503E11    0F84 3F010000  je ****CHM.00503F56                ; 这里跳的话的Game Over啦

004CF334这个Call,读取了3个MD5值,与KeyFile的MD5比较。估计是检测流传出去的KeyFile的黑名单吧。如果相等的话,嘿嘿,就删除KeyFile,删除注册信息。
我自己构造了一个KeyFile,肯定不在黑名单里啦。所以直接pass,呵呵。

之后来到这里:

00503FBC    8B85 64FDFFFF  mov eax, [ebp-29C]                ; 用户名
00503FC2    8B4D F0        mov ecx, [ebp-10]                ; KeyFile文件名
00503FC5    5A             pop edx
00503FC6    E8 BD65FDFF    call ****CHM.004DA588        ; 解密KeyFile,详细算法参见第7、8、9关
00503FCB    8B85 70FDFFFF  mov eax, [ebp-290]
00503FD1    50             push eax                        ; KeyFile解密后的字串
00503FD2    8D85 58FDFFFF  lea eax, [ebp-2A8]
00503FD8    8D95 F3FEFFFF  lea edx, [ebp-10D]
00503FDE    E8 390FF0FF    call ****CHM.00404F1C        ; System.@LStrFromString(String;String;ShortString;ShortString);
00503FE3    8B85 58FDFFFF  mov eax, [ebp-2A8]                ; 加密后的注册码
00503FE9    8D95 5CFDFFFF  lea edx, [ebp-2A4]
00503FEF    E8 2C24F3FF    call ****CHM.00436420        ; 解密
00503FF4    8B85 5CFDFFFF  mov eax, [ebp-2A4]                ; 注册码
00503FFA    50             push eax
00503FFB    8D85 50FDFFFF  lea eax, [ebp-2B0]
00504001    8D95 8EFEFFFF  lea edx, [ebp-172]
00504007    E8 100FF0FF    call ****CHM.00404F1C        ; System.@LStrFromString(String;String;ShortString;ShortString);
0050400C    8B85 50FDFFFF  mov eax, [ebp-2B0]                ; 加密后的用户名
00504012    8D95 54FDFFFF  lea edx, [ebp-2AC]
00504018    E8 0324F3FF    call ****CHM.00436420        ; 解密
0050401D    8B95 54FDFFFF  mov edx, [ebp-2AC]                ; 用户名
00504023    59             pop ecx                        ; 注册码
00504024    58             pop eax                        ; KeyFile解密后的字串
00504025    E8 1667FDFF    call ****CHM.004DA740        ; 12、13关
0050402A    84C0           test al, al
0050402C    74 10          je short ****CHM.0050403E
0050402E    EB 06          jmp short ****CHM.00504036

004DA740就是关键Call啦,F7跟进:

++++++++++++++++++++++++++++++++++++++++ 第12关:KeyFile的压缩变换 ++++++++++++++++++++++++++++++++++++++++

取KeyFile的第110位,记为a,并转换成数字:

004DA7E2    8D45 D8        lea eax, [ebp-28]
004DA7E5    50             push eax
004DA7E6    B9 01000000    mov ecx, 1
004DA7EB    BA 6E000000    mov edx, 6E
004DA7F0    8B45 FC        mov eax, [ebp-4]                ; KeyFile解密后的字串
004DA7F3    E8 FCA9F2FF    call ****CHM.004051F4        ; System.@LStrCopy;
004DA7F8    8B45 D8        mov eax, [ebp-28]
004DA7FB    E8 E0F6F2FF    call ****CHM.00409EE0        ; SysUtils.StrToInt(AnsiString):Integer;
004DA800    8BD8           mov ebx, eax
004DA802    EB 04          jmp short ****CHM.004DA808

取KeyFile的第1-109位:

004DA83A    8D45 E0        lea eax, [ebp-20]
004DA83D    50             push eax
004DA83E    B9 6D000000    mov ecx, 6D
004DA843    BA 01000000    mov edx, 1
004DA848    8B45 FC        mov eax, [ebp-4]                ; KeyFile解密后的字串
004DA84B    E8 A4A9F2FF    call ****CHM.004051F4        ; System.@LStrCopy;
004DA850    EB 04          jmp short ****CHM.004DA856

取KeyFile的(a+130)-(a+143)位:

004DA86A    8D45 DC        lea eax, [ebp-24]
004DA86D    50             push eax
004DA86E    8D93 8D000000  lea edx, [ebx+8D]
004DA874    B9 04000000    mov ecx, 4
004DA879    8B45 FC        mov eax, [ebp-4]                ; KeyFile解密后的字串
004DA87C    E8 73A9F2FF    call ****CHM.004051F4        ; System.@LStrCopy;

转换成10进制,记为b:

004DA8B9    33D2           xor edx, edx
004DA8BB    8B45 DC        mov eax, [ebp-24]
004DA8BE    E8 59F6F2FF    call ****CHM.00409F1C        ; SysUtils.StrToIntDef(AnsiString;Integer):Integer;
004DA8C3    8BF0           mov esi, eax

b是下面字符串的长度。接着取紧随其后的b个字符,记为Str1:

004DA8E1    8D45 E4        lea eax, [ebp-1C]
004DA8E4    50             push eax
004DA8E5    8D93 91000000  lea edx, [ebx+91]                ; a+144
004DA8EB    8BCE           mov ecx, esi                        ; b
004DA8ED    8B45 FC        mov eax, [ebp-4]                ; KeyFile解密后的字串
004DA8F0    E8 FFA8F2FF    call ****CHM.004051F4        ; System.@LStrCopy;

计算KeyFile的第1-109位的Hash值:

004DA94D    8D4D D0        lea ecx, [ebp-30]
004DA950    33D2           xor edx, edx
004DA952    8B45 E0        mov eax, [ebp-20]                ; 字符串前110位
004DA955    E8 EE56FFFF    call ****CHM.004D0048        ; 自定义的Hash算法,详细算法参见第6关
004DA95A    8B45 D0        mov eax, [ebp-30]                ; KeyFile的第1-109位的Hash字符串
004DA95D    50             push eax

注意:这里计算出了一个Hash字串,下一关要用到。

下面是本关的重头戏,压缩变换:

004DA95E    8D55 CC        lea edx, [ebp-34]
004DA961    8B45 E4        mov eax, [ebp-1C]                ; 取出的b个字节的字符串Str1
004DA964    E8 A74CFFFF    call ****CHM.004CF610        ; 进行压缩变换,F7跟进可看到算法

F7跟进:

---------------------------------------- 004CF610 ----------------------------------------

004CF610    55             push ebp

…………
…………
…………

004CF64A    8D55 F4        lea edx, [ebp-C]
004CF64D    8B45 FC        mov eax, [ebp-4]
004CF650    E8 D3000000    call ****CHM.004CF728        ; 3字节→1字节压缩变换并逆序,F7跟进可看到算法

下面是一个循环:

004CF680    46             inc esi                        ; 变换后字符串长度,作循环计数器
004CF681    33FF           xor edi, edi
004CF683    8B45 F4        mov eax, [ebp-C]                ; 变换后的字符串
004CF686    8A5C38 FF      mov bl, [eax+edi-1]
004CF68A    8B45 F4        mov eax, [ebp-C]
004CF68D    E8 0259F3FF    call ****CHM.00404F94        ; System.@LStrLen(String):Integer;
004CF692    2BC7           sub eax, edi
004CF694    8B55 F4        mov edx, [ebp-C]
004CF697    8A0402         mov al, [edx+eax]
004CF69A    50             push eax
004CF69B    8D45 F4        lea eax, [ebp-C]
004CF69E    E8 495BF3FF    call ****CHM.004051EC
004CF6A3    5A             pop edx
004CF6A4    885438 FF      mov [eax+edi-1], dl
004CF6A8    8B45 F4        mov eax, [ebp-C]
004CF6AB    E8 E458F3FF    call ****CHM.00404F94        ; System.@LStrLen(String):Integer;
004CF6B0    2BC7           sub eax, edi
004CF6B2    50             push eax
004CF6B3    8D45 F4        lea eax, [ebp-C]
004CF6B6    E8 315BF3FF    call ****CHM.004051EC
004CF6BB    5A             pop edx
004CF6BC    881C10         mov [eax+edx], bl
004CF6BF    8B45 F4        mov eax, [ebp-C]
004CF6C2    E8 CD58F3FF    call ****CHM.00404F94        ; System.@LStrLen(String):Integer;
004CF6C7    D1F8           sar eax, 1
004CF6C9    79 03          jns short ****CHM.004CF6CE
004CF6CB    83D0 00        adc eax, 0
004CF6CE    3BF8           cmp edi, eax
004CF6D0    7D 04          jge short ****CHM.004CF6D6
004CF6D2    47             inc edi
004CF6D3    4E             dec esi
004CF6D4  ^ 75 AD          jnz short ****CHM.004CF683

这段循环看着有些长,实际上就是将压缩逆序变换后的字符串再次逆序,负负得正。

以下就返回了……

--------------------------------------------------------------------------------

3字节→1字节压缩变换并逆序:

---------------------------------------- 004CF728 ----------------------------------------

004CF728    55             push ebp

…………
…………
…………

字符串长度除以3作为循环次数:

004CF757    8B45 FC        mov eax, [ebp-4]                ; 字符串
004CF75A    E8 3558F3FF    call ****CHM.00404F94        ; System.@LStrLen(String):Integer;
004CF75F    B9 03000000    mov ecx, 3
004CF764    99             cdq
004CF765    F7F9           idiv ecx
004CF767    8945 F4        mov [ebp-C], eax

循环,进行压缩变换:

004CF787    8B5D F4        mov ebx, [ebp-C]
004CF78A    85DB           test ebx, ebx
004CF78C    7E 40          jle short ****CHM.004CF7CE
004CF78E    BF 01000000    mov edi, 1
004CF793    8D45 EC        lea eax, [ebp-14]
004CF796    50             push eax
004CF797    8BC7           mov eax, edi
004CF799    48             dec eax
004CF79A    8D1440         lea edx, [eax+eax*2]
004CF79D    42             inc edx
004CF79E    B9 03000000    mov ecx, 3
004CF7A3    8B45 FC        mov eax, [ebp-4]                ; 字符串
004CF7A6    E8 495AF3FF    call ****CHM.004051F4        ; System.@LStrCopy;
004CF7AB    8B45 EC        mov eax, [ebp-14]
004CF7AE    E8 2DA7F3FF    call ****CHM.00409EE0        ; SysUtils.StrToInt(AnsiString):Integer;
004CF7B3    8BD0           mov edx, eax
004CF7B5    80F2 FF        xor dl, 0FF                        ; 和0FFh异或就是求反
004CF7B8    8D45 F0        lea eax, [ebp-10]
004CF7BB    E8 E056F3FF    call ****CHM.00404EA0        ; System.@LStrFromChar(String;String;Char);
004CF7C0    8B55 F0        mov edx, [ebp-10]
004CF7C3    8BC6           mov eax, esi
004CF7C5    E8 D257F3FF    call ****CHM.00404F9C        ; System.@LStrCat;
004CF7CA    47             inc edi
004CF7CB    4B             dec ebx
004CF7CC  ^ 75 C5          jnz short ****CHM.004CF793

这是一个3字节→1字节的压缩变换,变换规则是依次将3字节的数字转换成16进制,再求反。
例如:
                   转换             求反
31 38 34(Ascii:184)--→B8(184==0B8h)--→47

下面又是一轮循环:

004CF7E6    BF 01000000    mov edi, 1
004CF7EB    8D45 E8        lea eax, [ebp-18]
004CF7EE    8B55 F4        mov edx, [ebp-C]
004CF7F1    2BD7           sub edx, edi
004CF7F3    8B4D F8        mov ecx, [ebp-8]                ; 压缩后的字符串
004CF7F6    8A1411         mov dl, [ecx+edx]                ; 逆序依次取出字符
004CF7F9    E8 A256F3FF    call ****CHM.00404EA0        ; System.@LStrFromChar(String;String;Char);
004CF7FE    8B55 E8        mov edx, [ebp-18]
004CF801    8BC6           mov eax, esi
004CF803    E8 9457F3FF    call ****CHM.00404F9C        ; System.@LStrCat;
004CF808    47             inc edi
004CF809    4B             dec ebx
004CF80A  ^ 75 DF          jnz short ****CHM.004CF7EB

这个循环将压缩变换后的字符串逆序。

以下就返回了……

--------------------------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++ 第13关:生成RC4密钥 ++++++++++++++++++++++++++++++++++++++++

004D901E    8B45 F8        mov eax, [ebp-8]                ; Hash字串
004D9021    E8 92FEFFFF    call ****CHM.004D8EB8        ; 生成随机数种子,F7跟进可看到算法
004D9026    8B15 48CD5500  mov edx, [55CD48]
004D902C    8902           mov [edx], eax                ; 保存计算得到的值

下面是一个循环:

004D902E    BB FF000000    mov ebx, 0FF                        ; 循环256次
004D9033    8DB5 F2FDFFFF  lea esi, [ebp-20E]
004D9039    B8 FF000000    mov eax, 0FF
004D903E    E8 61A6F2FF    call ****CHM.004036A4        ; system.@RandInt,对这个函数的说明见下面
004D9043    8806           mov [esi], al                ; 保存
004D9045    46             inc esi
004D9046    4B             dec ebx
004D9047  ^ 75 F0          jnz short ****CHM.004D9039

这段循环是生成一个256字节的伪随机序列。这就是RC4的密钥了。

下面是这关用到的2个Call

生成随机数种子:

---------------------------------------- 004D8EB8 ----------------------------------------

004D8EB8    55             push ebp

…………
…………
…………

外层循环:

004D8EEF    C745 F8 010000>mov dword ptr [ebp-8], 1        ; 循环计数器
004D8EF6    33C0           xor eax, eax
004D8EF8    55             push ebp
004D8EF9    68 588F4D00    push ****CHM.004D8F58
004D8EFE    64:FF30        push dword ptr fs:[eax]
004D8F01    64:8920        mov fs:[eax], esp
004D8F04    8B45 FC        mov eax, [ebp-4]
004D8F07    8B55 F8        mov edx, [ebp-8]
004D8F0A    0FB64410 FF    movzx eax, byte ptr [eax+edx-1]
004D8F0F    8D55 EC        lea edx, [ebp-14]
004D8F12    E8 5D0EF3FF    call ****CHM.00409D74        ; SysUtils.IntToStr(Integer):AnsiString;overload;
004D8F17    8B55 EC        mov edx, [ebp-14]
004D8F1A    8D45 F0        lea eax, [ebp-10]
004D8F1D    E8 7AC0F2FF    call ****CHM.00404F9C        ; System.@LStrCat;
004D8F22    8B45 FC        mov eax, [ebp-4]
004D8F25    E8 6AC0F2FF    call ****CHM.00404F94        ; System.@LStrLen(String):Integer;
004D8F2A    8BD8           mov ebx, eax
004D8F2C    85DB           test ebx, ebx
004D8F2E    7E 1E          jle short ****CHM.004D8F4E

内层循环:

004D8F30    BE 01000000    mov esi, 1
004D8F35    8B45 F0        mov eax, [ebp-10]
004D8F38    E8 A30FF3FF    call ****CHM.00409EE0        ; SysUtils.StrToInt(AnsiString):Integer;
004D8F3D    8B55 FC        mov edx, [ebp-4]
004D8F40    0FB65432 FF    movzx edx, byte ptr [edx+esi-1]
004D8F45    F7EA           imul edx
004D8F47    8945 F4        mov [ebp-C], eax
004D8F4A    46             inc esi
004D8F4B    4B             dec ebx
004D8F4C  ^ 75 E7          jnz short ****CHM.004D8F35

外循环的作用是将字符串的前3位依次取Ascii码,转换成10进制,再连成一个字符串。
内循环的作用是将次字符串转为数字,再与原字符串的最后1个字符相乘。

以下就返回了……

--------------------------------------------------------------------------------

system.@RandInt:

说明:
注意函数中的常数,或者叫作Magic Number:8088405!仅仅在看雪搜索这个数值8088405,就可以找到N篇帖子,而且算法竟然一模一样!所以这个函数不可能是作者的算法,一定Delphi的系统函数,但是Dede没有给出分析,不知道这个函数的作用。
后来终于在这里(http://bbs.pediy.com/showthread.php?threadid=13290)找到了,原来这个函数是system.@RandInt(奇怪,不知道为什么我的Dede没有分析出来)。以后看到8088405这个值就知道是这个函数了,可以避免重复劳动。
这里程序用system.@RandInt来生成一个伪随机数作为RC4的密钥。

---------------------------------------- 004036A4(system.@RandInt) ----------------------------------------

004036A4    53             push ebx
004036A5    31DB           xor ebx, ebx
004036A7    6993 08605500 >imul edx, [ebx+556008], 8088405        ; 将随机数种子乘以8088405h
004036B1    42             inc edx                        ; 再+1
004036B2    8993 08605500  mov [ebx+556008], edx        ; 保存
004036B8    F7E2           mul edx                        ; 将此值再与eax相乘
004036BA    89D0           mov eax, edx                        ; 取高32位
004036BC    5B             pop ebx
004036BD    C3             ret

--------------------------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++ 第14关:生成RC4密文 +++++++++++++++++++++++++++++++++++++++

上一关的密钥生成之后,就来到14关:

004D9049    8D95 ECFBFFFF  lea edx, [ebp-414]
004D904F    8B45 FC        mov eax, [ebp-4]                ; Str1经压缩变换后的字符串
004D9052    E8 2D010000    call ****CHM.004D9184        ; 4字节→3字节压缩变换,将Str1转换成密文

这一关的内容不多,就是这一个Call了。关键是它的算法。
F7跟进:

---------------------------------------- 004D9184 ----------------------------------------

004D9184    55             push ebp

…………
…………
…………

004D91AD    C645 F7 00     mov byte ptr [ebp-9], 0
004D91B1    33C0           xor eax, eax
004D91B3    8945 F8        mov [ebp-8], eax
004D91B6    BB 01000000    mov ebx, 1
004D91BB    33F6           xor esi, esi
004D91BD    EB 5A          jmp short ****CHM.004D9219

循环,压缩变换:

004D91BF    85F6           test esi, esi
004D91C1    75 18          jnz short ****CHM.004D91DB
004D91C3    33C0           xor eax, eax
004D91C5    8A441F FF      mov al, [edi+ebx-1]
004D91C9    8A80 BCAA5500  mov al, [eax+55AABC]                ; 查表替换
004D91CF    8845 F7        mov [ebp-9], al
004D91D2    C745 F8 020000>mov dword ptr [ebp-8], 2
004D91D9    EB 39          jmp short ****CHM.004D9214
004D91DB    8D45 F0        lea eax, [ebp-10]
004D91DE    8B4D F8        mov ecx, [ebp-8]
004D91E1    33D2           xor edx, edx
004D91E3    8A55 F7        mov dl, [ebp-9]
004D91E6    D3E2           shl edx, cl
004D91E8    81E2 C0000000  and edx, 0C0
004D91EE    33C9           xor ecx, ecx
004D91F0    8A4C1F FF      mov cl, [edi+ebx-1]
004D91F4    0FB689 BCAA550>movzx ecx, byte ptr [ecx+55AABC]        ; 查表替换
004D91FB    0BD1           or edx, ecx
004D91FD    E8 9EBCF2FF    call ****CHM.00404EA0        ; System.@LStrFromChar(String;String;Char);
004D9202    8B55 F0        mov edx, [ebp-10]
004D9205    8B45 FC        mov eax, [ebp-4]
004D9208    E8 8FBDF2FF    call ****CHM.00404F9C        ; System.@LStrCat;
004D920D    8B45 FC        mov eax, [ebp-4]
004D9210    8345 F8 02     add dword ptr [ebp-8], 2
004D9214    46             inc esi
004D9215    83E6 03        and esi, 3
004D9218    43             inc ebx
004D9219    8BC7           mov eax, edi
004D921B    E8 74BDF2FF    call ****CHM.00404F94        ; System.@LStrLen(String):Integer;
004D9220    3BD8           cmp ebx, eax
004D9222    7F 07          jg short ****CHM.004D922B
004D9224    807C1F FF 2E   cmp byte ptr [edi+ebx-1], 2E
004D9229  ^ 75 94          jnz short ****CHM.004D91BF

以下就返回了……

上面这个循环很让人头痛,不知道是作者从哪里找来的算法。我是在IDA当中分析的。

这是所查的表,姑且也算一个S-Box吧,设为S[i]。
表中有效数值从0-39h:

0055AABC  80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80  ????????
0055AACC  80 80 80 80 80 80 80 80 80 80 80 80 80 80 80 80  ????????
0055AADC  80 80 80 3A 80 3B 3C 80 80 80 3D 3E 80 3F 80 80  ???<??>??
0055AAEC  80 80 00 01 02 03 04 05 06 07 80 80 80 80 80 80  ?.???
0055AAFC  80 08 09 0A 0B 0C 0D 0E 0F 80 10 11 12 13 14 80  ?.. ..??
0055AB0C  15 16 17 18 19 1A 1B 1C 1D 1E 1F 80 80 80 80 80  ???
0055AB1C  80 20 21 22 23 24 25 26 27 28 29 2A 80 2B 2C 2D  ?!"#$%&'()*?,-
0055AB2C  2E 2F 30 31 32 33 34 35 36 37 38 80 80 80 39 80  ./012345678???

下面将其算法用C语言给出:

for(i=0;i<=sizeof(str)&&strIn[i]!=0x2E;i++)
{
        if(!(i%4))
        {
                colume=2;
                key=S[strIn[i]];
        }
        else
        {
                strOut[i-1]=((key<<colume)&0xC0)|S[strIn[i]];
                colume+=2;
        }
}

以上算法以4字节为一组,第1字节为子密钥,将其后的3字节解密,如果遇到2Eh,则停止解密。

下面介绍一下该算法原理。

看算法中的Magic Number:0C0h,为什么要用它呢?
将它转换成2进制看看:1100 0000。
它实际上就是一个掩码。
子密钥的15、14位为0,0-13位为下面3字节密文的15、14位的掩码。
密文的15、14位由子密钥和掩码决定,0-13位由查表得出。
因为子密钥和密文字节都只用到0-13位,也就是0-39h,这就解释了上面S-Box有效值的范围。

至于加密算法嘛,先将明文按3字节分组,然后将3个密文字节的15、14位组合成密钥,再换成在S-Box中的位置。密文的0-13位也分别换成在S-Box中的位置。

--------------------------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++ 第15关:RC4解密 +++++++++++++++++++++++++++++++++++++++

既然密文、密钥都有了,下面就是解密啦:

004D90B3    8D95 F2FDFFFF  lea edx, [ebp-20E]                ; RC4密钥
004D90B9    8D85 F2FBFFFF  lea eax, [ebp-40E]
004D90BF    B9 FF000000    mov ecx, 0FF
004D90C4    E8 67FCFFFF    call ****CHM.004D8D30        ; S-Box初始化,F7跟进可看到算法
004D90C9    68 FF000000    push 0FF                        ; 密钥长度
004D90CE    8D8D F1FEFFFF  lea ecx, [ebp-10F]                ; 解密后明文地址,覆盖掉密文
004D90D4    8D95 F1FEFFFF  lea edx, [ebp-10F]                ; 上一关解密后字串作明文
004D90DA    8D85 F2FBFFFF  lea eax, [ebp-40E]                ; S-Box
004D90E0    E8 1FFDFFFF    call ****CHM.004D8E04        ; RC4加密

RC4加密的代码比较长,我就不贴了。其实算法还是很简单的,见本文开头的说明。

下面是对S-Box的初始化:

---------------------------------------- 004D8D30 ----------------------------------------

004D8D30    53             push ebx
004D8D31    56             push esi
004D8D32    57             push edi
004D8D33    55             push ebp
004D8D34    81C4 FCFEFFFF  add esp, -104
004D8D3A    8BEA           mov ebp, edx
004D8D3C    890424         mov [esp], eax
004D8D3F    85C9           test ecx, ecx
004D8D41    7E 08          jle short ****CHM.004D8D4B
004D8D43    81F9 00010000  cmp ecx, 100                        ; 密钥长度不能大于256位,否则就产生异常啦
004D8D49    7E 16          jle short ****CHM.004D8D61
004D8D4B    B9 E08D4D00    mov ecx, ****CHM.004D8DE0    ; ASCII "Invalid key length"
004D8D50    B2 01          mov dl, 1
004D8D52    A1 3C884000    mov eax, [40883C]
004D8D57    E8 0C52F3FF    call ****CHM.0040DF68        ; AxCtrls.TOleStream.Create(TOleStream;boolean;IStream);
004D8D5C    E8 ABB8F2FF    call ****CHM.0040460C        ; System.@RaiseExcept;

下面的循环对S-Box进行线性填充,生成0-0FFh的数列:

004D8D61    33DB           xor ebx, ebx
004D8D63    8B3424         mov esi, [esp]
004D8D66    8D7C24 04      lea edi, [esp+4]
004D8D6A    881E           mov [esi], bl
004D8D6C    8BC3           mov eax, ebx
004D8D6E    99             cdq
004D8D6F    F7F9           idiv ecx
004D8D71    03D5           add edx, ebp
004D8D73    8A02           mov al, [edx]
004D8D75    8807           mov [edi], al
004D8D77    43             inc ebx
004D8D78    47             inc edi
004D8D79    46             inc esi
004D8D7A    81FB 00010000  cmp ebx, 100
004D8D80  ^ 75 E8          jnz short ****CHM.004D8D6A

下面的循环根据密钥(K[i])扰乱S-Box:

004D8D82    33F6           xor esi, esi
004D8D84    BB 00010000    mov ebx, 100
004D8D89    8B0424         mov eax, [esp]                ; S-Box,S[i]
004D8D8C    8D7C24 04      lea edi, [esp+4]                ; 密钥,K[i]
004D8D90    8A10           mov dl, [eax]
004D8D92    33C9           xor ecx, ecx
004D8D94    8ACA           mov cl, dl
004D8D96    03F1           add esi, ecx
004D8D98    33C9           xor ecx, ecx
004D8D9A    8A0F           mov cl, [edi]
004D8D9C    03F1           add esi, ecx
004D8D9E    81E6 FF000000  and esi, 0FF
004D8DA4    8B0C24         mov ecx, [esp]
004D8DA7    8A0C31         mov cl, [ecx+esi]
004D8DAA    8808           mov [eax], cl
004D8DAC    8B0C24         mov ecx, [esp]
004D8DAF    881431         mov [ecx+esi], dl
004D8DB2    47             inc edi
004D8DB3    40             inc eax
004D8DB4    4B             dec ebx
004D8DB5  ^ 75 D9          jnz short ****CHM.004D8D90

以下就返回了……

--------------------------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++ 第16关:数字和的验证 +++++++++++++++++++++++++++++++++++++++

还记得12关中从KeyFile读取的那个数字b吗?它是Str1的长度。

下面的代码读取KeyFile的(b+244)-(b+247)字节:

004DA9A2    8D45 E8        lea eax, [ebp-18]
004DA9A5    50             push eax
004DA9A6    8D941E F500000>lea edx, [esi+ebx+F5]
004DA9AD    03D3           add edx, ebx
004DA9AF    B9 04000000    mov ecx, 4
004DA9B4    8B45 FC        mov eax, [ebp-4]                ; KeyFile解密后的字串
004DA9B7    E8 38A8F2FF    call ****CHM.004051F4        ; System.@LStrCopy;

转换成10进制,记为c:

004DA9F4    33D2           xor edx, edx
004DA9F6    8B45 E8        mov eax, [ebp-18]
004DA9F9    E8 1EF5F2FF    call ****CHM.00409F1C        ; SysUtils.StrToIntDef(AnsiString;Integer):Integer;
004DA9FE    8BF8           mov edi, eax
004DAA00    EB 04          jmp short ****CHM.004DAA06

和b一样,c也是一个字符串的长度。是哪个字符串呢?就是下面要读取的,记为Str2:

004DAA1A    8D45 EC        lea eax, [ebp-14]
004DAA1D    50             push eax
004DAA1E    8D941E F500000>lea edx, [esi+ebx+F5]
004DAA25    03D3           add edx, ebx
004DAA27    83C2 04        add edx, 4
004DAA2A    8BCF           mov ecx, edi
004DAA2C    8B45 FC        mov eax, [ebp-4]                ; KeyFile解密后的字串
004DAA2F    E8 C0A7F2FF    call ****CHM.004051F4        ; System.@LStrCopy;

004DAA6C    8B45 EC        mov eax, [ebp-14]                ; Str2
004DAA6F    E8 70EBFFFF    call ****CHM.004D95E4        ; 取字符串的数字和的十位(如只有1位,就取个位),F7跟进可看到算法
004DAA74    3BD8           cmp ebx, eax
004DAA76    74 0B          je short ****CHM.004DAA83
004DAA78    EB 04          jmp short ****CHM.004DAA7E        ; 不为0就Game Over啦

呵呵,还真苛刻呢,字符串里每个数字累加的和的十位必须要是0才行。

下面就是这个取十位的算法:

---------------------------------------- 004DAA7E ----------------------------------------

004D95E4    55             push ebp

…………
…………
…………

进入一个循环:

004D962D    BE 01000000    mov esi, 1
004D9632    EB 06          jmp short ****CHM.004D963A

依次取出一个字符:

004D963A    8D45 F8        lea eax, [ebp-8]
004D963D    50             push eax
004D963E    B9 01000000    mov ecx, 1
004D9643    8BD6           mov edx, esi
004D9645    8B45 FC        mov eax, [ebp-4]
004D9648    E8 A7BBF2FF    call ****CHM.004051F4        ; System.@LStrCopy;
004D964D    EB 04          jmp short ****CHM.004D9653

004D9653    33D2           xor edx, edx
004D9655    8B45 F8        mov eax, [ebp-8]
004D9658    E8 BF08F3FF    call ****CHM.00409F1C        ; SysUtils.StrToIntDef(AnsiString;Integer):Integer;
004D965D    03F8           add edi, eax
004D965F    46             inc esi
004D9660    4B             dec ebx
004D9661  ^ 75 CF          jnz short ****CHM.004D9632

这个循环的作用是将字符串的每一位转换成数字并求和。

取和的十位数:

004D9687    8BD0           mov edx, eax                        ; 数字位数
004D9689    4A             dec edx
004D968A    B9 01000000    mov ecx, 1
004D968F    8B45 F8        mov eax, [ebp-8]                ; 求和的数字转换成的字符串
004D9692    E8 5DBBF2FF    call ****CHM.004051F4        ; System.@LStrCopy;
004D9697    8B45 F4        mov eax, [ebp-C]
004D969A    33D2           xor edx, edx
004D969C    E8 7B08F3FF    call ****CHM.00409F1C        ; SysUtils.StrToIntDef(AnsiString;Integer):Integer;

以下就返回了……

--------------------------------------------------------------------------------

++++++++++++++++++++++++++++++++++++++++ 第17关:再次RC4解密 +++++++++++++++++++++++++++++++++++++++

004DAA8B    8D45 C0        lea eax, [ebp-40]
004DAA8E    8B4D F4        mov ecx, [ebp-C]                ; 注册码
004DAA91    8B55 F8        mov edx, [ebp-8]                ; 用户名
004DAA94    E8 47A5F2FF    call ****CHM.00404FE0        ; System.@LStrCat3;
004DAA99    8B45 C0        mov eax, [ebp-40]                ; 用户名+注册码
004DAA9C    8D4D C4        lea ecx, [ebp-3C]
004DAA9F    33D2           xor edx, edx
004DAAA1    E8 4A56FFFF    call ****CHM.004D00F0        ; 又是加上那个长长的字符串再取MD5,这个Call在第7关和第10关都出现过,这里是出现第3次了
004DAAA6    8B45 C4        mov eax, [ebp-3C]                ; 算出的MD5作为初始密钥
004DAAA9    50             push eax
004DAAAA    8D55 BC        lea edx, [ebp-44]
004DAAAD    8B45 EC        mov eax, [ebp-14]                ; Str2
004DAAB0    E8 734CFFFF    call ****CHM.004CF728        ; 压缩变换并逆序,这个Call在第12关中见过(注意:第12关中是2次逆序,负负得正;而这里是1次逆序)
004DAAB5    8B55 BC        mov edx, [ebp-44]                ; Str2经压缩变换并逆序后的字符串
004DAAB8    8D4D C8        lea ecx, [ebp-38]
004DAABB    58             pop eax                        ; MD5值
004DAABC    E8 0FECFFFF    call ****CHM.004D96D0        ; 这里是RC4从初始化到解密,又把13、14、15关过了一遍

把前面几关的算法研究透了,这关其实很简单的。

++++++++++++++++++++++++++++++++++++++++ 第18关:最后的比较 +++++++++++++++++++++++++++++++++++++++

比较Str1和Str2解密后的长度:

004DAAD2    8B45 EC        mov eax, [ebp-14]
004DAAD5    E8 BAA4F2FF    call ****CHM.00404F94        ; System.@LStrLen(String):Integer;
004DAADA    8BD8           mov ebx, eax
004DAADC    8B45 E4        mov eax, [ebp-1C]
004DAADF    E8 B0A4F2FF    call ****CHM.00404F94        ; System.@LStrLen(String):Integer;
004DAAE4    3BD8           cmp ebx, eax
004DAAE6    74 08          je short ****CHM.004DAAF0

比较Str1和Str2解密后的明文:

004DAAF8    8B45 EC        mov eax, [ebp-14]
004DAAFB    8B55 E4        mov edx, [ebp-1C]
004DAAFE    E8 DDA5F2FF    call ****CHM.004050E0        ; System.@LStrCmp;
004DAB03    74 08          je short ****CHM.004DAB0D

再次比较明文,并与用户名比较:

004DAB13    8B45 EC        mov eax, [ebp-14]
004DAB16    8B55 E4        mov edx, [ebp-1C]
004DAB19    E8 C2A5F2FF    call ****CHM.004050E0        ; System.@LStrCmp;
004DAB1E    75 0D          jnz short ****CHM.004DAB2D
004DAB20    8B45 F8        mov eax, [ebp-8]                ; 用户名
004DAB23    8B55 EC        mov edx, [ebp-14]
004DAB26    E8 B5A5F2FF    call ****CHM.004050E0        ; System.@LStrCmp;
004DAB2B    74 04          je short ****CHM.004DAB31
004DAB2D    33C0           xor eax, eax
004DAB2F    EB 02          jmp short ****CHM.004DAB33
004DAB31    B0 01          mov al, 1
004DAB33    8845 F3        mov [ebp-D], al

呵呵,最后原来是跟用户名比较。
如果这3次比较都相等的话,呵呵,我发誓,不会再有第19关啦。到这里就彻底通关啦!

至于KeyFile的构造,只要将上面的算法逆序,生成2个密钥,再将用户名用RC4加密2次,再用前面提到的算法加密,最后写到KeyFile里,再将KeyFile用ZLib压缩,最后AES-256加密就行啦。

其实要顺利通关,这里还有一个小问题:

Str2解密后必须为用户名,那万一Str2的数字和的十位不为0,第16关过不去怎么办?
回顾一下第14关,生成RC4密文的时候,判断了字符串中是否遇到2Eh,如果遇到,就结束循环。那么就可以利用这一点,在后面加上2Eh,然后就可以在后面添加任何数字来使数字和的十位为0了。

后记:到这里终于算是功德圆满啦,算是完美注册成功了。最后感谢论坛上多位朋友的支持和鼓励!

----------------------------------------------------

【版权声明】   本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 7
支持
分享
最新回复 (31)
雪    币: 450
活跃值: (552)
能力值: ( LV9,RANK:690 )
在线值:
发帖
回帖
粉丝
2
好文
完美了
2006-12-4 18:35
0
雪    币: 267
活跃值: (44)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
3
祝贺楼主功德圆满!支持一下!
2006-12-4 18:37
0
雪    币: 2899
活跃值: (1753)
能力值: ( LV9,RANK:850 )
在线值:
发帖
回帖
粉丝
4
不是很好,
……
是特别的好!!
不要看注册信息是否有效,光看注释,就要 I 服了 U。
2006-12-4 18:50
0
雪    币: 452
活跃值: (72)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
5
最初由 wofan[OCN] 发布
不是很好,
……

吓死我了!

至于注释嘛,很多是从Dede里copy过来的,还有就是分析的时候自己加上的,这样自己看着也方便
2006-12-4 18:53
0
雪    币: 1919
活跃值: (901)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
6

恭喜楼主啦,继续支持+学习
2006-12-4 19:34
0
雪    币: 203
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
文章写的不错
2006-12-4 21:14
0
雪    币: 206
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
难得的密码学教程.谢谢!
2006-12-4 21:37
0
雪    币: 146
活跃值: (72)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
9
太强了~
适用最新版。
2006-12-4 22:35
0
雪    币: 716
活跃值: (162)
能力值: ( LV9,RANK:250 )
在线值:
发帖
回帖
粉丝
10
牛人,
2006-12-4 22:39
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
强贴留名啊
2006-12-4 23:00
0
雪    币: 452
活跃值: (72)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
12
看来我的版本旧了嘛,去下载个最新版
2006-12-4 23:32
0
雪    币: 211
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
如果其他几个软件也采用类似算法,那岂不是连锅端!
2006-12-5 14:24
0
雪    币: 250
活跃值: (103)
能力值: ( LV6,RANK:90 )
在线值:
发帖
回帖
粉丝
14
好功夫,佩服!
2006-12-5 15:15
0
雪    币: 442
活跃值: (287)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
15
最初由 Ptero 发布
看来我的版本旧了嘛,去下载个最新版

升级到3.60 b470这个keyfile还是注册成功
2006-12-5 19:42
0
雪    币: 348
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
16
难的一见的好文,这个公司的软件防破强度高,而且打过招呼,目前这是我见到的最详细的破文。楼主强。
2006-12-5 20:33
0
雪    币: 44229
活跃值: (19960)
能力值: (RANK:350 )
在线值:
发帖
回帖
粉丝
17
最初由 Ptero 发布
【破文标题】**CHM 3.50 注册流程及算法分析(续)
........


Ptero 你好!
文章分析的相当不错,应国华软件作者要求,我删除了keyfile成品,还希望你能理解。

下面是国华软件作者的来信,我帖上来,大家讨论一下。论坛现在定位要求是讨论分析过程,而不要散发成品注册机或破解补丁。


看雪你好,

【原创】****CHM 注册流程及算法分析(MD5+AES-256+ZLIB+RC4)(续),附KeyFile

http://bbs1.pediy.com:8081/showthread.php?s=&threadid=35892

http://bbs1.pediy.com:8081/showthread.php?s=&threadid=35623

以上两篇文章分析的很不错,我不反对写分析EASY CHM注册算法的文章 - 事实上我也经常在读此类文章,
但绝对反对任何恶意散步注册码及注册机的行为 - 特别是当注册文件被上传到你的服务器上。

我想Ptero也不是有意在散布EASY CHM的注册机,或许是由于喜悦,或许是想证实一下自己的分析正确而已,但是,
从昨天晚上他上传注册文件后,我想这个注册文件就已经注定会满天飞。

我个人只希望下次EASY CHM的注册文件散布发源地不再是看雪论坛;大家分析注册算法的时候也请点到为止 - 这样或许更有利于技术学习,以免被人恶意利用。

请给与协助,谢谢,同时也感谢你之前对解决此类问题的态度,我也看到了你尽量把EASY CHM的程序名隐去的做法,当然也看到了你亲自删除了那个肆意谩骂软件作者的帖子,可以理解想维护一个好的技术讨论环境实属不易。

也请转告Ptero,EASY CHM软件的加密远不如他想像的那么复杂,也不必为此过于兴奋,有些地方他想的太复杂了,反倒绕了很多弯路。当开始写软件之后,我就明白注册算法不应该很复杂,只是多一点技巧就可以了 - 因为越复杂的注册算法,会让用户在使用的时候消耗更多不必要的时间。所以作为CRACKER的Ptero,在分析完EASY CHM后,倘若他能理解软件作者的这种不想消耗用户资源的苦衷,或许喜悦之情会大大减少。

软件作品毕竟不是CRACKEME,只能做有限的保护而已。


如果可以,你也可以帮我把这封信发到论坛上,大家可以探讨。

冯国华

有问题请随时与我们联系!

国华软件
------------------
http://www.eTextWizard.com
webmaster@etextwizard.com
2006-12-5 21:12
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
删了也没关系,俺已经下了,

2006-12-5 21:41
0
雪    币: 146
活跃值: (72)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
19
支持讨论技术.
软件作者也说了,保护只是有限的,因此,从这点上讲,没有破解不了的软件,有的只是需不需要花时间去做.所以,坛主为我们提供了这么一个学习的环境,每个成员都应该共同维护好,理解坛主一片苦心.
2006-12-5 22:01
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
20
正好用到这个软件,本也想试着分析一下没想精贴都出了,顶一下.
也支持坛主的管理风格
2006-12-5 23:16
0
雪    币: 207
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
21
我有话说,是我骂了冯国华。
看到这封信,我觉得冯国华有点太高估自己了。软件的功能不咋样,加密却是这么用心,有点不对劲。如果你能将主要精力放在软件质量功能上面,那是很值得赞扬的。但是你来看雪论坛不是来讨论编程技术的,却是来看有没有人破你的软件,这是啥道理呢?

chm编译软件很多,其中免费的也多。你的EASY CHM和那些免费软件比较一下,比别人强多少呢?很早以前我就用过EASY CHM,用过几次之后,觉得很垃圾啊,虽然也有注册码,但干脆彻底删除,免得用起来心烦。

希望冯国华好好考虑一下,是不是写一个软件就要收费?这个软件或者这种软件有没有商业化的价值?我的意见是,你网站那些软件统统免费,像EASY CHM这么简单的软件就开源,让大家借鉴一下你的代码。不要满脑子装的都是钱,要尽点责任。想想现在还是社会主义初级阶段,还是计算机普及阶段。如果你不愿意尽点责任,那么社会主义初级阶段的法制也不健全,很难惩罚破解者,所以这个社会还是很公平的,为啥要建设和谐社会呢?也不要动不动就给看雪大哥写信,说有人发布了KeyFile,你的心情大家都理解,但是你也要理解这个社会主义初级阶段+计算机普及阶段!1个EASY CHM注册费就要99元!我的天啊,这难道不是明显的打劫吗?想想中国人均收入才多少啊?话不多说了,有啥不对的地方,欢迎讨论啊。
2006-12-6 01:32
0
雪    币: 51
活跃值: (1841)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
如果这软件真像楼上说的那么差的话,估计也没人会去研究它的加密了。应该还是由它的可取之处吧
2006-12-6 02:24
0
雪    币: 452
活跃值: (72)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
23
最初由 kanxue 发布
从昨天晚上他上传注册文件后,我想这个注册文件就已经注定会满天飞。


这点是我不对,向冯国华先生道歉。
这个KeyFile在下一版本中一定会被列入黑名单的。
以后不会在看雪上发布国华软件的注册码和注册文件了。

最初由 wfwf 发布
删了也没关系,俺已经下了,

也希望已经下载了KeyFile的朋友只作学习和研究用,不要散播注册文件。
2006-12-6 08:06
0
雪    币: 1919
活跃值: (901)
能力值: ( LV9,RANK:490 )
在线值:
发帖
回帖
粉丝
24
我有空打算仔细研究哈它的加密方式并遵守论坛的制度。
冯国华先生既然提到了现在的解密方式过于复杂下步就要研究哈有没有更简单的方法,同时也希望作者看了后能继续改进它的加密方式,最后希望下了注册文件的兄弟不要传播它
2006-12-6 09:52
0
雪    币: 452
活跃值: (72)
能力值: ( LV9,RANK:330 )
在线值:
发帖
回帖
粉丝
25
最初由 yijun8354 发布
我有空打算仔细研究哈它的加密方式并遵守论坛的制度。
…………下步就要研究哈有没有更简单的方法…………

期待楼上的研究成果
2006-12-6 10:14
0
游客
登录 | 注册 方可回帖
返回
//