Ora viva!
Já há uns tempos que tinha falado com o Dongs para fazer um KeygenMe para eu tentar keygennar porque nunca o tinha feito. Ele então fez um e eu pus mãos à obra.
[ kgme.tar.bz2 ]
$ ./kgme
Name: falso
Serial: 12345
Bad serial!
Abri então o ficheiro no IDA e andei a procurar por “Bad serial!”, e então que achei. Vai aqui um dump comentado:
0804830C movzx ecx, byte ptr [esi] ; ESI contem o "User"
0804830C ; e ECX contem o primeiro char do "User"
0804830F test ecx, ecx ; Se (ECX != 0x0) ZF=0 ELSE ZF=1
08048311 jz short loc_8048333 ; Se ZF=1 Salta pro drama
08048313 lea edx, [ebp-10Fh] ; EDX fica com "User" sem o primeiro char
08048319 mov ebx, 0DEADh
0804831E db 66h
0804831E nop ; No Operation
08048320
08048320 loc_8048320:
08048320 mov eax, ebx ; EAX passa a tar 0xDEAD tambem
08048322 shl eax, 5 ; Shift Logical Left 5 vezes
08048325 add eax, ecx ; Adiciona o valor do char do "User" (ECX) a EAX
08048327 movzx ecx, byte ptr [edx] ; le o proximo char pra ECX
0804832A add edx, 1 ; Add
0804832D add ebx, eax ; Add
0804832F test ecx, ecx ; Logical Compare
08048331 jnz short loc_8048320 ; Jump if Not Zero (ZF=0)
08048333
08048333 loc_8048333:
08048333 xor edx, edx ; Logical Exclusive OR
08048335 mov eax, offset aBadSerial ; "Bad serial!"
0804833A cmp [ebp-10h], ebx ; Compare Two Operands
0804833D setz dl ; Set Byte if Zero (ZF=1)
08048340 xor eax, offset aGoodSerial ; "Good serial!"
08048345 neg edx ; Two's Complement Negation
08048347 and eax, edx ; Logical AND
08048349 xor eax, offset aBadSerial ; "Bad serial!"
Então o funcionamento da coisa é mais ou menos o seguinte:
/\* inicialização(?) */
\* Le o Name para o registo ESI
\* Mete o primeiro char do Name no registo ECX
\* EDX fica com o Name sem o primeiro char
\* EBX fica com o valor 0xDEAD
/\* ciclo central */
\* Copia o valor de EBX para EAX
\* Faz um shift left 5 vezes a EAX
\* Adiciona o valor do char em ECX a EAX
\* Le o proximo char do Name para ECX
\* Adiciona 1 a EDX
\* Adiciona EAX a EBX
\* Se ECX nao for diferente de 0 salta para o inicio do ciclo
Vou tentar dar um exemplo:
User: Jim
/\* inicialização */
\* ESI = Jim
\* ECX = J (0x4a)
\* EDX = im
\* EBX = 0xdead
( valores dos registos )
-- ECX = J | EDX = im | EBX = 0xdead –
/\* ciclo central */
( Primeiro ciclo - J )
\* EAX = EBX
-- ECX = J | EDX = im | EBX = 0xdead | EAX = 0xdead –
\* shift left 5 vezes a EAX
-- ECX = J | EDX = im | EBX = 0xdead | EAX = 0x1bd5a0 –
\* adiciona o valor do char em ECX (J - 0x4a) a EAX
-- ECX = J | EDX = im | EBX = 0xdead | EAX = 0x1bd5ea –
\* Le o proximo char do Name pra ECX
-- ECX = i | EDX = im | EBX = 0xdead | EAX = 0x1bd5ea –
\* Adiciona 1 a EDX
-- ECX = i | EDX = m | EBX = 0xdead | EAX = 0x1bd5ea –
\* Adiciona EAX a EBX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x1bd5ea –
\* Se ECX é diferente de 0 então do inicio de novo!
( Segundo ciclo - i )
\* EAX = EBX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x1cb497 –
\* shift left 5 vezes a EAX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x39692e0 –
\* adiciona o valor do char em ECX (i - 0x69) a EAX
-- ECX = i | EDX = m | EBX = 0x1cb497 | EAX = 0x3969349 –
\* Le o proximo char do Name pra ECX
-- ECX = m | EDX = m | EBX = 0x1cb497 | EAX = 0x3969349 –
\* Adiciona 1 a EDX (fica limpo)
-- ECX = m | EBX = 0x1cb497 | EAX = 0x3969349 –
\* Adiciona EAX a EBX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x3969349 –
\* Se ECX é diferente de 0 então do inicio de novo!
( Terceiro ciclo - m )
\* EAX = EBX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x3b347e0 –
\* shift left 5 vezes a EAX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x7668fc00 –
\* adiciona o valor do char em ECX (m - 0x6d) a EAX
-- ECX = m | EBX = 0x3b347e0 | EAX = 0x7668fc6d –
\* Le o proximo char do Name pra ECX (fica limpo)
-- EBX = 0x3b347e0 | EAX = 0x7668fc6d –
\* Adiciona 1 a EDX (continua limpo)
-- EBX = 0x3b347e0 | EAX = 0x7668fc6d –
\* Adiciona EAX a EBX
-- EBX = 0x7a1c444d | EAX = 0x7668fc6d –
\* ECX é zero, então temos a serial em EBX
$ kgme
Name: Jim
Serial: 7a1c444d
Good serial!
Fiz uma implementação em C++ do keygen, que a única coisa que usa de c++ é o cout mas mesmo assim, aqui vai:
//============================================================================
// Name : lolkg.cpp
// Author : falso
// Version : 0.1-pre1-beta2-rc4
// Copyright : WTFPL
// Description : Keygen para o kgme do dongs
//============================================================================
#include <iostream>
using namespace std;
int main() {
char name[50];
cout << "| | |\n";
cout << "| ,---.| |__/ ,---.\n";
cout << "| | || | \\ | |\n";
cout << "`---'`---'`---'` ``---|\n";
cout << " `---'\n";
cout << "Name: " << flush;
cin >> name;
int ebx = 0xdead;
int eax = ebx;
int ecx = name[0];
int i = 1;
while (ecx != 0x0) {
eax = ebx;
eax = eax << 5;
eax = eax + ecx;
ecx = name[i];
i++;
ebx = ebx + eax;
}
printf("Serial: %x\n",ebx);
return 0;
}
E já agora, uma em MIPS assembly pro PCSpim:
# this asm can never fail
#
# lolkg
.data
us: .asciiz "User: "
u: .space 50
.text
.globl main
main:
# print user..
li $v0,4
la $a0,us
syscall
# get string
li $v0,8
la $a0,u
syscall
# variavel inicial do algoritmo
li $s0,0xdead # s0 - ebx
move $s1,$s0 # s1 - eax
la $s5,u # tmp
#apanha o primeiro char do Username
lb $s2,0($s5) # s2 - ecx
_while:
move $s1,$s0
sll $s1,$s1,5
add $s1,$s1,$s2
addi $s5,$s5,1
lb $s2,0($s5) # s2 - ecx
addu $s0,$s0,$s1
bne $s2,0xa,_while
# imprime o serial, mas em decimal ;-(
# nao sei como imprimir em hex, pois o serial é em HEX
li $v0,1
move $a0,$s0
syscall
#sai
li $v0,10
syscall
Espero que todo este conhecimento inútil sirva para alguém, nem que seja pra despertar um bichozinho pequenino pro cracking! Fiquem bem, e crackem muito!