Sandboxie源码解读:进程钩子与系统调用拦截实现

Sandboxie源码解读:进程钩子与系统调用拦截实现

【免费下载链接】Sandboxie Sandboxie Plus & Classic 【免费下载链接】Sandboxie 项目地址: https://gitcode.com/gh_mirrors/sa/Sandboxie

1. 引言:沙箱技术的底层基石

你是否曾好奇,当你在沙箱中运行可疑程序时,系统是如何拦截其恶意行为的?Sandboxie作为一款经典的应用程序隔离工具,其核心能力源于对Windows内核机制的深度操控。本文将带你深入Sandboxie的内核驱动层,解析进程钩子(Process Hook)与系统调用(Syscall)拦截的实现原理,揭示沙箱隔离的底层技术细节。

读完本文,你将掌握:

  • Sandboxie进程钩子的构建与注入机制
  • 系统调用拦截表的初始化与管理流程
  • 32位/64位架构下的钩子实现差异
  • 沙箱隔离的核心技术原理与代码实现

2. 进程钩子:用户态与内核态的桥梁

2.1 钩子入口构建:Trampoline技术解析

Sandboxie通过Process_BuildHookEntry函数构建钩子入口点,该函数位于Sandboxie/core/drv/process_hook.c文件中。其核心是创建一个蹦床(Trampoline)结构,用于拦截目标函数调用:

ULONG_PTR Process_BuildHookEntry(ULONG_PTR NewProc, ULONG_PTR OldProc, ULONG *IncPtr) {
    HOOK_TRAMP *tramp;
    UCHAR *code;
    ULONG_PTR *pOldProc;

    tramp = Hook_BuildTramp(NULL, NULL, FALSE, FALSE);
    if (!tramp) return 0;
    tramp = HOOK_TRAMP_CODE_TO_TRAMP_HEAD(tramp);

    tramp->eyecatcher = tzuk;  // 用于验证的魔术值
    tramp->target = (void *)NewProc;  // 钩子处理函数地址

    code = &tramp->code[0];
    pOldProc = (ULONG_PTR *)&tramp->code[sizeof(tramp->code) - 8];

#ifdef _WIN64
    code[0] = 0xFB;  // sti指令,重新启用中断
    ++code;
#endif

    // 构建跳转代码...
    return (ULONG_PTR)&tramp->code;
}

蹦床结构包含以下关键部分:

  • 魔术值验证eyecatcher = tzuk用于后续验证蹦床有效性
  • 钩子目标地址target字段存储新的处理函数地址
  • 机器码区域code数组存储动态生成的跳转指令

2.2 钩子代码生成:32位与64位架构适配

Sandboxie的钩子实现需要兼容32位和64位Windows系统,这通过条件编译实现:

#ifdef _WIN64
#define PREFIX64() code[0] = 0x48; ++code;  // 64位前缀
#else
#define PREFIX64()
#endif

// 加载Process_FindSandboxed函数地址到寄存器
PREFIX64();
code[0] = 0xB8;  // mov rax/eax, address
#ifdef _WIN64
*(ULONG_PTR *)&code[1] = (ULONG_PTR)Process_FindSandboxed64;
#else
*(ULONG_PTR *)&code[1] = (ULONG_PTR)Process_FindSandboxed;
#endif
code += 1 + sizeof(ULONG_PTR);

64位架构下的特殊处理:

  • 增加sti指令重新启用中断
  • 使用0x48前缀指定64位操作数
  • 调用Process_FindSandboxed64专用函数

32位架构下的额外处理:

  • 不需要中断重新启用指令
  • 函数调用前需要压入两个0作为参数

2.3 钩子禁用机制:动态热修补

Sandboxie实现了钩子的动态禁用功能,通过Process_DisableHookEntry函数修改钩子代码:

void Process_DisableHookEntry(ULONG_PTR HookEntry) {
    HOOK_TRAMP *tramp = HOOK_TRAMP_CODE_TO_TRAMP_HEAD(HookEntry);
    UCHAR *code = &tramp->code[0];
    UCHAR *test;
    UCHAR hotpatch[2];

#ifdef _WIN64
    ++code;  // 跳过sti指令
#endif

    test = code;
    // 定位test eax,eax指令位置
    test += 2;  // 跳过mov eax,eax
    // 处理可选的inc指令
    if (*test == 0x90)
        test += 1 + PREFIX64 + 1 + sizeof(ULONG_PTR) + 2;
    // 跳过mov eax, Process_Find
    test += PREFIX64 + 1 + sizeof(ULONG_PTR);
#ifndef _WIN64
    test += 4;  // 跳过两个push 0指令
#endif
    test += 2;  // 跳过程序调用

    // 将test eax,eax改为xor eax,eax,始终跳转原函数
    *(test + PREFIX64) = 0x33;

    // 热修补跳转
    hotpatch[0] = 0xEB;  // jmp short
    hotpatch[1] = (UCHAR)(test - (code + 2));
    *(USHORT *)code = *(USHORT *)&hotpatch[0];
}

禁用钩子的核心原理:

  1. 定位钩子代码中的test eax,eax指令
  2. 将其修改为xor eax,eax,使条件跳转始终成立
  3. 添加短跳转指令,绕过钩子逻辑直接执行原函数

3. 系统调用拦截:内核级监控的实现

3.1 系统调用表初始化流程

Sandboxie的系统调用拦截通过Syscall_Init函数初始化,位于Sandboxie/core/drv/syscall.c文件中,其流程如下:

mermaid

关键初始化函数Syscall_Init_List负责扫描NTDLL导出的系统调用函数:

BOOLEAN Syscall_Init_List(void) {
    List_Init(&Syscall_List);
    
    // 加载NTDLL并扫描导出函数
    dll = Dll_Load(Dll_NTDLL);
    if (!dll) return FALSE;
    
    proc_offset = Dll_GetNextProc(dll, "Zw", &name, &proc_index);
    while (proc_offset) {
        if (name[0] != 'Z' || name[1] != 'w') break;
        name += 2;  // 跳过"Zw"前缀
        
        // 过滤不需要拦截的系统调用
        if (IS_PROC_NAME(8, "Continue") || 
            IS_PROC_NAME(14, "CallbackReturn") ||
            IS_PROC_NAME(14, "RaiseException")) {
            goto next_zwxxx;
        }
        
        // 分析系统调用索引
        ntdll_code = Dll_RvaToAddr(dll, proc_offset);
        syscall_index = Syscall_GetIndexFromNtdll(ntdll_code);
        
        // 创建并初始化SYSCALL_ENTRY结构
        entry = Mem_AllocEx(Driver_Pool, entry_len, TRUE);
        entry->syscall_index = (USHORT)syscall_index;
        entry->param_count = (USHORT)param_count;
        entry->ntdll_offset = proc_offset;
        entry->ntos_func = ntos_addr;
        List_Insert_After(&Syscall_List, NULL, entry);
        
        next_zwxxx:
        proc_offset = Dll_GetNextProc(dll, NULL, &name, &proc_index);
    }
    return TRUE;
}

3.2 系统调用索引解析

Syscall_GetIndexFromNtdll函数负责从NTDLL的ZwXxx函数代码中提取系统调用号:

ULONG Syscall_GetIndexFromNtdll(UCHAR *code) {
    // 32位系统调用指令模式: mov eax, index; sysenter
    if (code[0] == 0xB8 && code[5] == 0x0F && code[6] == 0x34) {
        return *(ULONG *)&code[1];
    }
    
    // 64位系统调用指令模式: mov rax, index; syscall
    if (code[0] == 0x48 && code[1] == 0xB8 && code[10] == 0x0F && code[11] == 0x05) {
        return *(ULONG *)&code[2];
    }
    
    // 其他模式处理...
    return -1;
}

该函数通过识别x86/x64汇编指令模式,从NTDLL导出函数中提取系统调用号,为后续拦截做准备。

3.3 系统调用处理流程

Sandboxie通过Syscall_Invoke函数调用原始系统服务,并通过注册的处理函数进行拦截:

NTSTATUS Syscall_Invoke(SYSCALL_ENTRY *entry, ULONG_PTR *stack) {
    NTSTATUS status;
    
    __try {
        status = Sbie_InvokeSyscall_asm(entry->ntos_func, entry->param_count, stack);
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        status = GetExceptionCode();
    }
    
    return status;
}

系统调用拦截的核心处理在Syscall_Api_Invoke函数中实现:

NTSTATUS Syscall_Api_Invoke(PROCESS *proc, ULONG64 *parms) {
    // 检查调用者权限和参数
    if (!proc) return STATUS_NOT_IMPLEMENTED;
    syscall_index = (ULONG)parms[1];
    
    // 查找系统调用表项
    entry = Syscall_Table[syscall_index];
    if (!entry) return STATUS_INVALID_SYSTEM_SERVICE;
    
    // 调用拦截处理函数
    if (entry->handler1_func && !proc->open_all_nt) {
        status = entry->handler1_func(proc, entry, user_args);
    } else {
        status = Syscall_Invoke(entry, user_args);
    }
    
    return status;
}

3.4 特定系统调用的拦截处理

Sandboxie为关键系统调用注册了专用处理函数,例如文件操作和进程管理相关调用:

// 注册系统调用处理函数
Syscall_Set1("DuplicateObject", Syscall_DuplicateHandle);
Syscall_Set1("GetNextProcess", Syscall_GetNextProcess);
Syscall_Set1("GetNextThread", Syscall_GetNextThread);
Syscall_Set1("DeviceIoControlFile", Syscall_DeviceIoControlFile);

Syscall_DuplicateHandle为例,其实现了句柄复制操作的拦截与重定向:

NTSTATUS Syscall_DuplicateHandle(PROCESS *proc, SYSCALL_ENTRY *syscall_entry, ULONG_PTR *user_args) {
    // 解析参数
    HANDLE SourceProcessHandle = (HANDLE)user_args[0];
    HANDLE SourceHandle = (HANDLE)user_args[1];
    HANDLE TargetProcessHandle = (HANDLE)user_args[2];
    PHANDLE TargetHandle = (PHANDLE)user_args[3];
    ACCESS_MASK DesiredAccess = (ACCESS_MASK)user_args[4];
    ULONG Options = (ULONG)user_args[5];
    
    // 检查是否需要重定向
    if (SourceProcessHandle == NtCurrentProcess() && 
        Obj_IsSandboxedHandle(proc, SourceHandle)) {
        // 沙箱内句柄处理...
        return STATUS_SUCCESS;
    }
    
    // 调用原始系统服务
    return Syscall_Invoke(syscall_entry, user_args);
}

4. 架构差异:32位与64位实现对比

4.1 钩子代码结构差异

架构指令前缀中断处理地址长度调用约定
32位不需要显式启用4字节stdcall
64位0x48前缀需要sti指令启用8字节fastcall

4.2 系统调用处理差异

32位系统调用拦截:

// syscall_32.c
VOID Syscall_Dispatch32(ULONG index, ULONG_PTR *args) {
    SYSCALL_ENTRY *entry = Syscall_Table[index];
    if (entry && entry->handler1_func) {
        status = entry->handler1_func(proc, entry, args);
    } else {
        // 调用原始系统服务
        status = Native32_Syscall(index, args);
    }
}

64位系统调用拦截:

// syscall_64.c
VOID Syscall_Dispatch64(ULONG index, ULONG_PTR *args) {
    PROCESS *proc = Thread_GetProcess();
    SYSCALL_ENTRY *entry = Syscall_Table[index];
    
    // 64位特定的参数验证
    ProbeForRead(args, entry->param_count * sizeof(ULONG_PTR), 8);
    
    if (entry && entry->handler1_func) {
        status = entry->handler1_func(proc, entry, args);
    } else {
        // 调用原始系统服务
        status = Native64_Syscall(index, args);
    }
}

5. 实战分析:进程创建拦截流程

NtCreateProcessEx系统调用为例,分析Sandboxie的拦截处理流程:

mermaid

拦截处理的核心代码位于process_hook.csyscall.c中,通过以下步骤实现:

  1. 进程检查Process_FindSandboxed确定调用进程是否在沙箱中
  2. 参数过滤:检查并修改进程创建参数,如路径重定向
  3. 权限验证:验证沙箱进程是否有权限创建新进程
  4. 资源重定向:将文件系统/注册表访问重定向到沙箱目录
  5. 原始调用:使用过滤后的参数调用原始系统服务

6. 总结与展望

Sandboxie通过进程钩子和系统调用拦截技术,构建了一个高效的应用程序隔离环境。其核心实现依赖于对Windows内核机制的深入理解和精巧的汇编级代码操控。

关键技术点总结:

  • 蹦床钩子:使用动态生成的汇编代码实现函数拦截
  • 系统调用表:通过解析NTDLL函数构建完整的系统调用映射
  • 条件拦截:基于进程沙箱状态决定是否启用拦截逻辑
  • 架构适配:同时支持32位和64位Windows系统

未来可能的改进方向:

  • 增加对Windows最新版本内核保护机制的适配
  • 优化系统调用拦截性能,减少沙箱运行开销
  • 增强钩子代码的抗检测能力,提高与安全软件的兼容性

通过本文的解析,相信你已经对Sandboxie的底层实现有了深入了解。这些技术不仅适用于沙箱隔离,也可应用于系统监控、安全审计等领域,具有广泛的参考价值。

要进一步探索Sandboxie的实现细节,可以重点研究以下文件:

  • Sandboxie/core/drv/process_hook.c - 进程钩子实现
  • Sandboxie/core/drv/syscall.c - 系统调用拦截
  • Sandboxie/core/drv/obj.c - 对象管理与重定向
  • Sandboxie/core/drv/file.c - 文件系统虚拟化

【免费下载链接】Sandboxie Sandboxie Plus & Classic 【免费下载链接】Sandboxie 项目地址: https://gitcode.com/gh_mirrors/sa/Sandboxie

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值