My main concern about the long list of caveats for writing memory is the
user will almost certainly cause weird things to happen which will then
be hard to debug. I can see the patcher example however it would be
useful to know what other practical uses this interface provides.
Of course! My main personal intent here is to facilitate introspection
and manipulation of guest state for security analysis. Some examples of
why the memory/register R/W primitives are necessary here include:
Fuzzing:
- Read registers and memory for tracing control flow, comparison
operands, and profiled values (e.g. memcmp arguments)
- Write memory to inject test cases into the guest (for me and other
fuzzer developers, this is the biggest reason!)
- Write registers to reset execution or skip over complex checks like
checksums
- Read and write memory, and read and write registers, to do basic
snapshot/restore by tracking dirty pages and resetting them
Virtual Machine Introspection (for malware analysis and reverse
engineering):
- Read memory and registers to find kernel, analyze kernel structures,
and retrieve info like process lists, memory mappings
- Read memory and registers to quickly trace malware execution in VM
- Write memory and registers to test behavior under various conditions,
like skipping into checks (motivating example: what happens if you skip
into the kill switch statement for WannaCry)
Runtime patching (as in the example):
- Writing memory to patch critical legacy code in production often can
no longer be built or patched via means other than by applying binary
patches (this is a real problem for e.g. the government, to the point
where DARPA ran a program
https://www.darpa.mil/research/programs/assured-micropatching to work on
it!)
- Writing registers to skip over broken code, redirect to patch code, etc.
Ultimately, the caveats boil down to "don't modify stuff that's touched
by currently executing code". I personally don't think that's
unreasonable (as long as it's in the doc-strings) because for any plugin
that needs to write memory, ensuring the write consistency is probably
the easiest problem to solve and people working in this space are used
to having way worse and jankier workarounds. These plugin functions make
life way easier for them. I have been in touch with 20+ people from
various companies and projects (including Microsoft, where I work, as
well as other big and small tech) all working on plugins that could be
better if this feature existed, so there is definitely a user-base and
appetite for it!
The last cool use-case is that this moves us a long way towards cleaning
up the large number of QEMU forks out there designed for RE and security
testing like QEMU-Nyx, qemuafl, symqemu, and many more. Instead of
maintaining forks of QEMU (many of these are based on 4.2.0 or older!)
folks can just maintain a plugin, which lets them take advantage of
updates and fixes without giant rebases. My goal is to kill these forks
and have these projects write small, maintainable plugins instead, and
the authors are on board :)