關於Int 2E

下面是反彙編ntdll.dll的NtCreateEvent部分 NtCreateEvent調用了int 2E

Exported fn(): NtCreateEvent - Ord:005Ah
Exported fn(): ZwCreateEvent 
- Ord:02E2h
:77F83219 B81E000000         mov eax, 0000001E
:77F8321E 8D542404           lea edx, dword ptr [esp
+04]
:77F83222 CD2E             
int 2E
:77F83224 C21400             ret 
0014


int 2e的使用方法:

mov eax, service_id
lea edx, service_param
int 2e



Windows 2000 int 2e 功能表
共248個
EAX = function number
EDX = address of parameter block
0x0   AcceptConnectPort
0x1   AccessCheck
0x2   AccessCheckAndAuditAlarm
0x3   AccessCheckByType
0x4   AccessCheckByTypeAndAuditAlarm
0x5   AccessCheckByTypeResultList
0x6   AccessCheckByTypeResultListAndAuditAlarm
0x7   AccessCheckByTypeResultListAndAuditAlarmByHandle
0x8   AddAtom
0x9   AdjustGroupsToken
0xa   AdjustPrivilegesToken
0xb   AlertResumeThread
0xc   AlertThread
0xd   AllocateLocallyUniqueId
0xe   AllocateUserPhysicalPages
0xf   AllocateUuids
0x10 AllocateVirtualMemory
0x11 AreMappedFilesTheSame
0x12 AssignProcessToJobObject
0x13 CallbackReturn
0x14 CancelIoFile
0x15 CancelTimer
0x16 CancelDeviceWakeupRequest
0x17 ClearEvent
0x18 Close
0x19 CloseObjectAuditAlarm
0x1a CompleteConnectPort
0x1b ConnectPort
0x1c Continue
0x1d CreateDirectoryObject
0x1e CreateEvent                 請注意這裏




Ntdll.dll通過軟件中斷int 2Eh進入ntoskrnl.exe,就是通過中斷門切換CPU特權級。比如kernel32.dll導出的函數DeviceIoControl()實際上調用ntdll.dll中導出的NtDeviceIoControlFile(),反彙編一下這個函數可以看到,EAX載入magic數0x38,實際上是系統調用號,然後EDX指向堆棧。目標地址是當前堆棧指針ESP+4,所以EDX指向返回地址後面一個,也就是指向在進入 NtDeviceIoControlFile()之前存入堆棧的東西。事實上就是函數的參數。下一個指令是int 2Eh,轉到中斷描述符表IDT位置0x2E處的中斷處理程序。

反編匯這個函數得到:

mov eax, 38h

lea edx, [esp
+4]

int 2Eh

ret 28h



當然int 2E接口不僅僅是簡單的API調用調度員,他是從用戶模式進入內核模式的main gate。

W2k Native API由248個這麼處理的函數組成,比NT 4.0多了37個。可以從ntdll.dll的導出列表中很容易認出來:前綴Nt。Ntdll.dll中導出了249個,原因在於 NtCurrentTeb()爲一個純用戶模式函數,所以不需要傳給內核。令人驚奇的是,僅僅Native API的一個子集能夠從內核模式調用。而另一方面,ntoskrnl.exe導出了兩個Nt*符號,它們不存在於ntdll.dll中: NtBuildNumber, NtGlobalFlag。它們不指向函數,事實上,是指向ntoskrnl.exe的變量,可以被使用C編譯器extern關鍵字的驅動模塊導入。 Ntdll.dll和ntoskrnl.exe中都有兩種前綴Nt*,Zw*。事實上ntdll.dll中反彙編結果兩者是一樣的。而在 ntoskrnl.exe中,nt前綴指向真正的代碼,而zw還是一個int 2Eh的stub。也就是說zw*函數集通過用戶模式到內核模式門傳遞的,而Nt*符號直接指向模式切換以後的代碼。Ntdll.dll中的 NtCurrentTeb()沒有相對應的zw函數。Ntoskrnl並不導出配對的Nt/zw函數。有些函數只以一種方式出現。

2Eh中斷處理程序把EAX裏的值作爲查找表中的索引,去找到最終的目標函數。這個表就是系統服務表SST,C的結構 SYSTEM_SERVICE_TABLE的定義如下:清單也包含了結構SERVICE_DESCRIPTOR_TABLE中的定義,爲SST數組第四個成員,前兩個有着特別的用途。

typedef NTSTATUS (NTAPI *NTPROC) ( ) ;

typedef NTPROC 
*PNTPROC;

#define NTPROC_ sizeof (NTPROC)

typedef 
struct _SYSTEM_SERVICE_TABLE

{ PNTPROC ServiceTable; // 這裏是入口指針數組

PDWORD CounterTable; 
// 此處是調用次數計數數組

DWORD ServiceLimit ; 
// 服務入口的個數

PBYTE ArgumentTable; 
// 服務參數字節數的數組

) SYSTEM_SERVICE_TABLE ,

* PSYSTEM_SERVICE_TABLE ,

* * PPSYSTEM_SERVICE_TABLE ;

/ / _ _ _ _ _ _ _ _ _ _ _ _

typedef 
struct _SERVICE_DESCRIPTOR_TABLE

{ SYSTEM_SERVICE_TABLE ntoskrnl ; // ntoskrnl所實現的系統服務,本機的API}

SYSTEM_SERVICE_TABLE win32k; 
// win32k所實現的系統服務

SYSTEM_SERVICE_TABLE Table3; 
// 未使用

SYSTEM_SERVICE_TABLE Table4; 
// 未使用

}
 SERVICE_DESCRIPTOR_TABLE ,

* PSERVICE_DESCRIPTOR_TABLE,

* PPSERVICE_DESCRIPTOR_TABLE ;
ntoskrnl通過KeServiceDescriptorTable符號,導出了主要SDT的一個指針。內核維護另外的一個SDT,就是 KeServiceDescriptorTableShadow。但這個符號沒有導出。要想在內核模式組件中存取主要SDT很簡單,只需兩行C語言的代碼:

 

 

extern PSERVICE_DESCRIPTOR_TABLE KeServiceDescriptorTable;

PSERVICE_DESCRIPTOR_TABLE psdt
= KeServiceDescriptorTable;




NTPROC爲本機 API的方便的佔位符,他類似於Win32編程中的PROC。Native API正常的返回應該是一個NTSTATUS代碼,他使用NTAPI調用約定,它和_stdcall一樣。ServiceLimit成員有在 ServiceTable數組裏找到的入口數目。在2000下,默認值是248。ArgumentTable爲BYTEs的數組,每一個對應於 ServiceTable的位置並顯示了在調用者堆棧裏的參數比特數。這個信息與EDX結合,這是內核從調用者堆棧copy參數到自己的堆棧所需的。 CounterTable成員在free buid的2000中並沒有使用到,在debug build中,這個成員指向代表所有函數使用計數的DWORDS數組,這個信息能用於性能分析。
  可以使用這個命令來顯示:dd KeServiceDescriptorTable,調試器把此符號解析爲0x8046e0c0。只有前四行是最重要的,對應那四個SDT成員。
  運行這個命令:ln 8046e100,顯示符號是KeServiceDescriptorTableShadow,說明第五個開始確實爲內核維護的第二個SDT。主要的區別在於後一個包含了win32k.sys的入口,前一個卻沒有。在這兩個表中,Table3與Table4都是空的。Ntoskrnl.exe提供了一個方便的API函數。這個函數的名字爲:

  KeAddSystemServiceTable
此函數去填充這些位置。

2Eh的中斷處理標記是KisystemService()。這也是ntoskrnl.exe沒有導出的內部的符號,但包含在2k符號文件中。關於KisystemService的操作如下:

1 從當前的線程控制塊檢索SDT指針

2 決定使用SDT中4個SST的其中一個。通過測試EAX中遞送ID的第12和13位來決定。ID在0x0000-0x0fff的映射至ntoskrnl表格,ID在

0x1000與0x1ffff的分配給win32k表格。剩下的0x2000-0x2ffff與

0x3000-0x3ffff則是Table3和Table4保留。

3 通過選定SST中的ServiceLimit成員檢查EAX的0-11位。如果ID超過了範圍,返回錯誤代碼爲STATUS_INVALID_SYSTEM_SERVICE。

4 檢查EAX中的參數堆棧指針與MmUserProbeAddress。這是一個ntoskrnl導出的全局變量。通常等於0x7FFF0000,如果參數指針不在這個地址之下,返回STATUS_ACCESS_VIOLATION。

5 查找ArgumentTable中的參數堆棧的字節數,從調用者的堆棧copy所有的參數至當前內核模式堆棧。

6 搜索serviceTable中的服務函數指針,並調用這個函數。

7 控制轉到內部的函數KiserviceExit,在此次服務調用返回之後。

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章