win下內核重載過保護
阿新 • • 發佈:2018-06-23
3.1 wke 偏移 ffffff exe images sizeof RM img 這裏以SSDT為例
原理:
程序要用到哪些模塊自己加載。但是修復重定位時。要以原來的模塊為基址 而 SSDT以新的為基址。
這裏只過了openprocess的保護
原理:
程序要用到哪些模塊自己加載。但是修復重定位時。要以原來的模塊為基址 而 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下內核重載過保護