Barnum Trace 简介
这是一个能够利用Intel PT机制对虚拟机中运行的可执行程序进行信息记录并收集,使用它来对能够在虚拟机中动态运行的可执行文件或者恶意文档查看器的运行来判断分析其文件特性。
安装
代码分析
run.py
主要函数
transfer_sample():
- 进行job文件的拷贝传输。
- 使用qemu-nbd的mount功能,将镜像disk.qcow2与临时创建的tmp下文件挂载。
- 将文件拷贝到挂载的文件下,也就是将文件拷贝到虚拟机镜像中。
vol_cr3_lookup():
- 主要功能是进行获取cr3值的函数。
- 使用qemu-guest-memory命令在monitor中获取dump.qemu文件。
- 对于qemu文件,使用volatility进行psscan操作,获取进程信息,进行遍历获取proc_name的pid和cr3值。
post_processing():
- 使用qemu-nbd的mount功能,将镜像clean_disk.qcow2与临时创建的文件挂载。
- 将\装换为/,便于进行路径操作。
- 循环进行路径遍历,对其进行pefile.pe()操作,之后使用获取image函数操作获取数据,并存入文件中。
run_job() :
- 首先对一部分KAFL需要的运行环境进行识别,包括qemu-pt版本,qemu-nbd.
- 之后使用init_socket()初始化socket接口,绑定当前桥接网络ip。
- 检查需要的job即文档是否存在与input文件中。
- 创建disk.qcow2和disk-clean.qcow2,然后使用transfer_sample()将inputs文件中job复制到所创建的快照中。
- 调用Popen命令开启qemu虚拟机(这里要注意对虚拟机桥接网络的设置,需要使用相一致的命令打开虚拟机设置静态网络)
- 等待虚拟机socket接口连接,这里需要设置虚拟机桥接网络联通以及agent.py能够开机自启。
- 首先向虚拟机发送example.job文件,也就是需要虚拟机运行的命令或者host需要接受的字符串。
- 从socket连接中接受cmd命令,此时虚拟机已经打开了AcroRd32.exe,从而接收到vmi命令,可以获取此时的pid和cr3数值,调用vol_cr3_lookup()函数,可以获取cr3的值。
- 接着接收到pt信息,开始运行pt命令,包括cr3_filtering,set_file,enable,并可以关闭socket连接。
- 等待程序运行一段时间,查看trace文件中是否包含信息。
- 还需要进行后期处理。需要提取内存镜像,使用volatility ldrmodules可以查看一些隐藏的DLL文件,并输出到ofile文件中。调用post_processing函数,挂载clean_disk.qcow2镜像文件,提取出所有的在运行时调用的二进制文件,并将起记录到extract文件中。
agent.py
主要函数
main():
- 开启socket连接,运用socket连接获取job文档,确定虚拟机的运行工作。
recv_file():
- 接受job文件
parse_and_exec():
- 解析和运行job文件命令
- 包括对job命令进行判断,并顺序运行
monkey():
- 如果想模拟人类行为,包括点击‘I accept’,‘yes’,'run’等按钮进行点击,使得程序能够继续进行,可以添加monkey命令来模拟人类行为。
代码修改
对代码的修改主要是对run.py进行修改
- 修改jobs:原来的jobs主要是对inputs文件夹进行遍历获取jobs,对其进行修改为获取jobs文件夹内的文件,当前jobs内的文件为使用python自动生成的jobs文件。
- 由于不需要在命令中设置benign和malicious,所以将最后生成的info.txt进行删除
- 不需要向虚拟机中传递pdf数据,所以transfer_sample(用来传递pdf文件)这个函数不需要
- 添加ip_filtering命令
exec_cmd(qemu, "pt ip_filtering 0 0 "+str(ip_filtering_a)+" "+str(ip_filtering_b))
- 需要调用vol_cr3_lookup函数,在此函数中进行修改,添加volitility命令,使用如下的代码进行获取当前可执行文件的内存范围以设定ip_filtering_a以及ip_filtering_b。
##修改获取ip_filtering a, b
vol_ip = subprocess.Popen(['volatility',
'-f',trace_path + '/dump.qemu',
'--profile','Win7SP1x64',
'dlllist',
'--output=json',
'-p', str(pid) ],
stdout=subprocess.PIPE,
stderr=DEVNULL)
try:
#将输出的json信息进行获取
output = vol_ip.stdout.read()
res_ip = json.loads(output)
#print 'res_ip:'+res_ip
except Exception as ex:
vol_ip.terminate()
return (0, 0, 0, 0)
vol_ip.terminate()
#使用try expect方式来抛出异常,使程序能够继续运行
try:
for row_ip in res_ip['rows']:
#需要使用replace函数将路径转换为ubuntu下可以识别的路径
a = row_ip[4].replace('\\','/')
basename = os.path.basename(a)
if basename[-4:] == '.exe':
# 下面大括号中是为了将输出转换为16进制并保持为18位
ip_filtering_a = "{:#018X}".format(row_ip[1])
ip_filtering_b = "{:#018X}".format(row_ip[1]+row_ip[2])
print ip_filtering_a
print ip_filtering_b
except:
vol_ip.terminate()
return (0, 0, 0, 0)
- 比较常见的问题就是会出现程序被中断的程序,包括在虚拟机中无法找到对应的exe进程。此时需要继续对run.py进行修改,包括使用try except return等跑出异常或函数返回将数据进行处理,在return之前,将之前生成的trace文件夹数据进行删除,使用的subprocess.calll函数进行运行rm -rf 命令。