Thursday, June 27, 2013

Manual UPX Unpacking of binary files

Hey guys ,

Here comes a new technical post after a 4-month delay  ,and some rough political issues in here !

Anyway ,in this post we are going to discuss one of the most important tools that must be known for any reverse engineering hacker or binary analyst professional ,technically named UPX unpacking ,unlike famous unpacking tools nowadays people using ,we are going to manually unpack a UPX packed binary image .

First of all ,what is UPX ?

UPX ,abbreviation of the Ultimate Packing for eXecutables ,is a packing tool for binary files ,used to compress data inside a file ,and automatically decompress it when executing the file ,that way of obfuscation of data helps malware hackers to trick in-memory scanning applications ( AV's ,anti-spyware's ,etc... ) ,so that they won't find in a direct way ,the malicious signature of the malware ,before actually executing it .

Technically ,in the packing step ,UPX compresses every section of the binary file ( such as .text , .bss sections ) ,and renames them to sequential blocks UPX0 ,UPX1 and so on ,and then it appends a block of code in the end of the code section of the file ,that decompresses each section and fixes the IAT when executing the file ,and finally ,it changes the OEP of the real binary file to the new decompressing code block appended in the end of the code section .

In the unpacking step ,the following scenario takes place :
-First ,It saves the values of all GPR's and status flags by pushing them into the stack ( most probably ,using a PUSHAD instruction )
-Then ,It starts decompressing the packed blocks ( UPX0 ,UPX1 ,... ) ,and saves them into memory .
-It then fixes the IAT of the real unpacked binary .
-Finally ,it restores saved GPR's and flags ( most probably ,using a POPAD instruction ) ,and redirects the flow to the real OEP .

Well ,honestly ,this is one of the simplest kinds of in-memory executable packing ,some other packers use custom compressing algorithms ( or even a custom UPX packing scheme ) ,and sometimes ,UPX packing doesn't unpack bytes in-place ,it might use a temporary file to unpack the real executable at .

To clarify the picture a bit ,I'm going to show a real world study case ,we will step from unpacking ,until getting the full real executable ,manually !

Tools :
-PEid ,to just be sure that our executable is detected as a UPX packed binary file.
-Immunity Debugger ,or any other debugger you prefer working with .
-UPX ,the official UPX packing tool .
-Import reconstructor ,a great (really!) IAT fixing tool .

Note: Import Reconstructor is used because the fact that we are going to manually unpack our binary file ,so we will make each step the unpacker does to decompress the file ,including the IAT fixing step .

Scenario :
First of all ,we will make our own simple executable ,here ,i prefer to make a simple one ,so i made this simple C application :

Compile ,link ,and take the binary executable aside ,that will be our *real* executable file .
First ,let's note some info about it ,we will first load it into PEid ,to make sure about some stuff .

We can notice some important stuff ,first ,it's not a packed binary ,it was compiled in Dev-C++ 4.9.x and linker version is 2.56 ,with the file size and RVA entry address .
Hanging around with PEid ,we can reveal more important information .

Here ,we can see all the binary image sections ,their relative virtual addresses and offsets .

Note that if the file was packed ,we should see UPX0 ,UPX1 ,UPX2 ,and so on ,instead of the real sections .

Here ,there are a lot of information to notice ,first ,our real OEP is 0x401220 ( Flat model physical address ) ,we can also notice the number of sections ,as well as the base address of the code and data sections ,also we can see here the base RVA of the IAT ,if we took a look at the libraries it loads ,we will find that it loads 2 dynamic libraries ,which are Kernel32 and msvcrt ,it loads from Kernel32 the following functions :
AddAtomA() ,FindAtomA() ,GetAtomNameA() ,SetUnhandledExceptionFilter() ,ExitProcess()
and from msvcrt the following functions :
__getmainargs() , __p__environ() ,__p__fmode() , __set_app_type() ,_cexit() , _iob() , _onexit() , _setmode() , abort() ,atexit() , fflush() ,fprintf() , free() ,malloc() ,printf() ,scanf() ,signal()

So ,as shown ,we can see all the functions used in our executable by system's kernel ,so that it operates the way it should .

Here ,we must notice the real entry point of our binary image ,let's just take the first 4 instructions :

SUB ESP, 00000008H
MOV [ESP], 00000001H

So ,now let's start the packing process ,we are going to use UPX official free tool ,to pack our executable ,and we will output the file as upx_calc.exe ,and packing it using this simple command :

Now ,we have a packed binary ,let's try to load it into PEid again ,and see the difference .

We can notice various things here ,first ,it didn't recognize what compiler is used to compile this binary file ,also the OEP has changed .

If we saw the sections of our new binary ,we will notice that the sections have been packed and renamed to UPX0 ,UPX1 ,UPX2 blocks .

 Also ,the IAT has been changed ,now ,it loads the following functions from Kernel32 library :
LoadLibraryA() ,GetProcAddress() ,ExitProcess() ,VirtualFree() ,VirtualAlloc() ,VirtualProtect()

msvcrt library changed to only load the _iob() function .

So ,we can notice here that a lot of stuff changed compared to our real executable ,now let's start unpacking it manually using the lovely immunity debugger .

First ,we launch our upx_calc.exe binary file from within the debugger .

As we can see here ,the OEP is not the same as our old unpacked executable file .
Starting the unpacking routine ,we notice that the very first byte here is a PUSHAD byte ,which as mentioned before ,saves all current GPR's status as well as flags into stack ,stepping over this instruction let us see the stack pointer decremented by the size of the pushed pointers .

From the push instruction on ,we can see the unpacking routines ,and how compressed sections are being decoded and written into the memory ,i won't discuss this algorithm ,as there are a lot of resources on the internet ,and it's out of this article main topic ,so i will concentrate on unpacking the executable only .

Now ,let's find the POPAD instruction ( since it used PUSHAD to save registers ) ,it's opcode is 0x61 .

As we can see here ,after POPAD ,all registers will return to their default values as when we launched the application ,stepping over ,the LEA copies pointer to the address RVA 0xFF44 of the stack to EAX ,so now EAX points to the same as this stack pointer points to ( full physical memory address 0x22FF44 ) ,stepping over again ,we can see this little routine :

JNZ SHORT upx_calc.0040794A

 This routine ,just fills the stack with zeros ,as long as the Zero flag isn't set ( which means that the pointer EAX is not yet equal to pointer ESP ) ,and we already know ( because of the previous LEA instruction ) that this routine will stop when the stack is filled with 0x80 * 4 bytes of zero's ,after this routine ,0x80 bytes will be removed from the stack ( the zero's ) ,and the stack will point to 0xFF44 - 0x80 .

Now ,if we stepped into the JMP 00401220 instruction ,we will land on our real old executable OEP !

All we have to do now ,is just start the application ,and it should start our normal execution flow ,also we can from that address ( the real OEP ) ,dump the data to an external file ,and then use Import Reconstructor to fix the IAT inside the dumped binary image ,so that way ,the dumped binary is our real ,unpacked application ,Voilà .

Today's article ,was a great example ,to start your way into manually unpacking and/or tracing packers' compressing algorithms ,it's also a prerequisite article for the next articles which is going to talk about DLL injection and how to get a file-less code execution into the victim's computer .


References :
[-] UPX Wikipedia :
[-] Security Xploded site articles :

No comments:

Post a Comment