* 实在有点尴尬,不太会用,谢谢版主大大的修改~_~,一直没编辑过,预览也没达到自己的目标,后面内容,我在回帖中,补充吧。
--------------------------------------------------
还原的算法 的验证脚本:
1,脚本开发背景:
在逆向过程中,我们对照汇编代码/ida伪代码 等翻译了算法。然后需要知道这个算法是不是正确的。平常的做法,就是一边继续调试一遍验证。对于代码比较少的,调试几次,就能搞定最后搞定;而对于算法复杂点,代码多点的,调试一遍,就需要花费不少经历,何况需要调试多次,才能验证整个算法的ok。工作量很大的。。。
2,脚本原理:
1,不论是原算法,还是翻译的算法,其数据流必须一样。(所以,借助lldb-trace的结果,拿到数据流... 也就是拿到trace的log信息。。。)
2,由于python sys模块中,有settrace相关的函数。如果设置好sys.settrace(dest_fun()),在dest_fun()中,我们就能监控到相关的信息。原理就是 用 python 调试器,调试你的python代码....大概就是这样。(别的语言,不太熟悉,恰好python,简单方便,所以就选他了..)
3,由于test_fun()监控的是所有的 python代码,而我们只用监测某些变量,所以引入一个api,check_value(value,flag) 表示监控此value,同时,让注册的回调函数,监控他。
4,根据 flag,解析lldb-trace的log信息,拿到所有的数据流...
5,然后判断数据流,在算法中,是否一一对应。。。
3,怎么使用该脚本:
1,在ida中,找到需要还原的算法(可以是汇编,也可以是伪代码),翻译成对应的python代码
2,利用lldbTrace.py脚本,把需要翻译的函数trace一哈
3,在ida伪代码中,切换到对应的汇编,对照trace结果,确定具体检测的地址。因为trace,当前打印的是上一句代码执行后的寄存器值。所以,我们把check的地址,定义为 0x10000393c,寄存器为 w8.
4,定义检测的 变量 Check_0x10000393c_w8 = ‘Check_0x10000393c_w8’ 。在翻译的代码中,添加检测函数 check_value(ret,Check_0x10000393c_w8) 。在解析tracelog文件之前,设置check的相关信息 set_trace_data(Check_0x10000393c_w8)
5,用python 调用此脚本,并传入 tracelog文件的路径。结果如下:
4,具体代码:
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
#@file : wtpytracer.py
#@Time : 2021/07/27 18:17:18
#@AuThor : wt
import re
import sys
import inspect
from collections import OrderedDict
class TracebackFancy:
def __init__(self, traceback):
self.t = traceback
def getFrame(self):
return FrameFancy(self.t.tb_frame)
def getLineNumber(self):
return self.t.tb_lineno if self.t is not None else None
def getNext(self):
return TracebackFancy(self.t.tb_next)
def __str__(self):
if self.t is None:
return ""
str_self = "%s @ %s" % (
self.getFrame().getName(), self.getLineNumber())
return str_self + "\n" + self.getNext().__str__()
class ExceptionFancy:
def __init__(self, frame):
self.etraceback = frame.f_exc_traceback
self.etype = frame.exc_type
self.evalue = frame.f_exc_value
def __init__(self, tb, ty, va):
self.etraceback = tb
self.etype = ty
self.evalue = va
def getTraceback(self):
return TracebackFancy(self.etraceback)
def __nonzero__(self):
return self.etraceback is not None or self.etype is not None or self.evalue is not None
def getType(self):
return str(self.etype)
def getValue(self):
return self.evalue
class CodeFancy:
def __init__(self, code):
self.c = code
def getArgCount(self):
return self.c.co_argcount if self.c is not None else 0
def getFilename(self):
return self.c.co_filename if self.c is not None else ""
def getVariables(self):
return self.c.co_varnames if self.c is not None else []
def getName(self):
return self.c.co_name if self.c is not None else ""
def getFileName(self):
return self.c.co_filename if self.c is not None else ""
class ArgsFancy:
def __init__(self, frame, arginfo):
self.f = frame
self.a = arginfo
def __str__(self):
args, varargs, kwargs = self.getArgs(), self.getVarArgs(), self.getKWArgs()
ret = ""
count = 0
size = len(args)
for arg in args:
ret = ret + ("%s = %s" % (arg, args[arg]))
count = count + 1
if count < size:
ret = ret + ", "
if varargs:
if size > 0:
ret = ret + " "
ret = ret + "varargs are " + str(varargs)
if kwargs:
if size > 0:
ret = ret + " "
ret = ret + "kwargs are " + str(kwargs)
return ret
def getNumArgs(wantVarargs=False, wantKWArgs=False):
args, varargs, keywords, values = self.a
size = len(args)
if varargs and wantVarargs:
size = size + len(self.getVarArgs())
if keywords and wantKWArgs:
size = size + len(self.getKWArgs())
return size
def getArgs(self):
args, _, _, values = self.a
argWValues = OrderedDict()
for arg in args:
argWValues[arg] = values[arg]
return argWValues
def getVarArgs(self):
_, vargs, _, _ = self.a
if vargs:
return self.f.f_locals[vargs]
return ()
def getKWArgs(self):
_, _, kwargs, _ = self.a
if kwargs:
return self.f.f_locals[kwargs]
return {}
class FrameFancy:
def __init__(self, frame):
self.f = frame
def getCaller(self):
return FrameFancy(self.f.f_back)
def getLineNumber(self):
return self.f.f_lineno if self.f is not None else 0
def getCodeInformation(self):
return CodeFancy(self.f.f_code) if self.f is not None else None
def getExceptionInfo(self):
return ExceptionFancy(self.f) if self.f is not None else None
def getName(self):
return self.getCodeInformation().getName() if self.f is not None else ""
def getFileName(self):
return self.getCodeInformation().getFileName() if self.f is not None else ""
def getLocals(self):
return self.f.f_locals if self.f is not None else {}
def getArgumentInfo(self):
return ArgsFancy(
self.f, inspect.getargvalues(
self.f)) if self.f is not None else None
class TracerClass:
def callEvent(self, frame):
pass
def lineEvent(self, frame):
pass
def returnEvent(self, frame, retval):
pass
def exceptionEvent(self, frame, exception, value, traceback):
pass
def cCallEvent(self, frame, cfunct):
pass
def cReturnEvent(self, frame, cfunct):
pass
def cExceptionEvent(self, frame, cfunct):
pass
tracer_impl = TracerClass()
data_dic = {}
old_trace_func = None
def parser_flag(flag):
import re
aa = re.split(r'_',flag)
if len(aa) != 3 :
return None,None
return aa[1],aa[2]
class CheckFunctionTracer():
def callEvent(self, frame):
if 'check_value' == frame.getName():
flag = frame.getArgumentInfo().getArgs()['check_flag']
value = frame.getArgumentInfo().getArgs()['value']
addr,register = parser_flag(flag)
if addr in data_dic and register in data_dic[addr]:
run_index = data_dic[addr][register]['run_index']
data_len = len(data_dic[addr][register]['data'])
if run_index >= data_len:
print('*** err : at address : {} . run_index : {} out of rang'.format(addr,run_index))
return
if value == data_dic[addr][register]['data']['{}'.format(run_index + 1)] :
print('check : {} at {} times,match.'.format(addr,run_index + 1))
data_dic[addr][register]['run_index'] = run_index + 1
# print("->>LoggingTracer : call " + frame.getName() + " from " + frame.getCaller().getName() + " @ " + str(frame.getCaller().getLineNumber()) + " args are " + str(frame.getArgumentInfo()))
# @ check_flag 为 携带了地址,和寄存器名称
# @ value 为当前需要 check 的值
# 在 sys.settracer设置的回调中,只接管此函数
def check_value(value,check_flag):
pass
def set_trace_data(check_flag):
global data_dic
addr,register = parser_flag(check_flag)
if not addr or not register :
print('err : check_flag is wrong.')
return
if addr in data_dic:
data_dic[addr][register] = {
'data':{},
'run_index':0
}
else:
addr_dic = {
register:{
'data':{},
'run_index':0
}
}
data_dic[addr] = addr_dic
def add_data_in_data_dic(addr,register,value):
global data_dic
cur_reg_dic = data_dic[addr][register]
data_len = len(cur_reg_dic['data'])
data_dic[addr][register]['data']['{}'.format(data_len + 1)] = value
def parser_trace_log_file(fileName):
global data_dic
file = open(fileName)
while True:
lines = file.readlines(100000)
if not lines:
break
for line in lines:
matchObj = re.match(r'\s*(\S+)\s+',line,re.M|re.I)
if matchObj:
addr = str(matchObj.group()).replace(' ','')
if addr in data_dic:
reg = data_dic[addr]
for register in data_dic[addr].keys():
register_out = re.findall(register +r' : (\S+)',line)
if register_out:
register_value = int(register_out[0],16)
add_data_in_data_dic(addr,register,register_value)
file.close()
# {'1234':{'1':0,"2":1}} # flag : {...} address:{'x0':{data:{},run_index:0},'x1':{data:{},run_index:0}}
def the_tracer_check_data(frame, event, args = None):
global data_dic
global tracer_impl
code = frame.f_code
func_name = code.co_name
line_no = frame.f_lineno
if tracer_impl is None:
print('@@@ tracer_impl : None.')
return None
if event == 'call':
tracer_impl.callEvent(FrameFancy(frame))
return the_tracer_check_data
def enable(tracer_implementation=None):
global tracer_impl,old_trace_func
if tracer_implementation:
tracer_impl = tracer_implementation # 传递 工厂实力的对象
old_trace_func = sys.gettrace()
sys.settrace(the_tracer_check_data) # 注册回调到系统中
def check_run_ok():
global data_dic
for addr,addr_dic in data_dic.items():
for _,reg_dic in addr_dic.items():
if reg_dic['run_index'] == len(reg_dic['data']):
print('->>> at {} check value is perfect.'.format(addr))
else:
print('*** err : at {} check {} times.'.format(addr,reg_dic['run_index']))
def disable():
check_run_ok()
global old_trace_func
sys.settrace(old_trace_func)
demo,如下:
#!/usr/bin/python3
# -*- encoding: utf-8 -*-
#@File : test.py
#@Time : 2021/07/29 15:15:38
#@Author : wt
from wtpytracer import *
####################
## command :
## python3 test.py ~/Desktop/1627533881instrace.log
## 定义需要检测的 变量 : flag + '_' + 地址 + '_' + 寄存器
Check_0x10000393c_w8 = 'Check_0x10000393c_w8'
### 翻译的测试代码
def f(x):
ret = 0
for index in range(x):
ret = ret + index
check_value(ret,Check_0x10000393c_w8) # check ret 和 0x10000393c 的 w8 的寄存器值
return ret + x
if __name__ == '__main__':
import sys
args_list = sys.argv
if len(args_list) != 2 :
exit()
file_name = args_list[1]
try:
set_trace_data(Check_0x10000393c_w8)
parser_trace_log_file(file_name)
enable(CheckFunctionTracer())
f(5)
finally:
disable()