gpt4 book ai didi

winapi - x86_64 - Windows 上的 64 位应用程序可以执行 INT 2E 而不是系统调用吗?

转载 作者:行者123 更新时间:2023-12-05 00:52:27 25 4
gpt4 key购买 nike

此问题与 this one 有关但它并没有填补我的一些空白,所以我决定再问一遍,并提供更多细节,也许会为此提供赏金。

不管怎样,通常如果你在 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

现在我知道这是比较 KUSER_SHARED_DATA 的偏移量并决定是否执行 syscallINT 2E。起初我认为如果正在运行的程序是 32 位应用程序,就会执行 INT 2E,但是在阅读了一些关于 WOW64 的信息之后,似乎这些应用程序将使用不执行 int 2e 而是执行的 ntdll 的 32 位版本穿过天堂之门到达内核:

    public ZwClose
ZwClose proc near
mov eax, 3000Fh ; NtClose
mov edx, offset j_Wow64Transition
call edx ; j_Wow64Transition
retn 4
ZwClose endp

据我了解,Wow64Transition 最终会跳转到我首先列出的 64 位版本的 ntdll,对吧?如果是这样,那是在执行 INT 2E 而不是系统调用时?我还被告知 INT 2E 的原因之一是 CET兼容性,所以我对 INT 2E 有点困惑。

最佳答案

So as far as I understand Wow64Transition will eventually jump to the 64-bit version of ntdll which I listed first, right?

是的。

If that's so, is that when INT 2E is executed instead of syscall?

没有。

首先,让我们明白一点:您仍然可以在现代 Windows 系统上毫无问题地调用 INT 0x2E,中断向量仍然在这里并指向系统调用调度程序:

0: kd> !idt 0x2e

Dumping IDT: fffff8010a900000

2e: fffff8010ca11ec0 nt!KiSystemServiceShadow

是什么让它调用 int 0x2E?

如您所见,执行 ring3/ring0 转换的代码段检查了 KUSER_SHARED_DATA 结构中的位。

在偏移量 0x308 处,我们有一个名为 SystemCall 的字段:

0: kd> dt _kuser_shared_data
nt!_KUSER_SHARED_DATA
...
+0x308 SystemCall : Uint4B
...

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

因此,如果 KiSystemCallSelector 为 1 并且 HvlEnlightenments 设置了第 19 位,则设置了 KUSER_SHARED_DATA.SystemCall

HvlEnlightenments 是一个位域,当虚拟化操作系统知道它实际上是虚拟化时设置(这些操作系统称为“启蒙操作系统”)。这意味着该功能(调用 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

我们可以看到这个函数的第一个参数很重要;碰巧它是一个名为 KeLoaderBlock 的全局内核变量:

PAGELK:0000000140597154                 mov     rcx, cs:KeLoaderBlock_0
PAGELK:000000014059715B call KiInitializeBootStructures

它的类型已知是_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

_LOADER_PARAMETER_EXTENSION 结构的偏移量 0x74 处,我们有一个位域:

              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
};

bt eax, 8 指令正在测试第 8 位,因此是 VsmConfigured 位。

因此,如果我们被虚拟化并且 VsmConfigured 为 1,那么我们使用 INT 0x2E。

为什么?

VSM 代表 Virtual Secure Mode它引入了用于隔离操作系统本身的部分的 VTL(虚拟信任级别):例如,VTL0 是所谓的“正常世界”,操作系统的“通常”部分驻留(包括内核及其虚拟空间)而 VTL1 包含安全内核和称为“truslets”的非常具体的进程(参见 IUM 以获得更详细的说明)。

那时我只能猜测;我的第一个想法是调用 INT 0x2E 仅适用于特定内核(不是 VTL0 中的“普通”内核,但我仍然不知道是哪个)。

VMM(管理程序)实际上比系统调用更容易捕获 VM 退出以进行中断;当某些事件(例如 INT、RDMSR、WMSR 等特定指令)发生时,会发生 VM 退出,这些事件使代码从其正常执行流回到管理程序,因此管理程序实际上可以查看是什么触发了 VM 退出并采取行动相应地(例如重定向代码流或“撒谎”到操作系统)。


写完这个答案后,我看到有人在一篇解释更详尽的博文中实际上追逐了相同的路径:The Windows 10 TH2 INT 2E mystery .他们不确定内核将在哪种情况下使用 INT2E。我们只能在那个时候猜测。

关于winapi - x86_64 - Windows 上的 64 位应用程序可以执行 INT 2E 而不是系统调用吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70028273/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com