Using AI-assisted decompilation of Radare2

@cryptax
6 min readSep 17, 2024

--

A few months ago, Radare2 (aka r2), an open source disassembler which can be entirely used by command line, started implementing AI plugins to assist with artificial intelligence.

Assuming you don’t have any API key for a paid language model, I’m going to explain how to set up r2 with a free local AI model. We’ll also discuss the results.

Setup

We need:

  1. A fresh radare2 (I’m not covering this) and an updated package manager: r2pm -U
  2. Decai. That’s a decompiler based on AI. To install it, r2pm -ci decai.
  3. A language model. As I don’t have access to any commercial AI, I’ll use a local language model. I am not sure what’s the intended way to download a language model, but I do it via a dummy request to r2ai. So, we need r2ai, to be installed with r2pm -ci r2ai.
  4. To use a local model, we need r2ai-server: r2pm -ci r2ai-server.

Installing a language model

I launch r2ai interactively with r2pm -r r2ai. This should give you a r2ai prompt:

$ r2pm -r r2ai
None of PyTorch, TensorFlow >= 2.0, or Flax have been found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.
[r2ai:0x00006aa0]>

The available commands can be listed with -h. Language models often have funky long names. Use -M to get examples of local models (see the “decai” section) , and select the model I want to use with -m.

[r2ai:0x00006aa0]> -M
Decai:
-m ibm-granite/granite-20b-code-instruct-8k-GGUF
-m QuantFactory/granite-8b-code-instruct-4k-GGUF
-m cognitivecomputations/dolphin-2.9.3-mistral-nemo-12b-gguf
-m bartowski/Gemma-2-9B-It-SPPO-Iter3-GGUF
[r2ai:0x00006aa0]> -m QuantFactory/granite-8b-code-instruct-4k-GGUF
[r2ai:0x00006aa0]> write a 4 line poem thanking Pancake, the maintainer of radare2, for his time and work

To initiate the download of the model, I’m not sure what’s the official way to do it. Personally, I simply ask the AI a dummy questions, e.g. write a short poem, or whatever. This triggers several questions about which exact model r2ai should download. For this attempt, I selected the smallest model.

Downloading the model takes some time as models are big. Fortunately, there is a progress bar :)

Launching r2ai-server

Next step is to make that model available. For that I use r2ai-server.

$ r2pm -r r2ai-server -l
r2ai
llamafile
llamacpp
koboldcpp
$ r2pm -r r2ai-server -m
granite-8b-code-instruct-4k.Q2_K

Option -m select the model, and -l the server. Note the server launches on port 8080 by default (configurable with -p).

$ r2pm -r r2ai-server -l r2ai -m granite-8b-code-instruct-4k.Q2_K
None of PyTorch, TensorFlow >= 2.0, or Flax have been found. Models won't be available and only tokenizers, configuration and file/data utilities can be used.
[09/17/24 14:50:24] INFO r2ai.server - INFO - [R2AI] Serving at port 8080 web.py:334

Testing with Caesar encryption

Let’s test the AI-assisted decompiler. I wrote a very basic routine, which implements the ancient Caesar encryption algorithm, and compiled it.

int shift = 3;

char *encrypt(char *input, int len) {
char *output = (char*) malloc(sizeof(char)*(len+1));
for (int i = 0; i < len; i++) {
output[i] = (input[i] + shift) % 256;
}
output[len] = '\0';
return output;
}

I run r2, analyze the program (aaa), list functions (afl) to locate encrypt(), jump to the function (s sym.encrypt), disassemble it to have a look (pdf).

Radare2’s disassembly for the encrypt() function

Using decai

Let’s get some help from the AI to understand the assembly. The command is decai, followed by an option: -H for help, -d to decompile the current function, -e to configure.
In my case, all default settings are correct, except the api which should be set to r2ai: decai -e api=r2ai. Some other settings can be set like decai -e debug=true for more information, or decai -e lang=java to produce Java output (depending on models and complexity of the function, some output languages may work better than others).

Finally, issue decai -d to ask the AI to help us understand the current function. Note that the answer may take a long time to return (depends on your machine, the model, the question…) and unfortunately there is no progress bar for that.

We can check the r2ai-server receives the request (at 14:50:53). Actually, decai posts an HTTP request (on port 8080), via curl, asking it to “show the code with no explanation” etc.

GET
CUSTOM
RUNLINE: -R
127.0.0.1 - - [17/Sep/2024 14:50:53] "GET /cmd/-R HTTP/1.1" 200 -
GET
CUSTOM
RUNLINE: -i /tmp/.pdc.txt Only show the code with no explanation or introductions. Simplify the code: - take function arguments from comment - remove dead assignments - refactor goto with for/if/while - use better names for variables - simplify as much as possible. Explain this pseudocode in java

At 14:51:41, I got the following response:

How good is that result, compared to the original source code? The AI understood the function takes 2 arguments, how the malloc is done, and the loop. But it completely missed the most important part: what’s done inside the loop i.e shifting character by 3.

Decompiling the main

Our first test is a bit disappointing in quality, but let’s not jump to a conclusion yet. This time, I asked it to decompile the main() function.

On the left hand side, we have the original source code. The encrypt() function is not displayed (but I provided its source code earlier in this blog post). On the right side, we have the output given by decai. On my laptop, the answer was returned in 3 minutes.

The result is far better!

  • The login of main() is correctly implemented. Only the printf “Welcome to Caesar encryption” is missing (this is surprising), and the tests for null pointer (not very important to understand the logic).
  • The logic of encrypt() and decrypt() are perfectly correct this time! It’s just surprising the AI does not work out the return type.

Overall, this time, I’d say that because the logic is well implemented, the results are helpful to a reverse engineer. Of course, we’ll still need some brains to work out the initial source code, but we’re one step closer.

Decompiling Dart

In a final test, I ask decai to decompile the same Caesar program, implemented in Dart. I compiled it as a non-stripped AOT binary, and asked decai to provide the decompilation in pseudo Dart.

On the left, the original source code in Dart. On the right, the decompiled code in pseudo Dart.

The result is “amusing” but not satisfactory: it invents a “Hello World” string which never existed (this is typical of AI errors), and doesn’t grasp the Caesar algorithm.
Finally, I tested decai over a stripped Dart AOT binary. The AI server did not respond (“input is too large”).

Approximate tests of decai over a simple Caesar encryption program, implemented in C or in Dart. The time to get an answer from the AI was measured on a quad core 11th Gen Intel Core i7–1185G7 laptop. The results are approximate because I did not pay attention to kill other tasks, nor to time the answers precisely. The results quality are my own evaluation: 0 means no result (did not work), and 10 means perfect (as good or even better than the original source code).

Conclusion

  • Decai can be used with a local AI model.
  • Over a (very) simple program, the quality of results are quite useful, but for more complex cases, currently, the AI simply never responds (“infinite” loop) or produces useless results.
  • It’s obvious, but it should be repeated: the quality of results depends on the AI model: pancake obtains excellent results with Claude AI, proof that decai is able to work perfectly well when fed with a good model! [haskell, bash, C].

Many thanks to pancake, and all people who contribute to r2ai, r2ai-server and decai! Love the idea!

— Cryptax

--

--

@cryptax
@cryptax

Written by @cryptax

Mobile and IoT malware researcher. The postings on this account are solely my own opinion and do not represent my employer.