Posted: Tue Jul 03, 2012 10:05 am Post subject: Detten's Crackme 13
Hi there,
I am trying to solve the serial generator algorithm but I bumped a dead end. Below is the dump of algo but in order to get a working serial function/subroutine should return DEADBABE in eax. If you examine the code below you never reach
Code:
mov eax, DEADBABE
instruction. And you never get a working serial. So my question is, is the algo possibly wrong? Or am I doing something wrong?
Subroutine:
Code:
02201D0 90 NOP
002201D1 90 NOP
002201D2 90 NOP
002201D3 33C0 XOR EAX,EAX
002201D5 33C9 XOR ECX,ECX
002201D7 33DB XOR EBX,EBX
002201D9 8A99 48304000 MOV BL,BYTE PTR DS:[ECX+403048] ; 403048 points to name (character array)
002201DF 03C3 ADD EAX,EBX
002201E1 D3C0 ROL EAX,CL
002201E3 3305 5E304000 XOR EAX,DWORD PTR DS:[40305E] ; 40305E points to serial (integer)
002201E9 D3C8 ROR EAX,CL
002201EB 41 INC ECX
002201EC 80B9 48304000 0>CMP BYTE PTR DS:[ECX+403048],0
002201F3 ^ 75 E4 JNZ SHORT 002201D9
002201F5 66:B9 0200 MOV CX,2
002201F9 99 CDQ
002201FA 50 PUSH EAX
002201FB F7F9 IDIV ECX ; divide edx:eax with ecx. Start ecx with 2.
002201FD 58 POP EAX
002201FE 83FA 00 CMP EDX,0 ; remainder of divition could be zero or not
00220201 74 10 JE SHORT 00220213 ; if it's zero return (bad boy)
00220203 66:41 INC CX ; else increase ecx and divide again.
00220205 ^ 75 F2 JNZ SHORT 002201F9 ; this is looks like conditional jump but actually zero flag is set at 002201FE. So it behaves like unconditional jmp.
00220207 3D FFFFFFBF CMP EAX,BFFFFFFF ;---> Below three block never executes
0022020C 76 05 JBE SHORT 00220213 ;--->
0022020E B8 BEBAADDE MOV EAX,DEADBABE ;---> So never return good boy
00220213 C3 RETN
Almost everything you deducted so far is correct, but there is no error in the algorithm afaik.
Quote:
00220205 ^ 75 F2 JNZ SHORT 002201F9 ; this is looks like conditional jump but actually zero flag is set at 002201FE. So it behaves like unconditional jmp.
There is another way to quit this loop, it is not that obvious to spot. I requires a little mathematical thinking
So far you deducted it cannot be dividable by any integer or it triggers the badboy return.
Which implies you need a prime number, and 'some' relation to CX... _________________ Ignorance is bliss, knowledge is power
// Read every character of name then add them together
002201D9 8A99 48304000 MOV BL,BYTE PTR DS:[ECX+403048] ; 403048 points to name (character array)
; reads one byte at a time.
002201DF 03C3 ADD EAX,EBX ; eax += name[ecx]
002201E1 D3C0 ROL EAX,CL ; rol(eax, ecx)
002201E3 3305 5E304000 XOR EAX,DWORD PTR DS:[40305E] ; 40305E points to serial (integer)
002201E9 D3C8 ROR EAX,CL ; ror(eax, ecx)
002201EB 41 INC ECX ; ecx++
002201EC 80B9 48304000 0>CMP BYTE PTR DS:[ECX+403048],0 ; while name[ecx]!='\0'
002201F3 ^ 75 E4 JNZ SHORT 002201D9
// Test eax for being a prime number or not
// This sectiom of code checks whether or not eax is prime. Normally a prime number checking routine should
// check all numbers between 2 <= cx <= eax-1.
// Because a prime number is a number which can only has dividents 1 and itself.
002201F5 66:B9 0200 MOV CX,2 ; ecx = 2
002201F9 99 CDQ ; sign extend eax into edx:eax
002201FA 50 PUSH EAX ; save eax
002201FB F7F9 IDIV ECX ; signed division of edx:eax with ecx.
002201FD 58 POP EAX ; put back into eax
002201FE 83FA 00 CMP EDX,0 ; check remainder. If remainder is zero then eax is not a prime,
; increase ecx until overflow word boundry 0xFFFF.
00220201 74 10 JE SHORT 00220213 ; bad boy jump
00220203 66:41 INC CX ; else increase ecx and divide again.
// Following opcode tests if cx overflows. All cx values will be tested againist being divisor of eax.
// If all cx values are tested and there is no overflow then it means there is a divisor.
// So eax should not be between 2 <= eax <= cx, but should be bigger than 0xFFFF.
// Otherwise cx would divide eax.
// Hmm, the cx has only gets 2 <= cx <= 0xFFFF values. That means this routine is not an complete prime number tester.
// I would expect cx get values between 1 < ecx <eax.
// This routine only test that eax has no diviser below and including 0xFFFF.
00220205 ^ 75 F2 JNZ SHORT 002201F9 ;
// Below line states that eax should be above 0xBFFFFFFF.
// So we are looking for a prime number which has no divisor below 0xFFFF and bigger than 0xBFFFFFFF.
// Following opcode treats comparison as unsigned numbers.
// signed 0xBFFFFFFF equals -1073741825 in decimal.
00220207 3D FFFFFFBF CMP EAX,BFFFFFFF
0022020C 76 05 JBE SHORT 00220213 ;---> Bad boy jump
0022020E B8 BEBAADDE MOV EAX,DEADBABE ;---> Good boy return value
00220213 C3 RETN
RESULT
eax is a signed number, a prime number (has no divisor below 0xFFFF)
and bigger than BFFFFFFF. Since division is carried out in a signed sense prime numbers
we are looking for are negative numbers. Prime numbers should be between 0xFFFFFFFF (-1)
and BFFFFFFF (-1073741825).
Because eax is a prime number, and has no divisor, cx register overflows after 0xFFFF value.
That causes zero flag gets set and conditional jump at 00220205 is not taken.
KEYGEN
I am not sure finding a serial without brute force. Because serial gets mixed in every iteration.
In each iteration serial and characters of name gets mixed with xor, rol and ror insructions.
The only way I see to find a serial is using brute force the algorithm.
Here is my brute force way of getting a working serial.
Code:
#include <iostream>
#include <stdlib.h>
using namespace std;
bool isPrime(int input);
bool findSerial(char name[], unsigned int& output);
unsigned int rol(unsigned int input, int times);
unsigned int ror(unsigned int input, int times);
int main()
{
// initialization
char name[35];
unsigned int serial = 0;
for (int i = 0; i < sizeof(name); i++ ) {
name[i] = '\0';
}
cout << "Your Name: " << endl;
cin >> name;
cout << "Please wait while searching for a serial..." << endl;
bool isPrime(int input) {
// Tests input, whether it is a prime or not
// Actually the comparison condition (j < 0xFFFF) would be j < i
// But since crackme doesn't cares values after 0xFFFF
// we are also letting for loop ends early
if (input > 0xBFFFFFFF && input <=0xFFFFFFFF) {
for (int divisor = 2; divisor <= 0xFFFF; ++divisor) {
if (!(input%divisor)) {
return false;
}
}
return true;
} else {
return false;
}
}
Great work highenergy,
I hope you had some fun with this crackme
I will see if I can dig up the original tutorial (maybe the source code) and submit it on the site. I already promised bor0 I would do that, but so far I had no luck finding it in my 'well-organized' archives _________________ Ignorance is bliss, knowledge is power
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum You cannot attach files in this forum You cannot download files in this forum