Unpacking UPX
 
Theory

What is UPX?

UPX is a portable, extendable, high-performance executable packer for several different executable formats. It achieves an excellent compression ratio and offers **very** fast decompression. Your executables suffer no memory overhead or other drawbacks for most of the formats supported.

And for all reversers out there, it includes a nice feature :)

All UPX supported file formats can be unpacked using the -d switch, eg.
upx -d yourfile.exe will uncompress the file you've just compressed
.

So, before trying anything else, try to unpack it with the -d switch. If that doesn't work...read on :)

Every packer needs to unpack the wrapped program in memory, and this works in the following way :
A (small) chunk of code is decrypted and placed on the right position in memory by the loader. This process continues until the entire program is unpacked. Next, the loader jumps to the entrypoint of the original program (before it was packed). At this moment, the program is fully unpacked and ready to be dumped from memory to disk :)

Let's dig in!

In the next part I'll use Lockless crackme by bishop( www.crackmes.cjb.net). It's packed with UPX and scrambled

Tracing the loader
TOOLS : SoftIce, Procdump, FileScanner

If we examine the packed file (with a Fileanalyser :), we see only 4 functions in the ImportTable. Among them GetProcAddress, a very useful function. Loadlibrary, or Getmodulehandle can do the trick too :)

We can use it to break. Put a 'bpx GetProcAdress' in SoftIce.

Now what are we looking for? We need to find the point in the code where the entire program is unpacked, and when the loader jumps to the OEP (Original Entry Point).
How can we find this OEP? Well this depends on the packer and version. We want to know an address in the 40xxxx range. This jump to the OEP can happen in various ways eg.a 'Call eax', a ret, or a jmp.

Let's find out ! Run the packed program...

We land here :

:004209C9
:004209CB
:004209CC
:004209CE
:004209D0
:004209D2
:004209D3
:004209D4
:004209D5
:004209D6
:004209D7
:004209DD
:004209DF



:004209E1
:004209E3
:004209E6
:004209E8
:004209EE
:004209EF

8A07
47
08C0
74DC
89F9
57
48
F2
AE
55
FF96640A0200
09C0
7407



8903
83C304
EBE1
FF96680A0200
61
E90B65FEFF

mov al, byte ptr [edi]
inc ed
or al, al
je 004209AC
mov ecx, edi
push edi
dec eax
repnz
scasb
push ebp
call dword ptr [esi+00020A64]
or eax, eax
je 004209E8



mov dword ptr [ebx], eax
add ebx, 00000004
jmp 004209C9
call dword ptr [esi+00020A68]
popad
jmp 00406EFF

- - -^ - - -


This is the

unpacking

routine...



=> We land here!
=>When the entire
program is unpacked
we take this jump.
- - - v - - -




restore Registers
=> Jump to OEP

Dumping the program


When you arrive at 004209EF (dont step there with F10! type 'g 4209E8' , trust me you'll need your fingers again in the future :))
Type 'a' in SoftIce. Now you can change the current opcodes.
Type 'jmp eip' and press enter twice.

Now the program is in an endless loop, and most important, just freshly unpacked, and helpless :)
Press F5 and load Procdump

Search for the crackme in the Process list, right-click it, and choose full dump. Save the file, but don't run it yet.


Why not? The OEP of the program is still set somewhere in an UPX section (Entry point of loader). That's not neccessary anymore, cuz the program is already unpacked :) Got it?

All we have to do is change the OEP to the entry point of the original program. (Which we found already : 406EFF)

Fire up your favorite PEeditor (or use the one in Procdump) and change the Entry Point to :

OEP - Image Base = 406EFF - 400000 = 6EFF

Put the value 6EFF as Entry point. Save it, and run the program...

It works !

There is still a lot to do, optimizing PE header, removing the UPX related sections,...but that's not necessary for a crackme :)
Ok, now you have an uppacked crackme, ready to solve!
Enjoy :)

Detten
Detn@hotmail.com

Back to tutorials

www.biw-reversing.cjb.net