Level : intermediate
Armadillo with Code Splicing (anti dump) - manually unpacking
This is the third tutorial in Armadillo serial and it will discuss about one specific feature - code splicing. I will show two ways how to deal with this protection.
1. Requirements and tools
- Target www.reversing.be/binaries/articles/2005092917530000.rar
Basic info needed:
- Basic knowledge about PE structure;
- some experience with unpacking protectors;
- read my two previous tutorials about Armadillo.
- OllyDbg 1.10
- Hex editor
Code splicing is feature that "steals" blocks of original program code and redirects it to some allocated memory block. If we
dump such file, dump will be crippled and this is known also as anti-dump method. Interesting is that allocated blocks vary
from execution to execution of packed program, always on little different address. There is two ways to fix this; first way
is to prevent and redirect splicing, second is to dump that spliced block(s) from memory and attach it to our dump.
Also note that this target finds our ollydbg.exe and refuse to start if process with that name is pressent so you must rename
olly to something else. Also, this Armadillo version detects breakpoints on dll's so we will place our bp's always on second
or even better, on last API opcodes. That is not problem at all.
2. Searching OEP, fixing IAT
This version of Armadillo doesn't use Olly exploit so just place bp on last opcode in CreateThread API (you know, go to ->
expression -> type CreateThread and place bp on last RETN) and run. Return to code and find that CALL EDI that throws you on
003D6E73 CALL EDI <------ Jump to OEP!!
003D6E75 MOV EBX,EAX
003D6E77 POP EDI
003D6E78 MOV EAX,EBX
003D6E7A POP ESI
003D6E7B POP EBX
This is probably some 3.xx armadillo version because there are no CALL ECX but EDI. Execute it and you will land on OEP:
004061D0 PUSH EBP <------------------------- OEP!!!
004061D1 MOV EBP,ESP
004061D3 PUSH -1
004061D5 PUSH Armadill.0040A0D0
004061DA PUSH Armadill.00406F5C
004061DF MOV EAX,DWORD PTR FS:
004061E5 PUSH EAX
004061E6 MOV DWORD PTR FS:,ESP
004061ED SUB ESP,58
004061F0 PUSH EBX
004061F1 PUSH ESI
004061F2 PUSH EDI
004061F3 MOV DWORD PTR SS:[EBP-18],ESP
004061F6 CALL DWORD PTR DS:[40A02C] <------- Missing import!!!
004061FC XOR EDX,EDX
004061FE MOV DL,AH
00406200 MOV DWORD PTR DS:[40C57C],EDX
You will not dump file now. Dumping will be the last thing to do in this tutorial. First we will prevent IAT redirecting and
save ImpREC tree for further use. Follow that DS:[40A02C] in dump:
0040A02C F7 DC 3B 00 1A AF 3B 00 B7 AF 3B 00 0C E6 E7 77 ..;...;...;....w
0040A03C A1 16 F5 77 6B 15 F5 77 95 9B E9 77 FC AC E7 77 ...wk..w...w...w
0040A04C 2F E0 E9 77 E8 E4 E7 77 9C A8 E7 77 54 AB 3B 00 /..w...w...wT.;.
Do you remember what you need to do from previous tutorials? Remember it's address in dump 0040A02C and restart target in
olly. Place bp on end of CreateThread API and follow in dump 0040A02C address which is place where API redirection value will
0040A02C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
Place hardware bp on write there, on first 4 bytes and run olly.
Breaking first time is not important:
77C42F43 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
77C42F45 JMP DWORD PTR DS:[EDX*4+77C43058]
77C42F4C MOV EAX,EDI
77C42F4E MOV EDX,3
77C42F53 SUB ECX,4
77C42F56 JB SHORT msvcrt.77C42F64
77C42F58 AND EAX,3
77C42F5B ADD ECX,EAX
77C42F5D JMP DWORD PTR DS:[EAX*4+77C42F70]
77C42F64 JMP DWORD PTR DS:[ECX*4+77C43068]
Second time is what we want (then delete hw bp):
003D4045 CMP DWORD PTR DS:[EAX+8],0
003D4049 JE SHORT 003D4093
003D404B PUSH 100
003D4050 LEA EAX,DWORD PTR SS:[EBP-1E84]
003D4056 PUSH EAX
003D4057 MOV EAX,DWORD PTR SS:[EBP-1D84]
003D405D PUSH DWORD PTR DS:[EAX]
003D405F CALL 003B7816
003D4064 ADD ESP,0C
003D4067 LEA EAX,DWORD PTR SS:[EBP-1E84]
003D406D PUSH EAX
003D406E PUSH DWORD PTR SS:[EBP-1D7C]
003D4074 CALL DWORD PTR DS:[3DB32C] ; msvcrt._stricmp
003D407A POP ECX
003D407B POP ECX
003D407C TEST EAX,EAX
003D407E JNZ SHORT 003D4091 <---------------------- Most important place, magic jump!!!
003D419A MOV DWORD PTR DS:[EAX],ECX
003D419C MOV EAX,DWORD PTR SS:[EBP-17DC] <--------- You landed here!!!
Remember address of "magic jump" 003D407E. Of course, on your system it can have different value. Now, remember what we have
done in second tutorial. This file has those CRC-decryption-encryption loops and file will crush when we change something.
Restart target, place bp on VirtualAlloc+1 in command bar and press Shift+F9 two times. Then in CPU window go to our jump
003D407E ADD BYTE PTR DS:[EAX],AL <---- You're here!
003D4080 ADD BYTE PTR DS:[EAX],AL
003D4082 ADD BYTE PTR DS:[EAX],AL
003D4084 ADD BYTE PTR DS:[EAX],AL
003D4086 ADD BYTE PTR DS:[EAX],AL
You will see nothing there because armadillo dll still isn't unpacked there. Never mind, you place hardware bp on execution
on 003D407E address, remove bp from VirtualAlloc and run olly. You will break on unpacked-decrypted code:
003D407E JNZ SHORT 003D4091 <---------------- Our magic jump!!!
003D4080 MOV EAX,DWORD PTR SS:[EBP-1D84]
003D4086 MOV EAX,DWORD PTR DS:[EAX+8]
003D4089 MOV DWORD PTR SS:[EBP-1D78],EAX
003D408F JMP SHORT 003D4093
003D4091 JMP SHORT 003D4030
003D4093 CMP DWORD PTR SS:[EBP-1D78],0
003D409A JNZ SHORT 003D40DB
003D409C MOVZX EAX,WORD PTR SS:[EBP-1D74]
Remove hardware bp, change JNZ to JMP and run olly. It will crush, but we have expected that. Still our import section will
have what we want. Like in the second tutorial, open another olly instance (hidden one too) and find OEP in new instance of
target. Import section is .rdata , binary copy whole that section from crushed target and paste it to new one where you found
OEP. Close olly that holds crushed target, open ImpREC and find imports. You will have 3 invalid thunks, cut it, and save
import tree to hard drive (remember, dumping is last thing).
First part of job is done and now it's time to meet Code Splacing.
3. Code Splicing - anti dumps
Trace in olly with F8 to first call:
0040622A CALL Armadill.00406E27
and enter in it:
00406E27 XOR EAX,EAX
00406E29 PUSH 0
00406E2B CMP DWORD PTR SS:[ESP+8],EAX
00406E2F JMP 03B542CA <------------------------------- Look this jump!!!
00406E34 SETE AL
00406E37 PUSH EAX
00406E38 CALL DWORD PTR DS:[40A06C] ; kernel32.HeapCreate
00406E3E TEST EAX,EAX
00406E40 JMP 03B542DC <------------------------------- Look this jump!!!
00406E45 JE SHORT Armadill.00406E5C
00406E47 CALL Armadill.004074B3
00406E4C TEST EAX,EAX
00406E4E JNZ SHORT Armadill.00406E5F
00406E50 JMP 03B542EF <------------------------------- Look this jump!!!
00406E56 CALL DWORD PTR DS:[40A068] ; kernel32.HeapDestroy
00406E5C XOR EAX,EAX
00406E5F PUSH 1
00406E61 POP EAX
00406E64 JMP 03B54300 <------------------------------- Look this jump!!!
There you will see lot of such jumps. Values of that jumps will be different at you. That is code splicing. Program flow
jumps to some allocated place of memory which cannot be dumped with main exe. There are two ways to fix this.
3.1 First way (lame one :) - dump exe, dump spliced block and attach it to main dump
- Ok, first repair PE header of our target, you know how, open another olly and copy-paste PE header from packed instance of
target to our first - unpacked. Then dump file with LordPE or olly dump (OEP is 61D0). Now use ImpREC and saved tree and fix
IAT. Now we need to add spliced block to dumped file in order to get working dump.
At my computer spliced block is here (and it's size is that big):
03B50000 00016000 Priv RWE RWE
- I will add to my dumped file 4000000 bytes (zeros) with hex editor to the end of the file. That is more than 60 MB, quite a
big. Don't wory for that, we gona fix size of exe later.
- Then I open LordPE's PE editor and there I will change VirtualSize and RawSize of last section (.mackt) for that amount of
bytes, so now my VS is 04001000 and RW is 04001000 too.
- Now I will open that dump in another olly and I will go at the 3B50000 address in it:
03B50000 0000 ADD BYTE PTR DS:[EAX],AL
03B50002 0000 ADD BYTE PTR DS:[EAX],AL
03B50004 0000 ADD BYTE PTR DS:[EAX],AL
03B50006 0000 ADD BYTE PTR DS:[EAX],AL
03B50008 0000 ADD BYTE PTR DS:[EAX],AL
03B5000A 0000 ADD BYTE PTR DS:[EAX],AL
03B5000C 0000 ADD BYTE PTR DS:[EAX],AL
03B5000E 0000 ADD BYTE PTR DS:[EAX],AL
That is where spliced code block should be. Now I will just binary copy all bytes from spliced block and paste it to my
dumped file starting from 3b5000 address and save changes. Since dump is big it can take some time.
- Now, after I have save all changes, I runned dumped file and guess what? Dump works great! Yeah! Do it like I did and your
file fill also work. If it's not working, check where did you make mistake. It's wery simple actually.
Ok, file works, but file on my drive is 64 MB in size! That's a big waste of space. Do not wory, you can reduce size of it
with LordPE or just pack it with some packer (WinUpack can pack it to ~250 kb). Rared file is less than 300 kb. But still,
this file waste lot of memory space for nothing. Second way of solwing splicing problem is much better.
Dumped file that I got with this way of unpacking I didn't put in archive to keep it smaller.
3.2 Redirecting spliced block
If you find OEP couple times in the same packed file, you will see that jumps to spliced block change thir values. This block
is loaded every time under some other address. And there is idea, maybe we can redirect that block to some closer place in
memory or even in our dump. Since base address of spliced block is some random value, you can conclude that armadillo uses
some function like GetTickCount to get/generate some random values. Indeed, I found that armadillo uses "time" API from
msvcrt.dll. You going to find it in this way, place bp on VirtualAlloc and press Shift+F9 3 times. At this time most of dll's
needed for operation are loaded. Then place bp on time API and pree Shift+F9 (remove bp from VirtualAlloc before). You will
break in msvcrt.dll, remove breakpoint and return tu code:
003D1EAF CALL DWORD PTR DS:[3DB2A8] ; msvcrt.time
003D1EB5 POP ECX <-------------------------------------- You are here!!!
003D1EB6 MOV DWORD PTR SS:[EBP-3110],EAX <-------------- EAX holds random value.
003D1EBC MOV EAX,DWORD PTR SS:[EBP-3110]
003D1EC2 MOV DWORD PTR SS:[EBP-1998],EAX
003D1EC8 AND DWORD PTR SS:[EBP-1980],0
003D1ECF CMP DWORD PTR SS:[EBP-1980],0
003D1ED6 JNZ 003D1FA3
003D1EDC MOV EAX,DWORD PTR SS:[EBP-1984]
003D1EE2 DEC EAX
003D1EE3 MOV DWORD PTR SS:[EBP-1984],EAX
003D1EE9 CMP DWORD PTR SS:[EBP-1984],0
003D1EF0 JNB SHORT 003D1F13
003D1EF2 MOV EAX,DWORD PTR SS:[EBP+8]
003D1EF5 MOV EAX,DWORD PTR DS:[EAX]
003D1EF7 AND DWORD PTR DS:[EAX],0
003D1EFA PUSH 3E1A9C ; ASCII "Location CS1"
003D1EFF MOV EAX,DWORD PTR SS:[EBP+8]
003D1F02 PUSH DWORD PTR DS:[EAX+4]
003D1F05 CALL 003DA258 ; JMP to msvcrt.strcpy
003D1F0A POP ECX
003D1F0B POP ECX
003D1F0C XOR EAX,EAX
003D1F0E JMP 003D512A
003D1F13 CMP DWORD PTR SS:[EBP-1984],0
003D1F1A JBE SHORT 003D1F33
003D1F1C LEA ECX,DWORD PTR SS:[EBP-1998]
003D1F22 CALL 003B511B
003D1F27 AND EAX,3FF0000 <---------------- The most important place vhere value gets final shape.
003D1F2C MOV DWORD PTR DS:[3E91D8],EAX <-- Save that value.
003D1F31 JMP SHORT 003D1F43
003D1F33 CMP DWORD PTR SS:[EBP-1984],0
003D1F3A JNZ SHORT 003D1F43
003D1F3C AND DWORD PTR DS:[3E91D8],0
003D1F43 PUSH 40
003D1F45 PUSH 2000
003D1F4A PUSH DWORD PTR SS:[EBP-1988]
003D1F50 PUSH DWORD PTR DS:[3E91D8]
003D1F56 CALL DWORD PTR DS:[3DB1B0] ; kernel32.VirtualAlloc <--- Allocating that memory.
003D1F5C MOV DWORD PTR SS:[EBP-1980],EAX <----------------------- We will change this EAX value!!!
003D1F62 CMP DWORD PTR SS:[EBP-1980],0
003D1F69 JE SHORT 003D1F9E <--------------- Check! This jump must not be taken!!
003D1F6B PUSH 40
003D1F6D PUSH 1000
003D1F72 PUSH DWORD PTR SS:[EBP-1988]
003D1F78 PUSH DWORD PTR DS:[3E91D8]
003D1F7E CALL DWORD PTR DS:[3DB1B0] ; kernel32.VirtualAlloc
003D1F84 MOV DWORD PTR SS:[EBP-1980],EAX <----------------------- We will change this EAX value!!!
003D1F8A CMP DWORD PTR SS:[EBP-1980],0
003D1F91 JE SHORT 003D1F9E <--------------- Check! This jump must not be taken!!
003D1F93 MOV EAX,DWORD PTR SS:[EBP-1980]
003D1F99 MOV DWORD PTR DS:[3E91D8],EAX
003D1F9E JMP 003D1ECF
003D1FA3 PUSH 1
You have just came from time function and EAX holds some random value. Most important place is that at 003D1F27 address.
There that random value will get final shape, something like 1C70000 and that is address where block will start. Than that
value is saved. Here we can force armadillo to extract that code where we like. Notice in memory dump what sections armadillo
00400000 00001000 Armadill PE header Imag R RWE
00401000 00009000 Armadill .text Imag R RWE
0040A000 00001000 Armadill .rdata Imag R RWE
0040B000 00003000 Armadill .data Imag R RWE
0040E000 00030000 Armadill .text1 code Imag R RWE
0043E000 00010000 Armadill .adata code Imag R RWE
0044E000 00020000 Armadill .data1 data,imports Imag R RWE
0046E000 00030000 Armadill .pdata Imag R RWE
0049E000 00001000 Armadill .rsrc resources Imag R RWE
.text1, .adata, .data1 and .pdata are armadillo's sections. We can use some of that section as place for redirection. I will
force my redirection to .pdata section.
We will let armadillo to reserve block where he likes, but we gonna change address where it will extract code. Simply, when
you came to this two lines, change EAX to 46E000:
003D1F5C MOV DWORD PTR SS:[EBP-1980],EAX
003D1F84 MOV DWORD PTR SS:[EBP-1980],EAX
And that's it ;)! Armadillo will extract spliced plock in our dump. Now just place bp on CreateThread API and find OEP. When
you find OEP don't forget to fix PE header first. Than dump and then use that imports tree for ImpREC that you created and
saved in 2. chapter of this tutorial. With it , fix IAT of dumped file. Run it... It works!!! And our file size is now 650 kb
and not gigabytes like in first example. Maybe you can reduce size even more, but who cares. Armadillo with splicing code is
4. Final words
I hope that my next tutorial will be about armadillo's import destruction or Debug Blocker features, but I will see can I do
that. With this knolwedge from this three tutorials, you can already unpack bunch of armadillo targets. Lot of targets don't
even use splicing code, just minimum protection.
See you in the next tutorial!
Greets goes to all folks on BIW!
[ haggar ]