Win11环境下使用BYOVD方法破解SandboxiePlus验证

查看 52|回复 8
作者:暴龙兽   
前几天有评论说win11环境下之前的方法不行,试了一下确实不行。主要原因还是echo_driver.sys驱动加载失败了,返回的错误码是0x800B010C,对应的含义如下:


error_code.png (128.67 KB, 下载次数: 0)
下载附件
错误码
2025-9-22 09:40 上传

需要重新寻找新的具有任意地址读写漏洞的具有合法签名驱动文件。
经过一段时间的寻找,功夫不负有心人,终于找到了一个符合需求的项目BYOVD_read_write_primitive,相关项目地址如下:https://github.com/0xJs/BYOVD_read_write_primitive。重新编写程序的过程中,发现一个有意思的问题:win11环境下,管理员执行NtQuerySystemInformation获取SbieDrv驱动模块基地址的竟然是0,而在win10环境下符合预期。
本篇文章的第一部分主要探究这个问题,第二部分给出基于BYOVD_read_write_primitive的破解思路。
NtQuerySystemInformation系统调用问题
破解程序使用该系统调用获取SbieDrv驱动模块的基地址,它的函数原型如下:
NTSTATUS NtQuerySystemInformation(
  [in]            SYSTEM_INFORMATION_CLASS SystemInformationClass,
  [in, out]       PVOID                    SystemInformation,
  [in]            ULONG                    SystemInformationLength,
  [out, optional] PULONG                   ReturnLength
);
第一个参数为SystemModuleInformation(11)时,获取当前所有驱动模块的信息,比较文件名即可获取SbieDrv驱动模块的基地址。
Win10环境
当NtQuerySystemInformation系统调用的第一个参数为SystemModuleInformation时,NtQuerySystemInformation会调用ExpQuerySystemInformation函数,该函数相关的代码如下:


enum_module.png (25.38 KB, 下载次数: 0)
下载附件
函数代码
2025-9-22 09:44 上传

  • ExIsRestrictedCaller的IDA F5反汇编代码如下:

    _BOOL8 __fastcall ExIsRestrictedCaller(char a1)
    {
      BOOLEAN v1; // bl
      _BOOL8 result; // rax
      struct _SECURITY_SUBJECT_CONTEXT SubjectSecurityContext; // [rsp+50h] [rbp-28h] BYREF
      NTSTATUS AccessStatus; // [rsp+80h] [rbp+8h] BYREF
      ACCESS_MASK GrantedAccess; // [rsp+88h] [rbp+10h] BYREF
      AccessStatus = 0;
      GrantedAccess = 0;
      memset(&SubjectSecurityContext, 0, sizeof(SubjectSecurityContext));
      result = 0;
      if ( a1 )
      {
        SeCaptureSubjectContext(&SubjectSecurityContext);
        v1 = SeAccessCheck(
               SeMediumDaclSd,  // SecurityDescriptor
               &SubjectSecurityContext, // SubjectSecurityContext
               0,   // SubjectContextLocked
               0x20000u,    // DesiredAccess, READ_CONTROL ->
               0,           // PreviouslyGrantedAccess
               0i64,        // Privileges
               (PGENERIC_MAPPING)&ExpRestrictedGenericMapping,  // GenericMapping
               1,   // AccessMode,KernelMode -> 0, UserMode -> 1
               &GrantedAccess,  // GrantedAccess
               &AccessStatus);  // AccessStatus
        SeReleaseSubjectContext(&SubjectSecurityContext);
        if ( !v1 || AccessStatus
    如果ExIsRestrictedCaller返回一个true,则NtQuerySystemInformatio n系统调用返回一个0xC0000022,表示STATUS_ACCESS_DENIED。管理员启动一个进程的话,ExIsRestrictedCaller返回一个False,非管理员启动则返回一个True。
  • 接下来执行ExpQueryModuleInformation函数,



    ExpQueryModuleInformation_win10_ida.png (84.36 KB, 下载次数: 0)
    下载附件
    IDA ExpQueryModuleInformation_win10
    2025-9-22 09:47 上传



    ExpQueryModuleInformation_win10_windbg.png (65.67 KB, 下载次数: 0)
    下载附件
    windbg ExpQueryModuleInformation_win10
    2025-9-22 09:47 上传

    从PsLoadModuleList链表中遍历已经加载的内核模块,提取信息赋值给NtQuerySystemInformation的第二个参数SystemInformation,它是一个PRTL_PROCESS_MODULES结构体。该结构体的内容如下:
    typedef struct _RTL_PROCESS_MODULE_INFORMATION
    {
        HANDLE Section;
        PVOID MappedBase;
        PVOID ImageBase;
        ULONG ImageSize;
        ULONG Flags;
        USHORT LoadOrderIndex;
        USHORT InitOrderIndex;
        USHORT LoadCount;
        USHORT OffsetToFileName;
        UCHAR FullPathName[256];
    } RTL_PROCESS_MODULE_INFORMATION, * PRTL_PROCESS_MODULE_INFORMATION;
    typedef struct _RTL_PROCESS_MODULES
    {
        ULONG NumberOfModules;
        RTL_PROCESS_MODULE_INFORMATION Modules[1];
    }
    //
    总结:在win10环境下,只要使用管理员启动进程来调用NtQuerySyst emInformation系统调用即可获取所有内核模块的的信息,包括基地址。
    Win11环境
    Win11下NtQuerySystemInformation系统调用的执行和win10相似,主要在ExIsRestrictedCaller和ExpQueryModuleInformation函数中有些不同。
  • ExIsRestrictedCaller函数的不同,增加了是否拥有SeDebugPrivilege权限的验证。

    __int64 __fastcall ExIsRestrictedCaller(KPROCESSOR_MODE a1, _DWORD *a2)
    {
      unsigned int v2; // edi
      BOOLEAN v5; // bl
      struct _SECURITY_SUBJECT_CONTEXT SubjectContext; // [rsp+50h] [rbp-28h] BYREF
      NTSTATUS AccessStatus; // [rsp+80h] [rbp+8h] BYREF
      ACCESS_MASK GrantedAccess; // [rsp+88h] [rbp+10h] BYREF
      v2 = 0;
      AccessStatus = 0;
      GrantedAccess = 0;
      memset(&SubjectContext, 0, sizeof(SubjectContext));
      if ( a2 )
        *a2 = 0;
      if ( !a1 )
        return 0i64;
      // 不同之处,新增
      if ( a2 && (unsigned int)Feature_RestrictKernelAddressLeaks__private_IsEnabledDeviceUsageNoInline() )
        *a2 = SeSinglePrivilegeCheck(SeDebugPrivilege, a1) == 0;   
      SeCaptureSubjectContext(&SubjectContext);
      v5 = SeAccessCheck(
             SeMediumDaclSd,
             &SubjectContext,
             0,
             0x20000u,
             0,
             0i64,
             (PGENERIC_MAPPING)&ExpRestrictedGenericMapping,
             1,
             &GrantedAccess,
             &AccessStatus);
      SeReleaseSubjectContext(&SubjectContext);
      if ( !v5 )
        return 1i64;
      LOBYTE(v2) = AccessStatus
    如果拥有SeDebugPrivilege权限,将a2指针处设置为False,否则为True。从Feature_RestrictKernelAddressLeaks__private_IsEnabled DeviceUsageNoInline这个符号名也可以看出,此处是为了限制非SeDebugPrivilege权限的进程获取内核模块的基地址的。
  • ExpQueryModuleInformation函数的不同,ExIsRestrictedCaller函数验证通过之后,会将a2指针处的值传递给ExpQueryModuleInforma tion函数的第一个参数。

    __int64 __fastcall ExpQueryModuleInformation(int a1, _DWORD *a2, unsigned int a3, _DWORD *a4)
    {
      __int64 result; // rax
      unsigned int v8; // ecx
      _QWORD v9[2]; // [rsp+30h] [rbp-38h] BYREF
      unsigned int v10; // [rsp+40h] [rbp-28h]
      int v11; // [rsp+44h] [rbp-24h]
      _DWORD *v12; // [rsp+48h] [rbp-20h]
      int v13; // [rsp+50h] [rbp-18h]
      int v14; // [rsp+54h] [rbp-14h]
      v9[0] = 0i64;
      v13 = a1;
      v14 = 0;
      v12 = a4;
      v11 = 8;
      v9[1] = a2 + 2;
      v10 = a3;
      // v9是一个结构体,
      result = MmEnumerateSystemImagesShared(ExpQueryModuleInformationImage, v9);   
      v8 = 0xC0000004;
      if ( (int)(result + 0x80000000) = 8 )
        {
          *a2 = v14;
          return LODWORD(v9[0]);
        }
        return v8;
      }
      return result;
    }
    __int64 __fastcall MmEnumerateSystemImagesShared(__int64 a1, __int64 a2)
    {
      return MiEnumerateSystemImages(a1, a2, 2i64);
    }
  • MiEnumerateSystemImages函数的反汇编代码如下:

    // a1 -> ExpQueryModuleInformationImage函数
    // a2 -> ExpQueryModuleInformation的v9
    __int64 __fastcall MiEnumerateSystemImages(__int64 a1, __int64 a2, __int64 a3)
    {
      struct _KTHREAD *CurrentThread; // r9
      int v4; // edi
      unsigned int v5; // r12d
      __int64 Lock; // rax
      PVOID *v8; // rbx
      __int64 v9; // rbp
      __int64 v10; // r8
      __int64 v11; // r9
      PVOID *i; // rbx
      CurrentThread = KeGetCurrentThread();
      v4 = 0;
      v5 = a3;
      if ( (struct _KTHREAD *)qword_140E2D610 == CurrentThread )
      {
        for ( i = (PVOID *)PsLoadedModuleList; i != &PsLoadedModuleList; i = (PVOID *)*i )
        {
          v4 = guard_dispatch_icall_no_overrides(i, a2, a3, CurrentThread); //
          if ( v4
    这个函数就是遍历PsLoadedModuleList链表中的内核模块,每一个模块都调用一次ExpQueryModuleInformationImage函数。ExpQueryModuleInformationImage函数伪代码如下:


    ExpQueryModuleInformationImage_win11_ida.png (56.69 KB, 下载次数: 0)
    下载附件
    IDA ExpQueryModuleInformationImage_win11
    2025-9-22 09:52 上传

    其实a2就是v9,那a2+0x20其实就是ExpQueryModuleInformation函数的 _QWORD v9[2]; // [rsp+30h] [rbp-38h] BYREF加0x20,就是 int v13; // [rsp+50h] [rbp-18h]。这就是说,如果拥有SeDebugPrivilege就将内核模块的地址透出,否则置为空。
    总结:win11环境下除了需要管理员权限启动进程之外,还需要额外获取SeDebugPrivilege权限才能破解成功。设想一下,补丁程序在win11上面运行,管理员权限启动,发现破解失败;这时你需要使用调试器去定位问题,发现又可以破解成功,这就很让人迷糊。当你使用管理员权限启动一个调试器创建管理员进程,进行分析,其实被调试的进程已经具备了SeDebugPrivilege权限,但如果你附加调试,目标进程的权限位并未发生变化。


    SeDebugPrivilege.png (196.71 KB, 下载次数: 0)
    下载附件
    SeDebugPrivilege
    2025-9-22 09:53 上传

    而问题的真正原因呢,还是在内核,如果不逆向分析内核,无法得出定位出。
    破解程序代码
  • 第一步:获取Debug权限

    bool GetDebugPrivilege()
    {
        HANDLE hToken;
        LUID luid;
        TOKEN_PRIVILEGES tokenPrivileges;
        if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
        {
            std::cerr
  • 第二步:获取Verify_CertInfo的地址。getVerifyCertInfoOffset获取Verify_CertInfo关键验证变量的偏移
    // sysPath -> 驱动文件的位置
    // pdbPath -> 驱动文件pdb的位置
    uint64_t getVerifyCertInfoOffset(const wchar_t* sysPath, const wchar_t* pdbPath)
    {
    uint64_t res = 0;
    SymInitialize(GetCurrentProcess(), NULL, FALSE);
    // load sys file
    uint64_t baseAddress = SymLoadModuleExW(GetCurrentProcess(), NULL, sysPath, NULL, 0x10000000, 0, NULL, 0);
    if (baseAddress == 0)
    {
        printf("[!] Error load sys file\n");
        return 0;
    }
    // load pdb file
    if (!SymSetSearchPathW(GetCurrentProcess(), pdbPath))
    {
        printf("[!] Error load pdb file\n");
        SymUnloadModule(GetCurrentProcess(), baseAddress);
        SymCleanup(GetCurrentProcess());
        return 0;
    }
    char symbolBuffer[sizeof(SYMBOL_INFOW) + 0x100 * sizeof(wchar_t)] = { 0 };
    PSYMBOL_INFOW pSymbol = (PSYMBOL_INFOW)symbolBuffer;
    pSymbol->SizeOfStruct = sizeof(SYMBOL_INFOW);
    pSymbol->MaxNameLen = 0xff;
    if (SymFromNameW(GetCurrentProcess(), L"Verify_CertInfo", pSymbol))
    {
        res = pSymbol->Address - baseAddress;
    }
    SymUnloadModule(GetCurrentProcess(), baseAddress);
    SymCleanup(GetCurrentProcess());
    return res;
    }

    getSbieDrvBaseAddress获取SbieDrv内核模块的基地址
    uint64_t getSbieDrvBaseAddress()
    {
        NTSTATUS status = STATUS_SUCCESS;
        uint64_t sbiedrvBaseAddress = 0;
        PRTL_PROCESS_MODULES moduleInfo = (PRTL_PROCESS_MODULES)VirtualAlloc(NULL, 0x400 * 0x400, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
        if (moduleInfo == NULL)
        {
            printf("[!] Error allocating module memory! Error Code: %lu\n", GetLastError());
            return 0;
        }
        if(!NT_SUCCESS(status = NtQuerySystemInformation((SYSTEM_INFORMATION_CLASS)11,
            moduleInfo, 0x400 * 0x400, NULL)))
        {
            printf("[!] Error: Unable to query module list (%#x)\n", status);
            VirtualFree(moduleInfo, 0, MEM_RELEASE);
            return 0;
        }
        for (ULONG i = 0; i NumberOfModules; i++)
        {
            if (!_stricmp((const char*)moduleInfo->Modules.FullPathName +
                moduleInfo->Modules.OffsetToFileName, "sbiedrv.sys"))
            {
                sbiedrvBaseAddress = (uint64_t)moduleInfo->Modules.ImageBase;
                break;
            }
        }
        return sbiedrvBaseAddress;
    }
    最后将两个函数的结果相加即可得到Verify_CertInfo变量的内核地址
    第三步:修改变量的内容
    DriverInterface Driver(rotcoreSysPath);
    uint64_t newValue = 0xfffffffff000fcc1;
    res =  Driver.write_arbitary_address_memory((SbieDrvBaseAddress + verifyCertInfoOffset), (newValue & 0xffffffff));  // // 0x000c3550 f000fcc1 -> 0xffffffff f000fcc1
    if (res == false)
    {
        printf("Failure\n");
        return 0;
    }
    res = Driver.write_arbitary_address_memory((SbieDrvBaseAddress + verifyCertInfoOffset + 4), (newValue & 0xffffffff00000000) >> 32);   // // 0x000c3550 f000fcc1 -> 0xffffffff f000fcc1
    if (res == false)
    {
        printf("Failure\n");
        return 0;
    }
    总结
    Win11系统中采用更加严格的驱动策略导致驱动加载失败,无法破解SandboxiePlus。使用新版漏洞驱动之后,内核的一些策略导致了获取驱动地址失败。以后,为了兼容各种windows系统版本,可以check一遍操作所需的权限,不管有没有已经获得。

    函数, 内核

  • 小腾子   

    顺丰到付大幅度发生
    VMxxxz   

    厉害了,我滴神。学习学习
    w1066602520   

    好复杂 先试试
    HuaGdao1   

    技术贴,学习了
    ijack2001   

    厉害了。学习学习
    SN1t2lO   

    只要不升级,第一代方案好用
    暴龙兽
    OP
      


    SN1t2lO 发表于 2025-9-22 11:16
    只要不升级,第一代方案好用

    新版本会修复一些bug和cve漏洞
    seanwang   

    新版本修复了cve漏洞,这个挺好的。
    您需要登录后才可以回帖 登录 | 立即注册