How to unpack, crack and patch a packed copy of AZPR 3.01


You will need the following tools:

Softice
Icedump
Procdump
W32Dasm
HIEW
A hexeditor

Before I start: My greetings to Predator, a_ronin and tiamath for support in my efforts of understanding this protection system. My greetings also goes to the regulars at +Sandmans forum. Without you I would never have started to learn about code reversing.

This tutorial is written basicly for newbies.

First some words on the protection system itself. Asprotect is a powerful protector, and it can be hard to defeat if you are unexperienced in the art of cracking. It involves the packing routine you see in Aspack, but it uses  total different memory locations for it's unpacking, and it is a little bit more complicated. The unpacking routine includes anti Softice code and CRC check code. In this case (AZPR) you will find only one kind of anti Softice code (often refered to as Meltice), but in more recent Asprotected programs you will also see the SEH  protection. (If you want to see in detail what this and other types of anti Sice code really looks like, take a look at the help/text file that follows Frogsize). Excellent program. 

I will explain in detail how to unpack this program manually, and then how to patch the packed program. I'll do it in this order, because during the unpacking process I get some information I need in the patching process.The unpacked program can be run without trouble of any kind, but the patched packed file will still show the "debugger detected" message if you have Softice active . (I haven't figured out how to get rid of that without unpacking it). Maybe a nice challenge for the future? 

Okey. The first thing we need to do is to check out the program in Procdump. We need the virtual address and the virtual size of the .idata section. We need this so we know where to look for unpacking activity when we are stepping through the unpacking routine, and to know how many bytes of the code we need to dump later on. The virtual address is where in memory the .idata section is located while the program is loaded into memory, and the size shows how many bytes it occupies in the memory.I found the virtual address to be 23000 and the size to be 2000. We have to add the image base of the program (which in this case is 400000), so the whole address is 423000. Make a note of this numbers. We have to dump the import section while we're in the midst of the unpacking process. This is the only way we can get a fully working .idata section we can paste into the unpacked program later on. We can not use the .idata section in the unpacked program as it turns out in the end. After the import section is loaded into memory, and the program have utilized the information contained in the .idata section, the program overwrites some of the addresses within the .idata section with some other information as the unpacking continues. This is why we can't get a working dump of the program at the end of the unpacking routine. 

Now, after this, activate Icedump. We need this gem of a tool to dump some code while we unpack the program.

Then put a bpx _lopen in Sice, and run AZPR. When Sice breaks, push F5 and Sice breaks again. Push F11, and you should be back here:

0177:00660581  E84E3EFFFF          CALL      KERNEL32!_lopen                   
0177:00660586  40                  INC       EAX    --- you're here                           
0177:00660587  7425                JZ        006605AE                           
0177:00660589  8D55E4              LEA       EDX,[EBP-1C]                       
0177:0066058C  33C0                XOR       EAX,EAX                            
0177:0066058E  E89521FFFF          CALL      00652728                           
0177:00660593  8B45E4              MOV       EAX,[EBP-1C]                       
0177:00660596  8D55E8              LEA       EDX,[EBP-18]                       
0177:00660599  E8EA45FFFF          CALL      00654B88                           
0177:0066059E  8B45E8              MOV       EAX,[EBP-18]                       
0177:006605A1  E8D22DFFFF          CALL      00653378                           
0177:006605A6  8B5303              MOV       EDX,[EBX+03]                       
0177:006605A9  E822FAFFFF          CALL      0065FFD0                           
0177:006605AE  8A1508666600        MOV       DL,[00666608]                      
0177:006605B4  8B45F8              MOV       EAX,[EBP-08]                       
0177:006605B7  E810F9FFFF          CALL      0065FECC                           
0177:006605BC  8BD8                MOV       EBX,EAX                            
0177:006605BE  85DB                TEST      EBX,EBX                            
0177:006605C0  0F84F4000000        JZ        006606BA                          
0177:006605C6  8B4303              MOV       EAX,[EBX+03]

If you check the eax value now, you will see that it is C. Change that to FFFFFFFF (-1). (Type R eax ffffffff at the commandline). Then disable the breakpoint in Sice, we don't need it anymore. By doing this you have defeated the anti Softice code. We could have entered the call to the function _lopen, and then patched that call to return the correct value in eax. Or we could have changed some data strings while tracing through this call. But I like to keep things simple. The only thing we need here is to make sure the program jumps at the address :00660587. After forcing the program to jump, and after a few instructions, you end up at the conditional jump at :006605C0. If you force the program to jump here, you avoid the CRC check, and you can go on with the unpacking routine. But we need to reach the CRC check point in order to find out what the hard coded checksum is. So don't jump and keep on stepping until you reach this code:

0177:00660669  8B45EC              MOV       EAX,[EBP-14]                      
0177:0066066C  E8AB22FFFF          CALL      0065291C                          
0177:00660671  C3                  RET                                          
0177:00660672  E93926FFFF          JMP       00652CB0                           
0177:00660677  EBF0                JMP       00660669                           
0177:00660679  8B45F0              MOV       EAX,[EBP-10] 
0177:0066067C  3B45F4              CMP       EAX,[EBP-0C]--- CRC check                       
0177:0066067F  7439                JZ        006606BA    
0177:00660681  8A150C666600        MOV       DL,[0066660C]                      
0177:00660687  8B45F8              MOV       EAX,[EBP-08] 

If the program jumps at the address :0066067F, you will not see the "Invalid checksum" message. So if you just want to unpack it, just force the program to jump here. The code you see from the address :00660679 through :0066067F is characteristic for Asprotected program. You will find it in all of them, I think. I have seen them in the ones I have cracked. But we need the original checksum value when we are about to patch the packed file later on. At the address :00660679 the calculated checksumm is pushed into eax, and at the next instruction that sum is compared with the one stored at the location pointed to by [ebp-0C]. This is the original checksum. Write it down, you will need it later. (If you make changes in the code in the raw file, and then check the values here, you will see that the eax value changes, not the value pointed to by [ebp-0C]). If you type d ebp-0C at the Softice commandline, you will see this in the data window:

017F:0060FDDC 51 33 FA 62 D8 02 B9 00-EC FD 60 00 10 FE 60 00

The four first bytes are the checksum. In reversed order they are 62FA3351.


Now, let us concentrate on tracing and dumping the .idata section before it gets overwritten. Type in dd 423000 at the Softice commandline. You will see that your data window changes it's outlook to show four eight digit rows. So far they are all question marks. That means nothing has been loaded into this memory address yet. Cool. 
Now, just keep on single stepping until you see something is written into the data window. After the program has executed the call at the address :006609AD, you will see that some data is written into the address 423000. This is not the correct data we need. This is only a part of the unpacking of the .idata section. Just keep on stepping until some data is written once more into the address 423000. When that happens you should be here:

0177:00660A0A  8B4008              MOV       EAX,[EAX+08]                       
0177:00660A0D  E81EF7FFFF          CALL      00660130                           
0177:00660A12  33C0                XOR       EAX,EAX -dump idata section                           
0177:00660A14  5A                  POP       EDX

And the data window should look like this:

017F:00423000 000230DC  00000000  00000000  00023864      .0..........d8..     
017F:00423010 000234A0  000230F4  00000000  00000000      .4...0..........     
017F:00423020 00023871  000234B8  00023104  00000000      q8...4...1......      
017F:00423030 00000000  0002387E  000234C8  00023128      ....~8...4..(1..     
017F:00423040 00000000  00000000  0002388A  000234EC      .........8...4..

Use Icedump to dump the .idata section. I typed the following at the Softice commandline:

Pagein d 423000 2000 c:\idata.bin

We are dumping 2000 bytes of the loaded program starting at the address 423000. That leaves us a fully working .idata section for later use. 

The next thing to do is to step through the code intil we reach the end of the unpacking routine in order to get hold of the original entry point of the program, and to dump the whole program.

After some stepping you get here:

0177:00660B2E  8B4508              MOV       EAX,[EBP+08]                       
0177:00660B31  8B10                MOV       EDX,[EAX]                          
0177:00660B33  8B4508              MOV       EAX,[EBP+08]                       
0177:00660B36  035018              ADD       EDX,[EAX+18]                       
0177:00660B39  8B4508              MOV       EAX,[EBP+08]                       
0177:00660B3C  8B401C              MOV       EAX,[EAX+1C]                       
0177:00660B3F  E880F9FFFF          CALL      006604C4                           
0177:00660B44  5F                  POP       EDI                                
0177:00660B45  5E                  POP       ESI                                
0177:00660B46  5B                  POP       EBX                                
0177:00660B47  59                  POP       ECX

Enter the call at :00660B3F, and step carefully throught he code until you reach a ret instruction. The code your tracing through now is selfmodifying, so keep a close look at the code. Eventually you end up here:

0177:006604CE  EB02                JMP       006604D2                           
0177:006604D0  E9148B1D30          JMP       30838FE9                           
0177:006604D5  666600EB            ADD       BL,CH                              
0177:006604D9  01EB                ADD       EBX,EBP                            
0177:006604DB  89041C              MOV       [EBX+ESP],EAX                      
0177:006604DE  EB02                JMP       006604E2                           
0177:006604E0  EB02                JMP       006604E4                           
0177:006604E2  61                  POPAD                                        
0177:006604E3  EB01                JMP       006604E6                           
0177:006604E5  E850EB02E9          CALL      E968F03A                           
0177:006604EA  17                  POP       SS                                 
0177:006604EB  C3                  RET   --- you are here

When you're at the ret instruction, make a note of the eax value. It shows the original entry point (OEP) of the unpacked program. Here it is 401000.

Okey, let us dump the program. Make sure you have disabled all breakpoints before you do this. While you're still at the ret instruction, type "a eip", enter, "jmp eip", enter, enter and then exit Softice. You have now put the program loaded in memory into an endless loop, ready to be dumped. Run Procdump, mark the file azpr.exe, rightclick on it and choose full dump. Place the dumped file in the azpr directory after renaming it. Then remember to kill the task.

Now, let us rebuild the dumped file into a fully working copy. Open the PE editor in Procdump, and enter the file you just dumped. Change the entry point you see to the one you just wrote down.(I had to change 000AF001 to 00001000). Then open sections. We have to change some more numbers. As a start change the characteristics for the text section from C0000040 to E0000020. By doing this you make sure that the dumped file can be disassembled in W32dasm. Change the raw size for the .bss section to 00000000. If you don't do this, the program will not run properly. (Frankly I do not exactly know what happens here. I learned it from tiamath.) Next thing is to write down the raw offset for the .idata section. We have to know where in the unpacked file to insert our dumped .idata section.(For me the address was 00021800). One more thing to do here:
Open up directory in procdump and change the values you see for the import table to the values you saw listed under the virtual address and virtual size for the .idata section. ( I changed 000AFD1C and 000001D8 to 00023000 and 00002000).

Okey, now we are finished with Procdump. Exit Procdump, and open your hexeditor. Load the dumped file and the dumped .idata section.(I use Hexworkshop). Open the dumped file, and go to the raw offset for the .idata section. Scroll down to the end of it, and mark the whole block. (I marked the block from 21800 through 22FC0). The last word you see in the .idata section should be WriteFile. Delete the whole block, open up idata.bin and copy the exact same amount of bytes, starting from the beginning of the file, into the offset 21800 in the dumped azpr.exe. By doing this you have replaced a damaged .idata section with a fully working one.

You now have have a fully working unpacked copy of AZPR. So let us crack it, and then patch a packed copy.

There are several ways to crack this program. Tiamath and a_ronin have shown us two different ways to do it. Nice work guys. I will go for patching it into a fully working program, so we don't have to enter any serial at all.
I must admit that I don't exactly remember how I first came up with the approach I'm about to explain. But it works. Put a bpx regqueryvalueexa in Softice, and run AZPR. Softice breaks and after hitting F5 and a few F10'2, you will end up here:

0177:00412F69  E829FFFFFF          CALL      00412E97                          
0177:00412F6E  8D45C0              LEA       EAX,[EBP-40] -you're here                     
0177:00412F71  E804FEFFFF          CALL      00412D7A                           
0177:00412F76  C9                  LEAVE                                        
0177:00412F77  C3                  RET

I think I once tried to put a bpx on regqueryvalueexa in softice, and then checked the dictionary box in AZPR. If I recall this correctly, this function used to be disabled. After ending up as shown, I stepped over the instruction at :00412F71, and found that the returned eax value was 0. I changed it to 1, and tried to run the program. And the dictionary function worked. Call it a hunch, but after seeing this, I put a bpx at :00412F76, ran AZPR, and everytime Softice broke I changed eax=0 to eax=1. When the startup was completed, I had a fully functional program. So why not check out the call at :00412F71 and make sure it always returns eax=1?
There are several ways to patch this call so it always returns eax=1. this is what I did:

Change the conditional jump at :00412D8F from "7D07" to "EB00", and the jmp instruction at :00412D98 from E9E8000000 to E9E9000000. Check it out yourself. Or try to find another way to do it.

After this patch is made, we have a fully functional copy of AZPR. 

The next thing to do: Patch a packed copy into a fully functional copy.

I got really inspired by reading Predator's tutorial on the subject, and this is how I did it with AZPR.

We have to do two things: Patch the program to avoid the CRC check, and to patch the program so it runs as a fully functional program. It's logical to make the latter patch first. We know the OEP. It is 401000. If we remove the imagebase, we have 00001000. Let us search for this value in a hexeditor. First reverse the value. It then looks like this: 00100000. When we search for this, we find 14 occurances, and they are located at the following offsets:

A4
B0
E4
EB
17C
290
2867D
2875B
287D8
28B16
28B22
28D31
29817
2993F

This looks like a lot, but it is logical to exclude the first 6 occurances. They are located very early in the program and are a part of the PE header (some of them are). So let us concentrate on the last 8 ones. Load the packed AZPR in HIEW and check out the addresses (you can of course calculate them if you prefer):

2867D  - 4AF07D
2875B  - 4AF15B
287D8  - 4AF1D8
28B16  - 4AF516
28B22  - 4AF522
28D31  - 4AF731
29817  - 4B0217
2993F  - 4B033F

This is all the places in the raw file where the value of the OEP is located. When we run the program, the unpacking routine reads this value from one of these locations. We need to find out which one. Then we can change that value to another one in order to redirect the program to another location. At this location we will add some code, and then direct the program back to the OEP.

Start by going through the routine we did earlier. Bypass the anti Softice code and the CRC check and go to where the .idata section has been loaded. Then put a bpm xxxxxx r on the first four addresses you just found (the xxxxxx is the actual address). By doing this we command Softice to break every time some information is read from the addresses where we put a breakpoint.
After putting a bpm r at the four first addresses ( Softice only allows four bpm's at the time), it breaks here after hitting F5:

0177:00660B36  035018              ADD       EDX,[EAX+18]                      
0177:00660B39  8B4508              MOV       EAX,[EBP+08]-you're here                     
0177:00660B3C  8B401C              MOV       EAX,[EAX+1C]                       
0177:00660B3F  E880F9FFFF          CALL      006604C4                           
0177:00660B44  5F                  POP       EDI                                
0177:00660B45  5E                  POP       ESI                                
0177:00660B46  5B                  POP       EBX                                
0177:00660B47  59                  POP       ECX                                
0177:00660B48  59                  POP       ECX                                
0177:00660B49  5D                  POP       EBP                                
0177:00660B4A  C20400              RET       0004

(This code should be familiar to you. It is the code you step through right before the call that brings up the final ret instruction (and the OEP), and before the unpacked program starts to run.)

The command window in Softice reveals this information for us:

Break due to BPMB #0177:004AF516 R DR0  (ET=3.24 milliseconds)                  
  MSR LastBranchFromIp=00660B19                                                 
    MSR LastBranchToIp=00660B2E                           

At :00660B36 the value pointed to by [eax+18] is pushed into edx. If you check the edx value in the register window you'll see 00401000, and if you type d eax+18 at the commandline, the data window show this:

017F:004AF516 00 10 00 00 1C FE 60 00-9F B5 CE 0C 00 10 00 00  

It's quite clear that the address we're looking for is 4AF516.

Okey, look up the offset 28B16 in the hexeditor again. After finding it, take a look a couple of lines further down. There's a lot of empty space there. This looks like a good place to write in some new code. Okey, let us try. Why not use the offset 28B70 as a starting point. When I looked that offset up in HIEW, I found the address to be 4AF570. The value originally written at offset 28B16 is 00100000, which reversed will be the offset 00001000 (address 00401000). This makes the program to jump to the address 00401000. We want the program to jump to the address 004AF570. If we remove the imagebase value from the address, we get 000AF570. Reversed this gives 70F50A00, which is the value we have to replace the 00100000 at offset 28B16 with.

After doing that we have to write in some new code at address 004AF570.
In order to patch the program into a fully functional copy I have to change
The "7D07" instruction at address 00412D8F to "EB00", and the "E9E8000000" instruction at address 00412D93 into "E9E9000000" (actually I have only to change one byte).

The code to be written into the program at location 004AF570 will look like this:

0177:004AF570  66C7058F2D4100EB00  MOV       WORD PTR [00412D8F],00EB          
0177:004AF579  C605942D4100E9      MOV       BYTE PTR [00412D94],E9             
0177:004AF580  E97B1AF5FF          JMP       00401000

After this patching is done, the program will always go through this code first, and therefore continue as a registered copy.

The last thing we have to do is to find and change the checksum. First, go through the steps described above once more, and step through the code until you reach the CRC checkpoint.The value stored in eax at address :0066067C is the new one (for me it was C9AAB61C). Reverse it (for me it became 1CB6AAC9). Remember the one you found earlier on? Reversed it was 5133FA62. Load the patched copy in a hexeditor and search for this value. You will find it only at one place. Replace that occurance with the reversed value of the new checksum. Then you will never see the "Invalid checksum" message again.

That's it. This program is now cracked, patched and unpacked.

But remember, in the patched, packed version you will still get the debugger message when Softice is active.

"Remember, the best way to keep something is to give it away."


Hobgoblin, May 2000.










1


