中国IT动力,最新最全的IT技术教程
最新100篇 | 推荐100篇 | 专题100篇 | 排行榜 | 搜索 | 在线API文档
首 页 | 程序开发 | 操作系统 | 软件应用 | 图形图象 | 网络应用 | 精文荟萃 | 教育认证 | 硬件维护 | 未整理篇 | 站长教程
ASP JS PHP工程 ASP.NET 网站建设 UML J2EESUN .NET VC VB VFP 网络维护 数据库 DB2 SQL2000 Oracle Mysql
服务器 Win2000 Office C DreamWeaver FireWorks Flash PhotoShop 上网宝典 CorelDraw 协议大全 网络安全 微软认证
硬件维护  CPU  主板  硬盘  内存  显卡  显示器  键盘鼠标  声卡音箱  打印机  机箱电源  BIOS  网卡  C#  Java  Delphi  vs.net2005
  当前位置:> 看雪学院专区 > 软件调试
****CHM 注册流程及算法分析(MD5+AES-256+ZLIB+RC4)(续)
作者:佚名 时间:2006-12-11 21:30 出处:pediy.com 责编:月夜寒箫
              摘要:****CHM 注册流程及算法分析(MD5+AES-256+ZLIB+RC4)(续)
【破文标题】**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 alal
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 alal
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 ebxeax
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 edxedx
004DA8BB    8B45 DC        mov eax, [ebp-24]
004DA8BE    E8 59F6F2FF    call ****CHM.00409F1C  ; SysUtils.StrToIntDef(AnsiString;Integer):Integer;
004DA8C3    8BF0           mov esieax

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 ecxesi      ; 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 edxedx
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 ediedi
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 eaxedi
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 eaxedi
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 edieax
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 ebxebx
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 eaxedi
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 edxeax
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 eaxesi
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 edxedi
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 eaxesi
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 eaxeax
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 eaxbyte 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 ebxeax
004D8F2C    85DB           test ebxebx
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 edxbyte 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 ebxebx
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 eaxedx      ; 取高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 eaxeax
004D91B3    8945 F8        mov [ebp-8], eax
004D91B6    BB 01000000    mov ebx, 1
004D91BB    33F6           xor esiesi
004D91BD    EB 5A          jmp short ****CHM.004D9219

循环,压缩变换:

004D91BF    85F6           test esiesi
004D91C1    75 18          jnz short ****CHM.004D91DB
004D91C3    33C0           xor eaxeax
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 edxedx
004D91E3    8A55 F7        mov dl, [ebp-9]
004D91E6    D3E2           shl edxcl
004D91E8    81E2 C0000000  and edx, 0C0
004D91EE    33C9           xor ecxecx
004D91F0    8A4C1F FF      mov cl, [edi+ebx-1]
004D91F4    0FB689 BCAA550>movzx ecxbyte ptr [ecx+55AABC]  ; 查表替换
004D91FB    0BD1           or edxecx
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 eaxedi
004D921B    E8 74BDF2FF    call ****CHM.00404F94  ; System.@LStrLen(String):Integer;
004D9220    3BD8           cmp ebxeax
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€€€9€

下面将其算法用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 ebpedx
004D8D3C    890424         mov [esp], eax
004D8D3F    85C9           test ecxecx
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 ebxebx
004D8D63    8B3424         mov esi, [esp]
004D8D66    8D7C24 04      lea edi, [esp+4]
004D8D6A    881E           mov [esi], bl
004D8D6C    8BC3           mov eaxebx
004D8D6E    99             cdq
004D8D6F    F7F9           idiv ecx
004D8D71    03D5           add edxebp
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 esiesi
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 ecxecx
004D8D94    8ACA           mov cldl
004D8D96    03F1           add esiecx
004D8D98    33C9           xor ecxecx
004D8D9A    8A0F           mov cl, [edi]
004D8D9C    03F1           add esiecx
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 edxebx
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 edxedx
004DA9F6    8B45 E8        mov eax, [ebp-18]
004DA9F9    E8 1EF5F2FF    call ****CHM.00409F1C  ; SysUtils.StrToIntDef(AnsiString;Integer):Integer;
004DA9FE    8BF8           mov edieax
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 edxebx
004DAA27    83C2 04        add edx, 4
004DAA2A    8BCF           mov ecxedi
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 ebxeax
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 edxesi
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 edxedx
004D9655    8B45 F8        mov eax, [ebp-8]
004D9658    E8 BF08F3FF    call ****CHM.00409F1C  ; SysUtils.StrToIntDef(AnsiString;Integer):Integer;
004D965D    03F8           add edieax
004D965F    46             inc esi
004D9660    4B             dec ebx
004D9661  ^ 75 CF          jnz short ****CHM.004D9632

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

取和的十位数:

004D9687    8BD0           mov edxeax      ; 数字位数
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 edxedx
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 edxedx
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 ebxeax
004DAADC    8B45 E4        mov eax, [ebp-1C]
004DAADF    E8 B0A4F2FF    call ****CHM.00404F94  ; System.@LStrLen(String):Integer;
004DAAE4    3BD8           cmp ebxeax
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 eaxeax
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了。



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

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

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


关闭本页
 
首页 | 投资与合作 | 服务条款 | 隐私政策 | 收藏本站 | 设为首页 | 新用户注册 | 免责声明 | 使用帮助
Copyright ©2005-2008 chinaitpower.com All rights reserved. www.chinaitpower.com 版权所有