如何建立自己生活网站,怎么进入公众号,网站模版 蓝色,上海单位名称大全欢迎各位添加微信号#xff1a;qinchang_198231 加入安全 交流群 和大佬们一起交流安全技术01技术概要这是一种允许攻击者从内存而非磁盘向指定进程注入DLL的技术#xff0c;该技术比常规的DLL注入更为隐蔽#xff0c;因为除了不需要磁盘上的实际DLL文件之外#xff0c;它… 欢迎各位添加微信号qinchang_198231 加入安全 交流群 和大佬们一起交流安全技术01技术概要这是一种允许攻击者从内存而非磁盘向指定进程注入DLL的技术该技术比常规的DLL注入更为隐蔽因为除了不需要磁盘上的实际DLL文件之外它也不需要任何Windows加载程序的辅助即可注入。这消除了将DLL注册为进程已加载模块的需求从而可逃脱工具的监视。首先准备好测试dll使用VS2015先编译生成一个测试dll文件作用是dll被进程附加的时候会执行MessageBox弹框切记不要选择空项目。如下在DLL_PROCESS_ATTACH添加一个消息框函数直接编译生成dll。下面会根据源码逐步分析整体执行流程示例代码是模拟内存自加载dll的过程为了演示所以采用了比较简单的方式dll文件生成后放在在本地而真实案例中恶意代码会存在母体文件的内存中因为需要不落地内存加载。先声明相应的结构体变量采用指定位数的方式指定结构体变量实际占用的位数(根据重定位表的特性)声明一个函数指针便于后续进行调用执行dll入口。第一步先通过GetModuleHandleA获取基址避免ASLR随机基址影响读取dll文件内容加载至内存中通过PE结构获取实际加载至内存中dll的PE头部数据。分配dll加载时所需的内存空间获取加载基址与预期基址的差值接着复制dll头部数据至新的内存空间。开始模拟Windows加载器功能加载PE文件至内存如下。02PE文件重定位基址重定位表位于PE头的IMAGE_NT_HEADERS/IMAGE_OPTION_HEADER/IMAGE_DATA_DIRECTORY[5]换句话说重定位表位于可选头的数据目录表下的第六项基址重定位表中记录硬编码地址的位置(偏移)使用这张表就能获得准确的硬编码地址偏移并后续对其修正。重定位表是按照一个物理页(4kb或1000H)进行存储的也就是一个重定位块负责一个4kb内存页一个重定位表只管自己当前的物理页重定位。一个重定位表的记录偏移的大小是2个字节(1000H最多需要12bit即可0~FFFH)也就是16位而记录偏移的大小是由SizeofBlock决定的。如何修正将指令中的操作数按照指针字节数读取出来然后将其减去默认加载基址(扩展头中的字段ImageBase)再加上新的加载基址最后把新地址存入原来的地址中。重定位工作完成之后进行导入表的解析。以上工作准备完毕之后就已经模拟内存加载完成之后获取dll文件入口点进行执行利用了之前声明的函数指针。03示例源码#include #include typedef struct BASE_RELOCATION_BLOCK { DWORD PageAddress; DWORD BlockSize;} BASE_RELOCATION_BLOCK, *PBASE_RELOCATION_BLOCK;typedef struct BASE_RELOCATION_ENTRY { USHORT Offset : 12; USHORT Type : 4;} BASE_RELOCATION_ENTRY, *PBASE_RELOCATION_ENTRY;using DLLEntry BOOL(WINAPI *)(HINSTANCE dll, DWORD reason, LPVOID reserved);int main(){ //得到当前模块的基址 PVOID imageBase GetModuleHandleA(NULL); //本地加载dll内容至内存中 HANDLE dll CreateFileA(C:\\Users\\onion\\Desktop\\dll\\Release\\dll.dll, GENERIC_READ, NULL, NULL, OPEN_EXISTING, NULL, NULL); DWORD64 dllSize GetFileSize(dll, NULL); LPVOID dllBytes HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dllSize); DWORD outSize 0; ReadFile(dll, dllBytes, dllSize, outSize, NULL); //获取已加载至内存中的dll的头部数据 PIMAGE_DOS_HEADER dosHeaders (PIMAGE_DOS_HEADER)dllBytes; PIMAGE_NT_HEADERS ntHeaders (PIMAGE_NT_HEADERS)((DWORD_PTR)dllBytes dosHeaders-e_lfanew); SIZE_T dllImageSize ntHeaders-OptionalHeader.SizeOfImage; //分配dll加载时所需的内存空间 LPVOID dllBase VirtualAlloc((LPVOID)ntHeaders-OptionalHeader.ImageBase, dllImageSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); //得到实际分配的内存基址与预期的基址差值,便于后续进行重定向 DWORD_PTR deltaImageBase (DWORD_PTR)dllBase - (DWORD_PTR)ntHeaders-OptionalHeader.ImageBase; //将dll头部数据复制到分配的内存空间 std::memcpy(dllBase, dllBytes, ntHeaders-OptionalHeader.SizeOfHeaders); //加载节区数据至新的内存空间 PIMAGE_SECTION_HEADER section IMAGE_FIRST_SECTION(ntHeaders); for (size_t i 0; i ntHeaders-FileHeader.NumberOfSections; i) { LPVOID sectionDestination (LPVOID)((DWORD_PTR)dllBase (DWORD_PTR)section-VirtualAddress); LPVOID sectionBytes (LPVOID)((DWORD_PTR)dllBytes (DWORD_PTR)section-PointerToRawData); std::memcpy(sectionDestination, sectionBytes, section-SizeOfRawData); section; } // 开始dll加载实现重定位 IMAGE_DATA_DIRECTORY relocations ntHeaders-OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]; DWORD_PTR relocationTable relocations.VirtualAddress (DWORD_PTR)dllBase; DWORD relocationsProcessed 0; while (relocationsProcessed relocations.Size) { PBASE_RELOCATION_BLOCK relocationBlock (PBASE_RELOCATION_BLOCK)(relocationTable relocationsProcessed); relocationsProcessed sizeof(BASE_RELOCATION_BLOCK); DWORD relocationsCount (relocationBlock-BlockSize - sizeof(BASE_RELOCATION_BLOCK)) / sizeof(BASE_RELOCATION_ENTRY); PBASE_RELOCATION_ENTRY relocationEntries (PBASE_RELOCATION_ENTRY)(relocationTable relocationsProcessed); for (DWORD i 0; i relocationsCount; i) { relocationsProcessed sizeof(BASE_RELOCATION_ENTRY); if (relocationEntries[i].Type 0) { continue; } DWORD_PTR relocationRVA relocationBlock-PageAddress relocationEntries[i].Offset; DWORD_PTR addressToPatch 0; ReadProcessMemory(GetCurrentProcess(), (LPCVOID)((DWORD_PTR)dllBase relocationRVA), addressToPatch, sizeof(DWORD_PTR), NULL); addressToPatch deltaImageBase; std::memcpy((PVOID)((DWORD_PTR)dllBase relocationRVA), addressToPatch, sizeof(DWORD_PTR)); } } //解析导入表 PIMAGE_IMPORT_DESCRIPTOR importDescriptor NULL; IMAGE_DATA_DIRECTORY importsDirectory ntHeaders-OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT]; importDescriptor (PIMAGE_IMPORT_DESCRIPTOR)(importsDirectory.VirtualAddress (DWORD_PTR)dllBase); LPCSTR libraryName ; HMODULE library NULL; while (importDescriptor-Name ! NULL) { libraryName (LPCSTR)importDescriptor-Name (DWORD_PTR)dllBase; library LoadLibraryA(libraryName); if (library) { PIMAGE_THUNK_DATA thunk NULL; thunk (PIMAGE_THUNK_DATA)((DWORD_PTR)dllBase importDescriptor-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; } } importDescriptor; } //执行加载的dll DLLEntry DllEntry (DLLEntry)((DWORD_PTR)dllBase ntHeaders-OptionalHeader.AddressOfEntryPoint); (*DllEntry)((HINSTANCE)dllBase, DLL_PROCESS_ATTACH, 0); CloseHandle(dll); HeapFree(GetProcessHeap(), 0, dllBytes); return 0;}04整个流程1、读入原始DLL文件至内存缓冲区2、解析DLL标头并获取SizeOfImage3、为DLL分配新的内存空间大小为SizeOfImage4、将DLL标头和PE节复制到步骤3中分配的内存空间5、执行重定位6、加载DLL导入的库7、解析导入地址表(IAT)8、调用DLL的DLL_PROCESS_ATTACH05演示效果06真实案例Netwalker勒索软件dll自加载技术恶意文件是一个混淆并加密过的PowerShell脚本先对PowerShell脚本进行解混淆。解混淆后的内容如下字节序列中的0x4d、0x5a明显是一个PE文件标志头一旦加载执行后这部分内容就在内存中了。明显的C#代码模拟解析PE结构如下。执行自加载执行如下。更详细的内容可去除混淆后自行查看该勒索样本实现的反射加载过程非常明显。加载执行后通过模拟加载dll并调用导出函数Do后进而实现注入目标进程类似的技术则与开源项目PowerSploit中的反射自加载的Mimikatz脚本实现相类似。07参考https://ired.team/offensive-security/code-injection-process-injection/reflective-dll-injectionhttps://blog.trendmicro.com/trendlabs-security-intelligence/netwalker-fileless-ransomware-injected-via-reflective-loading/https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Invoke-Mimikatz.ps1