IDA
Hex Editor
Reflector (free .net disassembler, it's not required, but to follow a long it makes it far easier)
download the crackme from here: http://www.crackmes.de/users/raham2755/sep..._not_protected/
First open it in reflector and check .ctor() to see what functions are first loaded, initializecomponent is this function, go there
now aside from the fact that button 5 is never given an event handler, i will get to that later, but the author should have fixed this before posting.
double click .ctor() and go near the bottom and you will see base.Load += new EventHandler(this.Form1_Load);
this means that function Form1_Load is going to execute when the program does, so go there now
here you will see mostly things that do not concern us because we are not keygenning
find this:
bool flag = false;
flag = Scanner.ScanFile(Environment.CommandLine.Substring(1, Environment.CommandLine.Length - 3));
Crypt crypt = new Crypt();
crypt.STR1 = "True";
crypt.STR2 = flag.ToString();
long diff = crypt.GetDiff();
try
{
long num3 = 0x43L / diff;
}
catch
{
this.regPanel.Visible = true;
this.panel1.Visible = false;
}
}
this line of code is important
flag = Scanner.ScanFile(Environment.CommandLine.Substring(1, Environment.CommandLine.Length - 3));
this calls the PEScan.dll file to get a sha-512 hash of the file and compare it to a locally stored string that the designer put in, now we could go in and hash shit and see if it works, but that's a waste of time, go below that
now we see crypt.STR1 and STR2 are set to compare them using GetDiff()
STR1 it set to "TRUE" and STR2 is set to the string equivalent of the boolean flag, so we want true as well, remember that for when we modify the file, we need it to load the regpanel and not panel1 with the badboy message.
back in initialize component you see that button 5 is set as this.button5.Text = "Register";
so the function button5_Click is what we want to check out, go there now in reflector
again, skip the top stuff, it doesn't matter, check near the bottom
crypt.STR1 = txtToCode;
crypt.STR2 = str4;
long diff = crypt.GetDiff();
int num2 = 0;
try
{
num2 = Convert.ToInt32(Math.Log(Convert.ToDouble(diff)));
}
catch
{
num2 = 1;
}
bool flag = Convert.ToBoolean(diff);
bool flag2 = false;
try
{
long num3 = 60L / diff;
}
catch (DivideByZeroException)
{
flag2 = true;
}
this.tmrDisable.Enabled = flag;
this.tmrRegister.Enabled = flag2;
}
there are 2 ways to crack this, we can change the STR1 and STR2 to equal the same strings like in the Form1_Load function, or we can do it the way i did (the easy way)
down below you see:
this.tmrDisable.Enabled = flag;
this.tmrRegister.Enabled = flag2;
well if you check the tmrDisable object's event handler, you will see this gives us the badboy message on startup saying "No.its diffrent to last time :-P"
and the tmrRegister event handler says "The Game is overed again. :((" (this is the good boy message as it's in green, even though there is a frown)
flag2 is set to false meaning that it is not registered, and it will only register if the 'diff' variable (return from crypt.getDiff() above) is a 0 (meaning they are the same strings)
very simple, change flag2 to true and flag to false like this
bool flag2 = true;
try
{
flag = false;
}
catch (DivideByZeroException)
{
flag2 = true;
}
this.tmrDisable.Enabled = flag;
this.tmrRegister.Enabled = flag2;
}
now as far as i know, you can't actually change anything using reflector, so you have to load it into IDA and check the functions window (default top right) for the same function names, then look for the right locations that compare to the source code above.
for the button5_click function it can be difficult as there aren't many strings indicating anything, but have a look for strings you can find like "myReg" and "hey" first right click in the background of IDA View-A and select Text View
scroll down a little bit and you should see those strings along with "NoneBuddy" (you see that in reflector too) now IDA is nice in that it will show the try{} and catch{} statements for us, so look for the try with a catch that has the DivideByZeroException with it
you should see it just below this:
stloc.s 8
ldc.i4.0
stloc.s 9
now we want the 0 (which means false) to be a true, so we need it to be a 1, now unless you have knowledge of .NET assembly instructions and their bytes that match, how do we know that to change the 0x16 to at offset 0xBA0 ?
well we know that ldc.i4 0 = false right? so look for something that is true, should be ldc.i4 1 :) once you find that you will know that 16 makes it a 0 and 17 makes it a 1
now that flag2 is changed (which gives us the good boy message) we need to fix the other flag variable to be false or our change to true won't matter.
below the try we have basically a confusing IF statement in a try/catch, try to divide a number by the 'diff' variable and if 'diff' is a zero it will throw the exception and set the flag2 as true, well we already did that, so lets change all the code in the try{} statement to modify the flag variable to be false
now in .net, unlike almost all other languages, a 0x00 byte = NOP (no operation point) so we want to NOP out everything inside the try{} statement EXCEPT the leave.s LOC xxx this is because the leave.s LOC points to the location it should jump to, if the previous instruction does not throw the divide by zero exception
it should be offsets 0xBA4 to 0xBAC, now we just need to insert the 0 for the flag variable, but how do we know what instruction to put for modifying the different variable? well that's easy
above, we changed the value for flag2 to be 1 instead of zero, well just above that is the modification of the flag variable, and you will see that it uses stloc.s 8 instead of flag2 that uses stloc.s 9
so now we know what opcodes we need :)
use these: ldc.i4.0 stloc.s 8 and in hex these are as follows 0x16 0x13 0x08
change the following offsets/bytes in a hex editor
-offset- | -bytes-
0xBA0 16->17
0xBA4 1F->00
0xBA5 3C->16
0xBA6 6A->13
0xBA7 11->08
0xBA8-0xBAC make all 0x00
now we have to go back to the form1_load function and fix the two strings to always be true, otherwise on the load, it will use the PEScan.dll file to check if any modifications have been made, and give us an even worse badboy message.
in IDA go to form1_load and use the same method we used before to try and find the right location, we know that STR1 = "True"; is there, so look for that string and we will be right by our location
now it gets tricky, you should see this in IDA View-A when you find the "True" string:
stloc.s 9
ldloc.s 9
ldstr "True"
stfld class System.String Sepanta_Security_Lock.Crypt::STR1
ldloc.s 9
ldloca.s 8
call class System.String [mscorlib]System.Boolean::ToString()
stfld class System.String Sepanta_Security_Lock.Crypt::STR2
ldloc.s 9
here can be difficult to figure out, i had a few crashed .exe files and improper functions before i got it right, the problem is that the auther typecasts a boolean to a string and then sets that as STR2
a good way to look at this is calling a function inside a function, in the source code you see this:
crypt.STR2 = flag.ToString();
but a computer can't assign a variable like that directly, because it's in a different class, so it calls the system.string function with the parameter we say (pushed on the cpu stack)
so first it has to push the location of the string we want to end on (STR2) to the stack, then it has to push the location of the variable we are acting on to convert to a string (flag) onto the stack using ldloca.s 8
if we compare it to the function above for STR1, the only pieces we should need are the stfld instruction and the ldloc.s 9 instruction, these are the only 2 that are common between them, so they are the only 2 we should have to use (if not, we always have backups right?
so NOP out the ldloc.s 9 and change the ldloca.s 8 to ldloc.s 9, then we have to change the call to ToString() to use the "True" string
check the hex bytes of the above ldstr "True" by clicking on it, and selecting Hex View-A and it will highlight the bytes you need, there should be 5 in total
now do the same for the call to ToString() and it should use the same amount of bytes (lucky us)
change the call from what it is, to the EXACT same bytes as above, and it should load STR2 with "True" so this way the call to GetDiff() will return a 0 and will think that the file has not been modified on startup
offset changes:
go to 0x76E
change from:
0000076eh: 11 09 12 08 28 2F 00 00 0A
to:
0000076eh: 00 00 11 09 72 DD 00 00 70
now because the author didn't put an event handler on the button5 (register) you have to fix that as well, just change the bytes below and it will work ok, if there is a demand for it, i will explain how i figured this little tidbit out as well :)
at offset 0x111C
from:
0000111ch: 7B 0D 00 00 04 02 FE 06 13
to:
0000111ch: 7B 11 00 00 04 02 FE 06 10
enjoy
-mazuki