Petite Adventures in PE Land - Tutorial by Accz

"A little SEH, a little redirection, a little cracking, a little CRC correction and a little of our own code, mix the ingredients into a large bowl and you have the basis of a very good tutorial. I wouldn't say Petite has been feared as a packer (the whole PE encryption concept is pretty flawed anyhow imho), yet as Accz points out, ProcDump could never handle 2.x as Petite makes the import table unusable. Read slowly this document, see how the author of this program actually deserves some credit, self-healing his software (albeit in a fairly rudimentary fashion), see how Accz exploits the fact that the author coded this routine in a HLL, PUSH 7E oh dear!. I hope Accz will send me the 2nd part of this series in due course, in the interim turn on some music, read, sip and enjoy". "Edited by CrackZ - especially the Shania comment dear Accz!".

Part I - or 'how to crack every Petite v2.x protected target'.

Download the original target here (204k).

Tools Used

Hiew, UltraEdit & Notepad.
SoftICE.
IDA.
ProcDump's PE Editor.

Well, first of all we need to find a suitable target. One of my favourites is Url Organizer from http://ww.urlorg.com. For this text I am using v2.3.1 second build. There are actually two builds of v2.3.1, the latest one introduces a new trick :-), this is a very nice program to keep your bookmarks organized.

History

Some time ago I got an e-mail from someone asking for a crack for v2.2x of Url Organizer, the publicly available cracks were all non-working. Not that it had a hard protection though. Anyway, v2.3.x introduced a new (weaker imho) scheme, and additional Petite protection (packed).

We will use hardware breakpoints i.e. when we need to break at 4028D1 a "bpm cs:4028D1 x" will do it (instead of bpx 4028D1). First of all when the program starts we get a nice nag. A typical breakpoint (MessageBoxA), a little tracing back lead us here :-

:004028CA CALL 00404CD9
:004028CF TEST EAX, EAX
:004028D1 JGE 00402934 <-- jump required else display the nag.

So we need to force the JGE to permanently JMP. When our trial period is over another nag appears informing us to register, again some easy back tracing (MessageBoxA) and another interesting routine :-

:00404188 XOR EBX, EBX
:0040418A PUSH EBX
:0040418B CALL 00419980
:00404190 MOV EDI, DWORD PTR [EBP-1C]
:00404193 POP ECX
:00404194 SUB EAX, DWORD PTR [EDI+00000370]
:0040419A CMP EAX, 0027F240
:0040419F JZ 004041F2

This isn't the typical test flag time-trial routine. Instead the program uses a function that returns a value which gets directly
compared with 0027F240h, if the JZ doesn't happen our trial period is over. So, we just need to force this, thus our second patch is change bytes 7451 to EB51. After this the program runs nice. However if we change to Organize Menu (F4) and we click "New" a new nag appears and the function gets disabled. Sure thing, we can reverse starting from the MessageBoxA code location and once again tracing a few calls back we land at this code snippet :-

:0040E98D CALL 00419980
:0040E992 SUB EAX, DWORD PTR [0044F1B8]
:0040E998 POP ECX
:0040E999 CMP EAX, 0028192A
:0040E99E JLE 0040E9CD <-- Jump and we win.


Now we have our third patch which is to change bytes 7E2D to EB2D. If we test our patches with SoftICE in memory everything works fine. After this I tried to code a loader-patch, just for practice, to my suprise I found that the loader failed to crack the program. The logical assumption that I am a lousy coder proved to be wrong (in this case:P) because I tried some other ready-made loader engines and all failed.

Well lets set breakpoints at our three addresses :-

bpm cs:4028D1 x
bpm cs:40419F x
bpm cs:40E99E x

We can now see that our loader (just try it) fails to fix the code at 40419F. The instructions there are the same as the original. So whats wrong?, after some thought I came up with the (obvious) result that something overwrites our patch. A bpx WriteProcessMemory revealed a nice, yet amusing scenario. Now return from the CALL (F12) and check this out :-

:00404E32 CALL DWORD PTR [0043C2D8] <-- WriteProcessMemory().
:00404E38 PUSH ESI
:00404E39 CALL DWORD PTR [0043C2D4] <-- CloseHandle().
:00404E3F POP ESI
:00404E40 RET 8 <-- Return.

The author uses a routine that patches four address in memory. Actually he hacked his own protection scheme just to keep us out. Address 40419F gets patched second (remember I said four similar calls), when we ride on that RET instruction this code snippet appears :-

:00403FDB MOV [EBP-20], 0040417A <-- Here.
:00403FE2 MOV ESI, DWORD PTR [EBP-20]
:00403FE5 PUSH 0000007D
:00403FE7 MOV ECX, EBX
:00403FE9 LEA EAX, DWORD PTR [ESI+0C]
:00403FEC PUSH EAX
:00403FED CALL 00404E10 <-- We came from here.
:00403FF2 ADD ESI, 00000025 <-- ESI+25h == 40419Fh.
:00403FF5 PUSH 0000007E <-- Write 7E.
:00403FF7 PUSH ESI
:00403FF8 MOV ECX, EBX
:00403FFA CALL 00404E10 <-- Patch it.

Trace into this call and we have :-

:00404E10 PUSH ESI <-- Save on stack.
:00404E11 CALL DWORD PTR [0043C2E0] <-- GetCurrentProcessID().
:00404E17 PUSH EAX <-- Process ID.
:00404E18 PUSH 00000000
:00404E1A PUSH 00000038
:00404E1C CALL DWORD PTR [0043C2DC] <-- OpenProcess().
:00404E22 MOV ESI, EAX
:00404E24 PUSH 00000000
:00404E26 LEA EAX, DWORD PTR [ESP+10]
:00404E2A PUSH 00000001 <-- Write 1 byte.
:00404E2C PUSH EAX <-- Byte 7E (conditional jump).
:00404E2D PUSH [ESP+14] <-- where? address 40419Fh.
:00404E31 PUSH ESI <-- Handle.
:00404E32 CALL DWORD PTR [0043C2D8] <-- WriteProcessMemory().
:00404E38 PUSH ESI
:00404E39 CALL DWORD PTR [0043C2D4] <-- CloseHandle().
:00404E3F POP ESI
:00404E40 RET 8

Its clear now, but we need to break this. My preferred solution is to make it patch *our own* way. Just don't go disable the entire call. Well remember this? :-

:00403FF5 PUSH 0000007E

If we change it to :-

:00403FF5 PUSH 000000EB

Conclusion :- Here the list of our patches.

[000403FF6], 0EB <-- WriteProcessMemory().
[0004028D1], 0EB <-- StartUp Nag.
[00040E99E], 0EB <-- Hidden Check.
[00040419F], 0EB <-- TimeTrial.

Now the real fun starts - if we code a memory-patcher we are done. The other way is to inject our code. We'll therefore add a PE section, but remember this EXE is packed with Petite so we need also to break the packer. First of all we can use an editor to add 200h=512d bytes at the end of the EXE. We load the program into a PE Editor and edit the sections like so :-

Name     Virtual Size   Virtual Offset   Raw Size   Raw Offset   Characteristics
.text      00052000        00001000      00023800    00000800        E0000060
.rsrc      0000F000        00053000      00005600    00024000        C0000040
.petite    000003FE        00062000      00000400    00000400        E2000060
           00001000        00063000      00000000    00000000        C2000080

Here we can see the last section in the file is actually .rsrc which ends at 24000+5600 = 29600. This value is good so no need to round it up, our section can start there. We also need to give our new section a Virtual Size, lets say 1000, so our Virtual Offset should be 63000 + 1000 = 64000. Also the raw size must be a multiple of the file alignment (which is normally 200h)
so we will use 200h. Finally our section contains executable code therefore we need E0000060 as Characteristics (check your winnt.h file).

This is how the PE sections should now look :-

Name     Virtual Size   Virtual Offset   Raw Size   Raw Offset   Characteristics
.text      00052000        00001000      00023800    00000800        E0000060
.rsrc      0000F000        00053000      00005600    00024000        C0000040
.petite    000003FE        00062000      00000400    00000400        E2000060
           00001000        00063000      00000000    00000000        C2000080
.accz      00001000        00064000      00000200    00029600        E0000060

We also need to update the size of the image (since its bigger now). So the old value 64000 + 200 = 64200 since we have added 200h bytes. Update the PE header and exit. Our EXE should now be 169,472d + 512d = 169,984d bytes. Still lots of work to do though if we run the program a nag will appear accusing us of infecting the EXE. It's Petite's CRC check. We need to add our code into the new section, break the CRC check and make Petite's loader run our patch.

Load the program into SoftICE :-

00462042: MOV EAX,000462000 ;" F " <-- Points to .petite.
00462047: PUSH 000449870 ;" D p" <-- New SEH's address.
0046204C: PUSH D,FS:[000000000] <-- Save old SEH address.
00462053: MOV FS:[000000000],ESP <-- Install new SEH.

Petite installs a new Structured Exception Handler (SEH). We should know that when a thread fails -- i.e. executes an illegal
instruction, an exception will be raised and control will passed to an "exception handler". Each thread has its own exception handler "callback" function so when the fault occurs Windows executes that "callback" function (which can be user-defined). The FS register (always) points to the current TIB (Thread Information Block), thus at FS:[000000000] there is a pointer to a structure that contains information for the OS to find the "callback" function to execute.

Note :- if you need to know more read Matt Pietrek's text.

So what do we have here?, Petite saves the old SEH address and installs a new one, address 449870. Therefore when Petite (intentionally) causes a fault, the OS will execute the code at that address.

Also notice how our very first instruction corresponds with the PE header :-

.petite  000003FE  00062000  00000400  00000400  E2000060

The ImageBase 400000 + VA = 462000. Now bpm cs:462042 x and bpm cs:449870 x then press F5. If you run the original.exe it will break two times, but if you run our modified.exe a nag will appear *before* the second break at 462042 :-

:00449870 CALL 004498C4 <-- A whole new layer.

This is a long and complicated routine which does the CRC check.

:004499DE CMP [EAX+04], EBX <-- EBX holds currect CRC value.
:004499E1 JZ 004499EB <-- Jump yes please.
:004499E3 ADD ESP, 2A
:004499E6 JMP 00462015 <-- CRC failed.
:004499EB MOV ESI, 000478F0 <-- CRC verified
.

We obviously need to take that jump at 4499E1, how though. Lets look at the values being compared :-

Break due to BPMB #022F:004499E1 X DR2 (ET=14.50 milliseconds)
:d eax+04
:? eax+04
00462004 0004595716 "F "
:d ebx
:? ebx
60C87AA8 1623751336 "`Èz¨"

Can you see whats going on here?, remember we use Intel's format, bytes in reverse order, a hint, check this out :-

.petite  000003FE  00062000  00000400  00000400  E2000060

The typical 4MB ImageBase + VA = 462000 and a dword (4 bytes) which is our correct CRC value. Load the file into Hiew and go to file offset 400 (3FE rounded up).

.00462000   A0 02 00 00-A8 7A C8 60-8B 44 24 04-83 C4 2A 8D
                        |         |
                        |---------|-----> Stored CRC value.

Conclusion :- at file offset 400 Petite stores the correct CRC value, the call re-calculates it again, stores it into EBX and we have a compare. So to crack this we need to update the EXE. We run our modified.exe so we can catch the new EBX value.

:bpm cs:4499e1 x
Break due to BPMB #022F:004499E1 X DR3 (ET=129.92 milliseconds)
:d eax+04
:? ebx
FBF75C7E 4227292286 (-67675010) "û÷\~"
:d ebx

Now go to Hiew and update our file :-

.00462000   A0 02 00 00-7E 5C F7 FB-8B 44 24 04-83 C4 2A 8D
                        |         |
                        |---------|-----> New CRC.

Our other problem is also solved, instead of installing new SEH at 449870 we will redirect it to our section, execute code and then jump back to 449870. First go to file offset 442 (F5 in Hiew) :-

00462042: MOV EAX,000462000
00462047: PUSH 000449870

Change to :-

00462042: B800204600 MOV EAX,000462000
00462047: 6800404600 PUSH 000464000

Now when the page fault occurs we will be at the start of the .accz section. Therefore in Hiew we go to offset 29600 (F5) and our address is Image Base + VA = 400000 + 64000 = 464000

.00464000: C605F63F4000EB MOV B,[000403FF6],0EB <-- Patch 1.
.00464007: C605D1284000EB MOV B,[0004028D1],0EB <-- Patch 2.
.0046400E: C6059EE94000EB MOV B,[00040E99E],0EB <-- Patch 3.
.00464015: C6059F414000EB MOV B,[00040419F],0EB <-- Patch 4.
.0046401C: E94F58FEFF JMP .00040F670 <-- Jump to Petite's SEH 449870.

We actually need to go to SoftICE to find the opcodes for the very last instruction (you don't says CrackZ!). Just use assemble mode (ie "a") and make it "jmp 449870", use Hiew and go to file offset 29600 to see it. What we did here is : we installed a new SEH address (ie 464000) that will do our patch and the we return to Petite's SEH address i.e. 449870, so it can load the file as per usual, thats it, the file works well now.

Whats left? lets say we need to unpack it manually. You may know that ProcDump and other scripts for Petite don't work. Remember our VERY first breakpoint?, bpm cs:462042 x and press F5,w e already know we will pass the CRC check routine and then the second time we break here :-

:00462042 JMP 0041BA50

Notice that we are still in .petite. Also notice the jump table below it, if we take the jump we land here :-

:0041BA50 PUSH EBP <-- We are here.
:0041BA51 MOV EBP, ESP
:0041BA53 PUSH FFFFFFFF
:0041BA55 PUSH 00441020
:0041BA5A PUSH 00420408
:0041BA5F MOV EAX, DWORD PTR FS:[00000000]

See what we should do, load the original EXE into SoftICE :-

Break due to BPMB #022F:00462042 X DR3  (ET=163.98 milliseconds)
:map32 url organizer
Owner       Obj Name  Obj#  Address        Size      Type
URL ORGANI  .text     0001  022F:00401000  00052000  CODE  RW
URL ORGANI  .rsrc     0002  0237:00453000  0000F000  IDATA RW
URL ORGANI  .petite   0003  022F:00462000  000003FE  CODE  RW
URL ORGANI            0004  0237:00463000  00001000  UDATA RW
:pagein d 400000 64000 newurlog.exe

Starting from the Image Base 400000 we need 63000 + the header, so we get all our sections and need to dump 64000h bytes, also we now know the EIP is 41BA50. The problem is Petite scrambles the Import Table. The Import Table Relative Virtual offset (RVA) is stored in the corresponding directory entry in the PE Header. As it is a virtual offset, it won't match the file offset (VA) of the import table, we just need to convert RVA to the corresponding VA.

A fast way to test our newurlog.exe is to edit the header and set all RAW offset = VA, but this will run for our computer only. So we need to fix the Import Table and thats going to be another adventure.

Lets say we want to go deeper, lets explore this part of the code :-

.004620A1: POP EAX
.004620B2: SUB ESI,-001 <-- Modify ESI.
.004620B7: RETN

.004620F0: PUSH 0
.004620F2: XOR DL, DL
.004620F4: DEC EBX
.004620F5: MOVSB <-- bpm cs:4620F5 x.
.004620F6: XOR ECX, ECX
.004620F8: CMP EBX,0 <-- Loop.
.004620FB: JLE .004620A1

.00462104: MOVSB
.00462105: XOR [EDI][-0001],BL <-- [EDI].
.00462108: DEC EBX

.0046216B: SUB EBX, ECX
.0046216D: LEA ESI,[EAX][EDI] <-- [ESI].
.00462170: REPE MOVSB
.00462172: POP ESI
.00462173: JMP .004620F8 <-- Repeat.

The MOVSB instruction at 4620F5 causes the page fault. We know that it should copy the contents of ds:si to es:di, so we actually care about ESI and EDI's values *only*. The SEH callback function gets executed (449870) and Petite goes
to a new protection layer i.e. the CRC check routine. If we want to check that at the very beginning of the program we can skip the SEH installation (i.e. at 462047 type r eip=46205D) and then we can see that :-

URL ORGANIZER caused an invalid page fault in
module URL ORGANIZER.EXE at 022f:004620f5.
Registers:
EAX=00400000 CS=022f EIP=004620f5 EFLGS=00000302
EBX=00000270 SS=0237 ESP=0069fe02 EBP=00000001
ECX=00000000 DS=0237 ESI=00400000 FS=3a37
EDX=00460500 ES=0237 EDI=00400000 GS=0000

We can check and find out that here ESI = EDI therefore the MOVSB leads to the CRASH.

Greetings

These only just survived the editing axe (CrackZ) :-).

Carpathia :- Lousy taste in music but thanx for chatting with me anyway (haha).
Neural Noise :- You are nice, you helped me a lot, glad to meet you.
Ultraschall :- You cracking machine, don't be so productive ..chill out for a while :-)
_y :- Good luck.
AB4DS :- DEEEEEEEEEEEEEAAAAAAAAATTHHHH!!!!!!!.
Dimedrol :- I am glad to meet you.
Dustie :- You started it.

twisti, berserki, HardLock, DV, Titi, cujo, zaarnik, bb, ibh and the rest of us. ACiD BuRN, Bisoux, Warezpup,_pain, ice.ro (whats a register? :P), sortof, fresh, night- (mp3), harvestr, defiler (nice nicks :P), duelist (take it easy), masta, scut, lazarus,
e_bliss, dezm (you too take it easy), r!sc, ganja farmer, hutch, iczelion, elicz, kwai, quantico, cruehead (try Saturnus), intern, egoiste, CrackZ (Shania rocks I know :-) ), vizion, muad .....

& All the people I forgot.

Somewhere in time -- Accz.


Packers Return to Main Index


© 1998, 1999, 2000 Hosted by CrackZ, Tutorial by Accz 7th March 2000.