SSDT(System Services Descriptor Table),系統服務描述符表。這個表就是一個把ring3的Win32 API和ring0的核心API聯系起來。SSDT并不僅僅隻包含一個龐大的位址索引表,它還包含着一些其它有用的資訊,諸如位址索引的基位址、服務函數個數等。
通過修改此表的函數位址可以對常用windows函數及API進行hook,進而實作對一些關心的系統動作進行過濾、監控的目的。一些HIPS、防毒軟體、系統監控、系統資料庫監控軟體往往會采用此接口來實作自己的監控子產品,
目前極個别病毒确實會采用這種方法來保護自己或者破壞防毒軟體,但在這種病毒進入系統前如果防毒軟體能夠識别并清除它将沒有機會發作.
SSDT到底是什麼呢?打一個比方,SSDT相當于系統内部API的指向标,作用就是告訴系統,需要調用的API在什麼地方。
請看源碼解析
/*************************************************************************
*
*ZwQuerySystemInformation函數常用結構
*
**************************************************************************/
#ifndef _SPS_H_
#define _SPS_H_ 1
#include <ntddk.h>
#include "table.h"
#include "pe.h"
#endif
typedef enum _SYSTEM_INFORMATION_CLASS
{
SystemBasicInformation, // 0
SystemProcessorInformation, // 1
SystemPerformanceInformation, // 2
SystemTimeOfDayInformation, // 3
SystemNotImplemented1, // 4
SystemProcessesAndThreadsInformation, // 5
SystemCallCounts, // 6
SystemConfigurationInformation, // 7
SystemProcessorTimes, // 8
SystemGlobalFlag, // 9
SystemNotImplemented2, // 10
SystemModuleInformation, // 11 系統子產品
SystemLockInformation, // 12
SystemNotImplemented3, // 13
SystemNotImplemented4, // 14
SystemNotImplemented5, // 15
SystemHandleInformation, // 16
SystemObjectInformation, // 17
SystemPagefileInformation, // 18
SystemInstructionEmulationCounts, // 19
SystemInvalidInfoClass1, // 20
SystemCacheInformation, // 21
SystemPoolTagInformation, // 22
SystemProcessorStatistics, // 23
SystemDpcInformation, // 24
SystemNotImplemented6, // 25
SystemLoadImage, // 26
SystemUnloadImage, // 27
SystemTimeAdjustment, // 28
SystemNotImplemented7, // 29
SystemNotImplemented8, // 30
SystemNotImplemented9, // 31
SystemCrashDumpInformation, // 32
SystemExceptionInformation, // 33
SystemCrashDumpStateInformation, // 34
SystemKernelDebuggerInformation, // 35
SystemContextSwitchInformation, // 36
SystemRegistryQuotaInformation, // 37
SystemLoadAndCallImage, // 38
SystemPrioritySeparation, // 39
SystemNotImplemented10, // 40
SystemNotImplemented11, // 41
SystemInvalidInfoClass2, // 42
SystemInvalidInfoClass3, // 43
SystemTimeZoneInformation, // 44
SystemLookasideInformation, // 45
SystemSetTimeSlipEvent, // 46
SystemCreateSession, // 47
SystemDeleteSession, // 48
SystemInvalidInfoClass4, // 49
SystemRangeStartInformation, // 50
SystemVerifierInformation, // 51
SystemAddVerifier, // 52
SystemSessionProcessesInformation // 53
}SYSTEM_INFORMATION_CLASS; //核心子產品類型,我們要列舉的是SystemProcessesAndThreadsInformation,程序和線程信
//線程資訊
typedef struct _SYSTEM_THREAD_INFORMATION
{
LARGE_INTEGER KernelTime;
LARGE_INTEGER UserTime;
LARGE_INTEGER CreateTime;
ULONG WaitTime;
PVOID StartAddress;
CLIENT_ID ClientId;
KPRIORITY Priority;
KPRIORITY BasePriority;
ULONG ContextSwitchCount;
LONG State;
LONG WaitReason;
} SYSTEM_THREAD_INFORMATION, *PSYSTEM_THREAD_INFORMATION; //線程結構
//程序資訊
typedef struct _SYSTEM_PROCESS_INFORMATION
{
ULONG NextEntryDelta; //NextEntryOffset下一個程序結構的偏移量,每一個程序對應一個結構
//最後一個程序的NextEntryOffset=0
ULONG NumberOfThreads; //線程數目
LARGE_INTEGER Reserved[3];
LARGE_INTEGER CreateTime; //建立時間
LARGE_INTEGER UserTime; //使用者模式(Ring 3)的CPU時間
LARGE_INTEGER KernelTime; //核心模式(Ring 0)的CPU時間
UNICODE_STRING ProcessName; //程序名
KPRIORITY BasePriority; //程序優先權
ULONG ProcessId; //程序辨別符
ULONG InheritedFromProcessId; //父程序的辨別符
ULONG HandleCount; //句柄數目
ULONG Reserved2[2];
ULONG PrivatePageCount;
VM_COUNTERS VirtualMemoryCounters; //虛拟存儲器的結構
IO_COUNTERS IoCounters; //IO計數結構
SYSTEM_THREAD_INFORMATION Threads[0]; //程序相關線程的結構數組
} SYSTEM_PROCESS_INFORMATION, *PSYSTEM_PROCESS_INFORMATION;
//------------------------------------------------------------------------------
//子產品資訊
typedef struct _SYSTEM_MODULE_INFORMATION {
ULONG Reserved[2];
PVOID Base;
ULONG Size;
ULONG Flags;
USHORT Index;
USHORT Unknown;
USHORT LoadCount;
USHORT ModuleNameOffset;
CHAR ImageName[256];
} SYSTEM_MODULE_INFORMATION, *PSYSTEM_MODULE_INFORMATION;
//子產品清單
typedef struct _SYSMODULELIST{
ULONG ulCount;
SYSTEM_MODULE_INFORMATION smi[1];
} SYSMODULELIST, *PSYSMODULELIST;
//-----------------------------------------------------------------------------------
//DRIVER_SECTION結構
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;
union {
LIST_ENTRY HashLinks;
struct
{
PVOID SectionPointer;
ULONG CheckSum;
};
};
union {
struct
{
ULONG TimeDateStamp;
};
struct
{
PVOID LoadedImports;
};
};
struct _ACTIVATION_CONTEXT * EntryPointActivationContext;
PVOID PatchInformation;
}LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
//===================================================================================
NTKERNELAPI NTSTATUS
ZwQuerySystemInformation(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
); //最終是通過周遊EPROCESS擷取的
typedef NTSTATUS (*ZWQUERYSYSTEMINFORMATION)(
IN SYSTEM_INFORMATION_CLASS SystemInformationClass,
OUT PVOID SystemInformation,
IN ULONG SystemInformationLength,
OUT PULONG ReturnLength OPTIONAL
); //定義結構
NTSYSAPI BOOLEAN NTAPI KeAddSystemServiceTable(
ULONG lpAddressTable,
BOOLEAN bUnknown,
ULONG dwNumEntries,
ULONG lpParameterTable,
ULONG dwTableID
);
//****************************函數聲明*************************************
//根據位址查找子產品
void FindModuleByAddress( ULONG Address, PVOID buffer);
//根據RVA查找SSDT 檔案偏移
ULONG FindFileOffsetByRva( ULONG ModuleAddress,ULONG Rva);
//路徑解析出子程序名
void GetModuleName( char *ProcessPath, char *ProcessName);
//根據服務号得到目前的位址
ULONG FindOriAddress( ULONG index );
//得到SSDT Shadow表位址
ULONG GetSSDTShadowAddress2();
ULONG GetWin32Base2( PDRIVER_OBJECT driver);
ULONG FindShadowOriAddress( ULONG index );
/*****************************************************************************************
*
*函數名:FindModuleByAddress
*功能描述:根據函數位址查找所屬子產品
*
******************************************************************************************/
/*****************************************************************************************
*
* 原理: 利用ZwQuerySystemInformation傳入SystemModuleInformation(11)得到系統子產品清單
* 得到每個子產品的起始和結束位址
* 比對位址,在那個範圍就屬于哪個子產品
* 得到子產品名
*
******************************************************************************************/
#include "refresh.h"
void FindModuleByAddress( ULONG Address, PVOID buffer)
{
NTSTATUS status;
ULONG size;
ULONG i;
ULONG minAddress;
ULONG maxAddress;
PSYSMODULELIST List;
ZwQuerySystemInformation( SystemModuleInformation ,&size,0,&size);
KdPrint(("[FindModuleByAddress] size:0x%x\n",size));
List=(PSYSMODULELIST)ExAllocatePool(NonPagedPool,size);
if(List==NULL)
{
KdPrint(("[FindModuleByAddress] malloc memory failed\n"));
return ;
}
status=ZwQuerySystemInformation(SystemModuleInformation,List,size,0);
if(!NT_SUCCESS(status))
{
KdPrint(("[FindModuleByAddress] query failed\n"));
//列印錯誤
KdPrint(("[FindModuleByAddress] status: 0x%x\n",status));
ExFreePool( List );
return ;
}
//得到了子產品連結清單
//判斷子產品名
for( i=0; i<List->ulCount; i++)
{
//得到子產品的範圍
minAddress = (ULONG)List->smi[i].Base;
maxAddress = minAddress + List->smi[i].Size;
//判斷位址
if( Address >= minAddress && Address <= maxAddress )
{
memcpy( buffer, List->smi[i].ImageName,sizeof(List->smi[i].ImageName));
KdPrint(("[FindModuleByAddress] modulename: %s\n",buffer));
//釋放記憶體
ExFreePool(List);
break;
}
}
}
/***************************************************************************************
*
* 函數名:GetOriFunctionAddress
* 功能描述:得到原始SSDT表中函數位址
*
****************************************************************************************/
/***************************************************************************************
*
* 原理: 找到核心檔案,擷取基址BaseAddress
* 根據核心檔案查找SSDT表的檔案偏移SSDTFileOffset = SSDTRVA-(節RVA-節Offset)
* 讀取函數的檔案偏移FunctionFileOffset
* VA=BaseAddress+FunctionFileOffset-00400000=800d8000 + FunctionFileOffset
*
*****************************************************************************************/
/****************************************************************************************
*
* 根據RVA查找所在的檔案偏移:FileOffset = Rva- (節Rva - 節Offset)
* 找到區塊表
*****************************************************************************************/
ULONG FindFileOffsetByRva( ULONG ModuleAddress,ULONG Rva)
{
PIMAGE_DOS_HEADER dos;
PIMAGE_FILE_HEADER file;
PIMAGE_SECTION_HEADER section;
//區塊數目
ULONG number;
ULONG i;
ULONG minAddress;
ULONG maxAddress;
ULONG SeFileOffset;
ULONG FileOffset;
dos = (PIMAGE_DOS_HEADER)ModuleAddress;
file = (PIMAGE_FILE_HEADER)( ModuleAddress + dos->e_lfanew + 4 );
//得到區塊數量
number = file->NumberOfSections;
KdPrint(("[FindFileOffsetByRva] number :0x%x\n",number));
//得到第一個區塊位址
section = (PIMAGE_SECTION_HEADER)(ModuleAddress + dos->e_lfanew + 4 + sizeof(IMAGE_FILE_HEADER) + file->SizeOfOptionalHeader);
for( i=0;i<number;i++)
{
minAddress = section[i].VirtualAddress;
maxAddress = minAddress + section[i].SizeOfRawData;
SeFileOffset = section[i].PointerToRawData;
if( Rva > minAddress && Rva < maxAddress)
{
KdPrint(("[FindFileOffsetByRva] minAddress :0x%x\n",minAddress));
KdPrint(("[FindFileOffsetByRva] SeFileOffset :0x%x\n",SeFileOffset));
FileOffset = Rva - ( minAddress - SeFileOffset);
KdPrint(("[FindFileOffsetByRva] FileOffset :0x%x\n",FileOffset));
break ;
}
}
return FileOffset;
}
//路徑解析出子程序名
void GetModuleName( char *ProcessPath, char *ProcessName)
{
ULONG n = strlen( ProcessPath) - 1;
ULONG i = n;
KdPrint(("%d",n));
while( ProcessPath[i] != '\\')
{
i = i-1;
}
strncpy( ProcessName, ProcessPath+i+1,n-i);
}
/****************************************************************************************
*
* 根據傳入的服務号得到函數原始位址
*
****************************************************************************************/
ULONG FindOriAddress( ULONG index )
{
//根據傳入的index得到函數VA位址
//重定位函數位址
//BaseAddress - 0x00400000 + *(PULONG)(FileOffset+(index*4))
//ZwQuerySystemInformation得到核心檔案基位址
//得到SSDT表的位址
//得到SSDT RVA 查找SSDT RVA所在的節
NTSTATUS status;
ULONG size;
ULONG BaseAddress;
ULONG SsdtRva;
ULONG FileOffset = 0;
PSYSMODULELIST list;
char Name[32]={0};
char PathName[256] = "\\SystemRoot\\system32\\";
ANSI_STRING name;
UNICODE_STRING modulename;
OBJECT_ATTRIBUTES object_attributes;
IO_STATUS_BLOCK io_status = {0};
HANDLE hFile;
//讀取的位置
ULONG location;
LARGE_INTEGER offset;
ULONG address;
//得到需要申請的記憶體大小
ZwQuerySystemInformation( SystemModuleInformation,&size,0,&size );
//申請記憶體
list = (PSYSMODULELIST) ExAllocatePool( NonPagedPool,size );
//驗證是否申請成功
if( list == NULL)
{
//申請失敗
KdPrint(("[FindOriAddress] malloc memory failed\n"));
ExFreePool(list);
return 0;
}
status = ZwQuerySystemInformation( SystemModuleInformation,list,size,0);
if( !NT_SUCCESS( status ))
{
//擷取資訊失敗
KdPrint(("[FindOriAddress] query failed\n"));
KdPrint(("[FindOriAddress] status:0x%x\n",status));
ExFreePool(list);
return 0;
}
//得到子產品基址,第一個子產品為核心檔案
BaseAddress = (ULONG )list->smi[0].Base;
KdPrint(("[FindOriAddress] BaseAddress:0x%x\n",BaseAddress));
//分離出核心檔案名
GetModuleName(list->smi[0].ImageName,Name);
KdPrint(("[FindOriAddress] processname:%s\n",Name));
strcat(PathName,Name);
RtlInitAnsiString(&name,PathName);
RtlAnsiStringToUnicodeString(&modulename,&name,TRUE);
KdPrint(("[FindOriAddress] modulename: %wZ\n",&modulename));
ExFreePool(list);
//經驗證位址正确
//得到SSDT表的Rva
SsdtRva = (ULONG)KeServiceDescriptorTable->ServiceTableBase - BaseAddress;
//驗證
KdPrint(("[FindOriAddress] SsdtRva:0x%x\n",SsdtRva));
//根據RVA查找檔案偏移,//得到檔案偏移了
FileOffset= FindFileOffsetByRva( BaseAddress,SsdtRva);
KdPrint(("[FindOriAddress] FileOffset:0x%x\n",FileOffset));
//讀取的位置
location = FileOffset + index * 4;
offset.QuadPart =location;
KdPrint(("[FindOriAddress] location:0x%x\n",location));
//利用ZwReadFile讀取檔案
//初始化OBJECT_ATTRIBUTES結構
InitializeObjectAttributes(
&object_attributes,
&modulename,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
//打開檔案
status = ZwCreateFile(
&hFile,
FILE_EXECUTE | SYNCHRONIZE,
&object_attributes,
&io_status,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE |
FILE_RANDOM_ACCESS |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if( !NT_SUCCESS( status ))
{
KdPrint(("[FindOriAddress] open error\n"));
KdPrint(("[FindOriAddress] status = 0x%x\n", status));
ZwClose( hFile );
return 0;
}
status = ZwReadFile(
hFile,
NULL,
NULL,
NULL,
NULL,
&address,
sizeof(ULONG),
&offset,
NULL);
if( !NT_SUCCESS( status ))
{
KdPrint(("[FindOriAddress] read error\n"));
KdPrint(("[FindOriAddress] status = 0x%x\n", status));
ZwClose( hFile );
return 0;
}
KdPrint(("[FindOriAddress] address:0x%x\n",address));
//重定位
address = BaseAddress - 0x00400000 + address;
KdPrint(("[FindOriAddress] Oriaddress:0x%x\n",address));
//釋放動态配置設定的記憶體
RtlFreeUnicodeString(&modulename);
ZwClose( hFile );
return address;
}
/******************************************************************************************
*
* 得到SSDT Shadow目前位址
* 1、KeServiceDescriptorTable - 0x40 + 0x10
* 2、搜尋KeAddSystemServiceTable函數,特征碼
* 3、Kthread->ServiceTable指向
* 4、MJ提出的搜尋特定記憶體
*
*******************************************************************************************/
//方式1,XP下-0x40;
ULONG GetSSDTShadowAddress1()
{
ULONG address;
ULONG ssdt;
ssdt = (ULONG)KeServiceDescriptorTable;
address = ssdt - 0x30;
KdPrint(("[GetSSDTShadowAddress] ssdt:0x%x\n",ssdt));
KdPrint(("[GetSSDTShadowAddress] address:0x%x\n",address));
return address;
}
//方式2
ULONG GetSSDTShadowAddress2()
{
ULONG address;
PUCHAR addr;
PUCHAR p;
addr = (PUCHAR)KeAddSystemServiceTable;
for( p=addr; p<addr+PAGE_SIZE; p++)
{
if(*(PUSHORT)p == 0x888D)
{
address = *(PULONG)((ULONG)p+2);
break;
}
}
address = address + 0x10;
KdPrint(("[GetSSDTShadowAddress] address:0x%x\n",address));
return address;
}
//方式3
ULONG GetSSDTShadowAddress3()
{
return 0;
}
//方式4
ULONG GetSSDTShadowAddress4()
{
return 0;
}
/*********************************************************************************
*
* 獲得win32k.sys基址
* 1、ZwQuerySystemInformation
* 2、周遊DriverSection連結清單
*
**********************************************************************************/
ULONG GetWin32Base1()
{
NTSTATUS status;
ULONG i;
ULONG size;
ULONG address;
PSYSMODULELIST List;
ZwQuerySystemInformation( SystemModuleInformation ,&size,0,&size);
KdPrint(("[FindModuleByAddress] size:0x%x\n",size));
List=(PSYSMODULELIST)ExAllocatePool(NonPagedPool,size);
if (List==NULL)
{
KdPrint(("[FindModuleByAddress] malloc memory failed\n"));
ExFreePool( List );
return 0;
}
status=ZwQuerySystemInformation(SystemModuleInformation,List,size,0);
if (!NT_SUCCESS(status))
{
KdPrint(("[FindModuleByAddress] query failed\n"));
//列印錯誤
KdPrint(("[FindModuleByAddress] status: 0x%x\n",status));
ExFreePool( List );
return 0;
}
for ( i=0; i < List->ulCount; i++ )
{
if( strcmp(List->smi[i].ImageName,"\\SystemRoot\\System32\\win32k.sys") == 0)
{
KdPrint(("[GetWin32Base]name :%s\n",List->smi[i].ImageName));
address = (ULONG)List->smi[i].Base;
KdPrint(("[GetWin32Base1] win32k.sys address:0x%x\n",address));
}
}
return address;
}
/*********************************************************************************************
*
* 驅動對象DRIVER_OBJECT中的DRIVER_SECTION
* LDR_DATA_TABLE_ENTRY結構包含系統加載子產品連結清單及基址
*
*
**********************************************************************************************/
ULONG GetWin32Base2( PDRIVER_OBJECT driver)
{
PLIST_ENTRY pList = NULL;
PLDR_DATA_TABLE_ENTRY pLdr = NULL;
ULONG BaseAddress = 0;
pList = ( (PLIST_ENTRY)driver->DriverSection )->Flink;
do
{
pLdr = CONTAINING_RECORD(
pList,
LDR_DATA_TABLE_ENTRY,
InLoadOrderLinks
);
if( pLdr->EntryPoint != NULL && pLdr->FullDllName.Buffer!= NULL )
{
if( !_wcsicmp( pLdr->FullDllName.Buffer, L"\\SystemRoot\\System32\\win32k.sys"))
{
BaseAddress = (ULONG )pLdr->DllBase;
KdPrint(("[GetWin32Base2] win32k.sys address:0x%x\n",BaseAddress));
break ;
}
}
pList = pList->Flink;
}while( pList != ((PLIST_ENTRY)driver->DriverSection)->Flink );
return BaseAddress;
}
/****************************************************************************************
*
* 根據傳入的服務号得到Shadow 函數原始位址
*
****************************************************************************************/
ULONG FindShadowOriAddress( ULONG index )
{
//核心檔案win32k.sys基位址
//得到SSDT Shadow表的位址
//得到檔案偏移
NTSTATUS status;
ULONG size;
ULONG BaseAddress;
ULONG ShadowBase;
ULONG ShadowAddress;
ULONG SsdtRva;
ULONG FileOffset = 0;
UNICODE_STRING modulename;
OBJECT_ATTRIBUTES object_attributes;
IO_STATUS_BLOCK io_status = {0};
HANDLE hFile;
//讀取的位置
ULONG location;
LARGE_INTEGER offset;
ULONG address;
BaseAddress = GetWin32Base1();
KdPrint(("[FindShadowOriAddress] BaseAddress:0x%x\n",BaseAddress));
//經驗證位址正确
ShadowBase = GetSSDTShadowAddress2();
ShadowAddress = *(PULONG)ShadowBase;
KdPrint(("[FindShadowOriAddress] ShadowAddress:0x%x\n",ShadowAddress));
//得到SSDT表的Rva
SsdtRva = ShadowAddress - BaseAddress;
//驗證
KdPrint(("[FindOriAddress] SsdtRva:0x%x\n",SsdtRva));
//讀取的位置
location = SsdtRva + index * 4;
offset.QuadPart =location;
KdPrint(("[FindOriAddress] location:0x%x\n",location));
//利用ZwReadFile讀取檔案
//初始化OBJECT_ATTRIBUTES結構
RtlInitUnicodeString(&modulename, L"\\SystemRoot\\system32\\win32k.sys");
InitializeObjectAttributes(
&object_attributes,
&modulename,
OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE,
NULL,
NULL);
//打開檔案
status = ZwCreateFile(
&hFile,
FILE_EXECUTE | SYNCHRONIZE,
&object_attributes,
&io_status,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ,
FILE_OPEN,
FILE_NON_DIRECTORY_FILE |
FILE_RANDOM_ACCESS |
FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0);
if( !NT_SUCCESS( status ))
{
KdPrint(("[FindOriAddress] open error\n"));
KdPrint(("[FindOriAddress] status = 0x%x\n", status));
ZwClose( hFile );
return 0;
}
status = ZwReadFile(
hFile,
NULL,
NULL,
NULL,
NULL,
&address,
sizeof(ULONG),
&offset,
NULL);
if( !NT_SUCCESS( status ))
{
KdPrint(("[FindOriAddress] read error\n"));
KdPrint(("[FindOriAddress] status = 0x%x\n", status));
ZwClose( hFile );
return 0;
}
KdPrint(("[FindOriAddress] address:0x%x\n",address));
address = address;
KdPrint(("[FindOriAddress] Oriaddress:0x%x\n",address));
ZwClose( hFile );
return address;
}