Un buen día, diferentes canales en el
telegrama comenzaron a lanzar un
enlace al crackmix de LK. ¡
Aquellos que completaron con éxito la tarea serán invitados a una entrevista! . Después de una declaración tan fuerte, me pregunté qué tan difícil sería lo contrario. La forma en que resolví esta tarea se puede leer debajo del corte (muchas fotos).
Al llegar a casa, volví a leer cuidadosamente la tarea, descargué el archivo y comencé a mirar lo que había dentro. Y dentro estaba:

Comenzamos x64dbg, volcamos después de desempacar, mira lo que realmente hay dentro:


Tomamos el nombre del archivo de los argumentos de la línea de comando -> abrir, leer -> cifrar el primer paso -> cifrar el segundo paso -> escribir en un nuevo archivo.
Es simple, es hora de ver el cifrado.
Comencemos con la etapa 1
En la dirección 0x4033f4 hay una función que llamé crypt_64bit_up (más tarde entenderá por qué), se llama desde un bucle en algún lugar dentro de stage1

Y un resultado de descompilación un poco torcido

Al principio traté de reescribir el mismo algoritmo en Python, lo eliminé durante varias horas y resultó algo así (lo que hace que get_dword y byteswap deberían estar claros de los nombres)
def _add(x1, x2): return (x1+x2) & 0xFFFFFFFF def get_buf_val(t, buffer): t_0 = t & 0xFF t_1 = (t >> 8) & 0xFF t_2 = (t >> 16) & 0xFF t_3 = (t >> 24) & 0xFF res = _add(get_dword(buffer, t_0 + 0x312), (get_dword(buffer, t_1 + 0x212) ^ _add(get_dword(buffer, t_2+0x112), get_dword(buffer, t_3+0x12))))
Pero luego decidí prestar atención a las constantes 0x12, 0x112, 0x212, 0x312 (sin el hex 18, 274, 536 ... no muy similar a algo inusual). Intentamos buscarlos en Google y encontrar un repositorio completo (pista: NTR) con la implementación de funciones de cifrado y
descifrado , esta es buena suerte. Intentamos cifrar un archivo de prueba con contenido aleatorio en el programa original, volcarlo y cifrar el mismo archivo con un pequeño script, todo debería funcionar y los resultados deberían ser los mismos. Después de eso, tratamos de descifrarlo (decidí no entrar en detalles y simplemente copiar y pegar la función de descifrado de la fuente)
def crypt_64bit_down(initials, keybuf): x = initials[0] y = initials[1] for i in range(0x11, 1, -1): z = get_dword(keybuf, i) ^ x x = get_buf_val(z, keybuf) x = y ^ x y = z res_0 = x ^ get_dword(keybuf, 0x01)
Nota importante: la clave en el repositorio es diferente de la clave en el programa (lo cual es bastante lógico). Por lo tanto, después de que se inicializa la clave, simplemente la volqué en un archivo, esto es buffer / keybuf
Pasamos a la segunda parte
Aquí todo es mucho más simple: primero, se crea una matriz de caracteres únicos con un tamaño de 0x55 bytes en el rango (33, 118) (caracteres imprimibles), luego el valor de 32 bits se empaqueta en 5 caracteres imprimibles de la matriz creada anteriormente.


Como no hay aleatoriedad al crear la matriz mencionada anteriormente, cada vez que se inicia el programa, esta matriz será la misma, la volcamos después de la inicialización y podemos desempaquetar stage_2 con una función simple
def stage2_unpack(packed_data, state):
Hacemos algo como esto:
f = open('stage1.state.bin', 'rb') stage1 = f.read() f.close() f = open('stage2.state.bin', 'rb') stage2 = f.read() f.close() f = open('rprotected.dat', 'rb') packed = f.read() f.close() unpacked_from_2 = stage2_unpack(packed, stage2) f = open('unpacked_from_2', 'wb') f.write(unpacked_from_2) f.close() unpacked_from_1 = stage1_unpack(unpacked_from_2, stage1) f = open('unpacked_from_1', 'wb') f.write(unpacked_from_1) f.close()
Y obtenemos el resultado
