Log in

View Full Version : odd behavior in a keygen crackme


tdennist
March 21st, 2005, 21:04
So, I (hopefully!) successfully reversed using OllyDbg this crackme that asks for a name and then calculates the serial from the name. Here's what I discerned to be the algorithm:

Code:

004012BA /$ 53 PUSH EBX ; 0
004012BB |. 56 PUSH ESI ; our serial
004012BC |. 57 PUSH EDI ; our name
004012BD |. BF 88304000 MOV EDI,keygen_#.00403088
004012C2 |. 33C0 XOR EAX,EAX
004012C4 |. 33DB XOR EBX,EBX
004012C6 |. 33C9 XOR ECX,ECX ; zero out our counter register
004012C8 |> 8A1C39 /MOV BL,BYTE PTR DS:[ECX+EDI] ; this will move into BL the letters of the name individually, depending on ecx our counter
004012CB |. 84DB |TEST BL,BL ; test to see if we have a letter
004012CD |. 74 08 |JE SHORT keygen_#.004012D7 ; jump if there are no more letters
004012CF |. 03C3 |ADD EAX,EBX ; add ebx to the value of eax
004012D1 |. 83C0 10 |ADD EAX,10 ; increase the letter val by 10. i.e. 74 becomes 84
004012D4 |. 41 |INC ECX ; increment our counter
004012D5 |.^EB F1 \JMP SHORT keygen_#.004012C8
004012D7 |> 50 PUSH EAX ; /push final serial to stack
004012D8 |. 68 76304000 PUSH keygen_#.00403076 ; |Format = "%X"
004012DD |. 68 D0304000 PUSH keygen_#.004030D0 ; |s = keygen_#.004030D0
004012E2 |. E8 1F000000 CALL <JMP.&user32.wsprintfA> ; \call wsprintf, storing final serial in keygen_#.004030D0
004012E7 |. 83C4 0C ADD ESP,0C ; clean up stack
004012EA |. 5F POP EDI
004012EB |. 5E POP ESI
004012EC |. 5B POP EBX
004012ED \. C3 RETN



With comments that I made to the side. So, I run the application in OllyDbg and try the string "tyler" as the name, and "280" as the serial, and lo and behold, it works! However, if I run the program from windows and try the same name/serial combination, it does not work.

(Also odd is that my keygen which I wrote in Perl generates the serial number "280" for the name "tyler", which apparently is correct, but when I try other generated serials, they don't work through OllyDbg OR Windows! Is it legal for me to post my Perl here, too, and have someone go over that?)

So you see, I've done my stuff...I found the algorithm (or what I believe to be it) and I made the keygen. In other words...don't go ballistic calling me a newbie and such ;-). I did what I think is right, and now I just have some questions.

Thanks!

bilbo
March 22nd, 2005, 03:10
Hi, tdennist,

Tyler? I love her... She is one of my best friends... She is our Elf princess!

The algorithm is ok: tyler->0x280

In C language (with a printf at every step, to see better the serial building process):
Code:

#include <windows.h>
#include <stdio.h>

void
main(void)
{
DWORD ret=0;
char *name = "tyler";

while (*name) {
ret += *name++ + 0x10;
printf("%x\n", ret);
}
}


Anyway, I suspect you are facing two problems here...

First problem:
Quote:
So, I run the application in OllyDbg and try the string "tyler" as the name, and "280" as the serial, and lo and behold, it works! However, if I run the program from windows and try the same name/serial combination, it does not work.
There must be some other name manipulation outside of this routine, which you are not aware of using a debugger... Try using an "hide debugger plugin"...

Second problem:
Quote:
When I try other generated serials, they don't work through OllyDbg OR Windows!
Are you sure you are building up your Perl result in a longword? Maybe Perl is wrapping inside a 16bit word, but the check is performed on 32bits? Just a speculation...

Best regards, bilbo

ZaiRoN
March 22nd, 2005, 06:30
Hi tdennist,
if you want you can share your crackme with us. We'll try to figure out the problem...

Regards,
ZaiRoN

tdennist
March 22nd, 2005, 18:36
My gosh...you can't imagine how frustrated that makes me that you were able to bang out that utterly simple keygen, when I went around and around first with C, then with C++, then finally with Perl, all with no luck. While I admit I am verrry very far from proficient in C/C++ (in fact, as it is I greatly prefer programming in ASM than either C or C++...they're just so RESTRICTING...), I like to consider myself at the very least proficient in Perl. Maybe I'll try my next one in Java....

But anyhoo.

As far as getting a "hide debugger plugin" is concerned...I have absolutely no idea where one would go to get one o' those, nor how one would go about using one. So...any suggestions?

I am indeed not sure what is wrong with my Perl. I'll post it here and anybody else who knows Perl mayhap can tell me what's wrong:

Code:

#!/usr/bin/perl
#keygen for keygen #1.exe

print "Name: ";
chomp($name=<STDIN>;

$serial = 0;
$ascletter = 0x0;

@letters = split(//,$name);

#doing all arithmetic in base 10, then at the end converting to base 16
foreach $letter (@letters) {
$ascletter = ord($letter);
$serial += $ascletter;
$serial += 16; # 16 = 0x10
}

printf("Serial! -> %x\n", $serial);


And as far as ZaiRoN's suggestion goes...I've attached it! Feel free to scan it for virii and such, but I'm fairly certain it's free of 'em.

Thanks for your time and help!

TQN
March 22nd, 2005, 21:58
You are right, bilbo !
The keygen uses IsDebugPresent to detect debugger.
Code:

.text:0040100C call IsDebuggerPresent
.text:00401011 cmp eax, 1
.text:00401014 jnz short ShowDlg
.text:00401016 push eax
.text:00401017 pop bDebuggerPresent

And at 004011C8, the keygen call one of two check function, according to bDebuggerPresent flag:
Code:

.text:004011C8 HaveNameAndSerial: ; CODE XREF: DoCheck+17j
.text:004011C8 mov eax, bDebuggerPresent
.text:004011CD cmp eax, 1
.text:004011D0 jnz short loc_4011D9
.text:004011D2 call GenerateFakeSerial
.text:004011D7 jmp short CompareSerial
.text:004011D9 ; ---------------------------------------------------------------------------
.text:004011D9
.text:004011D9 loc_4011D9: ; CODE XREF: DoCheck+37j
.text:004011D9 call GenerateRealSerial
.text:004011DE CompareSerial:

So the real generating serial code at 0040123C.
Code:

.text:0040123C GenerateRealSerial proc near ; CODE XREF: DoCheck:loc_4011D9p
.text:0040123C push ebx
.text:0040123D push esi
.text:0040123E push edi
.text:0040123F push 155h
.text:00401244 push offset DoCheck
.text:00401249 call CheckCRC32
.text:0040124E cmp eax, crc32ofDoCheck
.text:00401254 jz short Start
.text:00401256 call GenerateFakeSerial
.text:0040125B jmp short Done
.text:0040125D ; ---------------------------------------------------------------------------
.text:0040125D
.text:0040125D Start: ; CODE XREF: GenerateRealSerial+18j
.text:0040125D mov edi, offset nameBuf
.text:00401262 xor eax, eax ; eax = 0
.text:00401264 xor ebx, ebx ; ebx = 0
.text:00401266 xor ecx, ecx ; ecx = 0
.text:00401268 xor edx, edx ; edx = 0
.text:0040126A
.text:0040126A Loop: ; CODE XREF: GenerateRealSerial+49j
.text:0040126A mov bl, [ecx+edi] ; bl = nameBuf[ecx]
.text:0040126D test bl, bl ; bl == 0 ?
.text:0040126F jz short Check
.text:00401271 add edx, ebx ; edx += nameBuf[ecx]
.text:00401273 or eax, edx ; eax |= edx
.text:00401275 inc eax ; eax++
.text:00401276 rol edx, cl ; edx <<= cl
.text:00401278 not eax ; eax = !eax
.text:0040127A mov esi, offset realSerialBuf
.text:0040127F add al, [esi+4]
.text:00401282 inc ecx ; ecx++
.text:00401283 add eax, edx ; eax += edx
.text:00401285 jmp short Loop
.text:00401287 ; ---------------------------------------------------------------------------
.text:00401287
.text:00401287 Check: ; CODE XREF: GenerateRealSerial+33j
.text:00401287 push edx
.text:00401288 push 155h
.text:0040128D push offset DoCheck
.text:00401292 call CheckCRC32
.text:00401297 pop edx
.text:00401298 cmp eax, crc32ofDoCheck
.text:0040129E jz short Right
.text:004012A0
.text:004012A0 Wrong:
.text:004012A0 push eax
.text:004012A1 jmp short Format
.text:004012A3 ; ---------------------------------------------------------------------------
.text:004012A3
.text:004012A3 Right: ; CODE XREF: GenerateRealSerial+62j
.text:004012A3 push edx
.text:004012A4
.text:004012A4 Format: ; CODE XREF: GenerateRealSerial+65j
.text:004012A4 push offset asc_403076 ; "%X"
.text:004012A9 push offset realSerialBuf
.text:004012AE call wsprintfA
.text:004012B3 add esp, 0Ch
.text:004012B6
.text:004012B6 Done: ; CODE XREF: GenerateRealSerial+1Fj
.text:004012B6 pop edi
.text:004012B7 pop esi
.text:004012B8 pop ebx
.text:004012B9 retn
.text:004012B9 GenerateRealSerial endp

The source of keygen:
Code:

#include <stdio.h>
#include <string.h>

int main(void)
{
char szName[36] = { 0 };
int serial = 0;

printf("Input user name: ";
scanf("%32s", szName);

for (int i = 0, len = strlen(szName); i < len; i++)
{
serial += szName[I];
serial <<= i;
}

printf("Serial = %X\n", serial);

return serial;
}

Serial for "tyler" is "4C5A0"
Regards,
TQN

bilbo
March 23rd, 2005, 03:24
tdennist,
please allow me to write down just 3 remarks, without any malicious intent:

(1)
Quote:
I greatly prefer programming in ASM than either C or C++...they're just so RESTRICTING...
everything is RESTRICTING when you cannot fully dominate it... also Windows is so RESTRICTING, isn't it? Why? Because it comes without sources...

(2) I quoted "hide debugger plugin" just for an easy cut and paste of the three words into Google (without quotes): try it, it will make no harm to you!

(3) Have you already read the two solutions submitted on www.crackmes.de? But yes, TQN's one is better: TQN, why don't you submit it?

Best regards, bilbo

TQN
March 23rd, 2005, 03:45
Hi bilbo !
I am very afraid when writting English because my bad English skill, and a solution for crackmes.de need a long description, and I am always open aside a Vietnam-English dictionary when writting English.
Best regards,
TQN

bilbo
March 23rd, 2005, 06:36
TQN,
why you do not write an English<->Vietnamese dictionary for Babylon?
They will reward you for this...
http://www.babylon.com/display.php?id=55

By the way, a nice project would be to reverse engineer the Babylon dictionary format (.BGL) - compressed and/or encrypted - in order to revert back to its source format (.GLS)

Best regards, bilbo

TQN
March 23rd, 2005, 06:57
Thank for your idea, bilbo !
I will take a look at it.