Introduction

Welcome to official MWEMU documentation, you can do scrolling down or using the direct links that are on the left.

Github repo: https://github.com/sha0coder/mwemu

What is MWEMU?

MWEMU is a hardware emulator and Windows process simulator written in pure Rust from scratch, It was created by @sha0coder and It's open source. The contributors of this software have improved the quality of the project a lot, check to know them.

The implemented hardware is x86/x64, unlike other emulators also implemt some parts of the OS (mainly windows) because asm code soon or later is going to do system calls (WinAPI, linux syscalls, etc).

It resulted extremely useful for malware deobfuscation, but this don't replace reversing, I't needed a previous reversing to prepare well the emulation initial state, and emulate only small group of functions, like decryption, keygen etc. In some specific cases mwemu can do full-emulation, wihth simple packers, encoded shellcodes, etc.

The emulation and simulation is implemented from scratch, but the awesome Rust library iced-x86 is used to the translation from a bunch of bytes to instruction objects. I implemented more than 300 x86 instructions, flags, exceptions, some loaders PE32/PE64/ELF64/shellcode32/shellcode64 and many other stuff.

MWEMU is blazing fast and also is memory-safe thanks to the Rust magic compiler.

The 3 ways of using MWEMU

1. The first way is commandline, see commandline for more details, this is for trying full-emulation.

2. Second way is creating python scripts with pymwemu module, can be installed with pip or git.

pypi: https://pypi.org/project/pymwemu/

If you need features or bug-fixes implemented recently use the git, will need to compile the full project and then the python bindings with maturin, more details in this section pymwemu instalation

3. Third way is using the rust crate published in crates.io https://crates.io/crates/libmwemu from a rust application.

Architectures

You can run MWEMU from Windows, Linux and Mac (x86 and also m1)

But only can be emulated x86 code (32bits and 64bits) mainly for Windows, there is linux shellcode support even syscalls are implemented but regarding ELF only 64bits, only static compiled and quite basic support for now, but no problem with shellcodes.

libc can be emulated well despite it is plenty of ymm instructions, but the linker cannot be emulated totally so far, my plan is fully emulating the linker, in this case I don't need to implement the hole linking process (.got and .plt creation etc)

A bit of internals

For now, just a basic overview of the internals.

In the past I named the project SCEMU, and was stored in 3 separated repos for mwemu (the commandline) pymwemu (pyhon module) and (libmwemu) the engine where all is implemented and also it's the Rust Crate module on crates.io.

Then it was renamed to MWEMU, because SCEMU is more specific to shellcode and because it's an italian bad word.

So, now it's one repo with a crates/ folder with the 3 crates.

The tests are implemented on crates/libmwemu/src/tests/ and are described lately.

Most of files were splitted in small files.

Shell

              ~/s/mwemu ❯❯❯ ls crates/libmwemu/src/                                main ⬆ ✱
banzai.rs       emu/               kuser_shared.rs  syscall/
breakpoint.rs   emu_context.rs     lib.rs           tests/
colors.rs       engine/            macros.rs        thread_context.rs
config.rs       err.rs             maps/            threading.rs
console.rs      exception.rs       ntapi/           tools/
constants.rs    exception_type.rs  pe/              tracing.rs
context/        flags.rs           peb/             utils.rs
crit_state.rs   fpu/               regs64.rs        winapi/
definitions.rs  fpu.rs             script.rs
eflags.rs       global_locks.rs    serialization/
elf/            hooks.rs           structures/
              

emu/ contains emu methods and sub-bojects that are involved in the emulation itself.

In engine/ there are the implementation of all the instructions.

winapi/ contains the WinAPI implemntations divided in winapi32/ and winapi64/

there are other thigngs like contants.rs structures/ etc.

Test System

For triggering locally use make tests this downloads some binaries and launch the test system cargo test

Don't use --release, always is more convinient do the tests without aplying the optimizations, that could ignore some type of errors. Actually the github CI is configured to do cargo test and also cargo test --release to check both modes.

Every git push or pull-request will trigger the CI in gibhut to perform all the tests in Linux, Windows and Mac. In in the case of PR is mandatory, in the case of a git push is only informative.

PR also triggers a coverate analysis of the tests, which is currently only 32%

Project Contributtors

Brandon Ros

Archeron2302

ElCapor

There are also other people that suggested intereting ideas and optimizations.

Regarding wit00 it's a github glitch for git pushing having a bad configuration in git config. (the bug was reported to github)

I'm @sha0coder and I created this software to empower my reversing engineering works, and I'm sharing this because I think it's useful for some cases.

Some graphs:https://github.com/sha0coder/mwemu/graphs/contributors

Videos and pics

radare2 integration on mwemu - r2con2025

my youtube channel, there are some demos

zloader emulation - NcNLabs2022

Fixing FPU emulation

License

Actually there are several licenses, the source code is GPLv3, but the crates.io rust module and pypi python module are MIT for having less restrictions in distributing software using libmwemu or pymwemu.

https://github.com/sha0coder/mwemu/blob/main/LICENSE

Don't hesitate in contact me for creating technologies based of this software.

email: sha0 at badchecksum dot net

MWEMU commandline tool

The commandline is a quick way of using mwemu, and there are many features like register/memory/call/string tracing or capturing emulation moments.

If the packer is simple probably can be full-emulated using the commandline tool, but if you need more control use pymwemu and for total control libmwemu.

MWEMU Installation

1. First you need to install Rust and Cargo, and the best way is using rustup.

https://rustup.rs/

For instance in linux or mac:

Shell

                curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
              

In the default setup binaries are in ~/.cargo/bin/ but you need to add this to the path, ideally on last section of the path.

Shell

                export PATH="$PATH:/home/username/.cargo/bin/"
              

The installer says how to put in the bashrc or other shells rc.

2. Then there are 2 options to install this

--help

Use help option for viewing the commandline options, note that the before the "--" there are cargo flags and after the "--" are the parameters of the program, in this case mwemu commandline.

Shell

              ~/s/mwemu ❯❯❯ cargo run --release -- --help                                                                                                        Finished `release` profile [optimized] target(s) in 0.06s
Running `target/release/mwemu --help`
MWEMU emulator for malware 0.7.12
@sha0coder

USAGE:
    mwemu [FLAGS] [OPTIONS]

FLAGS:
    -6, --64bits             enable 64bits architecture emulation
        --banzai             skip unimplemented instructions, and keep up emulating what can be emulated
        --call               enable call tracer
        --entropy            display changes in the entropy
        --flags              trace the flags hex value in every instruction.
    -F, --fpu                trace the fpu states.
    -h, --handle             handle Ctrl+C to spawn console
        --help               Prints help information
    -l, --loops              show loop interations, it is slow.
        --multithread        enable multithread emulation
    -n, --nocolors           print without colors for redirecting to a file >out
    -p, --stack_trace        trace stack on push/pop
    -t, --test               test mode
    -m, --trace_memory       trace all the memory accesses read and write.
    -r, --trace_registers    print the register values in every step.
        --version            Prints version information
    -v, --verbose            -vv for view the assembly, -v only messages, without verbose only see the api calls and
                             goes faster

OPTIONS:
    -A, --args                              provide arguments to the EXE like: --args '"aa" "bb"'
        --cmd                            launch a console command
    -b, --base <ADDRESS>                          set base address for code
    -c, --console                         select in which moment will spawn the console to inspect.
    -C, --console_addr <ADDRESS>                  spawn console on first eip = address
    -d, --dump                              load from dump.
    -a, --entry <ADDRESS>                         entry point of the shellcode, by default starts from the beginning.
    -e, --exit                          exit position of the shellcode
    -f, --filename                          set the shellcode binary file.
    -i, --inspect                      monitor memory like: -i 'dword ptr [ebp + 0x24]
    -L, --log <LOG_FILENAME>                      log output to file
    -M, --maps                              select the memory maps folder
        --mxcsr                            set mxcsr register
        --r10                                set r10 register
        --r11                                set r11 register
        --r12                                set r12 register
        --r13                                set r13 register
        --r14                                set r14 register
        --r15                                set r15 register
        --r8                                  set r8 register
        --r9                                  set r9 register
        --rax                                set rax register
        --rbp                                set rbp register
        --rbx                                set rbx register
        --rcx                                set rcx register
        --rdi                                set rdi register
        --rdx                                set rdx register
        --rflags                          set rflags register
        --rip                                set rip register
        --rsi                                set rsi register
        --rsp                                set rsp register
    -x, --script <SCRIPT>                         launch an emulation script, see scripts_examples folder
        --stack_address <ADDRESS>                 set stack address
    -s, --string <ADDRESS>                        monitor string on a specific address
    -T, --trace_filename          output trace to specified file
    -R, --trace_register     trace a specific register in every step, value and content
    -S, --trace_start                start trace at specified position
    -V, --verbose_at 
            start displaying assembly at specific position (is like -vv enabled in specific moment)
              

Full Emulation

Main options are:

Shell

               ❯❯❯ cargo run --release -- -6 -f test/elf64lin_cpu_arithmetics1.bin -vv  
              

Capturing a moment

MWEMU displays always the number of emulated instructions, and this is a unique identificator of a moment.

The moment 1 is the first assembly instruction, if you add the flag -c 1 the emulator will stop before emulating instruction 1

Shell

              ~/s/mwemu ❯❯❯ cargo run --release -- -6 -f test/elf64lin_cpu_arithmetics1.bin -vv -c 1                                                           main ⬆ ✱
    Finished `release` profile [optimized] target(s) in 0.07s
     Running `target/release/mwemu -6 -f test/elf64lin_cpu_arithmetics1.bin -vv -c 1`
static elf64 detected.
Entry point pointing to .text 0x401000
-------
1 0x401000: inc   al
--- console ---
=>
              

The instruction 1 will not be colored, this means that that instruction is going to be emulated in next step.

This spawns the console in that emulator state, and you can press enters to emulate steps or "h" command for viewing options.

If verbose mode is not set, only WinAPI, syscalls etc will be displayed, but there will be also a number that represent the emulated instructions until that state.

There is the option of stopping the emulator at specific address with uppercase "-C addr" but note that the address can be triggered multiple times and is not an unique identificator like the -c moment

Verbosity

There are 4 verbosity levels:

Use -V or --verbose_at for enabling verbose mode at specific point.

The verbose mode is automatically enabled 100 instructions previous to the configured -c moment to stop.

Logging

It's possible to redirect the output to a file, ie:

Shell

              ❯❯❯ cargo run --release -- -6 -f test/elf64lin_cpu_arithmetics1.bin -vv -c 1 > /tmp/log
              

But note that colors are terminal escape bytes and will be logged making parsing more difficult, if you do cat /tmp/log you will see the colors but if you use an editor you will see those bytes

It is more convinient using --log option for a clean logs.

Shell

              ❯❯❯ cargo run --release -- -6 -f test/elf64lin_cpu_arithmetics1.bin -vv -c 1 --log /tmp/log
              

Initialize registers

There are some cases like emulating dlls or chunk of code that needs some initial values in the registers.

The commandline tool allows to set registers using those options:

Shell

        --mxcsr <MXCSR>                           set mxcsr register
        --r10 <R10>                               set r10 register
        --r11 <R11>                               set r11 register
        --r12 <R12>                               set r12 register
        --r13 <R13>                               set r13 register
        --r14 <R14>                               set r14 register
        --r15 <R15>                               set r15 register
        --r8 <R8>                                 set r8 register
        --r9 <R9>                                 set r9 register
        --rax <RAX>                               set rax register
        --rbp <RBP>                               set rbp register
        --rbx <RBX>                               set rbx register
        --rcx <RCX>                               set rcx register
        --rdi <RDI>                               set rdi register
        --rdx <RDX>                               set rdx register
        --rflags <RFLAGS>                         set rflags register
        --rip <RIP>                               set rip register
        --rsi <RSI>                               set rsi register
        --rsp <RSP>                               set rsp register
              

But note that colors are terminal escape bytes and will be logged making parsing more difficult, if you do cat /tmp/log you will see the colors but if you use an editor you will see those bytes

It is more convinient using --log option for a clean logs.

Shell

              ❯❯❯ cargo run --release -- -6 -f test/elf64lin_cpu_arithmetics1.bin -c 1 --rax 0x123 --rbx 0x1337
        Finished `release` profile [optimized] target(s) in 0.08s
        Running `target/release/mwemu -6 -f test/elf64lin_cpu_arithmetics1.bin -c 1 --rax 0x123 --rbx 0x1337`
        static elf64 detected.
        Entry point pointing to .text 0x401000
        --- console ---
        =>r rax
            rax: 0x123 291
        =>r rbx
            rbx: 0x1337 4919
              

Trace Registers

Use option -R <registers to trace> to trace some registers

Shell

               ❯❯❯ cargo run --release -- -6 -f test/elf64lin_cpu_arithmetics1.bin -vv -R rax,rsp
               ...
            57 0x4010a2: rcr   al,1
                58 rax: 0x82f61001 2197164033
                58 rsp: 0x7fffffffe270
            58 0x4010a4: rcr   ax,1
                59 rax: 0x82f60800 2197161984
                59 rsp: 0x7fffffffe270
            59 0x4010a7: rcr   eax,1
                60 rax: 0xc17b0400 3246064640
                60 rsp: 0x7fffffffe270
            60 0x4010a9: rcr   rax,1
                61 rax: 0x60bd8200 1623032320
                61 rsp: 0x7fffffffe270
            61 0x4010ac: rcr   al,cl
                62 rax: 0x60bd8200 1623032320
                62 rsp: 0x7fffffffe270
            62 0x4010ae: rcr   ax,cl
                63 rax: 0x60bd8200 1623032320
                63 rsp: 0x7fffffffe270
            63 0x4010b1: rcr   eax,cl
                64 rax: 0x60bd8200 1623032320
                64 rsp: 0x7fffffffe270
            64 0x4010b3: rcr   rax,cl
                65 rax: 0x60bd8200 1623032320
                65 rsp: 0x7fffffffe270
            65 0x4010b6: mov   eax,90909090h ; 0x90909090
                66 rax: 0x90909090 2425393296
                66 rsp: 0x7fffffffe270
            66 0x4010bb: mov   edx,90909090h ; 0x90909090
                67 rax: 0x90909090 2425393296
                67 rsp: 0x7fffffffe270
            67 0x4010c0: mov   ecx,0 ; 0x0
                68 rax: 0x90909090 2425393296
                68 rsp: 0x7fffffffe270
            68 0x4010c5: shrd  eax,edx,cl
                69 rax: 0x90909090 2425393296
                69 rsp: 0x7fffffffe270
            69 0x4010c8: ret ; ret-addr: 0x0 ret-value: 0x90909090
              

You can trace one or multiple registers at same time with -R option but with no spaces between registers.

  • trace memory