滴水逆向笔记系列-win32总结7-57.进程创建-58.挂起方式创建进程

第五十七课 win32 进程创建

1.进程创建的过程

父进程创建子进程,父进程挂了子进程不会挂

0x00 程序、imagebuffer、进程的关系

程序就是一个普通的二进制文件;imagebuffer就是程序拉伸到内存后的二进制文件,但是没有线程去执行他,进程则是有线程去运行这个imagebuffer

0x01 过程

image.png

image.png

0x02 CreateProcess做了什么

BOOL CreateProcess(								
  LPCTSTR lpApplicationName,                 // name of executable module								
  LPTSTR lpCommandLine,                      // command line string								
  LPSECURITY_ATTRIBUTES lpProcessAttributes, // SD								
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // SD								
  BOOL bInheritHandles,                      // handle inheritance option								
  DWORD dwCreationFlags,                     // creation flags								
  LPVOID lpEnvironment,                      // new environment block								
  LPCTSTR lpCurrentDirectory,                // current directory name								
  LPSTARTUPINFO lpStartupInfo,               // startup information								
  LPPROCESS_INFORMATION lpProcessInformation // process information								
);	

a.创建内核对象

image.png
句柄表:第一列就是句柄,相当于内核对象地址的编号,第二列就是内核对象的真正地址,第三列表示是否可继承

b.分配4GB虚拟内存空间

image.png

c.创建进程的主线程

image.png

2.示例代码

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); 	
    
    printf("%x %x %x %x\n",pi.dwProcessId,pi.dwThreadId,pi.hProcess,pi.hThread);
}					

STARTUPINFO结构(倒数第二参数)

用来设定要创建的应用程序的属性,比如可以指定新创建的控制台程序的标题等待。
一般情况,只要为第一个成员赋值就可以了.
si.cb = sizeof(si);

typedef struct _STARTUPINFO			
{			
   DWORD cb;			
   PSTR lpReserved;			
   PSTR lpDesktop;			
   PSTR lpTitle;			
   DWORD dwX;			
   DWORD dwY;			
   DWORD dwXSize;			
   DWORD dwYSize;			
   DWORD dwXCountChars;			
   DWORD dwYCountChars;			
   DWORD dwFillAttribute;			
   DWORD dwFlags;			
   WORD wShowWindow;			
   WORD cbReserved2;			
   PBYTE lpReserved2;			
   HANDLE hStdInput;			
   HANDLE hStdOutput;			
   HANDLE hStdError;			
} STARTUPINFO, *LPSTARTUPINFO;			

PROCESS_INFORMATION结构(倒数第一参数)

typedef struct _PROCESS_INFORMATION					
{					
   HANDLE hProcess;				//进程句柄	
   HANDLE hThread;				//主线程句柄	
   DWORD dwProcessId;				//进程ID	
   DWORD dwThreadId;				//线程ID	
} PROCESS_INFORMATION;

关于句柄和ID

1、都是系统分配的一个编号,句柄是客户程序使用 ID主要是系统调度时使用.
2、调用CloseHandle关闭进程或者线程句柄的时候,只是让内核计数器减少一个,并不是终止进程或者线程.进程或线程将继续运行,直到它自己终止运行。
3、进程ID与线程ID 是不可能相同。但不要通过进程或者线程的ID来操作进程或者线程,因为,这个编号是会重复使用的,也就是说,当你通过ID=100这个编号去访问一个进程的时候,它已经结束了,而且系统将这个编号赋给了另外一个进程或者线程.

3.进程终止

终止进程的三种方式:

1、VOID ExitProcess(UINT fuExitCode)							//进程自己调用			
										
2、BOOL TerminateProcess(HANDLE hProcess, UINT fuExitCode);		//终止其他进程			
										
3、ExitThread													//终止进程中的所有线程,进程也会终止			

获取进程的退出码:

BOOL GetExitCodeProcess(HANDLE hProcess,PDWORD pdwExitCode);

进程终止时相关操作:

1、进程中剩余的所有线程全部终止运行
2、进程指定的所有用户对象均被释放,所有内核对象均被关闭
3、进程内核对象的状态变成收到通知的状态
4、进程内核对象的使用计数递减1

4.句柄的继承

让不同进程间拥有相同的内核对象
进程A的代码

#include <windows.h>
#include <stdio.h>

int main(int argc,char* argv[])
{
	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,
		CREATE_NEW_CONSOLE,
		NULL,
		NULL, &si, &pi);


	//设置事件为已通知							
	SetEvent(g_hEvent);

	//关闭句柄 内核对象是否会被销毁?							
	CloseHandle(g_hEvent);
	return 0;
}

进程B的代码

#include <windows.h>
#include <stdio.h>

int main(int argc,char* argv[])
{
    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();				
}

这是直接运行进程A的exe的结果,他会直接执行进程B z2.exe,因为进程A创建子进程并且使用命令C:/z2.exe,所以z2.exe才会被执行
image.png
如果在进程A的SetEvent(g_hEvent);打个断点
image.png
这就是最终想演示的结果,跨进程共用一个内核对象
image.png

总结

HANDLE g_hEvent = CreateEvent(&sa, TRUE, FALSE, NULL);表示创建一个可以被继承的对象,重点在第一个参数
BOOL res = CreateProcess(NULL, szBuffer, NULL, NULL, TRUE,CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);表示创建子进程,并设置子进程可以进程父进程的句柄表
一个是表示可以被继承(通过句柄表的第三列值0和1表示是否可以被继承)
一个是要不要继承,要的话在创建子进程的时候就会把可以继承的内核对象复制一份句柄表
image.png
image.png
image.png

第五十八课 win32 挂起方式创建进程

1.继承进程句柄和线程句柄(第三四个参数)

示例代码

进程A代码:

#include <windows.h>
#include <stdio.h>

int main(int argc,char* argv[])
{
    char szBuffer[256] = {0};								
    char szHandle[8] = {0};								
    								
    SECURITY_ATTRIBUTES ie_sa_p;								
    ie_sa_p.nLength = sizeof(ie_sa_p);								
    ie_sa_p.lpSecurityDescriptor = NULL;								
    ie_sa_p.bInheritHandle = TRUE; 								
    								
    SECURITY_ATTRIBUTES ie_sa_t;								
    ie_sa_t.nLength = sizeof(ie_sa_t);								
    ie_sa_t.lpSecurityDescriptor = NULL;								
    ie_sa_t.bInheritHandle = TRUE; 								
    //创建一个可以被继承的内核对象,此处是个进程								
    STARTUPINFO ie_si = {0};   								
    PROCESS_INFORMATION ie_pi;								
    ie_si.cb = sizeof(ie_si);								
    								
    TCHAR szCmdline[] =TEXT("c://program files//internet explorer//iexplore.exe");								
    CreateProcess(								
    	NULL, 							
    	szCmdline, 							
    	&ie_sa_p, 							
    	&ie_sa_t, 							
    	TRUE, 							
    	CREATE_NEW_CONSOLE, 							
    	NULL, 							
    	NULL, &ie_si, &ie_pi); 							
    								
    //组织命令行参数								
    sprintf(szHandle,"%x %x",ie_pi.hProcess,ie_pi.hThread);								
    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, 							
    	CREATE_NEW_CONSOLE, 							
    	NULL, 							
    	NULL, &si, &pi); 							
}

进程B代码

#include <windows.h>
#include <stdio.h>

int main(int argc,char* argv[])
{
    DWORD dwProcessHandle = -1;						
    DWORD dwThreadHandle = -1;						
    char szBuffer[256] = {0};
    						
    memcpy(szBuffer,argv[1],8);						
    sscanf(szBuffer,"%x",&dwProcessHandle);						
    						
    memset(szBuffer,0,256);						
    memcpy(szBuffer,argv[2],8);						
    sscanf(szBuffer,"%x",&dwThreadHandle);						
    						
    printf("获取IE进程、主线程句柄\n");						
    Sleep(2000);						
    //挂起主线程						
    printf("挂起主线程\n");						
    ::SuspendThread((HANDLE)dwThreadHandle);						
    						
    Sleep(5000);						
    						
    //恢复主线程						
    ::ResumeThread((HANDLE)dwThreadHandle);						
    printf("恢复主线程\n");						
    						
    Sleep(5000);						
    						
    //关闭ID进程						
    ::TerminateProcess((HANDLE)dwProcessHandle,1);						
    ::WaitForSingleObject((HANDLE)dwProcessHandle, INFINITE);						
    						
    printf("ID进程已经关闭.....\n");	
}

实现功能

在A进程中创建一个进程(比如浏览器进程IE),并设定该子进程的进程内核句柄与主线程内核句柄为可继承
在A进程中再创建一个进程B,在B中对IE进程控制

代码解释

进程A:

  • CreateProcess(NULL, szCmdline, &ie_sa_p, &ie_sa_t, FALSE, CREATE_NEW_CONSOLE, NULL,NULL, &ie_si, &ie_pi); 创建IE浏览器子进程,&ie_sa_p, &ie_sa_t表示进程句柄和线程句柄均可被继承,在句柄表里大概是这样子image.png,然后FALSE表示ie子进程不想继承进程A的句柄表
  • 再通过这两段代码sprintf(szHandle,"%x %x",ie_pi.hProcess,ie_pi.hThread); sprintf(szBuffer,"C:/z2.exe %s",szHandle);拼接好进程B想运行的命令,其实就是想把IE浏览器的进程句柄和线程句柄传给进程B,用命令写出来后进程B可以通过argv[1]接收到IE浏览器的进程句柄和线程句柄
  • 最后创建进程BCreateProcess(NULL, szBuffer, NULL,NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);TRUE表示进程B想要继承进程A的句柄表,

个人疑问:后面进程B是可以控制IE浏览器的,但是我们只继承了进程A的句柄表,难道IE浏览器进程被创建时&ie_sa_p, &ie_sa_t这样设置后,IE浏览器的进程句柄和线程句柄就写入了进程A的句柄表了吗
进程B:

  • 这段代码是为了接收IE浏览器的进程句柄和线程句柄memcpy(szBuffer,argv[1],8); scanf(szBuffer,"%x",&dwProcessHandle);
  • 后面的挂起主线程时,十秒内IE浏览器就是被挂起状态,我们可以看到十秒内浏览器是不能动的,恢复后的十秒内则可以,接着十秒后IE浏览器进程将被进程B关闭,由此可以看到进程B通过继承过来的IE浏览器的进程句柄和线程句柄在操控IE浏览器

2.lpCurrentDirectory(第六个参数)

示例代码

打印当前exe的目录
image.png
第三个参数如果为NULL则是父进程的目录,如果是C:\,则打印C:<br />image.png

3.dwCreationFlags(第六参数)

当他为CREATE_NEW_CONSOLE时,子进程会有自己的控制台窗口,NULL则反之

4.以CREATE_SUSPENDED挂起方式创建进程

以挂起的方式创建进程,获取进程的ImageBase和AddressOfEntryPoint

STARTUPINFO ie_si = {0};   							
PROCESS_INFORMATION ie_pi;							
ie_si.cb = sizeof(ie_si);							
							
//以挂起的方式创建进程							
TCHAR szBuffer[256] = "C:\\ipmsg.exe";							
CreateProcess(							
	NULL,                    // name of executable module						
	szBuffer,                // command line string						
	NULL, 					 // SD	
	NULL,  		             // SD				
	FALSE,                   // handle inheritance option						
	CREATE_SUSPENDED,     	 // creation flags  					
	NULL,                    // new environment block						
	NULL,                    // current directory name						
	&ie_si,                  // startup information						
	&ie_pi                   // process information						
	);						
							
							
CONTEXT contx;  							
contx.ContextFlags = CONTEXT_FULL;  												
GetThreadContext(ie_pi.hThread, &contx);							
							
//获取入口点							
DWORD dwEntryPoint = contx.Eax;							
							
//获取ImageBase							
char* baseAddress = (CHAR *) contx.Ebx+8;							
							
memset(szBuffer,0,256);							
							
ReadProcessMemory(ie_pi.hProcess,baseAddress,szBuffer,4,NULL);							
							
							
ResumeThread(ie_pi.hThread);							
			

posted @ 2024-03-17 17:14  小新07  阅读(50)  评论(0编辑  收藏  举报