DEV Community

Noble Ogbonna
Noble Ogbonna

Posted on

How I Used a Graph Database to Rebuild Lost Source Code with AI

Banks, space programs, and old industrial systems still run on compiled programs (.exe files, Linux ELF files, firmware) whose source code was lost years ago. The authors retired, and the tools used to build them no longer exist.

The standard way to look inside is a decompiler like NSA's Ghidra, which turns machine code back into C-like text. But the output is rough. It won't compile, and values show up only as raw memory addresses with no explanation.

The problem: the context cliff

Here is a function from our billing test:

double compute_progressive_tax(double amount) {
    double *brackets = (double *)0x00405000; // what is this?
    ...
}
Enter fullscreen mode Exit fullscreen mode

The function reads a table at address 0x00405000, but nothing in its code says what's in the table. Another function, two calls away, filled that table with the tax brackets when the program started.

An AI model that sees only this one function can't know that. So it guesses, usually a flat 10% rate. We call this the context cliff: the answer exists in the program, but outside what the model can see. On our billing test, a standalone model matched the original program's results only 35% of the time.

The idea: store the program as a graph

A graph database stores things (nodes) and the links between them (edges). A compiled program is already shaped like that: functions call other functions, and functions read and write memory.

GhostGraph loads the decompiled program into FalkorDB as a graph:

  • Functions are nodes
  • CALLS edges connect a function to the functions it calls
  • READS and WRITES edges connect functions to memory addresses

Now the missing information is one query away. Cypher, FalkorDB's query language, follows the link from the function that reads an address to the function that wrote it:

MATCH (target:Function {name: $function_name})-[:READS]->(mem:GlobalMemory)<-[:WRITES]-(initializer:Function)
RETURN mem.address, initializer.name, initializer.pseudocode
Enter fullscreen mode Exit fullscreen mode

In plain words: "Find the memory this function reads, then find whoever wrote to it." For the billing example, this returns the initializer function with the real brackets: 0% up to 100, 5% up to 500, and 12% up to 2000.

A team of agents working on the graph

Four AI agents share the graph as their common memory:

  1. Topology agent: reads the binary with the Capstone disassembler and loads the graph.
  2. Reconstruction agent: queries the graph through MCP tools (a standard way to give AI models tools) and writes clean source code.
  3. Verification agent: runs the new code against 20 test inputs and compares results with the original program.
  4. Supervisor agent: passes work between the others and restarts the loop when tests fail.

We also used FalkorDB's built-in PageRank (a score of which functions are most connected) to decide which functions to rebuild first, and shortest path queries to trace call chains. Separate graphs hold each target, so billing, Space Invaders, and Lunar Lander never mix.

Failure memory

When rebuilt code fails a test, the agent writes the failure into the graph:

CREATE (c)-[:FAILED_ON {diff: $diff}]->(t)
Enter fullscreen mode Exit fullscreen mode

On the next attempt, the agent reads those failures first and fixes the exact edge case instead of repeating the same mistake. The graph is both the knowledge base and the agents' memory of what went wrong.

Results

We tested three targets:

Target Without graph With GhostGraph
Billing engine 35% 100%
Apollo 11 Lunar Lander 40% 100%
1978 Space Invaders Broken game state 100%

The Lunar Lander shows why the graph matters. Without it, the model assumed Earth's gravity (9.81 m/s²). With it, the agents found the real lunar value (1.62 m/s²) in a memory table, and the rebuilt physics landed the module.

Each verified result also gets a signed certificate (HMAC-SHA256) that records the tests passed. Changing anything afterward breaks the signature, so anyone can check that a result is real.

What I learned

Graph queries gave the model the one thing it was missing: connections between functions that its context window never held. And storing failures in the graph made the agents improve with each attempt.

Try it

Built for the WeMakeDevs Ɨ FalkorDB Graph Hacks hackathon. Thanks to both teams.

Top comments (0)