Solução de crackme da Kaspersky Lab

Em um belo dia, diferentes canais do telegrama começaram a fornecer um link para o crackmix da LK. Aqueles que concluíram a tarefa com sucesso serão convidados para uma entrevista! . Depois de uma declaração tão alta, imaginei o quão difícil seria o contrário. Como eu resolvi essa tarefa pode ser lido abaixo do corte (muitas fotos).

Chegando em casa, reli cuidadosamente a tarefa, baixei o arquivo e comecei a ver o que havia dentro. E dentro estava:



Começamos x64dbg, despejo após descompactar, olha o que realmente está dentro:





Pegamos o nome do arquivo nos argumentos da linha de comando -> abrir, ler -> criptografar o primeiro passo -> criptografar o segundo passo -> gravar em um novo arquivo.

É simples, é hora de analisar a criptografia.

Vamos começar com o stage1


No endereço 0x4033f4, existe uma função que chamei de crypt_64bit_up (mais tarde você entenderá o porquê), que é chamada de um loop em algum lugar dentro do stage1



E um resultado de descompilação um pouco torto



No começo, tentei reescrever o mesmo algoritmo em python, o matei por várias horas e resultou em algo assim (o que get_dword e byteswap devem estar claros nos nomes)

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)))) # print('Got buf val: 0x%X' % res) return res def crypt_64bit_up(initials, buffer): steps = [] steps.append(get_dword(buffer, 0) ^ byteswap(initials[0])) # = z steps.append(get_buf_val(steps[-1], buffer) ^ byteswap(initials[1]) ^ get_dword(buffer, 1)) for i in range(2, 17): steps.append(get_buf_val(steps[-1], buffer) ^ get_dword(buffer, i) ^ steps[i-2]) res_0 = steps[15] ^ get_dword(buffer, 17) res_1 = steps[16] print('Res[0]=0x%X, res[1]=0x%X' % (res_0, res_1)) 

Mas então eu decidi prestar atenção nas constantes 0x12, 0x112, 0x212, 0x312 (sem hexadecimal 18, 274, 536 ... não muito parecido com algo incomum). Tentamos pesquisá-los no Google e encontrar um repositório inteiro (dica: NTR) com a implementação das funções de criptografia e descriptografia , isso é boa sorte. Tentamos criptografar um arquivo de teste com conteúdo aleatório no programa original, despejá-lo e criptografar o mesmo arquivo com um pequeno script, tudo deve funcionar e os resultados devem ser os mesmos. Depois disso, tentamos descriptografá-lo (decidi não entrar em detalhes e apenas copiar e colar a função de descriptografia da fonte)

 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) # x - step[i], y - step[i-1] res_1 = y ^ get_dword(keybuf, 0x0) return (res_1, res_0) def stage1_unpack(packed_data, state): res = bytearray() for i in range(0, len(packed_data), 8): ciphered = struct.unpack('>II', packed_data[i:i+8]) res += struct.pack('>II', *crypt_64bit_down(ciphered, state)) return res 

Nota importante: a chave no repositório é diferente da chave no programa (o que é bastante lógico). Portanto, depois que a chave é inicializada, eu a despejo em um arquivo, isso é buffer / keybuf

Passamos para a segunda parte


Tudo é muito mais simples aqui: primeiro, uma matriz de caracteres únicos é criada com tamanho 0x55 bytes no intervalo (33, 118) (caracteres imprimíveis) e, em seguida, o valor de 32 bits é compactado em 5 caracteres imprimíveis da matriz criada anteriormente.





Como não há aleatoriedade ao criar a matriz mencionada acima, toda vez que o programa for iniciado, essa matriz será a mesma, despejamos após a inicialização e podemos descompactar stage_2 com uma função simples

 def stage2_unpack(packed_data, state): # checked! res = bytearray() for j in range(0, len(packed_data), 5): mapped = [state.index(packed_data[j+i]) for i in range(5)] res += struct.pack('>I', sum([mapped[4-i]*0x55**i for i in range(5)])) return res 

Fazemos algo assim:

 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() 

E nós obtemos o resultado

Source: https://habr.com/ru/post/pt431690/


All Articles