libmwemu 0.24.1

x86 32/64bits and system internals emulator, for securely emulating malware and other stuff.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456

use crate::*;
const DATA_ADDR: u64 = 0x7000;

// POP - Pop Value from Stack
// Loads value from RSP into destination, then increments RSP

// Basic POP register (64-bit)
#[test]
fn test_pop_rax() {
    let code = [
        0x50, // PUSH RAX (put value on stack)
        0x48, 0xc7, 0xc0, 0x00, 0x00, 0x00, 0x00, // MOV RAX, 0 (clear RAX)
        0x58, // POP RAX (restore from stack)
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.regs_mut().rax = 0x1234567890ABCDEF;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rax, 0x1234567890ABCDEF, "RAX restored from stack");
    assert_eq!(emu.regs().rsp, 0x1000, "RSP back to original");
}

// POP different registers
#[test]
fn test_pop_rbx() {
    let code = [
        0x48, 0xc7, 0xc3, 0x11, 0x22, 0x33, 0x44, // MOV RBX, 0x44332211
        0x53, // PUSH RBX
        0x48, 0x31, 0xdb, // XOR RBX, RBX (clear)
        0x5b, // POP RBX
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rbx, 0x44332211, "RBX restored");
}

#[test]
fn test_pop_rcx() {
    let code = [0x51, 0x59, 0xf4]; // PUSH RCX, POP RCX, HLT
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.regs_mut().rcx = 0xAAAAAAAABBBBBBBB;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rcx, 0xAAAAAAAABBBBBBBB);
}

// POP all general purpose registers
#[test]
fn test_pop_all_gp_registers() {
    let code = [
        0x50, 0x53, 0x51, 0x52, 0x56, 0x57, 0x55, // PUSH all
        0x5d, 0x5f, 0x5e, 0x5a, 0x59, 0x5b, 0x58, // POP all (reverse order)
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.regs_mut().rax = 0x1111111111111111;
    emu.regs_mut().rbx = 0x2222222222222222;
    emu.regs_mut().rcx = 0x3333333333333333;
    emu.regs_mut().rdx = 0x4444444444444444;
    emu.regs_mut().rsi = 0x5555555555555555;
    emu.regs_mut().rdi = 0x6666666666666666;
    emu.regs_mut().rbp = 0x7777777777777777;
    emu.run(None).unwrap();

    assert_eq!(emu.regs().rbp, 0x7777777777777777, "RBP restored");
    assert_eq!(emu.regs().rdi, 0x6666666666666666, "RDI restored");
    assert_eq!(emu.regs().rsi, 0x5555555555555555, "RSI restored");
    assert_eq!(emu.regs().rdx, 0x4444444444444444, "RDX restored");
    assert_eq!(emu.regs().rcx, 0x3333333333333333, "RCX restored");
    assert_eq!(emu.regs().rbx, 0x2222222222222222, "RBX restored");
    assert_eq!(emu.regs().rax, 0x1111111111111111, "RAX restored");
    assert_eq!(emu.regs().rsp, 0x1000, "RSP restored");
}

// POP extended registers (R8-R15)
#[test]
fn test_pop_r8() {
    let code = [
        0x41, 0x50, // PUSH R8
        0x49, 0x31, 0xc0, // XOR R8, R8
        0x41, 0x58, // POP R8
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.regs_mut().r8 = 0xCCCCCCCCCCCCCCCC;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().r8, 0xCCCCCCCCCCCCCCCC);
}

#[test]
fn test_pop_r15() {
    let code = [
        0x41, 0x57, // PUSH R15
        0x41, 0x5f, // POP R15
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.regs_mut().r15 = 0xDDDDDDDDDDDDDDDD;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().r15, 0xDDDDDDDDDDDDDDDD);
}

// POP increments RSP
#[test]
fn test_pop_increments_rsp() {
    let code = [
        0x6a, 0x42, // PUSH 0x42
        0x58, // POP RAX
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rsp, 0x1000, "RSP back to original after PUSH/POP");
    assert_eq!(emu.regs().rax, 0x42, "Value popped");
}

// Multiple POP operations
#[test]
fn test_multiple_pop() {
    let code = [
        0x6a, 0x11, // PUSH 0x11
        0x6a, 0x22, // PUSH 0x22
        0x6a, 0x33, // PUSH 0x33
        0x58, // POP RAX (gets 0x33)
        0x5b, // POP RBX (gets 0x22)
        0x59, // POP RCX (gets 0x11)
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rax, 0x33, "Last pushed, first popped");
    assert_eq!(emu.regs().rbx, 0x22, "Middle value");
    assert_eq!(emu.regs().rcx, 0x11, "First pushed, last popped");
    assert_eq!(emu.regs().rsp, 0x1000, "Stack balanced");
}

// POP preserves flags
#[test]
fn test_pop_preserves_flags() {
    let code = [
        0x48, 0xc7, 0xc0, 0xff, 0xff, 0xff, 0xff, // MOV RAX, -1
        0x48, 0x83, 0xc0, 0x01, // ADD RAX, 1 (sets ZF)
        0x50, // PUSH RAX
        0x58, // POP RAX
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert!(emu.flags().dump() & 0x40 != 0, "ZF should still be set");
}

// POP RSP (special case - uses value from stack)
#[test]
fn test_pop_rsp() {
    let code = [
        0x54, // PUSH RSP
        0x5c, // POP RSP
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rsp, 0x1000, "RSP restored from stack");
}

// Test LIFO (Last In First Out) behavior
#[test]
fn test_lifo_behavior() {
    let code = [
        0x48, 0xc7, 0xc0, 0x01, 0x00, 0x00, 0x00, // MOV RAX, 1
        0x48, 0xc7, 0xc3, 0x02, 0x00, 0x00, 0x00, // MOV RBX, 2
        0x48, 0xc7, 0xc1, 0x03, 0x00, 0x00, 0x00, // MOV RCX, 3
        0x50, 0x53, 0x51, // PUSH RAX, RBX, RCX
        0x5a, 0x5f, 0x5e, // POP RDX, RDI, RSI
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rdx, 3, "RDX gets last pushed (RCX=3)");
    assert_eq!(emu.regs().rdi, 2, "RDI gets middle (RBX=2)");
    assert_eq!(emu.regs().rsi, 1, "RSI gets first pushed (RAX=1)");
}

// POP with zero value
#[test]
fn test_pop_zero() {
    let code = [
        0x6a, 0x00, // PUSH 0
        0x48, 0xc7, 0xc0, 0xff, 0xff, 0xff, 0xff, // MOV RAX, -1
        0x58, // POP RAX
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rax, 0, "RAX should be 0");
}

// POP with maximum value
#[test]
fn test_pop_max_value() {
    let code = [
        0x48, 0xc7, 0xc0, 0xff, 0xff, 0xff, 0xff, // MOV RAX, 0xFFFFFFFF
        0x50, // PUSH RAX
        0x48, 0x31, 0xc0, // XOR RAX, RAX
        0x58, // POP RAX
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    // MOV r64, imm32 sign-extends: 0xFFFFFFFF becomes 0xFFFFFFFFFFFFFFFF
    assert_eq!(emu.regs().rax, 0xFFFFFFFFFFFFFFFF, "RAX should be sign-extended max");
}

// Practical use case: function epilogue
#[test]
fn test_pop_practical_function_epilogue() {
    let code = [
        // Prologue
        0x55, // PUSH RBP
        0x48, 0x89, 0xe5, // MOV RBP, RSP
        0x50, // PUSH RAX
        0x53, // PUSH RBX
        // ... function body would be here ...
        // Epilogue
        0x5b, // POP RBX (restore RBX)
        0x58, // POP RAX (restore RAX)
        0x5d, // POP RBP (restore RBP)
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.regs_mut().rbp = 0x2000;
    emu.regs_mut().rax = 0x1111;
    emu.regs_mut().rbx = 0x2222;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rbp, 0x2000, "RBP restored");
    assert_eq!(emu.regs().rax, 0x1111, "RAX restored");
    assert_eq!(emu.regs().rbx, 0x2222, "RBX restored");
    assert_eq!(emu.regs().rsp, 0x1000, "RSP balanced");
}

// POP from different stack positions
#[test]
fn test_pop_after_stack_manipulation() {
    let code = [
        0x6a, 0x01, // PUSH 1
        0x6a, 0x02, // PUSH 2
        0x6a, 0x03, // PUSH 3
        0x48, 0x83, 0xc4, 0x08, // ADD RSP, 8 (skip one value)
        0x58, // POP RAX (gets 2, not 3)
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rax, 2, "Skipped top value, got second");
}

// Test with all extended registers
#[test]
fn test_pop_all_extended_regs() {
    let code = [
        0x41, 0x50, 0x41, 0x51, 0x41, 0x52, 0x41, 0x53, // PUSH R8-R11
        0x41, 0x54, 0x41, 0x55, 0x41, 0x56, 0x41, 0x57, // PUSH R12-R15
        0x41, 0x5f, 0x41, 0x5e, 0x41, 0x5d, 0x41, 0x5c, // POP R15-R12
        0x41, 0x5b, 0x41, 0x5a, 0x41, 0x59, 0x41, 0x58, // POP R11-R8
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.regs_mut().r8 = 0x08;
    emu.regs_mut().r9 = 0x09;
    emu.regs_mut().r10 = 0x0A;
    emu.regs_mut().r11 = 0x0B;
    emu.regs_mut().r12 = 0x0C;
    emu.regs_mut().r13 = 0x0D;
    emu.regs_mut().r14 = 0x0E;
    emu.regs_mut().r15 = 0x0F;
    emu.run(None).unwrap();

    assert_eq!(emu.regs().r15, 0x0F, "R15 restored");
    assert_eq!(emu.regs().r14, 0x0E, "R14 restored");
    assert_eq!(emu.regs().r13, 0x0D, "R13 restored");
    assert_eq!(emu.regs().r12, 0x0C, "R12 restored");
    assert_eq!(emu.regs().r11, 0x0B, "R11 restored");
    assert_eq!(emu.regs().r10, 0x0A, "R10 restored");
    assert_eq!(emu.regs().r9, 0x09, "R9 restored");
    assert_eq!(emu.regs().r8, 0x08, "R8 restored");
}

// Chain of PUSHes and POPs
#[test]
fn test_push_pop_chain() {
    let code = [
        0x48, 0xc7, 0xc0, 0x11, 0x00, 0x00, 0x00, // MOV RAX, 0x11
        0x50, // PUSH RAX
        0x48, 0xc7, 0xc0, 0x22, 0x00, 0x00, 0x00, // MOV RAX, 0x22
        0x50, // PUSH RAX
        0x48, 0xc7, 0xc0, 0x33, 0x00, 0x00, 0x00, // MOV RAX, 0x33
        0x50, // PUSH RAX
        0x5b, // POP RBX (0x33)
        0x59, // POP RCX (0x22)
        0x5a, // POP RDX (0x11)
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rbx, 0x33);
    assert_eq!(emu.regs().rcx, 0x22);
    assert_eq!(emu.regs().rdx, 0x11);
}

// Test stack alignment
#[test]
fn test_stack_alignment() {
    let code = [
        0x50, 0x50, 0x50, 0x50, // PUSH RAX 4 times
        0x58, 0x58, 0x58, 0x58, // POP RAX 4 times
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.regs_mut().rax = 0x42;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rsp, 0x1000, "Stack aligned after equal PUSH/POP");
}

// POP into same register multiple times
#[test]
fn test_pop_same_register_multiple() {
    let code = [
        0x6a, 0x01, // PUSH 1
        0x6a, 0x02, // PUSH 2
        0x6a, 0x03, // PUSH 3
        0x58, // POP RAX (gets 3)
        0x58, // POP RAX (gets 2, overwrites 3)
        0x58, // POP RAX (gets 1, overwrites 2)
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rax, 1, "RAX has last popped value");
}

// Test interaction with MOV
#[test]
fn test_pop_with_mov() {
    let code = [
        0x48, 0xc7, 0xc0, 0xaa, 0x00, 0x00, 0x00, // MOV RAX, 0xAA
        0x50, // PUSH RAX
        0x48, 0xc7, 0xc0, 0xbb, 0x00, 0x00, 0x00, // MOV RAX, 0xBB
        0x48, 0x89, 0xc3, // MOV RBX, RAX
        0x58, // POP RAX
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rax, 0xAA, "RAX restored from stack");
    assert_eq!(emu.regs().rbx, 0xBB, "RBX has copied value");
}

// Deep stack test
#[test]
fn test_deep_stack() {
    let code = [
        0x6a, 0x01, 0x6a, 0x02, 0x6a, 0x03, 0x6a, 0x04, 0x6a, 0x05,
        0x6a, 0x06, 0x6a, 0x07, 0x6a, 0x08, 0x6a, 0x09, 0x6a, 0x0a,
        // Pop all back
        0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    assert_eq!(emu.regs().rsp, 0x1000, "Deep stack balanced");
    assert_eq!(emu.regs().rax, 1, "Last pop gets first push");
}

// Test POP doesn't affect other registers
#[test]
fn test_pop_preserves_other_registers() {
    let code = [
        0x48, 0xc7, 0xc3, 0x33, 0x00, 0x00, 0x00, // MOV RBX, 0x33
        0x48, 0xc7, 0xc1, 0x44, 0x00, 0x00, 0x00, // MOV RCX, 0x44
        0x6a, 0x99, // PUSH 0x99
        0x58, // POP RAX
        0xf4, // HLT
    ];
    let mut emu = emu64();
    emu.load_code_bytes(&code);
    emu.maps.create_map("stack_test", 0x1000-(0x1000 / 2), 0x1000, crate::maps::mem64::Permission::READ_WRITE_EXECUTE).unwrap();
    emu.regs_mut().rsp = 0x1000;
    emu.run(None).unwrap();
    // PUSH imm8 sign-extends: 0x99 (bit 7 set) -> 0xFFFFFFFFFFFFFF99
    assert_eq!(emu.regs().rax, 0xFFFFFFFFFFFFFF99, "RAX popped (sign-extended)");
    assert_eq!(emu.regs().rbx, 0x33, "RBX unchanged");
    assert_eq!(emu.regs().rcx, 0x44, "RCX unchanged");
}