SSDT表的知识目录:
A、了解SSDT结构
B、由SSDT索引号获取当前函数地址
C、如何获取索引号
D、获取起源地址-判断SSDT是否被HOOK
E、如何向内核地址写入自己代码
A、了解SSDT结构
SSDT的全称是 System ServicesDescriptor Table--系统服务描述符表,是由 ntoskrnl.exe导出KeServiceDescriptorTable 这个表,是全局的,声明导出即可在编程中使用。
typedef struct ServiceDescriptorEntry
{
unsigned int *ServiceTableBase; //SystemService Dispatch Table 的基地址
unsigned int *ServiceCounterTable(0);
//包含着 SSDT 中每个服务被调用次数的计数器。这个计数器一般由 sysenter 更新。
unsigned int NumberOfServices; //由 ServiceTableBase描述的服务的数目。
unsigned char *ParamTableBase;
//包含每个系统服务参数字节数表的基地址-系统服务参数表
} SSDT_Entry;
用windbg 了解SSDT结构,命令:
dp nt!KeServiceDescriptorTable
B、由SSDT索引号获取当前函数地址
[[KeServiceDescriptorTable基址] + index*4]
C、如何获取SSDT索引号
用工具查看如:狙剑、火眼等
D、获取起源地址-判断SSDT是否被HOOK
MmGetSystemRoutineAddress函数,得到系统导出函数的地址
包含头文件Wdm.h 或Ntddk.h
//NTKERNELAPI PVOID MmGetSystemRoutineAddress ( __in PUNICODE_STRING SystemRoutineName ) /*++ Routine Description: This functionreturns the address of the argument function pointer if it is in the kernelor HAL, NULL if it is not. Arguments: SystemRoutineName -Supplies the name of the desired routine. Return Value: Non-NULL functionpointer if successful. NULL if not. Environment: Kernel mode,PASSIVE_LEVEL, arbitrary process context. --*/ { PKTHREADCurrentThread; NTSTATUS Status; PKLDR_DATA_TABLE_ENTRY DataTableEntry; ANSI_STRINGAnsiString; PLIST_ENTRYNextEntry; UNICODE_STRINGKernelString; UNICODE_STRINGHalString; PVOIDFunctionAddress; LOGICAL Found; ULONGEntriesChecked; ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); EntriesChecked = 0; FunctionAddress =NULL; KernelString.Buffer= (const PUSHORT) KERNEL_NAME; KernelString.Length= sizeof (KERNEL_NAME) - sizeof (WCHAR); KernelString.MaximumLength = sizeof KERNEL_NAME; HalString.Buffer =(const PUSHORT) HAL_NAME; HalString.Length =sizeof (HAL_NAME) - sizeof (WCHAR); HalString.MaximumLength = sizeof HAL_NAME; do { Status =RtlUnicodeStringToAnsiString (&AnsiString, SystemRoutineName, TRUE); if (NT_SUCCESS(Status)) { break; } KeDelayExecutionThread (KernelMode, FALSE,(PLARGE_INTEGER)&MmShortTime); } while (TRUE); // // Arbitraryprocess context so prevent suspend APCs now. // CurrentThread =KeGetCurrentThread (); KeEnterCriticalRegionThread(CurrentThread); ExAcquireResourceSharedLite (&PsLoadedModuleResource, TRUE); // // Check only thekernel and the HAL for exports. // NextEntry =PsLoadedModuleList.Flink; while (NextEntry !=&PsLoadedModuleList) { Found = FALSE; DataTableEntry= CONTAINING_RECORD(NextEntry, KLDR_DATA_TABLE_ENTRY, InLoadOrderLinks); if (RtlEqualUnicodeString(&KernelString, &DataTableEntry->BaseDllName, TRUE)) { Found =TRUE; EntriesChecked += 1; } else if(RtlEqualUnicodeString (&HalString, &DataTableEntry->BaseDllName, TRUE)){ Found =TRUE; EntriesChecked += 1; } if (Found ==TRUE) { FunctionAddress = MiFindExportedRoutineByName(DataTableEntry->DllBase, &AnsiString); if(FunctionAddress != NULL) { break; } if (EntriesChecked== 2) { break; } } NextEntry =NextEntry->Flink; } ExReleaseResourceLite (&PsLoadedModuleResource); KeLeaveCriticalRegionThread (CurrentThread); RtlFreeAnsiString(&AnsiString); returnFunctionAddress; } //WRK中的源码
E、如何向内核地址写入自己代码
在后期版本的操作系统中,要求SSDT是只读的,因此任何合法的程序都不可能修改这个表,不过聪明的大牛们还是想出了修改SSDT表的方法。
方法一:更改注册表(需要重启)
HKLM\SYSTEM\CurrentControlset\Control\SessionManger\MemoryManagement\EnforceWriteProtection= 0
与
HKLM\SYSTEM\CurrentControlset\Control\SessionManger\MemoryManagement\DisablePagingExecutive= 1
方法二:修改CR0寄存器的第1位即wp位置0
//关闭内存保护 _asm { push eax mov eax, CR0 and eax, 0FFFEFFFFh mov CR0, eax pop eax } //恢复内存保护 _asm { push eax mov eax, CR0 or eax , not 0FFFEFFFFh mov CR0, eax pop eax }
方法三:利用MDL(Memory Descriptor List)来绕过写保护
//MDL reference defined inntddk.h typedef struct _MDL{ Struct _MDL *Next; CSHORT Size; CSHORT MdlFlags; Struct _EPROCESS *Process; PVOID MappedSystemVa; PVOID StartVa; ULONG ByteCount; ULONG ByteOffset; }MDL,*PMDL; //MDL Flags #define MDL_MAPPED_TO_SYSTEM_VA 0x0001 #define MDL_PAGES_LOCKED 0x0002 #define MDL_SOURCE_IS_NONPAGED_POOL 0x0004 #define MDL_ALLOCATED_FIXED_SIZE 0x0008 #define MDL_PARTIAL 0x0010 #define MDL_PARTIAL_HAS_BEEN_MAPPED 0x0020 #define MDL_IO_PAGE_READ 0x0040 #define MDL_WRITE_OPERATION 0x0080 #define MDL_PARENT_MAPPED_SYSTEM_VA 0x0100 #define MDL_LOCK_HELD 0x0200 #define MDL_PHYSICAL_VIEW 0x0400 #define MDL_IO_SPACE 0x0800 #define MDL_NETWORK_HEADER 0x1000 #define MDL_MAPPING_CAN_FAIL 0x2000 #define MDL_ALLOCATED_MUST_SUCCEED 0x4000 //声明 #pragma pack(1) typedef struct ServiceDescriptorEntry{ unsigned int* ServiceTableBase; unsigned int* ServiceCounterTableBase; unsigned int NumberOfService; unsigned char* ParamTableBase; }SSDT_Entry; #pragma pack() //导出SSDT表 _declspec(dllimport)SSDT_Entry KeServiceDescriptorTable; //保存原始的系统调用地址 PMDL g_pmdlSystemCall; PVOID *MappedSystemCallTable; //创建MDL g_pmdlSystemCall = MmCreateMdl(NULL, KeServiceDescriptorTable, ServiceTableBase, KeServcieDescriptorTable.NumberOfService*4); if(!g_pmdlSystemCall) { return STATUS_UNSUCCESSFUL; } //构建非分页内存 MmBuildMdlForNonPagedPool(g_pmdlSystemCall); //改变MDL的标志 g_pmdlSystemCall->MdlFlags = g_pmdlSystemCall->MdlFlags | MDL_MAPPED_TO_SYSTEM_VA ; //锁定内存 MappedSystemCallTable = MmMapLockedPages(g_pmdlSystemCall, KernelMode);
参考资料:
《Windows内核的安全防护》推荐
http://blog.csdn.net/evi10r/article/details/6840564