【破文标题】**CHM 3.50 注册流程及算法分析
【破文作者】Ptero
【破解工具】FI,OllyDbg,Dede,IDA,MD5工具,AES库,ZLib库
【注册方式】序列号+KeyFile
【保护方式】花指令,自校检,进程检测,API断点检测
【加壳方式】UPX v0.94-1.90
【加密算法】MD5+AES-256(Rijndael)+ZLib
【软件限制】功能限制
【破解难度】难
【破解声明】初学Crack,只是感兴趣,没有其它目的。失误之处敬请诸位大侠赐教!
----------------------------------------------------
【破解分析】
最近要做一个chm文件,就找到了这个软件。正好这几天有空,就拿它开刀啦!
初次接触加密算法保护的软件,就碰上了这么一个家伙,耗掉了我N个晚上……
关于AES:网上有很多了,我就不多说了。附件里有一个Delphi的AES库。
关于ZLib:这是Zip文件采用的算法,开源。有关资料可以去这里找:http://www.zlib.net
下面开始闯关:++++++++++++++++++++++++++++++++++++++++ 第1关:脱壳 ++++++++++++++++++++++++++++++++++++++++
用FI查壳:UPX v0.94-1.90。简单脱掉,不多说了。
加这么弱的壳,不是菜鸟,就是对自己的注册算法很有信心。呵呵,我们看看是哪一种吧……++++++++++++++++++++++++++++++++++++++++ 第2关:自校检 ++++++++++++++++++++++++++++++++++++++++
脱壳后运行程序会提示错误信息,然后程序崩溃。
走捷径:按照askformore给出的方法(http://bbs.pediy.com/showthread.php?threadid=7382 ),搜索cmp dword [ebp -0A],0(共2个)然后把后面的条件跳转改成nop ,顺利过关。++++++++++++++++++++++++++++++++++++++++ 第3关:检测敌意进程 ++++++++++++++++++++++++++++++++++++++++
程序检测敌意进程共3种方法:
1. FindWindow
这是黑名单:
Class:OWL_Window ; 没见过这个
Class:TIdaWindow ; IDA
Class:OLLYDBG ; OllyDbg
Title:TRW2000 ; TRW2000
Class:TDeDeMainform ; Dede
Class:FileMonClass ; FileMon
Class:RegMonClass ; RegMon
Class:SnackerClass ; 没见过这个
Title:OLLYDBG ; OllyDbg
Title:WinHex ; WinHex
Title:ProcDump32 (C) 1998, 1999, 2000 G-RoM, Lorian & Stone ; ProcDump32
Title:URSoft W32Dasm Ver 8.93 Program Disassembler/Debugger ; W32Dasm
Title:NuMega SoftICE Symbol Loader ; SoftICE
Title:TRW2000 for Windows 9x ; TRW2000
Class:aqpqpxt|{szgx ; 没见过这个
Title:DarK 3.50.04 (c) 1999-2003 by DaFixer/TMG ; Dede
解决方法:
把这里:
0055536B E8 3CF1FFFF call fixed.005544AC
00555370 EB 04 jmp short fixed.00555376
全部nop 掉,就可以了。
2. EnumWindows
这是黑名单:
ExeSpy
wxr95
Regmon
File Monitor
RegMonEx
Window Detective
DebugView
ResSpy
Advanced Registry Tracer
regsnap
MEMSPY
Memory Doctor
ProcDump32
Memory Editor
FrogsICE
SMU Winspector
Memory Dumper
MemoryMonitor
Regmon ; 检测了2次。作者很怕这个吗?
DeDe
OpenTrap
Registry Monitor
InCtrl
解决方法:
把这里:
00407C9C - FF25 2CD77500 jmp near [<&user32.#223>] ; user32.EnumWindows
改为:
00407C9C C2 0800 ret 8
00407C9F 90 nop
00407CA0 90 nop
00407CA1 90 nop
3. EnumThreadWindows
这里我没有跟踪就直接修补了,不知道它检测的是哪些程序。
解决方法:
把这里:
00407C94 - FF25 30D77500 jmp near [<&user32.#220>] ; user32.EnumThreadWindows
改为:
00407C94 C2 0C00 ret 0C
00407C97 90 nop
00407C98 90 nop
00407C99 90 nop
修改完毕,把修改过的文件重命名为fixed.exe,顺利过关。++++++++++++++++++++++++++++++++++++++++ 第4关:检测API int3断点 ++++++++++++++++++++++++++++++++++++++++
有若干个函数是用来检测int3断点的。
被检测的API如下:
MessageBoxA
GetWindowTextA
CreateFileA
DialogBoxParamA
GetModuleHandleA
GetWindowA
GetDlgItemTextA
CompareFileTimeA
GetLocalTimeA
GetSystemTimeA
GetTimeZoneInformationA
然而,作者检测的是程序中跳到API的地方的断点,所以还是可以直接在API入口出下断点的,而且并没有检测操作注册表的函数。难道是作者在放水吗?
不管怎么说,这一关形同虚设,直接pass。++++++++++++++++++++++++++++++++++++++++ 第5关:验证注册码 ++++++++++++++++++++++++++++++++++++++++
用FI查看:Borland Delphi。所以要用Dede分析。
还有,IDA 5.0的流程图功能不错,大大提高了效率,所以也用IDA分析。
最后,用OD加载,忽略所有异常。
在0052DBAD处下断点,输入用户名、注册码,断下:
获得用户名长度:
0052DBAD 8B45 F8 mov eax , [ebp -8]
0052DBB0 E8 DF73EDFF call fixed.00404F94 ; System.@LStrLen(String):Integer;
0052DBB5 83F8 02 cmp eax , 2 ; 如果用户名长度<2,就Game Over啦
0052DBB8 7D 08 jge short fixed.0052DBC2
0052DBBA 8D45 F8 lea eax , [ebp -8]
0052DBBD E8 F670EDFF call fixed.00404CB8 ; System.@LStrClr(void;void);
0052DBC2 EB 04 jmp short fixed.0052DBC8
获得注册码长度:
0052DBC8 8B45 F4 mov eax , [ebp -C]
0052DBCB E8 C473EDFF call fixed.00404F94 ; System.@LStrLen(String):Integer;
0052DBD0 83F8 13 cmp eax , 13 ; 如果注册码长度<19,就Game Over啦
0052DBD3 7D 08 jge short fixed.0052DBDD
0052DBD5 8D45 F4 lea eax , [ebp -C]
0052DBD8 E8 DB70EDFF call fixed.00404CB8 ; System.@LStrClr(void;void);
0052DBDD EB 04 jmp short fixed.0052DBE3
可以看到用户名长度要大于等于2,注册码长度要大于等于19才行。
检验注册码中是否包含字符'-':
004FF49D 8D45 EC lea eax , [ebp -14]
004FF4A0 8B15 28CC5500 mov edx , [55CC28]
004FF4A6 8A52 01 mov dl , [edx +1] ; '-'
004FF4A9 E8 F259F0FF call fixed.00404EA0 ; System.@LStrFromChar(String;String;Char);
004FF4AE 8B45 EC mov eax , [ebp -14]
004FF4B1 8B55 FC mov edx , [ebp -4]
004FF4B4 E8 1F5EF0FF call fixed.004052D8 ; System.@LStrPos;
004FF4B9 85C0 test eax , eax ; 如果注册码中不含'-',就Game Over啦
004FF4BB 75 0D jnz short fixed.004FF4CA
004FF4BD EB 06 jmp short fixed.004FF4C5
这里计算注册码前2位的MD5值:
004CFAA3 8D55 FC lea edx , [ebp -4]
004CFAA6 8B45 F8 mov eax , [ebp -8] ; 注册码的前2位
004CFAA9 E8 FEA5FFFF call fixed.004CA0AC ; 计算MD5值
如果一路F7跟进的话可以在这里看到MD5的特征码:
004C9D08 C700 01234567 mov dword ptr [eax ], 67452301
004C9D0E C740 04 89ABCD>mov dword ptr [eax +4], EFCDAB89
004C9D15 C740 08 FEDCBA>mov dword ptr [eax +8], 98BADCFE
004C9D1C C740 0C 765432>mov dword ptr [eax +C], 10325476
循环6次,比较2个MD5值:
004CFAB4 BE 06000000 mov esi , 6
004CFAB9 BF 98785500 mov edi , fixed.00557898
004CFABE EB 06 jmp short fixed.004CFAC6
004CFAC6 8B17 mov edx , [edi ] ; 内存中的某个MD5值
004CFAC8 8B45 FC mov eax , [ebp -4] ; 注册码前2位的MD5值
004CFACB E8 B09BF3FF call fixed.00409680 ; SysUtils.SameText(AnsiString;AnsiString):Boolean;
004CFAD0 8BD8 mov ebx , eax
004CFAD2 EB 04 jmp short fixed.004CFAD8
004CFAD8 84DB test bl , bl
004CFADA 75 06 jnz short fixed.004CFAE2 ; 如果与6个值都不相等,就Game Over啦
004CFADC 83C7 04 add edi , 4
004CFADF 4E dec esi
004CFAE0 ^75 DC jnz short fixed.004CFABE
内存中的6个MD5值和明文分别是:
MD5值 明文"3e850aae9e730e9fee413f5219abd997" "QE" "42983b05e2f2cc22822e30beb7bdd668" "CO" "3fd6b696867d70225deda7868308679b" "EC" "c562607189d77eb9dfb707464c1e7b0b" "LT" "ae41a6d38b78679b4675941ff0c0c92d" "ET" "b2e68ec1fa49da52ca5b0f436e032dd6" "LD"
也就是说,注册码的前2位必须要等于上面6者之一。
如果是"QE" 、"EC" 、"ET" 的情况,在这里可以看到相应的注册类型:
004FF66F 8B45 F8 mov eax , [ebp -8] ; "Single User License"
004FF672 E8 1D59F0FF call fixed.00404F94
如果是"CO" 、"LT" 、"LD" 的情况,则在这里:
取注册码的3-6位(0-9或A-F)并转为16进制:
004FF56A 8B45 F8 mov eax , [ebp -8] ; 注册码的3-6位
004FF56D E8 CEFDFFFF call fixed.004FF340 ; 转为16进制(大写)
004FF572 8BF0 mov esi , eax
004FF574 83F6 4F xor esi , 4F ; 与4Fh异或
004FF577 EB 04 jmp short fixed.004FF57D
004FF57D 83FE 01 cmp esi , 1
004FF580 75 20 jnz short fixed.004FF5A2
004FF582 EB 04 jmp short fixed.004FF588 ; 等于1就跳到"Single User License"
不等于就跳到这里:
004FF5A2 EB 06 jmp short fixed.004FF5AA
004FF5AA 8D55 F8 lea edx , [ebp -8]
004FF5AD 8BC6 mov eax , esi
004FF5AF E8 C0A7F0FF call fixed.00409D74 ; SysUtils.IntToStr(Integer):AnsiString;overload;
004FF5B4 EB 04 jmp short fixed.004FF5BA
004FF5BA 8D55 E0 lea edx , [ebp -20]
004FF5BD A1 28CB5500 mov eax , [55CB28]
004FF5C2 E8 1578F0FF call fixed.00406DDC ; 取加密字符串
004FF5C7 8B45 E0 mov eax , [ebp -20]
004FF5CA 8D55 E4 lea edx , [ebp -1C]
004FF5CD E8 CEFAFFFF call fixed.004FF0A0 ; 解密
004FF5D2 8B55 E4 mov edx , [ebp -1C] ; " User Licences"
004FF5D5 8D45 F8 lea eax , [ebp -8]
004FF5D8 E8 BF59F0FF call fixed.00404F9C ; System.@LStrCat;
到这里就明白了:这是多用户许可,注册码3-6位异或4Fh为许可证数量!
到这里,注册码的验证工作就算完毕了。什么?居然与用户名没有关系?是的。这是KeyFile的注册方式啊!这里只是万里长征的第一步呢!
下面就要存放注册信息了:
加密用户名:
0052DC23 8D95 70FDFFFF lea edx , [ebp -290]
0052DC29 8B45 F8 mov eax , [ebp -8] ; 用户名
0052DC2C E8 F786F0FF call fixed.00436328 ; 将用户名的每一位加密
加密注册码:
0052DC60 8D95 6CFDFFFF lea edx , [ebp -294]
0052DC66 8B45 F4 mov eax , [ebp -C] ; 注册码
0052DC69 E8 BA86F0FF call fixed.00436328 ; 将注册码的每一位加密
加密注册类型:
0052DC9F 8D95 68FDFFFF lea edx , [ebp -298]
0052DCA5 8B45 F0 mov eax , [ebp -10] ; 注册类型
0052DCA8 E8 7B86F0FF call fixed.00436328 ; 将注册信息的每一位加密
获取日期时间:
0052DCDC E8 F3E3EDFF call fixed.0040C0D4 ; SysUtils.Date:TDateTime;
下面是一个Call :
0052DD20 E8 2710FAFF call fixed.004CED4C
如果F7跟进的话,就能看到程序把以上4项数据放在注册表"HKCR\CLSID\{2757D5E0-7A80-11D8-A102-00E0C6843FFA}\ProgInfo" 下的"ProgData" 里。
好,如果能到达这里,就顺利过关啦。++++++++++++++++++++++++++++++++++++++++ 第6关:计算KeyFile文件名 ++++++++++++++++++++++++++++++++++++++++
因为程序把注册信心都存放在注册表里,所以就下注册表函数的断点。
在RegQueryValueExA处下条件断点:[esp +8]=="ProgData" &&[esp +14]!=0
中断2次后取消断点。看堆栈中的"Buffer" ,在其指向的内存上设内存访问断点,F9运行。
中断在这里:
00404F1E 8A0A mov cl , [edx ]
00404F20 42 inc edx
00404F21 ^ E9 82FEFFFF jmp fixed.00404DA8
取消内存断点,Ctrl+F9返回:
0054F332 8B45 F8 mov eax , [ebp -8]
0054F335 50 push eax
0054F336 8D45 F4 lea eax , [ebp -C]
0054F339 8B55 FC mov edx , [ebp -4]
0054F33C 81C2 C8050000 add edx , 5C8 ; edx指向加密后的注册信息
0054F342 E8 D55BEBFF call fixed.00404F1C ; System.@LStrFromString(String;String;ShortString;ShortString);
0054F347 8B45 F4 mov eax , [ebp -C]
0054F34A 5A pop edx
0054F34B E8 7003FBFF call fixed.004FF6C0 ; 这就是关键Call
0054F350 84C0 test al , al ; 经典的cmp/test+jz/jnz指令
0054F352 0F85 AD000000 jnz fixed.0054F405 ; 这里就是爆破点,改成jmp就是注册版啦!
F7跟进这个Call ,来到这里:
计算KeyFile文件名:
004FF74B 8D55 E4 lea edx , [ebp -1C]
004FF74E 8B45 F8 mov eax , [ebp -8] ; 加密后的注册码
004FF751 E8 CA6CF3FF call fixed.00436420 ; 解密
004FF756 8B45 E4 mov eax , [ebp -1C] ; 注册码
004FF759 50 push eax
004FF75A 8D55 E0 lea edx , [ebp -20]
004FF75D 8B45 FC mov eax , [ebp -4] ; 加密后的用户名
004FF760 E8 BB6CF3FF call fixed.00436420 ; 解密
004FF765 8B45 E0 mov eax , [ebp -20] ; 用户名
004FF768 8D4D F0 lea ecx , [ebp -10]
004FF76B 5A pop edx
004FF76C E8 139CFDFF call fixed.004D9384 ; 根据用户名和注册码的一部分计算出KeyFile文件名,具体过程见下文
04FF771 33C0 xor eax , eax
004FF773 5A pop edx
004FF774 59 pop ecx
004FF775 59 pop ecx
004FF776 64:8910 mov fs :[eax ], edx
004FF779 EB 31 jmp short fixed.004FF7AC
检验文件是否存在:
004FF7AC 8D55 DC lea edx , [ebp -24] ; KeyFile文件名
004FF7AF 8B45 F0 mov eax , [ebp -10]
004FF7B2 E8 FD9FF0FF call fixed.004097B4 ; SysUtils.Trim(AnsiString):AnsiString;overload;
004FF7B7 837D DC 00 cmp dword ptr [ebp -24], 0
004FF7BB 74 0C je short fixed.004FF7C9
004FF7BD 8B45 F0 mov eax , [ebp -10]
004FF7C0 E8 27ACF0FF call fixed.0040A3EC ; SysUtils.FileExists(AnsiString):Boolean;
004FF7C5 84C0 test al , al
004FF7C7 75 18 jnz short fixed.004FF7E1 ; 如果文件不存在,就Game Over啦
如果KeyFile存在就跳到这里:
004FF7E1 EB 04 jmp short fixed.004FF7E7
004FF7E7 8D45 D8 lea eax , [ebp -28]
004FF7EA 50 push eax
004FF7EB 8D55 D4 lea edx , [ebp -2C]
004FF7EE 8B45 F8 mov eax , [ebp -8] ; 加密后的注册码
004FF7F1 E8 2A6CF3FF call fixed.00436420 ; 解密
004FF7F6 8B45 D4 mov eax , [ebp -2C] ; 注册码
004FF7F9 50 push eax
004FF7FA 8D55 D0 lea edx , [ebp -30]
004FF7FD 8B45 FC mov eax , [ebp -4] ; 加密后的用户名
004FF800 E8 1B6CF3FF call fixed.00436420 ; 解密
004FF805 8B45 D0 mov eax , [ebp -30] ; 用户名
004FF808 8B4D F0 mov ecx , [ebp -10] ; KeyFile文件名
004FF80B 5A pop edx
004FF80C E8 879FFDFF call fixed.004D9798 ; 这里进入7、8、9三关,KeyFile解密
004FF811 8B45 D8 mov eax , [ebp -28] ; KeyFile解密后的字串
004FF814 E8 3BA1FDFF call fixed.004D9954 ; 进入最后一关第10关!
好啦,到这里就可以进下一关啦!
下面是这关用到的一些函数:
根据用户名和注册码的一部分计算出KeyFile文件名:
---------------------------------------- 004D9384 ----------------------------------------
004D9384 55 push ebp
…………
…………
…………
004D93FF 8B45 F8 mov eax , [ebp -8] ; 注册码
004D9402 E8 4566FFFF call fixed.004CFA4C ; 比较注册码的前2位是否合法
004D9407 84C0 test al , al
004D9409 75 0B jnz short fixed.004D9416
注册码前2位合法就跳到这里:
004D9416 EB 04 jmp short fixed.004D941C
取注册码的倒数第13-10位:
004D941C 33C0 xor eax , eax
004D941E 55 push ebp
004D941F 68 55944D00 push fixed.004D9455
004D9424 64:FF30 push dword ptr fs :[eax ]
004D9427 64:8920 mov fs :[eax ], esp
004D942A 8D45 F0 lea eax , [ebp -10]
004D942D 50 push eax
004D942E 8B45 F8 mov eax , [ebp -8] ; 注册码
004D9431 E8 5EBBF2FF call fixed.00404F94 ; System.@LStrLen(String):Integer;
004D9436 8BD0 mov edx , eax
004D9438 83EA 03 sub edx , 3
004D943B 83EA 09 sub edx , 9
004D943E B9 04000000 mov ecx , 4
004D9443 8B45 F8 mov eax , [ebp -8] ; 注册码
004D9446 E8 A9BDF2FF call fixed.004051F4 ; System.@LStrCopy
004D944B 33C0 xor eax , eax
004D944D 5A pop edx
004D944E 59 pop ecx
004D944F 59 pop ecx
004D9450 64:8910 mov fs :[eax ], edx
004D9453 EB 20 jmp short fixed.004D9475
004D9475 EB 06 jmp short fixed.004D947D
循环,异或用户名的每一位并相加:
004D947D 8B45 FC mov eax , [ebp -4] ; 用户名
004D9480 E8 0FBBF2FF call fixed.00404F94 ; System.@LStrLen(String):Integer;
004D9485 85C0 test eax , eax
004D9487 7E 21 jle short fixed.004D94AA
004D9489 BA 01000000 mov edx , 1
004D948E EB 04 jmp short fixed.004D9494
004D9494 8B4D FC mov ecx , [ebp -4]
004D9497 8A4C11 FF mov cl , [ecx +edx -1] ; 取用户名的每一位
004D949B 80F1 5C xor cl , 5C ; 异或5Ch
004D949E 81E1 FF000000 and ecx , 0FF
004D94A4 03D9 add ebx , ecx ; 累加
004D94A6 42 inc edx
004D94A7 48 dec eax
004D94A8 ^ 75 E4 jnz short fixed.004D948E
004D94B0 33C0 xor eax , eax
004D94B2 55 push ebp
004D94B3 68 EA944D00 push fixed.004D94EA
004D94B8 64:FF30 push dword ptr fs :[eax ]
004D94BB 64:8920 mov fs :[eax ], esp
004D94BE 33D2 xor edx , edx
004D94C0 8B45 F0 mov eax , [ebp -10]
004D94C3 E8 540AF3FF call fixed.00409F1C ; SysUtils.StrToIntDef(AnsiString;Integer):Integer;
004D94C8 83F3 2F xor ebx , 2F ; 将用户名的累加和异或2Fh
004D94CB 03C3 add eax , ebx ; 再加上注册码的倒数13-10位
004D94CD 8D55 E8 lea edx , [ebp -18]
004D94D0 E8 9F08F3FF call fixed.00409D74 ; SysUtils.IntToStr(Integer):AnsiString;overload;
004D94D5 8B55 E8 mov edx , [ebp -18]
004D94D8 8D45 F0 lea eax , [ebp -10]
004D94DB E8 70B8F2FF call fixed.00404D50 ; System.@LStrLAsg(void;void;void;void);
004D94E0 33C0 xor eax , eax
004D94E2 5A pop edx
004D94E3 59 pop ecx
004D94E4 59 pop ecx
004D94E5 64:8910 mov fs :[eax ], edx
004D94E8 EB 20 jmp short fixed.004D950A
004D950A EB 06 jmp short fixed.004D9512
004D9512 33C0 xor eax , eax
004D9514 55 push ebp
004D9515 68 64954D00 push fixed.004D9564
004D951A 64:FF30 push dword ptr fs :[eax ]
004D951D 64:8920 mov fs :[eax ], esp
004D9520 EB 04 jmp short fixed.004D9526
004D9526 8D55 E0 lea edx , [ebp -20]
004D9529 8B45 F8 mov eax , [ebp -8] ; 注册码
004D952C E8 DF65FFFF call fixed.004CFB10
004D9531 FF75 E0 push dword ptr [ebp -20] ".dll"
004D9534 FF75 FC push dword ptr [ebp -4] 用户名
004D9537 FF75 F0 push dword ptr [ebp -10] 注册码倒数13-10位和用户名计算结果
004D953A FF75 F8 push dword ptr [ebp -8] 注册码
004D953D FF75 F0 push dword ptr [ebp -10] 注册码倒数13-10位和用户名计算结果
004D9540 8D45 E4 lea eax , [ebp -1C]
004D9543 BA 05000000 mov edx , 5
004D9548 E8 07BBF2FF call fixed.00405054 ; System.@LStrCatN;
004D954D 8B45 E4 mov eax , [ebp -1C] ; 长字符串1=上面5个字符串相连
004D9550 8D4D EC lea ecx , [ebp -14]
004D9553 33D2 xor edx , edx
004D9555 E8 EE6AFFFF call fixed.004D0048 ; 计算出一个Hash值,F7跟进可看到算法
004D955A 33C0 xor eax , eax
004D955C 5A pop edx
004D955D 59 pop ecx
004D955E 59 pop ecx
004D955F 64:8910 mov fs :[eax ], edx
004D9562 EB 1D jmp short fixed.004D9581
004D9581 EB 04 jmp short fixed.004D9587
004D9587 8B45 EC mov eax , [ebp -14] ; 计算出的Hash值
004D958A E8 05BAF2FF call fixed.00404F94
004D958F 83F8 08 cmp eax , 8
004D9592 7C 1A jl short fixed.004D95AE ; System.@LStrLen(String):Integer;
004D9594 EB 06 jmp short fixed.004D959C
004D959C 8B45 F4 mov eax , [ebp -C]
004D959F 50 push eax
004D95A0 8B4D EC mov ecx , [ebp -14] ; Hash值
004D95A3 8B55 F0 mov edx , [ebp -10] ; 注册码倒数13-10位和用户名计算结果
004D95A6 8B45 F8 mov eax , [ebp -8] ; 注册码
004D95A9 E8 32FDFFFF call fixed.004D92E0 ; 计算KeyFile文件名,F7跟进可看到算法
以下就返回了……
-------------------------------------------------------------------------------- 根据用户名和注册码计算Hash值:
---------------------------------------- 004D0048 ----------------------------------------
004D0048 55 push ebp
…………
…………
…………
004D009A 8D55 F4 lea edx , [ebp -C]
004D009D A1 C4C65500 mov eax , [55C6C4]
004D00A2 E8 356DF3FF call fixed.00406DDC ; 取字符串
004D00A7 8B4D F4 mov ecx , [ebp -C] ; "AbVytyNzU3NEpiqG0txQBxhLTMQiwbSCmAwZ0mBgKgOGGw6GM2RkFNdy1Q=="
004D00AA 8D45 F8 lea eax , [ebp -8]
004D00AD 8B55 FC mov edx , [ebp -4] ; 用户名和注册码一部分的混合
004D00B0 E8 2B4FF3FF call fixed.00404FE0 ; System.@LStrCat3;
004D00B5 8B45 F8 mov eax , [ebp -8] ; 上面2个字符串相连
004D00B8 8BD6 mov edx , esi
004D00BA E8 C1FEFFFF call fixed.004CFF80 ; 计算Hash值,F7跟进可看到算法
以下就返回了……
-------------------------------------------------------------------------------- 这里是真正计算Hash的Call :
---------------------------------------- 004CFF80 ----------------------------------------
004CFF80 55 push ebp
…………
…………
…………
循环取每一位,计算出一个Hash值:
004CFFBD BE 01000000 mov esi , 1
004CFFC2 8B45 FC mov eax , [ebp -4]
004CFFC5 8A4430 FF mov al , [eax +esi -1] ; 取出字符串的每一位
004CFFC9 83CA FF or edx , FFFFFFFF
004CFFCC E8 77FEFFFF call fixed.004CFE48 ; 查表替换,F7跟进可看到算法
004CFFD1 03F8 add edi , eax ; 将结果累加
004CFFD3 46 inc esi
004CFFD4 4B dec ebx
004CFFD5 ^ 75 EB jnz short fixed.004CFFC2
004CFFD7 8BC7 mov eax , edi
004CFFD9 E8 86FEFFFF call fixed.004CFE64 ; 求反
004CFFDE 8BF8 mov edi , eax
004CFFE0 8B55 F8 mov edx , [ebp -8]
004CFFE3 8BC7 mov eax , edi ; 计算出的Hash值
004CFFE5 E8 2AFFFFFF call fixed.004CFF14 ; 转换成Ascii
以下就返回了……
-------------------------------------------------------------------------------- 计算Hash过程中的查表替换:
---------------------------------------- 004CFE48 ----------------------------------------
004CFE48 32C2 xor al , dl ; 求反
004CFE4A 25 FF000000 and eax , 0FF
004CFE4F 8B0485 F478550>mov eax , [eax *4+5578F4] ; 查表
004CFE56 C1EA 08 shr edx , 8
004CFE59 81E2 FFFFFF00 and edx , 0FFFFFF
004CFE5F 33C2 xor eax , edx ; 再与0FFFFFFh异或
004CFE61 C3 ret
这就是那张巨大的表:
005578F4 00 00 00 00 96 30 07 77 2C 61 0E EE BA 51 09 99 ....?w,a詈Q.
00557904 19 C4 6D 07 8F F4 6A 70 35 A5 63 E9 A3 95 64 9E 捻?jp5ャ椋?
00557914 32 88 DB 0E A4 B8 DC 79 1E E9 D5 E0 88 D9 D2 97 2?じ荠檎?僖
00557924 2B 4C B6 09 BD 7C B1 7E 07 2D B8 E7 91 1D BF 90 +L?近炳-哥??
00557934 64 10 B7 1D F2 20 B0 6A 48 71 B9 F3 DE 41 BE 84 d??瓣Hq贵蘖?
00557944 7D D4 DA 1A EB E4 DD 6D 51 B5 D4 F4 C7 85 D3 83 }在脘蓓Q翟羟?
00557954 56 98 6C 13 C0 A8 6B 64 7A F9 62 FD EC C9 65 8A V?括kdz??慑
00557964 4F 5C 01 14 D9 6C 06 63 63 3D 0F FA F5 0D 08 8D O\凫cc=?.
00557974 C8 20 6E 3B 5E 10 69 4C E4 41 60 D5 72 71 67 A2 ?n;^iL淞`镇qg
00557984 D1 E4 03 3C 47 D4 04 4B FD 85 0D D2 6B B5 0A A5 唁<G?K?.译?
00557994 FA A8 B5 35 6C 98 B2 42 D6 C9 BB DB 40 F9 BC AC ??l?B稚慧@?
005579A4 E3 6C D8 32 75 5C DF 45 CF 0D D6 DC 59 3D D1 AB 沆?u\吲?周Y=勋
005579B4 AC 30 D9 26 3A 00 DE 51 80 51 D7 C8 16 61 D0 BF ??:.扪?兹a锌
005579C4 B5 F4 B4 21 23 C4 B3 56 99 95 BA CF 0F A5 BD B8 掉?#某V?合ソ
005579D4 9E B8 02 28 08 88 05 5F B2 D9 0C C6 24 E9 0B B1 ?(?_操.??
005579E4 87 7C 6F 2F 11 4C 68 58 AB 1D 61 C1 3D 2D 66 B6 ?o/LhX?a?-f
005579F4 90 41 DC 76 06 71 DB 01 BC 20 D2 98 2A 10 D5 EF ?荟q???*诊
00557A04 89 85 B1 71 1F B5 B6 06 A5 E4 BF 9F 33 D4 B8 E8 ?瘪刀ヤ?3愿
00557A14 A2 C9 07 78 34 F9 00 0F 8E A8 09 96 18 98 0E E1 ⑸x4??.??
00557A24 BB 0D 6A 7F 2D 3D 6D 08 97 6C 64 91 01 5C 63 E6 ?j-=m?d?\c
00557A34 F4 51 6B 6B 62 61 6C 1C D8 30 65 85 4E 00 62 F2 粞kkbal?e?.b
00557A44 ED 95 06 6C 7B A5 01 1B C1 F4 08 82 57 C4 0F F5 ?l{?留??
00557A54 C6 D9 B0 65 50 E9 B7 12 EA B8 BE 8B 7C 88 B9 FC 瀑板P榉旮?|?
00557A64 DF 1D DD 62 49 2D DA 15 F3 7C D3 8C 65 4C D4 FB ?葩I-?簏?eL喳
00557A74 58 61 B2 4D CE 51 B5 3A 74 00 BC A3 E2 30 BB D4 Xa餐窝?t.迹?辉
00557A84 41 A5 DF 4A D7 95 D8 3D 6D C4 D1 A4 FB F4 D6 D3 AミJ??m难?糁
00557A94 6A E9 69 43 FC D9 6E 34 46 88 67 AD D0 B8 60 DA j殚C?n4F??膏
00557AA4 73 2D 04 44 E5 1D 03 33 5F 4C 0A AA C9 7C 0D DD s-D?3_L.?|.
00557AB4 3C 71 05 50 AA 41 02 27 10 10 0B BE 86 20 0C C9 <qP?'? .
00557AC4 25 B5 68 57 B3 85 6F 20 09 D4 66 B9 9F E4 61 CE %佃W?o .枣?溽
00557AD4 0E F9 DE 5E 98 C9 D9 29 22 98 D0 B0 B4 A8 D7 C7 ?^??"?按ㄗ
00557AE4 17 3D B3 59 81 0D B4 2E 3B 5C BD B7 AD 6C BA C0 =迟??;\椒?豪
00557AF4 20 83 B8 ED B6 B3 BF 9A 0C E2 B6 03 9A D2 B1 74 ?矶晨?舛?濒
00557B04 39 47 D5 EA AF 77 D2 9D 15 26 DB 04 83 16 DC 73 9G贞??&??荏
00557B14 12 0B 63 E3 84 3B 64 94 3E 6A 6D 0D A8 5A 6A 7A c?;d?jm.ㄚjz
00557B24 0B CF 0E E4 9D FF 09 93 27 AE 00 0A B1 9E 07 7D ?????.?}
00557B34 44 93 0F F0 D2 A3 08 87 68 F2 01 1E FE C2 06 69 D?鹨????i
00557B44 5D 57 62 F7 CB 67 65 80 71 36 6C 19 E7 06 6B 6E ]Wb魉ge?6l?kn
00557B54 76 1B D4 FE E0 2B D3 89 5A 7A DA 10 CC 4A DD 67 v轧??Zz?淌葭
00557B64 6F DF B9 F9 F9 EF BE 8E 43 BE B7 17 D5 8E B0 60 o吖?锞?痉?班
00557B74 E8 A3 D6 D6 7E 93 D1 A1 C4 C2 D8 38 52 F2 DF 4F 瑁种~?∧仑8R蜻O
00557B84 F1 67 BB D1 67 57 BC A6 DD 06 B5 3F 4B 36 B2 48 耒谎gW鸡??K6踩
00557B94 DA 2B 0D D8 4C 1B 0A AF F6 4A 03 36 60 7A 04 41 ?.靥.?J6`zA
00557BA4 C3 EF 60 DF 55 DF 67 A8 EF 8E 6E 31 79 BE 69 46 蔑`哒哏??1y鹃F
00557BB4 8C B3 61 CB 1A 83 66 BC A0 D2 6F 25 36 E2 68 52 ?a??歼绎%6忤R
00557BC4 95 77 0C CC 03 47 0B BB B9 16 02 22 2F 26 05 55 ?.?G还"/&U
00557BD4 BE 3B BA C5 28 0B BD B2 92 5A B4 2B 04 6A B3 5C ?号(讲??j耻
00557BE4 A7 FF D7 C2 31 CF D0 B5 8B 9E D9 2C 1D AE DE 5B ?茁1闲??,?[
00557BF4 B0 C2 64 9B 26 F2 63 EC 9C A3 6A 75 0A 93 6D 02 奥d?蜚?ju.?
00557C04 A9 06 09 9C 3F 36 0E EB 85 67 07 72 13 57 00 05 ?.?6?grW.
00557C14 82 4A BF 95 14 7A B8 E2 AE 2B B1 7B 38 1B B6 0C ??z糕?丙8?
00557C24 9B 8E D2 92 0D BE D5 E5 B7 EF DC 7C 21 DF DB 0B ??.菊宸镘|!咣
00557C34 D4 D2 D3 86 42 E2 D4 F1 F8 B3 DD 68 6E 83 DA 1F 砸?B庠聒齿hn?
00557C44 CD 16 BE 81 5B 26 B9 F6 E1 77 B0 6F 77 47 B7 18 ??[&滚狩帮wG?
00557C54 E6 5A 08 88 70 6A 0F FF CA 3B 06 66 5C 0B 01 11 孚?j?f\
00557C64 FF 9E 65 8F 69 AE 62 F8 D3 FF 6B 61 45 CF 6C 16 ?e????aE响
00557C74 78 E2 0A A0 EE D2 0D D7 54 83 04 4E C2 B3 03 39 x?_?自?N鲁9
00557C84 61 26 67 A7 F7 16 60 D0 4D 47 69 49 DB 77 6E 3E a&g?`型GiI埙n>
00557C94 4A 6A D1 AE DC 5A D6 D9 66 0B DF 40 F0 3B D8 37 Jj旬苴仲f呃??
00557CA4 53 AE BC A9 C5 9E BB DE 7F CF B2 47 E9 FF B5 30 S?┡??喜G??
00557CB4 1C F2 BD BD 8A C2 BA CA 30 93 B3 53 A6 A3 B4 24 蚪?潞??SΓ?
00557CC4 05 36 D0 BA 93 06 D7 CD 29 57 DE 54 BF 67 D9 23 6泻?淄)W拊跨?
00557CD4 2E 7A 66 B3 B8 4A 61 C4 02 1B 68 5D 94 2B 6F 2A .zf掣Ja?h]?o*
00557CE4 37 BE 0B B4 A1 8E 0C C3 1B DF 05 5A 8D EF 02 2D 7?础???Z?-
-------------------------------------------------------------------------------- 根据Hash值和用户名、注册码计算KeyFile文件名:
---------------------------------------- 004D92E0 ----------------------------------------
004D92E0 55 push ebp
…………
…………
…………
004D930A 8D55 F4 lea edx , [ebp -C]
004D930D 8BC3 mov eax , ebx ; 注册码
004D930F E8 D468FFFF call fixed.004CFBE8
004D9314 FF75 F4 push dword ptr [ebp -C] ; "CHM"
004D9317 56 push esi ; 注册码倒数13-9位和用户名计算结果
004D9318 57 push edi ; Hash值
004D9319 8D55 F0 lea edx , [ebp -10]
004D931C 8BC3 mov eax , ebx ; 注册码
004D931E E8 ED67FFFF call fixed.004CFB10
004D9323 FF75 F0 push dword ptr [ebp -10] ; ".dll"
004D9326 8D45 F8 lea eax , [ebp -8]
004D9329 BA 04000000 mov edx , 4
004D932E E8 21BDF2FF call fixed.00405054 ; System.@LStrCatN;
004D9333 8B45 F8 mov eax , [ebp -8] ; "CHM"+注册码倒数13-9位和用户名计算结果+Hash值+".dll"
004D9336 8D55 FC lea edx , [ebp -4]
004D9339 E8 4E02F3FF call fixed.0040958C ; 转换为小写
004D933E 8B45 FC mov eax , [ebp -4] ; 这就是KeyFile文件名了
以下就返回了……
--------------------------------------------------------------------------------++++++++++++++++++++++++++++++++++++++++ 第7关:计算AES-256初始密钥 ++++++++++++++++++++++++++++++++++++++++
呼……第6关还真是长啊!终于闯到第7关了!
进入第7关后一路F8往下,来到这里:
004D9839 8D45 E4 lea eax , [ebp -1C]
004D983C E8 F7FEFFFF call fixed.004D9738 ; 获得程序文件名
004D9841 8B4D E4 mov ecx , [ebp -1C] ; "FIXED.exe"
004D9844 8D45 E8 lea eax , [ebp -18]
004D9847 8B55 FC mov edx , [ebp -4] ; 注册码
004D984A E8 91B7F2FF call fixed.00404FE0 ; System.@LStrCat3;
004D984F 8B45 E8 mov eax , [ebp -18] ; 注册码+文件名(大写)
004D9852 8D4D EC lea ecx , [ebp -14]
004D9855 33D2 xor edx , edx
004D9857 E8 9468FFFF call fixed.004D00F0 ; 计算出一个MD5值,具体算法如下
计算MD5值的Call :
---------------------------------------- 004D00F0 ----------------------------------------
004D00F0 55 push ebp
…………
…………
…………
004D0144 8D55 F4 lea edx , [ebp -C]
004D0147 A1 5CC65500 mov eax , [55C65C]
004D014C E8 8B6CF3FF call fixed.00406DDC ; 取字符串
004D0151 8B4D F4 mov ecx , [ebp -C] ; "bZxvRs2puRGpimSWzVBwwLTExBEHS1CJDJFawAZRwKFs4NCZRoxQX5N1W+RvRs2p5oimqGRL3ApwwA0bg0OZRsgor8nq"
004D0154 8D45 F8 lea eax , [ebp -8]
004D0157 8B55 FC mov edx , [ebp -4] ; 字符串
004D015A E8 814EF3FF call fixed.00404FE0 ; System.@LStrCat3;
004D015F 8B45 F8 mov eax , [ebp -8]
004D0162 8BD6 mov edx , esi
004D0164 E8 439FFFFF call fixed.004CA0AC ; 计算MD5值
以下就返回了……
--------------------------------------------------------------------------------
原来是把注册码加上文件名,再加上一个很长的字符串,一起计算MD5值。
MD5算出,过关!++++++++++++++++++++++++++++++++++++++++ 第8关:AES-256解密 ++++++++++++++++++++++++++++++++++++++++
第8关的关键代码在这里:
004D2601 5A pop edx ; KeyFile文件大小
004D2602 2BD0 sub edx , eax ; KeyFile文件大小-4为解密长度
004D2604 8D4D D4 lea ecx , [ebp -2C] ; MD5值作为初始密钥
004D2607 8BC3 mov eax , ebx ; KeyFile作为待解密字串
004D2609 E8 92FBFFFF call fixed.004D21A0 ; 用AES-256解密,F7跟进可看到算法
解密后来到这里:
004D263B 52 push edx
004D263C 50 push eax ; KeyFile的前4字节作为解密字串长度
004D263D 8B45 FC mov eax , [ebp -4]
004D2640 E8 4BC3F4FF call fixed.0041E990 ; 获得解密后的字串
004D2645 EB 04 jmp short fixed.004D264B
004D264B 8B45 F4 mov eax , [ebp -C]
004D264E 99 cdq
004D264F 52 push edx
004D2650 50 push eax
004D2651 8B45 FC mov eax , [ebp -4]
004D2654 E8 D3C2F4FF call fixed.0041E92C ; 设置解密字串长度
004D2659 5F pop edi
004D265A 5E pop esi
004D265B 5B pop ebx
004D265C 8BE5 mov esp , ebp
004D265E 5D pop ebp
004D265F C3 ret
这样KeyFile的格式就清楚了:前4个字节是明文长度,后面的部分是密文。
把密文还原成明文,就过关啦!
下面是AES-256解密的函数:
---------------------------------------- 004D21A0 ----------------------------------------
004D21A0 55 push ebp
004D21A1 8BEC mov ebp , esp
004D21A3 81C4 00FFFFFF add esp , -100
004D21A9 53 push ebx
004D21AA 56 push esi
004D21AB 8BF2 mov esi , edx
004D21AD 8BD8 mov ebx , eax
004D21AF 8D95 00FFFFFF lea edx , [ebp -100]
004D21B5 8BC1 mov eax , ecx ; MD5值
004D21B7 E8 30E5FFFF call fixed.004D06EC ; 生成解密密钥,F7跟进可看到算法
004D21BC 8B45 08 mov eax , [ebp +8]
004D21BF 50 push eax
004D21C0 8D8D 00FFFFFF lea ecx , [ebp -100] ; 解密密钥
004D21C6 8BD6 mov edx , esi ; 需要解密的长度
004D21C8 8BC3 mov eax , ebx
004D21CA E8 09000000 call fixed.004D21D8 ; 将密文解密
004D21CF 5E pop esi
004D21D0 5B pop ebx
004D21D1 8BE5 mov esp , ebp
004D21D3 5D pop ebp
004D21D4 C2 0400 ret 4
-------------------------------------------------------------------------------- 这里是生成解密密钥的过程:
---------------------------------------- 004D06EC ----------------------------------------
004D06EC 53 push ebx
004D06ED 8BDA mov ebx , edx
004D06EF 8BD3 mov edx , ebx ; MD5值
004D06F1 E8 52FBFFFF call fixed.004D0248 ; KeySchedule,产生加密子密钥,F7跟进可看到算法
004D06F6 8BC3 mov eax , ebx ; MD5值
004D06F8 E8 03FDFFFF call fixed.004D0400 ; 根据加密子密钥生成解密子密钥
004D06FD 5B pop ebx
004D06FE C3 ret
-------------------------------------------------------------------------------- 生成加密密钥:
---------------------------------------- 004D0248 ----------------------------------------
004D0248 53 push ebx
004D0249 56 push esi
004D024A 57 push edi
004D024B 83C4 F4 add esp , -0C
复制MD5值:
004D024E 8B08 mov ecx , [eax ]
004D0250 890A mov [edx ], ecx
004D0252 8D48 04 lea ecx , [eax +4]
004D0255 8B09 mov ecx , [ecx ]
004D0257 894A 04 mov [edx +4], ecx
004D025A 8D48 08 lea ecx , [eax +8]
004D025D 8B09 mov ecx , [ecx ]
004D025F 894A 08 mov [edx +8], ecx
004D0262 8D48 0C lea ecx , [eax +C]
004D0265 8B09 mov ecx , [ecx ]
004D0267 894A 0C mov [edx +C], ecx
004D026A 8D48 10 lea ecx , [eax +10]
004D026D 8B09 mov ecx , [ecx ]
004D026F 894A 10 mov [edx +10], ecx
004D0272 8D48 14 lea ecx , [eax +14]
004D0275 8B09 mov ecx , [ecx ]
004D0277 894A 14 mov [edx +14], ecx
004D027A 8D48 18 lea ecx , [eax +18]
004D027D 8B09 mov ecx , [ecx ]
004D027F 894A 18 mov [edx +18], ecx
004D0282 83C0 1C add eax , 1C
004D0285 8B00 mov eax , [eax ]
004D0287 8942 1C mov [edx +1C], eax
004D028A 33C0 xor eax , eax
004D028C C70424 0100000>mov dword ptr [esp ], 1 ; 循环计数器
循环,计算每一轮的子密钥:
004D0293 8B4C82 1C mov ecx , [edx +eax *4+1C] ; MD5值的28-32位作为初始密钥的第8列
004D0297 8BF9 mov edi , ecx
004D0299 C1E7 18 shl edi , 18 ;\
004D029C C1E9 08 shr ecx , 8 ;-循环右移8位,RotWord
004D029F 0BF9 or edi , ecx ;/
004D02A1 8BCF mov ecx , edi
004D02A3 81E1 FF000000 and ecx , 0FF
004D02A9 8B0C8D 7C7D550>mov ecx , [ecx *4+557D7C] ;查S-Box替换,SubBytes,这段代码是按字节方式算的,所以要4次
004D02B0 894C24 04 mov [esp +4], ecx
004D02B4 8BCF mov ecx , edi
004D02B6 C1E9 08 shr ecx , 8
004D02B9 81E1 FF000000 and ecx , 0FF
004D02BF 8B0C8D 7C7D550>mov ecx , [ecx *4+557D7C]
004D02C6 8BDF mov ebx , edi
004D02C8 C1EB 10 shr ebx , 10
004D02CB 81E3 FF000000 and ebx , 0FF
004D02D1 8B349D 7C7D550>mov esi , [ebx *4+557D7C]
004D02D8 8BDF mov ebx , edi
004D02DA C1EB 18 shr ebx , 18
004D02DD 81E3 FF000000 and ebx , 0FF
004D02E3 8B1C9D 7C7D550>mov ebx , [ebx *4+557D7C]
004D02EA 895C24 08 mov [esp +8], ebx
004D02EE 8BD9 mov ebx , ecx
004D02F0 C1E3 08 shl ebx , 8
004D02F3 C1E9 18 shr ecx , 18
004D02F6 0BD9 or ebx , ecx
004D02F8 335C24 04 xor ebx , [esp +4]
004D02FC 8BCE mov ecx , esi
004D02FE C1E1 10 shl ecx , 10
004D0301 C1EE 10 shr esi , 10
004D0304 0BCE or ecx , esi
004D0306 33D9 xor ebx , ecx
004D0308 8B4C24 08 mov ecx , [esp +8]
004D030C C1E1 18 shl ecx , 18
004D030F 8B7424 08 mov esi , [esp +8]
004D0313 C1EE 08 shr esi , 8
004D0316 0BCE or ecx , esi
004D0318 33D9 xor ebx , ecx
004D031A 331C82 xor ebx , [edx +eax *4] ; MD5值的前4位作为初始密钥的第1列,与W(i-4)异或
004D031D 8B0C24 mov ecx , [esp ]
004D0320 331C8D 007D550>xor ebx , [ecx *4+557D00] ; 与Rcon(i)异或,得到下一轮子密钥的第1列
004D0327 895C82 20 mov [edx +eax *4+20], ebx
004D032B FF0424 inc dword ptr [esp ] ; 循环计数器+1
004D032E 8B4C82 04 mov ecx , [edx +eax *4+4] ; MD5值的5-8位作为初始密钥的第2列
004D0332 334C82 20 xor ecx , [edx +eax *4+20] ; W(i)与W(i-4)异或,得到密钥的下一列
004D0336 894C82 24 mov [edx +eax *4+24], ecx
004D033A 8B5C82 08 mov ebx , [edx +eax *4+8] ; MD5值的9-12位作为初始密钥的第3列
004D033E 33D9 xor ebx , ecx ; W(i)与W(i-4)异或,得到密钥的下一列
004D0340 895C82 28 mov [edx +eax *4+28], ebx
004D0344 8B7C82 0C mov edi , [edx +eax *4+C] ; MD5值的13-16位作为初始密钥的第4列
004D0348 33FB xor edi , ebx ; W(i)与W(i-4)异或,得到密钥的下一列
004D034A 897C82 2C mov [edx +eax *4+2C], edi
004D034E 33C9 xor ecx , ecx
004D0350 8A4C82 2C mov cl , [edx +eax *4+2C]
004D0354 8B0C8D 7C7D550>mov ecx , [ecx *4+557D7C] ; SubBytes
004D035B 894C24 04 mov [esp +4], ecx
004D035F 8BCF mov ecx , edi
004D0361 C1E9 08 shr ecx , 8
004D0364 81E1 FF000000 and ecx , 0FF
004D036A 8B0C8D 7C7D550>mov ecx , [ecx *4+557D7C]
004D0371 8BDF mov ebx , edi
004D0373 C1EB 10 shr ebx , 10
004D0376 81E3 FF000000 and ebx , 0FF
004D037C 8B349D 7C7D550>mov esi , [ebx *4+557D7C]
004D0383 8BDF mov ebx , edi
004D0385 C1EB 18 shr ebx , 18
004D0388 81E3 FF000000 and ebx , 0FF
004D038E 8B1C9D 7C7D550>mov ebx , [ebx *4+557D7C]
004D0395 895C24 08 mov [esp +8], ebx
004D0399 8BD9 mov ebx , ecx
004D039B C1E3 08 shl ebx , 8
004D039E C1E9 18 shr ecx , 18
004D03A1 0BD9 or ebx , ecx
004D03A3 335C24 04 xor ebx , [esp +4]
004D03A7 8BCE mov ecx , esi
004D03A9 C1E1 10 shl ecx , 10
004D03AC C1EE 10 shr esi , 10
004D03AF 0BCE or ecx , esi
004D03B1 33D9 xor ebx , ecx
004D03B3 8B4C24 08 mov ecx , [esp +8]
004D03B7 C1E1 18 shl ecx , 18
004D03BA 8B7424 08 mov esi , [esp +8]
004D03BE C1EE 08 shr esi , 8
004D03C1 0BCE or ecx , esi
004D03C3 33D9 xor ebx , ecx
004D03C5 335C82 10 xor ebx , [edx +eax *4+10] ; MD5值的17-20位作为初始密钥的第5列
004D03C9 895C82 30 mov [edx +eax *4+30], ebx ; W(i)与W(i-4)异或,得到密钥的下一列
004D03CD 8B4C82 14 mov ecx , [edx +eax *4+14] ; MD5值的21-24位作为初始密钥的第6列
004D03D1 334C82 30 xor ecx , [edx +eax *4+30] ; W(i)与W(i-4)异或,得到密钥的下一列
004D03D5 894C82 34 mov [edx +eax *4+34], ecx
004D03D9 8B5C82 18 mov ebx , [edx +eax *4+18] ; MD5值的25-28位作为初始密钥的第7列
004D03DD 33D9 xor ebx , ecx ; W(i)与W(i-4)异或,得到密钥的下一列
004D03DF 895C82 38 mov [edx +eax *4+38], ebx
004D03E3 8B4C82 1C mov ecx , [edx +eax *4+1C] ; MD5值的28-32位作为初始密钥的第8列
004D03E7 33CB xor ecx , ebx ; W(i)与W(i-4)异或,得到密钥的下一列
004D03E9 894C82 3C mov [edx +eax *4+3C], ecx
004D03ED 83C0 08 add eax , 8
004D03F0 83F8 34 cmp eax , 34
004D03F3 ^ 0F8C 9AFEFFFF jl fixed.004D0293 ; 共计算7轮,得到14个子密钥
004D03F9 83C4 0C add esp , 0C
004D03FC 5F pop edi
004D03FD 5E pop esi
004D03FE 5B pop ebx
004D03FF C3 ret
--------------------------------------------------------------------------------
在这里,作者调用了一个AES的库,很不幸地,或者说很幸运地,这个库被我用Google找到了。有了这个库,上面这段代码跟踪起来就比较轻松了。
下面是部分源码:
这是004D21A0这个Call,AES-256解密:
procedure DecryptAESStreamECB(Source: TStream; Count: cardinal;
const Key: TAESKey256; Dest: TStream);
var
ExpandedKey: TAESExpandedKey256;
begin
ExpandAESKeyForDecryption(Key, ExpandedKey);
DecryptAESStreamECB(Source, Count, ExpandedKey, Dest);
end;
这是004D06EC这个Call,生成解密密钥:
procedure ExpandAESKeyForDecryption(const Key: TAESKey256; var ExpandedKey: TAESExpandedKey256);
begin
ExpandAESKeyForEncryption(Key, ExpandedKey);
ExpandAESKeyForDecryption(ExpandedKey);
end;
我把这个库放到附件里了,有兴趣的朋友可以看看。++++++++++++++++++++++++++++++++++++++++ 第9关:ZLib 解压 ++++++++++++++++++++++++++++++++++++++++
用注册码+文件名+某字符串的MD5值做密钥,用AES-256算法将KeyFile解密出来以后,来到这里:
004D98B5 8D55 F4 lea edx , [ebp -C]
004D98B8 8B45 F0 mov eax , [ebp -10] ; AES-256解密后字串
004D98BB E8 EC91FFFF call fixed.004D2AAC ; 用ZLib解压
004D98C0 EB 06 jmp short fixed.004D98C8
ZLib是开源的,而且Delphi自带了ZLib库,有兴趣的可以F7跟进看看算法。
解压后来到这里:
004D98FB 8B45 F4 mov eax , [ebp -C] ; ZLib解压后字串
004D98FE E8 91B6F2FF call fixed.00404F94 ; System.@LStrLen(String):Integer;
004D9903 85C0 test eax , eax
004D9905 74 1F je short fixed.004D9926
004D9907 8B45 F4 mov eax , [ebp -C] ; ZLib解压后字串
004D990A E8 85B6F2FF call fixed.00404F94 ; System.@LStrLen(String):Integer;
004D990F 83F8 64 cmp eax , 64
004D9912 7C 12 jl short fixed.004D9926 ; 长度<100就Game Over啦
004D9914 EB 06 jmp short fixed.004D991C
呵呵,ZLib解压出来的东东还不能少于100字节呢。
过了这里,就过关啦!
可以Ctrl+F9返回啦!
让我们进入最后一关,挑战最终Boss!++++++++++++++++++++++++++++++++++++++++ 第10关:MD5 校检 ++++++++++++++++++++++++++++++++++++++++
从这里开始,就是第10关啦:
---------------------------------------- 004D9954 ----------------------------------------
004D9954 55 push ebp
004D9955 8BEC mov ebp , esp
004D9957 33C9 xor ecx , ecx
004D9959 51 push ecx
004D995A 51 push ecx
004D995B 51 push ecx
004D995C 51 push ecx
004D995D 51 push ecx
004D995E 53 push ebx
004D995F 56 push esi
004D9960 57 push edi
004D9961 8945 FC mov [ebp -4], eax
004D9964 8B45 FC mov eax , [ebp -4]
004D9967 E8 18B8F2FF call fixed.00405184 ; System.@LStrAddRef(void;void):Pointer;
004D996C 33C0 xor eax , eax
004D996E 55 push ebp
004D996F 68 AC9A4D00 push fixed.004D9AAC
004D9974 64:FF30 push dword ptr fs :[eax ]
004D9977 64:8920 mov fs :[eax ], esp
004D997A EB 04 jmp short fixed.004D9980
004D9980 C645 FB 00 mov byte ptr [ebp -5], 0 ; 注册标志位清0
004D9984 EB 04 jmp short fixed.004D998A
004D998A 8B45 FC mov eax , [ebp -4] ; KeyFile解密后的字串
004D998D E8 02B6F2FF call fixed.00404F94 ; System.@LStrLen(String):Integer;
004D9992 85C0 test eax , eax
004D9994 75 0D jnz short fixed.004D99A3
004D99A3 33C0 xor eax , eax
004D99A5 55 push ebp
004D99A6 68 E0994D00 push fixed.004D99E0
004D99AB 64:FF30 push dword ptr fs :[eax ]
004D99AE 64:8920 mov fs :[eax ], esp
004D99B1 EB 04 jmp short fixed.004D99B7
取字符串的最后32字节:
004D99B7 8D45 F4 lea eax , [ebp -C]
004D99BA 50 push eax
004D99BB 8B45 FC mov eax , [ebp -4] ; KeyFile解密后的字串
004D99BE E8 D1B5F2FF call fixed.00404F94 ; System.@LStrLen(String):Integer;
004D99C3 8BD0 mov edx , eax
004D99C5 83EA 20 sub edx , 20
004D99C8 42 inc edx
004D99C9 B9 20000000 mov ecx , 20
004D99CE 8B45 FC mov eax , [ebp -4] ; 字符串
004D99D1 E8 1EB8F2FF call fixed.004051F4 ; System.@LStrCopy
004D99D6 33C0 xor eax , eax
004D99D8 5A pop edx
004D99D9 59 pop ecx
004D99DA 59 pop ecx
004D99DB 64:8910 mov fs :[eax ], edx
004D99DE EB 26 jmp short fixed.004D9A06
004D9A06 33C0 xor eax , eax
004D9A08 55 push ebp
004D9A09 68 519A4D00 push fixed.004D9A51
004D9A0E 64:FF30 push dword ptr fs :[eax ]
004D9A11 64:8920 mov fs :[eax ], esp
004D9A14 EB 06 jmp short fixed.004D9A1C
取字符串的其余字节:
004D9A1C 8D45 EC lea eax , [ebp -14]
004D9A1F 50 push eax
004D9A20 8B45 FC mov eax , [ebp -4] ; 字符串
004D9A23 E8 6CB5F2FF call fixed.00404F94 ; System.@LStrLen(String):Integer;
004D9A28 8BC8 mov ecx , eax
004D9A2A 83E9 20 sub ecx , 20
004D9A2D BA 01000000 mov edx , 1
004D9A32 8B45 FC mov eax , [ebp -4] ; 字符串
004D9A35 E8 BAB7F2FF call fixed.004051F4 ; System.@LStrCopy;
004D9A3A 8B45 EC mov eax , [ebp -14]
004D9A3D 8D4D F0 lea ecx , [ebp -10]
004D9A40 33D2 xor edx , edx
004D9A42 E8 A966FFFF call fixed.004D00F0 ; 加上某字符串,取MD5,这个Call在第7关计算AES-256密钥时见过
004D9A47 33C0 xor eax , eax
004D9A49 5A pop edx
004D9A4A 59 pop ecx
004D9A4B 59 pop ecx
004D9A4C 64:8910 mov fs :[eax ], edx
004D9A4F EB 23 jmp short fixed.004D9A74
004D9A47 33C0 xor eax , eax
004D9A49 5A pop edx
004D9A4A 59 pop ecx
004D9A4B 59 pop ecx
004D9A4C 64:8910 mov fs :[eax ], edx
004D9A4F EB 23 jmp short fixed.004D9A74
004D9A74 EB 04 jmp short fixed.004D9A7A
004D9A7A 8B45 F0 mov eax , [ebp -10] ; 计算出的MD5值
004D9A7D 8B55 F4 mov edx , [ebp -C] ; 注册码后32位
004D9A80 E8 5BB6F2FF call fixed.004050E0 ; System.@LStrCmp;
004D9A85 0F9445 FB sete [ebp -5] ;设置注册标志位
原来是这样:ZLib解压出来的不小于100字节的东东,后32字节作为校检码,如果和前面字节的MD5相等,就注册成功啦!
到这里,历尽艰难险阻,终于通关啦!!!
--------------------------------------------------------------------------------
【总结】
KeyFile的文件名用了自定义的算法,其余都用了标准算法,尤其是用MD5值做密钥,用了高强度的AES-256算法。
写到这里已经很累了,不想做注册机了。
下面举一个例子,作为注册流程和算法总结:
用户名:Ptero
注册码:COFFB00000-00000000
程序名:EasyCHM.EXE
KeyFile文件名:chm2259abbf22f.dll
KeyFile内容:
00000000h: 2C 00 00 00 08 CD EA 55 F3 C0 8E 1B 4F AA 26 04 ; ,....完U罄?O?.
00000010h: 72 FC 23 E7 68 6F DD 7D DD DC EF 48 86 4E 70 1E ; r?玷o蔟蒈锶?p.
00000020h: 62 BC 9E 40 C0 1A 4C 5A EE 80 DB A8 F4 61 98 C8 ; b?@?LZ?郇翎?
00000030h: 65 68 53 B6 ; ehS
算法:
1. 用MD5的方法验证注册码前2位:"QE" 、"EC" 、"ET" 为个人用户,"CO" 、"LT" 、"LD" 为企业用户
2. 如果是企业用户,则取注册码前4位"FFB0" 异或4Fh=FFFFh(65535)为许可证数量
3. 用第6关的算法算出KeyFile文件名"chm2259abbf22f.dll"
4. 注册码+程序名(大写)+特定字符串:"COFFB00000-00000000EASYCHM.EXEbZxvRs2puRGpimSWzVBwwLTExBEHS1CJDJFawAZRwKFs4NCZRoxQX5N1W+RvRs2p5oimqGRL3ApwwA0bg0OZRsgor8nq"
5. 取MD5值(小写)的Ascii码:"dd069b9bb7ce9f3570ae1a2d40ca38b2"
6. 用此MD5的Ascii码作为256位初始密钥,解密KeyFile第4字节以后的内容
7. KeyFile前4字节为明文长度(2Ch)
解密为:
00000000h: 78 9C 33 30 A0 1C 98 A7 A5 18 98 5B 58 9A A4 99 ; x?0????X??
00000010h: 98 A5 98 9A 98 25 25 1A 24 1B 26 25 9A 58 18 24 ; ???%.$.&%?.$
00000020h: 5B 98 5A 58 58 18 02 00 DF 40 15 30 ; [?XX...呃.0
8. 再用ZLib解压,得到:"000000000000000000000000000000000000000000000000000000000000000000007fd07894f46d546ba0c1ba480c858881"
9. 此字符串的前68字节+特定字符串:"00000000000000000000000000000000000000000000000000000000000000000000bZxvRs2puRGpimSWzVBwwLTExBEHS1CJDJFawAZRwKFs4NCZRoxQX5N1W+RvRs2p5oimqGRL3ApwwA0bg0OZRsgor8nq"
10.取MD5:7fd07894f46d546ba0c1ba480c858881,再转换成Ascii码
与字符串的后32字节比较,如果相等即注册成功!
后记:其实这篇文章还有很多不完善之处,比如19楼提到的,只是表面上注册成功了,实际上还是有功能限制的。相关算法我现在正在研究之中,过段时间会再发一篇的。
----------------------------------------------------
【版权声明】 本文纯属技术交流, 转载请注明作者并保持文章的完整, 谢谢
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
上传的附件: