吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 4663|回复: 6
收起左侧

[原创] 160 个 CrackMe 之 55 -- Elraizer.1 详细分析和追码过程

  [复制链接]
solly 发表于 2019-5-11 02:04
本帖最后由 solly 于 2019-5-11 13:53 编辑

160 个 CrackMe 之 55 -- Elraizer.1  是 32 位原生CrackMe程序,首先检查一下文件信息:
010.png
没有加壳,节的情况如下:
011.png

这个CrackMe包括一共含9个子过程,有一个统一的入口列表,如下所示:
[Asm] 纯文本查看 复制代码
71131004   .  CC            int3
71131005   $  E9 F6070000   jmp     71131800                                    ;  显示错误信息对话框
7113100A   .  E9 71080000   jmp     71131880                                    ;  NAG的DlgProc,有时间事件和修改内存指令的处理
7113100F   .  E9 4C000000   jmp     71131060                                    ;  WndProc,Windows消息处理回调函数
71131014   .  E9 C7030000   jmp     711313E0                                    ;  HookProc,键盘Hook的回调处理函数
71131019   .  E9 82090000   jmp     711319A0                                    ;  Timer回调函数
7113101E   .  E9 2D010000   jmp     71131150                                    ;  主窗口对话框的DlgProc,有对序列号进行处理的过程
71131023   $  E9 B8040000   jmp     711314E0                                    ;  注册窗口类
71131028   $  E9 84050000   jmp     711315B1                                    ;  WinMain()入口, 显示NAG和创建窗口,并调用WinMain2()
7113102D   .  E9 49050000   jmp     7113157B                                    ; WinMain2(),创建主窗口对话框(主界面),由WinMain()调用
71131032      CC            int3

这些子过程的流程关系,如下图:
流程图.jpg
下面分别説明这些子过程:

一、WinMain()过程
该过程主要完成下面几个工作:
  1),动态生成一部分反调试代码,但存在数据节,还没有写入代码节。
  2),显示NAG对话框,也可以説是Splash屏,因为8秒后会自动关闭。
  3),从指令节读取4字节的反调试代码数据,通过这个数据计算出WinMain2()子过程的入口地址并调用,如果这个代码被Crack,则地址计算错误,调用会失败。
  4),计算主对话框的资源ID值并保存,在WinMain2()中用到这个ID值。  
  5),通过CreateWindowEx创建一个标准Windows窗口,但没有显示该窗口就退出CrackMe了。
具体每条代码分析,见下面代码:
[Asm] 纯文本查看 复制代码
; WinMain() 函数,程序入口
711315B1   >  55            push    ebp                                         ;  _WinMain()。入口主函数
711315B2   .  8BEC          mov     ebp, esp
711315B4   .  6A FF         push    -1
711315B6   .  68 00701371   push    71137000                                    ;  hInstance
711315BB   .  68 841C1371   push    71131C84                                    ;  SE 处理程序安装
711315C0   .  64:A1 0000000>mov     eax, dword ptr fs:[0]
711315C6   .  50            push    eax
711315C7   .  64:8925 00000>mov     dword ptr fs:[0], esp                       ; 安装新的 SEH
711315CE   .  83C4 E0       add     esp, -20
711315D1   .  53            push    ebx
711315D2   .  56            push    esi
711315D3   .  57            push    edi
711315D4   .  8965 E8       mov     dword ptr [ebp-18], esp
; 初始化变量,主要是动态生成两组汇编指令,这里只生成了一部分,还有一部分在NAG的DlgProc中补充生成的。
; 另外, [71138A94]这个变量是记录系统中是否驻留了SoftICE,并且初始化值0x0F386就表示SoftICE运行了,后面会通过动态生成的 int 68 指令修改其值(在主对话框的DlgProc中)。
711315D7   .  C745 FC 00000>mov     dword ptr [ebp-4], 0
711315DE   .  C605 27961371>mov     byte ptr [71139627], 0B4                    ;  汇编指令1[3]
711315E5   .  66:C705 948A1>mov     word ptr [71138A94], 0F386                  ;  记录SoftICE调试状态的变量,初始化成 SoftICE 在运行,后面在主对话框的DlgProc中会修改,在HookProc中也会有影响
711315EE   .  C605 10941371>mov     byte ptr [71139410], 0A3                    ;  汇编指令2[0],第2组[0]~[4]共5字节,拼成 mov dword ptr [7113939C], eax
711315F5   .  C605 11941371>mov     byte ptr [71139411], 9C                     ;  汇编指令2[1]
711315FC   .  C605 12941371>mov     byte ptr [71139412], 93                     ;  汇编指令2[2]
71131603   .  C605 13941371>mov     byte ptr [71139413], 13                     ;  汇编指令2[3]
7113160A   .  C605 14941371>mov     byte ptr [71139414], 71                     ;  汇编指令2[4]
71131611   .  C605 24961371>mov     byte ptr [71139624], 90                     ;  汇编指令1[0],第1组共8字节,分散在各处填充的,构成一个 int 68 中断,检测SoftICE,但 WinNT 4~10 系列不支持 int 68,会报错,运行不了。
71131618   .  8B45 08       mov     eax, dword ptr [ebp+8]                      ;  hInstance
7113161B   .  A3 18941371   mov     dword ptr [71139418], eax                   ;  保存 hInstance
71131620   .  C605 26961371>mov     byte ptr [71139626], 0C0                    ;  汇编指令1[2]
71131627   .  C745 E4 655CF>mov     dword ptr [ebp-1C], BFF55C65                ;  修改传入参数的值, 0xBFF55C65
7113162E   .  C605 29961371>mov     byte ptr [71139629], 0CD                    ;  汇编指令1[5]
; 显示 NAG 对话框,就是显示一张图片,8秒后自己关闭,应该算是一个 splash 封面吧,而且这个 NAG不是纯粹的显示,还有其它功能,在后面交待。
71131635   .  6A 00         push    0                                           ; /lParam = NULL
71131637   .  68 0A101371   push    7113100A                                    ; |DlgProc = ELRAIZER.7113100A
7113163C   .  6A 00         push    0                                           ; |hOwner = NULL
7113163E   .  68 E9030000   push    3E9                                         ; |pTemplate = 3E9
71131643   .  8B0D 18941371 mov     ecx, dword ptr [71139418]                   ; |
71131649   .  51            push    ecx                                         ; |hInst => NULL
7113164A   .  FF15 50A31371 call    dword ptr [<&USER32.DialogBoxParamA>]       ; \DialogBoxParamA
; 读取TimerProc中动态生成的汇编指令,读取其中4个字节(0x68CD43B4, 就是 mov ah, 0x43 / int 68 两条指令),作为计算基数,计算出另一个函数的地址,如果直接NOP掉前面的NAG显示代码,后面就会出错,无法调用函数。
71131650   .  8D55 DC       lea     edx, dword ptr [ebp-24]                     ;  开始验证写入的汇编指令是否成功并且未被破解修改
71131653   .  52            push    edx                                         ; /pBytesRead
71131654   .  6A 04         push    4                                           ; |BytesToRead = 4
71131656   .  8D45 E0       lea     eax, dword ptr [ebp-20]                     ; |EAX===>保存4字节地址的缓冲区
71131659   .  50            push    eax                                         ; |Buffer
7113165A   .  68 FC121371   push    711312FC                                    ; |pBaseAddress = 711312FC
7113165F   .  FF15 40A21371 call    dword ptr [<&KERNEL32.GetCurrentProcess>]   ; |[GetCurrentProcess
71131665   .  50            push    eax                                         ; |hProcess
71131666   .  FF15 34A21371 call    dword ptr [<&KERNEL32.ReadProcessMemory>]   ; \ReadProcessMemory
; 下面通过计算,生成一个地址值(0x7113157B),作为 call 的地址参数
7113166C   .  8B4D E0       mov     ecx, dword ptr [ebp-20]                     ;  0x000000FF, 上面读的4字节内容,转换成后面的call指令的地址参数
7113166F   .  81E1 FF000000 and     ecx, 0FF
71131675   .  8B55 E1       mov     edx, dword ptr [ebp-1F]                     ;  0x0000FF00,FF表示读取的数据的字节位置,下面几个也一样。
71131678   .  81E2 FF000000 and     edx, 0FF
7113167E   .  2BCA          sub     ecx, edx
71131680   .  C1E1 18       shl     ecx, 18
71131683   .  8B45 E1       mov     eax, dword ptr [ebp-1F]                     ;  0x0000FF00
71131686   .  25 FF000000   and     eax, 0FF
7113168B   .  83E8 30       sub     eax, 30
7113168E   .  C1E0 10       shl     eax, 10
71131691   .  0BC8          or      ecx, eax
71131693   .  8B55 E2       mov     edx, dword ptr [ebp-1E]                     ;  0x00FF0000
71131696   .  81E2 FF000000 and     edx, 0FF
7113169C   .  81EA B8000000 sub     edx, 0B8
711316A2   .  C1E2 08       shl     edx, 8
711316A5   .  0BCA          or      ecx, edx
711316A7   .  8B45 E3       mov     eax, dword ptr [ebp-1D]                     ;  0xFF000000
711316AA   .  25 FF000000   and     eax, 0FF
711316AF   .  83C0 13       add     eax, 13
711316B2   .  0BC8          or      ecx, eax
711316B4   .  894D D8       mov     dword ptr [ebp-28], ecx                     ;  合成下面的 call 调用地址,ecx = 0x7113157B 才是正确值
;下面这个全局变量[71138A9C],是非常重要的一个变量,会影响多个地方的计算,包括后面 SN 的计算,其值为 -1(0xFFFFFFFF) 才正确,其在NAG的DlgProc中会初始化。
711316B7   .  8B0D 9C8A1371 mov     ecx, dword ptr [71138A9C]                   ;  ecx = -1, NAG的DlgProc中检查SoftIce的标志
;再次计算地址值
711316BD   .  8B55 D8       mov     edx, dword ptr [ebp-28]                     ;  下面的 call 调用地址
711316C0   .  8D444A 02     lea     eax, dword ptr [edx+ecx*2+2]                ;  eax = 0x7113157B + (-1)*2 + 2 = 0x7113157B
711316C4   .  A3 FC931371   mov     dword ptr [711393FC], eax                   ;  保存下面调用的地址(0x7113157B)
711316C9   .  8B0D 9C8A1371 mov     ecx, dword ptr [71138A9C]                   ;  ecx = -1
711316CF   .  8D9409 EA0300>lea     edx, dword ptr [ecx+ecx+3EA]                ;  计算得到主对话的框的资源ID,edx = (-1) + (-1) + 0x03EA = 0x03E8
711316D6   .  66:8915 08941>mov     word ptr [71139408], dx                     ;  dx=0x03E8=1000,主对话的框的资源ID
711316DD   .  FF15 FC931371 call    dword ptr [711393FC]                        ;  call _WinMain2(), [711393FC]==7113157B
;输入 SN 并按"Verify ?"按钮后,如果记录的 SN 正确则来到这里,否则会出错退出。
711316E3   .  6A 00         push    0                                           ; /lParam = NULL
711316E5   .  A1 18941371   mov     eax, dword ptr [71139418]                   ; |hInstance
711316EA   .  50            push    eax                                         ; |hInst => NULL
711316EB   .  6A 00         push    0                                           ; |hMenu = NULL
711316ED   .  6A 00         push    0                                           ; |hParent = NULL
711316EF   .  68 96000000   push    96                                          ; |Height = 96 (150.)
711316F4   .  68 F4010000   push    1F4                                         ; |Width = 1F4 (500.)
711316F9   .  6A 00         push    0                                           ; |Y = 0
711316FB   .  6A 00         push    0                                           ; |X = 0
711316FD   .  68 0000CF90   push    90CF0000                                    ; |Style = WS_POPUP|WS_MINIMIZEBOX|WS_MAXIMIZEBOX|WS_VISIBLE|WS_SYSMENU|WS_THICKFRAME|WS_CAPTION
71131702   .  68 388B1371   push    71138B38                                    ; |WindowName = "ATPTeam CrackMe zer0"
71131707   .  68 E88A1371   push    71138AE8                                    ; |Class = "_#_"
7113170C   .  68 00000200   push    20000                                       ; |ExtStyle = WS_EX_STATICEDGE
71131711   .  FF15 4CA31371 call    dword ptr [<&USER32.CreateWindowExA>]       ; \CreateWindowExA
71131717   .  8B0D E0931371 mov     ecx, dword ptr [711393E0]                   ;  [711393E0] == 0,作为本函数返回值
7113171D   .  894D D4       mov     dword ptr [ebp-2C], ecx                     ;  保存返回值
71131720   .  C745 FC FFFFF>mov     dword ptr [ebp-4], -1
71131727   .  8B45 D4       mov     eax, dword ptr [ebp-2C]                     ;  取得返回值
7113172A   .  EB 1F         jmp     short 7113174B                              ;  没有错误则跳入正常返回代码
7113172C   .  8B55 EC       mov     edx, dword ptr [ebp-14]
7113172F   .  52            push    edx
71131730   .  E8 D0F8FFFF   call    71131005                                    ;  显示创建窗口错误
71131735   .  C3            retn
71131736   .  8B65 E8       mov     esp, dword ptr [ebp-18]
71131739   .  A1 E0931371   mov     eax, dword ptr [711393E0]
7113173E   .  8945 D0       mov     dword ptr [ebp-30], eax
71131741   .  C745 FC FFFFF>mov     dword ptr [ebp-4], -1
71131748   .  8B45 D0       mov     eax, dword ptr [ebp-30]
7113174B   >  8B4D F0       mov     ecx, dword ptr [ebp-10]                     ;  取得原SEH
7113174E   .  64:890D 00000>mov     dword ptr fs:[0], ecx                       ;  恢复 SEH
71131755   .  5F            pop     edi
71131756   .  5E            pop     esi
71131757   .  5B            pop     ebx
71131758   .  8BE5          mov     esp, ebp
7113175A   .  5D            pop     ebp
7113175B   .  C2 1000       retn    10
7113175E      CC            int3
7113175F      CC            int3



二、NAG 的 DlgProc 过程
该子过程主要完成以下工作:
  1)、补充WinMain()中还没有生成的4个字节的汇编指令,用于反调试;
  2)、通过 CreateFile("\\\\.\\FrogSICE")来检查是否 SoftICE 调试器在运行,并通过检查结果初始化一个重要的全局变量[71138A9C],这个变量等于 -1 才是正确值;
  3)、创建一个定时器,间隔为1000ms。
如果SoftICE和FrogSICE在内存中加载,则GetLastError会返回0,则全局变量会初始化为一个不正确的值,当前过程不会有问题,但其它过程会引用该值,会引起内存访问错误。
NAG的界面如下:
001.png
具体代码分析如下:
[Asm] 纯文本查看 复制代码
; NAG 对话框的 DlgProc,主要功能就是检测SoftICE并初始化一个非常重要的全局变量[71138A9C],同时生成定时器。
71131880  />  55            push    ebp                                         ;  NAG对话框DlgProc
71131881  |.  8BEC          mov     ebp, esp
71131883  |.  83EC 08       sub     esp, 8
71131886  |.  8B45 0C       mov     eax, dword ptr [ebp+C]
71131889  |.  8945 F8       mov     dword ptr [ebp-8], eax
7113188C  |.  837D F8 10    cmp     dword ptr [ebp-8], 10                       ;  WM_CLOSE
71131890  |.  0F84 96000000 je      7113192C
71131896  |.  817D F8 10010>cmp     dword ptr [ebp-8], 110                      ;  WM_INITDIALOG
7113189D  |.  74 05         je      short 711318A4
7113189F  |.  E9 A8000000   jmp     7113194C
711318A4  |>  C605 2A961371>mov     byte ptr [7113962A], 68                     ;  汇编指令1[6],补充填充用的汇编代码
711318AB  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]
711318AE  |.  890D 04941371 mov     dword ptr [71139404], ecx
711318B4  |.  C605 2B961371>mov     byte ptr [7113962B], 90                     ;  汇编指令1[7],补充填充用的汇编代码
; 以下代码是检查 FrogSICE是否在运行,并按返回结果初始化全局变量[71138A9C]
711318BB  |.  6A 00         push    0                                           ; /hTemplateFile = NULL
711318BD  |.  68 00000004   push    4000000                                     ; |Attributes = DELETE_ON_CLOSE
711318C2  |.  6A 03         push    3                                           ; |Mode = OPEN_EXISTING
711318C4  |.  6A 00         push    0                                           ; |pSecurity = NULL
711318C6  |.  6A 03         push    3                                           ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
711318C8  |.  68 000000C0   push    C0000000                                    ; |Access = GENERIC_READ|GENERIC_WRITE
711318CD  |.  8B15 988A1371 mov     edx, dword ptr [71138A98]                   ; |ELRAIZER.71138AD8
711318D3  |.  52            push    edx                                         ; |FileName => "\\.\FROGSICE"
711318D4  |.  FF15 48A21371 call    dword ptr [<&KERNEL32.CreateFileA>]         ; \CreateFileA
711318DA  |.  8945 FC       mov     dword ptr [ebp-4], eax
711318DD  |.  C605 28961371>mov     byte ptr [71139628], 43                     ;  汇编指令1[4],补充填充用的汇编代码
711318E4  |.  FF15 44A21371 call    dword ptr [<&KERNEL32.GetLastError>]        ; [GetLastError
711318EA  |.  8B0D 9C8A1371 mov     ecx, dword ptr [71138A9C]                   ;  调试标志:0x1F,需变成0xFFFFFFFF
711318F0  |.  33C8          xor     ecx, eax                                    ;  eax==2, 表示“文件未找到”的错误
711318F2  |.  83E9 1E       sub     ecx, 1E                                     ;  ecx=0x1D-0x1E=0xFFFFFFFF
711318F5  |.  890D 9C8A1371 mov     dword ptr [71138A9C], ecx                   ;  调试标志:0x1F,在这里变成0xFFFFFFFF
; 关闭句柄
711318FB  |.  837D FC FF    cmp     dword ptr [ebp-4], -1
711318FF  |.  74 0A         je      short 7113190B
71131901  |.  8B55 FC       mov     edx, dword ptr [ebp-4]
71131904  |.  52            push    edx                                         ; /hChange
71131905  |.  FF15 50A21371 call    dword ptr [<&KERNEL32.CloseHandle>]         ; \FindCloseChangeNotification
; 补充填充用的汇编代码
7113190B  |>  C605 25961371>mov     byte ptr [71139625], 33                     ;  汇编指令1[1],补充填充用的汇编代码
; 生成定时器,在定时器中填充主Dlg的汇编代码
71131912  |.  68 19101371   push    71131019                                    ; /Timerproc = ELRAIZER.71131019
71131917  |.  68 E8030000   push    3E8                                         ; |Timeout = 1000 ms
7113191C  |.  6A 01         push    1                                           ; |TimerID = 1
7113191E  |.  A1 04941371   mov     eax, dword ptr [71139404]                   ; |
71131923  |.  50            push    eax                                         ; |hWnd => NULL
71131924  |.  FF15 40A31371 call    dword ptr [<&USER32.SetTimer>]              ; \SetTimer
7113192A  |.  EB 24         jmp     short 71131950
; WM_CLOSE处理,消毁定时器,并关闭NAG对话框
7113192C  |>  6A 01         push    1                                           ; /TimerID = 1
7113192E  |.  8B0D 04941371 mov     ecx, dword ptr [71139404]                   ; |
71131934  |.  51            push    ecx                                         ; |hWnd => NULL
71131935  |.  FF15 44A31371 call    dword ptr [<&USER32.KillTimer>]             ; \KillTimer
7113193B  |.  6A 01         push    1                                           ; /Result = 1
7113193D  |.  8B15 04941371 mov     edx, dword ptr [71139404]                   ; |
71131943  |.  52            push    edx                                         ; |hWnd => NULL
71131944  |.  FF15 68A31371 call    dword ptr [<&USER32.EndDialog>]             ; \EndDialog
7113194A  |.  EB 04         jmp     short 71131950
7113194C  |>  33C0          xor     eax, eax
7113194E  |.  EB 05         jmp     short 71131955
71131950  |>  B8 01000000   mov     eax, 1
71131955  |>  8BE5          mov     esp, ebp
71131957  |.  5D            pop     ebp
71131958  \.  C2 1000       retn    10
7113195B      CC            int3



三、定时器回调过程(Callback Proc)
该过程完成以下工作:
  1)将前面写入数据节的两段代码,调用WriteProcessMemory函数写入代码节,将 CrackMe的原有NOP代码和错误代码覆盖,不覆盖会引起非法内存访问错误;代码是分成8次写入的,每次只写1个字节
  2)8次时间事件后,会发送WM_CLOSE关闭NAG;
  3)计算代码执行时间,用于反单步调试,超时则退出,会导致代码写入不完整;
这里将数据区的代码数据写入代码区,这些代码是用于反 SoftICE 调试的,对目前用的OD没什么用,但其中一条 int 68 指令在Windows NT/2000/XP/8/10 下是不支持的,会导致程序异常,报错并退出,错误如下:
002.png
西班牙文,大概意思是:“如果你看到了这条信息,那是因为程序代码被篡改了,或者你录入了一段非常糟糕的代码;)(或者加载了SoftICE / FrogSICE............ )...因此你造成了“一般保护性错误”!!! ...”,谷~歌~翻译的。
因此,这条 int 68 指令要处理,但WinMain()中又会读取这条指令数据来计算WinMain2()的入口地址,如何修改,在后面再交待,现在先不管。。。
如果直接NOP掉NAG显示代码,就不会有这些代码填充,但在调试器中会引起类似如下的错误:
003.png
具体代码分析如下:
[Asm] 纯文本查看 复制代码
; Timer Proc 定时器回调函数,完成主对话框DlgProc中的汇编的填充,不然主对话框DlgProc会报非法内存访问的错误
; 本函数共执行8次,分成两个填充过程,一个填充5字节,一个填充8字节,一起完成代码的填充。
711319A0  />  55            push    ebp                                         ;  Timer回调函数
711319A1  |.  8BEC          mov     ebp, esp
711319A3  |.  8B45 14       mov     eax, dword ptr [ebp+14]                     ;  Timer回调TimerProc时的系统时间
711319A6  |.  A3 B0961371   mov     dword ptr [711396B0], eax                   ;  保存时间,用于检测单步调试
; 开始代码填充
711319AB  |.  8B0D 9C8A1371 mov     ecx, dword ptr [71138A9C]                   ;  取到-1才正确,否则就是检测到了softice或单步调试
711319B1  |.  83C1 01       add     ecx, 1                                      ;  ecx = 0
711319B4  |.  6BC9 03       imul    ecx, ecx, 3                                 ;  ecx = 0
711319B7  |.  81C9 F4121371 or      ecx, 711312F4                               ;  711312F4是需要填充汇编指令2的位置(5个字节)
711319BD  |.  030D C4961371 add     ecx, dword ptr [711396C4]                   ;  加上偏移量,也是个计数器,每进一次时间事件,加1
711319C3  |.  890D BC961371 mov     dword ptr [711396BC], ecx                   ;  保存地址
; 下面填充8字节,分成8次填充完成,每次填充1字节
711319C9  |.  8B15 9C8A1371 mov     edx, dword ptr [71138A9C]                   ;  取到-1才正确,否则就是检测到了softice或单步调试
711319CF  |.  83C2 01       add     edx, 1                                      ;  edx = 0
711319D2  |.  6BD2 03       imul    edx, edx, 3                                 ;  edx = 0
711319D5  |.  81CA F9121371 or      edx, 711312F9                               ;  711312F9是需要填充汇编指令1的位置(8个字节)
711319DB  |.  0315 C0961371 add     edx, dword ptr [711396C0]                   ;  加上偏移量,也是个计数器,每进一次时间事件,加1
711319E1  |.  8915 B4961371 mov     dword ptr [711396B4], edx                   ;  保存地址
711319E7  |.  A1 B8961371   mov     eax, dword ptr [711396B8]                   ;  写指令,每次写一字节
711319EC  |.  50            push    eax                                         ; /pBytesWritten => NULL
711319ED  |.  6A 01         push    1                                           ; |BytesToWrite = 1
711319EF  |.  8B0D C0961371 mov     ecx, dword ptr [711396C0]                   ; |偏移量
711319F5  |.  81C1 24961371 add     ecx, 71139624                               ; |数据来源,在WinMain中生成
711319FB  |.  51            push    ecx                                         ; |Buffer
711319FC  |.  8B15 B4961371 mov     edx, dword ptr [711396B4]                   ; |
71131A02  |.  52            push    edx                                         ; |Address => 0
71131A03  |.  FF15 40A21371 call    dword ptr [<&KERNEL32.GetCurrentProcess>]   ; |[GetCurrentProcess
71131A09  |.  50            push    eax                                         ; |hProcess
71131A0A  |.  FF15 4CA21371 call    dword ptr [<&KERNEL32.WriteProcessMemory>]  ; \WriteProcessMemory
; 下面填充5字节,分成5次填充完成,每次填充1字节
71131A10  |.  833D C4961371>cmp     dword ptr [711396C4], 5                     ;  下面只写5字节指令,超过5则跳过
71131A17  |.  7D 29         jge     short 71131A42
71131A19  |.  A1 B8961371   mov     eax, dword ptr [711396B8]                   ;  写指令,每次写一字节
71131A1E  |.  50            push    eax                                         ; /pBytesWritten => NULL
71131A1F  |.  6A 01         push    1                                           ; |BytesToWrite = 1
71131A21  |.  8B0D C4961371 mov     ecx, dword ptr [711396C4]                   ; |
71131A27  |.  81C1 10941371 add     ecx, 71139410                               ; |
71131A2D  |.  51            push    ecx                                         ; |Buffer
71131A2E  |.  8B15 BC961371 mov     edx, dword ptr [711396BC]                   ; |取得前面生成的地址(在 0x711319C3处保存的地址)
71131A34  |.  52            push    edx                                         ; |Address => 0
71131A35  |.  FF15 40A21371 call    dword ptr [<&KERNEL32.GetCurrentProcess>]   ; |[GetCurrentProcess
71131A3B  |.  50            push    eax                                         ; |hProcess
71131A3C  |.  FF15 4CA21371 call    dword ptr [<&KERNEL32.WriteProcessMemory>]  ; \WriteProcessMemory
; 下面是写入次数的处理,达到8次数后就发送关闭NAG的消息
71131A42  |>  A1 C0961371   mov     eax, dword ptr [711396C0]
71131A47  |.  83C0 01       add     eax, 1                                      ;  计数器1 + 1
71131A4A  |.  A3 C0961371   mov     dword ptr [711396C0], eax
71131A4F  |.  8B0D C4961371 mov     ecx, dword ptr [711396C4]
71131A55  |.  83C1 01       add     ecx, 1                                      ;  计数器2 + 1
71131A58  |.  890D C4961371 mov     dword ptr [711396C4], ecx
71131A5E  |.  833D C0961371>cmp     dword ptr [711396C0], 7                     ;  判断写了多少节字,少于8字节则继续写,也就是继续进入时间事件
71131A65  |.  7E 13         jle     short 71131A7A                              ;  大于7则关闭NAG,停止填充汇编代码
71131A67  |.  6A 00         push    0                                           ; /lParam = 0
71131A69  |.  6A 00         push    0                                           ; |wParam = 0
71131A6B  |.  6A 10         push    10                                          ; |Message = WM_CLOSE
71131A6D  |.  8B15 04941371 mov     edx, dword ptr [71139404]                   ; |
71131A73  |.  52            push    edx                                         ; |hWnd => NULL
71131A74  |.  FF15 54A31371 call    dword ptr [<&USER32.SendMessageA>]          ; \SendMessageA
; 反调试代码,防止单步调试
71131A7A  |>  FF15 3CA21371 call    dword ptr [<&KERNEL32.GetTickCount>]        ; [取当前时间
71131A80  |.  2B05 B0961371 sub     eax, dword ptr [711396B0]                   ;  取得时间差
71131A86  |.  83F8 07       cmp     eax, 7                                      ;  大于7ms则表示在调试,Post一个退出消息,关闭NAG,会导致代码填充不完整等问题
71131A89  |.  76 12         jbe     short 71131A9D
71131A8B  |.  6A 00         push    0                                           ; /lParam = 0
71131A8D  |.  6A 00         push    0                                           ; |wParam = 0
71131A8F  |.  6A 10         push    10                                          ; |Message = WM_CLOSE
71131A91  |.  A1 04941371   mov     eax, dword ptr [71139404]                   ; |
71131A96  |.  50            push    eax                                         ; |hWnd => NULL
71131A97  |.  FF15 58A31371 call    dword ptr [<&USER32.PostMessageA>]          ; \PostMessageA
71131A9D  |>  5D            pop     ebp
71131A9E  \.  C2 1000       retn    10
71131AA1      CC            int3
71131AA2      CC            int3
71131AA3      CC            int3



四、WinMain2()过程
这个子过程相对简单,功能如下:
  1)显示主界面对话框;
  2)根据主对框的返回值,决定是返回WinMain(),还是报错并退出
具体代码分析如下:
[Asm] 纯文本查看 复制代码
; WinMain2(),由WinMain()调用,用来显示主界面 DialogBox,进行下一步的 SN 录入及验证。
7113157B  />  55            push    ebp                                         ;  _WinMain2(),显示主对话框
7113157C  |.  8BEC          mov     ebp, esp
7113157E  |.  6A 00         push    0                                           ; /lParam = NULL
71131580  |.  68 1E101371   push    7113101E                                    ; |DlgProc = ELRAIZER.7113101E
71131585  |.  6A 00         push    0                                           ; |hOwner = NULL
71131587  |.  33C0          xor     eax, eax                                    ; |
71131589  |.  66:A1 0894137>mov     ax, word ptr [71139408]                     ; |[71139408]=1000(这是主对话框资源ID,是在WinMain()中计算出来的,见0x711316D6处指令)
7113158F  |.  50            push    eax                                         ; |pTemplate
71131590  |.  8B0D 18941371 mov     ecx, dword ptr [71139418]                   ; |
71131596  |.  51            push    ecx                                         ; |hInst => NULL
71131597  |.  FF15 50A31371 call    dword ptr [<&USER32.DialogBoxParamA>]       ; \DialogBoxParamA, 显示主对话框
7113159D  |.  83F8 FF       cmp     eax, -1
711315A0  |.  75 0D         jnz     short 711315AF
711315A2  |.  68 EC8A1371   push    71138AEC                                    ; /MessageText = "Error during creation of window ! Try to reload program ...."
711315A7  |.  6A 00         push    0                                           ; |Action = 0
711315A9  |.  FF15 30A21371 call    dword ptr [<&KERNEL32.FatalAppExitA>]       ; \FatalAppExitA
711315AF  |>  5D            pop     ebp
711315B0  \.  C3            retn



五、主对话框 DlgProc 过程
这是该 CrackMe 的主要部分,这部分会Hook键盘输入进行SN的录入,还会通过SN计算 一个WndProc地址,用于注册窗口类,具体功能如下:
  1)在WM_INITDIALOG消息处理过程中,创建一个了线程的键盘Hook,记录所有的键盘操作,包括功能键的操作;
  2)在按钮“Verify ?”的Click()事件中,对记录下的键码进行计算,生成一个地址,用于后面窗口类的注册,同时,发WM_CLOSE关闭主界面
  3)在关闭对话框时,会取消键盘Hook,并调用注册窗口类的一个过程。
  4)前面説的动态生成代码,就是写入到这个过程的代码中,见下面代码分析,从 0x711312F4处开始的代码,共13字节代码是后期生成的。这些代码是用于反调试的,其中 int 68指令在目前的NT系列内核的系统中是不能运行的,会导致异常并退出。
  这里会对动态生成的代码如何破解进行説明,并在下面代码分析中交待。
  主对话框界面如下:
004.png
具体代码分析如下:
[Asm] 纯文本查看 复制代码
; 主对话框 DlgProc,进行 SN 验证计算,其结果作为 WndProc 地址,进行窗口注册。
71131150  />  55            push    ebp                                         ;  主对话框的 DlgProc
71131151  |.  8BEC          mov     ebp, esp
71131153  |.  83EC 18       sub     esp, 18
71131156  |.  53            push    ebx
71131157  |.  56            push    esi
71131158  |.  57            push    edi
71131159  |.  C745 F8 00000>mov     dword ptr [ebp-8], 0
71131160  |.  C645 FC 43    mov     byte ptr [ebp-4], 43
71131164  |.  8B45 0C       mov     eax, dword ptr [ebp+C]                      ;  message id
71131167  |.  8945 EC       mov     dword ptr [ebp-14], eax
7113116A  |.  837D EC 10    cmp     dword ptr [ebp-14], 10                      ;  WM_CLOSE
7113116E  |.  0F84 35010000 je      711312A9
71131174  |.  817D EC 10010>cmp     dword ptr [ebp-14], 110                     ;  WM_INITDIALOG
7113117B  |.  0F84 5B010000 je      711312DC
71131181  |.  817D EC 11010>cmp     dword ptr [ebp-14], 111                     ;  WM_COMMAND
71131188  |.  74 05         je      short 7113118F
7113118A  |.  E9 B5010000   jmp     71131344
7113118F  |>  8B4D 10       mov     ecx, dword ptr [ebp+10]
71131192  |.  894D E8       mov     dword ptr [ebp-18], ecx
71131195  |.  837D E8 01    cmp     dword ptr [ebp-18], 1                       ;  按钮ID,处理按钮的 Click 事件
71131199  |.  74 05         je      short 711311A0
7113119B  |.  E9 04010000   jmp     711312A4
; 以下是按钮"Verify"的Click事件处理过程,对 SN 进行处理,生成WndProc地址值
711311A0  |>  833D A8931371>cmp     dword ptr [711393A8], 0                     ;  标志,不等于0才处理该事件,在窗口的HookProc修改该值(hookProc执行次数,也就是 SN 的长度不为0才处理)
711311A7  |.  75 05         jnz     short 711311AE
711311A9  |.  E9 F6000000   jmp     711312A4
711311AE  |>  0FBE15 359413>movsx   edx, byte ptr [71139435]                    ;  SN索引值基值, [0x71139435] == 0x0
711311B5  |.  8955 F8       mov     dword ptr [ebp-8], edx
711311B8  |.  8B45 F8       mov     eax, dword ptr [ebp-8]
711311BB  |.  83C0 01       add     eax, 1                                      ;  i=eax=0+1
711311BE  |.  2B05 9C8A1371 sub     eax, dword ptr [71138A9C]                   ;  eax=i-(-1)=2, 0x71138A9C==>调用入口地址偏移量 0xFFFFFFFF
711311C4  |.  0FBE88 948913>movsx   ecx, byte ptr [eax+71138994]                ;  ecx=sn[2],  eax=2时,生成指向序列号的地址
711311CB  |.  83E1 0F       and     ecx, 0F                                     ;  atoi(),仅针对数字
711311CE  |.  C1E1 1C       shl     ecx, 1C                                     ;  ecx=3 0000000(调试时输入的假码,不是正确值)
711311D1  |.  8B55 F8       mov     edx, dword ptr [ebp-8]                      ;  edx=0
711311D4  |.  83C2 02       add     edx, 2                                      ;  i=edx=2
711311D7  |.  2B15 9C8A1371 sub     edx, dword ptr [71138A9C]                   ;  edx = i-(-1)=3
711311DD  |.  0FBE82 948913>movsx   eax, byte ptr [edx+71138994]                ;  sn[3]
711311E4  |.  83E0 0F       and     eax, 0F
711311E7  |.  C1E0 18       shl     eax, 18
711311EA  |.  0BC8          or      ecx, eax                                    ;  ecx=34 000000(调试时输入的假码,不是正确值)
711311EC  |.  8B55 F8       mov     edx, dword ptr [ebp-8]                      ;  edx=0
711311EF  |.  83C2 08       add     edx, 8                                      ;  i=edx=8
711311F2  |.  2B15 9C8A1371 sub     edx, dword ptr [71138A9C]                   ;  edx=i-(-1)-9
711311F8  |.  0FBE82 948913>movsx   eax, byte ptr [edx+71138994]                ;  eax=sn[9]
711311FF  |.  83E0 0F       and     eax, 0F
71131202  |.  C1E0 14       shl     eax, 14
71131205  |.  0BC8          or      ecx, eax                                    ;  ecx=340 00000(调试时输入的假码,不是正确值)
71131207  |.  8B55 F8       mov     edx, dword ptr [ebp-8]                      ;  edx=0
7113120A  |.  83C2 05       add     edx, 5                                      ;  i=edx=5
7113120D  |.  2B15 9C8A1371 sub     edx, dword ptr [71138A9C]                   ;  i-(-1)
71131213  |.  0FBE82 948913>movsx   eax, byte ptr [edx+71138994]                ;  eax=sn[6]
7113121A  |.  83E0 0F       and     eax, 0F
7113121D  |.  C1E0 10       shl     eax, 10
71131220  |.  0BC8          or      ecx, eax
71131222  |.  8B55 F8       mov     edx, dword ptr [ebp-8]                      ;  ecx=3407 0000(调试时输入的假码,不是正确值)
71131225  |.  83C2 07       add     edx, 7                                      ;  i=edx=7
71131228  |.  2B15 9C8A1371 sub     edx, dword ptr [71138A9C]                   ;  i-(-1)
7113122E  |.  0FBE82 948913>movsx   eax, byte ptr [edx+71138994]                ;  eax=sn[8]
71131235  |.  83E0 0F       and     eax, 0F
71131238  |.  C1E0 0C       shl     eax, 0C
7113123B  |.  0BC8          or      ecx, eax                                    ;  ecx=34079 000(调试时输入的假码,不是正确值)
7113123D  |.  8B55 F8       mov     edx, dword ptr [ebp-8]                      ;  edx=0
71131240  |.  83C2 09       add     edx, 9                                      ;  i=edx=9
71131243  |.  2B15 9C8A1371 sub     edx, dword ptr [71138A9C]                   ;  i-(-1)
71131249  |.  0FBE82 948913>movsx   eax, byte ptr [edx+71138994]                ;  eax=sn[10]
71131250  |.  83E0 0F       and     eax, 0F
71131253  |.  C1E0 08       shl     eax, 8
71131256  |.  0BC8          or      ecx, eax                                    ;  ecx=340791 00(调试时输入的假码,不是正确值)
71131258  |.  8B55 F8       mov     edx, dword ptr [ebp-8]                      ;  edx=0
7113125B  |.  83C2 06       add     edx, 6                                      ;  i=edx=6
7113125E  |.  2B15 9C8A1371 sub     edx, dword ptr [71138A9C]                   ;  i-(-1)
71131264  |.  0FBE82 948913>movsx   eax, byte ptr [edx+71138994]                ;  eax=sn[7]
7113126B  |.  83E0 0F       and     eax, 0F
7113126E  |.  C1E0 04       shl     eax, 4
71131271  |.  0BC8          or      ecx, eax                                    ;  ecx=3407918 0(调试时输入的假码,不是正确值)
71131273  |.  8B55 F8       mov     edx, dword ptr [ebp-8]                      ;  edx=0
71131276  |.  83C2 03       add     edx, 3                                      ;  i=edx=3
71131279  |.  2B15 9C8A1371 sub     edx, dword ptr [71138A9C]                   ;  i-(-1)
7113127F  |.  0FBE82 948913>movsx   eax, byte ptr [edx+71138994]                ;  eax=sn[4]
71131286  |.  83E0 0F       and     eax, 0F
71131289  |.  0BC8          or      ecx, eax                                    ;  ecx=34079185(调试时输入的假码,不是正确值,正确应该为 71131060 或 7113100F)
7113128B  |.  890D 1C941371 mov     dword ptr [7113941C], ecx                   ;  保存序列号计算后的数值,保存后就发关闭CrackMe的消息
; 完在 SN 计算出WndProc地址后,直接发送 WM_CLOSE 消息,去关闭主对话框。
71131291  |.  6A 00         push    0                                           ; /lParam = 0
71131293  |.  6A 00         push    0                                           ; |wParam = 0
71131295  |.  6A 10         push    10                                          ; |Message = WM_CLOSE
71131297  |.  8B0D F8931371 mov     ecx, dword ptr [711393F8]                   ; |
7113129D  |.  51            push    ecx                                         ; |hWnd => NULL
7113129E  |.  FF15 54A31371 call    dword ptr [<&USER32.SendMessageA>]          ; \发送关闭CrackMe主界面的消息,退出CrackMe
711312A4  |>  E9 9F000000   jmp     71131348
; 接收到 WM_CLOSE 消息,取消键盘Hook,并关闭对话框
711312A9  |>  8B15 0C941371 mov     edx, dword ptr [7113940C]                   ; hook handler
711312AF  |.  52            push    edx                                         ; /hHook => NULL
711312B0  |.  FF15 64A31371 call    dword ptr [<&USER32.UnhookWindowsHookEx>]   ; \UnhookWindowsHookEx
711312B6  |.  6A 01         push    1                                           ; /Result = 1
711312B8  |.  A1 F8931371   mov     eax, dword ptr [711393F8]                   ; |
711312BD  |.  50            push    eax                                         ; |hWnd => NULL
711312BE  |.  FF15 68A31371 call    dword ptr [<&USER32.EndDialog>]             ; \EndDialog
; 调用另一个函数,注册一个窗口类,如果上面计算的地址有误,则会产生系统错误,程序也会退出。
711312C4  |.  E8 5AFDFFFF   call    71131023                                    ; 调用函数注册一个窗口类,该类的WndProc地址为前面通过SN计算得到的地址
711312C9  |.  25 FF000000   and     eax, 0FF
711312CE  |.  85C0          test    eax, eax
711312D0  |.  75 08         jnz     short 711312DA
711312D2  |.  6A 01         push    1                                           ; /ExitCode = 1
711312D4  |.  FF15 38A21371 call    dword ptr [<&KERNEL32.ExitProcess>]         ; \ExitProcess
711312DA  |>  EB 6C         jmp     short 71131348
; 下面中 WM_INITDIALOG 消息处理过程,其中有部分代码是在 NAG 的 Timer 事件中会填充为正确的代码
711312DC  |>  8B4D 08       mov     ecx, dword ptr [ebp+8]                      ;  处理 WM_INITDIALOG 消息
711312DF  |.  890D F8931371 mov     dword ptr [711393F8], ecx                   ;  Dialog handler?
711312E5  |.  FF15 3CA21371 call    dword ptr [<&KERNEL32.GetTickCount>]        ; [GetTickCount
711312EB  |.  8945 F4       mov     dword ptr [ebp-C], eax                      ;  取当前时间
711312EE  |.  FF15 A8A21371 call    dword ptr [<&KERNEL32.GetCurrentThreadId>]  ; [GetCurrentThreadId
; 以下是CrackMe填充前的代码
;711312F4  |.  90            nop                                                ;  nop,TimerProc中填充,破解后预填充
;711312F5  |.  90            nop                                                ;  nop,TimerProc中填充,破解后预填充
;711312F6  |.  90            nop                                                ;  nop,TimerProc中填充,破解后预填充
;711312F7  |.  90            nop                                                ;  nop,TimerProc中填充,破解后预填充
;711312F8  |.  90            nop                                                ;  nop,TimerProc中填充,破解后预填充
;711312F9  |.  B8 39425573   mov     eax, 73554239                              ;  这里也会在TimerProc中填充,改变指令,破解后预填充
;711312FE  |.  C600 C4       mov     byte ptr [eax], 0C4                        ;  这里也会在TimerProc中填充,改变指令,破解后预填充
;以下是CrackMe程序填充后的代码
;711312F4  |.  A3 9C931371   mov     dword ptr [7113939C], eax                  ;  修改指令起点,保存线程ID
;711312F9  |.  90            nop
;711312FA  |?  33C0          xor     eax, eax
;711312FC  |?  B4 43         mov     ah, 43                                     ; 这里在WinMain1()中有检测,用于生成调用WinMain2()的地址
;711312FE  |.  CD 68         int     68                                         ; 这里在WinMain1()中有检测,用于生成调用WinMain2()的地址
;71131300  |?  90            nop
;以下修改是为了防止在 Windows NT系列内核的系统上报异常,破解NAG后手工填充的代码,就是跳过 int 68 指令的执行
711312F4  |.  A3 9C931371   mov     dword ptr [7113939C], eax                   ;  保存ThreadID
711312F9  |.  90            nop
711312FA      EB 04         jmp     short 71131300                              ;  将 xor eax, eax 修改成 jmp 71131300
711312FC  |.  B4 43         mov     ah, 43
711312FE  |.  CD 68         int     68
71131300  |.  90            nop
; 保存检查值,如果在Windows 9x下,没有检查到SoftICE,则ax=0x4300,破解NAG后,ax中前面的线程ID。
71131301  |.  66:A3 948A137>mov     word ptr [71138A94], ax                     ; 保存检查值,破解后保存的是前面取得的线程ID,只要线程ID不等于0xF386就不影响CrackMe后面的运行
; 以下是反调试代码,如果单步执行,就会有问题,会导致全局变量[71138A9C]的值会改变,不再等于 -1(0xFFFFFFFF),会影响其它代码运算出错,如 SN 的计算。
71131307  |.  FF15 3CA21371 call    dword ptr [<&KERNEL32.GetTickCount>]        ; [GetTickCount
7113130D  |.  8945 F0       mov     dword ptr [ebp-10], eax                     ;  再次取得当前时间
71131310  |.  8B55 F0       mov     edx, dword ptr [ebp-10]
71131313  |.  2B55 F4       sub     edx, dword ptr [ebp-C]                      ;  计算时间差,用于反调试,修改 0xFFFFFFFF 为其它值,影响其它地方的指令执行
71131316  |.  A1 9C8A1371   mov     eax, dword ptr [71138A9C]                   ;  eax=0xFFFFFFFF,前面检查Softice的标志值
7113131B  |.  2BC2          sub     eax, edx                                    ;  修改 0xFFFFFFFF 为其它值,影响其它地方的指令执行
7113131D  |.  A3 9C8A1371   mov     dword ptr [71138A9C], eax                   ;  eax = 0xFFFFFFFF 才是合法数据
;下面设置一个键盘 Hook,用来记录当前线程所有的键盘击键操作
71131322  |.  8B0D 9C931371 mov     ecx, dword ptr [7113939C]
71131328  |.  51            push    ecx                                         ; /ThreadID => 0
71131329  |.  8B15 18941371 mov     edx, dword ptr [71139418]                   ; | hInstance
7113132F  |.  52            push    edx                                         ; |hModule => NULL
71131330  |.  68 14101371   push    71131014                                    ; |Hookproc = ELRAIZER.71131014
71131335  |.  6A 02         push    2                                           ; |HookType = WH_KEYBOARD
71131337  |.  FF15 60A31371 call    dword ptr [<&USER32.SetWindowsHookExA>]     ; \SetWindowsHookExA
7113133D  |.  A3 0C941371   mov     dword ptr [7113940C], eax                   ; 返回结果保存在[7113940C],用于UnhookWindowsHookEx的参数
71131342  |.  EB 04         jmp     short 71131348
71131344  |>  33C0          xor     eax, eax
71131346  |.  EB 05         jmp     short 7113134D
71131348  |>  B8 01000000   mov     eax, 1
7113134D  |>  5F            pop     edi
7113134E  |.  5E            pop     esi
7113134F  |.  5B            pop     ebx
71131350  |.  8BE5          mov     esp, ebp
71131352  |.  5D            pop     ebp
71131353  \.  C2 1000       retn    10
71131356      CC            int3
71131357      CC            int3

序列号的分析:
  椐据上面代码中对输入的 SN 的处理算法,就是截取SN中8个字符的ASCII值的后4bit作为一位16进位的数字,然后将8位数字组合成一个32bit的整数,组合顺序如下:
最终计算后的SN数字结果 = SN[2] SN[3] SN[9] SN[6] SN[8] SN[10] SN[7] SN[4]
由此可以看出,输入的SN至少要11个字符,其中 SN[0],SN[1],SN[5]三个位置的字符没有用到,因此可以为任意字符,11个字符的后面还可任意添加字符,不影响计算。


六、键盘 Hook 过程
该CrackMe的序列号的录入,是通过键盘Hook完成的,并不是界面上取的字符串,界面上只是纯显示,并且,Hook过程处理所有击键的键码,包括功能键,比如删除、回退、F8等,都会记录并保存,所以内存缓冲区中保存的SN与对话框界面显示的SN可能并不一致。另外,Hook进入256次,不再记录键盘事件的键码了。
1)Hook记录键码,并通过一个变量,过滤掉 KeyUp事件,只记录 KeyDown事件中的键码;
2)对主对话框 DlgProc 对SoftICE 的检查结果进行核查,如果 SoftICE 在运行,则退出CrackMe。
具体分析如下:
[Asm] 纯文本查看 复制代码
; 键盘 Hook 的回调函数,记录所有的键盘输入,包括功能键,在OD中调试时的按键也会影响
711313E0  />  55            push    ebp                                         ;  HookProc
711313E1  |.  8BEC          mov     ebp, esp
711313E3  |.  83EC 08       sub     esp, 8
711313E6  |.  8B45 08       mov     eax, dword ptr [ebp+8]                      ;  message
711313E9  |.  8945 F8       mov     dword ptr [ebp-8], eax
711313EC  |.  837D F8 00    cmp     dword ptr [ebp-8], 0                        ;  HC_ACTION = 0
711313F0  |.  74 08         je      short 711313FA                              ;  nCode==HC_ACTION(即等到于0)时,在hook中进行处理
711313F2  |.  837D F8 03    cmp     dword ptr [ebp-8], 3                        ;  HC_NOREMOVE = 3
711313F6  |.  74 78         je      short 71131470
711313F8  |.  EB 78         jmp     short 71131472
;下面是按钮事件处理过程,通过一个bool变量,针对KeyDown和KeyUp事件,只记录一次键码。
711313FA  |>  33C9          xor     ecx, ecx
711313FC  |.  8A0D A0931371 mov     cl, byte ptr [711393A0]                     ;  bool标志, 0 和 1,主要用来做 KeyDown 和 KeyUp 事件处理,只记录一次事件
71131402  |.  85C9          test    ecx, ecx
71131404  |.  75 3F         jnz     short 71131445                              ;  bool标志不等于0则跳过,不记录键值
71131406  |.  813D A8931371>cmp     dword ptr [711393A8], 100                   ;  Hook进入次数
71131410  |.  73 2A         jnb     short 7113143C                              ;  不低于是0x100就跳转
71131412  |.  8B15 A8931371 mov     edx, dword ptr [711393A8]                   ;  次数
71131418  |.  8A45 0C       mov     al, byte ptr [ebp+C]                        ;  al = wParam
7113141B  |.  8882 94891371 mov     byte ptr [edx+71138994], al                 ;  记录键值存入序列号缓冲区(0x0~0x49, 0x100/2)
71131421  |.  8B0D A8931371 mov     ecx, dword ptr [711393A8]
71131427  |.  83C1 01       add     ecx, 1                                      ;  进入次数+1
7113142A  |.  890D A8931371 mov     dword ptr [711393A8], ecx
71131430  |.  33D2          xor     edx, edx
71131432  |.  66:8B15 948A1>mov     dx, word ptr [71138A94]                     ;  取得 int 68 检查 SoftICE 调试的检查标志值
71131439  |.  8955 FC       mov     dword ptr [ebp-4], edx                      ;  保存这个值到局部变量,不过好象没有地方用到这个局部变量
7113143C  |>  C605 A0931371>mov     byte ptr [711393A0], 1                      ;  bool标志 = 1
71131443  |.  EB 29         jmp     short 7113146E
71131445  |>  C605 A0931371>mov     byte ptr [711393A0], 0                      ;  bool标志 = 0
; 下面是反调试代码,不过对 OD 无效
7113144C  |.  33C0          xor     eax, eax
7113144E  |.  66:A1 948A137>mov     ax, word ptr [71138A94]                     ;  该值在WinMain初始化为0x0F386,在MainDialog的初始化事件中有修改。上面有读取,未修改。反SoftICE调试用的
71131454  |.  3D 86F30000   cmp     eax, 0F386                                  ;  反SoftIce调试,eax不能等于0x0F386
71131459  |.  75 13         jnz     short 7113146E                              ;  如果检测到SoftICE调试,则Post关闭消息退出CrackMe
7113145B  |.  6A 00         push    0                                           ; /lParam = 0
7113145D  |.  6A 00         push    0                                           ; |wParam = 0
7113145F  |.  6A 10         push    10                                          ; |Message = WM_CLOSE
71131461  |.  8B0D F8931371 mov     ecx, dword ptr [711393F8]                   ; |
71131467  |.  51            push    ecx                                         ; |hWnd => NULL
71131468  |.  FF15 58A31371 call    dword ptr [<&USER32.PostMessageA>]          ; \Post关闭消息退出CrackMe
7113146E  |>  EB 1D         jmp     short 7113148D
71131470  |>  EB 1B         jmp     short 7113148D
; 下面是Hook传递,不然,界面文本框无法得到键码并显示(因此,本CrackMe的录入和显示是分开的,没有相关性,由于有功能键操作,可能录入的与显示的不一样)
71131472  |>  8B55 10       mov     edx, dword ptr [ebp+10]
71131475  |.  52            push    edx                                         ; /lParam
71131476  |.  8B45 0C       mov     eax, dword ptr [ebp+C]                      ; |
71131479  |.  50            push    eax                                         ; |wParam
7113147A  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]                      ; |
7113147D  |.  51            push    ecx                                         ; |HookCode
7113147E  |.  8B15 0C941371 mov     edx, dword ptr [7113940C]                   ; |
71131484  |.  52            push    edx                                         ; |hHook => NULL
71131485  |.  FF15 5CA31371 call    dword ptr [<&USER32.CallNextHookEx>]        ; \CallNextHookEx
7113148B  |.  EB 18         jmp     short 711314A5
7113148D  |>  8B45 10       mov     eax, dword ptr [ebp+10]
71131490  |.  50            push    eax                                         ; /lParam
71131491  |.  8B4D 0C       mov     ecx, dword ptr [ebp+C]                      ; |
71131494  |.  51            push    ecx                                         ; |wParam
71131495  |.  8B55 08       mov     edx, dword ptr [ebp+8]                      ; |
71131498  |.  52            push    edx                                         ; |HookCode
71131499  |.  A1 0C941371   mov     eax, dword ptr [7113940C]                   ; |
7113149E  |.  50            push    eax                                         ; |hHook => NULL
7113149F  |.  FF15 5CA31371 call    dword ptr [<&USER32.CallNextHookEx>]        ; \CallNextHookEx
711314A5  |>  8BE5          mov     esp, ebp
711314A7  |.  5D            pop     ebp
711314A8  \.  C2 0C00       retn    0C
711314AB      CC            int3



七、窗口注册 Proc
这个函数注册了一个窗口类,并且该类的 WndProc就是主对话框按钮Click()事件中计算出来的地址,如果输入的SN不对,则此地址无效,也会引起程序直接被系统关闭。
功能如下:
1)调用 RegisterClass注册一个标准 Windows 窗口类;
具体代码如下:
[Asm] 纯文本查看 复制代码
; 本函数用来注册一个Windows的窗口类,这个窗口类有WndProc地址是通过录入的SN计算得来的
711314E0  />  55            push    ebp                                         ;  注册窗口类
711314E1  |.  8BEC          mov     ebp, esp
711314E3  |.  51            push    ecx
711314E4  |.  C745 FC 6243F>mov     dword ptr [ebp-4], BFF54362
711314EB  |.  A1 1C941371   mov     eax, dword ptr [7113941C]                   ;  eax=计算后的序列号
711314F0  |.  A3 F4931371   mov     dword ptr [711393F4], eax                   ;  计算后的序列号
711314F5  |.  C705 B0931371>mov     dword ptr [711393B0], 3
711314FF  |.  8B0D F4931371 mov     ecx, dword ptr [711393F4]                   ;  ecx=eax=计算后的序列号
71131505  |.  890D B4931371 mov     dword ptr [711393B4], ecx                   ;  WndClass.lpfnWndProc = eax,窗口的消息处理函数入口
7113150B  |.  C705 B8931371>mov     dword ptr [711393B8], 0
71131515  |.  C705 BC931371>mov     dword ptr [711393BC], 0
7113151F  |.  8B15 18941371 mov     edx, dword ptr [71139418]
71131525  |.  8915 C0931371 mov     dword ptr [711393C0], edx
7113152B  |.  C705 C4931371>mov     dword ptr [711393C4], 0
71131535  |.  C705 C8931371>mov     dword ptr [711393C8], 0
7113153F  |.  C705 CC931371>mov     dword ptr [711393CC], 6
71131549  |.  C705 D0931371>mov     dword ptr [711393D0], 0
71131553  |.  C705 D4931371>mov     dword ptr [711393D4], 71138AE8              ;  ASCII "_#_"
7113155D  |.  68 B0931371   push    711393B0                                    ; /pWndClass = ELRAIZER.711393B0
71131562  |.  FF15 48A31371 call    dword ptr [<&USER32.RegisterClassA>]        ; \RegisterClassA, 注册一个窗口类
71131568  |.  25 FFFF0000   and     eax, 0FFFF
7113156D  |.  85C0          test    eax, eax
7113156F  |.  75 04         jnz     short 71131575
71131571  |.  32C0          xor     al, al
71131573  |.  EB 02         jmp     short 71131577
71131575  |>  B0 01         mov     al, 1
71131577  |>  8BE5          mov     esp, ebp
71131579  |.  5D            pop     ebp
7113157A  \.  C3            retn



八、Window Callback Proc
这个就是一个窗口类的 WndProc,就是上面过程中注册窗口类要用到的这个Proc。因为CrackMe并没有指明这个函数就是 WndProc,指向这个函数的地址也是计算出来的,为什么确定其就是一个 WndProc呢,很简单:其调用了系统的一个函数 DefWindowProc(),而这个函数是标准的 WndProc 才用得到,同时也只有这个过程调用这个函数,因此确定此函数就是 WndProc,并且其入口地址为:0x71131060,也可以是 0x7113100F(见最前面的入口列表),要根据这个地址及主对话框 DlgProc中的SN处理算法,就可以确定序列号了。
有效的序列号如下,当然不限于这些,只是这几个简单:
  1)xx710x36110
  2)xx71/x30110
  3)xx71?x30110
  4)xx71Ox30110
  5)xx71_x30110
  6)xx71ox30110

每个SN中有3个x,x可以为任意字符,11个字符后也可以输入任意字符。只是注意,录入时不要按其它键,只能一次性按顺序输入所有字符。如下,是一个有效的输入:
005.png
这个WndProc的代码如下:
[Asm] 纯文本查看 复制代码
; WndProc 过程,窗口消息处理回调函数
71131060  />  55            push    ebp                                         ;  窗口消息处理WinProc
71131061  |.  8BEC          mov     ebp, esp
71131063  |.  83EC 08       sub     esp, 8
71131066  |.  8B45 0C       mov     eax, dword ptr [ebp+C]
71131069  |.  8945 F8       mov     dword ptr [ebp-8], eax
7113106C  |.  837D F8 10    cmp     dword ptr [ebp-8], 10                       ;  WM_CLOSE
71131070  |.  77 14         ja      short 71131086                              ;  大于 0x10
71131072  |.  837D F8 10    cmp     dword ptr [ebp-8], 10                       ;  WM_CLOSE
71131076  |.  74 19         je      short 71131091                              ;  等于 WM_CLOSE
71131078  |.  837D F8 01    cmp     dword ptr [ebp-8], 1
7113107C  |.  74 1F         je      short 7113109D
7113107E  |.  837D F8 0F    cmp     dword ptr [ebp-8], 0F                       ;  WM_PAINT,这个消息没什么用,可以算干扰代码
71131082  |.  74 24         je      short 711310A8
71131084  |.  EB 78         jmp     short 711310FE
71131086  |>  817D F8 11010>cmp     dword ptr [ebp-8], 111                      ;  WM_COMMAND
7113108D  |.  74 0C         je      short 7113109B
7113108F  |.  EB 6D         jmp     short 711310FE                              ;  其它消息
71131091  |>  6A 00         push    0                                           ; /ExitCode = 0
71131093  |.  FF15 70A31371 call    dword ptr [<&USER32.PostQuitMessage>]       ; \PostQuitMessage
71131099  |.  EB 7B         jmp     short 71131116
7113109B  |>  EB 79         jmp     short 71131116
7113109D  |>  8B4D 08       mov     ecx, dword ptr [ebp+8]
711310A0  |.  890D A4931371 mov     dword ptr [711393A4], ecx
711310A6  |.  EB 6E         jmp     short 71131116
711310A8  |>  8B15 A4931371 mov     edx, dword ptr [711393A4]                   ;  以下代码不会执行到,没有ShowWindow,也没有WM_PAINT消息产生
711310AE  |.  52            push    edx                                         ; /hWnd => NULL
711310AF  |.  FF15 74A31371 call    dword ptr [<&USER32.GetDC>]                 ; \GetDC
711310B5  |.  8945 FC       mov     dword ptr [ebp-4], eax
711310B8  |.  6A 2B         push    2B                                          ; /StringSize = 2B (43.)
711310BA  |.  68 60891371   push    71138960                                    ; |指向西班牙语:我靠。。。。实际上这是多余的
711310BF  |.  6A 32         push    32                                          ; |YStart = 32 (50.)
711310C1  |.  6A 32         push    32                                          ; |XStart = 32 (50.)
711310C3  |.  8B45 FC       mov     eax, dword ptr [ebp-4]                      ; |
711310C6  |.  50            push    eax                                         ; |hDC
711310C7  |.  FF15 00A21371 call    dword ptr [<&GDI32.TextOutA>]               ; \TextOutA
711310CD  |.  6A 25         push    25                                          ; /StringSize = 25 (37.)
711310CF  |.  68 30891371   push    71138930                                    ; |指向西班牙语:我甚至都没有意识到这一点!!!
711310D4  |.  6A 46         push    46                                          ; |YStart = 46 (70.)
711310D6  |.  6A 32         push    32                                          ; |XStart = 32 (50.)
711310D8  |.  8B4D FC       mov     ecx, dword ptr [ebp-4]                      ; |
711310DB  |.  51            push    ecx                                         ; |hDC
711310DC  |.  FF15 00A21371 call    dword ptr [<&GDI32.TextOutA>]               ; \TextOutA
711310E2  |.  6A 00         push    0                                           ; /pRect = NULL
711310E4  |.  8B55 08       mov     edx, dword ptr [ebp+8]                      ; |
711310E7  |.  52            push    edx                                         ; |hWnd
711310E8  |.  FF15 6CA31371 call    dword ptr [<&USER32.ValidateRect>]          ; \ValidateRect
711310EE  |.  8B45 FC       mov     eax, dword ptr [ebp-4]
711310F1  |.  50            push    eax                                         ; /hDC
711310F2  |.  8B4D 08       mov     ecx, dword ptr [ebp+8]                      ; |
711310F5  |.  51            push    ecx                                         ; |hWnd
711310F6  |.  FF15 38A31371 call    dword ptr [<&USER32.ReleaseDC>]             ; \ReleaseDC
711310FC  |.  EB 18         jmp     short 71131116
711310FE  |>  8B55 14       mov     edx, dword ptr [ebp+14]                     ;  其它消息默认处理
71131101  |.  52            push    edx                                         ; /lParam
71131102  |.  8B45 10       mov     eax, dword ptr [ebp+10]                     ; |
71131105  |.  50            push    eax                                         ; |wParam
71131106  |.  8B4D 0C       mov     ecx, dword ptr [ebp+C]                      ; |
71131109  |.  51            push    ecx                                         ; |Message
7113110A  |.  8B55 08       mov     edx, dword ptr [ebp+8]                      ; |
7113110D  |.  52            push    edx                                         ; |hWnd
7113110E  |.  FF15 34A31371 call    dword ptr [<&USER32.DefWindowProcA>]        ; \DefWindowProcA
71131114  |.  EB 02         jmp     short 71131118
71131116  |>  33C0          xor     eax, eax
71131118  |>  8BE5          mov     esp, ebp
7113111A  |.  5D            pop     ebp
7113111B  \.  C2 1000       retn    10
7113111E      CC            int3
7113111F      CC            int3



九、系统错误提示消息框
这个过程只是显示一个固定的消息框。当系统出错就会调用本过程,代码如下:
[Asm] 纯文本查看 复制代码
;全局SEH异常处理,显示出错信息
71131800  /> \55            push    ebp                                         ;  显示错误信息消息框
71131801  |.  8BEC          mov     ebp, esp
71131803  |.  83EC 08       sub     esp, 8
71131806  |.  8B45 08       mov     eax, dword ptr [ebp+8]
71131809  |.  8B08          mov     ecx, dword ptr [eax]
7113180B  |.  8B11          mov     edx, dword ptr [ecx]
7113180D  |.  8955 FC       mov     dword ptr [ebp-4], edx
71131810  |.  817D FC 05000>cmp     dword ptr [ebp-4], C0000005                 ;  STATUS_ACCESS_VIOLATION: 写入位置时发生访问冲突,程序企图读写一个不可访问的地址时引发的异常,也就是地址指针错误
71131817  |.  74 12         je      short 7113182B
71131819  |.  817D FC 90000>cmp     dword ptr [ebp-4], C0000090                 ;  STATUS_FLOAT_INVALID_OPERATION: 该异常表示一个未知的浮点数异常。
71131820  |.  74 09         je      short 7113182B
71131822  |.  817D FC 8E000>cmp     dword ptr [ebp-4], C000008E                 ;  STATUS_FLOAT_DIVIDE_BY_ZERO: 浮点数除法的除数是0时引发该异常。
71131829  |.  75 21         jnz     short 7113184C
7113182B  |>  6A 00         push    0                                           ; /Style = MB_OK|MB_APPLMODAL
7113182D  |.  68 208D1371   push    71138D20                                    ; |Title = "ATPTeam CrackMe zer0 Error Message"
71131832  |.  68 548B1371   push    71138B54                                    ; |Text = "Si tu es arriv?l?c'est que des octets sont chang閟 , ou que tu as entr?un tres mauvais code ;)",LF,"(Ou encore que SoftICE / FrogSICE sont charg閟 ...........) ",LF,"...Du coup tu as provoqu?une 'General Protection Fault' ",LF,"!!!"...
71131837  |.  A1 F8931371   mov     eax, dword ptr [711393F8]                   ; |
7113183C  |.  50            push    eax                                         ; |hOwner => NULL
7113183D  |.  FF15 3CA31371 call    dword ptr [<&USER32.MessageBoxA>]           ; \MessageBoxA
71131843  |.  C745 F8 01000>mov     dword ptr [ebp-8], 1
7113184A  |.  EB 07         jmp     short 71131853
7113184C  |>  C745 F8 00000>mov     dword ptr [ebp-8], 0
71131853  |>  8B45 F8       mov     eax, dword ptr [ebp-8]
71131856  |.  8BE5          mov     esp, ebp
71131858  |.  5D            pop     ebp
71131859  \.  C2 0400       retn    4

其界面如下图:

如果上面图片没有显示出来(好象论坛中同一张图只能显示一次),请参见“三、定时器回调Proc”中的第一个图片。

十、总结
根据以上代码分析,我们不能简单的把NAG代码NOP掉,因为NAG中有对全局变量的初始化,还对代码进行了生成和回填,不回填的话原代码是错误的,会导致内存访问错误,还会导致 WinMain2()入口计算错误等,同时 int 68 也不能再使用,所以,本CrackMe有以下三个修改,才能完成NAG的破除,直接用16进制工具修改exe文件:
  (1) 修改数据节变量静态初值:全局变量[0x71138A9C]的静态初始值由0x0000001F 修改成 0xFFFFFFFF,如下:
修改文件,由:
[Asm] 纯文本查看 复制代码
00008a90h: 00 00 00 00 FF FF 00 00 D8 8A 13 71 1F 00 00 00 

改为:
[Asm] 纯文本查看 复制代码
00008a90h: 00 00 00 00 FF FF 00 00 D8 8A 13 71 FF FF FF FF

  (2) 修改指令节汇编代码:
由     90 90 90 90 90 B8 39 42 55 73 C6 00 C4:
[Asm] 纯文本查看 复制代码
711312F4  |.  90            nop                                      ;  修改指令起点
711312F5  |.  90            nop
711312F6  |.  90            nop
711312F7  |.  90            nop
711312F8  |.  90            nop
711312F9  |.  B8 39425573   mov     eax, 73554239
711312FE  |.  C600 C4       mov     byte ptr [eax], 0C4

修改成 A3 9C 93 13 71 90 EB 04 B4 43 CD 68 90:
[Asm] 纯文本查看 复制代码
711312F4  |.  A3 9C931371   mov     dword ptr [7113939C], eax               ;  修改指令
711312F9  |.  90            nop
711312FA      EB 04         jmp     short 71131300                          ;  将 xor eax, eax 修改成 jmp 71131300
711312FC  |.  B4 43         mov     ah, 43
711312FE  |.  CD 68         int     68
71131300  |.  90            nop

在文件中是由:
[Asm] 纯文本查看 复制代码
000012f0h: A8 A2 13 71 90 90 90 90 90 B8 39 42 55 73 C6 00 
00001300h: C4

修改成:
[Asm] 纯文本查看 复制代码
000012f0h: A8 A2 13 71 A3 9C 93 13 71 90 EB 04 B4 43 CD 68
00001300h: 90

  (3) 将显示NAG的代码填充成NOP:
[Asm] 纯文本查看 复制代码
71131635   .  6A 00                    push    0                                       ; /lParam = NULL
71131637   .  68 0A101371              push    7113100A                                ; |DlgProc = Elraizer.7113100A
7113163C   .  6A 00                    push    0                                       ; |hOwner = NULL
7113163E   .  68 E9030000              push    3E9                                     ; |pTemplate = 3E9
71131643   .  8B0D 18941371            mov     ecx, dword ptr [71139418]               ; |Elraizer.71130000
71131649   .  51                       push    ecx                                     ; |hInst => 71130000
7113164A   .  FF15 50A31371            call    dword ptr [<&USER32.DialogBoxParamA>]   ; \DialogBoxParamA

以上代码全部填充成NOP指令。
在文件中由:
[Asm] 纯文本查看 复制代码
00001630h: 29 96 13 71 CD 6A 00 68 0A 10 13 71 6A 00 68 E9
00001640h: 03 00 00 8B 0D 18 94 13 71 51 FF 15 50 A3 13 71

改成如下:
[Asm] 纯文本查看 复制代码
00001630h: 29 96 13 71 CD 90 90 90 90 90 90 90 90 90 90 90 
00001640h: 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90

完成以上修改后,可以在Windows 10 中完美运行此 CrackMe 了。

运气好的话或在调试状态下,在 CrackMe关闭时,还可以看到最后 CreateWindow生成的窗口,SN输入正确时,该窗口会在屏幕左上角闪一下,然后消失,SN输入错误时则不会出现:
006.png
分析完毕!!!

免费评分

参与人数 4威望 +2 吾爱币 +12 热心值 +4 收起 理由
Hmily + 2 + 7 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
WYWZ + 1 + 1 谢谢@Thanks!
liphily + 3 + 1 太可怕,超出了目前能看懂的——暂时先不收藏了
Yakult + 1 + 1 用心讨论,共获提升!

查看全部评分

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

kown 发表于 2019-5-11 19:09
奈何本人文化浅,一声卧槽送楼主。一脸懵逼的进来,现在准备一脸懵逼的出去。。
HackerWen 发表于 2019-5-11 21:06
 楼主| solly 发表于 2019-5-12 02:46
娄不夜 发表于 2019-5-12 11:32
怎么判断汇编语言功能的啊,我只知道指令的意思
我还不够叼 发表于 2019-5-12 20:28
感谢分享!
 楼主| solly 发表于 2019-5-12 21:30
娄不夜 发表于 2019-5-12 11:32
怎么判断汇编语言功能的啊,我只知道指令的意思

可以结合一些反汇编工具(如 WDASM,IDA等)来判断,还有就是联系上下文,API调用(DOS下是中断),以及一段代码组合在一起来判断,等等。。。。。。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则 警告:本版块禁止灌水或回复与主题无关内容,违者重罚!

快速回复 收藏帖子 返回列表 搜索

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

GMT+8, 2024-4-19 04:39

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

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