4 ms·
Author here. Usercorn at a basic level is like qemu-user in that it can load a userspace binary like ELF, MachO, etc into a CPU emulator and provide a syscall e
by lunixbochs 11y ago
Author here. Usercorn at a basic level is like qemu-user in that it can load a userspace binary like ELF, MachO, etc into a CPU emulator and provide a syscall emulator interface.
It's built as an API that allows instrumentation of the resulting binary. You can hook instructions, basic blocks, syscalls, memory access... You get information like stacktraces in a cross-platform manner. There are helpers to compute and display diffs of memory and registers throughout execution.
There are many differences from qemu-user:
--
Usercorn attempts to emulate several OS/syscall ABIs all from one binary (Linux, BSD, XNU, CGC, more to come like Redox, maybe weird stuff like Windows and DOS). qemu-user just translates arguments to your host kernel so to run a Linux binary you need a Linux host. qemu-user also has a different binary for each arch, like qemu-i386 and qemu-arm. The primary usercorn binary can emulate every single supported OS/arch combination and aims automatically detect the correct arch/kernel when you run a program.
Usercorn is being built as a framework around the Unicorn Engine. It's not just a way to run a binary on the command line. It can be loaded as a library (`NewUsercorn("binary").Run(args, env)` will run an arbitrary supported app). It allows hooking many things and I've been building a lot of features around static trace/analysis. I'm extremely excited about some of the features and supporting tools I have planned.
This twitter thread shows examples of a tool called imgtrace I built with Usercorn. imgtrace can visualize memory writes done by a program. The Hilbert curve view of MD5 is nice, as it makes the memory containing the internal MD5 state as well as the iterators very obvious at a glance: https://twitter.com/lunixbochs/status/717945725024280578 https://twitter.com/lunixbochs/status/717945725024280578
The underlying code of imgtrace is also extremely simple (basically all of it is to create the image data), which shows off the power of Usercorn's simplicity: https://github.com/lunixbochs/usercorn/tree/master/go/cmd/imgtrace https://github.com/lunixbochs/usercorn/tree/master/go/cmd/im...
Usercorn is much less mature than qemu-user. It supports around ~50/400 Posix syscalls and is missing many architecture-specific features. Some architectures still need work on memory segmentation and thread-local storage. I've split up functionality internally in a way that makes sense, but I'm still building the base functionality and haven't designed the public API at all yet (even though it's possible to use Usercorn from external code, that's just happenstance due to the functions/types I exposed while building it).
----
Architecture support is tied to Unicorn. Usercorn supports x86_64 best, and has various levels of support for ARM, MIPS, sparc, and m68k.
It's still very much WIP, but can run many binaries at this point, even Linux binaries dynamically linked to glibc. Host support is best on OS X and Linux. Guest support is best on Linux.
- koolba 11y agoWow this is seriously cool. Took a while of going through it and the links to Unicorn to understand what it was (your top level comment wasn't up yet). I think this snippet from your Examples section sums it up best: usercorn bins/x86.linux.elf usercorn bins/x86_64.linux.elf usercorn bins/x86.darwin.macho usercorn bins/x86_64.darwin.macho usercorn bins/x86.linux.cgc usercorn bins/mipsel.linux.elf
- lunixbochs 11y agoThat only explains the qemu-user part. The ability to run binaries in Usercorn is just a means to an end - I want to build tools in the vein of `maybe` (VFS layer will make this easy, and so much safer), `imgtrace`, `strace`, and some stuff that's 1000x more ambitious than these. Running binaries is just... I'm recreating the universe so I can change just a few things, like logging memory access or doing taint analysis. This is similar to tools like DynamoRIO, DrMemory, PIN, valgrind, but it not only covers all of those spaces, I could probably spend a few hours listing interesting stuff outside those scopes I want to build on Usercorn (for example, you could ignore the CPU emulation part of of Usercorn and use just the kernel emulation interface to implement the Linuxulator or Windows10+Linux compat layer natively for an OS). I'll probably write real docs sometime after a proper API, but both are lower priority than core features atm.
- pm215 11y agoMy rough view of emulating userspace and the syscall ABI is that the three main tricky parts are (1) signals (2) threads (3) mmap and generally handling the guest address space; most of the rest is just a big pile of fiddly details. How does Usercorn do there currently? (Among other things there are some pretty tricky race conditions to avoid between signals and syscalls to make sure you return to the guest and don't acidentally block forever in a syscall.) (I work on qemu, including its linux-user stuff...)
- lunixbochs 11y agoPosix mmap is generally working, because that's easily mapped to a core API in Unicorn. I exposed the qemu mem_map_ptr API for mapping files as well, but I haven't integrated it yet (I emulate file mmap for now by copying the file into guest memory). Signals are a pretty high priority, but are currently unimplemented. Usercorn has a trampoline mode that allows stopping the CPU, executing a section of code, and returning to the previous location when it's done. I can use this for signal dispatch. sigaction/signal syscalls will just register the address to call. fork/clone/threads in general are not yet implemented, but the groundwork is mostly there - I'm mainly worried about memory synchronization, and might need to add APIs to Unicorn for this. Unicorn supports running an arbitrary number of CPUs in parallel in the same process, so I'm actually planning on making both forked processes and threads run in the same host process, and do a sort of PID namespace/transparent layer. This will make stuff like emulating IPC or a process ptracing another process much easier to control, and I won't need an external process like wineserver to negotiate stuff the native system doesn't know how to do.