Making a "special" crack for bLaCk-eye's first crackme - by Sunshine

Target : bLaCk-eye's First Crackme
File : Dan.exe (10.240 bytes)
Downloaded from : Kanal23
Author : bLaCk-eye
Tools used :

- PEditor
- OllyDbg
- Hex Editor (I used Hex Workshop)
- Knowledge of PE structure and asm

Introduction:
Ok first lets's run the crackme. Two Messageboxes pop up, the first one says it's a nag and we have to remove it. The second one says it's unregistered and we have to make it say 'Registered'.
Open the file in PEditor to gather some infos. What comes to our eyes is the fact it has 7 sections (2 .adata and 2 .@data sections... quite abnormal) and the entrypoint is 7001 which is in the .@data section. So we can assume it's packed or crypted.
This tutorial consists of two parts. First we'll gonna crack the file, then we code a nice crack in MASM which will exactly perform the steps we do by hand.


PART I : Let's crack it...
Let's start Ollydbg, load our file (ignore the warning), set a breakpoint with F2 (again ignore the warning) on the second line (00407002 E8 03000000 CALL DAN.0040700A) , run it with F9, trace with F7 into this call and continue tracing with F8. We see some suspicious 'pop ebp' and loops etc. Well, I must admit that I don't fully understand what's going on here. But it looks like self-modifying code, which unpacks/uncrypts the section.
So what should I do now? I tried tracing to the point where we jump to the real entrypoint but it took so long... so I gave up.
I tried another approach. Close Ollydbg, run the crackme (don't press a button!), open PEditor, go to tasks and choose our crackme. Then click with the right mousebutton on it and choose "Dump (Full)" and save it as Dumped.exe. Now we've dumped the file from memory (where it is unpacked/uncrypted) to disk. Wow, the file has now 40kb (!) and it still runs normal without crashing! Very good, so we need no import table rebuilding.
So run Ollydbg again, load our dumped file, set again a breakpoint on the second line, start it with F9 and trace with F7. After some tracing, we jump to this:


00401000 . 6A 30 68 00 ASCII "j0h",0 ; this is 6A30 = push 30
00401004 . 30 40 00 ASCII "0@",0 ; this is 6800304000 = push 000403000
00401007 . 68 0A 30 40 00>ASCII "h 0@",0 ; this is 680A304000 = push 0040300A
0040100C . 6A 00 PUSH 0 ; |hOwner = NULL
0040100E . E8 1B000000 CALL DUMPED.0040102E ; \MessageBoxA
00401013 . 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401015 . 68 2A304000 PUSH DUMPED.0040302A ; |Title = "UNREGISTERED!!"
0040101A . 68 39304000 PUSH DUMPED.00403039 ; |Text = "Fix it so it says registered"
0040101F . 6A 00 PUSH 0 ; |hOwner = NULL
00401021 . E8 08000000 CALL DUMPED.0040102E ; \MessageBoxA
00401026 . 6A 00 PUSH 0 ; /ExitCode = 0
00401028 . E8 07000000 CALL DUMPED.00401034 ; \ExitProcess

So the real program code starts at 401000. But is this code in file? Or is this real code still crypted and decrypted at runtime?. So close Olldbg, start Hex Workshop and go to offset 1000 (file offset of 401000) and we see exactly these bytes. At offset 3000 we see the strings. Yeah! So we open PEditor and change entrypoint from 7001 to 1000. So just the code above is executed. Run it and we see it works. Now we can patch it to solve this crackme.

Go to offset 1000 and change 6A00 (push 30) to EB11 (jmp 401013). That will skip the nag box, we just jump over it directly to the second messagebox.
Now go to offset 302A and overwrite this UNREGISTERED!!. with REGISTERED!. Don't forget to terminate this string with a zero.
Finally go to offset 101A and change 6839304000 (push 00403039) to 682A304000 (push 0040302A). So we push our "REGISTERED!" instead of "Fix it so it says registered". Save it and run it. Just one messagebox pops up saying Registered! Solved! Now it looks like::


00401000 > $ EB 11 JMP SHORT DUMPEDOR.00401013 ;jump over the nag
00401002 . 68 00304000 PUSH DUMPEDOR.00403000 ; |Title = "bLaCk-eye"
00401007 . 68 0A304000 PUSH DUMPEDOR.0040300A ; |Text = "This is a nag!!! Eliminate it!!"
0040100C . 6A 00 PUSH 0 ; |hOwner = NULL
0040100E . E8 1B000000 CALL DUMPEDOR.0040102E ; \MessageBoxA
00401013 > 6A 00 PUSH 0 ; /Style = MB_OK|MB_APPLMODAL
00401015 . 68 2A304000 PUSH DUMPEDOR.0040302A ; |Title = "REGISTERED!"
0040101A . 68 2A304000 PUSH DUMPEDOR.0040302A ; |Text = "REGISTERED!" ;push "REGISTERED" again
0040101F . 6A 00 PUSH 0 ; |hOwner = NULL
00401021 . E8 08000000 CALL DUMPEDOR.0040102E ; \MessageBoxA
00401026 . 6A 00 PUSH 0 ; /ExitCode = 0
00401028 . E8 07000000 CALL DUMPEDOR.00401034 ; \ExitProcess

PART II : Coding our crack
(Note: this crack will just work on Win95/98 cause we use toolhelp apis like CreateToolhelp32Snapshot... which don't work under Win2000/XP as far as I know. I just have Win98 so it's ok for me. But perhaps someone wants to write a Win2000 version of this crack and send it to me? Could publish it on my site :-)

Ok, our crack has to do the following things: Dump the File from memory, fix the sections (I explain this later), change entrypoint and crack it.
I explain just the theory here, so I suggest you have also a look at the commented source!

PART II-1 : Dump the file from memory
Well, we have to start the crackme (function: CreateProcess) and wait till it has fully loaded and unpacked in memory (function: WaitForInputIdle). But we need 2 pieces of information : where the process is loaded in memory and what size it has.
So we make a snapshot of all running process
(function: CreateToolhelp32Snapshot) and look for the crackme (Pe32.th32ProcessID == pi.dwProcessId). Then we make a snapshot of all modules of this process and search for the right one (we need the crackme itself, not a dll it uses or something). Now we can read from out module the modBaseSize and modBaseAddr. Then we get a buffer (function: LocalAlloc), read whole process in it (function: ReadProcessMemory) and save it to disk (function: CreateFile, WriteFile). Finally we close all handle and terminate the process (function: TerminateProcess).

PART II-2 : Fix the sections
Now you will probably ask: Hey what is this? And why did we have not done it when we cracked it?
Well, if we dump the file from memory the section table does not fit to the dumped file. PEditor fixed it automatically for us when we dumped it.
The section table looks in the original file (Dan.exe) like this:

Section Virtual Size Virtual Offset Raw Size Raw Offset
.text 00001000 00001000 00000045 00000400
.rdata 00001000 00002000 0000004E 00000600
.data 00001000 00003000 0000006A 00000800
.@data 00002000 00004000 00000A18 00000A00
.adata 00001000 00006000 00000000 00001600
.@data 00002000 00007000 0000100C 00001600
.adata 00001000 00009000 00000045 00002800

Consider the first section: It consists of 45h bytes and is at offset 400h in file. When the file is loaded in memory (that means when it's started) the first section is loaded at offset 1000h, not at 400h! And it has a size of 1000h bytes in memory, not only 45h bytes. (the rest is filled with zeros). The second section is loaded at offset 2000h with a size of 1000h bytes in memory. In file it's at offset 600.
That's because section alignment is 1000h, but file alignment is 200h as you can easily see in PEditor.
But if you dump (or say here "copy") the file from memory, it's clear that the first section begins now in file also at offset 1000h, the second at offset 2000h and so on. Now you see why the file size grows that much.
When you try to run the dumped file, it's obvious why it crash. In the PE Header the section table is still the same, but the file offsets are now wrong. The first section start at offset 1000h, no more at 400h.
If you understood this, then it's clear to you that we have to make Raw Offset the same as Virtual Offset and Raw Size the same as Virtual Size. So new section table looks like:

Section Virtual Size Virtual Offset Raw Size Raw Offset
.text 00001000 00001000 00001000 00001000
.rdata 00001000 00002000 00001000 00002000
.data 00001000 00003000 00001000 00003000
.@data 00002000 00004000 00002000 00004000
.adata 00001000 00006000 00001000 00006000
.@data 00002000 00007000 00002000 00007000
.adata 00001000 00009000 00001000 00009000

The source code which fixes the section: In ecx is counter for section (ecx=0 -> first section; ecx = 1 -> second section and so on)
xor ecx, ecx ; ecx=0
DoNextSection:
xor ebx, ebx ; ebx=0
add ebx, elfanew ; ebx=address of new PE Header
add ebx, 0F8h ; 0F8h bytes after PE begins section table
add ebx, 8 ; go to virtual size

mov edx, 40 ; 40 = size of one section in Section Table
imul edx, ecx ; edx = 40 * section where we are

add ebx,edx ; add it to ebx
pushad ; save registers
invoke SetFilePointer, DumpFileHandle, ebx, 0, FILE_BEGIN
invoke ReadFile, DumpFileHandle, addr VirtualSize, 4, addr howmany, NULL ; read VirtualSize
invoke SetFilePointer, DumpFileHandle, 4, 0, FILE_CURRENT
invoke WriteFile, DumpFileHandle, addr VirtualSize, 4, addr howmany, NULL ; write VirtualSize as new RawSize

invoke SetFilePointer, DumpFileHandle, -8, 0, FILE_CURRENT
invoke ReadFile, DumpFileHandle, addr VirtualOffset, 4, addr howmany, NULL ; read VirtualOffset
invoke SetFilePointer, DumpFileHandle, 4, 0, FILE_CURRENT
invoke WriteFile, DumpFileHandle, addr VirtualOffset, 4, addr howmany, NULL ; write VirtualOffset as new Raw Offset

popad ; restore registers
inc ecx ; next section
cmp ecx,7 ; all sections done
jnz DoNextSection

PART II-3 : Fix the entrypoint and patch file
Well, not much to say here. Move at offset 28h in the PE Header and replace the old entrypoint with the new one (function: SetFilePointer, WriteFile). With the same functions you easily patch the file.

Hope everything is right and you understand it.
A question to everyone out there: We use this complicated & incompatible "CreateToolhelp32Snapshot"-thing to get the size and address of process in memory. You can easier get the size from the PE Header, it's the Size of Image value. But is there another way to get the address of the process in memory? The Imagebase value in the PE is just the prefered address which not means that the process is every time mapped to this adress. Can anyone help me?

Sunshine, May 2003


[back]

This site is part of Sunshine's Homepage