一. 進程的創建
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
);
第一個參數也可以只傳入程序名稱自動進行檢索,但不推薦。第一個參數可以爲常量。
第二個參數不可以直接傳字符串常量,因爲系統可能會對第二個參數直接進行修改;可以通過第二個參數來創建進程,第一個賦值爲NULL,這樣可以直接打開一個網址;單獨傳參數的時候要在最前面留一個空格,否則不會識別此參數。
第三個參數:此進程的進程句柄是否可以被繼承。
第四個參數:此線程的進程句柄是否可以被繼承。
第五個參數:是否可以繼承父進程的句柄表(句柄表爲1的句柄纔可以執行)。
第六個參數:傳NULL使用父進程控制檯;傳CREATE_NEW_CONSOLE創建新的控制檯;CREATE_SUSPENDED以掛起的方式創建進程。
第八個參數:傳NULL 在子進程中調用GetCurrentDirectory函數獲取的是父進程的目錄,如果創建時添加目錄那麼GetCurrentDirectory函數獲取的就是給定的目錄。
第九個參數:
1. 用來設定要創建的應用程序的屬性,比如可以指定新創建的控制檯程序的標題等待。
2. STARTUPINFO si = { 0 }; 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;
第十個參數:Out類型參數,成功創建進程後,會返回以下結構。
typedef struct _PROCESS_INFORMATION
{
HANDLE hProcess; //進程句柄
HANDLE hThread; //主線程句柄
DWORD dwProcessId; //進程ID
DWORD dwThreadId; //線程ID
} PROCESS_INFORMATION;
創建一個進程:
int main(int argc, char* argv[])
{
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
TCHAR szApplicationName[] = TEXT(" https://www.baidu.com");
BOOL res = CreateProcess(
TEXT("C:\\Program Files\\Mozilla Firefox\\firefox.exe"),
szApplicationName,
NULL,
NULL,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL, &si, &pi);
cout << res << endl;
cout << pi.dwProcessId << endl << pi.dwThreadId << endl << pi.hProcess << endl << pi.hThread << endl;
system("pause");
return 0;
}
關於句柄和ID
1、都是系統分配的一個編號,句柄是客戶程序使用 ID主要是系統調度時使用。
2、調用CloseHandle關閉進程或者線程句柄的時候,只是讓內核計數器減少一個,並不是終止進程或者線程。進程或線程將繼續運行,直到它自己終止運行。
3、進程ID與線程ID 是不可能相同。但不要通過進程或者線程的ID來操作進程或者線程,因爲,這個編號是會重複使用的,也就是說,當你通過ID=100這個編號去訪問一個進程的時候,它已經結束了,而且系統將這個編號賦給了另外一個進程或者線程。
二. 終止進程
終止進程的三種方式:
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
三. 進程的繼承
創建內核對象時需要設置爲可繼承,這樣會在進程的句柄表中賦值爲1,這是子進程可以繼承父進程句柄表的前提。之後在子進程創建時將第五個參數設置爲TRUE即可。
另外,雖然子進程繼承可句柄,但是無法直接使用,需要父進程傳遞才能使用,下面選擇的是通過命令行參數傳遞事件句柄。
父進程: 在SetEvent處下斷點,單補觀察。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <locale.h>
#include <Windows.h>
using namespace std;
#include <atlconv.h>
int main(int argc, char* argv[])
{
CHAR szBuffer[256] = { 0 };
CHAR szHandle[8] = { 0 };
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
HANDLE g_hEvent = CreateEvent(&sa, TRUE, FALSE, NULL);
if (g_hEvent == NULL) cout << "CreateEventError" << endl;
sprintf(szHandle, "%x", g_hEvent);
sprintf(szBuffer, "D:/ZZZ.exe %s", szHandle);
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
USES_CONVERSION;
TCHAR *str = new TCHAR[256];
str = A2T(szBuffer);
BOOL res = CreateProcess(
NULL,
str,
NULL,
NULL,
TRUE,
CREATE_NEW_CONSOLE,
NULL,
NULL, &si, &pi);
if (res)
cout << "創建成功" << endl;
else
cout << "創建失敗" << endl;
SetEvent(g_hEvent);
CloseHandle(g_hEvent);
system("pause");
return 0;
}
子進程:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <locale.h>
#include <Windows.h>
using namespace std;
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();
system("pause");
return 0;
}
四. 掛起方式創建進程——進程創建的第三第四個參數
一. 創建並掛起進程
main.cpp:進程創建的第三第四個參數表示此進程的進程句柄和線程句柄是否可以被子進程繼承。內核對象都有此類安全參數,用來設置是否可以被繼承句柄。如下代碼創建一個父進程(隨意一個軟件),子進程繼承其句柄設置父進程掛起,軟件一般會顯示未響應。但測試了Firefox,掛起後仍能進行操作。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <locale.h>
#include <Windows.h>
using namespace std;
#include <atlconv.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:\\Users\\lenovo\\Desktop\\DTDebug\\DTDebug.exe");
CreateProcess(
NULL,
szCmdline,
&ie_sa_p,
&ie_sa_t,
FALSE,
CREATE_NEW_CONSOLE,
NULL,
NULL, &ie_si, &ie_pi);
//組織命令行參數
sprintf(szHandle, "%x %x", ie_pi.hProcess, ie_pi.hThread);
sprintf(szBuffer, "D:/aaa.exe %s", szHandle);
//定義創建進程需要用的結構體
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi;
si.cb = sizeof(si);
USES_CONVERSION;
TCHAR *str = new TCHAR[256];
str = A2T(szBuffer);
//創建子進程
BOOL res = CreateProcess(
NULL,
str,
NULL,
NULL,
TRUE,
CREATE_NEW_CONSOLE,
NULL,
NULL, &si, &pi);
if (res)
cout << "B創建成功" << endl;
else
cout << "B創建失敗" << endl;
system("pause");
return 0;
}
aaa.cpp:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <locale.h>
#include <Windows.h>
using namespace std;
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(1000);
//掛起主線程
printf("掛起主線程\n");
::SuspendThread((HANDLE)dwThreadHandle);
Sleep(30000);
//恢復主線程
::ResumeThread((HANDLE)dwThreadHandle);
printf("恢復主線程\n");
Sleep(5000);
//關閉ID進程
::TerminateProcess((HANDLE)dwProcessHandle, 1);
::WaitForSingleObject((HANDLE)dwProcessHandle, INFINITE);
printf("ID進程已經關閉.....\n");
system("pause");
return 0;
}
二. 以掛起的方式創建進程
int main(int argc, char* argv[])
{
STARTUPINFO ie_si = { 0 };
PROCESS_INFORMATION ie_pi;
ie_si.cb = sizeof(ie_si);
//以掛起的方式創建進程
TCHAR szBuffer[256] = TEXT("C:\\Users\\lenovo\\Desktop\\DTDebug\\DTDebug.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;
printf("%x\n", dwEntryPoint);
//獲取ImageBase
char* baseAddress = (CHAR *)contx.Ebx + 8;
memset(szBuffer, 0, 256);
ReadProcessMemory(ie_pi.hProcess, baseAddress, szBuffer, 4, NULL);
printf("%p\n", szBuffer);
ResumeThread(ie_pi.hThread);
system("pause");
return 0;
}