Intro-
OllyDbg - the best ring3 debugger ever :) - has a very handy feature: if you give Olly the path of the Win32 API documentation, you can get info on an API function by highlighting it, and pressing CTRL+F1. Well, this works fine on XP, but it doesn't work on Vista. Why? As you may already know, Microsoft made .hlp help files deprecated since Vista. Instead, the .chm format is recommended. Olly uses the WinHelp API (this function is used to open .hlp helps) to open the API documentation. When a program uses WinHelp on Vista (or you just simply try to open a .hlp file), a window pops up that tells you, that .hlp is dead and buried. Vista is good, CTRL+F1 is good too, so something had to be done.. Yeah, well, I could just simply download WinHlp32.exe for Vista, but hey, what's the fun in that? :)
.hlp to .chm
First, I did the boring part of the job: I converted the Win32 API .hlp file to .chm format. This can be done with two free tools: HelpDeco and HTML Help Workshop. Uncompress the .hlp file...
CODE :
helpdeco.exe win32.hlp /y
... and generate a content file:
CODE :
helpdeco.exe win32.hlp /y /c
When this is done, start a new project in HTML Help Workshop, and check the Convert WinHelp project checkbox. From now on, just follow the wizard, and your reward will be a brand new, shiny .chm file!
Let's code!
There is an API function similar to WinHelp which deals with .chm help files: HtmlHelp. So, my plan was simple:
1. write a function, that wraps HtmlHelp, so it can be called with the same parameters as WinHelp
2. find a code cave (a bunch of 0x00 bytes) in Olly, and insert the wrapper there
3. replace all WinHelp calls with a call to my wrapper.
Indeed, I could write the wrapper function inside Olly into the code cave, but I choose a more comfortable way. I fired up WinASM, and wrote the function and a little framework to test it. Here is the source code of the function:
CODE :
MyWinHelp proc hWndMain:HWND, lpszHelp:LPCTSTR, uCommand:UINT, dwData:DWORD
LOCAL aklink:HH_AKLINK
.if uCommand == HELP_PARTIALKEY
mov aklink.cbStruct, sizeof(HH_AKLINK)
mov aklink.fReserved, FALSE
mov eax, dwData
mov aklink.pszKeywords, eax
mov aklink.pszUrl, NULL
mov aklink.pszMsgText, NULL
mov aklink.pszMsgTitle, NULL
mov aklink.pszWindow, NULL
mov aklink.fIndexOnFail, TRUE
invoke HtmlHelp, hWndMain, lpszHelp, HH_KEYWORD_LOOKUP, addr aklink
.elseif uCommand == HELP_CONTEXT
invoke HtmlHelp, hWndMain, lpszHelp, HH_HELP_CONTEXT, dwData
.elseif uCommand == HELP_INDEX
invoke HtmlHelp, hWndMain, lpszHelp, HH_DISPLAY_INDEX, 0
.endif
ret
MyWinHelp endp
The WinHelp function's uCommand parameter can have a lot of values; I implemented only the ones, that occur in Olly (HELP_PARTIALKEY, HELP_CONTEXT and HELP_INDEX).
OK, I got the wrapper, I had to insert it into Olly. I opened the above mentioned framework in Olly, highlighted the wrapper, and copied the opcodes to the clipboard with the Asm2Clipboard plugin. Then, I opened Olly in - guess what - another instance of Olly, did a little digging to find a code cave, and binary pasted the content of the clipboard. NOTE: I have cheated a bit with the code below. It doesn't look like this right after inserting it; a few adjustments have to be made. I'll explain these changes later.
CODE :
004AF6EE /$ 55 PUSH EBP
004AF6EF |. 8BEC MOV EBP,ESP
004AF6F1 |. 83C4 E0 ADD ESP,-20
004AF6F4 |. 817D 10 05010000 CMP [ARG.3],105
004AF6FB |. 75 4B JNZ SHORT Lbr68_-_.004AF748
004AF6FD |. C745 E0 20000000 MOV [LOCAL.8],20
004AF704 |. C745 E4 00000000 MOV [LOCAL.7],0
004AF70B |. 8B45 14 MOV EAX,[ARG.4]
004AF70E |. 8945 E8 MOV [LOCAL.6],EAX
004AF711 |. C745 EC 00000000 MOV [LOCAL.5],0
004AF718 |. C745 F0 00000000 MOV [LOCAL.4],0
004AF71F |. C745 F4 00000000 MOV [LOCAL.3],0
004AF726 |. C745 F8 00000000 MOV [LOCAL.2],0
004AF72D |. C745 FC 01000000 MOV [LOCAL.1],1
004AF734 |. 8D45 E0 LEA EAX,[LOCAL.8]
004AF737 |. 50 PUSH EAX
004AF738 |. 6A 0D PUSH 0D
004AF73A |. FF75 0C PUSH [ARG.2]
004AF73D |. FF75 08 PUSH [ARG.1]
004AF740 |. FF15 95F64A00 CALL DWORD PTR DS:[4AF695]
004AF746 |. EB 2F JMP SHORT Lbr68_-_.004AF777
004AF748 |> 837D 10 01 CMP [ARG.3],1
004AF74C |. 75 13 JNZ SHORT Lbr68_-_.004AF761
004AF74E |. FF75 14 PUSH [ARG.4]
004AF751 |. 6A 0F PUSH 0F
004AF753 |. FF75 0C PUSH [ARG.2]
004AF756 |. FF75 08 PUSH [ARG.1]
004AF759 |. FF15 95F74A00 CALL DWORD PTR DS:[4AF795]
004AF75F |. EB 16 JMP SHORT Lbr68_-_.004AF777
004AF761 |> 837D 10 03 CMP [ARG.3],3
004AF765 |. 75 10 JNZ SHORT Lbr68_-_.004AF777
004AF767 |. 6A 00 PUSH 0
004AF769 |. 6A 02 PUSH 2
004AF76B |. FF75 0C PUSH [ARG.2]
004AF76E |. FF75 08 PUSH [ARG.1]
004AF771 |. FF15 95F64A00 CALL DWORD PTR DS:[4AF695]
004AF777 |> C9 LEAVE
004AF778 \. C2 1000 RETN 10
The wrapper uses HtmlHelp which is exported by HHCtrl.ocx. This file is not loaded by Olly, so I had to load it, and get the address of the HtmlHelp API function. This piece of code is quite simple, so I didn't use WinASM this time.
CODE :
004AF6BC . A1 1B014B00 MOV EAX,DWORD PTR DS:[4B011B]
004AF6C1 . 90 NOP
004AF6C2 . 60 PUSHAD
004AF6C3 . 68 80F64A00 PUSH Lbr68_-_.004AF680 ; /FileName = "HHCtrl.ocx"
004AF6C8 . E8 6BFAFFFF CALL <JMP.&KERNEL32.LoadLibraryA> ; \LoadLibraryA
004AF6CD . 68 8BF64A00 PUSH Lbr68_-_.004AF68B ; /ProcNameOrOrdinal = "HtmlHelpA"
004AF6D2 . 50 PUSH EAX ; |hModule = 764C36C4
004AF6D3 . E8 B8F9FFFF CALL <JMP.&KERNEL32.GetProcAddress> ; \GetProcAddress
004AF6D8 . A3 95F64A00 MOV DWORD PTR DS:[4AF695],EAX ; kernel32.BaseThreadInitThunk
004AF6DD . 61 POPAD
004AF6DE .^ E9 3419F5FF JMP Lbr68_-_.00401017
A little explanation: instructions through 0x004AF6C3 to 0x004AF6D3 load HHCtrl.ocx and get the address of the HtmlHelp function. Two APIs are used to achieve this task: LoadLibrary and GetProcAddress. If I assemble the call of these functions in Olly as CALL LoadLibrary and CALL GetProcAddress, Olly will use their actual address. This can be a problem, because the actual address of API's can vary (e.g.: ASLR, different version of OS). The problem can be eliminated by calling the APIs through the jump thunk table. So, I had to find the functions in the jump thunk table, and CALL those addresses (e.g. instead of CALL LoadLibrary, I had to assemble CALL 4AF138):
CODE :
004AF138 $- FF25 B4D35000 JMP DWORD PTR DS:[<&KERNEL32.LoadLibraryA>] ; kernel32.LoadLibraryA
There are some instructions, I haven't explained yet, so let's move on! The instruction at 0x004AF6BC was originally located near the entry point of Olly. I've replaced it with the JMP, which jumps to this piece of code, so I had to insert it here. Here is the JMP at near EP (at 0x00401012):
CODE :
0040100D E9 DB E9
0040100E . 28014B00 DD Lbr68_-_.004B0128
00401012 > E9 A5E60A00 JMP Lbr68_-_.004AF6BC
00401017 > C1E0 02 SHL EAX,2
The PUSHAD stores the values of the general purpose registers before my code runs, and the POPAD restores these values after my code. This is important, because a messed up register value could crash the program. The MOV at 0x004AF6D8 stores the address of HtmlHelp at address 0x004AF695 (this address is also located in the code cave). And now is the time to tell you more about the above mentioned adjustments to the wrapper code. All CALL HtmlHelp instructions had to be changed to CALL DWORD PTR[4AF695], because the address of HtmlHelp is stored at 0x004AF695. The last instruction, the JMP at 0x004AF6DE jumps back to near the entry point, from where we have jumped here earlier.
One more thing: we need the strings "HHCtrl.ocx" and "HtmlHelpA" to be able to load the library, and get the address of the function. Of course this is not a big deal, there are plenty of space in the code cave, so we can put them there:
CODE :
004AF680 . 48 48 43 74 72 6C 2E 6>ASCII "HHCtrl.ocx",0
004AF68B . 48 74 6D 6C 48 65 6C 7>ASCII "HtmlHelpA",0
If we save the modifications to the .exe, and try to run it, it will crash. Uh-oh, now what? Never fear, I is here (yeah, well.. who can tell me, which movie is this quote from? :) ). The code cave is in the .text section, which is a non-writable section, yet we try to write the address of HtmlHelp to 0x004AF695, which is indeed in the .text section. We can easily make .text section writable, there are a lot of tools capable of doing the job. Or it can be done manually, with a hex editor, but we are not masochists. At least, I am not, so I have used PEditor. Here is how to do it with that tool: open the .exe with PEditor, click on the sections button, right click on the .text section, choose edit section, than char. wizard. Now check the writeable checkbox, click on take it!, and you're ready.
Now the program runs normally. All we have to do is replace all WinHelp calls with a call to the wrapper. We could search for all WinHelps, and replace them with a CALL 4AF6EE (I have the wrapper at that address), but we are lazy, so we take another path. That path leads us to the jump thunk table. We search for WinHelp in the table, and replace the jump with a JMP 4AF6EE. Here I have done the change (at 0x004AF5FA):
CODE :
004AF5F4 $- FF25 90D95000 JMP DWORD PTR DS:[<&USER32.UpdateWindow>] ; USER32.UpdateWindow
004AF5FA E9 EF000000 JMP Lbr68_-_.004AF6EE
004AF5FF 90 NOP
004AF600 $- FF25 98D95000 JMP DWORD PTR DS:[<&USER32.WindowFromPoint>] ; USER32.WindowFromPoint
We can now save the modifications to the .exe, and we can enjoy our .chm-capable OllyDbg :)
Thank you for reading this stuff.
Amit Tyagi
amazing software cracking tool
ReplyDelete