asm_to_csv/exe2json.py

197 lines
7.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import os
import r2pipe
import re
import hashlib
import log_utils
import json
def calc_sha256(file_path):
with open(file_path, 'rb') as f:
bytes = f.read()
sha256obj = hashlib.sha256(bytes)
sha256 = sha256obj.hexdigest()
return sha256
def extract_opcode(disasm_text):
"""
从反汇编文本中提取操作码和操作数
正则表达式用于匹配操作码和操作数,考虑到操作数可能包含空格和逗号
"""
match = re.search(r"^\s*(\S+)(?:\s+(.*))?$", disasm_text)
if match:
opcode = match.group(1)
# operands_str = match.group(2) if match.group(2) is not None else ""
# split_pattern = re.compile(r",(?![^\[]*\])") # 用于切分操作数的正则表达式
# operands = split_pattern.split(operands_str)
# return opcode, [op.strip() for op in operands if op.strip()]
return opcode
return ""
def get_graph_cfg_r2pipe(r2pipe_open):
# CFG提取
acfg_item = []
try:
# 获取函数列表
function_list = r2pipe_open.cmdj("aflj")
for function in function_list:
# 局部函数内的特征提取
node_list = []
edge_list = []
temp_edge_list = []
block_list = r2pipe_open.cmdj("afbj @" + str(function['offset']))
block_number = len(block_list)
block_feature_list = []
for block in block_list:
node_list.append(block["addr"])
# 获取基本块的反汇编指令
disasm = r2pipe_open.cmdj("pdj " + str(block["ninstr"]) + " @" + str(block["addr"]))
if disasm:
for op in disasm:
if op["type"] == "invalid":
continue
# TODO :这里需要处理指令的特征提取
block_feature = ''
block_feature_list.append(block_feature)
# 处理跳转指令
if "jump" in op and op["jump"] != 0:
temp_edge_list.append([block["addr"], op["jump"]])
for temp_edge in temp_edge_list:
if temp_edge[1] in node_list:
edge_list.append(temp_edge)
acfg = {
'block_number': block_number,
'block_edges': [[d[0] for d in edge_list], [d[1] for d in edge_list]],
'block_features': block_feature_list
}
acfg_item.append(acfg)
return True, "二进制可执行文件解析成功", acfg_item
except Exception as e:
return False, e, None
# for block in block_list:
# node_list.append(block["addr"])
#
# # 获取基本块的反汇编指令
# disasm = r2pipe_open.cmdj("pdj " + str(block["ninstr"]) + " @" + str(block["addr"]))
# node_info = []
# if disasm:
# for op in disasm:
# if op["type"] == "invalid":
# continue
# opcode, operands = extract_opcode(op["disasm"])
# # 处理跳转指令
# if "jump" in op and op["jump"] != 0:
# temp_edge_list.append([block["addr"], op["jump"]])
# node_info.append([op["offset"], op["bytes"], opcode, op["jump"]])
# else:
# node_info.append([op["offset"], op["bytes"], opcode, None])
# node_info_list.append(node_info)
# 完成 CFG 构建后, 检查并清理不存在的出边
# 获取排序后元素的原始索引
# sorted_indices = [i for i, v in sorted(enumerate(node_list), key=lambda x: x[1])]
# # 根据这些索引重新排列
# node_list = [node_list[i] for i in sorted_indices]
# node_info_list = [node_info_list[i] for i in sorted_indices]
#
# return True, "二进制可执行文件解析成功", node_list, edge_list, node_info_list
# except Exception as e:
# return False, e, None, None, None
def get_graph_fcg_r2pipe(r2pipe_open):
# FCG提取
try:
function_list = r2pipe_open.cmdj("aflj")
node_list = []
func_name_list = []
edge_list = []
temp_edge_list = []
function_num = len(function_list)
for function in function_list:
func_name_list.append(function["name"])
r2pipe_open.cmd(f's ' + str(function["offset"]))
pdf = r2pipe_open.cmdj('pdfj')
if pdf is None:
continue
node_bytes = ""
node_opcode = ""
for op in pdf["ops"]:
if op["type"] == "invalid":
continue
node_bytes += op["bytes"]
opcode = extract_opcode(op["disasm"])
node_opcode += opcode + " "
if "jump" in op and op["jump"] != 0:
temp_edge_list.append([function["offset"], op["jump"]])
node_list.append(function["offset"])
# 完成 FCG 构建后, 检查并清理不存在的出边
for temp_edge in temp_edge_list:
if temp_edge[1] in node_list:
edge_list.append(temp_edge)
sub_function_name_list = ('fcn.', 'loc.', 'main', 'entry')
func_name_list = [func_name for func_name in func_name_list if not func_name.startswith(sub_function_name_list)]
return True, "二进制可执行文件解析成功", function_num, edge_list, func_name_list
except Exception as e:
return False, e, None, None, None
def get_r2pipe(file_path):
try:
r2 = r2pipe.open(file_path, flags=['-2'])
r2.cmd("aaa")
r2.cmd('e arch=x86')
return r2
except Exception as e:
return None
def init_logging():
log_file = "./out/exe2json.log"
logging = log_utils.setup_logger('exe2json', log_file)
return logging
def exe_to_json(file_path, output_path):
logging = init_logging()
r2 = get_r2pipe(file_path)
fcg_Operation_flag, fcg_Operation_message, function_num, function_fcg_edge_list, function_names = get_graph_fcg_r2pipe(r2)
cfg_Operation_flag, cfg_Operation_message, cfg_item = get_graph_cfg_r2pipe(r2)
file_fingerprint = calc_sha256(file_path)
if fcg_Operation_flag and cfg_Operation_flag:
json_obj = {
'hash': file_fingerprint,
'function_number': function_num,
'function_edges': [[int(d[0]) for d in function_fcg_edge_list],
[int(d[1]) for d in function_fcg_edge_list]],
'acfg_list': cfg_item,
'function_names': function_names
}
else:
logging.error(f"二进制可执行文件解析失败 文件地址{file_path}")
if not fcg_Operation_flag:
logging.error(f"fcg错误{fcg_Operation_message}")
if not cfg_Operation_flag:
logging.error(f"cfg错误{cfg_Operation_message}")
return False
r2.quit()
result = json.dumps(json_obj,ensure_ascii=False)
with open(os.path.join(output_path, file_fingerprint + '.jsonl'), 'w') as out:
out.write(result)
out.close()
return True
if __name__ == '__main__':
test_file_path = '/mnt/d/bishe/exe2json/sample/VirusShare_0a3b625380161cf92c4bb10135326bb5'
exe_to_json(test_file_path, './out/json')