更新 :Microsoft尚未在Windows 8.1中修复它。
编辑 :结果是WOW64中的bug - 当线程在长模式环中挂起时,GetThreadContext()可能会返回陈旧内容3(用户模式)。我建议微软使用ring-2来执行翻译。然后,SuspendThread只会挂起ring-3中的线程(就像现在一样 - 不需要进行任何更改),而ring-2中的崩溃/故障/漏洞不会影响内核 - 它只会影响ring-2和ring- 3。
此类更改将需要更改一些WinAPI函数,例如Wow64Get / SetThreadContext等。这会破坏依赖于未记录的功能的应用程序,但这是可以预期的。当然,转换速度会慢一些,因为需要几个CPU周期才能从ring-3转换到ring-2(取决于CPU系列),但我认为操作系统的作用首先是确保正确操作。翻译已经增加了在WOW64下运行的应用程序的开销,所以这也是预期的。
我确实希望微软能解决这个问题 - 否则依赖于WOW64下的GetThreadContext()的调试器/ Mono应用程序/ Boehm GC /应用程序将无效(对于初学者,我看到调试器显示过时的堆栈跟踪)。 / p>
EDIT2 :坏消息。从我与MSFT的Alexey(here)的对话中看起来似乎根本不会修复,因为担心修复会破坏依赖于未记录的功能的应用程序。
原始问题
GetThreadContext()
返回的陈旧内容。来自MSDN:
Suspending a thread causes the thread to stop executing user-mode (application) code.
然而,我发现我的Windows 7中的32位应用程序在WOW64下运行,线程A调用线程B上的SuspendThread可以在运行64位代码时将其暂停(我希望不是用户模式代码)。 EIP显示暂停的线程停在
wow64cpu!X86SwitchTo64BitMode:
00000000`759c31b0 ea27369c753300 jmp 0033:759C3627
其ESP已更改(我知道这是因为,虽然ESP指向与该线程的堆栈相同的页面,但它的地址比当前堆栈指针高得多)。如果我在上面返回的指令处放置断点,然后让线程恢复,我发现ESP更改回X86SwitchTo64BitMode调用之前的值(这是正确的堆栈指针)。我还发现,当单步进入同一个函数时,我无法在单步的任何一点获得更高的地址ESP值。事实上,当单步执行时,ESP值在X86SwitchTo64BitMode调用之前和之后都不会发生变化。
另外,我确实通过检查(DWORD)-1确保SuspendThread成功。
所有这些让我相信线程在内核模式代码中被暂停。
什么可能导致操作系统在运行非用户模式代码时挂起线程?我该如何预防呢?这基本上阻止我获取线程B的实际当前堆栈指针。注意,当应用程序在WOW64之外运行时(在本机x86 OS上),不存在这样的问题。
答案 0 :(得分:3)
答案 1 :(得分:1)
请参阅此说明:GetThreadContext in Wow64
本文解释说,x86和amd64模式之间的转换是在用户模式下完成的。
答案 2 :(得分:0)
你的线程在用户模式下做了什么?当你拨打SuspendThread
时,它似乎已经处于内核模式。它暂停时是否可能正在执行系统功能?
什么可能导致操作系统在运行非用户模式代码时挂起线程?
许多系统或库调用可能导致切换到内核模式。并且因为Windows Kernel在大多数情况下都是可重入的,所以在第一个线程处于内核模式时从一个线程切换到另一个线程是很正常的。
我该如何预防?
只是一个想法:创建一个只执行空循环的线程(例如for(;;);
)并暂停该线程。不应该在内核模式下暂停这个。
另外,为什么ESP寄存器等正确对你很重要?我希望您正在编写某种调试器或相关的东西,因为这是SuspendThread
的用途。
答案 3 :(得分:-1)
从技术上讲,当一个线程根本没有运行时,它既不运行内核模式代码也不运行用户模式代码。所以你的观察与声明并不矛盾。
贝斯德斯,你不应该搞乱这个。如果您(在用户模式下)可以控制是否执行了内核模式代码,那将是一个操作系统错误。