1 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 2 // 3 // 驱动编程: HOOK SSDT 4 // 5 ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 6 #include "ntddk.h" 7 8 // 全局变量 9 // 声明NtOpenProcess 函数指针 10 typedef NTSTATUS (_stdcall *NTOPENPROCESS ) ( __out PHANDLE ProcessHandle, 11 __in ACCESS_MASK DesiredAccess, 12 __in POBJECT_ATTRIBUTES ObjectAttributes , 13 __in_opt PCLIENT_ID ClientId ); 14 15 ULONG g_nOldNtOpenProcessAddr ; // 存储NtOpenProcess原地址 16 17 // 内存字节对齐 18 #pragma pack(1) 19 20 //struct: ServiceDescriptorEntry_t 21 typedef struct ServiceDescriptorEntry 22 { 23 unsigned int * ServiceTableBase; // SSDT 基址+偏移 (索引 )定位内核函数 24 unsigned int * ServiceCounterTableBase; // checked build 25 unsigned int nNumberOfServices; // SSDT 中函数个数 26 unsigned char * ParamTableBase; // SSDT中函数的参数 27 }ServiceDescriptorEntry_t, * PServiceDescriptorTableEntry_t; 28 29 #pragma pack() 30 31 // 导出( 微软未公开,需导出 ) 32 _declspec(dllimport ) ServiceDescriptorEntry_t KeServiceDescriptorTable; 33 34 35 // 恢复内存页保护 36 void fnPageProtectOn () 37 { // 【 or】 : 按位“或”运算 ,当且仅当两操作数对应位都为“”时 38 _asm // 结果相应位为“”,否则结果相应位为“”。 39 { 40 mov eax, cr0 // cr0寄存器内核保护标志位当前为 41 or eax,10000h // 将 h通过 or汇编指令按位 "或" 运算,cr0寄存器标志位 42 mov cr0, eax // 从变为 ,从而达到恢复内存保护页的目的 43 sti // sti汇编指令: 允许中断发生 44 } 45 } 46 47 // 清除内存页保护 48 void fnPageProtectOff () 49 { // 【 and】 : 按位“与”操作 ,当且仅当两操作数对应位都为“”时 50 _asm // 结果的相应位为“”,否则结果相应位为“”。 51 { 52 cli // cli汇编指令: 禁用中断发生 ;当修改中断向量或堆栈指针时 , 53 // 必须先禁用 ,否则产生中断会破坏当前的环境 ,造成程序崩溃 54 mov eax, cr0 // cr0寄存器保存了内核保护的标志位 :保护状态 ,否则为 ; 55 and eax, not 10000h // 将 h取反并通过 and指令按位 "与" 运算,cr0寄存器标志 56 mov cr0, eax // 位从变成 ,从而达到了取消内存页保护的目的 57 } 58 } 59 60 61 // 实际执行NtOpenProcess 函数的函数 62 NTSTATUS fnNewNtOpenProcess ( __out PHANDLE ProcessHandle, 63 __in ACCESS_MASK DesiredAccess, 64 __in POBJECT_ATTRIBUTES ObjectAttributes, 65 __in_opt PCLIENT_ID ClientId) 66 { 67 // PEPROCESS结构体偏移 x16c即 :ImageFileName 存放的是当前进程的名称 68 KdPrint(( "当前调用fnNewNtOpenProcess 函数的进程: %s", (UCHAR*)PsGetCurrentProcess ()+0x16c)); 69 70 // g_nOldNtOpenProcessAddr中存储原 NtOpenProcess地址, 强制转换成原函数指针类型后下发 71 return ((NTOPENPROCESS)g_nOldNtOpenProcessAddr )(ProcessHandle , DesiredAccess,ObjectAttributes , ClientId ); 72 } 73 74 NTSTATUS fnHookNtOpenProcess (void ) 75 { 76 /************************************************************************************** 77 //ULONG nIndex; 78 // 遍历SSDT 中函数的内存地址 79 for ( nIndex=0; nIndex<KeServiceDescriptorTable.nNumberOfServices; ++nIndex) 80 { 81 KdPrint(("ServiceTable[%d]:%X",nIndex, KeServiceDescriptorTable.pnServiceTableBase[nIndex])); 82 } 83 **************************************************************************************/ 84 85 // 关闭页保护 86 fnPageProtectOff(); 87 88 // 替换目标函数地址前 ,先保存原地址 89 g_nOldNtOpenProcessAddr = KeServiceDescriptorTable.ServiceTableBase [190]; 90 91 // NtOpenProcess内存地址保存在 pnServiceTableBase索引第位, 92 // 将 NtOpenProcess内存地址替换为目标函数内存地址 (fnNewNtOpenProcess) 93 KeServiceDescriptorTable. ServiceTableBase[190] = (unsigned int)fnNewNtOpenProcess ; 94 // 恢复页保护 95 fnPageProtectOn(); 96 return STATUS_SUCCESS; 97 } 98 99 // 卸载hook 100 void fnUnHookNtOpenProcess () 101 { 102 // 先关闭内存页保护 103 fnPageProtectOff(); 104 // 再恢复 NtOpenProcess 105 KeServiceDescriptorTable. ServiceTableBase[190] = (unsigned int)g_nOldNtOpenProcessAddr ; 106 // 最后恢复页保护 107 fnPageProtectOn(); 108 } 109 110 // 卸载驱动 111 void fnUnLoadDriver (PDRIVER_OBJECT pDriverObj ) 112 { 113 fnUnHookNtOpenProcess(); // 卸载 Hook 114 KdPrint(( "驱动卸载成功!" )); 115 } 116 // 驱动入口 117 NTSTATUS DriverEntry (PDRIVER_OBJECT pDriverObj , PUNICODE_STRING pszRegisterPath ) 118 { 119 KdPrint(( "驱动加载成功!" )); 120 fnHookNtOpenProcess(); // 开始 Hook 121 pDriverObj-> DriverUnload = fnUnLoadDriver; 122 return STATUS_SUCCESS; 123 }