-
1 赛题回顾 -
2 最终排名(部分) -
3 启发与思路 -
4 算法与模型 -
4.5 函数名(CG图) -
4.6 复赛模型融合 -
4.4.1 Section信息 -
4.4.2 字符匹配 -
4.4.3 Yara匹配 -
4.4.4 Opcode -
4.4.5 其他布尔信息 -
4.1 灰度图 -
4.2 直方图 -
4.3 PE静态特征模型 -
4.4 特征工程
-
-
5 结果与改进 -
5.1 复赛结果 -
5.2 改进方向
-
-
6 后记 -
7 参考资料
1 赛题回顾
2070年,GY市将举办冬季奥运会,将会吸引了全球100多个国家的运动员、政要、众多媒体齐聚。冬季奥运会将会成为很多国际网络恐怖组织、黑客组织的关注目标,安全形势十分严峻。为了保证冬季奥运会的顺利举行,大会组委会成立了”重保小组“,针对全市重点单位、会议相关单位网站、系统进行护网攻防演练。小组将根据攻防演练的流量数据,分析并处置可能存在的各种安全威胁,保证大会的顺利进行。假设您是”重保小组“的成员,请您结合攻防演练的流量数据,溯源分析处置各类的安全事件,同时帮助大会及时准确地找出可能存在的安全威胁。
【方向五】:恶意代码分析
赛事期间各类体育赛事一票难求,暗网中出现了很多高价倒卖门票的不法行为,一度使得虚拟货币市场交易十分火爆,挖矿型恶意代码的数量也呈爆发式增长,不少个人用户、企业用户受到感染,沦为免费矿工。为对抗挖矿木马,需要针对近期互联网上流行的恶意软件进行深入分析,研究并设计挖矿型恶意代码的检测方法,保卫广大用户的财产安全!
【考察能力】:系统安全相关知识、程序分析与特征提取能力(如逆向分析或机器学习算法)
【方向介绍】
冬奥会期间各类体育赛事一票难求,暗网中出现了很多高价倒卖门票的不法行为,一度使得虚拟货币市场交易十分火爆,挖矿型恶意代码的数量也呈爆发式增长,不少个人用户、企业用户受到感染,沦为免费矿工。为对抗挖矿木马,需要针对近期互联网上流行的恶意软件进行深入分析,研究并设计挖矿型恶意代码的检测方法,保卫广大用户的财产安全!
2 最终排名(部分)
本次比赛我们战队在恶意代码分析方向获得初赛第三,复赛第一的成绩,最终为该方向冠军。
3 启发与思路
- 挖矿软件常见套路[1]
-
资格赛中获得的启发
-
①~②:需要关注虚拟机、调试软件、反编译软件、逆向分析工具和杀软名
-
③~⑤:需要关注系统关键路径、注册表
-
⑥~⑦:需要关注域名、IP、端口、钱包地址、可见字符串
-
⑧~⑩:同③~⑤
-
-
逆向工程中得到的思路
- 通过逆向分析,发现许多样本函数名包含数据货币名、密码学算法名(哈希算法)
-
- 很多带壳样本:UPX、Pelite、VMP……
-
- 白样本含有很多其他类别恶意程序,如病毒、外挂……
4 算法与模型
本次初赛、附加赛与复赛我们队使用的五种算法或模型相关代码见文末阅读全文,思路如下(其中在复赛中因为有性能的需求,部分模型未使用):
4.1 灰度图
PE文件二进制每一个字节对应一个像素,最后缩放成固定大小的灰度图。这是最常见也容易实现的模型,在恶意代码检测中已经广泛使用。
但我们仅在初赛时使用,原因如下:
-
文件大小差异较大,缩放比例不一致。
-
给定的样本集中包含许多加壳样本,使得数据分布被打乱,黑白特征不明显。
- 预处理时间较长。
4.2 直方图
- 字节直方图:
- 统计字节0-255出现个数
- 字节熵直方图[2]:
- 滑动一个1024字节的窗口,步长为256字节
- 计算每个1024字节窗口的熵
- 统计滑动窗口的(字节,熵值)对,最后转换成1x256维的特征向量
最后连接这两个特征向量,使用深度学习模型学习。效果好,预处理快,初赛单用这个模型便拿到93.8425分。
4.3 PE静态特征模型
虽然提供的样本被抹掉了样本PE结构中的MZ、PE、导入导出表等信息,但我们只需要恢复MZ头和”PE��”即可使用常规的分析工具对PE样本进行分析。
因为恢复也只是能解析PE文件的静态格式和特征,并不能将其直接运行,所以只能从静态特征入手。最后我们使用的是著名EMBER数据集[3]提到的PE文件静态特征提取方法。虽然原文用于检测恶意Windows PE文件,但是我们也将其移植过来检测挖矿软件。
原始方法提取了许多PE文件静态特征,如下:
- [✓] ByteHistogram、ByteEntropyHistogram:直方图
- [✓] GeneralFileInfo:调试信息、TLS段、重定位信息……
- [✓] HeaderFileInfo:PE头基本所有信息
- [✓] ExportsInfo:导出表个数、名称
- [✓] SectionInfo:Section名、大小、熵、属性等……
- [×] ImportsInfo:导入表被破坏,无法解析导入函数信息
- [×] StringExtractor:字符串提取在特征工程里做,这里删掉一是为了节省时间,二是防止特征重叠
4.4 特征工程
我们队所用特征工程主要包括五部分,分别为:Section信息、字符匹配、Yara匹配、Opcode和其他布尔信息。
4.4.1 Section信息
节区特征是PE文件一种重要特征,过多的节区、异常的节区名、异常的资源节区个数等指标都可以指示这个PE文件的可疑程度,因此我们首先针对节区进行特征统计:
- OEP所在节区名长度
- OEP所在节区名一般为”.text”,如果过长或过短说明很可能被混淆
- 比如UPX壳OEP处节区名为”UPX1”
- 各可读、可写、可执行节区大小和熵,和各属性节区占文件大小比例
- 举例:如果可执行节区占比过小,很可能加壳了(压缩壳)
- 资源节区个数
- 资源节区一般藏又一些压缩数据,比如挖矿恶意载荷
- 节区总个数
- 恶意软件节区数一般比较多
# OEP处section名长度
section_info["entry"] = len(entry_section)
section_info["section_num"] = len(lief_binary.sections)
# 可读、可写、可执行sections大小均值
sR, sW, sX = [], [], []
# 可读、可写、可执行sections熵值均值
entrR, entrW, entrX = [], [], []
# 资源section个数
rsrc_num = 0
for s in lief_binary.sections:
props = [str(c).split('.')[-1] for c in s.characteristics_lists]
if "MEM_READ" in props:
sR.append(s.size)
entrR.append(s.entropy)
if "MEM_WRITE" in props:
sW.append(s.size)
entrW.append(s.entropy)
if "MEM_EXECUTE" in props:
sX.append(s.size)
entrX.append(s.entropy)
if 'rsrc' in s.name:
rsrc_num += 1
section_info['size_R'], section_info['size_W'], section_info['size_X'] = np.mean(sR), np.mean(sW), np.mean(sX)
section_info['entr_R'], section_info['entr_W'], section_info['entr_X'] = np.mean(entrR), np.mean(entrW), np.mean(entrX)
section_info['rsrc_num'] = rsrc_num
4.4.2 字符匹配
根据资格赛获得的启发,队员们手写相应的正则匹配模式,其中包括:
- 路径、注册表、URL、IP地址正则匹配
- 其中因为注册表正则模式存在回溯问题,有的样本存在特别长的字符串,导致一个样本可能匹配了八分钟,所以我们复赛简单粗暴改成匹配字符串”reg”。
主要原因是我们认为操作注册表必然存在相应函数,而这些函数名基本含有”reg”。
- 其中因为注册表正则模式存在回溯问题,有的样本存在特别长的字符串,导致一个样本可能匹配了八分钟,所以我们复赛简单粗暴改成匹配字符串”reg”。
- 比特币钱包地址正则匹配
- 主要写了三种货币:
比特币、莱特币、门罗币
- 主要写了三种货币:
- 一些重要字符串匹配
- ”MZ”、”PE”指示可能含别的PE文件
- ”pool”、”cpu”、”gpu”、”coin”则是我们认为挖矿软件普遍存在的字符串
self.path_pattern = re.compile(b'[C-Zc-z]:(?:(?:\\|/)[^\\/:*?"<>|"x00-x19x7f-xff]+)+(?:\\|/)?')
self.regs_pattern = re.compile(b'reg', re.IGNORECASE)# re.compile(b'[A-Z_ ]{5,}(?:\\[a-zA-Z ]+)+')
self.urls_pattern = re.compile(b'https?://(?:[-w.]|(?:%[da-fA-F]{2}))+')
# self.strings_pattern = re.compile(b'[x20-x7f]{5,}')
self.ip_pattern = re.compile(b'(?:(?:25[0-5]|2[0-4]d|[01]?d{1,2}).){3}(?:25[0-5]|2[0-4]d|[01]?d{1,2})')
# #比特币钱包地址
self.wallet_pattern_btc = re.compile(b'(?:1|3|bc1|bitcoincash:q)(?:(?![0OIi])[0-9A-Za-z]){25,34}')
self.wallet_pattern_ltc = re.compile(b'(?:ltc1|M|L)[A-Za-z0-9]{25,36}')
self.wallet_pattern_xmr = re.compile(b'[0-9A-Za-z]{90,100}') #门罗币
self.mz_pattern = re.compile(b'MZ')
self.pe_pattern = re.compile(b'PE')
self.pool_pattern = re.compile(b'pool', re.IGNORECASE)
self.cpu_pattern = re.compile(b'cpu', re.IGNORECASE)
self.gpu_pattern = re.compile(b'gpu', re.IGNORECASE)
self.coin_pattern = re.compile(b'coin', re.IGNORECASE)
4.4.3 Yara匹配
Yara规则是基于二进制文件中包含的文本或二进制字符串的描述创建的。我们首先使用Yara-Rules[4]提供的规则进行匹配,其中包括:
- 壳规则
- 包含许多已知壳的Yara匹配规则
- 密码学常量规则
- 特别是哈希算法初始值
- 匹配时间略长,复赛忍痛舍弃
通过不断的调整阈值参数与筛除比例,在尽可能泛化的同时匹配到更多的黑样本,最后人工结合挖矿特征筛选出更值得关注的部分,优化规则集。
最终得到的自定义Yara规则集阈值为20,即每条规则在训练集黑样本中匹配样本个数大于等于20。
4.4.4 Opcode
通过传统逆向工具解析PE文件中的函数实在太耗时,因此我们打算通过简单的正则搜索识别代码中的函数,然后提取函数片段中的Opcode并保存。例如x86下,按栈匹配”push ebp; mov ebp, esp; ……; ret”如下代码段所示。
self.m32_pat = re.compile(b'x55x8bxec[^xc3]*xc3')
# …………
all_functions = self.m32_pat.findall(binary)
for function in all_functions:
function_op = []
for _, _, mnemonic, _ in self.md32.disasm_lite(function, 0x0):
try:
function_op.append(self.opcode_dict[mnemonic])
except Exception:
break
else:
op_pattern.append(function_op)
原因是发现在挖矿样本中有大量样本间共有的opcode特征,而白样本中却不明显。因此可以统计匹配出的函数个数、opcode种类个数、平均值、方差等特征。
4.4.5 其他布尔信息
根据资格赛所提供的启发,我们还收集了各种类别的进程名、数字货币名、密码学算法名等信息,以检测它们是否在给定PE文件内。
我们搜集了常见的杀毒软件的进程名称[6],以此作为挖矿软件对杀毒软件的检测行为的特征。
考虑到挖矿软件需要反分析、反调试来保证持久化,我们还以常见的调试器进程名作为挖矿软件的反调试行为的特征,主要为我们日常使用的调试工具。
考虑到挖矿软件会进行反沙箱对抗,我们搭建了各种不同的虚拟机软件环境,整理了挖矿软件可能检测的虚拟机环境特征。
由于挖矿过程必然存在矿池,因此我们想到检测矿池二级域名[7]来作为挖矿软件的一个有效特征,于是编写了爬虫脚本通过正则匹配的方式爬取了目前常用的矿池域名300余条。
4.5 函数名(CG图)
- IDA Pro提取函数调用生成GDL(Graph Description Language)文件
- GDL文件包含函数名(结点)、调用关系(边),如下述代码段
- 这样可以对函数调用次数进行排序,作为一种序列信息进行训练
graph: {
title: "Building graph"
IDA palette
....
colorentry 71: 255 255 0
colorentry 72: 0 0 0
colorentry 73: 0 0 0
colorentry 74: 0 0 0
colorentry 75: 0 255 255
colorentry 76: 192 192 192
// ....
node: { title: "165" label: "__aulldiv" color: 75 textcolor: 73 bordercolor: black }
node: { title: "166" label: "__aulldvrm" color: 75 textcolor: 73 bordercolor: black }
node: { title: "167" label: "__aullshr" color: 75 textcolor: 73 bordercolor: black }
// ....
node 169
edge: { sourcename: "169" targetname: "135" }
edge: { sourcename: "169" targetname: "136" }
edge: { sourcename: "169" targetname: "170" }
edge: { sourcename: "169" targetname: "171" }
....
}
4.6 复赛模型融合
5 结果与改进
5.1 复赛预测结果
在判分前主办方提供了1k个测试样本,我们使用这1k个样本进行检验与测试,最终耗时大约为1min20s,得分为95.52分。
因此预估判分用的1w多个样本耗时在20min上下,扣掉0.2分后与最终的95.38十分接近,证明了我们所用方法泛化能力以及稳定性。
5.2 改进方向
- 特征工程中我们提取的Opcode序列仅用了统计特征,我们也可以将其当作一种序列信息,使用NLP方法训练学习。
- 提供的样本中还是含有很多加壳样本的,因此我们可以对Yara匹配出的加壳样本进行单独处理。
- 特征工程的完善,例如:
- 任务计划名: Drivers、WebServers、DnsScan
- Powershell、Vbs脚本
- 端口,特别是高端口(>10000)
- ……
6 后记
7 参考资料
-
挖矿软件常见套路 https://zhuanlan.zhihu.com/p/164557943 -
Deep Neural Network Based Malware Detection Using Two Dimensional Binary Program Features https://arxiv.org/pdf/1508.03096.pdf -
EMBER: An Open Dataset for Training Static PE Malware Machine Learning Models https://arxiv.org/abs/1804.04637 -
Yara-Rules / rules https://github.com/Yara-Rules/rules -
Neo23x0 / yarGen https://github.com/Neo23x0/yarGen -
BrownFly / findAV https://github.com/BrownFly/findAV -
Mining Pools Live Monitoring Tools https://investoon.com/mining_pools -
DeepCG: Classifying Metamorphic Malware Through Deep Learning of Call Graphs https://www.researchgate.net/publication/337954044_DeepCG_Classifying_Metamorphic_Malware_Through_Deep_Learning_of_Call_Graphs
原文始发于微信公众号(国家网络威胁情报共享开放平台):DataCon2020 恶意代码分析冠军writeup
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论