Reverse Engineering

Ini semua tentang Reverse Engineering

by invalid

Pendahuluan

Tutorial ini akan membahas solusi untuk soal keyGme yang pernah di-post pada group facebook ReversingID.

Langkah-langkah

Tutorial ini akan menggunakan radare2 untuk melakukan disassembly terhadap soal keyGme. Jadi, jalankan radare2 dengan opsi -A untuk melakukan analisis terhadap aplikasi keyGme:

% r2 -A keyGme
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Constructing a function name for fcn.* and sym.func.* functions (aan)
[x] Type matching analysis for all functions (afta)
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x08048280]>

Selanjutnya, disassemble fungsi main pada aplikasi tersebut dengan menggunakan perintah pdf @main seperti berikut ini:

[0x08048280]> pdf @main
┌ (fcn) main 167
│   main (int argc, char **argv, char **envp);
│           ; arg int arg_8h @ ebp+0x8
│           ; arg int arg_ch @ ebp+0xc
│           0x0804841d      55             push ebp                    ;
│           0x0804841e      89e5           mov ebp, esp                ;
│           0x08048420      81ec00000000   sub esp, 0                  ;
│           0x08048426      8b4508         mov eax, dword [arg_8h]     ; eax = argc
│           0x08048429      83f802         cmp eax, 2                  ; argc == 2?
│       ┌─< 0x0804842c      0f841e000000   je 0x8048450                ; ya, lanjut ke 0x8048450
│       │   0x08048432      8b450c         mov eax, dword [arg_ch]     ; eax = argv
│       │   0x08048435      8b08           mov ecx, dword [eax]        ; ecx = argv[0]
│       │   0x08048437      51             push ecx                    ;
│       │   0x08048438      b8e9960408     mov eax, str.Usage:__s__serial_key ; "Usage: %s [serial-key]\n"
│       │   0x0804843d      50             push eax                    ;
│       │   0x0804843e      e869020000     call sub.printf_80486ac     ; printf("Usage: %s [serial-key]\n", argv[0]);
│       │   0x08048443      83c408         add esp, 8                  ;
│       │   0x08048446      b801000000     mov eax, 1                  ; eax = 1
│      ┌──< 0x0804844b      e972000000     jmp 0x80484c2               ; lompat ke 0x80484c2
│      ││   ; CODE XREF from main (0x804842c)                          ;
│      │└─> 0x08048450      8b450c         mov eax, dword [arg_ch]     ; eax = argv
│      │    0x08048453      83c004         add eax, 4                  ; eax = argv[1]
│      │    0x08048456      8b08           mov ecx, dword [eax]        ; ecx = argv[1] (serial)
│      │    0x08048458      51             push ecx                    ;
│      │    0x08048459      e81e020000     call sub.strlen_804867c     ; strlen(serial)
│      │    0x0804845e      83c404         add esp, 4                  ;
│      │    0x08048461      83f810         cmp eax, 0x10               ; strlen(serial) == 16?
│      │┌─< 0x08048464      0f8418000000   je 0x8048482                ; ya, lanjut ke 0x8048482
│      ││   0x0804846a      b801970408     mov eax, str.Your_key_must_be_16_characters. ; 0x8049701 ; "Your key must be 16 characters."
│      ││   0x0804846f      50             push eax                    ;
│      ││   0x08048470      e817020000     call sub.puts_804868c       ; puts("Your key must be 16 characters.");
│      ││   0x08048475      83c404         add esp, 4                  ;
│      ││   0x08048478      b802000000     mov eax, 2                  ;
│     ┌───< 0x0804847d      e940000000     jmp 0x80484c2               ; lompat ke 0x80484c2
│     │││   ; CODE XREF from main (0x8048464)                          ;
│     ││└─> 0x08048482      8b450c         mov eax, dword [arg_ch]     ;
│     ││    0x08048485      83c004         add eax, 4                  ;
│     ││    0x08048488      8b08           mov ecx, dword [eax]        ;
│     ││    0x0804848a      51             push ecx                    ;
│     ││    0x0804848b      e893feffff     call sub.Found_invalid_character_8048323
│     ││    0x08048490      83c404         add esp, 4                  ;
│     ││    0x08048493      83f800         cmp eax, 0                  ;
│     ││┌─< 0x08048496      0f8413000000   je 0x80484af                ;
│     │││   0x0804849c      b821970408     mov eax, str.Congratulation__You_are_registered_now. ; 0x8049721 ; "Congratulation, You are registered now."
│     │││   0x080484a1      50             push eax                    ;
│     │││   0x080484a2      e8e5010000     call sub.puts_804868c       ; puts("Congratulation, You are registered now.");
│     │││   0x080484a7      83c404         add esp, 4                  ;
│    ┌────< 0x080484aa      e90e000000     jmp 0x80484bd               ;
│    ││││   ; CODE XREF from main (0x8048496)
│    │││└─> 0x080484af      b849970408     mov eax, str.Sorry__the_code_was_invalid. ; "Sorry, the code was invalid."
│    │││    0x080484b4      50             push eax                    ;
│    │││    0x080484b5      e8d2010000     call sub.puts_804868c       ; puts("Sorry, the code was invalid.");
│    │││    0x080484ba      83c404         add esp, 4                  ;
│    │││    ; CODE XREF from main (0x80484aa)                          ;
│    └────> 0x080484bd      b800000000     mov eax, 0                  ; eax = 0
│     ││    ; CODE XREFS from main (0x804844b, 0x804847d)              ;
│     └└──> 0x080484c2      c9             leave                       ;
└           0x080484c3      c3             ret                         ; return

Dari potongan disassembly fungsi main di atas, bisa terlihat bahwa aplikasi tersebut memerlukan input berupa serial yang dimasukkan sebagai parameter pertama jika menjalankan aplikasi tersebut. Selain itu, panjang serial harus 16 karakter. Dan serial akan diperiksa oleh fungsi sub.Found_invalid_character_8048323. Disassembly fungsi main di atas, jika diterjemahkan ke dalam bahasa C, kurang lebih seperti ini:

int main(int argc, char *argv[])
{
    int retval = 0;

    if (argc != 2)
    {
        printf("Usage: %s [serial-key]\n", argv[0]);
        retval = 1;
        goto done;
    }

    if (strlen(argv[1]) != 0x10) {
        puts("Your key must be 16 characters.");
        retval = 2;
        goto done;
    }

    if (Found_invalid_character(argv[1]))
    {
        puts("Congratulation, You are registered now.");
    } else {
        puts("Sorry, the code was invalid.");
    }

done:
    return retval;
}

Selanjutnya, lakukan disassembly terhadap fungsi sub.Found_invalid_character_8048323 yang melakukan pemeriksaan terhadap serial. Gunakan perintah pdf @sub.Found_invalid_character_8048323 pada radare2, dan hasilnya adalah sebagai berikut:

[0x08048280]> pdf @sub.Found_invalid_character_8048323
┌ (fcn) sub.Found_invalid_character_8048323 250
│   sub.Found_invalid_character_8048323 (int arg_8h);
│           ; var int local_ch @ ebp-0xc
│           ; var int local_8h @ ebp-0x8
│           ; var int local_4h @ ebp-0x4
│           ; arg int arg_8h @ ebp+0x8
│           ; CALL XREF from main (0x804848b)
│           0x08048323      55             push ebp
│           0x08048324      89e5           mov ebp, esp
│           0x08048326      81ec0c000000   sub esp, 0xc
│           0x0804832c      8b4508         mov eax, dword [arg_8h]     ; eax = serial
│           0x0804832f      50             push eax                    ;
│           0x08048330      e847030000     call sub.strlen_804867c     ; strlen(serial)
│           0x08048335      83c404         add esp, 4                  ;
│           0x08048338      8945fc         mov dword [local_4h], eax   ; local_4h = strlen(serial)
│           0x0804833b      b800000000     mov eax, 0                  ; eax = 0
│           0x08048340      8945f8         mov dword [local_8h], eax   ; local_8h = 0
│           0x08048343      b800000000     mov eax, 0                  ; eax = 0
│           0x08048348      8945f4         mov dword [local_ch], eax   ; local_ch = 0
│           ; CODE XREF from sub.Found_invalid_character_8048323 (0x8048368)
│       ┌─> 0x0804834b      8b45fc         mov eax, dword [local_4h]   ; eax = local_4h (serial_length)
│       ╎   0x0804834e      48             dec eax                     ; eax--
│       ╎   0x0804834f      8b4df4         mov ecx, dword [local_ch]   ; ecx = local_ch (counter)
│       ╎   0x08048352      39c1           cmp ecx, eax                ; eax >= eax?
│      ┌──< 0x08048354      0f8da8000000   jge 0x8048402               ; jika ya, lompat ke 0x8048402
│     ┌───< 0x0804835a      e90b000000     jmp 0x804836a               ; lompat ke 0x804836a
│     ││╎   ; CODE XREF from sub.Found_invalid_character_8048323 (0x80483fd)
│    ┌────> 0x0804835f      8b45f4         mov eax, dword [local_ch]   ; eax = local_ch
│    ╎││╎   0x08048362      89c1           mov ecx, eax                ; ecx = eax
│    ╎││╎   0x08048364      40             inc eax                     ; eax++
│    ╎││╎   0x08048365      8945f4         mov dword [local_ch], eax   ; local_ch = eax
│    ╎││└─< 0x08048368      ebe1           jmp 0x804834b               ; lompat ke 0x804834b
│    ╎││    ; CODE XREF from sub.Found_invalid_character_8048323 (0x804835a)
│    ╎└───> 0x0804836a      8b45f4         mov eax, dword [local_ch]   ; eax = local_ch (counter)
│    ╎ │    0x0804836d      8b4d08         mov ecx, dword [arg_8h]     ; ecx = serial
│    ╎ │    0x08048370      01c1           add ecx, eax                ; serial + local_ch
│    ╎ │    0x08048372      0fbe01         movsx eax, byte [ecx]       ; eax = serial[i]
│    ╎ │    0x08048375      50             push eax                    ; param_1 = eax
│    ╎ │    0x08048376      e846ffffff     call fcn.080482c1           ; check_invalid_char1(serial[i])
│    ╎ │    0x0804837b      83c404         add esp, 4                  ; normalisasi stack setelah ccall
│    ╎ │    0x0804837e      83f800         cmp eax, 0                  ; eax == 0?
│    ╎ │┌─< 0x08048381      0f8522000000   jne 0x80483a9               ; jika tidak, lompat ke 0x80483a9
│    ╎ ││   0x08048387      8b45f4         mov eax, dword [local_ch]   ; eax = local_ch (counter)
│    ╎ ││   0x0804838a      8b4d08         mov ecx, dword [arg_8h]     ; ecx = serial
│    ╎ ││   0x0804838d      01c1           add ecx, eax                ; serial + local_ch
│    ╎ ││   0x0804838f      0fbe01         movsx eax, byte [ecx]       ; eax = serial[i]
│    ╎ ││   0x08048392      50             push eax                    ; param_1 = eax
│    ╎ ││   0x08048393      e85affffff     call fcn.080482f2           ; check_invalid_char2(serial[i])
│    ╎ ││   0x08048398      83c404         add esp, 4                  ; normalisasi stack setelah ccall
│    ╎ ││   0x0804839b      83f800         cmp eax, 0                  ; eax == 0?
│    ╎┌───< 0x0804839e      0f8505000000   jne 0x80483a9               ; jika tidak, lompat ke 0x80483a9
│   ┌─────< 0x080483a4      e938000000     jmp 0x80483e1               ; lompat ke 0x80483e1
│   │╎│││   ; CODE XREFS from sub.Found_invalid_character_8048323 (0x8048381, 0x804839e)
│   │╎└─└─> 0x080483a9      8b45f4         mov eax, dword [local_ch]   ; eax = local_ch (counter)
│   │╎ │    0x080483ac      8b4d08         mov ecx, dword [arg_8h]     ; ecx = serial
│   │╎ │    0x080483af      01c1           add ecx, eax                ; serial + counter
│   │╎ │    0x080483b1      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│   │╎ │    0x080483b4      0fbe11         movsx edx, byte [ecx]       ; edx = serial[i]
│   │╎ │    0x080483b7      01d0           add eax, edx                ; eax += edx
│   │╎ │    0x080483b9      8945f8         mov dword [local_8h], eax   ; local_8h = eax
│   │╎ │    0x080483bc      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│   │╎ │    0x080483bf      c1f801         sar eax, 1                  ; eax =>> 1
│   │╎ │    0x080483c2      8945f8         mov dword [local_8h], eax   ; local_8h = eax
│   │╎ │    0x080483c5      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│   │╎ │    0x080483c8      b9000f0000     mov ecx, 0xf00              ; ecx = 0xf00 (3840)
│   │╎ │    0x080483cd      99             cdq                         ; ubah ke qword
│   │╎ │    0x080483ce      f7f9           idiv ecx                    ; bagi dengan ecx
│   │╎ │    0x080483d0      8955f8         mov dword [local_8h], edx   ; local_8h = edx
│   │╎ │    0x080483d3      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│   │╎ │    0x080483d6      83c00a         add eax, 0xa                ; eax += 0x0a
│   │╎ │    0x080483d9      8945f8         mov dword [local_8h], eax   ; local_8h = eax
│   │╎ │┌─< 0x080483dc      e91c000000     jmp 0x80483fd               ; lompat ke 0x80483fd
│   │╎ ││   ; CODE XREF from sub.Found_invalid_character_8048323 (0x80483a4)
│   └─────> 0x080483e1      b8d0960408     mov eax, str.Found_invalid_character ; "Found invalid character!"
│    ╎ ││   0x080483e6      50             push eax                    ;
│    ╎ ││   0x080483e7      e8a0020000     call sub.puts_804868c       ; puts("Found invalid character!");
│    ╎ ││   0x080483ec      83c404         add esp, 4                  ; normalisasi stack setelah ccall
│    ╎ ││   0x080483ef      b803000000     mov eax, 3                  ; eax = 3
│    ╎ ││   0x080483f4      50             push eax                    ;
│    ╎ ││   0x080483f5      e8a2020000     call sub.exit_804869c       ; exit(3);
│    ╎ ││   0x080483fa      83c404         add esp, 4                  ;
│    │ ││   ; CODE XREF from sub.Found_invalid_character_8048323 (0x80483dc)
│    └──└─> 0x080483fd      e95dffffff     jmp 0x804835f               ; lompat ke 0x804835f
│      │    ; CODE XREF from sub.Found_invalid_character_8048323 (0x8048354)
│      └──> 0x08048402      8b45fc         mov eax, dword [local_4h]   ; eax = local_4 (SERIAL_LEN)
│           0x08048405      48             dec eax                     ; eax--
│           0x08048406      8b4d08         mov ecx, dword [arg_8h]     ; ecx = serial
│           0x08048409      01c1           add ecx, eax                ; serial[SERIAL_LEN-1]
│           0x0804840b      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│           0x0804840e      0fbe11         movsx edx, byte [ecx]       ; edx = serial[SERIAL_LEN-1]
│           0x08048411      39d0           cmp eax, edx                ; eax == edx?
│           0x08048413      b800000000     mov eax, 0                  ; eax = 0
│           0x08048418      0f94c0         sete al                     ; jika komparasi di atas sama, maka al akan di-set true
│           0x0804841b      c9             leave                       ;
└           0x0804841c      c3             ret                         ; return

Fungsi di atas cukup panjang, namun ada beberapa hal yang penting dari fungsi di atas, yaitu terdapat 2 fungsi pengecekan karakter yang valid, serta fungsi yang akan menghitung serial yang valid. Berikut ini adalah salah satu fungsi yang bertugas untuk memeriksa karakter yang invalid (fcn.080482c1):

[0x08048280]> pdf @fcn.080482c1
┌ (fcn) fcn.080482c1 49
│   fcn.080482c1 (int arg_8h);
│           ; arg int arg_8h @ ebp+0x8
│           ; CALL XREF from sub.Found_invalid_character_8048323 (0x8048376)
│           0x080482c1      55             push ebp                    ;
│           0x080482c2      89e5           mov ebp, esp                ;
│           0x080482c4      81ec00000000   sub esp, 0                  ;
│           0x080482ca      0fbe4508       movsx eax, byte [arg_8h]    ; eax = param_1
│           0x080482ce      83f841         cmp eax, 0x41               ; apakah eax < 'A' (65)?
│       ┌─< 0x080482d1      0f8c14000000   jl 0x80482eb                ; jika ya, maka lompat ke 0x80482eb
│       │   0x080482d7      0fbe4508       movsx eax, byte [arg_8h]    ; eax = param_1
│       │   0x080482db      83f85a         cmp eax, 0x5a               ; apakah eax > 'Z' (90)?
│      ┌──< 0x080482de      0f8f07000000   jg 0x80482eb                ; jika ya, maka lompat ke 0x80482eb
│      ││   0x080482e4      b801000000     mov eax, 1                  ; eax = 1
│     ┌───< 0x080482e9      eb05           jmp 0x80482f0               ; lompat ke 0x80482f0
│     │││   ; CODE XREFS from fcn.080482c1 (0x80482d1, 0x80482de)      ;
│     │└└─> 0x080482eb      b800000000     mov eax, 0                  ; eax = 0
│     │     ; CODE XREF from fcn.080482c1 (0x80482e9)                  ;
│     └───> 0x080482f0      c9             leave                       ;
└           0x080482f1      c3             ret                         ; return

Fungsi di atas bertugas untuk memeriksa karakter yang invalid, dan jika ditemukan maka fungsi tersebut akan mengembalikan nilai 0. Berikut ini adalah bentuk fungsi tersebut dalam bahasa C:

int check_invalid_char_1(char c)
{
    return ((c < 'A') || (c > 'Z')) ? 0 : 1;
}

Selain fungsi tersebut, terdapat fungsi lain yang melakukan pemeriksaan terhadap karakter yang invalid. Berikut ini adalah fungsi yang dimaksud (fcn.080482f2):

[0x08048280]> pdf @fcn.080482f2
┌ (fcn) fcn.080482f2 49
│   fcn.080482f2 (int arg_8h);
│           ; arg int arg_8h @ ebp+0x8
│           ; CALL XREF from sub.Found_invalid_character_8048323 (0x8048393)
│           0x080482f2      55             push ebp                    ;
│           0x080482f3      89e5           mov ebp, esp                ;
│           0x080482f5      81ec00000000   sub esp, 0                  ;
│           0x080482fb      0fbe4508       movsx eax, byte [arg_8h]    ; eax = param_1
│           0x080482ff      83f830         cmp eax, 0x30               ; apakah eax < '0' (48)?
│       ┌─< 0x08048302      0f8c14000000   jl 0x804831c                ; jika ya, maka lompat ke 0x804831c
│       │   0x08048308      0fbe4508       movsx eax, byte [arg_8h]    ; eax = param_1
│       │   0x0804830c      83f839         cmp eax, 0x39               ; apakah eax > '9' (57)?
│      ┌──< 0x0804830f      0f8f07000000   jg 0x804831c                ; jika ya, maka lompat ke 0x804831c
│      ││   0x08048315      b801000000     mov eax, 1                  ; eax = 1
│     ┌───< 0x0804831a      eb05           jmp 0x8048321               ; lompat ke 0x8048321
│     │││   ; CODE XREF from fcn.080482f2 (0x804830f)                  ;
│     │└└─> 0x0804831c      b800000000     mov eax, 0                  ; eax = 0
│     │     ; CODE XREF from fcn.080482f2 (0x804831a)                  ;
│     └───> 0x08048321      c9             leave                       ; return
└           0x08048322      c3             ret

Fungsi di atas jika diterjemahkan ke dalam bahasa C, maka bentuknya akan seperti ini:

int check_invalid_char_2(char c)
{
    return ((c < '0') || (c > '9')) ? 0 : 1;
}

Setelah melakukan pemeriksaan terhadap karakter yang invalid, maka eksekusi akan dilanjutkan pada bagian ini:

│   │╎└─└─> 0x080483a9      8b45f4         mov eax, dword [local_ch]   ; eax = local_ch (counter)
│   │╎ │    0x080483ac      8b4d08         mov ecx, dword [arg_8h]     ; ecx = serial
│   │╎ │    0x080483af      01c1           add ecx, eax                ; serial + counter
│   │╎ │    0x080483b1      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│   │╎ │    0x080483b4      0fbe11         movsx edx, byte [ecx]       ; edx = serial[i]
│   │╎ │    0x080483b7      01d0           add eax, edx                ; eax += edx
│   │╎ │    0x080483b9      8945f8         mov dword [local_8h], eax   ; local_8h = eax
│   │╎ │    0x080483bc      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│   │╎ │    0x080483bf      c1f801         sar eax, 1                  ; eax =>> 1
│   │╎ │    0x080483c2      8945f8         mov dword [local_8h], eax   ; local_8h = eax
│   │╎ │    0x080483c5      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│   │╎ │    0x080483c8      b9000f0000     mov ecx, 0xf00              ; ecx = 0xf00 (3840)
│   │╎ │    0x080483cd      99             cdq                         ; ubah ke quad word
│   │╎ │    0x080483ce      f7f9           idiv ecx                    ; bagi dengan ecx
│   │╎ │    0x080483d0      8955f8         mov dword [local_8h], edx   ; local_8h = edx (sisa pembagian)
│   │╎ │    0x080483d3      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│   │╎ │    0x080483d6      83c00a         add eax, 0xa                ; eax += 0x0a
│   │╎ │    0x080483d9      8945f8         mov dword [local_8h], eax   ; local_8h = eax
│   │╎ │┌─< 0x080483dc      e91c000000     jmp 0x80483fd               ; lompat ke 0x80483fd

... snip ...

│      └──> 0x08048402      8b45fc         mov eax, dword [local_4h]   ; eax = local_4 (SERIAL_LEN)
│           0x08048405      48             dec eax                     ; eax--
│           0x08048406      8b4d08         mov ecx, dword [arg_8h]     ; ecx = serial
│           0x08048409      01c1           add ecx, eax                ; serial[SERIAL_LEN-1]
│           0x0804840b      8b45f8         mov eax, dword [local_8h]   ; eax = local_8h
│           0x0804840e      0fbe11         movsx edx, byte [ecx]       ; edx = serial[SERIAL_LEN-1]
│           0x08048411      39d0           cmp eax, edx                ; eax == edx?
│           0x08048413      b800000000     mov eax, 0                  ; eax = 0

Potongan disassembly di atas jika diterjemahkan ke dalam pseudocode, maka akan seperti ini:

  • Siapkan variabel yang nilai awalnya adalah 0, misalnya beri nama variabel X.
  • Ambil satu karakter dari serial.
  • Jumlahkan nilai pada variabel X dengan karakter tersebut.
  • Hasil penjumlahan tersebut kemudian digeser 1 bit ke kanan (Shift Arithmetic Right).
  • Hasil langkah di atas, kemudian digunakan untuk operasi modulus 0xf00.
  • Kemudian hasilnya ditambah 0x0a (10 desimal) dan disimpan ke variabel X.
  • Ulangi dari awal, hingga karakter ke 15 pada serial.
  • Setelah selesai, nilai akhir variabel X harus sama dengan karakter ke-16 pada serial.

Jika pseudocode di atas diterjemahkan ke dalam bahasa C, maka kurang lebih akan seperti ini:


static char serial[] = "0123456789ABCDEF";
int i, x = 0;

for (i = 0; i < 15; i++)
{
    x = (((x + serial[i]) >> 1) % 0xf00) + 0x0a;
}
serial[15] = x;

Dengan informasi di atas, kita dapat dengan mudah membuat key generator untuk aplikasi tersebut. Berikut ini adalah key generator menggunakan bahasa python:

#!/usr/bin/env python
import random

VALID_CHAR = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'

def serial_gen():
    s = ''
    x = 0
    for i in range(15):
        c = random.choice(VALID_CHAR)
        s += c
        x = (((x + ord(c)) >> 1) % 0xf00) + 0x0a

    s += chr(x)
    return s

try:
    while True:
        serial = serial_gen()
        if serial[-1] not in VALID_CHAR:
            continue
        print "Serial: {}".format(serial)
        c = raw_input("Generate again [y/n]? ")
        if c in 'yY':
            continue
        else:
            break
except:
    exit()

Simpan script di atas dengan nama keygen.py, lalu jalankan:

% chmod +x keygen.py
% ./keygen.py
Serial: TBYQ2921PHYEHBFY
Generate again [y/n]? y
Serial: M24FSXYRY446S20J
Generate again [y/n]? n

Gunakan serial tersebut untuk registrasi aplikasi keyGme:

% ./keyGme M24FSXYRY446S20J
Congratulation, You are registered now.

Nah, serialnya valid. Dengan demikian, kita telah berhasil membuat key generator. Sekian tutorial singkat kali ini, semoga bermanfaat. Terima kasih kepada Tuhan Yang Maha Esa, dan Anda yang telah membaca tutorial ini.