前一篇写了 inline hook和ssdt两种方式,但是相关的的某些东西没讲清楚。
windows rookit防护-Kernel Hook 1.5
前一篇写了 inline hook和ssdt两种方式,但是相关的的某些东西没讲清楚。
TL,DR:介绍驱动加载的方法,PatchGuard简介 ,如何在测试中关闭/开启KVAS保护(Shadow SSDT中的KiSystemCall64Shadow相关),然后测试ssdt/shadow ssdt hook方法,最后内容是Shadow SSDT枚举。
Ring3 加载一个sys文件
首先有一个hello world的驱动,尝试使用命令行加载他(使用管理员权限)
1 2 3 4 5 sc create <使用的名称> type = kernel start = demand binPath="/??/<驱动文件路径,这里应该用的是描述符格式>" sc start <使用的名称> sc stop <使用的名称> sc delete <使用的名称>
sc是Service Control的简称,本质是操作 Windows 的 SCM(服务控制管理器),这里的加载驱动本质上是创建一个 kernel类型的服务,然后启动。
服务控制管理器(SCM)在系统启动时启动。 它是远程过程调用 (RPC) 服务器,以便服务配置和服务控制程序可以作远程计算机上的服务。
服务函数为 SCM 执行的以下任务提供接口:
维护已安装服务的数据库。
在系统启动时或按需启动服务和驱动程序服务。
枚举已安装的服务和驱动程序服务。
维护运行服务和驱动程序服务的状态信息。
将控制请求传输到正在运行的服务。
锁定和解锁服务数据库。
https://learn.microsoft.com/zh-cn/windows/win32/services/service-control-manager
那么sc是如何完成这一过程的呢?对应的代码在附录的《简单的ring3驱动加载器》
Ring0 加载一个sys文件
正常做法就是先注册表HKLM\SYSTEM\CurrentControlSet\Services\<service name>
然后调用ZwLoadDriver API
1 2 3 4 5 6 7 UNICODE_STRING regPath; RtlInitUnicodeString ( ®Path, L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\MyDriver2" ); NTSTATUS status = ZwLoadDriver (®Path);
**那么使用手动加载呢?**这里需要PE 文件格式的基础,可以看:PE文件格式解析
这里修改下,就可以的得到内核手动映射,不过这个暂时过不了__security_cookie的检测,所以得自己设置AddressofEntry的值或者改写cookie,代码在附录的《手动映射SYS》 ,注意修改
main中的文件路径
ExecuteMappedDriver中的entry偏移
FixImportTable中while循环中的IAT个数大小
PatchGuard
具体的分析可能会后续重新写一篇文章,如果实在想提前了解可以参考
https://blog.tetrane.com/downloads/Tetrane_PatchGuard_Analysis_RS4_v1.01.pdf
基本上所有的入门介绍都或多或少参考了这个PDF
本质上,它是内核的重要组成部分,与内核的其他部分一样在 Ring 0 中运行。它并非某种对 Ring 0 代码拥有更大权限的 Ring -1 机制(至少本文讲的范围内没有)。它的主要目的是检查关键的内核结构和代码,确保它们没有被篡改。如果检测到任何未经授权的修改,它会触发蓝屏死机,并附带错误代码CRITICAL_STRUCTURE_CORRUPTION和错误检查代码0x109,从而避免任何错误或混淆。它明确地表明 Ring 0 中某些不应该被修改的内容已被修改。
并且由于PG的异步机制,因此无法确定它何时会检查关键结构和代码。
主要检测的内容有
IDT(中断描述符表)和 GDT(全局描述符表)
GDT :是IA-32和x86-64架构特有的二进制数据结构。它包含向CPU提供有关内存段的条目。
IDT :是一种专用于 IA-32 和 x86-64 架构的二进制数据结构。它是保护模式和长模式下与实模式中断向量表 (IVT) 对应的机制,用于告知 CPU 中断服务例程 (ISR) 的位置(每个中断向量对应一个 ISR)。
MSR (模型特定寄存器):CPU 寄存器,用于控制高级行为,例如功能、限制和执行流程管理。
SSDT (系统服务描述符表):一个包含指向实现系统调用(例如NtCreateFile,、NtOpenProcess等)的内核函数的指针的表。
内核栈
内核结构
全局变量
KPP引擎
关于KVAS保护
KVAS全称是Kernel Virtual Address Shadow,它的出现与MeltDown(CVE-2017-5754)和TotalMeltDown(CVE-2018-1038)有关。
我的描述不一定准确,大致上来说这两个漏洞利用了CPU的乱序执行技术,即CPU在执行时不一定会按照流程执行。当我们访问一个不能被用户模式访问的内存页时,CPU会执行该语句然后将其缓存到内存中,等到发现不能访问后返回错误,但是该数据依旧存在于缓存当中。利用这种思路就可以完全读取内核中的数据,实现权限提升等。
微软为了缓解该漏洞,从用户页表中隔离出内核页表,让用户态访问到的内核页表也是经过映射的,并且会将用户页表自动标记为NX,让我们的shellcode无法执行
笔者在这里遇到了很有趣的一点就是在最新的AMD Ryzen9 8945HX 上
1 2 3 4 5 DbgPrint("Driver Loaded\n" ); DriverObject->DriverUnload = DriverUnload; DWORD64 dmsr = __readmsr(0xC0000082 ); DbgPrint("KiSystemCall64 at %p\n" , dmsr);
无法让结果是**KiSystemCall64Shadow**(Shadow SSDT依旧是存在的!),反倒是在Intel i5 9300H的CPU上成功实现了。
所以这里只讲在受影响的Intel CPU上如何实现的
Windows内核缓解机制使用了Kva Shadow内存,尝试将其关闭
在注册表HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management
创建两个DWORD值:FeatureSettingsOverride FeatureSettingsOverrideMask,设置如下后重启
设置值为3,然后重启
**这两个值存在并且值正确的时候,关闭KVAS,**使用的就是KiSystemCall64,找到的是SSDT
删除这两个,开启KVAS 的时候就为KiSystemCall64Shadow,需要处理的是Shadow SSDT
Full SSDT Hook
上一篇文章提到 主要的问题是在64位系统上根据 ssdt 的算法,不能直接将新的函数的地址直接转为数组的下表(寻址范围太小 )
找到临近的空位内存并申请
我手动调试找到了一块合适的位置进行测试,这一过程可以被自动化
我选择了在ntoskrl.exe的.text最后一部分,根据RVA找到位置,而且调试后发现距离ssdt->table仅有0x04ff189
在新申请的临近位置内编写trampoline,跳转到新的函数
最后的效果:
实现代码在附录[《完整SSDT Hook》](##完整SSDT Hook)部分
枚举Shadow SSDT
由于前文关于KVAS保护 中所提到的内容,这里要分两种情况进行探讨了
从KiSystemCall64到Shadow SSDT(我使用AMD CPU)
从KiSystemCall64Shadow到Shadow SSDT(我使用Intel CPU)
从KiSystemCall64开始
回顾前文
然后继续向下查到KiSystemServiceRepeat,主要是为了拿到KeServiceDescriptorTable或者KeServiceDescriptorTableShadow的值,在这里就是
35 05 9F 00和AE B6 8E 00
我们只需要改特征值找到KeServiceDescriptorTableShadow
1 2 3 4 5 6 7 8 for (size_t i = 0 ; i < 0x1000 ; i++){ if (*(tempptr + i) == 0x4c && *(tempptr + i + 1 ) == 0x8d && *(tempptr + i + 2 ) == 0x1D ) { offset = *((PLONG)(tempptr + i + 3 )); table = (PSYSTEM_SERVICE_TABLE)(tempptr + i + 7 + offset); break ; } }
1 dps nt!KeServiceDescriptorTableShadow
对比一下SSDT
多了更多的win32k的函数,并且这里是看不了win32k!W32pServiceTable的
附加到使用了win32k的GUI用户态进程就可以看到了,这里attach winlogon.exe
这个是文件管理器explore.exe看到的
到了这里大多数网上的教程就讲的不清楚了,所以我也是慢慢调试出来的
突破点是尝试看下win32k!NtUserQueryWindow
地址居然在win32k!W32pServiceTable之前(比win32k!W32pServiceTable小),而且注意到win32k!W32pServiceTable中的值均为ff开头,不免的让人想起这是做减法
win32k!NtUserQueryWindow在table前0x70c3c,尝试使用table的第一个数做减法
总结出公式
1 RealFuncAddr = table - (0xFFFFFFFF-(DWORD)W32pServiceTable[index])>>4
测试代码可见附录[《Shadow SSDT 枚举》](##Shadow SSDT 枚举)
DriverEntry中的HANDLE pid = (HANDLE)600;需要修改,这里我用的是winlogon.exe的pid
关于结构体SYSTEM_SERVICE_TABLE的定义可以精简
Hook :手法和SSDT的一致
从KiSystemCall64Shadow开始
这个时候要完成两件事情了,KiSystemCall64Shadow找到SSDT和ShadowSSDT
跟踪KiSystemCall64Shadow找到KiSystemServiceUser
KiSystemServiceUser向下就会找到老熟人
然后用老方法即可,无非就是多几段字节序列搜索
引用
https://learn.microsoft.com/zh-cn/windows/win32/services/service-control-manager
https://r0keb.github.io/posts/PatchGuard-Internals/
https://standa-note.blogspot.com/2015/10/some-tips-to-analyze-patchguard.html
https://blog.tetrane.com/downloads/Tetrane_PatchGuard_Analysis_RS4_v1.01.pdf
https://joe1sn.eu.org/2024/01/25/win-hevd-exp-stackoverflow-III/
附录
简单的ring3驱动加载器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 #include <windows.h> #include <iostream> #include <string> bool CreateDriver (const std::string& name, const std::string& path) { SC_HANDLE hSCM = OpenSCManager (NULL , NULL , SC_MANAGER_CREATE_SERVICE); if (!hSCM) { std::cerr << "OpenSCManager failed: " << GetLastError () << std::endl; return false ; } std::string ntPath = "\\??\\" + path; SC_HANDLE hService = CreateServiceA ( hSCM, name.c_str (), name.c_str (), SERVICE_START | DELETE | SERVICE_STOP, SERVICE_KERNEL_DRIVER, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, ntPath.c_str (), NULL , NULL , NULL , NULL , NULL ); if (!hService) { DWORD err = GetLastError (); if (err == ERROR_SERVICE_EXISTS) { std::cout << "Service already exists." << std::endl; } else { std::cerr << "CreateService failed: " << err << std::endl; CloseServiceHandle (hSCM); return false ; } } else { std::cout << "Service created successfully." << std::endl; CloseServiceHandle (hService); } CloseServiceHandle (hSCM); return true ; } bool StartDriver (const std::string& name) { SC_HANDLE hSCM = OpenSCManager (NULL , NULL , SC_MANAGER_CONNECT); if (!hSCM) return false ; SC_HANDLE hService = OpenServiceA (hSCM, name.c_str (), SERVICE_START); if (!hService) { std::cerr << "OpenService failed: " << GetLastError () << std::endl; CloseServiceHandle (hSCM); return false ; } if (!StartService (hService, 0 , NULL )) { DWORD err = GetLastError (); if (err == ERROR_SERVICE_ALREADY_RUNNING) { std::cout << "Already running." << std::endl; } else { std::cerr << "StartService failed: " << err << std::endl; } } else { std::cout << "Driver started." << std::endl; } CloseServiceHandle (hService); CloseServiceHandle (hSCM); return true ; } bool StopDriver (const std::string& name) { SC_HANDLE hSCM = OpenSCManager (NULL , NULL , SC_MANAGER_CONNECT); if (!hSCM) return false ; SC_HANDLE hService = OpenServiceA (hSCM, name.c_str (), SERVICE_STOP); if (!hService) { std::cerr << "OpenService failed: " << GetLastError () << std::endl; CloseServiceHandle (hSCM); return false ; } SERVICE_STATUS status; if (!ControlService (hService, SERVICE_CONTROL_STOP, &status)) { std::cerr << "Stop failed: " << GetLastError () << std::endl; } else { std::cout << "Driver stopped." << std::endl; } CloseServiceHandle (hService); CloseServiceHandle (hSCM); return true ; } bool DeleteDriver (const std::string& name) { SC_HANDLE hSCM = OpenSCManager (NULL , NULL , SC_MANAGER_CONNECT); if (!hSCM) return false ; SC_HANDLE hService = OpenServiceA (hSCM, name.c_str (), DELETE); if (!hService) { std::cerr << "OpenService failed: " << GetLastError () << std::endl; CloseServiceHandle (hSCM); return false ; } if (!DeleteService (hService)) { std::cerr << "Delete failed: " << GetLastError () << std::endl; } else { std::cout << "Service deleted." << std::endl; } CloseServiceHandle (hService); CloseServiceHandle (hSCM); return true ; } int main (int argc, char * argv[]) { if (argc < 3 ) { std::cout << "Usage:\n" << " create <name> <path>\n" << " start <name>\n" << " stop <name>\n" << " delete <name>\n" ; return 0 ; } std::string action = argv[1 ]; std::string name = argv[2 ]; if (action == "create" ) { if (argc < 4 ) { std::cerr << "Missing driver path.\n" ; return 1 ; } std::string path = argv[3 ]; CreateDriver (name, path); } else if (action == "start" ) { StartDriver (name); } else if (action == "stop" ) { StopDriver (name); } else if (action == "delete" ) { DeleteDriver (name); } else { std::cerr << "Unknown action.\n" ; } return 0 ; }
手动映射SYS
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 #include <ntifs.h> #include <ntimage.h> typedef NTSTATUS (*DriverEntry_t) ( PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath ) ;typedef struct _SYSTEM_MODULE_ENTRY { PVOID Reserved[2 ]; PVOID ImageBase; ULONG ImageSize; ULONG Flags; USHORT Index; USHORT Unknown; USHORT LoadCount; USHORT ModuleNameOffset; CHAR FullPathName[256 ]; } SYSTEM_MODULE_ENTRY, * PSYSTEM_MODULE_ENTRY; typedef struct _SYSTEM_MODULE_INFORMATION { ULONG NumberOfModules; SYSTEM_MODULE_ENTRY Modules[1 ]; } SYSTEM_MODULE_INFORMATION, * PSYSTEM_MODULE_INFORMATION; typedef enum _SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0 , SystemModuleInformation = 11 } SYSTEM_INFORMATION_CLASS; NTSYSAPI NTSTATUS NTAPI ZwQuerySystemInformation ( SYSTEM_INFORMATION_CLASS SystemInformationClass, PVOID SystemInformation, ULONG SystemInformationLength, PULONG ReturnLength ) ;PVOID GetKernelBase (PCSTR ModuleName) { NTSTATUS status; ULONG size = 0 ; PVOID buffer = NULL ; PVOID base = NULL ; status = ZwQuerySystemInformation( SystemModuleInformation, NULL , 0 , &size ); if (status != STATUS_INFO_LENGTH_MISMATCH) return NULL ; buffer = ExAllocatePool2( POOL_FLAG_NON_PAGED, size, 'bKmM' ); if (!buffer) return NULL ; status = ZwQuerySystemInformation( SystemModuleInformation, buffer, size, &size ); if (!NT_SUCCESS(status)) { ExFreePool(buffer); return NULL ; } PSYSTEM_MODULE_INFORMATION modules = (PSYSTEM_MODULE_INFORMATION)buffer; for (ULONG i = 0 ; i < modules->NumberOfModules; i++) { PSYSTEM_MODULE_ENTRY mod = &modules->Modules[i]; PCSTR fullPath = mod->FullPathName; PCSTR fileName = fullPath + mod->ModuleNameOffset; if (_stricmp(fileName, ModuleName) == 0 ) { base = mod->ImageBase; break ; } } ExFreePool(buffer); return base; } PVOID GetKernelModuleBaseByName (PCSTR moduleName) { NTSTATUS status; ULONG size = 0 ; PVOID buffer = NULL ; PVOID base = NULL ; status = ZwQuerySystemInformation(SystemModuleInformation, NULL , 0 , &size); if (status != STATUS_INFO_LENGTH_MISMATCH) return NULL ; buffer = ExAllocatePool2(POOL_FLAG_NON_PAGED, size, 'tag1' ); if (!buffer) return NULL ; status = ZwQuerySystemInformation(SystemModuleInformation, buffer, size, &size); if (!NT_SUCCESS(status)) { ExFreePool(buffer); return NULL ; } PSYSTEM_MODULE_INFORMATION sysModules = (PSYSTEM_MODULE_INFORMATION)buffer; for (ULONG i = 0 ; i < sysModules->NumberOfModules; i++) { PSYSTEM_MODULE_ENTRY entry = &sysModules->Modules[i]; PCSTR name = entry->FullPathName + entry->ModuleNameOffset; if (_stricmp(name, moduleName) == 0 ) { base = entry->ImageBase; break ; } } ExFreePool(buffer); return base; } PVOID GetKernelExport (PVOID moduleBase, PCSTR functionName) { if (!moduleBase || !functionName) return NULL ; PIMAGE_DOS_HEADER dosHeader = (PIMAGE_DOS_HEADER)moduleBase; if (dosHeader->e_magic != IMAGE_DOS_SIGNATURE) return NULL ; PIMAGE_NT_HEADERS64 ntHeaders = (PIMAGE_NT_HEADERS64)((PUCHAR)moduleBase + dosHeader->e_lfanew); if (ntHeaders->Signature != IMAGE_NT_SIGNATURE) return NULL ; IMAGE_DATA_DIRECTORY exportDirData = ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; if (!exportDirData.Size) return NULL ; PIMAGE_EXPORT_DIRECTORY exportDir = (PIMAGE_EXPORT_DIRECTORY)((PUCHAR)moduleBase + exportDirData.VirtualAddress); ULONG* nameRvas = (ULONG*)((PUCHAR)moduleBase + exportDir->AddressOfNames); USHORT* ordinals = (USHORT*)((PUCHAR)moduleBase + exportDir->AddressOfNameOrdinals); ULONG* functions = (ULONG*)((PUCHAR)moduleBase + exportDir->AddressOfFunctions); for (ULONG i = 0 ; i < exportDir->NumberOfNames; i++) { PCSTR name = (PCSTR)((PUCHAR)moduleBase + nameRvas[i]); if (_stricmp(name, functionName) == 0 ) { USHORT ordinal = ordinals[i]; ULONG funcRva = functions[ordinal]; return (PVOID)((PUCHAR)moduleBase + funcRva); } } return NULL ; } BOOLEAN FixRelocation (PVOID newBase, PIMAGE_NT_HEADERS64 nt) { ULONGLONG delta = (ULONGLONG)newBase - nt->OptionalHeader.ImageBase; if (delta == 0 ) return TRUE; IMAGE_DATA_DIRECTORY relocDir = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; if (!relocDir.Size) return TRUE; PIMAGE_BASE_RELOCATION reloc = (PIMAGE_BASE_RELOCATION)((PUCHAR)newBase + relocDir.VirtualAddress); PUCHAR end = (PUCHAR)reloc + relocDir.Size; while ((PUCHAR)reloc < end && reloc->SizeOfBlock) { if (reloc->SizeOfBlock < sizeof (IMAGE_BASE_RELOCATION)) break ; UINT32 count = (reloc->SizeOfBlock - sizeof (IMAGE_BASE_RELOCATION)) / sizeof (USHORT); PUSHORT list = (PUSHORT)((PUCHAR)reloc + sizeof (IMAGE_BASE_RELOCATION)); for (UINT32 i = 0 ; i < count; i++) { USHORT type = list [i] >> 12 ; USHORT offset = list [i] & 0xFFF ; if (type == IMAGE_REL_BASED_ABSOLUTE) continue ; if (type == IMAGE_REL_BASED_DIR64) { PULONGLONG addr = (PULONGLONG)((PUCHAR)newBase + reloc->VirtualAddress + offset); *addr += delta; } } reloc = (PIMAGE_BASE_RELOCATION)((PUCHAR)reloc + reloc->SizeOfBlock); } return TRUE; } BOOLEAN FixImportTable (PVOID imageBase, PIMAGE_NT_HEADERS64 nt) { IMAGE_DATA_DIRECTORY importDir = nt->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; if (!importDir.Size) return TRUE; PIMAGE_IMPORT_DESCRIPTOR import = (PIMAGE_IMPORT_DESCRIPTOR)((PUCHAR)imageBase + importDir.VirtualAddress); size_t i = 0 ; while (i < 2 ) { ++i; PCSTR moduleName = (PCSTR)((PUCHAR)imageBase + import->Name); PVOID moduleBase = GetKernelBase(moduleName); if (!moduleBase) return FALSE; PIMAGE_THUNK_DATA64 thunk = (PIMAGE_THUNK_DATA64)((PUCHAR)imageBase + import->FirstThunk); PIMAGE_THUNK_DATA64 origThunk = (PIMAGE_THUNK_DATA64)((PUCHAR)imageBase + import->OriginalFirstThunk); if (import->OriginalFirstThunk) { origThunk = (PIMAGE_THUNK_DATA64)((PUCHAR)imageBase + import->OriginalFirstThunk); } else { origThunk = (PIMAGE_THUNK_DATA64)((PUCHAR)imageBase + import->FirstThunk); } while (origThunk->u1.AddressOfData) { PIMAGE_IMPORT_BY_NAME importName = (PIMAGE_IMPORT_BY_NAME)((PUCHAR)imageBase + origThunk->u1.AddressOfData); PVOID fn = GetKernelExport(moduleBase, importName->Name); if (!fn) return FALSE; thunk->u1.Function = (ULONGLONG)fn; origThunk++; thunk++; } import++; } return TRUE; } NTSTATUS ExecuteMappedDriver (PDRIVER_OBJECT DriverObject, PVOID imageBase) { PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)imageBase; PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((PUCHAR)imageBase + dos->e_lfanew); ULONG entryRVA = nt->OptionalHeader.AddressOfEntryPoint; DriverEntry_t entry = (DriverEntry_t)((PUCHAR)imageBase + 0x2C ); UNICODE_STRING fakeReg; RtlInitUnicodeString(&fakeReg, L"\\Registry\\Machine\\System\\Fake" ); return entry(DriverObject, &fakeReg); } NTSTATUS MapImage (PDRIVER_OBJECT DriverObject, PVOID buffer) { PIMAGE_DOS_HEADER dos = (PIMAGE_DOS_HEADER)buffer; PIMAGE_NT_HEADERS nt = (PIMAGE_NT_HEADERS)((PUCHAR)buffer + dos->e_lfanew); SIZE_T imageSize = nt->OptionalHeader.SizeOfImage; PVOID imageBase = ExAllocatePool(NonPagedPool, imageSize); if (!imageBase) return STATUS_INSUFFICIENT_RESOURCES; RtlZeroMemory(imageBase, imageSize); RtlCopyMemory(imageBase, buffer, nt->OptionalHeader.SizeOfHeaders); PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(nt); for (int i = 0 ; i < nt->FileHeader.NumberOfSections; i++) { PVOID dest = (PUCHAR)imageBase + section[i].VirtualAddress; PVOID src = (PUCHAR)buffer + section[i].PointerToRawData; RtlCopyMemory(dest, src, section[i].SizeOfRawData); } FixRelocation(imageBase, nt); FixImportTable(imageBase, nt); return ExecuteMappedDriver(DriverObject, imageBase); } NTSTATUS ReadFileToBuffer ( PCWSTR filePath, PVOID* outBuffer, PULONG outSize ) { NTSTATUS status; HANDLE fileHandle; OBJECT_ATTRIBUTES objAttr; IO_STATUS_BLOCK ioStatus; UNICODE_STRING uFilePath; *outBuffer = NULL ; *outSize = 0 ; RtlInitUnicodeString(&uFilePath, filePath); InitializeObjectAttributes( &objAttr, &uFilePath, OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE, NULL , NULL ); status = ZwCreateFile( &fileHandle, GENERIC_READ, &objAttr, &ioStatus, NULL , FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN, FILE_NON_DIRECTORY_FILE | FILE_SYNCHRONOUS_IO_NONALERT, NULL , 0 ); if (!NT_SUCCESS(status)) { DbgPrint("ZwCreateFile failed: 0x%X\n" , status); return status; } FILE_STANDARD_INFORMATION fileInfo; status = ZwQueryInformationFile( fileHandle, &ioStatus, &fileInfo, sizeof (fileInfo), FileStandardInformation ); if (!NT_SUCCESS(status)) { ZwClose(fileHandle); return status; } ULONG fileSize = (ULONG)fileInfo.EndOfFile.QuadPart; PVOID buffer = ExAllocatePool(NonPagedPool, fileSize); if (!buffer) { ZwClose(fileHandle); return STATUS_INSUFFICIENT_RESOURCES; } status = ZwReadFile( fileHandle, NULL , NULL , NULL , &ioStatus, buffer, fileSize, NULL , NULL ); if (!NT_SUCCESS(status)) { ExFreePool(buffer); ZwClose(fileHandle); return status; } ZwClose(fileHandle); *outBuffer = buffer; *outSize = fileSize; DbgPrint("Read file success, size: %lu\n" , fileSize); return STATUS_SUCCESS; } VOID DriverUnload (PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); DbgPrint("Driver Stopping -> %wZ\n" , &DriverObject->DriverName); } NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); DbgPrint("Driver Running -> %wZ\n" , &DriverObject->DriverName); DriverObject->DriverUnload = DriverUnload; PVOID buffer; ULONG size; NTSTATUS status = ReadFileToBuffer( L"\\??\\C:\\Users\\WDKRemoteUser\\Desktop\\test\\helloworld.sys" , &buffer, &size ); if (!NT_SUCCESS(status)) { DbgPrint("Can't read file\n" ); return status; } MapImage(DriverObject, buffer); status = STATUS_SUCCESS; return status; }
完整SSDT Hook
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 #include "helper.h" #include <ntifs.h> typedef NTSTATUS (*NTOPENFILE) ( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG OpenOptions ) ;typedef struct _SYSTEM_SERVICE_TABLE { PVOID tableBase; PVOID serviceCountBase; ULONG64 numberOfServices; PVOID unkown; }SYSTEM_SERVICE_TABLE, * PSYSTEM_SERVICE_TABLE; NTOPENFILE OriginalNtOpenFile = NULL ; ULONG NtOpenFileIndex = 51 ; PSYSTEM_SERVICE_TABLE g_Table = NULL ; ULONG64 g_Trampoline = 0x0 ; NTSTATUS HookNtOpenFile ( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG OpenOptions ) { if (ObjectAttributes && ObjectAttributes->ObjectName) { DbgPrint("HookNtOpenFile: %wZ\n" , ObjectAttributes->ObjectName); } return OriginalNtOpenFile( FileHandle, DesiredAccess, ObjectAttributes, IoStatusBlock, ShareAccess, OpenOptions ); } LONG64 GetFuncAddr (size_t index, PSYSTEM_SERVICE_TABLE ssdt) { if (index > ssdt->numberOfServices) return 0 ; LONG offset = ((PLONG)(ssdt->tableBase))[index] >> 4 ; return (DWORDLONG)(ssdt->tableBase) + offset; } VOID DisableWP () { ULONG64 cr0 = __readcr0(); cr0 &= 0xfffffffffffeffff ; __writecr0(cr0); _disable(); } VOID EnableWP () { ULONG64 cr0 = __readcr0(); cr0 |= 0x10000 ; __writecr0(cr0); _enable(); } VOID HookSSDT (PSYSTEM_SERVICE_TABLE ssdt) { PULONG table = ssdt->tableBase; ULONG entry = table[NtOpenFileIndex]; ULONG64 base = (ULONG64)table; ULONG64 original = base + (entry >> 4 ); OriginalNtOpenFile = (NTOPENFILE)original; ULONG param = entry & 0xF ; if (g_Trampoline == 0 ) { return ; } ULONG64 hookAddr = (ULONG64)g_Trampoline; ULONG newEntry = (ULONG)(((hookAddr - base) << 4 ) | param); DisableWP(); table[NtOpenFileIndex] = newEntry; EnableWP(); } VOID UnhookSSDT (PSYSTEM_SERVICE_TABLE ssdt) { PULONG table = ssdt->tableBase; ULONG entry = table[NtOpenFileIndex]; ULONG param = entry & 0xF ; ULONG64 base = (ULONG64)table; ULONG64 original = (ULONG64)OriginalNtOpenFile; ULONG newEntry = (ULONG)(((original - base) << 4 ) | param); DisableWP(); table[NtOpenFileIndex] = newEntry; EnableWP(); } PUCHAR BuildTrampoline (ULONG64 funcAddr) { static UCHAR trampo[12 ] = { 0x48 , 0xB8 , 0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 ,0x00 , 0xFF , 0xE0 }; *(PULONG64)&trampo[2 ] = funcAddr; return trampo; } VOID WriteTpCode (PSYSTEM_SERVICE_TABLE ssdt, PUCHAR jmpCode) { UNREFERENCED_PARAMETER(jmpCode); PUCHAR tpAddr = (PUCHAR)ssdt->tableBase + 0x04ff189 ; DbgPrint("[ssdt hook] trampoline code at %p\n" , tpAddr); DisableWP(); RtlCopyMemory(tpAddr, jmpCode, 12 ); EnableWP(); g_Trampoline = (ULONG64)tpAddr; } VOID DriverUnload (PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); UnhookSSDT(g_Table); DbgPrint("Driver Unloaded\n" ); } NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); DbgPrint("Driver Loaded\n" ); DriverObject->DriverUnload = DriverUnload; PVOID kernelbase = GetKernelBase("ntoskrnl.exe" ); DbgPrint("[ssdt hook]ntoskrnl base: %p\n" , kernelbase); DWORD64 dmsr = __readmsr(0xC0000082 ); DbgPrint("KiSystemCall64 at %p\n" , dmsr); PUCHAR tempptr = (PUCHAR)(dmsr); LONG offset = 0 ; PSYSTEM_SERVICE_TABLE table = NULL ; for (size_t i = 0 ; i < 0x1000 ; i++) { if (*(tempptr + i) == 0x4c && *(tempptr + i + 1 ) == 0x8d && *(tempptr + i + 2 ) == 0x15 ) { offset = *((PLONG)(tempptr + i + 3 )); table = (PSYSTEM_SERVICE_TABLE)(tempptr + i + 7 + offset); break ; } } if (offset == 0 ) { DbgPrint("Not found KeServiceDescriptorTable\n" ); return STATUS_NOT_FOUND; } g_Table = table; DbgPrint("KeServiceDescriptorTable at: %p\n tableBase: %p\n MyHook at: %p\n" , table, table->tableBase, HookNtOpenFile); DbgPrint("NtOpenFile at: %p\n" , GetFuncAddr(NtOpenFileIndex, table)); PUCHAR tpCode = BuildTrampoline((ULONG64)HookNtOpenFile); DbgPrint("Trampoline code at: %p\n" , tpCode); WriteTpCode(table, tpCode); HookSSDT(table); DbgPrint("[hooked] NtOpenFile at: %p\n" , GetFuncAddr(NtOpenFileIndex, table)); return STATUS_SUCCESS; }
Shadow SSDT 枚举
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 #include <ntifs.h> #include <ntddk.h> extern PEPROCESS NTAPI PsGetNextProcess (_In_opt_ PEPROCESS Process) ;extern UCHAR* NTAPI PsGetProcessImageFileName (_In_ PEPROCESS Process) ;typedef NTSTATUS (*NTOPENFILE) ( PHANDLE FileHandle, ACCESS_MASK DesiredAccess, POBJECT_ATTRIBUTES ObjectAttributes, PIO_STATUS_BLOCK IoStatusBlock, ULONG ShareAccess, ULONG OpenOptions ) ;typedef struct _SYSTEM_SERVICE_TABLE { PVOID tableBase; PVOID serviceCountBase; ULONG64 numberOfServices; PVOID unkown1; PVOID shadowTableBase; PVOID shaodwserviceCountBase; ULONG64 numberOfShadowServices; PVOID unkown2; }SYSTEM_SERVICE_TABLE, * PSYSTEM_SERVICE_TABLE; PSYSTEM_SERVICE_TABLE g_Table = 0x0 ; LONG64 GetFuncAddr (size_t index, PSYSTEM_SERVICE_TABLE ssdt) { if (index > ssdt->numberOfShadowServices) return 0 ; LONG offset = (0xFFFFFFFF - ((PLONG)(ssdt->shadowTableBase))[index]) >> 4 ; return (DWORDLONG)(ssdt->shadowTableBase) - offset; } VOID DriverUnload (PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); DbgPrint("Driver Unloaded\n" ); } NTSTATUS DriverEntry (PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); DbgPrint("Driver Loaded\n" ); DriverObject->DriverUnload = DriverUnload; DWORD64 dmsr = __readmsr(0xC0000082 ); DbgPrint("KiSystemCall64 at %p\n" , dmsr); PUCHAR tempptr = (PUCHAR)(dmsr); LONG offset = 0 ; PSYSTEM_SERVICE_TABLE table = NULL ; for (size_t i = 0 ; i < 0x1000 ; i++) { if (*(tempptr + i) == 0x4c && *(tempptr + i + 1 ) == 0x8d && *(tempptr + i + 2 ) == 0x1D ) { offset = *((PLONG)(tempptr + i + 3 )); table = (PSYSTEM_SERVICE_TABLE)(tempptr + i + 7 + offset); break ; } } if (offset == 0 ) { DbgPrint("Not found KeServiceDescriptorTableShadow\n" ); return STATUS_NOT_FOUND; } g_Table = table; DbgPrint("KeServiceDescriptorTableShadow at: %p\n tableBase: %p\n" , table, table->shadowTableBase); HANDLE pid = (HANDLE)600 ; PEPROCESS Process = NULL ; if (NT_SUCCESS(PsLookupProcessByProcessId(pid, &Process))) { KAPC_STATE state; KeStackAttachProcess(Process, &state); for (size_t i = 0 ; i < table->numberOfShadowServices; i++) { DbgPrint("No.%d func at: %p\n" , i, GetFuncAddr(i, table)); } KeUnstackDetachProcess(&state); ObDereferenceObject(Process); } return STATUS_SUCCESS; }