INTRO / INFO | |
---|---|
PROGRAM | PhRoZeN CReW '97 CrAcK ME #2 |
TOOLZ | NuMega Softice 3.22, a brain |
HANDY REFZ | HelpPC, Win32 API Docco |
RESULTZ | This docco, my Keygen |
THANKS | To Sandman'sawesome RCE site. This man's page is just awesome for all crackers (beg-pro). Without that page, I wouldn't have cracked this little proggie. |
ABBREVIATIONS | SI - Softice ML - Memory Location HV - Hexadecimal Value AV - ASCII Value WTF - What The Fuck!? |
ESSAY | Shows ya how to crack this little baby, and helps with the logic of writing a KeyGen. I was going
to actually write the source code to the my keygen in here also, so that you could see it. But i decided
to let you guys try and figure it out. I'll just point you in the right direction. If you want to see the
source, please mail me at karnak_thecracker@hotmail.com
All SI commands that i used i'll put in
RED. Before i say any more, i must warn people that this essay is for learning ONLY. You won't gain access to an amazing application, all you will gain is knowledge. This essay will focus on making a key generator because patching this baby would be a piece of piss and would be no challenge at all. Anyway, keygen's are by far the better solution for any situation. This essay is targetted at the REAL beginners (just like me), it basically assumes that you know what softice is, and what a breakpoint is, and hopes that you know at least a little bit about x86 assembler. Softice work is a must, so if you dont know much then you'll find tute's on the web. So go search. The average cracker will probably find that this essay is shit boring. The reason I wrote it like this was because I wanted to find something like this myself, but could never seem to find anything like it. So i thought that i'd follow my own request, and create something that all aspiring crackers can read, understand and learn from. |
THE GOODS | |
OK, now its time to get down to the nitty gritty. This is the first time i've ever
tried to write a cracking essay etc.. so please bear with me if its shit. OK. Upon loading the proggie we can see that the protection is basically a nice little name/serial. I chucked in a username (i used 'karnak'), and a serial (i used 12345). Put in something easy to recognise like WAHOO and 666111666. After entering those details in, start up SI (CTRL+D) and i whack in BPX GetDlgItemTextA (as most beginners do) in the hope that the program uses that windows function to get the text from one of the edit controls. Jump back into windoze ( CTRL+DBC 0. OK, so we need a lower level function that is used to get text out of the edit controls. At this point I decided to try 'HMEMCPY'. Why?? Coz i remembered seeing it in a cracking tutorial WAAAAAAYY back when I first got interested in this stuff. Anyway, it doesn't matter why at this point, all that matters is that it uses it. So back in SI again stick in the breakpoint BPX hmemcpy, and jump back into windows. Press the OK button and ... WHOOSH ... SI should pop right up in front of you. Good stuff, now we know that the program is starting to get the text from (one of) the controls. This function was called deep down in the dark depths of the windows library, so that's basically where we are currently sitting. Press F11 to take you to the point after the calling of the function, and you'll notice that were are located in part of the USER32 code, keep pressing F12 until you reach the Crackme2 text (you'll go through KERNEL32 also). To make sure you know what i'm doing, this is a quick rundown so far:
- pressed OK in app OK, at this point in time it's probably a good idea to get the HV for each character in your name, coz alot of protection schemes use them. I used HelpPC to get mine. Anyway the HV for my username is 6B 61 72 6E 61 6B, and for reference I got the HV for my password too, which is 3039 from this you can get the decimal values. OK, you should be looking at the assembler code that has just grabbed the text from the first of the edit controls ie. your password (12345 in my case). Can't see it?? OK, if you look at the contents of the EAX register ... hmmmm 3039 :-) Strange how thats the HV for my password dont you think? Right, so that function has just grabbed the password. But where the hell is the username? Lets take a look at a snippet of code from SI: |
|
:004012F1 90 NOP :004012F2 E85F000000 CALL 00401356 <--- Hmm, interesting :004012F7 6A00 PUSH 00 :004012F9 6A00 PUSH 00 :004012FB 6A66 PUSH 66 :004012FD FF7508 PUSH DWORD PTR [EBP+08] :00401300 E821010000 CALL USER32!GetDlgItemInt <--- Hmm, interesting :00401305 A3D2214000 MOV [004021D2],EAX <--- we are here :0040130A 3C00 CMP AL,00 :0040130C 743F JZ 0040134D :0040130E 90 NOP |
|
OK, from this little snippet we can see a few things: The actual function that extracted our password was 'GetDlgItemInt'. That is what put the value into EAX. Ok guys, enough of this Hmemcpy stuff, lets break on that baby. So in SI we go BC * to get rid of all the breakpoints we have, then BPX GetDlgItemInt. OK, for now lets just disable that breakpoint, BD 0 as there is something a little more interesting before we carry on. Take a look at ML 004012F2. Seems we have invoked some sort of function/procedure. Now me being the way that I am, if I had coded this thing I would have grabbed the username first. So I guessed that this function could be the lad that does just that. So in SI lets breakpoint on that function call - BPX CS:00401356. Note the 'CS', this makes sure we are pointing to the code segment rather than any other segement (eg Data). Righto, now go back to the app and press OK again. AH HA! SI jumps up and we are right at the start of the function. So lets have a look at this code: |
|
:00401356 33DB XOR EBX,EBX <--- we are here :00401358 8A1DD1214000 MOV BL,[004021D1] :0040135E B108 MOV CL,08 :00401360 2ACB SUB CL,BL :00401362 BFA9214000 MOV EDI,004021A9 :00401367 6603FB ADD DI,BX :0040136A B030 MOV AL,30 :0040136C AA STOSB :0040136D FEC0 INC AL :0040136F E2FB LOOP 0040136C :00401371 C60700 MOV BYTE PTR [EDI],00 :00401374 C3 RET |
|
This is quite a simple little doozy. I'll just go through this code briefly. First EBX is cleared,
and a valued is loaded into the lower byte of the lower 16 bits of that register. Hmm, is it just me? Or is that value
the same as the number of chars in your username? Nah, its not just me :-) So at this point I guessed that they must
have already got the value of our username and stored it somewhere. But where? And When? Well, we'll find out the former
in just a moment, so we aren't going to care about the latter. Notice the code at ML 00401362? Its loading an address
in the EDI register. So lets check out that address by typing in SI DD DS:004021A9. Ah!!
Marvellous, there it is! Guess this means we dont need to know where/how we actually extracted this, coz we now know
where its kept. OK, now make sure you keep that ML on hand, coz you'll see it a fair bit more later on. Anyway, that's not all that is going on here. After loading the amount of chars in our name into BL, we seem to be doing something else that is interesting. In a nutshell we are going CL = 8 - BL where BL = length of our name. If I have to go as far as to spell that snippet out any more, then you obviously don't know enough assembler and shouldn't even be reading this yet! So if that's the case, piss off and learn some more and then come back. OK guys, reading through the rest of the code in this little func we can see that we are moving the DI register to point to ADDRESS_OF_NAME + LENGTH_OF_NAME ie. to the ML directly after our name, and moving the HV of 30 into the AL register. So what is 30? Umm.. my guess is the HV for the character '0' (zero). OK, this bit I found a bit trickier, coz at the time I didn't know what the STOSB instruction did, so I just followed the contents of the MLs and figured it out that way. You are luckier my friend!! I'll tell you what this instruction means in case you dont know it. STOSB = MOV [EDI], AL; INC EDI;
Pretty groovey huh! After my short days of assembler in UNIX I never thought i'd see something like this! Good stuff
Intel! Right, so what the hell are we doing anyway? We are looping CL times. For me that would be 8 - 6 = 2 times. Each
time we loop we are moving the current value of AL into the memory location pointed to by EDI. All nice and clear?? Thought so :-) So we now have the value karnak01 (HV 6B 61 72 6E 61 6B 30 31) stored in ML 004021A9. At this point we jump back to the previous section of code, and to avoid having to constantly scroll up and down, i'll paste the code in again just here: |
|
:004012F1 90 NOP :004012F2 E85F000000 CALL 00401356 <--- Just came back from here :004012F7 6A00 PUSH 00 <--- we are here :004012F9 6A00 PUSH 00 :004012FB 6A66 PUSH 66 :004012FD FF7508 PUSH DWORD PTR [EBP+08] :00401300 E821010000 CALL USER32!GetDlgItemInt <--- Disabled breakpoint :00401305 A3D2214000 MOV [004021D2],EAX <--- hmm, interesting :0040130A 3C00 CMP AL,00 :0040130C 743F JZ 0040134D :0040130E 90 NOP |
|
Ok guys, as we can see, we then extract the integer value of our password and store in ML 004021D2.
After doing this, we do fuck all (JMP) if the extracted password integer value is zero. AH! All nice and clear? I sure
hope so, if you can't follow this essay then you really need some help :-) Righto, we've got our password and username, now what are we going to do with them? Take a look a bit further down and we find: |
|
:00401311 90 NOP :00401312 E85E000000 CALL 00401375 <--- hmm, interesting :00401317 E8A5000000 CALL 004013C1 <--- hmm, interesting :0040131C 85C0 TEST EAX,EAX <--- hmm, interesting :0040131E 742D JZ 0040134D <--- hmm, interesting :00401320 90 NOP :00401321 90 NOP :00401322 90 NOP :00401323 90 NOP :00401324 6A00 PUSH 00 :00401326 689F214000 PUSH 0040219F :0040132B 683A214000 PUSH 0040213A :00401330 FF7508 PUSH DWORD PTR [EBP+08] :00401333 E806010000 CALL USER32!MessageBoxA :00401338 FF7510 PUSH DWORD PTR [EBP+10] :0040133B FF7508 PUSH DWORD PTR [EBP+08] :0040133E E8D1000000 CALL USER32!EndDialog :00401343 B801000000 MOV EAX,00000001 :00401348 EB08 JMP 00401352 :0040134A 90 NOP :0040134B 90 NOP :0040134C 90 NOP :0040134D B800000000 MOV EAX,00000000 :00401352 C9 LEAVE :00401353 C21000 RET 0010 |
|
I put a fair bit of code in here so we can see the basic principal behind the proggie. Near the
top of this code we have calls to 2 functions, and then we have a test and a jump. OK, from this we can see that
if after the 2 functions if EAX is NOT zero then we throw up a Message Box, but if its zero what happens? That's
right .. fuck all! Don't know about you, but up until now, all that's happened to me when I press OK is ...
FUCK ALL! So we put 2 and 2 together and decide that to throw up this message box we need to make EAX something
other than zero. "But I wanna see this message box now!" I hear you cry. Well if you really want to see it now, step through the code using F10 until you reach ML 0040131E. At this point we can see that the Zero Flag is true, so if we change that to false then we should jump. So at this point type R FL Z, and this switches the value in the Zero Flag. Righto, CTRL+D and VOILA! Ahhhh, thats what we want isn't it ;-). OK, those of you who who wish to stop here and just patch the proggie so that this always happens, go ahead and NOP out the JZ and you'll be right, but I wont be showing you how to do that coz I don't approve of such an easy fix in this instance. Those battlers out there that wanna learn how to figure out this algo and write a keygen should keep reading, you'll get alot more satisfaction out of cracking the program without having to alter one part of it :-). OK, lets take a look at the code in that first function F10 until you reach the function call and then F8 to step into the function as see this: |
|
:00401375 33C9 XOR ECX,ECX <--- Start Of Function :00401377 BEA9214000 MOV ESI,004021A9 :0040137C 8BFE MOV EDI,ESI :0040137E AC LODSB <--- WTF?! :0040137F F7D1 NOT ECX :00401381 8A99B1214000 MOV BL,[ECX+004021B1] :00401387 F7D1 NOT ECX :00401389 02C3 ADD AL,BL :0040138B AA STOSB :0040138C FEC1 INC CL :0040138E 80F904 CMP CL,04 :00401391 75EB JNZ 0040137E :00401393 B904000000 MOV ECX,00000004 :00401398 BEA9214000 MOV ESI,004021A9 :0040139D 8BFE MOV EDI,ESI :0040139F 83C704 ADD EDI,04 :004013A2 F2A4 REPNZ MOVSB <--- WTF?! :004013A4 BEA9214000 MOV ESI,004021A9 :004013A9 8BFE MOV EDI,ESI :004013AB B908000000 MOV ECX,00000008 :004013B0 AC LODSB :004013B1 F7D1 NOT ECX :004013B3 8A99DF214000 MOV BL,[ECX+004021DF] :004013B9 F7D1 NOT ECX :004013BB 32C3 XOR AL,BL :004013BD AA STOSB :004013BE E2F0 LOOP 004013B0 :004013C0 C3 RET <--- End Of Function |
|
Firstly, I'll just explain the WTFs:
LOSB = MOV [ESI], AL; INC ESI;
This code is pretty tricky to follow if you dont really understand assembler, so I'm going to do my best explaining
it without completely boring myself to death! The first 3 lines clear one register, and load our username into both
the ESI and EDI registers. We then iterate 4 times (CMP CL,04) through ESI, and add the HV of the character in our
username (that is being pointed to by ESI) to something else. So what is that something? Well, we have a counter going
from 1 to 4, but during that time we are using the NOT of this counter to extract other values. This loops through -1
to -4 also, and uses this as an offset to 004021B1, this result is the address of an ML in which a character is
fetched from. Now if we look close enough we can see that 004021B1 = 004021A9 + 8. So what the hell are we doing?
We are looping through the first 4 and last 4 chars of our name, and adding them together.
1st time through: karnak01 ^ * EDI = ^ + * 2nd time through: karnak01 ^ * EDI+1 = ^ + * 3rd time through: karnak01 ^ * EDI+2 = ^ + * 4th time through: karnak01 ^* EDI+3 = ^ + *So in my case I have the values 9C 91 DC CF. We then reset EDI and ESI with the location of our username, add 4 to EDI, and loop 4 times (counting with ECX, incrementing EDI) and copy from ESI to EDI. This means that we no longer have karnak01 in that ML, rather we have the values 9C 91 DC CF 9C 91 DC CF Righto, at this point the program gets ready to muck around with these generated values. Again we reset ESI and EDI to our username ML and set ECX to 8. Using LODSB we iterate through the memory location starting at 004021DF, and are XORing the values in that location with our other generated values. All of a sudden this isn't looking so hard is it? We store these values in EDI as we go (ie the same original location that our username was in) through this loop. After the loop my values are CC F9 AF A0 C6 F4 B3 9E. Well done guys, you've just figured out what the hell is going on with your username, and are now ready to piss around with the password. You've gone through most of the harder stuff. Now, lets have a look at the other function F12 to run until the next RET statement and then F8 to step into the next function and see this: |
|
:004013C1 A1D2214000 MOV EAX,[004021D2] <--- Start Of Function :004013C6 8A0DA9214000 MOV CL,[004021A9] :004013CC D3C0 ROL EAX,CL <--- WTF?! :004013CE 8B0DAE214000 MOV ECX,[004021AE] :004013D4 33C1 XOR EAX,ECX :004013D6 8B1DAA214000 MOV EBX,[004021AA] :004013DC 8AC8 MOV CL,AL :004013DE D3CB ROR EBX,CL <--- WTF?! :004013E0 33C3 XOR EAX,EBX :004013E2 B800000000 MOV EAX,00000000 <--- BAD!!! :004013E7 7506 JNZ 004013EF :004013E9 90 NOP :004013EA 90 NOP :004013EB 90 NOP :004013EC 90 NOP :004013ED FEC0 INC AL <--- Good :-) :004013EF C3 RET <--- End Of Function |
|
As per usual i'll explain the WTFs:
ROR X, Y = Roll bits in X right Y times
eg. OK, lets see whats happening at the start of this little proc. We can see that we are loading the value of our password into EAX, and load the first of our generated values (in my case CC - I'll refer to this later as ZVALUE) into CL. Then we roll our EAX bitmask left CL times. So our first generated value is being used as a roll counter. Next we are putting the last 3 values of the generated code (in my case 9E B3 F4 - I'll refer to this later as XVALUE) into ECX, and XORing them with the now rolled value in EAX. Now we load into EBX the generated values 2-5 (in my case C6 A0 Af F9 - I'll refer to this later as YVALUE), and MOV the current LAST value of EAX into CL (MOV CL, AL). This value is then used as a roll counter for the values in EBX. So EBX's bitmask is rolled left CL times. Now comes the big test. By looking at the code we can see that if EAX and EBX are different, then we set EAX to zero and jump to the end of the routine. If EAX == EBX then set EAX to zero, skip the jump and then increment EAX. THIS IS WHAT WE WANT! Remember? After returning to this function, EAX is tested, and if its zero then we skip the message box. So we want the INC AL instruction to run! Therefore we now have to find a way to make EAX = EBX and we're done!
|
|
THE KEYGEN | |
OK people, congratulations, you've managed to stick with me throughout this lovely ordeal, and now know what the
program is doing with your username and password. Now comes the tricky bit, working the KeyGen :-). Before we start
working on the code its important to take a step back and try and see how we would approach this. It's not just a
simple matter of writing a quick program, firstly we have to try and get our heads around the logic first so that
when we code it, we know its behaving. So here's the program's key testing algorithm (in a C-ish Psuedo):
FirstValue = PassWord RolledRightBy ZVALUE FirstValue = FirstValue XOR XVALUE OtherValue = LastHexValue in FirstValue SecondValue = YVALUE RolledLeftBy OtherValue If FirstValue == SecondValue Then WAHOO else BOOHOO end ifKeeping up? Sure hope so, now this was the deciding factor for me. This was the hardest part of the whole process. I knew what was going on, but for some reason i couldn't think of how the hell to do this process backwards. Why? Well when i first looked at it I thought "Fuck, this thing uses not only a mixture of generated values, but a mixture of parts of those generated values to get OTHER generated values. At first it just seemed way over my head, but then the 'Logical' Karnak woke up and took a look. If we have a look at the SecondValue we can see that the only thing thats going on is a ROL. We know YVALUE, but we dont know OtherValue. OK, but since we are only rolling this value, the actual bitmask AFTER the roll is still the same bitmask, just swivelled a bit. So, if we think about it, OtherValue's bitmask must also be just a swivelled version of YVALUE. We just need to find out the extent of the swivel. With this ROL function, there are only 32 possible different orders for one bitmask (since we're dealing with 32 bits). Its the last 8 bits of the bitmask that tell us the ROL anyway! So, if we grab the YVALUE, and roll it by one bit (creating PVALUE), we grab the last 8 bits of PVALUE, and roll YVALUE by those 8 bits (creating QVALUE). If QVALUE == PVALUE then we can just do the reverse process of FirstValue on PVALUE and we'll get a serial! I know its hard to get the mind round, but read it a few more times if you are having a bit of trouble understanding it, and you'll soon see what i mean. OK guys, that about concludes my first essay. I hope you have found it as informative as i planned it to be. As already said i thought i'd leave you guys to try and figure out the source for the keygen, coz its a very good excercise. If you are really desperate and want to see my source please send a mail to the address at the top of this page and let me know. Thanks for taking the time to read this essay! Happy Cracking! -=+ Karnak +=- |