一
介绍
二
系统调用
1.识别出Bochs正在尝试进行系统调用(奇怪的是,这并不总是容易做到的)。
2.拦截执行并重定向到适当的代码路径。
3.保存Bochs的执行状态。
4.执行Lucid的逻辑来替代内核的功能,可以把Lucid看作是Bochs的内核。
5.通过恢复Bochs的状态优雅地返回到Bochs。
三
C库
open
,你并没有直接进行系统调用,而是调用了库中的open
函数,而这个函数才是真正发出系统调用指令并执行上下文切换进入内核的。以这种方式编写代码可以大大减轻程序员的跨平台工作,因为库函数的内部实现会执行所有的环境变量检查并相应地执行。程序员只需调用open
函数,而不必担心系统调用编号、错误处理等问题,因为这些都在提供给程序员的代码中保持了抽象和统一。fn syscall()
if lucid:
lucid_syscall()
else:
normal_syscall()
#include <stdio.h>
#include <unistd.h>
#include <lucid.h>int main(int argc, char *argv[]) {
printf("Argument count: %d\n", argc);
printf("Args:\n");
for (int i = 0; i < argc; i++) {
printf(" -%s\n", argv[i]);
}size_t iters = 0;
while (1) {
printf("Test alive!\n");
sleep(1);
iters++;if (iters == 5) { break; }
}printf("g_lucid_ctx: %p\n", g_lucid_ctx);
}
四
执行上下文跟踪
lucid.h
。该文件定义了我们在编译时需要 Bochs 访问的所有 Lucid 特定的数据结构。因此,在头文件中,我们目前定义了一个lucid_ctx
数据结构,并创建了一个全局实例,名为g_lucid_ctx
。// An execution context definition that we use to switch contexts between the
// fuzzer and Bochs. This should contain all of the information we need to track
// all of the mutable state between snapshots that we need such as file data.
// This has to be consistent with LucidContext in context.rs
typedef struct lucid_ctx {
// This must always be the first member of this struct
size_t exit_handler;
int save_inst;
size_t save_size;
size_t lucid_save_area;
size_t bochs_save_area;
struct register_bank register_bank;
size_t magic;
} lucid_ctx_t;// Pointer to the global execution context, if running inside Lucid, this will
// point to the a struct lucid_ctx_t inside the Fuzzer
lucid_ctx_t *g_lucid_ctx;
dlstart.c
中的 Musl 函数_dlstart_c
。目前,我们在堆上创建该全局执行上下文,然后将该地址传递给任意选择的r15
。这个函数最终会发生变化,因为我们将在未来想要从 Lucid 切换到 Bochs 来执行这一操作,但目前我们所做的只是:pub fn start_bochs(bochs: Bochs, context: Box<LucidContext>) {
// rdx: we have to clear this register as the ABI specifies that exit
// hooks are set when rdx is non-null at program start
//
// rax: arbitrarily used as a jump target to the program entry
//
// rsp: Rust does not allow you to use 'rsp' explicitly with in(), so we
// have to manually set it with a `mov`
//
// r15: holds a pointer to the execution context, if this value is non-
// null, then Bochs learns at start time that it is running under Lucid
//
// We don't really care about execution order as long as we specify clobbers
// with out/lateout, that way the compiler doesn't allocate a register we
// then immediately clobber
unsafe {
asm!(
"xor rdx, rdx",
"mov rsp, {0}",
"mov r15, {1}",
"jmp rax",
in(reg) bochs.rsp,
in(reg) Box::into_raw(context),
in("rax") bochs.entry,
lateout("rax") _, // Clobber (inout so no conflict with in)
out("rdx") _, // Clobber
out("r15") _, // Clobber
);
}
}
r15
应该持有执行上下文的地址。在_dlstart_c
中,我们可以检查r15
并据此采取行动。这是我对 Musl 启动例程所做的那些添加:hidden void _dlstart_c(size_t *sp, size_t *dynv)
{
// The start routine is handled in inline assembly in arch/x86_64/crt_arch.h
// so we can just do this here. That function logic clobbers only a few
// registers, so we can have the Lucid loader pass the address of the
// Lucid context in r15, this is obviously not the cleanest solution but
// it works for our purposes
size_t r15;
__asm__ __volatile__(
"mov %%r15, %0" : "=r"(r15)
);// If r15 was not 0, set the global context address for the g_lucid_ctx that
// is in the Rust fuzzer
if (r15 != 0) {
g_lucid_ctx = (lucid_ctx_t *)r15;// We have to make sure this is true, we rely on this
if ((void *)g_lucid_ctx != (void *)&g_lucid_ctx->exit_handler) {
__asm__ __volatile__("int3");
}
}// We didn't get a g_lucid_ctx, so we can just run normally
else {
g_lucid_ctx = (lucid_ctx_t *)0;
}
r15
。因此,我们使用内联汇编将值提取到一个名为r15
的变量中,并检查它是否有数据。如果有数据,我们将全局上下文变量设置为r15
中的地址;否则,我们将其显式设置为 NULL 并按正常方式运行。现在设置了全局变量,我们可以在运行时检查我们的环境,并选择性地调用真实的内核或 Lucid。arch/x86_64/syscall_arch.h
中。它们按照系统调用所需参数的数量进行组织:static __inline long __syscall0(long n)
{
unsigned long ret;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n) : "rcx", "r11", "memory");
return ret;
}static __inline long __syscall1(long n, long a1)
{
unsigned long ret;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1) : "rcx", "r11", "memory");
return ret;
}static __inline long __syscall2(long n, long a1, long a2)
{
unsigned long ret;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2)
: "rcx", "r11", "memory");
return ret;
}static __inline long __syscall3(long n, long a1, long a2, long a3)
{
unsigned long ret;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2),
"d"(a3) : "rcx", "r11", "memory");
return ret;
}static __inline long __syscall4(long n, long a1, long a2, long a3, long a4)
{
unsigned long ret;
register long r10 __asm__("r10") = a4;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2),
"d"(a3), "r"(r10): "rcx", "r11", "memory");
return ret;
}static __inline long __syscall5(long n, long a1, long a2, long a3, long a4, long a5)
{
unsigned long ret;
register long r10 __asm__("r10") = a4;
register long r8 __asm__("r8") = a5;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2),
"d"(a3), "r"(r10), "r"(r8) : "rcx", "r11", "memory");
return ret;
}static __inline long __syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6)
{
unsigned long ret;
register long r10 __asm__("r10") = a4;
register long r8 __asm__("r8") = a5;
register long r9 __asm__("r9") = a6;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2),
"d"(a3), "r"(r10), "r"(r8), "r"(r9) : "rcx", "r11", "memory");
return ret;
}
eax
中,然后接下来的n个参数按顺序通过寄存器传递:rdi
、rsi
、rdx
、r10
、r8
和r9
。__asm__ __volatile__ ("syscall")
行上,很难看出它在做什么。让我们以最复杂的函数__syscall6
为例,详细解析一下所有的语法。我们可以将汇编语法看作是一个格式字符串,就像打印时使用格式字符串一样,但这是为了生成代码:unsigned long ret
是我们将存储系统调用结果的地方,以指示系统调用是否成功。在原始汇编中,我们可以看到有一个:
然后是"=a(ret)"
,这个冒号后的第一个参数集用于指示输出参数。我们是在说请将结果存储在eax
(在语法中用a
表示)中并放入变量ret
。"a"(n)
的意思是,将函数参数n
(即系统调用号)放入eax
,这在语法中再次用a
表示。接下来是将a1
存储在rdi
中,rdi
在语法中表示为D
,依此类推。register long r10 __asm__("r10") = a4;
是一个强烈的编译器提示,要求将a4
存储到r10
中。然后我们看到"r"(r10)
表示将变量r10
输入到一个通用寄存器中(这已经满足了)。rcx
、r11
和内存可能会被内核覆盖。__syscall6
并传递其参数时,每个参数存储在以下寄存器中:n
→rax
a1
→rdi
a2
→rsi
a3
→rdx
a4
→rcx
a5
→r8
a6
→r9
◆r15
:包含全局 Lucid 执行上下文的地址。
◆r14
:包含一个“退出原因”,这是一个枚举值,解释我们为什么要进行上下文切换。
◆r13
:是 Lucid 执行上下文的寄存器库结构的基地址,我们需要这个内存区域来存储寄存器值,以便在上下文切换时保存我们的状态。
◆r12
:存储“退出处理程序”的地址,这是一个用于进行上下文切换的函数。
__syscall6_original
的单独函数中:static __inline long __syscall6_original(long n, long a1, long a2, long a3, long a4, long a5, long a6)
{
unsigned long ret;
register long r10 __asm__("r10") = a4;
register long r8 __asm__("r8") = a5;
register long r9 __asm__("r9") = a6;
__asm__ __volatile__ ("syscall" : "=a"(ret) : "a"(n), "D"(a1), "S"(a2), "d"(a3), "r"(r10),
"r"(r8), "r"(r9) : "rcx", "r11", "memory");return ret;
}static __inline long __syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6)
{
if (!g_lucid_ctx) { return __syscall6_original(n, a1, a2, a3, a4, a5, a6); }
r12
到r15
来按照我们在上下文切换到 Lucid 时的预期设置调用约定。static __inline long __syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6)
{
if (!g_lucid_ctx) { return __syscall6_original(n, a1, a2, a3, a4, a5, a6); }register long ret;
register long r12 __asm__("r12") = (size_t)(g_lucid_ctx->exit_handler);
register long r13 __asm__("r13") = (size_t)(&g_lucid_ctx->register_bank);
register long r14 __asm__("r14") = SYSCALL;
register long r15 __asm__("r15") = (size_t)(g_lucid_ctx);
syscall
指令替换为call r12
,即像普通函数一样调用我们的退出处理程序:__asm__ __volatile__ (
"mov %1, %%rax\n\t"
"mov %2, %%rdi\n\t"
"mov %3, %%rsi\n\t"
"mov %4, %%rdx\n\t"
"mov %5, %%r10\n\t"
"mov %6, %%r8\n\t"
"mov %7, %%r9\n\t"
"call *%%r12\n\t"
"mov %%rax, %0\n\t"
: "=r" (ret)
: "r" (n), "r" (a1), "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6),
"r" (r12), "r" (r13), "r" (r14), "r" (r15)
: "rax", "rcx", "r11", "memory"
);return ret;
extern C
函数并在内联汇编中给它一个标签,使其对 Bochs 可见(通过我们修改过的 Musl):extern "C" { fn exit_handler(); }
global_asm!(
".global exit_handler",
"exit_handler:",
r13
来存储执行上下文寄存器库的基地址:#[repr(C)]
#[derive(Default, Clone)]
pub struct RegisterBank {
pub rax: usize,
rbx: usize,
rcx: usize,
pub rdx: usize,
pub rsi: usize,
pub rdi: usize,
rbp: usize,
rsp: usize,
pub r8: usize,
pub r9: usize,
pub r10: usize,
r11: usize,
r12: usize,
r13: usize,
r14: usize,
r15: usize,
}
// Save the GPRS to memory
"mov [r13 + 0x0], rax",
"mov [r13 + 0x8], rbx",
"mov [r13 + 0x10], rcx",
"mov [r13 + 0x18], rdx",
"mov [r13 + 0x20], rsi",
"mov [r13 + 0x28], rdi",
"mov [r13 + 0x30], rbp",
"mov [r13 + 0x38], rsp",
"mov [r13 + 0x40], r8",
"mov [r13 + 0x48], r9",
"mov [r13 + 0x50], r10",
"mov [r13 + 0x58], r11",
"mov [r13 + 0x60], r12",
"mov [r13 + 0x68], r13",
"mov [r13 + 0x70], r14",
"mov [r13 + 0x78], r15",
pushfq
。r15
和r14
中。因此,我们可以简单地将它们放入用于传递函数参数的寄存器中,并立即调用一个名为lucid_handler
的 Rust 函数。// Save the CPU flags
"pushfq",// Set up the function arguments for lucid_handler according to ABI
"mov rdi, r15", // Put the pointer to the context into RDI
"mov rsi, r14", // Put the exit reason into RSI// At this point, we've been called into by Bochs, this should mean that
// at the beginning of our exit_handler, rsp was only 8-byte aligned and
// thus, by ABI, we cannot legally call into a Rust function since to do so
// requires rsp to be 16-byte aligned. Luckily, `pushfq` just 16-byte
// aligned the stack for us and so we are free to `call`
"call lucid_handler",
lucid_handler
函数:// This is where the actual logic is for handling the Bochs exit, we have to
// use no_mangle here so that we can call it from the assembly blob. We need
// to see why we've exited and dispatch to the appropriate function
#[no_mangle]
fn lucid_handler(context: *mut LucidContext, exit_reason: i32) {
// We have to make sure this bad boy isn't NULL
if context.is_null() {
println!("LucidContext pointer was NULL");
fatal_exit();
}// Ensure that we have our magic value intact, if this is wrong, then we
// are in some kind of really bad state and just need to die
let magic = LucidContext::ptr_to_magic(context);
if magic != CTX_MAGIC {
println!("Invalid LucidContext Magic value: 0x{:X}", magic);
fatal_exit();
}// Before we do anything else, save the extended state
let save_inst = LucidContext::ptr_to_save_inst(context);
if save_inst.is_err() {
println!("Invalid Save Instruction");
fatal_exit();
}
let save_inst = save_inst.unwrap();// Get the save area
let save_area =
LucidContext::ptr_to_save_area(context, SaveDirection::FromBochs);if save_area == 0 || save_area % 64 != 0 {
println!("Invalid Save Area");
fatal_exit();
}// Determine save logic
match save_inst {
SaveInst::XSave64 => {
// Retrieve XCR0 value, this will serve as our save mask
let xcr0 = unsafe { _xgetbv(0) } as u64;// Call xsave to save the extended state to Bochs save area
unsafe { _xsave64(save_area as *mut u8, xcr0); }
},
SaveInst::FxSave64 => {
// Call fxsave to save the extended state to Bochs save area
unsafe { _fxsave64(save_area as *mut u8); }
},
_ => (), // NoSave
}// Try to convert the exit reason into BochsExit
let exit_reason = BochsExit::try_from(exit_reason);
if exit_reason.is_err() {
println!("Invalid Bochs Exit Reason");
fatal_exit();
}
let exit_reason = exit_reason.unwrap();// Determine what to do based on the exit reason
match exit_reason {
BochsExit::Syscall => {
syscall_handler(context);
},
}// Restore extended state, determine restore logic
match save_inst {
SaveInst::XSave64 => {
// Retrieve XCR0 value, this will serve as our save mask
let xcr0 = unsafe { _xgetbv(0) } as u64;// Call xrstor to restore the extended state from Bochs save area
unsafe { _xrstor64(save_area as *const u8, xcr0); }
},
SaveInst::FxSave64 => {
// Call fxrstor to restore the extended state from Bochs save area
unsafe { _fxrstor64(save_area as *const u8); }
},
_ => (), // NoSave
}
}
五
扩展状态
lucid_handler
中使用任何扩展状态,所以它仍然被保留。你可以在这里看到我在上下文初始化期间是如何检查的:pub fn new() -> Result<Self, LucidErr> {
// Check for what kind of features are supported we check from most
// advanced to least
let save_inst = if std::is_x86_feature_detected!("xsave") {
SaveInst::XSave64
} else if std::is_x86_feature_detected!("fxsr") {
SaveInst::FxSave64
} else {
SaveInst::NoSave
};// Get save area size
let save_size: usize = match save_inst {
SaveInst::NoSave => 0,
_ => calc_save_size(),
};
// Standalone function to calculate the size of the save area for saving the
// extended processor state based on the current processor's features. `cpuid`
// will return the save area size based on the value of the XCR0 when ECX==0
// and EAX==0xD. The value returned to EBX is based on the current features
// enabled in XCR0, while the value returned in ECX is the largest size it
// could be based on CPU capabilities. So out of an abundance of caution we use
// the ECX value. We have to preserve EBX or rustc gets angry at us. We are
// assuming that the fuzzer and Bochs do not modify the XCR0 at any time.
fn calc_save_size() -> usize {
let save: usize;
unsafe {
asm!(
"push rbx",
"mov rax, 0xD",
"xor rcx, rcx",
"cpuid",
"pop rbx",
out("rax") _, // Clobber
out("rcx") save, // Save the max size
out("rdx") _, // Clobbered by CPUID output (w eax)
);
}// Round up to the nearest page size
(save + PAGE_SIZE - 1) & !(PAGE_SIZE - 1)
}
lucid_handler
的运行时,我们可以保存扩展状态:// Determine save logic
match save_inst {
SaveInst::XSave64 => {
// Retrieve XCR0 value, this will serve as our save mask
let xcr0 = unsafe { _xgetbv(0) } as u64;// Call xsave to save the extended state to Bochs save area
unsafe { _xsave64(save_area as *mut u8, xcr0); }
},
SaveInst::FxSave64 => {
// Call fxsave to save the extended state to Bochs save area
unsafe { _fxsave64(save_area as *mut u8); }
},
_ => (), // NoSave
}
exit_handler
汇编存根之前恢复扩展状态:// Determine what to do based on the exit reason
match exit_reason {
BochsExit::Syscall => {
syscall_handler(context);
},
}// Restore extended state, determine restore logic
match save_inst {
SaveInst::XSave64 => {
// Retrieve XCR0 value, this will serve as our save mask
let xcr0 = unsafe { _xgetbv(0) } as u64;// Call xrstor to restore the extended state from Bochs save area
unsafe { _xrstor64(save_area as *const u8, xcr0); }
},
SaveInst::FxSave64 => {
// Call fxrstor to restore the extended state from Bochs save area
unsafe { _fxrstor64(save_area as *const u8); }
},
_ => (), // NoSave
}
Argument count: 1
Args:
-./test
Test alive!
Test alive!
Test alive!
Test alive!
Test alive!
g_lucid_ctx: 0
strace
运行它时,我们可以看到进行了哪些系统调用:execve("./test", ["./test"], 0x7ffca76fee90 /* 49 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x7fd53887f5b8) = 0
set_tid_address(0x7fd53887f7a8) = 850649
ioctl(1, TIOCGWINSZ, {ws_row=40, ws_col=110, ws_xpixel=0, ws_ypixel=0}) = 0
writev(1, [{iov_base="Argument count: 1", iov_len=17}, {iov_base="\n", iov_len=1}], 2Argument count: 1
) = 18
writev(1, [{iov_base="Args:", iov_len=5}, {iov_base="\n", iov_len=1}], 2Args:
) = 6
writev(1, [{iov_base=" -./test", iov_len=10}, {iov_base="\n", iov_len=1}], 2 -./test
) = 11
writev(1, [{iov_base="Test alive!", iov_len=11}, {iov_base="\n", iov_len=1}], 2Test alive!
) = 12
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc2fb55470) = 0
writev(1, [{iov_base="Test alive!", iov_len=11}, {iov_base="\n", iov_len=1}], 2Test alive!
) = 12
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc2fb55470) = 0
writev(1, [{iov_base="Test alive!", iov_len=11}, {iov_base="\n", iov_len=1}], 2Test alive!
) = 12
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc2fb55470) = 0
writev(1, [{iov_base="Test alive!", iov_len=11}, {iov_base="\n", iov_len=1}], 2Test alive!
) = 12
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc2fb55470) = 0
writev(1, [{iov_base="Test alive!", iov_len=11}, {iov_base="\n", iov_len=1}], 2Test alive!
) = 12
nanosleep({tv_sec=1, tv_nsec=0}, 0x7ffc2fb55470) = 0
writev(1, [{iov_base="g_lucid_ctx: 0", iov_len=14}, {iov_base="\n", iov_len=1}], 2g_lucid_ctx: 0
) = 15
exit_group(0) = ?
+++ exited with 0 +++
set_tid_address
、ioctl
和writev
。我们暂时不担心exit_group
,因为如果我们正在进行快照模糊测试,Bochs 不应该退出,这将是一个致命的退出条件。eax
中提取系统调用号,并将其分派到相应的系统调用函数!你可以在这里看到该逻辑:// This is where we process Bochs making a syscall. All we need is a pointer to
// the execution context, and we can then access the register bank and all the
// peripheral structures we need
#[allow(unused_variables)]
pub fn syscall_handler(context: *mut LucidContext) {
// Get a handle to the register bank
let bank = LucidContext::get_register_bank(context);// Check what the syscall number is
let syscall_no = (*bank).rax;// Get the syscall arguments
let arg1 = (*bank).rdi;
let arg2 = (*bank).rsi;
let arg3 = (*bank).rdx;
let arg4 = (*bank).r10;
let arg5 = (*bank).r8;
let arg6 = (*bank).r9;match syscall_no {
// ioctl
0x10 => {
//println!("Handling ioctl()...");
// Make sure the fd is 1, that's all we handle right now?
if arg1 != 1 {
println!("Invalid `ioctl` fd: {}", arg1);
fatal_exit();
}// Check the `cmd` argument
match arg2 as u64 {
// Requesting window size
libc::TIOCGWINSZ => {
// Arg 3 is a pointer to a struct winsize
let winsize_p = arg3 as *mut libc::winsize;// If it's NULL, return an error, we don't set errno yet
// that's a weird problem
// TODO: figure out that whole TLS issue yikes
if winsize_p.is_null() {
(*bank).rax = usize::MAX;
return;
}// Deref the raw pointer
let winsize = unsafe { &mut *winsize_p };// Set to some constants
winsize.ws_row = WS_ROW;
winsize.ws_col = WS_COL;
winsize.ws_xpixel = WS_XPIXEL;
winsize.ws_ypixel = WS_YPIXEL;// Return success
(*bank).rax = 0;
},
_ => {
println!("Unhandled `ioctl` argument: 0x{:X}", arg1);
fatal_exit();
}
}
},
// writev
0x14 => {
//println!("Handling writev()...");
// Get the fd
let fd = arg1 as libc::c_int;// Make sure it's an fd we handle
if fd != STDOUT {
println!("Unhandled writev fd: {}", fd);
}// An accumulator that we return
let mut bytes_written = 0;// Get the iovec count
let iovcnt = arg3 as libc::c_int;// Get the pointer to the iovec
let mut iovec_p = arg2 as *const libc::iovec;// If the pointer was NULL, just return error
if iovec_p.is_null() {
(*bank).rax = usize::MAX;
return;
}// Iterate through the iovecs and write the contents
green!();
for i in 0..iovcnt {
bytes_written += write_iovec(iovec_p);// Update iovec_p
iovec_p = unsafe { iovec_p.offset(1 + i as isize) };
}
clear!();// Update return value
(*bank).rax = bytes_written;
},
// nanosleep
0x23 => {
//println!("Handling nanosleep()...");
(*bank).rax = 0;
},
// set_tid_address
0xDA => {
//println!("Handling set_tid_address()...");
// Just return Boch's pid, no need to do anything
(*bank).rax = BOCHS_PID as usize;
},
_ => {
println!("Unhandled Syscall Number: 0x{:X}", syscall_no);
fatal_exit();
}
}
}
rax
设置返回码,然后优雅地返回到exit_handler
存根并返回到 Bochs。// Restore the flags
"popfq",// Restore the GPRS
"mov rax, [r13 + 0x0]",
"mov rbx, [r13 + 0x8]",
"mov rcx, [r13 + 0x10]",
"mov rdx, [r13 + 0x18]",
"mov rsi, [r13 + 0x20]",
"mov rdi, [r13 + 0x28]",
"mov rbp, [r13 + 0x30]",
"mov rsp, [r13 + 0x38]",
"mov r8, [r13 + 0x40]",
"mov r9, [r13 + 0x48]",
"mov r10, [r13 + 0x50]",
"mov r11, [r13 + 0x58]",
"mov r12, [r13 + 0x60]",
"mov r13, [r13 + 0x68]",
"mov r14, [r13 + 0x70]",
"mov r15, [r13 + 0x78]",// Return execution back to Bochs!
"ret"
ret
,就好像我们完成了函数调用一样。不要忘记我们在从lucid_context
返回之前已经恢复了扩展状态。六
结论
[08:15:56] lucid> Loading Bochs...
[08:15:56] lucid> Bochs mapping: 0x10000 - 0x18000
[08:15:56] lucid> Bochs mapping size: 0x8000
[08:15:56] lucid> Bochs stack: 0x7F8A50FCF000
[08:15:56] lucid> Bochs entry: 0x11058
[08:15:56] lucid> Creating Bochs execution context...
[08:15:56] lucid> Starting Bochs...
Argument count: 4
Args:
-./bochs
-lmfao
-hahahah
-yes!
Test alive!
Test alive!
Test alive!
Test alive!
Test alive!
g_lucid_ctx: 0x55f27f693cd0
Unhandled Syscall Number: 0xE7
七
下一步?
看雪ID:pureGavin
https://bbs.kanxue.com/user-home-777502.htm
# 往期推荐
2、恶意木马历险记
球分享
球点赞
球在看
点击阅读原文查看更多