Windows核心程式設計_遠執行緒方式實現Dll注入
之前有介紹過HOOK的方式注入,這次介紹以其它方式注入,而無須HOOK,要知道在Windows這個浩蕩的海洋裡,API就是寶藏,找到足夠多的寶藏那麼你就是海賊王~!
實現思路如下:
首先開啟一個程序的地址空間,然後開闢一塊空間將動態庫copy進去,然後找到動態庫要呼叫的函式首地址,在呼叫此函式!
首先給大家介紹幾個dll注入的方法:
1.通過呼叫Kernel32動態庫裡的LoadLibraryW函式來載入dll到指定程序下,然後在建立遠執行緒!
這是最常用的一個方法,但是很遺憾這種方法可用性並不大,因為在Win8和Win10以上你會發現可以建立遠執行緒的程序沒有幾個,Windows核心裡已經放棄這個API了,因為這個API本身就不是ring0級別的程式碼,Windows代代更新核心之後就已經不在支援這個API了!
那麼來介紹一下實現這個方法的API:
有些API的原型我在其他文章裡已經介紹過,這裡就不重複介紹,有興趣的可以去翻一下:
1.首先第一步使用FindWindow得到視窗控制代碼,
2.在使用OpenProcess開啟視窗程序,但是需要獲取視窗的PID,
3.所以要先使用GetWindowThreadProcessId獲取PID,
4.在使用VirtualAllocEx開闢記憶體,開闢記憶體大小是要注入dll絕對路徑的長度,
5.然後在使用WriteProcessMemory函式寫入路徑字元,
6.在使用GetProcAddress獲取Kernel32裡的LoadLibraryW函式地址!
7.在使用CreateRemoteThread函式將LoadLibraryW載入到指定程序下,然後呼叫傳遞引數
在此之前需要給大家介紹一下CreateRemoteThread,GetProcAddress以及GetModuleHandle這三個API:
1.CreateRemoteThread
HANDLE WINAPI CreateRemoteThread( __in HANDLE hProcess, __in LPSECURITY_ATTRIBUTES lpThreadAttributes, __in SIZE_T dwStackSize, __in LPTHREAD_START_ROUTINE lpStartAddress, __in LPVOID lpParameter, __in DWORD dwCreationFlags, __out LPDWORD lpThreadId );
引數介紹:
hProcess [in]
執行緒所屬程序的程序控制代碼.
該控制代碼必須具有 PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE,和PROCESS_VM_READ 訪問許可權.
lpThreadAttributes [in]
一個指向 SECURITY_ATTRIBUTES 結構的指標, 該結構指定了執行緒的安全屬性.
dwStackSize [in]
執行緒初始大小,以位元組為單位,如果該值設為0,那麼使用系統預設大小.
lpStartAddress [in]
在遠端程序的地址空間中,該執行緒的執行緒函式的起始地址.
lpParameter [in]
傳給執行緒函式的引數.
dwCreationFlags [in]
執行緒的建立標誌.
值 含義
0 執行緒建立後立即執行
CREATE_SUSPENDED 執行緒建立後先將執行緒掛起,直到 ResumeThread 被呼叫.
0x00000004
STACK_SIZE_PARAM_IS_A_RESERVATION dwStackSize 引數指定為執行緒棧預訂大小,如果STACK_SIZE_PARAM_IS_A_RESERVATION沒有被指定,dwStackSize 引數指定為執行緒棧分配大小.
0x00010000
lpThreadId [out]
用來存放建立執行緒後的執行緒ID,如果執行緒建立成功,那麼執行緒ID會被存放到這個變數裡,一般不需要給NULL,如果建立失敗則給NULL!
返回值:
成功返回非0否則可以使用GetLastError獲取錯誤碼
2.GetProcAddress
FARPROC GetProcAddress(
HMODULE hModule, // DLL模組控制代碼
LPCSTR lpProcName // 函式名
);
返回值:
如果函式呼叫成功,返回值是DLL中的輸出函式地址。
如果函式呼叫失敗,返回值是NULL。得到進一步的錯誤資訊,呼叫函式GetLastError。
3.GetModuleHandle
HMODULE WINAPI GetModuleHandle(
_In_opt_LPCTSTR lpModuleName
);
lpModuleName String,指定模組名,這通常是與模組的檔名相同的一個名字。例如,NOTEPAD.EXE程式的模組檔名就叫作NOTEPAD。
返回值:
HMODULE,如執行成功成功,則返回模組控制代碼。零表示失敗。獲取錯誤資訊,請呼叫
GetLastError。
其次可以使用
來對映指定模組
實現程式碼如下:
#include "windows.h"
int main()
{
//程式的視窗控制代碼
HWND hWnd = FindWindow(NULL, "123");
if (hWnd == NULL) {
MessageBox(NULL, "無法開啟指定程序", "Error", 0);
}
//根據視窗控制代碼獲取程序PID
DWORD Process_ID = (DWORD)0;
GetWindowThreadProcessId(hWnd, &Process_ID); //PID這裡傳址
if (Process_ID == (DWORD)0) { //判斷是否與原值相同
MessageBox(NULL, "無法獲取程序PID", "Error", 0);
}
//開啟程序
HANDLE hPro = OpenProcess(PROCESS_CREATE_THREAD | //允許遠端建立執行緒
PROCESS_VM_OPERATION | //允許遠端VM操作
PROCESS_VM_WRITE,//允許遠端VM寫
FALSE, Process_ID);
if (hPro == NULL) {
MessageBox(NULL, "無法開啟程序空間", "Error", 0);
}
// 在遠端程序中為路徑名稱分配空間
char str[256] = { 0 };
strcpy_s(str,"d:\\3.dll"); //你的dll路徑
int dwSize = (lstrlenA(str)+1); //路徑大小+1是因為\0
//開闢空間
LPVOID pszLibFileRemote = (PWSTR)VirtualAllocEx(hPro, NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (pszLibFileRemote == NULL) {
MessageBox(NULL, "無法開闢記憶體!", "Error", 0);
return -1;
}
//寫入資料
DWORD dwWritten; //寫入位元組數
DWORD n = WriteProcessMemory(hPro, (LPTHREAD_START_ROUTINE)pszLibFileRemote, "d:\\4.dll", dwSize, &dwWritten);
if (n == NULL) {
MessageBox(NULL, "無法寫入記憶體!", "Error", 0);
return -1;
}
//Kernel32模組已經被包含在依賴庫裡自動載入進來了,所以我們可以直接獲取
PTHREAD_START_ROUTINE pfnThreadRtn = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(TEXT("Kernel32")), "LoadLibraryA");
if (pfnThreadRtn == NULL) {
MessageBox(NULL, "無法獲取dll庫函式", "Error", 0);
return -1;
}
//建立遠執行緒
DWORD dwID = NULL;
HANDLE hThread = CreateRemoteThread(hPro, 0, 0, pfnThreadRtn, pszLibFileRemote, 0, &dwID);
if (hThread == NULL) {
MessageBox(NULL, "無法建立遠執行緒!", "Error", 0);
printf("%d", GetLastError());
}
//等待執行緒結束
WaitForSingleObject(hThread, INFINITE);
return 0;
}
這裡有個程式博主已經寫好的了:
dll程式碼:
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
MessageBox(NULL, "wed", "wed", 0);
return TRUE;
}
執行結果:
可以看到已經成功注入到指定視窗下了,而且資訊框也是在指定視窗下彈出的!
這裡有幾個載入問題給大家解釋一下:
GetProcAddress是獲取當前模組下指定函式的地址,注意是當前程序下的,那麼為什麼CreateRemoteThread
是建立遠端函式應該給遠端函式的地址才對,為什麼是本地下的?
答:
這是Windows的一套機制,在Windows上執行的話會附加一系列的依賴庫,這些依賴庫在載入到記憶體中會被存放在
程序的空間下,但不是亂放的,是有序載入的,而且是通過模組表來記錄每個模組的記憶體位置,而這些順序在不同的作業系統下可能不同(xp以下是在登錄檔裡的,可以通過修改登錄檔的方式來載入自己的dll,但在xp以上就不可行了
xp登錄檔裝載dll方式:
開啟
HKEY_LOCAL_MACHINE\SoftWare\MicroSoft\Windows NT\CurrentVersion\Windows\
找到:
AppInit_Dlls健值,在裡面新增上你的dll絕對路徑,用“,”號分開
)
但是GetProcAddress可以很好的幫我們從模組表裡找到我們需要的地址,然後返回給我們,然後我們在給它偏移量,最後CreateRemoteThread
在建立的時候會根據控制代碼找到對應的段地址,在GDT表-LRT表裡找到對應的實體地址在偏移量相加即成為每個程式的模組函式地址!
這樣的方法僅在win7以下有效,win7以上需要提權!但Win10以後的系統程式無法注入dll!
並且呼叫LoadLibraryW函式會註冊你的dll模組到程式中的模組表裡,所以可以被第三方查詢出來是否有可疑dll注入!
當然注入方法不止這一種你可以呼叫其它的建立執行緒函式來建立遠執行緒!