shellcode分析技巧总结

阅读量321466

|

发布时间 : 2021-05-26 17:30:05

 

1.摘要

很多恶意代码会使用在内存中解密shellcode来执行其主要的恶意行为,使用这种技术不会在磁盘留下任何痕迹,从而来躲避杀软的检测,还有的恶意代码使用多层嵌套的shellcode来增加分析的难度,下面我总结了恶意代码经常使用shellcode的方式,和分析shellcode的一些技巧

 

2.恶意代码执行shellcode的方法

2.1 从资源或数据段中获取

这是最常见的一种加载shellcode的方法,大多数样本都使用这种方法来把shllcode加载到内存中执行,通常都是先申请内存(一般使用VirtualAlloc、HeapCreate、GlobalAlloc),之后从资源(一般使用FindResource、LoadResource、LockResource)或者从C&C服务器把shellcode下载到内存中加载,或者是将加密后的shellcode存入数据段,恶意代码运行时,先将数据段中的shellcode解密到内存中,最后一步就是执行内存中的shellcode,xshell后门(MD5: 97363d50a279492fda14cbab53429e75)用的就是这种方法

2.2宏代码

样本通过带有宏的doc文档为载体,宏代码中硬编码加密后的shellcode,宏代码运行后,首先申请内存,然后使用WriteProcessMemory将解密shellcode写入申请的内存中,最后CreateThread创建线程执行

直接使用Windows API来进行上述操作很容易被杀软拦截,恶意代码使用其他可以达到同样功能的API来代替易被检测的API,恶意代码一般会选择参数有输入和输出buffer的函数来替代WriteProcessMemory,一些格式转换函数符合要求,除了这种方法还可以使用可以设置回调函数的API来替代CreateThread执行shellcode,以Lazarus组织的样本为例(MD5: e87b575b2ddfb9d4d692e3b8627e3921)

此样本首先使用 “Microsoft窗体2.0框架”的ActiveX控件中的Frame1_Layout事件,触发事件后使用HeapCreate、HeapAlloc来创建分配内存,样本将shellcode转换为UUID字符串硬编码在宏脚本中,样本将UUID和申请的内存作为参数传入UuidFromStringA函数将shellcode解码并写入申请的内存中,UuidFromStringA函数的功能为将UUID字符串转换为二进制数据,有两个参数,第一个为字符串的UUID,第二个为指向转换后二进制数据的指针

样本使用UuidFromStringA将硬编码的shellcode的uuid,解码并存入申请的内存后,通过设置EnumSystemLocalesA的第一个参数,使用回调函数的方法来执行内存中的shellcode,也可以替换成其他参数有回调函数指针的API来通过回调函数执行shellcode

总结下用UUID来执行shellcode这种方法的流程

1.创建并申请内存

2.使用UuidFromStringA将uuid字符串转换成shellcode并存储在申请的内存中

3.通过调用参数有回调函数指针的API,通过回调函数来执行shellcode

2.3 注入

恶意代码可以通过运行脚本文件或PE文件,将shellcode注入到其他进程中,注入的方法很多,其一般流程为

1.获取进程句柄

2.申请内存

3.将shellcode写入或映射到申请的内存中

4.执行内存中的shellcode(有很多方法)

2.4 嵌入到文件

一些样本将shellcode嵌入到各种文件当中,例如BMP文件(ProLock样本)或RTF文档中

 

3.shellcode分析技巧

3.1 ida调试堆代码

当我们调试复杂的多层的样本时,如果我们一层一层dump shellcode,在对dump下来的shellcode进行静态和动态分析时,这样分析过于麻烦,对于这种情况使用ida双机调试是一个很好的选择,我们以APT41的样本(MD5:830a09ff05eac9a5f42897ba5176a36a)为例

首先配置双机调试环境,我们在虚拟机中安装Debugging Tools for Windows,从Debuggers x86目录下启动调试服务dbgsrv -t tcp:port=5000,server=localhost(64位在x64目录下运行dbgsrv -t tcp:port=5064,server=localhost)

之后在ida中选择Debugger–>Switch debuggers选择Windbg debugger

在ida中选择Debugger–>Process options

Connection string 填写tcp:port=5000,server=虚拟机ip

application填写在虚拟机中要调试的样本的全路径

input file是本地ida分析的样本文件

directory填写样本所在的目录

接下来将windbg路径设置为环境参数或者修改ida cfg文件夹下的ida.cfg文件 将DBGTOOLS字段修改windbg的路径

之后我们使用开源ida脚本IDA-WindbGlue来进行双机调试

首先我们根据虚拟机的情况修改config.py

配置好后alt+F7运行windbg_remote.py,再在ida的python命令窗口中输入remote_debug()进行远程调试

之后我们开始分析样本,样本首先会创建解密并运行第一层shellcode的线程

为了将每个阶段解密出的shellcode都保存在idb文件中,方便后续对样本每个阶段进行分析,我们要修改样本每次为shellcode申请的内存的段属性,使用alt+s快捷键修改段属性 将Loader segment勾选

在点击Debugger–>Take memory snapshot–> Loader Segments将动态申请的内存保存到idb文件中

经过上述操作之后,即使我们结束动态调试,也可以在idb文件中清晰的看到样本的各阶段在内存中所解密出的shellcode,可以对其进行注释,更有效的分析多层的恶意代码在内存的各个阶段中释放出的shellcode

样本每次分配的内存是随机的,这导致每次分析shellcode的内存都不一样,很影响对样本的分析,虽然可以修改VirtualAlloc函数的第一个参数来指定申请内存的地址,但一般样本此参数都为NULL,我们没有足够的空间去修改参数为指定的地址,所以这里用创建虚拟机快照的方法来解决此问题

创建快照的步骤

1.首先在ida的windbg命令行窗口中输入〜* n挂起所有线程

2.之后在ida调试器中设置下一条命令的断点

3.再在ida中点击Debugger –> Detach from process 分离进程

4.创建虚拟机快照

恢复快照的步骤

1.恢复快照

2.首先点击ida的Debugger -> Attach to process附加到样本进程

3.在ida的windbg命令行窗口中输入〜*m恢复所有线程

4.F9运行便停在了断点处,方便继续调试

这样我们就可以在同一地址空间不断分析每个阶段样本在内存中释放的shellcode,并存储在idb文件中,可以在idb文件中清晰的注释出各个阶段shellcode的功能

3.2模拟执行

模拟执行shellcode可以快速的了解这段shellcode的功能及其API调用,一般模拟执行shellcode使用scdbg.exe和speakeasy,这里我们以xshell后门用scdbg模拟执行为例

模拟执行可以快速看出shellcode调用了哪些api,这段shellcode大概是什么功能,方便接下来进一步分析

3.3使用Loader加载shellcode动态调试

Dump下来的shellcode是一个二进制数据块,无法直接运行和调试,我们可以用loader将shellcode加载起来进行动态调试,有很多这种工具例如jmp2it.exe,这些工具实现原理都差不多,也可以自己实现一个loader

Jmp2it.exe的原理就是传入的参数1 shellcode的路径用CreateFile获取文件句柄,之后使用CreateFileMapping、MapViewOfFile映射到自己的内存空间中,再根据参数2偏移量加上MapViewOfFile返回的基址获取入口地址,将此地址设为函数指针,在执行此函数前用EB FE循环跳转当前指令

以xshell后门内置的shellcode为例,首先我们运行以下命令加载shellcode到jmp2it进程中

使用OD附加jmp2it.exe进程

F9运行进程,几秒后在将进程暂停,将停在EB FE(循环跳转当前指令)指令上,将其nop掉,步入接下来的call就是shellcode的代码了

3.4 OD调试注入的shellcode

第一种情况例如Lazarus组织的样本(MD5: e87b575b2ddfb9d4d692e3b8627e3921),此类通过文档宏脚本将shellcode写入word进程然后通过回调函数(或创建线程)执行的样本,使用word内的调试器无法调试执行的shellcode,通常通过分析宏代码找到shellcode地址,在使用OD附加到word进程上,然后将eip修改为shellcode的地址开始分析,以Donot组织的样本(MD5:7a6559ff13f2aecd89c64c1704a68588)为例

由于样本使用了密码保护VBA脚本,我们使用VBA Password Bypasser跳过密码保护调试VBA脚本

通过调试vba脚本我们找到样本创建shellcode线程的地址为0x17210000,创建线程后使用OD附加到word进程上,将eip修改成shellcode地址,就可以调试样本注入的shellcode了

另一种情况是将shellcode注入到其他进程来执行其恶意行为,例如Ramnit蠕虫(MD5: ff5e1f27193ce51eec318714ef038bef)此样本会创建系统默认浏览器进程

之后将其在内存中解密shellcode注入到浏览器进程中中来执行其恶意行为,这种情况我们首先要找到样本将shellcode写入到其他进程的地址

知道写入shellcode的地址后,我们可以使用内存修改工具(winhex 或ProcessHacker等)将入口点修改为int3(CC)或是循环跳转当前指令(EB FE)

之后让样本母体运行到恢复浏览器进程执行,我们使用OD附加到浏览器进程(也可以将OD设置为实时调试器自动附加)F9运行浏览器进程,过几秒暂停,会停到shellcode入口点,将EB FE修改为原来的指令,就可以调试样本注入的shellcode了

3.5 将shellcode转换成exe

我们可以使用yasm将dump下来的shellcode转换成exe来进行调试,不过这种方法使用场景有限,无法处理 shellcode需要其他地方的代码和数据的情况(比如APT10的Ecipekac样本需要从另一个DLL中读取嵌入的数据后解密出payload)

 

4.总结

分析样本所使用的shellcode时,我们要针对不同的样本类型灵活运用不同的分析方法来提高分析效率

本文由WINORDIE原创发布

转载,请参考转载声明,注明出处: https://www.anquanke.com/post/id/239441

安全KER - 有思想的安全新媒体

分享到:微信
+114赞
收藏
WINORDIE
分享到:微信

发表评论

Copyright © 北京奇虎科技有限公司 三六零数字安全科技集团有限公司 安全KER All Rights Reserved 京ICP备08010314号-66