干貨|Windows下進程操作的一些C++代碼
0x01 進程遍歷
因為進程是在隨時進行變動的所以我們需要獲取一張快照
1.1 CreateToolhelp32Snapshot
HANDLE CreateToolhelp32Snapshot( DWORD dwFlags, DWORD th32ProcessID );
因為要獲取進程第一個參數選擇TH32CS_SNAPPROCESS來獲取系統中所有的進程,具體可以參考[CreateToolhelp32Snapshot]:https://docs.microsoft.com/zh-cn/windows/win32/api/tlhelp32/nf-tlhelp32-createtoolhelp32snapshot?f1url=%3FappId%3DDev16IDEF1%26l%3DZH-CN%26k%3Dk(TLHELP32%252FCreateToolhelp32Snapshot);k(CreateToolhelp32Snapshot);k(DevLang-C%252B%252B);k(TargetOS-Windows)%26rd%3Dtrue
1.2 Process32First
BOOL Process32First( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
第一個參數使用上面CreateToolhelp32Snapshot函數返回的句柄。第二個參數執行了PROCESSENTRY32結構的指針,它包含了進程信息。檢索進程里的第一個進程信息。
1.2.1 PROCESSENTRY32
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
CHAR szExeFile[MAX_PATH];
} PROCESSENTRY32;
使用時候要把結構體清零。szExeFile為進程名稱,其他都根據名稱一樣。
1.3 Process32Next
BOOL Process32Next( HANDLE hSnapshot, LPPROCESSENTRY32 lppe );
檢索快照中的下一個進程信息。
void ListProcess() {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
BOOL bRet = Process32First(hSnap, &pe32);
while (bRet)
{
bRet = Process32Next(hSnap, &pe32);
printf(pe32.szExeFile);
pid = pe32.th32ProcessID;
wprintf(L"\r");
printf("pid:%d", pe32.th32ProcessID);
printf("\r-----------------------------------------");
wprintf(L"\r");
}
::CloseHandle(hSnap);
}

0x02 模塊遍歷
同理只需要將CreateToolhelp32Snapshot的dwFlags修改為TH32CS_SNAPMODULE,th32ProcessID參數為進程的pid,這里要先獲取進程pid。
2.1 獲取進程pid
typedef struct tagPROCESSENTRY32 {
DWORD dwSize;
DWORD cntUsage;
DWORD th32ProcessID;
ULONG_PTR th32DefaultHeapID;
DWORD th32ModuleID;
DWORD cntThreads;
DWORD th32ParentProcessID;
LONG pcPriClassBase;
DWORD dwFlags;
CHAR szExeFile[MAX_PATH];
} PROCESSENTRY32;
通過PROCESSENTRY32結構體的th32ProcessID來獲取進程pid。遍歷進程通過strcmp匹配到我們的進程名就返回the32ProcessID。
int GetProcessPid(char* ProcessName) {
Flag = CheckPorcess(ProcessName);
if (Flag) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
BOOL bRet = Process32First(hSnap, &pe32);
while (bRet)
{
bRet = Process32Next(hSnap, &pe32);
if (strcmp(pe32.szExeFile, ProcessName) == 0) {
pid = pe32.th32ProcessID;
}
}
CloseHandle(hSnap);
return pid;
}
printf("[-]Process not found");
return 0;
}
2.2 模塊遍歷
獲取到了進程pid,放入CreateToolhelp32Snapshot第二個參數,但是因為考慮到進程可能根本不存在所以寫一個CheckPorcess方法來判斷是否存在該進程。
BOOL CheckPorcess(char* ParanetProcessName) {
DWORD i = 0;
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
BOOL bRet = Process32First(hSnap, &pe32);
while (bRet)
{
bRet = Process32Next(hSnap, &pe32);
if (strcmp(pe32.szExeFile, ParanetProcessName) == 0) {
CloseHandle(hSnap);
return TRUE;
}
}
return FALSE;
}
然后遍歷模塊
BOOL ListModule(char* ProcessName) {
Flag = CheckPorcess(ProcessName);
if (Flag) {
pid = GetProcessPid(ProcessName);
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
MODULEENTRY32 me32 = { 0 };
me32.dwSize = sizeof(MODULEENTRY32);
BOOL bRet = Module32First(hSnap, &me32);
while (bRet)
{
bRet = Module32Next(hSnap, &me32);
printf(me32.szExePath);
printf("\r");
}
CloseHandle(hSnap);
return TRUE;
}
printf("[-]Process not found");
return FALSE;
}

0x03 遍歷線程
使用TH32CS_SNAPTHREAD參數來獲取,這里都大同小異了。
BOOL ListThread(char* ProcessName) {
Flag = CheckPorcess(ProcessName);
if (Flag) {
pid = GetProcessPid(ProcessName);
THREADENTRY32 te32 = { 0 };
te32.dwSize = sizeof(THREADENTRY32);
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, pid);
BOOL bRet = Thread32First(hSnap, &te32);
while (bRet)
{
Thread32Next(hSnap, &te32);
if (te32.th32OwnerProcessID == pid) {
printf(TEXT(" THREAD ID = 0x%08X"), te32.th32ThreadID);
printf(TEXT(" base priority = %d"), te32.tpBasePri);
printf(TEXT(" delta priority = %d"), te32.tpDeltaPri);
break;
}
}
CloseHandle(hSnap);
return TRUE;
}
printf("[-]Process not found");
return FALSE;
}
通過te32.th32OwnerProcessID與GetProcessPid方法獲取到的pid來對比進而確定當前進程。
0x04 干進程
4.1 TerminateProcess
BOOL TerminateProcess( HANDLE hProcess, UINT uExitCode );
第一個參數為要結束進程的進程句柄,第二個參數為終止代碼。
4.2 OpenProcess
HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId );
第一個參數為進程訪問權限這里設置為擁有全部權限PROCESS_ALL_ACCESS,具體查看[進程訪問權限]:https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights 第二個參數為是否要繼承句柄。第三個參數為進程pid。
BOOL KillProcess(char* ProcessName) {
Flag = CheckPorcess(ProcessName);
if (Flag) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
BOOL bRet = Process32First(hSnap, &pe32);
while (bRet)
{
bRet = Process32Next(hSnap, &pe32);
if (strcmp(pe32.szExeFile, ProcessName) == 0) {
pid = pe32.th32ProcessID;
}
}
HANDLE hProcessName = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
bRet = TerminateProcess(hProcessName, 0);
if (bRet) {
printf("Kill %s success!", pe32.szExeFile);
}
else
{
printf("Kill %s fail!", pe32.szExeFile);
}
CloseHandle(hSnap);
return TRUE;
}
printf("[-]Process not found");
return FALSE;
}
0x05 dll注入
dll加載:1.靜態調用:通過在我們的程序中添加頭文件,以及lib文件來完成調用,前提就是獲取dll然后還有頭文件 2.動態調用:僅僅只需要一個dll即可完成調用
這里寫一個Test方法
#include
__declspec(dllexport) void Test(){
MessageBox(NULL, NULL, NULL, NULL);
}

可以看到有一些臟數據。這里可以協商約定來解決 1.__stdcall 標準 棧傳參,函數內部(被調用者)平棧 2. __cdecl c 棧傳參,函數外部(調用者)平棧 3. __fastcall 快速 寄存器傳參 4. __thiscall 類的thiscall調用約定,使用ecx寄存器來傳遞this指針
extern "C"{
__declspec(dllexport) void __stdcall Test(){
MessageBox(NULL, NULL, NULL, NULL);
}
}
__stdcall是函數內部平參可以舉個例子
void __stdcall test(int n1, int n2){
return;
}
int main()
{
test(1, 2);
return 0;
}
兩個返回8一個返回4
void __stdcall test(int n1, int n2){
002013C0 push ebp
002013C1 mov ebp,esp
002013C3 sub esp,0C0h
002013C9 push ebx
002013CA push esi
002013CB push edi
002013CC lea edi,[ebp-0C0h]
002013D2 mov ecx,30h
002013D7 mov eax,0CCCCCCCCh
002013DC rep stos dword ptr es:[edi]
return;
}
002013DE pop edi
002013DF pop esi
002013E0 pop ebx
002013E1 mov esp,ebp
002013E3 pop ebp
002013E4 ret 8
5.1 動態調用
這里使用動態調用
流程大概就是 1.在目標進程中申請內存 2.向目標進程內存中寫入shellcode(沒有特征,編碼比較麻煩) 3.創建遠線程執行shellcode
首先打開進程獲取到進程句柄
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, rProcessId);
if (hProcess == INVALID_HANDLE_VALUE) {
return FALSE;
}
然后再要注入的進程中申請內存
VirtualAllocEx
LPVOID VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect );
詳情查看[VirtualAllocEx]:https://docs.microsoft.com/zh-cn/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex?f1url=%3FappId%3DDev16IDEF1%26l%3DZH-CN%26k%3Dk(MEMORYAPI%252FVirtualAllocEx);k(VirtualAllocEx);k(DevLang-C%252B%252B);k(TargetOS-Windows)%26rd%3Dtrue 這里要用到可讀可寫權限PAGE_READWRITE。
pDllAddr = VirtualAllocEx(hProcess, NULL, strlen(szDllPath) + 1, MEM_COMMIT, PAGE_READWRITE);
然后再要注入的進程里面寫入數據
WriteProcessMemory
BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten ); WriteProcessMemory(hProcess, pDllAddr, szDllPath, strlen(szDllPath) + 1, NULL);
然后獲取loadlibrary的地址后就通過CreateRemoteThread加載dll地址和函數地址來調用
pfnStartAddr = GetProcAddress(::GetModuleHandle("Kernel32"), "LoadLibraryA");
最后再使用CreateRemoteThread創建遠線程,注入DLL
if ((hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnStartAddr, pDllAddr, 0, NULL)) == NULL)
{
std::cout << "Injecting thread failed!" << std::endl;
return FALSE;
}
InjectDll函數
BOOL InjectDll(int rProcessId, const char* szDllPath) {
HANDLE hProcess = NULL;
LPVOID pDllAddr = NULL;
FARPROC pfnStartAddr = NULL;
HANDLE hRemoteThread = NULL;
//打開進程
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, rProcessId);
if (hProcess == INVALID_HANDLE_VALUE) {
return FALSE;
}
//在要注入的進程中申請內存
pDllAddr = VirtualAllocEx(hProcess, NULL, strlen(szDllPath) + 1, MEM_COMMIT, PAGE_READWRITE);
if (!pDllAddr)
{
return FALSE;
}
//給要注入的進程中寫入數據
WriteProcessMemory(hProcess, pDllAddr, szDllPath, strlen(szDllPath) + 1, NULL);
//獲取LoadLibraryA函數的地址
pfnStartAddr = GetProcAddress(::GetModuleHandle("Kernel32"), "LoadLibraryA");
//使用CreateRemoteThread創建遠線程,注入DLL
if ((hRemoteThread = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)pfnStartAddr, pDllAddr, 0, NULL)) == NULL)
{
std::cout << "Injecting thread failed!" << std::endl;
return FALSE;
}
/*CloseHandle(hProcess);
CloseHandle(hRemoteThread);*/
return TRUE;
}
這里我們寫一個程序加一個dll來驗證
DemoDll.dll
// dllmain.cpp : 定義 DLL 應用程序的入口點。
#include "pch.h"
#include
#include
#include
#include
int pid = 0;
CString str;
int GetCurrentID() {
pid = _getpid();
str.Format("%d", pid);
return pid;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
pid = GetCurrentID();
MessageBox(NULL, str, "title", MB_OK);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Demo.exe
#include
#include
void main() {
printf("test\r");
system("pause");
}
運行Demo.exe,然后先遍歷模塊

dll注入



0x06 PPID欺騙
PPID欺騙允許使用任意進程啟動程序
同理先判斷父進程是否存在
DWORD GetParanetProcessID(char* ParanetProcessName) {
BOOL Check = CheckPorcess(ParanetProcessName);
if (Check) {
HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 pe32 = { 0 };
pe32.dwSize = sizeof(PROCESSENTRY32);
BOOL bRet = Process32First(hSnap, &pe32);
while (bRet)
{
Process32Next(hSnap, &pe32);
if (strcmp(pe32.szExeFile, ParanetProcessName) == 0) {
ppid = pe32.th32ProcessID;
break;
}
}
CloseHandle(hSnap);
}
return ppid;
}
void PpidSpoofing(char* ProcessAddr,DWORD ParaentPid) {
STARTUPINFOEXA si = { 0 };
si.StartupInfo.cb = sizeof(STARTUPINFOEXA);
PROCESS_INFORMATION pi = { 0 };
SIZE_T SizeBuff;
HANDLE parentProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, false, ParaentPid);
ZeroMemory(&si, sizeof(STARTUPINFOEXA));
InitializeProcThreadAttributeList(NULL, 1, 0, &SizeBuff);
si.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, SizeBuff);
InitializeProcThreadAttributeList(si.lpAttributeList, 1, 0, &SizeBuff);
UpdateProcThreadAttribute(si.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parentProcessHandle, sizeof(HANDLE), NULL, NULL);
BOOL bRet = CreateProcessA(ProcessAddr, NULL, NULL, NULL, TRUE, CREATE_NEW_CONSOLE | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, reinterpret_cast<LPSTARTUPINFOA>(&si), &pi);
if (!bRet) {
printf("ProcessAddr error!");
}
CloseHandle(parentProcessHandle);
}
首先我們要獲取父進程的進程句柄然后為進程和線程創建初始化指定的屬性列表使用InitializeProcThreadAttributeList。
BOOL InitializeProcThreadAttributeList( LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, DWORD dwAttributeCount, DWORD dwFlags, PSIZE_T lpSize );
最后一個參數指定輸入時lpAttributeList緩沖區的大小。si.lpAttributeList在堆中分配一塊內存,分配的大小為前面的SizeBuff。然后再使用InitializeProcThreadAttributeList初始化進程和線程的屬性列表最后使用UpdateProcThreadAttribute函數來更新進程和線程的指定屬性,最后創建我們的進程。

