Frida API示例
Java层
hook普通方法
# -*- coding: utf-8 -*-
import frida
import sys
hook_code = """
Java.perform(function(){
var utils = Java.use("类名路径");
utils.方法名.implementation = function(a, b){
a = 123;
b = 456;
var retval = this.方法名(a, b);
console.log(a, b, retval);
return retval;
}
});
"""
process = frida.get_usb_device().attach('包名')
script = process.create_script(hook_code)
script.load()
sys.stdin.read()
hook多个重载方法
# -*- coding: utf-8 -*-
import frida
import sys
hook_code = """
Java.perform(function hookTest3(){
var utils = Java.use("类名路径");
utils.方法名.overload('重载的参数1').implementation = function(a){
a = 888;
var retval = this.方法名();
console.log(a, retval);
return retval;
}
utils.方法名.overload(无参).implementation = function(){
var retval = this.方法名();
console.log(retval);
return retval;
}
utils.方法名.overload('重载的参数2').implementation = function(a){
var retval = this.方法名(a);
console.log(retval);
return retval;
}
});
"""
process = frida.get_usb_device().attach('包名')
script = process.create_script(hook_code)
script.load()
sys.stdin.read()
hook所有重载方法
与 “多个重载方法” 不同点在于写了参数判断逻辑
# -*- coding: utf-8 -*-
import frida
import sys
hook_code = """
Java.perform(function hookTest3(){
var utils = Java.use("类名路径");
//console.log(utils.方法名.overloads.length);
for(var i = 0; i < utils.方法名.overloads.length; i++){
utils.方法名.overloads[i].implementation = function(){
//console.log(JSON.stringify(arguments));
if(arguments.length == 0){
return "调用了没有参数的";
}else if(arguments.length == 1){
if(JSON.stringify(arguments).indexOf("Money") != -1){
return "调用了Money参数的";
}else{
return "调用了int参数的";
}
}
arguments[0] = 1000;
return this.方法名.apply(this, arguments);
}
}
});
"""
process = frida.get_usb_device().attach('包名')
script = process.create_script(hook_code)
script.load()
sys.stdin.read()
hook构造方法
Java.perform(function hookTest4(){
var money = Java.use("类名路径");
money.$init.overload('重载的参数1', '重载的参数2').implementation = function(str, num){
console.log(str, num);
str = "欧元";
num = 2000;
this.$init(str, num);
}
});
对象实例化
hook_code = """
Java.perform(function hookTest2(){
var utils = Java.use("utils类路径");
var money = Java.use("money类路径");
utils.方法名.overload('重载参数').implementation = function(a){
a = 888;
var retval = this.方法名(money.$new("日元", 100000));//对象实例化
console.log(a, retval);
return retval;
}
});
"""
修改类的字段
hook_code = """
Java.perform(function(){
//静态字段的修改
var money = Java.use("money类路径");
//console.log(JSON.stringify(money.字段名));
money.字段名.value = "xxxxx";
console.log(money.flag.value);
//非静态字段的修改
Java.choose("money类路径", {
onMatch: function(obj){
obj._name.value = "ouyuan"; //字段名与函数名相同
obj.name.value = "ouyuan"; //字段名与函数名不同
前面加个下划线
obj.num.value = 150000;
},
onComplete: function(){
}
});
});
"""
hook内部类与匿名类
hook_code = """
Java.perform(function hookTest6(){
Java.perform(function(){
var innerClass = Java.use("money类路径$内部类名");
console.log(innerClass);
innerClass.$init.implementation = function(a, b){
a = "nb";
b = 888;
return this.$init(a, b);
}
var xxx = Java.use("xxx类路径$smali中查看匿名类数字编号");
console.log(xxx);
xxx.getInfo.implementation = function(){
return "匿名类被Hook了"
}
});
});
"""
枚举所有的类与类的所有方法
# -*- coding: utf-8 -*-
hook_code = """
Java.perform(function hookTest7(){
Java.perform(function(){
// Java.enumerateLoadedClasses({
// onMatch: function(name, handle){
// if(name.indexOf("包名") != -1){
// console.log(name);
// var clazz = Java.use(name);
// console.log(clazz);
// var methods = clazz.class.getDeclaredMethods();
// for(var i = 0; i < methods.length; i++){
// console.log(methods[i]);
// }
// }
// },
// onComplete: function(){
// }
// }); 两种写法
var classes = Java.enumerateLoadedClassesSync();
for(var i = 0; i < classes.length; i++){
if(classes[i].indexOf("包名") != -1){
console.log(classes[i]);
var clazz = Java.use(classes[i]);
var methods = clazz.class.getDeclaredMethods();
for(var j = 0; j < methods.length; j++){
console.log(methods[j]);
}
}
}
});
});
"""
hook类的所有方法
hook_code = """
Java.perform(function hookTest8(){
Java.perform(function(){
var md5 = Java.use("md5类路径");
var methods = md5.class.getDeclaredMethods();
for(var j = 0; j < methods.length; j++){
var methodName = methods[j].getName();
console.log(methodName);
for(var k = 0; k < md5[methodName].overloads.length; k++){
md5[methodName].overloads[k].implementation = function(){
for(var i = 0; i < arguments.length; i++){
console.log(arguments[i]);
}
return this[methodName].apply(this, arguments);
}
}
}
});
});
"""
Hook动态加载的dex(Android 7以上)
hook_code = """
Java.perform(function () {
Java.enumerateClassLoaders({
onMatch: function (loader) {
try {
if (loader.loadClass("com.xiaojianbang.app.Dynamic")) {
Java.classFactory.loader = loader;
var Dynamic = Java.use("com.xiaojianbang.app.Dynamic");
console.log(Dynamic);
Dynamic.sayHello.implementation = function () {
return "9999999";
}
}
} catch (error) {
}
},
onComplete: function () {
}
});
});
"""
Java特殊类型的遍历与修改
hook_code = """
Java.perform(function () {
var ShufferMap = Java.use("com.xiaojianbang.app.ShufferMap");
console.log(ShufferMap);
ShufferMap.show.implementation = function (map) {
console.log(JSON.stringify(map));
//Java map的遍历
var key = map.keySet();
var it = key.iterator();
var result = "";
while(it.hasNext()){
var keystr = it.next();
var valuestr = map.get(keystr);
result += valuestr;
}
console.log(result);
// return result;
//Java map的修改
map.put("pass", "zygx8");
map.put("guanwang", "www.zygx8.com");
var retval = this.show(map);
console.log(retval);
return retval;
}
});
"""
打印HashMap
console.log(JSON.stringify(arguments))
var Map = Java.use('java.util.HashMap');
var args_map = Java.cast(arguments[1], Map);
console.log(args_map.toString());
Java层主动调用
hook_code = """
Java.perform(function () {
//静态方法的主动调用
var rsa = Java.use("com.xiaojianbang.app.RSA");
var str = Java.use("java.lang.String");
var base64 = Java.use("android.util.Base64");
var bytes = str.$new("xiaojianbang").getBytes();
console.log(JSON.stringify(bytes));
var retval = rsa.encrypt(bytes);
var result = base64.encodeToString(retval, 0);
console.log(result);
//非静态方法的主动调用1 (新建一个对象去调用)
var res = Java.use("com.xiaojianbang.app.Money").$new("日元", 300000).getInfo();
console.log(res);
var utils = Java.use("com.xiaojianbang.app.Utils");
res = utils.$new().myPrint(["xiaojianbang", "is very good", " ", "zygx8", "is very good"]);
console.log(res);
//非静态方法的主动调用2 (获取已有的对象调用)
Java.choose("com.xiaojianbang.app.Money", {
onMatch: function (obj) {
if (obj._name.value == "美元") {
res = obj.getInfo();
console.log(res);
}
},
onComplete: function () {
}
});
});
"""
删除对象引用
$.dispose
获取参数类型
xxx.class.getType()
打印Java层函数堆栈定位关键代码
hook_code = """
Java.perform(function () {
var MessageDigest = Java.use("java.security.MessageDigest");
MessageDigest.digest.overload().implementation = function () {
var stack = Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new());
console.log(stack);
return this.digest();
}
});
"""
用frida注入dex文件
hook_code = """
Java.perform(function () {
Java.openClassFile("/data/local/tmp/xiaojianbang.dex").load();
var xiaojianbang = Java.use("com.xiaojianbang.test.xiaojianbang");
var ShufferMap = Java.use("com.xiaojianbang.app.ShufferMap");
ShufferMap.show.implementation = function (map) {
var retval = xiaojianbang.sayHello(map);
console.log(retval);
return retval;
}
});
"""
端口检测解决方案
./data/local/tmp/frida_server_arm64 -l 127.0.0.1:9999
adb forward tcp:9999 tcp:9999
frida启动前注入
frida -H 127.0.0.1:9999 -f com.xjb.cpp -l hook.js --no-pause
frida同时Hook多个设备
获取当前的连接设备
adb devices
指定连接某个设备
adb -s 127.0.0.1:5555 shell
端口转发
adb -s 127.0.0.1:5555 forward tcp:27042 tcp:27042
hook代码
# -*- coding: UTF-8 -*-
import frida, sys
jscode = """
setImmediate(function(){
Java.perform(function () {
var utils = Java.use("com.xiaojianbang.app.Utils");
utils.getCalc.implementation = function(a, b){
a = 12345;
b = 54321;
var retval = this.getCalc(a, b);
console.log(a, b, retval);
return retval;
}
});
});
"""
def message(message, data):
if message["type"] == 'send':
print("[*] {0}".format(message['payload']))
else:
print(message)
# rdev = frida.get_device_manager().enumerate_devices()
rdev = frida.enumerate_devices()
print(rdev)
rdev = rdev[int(sys.argv[1])]
process = rdev.attach('com.xiaojianbang.app') # pid
script = process.create_script(jscode)
script.on('message', message)
script.load()
sys.stdin.read()
so层
枚举导入导出表(ELF即so文件)
枚举导入表
var imports = Module.enumerateImports("libxiaojianbang.so");
for(var i = 0; i < imports.length; i++){
if(imports[i].name == "strncat"){
console.log(JSON.stringify(imports[i]));
console.log(imports[i].address);
}
}
枚举导出表
var exports = Module.enumerateExports("libxiaojianbang.so");
for(var i = 0; i < exports.length; i++){
//if(exports[i].name == "strncat"){
console.log(JSON.stringify(exports[i]));
//}
}
hook导出函数
hook_code = """
Java.perform(function hookTest2() {
var helloAddr = Module.findExportByName("libxiaojianbang.so", "Java_com_xiaojianbang_app_NativeHelper_add");
console.log(helloAddr);
if (helloAddr != null) {
Interceptor.attach(helloAddr, {
onEnter: function (args) {
//args参数数组
console.log(args[0]);
console.log(args[1]);
console.log(args[2]);
console.log(args[3]);
console.log(args[4].toInt32());
},
onLeave: function (retval) {
//retval函数返回值
console.log(retval);
console.log("retval", retval.toInt32());
}
});
}
});
"""
函数地址计算
function hookTest14(){
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
console.log(soAddr);
var funcAddr = soAddr.add(0x23F4);
console.log(funcAddr);
}
Hook未导出函数
function hookTest14(){
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
console.log(soAddr);
var funcAddr = soAddr.add(0x23F4);
console.log(funcAddr);
if(funcAddr != null){
Interceptor.attach(funcAddr,{
onEnter: function(args){
},
onLeave: function(retval){
console.log(hexdump(retval));
}
});
}
}
获取指针参数返回值
function hookTest5(){
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
console.log(soAddr);
var sub_930 = soAddr.add(0x930); //函数地址计算 thumb+1 ARM不加
console.log(sub_930);
var sub_208C = soAddr.add(0x208C); //函数地址计算 thumb+1 ARM不加
console.log(sub_208C);
if(sub_208C != null){
Interceptor.attach(sub_208C,{
onEnter: function(args){
this.args1 = args[1];
},
onLeave: function(retval){
console.log(hexdump(this.args1));
}
});
}
}
Hook_dlopen
/data/app/com.xxx.app-l/lib/arm64/xxxxxx.so
function hookTest6(){
var dlopen = Module.findExportByName(null, "dlopen");
console.log(dlopen);
if(dlopen != null){
Interceptor.attach(dlopen,{
onEnter: function(args){
var soName = args[0].readCString();
console.log(soName);
if(soName.indexOf("libxiaojianbang.so") != -1){
this.hook = true;
}
},
onLeave: function(retval){
if(this.hook) { hookTest5() };
}
});
}
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
console.log(android_dlopen_ext);
if(android_dlopen_ext != null){
Interceptor.attach(android_dlopen_ext,{
onEnter: function(args){
var soName = args[0].readCString();
console.log(soName);
if(soName.indexOf("libxiaojianbang.so") != -1){
this.hook = true;
}
},
onLeave: function(retval){
if(this.hook) { hookTest5() };
}
});
}
}
内存读写
function hookTest7(){
var soAddr = Module.findBaseAddress("libxiaojianbang.so");
console.log(soAddr);
if(soAddr != null){
//console.log(soAddr.add(0x2C00).readCString());
//console.log(hexdump(soAddr.add(0x2C00))); //读取指定地址的字符串
//var strByte = soAddr.add(0x2C00).readByteArray(16); //读内存
//console.log(strByte);
//soAddr.add(0x2C00).writeByteArray(stringToBytes("xiaojianbang")); //写内存
//console.log(hexdump(soAddr.add(0x2C00))); //dump指定内存
//var bytes = Module.readByteArray(soAddr.add(0x2C00), 16);
//console.log(bytes);
}
}
主动调用JNI函数
function hookTest8(){
var funcAddr = Module.findExportByName("libxiaojianbang.so", "Java_com_xiaojianbang_app_NativeHelper_helloFromC");
console.log(funcAddr);
if(funcAddr != null){
Interceptor.attach(funcAddr,{
onEnter: function(args){
},
onLeave: function(retval){
var env = Java.vm.tryGetEnv();
var jstr = env.newStringUtf("www.zygx8.com"); //主动调用jni函数 cstr转jstr
retval.replace(jstr);
var cstr = env.getStringUtfChars(jstr); //主动调用 jstr转cstr
console.log(cstr.readCString());
console.log(hexdump(cstr));
}
});
}
}
jni函数Hook(计算地址方式)
function hookTest9(){
Java.perform(function(){
//console.log(JSON.stringify(Java.vm.tryGetEnv()));
var envAddr = ptr(Java.vm.tryGetEnv().handle).readPointer();
var newStringUtfAddr = envAddr.add(0x538).readPointer();
var registerNativesAddr = envAddr.add(1720).readPointer();
console.log("newStringUtfAddr", newStringUtfAddr);
console.log("registerNativesAddr", registerNativesAddr)
if(newStringUtfAddr != null){
Interceptor.attach(newStringUtfAddr,{
onEnter: function(args){
console.log(args[1].readCString());
//args[1] = "xiaojianbang is very good!";
},
onLeave: function(retval){
}
});
}
if(registerNativesAddr != null){ //Hook registerNatives获取动态注册的函数地址
Interceptor.attach(registerNativesAddr,{
onEnter: function(args){
console.log(args[2].readPointer().readCString());
console.log(args[2].add(Process.pointerSize).readPointer().readCString());
console.log(args[2].add(Process.pointerSize * 2).readPointer());
console.log(hexdump(args[2]));
console.log("sub_289C", Module.findBaseAddress("libxiaojianbang.so").add(0x289C));
},
onLeave: function(retval){
}
});
}
});
}
jni函数Hook(libart.so)
function hookTest10(){
var artSym = Module.enumerateSymbols("libart.so");
var NewStringUTFAddr = null;
for(var i = 0; i < artSym.length; i++){
if(artSym[i].name.indexOf("CheckJNI") == -1 && artSym[i].name.indexOf("NewStringUTF") != -1){
console.log(JSON.stringify(artSym[i]));
NewStringUTFAddr = artSym[i].address;
}
};
if(NewStringUTFAddr != null){
Interceptor.attach(NewStringUTFAddr,{
onEnter: function(args){
console.log(args[1].readCString());
},
onLeave: function(retval){
}
});
}
}
so层堆栈_快速定位函数注册在哪个so
so层函数主动调用
function hookTest11(){
Java.perform(function(){
var funcAddr = Module.findBaseAddress("libxiaojianbang.so").add(0x23F4);
var func = new NativeFunction(funcAddr, "pointer", ['pointer', 'pointer']);
var env = Java.vm.tryGetEnv();
console.log("env: ", JSON.stringify(env));
if(env != null){
var jstr = env.newStringUtf("xiaojianbang is very good!!!");
//console.log("jstr: ", hexdump(jstr));
var cstr = func(env, jstr);
console.log(cstr.readCString());
console.log(hexdump(cstr));
}
});
}
frida读写文件
//frida API 读写文件
function hookTest12(){
var ios = new File("/sdcard/xiaojianbang.txt", "w");
ios.write("xiaojianbang is very good!!!\n");
ios.flush();
ios.close();
}
//Hook libc 读写文件
function hookTest13() {
var addr_fopen = Module.findExportByName("libc.so", "fopen");
var addr_fputs = Module.findExportByName("libc.so", "fputs");
var addr_fclose = Module.findExportByName("libc.so", "fclose");
console.log("addr_fopen:", addr_fopen, "addr_fputs:", addr_fputs, "addr_fclose:", addr_fclose);
var fopen = new NativeFunction(addr_fopen, "pointer", ["pointer", "pointer"]);
var fputs = new NativeFunction(addr_fputs, "int", ["pointer", "pointer"]);
var fclose = new NativeFunction(addr_fclose, "int", ["pointer"]);
var filename = Memory.allocUtf8String("/sdcard/xiaojianbang.txt");
var open_mode = Memory.allocUtf8String("w");
var file = fopen(filename, open_mode);
console.log("fopen:", file);
var buffer = Memory.allocUtf8String("zygxb\n");
var retval = fputs(buffer, file);
console.log("fputs:", retval);
fclose(file);
}