首页
社区
课程
招聘
[求助]Lesco's first assembler crackme分析与疑问
发表于: 2011-11-5 00:03 5309

[求助]Lesco's first assembler crackme分析与疑问

2011-11-5 00:03
5309
【文章标题】Lesco's first assembler crackme分析与疑问
【难度级别】初入门的新手
【下载地址】见附件
【破解工具】OD,peid,Resource Hacker
【加壳方式】无壳
【保护方式】最基本Anti-debug手段
【个人声明】本人破解特菜,此CrackMe算是刚接触的,这是一个在精华2007下的一个CrackMe,因为这些CrackMe之前都有大牛发过详细的解法过程,所以我一般都不写分析的,但是今天这个比较例外,这个CrackMe比较特别,其中算法的嵌入实现想问问坛子里的朋友~这有点像16位汇编中的修改中断地址 改写中断程序..但是到了WIN32汇编我并不知道怎么实现,这个CrackMe具体的精明之处请看下文,同理 如果你是大牛请忽略分析过程直接看中间和末尾的红色字体问题~

这个CrackMe由于是在精华2007里面的,所以详细的分析过程里面肯定有,这里我就按自己的思路说一遍.

One:
首先载入PEID查看发现是MASM32 / TASM32,原来是用比较熟悉的WIN32ASM写的,那直接载入Resource Hacker
发现控件标识如下:
CONTROL "", 1001, EDIT, ....
   CONTROL "", 1002, EDIT, ....
对我们有用的只有1001和1002
Two:
载入后直接右键查找所有模块的调用,这时候看到两个GetDlgItemTextA 刚好对应两个EDIT 一个Name 一个Key,果断F2下断:

0040142F  |.  6A 15         push    15                                       ; /Count = 15 (21.)
00401431  |.  68 F8304000   push    004030F8                                 ; |Buffer = asm_cm01.004030F8
00401436  |.  68 E9030000   push    3E9                                      ; |ControlID = 3E9 (1001.)
0040143B  |.  FF75 08       push    dword ptr [ebp+8]                        ; |hWnd
0040143E  |.  E8 4F010000   call    <jmp.&user32.GetDlgItemTextA>            ; \GetDlgItemTextA
00401443  |.  83F8 05       cmp     eax, 5                                   ;  NameLen>=5



输入用户名和密码:
pediy
123456
后按F9 程序断在0x0040142F处.往后单步F8跟踪不久遍发现一个算法:

0040146E  |> /8D15 F8304000 /lea     edx, dword ptr [4030F8]
00401474  |. |0FB61C11      |movzx   ebx, byte ptr [ecx+edx]
00401478  |. |881D 04394000 |mov     byte ptr [403904], bl
0040147E  |. |41            |inc     ecx
0040147F  |. |51            |push    ecx
00401480  |. |0FAF0D FC3840>|imul    ecx, dword ptr [4038FC]                 ;  *5
00401487  |. |83C1 17       |add     ecx, 17                                 ;  +0x17
0040148A  |. |83F1 0F       |xor     ecx, 0F                                 ;  xor 0x0F
[COLOR="red"]0040148D  |. |33D9          |xor     ebx, ecx
0040148F  |. |8BC1          |mov     eax, ecx[/COLOR]
00401491  |. |8D0D F8324000 |lea     ecx, dword ptr [4032F8]
00401497  |. |030D 00394000 |add     ecx, dword ptr [403900]
0040149D  |. |51            |push    ecx
0040149E  |. |50            |push    eax                                     ; /<%u>
0040149F  |. |68 12304000   |push    00403012                                ; |Format = "%u"
004014A4  |. |51            |push    ecx                                     ; |s
004014A5  |. |E8 D0000000   |call    <jmp.&user32.wsprintfA>                 ; \wsprintfA
004014AA  |. |83C4 0C       |add     esp, 0C
004014AD  |. |59            |pop     ecx
004014AE  |. |51            |push    ecx                                     ; /String
004014AF  |. |E8 2C010000   |call    <jmp.&kernel32.lstrlenA>                ; \lstrlenA
004014B4  |. |0105 00394000 |add     dword ptr [403900], eax
004014BA  |. |59            |pop     ecx
004014BB  |. |803D 04394000>|cmp     byte ptr [403904], 0
004014C2  |.^\75 AA         \jnz     short 0040146E


这个算法很简单,直接执行到循环结束,首先说明这个算法是作者用来混淆的,但是开始我们并不知道.
这个算法算出来pediy就是:194641366358
在分析完算法之前 我们看看算法本身,红色部分是不是值得斟酌斟酌?在0x0040148D和0x0040148F两行显然是多余...思考大家会喜欢这样做么?
从这里是否可以看出这个算法带有假算法的嫌疑?
虽然对算法有疑问,但是它毕竟能算出一个'序列号'来

重新运行此程序,输入用户名和密码:
pediy
194641366358
同样中断运行到上面那算法之后往后的代码如下:

004014C4  |.  68 F4010000   push    1F4                                      ; /Count = 1F4 (500.)
004014C9  |.  68 F8344000   push    004034F8                                 ; |Buffer = asm_cm01.004034F8
004014CE  |.  68 EA030000   push    3EA                                      ; |ControlID = 3EA (1002.)
004014D3  |.  FF75 08       push    dword ptr [ebp+8]                        ; |hWnd
004014D6  |.  E8 B7000000   call    <jmp.&user32.GetDlgItemTextA>            ; \GetDlgItemTextA
004014DB  |.  66:0BC0       or      ax, ax
004014DE  |.  75 1E         jnz     short 004014FE
004014E0  |.  6A 00         push    0                                        ; /LanguageID = 0 (LANG_NEUTRAL)
004014E2  |.  6A 00         push    0                                        ; |Style = MB_OK|MB_APPLMODAL
004014E4  |.  68 76304000   push    00403076                                 ; |Title = "Lesco's first assembler crackme"
004014E9  |.  68 D8304000   push    004030D8                                 ; |Text = "Enter a serial"
004014EE  |.  6A 00         push    0                                        ; |hOwner = NULL
004014F0  |.  E8 AF000000   call    <jmp.&user32.MessageBoxExA>              ; \MessageBoxExA
004014F5  |.  B8 01000000   mov     eax, 1
004014FA  |.  C9            leave
004014FB  |.  C2 1000       retn    10
[COLOR="red"]004014FE  |>  68 F8324000   push    004032F8                                 ; /String2 = "194641366358"
00401503  |.  68 F8344000   push    004034F8                                 ; |String1 = "194641366358"[/COLOR]
00401508  |.  E8 CD000000   call    <jmp.&kernel32.lstrcmpA>                 ; \lstrcmpA



一切似乎非常顺利,具备一切正确注册码的条件~
然后程序如愿的运行到U do it 的MessageBox函数中~

004014FE  |> \68 F8324000   push    004032F8                                 ; /String2 = "194641366358"
00401503  |.  68 F8344000   push    004034F8                                 ; |String1 = "194641366358"
00401508  |.  E8 CD000000   call    <jmp.&kernel32.lstrcmpA>                 ; \lstrcmpA
0040150D  |.  0BC0          or      eax, eax
0040150F  |.  75 15         jnz     short 00401526
00401511  |.  6A 00         push    0                                        ; /Style = MB_OK|MB_APPLMODAL
00401513  |.  68 76304000   push    00403076                                 ; |Title = "Lesco's first assembler crackme"
00401518  |.  68 96304000   push    00403096                                 ; |Text = "Congrats, u did it!!!"
0040151D  |.  6A 00         push    0                                        ; |hOwner = NULL
0040151F  |.  E8 7A000000   call    <jmp.&user32.MessageBoxA>                ; \MessageBoxA
00401524  |.  EB 13         jmp     short 00401539
00401526  |>  6A 00         push    0                                        ; /Style = MB_OK|MB_APPLMODAL
00401528  |.  68 76304000   push    00403076                                 ; |Title = "Lesco's first assembler crackme"
0040152D  |.  68 AC304000   push    004030AC                                 ; |Text = "This serial sucks!!!"
00401532  |.  6A 00         push    0                                        ; |hOwner = NULL
[COLOR="red"]00401534  |.  E8 65000000   call    <jmp.&user32.MessageBoxA>                ; \MessageBoxA[/COLOR]
00401539  |>  6A 00         push    0                                        ; /ExitCode = 0
0040153B  |.  E8 76000000   call    <jmp.&kernel32.ExitProcess>              ; \ExitProcess


F8一步一步跟踪..到上面红色一行,马上就CALL MessageBox的时候  程序跳了,对 它跳了~

跳到这个地方:

0040103D  /.  55                push    ebp
0040103E  |.  8BEC              mov     ebp, esp
[COLOR="red"]00401040  |.  8D1D F8304000     lea     ebx, dword ptr [4030F8]                  ;  Name[/COLOR]
00401046  |.  33C0              xor     eax, eax
00401048  |.  66:A1 F8304000    mov     ax, word ptr [4030F8]
0040104E  |.  66:35 2FE3        xor     ax, 0E32F
00401052  |.  66:0FAFC0         imul    ax, ax                                   ;  ^2
00401056  |.  66:35 6CAB        xor     ax, 0AB6C                                ;  0xAB6C
0040105A  |.  33C9              xor     ecx, ecx
0040105C  |.  890D 00394000     mov     dword ptr [403900], ecx
00401062  |>  50                /push    eax
00401063  |.  8D1D F8304000     |lea     ebx, dword ptr [4030F8]
00401069  |.  0FB61419          |movzx   edx, byte ptr [ecx+ebx]
0040106D  |.  51                |push    ecx
0040106E  |.  66:0FB6C8         |movzx   cx, al                                  ;  AL==2D
00401072  |.  66:0FAFD1         |imul    dx, cx
00401076  |.  66:0FB6CC         |movzx   cx, ah                                  ;  AH==3C
0040107A  |.  66:0FAFD1         |imul    dx, cx
0040107E  |.  59                |pop     ecx
0040107F  |.  66:81F2 EB45      |xor     dx, 45EB
00401084  |.  51                |push    ecx
00401085  |.  C1EA 02           |shr     edx, 2                                  ;  /0x04
00401088  |.  66:03D2           |add     dx, dx                                  ;  ....
0040108B  |.  41                |inc     ecx
0040108C  |.  52                |push    edx                                     ;  edx~~~输出
0040108D  |.  50                |push    eax
0040108E  |.  53                |push    ebx
0040108F  |.  8BC1              |mov     eax, ecx
00401091  |.  BB 02000000       |mov     ebx, 2
00401096  |.  99                |cdq
00401097  |.  F7FB              |idiv    ebx                                     ;  /0x02
00401099  |.  5B                |pop     ebx
0040109A  |.  58                |pop     eax
0040109B  |.  0AD2              |or      dl, dl
0040109D  |.  75 08             |jnz     short 004010A7
0040109F  |.  8D35 0C304000     |lea     esi, dword ptr [40300C]                 ;  %X
004010A5  |.  EB 06             |jmp     short 004010AD
004010A7  |>  8D35 0F304000     |lea     esi, dword ptr [40300F]                 ;  %x
004010AD  |>  5A                |pop     edx
004010AE  |.  8D3D F8324000     |lea     edi, dword ptr [4032F8]
004010B4  |.  033D 00394000     |add     edi, dword ptr [403900]
004010BA  |.  52                |push    edx
004010BB  |.  56                |push    esi                                     ; |Format
004010BC  |.  57                |push    edi                                     ; |s
004010BD  |.  E8 B8040000       |call    <jmp.&user32.wsprintfA>                 ; \wsprintfA
004010C2  |.  83C4 0C           |add     esp, 0C
004010C5  |.  57                |push    edi                                     ; /String
004010C6  |.  E8 15050000       |call    <jmp.&kernel32.lstrlenA>                ; \lstrlenA
004010CB  |.  0105 00394000     |add     dword ptr [403900], eax
004010D1  |.  59                |pop     ecx
004010D2  |.  41                |inc     ecx
004010D3  |.  58                |pop     eax
004010D4  |.  3B0D FC384000     |cmp     ecx, dword ptr [4038FC]
004010DA  |.^ 7C 86             \jl      short 00401062



跳到红色代码行~换言之,就是在一个毫无JMP征兆的地方出现了跳转
这就是我想问的问题之一:
疑惑一:为什么会在一个毫无征兆的地方出现JMP?是OD的错误,还是作者编码的实现?

接着分析上面的代码 显然这是一个算法~我们把它用C还原:


     BYTE szKeyName[100];
	int szNameLen,i=0,j=0;
	BYTE szFirst=0,szSecond=0;
	WORD  szSum=0;
	BYTE szEnd[100]={0};
	BYTE szEE[10]={0};
	DWORD szSS=0;
       ......省略一些代码~
szNameLen=GetDlgItemText(hDlg,IDC_Name,szKeyName,sizeof(BYTE)*100);
			if(szNameLen<0x05)
			{
				SetDlgItemText(hDlg,IDC_Serial,TEXT("NameLen>=0x05h"));
				break;
			}
				__asm	mov	al, szKeyName[0]
				__asm	mov	ah, szKeyName[1]
				__asm	xor		ax,0x0E32F
				__asm	imul	ax,ax
				__asm	xor		ax,0x0AB6C
				__asm	mov	szFirst,al
				__asm	mov	szSecond,ah
				SetDlgItemText(hDlg,IDC_Serial,TEXT("[The Key Is]"));
			for (i=0;i<szNameLen;i++)
			{
				szSum=szKeyName[i];
				szSum *=szFirst;
				szSum=szSum&0xFFFF;
				szSum *=szSecond;
				szSum=szSum&0xFFFF;
				szSum =szSum^0x45EB;
				szSum /=0x04;
				szSum=szSum&0xFFFF;
				szSum *=0x02;
				szSS=szSum;
				if (i%2)
				{
					wsprintf(szEE,"%X",szSS);
					GetDlgItemText(hDlg,IDC_Serial,szEnd,sizeof(BYTE)*100);
					lstrcat(szEnd,szEE);
					SetDlgItemText(hDlg,IDC_Serial,szEnd);
				} 
				else
				{
					wsprintf(szEE,"%x",szSum);
					GetDlgItemText(hDlg,IDC_Serial,szEnd,sizeof(BYTE)*100);
					lstrcat(szEnd,szEE);
					SetDlgItemText(hDlg,IDC_Serial,szEnd);
				}
			}
/*各个变量顾名思义就知道它意思了*/


可见这里有可以算出一个序列字符:6c54366A2dacB425ce2

往后看就知道这才是正在的算法所在了~

004010F6  |.  68 F8344000       push    004034F8                                 ; /String = "194641366358" ;我们输入的注册码~
004010FB  |.  E8 E0040000       call    <jmp.&kernel32.lstrlenA>                 ; \lstrlenA  ;
00401100  |.  59                pop     ecx
00401101  |.  3BC8              cmp     ecx, eax
00401103      74 25             je      short 0040112A
00401105  |.  33C9              xor     ecx, ecx
00401107  |.  33D2              xor     edx, edx
00401109  |>  8D05 F8324000     /lea     eax, dword ptr [4032F8];正确的注册码
0040110F  |.  8D1D F8344000     |lea     ebx, dword ptr [4034F8]
00401115  |.  8A0401            |mov     al, byte ptr [ecx+eax]
00401118  |.  8A1C19            |mov     bl, byte ptr [ecx+ebx]
0040111B  |.  38D8              |cmp     al, bl
0040111D  |.  75 0B             |jnz     short 0040112A
0040111F  |.  41                |inc     ecx
00401120  |.  3B0D 00394000     |cmp     ecx, dword ptr [403900]
00401126  |.^ 7C E1             \jl      short 00401109   ;以上一堆是相等验证~



好了到这里这个CrackMe基本分析完了,这个CrackMe在精华2007中随便找的,估计里面也有相关的分析 可能那比较正确 ,我刚接触CrackMe和反汇编言语之中会有诸多谬误,望多多谅解.
这里分析下只是对这个CrackMe做个解释,然后我想问的第二个问题是:
在内存中查看会发现有MessageBoxA的ASC 这可能和伪造正确提示MessageBox的跳转有关
疑惑二:我非常希望知道的是 作者是如何通过编码实现的?这感觉好像是16位ASM的修改中断地址 修改中断程序
或者说,OD里面的函数提示 是可以人为在编码里修改的??
附件加上了我写的注册机,注册机算法部分就是上面发的C代码

[2011.11.08]
/*经过这段时间的对API的认识,我发现了实现这种情况的方法之一*/
这涉及函数约定调用问题`下面我们看_stdcall和_declspec两种约定下的同一个函数的调用的DASM:
首先是_STDCALL
0040105A  |.  FF35 30304000 push    dword ptr [403030]
00401060  |.  8F45 E4       pop     dword ptr [ebp-1C]
00401063  |.  C745 F0 06000>mov     dword ptr [ebp-10], 6
0040106A  |.  C745 F4 00000>mov     dword ptr [ebp-C], 0
00401071  |.  C745 F8 00304>mov     dword ptr [ebp-8], 00403000      ;  
00401078  |.  68 007F0000   push    7F00                             ; /RsrcName = IDI_APPLICATION
0040107D  |.  6A 00         push    0                                ; |hInst = NULL
0040107F  |.  E8 E0000000   call    <jmp.&USER32.LoadIconA>          ; \LoadIconA
......省略一些代码
00401146   $- FF25 30204000 jmp     dword ptr [<&USER32.CreateWindow>;  USER32.CreateWindowExA
0040114C   $- FF25 2C204000 jmp     dword ptr [<&USER32.DefWindowPro>;  USER32.DefWindowProcA
00401152   $- FF25 1C204000 jmp     dword ptr [<&USER32.DispatchMess>;  USER32.DispatchMessageA
00401158   $- FF25 18204000 jmp     dword ptr [<&USER32.GetMessageA>>;  USER32.GetMessageA
0040115E   $- FF25 28204000 jmp     dword ptr [<&USER32.LoadCursorA>>;  USER32.LoadCursorA
[COLOR="Red"]00401164   $- FF25 10204000 jmp     dword ptr [<&USER32.LoadIconA>]  ;  USER32.LoadIconA[/COLOR]
[COLOR="red"];到0040107F跟进后先跳到这里 看地址显然还在程序领空[/COLOR]
0040116A   $- FF25 14204000 jmp     dword ptr [<&USER32.PostQuitMess>;  USER32.PostQuitMessage
00401170   $- FF25 34204000 jmp     dword ptr [<&USER32.RegisterClas>;  USER32.RegisterClassExA
00401176   $- FF25 38204000 jmp     dword ptr [<&USER32.ShowWindow>] ;  USER32.ShowWindow
0040117C   $- FF25 20204000 jmp     dword ptr [<&USER32.TranslateMes>;  USER32.TranslateMessage
00401182   $- FF25 24204000 jmp     dword ptr [<&USER32.UpdateWindow>;  USER32.UpdateWindow
00401188   .- FF25 08204000 jmp     dword ptr [<&KERNEL32.ExitProces>;  kernel32.ExitProcess
0040118E   $- FF25 04204000 jmp     dword ptr [<&KERNEL32.GetCommand>;  kernel32.GetCommandLineA
00401194   $- FF25 00204000 jmp     dword ptr [<&KERNEL32.GetModuleH>;  kernel32.GetModuleHandleA




再看看_DECLSPEC
0042DA58  |.  6A 7C         push    7C                                       ; /RsrcName = 124.
0042DA5A  |.  A1 D0444900   mov     eax, dword ptr [hInst]                   ; |
0042DA5F  |.  50            push    eax                                      ; |hInst => NULL
0042DA60  |.  FF15 FC744900 call    dword ptr [<&USER32.LoadIconA>]          ; \LoadIconA
....省略一些代码
[COLOR="red"]77D2E8F6 >  8BFF            mov     edi, edi[/COLOR]
[COLOR="red"];到0042DA60后跟进到这里  看地址显然已经不是程序领空了 也就是说直接进入LoadIconA函数里面了~[/COLOR]
77D2E8F8    55              push    ebp
77D2E8F9    8BEC            mov     ebp, esp
77D2E8FB    66:F745 0E FFFF test    word ptr [ebp+E], 0FFFF
77D2E901    0F85 EC590100   jnz     77D442F3
77D2E907    FF75 0C         push    dword ptr [ebp+C]
77D2E90A    FF75 08         push    dword ptr [ebp+8]
77D2E90D    E8 AAFFFFFF     call    LoadIconW
77D2E912    5D              pop     ebp
77D2E913    C2 0800         retn    8
77D2E916    6A 09           push    9
77D2E918    8D7B 04         lea     edi, dword ptr [ebx+4]
77D2E91B    59              pop     ecx



对比 两种约定方式   如果在_stdcall方式下,对JMP这句进行修改  改为跳到自己写的算法CALL 然后运行完算法在JMP的话.这不久OK了.这估计是实现这个CM这种情况的方法之一

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (3)
雪    币: 69
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
2
sub_401214:
  MessageBoxA = GetProcAddress(hModule, ProcName);
  lpBaseAddress = MessageBoxA;
  JMP = 0xE9u;
  *(&JMP + 1) = 0x40103d - (MessageBoxA + 5);
  return WriteProcessMemory(hProcess, lpBaseAddress, &JMP, 5u, 0);

msgbox的头5字节被改成jmp 40103d了
2011-11-7 15:36
0
雪    币: 93
活跃值: (41)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
3
嗯 就是这个问题  感谢
2011-11-7 19:13
0
雪    币: 69
活跃值: (25)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
4
2011-11-7 19:52
0
游客
登录 | 注册 方可回帖
返回
//