into a high level interpretation of a disassembled file
In this applied guide, our main goal is to apply reverse engineering to real life situations. Today we will be analyzing a crackme I coded in assembly and will disassemble the file and reconstruct it into a C-style code, and compile our own version.
The tools required for today is just good old Ollydbg, a ring3 debugger or an user interface debugger, and optional is a C/C++ compiler.
Wait! What is Ollydbg and ring3 ? I don't have a ring. ='[ Well no need to worry, if you don't know what these terms are. I'll review them just in case you forgot or don't know exactly what they are.
OllyDbg – is a ring3 debugger also known as a user-mode interface debugger. Basically OllyDbg is a tool that allows us to see the inner-workings of a target application. OllyDbg makes more of an effort as a debugger and provides a strong and easy interface and allows extensible plugins to be added, as well as provides built-in hex-editor features and tons more nifty stuff.
Ring3/Ring0 Debugger – Basically there are two types of debuggers out there, there are the strong powerful Ring0 debuggers, which are also known as kernel debuggers, than theres user-mode aka ring3 debuggers. The main difference between these debuggers is, the operating system interaction. With a Ring0 debugger you get to analyze the operating systems interaction with the program, and can see library functional calls from DLLs. Ring0 are very powerful but provide tons of draw-backs, ring0 debuggers are advance debuggers and mainly used for driver development programming. Ring3 are more commonly used to analyze one particular executable file and reverse engineer a file. Most crackers out there, widely use a Ring3 Debugger like Olly because it provides easier use, but old school crackers and more knowledgeable power users or operates prefer Ring0 debuggers such as SoftICE.
Compiler – A compiler is a nifty tool for programmers to link an object file into a machine executable code or program. Programming with a compiled language such as C requires a programmer to type in its source code, the source code is then compiled into an object file, which has symbols and information ready , but cannot be executed by the operating system yet. The object file is then linked, and built into an executable by a linker.
What we would be doing today, is taking OllyDbg and ripping apart the inner-workings of a program. We'll see how the program is constructed, and we will interpret this into a higher level language such as C. Little knowledge of C is expected because we will reconstruct the target executable into C
First we start off by opening up the target executable into OllyDbg
.
This is the code view of Olly. And as you can see this is our main program. Right away you can see we start off execution with an API call. OllyDbg is kind enough to leave us comments about what it thinks about the following code.
As we can see on the far right a call to the API MessageBoxA is being made. The call is shown as followed:
CALL <JMP.&user32.MessageBoxA> . If we check our Win32 Programmers Reference Guide on this symbol we get the following:
Whats more important is its parameters, which are constructed as follows:“The MessageBox function creates, displays, and operates a message box. The message box contains an application-defined message and title, plus any combination of predefined icons and push buttons.”
Code: Select all
int MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
UINT uType // style of message box
);
Code: Select all
Style = MB_OK
As you can if the first placed first, it would wind up ending up on the botton of the stack being the last value. Such as the above figure. So so far our parameters look as the following:
int MessageBox(
HWND hWnd, // handle of owner window
LPCTSTR lpText, // address of text in message box
LPCTSTR lpCaption, // address of title of message box
MB_OK // style of message box
);
If referred to our documentation we can also see that MB_OK is the default flag, and contains an ok push botton and just displays texts:
Now reading the next line in Olly and we can see its the next set of parameters for our function call to the API MessageBox.“MB_OK The message box contains one push button: OK. This is the default.”
Code: Select all
00401002 PUSH crackme1.00403000 ;|Title = "CrackMe by fAIL!"
00401007 PUSH crackme1.0040306C ;|Text = "This is a Crackme coded by fAIL! IN ….”
0040100C |.PUSH 0 ; |hOwner = NULL
Code: Select all
int MessageBox(
NULL,
“This is a Crackme coded by fAIL! iN pure ASM. ^^ Enjoy”,
“CrackMe coded by fAIL!”
MB_OK
);
At this point we have completely reconstructed the first API call of our application. So we can start mapping out our source code as the following:
Code: Select all
#include <windows.h> // for our WinAPI call to MessageBoxA
int main()
{
//Our first reconstructed API call takes the given four parameters.
MessageBox(NULL,”This is a Crackme coded by fAIL! IN pure ASM. ^^ Enjoy”,”Crackme coded by fAIL!”,MB_OK);
}
Code: Select all
00401013 | . A1 A3304000 | MOV EAX,DWORD PTR DS:[4030A3]
Reference from the Intel Opcodes and Mnemonics manual we define the mnemonic MOV as:
The MOV operation in this case is using the EAX register. You can think of registers as local variables. So obviously this line we are assigning a local variable a value. That value is being pointed to the data segment, address space 4030A3. We could search the address and find its value or we can use Olly to help us find the value of EAX after being assigned by the MOV mnemonic.MOV – Move Byte or Word
Usage: MOV dest,src
Modifies flags: none
Copies byte or word from the source operand to the destination operand. If the destination is SS interrupts are disabled expect on early buggy 808x CPUs. Some CPUs disable interrupts if the desintation is any of the segment registers. “
In between the hex-edior and our debugger view is a small box that shows the value of both 004030A3 and the value of EAX after being assigned a value. We can also look on the right hand corner and see in the Registers panel that EAX is assigned the value 00000000 which is equal to the value 0. [Appended zeros are added as address space, but aren't really required when reconstructing our binary file.
Since the EAX is being assigned to a pointer pointing to data in a remote location, we can assume that this value, address space DS:[4030A3] is not initialized in our main() function but initialized outside as a global value. So the c-equivalent would be: int eax = 0;
The next line introduces us to another, but more simply mnemonic, the CMP mnemonic.
Code: Select all
00401018 . 83F8 00 CMP EAX,0
In our case the value of EAX is being compared to the integer 0. Knowing that EAX is in fact zero, the compare returns as true. So the comparison is set to true and the zero flag is set to one which is equal to saying true. Compares can be thought as or implemented as if's statements in C.“ CMP – Compare
Usage: CMP dest,src
Modifies flags: AF CF OF PF SF ZF
Subtracts source from destination and updates the flags but does not save result. Flags can subsequently be checked for conditions. “
In our case, we implement this as: if(eax == 0){executenextline}
Code: Select all
0040101B . /75 0C JNZ SHORT reconstr.00401029
If we take the jump we can see the next call is an API call to the MessageBoxA function, and we've seen this before so we can skip going over how to construct this and we'll just map it as:
if(eax==0){MessageBox(insertparametershere);}
But what if we, or a hacker, somehow change the value if our int eax, than we need a else statement for those conditions. In the else statement we will refer to the next line of JNZ because if validated false, than the computer just executes the next line of code, which would be:
Code: Select all
0040101D . /EB 1F JMP SHORT reconstr.0040103E
After the MessageBox call, you notice another jump to the following code:
Code: Select all
0040101F POP EAX ;kernel32.7C817077
00401020 PUSH 0
00401022 PUSH 0 ; /ExitCode = 0
00401024 CALL <JMP.&kernel32.ExitProcess> ; \ExitProcess
Now lets put the pieces together and see what we get ;]
http://code.suck-oold.com/182 -- code cased in pastebin section to save room in post.
Explanation and implementation
Lets explain the following code, although theres not much to say if you know some C coding already. We start off with the #include <windows.h> header because the MessageBoxA api call is from the windows library, thus we include the library so we can call the function MessageBox() [the appened A just denotes for ASCII character code set, while MessageBoxW denotes for wide-character code set, MessageBoxA is preset default.]
Now we move onto the next line, which is a global integer named eax: int eax = 0; This variable is initialized as global because it allows for easier modification, any function call can modify its value, but if the variable eax belongs to the function main() than the variable will be out of scope after the main() function exits. This also gives it its own special allocation in memory at a separate special segment as we saw before.
Next we call our main function int, just for convenience, we could've used the WinAPI call to main() and disabling the use of the commandline showing, but its not necessary. You could however, replace int main with:
Code: Select all
int WINAPI WinMain (HINSTANCE hThisInstance,
HINSTANCE hPrevInstance,
LPSTR lpszArgument,
int nFunsterStil)
Next this is where making the variable eax global is a very important role and provides a horrible coding choice for programming. Now the program checks to see if eax is equal to the value 0, and since we first initialized eax as 0 , than the statement is true. But since its a global value, another function may accidental modify it if you forget that the value has already been initialized, or, a hacker can easily locate its memory address since its stored away in a special segment and modify the value to a non-zero character, for example modify its value to 1 so the statement turns false.
This is where program protection comes into play, such a program scheme where if a registered user is given a boolean evaluation, if the user is legit, set variable to 1, if not than set variable to 0, provides poor coding choices. These values are easily modified in memory and thus provide no protection.
For this program, if the eax value is not set to 0, than you jump to the goodboy message, which says you successfully modified the program against its will. Such a scheme provides simple obscurity thats easily crackable even to the neophytes of the reverse engineering scene. Higher obscurity is advised , such as string encryption and executable protection. Such topics will be covered in other issues, as for now, I am out.
// written by EbRiZZlez.
Download Links:
Pdf Verision:
Code: Select all
http://rapidshare.com/files/254034800/Output.zip.html
PrePacked Reversing Kit (Self Extracting):
Code: Select all
http://rapidshare.com/files/254032555/cracktools.exe.html
/*Note: dont mind the grammar errors , tut was made at 5 in the morning. ^^ */
contact iNFO:
ebrizzlez @ hotmail [dot] com
-- yahoo account is locked currently, and for unknown reasons, I am trying my best to find out why its locked, but nothing so far.