ASProtect Unpacking Tut
Heres a tut I wrote quite awhile ago on unpacking a aspr'd target with ollydbg. Since andreageddon already wrote an indepth analysis for the essay section, i just decided to post mine here instead. some of you may have already read this, but it remained private for the most part.
Unpacking a Delphi App Protected with ASProtect 1.23 RC 4 with OllyDbg
I've tried to make this tut generic so it can be posted on sites that do
not allow the targets names and shit, but if anyone wants to know the
target, or if you just have some questions, email me. I'm not offended
by stupid questions.
OllyDbg v1.09d with isdebugpresent plugin.
ImpREC v1.6 with ASPR plugin.
Uncheck all the boxes in the exceptions tab in the olly options.
Set isdebugpresent plugin to Hide in olly.
Shift+F9 til you reach:
XOR DWORD PTR DS:[EAX],EAX
POP DWORD PTR FS:
CMP DWORD PTR DS:[11F7EB0],0
JE SHORT 011F3A13
LEA EAX,DWORD PTR SS:[EBP-8]
PUSH DWORD PTR SS:[EBP-4]
PUSH DWORD PTR SS:[EBP-8]
MOV EAX,DWORD PTR SS:[EBP-C]
CMP DWORD PTR DS:[EAX],0
JE SHORT 011F3A23
PUSH DWORD PTR DS:[EAX]
PUSH DWORD PTR SS:[EBP-10]
PUSH DWORD PTR SS:[EBP-14]
Set a breakpoint at the first RETN
shift + f9
Now we are gona find the stolen bytes:
Click on the ... button at the top of olly, right click and click the "Log to file"
Go back to the CPU window and press CTRL + T
Check "Command is one of these" and put " REP STOS BYTE PTR ES:[EDI] " in the box
and click ok. Now press CTRL + F11. Click the run trace button again close log file.
Now f8 into the RETN and Ctrl+A.
You should be at something similar to JMP DWORD PTR DS:[BF52C4]
Thats how aspr sets up its API's
f7 into the JMP and f8 over the crap till the retn.
f7 or f8 into the retn.
Should be at something that looks like MOV DWORD PTR DS:,EAX
Dump with LordPE like normal. ( Click the process name in the top box, the right click
the exe in the lower box and do a Dump -> Full ).
Back in Olly, f8 through the crap til the retn. You will come out after
the first call of the program.
0058C170 A4BA5800 DD Blah.0058BAA4
0058C174 00 DB 00 // OEP = 58C174 -- SAVE THE OEP
0058C175 00 DB 00
0058C176 00 DB 00 // This app has 12 Stolen bytes
0058C177 00 DB 00
0058C178 00 DB 00
0058C179 00 DB 00
0058C17A 00 DB 00
0058C17B 00 DB 00
0058C17C 00 DB 00
0058C17D 00 DB 00
0058C17E 00 DB 00
0058C17F 00 DB 00
0058C180 . E8 3FADE7FF CALL Blah.00406EC4
0058C185 . 8B1D 60095900 MOV EBX,DWORD PTR DS: // We come out here so scroll up
Close OllyDBG and restart the app on its own
Start imprec and load the process
Now press iat auto search straight away.
Now change the size to 1000 and click get imports.
Open that import up and scroll to the bottom
Click on Select invalid, then Trace Level 1.
Now click show invalid again and trace with the ASPR plugin.
Find the bottom valid API in the tree.
Cut everything below it. Keep deleting from the bottom up if
the bottom API is already in the list elsewhere.
In imprec, replace RestoreLastError with SetLastError (both in kernal32)
Look in kernel32 for GETACP, underneith it is an unresolved one.
This is always "FreeResource"
Click show invalid again and cut thunks
Fix the oep with lordpe.
Find out what the program was made with using PEiD.
Open the fixed exe in Olly
You should be at the shit with the 00's. Our stolen bytes.
Open up the log we made
Search for RETN 8
Will have to disassemble some different exe's and get used to the opening
patterns, the app that we used during this tutorial was delphi.
Scroll down until you reach PUSH EBP
Thats our first shit beings the app is delphi
Remember, in this example, we have 12 bytes.
It will be:
PUSH EBP 1 Byte
MOV EBP,ESP 2 Bytes
MOV EAX, Something 5 Bytes
Assemble the first two into the exe (Hit spacebar)
Our example logfile.
0120D971 Main PUSH EBP
0120D972 Main JMP SHORT 0120D975
0120D975 Main POP DWORD PTR SS:[ESP]
0120D979 Main MOV EBP,ESP EBP=0012FFC0
0120D97B Main SUB ESP,10
We know MOV EAX, ADDR = 5 bytes + Our Push and MOV we already found,
we've used 8 total bytes already. Meaning we are still missing 4.
SUB ESP,10 is 3 bytes so put it as the next one.
Lets look some more:
0120D981 Main ADD WORD PTR DS:[120D98B],7AF4
0120D98A Main PREFIX REP:
0120D98F Main ADD WORD PTR DS:[120D999],8F70
0120D998 Main PREFIX REPNE:
0120D99C Main LEA ESP,DWORD PTR SS:[ESP+ESI-1]
0120D9A0 Main SUB ESP,ESI
We are missing 6 bytes and 5 will be taken from the mov eax,
so we are missing 1. If we look there, it cant be any of them.
The ADD, PREFIX and LEA is aspr crap
SUB ESP, ESI = 4 bytes ... too big
0120D9A2 Main JMP SHORT 0120D9A7
0120D9A7 Main LEA ESP,DWORD PTR SS:[ESP+5A]
0120D9AB Main LEA ESP,DWORD PTR SS:[ESP-5D]
0120D9AF Main JMP SHORT 0120D9B4
0120D9B4 Main PUSH EBX
PUSH EBX = 1 byte so, we take that.
Now we need to find what is moved into EAX. So scroll further
down through the log, looking at all the values of EAX.
0120DB00 Main ROL EAX,8D EAX=757295B8
0120DB03 Main XCHG EAX,EBX EAX=7FFDF000, EBX=757295B8
757295B8 is way out of range, so futher down:
0120DB34 Main ADD EAX,4BF92A21 EAX=0058BACC
58BACC sounds good to me
so last one is MOV EAX, 0058BACC.
Now lets save em:
right click, goto copy to executable, then choose All modifications.
Click "Copy All" and a new window pops up with all the changes in grey.
Right click and save the file.
Run the newly fixed exe. If you're lucky, it might work and you're done, but
odds are, there are dips (Pointers used by ASPR that are no longer valid in
the unpacked exe. )
Open up the newly fixed exe in olly and f8 over the calls till it crashes.
Restart and trace into the call it crashed at. Keep repeating until you're
in the call that actually crashes.
Replace the first line of this call (Generally small call starting with
PUSH EBP) with RETN and continue tracingtil you find the next call that crashes.
These calls that crash us are actually leading to RaiseException, so, by
doing the RETN, we just bypass it. If there are any more calls like this,
do the same RETN trick.
Keep tracing over til you find the next one that will crash you, it may
look like this:
00579E54 55 PUSH EBP
00579E55 |. 8BEC MOV EBP,ESP
00579E57 |. 51 PUSH ECX
00579E58 |. 53 PUSH EBX
00579E59 |. 8B05 62724000 MOV EAX,DWORD PTR DS: ; <&kernel32.GetModuleHandleA>
00579E5F |. 8B18 MOV EBX,DWORD PTR DS:[EAX]
00579E61 |. FF33 PUSH DWORD PTR DS:[EBX]
00579E63 |. 895D FC MOV DWORD PTR SS:[EBP-4],EBX
00579E66 |. 8F03 POP DWORD PTR DS:[EBX]
00579E68 |. 8B45 FC MOV EAX,DWORD PTR SS:[EBP-4]
00579E6B |. 5B POP EBX
00579E6C |. 59 POP ECX
00579E6D |. 5D POP EBP
00579E6E . C3 RETN
You will see this one a lot. What it basically does is move the emulated api
into the shit, but we dont have emulated api's no more. So just change the PUSh
to a RETN.
Restart the app once all the crash calls have been fixed.
Click on the "/" button thats patches press space bar on all of the lines.
Search the program for things that no longer work or that crash it.
In my target here, one of the menu items originally brought up a message box
with the text "Filelist is empty. Do you still want to build ISO?", the newly
fixed exe however does nothing.
Open up the even newer fixed exe in Olly and right click in the CPU window.
Search for -> All referenced text strings. Search for the string.
Scroll up and find where the function starts and trace here in the fixed and the
packed app. One will most likely take a jump while the other doesn't or some shit.
My function looked like this:
00585C28 $ 55 PUSH EBP
00585C58 . E8 5B3FFFFF CALL dumped_f.00579BB8 // Lets look in this call.
00585C5D . 837D F8 00 CMP DWORD PTR SS:[EBP-8],0
00585C61 . 75 0A JNZ SHORT dumped_f.00585C6D // Packed app jumps here
Lets look inside the call
00579BB8 /$ 53 PUSH EBX
00579BB9 |. 8BD8 MOV EBX,EAX
00579BBB |. 8BC3 MOV EAX,EBX
00579BBD |. 8B15 F8005900 MOV EDX,DWORD PTR DS:[5900F8]
00579BC3 |. E8 48B0E8FF CALL dumped_f.00404C10
00579BC8 |. 5B POP EBX
00579BC9 . C3 RETN
In the packed app, MOV EDX,DWORD PTR DS:[5900F8] is a pointer
to your computers machine id. But in our unpacked app, the PTR
no longer exists.
Set a breakpoint there so we can remember it.
Now scroll with olly to the bottom till u see some unused space, 0's and shit.
0058C28C . 44 72 6F 70 54>ASCII "blah blah"
0058C29C . 29 00 ASCII ")",0
0058C29E 00 DB 00
0058C29F 00 DB 00
0058C2A0 00 DB 00
0058C2A1 00 DB 00
From here on, safe every change you make and its address somewhere.
At 0058C29F, select enough bytes to hold your name.
Right click -> binary -> edit.
Type in your name or some other random bullshit.
Lets go back to where we were, the place we set the breakpoint.
In olly u have the CPU windows, a middle window, and the hex wnidow.
If u click on that line u will see in the middle window DS:[5900F8]=
Right click and follow address in dump so we can change the pointer.
The address must be in reverse order, so select them 4 bytes.
Right click, binary edit
I stuck my name in at 0058C2A0 but yours will most likely be different.
so, enter: A0, C2, 58, 00
The DS:[5900F8]= should now say your name.
Scroll up/down a bit and you may see another call similar to that one
00579B88 /$ 53 PUSH EBX
00579B89 |. 8BD8 MOV EBX,EAX
00579B8B |. 8BC3 MOV EAX,EBX
00579B8D |. 8B15 F0005900 MOV EDX,DWORD PTR DS:[5900F0]
00579B93 |. E8 78B0E8FF CALL dumped_f.00404C10
00579B98 |. 5B POP EBX
00579B99 . C3 RETN
Lets fix this pointer too so do the same.
Run the app (F9) and it should all work fine now.
Restart the app make the changes to the CPU window again.
Copy to executable -> All modifications. Save file. Blah.
Open up this newest exe in Olly and do the Hex Dump changes.
Right click in the Dump, Copy to executable, Save file.
You're know done!!!! Woohoo.
We had to restart and change that shit again without the program
running because otherwise the variables are already unloaded into
memory and the unpacked exe wont run on anyone elses machine.
Good luck unpacking shit from here on out,
Hundreds of thousands of thanks to my good buddy who's taught me every thing
I know about unpacking.
I have been trying for a while now to get a working dump of Gene6 FTP Server, which is packed with ASProtect 1.23 RC 4 versions
The lastest ftp verions is 3.0.2 build 39, i have found the following from your tute-
Stolen bytes: 558BEC83EC10B828104900
And also i have resolved imports with imprec, next i try to place RETN in aspr dips, but not all the exceptions in my dump are in small funtions starting push ebp, i placed RETN anyways, but i dont get a working dump
aspr 1.23 is an old version. Could you please explain how to unpack version 1.31
instead, and specifically, which new obstacles i may face when attacking it. thank you!