Análisis del sistema de verificación de licencias de WinRAR 7.12

Descripción general del análisis

Este documento presenta un análisis técnico del sistema de verificación de licencias en WinRAR versión 7.12 (build 712sc). Se examinan los mecanismos de validación del archivo de注册密钥 (rarreg.key) y la implementación de criptografía de curva elíptica (ECC) utilizada por el software.

Localización del proceso de verificación

WinRAR permite el registro mediante el archivo rarreg.key, que debe copiarse a la carpeta de instalación o al directorio %APPDATA%\WinRAR. También es posible arrastrar el archivo directamente a la ventana de WinRAR.

Punto de entrada principal

La búsqueda de la cadena "rarreg.key" permite localizar la función principal de procesamiento: buscar_nueva_clave_14019A158. El flujo de registro mediante arrastre se gestiona en la función sub_14019C2F4, que utiliza la interfaz IDropTarget para detectar cuando se suelta un archivo sobre la ventana.

La implementación básica del gestor de arrastre es:

gestor_arrastre* crear_gestor_arrastre(HWND ventana_padre) {
    gestor_arrastre* gestor = new gestor_arrastre();
    if (gestor) {
        gestor->vtabla = &vtabla_arrastre_14020BA40;
    }
    return registrar_arrastre(ventana_padre, gestor);
}

Función de carga y verificación

Una vez cargado el archivo, se invoca cargar_y_verificar_1400C62CC que realiza la lectura del archivo y llama a la validación principal:

bool cargar_y_verificar_1400C62CC(ruta_archivo* ruta, char indicador) {
    archivo_licencia licencia;
    bool resultado = false;
    
    if (abrir_archivo(&licencia, &ruta)) {
        datos = (char*)asignar_memoria(0x2000);
        if (datos) {
            tamano = leer_datos(&licencia, datos, 0x2000);
            if (tamano > 0 && verificar_datos_1400C6390(datos, tamano, indicador)) {
                resultado = true;
            }
            liberar(datos);
        }
    }
    cerrar_archivo(&licencia);
    return resultado;
}

Verificación de datos del archivo de licencia

La función verificar_datos_1400C6390 constituye el núcleo del sistema de validación. Cuando el tercer parámetro es diferente de cero, se realiza una verificación completa; de lo contrario, solo se valida el CRC32.

Proceso de verificación

El sistema realiza las siguientes validaciones:

  1. Verifica el CRC32 de la clave pública ECC incluidahardcoded: 70c2441db366d92ea7be1342b3bf629026ba92bb675f06e684bdd34511097434
  2. Procesa las líneas del archivo de licencia extrayendo:
    • Nombre de usuario
    • Tipo de licencia
    • UID
    • Datos de registro (RegisterData[0-3])
  3. Parsed los primeros bytes de lic_data para obtener las longitudes de los 4 segemntos RegisterData
  4. Calcula el checksum y verifica la integridad

Extracción de datos de registro

El formato del archivo de licecnia utiliza un formato estructurado donde los primeros caracteres indican la longitud de cada segmento:

// Formato: LLDDDD... (L = longitud en ASCII, D = datos)
// Ejemplo: "64" seguido de 64 caracteres = RegisterData[0]
//          "122" seguido de 122 caracteres = RegisterData[1]
//          etc.

extraer_longitudes(&lic_data, &len0, &len1, &len2, &len3);

// Extraer cada segmento
RegisterData[0] = lic_data[10 : 10 + len0]
RegisterData[1] = lic_data[10 + len0 : 10 + len0 + len1]
RegisterData[2] = lic_data[10 + len0 + len1 : ...]
RegisterData[3] = lic_data[... : lic_data.tamaño - checksum]
checksum = lic_data[ resto ]

Flujo de validación de RegisterData

El sistema de validación ECC realiza cuatro verificaciones independientes:

Primera verificación

Se utiliza RegisterData[1] como firma, lic_type como mensaje, y la clave pública hardcoded de WinRAR para verificar la firma del tipo de licencia:

clave_publica = obtener_clave_publica_interna();
resultado1 = verificar_firmaECC(&tipo_licencia, &clave_publica, &RegisterData[1]);

Segunda verificación

RegisterData[2] contiene la firma; el mensaje es la concatenación de username + RegisterData[0]:

mensaje2 = concatenar(nombre_usuario, RegisterData[0]);
resultado2 = verificar_firmaECC(&mensaje2, &clave_publica_interna, &RegisterData[2]);

Tercera verificación

A partir de RegisterData[3] se deriva una clave privada, cuya clave pública对应 RegisterData[0]. Luego se genera una firma del mensaje:

clave_privada = derivar_clave_privada(RegisterData[3]);
clave_publica_generada = calcular_clave_publica(clave_privada);

// Verificar que coincide con RegisterData[0]
firmar_mensaje(mensaje2, clave_privada, &firma_temporal);

Cuarta verificación

Se verifica que la firma temporal es correcta usando RegisterData[0] como clave pública:

resultado_final = verificar_firmaECC(&mensaje2, &RegisterData[0], &firma_temporal);

Generación de clave y firma ECC

La función generar_clave_firma_1400CECC4 implementa la derivación de claves y generación de firmas:

void generar_clave_firma_1400CECC4(mensaje* msg, datos* entrada, firma* salida) {
    generador generador;
    clave_privada clave_priv;
    
    // Inicializar generador a partir de los datos de entrada
    iniciales_sha1_1400D0888(&generador, entrada);
    
    // Derivar la clave privada mediante 15 iteraciones SHA1
    obtener_clave_privada_sha1_1400D0908(&generador, &clave_priv);
    
    // Calcular hash del mensaje
    sha1_mensaje_1400D0758(&generador, msg, msg->tamano, 2);
    convertir_hex_bigint(generador.hash_mensaje, &hash);
    
    // Firmar hasta obtener una firma válida
    do {
        obtener_valor_aleatorio_sha1_1400D0908(&generador, &aleatorio);
        generar_firma_ECC_1400CF368(&clave_priv, &aleatorio, &hash, &firma);
    } while (!firma.r.tamano_valido);
    
    // Formatear firma de salida
    formatear_firma(&firma, salida);
}

Estructuras de datos principales

struct HashNumero {
    int tamano_total;
    int indice;
    int generador_sha1[5];
    int hash_mensaje[11];
};

struct NumeroGrande {
    unsigned int tamano;
    unsigned int valor[17];
};

struct ContextoSHA1 {
    unsigned int estado[5];
    unsigned long long conteo;
    unsigned char buffer[64];
};

Obtención del generador

La función obtener_generador_sha1_1400D0888 calcula el valor inicial del generador usando SHA1:

void obtener_generador_sha1_1400D0888(HashNumero* destino, datos* entrada) {
    ContextoSHA1 ctx;
    
    inicializar_sha1(&ctx);
    actualizar_sha1(&ctx, entrada->datos, entrada->tamano);
    finalizar_sha1(&ctx, destino->generador_sha1);
    destino->tamano_total = 6;
}

Derivate de clave privada

La función obtener_clave_privada_1400D0908 genera la clave privada mediante iteraciones SHA1:

// Pseudocódigo del proceso:
// para i = 1 hasta 15:
//     sha1 = hashlib.sha1()
//     sha1.update(i.to_bytes(4, "little") + generador)
//     clave_privada += sha1.digest()[3:1:-1]  // Primeros 2 bytes

int obtener_clave_privada_sha1_1400D0908(HashNumero* generador, NumeroGrande* clave_priv) {
    clave_priv->tamano = 15;
    for (int i = 1; i < 16; i++) {
        // Calcular valor SHA1 y tomar solo primeros 2 bytes
        *(&clave_priv->tamano + i) = calcular_sha1_parcial(generador);
    }
    return 0;
}

Algoritmo de hash de mensaje

Elhash del mensaje se calcula mediante una variantede SHA1 con un proceso iterativo:

unsigned long long sha1_mensaje_1400D0758(HashNumero* destino, mensaje* msg, 
                                          long tamano_msg, long iteraciones) {
    unsigned char contador = 0;
    ContextoSHA1 contextos[2];
    
    // Iniciar tantos contextos como iteraciones especificadas
    for (int i = 0; i < iteraciones; i++) {
        inicializar_sha1(&contextos[i]);
    }
    
    // Actualizar todos los contextos con el mensaje
    for (int i = 0; i < iteraciones; i++) {
        actualizar_sha1(&contextos[i], msg, tamano_msg);
    }
    
    // Recolectar resultados
    for (int i = 0; i < iteraciones; i++) {
        finalizar_sha1(&contextos[i], &destino->hash_mensaje[4*i + i]);
        inicializar_sha1(&contextos[i]); // Reiniciar para hash vacío
    }
    
    // Calcular tamaño total
    destino->tamano_total = iteraciones + 4 * iteraciones + 6;
    return destino->tamano_total;
}

Parámetros de la curva elíptica

WinRAR utiliza una curva elíptica sobre GF(2^m) con los siguientes parámetros:

  • Ecuación: y² + xy = x³ + ax² + b (donde a=0, b=0xa1)
  • Campo: GF(2¹³⁷)
  • Orden de la curva: 0x1026DD85081B82314691CED9BBEC30547840E4BF72D8B5E0D258442BBCD31

Punto base G

// Coordenada X
Gx = {
    0x11, 0x56FD, 0x289E, 0x59DC, 0x0CC2, 0x4CB7, 0x0025, 
    0x55CE, 0x4FEB, 0x0D67, 0x4882, 0x4468, 0x1B89, 0x45AA, 0x2510, 0x52F, 0x38CC
};

// Coordenada Y
Gy = {
    0x20DA, 0x1974, 0x2BF2, 0x216F, 0x4F0E, 0x3B5E, 0x7911, 
    0x12D6, 0x4D07, 0x539B, 0x54C1, 0x7388, 0x3412, 0x18C4, 0x65F2, 0x31A7
};

逆向工程 del proceso de generación

El proceso inverso presenta varios desafíos técnicos:

Desafío 1: Clave privada ECC de WinRAR

La clave privada de WinRAR se calcula a partir de un generador determinado. El generador inicial se obtiene mediante SHA1 y luego se procesa a través de 15 iteraciones para obtener la clave privada:

clave_privada_winrar = 0x59fe6abcca90bdb95f0105271fa85fb9f11f467450c1ae9044b7fd61d65e

Desafío 2: RegisterData[3]

RegisterData[3] se genera a partir del nombre de usuario. Primero se calcula la clave pública del usuario, luego se concatena '60' con los primeros 48 caracteres hexadecimales de dicha clave:

clave_publica_usuario = derivar_clave_publica(nombre_usuario)
RegisterData[3] = "60" + clave_publica_usuario.hex()[:48]

Desafío 3: UID

El campo UID no es validado activamente durante el proceso de verificación. Se calcula como:

UID = clave_publica_usuario.hex()[48:] + RegisterData[0][:4]
// 8 bytes + 2 bytes = 10 bytes

Notas finales

Este análisis revela que WinRAR implementa un sistema robusto de verificación de licencias basado en criptografía de curva elíptica. El sistema utiliza una clave pública hardcoded para validar parcialmente las credenciales, mientras que la generación de claves específicas de usuario permite la verificación de la autenticidad del archivo de licencia.

Los archivos de licencia legítimos contienen estructuras de datos específicas que incluyen firmas ECC válidas, derivadas de las claves privadas correctas.

Etiquetas: Reverse-Engineering winrar criptografía curva-elíptica security-analysis

Publicado el 7-29 18:13