SSDT表概念詳解
SSDT 的全稱是 System Services Descriptor Table,系統服務描述符表。
這個表就是一個把 Ring3 的 Win32 API 和 Ring0 的核心 API 聯絡起來。Ring3下呼叫的所有函式最終都會先進入到ntdll裡面的,比如ReadFile,就會進入ntdll的ZwReadFile
SSDT 並不僅僅只包含一個龐大的地址索引表,它還包含著一些其它有用的資訊,諸如地址索引的基地址、服務函式個數等。
1. //系統服務描述符表-在ntoskrnl.exe中匯出KeServiceDescriptorTable這個表
2. #pragma pack(1)
3. typedef struct _ServiceDescriptorTable
4. {
5. //System Service Dispatch Table的基地址
6. PVOID ServiceTableBase;
7. //SSDT中每個服務被呼叫次數的計數器。這個計數器一般由sysenter 更新。
8. PVOID ServiceCounterTable;
9. //由 ServiceTableBase 描述的服務數目。
10. unsigned int NumberOfServices;
11. //每個系統服務引數位元組數表的基地址-系統服務參數列SSPT
12. PVOID ParamTableBase;
13. }*PServiceDescriptorTable;
14. #pragma pack()
通過修改此表的函式地址可以對常用 Windows 函式及 API 進行 Hook,從而實現對一些關心的系統動作進行過濾、監控的目的。ZwOpenProcess、ZwLoadDriver。一些 HIPS、防毒軟體、系統監控、登錄檔監控軟體往往會採用此介面來實現自己的監控模組。
在 NT 4.0 以上的 Windows 作業系統中(windows2000),預設就存在兩個系統服務描述表,這兩個排程表對應了兩類不同的系統服務,這兩個排程表為:
SSDT:KeServiceDescriptorTable
ShadowSSDT:KeServiceDescriptorTableShadow
KeServiceDescriptorTable 主要是處理來自 Ring3 層的 Kernel32.dll 中的系統呼叫
比如函式 OpenProcess、ReadFile 等函式。從kernel32.dll--->ntdll.dll--->進入核心 ntoskrnl.exe(有些機器可能不是這個名字)
KeServiceDescriptorTableShadow 則主要處理來自 User32.dll 和 GDI32.dll 中的系統呼叫
比如常見的PostMessage、SendMessage、FindWindow,Win32k.sys等。
很多人一定很奇怪,為什麼系統中有很多核心檔案 ntoskrnl.exe 、ntkrnlpa.exe,簡單來說就是他們都是同一套原始碼根據編譯選項的不同而編譯出四個可執行檔案,分別用於:
ntoskrnl - 單處理器,不支援PAE(實體地址擴充套件)
ntkrnlpa - 單處理器,支援PAE
ntkrnlmp - 多處理器,不支援PAE
ntkrpamp - 多處理器,支援PAE
在Vista之前,安裝程式會在安裝時根據系統的配置選擇兩個多處理器或者兩個單處理器的版本複製到目標系統 system32中。從Vista開始以後,會統一使用多處理器版本,因為多處理器版本執行在單處理器上只是效率稍微低一些。
SSDT表已經匯出了,通過ntoskrnl.exe的匯出表可以檢視到。既然KeServiceDescriptorTable是一個匯出的全域性變數(陣列),那麼我們來看wrk,大家都知道在編寫程式碼的時候,要匯出一個函式,通常使用def檔案。所以ntoskrnl在編寫的時候,同樣也用到了def來匯出匯出檔案是ntosx86.def,我們翻看wrk:
*********** ntosx86.def-->匯出了 KeServiceDescriptorTable CONSTANT ***********
有了上面的介紹後,我們可以簡單的將 KeServiceDescriptor 看做是一個陣列了(其實質也就是個陣列),在應用層 ntdll.dll 中的 API 在這個系統服務描述表(SSDT)中都存在一個與之相對應的服務.
Ntdll ZwReadFile 111h
Ntos mov eax, 111h
當我們的應用程式呼叫 ntdll.dll 中的 API 時,最終會呼叫核心中與之相對應的系統服務,由於有了 SSDT,所以我們只需要告訴核心需要呼叫的服務所在 SSDT 中的索引就 OK 了,然後核心根據這個索引值就可以在 SSDT 中找到相對應的服務了,然後再由核心呼叫服務完成應用程式 API 的呼叫請求即可。
在ntdll下NtQuerySystemInformation和ZwQuerySystemInformation 的開頭雖然是nt、zw兩套函式,其實是一樣的。我們看IDA,我們先看Nt*系列的函式地址:
.text:77F061F8 _NtQuerySystemInformation@16
在ntdll中,zw和nt的兩套函式其實他們都是同一個主體:
.text:77F061F8 mov eax, 105h ; NtQuerySystemInformation
.text:77F061F8 ; RtlGetNativeSystemInformation
.text:77F061FD mov edx, 7FFE0300h
.text:77F06202 call dword ptr [edx]
.text:77F06204 retn 10h
然後再對比圖片:
Mode檢查 是 usermode 還是kernelmode。
眾所周知 Ntdll.dll 中的 API 都只不過是一個簡單的包裝函式而已,當 Kernel32.dll 中的 API 通過 Ntdll.dll 時(比如:ReadFile --->ZwReadFile),會完成引數的檢查,再呼叫一箇中斷(int 2Eh 或者 SysEnter 指令),從而實現從 Ring3 進入 Ring0 層,並且將所要呼叫的服務號(也就是在 SSDT 陣列中的索引值)存放到暫存器 EAX 中 mov eax, 105h(比如看IDA,然後對比xuetr是否一致:結果吻合),並且將引數地址放到指定的暫存器 EDX 中( mov edx, 7FFE0300h),再將引數複製到核心地址空間中,再根據存放在 EAX 中的索引值來在 SSDT 陣列中呼叫指定的服務。
我們來看核心下這個函式:
windbg的命令 u nt!ZwQuerySystemInformation
nt!ZwQuerySystemInformation:
804ffb1c b8ad000000 mov eax,0ADh
804ffb21 8d542404 lea edx,[esp+4]
804ffb25 9c pushfd
804ffb26 6a08 push 8
804ffb28 e854e90300 call nt!KeReleaseInStackQueuedSpinLockFromDpcLevel+0x95d (8053e481)
804ffb2d c21000 ret 10h
804ffb30 b8ae000000 mov eax,0AEh
804ffb35 8d542404 lea edx,[esp+4]
可以看到在 Ring0 下的 ZwQuerySystemInformation 將 105h 放入了暫存器 eax 中,
lkd> u ZwQuerySystemInformation
nt!ZwQuerySystemInformation:
84456c38 b805010000 mov eax,105h //將 105h 放入了暫存器eax中
84456c3d 8d542404 lea edx,[esp+4]
84456c41 9c pushfd
84456c42 6a08 push 8
84456c44 e835140000 call nt!KiSystemService (8445807e)
84456c49 c21000 ret 10h
然後呼叫了系統服務分發函式 KiSystemService,而這個 KiSystemService 函式則是根據 eax 暫存器中的索引值,然後再到SSDT 陣列中找到索引值為eax 暫存器中存放的值的那個 SSDT 項,最後就是根據這個 SSDT 項中所存放的系統服務的地址來呼叫這個系統函式了。比如在這裡就是呼叫 KeServiceDescriptorTable[105h] 處所儲存的地址所對應的系統服務了也就是呼叫 Ring0 下的 NtQuerySystemInformation了。
說明一下核心中 Zw和Nt兩套函式的區別
lkd> u ZwQuerySystemInformation
nt!ZwQuerySystemInformation:
84456c38 b805010000 mov eax,105h //將 105h 放入了暫存器 eax 中
84456c3d 8d542404 lea edx,[esp+4]
84456c41 9c pushfd
84456c42 6a08 push 8
84456c44 e835140000 call nt!KiSystemService (8445807e)
84456c49 c21000 ret 10h
lkd> u NtQuerySystemInformation l 10
nt!NtQuerySystemInformation:
8464ae3e 8bff mov edi,edi
8464ae40 55 push ebp
8464ae41 8bec mov ebp,esp
8464ae43 8b5508 mov edx,dword ptr [ebp+8]
8464ae46 83fa53 cmp edx,53h
8464ae49 7f21 jg nt!NtQuerySystemInformation+0x2e (8464ae6c)
8464ae4b 7440 je nt!NtQuerySystemInformation+0x4f (8464ae8d)
主體,就是nt系列函式。
所以結論就是:Zw系列函式只是類似一個過渡而Nt系列函式才是真正的執行主體。
至此,在應用層中呼叫 NtQuerySystemInformation 的全部流程也就結束了 ~
Ring3!ZwQuerySystemInformation 或者 NtQuerySystemInformation
進入核心
Ntos 105h ntos!ZwQuerySystemInformation
接著通過ssdt索引,找到
ntos!NtQuerySystemInformation 執行主體。
說了那麼多理論知識,我們windbg來看下SSDT表的結構:
lkd> dd KeServiceDescriptorTable
84583b00 84498d5c 00000000 00000191 844993a4
84498d5c 就是SSDT表的起始地址。
00000191 就是SSDT表的個數 unsigned int NumberOfServices //這個成員就是個數
lkd> dd 84498d5c
84498d5c 84693e78 844db3ad 84623c60 8443f8ba
84498d6c 8469574f 84518306 84705f53 84705f9c
84498d7c 846184af 8471f7c2 84720a17 8460ec87
84498d8c 8469fd8d 846f8ca9 8464bbc0 8461b7c4
84498d9c 845b19ae 846eab84 84602240 84644bcc
84498dac 84691041 845f22bc 8469044e 8460fcfe
84498dbc 846a1814 84612381 846a15f4 84699d4c
84498dcc 846241e8 846e5927 84697119 846a1a46
這些是nt函式的主體:
lkd> u 84693e78
nt!NtAcceptConnectPort:
84693e78 8bff mov edi,edi
84693e7a 55 push ebp
84693e7b 8bec mov ebp,esp
84693e7d 64a124010000 mov eax,dword ptr fs:[00000124h]
84693e83 66ff8884000000 dec word ptr [eax+84h]
84693e8a 56 push esi
84693e8b 57 push edi
84693e8c 6a01 push 1
所謂主體,就是真正的彙編執行程式碼而不是直接的過渡程式碼。
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
首地址 84498d5c ring0 服務號 105h
[Address] = SSDT首地址 + 4 * 索引號
ntos!NtQuerySystemInformation = 84498d5c + 4 * 105h = [84499170h]
[84499170h] = 8464ae3eh
lkd> u 8464ae3e
nt!NtQuerySystemInformation:
8464ae3e 8bff mov edi,edi
8464ae40 55 push ebp
8464ae41 8bec mov ebp,esp
8464ae43 8b5508 mov edx,dword ptr [ebp+8]
8464ae46 83fa53 cmp edx,53h
8464ae49 7f21 jg nt!NtQuerySystemInformation+0x2e (8464ae6c)
8464ae4b 7440 je nt!NtQuerySystemInformation+0x4f (8464ae8d)
8464ae4d 83fa08 cmp edx,8
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
SSDT表的遍歷詳細程式碼示例:http://blog.csdn.net/qq1084283172/article/details/41077983
下面也提供一份簡單的SSDT表的遍歷程式碼:
#include <ntifs.h>
typedef struct _SERVICE_DESCRIPTOR_TABLE {
/*
* Table containing cServices elements of pointers to service handler
* functions, indexed by service ID.
*/
PULONG ServiceTable;
/*
* Table that counts how many times each service is used. This table
* is only updated in checked builds.
*/
PULONG CounterTable;
/*
* Number of services contained in this table.
*/
ULONG TableSize;
/*
* Table containing the number of bytes of parameters the handler
* function takes.
*/
PUCHAR ArgumentTable;
} SERVICE_DESCRIPTOR_TABLE, *PSERVICE_DESCRIPTOR_TABLE;
//ssdt表已經匯出了,這裡例行公事下
extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;
//解除安裝函式
VOID DriverUnload(IN PDRIVER_OBJECT DriverObject)
{
DbgPrint("解除安裝完成!\n");
}
//入口函式
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,PUNICODE_STRING RegistryPath)
{
int i = 0;
DriverObject->DriverUnload = DriverUnload;
for (i=0;i<KeServiceDescriptorTable->TableSize;i++)
{
DbgPrint("Number:%d Address:0x%08X\r\n\r\n",i, KeServiceDescriptorTable->ServiceTable[i]);
}
return STATUS_SUCCESS;
}
本文文件和程式碼的下載地址:http://download.csdn.net/detail/qq1084283172/8837431
註釋:
學習資料整理於AGP講課資料,感覺還不錯。
圖片來源於網上。
相關文章
- RabbitMQ概念詳解MQ
- Git物件概念詳解Git物件
- Docker基本概念詳解Docker
- js 變數概念詳解JS變數
- 【DG】DG概念原理詳解
- 詳細講解Oracle表分割槽相關概念及優點Oracle
- 1-Hyperledger Fabric概念詳解
- Laravel Service Provider 概念詳解LaravelIDE
- locale 詳解 字符集概念
- ZooKeeper 系列(一)—— ZooKeeper核心概念詳解
- Laravel Container (容器) 概念詳解 (上)LaravelAI
- 系統呼叫篇——SSDT
- SSDT結構簡記
- iOS多執行緒詳解:概念篇iOS執行緒
- 鐳速——FTP伺服器概念詳解FTP伺服器
- Laravel Dependency Injection (依賴注入) 概念詳解Laravel依賴注入
- c++中的抽象概念詳解C++抽象
- Oracle dual表 詳解Oracle
- ORACLE DUAL表詳解Oracle
- 雜湊表(雜湊表)詳解
- C++指標的概念解讀 超詳細C++指標
- 概念POJO、DTO、DAO、PO、BO、VO、ENTITY詳解POJO
- 【Oracle-資料庫概念】-Oracle checkpoint詳解Oracle資料庫
- .NET Framework非託管相關概念詳解Framework
- Postgresql表空間詳解SQL
- oracle 分割槽表詳解Oracle
- oracle表分割槽詳解Oracle
- oracle分割槽表詳解Oracle
- Oracle 表分割槽詳解Oracle
- 雜湊表(雜湊表)原理詳解
- .NET程式執行原理及基本概念詳解
- Python中集合的概念及基本操作詳解!Python
- spring之AOP基本概念和配置詳解Spring
- Laravel 控制反轉和門面模式概念詳解Laravel模式
- 表連線概念
- oracle外部表詳解以及使用Oracle
- SQL Server表分割槽詳解SQLServer
- Oracle表空間操作詳解Oracle