【问题标题】:Weird callstack leading up to CreateRemoteThread导致 CreateRemoteThread 的奇怪调用堆栈
【发布时间】:2017-10-09 08:06:24
【问题描述】:

我很难理解下面的调用堆栈。

ntdll!NtCreateThreadEx+0xc
KERNELBASE!CreateRemoteThreadEx+0x1f0
kernel32!CreateThreadStub+0x27
MyModule!_beginthreadex+0x87

很抱歉,由于专有问题,我无法透露更多信息,但我相信我们应该能够至少部分理解这一点。那是我正在查看的崩溃转储中第三方库生成的线程的状态。 (线程本身与我正在调查的问题无关。)

什么情况可能导致对 _beginthreadex 的调用最终进入 CreateRemoteThread? API 的 MSDN 文档没有给我任何线索(“远程”这个词甚至没有出现在那个页面上。)

我有第三方库的符号,调用堆栈绝对是合法的。

更新:

有趣。如果我从CreateThreadStub 应该返回的位置向后拆卸,我会看到以下说明:

call    dword ptr [MyModule!_imp__CreateThread (573701a0)]
cmp     eax,ebx

当我这样做时:

0:048> dds MyModule!_imp__CreateThread l1
573701a0  75b5cf30 kernel32!CreateThreadStub

当我反汇编 CreateThreadStub 时,我确实看到它在调用 CreateRemoteThread。这一切都非常令人困惑!

【问题讨论】:

  • 这是创建线程的绝对常用堆栈。 CreateRemoteThread[Ex] 在创建线程时总是被调用。
  • @RbMm 真的吗?我一直以为 CreateRemoteThread 在一个不同的进程的地址空间中创建了一个线程?
  • 是的,真的。 CreateRemoteThread[Ex] 总是在您调用 CreateThread_beginthreadex 时出现。你问What condition can possibly cause - 这是无条件的。 总是
  • @RbMm 谢谢!我不怕承认我从来不知道!我想我应该删除这个问题。

标签: multithreading winapi windbg callstack


【解决方案1】:

您发布的堆栈是普通堆栈。编译以下 C++ 代码:

#include "stdafx.h"
#include <iostream>
#include <process.h>
#include <Windows.h>
using namespace std;

void test(void *param)
{
    Sleep(1000);
    _endthread();
}

int main()
{
    HANDLE hThread;
    hThread = (HANDLE)_beginthread(test, 0, NULL);
    WaitForSingleObject(hThread, INFINITE);
    return 0;
}
  1. 启动 WinDbg
  2. 打开可执行文件
  3. 在初始断点处,设置断点

    0:000> bp KERNELBASE!CreateRemoteThreadEx
    0:000> bl
         0 e Disable Clear  7485b060     0001 (0001)  0:**** KERNELBASE!CreateRemoteThreadEx
    0:000> g
    
  4. 看到main() 方法在遇到_beginthread() 方法时调用CreateRemoteThreadEx()

    0:000> k
     # ChildEBP RetAddr  
    WARNING: Stack unwind information not available. Following frames may be wrong.
    00 00f7f714 0f7ba3cd KERNELBASE!CreateRemoteThreadEx
    01 00f7f758 000f255f ucrtbased!_beginthread+0xed [d:\rs1\minkernel\crts\ucrt\src\appcrt\startup\thread.cpp @ 174] 
    02 00f7f844 000f2cbe ThreadStartStack!main+0x2f [c:\users\t\documents\visual studio 2015\projects\threadstartstack\threadstartstack\threadstartstack.cpp @ 19] 
    03 00f7f858 000f2b20 ThreadStartStack!invoke_main+0x1e [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 64] 
    04 00f7f8b0 000f29bd ThreadStartStack!__scrt_common_main_seh+0x150 [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 253] 
    05 00f7f8b8 000f2cd8 ThreadStartStack!__scrt_common_main+0xd [f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl @ 296] 
    06 00f7f8c0 752362c4 ThreadStartStack!mainCRTStartup+0x8 [f:\dd\vctools\crt\vcstartup\src\startup\exe_main.cpp @ 17] 
    07 00f7f8d4 77370fd9 KERNEL32!BaseThreadInitThunk+0x24
    08 00f7f91c 77370fa4 ntdll!__RtlUserThreadStart+0x2f
    09 00f7f92c 00000000 ntdll!_RtlUserThreadStart+0x1b
    

【讨论】:

  • 谢谢托马斯。我应该自己做的。我只是没有意识到所有创建线程的请求最终都会发送到 CreateRemoteThreadEx,正如上面 Ben Voight 所说的那样。
【解决方案2】:

MSDN上的功能描述有点误导:

创建一个在另一个进程的虚拟地址空间中运行的线程。

第一行注释是对CreateRemoteThread的更好总结:

CreateRemoteThread 函数使新的执行线程在指定进程的地址空间开始。

至少通过这个描述,很明显可以使用CreateRemoteThread在本地进程中创建线程,只需指定当前进程即可。在CreateRemoteThreadEx 备注中还要注意这一点:

CreateRemoteThreadEx 函数使新的执行线程在指定进程的地址空间开始。线程可以访问进程打开的所有对象。 lpAttribute 参数可用于指定扩展属性,例如新线程的处理器组关联性。 如果lpAttribute 为NULL,则函数的行为与CreateRemoteThread 相同

当您考虑转换到内核模式时,以及CreateRemoteThreadEx 通过选择参数可以完成CreateThreadCreateRemoteThread 可以做的所有事情,以及测试正确性和安全性的维护成本在每个系统调用中,只为一个系统调用提供所有功能的超集并让所有这些功能都使用它是有意义的。

【讨论】:

  • 我现在意识到了这一点。在发布之前应该尝试一下 Thomas Weller 在下面展示的内容!谢谢本。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-02-09
  • 2012-03-20
  • 2011-07-02
  • 2012-02-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多