以挂起方式创建进程

1.进程的继承
创建进程的函数:BOOL CreateProcess(                                
  LPCTSTR lpApplicationName,                 // 创建进程时打开的exe文件名                               
  LPTSTR lpCommandLine,                      // 创建进程时的命令行参数                               
  LPSECURITY_ATTRIBUTES lpProcessAttributes, // 安全属性,可用来设置该进程句柄是否可继承                                
  LPSECURITY_ATTRIBUTES lpThreadAttributes,  // 可用来设置进程的主线程句柄是否可继承                                
  BOOL bInheritHandles,                      // 是否继承父进程的句柄表                               
  DWORD dwCreationFlags,                     // creation flags                                
  LPVOID lpEnvironment,                      // new environment block                                
  LPCTSTR lpCurrentDirectory,                // 进程的当前目录                                
  LPSTARTUPINFO lpStartupInfo,               // 程序状态设置                                
  LPPROCESS_INFORMATION lpProcessInformation // out参数进程信息                                
);                        
lpProcessAttributes    ->安全属性,用来设定进程是否能被继承;一个SECURITY_ATTRIBUTES结构的指针,第三个成员为TRUE可继承;
lpThreadAttributes    ->安全属性,线程能否被继承;SECURITY_ATTRIBUTES结构指针,第三个成员为TRUE可继承;
dwCreationFlags        ->创建标记;如果是控制台程序,CREATE_NEW_CONSOLE表示子进程和父进程都有自己的控制台窗口;为NULL则子进程会把信息打印到父进程的控制台;
lpCurrentDirectory    ->进程的当前目录;如果为NULL,子进程的当前目录将和父进程一样;
                                        如果想要当前目录为子进程exe文件所在目录,则需要用字符串指定;
 
获取进程当前目录:
char szBuffer[256] = {0};
GetCurrentDirectory(256,szBuffer);
printf("%s\n",szBuffer);

 

当在进程A中CreateProcess创建一个子进程B时,进程A的句柄表中会多有两个条记录,一个是进程B的内核对象句柄,一个是B主线程的内核对象句柄;   
如果lpProcessAttributes、lpThreadAttributes都为NULL;则两个句柄都不可继承,也就是可继承状态为0;
进程A句柄表:
此时如果在进程A中创建一个子进程C,那么C将不能继承A的进程内核和线程内核句柄;
但如果将进程B设为可继承,那么C可以从A的句柄表中继承到B的进程句柄和主线程句柄;
利用这一特点可以在进程C中空值进程B;
 
1)目标
在A进程中创建一个进程(比如浏览器进程IE),并设定该子进程的进程内核句柄与主线程内核句柄为可继承                                
在A进程中再创建一个进程B,在B中对IE进程控制    
 
2)进程B
#include<stdio.h>
#include<windows.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");                        
    return 0;
}
 
3)进程A
#include<stdio.h>
#include<windows.h>
 
int main(int argc, char* argv[]){
    char szHandle[8] = {0};    
    char szBuffer[256] = {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,"D:/create_child.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);                         
                                                
    return 0;
}
 
4)结果
运行进程A,ie浏览器被打开;然后被进程B结束,浏览器关闭;
 
2.以挂起的方式创建进程
用CreateProcess函数可以创建一个进程;
正常情况下CreateProcess做以下事情:创建一个进程的内核对象、给进程分配一个4GB的空间、加载pe:包括exe、dll、修复dll、创建主线程,把程序入口地址交给EIP
 
如果参数dwCreationFlags传入CREATE_SUSPENDED将以挂起的方式创建进程;
以这种方式创建的进程只有一个壳;也就是进程的主线程没有开始运行;
需要调用函数让进程的主线程恢复执行:
ResumeThread(ie_pi.hThread);
 
1)已挂起方式打开进程实例
例如:以挂起方式打开notpad++
#include<stdio.h>
#include<windows.h>
 
int main(int argc, char* argv[]){
    TCHAR szAppName[256] = TEXT("D:\\Program Files\\Notepad++\\notepad++.exe");
    STARTUPINFO si = {0};    //程序启动设置
    si.cb = sizeof(si);    //只需要传递结构大小即可
    PROCESS_INFORMATION pi;    //记录进程句柄信息等
    ::CreateProcess(
        NULL,
        szAppName,
        NULL,
        NULL,
        FALSE,
        CREATE_SUSPENDED,
        NULL,
        NULL,
        &si,
        &pi
    );
    //恢复主进程
    //::ResumeThread(pi.hThread);
    return 0;
}
当没有恢复主进程时,notepad++没有运行;
但任务管理器中可以看到notpad++的进程,只不过占内存比正常运行小,因为主线程根本没运行;
 
当调用ResumeThread恢复主进程时,notepad++正常运行
 
2)关于加壳
挂起方式创建的进程之是一个外壳;
我们可以修改外壳程序的内容;
这意味着在程序调用ResumeThread恢复执行的时候,外壳还是notepad++,但里面的内容完全变了;
可以将自己的程序拉伸为镜像文件,然后替换外壳程序,接下来恢复执行时执行的就是自己的程序了;
这就是所谓的加壳;
 
想以这种借助其它程序的外壳来执行程序时,必须将外壳进程的主线程入口和ImageBase给替换掉;
入口地址=程序的镜像基址ImageBase + 程序的入口函数地址OEP;
 
因为外壳程序已挂起的方式运行的;
也就是说外壳主线程是挂起的,可以得到主线程上下文对象CONTEXT;
CONTEXT contx;  
contx.ContextFlags = CONTEXT_FULL;      //CONTEXT_FULL表示除了寄存器的值,还要得到其他信息
GetThreadContext(pi.hThread, &contx);    //第一个参数为主线程句柄,主线程句柄在进程创建后会存在PROCESS_INFORMATION结构中;

 

然后就可以通过CONTEXT结构获取程序入口点
//获取入口点
DWORD dwEntryPoint = contx.Eax;    //eax中存储的入口点并不一定是ImageBase+oep,而是真正的入口点;
                                   //当程序不是占据ImageBase时,入口点将不等于ImageBase+oep;
//获取ImageBase
char* baseAddress = (CHAR *) contx.Ebx+8;    //这里的baseAddress不能直接输出
这里获取的baseAddress并不是想要的ImageBase,而是ImageBase存在哪里;
但父进程并不能直接通过这个地址找到ImageBase,因为这是子进程中的地址;
需要用到进程间读取的函数:ReadProcessMemory;
5个参数依次为:进程句柄、从这进程的什么位置开始读、读的东西放到哪里、读多少个字节、(out参数)真正读了多少个字节;
TCHAR szBuffer[256];
memset(szBuffer,0,256);
ReadProcessMemory(pi.hProcess,baseAddress,szBuffer,4,NULL);
这样就得到了需要修改的外壳进程的入口点和ImageBase;
接下来想借壳上市只需要做相应的修改即可;  
 
 
 
 
 
posted @ 2020-01-10 10:53  L丶银甲闪闪  阅读(1213)  评论(0编辑  收藏  举报