#reverse-engineering #malware #emulation #internals #x86 #shellcode #binary

libscemu

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

260 releases (15 breaking)

0.19.4 Dec 28, 2024
0.18.7 Dec 6, 2024
0.18.6 Nov 8, 2024
0.16.7 Jul 14, 2024
0.8.9 Nov 11, 2022

#103 in Operating systems

Download history 161/week @ 2024-09-17 199/week @ 2024-09-24 264/week @ 2024-10-01 329/week @ 2024-10-08 118/week @ 2024-10-15 763/week @ 2024-10-22 152/week @ 2024-10-29 256/week @ 2024-11-05 9/week @ 2024-11-12 10/week @ 2024-11-19 2/week @ 2024-11-26 125/week @ 2024-12-03 53/week @ 2024-12-10 317/week @ 2024-12-17 347/week @ 2024-12-24 4/week @ 2024-12-31

724 downloads per month

MIT license

2MB
38K SLoC

SCEMU the lib

This repo has been archived, now libscemu, pyscemu and scemu become 1 repo: https://github.com/sha0coder/mwemu https://crates.io/crates/libmwemu

Usage

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

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(false, false);

Load your shellcode or PE binary and run the emulator. None 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
log::info!("return value: 0x{:x}", emu.regs.get_eax());
emu.maps.dump(param2_out_buff);

Now it's possible to do hooks on libscemu but not on pyscemu.

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) {
    log::info!("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 {
    log::info!("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 {
    log::info!("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 {
    log::info!("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();
    log::info!("end!");
}

Dependencies

~20–29MB
~465K SLoC