Theme: manual unpacking Packer: PECompact v2.50 by Bitsum Technologies Author: SMoKE Tools: OllyDbg, ImpRec, OllyDump (or some other dumper) URL: http://www.bitsum.com Target: custom executable... I took one of my delphi7 programs and choose this options on it. Action: Compress General: ...., Compress safe resources, Perform code integrity check, Strip fixups, Strip unused resources, Enforce memory protection Plug-ins: loader->antidebug, codecs->crc32, jcalg1, crc32, api hook-> fastimport Compression level: 9 PEC2 Command Line: /Cl:9 /Emp:Yes /CodecHost:pec2codec_crc32.dll,pec2codec_jcalg1.dll,pec2codec_crc32.dll /HooksHost:pec2hooks_fastimport.dll /LoaderHost:pec2ldr_antidebug.dll and press Compress selected... let's start to research the protections that made pecompact. load the file in your olly... 00401000 MOV EAX, KEYGEN.00464C04 00401005 PUSH EAX 00401006 PUSH DWORD PTR FS:[0] 0040100D MOV DWORD PTR FS:[0], ESP 00401014 XOR EAX, EAX 00401016 MOV DWORD PTR [EAX], ECX it sets exception handler to 464C04 and writes to zero address, so press SHIFT+F8 when you got exception and staying on the line MOV DWORD PTR [EAX], ECX OK, now you are out of seh frame and can trace a little, till you will reach this code... 00464CA9 MOV ECX, DWORD PTR [EBX+C] 00464CAC MOV DWORD PTR [ESI+14], ECX 00464CAF CALL EDI 00464CB1 MOV DWORD PTR [EBP+1000125B], EAX 00464CB7 MOV ESI, EAX trace into the CALL EDI 00BC0908 JMP SHORT 00BC090B 00BC090A AND EAX, 55565753 00BC090F CALL 00BC0914 00BC0914 POP EBP 00BC0915 SUB EBP, 1000126B go ahead, nothing interesting, loader takes some API addresses... 00BC098B LEA ECX, DWORD PTR SS:[EBP+10001BE2] 00BC0991 PUSH ECX 00BC0992 CALL 00BC0ADC you can trace into this call and everything you will see is a LoadLibraryA and GetProcAddress to get some API's addresses. 00BC099F MOV EAX, DWORD PTR SS:[EBP+10001C8D] 00BC09A5 TEST EAX, EAX 00BC09A7 JE SHORT 00BC09B0 00BC09A9 CALL EAX 00BC09AB TEST EAX, EAX 00BC09AD JE SHORT 00BC09B0 here comes an interesting stuff, what you think is at address pointed by EBP+10001C8D ? It's the address of IsDebuggerPresent API ! So as you can see loader calls that function to know is the process running under debugger (ofcourse only ring3 debuggers detectable this way) Just simply change to 00BC099F MOV EAX, 0 00BC09A4 NOP 00BC09A5 TEST EAX, EAX 00BC09A7 JE SHORT 00BC09B0 00BC09A9 CALL EAX to bypass the detection... 00BC09BF PUSH 40 ; type of access protection (PAGE_EXECUTE_READWRITE) 00BC09C1 PUSH 1000 ; type of allocation (MEM_COMMIT) 00BC09C6 PUSH ECX ; size of region (358039 bytes) 00BC09C7 PUSH 0 ; address of region to reserve or commit 00BC09C9 CALL DWORD PTR DS:[EDX] ; VirtualAlloc store allocated memory address... 00BC09CB MOV DWORD PTR SS:[EBP+10001CA1], EAX now trace into this call... 00BC09D1 PUSH ESI 00BC09D2 CALL 00BC0DBA 00BC09D7 TEST EAX, EAX 00BC09D9 JNZ 00BC0A85 You are here... 00BC0DBA PUSH EBP 00BC0DBB MOV EBP, ESP 00BC0DBD ADD ESP, -18 00BC0DC0 PUSH EBX Now trace a little until you reach this peace of code 00BC0E10 XOR EAX, EAX 00BC0E12 MOV AX, WORD PTR DS:[ESI+10] 00BC0E16 TEST EAX, 10 00BC0E1B JE 00BC0EF9 This is important part, loader will search for 010h everytime in ESI+10 and will increase ESI by 01Ch, all that will do ECX times. When it finds 010h in ESI+10 then will start battle :) As you remember i choosed CRC32, JCALG1, CRC32 Going forward lemme tell you that in this routine loader will generate crc32's address, then decompression, and finally again for crc32. lets see how that happens. 00BC0E22 MOV EDI, DWORD PTR DS:[EBX+10001CA1] 00BC0E28 MOV DWORD PTR SS:[EBP-14], EDI 00BC0E2B MOV ECX, DWORD PTR DS:[ESI+8] 00BC0E2E MOV EAX, DWORD PTR DS:[ESI+14] 00BC0E31 SUB ECX, EAX 00BC0E33 MOV ESI, DWORD PTR DS:[ESI] 00BC0E35 ADD ESI, EDX 00BC0E37 MOV EAX, ECX 00BC0E39 SAR ECX, 2 00BC0E3C REP MOVS DWORD PTR ES:[EDI], DWORD PTR DS:[ESI] 00BC0E3E ADD ECX, EAX 00BC0E40 AND ECX, 3 00BC0E43 REP MOVS BYTE PTR ES:[EDI], BYTE PTR DS:[ESI] Here calculates needed sizes and copies memory from loaded image starting from entry point to allocated memory address for later use, and later use gonna be crc32 checksum calculation or decompression of that code. ...continue tracing... 00BC0E7D PUSH EDX 00BC0E7E PUSH EAX 00BC0E7F PUSH DWORD PTR SS:[EBP+8] 00BC0E82 CALL 00BC1149 Ok here the function which generates address for later use as crc32 or decompression or other, trace into it and see how its happen, very simple method used. And here you go first call to generated address, its gonna be crc32 i guess... (because i choosed crc32 first in codecs list) 00BC0E8D PUSH ECX 00BC0E8E PUSH DWORD PTR SS:[EBP-18] 00BC0E91 PUSH DWORD PTR SS:[EBP-14] 00BC0E94 CALL EAX and here the crc32 checksum calculation routine, the algo 00BC06D5 MOV DL, BYTE PTR DS:[ESI] 00BC06D7 ADD ESI, 1 00BC06DA XOR AL, DL 00BC06DC MOV BYTE PTR DS:[EDI], DL 00BC06DE ADD EDI, 1 00BC06E1 MOV EDX, 0F 00BC06E6 AND EDX, EAX 00BC06E8 SHR EAX, 4 00BC06EB XOR EAX, DWORD PTR DS:[EBX+EDX*4] 00BC06EE MOV EDX, 0F 00BC06F3 AND EDX, EAX 00BC06F5 SHR EAX, 4 00BC06F8 XOR EAX, DWORD PTR DS:[EBX+EDX*4] 00BC06FB DEC ECX 00BC06FC JNZ SHORT 00BC06D5 00BC06FE NOT EAX and here goes the checking of valid checksum 00BC0700 SUB EAX, DWORD PTR DS:[ESI] if the result will be zero, then the checksum was right, lemme tell you that you can find this DWORD in the file, its the last double word of the first section :) so you can bypass the checksum checking by making a little tool which will generate correct checksum and then replace the existing or you can just patch the jump and make loader think that the checksum was right. 00BC0702 POP EAX 00BC0703 JE SHORT 00BC0778 jump if checksum was right :) after this routine returns again to 00BC0E8D PUSH ECX 00BC0E8E PUSH DWORD PTR SS:[EBP-18] 00BC0E91 PUSH DWORD PTR SS:[EBP-14] 00BC0E94 CALL EAX but this time EAX points to decompression routine, and third time it will point to crc32 checksum routine again, as i selected in codecs list. But the second time, when crc32 check is working on decompressed code, you cant find checksum in the compressed file, it appears after decompression, so if you have two checkings before and after decompression better to just patch the jump. Ok trace, trace, trace... and go back, there is no more interesting stuff so you reached here 00464CAF CALL EDI 00464CB1 MOV DWORD PTR SS:[EBP+1000125B], EAX 00464CB7 MOV ESI, EAX 00464CB9 POP ECX 00464CBA POP EDX 00464CBB JMP SHORT KEYGEN.00464CC9 00464CBD ADD ECX, EDX 00464CBF PUSH 8000 00464CC4 PUSH 0 00464CC6 PUSH EDI 00464CC7 CALL DWORD PTR DS:[ECX] 00464CC9 MOV EAX, ESI 00464CCB POP EDX 00464CCC POP ESI 00464CCD POP EDI 00464CCE POP ECX 00464CCF POP EBX 00464CD0 POP EBP 00464CD1 JMP EAX and here's your jump to original entry point... stay on OEP and dump the process without import rebuilding. Fire up ImpRec, run compressed executable, choose it from imprec list, type ypur OEP value in the OEP edit box, press IAT AutoSearch, then Get Imports and Fix Dump (you shouldn't get any invalid imports) In one word nothing special on import table rebuilding. this was another lame and not detailed tutorial by me... 16:36 16.04.05 armfn.net/~softland smoke@armfn.net