Turb0
Bits, bytes, and bad ideas

Multicall Binary Packer

Wed 28 Apr 2021 05:47:27 PM

Multicall binaries are cool. Packers are cool. I wanted to make a proof of concept packer that would pack multiple binaries into a multicall binary because it sounded cool squared. I also wanted to see if I could even do it and if I could think of any use for it.

Background

A multicall binary is a binary that changes behavior based on the name it is executed as. Busybox is probably the most common example of this. Busybox is a size optimized collection of userland tools and programs that exists as a single multicall binary. Depending on the name this binary is executed as, a different codepath is run. Symlinking /bin/busybox to a soft link called ls and executing it will invoke /bin/busybox with argv[0] as ls, which the busybox binary will see and then run only the codepath for ls. Having several different utilities in one binary like this allows for code sharing and reuse with less overhead and allows busybox to be even smaller.

A packer is a tool that packs a compiled binary into a new binary that runs the packed binary, usually transforming it in some way either for compression reasons or to avoid malware detection. This is commonly used for malicious purposes.

A multicall binary therefore would be a binary that has several existing binaries packed into it. It would choose which one to unpack and execute based on the name it is executed as, hence, multicall. It loses the practical purpose of code sharing from a traditional multicall binary because each unique call is its own independent compiled binary running only its own code. There isn't really a good use for this beyond it being a cool party trick and a way to take binaries already on a system and fuse them into one multicall binary.

My plan to implement a multicall binary was to write a nodejs script that ingested a list of paths of binaries on the system to pack into one multicall binary. The script would then copy over some C files with code to act as a multicall binary and generate header files that included the bytes of the binaries. These would then all be compiled into a binary that would unpack and execute different binaries based on what its name is.

Implementation

The source for my proof of concept multicall binary packer can be found here.

The script itself follows the following steps:

The template C files that get built into the multicall packed binary follow the following execution steps:

The C code relies heavily on abusing the preprocessor and including files in odd places with fixed names that get generated by the script. The key to all of this is the memfd_create call that allows for writing the unpacked selected binary into a file that only exists in memory and being able to execute it from the file descriptor.

Demo

A file will be created named "binary_list.txt" that includes the binaries to be packed into a single multicall binary.

The binary list

The script will then be called with the list file passed as an argument and it will build the multicall binary in the build/ folder.

The binary list

From there the mcall binary can be executed and it will do nothing. Creating a symlink for each packed binary inside will allow you to execute mcall with a different name, having it unpack and execute the selected binary.

The binary list

Copying the mcall binary to a binary named each thing would also work to have it unpack and execute the specified binary.

Conclusion

While it may not be useful, it is very much possible, and not very difficult to compress and pack entire binaries into one binary that unpacks and executes a packed binary based on the name it is executed as. This could be a cool party trick or interesting way to mess with a friend you are on a system with, but doesn't seem very practical or useful. It was a cool thing to figure out how to get working and I'm pretty happy with the end result. It's cool to see the things you can do that there really is no reason to do and punch around code until you get it working. Now I can pack any binary into anything and slip in some extra binaries if I want, as limitedly useful as this is.