This tutorial is not exact solution for unpacking obsidium, instead it will just discuss some protection features and new implemented tricks in this version of obsidium. The new version of obsidium, 1.3.0.4 one, is pretty much the same as 1.2.5.0 one. One new trick is implemented, more junk code is added and that would be it.
First four tricks are described in previous obsidium tutorial, so here only goes explanation for CreateToolhelp32Snapshot.
This API creates snapshot of all running processes. Than, that snapshot is examned by Process32First and Process32Next APIs. In our target, we place breakpoint at the end of this API and we break there. EAX returns handle of snapshot, in my case EAX = 0000003C which is handle of snapshot. But main part is just comming. Now, that snapshot is examned with Process32Next and Process32First APIs. Process32First examnes only first process in snapshot, while second API examnes all others.
These two APIs returns some very usefull informations about specific process. Part of that information is name of process, it's PID and it's "father" PID. "Father" PID is PID of process that started some process. So protector can see does OLLYDBG.EXE is running in background. But that is not some smart trick since we can just rename olly and obsidium will not do that.
Obsidium will find it's own process and it will take PID of it's "father", the process who started it. Then it will examne all processes to find process with "father" PID, and it will check something. I din't trace to see what and how obsidium checking. Code is full of junk and I didn't want to waste time. I assumed that obsidium checking is "father" process explorer.exe, but I think that it is not exactly that. Nevermind, here is simple trick how to avoid olly detection:
After breaking at CreateToolhelp32Snapshot , I place bp on end of Process32Next API. End because obsidium checks couple bytes for breakpoints, and it also emulate first couple opcodes. Then I check stack window:
0012FD04 009074A1 RETURN to 009074A1
0012FD08 0000003C
0012FD0C 0012FD18
Note that in your case, these values will probably be different, but that is not important. Third line from the top shows address of buffer for process info 0012FD18. Now I go there in dump window:
We can see what process now this API examnes - System. It's PID is between [] while it's "father" PID is in (). This process doesn't have father PID. I'm searching for explorer.exe, ollydbg.exe and target.exe. Kep pressing F9:
Those PID's will always be different, but you can see that Olly is "father" for target.exe. Obsidium will see that too. But, we can simply change that return value in buffer. We change "father" PID from Olly's to eplorer.exe:
And now obsidium will not notice that it's been debugged. It will search again all processes for explorer.exe and it will see that explorer.exe is good buddy.
Now we can run file under olly, but obsidium created one thread that check for debugger presence all the time. We can simply suspend that thread in olly and then run target normally under olly. Red thread in thread window is our main process, so you can suspend second one (first in this example):
Threads
Ident Entry Data block Last error Status Priority User time System time
000001D0 7C810856 7FFDB000 ERROR_SUCCESS (000 Active 32 - 2 0.0000 s 0.0000 s
000004EC 00408000 7FFDD000 ERROR_SUCCESS (000 Active 32 + 0 0.0000 s 0.0000 s
Threads
Ident Entry Data block Last error Status Priority User time System time
000001D0 7C810856 7FFDB000 ERROR_SUCCESS (000 Suspended 32 - 2 0.0000 s 0.0000 s
000004EC 00408000 7FFDD000 ERROR_SUCCESS (000 Active 32 + 0 0.0000 s 0.0000 s
2. OEP
OEP can be found like in previous tutorial, but obsidium code it to much filled with crappy opcodes. Since my target is Delphi one, first import that it uses is GetModuleHandleA. I placed bp on the end of that API,stopped there and returned to code:
Ok, I didn't packed this file with stolen code option so we know OEP bytes. Problem in this file are imports and fact that whole code is relocated to this 003B0000 block. Maybe this can be prevented during obsidium runtime execution, but I have no will to trace trough obsidium junk code. Instead I will go around this, making weird dump. But I will leave that as a last thing. First imports.
3. Imports
Imports can be found like in previous tutorial, but script needs some changes because Delphi uses JMP DWORD[IMPORT] which affects stack diffrent. Also , obsidum has lot of junk there so I needed to clean it somehow (I manually patched lot of those small jumps). From previous tutorial (obsidium 1.2.5.0) I sow how obsidium handles imports. Obsidium has some internal table that consist from two parts (three, third was DLL names but they are erased after unpacking):
First part are two DWORDS, first one is base of DLL and second is FFFFFFFF flag which probably means that DLL is loaded in memory. If that flag is 0, it means that API which is called is Obsidium protection API.
Second is decrypted imports. It consinst from 1. WORD value, which determs type of protection, 2. WORD value and 3. DWORD value. Everything depends about 1. WORD value. Let's call that value "import code". There are couple such codes types:
- "code 2" type:
"Code 2" means that import is stored as first character of import and import CRC32 hash, information in table looks like this:
code number - first char - CRC32 hash
02 00 47 00 A0 F7 BF 08
After decrypting, that import becomes "code 4" import, where code number is changed to 4, first char info is erased and hash is replaced with xor-ed import with value that it seams to be some unique constant for target. That is probably for speeding up execution. In executables who uses imports via CALL -> JMP DWORD[API] combination, such as ASM, Borland C++, Delphi, etc. compiled programs, CALL is then replaced with direct jump to import CALL API. For MSVC++ and similar compilers, who use CALL DWORD[API], that is not case.
- "code 4" type:
It seams that these imports are already used ones. Some targets doesn't have these ones by default because I checked every single import in some of them, but I notced that there are such values in their obsidium table. Question stays.
- "code 1" type:
code number - ? - ?
01 00 00 00 11 00 00 00
Code number is 1, first char is zero, hash is not hash. I didn't tried to find what those values means since this import can be retrieved similar to "type 2". Also, this type becames then type 4 too.
- "code 80" type:
Something similar to "code 1".
code number - ? - ?
80 00 00 00 04 00 00 00
This type goes in little longer way. It returns to main image where emulation is performed and then it jumps to import. Again, import can be retrieved at one point, but this import doesn't becomes type 4 after.
- "code 40" type:
code number - ? - ?
40 00 00 00 03 00 00 00
This one doesn't seem to be emulated or obfuscated. ExitProcess is usually one of this type. Little harder for script but probably there are no many these ones.
- "code 10" type:
code number - ? - CRC32 hash
10 00 00 00 C2 2E C7 4E
This is internal Obsidium API. There are about 10 such API's whic are used to check registration information or trial one. Information about these API's can be found in obsidium help file.
00905FBA 3D C22EC74E CMP EAX,4EC72EC2 -> isRegistered
00905FCE 3D A1A0C163 CMP EAX,63C1A0A1 -> getRegInfo
00905FE1 3D 09586CFC CMP EAX,FC6C5809 -> ??? maybe setExternalKey
00905FF6 3D 8019A9B7 CMP EAX,B7A91980 -> getSystemID
0090600D 3D B4D93687 CMP EAX,8736D9B4 -> setKeyfile
00906020 3D 261923D3 CMP EAX,D3231926 -> getTrialDays
00906037 3D 5F9EFDB8 CMP EAX,B8FD9E5F -> getTrialRuns
0090604A 3D 925D522F CMP EAX,2F525D92 -> getLicenseExpiration
00906060 3D E4234726 CMP EAX,264723E4 -> setShortKey
00906072 3D 057EC561 CMP EAX,61C57E05 -> getCustomValue
- rest of possible imports
I noticed some code 8 type. Don't know what is that.
Below code snippet is taken from previous obsidium tutorial (example target) and shows how imports type 1 and 2 can be found using script. It is indentical in both versions , only 1.3.0.4 one have tons of junk between "real" code (and that is reason why I didn't show that snippet).
Before trying script or manually tracing, set access on image to "full access". Ok, after writting script here is partially found IAT, it misses about 20 imports:
It looks like 7 of them are still unknown but searching all intermodular calls, I didn't find any missing one. Maybe if I check jumps !?! Hmm, no, everything looks fine. Those leftovers are probably just space between thunks. I will not use ImpREC now, because it cannot get imports (image is relocated). Ok, now dumping problem.
4. Dumping
Buah, how to dump now, when target is totally crippled? This is how target looks in memory:
Our target is located at 00400000 while code is relocated in 003B0000 to prevent dumping. So I dumped my target with lor PE, then I dumped whole block from 003B0000 to 00400000. Difference from 003B0000 to 00400000 is 50000. Then I added to dump 50000 bytes, but at the beggining of file! Now, PE header in dump is at 50000 offset and dump is totally invalid. At the beggining of file, in that 0-50000 range, I paste that dumped block from 003B0000-00400000. Now, in my dump is all that I need, but everything is screwed. Ok, time to fix dump.
I moved header from 50000 to 0 offset (simply copying 400 bytes from 50000 and paste it to 0 offset). Now dump has header (old one at 50000 can be erased - filled with zeros), but it's invalid. Using LordPE, of maybe better CFF Explorer, I need to shift all values up for 50000 bytes. These are values that I set in LordPE:
Notice that I set file to have only 2 sections. Rest of them will not be erased, I will just expand those two. Sections are set like this, again LordPE is used:
First section:
Name = CODE
VOffset = 1000
VSize = 51000
ROffset = 1000
RSize = 51000
Flags = E00000E0
Second section
Name = .rsrc
VOffset = 52000
VSize = 12000
ROffset = 52000
RSize = 12000
Flags = E00000E0
Sections are on the place now, but resources, imports and TLS data needs to be corrected. TLS and resources I changed with CFF (but it can be done with LordPE too):
TLS table:
RVA = 00057000 (it was 7000, I added 50000)
Resurce:
RVA = 00052000 (shifted up for 50000)
Resources can be viewed clicking on "..." button by the resurce info. My first two entries are incorrect. I fix them to:
first one (which shows as bitamp when fixed):
RVA = 00052910
Offset = 00052910
second (icon)
RVA = 000520C0
Offset = 000520C0
Now is everything almost fixed. Imports are only that's left. In LordPE, I set ImportTable RVA and Size to 00000000. Now I can load it in olly without problem.
5. Rebuilding import table
What is wrong with imports? We don't have IAT, DLLs are not loaded in memory, but jumps point to some pointers/thunks who are correct pointers to imports if DLLs are loaded. So if I load DLLs in memory, this dump will work on my machine. If I load DLLs , I could then use ImpREC to get imports (since pointers now are in main image an ImpREC can find thm).
How to load DLLs? Simply, by injecting two lines of code that will push DLL name and then call LoadLibraryA API. And that for all DLLs. What DLLs I need? When I'm at OEP of my unpacked (not yet dumped , or you can just attach to target if you don't want to search OEP again) target, I check modules window:
Some of those DLLs are not needed, but nevermind. Now I find some empty place in my dump, I write in that DLL names, then I just inject three lines for every DLL:
After doing that and executing that code, all DLLs are loaded and now I use ImpREC. Dump is fixed now.
6. The end
That is all. Btw, obsidium has runtime encryption which is not present in this file. That protection can be defeated just like import one. While unpacking Obsidium 1.3.0.37 itself, I just wrote script for olly that found all encryption calls, executed them to decrypt code, then patched them. It took some time to make nice dump, but it can be done. And that would be the end of this tutorial.
Greets to everybody on BIW, ARTEAM, SnD, CRACKMES.DE, ... and to everybody who reads thisstuff.