Disassembler for Zeus VM custom instruction set with a Binary Ninja plugin.
This is just a demo to learn more about reverse engineering virtual machines and Binary Ninja architecture plugin development. Our goal is to build a disassembler for the custom Zeus VM instruction set and disassemble the VM code then lift it in Binary Ninja to reveal the custom algorithm used to protect their configuration file.
The Zeus VM uses a custom set of XOR keys in the instruction set that is updated with each build. Because of this our disassembler will only work with a single version of the malware f792997cb36a477fa55102ad6b680c97e3517b2e63c83c802bf8d57ae9ed525e
[Download].
The full analysis and development process is being streamed on Twitch and the VODs are available on the OALABS Patreon.
Notes from our streams are also available here.
For the Binary Ninja Plugin we loosely followed the binja Architecture Plugin guides and had a lot of help from @xusheng6.
The following references were also helpful.
- LEVERAGING BINARY NINJA IL TO REVERSE A CUSTOM ISA: CRACKING THE “POT OF GOLD” 37C3
- Untangling Exotic Architectures with Binary Ninja Supplementing Flare-On 2017 with some sanity
- Lifting VM based obfuscators in Binary Ninja
- Breaking Down Binary Ninja’s Low Level IL
For our architecture we had two unique instructions, rc4
and shuffle
both of which don't have a good representation in the binja IL. To represent these we used intrinsics
from binaryninja.architecture import IntrinsicInfo
from binaryninja.types import Type
intrinsics = {'rc4': IntrinsicInfo(inputs=[], outputs=[], index=1),
'shuffle': IntrinsicInfo(inputs=[Type.int(4, False), Type.int(1, False)], outputs=[], index=2)
Loops are also interesting as they require an expression that branches. The code is straight forward but one thing to keep in mind is that each expression in the IL has a label an labels are used for branching (instead of addresses).
il.append(il.set_reg(4,
'loop_counter',
il.sub(4, il.reg(4, 'loop_counter'), il.const(4, 1))))
condition = il.compare_not_equal(4, il.reg(4, 'loop_counter'), il.const(4, 0))
t = il.get_label_for_address(Architecture['ZVM'], addr + instr.size - op2.value)
f = il.get_label_for_address(Architecture['ZVM'], addr + instr.size)
# here we just think t and f are both valid, and take the easy route
il.append(il.if_expr(condition, t, f))
Because this project is meant to be a community effort on stream we won’t be accepting PRs. Aside from some maintenance/cleanup all coding will be done on-stream. If you have feature requests or suggestions leave your feedback as an Issue or come chat with us on Discord.
💖 Check out our schedule we stream Sundays at 1300 EST