In 32-bit windows, we have many methods to locate ssdt. The most direct one is to use the export symbol to find ssdt. Then there is through nt! Search by disassembly in the keaddsystemservicetable function. However, in 64-bit windows, neither of these methods works. Ssdt is not exported in 64-bit windows. In this case, the first problem occurs when you hook the ssdt table. How can we find it? I thought about three ideas.
Thought 1:
Determine a hard encoding offset from ntoskrnl to ssdt for each version of the system. Haha, This method may be silly, but the simplest and most effective. It is very troublesome to maintain, and each version should be processed separately. If different patch versions change, it should also be handled.
Idea 2:
In 64-bit windows, the entry offset of each service routine is stored (this offset is relative to the start address of ssdt ). I take an entry offset of a service routine that is generally not hooked, and then search for the four bytes from the start address of ntoskrnl until the end, then, locate the start position of ssdt Based on the index of the service routine. The offsets and indexes of different versions of the operating system may need to be processed separately, but they are more common than the first approach.
Thought 3:
Or the disassembly method. Later I thought I could not find the ssdt address for the image.
Kd> DQ nt! Keservicedescriptortable
Fffff800 '03eab840 fffff800 '03c75b00 100' 00000000
Fffff800 '03eab850 00000000 '00000191 fffff800' 03c7678c
Fffff800 '03eab860 00000000 '00000000 00000000' 00000000
Fffff800 '03eab870 00000000 '00000000 00000000' 00000000
Fffff800 '03eab880 fffff800 '03c75b00 100' 00000000
Fffff800 '03eab890 00000000 '00000191 fffff800' 03c7678c
Fffff800 '03eab8a0 fffff960 '00111c00 100' 00000000
Fffff800 '03eab8b0 00000000 '0000033b fffff960' 0011391c
Kd> lm M NT
Start end module name
Fffff800 '03c03000 fffff800' 041e0000 NT (PDB symbols)
Kd> S-Q fffff800 '03c03000 l600000 fffff800 '03eab840
Unfortunately, I did not find any related information. Haha, but I am not dead. I believe there will be a reference somewhere. So I searched for all the symbols whose names contain service words.
Kd> X nt! Ki * Service *
Fffff800 '03c73e40 nt! Kiserviceinternal = <no type information>
Fffff800 '03c7415b nt! Kisystemserviceexit = <no type information>
Fffff800 '03c73fde nt! Kisystemservicestart = <no type information>
Fffff800 '03c76788 nt! Kiservicelimit = <no type information>
Fffff800 '03c73b00 nt! Kidebugservicetrap = <no type information>
Fffff800 '03c74140 nt! Kisystemservicecopyend = <no type information>
Fffff800 '03c75b00 nt! Kiservicetable = <no type information>
Fffff800 '03c74037 nt! Kisystemservicegditebaccess = <no type information>
Fffff800 '03c73ff2 nt! Kisystemservicerepeat = <no type information>
Fffff800 '03c740d0 nt! Kisystemservicecopystart = <no type information>
Fffff800 '03c706f0 nt! Kiservicelinkage = <no type information>
Fffff800 '03c73d40 nt! Kisystemservicehandler = <no type information>
Finally at nt! The ssdt information is found in kisystemservicerepeat.
NT! Kisystemservicerepeat:
Fffff800 '03c73ff2 4c8d1547782300 Lea R10, [nt! Keservicedescriptortable (fffff800 '03eab840)]
Fffff800 '03c73ff9 4c8d1d80782300 Lea R11, [nt! Keservicedescriptortableshadow (fffff800 '03eab880)]
Fffff800 '03c74000 f783000000080000000 test dword ptr [RBx + 100 H], 80 h
Fffff800 '03c7400a 4d0f45d3 cmovne R10, R11
Fffff800 '03c7400e 423b441710 CMP eax, dword ptr [RDI + R10 + 10 h]
Fffff800 '03c74013 0f83e9020000 Jae nt! Kisystemserviceexit + 0x1a7 (fffff800 '03c74302)
Fffff800 '03c74019 4e8b1417 mov R10, qword PTR [RDI + R10]
Fffff800 '03c7401d 4d631c82 movsxd R11, dword ptr [R10 + Rax * 4]
But a new problem also arises at the same time, NT! Kisystemservicerepeat is not an export function of the system. How can I find it? Later I thought that although it was not exported, there should be at least one call path from the external to it! Then I can find it through this path. If you think of this, you have a breakpoint to check who will call it. The results are the same as I thought, but I found myself a cainiao. I was so happy to find the ssdt value that I did not carefully look at its assembly code. NT! Kisystemservicerepeat is used to obtain and call the service routine entry from ssdt according to the call number. That is to say, every zwxxx class function will call it, and the ZW class function is exported by the system so that we can find NT through any ZW class function! Kisystemservicerepeat to locate ssdt. So I tried it manually. The following is the attempt process.
Kd> u nt! Zwclose L20
NT! Zwclose:
Fffff800 '03c6d640 488bc4 mov rax, RSP
Fffff800 '03c6d643 fa CLI
Fffff800 '03c6d644 4883ec10 sub RSP, 10 h
Fffff800 '03c6d648 50 push Rax
Fffff800 '03c6d649 9C pushfq
Fffff800 '03c6d64a 6a10 push 10 h
Fffff800 '03c6d64c 488d059d300000 Lea rax, [nt! Kiservicelinkage (fffff800 '03c706f0)]
Fffff800 '03c6d653 50 push Rax
Fffff800 '03c6d654 b80c000000 mov eax, 0ch
Fffff800 '03c6d659 e9e2670000 JMP nt! Kiserviceinternal (fffff800 '03c73e40)
Fffff800 '03c6d65e 6690 xchg ax, ax
In zwclose (other similar), it jumps to NT! Kiserviceinternal. Let's track nt again! Kiserviceinternal
NT! Kiserviceinternal:
Fffff800 '03c73e40 4883ec08 sub RSP, 8
Fffff800 '03c73e44 55 push RBP
Fffff800 '03c73e45 4881ec58010000 sub RSP, 158 h
Fffff800 '03c73e4c 488dac2480000000 Lea RBP, [RSP + 80 h]
Fffff800 '03c73e54 48899dc0000000 mov qword PTR [RBP + 0c0h], RBx
Fffff800 '03c73e5b 4889bdc8000000 mov qword PTR [RBP + 0c8h], RDI
Fffff800 '03c73e62 4889b5d0000000 mov qword PTR [RBP + 0d0h], RSI
Fffff800 '03c73e69 FB STI
Fffff800 '03c73e6a 65488b1c2588010000 mov RBx, qword ptr gs: [188 H]
Fffff800 '03c73e73 0f0d8bd8010000 prefetchw [RBx + 1d8h]
Fffff800 '03c73e7a 0fb6bbf6010000 movzx EDI, byte PTR [RBx + 1f6h]
Fffff800 '03c73e81 40887da8 mov byte PTR [rbp-58h], Dil
Fffff800 '03c73e85 c683f60000000 mov byte PTR [RBx + 1f6h], 0
Fffff800 '03c73e8c 4c8b93d8010000 mov R10, qword PTR [RBx + 1d8h]
Fffff800 '03c73e93 4c8995b8000000 mov qword PTR [RBP + 0b8h], R10
Fffff800 '03c73e9a 4c8d1d3d010000 Lea R11, [nt! Kisystemservicestart (fffff800 '03c73fde)]
Fffff800 '03c73ea1 41ffe3 JMP R11
Fffff800 '03c73ea4 6666666666660f1f840000000000 NOP word PTR [Rax + Rax]
Fffff800 '03c73eb3 66666666660f1f840000000000 NOP word PTR [Rax + Rax]
This function jumps to NT again! Kisystemservicestart, here the 4c8d1d3d010000 Command needs to refer to the knowledge about Rex. W prefix and mod R/M addressing in x64 assembly to explain this offset. Here fffff800 '03c73ea1 + 013d is NT! The address of the kisystemservicestart entry. Next we will start disassembly from this address.
NT! Kisystemservicestart:
Fffff800 '03c73fde 4889a3d8010000 mov qword PTR [RBx + 1d8h], RSP
Fffff800 '03c73fe5 8bf8 mov EDI, eax
Fffff800 '03c73fe7 c1ef07 shr edi, 7
Fffff800 '03c73fea 83e720 and EDI, 20 h
Fffff800 '03c73fed 25ff0f0000 and eax, 0 fffh
NT! Kisystemservicerepeat:
Fffff800 '03c73ff2 4c8d1547782300 Lea R10, [nt! Keservicedescriptortable (fffff800 '03eab840)]
Fffff800 '03c73ff9 4c8d1d80782300 Lea R11, [nt! Keservicedescriptortableshadow (fffff800 '03eab880)]
Fffff800 '03c74000 f783000000080000000 test dword ptr [RBx + 100 H], 80 h
Fffff800 '03c7400a 4d0f45d3 cmovne R10, R11
Fffff800 '03c7400e 423b441710 CMP eax, dword ptr [RDI + R10 + 10 h]
Fffff800 '03c74013 0f83e9020000 Jae nt! Kisystemserviceexit + 0x1a7 (fffff800 '03c74302)
Fffff800 '03c74019 4e8b1417 mov R10, qword PTR [RDI + R10]
Fffff800 '03c7401d 4d631c82 movsxd R11, dword ptr [R10 + Rax * 4]
Fffff800 '03c74021 498bc3 mov rax, R11
Fffff800 '03c74024 49c1fb04 SAR R11, 4
Fffff800 '03c74028 4d03d3 add R10, R11
Fffff800 '03c7402b 83ff20 cmp edi, 20 h
Fffff800 '03c7402e 7550 JNE nt! Kisystemservicegditebaccess + 0x49 (fffff800 '03c74080)
Fffff800 '03c74030 4c8b9bb8000000 mov R11, qword PTR [RBx + 0b8h]
Finally, we need to find the function. According to the two commands, I can locate the ssdt. 4c8d1547782300 and 4c8d1d80782300. The cloud is now available. Rex is added to both commands. according to the meaning of the specific field, the W prefix can be found in ssdt, 4c8d1547782300. The 64-bit addressing of this instruction should be fffff800 '03c73ff9 + 237847, which is the RIP + offset. Similar to the 4c8d1d80782300 command, the immediate number should be extended to fffff800 '03c74000 + 237880. You can use the DQ command to view the content of ssdt.
Kd> DQ fffff800 '03c74000 + 237880
Fffff800 '03eab880 fffff800 '03c75b00 100' 00000000
Fffff800 '03eab890 00000000 '00000191 fffff800' 03c7678c
Fffff800 '03eab8a0 fffff960 '00111c00 100' 00000000
Fffff800 '03eab8b0 00000000 '0000033b fffff960' 0011391c
Fffff800 '03eab8c0 00000000 '7771fdd6 100' 00000000
Fffff800 '03eab8d0 fffff800 '00a01400 fffff800' 00a013b0
Fffff800 '03eab8e0 00000000 '00000002 00000000' using 5bdb
Fffff800 '03eab8f0 00000000 '00023f05 00000000' 00000000
Kd> DQ nt! Keservicedescriptortableshadow
Fffff800 '03eab880 fffff800 '03c75b00 100' 00000000
Fffff800 '03eab890 00000000 '00000191 fffff800' 03c7678c
Fffff800 '03eab8a0 fffff960 '00111c00 100' 00000000
Fffff800 '03eab8b0 00000000 '0000033b fffff960' 0011391c
Fffff800 '03eab8c0 00000000 '7771fdd6 100' 00000000
Fffff800 '03eab8d0 fffff800 '00a01400 fffff800' 00a013b0
Fffff800 '03eab8e0 00000000 '00000002 00000000' using 5bdb
Fffff800 '03eab8f0 00000000 '00023f05 00000000' 00000000
The two methods have the same location. My output here is for calls to the win7 64-bit system. By debugging 2003, it is found that this method is also applicable. Because there are no other operating systems. The applicability of other operating systems is unknown. I guess XP and Vista should be similar, but it must be proved by facts. However, the ssdt table items in win7 and 2003 are slightly different, although the last four digits of the four sections are the number of parameters of the routine, however, the offset on 2003 is calculated based on ssdt base address + Table item Value & 0xfffffff0, while on win7 it is changed to ssdt base address + Table item value> 4. If you want to separate the hook table items. If any errors are described above, please kindly advise me. I am very grateful to you.