Cracking OpenNT 2.0 - object oriented cracking
(The temporary DLL trick)
Advanced cracking series
by chown
(1 November 1997)
Courtesy of Fravia's page
of reverse engineering
Well, a VERY interesting Unix-school essay, that
I'm happy to host on my site. Object oriented cracking! A very nice definition indeed!
I have included this essay in the advanced section because I believe it well
deserves a place there. You'll notice that there is a lot to learn for any
reverser inside this...
even if this (good) author seems to be only at his 'first steps' in windows programming... yet,
this notwithstanding, this is NOT for newbyes. Newbyes should pheraphs have first a look
at +gthorne's OTHER +HCU page first.
I find the part where chown describes is first programming experiences with
windows programming particularly amusing... in fact I believe that any good cracker
can indeed often program better than a 'real' programmer because he can CRACK the compiler
into doing whatever he fancies if needs be!
Enjoy!
Cracking OpenNT 2.0
===================
==== by chown =====
target : OpenNT 2.0, by Softway Systems
link : http://www.softway.com
protection : generic serial numbers
method : brute force
tools : SoftICE NT, disassembler, C++ compiler (Win32)
needed : average cracking skills, knowledge of C/C++
About
-----
If like Unix and you haven't got Linux, then OpenNT is probably the
next-best thing.
Unlike most Unix-like environments available today, this one doesn't
emulate the programs; instead they run on their own POSIX subsystem
(POSIX is an interface just like Win32 or OS/2).
Besides this enhanced subsystem, you'll get bunch of Unix
utilities, the KornShell & C shell, Perl, sed, awk ...; support for
sockets, memory mapped files, hard links...; an SDK, X11R6 windowing,
Motif window manager, etc...
All these little wonders, nicely packed on a CD-ROM, shipped to your
font door for free...
You're probably thinking : sounds too good to be true, and you're right.
-> when you receive the CD-ROM, you get 2 keys which will only
unlock some components of OpenNT; and to make things even worse, those
components have "best before dates" (also known as 'drop dead dates' i.e.
they stop working on a fixed date, no matter when you start using them)
-> the other components can only be bought (you'll then get "everlasting"
keys)
To sum things up, here's the whole list of available components :
OpenNT Workstation Lite (pay for key)
OpenNT Workstation (best before date OR pay for "everlasting" key)
OpenNT Server (pay for key)
OpenNT X11R6 Server (best before date OR pay for "everlasting" key)
OpenNT SDK (best before date OR pay for "everlasting" key)
OpenNT OpenNTiF (pay for key)
OpenNT OpenNTiF SDK (pay for key)
(Note : when reading the essay, don't forget that the "Lite" version of
the Workstation is NOT the version you can install with the best before
key !! You need to pay for the Lite version!)
So let's get started!
When you run the installation, the second screen prompts you for the
serial numbers.
As a cracker, you would normally have 2 options:
(a) you fill in the keys you received, and later you simply disable the
best before dates; the problem here is that you'll eventually have to
rerun and crack the setup program if you want to install the other
components.
(b) you immediately crack the setup program in order to install whatever
component you want; the problem with this option is that after you've
cracked the installation, you'll need to crack the installed programs,
because they too will look for the "everlasting" keys (in the registry).
None of these 2 options are very difficult, in fact I'm sure most of you
could easily crack it either way. The purpose of this essay however,
is to show you yet another approach.
Let's start by sniffing around a little to see what the setup program
(InstallShield) is really up to. You'll quickly find out that it has
created a directory (called "_ISTMP0.DIR") in your TEMP directory.
So what's in _ISTMP0.DIR
?
9/16/94 14:00 25,088 CTL3D32.DLL
9/16/94 14:00 26,112 CTL3D32S.DLL
5/08/97 3:31 19,456 e3e9c.DLL
1/22/96 20:59 90,112 e3e92.DLL
5/05/97 16:03 5,776 LICENSE.TXT
5/08/97 3:31 46,080 MYDLL.dll
2/28/97 21:11 20,784 opennt.bmp
4/11/97 9:24 75,466 setup.bmp
5/21/97 12:05 88,574 _SETUP.LIB
Let's see... CTL3*.DLL are the 3D-controls the setup program needs,
LICENSE.TXT is the license info nobody ever reads, *.bmp are bitmaps,
and _SETUP.LIB is a compressed file (see note [1]) that contains the
other 8 files in this directory.
So this leaves us with the 3 DLLs; two of them have pseudo-random
names (every installation will name these files differently), the
other DLL has a strange name.
[1] you could check this if you have a nice little program called
"compress" (which is from the company that makes InstallShield); you can
use this program to unpack all compressed file of InstallShield.
So let's start with MYDLL.DLL (the programmer who wrote this library
must have been a hell of a creative guy, and quite possessive too!).
Time for some disassembly...
(Note : you'll have to copy these files to another directory BEFORE you
quit setup, because they will get deleted upon exit.)
After WDasm has done its job, we first check the string references for
anything suspicious. We find strings like "sdk", "sdk_demo", "xs",
"xs_demo".... hmmm, interesting, but since we're dealing with a DLL,
let's take a look at the exports...
... Bingo!! Look at names of some of these exported functions :
isDemoKey
whatIsThis
verifyKey
Now, how many times does Fravia+ and all other +HCUkers have to repeat
themselves ?!!!
When will the programmers finally get the message? These names are like
a path of oasis inside a desert, they are like flashing neon lights in
the dark alleys of disassembly.
So, once more for the record
-> ATTENTION TO ALL PROTECTIONISTS: DON'T CLUTTER YOUR PROGRAMS WITH
TELLTALE NAMES !
Back to MYDLL.DLL. Let's check each of these suspicious functions :
_____________________________isDemoKey____________________________
Exported fn(): isDemoKey - Ord:000Ah
//...
:100031D4 test eax, eax
:100031D6 je 100031E8
:100031DC xor eax, eax
:100031DE jmp 100031F2
:100031E3 jmp 100031F2
:100031E8 mov eax, 00000001
:100031ED jmp 100031F2
:100031F2 pop edi
:100031F3 pop esi
:100031F4 pop ebx
:100031F5 leave
:100031F6 ret 0004
I've left most of the code out because it really doesn't matter how
"isDemoKey" is implemented. The only thing we're interested in,
is its interface (don't forget that these are *exported* functions
- try to think of this as object oriented cracking :-).
So these few lines of assembly tell us enough: isDemoKey either
returns 0 or 1 in EAX.
(1 in EAX most likely means the key is a 'drop dead' key, 0 means
you paid for the key; we'll check this later in SoftICE)
__________________________whatIsThis_____________________________
Exported fn(): whatIsThis - Ord:000Eh
//...
:10003211 C745FC2D010000 mov [ebp-04], 0000012D
:10003218 E990000000 jmp 100032AD
:1000321D C745FC2E010000 mov [ebp-04], 0000012E
:10003224 E984000000 jmp 100032AD
:10003229 C745FC2F010000 mov [ebp-04], 0000012F
:10003230 E978000000 jmp 100032AD
:10003235 C745FC31010000 mov [ebp-04], 00000131
:1000323C E96C000000 jmp 100032AD
:10003241 C745FC32010000 mov [ebp-04], 00000132
:10003248 E960000000 jmp 100032AD
:1000324D C745FC33010000 mov [ebp-04], 00000133
:10003254 E954000000 jmp 100032AD
:10003259 C745FC30010000 mov [ebp-04], 00000130
:10003260 E948000000 jmp 100032AD
:10003265 C745FC00000000 mov [ebp-04], 00000000
:1000326C E93C000000 jmp 100032AD
//...
:100032AD 8B45FC mov eax, dword ptr [ebp-04]
:100032B0 E900000000 jmp 100032B5
:100032B5 5F pop edi
:100032B6 5E pop esi
:100032B7 5B pop ebx
:100032B8 C9 leave
:100032B9 C20400 ret 0004
Again, most of the code is left out. We see that "whatIsThis" returns
one of the following in EAX : 12D, 12E, 12F, 131, 132, 133, 130 or 0.
What do these numbers stand for, you ask?? Patience, first verifyKey...
--------------------------intermezzo-------------------------------
Quoting Larry Wall (father of the Perl language) :
"the three great virtues of a programmer: laziness, impatience, and
hubris." Well, these three "virtues" are, in fortiori, applicable to
crackers (IMO).
But think about it: the latter is a consequence of the former, because
IF the protectionists were to put more work protecting their software,
we would HAVE TO do the same for our cracks!
But that ... is a BIG, VERY BIG IF.
The only way out of this vicious circle is to crack ALL programs to
pieces, in order to force the protectionist to take more care & pride
in their work.
The Perl language, by the way, is included on this CDROM and is a very
nice & practical language indeed. The quote is from Larry Wall's very
funny & good book "Programming Perl". There used to be a complete online
version of the book available
at http://online-books.oreilly.com/books/webref/perl/index.htm,
maybe it's still there...
--------------------------------------------------------------------------
____________________________verifyKey_____________________________
Exported fn(): verifyKey - Ord:0009h
//...
:10002349 E815000000 call 10002363
:1000234E 83C408 add esp, 00000008
:10002351 8945FC mov dword ptr [ebp-04], eax
:10002354 8B45FC mov eax, dword ptr [ebp-04]
:10002357 E900000000 jmp 1000235C
:1000235C 5F pop edi
:1000235D 5E pop esi
:1000235E 5B pop ebx
:1000235F C9 leave
:10002360 C20400 ret 0004
Conclusion : verifyKey return its result in eax.
__________________________________________________________________________________
Now, to be sure these functions really do what we hope they do, we
need SoftICE.
But before we do that we need to get a feel of out target, so we
start setup.exe, we then fill in some bogus keys to see what happens...
Well, we get a little messagebox saying that the keys are invalid.
Now we fill in the 'drop dead' keys we received with the package,
press "next" and... we simply get the next screen.
OK, now press 'back' or 'cancel' to return to the first screen.
Fire up SoftICE and let the SoftICE Symbol Loader load the exports of
MYDLL.DLL.
We then set execution breakpoints on each of these functions
(BPX verifyKey;
BPX whatIsThis;
BPX isDemoKey).
Again, we enter the 2 keys we received from Softway. Press the "OK"
button... SoftICE immediately pops up at verifyKey.
All we need to know about verifyKey, is what kind of result it returns
in EAX; so we simply enter "G @SS:ESP" in SoftICE (or press F11) to
return to the calling function, and we see there that EAX = 0.
We press F5 to continue... SoftICE pops up at whatIsThis, press
F11 to return... EAX = 12E.
We take note and continue with F5.
It's now isDemoKey's turn to pop up, again F11... EAX = 1... F5...
Now SoftICE pops at verifyKey for the second time. Well, this is
MYDLL.DLL checking the second key. The second time around we have :
verifyKey -> EAX = 0
whatIsThis -> EAX = 131
isDemoKey -> EAX = 1
We could already try to draw some conclusions here, but let's try the
same routine with some *bogus* keys. If you would do this, you'd get
twice (for 2 keys) :
verifyKey -> EAX = 1
whatIsThis -> EAX = 0
isDemoKey -> doesn't get called
You should be getting the picture by now :
verifyKey -> returns 0 if the key was valid, 1 if invalid
whatIsThis -> returns 0 if invalid key, another value
(see note [2]) if valid
isDemoKey -> returns 1 if the key was a demo key, 0 if key
was paid for
[2] for the "other" value we have so far :
12E -> iff the valid key was for the Workstation option
131 -> iff the valid key was for the SDK option
Let's try the other return values to see what they mean. This is a
typical and VERY important reversing approach.
Therefore we need to enter some bogus key, and upon return from
verifyKey, we change the value in EAX from 1 to 0. We change the
return value of whatIsThis from 0 to (12D | 12F | 130 | 132 | 133),
and we also make sure isDemoKey returns 0 (=full version).
Doing this, the setup program will reveal which versions match which
keys.
Don't forget that, using this method, you COULD trick the setup program
to install whatever version you want, BUT (a) this is not the point
of the essay, and
(b) you'll need to crack the program a 2nd time after installation
(and the 2nd crack is not as trivial as this one).
So, after having tried all combination we get the following list :
12D = Workstation Lite
12E = Workstation
12F = Server
130 = Bad key
131 = X11R6 Server
132 = SDK
Up to now everything we've done has been pretty elementary, but did you
know that that was the hardest part of this crack? We are now simply going
to use these three functions to generate valid serial numbers for us!!
How ? Well, since the functions are exported, we'll just write a program
that *imports* these functions. We will repeatedly call the three functions
and test their results until we have correct keys!
Here's the idea in pseudo-code :
WHILE (we haven't found enough keys yet) DO
generate a key to test (see note [3])
test key in verifyKey
test key in whatIsThis
test key in isDemo
IF (the key is good) THEN
print the key
print what kind of key it is
ELSE
simply try another key
END
END
[3] We are going to loop over all possible keys that
have the same form as the keys we received from Softway.
The keys you'll receive will look something like "fSu,0qw?eSK",
so this means all keys of length 11 (eleven) that are a
combination of all possible letters, all possible digits,
and all possible punctuation symbols will be tried).
In the code of my program, however, you'll see that I only
try combinations in the following subset
"ABCDEFGH=JKLMN*PQRSTUVWXYZabcdefghijk?mnopqrstuvwxyz()23456789+/" ,
this is because I had a quick look at what the program
compares the input key to. But even if hadn't done that, the
program would still find the good keys, it would only take a
little longer.
First a note : I'm a real newbie at Windows programming, in fact the
following code is the second Windows program I've ever made (the first
one, 2 weeks ago, was a program that simply displayed a message box :-),
so bear with me if I've made some stupid mistake(s).
But it does compile and work!
For those of you who know even less of Windows programming than I do :
I chose to dynamically load the DLL, which you should do like this (I
think)
-> define a global handle : HMODULE hlib
-> load MYDLL.DLL like : HMODULE LoadLibrary("MYDLL.DLL")
-> declare the imported function
-> get a pointer to the function like:
FARPROC GetProcAddress(hlib,"verifyKey");
-> call the function
-> free the DLL like : FreeLibrary()
Now, how on earth are we going to declare a function if we don't really
know its signature (all we do know is that it returns "something" in EAX)?
Well, you simply call it with whatever parameters you chose, and then
when you try to compile it, the compiler will do the rest for us by
flagging an error AND giving the real signature of the function.
For instance, when I declared the function verifyKey with :
int verifyKey(char*);
the compiler told me that it really should be :
void cdecl (*verifyKey)(void*, long);
That told me all I needed, except that I had no clue whatsoever what the
hell "cdecl" meant! So I looked it up, and found out that it's some special
calling convention where the function the *caller* of a function cleans
up the stack....
I didn't really figure out what that all meant for me, so I tried a few
things (using SoftICE as a true debugger, finally!! :-), and ended up
with a solution only a cracker could have come up with : inline assembly
to get the stack pointer to point to what it should be pointing to!
Anyway, here's the code :
_________________________________OpenNT.cpp______________________________________
/*
* This program generates serial numbers for OpenNT 2.0
* After a couple of minutes, valid keys should start to appear on the
* screen.
* The program imports MYDLL.DLL, so make sure that in your PATH
*
* I managed to compile it with VC++ 5 (choose "console, Win32 app")
* using its
* DEFAULT configuration. If turn on optimizations, the program will
* probably crash!
* (just type "cl OpenNT.cpp" at the command prompt)
*
* Also BC++ 5 managed to spit out some executable, again using the
* default
* configuration.
* (just type "bcc32 OpenNT.cpp" at the command prompt)
*/
#include
#include
#include
HMODULE hlib;
void checkIt(char* key) {
int result;
bool goodKey = true;
void cdecl (*isWhat)(void*, long);
isWhat = (void cdecl(*)(void*, long)) GetProcAddress(hlib,
"whatIsThis");
(*isWhat)(key, 0); // the first parameter is a pointer to
our key;
// the second parameter doesn't seem to
be used,
// so I simply chose 0
_asm mov [result], eax // inline assembly to get to the value
in EAX
char version[32];
switch(result) {
case 0x012D : strcpy(version, "WorkStation Lite"); break;
case 0x012E : strcpy(version, "WorkStation"); break;
case 0x012F : strcpy(version, "Server"); break;
case 0x0131 : strcpy(version, "SDK"); break;
default : goodKey = false;
}
if (goodKey) {
void cdecl (*isDemo)(void*, long);
isDemo = (void cdecl(*)(void*, long)) GetProcAddress(hlib,
"isDemoKey");
(*isDemo)(key, 0); // call isDemoKey
_asm mov [result], eax // get result in EAX
cout " << version; if (result) cout << " Demo version."; cout << endl; } } int main() { cout << "Press Control+C when you've seen enough..." << endl; char key[12]="ABCDEFGH=JK" ; const char symbol[]="ABCDEFGH=JKLMN*PQRSTUVWXYZabcdefghijk?mnopqrstuvwxyz()23456789+/" ; // these are the 64 symbols we'll use make our key int result="1;" hlib="LoadLibrary("MYDLL.DLL");" void cdecl (*verKey)(void*, long); verKey="(void" cdecl(*)(void*, long)) GetProcAddress(hlib, "verifyKey"); // this is the loop over all possible combinations for key of length 11 for (int i="0" ; i < 64 ; i++) { key[0]="symbol[i];" for (int j="0" ; j < 64 ; j++) { key[1]="symbol[j];" for (int k="0" ; k < 64 ; k++) { key[2]="symbol[k];" for (int l="0" ; l < 64 ; l++) { key[3]="symbol[l];" for (int m="0" ; m < 64 ; m++) { key[4]="symbol[m];" for (int n="0" ; n < 64 ; n++) { key[5]="symbol[n];" for (int o="0" ; o < 64 ; o++) { key[6]="symbol[o];" for (int p="0" ; p < 64 ; p++) { key[7]="symbol[p];" for (int q="0" ; q < 64 ; q++) { key[8]="symbol[q];" for (int r="0" ; r < 64 ; r++) { key[9]="symbol[r];" for (int s="0" ; s < 64 ; s++) { key[10]="symbol[s];" (*verKey)(key, 0); // call MYDLL's verifyKey _asm sub esp, 4 // change the stack pointer _asm mov [result], eax // get value in EAX if (!result) // does verifyKey say it's good? checkIt(key); // then send it to whatIsThis } } } } } } } } } } } BOOL ret="FreeLibrary(hlib);" return 0; }
____________________________________________________________________________________
The program starts the loop with the key "AAAAAAAAAAA", then it checks
"AAAAAAAAAAB",
and so on...
The output will be of the form :
AJKDS9)fdoe -> Server
lkjsdfhuyee -> WorkStation
67e4dsfew78 -> SDK
....
(these keys are false of course!!)
The OpenNTif component of OpenNT isn't covered here because it's a
different installation all together. I've cracked that the
"conventional" way myself, but I'm not going to bother you with that,
because you could probably crack it blindfolded! ( -> a simple
"jmp goodguy / jmp badguy" inside the file "PINSTALL", and once
it's installed, there are no further checks)
-> you see, I even left you a piece of the cake ;-)
That's all for now,
chown
--------
ADDENDUM
--------
I've just checked everything again, and it seems that the executables
created by VC++ 5 and BC++ 5 work differently !
The executable created by BC++ 5 doesn't find as many valid keys as the
one compiled by VC++; it just seems to skip many valid keys!
It doesn't really matter, because they both generate enough valid keys
anyway, but I just thought I'd let you know...
Another point to remember is that with "default configuration" I meant
that VC++ MUST compile the code *with debug* information (which is the
default setting if I'm not mistaken). If you compile it without, the
executable will crash (on my machine anyway).
The executable of BC++ will run with or without debug info.
(c) Chown 1997. All rights reserved
You are deep inside fravia's page of reverse engineering,
choose your way out:
Back to Advanced Cracking
homepage
links
anonymity
+ORC
students' essays
Academy database
tools
cocktails
antismut CGI-scripts
search_forms
mail_Fravia
Is reverse engineering illegal?