ASProtect 1.23RC4 tutorial

Wednesday, April 13 2005 @ 04:57 PM CEST

Contributed by: haggar

Level : newbie

This is my tutorial about ASProtect. Well, actually it is my attempt to understand what ASProtect doing to packed programs. I wrote this tut maybe couple days ago and yet today I would change some parts if I had a time.

There is an 'interactive' Ollydbg plugin tutorial in the attachment, or you can just read the regular tutorial below.

Enjoy and post questions, commmets, corrections, ideas ..... I need all of them ;-)

__..--~~""~~--.._( Unpacking ASProtect )_..--~~""~~--..__

Find the Ollydbg plugin tutorial & Ollydbg asprotect plugins here : attachment

Lately I had some succses with unpacking ASProtect (half)manually so I have decided to share this experience with others (beginners).

Like many other noobs, I have read dozen of tutorials which describe unpacking ASProtect, but blindly following that tutorials I couldn't unpack anything by myself. Those "...just press Ctrl+F9 now and you will emediatley land on OEP..." or "...enter tc>900000 and hit run..." have take me usually to some exception that Olly couldn't pass. So I started to digging and pasing through ASPr code just to tray figure something myself. Well, I had some sucsess ;-)

1. We gonna need something for this tutor:

- target: Easy Video Capture v1.30 (~600kb in size) ,
- OllyDbg 1.10 (try to have it's plugins, esp. CommandBar and Dump)
- LordPE
- ImportREC

2. IsDebuggerPresent check:

First stepp is of course, to install our app and then load it to Olly.
ASProtect have option to detect debugger through IsDebuggerPresent API function. Some protected programs use that option and some doesn't which depends of app autor. I usually just run first time app in Olly just to see will I get that message "Debugger detected...". This option is just pain in the ass because we must spend some time to click on IsDebuggerPresent plugin in Olly if program uses this check and that is quite boring since usually I must restart target couple times. Buah, our target is using that check. Ok, find someware that plugin for Olly which fools this check because that is fastest way to pass it, but I'm gonna also describe manually how to do it in next part. And next part is:

3. Finding OEP:

Finding OEP is not some hard thinking proces, it's acctually well know Shift+F9 + counting exceptions and then watching jumps and lots of tracing through code.
So, go to Olly Debugging Options and uncheck all that exceptions except those memory violations in KERNEL32.DLL.

Also we must fool debugger check. In CommandLine plugin type "bp IsDebuggerPresent" and press ENTER; or if you have not that plugin right click on CPU window in Olly, select "Search for" ... "Names in all modules", find Eport KERNEL32.IsDebuggerPresent, right click and "Toggle brakpoint". Now just press Shift+F9 and after 15 times you should land in kernel:

77E7276B > 64:A1 18000000   MOV EAX,DWORD PTR FS:[18]
77E72771   8B40 30          MOV EAX,DWORD PTR DS:[EAX+30]
77E72774   0FB640 02        MOVZX EAX,BYTE PTR DS:[EAX+2]  <---Follow this address in dump
77E72778   C3               RETN

On first line is our breakpoint, remove it and go with F8 to 3-th line and follow that addres in dump:
7FFDF002  01 00 FF FF FF FF 00 00 40 00 A0 1E 24 00 00 00  _...@. _$...
Maybe in your case this will look diferent, but first byte must be 01. That byte is confirmation that debuger is present and you need set it on zero. Rigt click on that 01 byte in dump and chose "Bynary - Fill with 00's". It should look like this now:

7FFDF002  00 00 FF FF FF FF 00 00 40 00 A0 1E 24 00 00 00  ....@. _$...
You have just fooled debugger check. Probably all this you know from before.

Now we must continue with Shift+F9. I press first until bp 15 times and after 16 and then program has started. So I have pressed it 31 times. Now we need again but this time press it only 30 times. Also, you culd not even to count untill you triger bp in kernel and then count only last 16-1. You should land now here:

00B339EC   3100             XOR DWORD PTR DS:[EAX],EAX       <---- Your here on the exception!
00B339EE   64:8F05 00000000 POP DWORD PTR FS:[0]
00B339F5   58               POP EAX                          <---- Place bp here!
00B339F6   833D B07EB300 00 CMP DWORD PTR DS:[B37EB0],0
00B339FD   74 14            JE SHORT 00B33A13
00B339FF   6A 0C            PUSH 0C
00B33A01   B9 B07EB300      MOV ECX,0B37EB0
00B33A06   8D45 F8          LEA EAX,DWORD PTR SS:[EBP-8]
00B33A09   BA 04000000      MOV EDX,4
00B33A0E   E8 2DD1FFFF      CALL 00B30B40
00B33A13   FF75 FC          PUSH DWORD PTR SS:[EBP-4]
00B33A16   FF75 F8          PUSH DWORD PTR SS:[EBP-8]
00B33A19   8B45 F4          MOV EAX,DWORD PTR SS:[EBP-C]
00B33A1C   8338 00          CMP DWORD PTR DS:[EAX],0
00B33A1F   74 02            JE SHORT 00B33A23
00B33A21   FF30             PUSH DWORD PTR DS:[EAX]
00B33A23   FF75 F0          PUSH DWORD PTR SS:[EBP-10]
00B33A26   FF75 EC          PUSH DWORD PTR SS:[EBP-14]
00B33A29   C3               RETN                             <--- Or place bp here!

Now, I'm gonna tell you something from my experience. I have notice mostly two variations of ASProtect: first group have this last pice of code that you see above exactly the same while second have differnt with some calls and jumps. At this first group it is a very easy to find OEP, while for second one it needs to be more patient. Also, there are no anti-debug tricks after this period. To continue with our target, place bp on one of two places that I have marked on abowe code and run app. We have reall boring job now - we must trace with F7 to see what code is doing. It will circle around and around with conditional jumps. If you notice that behaviouring, place bp under couple such jumps and run program to save time, for example:
After you are on that last RETN, continue with F7. Look where jumps are throwing you and you'll see that loot of jumps has no purpose at all - they throw you at the same place where you going to pass when they don't execute. After some time you will find yourself here:

00B45ED4   81EA 314F0F4A    SUB EDX,4A0F4F31
00B45EDA   0F81 1F000000    JNO 00B45EFF
00B45EE0   E8 08000000      CALL 00B45EED     <------------------------ Place bp here!
00B45EEC   AB               STOS DWORD PTR ES:[EDI]
00B45EED   E8 0B000000      CALL 00B45EFD
00B45EF2   87B4DD 522320D9  XCHG DWORD PTR SS:[EBP+EBX*8+D9202352],E>
00B45EF9   9E               SAHF
00B45EFA   7F 4C            JG SHORT 00B45F48
00B45EFC   95               XCHG EAX,EBP
00B45EFD   59               POP ECX
00B45EFE   5E               POP ESI
00B45EFF   81C2 2D4F0F4A    ADD EDX,4A0F4F2D
00B45F05   81EB 4D667D59    SUB EBX,597D664D
00B45F0B   81FA 54EBFFFF    CMP EDX,-14AC
00B45F11  ^0F85 5BFFFFFF    JNZ 00B45E72
00B45F17   E8 0E000000      CALL 00B45F2A     <----------------------- Place bp here!
00B45F1C   4E               DEC ESI
00B45F1D   6F               OUTS DX,DWORD PTR ES:[EDI] 00B45F1E   7C 05            JL SHORT 00B45F25

You just keep on tracing with F7 and you'll see that these two jumps just spining you around. So place bp under them on those calls and run app. You will land on that second call and some code below will change. Continue with F7 and you'll find one more jump that is doing similar thing:

00B44E14  ^75 E9            JNZ SHORT 00B44DFF
00B44E16   5E               POP ESI        <--------------- Place bp here and run app!
00B44E17   5B               POP EBX
00B44E18   5D               POP EBP
00B44E19   C2 0800          RETN 8
00B44E1C   0102             ADD DWORD PTR DS:[EDX],EAX
00B44E1E   C3               RETN

Place bp on marked place and run target. Continue with F7 and you'll land on POPAD opcode after which one there are lots of JMP's. This pice is standard in this version of ASProtect and this is place where ASProtect is stealing bytes (but later about that). Pass this junk of code with F7 to the last RETN at:

00B45000   68 C3F44000      PUSH 40F4C3
00B45005   68 DB4DB400      PUSH 0B44DDB
00B4500A   C3               RETN   <--------- Until this RETN
Then execute that RETN:

00B44DDB   EB 01            JMP SHORT 00B44DDE
00B44DDD   9A 51579CFC BF1F CALL FAR 1FBF:FC9C5751
00B44DE4   4E               DEC ESI
00B44DE5   B4 00            MOV AH,0
00B44DE7   B9 5E140000      MOV ECX,145E
00B44DEC   F3:AA            REP STOS BYTE PTR ES:[EDI]
00B44DEE   9D               POPFD
00B44DEF   5F               POP EDI
00B44DF0   59               POP ECX
00B44DF1   C3               RETN   <-------------------- Put bp here!

Now you can trace further with F7 (if you want to see what is doing here - nothing special) or put bp on this new RETN and start target. You will see that lot of code has changed at the bottom of the Olly window, but notice where this last RETN is returning you! It returns you to VideoCap.0040F4C3. Execute that RETN with F7 and youl land here:

0040F4C3   FF15 30174100    CALL DWORD PTR DS:[411730]               ; msvcrt.__set_app_type
0040F4C9   59               POP ECX
0040F4D8   FF15 38174100    CALL DWORD PTR DS:[411738]               ; msvcrt.__p__fmode

That oppcode at 0040F4C3 is our false OEP!

Press Ctrl+A to analize code and scroll a litle bit up. Those zeroes are stolen bytes/opcodes/instructions and real OEP starts at first zero byte at 0040F496!

0040F490   $-FF25 50174100  JMP DWORD PTR DS:[411750]                ;  msvcrt._ftol
0040F496     00             DB 00   <-------------------------This shuld be real OEP.
0040F497     00             DB 00
0040F498     00             DB 00
0040F4C0     00             DB 00
0040F4C1     00             DB 00
0040F4C2   . 00FF           ADD BH,BH
0040F4C4   . 15 30174100    ADC EAX,VideoCap.00411730
0040F4C9   . 59             POP ECX
0040F4CA   . 830D F4AE4100 >OR DWORD PTR DS:[41AEF4],FFFFFFFF
0040F4D1   . 830D F8AE4100 >OR DWORD PTR DS:[41AEF8],FFFFFFFF
0040F4D8   . FF15 38174100  CALL DWORD PTR DS:[411738]               ;  msvcrt.__p__fmode

Now you have founded false and right OEP! False is at the 0040F4C3 and real is at the 0040F496 address! Remember that two addresses!!! What is false and what purpose of false OEP I will explain in next part:

4. False and right OEP, stolen bytes:

What is this thing with false-real OEP and stolen bytes? From where these bytes are stolen and why? This used to confusing me in the beining reading others tutorials.

The answer is very simple:
You know that every program has it's beginning, first line of code to process. That first line is OEP. You also know that packers unpacking program in memory and then execute it right from that OEP. ASProtect is doing that litle diferent. ASProtect copyes first couple instructiones from packed program to itself code and substitute those in program with zeros. After unpacking program in memory it executes those couple instructions (that should be in original program) within it self code and then continue to run original program from that address after that zeroes. Now we gonna find stolen bytes. These bytes can be little hard to determine because ASProtect mix them with some junk code just to confuse reverser. Only in one version of ASProtect I have fand those stolen bytes in original form.
Also you must know that usualy programs compiled with same compiler have same or similar first instructions. That can help you because if you open some other nonpacked program in Olly which is compiled with same compiler you can see how OEP shuld look like and you'll know when you see stolen bytes somewhere in ASProtect code.

Now we gonna find that bytes. At first look, this program look like it's compiled in some MS Visual C++. Open couple nonpacked programs in Olly and scan them with PEiD and you will easy notice diference between Delphy, Borlland's C++, MS Visual C++ ... In most cases your target will be compiled in some MSV C++.
So now restart target in Olly, pass that exceptions and debugger check again and stop at POPAD opcode after which you can notice lot of jumps:

00B45134   61               POPAD
00B45135   F2:              PREFIX REPNE:                            ; Superfluous prefix
00B45136   EB 01            JMP SHORT 00B45139
00B45138   F0:EB 01         LOCK JMP SHORT 00B4513C                  ; LOCK prefix is not allowed
00B4513B   698D 6424D683 C4>IMUL ECX,DWORD PTR SS:[EBP+83D62464],EBF>
00B45145   02CD             ADD CL,CH
00B45147   2089 442400F2    AND BYTE PTR DS:[ECX+F2002444],CL
00B4514D   EB 01            JMP SHORT 00B45150
00B4514F  -E9 C1E8E136      JMP 37963A15
00B45154   EB 01            JMP SHORT 00B45157
00B45156  -0F83 E0413EEB    JNB EBF2933C
00B4515C   02CD             ADD CL,CH
00B4515E   2068 64          AND BYTE PTR DS:[EAX+64],CH
00B45161   51               PUSH ECX
00B45162   B4 00            MOV AH,0
00B45164   58               POP EAX
00B45165   FF50 09          CALL DWORD PTR DS:[EAX+9]
00B45168   0FF39A F29A7751  PSLLQ MM3,QWORD PTR DS:[EDX+51779AF2]
00B4516F   B4 00            MOV AH,0
00B45171   F0:E8 F2F269F3   LOCK CALL F41E4469                       ; LOCK prefix is not allowed
00B45177   58               POP EAX
00B45178   EB 01            JMP SHORT 00B4517B

This place contains our stolen bytes but also lot of junk code. Trace through with F7 and you will keep finding byte by byte. All that jumps and weird opcodes are just junk and I will show here just real stolen bytes:

00B451B7   55               PUSH EBP           This is first stolen byte and it belongs on OEP.

00B451C0   8BEC             MOV EBP,ESP                  This is the next stolen part.
00B451C2   6A FF            PUSH -1
00B451C4   68 D8354100      PUSH 4135D8
00B451C9   68 1CF64000      PUSH 40F61C
00B451CE   64:A1 00000000   MOV EAX,DWORD PTR FS:[0]

00B463F9   50               PUSH EAX                      This one too.

00B45206   64:8925 00000000 MOV DWORD PTR FS:[0],ESP      Next part.
00B4520D   83EC 68          SUB ESP,68

00B45239   53               PUSH EBX 

00B4526B   56               PUSH ESI

00B4529D   57               PUSH EDI

00B452A6   8965 E8          MOV DWORD PTR SS:[EBP-18],ESP    This is last stolen pice.
00B452A9   33DB             XOR EBX,EBX
00B452AB   895D FC          MOV DWORD PTR SS:[EBP-4],EBX
00B452AE   6A 02            PUSH 2

Of course you will need some experience to recognize real bytes but that will come after few unpacked targets. This code above is all that is stolen. Trace further until you reach here:

00B452B8   68 C3F44000      PUSH 40F4C3
00B452BD   68 9350B400      PUSH 0B45093
00B452C2   C3               RETN

Look at that PUSH 40F4C3 opcode. Do you remmber address of the false OEP? If not, than scroll up in this tutorial where I told you to remember OEP's addresses! Yep, that opcode pushes false OEP address! Change that address to 0040F496 which is address of the real OEP by pressing SPACE and changing opcode to PUSH 0040F496. It should look now:

00B452B8   68 96F44000      PUSH 40F496
00B452BD   68 9350B400      PUSH 0B45093
00B452C2   C3               RETN

Now trace further:

00B45093   EB 01            JMP SHORT 00B45096
00B45095   9A 51579CFC BFD7 CALL FAR D7BF:FC9C5751                   ; Far call
00B4509C   50               PUSH EAX
00B4509D   B4 00            MOV AH,0
00B4509F   B9 5E140000      MOV ECX,145E
00B450A4   F3:AA            REP STOS BYTE PTR ES:[EDI]
00B450A6   9D               POPFD
00B450A7   5F               POP EDI
00B450A8   59               POP ECX
00B450A9   C3               RETN       <------------- Put bp!

Place bp on this last RETN, run target, remove bp, execute RETN with F7 and you'll land on real OEP address right on that first zeros. Pres Ctrl+A and it suld look like this:

0040F496     00             DB 00
0040F497     00             DB 00
0040F498     00             DB 00
0040F499     00             DB 00
0040F49A     00             DB 00
0040F49B     00             DB 00
0040F4BD     00             DB 00
0040F4BE     00             DB 00
0040F4BF     00             DB 00
0040F4C0     00             DB 00
0040F4C1     00             DB 00
0040F4C2   . 00FF           ADD BH,BH
0040F4C4   . 15 30174100    ADC EAX,VideoCap.00411730
0040F4C9   . 59             POP ECX
0040F4CA   . 830D F4AE4100 >OR DWORD PTR DS:[41AEF4],FFFFFFFF
0040F4D1   . 830D F8AE4100 >OR DWORD PTR DS:[41AEF8],FFFFFFFF
0040F4D8   . FF15 38174100  CALL DWORD PTR DS:[411738]               ;  msvcrt.__p__fmode
0040F4DE   . 8B0D E8AE4100  MOV ECX,DWORD PTR DS:[41AEE8]
0040F4E4   . 8908           MOV DWORD PTR DS:[EAX],ECX
0040F4E6   . FF15 84174100  CALL DWORD PTR DS:[411784]               ;  msvcrt.__p__commode

Now you need to put in all that stolen bytes that we have founded before. Do it by asembly or by binary editing. At the end it should look like this (blue was stolen):

0040F496  /. 55             PUSH EBP
0040F497  |. 8BEC           MOV EBP,ESP
0040F499  |. 6A FF          PUSH -1
0040F49B  |. 68 D8354100    PUSH VideoCap.004135D8
0040F4A0  |. 68 1CF64000    PUSH VideoCap.0040F61C     ;  JMP to msvcrt._except_handler3
0040F4A5  |. 64:A1 00000000 MOV EAX,DWORD PTR FS:[0]
0040F4AB  |. 50             PUSH EAX
0040F4AC  |. 64:8925 000000>MOV DWORD PTR FS:[0],ESP
0040F4B3  |. 83EC 68        SUB ESP,68
0040F4B6  |. 53             PUSH EBX
0040F4B7  |. 56             PUSH ESI
0040F4B8  |. 57             PUSH EDI
0040F4B9  |. 8965 E8        MOV DWORD PTR SS:[EBP-18],ESP
0040F4BC  |. 33DB           XOR EBX,EBX
0040F4BE  |. 895D FC        MOV DWORD PTR SS:[EBP-4],EBX
0040F4C1  |. 6A 02          PUSH 2
0040F4C3  |. FF15 30174100  CALL DWORD PTR DS:[411730]             ;  msvcrt.__set_app_type
0040F4C9  |. 59             POP ECX
0040F4CA  |. 830D F4AE4100 >OR DWORD PTR DS:[41AEF4],FFFFFFFF
0040F4D1  |. 830D F8AE4100 >OR DWORD PTR DS:[41AEF8],FFFFFFFF
0040F4D8  |. FF15 38174100  CALL DWORD PTR DS:[411738]             ;  msvcrt.__p__fmode

Now dump target with LordPE. If you scan it with PEiD you will found --> Microsoft Visual C++ 6.0. Our target is unpacked and stolen bytes are returned but we need to repir IAT.

5. Fixing IAT:

We will repair IAT using ImpREC but ASProtect is using one feature which make harder to do that job. I'm not pozitive sure if I'm right (somebody correct me if not) but ASProtect redirects some part of IAT jumps. I think that idea is to redirect jump in IAT table to some other location and there litle encrypt that jump, something like :

XOR EAX,76543210
jmp EAX

Above is just example of code that ImpREC cannot read. When you unpack target, that jump in IAT that leads to this example now leads to nothing and our target is crushing. Tankfuly, there are peoples who know about this more than me and they have made some plugins for ImpREC to fix this. To repair IAT use the plugins in the attachment.