Part I - or 'how to crack every Petite v2.x protected target'.
Download the original target here (204k).
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.
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
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 :-
:004028CF TEST EAX, EAX
:004028D1 JGE 00402934 <-- jump required else display the nag.
: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.
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.