13 ms·
LLM4Decompile: Decompiling Binary Code with LLM
- celdon25 3y agoHow does it actually compare to non-LLM decompilers IDA, Binja, etc? I only see comparisons with other LLMs.
- albertan017 3y agoThanks! We're working on Ghidra/IDA pro. The problem we face is the right kind of data to test with and how to evaluate it. It's like there's no "standard" benchmark/metrics that everyone uses for decompilation.
- saagarjha 3y agoThe approach here is interesting in that it answers a question a lot of people have been asking: “what happens if we pipe a binary into a trained LLM and ask it to decompile it?” The answer is that it doesn’t really work at all right now! This is a surprising result because the design of the paper kind of doesn’t allow for any other conclusion to be drawn. Notably, if the LLM did a really good job in the evaluation they designed it would still be unclear whether it was actually useful, because the test “does it compile and pass a few test cases” is not actually a very good way to test a decompiler. A couple people here have suggested that the generated decompilation should match the source code exactly, which is a challenging thing to achieve and still hotly debated on whether it is a good metric or not. But the results here show that we’re starting to barely get past the “does it produce code” stage and move towards “does it produce code that looks vaguely correct” status but we’re definitely not there yet. Future steps of “is this a useful tool to drive decompilation” and “does this do better than state of the art” and “is this perfect at decompiling things” are still a long ways away. So it’s good to look at as a negative result as this area continues to attract new interest.
- albertan017 3y agoThanks! Our initial experiments indicate that for simple cases, such as short snippets (tens of lines) of code without external dependencies, the LLM can decompile very well. However, for more complicated examples, it tends to offer speculative solutions, and the utility of these results is challenging to assess. The determination of whether the decompiled output is correct or useful is subjective and lacks a universal standard. One approach we're considering is utilizing GPT-4 as a benchmark to evaluate other models' performance. We're open to further suggestions to refine our evaluation methods.
- xvilka 3y agoI think using higher-level input, e.g. the intermediate language like RzIL[1] could produce better results and is more scalable for making such decompliation multiplatform. As RzIL text form resemples SMT, it should make LLM easier to "understand" the meaning. Moreover, information from binary such as symbols, signatures, debug information (DWARF, PDB, etc) could enrich the result further. You can download Rizin[2] and try for yourself by calling `aaa` then `plf` for any chosen functions for architectures supported by RzIL. See the example excerpt for a function with this disassembly: │ │ 0x140007e51 movsd qword [rdi + 0x50], xmm2 │ │ 0x140007e56 mov qword [rdi + 0x48], 0 │ │ 0x140007e5e call sym.rz_test.exe_ht_pp_free ; sym.rz_test.exe_ht_pp_free │ │ 0x140007e63 movaps xmm7, xmmword [var_38h] │ │ 0x140007e68 movaps xmm6, xmmword [var_28h] │ │ 0x140007e6d mov rbp, qword [var_10h] │ └─> 0x140007e72 add rsp, 0x48 │ 0x140007e76 pop r15 │ 0x140007e78 pop rdi └ 0x140007e79 ret 0x140007e6d (set rbp (loadw 0 64 (+ (var rsp) (bv 64 0x68)))) 0x140007e72 (seq (set op1 (var rsp)) (set op2 (bv 64 0x48)) (set sum (+ (var op1) (var op2))) (set rsp (var sum)) (set _result (var sum)) (set _popcnt (bv 8 0x0)) (set _val (cast 8 false (var _result))) (repeat (! (is_zero (var _val))) (seq (set _popcnt (+ (var _popcnt) (ite (lsb (var _val)) (bv 8 0x1) (bv 8 0x0)))) (set _val (>> (var _val) (bv 8 0x1) false)))) (set pf (is_zero (mod (var _popcnt) (bv 8 0x2)))) (set zf (is_zero (var _result))) (set sf (msb (var _result))) (set _result (var sum)) (set _x (var op1)) (set _y (var op2)) (set cf (|| (|| (&& (msb (var _x)) (msb (var _y))) (&& (! (msb (var _result))) (msb (var _y)))) (&& (msb (var _x)) (! (msb (var _result)))))) (set of (|| (&& (&& (! (msb (var _result))) (msb (var _x))) (msb (var _y))) (&& (&& (msb (var _result)) (! (msb (var _x)))) (! (msb (var _y)))))) (set af (|| (|| (&& (msb (cast 4 false (var _x))) (msb (cast 4 false (var _y)))) (&& (! (msb (cast 4 false (var _result)))) (msb (cast 4 false (var _y))))) (&& (msb (cast 4 false (var _x))) (! (msb (cast 4 false (var _result)))))))) 0x140007e76 (seq (set r15 (cast 64 false (loadw 0 64 (+ (var rsp) (bv 64 0x0))))) (set rsp (+ (var rsp) (bv 64 0x8)))) 0x140007e78 (seq (set rdi (loadw 0 64 (+ (var rsp) (bv 64 0x0)))) (set rsp (+ (var rsp) (bv 64 0x8)))) 0x140007e79 (seq (set tgt (loadw 0 64 (+ (var rsp) (bv 64 0x0)))) (set rsp (+ (var rsp) (bv 64 0x8))) (jmp (var tgt))) [1] https://github.com/rizinorg/rizin/blob/dev/doc/rzil.md https://github.com/rizinorg/rizin/blob/dev/doc/rzil.md [2] https://rizin.re https://rizin.re
- 3y ago
- dolmen 3y agoIt seems to me that the objdump step (to transform binary to human readable assembly) seems an unnecessary waste of runtime resources. It should be possible to tokenize directly from the binary.
- albertan017 3y agoThanks! Processing raw binary data directly would be inefficient for the language model, as it's not designed to interpret strings of zeros and ones but for understanding higher-level instructions (like code and natural language).
- dwrodri 3y agoI have been planning to work on something like this. I think that eventually, someone will crack the "binary in -> good source code out of LLM" pipeline but we are probably a few years away from that still. I say a few years because I don't think there's a huge pile of money sitting at the end of this problem, but maybe I'm wrong. A really good "stop-gap" approach would be to build a decompilation pipeline using Ghidra in headless mode and then combine the strict syntax correctness of a decompiler with the "intuition/system 1 skills" of an LLM. My inspiration for this setup comes from two recent advancements, both shared here on HN: 1. AlphaGeometry: The Decompiler and the LLM should complement each other, covering each other's weaknesses. https://deepmind.google/discover/blog/alphageometry-an-olympiad-level-ai-system-for-geometry/ https://deepmind.google/discover/blog/alphageometry-an-olymp... 2. AICI: We need a better way of "hacking" on top of these models, and being able to use something like AICI as the "glue" to coordinate the generation of C source. I don't really want the weights of my LLM to be used to generate syntactically correct C source, I want the LLM to think in terms of variable names, "snippet patterns" and architectural choices while other tools (Ghidra, LLVM) worry about the rest. https://github.com/microsoft/aici https://github.com/microsoft/aici Obviously this is all hand-wavey armchair commentary from a former grad student who just thinks this stuff is cool. Huge props to these researchers for diving into this. I know the authors already mentioned incorporating Ghidra into their future work, so I know they're on the right track.
- potatoman22 3y agoIt's interesting the 6b model outperforms the 33b model. I wonder if it means the 33b model needs more training data? It was pretrained on ~1 million C programs, compared to DeepSeek-Coder, which was trained on 2 trillion tokens, which is a few orders of magnitude more data. I'm also curious about how this compares to non-LLM solutions.
- albertan017 3y agoYes, it's not easy to train a 33B model. An interesting point is, naive fine-tuning, which means if one followed the standard way to fine-tune the model. Training a larger model is tricky, not only the data amount matters, everything like data cleaning, learning rate, and decays will affect the final performance.
- mattashii 3y ago> on ~1 million C programs, compared to [...] 2 trillion tokens, which is a few orders of magnitude more data. Is that comparable like that? This would assume that the average C program of the set is orders (plural) of magnitude less than 2m tokens in size, which could indeed be true but sounds like an optimistic assumption.
- Der_Einzige 3y agoThis has been the dynamics with LLMs for awhile. The majority of LLMs are massively undertrained. 7b models are the least "undertrained" mainstream models we have, hence why they have proliferated so much among the LLM fine-tuning community.
- maCDzP 3y agoCan this be used for deobfuscation of code? I really hadn’t thought about LLM being a tool during reverse engineering.
- albertan017 3y agoThanks! The model is trained only for O0-3, not support for obfuscation. There's still a long way for llm to go.
- Tiberium 3y agoBig LLMs like GPT-4 (and even GPT 3.5 Turbo) can be directly used to beautify obfuscated/minified JS, see e.g. https://thejunkland.com/blog/using-llms-to-reverse-javascript-minification.html https://thejunkland.com/blog/using-llms-to-reverse-javascrip... and https://news.ycombinator.com/item?id=34503233 https://news.ycombinator.com/item?id=34503233
- Eager 3y agoI have tried feeding some of the foundation models obfuscated code from some of the competitions. People might think that the answers would be in the training data already, but I didn't find that to be the case. At least in my small experiments. The model's did try to guess what the code does. They would say things like, "It seems to be trying to print some message to the console". I wasn't able to get full solutions. It's definitely worth more research, not just as a curiosity, but these kinds of problems are good proxies for other tasks and also excellent benchmarks for LLMs particularly.
- evmar 3y agoI did a little experiment with this here: https://neugierig.org/software/blog/2023/01/compiling-advent.html https://neugierig.org/software/blog/2023/01/compiling-advent...
- deleted 3y ago[deleted]
- kken 3y agoPretty wild how well GPT4 is still doing in comparison. It's significantly better than their model at creating compilable code, but is less accurate at recreating functional code. Still quite impressive.
- celdon25 3y agoI’d be impressed if it could do C++ as well as C, which this doesn’t.
- albertan017 3y agoYes, GPT4 is very impressive, as it's not directly trained on the decompilation. We're working on improving our model, please keep watching updates!
- nebula8804 3y agoWill be interesting to see is there is some way to train a decompilation module based on who we know developed the application and use their previous code used as training. For example: Super Mario 64 and Zelda 64 were fully decompiled and a handful of other N64 games are in the process. I wonder if we could map which developers worked on these two games (maybe even guess who did what module) and then use that to more easily decompile any other game that had those developers working on it. If this gets really good, maybe we can dream of having a fully de-obfuscated and open source life. All the layers of binary blobs in a PC can finally be decoded. All the drivers can be open. Why not do the OS as well! We don't have to settle for Linux, we can bring back Windows XP and back port modern security and app compatibility into the OS and Microsoft can keep their Windows 11 junk...at least one can dream! :D
- ZitchDog 3y agoI doubt the code would be identifiable. It wouldn’t be the actual code written, but it would be very similar. But I assume many elements of code style would be lost, and any semblance of code style would be more or less hallucinated.
- K0IN 3y agoif it can make test from the decompiled code, we could reimplement it with our code style. might be cool to have some bunch of llms working together with feedback loops.
- coddle-hark 3y agoI wrote my bachelor thesis on something tangential — basically, some researchers found that it was possible in some very specific circumstances to train a classifier to do author attribution (i.e. figure out who wrote the program) based just on the compiled binaries they produced. I don’t think the technique has been used for anything actually useful, but it’s cool to see that individual coding style survives the compilation process, so much so that you can tell one person’s compiled programs apart from another’s.
- 3y ago
- kukas 3y agoHey, I am working on my own LLM-based decompiler for Python bytecode (https://github.com/kukas/deepcompyle https://github.com/kukas/deepcompyle). I feel there are not many people working on this research direction but I think it could be quite interesting, especially now that longer attention contexts are becoming feasible. If anyone knows a team that is working on this, I would be quite interested in cooperation.
- maple3142 3y agoThere is [PyLingual](https://pylingual.io/ https://pylingual.io/), but it is not open source unfortunately. I am not sure if it is also LLM based.
- albertan017 3y agoI found lots of decompilation work are conducted on C. It seems not much python projects are compiled into binaries.
- ok123456 3y agoIs there a benefit from using an LLM for Python byte code? Python byte code is high enough level that it's possible to translate it directly to source code from my experience.
- kukas 3y agoMy motivation is that the existing decompilers work only for Python versions till ~3.8. Having a model that could be finetuned with every new Python version release might overcome the need for highly specialized programmer that is able to update the decompiler to be compatible with the new version. It is also a toy example for me to set up a working pipeline and then try to decompile more interesting targets.
- a2code 3y agoWhy Python? First, python is a language with a large open-source library. Second, I do not think it is used for software that is distributed as binaries?
- idansukmawijaya 3y ago[flagged]
- idansukmawijaya 3y ago[flagged]
- jagrsw 3y agoDecompilation is somewhat a default choice for ML in the world of comp-sec. Searching for vulns and producing patches in source code is a bit problematic, as the databases of vulnerable source code examples and their corresponding patches are neither well-structured nor comprehensive, and sometimes very, very specific to the analyzed code (for higher abstraction type of problems). So, it's not easy to train something usable beyond standard mem safety problems and use of unsafe APIs. The area of fuzzing is somewhat messy, with sporadic efforts undertaken here and there, but it also requires a lot of preparatory work, and the results might not be groundbreaking unless we reach a point where we can feed an ML model the entire source code of a project, allowing it to analyze and identify all bugs, producing fixes and providing offending inputs. i.e. not yet. While decompilation is a fairly standard problem, it is possible to produce input-output pairs somewhat at will based on existing source code, using various compiler switches, CPU architectures, ABIs, obfuscations, syscall calling conventions. And train models on those input-output pairs (i.e. in reversed order).
- albertan017 3y agoThanks! But people want an all-in-one solution for decompilation. Given the vast array of architectures and compilation settings, and the fact that these information are usually not predetermined, finding a way to effectively navigate this complexity is quite difficult.
- a2code 3y agoThe problem is interesting in at least two aspects. First, an ideal decompiler would eliminate proprietary source code. Second, the abundant publicly available C code allows you to simply make a dataset of paired ASM and source code. There is also a lot of variety with optimization level, compiler choice, and platform. What is unclear to me is: why did the authors fine-tune the DeepSeek-Coder model? Can you train an LLM from zero with a similar dataset? How big does the LLM need to be? Can it run locally?
- saagarjha 3y agoIdeal decompilers do not exist. In some sense they can never exist as compilers are lossy, but even taking a liberal view of “high level understanding of the resulting code” this is essentially the AGI for computer security. Nobody has come close to it!
- albertan017 3y agoThanks! Training a language model from scratch is data-intensive; Llama2 was developed using 2 trillion tokens, while our dataset is around 4 billion. The appropriate size of the model is not straightforward to determine. In our experiments, a 7 billion parameter model achieved 21% executability compared to just 10% for a 1 billion parameter model. However, their re-compilability rates are quite similar. To run a 1 billion parameter model, a minimum of 2GB GPU memory is necessary, which is feasible on most GPUs. A 7 billion parameter model needs 14GB, suitable for GPUs like the 3090/4090 series. For running a 33 billion parameter model, an A100 GPU (80G) would be the single card option, although technically a MacBook could work, but you won't really want to use it.
- 3abiton 3y agoI assume it's related to the cost of training vs fine-tuning. It could be also a starting point to validate an idea.
- mike_hearn 3y agoMost proprietary code runs behind firewalls and won't be affected by this one way or another. It's basically always better to start training with a pre-trained model rather than random, even if what you want isn't that close to what you start with.
- madisonmay 3y agoThis is an excellent use case for LLM fine-tuning, purely because of the ease of generating a massive dataset of input / output pairs from public C code
- bt1a 3y agoI would also think that generating a very large amount of C code using coding LLMs (using deepseek, for example, + verifying that the output compiles) as synthetic training data would be quite beneficial in this situation. Generally the quality of synthetic training data is one of the main concerns, but in this case, the ability for the code to compile is the crux.
- Zambyte 3y agoI would think that the primary benefit of this over existing decompiler tools would be the ability to use sensible names for identifiers, break up a project to be a sensible set of modules, and maybe even add realistic / helpful comments. If you're synthesizing code to do that, you'll probably gain on the front of generating code that compiles, at the cost of these advantages.
- klik99 3y agoThis is a fascinating idea, but (honest question, not a judgement) would the output be reliable? It would be hard to identify hallucinations since recompiling could produce different machine code. Particularly if there is some novel construct that could be a key part of the code. Are there ways of also reporting the LLMs confidence in sections like this when running generatively? It’s an amazing idea but I worry it would stumble invisibly on the parts that are most critical. I suppose it would just need human confirmation on the output
- GoblinSlayer 3y agoSounds like it should be able to split the code into functions with inferred API, then you should be able to fuzz these functions in binary and source versions.
- Eager 3y agoThis is why round-tripping the code is important. If you decompile the binary to source, then compile the source back to binary you should get the original binary. You just need to do this enough times until the loss drops to some acceptable amount. It's a great task for reinforcement learning, which is known to be unreasonably effective for these types of problems.
- thfuran 3y ago>If you decompile the binary to source, then compile the source back to binary you should get the original binary. You really can't expect that if you're not using exactly the same version of exactly the same compiler with exactly the same flags, and often not even then.
- vasvir 3y agoRight. A less formidable problem with higher chances of succeeding is from a given binary to figure out first compiler, compiler-version, compiler-flags. From there you could have a model for every combination or at least a model for the compiler variant and use the other info (version, flags) as input to the model.
- Eager 3y ago[flagged]
- asylteltine 3y ago[dead]
- ReptileMan 3y agoLet's hope it kills Denuvo ...
- Retr0id 3y agoDecompilation and deobfuscation are related but distinct tasks
- AndrewKemendo 3y agoIf successful wouldn’t you be replicating the compilers machine code 1:1? In which case that means fully complete code can live in the “latent space” but is distributed as probabilities Or perhaps more likely would it be replicating the logic only, which can then be translated into the target language I would guess that any binary that requires a non-deterministic input (key, hash etc…) to compile would break this Fascinating
- m3kw9 3y agoBasically predicting code token by token except now you don’t even have a large enough context size and worse, you are using RAG
- xorvoid 3y agoAs someone who is actively developing a decompiler to reverse engineer old DOS 8086 video games, I'd have a hard time trusting an LLM to do this correctly. My standard is accurate semantics lifting from Machine Code to C. Reversing assembly to C is very delicate. There are many patterns that tend to usually map to obvious C constructs... except when they don't. And that assumes the original source was C. Once you bump into routines that were hand-coded assembly and break every established rule in the calling conventions, all bets are off. I'm somewhat convinced that decompilation cannot be made fully-automatic. Instead a good decompiler is just a lever-arm on the manual work a reverser would otherwise be doing. Corollary: I'm also somewhat convinced that only the decompiler's developers can really use it most effectively because they know where the "bodies are buried" and where different heuristics and assumptions were made. Decompilers are compilers with all the usual engineering challenges, plus a hard inference problem tacked on top. All that said, I'm not a pessimist on this idea. I think it has pretty great promise as a technique for general reversing security analysis where the reversing is done mostly for "discovery" and "understanding" rather than for perfect semantic lifting to a high-level language. In that world, you can afford to develop "hypotheses" and then drill down to validate if you think you've discovered something big. Compiling and testing the resulting decompilation is a great idea. I do that as well. The limitation here is TEST SUITE. Some random binary doesn't typically come with a high-coverage test suite, so you have to develop your own acceptance criterion as you go along. In other words: write tests for a function whose computation you don't understand (ha). I suppose a form of static-analysis / symbolic-computation might be handy here (I haven't explored that). Here you're also beset with challenges of specifying which machine state changes are important and which are superfluous (e.g. is it okay if the x86 FLAGS register isn't modified in the decompiled version, probably yes, but sometimes no). In my case I don't have access to the original compiler and even if I did, I'm not sure I could convince it to reproduce the same code. Maybe this is more feasible for more modern binaries where you can assume GCC, Clang, MSVC, or ICC. At any rate: crazy hard, crazy fun problem. I'm sure LLMs have a role somewhere, but I'm not sure exactly where: the future will tell. My guess is some kind of "copilot" / "assistant" type role rather than directly making the decisions. (If this is your kind of thing... I'll be writing more about it on my blog soonish...)
- ouraf 3y ago
- mdaniel 3y agorelevant: https://news.ycombinator.com/item?id=34250872 https://news.ycombinator.com/item?id=34250872 (G-3PO: A protocol droid for Ghidra, or GPT-3 for reverse-engineering <https://github.com/tenable/ghidra_tools/blob/main/g3po/g3po.py https://github.com/tenable/ghidra_tools/blob/main/g3po/g3po....>; Jan, 2023; 44 comments) ed: seems they have this, too, which may value your submission: https://github.com/tenable/awesome-llm-cybersecurity-tools#awesome-large-language-model-tools-for-cybersecurity-research https://github.com/tenable/awesome-llm-cybersecurity-tools#a...
- albertan017 3y agoWe find several work on refining Ghidra decompilation results with GPTs, that could be another interesting directions!
- sinuhe69 3y agoFor me the huge difference between re-compilability and re-excuteability scores is very interesting. GTP4 achieved 8x% on re-compilability (syntactically correct) but abysmal 1x% in re-excutability (schematically correct) demonstrated once again its overgrown mimicry capacity.
- sitkack 3y ago> overgrown mimicry I don't think it shows that. GPT4 was not trained on decompiling binaries back into C. Amazing result for an untrained task. We are soon going to have robust toolchain detection from binaries, and source recovery with variable and function names.
- albertan017 3y agoWe're interested in the toolchain, could you share the link or reference to it? GPT4 does an amazing work, we're also very surprised that it can work.
- sitkack 3y agoI don't have a toolchain, I am predicting research that will be able to detect the exact toolchain used from the binary. If you can detect the toolchain, then you can iterate to a fixed point (grind) until you recover a perfect copy of the source.
- speedylight 3y agoI have thought about doing something similar for heavily obfuscated JavaScript. Very useful for security research I imagine!
- albertan017 3y agoIdeally, with a substantial dataset of obfuscated JavaScript and corresponding raw code, a language model could potentially make good predictions. The first key difficulty, however, is collecting a large-scale dataset and setting up a system for automatic compilation and segment out the binary-source pairs.
- quantum_state 3y agoIt seems the next logical step would be LLMAssistedHacking to turn things up side down…
- deleted 3y ago[deleted]
- mahaloz 3y agoIt’s always cool to see different approaches in this area, but I worry its benchmarks are meaningless without a comparison of non-AI based approaches (like IDA Pro). It would be interesting to see how this model holds up on metrics from previous papers in security.
- albertan017 3y agoThanks! We're working on Ghidra/IDA pro. The problem we face is the right kind of data to test with and how to evaluate it. It's like there's no "standard" benchmark/metrics that everyone uses for decompilation.
- mahaloz 3y agoAs others have said, the standardization of metrics is still something debated, but at the same time, this space has been explored by various top-tier papers that your paper did not cite. For example, DREAM [1], evaluated using the classic metric of goto-emittence. Rev.ng [2], evaluated using Cyclomatic Complexity and gotos. SAILR [3], evaluated using the previous metrics and a Graph Edit Distance score for the structure of the code. I feel that without a justification for dropping previously established metrics by the peer review process, you weaken your new metrics. However, I still think this is an interesting paper. It just could be made more legit by thoroughly reading/citing previous work in the area and building an argument for why you may go against it. [1]: https://net.cs.uni-bonn.de/fileadmin/ag/martini/Staff/yakdan/dream_ndss2015.pdf https://net.cs.uni-bonn.de/fileadmin/ag/martini/Staff/yakdan... [2]: https://rev.ng/downloads/asiaccs-2020-paper.pdf https://rev.ng/downloads/asiaccs-2020-paper.pdf [3]: https://www.usenix.org/system/files/sec23winter-prepub-301-basque.pdf https://www.usenix.org/system/files/sec23winter-prepub-301-b...
- sitkack 3y agoReferences make a paper stronger!
- YeGoblynQueenne 3y agoIf I read the "re-executability" results in the Results figure right then that's a great idea but it doesn't really work: https://raw.githubusercontent.com/albertan017/LLM4Decompile/main/samples/results_decompile.png https://raw.githubusercontent.com/albertan017/LLM4Decompile/... To clarify: >> Re-executability provides this critical measure of semantic correctness. By re-compiling the decompiled output and running the test cases, we assess if the decompilation preserved the program logic and behavior. Together, re-compilability and re-executability indicate syntax recovery and semantic preservation - both essential for usable and robust decompilation.