文章目录
- 安装frida
- 安装python3.7
- 设置环境变量
- 安装pycharm和nodejs
- 使用frida
- 将frida-server push到手机设备中
- 端口转发
- 安装apk
- 使用jadx查看java代码
- 运行frida-server
- frida源码阅读
- frida hook方法
- Frida Java层hoook
- JavaHook.java
- JavaHook.js
- Frida native层hook 一
- NativeHook.js
安装frida
Frida框架简介
Frida是一款基于Python + JavaScript的Hook与调试框架。
Firda是一款易用的跨平Hook工具,Java层到Native层的Hook无所不能,是一种动态
的插桩工具,可以插入代码到原生 App 的内存空间中,动态的去监视和修改行为,
原生平台包括Win、Mac、Linux、Android、iOS全平台。环境配置步骤:
1.安装Python环境 3.72.安装frida模块打开Py输入命令:pip install fridapip install frida-toolspip uninstall fridapip uninstall frida-toolspip install frida==15.1.27pip install frida-tools==10.6.2google pixels 3a
frida=16.1.3
frida-tools=12.2.13.frida-server下载地址:查看版本信息:frida --versionhttps://github.com/frida/frida/releases下载安装frida server 版本和类型对应,框架和设备对应4.安装PyCharm 5.启动frida-server6.端口转发adb forward tcp:27042 tcp:270427.测试frida-ps -U frida -U -f com.qianyu.fridaapp --no-pause
安装python3.7
设置环境变量
安装pycharm和nodejs
创建工程的时候,记得将鼠标所在的两个勾选框选上
使用frida
将frida-server push到手机设备中
端口转发
安装apk
使用jadx查看java代码
运行frida-server
我使用15.1.27版本的frida版本,在安卓手机上会报错。所以我果断升级了frida版本
sargo:/data/local/tmp # ./frida-server-15.1.27-android-arm64
{"type":"error",
"description":"Error: Unable to determine ClassLinker field offsets",
"stack":"Error: Unable to determine ClassLinker field offsets\n at Ye (frida/node_modules/frida-java-bridge/lib/android.js:400:1)\n at frida/node_modules/frida-java-bridge/lib/memoize.js:4:1\n at ze (frida/node_modules/frida-java-bridge/lib/android.js:193:1)\n at Oe (frida/node_modules/frida-java-bridge/lib/android.js:16:1)\n at _tryInitialize (frida/node_modules/frida-java-bridge/index.js:29:1)\n at new _ (frida/node_modules/frida-java-bridge/index.js:21:1)\n at Object.4../lib/android (frida/node_modules/frida-java-bridge/index.js:332:1)\n at o (frida/node_modules/browser-pack/_prelude.js:1:1)\n at frida/node_modules/browser-pack/_prelude.js:1:1\n at Object.22.frida-java-bridge (frida/runtime/java.js:1:1)",
"fileName":"frida/node_modules/frida-java-bridge/lib/android.js","lineNumber":400,"columnNumber":1}
frida源码阅读
frida hook方法
注意点:
- frida和frida-server版本号必须要一致
- hook的时候,必须要运行hook的程序,否则报错
Frida Java层hoook
JavaHook.java
# -*- coding: utf-8 -*-import os
import sys
import frida
import codecsdef message(message, data):if message["type"] == 'send':print("[*] {0}".format(message['payload']))else:print(message)process = frida.get_remote_device().attach('NDKDemo')
if not os.path.isfile('./JavaHook.js'):raise TypeError("./JavaHook.js does not exist")
with codecs.open('./JavaHook.js', 'r', 'UTF-8') as file:js_code = file.read()
script = process.create_script(js_code)
script.on("message", message)
script.load()
# script.exports.test()
# script.exports.test()
# script.exports.test()
# script.exports.test()
sys.stdin.read()# frida使用非标准端口
# /data/local/tmp # ./fs_12.7.22_arm64 -l 127.0.0.1:31928 默认端口: 27046
# process = frida.get_device_manager().add_remote_device('127.0.0.1:31928').attach('FridaApp')
# if not os.path.isfile('./JavaHook.js'):
# raise TypeError("./JavaHook.js does not exist")
# with codecs.open('./JavaHook.js', 'r', 'UTF-8') as file:
# js_code = file.read()
# script = process.create_script(js_code)
# script.on("message", message)
# script.load()
# sys.stdin.read()
JavaHook.js
// HOOK普通方法、静态方法
function Test01(){let loginActivity = Java.use('com.yijincc.ndkdemo.LoginActivity');loginActivity.login.implementation = function () {console.log("=============login===============");console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));console.log(arguments[0]);console.log(arguments[1]);this.login(arguments[0],arguments[1]);}
}// HOOK构造方法、重载方法
function Test02(){let intent = Java.use('android.content.Intent');intent.$init.overload('android.content.Context', 'java.lang.Class').implementation = function () {console.log("=============intent===============");console.log(arguments[0]);console.log(arguments[1]);this.$init(arguments[0],arguments[1]);}
}// HOOK内部类
function Test03(){let loginActivity$1 = Java.use('com.yijincc.ndkdemo.LoginActivity$1');loginActivity$1.onClick.implementation = function () {console.log("=============onClick===============");console.log(arguments[0]);this.onClick(arguments[0]);}
}// 主动调用构造方法
function Test04(){let money = Java.use('com.yijincc.fridaapp.Money');let obj = money.$new(1000,'RMB');console.log(obj.getInfo());
}// 操作对象里面的成员变量
function Test05(){let money = Java.use('com.yijincc.fridaapp.Money');let obj = money.$new(10000,'RMB');console.log(obj.name.value);console.log(obj.num.value);console.log('===============================');obj.name.value = 'RMB';obj.num.value = 10000000;console.log(obj.name.value);console.log(obj.num.value);
}// 主动调用普通方法
function Test06(){let money = Java.use('com.yijincc.fridaapp.Money');let obj = money.$new(2000,'RMB');console.log(obj.getInfo());
}// 获取当前类已有的实例实现主动调用普通方法
function Test07(){Java.choose('com.yijincc.ndkdemo.MainActivity',{onMatch: function(obj){ // 枚举时调用console.log(obj);console.log(obj.name.value);console.log(obj.age.value);console.log(obj.sex.value);console.log(obj.rand('B',99))}, onComplete: function(){ // 枚举完成后调用console.log("end");}});
}// 主动调用静态方法
function Test08(){let mainActivity = Java.use('com.yijincc.ndkdemo.MainActivity');console.log(mainActivity.isRel(444,444));
}// HOOK打印堆栈信息
// function Test09(){
// let money = Java.use('com.yijincc.fridaapp.Money');
// money.getInfo.implementation = function () {
// console.log("=============getInfo===============");
// console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
// return this.getInfo();
// }
// }// HOOK指定类的所有方法
function Test10(){let money = Java.use('com.yijincc.ndkdemo.MainActivity');let methods = money.class.getDeclaredMethods();for(let j = 0; j < methods.length; j++){let methodName = methods[j].getName();console.log(methodName);for(let k = 0; k < money[methodName].overloads.length; k++){money[methodName].overloads[k].implementation = function(){console.log('==========='+methodName+'===========');for(let i = 0; i < arguments.length; i++){console.log(arguments[i]);}console.log('===========end===========');return this[methodName].apply(this, arguments);}}}
}// 枚举已加载的所有类与枚举类的所有方法
function Test11(){let classes = Java.enumerateLoadedClassesSync();for(let i = 0; i < classes.length; i++){if(classes[i].indexOf("com.") !== -1){console.log("clazz:"+classes[i]);let clazz = Java.use(classes[i]);let methods = clazz.class.getDeclaredMethods();for(let j = 0; j < methods.length; j++){console.log("method:"+methods[j]);}}}
}// hook动态加载dex文件
function Test12(){// Java.enumerateLoadedClasses({// onMatch: function (name, handle) {// if (name.indexOf("com.example") >= 0) {// console.log(name);// let class6 = Java.use(name);// class6.check.implementation = function () {// console.log("check:", this);// return true;// };// }// }, onComplete: function () {}// });Java.enumerateClassLoaders({onMatch: function (loader) {try {if (loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")) {console.log(loader);Java.classFactory.loader = loader; //切换classloader}} catch (error) {}}, onComplete: function () {}});let DynamicCheck = Java.use("com.example.androiddemo.Dynamic.DynamicCheck");console.log(DynamicCheck);DynamicCheck.check.implementation = function () {console.log("DynamicCheck.check");return true;}
}// 动态加载dex文件
function Test13(){// jar -cvf dex.jar com/example/androiddemo/StringUtils.class// dx --dex --output=dex.dex dex.jarlet dex= Java.openClassFile("/data/local/tmp/dex.dex");dex.load();let stringUtils = Java.use("com.example.androiddemo.StringUtils");console.log(stringUtils.tohexString("1234567890"));
}rpc.exports = {test:function () {Java.choose('com.yijincc.ndkdemo.MainActivity',{onMatch: function(obj){ // 枚举时调用console.log(obj);console.log(obj.name.value);console.log(obj.age.value);console.log(obj.sex.value);console.log(obj.rand('B',99))}, onComplete: function(){ // 枚举完成后调用console.log("============end============");}});}
}Java.perform(function () {Test01();// Test02(); // 重载方法// Test03(); // 内部类// Test04() // 主动调用构造方法,创建一个对象// Test07();// Test08();// Test10(); // 打印所有的方法// Test11();//Test13();
});
Frida native层hook 一
通过IDA找到要hook的native函数(静态)
base64魔改
## NativeHook.py
# -*- coding: utf-8 -*-import os
import sys
import frida
import codecsdef message(message, data):if message["type"] == 'send':print("[*] {0}".format(message['payload']))else:print(message)process = frida.get_remote_device().attach('NDKDemo')
if not os.path.isfile('./NativeHook.js'):raise TypeError("./NativeHook.js does not exist")
with codecs.open('./NativeHook.js', 'r', 'UTF-8') as file:js_code = file.read()
script = process.create_script(js_code)
script.on("message", message)
script.load()
sys.stdin.read()# frida使用非标准端口
# /data/local/tmp # ./fs_12.7.22_arm64 -l 127.0.0.1:31928 默认端口: 27046
# process = frida.get_device_manager().add_remote_device('127.0.0.1:31928').attach('Android_crackme')
# if not os.path.isfile('./NativeHook.js'):
# raise TypeError("./NativeHook.js does not exist")
# with codecs.open('./NativeHook.js', 'r', 'UTF-8') as file:
# js_code = file.read()
# script = process.create_script(js_code) # 创建脚本
# script.on("message", message)
# script.load()
# sys.stdin.read()
NativeHook.js
// HOOK导出函数
function Test01(){// 用于在指定的模块中查找导出函数的地址let funGetFlag = Module.findExportByName("libnative-lib.so", "Java_com_yijincc_ndkdemo_LoginActivity_login");send("native: " + funGetFlag); // 基地址Interceptor.attach(funGetFlag, {onEnter: function(args){send("============getFlag===============");send(args[0]);send(args[1]);},onLeave: function(retval){send("============result===============");send(retval);// 获取JNIEnv*let env = Java.vm.tryGetEnv();// 将jstring 转换 const char*let str=env.getStringUtfChars(retval,0);send(str.readCString());}});
}// HOOK未导出函数
function Test02(){// 绝对地址=so模块起始地址(基地址)+偏移地址let baseAddr = Module.findBaseAddress("libnative-lib.so");send("baseAddr:"+baseAddr);// 指令集 分为ARM指令、thumb指令// ARM指令地址不变 thumb指令地址+1 sub_ 开头的函数 这种函数只能使用这种方式来进行Interceptor.attach(baseAddr.add(0x15F08), {onEnter: function(args){send("============encrypt===============");send(args[0]);send(args[1]);send(args[2]);console.log(hexdump(args[2], {offset: 0,length: 16,header: true,ansi: false}));// 获取JNIEnv*let env = Java.vm.tryGetEnv();// 将jstring 转换 const char*let str=env.getStringUtfChars(args[2],0);send(str.readCString());},onLeave: function(retval){send("============result===============");send(retval);// 获取JNIEnv*let env = Java.vm.tryGetEnv();// 将jstring 转换 const char*let str=env.getStringUtfChars(retval,0);send(str.readCString());}});
}// HOOK枚举导入函数信息
function Test03(){send("Test03")let imports = Module.enumerateImportsSync("libnative-lib.so");send(imports)for(let i=0;i<imports.length;i++){// if(imports[i].name.indexOf('raise') !== -1){send(imports[i]);// }}
}// HOOK枚举导出函数信息
function Test04(){send("Test04-start")send(device)let exports = Module.enumerateExportsSync("libnative-lib.so");send(exports)for(let i=0;i<exports.length;i++){if(exports[i].name.indexOf('Java_') !== -1){send("name:"+exports[i].name+" address:"+exports[i].address);}}send("Test04-end")
}// 遍历模块列表信息
function Test05(){Process.enumerateModules({onMatch: function(exp){send(exp)if(exp.name.indexOf('libnative-lib.so') !== -1){send('enumerateModules find');send(exp);return 'stop';}},onComplete: function(){send('enumerateModules stop');}});
}// 读写内存数据
function Test06(){let mem_addr=Memory.alloc(20);//Memory.writeInt(mem_addr,0x1234567890abcdef);Memory.writeLong(mem_addr,0x1234567890abcdef);// console.log(hexdump(mem_addr));console.log(hexdump(mem_addr, {offset: 0,length: 20,header: true,ansi: true}));
}// 使用frida api读写文件
function Test07(){let file = new File("/data/data/com.yijincc.ndkdemo/yijincc.txt", "w");file.write("hello world!!!\\n");file.flush();file.close();
}// 基于主动调用libc.so里面的函数实现文件的读写操作
function Test08(){let addr_fopen = Module.findExportByName("libc.so", "fopen");send("1")let addr_fputs = Module.findExportByName("libc.so", "fputs");send("2")let addr_fclose = Module.findExportByName("libc.so", "fclose");send("3")let fopen = new NativeFunction(addr_fopen, "pointer", ["pointer", "pointer"]);send("4")let fputs = new NativeFunction(addr_fputs, "int", ["pointer", "pointer"]);send("5")let fclose = new NativeFunction(addr_fclose, "int", ["pointer"]);send("6")let filename = Memory.allocUtf8String("/data/data/com.yijincc.ndkdemo/yijincc.txt");send("7")let open_mode = Memory.allocUtf8String("w");send("8")let file = fopen(filename, open_mode);send("9")let buffer = Memory.allocUtf8String("hello world!!!\\n");send("10")let result = fputs(buffer, file);send("11")send("fputs:" + result);fclose(file);
}Java.perform(function () {// Test01();// Test02();// Test03();// Test04();// Test05();// Test06();// Test07();Test08();
})