win10 Dynamic Value Relocation Table Retpoline解析

本文介绍了Windows 10在1809版本后如何使用Retpoline和导入优化来应对安全漏洞,特别是针对DVRT(Dynamic Value Relocation Table)的解析,包括其定位方式、结构体变化以及在ntos和hal.dll中的应用。同时,文章讨论了如何通过GuardFlags判断Retpoline支持,并展示了pte随机化和代码修改的过程。
摘要由CSDN通过智能技术生成

前言:

   在CVE-2017-5715漏洞曝出之后,微软做了一系列的措施来缓解,并在win10 1809(17763)之后会默认启用了Retpoline或导入优化,这取决于用户设置或cpu支持情况,也有两个都不启动的,但KASLR总是启动的,启动KASLR,会替换pte的旧值到随机化后的值,不过即使不能启用Retpoline,也会启动导入优化(1809之后),启用RetPoline或导入优化之后,winload.exe在加载ntos和hal.dll时,会替换一些指令,pe格式也发生了部分改变(win8.1已改变,后续只是增加).获取启用状态可调用ZwQuerySystemInformation的201号功能查询(>=1803版本),结构体定义如下:

typedef struct _SYSTEM_SPECULATION_CONTROL_INFORMATION
{
    struct
    {
        ULONG BpbEnabled : 1;
        ULONG BpbDisabledSystemPolicy : 1;
        ULONG BpbDisabledNoHardwareSupport : 1;
        ULONG SpecCtrlEnumerated : 1;
        ULONG SpecCmdEnumerated : 1;
        ULONG IbrsPresent : 1;
        ULONG StibpPresent : 1;
        ULONG SmepPresent : 1;
        ULONG SpeculativeStoreBypassDisableAvailable : 1;
        ULONG SpeculativeStoreBypassDisableSupported : 1;
        ULONG SpeculativeStoreBypassDisabledSystemWide : 1;
        ULONG SpeculativeStoreBypassDisabledKernel : 1;
        ULONG SpeculativeStoreBypassDisableRequired : 1;
        ULONG BpbDisabledKernelToUser : 1;
        ULONG SpecCtrlRetpolineEnabled : 1;
        ULONG SpecCtrlImportOptimizationEnabled : 1;
        ULONG Reserved : 24;
    } SpeculationControlFlags;
} SYSTEM_SPECULATION_CONTROL_INFORMATION, *PSYSTEM_SPECULATION_CONTROL_INFORMATION;

DVRT:

    全名Dynamic Value Relocation Table,此表记录需要替换的指令的指针,当然,系统也可以选择不进行替换,因为这些镜像在编译时,会留出一些空隙,比如:

call qword ptr[__imp__func]
nop

即使不替换,也不会影响正常执行.

在开启pchunter,windows kernel explorer 内核钩子扫描时,会扫出大量钩子(pchunter最新版已不再扫ntos).

查看sdk 1909 ntimage.h(win8.1时就存在一些),可发现有新的结构体,如下

typedef struct _IMAGE_LOAD_CONFIG_DIRECTORY64 {
    xxxxxxxxxxxxx
    DWORD      GuardFlags;
    IMAGE_LOAD_CONFIG_CODE_INTEGRITY CodeIntegrity;
    ULONGLONG  GuardAddressTakenIatEntryTable; // VA
    ULONGLONG  GuardAddressTakenIatEntryCount;
    ULONGLONG  GuardLongJumpTargetTable;       // VA
    ULONGLONG  GuardLongJumpTargetCount;
    ULONGLONG  DynamicValueRelocTable;         // VA
    ULONGLONG  CHPEMetadataPointer;            // VA
    ULONGLONG  GuardRFFailureRoutine;          // VA
    ULONGLONG  GuardRFFailureRoutineFunctionPointer; // VA
    DWORD      DynamicValueRelocTableOffset;
    WORD       DynamicValueRelocTableSection;
    WORD       Reserved2;
    ULONGLONG  GuardRFVerifyStackPointerFunctionPointer; // VA
    DWORD      HotPatchTableOffset;
    DWORD      Reserved3;
    ULONGLONG  EnclaveConfigurationPointer;     // VA
    ULONGLONG  VolatileMetadataPointer;         // VA
    ULONGLONG  GuardEHContinuationTable;        // VA
    ULONGLONG  GuardEHContinuationCount;
} IMAGE_LOAD_CONFIG_DIRECTORY64, *PIMAGE_LOAD_CONFIG_DIRECTORY64;

typedef struct _IMAGE_PROLOGUE_DYNAMIC_RELOCATION_HEADER {
    BYTE       PrologueByteCount;
    // BYTE    PrologueBytes[PrologueByteCount];
} IMAGE_PROLOGUE_DYNAMIC_RELOCATION_HEADER;
typedef IMAGE_PROLOGUE_DYNAMIC_RELOCATION_HEADER UNALIGNED * PIMAGE_PROLOGUE_DYNAMIC_RELOCATION_HEADER;

typedef struct _IMAGE_EPILOGUE_DYNAMIC_RELOCATION_HEADER {
    DWORD      EpilogueCount;
    BYTE       EpilogueByteCount;
    BYTE       BranchDescriptorElementSize;
    WORD       BranchDescriptorCount;
    // BYTE    BranchDescriptors[...];
    // BYTE    BranchDescriptorBitMap[...];
} IMAGE_EPILOGUE_DYNAMIC_RELOCATION_HEADER;
typedef IMAGE_EPILOGUE_DYNAMIC_RELOCATION_HEADER UNALIGNED * PIMAGE_EPILOGUE_DYNAMIC_RELOCATION_HEADER;

typedef struct _IMAGE_IMPORT_CONTROL_TRANSFER_DYNAMIC_RELOCATION {
    DWORD       PageRelativeOffset : 12;
    DWORD       IndirectCall       : 1;
    DWORD       IATIndex           : 19;
} IMAGE_IMPORT_CONTROL_TRANSFER_DYNAMIC_RELOCATION;
typedef IMAGE_IMPORT_CONTROL_TRANSFER_DYNAMIC_RELOCATION UNALIGNED * PIMAGE_IMPORT_CONTROL_TRANSFER_DYNAMIC_RELOCATION;

typedef struct _IMAGE_INDIR_CONTROL_TRANSFER_DYNAMIC_RELOCATION {
    WORD        PageRelativeOffset : 12;
    WORD        IndirectCall       : 1;
    WORD        RexWPrefix         : 1;
    WORD        CfgCheck           : 1;
    WORD        Reserved           : 1;
} IMAGE_INDIR_CONTROL_TRANSFER_DYNAMIC_RELOCATION;
typedef IMAGE_INDIR_CONTROL_TRANSFER_DYNAMIC_RELOCATION UNALIGNED * PIMAGE_INDIR_CONTROL_TRANSFER_DYNAMIC_RELOCATION;

typedef struct _IMAGE_SWITCHTABLE_BRANCH_DYNAMIC_RELOCATION {
    WORD        PageRelativeOffset : 12;
    WORD        RegisterNumber     : 4;
} IMAGE_SWITCHTABLE_BRANCH_DYNAMIC_RELOCATION;
typedef IMAGE_SWITCHTABLE_BRANCH_DYNAMIC_RELOCATION UNALIGNED * PIMAGE_SWITCHTABLE_BRANCH_DYNAMIC_RELOCATION;

//
// Dynamic value relocation table in loadconfig
//

typedef struct _IMAGE_DYNAMIC_RELOCATION_TABLE {
    DWORD Version;
    DWORD Size;
//  IMAGE_DYNAMIC_RELOCATION DynamicRelocations[0];
} IMAGE_DYNAMIC_RELOCATION_TABLE, *PIMAGE_DYNAMIC_RELOCATION_TABLE;

//
// Dynamic value relocation entries following IMAGE_DYNAMIC_RELOCATION_TABLE
//

#include "pshpack1.h"

typedef struct _IMAGE_DYNAMIC_RELOCATION32 {
    DWORD      Symbol;
    DWORD      BaseRelocSize;
//  IMAGE_BASE_RELOCATION BaseRelocations[0];
} IMAGE_DYNAMIC_RELOCATION32, *PIMAGE_DYNAMIC_RELOCATION32;

typedef struct _IMAGE_DYNAMIC_RELOCATION64 {
    ULONGLONG  Symbol;
    DWORD      BaseRelocSize;
//  IMAGE_BASE_RELOCATION BaseRelocations[0];
} IMAGE_DYNAMIC_RELOCATION64, *PIMAGE_DYNAMIC_RELOCATION64;

可以发现有一个新的重定位表,也是DVRT.当我以旧重定位表的解析方式进行时,发现数据异常,于是就有这篇文章.

首先动态重定位表有两种方式进行定位,一是此表跟在重定位表之后,二是通过LOAD_CONFIG表得到offset,此offset为相对一个节的rva,节的索引在offset的下一个成员指出.

得到节base + rva + imagebase即可定位,另外判断镜像是否支持RetPoline,可通过LOAD_CONFIG下的GuardFlags标志进行判断,

得到DVRT表之后,Version表示版本号,动态重定位表有两个版本,由于在测试中没有发现版本号为2的镜像,故此文仅对v1版本进行解析.

size表示大小,接着跟着一个IMAGE_DYNAMIC_RELOCATION结构,

Symbol 定义如下:

#define IMAGE_DYNAMIC_RELOCATION_GUARD_RF_PROLOGUE   0x00000001
#define IMAGE_DYNAMIC_RELOCATION_GUARD_RF_EPILOGUE   0x00000002
#define IMAGE_DYNAMIC_RELOCATION_GUARD_IMPORT_CONTROL_TRANSFER  0x00000003
#define IMAGE_DYNAMIC_RELOCATION_GUARD_INDIR_CONTROL_TRANSFER   0x00000004
#define IMAGE_DYNAMIC_RELOCATION_GUARD_SWITCHTABLE_BRANCH       0x00000005

ntos的特殊一些,解析时会发现有一些Symbol对不上,实际上,ntos还包含了pte随机化,在实现pte随机化时,需要替换原定义的pte信息,定义如下:

#ifndef PXE_BASE
#define PXE_BASE    0xFFFFF6FB7DBED000UI64
#endif
#ifndef PXE_SELFMAP
#define PXE_SELFMAP 0xFFFFF6FB7DBEDF68UI64
#endif
#ifndef PPE_BASE
#define PPE_BASE    0xFFFFF6FB7DA00000UI64
#endif
#ifndef PDE_BASE
#define PDE_BASE    0xFFFFF6FB40000000UI64
#endif
#ifndef PTE_BASE
#define PTE_BASE    0xFFFFF68000000000UI64
#endif

#define PXE_TOP           0xFFFFF6FB7DBEDFFFUI64
#define PPE_TOP           0xFFFFF6FB7DBFFFFFUI64
#define PDE_TOP           0xFFFFF6FB7FFFFFFFUI64
#define PTE_TOP           0xFFFFF6FFFFFFFFFFUI64

#define MmPagedPoolStart  0xFFFFFA8000000000UI64

对于IMPORT_CONTROL:

call qword ptr [__imp_func]
nop

->

mov r10, qword ptr [__imp_func]
call _guard_retpoline_import_r10

也有可能替换为直接call r10,这种是因为启用了导入优化,

对于其它两个,windows并不一定会进行代码替换.

在较新的系统上,ntos的RtlPerformRetpolineRelocationsOnImageEx函数进行修改代码操作

对于pte随机化,只是将常量替换为新的地址

对于代码的修改

符号对应关系 未知为没有发现对应的要修改的地址

1->未知

2->未知

3->12字节 mov r10,[__imp___],call r10 or call retpoline_r1_guard

4->5字节或不修改

5->不修改

pte相关->8字节 直接将随机化后的pte相关地址替换

在支持Retpoline的pe文件中,有一个节叫RETPOL的节,

此节有替换时需要call的函数

此节类似于apiset,开头是一共结构体,此结构体定义如下:

struct _RETPOL_SECTION
{
	DWORD sizeOfSection;
	DWORD rva_switchtable_jmp_rax;
	DWORD rva_switchtable_jmp_rcx;
	DWORD rva_switchtable_jmp_rdx;
	DWORD rva_switchtable_jmp_rbx;
	DWORD rva_switchtable_jmp_rsp;
	DWORD rva_switchtable_jmp_rbp;
	DWORD rva_switchtable_jmp_rsi;
	DWORD rva_switchtable_jmp_rdi;
	DWORD rva_switchtable_jmp_r8;
	DWORD rva_switchtable_jmp_r9;
	DWORD rva_switchtable_jmp_r10;
	DWORD rva_switchtable_jmp_r11;
	DWORD rva_switchtable_jmp_r12;
	DWORD rva_switchtable_jmp_r13;
	DWORD rva_switchtable_jmp_r14;
	DWORD rva_switchtable_jmp_r15;
	DWORD rva_indirect_rax;
	DWORD rva_import_r10;
	DWORD unknow;
	DWORD unknow;
	DWORD unknow;
	DWORD unknow;
}

通过rva+节base就能得到函数的va,也在RETPOL节中

代码:

struct TypeOffset
{
    WORD Offset : 12;
    WORD Type : 4;
};

typedef struct _SYMBOL_NAME {
    ULONG sizeOfOneBlock;
    ULONGLONG base;
    const char* szName;
}SYMBOL_NAME, *PSYMBOL_NAME;

union UNION_SYMBOL_DATA
{
    TypeOffset offset;
    IMAGE_PROLOGUE_DYNAMIC_RELOCATION_HEADER prologue;
    IMAGE_EPILOGUE_DYNAMIC_RELOCATION_HEADER epilogue;
    IMAGE_IMPORT_CONTROL_TRANSFER_DYNAMIC_RELOCATION imp;
    IMAGE_INDIR_CONTROL_TRANSFER_DYNAMIC_RELOCATION inDir;
    IMAGE_SWITCHTABLE_BRANCH_DYNAMIC_RELOCATION switchBranch;
};

SYMBOL_NAME pteSymbolName[10] = {
    {sizeof(TypeOffset), PXE_BASE, "PXE_BASE"},
    {sizeof(TypeOffset), PXE_SELFMAP, "PXE_SELFMAP"},
    {sizeof(TypeOffset), PPE_BASE, "PPE_BASE"},
    {sizeof(TypeOffset), PDE_BASE, "PDE_BASE"},
    {sizeof(TypeOffset), PTE_BASE, "PTE_BASE"},
    {sizeof(TypeOffset), PXE_TOP, "PXE_TOP"},
    {sizeof(TypeOffset), PPE_TOP, "PPE_TOP"},
    {sizeof(TypeOffset), PDE_TOP, "PDE_TOP"},
    {sizeof(TypeOffset), PTE_TOP, "PTE_TOP"},
    {sizeof(TypeOffset), MmPagedPoolStart, "MmPagedPoolStart"}
};

SYMBOL_NAME normalSymbolName[6] = {
    {0, 0, "error symbol name"},
    {sizeof(IMAGE_PROLOGUE_DYNAMIC_RELOCATION_HEADER), IMAGE_DYNAMIC_RELOCATION_GUARD_RF_PROLOGUE, "IMAGE_DYNAMIC_RELOCATION_GUARD_RF_PROLOGUE"},
    {sizeof(IMAGE_EPILOGUE_DYNAMIC_RELOCATION_HEADER), IMAGE_DYNAMIC_RELOCATION_GUARD_RF_EPILOGUE, "IMAGE_DYNAMIC_RELOCATION_GUARD_RF_EPILOGUE"},
    {sizeof(IMAGE_IMPORT_CONTROL_TRANSFER_DYNAMIC_RELOCATION), IMAGE_DYNAMIC_RELOCATION_GUARD_IMPORT_CONTROL_TRANSFER, "IMAGE_DYNAMIC_RELOCATION_GUARD_IMPORT_CONTROL_TRANSFER (imp mov r10 call)"},
    {sizeof(IMAGE_INDIR_CONTROL_TRANSFER_DYNAMIC_RELOCATION), IMAGE_DYNAMIC_RELOCATION_GUARD_INDIR_CONTROL_TRANSFER, "IMAGE_DYNAMIC_RELOCATION_GUARD_INDIR_CONTROL_TRANSFER (call rax)"},
    {sizeof(IMAGE_SWITCHTABLE_BRANCH_DYNAMIC_RELOCATION), IMAGE_DYNAMIC_RELOCATION_GUARD_SWITCHTABLE_BRANCH, "IMAGE_DYNAMIC_RELOCATION_GUARD_SWITCHTABLE_BRANCH (jmp rcx)"}
};

/*
* zero: invalid symbol
* >0 : sizeofOneBlock
*/
DWORD GetSymbolOneBlockSize(ULONG_PTR Symbol)
{
    if (Symbol == 0)
        return 0;

    if (Symbol > 5)
    {
        for (size_t i = 0; i < sizeof(pteSymbolName); i++)
        {
            if (Symbol == pteSymbolName[i].base)
                return pteSymbolName[i].sizeOfOneBlock;
        }

        return 0;
    }
    else
    {
        return normalSymbolName[Symbol].sizeOfOneBlock;
    }
}

void Parsefunc(PIMAGE_DYNAMIC_RELOCATION pDvr, PVOID base, ULONG_PTR dqExistRealBase)
{
    if (pDvr == nullptr)
        return;

    auto sizeOfOne = GetSymbolOneBlockSize(pDvr->Symbol);

    if (sizeOfOne == 0)
        return;

    auto pBaseR = (PIMAGE_BASE_RELOCATION)(pDvr + 1);
    auto sizeOfReloca = pDvr->BaseRelocSize;

    while (pBaseR->SizeOfBlock && pBaseR->VirtualAddress && sizeOfReloca)
    {
        auto diff = (ULONG_PTR)base + pBaseR->VirtualAddress;
        auto item = (UNION_SYMBOL_DATA*)(pBaseR + 1);
        auto count = (pBaseR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeOfOne;

        for (size_t i = 0; i < count; ++i)
        {
            auto va = diff;
            switch (pDvr->Symbol)
            {
            case 1:
                break;
            case 2:
                break;
            case 3:
                va = va + item->imp.PageRelativeOffset;
                break;
            case 4:
                va = va + item->inDir.PageRelativeOffset;
                break;
            case 5:
                va = va + item->switchBranch.PageRelativeOffset;
                break;
            default:
                // ntos kernel about the randomization of pte
                va = va + item->offset.Offset;
                break;
            }

            item = (UNION_SYMBOL_DATA*)((PUCHAR)item + sizeOfOne);

            // end of block
            if (va == diff && i)
                continue;

            auto OriginVa = va - (ULONG_PTR)base + dqExistRealBase;

            if (g_bPdb)
            {
                ULONG_PTR dwDisplacement = 0;
                auto strName = parseAddress(va, &dwDisplacement);
                std::cout << va << " name: " << strName << "+0x" << std::hex << dwDisplacement << " OriginVa: " << std::hex << OriginVa << std::endl;
            }
            else
            {
                std::cout << va << " OriginVa: " << std::hex << OriginVa << std::endl;
            }
        }

        sizeOfReloca -= pBaseR->SizeOfBlock;

        pBaseR = (PIMAGE_BASE_RELOCATION)((ULONG_PTR)pBaseR + pBaseR->SizeOfBlock);
    }
}
void Test(const wchar_t* szFileName, ULONG_PTR dqExistRealBase)
{
    HANDLE hFile = INVALID_HANDLE_VALUE;
    HANDLE hSection = nullptr;
    PVOID base = nullptr;

    do
    {
        if (szFileName == nullptr)
            break;

        std::wcout << szFileName << std::endl;

        GetFileVersionOfPath(szFileName, nullptr, nullptr, nullptr, nullptr);

        hFile = CreateFile(szFileName, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);

        if (hFile == INVALID_HANDLE_VALUE)
        {
            std::cout << "open file faild" << std::endl;
            break;
        }

        if (!NT_SUCCESS(ZwCreateSection(&hSection, SECTION_MAP_READ, NULL, NULL, PAGE_READONLY, SEC_IMAGE, hFile)))
        {
            std::cout << "create section faild" << std::endl;
            break;
        }

        SIZE_T viewsize = 0;
        
        if (!NT_SUCCESS(ZwMapViewOfSection(hSection, ZwCurrentProcess(), &base, 0, 0, NULL, &viewsize, ViewUnmap, 0, PAGE_READONLY)))
        {
            std::cout << "ZwMapViewOfSection faild" << std::endl;
            break;
        }

        std::cout << "map at : " << base << std::endl;

        g_bPdb = pdbParseInitializing(szFileName, (ULONG_PTR)base);

        if (!g_bPdb)
            std::cout << "pdb Symbol Invalid" << std::endl;
        else
            std::cout << "pdb Symbol ok" << std::endl;

        ULONG size = 0;

        auto pLoadConfig = (PIMAGE_LOAD_CONFIG_DIRECTORY64)RtlImageDirectoryEntryToData(base, true, IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG, &size);

        if (pLoadConfig == nullptr)
        {
            std::cout << "load config info is null" << std::endl;
            break;
        }

        std::cout << "GuardFlags:" << std::hex << pLoadConfig->GuardFlags << std::endl;
        std::cout << GetGuardFlagsName(pLoadConfig->GuardFlags) << std::endl;

        if (!BooleanFlagOn(pLoadConfig->GuardFlags, IMAGE_GUARD_RETPOLINE_PRESENT))
        {
            std::cout << "current file not enable retpoline " << std::endl;
            break;
        }

        if (pLoadConfig->DynamicValueRelocTableOffset == 0)
        {
            std::cout << "DynamicValueRelocTableOffset is null." << std::endl;
            break;
        }

        // DVRT at reloc table later
        auto pOldRelocaTable = (PUCHAR)RtlImageDirectoryEntryToData(base, true, IMAGE_DIRECTORY_ENTRY_BASERELOC, &size);

        if (pOldRelocaTable == nullptr || size == 0)
        {
            std::cout << "RelocTable is null." << std::endl;
            break;
        }

        PIMAGE_DYNAMIC_RELOCATION_TABLE pdvrt = (PIMAGE_DYNAMIC_RELOCATION_TABLE)(pOldRelocaTable + size);

        if (IsBadReadPtr(pdvrt, sizeof(IMAGE_DYNAMIC_RELOCATION_TABLE)))
        {
            std::cout << "DVRT is bad : " << std::hex << pdvrt << std::endl;
            break;
        }

        std::cout << "version: " << pdvrt->Version << std::endl;
        std::cout << "size: " << std::hex << pdvrt->Size << std::endl;

        if (pdvrt->Version == 2)
        {
            std::cout << "DVRT version is 2, current version not support parse" << std::endl;
            break;
        }

        DWORD sizeOfDvr = 0;

        PIMAGE_DYNAMIC_RELOCATION pDvr = (PIMAGE_DYNAMIC_RELOCATION)(pdvrt + 1);

        while (sizeOfDvr < pdvrt->Size)
        {
            if (IsBadReadPtr(pDvr, sizeof(IMAGE_DYNAMIC_RELOCATION)))
            {
                std::cout << "bad read ptr of Dvr :" << std::hex << pDvr << " size: " << std::hex << sizeOfDvr << std::endl;
                break;
            }

            if (pDvr->Symbol > IMAGE_DYNAMIC_RELOCATION_GUARD_SWITCHTABLE_BRANCH)
            {
                auto bParsed = false;

                for (size_t i = 0; i < sizeof(pteSymbolName); i++)
                {
                    if (pDvr->Symbol == pteSymbolName[i].base)
                    {
                        std::cout << "Symbol : " << pDvr->Symbol << " parse: " << pteSymbolName[i].szName << std::endl;
                        bParsed = true;
                        break;
                    }
                }

                if(!bParsed)
                    std::cout << "Symbol : " << pDvr->Symbol << " parse: unknow" << std::endl;
            }
            else
            {
                std::cout << "Symbol : " << pDvr->Symbol << " parse: " << normalSymbolName[pDvr->Symbol].szName << std::endl;
            }

            std::cout << "baseRelocSize : " << pDvr->BaseRelocSize << std::endl;

            // parse data
            Parsefunc(pDvr, base, dqExistRealBase);

            sizeOfDvr += pDvr->BaseRelocSize + sizeof(IMAGE_DYNAMIC_RELOCATION);

            auto tmp = pDvr->BaseRelocSize;

            pDvr = (PIMAGE_DYNAMIC_RELOCATION)((PUCHAR)(pDvr + 1) + tmp);
        }

        std::cout << "TotalSize: " << std::hex << sizeOfDvr << std::endl;

        std::cout << "any key to unmap image" << std::endl;
        system("pause");

    } while (false);

    if (g_bPdb && base)
        pdbParseTerminate((ULONG_PTR)base);

    g_bPdb = false;

    if (base)
        ZwUnmapViewOfSection(ZwCurrentProcess(), base);

    if (hSection)
        CloseHandle(hSection);

    if (hFile != INVALID_HANDLE_VALUE)
        CloseHandle(hFile);
}

Mitigating Spectre variant 2 with Retpoline on Windows - Microsoft Community Hub

Return Flow Guard - Tencent Security Xuanwu Lab

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值