Skip to content

Instantly share code, notes, and snippets.

@callowaysutton
Last active June 24, 2023 22:42
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save callowaysutton/7a045a940088a337469b47a3c801b154 to your computer and use it in GitHub Desktop.
Save callowaysutton/7a045a940088a337469b47a3c801b154 to your computer and use it in GitHub Desktop.
The Booklet of Hacking
HOW TO CRACK, by +ORC, A TUTORIAL
LESSON C (1) - How to crack, Cracking as an art
[BARCODES] [INSTANT ACCESS]
[BARCODES]
First of all, let me stress the importance of cracking in
our everyday life. Cracking it's not just about software, it's
about information, about all patterns of life. To crack is to
refuse to be controlled and used by others, to crack is to be
free. But you must also be yourself free from petty conventions
in order to crack properly.
You must learn to discerne cracking possibilities all around
yourself, and believe me, the development of this ghastly society
brings every day new codes, protections and concealing
mechanismes.
All around us grows a world of codes and secret and not so
secret patterns. Codes that are at times so familiar and common
that we do not even notice them any more... and yet they are
there to fool us, and yet they offer marvellous cracking
possibilities.
Let's take as an striking example BARCODES... those little
lines that you see on any book you buy, on any bottle you get,
on any item around you... do you know how they work? If you do
not you may be excused, but you cannot be excused if you never
had the impulse to understand them... crackers are curious by
nature... heirs of an almost extinct race of researchers that has
nothing in common with the television slaves and the publicity
and trend zombies around us. Cracker should always be capable of
going beyond the obvious, seek knowledge where others do not see
and do not venture.
[BARCODE HISTORY]
Let's begin with a little history. Universal Product Code
(UPC) was adopted for commercial use by the grocery industry in
the USA. Among the advantages were a rapid, accurate and reliable
way of entering stock information into a computer and the
possibility to sack a lot of workers and to do more profit. The
early success led to the development of the European Article
Numbering System (EAN), a symbology similar to UPC, that is
widely used in Europe and in the rest of the World. I'll teach
you to crack this one, since I do not -fortunately- live in the
States. Keep in mind, anyway, that there are different barcode
symbologies, each with its own particular pattern of bars. The
UPC/EAN code used on retail products is an all-numeric code; so
is the Interleaved 2 of 5 Code. Code 39 includes upper case
letters, digits, and a few symbols. Code 128 includes every
printable and unprintable ASCII character code. The most new one
is a 2-D code. These are special rectangular codes, called
stacked barcodes or matrix codes. They can store considerably
more information than a standard barcode. They require special
readers which cost more than a standard scanner. The practical
limit for a standard barcode depends on a number of factors, but
20 to 25 characters is an approximate maximum. For applications
that need more data, matrix codes are used. For example, the next
time you receive a package from United Parcel Service look for
a small square label with a pattern of dots and a small bullseye
in the centre. This is a MaxiCode label, and it is used by UPS
for automatic destination sortition.
The manufacturer's ID number on the barcode uniquely
identifies products. These numbers are managed by the Uniform
Code Council in Dayton, Ohio for the States and Canada and by the
EAN authority (Internationale Article Numbering Association) in
Bruxelles, for Europe and the rest of the World. The
manufacturer's ID number accounts for some digits of the code,
which leaves other digits to be assigned in any way the producer
wants. He provides retail outlets with a list of his products and
their assigned codes so that they can be entered in the cash
register system. Many codes are NOT on the products and are added
by the supermarkets on the fly, using an internal code schema
that may be non standard. Now it's enough... let's crack.
BARCODES are the only thing an automated casher needs to see
on a product to calculate its price and automatically catalogate
the sold merchandise... imagine (just imagine it :=) coz it would
be extremely illegal to act in this way) somebody would fasten
an adhesive home-made codebar label direct on the top of the
supermarket/mall/retail store label, say on a bottle of Pomerol
(that's a very good but unfortunately very expensive french
wine).
The new label would mean for the casher something like
"cheap wine from Bordeaux, France, cost so and so, everything
it's OK, do not worry"... do you think that anybody would come
to the idea that there is something wrong with the label, with
the bottle or with you? I have been codebaring for years and had
only once a problem, coz my printer was running out of ink and
the scanner in the supermarket could not read it... so what? Act
uninterested, always wear jackets of the utmost quality, shetland
pullovers and beautiful expensive shoes... (all articles that you
may codebar too, by the way), in this society appearance and look
count much more than substance and knowledge... LET'S USE THIS
TO OUR ADVANTAGE! Nobody will ever come to the idea that you may
actually really know the working of the scheme... coz codebar is
pretty complicated and not exactly exceptionally public. On the
Web there are a lot information about it, but most of them are
useless, unless you know how to search most of the time you'll
find only sentences like this one:
"The calculated check digit is the twelfth and final
digit in the U.P.C.code. It is calculated based on a
specific algorithm, and is necessary to ensure that
the number is read or key-entered correctly."
But good +ORC will now explain you everything you need to crack:
[THE 13 BAR "CODES"]
Each barcode label has 13 values, from #0 to #12 (that's the EAN
code, the UPC american one has only 12, from #0 to #11).
#0 and #1 indicate the origin of the product.
#2 to #11 give the article code
#12 (the last and 13th one) is a checksum value, that
verifies the validity of all the other numbers.
How is it calculated? #12 is calculated in 4 steps
VALUE A: You sum odd position numbers (#0+#2+#4+#6+#8+#10)
VALUE B: You sum even position numbers and multiply by 3
((#1+#3+#5+#7+#9+#11)*3)
VALUE C: You sum value A and value B
VALUE D: You mod value C (you divide by 10 and only keep
the remaining units, a very widespread checking scheme as
you'll see in the software part of this lesson)
If the result is not zero, you subtract it from 10.
Now look at a barcode label, get some books or other barcoded
items and *watch* it...
Bar codes are supposed to have "quiet zones" on either side of
the symbol. Quiet zones are blank areas, free of any printing or
marks,typically 10 times the width of the narrowest bar or space
in the bar code. Failure to allow adequate space on either side
of the symbol for quiet zones can make it impossible to read the
bar code.
On the barcode there are two "borders", left and right, and a
"middle" longer line. These three lines are longer than the
others and are used to "regulate" the scanner to whatever
dimension has been used for the barcode.
#0 dwells left of the first (left) border and has a special
meaning, the other 12 numbers are written "inside" the code and
are divided in two "groups" by the middle bar.
Each value is coded through SEVEN bars: black=1 and White=0.
These form two couples of "optic" bars of different widths.
We come now to the "magic" part: In order to bluff the
simpletons, barcode uses three different SETS of characters to
represent the values 0-9. This should make it impossible for you
to understand what's going on, as usual, in this society, slaves
should not need to worry with the real functioning of things.
Here are the graphic codes of the three graphic sets:
CODE A CODE B (XOR C) CODE C (NOT A)
0: 0001101 (13) 0100111 (39) 1110010 (114)
1: 0011001 (25) 0110011 (51) 1100110 (102)
2: 0010011 (19) 0011011 (27) 1101100 (108)
3: 0111101 (61) 0100001 (33) 1000010 (066)
4: 0100011 (35) 0011101 (29) 1011100 (092)
5: 0110001 (49) 0111001 (57) 1001110 (078)
6: 0101111 (47) 0000101 (05) 1010000 (080)
7: 0111011 (59) 0010001 (17) 1000100 (068)
8: 0110111 (55) 0001001 (09) 1001000 (072)
9: 0001011 (11) 0010111 (23) 1110100 (116)
Borders: 101
Centre: 01010
- The C graphic set is a "NOT A" graphic set.
- The B graphic set is a "XOR C" graphic set.
- each value has two couples of bars with different widths
Now watch some labels yourself... see the difference between the
numbers left and the numbers right? The first "half" of the
barcode is coded using sets A and B, the second "half" using set
C. As if that were not enough, A and B are used inside the first
"half" in a combination that varies and depends from value #0,
following 10 different patterns:
#1 #2 #3 #4 #5 #6
0 A A A A A A
1 A A B A B B
2 A A B B A B
3 A A B B B A
4 A B A A B B
5 A B B A A B
6 A B B B A A
7 A B A B A B
8 A B A B B A
9 A B B A B A
"Ah! Stupid buyer will never understand why the same values gives
different bars! Nothing is as reliable as barcodes!" :=)
Let's take as example the codebar for Martini Dry:
BARCODE: 8 0 00570 00425 7
Let's see: we have a 8 0 0 = booze
Then a 000570 as ABABBA and a 004257 as C
"Even" sum: 8+0+5+0+0+2 = 15 (even sum)
Then a 0+0+7+0+4+5= 16 and 16 *3 = 48 (odd sum)
Then a 15+48=63
63 === 3
10 - 3 = 7 = checksum
Pattern = 8 = ABABBA CCCCCC
OK, one more example: Osborne Windows programming series Volume
2 General purpose API functions (always here on my table)...
BARCODE: 9 7 80078 81991 9
Let's see: we have a 9 7 8 = book
Then a 780078 as ABBABA and a 819919 as C
"Even" sum: 9+8+5+8+8+4 = 42 (even sum)
Then a 7+1+5+2+4+4= 23 and 23 * 3 = 69 (odd sum)
Then a 42+69=111
111 === 1
10 - 1 = 9 = checksum
Pattern = 9 = ABBABA
Well... what's the point of all this?
The point, my pupils, is that who DOES NOT KNOW is taken along
on a boat ride, who KNOWS and LEARNS can use his knowledge in
order to try to beat blue and black the loathsome consumistic
oligarchy where we are compelled to live. Try it out for
yourself... if you crack correctly and wisely your supermarket,
mall and library bills will be cut to almost zero.
Write a small program to print whichever codebar you fancy
(or whichever your mall uses) in whichever size on whichever sort
of label you (or better your targets) fancy... it's quickly done
with Visualbasic or Delphy... but you'll not find much on the Web
Alternatively you could also write, as I did long ago, a short
c program in dos, using a modified upper char set... and there
you are, have labels... see the world.
A small word of caution... crack only ONE item at time and
try it out first with the SAME label for the same product... i.e.
the correct code for that item, but on your own label. If it goes
through your program works good, if not, nobody will ever be able
to harm you. Anyway it never happens anything, never: the bar
code reading equipments have great tolerance, coz the scanners
must be able to recognize barcodes that have been printed on many
different medias. You should choose labels similar to the ones
effectively used only in order not to arise human suspects, coz
for all the scanner itself cares, your label could be pink with
green stripes and with orange hand-written, numbers. Mind you,
we are still just academically imagining hypothetical situations,
coz it would be extremely illegal to act in such an inconsiderate
manner.
CRACKING POWER! It's true for barcodes, for Telecom bills,
for Compuserve accounts, for Amexco cards, for banking cheques
(do you know what MICR is? Magnetic Ink Character Recognition...
the stylized little printing on the lower left of new cheques...
there is a whole cracking school working on it), for registration
numbers... you name it, they develope it, we crack it...
Begin with barcodes: it's easy, nice and pretty useful! Live
in opulence, with the dignity and affluence that should always
distinguish real crackers. Besides... you should see the
assortment of 'Pomerols' in my "Cave-a-vin" :=)
[INSTANT ACCESS]
The (c) Instant access routines are a commercial protection
scheme used to "unlock" complete commercial applications that
have been encrypted on CD-
ROMs which are distributed (mostly) through reviews.
This is an ideal cracking target: it's commercial software,
complete, uncrippled and of (relatively) prominent quality, that
you can get in tons for the price of a coke. Obviously this kind
of protection represents an ideal subject for our lessons. This
fairly intricate protection scheme has not yet been cracked by
anybody that I am aware of, anyway not publicly, therefore it's
an ideal candidate for a "strainer" to my university. I'll teach
you here how to crack it in three lessons, C.1, C.2 and C.3. I warn
you... it's a difficult cracking session, and this protection
represents quite an intellectual challenge. But if you are
seriously interested in our trade you will enjoy these lessons
more than anything else.
This cracking is intended as an "assignment" for my +HCU
"cracking university": you'll find inside lessons C.1 and C.2 a
relatively deep "introduction" to Instant access cracking. This
will teach you a lot anyway, and spare you hours of useless
roaming around, bringing you straight to the cracking point. But
I'll release the third part of this session, with the complete
solution (lesson C.3) on the Web only in october 1996, not a day
before. All the students that would like to apply to the Higher
Cracking University, opening on the web 01/01/1997, should work
in July, August and September (three months is more than enough
time) on this assignment. They should crack completely the
instant access scheme and send me their solutions, with a good
documentation of their cracking sessions, before 30/09/1996
(WATCH IT! You can crack this scheme in -at least- three
different paths, be careful and choose the *best* one. WATCH IT!
Some of the informations) in lesson C.1 and C.2 are slightly incorrect:
check it!).
There are four possibilities:
1) The candidate has not found the crack or his solution is
not enough documented or not enough viable... the candidate
is therefore not (yet) crack-able, he will not be admitted
to the +HCU 1997 curses, better luck in 1998;
2) The cracking solution proposed by the candidate is not as
good as mine (you'll judge for yourself in october) but it
works nevertheless... he'll be admitted at the 1997
courses;
3) The cracking solution of the candidate is more or less
equal to mine, he'll be admitted, personally monitored, and
he'll get all the material he needs to crack on higher
paths;
4) The cracking solution of the candidate is better than mine,
he'll be admitted, get all the material he wishes and asked
to teach us as well as study with us: "homines, dum docent,
discunt".
[Cracking Instant access]
The user that wants to "unlock" a software application
protected with (c) Instant Access must enter first of all a
REGISTRATION number string, which through a series of
mathematical manipulations gives birth to a special "product"
code. On the basis of this "product code" the user is asked to
phone the commercial protectors (and pay) in order to get a
special "unlock code" that will allow him to decrypt the relevant
software.
This kind of "passnumber" protection routines are widely
used for software unlocking, BBS access, server access, backdoor
opening and many other protection schemes. We have already seen
password cracks in different lessons of this tutorial (in
particular Lessons 3.1 and 3.2 for DOS and Lessons 8.1, 8.2 and
9.1 for WIN) albeit on a more simplistic scale: there it did
mostly not matter very much *HOW* you passed the protection: once
passed, you could have access to the application. This is not the
case with (c) Instant Access. Face it: it's a little boring, but
important that you learn how to defeat intricate protection
routines (you'll meet them often in the next years) and I believe
that the following example will give you a "feeling" for the
right cracking approach.
In this case we must not only "crack" this protection scheme
but also study it thoroughly in order to achieve our blessed
aims. This is a very good exercise: reverse disassembling will
teach you a lot of little tricks that you'll be able to use in
your other future cracking sessions.
Instant access (c) is a exceptionally widespread protection
scheme, and it should be relatively easy for you to gather some
encrypted software that has been protected with this method...
*DO IT QUICKLY!!* After the Web publishing of this lessons (I am
sending C.1 to 8 pages and 4 usenet groups on 25/06/1996) this
protection is obviously as dead as a Dodo. The "Accessors" guys
will have to conceive something smarter if they want to keep
selling "protections" to the lamer producers of "big" software.
BTW, if you are reading this and are working for some
commercial "protection" company, consider the possibility to
double cross your masters! Deliver me anonymously all the future
projects you are working on! That will amuse me, speed up the
advent of a true altruistic society and earn you the respect of
the better part of humanity.
As I said, many "huge" application are still protected with
this "Instant access" system. I have personally bought at least
7 or 8 "second hand" CD-ROMs packed full with Microsoft, Lotus,
Norton, Symantec, you name it, applications all "protected"
through this crap. The cost of this bunch of CD-ROMs was the
equivalent of a bottle of Dry Martini, maybe less. The same
software is sold, unlocked, to zombies and lusers for ludicrous
amounts of money.
Never buy CD-ROMs magazines when they appear! Be cool! Buy
them two or three months after the publishing date! Buy
"remainders" or "second hand" CD-ROM magazines "at kilo price"...
Come to think of it, never buy *anything* when it appears or when
some (paid) advertiser tells you to... remember that "trends",
"vogues", "fashions" and "modes" are only different names for the
whips that drill and chain the dull-witted slaves of this
loathsome society: "clever crackers consider cool, crack cheap,
cheat customary culture" (a rhetorical figure: an "Alliteration".
To defend yourself learn rhetoric... it's a more powerful and
more useful weapon than Kung-fu).
The "triple" password protection routine in (c) Instant
Access is very interesting from a cracker point of view. It's a
relatively complex scheme: I'll teach you to crack it in two
phases: First of all you must find the "allowed" registration
code, the one that "ignites" the "product code". We must crack
and understand this re_code first if we want to crack the rest.
Just for the records, I am cracking here (c) Action Instant
access version 1.0 (CD-ROM found on a old copy of "Personal
Computer World" of August 1994, packed full with encrypted Lotus,
Symantec, Claris and Wordperfect applications. Just to be sure
I crosschecked my results with another CD-ROM which also has
applications protected with (c) Instant Access: Paragon
Publishing's PC OFFICE: the protection scheme remains the same).
I am focusing for this lesson on the cracking of the specific
protection for the encrypted Symantec's Norton Utilities v.8.0.
Please refer to the previous lessons for the basic
techniques used in order to find the protection routine inside
our babe... for "low" cracking purposes you -basically- type a
number (in this case, where the input gets 10 numbers, we'll use
"1212-1212-12"), do your search inside the memory (s 30:0
lffffffff "your_string") and then set memory breakpoints on all
the relevant memory locations till winice pops (I know, I know,
buddies... there are more effective ways... but hold your mouth:
for now we'll keep them among us: let's make things a little
harder for the protectionists who read this... Besides: the old
approach works here flawlessly). After getting the Registration
window on screen the Winice standard procedure is:
:task ; how
:heap IABROWSE ; where & what
:hwnd IABROWSE ; get the Winhandle
:bpx [winhandle] WM_GETTEXT ; pinpoint code
:bpx GetProcAddress ; in case of funny routines
:dex 0 ds:dx ; let's see their name
:gdt ; sniff the selectors
:s 30:0 lffffffff "Your_input_string" ; search in 4 giga data
:bpr [all memory ranges for your string that are above 80000000]
and so on. (continued in lesson C.2)
Well, that's it for this lesson, reader. Not all lessons of my
tutorial are on the Web.
You 'll obtain the missing lessons IF AND ONLY IF you mail
me back (via anon.penet.fi) with some tricks of the trade I may
not know that YOU discovered. Mostly I'll actually know them
already, but if they are really new you'll be given full credit,
and even if they are not, should I judge that you rediscovered them
with your work, or that you actually did good work on them,
I'll send you the remaining lessons nevertheless. Your
suggestions and critics on the whole crap I wrote are also
welcomed.
+ORC an526164@anon.penet.fi
HOW TO CRACK, by +ORC, A TUTORIAL
LESSON C (2) - How to crack, Cracking as an art
[INSTANT ACCESS]
cracking Instant Access (2) - strainer for the +HCU
[SEE LESSON C.1 for the first part of this cracking session]
Here follow the relevant protection routines for the first
(The "Registration") number_code of Instant Access, with my
comments: you have to investigate a little the following code.
Later, when you'll crack on your own, try to recognize the
many routines that fiddle with input BEFORE the relevant (real
protection) one. In this case, for instance, a routine checks the
correctness of the numbers of your input:
This_loop_checks_that_numbers_are_numbers:
1B0F:2B00 C45E06 LES BX,[BP+06] ; set/reset pointer
1B0F:2B03 03DF ADD BX,DI
1B0F:2B05 268A07 MOV AL,ES:[BX] ; get number
1B0F:2B08 8846FD MOV [BP-03],AL ; store
1B0F:2B0B 807EFD30 CMP BYTE PTR [BP-03],30
1B0F:2B0F 7C06 JL 2B17 ; less than zero?
1B0F:2B11 807EFD39 CMP BYTE PTR [BP-03],39
1B0F:2B15 7E05 JLE 2B1C ; between 0 & 9?
1B0F:2B17 B80100 MOV AX,0001 ; no, set flag=1
1B0F:2B1A EB02 JMP 2B1E ; keep flag
1B0F:2B1C 33C0 XOR AX,AX ; flag=0
1B0F:2B1E 0BC0 OR AX,AX ; is it zero?
1B0F:2B20 7507 JNZ 2B29 ; flag NO jumps away
1B0F:2B22 8A46FD MOV AL,[BP-03] ; Ok, get number
1B0F:2B25 8842CC MOV [BP+SI-34],AL ; Ok, store number
1B0F:2B28 46 INC SI ; inc storespace
1B0F:2B29 47 INC DI ; inc counter
1B0F:2B2A C45E06 LES BX,[BP+06] ; reset pointer
1B0F:2B2D 03DF ADD BX,DI ; point next number
1B0F:2B2F 26803F00 CMP BYTE PTR ES:[BX],00 ; input end?
1B0F:2B33 75CB JNZ 2B00 ; no:loop next num
You now obviously understand that the "real" string is
stored inside memory location [BP+SI-34]... set a memory
breakpoint on this area to get the next block of code that
fiddles with the transformed input. Notice how this routine
"normalizes" the input, strips the "-" off and puts the 10
numbers together:
user input: 1 2 1 2 1 2 1 2 1 2 End
1E7F:92E2 31 32 31 32 31 32 31 32 31 32 00 45 AF 1F 70 9B
Stack ptr: 0 1 2 3 4 5 6 7 8 9 A B C D E F
Let's now look at the "real" protection routine: the one
that checks these numbers and throw you out if they are not
"sound". Please pay attention to the following block of code:
check_if_sum_other_9_numbers_=_remainder_of_the_third_number:
:4B79 8CD0 MOV AX,SS ; we'll work inside the stack...
:4B7B 90 NOP
:4B7C 45 INC BP
:4B7D 55 PUSH BP ; save real BP
:4B7E 8BEC MOV BP,SP ; BP = stackpointer
:4B80 1E PUSH DS ; save real Datasegment
:4B81 8ED8 MOV DS,AX ; Datasegment = stacksegment
:4B83 83EC04 SUB SP,+04
:4B86 C45E06 LES BX,[BP+06] ; BX points input_start
:4B89 268A07 MOV AL,ES:[BX] ; load first number
:4B8C 98 CBW ; care only for low
:4B8D C45E06 LES BX,[BP+06] ; reset pointer
:4B90 50 PUSH AX ; save 1st number
:4B91 268A4701 MOV AL,ES:[BX+01] ; load 2nd number
:4B95 98 CBW ; only low
:4B96 8BD0 MOV DX,AX ; 2nd number in DX
:4B98 58 POP AX ; get 1st number
:4B99 03C2 ADD AX,DX ; sum with second
:4B9B C45E06 LES BX,[BP+06] ; reset pointer
:4B9E 50 PUSH AX ; save sum
:4B9F 268A4707 MOV AL,ES:[BX+07] ; load 8th number
:4BA3 98 CBW ; only low
:4BA4 8BD0 MOV DX,AX ; 8th number in DX
:4BA6 58 POP AX ; old sum is back
:4BA7 03C2 ADD AX,DX ; sum 1+2+8
:4BA9 C45E06 LES BX,[BP+06] ; reset pointer
:4BAC 50 PUSH AX ; save sum
:4BAD 268A4703 MOV AL,ES:[BX+03] ; load 4rd number
:4BB1 98 CBW ; only low
:4BB2 8BD0 MOV DX,AX ; #4 in DX
:4BB4 58 POP AX ; sum is back
:4BB5 03C2 ADD AX,DX ; sum 1+2+8+4
:4BB7 C45E06 LES BX,[BP+06] ; reset pointer
:4BBA 50 PUSH AX ; save sum
:4BBB 268A4704 MOV AL,ES:[BX+04] ; load 5th number
:4BBF 98 CBW ; only low
:4BC0 8BD0 MOV DX,AX ; #5 in DX
:4BC2 58 POP AX ; sum is back
:4BC3 03C2 ADD AX,DX ; 1+2+8+4+5
:4BC5 C45E06 LES BX,[BP+06] ; reset pointer
:4BC8 50 PUSH AX ; save sum
:4BC9 268A4705 MOV AL,ES:[BX+05] ; load 6th number
:4BCD 98 CBW ; only low
:4BCE 8BD0 MOV DX,AX ; #6 in DX
:4BD0 58 POP AX ; sum is back
:4BD1 03C2 ADD AX,DX ; 1+2+8+4+5+6
:4BD3 C45E06 LES BX,[BP+06] ; reset pointer
:4BD6 50 PUSH AX ; save sum
:4BD7 268A4706 MOV AL,ES:[BX+06] ; load 7th number
:4BDB 98 CBW ; only low
:4BDC 8BD0 MOV DX,AX ; #7 in DX
:4BDE 58 POP AX ; sum is back
:4BDF 03C2 ADD AX,DX ; 1+2+8+4+5+6+7
:4BE1 C45E06 LES BX,[BP+06] ; reset pointer
:4BE4 50 PUSH AX ; save sum
:4BE5 268A4708 MOV AL,ES:[BX+08] ; load 9th number
:4BE9 98 CBW ; only low
:4BEA 8BD0 MOV DX,AX ; #9 in DX
:4BEC 58 POP AX ; sum is back
:4BED 03C2 ADD AX,DX ; 1+2+8+4+5+6+7+9
:4BEF C45E06 LES BX,[BP+06] ; reset pointer
:4BF2 50 PUSH AX ; save sum
:4BF3 268A4709 MOV AL,ES:[BX+09] ; load 10th #
:4BF7 98 CBW ; only low
:4BF8 8BD0 MOV DX,AX ; #10 in DX
:4BFA 58 POP AX ; sum is back
:4BFB 03C2 ADD AX,DX ; 1+2+8+4+5+6+7+9+10
:4BFD 0550FE ADD AX,FE50 ; clean sum to 0-51
:4C00 BB0A00 MOV BX,000A ; BX holds 10
:4C03 99 CWD ; only AL
:4C04 F7FB IDIV BX ; remainder in DX
:4C06 C45E06 LES BX,[BP+06] ; reset pointer
:4C09 268A4702 MOV AL,ES:[BX+02] ; load now # 3
:4C0D 98 CBW ; only low
:4C0E 05D0FF ADD AX,FFD0 ; clean # 3 to 0-9
:4C11 3BD0 CMP DX,AX ; remainder = pampered #3?
:4C13 7407 JZ 4C1C ; yes, go on good guy
:4C15 33D2 XOR DX,DX ; no! beggar off! Zero DX
:4C17 33C0 XOR AX,AX ; and FLAG_AX = FALSE
:4C19 E91701 JMP 4D33 ; go to EXIT
let's_go_on_if_first_check_passed:
:4C1C C45E06 LES BX,[BP+06] ; reset pointer
:4C1F 268A4701 MOV AL,ES:[BX+01] ; now load #2 anew
:4C23 98 CBW ; only low
:4C24 05D7FF ADD AX,FFD7 ; pamper adding +3
:4C27 A38D5E MOV [5E8D],AX ; save SEC_+3
:4C2A 3D0900 CMP AX,0009 ; was it < 9? (no A-F)
:4C2D 7E05 JLE 4C34 ; ok, no 0xletter
:4C2F 832E8D5E0A SUB WORD PTR [5E8D],+0A ; 0-5 if A-F
:4C34 C45E06 LES BX,[BP+06] ; reset pointer
:4C37 268A07 MOV AL,ES:[BX] ; load 1st input number
:4C3A 98 CBW ; only low
:4C3B 05C9FF ADD AX,FFC9 ; pamper adding +7
:4C3E A38F5E MOV [5E8F],AX ; save it in FIR_+7
:4C41 0BC0 OR AX,AX ; if #1 > 7
:4C43 7D05 JGE 4C4A ; no need to add 0xA
:4C45 83068F5E0A ADD WORD PTR [5E8F],+0A ; FIR_+7 + 0xA
now_we_have_the_sliders_let's_prepare_for_loop:
:4C4A C45E0E LES BX,[BP+0E] ; Set pointer to E
:4C4D 26C747020000 MOV WORD PTR ES:[BX+02],0000 ; 0 flag
:4C53 26C7070000 MOV WORD PTR ES:[BX],0000 ; 0 flag
:4C58 C706975E0900 MOV WORD PTR [5E97],0009 ; counter=9
:4C5E E99500 JMP 4CF6 ; Jmp check_counter
loop_8_times:
:4C61 C45E06 LES BX,[BP+06] ; reset pointer
:4C64 031E975E ADD BX,[5E97] ; add running counter
:4C68 268A07 MOV AL,ES:[BX] ; load # counter+1
:4C6B 98 CBW ; only low
:4C6C 50 PUSH AX ; save 10th number
:4C6D A18D5E MOV AX,[5E8D] ; ld SEC_+3 down_slider
:4C70 BA0A00 MOV DX,000A ; BX holds 0xA
:4C73 F7EA IMUL DX ; SEC_+3 * 0xA
:4C75 03068F5E ADD AX,[5E8F] ; plus FIR_+7 up_slider
:4C79 BAA71E MOV DX,1EA7 ; fixed segment
:4C7C 8BD8 MOV BX,AX ; BX = Lkup_val=(SEC_+3*10+FIR_+7)
:4C7E 8EC2 MOV ES,DX ; ES = 1EA7
:4C80 268A870000 MOV AL,ES:[BX+0000] ; ld 1EA7:[Lkup_val]
:4C85 98 CBW ; only low: KEY_PAR
:4C86 8BD0 MOV DX,AX ; save KEY_PAR in DX
:4C88 58 POP AX ; repops 10th number
:4C89 03C2 ADD AX,DX ; RE_SULT=KEY_PAR+#10
:4C8B 05D0FF ADD AX,FFD0 ; polish RE_SULT
:4C8E 99 CWD ; only low: RE_SULT
:4C8F 8956FC MOV [BP-04],DX ; save here KEY_PAR [9548]
:4C92 8946FA MOV [BP-06],AX ; save here RE_SULT [9546]
:4C95 0BD2 OR DX,DX ; KEY_PAR < 0?
:4C97 7C0F JL 4CA8 ; yes: KEY_PAR < 0
:4C99 7F05 JG 4CA0 ; no: KEY_PAR > 0
:4C9B 3D0900 CMP AX,0009 ; KEY_PAR = 0
:4C9E 7608 JBE 4CA8 ; no pampering if RE_SULT < 9
:4CA0 836EFA0A SUB WORD PTR [BP-06],+0A ; else pamper
:4CA4 835EFC00 SBB WORD PTR [BP-04],+00 ; and SBB [9548]
:4CA8 C45E0E LES BX,[BP+0E] ; reset pointer to E
:4CAB 268B4F02 MOV CX,ES:[BX+02] ; charge CX [958C]
:4CAF 268B1F MOV BX,ES:[BX] ; charge BX slider [958A]
:4CB2 33D2 XOR DX,DX ; clear DX to zero
:4CB4 B80A00 MOV AX,000A ; 10 in AX
:4CB7 9A930D2720 CALL 2027:0D93 ; call following RO_routine
This is the only routine called from our protection, inside the
loop (therefore 8 times), disassembly from WCB. Examining this
code please remember that we entered here with following
configuration: DX=0, AX=0xA, CX=[958C] and BX=[958A]...
1.0D93 56 push si ; save si
1.0D94 96 xchg ax, si ; ax=si, si=0xA
1.0D95 92 xchg ax, dx ; dx=0xA ax=dx
1.0D96 85C0 test ax, ax ; TEST this zero
1.0D98 7402 je 0D9C ; zero only 1st time
1.0D9A F7E3 mul bx ; BX slider! 0/9/5E/3B2...
1.0D9C >E305 jcxz 0DA3 ; cx=0? don't multiply!
1.0D9E 91 xchg ax, cx ; cx !=0? cx = ax & ax = cx
1.0D9F F7E6 mul si ; ax*0xA in ax
1.0DA1 03C1 add ax, cx ; ax= ax*0xA+cx = M_ULT
1.0DA3 >96 xchg ax, si ; ax=0xA; si evtl. holds M_ULT
1.0DA4 F7E3 mul bx ; ax= bx*0xA
1.0DA6 03D6 add dx, si ; dx= dx_add
1.0DA8 5E pop si ; restore si
1.0DA9 CB retf ; back to caller with two
parameters: DX and AX
Back_to_main_protection_loop_from_RO_routine:
:4CBC C45E0E LES BX,[BP+0E] ; reset pointer
:4CBF 26895702 MOV ES:[BX+02],DX ; save R_DX par [958C]
:4CC3 268907 MOV ES:[BX],AX ; save R_AX par [958A]
:4CC6 0346FA ADD AX,[BP-06] ; add to AX RE_SULT [9546]
:4CC9 1356FC ADC DX,[BP-04] ; add to DX KEY_PAR [9548]
:4CCC C45E0E LES BX,[BP+0E] ; reset pointer
:4CCF 26895702 MOV ES:[BX+02],DX ; save R_DX+KEY_PAR [958C]
:4CD3 268907 MOV ES:[BX],AX ; save R_AX+RE_SULT [958A]
:4CD6 FF0E8D5E DEC WORD PTR [5E8D] ; down_slide SEC_+3
:4CDA 7D05 JGE 4CE1 ; no need to add
:4CDC 83068D5E0A ADD WORD PTR [5E8D],+0A ; pamper adding 10
:4CE1 FF068F5E INC WORD PTR [5E8F] ; up_slide FIR_+7
:4CE5 A18F5E MOV AX,[5E8F] ; save upslided FIR_+7 in AX
:4CE8 3D0900 CMP AX,0009 ; is it over 9?
:4CEB 7E05 JLE 4CF2 ; no, go on
:4CED 832E8F5E0A SUB WORD PTR [5E8F],+0A ; yes, pamper -10
:4CF2 FF0E975E DEC WORD PTR [5E97] ; decrease loop counter
check_loop_counter:
:4CF6 833E975E03 CMP WORD PTR [5E97],+03 ; counter = 3?
:4CFB 7C03 JL 4D00 ; finish if counter under 3
:4CFD E961FF JMP 4C61 ; not yet, loop_next_count
loop_is_ended:
:4D00 C45E06 LES BX,[BP+06] ; reset pointer to input
:4D03 268A4701 MOV AL,ES:[BX+01] ; load 2nd number (2)
:4D07 98 CBW ; only low
:4D08 05D0FF ADD AX,FFD0 ; clean it
:4D0B BA0A00 MOV DX,000A ; DX = 10
:4D0E F7EA IMUL DX ; AX = SEC_*10 = 14
:4D10 C45E06 LES BX,[BP+06] ; reset pointer
:4D13 50 PUSH AX ; save SEC_*10
:4D14 268A07 MOV AL,ES:[BX] ; load 1st number (1)
:4D17 98 CBW ; only low
:4D18 8BD0 MOV DX,AX ; save in DX
:4D1A 58 POP AX ; get SEC_*10
:4D1B 03C2 ADD AX,DX ; sum SEC_*10+1st number
:4D1D 05D0FF ADD AX,FFD0 ; clean it
:4D20 99 CWD ; only low
:4D21 C45E0A LES BX,[BP+0A] ; get pointer to [9582]
:4D24 26895702 MOV ES:[BX+02],DX ; save 1st (1) in [9584]
:4D28 268907 MOV ES:[BX],AX ; save FINAL_SUM (15) [9582]
:4D2B 33D2 XOR DX,DX ; DX = 0
:4D2D B80100 MOV AX,0001 ; FLAG TRUE !
:4D30 E9E6FE JMP 4C19 ; OK, you_are_a_nice_guy
EXIT:
:4D33 59 POP CX ; pop everything and
:4D34 59 POP CX ; return with flag
:4D35 1F POP DS ; AX=TRUE if RegNum OK
:4D36 5D POP BP ; with 1st # in [9584]
:4D37 4D DEC BP ; with FINAL_SUM in [9582]
:4D38 CB RETF
Let's translate the preceding code: first of all the pointers:
At line :4B86 we have the first of a long list of stack ptrs:
LES BX,[BP+06]
This stack pointer points to the beginning of the input string,
which, once polished from the "-", has now a length of 10 bytes,
concluded by a 00 fence. At the beginning, before the main loop,
9 out of our 10 numbers are added, all but the third one.
Notice that protection has jumped # 3 (and added # 8 out of the
line). The rest is straightforward. Now, at line :4BFD we have
our first "cleaning" instruction. You see: the numbers are
hexadecimal represented by the codes 0x30 to 0x39. If you add
FE50 to the minimum sum you can get adding 9 numbers (0x30*9 =
0x160) You get 0. The maximum you could have adding 9 numbers,
on the contrary is (0x39*9=0x201), which, added to FE50 gives
0x51. So we'll have a "magic" number between 0x0 and 0x51 instead
of a number between 0x160 and 0x201. Protection pampers this
result, and retains only the last ciffer: 0-9. Then protection
divides this number through 0xA, and what happens? DX get's the
REMAINDER of it.
If we sum the hexcodes of our (1212-1212-12) we get 0x1BE (we
sum only 9 out of then numbers: the third "1" -i.e. "31"- does
not comes into our count); 0x1BE, cleaned and pampered gives E.
Therefore (0xE/0xA = 1) We get 1 with a remainder of 4.
You may observe that of all possible answers, only sums
finishing with A, B, C, D, E or F give 1 (and rem=0,1,2,3,4 or
5). Sums finishing 0 1 2 3 4 5 6 7 8 or 9 give 0 as result and
themselves as reminder. The chance of getting a 0,1,2,3 or 4 are
therefore bigger as the chance of getting a 5, 6, 7, 8 or 9. We
are just observing... we do not know yet if this should play a
role or not.
Now this remainder is compared at :4C11 with the third number
polished from 0x30-0x39 to 0-9. This is the only protection check
for the registration number input: If your third number does not
match with the remainder of the sum of all the 9 others numbers
of your input you are immediately thrown out with FLAG AX=FALSE
(i.e. zero).
To crack the protection you now have to MODIFY your input string
accordingly. Our new input string will from now on be "1242-1212-
12": we have changed our third number (originally a "2") to a "4"
to get through this first strainer in the correct way. Only now
protection starts its mathematical part (We do not know yet why
it does it... in order to seed the random product number? To
provide a check for the registration number you'll input at the
end? We'll see).
- Protection saves the second number of your input (cleaned
with FFD7) in SEC_+3 [5E8D], pampering it if it is bigger
than 9 (i.e. if it is 0xA-0xF). Here you'll have therefore
following correspondence: 0=7 1=8 2=9 3=0 4=1 5=2 6=3 7=4
8=5 9=6. The second number of your input has got added +3.
This is value SEC_+3. In (lengthy) C it would look like
this:
If (RegString(2)is lower than 7) RegString(2) = RegString(2)+3
Else Regstring(2) = ((RegString(2)-10)+3)
- Protection saves your first number in FIR_+7 [5E8F] with a
different cleaning parameter (FFC9). The next pampering
adds 0xA if it was not 7/8/9 therefore you have here
following correspondence 7=0 8=1 9=2 0=3 1=4 2=5 3=6 4=7
5=8 6=9). This is value FIR_+7. In (lengthy) C it would
look like this:
If (RegString(1) is lower than 3) RegString(1) = RegString(1)+7
Else Regstring(1) = ((RegString(1)-10)+7)
So protection has "transformed" and stored in [5E8D] and [5E8F]
the two numbers 1 and 2. In our RegString: 1242-1212-12 the first
two numbers "12" are now stored as "94". These will be used as
"slider" parameters inside the main loop, as you will see.
Only now does protection begin its main loop, starting from the
LAST number, because the counter has been set to 9 (i.e. the
tenth number of RegString). The loop, as you'll see, handles only
the numbers from 10 to 3: it's an 8-times loop that ends without
handling the first and second number. What happens in this
loop?... Well, quite a lot: Protection begins the loop loading
the number (counter+1) from the RegString. Protection then loads
the SEC_+3 down_slider parameter (which began its life as second
number "transformed"), multiplies it with 0xA and then adds the
up_slider parameter FIR_+7 (at the beginning it was the first
number transformed).
This sum is used as "lookup pointer" to find a parameter
inside a table of parameters in memory, which are all numbers
between 0 and 9. Let's call this value Lkup_val.
Protection looks for data in 1EA7:[Lkup_val]. In our case (we
entered 1242-1212-12, therefore the first SEC_+3 value is 9 and
the first FIR_+7 value is 4): [Lkup_val] = 9*0xA+4; 0x5A+4 =
0x5E. At line :4C80 therefore AL would load the byte at 1EA7:005E
(let's call it KEY_PAR), which now would be ADDED to the #
counter+1 of this loop. In our case KEY_PAR at 1EA7:005E it's a
"7" and is added to the pampered 0x32=2, giving 9.
Let's establish first of all which KEY_PAR can possibly get
fetched: the maximum is 0x63 and the minimum is 0x0. The possible
KEY_PARs do therefore dwell in memory between 1EA7: and
1EA7:0063. Let's have a look at the relative table in memory,
where these KEY_PARs are stored ("our" first 0x5Eth byte is
underlined):
1EA7:0000 01 03 03 01 09 02 03 00-09 00 04 03 08 07 04 04
1EA7:0010 05 02 09 00 02 04 01 05-06 06 03 02 00 08 05 06
1EA7:0020 08 09 05 00 04 06 07 07-02 00 08 00 06 02 04 07
1EA7:0030 04 04 09 05 09 06 00 06-08 07 00 03 05 09 00 08
1EA7:0040 03 07 07 06 08 09 01 05-07 04 06 01 04 02 07 01
1EA7:0050 03 01 08 01 05 03 03 01-02 08 02 01 06 05 07 02
1EA7:0060 05 09 09 08 02 09 03 00-00 04 05 01 01 03 08 06
1EA7:0070 01 01 09 00 02 05 05 05-01 07 01 05 08 07 01 09
1EA7:0080 08 07 07 04 04 08 03 00-06 01 09 08 08 04 09 09
1EA7:0090 00 07 05 02 03 01 03 08-06 05 07 06 03 07 06 07
1EA7:00A0 04 02 02 05 02 04 06 02-06 09 09 01 05 02 03 04
1EA7:00B0 04 00 03 05 00 03 08 07-06 04 08 08 02 00 03 06
1EA7:00C0 09 00 00 06 09 04 07 02-00 01 01 01 01 00 01 FF
1EA7:00D0 00 FF FF FF FF 00 FF 01-00 00 00 00 00 00 00 00
An interesting table, where all the correspondences are
between 0 and 9... are we getting some "secret" number here? But,
hey, look there... funny, isn't it? Instead of only 0-0x63 bytes
we have roughly the DOUBLE here: 0-0xC8 bytes (the 01 sequence
starting at CA "feels" like a fence). We'll see later how
important this is. At the moment you should only "perceive" that
something must be going on with a table that's two time what she
should be.
As I said the result of KEY_PAR + input number is polished
(with a FFDO) and pampered (subtracting, if necessary, 0xA).
Therefore the result will be the (counter+1) input number +
KEY_PAR (let's call it RE_SULT], in our case, (at the beginning
of the loop) a 9. Now (DX=0 because of the CWD instruction) DX
will be saved in [9548] and RE_SULT in [9546].
Now Protection prepares for the RO_routine: resets its pointer
and charges CX and BX from [958C] and from [958A] respectively,
charges AX with 0xA and sets DX to zero.
The routine performs various operations on AX and DX and saves
the results in the above mentioned locations [958A] and [958C].
Now KEY_PAR and RE_SULT are added respectively to the DX and AX
value we got back from the RO_routine call, and saved once more
in the last two locations: AX+RE_SULT in [958A] and DX+KEY_PAR
in [958C]
Now the value in SEC_+3 is diminished by 1 (if it was 9 it's now
8, if it was zero it will be pampered to 9). It's a "slider"
parameter (in this case a down_slider), typically used in
relatively complicated protections to give a "random" impression
to the casual observer. The value in FIR_+7, on the contrary, is
augmented by one, from 4 to 5... up_sliding also.
Protection now handles the next number of your input for the
loop. In our case this loop uses following protection
configuration with our "sliding" parameters:
Input # pamp_2nd pamp_1st Lookup value KEY_PAR # RE_SULT
# 10 = 2, SEC_+3= 9, FIR_+7= 4, Lkup_val = 0x5E, KEY=7 +2 = 9
# 9 = 1, SEC_+3= 8, FIR_+7= 5, Lkup_val = 0x55, KEY=3 +1 = 4
# 8 = 2, SEC_+3= 7, FIR_+7= 6, Lkup_val = 0x4C, KEY=4 +2 = 6
# 7 = 1, SEC_+3= 6, FIR_+7= 7, Lkup_val = 0x43, KEY=7 +1 = 7
# 6 = 2, SEC_+3= 5, FIR_+7= 8, Lkup_val = 0x3A, KEY=0 +2 = 2
# 5 = 1, SEC_+3= 4, FIR_+7= 9, Lkup_val = 0x31, KEY=4 +1 = 5
# 4 = 2, SEC_+3= 3, FIR_+7= 0, Lkup_val = 0x1E, KEY=5 +2 = 7
# 3 = 4, SEC_+3= 2, FIR_+7= 1, Lkup_val = 0x15, KEY=2 +4 = 5
Notice how our "regular" input 21212124 has given an "irregular"
94672575.
You may legitimately ask yourself what should all this mean:
what are these RE_SULTs used for? Well they are used to slide
another parameter: this one inside the called routine... this is
what happens to AX and DX inside the routine, and the lines after
the called routine:
:4CBF 26895702 MOV ES:[BX+02],DX ; save R_DX par [958C]
:4CC3 268907 MOV ES:[BX],AX ; save R_AX par [958A]
:4CC6 0346FA ADD AX,[BP-06] ; add to AX RE_SULT [9546]
:4CC9 1356FC ADC DX,[BP-04] ; add to DX KEY_PAR [9548]
:4CCC C45E0E LES BX,[BP+0E] ; reset pointer to E
:4CCF 26895702 MOV ES:[BX+02],DX ; save R_DX+KEY_PAR [958C]
:4CD3 268907 MOV ES:[BX],AX ; save R_AX+RE_SULT [958A]
:4CC6 :4CC9 :4CCF Odd_DX :4CD3 slider_sum
RE_SULT [958A] [958C] [958C] [958A]
0 0 0 0 0
9 5A 0 0 9
4 3AC 0 0 5E
6 24F4 0 0 3B2
7 71CE 1 1 24FB
2 7220 4 E 71D0
5 7572 4 90 7225
7579
Now the loops ends, having handled the input numbers from tenth
to third. Protection loads the second number and multiplies it
by 10 (let's call this result SEC_*10), in our case 2*0xA=14.
Protection loads the first number and adds it to the
multiplication, in our case 1+0x14=0x15 (FINAL_SUM].
Now everything will be added to FFDO to "clean" it.
Pointer will now be set to the end of the input number.
DX, zeroed by CDW, will be saved as parameter in [9584] and the
cleaned and pampered sum will be saved in [9582].
FLAG is set to true and this routine is finished! No parameter
are passed and the only interesting thing is what actually
happens in the locations [9582], [9584], [958A] and [958C], i.e.:
FINAL_SUM, 0, slider_sum, odd_dx.
In the next lesson we'll crack everything, but I'll give you
already some hints here, in case you would like to go ahead on
your own: we'll see how the scheme used for the third (the
registration) number show analogies and differences with the
scheme we have studied (and cracked) here for the first number.
Our 3434-3434-3434-3434-34 input string for the registration
number will be transformed in the magic string
141593384841547431, but this will not work because the "magic"
12th number: "1" will not correspond to the remainder calculated
inside this check through the previous locations of the other
checks.
Here the things are more complicated because every little
change in your input string transforms COMPLETELY the "magic"
string... therefore in order to pass the strainer you'll have to
change 3434-3434-3434-3434-34 in (for instance) 7434-3434-3434-
3434-96. The "magic" string 219702960974498056 that this
registration input gives will go through the protection strainer.
Only then we'll be able to step over and finally crack the whole
protection... it's a pretty complicated one as I said. Now crack
it pupils... you have three months time. From this crack depends
your admission to the Uni, there will be no other admission text
till summer 1997 (it's a hell of work to prepare this crap)...
work well.
Well, that's it for this lesson, reader. Not all lessons of my
tutorial are on the Web.
You 'll obtain the missing lessons IF AND ONLY IF you mail
me back (via anon.penet.fi) some tricks of the trade I may not
know but YOU've discovered. I'll probably know most of them
already, but if they are really new you'll be given full credit,
and even if they are not, should I judge that you "rediscovered"
them with your work, or that you actually did good work on them,
I'll send you the remaining lessons nevertheless. Your
suggestions and critics on the whole crap I wrote are also
welcomed.
+ORC an526164@anon.penet.fi
--------------------------------------------------------
LESSON C (3) - How to crack Windows, cracking as anArt:
Web trends, Instant Access (end) and the proximity trick
--------------------------------------------------------
I. [WEB TRENDS]
It's really amazing: I began this tutorial in february 1996...
the year is not yet finished but many things have already
changed, and how! First of all the Web proves to be even more
significant that I would ever have thought: it's -de facto- an
ANTI-ESTABLISHMENT and ANTI-CONSUME "permanent" tool... more than
that: it's an EVOLVING and SHARP tool!
I do not know if you will agree with me, but it seems to me that
it is now NOT ANY MORE NECESSARY to buy any of the following
things (and this is a quite incomplete list:
1) Newspapers Are almost all on the Web for free
2) Magazines Are almost all on the Web for free
3) Software All on the Web for free
4) Books on the Web (even the *images* inside the
books you would have bought are somewhere)
5) Post_stamps e-mail is free and ubiquitous
6) Hard_disks free megabytes everywhere on the Web (for
pages and/or software, quite a lot of
offering (at the moment, middle november
1996 you can get -for free- about 80
megabytes for each email address you have).
Should you do not deem this space enough,
you may multiply them -ad libitum- using one
of the many free "alias e-mail addresses"
generators or, as a brilliant scholar of
mine (re)discovered, you may easily "caper"
(the passwords of) pages established by the
lusers.
I assure you that I used to spend quite a lot of money
buying all the preceding items. The case of the CD-ROM magazines
(and of software in general) seems to me to be the most relevant.
In my earlier lessons I advised you to buy CD-ROM software at
"kilo" prices in second hand shops (obviously never when the
magazines themselves do appear)... even this trick is now
useless: there is NO software that you cannot find on the Web
(I'll bet that the CD-ROM mags are not making any easy money any
more now). This truth may be difficult to slurp until you learn
HOW to search effectively on the Web, a very difficult art -per
se-, that all real crackers should master. A little tip: if you
are in a hurry do your searches using your own search engines
instead of using only the ubiquitous AltaVista, FTP and
Webcrawler (which you SHOULD use, of course, but not alone).
And loo! Good old Internet, made by governments and
universities with public money and no private partecipation
whatsoever reveals itself to be the most striking revolution of
the last years in spite of the continuous blah blah about free
markets and private initiative... quite funny, isn't it?
New promising patterns are emerging: it is clear (to me)
that the future of cracking lays in JavaScript applets: Netscape
has opened quite a Pandora Box... now everybody is scrambling to
close it... TOO LATE! Find the relevant documentation, tutorials,
software, tools, examples... even teachers! Everything is there
for free on the Web! Study it, and when you are ready study it
again... take it easy: we have a long cracking future before
us...
Here is a nice example (by LaDue) of an interesting applet:
this one forges an e-mail from the browsers of your page sending
it to the address contained in the string "toMe". The browser of
your page initiates the mail by connecting (involuntarily) to
port 25 (int mailPort=25).
Now, let's just academically imagine... you would put your
own address (faked email address but working depot when you need
to fetch the messages, as usual) in the string "toMe"... well,
scanning incoming mail you could get the full e-mail address,
including the user name, of many people who have seen your page.
See LaDue example at:
http://www.math.gatech.edu/~mladue/PenPal.java
JavaScript and Java are two completely different things: The
coincidence in the name is only a stupid marketing move from
Netscape. JavaScript is so easy it's a shame, and this makes it
the ideal weapon for masscracking (Java on the countrary is a
"real" programming language). The implications of easy Javascript
use, of internet growth and of the "on the fly" JavaScript
workings for the drafting of much more interesting applets are
obvious, I'll let them to the intuition (and fantasy) of my
cleverest readers.
II.[INSTANT ACCESS] End
I'll admit it: I am a little deceived: the +HCU strainer
(Instant access protection scheme) has been solved only by very
few (pretty competent) crackers. The amount of people that has
NOT cracked instant access (and that as relentlessy asked for
more "clues") stood in no sound relation to the very few that
solved it. I intended to give my complete solution in this lesson
C3 to allow everybody to have (good) software for free... but too
few worked hard on it to let you all lazy ones deserve a "ready-
made" solution... I will therefore publish here one of the
"incomplete" (albeit very good) solutions.
The solution cracks the scheme but requires a little work
of your part to accomplish it... what I mean is this: studying
the following you'll be able to crack every Instant access
protected code in a couple of hours, not immediatly... this is
good, will make the lazy lurkers work (at least a little :=)
Here it is (C++ code of the solution and admission letter),
I only took off the name of the candidate:
Cracking Instant Access_____________
by xxxxxxxx
Application for 1996 Higher Cracking University
This is my solution to the strainer for admittance into your
HCU. While I was successful in bypassing the protection (and
hence now have a nice collection of software for free) I am the
first to admit that my solution is not the best. However, I am
very proud of the work I have done on this project and it is by
far the most difficult crack I've ever done. In the past I've
traced programs, and when they did something I did not like, I
looked at the jumps immediately before that and reversed them.
Because of idiot programming this worked about 60% of the
time, however in many programs I was just stuck. With the hints
you provided in your tutors I was able to actually disassemble
the program and understand why it did things. Believe me this is
a big jump. Anyway, here is my solution.
I have dozens of handwritten notes and pages of code that
I copied out of soft-ice, and any that are important I will type
into this report, however most turned out to be unimportant. I
have also created a "Magic" number generator and a reverse
generator. I am very proud that I was able to create these,
because the "Magic" number seemed so mysterious at first, and now
I have complete mastery of it, a great feeling of power.
I began the project by following the introductory steps in
lessons C1 and C2. I got lost somewhere in C2, but I kept going.
I got to the end with a vague understanding of what was happening
and decided that I needed to understand this fully before I could
do anything useful towards cracking it.
I left my computer alone and read through the code again,
making notes and explanations for my own use. About the third
time through everything clicked, it was like a light bulb going
off in my head. You mentioned that not everything in Lesson C1
was correct.
Here is a list of what I found to be incorrect.
1. The offsets in the code were not the same. (this is a good
idea to keep people from cheating when pinpointing the correct
code)
2. The pointers to where things are saved in memory were not the
same.
3. You wrote that the 1st digit plus 7 was saved and then you
wrote that the 2nd plus 3 was saved. It is the other way around!
1st plus 3 and 2nd plus 7. (just checking if we are paying
attention huh?)
I think that's all of the one's I found although there were
many specific instances of each one. So here's what I did.
I did a search on the 18 digit code I typed in. I found it
at 30:8xxxxx and did a bpr on it. I let it run, and looked each
time something accessed that area. Eventually I found code that
checked if the digits were between 30 and 39 and copied them to
DS:8CD8. So there lies the code with the "-" 's stripped off of
it. I did a bpr on this area. It copied itself to DS:8CB8, and
I bpr'ed that as well. I discovered that what was going on was,
it copied itself, then that copy was transformed into the "Magic"
number.
So I did a little stack fishing, and found a CALL at offset
5C04 which copies the code from 8CB8 and converts it into the
"Magic" number. At this point I traced into the call and got
really fucking lost, so I stepped back had a sip of Vodka and
thought. I don't care HOW the "magic" gets there, only that it
is there. I figured once I figured out what "magic" I needed I
could trace over the call that put it there, and then put in
whatever "magic" I wanted. So I traced on to see what happened
to the "magic" number that had been produced. I had a bpr on the
"magic" and it stopped on the first line of code below.
The code is copied from my handwritten notes, so not
everything is accurate (I only wrote down what I thought was
important)
2b67:2598 mov al, es:[bx] ; 12th digit of magic
mov [bp-03], al ; ??????
mov al, [bp-03] ; maybe an inefficient compiler
result
add al, d0 ; clean it
mov [bp-04], al ; save it in [8ca6]
les bx, bp+06
add bx, si ; point to 12th again
mov byte ptr es:bx, 30 ; make it a '0'
push then more crap and then
:253d mov al, es:bx ; 1st digit
mov ah, 00 add ax,ffd0 ; clean it
cwd
add [bp-06], ax ; [8c90] is zero to start
this loop repeats 18 times, summing up the "magic" number, with
the 12th set to 0
:256e mov [bp-07], al ; save remainder of sum/a in [8c8f]
cmp [al, bp-05] ; is 12th (in al) save as remainder
of sum/a ? Aha!, this is what you were talking about at the end
of C2, where the remainder doesn't match the 12th number.
I knew I was on the right track. I could feel it.
I traced down farther after the remainder check (I used
8888-8888-8888-8888-88 as my code from then on because it passed
this check and was easy to remember) and I found code which
compared the value at ds:8D00 with the value at ds:8D0C and if
it did not match jumped to beggar off.
Then it checks if ds:8D06 is equal to ds:8D0E and if not
equal jumps to beggar off. So I knew that 8D00 must equal 8D0C
and that 8D06 must equal 8D0E.
All I needed to do was figure out where these came from. I
bpr'ed on 8D0C and found code which wrote the number to it.
I did not copy the ASM down, but this is what I wrote:
move 15th of "Magic" into AX fix it to 0-9 by +- A
put it in SI
mov 16th into AX
mul si by A
add ax to si
mov 17th to AX
mul si by A
add ax to it
put 18th in AX
mul si by A
add AX to it ; This is ds:8D0C !!!!
So now I knew where this came from, the last 4 digits of the
"magic" I bpr'ed on 8D0E and found out quickly that the first
digit of the "magic" was put into ds:8D0E.
Things were looking good. However, I was unable to figure out how
ds:8D06 and ds:8D00 were created. I know they are related to the
product code because they only change when it does. But they are
put there by a MOVSW command and I cannot figure out how to
predict where they are copied from, because it is only done once
and it is never from the same place, so all my attempts to bpr
on the spot they are copied from failed because it copies from
a new spot each time.
I felt dejected. I could not figure it out, even after days
of pointless tracing.
I stepped back and thought, and drank a can of Coke at 2
a.m...
(note from +ORC: Coke is dangerous for your health and your cracking
purposes... drink only Martini-Wodka and use by all means only russian
Wodka)
...I still had not figured out how the "magic" worked. I
decided to do that and come back to the problem of the numbers
generated from the Product Code.
I knew the call at cs:5C04 completely generated the "magic"
so I started there. I traced through it several times and found
that it made a CALL 3517 three times, then looped 6 times. So it
called 3517 a total of 18 times. I also noticed that the CALL
changed the number, but nothing else did, it just set up the
calls.
So I traced into CALL 3517 and came up with this:
mov ax,ss
nop inc bp
push bp
mov bp,sp
push ds
mov ds,ax
xor ax a bunch more unimportant stuff
:356b mov al,es:[bx} ; al = 18th digit
cbw push ax
mov ax, A
sub ax,[5dad] ; subtract 6 from a to get 4
imul [5db1] pop dx ; 18th digit
add dx,ax add dx, -30 ;clean it
mov [5db5], dx ;save it then fix [5db5] to be
between 0 and 9
mov al, es:[bx] ; load 18th again
cbw push ax
mov ax,a
sub ax,[5dad]
imul [5db3]
pop dx ; 18th digit
add dx, ax
add dx, -30 ;clean it
:35bb mov [5db7], dx ;save it.
:35d9 mov bx,[5dad]
mov es, bp+1a
add bx,[bp+18]
mov al,es:[bx-1] ; al = 6th digit
mov [5dc3], al ; save it
mov bx,[5dad]
mov es,[bp+1e]
add bx,[bp+1c]
mov al,es:[bx-1] ; 12th digit
mov [5dc4], al ; save it more junk then
3605: mov bx,[5dbf] ; this is the beginning of a loop
mov es,[bp+1a]
add bx,[bp+18]
mov al,es:[bx-1] ; 5th digit
push ax ; save it
mov ax,[5db5] ; [5db5] created above using 18th digit
mov dx,A
imul dx ;[5db] *A
les bx,[bp+0c]
add bx,ax
add bx,[5db7] ; created using 18th
pop ax ;5th digit
sub al,es:[bx};subtract a value from the lookup table
les bx add bx,[5dbf]
mov es:[bx],al ; Put new value in 6th spot fix it
so that it's between 0 and 9 by +- A
:3656 mov bx,[5dbf]
mov es,[bp+1e]
add bx,[bp+1c]
mov al,es:[bx-1] ; 11th digit
push ax mov ax,[5db5]
mov dx,a
imul dx
les bx
add bx,ax
add bx,[5db7]
pop ax ;11th digit
sub al,es:[bx] ; subtract a value from lookup
table
les bx, [bp+1c]
add bx,[5db7]
mov es:[bx],al ;put it in 12th spot fix it to be
between 0 and 9 The loop above repeats doing the same thing,
changing 2 numbers, but not always the same two. The next time
through it changes 5th and 11th, after that the 4th and 10th, 3rd
and 9th then the 2nd and 8th using this same pattern. After the
loop it changes the 1st and 7th using the values of the original
6th and 12th which were saved in [5dc3] and [5dc4] using the same
pattern. I quickly wrote a program in C which would produce this
number, and it worked fine.
I traced into the second call of 3517 and found that the
parameters passed to it changed which values where used to create
[5db5] and [5db7], whether they increment or decrement, whether
you add 0 or 64 to your index for the lookup and the digits which
are changed. All three calls to 3517 have a different
arrangement, but the their arrangement is the same each time they
are called. For instance, the three calls are looped over 6
times, on each instance that the 1st call is executed it will
change the 6th,12th,5th, 11th, etc. So I modified my C program
to mimic the behaviour of each call and looped it six times,
expecting this to be the "magic" number. To my surprise it was
not right.
So I followed the code until after the 3 CALL 3517's had
been made, this was the number my generator had given me, so it
must do something more afterwards.
I found the following code, still within the cs:5c04 call
:44C1 mov al,es:[bx+si-2] ; 17th digit
add al,d0 ;clean it
mov [5dc1], al ;save it
les bx
mov al,es:[bx+si-2] ; 18th digit
add al,d0 ;clean it
mov [5dc2],al ;save it
mov [5dbf],0
jmp 455f :44df
les bx
add bx,[5dbf]
mov al,[5dc1] ;17th cleaned
sub es:[bx],al ;1st digit has 17th cleaned
subtracted from it fix it between 0 and 9
mov al,[5dc2] ;18th cleaned 4520
sub es:[bx],al ;7th - 18th cleaned is put in 7th
spot fix it between 0 and 9
:455b inc word ptr [5dbf]
mov ax,[5dbf]
cmp ax,[5dad] ; run six times
jge 456b
jmp 44df 456b: blah blah continue on.
This loop executes six times each time incrementing the digit to
be changed by one so that the first change changes the 1st digit,
and the next time through the loop the 2nd then the 3rd.....till
the sixth. The second change alters the 7th through the 12th
digits. I added code to do this at the end of my Generator, and
I now had a "Magic" number generator. However this did not do me
much good in itself. The breakthrough was reversing this program
(it wasn't hard, but getting all the bugs out was really tough)
so that it takes a "magic" number as input and tells you what
registration number will produce it. I have included the source
code for both programs to prove that they are my own work. The
coding is not the best, they are my own crude tools, and they do
the job I need them for. But now I am home free. Even without
knowing how the product code is manipulated to come up with
ds:8D00 and ds:8d06 I can crack it. Here's what I did.
The product code given me was 3850-0118-6260-1057-23 I
traced to where ds:8D00 and ds:8D06 are placed they were:
ds:8D00 = E03 ds:8D06 = 3 I knew the last 4 digits when added,
and multiplied as explained above must be E03 so this is what I
wrote down, using my calculator
DFC + 7 =E03
This is the final answer, but I need to work backwards from here
166 * A = DFC
15E + 8 = 166
23 * A = 15E
1E +5 = 23
3 * A = 1E
Just working things backwards from the way the program did it I
figured out the last 4 digits of the magic code need to be 3587
in order for it to produce E03 as a result. I also know that the
first digit must be equal to ds:8d06 which is 3 so I now have:
3___-____-____-__35-87
as a "magic" number and I fill it in with 1's
3111-1111-111X-1135-87
I left the 12th number as an X because I remember that the
remainder of the sum of all the digits except the 12th must be
equal to the 12th.
3+1+1+1+1+1+1+1+1+1+1+1+1+3+5+8+7 = 26 26/A
26 26/A = 3 with a remainder of 8,
so the 12th digit is an 8!
My "magic" number should be 3111-1111-1118-1135-87
So I run my UNINSTAN program, which tells me that in order to get
that "magic" I need to enter the following registration code:
4798-8540-6989-6899-53 I enter this in, the "Retrieve" button is
enabled and I install Norton Utilities without a hassle! I used
the same method to install Wine Select (I've been interested in
wine since reading about your Pomerol), Labels Unlimited (which
I use for what else? Barcodes!), Harvard Graphics, and Lotus Ami
Pro, which I'm using to write this report on!
Well, that's it. That is how I cracked Instant Access. As
I mentioned above it is not the best way, but I gave everything
I had and it's the best I could do.
I have succeeded because I have beaten the protection, and
because I taught myself a lot along the way. I'm sure you already
have a "magic" number generator of your own, but I included mine
so you could see it. If I just knew how the product code produces
those 2 numbers I could create a product code to registration
number converter, which I assume is what the operators at Instant
Access have when you call them to buy stuff.
One last note about this assignment. I know that you have
realized that Instant Access was hard to find. I want to tell you
how I got it, a bit a social engineering in itself. After
searching every library and book/magazine store in the city I got
on the Internet and asked. Nobody had it.
So I found the Instant Access homepage. ...
(this will not be published, coz I, +ORC, do not want to expose my crackers,
but the way this guy got hold of the protection scheme is in itself worth
is access to the +HCU)
...So as you can see, I have gone to great lengths for admittance
into your University, and I hope I have earned it. I am proud to
wear my +
...address follows
And here are the two C++ programs:
INSTANT.CPP-----------------------------------
// Template for byte patch files
#include
#include
#include
#include
#include
void main()
{
char fix(char x);
char *t; //*stopstring
int save1, save2,fdbf,fdbs, i;
static int table[208] = {1,3,3,1,9,2,3,0, 9,0,4,3,8,7,4,4,
5,2,9,0,2,4,1,5, 6,6,3,2,0,8,5,6,
8,9,5,0,4,6,7,7, 2,0,8,0,6,2,4,7,
4,4,9,5,9,6,0,6, 8,7,0,3,5,9,0,8,
3,7,7,6,8,9,1,5, 7,4,6,1,4,2,7,1,
3,1,8,1,5,3,3,1, 2,8,2,1,6,5,7,2,
5,9,9,8,2,9,3,0, 0,4,5,1,1,3,8,6,
1,1,9,0,2,5,5,5, 1,7,1,5,8,7,1,9,
8,7,7,4,4,8,3,0, 6,1,9,8,8,4,9,9,
0,7,5,2,3,1,3,8, 6,5,7,6,3,7,6,7,
4,2,2,5,2,4,6,2, 6,9,9,1,5,2,3,4,
4,0,3,5,0,3,8,7, 6,4,8,8,2,0,3,6,
9,0,0,6,9,4,7,2, 0,1,1,1,1,0,1} ;
//_clearscreen(_GCLEARSCREEN);
printf("Enter the 18 digit Reg code: ");
gets(t);
for (i=1; i<=6 ; i++)
{
save1 = t[5]; // save the sixth digit
save2 = t[11]; // save the twelfth digit
fdbf = 0xFFC+t[17]-0x1000-0x30 ; // create [5db5]
if (fdbf < 0x0)
fdbf += 0xA; // fix it if necessary
else if (fdbf >= 0xA)
fdbf -= 0xA;
fdbs = fdbf; // and [5db7]
t[5] = t[4] - table[fdbf*0xA+fdbs] ; // sixth number
t[5] = fix(t[5]);
t[11] = t[10] - table[fdbf*0xA+fdbs+0x64]; // 12th
number
t[11] = fix(t[11]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[4] = t[3] - table[fdbf*0xA+fdbs] ; // 5th number
t[4] = fix(t[4]);
t[10] = t[9] - table[fdbf*0xA+fdbs+0x64]; // 11th
number
t[10] = fix(t[10]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[3] = t[2] - table[fdbf*0xA+fdbs] ; // 4th number
t[3] = fix(t[3]);
t[9] = t[8] - table[fdbf*0xA+fdbs+0x64]; // 10th number
t[9] = fix(t[9]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[2] = t[1] - table[fdbf*0xA+fdbs] ; // 3rd number
t[2] = fix(t[2]);
t[8] = t[7] - table[fdbf*0xA+fdbs+0x64]; // 9th number
t[8] = fix(t[8]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[1] = t[0] - table[fdbf*0xA+fdbs] ; // 2nd number
t[1] = fix(t[1]);
t[7] = t[6] - table[fdbf*0xA+fdbs+0x64]; // 8th number
t[7] = fix(t[7]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[0] = save1 - table[fdbf*0xA+fdbs]; // first digit
t[0] = fix(t[0]);
t[6] = save2 - table[fdbf*0xA+fdbs+0x64]; // 7th digit
t[6] = fix(t[6]);
//puts(t);
// end of first call
////////////////////////////////////////////////
save1 = t[5]; // save the sixth digit
save2 = t[17]; // save the 18th digit
fdbf = t[10]+0x4-0x30 ; // create [5db5]
if (fdbf < 0x0)
fdbf += 0xA; // fix it if necessary
else if (fdbf >= 0xA)
fdbf -= 0xA;
fdbs = t[9]+0x4-0x30; // and [5db7]
if (fdbs < 0x0)
fdbs += 0xA; // fix it if necessary
else if (fdbs >= 0xA)
fdbs -= 0xA;
t[5] = t[4] - table[fdbf*0xA+fdbs] ; // sixth number
t[5] = fix(t[5]);
t[17] = t[16] - table[fdbf*0xA+fdbs+0x64]; // 18th
number
t[17] = fix(t[17]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[4] = t[3] - table[fdbf*0xA+fdbs] ; // 5th number
t[4] = fix(t[4]);
t[16] = t[15] - table[fdbf*0xA+fdbs+0x64]; // 17th
number
t[16] = fix(t[16]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[3] = t[2] - table[fdbf*0xA+fdbs] ; // 4th number
t[3] = fix(t[3]);
t[15] = t[14] - table[fdbf*0xA+fdbs+0x64]; // 16th
number
t[15] = fix(t[15]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[2] = t[1] - table[fdbf*0xA+fdbs] ; // 3rd number
t[2] = fix(t[2]);
t[14] = t[13] - table[fdbf*0xA+fdbs+0x64]; // 15th
number
t[14] = fix(t[14]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[1] = t[0] - table[fdbf*0xA+fdbs] ; // 2nd number
t[1] = fix(t[1]);
t[13] = t[12] - table[fdbf*0xA+fdbs+0x64]; // 14th
number
t[13] = fix(t[13]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[0] = save1 - table[fdbf*0xA+fdbs]; // first digit
t[0] = fix(t[0]);
t[12] = save2 - table[fdbf*0xA+fdbs+0x64]; // 13th
digit
t[12] = fix(t[12]);
//puts(t);
// end of second call
////////////////////////////////////////////////
save1 = t[11]; // save the 12th digit
save2 = t[17]; // save the 18th digit
fdbf = t[1]+0x4-0x30 ; // create [5db5]
if (fdbf < 0x0)
fdbf += 0xA; // fix it if necessary
else if (fdbf >= 0xA)
fdbf -= 0xA;
fdbs = t[2]+0x4-0x30; // and [5db7]
if (fdbs < 0x0)
fdbs += 0xA; // fix it if necessary
else if (fdbs >= 0xA)
fdbs -= 0xA;
t[17] = t[16] - table[fdbf*0xA+fdbs] ; // 18th number
t[17] = fix(t[17]);
t[11] = t[10] - table[fdbf*0xA+fdbs+0x64]; // 12th
number
t[11] = fix(t[11]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[16] = t[15] - table[fdbf*0xA+fdbs] ; // 17th number
t[16] = fix(t[16]);
t[10] = t[9] - table[fdbf*0xA+fdbs+0x64]; // 11th
number
t[10] = fix(t[10]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[15] = t[14] - table[fdbf*0xA+fdbs] ; // 16th number
t[15] = fix(t[15]);
t[9] = t[8] - table[fdbf*0xA+fdbs+0x64]; // 10th number
t[9] = fix(t[9]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[14] = t[13] - table[fdbf*0xA+fdbs] ; // 15th number
t[14] = fix(t[14]);
t[8] = t[7] - table[fdbf*0xA+fdbs+0x64]; // 9th number
t[8] = fix(t[8]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[13] = t[12] - table[fdbf*0xA+fdbs] ; // 14th number
t[13] = fix(t[13]);
t[7] = t[6] - table[fdbf*0xA+fdbs+0x64]; // 8th number
t[7] = fix(t[7]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[12] = save2 - table[fdbf*0xA+fdbs]; // 13th digit
t[12] = fix(t[12]);
t[6] = save1 - table[fdbf*0xA+fdbs+0x64]; // 7th digit
t[6] = fix(t[6]);
// end of third call
////////////////////////////////////////////////
} // end of for loop
// Now we finish it up
save1 = t[16] + 0xD0 - 0x100; // [5dc1]
save2 = t[17] + 0xD0 - 0x100; // [5dc2]
for (i=0; i<6; i++)
{
t[i] = t[i] - save1;
t[i] = fix(t[i]);
t[i+6] = t[i+6] - save2;
t[i+6] = fix(t[i+6]);
}
printf("'Magic' code is: ");
for (i=0; i<18 ;i++) // output the string (only first 18)
putc(t[i], stdout);
printf("\n\n Created by xxxxx for +Orc's HCU 1996");
} // end of main()
char fix(char x)
{
if (x < '0')
x = x+0xA;
else if (x > 0x39)
x -= 0xA;
return x;
}
---------------------------------------------------
UNINSTANT.CPP
#include
#include
#include
#include
#include
void main()
{
char fix(char x);
char *t;
int save1, save2,fdbf,fdbs, i,q, fdbssave,fdbfsave;
static int table[208] = {1,3,3,1,9,2,3,0, 9,0,4,3,8,7,4,4,
5,2,9,0,2,4,1,5, 6,6,3,2,0,8,5,6,
8,9,5,0,4,6,7,7, 2,0,8,0,6,2,4,7,
4,4,9,5,9,6,0,6, 8,7,0,3,5,9,0,8,
3,7,7,6,8,9,1,5, 7,4,6,1,4,2,7,1,
3,1,8,1,5,3,3,1, 2,8,2,1,6,5,7,2,
5,9,9,8,2,9,3,0, 0,4,5,1,1,3,8,6,
1,1,9,0,2,5,5,5, 1,7,1,5,8,7,1,9,
8,7,7,4,4,8,3,0, 6,1,9,8,8,4,9,9,
0,7,5,2,3,1,3,8, 6,5,7,6,3,7,6,7,
4,2,2,5,2,4,6,2, 6,9,9,1,5,2,3,4,
4,0,3,5,0,3,8,7, 6,4,8,8,2,0,3,6,
9,0,0,6,9,4,7,2, 0,1,1,1,1,0,1} ;
printf("Enter the 18 digit 'Magic' code: ");
gets(t);
save1 = t[16] + 0xD0 - 0x100; // [5dc1]
save2 = t[17] + 0xD0 - 0x100; // [5dc2]
for (i=5; i>=0 ; i--) // fix it before main loop
{
t[i] = t[i] +save1;
t[i] = fix(t[i]);
t[i+6] = t[i+6] + save2;
t[i+6] = fix(t[i+6]);
}
for (i=1; i<=6 ; i++)
{
// begin third call
fdbf = 0x4+t[1]-0x30 ; // create [5db5]
if (fdbf < 0x0)
fdbf += 0xA; // fix it if necessary
else if (fdbf >= 0xA)
fdbf -= 0xA;
fdbs = 0x4+t[2]-0x30 ; // create [5db7]
if (fdbs < 0x0)
fdbs += 0xA; // fix it if necessary
else if (fdbs >= 0xA)
fdbs -= 0xA;
save1 = t[6]; //save 7th
save2 = t[12]; // and 13th
for (q=1; q<=5; q++) // put [ ]'s where they were at end
of loop
{
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
}
fdbssave = fdbs;
fdbfsave = fdbf;
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[6] = t[7] + table[fdbf*0xA+fdbs+0x64]; // 7th digit
t[6] = fix(t[6]);
t[12] = t[13] + table[fdbf*0xA+fdbs]; // 13th digit
t[12] = fix(t[12]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[7] = t[8] + table[fdbf*0xA+fdbs+0x64]; // 8th digit
t[7] = fix(t[7]);
t[13] = t[14] + table[fdbf*0xA+fdbs]; // 14th digit
t[13] = fix(t[13]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[8] = t[9] + table[fdbf*0xA+fdbs+0x64]; // 9th digit
t[8] = fix(t[8]);
t[14] = t[15] + table[fdbf*0xA+fdbs]; // 15th digit
t[14] = fix(t[14]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[9] = t[10] + table[fdbf*0xA+fdbs+0x64]; // 10th digit
t[9] = fix(t[9]);
t[15] = t[16] + table[fdbf*0xA+fdbs]; // 16th digit
t[15] = fix(t[15]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[10] = t[11] + table[fdbf*0xA+fdbs+0x64]; // 11th
digit
t[10] = fix(t[10]);
t[16] = t[17] + table[fdbf*0xA+fdbs]; // 17th digit
t[16] = fix(t[16]);
t[11] = save1 + table[fdbfsave*0xA+fdbssave+0x64]; //
12th digit
t[11] = fix(t[11]);
t[17] = save2 + table[fdbfsave*0xA+fdbssave]; // 18th digit
t[17] = fix(t[17]);
// end of third call
// begin second call
fdbf = 0x4+t[10]-0x30 ; // create [5db5]
if (fdbf < 0x0)
fdbf += 0xA; // fix it if necessary
else if (fdbf >= 0xA)
fdbf -= 0xA;
fdbs = 0x4+t[9]-0x30 ; // create [5db7]
if (fdbs < 0x0)
fdbs += 0xA; // fix it if necessary
else if (fdbs >= 0xA)
fdbs -= 0xA;
save1 = t[0]; //save first
save2 = t[12]; // and 13th
for (q=1; q<=5; q++) // put [ ]'s where they were at end
of loop
{
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
}
fdbssave = fdbs;
fdbfsave = fdbf;
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[0] = t[1] + table[fdbf*0xA+fdbs]; // 1st digit
t[0] = fix(t[0]);
t[12] = t[13] + table[fdbf*0xA+fdbs+0x64]; // 13th digit
t[12] = fix(t[12]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[1] = t[2] + table[fdbf*0xA+fdbs]; // 2nd digit
t[1] = fix(t[1]);
t[13] = t[14] + table[fdbf*0xA+fdbs+0x64]; // 14th digit
t[13] = fix(t[13]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[2] = t[3] + table[fdbf*0xA+fdbs]; // 3rd digit
t[2] = fix(t[2]);
t[14] = t[15] + table[fdbf*0xA+fdbs+0x64]; // 15th digit
t[14] = fix(t[14]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[3] = t[4] + table[fdbf*0xA+fdbs]; // 4th digit
t[3] = fix(t[3]);
t[15] = t[16] + table[fdbf*0xA+fdbs+0x64]; // 16th digit
t[15] = fix(t[15]);
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
t[4] = t[5] + table[fdbf*0xA+fdbs]; // 5th digit
t[4] = fix(t[4]);
t[16] = t[17] + table[fdbf*0xA+fdbs+0x64]; // 17th digit
t[16] = fix(t[16]);
t[5] = save1 + table[fdbfsave*0xA+fdbssave]; // 6th
digit
t[5] = fix(t[5]);
t[17] = save2 + table[fdbfsave*0xA+fdbssave+0x64]; // 18th
digit
t[17] = fix(t[17]);
// end of second call
// begin first call
fdbf = 0xFFC+t[17]-0x1000-0x30 ; // create [5db5]
if (fdbf < 0x0)
fdbf += 0xA; // fix it if necessary
else if (fdbf >= 0xA)
fdbf -= 0xA;
fdbs = fdbf; // and [5db7]
save1 = t[0]; //save first
save2 = t[6]; // and 7th
for (q=1; q<=5; q++) // put [ ]'s where they were at end
of loop
{
fdbf -= 1; // decrement
if (fdbf == -1)
fdbf = 9;
fdbs -= 1;
if (fdbs == -1)
fdbs = 9;
}
fdbssave = fdbs;
fdbfsave = fdbf;
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[0] = t[1] + table[fdbf*0xA+fdbs]; // 1st digit
t[0] = fix(t[0]);
t[6] = t[7] + table[fdbf*0xA+fdbs+0x64]; // 7th digit
t[6] = fix(t[6]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[1] = t[2] + table[fdbf*0xA+fdbs]; // 2nd digit
t[1] = fix(t[1]);
t[7] = t[8] + table[fdbf*0xA+fdbs+0x64]; // 8th digit
t[7] = fix(t[7]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[2] = t[3] + table[fdbf*0xA+fdbs]; // 3rd digit
t[2] = fix(t[2]);
t[8] = t[9] + table[fdbf*0xA+fdbs+0x64]; // 9th digit
t[8] = fix(t[8]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[3] = t[4] + table[fdbf*0xA+fdbs]; // 4th digit
t[3] = fix(t[3]);
t[9] = t[10] + table[fdbf*0xA+fdbs+0x64]; // 10th digit
t[9] = fix(t[9]);
fdbf += 1; // increment
if (fdbf == 10)
fdbf = 0;
fdbs += 1;
if (fdbs == 10)
fdbs = 0;
t[4] = t[5] + table[fdbf*0xA+fdbs]; // 5th digit
t[4] = fix(t[4]);
t[10] = t[11] + table[fdbf*0xA+fdbs+0x64]; // 11th digit
t[10] = fix(t[10]);
t[5] = save1 + table[fdbfsave*0xA+fdbssave]; // 6th
digit
t[5] = fix(t[5]);
t[11] = save2 + table[fdbfsave*0xA+fdbssave+0x64]; // 12th
digit
t[11] = fix(t[11]);
// end of first call
} // end for loop
printf("\nTo Get That 'Magic' Use: ");
for (i=0; i<18 ;i++) // output the string (only first 18)
putc(t[i], stdout);
printf("\n\n Created by +xxxxx for +Orc's HCU 1996");
} // end of main()
char fix(char x)
// fixes chars to between 0 and 9
{
if (x < '0')
x = x+0xA;
else if (x > 0x39)
x -= 0xA;
return x;
}
III. THE [DATADUMP_WINDOW] TRICK & HOW TO SEARCH THE WEB.
[WINFORMANT 4 HOW TO FIND IT]
I have chosen (as usual) an older windows application for
Win 3.1. (Version 1.10, by Joseph B. Albanese), in order to
terminate completely the "password lessons" and at the same time
show you a nice little trick that can be very useful in cracking
*ALL* protected programs (password protected or time protected
or function disabled): memory windows_dumping. There is in almost
all protection routines, as you have already learned, a moment
when on the stack appears the ECHO of the real, "correct"
passnumber or password, in order to compare the input of the user
with it.
The location of this ECHO varies, but it will be most of the
time in a range of +- 0x90 bytes from the user input. This is due
to datadump windows restraints inside the tools used by the
protectionists I'll not delve inside here, and this use is bound
to diminish (especially after this lesson :=).
You'll find the files that I use in this lesson searching
the web with the usual search_tools and search_strategies: These
are names, lengths and dates of the relevant files... this will
allow you to FTPMAIL them after having located them through an
ARCHIE_search:
CTL3D DLL 20976 17/08/93 4:36
README WRI 2688 08/05/94 1:54
SS3D2 VBX 88096 11/06/92 18:42
STDLL DLL 10880 06/05/94 22:57
THREED VBX 64432 17/07/93 0:00
WIN4MANT EXE 562271 07/06/96 17:51
WIN4MANT HLP 190608 08/05/94 0:36
XLIST VBX 15248 15/02/93 0:00
Please do not underestimate the importance of *EXACT NAMES*
on the Web (be it of people, of subjects or of software)... as
a matter of fact the Web corroborates (every day more). The old
intuition from Persio: NOMEN ATQUE OMEN: how true! Think a moment
about it, the importance of the NAMES on the Web is astonishing
(and growing)!
1) It is true for http://... addresses: domains must be
unique and registered (and the Web registration burocrats will
get from you 100 dollars per year just to keep them registered);
2) It is true for programs (you must know BEFOREHAND the name of
a file to find it quickly on the Web); 3) It's even true for your
own site denomination (try searching for "Bill's" page instead
than for "WIKKY_WAKKY's" page... that's (reversing this approach)
one of the reason I have a "+" inside my handle, this confuses
the search engines just enough to give me a little more anonymity
(search for me! You'll get quite a lot of Warcraft stuff :=).
Enough! If you do not know neither why all this happen nor
how to search the Web, but are interested in these matters (as
you should), study the web search engines themselves and read the
relevant help files (search AltaVista and WebCrawler for
"FTPMAIL", "WWW via e-mail", "AGORA", "search strategies" etc).
It's amazing how few crackers (not to mention the lusers)
do actually read the help files of the search engines they are
using, which should be your bible (or the coran, or some other
crap, for all I care about religions), your alpha and omega! The
(growing) amount of junk on the Web makes your ability to search
effectively the little grains of interesting truths that are
escaping the growing heavy censorship of our enemies even more
important.
Back to our [Winformant] cracking now, and back to our
stackdump window trick... here you are:
[WINFORMANT CRACKING]
This application is -per se- absolutely crap, I doubt you'll
ever use it: this program is so primitive it must have been one
of the first crappy visual basic experiments made by his
programmer... but this [Winformant] program is nevertheless very
interesting for us coz it possesses a curious (and pretty rare)
"deactivate" mode, i.e. you can "unregister" it on the fly if you
feel the need to... it beats me why the programmer wanted such
a feature inside... he was just probably collecting little
routines and mixing them without sound reasons.
This feature is as rare as useless, but it is worth for
cracking scholars that (should) investigate password algorithms
with valid and invalid codes without having to reinstall
everything only in order to delete previous valid codes.
For your cracking exercises you should therefore choose
programs that have "REVERSIBLE" protections (like this
Winformant... very rare) or that can be re-registered a billion
times (that's a more frequent protection pattern). Programs that
keep the valid registration on *.ini or special files can also
be useful... you just need to change a couple of lines in these
files to restore the "unregistered" mode.
The trick we'll use in this lesson: "password proximity",
bases on the fact that the protectionists need to keep an eye on
their protection when they "build" it and have to *see* closely
the relationships between
1) USER INPUT PASSNUMBER (i.e. the input registration number
that the user should have bought, but could be a fake bad guy
input)
2) USER INPUT TRANSFORMED (i.e. the result of the working of
the protectionist's algorithm on the user input passnumber)
and the
3) CORRECT PASSNUMBER ANSWER (The BINGO!) i.e., the Passnumber
calculated with some algorithm on the bases of the USER INPUT
NAME (the name of the user, eventually transformed in USER INPUT
TRANSFORMED).
In order to clear bugs these relationships must be
constantly checked when they prepare the protection... i.e. when
they are writing the protection code.
Most of the time all these data will therefore dwell inside
a small stack... that means they will be "visible" in the SAME
"watchwindow" inside the protectionist's debugger... and they use
the same turbodebugger (or Winice) YOU are using!
This means that most of the time the "ECHO" will swell not
very far away from the USER INPUT. Therefore proceed as follows:
Fire Winice
Fire Winformant
Choose HELP
Choose REGISTRATION
Fill the registration fields
this is mine: "+ORC+ORC" as "Registrant"
and "12121212" as "Activation" code
CTRL+D ;switch to Winice
task ;let's see the names
:task
TaskName SS:SP StackTop StackBot StackLow TaskDB hQueue Events
WINWORD 1AD7:85F2 4A52 8670 7532 1247 122F 0000
PROGMAN 1737:200A 0936 2070 1392 066F 07F7 0000
DISKOMAT *2C5F:6634 1D3C 6AC6 5192 2CB7 2C9F 0000
hwnd DISKOMAT ;which window is getting the input?
:hwnd diskomat
WinHandle Hqueue QOwner Class Name Window Procedure
0EB4(0) 2C9F DISKOMAT #32769 04A7:9E6B
0F34(1) 2C9F DISKOMAT #32768 USER!BEAR306
365C(1) 2C9F DISKOMAT #32770 2C3F:0BC6
36BC(2) 2C9F DISKOMAT Button 2C3F:1CEA
3710(2) 2C9F DISKOMAT Edit 2C3F:24BE
3758(2) 2C9F DISKOMAT Edit 2C3F:24BE
37A0(2) 2C9F DISKOMAT Button 2C3F:1CEA
37E4(2) 2C9F DISKOMAT Button 2C3F:1CEA
... and many more irrelevant windows.
bmsg relevant_window wm_gettext ;let's pinpoint the code, here
;the relevant window is the first "edit" one (obviously),
;i.e. wHnd 3710 you could also use GetWindowsText or
;GetDlgItmText to locate the relevant routines
:bmsg 3710 wm_gettext ;set breakpoint
CTRL+D ;run the babe
Break Due to BMSG 3710 WM_GETTEXT C=01
Hwnd=3710 wParam=0050 lParam=2C5F629A msg=000D WM_GETTEXT
2C3F:000024BE B82F2C MOV AX,2C2F
So! Now that we have pinpointed the code... let's snoop around
a little: first thing to do is a good stack command which, here,
will work OK (in other cracking sessions it may not -magic
involved- but do not worry: if it does not work immediately, just
pinpoint a little more... for instance on GetWindowText() (always
good) or do a BPRW diskomat (also very useful), and then try and
retry the stack... should this too fail to work, do search for
your input in memory (in the 30:0 lffffffff selector, as usual)
and breakpoint range on it with ReadWrite, and then stack, stack,
stack... till you get the "real" list of calls coming from your
babe's protection (in our example the babe's name is "DISKOMAT").
:stack
USER(19) at 073F:124C [?] through 073F:1239
CTL3D(02) at 2C3F:0D53 [?] through 2C3F:0D53
DISKOMAT(01) at 2C97:20B9 [?] through 2C97:20B9
DISKOMAT(01) at 2C97:3D94 [?] through 2C97:3D94
DISKOMAT(01) at 2C97:49E2 [?] through 2C97:4918
DISKOMAT(04) at 2C7F:EA20 [?] through 2C7F:EA20
USER(01) at 04A7:19BE [?] through USER!GETWINDOWTEXT
=> CTL3D(02) at 2C3F:24BE [?] through 04A7:3A3C‘
Beautiful stack picture! Immediately BPX on 2C7F:EA20 (on your
computer the segment will differ, the offset will be the SAME).
2C7F:EA20 9A25ABA704 CALL USER!GETWINDOWTEXT
2C7F:EA25 8E4608 MOV ES,[BP+08]
2C7F:EA28 26FFB42C02 PUSH WORD PTR ES:[SI+022C]
2C7F:EA2D 8D865CFF LEA AX,[BP+FF5C]
2C7F:EA31 16 PUSH SS
2C7F:EA32 50 PUSH AX
2C7F:EA33 6A50 PUSH 50
2C7F:EA35 9A25ABA704 CALL USER!GETWINDOWTEXT
2C7F:EA3A 8D46AE LEA AX,[BP-52] ;load ptr "+ORC+ORC"
2C7F:EA3D 16 PUSH SS ;save pointer segment
2C7F:EA3E 50 PUSH AX ;save pointer offset
2C7F:EA3F 9A768D872C CALL 2C87:8D76 ;strlen "ORC+ORC"
2C7F:EA44 83C404 ADD SP,+04
2C7F:EA47 3D2800 CMP AX,0028
2C7F:EA4A 762C JBE EA78
...
2C7F:EA78 8D442C LEA AX,[SI+2C]
2C7F:EA7B FF7608 PUSH WORD PTR [BP+08]
2C7F:EA7B FF7608 PUSH WORD PTR [BP+08]
2C7F:EA7E 50 PUSH AX
2C7F:EA7F 9AE002772C CALL 2C77:02E0
2C7F:EA84 0BC0 OR AX,AX
2C7F:EA86 740F JZ EA97
2C7F:EA88 687F2C PUSH 2C7F
2C7F:EA8B 68E4ED PUSH EDE4
2C7F:EA8E 6A00 PUSH 00
2C7F:EA90 6A00 PUSH 00
2C7F:EA92 6A00 PUSH 00
2C7F:EA94 E94501 JMP EBDC
2C7F:EA97 8D46AE LEA AX,[BP-52] ;load ptr "+ORC+ORC"
2C7F:EA9A 16 PUSH SS ;various algor on input
2C7F:EA9B 50 PUSH AX ;we do not care
2C7F:EA9C 8D860AFF LEA AX,[BP+FF0A]
2C7F:EAA0 16 PUSH SS
2C7F:EAA1 50 PUSH AX
2C7F:EAA2 6A51 PUSH 51
2C7F:EAA4 8D442C LEA AX,[SI+2C]
2C7F:EAA7 FF7608 PUSH WORD PTR [BP+08]
2C7F:EAAA 50 PUSH AX
2C7F:EAAB 9ABA00772C CALL 2C77:00BA
2C7F:EAB0 0BC0 OR AX,AX
2C7F:EAB2 0F851101 JNE EBC7
2C7F:EAB6 8D8E5CFF LEA CX,[BP+FF5C] ;ptr "12121212"
2C7F:EABA 16 PUSH SS
2C7F:EABB 51 PUSH CX
2C7F:EABC 9A768D872C CALL 2C87:8D76 ;strlen "12121212"
2C7F:EAC1 83C404 ADD SP,+04
2C7F:EAC4 50 PUSH AX
2C7F:EAC5 8D865CFF LEA AX,[BP+FF5C] ;ptr "12121212"
2C7F:EAC9 16 PUSH SS
2C7F:EACA 50 PUSH AX
2C7F:EACB 8D860AFF LEA AX,[BP+FF0A] ...etc
OK, it's enough: now what obviously follows is to "algorithmize"
this second string, and somewhere, then, you'll have a compare
that checks and divides good guys from bad fellows.
BUT NOW IT'S ALSO THE MAGIC MOMENT OF THE ECHO! We know it, we
feel it: The echo is somewhere... what can we do to find it?
Should we search "12121212" in memory? No, look how many
locations we would find...
:s 30:0 lffffffff '12121212'
Pattern Found at 0030:0005AD6A
Pattern Found at 0030:0048AD6A
Pattern Found at 0030:007DED98
Pattern Found at 0030:007E25F8
Pattern Found at 0030:008E0FE1
Pattern Found at 0030:008E1433
Pattern Found at 0030:008E186F
Pattern Found at 0030:008E1904
Pattern Found at 0030:008E601A
Pattern Found at 0030:80509D6A
Pattern Found at 0030:8145AD6A
Pattern Not Found
And now...should we look for all occurrences of this string
and get a memory dump of +- 0x90 around it till we find the
echo... that's not zen... that's boring, even if we know that the
relevant ones will ALWAYS be the ones that have MORE than
80000000 in their "offset", i.e., in this case, only two:
Pattern Found at 0030:80509D6A
Pattern Found at 0030:8145AD6A
But this procedure is not always true, and in other
protection there will be a proliferation of locations with the
aim of deterring casual crackers... clearly the above method is
no good... there must be some other way... YES THERE IS!
THE LAST loading of the numeric input string in the code
(the one after the strlen count) is most of the time (exspecially
in Visual Basic and Delphy programs) the "right" one for our
cracking purposes, coz the protections follow (most of the time)
this pattern (remember that we are here inside a stack "heavy"
section of the code... if you want to crack higher I suggest you
read some good literature about stack working and stack magics
inside the 80386/80486/80586 processors).
This is the usual sequence:
LOAD NAME
COUNT NAMELENGTH
LOAD NAME_AGAIN
TRANSFORM NAME
LOAD PASSCODE
COUNT PASSCODE_LENGTH
LOAD PASSCODE_AGAIN
<- ECHO CHECK here
TRANSFORM PASSCODE
<- ECHO CHECK here
COMPARE TRANSFORMED_NAME WITH TRANSFORMED_PASSCODE
So... what does this mean? This means that at line
2C7F:EAC5 8D865CFF LEA AX,[BP+FF5C] ;ptr "12121212"
you'll already have your echo somewhere... just dump the memory
around the pointer [BP+FF5C]:
:d 2c5f:61e8
2C5F:61E8
02 62 2F 06 02 00 26 2E-A3 4E A3 4E 01 00 38 30 .b/...&..N.N..80
33 37 2D 36 34 36 2D 33-38 33 36 00 01 06 02 00 37-646-3836.....
2F 06 75 62 C3 2E B7 04-F2 24 2F 06 CE 6E 2F 06 /.ub.....$/..n/.
49 00 5A 00 00 00 01 00-04 2C 2F 06 AE 24 36 62 I.Z......,/..$6b
74 62 7A 2E B7 04 36 62-01 00 C2 62 2F 2C 26 2E tbz...6b...b/,&.
03 01 BA 0F AE 24 5F 02-C9 01 5E 02 BA 01 5F 02 .....$_...^..._.
31 32 31 32 31 32 31 32-00 00 0C 00 BC 02 00 00 12121212........
00 00 00 00 49 00 BA 0F-AE 24 F2 24 2F 06 00 00 ....I....$.$/...
AF 17 00 00 00 00 E2 5F-7A 62 FE FF 79 1B BA 0F ......._zb..y...
96 0B 01 00 02 4E 00 00-37 01 8A 62 D2 0F 8F 17 .....N..7..b....
2F 06 00 00 00 00 37 01-98 62 20 10 16 03 2F 06 /.....7..b .../.
C2 62 2B 4F 52 43 2B 4F-52 43 00 0D AE 24 2F 06 .b+ORC+ORC...$/.
2C5F:62A7
and look... everybody is there! The stack pointers points in the
middle of this dump, at the string "12121212". 0x50 bytes before
is our good old ECHO (i.e. the CORRECT passnumber) and 0x50 bytes
afterwards is my beautiful input name "+ORC+ORC".
Therefore the "right" code for "+ORC+ORC" is 8037-646-3836.
It cannot be so easy! You'll protest. It is: this crap protection
is already cracked and hunderts of Visual Basic/Delphy schemes
are absolutely identical.
Now begins the hard work: if you really want to learn,
accomplish the following tasks:
- First of all "Unregister" and find anew your own code for
your own handle. *DO NOT* use serial numbers with any other
name that your own handle.
- Study the two coding algorithms, the one for the input name
and the one for the input passnumber, this will be useful
for ALL your future cracking sessions.
- Find the compare locations, i.e. the code block that sets
the two usual flags "good guy, you may move on" and "bad
cracker, beggar off", and create a patch crack for this
protection, that will allow anybody, with any name and any
password number to get through.
Please accomplish all of the preceding tasks: once you do it
you'll have FINISHED the password protection schemes part of my
tutorial and you'll be able to pass over to the (very
interesting) world of disabled and crippled functions (all these
"demos" that do not save and do not print... I'll teach you how
to do it, starting in Februar 1997).
Well, that's it for this lesson, reader. Not all lessons of my
tutorial are on the Web.
You 'll obtain the missing lessons IF AND ONLY IF you mail
me back (via anon.penet.fi) with some tricks of the trade I may
not know that YOU discovered. Mostly I'll actually know them
already, but if they are really new you'll be given full credit,
and even if they are not, should I judge that you "rediscovered"
them with your work, or that you actually did good work on them,
I'll send you the remaining lessons nevertheless. Your
suggestions and critics on the whole crap I wrote are also
welcomed.
"If you give a man a crack he'll be hungry again
tomorrow, but if you teach him how to crack, he'll
never be hungry again"
+ORC 526164@anon.penet.fi
HOW TO CRACK, A TUTORIAL - LESSON 4
by +ORC (the old red cracker)
How to crack, an approach LESSON 1
How to crack, tools and tricks of the trade LESSON 2
How to crack, hands on, paper protections LESSON 3 (1-2)
-> How to crack, hands on, time limits LESSON 4
How to crack, hands on, disk-Cdrom access LESSON 5
How to crack, funny tricks LESSON 6 (1-2)
How to crack, intuition and luck LESSON 7
How to crack windows, an approach LESSON 8
How to crack windows, tools of the trade LESSON 9
How to crack, advanced cracking LESSON A (1-2)
How to crack, zen-cracking LESSON B
How to crack, cracking as an art LESSON C
How to crack INDEX
LESSON 4 - HOW TO CRACK, HANDS ON, Time Limits
For 'time protections' we intend a serie of protection schemes
which are aimed to restrict the use of an application
ONE
-to a predetermined amount of days, say 30 days, starting with
the first day of installation... 'CINDERELLA' TIME PROTECTIONS
TWO
-to a predetermined period of time (ending at a specific fixed
date) independently from the start date... 'BEST_BEFORE' TIME
PROTECTIONS
THREE
-to a predetermined amount of minutes and/or seconds each time
you fire them... 'COUNTDOWN' TIME PROTECTIONS
FOUR
-to a predetermined amount of 'times' you use them, say 30
times. Strictly speaking these protections are not 'time'
dependent, but since their schemas are more or less on the
same lines as in the cases ONE, TWO and THREE, we will examine
them inside this part of my tutorial. Let's call them 'QUIVER'
protections since, as with a quiver, you only have a
predetermined amount of 'arrows' to shoot (and if you never
went fishing with bow and arrows, on a mountain river, you do
not know what's real zen... the fish springs out suddendly, but
you 'knew' it, and your fingers had already reacted... a lot of
broken arrows on the rocks, though :=)
As first example I have chosen a double protected
application: it has a time protection (of the 'Cinderella' type,
limited to 90 days) as well as a 'quiver' protection
scheme, which is the other -not time bounded- current variante
of the shareware protections... i.e. you should use this program
only 25 times before a protection lock.
It's a relatively 'old' windows protection (april 1995). I found
the program on a cheap cd-rom, which I bought (in a bunch with
9 others) a month ago: 6000 megabytes of bad protected software
for the price of a good glass of wine! PCPLUS SUPER CD nø13,
originally edited in July 1995. I believe it should be pretty
easy to find it or to find this program on the Web if you do not
already have it inside your collection of cheap CD-ROM. Another
advantage of this program, from our perspective, is that the
whole PCFILE.EXE represents de facto the protection scheme
itself... not excessively overbloated: only 8912 bytes, when the
'real' application works inside the (huge and overbloated)
pcf.dll, which will be called only if the user passes the
protection. You can easily print the WHOLE disassembled listing
of PCFILE.EXE (46 Wordperfect pages), that you'll quickly get
through wcb (for instance). For once you'll have a COMPLETE and
COMPLICATED protection scheme under your eyes.
Basically we'll study here the 'beginning' of more complex
time protection schemes, the ones we'll crack with our later
lessons. Some protection elements are here still 'na‹v', but the
protectionists have -at least- worked a little against easy
cracks... which makes this protection even more interesting for
us :=)
This program shows even a 'nasty' behaviour: should you use
it after the locking snapped, it will obliterate the whole (main)
pcf.dll from your harddisk, without any warning. This obviously
does not mean anything at all here, but it's the secret to more
advanced (and nastier) protection schemes, so you better have a
look at it too. Nice, enough let's start now.
[PCFILE] (aka the 'dll counter' method)
PCFILE, version 8, (PCFILE.EXE, 8912 bytes, 17 apr 1995, Atlantic
Coast software) is a database program which will be disabled
after having 90 days from its first use or after having used it
25 times, whichever comes first.
We'll begin as usual: just use your wordprocessor search
capacities to search inside the whole directory (and
subdirectories) of PCFILE for words like 'demo' 'order' 'contact'
'expire' 'disabling' 'evaluation' and so on (alternatively, like
I do, you can write your own little C utility to do it even more
quickly and automatically on the whole 600 megabytes CD-ROM you
have inserted on your drive :=)... You'll see immediately that
only two of the PC-files can interest us: PCFILE.EXE and
PCFRES.DLL. A quick 'turbodumping' of PCFILE.EXE itself will
fetch all filenames and nagstrings we need to be happy from the
end of the file... here they are:
A) 010C PCF.DAT
B) 0114 PCF.DLL
1) 2.011C PC-FIle demo has been disabled...
2) 2.01A2 The PC-File demo program has reached the maximum
allowable 25 sessions...
3) 2.0298 This demo version of PC-File 8 is designed...
4) 2.035A The PC-File demo program has reached... 90 days
5) 2.0474 This is the last demo session...
When I see something like this I know that the crack is already
made... it's so easy I can't understand why they don't just give
their software away for free... money I suppose, people seem to
be obsessed with this prepuberal problem... how stupid, besides:
neminem pecunia divitem fecit.
Beside, snooping inside files can be graet fun! At times you find
some 'real' info inside them... Have a look at lotus Wordpro,
for instance, you'll read something like: 'You idiot! Can't flow
a partial paragraph!'; 'Yow! Need to SetFoundry() on this object!';
'Dude! I couldn't find myself!'; 'Ain't nothing to pop!' and many
other amenities which throw a crude light on the life (and possible
blunders) of commercial programmers and on the well know fact
that most application are throw out FULL of bugs just in order
to make money ('bugs for bucks').
OK, back to our cracking: let's just search for the above NUMBERS
inside the code of PCFILE:
1) PC-File has been disabled: 011C
1.1100 >C8040100 enter 0104, 00
1.1104 56 push si
1.1105 C70632060000 mov word ptr [0632], 0000
1.110B 6A00 push 0000
1.110D B81401 mov ax, 0114; THIS is PCF.DLL
1.1110 8946FE mov [bp-02], ax
1.1113 50 push ax
1.1114 9A2E0D0212 call 1:0D2E ;what happens here?
1.1119 83C404 add sp, 0004
1.111C 40 inc ax
1.111D 7532 jne 1151
1.111F 1E push ds
1.1120 681C01 push 011C ;HERE****
1.1123 8D86FCFE lea ax, [bp-0104]
1.1127 16 push ss
1.1128 50 push ax
1.1129 9A6E110000 call USER._WSPRINTF
Therefore this target will be disabled after a check at the
beginning of WinMain (1.1100) if ax, after having been
incremented is non zero. We should have a look at the routine at
1:0D2E to see what happens... but let's first check the other
nagstrings... no point in delving immediatly inside routines.
2) The PC-File demo has reached the maximum allowable 25
sessions... 01A2
1.11C9 >807EFC66 cmp byte ptr [bp-04], 66
1.11CD 7C0F jl 11DE
1.11CF 6AFF push FFFF
1.11D1 9A36120000 call USER.MESSAGEBEEP
1.11D6 6A00 push 0000
1.11D8 1E push ds
1.11D9 68A201 push 01A2 ; HERE ****
1.11DC EB62 jmp 1240
Therefore 25 sessions if byte ptr [bp-04] >= 66 (as you can see,
the protectionists did not use anything vaguely similar to 25dec,
which is 19hex).
3) This demo version of PC-File 8 is designed... : 0298
1.11DE >807EFC4D cmp byte ptr [bp-04], 4D
1.11E2 7518 jne 11FC
1.11E4 6A00 push 0000
1.11E6 1E push ds
1.11E7 689802 push 0298 ;HERE ****
1.11EA 1E push ds
1.11EB FF361000 push word ptr [0010]
1.11EF 6A00 push 0000
1.11F1 9A48120000 call USER.MESSAGEBOX
1.11F6 C70632060100 mov word ptr [0632], 1 ;Flag 632!
This 'Welcome nagged user' message appears therefore only THE
FIRST time you run, when our byte ptr [bp-04] has been set to 4D.
That figures: 66h - 4Dh = 19h, which are the 25 times allowed...
the programmers from Atlantic Coast must have thought something
like 'Stupid crackers will not fetch our nice clever protection:
he'll be searching for byte 19h! Ah!' Note the flag set in
location [632] if it's the first run :=)
4) The PC-File demo program has reached... 90 days : 035A
1.1211 833E320600 cmp word ptr [0632], 0000
1.1216 7565 jne 127D
1.1218 A13406 mov ax, [0634]
1.121B 8B163606 mov dx, [0636]
1.121F 2B062C06 sub ax, [062C]
1.1223 1B162E06 sbb dx, [062E]
1.1227 83FA76 cmp dx, 0076
1.122A 7251 jb 127D
1.122C 7705 ja 1233
1.122E 3D00A7 cmp ax, A700
1.1231 764A jbe 127D
1.1233 >6AFF push FFFF
1.1235 9A3C130000 call USER.MESSAGEBEEP
1.123A 6A00 push 0000
1.123C 1E push ds
1.123D 685A03 push 035A ; HERE!
There, location [634] in ax and location [636] in dx.
ax subtracts location [62C] and dx subtracts with carry location
[62E]. Is it more than 76h? (Which is 118 dec), Tell user he has
reached 90 days. Is it exactly 76h? Then have a look at ax, if
it is more than A700 then tell user the same.
5) This is the last demo session... : 0474
1.132D >56 push si
1.132E 9AFFFF0000 call KERNEL._LCLOSE
1.1333 807EFC66 cmp byte ptr [bp-04], 66
1.1337 7C19 jl 1352
1.1339 6AFF push FFFF
1.133B 9AFFFF0000 call USER.MESSAGEBEEP
1.1340 6A00 push 0000
1.1342 1E push ds
1.1343 687404 push 0474 ;HERE****
1.1346 1E push ds
1.1347 FF361000 push word ptr [0010]
1.134B 6A10 push 0010
1.134D 9AFFFF0000 call USER.MESSAGEBOX
1.1352 >1E push ds
1.1353 681401 push 0114 ;this is PCF.DLL
1.1356 6A01 push 0001
1.1358 9AFFFF0000 call KERNEL.WINEXEC ;exec PCF.DLL
And here, finally we have our good old [bp-04] -once more-
compared to 66h. Notice that there is no Jumpequal nor
jumpgreater check. This means that the program ALREADY KNOWS that
the user has reached here for the first time the fatidic 66. This
means (of course) that this code will be examined AFTER having
incremented the counter of the protection, which must therefore
happen somewhere between 1.123D and 1.132D (the end of routine
4 and the beginning of routine 5). If you have printed the whole
disassembled listing of PCFILE.EXE and if you have read my other
lessons about dead listing (-> 9.3 and 9.4) you do not need any
more to read the following part of this lesson. Choose your
armchair and sit there with a pen, your listing and a good
cocktail (may I suggest a good Martini-Wodka? Don't use anything
else but Moskowskaja). The moment to start 'feeling' the code has
come! You can do everything alone. Write colored arrows on your
listing! The first (or the fourth) simphony of Mahler on your CD!
Everything will appear!
Indeed, if you prefer to follow here, behold: at 1.12B2 we
have a call KERNEL._LOPEN wich opens the file PCF.DLL (0114):
1.12AD 681401 push 0114 ;want pcf.dll
1.12B0 6A01 push 0001
1.12B2 9AFFFF0000 call KERNEL._LOPEN ;open it
and at 1.12CD we have the exact point where, inside pcf.dll, a
byte will be modified (at 10AF8):
1.12C6 6A01 push 0001
1.12C8 68F80A push 0AF8
1.12CB 6A00 push 0000
1.12CD 9AFFFF0000 call KERNEL._LLSEEK
The only modification takes place therefore inside PCF.DLL, a
monstruosity of 1088832 bytes, where location 10af8 grows WITHOUT
any change in the date of the dll. You can easily check this:
* copy pcf.dll pcf.ded
* (run pcfile a couple of time)
* fc /b pcf.dll pcf.ded
fc /b is file compare /binary, good old (and quick) dos, duh?
And this is what you get...
Comparing files PCF.DLL and PCF.DED
00010AF8: 55 50
Et voila mesdames et messieurs, found the other way round, please
note that this more 'practical' method can also be used *before*
beginning the dead listing examination of the file (and would
have given you the '0AF8' string to search for).
Well, what did we learn? A lot: an hidden counter grows in
another file without leaving many traces. The 'quiver'
protection snaps after growing more than 66h, having started at
4Dh. The flag for first time user is inside [0632]. [0634] and
[0636] are used for the current date, [062C] and [062E] are the
original date against which they are checked in a funny way.
There are two different protections, therefore we'll need
two different cracks to deprotect this cram. Let's begin with the
easiest one.
Our FIRST crack, must destroy the counter that increases inside
pcf.dll (the '25' session allowance). This will be made cracking
following instruction:
1.12F3 FE46FC inc byte ptr [bp-04]
which is obviously the increasing instruction we are searching
for (BECAUSE it's the only 'inc byte ptr' in the whole stupid
program, AND because it is located short after the _LLSEEK, AND
because it's incrementing nobody else than our good old [bp-
04]... what do you want more, a neon green flashing arrow light
on the top of it?)
We'll very simply "noop" this instruction, transforming it, for
instance, in 40 90 48 (inc ax, nop, dec ax = do nothing). Well,
yes, that was it for the '25 sessions' lock protection, thankyou,
you may use the program a zillion times now. What now? Ah, yes,
the DATE lock, let's have a look once more at it:
1.1218 A13406 mov ax, [0634]
1.121B 8B163606 mov dx, [0636]
1.121F 2B062C06 sub ax, [062C]
1.1223 1B162E06 sbb dx, [062E]
1.1227 83FA76 cmp dx, 0076 ;118 (-90=1c)
1.122A 7251 jb 127D
1.122C 7705 ja 1233
1.122E 3D00A7 cmp ax, A700 ;(42572)
1.1231 764A jbe 127D
1.1233 >6AFF push FFFF
1.1235 9A3C130000 call USER.MESSAGEBEEP
1.123A 6A00 push 0000
1.123C 1E push ds
1.123D 685A03 push 035A ;HERE! 90 days!
Therefore, if location [636] is > than 76, the nag snaps.
This 76 is calculated through what SEEMS a simple comparison
between the actual date and the installation date.
1.1218 A13406 mov ax, [0634] ;load date ax
1.121B 8B163606 mov dx, [0636] ;load date dx
1.121F 2B062C06 sub ax, [062C] ;subtract first date
1.1223 1B162E06 sbb dx, [062E] ;subtract first date
1.1227 83FA76 cmp dx, 0076 ;allowed limit (?)
1.122A 7251 jb 127D ;ok: you may
1.122C 7705 ja 1233 ;beggar off
1.122E 3D00A7 cmp ax, A700 ;well, what's this
1.1231 764A jbe 127D ;then?
In the reality there are various mathematical checkings
going on here, as the second check on ax = A700 shows. This DOES
NOT need to concern us much (we'll crack this code, later,
changing the 'first time user' flag), but it's useful you have
a rough understanding of what goes on inside these schemes,
therefore let's delve a little inside it.
Basically, the good old dos function GetSystemDate (21/2A)
works like this: On entry: ah = 2a
On return:
al = day of the week (0 = Sunday, 1 = Monday...)
cx = year
dh = month
dl = day
Short before the 90 days check, the protection calls two
routines:
1:09B4 (GetSystemDate) and 1:0D64 (FetchInstallationCode)
The first one fetches the date (1.9D3-1.9D7) and the Time
(21/2C, at 1.9E2), get's ONCE MORE the system date (1.9F7)
subtracts the years against 1980 (1.A20: sub cx, 07BC) and then
makes quite a lot of maniuplation of these data (around 1.C7D,
where one year LESS than the current year will be stored in
[SI+03], in order to calculate the total amount of days). The
second one prepares the numbers for the sub ax and sbb dx of the
90 days check.
As I said all this does not need to concern you much, coz
the protectionists have mad a 'protecion blunder': they have made
every time snapping depending on a flag, the one in [0632].
What happens is: THE FIRST THING this program makes, smack
at the beginning of WinMain, is to set to zero (FALSE) the
abovementioned flag:
1.1105 C70632060000 mov word ptr [0632], 0000
Only in case of first time use, this flag will be set to TRUE at
1.11F6 C70632060100 mov word ptr [0632], 0001
knowing that, anyway, as soon as the program runs again this flag
will be reset to FALSE by Winmain.
And, as we saw, this flag is checked both for the 90 days snap:
1.1211 833E320600 cmp word ptr [0632], 0000
and for the 'This is your last day Cinderella' Warning:
1.1315 >833E320600 cmp word ptr [0632], 0000
A good fundamental crack will therefore be the 'automatical'
setting to TRUE of this flag by our Winmain:
1.1105 C70632060100 mov word ptr [0632], 0001
Everytime the program runs it will believe that's the first time
it does it.
I know, theoretically, having nooped the increase inside PCF.DLL,
the counter should remain always at 4D, which would set ANEW the
flag to true every run... but we do not want the first 'welcome'
nagscreen either, do we? Therefore:
****** Crack for PCFILE version 8, by +ORC, march 1997 ***
psedit pcf.dll
search 4E 49 44 4D (4D only if you did not run it)
modify in 4E 49 44 50 (second time run)
psedit pcfile.exe
search 83 C4 06 FE 46 FC
modify in 83 C4 06 40 90 48 (nooped increase)
search C7 06 32 06 00 00
modify in C7 06 32 06 01 00 (flag always true)
*********************************************
As second example I have chosen a fairly interesting 'CINDERELLA'
protection scheme of a Window application which can be useful for
our purposes: Link Check (Version 5.1), an application written
in august 1996. I'll crack here the Windows 3.1 version, for
reasons explained in lesson 9.4, but you'll easily find the Win95
version on the net, whose protection scheme works on the same
lines.
Link Check is a suite of three (3) diagnostic programs which
allows the user to examine different areas of the system.
1) Link Check (WLCHECK.EXE) enables the user to view the links
between an executable file and the modules it requires to run on
the system.
2) Memory Check (WMCHECK.EXE) allows the user to view, load and
unload modules currently in memory.
3) Function Check (WFCHECK.EXE) allows the user to view actual
function calls inside modules.
WLCHECK EXE 40400 24/08/96 5:10
WMCHECK EXE 37104 18/08/96 5:10
WFCHECK EXE 45424 24/08/96 5:10
WLCCOMM DLL 46960 18/08/96 5:10
KSLHOOKS DLL 29568 15/08/96 1:00
The protection scheme inside this program allows a 21 days use
of the program, then 'disables' it. Even in the first 21
'allowed' days there are some functions that are disabled,
anyway. Another interesting feature of the protection scheme, is
that once you register, an 'electronic key' will be created and
sended to you in order to unlock Link Check for the full retail
version (which, as usual, means that the shareware version you
are using CAN be unlocked).
Therefore this application:
is TIME-LIMITED
has been CRIPPLED
has some DISABLED functions
can be UNLOCKED.
A wonderful world of cracking possibilities! Let's rub our hands!
So much to find! So much to learn! Thanks, Karri Software Ltd!
(100422.3521@compuserve.com)
For these protection schemes we must use both the 'Winice' live
approach and the 'dead listing' one. (both described elsewhere
in my tutorial).
Let's begin at the beginning, i.e. searching for strings inside
the WLCHECK.EXE we'll find nothing.
You'll soon realise that the protection scheme hides inside the
two *.dll WLCCOMM.DLL & KSLHOOKS.DLL... the real problem, with
this kind of protections, is that the 'modalities' to unlock it
are not known, i.e., that you cannot just crack the unlock
procedure itself, but you must reverse engineer the program long
enough to find the 'switch' that fires your cracked 'unlock'
procedure, in order to 'register' this program and in order to
be able to use it ad libitum.
What happens with time protections?
The first problem for the protectionists is the tampering with
the system date. Even a stupid user could set the system clock
backwards in order to use a program of the CINDERELLA sort.
Your target would be easily fooled by any stupid user if it did
just set a variable [START_DATE] and then simply check the system
time with something like
IF SystemTime > [START_DATE+30] then beggar off
ELSE OK
Therefore (almost) all this program use some sort of 'diode'
location. Like diodes, which let current through in only one
direction, these locations can only grow... i.e, if you set the
system time to 1 January 2000 and then run the program, it will
throw you off, as expected, but even when you go back to your
current year and date this will be 'remembered'...and the
protection will NOT allow you any more to use the program even
should you (theoretically) still have some free 'try me' days...
your setting at year 2000 screwed up your license for ever.
IF SystemTime > [START_DATE+30] then [MARK_HERE]
ELSE continue
If [MARK_HERE] = TRUE then beggar off
ELSE OK
Let's try altering the system date on our WLCHECK.EXE target...
Woa! As I said... it does not work anymore.
It's fairly easy to get at this part through Winice: Just bpx
WritePrivateProfileString (which is a very interesting function
indeed) and then have a good look at the pointers: You'll quick
find out that KSLHOOKS (Segment 0B) writes his own xCLSID value
inside system.ini. The block of KSLHOOKS.DLL's code responsable
for this is the following:
11.0569 9AE4013500 call 7:01E4 ;'Value' and 'SYSTEM.INI'
11.056E 83C408 add sp, 8 ;adjusting stack
11.0571 8D843901 lea ax, [si+0139]
11.0575 57 push di
11.0576 50 push ax ;pushing 'xCLSID'
11.0577 8D46FA lea ax, [bp-06]
11.057A 16 push ss
11.057B 50 push ax ;pushing 'Value'
11.057C 8D468A lea ax, [bp-76]
11.057F 16 push ss
11.0580 50 push ax ;pushing '{6178-0503...}'
11.0581 8D46EE lea ax, [bp-12]
11.0584 16 push ss
11.0585 50 push ax ;pushing 'SYSTEM.INI'
11.0586 9AFFFF0000 call KERNEL.WRITEPRIVATEPROFILESTRING
11.058B 33C0 xor ax, ax
11.058D 5E pop si
11.058E 5F pop di
11.058F C9 leave
11.0590 CB retf
The call to 7.01E4 fetches the strings 'Value' and 'SYSTEM.INI'
which are 'hardwired' there byte by byte, for instance, 'INI' is
fetched like this:
7.0234 26C6440749 mov byte ptr es:[si+07], 49 ;I
7.0239 26C644084E mov byte ptr es:[si+08], 4E ;N
7.023E 26C6440949 mov byte ptr es:[si+09], 49 ;I
What is really interesting in this part of the protection scheme,
is that the function WritePrivateProfileString is one of the MOST
COMMON functions used for this kind of protections, being the
function normally used in order to 'keep track' inside an 'INI'
file of the particular configuration of an application that the
user has chosen... as a matter of fact this program creates an
hidden WLCHECK.SWL file inside c:\windows where it writes its
data, it also writes, through the above code,
[xCLSID]
Value={0000006236-0017105173-6326000000}
inside system.ini
and then it writes ANOTHER string inside the reg.dat 'register'
of the windows directory. A short digression, about registrations
in the reg.dat of the Windows directory. If you never had a look
at the reg.dat file (wich you should not have only firing
regedit.exe, but using the switch /v TROUGH THE COMMAND LINE
run!) you are in for a big surprise. If you are used to install
and de-install programs as much as I do, you'll be able to see,
for instance, real BATTLES between big or widespread software
packages (for instance Coreldraw and PaintShopPro) fought
there... but you'll also find some cryptic messages like
WB_10=VMWB20
FILTER = 000000000e
OPTION = 0000000005
TAG = 0000001857
KEY = 0000184F
or, even more cryptic:
VxDSettings = {0000006178-0419758349-4326000000}
And this is actually our target, as you can see... the first
thing you should know is that some protection schemes hyde the
date checking part of their protection inside reg.dat.
The above value is the 'ID' of our target, and the ciffer in the
'middle' varies with the date and with the passing of the time.
As we said, once the protection snaps, there is no 'normal'
way to reinstall a working copy of the program, even substituting
ALL the files with fresh ones and deleting the 'secret'
WLCHECK.SWL will not help... in order to reinstall this program
or to use it for the eternity (in 21 days chunks) you would have
to do the following every time the limit snaps:
A) regedit /v
delete key VxD
B) edit system.ini
manually delete the block
"[xCLSID]
Value={0000006236-0017105173-6326000000}"
C) attrib c:\windows\wlcheck.swl -r -s -h
del c:\windows\wlcheck.swl
D) reinstall everything anew and run 21 more days... clearly not
a satisfactory solution, exspecially given the fact that some
routines are disabled... therefore let's delve a little more
inside this protection scheme... we'll find a much neater crack,
you'll see... :=)
Since the 'legitimate' user will get 'an electronic key' from the
protectionists, there must exist, somewhere, a small menu of the
kind 'Enter your electronic key, legitimate sucker'... we could
find it searching with a little imagination (and/or zen) inside
our listings, but in these cases, it's much more quicker a small
run with WRT (Windows Resource Toolkit) by borland. Since we are
already inside KSLHOOKS.DLL, let's begin with this one.
Wrt loads kslhooks.dll and shows you immediatly that there are
only three dialog items, the last one, tagged as 'dialog 503'
represents the 'Unlock' little window: ('Please enter your key'),
which has two buttons: OK (1) and Cancel (2). Let's use WRT
'ID_tagging' option: we'll immediatly fetch the ID number of the
'Please enter your key' field: 2035.
2035 dec is 7F3 hex, therefore we now just need to search 07F3
inside our listing... and we land immediatly here:
6.00DE >8B760A mov si, [bp+0A]
6.00E1 FF760E push word ptr [bp+0E]
6.00E4 6A08 push 0008
6.00E6 9AFFFF0000 call USER.GETWINDOWLONG
6.00EB 8946FC mov [bp-04], ax
6.00EE 8956FE mov [bp-02], dx
6.00F1 83FE01 cmp si, 0001
6.00F4 7556 jne 014C
6.00F6 FF760E push word ptr [bp+0E]
6.00F9 68F307 push 07F3 ;HERE! ****
6.00FC 9AFFFF0000 call USER.GETDLGITEM
6.0101 50 push ax
6.0102 8D4698 lea ax, [bp-68]
6.0105 16 push ss
6.0106 50 push ax
6.0107 6A63 push 0063
6.0109 9AFFFF0000 call USER.GETWINDOWTEXT
6.010E 8D4698 lea ax, [bp-68]
6.0111 16 push ss
This block of code is part of an Exported function from
kslhooks.dll: KSLHOOKPROC4 - Ord:0006h
Here is the whole sequence:
:CALL_PLEASE_ENTER_ELECTROKEY
6.00DE >8B760A mov si, [bp+0A]
...
6.00F9 68F307 push 07F3 ;HERE ***
is called (being at 6.00DE) from
:ENTER 68
6.0082 C8680000 enter 0068, 00
...
6.009B 7441 je 00DE ;HERE ***
which (being at 6.00082) is called from
:PUSH_82
6.000F 68FFFF push selector KSLHOOKPROC4
6.0012 688200 push 0082 ;HERE ***
6.0015 FF36200C push word ptr [0C20]
6.0019 9AFFFF0000 call KERNEL.MAKEPROCINSTANCE
Much interesting, but we are not yet there...
let's see if we have other occurrences of our 7F3h instance
(which, as we saw through WRT, corresponds to the 'Enter your
Key' field of the 'Unlock' window). Yes, we have one more
occurrence (always inside KSLHOOKS.DLL):
4.030A >81FEF307 cmp si, 07F3 ;HERE ***
4.030E 7515 jne 0325 ;don't care if not unlock
4.0310 FF760E push word ptr [bp+0E] ;nID
4.0313 56 push si ;=7F3, =unlock, =hDlg
4.0314 9AFFFF0000 call USER.ISDLGBUTTONCHECKED
4.0319 0BC0 or ax, ax ;mashed button?
4.031B 7408 je 0325 ;Yeah, jump...
4.031D C45EFC les bx, [bp-04]
4.0320 2689B7B104 mov es:[bx+04B1], si
4.0325 >83FE02 cmp si, 0002 ;...here
Now, IsDlgButtonChecked is a 'typical' windows function with
following structure:
UINT IsDlgButtonChecked(HWND hFlg, int nID)
where the handle of the dialog box contaning the button control
is specified in hDlg. The ID value of the desired button is
passed in nID. For two-state buttons this function returns zero
if the button is unchecked and non zero if it is checked, -1 if
an error occurs.
What else can we do?
Let's search for the limit (21 days, that corresponds to 15h)
inside our code. Well, we'll find two interesting occurrences
inside the OTHER dll module: WLCCOMM.DLL:
:OCCURRENCE_1_OF_21_DAYS_LIMIT
1.3E25 >80BEFFFE15 cmp byte ptr [bp-0101], 15 ;here***
1.3E2A 7403 je 3E2F ;Please restart...
1.3E2C E9B900 jmp 3EE8 ;xor ax and retf
and now, look what we have immediately afterwards...
1.3E2F >FF760E push word ptr [bp+0E]
1.3E32 1E push ds
1.3E33 681306 push 0613 ;Please restart...
1.3E36 1E push ds
1.3E37 68EE05 push 05EE ;Retail version...
1.3E3A 6A40 push 0040
1.3E3C 9A90080000 call USER.MESSAGEBOX
1.3E41 FF760E push word ptr [bp+0E]
1.3E44 6A01 push 0001
1.3E46 9AE03E0000 call USER.ENDDIALOG
1.3E4B E99A00 jmp 3EE8 ;xor ax and retf
Now, string 0613 is
"Please restart the program for the reatil version to take
effect"
and string 05EE is
"Retail version successfully unlocked"
...clearly we have found the part of the code where the user gets
the appropriate message once he has digited the correct key
inside the unlock window in KSLHOOKS.
But let's use a little more our 'new' WRT approach. Examining the
'dialog' items through WRT, we'll see that inside WLCCOMM.DLL
there are 'two' About Link check templates, a 'nice' one (for
registered users) and a 'nag' one (for Cinderella's users).
The nice one is WLCCOMM.DIALOG 130, and its second part reads
'This copy of Link check is licensed to'
FIELD 1 = 603 (25bh)
FIELD 2 = 604 (25Ch)
The 'nag' one is WLCCOMM.DIALOG 131 and its second part reads
'UNREGISTERED Shareware notice...' with two buttons:
'How do I register' which is 601 (259h) and
What do I get for it which is 602 (25ah).
Well... let's have a look around our code... and here is
(obviously) the relevant part of it inside WLCCOMM.DLL:
1.3C60 >8B760E mov si, [bp+0E]
1.3C63 FF7606 push word ptr [bp+06]
1.3C66 6AF4 push FFF4
1.3C68 9A8A1D0000 call USER.GETWINDOWWORD
1.3C6D 56 push si
1.3C6E 685B02 push 025B ;here***
1.3C71 9A803C0000 call USER.GETDLGITEM
1.3C76 394606 cmp [bp+06], ax
1.3C79 7421 je 3C9C
1.3C7B 56 push si
1.3C7C 685C02 push 025C ;here***
1.3C7F 9ADA3C0000 call USER.GETDLGITEM
1.3C84 394606 cmp [bp+06], ax
1.3C87 7413 je 3C9C
1.3C89 FF760A push word ptr [bp+0A]
1.3C8C FF7608 push word ptr [bp+08]
1.3C8F FF7606 push word ptr [bp+06]
1.3C92 6A01 push 0001
1.3C94 9A08039E3D call KSLCONTROLCOLOR
1.3C99 E94E02 jmp 3EEA
Whereby, here is the part for the shareware user:
1.3EA6 >81FE5902 cmp si, 0259 ;How do I register?
1.3EAA 7513 jne 3EBF
1.3EAC FF760E push word ptr [bp+0E]
1.3EAF 1E push ds
1.3EB0 688B06 push 068B
1.3EB3 6A01 push 0001
1.3EB5 6A00 push 0000
1.3EB7 687217 push 1772
1.3EBA 9AD43E0000 call USER.WINHELP
1.3EBF >81FE5A02 cmp si, 025A ;What do I get for it?
1.3EC3 7523 jne 3EE8
1.3EC5 FF760E push word ptr [bp+0E]
1.3EC8 1E push ds
1.3EC9 689706 push 0697
1.3ECC 6A01 push 0001
1.3ECE 6A00 push 0000
1.3ED0 687117 push 1771
1.3ED3 9AFFFF0000 call USER.WINHELP
1.3ED8 EB0E jmp 3EE8
and as you can easily see, here lays the 'working' for the two
mushbuttons of the shareware version.
Shareware starts at 1.3EA6 and will be called from here
1.3DB9 >81FE5802 cmp si, 0258
1.3DBD 7403 je 3DC2
1.3DBF E9E400 jmp 3EA6
Unlocked version starts at 1.3C60 and will be called from here:
1.3C3E C8FE0400 enter 04FE, 00
1.3C42 57 push di
1.3C43 56 push si
1.3C44 1E push ds
1.3C45 B87938 mov ax, selector 2:0000
1.3C48 8ED8 mov ds, ax
1.3C4A 8B460C mov ax, [bp+0C]
1.3C4D 2D1900 sub ax, 0019
1.3C50 740E je 3C60 ;***here! UNLOCKED
1.3C52 2DF700 sub ax, 00F7
1.3C55 7465 je 3CBC ;copyright, 1st part
1.3C57 48 dec ax
1.3C58 7503 jne 3C5D ;(jmp 3EE8) out
1.3C5A E94901 jmp 3DA6
Well... if [bp+0C] is 19 (dec25) then we'll jump to our unlocked
routine?
********************************************
BELOW IS WORK FROM THE STUDENTS OF THE +HCU
wlcheck for windows 3.1
********************************************
Starting with the nag screen, here is a silly fix for it that works on many programs
that use windows resource windows (such as an about box) as the nag screen.
Load up the file with the nagscreen in it (as listed above) with WRT (I am using
borland resource workshop - same program, different version) and delete it.
I am serious; try it: it works!
save the .DLL and it recompiles the binary without the nagscreen.
(Those borland people scare me sometimes)
---------------------------------------------------------
Back to more serious work...
Since we are learning methods here, this is where I get to go
after individual parts of the protection and defeat them. I will
work on finding the flag to register the program later, first I
want to do a little digging.
Looking through our dead listing of kslhooks.dll:
Going through our lesson so far, our file included a file reference
to SYSTEM.INI by means of a byte-at-a-time string creation rather
than a full data statement
(Note: i do this sometimes to make it hard for simpletons to change
my name in my programming
---but i at lest jumble the lines around so it isnt so obvious)
I will show you two ways of removing this particular hurdle, here is
the first, and most obvious: (THE NULL TERMINATOR)
---------------------------------------------------------
here is the full code from the disassembly:
---------------------------------------------------------
:0007.01E3 90 nop
:0007.01E4 55 push bp
:0007.01E5 8BEC mov bp, sp
:0007.01E7 57 push di
:0007.01E8 56 push si
:0007.01E9 8B7E06 mov di, [bp+06]
:0007.01EC 8B760A mov si, [bp+0A]
:0007.01EF 8E4608 mov es, [bp-08]
:0007.01F2 26C60556 mov byte ptr es:[di], 56 ;V
:0007.01F6 26C6450161 mov byte ptr es:[di+01], 61 ;a
:0007.01FB 26C645026C mov byte ptr es:[di+02], 6C ;l
:0007.0200 26C6450375 mov byte ptr es:[di+03], 75 ;u
:0007.0205 26C6450465 mov byte ptr es:[di+04], 65 ;e
:0007.020A 26C6450500 mov byte ptr es:[di+05], 00 ; <00> (end of string)
:0007.020F 8E460C mov es, [bp-0C]
:0007.0212 26C60453 mov byte ptr es:[si], 53 ;S
:0007.0216 26C6440159 mov byte ptr es:[si+01], 59 ;Y
:0007.021B 26C6440253 mov byte ptr es:[si+02], 53 ;S
:0007.0220 26C6440354 mov byte ptr es:[si+03], 54 ;T
:0007.0225 26C6440445 mov byte ptr es:[si+04], 45 ;E
:0007.022A 26C644054D mov byte ptr es:[si+05], 4D ;M
:0007.022F 26C644062E mov byte ptr es:[si+06], 2E ; .
:0007.0234 26C6440749 mov byte ptr es:[si+07], 49 ;I
:0007.0239 26C644084E mov byte ptr es:[si+08], 4E ;N
:0007.023E 26C6440949 mov byte ptr es:[si+09], 49 ;I
:0007.0243 26C6440A00 mov byte ptr es:[si+0A], 00 ;<00> (end of string)
:0007.0248 5E pop si
:0007.0249 5F pop di
:0007.024A C9 leave
:0007.024B CB retf
---------------------------------------------------------------------------
to me this looks like an easy section to defeat - this because the full
filename is here, and because it is in a standard string format
terminating in hex zero (00) a.k.a.: NULL
as with programs with unencrypted passwords (yes even some programs
you may use.. like X-WING have no encryption whatsoever - just try
scanning FRONTEND.OVL for DANTOOINE with a hex editor, and all the
passwords are sitting there for you to zero-out)
in other words, why bother disabling the function that calls this
data when you can simply change each character in SYSTEM.INI to a
hex zero
i did and just as i suspected, out of the three places that are
causing us hassles, SYSTEM.INI, WLCHECK.SWL, and the registry (REG.DAT)
I no longer have to deal with one of them
just run it, you will see: no more added line in system.ini
NOW FOR THE OTHER WAY:
(This one is more useful for a cracker point of view since good
protections tend to be smarter than letting you view filenames
like we saw above)
This is where we go back to the WRITEPRIVATEPROFILESTRING function
and check it out.
A text search of the dead listing reveals quickly:
:0011.0535 90 nop
:0011.0536 C8760000 enter 0076, 00
:0011.053A 57 push di
:0011.053B 56 push si
:0011.053C 8B7606 mov si, [bp+06]
:0011.053F 33C0 xor ax, ax
:0011.0541 B93200 mov cx, 0032
:0011.0544 8D7E8A lea di, [bp-76]
.
.
.
:0011.0584 16 push ss
:0011.0585 50 push ax
:0011.0586 9AFFFF0000 call KERNEL.WRITEPRIVATEPROFILESTRING
:0011.058B 33C0 xor ax, ax
:0011.058D 5E pop si
:0011.058E 5F pop di
:0011.058F C9 leave
:0011.0590 CB retf
notice the end of the function and how it exits...
5E POP SI
5F POP DI
C9 LEAVE
CB RETF
In order to give the function a little meat to play with but still
return early, lets insert this code right at the start of the function,
right after push si.
:0011.0536 C8760000 enter 0076, 00
:0011.053A 57 push di
:0011.053B 56 push si
:0011.053C 5E pop si
:0011.053D 5F pop di
:0011.053E C9 leave
:0011.053F CB retf
and it really works out, the function gets called, it starts, quits,
and returns... veni, vidi, crakki.
a quick hex edit of the dll to alter this..
searching for a good string to replace, we get 2 occurrances of:
8b760633c0b93200
They are GETPRIVATEPROFILESTRING and WRITEPRIVATEPROFILESTRING respectively.
Go ahead and do the same damage that you did above to both of them. You will notice that
they are very similar functions, with exactly the same method of beginning
and ending.
so we can change both occurrances toto:
5e5fc9cbc0b93200
NOTE:
This patch takes care of the system.ini change, so no need to do a
zero-out of the file.
It didnt do any good for the .swl file however, because does some
other method of storing it's data.
**********************************************
Here is a little clue in how to find the other filenames that may be
hidden in the file.
From the SYSTEM.INI example, which has a period (hex 2E) and a file
extension, i knew to look for ", 2E" (COMMA SPACE 2E) in the text
editor while reading kslhooks.alf
i decided to give it another whirl and see if there were any other
surprises
Just looking for 2E will work, but you will find many occurrances of
it in hex data, so it is best to try to differentiate it as much as
possible for sanity reasons.
Apparently it bears fruit...
--------------------------------------------------------------------------
Here is the first block of code i landed in:
--------------------------------------------------------------------------
:0003.00B0 F3 repz
:0003.00B1 A5 movsw
:0003.00B2 13C9 adc cx, cx
:0003.00B4 F3 repz
:0003.00B5 A4 movsb
:0003.00B6 1F pop ds
:0003.00B7 39460A cmp [bp+0A], ax COMPARE AND JUMP...
:0003.00BA 7516 jne 00D2
:0003.00BC C646FA2E mov byte ptr [bp-06], 2E ; . <----- A .SWL FILENAME EXTENSION
:0003.00C0 C646FB53 mov byte ptr [bp-05], 53 ;S
:0003.00C4 C646FC57 mov byte ptr [bp-04], 57 ;W
:0003.00C8 C646FD4C mov byte ptr [bp-03], 4C ;L
:0003.00CC 8846FE mov [bp-02], al
:0003.00CF EB0E jmp 00DF
:0003.00D1 90 nop
:0003.00D2 8D7EFA lea di, [bp-06]
* Possible StringData Ref from Data Seg 013 ->".LIC" <---- A NEW FILENAME EXTENSION ".LIC"
|
:0003.00D5 BE1800 mov si, 0018
--------------------------------------------------------------------------
what it looks like to me is that WHEN registered, the
.SWL file extension is replaced by a .LIC file extension
though changing the name of the SWL file to LIC
does not seem to have any beneficial result at this time
it may once the other file checks have been disabled
regardless, it is apparent that there is a file with a .LIC
extension that gets created upon successfully registering
this software
--------------------------------------------------------------------------
note that because there appears only once a .SWL reference in
the KSLHOOKS.DLL, my guess is that if i hex-zero the .SWL like i
did the SYSTEM.INI reference, it would not matter because the file
write command and file read command apparently use the same
string for their data. in other words, changing .SWL to anything,
the file would just have a different name.
testing this out, i found that i was correct. hexing out .SWL with
zeroes resulted in a file in my windows directory called WLCHECK
with no extension, rather than .SWL (sometimes it would be nice if
my theories wouldnt be quite so correct)
so we are at least in the ballpark, but no real improvements yet.
It is still going to take more looking to do anything with this yet
----------------------------------------------------------
Now let's try for the registry...
Scan the file for REG, and you will inveitable find quite a few
registry commands. which are registry key edit functions.
:0011.033A C8300100 enter 0130, 00
:0011.033E 57 push di
:0011.033F 56 push si
:0011.0340 8B5E06 mov bx, [bp+06]
:0011.0343 8B4E08 mov cx, [bp+08]
:0011.0346 81C32D01 add bx, 012D
:0011.034A 1E push ds
:0011.034B 8BFB mov di, bx
.
.
:0011.037D 9AFFFF0000 call SHELL.REGCREATEKEY
.
.
and it ends JUST LIKE the previoous functions
with a:
5E POP SI
5F POP DI
C9 LEAVE
CB RETF
so we just hexedit the changes...
2 occurrances of:
010057568b5e068b
(regopenkey and regcreatekey respectively - feel free to
look for yourself in the dead listing)
it is just fine to change both to:
010057565e5fc9cb
and just like in the case of the writeprivateprofilestring,
we have cracked the registry.
NOW -
2 out of 3 of the hoops have been jumped
Now it is time to test the .SWL file and afterwards we will
deal with the NAG feature itself.
Here is where i get curious to see the differences in the
before and after... i want to see exactly what i have left
to conquer, and the resultant file differences in wlcheck.swl
BEFORE the 21 day date expires, and AFTER it expires.
I wrote a program a while back to datecrack stuff like this -
but as we already know, this program is a little smarter than
the average 'check today's date' type of protection.
The way my program (cdate.exe) works is relatively simple:
it alters the system date upon program entry, and changes it
back to normal. The interesting thing about this is that it
allows future dates to be set as well as past ones since i
didnt care to put a block on WHICH dates could be set with it.
Note that this one works fine past midnight because it has a
calendar built in, so if you are to write one yourself
remember that when midnight comes and your calendar strangely
goes off by a day every time you pass midnight while using a
datecracked program.
CDATE USAGE:
cdate mm dd yyyy
So, since all that is left to crack is the swl file, i can
delete it with my handy RM command - which like all of
my little unix tools strips all attribs from the file
(ignores them really)...
rm c:\windows\wlcheck.swl
...And i can run wlcheck.exe again (this time with false
future date)
cdate wlcheck 9 9 1999 (it's now 1997 so this works fine)
note the result: expired program!!!
exit wlcheck and try running it normally (no funky date this time)
guess what... STILL expired.
that means it records not only date info, but EXPIRED info as
well, exactely as +ORC said.
Do the little effect of RM C:\WINDOWS\WLCHECK.SWL again and run
wlcheck
It isn't expired now
That means that the ONLY recorder for 'expired software' is in that
file... doing a little dos file compare between a copy of the swl
file before, and after (the BAD one), here are my results
again i used CP.EXE to copy the swl file since it strips attribs
and i dont have to worry about them now.
-----------------------------------------------------------
C:\WLCHECK\> FC WLCHECK.SWL WLCHECK.BAD
Comparing files wlcheck.swl and wlcheck.bad shows them to be quite
different (you can try this for yourself if you like)
------------------------------------------------------------
There are a few ways we could go about this.
We could either try to make it so the 21 day period cannot
expire, or remove the command that records the info to the
file. In all honesty, we will probably have to do both in
order to deprotect it completely.
**********************************************************
maybe we should do a little windows directory listing just
to see if there are any more surprises
dir /a c:\windows\wlc*
what do we see:
wlcheck.ini, wlcheck.ord, and wlcheck.swl
that is all fine, no more surprises yet (if you didnt expect
a wlcheck.ini file: WHY NOT?
if you edit wlcheck.ord, it is just your order blank from when
you filled out the wlcheck form nothing impressive, but at least
it has the product serial number listed at the bottom - sometimes
handy
*********************************************************
I cannot seem to find any more windows file commands, so i decided
to see if they had included in the dll their own file access commands
that means... look for int21
(there are a TON of int21 calls in this program! - and to think
that some people think that dos cracking is dead...)
these are the KSL file i/o and system functions, with direct
access to hardware through DOS.
upon searching for int21 calls - specifically int21 with ah=2a
b42a (mov ah, 2a) = get system date
I found 3 instances
2 occurrances of:
008C D89045558BEC1E8ED856 B42A
changed to
00CB D89045558BEC1E8ED856 B42A
(CB = retf)
an interesting thing happens, on the first run, it works fine,
writes the .swl file, and goes on it's merry way
on any subsequent runs, it says expired
that tells me that the changes i made, set the date to a nothing value
in the wlcheck.swl file
in easier to understand lingo, i hit the nail on the head.
i found the date checker - in old int21 style.
if you wish to play with this more yourself, go ahead. by all means.
i still havent worried with the 3rd date check, which is the hex string:
9045558BEC1E8ED856 B42A
About this time, I begin to think - maybe there is a better way...
(I have gotten a bit tired of playing around, and I want to fully crack it)
----------------------------------------------------------
Now we get down to the nitty gritty.
The above is necessary work.. handy for other protection schemes.
It is, however, not incredibly useful here in this one. If you run wlcheck
or one of the other executables, you will notice something frustrating:
you cannot print. Only registered users get that option. That means we
either have to crack more functions, like above, or just go ahead and
register the thing and get it over with.
So we shall.
------------------------------------------------------------
Time to go into the WLCCOMM.DLL...
remember how i said +ORC mentioned 2 occurrances of 15 that were interesting?
i search for " 15" (a space in front so it didn't get every 15 in the
wsccomm file listing)
i didnt find what i wanted other than the original one, so i looked for 0015
and i found one that looked promising..
:0001.3F5D C786E7FB1500 mov word ptr [bp-0419], 0015
:0001.3F63 B001 mov al, 01
:0001.3F65 8886CEFB mov [bp+FBCE], al
:0001.3F69 8886E9FB mov [bp+FBE9], al
:0001.3F6D 8886EAFB mov [bp+FBEA], al
:0001.3F71 C68648FC15 mov byte ptr [bp-03B8], 15
(I must remember to check for bgoth from now on)
just below all that, i saw something strange...
several 'set value to 1' - in other words, it looks like we see a bunch
of flags
changing the below statement to 00 returns this error: this is an old
version (and quits) - not extremely useful, but a green light shall we say.
:0001.3F63 B001 mov al, 01
:0001.3F65 8886CEFB mov [bp-0432], al
:0001.3F69 8886E9FB mov [bp-0417], al
:0001.3F6D 8886EAFB mov [bp-0416], al
going further down...
we have a comparison (in the form of an 'OR')
:0001.3F86 9AFFFF0000 call KSLHOOKS.Ord{0038h}
:0001.3F8B 8BF8 mov di, ax <--- backing up ax
This program apparently wants to save whatever came out of the strange
kslhooks call above before making this compare...
:0001.3F8D 0BF8 or di, ax <--- COMPARE BOTH
Note the special nature of this compare.. since both values are
the same, it is basically the same as saying if ax is zero, it
stays zero, if it is not, it becomes a 1 since the result of any
compare is stored in ax
di still has the saved value in it however... for future use by the program
as you will see below, it is flag containing error codes
:0001.3F8F 750F jne 3FA0 <--- FIRST JUMP IF NONZERO
This smells to me like a 'beggar off jerk'...
(i already know what i have here, do you?)
:0001.3F91 B80100 mov ax, 0001 <--- SET A FLAG?!?
This just gets better and better, but i still look before i try anything,
I don't want to jump the gun and assume anything without proof...
:0001.3F94 C45E06 les bx, [bp+06]
:0001.3F97 268987A400 mov es:[bx+00A4], ax
:0001.3F9C E9D600 jmp 4075 <--- 2nd JMP
:0001.3F9F 90 nop
HERE is where jmp 1 takes me...
:0001.3FA0 8B760A mov si, [bp+0A]
:0001.3FA3 83FF0A cmp di, 000A
:0001.3FA6 7510 jne 3FB8
:0001.3FA8 56 push si
:0001.3FA9 1E push ds
* StringData Ref from Data Seg 002 ->"An upgrade is required.
Continuing as shareware only."
|
:0001.3FAA 68CF06 push 06CF
AND WOULD YOU LOOK AT THAT EVIL MESSAGE!
it is very clear that the beggar off guess was correct...
just go down a few lines and you will see ALL SORTS of nasty
error messages, including the shareware expiry message we get
when we try to run after 21 days (note that it would have been
much easier had we scanned the text for keywords like shareware,
reg, exp, or others we could imagine... but that would not work
with all programs, and we are here to learn how to crack ALSO
protections that do not do us the favour of carrying their doom
inside... therefore the approach above is much more solide :-)
paging down a little we see this at the location JMP 2 sent us at 4075...
:0001.4075 1F pop ds
:0001.4076 5E pop si
:0001.4077 5F pop di
:0001.4078 C9 leave
:0001.4079 CA0600 retf 0006
it just quits... but if you remember up above, it set a flag before it
did so!
now how do we get it to ignore those nasty error messages and we ALWAYS
jump to 4075 with the flag set?
looking back at our decision code from above:
:0001.3F86 9AFFFF0000 call KSLHOOKS.Ord{0038h}
:0001.3F8B 8BF8 mov di, ax
:0001.3F8D 0BF8 or di, ax
:0001.3F8F 750F jne 3FA0 <---- evil jump
:0001.3F91 B80100 mov ax, 0001
:0001.3F94 C45E06 les bx, [bp+06]
:0001.3F97 268987A400 mov es:[bx+00A4], ax
:0001.3F9C E9D600 jmp 4075 <---- good jump
notice the jne? there are quite a few ways of attacking this, but think
about it, there are a few things that must be done.
first, the jne could be changed to a je (or jz) but if we do that, we
have to WAIT 21 days to be able to use the program, or screw up the date
at install, or something dumb like that (not a good crack)
if AX is set to anything, it is deemed an error by the program and the error
code is saved in DI. So we need to make sure ax is zero, and it might be
smart to cover our bases and set di to zero as well (you never know if some
value had been sitting in it to be confused as an error for our crazy
program wlcheck to find and complain about)
so if we set both to zero, then the jne CANNOT ever jump out and we stay long
enough for us to set the AX flag and go along happily.
it just so happens that there is a simple way to set any variable to zero
(if you are familiar with assembly, ignore this, i am putting this in
here for those who havent become as familiar with it as the rest of us -
this is a tutorial after all isnt it?)
xor ax, ax <--- sets ax to zero
xor di, di <--- sets di to zero
if you are lazy like i am, you can search your dead listing for both
(the listing is so large, that you can probably find examples of many byte
values that you need)
it turns out that the values are:
33 C0 xor ax, ax
33 FF xor di, di
and here's how our code will look:
:0001.3F86 9AFFFF0000 call KSLHOOKS.Ord{0038h}
:0001.3F8B 33C0 xor ax, ax
:0001.3F8D 33FF xor di, di
:0001.3F8F 750F jne 3FA0
:0001.3F91 B80100 mov ax, 0001
:0001.3F94 C45E06 les bx, [bp+06]
:0001.3F97 268987A400 mov es:[bx+00A4], ax
:0001.3F9C E9D600 jmp 4075
simply enough, now we just need to make the changes in the wlccomm.dll
*****************************************************
Crack for 16-bit wlcheck by +gthorne of the +HCU:
pop into your favorite hex editor and load WLCCOMM.DLL
(File Size: 46,960 bytes)
search for byte pattern:
8BF80BF8750F
replace with:
33C033FF750F
and run it...
it is registered!
----------------------------------------------------------
Note for showoffs:
If you wish it to say that it is registered to you, go to
the about box, and run the "registration" part of the program
BEFORE you crack it, entering data in the order form as you
want it to be registered.
The target stores this info in the windows directory, in
the file: WLCHECK.ORD.
After cracking, that info is displayed proudly in the about box.
*****************************************************
None of the many changes listed at the beginning of this
section are necessary now, not since we have a good, clean
crack.
Don't disregard the work though, some programs I've seen are
defeatable with the kind of work done before the register flag
was found.
If this were a program with no flag to register, it would have
REQUIRED all that work anyway, and then some.
********************************************
wlcheck for windows 95
********************************************
Ok, building on my fellow +cracker's good work it was
pretty easy to defeat the Win'95 protection, which follows
the same lines as the 16 bit one above... I lost
some time on a stupid beta version of wlcheck for win 95,
that I had inside my collection though... how stupid.
This will teach me to ALWAYS work methodically.
Therefore:
FIRST OF ALL
perform an archie or ftp search for wlck95, you'll find a
whole bunch of servers carrying it, choose a ftp-server
near you and get it ftpmailed to you or download it (as
you prefer).
You'll soon find all the relevant data:
WLCHK955.ZIP 213.156 bytes
SECOND
You have it, unzip it and examine it:
FILE_ID DIZ 438 23/08/96 5:10 FILE_ID.DIZ
WLCHK95 EXE 70.656 23/08/96 5:10 WLCHK95.EXE
KSLHKS95 DLL 52.224 21/08/96 1:00 KSLHKS95.DLL
WMCHK95 EXE 63.488 23/08/96 5:10 WMCHK95.EXE
WLCHK95 HLP 33.759 23/08/96 5:10 WLCHK95.HLP
WFCHK95 EXE 77.824 23/08/96 5:10 WFCHK95.EXE
WMCHK95 HLP 32.463 23/08/96 5:10 WMCHK95.HLP
WFCHK95 HLP 29.696 23/08/96 5:10 WFCHK95.HLP
README TXT 8.689 23/08/96 5:10 README.TXT
WLCCOM95 DLL 73.216 26/03/97 20:11 WLCCOM95.DLL
(ignore the date of the last dll, that's just because I tampered
with it yesterday).
THIRD
Using what we have learned (quite a lot) let's work on
wlccom95.dll: here the relevant part of the dead listing:
* Referenced by a Jump at Address:|:1C005B50(C)
|
:1C005B76 C685C3FBFFFF05 mov byte ptr [ebp+FFFFFBC3], 05
:1C005B7D C685C4FBFFFF01 mov byte ptr [ebp+FFFFFBC4], 01
:1C005B84 66C785DDFBFFFF1500 mov word ptr [ebp+FFFFFBDD], 0015
:1C005B8D C685DFFBFFFF01 mov byte ptr [ebp+FFFFFBDF], 01
:1C005B94 C685E0FBFFFF01 mov byte ptr [ebp+FFFFFBE0], 01
:1C005B9B C6853EFCFFFF15 mov byte ptr [ebp+FFFFFC3E], 15
:1C005BA2 8B450C mov eax, [ebp+0C]
:1C005BA5 66C780A80000000000 mov word ptr [ebx+000000A8], 0000
:1C005BAE 8D8598FAFFFF lea eax, [ebp+FFFFFA98]
:1C005BB4 50 push eax
* Reference To: kslhks95._KslHookProc1@4, Ord:0000h
|
:1C005BB5 E872420000 Call 1C009E2C
:1C005BBA 66894598 mov [ebp-68], ax
:1C005BBE 0FBF4598 movsx word ptr eax, [ebp-68]
:1C005BC2 85C0 test eax, eax
:1C005BC4 0F8516000000 jne 1C005BE0
:1C005BCA 8B450C mov eax, [ebp+0C]
:1C005BCD 66C780A80000000100 mov word ptr [ebx+000000A8], 0001
:1C005BD6 B801000000 mov eax, 00000001
:1C005BDB E946010000 jmp 1C005D26
* Referenced by a Jump at Address: |:1C005BC4(C)
|
:1C005BE0 0FBF4598 movsx word ptr eax, [ebp-68]
:1C005BE4 83F80A cmp eax, 0000000A
:1C005BE7 0F8516000000 jne 1C005C03
:1C005BED 6A40 push 00000040
* Possible StringData Ref from Data Obj ->"License Expired"
|
:1C005BEF 6828E8001C push 1C00E828
* Possible StringData Ref from Data Obj ->"An upgrade is required. Continuing "
->"as shareware only."
|
:1C005BF4 6838E8001C push 1C00E838
:1C005BF9 8B4508 mov eax, [ebp+08]
:1C005BFC 50 push eax
* Reference To: USER32.MessageBoxA, Ord:0188h
|
:1C005BFD FF151C04011C Call dword ptr [1C01041C]
* Referenced by a Jump at Address:|:1C005BE7(C)
|
:1C005C03 0FBF4598 movsx word ptr eax, [ebp-68]
:1C005C07 83F807 cmp eax, 00000007
:1C005C0A 0F8516000000 jne 1C005C26
:1C005C10 6A40 push 00000040
* Possible StringData Ref from Data Obj ->"License Violated"
|
:1C005C12 6870E8001C push 1C00E870
* Possible StringData Ref from Data Obj ->"The license file has been changed. "
->"Continuing as shareware only."
|
:1C005C17 6884E8001C push 1C00E884
:1C005C1C 8B4508 mov eax, [ebp+08]
:1C005C1F 50 push eax
* Reference To: USER32.MessageBoxA, Ord:0188h
|
:1C005C20 FF151C04011C Call dword ptr [1C01041C]
* Referenced by a Jump at Address:|:1C005C0A(C)
|
:1C005C26 0FBF4598 movsx word ptr eax, [ebp-68]
:1C005C2A 83F808 cmp eax, 00000008
:1C005C2D 0F8516000000 jne 1C005C49
:1C005C33 6A40 push 00000040
* Possible StringData Ref from Data Obj ->"License Violated"
|
:1C005C35 68C8E8001C push 1C00E8C8
* Possible StringData Ref from Data Obj ->"This seems to be an unlicensed "
->"copy. Continuing as shareware "
->"only."
|
:1C005C3A 68DCE8001C push 1C00E8DC
:1C005C3F 8B4508 mov eax, [ebp+08]
:1C005C42 50 push eax
* Reference To: USER32.MessageBoxA, Ord:0188h
|
:1C005C43 FF151C04011C Call dword ptr [1C01041C]
* Referenced by a Jump at Address:|:1C005C2D(C)
|
:1C005C49 8D8598FAFFFF lea eax, [ebp+FFFFFA98]
:1C005C4F 50 push eax
* Reference To: kslhks95._KslHookProc2@4, Ord:0001h
|
:1C005C50 E8D1410000 Call 1C009E26
:1C005C55 66894598 mov [ebp-68], ax
:1C005C59 33C0 xor eax, eax
:1C005C5B 8A853EFCFFFF mov al , [ebp+FFFFFC3E]
:1C005C61 83F80D cmp eax, 0000000D
:1C005C64 0F8536000000 jne 1C005CA0
* Possible StringData Ref from Data Obj ->"Link Check evaluation license "
->"has expired."
|
:1C005C6A 6820E9001C push 1C00E920
:1C005C6F 8D459C lea eax, [ebp-64]
:1C005C72 50 push eax
:1C005C73 E871030000 call 1C005FE9
:1C005C78 83C408 add esp, 00000008
:1C005C7B 6A10 push 00000010
* Possible StringData Ref from Data Obj ->"License Expiry"
|
:1C005C7D 684CE9001C push 1C00E94C
:1C005C82 8D459C lea eax, [ebp-64]
:1C005C85 50 push eax
:1C005C86 8B4508 mov eax, [ebp+08]
:1C005C89 50 push eax
* Reference To: USER32.MessageBoxA, Ord:0188h
|
:1C005C8A FF151C04011C Call dword ptr [1C01041C]
:1C005C90 8B450C mov eax, [ebp+0C]
:1C005C93 50 push eax
:1C005C94 E857E3FFFF call 1C003FF0
:1C005C99 33C0 xor eax, eax
:1C005C9B E986000000 jmp 1C005D26
* Referenced by a Jump at Address:|:1C005C64(C)
|
:1C005CA0 0FBF4598 movsx word ptr eax, [ebp-68]
:1C005CA4 83F804 cmp eax, 00000004
:1C005CA7 0F8536000000 jne 1C005CE3
* Possible StringData Ref from Data Obj ->"This is an old version,"
|
:1C005CAD 685CE9001C push 1C00E95C
:1C005CB2 8D459C lea eax, [ebp-64]
:1C005CB5 50 push eax
:1C005CB6 E82E030000 call 1C005FE9
:1C005CBB 83C408 add esp, 00000008
:1C005CBE 6A10 push 00000010
* Possible StringData Ref from Data Obj ->"License Violation"
|
:1C005CC0 6874E9001C push 1C00E974
:1C005CC5 8D459C lea eax, [ebp-64]
:1C005CC8 50 push eax
:1C005CC9 8B4508 mov eax, [ebp+08]
:1C005CCC 50 push eax
* Reference To: USER32.MessageBoxA, Ord:0188h
|
:1C005CCD FF151C04011C Call dword ptr [1C01041C]
:1C005CD3 8B450C mov eax, [ebp+0C]
:1C005CD6 50 push eax
:1C005CD7 E814E3FFFF call 1C003FF0
:1C005CDC 33C0 xor eax, eax
:1C005CDE E943000000 jmp 1C005D26
* Referenced by a Jump at Address:|:1C005CA7(C)
|
:1C005CE3 0FBF4598 movsx word ptr eax, [ebp-68]
:1C005CE7 85C0 test eax, eax
:1C005CE9 0F8D2D000000 jnl 1C005D1C
* Possible StringData Ref from Data Obj ->"An unexpected error has occurred."
|
:1C005CEF 6888E9001C push 1C00E988
:1C005CF4 8D459C lea eax, [ebp-64]
:1C005CF7 50 push eax
:1C005CF8 E8EC020000 call 1C005FE9
:1C005CFD 83C408 add esp, 00000008
:1C005D00 6A10 push 00000010
* Possible StringData Ref from Data Obj ->"System Error"
|
:1C005D02 68ACE9001C push 1C00E9AC
:1C005D07 8D459C lea eax, [ebp-64]
:1C005D0A 50 push eax
:1C005D0B 8B4508 mov eax, [ebp+08]
:1C005D0E 50 push eax
* Reference To: USER32.MessageBoxA, Ord:0188h
|
:1C005D0F FF151C04011C Call dword ptr [1C01041C]
:1C005D15 33C0 xor eax, eax
:1C005D17 E90A000000 jmp 1C005D26
* Referenced by a Jump at Address:|:1C005CE9(C)
|
:1C005D1C B801000000 mov eax, 00000001
:1C005D21 E900000000 jmp 1C005D26
* Referenced by a Jump at Addresses:|:1C005BDB(U),
:1C005C9B(U), :1C005CDE(U), :1C005D17(U), :1C005D21(U)
|
:1C005D26 5F pop edi
:1C005D27 5E pop esi
:1C005D28 5B pop ebx
:1C005D29 C9 leave
:1C005D2A C20800 ret 0008
*******************************************
OK! let's crack...
Well it's all pretty obvious:
After having prepared the call with a lot of parameters
:1C005B76 C685C3FBFFFF05 mov byte ptr [ebp+FFFFFBC3], 05
:1C005B7D C685C4FBFFFF01 mov byte ptr [ebp+FFFFFBC4], 01
:1C005B84 66C785DDFBFFFF1500 mov word ptr [ebp+FFFFFBDD], 0015
:1C005B8D C685DFFBFFFF01 mov byte ptr [ebp+FFFFFBDF], 01
:1C005B94 C685E0FBFFFF01 mov byte ptr [ebp+FFFFFBE0], 01
:1C005B9B C6853EFCFFFF15 mov byte ptr [ebp+FFFFFC3E], 15
note the two x15 parameters... that will of course be the 21
days limit... well, our target calls the kslhks95._KslHookProc1@4
function with all its params and upon return the 32 bit version
uses the SAME protection scheme used in the 16 bit one: it has an
"evil" and a "good" jump:
* Reference To: kslhks95._KslHookProc1@4, Ord:0000h
|
:1C005BB5 E872420000 Call 1C009E2C
:1C005BBA 66894598 mov [ebp-68], ax
:1C005BBE 0FBF4598 movsx word ptr eax, [ebp-68]
:1C005BC2 85C0 test eax, eax ;is it zero?
:1C005BC4 0F8516000000 EVIL jne 1C005BE0 ;not 0: bagger off
:1C005BCA 8B450C mov eax, [ebp+0C]
:1C005BCD 66C780A80000000100 mov word ptr [ebx+000000A8], 0001 ;OK, guy
:1C005BD6 B801000000 mov eax, 00000001 ;eat another good flag
:1C005BDB E946010000 HOLY jmp 1C005D26 ;and be happy for ever
if you throw another look at the listing you'll see all the nasty
messages following the evil jump
* Referenced by a Jump at Address: |:1C005BC4(C)
|
:1C005BE0 0FBF4598 movsx word ptr eax, [ebp-68]
:1C005BE4 83F80A cmp eax, 0000000A
and the subsequent compare eax ARE intersting, they give
you an exact look upon the inner working of our target:
here
eax=A means "License expired"
eax=7 means "License violated" (changed)
eax=8 means "License violated" (unlicensed)
eax=4 means "old version" etcetera...
as a matter of fact it may well be that the crack we
made goes crazy after 21 days (it won't if you push
the date around, we checked) use... in that case it
will be only a question of "fine tuning" of this crack,
and you already know where the relevant protection scheme
dwells... We do not want to wait 21 days just to be
absolutely sure that the crack works perfectly... so it
seems, and so it should be... should it have another
check somewhere (that I do not see now), I promise you
that you'll find the crack for it in three weeks time,
but I'm pretty sure you will not need it :-)
Well, we learned a lot:
Time/Disabling protections may vary a lot, but even in
apparently very complicated schemes (like the wlcheck one),
wich do tamper with a lot of more or less hidden files,
there can be a very simple "hollow" point, where you
can cut mustard with a neat targeted crack... you need to
understand and to "feel" a little the program, though, and
I'm now beginning to understand what +ORC means with his
zen mystique of "feeling" the code.
So here is the simple crack for wlcheck 32 bits:
search for
:1C005BC2 85C0 test eax, eax
:1C005BC4 0F8516000000 jne 1C005BE0
:1C005BCA 8B450C mov eax, [ebp+0C]
:1C005BCD 66C780A80000000100 mov word ptr [ebx+000000A8], 0001
85C00F8516000000
and at the third occurrence of it
(well, if you want -instead of searching the third occurrence of that
string- to type a long string... then search directly for the whole set
85C00F85160000008B450C66C780A80000000100) do as you like, as far as
you land where you should:
:1C005BC2 85C0 test eax, eax
:1C005BC4 0F8516000000 jne 1C005BE0
it's the time to crack your target! Noop the first 8 bytes out,
that is from 85C0 until the three subsequent zeros of instruction
:1C005BC2 ... you may even use the nop=90x instruction like the
lamers if you fancy... here there is absolutely no checking-protection
that examine eventual patchings... noop as you like.
***************************************
Thinking about it we believe that the aim of this first lesson of
the "4" series from +ORC was the following: +he found an apparently
overcomplicated protection only to show us that, hidden behind
everything, a single neat crack was needed... as the fellow +cracker
of the 16 bit version observed, +he gave us a single (but decisive)
hint: he spoke about the second occurrence of the 15x byte, which
proved decisive -as you already did read- in individuating the
"hollow" point of our target.
As this lesson 4.1 was intended as second "+HCU" lesson, we believe
(and hope) that in finding the neat cracks for the 16 and the 32 bit
versions of wlcheck (which is a damn useful program in our trade, btw)
we have accomplished our task.
Now a question arises:
Should really all time protections be variations of this scheme?
(we do not know... we are awaiting the next "4" lesson of +ORC
like everybody else). In that case there is not a single program
(now) able to elude us :-)
Another system: inside win.ini:
[License]
Installed=854824551
Expires=857416551
LastUsed=854824717
i.e. calculated in seconds,
Where 30 days allowance is 857416551 - 854824551 = 2592000
2592000/30 = 86400 (one day)
86400/24 = 3600 (one hour)
3600/60 = 60 (one minute)
Well, that's it for this lesson, reader. Not all lessons of my
tutorial are -or will be- on the Web.
You'll obtain the missing lessons IF AND ONLY IF you mail
me back (via anon.penet.fi) with some tricks of the trade I may
not know that YOU discovered. Mostly I'll actually know them
already, but if they are really new you'll be given full credit,
and even if they are not, should I judge that you "rediscovered"
them with your work, or that you actually did good work on them,
I'll send you the remaining lessons nevertheless. Your
suggestions and critics on the whole crap I wrote are also
welcomed. Do not annoy me with requests for warez, everything is
on the Web, learn how to search, for Jimmy Olden sake.
"If you give a man a crack he'll be hungry again
tomorrow, but if you teach him how to crack, he'll
never be hungry again"
+ORC na526164@anon.penet.fi
HOW TO CRACK, by +ORC, A TUTORIAL
Lesson 5.1: Disk & CD-Rom access (basics)
LESSON 5 (1) - HOW TO CRACK, HANDS ON - Disk/CDROM access (plus
bypasses "on the fly")
Somewhere I have to put the bypasses (loader programs) in this
tutorial, allow me to put them here:
Preparing a loader to bypass a protection [MARIO ANDRETTI]
At time the protectionists hook vectors in order to impose
a particular protection. In this (and similar) cases a good
crack-way is to prepare a "loader" program, that "de-hooks" the
vector used for the protection. This kind of crack can be used
also for internet cracking (on some firewall configurations, see
lesson A.2).
As example let's take "Mario andretti racing challenge", a
stupid game that uses the SAME (!) protection scheme you'll still
find to day on some access routines of military servers around
the witlessly called "free" world.
In order to crack this cram you would prepare a loader on the
following lines:
loc code instruction what's going on
-------------------------------------------------------
:0100 EB44 JMP 0146
...
:0142 0000 <- storing for offset of INT_21
:0144 5887 <- storing for segment of INT_21
:0146 FA CLI
:0147 0E PUSH CS
:0148 1F POP DS
:0149 BCB403 MOV SP,03B4
:014C FB STI
:014D 8C1EA901 MOV [01A9],DS <- save DS
:0151 8C1EAD01 MOV [01AD],DS three
:0155 8C1EB101 MOV [01B1],DS times
:0159 B82135 MOV AX,3521 <- get INT_21
:015C CD21 INT 21 in ES:BX
:015E 891E4201 MOV [0142],BX <- store offset
:0162 8C064401 MOV [0144],ES <- store segment
:0166 BA0201 MOV DX,0102
:0169 B82125 MOV AX,2521 <- set INT_21 to
:016C CD21 INT 21 DS:0102
:016E 0E PUSH CS
:016F 07 POP ES <- ES= current CS
:0170 BBB403 MOV BX,03B4
:0173 83C30F ADD BX,+0F
:0176 B104 MOV CL,04
:0178 D3EB SHR BX,CL <- BX= 3C
:017A B8004A MOV AX,4A00 <- Modify memory block
:017D CD21 INT 21 to 3C paragraphs
:017F BA9E01 MOV DX,019E <- ds:dx=program name
:0182 BBA501 MOV BX,01A5 <- es:bx = param. block
:0185 B8004B MOV AX,4B00 <- load ma.com
:0188 CD21 INT 21
:018A 2E8B164201 MOV DX,CS:[0142] <- reset old int_21
:018F 2E8E1E4401 MOV DS,CS:[0144]
:0194 B82125 MOV AX,2521
:0197 CD21 INT 21
:0199 B8004C MOV AX,4C00 <- terminate with return
:019C CD21 INT 21 code
:019E 6D612E636F6D00 "ma.com"
0000 fence
:01A7 B2015887
:01AB B2015887
:O1AF B2015887
0000 fence
let's now prepare a routine that hooks INT_21:
push all
CMP AX,2500 <- go on if INT_21 service 25
JNZ ret
CMP Word Ptr [0065], C00B <- go on if location 65 = C00B
JNZ ret
MOV Byte Ptr [0060], EB <- crack instructions
MOV Byte Ptr [0061], 3C
MOV Byte Ptr [0062], 40 <- INC AX
MOV Byte Ptr [0063], 90 <- NOP
MOV Byte Ptr [0064], 48 <- DEC AX
pop all
JMP FAR CS:[0142] <- JMP previous INT_21
From now on this loader will work every time that a program
with location [0065] containing an 0R AX,AX instruction (0BC0:
it's the case of ma.com) calls INT_21 service 25 (hook a vector),
the target program will be modified on the fly and will get, at
location [0060], the instruction JMP 3C locations ahead, despite
the fact that it has routines capable of self checking in order
to make sure it has not been modified.
The most important thing is the routine that YOU write that
will precede the call to INT_21 (or any other INT) service 25 (or
any other service) in order to crack on the fly the offending
program. I'll show you another one, this one for [Reach for the
skies] (reach.com):
push all
CMP AH,3D <- is it service 3D? (open file)
JNZ ret <- no, so ret
CMP DX,13CE <- you wanna open file at 13CE?
JNZ ret <- no, so ret
MOV AX,[BP+04] <- in this case
MOV DS,AX
CMP Byte Ptr [B6DA],74 <- old instructions
JNZ 015B
CMP Byte Ptr [B6DB],0F <- ditto
JNZ 015B
CMP Byte Ptr [B6DC],80 <- ditto, now we now where we are
JNZ 015B
MOV Byte Ptr [B6DA],EB <- crack
MOV Byte Ptr [B697],40 <- camouflaged no-opping
MOV Byte Ptr [B698],48 <- cam nop
MOV Byte Ptr [B699],90 <- cam nop
MOV Byte Ptr [B69A],40 <- cam nop
MOV Byte Ptr [B69B],48 <- cam nop
MOV DX,CS:[0165]
MOV DS,CS:[0167]
MOV AX,2521 <- set hook
INT 21
POP all
JMP FAR CS:[0165]
Here you did change the instruction 740F in the instruction EB0F,
and you did "noop" the instructions at B697-B69B. (Well, more
elegantly than "noop" them with "90" bytes, you choose a INC AX,
DEC AX, NOP, INC AX, DEC AX sequence instead! There are sound
reasons to use a sequence of "working" instructions instead of
NOPs: recent protection schemes "smell" patched nops inside the
program and trash everything if they find more than -say- three
consecutive NOPs! You should always try to choose THE LESS
INTRUSIVE and MORE "CAMOUFLAGED" solution when you crack!)
You can apply this kind of crack, on the same lines, to many
programs that perform self checking of the code and hook the
vectors.
REAL DISK ACCESS STUFF
Now we may come to the subject of this lesson:
As usual, let's begin from the beginning: history is always
the key that allows an understanding of present and future, in
cracking matters too. As the older 5 1/4 inch big black floppy
disks were still used (the 320K/8 tracks or 360K/9 tracks ones,
that were really "floppy" and have nowadays almost disappeared)
one of the more common methods to protect a program, was to
format the "master" (key) disk in a weird way. Old floppy disk
for the PC did usually store 360K at 9 sectors per track.
Some basics for those of you that do not know anything: in
order to defeat this kind of cracks you need to know two things:
the floppy disk parameter block (FDPB) and the interrupt routines
dealing with format/read disk (basically INT_13).
Most often, the protection scheme is to either format one
or more sectors or tracks with sector sizes other than the
standard 512 bytes, or to either give one of the sectors a wild
sector number like 211 or just not format a whole track of
eight/nine/15 sectors. If you, for instance, have got the same
(very old) copy of VisiCalc master I do, you'll find that sector
8 on track 39 is missing entirely. The interrogation with
assembly or with an "ad hoc" utility (I use the tools I wrote
myself, but you 'll be able to find many such utilities in public
domain, the oldest one, from 1984 (!) being the seasoned [U-ZAP]
an "Ultra utility" from the "Freesoft company") will tell you
which sector numbers were altered, their size in bytes, and if
they were formatted with a CRC error (another not so fancy
trick).
The floppy disk parameters are stored in the BIOS: interrupt
vector 1E contains the address of the floppy disk parameter
block. The FDPB's contents are the following:
Offset Function crackworthy? Example
0 Step rate & head unload no DF
1 head load time no 02
2 Motor on delay no 25
3 Number of bytes per sector yes 02
4 Last sector number yes 12
5 Gap length yes 1B
6 Data track length yes FF
7 Format gap length yes 54
8 Format byte no F6
9 Head settle time no 0F
A Motor start time no 02
0) Offset #0: the left "nybble" (single digit) of this value
is the step rate time for the disk drive head. The right
nybble is the disk head unload time. These values are best
left alone.
1) Offset #1: again, don't fool around with these values. The
left nybble is the disk head load time, and the right
nybble is the direct memory access mode select.
2) Wait time until motor is turned off. Not normally of use.
3) Bytes-per-sector value: AH-HAH! If you place a "0" in this
value, the PC expects all sectors to be 128 bytes long. A
"1" means a sector size of 256 bytes, a "2" means 512
bytes (this is the standard DOS value), and a "3" means
1024 bytes per sector.
4) Highest sector number on a track: this is used for
formatting and tells DOS how many sectors there are on each
track.
5) Gap length for diskette reads: this is what you fool around
with if you keep getting CRC errors when you try to read a
non-standard size sector. Normally, you can just leave this
alone except when formatting with a U-Format tool.
6) Data length: This contains the number of bytes in a sector
when the value in table byte #4 doesn't contain a 0, 1, 2,
or 3.
7) Number of bytes in the gap between sectors: this is also
only used when formatting special tracks.
8) Format fill byte: When formatting, this is the
initialization byte that will be placed in all new sectors.
9) Head settle time: leave this alone.
A) Motor start time: don't fool with this either.
In order to modify globally the number of tracks on a given disk
and the number of sectors per track you can always format with
the DOS command switches "/t:" and "/n:"
FORMAT /t:tracks /n:sectors
If you want to find out what the existing parameters are,
run [Debug.exe] or [Symdeb.exe] and enter the following commands:
- d 0:78 l 4 <- get FDPB address
0000:0070 22 05 00 <- debugger's likely response
- d 0:522 l a <- get 10 FDPB values
0000:520 DF 02 25 02 12 1B FF... <- see preceding table
Remember that all standard disk formats under DOS support
a sector size of 512 bytes, therefore, for one-sided 5.25 inch
floppies:
40t*8s*512b=163.840 bytes (160Kb)
40t*9s*512b=184.320 bytes (180Kb)
and for two-sided 5.25 inch floppies:
40t*8s*512b*2sides=327.680 bytes (320Kb)
40t*9s*512b*2sides=368.640 bytes (360Kb)
Beginning with DOS version 3.0 (Yeah, more and more
history!) a new floppy disk format has been supported: The IBM
AT (80286 CPU) introduced the so called "high capacity" 5.25 u-
inch floppy, capable of storing 1.2M at 15 sectors per track:
80t*15s*512b*2sides=1.228.800 bytes (1.2Mb)
Later on were introduced the to-day universally used 3.5
inch floppies, the ones inside a rigid small plastic cartridge,
and we have, similarly:
3.5-inch double sided/double density 720K
3.5-inch double sided/quad density (HD) 1440K
3.5-inch double sided/high density 2880K
[INT_13, AH=18, Set media type for format]
In order to create weird layouts, the protectionists use
interrupt 13h, service 18h, that specifies to the formatting
routines the number of tracks and sectors per track to be placed
on the media:
* Registers on entry: AH=18h; CH=N&#248; of tracks; CL= Sectors
per track; DL= Drive number (A=0; B=1;C=2... bit 7 is set
if the drive is an hard disk)
* Registers on Return: DI: Offset address of 11-byte
parameter table; ES: Segment address of 11-byte parameter
table.
[INT_13, AH=2, Read disk sectors]
In order to read them, they have to use INT_13, service 2, read
disk sectors, with following layout:
* Registers on entry: AH=2h; AL= N&#248; of sectors; BX= Offset
address of data buffer; CH=track; CL= Sector; DH= Head
(side) number; DL= Drive number; ES: Segment address of
data buffer.
* Registers on Return: AH= return code. If the carry flag is
not set, AH=0, therefore the weird sector has been read, if
on the contrary the carry flag is set, AH reports the
status byte as follows:
76543210 HEX DEC Meaning
1 80h 128 Time out - drive crazy
1 40h 064 Seek failure, could not move to track
1 20h 032 Controller kaputt
1 10h 016 Bad CRC on disk read
1 09h 009 DMA error - 64K boundary crossed
1 08h 008 DMA overrun
1 04h 004 Bad sector - sector not found
11 03h 003 Write protect!
1 02h 002 Bad sector ID (address mark
1 01h 001 Bad command
[Return code AH=9: DMA boundary error]
One of the possible errors should be explained, coz it is
used in some protection schemes: AH=9 DMA boundary error, means
that an illegal boundary was crossed when the in formation was
placed into RAM. DMA (Direct memory access) is used by the disk
service routines to place information into RAM. If a memory
offset address ending in three zeros (ES:1000, ES: 2000...) falls
in the middle of the area being overlaid by a sector, this error
will occur.
[INT_13, AH=4 Verify disk sectors]
Another possible protection interrupt is interrupt 13H,
service 4, Verify disk sectors. Disk verification takes place on
the disk and DOES NOT involve verification of the data on the
disk against data in memory! This function has no buffer
specification, does not read or write a disk: it causes the
system to read the data in the designated sector or sectors and
to check its computed cyclic redundancy check (CRC) against data
stored on the disk. See INT_13, AH=2 registers and error report.
[CRC]
The CRC is a checksum, that detects general errors. When a
sector is written to disk, an original CRC is calculated AND
WRITTEN ALONG with the sector data. The verification service
reads the sector, recalculates the CRC, and compares the
recalculated CRC with the original CRC.
We saw that some protection schemes attempt to disguise
interrupt calls. This is particularly frequent in the disk access
protection schemes that utilize INT_13 (the "disk" interrupt).
If you are attempting to crack such programs, the usual
course of action is to search for occurrences of "CD13", which
is machine language for interrupt 13. One way or another, the
protection scheme has to use this interrupt to check for the
special sectors of the disk. If you examine a cross section of
the program, however, you'll find programs which do not have
"CD13" in their machine code, but which clearly are checking the
key disk for weird sectors. How comez?
There are several techniques which can be used to camouflage
the protection scheme from our nice prying eyes. I'll describe
here the three such techniques that are more frequent:
1) The following section of code is equivalent to issuing an
INT 13 command to read one sector from drive A, side 0, track
29h, sector ffh, and then checking for a status code of 10h:
cs:1000 MOV AH,02 ;read operation
cs:1002 MOV AL,01 ;1 sector to read
cs:1004 MOV CH,29 ;track 29h
cs:1006 MOV CL,FF ;sector ffh
cs:1008 MOV DX,0000 ;side 0, drive A
cs:100B XOR BX,BX ;move 0...
cs:100D MOV DS,BX ;...to DS register
cs:100F PUSHF ;pusha flags
cs:1010 PUSH CS ;pusha CX
cs:1011 CALL 1100 ;push address for next
instruction onto stack and branch
cs:1014 COMP AH,10 ;check CRC error
cs:1017 ... rest of verification code
...
...
cs:1100 PUSHF ;pusha flags
cs:1101 MOV BX,004C ;address of INT_13 vector
cs:1104 PUSH [BX+02] ;push CS of INT_13 routine
cs:1107 PUSH [BX] ;push IP of INT_13 routine
cs:1109 IRET ;pop IP,CS and flags
Notice that there is no INT 13 command in the source code, so if
you had simply used a debugger to search for "CD13" in the
machine code, you would never have found the protection routine.
2) Another technique is to put in a substitute interrupt
instruction, such as INT 10, which looks harmless enough, and
have the program change the "10" to "13 (and then back to "10")
on the fly. A search for "CD13" would turn up nothing.
3) The best camouflage method for interrupts I have ever
cracked (albeit not on a INT 13) was a jump to a section of the
PROGRAM code that reproduces in extenso the interrupt code. This
elegant (if a little overbloated) disguise mocks every call to
the replicated interrupt.
LOADING ABSOLUTE DISK SECTORS
Old good [debug.com] has been called the "swiss army knife" of
the cracker. It allows a lot of nice things, inter alia the
loading, reading, modifying and writing of absolute sectors of
the disks. The sector count starts with the first sector of track
0, next sector is track 0, second side (if double sided), then,
back to the first side, track 1, and so on, until the end of the
disk. Up to 80h (128) sectors can be loaded at one time. To use
you must specify starting address, drive (0=A, 1=B, etc...),
starting sector and number of sectors to load.
- l 100 0 10 20
This instruction tells DEBUG to load, starting at DS:0100, from
drive A, sector 10h for 20h sectors. This allows at times the
retrieval of hidden and/or weird formatted data. If you get an
error, check the memory location for that data. Often times, part
of the data has been transferred before the error occurs, and the
remainder can be manually entered or gathered through repetitive
retries.
Bear all this in mind learning the following cracks.
Let's now crack an "oldie" primitive:
MS Flight simulator (old version 2.12, from 1985!)
This old program used -in 1985!- following beautiful protection
scheme: on the disk you had only a "stub", called FS.COM with few
bytes, which had following instructions:
loc code instruction what's going on
-------------------------------------------------------
:0100 FA CLI ;why not?
:0101 33C0 XOR AX,AX ;ax=0
:0103 8ED0 MOV SS,AX ;ss=0
:0105 BCB0C0 MOV SP,C0B0 ;SP=C0B0
:0108 8EC0 MOV ES,AX ;ES=0
:010A 26C70678003001 MOV Wptr ES:[0078],0130 ;Wp 0:78=130
:0111 268C0E7A00 MOV ES:[007A],CS ;0:7A=Segment
:0116 BB0010 MOV BX,1000 ;BX=1000
:0119 8EC3 MOV ES,BX ;ES=1000
:011B 33DB XOR BX,BX ;BX=0
:011D B80102 MOV AX,0201 ;AH=2 AL=1 sector
:0120 BA0000 MOV DX,0000 ;head=0 drive=0
:0123 B96501 MOV CX,0165 ;track=1 sector=65 (!)
:0126 CD13 INT 13 ;INT 13/AH=2
:0128 B83412 MOV AX,1234 ;AX=1234
:012B EA00000010 JMP 1000:0000 ;JMP to data we just read
:0130 CF IRET ;Pavlovian, useless ret
You see what's happening in this old protection scheme,
don't you? Herein you can watch the same snap that happens in
more recent (much more recent) protection schemes (as you'll see
in the next lesson): the protection searches for a weird
formatted sector and/or for particular data.
That should be no problem for you any more: you should just
reverse engineer everything (and that goes on pretty quickly:
just watch and break on the INT_13 calls), fetch the "weird"
data, tamper the whole crap and have your soup as you like it.
One more word about "old" protection schemes. Be careful not
to spurn them! Some of them are
--CLEVER
--STILL USED
--DIFFICULT TO CRACK... I mean, this older DOS programs had
nice protections... it's pretty annoying to crack windows
programs that require a registration number: as you saw in Lesson
3, you just type your name and a serial number of your choice in,
say "666666666", break into the program with WINICE, search the
"666666666" and search too, for good measure, your own name, set
a memory read breakpoint where the number dwells and look at the
code that manipulates your input. As [Chris] rightly pointed out,
you can even rip the code straight out of the program and create
a key generator which will produce a valid code. This code will
work for any name you typed in only in the "pure maths
manipulation" protection schemes, and will on the contrary be
specific, following the name you typed in, the "alpha-maths
manipulation" protection schemes (like MOD4WIN, see the Windows
lessons), watch in this case the "pseudo-random xoring" of the
letters that compose your name.
--STUNNING, coz new ideas have always been infrequent, and
they are getting more and more rare in this objectionable world
of lazy, incapable programmers patronizing us with ill-cooked
outrages like Windows'95... yeah, as usual there is no
"development" at all, quite the contrary, I would say. Take a
step backward, sip a good Martini-Wodka (please remember that
only Ice cubes, Dry Martini, Wodka Moskovskaja, Schweppes'
"Indian tonic" a green olive from Tuskany and a maltese lemon
zest will really be perfect) and watch from your balcony, with
unsullied eyes, your town and the people around you: slaves
everywhere, leaving home at 7.30 in the morning, stinking in a
progression of identical cars, forced to interminably watch
advertisement panels and endlessly listen to boorish publicity,
happy to go to work (if they happen to have the "luck" to work,
in this inequitable society) the whole day long in order to
produce other cars in order to buy, one day, a new car with a
different colour...
Why people don't look at the stars, love each other, feel
the winds, ban the stinking cars from the places where they live
and eat, study colours... name yourself a not-consumistic
activity? Why don't they read any poems any more? No poetry any
more, in the grey society of the publicity-spots slaves...poetry
will soon be forbidden, coz you cannot CONSUME as you read poems,
and in this farce of a society you are BOUND to consume, that's
the only thing they want you to do... you are CULTIVATED to
consume... no books worth to read any more... stupid american
conventional cram everywhere... boy, at times I'm missing some
well placed neutron bombs, the ones that would kill all these
useless zombies and leave noble books and good Wodka untouched.
It's difficult to believe in democracy any more... if I ever
did... all the useless zombie do -unfortunately- vote, and they
do vote for "smiling semblances", for "conventionally minded
idiots" that so act as if they would "really" be like what they
"look" like and could not care less about anything else than
making bucks and defend intolerant and petty patterns. The slaves
choose the people they have "seen" on TV... as if the egyptians
would VOTE for their pharaohs, exhilarated under the whips of
publicity... sorry, at times I forget that you are here for the
cracks, and could not care less about what I think...
You 'll obtain the OTHER missing lessons IF AND ONLY IF you
mail me back (via anon.penet.fi) with some tricks of the trade
I may not know that YOU discovered. Mostly I'll actually know
them already, but if they are really new you'll be given full
credit, and even if they are not, should I judge that you
"rediscovered" them with your work, or that you actually did good
work on them, I'll send you the remaining lessons nevertheless.
Your suggestions and critics on the whole crap I wrote are also
welcomed.
+ORC an526164@anon.penet.fi
HOW TO CRACK, by +ORC, A TUTORIAL
Lesson 6.1: Funny tricks (1)
LESSON 6 (1) - Funny tricks. Xoring, Junking, Sliding
EXERCISE 01: [LARRY in search of the King]
Before the next step let's resume what you have learned in
the lessons 3-5, beginning with a very simple crack exercise
(again, we'll use the protection scheme of a game, for the
reasons explained in lesson 1): SEARCH FOR THE KING (Version
1.1.). This old "Larry" protection sequence, is a "paper
protection" primitive. It's a very widespread (and therefore easy
to find) program, and one of the first programs that instead of
asking meaningful passwords (which offer us the possibility to
immediately track them down in memory) asked for a random number
that the good buyer could find on the manual, whereby the bad
cracker could not. (Here you choose -with the mouse- one number
out of 5 possible for a "gadget" choosen at random). I don't need
any more to teach you how to find the relevant section of code
(-> see lesson 3). Once you find the protection, this is what you
get:
:protection_loop
:C922 8E0614A3 MOV ES,[A314]
...
:C952 50 0E PUSH AX & CS
:C954 E81BFF CALL C872 <- call protection scheme
:C957 5B POP BX twice
:C959 8B76FA MOV SI,[BP-06] <- prepare store_room
:C95C D1E6 SHL SI,1 <- final prepare
:C95E 8942FC MOV [BP+SI-04],AX <- store AX
:C961 837EFA00 CMP Word Ptr [BP-06],+00 <- good_guy?
:C965 75BB JNZ C922 <- loop, bad guy
:C967 8E0614A3 MOV ES,[A314]
:C96B 26F606BE3501 TEST Byte Ptr ES:[35BE],01 <- bad_guy?
:C971 74AF JZ C922 <- loop, bad guy
:C973 8B46FC MOV AX,[BP-04]... <- go on good guy
Let's see now the protection scheme called from :C954
:C872 55 PUSH BP
...
:C8F7 90 NOP
:C8F8 0E PUSH CS
:C8F9 E87234 CALL FD6E <- call user input
:C8FC 5B POP BX
:C8FD 5B POP BX
:C8FE 8B5E06 MOV BX,[BP+06]
:C901 D1E3 SHL BX,1
:C903 39872266 CMP [BX+6622],AX <- right answer?
:C907 7505 JNZ C90E <- no, beggar_off
:C909 B80100 MOV AX,0001 <- yes, AX=1
:C90C EB02 JMP C910
:C90E 2BC0 SUB AX,AX <- beggar_off with AX=0
:C910 8BE5 MOV SP,BP
:C912 5D POP BP
:C913 CB RETF <- back to main
Here follow 5 questions, please answer all of them:
1) Where in memory (in which locations) are stored the "right"
passnumbers? Where in memory is the SEGMENT of this
locations stored? How does the scheme get the OFFSET?
2) Would setting NOPs instructions at :C965 and :C971 crack?
Would it be a good idea?
3) Would changing :C907 to JZ crack? Would it be a good idea?
4) Would changing :C907 to JNZ C909 crack? Would it be a good
idea?
5) Write down (and try) at least 7 OTHER different patches to
crack this scheme in spades (without using any NOP!).
Uff! By now you should be able to do the above 5 exercises in
less than 15 minutes WITHOUT USING THE DEBUGGER! Just look at the
data above and find the right answers feeling them... (you 'll
now which one are the right one checking with your debugger...
score as many points as you like for each correct answer and sip
a good Martini-Wodka... do you know that the sequence should
ALWAYS be 1) Ice cubes 2) Martini Dry 3) Wodka Moskovskaja 4)
olive 5) lemon 6) Schweppes Indian tonic?
Let's now come to the subject of this lesson:
-----> [Xoring] (Simple encryption methods)
One easy way to encrypt data is the XOR method. XOR is a bit
manipulation instruction that can be used in order to cipher and
decipher data with the same key:
Byte to encrypt key result
FF XOR A1 5E
5E XOR A1 FF
As you can see XOR offers a very easy way to encrypt or to
decrypt data, for instance using the following routine:
encrypt_decrypt:
mov bx, offset_where_encryption/decryption_starts
xor_loop:
mov ah, [bx] <- get current byte
xor ah, encrypt_value <- engage/disengage xor
mov [bx], ah <- back where you got it
inc bx <- ahead one byte
cmp bx, offset_start_+_size <- are we done?
jle xor_loop <- no, then next cycle
ret <- back where we came from
The encrypt_value can be always the same (fixed) or chosen at
random, for instance using INT_21, service 2Ch (get current time)
and choosing as encrypt_value the value reported in DL (but
remembering to discard the eventual value 0, coz otherwise it
would not xor anything at all!)
random_value:
mov ah,2Ch
int 21h
cmp dl,0
je random_value
mov encrypt_value,dl
The problem with XORing (and with many other encryption
methods), is that the part of the code that calls the encryption
routine cannot be itself encrypted. You'll somewhere have, "in
clear" the encryption key.
The protectionist do at times their best to hide the
decrypting routine, here are some common methods:
-----> JUNK FILLING, SLIDING KEYS AND MUTATING DECRYPTORS
These are the more common protection method for the small
decryption part of the program code. This methods, originally
devised to fool signature virus scanners, have been pinched from
the polymorphic virus engines of our fellows viriwriters, and are
still in use for many simple decryption protection schemes. For
parts of the following many thanks go to the [Black Baron], it's
a real pity that so many potential good crackers dedicate so much
time to useless (and pretty repetitive) virus writing instead of
helping in our work. This said, virus studying is VERY important
for crackers coz the code of the viri is
* ULTRAPROTECTED
* TIGHT AND EFFECTIVE
* CLOAKED AND CONCEALED.
Let's show as example of the abovementioned protection tactics
the following ultra-simple decryptor:
MOV SI,jumbled_data ;Point to the jumbled data
MOV CX,10 ;Ten bytes to decrypt
mn_loop: XOR BYTE PTR [SI],44 ;XOR (un_scramble!) a byte
INC SI ;Next byte
LOOP mn_loop ;Loop the 9 other bytes
This small program will XOR the ten bytes at the location pointed
to by SI with the value 44. Providing the ten bytes were XORed
with 44 prior to running this decryptor the ten bytes will be
restored to their original state.
In this very simple case the "key" is the value 44. But there are
several tricks involving keys, the simplest one being the use of
a "sliding" key: a key that will be increased, or decreased, or
multiplied, or bit-shifted, or whatever, at every pass of the
loop.
A possible protection can also create a true "Polymorph"
decryptor, a whole decryptor ROUTINE that looks completely
different on each generation. The trick is to pepper totally
random amounts of totally random instructions, including JUMPS
and CALLS, that DO NOT AFFECT the registers that are used for the
decryption. Also this kind of protection oft uses a different
main decryptor (possibly from a selection of pre-coded ones) and
oft alters on each generation also all the registers that the
decryptor uses, invariably making sure that the JUNK code that
it generates doesn't destroy any of the registers used by the
real decryptor! So, with these rules in mind, here is our simple
decryptor again:
MOV DX,10 ;Real part of the decryptor!
MOV SI,1234 ;junk
AND AX,[SI+1234] ;junk
CLD ;junk
MOV DI,jumbled_data ;Real part of the decryptor!
TEST [SI+1234],BL ;junk
OR AL,CL ;junk
mn_loop: ADD SI,SI ;junk instr, but real loop!
XOR AX,1234 ;junk
XOR BYTE PTR [DI],44 ;Real part of the decryptor!
SUB SI,123 ;junk
INC DI ;Real part of the decryptor!
TEST DX,1234 ;junk
AND AL,[BP+1234] ;junk
DEC DX ;Real part of the decryptor!
NOP ;junk
XOR AX,DX ;junk
SBB AX,[SI+1234] ;junk
AND DX,DX ;Real part of the decryptor!
JNZ mn_loop ;Real part of the decryptor!
As you should be able to see, quite a mess! But still executable
code. It is essential that any junk code generated by the
Polymorph protection is executable, as it is going to be peppered
throughout the decryptor. Note, in this example, that some of the
junk instructions use registers that are actually used in the
decryptor! This is fine, providing the values in these
registers aren't destroyed. Also note, that now we have random
registers and random instructions on each generation. So, a
Polymorph protection Engine can be summed up into three major
parts:
1 .. The random number generator.
2 .. The junk code generator.
3 .. The decryptor generator.
There are other discrete parts but these three are the ones where
most of the work goes on!
How does it all work? Well a good protection would
* choose a random selection of registers to use for the
decryptor and leave the remaining registers as "junk" registers
for the junk code generator.
* choose one of the compressed pre-coded decryptors.
* go into a loop generating the real decryptor, peppered with
junk code.
From the protectionist's point of view, the advantages of this
kind of method are mainly:
* the casual cracker will have to sweat to find the decryptor.
* the casual cracker will not be able to prepare a "patch" for
the lamers, unless he locates and patches the generators, (that
may be compressed) coz otherwise the decryptor will vary every
time.
To defeat this kind of protection you need a little "zen" feeling
and a moderate knowledge of assembler language... some of the
junk instructions "feel" quite singular when you look at them
(->see lesson B). Besides, you (now) know what may be going on
and memory breakpoints will immediately trigger on decryption...
the road is open and the rest is easy (->see lessons 3-5).
-----> Starting point number magic
For example, say the encrypted code started at address 10h, the
following could be used to index this address:
MOV SI,10h ;Start address
MOV AL,[SI] ;Index from initial address
But sometimes you'll instead find something like the following,
again based on the encrypted code starting at address 10h:
MOV DI,0BFAAh ;Indirect start address
MOV AL,[DI+4066h) ;4066h + 0BFAAh = 10010h (and FFFF = 10h)!!
The possible combinations are obviously infinite.
[BIG KEYS] (Complicated encryption methods)
Prime number factoring is the encryption used to protect
sensible data and very expensive applications. Obviously for few
digit keys the decoding is much easier than for, say, 129 or 250
digit keys. Nevertheless you can crack those huge encryption too,
using distributed processing of quadratic sieve equations (which
is far superior for cracking purpose to the sequential processing
methods) in order to break the key into prime numbers. To teach
you how to do this sort of "high" cracking is a little outside
the scope of my tutorial: you'll have to write a specific short
dedicated program, linking together more or less half a thousand
PC for a couple of hours, for a 250 bit key, this kind of things
have been done quite often on Internet, were you can also find
many sites that do untangle the mysteries (and vagaries) of such
techniques.
As References I would advocate the works of Lai Xueejia, those
swiss guys can crack *everything*. Begin with the following:
Xuejia Lai, James Massey, Sean Murphy, "Markov Ciphers and
Differential Cryptanalysis", Advances in Cryptology,
Eurocrypt 1991.
Xuejia Lai, "On the Design and Security of Block Ciphers",
Institute for Signal and Information Processing,
ETH-Zentrum, Zurich, Switzerland, 1992
Factoring and primality testing is obviously very important for
this kind of crack. The most comprehensive work I know of is:
(300 pages with lengthy bibliography!)
W. Bosma & M. van der Hulst
Primality Testing with Cyclotomy
Thesis, University of Amsterdam Press.
A very good old book you can incorporate in your probes to build
very effective crack programs (not only for BBS accesses :=) is
*the* "pomerance" catalog:
Pomerance, Selfridge, & Wagstaff Jr.
The pseudoprimes to 25*10^9
Math. Comp. Vol 35 1980 pp. 1003-1026
Anyway... make a good search with Lykos, and visit the relevant
sites... if encryption really interests you, you'll be back in
two or three (or thirty) years and you'll resume cracking with
deeper erudite knowledge.
[PATENTED PROTECTION SYSTEMS]
The study of the patented enciphering methods is also *quite*
interesting for our aims :=) Here are some interesting patents,
if you want to walk these paths get the complete texts:
[BEST] USPat 4168396 to Best discloses a microprocessor
for executing enciphered programs. Computer programs which have
been enciphered during manufacture to deter the execution of the
programs in unauthorized computers, must be decrypted before
execution. The disclosed microprocessor deciphers and executes
an enciphered program one instruction at a time, instead of on
a continuous basis, through a combination of substitutions,
transpositions, and exclusive OR additions, in which the address
of each instruction is combined with the instruction. Each unit
may use a unique set of substitutions so that a program which can
be executed on one microprocessor cannot be run on any other
microprocessor. Further, Best cannot accommodate a mixture of
encrypted and plain text programs.
[JOHNSTONE] USPat 4120030 to Johnstone describes a
computer in which the data portion of instructions are scrambled
and in which the data is of necessity stored in a separate
memory. There is no disclosure of operating with instructions
which are completely encrypted with both the operation code and
the data address portion being unreadable without a corresponding
key kernel.
[TWINPROGS] USPat 4183085 describes a technique for
protecting software by providing two separate program storages.
The first program storage is a secure storage and the second
program storage is a free storage. Security logic is provided to
check whether an output instruction has originated in the secure
store and to prevent operation of an output unit which receives
output instructions from the free storage. This makes it
difficult to produce information by loading a program into free
storage.
[AUTHENTICATOR] USPat 3996449 entitled "Operating System
Authenticator," discloses a technique for authenticating the
validity of a plain text program read into a computer, by
exclusive OR'ing the plain text of the program with a key to
generate a code word which must be a standard recognizable code
word which is successfully compared with a standard corresponding
code word stored in the computer. If there is a successful
compare, then the plain text program is considered to be
authenticated and is allowed to run, otherwise the program
is not allowed to run.
ELEMENTS OF [PGP] CRACKING
In order to try to crack PGP, you need to understand how these
public/private keys systems work. Cracking PGP seems extremely
difficult, though... I have a special dedicated "attack" computer
that runs 24 hours on 24 only to this aim and yet have only begun
to see the light at the famous other end of the tunnel. It's
hard, but good crackers never resign! We'll see... I publish here
the following only in the hope that somebody else will one day
be able to help...
In the public key cryptosystems, like PGP, each user has an
associated encryption key E=(e,n) and decryption key D=(d,n),
wherein the encryption keys for all users are available in a
public file, while the decryption keys for the users are only
known to the respective users. In order to maintain a high level
of security a user's decoding key is not determinable in a
practical manner from that user's encoding (public) key. Normally
in such systems, since
e.multidot.d.ident.1 (mod(1 cm((p-1),(q-1)))),
(where "1 cm((p-1),(q-1))" is the least common multiple of the
numbers p-1 and q-1)
d can be determined from e provided p and q are also known.
Accordingly, the security of the system is dependent upon the
ability to determine p and q which are the prime factors of n.
By selecting p and q to be large primes, the resultant composite
number n is also large, and correspondingly difficult to factor.
For example, using known computer-implemented factorization
methods, on the order of 10.sup.9 years is required to factor a
200 digit long number. Thus, as a practical matter, although a
user's encryption key E=(e,n) is public, the prime factors p and
q of n are effectively hidden from anyone due to the enormous
difficulty in factoring n. These aspects are described more fully
in the abundant publications on digital signatures and Public-Key
Cryptosystems. Most public/private systems relies on a message-
digest algorithm.
A message-digest algorithm maps a message of arbitrary length
to a "digest" of fixed length, and has three properties:
Computing the digest is easy, finding a message with a given
digest "inversion" is hard, and finding two messages with the
same digest "collision" is also hard. Message-digest algorithms
have many applications, not only digital signatures and message
authentication. RSA Data Security's MD5 message-digest algorithm,
developed by Ron Rivest, maps a message to a 128-bit message
digest. Computing the digest of a one-megabyte message takes as
little as a second. While no message-digest algorithm can yet
be secure, MD5 is believed to be at least as good as any other
that maps to a 128-bit digest.
As a final gift, I'll tell you that PGP relies on MD5 for a
secure one-way hash function. For PGP this is troublesome, to say
the least, coz an approximate relation exists between any four
consecutive additive constants. This means that one of the design
principles behind MD4 (and MD5), namely to design a collision
resistant function, is not satisfied. You can construct two
chaining variables (that only differ in the most significant bit
of every word) and a single message block that yield the same
hashcode. The attack takes a few minutes on a PC. From here you
should start, as I did.
[DOS 4GW] cracking - This is only a very provisory part of this
tutorial. DOS 4GW cracking will be much better described as soon
as [Lost soul] sends his stuff, if he ever does. For (parts of)
the following I thank [The Interrupt].
Most applications of every OS, and also of DOS 4GW, are
written in C language, coz as you'll have already learned or,
either, you'll learn, only C allows you to get the "guts" of a
program, almost approaching the effectiveness of assembler
language.
C is therefore the LANGUAGE OF CHOICE for crackers, when you
prepare your tools and do not directly use assembler routines.
Besides... you'll be able to find VERY GOOD books about C for
next to nothing in the second hand bookshops. All the lusers are
throwing money away in spades buying huge, coloured and
absolutely useless books on unproductive "bloated" languages like
Visual basic, C++ and Delphy. Good C new books are now rare
(books on assembler language have always been) and can be found
almost exclusively on the second hand market. Find them, buy
them, read them, use them for your/our aims. You can find a lot
of C tutorials and of C material on the Web, by all means DO IT!
Be a conscientious cracker... learn C! It's cheap, lean, mean and
very productive (and creative) :=)
Back to the point: most stuff is written in C and therefore
you need to find the "main" sub-routine inside the asm. With
DOS/4GW programs, search the exe file for "90 90 90 90", almost
always it'll be at the start of the compiled code. Now search for
an INT_21 executed with 4C in AH, the exec to dos code (if you
cannot "BPINT 21 AH=4C" with your tool, then search for the
sequence "b4 4c cd 21". This is the equivalent to [mov AH,4C &
int 21]: it's the most direct call, but as you'll have already
learned, there are half a dozen ways to put 4C in AX, try them
all in the order of their frequency).
A few bytes above the INT_21 service 4C, you'll find the
call to the "main" subroutine: "E8 xx xx". Now place a "CC" byte
a few bytes above the call in the exe and run the exe under a
debugger. When the computer tries to execute the instruction
you'll be throw back in the debugger coz the "CC" byte acts as
INT_01 instruction. Then proceed as usual.
[THE "STEGONATED" PASSWORD HIDEOUT]
A last, very nice trick should be explained to every wannabe
cracker, coz it would be embarrassing to search for passwords or
protection routines that (apparently) are not there. They may be
hidden INSIDE a picture (or a *.waw file for that matter). This
is steganography, a method of disguising messages within other
media.
Depending on how many shades of grey or hues of colour you want
to have, a pixel can be expressed using 8. 16, 32 or even more
bits. If the least significant bit is changed. the shade of the
pixel is altered only one-256th, one-65,OOOth or even less. No
human eye could tell the difference.
What the protectionist does, is hijack the least significant
bit in each pixel of a picture. It uses that bit to store one bit
of a protection, or of a password (or of a file, or of a secret
message). Because digitized pictures have lots of pixels, it's
possible to store lots of data in a single picture. A simple
algorithm will transfer them to the relevant parts of the program
when it needs be, and there we'll intercept them. You'll need to
learn very well the zen-cracking techniques to smell this kind
of stuff though (-> see lesson B).
Well, that's it for this lesson, reader. Not all lessons of my
tutorial are on the Web.
You 'll obtain the OTHER missing lessons IF AND ONLY IF you
mail me back (via anon.penet.fi) with some tricks of the trade
I may not know that YOU discovered. Mostly I'll actually know
them already, but if they are really new you'll be given full
credit, and even if they are not, should I judge that you
"rediscovered" them with your work, or that you actually did good
work on them, I'll send you the remaining lessons nevertheless.
Your suggestions and critics on the whole crap I wrote are also
welcomed.
an526164@anon.penet.fi (+ORC)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment