160 个 CrackMe 之 155 - The_q.3 的注册分析和BUG修复,适应新系统!
本帖最后由 solly 于 2019-7-22 23:38 编辑160 个 CrackMe 之 155 - The_q.3 的是一个文件验证的 CrackMe,并且这个文件的位置,需要通过注册表中的一个键值来定位,但是 CrackMe 读取注册表的操作有BUG,会导致读取失败,无法验证,本文将修正这个Bug,完成其文件验证。另外,CrackMe 还通过一个 DLL 文件,安装一个桌面鼠标 Hook,通过 HookProc 对 FileMon.exe 和 RegMon.exe 的进程进行检查,是反调试手段,不过现在基本没有用了。
首先看看文件信息:
按“Scan /t",再次深度扫描,如下:
文件节的信息如下:
与我原来分析过的 159 CrackMe - Torn@do.2 的壳一样,只是155 CrackMe 只加了一层壳,而 159 加了 10 层。
脱壳也很简单,一个 F4 就可以了。如下图所示:
在 0x00418096 处,按 F4 就脱壳了,再按 F8 就到了 OEP,如下图所示:
我这里显示有问题,没有显示为指令,如下操作一下就会显示正常了:
由OD重新”分析代码“,就会正常显示汇编代码了:
先看看静态代码,往下走,如下图位置:
有这么一段代码:
00401603|.68 A0344000 push 004034A0 ; /FileName = "flaf.dll"
00401608|.E8 8C020000 call <jmp.&KERNEL32.LoadLibraryA> ; \LoadLibraryA
0040160D|.83F8 00 cmp eax, 0
00401610 74 6A je short 0040167C ;由 je short 0040167C 改成 jmp 00401636,跳过Hook的干扰
00401612|.90 nop
00401613 90 nop
00401614 90 nop
00401615|.90 nop
00401616|.A3 AE344000 mov dword ptr , eax
0040161B|.68 A9344000 push 004034A9 ; /ProcNameOrOrdinal = "FLAF"
00401620|.50 push eax ; |hModule
00401621|.E8 85020000 call <jmp.&KERNEL32.GetProcAddress> ; \GetProcAddress
00401626|.6A 00 push 0 ; /ThreadID == 0, Hook 桌面,开始菜单栏。
00401628|.FF35 AE344000 push dword ptr ; |hModule = 004B0000 (flaf)
0040162E|.50 push eax ; |Hookproc
0040162F|.6A 07 push 7 ; |HookType = WH_MOUSE
00401631|.E8 A5020000 call <jmp.&USER32.SetWindowsHookExA> ; \SetWindowsHookExA
这段代码的作用是加载 FLAF.DLL 动态库,并取得 FLAF() 函数,当作回调函数安装为一个鼠标 Hook。前面説了,是检查 FileMon.exe 和 RegMon.exe 的,检查到了就结束 CrackMe。
接下来就是一段 Windows 消息循环处理代码,如下图:
我们回到 OEP ,来找 WndProc,如图所示:
找到 WndProc,代码如下所示:
00401555 .6A 00 push 0 ; /pModule = NULL
00401557 .E8 49030000 call <jmp.&KERNEL32.GetModuleHandleA>; \GetModuleHandleA
0040155C .A3 A0334000 mov dword ptr , eax
00401561 .E8 0FFDFFFF call 00401275
00401566 .C705 A8334000 0B0>mov dword ptr , 0B
00401570 .C705 AC334000 2A1>mov dword ptr , WndPROC ;窗口回调函数
我们跟随立即数,来到 WndProc(0x0040172A),如下图:
再往下找,我们需要找 WM_COMMAND 消息处理入口,如下图,找到其处理的代码位置:
可见,处理 WM_COMMAND 消息的是 0x0040178B 处的 call 004017E9,在此位置跟随,来到该函数处:
可以看到有,0x65, 0x64, 0x66 等几个 ControlID,通过上下内容或函数跟随,可以看出,0x64 是显示 About 消息弹出框的,0x65 是退出(call 004017D4 内有 PostQuitMessage 调用)。剩下的 0x66 就是验证了,跟随 call 00401849,如下图所示:
看到以下代码:
00401849/$E8 CFF8FFFF call 0040111D ;Test
0040184E|.83F8 00 cmp eax, 0
00401851|.75 19 jnz short 0040186C
00401853|.90 nop
00401854|.90 nop
00401855|.90 nop
00401856|.90 nop
00401857|.68 8B204000 push 0040208B ; /lParam = 40208B, lParameter ===> "nOT cORRECT !!! :-P"
0040185C|.6A 00 push 0 ; |wParam = 0
0040185E|.6A 0C push 0C ; |Message = WM_SETTEXT
00401860|.FF35 3A344000 push dword ptr ; |hWnd = NULL
00401866|.E8 94000000 call <jmp.&USER32.SendMessageA> ; \SendMessageA
0040186B|.C3 retn
0040186C|>68 9F204000 push 0040209F ; /lParam = 40209F, lParam ===> "cORRECT !!!!!!:-)"
00401871|.6A 00 push 0 ; |wParam = 0
00401873|.6A 0C push 0C ; |Message = WM_SETTEXT
00401875|.FF35 3A344000 push dword ptr ; |hWnd = NULL
0040187B|.E8 7F000000 call <jmp.&USER32.SendMessageA> ; \SendMessageA
00401880\.C3 retn
可以看到,该函数直接调用另一个函数:call 0040111D,然后根据不同的返回结果(eax 的值),设置显示不同的文字。看文字字符串提示信息,可以确定 0x0040111D 就应该是校验注册的入口函数了。
跟随 call 0040111D 进去一看,有注册表和文件等方面的操作,不作静态分析,我们先返回到 OEP 去脱壳,直接用 OD 的脱壳功能就可以了。
保存脱壳文件后,我们再次用 OD 打开已脱壳的 CrackMe,进行动态跟踪。
用 OD 载入后,直接 F9 运行,出现如下界面:
按 "CONTiNUE"后,才进入主界面,如下图所示:
同时,第1个窗口最小化,按钮标签变为”EXiT",如下图所示:
这个可以算作 NAG,我们先去掉这个 NAG,退出 CrackMe 并重新加载,在 OEP 处,F8 单步运行,如下所示:
00401555 >/$6A 00 push 0 ; /pModule = NULL
00401557|.E8 49030000 call <jmp.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
0040155C|.A3 A0334000 mov dword ptr , eax
00401561|.E8 0FFDFFFF call 00401275 ;显示 NAG 窗口
执行到第4行 call 00401275 就会弹出前面所説的 NAG,因此直接 NOP 掉这个 call 00401275 就可以去除这个 NAG 了。如下所示:
00401555 >/$6A 00 push 0 ; /pModule = NULL
00401557|.E8 49030000 call <jmp.&KERNEL32.GetModuleHandleA> ; \GetModuleHandleA
0040155C|.A3 A0334000 mov dword ptr , eax
00401561 90 nop ;显示 NAG 窗口(call 00401275)
00401562 90 nop
00401563 90 nop
00401564 90 nop
00401565 90 nop
根据前面我们做的静态分析,在处理 WM_COMMAND 消息的代码中,有ID为 0x65, 0x64, 0x66 的三处分支。从上面主界面上可以看到,三个菜单 "Exit", "About", "re-Test"就是对应这三个菜单项ID。
如前面图片所示,我们在 0x004017FF 处下一个断点,分别点击这三个菜单,就可以验证其对应关系。
如点”About",就会弹出以下对话框:
在弹出前,就会断下来,我们也就知道是哪个ID了。
接下来, 我们点击 "re-Test",进行动态的验证跟踪。
通过前面的静态分析,我们知道验证函数是 0x0040111D 开始的函数,如下所示:
0040111D/$53 push ebx
0040111E|.51 push ecx
0040111F|.52 push edx
00401120|.56 push esi
00401121|.57 push edi
00401122|.55 push ebp
00401123|.BF 66214000 mov edi, 00402166
00401128|.4F dec edi
00401129|.C607 00 mov byte ptr , 0
0040112C|.68 90214000 push 00402190 ; /pHandle = The_q_3_.00402190
00401131 68 0700003C push 3C000007 ;push 3C000007 不正确,需要改成 push 00020019
00401136|.6A 00 push 0 ; |Reserved = 0
00401138|.68 3B214000 push 0040213B ; |Subkey = "SOFTWARE\Netscape\Netscape Navigator\Users"
0040113D|.68 02000080 push 80000002 ; |hKey = HKEY_LOCAL_MACHINE
00401142|.E8 3A070000 call <jmp.&ADVAPI32.RegOpenKeyExA> ; \RegOpenKeyExA
00401147|.83F8 00 cmp eax, 0
0040114A|.0F85 19010000 jnz 00401269
00401150|.68 B2214000 push 004021B2 ; /pBufSize = The_q_3_.004021B2
00401155|.68 94214000 push 00402194 ; |Buffer = The_q_3_.00402194
0040115A|.6A 00 push 0 ; |pValueType = NULL
0040115C|.6A 00 push 0 ; |Reserved = NULL
0040115E|.68 84214000 push 00402184 ; |ValueName = "CurrentUser"
00401163|.FF35 90214000 push dword ptr ; |hKey = 0
00401169|.E8 1F070000 call <jmp.&ADVAPI32.RegQueryValueExA> ; \RegQueryValueExA
0040116E|.83F8 00 cmp eax, 0
00401171|.0F85 F2000000 jnz 00401269
00401177|.FF35 90214000 push dword ptr ; /hKey = NULL
0040117D|.E8 05070000 call <jmp.&ADVAPI32.RegCloseKey> ; \RegCloseKey
00401182|.8B0D B2214000 mov ecx, dword ptr
00401188|.BE 94214000 mov esi, 00402194
0040118D|.BF 65214000 mov edi, 00402165
00401192|.B0 5C mov al, 5C
00401194|.AA stos byte ptr es: ;字符串连接
00401195|.F2:A4 repne movs byte ptr es:, byte ptr ;字符串连接
00401197|.B0 00 mov al, 0
00401199|.AA stos byte ptr es:
0040119A|.68 90214000 push 00402190 ; /pHandle = The_q_3_.00402190
0040119F 68 0700003C push 3C000007 ;push 3C000007 不正确,需要改成 push 00020019
004011A4|.6A 00 push 0 ; |Reserved = 0
004011A6|.68 3B214000 push 0040213B ; |Subkey = "SOFTWARE\Netscape\Netscape Navigator\Users"
004011AB|.68 02000080 push 80000002 ; |hKey = HKEY_LOCAL_MACHINE
004011B0|.E8 CC060000 call <jmp.&ADVAPI32.RegOpenKeyExA> ; \RegOpenKeyExA
004011B5|.83F8 00 cmp eax, 0
004011B8|.0F85 AB000000 jnz 00401269
004011BE|.68 0E224000 push 0040220E ; /pBufSize = The_q_3_.0040220E
004011C3|.68 BE214000 push 004021BE ; |Buffer = The_q_3_.004021BE
004011C8|.6A 00 push 0 ; |pValueType = NULL
004011CA|.6A 00 push 0 ; |Reserved = NULL
004011CC|.68 B6214000 push 004021B6 ; |ValueName = "DirRoot"
004011D1|.FF35 90214000 push dword ptr ; |hKey = 0
004011D7|.E8 B1060000 call <jmp.&ADVAPI32.RegQueryValueExA> ; \RegQueryValueExA
004011DC|.83F8 00 cmp eax, 0
004011DF|.0F85 84000000 jnz 00401269
004011E5|.FF35 90214000 push dword ptr ; /hKey = NULL
004011EB|.E8 97060000 call <jmp.&ADVAPI32.RegCloseKey> ; \RegCloseKey
004011F0|.8B0D 0E224000 mov ecx, dword ptr
004011F6|.49 dec ecx
004011F7|.BF BE214000 mov edi, 004021BE
004011FC|.03F9 add edi, ecx
004011FE|.BE 12224000 mov esi, 00402212 ;ASCII "\bookmark.htm"
00401203|.B9 0E000000 mov ecx, 0E
00401208|.F2:A4 repne movs byte ptr es:, byte ptr ;字符串连接
0040120A|.6A 00 push 0 ; /hTemplateFile = NULL
0040120C|.68 80000000 push 80 ; |Attributes = NORMAL
00401211|.6A 03 push 3 ; |Mode = OPEN_EXISTING
00401213|.6A 00 push 0 ; |pSecurity = NULL
00401215|.6A 03 push 3 ; |ShareMode = FILE_SHARE_READ|FILE_SHARE_WRITE
00401217|.68 000000C0 push C0000000 ; |Access = GENERIC_READ|GENERIC_WRITE
0040121C|.68 BE214000 push 004021BE ; |FileName = ""
00401221|.E8 91060000 call <jmp.&KERNEL32.CreateFileA> ; \CreateFileA
00401226|.83F8 FF cmp eax, -1
00401229|.74 3E je short 00401269
0040122B|.90 nop
0040122C|.90 nop
0040122D|.90 nop
0040122E|.90 nop
0040122F|.A3 20224000 mov dword ptr , eax
00401234|.68 24224000 push 00402224 ; /pFileSizeHigh = The_q_3_.00402224
00401239|.FF35 20224000 push dword ptr ; |hFile = NULL
0040123F|.E8 4F060000 call <jmp.&KERNEL32.GetFileSize> ; \GetFileSize
00401244|.8BD8 mov ebx, eax ;ebx == eax ==文件大小
00401246|.C1E8 10 shr eax, 10 ;eax = 文件大小 / 65536
00401249|.66:8BD0 mov dx, ax ;dx == 商
0040124C|.66:8BC3 mov ax, bx ;ax == 余数
0040124F|.E8 26FEFFFF call 0040107A ;====== 文件内容验证 ======
00401254|.83F8 01 cmp eax, 1
00401257|.75 10 jnz short 00401269
00401259|.90 nop
0040125A|.90 nop
0040125B|.90 nop
0040125C|.90 nop
0040125D|.B8 01000000 mov eax, 1
00401262|.5D pop ebp
00401263|.5F pop edi
00401264|.5E pop esi
00401265|.5A pop edx
00401266|.59 pop ecx
00401267|.5B pop ebx
00401268|.C3 retn
00401269|>B8 00000000 mov eax, 0
0040126E|.5D pop ebp
0040126F|.5F pop edi
00401270|.5E pop esi
00401271|.5A pop edx
00401272|.59 pop ecx
00401273|.5B pop ebx
00401274\.C3 retn
从上面代码可以看到,需要打开注册表路径:"HKEY_LOCAL_MACHINE\SOFTWARE\Netscape\Netscape Navigator\Users",这个键现在的系统基本不存在,因此需要先创建这些键,才可能通过 CrackMe 的验证:
可以通过导入以下内容完成这些健的创建,在 Windows x64 系统下 导下如下内容:
Windows Registry Editor Version 5.00
"CurrentUser"="solly"
"DirRoot"="F:\\Downloads\\crack\\155_The_q.3"
在 Windows x86 系统下,导入如下内容:
Windows Registry Editor Version 5.00
"CurrentUser"="solly"
"DirRoot"="F:\\Downloads\\crack\\155_The_q.3"
其中,那个 DirRoot 是一个目录,这个目录中需要有一个文件:bookmark.htm
这个文件的内容就是 CrackMe 需要验证的。上面目录名,大家需要改成自己的对应的正确路径名。
另外,上面的代码中,打开注册表的 RegOpenKeyEx()函数由于参数 samDesired 不正确,无法打开注册表进行访问。因此,需要将
00401131push 3C000007
;改成
00401131push 00020019
以及,将
0040119Fpush 3C000007
;改成
0040119Fpush 00020019
修改这两个参数问题的 Bug 后,就可以正常进行注册表操作。如下图所示:
系统首先读取 "HKEY_LOCAL_MACHINE\SOFTWARE\Netscape\Netscape Navigator\Users" 下的 CurrentUser 的键值,我设置的键值是"solly",用这个键值组成另一个注册表子路径:
“HKEY_LOCAL_MACHINE\SOFTWARE\Netscape\Netscape Navigator\Users\solly”,再读取这个子路径下的 DirRoot 的值,这个值就是一个文件路径,我设置的是 "F:\\Downloads\\crack\\155_The_q.3"。
读取到路径后,再连接文件名,组合成一个完整文件路径:
F:\\Downloads\\crack\\155_The_q.3\\bookmark.htm
然后打开并读取文件长度,通过 call 0040107A 进行文件内容验证。
call 0040107A 是对文件内容进行验证,代码如下:
0040107A/$66:50 push ax ;文件长度
0040107C|.66:8915 1A204000 mov word ptr , dx ;文件大于64K时dx才大于0
00401083|.66:8915 1C204000 mov word ptr , dx
0040108A|.66:33C0 xor ax, ax
0040108D|.A2 22204000 mov byte ptr , al ;设置成功标志为 0,表示 false
00401092|.66:833D 1A204000 00 cmp word ptr , 0
0040109A|.74 40 je short 004010DC
0040109C|.90 nop
0040109D|.90 nop
0040109E|.90 nop
0040109F|.90 nop
004010A0|>B9 10000000 /mov ecx, 10
004010A5|>51 |/push ecx
004010A6|.6A 00 ||push 0 ; /pOverlapped = NULL
004010A8|.68 18204000 ||push 00402018 ; |pBytesRead = The_q_3_.00402018
004010AD|.68 00100000 ||push 1000 ; |BytesToRead = 1000 (4096.)
004010B2|.68 26224000 ||push 00402226 ; |Buffer = The_q_3_.00402226
004010B7|.FF35 20224000 ||push dword ptr ; |hFile = NULL
004010BD|.E8 EF070000 ||call <jmp.&KERNEL32.ReadFile> ; \ReadFile
004010C2|.E8 39FFFFFF ||call 00401000
004010C7|.59 ||pop ecx
004010C8|.3C 01 ||cmp al, 1
004010CA|.74 3C ||je short 00401108
004010CC|.90 ||nop
004010CD|.90 ||nop
004010CE|.90 ||nop
004010CF|.90 ||nop
004010D0|.49 ||dec ecx
004010D1|.^ 75 D2 |\jnz short 004010A5
004010D3|.66:FF0D 1C204000 |dec word ptr
004010DA|.^ 75 C4 \jnz short 004010A0
004010DC|>66:59 pop cx ;cx == 文件长度
004010DE|.81E1 FFFF0000 and ecx, 0FFFF
004010E4|.6A 00 push 0 ; /pOverlapped = NULL
004010E6|.68 18204000 push 00402018 ; |pBytesRead = The_q_3_.00402018
004010EB|.51 push ecx ; |BytesToRead
004010EC|.68 26224000 push 00402226 ; |Buffer = The_q_3_.00402226
004010F1|.FF35 20224000 push dword ptr ; |hFile = NULL
004010F7|.E8 B5070000 call <jmp.&KERNEL32.ReadFile> ; \ReadFile
004010FC|.E8 FFFEFFFF call 00401000
00401101|.3C 01 cmp al, 1 ;这个判断没有用
00401103|.EB 05 jmp short 0040110A
00401105| 90 nop
00401106| 90 nop
00401107| 90 nop
00401108|>66:59 pop cx
0040110A|>FF35 20224000 push dword ptr ; /hObject = NULL
00401110|.E8 8A070000 call <jmp.&KERNEL32.CloseHandle> ; \CloseHandle
00401115|.33C0 xor eax, eax
00401117|.A0 22204000 mov al, byte ptr ;在这里取成功与否标志
0040111C\.C3 retn
这个函数读取文件内容,并通过调用另一个函数( call 00401000 )进行内容检查。函数 00401000 的代码如下:
00401000/$66:8B15 1A204000 mov dx, word ptr
00401007|.66:A1 1C204000 mov ax, word ptr
0040100D|.66:2BD0 sub dx, ax ;ax == dx
00401010|.66:8915 20204000 mov word ptr , dx
00401017|.66:33DB xor bx, bx
0040101A|.8B35 23204000 mov esi, dword ptr ;esi ===> 0x16,字符串长度
00401020|.AC lods byte ptr ;取得长度,al == 0x16, esi ===> "http://phrozencrew.org"
00401021|.8AD8 mov bl, al ;bl == al == 0x16
00401023|.8B0D 18204000 mov ecx, dword ptr ;文件长度
00401029|.BF 26224000 mov edi, 00402226 ;edi ===>文件内容
0040102E|.AC lods byte ptr ;al == str1
0040102F|>F2:AE /repne scas byte ptr es: ;在文件中定位 字符串: "http://phrozencrew.org"
00401031|.83F9 00 |cmp ecx, 0
00401034|.75 0A |jnz short 00401040
00401036|.90 |nop
00401037|.90 |nop
00401038|.90 |nop
00401039|.90 |nop
0040103A|>A0 22204000 |mov al, byte ptr ;是成功与否的标志
0040103F|.C3 |retn
00401040|>50 |push eax
00401041|.57 |push edi
00401042|.51 |push ecx
00401043|.66:8BCB |mov cx, bx ;字符串 str1 长度
00401046|.49 |dec ecx
00401047|>AC |/lods byte ptr
00401048|.8A27 ||mov ah, byte ptr
0040104A|.3AE0 ||cmp ah, al
0040104C|.75 15 ||jnz short 00401063 ;不相等则失败
0040104E|.90 ||nop
0040104F|.90 ||nop
00401050|.90 ||nop
00401051|.90 ||nop
00401052|.66:49 ||dec cx
00401054|.74 07 ||je short 0040105D ;当cx==0时,字符串相等。即文件中存在该字符串
00401056|.90 ||nop
00401057|.90 ||nop
00401058|.90 ||nop
00401059|.90 ||nop
0040105A|.47 ||inc edi
0040105B|.^ EB EA |\jmp short 00401047
0040105D|>FE05 22204000 |inc byte ptr ;设置 成功标志
00401063|>8B35 23204000 |mov esi, dword ptr ;The_q_3_.00402000
00401069|.83C6 02 |add esi, 2
0040106C|.59 |pop ecx
0040106D|.5F |pop edi
0040106E|.58 |pop eax
0040106F|.803D 22204000 01 |cmp byte ptr , 1
00401076|.^ 74 C2 |je short 0040103A
00401078\.^ EB B5 \jmp short 0040102F
这个函数就是检查文件中是否包含字符串:“http://phrozencrew.org”,并且这个字符串也不能位于文件的最前面。
如果文件包含“http://phrozencrew.org”并不在最前面,就表示验证通过,在 0x0040105D 处,将全局变量 的值设置为 1。否则其值就保持为 0 不变。
验证通过后,主界面显示如下:
Status 变为:cORRECT !!!!!! :-)
最后,説一下 Hook,这个 Hook 在 OD 暂停 CrackMe 时,也会导致Windows 的"开始菜单栏"暂停,不响应鼠标事件了。所以,也将这个处理掉,如下图所示:
就是将 0x00401613 处的 NOP,改成 JMP 00401636 就可以跳过鼠标 Hook 的加载。(更新补充:也可以直接修改 00401610 处的 je 0040167C 为 jmp 00401636,这样有没有 flaf.dll 都可正常运行 CrackMe,比加一个 jmp 更好。)
全部修改完后,我们利用 OD 保存 patch 的功能,保存这些修改:
1、去NAG。
2、注册表访问 BUG。
3、去鼠标 Hook。
如下图,保存这些修改:
选择后,会弹出一个确认框,确认“全部复制”后OD会转到 patch 子窗口,再右键选“保存文件”,输入一个新文件名再保存就可以了。
最后,在我机器上,OD 在 Dump 脱壳文件时,会失去响应,如下:
只能强行关闭,但脱壳的文件还是保存成功了,可以正常运行。
我的那个 bookmark.htm 文件内容如下:
正确的文件内容要包含一个网址,并且不放在最前面即可:http://phrozencrew.org
是包括前面的汉字的。
分析完毕!!!
膜拜大佬666 学习了,让我突然有了灵感 学习了,继续加油挑战更难的 还是比较深奥的,搞不懂。 感谢分享,长知识了 感谢分享
页:
[1]