Contribute  :  Web Resources  :  Past Polls  :  Site Statistics  :  Downloads  :  Forum
 The challenge is yours
 Welcome to BiW Reversing Thursday, June 01 2023 @ 02:44 PM CEST

Author Message
highenergy
Occasional Poster

Joined: 08 Mar 2005
Posts: 32

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

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

regards
detten

Joined: 05 Feb 2005
Posts: 317

Posted: Wed Jul 18, 2012 12:52 pm    Post subject:

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
highenergy
Occasional Poster

Joined: 08 Mar 2005
Posts: 32

Posted: Fri Feb 22, 2013 10:49 pm    Post subject:

Analysis:
 Code: 002201D0    90              NOP 002201D1    90              NOP 002201D2    90              NOP // Initialisation 002201D3    33C0            XOR EAX,EAX                     ; eax = 0 002201D5    33C9            XOR ECX,ECX                     ; ecx = 0 002201D7    33DB            XOR EBX,EBX                     ; abx = 0 // 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 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 #include 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;        if (findSerial(name, serial)){       cout << "Serial: " << serial;    } else {       cout << "Couldn't find a serial, sorry" << endl;    }        cout << endl;    system("pause");    return 0; } 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;    } } bool findSerial(char name[], unsigned int& output) {        for (unsigned int serial = 0; serial < 0xFFFFFFFF; serial++) {       int i = 0;       unsigned int sum = 0;              do {          sum += name[i];          sum = rol(sum, i);          sum = sum ^ serial;          sum = ror(sum, i);          i++;          } while (name[i] != '\0');              if (isPrime(sum)) {          output = serial;          return true;       }    }    return false; } unsigned int rol(unsigned int input, int times) {     return (input << times) | ((input & 0xFFFFFFFF) >> (32-times)); } unsigned int ror(unsigned int input, int times) {    return (input >> times) | ((input & 0xFFFFFFFF) << (32-times)); }

Above code works well. Here is the proof.

detten

Joined: 05 Feb 2005
Posts: 317

 Posted: Fri Mar 08, 2013 10:16 am    Post subject: 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
 Display posts from previous: All Posts1 Day7 Days2 Weeks1 Month3 Months6 Months1 Year Oldest FirstNewest First
 All times are GMT + 1 Hour Page 1 of 1

 Jump to: Select a forum General----------------BiW Member ChatN2C Member ChatFAQ'sForum Rules Windows Reversing----------------UnpackingTools GarageCoding CornerCode Reversing Linux Reversing----------------Tools Garage
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