Professional Documents
Culture Documents
ASProtect VM Analyze
July 2007
1. Introduction
Well it was nearly a year since I wrote tutorial on how to fix Poly Oep
in ASProtect. That method worked more than fine for me, but I also
saw some comments that people expected to see how VM is fully
disassembled. Have no idea why, and how, but I got interested into
ASPR once more.
This document will be about ASProtect VM, and how to fix it,
including Poly OEP, and Advanced Import protection when ASPR
emulates instructions after stolen call dwod ptr[]. Reason why Im
writing this tut is that ASPR wasnt updated for a long time, so I
doubt that there will be update after this tutorial comes out, and
yet its simple VM is good introduction to VM reversing, which
nowadays becomes a must in a world of copy protection.
Editor: deroko
ASPROTECT VM ANALYZE PAGE 2
Disclaimers
All code included with this tutorial is free to use and modify; we only ask that you mention where you found it. This
tutorial is also free to distribute in its current unaltered form, with all the included supplements.
All the commercial programs used within this document have been used only for the purpose of demonstrating
the theories and methods described. No distribution of patched applications has been done under any media or
host. The applications used were most of the times already been patched, and cracked versions were available
since a lot of time. ARTeam or the authors of the paper cannot be considered responsible damages the
companies holding rights on those programs. The scope of this tutorial as well as any other ARTeam tutorial is of
sharing knowledge and teaching how to patch applications, how to bypass protections and generally speaking
how to improve the RCE art. We are not releasing any cracked application.
Verification
ARTeam.esfv can be opened in the ARTeamESFVChecker to verify all files have been released by ARTeam and
are unaltered. The ARTeamESFVChecker can be obtained in the release section of the ARTeam site:
http://releases.accessroot.com
Table of Contents
1. Introduction.......................................................................................................................................................................... 1
1. Tools and Target .................................................................................................................................................................. 3
2. Poly OEP VM ........................................................................................................................................................................ 4
3. AIP Recovery ..................................................................................................................................................................... 15
4. Conclusion ......................................................................................................................................................................... 33
5. References ......................................................................................................................................................................... 33
6. Greetings ............................................................................................................................................................................ 33
PAGE 3 ASPROTECT VM ANALYZE
When it comes to debugger, I prefer SoftICE, but, Olly will work too.
Aslo you will need my tutorial about ASPR from last year as I will refer to it several times, so just
make sure that you have it with you
If you are using Olly, make sure to use Manual Load in IDA so you can adjust your dll to proper
base. This is required for fast unpacking, so offsets in IDA and loaded dll in olly will be the same.
To find base address of loaded ASPR dll, simply break at 2nd VirtualAlloc, and write down that
address, as this is the place where ASPR allocates memory for virtual.dll. 1st break is allocation of
memory which will be used as decompression buffer
2. Poly OEP VM
Before we even start we need virtua dll to analyze ASPR protection. If you really want to know
how to dump it, read my tutorial from last year. But now we will use AsprDllDumper utility, dont
laugh at code It was 5am when I wrote it, and I didnt have any Red Bull left. Most important
thing is that it works as expected
First you should dump virtual dll from unpackme.exe target, as I will cover Poly OEP analyze
based on that example. Later on you will see how to make this approach generic for any Aspr
2.3 target.
To find PolyOepDispatcher you will have to use common technique of setting memory break
point on code section, once you break there, step out of code section, and trace trough poly
oep until you see call in it. This call will lead you to obsfucated memory buffer whose job is to
save registers, eflags, address of call, and to pass to PolyOepDispatcher data needed to
process VM instruction. There are other easier ways to locate PolyOEP EntryPoint but that is not
important atm, as Im going to cover only VM in this document. You may also use byte search to
find it.
If you have dumped your target dll on WinXP SP2 you will not have problems following tutorial
and offsets in it, otherwise you will have to force IDA to load dumped dll to different image base,
which you may accomplish by selecting Manual Load, anyhow learn method, and dont follow
offsets blindly.
Poly OEP dispatcher hasnt changed since last year, well because in meanwhile there werent
any updates, and it is same in 2.2x targets.
It is very, very important to log data passed on stack to PolyOEPDispatcher or, as I have named
it, VM_Interpreter. You may see this arguments in above listing.
Here you may see code which obtains address of call __callpolyoep, and that address is used to
calculate unique ID of VM ocpde. I hope that I dont have to mention that ASPR can emulate
call/jmp/jcc/cmp,jcc in poly OEP.
OpcodeID is calculated by substracting call address with PolyOepVa and VM_Key2 which is
located at offset EBX+68h or VmData.68h. After that search for right opcode occurs here:
Aspr cycles trough array of opcodes until it finds right one, if no opcode is found then Protection
error is generated, and ASPR will terminate its execution. I hope that you see how you may
extract exact position of call in poly OEP? Simple, take VM_OpcodeID, add to it VmData.68h +
PolyOepVA, and you will get address that needs to be fixed.
Dont pay attention to comments 2nd call (movzx eax, [eax+14]) as those are specific only for
Delphi target unpackme.exe, those are changed from one to another application, as are VM
opcode size, and format. Still this can be fixed in generic way. You may for instance inject dll into
ASPR code, and perform decoding in its context. For the record I dont use this method in my
generic unpacker for ASPR 2.3 but it is up to you to figure how you will do this task. You will need
to call those procs located at VmData.40h using indexed from VmData.24h. Think about it
.CODE:009F8F56 dec bl
.CODE:009F8F58 jnz __protection_error
.CODE:009F8F5E mov ecx, esi
.CODE:009F8F60 mov edx, [ebp+register_struct]
.CODE:009F8F63 mov eax, [ebp+vmstructs_vmdata]
.CODE:009F8F66 call EmulateRealOpcode
You dont really need to know how those data are extracted from VmOpcode, well you may
disassemble each one of this procedures but remember it will be different in next target. Thats
what I figured after writing PolyOep analyzer for one target, and when I tested it on different
target, well guess what, it was different, so till this point you need to identify 4 procedures that is
ASPR calling to extract various data:
VmProcIndexes = VmData+24h
Index 0 opcode ID, this index is not really necessary as opcodes are stored in array.
Index 1 Opcode Type
Index 2 DataType3 (just remember it like that)
Index 3 DataType4 (again, just remember it like that)
At offset 9F8F46 you may see that ASPR is able to emulate a few instructions with this indexes:
0/1 = jmp or call, it doesnt really matter as you may identify call or jmp based on fact that
previous to call there was push or not. If push was there, then this was call, otherwise it is jmp.
2 = jcc emulation
3 = cmp,jcc emulation
Call/jmp Decoding:
.CODE:009F9022 __CallOrJmp:
.CODE:009F9022 mov eax, [ebp+vmstructs_vmdata]
.CODE:009F9025 mov edx, [eax+VM_Struct.VM_key2]
.CODE:009F9028 mov eax, edx
.CODE:009F902A add eax, [ebp+return_from_3rdcall]
.CODE:009F902D mov ecx, [ebp+vmstructs_vmdata]
.CODE:009F9030 mov ecx, [ecx+70h]
.CODE:009F9033 add eax, ecx
.CODE:009F9035 cmp eax, 0FFFFFFFFh
.CODE:009F9038 jnz short __JmpCallIsInPolyOep
.CODE:009F903A mov eax, edx
.CODE:009F903C add eax, [ebp+return_from_4thcall]
.CODE:009F903F mov edx, [ebp+vmstructs_vmdata]
.CODE:009F9042 add eax, [edx+VM_Struct.ImageBase]
.CODE:009F9045 add eax, ecx
.CODE:009F9047 jmp short __gobacktocode
.CODE:009F9049
.CODE:009F9049
.CODE:009F9049 __JmpCallIsInPolyOep:
.CODE:009F9049 mov edx, [ebp+vmstructs_vmdata]
.CODE:009F904C add eax, [edx+VM_Struct.PolyOepEntryPoint]
.CODE:009F904F jmp short __gobacktocode
ASPROTECT VM ANALYZE PAGE 8
If you take a closer look ASPR has to decide if call is going to stolen procedure which is in
PolyOEP, or it goes to the code section, or any other section out of poly oep range.
if (TempVar == -1)
TempVar = VmData.70h + VmData68h + DataType4 + ImageBase;
else
TempVar = TempVar + PolyOepVA;
For ASPR 2.2 VmData.70h is not used to calculate destinations. VmData.70h is introduces in ASPR
2.3 SKE.
.CODE:009F8FCB __emulate_jcc_only:
.CODE:009F8FCB xor eax, eax
.CODE:009F8FCD mov al, [edi+4]
.CODE:009F8FD0 mov edx, [ebp+vmstructs_vmdata]
.CODE:009F8FD3 mov ebx, [edx+eax*4+40h]
.CODE:009F8FD7 mov eax, esi ; VM opcode
.CODE:009F8FD9 call ebx ; extract jcc type
.CODE:009F8FDB mov ebx, eax
.CODE:009F8FDD mov eax, [ebp+vmstructs_vmdata]
.CODE:009F8FE0 xor bl, [eax+70h] ; decode jcc by xoring
it with byte at 70h
.CODE:009F8FE3 mov ecx, [ebp+eflags]
.CODE:009F8FE6 mov edx, ebx
.CODE:009F8FE8 mov eax, [ebp+vmstructs_vmdata]
.CODE:009F8FEB call EmulateJcc ; decides if jcc is
taken or not
.CODE:009F8FF0 test al, al
.CODE:009F8FF2 jz short _jcc_not_taken
.CODE:009F8FF4 mov eax, [ebp+vmstructs_vmdata]
.CODE:009F8FF7 mov eax, [eax+VM_Struct.PolyOepEntryPoint]
.CODE:009F8FFA add eax, [ebp+return_from_4thcall]
.CODE:009F8FFD mov edx, [ebp+vmstructs_vmdata]
.CODE:009F9000 add eax, [edx+VM_Struct.VM_key2]
.CODE:009F9003 mov edx, [ebp+vmstructs_vmdata]
.CODE:009F9006 add eax, [edx+70h]
.CODE:009F9009 jmp short __gobacktocode
.CODE:009F900B
.CODE:009F900B
.CODE:009F900B _jcc_not_taken:
.CODE:009F900B mov eax, [ebp+vmstructs_vmdata]
.CODE:009F900E mov eax, [eax+VM_Struct.PolyOepEntryPoint]
.CODE:009F9011 add eax, [ebp+return_from_3rdcall]
.CODE:009F9014 mov edx, [ebp+vmstructs_vmdata]
.CODE:009F9017 add eax, [edx+VM_Struct.VM_key2]
.CODE:009F901A mov edx, [ebp+vmstructs_vmdata]
.CODE:009F901D add eax, [edx+70h]
.CODE:009F9020 jmp short __gobacktocode
PAGE 9 ASPROTECT VM ANALYZE
Jcc Type is extracted from index 4, and in ASPR 2.3 SKE it is xored with byte at VmData.70h, this
xoring is not present in ASPR 2.2 SKE.
Depending on this value we enter into EmulateJcc procedure, and there bit test occurs.
.CODE:009F93C7 jcc_jc_jcc_jb:
.CODE:009F93C7 xor edx, edx ; case 0x2
.CODE:009F93C9 mov eax, esi
.CODE:009F93CB call BitTest_1ifset
.CODE:009F93D0 mov ebx, eax
.CODE:009F93D2 jmp loc_9F959D
bt eax, edx
sbb eax, eax
and eax, 1
What it does, is that it checks if certain bit(edx) is set in elfags(eax), if bit is set, then CF will be set
too, and sbb will set eax to -1, otherwise if bit is not set then CF wont be set, and sbb eax, eax
will result into 0, and, of course, 0 will be return value.
ASPROTECT VM ANALYZE PAGE 10
edx is bit position, so bit 0 is CF in eflags, and this code checks if CF is set, and returns proper
value back. So if CF is set then take jmp which means that this is jb or jc.
.CODE:009F93DA jcc_jnc_jcc_jnb:
.CODE:009F93DA xor edx, edx ; case 0x3
.CODE:009F93DC mov eax, esi
.CODE:009F93DE call BitTest_1ifset
.CODE:009F93E3 mov ebx, eax
.CODE:009F93E5 xor bl, 1
.CODE:009F93E8 jmp loc_9F959D
Here we have quite different situation, if CF is set then return value is 0 which means that jmp is
not taken. Only instructions that jmps if this bit(CF) is not set are jnb and jnc. Remember that ja !=
jnb as ja is taken only when ZF and CF are 0, while jnb is taken if CF == 0.
If you take a look at code which deals with jcc emulation (one where JccType is extracted) you
will see also how destination is calculated depending if jcc is taken or not:
JccDestination = 0;
if (JccTaken)
JccDestination = PolyEntryPoint + VmData.68h + VmData70h + DataType4;
else
JccDestination = PolyEntryPoint + VmData.68h + VmData70h + DataType3;
Remember that VmData70h is not present in 2.2 so calculation is same, except you dont use
VmData70h.
Lets focus now on decoding of CMP instruction, jcc which follows cmp instruction is always
decoded in the same way as if there was no cmp.
Dont ask me why I have named EmulateCmp as EmulateRealOpcode. This name gives
impression that jccs are not opcodes, but that wasnt my intention
This procedure is huge so Ill split it into several parts. 1st part is extraction of DataSrc and RegSrc:
PAGE 11 ASPROTECT VM ANALYZE
Index 8 is used to extract SrcData, depending on comparation later, this can be memory
address or displacement when indexing is used. I guess I dont have to remind you that
VmData.70h which is used here is NOT used in Aspr 2.2.
Index 6 on other hand is used to extract index of register which is SrcRegister, if this value is < 8
then there is no register used in this instruction, and depending on cmp type you can decide if
this is immediate value or memory address.
Index 7 is used to extract DstData which can be displacement or memory address, it cant be
immediate when Dst is used.
Index 5 on other hand is used to extract Dst register, if it is < 8 then there is no Dst Register used.
ASPR then advances to calculating Dst and Src, and performs comparation:
Index 9 is used to extract type of cmp instruction. One thing that doesnt seem logic here is
when SourceIsByte which means:
Cmp reg, byte ptr[], in IA32 there is no such instruction, as it is impossible to compare 32bit
register with 8bit. Simply this opcode cant be executed ever, and if it occurs in your decoding,
then you will know that something went terribly wrong
EBP has address which is computed as DstReg + DstData, while eax has SourceData calculated
as SrcReg + SrcData (well if SrcReg index < 8 then SrcReg is 0).
To decode this properly you will have to write bunch of if/else to isolate good opcode, here is
one example:
switch(CmpType){
case 0: //cmp [],
if (RegSrc < 8 && RegDst <8){
_snprintf(OpcodeBuffer, 256, "cmp [%s], %s", RegisterNames[RegDst],
RegisterNames[RegSrc]);
break;
}
if (RegSrc <8){
_snprintf(OpcodeBuffer, 256, "cmp dword ptr[%.08X], %s", DataDst,
RegisterNames[RegSrc]);
break;
}
if (RegDst <8){
_snprintf(OpcodeBuffer, 256, "cmp dword ptr[%s], %.08X",
RegisterNames[RegDst], DataSrc);
break;
}
_snprintf(OpcodeBuffer, 256, "cmp dword ptr[%.08X], %.08X", DataDst, DataSrc);
break;
Well some instruction combinations have less options, as you may see I dont decode indexing,
but it is possible to occur. For example when RegDst < 8 && DataDst != 0.
Ok thats pretty much all about Poly OEP decoding, here is reminder of indexes for data
extraction:
If you think this was hard, then what are you going to say when we hit AIP emulation This was
piece of cake
PAGE 15 ASPROTECT VM ANALYZE
3. AIP Recovery
Before we continue, I have to note that at this point Im switching to msvc protected application
dumped dll as it is good for tracing, and recovering Data from AIP. Delphi is not good example
for tracing AIP as those are all jmp dword [], and no instruction emulation.
API redirection done by ASPR can be separated into what I have named normal and AIP
redirection. Normal redirection is very simple as it consist of call to allocated memory block
which will then jmp to yet another block of memory where are stored a few stolen bytes and
push/ret to rest of API, to fix this you have several choices:
Whichever method you chose you are facing one big problem. You dont know if this was call
dword ptr[] or jmp dword ptr[], sure, you may push arguments on stack and check if those are
same (call) or 1st argument is erased from stack (jmp dword[]) or simply while tracing, check if in
1st virtual buffer there is add esp, 4 or lea esp, [esp+4] which indicated jmp dword ptr[]. This is to
much hustle, and ASPR 2.3 gives us chance to know if this was jmp or call dword ptr[] by simply
analyzing AIP interpreter.
To retrieve API when AIP is used you have also several choices, but before I say anything about
them, AIP interpreter will call LoadLibraryA and custom implementation of GetProcAddress. As
this implementation doesnt support ordinals you know that this custom GetProcAddress will
receive name of the API. Methods to obtain API are:
One major question arises, and thats how to identify if API redirection is normal or AIP, well, well,
you will need to find code responsible for writing redirections, you may do this by setting HWBP
on any redirection, and depending if it is Normal or AIP you will land here:
You are at this point in the loop which decides what API redirection should be written, here is
where you extract needed data:
Oki doki, EBX has data which I have named AIPData (similar as VMData when it comes to
PolyOEP). Similar concept is used here, using indexes to extract API data, API addresses, type of
redirection, is emulation needed, where is emulation opcode etc
You should note that index 9 in AipProcs is used to determine what type of redirection this is, and
index 0 is used to extract where is address to which redirection should be applied. Also you
needed data pointed by EBX because without it is impossible to fix AIP.
This 2 indexs combined (0 and 9) will give you enough to get started with fixing AIP. One quick
note before we fix AIP is that instructions which can be emulated in AIP are:
call
jmp
mov
add
cmp/jcc
cmp
Each one of them has its own decoding logic, mov for example has several decodings
depending on what is moved, cmp/jcc is pretty much same, call/jmp are on other hand really
easy.
Once you have found one AIP simply follow it till it enters AIP_Dispatcher:
It is very important to remember arguments passed to it. When it comes to unknown I think it was
SEH before enter to AIP_Dispatcher, cant remember, well anyway this value is not important at
all for decoding AIP.
Here you may see how AIP_ID is calculated, it is not very important as we are recovering API
based on data which is located in EBX (AipData). Each opcode held in this structure will tell us
everything we need to know about AIP, and as such we dont have to take care of AIP_ID.
Next thing important to note is how is calculated offset of AIP opcode, again, for fixing this is not
needed but this text is more on ASProtect VM, then about unpacking:
To find correct AIP opcode ASPR uses one byte after relative call, and xors that byte with data at
AipData.E0h, this is now index into array of AipOpcodes, and AipOpcode is obtained by adding
AipData.AipOpcodeSize (E4h) + AipData.AipOpcodes (54h). Now ASPR can enter into
AIP_Interpreter and handle opcode.
While API is being resolved it isnt smart to set breakpoints, as before dll name and API name are
extracted there is checksum check on certain portion of code. So make sure that you are using
hardware breakpoints to step over calls or you will end up with protection error.
Here you may see how local variables are filled with needed data, and that opposite to
PolyOep interpreter, which received VmOpcode, here AipInterpreter receives pointer to
AipOpcode. ASPR proceeds with extracting data needed for each AIPOpcode:
This time, each data extracted from certain index is named DataIndexX, for easier revesing I
chose it to be like that. You may use any other naming for local variables and retrived data.
Here Aspr sets local variable IsEmulationNeeded. Actually it is index of emulation opcode, and if
this index is -1 then there is no emulation used, otherwise we have to decode emulation
instructions and this is where fun really begins.
Now we come to code responsible for retrieving API, you may skip it, as you may easily find API
using some of mentioned methods above.
To determine if this is call dword ptr[] or jmp dword ptr[] ASPR uses this code:
.CODE:009DB0CA __notcalldword:
.CODE:009DB0CA mov eax, [ebp+AIP_Data]
.CODE:009DB0CD mov al, [eax+4Bh]
.CODE:009DB0D0 cmp al, [ebp+DataIndex1]
.CODE:009DB0D3 jnz short __nojmpdword_protection_error
So if DataIndex1 = AipData.4Ah we have call dword ptr[], else if DataIndex1 = AipData.4Bh then
we have jmp dword ptr[] otherwise we have error
How did I find this? Well if you trace ASPR a little bit you will see 2 very similar procedures but with
one huge huge difference:
And CallApiTroughObsfucation:
Note here how arguments from OldEsp are copied to new stack location, how new ret address is
stored on stack and API is called.
ASPROTECT VM ANALYZE PAGE 22
Well this is exactly how you may distinguish jmp dword ptr[] and call dword ptr[]. When call
dword ptr[] is used in AIP then arguments are located at OldStackLocation, but when jmp dword
ptr[] is used then arguments are located at OldEsp+4 because we have to take care of call
which called jmp dword ptr[].
When API is executed we are now going to check if emulation is needed or not. In case of jmp
dwod ptr[] ASPR checks if emulation is needed, but this will never occur. What we are interested
are call dword ptr[] because only and only then emulation of next instruction can occur.
.CODE:009DB170 __checkifemuationisneeded:
.CODE:009DB170 cmp [ebp+IsEmulationNeeded], 0FFFFFFFFh
.CODE:009DB174 jz short __noemulationneeded
.CODE:009DB176 mov edx, [ebp+IsEmulationNeeded]
.CODE:009DB179 mov eax, [ebp+AIP_Data]
.CODE:009DB17C call CalcualteAipOpcodeForEmulation ; =
IsEmulationNeeded *AipOpcodeSize(AipData.E4) + AipBaseOfOpcodes (AipData.58h)
.CODE:009DB181 mov [ebp+AipOpcodeForEmulation], eax
.CODE:009DB184 lea eax, [ebp+AipOpcodeForEmulation]
.CODE:009DB187 push eax
.CODE:009DB188 mov eax, [ebp+eflags]
.CODE:009DB18B push eax
.CODE:009DB18C mov eax, [ebp+RegisterStruct]
.CODE:009DB18F push eax
.CODE:009DB190 mov eax, [ebp+unknown]
.CODE:009DB193 push eax
.CODE:009DB194 mov eax, [ebp+LastError]
.CODE:009DB197 push eax
.CODE:009DB198 mov ecx, [ebp+EspBeforeCallAip]
.CODE:009DB19B mov edx, [ebp+CalledFrom]
.CODE:009DB19E mov eax, [ebp+AIP_Data]
.CODE:009DB1A1 call EmulateCmpAndJccAndMove
Now ASPR has to check if emulation is needed, if so, it calculates Address of EmuOpcode,
EmuOpcode array is located at AipData.58h, and size of this opcodes is same as AipOpcode, or
AipData.E4h.
Till this point everything was more than good, but now we are facing a little problem.
EmuOpcode is crypted with certain Constants. Here we have a little problem in writing generic
unpacker for ASProtect, we have to find way to decrypt EmuOpcode. It is up to you to think how
you will decrypt them. One simple way it to locate those constants in ASPR code and simply
execute DecryptAipOpcode in your tool.
After that it is much simpler, again ASProtect extracts various data using AipProcIndexes and
AipProc but when those are used on EmuOpcodes effects are quite different:
As you may see this code is really simple, ASPR updated EIP, and if we have call then and only
then ESP is updated with ret address. It is very simple example, so for now we know this:
I think that from above disassembly it is pretty much clear how Destination is calculated.
Remember you only need to assemble jmp/call, DataIndex2 is used to calculate ret address and
when you assemble call to righ position, this is not needed at all
This emulated opcode is very simple, by looking at comments and my labels you may see that it
extracts Data from Destination Register and adds to it Destination Data, and then that result
moves to Destination Register. Special case is handled when ESP is updated, this occurs when
there is __cdecl API called such as wsprintfA for example. In this case valid ESP is not stored in
RegisterStruct, it is stored in EspBeofreAipCall, so thats the one that needs to be updated.
This one is very simple, ASPR extracts Data from SrcRegisters and stores it into DstRegister, just
plain mov reg, reg.
Here you may see how ASPR extracts data from DstRegister, then it extracts RVA from DstData
and adds to it ImageBase. After that you see mov [ebx], eax which is mov [mem], reg. Easy to
decode, not problem so far.
PAGE 27 ASPROTECT VM ANALYZE
.CODE:009DB434 __EmulateMovRegMem_Reg:
.CODE:009DB434 xor eax, eax
.CODE:009DB436 mov al, [esi+5]
.CODE:009DB439 lea eax, [eax+eax*2]
.CODE:009DB43C mov edx, [ebp+AIP_Data]
.CODE:009DB43F mov edx, [edx+eax*4+68h]
.CODE:009DB443 mov eax, ebx
.CODE:009DB445 call edx
.CODE:009DB447 mov [ebp+DstRegisterIndex], al
.CODE:009DB44A xor eax, eax
.CODE:009DB44C mov al, [esi+6]
.CODE:009DB44F lea eax, [eax+eax*2]
.CODE:009DB452 mov edx, [ebp+AIP_Data]
.CODE:009DB455 mov edx, [edx+eax*4+68h]
.CODE:009DB459 mov eax, ebx
.CODE:009DB45B call edx
.CODE:009DB45D mov [ebp+SourceRegisterIndex], al
Now we see that ASPR extracts Destination Data, and adds it to the content of DstRegister, this is
done so ASPR can form memory address, after that content of SourceRegister is moved to this
memory location. Remember that here you may have 3 completely different opcodes:
If (DataDst == 0)
mov [RegDst], RegSrc
if (DataDst <= 0xFF)
mov [RegDst+disp8], RegSrc
else
mov [RegDst+disp32], RegSrc
ASPROTECT VM ANALYZE PAGE 28
.CODE:009DB520 __emualtecmp_ONLY:
.CODE:009DB520 mov edx, [ebp+AIP_Data]
.CODE:009DB523 cmp al, [edx+4Dh]
.CODE:009DB526 jnz short ProtectionError_0
Order of jccs here is the same as in PolyOEP (when it comes to switch/case), cmp is a little bit
different as it now uses indexes from 2-6 instead 0-4.
.CODE:009DB4BC __emualtecmp_and_jcc:
.CODE:009DB4BC mov ecx, ebx
.CODE:009DB4BE mov edx, [ebp+RegisterStruct]
.CODE:009DB4C1 mov eax, [ebp+AIP_Data]
.CODE:009DB4C4 call EmulateCmp
.CODE:009DB4C9 mov [ebp+eflags], eax
.CODE:009DB4CC jmp short loc_9DB4CF
Locating this data is not nuclear physic is it only patience, and correctly naming variables in
code that you are analyzing.
First ASPR sets DstRegister, and SrcRegister to 0, and then extracts needed data from
EmuOpcode.
PAGE 29 ASPROTECT VM ANALYZE
Basicaly if Index of DstRegister is >= 8 then only thing used here is DestinationData. Yes, order of
extracting data is not same as is in PoleOep, where ASPR extracted first SrcData, and
SrcRegister, but it is still same stuff. Also note that Source and Destination data are not modified
with KEY as is done in ASPR Poly Oep.
Index 9 is what ASPR uses to extract type of instruction. ASPR then advances to extraction of
SrcData, and SrcRegister, remember that if SrcRegister is >= 8, only SrcData is used:
After that ASPR tells us more about cmp decoding in following code. By loking in debugger, and
analyzing it ASPR reveals to us that depending on cmp instruction type, SrcData, and DstData
are threated as RVA or imm32. I didnt see this kind of SrcData and DstData adjustment in ASPR
poly oep, so I can assume that Poly OEP in dll cant be used, or it can be used without cmp/jcc
emulation, or those offsets are rearranged earlier in ASPR layer
.CODE:009DAD26 loc_9DAD26:
.CODE:009DAD26 mov ebx, [ebp+DstData]
.CODE:009DAD29 add ebx, [ebp+DstRegister]
.CODE:009DAD2C mov esi, [ebp+SrcData]
.CODE:009DAD2F add esi, [ebp+SrcRegister]
Once ASPR rearnages all data, it goes to switch/case and decides which cmp to perform:
I didnt mention earlier, but just_compare_it label, is just comparing two values, so thats how it
got its name. As you may see, now cmp indexes are 2-6 instead 0-4 as it was in Poly OEP. Again
you may see bug here, as there is not supported IA32 comparation used where 32bit register is
compared with 8bit. So if you decode some instruction like this, well you got an error. I have no
idea if this is a decoy, or just error in programming, as ASPR logic doesnt allow this instruction to
be executed ever.
One last thing left to look at is how jcc type is extracted, I wont cover its decoding as it is
exactly the same as in Poly Oep.
.CODE:009DB4BC __emualtecmp_and_jcc:
.CODE:009DB4BC mov ecx, ebx
.CODE:009DB4BE mov edx, [ebp+RegisterStruct]
.CODE:009DB4C1 mov eax, [ebp+AIP_Data]
.CODE:009DB4C4 call EmulateCmp
.CODE:009DB4C9 mov [ebp+eflags], eax
Index 4 is used to extract type of Jcc, and then that extracted index is used to decide if jcc is
taken or not. Jcc emulation is same as in poly oep, so no need to show it twice.
.CODE:009DB50F __jccnottaken:
.CODE:009DB50F mov eax, [ebp+AIP_Data]
.CODE:009DB512 mov ebx, [eax+0E0h]
.CODE:009DB518 add ebx, [ebp+DataIndex2]
.CODE:009DB51B add ebx, [ebp+CalledFrom]
.CODE:009DB51E jmp short __freeEmuOpcodeMemory
JccDestination = 0;
if (JccTaken)
JccDestination = DataIndex3 + AipData.E0 + AipData.ImageBase;
else
JccDestination = CalledFrom + DataIndex2 + AipData.E0;
ASPROTECT VM ANALYZE PAGE 32
Here we only have to take care about destination when Jcc is taken, because we have to
assemble only jcc __destination_if_taken after fixed call dword ptr[].
4. Conclusion
Well as you may see to do all of this by hand would be really crazy, thats why you need to write
own tools for automatization. World of protectors is moving toward Virtual Machines, although,
ASPR virtual machine is simple it is good exercise for RCE skills, and good introduction to
wonderful world of Virtual Machines. I also hope, that you, as a reader, learnt how it is
important to write tools to automate process of unpacking.
5. References
ASProtect SKE Unpacking Apprach, deroko of ARTeam, http://tutorials.accessroot.com
Unpacking with Tracers I, NCR, http://tutorials.accessroot.com
Unpacking with Tracers II, NCR, http://tutorials.accessroot.com
6. Greetings
I wish to thank to all my mates in ARTeam, 29a group for one of the best e-zine, great unpackers
from www.unpack.cn, softworm, fly, shoooo, ColdFever for his hard work on UnThemida project,
and, of course, you for reading this document.
, deroko of ARTeam