kgme1
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!
Como o Obama diria: "Yes YOU CAN!"
Um grande bem-haja
this dolphin shall never fail. (or be cracked, keygened, brute-forced)
http://www.cse.yorku.ca/~oz/hash.html - djb2 hash
O falso tinha-me pedido para explicar a selecção da string correcta com XOR aqui, só me lembrei agora.
O código relevante é o seguinte:
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!"
Em primeiro lugar, assumimos que a serial introduzida é errada - mov eax, offset aBadSerial.
Depois comparamos a correcta com a introduzida - cmp [ebp-10h], ebx. Neste caso o registo EFLAGS é actualizado; em particular, a ZERO flag e' colocada a 1 se a serial for correcta. Visto que estamos a trabalhar com operações lógicas, queremos extender este 1 a todos os restantes bits do registo (EDX). A forma mais fácil de fazer isto é por negar o mesmo (-EDX = ~EDX+1). Tendo o nosso registo de "masking" pronto, basta efectuar um AND lógico com a outra string --- se EDX for 1, EAX = (aBadSerial^aGoodSerial); se EDX=0, EAX=0. Finalmente, efectuamos um XOR com aBadSerial novamente --- se EDX=1, EAX = (aBadSerial^aGoodSerial^aBadSerial) = aGoodSerial; se EDX=0, EAX=0^aBadSerial = aBadSerial.