=========== The LOCKLESS Team ============ Prof. Pinker ppinker@lockless.com ========================================== How to crack: WINPROXY 3.0R1E ========================================== Toolz to use: Winhex 9.26 SoftIce 3.2 Turbo Debugger 5.0 ========================================== Since many people asked to me how to patch a runtime self-extracting EXE I thought that Winproxy was the best example to show this. First of all let's focus on our targets: - During the evaluation period the program is fully functional and every time we start it, a nag remembers us that it's not registered and that it will expire. - After the evaluation period the software expires. When expired if we try to start it, a nag appears remembering us to register it and if we click NO, the main window appears but *ALL* the services are disabled. We observe that taking back the system date winproxy starts again as not-expired. Ok. Let's start. I immediately try to disassemble the WINPROXY.EXE with Win32DASM v8.9 , but it fails. In my case an exception is generated and Win32DASM is closed. Why? It's simple. If you success to disassemble it you'll see that the great part of code is of no sense. This happens because the program is encrypted and packed and the only part of it which is not encrypted is the piece of code who decrypts run-time all the other parts of the EXE file. So the best way to understand how to crack this program is to use a debugger. In fact with a debugger we can run the program until it is decrypted in the ram and then we can have a look to the decrypted code and trace it. I strongly suggest to use Turbo Debugger since there's no reason for now to use SoftIce (more powerful this one). So let's open WINPROXY.EXE with a debugger and start to trace it. We are sure that there's a call to a routine which decrypts all the program in the ram, but we dunno where is it. How can we find it? Just step over every call and when the computer spends almost a second to execute that call then we have probably found our decrypting routine. We observe that at the line: 545303 call 545679 the computer executes it not so immediately! You can find confirm that this is the decrypting routine just having a look at the offset 401000 before and after executing the call at 545303. Something is really changed! So we have found the decrypting routine. Now all the code in ram is decrypted! Wow! And the line: 54530E jmp eax gives the control to the decrypted program. You can now dump the decrypted code and the disassemble it, but I think there's no need to do this. Now we need to find where the program decides to display the expiring nag after failing to check the system date. The simpliest method to do this is: - Open WINPROXY expired and when the expiring nag appears don't close it, just leave it on the screen. - Open a ram editor like WINHEX. With this ram editor you can easily select the running program you want to edit or view in ram. Just select WINPROXY (entire memory) and then look for the text displayed in the expiring nag. Winhex will find an offset where this text is written. - Find the part of program who uses that offset. In fact that part of program is the one who is issuing to display the expiring nag. To do this we can set a read-memory-breakpoint. I think that SoftIce is more useful in this case. So let's close Winproxy and reopen it with SoftIce and set a read memory breakpoint on the offset we read before using winhex. To do this just write: BPM CS:XXXXXXXX where XXXXXXXX is the offset. Then start WINPROXY pressing Ctrl-D. The program will stop exactly when it is reading the offset of the nag expiring strings! Great! Just press F12 to return from the calling routine which got us here and have a look around until you find a conditional jump before the calling routine. In fact I'm expecting that before the routine which calls the expiring nag there's a conditional jump to avoid the execution of the expiring nag in the case that winproxy is not expired! Mmmm..lemme have a look, there's a suspicious conditional jump here: 46A855 jne 46A883 if we force this line to jump, the nag who remembers us to register the program disappears! Just try changing the byte 75 at the offset 46A855 with an EB. Oh yes, of course you must take back the system date to verify this, because now by now our program is already expired and it has no reason to remember us to register it! But there's another suspicious conditional jump: 46A7A5 jmp 46A7BA If we force this one to jump when winproxy is expired then the expiring nag disappears! Wow! It seems we have cracked it! Nah...too simple. The nag disappears, but the services are disabled! It's like we have clicked on the NO button of the expiring nag....this change is not useful! Sad? Want to abandon your target? A cracker never abandons his target... What we need is a method. The useful method for us at this moment is to monitor all the conditional jumps of the main body of the decrypted program when it is EXPIRED and to compare them when it is NOT EXPIRED (just take back the system date). So we find (j=jumps n=not jumps): offset expired not expired --------------------------------------------- 4699AA n n 469A71 j j 469A82 j j 469AE3 j j 469B09 j j 469BBB j j 46A312 n n 46A31A j j 46A36D j j 46A5C2 j j ... 46A6C5 j j 46A731 n j <=== 46A754 n n ... Look! The conditional jump at 46A731 JUMPS ONLY IF THE PROGRAM IS NOT EXPIRED! And if we force it to jump? Old bytes: 46A731 0F,84,DF,00,00,00 je 46A816 New bytes: 46A731 90,E9,DF,00,00,00 jmp 46A816 Wow! What I see? The services now start! And the expiring nag doesn't appear! It works like it's registered! So we now know what to change in our program: a) Don't show the register-remember nag during the evaluation period: [46A855] replace 75 con EB b) Work as registered after the expiring: [46A731] replace 0F con 90 [46A732] replace 84 con E9 Well, now... how to patch? The EXE file is ecrypted! Eh!Eh! This is the best part. I explain it one time forever. When you have to patch a run-time decrypting program you have to do this: 1) Find an empty space inside the file, you need 20-30 bytes, no more (for example where there is a copyright signature of the crypter-packer, but you can also add a new section in the exe file and append it at the end of the EXE file). 2) Locate an offset of the program code WHICH IS NOT ENCRYPTED and which will be executed AFTER THE DECRYPTING ROUTINE, but BEFORE the code we have to patch! 3) Overwrite at this offset with a JMP to the empty space. Take note of the overwritten bytes. BE CAREFUL! Your JMP will be sure a long jump so it will require 5 bytes (E9,xx,xx,xx,xx). This means that you'll probably overwrite MORE than one assembler instruction, but not perfectly. For example is it possible you'll overwrite an instruction and half of another one. The remaining bytes of the second one can become another instruction, SO IT'S IMPORTANT THAT YOU NOP THE REMAINING BYTES OF AN OVERWRITTEN INSTRUCTION (just insert 90 to nop a byte), AND TAKE NOTE ALSO OF THOSE REMAINING BYTES. 4) At the offset of the empty space insert few assembler lines which execute the cracking patch DIRECTLY in the RAM (MOV BYTE PTR [xxxxxxxx],yy). After these, insert the bytes overwritten with the JMP of the point 3) just to execute the instructions replaced when you wrote that jump instruction there. Then from the empty space jump back to the next line after the JMP of the point 3). Too hard? I try to use a simple scheme: - THE PROGRAM STARTS (I can't patch now directly in the RAM, because the part of the code I have to patch is encrypted, like in the EXE file) ... - THERE'S A CALL TO THE ROUTINE WHICH DECRYPT THE ENCRYPTED PART OF PROGRAM (now the part of program I have to patch is decrypted, I can patch it directly in the RAM) ... - SOME INSTRUCTIONS ====> REPLACE WITH A JUMP TO "EMPTY SPACE" - NEXT INSTRUCTION AFTER OVERWRITTING ... - THE PROGRAM EXECUTE THE ORIGINAL INSTRUCTIONS OF PROTECTION CHECK (for example it checks the serial number and decides different behaviour according to the check it did) =====> ORIGINAL INSTRUCTIONS ARE PATCHED BY THE LINES EXECUTED AT THE EMPTY SPACES. THE PROGRAM IS NOW CRACKED IN THE RAM ... - PROGRAM END ... - EMPTY SPACE: i)Patch directly in the RAM the instructions of protection check ii)Execute "SOME INSTRUCTIONS" overwritten iii)JUMP TO "NEXT INSTRUCTION AFTER OVERWRITTING" Let's return to WINPROXY to apply this method. 1) Find an empty space. Look at the location 54526B, there's a copyright signature: 'Neolite...Copyright'. We can write here (writing the cracking lines among the copyright strings...really comic!). 2) Locate an offset of the program code WHICH IS NOT ENCRYPTED and which will be executed AFTER THE DECRYPTING ROUTINE, but BEFORE the code we have to patch!. Mmmm... that's the hard part. In fact you can think to place your JMP to the empty space at the next line after the 545303 (call to the decrypt routine). There the target code is decrypted. Yes. And the jump to the decrypted program isn't still executed so we are BEFORE the target code. But we don't know if at this point we can modify our decrypted code without generating a memory access violation exception! How can we verify that? Instead of the JMP just insert here (next instruction after the call to decrypt) one of the patching line we will put in the empty space, for example: C6,05,55,A8,46,00,EB mov byte ptr [46A855],EB and put a breakpoint here, then run and when the program stops here just step this instruction. Oh! A memory access violation exception was generated! This means that the decrypting routine have locked the memory area after decoding. The only thing we can do is to trace the decrypting routine and find a location which is AFTER decrypt, but BEFORE the memory locking. So why don't put our JMP (to the empty space) where there's the call to the memory lock? In fact there we are sure that all the code is decrypted and we can overwrite the call to the memory lock with a jump to the empty space (where we'll write the instructions to operate our patch in the RAM), then we can restore the call to the memory lock. But where's the call to the memory lock? To find it just set a breakpoint where do you want inside the decrypting routine, run the program, when it stops insert the MOV BYTE PTR instruction there and trace it. If no exception happens then repeat again setting a breakpoint some instructions later. When you obtain the exception then the call to the memory lock is between the last try (no exception) and this one (exception). We locate the memory lock at the: 545A1A call [ebp+6E] This line is executed 2 times. Pushed in the stack among the arguments there is [ebp-42] which is 401000 the first time and 4CF000 the second time. This is the starting address of the memory area to lock. So we are interested to this call only the first time. 3) Overwrite at this offset with a JMP to the empty space. We can overwrite starting from 545A10 with the JMP to the empty space. New bytes are: 545A10 E9,56,F8,FF,FF JMP 54526B (which is our empty space) 545A15 90 NOP 545A16 90 NOP 545A17 next line after overwritting 4) At the offset of the empty space insert few assembler lines which execute the cracking patch DIRECTLY in the RAM. So: 54526B 81,7D,BE,00,10,40,00 CMP DWORD PTR [EBP-42],401000 545272 75,1C JNE WINPROXY.545290 Why? Because as I said the 545A1A is executed 2 times and we are interested on patching the RAM only before locking the memory area we have to patch and this happens only when the argument [EBP-42] pushed in stack contains the value 401000. If not, just return. If yes we can patch in the RAM: 545274 C6,05,31,A7,46,00,90 MOV BYTE PTR [WINPROXY.46A731],90 54527B C6,05,32,A7,46,00,E9 MOV BYTE PTR [WINPROXY.46A732],E9 545282 C6,05,55,A8,46,00,EB MOV BYTE PTR [WINPROXY.46A855],EB 545289 C6,05,27,47,41,00,EB MOV BYTE PTR [WINPROXY.414727],EB Uh? Why I'm patching the offset 414727 with EB? Oh, sorry I forgot to explain. Just look at the note at the bottom of this tute. Ok, now we can execute the instructions overwritten at the offset 545A10: 545290 50 PUSH EAX 545291 FF,75,BA PUSH DWORD PTR [EBP-46] 545294 FF,75,4E PUSH DWORD PTR [EBP+4E] And finally we return to the next line after overwritting: 545297 E9,7B,07,00,00 JMP WINPROXY.545A17 As you can see our changes have the only effect to do our patch in the RAM runtime, without changing anything in the flow of code. Every instruction written is then executed in the same order as when there were no changes and every instruction added has no influence on the registers or stack. The only difference is that now WINPROXY is cracked! ------------------------------------------------------------------------------ NOTE: If you run WINPROXY ram-patched only with these changes: [46A855] replace 75 con EB [46A731] replace 0F con 90 [46A732] replace 84 con E9 the services start,but when you connect some clients to Winproxy you'll notice that after few minutes of regular working a window will appear saying 'Your seial number is expired' and the program stops to work. If you open WINHEX while winproxy is running and search this string you'll find it at 4F20B8 and if you search this number in the code you discover that it's used at the line: 41473C push 4F20B8 Well, just look some instructions back and you'll find: 414727 75,60 jne 414789 if you force this jump then you avoid the problem. To do this you have to replace the byte 75 at the offset 414727 with an EB. I can assure that adding this change you can really use winproxy for unlimited time, but the code has other checks followed by conditional jumps like this and you can find them as homework. The only thing I can say to you is that the check routine called before every conditional jump is the same and that they concern to the possibility of changing winproxy settings or to enable the antivirus or smart filter after the evaluation period!