最新发布
-
安卓逆向-Native层相关HOOK 一、Native层相关hook 1. hook_so层 hook_so层只需要得到它的函数地址,有函数地址就能hook与主动调用,而得到函数地址的方式有两种; 1.1 方式一 通过frida提供的api来得到,该函数必须有符号的才可以; 有符号是指此函数是否出现在导出、导入、符号表里; 1.2 方式二 通过计算得到地址:so基址+函数在so中的偏移[+1](32位+1) 2. 各种枚举 2.1 枚举导入表 通过枚举导入表,可以得到出现在导入表中的函数地址;(enumerateImports) var imports = Module.enumerateImports("libifeng_secure.so"); // console.log(JSON.stringify(imports[0])) for (var i = 0; i < imports.length; i++) { // if (imports[i].name == "atoi") { // console.log(JSON.stringify(imports[i])); console.log('导入函数名--->>>',imports[i].name); console.log('导入函数地址--->>>',imports[i].address); console.log('-----------------------------------------------------------------') // break; // } } 以某so为例,得到的结果如下: 2.2 枚举导出表 通过枚举导出表,可以得到出现在导出表中的函数地址,与导入表同理,api不同;(enumerateExports) var imports = Module.enumerateExports("libifeng_secure.so"); // console.log(JSON.stringify(imports[0])) for (var i = 0; i < imports.length; i++) { // if (imports[i].name == "atoi") { // console.log(JSON.stringify(imports[i])); console.log('导出函数名--->>>',imports[i].name); console.log('导出函数地址--->>>',imports[i].address); console.log('-----------------------------------------------------------------') // break; // } } 以某so为例结果如下: 2.3 枚举符号表 通过枚举符号表,可以得到出现在符号表中的函数地址; var symbols = Module.enumerateSymbols("libifeng_secure.so"); // console.log(JSON.stringify(symbols[0])) for (var i = 0; i < symbols.length; i++) { console.log('符号表函数名--->>>',symbols[i].name); console.log('符号表函数地址--->>>',symbols[i].address); console.log('-----------------------------------------------------------------') }2.4 枚举模块 通过枚举模块,再枚举模块里面的导出表,可以快速找到某个导入函数出自哪个so; // 枚举进程中已加载的模块 var modules = Process.enumerateModules(); var module = modules[0].enumerateExports() for(let i = 0; i < module.length; i++){ console.log('枚举进程函数名--->>>',module[i].name); console.log('枚举进程函数地址--->>>',module[i].address); console.log('-----------------------------------------------------------------') } // console.log(JSON.stringify(modules[0].enumerateExports())); 通过枚举模块得到的是一个数组,取第一个后就可以调用上述三个方法; 3. hook导出函数 在so导出表里的函数,可以通过frida提供的api来获取函数地址,Module.findExportByName("xxxx.so", "add"),函数名以汇编中出现的为准; // 导出函数的hook var funcAddr = Module.findExportByName("libencryptlib.so", "_ZN7MD5_CTX11MakePassMD5EPhjS0_"); console.log('函数地址--->>>',funcAddr); Interceptor.attach(funcAddr, { onEnter: function (args) { console.log("funcAddr onEnter args[1]: ", hexdump(args[1])); console.log("funcAddr onEnter args[2]: ", args[2].toInt32()); this.args3 = args[3]; }, onLeave: function (retval) { console.log("funcAddr onLeave args[3]: ", hexdump(this.args3)); } }); 在这里以口袋48为例: 在这里我们的前置条件是,传了三个参数,这里初始打印第三个参数时,发现结果全是0,这里大概就是一个缓冲区,这里是因为C语言里,非常喜欢将参数当作返回值使用,那么这里我们就需要在离开的时候再读取内存; this.args3 = args[3]; console.log("funcAddr onLeave args[3]: ", hexdump(this.args3)); 在这里我们知道它是MD5,我们对比一下结果; 可以发现与hook到的结果是一致的; 4. 获取模块基址 在此的前提是,我们需要的函数不在三个表里,我们就没办法直接使用frida提供的api来获取函数地址,在此需要计算它的函数地址; 计算公式如下: so基址+函数在so中的偏移[+1] 32位则 +1 因此,我们需要先得到so基址,也就是模块基址; 4.1 findModuleByName 使用findModuleByName时,指明so文件即可,得到的是一个module对象,可以进行转换,也可以直接.base取地址; var module1 = Process.findModuleByName("libencryptlib.so"); console.log("module1对象--->>>",JSON.stringify(module1)); console.log("module1基址--->>>", module1.base); 直接.base取的就是基址; 4.2 getModuleByName 与findModuleByName类似,得到的也是一个对象; var module2 = Process.getModuleByName("libencryptlib.so"); console.log("module2对象--->>>",JSON.stringify(module2)); console.log("module2基址--->>>", module2.base); 结果如下: 4.3 findBaseAddress(推荐) 与前两个有所不同,这里得到的直接就是函数地址,也就是基址; var soAddr = Module.findBaseAddress("libencryptlib.so"); console.log("soAddr基址--->>>", soAddr); 这里就无需再去操作,返回值就是地址; 4.4 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); } } 不过此方式用的较少; 也可以通过地址找模块,这也就可以调用一些方法; var module = Process.findModuleByAddress(Module.findBaseAddress("libencryptlib.so")); console.log("module " + module.name + " " + module.base);5. 函数地址计算 5.1 偏移 上述描述可知,函数地址计算如下: so基址+函数在so中的偏移[+1] [ 32位则 +1] 基址我们已经能够获取到了,那就剩下偏移; 以口袋48app为例,这里我们需要找某函数的偏移; 在其定义位置按下tab,则可转到汇编,界面如下: 实际上这就是它的偏移,它是相对so基址的偏移;得到这个偏移就可以计算出函数地址了; 在这里是否+1呢,在目前来说,大部分是64位的so,此时则不需要加,若为32位则需要加; 5.2 地址计算 依据公式,首先得到基址,再加上偏移; var soAddr = Module.findBaseAddress("libencryptlib.so"); console.log(ptr(soAddr).add(0xxx)); soAddr得到是基址,其实也就是指针,在这里通过add (sub方法为减) 加上偏移,ptr实际上就是指针,也可以不加ptr; 而add里面是一个数值,不是字符串,这里是十六进制,则需要加上0x; 何时加ptr呢,若你的soAddr为具体的数值,则需要加,如: var soAddr = Module.findBaseAddress("libencryptlib.so"); var so = 0x72777a6000; console.log("soAddr基址--->>>" + soAddr); console.log(ptr(so).add(0x1FA38)); // new NativePointer() == ptr() 在一个具体的数值add时,由于不是指针,则无法调用add方法,加上ptr即可,也是同样可以得到地址的; 6. hook任意函数 根据上述条件,我们已经可以hook任意的函数了,得到一个地址即可; var soAddr = Module.findBaseAddress("libencryptlib.so"); // var so = 0x72777a6000; console.log("soAddr基址--->>>" + soAddr); // console.log(ptr(so).add(0x1FA38)); // new NativePointer() var funcAddr = soAddr.add(0x1FA38); console.log("funcAddr函数地址--->>>" + funcAddr); Interceptor.attach(funcAddr, { onEnter: function (args) { console.log("funcAddr onEnter args[1]: ", hexdump(args[1])); console.log("funcAddr onEnter args[2]: ", args[2].toInt32()); this.args3 = args[3]; }, onLeave: function (retval) { console.log("funcAddr onLeave args[3]: ", hexdump(this.args3)); } }); 其余就与hook导出函数是类似的;