Tutorial de Keygen #01
Autor: rumble
Data: 12/05/2007
Alvo: Sistema de Gerenciamento de Telemensagens e Cestas - SGETC3000
Tools: Delphi 10, OllyDBG (plugin: API Break), RDG Packer Detector
Dificuldade: Mdio / Intermedirio
Compilador: Borland Delphi v6.0 - v7.0
Packer: Aspack v2.12
INTRODUO
Neste tutorial vou explicar como fazer um keygen para um programa que est compilado
em Delphi e compactado com Aspack. No irei fazer o unpack do mesmo, j que o
objetivo proposto construir o keygen. No entrarei em detalhes de uso do OllyDBG.
necessrio um conhecimento intermedirio!!!
CONHECENDO O INIMIGO
INICIANDO O PROGRAMA E CHEGANDO AT A ROTINA DO SERIAL
Carregue o programa no OllyDBG e aperte F9. Aps o mesmo ter carregado, a seguinte
tela ser exibida.
Clique em OK e veremos a tela para entrar com as informaes de registro. Temos os
seguintes campos para preencher: Nmero de Srie do sistema, Razo Social ou Nome
Fantasia, Data de Validade da Licena, Nmero de Registro, Nmero de Autenticao das
Licenas Informadas, Se mono ou multi usurio, se for multi, o nmero de licenas
adquiridas.
Vamos preecher as informaes conforme a tela abaixo:
Vamos voltar ao OllyDBG e usar o plugin API BREAK pra setar um breakpoint no Point H.
Clique em OK e em seguida, no boto Registrar do programa. Voc ir cair aqui:
Remova o breakpoint e clique em Debug -> Execute Till User Code (ALT+F9), e depois v
apertando F8 at chegar neste ponto:
Podemos ver o valor de EAX = 1F , que em decimal equivale a 31, e uma mensagem um
pouco acima informando sobre Problemas com data do sistema.... Ento sabemos que
esta rotina est testando se a data que digitamos vlida.
00B40C02 JE 00B40CF6 ; Testa o Dia
00B40C08 MOV EAX,DWORD PTR SS:[EBP-4]
00B40C0B MOV EAX,DWORD PTR DS:[EAX+3D4]
00B40C11 CALL 0098BFD8
00B40C16 TEST EAX,EAX ; Testa o Mes
00B40C18 JE 00B40CF6
00B40C1E MOV EAX,DWORD PTR SS:[EBP-4]
00B40C21 MOV EAX,DWORD PTR DS:[EAX+3D8]
00B40C27 CALL 0098BFD8
00B40C2C TEST EAX,EAX ; Testa o Ano
Trace com F8 at chegar aqui:
00B41118 PUSH DWORD PTR SS:[EBP-58] ; Tamanho da Razao Social : RUMBLE (6)
00B4111B PUSH DWORD PTR DS:[B6B1D0] ; Dia: 31
00B41121 PUSH DWORD PTR DS:[B6B1D4] ; Mes: 12
00B41127 PUSH DWORD PTR DS:[B6B1D8] ; Ano: 2200
00B4112D PUSH DWORD PTR DS:[B6B1DC] ; Chr: Fixo "S"
00B41133 PUSH DWORD PTR SS:[EBP-18] ; Numero de Serie do Sistema : 7544-832E-B6D6-230F
00B41136 PUSH DWORD PTR SS:[EBP-14] ; Chr: Fixo "R" se Multiusuario ou M se for Mono
00B41139 PUSH DWORD PTR SS:[EBP-10] ; Quantidade de Maquinas: 10 ou Char "N" se for Mono
Podemos ver que ele juntou todos os nossos valores digitados. No tem segredo, s ir
traando que ia chegar nessa rotina tranquilamente. Todos esses dados sero
concatenados.
Vamos continuar traando at aqui:
00B4115A CMP DWORD PTR SS:[EBP-5C],0
Onde ele compara se a chave de autenticao (no nosso caso 5678) foi preenchida.
Agora chegue at esse ponto e entre na call.
0B41191 CALL 00B3F768
E trace com F8 at chegar aqui:
0B3F7D5 PUSH 2
0B3F7D7 LEA EAX,DWORD PTR SS:[EBP-8]
0B3F7DA PUSH EAX
0B3F7DB MOV ECX,DWORD PTR DS:[EBX+38] Chave Fixa: "7319"
0B3F7DE MOV EDX,DWORD PTR DS:[EBX+34] Nossa autenticao: "5678"
0B3F7E1 MOV EAX,EBX
0B3F7E3 CALL 00B3F81C Rotina que calcula a autenticao
Em 0B3F7E3 temos uma CALL a 00B3F81C que ir calcular a autenticao dos dados que
digitamos. O Resultado dessa autenticao tem que ser:
Para multi usurios: tam+dia+ms+ano+S+numserie+R+nummaquinas
Para mono usurio: tam+dia+ms+ano+S+numserie+M+N
Onde:
tamanho da razo social: tam
dia da licena: dia
dia da licena: ms
ano da licena: ano
letra S fixa: S
n de srie do sistema: numserie
Tipo de Licena: R - para multi usurios ou MN para mono usurio
Nummaquinas: 1 a 10 de acordo com o nmero de licenas ou nulo para mono
Que no meu caso seria assim:
631122200S7544-832E-B6D6-230FR10, ento esta call tem que processar o nosso nmero
de autenticao e o resultado desta pequena decriptografia tem que ter o resultado igual
a nossa chave a ser autenticada.
Vamos entrar na call com F7. Aqui explico toda a rotina da call. Esta funo serve para
descriptografar a chave de autenticao de forma que ela seja igual a, no meu caso,
631122200S7544-832E-B6D6-230FR10.
Aqui vai a rotina:
00B3F911 MOV EBX,1 ; inicia o for com 1
00B3F916 MOV DWORD PTR SS:[EBP-C],2 ; move 2 para pegar 2 letras por vez
00B3F91D JMP 00B3F9D9 ; pula para o inicio do laco
00B3F922 LEA EAX,DWORD PTR SS:[EBP-20]
00B3F925 PUSH EAX
00B3F926 MOV ECX,DWORD PTR SS:[EBP-C] ; Pega a semente de decriptacao
00B3F929 MOV EDX,EBX ; Pega o valor do i
00B3F92B MOV EAX,DWORD PTR SS:[EBP-4] ; Pega a chave de autenticacao
00B3F92E CALL <Copy> ; Pega os Dois bytes da autenticacao
00B3F933 MOV ECX,DWORD PTR SS:[EBP-20] ; Primeiro laco: 0x56, Segundo lao 0x78
00B3F936 LEA EAX,DWORD PTR SS:[EBP-14]
00B3F939 MOV EDX,00B3FA2C
00B3F93E CALL <IntToHex> ; Transforma os dois chars em Hexa
00B3F943 MOV EAX,DWORD PTR DS:[ESI]
00B3F945 CALL <Length> ; Tamanho da String Decriptada
00B3F94A INC EAX ; Aumenta o tamanho
00B3F94B PUSH EAX
00B3F94C MOV EAX,DWORD PTR SS:[EBP-8] ; Pega a semente de decriptacao
00B3F94F CALL <Length> ; INICIO DO LACO DA SEMENTE
00B3F954 POP EDX ; .....
00B3F955 XCHG EAX,EDX ; Faz um for pela semente de decriptacao
00B3F956 MOV ECX,EDX ; onde que o i percorre toda a semente
00B3F958 CDQ ; quando i passa a ser maior que
00B3F959 IDIV ECX ; o tamanho da semente, entao o
00B3F95B TEST EDX,EDX ; i passa a receber 1
00B3F95D JE SHORT 00B3F97B ; e assim sucessivamente
00B3F95F MOV EAX,DWORD PTR DS:[ESI] ; ate que todos os chars tenham sidos
00B3F961 CALL <Length> ; processados e que cada uma letra
00B3F966 INC EAX ; entrou no laco da semente
00B3F967 PUSH EAX ; de decriptacao.
00B3F968 MOV EAX,DWORD PTR SS:[EBP-8] ; primeiro laco: i = 7
00B3F96B CALL <Length> ; segundo laco: i = 3
00B3F970 POP EDX ; terceiro laco: i = 1
00B3F971 XCHG EAX,EDX ; quarto laco: i = 9
00B3F972 MOV ECX,EDX ; quinto laco, i volta a ser 1
00B3F974 CDQ ; entao o char eh i = 7
00B3F975 IDIV ECX ; char eh i = 3
00B3F977 MOV EDI,EDX ; char eh i = 1
00B3F979 JMP SHORT 00B3F985 ; char eh i = 9
00B3F97B MOV EAX,DWORD PTR SS:[EBP-8] ; char eh i = 7....
00B3F97E CALL <Length> ; e por ai vai :)
00B3F983 MOV EDI,EAX ; sucessivamente....
00B3F985 MOV EAX,DWORD PTR SS:[EBP-14] ; FIM DO LACO DA SEMENTE
00B3F988 CALL 00809F10
00B3F98D MOV EDX,DWORD PTR SS:[EBP-8] ; pega o char da semente
00B3F990 MOVZX EDX,BYTE PTR DS:[EDX+EDI-1] ; indice [i] do laco da semente em edx
00B3F995 SUB EAX,EDX ; Os dois caracteres copiados da autent em hexa
00B3F997 MOV DWORD PTR SS:[EBP-10],EAX ; menos o indice i da semente
00B3F99A MOV EAX,DWORD PTR SS:[EBP-14] ; Resultado da subtracao
00B3F99D CALL 00809F10
00B3F9A2 MOV EDX,DWORD PTR SS:[EBP-10] ; Valor em EDX
00B3F9A5 SUB EDX,EAX ; Subtrai o valor de novo
00B3F9A7 MOV EAX,DWORD PTR SS:[EBP-8] ; pelo indice atual da semente
00B3F9AA MOVZX EAX,BYTE PTR DS:[EAX+EDI-1] ; pega o indice i da semente
00B3F9AF ADD EDX,EAX ; e torna soma-lo com o valor apenas para
00B3F9B1 TEST EDX,EDX ; conferir se foi igual a zero.
00B3F9B3 JNZ SHORT 00B3F9D6 ; se for diferente entao pega o next i
00B3F9B5 LEA EAX,DWORD PTR SS:[EBP-24] ; se for igual ento continue a processar
00B3F9B8 MOV EDX,DWORD PTR SS:[EBP-10] ; Pega o resultado da subtracao
00B3F9BB CALL 0080416C ; Converte para String
00B3F9C0 MOV EDX,DWORD PTR SS:[EBP-24] ; Pega a string decriptada
00B3F9C3 MOV EAX,ESI ; Pega o valor da converso de inteiro pra str
00B3F9C5 CALL 0080424C ; Concatena
00B3F9CA ADD EBX,2 ; Soma 2 ao Contador para pegar + 2 palavras
00B3F9CD MOV DWORD PTR SS:[EBP-C],2
00B3F9D4 JMP SHORT 00B3F9D9
00B3F9D6 INC DWORD PTR SS:[EBP-C]
00B3F9D9 MOV EAX,DWORD PTR SS:[EBP-4] ; Pega a nossa chave
00B3F9DC CALL <Length> ; Pega o Tamanho
00B3F9E1 CMP EBX,EAX ; Compara o indice do for com o tamanho da encriptacao
00B3F9E3 JLE 00B3F922 ; Eh menor ou igual entao continue a processar
00B3F9E9 XOR EAX,EAX ; FIM DO LACO da decriptacao :)
Ento j que entendemos a rotina de descriptografar a string, podemos criar a encriptao
facilmente. Onde que:
Se queremos que a nossa string decriptada seja igual a RUMBLE ento temos:
R = 0x52 + 0x37 (7) = 0x89
U = 0x55 + 0x33 (3) = 0x88
M = 0x4D + 0x31 (1) = 0x7E
B = 0x42 + 0x39 (9) = 0x7B
L = 0x4C + 0x37 (7) = 0x83
E = 0x45 + 0x33 (3) = 0x78
Onde que devemos somar os valores da semente para cada letra da palavra a ser
encriptada, j que a rotina subtrai os valores. Ento a nossa string encriptada passar a
ser: 89887E7B8378.
Voltando a string de autenticao do nosso software...
A autenticao para a nossa string:
631122200S7544-832E-B6D6-230FR10
:
6D66626A696563696786686E6B675E716A6576667969756F646564697D856269
Ento deixa eu fazer o passo-a-passo dessa encriptao.
Char Valor Ascii (hex) Semente[i] (hex) Resultado (hex)
6 = 0x36 + 0x37 (7) = 0x6D
3 = 0x33 + 0x33 (3) = 0x66
1 = 0x31 + 0x31 (1) = 0x62
1 = 0x31 + 0x39 (9) = 0x6A
2 = 0x32 + 0x37 (7) = 0x69
2 = 0x32 + 0x33 (3) = 0x65
2 = 0x32 + 0x31 (1) = 0x63
0 = 0x30 + 0x39 (9) = 0x69
0 = 0x30 + 0x37 (7) = 0x67
S = 0x53 + 0x33 (3) = 0x86
7 = 0x37 + 0x31 (1) = 0x68
5 = 0x35 + 0x39 (9) = 0x6E
4 = 0x34 + 0x37 (7) = 0x6B
4 = 0x34 + 0x33 (3) = 0x67
- = 0x2D + 0x31 (1) = 0x5E
8 = 0x38 + 0x39 (9) = 0x71
3 = 0x33 + 0x37 (7) = 0x6A
2 = 0x32 + 0x33 (3) = 0x65
E = 0x45 + 0x31 (1) = 0x76
- = 0x2D + 0x39 (9) = 0x66
B = 0x42 + 0x37 (7) = 0x79
6 = 0x36 + 0x33 (3) = 0x69
D = 0x44 + 0x31 (1) = 0x75
6 = 0x36 + 0x39 (9) = 0x6F
- = 0x2D + 0x37 (7) = 0x64
2 = 0x32 + 0x33 (3) = 0x65
3 = 0x33 + 0x31 (1) = 0x64
0 = 0x30 + 0x39 (9) = 0x69
F = 0x46 + 0x37 (7) = 0x7D
R = 0x52 + 0x33 (3) = 0x85
1 = 0x31 + 0x31 (1) = 0x62
0 = 0x30 + 0x39 (9) = 0x69
Concatenando todos os resultados teremos a seguinte string:
6D66626A696563696786686E6B675E716A6576667969756F646564697D856269
Pronto j entendemos como feita a encriptao e a decriptao. Depois vamos
codificar isso no Delphi. Agora vamos para a parte da verificao da chave. J que temos
a nossa chave de autenticao, vamos digitar a mesma no formulrio de registro.
Aps ter digitado, clique no boto Registrar. O OllyDBG vai brecar nos breakpoints
anteriores. Apenas limpe-os e trace com o F8 at aqui:
00B411AA MOV EAX,DWORD PTR SS:[EBP-C] ; Chave de Autenticacao Do Programa
00B411AD MOV EDX,DWORD PTR SS:[EBP-1C] ; Nossa chave que foi decriptada
00B411B0 CALL 00804354 ; Compara as Duas chaves (decriptada e a original)
00B411B5 JE SHORT 00B4120F ; Se for igual entao pule para 00B4120F (Good Boy)
Aps este ponto ele ir pular uns jmps e entrar em algumas calls para processar o
nmero de registro. Para te adiantar, esse programa protegido com o componente
TMxProtector, ento se voc quiser e souber, com um pouco de modificao, possvel
codificar um keygen genrico para qualquer software protegido com o mesmo, bastando
ver o software em um editor de recursos como o ResTunner, desde que o mesmo no
esteja packed. Caso esteja, s fazer o unpacking manual ou ler os resources na
memria.
Porm este que estamos lidando est packeado com Aspack e unpackear apenas
para ver as strings e resources no seria interessante. Vamos a parte 2 deste tutorial, a
gerao do nmero de registro.
Estamos parados no EIP 00B411B5, trace com o F8 at chegar aqui:
00B42F0E MOV EAX, DWORD PTR DS:[EBX+428]
00B42F14 CALL 00999610
Coloque um breakpoint em 00B42F14 e execute o software (F9).
Ento j sabemos que ele aceitou a nossa chave de autenticao :), agora vamos
entender como ele gera o cdigo do serial. Clique no OK da mensagem e volte a clicar no
boto Registrar. Voc cair no ponto onde tinha colocado o breakpoint.
Entre nesta call com F7. Trace at o endereo 0099966A. No vou explicar todo o
cdigo, somente as partes interessantes.
0099966A CALL DWORD PTR DS:[ESI+E0]
00999670 LEA EAX,DWORD PTR DS:[ESI+4C]
00999673 MOV EDX,DWORD PTR SS:[EBP-C] ; Pega a string 15072004 para passar pro TMxProtector
00999676 CALL 00804018
0099967B LEA ECX,DWORD PTR SS:[EBP-8]
0099967E MOV EDX,DWORD PTR DS:[ESI+4C] ; Coloca o nome "15072004" em EDX no TMxProtector
00999681 MOV EAX,ESI
00999683 CALL 0099985C ; Processa o nome colocado no TMxProtector
Seguinte, este programa usa o componente TmxProtector. Eu sei porque sou seu
amigo desde a verso anterior do mesmo...
Ele aparece assim no resource do programa:
object prot: TmxProtector
CodeKey = 'Ignore'
ProtectionTypes = [stDayTrial, stRegister]
Options = [poAutoInit, poCheckSytemTime, poPasswordOnce, poUseHardwareKey]
Expiration = 38159.970899756900000000
MaxStartNumber = 0
MaxDayNumber = 31
UserName = '15072004'
Version = '1.23'
OnExpiration = protExpiration
OnDayTrial = protDayTrial
OnInvalidSystemTime = protInvalidSystemTime
OnInvalidSerialNumber = protInvalidSerialNumber
OnGetSerialNumber = protGetSerialNumber
OnUnknownHardware = protUnknownHardware
Left = 472
Top = 80
UniqueCodeID =
'7A404635414532383A27383F343C22242227223957502120342B2A285C5F2610' +
'67141A1C175B'
UniqueID =
'210E52006A73621506737E697F236C630562686E647B697F4E0265731466761D' +
'746608510520'
end
O seu uso no software ocorre da seguinte maneira:
O nome de usurio igual para todo mundo;
15072004
Para registrar basta apenas o n de srie do sistema;
No meu caso : 7544-832E-B6D6-230F, como descrito no formulrio de
registro.
Vamos entrar na call do endereo 00999683 com F7. Vamos traar at aqui:
00999898 CMP DWORD PTR SS:[EBP-8],0
0099989C JNZ SHORT 009998B8
0099989E PUSH 0
009998A0 MOV CX,WORD PTR DS:[999B64]
009998A7 MOV DL,2
009998A9 MOV EAX,00999B70 ; ASCII "User name is empty. Please set this propery before!"
009998AE CALL 00860E24
009998B3 JMP 00999B24
009998B8 LEA EDX,DWORD PTR SS:[EBP-28]
009998BB MOV EAX,DWORD PTR SS:[EBP-8]
009998BE CALL 008098B4 ; Esta call passa o nome para maiusculo
009998C3 MOV EDX,DWORD PTR SS:[EBP-28] ; Coloca o valor do maiusculo em EDX
O que este pedao de cdigo faz comparar se foi colocado um nome de usurio, e
se tiver sido colocado, ento passe o mesmo para maisculo. Essa parte do nome em
maisculo eu vou pular. Mas basicamente subtrair 0x20 do caractere. Ou seja, passar os
caracteres para maisculos, os nmeros no entram nesta condio pois esta call processa
apenas chars entre a..z (0x61 e 0x7A) em hexa.
Continuando... Neste pedao de cdigo, feito um clculo de acordo com o nome.
009998EF MOV EBX,1 ; Inicio do loop
009998F4 MOV EAX,DWORD PTR SS:[EBP-8] ; Pega o nome "15072004"
009998F7 MOVZX EAX,BYTE PTR DS:[EAX+EBX-1] ; Pega o byte no indice do contador
009998FC IMUL EBX ; Multiplica o byte do nome pelo valor do contado
009998FE ADD DWORD PTR SS:[EBP-24],EAX ; Guarda a soma em [ebp-24]
00999901 INC EBX ; Incrementa o contador
00999902 DEC ESI ; Diminui o tamanho do nome
00999903 JNZ SHORT 009998F4 ; Se ESI = 0 ento sai do loop
O que isso? Ele pega cada valor do nome, multiplica pelo ndice do contador, e o
valor desta multiplicao guarda em uma varivel.
BYTE CONTADOR RESULTADO
0x31 * 1 = 0x31
0x35 * 2 = 0x6A
0x30 * 3 = 0x90
0x37 * 4 = 0xDC
0x32 * 5 = 0xFA
0x30 * 6 = 0x120
0x30 * 7 = 0x150
0x34 * 8 = 0x1A0
Vamos chamar a varivel de soma.
Soma = 31+6A+90+DC+FA+120+150+1A0
Soma = 711
Lembrando que estamos trabalhando em base hexadecimal.
Vamos guardar a varivel soma. Este pedao de cdigo abaixo usa o mesmo.
Traando um pouco chegamos at aqui:
009999D8 MOV EBX,1 ; Inicia o contador com 1
009999DD MOV EAX,DWORD PTR SS:[EBP-1C] ; Pega o ID sem "-"
009999E0 MOVZX EAX,BYTE PTR DS:[EAX+EBX-1] ; Pega o indice i do ID
009999E5 IMUL EBX ; Multiplica pelo contador
009999E7 ADD DWORD PTR SS:[EBP-24],EAX ; Soma o valor da multiplicacao ao SOMA do nome
009999EA INC EBX ; Aumenta o contador
009999EB DEC ESI ; Diminui o tamanho da string
009999EC JNZ SHORT 009999DD ; Compara se o tamanho eh zero
Esta parte faz o mesmo processo do clculo do nome. Ele vai somando o valor de
cada multiplicao do byte[i] pelo contador e adicionando o resultado na varivel anterior
SOMA.
Minha ID sem os - : 7544832EB6D6230F. Ento faremos um lao que iniciar
em 1 e ir at o tamanho do ID que 16.
BYTE (hex) CONTADOR (dec) RESULTADO (hex)
0x37 * 1 = 0x37
0x35 * 2 = 0x6A
0x34 * 3 = 0x9C
0x34 * 4 = 0xD0
0x38 * 5 = 0x118
0x33 * 6 = 0x132
0x32 * 7 = 0x15E
0x45 * 8 = 0x228
0x42 * 9 = 0x252
0x36 * 10 = 0x21C
0x44 * 11 = 0x2EC
0x36 * 12 = 0x288
0x32 * 13 = 0x28A
0x33 * 14 = 0x2CA
0x30 * 15 = 0x2D0
0x46 * 16 = 0x460
37+6A+9C+D0+118+132+15E+228+252+21C+2EC+288+28A+2CA+2D0+460 = 1E43
Soma = Soma + 1E43
Soma = 2554
Vamos continuar logo abaixo, onde o programa ir calcular o serial. Todo programa
protegido com o TmxProtector tem uma GUID embutida. Ela obtida atravs da
decriptao desses dois campos presentes no componente:
UniqueCodeID =
'7A404635414532383A27383F343C22242227223957502120342B2A285C5F2610' +
'67141A1C175B'
UniqueID =
'210E52006A73621506737E697F236C630562686E647B697F4E0265731466761D' +
'746608510520'
Porm nos no iremos calcular essa decriptao, o prprio componente ir decript-lo pra
gente na rotina.
CALCULANDO O NMERO DE SRIE
A rotina est toda comentada.
009999EE MOV EBX,1 ; Inicia o contador
009999F3 MOV EAX,DWORD PTR SS:[EBP-24] ; Coloca em EAX o valor da soma. No nosso caso (0x2554)
009999F6 ADD EAX,EBX ; Acrescenta o valor do contador em soma
009999F8 MOV ECX,0C8 ; Coloca em ECX o valor 0xC8
009999FD CDQ
009999FE IDIV ECX ; Soma dividido por C8
00999A00 MOV EAX,EDX ; Coloca o Resto da Divisao em EAX
00999A02 MOV EDX,EBX ; Coloca o contador em EDX
00999A04 ADD EDX,EDX ; EDX + EDX
00999A06 MOV ECX,DWORD PTR SS:[EBP-4]
00999A09 MOV ECX,DWORD PTR DS:[ECX+40] ; Poe a GUID: {76B114CF-814A-4BF9-817A-D9DC70F49B6E} em ECX
00999A0C MOV DL,BYTE PTR DS:[ECX+EDX-1] ; j = 0; -> j = j + 2; DL indice j. Sempre de 2 em 2
00999A10 MOV ECX,DWORD PTR SS:[EBP-4]
00999A13 MOV ECX,DWORD PTR DS:[ECX+40] ; Coloca a GUID em ECX
00999A16 MOV CL,BYTE PTR DS:[ECX+EBX-1] ; Pega o Indice i da GUID
00999A1A XOR DL,CL ; k = i xor j
00999A1C MOV ECX,DWORD PTR SS:[EBP-8] ; Coloca o nome em ECX
00999A1F MOV ESI,DWORD PTR SS:[EBP-10] ; contador j
00999A22 MOV CL,BYTE PTR DS:[ECX+ESI-1] ; Coloca o Nome indice i em CL
00999A26 XOR DL,CL ; k = k xor nome[i]
00999A28 AND EDX,0FF ; k = k and 0xFF
00999A2E XOR EAX,EDX ; (Resto da Divisao da Soma por C8) xor k
00999A30 LEA ECX,DWORD PTR SS:[EBP-30] ; Coloca em ECX a String do serial que ja foi
calculada
00999A33 MOV EDX,2 ; Parametro para converter para HEXA
00999A38 CALL 00809E94 ; Converte para Hex com 2 casas decimais
00999A3D MOV EDX,DWORD PTR SS:[EBP-30] ; Coloca a string convertida em EDX
00999A40 LEA EAX,DWORD PTR SS:[EBP-18]
00999A43 CALL 0080424C ; Concatena a String
00999A48 MOV EAX,DWORD PTR SS:[EBP-8] ; Coloca em EAX a string concatenada
00999A4B MOV EDX,DWORD PTR SS:[EBP-10] ; EDX recebe o contador
00999A4E MOVZX EAX,BYTE PTR DS:[EAX+EDX-1] ; Pega o nome[i] e coloca em EAX
00999A53 XOR EAX,EBX ; Faz um xor entre o nome[i] e o contador
00999A55 ADD DWORD PTR SS:[EBP-24],EAX ; Adiciona o resultado deste xor a SOMA
00999A58 MOV EAX,DWORD PTR SS:[EBP-4]
00999A5B TEST BYTE PTR DS:[EAX+49],8
00999A5F JE SHORT 00999A6E
00999A61 MOV EAX,DWORD PTR SS:[EBP-1C] ; Coloca a ID sem os "-" em EAX
00999A64 MOVZX EAX,BYTE PTR DS:[EAX+EBX-1] ; Pega o caracter da ID no indice[i]
00999A69 XOR EAX,EBX ; Faz um xor entre o contador e a ID[i]
00999A6B ADD DWORD PTR SS:[EBP-24],EAX ; Adiciona o resultado deste xor a soma
00999A6E INC DWORD PTR SS:[EBP-10] ; Aumenta o Contador
00999A71 INC DWORD PTR SS:[EBP-14] ; Aumenta o contador
00999A74 MOV EAX,DWORD PTR SS:[EBP-8] ; Coloca o nome em EAX
00999A77 CALL <Length> ; Pega o tamanho do nome
00999A7C CMP EAX,DWORD PTR SS:[EBP-10]
00999A7F JGE SHORT 00999A88
00999A81 MOV DWORD PTR SS:[EBP-10],1
00999A88 CMP DWORD PTR SS:[EBP-14],0B ; compara se j > 0xB
00999A8C JLE SHORT 00999A95 ; Se for menor ou igual entao pula
00999A8E MOV DWORD PTR SS:[EBP-14],1
00999A95 INC EBX ; Aumenta o contador do FOR
00999A96 CMP EBX,0D ; Verifica se o contador i e igual a 0xD
00999A99 JNZ 009999F3 ; Se nao for entao pula...
00999A9F MOV EAX,DWORD PTR SS:[EBP-18] ; Ao acabar o loop, EAX recebe a string do serial.
Aps ter rodado este loop, eax receber, no meu caso:
"E07C9C789C6F46330383536A"
Agora o programa coloca o caractere - a cada 8 caracteres como mostra o cdigo
a seguir:
00999AA2 CALL <Length> ; Pega o tamanho
00999AA7 CMP EAX,18 ; Compara o tamanho com 18
00999AAA JLE SHORT 00999AC2 ; Como o tamanho da nossa string eh 18 entao pule ->
00999AAC LEA EAX,DWORD PTR SS:[EBP-18]
00999AAF PUSH EAX
00999AB0 MOV ECX,18
00999AB5 MOV EDX,1
00999ABA MOV EAX,DWORD PTR SS:[EBP-18]
00999ABD CALL 0080444C
00999AC2 LEA EAX,DWORD PTR SS:[EBP-34] <-
00999AC5 PUSH EAX
00999AC6 MOV ECX,8 ; Copie 8 caracteres
00999ACB MOV EDX,1 ; A partir da Posicao 1
00999AD0 MOV EAX,DWORD PTR SS:[EBP-18] ; Da String E07C9C789C6F46330383536A
00999AD3 CALL 0080444C ; Funcao COPY(Str, Inicio, Quant)
00999AD8 PUSH DWORD PTR SS:[EBP-34] ; Coloca o resultado "E07C9C78" na pilha
00999ADB PUSH 00999BAC ; Pega o caracter "-"
00999AE0 LEA EAX,DWORD PTR SS:[EBP-38]
00999AE3 PUSH EAX
00999AE4 MOV ECX,8 ; Copie 8 Caracteres
00999AE9 MOV EDX,9 ; A partir da Posicao 9
00999AEE MOV EAX,DWORD PTR SS:[EBP-18] ; Da String E07C9C789C6F46330383536A
00999AF1 CALL 0080444C ; Funcao COPY(Str, Inicio, Quant)
00999AF6 PUSH DWORD PTR SS:[EBP-38] ; Coloca o resultado "9C6F4633" na pilha
00999AF9 PUSH 00999BAC ; Pega o caracter "-"
00999AFE LEA EAX,DWORD PTR SS:[EBP-3C]
00999B01 PUSH EAX
00999B02 MOV ECX,8 ; Copie 8 caracteres
00999B07 MOV EDX,11 ; A partir da Posicao 0x11
00999B0C MOV EAX,DWORD PTR SS:[EBP-18] ; Da String E07C9C789C6F46330383536A
00999B0F CALL 0080444C ; Funcao COPY(Str, Inicio, Quant)
00999B14 PUSH DWORD PTR SS:[EBP-3C] ; Coloca o resultado "0383536A" na pilha
Ao processar este cdigo j teremos o nosso serial: E07C9C78-9C6F4633-0383536A.
Vamos colocar o cdigo no programa e ver o que acontece:
Agora clicamos no boto registrar. Removemos os breakpoints do Olly e temos a
seguinte mensagem pulando aos nossos olhos:
Conferindo no menu Sobre -> Registro....
Ahhh danado hauahuha :P Conseguimos reverter toda a rotina do programa... Mas
o que ser que falta agora? Codificar o keygen! Ento vamos por a mo na massa. Abra o
Delphi e inicie um novo projeto. Adicione 7 campos Edits, 2 campos ComboBox e 1 boto.
Neste exemplo estarei usando o Delphi com VCL mesmo, deixo pra outra hora
colocar o keygen com msica, e com tamanho < que 50kb.
Coloque o programa assim:
eu sei que ta feio, mas o objetivo te ensinar a programar o keygen, layout vem
depois... Defina nome coerente aos campos.
Adicione na ListBox TipoDeLicenca os valores: Monousurio e Multiusurio.
Adicione na ListBox QuantLic os valores: 2 Maq, 3 Maq, 4 Maq, 5 Maq, ... 10 Maq.
Adicione o cdigo ao boto Gerar:
procedure [Link](Sender: TObject);
var
tmp : string;
begin
if (Length([Link]) = 19) then // Compara se o Hardware ID eh igual a 19
begin
[Link] := GeraSerial('15072004', [Link]); // Funo pra calcular o serial.
// Concatena os campos do keygen
tmp := IntToStr(Length([Link]))+[Link] + [Link] + [Link] +'S'+[Link];
if [Link] = 0 then // Se for o monousuario
tmp := tmp + 'MN' // Coloca no fim da str temporria a palavra MN
else // se for multiusuario ento coloca R + a quant de maquinas
tmp := tmp + 'R'+IntTOStr([Link] + 2);
[Link] := GeraAut(tmp, '7319'); // Chama a funo autenticao
end
else
[Link] := 'Digite um ID Vlido!'; // Caso o ID no seja igual a 19... Erro!!!
end;
Agora est faltando a funo GeraAut e a funo GeraSerial que ir nos retornar os
cdigos para registrar o nosso software. Vamos codificar a funo GeraAut:
function GeraAut(plaintext: String; seed: String): String;
var i, x, tam : Integer;
cripted : String;
begin
tam := Length(plaintext);
cripted := '';
x := 1;
for i := 1 to tam do
begin
if x > Length(seed) Then x := 1;
cripted := cripted + IntToHex(Ord(seed[x]) + Ord(plaintext[i]), 2 );
inc(x);
end;
GeraAut := cripted;
end;
E por ltimo vamos codificar a funo GeraSerial:
function GeraSerial(nome, id: string):String;
var
soma, i, resto, tam, j, k : integer;
tmp, hard, aux : string;
begin
soma := 0; tmp := '';
hard := '{76B114CF-814A-4BF9-817A-D9DC70F49B6E}'; // GUID decriptografada
id := Copy(id, 1, 4) + Copy(id, 6, 4) + Copy(id, 11, 4) + Copy(id, 16, 4); // Sem os -
tam := Length(nome);
for i := 1 to tam do
nome[i] := UpCase(nome[i]);
for i := 1 to tam do
Soma := Soma + (Ord(Nome[i]) * i);
while length(nome) < $10 do
nome := nome+nome;
tam := Length(id);
for i := 1 to tam do
Soma := Soma + (Ord(ID[i]) * i);
j := 0;
for i := 1 to tam do
begin
soma := soma + i;
resto := soma mod $C8;
soma := soma - i;
j := j + 2;
k := (Ord(hard[j]) xor Ord(hard[i])) xor Ord(nome[i]);
k := k and $FF;
k := k xor resto;
aux := IntToHex(k, 2);
tmp := tmp + aux; // Concatena...
soma := soma + (Ord(nome[i]) xor i);
soma := soma + (Ord(id[i]) xor i);
end;
GeraSerial := Copy(tmp, 1, 8) +'-'+ Copy(tmp, 9, 8) +'-'+ Copy(tmp, 17, 8); // Copia as partes
end;
Bom isso ai... Agora vamos testar pra ver se essa bagaa funciona mesmo...
Conferindo com os valores do programa...
AGRADECIMENTOS
NightWisher
BrunoMaestro
Nigga
Piteco
Valterri
Snacker
Tazmanian
Hawk
Fuss
FudoWarez
Trincado (sumido)
Carl-N
Ricardo Narvaja
Senna Spy
Todos do BRCRACKERS (old and new school)
CracksLatinos
E todos que eu esqueci e que deveriam estar aqui.
Finalizo este tutorial com as palavras do grande mestre e sbio ZimZum:
Some Success? Unpacked, Fixed?
By rumble / brcrackers
1997 - 2007