PDA

View Full Version : Keygen Attempt


personmans
February 25th, 2008, 17:33
I have been working on making a keygen for a target (which I know I can't name here)
(See update below for more info)
Code:

Working on it I've got this for the serial scheme:
MTWE60
[XXXXXX]
Version Number String
01
[XX]
Single vs Site license 01 = site 00 = single
(DASH)
0000001
[XXXXXX]
6 digit number, unknown use.
(DASH)
8ZAB5
[XXXXXX]
5 character string, based on the other two parts.

This is a working serial (MTWE601-000001-8ZAB5) with my description of each part.


If anyone has a few minutes to take a look at my target, it is not packed and has no crypto so it should be quick. I would just like to understand how the serials are being generated/used. I can provide the UDD files with my comments and breakpoints if needed. I just want to learn.

Thanks,
Personmans


UPDATE:
Just to update, I traced ~3 calls deep and found how the last 5 characters are generated. From here I could just brute force a serial every time. The Middle 6 digits (must be numbers) seem to just be random... I'm guessing this makes for the ability to have random looking serials, as well as 6 digits being enough to supply many many unique serials. However, like I said, I'm here to learn, so on I go!

Now in the generation scheme there are two large numbers used (Which I don't have right now) but they are undoubtedly based on the first and second part of the serial. I have traced the general area looking for when these numbers appear and they are both created a couple calls before the serial scheme.

Tracing ~5 calls deep into these I found myself very confused.
First, the use of a table in these calls gave me a LOT of greif because each time I encounter it, the thing just seems to be checking for alphanumeric input. (Why would they do it so redundantly? It happens like 4 times.)
Then I spent about 20 minutes understanding the first part of a call (with a loop and a few other things) only to see that it does not generate the serial, but right after there is another call to a function that looks the SAME... where the number is generated...

If anyone cares to see what I'm talking about, I've included just the loop from the last call that I spoke of below:
Code:

004C352A |> /833D D4475100 01 /CMP DWORD PTR DS:[5147D4],1
004C3531 |. |0FB6F3 |MOVZX ESI,BL
004C3534 |. |7E 0C |JLE SHORT MyTarget.004C3542
004C3536 |. |6A 04 |PUSH 4
004C3538 |. |56 |PUSH ESI
004C3539 |. |E8 63410000 |CALL MyTarget.004C76A1
004C353E |. |59 |POP ECX ; 00D89EF8
004C353F |. |59 |POP ECX ; 00D89EF8
004C3540 |. |EB 0B |JMP SHORT MyTarget.004C354D
004C3542 |> |A1 C8455100 |MOV EAX,DWORD PTR DS:[5145C8] ; This is the 'table'
004C3547 |. |8A0470 |MOV AL,BYTE PTR DS:[EAX+ESI*2]
004C354A |. |83E0 04 |AND EAX,4
004C354D |> |85C0 |TEST EAX,EAX
004C354F |. |74 08 |JE SHORT MyTarget.004C3559
004C3551 |. |0FBECB |MOVSX ECX,BL
004C3554 |. |83E9 30 |SUB ECX,30
004C3557 |. |EB 32 |JMP SHORT MyTarget.004C358B
004C3559 |> |833D D4475100 01 |CMP DWORD PTR DS:[5147D4],1
004C3560 |. |7E 0B |JLE SHORT MyTarget.004C356D
004C3562 |. |57 |PUSH EDI
004C3563 |. |56 |PUSH ESI
004C3564 |. |E8 38410000 |CALL MyTarget.004C76A1
004C3569 |. |59 |POP ECX ; 00D89EF8
004C356A |. |59 |POP ECX ; 00D89EF8
004C356B |. |EB 0B |JMP SHORT MyTarget.004C3578
004C356D |> |A1 C8455100 |MOV EAX,DWORD PTR DS:[5145C8]
004C3572 |. |66:8B0470 |MOV AX,WORD PTR DS:[EAX+ESI*2]
004C3576 |. |23C7 |AND EAX,EDI
004C3578 |> |85C0 |TEST EAX,EAX
004C357A |. |74 4A |JE SHORT MyTarget.004C35C6
004C357C |. |0FBEC3 |MOVSX EAX,BL
004C357F |. |50 |PUSH EAX
004C3580 |. |E8 081A0000 |CALL MyTarget.004C4F8D ; A whole other call, just like this one
004C3585 |. |59 |POP ECX ; 00D89EF8
004C3586 |. |8BC8 |MOV ECX,EAX
004C3588 |. |83E9 37 |SUB ECX,37
004C358B |> |3B4D 10 |CMP ECX,[ARG.3]
004C358E |. |73 36 |JNB SHORT MyTarget.004C35C6
004C3590 |. |8B75 F8 |MOV ESI,[LOCAL.2]
004C3593 |. |834D 14 08 |OR [ARG.4],8
004C3597 |. |3B75 F4 |CMP ESI,[LOCAL.3]
004C359A |. |72 14 |JB SHORT MyTarget.004C35B0
004C359C |. |75 0C |JNZ SHORT MyTarget.004C35AA
004C359E |. |83C8 FF |OR EAX,FFFFFFFF
004C35A1 |. |33D2 |XOR EDX,EDX
004C35A3 |. |F775 10 |DIV [ARG.3]
004C35A6 |. |3BCA |CMP ECX,EDX
004C35A8 |. |76 06 |JBE SHORT MyTarget.004C35B0
004C35AA |> |834D 14 04 |OR [ARG.4],4
004C35AE |. |EB 09 |JMP SHORT MyTarget.004C35B9
004C35B0 |> |0FAF75 10 |IMUL ESI,[ARG.3]
004C35B4 |. |03F1 |ADD ESI,ECX
004C35B6 |. |8975 F8 |MOV [LOCAL.2],ESI
004C35B9 |> |8B45 FC |MOV EAX,[LOCAL.1]
004C35BC |. |FF45 FC |INC [LOCAL.1]
004C35BF |. |8A18 |MOV BL,BYTE PTR DS:[EAX]
004C35C1 |.^\E9 64FFFFFF \JMP MyTarget.004C352A

personmans
February 26th, 2008, 16:15
Sorry to double post, just updating and there's a lot of info in the first post already.

Basically if you do:

Code:

#include <iostream>
using namespace std;

//Every letter above F to be treated as (ASCII-37h), 0-F to be treated as Hex
void fixHex(char *hexAry,int SIZE)
{
int fix;
for(int x=0; x<SIZE;x++){
if ((hexAry[x] >= 'G')&&(hexAry[x] < 'Z'))//letters betwen G-Z
{
hexAry[x] = (hexAry[x]-0x37);
}
else if ((hexAry[x] >= 'A')&&(hexAry[x] < 'G')) //letters between A-F
{
fix = hexAry[x]-55;// -55 makes 'A' = 10d
hexAry[x] = fix; // stores the Dec value for A back in array
}
else if ((hexAry[x] >= '0') && (hexAry[x] <= '9'))//Digits
{
fix = (hexAry[x]-48);
hexAry[x] = (fix + 0x0); //The 0x0 is like type-casting for dec->hex
}

}
}

//Add values of array and *24h them.
long calcSerial(char *fixedAry, int SIZE)
{
long sum = 0x0;
for(int i=0; i<(SIZE);i++)
{
sum *= 0x24;
sum += (fixedAry[I]);

}
return sum;
}


int main(){
char serialPart1[] = "WE601"; // First part of serial
static int SIZE1 = (sizeof(serialPart1)-1);
fixHex(serialPart1,SIZE1);
cout << "First part: " << hex << calcSerial(serialPart1,SIZE1) << endl;
///////////////////////
char serialPart2[] = "002521"; // Second part of serial
static int SIZE2 = (sizeof(serialPart2)-1);
fixHex(serialPart2,SIZE2);
cout << "Second part: " << hex << calcSerial(serialPart2,SIZE2) << endl;

return 0;
}

(Looking above I'm using the first part of my serial)
This code generates my "long numbers" that I was getting.

All that block of code from the other post did was multiply by 24h then add the next character's hex value. If it was a letter (after F) you convert to ASCII and subtract 37.


Little more info. The 6 digit part is restricted based on which type of user you are. Single user licenses can be up to 100000 (decimal) for the middle number, while the multi user licenses only go up to 012000 (decimal). If one was so inclined they could generate every possible serial.

One step left to keygen. =]

blurcode
February 26th, 2008, 19:19
test[I] + 0x0 ?

naides
February 27th, 2008, 08:26
Quote:
[Originally Posted by personmans;72984]

Basically if you do:

Code:

void main(){
char test[] = {'W',0xE,0x6,0x0,0x1}; //sets test = "WE601"




I think what you posted is only pseudocode, but watch out: 0xE is not equal to 'E'
nor 0x1 is '1' .
So

char test[] = {'W',0xE,0x6,0x0,0x1}; // WILL NOT sets test = "WE601"

Think about the differences between hex numbers and their char equivalents

0xE, 0x1 are a non printable characters, 'E' is 0x45 or dec 69. '1' is 0x31.

personmans
February 28th, 2008, 10:51
Quote:
[Originally Posted by blurcode;72986]test[I] + 0x0 ?


Yes, 0x0 to make it a hex value, I was getting incorrect values otherwise. It's almost like type-casting.

naides, this code produces the correct results, I compiled and tested. Of course, as you can see it's hardcoded to just the first part of the serial. The thing is 0xE and 0x1 are for the hex values, so E would be treated as 14d.

This is why I used char test[] = {'W',0xE,0x6,0x0,0x1};
Instead of char test [] = "WE601";

The scheme only uses char (ASCII values) for letters and only if greater than F, because 0-F are just treated as regular hex numbers. Sorry if my comments were misleading.

Note: Also, the code above is C++ (Well it was compiled with a C/C++ compiler, anyway)

owl
February 28th, 2008, 13:26
I guess I am as confuse as naides. E=0x45 and 1=0x31 not 0xE and 0x1. Although, if you use WE061 as charcters string, you can still access the individual hex values by using %X, or by moving the individual values to a int variable.

personmans
February 28th, 2008, 13:40
Quote:
[Originally Posted by owl;73014]I guess I am as confuse as naide. E=0x45 and 1=0x31 not 0xE and 0x1. Although, if you use WE061 as charcters string, you can still access the individual hex values by using %X, or by moving the individual values to a int variable.


You seem to have the right idea.


EDIT:
I don't think the %X can be used here, as I don't want the ASCII value of anything that is already a valid hex character, I just want the hex value of it.

The reasoning for my code is that I wanted a character array with the values in hex... so really 'W' is the odd man out, not 0xE, 0x6, etc...

Also, updated my code again, hopefully easier to understand now? --definitely more flexible code.

tHE mUTABLE
February 28th, 2008, 21:55
Quote:
Note: Also, the code above is C++
. In C++ main should return an integer and not void. You still attached to C

personmans
February 29th, 2008, 16:25
Quote:
[Originally Posted by tHE mUTABLE;73022]. In C++ main should return an integer and not void. You still attached to C


Sloppy code is my friend

I actually learned both languages at the same time, I just used void because I didn't need a real return value when all my code was in the main. I fixed that now. Still not pretty looking code though.

Personmans

Also, thank you guys for pointing out things to me. I learned Java/VB6 first, so my C++ is still terrible at times, just let me know what's wrong and I'll fix it.


One last edit to sum things up (I know it's a bit late, but whatever)

So the numbers (from above) are calculated, then multiplied by each other, then this number is divided by (44099087d) , and set equal to the remainder.
That is then added to (1336337d)-- I'm not sure why, but it is.
Finally this left over number is divided by 34d, and the remainder is saved as a character. These characters (5) are the last 5 in the serial. Last, o's are changed to Z's and i's become Y's. Then you have a valid serial.

Also if anyone wants the target, PM me. (even if thread is outdated, I don't mind)
Happy Reversing.