Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Context-Augmented LLM for Binary Function Understanding (RAG)

A lightweight, retrieval-augmented pipeline that recovers meaningful names for functions in stripped binaries — by feeding an off-the-shelf LLM the static-analysis context a reverse engineer would use (who the function calls, who calls it, its disassembly), instead of fine-tuning a model.

Function-name recovery is a core reverse-engineering task: a stripped binary has sub_401250 where the source had read_whole_file. Recovering the intent speeds up every downstream analysis.

Relation to ReCopilot — read this first

The idea of augmenting an LLM with static-analysis context (call graphs, data flow) for binary understanding comes from:

Guoqiang Chen, Huiqi Sun, et al. ReCopilot: Reverse Engineering Copilot in Binary Analysis. QI-ANXIN Technology Research Institute, 2025. arXiv:2505.16366 · code

This repository is not ReCopilot and does not reuse its code, model, or data. ReCopilot builds an expert model by continued-pretraining + fine-tuning Qwen2.5-Coder-7B on 36B tokens. This project is an independent, deliberately lightweight alternative take on the same idea: no training at all — just retrieval of call context + prompting of a general model. The goal is to see how far context alone gets you, as a cheap, reproducible baseline. All code here is my own.

This approach vs. ReCopilot

ReCopilot (Chen et al., 2025) This project
Core idea LLM + static-analysis context for binary understanding same idea
How the model is built continued-pretraining + SFT + DPO on 36B tokens, base Qwen2.5-Coder-7B no training — retrieval of call context + prompting
Compute to reproduce large (multi-GPU, weeks) a laptop; the default backend needs no GPU at all
Tasks 14 (names, types, structs, decompilation, summaries, …) function-name recovery (extensible; see roadmap)
Context used call graph + data flow callees / callers / disassembly (call graph)
Runs offline, no key yes (mock backend)
Best possible quality higher (a purpose-built model) bounded by the general LLM you plug in

The honest positioning: ReCopilot is the stronger system; this is the cheap, fully-reproducible baseline that isolates how much of the job context+prompting alone can do — and its pipeline can serve ReCopilot's own base model (Qwen2.5-Coder via Ollama) to measure exactly that.


How it works

 symbol-bearing binary
        │  objdump / llvm-objdump
        ▼
 per-function context ─── callees (what it calls) ── callers (who calls it) ── disassembly
        │                         ▲
        │   (RETRIEVAL)           └── reuses the call-graph extraction from my
        ▼                             gpu-graph-study project
 hide the real name → build a context-augmented prompt   (AUGMENT)
        ▼
 LLM backend proposes a name   (GENERATE)   ── mock | OpenAI-compatible | Ollama
        ▼
 score vs. the real name   (EVALUATE)   ── exact match · token-F1 · semantic hit

Ground truth is free: a symbol-bearing binary already knows each function's real name. We hide it, ask the pipeline to recover it from context, and score the guess — no manual labeling.


Method comparison

Five methods scored on the same functions (experiments/compare_methods.py). All are cheap alternatives to a fine-tuned model; the LLM column can host any backend, including ReCopilot's base model via Ollama.

Method comparison

Method what it is token-F1 semantic-hit
majority-callee name after the first callee 0.00 0.00
heuristic(calls) signature-call → name map 0.36 0.60
nearest-neighbor (RAG) retrieve most callee-similar labeled function 0.00 0.00
llm(mock) offline heuristic stand-in 0.36 0.60
llm(qwen2.5-coder, Ollama) ReCopilot-family base model — plug in to fill this row

On the demo binaries the call-pattern heuristic already recovers 60% of names semantically, while the retrieval baseline fails — the corpus (one binary) does not cover the test binary's call patterns, so nearest-neighbor has nothing similar to retrieve. That is the expected, honest behaviour of retrieval under domain mismatch, and it motivates the LLM row: a model that reads the disassembly is not limited to what the corpus happens to contain.

⚠️ These are tiny demo binaries (≈5–7 functions each) — the numbers illustrate the pipeline and the method differences, not a benchmark verdict. A real evaluation needs a large labeled corpus of binaries; the harness is built to scale to that (--corpus a b c --test d).

To include a real base model:

ollama pull qwen2.5-coder:7b
PYTHONPATH=. python experiments/compare_methods.py \
    --corpus examples/demo2 --test examples/demo --recopilot-model qwen2.5-coder:7b

Quick start

# 1) build the demo binary (needs gcc; or point --binary at any symbol-bearing binary)
bash examples/build.sh

# 2) run the pipeline — mock backend, no API key, no network:
PYTHONPATH=. python experiments/run_name_recovery.py --binary examples/demo

# offline smoke tests:
PYTHONPATH=. python tests/test_smoke.py

Plug in a real LLM

# Local model via Ollama (free, private; ReCopilot's base family):
ollama pull qwen2.5-coder:7b
PYTHONPATH=. python experiments/run_name_recovery.py \
    --binary examples/demo --backend ollama --model qwen2.5-coder:7b

# Or any OpenAI-compatible API:
pip install openai
export OPENAI_API_KEY=sk-...        # OPENAI_BASE_URL for OpenRouter/Together/vLLM
PYTHONPATH=. python experiments/run_name_recovery.py \
    --binary examples/demo --backend openai --model gpt-4o-mini

The pipeline is identical across backends — only the --backend flag changes — so the mock lower bound and a real LLM are measured on exactly the same prompts.


Layout

src/rag/context.py     # retrieval: disassemble -> per-function callees/callers/body
src/rag/prompt.py      # augment: build the context-rich prompt (never leaks the name)
src/llm/{mock,openai_api,ollama}.py   # pluggable generation backends
src/methods.py         # the compared methods: heuristic, NN-retrieval, majority, LLM
src/eval/name_recovery.py             # exact-match / token-F1 / semantic-hit metrics
experiments/run_name_recovery.py      # single-method end-to-end run
experiments/compare_methods.py        # head-to-head comparison + chart
examples/demo.c, demo2.c              # symbol-rich demos (ground truth)
tests/test_smoke.py                   # offline checks

Roadmap

  • Retrieval of call context + context-augmented prompting
  • Pluggable backends (mock / OpenAI-compatible / Ollama) + name-recovery metrics
  • Real-LLM run vs. the mock lower bound, across several binaries
  • More tasks from the ReCopilot menu: type inference, one-line summarization
  • Richer context: data-flow slice, string literals, and call-graph neighbors' names

License

MIT — see LICENSE. This project reuses none of ReCopilot's code, weights, or data; it only builds on the published idea, with attribution above.

Author

Emad Mahmodi · portfolio

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages