1. 程式人生 > >win下內核重載過保護

win下內核重載過保護

3.1 wke 偏移 ffffff exe images sizeof RM img

這裏以SSDT為例
原理:
程序要用到哪些模塊自己加載。但是修復重定位時。要以原來的模塊為基址 而 SSDT以新的為基址。
技術分享圖片
這裏只過了openprocess的保護

#include <ntifs.h>
#include <ntimage.h>

#pragma pack(1)
typedef struct _ServiceDesriptorEntry
{
    ULONG *ServiceTableBase;        // 服務表基址
    ULONG *ServiceCounterTableBase; // 計數表基址
    ULONG NumberOfServices;         // 表中項的個數
    UCHAR *ParamTableBase;          // 參數表基址
}SSDTEntry, *PSSDTEntry;
#pragma pack()
// 導入SSDT
NTSYSAPI SSDTEntry KeServiceDescriptorTable;

PSSDTEntry pNewSSDT;
PCHAR g_pHookpointer;
PCHAR g_pJmpPointer;
VOID DriverUnload(PDRIVER_OBJECT pDriver);
HANDLE KernelCreateFile(
    IN PUNICODE_STRING pstrFile,
    IN BOOLEAN         bIsDir);

ULONG64 KernelGetFileSize(IN HANDLE hfile);
ULONG64 KernelReadFile(
    IN  HANDLE         hfile,
    IN  PLARGE_INTEGER Offset,
    IN  ULONG          ulLength,
    OUT PVOID          pBuffer);
PVOID GetModuleBase(PDRIVER_OBJECT pDriver, PUNICODE_STRING pModuleName);

//讀取內核文件到內存,並且將其展開
void GetReloadBuf(PUNICODE_STRING KerPath, PCHAR* pReloadBuf)
{
    LARGE_INTEGER Offset = { 0 };
    HANDLE hFile = KernelCreateFile(KerPath, FALSE);

    ULONG64 uSize = KernelGetFileSize(hFile);

    PCHAR pKernelBuf = ExAllocatePool(NonPagedPool, (SIZE_T)uSize);

    RtlZeroMemory(pKernelBuf, (SIZE_T)uSize);
    KernelReadFile(hFile, &Offset, (ULONG)uSize, pKernelBuf);
    //2 展開內核文件
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pKernelBuf;

    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + pKernelBuf);
    PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNt);

    *pReloadBuf = ExAllocatePool(NonPagedPool, pNt->OptionalHeader.SizeOfImage);
    RtlZeroMemory(*pReloadBuf, pNt->OptionalHeader.SizeOfImage);
    //2.1 先拷貝PE頭部
    RtlCopyMemory(*pReloadBuf, pKernelBuf, pNt->OptionalHeader.SizeOfHeaders);

    //2.2 再拷貝PE各個區段
    for (size_t i = 0; i < pNt->FileHeader.NumberOfSections; i++)
    {
        RtlCopyMemory(
            *pReloadBuf + pSection[i].VirtualAddress,
            pKernelBuf + pSection[i].PointerToRawData,
            pSection[i].SizeOfRawData
            );
    }
    ExFreePool(pKernelBuf);
}

void FixReloc(PCHAR OldKernelBase, PCHAR NewKernelBase)
{
    typedef struct _TYPEOFFSET
    {

        USHORT Offset : 12;
        USHORT type : 4;
    }TYPEOFFSET, *PTYPEOFFSET;
    //1 找到重定位表
    PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)NewKernelBase;

    PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + NewKernelBase);

    PIMAGE_DATA_DIRECTORY pDir = (pNt->OptionalHeader.DataDirectory + 5);

    PIMAGE_BASE_RELOCATION pReloc = (PIMAGE_BASE_RELOCATION)
        (pDir->VirtualAddress + NewKernelBase);
    while (pReloc->SizeOfBlock != 0)
    {
        //2 尋找重定位的位置
        ULONG uCount = (pReloc->SizeOfBlock - 8) / 2;
        PCHAR pSartAddress = (pReloc->VirtualAddress + NewKernelBase);
        PTYPEOFFSET pOffset = (PTYPEOFFSET)(pReloc + 1);
        for (ULONG i = 0; i < uCount; i++)
        {
            if (pOffset->type == 3)
            {
                //3 開始重定位
                //NewBase-DefaultBase = NewReloc-DefaultReloc
                ULONG * pRelocAdd = (ULONG *)(pSartAddress + pOffset->Offset);
                *pRelocAdd += ((ULONG)OldKernelBase - pNt->OptionalHeader.ImageBase);
            }
            pOffset++;
        }

        pReloc = (PIMAGE_BASE_RELOCATION)((PCHAR)pReloc + pReloc->SizeOfBlock);
    }
}

void FixSSDT(PCHAR OldKernelBase, PCHAR NewKernelBase)
{
    //新內核中的某位置 - NewKernelBase = 老內核中的此位置 - OldKernelBase
    //新內核中的某位置  = NewKernelBase-OldKernelBase+老內核中的此位置
    ULONG uOffset = (ULONG)NewKernelBase - (ULONG)OldKernelBase;

    pNewSSDT =
        (PSSDTEntry)((PCHAR)&KeServiceDescriptorTable + uOffset);
    //填充SSDT函數數量
    pNewSSDT->NumberOfServices =
        KeServiceDescriptorTable.NumberOfServices;
    //填充SSDT函數地址表
    pNewSSDT->ServiceTableBase =
        (PULONG)((PCHAR)KeServiceDescriptorTable.ServiceTableBase + uOffset);
    for (ULONG i = 0; i < pNewSSDT->NumberOfServices; i++)
    {
        pNewSSDT->ServiceTableBase[i] = pNewSSDT->ServiceTableBase[i] + uOffset;
    }
    //填充SSDT參數表,表中一個元素占1個字節
    pNewSSDT->ParamTableBase =
        ((UCHAR*)KeServiceDescriptorTable.ParamTableBase + uOffset);

    memcpy(pNewSSDT->ParamTableBase, KeServiceDescriptorTable.ParamTableBase,
        KeServiceDescriptorTable.NumberOfServices);

    //填充SSDT調用次數表,表中一個元素占4個字節
    //pNewSSDT->ServiceCounterTableBase =
    //  (PULONG)((PCHAR)KeServiceDescriptorTable.ServiceCounterTableBase + uOffset);

    //memcpy(pNewSSDT->ServiceCounterTableBase, 
    //  KeServiceDescriptorTable.ServiceCounterTableBase,
    //  KeServiceDescriptorTable.NumberOfServices*4
    //  );

}

void * SearchMemory(char * buf, int BufLenth, char * Mem, int MaxLenth)
{
    int MemIndex = 0;
    int BufIndex = 0;
    for (MemIndex = 0; MemIndex < MaxLenth; MemIndex++)
    {
        BufIndex = 0;
        if (Mem[MemIndex] == buf[BufIndex] || buf[BufIndex] == ‘?‘)
        {
            int MemIndexTemp = MemIndex;
            do
            {
                MemIndexTemp++;
                BufIndex++;
            } while ((Mem[MemIndexTemp] == buf[BufIndex] || buf[BufIndex] == ‘?‘) && BufIndex < BufLenth);
            if (BufIndex == BufLenth)
            {
                return Mem + MemIndex;
            }
        }
    }
    return 0;
}

//RDMSR將64位由ECX寄存器指定的MSR(model specific register, 模式指定寄存器)
//的內容讀出至寄存器EDX:EAX中(在支持intel64架構的處理器中RCX的高32位忽略)。
PVOID GetKiFastCallEntryAddr()
{
    PVOID pAddr = 0;
    _asm
    {
        push ecx;
        push eax;
        mov ecx, 0x176;
        rdmsr;               //0環的EIP
        mov pAddr, eax;
        pop eax;
        pop ecx;
    }
    return pAddr;
}
//WP位0:禁用寫保護的功能
//
//WP位1:開啟寫保護的功能
//
//cr0的第16位是WP位,只要將這一位置0就可以禁用寫保護,置1則可將其恢復。
void OffProtect()
{
    __asm { //關閉內存保護
        push eax;
        mov eax, cr0;
        and eax, ~0x10000;
        mov cr0, eax;
        pop eax;
    }

}
void OnProtect()
{
    __asm { //開啟內存保護
        push eax;
        mov eax, cr0;
        OR eax, 0x10000;
        mov cr0, eax;
        pop eax;
    }
}
UCHAR CodeBuf[] = { 0x2b, 0xe1, 0xc1, 0xe9, 0x02 };
UCHAR NewCodeBuf[5] = { 0xE9 };

//獲取openprocss函數
ULONG  FilterSSDT(ULONG uCallNum, PULONG FunBaseAddress, ULONG FunAdress)
{
    //說明這個調用是用的SSDT,而不是ShowSSDT KeServiceDescriptorTable也就是一個全局變量已經導出來了
    if (FunBaseAddress == KeServiceDescriptorTable.ServiceTableBase)
    {
        if (uCallNum == 190)
        {
            return pNewSSDT->ServiceTableBase[190];
        }
    }
    return FunAdress;
}

//hook點是他獲取函數的相關指令處
//83e91871 8b1487          mov     edx, dword ptr[edi + eax * 4]              //重要 得到了函數地址   eax已經為索引號*4(4代表每個元素的大小)
//83e91874 2be1            sub     esp, ecx                                //棧頂擡高這麽大,準備拷貝參數
//83e91876 c1e902          shr     ecx, 2
//83e91879 8bfc            mov     edi, esp
_declspec(naked) void MyFilterFunction()
{
    //5 在自己的Hook函數中判斷走自己的內核還是原來的內核
    //eax 調用號
    //edi SSDT函數表地址
    //edx 裏面是從SSDT表中獲得的函數的地址
    _asm
    {
        pushad;
        pushfd;
        push edx;
        push edi;
        push eax;
        call FilterSSDT;
        mov dword ptr ds : [esp + 0x18], eax;
        popfd;
        popad;
        sub     esp, ecx;
        shr     ecx, 2;
        jmp g_pJmpPointer;  //指下原來的下一條指令繼續執行
    }
}
void OnHookKiFastCall()
{
    //1 先得到KifastcallEntry的地址
    PVOID KiFastCallAdd = GetKiFastCallEntryAddr();
    //2 搜索2b e1 c1 e9 02
    g_pHookpointer = SearchMemory(CodeBuf, 5, KiFastCallAdd, 0x200);
    //3 找到這個位置之後,直接hook
    //3.1關閉頁保護
    OffProtect();
    //3.2替換5個字節
    //先計算偏移
    *(ULONG*)(NewCodeBuf + 1) =
        ((ULONG)MyFilterFunction - (ULONG)g_pHookpointer - 5);
    //再換掉
    memcpy(g_pHookpointer, NewCodeBuf, 5);

    //3.3開啟頁保護
    OnProtect();
    //指下原來的下一條指令繼續執行
    g_pJmpPointer = g_pHookpointer + 5;
}

NTSTATUS DriverEntry(PDRIVER_OBJECT pDriver, PUNICODE_STRING pPath)
{
    UNREFERENCED_PARAMETER(pPath);
    DbgBreakPoint();
    PCHAR pNtModuleBase = NULL;
    UNICODE_STRING pNtModuleName;

    //1 找到內核文件,將其展開加載進內存
    PCHAR pReloadBuf = NULL;
    UNICODE_STRING KerPath;
    RtlInitUnicodeString(&KerPath, L"\\??\\C:\\windows\\system32\\ntkrnlpa.exe");

    GetReloadBuf(&KerPath, &pReloadBuf);
    //2 修復重定位ntoskrnl.exe
    RtlInitUnicodeString(&pNtModuleName, L"ntoskrnl.exe");
    pNtModuleBase = (PCHAR)GetModuleBase(pDriver, &pNtModuleName);
    FixReloc(pNtModuleBase, pReloadBuf);
    //3 修復自己的SSDT表
    FixSSDT(pNtModuleBase, pReloadBuf);
    //4 Hook KiFastCallEntry,攔截系統調用
    OnHookKiFastCall();
    pDriver->DriverUnload = DriverUnload;
    return STATUS_SUCCESS;
}

HANDLE KernelCreateFile(
    IN PUNICODE_STRING pstrFile, // 文件路徑符號鏈接
    IN BOOLEAN         bIsDir)   // 是否為文件夾
{
    HANDLE          hFile = NULL;
    NTSTATUS        Status = STATUS_UNSUCCESSFUL;
    IO_STATUS_BLOCK StatusBlock = { 0 };
    ULONG           ulShareAccess =
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
    ULONG           ulCreateOpt =
        FILE_SYNCHRONOUS_IO_NONALERT;
    // 1. 初始化OBJECT_ATTRIBUTES的內容
    OBJECT_ATTRIBUTES objAttrib = { 0 };
    ULONG             ulAttributes =
        OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE;
    InitializeObjectAttributes(
        &objAttrib,    // 返回初始化完畢的結構體
        pstrFile,      // 文件對象名稱
        ulAttributes,  // 對象屬性
        NULL, NULL);   // 一般為NULL
    // 2. 創建文件對象
    ulCreateOpt |= bIsDir ?
    FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE;
    Status = ZwCreateFile(
        &hFile,                // 返回文件句柄
        GENERIC_ALL,           // 文件操作描述
        &objAttrib,            // OBJECT_ATTRIBUTES
        &StatusBlock,          // 接受函數的操作結果
        0,                     // 初始文件大小
        FILE_ATTRIBUTE_NORMAL, // 新建文件的屬性
        ulShareAccess,         // 文件共享方式
        FILE_OPEN_IF,          // 文件存在則打開不存在則創建
        ulCreateOpt,           // 打開操作的附加標誌位
        NULL,                  // 擴展屬性區
        0);                   // 擴展屬性區長度
    if (!NT_SUCCESS(Status))
        return (HANDLE)-1;
    return hFile;
}

ULONG64 KernelGetFileSize(IN HANDLE hfile)
{
    // 查詢文件狀態
    IO_STATUS_BLOCK           StatusBlock = { 0 };
    FILE_STANDARD_INFORMATION fsi = { 0 };
    NTSTATUS Status = STATUS_UNSUCCESSFUL;
    Status = ZwQueryInformationFile(
        hfile,        // 文件句柄
        &StatusBlock, // 接受函數的操作結果
        &fsi,         // 根據最後一個參數的類型輸出相關信息
        sizeof(FILE_STANDARD_INFORMATION),
        FileStandardInformation);
    if (!NT_SUCCESS(Status))
        return 0;
    return fsi.EndOfFile.QuadPart;
}
ULONG64 KernelReadFile(
    IN  HANDLE         hfile,    // 文件句柄
    IN  PLARGE_INTEGER Offset,   // 從哪裏開始讀取
    IN  ULONG          ulLength, // 讀取多少字節
    OUT PVOID          pBuffer)  // 保存數據的緩存
{
    // 1. 讀取文件
    IO_STATUS_BLOCK StatusBlock = { 0 };
    NTSTATUS        Status = STATUS_UNSUCCESSFUL;
    Status = ZwReadFile(
        hfile,        // 文件句柄
        NULL,         // 信號狀態(一般為NULL)
        NULL, NULL,   // 保留
        &StatusBlock, // 接受函數的操作結果
        pBuffer,      // 保存讀取數據的緩存
        ulLength,     // 想要讀取的長度
        Offset,       // 讀取的起始偏移
        NULL);        // 一般為NULL
    if (!NT_SUCCESS(Status))  return 0;
    // 2. 返回實際讀取的長度
    return StatusBlock.Information;
}

typedef struct _LDR_DATA_TABLE_ENTRY {
    LIST_ENTRY InLoadOrderLinks;    //雙向鏈表
    LIST_ENTRY InMemoryOrderLinks;
    LIST_ENTRY InInitializationOrderLinks;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    USHORT LoadCount;
    USHORT TlsIndex;

} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

PVOID GetModuleBase(PDRIVER_OBJECT pDriver, PUNICODE_STRING pModuleName)
{
    PLDR_DATA_TABLE_ENTRY pLdr =
        (PLDR_DATA_TABLE_ENTRY)pDriver->DriverSection;
    LIST_ENTRY *pTemp = &pLdr->InLoadOrderLinks;
    do
    {
        PLDR_DATA_TABLE_ENTRY pDriverInfo =
            (PLDR_DATA_TABLE_ENTRY)pTemp;
        KdPrint(("%wZ\n", &pDriverInfo->FullDllName));
        if (
            RtlCompareUnicodeString(pModuleName, &pDriverInfo->BaseDllName, FALSE)
            == 0)
        {
            return pDriverInfo->DllBase;
        }
        pTemp = pTemp->Blink;
    } while (pTemp != &pLdr->InLoadOrderLinks);
    return 0;
}

VOID DriverUnload(PDRIVER_OBJECT pDriver)
{
    UNREFERENCED_PARAMETER(pDriver);
}

win下內核重載過保護