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 |
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
This site is part of Sunshine's Homepage