枚举各种
枚举导入表
通过枚举导入表,可以得到出现在导入表中的函数地址
var imports = Module.enumerateImports("libencryptlib.so"); for(var i = 0; i < imports.length; i++){ console.log(JSON.stringify(imports[i])); }
|
枚举导出表
通过枚举导出表,可以得到出现在导出表中的函数地址
var exports = Module.enumerateImports("libencryptlib.so"); for(var i = 0; i < exports.length; i++){ console.log(JSON.stringify(exports[i])); }
|
枚举符号表
通过枚举符号表,可以得到出现在符号表中的函数地址
var symbols = Module.enumerateSymbols("libencryptlib.so"); for(var i = 0; i < symbols.length; i++){ console.log(JSON.stringify(symbols[i])); }
|
枚举模块
通过枚举模块,再枚举模块里面的导出表,可以快速找到某个导入函数出自哪个so
var modules = Process.enumerateModules(); console.log(JSON.stringify(modules[0].enumerateExports()[0]));
|
为什么是导出表呢?
因为每个模块中的导入函数,必然是来自另一个模块的,所以会出现在另一个模块的导出表中
exports和symbols的区别
exports解析的是SHT_DYNSYM
symbols解析的是SHT_SYMTAB
SHT_SYMTAB比SHT_DYNSYM更全,SYMTAB是SYMTAB的子集,不是so运行所必须的,一般会被去掉
Hook导出函数
这里以旧版的口袋48为例,新版的加了 frida 检测,以我目前的水平还无法hook。。。。
findExportByName
在so导出表里的函数,可以通过frida提供的api来获取函数地址
var funcAddr = Module.findExportByName("libencryptlib.so", "_ZN7MD5_CTX11MakePassMD5EPhjS0_"); console.log(funcAddr);
|
函数名以汇编中出现的为准
Interceptor.attach
得到函数地址后,可以使用Interceptor.attach来对函数进行hook
Interceptor.attach(funcAddr,{ onEnter: function (args) { console.log("funcAddr onEnter args[0]: ",hexdump(args[0])); console.log("funcAddr onEnter args[1]: \n",hexdump(args[1])); console.log("funcAddr onEnter args[2]: ",args[2].toInt32()); console.log("funcAddr onEnter args[3]: ",hexdump(args[3])); this.args3 = args[3]; }, onLeave: function (retval){ console.log("funcAddr onLeave arg3: ", hexdump(this.args3)); } });
|
模块基址的几种获取方式
首先,为什么要学这个?
因为在导入表、导出表、符号表里找不到的函数,地址需要自己计算
Process.findModuleByName()
var module = Process.findModuleByName("libencryptlib.so"); console.log(module.base);
|
Process.getModuleByName()
var module2 = Process.getModuleByName("libencryptlib.so"); console.log(module2.base);
|
Module.findBaseAddress()
var soAddr = Module.findBaseAddress("libencryptlib.so"); console.log("soAddr: ", soAddr);
|
Process.enumerateModules()
var modules = Process.enumerateModules(); for(let i = 0; i< modules.length; i++) { if(modules[i].name == "libencryptlib.so") { console.log(modules[i].name + " " + modules[i].base); } }
|
Process.findModuleByAddress()
var module = Process.findModuleByAddress(Module.findBaseAddress("libencryptlib.so")); console.log("module: " + module.name + " " + module.base);
|
Process.getModuleByAddress()
var module = Process.getModuleByAddress(Module.findBaseAddress("libencryptlib.so")); console.log("module: " + module.name + " " + module.base);
|
函数地址的计算
thumb指令
so基址 + 函数在so中的偏移+1
arm指令
so基址 + 函数在so中的偏移
判断方式
在安卓中,32位的so中的函数,基本都是thumb指令;64的so中的函数,基本都是arm指令
也可以通过显示汇编指令对应的opcode bytes,来判断:
options -> general -> Number of opcode bytes (non-graph) 4
|
arm指令位4个字节,如果函数中有些指令是两个字节,那么函数地址计算就需要+1
代码示范
var soAddr = Module.findBaseAddress("libencryptlib.so"); console.log(soAddr.add(0x1FA38));
|
SoHook模板
function print_arg(addr){ var module = Process.findRangeByAddress(addr); if(module != null) return hexdump(addr) + "\n"; return ptr(addr) + "\n"; } function hook_native_addr(funcPtr, paramsNum){ var module = Process.findModuleByAddress(funcPtr); Interceptor.attach(funcPtr, { onEnter: function(args){ this.logs = []; this.params = []; this.logs.push("call " + module.name + "!" + ptr(funcPtr).sub(module.base) + "\n"); for(let i = 0; i < paramsNum; i++){ this.params.push(args[i]); this.logs.push("this.args" + i + " onEnter: " + print_arg(args[i])); } }, onLeave: function(retval){ for(let i = 0; i < paramsNum; i++){ this.logs.push("this.args" + i + " onLeave: " + print_arg(this.params[i])); } this.logs.push("retval onLeave: " + print_arg(retval) + "\n"); console.log(this.logs); } }); }
var soAddr = Module.findBaseAddress("libencryptlib.so"); var funcAddr = soAddr.add(0x1FA38); hook_native_addr(funcAddr, 4);
|