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