如何检查是否在Visual C ++应用程序的第三方DLL中创建了新线程

时间:2019-05-08 11:44:20

标签: c++ windows visual-studio profiling

我目前正在努力验证是否可以从可视c ++应用程序中的代码中调用的dll文件中创建新线程。最好是我想查看资源消耗并在dll内部创建任何线程的情况下调用堆栈。我需要在非调试模式下运行应用程序时发生这种情况。你们中有人知道我们如何解决这个问题吗?

1 个答案:

答案 0 :(得分:2)

让应用程序在其启动代码中挂勾CreateThread API,最好在DLL初始化之前。虽然这是深奥的魔力。您必须确切地知道自己在做什么。

编辑:如果必须询问,则可能无法完成任务。后果自负。

通常的想法是:使用反汇编程序在运行的进程中查看CreateThread()的前几个命令。在主要的exe代码中,修补CreateThread的内存,以将JMP命令插入到自己的蹦床功能中。在汇编中编写一个蹦床,它将调用您自己的钩子函数,也许从修补部分重复一些命令,然后跳回到CreateThread()的未修补部分。

这不是用于生产-仅用于在自己的计算机上运行具有已知版本的CreateThread的可执行文件,因为它取决于CreateThread的内容。在某种程度上,应用程序成为了自己的调试器,但这不是DLL可以轻易注意到的调试器。

要修补进程内存的可执行部分,您可能必须调整内存保护标志,默认情况下它将不可写。

自然,您必须要注意咬住。

或者,您可以修补CreateThread的开头以引发异常(例如INT 3),并使用向量异常处理程序来捕获该异常。 Win32 API函数的开头有一个2字节的NOP,刚好可以修补。

编辑:尝试使用VEH方法,看起来更干净。现在,在我的机器上,CreateThread的第一个命令是MOV EDI, EDI-实际上是2字节的无操作,非常适合修补。因此,异常处理程序变为:

LONG NTAPI OnExc(_EXCEPTION_POINTERS* Exc)
{
    if (Exc->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
    { //Feel free to add an extra check for exception address
      //Just in case there are rogue INT 3's elsewhere

        wprintf(L"Yay, thread created\n");
        //Not a good idea to do I/O from the exception handler :)

        //Continue from the next command after INT 3
        Exc->ContextRecord->Eip++; 
        return EXCEPTION_CONTINUE_EXECUTION;
    }
    else
        return EXCEPTION_CONTINUE_SEARCH;
}

挂钩代码变为:

AddVectoredExceptionHandler(1, OnExc);

HMODULE hKernel32 = GetModuleHandle(L"kernel32.dll");
unsigned char* pCreateThread = (unsigned char*)GetProcAddress(hKernel32, "CreateThread");

//Allow writing to the memory block where CreateThread is
MEMORY_BASIC_INFORMATION mi;
VirtualQuery(pCreateThread, &mi, sizeof mi);
DWORD dw;
VirtualProtect(mi.BaseAddress, mi.RegionSize, PAGE_EXECUTE_READWRITE, &dw);

//Check if the first two bytes are indeed MOV EDI, EDI
if (pCreateThread[0] == 0x8b && pCreateThread[1] == 0xff)
{
    //And patch!
    pCreateThread[0] = 0xcc; //Replace with INT 3 
    pCreateThread[1] = 0x90; //Replace with NOP
}

就这样,着迷了。随时通过调用_beginthread_beginthreadex进行测试-但不能在调试器下进行。调试器将在异常处理程序执行之前捕获并处理INT 3。

这都是32位代码。甚至都没有看过Win64的外观。

我没有涉及工作的其他方面。具体来说,如果所涉及的DLL是静态加载的,则会在其启动代码中创建线程,而(如果钩子已安装在[Win] main()中)会创建线程。 ,那么该钩子将不会捕获那些线程。由于这是您的项目,因此我无法对其进行检查。

另一种增强途径-如果您想捕捉线程创建的结果(例如线程ID),仅此技术是行不通的。您不仅需要钩住CreateThread的入口点,还需要钩住出口。