總覽:編程
IAT HOOK | Object Hook | Ssdt Hook |
---|---|---|
源碼 | 內核知識及源碼 | 內核知識級源碼 |
1、IAT HOOK:
由於上一篇博客對已經對IAT Hook基本流程及做用進行了介紹,但願能先學懂PE再來看IATHook.下面貼上Iathook的源碼,源碼中有詳細的註釋,還記着爲何不能結束360的進程嗎?參考思路以下圖(由於寫代碼的時候解決方案寫到了源碼中,不粘貼複製過來了):windows
如下代碼是DLL注入+iathook,經過測試procexp中的kill功能並無使用OpenProcess函數,因此須要逆向看一看他是如何結束的進程,下回與你們一塊兒討論,64的注入是成功,可是記得要改DWORD等32位整型變量,總體思路是不變的。
代碼中有ZwCreateThreadEx注入,是更底層的函數,你們若是注入系統進程失敗,能夠把註釋打開使用ZwCreateThreadEx進行注入,測試沒問題
聲明:數組
// 由於ZwCreateThreadEx沒有定義,因此本身定義一個僞函數 typedef DWORD(WINAPI* FnZwCreateThreadEx)(PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximunStackSize, LPVOID pUnkown); FnZwCreateThreadEx MyZwCreateThreadEx; // DLL的路徑 const char DllPath[MAX_PATH] = { "C:\\Users\\Administrator\\documents\\visual studio 2013\\Projects\\Text\\Debug\\TerminateProcessHook.dll" }; const char DllPath1[MAX_PATH] = { "C:\\Users\\Administrator\\documents\\visual studio 2013\\Projects\\Text\\Debug\\HookDll.dll" }; // 須要聲明的變量 HANDLE hProc = NULL; HANDLE RemoteHandle = NULL;
注入源碼:
這是寫在一個按鈕響應消息裏面的代碼,遠程線程掛起沒有測試,若是不行能夠刪除。數據結構
HMODULE hNtdHandle = LoadLibrary(L"ntdll.dll"); // 獲取地址給僞函數(由於ZwCreateThreadEx沒有聲明) MyZwCreateThreadEx = (FnZwCreateThreadEx)GetProcAddress(hNtdHandle, "ZwCreateThreadEx"); // 其實不必這樣寫,不過更爲規範一些 auto pFinAddress = GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "LoadLibraryA"); // 第一次點擊按鈕,開啓保護(只執行一次), 第二次點擊按鈕會掛起遠程線程(暫停保護),第三次會在恢復...... if ((IntHookFlag == FALSE) && (IntHookFlag == TRUE)) { IntHookFlag = TRUE; // 掛起遠程線程 SuspendThread(RemoteHandle); SetDlgItemText(IDC_STATIC3, L"×"); } else { IntHookFlag = FALSE; // 恢復遠程線程 ResumeThread(RemoteHandle); SetDlgItemText(IDC_STATIC3, L"√"); } // IAT Hook 自我保護未開啓 if (OneIntHookFlag == FALSE) { // 標記爲真 OneIntHookFlag = TRUE; // 1. 獲取被注入句柄 // HANDLE hProc = FindWindow(L"CalcFrame",NULL); hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 5000); if (!hProc) { AfxMessageBox(L"FindWindow() failuer"); return; } // DLL名稱大小 SIZE_T dwSize = strlen(DllPath1) + 1; // 2. 被注入進程申請內存空間 auto pDlladdress = VirtualAllocEx(hProc, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE); if (!pDlladdress) { CloseHandle(hProc); AfxMessageBox(L"VirtualAllocEx() failuer"); return; } // 3. 寫入內存數據 if (!WriteProcessMemory(hProc, pDlladdress, DllPath1, dwSize, &dwSize)) { VirtualFree(pDlladdress, dwSize, MEM_RELEASE); CloseHandle(hProc); AfxMessageBox(L"WriteProcessMemory() failuer"); return; } // 補:這個地方建立信號量來傳遞Pid; DWORD m_Pid = GetCurrentProcessId(); // HANDLE pProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, m_Pid); HANDLE hMap = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 0x10, L"Pid"); LPVOID hMapFile = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); // DLL裏面接收PID memcpy(hMapFile, &m_Pid, sizeof(HANDLE)); // 4. 遠程注入 LoadLibraryA獲取函數地址能夠直接函數名(編譯器會幫助你獲取VA),固然也能夠GetProcess本身來獲取VA DWORD dwTid = 0; // LoadLibrary(L""); RemoteHandle = CreateRemoteThread(hProc, NULL, 0, (LPTHREAD_START_ROUTINE)LoadLibraryA, (LPVOID)pDlladdress, 0, NULL); // HANDLE RemoteHandle = NULL; // DWORD dwStatu = MyZwCreateThreadEx(&RemoteHandle, PROCESS_ALL_ACCESS, NULL, hProc, (LPTHREAD_START_ROUTINE)pFinAddress, (LPVOID)pDlladdress, 0, 0, 0, 0, NULL); SetDlgItemText(IDC_STATIC3, L"√"); DWORD error = GetLastError(); // 5. 這個地方就不等待執行後在返回了WaitForSingleObjectEx(); // 6. 關閉遠程句柄(只是關閉了本進程獲取到的句柄,引用計數-1) CloseHandle(hProc);
DLL源碼:app
// dllmain.cpp : 定義 DLL 應用程序的入口點。 #include "stdafx.h" // Save New Function Address BYTE g_NewAddress[5] = { 0xE9 }; // Save Old Function Address BYTE g_OldAddress[5] = {}; // Save WriteAttrib DWORD g_OldAttrib = 0; // Save ProtectProcessPid DWORD g_Pid = 0; // Statement : Camouflage Function HANDLE WINAPI MyOpenProcess(_In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId); // Statement : Instanll Hook void InstallHook(); // Statement : UnInstall Hook void UnInstallHook(); // True OpenProcess typedef HANDLE (WINAPI* FnOpenProcess)( _In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId ); FnOpenProcess FOpenProcess; BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { // 被遠程線程建立時候會被調用 case DLL_PROCESS_ATTACH: { ::MessageBox(NULL, L"X64任務管理器", L"注入", NULL); // 安裝HOOK InstallHook(); } break; case DLL_PROCESS_DETACH: { // 卸載HOOK UnInstallHook(); } break; } return TRUE; } // Implementation : Camouflage Function HANDLE WINAPI MyOpenProcess(_In_ DWORD dwDesiredAccess, _In_ BOOL bInheritHandle, _In_ DWORD dwProcessId) { /* 作個過濾,由於咱們須要保護進程只有一個,被保護的PID是多少? 咱們能夠作用映射,信號量等把Pid傳到被注入進程中。 */ HANDLE hProce = NULL; if (dwProcessId == g_Pid) { // 先回復 UnInstallHook(); // 打開權限默認爲NULL 拒絕訪問 hProce = OpenProcess(NULL, bInheritHandle, dwProcessId); // 在安裝 InstallHook(); return hProce; } else { // 先回復 UnInstallHook(); // 調用正確的OPenProcess hProce = OpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); // 在安裝 InstallHook(); // 返回正確的句柄 return hProce;//FOpenProcess(dwDesiredAccess, bInheritHandle, dwProcessId); } } // Implementation : Instanll Hook void InstallHook() { // 前奏工做 HANDLE hMap = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, L"Pid"); LPVOID hAddr = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, 0); g_Pid = *(DWORD*)hAddr; // 1. 保存原來的地址(是指令長度) memcpy(g_OldAddress, OpenProcess, 5); FOpenProcess = (FnOpenProcess)OpenProcess; // 2. 計算偏移 DWORD dwOffset = (DWORD)MyOpenProcess - (DWORD)OpenProcess - 5; // 3. 數組填充新偏移 // memcpy(&g_NewAddress[1], &dwOffset, 4); 切記不可用這種方式 當年這個BUG卡了很久 內存大小端字符排序 內存拷貝是正向的 偏移錯誤 *(DWORD *)(g_NewAddress + 1) = dwOffset; VirtualProtect(OpenProcess, 5, PAGE_EXECUTE_READWRITE, &g_OldAttrib); // 4. 寫入地址 memcpy(OpenProcess, g_NewAddress, 5); VirtualProtect(OpenProcess, 5, g_OldAttrib, &g_OldAttrib); } // Implementation : UnInstall Hook void UnInstallHook() { VirtualProtect(OpenProcess, 5, PAGE_EXECUTE_READWRITE, &g_OldAttrib); // 寫回去就好了 memcpy(OpenProcess, g_OldAddress, 5); VirtualProtect(OpenProcess, 5, g_OldAttrib, &g_OldAttrib); }
2、ObjectHook:
ObjectHook相關內核數據結構知識分享,若是你寫過windows內核編程,那麼更容易理解一些。ide
如上圖所示:這就是Windows內核對象數據結構,表示註冊表、進程、線程等等。對象數據結構固然是對象管理器管理,全部的對象內部都會有OBJECT_HEADER的結構體,用來維護生命週期。函數
對象頭上面是對象頭引導,如OBJECT_HEADER_QUOTA_INFO、OBJECT_HEADER_HANDLE_INFO還有後面兩個,描述了相關對象額外的屬性,這些結構體對應着OBJECT_HEADER中的成員變量,以下介紹:
一、OBJECT_HEADER_NAME_INFO --> NameInfoOffset
二、OBJECT_HEADER_HANDLE_INFO --> HandleInfoOffset
三、OBJECT_HEADER_QUOTA_INFO --> QuotaInfoOffset工具
圖中OBJECT_HEADER結構體中前兩個PointerCount與HandleCount是引用計數。
一、PointerCount:內核模式對象引用數量
二、HandleCount:句柄數量
補充一些:
1. 內核中的指針引用. 一旦內核中新增了一個對象的引用, 則對象的引用計數自增一,若是一個對象的引用再也不有用,則引用計數自減一. 這兩種引用的增減是使用ObReferenceObjectByPointer和ObDereferenceObject致使的.
2. 一個進程打開一個對象併成功得到一個句柄, 會使得對象頭中的句柄計數自增一. 當一個句柄再也不被使用時, 句柄計數自減一. 這兩種引用的增減來自ObpIncrementHandleCount和ObpDecrementHandleCount函數.測試
+0x10指向的是_OBJECT_CREATE_INFORMATION
在建立CreateProcess時候會給該結構體申請空間,後面會填充該結構體信息。線程
咱們重點看一下偏移爲+0x008 OBJECT_TYPE,表示對於通用屬性(對象的通用屬性)存儲,以下圖所示:
OBJECT_TYPE.TypeInfo指向了OBJECT_TYPE_INITIALIZER結構體,這個結構體包含特定對象類型的函數,對象管理器用於各種的執行操做,以下圖所示:
你會發現我用紅色框框標記了上圖的一些成員,所謂的ObjectHook就是他們(替換地址),這些函數過程會在特定時機被調用。
如何找到對象頭?
我先們在windbg下來看一看,你要知道的一點是:內核變量ObTypeIndexTable是一個指針數組,它的每一個成員都指向一種對象類型的OBJECT_TYPE結構體,也就是說它是由一個指針數組維護的。
那麼咱們就好辦了,找到這個指針數組看一看
到底對不對?咱們來測試一下?
OBJECT_TYPE了+0x28就是_OBJECT_TYPE_INITIALIZER。windbg下面能找到,編寫代碼的時候如何獲取OBJECT_HEADER呢?
先來看一下,OBJECT_HEADER + 0x18,是成員變量Boby,這是什麼?這是對象主體,咱們能夠看到對象主體中OBJECT_DIRECTORY,DRIVER_OBJECT,DEVICE_OBJECT等對象結構,咱們在編寫windows內核編程的時候會建立驅動對象,設備對象,這樣就好說了。
假設驅動對象地址是OBJECT_HEADER + 0x18偏移的地方,那麼驅動對象地址-0x18則是OBJECT_HEADER的地址,以下圖所示:
上面代碼就是用匯編進行了的ObjectHook,若是理解了以上結構體概念,這些彙編代碼應該沒有難度。
爲何不用結構體去編程?由於我不想定義那麼多結構體,部分結構體windows是沒有公開的,須要本身在頭文件中定義,可是代碼中仍然給出了完整的結構體,能夠用結構體實現,源碼以下:
頭文件定義:
#include <ntddk.h>
/*定義的結構體信息*/ typedef struct _OBJECT_TYPE_INITIALIZER { USHORT Length; USHORT type; PVOID ObjectTypeCode; PVOID InvalidAttributes; GENERIC_MAPPING GenericMapping; PVOID ValidAccessMask; PVOID RetainAccess; POOL_TYPE PoolType; PVOID DefaultPagedPoolCharge; PVOID DefaultNonPagedPoolCharge; PVOID DumpProcedure; PVOID OpenProcedure; PVOID CloseProcedure; PVOID DeleteProcedure; PVOID ParseProcedure; PVOID SecurityProcedure; PVOID QueryNameProcedure; USHORT OkayToCloseProcedure; } OBJECT_TYPE_INITIALIZER, *POBJECT_TYPE_INITIALIZER; typedef struct _OBJECT_TYPE { LIST_ENTRY TypeList; // : _LIST_ENTRY UNICODE_STRING Name; // : _UNICODE_STRING PVOID DefaultObject; // : Ptr32 Void ULONG Index; // : UChar ULONG TotalNumberOfObjects; // : Uint4B ULONG TotalNumberOfHandles; // : Uint4B ULONG HighWaterNumberOfObjects; // : Uint4B ULONG HighWaterNumberOfHandles; // : Uint4B OBJECT_TYPE_INITIALIZER TypeInfo; // : _OBJECT_TYPE_INITIALIZER PVOID TypeLock; // : _EX_PUSH_LOCK ULONG Key; // : Uint4B LIST_ENTRY CallbackList; // : _LIST_ENTRY } OBJECT_TYPE, *POBJECT_TYPE; typedef struct _OBJECT_CREATE_INFORMATION { ULONG Attributes; HANDLE RootDirectory; KPROCESSOR_MODE ProbeMode; ULONG PagedPoolCharge; ULONG NonPagedPoolCharge; ULONG SecurityDescriptorCharge; PVOID SecurityDescriptor; PSECURITY_QUALITY_OF_SERVICE SecurityQos; SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService; } OBJECT_CREATE_INFORMATION, *POBJECT_CREATE_INFORMATION; typedef struct _OBJECT_HEADER { //對象頭部的指針計數,對對象頭指針引用的計數 LONG_PTR PointerCount; union { //句柄引用計數 LONG_PTR HandleCount; PVOID NextToFree; }; POBJECT_TYPE Type; //OBJECT_HEADER_NAME_INFO相對於此結構的偏移 UCHAR NameInfoOffset; //OBJECT_HEADER_HANDLE_INFO相對於此結構的偏移 UCHAR HandleInfoOffset; //OBJECT_HEADER_QUOTA_INFO相對於此結構的偏移 UCHAR QuotaInfoOffset; UCHAR Flags; union { //建立對象是用於建立對象附加頭的結構 //裏面保存了和附加對象頭相似的信息 PVOID ObjectCreateInfo; PVOID QuotaBlockCharged; }; PSECURITY_DESCRIPTOR SecurityDescriptor; QUAD Body; } OBJECT_HEADER, *POBJECT_HEADER; // 獲取頭信息 #define OBJECT_TO_OBJECT_HEADER(o)\ CONTAINING_RECORD((o),OBJECT_HEADER,Body) #define CONTAINING_RECORD(address,type,field)\ ((type*)(((ULONG_PTR)address)-(ULONG_PTR)(&(((type*)0)->field))))
代碼實現:
#include "HookHead.h" VOID UnLoadDriver() { } NTSTATUS MyDeleteProcedure(); NTSTATUS MaDefaultFunction(DEVICE_OBJECT* pDeviceObj, IRP* Irp) { Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } NTSTATUS DriverEntry(DRIVER_OBJECT* pDeviceObj, UNICODE_STRING* RegistryPath) { pDeviceObj->DriverUnload = UnLoadDriver; for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i) { pDeviceObj->MajorFunction[i] = MaDefaultFunction; } // 1. 獲取OBJECT_HEAD __asm { // 保存環境 pushad; pushfd; // 1 設備對象就是 OBJECT_BODY 也就是 -0x18是OBJECT_HREAD -0x10是OBJECT_TYPE(OBJECT_HANDLE + 0x8) lea eax, pDeviceObj; sub eax, 0x10; // 2 獲取到OBJECT_TYPE以後 地址加上0x3c則是DeleteProcdure(其實已經在OBJECT_TYPE_INITIALZER結構體中) lea eax, [eax + 0x3c]; // 3 DeleteProcdure地址替換成的MyDeleteProcedure地址 lea esi, MyDeleteProcedure; mov eax, esi; // 恢復環境 popad; popfd; } // 2. 獲取OBJECT_TYPE // 3. 替換相對應的函數 } NTSTATUS MyDeleteProcedure() { }
3、SsdtHook:
SSDT:System Service Descriptor Table,系統服務描述符表。這個表保存啥的?其實就是把三環零環的API聯起來,不僅僅是索引表,並且還包含一些索引基址、服務函數個數等。
先有個基本的概念,下面來看一張圖:
咱們發現不管是System support process,仍是Service processes他們都會進入內核模式以前都會通過Ntdll.dll模塊。而後經過系統服務調度程序到接口,到微內核,到HAL(驅動硬件相關聯的地方)。
因此內核層下也有微內核與HAL層,前面博客中介紹過一些相關的結構體如_EPROCESS裏面內嵌_KPROCESS,_EPROCESS被執行體層執行,而_KPROCESS是微內核層調度。
內核層分爲:
一、執行體層
二、微內核層
三、HAL層
用OD隨便跟蹤函數:
上面調用的函數有點奇怪,並非想象中的CALL [EDX],而是wow64cpu函數。這是用戶模式下實現的,做爲 ntdll.dll 和內核之間的層。若是你的應用程序是32位,爲了能在64位的系統上運行起來,就會調用這個函數兼容,Windows下的一個子系統。
該函數有三種返回值:
一、64位運行在64位系統下,不是WOW64模式,return 0;
二、32位運行在64位系統下,WOW64模式,return 1;
三、32位運行在32位系統下,return 0;
有點像傀儡進程同樣,函數過程大概是這樣,原進程會被建立(包括註冊表),而後判斷該程序信息,若是不是64位在C盤下(具體位置記不清楚),Temp的文件夾下建立一個64位的線程,修改註冊表信息等(不太準確,只是之前逆向的時候觀察過整個的過程)。
MOV EAX, 0x22(0x23) --> 保存調用號 ssdt表中的序號
EAX寄存器保存保存函數的調用號,其實到內核之後就靠調用號來確認調用的是哪一個函數。
對windows內核比較熟悉的應該知道,用戶的堆棧與內核的堆棧不是同一個堆棧空間,並且用戶層沒有權限去訪問內核層的數據,經過什麼進入內核層?一條彙編指令 SYSENTER,以下圖所示:
cs:ip執行這一條彙編指令以後,你將進入到內核。如何作到的呢?其實在SYSENTRY指令以前,先會用edx保存esp的值,執行SYSENTER時候會讀取特殊寄存器MSR模組寄存器,以下圖所示:
沒有名字,只有編號,經過如下兩條彙編指令對MSR進行操做:
操做碼 | 指令 | 說明 |
---|---|---|
0F 32 | RDMSR | 將 ECX 指定的 MSR 加載到 EDX:EAX |
操做碼 | 指令 | 說明 |
---|---|---|
0F 30 | WRMSR | 將 EDX:EAX 中的值寫入 ECX 指定的 MSR |
詳細:
一、RDMSR:將 ECX 寄存器指定的 64 位型號專用寄存器 (MSR) 的內容加載到寄存器 EDX:EAX。EDX 寄存器中加載 MSR 的高 32 位,EAX 寄存器中加載低 32 位。在讀取的 MSR 中,若是實現的位數小於 64,則返回 EDX:EAX 中未實現的位的值未定義。
二、WRMSR :將寄存器 EDX:EAX 的內容寫入 ECX 寄存器指定的 64 位型號專用寄存器 (MSR)。高 32 位從 EDX 複製,低 32 位從 EAX 複製。MSR 中未定義或保留的位老是設置爲上次讀取時的值。
補充一下:其實有些KiFastCallEntry Hook你們看到這裏應該明白,其實就是改變編號0x176保存的地址。
當SYSENTER執行時候,就把寄存器的值初始化成真正的寄存器CS,ESP,EIP寄存器的數據。這時候就跑到KiFastCallEntry。
KiFastCallEntry函數你們有興趣能夠分析下,那麼對上面的流程更爲清晰,怎樣去分析呢,windbg下就能夠,以下圖所示:
居然進入到了內核層,把用戶棧的內容拷貝到內核棧,可是拷貝多少個字節?參數個數?
經過eax在用戶層保存的序號,就能找到函數地址。經過調用號做爲序號,就能找到參數個數,個數*4就是總字節,其實這張表就是SSDT,還有一張表叫ShadowSSDT,專門用於保存和用戶界面相關服務,內核中還有兩張沒有使用的表,以下圖所示(兩個結構體)。
_KSYSTEM_SERVICE_TABLE即是SSDT的結構體,經過上圖咱們知道了SSDT的結構,另外一個結構體保存了這四張表。
windbg如何找到ssdt?
一、dd KeServiceDescriptorTable(由ntoskrnl.exe導出)。
二、還能夠經過_KTHREAD + 0xbc來找到ServiceTableBase結構體基址。
使用結構體解析地址看一下,以下圖所示:
圖中標紅便對應着結構體成員值,函數地址表首地址、每一個函數被調用次數、服務函數個數191個,參數表地址。
HOOK的是什麼? 其實HOOK的就是函數地址表中的地址,當內核層經過調用號找到ssdt中的索引號,調用的是咱們本身的函數地址便可。
究竟是不是這樣?通過測試確實是這樣,下圖是源碼測試圖:
咱們用ark工具看一下,以下圖所示:
源碼以下:
頭文件:
#pragma once #include <ntddk.h> #define CTL_SSDT_ENABLE \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x801, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) #define CTL_SSDT_DISABLE \ CTL_CODE(FILE_DEVICE_UNKNOWN, 0x802, METHOD_OUT_DIRECT, FILE_ANY_ACCESS) typedef struct _KSERVICE_TABLE_DESCRIPTOR { KSYSTEM_SERVICE_TABLE ntoskrnl; // ntoskrnl.exe的服務函數,即SSDT KSYSTEM_SERVICE_TABLE win32k; // win32k.sys的服務函數(GDI32.dll/User32.dll 的內核支持),即ShadowSSDT KSYSTEM_SERVICE_TABLE notUsed1; // 不使用 KSYSTEM_SERVICE_TABLE notUsed2; // 不使用 }KSERVICE_TABLE_DESCRIPTOR, *PKSERVICE_TABLE_DESCRIPTOR; typedef struct _KSYSTEM_SERVICE_TABLE { PULONG ServiceTableBase; // 函數地址表的首地址 PULONG ServiceCounterTableBase; // 函數表中每一個函數被調用的次數 ULONG NumberOfService; // 服務函數的個數, NumberOfService * 4 就是整個地址表的大小 UCHAR* ParamTableBase; // 參數個數表首地址 } KSYSTEM_SERVICE_TABLE, *PKSYSTEM_SERVICE_TABLE; // 僞函數(Hook的函數) typedef NTSTATUS(NTAPI*FnNtOpenProcess)(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId); // 保存舊的地址 FnNtOpenProcess g_OldNtOpenProcess; // 定義KeServieDescriptorTableShadow KSERVICE_TABLE_DESCRIPTOR* g_ServiceTab = NULL; // 保存被保護進程PID HANDLE g_Pid = 0;
驅動層:
#include "SSDTHookHead.h" // 聲明:驅動卸載 VOID DriverUnLoad(DRIVER_OBJECT* pDeviceobj); // 聲明:默認初始化 NTSTATUS DefaultFunction(DEVICE_OBJECT* pDeviceObj, IRP* Irp); // 聲明:控制碼 NTSTATUS ControlCode(DEVICE_OBJECT* pDeviceObj, IRP* Irp); // 聲明:安裝HOOK VOID InstallHook(); // 聲明:卸載HOOK VOID UnInstallHook(); // 聲明:關閉分頁保護 NTSTATUS ShudowMemoryPageProtect(); // 聲明:開啓分頁保護 NTSTATUS StartMemoryPageProtect(); // Hook實現函數 NTSTATUS MyOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId); // 聲明:入口點 NTSTATUS DriverEntry(DRIVER_OBJECT* pDriverObj, UNICODE_STRING* RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); DEVICE_OBJECT* pDeviceObj = NULL; UNICODE_STRING DevName; UNICODE_STRING SymbolicLinkName; NTSTATUS Status = STATUS_SUCCESS; RtlInitUnicodeString(&DevName, L"\\Device\\SsdtHook"); RtlInitUnicodeString(&SymbolicLinkName, L"\\DosDevices\\SymbolicLinkName"); // DbgBreakPoint(); pDriverObj->DriverUnload = DriverUnLoad; for (int i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; ++i) { pDriverObj->MajorFunction[i] = DefaultFunction; } // 設備對象 Status = IoCreateDevice(pDriverObj, 0, &DevName, FILE_DEVICE_UNKNOWN, 0, 0, &pDeviceObj); if (!NT_SUCCESS(Status)) return Status; // 使用緩衝區的方式進行3環與0環通信 pDriverObj->Flags = DO_BUFFERED_IO; // 符號對象暴露給三環使用 Status = IoCreateSymbolicLink(&SymbolicLinkName, &DevName); if (!NT_SUCCESS(Status)) return Status; pDriverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ControlCode; return STATUS_SUCCESS; } VOID DriverUnLoad(DRIVER_OBJECT* pDeviceobj) { UNICODE_STRING DeleteSymblolicLinkName; RtlInitUnicodeString(&DeleteSymblolicLinkName, L"\\DosDevices\\SymbolicLinkName"); IoDeleteSymbolicLink(&DeleteSymblolicLinkName); IoDeleteDevice(pDeviceobj->DeviceObject); } // 實現:默認初始化 NTSTATUS DefaultFunction(DEVICE_OBJECT* pDeviceObj, IRP* Irp) { UNREFERENCED_PARAMETER(pDeviceObj); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } // 實現:控制碼 NTSTATUS ControlCode(DEVICE_OBJECT* pDeviceObj, IRP* Irp) { // DbgBreakPoint(); UNREFERENCED_PARAMETER(pDeviceObj); // 經過Irp棧數據獲取控制碼 // NTSTATUS nStatus = STATUS_SUCCESS; PIO_STACK_LOCATION pIrpStack = IoGetCurrentIrpStackLocation(Irp); ULONG uControlCode = pIrpStack->Parameters.DeviceIoControl.IoControlCode; // 經過MDL獲取須要保護的Pid // g_Pid = (HANDLE)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority); PVOID Pbuf = Irp->AssociatedIrp.SystemBuffer; RtlCopyMemory((PVOID)&g_Pid, Pbuf, sizeof(ULONG)); switch (uControlCode) { case CTL_SSDT_ENABLE: { // DbgBreakPoint(); InstallHook(); } break; case CTL_SSDT_DISABLE: { UnInstallHook(); } break; default: break; } Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } // 實現:安裝HOOK VOID InstallHook() { // DbgBreakPoint(); // 1.1 獲取當前線程 PETHREAD pThread = PsGetCurrentThread(); // 1.2 線程結構體 +0xbc 獲取的是 ServiceTable g_ServiceTab = (KSERVICE_TABLE_DESCRIPTOR*)(*(ULONG*)((ULONG_PTR)pThread + 0xbc)); // 1.3 獲取SSDT地址基址且保存原始的函數VA g_OldNtOpenProcess = (FnNtOpenProcess)g_ServiceTab->ntoskrnl.ServiceTableBase[0xBE]; // 1.4 替換修改地址(這個地方先要關閉頁保護) /* 只介紹其中的三種: PE - 是否啓用保護模式,置1則啓用 PG - 是否使用分頁模式, 置1則開啓分頁模式, 此標誌置1時,PE標誌也必須置1,不然CPU報異常. WP - WP==1時, 不能修改只讀的內存頁 , WP==0 時, 能夠修改只讀的內存頁. */ ShudowMemoryPageProtect(); g_ServiceTab->ntoskrnl.ServiceTableBase[0xBE] = (ULONG)MyOpenProcess; StartMemoryPageProtect(); } // 實現:卸載HOOK VOID UnInstallHook() { ShudowMemoryPageProtect(); g_ServiceTab->ntoskrnl.ServiceTableBase[0xBE] = (ULONG)g_OldNtOpenProcess; // DbgBreakPoint(); StartMemoryPageProtect(); } // 實現:關閉分頁保護 NTSTATUS ShudowMemoryPageProtect() { __asm { pushad; pushfd; mov eax, cr0; // 前提內存保護必定是開啓的 WP = 1 不然..就給開啓了 and eax, ~0x10000; mov cr0, eax; popfd; popad; } } // 實現:開啓分頁保護 NTSTATUS StartMemoryPageProtect() { __asm { pushad; pushfd; mov eax, cr0; or eax, 0x10000; mov cr0, eax; popfd; popad; } } // 實現:HOOK函數 NTSTATUS MyOpenProcess(PHANDLE ProcessHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PCLIENT_ID ClientId) { // 訪問權限PROCESS_ALL_ACCESS改成NULL if (ClientId->UniqueProcess == g_Pid) { DbgBreakPoint(); DesiredAccess = 0; } return g_OldNtOpenProcess(ProcessHandle, DesiredAccess, ObjectAttributes, ClientId); }