unpacking Asprotect version 1.0
unpacking Asprotect version 2000
The encrypted import table
Written by Tsehp
Asprotect is a well know target, this packer was studied a lot and often used as a target practice for newbies. If you don't know anything about packing, improve your knowledge on the past essays, then come back, I'll try to explain you their last tricks. I use to find this target while looking for the sandman's newbie msgboard. Thanks to a reverser called SV that explained very well the protection on his threads, his work made me save a lot of time.
Important : This essay can bring you a working app with your actual system, but the import table is not rebuilt, so if you update your OS and the dll's addresses changes, it will not work anymore and you'll have to do the entire process again. I'm actually working on another essay to finalize the reconstruction of the import table.
An hex editor
Essay made on win 2000, expect to change (later in this essay) some import addresses if you're working on win 9x.
This is Commview 2.1, another portsniffer.
1) The anti-softice routines
Before lauching this app, you have to hide your debugger. This prog uses two typical anti softice tricks:
-Is the driver loaded ? : bpx createfileA do "d esp-4"
Commview tries to see if three different versions of softice are loaded. You will hit three times : SIWDEBUG SICE NTICE
Well it depends on the system you're on, press F12 at each time, the createfilea must return 0xFFFFFFFF If not, press F12 again and set your eip to the address pointed by the second jnz below, you will pass all the tests.
-The boundschecker trick. Sice uses int3 for its breakpoints, but when special values are set to register, especially ebp containing 'BCHK' 0x4243484b, then if an int3 is encountered, the normal interrupt handler just returns after this Int3 inside your target, so the code just after is executed if sice is present. To avoid this, press F10 several times after setting your eip, go slowly through the code until you see the int3 in sice. When you're on it, change your ebp to something else than 0x4243484b, then the program will run normally until a last check detected by bpx createfilea, just as above.
For the final check, just invert the jz after the createfileA, all the tests are now defeated.
Note : I used to munually do that, because frogsice is not actually implemented on win NT, the actual copy on nt called Ntall is not fully working, so I couldn't use a loaded device to avoid those manipulations.
2) Locating the Entry point.
If you want to dump your packed program, you must stop the target here, so all the sections would be loaded. I have to admit that I directly used the technique that Eternal Bliss found on the newbie's forum. Here it is, untouched :
Steps: 1) bpx on getprocaddress 2) Once you break, look at the .idata section by "dd 4E3000". This is found by doing "map32 cv" 3) bpm on the 1st byte (just to break there) and disable getprocaddress breakpoint 4) F5 and let the program run 5) Upon breaking, disable all breakpoints 6) F12 4 times and you will land at a Xor EAX, EAX 7) Start tracing with F10 8) Everytime you come across a CALL, check that a few instructions below are JMP 9) If it is, you can trace over it 10) If it is a ret, you have to trace into that CALL with F8 11) Then step over the 2 subsequent CALLs 12) You will see the OEP moved into EAX somewhere there 13) Then you will come across a POPAD 14) The JMP stuff appears again like the start of the unpacking code. 15) Trace into the next (3rd) Call and F8 all the way till you hit a ret 16) Continue with it and you will land in on the OEP. Just apply this and you will find it, 0x4de384 on my system. Just remember that you will find almost all the time a POPAD instruction just before the final jump.
Just as an exercise, you can try to dump the exe, using icedump and procdump to rebuild the pe (this will not be explained here, if you don't know how to do this, just look at the past packer/unpacker essays).
You have to manually hex copy the .aspr original section to your dumped file to continue the essay, this manipulation will just replicate the partial import table to your dumped exe, just like the packed commview.
But this will not work, just try to load your dumped exe inside ida and you will notice that the import table is damaged, some virtual addresses in other sections are also pointed to wrong places.
The main protection of Asprotect is just here : Some imports are fine, some other are pointing to a table (located in mem at 0x00CDxxxx) normally containing some decrypted import's addresses, and this table originally contains also some routines to decrypt other addresses when commview needs them, even if this prog is unpacked.
Well we have here a big problem : You can manually fix the import table if you convert all the tables addresses to the .idata at the right place.
So if inside you dumped app, inside .idata you have
4e328c : jmp [cd4950] and cd4950 containing 77db858e (a valid import address)
you have to convert the jump at 4e328c to directly point at 77db858e.
You will spend a lot of time, then it will not work. Because inside the cdxxxx table, some addresses points to code like this : cd4950 push cd495a call 182f68 (for example)
Whats happening ? When commview does call 4e328c (import table) the routine located at cd4950 just decrypts the right import adress AT RUNTIME ! You can also try to dump the cdxxxx table, the decrypting code will still be here and definitely not working. This feature is the real improvement made since the last Asprotect versions.
To summarize, four different things happens to the import table : 4 different cases in my following prog 1- api's address is copied directly to .idata 2- api's address is relocated to the Cxxxxx table 3- decypting routine is copied to the Cxxxxx table 4- an indirect call to GetprocAddress (in Cxxxxx table) is copied to .idata
3) Preparing to dump
You are still on the int3 instruction, just do a S 0 L FFFFFFFF AC 08 C0 74 E4 and you will localize the *culprit* loop that builds the import table, just do a bpmb at the start.
Here is what you find :
001B:0018A51E AC LODSB 001B:0018A51F 08C0 OR AL,AL 001B:0018A521 74E4 JZ 0018A507 001B:0018A523 4E DEC ESI 001B:0018A524 56 PUSH ESI 001B:0018A525 53 PUSH EBX 001B:0018A526 80F802 CMP AL,02 001B:0018A529 7407 JZ 0018A532 001B:0018A52B 0FB64E01 MOVZX ECX,BYTE PTR [ESI+01] 001B:0018A52F 41 INC ECX 001B:0018A530 EB05 JMP 0018A537 001B:0018A532 B904000000 MOV ECX,00000004 001B:0018A537 41 INC ECX 001B:0018A538 01CE ADD ESI,ECX 001B:0018A53A E8B5FDFFFF CALL 0018A2F4 <- redirect this point note the address 0x18a2f4. 001B:0018A53F AB STOSD 001B:0018A540 EBDC JMP 0018A51E <- note this address too, it's the loop back jump. All we have to do is to find a free mem location filled with 0 so we can write a small code to modify this routine. The address 0x190000 will be used as an example.
First patch 18a53a jmp 190000
Then write the following code in mem with softice :
190000 call 18a2f4 ; we use the legal call 190005 cmp ecx,40000000 ; ecx contains an api address ? 19000b jle 19001c ; if not 19000d add ecx,eax ; if yes convert the address***case 2 19000f add ecx,5 ; to the absolute api address for a normal import table 190012 mov dword ptr [edi],ecx ; put it in import table, pointed by edi 190014 add edi,4 ; updates edi to the next import 190017 jmp 18a51e ; back to normal 19001c cmp ecx,0 ; is it case 4 ? 19001f jz 19002e 190021 cmp eax,40000000 ; eax contains an api address ? 190026 jle 19003c ; if not go to 19003c 190028 stosd ; we're in case 1, direct copy of valid api address in import table 190029 jmp 18a51e ; back to normal 19002e push dword ptr [eax+1] ; we're in case 3, pushes the encrypted api address 190031 call 16df80 ;IMPORTANT you have to locate this Asprotect address by doing a S 0 L FFFFFFFF 55 8b ec 81 c4 f8 fe ff ff 53 56 so we found as example 16df80, you MUST modify the code at offset 16df80+95 with three nops (90), if you don't this call will generate an error 13 in Asprotect (the call doesn't return properly) this call is used to decypher the encrypted api address, normally decrypted at run time
190036 stosd ; the api's address is then decrypted, copy it to import table 190037 jmp 18a51e ; back to normal, don't forget that this is the loop back jump 19003c mov eax,KERNEL32!GetProcAddress ; use softice to have your system's getprocaddress. this is case 4 190041 stosd ; copy to import table 190042 jmp 18a51e ; back to normal
When you are finished typing this, put a bpmb to the prog's starting point (I found 0x4de384 ) then launch, if you typed correctly, the import table is now normal and you can dump it using icedump :
PAGEIN D 400000 12d000 c:\temp\cv_dump.exe
Then use procdump to rebuild the pe.
Very important : Use those procump options when rebuilding the pe : Structure : all enabled Import : don't rebuild import Leave other options untouched. You have to manually copy the .aspr section from cv.exe (original) to cv_dumped.exe, using your favorite hexeditor. As an example I found inside original raw offset : 5aa00 length 14800 dumped raw offset : ff400 length 14800, this manipulation will copy back the original import table.
Modify the entry point to 000de384 inside procdump, so your dumped exe will start at 4de384. Copy your dumped exe to the commview directory.
Now if you launch it, it will direct exit, because there is a crc check, we have to patch this code inside the dumped cv_dumped.exe : 004DE3B9 call sub_4524A0 004DE3BE mov eax, [ebp+var_10] 004DE3C1 call sub_465118 004DE3C6 cmp eax, 6F200h 004DE3CB jz short loc_4DE3D2 <- patch to jmp 4de3d2 004DE3CD call sub_403BB4
Don't forget one thing, there is still an anti softice left. Reactivate the bpx createfilea do "d esp->4"; press F12 when it hits then invert the jz just after.
Launch again, it's now working and you're ready to continue the reversing of this crippled, time trial exe.
4) What's left to crack ?
Now the real work is finished, we can patch whatever we want inside. I will easily give you the patches, just because this section was not the main part of my essay, this will surely save you some work. To finish its agony, we dwelve with :
-Anti softice routine : typical one, just after the well known createfileA : 0046573E call sub_406D0C (createfileA with softice driver as an argument) 00465743 00465743 crack5: 00465743 cmp eax, 0FFFFFFFFh is it loaded ? 00465746 jz short loc_465750 <- patch this to jump 00465748 push eax 00465749 call sub_406CEC 0046574E mov bl, 1 -The time limit :You usually find those after a bpx to getlocaltime, and trace after.
004D8D5B call sub_44EB74 ; this call contains a call to getlocaltime 004D8D60 004D8D60 loc_4D8D60: ; CODE XREF: sub_4D899C+386j 004D8D60 mov eax, ds:dword_4E051C 004D8D65 cmp byte ptr [eax], 0 ;is the trial finished ? 004D8D68 004D8D68 jnz short loc_4D8DB8 <- force this to jump 004D8D6A mov eax, ds:dword_4E0378
-half of the packets displayed : an easy one with ida, just find the reference to string :
0048B5A4 push offset loc_48BB1C 0048B5A9 push dword ptr fs:[eax] 0048B5AC mov fs:[eax], esp 0048B5AF cmp byte ptr [ebx+5Fh], 0 0048B5B3 0048B5B3 nop ; nop those so it never jumps to 48b5c2, you guessed why 0048B5B4 nop 0048B5B5 lea edx, [ebp-14h] 0048B5B8 mov eax, [ebx+22h] 0048B5BB call sub_48A7C0 0048B5C0 jmp short loc_48B5CF 0048B5C2 ; --------------------------------------------------------------------------- 0048B5C2 lea eax, [ebp-14h] 0048B5C5 mov edx, offset aDataThisEvalua ; "DATA:THIS EVALUATION VERSION DISPLAYS O"... 0048B5CA call sub_403E08 you have two more places to do the same, find them and correct the *bug* .
-the annoying nag when you leave commview: I found it after a bpx to createwindowsexa, you have to press F12 a lot of times to find this routine : 00459718 loc_459718: ; CODE XREF: sub_45969C+6Cj 00459718 ; sub_45969C+70j 00459718 mov eax, [ebp+var_C] 0045971B mov edx, [eax] 0045971D nop <- you have to nop this call to avoid the nag 0045971E nop 0045971F nop 00459720 nop 00459721 nop 00459722 nop 00459723 mov [ebp-8], eax 00459726 xor eax, eax 00459728 pop edx 00459729 pop ecx 0045972A pop ecx 0045972B mov fs:[eax], edx 0045972E push offset loc_459743 00459733 00459733 loc_459733: ; CODE XREF: CODE:00459741j 00459733 mov eax, [ebp+var_C] 00459736 call sub_403024 0045973B retn Now you can enjoy your new portsniffer, auto rebuilded by itself, just like it always should be.
I need your feedback for this essay, so please write your comments after trying it at the msgboard : http://www.insidetheweb.com/mbs.cgi/mb155985
You can join all the reverser's forums at sandman's , where I had this idea : http://www.insidetheweb.com/mbs.cgi/mb628842
Or on the fravia msgboard, for intermediate and advanced ones : http://www.insidetheweb.com/mbs.cgi/mb155985
Thanks to Eternal Bliss ,r!sc and SV for their preliminary work on that interesting target.