Extraction déterministe PyGhidra, triage et pipeline de backend de compilateur LLM local pour la réimplémentation en C.
Merci d'avoir téléchargé mon pipeline Ghidra + LLM.
C'est mon premier README. L'ASL (langue des signes américaine) est ma langue maternelle, donc mon anglais n'est pas parfait. J'ai fait de mon mieux.
J'adore une vieille simulation PC à laquelle je joue depuis l'enfance, et je veux l'améliorer sous le capot.
En regardant des jeux open source comme OpenRA et OpenTTD, j'ai appris ceci : une réimplémentation par un amateur ne consiste pas à obtenir le code source original. Il s'agit de construire une nouvelle version open source du jeu capable de charger les ressources de l'original. Ainsi, n'importe qui peut l'étendre ou y contribuer.
Pour faire ce travail, j'utilise deux types d'IA ensemble :
Chaque fonction reçoit une étiquette. Cela fait gagner beaucoup de temps, car je ne gaspille pas d'efforts sur le grand ensemble de déchets du système d'exploitation et du runtime.
|-----------|------------------------------------------------------------------------------------------------------------------
| Étiquette | Signification |
|---|---|
| KEEP | La logique propre au jeu (par exemple le modèle de vol, le modèle de dégâts, TrackIR) |
| WRAP | Colle orientée Java qui appelle des API du système d'exploitation ou d'entrée : garder la signature, écrire un nouveau corps. |
| (IL-2 1946 est un moteur intéressant : C++ et Java ensemble.) | |
| REPLACE | Ancien runtime C, allocateur, thunks : le compilateur ou le système d'exploitation les fournit, donc une nouvelle version 64 bits n'en a pas besoin |
| UNKNOWN | Pas clair : à examiner à la main |
| ----------- | ------------------------------------------------------------------------------------------------------------------ |
Dans mon programme de test, environ 44 % des fonctions étaient REPLACE, donc elles n'ont jamais eu besoin d'être converties.
Au début, je faisais confiance au pseudocode C pour avoir toutes les réponses. C'était naïf. C'est l'interprétation de l'assembleur par Ghidra, et il devine (types de variables, arguments perdus).
L'assembleur est la vérité brute. Il n'a pas de types de variables, mais il contient le comportement exact de chaque fonction. Donc les scripts lisent l'assembleur et le transforment en plan sous forme de C. Le C de Ghidra n'est conservé que comme indice.
Mes premiers tests utilisaient les LLM locaux (Qwen 2.5 Coder, DeepSeek Coder V2 Lite, Mellum 2) pour lire directement l'assembleur. Cela n'a jamais fonctionné de manière fiable. Ils hallucinaient, car ils sont faits pour écrire du code, pas pour lire du code machine brut.
Après de nombreuses expériences ratées, j'ai réparti le travail :
NOTE : Comment Python vérifie la réponse
Contrôles textuels : le nom de la fonction, chaque appel, globale et chaîne du plan, les boucles, les tailles de pas, les constantes comparées, la valeur de retour, et qu'aucun nom de registre ni nom inventé ne subsiste.
vérification de compilation gcc : le code est compilé avec gcc (syntaxe uniquement, rien n'est exécuté). Cela a beaucoup aidé. Lors d'une exécution précoce, les vérifications textuelles validaient les réponses de Mellum 2, mais aucune d'entre elles ne compilait, à cause d'un commentaire d'en-tête écrit en dehors d'un commentaire. gcc détecte ce que les vérifications textuelles ne peuvent pas voir : variables non déclarées ou redéclarées, types incorrects, et accolades ou points-virgules cassés. Une fonction est considérée comme PASS uniquement lorsque les vérifications textuelles et la compilation sont toutes deux OK. gcc ne prouve pas que la logique est correcte, donc je relis toujours le code par rapport au blueprint.
Si une vérification échoue, le LLM reçoit la liste de ce qui a échoué et réessaie une fois.
Python corrige lui-même les petites erreurs, sans le LLM : il déclare chaque variable une fois, enveloppe les lignes d'en-tête isolées dans un commentaire, fait en sorte que le dernier return renvoie le bon résultat, et ajoute les includes et les déclarations.
La checklist est venue après que j'ai vu les LLM échouer sur des fonctions très longues. Elle fonctionne comme la checklist qu'un pilote utilise avant le décollage et l'atterrissage. Python l'écrit à partir de la fonction, et vérifie la réponse par rapport à elle. Dans mes tests, les fonctions longues ont commencé à passer, et les exécutions sont devenues plus rapides.
|-------------------|---------------------------------------|---------------------------------------------
| Dossier | Ce qu'il fait | À lire |
|---|---|---|
ghidra_pipeline | Dump, regroupement, triage, rôles | ghidra_pipeline/INSTRUCTION.txt |
llm_pipeline | Blueprints et le runner LLM (v3) | llm_pipeline/INSTRUCTION_BLUEPRINT_LLM.txt |
| ------------------- | --------------------------------------- | --------------------------------------------- |
Commencez par ghidra_pipeline, puis passez à llm_pipeline lorsque vous êtes prêt à utiliser vos LLM locaux.
D'abord, analysez votre programme dans PyGhidra et exécutez PyGhidra Scripts/decompile_full_dump.py.
Les deux fichiers INSTRUCTION expliquent la configuration des dossiers. Ensuite :
D'abord, analysez votre programme dans PyGhidra et exécutez PyGhidra Scripts/decompile_full_dump.py.
Les deux fichiers INSTRUCTION expliquent la configuration des dossiers. Ensuite :
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>
Vous avez besoin de Ghidra avec PyGhidra, Python 3, LM Studio (un modèle chargé) et gcc (recommandé, utilisé comme vérification de compilation).