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