Cracking for dummies - by reverser+
"The difficult years" - Target: Netmed
1997 - version 0.01, will change)
Cracking and reverse engineering for those that
have NEVER done something like this before
"Reverser's lessons demonstrate that cracking can be great fun!"
"For people that would like doing useful cracking in a matter of hours"
"Reverser+'s 'Cracking for dummies' is the perfect choice
for those who want to use their programs in order to do what they want,
not what the programmers wanted they to do with them"
Expert +cracker and +ORC's student reverser+ builds on the success of his
cracking essays, updating his discussions in a humorous fashion.
His style helps to overcome the technical and non-intuitive
barriers presented by cracking. The lessons show readers everything
from how to 'dead list' a target to more advanced cracking tasks
like 'feeling' concealed and encrypted protection schemes.
Nothing, nada, nichts... it's here and free for everyone, of course :-)
OK, enough kidding... I have decided to write an easy and GUIDED tutorial to
software reverse engineering, aimed at all those among my
are interested yet AFRAID of the many 'technicalities'
are given for acquired inside the 'real' essays on the
student pages (those essays of the +HCU
are dedicated to a 'more mature' cracking audience). In fact
I believe that
our duty is not only to explain our science to those already
able to follow it, but also to allow anybody who cares to join us.
These essays does not have ANY value for
average or advanced crackers and are aimed
EXCLUSIVELY at those
among our readers that have NOT YET BEGONNEN any
cracking or reverse engineering at all.
This said, you'll not be able to understand this lesson if you do
not 'work' a little. Some of the explanations and of the
concepts that you have to learn may NOT be easy
to assimilate, no matter how 'basically' explained.
If this happens,
just read on, once you have got some more 'perspective' many
of the 'barriers' that you encounter will suddenly
Don't forget to read and assimilate lesson 1 before
GATHERING TARGET AND TOOLS FOR THE SECOND LESSON
You are about to begin a REVERSE ENGINEERING SESSION.
You need to PRINT this text, you need a PC-computer with
windows95 installed, you need a 'target' and you need
some tools in order to follow my lessons (tools are
forever, like diamonds).
For this second lesson you'll need, specifically, all tools described in
lesson 1 (the "tekfact" lesson: wdasm, psedit and pkunzip)
and two new programs
1) Base Calculator: an integer calculator
with not only the usual operations
such as addition and subtraction, but also the most common logical operations.
In addition, Base Calculator can display 8 bit, 16 bit and 32 bit numbers,
both signed and unsigned in decimal, hexadecimal, binary or octal.
You can fetch it right off my site, here:
bcalcns1.zip (148.156 bytes).
This program by John Zaitseff has been programmed in pascal (Delphi2) and
is one of the best tools you'll use when you
reverse engineer. It offers bin, hex, dec and octal calculations with XORing,
ANDing, NOTing and
whatever capabilities you need... you'll see and understand...
For now just download it, unzip it and keep it ready for use.
2) Netmedic. (Version 1.2). This is our target for this lesson:
You'll be able to fetch it easily
performing a ftp search.
for the strings 'nm.exe', 'netmedic' and
'vitalsign' in this order.
The self-extracting trial shareware
version 1.2 that we will use here as target has a length of 1.019.455 bytes
Alternatively you may fetch this target from vitalsign's own site at
http://www.vitalsigns.com. They will release
a new version very soon, of course, so harry up.
Netmedic is a PHANTASTIC tool for stalking activities, self-defense on the Web and net
surfing, as you will soon discover. To navigate the deep sees of the web with this
tool can be great fun, like being in
the gauge and machine room of a huge ship every time you approach a site.
protection scheme, altough extremely easy to defeat, does indeed present some very
I checked (just in case) and found that this target
has already been cracked by (at
least) three different cracker groups.
You could easily fetch all over
the web a ready made
instance its Phrozen Crew's patch...
But you will not do it, because
you will never need any more these ready-made "patches for lamers".
You are already a cracker
(in nuce) and therefore you are going to feel the code of your target, understand it
and crack it all by yourself (and in the mean time you will learn a lot, and you'll know
what you have done and why :-)
OK, let's start: fetch netmedic and install it on your harddisk.
Believe me: you'll love this
target for the sheer MIGHT that it gives (offensive and defensive) to all internauts
lost among the sees (and the currents) of the Web.
In fact you'll like netmedic so
much that you'll probably want to
register its next new version (which should deserve in my opinion a much more
complicated protection scheme :-)
STARTING THE SESSION: string fishing
We could have tackled this protection scheme inside the code of our
target in many other ways, like searching for the relative protection functions;
pinpointing the protection scheme through a debugger (the easiest if most boring
approach); getting the
pixel dimensions of the protection window elements with a screen ruler, individuating
the protection through a resource editor or even using a
software 'stepwatch' (great fun for graphical demos deprotection :-)
But we will for this lesson -once more- use the SIMPLEST way: the approach you
already began to
learn with lesson one: string fishing.
Run wdasm32 (by now you should have found on the web, or bought, version 8.9 of wdasm (see
my first lesson). If you are still using the demo version, what follows applies nevertheless,
yet you'll work on your target's code "on screen", using wdasm's own search capabilities,
and not inside your texteditor, since the demo version of wdasm does not allow the editing
and saving of the disassembled text file. Yet by the end of this lesson you should be enough
'semi-advanced' in our reversing art to be able to work on the essays inside
+HCU's project 0, where you'll find detailed instructions about
transforming the demo version of wdasm in a full working version adding or modifying all
disabled functions inside it. But wait, don't go there right now:
you better finish this lesson first :-)
The disassembly of a windows95 program can easily result in a huge
netmed.exe is a 1.016.832 bytes file, and wdasm89 -which I'm using as
instead of the more powerful (but newbyes scaring) IDA 3.7. for these lessons-
will give you a 11.927.177 bytes *.alf file: a dead listing that of 12 millions
bytes, quite a monster.
Of course you wont be able to handle well such a file inside Micro$oft Word, which is a
very sloppy wordprocessor by whatever standards you might choose to use.
I may suggest my personal
choice working under Windoze: Ultraedit32.
You'll have the possibility to customize its toolbar with your own 'reversing' macros and
it will be a lot quicker than Word. Moreover it will not crash and you'll be able
to cut and paste code without 'loosing' your mouse grip on the window (a very peculiar
characteristic of Micro$oft's abomination :-(
Choose a quiet evening and perform your string search, (using for instance
"expir"), see if something "bites" and once you think
you have found enough,
pull your nets out of the code and look at the many little poor zapping fishes you have
:00406A1B 6817E20000 push 0000E217 ID=57879: "Your advanced feature trial license expires in 1 day."
:00406A3A 6818E20000 push 0000E218 ID=57880: "Your advanced feature trial license expires in %d days." |
:00406A87 6819E20000 push 0000E219 ID=57881: "Your advanced feature trial license has expired."
:0043E901 6812E20000 push 0000E212 ID=57874: "Your evaluation period for Net.Medic has ended.
:0043F21F 6835E20000 push 0000E235 ID=57909: "Your advanced feature trial license expires in %d day(s)
:0043F234 6840094A00 push 004A0940 Data Obj ->"Your license expires in %d days."
Whoa! Even if something did slip
through we have pulled a full net of strings out of our target's code! We will not hunger
Notice that we already know WHERE are located the protection code snippets: in fact
we'll have a
protection snippet around 06A and another one from 3E9 to 3F2!
Let's prepare and sip our cocktail and continue:
DISSECTIONING OUR FIRST FISH
Let's pull out the guts of the first one
:00406A1B 6817E20000 push 0000E217 ID=57879: "Your advanced feature...expires in 1 day."
Before passing the protection code that dwells around this location under our
serene 'reversing' blick (we will do it in a minute), we better understand
first of all what's the
meaning of the code that precedes those 6 strings above. Let's look at the instruction
at location :00406A1B: 6817E20000
68 is push and 17E20000 is 0000E217
(which corresponds, hexadecimally to the decimal value ID=57879).
Why the pushing around?
First: all the strings you see are being PUSHED on the stack because a subsequent call
to a display function will need to know what to display on the screen. All the
parameters for any subroutine function must be pushed (the call must be prepared) before
executing the call, and the only (minor) question when you reverse is the ORDER of
this pushing. In C language it is rightmost parameter first,
in Pascal language the other way round.
Here we have a target written in C...
well, sort of, they have used overbloated Micro$oft's "Visual" C++...
would the authors have programmed with "real" C,
this target would have been a lot
smaller, yet some sort of degenerated C it is anyway,
and therefore the LAST parameter is pushed first on the stack... how do I know that
our target has been written and compiled with Micro$oft's VisualC++?
Well, the code is typical, and that would be more than enough, but if you want to
check on your own, just have a look at the list of IMPORTED FUNCTIONS (at the
beginning of your dead listing) and you'll find the following: Import Module 009:
MSVCRT40.dll... that's the (bad) Micro$oft's run-time library dll, as you'll see in
a moment... back to our pushing parameters around:
code snippet (from our very target) shows you for instance the typical and very simple
parameter passing for the ubiquitous messagebox function:
:00457586 6A00 push 00000000 ;0, the rightmost parameter
:00457588 68541E4A00 push 004A1E54 ;Data Obj ->"Error"
:0045758D 68481E4A00 push 004A1E48 ;Data Obj ->"Print Error"
:00457592 6A00 push 00000000 ;NULL, the leftmost parameter
:00457594 FF15A4864A00 Call dword ptr [004A86A4] ;USER32.MessageBoxA, Ord:0188h
Since the function MessageBox has 4 parameters,
int MessageBox(hwndParent, lpszText, lpszTitle, fuStyle)
you can see inside the target's code four pushes just before the call, so
easy and simple is that.
The above code means therefore:
MessageBoxA(NULL, Print Error, Error, 0)... in other words:
"please draw a DialogBox without parent window (hwndParent=NULL) with the word
"Error" as title and the words "Print Error" as message text and with
a nice little OK button!
See, fuStyle is 0, and this
correspond to MB_OK, and therefore this message box will contain only one
push button with the tag OK written onto it...
In fact you could modify this last parameter determining the "button",
through your hexeditor, and for instance substitute
0x20 to the zero for the rightmost parameter. If you then re-run the program
(and then cause a print error),
the modified line :00457586 6A20 would give you MB_ICONQUESTION instead of MB_OK,
and a silly question-mark icon
would in this case appear in the message box. Pretty cheap reversing
thrills, in this specific case,
yet I'm sure you are getting the point:
substituting parameters inside alien code it's very easy and
may be great fun!
Yet the 'MessageBox'
explanation above is fully valid only for ONE of the above fishes: the last string at
3F234, which is
directly located inside our target's data:
:0043F234 6840094A00 push 004A0940 Data Obj ->"Your license expires in %d days."
So it pushes a string located inside the target, at A0940!
Now -for this string- we will use psedit (or another hexeditor, see lesson1, the "tekfact" lesson)
and search it "physically" inside the BYTES
of our target, (I repeat: put now aside our disassembled wdasmed dead listing,
and look at the bytes of netmed.exe).
search for this string we find it at 9ED40:
9ED40 596F7572206C6963656E736520657870 Your license exp
9ED50 6972657320696E20256420646179732E ires in %d days.O mann_o_mann!
Hey! It is not at A0940!ou use your brand new base calculator, that
you should have downloaded and installed on your harddisk and notice a staggering
difference of 1C00 bytes! What's going on? Where does this difference come from?
Man! I thought you just pushed
the stupid data location for the call, getting thattaway the string you pushed for,
automagically, appear! Hey reverser+, what's going on?
Quiet! I have the pleasure
to introduce to you the subdivisions of all
windozes' programs (in fact of ALL programs but the most tight ones): have a
look at the TOP of your netmed's dead listing! The very first table you'll find there
is the following
Number of Objects = 0007 (dec), Imagebase = 00400000h
Object01: .text RVA: 00001000 Offset: 00000400 Size: 00087400 Flags: 60000020
Object02: .rdata RVA: 00089000 Offset: 00087800 Size: 00014C00 Flags: 40000040
Object03: .data RVA: 0009E000 Offset: 0009C400 Size: 00006200 Flags: C0000040
Object04: .idata RVA: 000A7000 Offset: 000A2600 Size: 00002E00 Flags: C0000040
Object05: .tls RVA: 000AA000 Offset: 000A5400 Size: 00000200 Flags: C0000040
Object06: .rsrc RVA: 000AB000 Offset: 000A5600 Size: 00046A00 Flags: 40000040
Object07: .reloc RVA: 000F2000 Offset: 000EC000 Size: 0000C400 Flags: 42000040
So, netmed.exe has 7 objects. Don't worry about their names and exact meanings for yet, just
remember that .text and .data are the two important (and standard) ones. .text is CODE
and .data is DATA.
Now look: the Imagebase is at 400000 and the DATA (Object03), is at
RVA 9E000 with offset 9C400.
And as you can easily check (using for instance
your brand new basecalculator
tool) once you take away the
dummy 400000 of the Imagebase, the simple subtraction 0x9E000-0x9C400 gives us a
difference of 1C00... Bingo! This little mistery is cleared.
Yet where are the other little zapping strings we have fished out?
ALL the other protection strings inside
our target's code (our zapping fishes) are in unicode (all the ascii characters
have an interpolated null) and dwell at
locations that (apparently) have nothing to do with the direct data
locations pushed above... for instance,
0xE217 (which is ID=57879) does NOT directly correspond to a code location... hey bud, what's that? Keep cool, just read on
:00406A1B 6817E20000 push 0000E217 ;Your advanced... license expires in 1 day."
What's "E217"? Some sort of european food component?
No. it's a stringtable pointer... see: 0xE217 is decimal 57879. Yet if we have look at
the hexes of our target with an hexeditor... our string begins at 0xE8DFC... what's going on here?
E8DFA 350059006F00 5.Y.o.
E8E00 7500720020006100640076006100 u.r. .a.d.v.a.n.
E8E10 6E00630065006400200066006500 c.e.d. .f.e.a.t.
Well: this is a pointer to the STRINGTABLE!
I'l show you a part of the stringtable of our target, which you could easily inspect
using a resource monitor tool like BRW from Borland... you will learn how to use this
reversing tool later studying the +HCU's essays, for the moment just have a look at this STRINGTABLE snippet):
57874, "Your evaluation period for Net.Medic has ended. You can continue using
57879, "Your advanced feature trial license expires in 1 day."
57880, "Your advanced feature trial license expires in %d days."
57881, "Your advanced feature trial license has expired."
57882, "The feature you have requested is available only in the retail
57883, "Net.Medic from VitalSigns Software uses analysis techniques which
57884, "Some of the information above has been provided by Net.Medic from\r\nVitalSigns
57885, "Date and Time of Occurrence: "
57886, "Date and Time of First Occurrence: "
57887, "Date and Time of Last Occurrence: "
If we look at the data part of the target code we find above our string running from E8DFC
(That's the "Y" of
Your) to E8E66 (that's the full point after "day."). Look once more at the data dump
above... at E8DFA, immediately before the "Y",
we have a "3500": this is 0x0035, and represents the number of characters (it's decimal
Your advanced feature trial license expires in 1 day.
So the real "block" is E8dfA-E8e65. And somewhere inside the code a TABLE translates
the string number 57879 (0xE217) into a pointer to our location E8defA... where?
Well as you'll see, the MFC40 function "NoName" 185 is an equivalent of a
LoadString function, and it works like this:
:00406A1B 6817E20000 push 0000E217 ;Your advanced... license expires in 1 day."
:00406A20 8D4DA8 lea ecx, dword ptr [ebp-58] ;prepare call
:00406A23 E8BC0C0800 Call 004876E4 ;MFC40:NoName0185, Ord:0E48h; call_this
This means: call MFC40:NoName0185 with a pre-determined cx and the stringtable ID pushed.
OK, we have seen enough, if you want to see what the cuckoo MFC40/185 does you know
already what you have to do: just disassemble MFC40.DLL and look at the dead listing!
A string table consists of one or more separate resources, each containing exactly 16
strings. The maximum length of each string is 255 bytes. One or more strings in a
block can be null or empty. The first byte in the string specifies the number of
characters in the string. (For null or empty strings, the first byte contains
the value zero.)
Windows uses a 16-bit identifier to locate a string in a string-table resource.
Bits 4 through 15 specify the block in which the string appears;
bits 0 through 3 specify the location of that string relative to the beginning of the block.
Time to get back to our little fishes and crack them, before they get stale.
Sniffing the code
Let's have a look at the code where our first string
"Your advanced... license expires in 1 day"
is pushed for the call: as we have already noticed, part of the protection scheme
MUST dwell around 00406Axx; here you have the relative code snippet, that I have commented
for you. Don't worry too much right now, get this snippet on your screen (or on
paper) as well and just try to follow:
* Referenced by a Jump at Address:069BF(C)
:069EC E8CF7D0100 call 0041E7C0 ;get PROT parameter good/bad guy !!!!!!
:069F1 83C404 add esp, 4 ;correct stack after call
:069F4 898542FFFFFF mov dword ptr [ebp+FFFFFF42], eax ;save PROT parameter
:069FA 83F801 cmp eax, 1 ;is it one?
:069FD 757F jne 00406A7E ;go check if PROT is three, or what else
:069FF E83C800100 call 0041EA40 ;else PROT = one, call this function
:06A04 8BF8 mov edi, eax ;and save in edi how many days this guys has left
:06A06 8D4DA8 lea ecx, dword ptr [ebp-58] ;and do whatever the following call
:06A09 E8CA0C0800 Call 004876D8 ;MFC40:NoName0183, Ord:01E6h, ;MFC40/183 does
:06A0E C645FC03 mov [ebp-04], 03 ;and flag with three [ebp-04]
:06A12 85F6 test esi, esi ;and check esi
:06A14 7455 je 00406A6B ;go flag [ebp-04] with 2, else
:06A16 83FF01 cmp edi, 1 ;does he have only one day left, the poor chap?
:06A19 751F jne 00406A3A ;no, then go calculate how many days left, else
:06A1B 6817E20000 push 0000E217 ;Your... license expires in 1 day"
:06A20 8D4DA8 lea ecx, dword ptr [ebp-58] ;prepare call
:06A23 E8BC0C0800 Call 004876E4 ;MFC40:NoName0185, Ord:0E48h; call MFC40/185
:06A28 85C0 test eax, eax ;did it work?
:06A2A 7430 je 00406A5C ;no:continue without ax/cx and without call213
:06A2C 8D45A8 lea eax, dword ptr [ebp-58] ;yes get ax
:06A2F 8D4DF0 lea ecx, dword ptr [ebp-10] ;yes get cx
:06A32 50 push eax ;and use ax for the following call213
:06A33 E8480D0800 Call 00487780 ;MFC40:NoName0213, Ord:02F8h ;call MFC40/
:06A38 EB22 jmp 00406A5C ;then continue
:let's tell you how many days you have left (from 6A19)
:00406A3A 6818E20000 push 0000E218 ;Your... license expires in %d days"
:00406A3F 8D4DA8 lea ecx, dword ptr [ebp-58]
:00406A42 E89D0C0800 Call 004876E4 ;MFC40.MFC40:NoName0185, Ord:0E48h
:check if parameter PROT is three (from 69FD):
:00406A7E 83BD42FFFFFF03 cmp dword ptr [ebp+FFFFFF42], 3 ;guy's parameter it's three
:00406A85 751C jne 00406AA3 ;if not three check what else it is
:00406A87 6819E20000 push 0000E219 ;and push "Your advanced license has expired"
:00406A8C 8D4DF0 lea ecx, dword ptr [ebp-10] ;beggar off bad guy
Well, an impressive code snippet with not only one but THREE zapping strings!
We can already understand some easy elements of this protection. The scheme checks right at the
beginning a protection PARAMETER, calling function 1E7C0 (that we will examine in a minute).
ON return we save the return value in [ebp+FFFFFF42] (which is [ebp-BE] probably). And chooses
different strings depending from these values.
We know now that:
call 1E7C0 and get "kinda_guy" return value"
if kinda _guy=1 then
call 1EA40 and save in edi how many days this guy has left
call MFC40/143 and check esi return value
and flag [EBP-4] with 2 or 3 accordingly
if the guy has 1 day tell him
else tell him how many days he has left
if kinda_guy=3 then
tell the guy that his license his expired.
I would say that our work is already (almost) finished.
Let's examine the code at the call that triggers the whole "kinda_guy" value,
we will surely learn more.
Where we will crack:
The following code snippet is the routine that rturns the "kinda_guy" parameter.
We already know that a value of 1 means a "time limited trial" and that a value of
3 means that "your allowed time limit has expired". Well, an immediate legitime quastion for
any researcher mind would be: "What about parameters "0", "2" and "4", "5", etcetera?
In the code below of our kinda_guy routine, you'll see many calls to OTHER routines (of
course) some of them are calls to MSVCRT40.DLL, like the following sscanf:
:0041E8A6 8B3D44854A00 mov edi, dword ptr [004A8544];MSVCRT40.sscanf, Ord:0442h
:0041E8AC C645C000 mov [ebp-40], 00 ;zeroing memory location [ebp-40]
:0041E8B0 51 push ecx ;pass parameter cx for sscanf
:0041E8B1 68A4F54900 push 0049F5A4 ;->"%x" ;pass parameter
:0041E8B6 52 push edx ;pass parameter dx for sscanf
:0041E8B7 FFD7 call edi ;sscanf(dx,%x,cx)
Clearly you may legitimately ask what the cuckoo is going on here. Well, I told you that
this target has been written with Microsoft Visual C++, and therefore read (and head) the
MSVCRT40 is "Microsoft C Runtime Library Version 4.10.6038"
for "Microsoft Visual C++", as you can see through any hexeditor inside the very code of
What the cuckoo is a "runtime library"?
A run time library is a set of hundreds of predifined
functions and macros, designed for use in C programs.
Run time libraries make programming (and cracking) easier,
providing tne following:
- an interface to operating system functions (such as opening
and closing files)
- fast functions to perform common programming task (such as
string manipulation, "sparing the programmer the time and
effort needed to write such functions".
Run time libraries are specially important in high level programming
because all "high level" programmers rely on libraries for basic functions not
provided by their languages: input and output, storage allocation, process
These C functions try to maintain a maximum system compatibility
between DOS, Xenix and Unix, and have mostly the same names in
the C runtime libraries for Unix operating systems.
OK, let's have a look "inside" our MSVCRT40.DLL (you'll find a couple
of them, at least, inside your own harddisk, disassemble one of them
with wdasm (or IDA) and look, for instance, at the very simple
in c you would have:
int toupeer(c); converts c to uppercase if appropriate
int _toupper(c); converts c to uppercase
and therefore you would for instance use this kind of programming code:
printf("_toupper =%#04x", _toupper(ch));
Well, let's look at its implementation INSIDE our disassembled MSVCRT40.DLL:
Exported fn(): _toupper - Ord:036Fh
:10215450 8B442404 mov eax, dword ptr [esp+04]
:10215454 83E820 sub eax, 00000020
:10215457 C3 ret
This means: get passed parameter inside eax
return home with subtracted parameter...
and since reverser is (ASCII) f r a v i a
66 72 61 76 69 61
if I call _toupper six times, one for each letter, this will return me
46 52 41 56 49 41
which, as you have guessed is:
F R A V I A
So easy is that (in assembly :-)
In fact it is so easy that one wonders why you should use twelwe bytes
to perform the same uppercasing (with a lot more CPU-cycles and a call
to a DLL outside your main exe code):
53 push ebx ;throw lowercase char_value
FF15C8854A00 Call dword ptr [004A85C8] ;MSVCRT40.toupper, Ord:0461h
83C404 add esp, 00000004 ;correct stack
8BD8 mov ebx, eax ;save uppercased returned char
instead of just using directly inside your program the three bytes:
83E820 sub eax, 00000020 ;uppercase char in ax
...misteryes of high level languages, I presume. Multiply this for MUCH more
complexes routines and MANY more bytes and you will quickly understand WHY the
programs that you are using nowadays
- are slow
- are huge
- need powerful CPUs and a lot of memory
- compel you to upgrade PC every two years
Let's look at our target's code now, it's time to CRACK!:
Let's have a look at the routine returning the "kinda_guy" parameter:
this code snippet is CALLED from three different points (:004069EC, :0041ED0D, and :0043F1F1)
it has also to do with a Menu whose ident is [ID=807Fh], as you will easily see just perusing
the routine, which starts at 41E7C0. Here follows ONLY the most interesting part of it for us,
but if you examine ALL its code, you'll see that we have already had another important
C++ functions: a strncpy and a sscanf(dx,%x,cx) and that now we come to the second
sscanf function: sscanf(ax,%x,cx).
....(started at 1E7C0)
:0041E8BC 8D4DEC lea ecx, dword ptr [ebp-14] ;prepare cx for call
:0041E8BF 8D45B8 lea eax, dword ptr [ebp-48] ;prepare ax for call
:0041E8C2 51 push ecx ;pass parameter cx for sscanf
:0041E8C3 68A4F54900 push 0049F5A4 ;->"%x" ;pass parameter
:0041E8C8 50 push eax ;pass parameter
:0041E8C9 FFD7 call edi ;sscanf(ax,%x,cx)
:0041E8CB 83C40C add esp, 0000000C ;correct stack
:0041E8CE 8B45F0 mov eax, dword ptr [ebp-10] ;get value
:0041E8D1 3501101000 xor eax, 00101001 ;xor it with 0010 1001
:0041E8D6 3B45EC cmp eax, dword ptr [ebp-14] ;is it like cx, once xored?
:0041E8D9 7521 jne 0041E8FC ;No? Beggar off: EVIL JUMP
:0041E8DB C745FCFFFFFFFF mov [ebp-04], FFFFFFFF ;dummy parameter
:0041E8E2 E831010000 call 0041EA18 ;dummy end call
:0041E8E7 B804000000 mov eax, 4 ;GOOD "registered" FLAG ax=4
* MAIN EXIT: Jump at Addresses :0041E916(U), :0041E936(U), :0041E968(U),
:0041E974(U), :0041E98A(U), :0041EA09(U)
:0041E8EC 8B4DF4 mov ecx, dword ptr [ebp-0C] ;poppall and exit
:0041E8EF 5F pop edi
:0041E8F0 64890D00000000 mov dword ptr fs:, ecx
:0041E8F7 5E pop esi
:0041E8F8 8BE5 mov esp, ebp
:0041E8FA 5D pop ebp
:0041E8FB C3 ret ;RETURN after check
* Referenced by a Jump at Address:0041E8D9(C) EVIL JUMP
:0041E8FC 833DA0F5490000 cmp dword ptr [0049F5A0], 0 ;d'we have a zero there?
:0041E903 7413 je 0041E918 ;if so go ahead and xor other things
:0041E905 C745FCFFFFFFFF mov [ebp-04], FFFFFFFF ;else dummy parameter
:0041E90C E807010000 call 0041EA18 ; dummy end call
:0041E911 B801000000 mov eax, 00000001 ;trial FLAG ax=1
:0041E916 EBD4 jmp 0041E8EC ;RET WITH trial FLAG 1
* Referenced by a Jump at Address:0041E903(C) EVIL JUMP
:0041E918 8B45F0 mov eax, dword ptr [ebp-10] ;clean and ready for xor once more
:0041E91B 3510101100 xor eax, 00111010 ;this time xor with 0011 1010
:0041E920 3B45EC cmp eax, dword ptr [ebp-14] ;is it, xored, like cx?
:0041E923 7513 jne 0041E938 ;No? Go ahead and xor 1101 1101
:0041E925 C745FCFFFFFFFF mov [ebp-04], FFFFFFFF ;else dummy parameter
:0041E92C E8E7000000 call 0041EA18 ; dummy end call
:0041E931 B805000000 mov eax, 00000005 ;Not yet advanced funct flag 5
:0041E936 EBB4 jmp 0041E8EC ;ret with bad flag ax=5
* Referenced by a Jump at Address:0041E923(C) EVIL JUMP
:0041E938 8B45F0 mov eax, dword ptr [ebp-10] ;ready for xor once more
:0041E93B 3501110111 xor eax, 11011101 ;this time xor with 1101 1101
:0041E940 3B45EC cmp eax, dword ptr [ebp-14] ;is it, xored, like cx?
:0041E943 7534 jne 0041E979 ;if yes check time else ret with
:0041E945 6A00 push 00000000 ;trial ax=1 and time unchecked
:0041E947 FF1548864A00 Call dword ptr [004A8648] ;MSVCRT40.time, Ord:045Dh
:0041E94D 83C404 add esp, 00000004
:0041E950 2B45F0 sub eax, dword ptr [ebp-10] ;number of second elapsed, subtracted
:0041E953 C745FCFFFFFFFF mov [ebp-04], FFFFFFFF ; dummy parameter
:0041E95A 3BC6 cmp eax, esi ;check if user has time or not
:0041E95C 760C jbe 0041E96A ;below, return with trial flag one
:0041E95E E8B5000000 call 0041EA18 ; dummy call
:0041E963 B803000000 mov eax, 3 ;else return with flag ax=3:
:0041E968 EB82 jmp 0041E8EC ;flag ax=3= no more time!
* Referenced by a Jump at Address:0041E95C(C)
:0041E96A E8A9000000 call 0041EA18 ; dummy call
:0041E96F B801000000 mov eax, 1 ;trial FLAG 1: time not to be...
:0041E974 E973FFFFFF jmp 0041E8EC ;...checked or something: return 1
* Referenced by a Jump at Address:0041E943(C)
:0041E979 C745FCFFFFFFFF mov [ebp-04], FFFFFFFF ;dummy parameter
:0041E980 E893000000 call 0041EA18 ;dummy call
:0041E985 B801000000 mov eax, 1 ;trial flag 1
:0041E98A E95DFFFFFF jmp 0041E8EC ;return with trial flag 1
OK, we can now experiment a little...
since the return flag of this function is important, you may want to see
what happens when you return: 0, 2, 4 (just try it :-) and 5. (parameter fiddling).
The big question is of course: what is the sscanf above?
The sscanf function reads data from buffer into the locations given by each argument.
Every argument must be a pointer to a variable with a type that corresponds to a type
specifier in format.
The sscanf function returns the NUMBER of fields that were successfully converted
and assigned. The return value does not include the fields that were read but
The return value is EOF for an attempt to read at end of string
A return value of ZERO means that no fields were assigned
among other things sscanf calls _isctype and isspace!
isspace is a test for white space chars (0x9, 0xD, 0x20)
_isctype is a test for integer values in an ASCII environment
(alphanumeric, letters or ASCII)
So we have here the part of the target code that checks if the name and
the serial number you have entered are or, are not, valid.
Actually, to be simple, sscanf goes into a string and interprets it according
to the format specifications... let's seay you want for instance to
convert a STRING OF DIGITS INTO AN INTEGER (typical of password protections :-)
In that case you would write in C:
sscanf (char_array_digitz, "%d",&intstorez);
this statement will convert the string of digits held inside the array of type char
into type int, and store it into the variable intstorez
Well, if you want to learn
more you'll find many resources about C language (and sscanf) online... just read on!
You'll notice that we have THREE xoring operations inside the code above: it work like this:
8B45F0 mov eax, dword ptr [ebp-10] ;get value
:0041E8D1 3501101000 xor eax, 00101001 ;1) here xor it with 0x00101001
:0041E91B 3510101100 xor eax, 00111010 ;2) here xor it with 0x00111010
:0041E93B 3501110111 xor eax, 11011101 ;3) here xor it with 0x11011101
3B45EC cmp eax, dword ptr [ebp-14] ;[ebp-14] like xored [ebp-10]?
XOR is a "logical" operation, like and, not and all the other
that you'll learn if you continue on your cracking path.
You take the bits of your
two operands and check, each bit of the resultant dword is set to 1 ONLY if the corrisponding
bits of the two operands contain opposite values.
So, first of all the three right operands of our three XORs are HEXADECIMAL values, in a
"binary-similar" format: 0x00101001, 0x00111010, 0x11011101.
(Watch it when you will search for these instructions inside the listing, there
are two occurrences inside our target, and the second one is the correct one, as you'll
notice if you acquire the GOOD practice of ALWAYS checking the code "around" your search
At your base calculator! Use hexadecimaml values and xore them, and see how
they see out in binary (and how the xoring affects the bits!)
In my case we have always Ox34829AB7 inside [ebp-10]
which, xored with 0x01101000 unfortunately does not give
0x25838bb6 (the value that I always had inside [ebp-14]... therefore we are
unfortunately NOT REGISTERED!
This Ox34829AB7 that we have inside [ebp-10]
xored with 0x10101100 (at the second check) does not give either
0x25838bb6 any more (it would have done it at the beginning, by the first run of our target)
therefore we don't get flag 5 and that means that we have CHOSEN THE SPECIAL advanced features
Finally, our Ox34829AB7, that we have inside [ebp-10]
xored with the third xoring choice: 0x01110111 does indeed
give us 0x25838bb6 therefore we have snapped the advanced features and
we are running the trial time flag.
What do we learn from this, besides using our base calculator?
1) That the only thing that matter is to get flag 4, therefore we just need to xor with
0x01110111 at the FIRST occurrence to have it every time!
2) That even xoring with 0x01110111 at the SECOND OCCURRENCE would be good, since we would
have 'just started' flag 5 every time.
3) That the only thing we really don't need is xoring 0x01110111 at the third xoring
occurrence, since that's the "normal" trial period checking.
4) That this protection is incredibly lame and that the programmer should NEVER have
included a 'registering' flag inside the target.
In order to crack the above target you may of course now
choose HUNDRED different solutions, and you may apply the crack wherever you want
inside the get the flag subroutine.
Now that you have seen the protection scheme naked, try to work a little on this using
the DEBUGGER that you have downloaded for the previous "tekfact" lesson (iwindebug).
Set some location breakpoints just before the xores above, and follow in the register
window (and in the memory windows) what's going on. You'll have a taste of the 'live'
approach to cracking, that we will use in the next lessons. Dead listing and live approach
together can reverse ANY code you will encounter on your way, even if it has been written on
Mars (or at Micro$oft :-)
I showed you enough, I'm not going to give you ANY ready made solution to crack this
don't need it any more. In fact I will never give ready made cracks again, you are (gonna be)
real crackers: you don't need people chewing code for you and spitting out ready made solutions
any more! Reread this lesson, experiment a little and crack this target on
your own, it's very easy (now).
Well, I reckon that's enough for this second lesson.
(c) reverser+ December 1997. All rights reserved.
WATCH IT, THIS LESSON IS STILL UNDER CONSTRUCTION!
You are deep inside reverser's page of reverse engineering,
choose your way out:
Is reverse engineering legal?