好友
阅读权限35
听众
最后登录1970-1-1
|
本帖最后由 solly 于 2019-5-11 13:53 编辑
160 个 CrackMe 之 55 -- Elraizer.1 是 32 位原生CrackMe程序,首先检查一下文件信息:
没有加壳,节的情况如下:
这个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
这些子过程的流程关系,如下图:
下面分别説明这些子过程:
一、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的界面如下:
具体代码分析如下:
[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 下是不支持的,会导致程序异常,报错并退出,错误如下:
西班牙文,大概意思是:“如果你看到了这条信息,那是因为程序代码被篡改了,或者你录入了一段非常糟糕的代码;)(或者加载了SoftICE / FrogSICE............ )...因此你造成了“一般保护性错误”!!! ...”,谷~歌~翻译的。
因此,这条 int 68 指令要处理,但WinMain()中又会读取这条指令数据来计算WinMain2()的入口地址,如何修改,在后面再交待,现在先不管。。。
如果直接NOP掉NAG显示代码,就不会有这些代码填充,但在调试器中会引起类似如下的错误:
具体代码分析如下:
[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系列内核的系统中是不能运行的,会导致异常并退出。
这里会对动态生成的代码如何破解进行説明,并在下面代码分析中交待。
主对话框界面如下:
具体代码分析如下:
[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个字符后也可以输入任意字符。只是注意,录入时不要按其它键,只能一次性按顺序输入所有字符。如下,是一个有效的输入:
这个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输入错误时则不会出现:
分析完毕!!!
|
免费评分
-
参与人数 4 | 威望 +2 |
吾爱币 +12 |
热心值 +4 |
收起
理由
|
Hmily
| + 2 |
+ 7 |
+ 1 |
感谢发布原创作品,吾爱破解论坛因你更精彩! |
WYWZ
| |
+ 1 |
+ 1 |
谢谢@Thanks! |
liphily
| |
+ 3 |
+ 1 |
太可怕,超出了目前能看懂的——暂时先不收藏了 |
Yakult
| |
+ 1 |
+ 1 |
用心讨论,共获提升! |
查看全部评分
|