【文章标题】: Obsidium_1.0.0.69 口令加密功能的完全分析
【文章作者】: SigCell
【测试软件】:对 Notepad 进行 Obsidium_1.0.0.69 口令加密
【作者声明】: 本文旨在探讨Obsidium_1.0.0.69 口令加密功能,然而对于“口令加密”问题无力提出解决方案。本文的研究对象不是Obsidium_1.0.0.69的脱壳,这点在kanxue老大的文章中已经写的很清楚了。
------------------------------------------------------------------------------------------
【技巧说明】:可以用“花指令去除器”去除Obsidium的花指令,此时可以把干净的代码拷贝出来,去掉无效的nop指令,首先静态的观察和分析,找出关键点以及猜测代码的大致功能。在调试之前,需要把花指令还原回来,避免自校验带的不必要麻烦。(另外下面的代码绝大部分是去除花指令后整理所得。)
【需要基础】:SEH + Stolen Code + WndProc
【详细过程】(原文有色彩、下华线等标记,附件中提供原文和测试软件)
(1)确定密码框是在第一个div 0之后和第二div 0之前出现。来到第一个div 0异常处.
(2)Ctr + G ==> 009304F5([ESP+4]) ==> F2 ==> SHIFT+F9 ==> 撤消断点:
1th_Handler ()
{
009304F5 90 nop ;
009304FA C8 000000 enter 0, 0
009304FE E8 00000000 call 00930503
00930507 5A pop edx
0093050C 8B4D 10 mov ecx, [ebp+10] ; ecx指向CONTEXT
0093050F 33C0 xor eax, eax
00930516 8D92 D4000000 lea edx, [edx+D4] ; edx = 9305D7
00930522 8991 B8000000 mov [ecx+B8], edx ; eip = edx = 9305D7
00930530 8941 04 mov [ecx+4], eax ; dr0 = 0
00930536 4A dec edx
0093053D 8951 0C mov [ecx+C], edx ; dr2 = edx - 1
00930544 42 inc edx
0093054B 8941 08 mov [ecx+8], eax ; dr1 = 0
00930553 8951 10 mov [ecx+10], edx ; dr3 = edx
0093055A C741 18 5500333>mov dword ptr [ecx+18], 33330055
00930567 C9 leave
00930568 C3 retn
}
(3)Ctrl + G ==> 9305D7 ==> F2 ==> SHIFT+F9 ==> 撤消断点:
009305D7 90 nop ; 返回到这里
009305DA 8B4D 10 mov ecx, [ebp+10]
009305DD 8D93 62FFFFFF lea edx, [ebx-9E]
009305E9 81B9 AC000000 C>cmp dword ptr [ecx+AC], 0CC ;检测int 3
009305F7 ^ 74 C3 je short 009305BC
009305FD 8991 B8000000 mov [ecx+B8], edx ;eip = edx =009304E0
0093060D 8941 04 mov [ecx+4], eax
00930614 4A dec edx
0093061A 8941 08 mov [ecx+8], eax
00930620 8951 0C mov [ecx+C], edx
00930626 8951 10 mov [ecx+10], edx
0093062E C741 18 5500333>mov dword ptr [ecx+18], 33330055
0093063E 64:8F00 pop dword ptr fs:[eax]
00930644 83C4 04 add esp, 4
0093064C 5B pop ebx
0093064D C9 leave
0093064E C3 retn
(4)Ctrl + G ==> 009304E0 ==> F2 ==> SHIFT+F9 ==> 撤消断点:
009304E0 64:8F00 pop dword ptr fs:[eax] ;删除异常帧
009304E9 83C4 04 add esp, 4
009304F0 5B pop ebx
009304F1 C3 retn
(5)返回到这里:
0101DEDF FF55FC call [ebp-4]
0101DEE6 F7470C0400000> test dword ptr [edi+C], 4 ; 返回到这里
0101DEF3 74 1E je short 0101DF13
0101DEF9 85C0 test eax, eax
0101DEFE 74 13 je short 0101DF13
0101DF09 50 push eax
0101DF0A FF53 2C call [ebx+2C]
0101DF19 8B07 mov eax, [edi]
0101DF1E 8D7C07 14 lea edi, [edi+eax+14]
0101DF28 ^ E9 64FCFFFF jmp 0101DB91 ;大循环
0101DF35 8D47 14 lea eax, [edi+14]
0101DF3C 64:67:8F06 0000 pop dword ptr fs:[0]
0101DF46 83C4 04 add esp, 4
0101DF4C 5F pop edi
0101DF4D 5B pop ebx
0101DF4E 5E pop esi
0101DF4F C9 leave
0101DF50 C2 0C00 retn 0C ;直接在这里F2,F9,跳出大循环
(6)返回到这里:
0101D61E 90 nop ;返回到这里
0101D623 85C0 test eax, eax
0101D628 0F84 C3000000 je 0101D6F1
0101D634 8BF8 mov edi, eax
0101D642 83BD B4B5B200 0>cmp dword ptr [ebp+B2B5B4], 0
0101D64C 74 25 je short 0101D673
0101D658 55 push ebp
0101D659 56 push esi
0101D65A E8 FA010000 call 0101D859 ; 关键函数,进 ==>(7)
0101D663 85C0 test eax, eax ;如果在上一行F8的话,会显示密码输入框
0101D668 ^ 0F84 B6FBFFFF je 0101D224 ;伪爆破点1(暴力分子最喜欢的地方)
0101D67C 8B46 04 mov eax, [esi+4] ;eax = 981750
0101D684 8D50 10 lea edx, [eax+10] ;edx = 981760
0101D68A 8D85 B7BAB200 lea eax, [ebp+B2BAB7] ;eax = 101E01B
0101D699 68 80000000 push 80 ;参数3:80
0101D69E 50 push eax ;参数2:eax = 101E01B
0101D69F 52 push edx ;参数1:edx = 981760
0101D6A0 FF56 18 call [esi+18] ;根据输入的密码产生子密钥
0101D6AB 8B46 04 mov eax, [esi+4]
0101D6B1 8D50 10 lea edx, [eax+10]
0101D6B7 8D8D DBBAB200 lea ecx, [ebp+B2BADB]
0101D6C6 68 5D0C0000 push 0C5D ; 参数4:长度 = C50
0101D6CB 51 push ecx ; 参数3:存放解密代码的地址,ecx = 101E03F
0101D6CC 6A 0A push 0A ;参数2:
0101D6CE 52 push edx ; 参数1:edx = 931760
0101D6CF FF56 1C call [esi+1C] ;利用子密钥解密代码
0101D6DC E9 5E090000 jmp 0101E03F ;关键的JMP,去执行解密后的代码(如果正确解密的话,可以正常执行,否则就是执行垃圾指令,无法正常执行)
伪爆破点1:如果修改为jne后,最终却发现不能正常执行,原因是0101E03F处的代码是动态解密的,如果没有输入正确的密码解密出来的就是错误的垃圾代码。大致跟了下0101D6A0和0101D6CF,发现是密码学算法,N复杂,所以估计前者是计算子密钥,后着是解密代码。
起初到了这里看到爆破点,眼神一亮,调了两次,绝望了。动态解密,而使用的加密算法肯定是不可逆的,也就是说在没有正确密码的情况下,绝对无法正确解密代码,此路不通。那么唯一的一条路:跟踪用于密码输入的对话框。解剖它的消息处理函数。绝望了,又出现希望,害怕再一次绝望。continue_debug,路漫漫,看不到终点。
(7)进入(6)中的关键函数:
0101D859 C8 040000 enter 4, 0 ; 进到这里
0101D85D 53 push ebx
0101D85E 56 push esi
0101D85F 57 push edi
0101D860 8B5D 08 mov ebx, [ebp+8] ; 第2参数
0101D863 8B45 0C mov eax, [ebp+C] ; 第1参数
0101D866 8B7B 04 mov edi, [ebx+4]
0101D869 83C7 03 add edi, 3
0101D86C 8DB0 0DB2B200 lea esi, [eax+B2B20D]
0101D872 83E7 FC and edi, FFFFFFFC
0101D875 B9 3A000000 mov ecx, 3A
0101D87A 897D FC mov [ebp-4], edi
0101D87D F3:A5 rep movs dword ptr es:[edi], dword ptr [esi]
0101D87F 8143 04 E800000>add dword ptr [ebx+4], 0E8
0101D886 6A 00 push 0
0101D888 68 706586B1 push B1866570
0101D88D 6A 00 push 0
0101D88F FF53 20 call [ebx+20] ; [ebx+20] = 010264CA
0101D892 8B55 0C mov edx, [ebp+C] ; 此时eax = 1000000
0101D895 899A B0B5B200 mov [edx+B2B5B0], ebx
0101D89B 8D92 6CB3B200 lea edx, [edx+B2B36C]
0101D8A1 6A 00 push 0
0101D8A3 52 push edx
0101D8A4 6A 00 push 0
0101D8A6 FF75 FC push dword ptr [ebp-4]
0101D8A9 50 push eax
0101D8AA 68 5C96F40A push 0AF4965C
0101D8AF 6A 01 push 1
0101D8B1 FF53 20 call [ebx+20] ; 关键函数,实现Stolen Code,并执行(显示对话框)==>(8)
0101D8B4 816B 04 E800000>sub dword ptr [ebx+4], 0E8 ;密码输入的对话框结束后返回到这一行(不能在这里F2设断,有自校验的,可以通过在上行直接F8),密码正确时eax = 1,点击对话中取消按钮时eax = 0
0101D8BB 83F8 FF cmp eax, -1 ;这两行为无效指令(伪爆破点2)
0101D8BE 74 07 je short 0101D8C7 ;总是不跳
0101D8C0 5F pop edi
0101D8C1 5E pop esi
0101D8C2 5B pop ebx
0101D8C3 C9 leave
0101D8C4 C2 0800 retn 8
0101D8C7 33C0 xor eax, eax
0101D8C9 5F pop edi
0101D8CA 5E pop esi
0101D8CB 5B pop ebx
0101D8CC C9 leave
0101D8CD C2 0800 retn 8
伪爆破点2:看到这里,有了上次的经验,知道怎么爆也是没有用的。
(8)进入101D8B1的CALL,分析下Stolen Code的细节(抽的是DialogBoxIndirectParamA):
010264CA 90 nop
010264D0 60 pushad
010264D5 E8 00000000 call 010264DA
010264DE 5B pop ebx
010264E5 81EB C85AB300 sub ebx, 0B35AC8
010264EF 8B9B B45AB300 mov ebx, [ebx+B35AB4]
010264FD 8B4424 24 mov eax, [esp+24]
0102650B 33C9 xor ecx, ecx
01026513 8B4483 48 mov eax, [ebx+eax*4+48]
0102651C 8B5424 28 mov edx, [esp+28]
01026525 51 push ecx
01026526 51 push ecx
01026527 51 push ecx
01026528 52 push edx
01026529 50 push eax
0102652A FF53 40 call [ebx+40] ; 取USER32.DialogBoxIndirectParamA地址
01026532 85C0 test eax, eax ;检测是否成功
01026538 0F84 DE010000 je 0102671C
01026544 8BF0 mov esi, eax ;esi = USER32.DialogBoxIndirectParamA
0102654D 83EC 28 sub esp, 28
01026553 8BFC mov edi, esp ;edi = 0006FDEC,stolen code存放点(动态变化)
01026559 C74424 4C 02000>mov dword ptr [esp+4C], 2
01026567 897424 50 mov [esp+50], esi
01026571 90 nop ;循环起点
0102657B 803E C2 cmp byte ptr [esi], 0C2 ;如果当前指令的第一字节是C2
01026584 0F84 B1000000 je 0102663B ;就跳出循环
0102658F 803E C3 cmp byte ptr [esi], 0C3 ;如果当前指令的第一字节是C3
01026598 0F84 9D000000 je 0102663B ;就跳出循环
010265A1 56 push esi ;参数:指令地址
010265A2 FF53 24 call [ebx+24] ;取当前指令的长度到eax
010265A8 83F8 FF cmp eax, -1
010265AB 0F84 21010000 je 010266D2
010265B7 F7C2 00060000 test edx, 600
010265C3 75 76 jnz short 0102663B
010265D1 56 push esi
010265D8 57 push edi
010265DE 8BC8 mov ecx, eax ; ecx = eax = 2
010265E3 F3:A4 rep movs byte ptr es:[edi]>; 拷贝指令
010265E9 5F pop edi
010265EF 5E pop esi
010265FB 803F CC cmp byte ptr [edi], 0CC ;检测int 3
01026603 0F84 DF000000 je 010266E8 ;也就是说,stolen code中不能下断
0102661A 03F0 add esi, eax
01026622 03F8 add edi, eax
0102662A FF4C24 4C dec dword ptr [esp+4C] ;指令数减一
01026631 0F85 3AFFFFFF jnz 01026571 ; 循环尾部
01026642 C607 E9 mov byte ptr [edi], 0E9 ; JMP
01026649 8BC6 mov eax, esi
0102664E 2BC7 sub eax, edi
01026653 83E8 05 sub eax, 5 ;计算偏移
01026659 8947 01 mov [edi+1], eax ;在edi位置形成指令JMP ********
01026666 8BC4 mov eax, esp
01026675 8B4C24 48 mov ecx, [esp+48]
01026686 8039 CC cmp byte ptr [ecx], 0CC ;检测int 3
0102668F 74 57 je short 010266E8
010266A0 894C24 50 mov [esp+50], ecx
010266AA 894424 4C mov [esp+4C], eax
010266B6 83C4 28 add esp, 28
010266BE 61 popad
010266C4 83C4 04 add esp, 4
010266CD C3 retn ;[esp] = 0006FDEC,stolen code存放点
(9)从10266CD处转到stolen code 处执行:
0006FDEC 8BFF mov edi, edi ;此时在堆栈观察DialogBoxIndirectParamA的5个参数
0006FDEE 55 push ebp
0006FDEF - E9 3E0A7A7C jmp kernel32.7C810832 ;这3句即是stolen code
0006FF6C 01000000 ASCII "MZP" ;第1参数
0006FF70 00931750 ;第2参数
0006FF74 00000000 ;第3参数
0006FF78 0101D8D0 NotepadX.0101D8D0 ;第4参数:lpDialogProc
0006FF7C 00000000 ;第5参数
Ctrl + G ==> 0101D8D0 ==> 显示如下:
DialogProc ( HWND HWND hWndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
0101D8D0 C8 000000 enter 0, 0
0101D8D4 53 push ebx
0101D8D5 8B45 0C mov eax, [ebp+C] ;eax = uMsg
0101D8D8 3D 11010000 cmp eax, 111 ;判断WM_COMMAND
0101D8DD 74 34 je short 0101D913 ;==> (10)
0101D8DF 3D 10010000 cmp eax, 110 ;判断是否WM_INITDIALOG
0101D8E4 74 07 je short 0101D8ED ;
0101D8E6 33C0 xor eax, eax ;EAX = 0
0101D8E8 5B pop ebx
0101D8E9 C9 leave
0101D8EA C2 1000 retn 10 ;return FALSE
}
(10)处理WM_COMMAND消息
0101D913 8B45 10 mov eax, [ebp+10] ;eax = uParam,控件ID,低字为控件ID
0101D916 66:3D 6700 cmp ax, 67 ;ID == 67
0101D91A 74 34 je short 0101D950 ;
0101D91C 66:3D 0100 cmp ax, 1 ;ID == 1
0101D920 74 2E je short 0101D950
0101D922 66:3D 0200 cmp ax, 2 ;ID == 2
0101D926 74 06 je short 0101D92E
0101D928 66:3D 6600 cmp ax, 66 ;ID == 66
0101D92C ^ 75 B8 jnz short 0101D8E6 ;其他控件消息就return false.
0101D92E E8 00000000 call 0101D933 ;ID = 2时,执行这里
0101D933 58 pop eax
0101D934 8B80 E1010000 mov eax, [eax+1E1]
0101D93A 6A 00 push 0
0101D93C FF75 08 push dword ptr [ebp+8]
0101D93F 68 6EF49B3B push 3B9BF46E
0101D944 6A 01 push 1
0101D946 FF50 20 call [eax+20] ;调用默认的消息处理体
0101D949 33C0 xor eax, eax ;eax = 0
0101D94B 5B pop ebx
0101D94C C9 leave
0101D94D C2 1000 retn 10 ;return FALSE
0101D950 FF75 08 push dword ptr [ebp+8] ;(参数:hWndDlg)ID = 1 或 ID = 67时,跳到这里执行
0101D953 E8 D8000000 call 0101DA30 ;检测密码是否正确 ==> ( 11 )
0101D958 83F8 FF cmp eax, -1
0101D95B ^ 74 D1 je short 0101D92E
0101D95D 85C0 test eax, eax ;输入的密码是否正确
0101D95F ^ 74 85 je short 0101D8E6 ;如果输入的密码错误,return false
0101D961 E8 00000000 call 0101D966
0101D966 58 pop eax
0101D967 8B80 AE010000 mov eax, [eax+1AE]
0101D96D 6A 01 push 1
0101D96F FF75 08 push dword ptr [ebp+8]
0101D972 68 6EF49B3B push 3B9BF46E
0101D977 6A 01 push 1
0101D979 FF50 20 call [eax+20] ;调用默认的消息处理体
0101D97C 33C0 xor eax, eax ;eax = 0
0101D97E 5B pop ebx
0101D97F C9 leave
0101D980 C2 1000 retn 10 ;return FALSE
(11)检测输入的密码是否正确
0101DA30 C8 340000 enter 34, 0
0101DA34 56 push esi
0101DA35 53 push ebx
0101DA36 57 push edi
0101DA37 E8 00000000 call 0101DA3C
0101DA3C 5B pop ebx
0101DA3D 8BF3 mov esi, ebx
0101DA3F 8B9B D8000000 mov ebx, [ebx+D8]
0101DA45 81EE D8B4B200 sub esi, 0B2B4D8
0101DA4B 68 80000000 push 80
0101DA50 FF73 04 push dword ptr [ebx+4] ; 字符串缓冲区
0101DA53 6A 65 push 65
0101DA55 FF75 08 push dword ptr [ebp+8]
0101DA58 68 79D84448 push 4844D879
0101DA5D 6A 01 push 1
0101DA5F FF53 20 call [ebx+20] ; 取得输入的字符串,返回长度
0101DA62 85C0 test eax, eax
0101DA64 0F84 97000000 je 0101DB01
0101DA6A 8BF8 mov edi, eax
0101DA6C 8D55 F0 lea edx, [ebp-10]
0101DA6F 52 push edx ; 参数3:
0101DA70 50 push eax ; 参数2:输入的长度,7
0101DA71 FF73 04 push dword ptr [ebx+4] ; 参数1:输入的字符串
0101DA74 FF53 0C call [ebx+C] ;单向的加密函数
0101DA77 8D8E B8B5B200 lea ecx, [esi+B2B5B8] ; ecx 为标准的密文
0101DA7D C745 CC 0400000>mov dword ptr [ebp-34], 4 ; 存放比较次数
0101DA84 8D55 F0 lea edx, [ebp-10] ; edx为unsigned数组,长度4
0101DA87 8B02 mov eax, [edx] ;这里开始的3句nop掉
0101DA89 3901 cmp [ecx], eax ;修改为mov eax, [ecx];mov [edx], eax
0101DA8B 75 3E jnz short 0101DACB ;这样虽然可以把密文盗窃过来,但还是不能解决问题
0101DA8D 83C2 04 add edx, 4
0101DA90 83C1 04 add ecx, 4
0101DA93 FF4D CC dec dword ptr [ebp-34] ; 比较的次数减1
0101DA96 ^ 75 EF jnz short 0101DA87 ; 继续比较(这7个指令的功能是比较密文)
0101DA98 8D45 D0 lea eax, [ebp-30]
0101DA9B 50 push eax
0101DA9C 57 push edi
0101DA9D FF73 04 push dword ptr [ebx+4]
0101DAA0 FF53 58 call [ebx+58]
0101DAA3 8DBE B7BAB200 lea edi, [esi+B2BAB7]
0101DAA9 B9 04000000 mov ecx, 4
0101DAAE 8D55 D0 lea edx, [ebp-30]
0101DAB1 8B02 mov eax, [edx]
0101DAB3 3342 04 xor eax, [edx+4]
0101DAB6 3107 xor [edi], eax
0101DAB8 83C7 04 add edi, 4
0101DABB 83C2 08 add edx, 8
0101DABE 49 dec ecx
0101DABF ^ 75 F0 jnz short 0101DAB1
0101DAC1 33C0 xor eax, eax
0101DAC3 40 inc eax
0101DAC4 5F pop edi
0101DAC5 5B pop ebx
0101DAC6 5E pop esi
0101DAC7 C9 leave
0101DAC8 C2 0400 retn 4
(12)再次绝望了,彻底的绝望
软件需要输入正确的密码才能正确执行,否则立即结束
验证密码正确性的算法为单向的(MD5,RSA)之类算法,所以在软件中只出现明文
代码的加密/解密算法是分组密码算法,需要正确的密码才能解密
这个问题,从理论上都是无法解决的。
带着希望来,带着失望去。
有了希望,可以风雨无阻;
没了希望,人生就走到了终点~~
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)