首页
论坛
课程
招聘

[翻译]使用r2pipe, capstone和Gephi进行二进制数据聚类

2020-3-10 15:40 2577

[翻译]使用r2pipe, capstone和Gephi进行二进制数据聚类

2020-3-10 15:40
2577

简介

新型恶意软件通常会依据代码相似性, 或者某些厂商所谓恶意软件DNA或基因组分析来将其归类为某种已知恶意软件.

 

在这里, 我将描述一个简单的PoC, 例如“DnA GeNoMe mAlWaRe AtTrIbUtIoN EnGiNe”

 

它基于代码相似性采取两个特征来对恶意软件进行聚类:

  • 字符串
  • 掩饰过的基本块

代码

首先, 使用以下代码通过r2pipe管道连接Radare2来提取二进制程序中的字符串:

r2 = r2pipe.open(path, flags=['-2'])
r2.cmd('aaaa')

strings_json = r2.cmdj('izj')

strings = []

for s in strings_json:
        # FIXME: we "loose" the encoding in s['type'] here
        # so a utf-8 strings will be treated the same as the same string but in ascii
        s = base64.b64decode(s['string'])
        strings.append( [hashlib.sha256(s).hexdigest(), s.decode('utf-8')] )

接下来, 使用以下代码提取基本块:

results = r2.cmd('pdbj @@ *').split('\n')
results.remove('')

temp = set()

for r in results:
        temp.add(r)
temp = list(temp)

bb = []

for t in temp:
        tbb = json.loads(t)
        offset = tbb[0]['offset']
        code = b''
        for b in tbb:
                code += bytes.fromhex(b['bytes'])
        bb.append(code)

之后, 对每个基本块进行掩饰, 即将块内的立即数和偏移量用0覆写(“掩盖”). 因为在不同二进制程序的不同偏移位置的值是不同的, 比如该值是会随着二进制程序版本而变化的内存地址, 所以这样的掩饰能确保基本块能在不同的位置下也能进行比较. 我们可以通过以下代码使用capstone:

md = Cs(CS_ARCH_X86, CS_MODE_32)
md.detail = True
md.syntax = CS_OPT_SYNTAX_INTEL

def get_masked(inst):
        code = inst.bytes
        for i in range(inst.imm_offset, inst.imm_offset + inst.imm_size):
                code[i] = 0
        for i in range(inst.disp_offset, inst.disp_offset + inst.disp_size):
                code[i] = 0
        return code

masked_bb = []


for b in bb:
        masked_b = b''
        disasm_b = ''
        for i in md.disasm(b, 0):
                #print("%s\t%s\t%s\t%d\t%d" %(i.mnemonic, i.op_str, i.insn_name(), i.imm_size, i.disp_size))
                disasm_b += i.mnemonic + '\t' + i.op_str + '\n'
                force_mask = False
                for g in i.groups:
                        if i.group_name(g) in ['call', 'jump']:
                                force_mask = True
                if force_mask or i.imm_size > 1 or i.disp_size > 1:
                        masked_b += get_masked(i)
                else:
                        masked_b += i.bytes

        masked_bb.append([hashlib.sha256(masked_b).hexdigest(),disasm_b])

最后但同样重要的是, 我们输出以tab分隔的字符串和基本块:

for s in strings:
        print(path +'\t'+ s[0] +'\t'+ s[1])

for b in masked_bb:
        print(path +'\t'+ b[0] +'\t'+ b[1].replace('\n','; '))

就是这样. 上述代码都保存在这个文件里 extract_features.py.

 

为了能输入大量样本并将输出格式化为Gephi的可用格式, 我们可以使用 extract_all.sh:

#!/bin/bash

mkdir -p features
ls samples/* | while read input; do
output="$(echo "${input}" | sed 's/samples/features/g').csv"
python3 extract_features.py "${input}" > "${output}"
done

cat features/*.csv | awk -F'\t' '{print $1";basic-block-"$2}' > for_gephi.csv

如何使用

1.将 extract_features.pyextract_all.sh 以及一个包含有你样本的samples文件夹放置在同一目录下. 目录结构如下所示:

.
|-- extract_all.sh
|-- extract_features.py
`-- samples
    |-- CozyDuke_f16cfb7e54a11689fc1a37145b7ff28f17a1930c74324650e9a080ac87d69ac7
    |-- CozyDuke_f9987e6be134bf29458a336a76600a267e14b07a57032b6a8fc656f750e40ce5
    |-- Duqu2_6e09e1a4f56ea736ff21ad5e188845615b57e1a5168f4bdaebe7ddc634912de9
    |-- Duqu2_d12cd9490fd75e192ea053a05e869ed2f3f9748bf1563e6e496e7153fb4e6c98
    |-- Stuxshop_32159d2a16397823bc882ddd3cd77ecdbabe0fde934e62f297b8ff4d7b89832a
    |-- Stuxshop_c074aeef97ce81e8c68b7376b124546cabf40e2cd3aff1719d9daa6c3f780532
    |-- TSCookie_6d2f5675630d0dae65a796ac624fb90f42f35fbe5dec2ec8f4adce5ebfaabf75
    `-- TSCookie_96306202b0c4495cf93e805e9185ea6f2626650d6132a98a8f097f8c6a424a33

1 directories, 10 files

2.运行 ./extract_all.sh. 你的文件结构现在应该会像下面这样:

.
|-- extract_all.sh
|-- extract_features.py
|-- features
|   |-- CozyDuke_f16cfb7e54a11689fc1a37145b7ff28f17a1930c74324650e9a080ac87d69ac7.csv
|   |-- CozyDuke_f9987e6be134bf29458a336a76600a267e14b07a57032b6a8fc656f750e40ce5.csv
|   |-- Duqu2_6e09e1a4f56ea736ff21ad5e188845615b57e1a5168f4bdaebe7ddc634912de9.csv
|   |-- Duqu2_d12cd9490fd75e192ea053a05e869ed2f3f9748bf1563e6e496e7153fb4e6c98.csv
|   |-- Stuxshop_32159d2a16397823bc882ddd3cd77ecdbabe0fde934e62f297b8ff4d7b89832a.csv
|   |-- Stuxshop_c074aeef97ce81e8c68b7376b124546cabf40e2cd3aff1719d9daa6c3f780532.csv
|   |-- TSCookie_6d2f5675630d0dae65a796ac624fb90f42f35fbe5dec2ec8f4adce5ebfaabf75.csv
|   `-- TSCookie_96306202b0c4495cf93e805e9185ea6f2626650d6132a98a8f097f8c6a424a33.csv
|-- for_gephi.csv
`-- samples
    |-- CozyDuke_f16cfb7e54a11689fc1a37145b7ff28f17a1930c74324650e9a080ac87d69ac7
    |-- CozyDuke_f9987e6be134bf29458a336a76600a267e14b07a57032b6a8fc656f750e40ce5
    |-- Duqu2_6e09e1a4f56ea736ff21ad5e188845615b57e1a5168f4bdaebe7ddc634912de9
    |-- Duqu2_d12cd9490fd75e192ea053a05e869ed2f3f9748bf1563e6e496e7153fb4e6c98
    |-- Stuxshop_32159d2a16397823bc882ddd3cd77ecdbabe0fde934e62f297b8ff4d7b89832a
    |-- Stuxshop_c074aeef97ce81e8c68b7376b124546cabf40e2cd3aff1719d9daa6c3f780532
    |-- TSCookie_6d2f5675630d0dae65a796ac624fb90f42f35fbe5dec2ec8f4adce5ebfaabf75
    `-- TSCookie_96306202b0c4495cf93e805e9185ea6f2626650d6132a98a8f097f8c6a424a33

2 directories, 19 files

3.在Gephi中打开for_gephi.csv(依次点击File -> Import spreadsheet…)并作为邻接表导入.

 

 

4.在导入设置界面上只需点击“Finish”.

 

 

5.导入报告中应该不会有错误警告

 

 

6.接下来转到“Data Laboratory”, 选取所有的节点, 右击并选择“Edit node”.

 

7.在属性窗口设置大小为1.0以及颜色为浅灰色.

 

 

8.接下来, 仅选择样本节点并将它的大小设置为20.0以及颜色为红色.

 

 

 

9.转到概要(“Overview”)视图, 并选取“ForceAtlas 2”布局, 随后点击“Play”.

 

 

10.当聚类结束时点击“Stop”, 然后转到“Preview”点击“Refresh”. 随后导出你的聚类图.

 

 

更新1: 11. 多花些时间设置颜色并应用“Yifan Hu”布局, 以及短暂播放一下“ForceAtlas 2”以防重叠, 你就能得到非常酷的聚类图像:

 

结论

从结果图像可以看出, 两两相关的恶意样本都聚类在了一起, 即力导向图里通过基本块和字符串边而拉在一起.

 

显然, 与简单的PoC相比, 真实的聚类需要花费更多精力完成. 但是, 代码和字符串的相似性匹配是准确的. 因此, 如果掩饰后的基本块在两个样本均存在, 那么这意味着两个样本共享了某份代码, 至少共享了该基本块. 这里重要的是共享代码十分有意义, 例如许多二进制文件共享库代码. 虽然仅有少量指令的基本块显然也并不代表着有意义的代码共享, 但如果你在样本之间的关键函数中找到了一些共享的基本块代码, 那么它们很有可能就是相关的.

 

当你使用Gephi通过力导向图进行聚类时, 你还可以轻易计算出共享基本块的数量, 或根据特征的相似程度使用其他度量将不同的二进制程序归类到一起.

 

我希望这个简单的PoC能让人们开始进行共享代码和代码相似度分析的工作. 如果你在此基础上做了一些很酷的事, 请务必告知我 —— 我非常乐意看见这样的成果.


 

原文链接: Binary clustering with r2pipe, capstone and Gephi
翻译: 看雪翻译小组 Vancir
校对: 看雪翻译小组 Nxe



[求职]想求职找工作,请来看雪招聘投递简历!

最新回复 (3)
mb_xghoecki 2020-3-13 18:07
2
0
感谢分享
git_60961smithCoderLeo 2020-4-8 20:35
3
0
求问大佬radare2用的是哪个版本?我用的1.6.0,'pdbj @@ *'执行结果不是json格式,我改成了pdfj凑合用,但是有不少数据结构的问题,谢谢大佬
git_60961smithCoderLeo 2020-4-9 11:33
4
0
文中的特征提取代码可以在radare2-3.7.0上运行,4.3.0不行,问题出在字符串的编码上,3.7.0对字符串长度不足4的倍数的字符串添加=,使得base64可以顺利解码
游客
登录 | 注册 方可回帖
返回