IDA插件
trace_Native and stalker_trace_so
这俩插件都是对于Native层也就是对于so的trace,其中的源码的意思都差不多,这里是对应于俩插件在相同层次上不同的实现
stalker_trace_so:
trace中的function函数的地址获取:
通过UI界面,判断鼠标选择的区域来判定所需要进行的trace跟踪的范围值,并且取得函数指针的list表
if ctx.widget_type == idaapi.BWN_FUNCS: #这里说明是在函数窗口的位置
selected = [idaapi.getn_func(idx).start_ea for idx in ctx.chooser_selection]
else:
selected = idautils.Functions() #这里没用选定函数窗口,那么就是整个so文件函数进行
generate_js_script(selected) #selected这里得到的是函数指针的list表
trace中函数偏移地址的获取
def generate_js_script(func_list):
func_addr = []
func_name = []
so_base = get_imagebase()
for func_ea in func_list:
# thumb mode
if idc.get_sreg(func_ea, "T"): #这里判定是不是Thumb模式 是的话 addr+1
func_addr.append(hex(func_ea + 1 - so_base)) #这里就添加偏移地址了
else:
func_addr.append(hex(func_ea - so_base))
func_name.append('"{}"'.format(idc.get_func_name(func_ea)))
so_path, so_name = os.path.split(ida_nalt.get_input_file_path()) #ida_nalt.get_input_file_path()得到当前的路径,os.path.split将路径和文件名进行分割,得到so_path, so_name
利用python实现JS数据的替换
def generate_hook_code(template_js, func_addr, func_name, so_name):
replacements = {
"[func_addr]": ', '.join(func_addr),
"[func_name]": ', '.join(func_name),
"[so_name]": "%s" % so_name
}
return reduce(lambda acc, item: acc.replace(item[0], item[1]), replacements.items(), template_js)
这里普及一下reduce()这个工具函数reduce
函数的签名如下:
reduce(function, iterable, [initializer])
function
:是一个函数(或者其他可调用对象),它接收两个参数,并返回一个结果。iterable
:是一个可迭代对象(如列表、元组等)。[initializer]
(可选):是一个初始值,作为第一次迭代的“累积值”。
reduce(lambda acc, item: acc.replace(item[0], item[1]), replacements.items(), template_js)
function
:lambda acc, item: acc.replace(item[0], item[1]) 这里的item就是字典里的键值对,item[0]键,item[1]值
iterable
:replacements.items() 这个是传入的元组 只要这个元组里面还没遍历完,那么就继续遍历迭代
[initializer]
:template_js这个值是(即模板字符串),也就是要去被替换的位置
var func_addr = [[func_addr]];
var func_name = [[func_name]];
var so_name = "[so_name]";
/*
@param print_stack: Whether printing stack info, default is false.
*/4
var print_stack = false;
/*
@param print_stack_mode
- FUZZY: print as much stack info as possible
- ACCURATE: print stack info as accurately as possible
- MANUAL: if printing the stack info in an error and causes exit, use this option to manually print the address
*/
var print_stack_mode = "FUZZY";
function addr_in_so(addr){
var process_Obj_Module_Arr = Process.enumerateModules();
for(var i = 0; i < process_Obj_Module_Arr.length; i++) {
if(addr>process_Obj_Module_Arr[i].base && addr<process_Obj_Module_Arr[i].base.add(process_Obj_Module_Arr[i].size)){
console.log(addr.toString(16),"is in",process_Obj_Module_Arr[i].name,"offset: 0x"+(addr-process_Obj_Module_Arr[i].base).toString(16));
}
}
}
function hook_dlopen() {
Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"),
{
onEnter: function (args) {
var pathptr = args[0];
if (pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
//console.log(path);
if (path.indexOf(so_name) >= 0) {
this.is_can_hook = true;
}
}
},
onLeave: function (retval) {
if (this.is_can_hook) {
// note: you can do any thing before or after stalker trace so.
trace_so();
}
}
}
);
}
function trace_so(){
var times = 1;
var module = Process.getModuleByName(so_name);
var pid = Process.getCurrentThreadId();
console.log("start Stalker!");
Stalker.exclude({
"base": Process.getModuleByName("libc.so").base,
"size": Process.getModuleByName("libc.so").size
})
Stalker.follow(pid,{
events:{
call:false,
ret:false,
exec:false,
block:false,
compile:false
},
onReceive:function(events){
},
transform: function (iterator) {
var instruction = iterator.next();
do{
if (func_addr.indexOf(instruction.address - module.base) !== -1) {
console.log("call" + times + ":" + func_name[func_addr.indexOf(instruction.address - module.base)])
times = times + 1
if (print_stack) {
if (print_stack_mode === "FUZZY") {
iterator.putCallout((context) => {
console.log("backtrace:\\n"+Thread.backtrace(context, Backtracer.FUZZY).map(DebugSymbol.fromAddress).join('\\n'));
console.log('---------------------')
});
}
else if (print_stack_mode === "ACCURATE") {
iterator.putCallout((context) => {
console.log("backtrace:\\n"+Thread.backtrace(context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\\n'));
console.log('---------------------')
})
}
else if (print_stack_mode === "MANUAL") {
iterator.putCallout((context) => {
console.log("backtrace:")
Thread.backtrace(context, Backtracer.FUZZY).map(addr_in_so);
console.log('---------------------')
})
}
}
}
iterator.keep();
} while ((instruction = iterator.next()) !== null);
},
onCallSummary:function(summary){
}
});
console.log("Stalker end!");
}
setImmediate(hook_dlopen,0)
所以这个trace的是通过python去得到对应so文件函数的偏移地址和函数名,然后通过JS来实现生成的frida脚本来实现的
trace_Native
trace_Native 通过判断是否是存在于text段来查看是否记录地址,并且通过这个区间进行函数的获取
def getSegAddr():
textStart = []
textEnd = []
for seg in idautils.Segments():
if (idc.get_segm_name(seg)).lower() == '.text' or (
idc.get_segm_name(seg)).lower() == 'text':
tempStart = idc.get_segm_start(seg) #获取text段的起始地址
tempEnd = idc.get_segm_end(seg) #结束地址
textStart.append(tempStart)
textEnd.append(tempEnd)
return min(textStart), max(textEnd)
在代码段中寻找函数,并且将函数的偏移地址记录下来
def run(self, arg):
# 查找需要的函数
ea, ed = getSegAddr()
search_result = []
for func in idautils.Functions(ea, ed):
try:
functionName = str(idaapi.ida_funcs.get_func_name(func))
if len(list(idautils.FuncItems(func))) > 10:
# 如果是thumb模式,地址+1
arm_or_thumb = idc.get_sreg(func, "T")
if arm_or_thumb:
func += 1
search_result.append(hex(func))
except:
pass
生成路径和写入文件
so_path, so_name = getSoPathAndName()
search_result = [f"'{offset}'" for offset in search_result]
search_result = ",".join(search_result)
search_result = '['+search_result+']'
script_name = so_name.split(".")[0] + "_" + str(int(time.time())) +".txt" #so_name = "libnative.so",那么 so_name.split(".")[0] 将得到 "libnative",libnative_1684675100.txt
save_path = os.path.join(so_path, script_name) #/path/to/so/libnative_1684675100.txt
with open(save_path, "w", encoding="utf-8")as F:
F.write(search_result)
print("使用方法如下:")
print(f"frida-trace -UF -O {save_path}")
相对于Stalker_trace_so来说这个只是对于函数的地址进行了偏移地址的记录,相对的Stalker_trace_so利用了frida的打印堆栈信息的打印,可以在源码中去设置选择mode,但是起始打印出来很乱而且作用不好
但是有些插件也可以连着用,结合findhash和traceNatives插件合成的升级自吐算法
通过获取函数偏移地址,直接进行函数的HOOK,也挺有用的,不过这里就可以把trace_Native的里面对应判断代码段的大小的位置设置大一点,这样HOOK的函数就可以少一点,实现一个小范围的细致HOOK
function hook_suspected_function(targetSo) {
const funcs = ['0x152c','0x16c4','0x16fc','0x1734','0x177c','0x187c','0x18b4','0x18f4','0x193c','0x1974','0x19a8','0x1b60','0x1c5c','0x1e04','0x1ecc','0x1f14','0x1fb0','0x1ff0','0x21c4','0x2380','0x25ac','0x25ec','0x262c','0x2854','0x2a1c','0x2a8c','0x2d04','0x4264','0x4360','0x449c'];
for (var i in funcs) {
let relativePtr = funcs[i];
let funcPtr = targetSo.add(relativePtr);
hook_native_addr(funcPtr);
}
function hook_native_addr(funcPtr){
var module = Process.findModuleByAddress(funcPtr);
Interceptor.attach(funcPtr, {
onEnter: function(args){
this.args0 = args[0];
this.args1 = args[1];
this.args2 = args[2];
this.args3 = args[3];
this.logs = [];
this.logs.push("call " + module.name + "!" + ptr(funcPtr).sub(module.base) + "\n");
this.logs.push("this.args0 onEnter: " + print_arg(this.args0));
this.logs.push("this.args1 onEnter: " + print_arg(this.args1));
this.logs.push("this.args2 onEnter: " + print_arg(this.args2));
this.logs.push("this.args3 onEnter: " + print_arg(this.args3));
}, onLeave: function(retval){
this.logs.push("this.args0 onLeave: " + print_arg(this.args0));
this.logs.push("this.args1 onLeave: " + print_arg(this.args1));
this.logs.push("this.args2 onLeave: " + print_arg(this.args2));
this.logs.push("this.args3 onLeave: " + print_arg(this.args3));
this.logs.push("retval onLeave: " + retval + "\n");
console.log(this.logs);
}
});
}
标签:addr,插件,name,trace,so,func,print,IDA
From: https://www.cnblogs.com/ovo-fisherman/p/18610596