First of all I have to thank crackmes.cjb.net for it's fantastic site that
offer me a simple way to spend my spare time, and then I would like to thank
daPope 'cause he indirectly taught me a new way of thinking about crackme's
world.
THE PROTECTION
==============
Let's start.
At a first look the protection doesn't seem to be very hard, in fact we have
only a name and a serial number to enter and then we can see if we are very
strong boy or not_so_very_strong boy.
As usual i have SIce loaded (who haven't?), and the program says 'you have
sice loaded, please unload' that is the standard phrase for an anti-sice
protection. We have to look at the code to search for a pair of byte that
build one of the many protections against NuMega's debugger, but first
think a bit...
The most simple way of detecting SIce is trying to open the SIce main EXE
so i look in the file with an hex-editor (HexWorkshop?) and found nothing.
Well, it isn't so strange...
Fire up W32Dasm and IDA (wow...) and try to disassemble (yeah, we can 'cause
there are no rules and as we look in the PE section, there aren't any
packing/compressing or other file protections).
Hey, look at the W32Dasm code...
It isn't reliable, but it's very strange...
AH YES!!! It's a Java Crackme!!!
Ok, look at the exe for some strings so we can have a start...
What we found?
The Java source...
It isn't strange? It's all there, all the code we are looking for is
standing in front of our eyes in a high level language...
Really good...
Open the file with Notepad or any other text editor and copy the code into
another file, let's say Crackme.java (it's not a random name as you'll see).
It's a strange code and I have to warn you: don't look at the
calculateSerial() routine 'cause it's only here to take us to the wrong
way (try to follow the lines of it if you can't believe, they'll lead you
to a dead road).
So, where can we look?
First I examined the anti-SIce code that is located in the routine called
checkForWinIce(). The routine do a very strange check to locate SIce. It
read the autoexec.bat in search of a string with "winice.exe" in it; it's
a strange protection, i've never seen such a thing, but it has a good
aspect: even if you unload SIce modifying only the autoexec.bat with a rem
in front of the launch command the program still know that you have SIce
in your system and it doesn't work...
Ok, we have to patch this little trick: I don't touch the crackme's code
'cause there were no rules, but the ethical one it's always there... and
also because to patch the exe you have to recompile the class and then
modify the bytes in the file (it's a huge work and may not satisfy); so
the only thing to do it's modify autoexec.bat so that there aren't more
"winice.exe" string in it. Ok, done; remember the rem in front of the line
to avoid errors on system startup.
Now, if you know a little of Java you'll ask yourself: 'how the hell a
java class can be packed into an exe file?'. The answer it's this:
a little tool called Microsoft Visual J++ offer you a GUI where you can
draw your application and write the code associated to each event (just
like VisualBasic, except that the code isn't in Basic but in Java).
Ok, let's think a bit about this: if we have the code right in front of
us maybe in the exe we only have the classes packed together like an
archive. It's our lucky day, it's as I've told you...
Now another problem stand in front of us: the code that we see it's there
to take us to a death way or it's real code? This is a question that can
be answered only extracting the real classes from the exe and then disasm
them to see if the two sources match each other.
If you've tryed to extract the MIDI files from Final Fantasy VII you would
have thought: 'maybe the files are only stored in the exe and when one
start, the other finish'.
It's a complicated idea but I explain it right now:
If you have, let's say, three txt files named 'one.txt', 'two.txt' and
'three.txt' and you want to put them in a file called 'archive.txt' what
will you do?
You simply begin to put one.txt and then two and three all one after the
other...
So, if we know the header of a class file we can extract it from the
exe without problem (we don't care about is size, the diasm, jad, won't
look at strange code at the end, it'll simply disassemble the code and
discard the garbage).
Looking at other class files i noticed that the header it's this (in
hexadecimal): CAFE BABE
Much fantasy, huh? I don't know who select this header...
Ok extract the files and notice that the last it's very big (about 20Kb).
This is the core of the crackme.
After disassembling it, however, we can't discover new worlds because the
code is the same as the first we see...
Now we have to do the hard part of the work: trying to transport ourself
inside the author's mind and decyfer the source code.
It's not so hard to do because crackme it's simple but we have to carefully
watch at dead points, that are many: one it's the one i've told you above,
another it's represented by the input boxes and the register button. They
don't have functionality in this crackme, they're there only to take us
to a death road.
If you carefully look at the source code, you'll see that the two text boxes
are used to generate the fake serial number and nothing else.
We have to input the name and the serial by command line in that order...
Ok, now we can declare war at the real source code. To simplify your life
here is it (i've added a few comments) :
public Crackme(String args[]) // this is like main() in C++
{
initComponents(); // Doesn't matter for us
pack(); // As above
setTitle("daPope's CrackMe #1 - Take a look att http://surf.to/daPopesTutorial");
if(!checkForWinIce()) // Interesting...
{
boolean registered = isRegistered(); // Uh, oh... what happen?
if(!registered) // Not registered it's our first state
{
if(args.length != 2)
{
label3.setText((new Character('E')).toString() + (new Character('n')).toString() + (new Character('t')).toString() + (new Character('e')).toString() + (new Character('r')).toString() + (new Character(' ')).toString() + (new Character('N')).toString() + (new Character('a')).toString() + (new Character('m')).toString() + (new Character('e')).toString() + (new Character(' ')).toString() + (new Character('A')).toString() + (new Character('n')).toString() + (new Character('d')).toString() + (new Character(' ')).toString() + (new Character('S')).toString() + (new Character('e')).toString() + (new Character('r')).toString() + (new Character('i')).toString() + (new Character('a')).toString() + (new Character('l')).toString() + (new Character(' ')).toString() + (new Character('T')).toString() + (new Character('o')).toString() + (new Character(' ')).toString() + (new Character('R')).toString() + (new Character('e')).toString() + (new Character('g')).toString() + (new Character('i')).toString() + (new Character('s')).toString() + (new Character('t')).toString() + (new Character('e')).toString() + (new Character('r')).toString());
} else
{
button1.setEnabled(false);
String name = args[0];
String serial = args[1];
StringBuffer correct = new StringBuffer();
// From here...
for(int i = 0; i < name.length(); i++)
{
char a = name.charAt(i);
int b = Character.getNumericValue(a);
int c = b % 10;
Integer d = new Integer(c);
correct.append(d.toString());
}
// ... to here the code is computed.
// You can understand what will happen under
// this line?
if(serial.equals(correct.toString()))
{
registered = true;
try
{
FileWriter theFile = new FileWriter("C:/Windows/systen.ini");
label3.setText((new Character('C')).toString() + (new Character('o')).toString() + (new Character('o')).toString() + (new Character('l')).toString() + (new Character('!')).toString() + (new Character(' ')).toString() + (new Character('d')).toString() + (new Character('a')).toString() + (new Character('P')).toString() + (new Character('o')).toString() + (new Character('p')).toString() + (new Character('e')).toString() + (new Character('\'')).toString() + (new Character('s')).toString() + (new Character(' ')).toString() + (new Character('C')).toString() + (new Character('r')).toString() + (new Character('a')).toString() + (new Character('c')).toString() + (new Character('k')).toString() + (new Character('M')).toString() + (new Character('e')).toString() + (new Character(' ')).toString() + (new Character('#')).toString() + (new Character('1')).toString() + (new Character(' ')).toString() + (new Character('S')).toString() + (new Character('u')).toString() + (new Character('c')).toString() + (new Character('c')).toString() + (new Character('e')).toString() + (new Character('s')).toString() + (new Character('s')).toString() + (new Character('f')).toString() + (new Character('u')).toString() + (new Character('l')).toString() + (new Character('l')).toString() + (new Character('y')).toString() + (new Character(' ')).toString() + (new Character('R')).toString() + (new Character('e')).toString() + (new Character('g')).toString() + (new Character('i')).toString() + (new Character('s')).toString() + (new Character('t')).toString() + (new Character('e')).toString() + (new Character('r')).toString() + (new Character('e')).toString() + (new Character('d')).toString());
setTitle((new Character('d')).toString() + (new Character('a')).toString() + (new Character('P')).toString() + (new Character('o')).toString() + (new Character('p')).toString() + (new Character('e')).toString() + (new Character('\'')).toString() + (new Character('s')).toString() + (new Character(' ')).toString() + (new Character('C')).toString() + (new Character('r')).toString() + (new Character('a')).toString() + (new Character('c')).toString() + (new Character('k')).toString() + (new Character('M')).toString() + (new Character('e')).toString() + (new Character(' ')).toString() + (new Character('#')).toString() + (new Character('1')).toString() + (new Character(' ')).toString() + (new Character('-')).toString() + (new Character(' ')).toString() + (new Character('R')).toString() + (new Character('e')).toString() + (new Character('g')).toString() + (new Character('i')).toString() + (new Character('s')).toString() + (new Character('t')).toString() + (new Character('e')).toString() + (new Character('r')).toString() + (new Character('e')).toString() + (new Character('d')).toString());
}
if(!registered)
label3.setText((new Character('I')).toString() + (new Character('n')).toString() + (new Character('v')).toString() + (new Character('a')).toString() + (new Character('l')).toString() + (new Character('i')).toString() + (new Character('d')).toString() + (new Character(' ')).toString() + (new Character('S')).toString() + (new Character('e')).toString() + (new Character('r')).toString() + (new Character('i')).toString() + (new Character('a')).toString() + (new Character('l')).toString() + (new Character('.')).toString() + (new Character(' ')).toString() + (new Character('T')).toString() + (new Character('r')).toString() + (new Character('y')).toString() + (new Character(' ')).toString() + (new Character('A')).toString() + (new Character('g')).toString() + (new Character('a')).toString() + (new Character('i')).toString() + (new Character('n')).toString());
setTitle((new Character('d')).toString() + (new Character('a')).toString() + (new Character('P')).toString() + (new Character('o')).toString() + (new Character('p')).toString() + (new Character('e')).toString() + (new Character('\'')).toString() + (new Character('s')).toString() + (new Character(' ')).toString() + (new Character('C')).toString() + (new Character('r')).toString() + (new Character('a')).toString() + (new Character('c')).toString() + (new Character('k')).toString() + (new Character('M')).toString() + (new Character('e')).toString() + (new Character(' ')).toString() + (new Character('#')).toString() + (new Character('1')).toString() + (new Character(' ')).toString() + (new Character('-')).toString() + (new Character(' ')).toString() + (new Character('R')).toString() + (new Character('e')).toString() + (new Character('g')).toString() + (new Character('i')).toString() + (new Character('s')).toString() + (new Character('t')).toString() + (new Character('e')).toString() + (new Character('r')).toString() + (new Character('e')).toString() + (new Character('d')).toString());
label3.setText((new Character('P')).toString() + (new Character('r')).toString() + (new Character('o')).toString() + (new Character('g')).toString() + (new Character('r')).toString() + (new Character('a')).toString() + (new Character('m')).toString() + (new Character(' ')).toString() + (new Character('I')).toString() + (new Character('s')).toString() + (new Character(' ')).toString() + (new Character('A')).toString() + (new Character('l')).toString() + (new Character('r')).toString() + (new Character('e')).toString() + (new Character('a')).toString() + (new Character('d')).toString() + (new Character('y')).toString() + (new Character(' ')).toString() + (new Character('R')).toString() + (new Character('e')).toString() + (new Character('g')).toString() + (new Character('i')).toString() + (new Character('s')).toString() + (new Character('t')).toString() + (new Character('e')).toString() + (new Character('r')).toString() + (new Character('e')).toString() + (new Character('d')).toString());
label3.setText((new Character('A')).toString() + (new Character(' ')).toString() + (new Character('C')).toString() + (new Character('e')).toString() + (new Character('r')).toString() + (new Character('t')).toString() + (new Character('a')).toString() + (new Character('i')).toString() + (new Character('n')).toString() + (new Character(' ')).toString() + (new Character('P')).toString() + (new Character('r')).toString() + (new Character('o')).toString() + (new Character('g')).toString() + (new Character('r')).toString() + (new Character('a')).toString() + (new Character('m')).toString() + (new Character(' ')).toString() + (new Character('I')).toString() + (new Character('s')).toString() + (new Character(' ')).toString() + (new Character('D')).toString() + (new Character('e')).toString() + (new Character('t')).toString() + (new Character('e')).toString() + (new Character('c')).toString() + (new Character('t')).toString() + (new Character('e')).toString() + (new Character('d')).toString() + (new Character('.')).toString() + (new Character(' ')).toString() + (new Character('U')).toString() + (new Character('n')).toString() + (new Character('l')).toString() + (new Character('o')).toString() + (new Character('a')).toString() + (new Character('d')).toString());
}
}
Ok, a big chunk of code but you can look at the for statement marked
as the routine that generate the code.
Here we compute the code from the name. The algo it's very simple: take
a char from name, get it's numerical value (UNICODE VALUE, IT'S VERY VERY
IMPORTANT), and append it to a correct string...
Then we compare the serials and so on
I have to underline that numerical values are UNICODE and NOT ASCII so
the value of 'A' isn't 65 but 10, and also 'A' it's equal to 'a'.
To compute the serial i've made a little program in VJ++ that do the work
for me but you can take a piece of paper and do some maths with your
brain... it's not so hard...
Then insert the code int the command line (try with mine:
Name: Scully
Code: 820114
From the DOS prompt digit 'crkme1 Scully 820114' and the program says it's
registered...
ANOTHER CONSIDERATION
=====================
Have you noticed that isRegistered() function and the boolean registered?
No? Really bad...
Only for who of you that want to know all the protection scheme here is
the explanation of that boolean variable:
Before parsing the command line the program check for a file named
'systen.ini' (note the 'n' instead of 'm') in the windows directory and