Extração determinística com PyGhidra, triagem e pipeline local de backend de compilador com LLM para reimplementação em C.
Obrigado por baixar meu pipeline Ghidra + LLM.
Este é meu primeiro README. ASL (Língua de Sinais Americana) é minha primeira língua, então meu inglês não é perfeito. Fiz o meu melhor.
Eu adoro uma antiga simulação de PC que jogo desde a infância, e quero melhorá-la por dentro.
Observar jogos de código aberto como OpenRA e OpenTTD me ensinou isto: uma reimplementação feita por um entusiasta não se trata de obter o código-fonte original. Trata-se de construir uma nova versão de código aberto do jogo que consiga carregar os assets do original. Assim, qualquer pessoa pode expandi-lo ou contribuir.
Para fazer o trabalho, uso dois tipos de IA juntos:
Cada função recebe um rótulo. Isso economiza muito tempo, porque não desperdiço esforço com o grande monte de lixo do SO e do runtime.
|-----------|------------------------------------------------------------------------------------------------------------------
| Rótulo | Significado |
|---|---|
| KEEP | A lógica do próprio jogo (por exemplo, o modelo de voo, o modelo de dano, TrackIR) |
| WRAP | Cola voltada para Java que chama APIs do SO ou de entrada: mantenha a assinatura, escreva um novo corpo. |
| (IL-2 1946 é um motor interessante: C++ e Java juntos.) | |
| REPLACE | Runtime C antigo, alocador, thunks: o compilador ou o SO os fornece, então uma nova versão de 64 bits não precisa deles |
| UNKNOWN | Não está claro: examine manualmente |
| ----------- | ------------------------------------------------------------------------------------------------------------------ |
No meu programa de teste, cerca de 44% das funções eram REPLACE, então nunca precisaram ser convertidas.
No começo eu confiava que o pseudocódigo C tinha todas as respostas. Isso foi ingênuo. Ele é a interpretação do Ghidra sobre o assembly, e ele faz suposições (tipos de variáveis, argumentos perdidos).
O assembly é a verdade bruta. Ele não tem tipos de variáveis, mas contém o comportamento exato de cada função. Então os scripts leem o assembly e o transformam em um blueprint em forma de C. O C do Ghidra é mantido apenas como dica.
Meus primeiros testes usaram os LLMs locais (Qwen 2.5 Coder, DeepSeek Coder V2 Lite, Mellum 2) para ler o assembly diretamente. Nunca funcionou de forma confiável. Eles alucinavam, porque são feitos para escrever código, não para ler código de máquina bruto.
Depois de muitos experimentos fracassados, dividi o trabalho:
NOTA: Como o Python verifica a resposta
Verificações de texto: o nome da função, cada chamada, global e string do blueprint, os laços, os tamanhos de passo, as constantes comparadas, o valor de retorno, e que nenhum nome de registrador ou nome inventado tenha sobrado.
verificação de compilação com gcc: o código é compilado com gcc (apenas sintaxe, nada é executado). Isso ajudou muito. Numa execução inicial, as verificações de texto aprovaram as respostas do Mellum 2, mas nenhuma delas compilou, por causa de um comentário de cabeçalho escrito fora de um comentário. O gcc apanha o que as verificações de texto não conseguem ver: variáveis não declaradas ou redeclaradas, tipos errados e chavetas ou pontos e vírgulas quebrados. Uma função conta como PASS apenas quando as verificações de texto e a compilação estão ambas OK. O gcc não prova que a lógica está correta, por isso continuo a ler o código em confronto com o blueprint.
Se uma verificação falhar, o LLM recebe a lista do que falhou e tenta mais uma vez.
O Python corrige pequenos deslizes sozinho, sem o LLM: declara cada variável uma vez, envolve linhas de cabeçalho soltas num comentário, faz com que o último return devolva o resultado correto e adiciona os includes e as declarações.
A checklist surgiu depois de eu ver os LLMs falharem em funções muito longas. Funciona como a checklist que um piloto usa antes da descolagem e da aterragem. O Python escreve-a a partir da função e verifica a resposta contra ela. Nos meus testes, as funções longas começaram a passar, e as execuções ficaram mais rápidas.
|-------------------|---------------------------------------|---------------------------------------------
| Pasta | O que faz | Ler |
|---|---|---|
ghidra_pipeline | Dump, agrupamento, triagem, papéis | ghidra_pipeline/INSTRUCTION.txt |
llm_pipeline | Blueprints e o runner do LLM (v3) | llm_pipeline/INSTRUCTION_BLUEPRINT_LLM.txt |
| ------------------- | --------------------------------------- | --------------------------------------------- |
Comece por ghidra_pipeline, depois passe para llm_pipeline quando estiver pronto para usar os seus LLMs locais.
Primeiro, analise o seu programa no PyGhidra e execute PyGhidra Scripts/decompile_full_dump.py.
Os dois ficheiros INSTRUCTION explicam a configuração das pastas. Depois:
Primeiro, analise o seu programa no PyGhidra e execute PyGhidra Scripts/decompile_full_dump.py.
Os dois ficheiros INSTRUCTION explicam a configuração das pastas. Depois:
python .\make_full_lite_v2.py --in . --out .\OUT
python .\triage_evidence_v2.py --lite .\OUT\LITE_AC
python .\build_roles_v2.py --triage .\OUT\TRIAGE
python .\blueprint_v3.py --group ".\SOURCE\LITE_AC\<GroupName>" --out ".\G_<GroupName>\BLUEPRINT_v3"
python .\run_blueprint_llm_v3.py --bp ".\G_<GroupName>\BLUEPRINT_v3" --out ".\G_<GroupName>\RUN1_v3_<model>" --model <model name>
Você precisa do Ghidra com PyGhidra, Python 3, LM Studio (um modelo carregado) e gcc (recomendado, usado como verificação de compilação).