```——> ./activateUsage: ./activate <PRODUCT_KEY>[andrei@jacky 01:35:47] ~/Documents/pico——> ./activate AAAAPlease Provide a VALID 16 byte Product Key.[andrei@jacky 01:35:51] ~/Documents/pico——> ./activate AAAABBBBCCCCDDDDINVALID Product Key.```
the executable wants a (exactly) 16 character code. inspecting the main we can clearly see we have to pass the validate_key function, then it will call print_flag.
this function has basically three blocks:
1)```[0x8048771]                              || (fcn) sym.validate_key 172             ||   sym.validate_key (char *serial);     || ; var int keysum @ ebp-0x14            || ; var signed int counter @ ebp-0x10    || ; var size_t serial_len @ ebp-0xc      || ; var int dunno @ ebp-0x4              || ; arg char *serial @ ebp+0x8           || ; CALL XREF from main (0x8048899)      || push ebp                               || ebp = esp                              || push ebx                               || esp -= 0x14                            || esp -= 0xc                             || ; const char *s                        || push dword [serial]                    || ; size_t strlen(const char *s)         || sym.imp.strlen ();[ga]                 || esp += 0x10                            || dword [serial_len] = eax               || dword [keysum] = 0                     || dword [counter] = 0                    || goto 0x80487c9;[gb]                    |```
function initialization; we can start by identifying the variables and renaming them (already done here) to understand the logic more easily. Since the name of the function is validate_key, we can assume the key we provide (here, serial) is passed to the function as an argument ( `sym.validate_key (char *serial)` and  `arg char *serial @ ebp+0x8`). Also, it is pushed on the stack right before calling strlen, and after that the valur returned in eax is stored in a local variable (`dword [serial_len] = eax`).
Moving on we have a while() block:
2.a - while head)```| ; CODE XREF from sym.validate_key (0x8048797) || eax = dword [serial_len]                      || eax -= 1                                      || var = eax - dword [counter]                   || if (var > 0) goto 0x8048799;[ge]              |```
2.b - while body)
```; CODE XREF from sym.validate_key (0x80487d2)edx = dword [counter]; [0x8:4]=-1eax = dword [serial]eax += edxeax = byte [eax]eax = alesp -= 0xcpush eaxsym.ord ();[gd]esp += 0x10eax = aledx = [eax + 1]eax = dword [counter]eax += 1eax = eax * edxdword [keysum] += eaxdword [counter] += 1```
at the end of the while body we can see a variable is incremented before returning to the while head (`dword [counter] += 1`) (you can't see the arrows indicated the program flow here but trust me lol) so i assumed it's a counter.
this is the first part of our key validation. it takes every character of our serial, calls ord() on it, then sums 1 to this value, gets the counter, sums 1 to it, multiplies ((ord(serial[counter]) + 1) * (counter + 1)) and adds this result into another local variable, that i renamed keusym. So basically this is what the second block does:
```int calculate_keysum(char *str) {
        int keysum = 0;        int i;
        for (i = 0; i < strlen(str) - 1; i++) {                keysum += (ord(str[i]) + 1) * (i + 1);        }
        return keysum;
} ```
 with ord() being the same function you find also in python, which returns the int corresponding to its char representation (ex ord('4') = 0x04):
 ``` int ord(char c) {        if (c < 48 || c > 57) {                puts("invalid char\n");                exit(1);        }        return c - 48;}```
and here comes the third block:
3)```0x80487d4 [gf]                    || ecx = dword [keysum]            || edx = 0x38e38e39                || eax = ecx                       || eax = eax * edx                 || ebx = edx                       || ebx >>>= 3                      || eax = ebx                       || eax <<<= 3                      || eax += ebx                      || eax <<<= 2                      || ecx -= eax                      || ebx = ecx                       || eax = dword [serial_len]        || edx = [eax - 1]                 || ; [0x8:4]=-1                    || ; 8                             || eax = dword [serial]            || eax += edx                      || eax = byte [eax]                || eax = al                        || esp -= 0xc                      || push eax                        || sym.ord ();[gd]                 || esp += 0x10                     || eax = al                        || var = ebx - eax                 || al = e                          || ebx = dword [dunno]             || leave                           || return                          |```
this is kind of a bit tricky to follow just commenting and renaming variables on r2, so first i reported the steps in C:
```int validate_key(int key, char *str) {
        int c = 0x38e38e39;        int len = 16;
        unsigned int ra, rb, rc, rd; // registers
        rc = key;        rd = c;        ra = key * c;        rb = ((long long)key * c) >> 32; // get high portion of mul into ebx        rb = rb >> 3;        ra = rb;        ra = ra << 3;          ra = ra + rb;        ra = ra << 2;        rc = rc - ra;        rb = rc;        ra = len;        rd = ra - 1;        ra = str[rd];        ra = ord(ra);
        printf("ebx: 0x%08x\neax: 0x%08x\n", rb, ra);        printf("result: 0x%08x\n", rb - ra);        return rb - ra;        // must return val != 0
}```
the `mul` operation is a bit tricky because, first of all it has the destination operand implicit being eax, and second, if the result is greater than 32 bits, it memorizes the high portion into edx. this was not evident from the asm code. also notice the (long long) casting: in C we have to cast one of the operands (or declare them as long long) otherwise it would truncate the result to 32 bits, and the next bit shifting by 32 would return zero.
at this point we can simplify this code into something like this, knowing that a right / left bit shifting by n is nothing but a division / product by (2 times n):
```int validate_key_simpl(int key, char *str) {
        int c = 0x38e38e39;        int ra, rb;
        rb = ((long long)key * c) / pow(2, 35);        rb = key - (rb * 36);        ra = ord(str[15]);
        printf("ebx: 0x%08x\neax: 0x%08x\n", rb, ra);        printf("result: 0x%08x\n", rb - ra);        return rb - ra;
}```
i don't really understand what these calculation do, i tried to come up with a formula or something, but i got nothing :(
however we can see that it has many solutions by executing this C code searching for a 0 as the return value.
```——> ./solver 6666999966669990key: 0x00000414ebx: 0x00000000eax: 0x00000000```
this is the one i got the flag with
```xnand@pico-2018-shell-3:/problems/keygen-me-1_1_8eb35cc7858ff1d2f55d30e5428f30a7$ ./activate 6666999966669990Product Activated Successfully: picoCTF{k3yg3n5_4r3_s0_s1mp13_3718231394}```