
Reverse Engineering: Dekompilieren von Binärcode mit großen Sprachmodellen
📊 Ergebnisse | 🤗 Modelle | 🚀 Schnellstart | 📚 HumanEval-Decompile | 📎 Zitation | 📝 Paper | 🖥️ Colab | ▶️ YouTube
Reverse Engineering: Dekompilieren von Binärcode mit großen Sprachmodellen
Während der Kompilierung verarbeitet der Präprozessor den Quellcode (SRC), um Kommentare zu entfernen und Makros oder Includes zu erweitern. Der bereinigte Code wird dann an den Compiler weitergeleitet, der ihn in Assembler-Code (ASM) umwandelt. Dieser ASM wird vom Assembler in Binärcode (Nullen und Einsen) transformiert. Der Linker schließt den Prozess ab, indem er Funktionsaufrufe zu einer ausführbaren Datei verknüpft. Die Dekompilierung hingegen besteht darin, Binärcode zurück in eine Quelldatei umzuwandeln. LLMs, die auf Text trainiert werden, sind nicht in der Lage, Binärdaten direkt zu verarbeiten. Daher müssen Binärdateien zuerst von Objdump in Assemblersprache (ASM) disassembliert werden. Es ist zu beachten, dass Binär- und disassemblierter ASM äquivalent sind und ineinander umgewandelt werden können; wir verwenden die Begriffe daher austauschbar. Schließlich wird der Verlust zwischen dem dekompilierten Code und dem Quellcode berechnet, um das Training zu steuern. Um die Qualität des dekompilierten Codes (SRC') zu bewerten, wird er anhand von Test-Assertions auf seine Funktionalität getestet (Re-Ausführbarkeit).
Unsere LLM4Decompile umfasst Modelle mit Größen zwischen 1,3 und 33 Milliarden Parametern, und wir haben diese Modelle auf Hugging Face verfügbar gemacht.
| Modell | Checkpoint | Größe | Re-Ausführbarkeit | Hinweis |
|---|---|---|---|---|
| llm4decompile-1.3b-v1.5 | 🤗 HF-Link | 1.3B | 27.3% | Hinweis 3 |
| llm4decompile-6.7b-v1.5 | 🤗 HF-Link | 6.7B | 45.4% | Hinweis 3 |
| llm4decompile-1.3b-v2 | 🤗 HF-Link | 1.3B | 46.0% | Hinweis 4 |
| llm4decompile-6.7b-v2 | 🤗 HF-Link | 6.7B | 52.7% | Hinweis 4 |
| llm4decompile-9b-v2 | 🤗 HF-Link | 9B | 64.9% | Hinweis 4 |
| llm4decompile-22b-v2 | 🤗 HF-Link | 22B | 63.6% | Hinweis 4 |
Hinweis 3: Die V1.5-Serie wird mit einem größeren Datensatz (15B Tokens) und einer maximalen Token-Größe von 4,096 trainiert, mit bemerkenswerter Leistung (über 100 % Verbesserung) im Vergleich zum vorherigen Modell.
Hinweis 4: Die V2-Serie basiert auf Ghidra und wird mit 2 Milliarden Tokens trainiert, um den von Ghidra dekompilierten Pseudo-Code zu verfeinern. Details finden Sie im ghidra-Ordner.
Einrichtung: Bitte verwenden Sie das folgende Skript, um die erforderliche Umgebung zu installieren.
git clone https://github.com/albertan017/LLM4Decompile.git
cd LLM4Decompile
conda create -n 'llm4decompile' python=3.9 -y
conda activate llm4decompile
pip install -r requirements.txt
Hier ist ein Beispiel für die Verwendung unseres Modells (überarbeitet für V1.5. Für frühere Modelle lesen Sie bitte die entsprechende Modellseite auf HF). Hinweis: Ersetzen Sie "func0" durch den Funktionsnamen, den Sie dekompilieren möchten.
Vorverarbeitung: Kompilieren Sie den C-Code in eine Binärdatei und disassemblieren Sie die Binärdatei in Assembler-Befehle.
import subprocess
import os
func_name = 'func0'
OPT = ["O0", "O1", "O2", "O3"]
fileName = 'samples/sample' #'path/to/file'
for opt_state in OPT:
output_file = fileName +'_' + opt_state
input_file = fileName+'.c'
compile_command = f'gcc -o {output_file}.o {input_file} -{opt_state} -lm'#compile the code with GCC on Linux
subprocess.run(compile_command, shell=True, check=True)
compile_command = f'objdump -d {output_file}.o > {output_file}.s'#disassemble the binary file into assembly instructions
subprocess.run(compile_command, shell=True, check=True)
input_asm = ''
with open(output_file+'.s') as f:#asm file
asm= f.read()
if '<'+func_name+'>:' not in asm: #IMPORTANT replace func0 with the function name
raise ValueError("compile fails")
asm = '<'+func_name+'>:' + asm.split('<'+func_name+'>:')[-1].split('\n\n')[0] #IMPORTANT replace func0 with the function name
asm_clean = ""
asm_sp = asm.split("\n")
for tmp in asm_sp:
if len(tmp.split("\t"))<3 and '00' in tmp:
continue
idx = min(
len(tmp.split("\t")) - 1, 2
)
tmp_asm = "\t".join(tmp.split("\t")[idx:]) # remove the binary code
tmp_asm = tmp_asm.split("#")[0].strip() # remove the comments
asm_clean += tmp_asm + "\n"
input_asm = asm_clean.strip()
before = f"# This is the assembly code:\n"#prompt
after = "\n# What is the source code?\n"#prompt
input_asm_prompt = before+input_asm.strip()+after
with open(fileName +'_' + opt_state +'.asm','w',encoding='utf-8') as f:
f.write(input_asm_prompt)
Assembler-Befehle sollten das folgende Format haben:
<FUNCTION_NAME>:\nOPERATIONS\nOPERATIONS\n
Typische Assembler-Befehle können wie folgt aussehen:
<func0>:
endbr64
lea (%rdi,%rsi,1),%eax
retq
Dekompilierung: Verwenden Sie LLM4Decompile, um die Assembler-Befehle in C zu übersetzen:
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
model_path = 'LLM4Binary/llm4decompile-6.7b-v1.5' # V1.5 Model
tokenizer = AutoTokenizer.from_pretrained(model_path)
model = AutoModelForCausalLM.from_pretrained(model_path,torch_dtype=torch.bfloat16).cuda()
with open(fileName +'_' + OPT[0] +'.asm','r') as f:#optimization level O0
asm_func = f.read()
inputs = tokenizer(asm_func, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(**inputs, max_new_tokens=2048)### max length to 4096, max new tokens should be below the range
c_func_decompile = tokenizer.decode(outputs[0][len(inputs[0]):-1])
with open(fileName +'.c','r') as f:#original file
func = f.read()
print(f'original function:\n{func}')# Note we only decompile one function, where the original file may contain multiple functions
print(f'decompiled function:\n{c_func_decompile}')
# build docker
docker build -t llm4decompile .
# run docker with GPU
docker run --gpus all -it --name llm4decompile llm4decompile /bin/bash
# run demo.py (choose a model suitable for your resources before running)
cd ghidra
python demo.py
Die Daten werden in llm4decompile/decompile-eval/decompile-eval-executable-gcc-obj.json im JSON-Listenformat gespeichert. Es gibt 164*4 (O0, O1, O2, O3) Beispiele, jeweils mit fünf Schlüsseln:
task_id: gibt die ID des Problems an.type: die Optimierungsstufe, eine von [O0, O1, O2, O3].c_func: C-Lösung für das HumanEval-Problem.c_test: C-Test-Assertions.input_asm_prompt: Assembler-Befehle mit Prompts, die wie in unserem Vorverarbeitungsbeispiel abgeleitet werden können.Bitte überprüfen Sie die Evaluierungsskripte.
Dieses Code-Repository ist unter der MIT- und der DeepSeek-Lizenz lizenziert.
@misc{tan2024llm4decompile,
title={LLM4Decompile: Decompiling Binary Code with Large Language Models},
author={Hanzhuo Tan and Qi Luo and Jing Li and Yuqun Zhang},
year={2024},
eprint={2403.05286},
archivePrefix={arXiv},
primaryClass={cs.PL}
}