概述
HeaderLessPE是一种移除了大多数字段的PE结构,由icedid木马所使用,本文通过对该技术的研究,扩展了该技术在HVNC下的攻击方式,使的通过远程注入后的HeaderLessPE可不受限制的运行图形化黑客工具。
01 HeaderLessPE 技术原理
HeaderLessPE最初由Icedid木马使用,该木马的多阶段内存加载和远程注入中都作为主要的技术手段。
如图所示,一个HeaderLessPE的结构定义如下:
typedef struct _HeaderLessSection {
DWORD VA; // 内存虚拟地址偏移
DWORD VirtualSize; // 内存加载大小
DWORD RawOffset; // 数据偏移
DWORD RawSize; // 数据大小
BYTE Access; // 内存属性
}HeaderLessSection, * PHeaderLessSection;
typedef struct _HeaderLessPE
{
PVOID ImageBase; // 映像基址
DWORD ImageSize; // 映像大小
DWORD ImageEntryPoint; // 入口函数
DWORD ImportTableVA; // 导入表
DWORD RelocTableVA; // 重定位表
DWORD RelocTableSize; // 重定位表大小
DWORD SectionCount; // 节区数量
HeaderLessSection Section[1];
}HeaderLessPE, * PHeaderLessPE;
与Memdll等内存加载技术实现一致,加载HeaderLessPE时都需要自行编写loader代码分别将节区拷贝到内存、修复重定向与导入表,设置内存权限并调用入口函数。
HeaderLessPE的加载过程如下:
1. 申请内存并拷贝节区
申请地址时我们可以先申请ImageBase映像基址,如果失败再申请随机地址。
PBYTE image = (PBYTE)VirtualAlloc(pe->ImageBase, pe->ImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (image == 0)
{
image = (PBYTE)VirtualAlloc(NULL, pe->ImageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
}
2. 修复重定位
如果申请地址与ImageBase不一致,就需要修复重定位数据,如果是注入到远程进程,需要计算与远程地址的偏移。
DWORD delta = (DWORD)((LPBYTE)image - (LPBYTE)pe->ImageBase); // 0x40000 - 0x1400,0000 计算申请地址与基址的偏移
3. 修复导入表
4. 更改节区内存权限、并调用入口函数
entry = (pfnWinMain)((PBYTE)image + pe->ImageEntryPoint);
entry(0,0, 0, 0);
相比MemDll等内存PE加载技术,HeaderLessPE有两个优点:
避免包含传统PE的DOS头、PE头特征:DOS头和PE头经常是被内存扫描的照顾重点特征,在使用Cobalt Strike的时候常需要设置Profile文件将加载完成的Beacon头抹掉。使用HeaderLessPE就不需要担心这个问题。
支持数据段和重定位表,能方便的将传统EXE文件转换为HeaderLessPE:只要是支持重定位,不包含如:Tls、delay import等结构都能够转换为HeaderLessPE, 这样不仅可以用来做木马内存模块,还可以将一些黑客工具方便的转换为HeaderLessPE进行内存加载运行,扩展可使用的攻击工具。
02 技术扩展,实现在HidenVNC下无文件运行GUI程序
HidenVNC又称HVNC,是一种远程桌面控制技术,多用于银行木马, 可以在受害者不知情的情况下,隐蔽的访问系统,浏览文件、查看凭证、启动程序、登录网银等。为了达到这一目的,HVNC会使用了一些鲜为人知Windows功能,使用CreateDesktop来创建一个新的虚拟桌面,隔绝了与用户操作互相影响。关于HVNC的介绍,看雪的这篇讲的比较全面https://bbs.kanxue.com/thread-264956.htm。总之,HVNC是一种高级的远程控制技术,提供了一种隐蔽的图形化控制手段。
单纯的利用HVNC只能运行一些受害者机器上已安装的应用程序,可执行的程序受限。假如我们能够执行一些如BrowsingHistoryView或者是ADExplorer之类的图形化工具,又避免在目标机器安装,我们就能扩展可执行程序的范围,例如NisSoft和微软的SysinternalsSuite都提供大量可以被用来攻击的管理用具。
在程序绘制GUI时,首先会查找相对应的资源,并将其加载至内存。程序资源是指程序或应用程序使用的各种非代码元素,这些元素可以描述图像、音频、文本、图标、位图、对话框框架、字符串等等。编程时,加载菜单资源的关键代码如下:
HMODULE hModule = GetModuleHandle(NULL);
HRSRC hResInfo = FindResource(hModule, MAKEINTRESOURCE(IDR_MYMENU), RT_RCDATA);
DWORD dwSize = SizeofResource(hModule, hResInfo);
HGLOBAL hResData = LoadResource(hModule, hResInfo);
LPVOID lpRes = LockResource(hResData);
在使用GetModuleHandle指明包含资源的模块后,使用FindResource在PE文件格式中查找资源信息,成功后通过SizeofResource和LoadResource获取资源的大小,加载资源。
在PE文件格式中,资源可以通过可选头数据目录表的第3个成员IMAGE_DIRECTORY_ENTRY_RESOURCE访问,IMAGE_DIRECTORY_ENTRY_RESOURCE指向了PE资源的根目录,之后资源按照资源类型->资源标识符->资源页的多层目录结构来组织的,只有通过层层索引才能够进入相应的子目录找到正确的资源。其中 IMAGE_RESOURCE_DIRECTORY 中会指明后面数组的成员个数,IMAGE_RESOURCE_DIRECTORY_ENTRY 指向具体的下一层目录结构。
其中资源的类型可以是图标(Icon)、位图(Bitmap)、对话框(Dialog Box)、菜单(Menu)等。找到相应类型之后,再匹配具体资源标识符。如:
理解了资源的加载过程,我们扩充HeaderLessPE包含的结构,增加一个ResourceTableVA指向资源根目录。
typedef struct _HeaderLessPE
{
PVOID ImageBase; // 镜像基址
DWORD ImageSize; // 镜像大小
DWORD ImageEntryPoint; // 入口函数
DWORD ImportTableVA; // 导入表
DWORD RelocTableVA; // 重定位表
DWORD RelocTableSize; // 重定位表大小
DWORD ResourceTableVA // 资源表
DWORD ResourceTableSize // 资源表大小
DWORD SectionCount; // 节区数量
HeaderLessSection Section[1];
}HeaderLessPE, * PHeaderLessPE;
劫持所有涉及资源的API如GetModuleHandleW、FindResourceExW等,将进程的模块地址返回成HeaderLessPE的ImageBase基址,当需要FindResources的时候,再通过层级目录从查找HeaderLessPE的空间内查找资源,返回
HMODULE GetModuleHandleW( [in, optional] LPCWSTR lpModuleName );
参数传入为空时为获取当前进程句柄(基址),返回当前基址返回为HeaderLessPE的基址能让我们后续判断是否查找HeaderLessPE的资源
HRSRC FindResourceExW( [in, optional] HMODULE hModule, [in] LPCWSTR lpType, [in] LPCWSTR lpName, [in] WORD wLanguage );
第一个参数是模块基址,当地址匹配我们的HeaderLessPE基址时,我们通过HeaderLessPE结构成员ResourceTable获取资源的根目录,再根据lpType(类型)和lpName(名称)通过名称或ID查找具体的资源,找到后返回资源句柄,实际就是PIMAGE_RESOURCE_DATA_ENTRY,
DWORD SizeofResource( [in, optional] HMODULE hModule, [in] HRSRC hResInfo );
SizeofResource通过资源句柄获取指定的资源大小,资源大小存储在PIMAGE_RESOURCE_DATA_ENTRY的的Size字段
HGLOBAL LoadResource( [in, optional] HMODULE hModule, [in] HRSRC hResInfo );
LoadResource通过资源句柄获取资源数据地址, 资源数据地址通过获取PIMAGE_RESOURCE_DATA_ENTRY的OffsetToData计算基地址而来。
实际要完成一个FileLess图形化程序还需要使用到注入技术,同时为了避免出现导入表依赖,需要先注入一个没有依赖的loader, loader程序再加载真正要运行的GUI程序。过程如下:
我们需要准备两个HeaderLessPE分别为loader和BrowsingHistoryView程序:
挂起启动c:\windows\system32\mspaint.exe,申请远程进程内存。
根据申请的地址,在本地修复loader程序的重定位和导入表。
写入mspaint.exe内存,修改权限,修改RIP指向loader的入口函数,恢复mspaint.exe主线程,完成远程注入HeaderLessPE。
loader再次申请内存,加载BrowsingHistoryView程序的HeaderLessPE。
实现效果如下:
附录 项目开源链接
https://github.com/M01N-Team/HeaderLessPE
附录 参考文献
[1] https://github.com/strivexjun/MemoryModulePP.git
[2] https://doxygen.reactos.org/
[3] https://github.com/hasherezade/pe-sieve.git
[4] https://bbs.kanxue.com/thread-264956.htm
绿盟科技天元实验室专注于新型实战化攻防对抗技术研究。
研究目标包括:漏洞利用技术、防御绕过技术、攻击隐匿技术、攻击持久化技术等蓝军技术,以及攻击技战术、攻击框架的研究。涵盖Web安全、终端安全、AD安全、云安全等多个技术领域的攻击技术研究,以及工业互联网、车联网等业务场景的攻击技术研究。通过研究攻击对抗技术,从攻击视角提供识别风险的方法和手段,为威胁对抗提供决策支撑。
M01N Team公众号
聚焦高级攻防对抗热点技术
绿盟科技蓝军技术研究战队
官方攻防交流群
网络安全一手资讯
攻防技术答疑解惑
扫码加好友即可拉群