C++ 是否可以将线程执行转移到另一个线程?
我目前正在尝试将线程执行从当前线程转移到另一个新创建的线程的可能性(我希望这是一个正确的词);以下是示例:C++ 是否可以将线程执行转移到另一个线程?,c++,c,multithreading,winapi,x86,C++,C,Multithreading,Winapi,X86,我目前正在尝试将线程执行从当前线程转移到另一个新创建的线程的可能性(我希望这是一个正确的词);以下是示例: 线程1运行 在代码中间停止创建TyRe2 Thread2从Thread1停止的代码中间继续 编辑:更新了示例 #include "stdafx.h" #include <memory> #include <windows.h> #include <cassert> int _eax, _ebx, _ecx, _edx; int _ebp, _esp
- 线程1运行
- 在代码中间停止创建TyRe2
- Thread2从Thread1停止的代码中间继续
#include "stdafx.h"
#include <memory>
#include <windows.h>
#include <cassert>
int _eax, _ebx, _ecx, _edx;
int _ebp, _esp, _esi, _edi;
int _eip;
int _flags;
int _jmp_addr;
bool thread_setup = false;
CONTEXT PrevThreadCtx;
HANDLE thread_handle;
int _newt_esp;
int _newt_ret;
DWORD WINAPI RunTheThread(LPVOID lpParam)
{
// 1000 is more than enough, call to CreateThread() should already return by now.
Sleep(1000);
ResumeThread(thread_handle);
return 0;
}
DWORD WINAPI DummyPrologueEpilogue(LPVOID lpParam)
{
return 123;
}
__declspec(naked) void TransferThread(LPVOID lpParam)
{
//longjmp(jmpbuf, 0);=
__asm
{
call get_eip;
cmp[_newt_esp], 0;
mov[_newt_ret], eax;
jz setup_new_thread;
jmp DummyPrologueEpilogue;
get_eip:
mov eax, [esp];
ret;
setup_new_thread:
pushad;
mov[_newt_esp], esp;
mov eax, [_flags];
push eax;
popfd;
mov eax, [_eax];
mov ebx, [_ebx];
mov ecx, [_ecx];
mov edx, [_edx];
mov ebp, [_ebp];
mov esp, [_esp];
mov esi, [_esi];
mov edi, [_edi];
jmp [_eip];
}
}
int _tmain(int argc, _TCHAR* argv[])
{
int x = 100;
char szTest[256];
sprintf_s(szTest, "x = %d", x);
//HideThread();
//setjmp(jmpbuf);
__asm
{
// Save all the register
mov[_eax], eax;
mov[_ebx], ebx;
mov[_ecx], ecx;
mov[_edx], edx;
mov[_ebp], ebp;
mov[_esp], esp;
mov[_esi], esi;
mov[_edi], edi;
push eax;
// Save the flags
pushfd;
pop eax;
mov[_flags], eax;
// If we on *new thread* jmp to end_asm, otherwise continue...
call get_eip;
mov[_eip], eax;
mov al, byte ptr[thread_setup];
test al, al;
jnz end_asm;
mov eax, [jmp_self];
mov[_jmp_addr], eax;
pop eax;
mov[_newt_esp], 0;
mov byte ptr[thread_setup], 1;
push 0;
push CREATE_SUSPENDED;
push 0;
push TransferThread;
push 0;
push 0;
call CreateThread;
mov [thread_handle], eax;
// Create another thread just to resume 'TransferThread()'/*new thread* to give time to
// __stdcall below to return properly, thus restoring the stack.
// So the *new thread* does not accidentally pop the value from stacks or the __stdcall cleanup
// code doesn't accidentally overwrites new pushed value from *new thread*.
push 0;
push 0;
push 0;
push RunTheThread;
push 0;
push 0;
call CreateThread;
// Jump to self, consumes CPU
jmp_self:
jmp jmp_self;
nop;
nop;
jmp end_asm;
get_eip:
mov eax, [esp];
ret;
end_asm:
}
// Test stack-based variable
MessageBoxA(0, szTest, "Hello World!", MB_OK);
assert(x = 100);
x += GetCurrentThreadId();
sprintf_s(szTest, "x = %d", x);
HMODULE hMod = LoadLibrary(TEXT("comctl32"));
FreeLibrary(hMod);
try
{
std::unique_ptr<char[]> pTest(new char[256]);
sprintf_s(pTest.get(), 256, "WinApi call test. Previous loadLibrary() call return %X", hMod);
MessageBoxA(0, pTest.get(), "Hello World!", MB_OK);
} catch (...) {}
char *pszTest = (char*) malloc(256);
if (pszTest)
{
float f = 1.0;
f *= (float) GetCurrentThreadId();
sprintf_s(pszTest, 256, "Current Thread ID = %X, Thread handle = %X, FP Test = %f", GetCurrentThreadId(), GetCurrentThread(), f);
MessageBoxA(0, pszTest, "Hello World!", MB_OK);
free( pszTest );
}
// printf() from *new thread* will fail on stkchk()
//printf("Simple test\n");
// Let's terminate this *new* thread and continue the old thread
if (thread_setup)
{
DWORD OldProtect;
thread_setup = false;
VirtualProtect((PVOID)_jmp_addr, 2, PAGE_EXECUTE_READWRITE, &OldProtect);
*(int*)(_jmp_addr) = 0x90909090; // Prev thread not suspended. Just hope this op is atomic.
// Operation below will change the stack pointer
//VirtualProtect((PVOID)_jmp_addr, 2, OldProtect, &OldProtect);
//FlushInstructionCache(GetCurrentProcess(), (PVOID)_jmp_addr, 2);
__asm {
push eax;
mov eax, jmp_self2;
mov[_jmp_addr], eax;
pop eax;
jmp_self2:
jmp jmp_self2;
nop;
nop;
mov esp, [_newt_esp];
popad;
jmp _newt_ret;
}
}
else
{
DWORD OldProtect;
VirtualProtect((PVOID)_jmp_addr, 2, PAGE_EXECUTE_READWRITE, &OldProtect);
*(int*)(_jmp_addr) = 0x90909090; // Prev thread not suspended. Just hope this op is atomic.
}
// Show both thread can be exited cleanly... with some hacks.
DWORD dwStatus;
while (GetExitCodeThread(thread_handle, &dwStatus) && dwStatus == STILL_ACTIVE) Sleep(10);
printf("*New Thread* exited with status %d (Expected 123), Error=%X\n", dwStatus, GetLastError());
assert(dwStatus == 123);
printf("Test printf from original thread!\n");
printf("printf again!\n");
printf("and again!\n");
Sleep( 1000 );
return 0;
}
#包括“stdafx.h”
#包括
#包括
#包括
整数eax、ebx、ecx、edx;
电子商务、电子商务、电子商务、电子数据交换;
国际环境保护计划;
国际旗帜;
地址;
bool thread_setup=false;
CTX的上下文;
手柄螺纹与手柄;
内特纽特esp;
国际新闻网;
DWORD WINAPI运行线程(LPVOID lpParam)
{
//1000就足够了,调用CreateThread()现在应该已经返回了。
睡眠(1000);
恢复螺纹(螺纹柄);
返回0;
}
DWORD WINAPI DummyPrologueEpilogue(LPVOID lpParam)
{
返回123;
}
__declspec(裸)无效传输线程(LPVOID lpParam)
{
//longjmp(jmpbuf,0)=
__asm
{
调用get_eip;
cmp[_newt_esp],0;
mov[_newt_ret],eax;
jz设置新线程;
jmp Dummy序言结语;
获取eip:
mov-eax[esp];
ret;
设置新线程:
普沙德;
动作,动作;
mov eax,[_标志];
推动eax;
popfd;
mov-eax,[[u-eax];
mov-ebx,[u-ebx];
mov-ecx,[u-ecx];
移动edx,[[u edx];
mov-ebp,[u-ebp];
移动esp,[[u esp];
电影esi,[[u esi];
移动电子数据交换;
jmp[_eip];
}
}
int _tmain(int argc,_TCHAR*argv[]
{
int x=100;
char-szTest[256];
sprintf_s(szTest,“x=%d”,x);
//HideThread();
//setjmp(jmpbuf);
__asm
{
//保存所有注册表
mov[_eax],eax;
mov[_ebx],ebx;
mov[_ecx],ecx;
mov[_edx],edx;
mov[_ebp],ebp;
mov[_esp],esp;
mov[_esi],esi;
电子数据交换,电子数据交换;
推动eax;
//保存标志
pushfd;
pop-eax;
mov[_标志]、eax;
//如果我们在*新线程*jmp上结束任务,否则继续。。。
调用get_eip;
mov[_eip],eax;
mov al,字节ptr[线程设置];
试验al,al;
jnz-end_-asm;
mov eax,[jmp_self];
mov[_jmp_addr],eax;
pop-eax;
mov[\u newt\u esp],0;
mov字节ptr[线程设置],1;
推0;
推式创建暂停;
推0;
推送螺纹;
推0;
推0;
调用CreateThread;
mov[thread_handle],eax;
//创建另一个线程只是为了恢复“TransferThread()”/*新线程*以便有时间
//_uustdcall在下面正确返回,从而恢复堆栈。
//因此,*新线程*不会意外地从堆栈或\uu stdcall清理中弹出值
//代码不会意外地覆盖*新线程*中的新推送值。
推0;
推0;
推0;
推送运行线程;
推0;
推0;
调用CreateThread;
//跳转到self,消耗CPU
jmp_self:
jmp-jmp_-self;
否;
否;
jmp-end_-asm;
获取eip:
mov-eax[esp];
ret;
完:
}
//基于测试堆栈的变量
MessageBoxA(0,szTest,“你好,世界!”,MB_OK);
断言(x=100);
x+=GetCurrentThreadId();
sprintf_s(szTest,“x=%d”,x);
HMODULE hMod=LoadLibrary(文本(“comctl32”);
免费图书馆;
尝试
{
std::unique_ptr pTest(新字符[256]);
sprintf_s(pTest.get(),256,“WinApi调用测试。上一个loadLibrary()调用返回%X”,hMod);
MessageBoxA(0,pTest.get(),“Hello World!”,MB_OK);
}捕获(…){}
char*pszTest=(char*)malloc(256);
if(pszTest)
{
浮点数f=1.0;
f*=(float)GetCurrentThreadId();
sprintf_s(pszTest,256,“当前线程ID=%X,线程句柄=%X,FP测试=%f”,GetCurrentThreadId(),GetCurrentThread(),f);
MessageBoxA(0,pszTest,“你好,世界!”,MB_OK);
免费(pszTest);
}
//来自*新线程*的printf()将在stkchk()上失败
//printf(“简单测试”);
//让我们终止这个*新*线程并继续旧线程
如果(线程设置)
{
德沃德·奥尔德普雷特;
线程设置=错误;
VirtualProtect((PVOID)_jmp_addr,2,PAGE_EXECUTE_READWRITE,&OldProtect);
*(int*)(_jmp_addr)=0x9090;//上一个线程没有挂起。只希望这个操作是原子的。
//下面的操作将更改堆栈指针
//VirtualProtect((PVOID)_jmp_addr,2,OldProtect和OldProtect);
//FlushInstructionCache(GetCurrentProcess(),(PVOID)_jmp_addr,2);
__asm{
推动eax;
mov eax,jmp_self2;
mov[_jmp_addr],eax;
pop-eax;
jmp_self2:
jmp jmp_self2;
否;
否;
移动esp,[新的esp];
波帕德;
jmp_newt_ret;
}
}
其他的
{
德沃德·奥尔德普雷特;
VirtualProtect((PVOID)_jmp_addr,2,PAGE_EXECUTE_READWRITE,&OldProtect);
*(int*)(_jmp_addr)=0x9090;//上一个线程没有挂起。只希望这个操作是原子的。
}
//显示两个线程都可以干净地退出…使用一些hack。
德沃德·德沃德身份;
while(GetExitCodeThread(thread\u handle,&dwStatus)和&dwStatus==仍处于活动状态)睡眠(10);
printf(“*新线程*已退出,状态为%d(预期为123),错误=%X\n”,dwStatus,GetLastError());
断言(dwStatus==123);
printf(“从原始线程测试printf!\n”);
printf(“再次printf!\n”);
printf(“再次!\n”);
睡眠(1000);
返回0;
}
代码可能很难阅读,因为它主要由asm组成。所以我加了一点评论来帮助你。现在我进行了测试,这是很有可能的,但是有一些问题。调用少量win api似乎不错,但调用printf