3环模拟线程切换
线程切换实际就是切换保存线程的堆栈。
线程切换参数
结构体仿写 _EHREAD 中基本的字段。
//线程结构体(_EHREAD)
typedef struct _IM_SWITCH_THREAD
{
char* name; //线程名
int iFlage; //线程状态
int SleepTime; //休眠时间
void* InitialStack; //线程堆栈起始的位置
void* StackLimit; //线程堆栈界限
void* KernelStack; //线程堆栈当前的位置,即ESP
void* lpParameter; //线程函数的参数
void (*func)(void* lpParameterFunction); //线程函数
}IM_SWITCH_THREAD, *PIM_SWITCH_THREAD;
在window内核中线程是根据状态分开存放的,这里用一个数组加标志位存放所有的线程状态。
//保存线程数组
static IM_SWITCH_THREAD ImThreadList_My[THREAD_MAX_COUNT] = { 0 };
用来保存当前运行线程的编号:
static int iCurrentThreadIndex = 0;
线程初始化堆栈函数
在创建一个一个线程时,我们要给线程初始化一个堆栈,用来存放线程数据,并给结构体赋值。
//初始化堆栈
void initImThread(PIM_SWITCH_THREAD ImThreadInfo, char* name, void (*func)(void* lpParameterFunction), void* lpParameter)
{
unsigned char* StackPages;
unsigned int* StackDwordParam;
//申请空间用来保存线程栈(申请物理页)
StackPages = (unsigned char*)VirtualAlloc(NULL, STACK_SIZE, MEM_COMMIT, PAGE_READWRITE);
//初始化内存
memset(StackPages, 0, STACK_SIZE);
//填充结构体 , 这里要模拟堆栈的数据
ImThreadInfo->iFlage = THREAD_CREAD;
ImThreadInfo->InitialStack = StackPages + STACK_SIZE;
ImThreadInfo->StackLimit = StackPages;
ImThreadInfo->name = name;
ImThreadInfo->func = func;
ImThreadInfo->lpParameter = lpParameter;
StackDwordParam = (unsigned int*)(StackPages + STACK_SIZE);
//入栈
PushStack(&StackDwordParam, (unsigned int)lpParameter); //压入函数参数
PushStack(&StackDwordParam, (unsigned int)7); //随便写一个参数,用来平衡堆栈
PushStack(&StackDwordParam, (unsigned int)ThreadStartUp); //线程入口
PushStack(&StackDwordParam, (unsigned int)0); //push ebp
PushStack(&StackDwordParam, (unsigned int)0); //push edi
PushStack(&StackDwordParam, (unsigned int)0); //push esi
PushStack(&StackDwordParam, (unsigned int)0); //push ebx
PushStack(&StackDwordParam, (unsigned int)0); //push ecx
PushStack(&StackDwordParam, (unsigned int)0); //push edx
PushStack(&StackDwordParam, (unsigned int)0); //push eax
ImThreadInfo->KernelStack = StackDwordParam; //栈顶
return;
}
线程切换函数
实际上线程切换就只切换了线程的堆栈,即esp。
__declspec(naked) void SwitchThreadContext(PIM_SWITCH_THREAD pCurrentThread, PIM_SWITCH_THREAD pNewThread)
{
_asm
{
push ebp
mov ebp, esp //提升堆栈
push edi
push esi
push ebx
push ecx
push edx
push eax
mov esi, pCurrentThread //当前线程结构体指针
mov edi, pNewThread //要切换线程指针
mov[esi + IM_SWITCH_THREAD.KernelStack], esp //保存当前堆栈栈顶
//------------------线程切换------------------------------
mov esp, [edi + IM_SWITCH_THREAD.KernelStack]
pop eax
pop edx
pop ecx
pop ebx
pop esi
pop edi
pop ebp
ret //call startUp
}
}
此处的 ret 会call 之前的地址。
PushStack(&StackDwordParam, (unsigned int)ThreadStartUp); //线程入口
因为根据入栈顺序在 mov esp, [edi + IM_SWITCH_THREAD.KernelStack] 之后栈的情况:
esp -> eax
edx
ecx
ebx
esi
edi
ebp
ThreadStartUp
7
lpParameter
经过多次出栈后,ret会直接call 到 ThreadStartUp 函数的位置。
线程入口函数
这是最简单也是最难理解的函数,其中当切换完堆栈后 ret 回跳到我们指定的入口函数的位置,注意,所有线程一开始的入口函数是一样的,通过在入口函数中根据每个线程的入口函数地址不同,调用不同函数。
void ThreadStartUp(PIM_SWITCH_THREAD pSwitchThread)
{
pSwitchThread.iFlage = THREAD_CREAD;
pSwitchThread->func(pSwitchThread->lpParameter); \\调用函数地址
ExecuteThread();
}
我们通过,ret指令直接调用的 ThreadStartUp 函数,也就是说没有向 ThreadStartUp 函数中, push 参数的这一步,那么我们在里面使用的线程结构体是从哪来的呢,当 ret 之后我们的线程堆栈是这样的:
eax
edx
ecx
ebx
esi
edi
ebp
ThreadStartUp
esp -> 7
lpParameter
来看下 ThreadStartUp 函数的反汇编:
void ThreadStartUp(PIM_SWITCH_THREAD pSwitchThread)
{
00882A90 push ebp
00882A91 mov ebp,esp
00882A93 sub esp,0C0h
00882A99 push ebx
00882A9A push esi
00882A9B push edi
00882A9C lea edi,[ebp-0C0h]
00882AA2 mov ecx,30h
00882AA7 mov eax,0CCCCCCCCh
00882AAC rep stos dword ptr es:[edi]
00882AAE mov ecx,offset _3F265073_ThreadSwitch@cpp (089805Ch)
00882AB3 call @__CheckForDebuggerJustMyCode@4 (08813ACh)
ImThreadList_My[iCurrentThreadIndex].iFlage = THREAD_CREAD;
00882AB8 mov eax,dword ptr [iCurrentThreadIndex (0894138h)]
00882ABD shl eax,5
00882AC0 mov dword ptr [eax+88C13Ch],1
pSwitchThread->func(pSwitchThread->lpParameter);
00882ACA mov esi,esp
00882ACC mov eax,dword ptr [ebp - 8] \\有时候传结构体回传指针即 [pSwitchThread],需要另外改
00882ACF mov ecx,dword ptr [eax+18h]
00882AD2 push ecx
00882AD3 mov edx,dword ptr [pSwitchThread]
00882AD6 mov eax,dword ptr [edx+1Ch]
00882AD9 call eax
00882ADB add esp,4
00882ADE cmp esi,esp
00882AE0 call __RTC_CheckEsp (08812B2h)
ExecuteThread();
00882AE5 call ExecuteThread (08813DEh)
}
根据:
00882ACC mov eax,dword ptr [ebp-8]
00882ACF mov ecx,dword ptr [eax+18h]
[ebp-8] 的位置刚好是我们 PIM_SWITCH_THREAD (线程结构体)的位置,刚进函数时 push 了 ebp, 所以此时堆栈为:
eax
edx
ecx
ebx
esi
edi
ebp
esp->ThreadStartUp
7
lpParameter
所以 [ebp-8] 的位置刚好是我们 PIM_SWITCH_THREAD (线程结构体)的位置。添加的 7 主要是用来平衡堆栈的。
ThreadSwitch.h
#include <windows.h>
#define THREAD_MAX_COUNT 1024 //线程最大数量
#define STACK_SIZE 0x8000 //堆栈大小
#define THREAD_CREAD 1 //创建flage
#define THREAD_EXECUTE 2 //执行
//线程结构体(_EHREAD)
typedef struct _IM_SWITCH_THREAD
{
char* name; //线程名
int iFlage; //线程状态
int SleepTime; //休眠时间
void* InitialStack; //线程堆栈起始的位置
void* StackLimit; //线程堆栈界限
void* KernelStack; //线程堆栈当前的位置,即ESP
void* lpParameter; //线程函数的参数
void (*func)(void* lpParameterFunction); //线程函数
}IM_SWITCH_THREAD, *PIM_SWITCH_THREAD;
//保存线程数组
static IM_SWITCH_THREAD ImThreadList_My[THREAD_MAX_COUNT] = { 0 };
static int iCurrentThreadIndex = 0;
int ImCreadThreadFunc(char* name, void (*func)(void* lpParameterFunction), void* lpParameter);
void initImThread(PIM_SWITCH_THREAD ImThreadInfo, char* name, void (*func)(void* lpParameterFunction), void* lpParameter);
void PushStack(unsigned int** iStackpp, unsigned int iVale);
void ExecuteThread();
//__declspec(naked) void SwitchThreadContext(PIM_SWITCH_THREAD pCurrentThread, PIM_SWITCH_THREAD pNewThread);
void ThreadStartUp(PIM_SWITCH_THREAD pSwitchThread);
ThreadSwitch.cpp
#include "ThreadSwitch.h"
void PushStack(unsigned int **iStackpp, unsigned int iVale)
{
*iStackpp -= 1;
**iStackpp = iVale;
}
__declspec(naked) void SwitchThreadContext(PIM_SWITCH_THREAD pCurrentThread, PIM_SWITCH_THREAD pNewThread)
{
_asm
{
push ebp
mov ebp, esp //提升堆栈
push edi
push esi
push ebx
push ecx
push edx
push eax
mov esi, pCurrentThread //当前线程结构体指针
mov edi, pNewThread //要切换线程指针
mov[esi + IM_SWITCH_THREAD.KernelStack], esp //保存当前堆栈栈顶
//------------------线程切换------------------------------
mov esp, [edi + IM_SWITCH_THREAD.KernelStack]
pop eax
pop edx
pop ecx
pop ebx
pop esi
pop edi
pop ebp
ret //call startUp
}
}
//初始化线程堆栈,创建线程
int ImCreadThreadFunc(char* name, void (*func)(void* lpParameterFunction), void* lpParameter)
{
int i = 1;
for (; ImThreadList_My[i].name; i++)
{
if (_stricmp(ImThreadList_My[i].name, name) == 0)
{
break;
}
}
//初始化线程堆栈
initImThread(&ImThreadList_My[i], name, func, lpParameter);
return i;
}
//初始化堆栈
void initImThread(PIM_SWITCH_THREAD ImThreadInfo, char* name, void (*func)(void* lpParameterFunction), void* lpParameter)
{
unsigned char* StackPages;
unsigned int* StackDwordParam;
//申请空间用来保存线程栈(申请物理页)
StackPages = (unsigned char*)VirtualAlloc(NULL, STACK_SIZE, MEM_COMMIT, PAGE_READWRITE);
//初始化内存
memset(StackPages, 0, STACK_SIZE);
//填充结构体
ImThreadInfo->iFlage = THREAD_CREAD;
ImThreadInfo->InitialStack = StackPages + STACK_SIZE;
ImThreadInfo->StackLimit = StackPages;
ImThreadInfo->name = name;
ImThreadInfo->func = func;
ImThreadInfo->lpParameter = lpParameter;
StackDwordParam = (unsigned int*)(StackPages + STACK_SIZE);
//入栈
PushStack(&StackDwordParam, (unsigned int)lpParameter); //压入函数参数
PushStack(&StackDwordParam, (unsigned int)7); //随便写一个参数,用来平衡堆栈
PushStack(&StackDwordParam, (unsigned int)ThreadStartUp); //线程入口
PushStack(&StackDwordParam, (unsigned int)0); //push ebp
PushStack(&StackDwordParam, (unsigned int)0); //push edi
PushStack(&StackDwordParam, (unsigned int)0); //push esi
PushStack(&StackDwordParam, (unsigned int)0); //push ebx
PushStack(&StackDwordParam, (unsigned int)0); //push ecx
PushStack(&StackDwordParam, (unsigned int)0); //push edx
PushStack(&StackDwordParam, (unsigned int)0); //push eax
ImThreadInfo->KernelStack = StackDwordParam; //栈顶
return;
}
//执行线程
void ExecuteThread()
{
//GetTickCount
int i = 1;
PIM_SWITCH_THREAD pCurrentThread = NULL; //当前执行线程
PIM_SWITCH_THREAD pNewThread = NULL; //当前执行线程
pCurrentThread = &ImThreadList_My[iCurrentThreadIndex];
for (; ImThreadList_My[i].name; i++)
{
if (ImThreadList_My[i].iFlage == THREAD_CREAD)
{
ImThreadList_My[i].iFlage = THREAD_EXECUTE;
iCurrentThreadIndex = i;
break;
}
}
pNewThread = &ImThreadList_My[i];
//切换线程
SwitchThreadContext(pCurrentThread, pNewThread);
return;
}
void ThreadStartUp(PIM_SWITCH_THREAD pSwitchThread)
{
ImThreadList_My[iCurrentThreadIndex].iFlage = THREAD_CREAD;
pSwitchThread->func(pSwitchThread->lpParameter);
ExecuteThread();
}
main.cpp
#include <iostream>
#include <windows.h>
#include "ThreadSwitch.h"
using namespace std;
void Thread1(void* pVale)
{
cout << "------------Thread1-----------------" << endl;
ExecuteThread();
}
void Thread2(void* pVale)
{
cout << "------------Thread2-----------------" << endl;
ExecuteThread();
}
void Thread3(void* pVale)
{
cout << "------------Thread3-----------------" << endl;
ExecuteThread();
}
int main()
{
const char* thread1 = "Thread1";
const char* thread2 = "Thread2";
const char* thread3 = "Thread3";
ImCreadThreadFunc((char*)thread1, Thread1, NULL);
ImCreadThreadFunc((char*)thread2, Thread2, NULL);
ImCreadThreadFunc((char*)thread3, Thread3, NULL);
ExecuteThread();
/*
while (TRUE)
{
Sleep(20);
ExecuteThread();
}
*/
}