Engenharia Reversa de Soul Knight: Análise de Memória e Descriptografia de Salvas

Este documento técnico aborda o processo de engenharia reversa aplicado ao jogo Soul Knight, com foco em contornar mecanismos de proteção para realizar análise de memória e descriptografia de arquivos de salvamento. O objetivo é descrever as etapas para extrair e analisar dados do jogo, incluindo a utilização de ferramentas especializadas e técnicas de criptografia.

Reparo e Análise do Arquivo Binário

Para iniciar, é necessário reparar o arquivo SO do jogo utilizando a ferramenta SoFixer. Em seguida, o arquivo reparado é carregado no IDA Pro, onde scripts como il2cppdumper são empregados para mapear símbolos e estruturas de dados. Devido ao tamanho do binário do jogo, a análise pode ser intensiva em tempo e recursos.

Identificação do Arquivo de Salvamento

O arquivo principal de salvamento, denominado game.data, é localizado dentro dos recursos do jogo. No IDA Pro, a navegação para endereços específicos e a análise de referências cruzadas permitem identificar funções responsáveis pela leitura e escrita desses dados, como a rotina LoadGameData.

Algoritmo de Criptografia

A função de criptografia emprega uma combinação de operações XOR com uma chave secreta e um fator calculado com base no índice do byte. A implementação a seguir ilustra uma versão reescrita do algoritmo em Python, mantendo a lógica original mas com estrutura modificada:


def decifrar_xor(dados_cifrados: bytes, chave: str) -> bytes:
    buffer_saida = bytearray(dados_cifrados)
    tamanho_chave = len(chave)
    for idx in range(len(buffer_saida)):
        byte_chave = ord(chave[idx % tamanho_chave])
        fator_indice = ((idx % 15) * (idx % 5)) % 92
        buffer_saida[idx] = buffer_saida[idx] ^ byte_chave ^ fator_indice
    return bytes(buffer_saida)

Técnica de Força Bruta para Descriptografia

Considerando que o arquivo descriptografado segue o formato JSON, é possível aplicar força bruta para determinar a chave. A abordagem baseia-se na detecção de padrões típicos de JSON, como sequências de caracteres comuns, para avaliar a probabilidade de uma chave correta. O script abaixo realiza essa análise:


import json
from collections import defaultdict

def encontrar_chave_bruta(dados_cifrados: bytes, limite_tamanho: int = 50):
    padroes_json = [b'{"', b'":', b',"', b'"}', b':[', b']}']
    candidatos = {}
    
    for padrao in padroes_json:
        lista_padrao = list(padrao)
        
        for tam_chave in range(1, limite_tamanho + 1):
            votos_por_posicao = [defaultdict(int) for _ in range(tam_chave)]
            
            for pos_inicial in range(len(dados_cifrados) - len(lista_padrao) + 1):
                chave_parcial = {}
                invalido = False
                
                for i, byte_esperado in enumerate(lista_padrao):
                    pos_absoluta = pos_inicial + i
                    fator = ((pos_absoluta % 15) * (pos_absoluta % 5)) % 92
                    byte_decifrado = dados_cifrados[pos_absoluta] ^ fator ^ byte_esperado
                    
                    if not (32 <= byte_decifrado < 127):
                        invalido = True
                        break
                    
                    pos_chave = pos_absoluta % tam_chave
                    
                    if pos_chave in chave_parcial:
                        if chave_parcial[pos_chave] != byte_decifrado:
                            invalido = True
                            break
                    else:
                        chave_parcial[pos_chave] = byte_decifrado
                
                if not invalido:
                    for pos_chave, valor in chave_parcial.items():
                        votos_por_posicao[pos_chave][valor] += 1
            
            chave_candidata = bytearray(tam_chave)
            votacao_minima = float('inf')
            
            for i in range(tam_chave):
                if not votos_por_posicao[i]:
                    chave_candidata = None
                    break
                byte_mais_votado = max(votos_por_posicao[i].items(), key=lambda x: x[1])
                chave_candidata[i] = byte_mais_votado[0]
                votacao_minima = min(votacao_minima, byte_mais_votado[1])
            
            if chave_candidata and votacao_minima >= 2:
                try:
                    chave_texto = chave_candidata.decode('utf-8')
                    if chave_texto not in candidatos or votacao_minima > candidatos[chave_texto][1]:
                        candidatos[chave_texto] = (padrao.decode('utf-8'), votacao_minima)
                except:
                    pass
    
    return sorted(candidatos.items(), key=lambda x: -x[1][1])

# Processo principal de descriptografia
if __name__ == "__main__":
    with open("game.data", "rb") as arquivo:
        dados_cifrados = arquivo.read()
    
    print(f"Tamanho dos dados cifrados: {len(dados_cifrados)} bytes")
    resultados = encontrar_chave_bruta(dados_cifrados, limite_tamanho=50)
    
    print(f"Candidatos de chave encontrados: {len(resultados)}")
    
    for chave, (padrao, votos) in resultados[:20]:
        print(f"Chave: '{chave}' (tamanho={len(chave)}, votos={votos}, padrão='{padrao}')")
        try:
            dados_decifrados = decifrar_xor(dados_cifrados, chave)
            texto_saida = dados_decifrados.decode('utf-8', errors='replace')
            json.loads(texto_saida)
            print("✓✓ Sucesso: JSON válido identificado.")
            with open("resultado_decifrado.txt", "wb") as saida:
                saida.write(dados_decifrados)
            print("Arquivo salvo como resultado_decifrado.txt")
            break
        except json.JSONDecodeError:
            print("✗ Formato JSON inválido.")
        except Exception as erro:
            print(f"✗ Erro durante o processamento: {erro}")

Ao executar o script, a chave correta é determinada e o arquivo de salvamento é descriptografado, permitindo a inspeção de dados internos do jogo, como configurações de mascotes e habilidades.

Tags: SoulKnight IL2Cpp IDAPro Python CriptografiaXOR

Publicado em 6-20 22:45