Contribute  :  Web Resources  :  Past Polls  :  Site Statistics  :  Downloads  :  Forum  
    BiW ReversingThe challenge is yours    
 Welcome to BiW Reversing
 Tuesday, March 31 2020 @ 09:15 PM CEST

Enigma protector 1.02 - manually unpacking

   

TutorialsLevel : intermediate

===========================
Enigma protector 1.02 - manually unpacking
===========================

Enigma protector 1.02 is another PE file protector. It is still in development and this version is a free demo without limitations. As a target for this tutorial I choose the protector itself because it has all options enabled that it has to offer us. Plus, unpacking a file that I didn't previously packed is chalenging because I do not know what the original was looking like. I must say that this protector gave me some problems, I was writing this tutorial (and that means unpacking it) for a couple of days. Tutorial is not hard to understand but since it's not linear (I was restarting target multiple times) , it is not for beginners. Let's say that it is on intermediate level. At the end of the tutorial, I will show how the protected target can be easily inline patched. Well, not so easy , but it can be easier than unpacking it.



1. Tools and stuff

Tools that we need are usual:

- OllyDbg 1.10
- ImpREC
- LordPE
- hex editor
- Windows XP
- target, protector itself (use google) and stuff http://www.reversing.be/binaries/articles/20060111231228879.rar


The protector has few bugs. Main bug is in "Exit on file name option" because every file packed with that option is non-working. Reason is that protector leave to file name exclamanation character " (example test.exe becomes test.exe") so it thinks that every name is invalid. Virus check also can be buggy.



2. OEP and loading DLL

Reaching OEP can be very easy. Just use Olly tracing option and after some time OEP is found. Protector has IsDebuggerPresent check so we must use plugin to hide olly. This way of finding OEP will work if protected file doesn't emulate OEP of other compilers. Altough with little PE header altering Olly can find OEP in those cases too, later we'll see more generic approach for OEP. When we land on OEP, we will see that we are actually on "false" OEP and that some original OEP opcodes are "stolen":

...
...
004F9903 0000 ADD BYTE PTR DS:[EAX],AL
004F9905 0000 ADD BYTE PTR DS:[EAX],AL
004F9907 007494 4F ADD BYTE PTR SS:[ESP+EDX*4+4F],DH
004F990B 009D 560B5927 ADD BYTE PTR SS:[EBP+27590B56],BL
004F9911 BF D30D736B MOV EDI,6B730DD3
004F9916 6A F8 PUSH -8
004F9918 E8 5BCAF0FF CALL Enigma.00406378 ; Real entry point of SFX code
004F991D 8B1D E4303E00 MOV EBX,DWORD PTR DS:[3E30E4]
004F9923 8B03 MOV EAX,DWORD PTR DS:[EBX]
004F9925 E8 E648F9FF CALL Enigma.0048E210
004F992A 8B0D 782E3E00 MOV ECX,DWORD PTR DS:[3E2E78]
004F9930 8B03 MOV EAX,DWORD PTR DS:[EBX]
004F9932 8B15 D0524E00 MOV EDX,DWORD PTR DS:[4E52D0]
004F9938 E8 EB48F9FF CALL Enigma.0048E228
004F993D 8B0D 14333E00 MOV ECX,DWORD PTR DS:[3E3314]
004F9943 8B03 MOV EAX,DWORD PTR DS:[EBX]
004F9945 8B15 50204D00 MOV EDX,DWORD PTR DS:[4D2050]
004F994B E8 D848F9FF CALL Enigma.0048E228
004F9950 8B0D 04333E00 MOV ECX,DWORD PTR DS:[3E3304]
004F9956 8B03 MOV EAX,DWORD PTR DS:[EBX]
004F9958 8B15 1CC34D00 MOV EDX,DWORD PTR DS:[4DC31C]
...
...

"False" OEP is at 004F9918, above code is stolen. Olly tok lot of time to find OEP. In log window we can see some interesting informations:

****************************************************************************
Log data
Address Message
0066C8A4 Access violation when writing to [00000000]
0066C8A4 Access violation when writing to [00000000]
0066C8A4 Access violation when writing to [00000000]
0066C8A4 Access violation when writing to [00000000]
...
loooooot of memory access violation exceptions
...
0066CBEE Access violation when writing to [00000000]
0066CBEE Access violation when writing to [00000000]
0066CD84 Access violation when writing to [00000000]
0066CEBE Access violation when writing to [00000000]
00C24333 INT3 command at 00C24333
00C243E5 Access violation when writing to [00000000]
00C243EC Integer division by zero
...
...
...
00C26C10 Illegal instruction
004F9918 Possible entry point of SFX code
CRC changed, discarding .udd data
Analysing Enigma
3459 heuristical procedures
51 calls to known, 1275 calls to guessed functions
1038 loops, 313 switches
*****************************************************************************


We can see that protector consist from two parts, first is code attached at the end of main exe, and second is dll loader that is extracted in some allocated memory block. We can see that in attached part occurres lot of memory violation exceptions, most of them at same place. Those exceptions slow down program execution in debugger, particulary if you try trace trough it. But in that attached code, exceptios are all memory violations. When dll loader is extracted, first exception ocurred in it is INT3. So we could easily break in loader just by setting exceptions handling in Olly options.

But there is smarter way: Last exception in attached code is at 0066CEBE. I will place bp there , ignore all exceptions, will not use Olly tracing option this time, and restart target. Soon I break on my bp:

0066CEBE 8900 MOV DWORD PTR DS:[EAX],EAX <------------- Here!
0066CEC0 9A 648F0500 0000 CALL FAR 0000:00058F64 ; Far call
0066CEC7 00EB ADD BL,CH
0066CEC9 02C1 ADD AL,CL
0066CECB 90 NOP
0066CECC 58 POP EAX ; 0012FFE0
0066CECD 61 POPAD
0066CECE EB 01 JMP SHORT Enigma.0066CED1

I removed bp and checked stack:

0012FF58 0012FFE0 Pointer to next SEH record
0012FF5C 0066CE84 SE handler <-------------------- Exception handler!
0012FF60 77EB6930 kernel32.77EB6930
0012FF64 00C00000 ASCII "MZP"

In stack you can see that local exception handler is at 0066CE84. That mean, after exception is coused, program will continue from that line. I'm going there:

0066CE84 EB 02 JMP SHORT Enigma.0066CE88
0066CE86 CD 20 INT 20
0066CE88 8B4424 0C MOV EAX,DWORD PTR SS:[ESP+C]
0066CE8C 8380 B8000000 03 ADD DWORD PTR DS:[EAX+B8],3
0066CE93 31C0 XOR EAX,EAX
0066CE95 C3 RETN

Last RETN will throw us to ntdll.dll that handles exceptions but here is conclusion: Since we passed last exception before jumping to loader code, here around must be some jump or RETN that throw us to loader code. Little down I found last instructions that are executed before jumping:

0066CF6E EB 01 JMP SHORT Enigma.0066CF71 <---- It jumps to obfuscated RETN below!
0066CF70 9A C35589E5 FF75 CALL FAR 75FF:E58955C3
0066CF77 0C FF OR AL,0FF
0066CF79 75 08 JNZ SHORT Enigma.0066CF83
0066CF7B E8 46000000 CALL Enigma.0066CFC6
0066CF80 8B7D 08 MOV EDI,DWORD PTR SS:[EBP+8]
...
...

Follow that jump:

0066CF71 C3 RETN <------------------------ It leads to loader!
0066CF72 55 PUSH EBP
0066CF73 89E5 MOV EBP,ESP
0066CF75 FF75 0C PUSH DWORD PTR SS:[EBP+C]
0066CF78 FF75 08 PUSH DWORD PTR SS:[EBP+8]
0066CF7B E8 46000000 CALL Enigma.0066CFC6
...
...

So I placed bp there and executed untill I stopped there. RETN leads to 00C2720C which is OEP of Borland Dll which is protector's loader:

00C2720C 55 PUSH EBP
00C2720D 8BEC MOV EBP,ESP
00C2720F 83C4 C4 ADD ESP,-3C
00C27212 B8 9C70C200 MOV EAX,0C2709C
00C27217 E8 C0E8FDFF CALL 00C05ADC
00C2721C E8 2BCAFDFF CALL 00C03C4C
00C27221 8D40 00 LEA EAX,DWORD PTR DS:[EAX]
00C27224 0000 ADD BYTE PTR DS:[EAX],AL
00C27226 0000 ADD BYTE PTR DS:[EAX],AL
00C27228 0000 ADD BYTE PTR DS:[EAX],AL
00C2722A 0000 ADD BYTE PTR DS:[EAX],AL
00C2722C 0000 ADD BYTE PTR DS:[EAX],AL
00C2722E 0000 ADD BYTE PTR DS:[EAX],AL
00C27230 0000 ADD BYTE PTR DS:[EAX],AL
...
...

We are at the entry point of loader dll. This dll will unpack/unprotect original file in memory, it will do all tricks to prevent debugging and dumping. We can even dump this file to disk and repair it, I will do this later just for fun. Scrolling up to beggining of section you will get address of it's base. Because this dll will probably be loaded on different base on your machine, it is good thing to know it's base because then you can find all addresses relative to it. What we know untill now:

Jump_to_loader_code = 0066CF71
Loader_Image_Base = 00C00000 (on my machine)
Loader_OEP = 00C2720C (on my machine)
Loader_relative_OEP = +2720C





3. Integrity traps

One wery annoying thing is that this protector has many internal integrity checks that detects every normal or memory breakpoint. Also protector kills/corrupts hardware breakpoints and examning loader code is hard because I had to use lot of breakpoints, then delete them to not be found. Protector uses lot of crypto algorithms for checking. After dumping this dll to disc I scaned it with PEiD's Kanal crypto plugin which found next signatures:

BASE64 encoding (used e.g. in e-mails - MIME)
CRC32 precomputed table for byte transform
MD5 transform ("compress") constants
TWOFISH: pregenerated 8x8 Sbox table

Well, this is not some information that will help me in unpacking , but it's interesting to know.

To find where check of dll is performed, I just placed memory bp on access on dll OEP and I stopped here:

$+1FEC9 > 8D40 00 LEA EAX,DWORD PTR DS:[EAX]
$+1FECC > 32C2 XOR AL,DL
$+1FECE > 25 FF000000 AND EAX,0FF
$+1FED3 > 8B0485 0C8CC200 MOV EAX,DWORD PTR DS:[EAX*4+C28C0C]
$+1FEDA > C1EA 08 SHR EDX,8
$+1FEDD > 81E2 FFFFFF00 AND EDX,0FFFFFF
$+1FEE3 > 33C2 XOR EAX,EDX
$+1FEE5 > C3 RETN
$+1FEE6 > 8BC0 MOV EAX,EAX
$+1FEE8 > 55 PUSH EBP
$+1FEE9 > 8BEC MOV EBP,ESP
$+1FEEB > 53 PUSH EBX
$+1FEEC > 56 PUSH ESI
$+1FEED > 57 PUSH EDI
$+1FEEE > 8B55 0C MOV EDX,DWORD PTR SS:[EBP+C]
$+1FEF1 > 8B7D 08 MOV EDI,DWORD PTR SS:[EBP+8]
$+1FEF4 > 85D2 TEST EDX,EDX
$+1FEF6 > 76 21 JBE SHORT 00C1FF19
$+1FEF8 > 83C8 FF OR EAX,FFFFFFFF
$+1FEFB > 8BDA MOV EBX,EDX
$+1FEFD > 4B DEC EBX
$+1FEFE > 85DB TEST EBX,EBX
$+1FF00 > 72 12 JB SHORT 00C1FF14
$+1FF02 > 43 INC EBX
$+1FF03 > 33F6 XOR ESI,ESI
$+1FF05 > 8D143E LEA EDX,DWORD PTR DS:[ESI+EDI]
$+1FF08 > 8A12 MOV DL,BYTE PTR DS:[EDX] <------------ Stopped here!!!
$+1FF0A > 92 XCHG EAX,EDX
$+1FF0B > E8 BCFFFFFF CALL 00C1FECC
$+1FF10 > 46 INC ESI
$+1FF11 > 4B DEC EBX
$+1FF12 >^75 F1 JNZ SHORT 00C1FF05
$+1FF14 > 83F0 FF XOR EAX,FFFFFFFF
$+1FF17 > EB 02 JMP SHORT 00C1FF1B
$+1FF19 > 33C0 XOR EAX,EAX
$+1FF1B > 5F POP EDI
$+1FF1C > 5E POP ESI
$+1FF1D > 5B POP EBX
$+1FF1E > 5D POP EBP
$+1FF1F > C2 0800 RETN 8

That is procedure that produces some hashes. It is used couple times. It's RVA from base of dll is +1FEE8 and that is same in every target. That line, PUSH EBP is called from two places. You can easily find them "finding references to selected command", those two calls are just up at +1FE86 and +1FEAB. But I'm going too much in that direction. Those who like and know crypto can research that.

So, conclusion is that next important address is +1FEE8 or 00C1FEE8 in my case. Protector also has integrity checks for unpacked sections of file. If we make some changes after sections are unpacked, we will get error Message Box saying that virus damaged file. I used memory bp on location where I sow that message is create and found that check is here:

00C1DA0C E8 0F95FEFF CALL 00C06F20
00C1DA11 84C0 TEST AL,AL <---------------------- Check!
00C1DA13 75 35 JNZ SHORT 00C1DA4A <-------------- Good jump!
00C1DA15 8D45 EC LEA EAX,DWORD PTR SS:[EBP-14]
00C1DA18 8B15 B492C200 MOV EDX,DWORD PTR DS:[C292B4]
00C1DA1E 81C2 28040000 ADD EDX,428
00C1DA24 E8 7765FEFF CALL 00C03FA0
00C1DA29 8B45 EC MOV EAX,DWORD PTR SS:[EBP-14]
00C1DA2C 50 PUSH EAX
00C1DA2D 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
00C1DA30 8B15 B492C200 MOV EDX,DWORD PTR DS:[C292B4]
00C1DA36 81C2 28050000 ADD EDX,528
00C1DA3C E8 5F65FEFF CALL 00C03FA0
00C1DA41 8B45 E8 MOV EAX,DWORD PTR SS:[EBP-18]
00C1DA44 5A POP EDX ; 0012FF54
00C1DA45 E8 8E000000 CALL 00C1DAD8
00C1DA4A 83C3 35 ADD EBX,35
00C1DA4D 837B 0D 00 CMP DWORD PTR DS:[EBX+D],0
00C1DA51 ^0F85 6EFFFFFF JNZ 00C1D9C5
00C1DA57 33C0 XOR EAX,EAX
00C1DA59 5A POP EDX ; 0012FF54
00C1DA5A 59 POP ECX ; 0012FF54
00C1DA5B 59 POP ECX ; 0012FF54
00C1DA5C 64:8910 MOV DWORD PTR FS:[EAX],EDX
00C1DA5F 68 79DAC100 PUSH 0C1DA79
00C1DA64 8D45 E8 LEA EAX,DWORD PTR SS:[EBP-18]
00C1DA67 BA 02000000 MOV EDX,2
00C1DA6C E8 EF62FEFF CALL 00C03D60
00C1DA71 C3 RETN

Jump above must be executed if we want to pass that check. It's relative address from base is +1DA11 and that is one more important address to remember. Actually not so much important. That check isn't important for unpacking, it is not even important in inline patching because changes can be made after this check.





4. OEP and stolen bytes

Let we return to OEP problem. For finding OEP we will use previous metnioned procedure that checks integrity in loader. Reason is that after last one check, that code is going straight to OEP (well, not straight but close). First we get to loader OEP (we already sow how to do it). Then place memory bp somewhere to stop in check procedure. We will break there

00C1FEE8 55 PUSH EBP <-------------------- Toggle bp.
00C1FEE9 8BEC MOV EBP,ESP
...
...
00C1FF08 8A12 MOV DL,BYTE PTR DS:[EDX] <---- We break here!
...
...
00C1FF1D 5B POP EBX
00C1FF1E 5D POP EBP
00C1FF1F C2 0800 RETN 8 <---------------------- Hw bp.

Idea is to count how many times will this procedure be executed before program starts. Then we do it again and stop at RETN 8 of last execution. But you must be careful. When you break at C1FF08 when memory is being read, remove that mem bp and place hw bp at RETN 8. Then place toggle bp on start of procedure 0C1FEE8. By switching breakpoint and hw bp, you will avoid detection and crushing (note: delete hw bp too). After 4 times program started. Do it all again and 4. time just trace with F7:

00C1FEB0 A3 D0CFC200 MOV DWORD PTR DS:[C2CFD0],EAX
00C1FEB5 031D D0CFC200 ADD EBX,DWORD PTR DS:[C2CFD0]
00C1FEBB 8BC3 MOV EAX,EBX
00C1FEBD 5B POP EBX
00C1FEBE C3 RETN

Continue with F7:

00C20547 A3 04D0C200 MOV DWORD PTR DS:[C2D004],EAX
00C2054C 8B65 08 MOV ESP,DWORD PTR SS:[EBP+8]
00C2054F 8B45 0C MOV EAX,DWORD PTR SS:[EBP+C]
00C20552 894424 1C MOV DWORD PTR SS:[ESP+1C],EAX
00C20556 61 POPAD
00C20557 A1 04D0C200 MOV EAX,DWORD PTR DS:[C2D004]
00C2055C 50 PUSH EAX
00C2055D A1 00D0C200 MOV EAX,DWORD PTR DS:[C2D000]
00C20562 010424 ADD DWORD PTR SS:[ESP],EAX
00C20565 C3 RETN

Execute RETN and you will be here:

00D39E70 E8 08000000 CALL 00D39E7D
00D39E75 EF OUT DX,EAX ; I/O command
00D39E76 28E2 SUB DL,AH
...
cut
...
00D3B943 51 PUSH ECX
00D3B944 5E POP ESI
00D3B945 5E POP ESI
00D3B946 FEC9 DEC CL
00D3B948 68 18994F00 PUSH 4F9918 <----------------- Address of false OEP!
00D3B94D 60 PUSHAD
00D3B94E 31C0 XOR EAX,EAX <----------------- You can (and should) NOP from here...
00D3B950 B9 D81A0000 MOV ECX,1AD8
00D3B955 BF 709ED300 MOV EDI,0D39E70
00D3B95A F2:AA REPNE STOS BYTE PTR ES:[EDI]
00D3B95C 47 INC EDI
00D3B95D AB STOS DWORD PTR ES:[EDI]
00D3B95E AB STOS DWORD PTR ES:[EDI] <----- ...to here. It will prevent crushing.
00D3B95F 61 POPAD
00D3B960 C3 RETN
00D3B961 0000 ADD BYTE PTR DS:[EAX],AL
00D3B963 0000 ADD BYTE PTR DS:[EAX],AL
00D3B965 0000 ADD BYTE PTR DS:[EAX],AL

This huge junky code holds stolen OEP code. I didn't bother to try find exact bytes because program will work with this block attached to main dump. Code that must be NOP-ed is used to delete all those code with stolen bytes. It must be patched because in main dump, it will try to erase bytes in 00D3xxxx block (it won't be there in dumped file) and that will couse exception. Place bp on RETN, run, execute RETN and there is our (false) OEP:

004F9918 E8 5BCAF0FF CALL Enigma.00406378
004F991D 8B1D E4303E00 MOV EBX,DWORD PTR DS:[3E30E4] ; Enigma.004FEBF0
004F9923 8B03 MOV EAX,DWORD PTR DS:[EBX]
...
...

Now we know how OEP can always be found, also we know that stolen code can be fixed by just adding section. I binary copied stolen bytes and saved it in StolenCode.txt file. I can use it later.





5. Code relocation

Enigma has some "Extra relocations protect" option which redirects whole one section. Check this:

004F9918 E8 5BCAF0FF CALL Enigma.00406378
004F991D 8B1D E4303E00 MOV EBX,DWORD PTR DS:[3E30E4] <--- This is what I'm talking about.
004F9923 8B03 MOV EAX,DWORD PTR DS:[EBX]
...
...

It redirected whole section to some virtual address and changed all opcodes that points to original section. There is easy way to prevent this. Enigma uses VirtualAlloc to allocate that block. When block is allocated, all we need to do is change EAX=003E0000 to original section value and enigma will direct all to that section. How to break on right place? Using memory breakpoints. This line

004F991D 8B1D E4303E00 MOV EBX,DWORD PTR DS:[3E30E4]

needs to be changed. More correct, these four bytes E4303E00. Restart target in olly, go to 004F991D+2=004F991F in dump and place mem bp there on 1 byte. Run Olly and you will break couple times in loader code. Wanted one is here:

00C26B6D 0108 ADD DWORD PTR DS:[EAX],ECX <------ ECX=FFEE6000 what redirects code!
00C26B6F EB 35 JMP SHORT 00C26BA6
00C26B71 8B0D B492C200 MOV ECX,DWORD PTR DS:[C292B4]
00C26B77 8B1D 24D3C200 MOV EBX,DWORD PTR DS:[C2D324]
00C26B7D 2B99 DC000000 SUB EBX,DWORD PTR DS:[ECX+DC]
00C26B83 8B15 B492C200 MOV EDX,DWORD PTR DS:[C292B4]
00C26B89 8B92 E9000000 MOV EDX,DWORD PTR DS:[EDX+E9]
00C26B8F 2BDA SUB EBX,EDX
00C26B91 25 FFFFFF7F AND EAX,7FFFFFFF
00C26B96 0305 24D3C200 ADD EAX,DWORD PTR DS:[C2D324]
00C26B9C 8B0D B492C200 MOV ECX,DWORD PTR DS:[C292B4]
00C26BA2 2BC2 SUB EAX,EDX
00C26BA4 0118 ADD DWORD PTR DS:[EAX],EBX
00C26BA6 8305 28D3C200 04 ADD DWORD PTR DS:[C2D328],4
00C26BAD A1 28D3C200 MOV EAX,DWORD PTR DS:[C2D328]
00C26BB2 8B00 MOV EAX,DWORD PTR DS:[EAX] ; Enigma.004FD0E4
00C26BB4 85C0 TEST EAX,EAX ; Enigma.004F991F
00C26BB6 ^75 88 JNZ SHORT 00C26B40
00C26BB8 33C0 XOR EAX,EAX ; Enigma.004F991F
00C26BBA 5A POP EDX ; 0012FF60
00C26BBB 59 POP ECX ; 0012FF60
00C26BBC 59 POP ECX ; 0012FF60
00C26BBD 64:8910 MOV DWORD PTR FS:[EAX],EDX
00C26BC0 EB 0F JMP SHORT 00C26BD1

Scroll up and we can see where it calls VirtualAlloc:

00C26AAF E8 E0F2FDFF CALL 00C05D94 ; JMP to kernel32.VirtualAlloc

That address is +26AAF from dll base. Address that is worth to remember. You need to break there somehow which again includes avoiding geting cought. Find your way. When you break on right line,

00C26AAF E8 E0F2FDFF CALL 00C05D94 ; JMP to kernel32.VirtualAlloc
00C26AB4 A3 24D3C200 MOV DWORD PTR DS:[C2D324],EAX

you need to change EAX value in second line. EAX must have base of original section. To find that, go now to 004F991D where code needs to be changed. You will see that it points somewere in 004FA000 section and that is original one. It means you must set EAX=004FA000. Then place memory bp on false OEP (erase all others) and run untill you break there:

004F9918 E8 5BCAF0FF CALL Enigma.00406378
004F991D 8B1D E4D04F00 MOV EBX,DWORD PTR DS:[4FD0E4] <---- See, it's OK now!
004F9923 8B03 MOV EAX,DWORD PTR DS:[EBX]
...
...

Check section there and you'll see that is OK. Let again see what we know till now:

Jump_to_loader_code = 0066CF71
Loader_Image_Base = 00C00000 (on my machine)
Loader_OEP = 00C2720C (on my machine)
Loader_relative_OEP = +2720C
Internal_check = +1FEE8 (00C1FEE8 on my machine)
Section_check = +1DA11 (00C1DA11 on my machine)
Jump_to_stolen_code = +20565 (00C20565 on my machine)
Code_relocation = +26AB4 (00C26AB4 on my machine)





6. General IAT problem

Enigma has three ways of import handling. First is completly removing whole IAT from IAT section, thunks are placed in some allocated memory block. Second, imports are little emulated/obfuscated. Then, it can combine both of that features together, remove and obfuscate. Third option, it can emulate some whole API's. This also can be combined with first feature. Our target has all this tricks included. Let's see one example: This is first import jump in exe:

004012A4 $-FF25 146FD300 JMP DWORD PTR DS:[00D36F14]

It points to some dword at 00D36F14 address but original jump should point to some dword in 00603000 section which was original IAT section. And dword that is placed there should be some API address, for example 77665544 but our dword is [00D36F14]=00C43268. We can follow that address:

00C43268 56 PUSH ESI
00C43269 64:A1 18000000 MOV EAX,DWORD PTR FS:[18]
00C4326F 8B4C24 08 MOV ECX,DWORD PTR SS:[ESP+8]
00C43273 83F9 F4 CMP ECX,-0C
00C43276 8B40 30 MOV EAX,DWORD PTR DS:[EAX+30]
00C43279 68 F8E3E777 PUSH 77E7E3F8
00C4327E C3 RETN

These opcodes are emulated first couple instructions of GetStdHandle API, then PUSH-RETN combination jumps someware in the middle of API. This was eaxample of removing IAT and emulating couple first opcodes. I found way how this can be little changed to help ImpREC fix imports. I found that these two jumps are important:

00C1ECE3 0F87 300A0000 JA 00C1F719 <------------------------- This one!
00C1ECE9 FF249D F0ECC100 JMP DWORD PTR DS:[EBX*4+C1ECF0]
00C1ECF0 F0: PREFIX LOCK:
00C1ECF1 F0:C100 FC LOCK ROL DWORD PTR DS:[EAX],0FC
00C1ECF5 F0:C100 08 LOCK ROL DWORD PTR DS:[EAX],8
00C1ECF9 F1 INT1
00C1ECFA C100 14 ROL DWORD PTR DS:[EAX],14
00C1ECFD F1 INT1
...

00C1F719 803C24 00 CMP BYTE PTR SS:[ESP],0
00C1F71D 0F84 A6000000 JE 00C1F7C9 <------------------- And this one (comes after first one)!
00C1F723 33C0 XOR EAX,EAX
00C1F725 8AC2 MOV AL,DL
00C1F727 83F8 78 CMP EAX,78
00C1F72A 7F 35 JG SHORT 00C1F761
00C1F72C 74 79 JE SHORT 00C1F7A7
00C1F72E 83C0 90 ADD EAX,-70
00C1F731 83F8 07 CMP EAX,7
00C1F734 0F87 8F000000 JA 00C1F7C9
00C1F73A FF2485 41F7C100 JMP DWORD PTR DS:[EAX*4+C1F741]
...

Distance first one from loader base is +1ECE3 and second one +1F719. Why are those jumps important? Because code below them is emulating api's. If those jumps are patched to JMP, all import jumps of that type will now look like this:

00C42FAE 68 E7E3E777 PUSH kernel32.GetStdHandle
00C42FB3 C3 RETN
00C42FB4 68 39D6D677 PUSH user32.GetKeyboardType
00C42FB9 C3 RETN
00C42FBA 68 FB6DD477 PUSH user32.LoadStringA
00C42FBF C3 RETN
00C42FC0 68 64B0D677 PUSH user32.MessageBoxA
00C42FC5 C3 RETN
...

And this is combination which ImpREC can easily find and repair. But jumps are still pointing to allocated block JMP DWORD PTR DS:[00D36F14]. There is way to fix that too. Restarting , placing memory breakpoints on address where import jump should be, stoppin couple times and finally I break on place where jumps are redirected:

00C1E457 037D 00 ADD EDI,DWORD PTR SS:[EBP]
00C1E45A 890F MOV DWORD PTR DS:[EDI],ECX <------ Here it changes jump pointers!
00C1E45C 40 INC EAX
00C1E45D 4A DEC EDX
00C1E45E ^75 D0 JNZ SHORT 00C1E430
00C1E460 43 INC EBX
00C1E461 FF0C24 DEC DWORD PTR SS:[ESP]
00C1E464 ^75 B2 JNZ SHORT 00C1E418
00C1E466 5A POP EDX
00C1E467 5D POP EBP
00C1E468 5F POP EDI
00C1E469 5E POP ESI
00C1E46A 5B POP EBX
00C1E46B C3 RETN

That line will change

004012AC FF25 18326000 JMP DWORD PTR DS:[603218]

to

004012AC -FF25 0B6FD300 JMP DWORD PTR DS:[D36F0B]


Let's remember this address too 00C1E457 or +1E45C. I have fixed that problem with injecting little code. Instead these two lines, I placed jump to empty space where I will add couple more lines:

00C1E457 037D 00 ADD EDI,DWORD PTR SS:[EBP]
00C1E45A 890F MOV DWORD PTR DS:[EDI],ECX

00C1E457 E9 CE8A0100 JMP 00C36F2A <------- I'm redirecting to empty space.

There I will inject some code. This code will not change jumps (I excluded MOV DWORD PTR DS:[EDI],ECX line which is doing that), it will place pointers in original IAT section.

00C36F2A 037D 00 ADD EDI,DWORD PTR SS:[EBP] <-- Old line copied.
00C36F2D 60 PUSHAD
00C36F2E 8B07 MOV EAX,DWORD PTR DS:[EDI]
00C36F30 8B09 MOV ECX,DWORD PTR DS:[ECX]
00C36F32 8908 MOV DWORD PTR DS:[EAX],ECX
00C36F34 61 POPAD
00C36F35 ^E9 2275FEFF JMP 00C1E45C <---------------- Return to main code.

I will fix fully emulated API's later, that is harder problem.





7. Dumping and repairing

And finally, I have now enough informations for making dump that is worth to dump. Let's repeat important addresses because I must all over again restart target:

Jump_to_loader_code = 0066CF71
Loader_Image_Base = 00C00000 (on my machine)
Loader_OEP = 00C2720C (on my machine)
Loader_relative_OEP = +2720C
Internal_check = +1FEE8 (00C1FEE8 on my machine)
Section_check = +1DA11 (00C1DA11 on my machine)
Jump_to_stolen_code = +20565 (00C20565 on my machine)
Code_relocation = +26AB4 (00C26AB4 on my machine)
First_import_jump = +1ECE3 (00C1ECE3 on my machine)
Second_import_jump = +1F71D (00C1F71D on my machine)
Jumps_redirection = +1E457 (00C1E457 on my machine)

To make dump we need to break on jumping to leader dll (0066CF71) , pass three times that internal integrity check (+1FEE8 from loader base) and then place breakpoints on Code_relocation, First_import_jump, Second_import_jump and Jumps_redirection where we need to inject that code. We must leave breakpoint on Internal_check. Then we must stop on every place and change all jumps, values and ijnect code like described before. After that, we will break on Internal_check for the last time. Then we need to undo all changes in loader code (jus select whole section and undo changes) and pass Internal_check for the last time. Continue to that RETN described before that should throw us to stolen code. But something happended and that RETN will lead to wrong address (to nowere , something like F453CDE5). I don't know reason for this, but it isn't important if you already know where stolen code starts. Go there and put new origin there and that's it. Break on fasle OEP, dump file , run ImpREC. In ImpREC use "Trace Level1" to find invaid imports. There should be ~10 invalid ones. Those are emulated API's. I didn't cut them, instead I saved import tree to hard disk.

I spend some time finding those API's, tracing trough original program and looking in some other Delphi programs. I managed to find them all:

004062B4 -FF25 64326000 JMP DWORD PTR DS:[603264]
DS:[00603264]=00C43002 --> GetModuleHandleA

004013B4 -FF25 B8316000 JMP DWORD PTR DS:[6031B8]
DS:[006031B8]=00C42F18 --> GetVersion

00401314 -FF25 EC316000 JMP DWORD PTR DS:[6031EC]
DS:[006031EC]=00C42F66 --> GetModuleHandleA

0040671C -FF25 48336000 JMP DWORD PTR DS:[603348]
DS:[00603348]=00C4314C --> GetModuleHandleA

0040677C -FF25 18336000 JMP DWORD PTR DS:[603318]
DS:[00603318]=00C43104 --> GetVersion

004066CC -FF25 70336000 JMP DWORD PTR DS:[603370]
DS:[00603370]=00C43188 --> GetCurrentProcessId

004067F4 -FF25 DC326000 JMP DWORD PTR DS:[6032DC]
DS:[006032DC]=00C430AA --> LoadLibraryA

00406804 -FF25 D4326000 JMP DWORD PTR DS:[6032D4]
DS:[006032D4]=00C4309E --> LockResource - SetHandleCount

Some are repeating. This one LockResource it seams that it doesn't exits on XP because it uses SetHandleCount (I have no idea :-?). What I have done is, I just wrote missing one in saved import tree. Then I load it in ImpREC and fixed dump. After that I added new section and placed stolen OEP's code and set OEP to be there in LordPE. And that's it :)





8. Final words

I sad that I will describe inline patching, but I have no will for that after all this writing. Unpacking this protector was very time consuming so I wrote two scripts that can help you. First script will find where is stolen code and OEP. Remember those values because second script that fixes imports needs that information. So, run first one, then restart an use second one. I also include Import_Tree.txt so you can fix all imports.

Protector has trial options which can be easily defeated. Using Regmon you can find where trial info is stored. Breaking on RegCreateKeyExA you can find where is procedure that checks trial limitations. Protector desn't check PE header so you can add new sections which makes inline patching easier. I don't know did I forgot something, but I guess that is all.

Sorry for mistakes in tutorial or in spelling.

Greets to all good people on biw reversing, arteam and crackmes.de. See you in the next tutorial :)



[ haggar , Croatia 2006 ]




What's Related

Story Options

Enigma protector 1.02 - manually unpacking | 0 comments | Create New Account
The following comments are owned by whomever posted them. This site is not responsible for what they say.
 Copyright © 2020 BiW Reversing
 All trademarks and copyrights on this page are owned by their respective owners.
Powered By Geeklog 
Created this page in 0.81 seconds