APT海莲花的某款样本分析
小常识: APT海莲花的英文名字 OceanLotus。
01 身份证
名字: 36 ASEAN Summit 26-06-2020 Conference.doc~.exe。
SHA256: dbde2b710bee38eb3ff1a72b673f756c27faa45d5c38cbe0f8a5dfccb16c18ba。
MD5: SHA1: 4a41bc81b27374b8a711794a7b27d51700403341。
样本格式: SFX(RAX自解压)。
回连C2: tripplekill.mentosfontcmb.com。
长这个样子:
看上去是一个word,但他本来就是一个exe。
02 运行效果
通过网上的样本分析得:很多东西都是为了解密一段shellcode,然后执行它。于是我用了比较直接的形式解密了shellcode然后执行它。
直接的效果就是wps打开了一个word,然后就没了。
03 基本PE信息
Die中,出现一些关键字,PE32和Zip经过查询资料,推断该文件为自解压压缩文件。
通过7-zip打开文件:
发现这些信息。
APT组织加载恶意样本的方式多种多样,其中白加黑的加载方式是一种常用的APT攻击手法,也是海莲花APT组织最常用的APT攻击手法之一,统计最近几年国内发布的OceanLotus(海莲花)APT组织报告中的白+黑程序名。
如下所示:
之前我们用7-zip打开过exe文件,我们可以解压打开它(哇塞,既然可以把一个exe 解压缩)。
内含有:
本次样本采用白+黑的模式运行。
启动白程序,然后利用升级程序MicrosoftUpdate.exe
加载SoftwareUpdateFiles.Resources目录下的恶意程序SoftwareUpdateFilesLocalized.dll。
SoftwareUpdateFilesLocalized.dll会读取目录下的SoftwareUpdateFiles.locale文件。
然后解密SoftwareUpdateFiles.locale并执行。
04 Dll 加载
主要是寻找SoftwareUpdateFilesLocalized.dll如何被加载的。
MicrosoftUpdate.exe的导入表里面有SoftwareUpdateFiles.dll,但是没有SoftwareUpdateFilesLocalized.dll ,而且查看MicrosoftUpdate.exe,SoftwareUpdateFiles.dll,SoftwareUpdateFilesLocalized.dll的字符串信息,并没发现SoftwareUpdateFilesLocalized.dll的字符串信息。
查资料后推测:SoftwareUpdateFilesLocalized.dll由其他系统dll加载(后来证实发现,是SoftwareUpdateFiles.dll,加载的SoftwareUpdateFilesLocalized.dll)。
还有一种情况是把数据解密后才会有目标字符,只是猜测罢了。
可以看见在C:WindowsSysWOW64msvcrt.dll加载后,就加载了C:UsersvirusDesktop7579AEDE6A223C96231AD30472A060DBSoftwareUpdateFiles.dll。
SoftwareUpdateFiles.dll是存在于导入表的,然后看SoftwareUpdateFilesLocalized.dll是怎么加载的,通过xdebug和IDA 2次测试,发现IMM32.DLL加载后,才会加载SoftwareUpdateFilesLocalized.dll,然后看IMM32.DLL是否引入SoftwareUpdateFilesLocalized.dll。
发现并没有引入。
可能是IMM32.DLL动态加载的,然后猜测是IMM32.DLL调用了Kernel32的LoadLibiary加载的。
所以,可以去对应的API下断点。
虽然导入表只有LoadLibiaryExW,但是我还是在可能的地方都设置了断点,比如在上面。
于是F9后,它停在了:
what?
难道不是IMM32.DLL加载的,明明IMM32.DLL只调用了Kernel32的LoadLibiaryExW。为什么这里是LoadLibarayW?
观察函数的参数:
就是一个字符串,内容是一个dll地址。
之后再次F9,就不会停在LoadLibary相关的API,说明后面也没有加载SoftwareUpdateFilesLocalized.dll。
于是我们算是知道了是LoadLibaryW加载的SoftwareUpdateFilesLocalized.dll。
但是很懵,到底是谁调用LoadLibiaryW加载的SoftwareUpdateFilesLocalized.dll?
到底是谁调用的LoadLibaryW,其实你看LoadLibaryW函数返回到哪个模块,说明就是那个模块就调用了LoadLibaryW呗。
这不就是<<逆向工程核心原理>>讲过的吗?
于是去往返回地址就在SoftwareUpdateFiles.dll模块。
通过返回地址和IDA的静态分析,发现在int __thiscall ASUResourceProxy::ASUResource-Proxy(int this,wchar_t *a2)函数里面加载了dll:
然后很奇怪的是,对于SoftwareUpdateFilesLocalized.dll的DllMain,
进入sub_10001020((int)hinstDLL);是对SoftwareUpdateFiles.dll做一个API的Hook。
也就是SoftwareUpdateFiles.dll(A)加载SoftwareUpdateFilesLocalized.dll(B),然后SoftwareUpdateFilesLocalized.dll(B)调用dllMain又把SoftwareUpdateFiles.dll(A)给Hook。
为什么这样做呢?
对于SoftwareUpdateFiles.dll加载SoftwareUpdateFilesLocalized.dll的时候,
它意思就是说,如果SoftwareUpdateFilesLocalized.dll加载失败了,那就不调用没有下面这句话。
也就是SoftwareUpdateFilesLocalized.dll加载失败后不报错。
然后对MicrosoftUpdate.exe的函数hook。
05 shellcode 加载
分析SoftwareUpdateFilesLocalized.dll对于存在SHELLCODE的样本,其加载原理大多通过"VirtualAlloc"函数将SHELLCODE注入到某个内存空间中,所以寻找VirtualAlloc的交叉引用即可。
于是就可以发现一个函数void __noreturn sub_100010C0(),在SoftwareUpdateFilesLocalized.dll被加载后,在一些关键位置下了断点。
第1次F9来到SoftwareUpdateFilesLocalized.dll的HMODULE __usercall sub_10001020@(int a1@),然后对SoftwareUpdateFiles.dll进行一个Hook,让它不报错。
然后再次对MicrosoftUpdate.exe Hook。
可以看出,他是Hook了12个字节, 2+4+2+4,效果如下,Hook前:
Hook后,去往SoftwareUpdateFilesLocalized.dll解密二进制文件SoftwareUpdateFiles.locale让他执行。
于是第二次F9,出现了很多C+= Exception,也就是F9了很多次,每次把异常交个程序自己处理。
后面又来到了SoftwareUpdateFiles.dll,然后它再次加载SoftwareUpdateFilesLocalized.dll,然后再F9,就发现它去往了二进制文件解密函数,该函数前面干的事情就是获取路径。
后面就是读取文件,然后分配内存,然后解密。
注意对内容解密后,它是直接:
也侧面说明了该二进制文件不是可执行的PE文件,而是shellcode。
06 shellcode 分析
这一次算是真正的发现了shellcode长什么样子。
ShellCode加入大量无意义运算和无效跳转花指令,用于干扰分析。
07 重定位分析
第一条指令运行: 如果st0<st5,那么st5=st0,否则啥也没发生< div="">,指令运行前:
运行后:
其实到这里,我们还是不太清楚第一句话是干嘛的。
(ps:后面一点点马上解释。)
执行fnstenv byte ptr [esp-0Ch]前:
当fnstenv byte ptr [esp-4*3]执行之后:
(ps:他就是把FPU的一部分环境拷贝到esp-4*3的地方。)
可以看到[esp]的地方,他的值=0x00BD0000,这个数据很特殊,也就是他的值就是shellcode开始的地方,也就是说这个是一个EIP,我尝试运行了几遍,发现[esp]始终的执行一个shellcode起始地址。后面拷打了很多次chatgpt,他才告诉我这是为什么。
首先是提问:
fnstenv指令保存的字节数取决于使用的保存格式,在x86汇编中,有两种保存格式:
1.短格式:fnstenv [目标],它保存28个字节的FPU环境信息。
2.长格式:fnstenv [目标],它保存108个字节的FPU环境信息。
因此,fnstenv指令会存储28个字节或108个字节,具体取决于您选择的保存格式,在大多数情况下,推荐使用较小的短格式,除非您需要完整的FPU环境信息。
然后,对于我们这里,他属于28字节的情况:
1.控制寄存器(Control Register,CW):2个字节;
2. 状态寄存器(Status Register,SW):2个字节;
3. 标签寄存器(Tag Register,TW):2个字节;
4.FPU指令指针偏移量(Instruction Pointer Offset):2个字节;
5.FPU指令指针选择器(Instruction Pointer Selector):2个字节;
6. 数据指针偏移量(Operand Pointer Offset):2个字节;
7.数据指针选择器(Operand Pointer Selector):2个字节;
8.FPU状态寄存器(FPU Status Word):2个字节;
9.FPU控制寄存器(FPU Control Word):2个字节;
10.FPU标签寄存器(FPU Tag Word):2个字节;
11.FPU数据寄存器(FPU Data Registers):8个字节。
通过观察发现,数据指针选择器和FPU状态寄存器构成的4字节是shellcode的起始地址。
于是问chargpt:
数据指针选择器(Data Pointer Selector)和FPU状态寄存器(FPU Status Register), 与当前运行的EIP(Extended Instruction Pointer)在x86架构中是有关系的。
当执行FPU指令时,数据指针选择器会帮助确定从哪里加载或存储FPU数据寄存器的值,而FPU状态寄存器则包含了FPU的运算状态和控制信息这些寄存器的值会影响FPU指令的执行过程,EIP则是指向当前正在执行的指令的指针在程序执行期间,如果涉及到FPU操作,FPU数据的加载和保存可能会涉及到EIP的信息,以确定正确的数据寄存器位置和正确的FPU状态。
因此,数据指针选择器和FPU状态寄存器与当前运行的EIP密切相关,用于在执行浮点运算时正确管理和控制FPU环境,对于chatgpt的回答,只能说是了解一下,可能就真的是那么一回事情。
这就是为什么shellcode一开始就要fcmovb st,st(5)
只是为了构造一个FPU指令,方便后面做一个环境的保存,至于为什么是[esp-0Ch],是为了刚好把那个FPU据指针选择器给pop出来。
08 SMC-1
然后就是SMC解码可以看出是通过Loop循环异或实现SMC。
一开始对00E0001B地址处解密,解密之后才是loop循环。
感觉这种加密算法更加小小的复杂。
09 找打kernel32并获GetProcAddress
函数分析struct LIST_ENTRY *__stdcall sub_11F0059(DWORD *a1),关于那个结构体到底怎么会是这样,查看笔记:https://www.notion.so/aca3da52fbe14f0380c8d770b79feaad。
然后具体说一下他是怎么回事的,关于模块寻找的函数如下:
对于每次遍历,如何处理?
遍历到kernel32.dll后如何获取导出函数?
如下:
10 加载API和其他操作
一开始加载了很多的API,接着才是调用API的一些操作。
接着后面就是一些奇奇怪怪的文件操作了。
11 后续
后续有一点多,这里就不进行具体分析了。
12 其它发现
热补丁:
长按二维码关注
D0g3
道格安全
原文始发于微信公众号(道格安全):海莲花APT的某款样本分析 | 42期
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论