#malware #emulation #internals #x86 #shellcode #binary #pe

libscemu

x86 32/64bits and system internals emulator, for securely emulating malware and other stuff

254 releases

0.18.6 Nov 8, 2024
0.18.4 Oct 29, 2024
0.16.7 Jul 14, 2024
0.15.6 Mar 27, 2024
0.8.9 Nov 11, 2022

#104 in Operating systems

Download history 1/week @ 2024-07-29 525/week @ 2024-08-19 6/week @ 2024-08-26 185/week @ 2024-09-16 202/week @ 2024-09-23 15/week @ 2024-09-30 452/week @ 2024-10-07 246/week @ 2024-10-14 756/week @ 2024-10-21 159/week @ 2024-10-28 250/week @ 2024-11-04 15/week @ 2024-11-11

1,191 downloads per month

MIT license

2MB
37K SLoC

SCEMU the lib

Usage

Download the maps32 or maps64 from: https://github.com/sha0coder/scemu

Uncompress it somewhere, in the example it's on /tmp/ but dont use tmp.

Create an emu32 or emu64 and it's important to set the maps folder.

use libscemu::emu32;


fn main() {
    let mut emu = emu32();
    emu.set_maps_folder("/tmp/maps32/");
    emu.init();

Load your shellcode or PE binary and run the emulator. Zero parameter means emulate for-ever.

emu.load_code("shellcodes32/shikata.bin");
emu.set_verbose(2);
emu.run(None).unwrap(); 

Or if you prefer call specific function.

emu.load_code("samples/malware.exe");

let crypto_key_gen = 0x40112233;
let ret_addr = 0x40110000; // any place safe to return.

let param1 = 0x33;
let param2_out_buff = emu.alloc("buffer", 1024);

emu.maps.memset(param2_out_buff, 0, 1024); // non necesary, by default alloc create zeros.
emu.maps.write_spaced_bytes(param2_out_buff, 
        "DE CC 6C 83 CC F3 66 85 34"); // example of initialization.

// call function
emu.regs.set_eip(crypto_key_gen);
emu.stack_push32(param2_out_buff);
emu.stack_push32(param1);
emu.stack_push32(ret_addr);
emu.run(Some(ret_addr)).unwrap();   // emulate until arrive to ret_addr

// or simpler way:
let eax = emu.call32(crypto_key_gen, &[param1, param2_out_buff]).unwrap();

// this would be slower but more control
while emu.step() {
    ...
}

// check result
println!("return value: 0x{:x}", emu.regs.get_eax());
emu.maps.dump(param2_out_buff);

Now it's possible to do hooks.

use libscemu::emu32;

//need iced_x86 crate only for instruction hooks, to get the
//instruction object, so add `iced-x86 = "1.17.0"`
use iced_x86::{Instruction};  


fn trace_memory_read(emu:&mut libscemu::emu::Emu, ip_addr:u64, 
                     mem_addr:u64, sz:u8) {
    println!("0x{:x}: reading {} at 0x{:x}", ip_addr, sz, mem_addr);
    if mem_addr == 0x22dff0 {
        emu.stop();
    }
}

fn trace_memory_write(emu:&mut libscemu::emu::Emu, ip_addr:u64, 
                      mem_addr:u64, sz:u8, value:u128) -> u128 {
    println!("0x{:x}: writing {} '0x{:x}' at 0x{:x}", ip_addr, sz, 
             value, mem_addr);
    value   // I could change the value to write
}

fn trace_interrupt(emu:&mut libscemu::emu::Emu, ip_addr:u64, 
                   interrupt:u64) -> bool {
    println!("interrupt {} triggered at eip: 0x{:x}", interrupt, 
             ip_addr);
    true  // do handle interrupts
}   

fn trace_exceptions(emu:&mut libscemu::emu::Emu, ip_addr:u64) -> bool {
    println!("0x{:x} triggered an exception", ip_addr);
    true // do handle exceptions
}

fn trace_pre_instruction(emu:&mut libscemu::emu::Emu, ip_addr:u64, 
                         ins:&Instruction, sz:usize) {
}

fn trace_post_instruction(emu:&mut libscemu::emu::Emu, ip_addr:u64, 
                          ins:&Instruction, sz:usize, emu_ok:bool) {
}

fn trace_winapi_call(emu:&mut libscemu::emu::Emu, ip_addr:u64, api_addr:u64) -> bool {
    return true; // handle api calls
}

fn main() {
    let mut emu = emu32();
    emu.set_maps_folder("../scemu/maps32/"); // download the maps, ideally from scemu git.
    emu.init();

    emu.load_code("/home/sha0/src/scemu/shellcodes32/mars.exe");
    emu.hook.on_memory_read(trace_memory_read);
    emu.hook.on_memory_write(trace_memory_write);
    emu.hook.on_interrupt(trace_interrupt);
    emu.hook.on_exception(trace_exceptions);
    emu.hook.on_pre_instruction(trace_pre_instruction);
    emu.hook.on_post_instruction(trace_post_instruction);
    emu.hook.on_winapi_call(trace_winapi_call);
    emu.run(None).unwrap();
    println!("end!");
}

Dependencies

~20–29MB
~467K SLoC