Obsidium 1.3.0.4 - unpacking

Wednesday, June 14 2006 @ 09:49 AM CEST

Contributed by: haggar

Level : advanced

------------------------------------------
Obsidium 1.3.0.4 - unpacking
------------------------------------------



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.







1. Tools and stuff

Tools that you will need:

- OllyDbg (with OllyScript plugin, one by SHaG or Epsylon3)
- ImpREC
- LordPE
- CFF Explorer
- some hex editor
- DAMN hash calculator can be usefull
- target and stuff is here http://www.reversing.be/binaries/articles/20060614094714582.rar


Anti debug tricks that Obsidium uses:

- UnhandledExceptionFilter
- FindWindowA
- CheckRemoteDebuggerPresent
- IsDebuggerPresent
- CreateToolhelp32Snapshot


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:

0012FD18 28 01 00 00 00 00 00 00[04 00 00 00]00 00 00 00 (...............
0012FD28 00 00 00 00 46 00 00 00(00 00 00 00)08 00 00 00 ....F...........
0012FD38 00 00 00 00 53 79 73 74 65 6D 00 20 50 72 6F 63 ....System. Proc
0012FD48 65 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 ess]............

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:

0012FD18 28 01 00 00 00 00 00 00 E0 04 00 00 00 00 00 00 (...............
0012FD28 00 00 00 00 13 00 00 00 C8 04 00 00 08 00 00 00 ................
0012FD38 00 00 00 00 65 78 70 6C 6F 72 65 72 2E 65 78 65 ....explorer.exe
0012FD48 00 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 .ss]............

0012FD18 28 01 00 00 00 00 00 00 60 04 00 00 00 00 00 00 (.......`.......
0012FD28 00 00 00 00 05 00 00 00 E0 04 00 00 08 00 00 00 ................
0012FD38 00 00 00 00 4F 4C 4C 59 44 42 47 2E 45 58 45 00 ....OLLYDBG.EXE.
0012FD48 00 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 .ss]............

0012FD18 28 01 00 00 00 00 00 00 28 01 00 00 00 00 00 00 (.......(.......
0012FD28 00 00 00 00 02 00 00 00(60 04 00 00)08 00 00 00 ........`.......
0012FD38 00 00 00 00 74 61 72 67 65 74 2E 65 78 65 00 00 ....target.exe..
0012FD48 00 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 .ss]............

We can see: name of process - PID - father's PID

explorer.exe - 4E0 - 4C8
OLLYDBG.EXE - 460 - 4E0
target.exe - 128 - 460

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:

0012FD18 28 01 00 00 00 00 00 00 28 01 00 00 00 00 00 00 (.......(.......
0012FD28 00 00 00 00 02 00 00 00(E0 04 00 00)08 00 00 00 ................
0012FD38 00 00 00 00 74 61 72 67 65 74 2E 65 78 65 00 00 ....target.exe..
0012FD48 00 73 73 5D 00 00 00 00 00 00 00 00 00 00 00 00 .ss]............

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:

003B45BC BA 98803B00 MOV EDX,3B8098
003B45C1 52 PUSH EDX
003B45C2 8905 B4943B00 MOV DWORD PTR DS:[3B94B4],EAX
003B45C8 8942 04 MOV DWORD PTR DS:[EDX+4],EAX
003B45CB E8 98FFFFFF CALL 003B4568
003B45D0 5A POP EDX
003B45D1 58 POP EAX
003B45D2 E8 DDE7FFFF CALL 003B2DB4
003B45D7 C3 RETN
003B45D8 55 PUSH EBP
003B45D9 8BEC MOV EBP,ESP
003B45DB 33C0 XOR EAX,EAX
003B45DD 55 PUSH EBP
003B45DE 68 FD453B00 PUSH 3B45FD
003B45E3 64:FF30 PUSH DWORD PTR FS:[EAX]
003B45E6 64:8920 MOV DWORD PTR FS:[EAX],ESP
003B45E9 FF05 B8943B00 INC DWORD PTR DS:[3B94B8]
003B45EF 33C0 XOR EAX,EAX
003B45F1 5A POP EDX
003B45F2 59 POP ECX
003B45F3 59 POP ECX
003B45F4 64:8910 MOV DWORD PTR FS:[EAX],EDX
003B45F7 68 04463B00 PUSH 3B4604
003B45FC C3 RETN
003B45FD ^E9 92E4FFFF JMP 003B2A94
003B4602 ^EB F8 JMP SHORT 003B45FC
003B4604 5D POP EBP
003B4605 C3 RETN
003B4606 8BC0 MOV EAX,EAX
003B4608 832D B8943B00 01 SUB DWORD PTR DS:[3B94B8],1
003B460F C3 RETN
003B4610 -FF25 A8A13B00 JMP DWORD PTR DS:[3BA1A8] <--- Imports.
003B4616 8BC0 MOV EAX,EAX
003B4618 -FF25 A4A13B00 JMP DWORD PTR DS:[3BA1A4]
003B461E 8BC0 MOV EAX,EAX
003B4620 -FF25 A0A13B00 JMP DWORD PTR DS:[3BA1A0]
003B4626 8BC0 MOV EAX,EAX
003B4628 -FF25 9CA13B00 JMP DWORD PTR DS:[3BA19C]
003B462E 8BC0 MOV EAX,EAX


Knowing Delphi apps, first RETN below leads back close to OEP:

003B79D0 55 PUSH EBP <----------------- This is OEP.
003B79D1 8BEC MOV EBP,ESP
003B79D3 83C4 F4 ADD ESP,-0C
003B79D6 53 PUSH EBX
003B79D7 56 PUSH ESI
003B79D8 57 PUSH EDI
003B79D9 B8 98793B00 MOV EAX,3B7998
003B79DE E8 D1CBFFFF CALL 003B45B4
003B79E3 BE CC953B00 MOV ESI,3B95CC <----------- Here I am right now.
003B79E8 BF E4953B00 MOV EDI,3B95E4
003B79ED BB A0953B00 MOV EBX,3B95A0
003B79F2 33C0 XOR EAX,EAX
003B79F4 55 PUSH EBP
003B79F5 68 847C3B00 PUSH 3B7C84
003B79FA 64:FF30 PUSH DWORD PTR FS:[EAX]
003B79FD 64:8920 MOV DWORD PTR FS:[EAX],ESP
003B7A00 A1 80823B00 MOV EAX,DWORD PTR DS:[3B8280]
003B7A05 8B00 MOV EAX,DWORD PTR DS:[EAX]
003B7A07 A3 C8953B00 MOV DWORD PTR DS:[3B95C8],EAX
003B7A0C C703 C0000000 MOV DWORD PTR DS:[EBX],0C0
003B7A12 C743 04 90783B00 MOV DWORD PTR DS:[EBX+4],3B7890
003B7A19 A1 C8953B00 MOV EAX,DWORD PTR DS:[3B95C8]
003B7A1E 8943 10 MOV DWORD PTR DS:[EBX+10],EAX
003B7A21 C743 1C 10000000 MOV DWORD PTR DS:[EBX+1C],10
003B7A28 B8 947C3B00 MOV EAX,3B7C94
003B7A2D 8943 24 MOV DWORD PTR DS:[EBX+24],EAX
003B7A30 68 007F0000 PUSH 7F00
003B7A35 6A 00 PUSH 0
003B7A37 E8 44CCFFFF CALL 003B4680

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).

00394505 MOVZX EDX,CL
00394508 MOVZX EAX,AX
0039450B CALL 00394510
00394510 POP EBX
00394511 MOV EBP,EBX
00394513 MOV EBX,DWORD PTR DS:[EBX-74]
00394516 SUB EBP,0A8A0CB
0039451C CALL 00394521
00394521 ADD DWORD PTR SS:[ESP],0D
00394525 MOV ECX,DWORD PTR SS:[EBP+A8A0BC]
0039452B CALL ECX
0039452D RETN
0039452E MOV ESI,DWORD PTR DS:[EBX+44]
00394531 XOR DL,BYTE PTR SS:[EBP+A8A18C]
00394537 XOR AX,WORD PTR SS:[EBP+A8A18D]
0039453E SHL EDX,3
00394541 MOV ECX,EDX
00394543 SHL EDX,1
00394545 ADD ECX,EDX
00394547 LEA EDI,DWORD PTR DS:[ESI+ECX+4]
0039454B CMP DWORD PTR DS:[EDI+4],0 <--------------------- DLL check.
0039454F JE 00394768
00394555 ADD ESI,DWORD PTR DS:[EDI+14]
00394558 LEA ESI,DWORD PTR DS:[ESI+EAX*8]
0039455B MOVZX EAX,WORD PTR DS:[ESI] <-------------------- Obsidium import table.
0039455E CMP EAX,4 <-------------------------------------- "code 4" check.
00394561 JE 003945FE
00394567 CMP EAX,1 <-------------------------------------- "code 1" check.
0039456A JE SHORT 003945D4
0039456C CMP EAX,80
00394571 JE 00394830
00394577 CMP EAX,40
0039457A JE 0039462A
00394580 MOVZX EAX,WORD PTR DS:[ESI+2]
00394584 PUSH 1
00394586 PUSH EAX
00394587 PUSH 0
00394589 PUSH DWORD PTR DS:[ESI+4]
0039458C PUSH DWORD PTR DS:[EDI]
0039458E CALL DWORD PTR DS:[EBX+50]
00394591 TEST EAX,EAX <----------------------- Here we can retrieve "code 2" import.
00394593 JNZ SHORT 003945E6
....
....
003945D9 JBE SHORT 003945DF
003945DB PUSH 0
003945DD PUSH DWORD PTR DS:[EDI]
003945DF CALL DWORD PTR DS:[EBX+50]
003945E2 TEST EAX,EAX <------------------------- Here we can retrieve "code 1" import.
003945E4 JE SHORT 003945B5
003945E6 MOV EDX,EAX




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:

003BA0B4 8A 18 91 7C ED 10 90 7C 05 10 90 7C A1 9F 80 7C ...|...|...|...|
003BA0C4 14 9B 80 7C 81 9A 80 7C 5D 99 80 7C BD 99 80 7C ...|...|]..|...|
003BA0D4 60 00 3C 00 C7 A0 80 7C AD 9C 80 7C 78 67 90 00 `.<....|...|xg..
003BA0E4 29 C7 80 7C 9C 00 3C 00 05 A4 80 7C EE 1E 80 7C )..|..<....|...|
003BA0F4 57 B3 80 7C 7E D4 80 7C 31 03 91 7C CF 01 3C 00 W..|~..|1..|..<.
003BA104 F0 00 3C 00 FC 00 3C 00 08 01 3C 00 14 01 3C 00 ..<...<...<...<.
003BA114 50 F8 81 7C 40 7A 93 7C 38 01 3C 00 E1 EA 81 7C P..|@z.|8.<....|
003BA124 A9 2C 81 7C 5C 01 3C 00 69 10 81 7C 74 01 3C 00 ,.|.<.i..|t.<.
003BA134 80 01 3C 00 8C 01 3C 00 46 FA D6 77 98 EC D6 77 ..<...<.F..w...w
003BA144 0B 05 D8 77 FD 01 3C 00 83 78 DD 77 1B 76 DD 77 ...w..<..x.w.v.w
003BA154 F0 6B DD 77 2D 02 3C 00 C4 65 12 77 48 D3 14 77 .k.w-.<..e.wH..w
003BA164 C0 48 12 77 3B 4C 12 77 50 48 12 77 59 4B 12 77 .H.w;L.wPH.wYK.w
003BA174 81 02 3C 00 F5 9B 80 7C 50 97 80 7C BD 99 80 7C ..<....|P..|...|
003BA184 29 B5 80 7C 57 B3 80 7C C9 02 3C 00 78 67 90 00 )..|W..|..<.xg..
003BA194 E1 02 3C 00 51 28 81 7C 05 A4 80 7C 57 B3 80 7C ..<.Q(.|...|W..|
003BA1A4 7E D4 80 7C E6 2B 81 7C 29 03 3C 00 80 AD F3 77 ~..|.+.|).<....w
003BA1B4 41 03 3C 00 64 C0 D4 77 CE 8B D4 77 DC E5 D4 77 A.<.d..w...w...w
003BA1C4 AE E2 D4 77 16 23 D5 77 0B 05 D8 77 98 EC D6 77 ..w.#.w...w...w
003BA1D4 FA E8 D4 77 2E F8 D6 77 75 8F D4 77 45 EA D6 77 ...w...wu..wE..w
003BA1E4 BD BC D4 77 6B DF D4 77 0B 19 D5 77 F5 03 3C 00 ...wk..w...w..<.

After long and painfull, manually tracing, I found some others:

003BA0B4 8A 18 91 7C ED 10 90 7C 05 10 90 7C A1 9F 80 7C ...|...|...|...|
003BA0C4 14 9B 80 7C 81 9A 80 7C 5D 99 80 7C BD 99 80 7C ...|...|]..|...|
003BA0D4 59 B8 80 7C C7 A0 80 7C AD 9C 80 7C E0 C6 80 7C Y..|...|...|...|
003BA0E4 29 C7 80 7C 4F 1D 80 7C 05 A4 80 7C EE 1E 80 7C )..|O..|...|...|
003BA0F4 57 B3 80 7C 7E D4 80 7C 31 03 91 7C 8D 2C 81 7C W..|~..|1..|.,.|
003BA104 66 AA 80 7C A2 CA 81 7C 9F 0F 81 7C A6 0D 81 7C f..|...|...|...|
003BA114 50 F8 81 7C 40 7A 93 7C 0E 18 80 7C E1 EA 81 7C P..|@z.|...|...|
003BA124 A9 2C 81 7C 8F 0C 81 7C 69 10 81 7C 24 1A 80 7C ,.|...|i..|$..|
003BA134 77 9B 80 7C 8C 01 3C 00 46 FA D6 77 98 EC D6 77 w..|..<.F..w...w
003BA144 0B 05 D8 77 FD 01 3C 00 83 78 DD 77 1B 76 DD 77 ...w..<..x.w.v.w
003BA154 F0 6B DD 77 2D 02 3C 00 C4 65 12 77 48 D3 14 77 .k.w-.<..e.wH..w
003BA164 C0 48 12 77 3B 4C 12 77 50 48 12 77 59 4B 12 77 .H.w;L.wPH.wYK.w
003BA174 81 02 3C 00 F5 9B 80 7C 50 97 80 7C BD 99 80 7C ..<....|P..|...|
003BA184 29 B5 80 7C 57 B3 80 7C C9 02 3C 00 E0 C6 80 7C )..|W..|..<....|
003BA194 59 B8 80 7C 51 28 81 7C 05 A4 80 7C 57 B3 80 7C Y..|Q(.|...|W..|
003BA1A4 7E D4 80 7C E6 2B 81 7C 29 03 3C 00 80 AD F3 77 ~..|.+.|).<....w
003BA1B4 41 03 3C 00 64 C0 D4 77 CE 8B D4 77 DC E5 D4 77 A.<.d..w...w...w
003BA1C4 AE E2 D4 77 16 23 D5 77 0B 05 D8 77 98 EC D6 77 ..w.#.w...w...w
003BA1D4 FA E8 D4 77 2E F8 D6 77 75 8F D4 77 45 EA D6 77 ...w...wu..wE..w
003BA1E4 BD BC D4 77 6B DF D4 77 0B 19 D5 77 F5 03 3C 00 ...wk..w...w..<.

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:

Memory map
Address Size Owner Section Contains Type Access Initial Mapped as
003B0000 0000E000 Priv RWE RWE
003C0000 00001000 Priv RWE RWE
003D0000 00004000 Priv RW RW
003E0000 00003000 Map R R
00400000 00001000 target PE header Imag RWE RWE
00401000 00001000 target code,imports Imag RWE RWE
00402000 00004000 target .rsrc code,resourc Imag RWE RWE
00406000 00001000 target code,resourc Imag RWE RWE
00407000 00001000 target code,resourc Imag RWE RWE
00408000 0000C000 target SFX,data,res Imag RWE RWE


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:

OEP = 000079D0
ImageBase = 003B0000
ImageSize = 00064000
BaseOfCode = 00001000
BaseOfData = 00001000
NumberOfSections = 0002
NumOfRvaAndSizes = 00000010

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:

Executable modules
Base Size Entry Name File version Path
77DD0000 0009B000 77DD70D4 advapi32 5.1.2600.2180 (x C:WINDOWSsystem32advapi32.dll
77F10000 00046000 77F163CA GDI32 5.1.2600.2180 (x C:WINDOWSsystem32GDI32.dll
7C800000 000F4000 7C80B436 kernel32 5.1.2600.2180 (x C:WINDOWSsystem32kernel32.dll
77C10000 00058000 77C1F2A1 msvcrt 7.0.2600.2180 (x C:WINDOWSsystem32msvcrt.dll
7C900000 000B0000 7C913156 ntdll 5.1.2600.2180 (x C:WINDOWSsystem32ntdll.dll
774E0000 0013C000 774F20C1 ole32 5.1.2600.2180 (x C:WINDOWSsystem32ole32.dll
77120000 0008C000 77121558 oleaut32 5.1.2600.2180 C:WINDOWSsystem32oleaut32.dll
77E70000 00091000 77E76284 RPCRT4 5.1.2600.2180 (x C:WINDOWSsystem32RPCRT4.dll
77FE0000 00011000 77FE2131 Secur32 5.1.2600.2180 (x C:WINDOWSsystem32Secur32.dll
77D40000 00090000 77D50EB9 USER32 5.1.2600.2180 (x C:WINDOWSsystem32USER32.DLL
00400000 00014000 00408000 target 1.0.0.0 D:Obsidium 1.3.0.4target.exe

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:

push offset_to_dll_name (example ASCII "advapi32.dll")
call LoadLibraryA
nop

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.


See you


haggar , 2006






0 comments



http://www.reversing.be/article.php?story=20060614094957880