I didn't unpack anything for a while due to my coledge obligations, but today I gave on examne and I'm in good mood. Obsidium is pretty good protector that comes from chinese author. I didn't notice any apps packed with it and I don't know why, since it has some pretty cool options. In this tutorial I will try 1.2.5.0 version which is older one, but it's good as starting point. I think that this unpackme doesn't have all protection options enabled since I know that obsidium has option to relocate whole image. That is probably only possible with Borland applications.
Windows XP; OllyDbg; ImpREC; LordPE; some hex editor; PEiD is not important but if you have good external database, at least it can detect correct version of packer.
Obsidium has some cool anti-debug trickcs:
- CheckRemoteDebuggerPresent; this API detects debugger on XP machines,
- UnhandledExceptionFilter; trick to crush application which runs under debugger,
- FindWindowA; it finds olly window class "OLLYDBG",
- IsDebuggerPresent; detects debugger,
- threads; obsidium runs one or two threads that uses previous tricks,
- stolen OEP code; it steal couple bytes from OEP,
- cool import protection.
Also, obsidum code is full of junk opcodes like short jumps that makes tracing very annoying. That is actually one thing that I realy hate. Obsidium doesn't use GetProcAddress to find imports, instead it have custom procedure that find imports. It also checks for breakpoints at API's and emulate them partialy, so we need to place bp's on end of API's.
Anti-debug tricks, CheckRemoteDebuggerPresent, FindWindowA and IsDebuggerPresent, can be bypassed simply by placing bp's at the end of those API's and then seting return value EAX=0.
UnhandledExceptionFilter is used to handle some exceptions that protector generate on purpose. Problem is that if we are in debugger, application expects that debugger will take control instead system debugger (Dr. Watson). We can force system to think that application is not running within debugger. This is UnhandledExceptionFilter function and below shows how to trick it:
PUSH 248 <------------------------- UnhandledExceptionFilter function starts here.
PUSH kernel32.7C8635E0
CALL kernel32.7C8024CB
MOV EAX,DWORD PTR DS:[7C8836CC]
MOV DWORD PTR SS:[EBP-1C],EAX
MOV EBX,DWORD PTR SS:[EBP+8]
MOV DWORD PTR SS:[EBP-178],EBX
MOV DWORD PTR SS:[EBP-148],4
XOR EDI,EDI
MOV DWORD PTR SS:[EBP-13C],EDI
MOV DWORD PTR SS:[EBP-16C],EDI
MOV EAX,DWORD PTR DS:[EBX]
TEST BYTE PTR DS:[EAX+4],10
JE SHORT kernel32.7C862BD4
PUSH DWORD PTR DS:[EAX]
PUSH -1
CALL DWORD PTR DS:[<&ntdll.NtTerminatePr>; ntdll.ZwTerminateProcess
MOV EAX,DWORD PTR DS:[EBX]
MOV ESI,C0000005
CMP DWORD PTR DS:[EAX],ESI
JNZ SHORT kernel32.7C862BF9
CMP DWORD PTR DS:[EAX+14],1
JNZ SHORT kernel32.7C862BF9
PUSH DWORD PTR DS:[EAX+18]
CALL kernel32.7C862874
CMP EAX,-1
JNZ SHORT kernel32.7C862BF9
OR EAX,EAX
JMP kernel32.7C863458
MOV DWORD PTR SS:[EBP-124],EDI
PUSH EDI
PUSH 4
LEA EAX,DWORD PTR SS:[EBP-124]
PUSH EAX
PUSH 7
CALL kernel32.GetCurrentProcess <------------- Here is trick! This API returns FFFFFFFF. Change EAX to 0.
PUSH EAX
CALL DWORD PTR DS:[<&ntdll.NtQueryInform>; ntdll.ZwQueryInformationProcess
TEST EAX,EAX
...
...
2. OEP and stolen code
While writing this text, I didn't find some genereic way to find OEP that would be suitable for writing script. Instead that, I traced and experimented with code, exceptions , breakpoints. I found where import jumps are allocated, conclusion is that when crackme is unpacked, it will try to use some import so I just placed memory bp on whole import block. Crackme is small so it is not problem to find starting point.
But let's go in other direction. I placed breakpoints on all checks to kill anti tricks. When new thread started, I took a look in log window. Last occured exception is interesting:
Log data
Address Message
00415022 Access violation when reading [00000000]
0041EFA5 Access violation when reading [FFFFFFFF]
77DD0000 Module C:WINDOWSsystem32advapi32.dll
00390295 Access violation when reading [FFFFFFFF]
7C85994E Breakpoint at kernel32.7C85994E
7C901230 INT3 command at ntdll.DbgBreakPoint
Access violation when executing [00000000]
7C862C10 Breakpoint at kernel32.7C862C10
003907E8 Illegal instruction
00390854 Integer division by zero
77D6F3DC Breakpoint at USER32.77D6F3DC
Access violation when executing [00000000]
7C862C10 Breakpoint at kernel32.7C862C10
00395F82 Integer division by zero
00395F84 Access violation when reading [00000000]
0039655D Access violation when reading [8003F41E]
0039654A Integer division by zero
0039654C Access violation when reading [00000000]
0039654F Access violation when reading [FFFFFFFF]
0039654D Access violation when reading [FFFFFFFF]
0039655D Access violation when reading [8003F418]
00396550 Illegal instruction
003965BC Integer division by zero
7C812E10 Breakpoint at kernel32.7C812E10
00396555 INT3 command at 00396555
00395041 Integer division by zero
00395041 Integer division by zero
0039520A Integer division by zero
00395041 Integer division by zero
0039520A Integer division by zero
0039520A Integer division by zero
00415DA1 Illegal instruction
00415F9B Integer division by zero <---------------- This one.
7C810856 New thread with ID 00000304 created
7C85994E Breakpoint at kernel32.7C85994E
Last exception is in main image (last section, protectors one) and it is "Integer division by zero". So I done it all again, only this time I unchecked "Intiger divison by 0" in olly options, so I could break on that exception. After couple ones, here is last one:
By this time, crackme code section is already unpacked. I could place memory breakpoint on it and I would land on "false oep". First, this crackme has code section at 404000. Second, Obsidium ALWAYS JUMPS ON FALSE OEP. Even if it doesn't steal bytes, it jumps couple opcodes further (I've been little examning obsidium work).
If we check stack, we can see where is SEH handler that will handle exception:
0012FF94 0012FFE0 Pointer to next SEH record
0012FF98 00415FB3 SE handler
That is code below our exception, so we can place bp there 00415FB3. We came to RETN that throw us to ntdll.dll:
00416009 C3 RETN
That is Ok, let's enter in and then place memory breakpoint on last section to return to obsidium code.
004161EB E8 67000000 CALL UnPackMe.00416257 <------------- We returning here.
Tracing in we find some decryptor loop which ends here:
Skipp all calls before JMP EDI (some decrypting is performed there) and to it (be aware of possible breakpoin checks if you place bp on JMP EDI). That JMP EDI leads to part which throws you at false oep. Trace through that code and it will transform to:
Problem is that OEP should be at 404000. That couple instructions are junk now, and they where probably obfuscated trough obsidium layer. But in this crackme, those few bytes are not important and file will work without it, so just leave it like that. So, make dump now.
3. Imports
Ah, imports are realy pain in the ass. If you check where calls lead, you will see this:
Import call goes in some obsidium procedure. There, obsidim doesn't use GetProcAddress. Intead, import is saved as
- dll image base (handle),
- some flag (FFFFFFFF is good one),
- import code (which is 2,4,1,80, or 40),
- first character of import,
- CRC32 hash.
Obsidium will get info which dll program needs, then it will do some internal checks (is this import already used, is it emulated whole or partially, etc...), then it will search in that DLL export (which is our import) which first character corespond with one in "obsidium import info" table, then it will search for such import which CRC32 hash is equal to one in table. When it finds it, then it will do some emulation and finaly jump to it.
It is very small. I placed breakpoints on [1], [2] and [3] lines in obsidium code. Then I wrote small script that will find one import call, place eip=that_line, run, check if DLL is OK [1] , check if "import code" is 2 [2], and in that case it will break on [3]. There eax holds correct import, now my script will take that API, place it in IAT, restore stack to default value and go searching for next one.
Basicly, I'm just using obsidium import algorithm against itself. Better would be, ofcourse, to code some app or plugin that could just do this by itself. After using script, IAT is:
Three imports are not repaired because they are not "code 2" type. I could write new script for those types, or rewrite this one, but that would first need examning obsidium code again (I'm little tired now) , so I will just try to fix it manually.
If I check "all intermodular calls", I see that only one call is bad:
I traced that call, it is "code 40" type which then lead to ExitProcess:
0039562E FFD0 CALL EAX ; kernel32.ExitProcess
When I found that import, I repaired dump and it worked.
4. Final words
Altough target works, job is not completed. Stolen OEP code can be easily restored if target is not compiled in ASM since obsidium steals only few opcodes. Imports that are not used in target need to be examned.
Obsidium has some more tricks like relocating image and little different import protection, but I will maybe onother tutorial on obsidium. It jus needs little more researching.
I hope that this tutorial will be of some use to somebody. Scripts that I used are included in archive, OEP one should work one evry Obsidium 1.2.5.0 target, while IAT one needs to be used as described in this tutorial. I'm not sure will my dump work on other machines, this was simply first touch on this good protector.
Untill next tutorial, I send regards to all great people on BIW, ARTEAM, SND, CRACKMES.DE, etc...
PS: I don't know which OS you used for unpacking, but the dump works great on WinXP SP2 (UK).