SSDT table concepts and ssdt concepts
The full name of SSDT is System Services Descriptor Table, a System service Descriptor Table.
This table associates Ring3's Win32 API with Ring0's kernel API. All functions called in Ring3 will be first imported into ntdll. For example, ReadFile will enter the ZwReadFile of ntdll.
SSDT not only contains a large address index table, but also contains some useful information, such as the base address of the address index and the number of service functions.
1. // System Service Identifier table-export the KeServiceDescriptorTable table in ntoskrnl.exe
2. # pragma pack (1)
3. typedef struct _ ServiceDescriptorTable
4 .{
5. // base address of the System Service Dispatch Table
6. PVOID ServiceTableBase;
7. // the counter of the number of times each service is called in SSDT. This counter is generally updated by sysenter.
8. PVOID ServiceCounterTable;
9. // number of services described by ServiceTableBase.
10. unsigned int NumberOfServices;
11. // The base address of each system service parameter byte-System Service parameter table SSPT
12. PVOID ParamTableBase;
13.} * PServiceDescriptorTable;
14. # pragma pack ()
By modifying the function address of this table, you can Hook common Windows functions and APIs to filter and monitor system actions that you are concerned about. ZwOpenProcess and ZwLoadDriver. Some HIPS, anti-virus software, system monitoring, and registry monitoring software often use this interface to implement their own monitoring modules.
In Windows operating systems (Windows 4.0) over NT, two system service description tables exist by default. These two scheduling tables correspond to two different types of system services, the two scheduling tables are:
SSDT: KeServiceDescriptorTable
ShadowSSDT: KeServiceDescriptorTableShadow
KeServiceDescriptorTable is mainly used to process system calls from Kernel32.dll at the Ring3 layer.
For example, functions such as OpenProcess and ReadFile. From kernel32.dll ---> ntdll. dll ---> to kernel ntoskrnl.exe (some machines may not have this name)
KeServiceDescriptorTableShadow mainly handles system calls from User32.dll and GDI32.dll.
For example, common PostMessage, SendMessage, FindWindow, and Win32k. sys.
Many people may wonder why the system has a lot of kernel files ntoskrnl.exe named ntkrnlpa.exe. Simply put, they all compile four executable files in the same source code based on different compilation options:
Ntoskrnl-single processor, does not support PAE (physical address extension)
Ntkrnlpa-single processor, supporting PAE
Ntkrnlmp-multi-processor, does not support PAE
Ntkrpamp-multi-processor, supporting PAE
Before Vista, the installer selects two multi-processor or one single processor versions based on the system configuration during installation and copies them to the target system system32. From Vista onwards, the multi-processor version will be used in a unified manner, because the multi-processor version runs on a single processor only slightly less efficient.
The ssdttable has been exported and can be viewed through the export table of ntoskrnl.exe. Since KeServiceDescriptorTable is an exported global variable (array), let's look at wrk. We all know that when writing code, we need to export a function, usually using the def file. So when ntoskrnl was compiled, it also used def to export the file ntosx86.def. Let's look at wrk:
* ********* Ntosx86.def --> the KeServiceDescriptorTable CONSTANT ************* is exported ***********
With the above introduction, we can simply regard the KeServiceDescriptor as an array (actually an array), at the application layer ntdll. the API in dll exists in this system service description table (SSDT.
Ntdll ZwReadFile 111 h
Ntos mov eax, 111 h
When our application calls ntdll. in dll, the corresponding system services in the kernel are eventually called, therefore, we only need to tell the kernel that the index of the SSDT where the service to be called is OK, and then the kernel can find the corresponding service in SSDT according to the index value, then, the kernel calls the service to complete the application API call request.
In ntdll, although NtQuerySystemInformation and ZwQuerySystemInformation start with nt and zw functions, they are actually the same. Let's look at IDA. Let's first look at the address of functions in the Nt * series:
. Text: 77F061F8 _ NtQuerySystemInformation @ 16
In ntdll, the two functions of zw and nt are actually the same subject:
. Text: 77F061F8 mov eax, 105 h; NtQuerySystemInformation
. Text: 77F061F8; RtlGetNativeSystemInformation
. Text: 77F061FD mov edx, 7FFE0300h
. Text: 77F06202 call dword ptr [edx]
. Text: 77F06204 retn 10 h
Then compare the image:
Check whether the Mode is usermode or kernelmode.
Ntdll. the APIs in dll are just a simple packaging function. When the APIs in Kernel32.dll use Ntdll. dll (for example, ReadFile ---> ZwReadFile), the parameter check is completed, and an interrupt (int 2Eh or SysEnter command) is called, so as to enter the Ring0 layer from Ring3, in addition, store the service number to be called (that is, the index value in the SSDT array) in the eax register mov eax, 105 h (for example, check IDA, and then compare whether xuetr is consistent: and put the parameter address in the specified register EDX (mov edx, 7FFE0300h). Then copy the parameter to the kernel address space, then, the specified service is called in the SSDT Array Based on the index value stored in EAX.
Let's look at this function in the kernel:
Windbg command 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 10 h
804ffb30 b8ae000000 mov eax, 0AEh
804ffb35 8d542404 lea edx, [esp + 4]
We can see that ZwQuerySystemInformation under Ring0 puts 105 h into the register eax,
Lkd> u ZwQuerySystemInformation
Nt! ZwQuerySystemInformation:
84456c38 b805010000 mov eax, 105 h // put 105 h into Register eax
84456c3d 8d542404 lea edx, [esp + 4]
84456c41 9c pushfd
84456c42 6a08 push 8
84456c44 e835140000 call nt! KiSystemService (8445807e)
84456c49 c21000 ret 10 h
Then the system service distribution function KiSystemService is called, and the KiSystemService function is based on the index value in the eax register, then, go to the SSDT array and find the SSDT entry whose index value is the value stored in the eax register, finally, the system function is called Based on the address of the System Service stored in this SSDT item. For example, the system service corresponding to the address stored in the KeServiceDescriptorTable [105 h] is called, that is, NtQuerySystemInformation under Ring0.
Differences between Zw and Nt functions in the kernel
Lkd> u ZwQuerySystemInformation
Nt! ZwQuerySystemInformation:
84456c38 b805010000 mov eax, 105 h // put 105 h into Register eax
84456c3d 8d542404 lea edx, [esp + 4]
84456c41 9c pushfd
84456c42 6a08 push 8
84456c44 e835140000 call nt! KiSystemService (8445807e)
84456c49 c21000 ret 10 h
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, 53 h
8464ae49 7f21 jg nt! NtQuerySystemInformation + 0x2e (8464ae6c)
8464ae4b 7440 je nt! NtQuerySystemInformation + 0x4f (8464ae8d)
The main body is the nt series functions.
Therefore, the conclusion is that the Zw series functions are just similar to a transition, and the Nt series functions are the real execution subjects.
So far, the entire process of calling NtQuerySystemInformation at the application layer has ended ~
Ring3! ZwQuerySystemInformation or NtQuerySystemInformation
Enter the kernel
Ntos 105 h ntos! ZwQuerySystemInformation
Then, through the ssdt index, find
Ntos! NtQuerySystemInformation execution subject.
Speaking of so many theoretical knowledge, we can see the structure of SSDT tables in windbg:
Lkd> dd KeServiceDescriptorTable
84583b00 84498d5c 00000000 00000191 844993a4
84498d5c is the starting address of the SSDT table.
00000191 is the number of SSDT tables. unsigned int NumberOfServices // This member is the number.
Lkd> dd 84498d5c
84498d5c 84693e78 844db3ad 84623608443f8ba
84498d6c 8469574f 84518306 84705f53 84705f9c
84498d7c 846184af 8471f7c2 84720a17 8460ec87
84498d8c 8469fd8d 846f8ca9 8464bbc0 8461b7c4
84498d9c 845b19ae 846eab84 84602240 84644bcc
84498dac 84691041 845f22bc 8469044e 8460 fcfe
84498dbc 846a1814 84612381 846a15f4 84699d4c
84498dcc 846241e8 846e5927 84697119 846a1a46
These are the subjects of nt functions:
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: [00000124 h]
84693e83 66ff8884000000 dec word ptr [eax + 84 h]
84693e8a 56 push esi
84693e8b 57 push edi
84693e8c 6a01 push 1
The so-calledSubjectThat is, the real Assembly Execution Code rather than the direct transition code.
++ ++
First address 84498d5c ring0 service No. 105 h
[Address] = SSDT first Address + 4 * Index Number
Ntos! NtQuerySystemInformation = 84498d5c + 4*105 h = [84499170 h]
[84499170 h] = 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, 53 h
8464ae49 7f21 jg nt! NtQuerySystemInformation + 0x2e (8464ae6c)
8464ae4b 7440 je nt! NtQuerySystemInformation + 0x4f (8464ae8d)
8464ae4d 83fa08 cmp edx, 8
++ ++
Sample Code for Traversing SSDT table: https://blog.csdn.net/qq1084283172/article/details/41077983
The following provides a simple SSDT table traversal code:
# Include
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; // The ssdt table has been exported. IN this example, the extern escape handler function VOID DriverUnload (IN PDRIVER_OBJECT DriverObject) is uninstalled) {dbuplint ("Uninstall completed! \ N ") ;}// entry function NTSTATUS DriverEntry (IN PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) {int I = 0; DriverObject-> DriverUnload = DriverUnload; for (I = 0; I
TableSize; I ++) {dbuplint ("Number: % d Address: 0x % 08X \ r \ n", I, keServiceDescriptorTable-> ServiceTable [I]);} return STATUS_SUCCESS ;}