Crackmes.de – san01suke’s SomeCrypto~02

The author san01suke submitted three crackmes to [www.crackmes.de][1] on July 1st. This is my attempt to solve the second one, called SomeCrypto~02. You can view and download the crackme [here][2]. The short description simply says:

“Just write a valid keygen for this crackme.”

− san01suke ## Reverse Engineering the Crackme

Getting the Disassembly

The user interface looks exactly like the one from [SomeCrypto~01][3]:
screenshot_SomeCrypto02.png

Let’s open the code in OllyDbg. At the entry point we get the following picture:
before_xor.png

One can spot two interesting things:

  • The code above 4014D3 could not be disassembled and looks obfuscated
  • The code that follows the entry point clearly modifies the code. Starting at location 401000, the snippet XORs exactly 4D3h bytes with 20h.

Let’s run the code up to 4014DA to get the changed code section. The bytes clearly changed

but OllyDbg doesn’t automatically reanalyze the section and still displays the section as data bytes. Hit CTRL+A to analyze the code again and now you should see meaningful disassembly:

I then created a patched version of SomeCrypto~02 with the bytes from 401000 to 4014D2 XORed with 20h. This allowed me to get the correct disassembly in IDA Pro.

sub_4011E0 – Part 1

I then opened the patched executable in IDA Pro and went to the strings subview to follow the “Success” string (like in [SomeCrypto~01][3]). The good boy message is produced here:

.text:00401475                 call    sub_4011E0
.text:0040147A                 add     esp, 8
.text:0040147D                 mov     byte_403270, al
.text:00401482                 test    al, al
.text:00401484                 jz      short loc_4014A2
.text:00401486                 mov     edx, [esp+0Ch]
.text:0040148A                 push    0
.text:0040148C                 push    offset aSuccess ; "Success"
.text:00401491                 push    edx
.text:00401492                 push    esi
.text:00401493                 call    ds:MessageBoxA
.text:00401499                 push    0

We will see the “Success”-message when sub_4011E0 returns a non zero value in eax. Let’s walk through subroutine sub_4011E0

.text:004011E0 ; =============== S U B R O U T I N E =======================================
.text:004011E0
.text:004011E0 ; Attributes: bp-based frame
.text:004011E0
.text:004011E0 sub_4011E0      proc near               ; CODE XREF: .text:00401475p
.text:004011E0
.text:004011E0 var_1C          = byte ptr -1Ch
.text:004011E0 arg_0           = dword ptr  8
.text:004011E0 arg_4           = dword ptr  0Ch
.text:004011E0
.text:004011E0                 push    ebp
.text:004011E1                 mov     ebp, esp
.text:004011E3                 xor     eax, eax
.text:004011E5                 sub     esp, 1Ch
.text:004011E8                 cmp     [edi], al
.text:004011EA                 jz      loc_401296
.text:004011F0

In OllyDbg we see that edi contains the serial:

The snippet translate to the following pseudo code:

serial = edi
IF strlen(serial) == 0 THEN
    RETURN 0 // loc_401296
END

This is the code at loc_401296 that returns 0:

.text:00401296 loc_401296:                             ; CODE XREF: sub_4011E0+Aj
.text:00401296                                         ; sub_4011E0+1Aj ...
.text:00401296                 xor     al, al
.text:00401298                 mov     esp, ebp
.text:0040129A                 pop     ebp
.text:0040129B                 retn
.text:0040129B sub_4011E0      endp

If the serial is not empty the next section is executed:

.text:004011F0 loc_4011F0:                             ; CODE XREF: sub_4011E0+15j
.text:004011F0                 inc     eax
.text:004011F1                 cmp     byte ptr [eax+edi], 0
.text:004011F5                 jnz     short loc_4011F0
.text:004011F7                 cmp     eax, 7
.text:004011FA                 jnz     loc_401296

The lines calculate the length of the serial and return 0 if the length is not 7:

serial_length = strlen(serial)
IF serial_length != 7 THEN
    RETURN 0 // loc_401296
END

Next follows a call to another subroutine:

.text:00401200                 mov     edx, [ebp+arg_0]
.text:00401203                 lea     eax, [ebp+var_1C]
.text:00401206                 call    sub_401000</pre>

eax is a local variable. The memory location ebp+arg_0 = ebp+8 points to the content of the name input box, as can be seen in OllyDbg:

So the call boils down to:

unknown_type var_1C;
sub_401000(&#038;var_1C, name)

sub_401000

The subroutine sub_401000 looks as follows:

.text:00401000 ; =============== S U B R O U T I N E =======================================
.text:00401000
.text:00401000
.text:00401000 sub_401000      proc near               ; CODE XREF: sub_4011E0+26p
.text:00401000                                         ; DATA XREF: .text:004014DAo
.text:00401000                 mov     dword ptr [eax], 0
.text:00401006                 mov     dword ptr [eax+4], 1
.text:0040100D                 mov     dword ptr [eax+8], 2
.text:00401014                 mov     dword ptr [eax+0Ch], 3
.text:0040101B                 mov     dword ptr [eax+10h], 4
.text:00401022                 mov     dword ptr [eax+14h], 5
.text:00401029                 mov     dword ptr [eax+18h], 6
.text:00401030                 mov     cl, [edx]
.text:00401032                 test    cl, cl
.text:00401034                 jz      short locret_40108C
.text:00401036                 push    esi
.text:00401037                 jmp     short loc_401040
.text:00401037 ; ---------------------------------------------------------------------------
.text:00401039                 align 10h
.text:00401040
.text:00401040 loc_401040:                             ; CODE XREF: sub_401000+37j
.text:00401040                                         ; sub_401000+89j
.text:00401040                 movsx   ecx, cl
.text:00401043                 and     ecx, 80000001h
.text:00401049                 jns     short loc_401050
.text:0040104B                 dec     ecx
.text:0040104C                 or      ecx, 0FFFFFFFEh
.text:0040104F                 inc     ecx
.text:00401050
.text:00401050 loc_401050:                             ; CODE XREF: sub_401000+49j
.text:00401050                 jz      short loc_40105C
.text:00401052                 mov     esi, [eax+4]
.text:00401055                 mov     ecx, [eax]
.text:00401057                 mov     [eax], esi
.text:00401059                 mov     [eax+4], ecx
.text:0040105C
.text:0040105C loc_40105C:                             ; CODE XREF: sub_401000:loc_401050j
.text:0040105C                 mov     ecx, [eax]
.text:0040105E                 mov     esi, [eax+4]
.text:00401061                 mov     [eax], esi
.text:00401063                 mov     esi, [eax+8]
.text:00401066                 mov     [eax+4], esi
.text:00401069                 mov     esi, [eax+0Ch]
.text:0040106C                 mov     [eax+8], esi
.text:0040106F                 mov     esi, [eax+10h]
.text:00401072                 mov     [eax+0Ch], esi
.text:00401075                 mov     esi, [eax+14h]
.text:00401078                 mov     [eax+10h], esi
.text:0040107B                 mov     esi, [eax+18h]
.text:0040107E                 mov     [eax+14h], esi
.text:00401081                 inc     edx
.text:00401082                 mov     [eax+18h], ecx
.text:00401085                 mov     cl, [edx]
.text:00401087                 test    cl, cl
.text:00401089                 jnz     short loc_401040
.text:0040108B                 pop     esi
.text:0040108C
.text:0040108C locret_40108C:                          ; CODE XREF: sub_401000+34j
.text:0040108C                 retn
.text:0040108C sub_401000      endp
</pre>

The code translates to the following pseudo code:

FUNCTION sub_401000(mapping&lt;var_1C>, name)
    mapping = {0,1,2,3,4,5,6} // in eax = &#038;var_1C
    FOR character IN name DO
        IF character % 2 != 0 DO
            swap(mapping[0], mapping[1])
        ENDIF
        circular_left_shift(mapping)
    ENDFOR
END

swap(mapping[0], mapping[1]) means {0,1,2,3,4,5,6} would become {1,0,2,3,4,5,6}. circular_left_shift(mapping) means, {0,1,2,3,4,5,6} becomes {1,2,3,4,5,6,0}.

sub_4011E0 – Part 2

Let’s go back to the caller where we continue with line 27:

.text:0040120B                 xor     eax, eax
.text:0040120D                 lea     ecx, [ecx+0]
.text:00401210
.text:00401210 loc_401210:                             ; CODE XREF: sub_4011E0+3Fj
.text:00401210                 mov     cl, byte_403010[eax]
.text:00401216                 mov     byte_403140[eax], cl
.text:0040121C                 inc     eax
.text:0040121D                 test    cl, cl
.text:0040121F                 jnz     short loc_401210
.text:00401221                 push    esi
.text:00401222                 xor     esi, esi
.text:00401224                 cmp     byte_403140, 0
.text:0040122B                 jz      short loc_40123A

These line simply copy the hard coded null-terminated string at byte_403010 to byte_403140 and check if the destination address is not null:

STRCPY(byte_403140, byte_403010) // copy string byte_403010 to byte_403140
IF byte_403140 == NULL THEN
    GOTO loc_40123A \\ should never happen
ENDIF

Next up is another small loop:

.text:0040122D                 lea     ecx, [ecx+0]
.text:00401230
.text:00401230 loc_401230:                             ; CODE XREF: sub_4011E0+58j
.text:00401230                 inc     esi
.text:00401231                 cmp     byte_403140[esi], 0
.text:00401238                 jnz     short loc_401230
.text:0040123A

The code searches the null-byte in string byte_403140. When the null byte is found, then the index in esi corresponds to the length of the string at byte_403140:

esi = strlen(byte_403140)

Another subroutine call follows:

.text:0040123A loc_40123A:                             ; CODE XREF: sub_4011E0+4Bj
.text:0040123A                 push    esi
.text:0040123B                 lea     eax, [ebp+var_1C]
.text:0040123E                 call    sub_401110

The only parameter is var_1C, which we know contains the scrambled sequence of numbers 0 to 7 that sub_401000 generated: sub_401110(mapping)

sub_401110

The subroutine is a little hard to read, because it uses a few local variables to shuffle characters. Here’s the disassembly:

.text:00401110 sub_401110      proc near               ; CODE XREF: sub_4011E0+5Ep
.text:00401110                                         ; sub_4011E0+71p
.text:00401110
.text:00401110 var_1C          = dword ptr -1Ch
.text:00401110 var_18          = word ptr -18h
.text:00401110 var_16          = byte ptr -16h
.text:00401110 var_14          = dword ptr -14h
.text:00401110 var_10          = dword ptr -10h
.text:00401110 var_C           = dword ptr -0Ch
.text:00401110 var_8           = dword ptr -8
.text:00401110 var_4           = dword ptr -4
.text:00401110 arg_0           = dword ptr  8
.text:00401110
.text:00401110                 push    ebp
.text:00401111                 mov     ebp, esp
.text:00401113                 mov     ecx, 7
.text:00401118                 sub     esp, 1Ch
.text:0040111B                 cmp     [ebp+arg_0], ecx
.text:0040111E                 jle     loc_4011CB
.text:00401124                 mov     edx, [eax+8]
.text:00401127                 lea     edx, [ebp+edx+var_1C]
.text:0040112B                 mov     [ebp+var_4], edx
.text:0040112E                 mov     edx, [eax+0Ch]
.text:00401131                 lea     edx, [ebp+edx+var_1C]
.text:00401135                 mov     [ebp+var_8], edx
.text:00401138                 mov     edx, [eax+10h]
.text:0040113B                 lea     edx, [ebp+edx+var_1C]
.text:0040113F                 push    ebx
.text:00401140                 push    esi
.text:00401141                 mov     esi, [eax]
.text:00401143                 mov     [ebp+var_C], edx
.text:00401146                 mov     edx, [eax+14h]
.text:00401149                 push    edi
.text:0040114A                 mov     edi, [eax+4]
.text:0040114D                 mov     eax, [eax+18h]
.text:00401150                 lea     edx, [ebp+edx+var_1C]
.text:00401154                 mov     [ebp+var_10], edx
.text:00401157                 lea     edx, [ebp+eax+var_1C]
.text:0040115B                 mov     eax, offset unk_403142
.text:00401160                 lea     esi, [ebp+esi+var_1C]
.text:00401164                 lea     edi, [ebp+edi+var_1C]
.text:00401168                 mov     [ebp+var_14], edx
.text:0040116B                 sub     ecx, eax
.text:0040116D                 lea     ecx, [ecx+0]
.text:00401170
.text:00401170 loc_401170:                             ; CODE XREF: sub_401110+B6j
.text:00401170                 movzx   edx, byte ptr [eax-2]
.text:00401174                 mov     ebx, [ebp+var_4]
.text:00401177                 mov     [esi], dl
.text:00401179                 movzx   edx, byte ptr [eax-1]
.text:0040117D                 mov     [edi], dl
.text:0040117F                 movzx   edx, byte ptr [eax]
.text:00401182                 mov     [ebx], dl
.text:00401184                 movzx   edx, byte ptr [eax+1]
.text:00401188                 mov     ebx, [ebp+var_8]
.text:0040118B                 mov     [ebx], dl
.text:0040118D                 movzx   edx, byte ptr [eax+2]
.text:00401191                 mov     ebx, [ebp+var_C]
.text:00401194                 mov     [ebx], dl
.text:00401196                 movzx   edx, byte ptr [eax+3]
.text:0040119A                 mov     ebx, [ebp+var_10]
.text:0040119D                 mov     [ebx], dl
.text:0040119F                 movzx   edx, byte ptr [eax+4]
.text:004011A3                 mov     ebx, [ebp+var_14]
.text:004011A6                 mov     [ebx], dl
.text:004011A8                 mov     edx, [ebp+var_1C]
.text:004011AB                 mov     [eax-2], edx
.text:004011AE                 mov     dx, [ebp+var_18]
.text:004011B2                 mov     [eax+2], dx
.text:004011B6                 movzx   edx, [ebp+var_16]
.text:004011BA                 mov     [eax+4], dl
.text:004011BD                 add     eax, 7
.text:004011C0                 lea     edx, [ecx+eax]
.text:004011C3                 cmp     edx, [ebp+arg_0]
.text:004011C6                 jl      short loc_401170
.text:004011C8                 pop     edi
.text:004011C9                 pop     esi
.text:004011CA                 pop     ebx
.text:004011CB
.text:004011CB loc_4011CB:                             ; CODE XREF: sub_401110+Ej
.text:004011CB                 mov     esp, ebp
.text:004011CD                 pop     ebp
.text:004011CE                 retn    4
.text:004011CE sub_401110      endp

All the code does is shuffle the characters in the string byte_403140 based on the mapping that sub_401000 generated:

    FUNCTION sub_401110(mapping)
    i = 0
    message = byte_403140
    WHILE i+7 &lt; strlen(mapping) DO
        tmp[7]
        FOR j=0 TO 6 DO:
            tmp[j] = message[i + mapping[j]]
        ENDFOR
        FOR j=0 TO 6 DO:
            message[j] = tmp[j] 
        ENDFOR
    END
END

The subroutine applies a permutation to the message in byte_403140. It permutates blocks of 7 characters based on the mapping that the subroutine sub_401000 generated. Let’s say the mapping is {3, 1, 0, 2, 5, 6, 4}, then the characters inside a 7 letter group would change like this:

screenshot_SomeCrypto02.png

sub_4011E0 - Part 3

Back at caller we see yet another subroutine:

.text:00401243                 mov     ecx, edi
.text:00401245                 lea     eax, [ebp+var_1C]
.text:00401248                 call    sub_401090

The subroutine operates on [ebp+var_1C] which we know contains the mapping, and on ecx = edi which holds the text from the serial input box:

sub_401090(mapping, serial)

sub_401090

This is sub_401090:

.text:00401090 ; =============== S U B R O U T I N E =======================================
.text:00401090
.text:00401090
.text:00401090 sub_401090      proc near               ; CODE XREF: sub_4011E0+68p
.text:00401090                 movsx   edx, byte ptr [ecx]
.text:00401093                 add     edx, 0FFFFFFD0h
.text:00401096                 push    esi
.text:00401097                 xor     esi, esi
.text:00401099                 mov     [eax], edx
.text:0040109B                 cmp     edx, 7
.text:0040109E                 jb      short loc_4010A2
.text:004010A0                 mov     [eax], esi
.text:004010A2
.text:004010A2 loc_4010A2:                             ; CODE XREF: sub_401090+Ej
.text:004010A2                 movsx   edx, byte ptr [ecx+1]
.text:004010A6                 add     edx, 0FFFFFFD0h
.text:004010A9                 mov     [eax+4], edx
.text:004010AC                 cmp     edx, 7
.text:004010AF                 jb      short loc_4010B4
.text:004010B1                 mov     [eax+4], esi
.text:004010B4
.text:004010B4 loc_4010B4:                             ; CODE XREF: sub_401090+1Fj
.text:004010B4                 movsx   edx, byte ptr [ecx+2]
.text:004010B8                 add     edx, 0FFFFFFD0h
.text:004010BB                 mov     [eax+8], edx
.text:004010BE                 cmp     edx, 7
.text:004010C1                 jb      short loc_4010C6
.text:004010C3                 mov     [eax+8], esi
.text:004010C6
.text:004010C6 loc_4010C6:                             ; CODE XREF: sub_401090+31j
.text:004010C6                 movsx   edx, byte ptr [ecx+3]
.text:004010CA                 add     edx, 0FFFFFFD0h
.text:004010CD                 mov     [eax+0Ch], edx
.text:004010D0                 cmp     edx, 7
.text:004010D3                 jb      short loc_4010D8
.text:004010D5                 mov     [eax+0Ch], esi
.text:004010D8
.text:004010D8 loc_4010D8:                             ; CODE XREF: sub_401090+43j
.text:004010D8                 movsx   edx, byte ptr [ecx+4]
.text:004010DC                 add     edx, 0FFFFFFD0h
.text:004010DF                 mov     [eax+10h], edx
.text:004010E2                 cmp     edx, 7
.text:004010E5                 jb      short loc_4010EA
.text:004010E7                 mov     [eax+10h], esi
.text:004010EA
.text:004010EA loc_4010EA:                             ; CODE XREF: sub_401090+55j
.text:004010EA                 movsx   edx, byte ptr [ecx+5]
.text:004010EE                 add     edx, 0FFFFFFD0h
.text:004010F1                 mov     [eax+14h], edx
.text:004010F4                 cmp     edx, 7
.text:004010F7                 jb      short loc_4010FC
.text:004010F9                 mov     [eax+14h], esi
.text:004010FC
.text:004010FC loc_4010FC:                             ; CODE XREF: sub_401090+67j
.text:004010FC                 movsx   ecx, byte ptr [ecx+6]
.text:00401100                 add     ecx, 0FFFFFFD0h
.text:00401103                 mov     [eax+18h], ecx
.text:00401106                 cmp     ecx, 7
.text:00401109                 jb      short loc_40110E
.text:0040110B                 mov     [eax+18h], esi
.text:0040110E
.text:0040110E loc_40110E:                             ; CODE XREF: sub_401090+79j
.text:0040110E                 pop     esi
.text:0040110F                 retn
.text:0040110F sub_401090      endp

The code is quite long because the assembler did loop unwinding. The underlying algorithm is very simple though:

FUNCTION sub_401090(mapping, serial)
    FOR i = 0 TO 6 DO
        nr = serial[i] - '0'
        IF nr >= 7 THEN
            mapping[i] = 0
        ELSE
            mapping[i] = nr
        ENDIF
    ENDFOR
END

So the serial number (7 letters) is converted to 7 integers that are stored in mapping (as long as numbers are smaller than 7):

FUNCTION sub_401090(mapping, serial)
    FOR i = 0 TO 6 DO
        nr = serial[i] - '0'
        IF nr >= 7 THEN
            mapping[i] = 0
        ELSE
            mapping[i] = nr
        ENDIF
    ENDFOR
END

sub_4011E0 - Part 4

After sub_401090 loaded the serial into the mapping we find a second call to the permutation routine sub_401110

.text:0040124D                 push    esi
.text:0040124E                 lea     eax, [ebp+var_1C]
.text:00401251                 call    sub_401110

The routine finishes up with some code that calculates a hash of byte_403140. If the hash matches 0B45D7873h we get the success message:

.text:00401256                 or      eax, 0FFFFFFFFh
.text:00401259                 mov     ecx, esi
.text:0040125B                 mov     edx, offset byte_403140
.text:00401260                 test    esi, esi
.text:00401262                 jz      short loc_40127D
.text:00401264
.text:00401264 loc_401264:                             ; CODE XREF: sub_4011E0+9Bj
.text:00401264                 movzx   esi, byte ptr [edx]
.text:00401267                 xor     esi, eax
.text:00401269                 and     esi, 0FFh
.text:0040126F                 shr     eax, 8
.text:00401272                 xor     eax, ds:dword_402058[esi*4]
.text:00401279                 inc     edx
.text:0040127A                 dec     ecx
.text:0040127B                 jnz     short loc_401264
.text:0040127D
.text:0040127D loc_40127D:                             ; CODE XREF: sub_4011E0+82j
.text:0040127D                 not     eax
.text:0040127F                 pop     esi
.text:00401280                 cmp     eax, 0B45D7873h
.text:00401285                 jnz     short loc_401296
.text:00401287                 mov     eax, [ebp+arg_4]
.text:0040128A                 mov     dword ptr [eax], offset byte_403140
.text:00401290                 mov     al, 1
.text:00401292                 mov     esp, ebp
.text:00401294                 pop     ebp
.text:00401295                 retn
.text:00401296 ; ---------------------------------------------------------------------------
.text:00401296
.text:00401296 loc_401296:                             ; CODE XREF: sub_4011E0+Aj
.text:00401296                                         ; sub_4011E0+1Aj ...
.text:00401296                 xor     al, al
.text:00401298                 mov     esp, ebp
.text:0040129A                 pop     ebp
.text:0040129B                 retn
.text:0040129B sub_4011E0      endp

In pseudo code:

some_hash = some_hash_routine(message)
IF some_hash = '0B45D7873h' THEN
    RETURN 1 // success
ELSE
    RETURN 0 // failure
ENDIF

Pseudo-Code

To summarize, here’s the cleaned up pseudo code:

FUNCTION name_mapping(name)
    mapping = {0,1,2,3,4,5,6} // in eax = &#038;var_1C
    FOR character IN name DO
        IF character % 2 != 0 DO
            swap(mapping[0], mapping[1])
        ENDIF
        circular_left_shift(mapping)
    ENDFOR
    RETURN mapping
END

FUNCTION serial_mapping(serial)
    message[7]
    FOR i = 0 TO 6 DO
        nr = serial[i] - '0'
        IF nr >= 7 THEN
            mapping[i] = 0
        ELSE
            mapping[i] = nr
        ENDIF
    ENDFOR
    RETURN message
END

FUNCTION permutation(message, mapping)
    i = 0
    message = byte_403140
    WHILE i+7 &lt; strlen(mapping) DO
        tmp[7]
        FOR j=0 TO 6 DO:
            tmp[j] = message[i + mapping[j]]
        ENDFOR
        FOR j=0 TO 6 DO:
            message[j] = tmp[j]
        ENDFOR
    END
    RETURN message
END

FUNCTION CHECK_SERIAL(serial, name)
    IF strlen(serial) != 7 THEN
        RETURN 0
    END
    mapping = name_mapping(name)

    STRCPY(message, byte_403010) // hard coded message
    IF message == NULL THEN
        GOTO loc_40123A \\ should never happen
    ENDIF
    message = permutation(message, mapping)
    mapping = serial_mapping(serial)
    message = permutation(message, mapping)
    some_hash = some_hash_routine(message)
    IF some_hash = '0B45D7873h' THEN
        RETURN 1 // success
    ELSE
        RETURN 0 // failure
    ENDIF
END

CHECK_SERIAL(serial, name)

Cracking the Code

Now that we know how the algorithm works, we need to first figure out which permutation would produce the correct plaintext message. The encrypted message in byte_403010 is:

prncyI In cryp haorptg e ,apy iamttru onbxo b Po -(r ix so)o t ehami fbdofu- hftss i gulnpod t e tr ueemnrr tao bep s sorat cisbSs -osn xs ioer,us ptnntii eauf if gdh inw soatl r ienssoi npg.

To crack the code, it is enough to find the permutation for one 7 letter block. The first 7 letters of the ciphertext are:

prncyI 

(there’s a space at the end). The capital letter I comes first, the rest isn’t too hard to guess either:

In cryp

So the correct decryption mapping is: 6 4 1 3 5 0 2. (the first letter goes to the 6th position, the second letter to the 4th, etc.).

Keygen

The SomeCrypt~02 applies two permutations to the ciphertext. The first is based on the name, the second is given by the serial. To write a keygen for a given name we need to calculate the resulting mapping by running the name_mapping routine. Then we can determine which second mapping, when combined with the name mapping, results in the correct mapping 6 4 1 3 5 0 2:

import argparse    
from collections import deque

parser = argparse.ArgumentParser(description="SomeCrypto~02 keygen")
parser.add_argument('name')
args = parser.parse_args()
name = args.name 

correct_key = [6, 4, 1, 3, 5, 0, 2]
cypher = deque(list(range(7)))

for c in name:
    if ord(c) % 2:
        cypher[0], cypher[1] = cypher[1], cypher[0]
    cypher.rotate(-1)

serial = 7*[None]
for c, k in zip(cypher, correct_key):
    serial[c] = k

print('serial: ' + ''.join(str(s) for s in serial))

Testing:

$ python keygen.py San01suke
serial: 2504613
comments powered by Disqus