因为进程是在随时进行变动的所以我们需要获取一张快照
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
BOOL Process32First(
HANDLE hSnapshot,
LPPROCESSENTRY32 lppe
);
第一个参数使用上面CreateToolhelp32Snapshot函数返回的句柄。第二个参数执行了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为进程名称,其他都根据名称一样。
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\n");
printf("pid:%d", pe32.th32ProcessID);
printf("\r\n-----------------------------------------");
wprintf(L"\r\n");
}
::CloseHandle(hSnap);
}
同理只需要将CreateToolhelp32Snapshot的dwFlags修改为TH32CS_SNAPMODULE,th32ProcessID参数为进程的pid,这里要先获取进程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;
}
获取到了进程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\n");
}
CloseHandle(hSnap);
return TRUE;
}
printf("[-]Process not found");
return FALSE;
}
使用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("\n THREAD ID = 0x%08X"), te32.th32ThreadID);
printf(TEXT("\n base priority = %d"), te32.tpBasePri);
printf(TEXT("\n delta priority = %d"), te32.tpDeltaPri);
break;
}
}
CloseHandle(hSnap);
return TRUE;
}
printf("[-]Process not found");
return FALSE;
}
通过te32.th32OwnerProcessID与GetProcessPid方法获取到的pid来对比进而确定当前进程。
BOOL TerminateProcess(
HANDLE hProcess,
UINT uExitCode
);
第一个参数为要结束进程的进程句柄,第二个参数为终止代码。
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;
}
dll加载:1.静态调用:通过在我们的程序中添加头文件,以及lib文件来完成调用,前提就是获取dll然后还有头文件 2.动态调用:仅仅只需要一个dll即可完成调用
这里写一个Test方法
#include <Windows.h>
__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
这里使用动态调用
流程大概就是 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 <windows.h>
#include <process.h>
#include <processthreadsapi.h>
#include <atlstr.h>
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 <windows.h>
#include <stdio.h>
void main() {
printf("test\r\n");
system("pause");
}
运行Demo.exe,然后先遍历模块
dll注入
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函数来更新进程和线程的指定属性,最后创建我们的进程。
推荐阅读:
本月报名可以参加抽奖送BADUSB的优惠活动
点赞,转发,在看
原创投稿作者:11ccaab