andorid jar/库源码解析之frida体验
Frida体验:
作用:
android手机上可以对,java和so层代码,进行hook.监控数据和处理内存数据。
官译:面向开发人员、逆向工程师和安全研究人员的动态工具工具包。
栗子:
运行步骤:
1、https://github.com/frida/frida/releases 下载适合需要运行环境的可执行程序。我这里是arm64,所以下载了一个最新版本的frida64位。
2、PC端,安装python环境,直接从官网下载一个python安装包安装即可,我这里安装的是python3, 安装包会自己配置环境变量,cmd运行 python,看看有没有,有就是安装上了环境变量了。
3、使用 pip install frida 安装 frida 环境,基于 python的。 再安装 pip install frida-tools ,我看大部分都是按照了。tools的。
4、一切ok。开始跑了,,,把步骤1中得到的,可执行文件,拷贝到手机上面 adb push D:\xxx\xxx\frixxxx /data/local/tmp/
5、因为push上去的可执行文件默认没有权限,下一步授权。。adb shell 进入手机 su,得到root权限。。cd /data/local 进入 local文件夹 chmod 777 -R * 授权可执行。。 再 cd tmp 进入 tmp 执行 :./frXXX回车运行exe
效果如下图:
6、端口转发,因为要把手机的数据和本地电脑互通,需要设置端口转发,两个端口:执行两条命令
adb forward tcp:27042 tcp:27042 adb forward tcp:27043 tcp:27043
7、接着就可以允许python了。
8、代码示例:
import frida, sys jsCode = """ Java.perform(function () { var impl = Java.use("com.android.test.TestUtil"); impl.test1.implementation = function () { send("test1 called!"); this.test1(); }; impl.test2.implementation = function () { send("test2 called!"); }; impl.test3.implementation = function (a) { send("test3 called!"); }; impl.test4.implementation = function (a,b,c) { send("test4 called!"); return this.test4(a,b,c); }; impl.test5.overload("int").implementation = function (a) { send("test5 1 called!"); }; impl.test5.overload("int", "java.lang.String").implementation = function (a,b) { send("test5 2 called!"); }; }); """ def message(message, data): if message["type"] == 'send': print(u"[*] {0}".format(message['payload'])) else: print(message) process = frida.get_remote_device().attach("com.android.test") script= process.create_script(jsCode) script.on("message", message) script.load() sys.stdin.read()
代码中,把js写到了一个string变量中。多行都是用这个固定格式。
对test1方法进行了hook同时,在后面也对 test1方法进行了调用,如果不调用,方法就不会执行了。参考 test2
test3针对单个参数的情况,test4针对多个参数。test5,针对不同参数进行了重载,需要指明参数类型,上面做了处理。
执行js的send方法,内容回呗,发往,scritp的绑定方法 。def message(message, data)在里面,进行输出,你也可以在js中使用 console.log进行输出参数。
针对是复杂类型的情况,可以直接a.XXXX拿到复杂类型的变量。
8.2、其他js
// 遍历所有js.指定名称,过滤 Java.perform(function(){ Java.enumerateLoadedClasses({ onMatch: function(className) { if(className.toString().indexOf("down") >= 0){ send(className+'\"));'); } }, onComplete:function(){ send("done"); } }); });
9:so,hook处理。
import frida, sys jsCode3 = """ Java.perform(function(){ var exports = Module.enumerateExportsSync("xxx.so"); for(var i = 0; i < exports.length; i++) { if(exports[i] && exports[i].name != undefined){ if(exports[i].name.indexOf("rapidjson")>=0){ var nativePointer = new NativePointer(exports[i].address); send("exports " + exports[i].name + " nativePointer " + nativePointer); Interceptor.attach(nativePointer, { onEnter: function(args) { send(this.context.pc + " called! "); send("context " + JSON.stringify(this.context)); // 保存长度,在返回方法进行调用 this.fd = args[1]; // send(nativePointer + " arg0 " +args[0] + " arg1 " +args[1] + " arg2 " +args[2]); }, onLeave:function(retval){ send(this.context.pc + " retval " +retval); // dump出内容 var size = parseInt(this.fd); if(size > 0){ const r = Memory.alloc(); // 复制以module.base地址开始的10个字节 那肯定会是7F 45 4C 46...因为一个ELF文件的Magic属性如此。 Memory.copy(r,ptr(retval),size); console.log(hexdump(r, { offset: 0, length: size, header: true, ansi: false })); } } }); } } } }); """; jsCode2 = """ Java.perform(function(){ // 遍历所有模块 Process.enumerateModules({ onMatch: function(exp){ //先获取so的module对象 var module = Process.findModuleByName(exp.name); //??是通配符 var pattern = "11 22 33 44 55 66"; // //基址 // console.log("base:" + module.base) //从so的基址开始搜索,搜索大小为so文件的大小,搜指定条件03 49 ?? 50 20 44的数据 var res = Memory.scan(module.base, module.size, pattern, { onMatch: function(address, size){ //搜索成功 console.log('搜索到 ' + pattern +" 地址是:"+ address.toString()); }, onError: function(reason){ //搜索失败 // console.log('搜索失败 ' + reason); }, onComplete: function(){ //搜索完毕 // console.log("搜索完毕") } }); }, onComplete: function(){ send('stop'); } }); // 导入函数 var imports = Module.enumerateImportsSync("xxx.so"); for(var i = 0; i < imports.length; i++) { if(imports[i].name == 'strncat'){ send(imports[i].name + ": " + imports[i].address); break; } } // 导出函数 var exports = Module.enumerateExportsSync("xxx.so"); for(var i = 0; i < exports.length; i++) { if(exports[i].name.indexOf('add') != -1){ send(exports[i].name + ": " + exports[i].address); break; } } }); """; jsCode = """ var module = Process.findModuleByName("xxx.so"); var nativePointer = new NativePointer(module.base.add(0x1740)); send("nativePointer " + nativePointer); Interceptor.attach(nativePointer, { onEnter: function(args) { send("mou sub_method called!"); }, onLeave:function(retval){ } }); var nativePointer = new NativePointer(ptr(0x00001122)); send("nativePointer " + nativePointer); Interceptor.attach(nativePointer, { onEnter: function(args) { send("mou sub_method 2 called!"); }, onLeave:function(retval){ } }); """ def message(message, data): if message["type"] == 'send': print(u"[*] {0}".format(message['payload'])) else: print(message) process = frida.get_remote_device().attach("com.android.test") script= process.create_script(jsCode2) script.on("message", message) script.load() sys.stdin.read()
提供了多种方法:
a>遍历所有so模块,查找指定关键字。
b>遍历某个模块的所有导出函数,名称包含
c>遍历某个模块的所有导入函数,名称包含。
d>直接通过,模块得到内存地址和偏移,进行hook.
e>通过利用 this.的成员变量,在结果中,dump出,入参的参数的某个值进行dump内容。
f>输出 this.context对象。
源码解读:
待补充
参考:
https://www.anquanke.com/post/id/195215
https://www.anquanke.com/member/131652