El cracking es el proceso de analizar y modificar software para eliminar o evitar ciertas restricciones, como:
| Tipo | Descripción |
|---|---|
| Keygen | Crear generadores de claves válidas |
| Patch | Modificar el ejecutable para saltarse protecciones |
| Loader | Cargar el programa en memoria y modificar su comportamiento |
| Reverse Engineering | Ingeniería inversa para entender el funcionamiento |
El ensamblador es crucial para entender cómo funciona un programa a bajo nivel.
EAX/RAX: Acumulador (resultados de operaciones)
EBX/RBX: Base (puntero a datos)
ECX/RCX: Contador (bucles)
EDX/RDX: Datos (extensión para operaciones)
ESI/RSI: Índice fuente (operaciones con cadenas)
EDI/RDI: Índice destino (operaciones con cadenas)
ESP/RSP: Puntero de pila
EBP/RBP: Puntero de base
EIP/RIP: Puntero de instrucción
MOV dest, src ; Mueve datos de src a dest
PUSH valor ; Empuja valor en la pila
POP dest ; Saca valor de la pila a dest
CALL dirección ; Llama a una función/subrutina
RET ; Retorna de una función
JMP dirección ; Salto incondicional
JE/JZ dirección ; Salto si igual/cero
JNE/JNZ dirección ; Salto si no igual/no cero
CMP op1, op2 ; Compara dos operandos
TEST op1, op2 ; Operación AND bit a bit (sin guardar)
ADD dest, src ; Suma
SUB dest, src ; Resta
NOP ; No operation (90 en x86)
; Supongamos que encontramos esta rutina de validación
00401234 MOV EAX, [ESP+4] ; Carga el serial ingresado
00401238 CALL 00401500 ; Llama a la función de validación
0040123D TEST EAX, EAX ; Comprueba el resultado
0040123F JZ 00401250 ; Salta si es cero (serial incorrecto)
00401241 MOV DWORD [ESP], 00403000 ; "Registro exitoso"
00401248 CALL MessageBoxA
0040124D JMP 00401260
00401250 MOV DWORD [ESP], 00403020 ; "Serial incorrecto"
00401257 CALL MessageBoxA
0040125C JMP 00401260
; Para crackear, podemos:
; 1. Cambiar JZ a JMP (salto incondicional)
; Original: 74 0F (JZ +15)
; Modificado: EB 0F (JMP +15)
; 2. O cambiar TEST EAX,EAX a XOR EAX,EAX (siempre cero)
; Original: 85 C0
; Modificado: 31 C0
Los ejecutables .NET son más fáciles de analizar ya que contienen metadatos y MSIL (Microsoft Intermediate Language).
// Código original en C#
public bool ValidateLicense(string key)
{
if (key == null) return false;
return key == "VALID-KEY-12345";
}
// IL original
.method public hidebysig instance bool ValidateLicense(string key) cil managed
{
ldarg.1
brfalse.s IL_0012
ldarg.1
ldstr "VALID-KEY-12345"
call bool [mscorlib]System.String::op_Equality(string, string)
ret
IL_0012:
ldc.i4.0
ret
}
// Para crackear, podemos modificar el IL para que siempre retorne true:
.method public hidebysig instance bool ValidateLicense(string key) cil managed
{
ldc.i4.1 // Carga 1 (true) en la pila
ret // Retorna
}
Los archivos JAR y WAR son básicamente archivos ZIP que contienen clases Java compiladas.
// Código original decompilado
public class LicenseChecker {
public boolean isValid(String key) {
return "SECRETKEY".equals(key);
}
}
// Para crackear, podemos:
// 1. Modificar el método para que siempre retorne true
public boolean isValid(String key) {
return true;
}
// 2. O cambiar la clave hardcodeada
public boolean isValid(String key) {
return "MYCRACKEDKEY".equals(key);
}
Las APKs son paquetes Android que contienen código DEX (Dalvik Executable).
# Método original en smali
.method public isValidLicense(Ljava/lang/String;)Z
.locals 1
const-string v0, "SECRET_KEY"
invoke-virtual {v0, p1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z
move-result v0
return v0
.end method
# Modificado para siempre retornar true
.method public isValidLicense(Ljava/lang/String;)Z
.locals 1
const/4 v0, 0x1
return v0
.end method
| Protección | Técnica de Bypass |
|---|---|
| Checksum | Parchear la rutina de verificación o modificar los valores esperados |
| Debugger Detection | NOPear las llamadas a IsDebuggerPresent o patchear las APIs |
| VM Detection | Modificar las comprobaciones de entornos virtuales |
| Code Obfuscation | Usar herramientas de deofuscación o análisis dinámico |
| Packing | Dumpear el proceso después de que se descomprima en memoria |
Crear un generador de claves válidas requiere entender el algoritmo de validación:
// Algoritmo simple de validación
bool ValidateKey(char* key) {
if (strlen(key) != 10) return false;
int sum = 0;
for (int i = 0; i < 10; i++) {
sum += key[i] * (i + 1);
}
return (sum % 0x1A) == 0;
}
// Keygen correspondiente
void GenerateKey() {
char key[11] = {0};
int sum = 0;
// Generar 9 caracteres aleatorios
for (int i = 0; i < 9; i++) {
key[i] = 'A' + (rand() % 26);
sum += key[i] * (i + 1);
}
// Calcular el último carácter para que (sum % 26) == 0
int last_char_weight = 10;
int required = (26 - (sum % 26)) % 26;
key[9] = 'A' + (required / last_char_weight);
printf("Key generada: %s\n", key);
}
Conociendo cómo se crackea el software, puedes proteger mejor tus aplicaciones:
// Detección simple de debugger en C
bool IsDebuggerPresentSimple() {
return IsDebuggerPresent();
}
// Técnica más avanzada usando el flag BeingDebugged en PEB
bool IsDebuggerPresentAdvanced() {
#ifdef _WIN64
PPEB pPeb = (PPEB)__readgsqword(0x60);
#else
PPEB pPeb = (PPEB)__readfsdword(0x30);
#endif
return pPeb->BeingDebugged;
}
// Técnica de timing attack
bool IsDebuggerPresentTiming() {
DWORD start = GetTickCount();
// Operación que debería ser rápida
volatile int sum = 0;
for (int i = 0; i < 1000000; i++) {
sum += i;
}
DWORD end = GetTickCount();
// Si el tiempo es demasiado largo, probablemente hay un debugger
return (end - start) > 100;
}
El cracking es un campo complejo que requiere conocimientos de programación, sistemas operativos y arquitectura de computadoras. Este manual cubre los fundamentos, pero la práctica constante es esencial para dominar estas técnicas.