Tre*_*rey 3 x86 assembly winapi x86-64 system-calls
这个问题与这个问题相关,但它没有填补我的一些空白,所以我决定再次询问它并提供更多细节,也许可以对此给予赏金。
无论如何,通常如果您在 ntdll 上查找 Nt/Zw 函数,您会看到类似以下内容的内容:
ZwClose proc near
mov r10, rcx
mov eax, 0Fh
test byte ptr ds:7FFE0308h, 1
jnz short loc_a
syscall
retn
loc_a:
int 2Eh
retn
NtClose endp
Run Code Online (Sandbox Code Playgroud)
现在我知道这是比较KUSER_SHARED_DATA的偏移量并决定是否执行系统调用与INT 2E。起初我认为如果正在运行的程序是 32 位应用程序,则会执行 INT 2E,但在阅读了一些有关 WOW64 的内容后,这些应用程序似乎会使用 32 位版本的 ntdll,它不执行 int 2e 而是执行穿过天堂之门到达内核:
public ZwClose
ZwClose proc near
mov eax, 3000Fh ; NtClose
mov edx, offset j_Wow64Transition
call edx ; j_Wow64Transition
retn 4
ZwClose endp
Run Code Online (Sandbox Code Playgroud)
据我了解,Wow64Transition最终会跳转到我首先列出的 ntdll 的 64 位版本,对吗?如果是这样,是不是执行 INT 2E 而不是系统调用时?我还被告知 INT 2E 的原因之一是CET兼容性,所以我对 INT 2E 有点困惑。
据我了解,Wow64Transition 最终会跳转到我首先列出的 ntdll 的 64 位版本,对吗?
是的。
如果是这样,是不是执行 INT 2E 而不是系统调用时?
不。
首先,让我们先弄清楚这一点:您仍然可以在现代 Windows 系统上调用 INT 0x2E,没有任何问题,中断向量仍然在这里并指向系统调用调度程序:
0: kd> !idt 0x2e
Dumping IDT: fffff8010a900000
2e: fffff8010ca11ec0 nt!KiSystemServiceShadow
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,执行ring3/ring0转换的代码片段检查KUSER_SHARED_DATA结构中的一位。
在偏移量 0x308 处,我们有一个名为 的字段SystemCall:
0: kd> dt _kuser_shared_data
nt!_KUSER_SHARED_DATA
...
+0x308 SystemCall : Uint4B
...
Run Code Online (Sandbox Code Playgroud)
KUSER_SHARED_DATA 映射到两个不同的地址:一个位于用户空间 (0x7FFE0000),另一个位于内核空间 (0xFFFFF78000000000)。这两个地址都由同一物理页支持(用户空间显然是只读的)。
请注意,这些地址是恒定的并且不受 ASLR 的影响。因此,我们可以在内核中搜索该0xFFFFF78000000308地址(即,KUSER_SHARED_DATA.SystemCall但在内核中)并查看是否有匹配项。
实际上,名为的函数中只有一个匹配项KiInitializeKernel
PAGELK:00000001405A36A2 mov r14d, 1
PAGELK:00000001405A36A8 cmp cs:KiSystemCallSelector, r14d
PAGELK:00000001405A36AF jnz loc_1405A3161
;...
PAGELK:00000001405A9236 test cs:HvlEnlightenments, 80000h
PAGELK:00000001405A9240 jz loc_1405A3161
PAGELK:00000001405A9246 mov eax, r14d
PAGELK:00000001405A9249 mov ds:0FFFFF78000000308h, eax
Run Code Online (Sandbox Code Playgroud)
因此,如果KiSystemCallSelector为 1 并且HvlEnlightenments设置了位 19,则KUSER_SHARED_DATA.SystemCall设置为 1。
HvlEnlightenments是当虚拟化操作系统知道它实际上是虚拟化时设置的位字段(这些操作系统称为“enlightened OS”)。这意味着该功能(调用 INT 0x2E 而不是 SYSCALL)与虚拟化操作系统相关。
我们只剩下KiSystemCallSelector;该变量在一个名为的函数中设置KiInitializeBootStructures:
PAGELK:00000001405A1E48 mov rsi, rcx ; rsi = rcx (1st function param)
; ...
PAGELK:00000001405A2052 mov rdx, [rsi+0F0h]
PAGELK:00000001405A2059 mov eax, [rdx+74h]
; ...
PAGELK:00000001405A206A loc_1405A206A:
PAGELK:00000001405A206A bt eax, 8
PAGELK:00000001405A206E jnb short loc_1405A2077
PAGELK:00000001405A2070 mov cs:KiSystemCallSelector, r13d ; r13d = 1
Run Code Online (Sandbox Code Playgroud)
我们可以看到这个函数的第一个参数很重要;它恰好是一个名为的全局内核变量KeLoaderBlock:
PAGELK:0000000140597154 mov rcx, cs:KeLoaderBlock_0
PAGELK:000000014059715B call KiInitializeBootStructures
Run Code Online (Sandbox Code Playgroud)
它的类型是已知的,_LOADER_PARAMETER_BLOCK并且它的定义在内核符号中是公开的,因此前面的代码看起来像这样,带有符号信息:
PAGELK:00000001405A2052 mov rdx, [rsi+_LOADER_PARAMETER_BLOCK.Extension] ; _LOADER_PARAMETER_EXTENSION*
PAGELK:00000001405A2059 mov eax, [rdx+_LOADER_PARAMETER_EXTENSION._bf_74] ; bit field
; ...
PAGELK:00000001405A206A loc_1405A206A:
PAGELK:00000001405A206A bt eax, 8
PAGELK:00000001405A206E jnb short loc_1405A2077
PAGELK:00000001405A2070 mov cs:KiSystemCallSelector, r13d ; r13d = 1
Run Code Online (Sandbox Code Playgroud)
在结构的偏移 0x74 处,_LOADER_PARAMETER_EXTENSION我们有一个位域:
struct // 22 elements, 0x4 bytes (sizeof)
{
/*0x074*/ ULONG32 LastBootSucceeded : 1; // 0 BitPosition
/*0x074*/ ULONG32 LastBootShutdown : 1; // 1 BitPosition
/*0x074*/ ULONG32 IoPortAccessSupported : 1; // 2 BitPosition
/*0x074*/ ULONG32 BootDebuggerActive : 1; // 3 BitPosition
/*0x074*/ ULONG32 StrongCodeGuarantees : 1; // 4 BitPosition
/*0x074*/ ULONG32 HardStrongCodeGuarantees : 1; // 5 BitPosition
/*0x074*/ ULONG32 SidSharingDisabled : 1; // 6 BitPosition
/*0x074*/ ULONG32 TpmInitialized : 1; // 7 BitPosition
/*0x074*/ ULONG32 VsmConfigured : 1; // 8 BitPosition
/*0x074*/ ULONG32 IumEnabled : 1; // 9 BitPosition
/*0x074*/ ULONG32 IsSmbboot : 1; // 10 BitPosition
/*0x074*/ ULONG32 BootLogEnabled : 1; // 11 BitPosition
/*0x074*/ ULONG32 DriverVerifierEnabled : 1; // 12 BitPosition
/*0x074*/ ULONG32 SuppressMonitorX : 1; // 13 BitPosition
/*0x074*/ ULONG32 SuppressSmap : 1; // 14 BitPosition
/*0x074*/ ULONG32 Unused : 6; // 15 BitPosition
/*0x074*/ ULONG32 FeatureSimulations : 6; // 21 BitPosition
/*0x074*/ ULONG32 MicrocodeSelfHosting : 1; // 27 BitPosition
/*0x074*/ ULONG32 XhciLegacyHandoffSkip : 1; // 28 BitPosition
/*0x074*/ ULONG32 DisableInsiderOptInHVCI : 1; // 29 BitPosition
/*0x074*/ ULONG32 MicrocodeMinVerSupported : 1; // 30 BitPosition
/*0x074*/ ULONG32 GpuIommuEnabled : 1; // 31 BitPosition
};
Run Code Online (Sandbox Code Playgroud)
该bt eax, 8指令正在测试位 8,即该VsmConfigured位。
因此,如果我们是虚拟化的且VsmConfigured为 1,则我们使用 INT 0x2E。
VSM 代表Virtual Secure Mode引入了 VTL(虚拟信任级别),用于隔离操作系统本身的各个部分:例如,VTL0 是所谓的“正常世界”,操作系统的“常用”部分(包括内核及其虚拟空间),而 VTL1 包含安全内核和称为“truslet”的非常具体的进程(请参阅IUM参考资料以获得更长的解释)。
那时我只能猜测;我的第一个想法是调用 INT 0x2E 仅适用于特定内核(不是 VTL0 中的“正常”内核,但我仍然不知道是哪一个)。
实际上,VMM(虚拟机管理程序)比系统调用更容易捕获 VM 退出以进行中断;当某些事件(例如 INT、RDMSR、WMSR 等特定指令)发生时,会发生 VM 退出,这些事件使代码从正常执行流转换回虚拟机管理程序,因此虚拟机管理程序实际上可以查看触发 VM 退出的原因并采取行动相应地(例如重定向代码流或“撒谎”到操作系统)。
写完这个答案后,我看到有人实际上在一篇更彻底解释的博客文章中追寻了同样的道路: Windows 10 TH2 INT 2E 之谜。但他们不确定内核在哪种具体情况下将使用 INT2E。到那时我们只能猜测。
| 归档时间: |
|
| 查看次数: |
1245 次 |
| 最近记录: |