-
-
[原创]逆向分析RE-Trace Crackme by Crudd^RET 对抗狡猾的SMC及其注册机编写
-
2013-2-23 19:56 7457
-
【文章标题】: 逆向分析RE-Trace Crackme by Crudd^RET 对抗狡猾的SMC及其注册机编写
【文章作者】: 返璞归真
【软件大小】: 9.50kb
【下载地址】: Reverse RE-Trace.rar
【加壳方式】: UPolyX v0.5
【编写语言】: MASM32 / TASM32
【操作平台】: WinALL
【软件介绍】: ReTeam的一个cm
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
【详细过程】
加了压缩壳。怎么搞都可以,直接来到OEP
来看下004012ED里做了些什么
-------------------------------------InitDialog消息处理结束----------------------------------------------
大致做了下面几件事情
1.获取3个Edit的句柄并且保存下来。
2.进行了2层SMC,对00401395代码进行了SMC,修改为另一个SMC功能
使用方法如下
lea esi, dword ptr [xxxxxxx]
call 00401395
将会把xxxxxxxxx处的0x70字节的代码复制到0x4013E6处,那么下面调用Call 0x4013E6实际上调用的是call xxxxxxx处的功能.
3.获取了User32.dll下的SendMessageA入口地址存在0x40309D处,以备后面的调用
----------------------------------------------------------------------------------------------------------
;看到邪恶的GNV组合了!这次是要让 0x4013E6变成为0x40317C啊,我们来看下0x40317C
004010A1 |. E8 40030000 call 004013E6
;0x4013E6被上面的GNV组合XX之后实际功能是0x40317C,根据strlen(group)%4来确定0x4031DA的值.
004010A6 |. E8 6C000000 call 00401117
我们看下0x4011BC这个Call干了些什么
;上面mov dword ptr [edi], esi 修改的内容恰巧在 00401261函数中。
----------------------------------------------构造满足条件的注册信息----------------------------------------
这个cm大致的程序我们已经了解了。
用户输入 name Gruop 和 serial
serial前8位其实转换成了程序内部的地址,参与校验码的计算,通过group的长度来计算内部验证 getcheckval()。
设内部地址转换函数为 conhex(),校验码计算函数为 getval()
A = getval( conhex( serial[0x0]~serial[ 0x7 ] )
B = getval( conhex( serial[0x0]~serial[ 0x7 ] ) + 0x15 )
C = ( A + B )^ getcheckval( strlen( group ) )
D = conhex( serial[0x9]~serial[ 0xF ] ) ^ getcheckval( strlen( group ) )
E = ascii ( 0x47 + strlen(group) )
km验证通过的条件就是
C == D 且 E == serial[ 0x8 ] 且 strlen( name ) >= 4
再来看下 A和B,C,D都是基于用户输入的serial和 group的,
那么我们只要满足conhex( serial[0x9]~serial[ 0xF ] ) == A + B 即可满足 C == D
而那么我们只要满足serial[0x9]~serial[ 0xF ] == ascii ( A + B ) 即可满足 C == D
显然只要计算出 A + B 那么serial的 0x9位~0xF位(后8位)就可以确定下来
注册码分为三部分
8位ASC+1位ASC+8位ASC
下面来构造一个可用的注册信息
就把oep-0x401031选作计算校验码的取址的起始地址吧
那么serial前8位则为13010400,第9位为acsii( strlen( group ) + 47h )
计算出来的校验码为000C442A
下面给出一个可用的注册信息
name:返璞归真
group:hello
serial:13010400LA244C000
---------------------------------------------------------------------------------------------------------------
理理思路,虽然构造出了一个可用的注册信息,但似乎偏移了这个km的初衷,把指令数据也当作了参与注册运算的一部分,
大量用户输入信息没有参与运算。
显然有点投机取巧。
回想上面的一些常量、取址范围之间的联系。
A和B在0x401261中计算时候获取的数据 有 0x15h的偏移.
且0x401261计算过程中每次处理 0x4长度的数据,也就是4个为一组。
再来看下,我们存储用户输入信息的地方
项目 存储位置 长度存储地址 控件ID HWND存储位置
user 0x4030C0 0x4030FF 0x64 0x403113
group 0x4030D5 0x403103 0x65 0x403117
serial 0x4030EA 0x403107 0x66 0x40311B
0x4030D5 - 0x4030D5 = 0x15 正好是0x15h!
而且user小于 0x4 长度时会提示错误!
瞬间理解了作者这个km的思路!
serial包含指向user的存储地址,让user和group参与信息的校验!,这样就可以做到
不同user和group不同 serial。当然要发现serial包含指向user的存储地址需要上面的分析过程(ps:我等菜鸟才需要)
那么serial的结构可以变成
0C030400+ ascii(strlen(group) + 47h)+ ascii( getval(user)+getval(group) )
总共17位,其中前8位为固定项,第9位还是和之前一样,后八位通过上面的推论得出是 user和group 校验码之和。
注册机代码下:
--------------------------------------------------------------------------------
【经验总结】
KM的算法十分简单,关键是分析其过程以及推出Serial的格式等信息。
这个KM设计的思想十分有新意。充分利用了汇编语言的特点,程序内部有多次SMC。
还有异常处理的应用等,
其中有一点一点:
通过用户输入的数据来计算取地范围,从而获得有效数据进行计算这点做的非常好。(用户输入的Serial中包含了计算校验码用的地址,个人觉得本文分析的这个地址指向
用户user的缓冲空间应该是作者原本的意思)
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2013年02月22日 23:54:40
【文章作者】: 返璞归真
【软件大小】: 9.50kb
【下载地址】: Reverse RE-Trace.rar
【加壳方式】: UPolyX v0.5
【编写语言】: MASM32 / TASM32
【操作平台】: WinALL
【软件介绍】: ReTeam的一个cm
【作者声明】: 只是感兴趣,没有其他目的。失误之处敬请诸位大侠赐教!
--------------------------------------------------------------------------------
引言.
本文主要是分析这个cm的Serial前8位为什么是0C030400的具体过程,以及算法分析。大家可以下载分析下,非常有意思,当然大牛可以直接无视
【详细过程】
加了压缩壳。怎么搞都可以,直接来到OEP
00401031 >/$ 6A 00 push 0x0 ; /pModule = NULL 00401033 |. E8 94040000 call <jmp.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA 00401038 |. A3 A8304000 mov dword ptr [0x4030A8], eax 0040103D |. 6A 00 push 0x0 ; /lParam = NULL 0040103F |. 68 5C104000 push 0040105C ; |DlgProc = RE-Trace.0040105C 00401044 |. 6A 00 push 0x0 ; |hOwner = NULL 00401046 |. 68 A1304000 push 004030A1 ; |pTemplate = "RETWIN" 0040104B |. FF35 A8304000 push dword ptr [0x4030A8] ; |hInst = NULL 00401051 |. E8 4C040000 call <jmp.&USER32.DialogBoxParamA> ; \DialogBoxParamA 00401056 |. 50 push eax ; /ExitCode 00401057 \. E8 6A040000 call <jmp.&KERNEL32.ExitProcess> ; \ExitProcess
--------------------------------------窗体过程函数---------------------------------------------------- 0040105C /. 55 push ebp 0040105D |. 8BEC mov ebp, esp 0040105F |. 817D 0C 10010>cmp dword ptr [ebp+0xC], 0x110 ;WM_InitDialog 00401066 |. 75 13 jnz short 0040107B
-------------------------------------InitDialog消息处理---------------------------------------------- 00401068 |. FF35 A8304000 push dword ptr [0x4030A8] 0040106E |. FF75 08 push dword ptr [ebp+0x8] 00401071 |. E8 77020000 call 004012ED
来看下004012ED里做了些什么
004012ED /$ 55 push ebp 004012EE |. 8BEC mov ebp, esp 004012F0 |. 60 pushad 004012F1 |. 8D35 08324000 lea esi, dword ptr [0x403208] 004012F7 |. 8D3D 95134000 lea edi, dword ptr [0x401395] 004012FD |. B9 20000000 mov ecx, 0x20 00401302 |. F3:A4 rep movs byte ptr es:[edi], byte ptr [esi] ;进行0x20字节的拷贝,SMC?看下,0040130A call 00401395,显然隐藏着巨大的阴谋 ;我们去看下0x403208和0x401395 ----------------------0x403208处代码----------------------------- 00403208 90 nop 00403209 60 pushad 0040320A 8D3D E6134000 lea edi, dword ptr [0x4013E6] 00403210 B9 70000000 mov ecx, 0x70 00403215 F3:A4 rep movs byte ptr es:[edi], byte ptr> 00403217 61 popad 00403218 C3 retn ----------------------0x401395处代码----------------------------- 00401395 /$ 60 pushad 00401396 |. 6A 21 push 0x21 00401398 |. 6A 00 push 0x0 ; /Count = 0x0 0040139A |. A0 C0304000 mov al, byte ptr [0x4030C0] ; | 0040139F |. 66:0FB6C0 movzx ax, al ; | 004013A3 |. 66:50 push ax ; |Buffer 004013A5 |. FF35 13314000 push dword ptr [0x403113] ; |hWnd = NULL 004013AB |. E8 04010000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA 004013B0 |. 6A 21 push 0x21 004013B2 |. 6A 00 push 0x0 ; /Count = 0x0 004013B4 |. A0 EA304000 mov al, byte ptr [0x4030EA] ; | 004013B9 |. 66:0FB6C0 movzx ax, al ; | 004013BD |. 66:50 push ax ; |Buffer 004013BF |. FF35 17314000 push dword ptr [0x403117] ; |hWnd = NULL 004013C5 |. E8 EA000000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA 004013CA |. 6A 21 push 0x21 004013CC |. 6A 00 push 0x0 ; /Count = 0x0 004013CE |. A0 D5304000 mov al, byte ptr [0x4030D5] ; | 004013D3 |. 66:0FB6C0 movzx ax, al ; | 004013D7 |. 66:50 push ax ; |Buffer 004013D9 |. FF35 1B314000 push dword ptr [0x40311B] ; |hWnd = NULL 004013DF |. E8 D0000000 call <jmp.&USER32.GetWindowTextA> ; \GetWindowTextA 004013E4 |. 61 popad 004013E5 \. C3 retn ---------------------------------------------------------------- 0x403208代码正好是0x20 ,功能是对0x4013E6处代码进行替换。然而0x401395可以看出是获取用户输入信息的,把这里覆盖了?后面怎么获取. 后来发现压根就没用到这些代码,而是作者耍调皮了。 00401304 |. 8D35 23314000 lea esi, dword ptr [0x403123] 0040130A |. E8 86000000 call 00401395 ;似乎看到了什么,此时00401395代码已经变成了 nop pushad lea edi, dword ptr [0x4013E6] mov ecx, 0x70 rep movs byte ptr es:[edi], byte ptr [esi] popad retn 进行一次SMC将0x403123代码填入到0x4013E6中,看下0x403123 00403123 90 nop 00403124 58 pop eax 00403125 5B pop ebx 00403126 891D AC304000 mov dword ptr [0x4030AC], ebx 0040312C 50 push eax 0040312D 68 C0304000 push 004030C0 00403132 6A 14 push 0x14 00403134 6A 0D push 0xD 00403136 FF35 13314000 push dword ptr [0x403113] 0040313C FF15 9D304000 call dword ptr [0x40309D] 00403142 A3 FF304000 mov dword ptr [0x4030FF], eax 00403147 68 D5304000 push 004030D5 0040314C 6A 14 push 0x14 0040314E 6A 0D push 0xD 00403150 FF35 17314000 push dword ptr [0x403117] 00403156 FF15 9D304000 call dword ptr [0x40309D] 0040315C A3 03314000 mov dword ptr [0x403103], eax 00403161 68 EA304000 push 004030EA 00403166 6A 14 push 0x14 00403168 6A 0D push 0xD 0040316A FF35 1B314000 push dword ptr [0x40311B] 00403170 FF15 9D304000 call dword ptr [0x40309D] 00403176 A3 07314000 mov dword ptr [0x403107], eax 0040317B C3 retn 0x40309D应该是SendMessageA指针 0xD正好是WM_GETTEXT,这里就是获取用户输入信息的正真的地方了。显然0x4030C0,0x4030D5,0x4030EA为存储用户输入信息的地址 0x403113,0x403117,0x40311B是存放窗体上三个Edit 句柄的地址,然后获取的最大长度均为0x14,那么根据常理可以推出 004030C0存放用户名.004030D5为Group.004030EA为Serial,0x4030FF,0x403103,0x403107存放实际获取长度 -----------------------备忘 代码SMC部分 0x403123 功能为获取用户输入信息 用户名存储位置0x4030C0 0040130F |. 6A 64 push 0x64 ; /ControlID = 64 (100.) 00401311 |. FF75 08 push dword ptr [ebp+0x8] ; |hWnd 00401314 |. E8 95010000 call <jmp.&USER32.GetDlgItem> ; \GetDlgItem 00401319 |. A3 13314000 mov dword ptr [0x403113], eax ;获取ID为0x64的控件句柄并且存到0x403113中.正式上面推断的用户名edit。 0040131E |. 6A 65 push 0x65 ; /ControlID = 65 (101.) 00401320 |. FF75 08 push dword ptr [ebp+0x8] ; |hWnd 00401323 |. E8 86010000 call <jmp.&USER32.GetDlgItem> ; \GetDlgItem 00401328 |. A3 17314000 mov dword ptr [0x403117], eax 0040132D |. 6A 66 push 0x66 ; /ControlID = 66 (102.) 0040132F |. FF75 08 push dword ptr [ebp+0x8] ; |hWnd 00401332 |. E8 77010000 call <jmp.&USER32.GetDlgItem> ; \GetDlgItem 00401337 |. A3 1B314000 mov dword ptr [0x40311B], eax 0040133C |. 68 81304000 push 00403081 ; /FileName = "User32.dll" 00401341 |. E8 9E010000 call <jmp.&KERNEL32.LoadLibraryA> ; \LoadLibraryA 00401346 |. A3 99304000 mov dword ptr [0x403099], eax 0040134B |. 68 8C304000 push 0040308C ; /ProcNameOrOrdinal = "SendMessageA" 00401350 |. FF35 99304000 push dword ptr [0x403099] ; |hModule = NULL 00401356 |. E8 77010000 call <jmp.&KERNEL32.GetProcAddress> ; \GetProcAddress 0040135B |. A3 9D304000 mov dword ptr [0x40309D], eax ;获取函数入口,存储,和我们之前上面看到的地址一样,验证了我的想法。 00401360 |. 68 0C304000 push 0040300C ; /lParam = 0x40300C 00401365 |. 6A 00 push 0x0 ; |wParam = 0x0 00401367 |. 6A 0C push 0xC ; |Message = WM_SETTEXT 00401369 |. FF75 08 push dword ptr [ebp+0x8] ; |hWnd 0040136C |. FF15 9D304000 call dword ptr [0x40309D] ; \SendMessageA 00401372 |. 68 C8000000 push 0xC8 ; /RsrcName = 200. 00401377 |. FF75 0C push dword ptr [ebp+0xC] ; |hInst 0040137A |. E8 3B010000 call <jmp.&USER32.LoadIconA> ; \LoadIconA 0040137F |. 50 push eax ; /lParam 00401380 |. 6A 01 push 0x1 ; |wParam = 0x1 00401382 |. 68 80000000 push 0x80 ; |Message = WM_SETICON 00401387 |. FF75 08 push dword ptr [ebp+0x8] ; |hWnd 0040138A |. FF15 9D304000 call dword ptr [0x40309D] ; \SendMessageA 00401390 |. 61 popad 00401391 |. C9 leave 00401392 \. C2 0800 retn 0x8
-------------------------------------InitDialog消息处理结束----------------------------------------------
大致做了下面几件事情
1.获取3个Edit的句柄并且保存下来。
2.进行了2层SMC,对00401395代码进行了SMC,修改为另一个SMC功能
使用方法如下
lea esi, dword ptr [xxxxxxx]
call 00401395
将会把xxxxxxxxx处的0x70字节的代码复制到0x4013E6处,那么下面调用Call 0x4013E6实际上调用的是call xxxxxxx处的功能.
3.获取了User32.dll下的SendMessageA入口地址存在0x40309D处,以备后面的调用
----------------------------------------------------------------------------------------------------------
00401076 |. E9 96000000 jmp 00401111 0040107B |> 817D 0C 11010>cmp dword ptr [ebp+0xC], 0x111 00401082 |. 75 7D jnz short 00401101 ------------------------------------WM_COMMAND消息处理---------------------------------------------------- 00401084 |. 817D 10 E8030>cmp dword ptr [ebp+0x10], 0x3E8 ;按下了check按钮 0040108B |. 75 5A jnz short 004010E7 0040108D |. 60 pushad 0040108E |. FF75 08 push dword ptr [ebp+0x8] 00401091 |. E8 50030000 call 004013E6 ;又看到了熟悉的0x4013E6,不过根据程序流程,目前的功能应该是0x403123的功能(获取用户输入信息,窗体句柄和SendMessageA函数入口已经在InitDialog中 获取过了,所以不用担心出错) ;获取用户信息完毕了,那些变量就被填入的具体的数据。 00401096 |. 8D35 7C314000 lea esi, dword ptr [0x40317C] 0040109C |. E8 F4020000 call 00401395
;看到邪恶的GNV组合了!这次是要让 0x4013E6变成为0x40317C啊,我们来看下0x40317C
-----------------------0x40317C--------------------------------- 0040317C 90 nop 0040317D A1 03314000 mov eax, dword ptr [0x403103] ;Group的长度0x403103 00403182 BB 04000000 mov ebx, 0x4 00403187 33D2 xor edx, edx 00403189 F7F3 div ebx 0040318B 8D05 D8314000 lea eax, dword ptr [0x4031D8] 00403191 40 inc eax 00403192 40 inc eax ;对 Group % 4的进行讨论 00403193 83FA 00 cmp edx, 0x0 00403196 74 11 je short 004031A9 00403198 83FA 01 cmp edx, 0x1 0040319B 74 13 je short 004031B0 0040319D 83FA 02 cmp edx, 0x2 004031A0 74 15 je short 004031B7 004031A2 C700 1754A70E mov dword ptr [eax], 0xEA75417 004031A8 C3 retn 004031A9 C700 52455400 mov dword ptr [eax], 0x544552 004031AF C3 retn 004031B0 C700 32303034 mov dword ptr [eax], 0x34303032 004031B6 C3 retn 004031B7 C700 53757A79 mov dword ptr [eax], 0x797A7553 004031BD C3 retn ;我们看下0x4031D8 004031D8 90 nop 004031D9 B8 EFBEADDE mov eax, 0xDEADBEEF 004031DE 8B0D 0B314000 mov ecx, dword ptr [0x40310B] 004031E4 33C8 xor ecx, eax ;0x4031D8+2处正好是常数0xDEADBEEF的地址,那么个call所做的事情就很清楚了. int mod = strlen( group ); if ( mod == 0 ) mov dword ptr [0x4031DA], 0x544552 else if ( mod == 1 ) mov dword ptr [0x4031DA], 0x34303032 else if ( mod == 2 ) mov dword ptr [0x4031DA], 0x797A7553 else mov dword ptr [0x4031DA], 0xEA75417 ;但是我们发现这个常量也是在代码中的,,作者又调皮了。 -----------------------0x40317C----------------------------------
004010A1 |. E8 40030000 call 004013E6
;0x4013E6被上面的GNV组合XX之后实际功能是0x40317C,根据strlen(group)%4来确定0x4031DA的值.
004010A6 |. E8 6C000000 call 00401117
-----------------------------Call 0x401117分析------------------- ;看到下面提示信息,似乎处理用户名的地方。分析之后并不单单如此。 00401117 /$ 60 pushad 00401118 |. C705 0F314000>mov dword ptr [0x40310F], 0x0 ;标志位清0 00401122 |. 8D05 EA304000 lea eax, dword ptr [0x4030EA] ;eax -> &serial[0] 00401128 |. E8 8F000000 call 004011BC
我们看下0x4011BC这个Call干了些什么
-----------------------------Call 0x4011BC分析--------------------- 004011BC /$ 60 pushad 004011BD |. 8BF0 mov esi, eax 004011BF |. 33C9 xor ecx, ecx 004011C1 |. 33D2 xor edx, edx 004011C3 |> 83F9 06 /cmp ecx, 0x6 004011C6 |. 7F 41 |jg short 00401209 004011C8 |. 8A06 |mov al, byte ptr [esi] 004011CA |. 3C 30 |cmp al, 0x30 004011CC |. 0F8C 84000000 |jl 00401256 004011D2 |. 3C 3A |cmp al, 0x3A 004011D4 |. 7E 12 |jle short 004011E8 004011D6 |. 3C 41 |cmp al, 0x41 004011D8 |. 7C 7C |jl short 00401256 004011DA |. 3C 46 |cmp al, 0x46 004011DC |. 7E 15 |jle short 004011F3 004011DE |. 3C 61 |cmp al, 0x61 004011E0 |. 7C 74 |jl short 00401256 004011E2 |. 3C 66 |cmp al, 0x66 004011E4 |. 7E 18 |jle short 004011FE 004011E6 |. EB 6E |jmp short 00401256 004011E8 |> 2C 30 |sub al, 0x30 004011EA |. 8AD0 |mov dl, al 004011EC |. 46 |inc esi 004011ED |. C1CA 04 |ror edx, 0x4 004011F0 |. 41 |inc ecx 004011F1 |.^ EB D0 |jmp short 004011C3 004011F3 |> 2C 37 |sub al, 0x37 004011F5 |. 8AD0 |mov dl, al 004011F7 |. 46 |inc esi 004011F8 |. C1CA 04 |ror edx, 0x4 004011FB |. 41 |inc ecx 004011FC |.^ EB C5 |jmp short 004011C3 004011FE |> 2C 57 |sub al, 0x57 00401200 |. 8AD0 |mov dl, al 00401202 |. 46 |inc esi 00401203 |. C1CA 04 |ror edx, 0x4 00401206 |. 41 |inc ecx 00401207 |.^ EB BA \jmp short 004011C3 ;以上是对 byte ptr [esi] ~ byte ptr [esi + 0x6 ]进行处理,将原先的Ascii转换成16进制,并且保存在edx中 uint edx; for (int i = 0; i <= 0x6 ; ++i) { if ( str[ i ] >= 0x3A && str[i ] < 0x41 ) { str[ i ] -0x30; } else if ( str[ i ] >= 0x41 && str[ i ] < 0x46 ) { str[ i ] - 0x41; } else if ( str[ i ] >= 0x61 && str[i ] < 0x66 ) { str[ i ] - 0x61; } } 00401209 |> 8A06 mov al, byte ptr [esi] 0040120B |. 8ACA mov cl, dl 0040120D |. 66:C1E1 04 shl cx, 0x4 00401211 |. 3C 30 cmp al, 0x30 00401213 |. 7C 41 jl short 00401256 00401215 |. 3C 3A cmp al, 0x3A 00401217 |. 7E 12 jle short 0040122B 00401219 |. 3C 41 cmp al, 0x41 0040121B |. 7C 39 jl short 00401256 0040121D |. 3C 46 cmp al, 0x46 0040121F |. 7E 19 jle short 0040123A 00401221 |. 3C 61 cmp al, 0x61 00401223 |. 7C 31 jl short 00401256 00401225 |. 3C 66 cmp al, 0x66 00401227 |. 7E 20 jle short 00401249 00401229 |. EB 2B jmp short 00401256 0040122B |> 2C 30 sub al, 0x30 0040122D |. 8AC8 mov cl, al 0040122F |. C0E1 04 shl cl, 0x4 00401232 |. 66:C1E9 04 shr cx, 0x4 00401236 |. 8AD1 mov dl, cl 00401238 |. EB 1C jmp short 00401256 0040123A |> 2C 37 sub al, 0x37 0040123C |. 8AC8 mov cl, al 0040123E |. C0E1 04 shl cl, 0x4 00401241 |. 66:C1E9 04 shr cx, 0x4 00401245 |. 8AD1 mov dl, cl 00401247 |. EB 0D jmp short 00401256 00401249 |> 2C 57 sub al, 0x57 0040124B |. 8AC8 mov cl, al 0040124D |. C0E1 04 shl cl, 0x4 00401250 |. 66:C1E9 04 shr cx, 0x4 00401254 |. 8AD1 mov dl, cl ;对str[0x7]进行处理 if ( str[ 0x7 ] >=0x30 && str[ 0x7 ] <= 0x3A ) { str[ 0x7 ] - 0x30; } else if ( str[ 0x7 ] >=0x41 && str[ 0x7 ] <= 0x46 ) { str[ 0x7 ] - 0x41; } else if ( str[ 0x7 ] >=0x61 && str[ 0x7 ] <= 0x66 ) { str[ 0x7 ] - 0x61; } 00401256 |> C1CA 04 ror edx, 0x4 00401259 |. 8915 0B314000 mov dword ptr [0x40310B], edx ;将最后的的hex值存入0x40310B中 ;本函数功能将字符串中的前8个字符(0~9,a~f,A~F)转换成hex,但是转换之后的顺序是颠倒的例如 字符串为 "Ffabcd12345",则转换成 21dcbaff 0040125F |. 61 popad 00401260 \. C3 retn -----------------------------Call 0x4011BC分析结束-------------------
0040112D |. 833D FF304000>cmp dword ptr [0x4030FF], 0x4 ;用户名长度是否小于4?小于4的话则弹出提示框 00401134 |. 7C 43 jl short 00401179 00401136 |. 833D 07314000>cmp dword ptr [0x403107], 0x0 ;输入的Serial是否为空? 0040113D |. 74 71 je short 004011B0 ;上面两个条件都不满足 0040113F |. 8B35 0B314000 mov esi, dword ptr [0x40310B] ;取出Serial转换过的hex值 00401145 |. 8D3D 95124000 lea edi, dword ptr [0x401295] 0040114B |. 83C7 02 add edi, 0x2 0040114E |. 8937 mov dword ptr [edi], esi ; mov dword ptr[ 0x401297 ],esi ;过去看下,00401150 |. E8 0C010000 call 00401261
00401295 |. 8D0D 17114000 lea ecx, dword ptr [0x401117]
;老把戏又上演了,0x401297是lea ecx, dword ptr []的取指地址,又进行了SMC。而且SMC的内容和用户输入相关
;那么可以得知,用户输入的内容必须包含一个地址(转成hex后是地址),作者好狡猾~-~。
0040129B |. 33C0 xor eax, eax
;上面mov dword ptr [edi], esi 修改的内容恰巧在 00401261函数中。
---------------------------------0x0401261分析---------------------------- 00401261 /$ 60 pushad 00401262 |. C705 1F314000>mov dword ptr [0x40311F], 0x0 0040126C |. C705 21324000>mov dword ptr [0x403221], 004012E1 00401276 |. 892D 1D324000 mov dword ptr [0x40321D], ebp 0040127C |. 68 00104000 push 0040100000401281 |. 64:FF35 00000>push dword ptr fs:[0] 00401288 |. 8925 19324000 mov dword ptr [0x403219], esp 0040128E |. 64:8925 00000>mov dword ptr fs:[0], esp ;设置SEH异常,异常处理跳转的最终地址是0x4012E1,对异常处理不太了解,就一句带过啦。 004012E1 |. 64:8F05 00000>pop dword ptr fs:[0] 004012E8 |. 83C4 04 add esp, 0x4 ;难怪在序列号不满足条件的情况下,程序不会报错。 00401295 |. 8D0D 17114000 lea ecx, dword ptr [0x401117] ;用户输入不同的Serial lea ecx, dword ptr [xxxxx] 寻的地址是不同的,肯定有一些是非法的地址, ;有了上面的异常处理就不怕了,作者这招用的实在巧妙啊。 0040129B |. 33C0 xor eax, eax 0040129D |. 33D2 xor edx, edx 0040129F |. 8A01 mov al, byte ptr [ecx] 004012A1 |. C601 00 mov byte ptr [ecx], 0x0 004012A4 |. 8801 mov byte ptr [ecx], al ;用户输入不合法可能会引发异常哦 int cnt = 0; int temp = 0; for (int i = 0; i < count; ++i) { if ( str[ i ] !=0 ) { if ( str[ i +1 ] !=0 ) { temp = str[i] * str[i+1]; if ( str[ i + 2 ] != 0 ) { temp = temp * str[ i + 2 ]; temp +=str[ i + 3]; cnt+=temp; i +=4; } else { break; } } else { break; } } else { break; } } cnt+=temp; 004012A6 |> 3C 00 /cmp al, 0x0 004012A8 |. 74 25 |je short 004012CF 004012AA |. 8A59 01 |mov bl, byte ptr [ecx+0x1] 004012AD |. 80FB 00 |cmp bl, 0x0 004012B0 |. 74 1D |je short 004012CF 004012B2 |. F6E3 |mul bl ;ax = al * bl 004012B4 |. 0FB659 02 |movzx ebx, byte ptr [ecx+0x2] 004012B8 |. 66:83FB 00 |cmp bx, 0x0 004012BC |. 74 11 |je short 004012CF 004012BE |. 52 |push edx 004012BF |. F7E3 |mul ebx ; eax =eax * ebx 004012C1 |. 5A |pop edx 004012C2 |. 0241 03 |add al, byte ptr [ecx+0x3] 004012C5 |. 83C1 04 |add ecx, 0x4 004012C8 |. 03D0 |add edx, eax 004012CA |. 0FB601 |movzx eax, byte ptr [ecx] 004012CD |.^ EB D7 \jmp short 004012A6 004012CF |> 03D0 add edx, eax 004012D1 |. 0115 0F314000 add dword ptr [0x40310F], edx 004012D7 |. C705 1F314000>mov dword ptr [0x40311F], 0x1 ;标志位0x40311F 设置为1 004012E1 |. 64:8F05 00000>pop dword ptr fs:[0] 004012E8 |. 83C4 04 add esp, 0x4 004012EB |. 61 popad 004012EC \. C3 retn ---------------------------------0x0401261分析结束----------------------------
00401000 /. 55 push ebp
00401001 |. 8BEC mov ebp, esp
00401003 |. 8B45 10 mov eax, dword ptr [ebp 0x10]
00401006 |. FF35 21324000 push dword ptr [0x403221]
0040100C |. 8F80 B8000000 pop dword ptr [eax 0xB8]
00401012 |. FF35 19324000 push dword ptr [0x403219]
00401018 |. 8F80 C4000000 pop dword ptr [eax 0xC4]
0040101E |. FF35 1D324000 push dword ptr [0x40321D]
00401024 |. 8F80 B4000000 pop dword ptr [eax 0xB4]
0040102A |. B8 00000000 mov eax, 0x0
0040102F |. C9 leave
00401030 \. C3 retn
00401155 |. 833D 03314000>cmp dword ptr [0x403103], 0x0 ;Serial长度为0 ? 0040115C |. 74 19 je short 00401177 ;Serial长度不为0 0040115E |. 8B35 0B314000 mov esi, dword ptr [0x40310B] ;再次取出转换过的hex值 00401164 |. 83C6 15 add esi, 0x15 00401167 |. 8D3D 95124000 lea edi, dword ptr [0x401295] 0040116D |. 83C7 02 add edi, 0x2 00401170 |. 8937 mov dword ptr [edi], esi ;又出现SMC了,这次是把 00401297 的值改为 (hex值 + 0x15) , ;用户输入的值转换成地址要满足访问两个连续的内存块,而且间隔是0x15? 00401172 |. E8 EA000000 call 00401261 ;再一次求值 00401177 |> EB 15 jmp short 0040118E 00401179 |> 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL 0040117B |. 68 0C304000 push 0040300C ; |Title = "RE-Trace Crackme by Crudd [RET]" 00401180 |. 68 2C304000 push 0040302C ; |Text = "Enter atleast 4 characters for your name." 00401185 |. 6A 00 push 0x0 ; |hOwner = NULL 00401187 |. E8 34030000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA 0040118C |. EB 20 jmp short 004011AE 0040118E |> 8D05 EA304000 lea eax, dword ptr [0x4030EA] 00401194 |. 83C0 09 add eax, 0x9 ; eax -> &serial[ 0x9 ] 00401197 |. E8 20000000 call 004011BC ; 将Serial转换的 0x9 ~ 0xF 转换成 hex值 ? 0040119C |. 8D35 BE314000 lea esi, dword ptr [0x4031BE] 004011A2 |. E8 EE010000 call 00401395 ;再次发现 GNV组合,0x401395再次被XX
---------------------------0x4031BE分析---------------------- 004031BE 90 nop 004031BF 60 pushad 004031C0 8D1D EA304000 lea ebx, dword ptr [0x4030EA] ; ptr[ ebx ] -> Serial 004031C6 83C3 08 add ebx, 0x8 004031C9 B8 47000000 mov eax, 0x47 004031CE 0305 03314000 add eax, dword ptr [0x403103] 004031D4 3803 cmp byte ptr [ebx], al ;条件1. Serial[ 0x8 ] 要和 strlen( Group ) + 0x47 相同 004031D6 75 24 jnz short 004031FC 004031D8 90 nop 004031D9 B8 EFBEADDE mov eax, 0xDEADBEEF ;这个值应该是有 strlen( Gruop ) % 4所确定了,之前的0x40317C中 004031DE 8B0D 0B314000 mov ecx, dword ptr [0x40310B] 004031E4 33C8 xor ecx, eax 004031E6 890D 0B314000 mov dword ptr [0x40310B], ecx Serial转换的地址 004031EC 8B1D 0F314000 mov ebx, dword ptr [0x40310F] 004031F2 33D8 xor ebx, eax 004031F4 891D 0F314000 mov dword ptr [0x40310F], ebx 004031FA 61 popad 004031FB C3 retn ;上面不同的话,则设置标志位为0 004031FC C705 1F314000 0>mov dword ptr [0x40311F], 0x0 00403206 ^ EB D0 jmp short 004031D8 ---------------------------0x4031BE分析结束---------------------- hex( Serial 0x9 ~ 0xF ) xor getval(strlen(group%4)) == (hash( xxx ) ) xor getval( strlen(group%4))
004011A7 |. 60 pushad 004011A8 |. E8 39020000 call 004013E6 ;调用0x4031BE的功能,将 004011AD |. 61 popad 004011AE |> 61 popad 004011AF |. C3 retn 004011B0 |> C705 0F314000>mov dword ptr [0x40310F], 0x1 004011BA \.^ EB F2 jmp short 004011AE
004010AB |. 8D35 23314000 lea esi, dword ptr [0x403123] 004010B1 |. E8 DF020000 call 00401395 ;再次遇到gnv。。。0x4013E6替换为0x403123(获取用户输入信息的功能 ) 004010B6 |. 61 popad 004010B7 |. 833D 1F314000>cmp dword ptr [0x40311F], 0x1 ;标志为不为1则失败,上面第一个条件Serial[ 0x8 ] 要和 strlen( Group ) + 0x47 相同 004010BE |. 75 25 jnz short 004010E5 004010C0 |. A1 0F314000 mov eax, dword ptr [0x40310F] 004010C5 |. 83F8 00 cmp eax, 0x0 004010C8 |. 74 1B je short 004010E5 ;标志0x40310F为0则失败 004010CA |. 3B05 0B314000 cmp eax, dword ptr [0x40310B] ;条件dword ptr [0x40310B]要和dword ptr [0x40310F]相等 004010D0 |. 75 13 jnz short 004010E5 004010D2 |. 6A 00 push 0x0 ; /Style = MB_OK|MB_APPLMODAL 004010D4 |. 68 0C304000 push 0040300C ; |Title = "RE-Trace Crackme by Crudd [RET]" 004010D9 |. 68 56304000 push 00403056 ; |Text = "Congratulations, now code a keygen for it." 004010DE |. 6A 00 push 0x0 ; |hOwner = NULL 004010E0 |. E8 DB030000 call <jmp.&USER32.MessageBoxA> ; \MessageBoxA 004010E5 |> EB 2A jmp short 00401111 004010E7 |> 817D 10 E9030>cmp dword ptr [ebp+0x10], 0x3E9 004010EE |. 75 21 jnz short 00401111 004010F0 |. 6A 00 push 0x0 004010F2 |. 6A 00 push 0x0 004010F4 |. 6A 10 push 0x10 004010F6 |. FF75 08 push dword ptr [ebp+0x8] 004010F9 |. FF15 9D304000 call dword ptr [0x40309D] 004010FF |. EB 10 jmp short 00401111 00401101 |> 837D 0C 10 cmp dword ptr [ebp+0xC], 0x10 00401105 |. 75 0A jnz short 00401111 00401107 |. 6A 00 push 0x0 ; /Result = 0x0 00401109 |. FF75 08 push dword ptr [ebp+0x8] ; |hWnd 0040110C |. E8 97030000 call <jmp.&USER32.EndDialog> ; \EndDialog 00401111 |> 33C0 xor eax, eax 00401113 |. C9 leave 00401114 \. C2 1000 retn 0x10 -------------------------------------------------窗体过程函数结束-------------------------------------------
----------------------------------------------构造满足条件的注册信息----------------------------------------
这个cm大致的程序我们已经了解了。
用户输入 name Gruop 和 serial
serial前8位其实转换成了程序内部的地址,参与校验码的计算,通过group的长度来计算内部验证 getcheckval()。
设内部地址转换函数为 conhex(),校验码计算函数为 getval()
A = getval( conhex( serial[0x0]~serial[ 0x7 ] )
B = getval( conhex( serial[0x0]~serial[ 0x7 ] ) + 0x15 )
C = ( A + B )^ getcheckval( strlen( group ) )
D = conhex( serial[0x9]~serial[ 0xF ] ) ^ getcheckval( strlen( group ) )
E = ascii ( 0x47 + strlen(group) )
km验证通过的条件就是
C == D 且 E == serial[ 0x8 ] 且 strlen( name ) >= 4
再来看下 A和B,C,D都是基于用户输入的serial和 group的,
那么我们只要满足conhex( serial[0x9]~serial[ 0xF ] ) == A + B 即可满足 C == D
而那么我们只要满足serial[0x9]~serial[ 0xF ] == ascii ( A + B ) 即可满足 C == D
显然只要计算出 A + B 那么serial的 0x9位~0xF位(后8位)就可以确定下来
注册码分为三部分
8位ASC+1位ASC+8位ASC
下面来构造一个可用的注册信息
就把oep-0x401031选作计算校验码的取址的起始地址吧
那么serial前8位则为13010400,第9位为acsii( strlen( group ) + 47h )
计算出来的校验码为000C442A
下面给出一个可用的注册信息
name:返璞归真
group:hello
serial:13010400LA244C000
---------------------------------------------------------------------------------------------------------------
理理思路,虽然构造出了一个可用的注册信息,但似乎偏移了这个km的初衷,把指令数据也当作了参与注册运算的一部分,
大量用户输入信息没有参与运算。
显然有点投机取巧。
回想上面的一些常量、取址范围之间的联系。
A和B在0x401261中计算时候获取的数据 有 0x15h的偏移.
0040113F |. 8B35 0B314000 mov esi, dword ptr [0x40310B]
;取出Serial转换过的hex值
00401145 |. 8D3D 95124000 lea edi, dword ptr [0x401295]
0040114B |. 83C7 02 add edi, 0x2
0040114E |. 8937 mov dword ptr [edi], esi
0040115E |. 8B35 0B314000 mov esi, dword ptr [0x40310B]
;再次取出转换过的hex值
00401164 |. 83C6 15 add esi, 0x15 ;有0x15的偏移
00401167 |. 8D3D 95124000 lea edi, dword ptr [0x401295]
0040116D |. 83C7 02 add edi, 0x2
00401170 |. 8937 mov dword ptr [edi], esi
且0x401261计算过程中每次处理 0x4长度的数据,也就是4个为一组。
再来看下,我们存储用户输入信息的地方
项目 存储位置 长度存储地址 控件ID HWND存储位置
user 0x4030C0 0x4030FF 0x64 0x403113
group 0x4030D5 0x403103 0x65 0x403117
serial 0x4030EA 0x403107 0x66 0x40311B
0x4030D5 - 0x4030D5 = 0x15 正好是0x15h!
而且user小于 0x4 长度时会提示错误!
瞬间理解了作者这个km的思路!
serial包含指向user的存储地址,让user和group参与信息的校验!,这样就可以做到
不同user和group不同 serial。当然要发现serial包含指向user的存储地址需要上面的分析过程(ps:我等菜鸟才需要)
那么serial的结构可以变成
0C030400+ ascii(strlen(group) + 47h)+ ascii( getval(user)+getval(group) )
总共17位,其中前8位为固定项,第9位还是和之前一样,后八位通过上面的推论得出是 user和group 校验码之和。
注册机代码下:
#include "stdafx.h" #include <windows.h> #include <stdio.h> #include <string.h> DWORD fun( char *p ,int len ) { DWORD t = 0; for (int i = 0 ; p[ i ] != 0 && i<len ; i+=4 ) { if ( p[ i ] != 0 ) { DWORD temp = p[ i ]; if ( p[ i + 1 ] != 0 ) { temp = p[ i ] * p[ i + 1 ]; if ( p[ i + 2 ] != 0 ) { temp = temp * p[ i + 2 ]; if ( p[ i + 3 ] != 0 ) { BYTE t = p[ i + 3 ]; __asm { push eax mov eax,temp add al,t mov temp,eax pop eax } } } } t += temp; } } return t; } int _tmain(int argc, _TCHAR* argv[]) { while (true) { DWORD temp = 0 ; char szBuffer[ 255 ]; printf("Input Yourname:"); gets(szBuffer); printf("%dwqdwq",strlen( szBuffer )); temp = fun( szBuffer,strlen( szBuffer )); printf("Input YouGroup:"); gets(szBuffer); temp += fun( szBuffer,strlen( szBuffer )); printf("Your Serial: 0C030400%c", strlen( szBuffer ) + 0x47 ); sprintf( szBuffer,"%.8X\n",temp ); for (int i = 7 ; i>=0 ;i--) { printf("%c",szBuffer[i]); } puts(""); } return 0; }
给出几组正确的注册信息
name:pediy
group:pediy
srial: 0C030400L44582200
name:fobnn
group:pediy
serial: 0C030400L2B132200
name:FiGHT2013
group:FiGHT2013
serial: 0C030400PC4516100
--------------------------------------------------------------------------------
【经验总结】
KM的算法十分简单,关键是分析其过程以及推出Serial的格式等信息。
这个KM设计的思想十分有新意。充分利用了汇编语言的特点,程序内部有多次SMC。
还有异常处理的应用等,
其中有一点一点:
通过用户输入的数据来计算取地范围,从而获得有效数据进行计算这点做的非常好。(用户输入的Serial中包含了计算校验码用的地址,个人觉得本文分析的这个地址指向
用户user的缓冲空间应该是作者原本的意思)
--------------------------------------------------------------------------------
【版权声明】: 本文原创于看雪技术论坛, 转载请注明作者并保持文章的完整, 谢谢!
2013年02月22日 23:54:40
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。
赞赏
他的文章
谁下载
谁下载
看原图