一
背景
二
介绍
三
系统调用基础设施更新
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;
}
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); }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);__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;
}
static __inline long __syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6)
{
if (g_lucid_syscall)
return g_lucid_syscall(g_lucid_ctx, n, a1, a2, a3, a4, a5, 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;
}
lucid.h
中创建一个新的全局函数指针变量,并在src/lucid.c
中为它定义一个函数,你可以在仓库中的 Musl 补丁中看到。Rust 端的g_lucid_syscall
看起来像这样:pub extern "C" fn lucid_syscall(contextp: *mut LucidContext, n: usize,
a1: usize, a2: usize, a3: usize, a4: usize, a5: usize, a6: usize)
-> u64
context_switch
函数根据上下文的值自行决定如何表现。本质上,我们将复杂性从调用方移动到了被调用方。这意味着复杂性不会在代码库中反复出现,而是一次性封装在context_switch
逻辑中。然而,这确实需要一些 hacky/brittle 代码,例如我们必须为 Lucid 执行数据结构硬编码一些结构偏移量,但我认为为大幅降低复杂性付出的小代价是值得的。context_switch
代码已更改为以下内容:extern "C" { fn context_switch(); }
global_asm!(
".global context_switch",
"context_switch:",// Save the CPU flags before we do any operations
"pushfq",// Save registers we use for scratch
"push r14",
"push r13",// Determine what execution mode we're in
"mov r14, r15",
"add r14, 0x8", // mode is at offset 0x8 from base
"mov r14, [r14]",
"cmp r14d, 0x0",
"je save_bochs",// We're in Lucid mode so save Lucid GPRs
"save_lucid: ",
"mov r14, r15",
"add r14, 0x10", // lucid_regs is at offset 0x10 from base
"jmp save_gprs",// We're in Bochs mode so save Bochs GPRs
"save_bochs: ",
"mov r14, r15",
"add r14, 0x90", // bochs_regs is at offset 0x90 from base
"jmp save_gprs",
Fault
概念,这是一种错误类,专门用于在上下文切换代码或系统调用处理期间遇到某种错误时使用。这个错误与我们最高级别的错误LucidErr
不同。当出现这些错误时,它们最终会被传递回 Lucid,以便 Lucid 进行处理。目前,Lucid 将任何Fault
都视为致命错误。#[inline(never)]
pub fn start_bochs(context: &mut LucidContext) {
// Set the execution mode and the reason why we're exiting the Lucid VM
context.mode = ExecMode::Lucid;
context.exit_reason = VmExit::StartBochs;// Set up the calling convention and then start Bochs by context switching
unsafe {
asm!(
"push r15", // Callee-saved register we have to preserve
"mov r15, {0}", // Move context into R15
"call qword ptr [r15]", // Call context_switch
"pop r15", // Restore callee-saved register
in(reg) context as *mut LucidContext,
);
}
}
// Execution context that is passed between Lucid and Bochs that tracks
// all of the mutable state information we need to do context-switching
#[repr(C)]
#[derive(Clone)]
pub struct LucidContext {
pub context_switch: usize, // Address of context_switch()
context_switch
例程中被保存,然后我们被传递到这段逻辑:// Handle Lucid context switches here
if LucidContext::is_lucid_mode(context) {
match exit_reason {
// Dispatch to Bochs entry point
VmExit::StartBochs => {
jump_to_bochs(context);
},
_ => {
fault!(context, Fault::BadLucidExit);
}
}
}
jump_to_bochs
:// Standalone function to literally jump to Bochs entry and provide the stack
// address to Bochs
fn jump_to_bochs(context: *mut 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) (*context).bochs_rsp,
in(reg) context,
in("rax") (*context).bochs_entry,
lateout("rax") _, // Clobber (inout so no conflict with in)
out("rdx") _, // Clobber
out("r15") _, // Clobber
);
}
}
Fault
,然后将该错误传递回 Lucid 进行处理。在fault_handler
中,我们在执行上下文中设置Fault
类型,然后尝试恢复执行回 Lucid:// Where we handle faults that may occur when context-switching from Bochs. We
// just want to make the fault visible to Lucid so we set it in the context,
// then we try to restore Lucid execution from its last-known good state
pub fn fault_handler(contextp: *mut LucidContext, fault: Fault) {
let context = unsafe { &mut *contextp };
match fault {
Fault::Success => context.fault = Fault::Success,
...
}// Attempt to restore Lucid execution
restore_lucid_execution(contextp);
}
// We use this function to restore Lucid execution to its last known good state
// This is just really trying to plumb up a fault to a level that is capable of
// discerning what action to take. Right now, we probably just call it fatal.
// We don't really deal with double-faults, it doesn't make much sense at the
// moment when a single-fault will likely be fatal already. Maybe later?
fn restore_lucid_execution(contextp: *mut LucidContext) {
let context = unsafe { &mut *contextp };// Fault should be set, but change the execution mode now since we're
// jumping back to Lucid
context.mode = ExecMode::Lucid;// Restore extended state
let save_area = context.lucid_save_area;
let save_inst = context.save_inst;
match save_inst {
SaveInst::XSave64 => {
// Retrieve XCR0 value, this will serve as our save mask
let xcr0 = unsafe { _xgetbv(0) };// 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
}// Next, we need to restore our GPRs. This is kind of different order than
// returning from a successful context switch since normally we'd still be
// using our own stack; however right now, we still have Bochs' stack, so
// we need to recover our own Lucid stack which is saved as RSP in our
// register bank
let lucid_regsp = &context.lucid_regs as *const _;// Move that pointer into R14 and restore our GPRs. After that we have the
// RSP value that we saved when we called into context_switch, this RSP was
// then subtracted from by 0x8 for the pushfq operation that comes right
// after. So in order to recover our CPU flags, we need to manually sub
// 0x8 from the stack pointer. Pop the CPU flags back into place, and then
// return to the last known good Lucid state
unsafe {
asm!(
"mov r14, {0}",
"mov rax, [r14 + 0x0]",
"mov rbx, [r14 + 0x8]",
"mov rcx, [r14 + 0x10]",
"mov rdx, [r14 + 0x18]",
"mov rsi, [r14 + 0x20]",
"mov rdi, [r14 + 0x28]",
"mov rbp, [r14 + 0x30]",
"mov rsp, [r14 + 0x38]",
"mov r8, [r14 + 0x40]",
"mov r9, [r14 + 0x48]",
"mov r10, [r14 + 0x50]",
"mov r11, [r14 + 0x58]",
"mov r12, [r14 + 0x60]",
"mov r13, [r14 + 0x68]",
"mov r15, [r14 + 0x78]",
"mov r14, [r14 + 0x70]",
"sub rsp, 0x8",
"popfq",
"ret",
in(reg) lucid_regsp,
);
}
}
Fault
时,我们可能在 Bochs 模式下运行,这意味着我们的堆栈是 Bochs 的堆栈,而不是 Lucid 的。因此,即使这在技术上只是一个上下文切换,我们仍然需要稍微改变顺序,将 Lucid 的已保存状态弹回当前状态并继续执行。现在,当 Lucid 调用进行上下文切换的函数时,它可以通过检查执行上下文中是否记录了Fault
来简单地检查这些函数的“返回”值,如下所示:// Start executing Bochs
prompt!("Starting Bochs...");
start_bochs(&mut lucid_context);// Check to see if any faults occurred during Bochs execution
if !matches!(lucid_context.fault, Fault::Success) {
fatal!(LucidErr::from_fault(lucid_context.fault));
}
四
沙盒化线程局部存储(TLS)
Fault
系统后,我注意到 Lucid 在退出时会发生段错误(segfault)。经过一些调试,我发现它正在调用一个函数指针,而该指针指向的是一个无效的地址。这怎么会发生呢?经过一番调查,我注意到就在那个函数调用之前,使用了fs
寄存器的偏移量从内存中加载了地址。通常,fs
用于访问 TLS。所以那时我强烈怀疑 Bochs 以某种方式破坏了我的fs
寄存器的值。因此,我通过grep
快速搜索了 Musl 中的fs
寄存器访问代码,发现了以下内容:/* Copyright 2011-2012 Nicholas J. Kain, licensed under standard MIT license */
.text
.global __set_thread_area
.hidden __set_thread_area
.type __set_thread_area,@function
__set_thread_area:
mov %rdi,%rsi /* shift for syscall */
movl $0x1002,%edi /* SET_FS register */
movl $158,%eax /* set fs segment to */
syscall /* arch_prctl(SET_FS, arg)*/
ret
__set_thread_area
使用内联系统调用指令调用arch_prctl
以直接操作fs
寄存器。这非常有意义,因为如果确实调用了系统调用指令,我们不会通过系统调用沙盒基础设施拦截它,因为我们从未对其进行过检测,我们只检测了 Musl 中syscall()
函数包装器的内容。因此,这会逃脱我们的沙盒并直接操作fs
。果然,我发现这个函数在src/env/__init_tls.c
中的 TLS 初始化期间被调用:int __init_tp(void *p)
{
pthread_t td = p;
td->self = td;
int r = __set_thread_area(TP_ADJ(p));
if (r < 0) return -1;
if (!r) libc.can_do_threads = 1;
td->detach_state = DT_JOINABLE;
td->tid = __syscall(SYS_set_tid_address, &__thread_list_lock);
td->locale = &libc.global_locale;
td->robust_list.head = &td->robust_list.head;
td->sysinfo = __sysinfo;
td->next = td->prev = td;
return 0;
}
__init_tp
函数中我们接收一个指针,然后调用TP_ADJ
宏对指针进行一些算术运算,并将该值传递给__set_thread_area
,这样就可以操作fs
寄存器。那么,如何对其进行沙盒化处理呢?我想避免直接修改__set_thread_area
中的内联汇编代码,因此我只是更改了源码,让 Musl 使用syscall()
包装函数,该函数会在后台调用我们经过检测的系统调用函数,如下所示:#ifndef ARCH_SET_FS
#define ARCH_SET_FS 0x1002
#endif /* ARCH_SET_FS */int __init_tp(void *p)
{
pthread_t td = p;
td->self = td;
int r = syscall(SYS_arch_prctl, ARCH_SET_FS, TP_ADJ(p));
//int r = __set_thread_area(TP_ADJ(p));
fs
的情况(尽管可能还会有!),我们应该就没问题了。我还调整了 Musl 代码,这样如果我们在 Lucid 下运行,我们可以通过执行上下文提供一个 TLS 区域,只需创建一个 Musl 所称的builtin_tls
的模拟区域:static struct builtin_tls {
char c;
struct pthread pt;
void *space[16];
} builtin_tls[1];
__init_tp
时,它给出的指针指向我们在执行上下文中创建的自己的 TLS 内存块,这样我们现在就可以在 Lucid 中访问errno
等内容:if (libc.tls_size > sizeof builtin_tls) {
#ifndef SYS_mmap2
#define SYS_mmap2 SYS_mmap
#endif
__asm__ __volatile__ ("int3"); // Added by me just in case
mem = (void *)__syscall(
SYS_mmap2,
0, libc.tls_size, PROT_READ|PROT_WRITE,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
/* -4095...-1 cast to void * will crash on dereference anyway,
* so don't bloat the init code checking for error codes and
* explicitly calling a_crash(). */
} else {
// Check to see if we're running under Lucid or not
if (!g_lucid_ctx) { mem = builtin_tls; }
else { mem = &g_lucid_ctx->tls; }
}/* Failure to initialize thread pointer is always fatal. */
if (__init_tp(__copy_tls(mem)) < 0)
a_crash();
#[repr(C)]
#[derive(Clone)]
pub struct Tls {
padding0: [u8; 8], // char c
padding1: [u8; 52], // Padding to offset of errno which is 52-bytes
pub errno: i32,
padding2: [u8; 144], // Additional padding to get to 200-bytes total
padding3: [u8; 128], // 16 void * values
}
read
系统调用期间,我们传递了一个 NULL 缓冲区,我们可以从 Lucid 中的系统调用处理程序返回一个错误代码,并适当地设置errno
:// Now we need to make sure the buffer passed to read isn't NULL
let buf_p = a2 as *mut u8;
if buf_p.is_null() {
context.tls.errno = libc::EINVAL;
return -1_i64 as u64;
}
fs
和gs
的访问尚未沙盒化,但我们还没有开发到那一部分。--static-pie
的普通 Bochs 对我来说就很困难。为了进一步复杂化 Bochs 的构建,我们需要基于自定义的 Musl 构建 Bochs,这意味着我们需要一个可以忽略常规标准 C 库并使用我们自定义 Musl libc 的编译器。对我来说,这证明是相当繁琐和困难的。一旦成功,我意识到这还不够。Bochs 作为一个 C++ 代码库,还需要访问标准的 C++ 库函数。像之前测试程序那样做是行不通的,因为我们没有一个基于自定义 Musl 构建的 C++ 库可用。libstdc++
,它是 gcc 项目的一部分。musl-cross-make
会下载所有组成工具链的组件,并从头开始创建一个工具链,该工具链将使用一个 Musl libc 和一个基于该 Musl 构建的libstdc++
。然后,对我们来说,唯一需要做的就是重新编译带有我们 Lucid 自定义补丁的 Musl libc,然后使用工具链编译--static-pie
的 Bochs。实际上,这很简单:1.克隆musl-cross-make
项目。
2.配置一个x86_64
工具链目标。
3.构建工具链。
4.进入其 Musl 目录,应用我们的 Musl 补丁。
5.配置 Musl 以构建/安装到musl-cross-make
输出目录。
6.重新构建 Musl libc。
7.配置 Bochs 以使用新工具链并设置--static-pie
标志。
#!/bin/sh CC="/home/h0mbre/musl_stuff/musl-cross-make/output/bin/x86_64-linux-musl-gcc"
CXX="/home/h0mbre/musl_stuff/musl-cross-make/output/bin/x86_64-linux-musl-g++"
CFLAGS="-Wall --static-pie -fPIE"
CXXFLAGS="$CFLAGS"export CC
export CXX
export CFLAGS
export CXXFLAGS./configure --enable-sb16 \
--enable-all-optimizations \
--enable-long-phy-address \
--enable-a20-pin \
--enable-cpu-level=6 \
--enable-x86-64 \
--enable-vmx=2 \
--enable-pci \
--enable-usb \
--enable-usb-ohci \
--enable-usb-ehci \
--enable-usb-xhci \
--enable-busmouse \
--enable-e1000 \
--enable-show-ips \
--enable-avx \
--with-nogui
brk
、mmap
和munmap
。我们的测试程序非常简单,因此我们还没有遇到这些系统调用。brk
和mmap
调用预分配两个内存池。我们还可以将 MMU 结构挂载到执行上下文中,以便在系统调用和上下文切换期间始终可以访问它。mmap
调用:// Structure to track memory usage in Bochs
#[derive(Clone)]
pub struct Mmu {
pub brk_base: usize, // Base address of brk region, never changes
pub brk_size: usize, // Size of the program break region
pub curr_brk: usize, // The current program breakpub mmap_base: usize, // Base address of the `mmap` pool
pub mmap_size: usize, // Size of the `mmap` pool
pub curr_mmap: usize, // The current `mmap` page base
pub next_mmap: usize, // The next allocation base address
}impl Mmu {
pub fn new() -> Result<Self, LucidErr> {
// We don't care where it's mapped
let addr = std::ptr::null_mut::<libc::c_void>();// Straight-forward
let length = (DEFAULT_BRK_SIZE + DEFAULT_MMAP_SIZE) as libc::size_t;// This is normal
let prot = libc::PROT_WRITE | libc::PROT_READ;// This might change at some point?
let flags = libc::MAP_ANONYMOUS | libc::MAP_PRIVATE;// No file backing
let fd = -1 as libc::c_int;// No offset
let offset = 0 as libc::off_t;// Try to `mmap` this block
let result = unsafe {
libc::mmap(
addr,
length,
prot,
flags,
fd,
offset
)
};if result == libc::MAP_FAILED {
return Err(LucidErr::from("Failed `mmap` memory for MMU"));
}// Create MMU
Ok(Mmu {
brk_base: result as usize,
brk_size: DEFAULT_BRK_SIZE,
curr_brk: result as usize,
mmap_base: result as usize + DEFAULT_BRK_SIZE,
mmap_size: DEFAULT_MMAP_SIZE,
curr_mmap: result as usize + DEFAULT_BRK_SIZE,
next_mmap: result as usize + DEFAULT_BRK_SIZE,
})
}
brk(0)
,这会返回当前的程序断点地址,然后如果程序想要 2 页额外的内存,它会调用brk(base + 0x2000)
,你可以在 Bochs 的strace
输出中看到这一点:[devbox:~/bochs/bochs-2.7]$ strace ./bochs
execve("./bochs", ["./bochs"], 0x7ffda7f39ad0 /* 45 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x7fd071a738a8) = 0
set_tid_address(0x7fd071a739d0) = 289704
brk(NULL) = 0x555555d7c000
brk(0x555555d7e000) = 0x555555d7e000
brk
有以下逻辑:// brk
0xC => {
// Try to update the program break
if context.mmu.update_brk(a1).is_err() {
fault!(contextp, Fault::InvalidBrk);
}// Return the program break
context.mmu.curr_brk as u64
},
Mmu
实现的update_brk
方法的包装器,所以让我们来看看:// Logic for handling a `brk` syscall
pub fn update_brk(&mut self, addr: usize) -> Result<(), ()> {
// If addr is NULL, just return nothing to do
if addr == 0 { return Ok(()); }// Check to see that the new address is in a valid range
let limit = self.brk_base + self.brk_size;
if !(self.curr_brk..limit).contains(&addr) { return Err(()); }// So we have a valid program break address, update the current break
self.curr_brk = addr;Ok(())
}
a1
中得到一个 NULL 参数,我们什么都不需要做,当前的 MMU 状态不需要调整,我们只需返回当前的程序断点。如果我们得到一个非 NULL 参数,我们进行合理性检查,以确保我们的brk
内存池足够大以满足请求,如果可以,我们调整当前的程序断点并将其返回给调用者。mmap
调用,我们需要跟踪更多的状态,因为实质上发生了我们需要记住的“分配”。大多数mmap
调用将有一个 NULL 地址参数,因为它们不关心内存映射在虚拟内存中的位置,在这种情况下,我们默认使用为 Mmu 实现的主要方法do_mmap
:// If a1 is NULL, we just do a normal mmap
if a1 == 0 {
if context.mmu.do_mmap(a2, a3, a4, a5, a6).is_err() {
fault!(contextp, Fault::InvalidMmap);
}// Succesful regular mmap
return context.mmu.curr_mmap as u64;
}
// Logic for handling a `mmap` syscall with no fixed address support
pub fn do_mmap(
&mut self,
len: usize,
prot: usize,
flags: usize,
fd: usize,
offset: usize
) -> Result<(), ()> {
// Page-align the len
let len = (len + PAGE_SIZE - 1) & !(PAGE_SIZE - 1);// Make sure we have capacity left to satisfy this request
if len + self.next_mmap > self.mmap_base + self.mmap_size {
return Err(());
}// Sanity-check that we don't have any weird `mmap` arguments
if prot as i32 != libc::PROT_READ | libc::PROT_WRITE {
return Err(())
}if flags as i32 != libc::MAP_PRIVATE | libc::MAP_ANONYMOUS {
return Err(())
}if fd as i64 != -1 {
return Err(())
}if offset != 0 {
return Err(())
}// Set current to next, and set next to current + len
self.curr_mmap = self.next_mmap;
self.next_mmap = self.curr_mmap + len;// curr_mmap now represents the base of the new requested allocation
Ok(())
}
mmap
内存池有足够的容量来满足分配需求,我们检查其他参数是否符合预期,然后我们简单地更新当前偏移量和下一个偏移量。这样我们就知道下次从哪里分配,同时也可以将当前的分配基址返回给调用者。mmap
会以非 NULL 地址和MAP_FIXED
标志调用,这意味着地址对调用者很重要,映射应该发生在提供的虚拟地址上。目前,这种情况发生在 Bochs 进程的早期:[devbox:~/bochs/bochs-2.7]$ strace ./bochs
execve("./bochs", ["./bochs"], 0x7ffda7f39ad0 /* 45 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x7fd071a738a8) = 0
set_tid_address(0x7fd071a739d0) = 289704
brk(NULL) = 0x555555d7c000
brk(0x555555d7e000) = 0x555555d7e000
mmap(0x555555d7c000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x555555d7c000
brk
内存池中。我们已经知道这块内存,并且已经创建了它,所以你上面看到的最后一个mmap
调用对我们来说相当于一个空操作(NOP),我们只需将地址返回给调用者即可。brk
内存池的MAP_FIXED
调用。munmap
,我们也将此操作视为 NOP,并向用户返回成功,因为目前我们不关注释放或重用内存。brk
和mmap
调用,而我们的模糊测试器现在能够通过我们的 MMU 处理它们:...
brk(NULL) = 0x555555d7c000
brk(0x555555d7e000) = 0x555555d7e000
mmap(0x555555d7c000, 4096, PROT_NONE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x555555d7c000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bde000
mmap(NULL, 16384, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bda000
mmap(NULL, 4194324, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd06f7ff000
mmap(NULL, 73728, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bc8000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bc7000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bc6000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bc5000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bc4000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bc3000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bc2000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bc1000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bc0000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bbe000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bbd000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bbc000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bbb000
munmap(0x7fd071bbb000, 4096) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bbb000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bba000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb9000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb8000
brk(0x555555d7f000) = 0x555555d7f000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb6000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb5000
munmap(0x7fd071bb5000, 4096) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb5000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb4000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb3000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb2000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb1000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bb0000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071baf000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bae000
munmap(0x7fd071bae000, 4096) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bae000
munmap(0x7fd071bae000, 4096) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bae000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bad000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071bab000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071baa000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071ba8000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071ba7000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071ba6000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071ba5000
munmap(0x7fd071ba5000, 4096) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071ba5000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071ba3000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071ba1000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071ba0000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071b9e000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071b9d000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071b9b000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071b99000
munmap(0x7fd071b99000, 8192) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071b99000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071b97000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071b96000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071b94000
munmap(0x7fd071b94000, 8192) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fd071b94000
...
open(".bochsrc", O_RDONLY|O_LARGEFILE) = 3
close(3) = 0
writev(2, [{iov_base="00000000000i[ ] ", iov_len=21}, {iov_base=NULL, iov_len=0}], 200000000000i[ ] ) = 21
writev(2, [{iov_base="reading configuration from .boch"..., iov_len=36}, {iov_base=NULL, iov_len=0}], 2reading configuration from .bochsrc
) = 36
open(".bochsrc", O_RDONLY|O_LARGEFILE) = 3
read(3, "# You may now use double quotes "..., 1024) = 1024
read(3, "================================"..., 1024) = 1024
read(3, "ig_interface: win32config\n#confi"..., 1024) = 1024
read(3, "ace to AT&T's VNC viewer, cross "..., 1024) = 1024
#[derive(Clone)]
pub struct FileTable {
files: Vec<File>,
}impl FileTable {
// We will attempt to open and read all of our required files ahead of time
pub fn new() -> Result<Self, LucidErr> {
// Retrieve .bochsrc
let args: Vec<String> = std::env::args().collect();// Check to see if we have a "--bochsrc-path" argument
if args.len() < 3 || !args.contains(&"--bochsrc-path".to_string()) {
return Err(LucidErr::from("No `--bochsrc-path` argument"));
}// Search for the value
let mut bochsrc = None;
for (i, arg) in args.iter().enumerate() {
if arg == "--bochsrc-path" {
if i >= args.len() - 1 {
return Err(
LucidErr::from("Invalid `--bochsrc-path` value"));
}bochsrc = Some(args[i + 1].clone());
break;
}
}if bochsrc.is_none() { return Err(
LucidErr::from("No `--bochsrc-path` value provided")); }
let bochsrc = bochsrc.unwrap();// Try to read the file
let Ok(data) = read(&bochsrc) else {
return Err(LucidErr::from(
&format!("Unable to read data BLEGH from '{}'", bochsrc)));
};// Create a file now for .bochsrc
let bochsrc_file = File {
fd: 3,
path: ".bochsrc".to_string(),
contents: data.clone(),
cursor: 0,
};// Insert the file into the FileTable
Ok(FileTable {
files: vec![bochsrc_file],
})
}// Attempt to open a file
pub fn open(&mut self, path: &str) -> Result<i32, ()> {
// Try to find the requested path
for file in self.files.iter() {
if file.path == path {
return Ok(file.fd);
}
}// We didn't find the file, this really should never happen?
Err(())
}// Look a file up by fd and then return a mutable reference to it
pub fn get_file(&mut self, fd: i32) -> Option<&mut File> {
self.files.iter_mut().find(|file| file.fd == fd)
}
}#[derive(Clone)]
pub struct File {
pub fd: i32, // The file-descriptor Bochs has for this file
pub path: String, // The file-path for this file
pub contents: Vec<u8>, // The actual file contents
pub cursor: usize, // The current cursor in the file
}
FileTable
中是否有正确的文件,然后从File::contents
缓冲区中读取其内容,并更新游标结构成员以跟踪我们在文件中的当前偏移量。// read
0x0 => {
// Check to make sure we have the requested file-descriptor
let Some(file) = context.files.get_file(a1 as i32) else {
println!("Non-existent file fd: {}", a1);
fault!(contextp, Fault::NoFile);
};// Now we need to make sure the buffer passed to read isn't NULL
let buf_p = a2 as *mut u8;
if buf_p.is_null() {
context.tls.errno = libc::EINVAL;
return -1_i64 as u64;
}// Adjust read size if necessary
let length = std::cmp::min(a3, file.contents.len() - file.cursor);// Copy the contents over to the buffer
unsafe {
std::ptr::copy(
file.contents.as_ptr().add(file.cursor), // src
buf_p, // dst
length); // len
}// Adjust the file cursor
file.cursor += length;// Success
length as u64
},
open
调用基本上只是作为合理性检查,以确保我们知道 Bochs 正在尝试访问什么:// open
0x2 => {
// Get pointer to path string we're trying to open
let path_p = a1 as *const libc::c_char;// Make sure it's not NULL
if path_p.is_null() {
fault!(contextp, Fault::NullPath);
}// Create c_str from pointer
let c_str = unsafe { std::ffi::CStr::from_ptr(path_p) };// Create Rust str from c_str
let Ok(path_str) = c_str.to_str() else {
fault!(contextp, Fault::InvalidPathStr);
};// Validate permissions
if a2 as i32 != 32768 {
println!("Unhandled file permissions: {}", a2);
fault!(contextp, Fault::Syscall);
}// Open the file
let fd = context.files.open(path_str);
if fd.is_err() {
println!("Non-existent file path: {}", path_str);
fault!(contextp, Fault::NoFile);
}// Success
fd.unwrap() as u64
},
// Attempt to open a file
pub fn open(&mut self, path: &str) -> Result<i32, ()> {
// Try to find the requested path
for file in self.files.iter() {
if file.path == path {
return Ok(file.fd);
}
}// We didn't find the file
Err(())
}
五
结论
看雪ID:pureGavin
https://bbs.kanxue.com/user-home-777502.htm
# 往期推荐
2、恶意木马历险记
球分享
球点赞
球在看
点击阅读原文查看更多