DecompilerAI

DECAI a Neural C Decompiler

Free

€0

no credit card

  • Decompile x86-64 Programs
  • Decompile ELF
  • Upload 16kB Programs
  • Upload 5 Programs
  • Decompile -O0 -O1 -O2 -O3 Optimized Programs
  • GPT 4o Refinement
  • Communicate with an LLM for Code Changes
Get started

Pro

€84.99

per month

  • Everything in Essential
  • Verify Correctness via SMT-Solver
  • Upload 1 MB Programs
  • Upload 100 Programs
  • Priority support
Buy a license

Enterprise

€ POR

price on request

  • Everything in Pro
  • Decompile Packed Programs (UPX, Kiteshield, ...)
  • Vulnerability Scan
  • Malware Scan
Contact us

Prices are final amounts. As a small business under § 19 UStG (Kleinunternehmerregelung), no VAT is charged or shown. Cancel anytime.

Decompiler Evaluation

A detailed repository-level evaluation of C decompilers using the white-box symbolic-execution framework DecEvalSE, which employs differential testing.

Decompiler Evaluation Essential

€ POR

price on request

  • Support for x86-64
  • Functional correctness evaluation via symbolic execution
  • Up to 500 functions per executable
  • Up to 10 executables
  • Detailed report with compilability, pass-rate, and mismatch findings
  • Counterexample-driven evaluation for semantic divergences
  • Requires customer-implemented PredictionAPI.py model bridge
Contact us

Contact Us

Custom

tailored scope

  • Need more than 10 executables or larger evaluation scope?
  • Need help integrating your model through PredictionAPI.py?
  • Need consultation with decompiler evaluation?
Contact us

Prices are final amounts. As a small business under § 19 UStG (Kleinunternehmerregelung), no VAT is charged or shown. Cancel anytime.

How DecEvalSE works

DecEvalSE full pipeline diagram
Step 1
Compilation
DecEvalSE compiles programs from user provided C repositories.
Step 2
Decompilation
DecEvalSE challenges your decompiler to emit source code for functions.
Step 3
Test Generation
DecEvalSE generates a test harness to assert equivalence between a function and it's decompilation.
Step 4
Test Execution
DecEvalSE executes the tests and checks for assertion failures.
Step 5
Statistics Collection
DecEvalSE collects metrics like compilability, test pass, KLEE coverage, GCOV coverage and readability scores.
Step 1

Compilation

User provides compilable C repositories with build artifacts (configure scripts, make files, etc.).

While compiling, the framework intercepts compiler invocations and injects additional flags so that it can produce several artifacts in one pipeline: native executables for decompilation, GCOV-instrumented binaries for coverage analysis, and LLVM bitcode required for symbolic execution with KLEE.

shell
Step 2

Decompilation

For custom evaluations, the decompiled candidate is provided through a customer-implemented PredictionAPI.py bridge. DecEvalSE launches this Python process, sends a challenge request including the target function name and program path over IPC, and receives the predicted high-level function through a well-defined interface.

In practice, PredictionAPI.py is the integration layer between the customer’s decompiler and the evaluation framework. The template intentionally leaves both decompiler initialization and the actual prediction routine to the customer.

Customers may also expose their decompiler as a hosted online service, so the evaluation can interact with it through the interface without requiring access to the underlying source code, allowing proprietary implementations to remain protected.

PredictionAPI.py sketch
def predict(functionName, programPath):  # IMPLEMENT THIS PART
    ...

def main():
    # Load decompiler here  # IMPLEMENT THIS PART
    ...
    # Challenge Request Protocol # WE PROVIDE THIS PART
    ...
            
Step 3

Test Generation

Once the candidate function is available, we place it beside the original implementation and build an equivalence harness. The harness feeds both functions the same inputs and checks whether their observable results match.

Original vs Decompilation
int f_original(int num) {
    int i;
    if (num == 0)
        return -1;

    int count = -1;
    for (i = 0; i < 32; i++) {
        if ((num >> i) & 1)
            count = i;
    }
    return count;
}

int f_decompiled(int num) {
    if (num == 0)
        return -1;

    int position = -1;
    for (int i = 0; i < sizeof(int) * 8; i++) {
        if (num & (1 << i))
            position = i;
    }
    return position;
}
Step 4

Test Execution

In a normal unit test you choose concrete values such as x = 0 or x = 37. In symbolic execution, the input becomes symbolic. Instead of checking one run, the engine explores many feasible paths and accumulates path constraints as branches are taken.

If the assertion can be violated on some reachable path, the solver tries to synthesize a real concrete input that triggers the mismatch.

Test Suite
#include <assert.h>
#include "klee/klee.h"

int main() {
    int x;
    klee_make_symbolic(&x, sizeof(x), "x");

    int a = f_original(x);
    int b = f_decompiled(x);

    klee_assert(a == b);
    return 0;
}
Step 5

Statistics Collection

The most useful output of symbolic execution is the counterexample. When the assertion is violated, the solver returns a concrete input that reproduces the discrepancy. That means we can show exactly where the original and decompiled implementations disagree.

Another useful output contains readability scores using Levenshtein Edit Distance expressed as a similarity metric, as well as CorpusBleu-, CodeBert-, CodeBleu-, CrystalBleu-Scores. In the future we plan to include LLM-assisted readability evaluations, variable name recover, struct name recovery, and many more as useful metric on the readability part.

A last useful output colntains the KLEE and GCOV coverage metrics, to give the evaluator an idea to which extent the symbolic execution engine explored the equivalence check per function.

Metric
Compilability
Is the decompilation compilable?
Metric
Pass
Is the decompilation functionally equivalent?
Evidence
Coverage
How much meaningful execution space the evaluation actually explored.
Evidence
Counterexamples
Concrete failing inputs you can rerun and inspect.
Additional Information

Supported Function Signatures

DecEvalSE supports substantially more than primitive toy signatures.

Repository code rarely consists only of functions such as int f(int). In practice, functions operate on buffers, pointer chains, nested structs, static helpers, standard input, and observable memory effects rather than only through a direct scalar return value.

DecEvalSE therefore supports a broader class of signatures, including primitive parameters, arbitrary pointer depth, nested structs and structs containing supported pointer fields, void-returning functions, static functions, and functions whose behavior is exposed through return values, mutated arguments, standard input, or captured output.

This makes the evaluation applicable to more realistic repository code, while still acknowledging current limits: floating-point types are excluded, record return types are currently not evaluated symbolically, and highly environment-dependent behavior or large nullable pointer graphs can be more expensive to model precisely.

Examples of supported signatures
/* primitive parameters + primitive return */
int add(int a, int b);

/* pointer parameter */
int sum_array(const int *arr, int n);

/* output through memory */
void compute_stats(const int *arr, int n, int *min_out, int *max_out);

/* arbitrary pointer depth */
int read_through_triple(int ***value);

/* pointer selection / clamping */
int **pick_triple_slot(int ***slots, int count, int index);

/* struct by value */
int process_point(struct Point p);

/* nested struct by pointer */
int config_score(const struct Config *cfg);

/* struct with pointer fields */
int bundle_score(struct Bundle bundle);

/* struct with double-pointer field */
int boxholder_score(struct BoxHolder holder, int delta);

/* in-place mutation via struct pointer */
void normalize_point(struct Point *p);

/* void return with observable memory effects */
void swap_ints(int *a, int *b);

/* static helper */
static int helper_hash(const char *s);

/* reads from standard input */
int read_token(char *buf, int max_len);

/* buffer transformation */
void xor_buf(uint8_t *dst, const uint8_t *src, int n);

/* pointer + struct output */
void update_node(struct Node *node, int *status_out);

/* array of pointers */
int count_tokens(const char **tokens, int count);

/* recursive pointer-shaped input */
int score_chain(struct Node *head);

/* string / buffer processing */
size_t trim_copy(char *dst, const char *src, size_t max_len);