吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 47963|回复: 125
上一主题 下一主题
收起左侧

[CTF] 《360安全招聘破解题CrackMe》题解

  [复制链接]
跳转到指定楼层
楼主
MistHill 发表于 2014-5-8 16:58 回帖奖励

既然提交答案已经截止了,响应hmily的号召,先把我的答案帖出来与大家讨论。
这是一个由浅入深的过程,有的认识可能是错误的,欢迎指正。本楼是我提交的内容,若有补充,见后面楼层。
这部分提交时,完全没有帖代码,只有文字描述。明白人一看就懂,关键代码以后补充时再帖。

第一次回贴的内容被hmily屏蔽了,抱歉那时不知道规矩!见《360安全招聘破解题CrackMe》。
2014-4-24 09:22
好比“盗梦空间”。
第一层:壳自身。代码简洁、高效、漂亮,很喜欢。
第二层:误导。无论是跟踪第一个进程或附加进程,永远的"Failed"。
第三层:Overlay。想办法跟到解开它的地方,离"Success"就不远了。
似乎还有“彩蛋”,也许我想得太多了……

解释一下,当时用SPY++检查对话框,发现显示提示信息的地方有两个Static控件ID:03EB和03EE,以为有什么机关。后来才知道,提示信息是分开的,分别对应"Failed"和"Success"。
以下按当时的理解原文帖出,有偏差的地方也不修改了。

《360安全招聘破解题CrackMe》 题解(初稿)
MistHill 52PoJie.CN
25/04/2014

假作真时真亦假;
无为有处有还无。

——曹雪芹撰 太虚幻境联

这就是本题的奥妙之处!

通常的思维是ANTI-DEBUG,人们想方设法不让被调试的目标发现自己被调试。
本题正好相反,处于“调试模式”时,功能才正常;否则永远不能接触目标的核心代码,无法显示成功信息。

目标进程在开启自己的另一个调试进程后终止,一方面使程序进入正常状态,另一方面阻止了新手的进一步跟踪。即使附加进程,因为处于“正常模式”,也不能进入下一阶段的流程。

一.初级阶段的算法
分为两部分:
1. Username和Password分别以UNICODE字符串计算MD5,MD5值转换为字符串(Password的MD5值转换时,不知是有Bug还是故意的,少一个字符),再strcat。
   结果写入文件"360.dat";再读入,与00588B20处的一个字符串比较。内容不同,长度也不同,必然是"Failed"。
   想做手脚的话,可选这里进行处理。
2. Username和Password转换为ASCII字符串,相加:Username + "|" + Password。各字符与0x23异或后,保存在全局变量地址005B40A8处备用。
   这是此阶段真正有意义的部分。

二.核心代码的解密
核心代码位于内部的库文件bitchDll.dll里。
它的解密从线程函数00401B80开始,读入自己后:
1. 先将文件偏移0xE8,长0x118的内容计算CRC32。转换ASCII "dfe963a6",计算MD5。作为CALG_AES_128的Key,解密文件偏移0x000ADE9E,长度0x000109B0的内容;解密后长度0x000109A4(去掉PADDING后)。
2. 用"zlib Versions 1.1.4"的解压函数对其进行解压缩,内存里最终得到长度为0x00011200的bitchDll.dll。我在这里将它Dump出来了,它本身是用UPX压缩过的,抹掉了原来文件头的信息,我以前的脚本不能Unpack它。
3. 下面要将这个内存DLL“载入”了。一个有意思的处理:用kernel32.GetSystemTimeAsFileTime产生随机数来指定载入的Imagebase。我这里将它改为固定值,以方便做笔记。
   分配内存、解压缩、导入、重定位,该做的都走一遍。最后控制交给Dll Entrypoint初始化,取唯一的导出函数"LSG"的地址,并填到相应位置[005B40A0]。
我在调试时简单改了两个地方,以保证调试进程不退出,并能正常进入核心部分。[PS:见下面补充楼层]

三.注册算法
1. 主线程重复“初级阶段的算法”的流程,界面的Static Ctrl_ID 03EB继续显示"Failed"。
2. 调用00401A40会检查[005B40A0]处的Dll导出函数"LSG"地址是否为NULL,检查标志[005B4098],kernel32.IsDebuggerPresent检查是否处于被调试,等等。一切OK后,用kernel32.CreateThread创建线程,线程函数00401A20。该线程以指针参数005B40A8,调用bitchDll.dll的导出函数"LSG"(Dll_RV_1F20),真正的演出开始了!
   之前,ntdll.ZwWriteFile函数被Hook了,具体作用是什么,ANTI-DUMP?没仔细看,尚不清楚。
3. 各字符与0x23异或还原,split出Username和Password。Password字符串转Hex,实际是Base64编码的。Base64 Decoding后得到注册信息数据。
4. Sub_Dll_RV_1A90会解密Base64解码后的数据。具体算法还没来得及仔细看,一开始有个调用会初始化sha512/sha384计算用到的常数,用的是SHA512。再加上"lingdux_keygen"大概是用作IV之类的;Dll_RV_24088处有个字符串,大概是Key,以ASCII "25"变换之,转为0x40字节的Hex。如果是RSA512,那就有意思了。
5. 标准答案解密出的数据长度0x33字节,含两部分:
   1) ASCII 用户名的半个MD5值(8字节,隔一字节取一个),会与输入用户名计算的MD5值作比较。
   2) 加密的提示信息。这里程序写死了,剩余的0x2B字节取0x28字节用于解密,丢弃3字节未用。
6. 提示信息的解密,Sub_Dll_RV_1DD0:使用CryptAPI,计算ASCII "lingdux"的MD5作为CALG_3DES_112的Key,解密之。标准答案的0x28字节解密出"Success\0\0...\0",共0x20字节。
7. 解出的ASCII "Success"计算MD5值,再与立即数进行比较。算是“签名验证”。这里程序再次写死,使自定义提示信息不可能。
8. 若通过验证,用user32.FindWindowW找到主窗口,取Static Ctrl_ID 03EE的句柄,显示提示信息,并清掉"Failed"。

因为提供了有效的用户名和注册码的提示信息,使分析难度大大降低!

TODO:需要仔细分析Sub_Dll_RV_1A90,看能否有Keygen的机会。还有一些细节不清楚,需要理解消化。
总之,这个CrackMe有很多有趣的东西值得学习,表示感谢!

《360安全招聘破解题CrackMe》题解——换Key法
MistHill 52PoJie.CN
07/05/2014

果然是RSA-155(RSA512)!
两个大数:N = "8566E66A...",E = "25" (37.)。

在感觉到是大数后,基本判断是非对称加密,但不能确定是哪种算法。
从它的大数表示方法开始找目标所用的库,已知的几个常用库显然不是:大数结构很简单,所有算法都是用C语言实现的,比我熟悉的libtommath还简洁。找库花了不少时间,在这个过程中已经配合大数计算器将简单的四则运算函数标注出来了。
但这显然不够弄清楚算法,即使是RSA,不同库的具体实现方法是不同的。

也许我人品好、运气不错,终于让我给找到了:SSL Library PolarSSL。我觉得这个库非常好,简单适用,对学习理解密码学理论非常有帮助,另外很多移动平台的实现也是基于PolarSSL的。感谢目标的作者让我了解到这个库!

这样一来事情就简单了许多。

因为知道RSA-155的悬赏在1999年已经被Herman J.J. te Riele成功分解,然后又开始找相关的攻击方法。
着重在RSA的低幂指数攻击上,一个是Wiener的低解密指数攻击,另一个是D. Coppersmith的低公钥指数攻击。
尝试几日无果,看来还真不是业余玩家能搞定的。时间也不够了……,于是放弃。

回到常用的换Key大法:我只要选择与目标相同的E,生成我的密钥对,用我的N替换目标的就OK了。

一种方法是将目标层层解开,换Key,再层层加密/压缩回去。这方法在我以前玩WinLicense的目标和主程序时曾用过,很麻烦。
第二,Dll注入换Key。如大家熟悉的LPK.DLL。这个办法在"Windows 8 x64"下好象无效,第一种方法是可以的。
第三,最简单的办法:待目标运行起来后,用个小程序去改目标内存的Key。

以下小程序是我的结果,都是命令行的:
[Plain Text] 纯文本查看 复制代码
2014-05-06  14:41            45,056 rsa_genkey.exe
2014-05-06  16:55            16,384 PatchRSAKey.exe
2014-05-07  00:16            28,672 Keygen4CM360.exe


1. rsa_genkey.exe
是用PolarSSL的示例源码简单修改的。生成一对密钥:
[Plain Text] 纯文本查看 复制代码
Public Key
----------
N = 860A7AB35B950C7DD9A487D246590B3827DD83C9FE3C28B635B3D0B947907030C9FC1090D6F9E35E937F254B0207695A76D0003E4BEA6C85D674A81C71963EFF
E = 25
 
Private Key
-----------
N = 860A7AB35B950C7DD9A487D246590B3827DD83C9FE3C28B635B3D0B947907030C9FC1090D6F9E35E937F254B0207695A76D0003E4BEA6C85D674A81C71963EFF
E = 25
D = 53529F4CE5E707C3DA5184DCA845307CDA82C78B66AFC647C08488AA866E8AECF57802A668A654647BD2EB27E6D67BF5C5BB7210D37DCC7A15C7BBF2E119D3E5
P = F6E7349ED26DA41C8E6F8D746728FD24B7CC53439546B8D348486F98E80136D5
Q = 8AFAC16BE1C374B6A20A33CE114299AA5F883843D1B7F05254DFC760FE127083
DP = 64188AF447788EA3CB0AA1215A410CAE0552D5A5C6E551D91D4DCC60956846A9
DQ = 7BF467526192A65DB31DDB2D6957136E70E146F06119ACD3CF28738DDBAF94C7
QP = 9B4B22409E6B51ADC0B6E53BA5B7934E0E223CEE52CA611ABA098A58095E4ED1


2. PatchRSAKey.exe
先运行目标,再运行它。一个示例输出(只在Windows XP SP3的虚拟机上测试过):
[Plain Text] 纯文本查看 复制代码
PatchRSAKey.exe
---------------
A RSA Public Key Modulus Patcher for 360CrackMe
by MistHill at 52pojie.CN, 05/05/2014
 
Target Process ID:      1796
Target Thread ID:       1780
Target Process Handle:  000007CC
Target Module Name:     C:\Temp\52T253719\360CrackMe.exe
 
Modules List in target process:
   00400000    C:\Temp\52T253719\360CrackMe.exe
   7C920000    C:\WINDOWS\system32\ntdll.dll
   7C800000    C:\WINDOWS\system32\kernel32.dll
   77D10000    C:\WINDOWS\system32\USER32.dll
   77EF0000    C:\WINDOWS\system32\GDI32.dll
   76300000    C:\WINDOWS\system32\IMM32.DLL
   77DA0000    C:\WINDOWS\system32\ADVAPI32.dll
   77E50000    C:\WINDOWS\system32\RPCRT4.dll
   77FC0000    C:\WINDOWS\system32\Secur32.dll
   62C20000    C:\WINDOWS\system32\LPK.DLL
   73FA0000    C:\WINDOWS\system32\USP10.dll
   762F0000    C:\WINDOWS\system32\MSIMG32.dll
   72F70000    C:\WINDOWS\system32\WINSPOOL.DRV
   77BE0000    C:\WINDOWS\system32\msvcrt.dll
   7D590000    C:\WINDOWS\system32\SHELL32.dll
   77F40000    C:\WINDOWS\system32\SHLWAPI.dll
   77180000    C:\WINDOWS\WinSxS\x86_Microsoft.Windows.Common-Controls_6595b64144ccf1df_6.0.2600.6028_x-ww_61e65202\comctl32.dll
   5ADC0000    C:\WINDOWS\system32\UxTheme.dll
   76990000    C:\WINDOWS\system32\ole32.dll
   770F0000    C:\WINDOWS\system32\OLEAUT32.dll
   74C90000    C:\WINDOWS\system32\oledlg.dll
   4AE90000    C:\WINDOWS\WinSxS\x86_Microsoft.Windows.GdiPlus_6595b64144ccf1df_1.0.6002.22791_x-ww_c8dff154\gdiplus.dll
   61880000    C:\WINDOWS\system32\OLEACC.dll
   76B10000    C:\WINDOWS\system32\WINMM.dll
   74680000    C:\WINDOWS\system32\MSCTF.dll
   73640000    C:\WINDOWS\system32\msctfime.ime
   68000000    C:\WINDOWS\system32\rsaenh.dll
   759D0000    C:\WINDOWS\system32\USERENV.dll
   5FDD0000    C:\WINDOWS\system32\netapi32.dll
   765E0000    C:\WINDOWS\system32\CRYPT32.dll
   76DB0000    C:\WINDOWS\system32\MSASN1.dll
 
Target Imagebase:       00400000
Address of exported function "LSG" in "bitchDll.dll":
        51D51F20
Module Imagebase of "bitchDll.dll":
        51D50000
Target's RSA public key (N):
        8566E66A9FAACDEAA74E77A674331C7A6E086A31C6EC837D2D8196432B7B5561A95F9E7A330866F8F8427567B8C30A8923F89572DCAB5575F2B19203B7EB6745
 
*** Patching succeeded! ***
 
You can use my keygen to create "Password" now.
Have fun!!!


3. Keygen4CM360.exe
用来计算Password:
[Plain Text] 纯文本查看 复制代码
SYNTAX:
        "Keygen4CM360.exe" Username
 
Keygen4CM360.exe MistHill
-------------------------
Keygen for 360CrackMe at: [url=http://www.52pojie.cn/thread-253719-1-1.html]http://www.52pojie.cn/thread-253719-1-1.html[/url]
By MistHill, build 422.332
 
Ciphertext (Size: 0x40)
        0A 54 44 39 EB EF 0B 3D 1D 37 B6 06 CB 5F D0 51
        02 35 0A 56 A4 DC DD A5 B1 59 C9 E0 94 5C FB AF
        FC A3 AF 50 A5 68 86 35 D5 3A 5F 5A CE 11 49 5C
        53 02 70 C7 9C 58 95 85 E7 4E 0F 11 14 34 86 48
 
Base64 Encoded as: length=88
        ClREOevvCz0dN7YGy1/QUQI1Clak3N2lsVnJ4JRc+6/8o69QpWiGNdU6X1rOEUlcUwJwx5xYlYXnTg8RFDSGSA==
 
*** RESULT ***
 
Username: MistHill
Password: 436c52454f657676437a30644e37594779312f5155514931436c616b334e326c73566e4a344a52632b362f386f363951705769474e6455365831724f45556c6355774a77783578596c59586e546738524644534753413d3d


PS:提交后,PatchRSAKey.exe在"Windows 7 x64"下测试过,工作正常(需要管理员权限)!

360CrackMeAnswer.7z (20.14 KB, 下载次数: 144)

免费评分

参与人数 13热心值 +13 收起 理由
speedboy + 1 谢谢@Thanks!
vigers + 1 我很赞同!
随意之水的一滴 + 1 大神给力!
丿丨玄 + 1 鄙人小菜鸟,根本看不懂
四步-像 + 1 我很赞同!
易木马 + 1 鞭辟入里,看看自己,太差劲了....
chaojiak47 + 1 大神就是大神,吾等小菜要学习的东西太多
九零-鑫鑫 + 1 膜拜
gggenwolai + 1 膜拜!!
bisoso + 1 太厉害了!
wTiAwTiAw + 1 谢谢@Thanks!乌龟大师
JoyChou + 1 膜拜大牛。
Bds1r + 1 太佩服

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

推荐
 楼主| MistHill 发表于 2014-5-8 17:00 |楼主
本帖最后由 MistHill 于 2014-5-9 15:08 编辑

为方便不太熟悉目标的童鞋调试,帖出我用的脚本:ByPassCreatingProcess.osc。作用是保证第一个进程不终止,又能进入关键流程。
[C] 纯文本查看 复制代码
/*
   File: "ByPassCreatingProcess.osc"
   Author: MistHill
 
   Version: 1.4.0.647
   Created: 17:07:21 2014-04-23
*/
 
   history 0
   lclr
 
   bpmc
   bphwc
   bc
 
   var OEP
   var PatchAddr
 
   mov OEP, 00527392
   mov PatchAddr, 00401D67
 
   bphws OEP, "x"
   erun
   bphwc OEP
 
/*
00401D67   3D B7000000       CMP EAX, 0xB7               ; patch eax to B7
*/
   bphws PatchAddr, "x"
   erun
   bphwc PatchAddr
   mov eax, B7
 
/*
00401C01   833D 94405B00 01  CMP DWORD PTR [5B4094], 1   ; patch [005B4094]=00000001
*/
   mov PatchAddr, [00401C01+2], 4
   mov [PatchAddr], 00000001, 4
 
   ret

OD载入目标后,运行脚本,就可以正常调试了。

解释如下:创建对话框后,定位pDlgProc(0041166C),处理WM_INITDIALOG消息,来到00401CC0。
[Asm] 纯文本查看 复制代码
<0041216C>
0041216C   6A 10                    PUSH    0x10
...
004121A9   FF15 0C575500            CALL    NEAR DWORD PTR [0x55570C]  ; user32.CreateDialogIndirectParamW
        0012FD40   00400000  |hInst = 00400000
        0012FD44   005BBBD0  |pTemplate = 360Crack.005BBBD0
        0012FD48   00000000  |hOwner = NULL
        0012FD4C   0041166C  |pDlgProc = 360Crack.0041166C
        0012FD50   00000000  \lParam = 0x0
...
004121C7   C2 1400                  RETN    0x14
</0041216C>
 
<0041166C>
0041166C   55                       PUSH    EBP
0041166D   8BEC                     MOV     EBP, ESP                   ; 0012F7DC
0041166F   817D 0C 10010000         CMP     DWORD PTR [EBP+0xC], 0x110
...
00411676   75 2A                    JNZ     SHORT 004116A2
00411678   FF75 08                  PUSH    DWORD PTR [EBP+0x8]        ; hWndMain: 005D0104; Processing WM_INITDIALOG
0041167B   E8 AD830000              CALL    00419A2D
00411680   50                       PUSH    EAX                        ; 0012FE84
00411681   68 086E5500              PUSH    00556E08
        00556E08  00556E00  ASCII "CDialog"
00411686   E8 91CE0000              CALL    0041E51C
0041168B   59                       POP     ECX
0041168C   59                       POP     ECX
0041168D   85C0                     TEST    EAX, EAX                   ; 0012FE84
0041168F   74 0C                    JE      SHORT 0041169D
00411691   8B10                     MOV     EDX, DWORD PTR [EAX]       ; 00588CFC
00411693   8BC8                     MOV     ECX, EAX
00411695   FF92 74010000            CALL    NEAR DWORD PTR [EDX+0x174] ; 360Crack.00401CC0
0041169B  /EB 07                    JMP     SHORT 004116A4
0041169D  |33C0                     XOR     EAX, EAX
0041169F  |40                       INC     EAX
004116A0  |EB 02                    JMP     SHORT 004116A4
004116A2  |33C0                     XOR     EAX, EAX
004116A4  -5D                       POP     EBP
004116A5   C2 1000                  RETN    0x10
</0041166C>
 
<00401CC0>
00401CC0   55                       PUSH    EBP
00401CC1   8BEC                     MOV     EBP, ESP
00401CC3   83E4 F8                  AND     ESP, 0xFFFFFFF8
00401CC6   81EC D8020000            SUB     ESP, 0x2D8
...
00401D0D   8D8424 C4000000          LEA     EAX, DWORD PTR [ESP+0xC4]  ; 0012F5B4
00401D14   50                       PUSH    EAX
00401D15   6A 00                    PUSH    0x0
00401D17   0F57C0                   XORPS   XMM0, XMM0
00401D1A   6A 00                    PUSH    0x0
00401D1C   C74424 14 00000000       MOV     DWORD PTR [ESP+0x14], 0x0
00401D24   660FD6442418             MOVQ    [ESP][018], XMM0
00401D2A   C74424 20 00000000       MOV     DWORD PTR [ESP+0x20], 0x0
00401D32   C78424 D0000000 68006D00 MOV     DWORD PTR [ESP+0xD0], 0x6D0068
00401D3D   C78424 D4000000 69006C00 MOV     DWORD PTR [ESP+0xD4], 006C0069
00401D48   C78424 D8000000 79000000 MOV     DWORD PTR [ESP+0xD8], 0x79
00401D53   FF15 48545500            CALL    NEAR DWORD PTR [0x555448]  ; kernel32.CreateMutexW
        0012F4E4   00000000  |pSecurity = NULL
        0012F4E8   00000000  |InitialOwner = FALSE
        0012F4EC   0012F5B4  \MutexName = "hmily"
00401D59   85C0                     TEST    EAX, EAX                   ; 000000BC, handle
00401D5B   0F84 14010000            JE      00401E75
00401D61   FF15 44545500            CALL    NEAR DWORD PTR [0x555444]  ; ntdll.RtlGetLastWin32Error
00401D67   3D B7000000              CMP     EAX, 0xB7                  ; 00000000
00401D6C  /0F84 EE000000            JE      00401E60
00401D72  |33C0                     XOR     EAX, EAX
00401D74  |68 06020000              PUSH    0x206
00401D79  |50                       PUSH    EAX
00401D7A  |66:898424 D8000000       MOV     WORD PTR [ESP+0xD8], AX
00401D82  |8D8424 DA000000          LEA     EAX, DWORD PTR [ESP+0xDA]
00401D89  |50                       PUSH    EAX
00401D8A  |E8 316E1200              CALL    00528BC0                   ; memset
00401D8F  |6A 40                    PUSH    0x40
00401D91  |8D8424 8C000000          LEA     EAX, DWORD PTR [ESP+0x8C]
00401D98  |6A 00                    PUSH    0x0
00401D9A  |50                       PUSH    EAX
00401D9B  |E8 206E1200              CALL    00528BC0                   ; memset
00401DA0  |83C4 18                  ADD     ESP, 0x18
00401DA3  |8D8424 D0000000          LEA     EAX, DWORD PTR [ESP+0xD0]  ; 0012F5C0
00401DAA  |68 08020000              PUSH    0x208
00401DAF  |50                       PUSH    EAX
00401DB0  |6A 00                    PUSH    0x0
00401DB2  |C78424 84000000 44000000 MOV     DWORD PTR [ESP+0x84], 0x44
00401DBD  |FF15 40545500            CALL    NEAR DWORD PTR [0x555440]  ; kernel32.GetModuleFileNameW
        0012F4E4   00000000  |hModule = NULL
        0012F4E8   0012F5C0  |PathBuffer = 0012F5C0
        0012F4EC   00000208  \BufSize = 208 (520.)
00401DC3  |8D4424 08                LEA     EAX, DWORD PTR [ESP+0x8]   ; 0012F4F8
00401DC7  |50                       PUSH    EAX
00401DC8  |8D4424 7C                LEA     EAX, DWORD PTR [ESP+0x7C]  ; 0012F568
00401DCC  |50                       PUSH    EAX
00401DCD  |6A 00                    PUSH    0x0
00401DCF  |6A 00                    PUSH    0x0
00401DD1  |6A 01                    PUSH    0x1
00401DD3  |6A 00                    PUSH    0x0
00401DD5  |6A 00                    PUSH    0x0
00401DD7  |6A 00                    PUSH    0x0
00401DD9  |6A 00                    PUSH    0x0
00401DDB  |8D8424 F4000000          LEA     EAX, DWORD PTR [ESP+0xF4]  ; 0012F5C0 UNICODE "C:\Temp\52T253719\360CrackMe.exe"
00401DE2  |50                       PUSH    EAX
00401DE3  |FF15 3C545500            CALL    NEAR DWORD PTR [0x55543C]  ; kernel32.CreateProcessW
        0012F4C8   0012F5C0  |ModuleFileName = "C:\Temp\52T253719\360CrackMe.exe"
        0012F4CC   00000000  |CommandLine = NULL
        0012F4D0   00000000  |pProcessSecurity = NULL
        0012F4D4   00000000  |pThreadSecurity = NULL
        0012F4D8   00000000  |InheritHandles = FALSE
        0012F4DC   00000001  |CreationFlags = DEBUG_PROCESS
        0012F4E0   00000000  |pEnvironment = NULL
        0012F4E4   00000000  |CurrentDir = NULL
        0012F4E8   0012F568  |pStartupInfo = 0012F568
        0012F4EC   0012F4F8  \pProcessInfo = 0012F4F8
00401DE9  |85C0                     TEST    EAX, EAX                   ; 00000001
00401DEB  |0F84 84000000            JE      00401E75
00401DF1  |6A 5C                    PUSH    0x5C
00401DF3  |8D4424 20                LEA     EAX, DWORD PTR [ESP+0x20]  ; 0012F50C
00401DF7  |6A 00                    PUSH    0x0
00401DF9  |50                       PUSH    EAX
00401DFA  |C74424 24 00000000       MOV     DWORD PTR [ESP+0x24], 0x0
00401E02  |E8 B96D1200              CALL    00528BC0                   ; memset 0012F50C 00 5C
00401E07  |8B35 38545500            MOV     ESI, DWORD PTR [0x555438]  ; kernel32.WaitForDebugEvent
00401E0D  |83C4 0C                  ADD     ESP, 0xC
00401E10  |8D4424 18                LEA     EAX, DWORD PTR [ESP+0x18]  ; 0012F508
00401E14  |6A FF                    PUSH    -0x1
00401E16  |50                       PUSH    EAX
00401E17  |FFD6                     CALL    NEAR ESI                   ; kernel32.WaitForDebugEvent
        0012F4E4   00401E19  /CALL to WaitForDebugEvent from 360Crack.00401E17
        0012F4E8   0012F508  |pDebugEvent = 0012F508
        0012F4EC   FFFFFFFF  \Timeout = INFINITE
00401E19  |85C0                     TEST    EAX, EAX                   ; 00000001
00401E1B  |74 29                    JE      SHORT 00401E46
00401E1D  |8B3D 34545500            MOV     EDI, DWORD PTR [0x555434]  ; kernel32.ContinueDebugEvent
00401E23 -|837C24 18 08             CMP     DWORD PTR [ESP+0x18], 0x8  ; Stack SS:[0012F508]=00000003, till == 00000008
00401E28 ||74 1C                    JE      SHORT 00401E46
00401E2A ||68 02000100              PUSH    0x10002
00401E2F ||FF7424 24                PUSH    DWORD PTR [ESP+0x24]
00401E33 ||FF7424 24                PUSH    DWORD PTR [ESP+0x24]
00401E37 ||FFD7                     CALL    NEAR EDI                   ; kernel32.ContinueDebugEvent
00401E39 ||6A FF                    PUSH    -0x1
00401E3B ||8D4424 1C                LEA     EAX, DWORD PTR [ESP+0x1C]  ; 0012F508
00401E3F ||50                       PUSH    EAX
00401E40 ||FFD6                     CALL    NEAR ESI                   ; kernel32.WaitForDebugEvent
00401E42 ||85C0                     TEST    EAX, EAX                   ; 00000001
00401E44 \|75 DD                    JNZ     SHORT 00401E23             ; loop to wait Sig.
00401E46  |6A 00                    PUSH    0x0
00401E48  |FF15 30545500            CALL    NEAR DWORD PTR [0x555430]  ; kernel32.DebugSetProcessKillOnExit
00401E4E  |FF7424 10                PUSH    DWORD PTR [ESP+0x10]       ; Stack SS:[0012F500]=00000928
00401E52  |FF15 2C545500            CALL    NEAR DWORD PTR [0x55542C]  ; kernel32.DebugActiveProcessStop
00401E58  |6A 00                    PUSH    0x0
00401E5A  |FF15 28545500            CALL    NEAR DWORD PTR [0x555428]  ; kernel32.ExitProcess
00401E60  \6A 00                    PUSH    0x0                        ; lpThreadId
00401E62   6A 00                    PUSH    0x0                        ; dwCreationFlags
00401E64   6A 00                    PUSH    0x0                        ; lpParameter
00401E66   68 C01B4000              PUSH    00401BC0                   ; lpStartAddress
00401E6B   6A 00                    PUSH    0x0                        ; dwStackSize
00401E6D   6A 00                    PUSH    0x0                        ; lpThreadAttributes
00401E6F   FF15 6C545500            CALL    NEAR DWORD PTR [0x55546C]  ; kernel32.CreateThread
00401E75   8B8C24 DC020000          MOV     ECX, DWORD PTR [ESP+0x2DC]
00401E7C   5F                       POP     EDI
00401E7D   5E                       POP     ESI
00401E7E   33CC                     XOR     ECX, ESP
00401E80   B8 01000000              MOV     EAX, 0x1
00401E85   E8 12551200              CALL    0052739C
00401E8A   8BE5                     MOV     ESP, EBP
00401E8C   5D                       POP     EBP
00401E8D   C3                       RETN
</00401CC0>

在看到用DEBUG_PROCESS创建第二个进程后,你大概就能猜到是怎么回事了。它等第二个进程起来后,自己会结束。要避免进程终止,又要能创建线程00401BC0的正确流程地址应该在00401D67处的比较。
错误号0xB7(183.)的含义:ERROR_ALREADY_EXISTS。

仅此一点不行,还有第二处。
在目标最初的静态分析时,请一定要注意"TLS Directory"!另外注意到壳的第一个区段是空的,系统载入时仅为它分配空间,所以这个结构没有作用。
(PS:在VMProtect的目标中,SOD的"Break On Tls"一定要勾选,否则它直接进VM,OD停不下来。本目标不需要勾选。)
但是一旦壳运行完毕到OEP处时,此结构就有内容了,不应忘记检查它的PIMAGE_TLS_CALLBACK,目标中TLS Callback函数只有一个00401AD0。现在开始,每创建一个线程都会将这个函数跑一遍:
[Asm] 纯文本查看 复制代码
TLS CALLBACK Function
=====================
<00401AD0>
00401AD0   833D 90405B00 00         CMP     DWORD PTR [0x5B4090], 0x0  ; DS:[005B4090]=00000001
00401AD7  /75 22                    JNZ     SHORT 00401AFB
00401AD9  |FF15 74545500            CALL    NEAR DWORD PTR [0x555474]  ; kernel32.IsDebuggerPresent
00401ADF  |8B0D 94405B00            MOV     ECX, DWORD PTR [0x5B4094]
00401AE5  |BA 01000000              MOV     EDX, 0x1
00401AEA  |85C0                     TEST    EAX, EAX
00401AEC  |0F45CA                   CMOVNE  ECX, EDX
00401AEF  |890D 94405B00            MOV     DWORD PTR [0x5B4094], ECX
00401AF5  |8915 90405B00            MOV     DWORD PTR [0x5B4090], EDX
00401AFB  -C2 0C00                  RETN    0xC
</00401AD0>

在第一次调用时,[5B4090]=0,调试器中kernel32.IsDebuggerPresent总是返回0,表示“嗨,妹儿,俺可没有调戏你哟!”。EDX=1,永远写不进[5B4094]中。[5B4094]==1?在前面那个线程00401BC0中是首先要检查的!
所以一定先要将[5B4094]置1。
在[5B4094]==1的情况下,线程00401BC0才会Hook ntdll.ZwWriteFile函数(跳向00401A40),为写文件360.dat时隐式调用内部bitchDll.dll的导出函数[005B40A0]作准备,同时创建另一个线程00401B80。
线程00401B80就是用来处理"bitchDll.dll"的:解密、解压、装载到内存并运行Dll Entrypoint初始化。
在写文件360.dat时(也标志着指针005B40A8处的参数已经准备好了),ntdll.ZwWriteFile先转到00401A40,这里检查必要的条件,满足后再创建线程00401A20,这个线程会调用内部Dll的导出函数,进入RSA512部分。

这部分更准确的流程请参阅@Hmily在36#楼的精彩描述。同时请参阅@我是用户的文章“360CM解答”,很详细!

我是很喜欢这类CrackMe的,可以与作者畅所欲言地交流、探讨、学习。一方面可以请教作者,检验自己对目标的理解是否准确、全面;作者也能从讨论中判断当初的意图是否有偏差。
若是商业软件的话就有所顾忌,即使私下讨论也不方便完全说透彻。
推荐
Hmily 发表于 2014-5-8 17:05
MistHill大哥应该是最快分析最全面分析出来流程提交答案的,非常棒了!
推荐
xjun 发表于 2014-5-8 17:03
推荐
 楼主| MistHill 发表于 2014-5-8 17:01 |楼主
本帖最后由 MistHill 于 2014-5-14 09:36 编辑

这楼说说换Key方案中的Repack方法,也是Hmily当时期待的一种方案(见#15楼),那时兴趣点在私钥上,没来得及弄,现在补上。

任务:替换目标中RSA Public Key的N,使之能够Keygen。

这个N是在内部的bitchDll.dll里(见一楼“核心代码的解密”部分),与几个较流行的商业保护软件类似,关键数据都在内部Dll里。
还好,目标的Dll严格来讲,并非在“内部”,而是附加数据(Overlay),位于目标的末尾部分。这样我们就省去了处理第一层壳RLPack的工作。

即使这样,整个过程还是显得比较啰嗦,并需要一点技巧。

一.bitchDll.dll修改
它的Dump点位于目标的0040299B处,此Call是zlib解压缩,之前已经过AES解密。OD里F8后,可Dump出完整的文件(见附件)。
[Asm] 纯文本查看 复制代码
0040299B   E8 20FD1300              CALL    005426C0
bitchDll.dll本身是用UPX加壳的,要改N就必须解压缩才行。注意,这里强调解压,而不是脱壳,这是有区别的!
脱壳很简单,将其拖进OD里,在OEP: 10010BAE处(基址10000000)用Scylla工具Dump并修复就好了,再扔进IDA里分析流程和算法。
这种“脱壳”的问题在于重定位项完全没有处理,不能使用,特别是本目标中它的基址是随机的,百分之百会挂掉。

所以需要“完美脱壳”。这样在修改N后,用其他加壳工具也是可以的。

然而,追求“完美”是要付出代价的!一楼说过,在UPX解压后,发现其中保存的原始文件"PE Header"被抹掉了,我以前的那个脚本和"upx -d"都没法工作。
只能从它的大小计算出原文件应该有5个区段(大概是".text"、".rdata"、".data"、".rsrc"和".reloc"),整个PE头需要重构!
无疑,重构是非常麻烦的。追求极致的做法是逐个找出那些被移动、修改后的结构在原文件中的位置,再还原;简单粗暴一点,至少也得补上".reloc"区段。

不用这么麻烦,我们的任务仅仅是:解压缩、修改、再压缩回去,没必要完全“脱掉”它。
那么我们需要找/写一个UPX的解压和压缩程序/函数。自然地,我们会想到用UPX的源码,也用不着费那事儿,官方已经替我们编译好了,就用upx.exe。

先试试解压:
[Plain Text] 纯文本查看 复制代码
upx.exe -d -o bitchDll_UpK.dll bitchDll.dll
提示:
[Plain Text] 纯文本查看 复制代码
upx: bitchDll.dll: CantUnpackException: file is modified/hacked/protected; take care!!!
我们知道"upx.exe -d"是依赖UPX1HEAD的,检查一下:
[Plain Text] 纯文本查看 复制代码
UPX1HEAD(header.S)
000003DB:  00 00 00 00 00       "x.xx" ASCII Version ? (probably 1.25)
000003E0:  00 00 00 00          UPX_MAGIC_LE32
000003E4:  00 00 02 0A          version: ?; format: ?; method: 02 = M_NRV2B_LE32; level: 0A
000003E8:  288A71D6             uncompressed adler32
000003EC:  345AD7C2             compressed adler32
000003F0:  0002F141             uncompressed length
000003F4:  00010777             compressed length
000003F8:  00028400             original file size (not including Overlay!)
000003FC:  26 0C 00 CB          filter id: 26; filter cto: 0C; unused: 00; header checksum: CB
看来作者有意留下了重要的解压信息,只抹掉了容易修复的部分,恢复如下:
[Plain Text] 纯文本查看 复制代码
000003DB:  31 2E 32 35 00       "1.25" ASCII Version
000003E0:  55 50 58 21          UPX_MAGIC_LE32: "UPX!" (55 50 58 21)
000003E4:  0D 09                version 0D = all; format 09 = UPX_F_WIN32_PE
再将第一个区段名"CODE",改为"UPXE"(upx.exe只检查前三个字符)。
重新运行上面的命令,这次报错:
[Plain Text] 纯文本查看 复制代码
upx: bitchDll.dll: CantUnpackException: unexpected value in the PE header
表明解压缩已经成功,UPX1HEAD里的那些adler32和length等参数都没有问题。只是原"PE Header"为空白,它没有办法进行下一步。

好了,现在我们用参数"-d -o bitchDll_UpK.dll bitchDll.dll"在OD里调试upx.exe(示例使用的是UPX 3.09w版本)。

1. Decompression
[Asm] 纯文本查看 复制代码
函数:
        void ph_decompress(PackHeader &ph, const upx_bytep in, upx_bytep out, bool verify_checksum, Filter *ft);
 
地址:00441AA8
0044ACD3   . FF50 5C                CALL    NEAR DWORD PTR [EAX+0x5C]  ;  upx.00441AA8
0044ACD6   . 8B93 E8000000          MOV     EDX, DWORD PTR [EBX+0xE8]
0044ACDC   . 83C4 14                ADD     ESP, 0x14
Stack 参数:
        0022F834   00634420     PackHeader &ph
        0022F838   00670058     upx_bytep in
        0022F83C   006807F8     upx_bytep out
        0022F840   00000001     bool verify_checksum
        0022F844   00000000     Filter *ft
在0044ACD3处下硬断(upx.exe本身是压缩过的),F9,F8(不要再继续!)。006807F8处得到解压数据。

2. Replace N
找到并替换原 N:
[Asm] 纯文本查看 复制代码
006A3880  "8566E66A9FAACDEAA74E77A674331C7A6E086A31C6EC837D2D8196432B7B5561A95F9E7A330866F8F8427567B8C30A8923F89572DCAB5575F2B19203B7EB6745"
为我的:
[Asm] 纯文本查看 复制代码
006A3880  "860A7AB35B950C7DD9A487D246590B3827DD83C9FE3C28B635B3D0B947907030C9FC1090D6F9E35E937F254B0207695A76D0003E4BEA6C85D674A81C71963EFF"

3. Compression
改寄存器EIP=00443617,EAX=00441522,运行压缩函数。
[Asm] 纯文本查看 复制代码
函数:
        bool Packer::compress(upx_bytep i_ptr, unsigned i_len, upx_bytep o_ptr, const upx_compress_config_t *cconf_parm);
 
地址:00441522
00443617   . FFD0                   CALL    NEAR EAX                   ;  upx.00441522, ret 00000001, and length=00010780(org: 00010777)
00443619   . 83C4 14                ADD     ESP, 0x14
Stack 参数:
        0022F834   00634420     PackHeader &ph
        0022F838   006807F8     upx_bytep i_ptr
        0022F83C   0002F141     unsigned i_len
        0022F840   00670058     upx_bytep o_ptr
        0022F844   00000000     upx_compress_config_t *cconf_parm
注意:继续使用第一步的栈数据(参数),1) PackHeader*上面解压后变为00634430,要改回00634420;2) 输入输出指针交换一下,并设置相应的length。
F8,压缩回去后,长度为00010780,相比原来的00010777增加了9字节。长度、压缩方法和级别等信息请在结构/类PackHeader 00634420里找。
将00670058处,长度00010780的内容Dump出来。

4. Replace DATA
用Dump出来的数据,替换bitchDll.dll第二个区段"DATA"的内容。
[Asm] 纯文本查看 复制代码
替换前(长度00010777):
        File Offset     Virtual Address
        00000400        10021000:  FF DB FF FF.55 8B EC 83.EC 14 53 89.55 EC 8B D1
                        10021010:  56 8B F2 57.33 FF 0A F8.33 DB 8D 4E.01 8D 9B 00
                        ...
                        10031760:  FE 4F E0 02.00 6C 26 02.00 8B E5 02.6E 00 00 00
        00010B70        10031770:  85 59 80 04.00 00 FF 00.00 00 00 00.00 00 00 00
替换后(长度00010780):
        File Offset     Virtual Address
        00000400        10021000:  FF DB FF FF.55 8B EC 83.EC 14 53 89.55 EC 8B D1
                        10021010:  56 8B F2 57.33 FF 0A F8.33 DB 8D 4E.01 8D 9B 00
                        ...
                        10031760:  8A AC 28 C8.48 18 45 00.CD FF A9 E0.02 00 6C 26
        00010B70        10031770:  02 00 8B E5.02 6E 00 00.A0 50 59 90.00 00 00 FF
很悬,刚好“塞”进去!地址10031780已经是这个Dll的UPX Stub入口了。如果这个位置不能容下替换后的内容,要么改Stub的位置,要么再计算新的N并重复这个过程。

将改好的Dll保存为"bitchDll_MyN.dll"。

二.bitchDll.dll压缩及加密
这个改好的Dll还需要压缩及加密才能“装配”进原目标。找不到好用的工具,这两步没法偷懒了,需要写程序来实现。

5. zlib Compression
为简单起见,使用官方编译好的Dll版本:http://zlib.net/zlib128-dll.zip
我的小程序zlibCompress.exe只用到库zlib1.dll里的两个函数:compressBound和compress。
[Plain Text] 纯文本查看 复制代码
命令行:
zlibCompress.exe bitchDll_MyN.dll
输出结果:
File: "bitchDll_MyN.dll_zlib" comprssed successfully, size: 000109AF
即Dll原大小00011200压缩为000109AF。

6. AES Encrypting
小程序AESEncrypt.exe与目标一样,使用Windows API。只不过目标是AES解密,这里是AES加密。因为没有动RLPack,加密的密钥都完全相同。
[Plain Text] 纯文本查看 复制代码
命令行:
AESEncrypt.exe bitchDll_MyN.dll_zlib
输出结果:
Encrypted successfully!
        Input file      000109AF        "bitchDll_MyN.dll_zlib"
        Output file     000109B0        "bitchDll_MyN.dll_zlib_AES"
非常好,加密后的长度000109B0,与原目标一致!(原目标这块解密后长度为000109A4)。

最后一步,将原目标文件偏移000ADE9E,长度000109B0用加密结果"bitchDll_MyN.dll_zlib_AES"替换即大功告成!
修改完毕后的目标命名为360CrackMe_Repacked.exe,配合一楼附件里的Keygen4CM360.exe使用。PatchRSAKey.exe不再需要了。

文件列表:
[Plain Text] 纯文本查看 复制代码
   Date      Time        Size  Name                             Comments
------------------- ---------  ------------------------         -----------------
2014-04-24 09:56:32     70144  Repack\bitchDll.dll              ; Dump出的原始Dll
2014-04-24 09:56:32     70144  Repack\bitchDll_MyN.dll          ; Pacth N后
2014-05-13 18:10:03     16384  Repack\zlibCompress.exe          ; 压缩小程序
2012-06-14 20:36:09    107520  Repack\zlib1.dll                 ; 压缩小程序需要的库
2014-05-13 19:02:58     16384  Repack\AESEncrypt.exe            ; 加密小程序
2014-04-10 16:20:57    780366  Repack\360CrackMe_Repacked.exe   ; 最终结果
------------------- ---------  ------------------------

360CrackMe.Repacked.by.MistHill.7z (880.64 KB, 下载次数: 51)

点评

这种解压再压缩对比修改的方法出乎意料!把upx原始信息抹了这招是学小胖子的,这样修改连pe头crc都不会变了,爽哉!  发表于 2014-5-14 10:10

免费评分

参与人数 1威望 +1 收起 理由
Hmily + 1 哈哈,赞!连解压的CRC都不用改,这方法太.

查看全部评分

推荐
Hmily 发表于 2014-5-8 22:34
我觉得题目有意思的地方是反调试和校验,越简单越容易让人忽视,再有意思的地方是如何进入真正的算法分析,外表能你让看到的都是假的,进入内脏的地方只有一处;

最简单的反调试方法被变相使用,容易让人看到就自以为是是个简单的反调试,其实不然,通过父子进程方式一方面达到防止直接调试,另一方面用来先调试自己在TLS里做一个反调试埋伏,别看简单的压缩壳,它的用处是防止静态分析和自校验,多线程处理环环相扣,完整分析下来,还是挺有意思的。

ps:反调试是可以发现大部分用了StrongOD和没用StrongOD的普通调试器,反正很坑,ximo说即使不调试,开着OD就无法注册成功,哈哈,另外发有的机器CrackMe会卡死,抓了个dump看是QQ输入法死锁了,把系统默认输入法切换到系统输入法就好了。

6#
我是用户 发表于 2014-5-8 17:08
膜拜算法大牛,我差一个RSA512,以前没怎么接触,学习了
头像被屏蔽
7#
zhang63 发表于 2014-5-8 17:10
前排暂个位先,崇拜
8#
这个昵称怎么样 发表于 2014-5-8 17:40
前排吗,哈哈
9#
Anyi 发表于 2014-5-8 17:41
虽然看不懂,过来顶下贴
10#
february 发表于 2014-5-8 17:46
好牛,膜拜啊
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-3-27 13:33

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表