3 ms·
In the past couple of years I've worked on a Ghidra extension that can export any subset of a program as a relocatable object file. I built that because I tried
by boricj 2y ago
In the past couple of years I've worked on a Ghidra extension that can export any subset of a program as a relocatable object file. I built that because I tried to reverse-engineer/decompile a video game and had unconventional opinions on how to proceed about it.
I didn't design it for a post-collapse scenario, but one can salvage program parts from binary artifacts and create new programs with it, just like how a mechanic in a Mad Max world can scavenge car parts from a scrapyard and create new contraptions. It deconstructs the classical compile-assemble-link workflow in a manner that tends to cause migraines to sane persons.
I've even managed to slightly bend the rules around ABI and sling binary code across similar-ish platforms (PlayStation to Linux MIPS and Linux x86 to Windows x86), but to really take this to its logical conclusion I'd need a way to glue together incompatible ABIs or even ISAs, in order to create program chimeras from just about any random set of binaries.
- dgellow 2y agoIs that extension publicly available?
- boricj 2y agoYou can find it here: https://github.com/boricj/ghidra-delinker-extension https://github.com/boricj/ghidra-delinker-extension I've demonstrated a bunch of use-cases on my blog, ranging from porting software to pervasive binary patching and pilfering application code into libraries, all done on artifacts without source code available. I have one user in particular who successfully used it on a huge (7 MiB) program in a span of a couple of weeks, most of them spent fixing bugs on my end. They then proceeded to crank up the insanity to 11 by splitting that link-time optimized artifact into pieces and swapping parts out willy-nilly. That is theoretically impossible because LTO turns the entire program into a single translation unit subject to inter-procedure optimizations, so functions will exhibit nonstandard calling conventions you can't replicate with freshly built source code, but I guess anything's possible when you binary patch __usercall into MSVC. I should get back to it once work is less hectic. Delinking is an exquisitely intricate puzzle to solve algorithmically and I've managed to make that esoteric but powerful reverse-engineering technique not only scalable to megabytes worth of code, but also within reach of everyday reverse-engineers.
- PerryStyle 2y agoWow this one of the most interesting things I’ve come across. Definitely could learn a lot by tinkering with this. Thanks!
- dgellow 2y agoThat’s insanely impressive. Your blog is a gold mine, thank you for sharing
- boricj 2y agoI've been meaning to write more articles and not just about delinking or reverse-engineering. I've been hoarding some lower-grade cursed stuff, like a git proxy/cache written in a hundred lines of bash, or a Jenkins plugin that exposes a Debian source repository as a SCM provider and enables one to trivially create a Debian package build server with it (complete with dependency management). Guess I'd better unbrick my Chromebook first. I still don't know how changing its battery managed to bork an unmodded, non-developer mode Chromebook so thoroughly that the official recovery claims it doesn't know what model it is ("no defined device identities matched this device.").
- 2OEH8eoCRo0 2y agoFascinating. I like it! Thanks for sharing.
- fallat 2y agoWhat a neat idea. We could use emulators to stitch together code from binaries compiled for particular CPUs.
- gblargg 2y agoI did this once years ago (on Mac OS classic). I had an mp3 player I liked for its low CPU usage, but didn't like the UI. I figured out the entry points for the mp3 decoding routines and turned it basically into a library to call from my own UI. Virtual machines/emulators are one extreme, recreating the environment it ran in so no human examination of the particular program is necessary. The approach you describe is at the other end, using bits of programs directly in other code to do basically black-box functions that aren't worth figuring out and coding properly.
- disqard 2y agoYou've tickled more than one person's brain here :D Please consider writing up something about this binary mashup toolkit. You have taken an unusual path and shown how far one can go... it's worthy of sharing more widely. Good luck!
- boricj 2y agoThe only unusual part about my toolkit is the delinker. Since it outputs relocatable object files, you can then make use of them with conventional tooling. I've mostly used a linker to build new programs with the parts I've exported. Some of my users use objdiff to compare the output of their decompilation efforts against recreated object files. Others use objcopy or homemade tools to further modify the object files (mostly tweaking the symbol table) prior to reuse. One can generate assembly listings with objdump, although Ghidra already gives you that. Ironically, the hardest part about using my delinker I think (besides figuring out errors when things go wrong) is just realizing and imagining what you can do with it. It's rather counter-intuitive at first because this goes against anything one might learn in CS 101, but once you get it then you can do all kinds of heretical things with it. To take this to the next level, beyond adding support for more ISAs and object file exporters, I'd need to generate debugging symbols in order to make debugging the pilfered code a less painful experience. There are plenty of ways delinking can go wrong if the Ghidra database is incorrect or incomplete and troubleshooting undefined behavior from outer space can get tedious. But in the context of a post-collapse scenario, what one would be really after is some kind of linker that can stitch together a working program from just about any random bunch of object files, regardless of ABI and ISA. My experiments on cross-delinking remained rather tame (same ISA, similar ABI) because gluing all that mess by hand gets really complicated really fast.
- Ericson2314 2y agoI worked on one of those too: https://github.com/cncNet/petool https://github.com/cncNet/petool
- boricj 2y agoNeat! Looks like the closest equivalent in your tooling would be pe2obj, which repacks a PE executable as a COFF object file. Unless I'm mistaken, it doesn't seem to do anything in particular w.r.t. relocations, which is the tricky part about delinking. My educated guess it that repacking might be enough for purely self-contained position-independent code and data, but anything that contains an absolute address will lead to dangling references to the original program's address space. My tooling recovers relocatable section bytes and recreates relocation tables in order to export object files that are actually relocatable, regardless of their contents.
- Ericson2314 2y agoIt's been a long time, but we manually annotated where the relations should be, and other edits, with the patch sections.
- bitwize 2y agoThis is a technique used in "living off the land" in infosec circles. An attacker might not have root privileges, but there might be a bit of code in this program that does one thing with escalated privilege, and a bit of code in that program that does another thing with escalated privilege, and if you have enough of these you can stitch together a "gadget", a series of jumps or calls to these bits of privileged code, to get a shell or do what you want.
- boricj 2y agoThis is a bit different. In your case, you're reusing code and data laying around in a running process to do your bidding from within. Here, I'm actually stealing code and data from one or more executables and turning these back into object files for further use. Think Doctor Frankenstein, but with program parts instead of human parts. I only operate on dead patients in a manner of speaking, but you could delink stuff from a running process too I suppose. I don't think it would be useful in the context of return-oriented programming, since all the code you can work with is already loaded in memory, there's no need to delink it back to relocatable code first.