В настоящее время я экспериментирую с возможностями переноса выполнения потока на другой вновь созданный поток из текущего потока (я надеюсь, что это правильное слово); Вот иллюстрация:Можно ли передать выполнение потока в другой поток?
- Резьба1 работает
- Резьба1 остановки в середине кода и создать thread2
- Резьба2 продолжается с середины кода, где Резьба1 остановить
EDIT: Обновлен пример.
#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;
}
Код может быть больно читать, поскольку он состоит в основном из asm. Поэтому я добавил небольшой комментарий, чтобы помочь. Теперь, когда я тестирую, это вполне возможно, но с некоторыми проблемами. Вызов нескольких win api кажется прекрасным, но вызов printf обязательно приведет к сбою функции stkchk() (доступ запрещен). Я попробую вариант, если есть какое-либо предложение.
Можете ли вы объяснить, почему вы думаете, что это может вам помочь? Таким образом, мы можем сказать вам правильное решение вашей проблемы. Это не так. –
Дэвид, это полностью мой собственный эксперимент - изучение возможностей. В настоящее время нет проблем, связанных с этим, просто эксперимент. Мне может понадобиться какое-то техническое объяснение, почему оно или не будет работать. Благодаря! –
Существует множество причин, по которым это не сработает. Я не могу найти мотивацию, чтобы попытаться убедить вас в этой бессмысленной задаче. Если вы хотите попытаться это сделать, продолжайте. –