Cracking Embird 7.11 Evaluation
Tools used - SoftIce, W32DASM, IDA, PE Explorer, any hex editor, Win32 API Reference manual
A very interesting program to crack, as it contains
so many examples of protection -
1. Debugger detect.
2. Program and file CRC checks.
3. Encrypted strings.
4. No obvious registration ability.
5. Use of program interrupts to pass control
around the program.
6. Time and Use limited trial.
1. Debugger detect.
In my case the first thing that happened when
I started the program was that it detected I had SoftIce installed on my
machine. It issued a message "This program is not designed to run with
a debugger" and closed down.
I'd recently read a crack tutorial about SoftIce
detection, so I started checking for each one in turn.
In this case the program checked to see if a
file with the name "\\.\SICE" was open in the system.
Apparently this indicates that the SoftIce Virtual
Device Driver has been loaded.
Here is the code used (from my annotated IDA
listing) -
0049619C detect_softice:
; CODE XREF: debug_detect+CD_j
0049619C lea
eax, [ebp+var_10]
0049619F mov
edx, offset aL3kilR7mjfxg ; This is encrypted "\\.\SICE"
004961A4 call
sub_403DC0
004961A9 lea
eax, [ebp+var_10]
004961AC call
sub_462544 ; decrypt to "\\.\SICE"
004961B1 push
0 ; setup parms
004961B3 push
80h ; for CreateFileA
004961B8 push
3
004961BA push
0
004961BC push
1
004961BE push
80000000h
004961C3 mov
eax, [ebp+var_10]
004961C6 call
check_EAX_not_0
004961CB push
eax ; EAX -> "\\.\SICE"
004961CC call
j_CreateFileA_0 ; Open the file for input
004961D1 cmp
eax, 0FFFFFFFFh ; invalid file handle returned?
004961D4 jz
short detect_TRW ; Yes - SoftIce not active- check for TRW debugger
004961D6 push
eax
004961D7 call
j_CloseHandle_0
004961DC mov
[ebp+var_6], 1 ; set "SoftIce active" flag
004961E0 jmp
short loc_49624F
004961E2
004961E2 detect_TRW:
; CODE XREF: debug_detect+154_j
004961E2 lea
eax, [ebp+var_10]
The string "\\.\SICE" is first referenced at 49619C
in encrypted form - so you won't find it if you search the Embird exec
for "\\.\SICE".
After being decrypted, it is passed as a filename
to a CreateFileA at 4961CC. The CreateFileA is issued with the create option
OPEN_EXISTING - if the file exists the function will return a valid file
handle.
The obvious crack is to alter the string "L3kiL>R7MJFXG<\xFF\xFF\xFF\xFF\f"
on disk so that it does not decrypt to "\\.\SICE" - which I did, and which
lead me on to Step 2 - Program and file CRC checks.
Before leaving this section I should mention
that the program does similar checks at this point for NTICE, and the TRW
debugger and then follows with a generalised check for ANY debugger, which
will be covered in Step 5 - Use of program Interrupts.
It is also worth mentioning that all this Debugger
Detect stuff is repeated at the END of program initialisation, using different
encrypted strings and the obsolete _LCREAT file call (just to make things
more difficult, I guess).
2. Program and file CRC checks.
After patching the encrypted string on disk,
I started the program again.
This time I got a "File is probably damaged"
message, because I had patched it on disk, and the program closed.
I put a SoftIce breakpoint on the Message Box
routine - BPX MessageBoxA, and when it triggered, worked my way back up
the call stack using the F12 key until I found some interesting-looking
code -
004960A0 mov
byte ptr [ebp-5], 0
004960A4 mov
eax, [ebp-4]
004960A7 cmp
byte ptr [eax+8D4h], 0 ; has embird.exe been modified?
; detected by CRC check at 494490
004960AE jnz
short detect_SoftIce ; No - go on to SoftIce detection
004960B0 push
1
; Yes - do error code
004960B2 lea
edx, [ebp-14h]
004960B5 mov
eax, 2
004960BA call
sub_408E64
004960BF mov
eax, [ebp-14h]
004960C2 mov
cx, ds:word_4962F4
004960C9 mov
dl, 1
004960CB call
do_msg_beep_box ; issue
"File is probably damaged"
004960D0 mov
byte ptr [ebp-5], 1 ; set "CRC Fail" flag
004960D4 jmp
short loc_496142
004960D6 detect_SoftIce:
; CODE XREF: debug_detect+2E_j
The decision whether to issue the message is taken
at 4960A7, so I noted the address of this byte, reloaded the program into
SoftIce and put a read/write memory breakpoint on it - BPM xxxxxx RW.
I wanted to find out exactly where this flag
was set as a result of the CRC check failure.
00494490 CRC_check:
; DATA XREF: CODE:00494473_o
00494490 mov
bx, 0Ah
00494494 lea
eax, [ebp-0Fh]
00494497 lea
edx, [ebp-19h]
0049449A
0049449A CRC_compare_loop:
; CODE XREF: CODE:004944A9_j
0049449A mov
cl, [eax]
0049449C cmp
cl, [edx]
; check CRC values
0049449E jz
short CRC_do_next_byte ; CRACK - change this JZ to a JMP
004944A0 mov
byte ptr [ebp-5], 0 ; set "CRC fail" flag
004944A4
004944A4 CRC_do_next_byte:
; CODE XREF: CODE:0049449E_j
004944A4 inc
edx
004944A5 inc
eax
004944A6 dec
bx
004944A9 jnz
short CRC_compare_loop
004944AB
004944AB CRC_done:
; CODE XREF: CODE:00494354_j
004944AB
; CODE:00494414_j
004944AB xor
eax, eax
004944AD pop
edx
Well, it was set at 4944A0. I wanted to bypass
setting that flag byte, so I just altered the JZ at 49449E to a JMP.
At this point we are free to actually start debugging
and patching the program as much as we like without being detected.
3. Encrypted strings.
I have already mentioned these back in Step 1.
However, it is worth mentioning that W32DASM
provides an excellent string Xref, and this can be a very valuable asset.
* Possible Reference to String Resource ID=00086:
"Unregistered"
|
:00495EC9 B856000000
mov eax, 00000049
:00495ECE E8912FF7FF
call 00408E64
Note that these are "possible" string references
- if the string is resource number 49, for example,
then any time the number 49 is used it is a POSSIBLE
use of that resource - you just have to develop a "feel" for this, and
set LOTS of breakpoints :)
4. No obvious registration ability.
This was very strange - none of the menus contained
an option that allowed you to register the program. Even when the trial
expired there was no option to register. I'm not sure whether this is a
deliberate "feature", or a bug. (I discovered later that it was a deliberate
trick - the menu item is marked as "Visible=False" in the Resource Table).
You could do this other ways, but I used a recently
acquired tool "PE Explorer" (Trial version at www.heaventools.com) to find
all the resources in the program.
I could see from the Resource List that it should appear as an item
in the Help menu, but it didn't <shrug>. So, I did an IDA label search
for the name "RegistrationMenu" and, unexpectedly, found this -
004935E6
dd offset sub_496068
004935EA aRegistrationmenu db 15h,'RegistrationMenuClick',14h,0
00493602
dd offset sub_49B7B0
00493606 aCopymenuclick db 0Dh,'CopyMenuClick',18h,0
00493616
dd offset sub_49B800
0049361A
dd 6E6F4311h, 746E6574h, 6E654D73h, 696C4375h, 116B63h
0049362E
dd offset sub_49B844
(Now, I don't want to turn this into an IDA tutorial, but let me just
say that those strings were not produced by IDA, but by me, manually redefining
them.
They originally looked like the data at 0049361A.
IDA was not able to determine that this was a string - I just happened
to be browsing the listing a few days earlier and saw what looked like
some ASCII in hex format, so I asked IDA to try turning them into ASCII.)
At this point, I took a guess (don't we always) that the subroutine address held at 004935E6 would display the registration menu.
I can't show all the code here, but the routine at 496068 did display the registration dialog, read the serial number, and validated it.
Another routine starting at 495F40 checked a flag, and decided whether
to show "Unregistered" or "Registered To -" in the main window title.
So, in memory, at 495F40 I patched a JMP 496068 (this routine above).
I wasn't concerned about returning to the correct place in the program
- I just wanted to see the Registration dialog, and set a SoftIce BPM on
the serial number.
So I just entered any number as a serial number, and let the routine
(sub_495D9C) do all its validation (which, of course, failed). But I didn't
care, I was just going to alter a JZ instruction to a JMP after the flag
test in 496068.
OK, so now we're at 495F40.
This still contains the JMP 496068 that I put there earlier (and we
have a recusive loop), so I alter that back to its original instruction,
and step down the code until the registration flag byte is checked - which
I alter to say OK. Then I just let the code run.
To my surprise, the program started, AND in the title bar was "Registered
to - xxxxxxx", where xxxxxxx was the fake serial number I had entered!
(The registration data is held in some encrypted file - I don't care
which one - it's not important to know. I found later, that it's held in
clear text in embird.ini). Now I patch the program on disk with some JMPs
to force our incorrect serial number (our name, in fact) to be OK and we
have a nice working version.
5. Use of program interrupts to pass control around the program.
This is not easy for me to explain - mainly because I don't fully understand
it myself.
The program makes a lot of use of "RaiseException" and "UnhandledExceptionFilter"
calls to pass control around the program in a very confusing way.
He issues RaiseException with his own exception codes which causes
execution to pass into the Windows Kernel routines, THEN onto the debugger,
before getting control back in his UnhandledExceptionFilter.
At this point the Windows Kernel tells him if he is running under a
debugger (ANY debugger) and he just issues ExitProcess if that is the case.
If he is NOT running under a debugger he inspects the exception code
to see it was one that he issued, and passes control to subroutines to
do normal functions.
There is an INT 3 instruction at 496236 which I think is an important
part of this.
In case you don't know, an INT 3 is how a debugger sets breakpoints
in your code. Normally, an INT 3 would cause your program to abort, but
the debugger sets himself up to handle it. If it is one that he has set,
then that's fine; otherwise he just lets it through for normal processing
- which is normally an abort. However, the application program can ALSO
request that he is allowed to handle an INT 3, which I think is how he
can detect that a debugger has been allowed to look at it first.
I admit to being very unclear about all this, and I need to do some
more research on it.
NOTE: See Microsoft Win32 Programmer's Reference for more information
- the most complete version I have found is included in the B5STD.ZIP download
at -
http://www.borland.com/techpubs/bcppbuilder/v5/updates/std.html
You need to read it together with a WINDOWS.INC file if you want to
understand all the flag settings for API calls.
6. Time and Use limited trial.
These routines are never executed now, because we have forced the program
to think that it is a registered version.
In fact, earlier in my debugging I had found these routines by using
W32DASM string xref and bypassed them fairly easily.
Well, I know that wasn't easy
to understand - there are places where I can't fully describe what I did
or why I did it.
I went down many "dead ends",
and I also had quite a lot of lucky guesses.
But, for me, that's part of
the cracking process.
I learnt a LOT cracking this
program - even though I nearly gave up many times.
NORTON_LEN
-=CROHACK=-