Skip to content

Commit 9937d4f

Browse files
committed
Merge remote-tracking branch 'origin/main'
2 parents 11e1a94 + 9ccb895 commit 9937d4f

File tree

11 files changed

+190
-12
lines changed

11 files changed

+190
-12
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,17 @@
66
[![Docs.rs](https://docs.rs/libmwemu/badge.svg)](https://docs.rs/libmwemu)
77
[![codecov](https://codecov.io/gh/sha0coder/mwemu/branch/main/graph/badge.svg)](https://codecov.io/gh/sha0coder/mwemu)
88

9+
## Official documentation
10+
11+
I'm working in the official documentation, note that it isn't finished yet.
12+
13+
[mwemu.github.io](https://mwemu.github.io)
914

1015
## What is this?
1116

1217
It's a hardware emulator + OS process simulator implemented in pure rust.
1318

14-
This approach is very conviniento to malware analysis and other stuff (PE, shellcode etc)
19+
This approach is very convinien to malware analysis and other stuff (PE, shellcode etc)
1520

1621
The OS is mainly windows, it emulates a windows process, some very basic support for linux.
1722

@@ -20,6 +25,7 @@ The hardware is x86 32/64bits it's fast and reliable.
2025

2126
![MWEMU Logo](./docs/pics/mwemu_logo.png)
2227

28+
2329
## Three ways of using the software
2430

2531
- mwemu commandline https://github.com/sha0coder/mwemu

crates/libmwemu/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "libmwemu"
3-
version = "0.22.2"
3+
version = "0.23.0"
44
edition = "2018"
55
authors = ["sha0coder"]
66
license = "MIT"

crates/libmwemu/src/emu/execution.rs

Lines changed: 93 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ impl Emu {
2424

2525
/// Call a 32bits function at addr, passing argument in an array of u64 but will cast to u32.
2626
/// The calling convention is stack, like winapi32.
27-
pub fn call32(&mut self, addr: u64, args: &[u64]) -> Result<u32, MwemuError> {
28-
//TODO: why this was u64?
27+
pub fn call32(&mut self, addr: u64, args: &[u32]) -> Result<u32, MwemuError> {
2928
if addr == self.regs().get_eip() {
3029
if addr == 0 {
3130
return Err(MwemuError::new(
@@ -37,7 +36,7 @@ impl Emu {
3736
}
3837
let orig_stack = self.regs().get_esp();
3938
for arg in args.iter().rev() {
40-
self.stack_push32(*arg as u32);
39+
self.stack_push32(*arg);
4140
}
4241
let ret_addr = self.regs().get_eip();
4342
self.stack_push32(ret_addr as u32);
@@ -47,9 +46,9 @@ impl Emu {
4746
Ok(self.regs().get_eax() as u32)
4847
}
4948

50-
/// Call a 64bits function at addr, passing argument in an array of u64.
51-
/// The calling convention is registers rcx/rdx/48/r9 and then stack. Like windows64.
52-
/// Dont use for linux64 syscall like convention, on those cases craft stack/register manually.
49+
/// Call 64bits function at addr using Microsoft x64 ABI, passing argument in an array of u64.
50+
/// The calling convention is registers rcx/rdx/r8/r9 and then stack. Like windows64.
51+
/// Dont use for linux64 syscall like convention, for this is linux_call64()
5352
pub fn call64(&mut self, addr: u64, args: &[u64]) -> Result<u64, MwemuError> {
5453
if addr == self.regs().rip {
5554
if addr == 0 {
@@ -74,21 +73,109 @@ impl Emu {
7473
if n >= 4 {
7574
self.regs_mut().r9 = args[3];
7675
}
76+
77+
// stack pointer backup, for restoring when function returns.
7778
let orig_stack = self.regs().rsp;
79+
80+
// padding
81+
let extra_args = if n > 4 { (n - 4) * 8 } else { 0 };
82+
let total = extra_args + 32 + 8;
83+
let padding = (16 - (self.regs().rsp as usize + total) % 16) % 16;
84+
self.regs_mut().rsp -= padding as u64;
85+
86+
// shadow space (32bits)
87+
for _ in 0..4 {
88+
self.stack_push64(0);
89+
}
90+
91+
// stack parameters
7892
if n > 4 {
7993
for arg in args.iter().skip(4).rev() {
8094
self.stack_push64(*arg);
8195
}
8296
}
8397

98+
// return address
99+
let ret_addr = self.regs().rip;
100+
self.stack_push64(ret_addr);
101+
102+
// trigger function
103+
self.regs_mut().rip = addr;
104+
105+
// emulate the function until return address is reached
106+
self.run(Some(ret_addr))?;
107+
108+
// recover stack and return rax
109+
self.regs_mut().rsp = orig_stack;
110+
Ok(self.regs().rax)
111+
}
112+
113+
114+
/// Call a 64bits function at addr, passing arguments in an array of u64.
115+
/// The calling convention is registers RDI, RSI, RDX, RCX, R8, R9 and then stack. Like linux64.
116+
pub fn linux_call64(&mut self, addr: u64, args: &[u64]) -> Result<u64, MwemuError> {
117+
if addr == self.regs().rip {
118+
if addr == 0 {
119+
return Err(MwemuError::new(
120+
"return address reached after starting the call64, change rip.",
121+
));
122+
} else {
123+
self.regs_mut().rip = 0;
124+
}
125+
}
126+
127+
let n = args.len();
128+
if n >= 1 {
129+
self.regs_mut().rdi = args[0];
130+
}
131+
if n >= 2 {
132+
self.regs_mut().rsi = args[1];
133+
}
134+
if n >= 3 {
135+
self.regs_mut().rdx = args[2];
136+
}
137+
if n >= 4 {
138+
self.regs_mut().rcx = args[3];
139+
}
140+
if n >= 5 {
141+
self.regs_mut().r8 = args[4];
142+
}
143+
if n >= 6 {
144+
self.regs_mut().r9 = args[5];
145+
}
146+
147+
// stack pointer backup, for restoring when function returns.
148+
let orig_stack = self.regs().rsp;
149+
150+
// padding
151+
let extra_args = if n > 6 { (n - 6) * 8 } else { 0 };
152+
let total = extra_args + 8;
153+
let padding = (16 - (self.regs().rsp as usize + total) % 16) % 16;
154+
self.regs_mut().rsp -= padding as u64;
155+
156+
// stack parameters
157+
if n > 6 {
158+
for arg in args.iter().skip(6).rev() {
159+
self.stack_push64(*arg);
160+
}
161+
}
162+
163+
// return address
84164
let ret_addr = self.regs().rip;
85165
self.stack_push64(ret_addr);
166+
167+
// trigger function
86168
self.regs_mut().rip = addr;
169+
170+
// emulate the function until return address is reached
87171
self.run(Some(ret_addr))?;
172+
173+
// recover stack and return rax
88174
self.regs_mut().rsp = orig_stack;
89175
Ok(self.regs().rax)
90176
}
91177

178+
92179
/// Start emulation until a ret instruction is found.
93180
/// It will return the address or MwemuError.
94181
#[inline]

crates/libmwemu/src/emu/initialization.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,10 @@ impl Emu {
227227
.unwrap();
228228
}
229229

230+
230231
/// Initialize windows simulator, this does like init_cpu() but also setup the windows memory.
232+
/// This require having the map files in place, otherwise use just init_cpu() but emu32() and
233+
/// emu64() already call init_cpu()
231234
/// This is called from load_code if the code is a PE or shellcode.
232235
/// load_code_bytes() and other loading ways don't call this, if you need windows simulation call this.
233236
pub fn init(&mut self, clear_registers: bool, clear_flags: bool) {
@@ -381,6 +384,8 @@ impl Emu {
381384
pub fn init_mem32(&mut self) {
382385
log::info!("loading memory maps");
383386

387+
self.maps.is_64bits = false;
388+
384389
let orig_path = std::env::current_dir().unwrap();
385390
std::env::set_current_dir(self.cfg.maps_folder.clone());
386391

@@ -487,6 +492,7 @@ impl Emu {
487492
/// This is called from init(), this setup the 64bits windows memory simulation.
488493
pub fn init_mem64(&mut self) {
489494
log::info!("loading memory maps");
495+
self.maps.is_64bits = true;
490496

491497
/*
492498
let orig_path = std::env::current_dir().unwrap();
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
use crate::tests::helpers;
2+
use crate::*;
3+
4+
#[test]
5+
// this tests the emu.call32()
6+
pub fn call32() {
7+
helpers::setup();
8+
9+
let mut emu = emu32();
10+
let opcodes: Vec<u8> = vec![ //TODO: test it with 7 parameters
11+
0x55, 0x89, 0xe5, 0x83, 0xec, 0x50, 0xb8, 0x37, 0x13, 0x00, 0x00, 0x83, 0xf0, 0x7b, 0xc9, 0xc3
12+
];
13+
emu.set_verbose(3);
14+
emu.linux = true; // otherwise I would need to set map files.
15+
emu.load_code_bytes(&opcodes);
16+
emu.regs_mut().rax = 0;
17+
let eax = emu.call32(emu.regs().rip, &[]).unwrap();
18+
assert_eq!(emu.regs().get_eax() as u32, eax);
19+
assert_eq!(eax, 0x134c);
20+
assert_eq!(emu.regs().rax, 0x134c);
21+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
use crate::tests::helpers;
2+
use crate::*;
3+
4+
#[test]
5+
// this tests the emu.call64() Microsoft ABI
6+
pub fn call64() {
7+
helpers::setup();
8+
9+
let mut emu = emu64();
10+
let opcodes: Vec<u8> = vec![
11+
0x55, 0x48, 0x89, 0xe5, 0x89, 0x7d, 0xfc, 0x89,
12+
0x75, 0xf8, 0x8b, 0x55, 0xfc, 0x8b, 0x45, 0xf8,
13+
0x01, 0xd0, 0x5d, 0xc3,
14+
];
15+
emu.set_verbose(3);
16+
emu.linux = true; // otherwise I would need to set map files.
17+
emu.load_code_bytes(&opcodes);
18+
emu.regs_mut().rax = 0;
19+
let rax = emu.call64(emu.regs().rip, &[]).unwrap();
20+
assert_eq!(emu.regs().rip, 0x3c0013);
21+
// TODO: improve this test with something with microsoft ABI and more than 4 params.
22+
//assert_eq!(rax, 0x134c);
23+
//assert_eq!(emu.regs().rax, 0x134c);
24+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
use crate::tests::helpers;
2+
use crate::*;
3+
4+
#[test]
5+
// this tests the emu.linux_call64() ARM ABI (used in linux calls)
6+
pub fn linux_call64() {
7+
helpers::setup();
8+
9+
/*
10+
int test(int p1, int p2, int p3, int p4, int p5, int p6, int p7) {
11+
return p1+p2+p3+p4+p5+p6+p7;
12+
}
13+
*/
14+
15+
let mut emu = emu64();
16+
let opcodes: Vec<u8> = vec![
17+
0x55, 0x48, 0x89, 0xe5, 0x89, 0x7d, 0xfc, 0x89, 0x75, 0xf8, 0x89, 0x55,
18+
0xf4, 0x89, 0x4d, 0xf0, 0x44, 0x89, 0x45, 0xec, 0x44, 0x89, 0x4d, 0xe8,
19+
0x8b, 0x55, 0xfc, 0x8b, 0x45, 0xf8, 0x01, 0xc2, 0x8b, 0x45, 0xf4, 0x01,
20+
0xc2, 0x8b, 0x45, 0xf0, 0x01, 0xc2, 0x8b, 0x45, 0xec, 0x01, 0xc2, 0x8b,
21+
0x45, 0xe8, 0x01, 0xc2, 0x8b, 0x45, 0x10, 0x01, 0xd0, 0x5d, 0xc3
22+
];
23+
emu.set_verbose(0);
24+
emu.linux = true;
25+
emu.load_code_bytes(&opcodes);
26+
emu.regs_mut().rax = 0;
27+
let rax = emu.linux_call64(emu.regs().rip, &[1,2,3,4,5,6,7]).unwrap();
28+
assert_eq!(rax, emu.regs().rax);
29+
assert_eq!(emu.regs().rax, 1+2+3+4+5+6+7);
30+
}

crates/libmwemu/src/tests/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,6 @@ mod stress_shl2p_all;
4545
mod stress_shl2p_trigger;
4646
mod stress_shr2p_all;
4747
mod test_unified_step_and_run_methods;
48+
mod call32;
49+
mod call64;
50+
mod linux_call64;

crates/mwemu/src/main.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,10 @@ fn main() {
9191
.arg(clap_arg!("flags", "", "flags", "trace the flags hex value in every instruction."))
9292
.arg(clap_arg!("maps", "M", "maps", "select the memory maps folder", "PATH"))
9393
.arg(clap_arg!("trace_registers", "r", "trace_registers", "print the register values in every step."))
94-
.arg(clap_arg!("trace_register", "R", "trace_register", "trace a specific register in every step, value and content", "REGISTER1,REGISTER2"))
94+
.arg(clap_arg!("register", "R", "trace_register", "trace a specific register in every step, value and content", "REGISTER1,REGISTER2"))
9595
.arg(clap_arg!("console", "c", "console", "select in which moment will spawn the console to inspect.", "NUMBER"))
9696
.arg(clap_arg!("loops", "l", "loops", "show loop interations, it is slow."))
97-
.arg(clap_arg!("nocolors", "n", "nocolors", "print without colors for redirectin to a file >out"))
97+
.arg(clap_arg!("nocolors", "n", "nocolors", "print without colors for redirecting to a file >out"))
9898
.arg(clap_arg!("string", "s", "string", "monitor string on a specific address", "ADDRESS"))
9999
.arg(clap_arg!("inspect", "i", "inspect", "monitor memory like: -i 'dword ptr [ebp + 0x24]", "DIRECTION"))
100100
//.arg(clap_arg!("endpoint", "e", "endpoint", "perform communications with the endpoint, use tor or vpn!"))
@@ -188,6 +188,7 @@ fn main() {
188188
.to_string();
189189
emu.cfg.reg_names = regs.split(',').map(|x| x.to_string()).collect();
190190
}
191+
191192
if matches.is_present("string") {
192193
emu.cfg.trace_string = true;
193194
emu.cfg.string_addr = u64::from_str_radix(

0 commit comments

Comments
 (0)