Mais um blog inútil.

Julho 31, 2009

kgme1

Filed under: Assembly,Cracking,Linux,Useless — falso @ 2:32

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!

4 comentários a “kgme1”

  1. falso fan #1 diz:

    Como o Obama diria: "Yes YOU CAN!"
    Um grande bem-haja

  2. mysql_dolphin diz:

    this dolphin shall never fail. (or be cracked, keygened, brute-forced)

  3. dongs diz:

    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.

Leave a Reply for dongs

widgeon
widgeon
widgeon
widgeon