Frida Learning Notes

Frida 学习笔记
基本运用
frida -u com.example.test -l injection.js # 以spwan 形式启动
frida -Uf com.example.test -l injection.js # 以附加(attach)形式启动 , F表示从当前最顶层的app
(function frida_hook(){
	Java.perform(function(){
		// 创建
		Java.use("com.example.lesson4one.MainActivity").fun.overload('int', 'int').implementation = function(arg1, arg2){
			// 使用当前的 use 对象主动调用 fun 方法
			var result = this.fun(arg1,arg2);
			// 打印堆栈当前对象的 fun 方法调用的 堆栈信息
			console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new()));
			// 修改返回结果
			return 800;
		}
		// 创建
		Java.use("com.example.lesson4one.MainActity").fun.overload('java.lang.String').implementation = function (arg1){
			var result = this.fun(Java.use('java.lang.String')).$new("NI HAO JAVA");
			console.log("arg1, result", arg1, result);
			return Java.use('java.lang.String').$new("NI HAO JS");
		}
		// 使用已存在的实例,若内存中不存在该实例会报错
		Java.choose("com.example.lesson4one.MainActity", {
			// 这里的instance指的是上面choose的实例
			onMatch: function(instance){
				console.log("found instance :", instance);
				// 利用实例调用里面的secret方法
				instance.secret();
			},
			onComplete: function(){
				
			}
		
		});
		
		// 静态直接调用,需要找到该静态方法的实例
		var result = Java.use("com.example.lesson4one.MainActivity").secret2();
		console.log(result);
	})


})()

function invoke(){
	
	Java.perform(function(){
		Java.choose("com.example.test.MainActivity",{
			onMatch: function(instacne){
				console.log("found instacne ", instance);
				// 主动调用
				console.log("invoke instance.a", instance.a());
			},
			onComplete: function(){
				console.log("search completed ~ ");
			}
		})
	})
}
// 延迟调用 3s
setTimeout(invoke, 3000);
使用
  1. frida-ps -Uf com.example.test -l injection.js
  2. %resume
  3. invoke()
使用Firda:构造数组、强制类型转换、注册接口实现方法、枚举、Map、
(function frida_middle(){
	

	Java.perform(function(){
		// hook 数组
		Java.use("java.util.Arrays").toString.overload('[C').implementation = function(x){
			var charArray = Java.array('char', ['一','去','二','三',里']);
			var result = this.toString(charArray);
			return Java.use('java.lang.String').$new(Java.array('char',['烟','村','四','五','家']));
		};
	
         // 强制类型转换
         var juiceHandle = null;
         Java.choose("com.r0ysue.a0526printout.Juince",{
            onMatch: function(instance){
                console.log("found instance :", instance);
                console.log("filling energy,", instance.fillEnergy());
            },
            onComplete: function(){console.log("Search completed ~")}
         })
         var watterHandle = Java.case(juiceHandle, Java.use("com.r0ysue.a0526printout.Water"));
         console.log("water invoke still ", waterHandle.still(waterHandle));

         // 注册接口实现
         var beer = Java.registerClass({
            // 注册的类名
            name: 'com.r0ysue.a0526printout.beer',
            // 需要实现的接口 包名 + 接口名
            implements: [Java.use('com.r0ysue.a0526printout.liquid')],
            methods{
                // 需要实现所有的方法,无参数则不需要传入,有参数就必须按照指定类型的参数进行传入
                flow: function(){
                    console.log("look I'm beer ~");
                    return "taste good ~";
                }
            }
         });
         console.log("beer flow: ", beer.$new().flow());

         // 使用枚举
         Java.choose("com.r0ysue.a0526printout.Signal",{
            onMatch: function(instance){
                console.log("found instance: ", instance);
                console.log("invoke getDeclaringClass", instance.getDeclaringClass());
            },
            onComplete: function(){console.log("Search Completed ~ ")}
         })

         // hook map
         Java.chooses("java.util.HashMap",{
         	onMatch: function(instance){
         		if(instance.toString.indexOf("ISBN") != -1){
         			console.log("found HashMap", instance);
         			console.log("HashMap toString :", instance.toString());
         		}
         	},
         	onComplete: function(){
         		console.log("Search completed ~");
         	}
         })
         
         // hook map.put
         Java.use("java.util.HashMap").put.implementation = function(x ,y){
         	var result = this.put(x,y);
         	console.log("x, y, result", x, y, result);
         }
     
     
     
	});	
})()
Frida hook 方法的返回实例路径、枚举加载的class
(function(){
	
	Java.perform(function(){
		Java.choose("com.example.androiddemo.Activity.FridaActivity5",{
			
			onMatch: function(instance){
				// 一定要在调用过后才能看到class name
				console.log("found instance getDynamicDexCheck :", instance.getDynamicDexCheck().$className);
			},
			onComplete: function(){
				console.log("Search completed ~~");
			}
			
			// 枚举加载的 class loaders 从内存中寻找
			Java.enumerateClassLoaders({
				onMatch: function(loader){
					try{
						if(loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")){
							console.log("Succefully found loader ~", loader);
							Java.classFactory.loader = loader;
						}
					}catch(error){
						console.log("found error", error);
					}
				},
				onComplete: function(){
					console.log("enum completed ~");
				}
			})
			
		
		})
	})

})()
Frida hook 接口实现类
(function(){
	Java.perform(function(){
		Java.enumerateLoadedClasses({
			"onMatch": function(className){
				if(class.indexOf("com.example.androiddemo") < 0){
					return;
				}
				var hookCls = Java.use(className);
				var interFaces = hookCls.clss.getInterfaces();
				if(interFaces.length > 0){
					console.log(className);
					for(var i in interFaces){
						console.log("\t", interFaces[i].toString())
					}
				}
			}
		})
	})
})()
Frida rpc
import time
import frida

def my_message_handler(message, payload):
	print(message)
	print(payload)
	
# method 1	
# device = frida.get_usb_device()

# method 2
# pid = device.spawn(["com.example.leeson4one"])
# device.resume
# time.sleep(1)
# session = device.attach(pid)

# method 3 非标准连接
device = firda.get_device_manager().add_remote_device("192.168.1.102:9999")

session = device.attach("com.example.lesson4one")
with open("lesson7lesson4.js") as f:
	script = seesion.create_script(f.read())
script.on("message", my_message_handler)
script.load()

command = ""
while True:
	command = input("Enter Command: ")
 if command == "1"
 	break
  elif command == "2":
     script.exports.invokefunc()
 	

invoke js

function invoke(){
	Java.perform(function(){
		Java.choose("com.example.lesson4one.MainActivity",{
			onMatch: function(instance){
				console.log("found instance :", instance)
				console.log("found instance :", instance.secret())
			},
			onComplete: function(){
				console.log("Search completed ~");
			}
		})
	})
}
rpc.exports = {
	invokefunc: invoke
}

firda 传入 和 传出

lesson7sec.js

	Java.perform(function(){
		Java.use("android.widget.TextView").setText.overload("java.lang.CharSequence").implementation = function(x){
			var string_to_send_x = x.toString();
			var string_to_recv;
			// 把 js 的参数传入到 python
			send(str_to_send_x);
			recv(function(received_json_objection){
				// 调用python的中的 .my_data方法
				string_to_recv = received_json_objection.my_data
				console.log("string_to_recv", string_to_recv);
			}).wait();
			
			var javaStringToSend = Java.use("java.lang.String").$new(string_to_recv);
			var result = this.setText(javaStringToSend);
			
			return result;
		}
	})

python

import time
import frida
import base64

def my_message_handler(message, payload):
	print(message)
	print(payload)
	if message["type"] == "send":
		print(message["payload"])
		data = message["payload"].split(":")[1].strip()
		print('message:', message)
		data = str(base64.b64decode(data))
		print("data:", data)
		usr.pwd = data.split(":")
		print('pw: ',pw)
		data = str(base64.b64encode(("admin" + ":" + pw).encode()))
		print("encode data:", data)
		script.post({"my_data":data})
		print("Modified data send ~")
		
device = frida.get_usb_device()
session = device.attach("com.example.lesson4sec")
with open("lesson7sec.js") as f:
	script = session.create_script(f.read())
script.on("message", my_message_handler)
script.load()
input()
Frida 主动调用so
(function(){
	Java.perform(()=>{
		var so_addr = Module.findExportByName("libnative-lib.so", "symbol");
		console.log("found address =>", so_addr);
		var use = new NativeFunction(so_addr, "pointer", ["pointer", "pointer", "pointer"]);
		var result = use(Java.vm.tryGetEnv(), Java.vm.tryGetEnv(), Java.vm.tryGetEnv().newStringUtf("12345roysue"));
		
	})
})()
Frida 加载 自制dex
  1. 使用 sdk 中的 d8 工具 打包 编译好的 class 文件 具体路径 # Sdk > build-tools > 29.0.3 > d8

  2. 使用 d8 编译目标文件

  3. adb push /data/local/tmp

  4. chmod 777 xxx.dex

  5. Java.openClassFile("/data/local/tmp/xxx.dex").load();
    const loadDex = Java.use("com.example.DexClass");
    console.log("Frida load dex method test ",loadDex.test());
    
注意事项
  1. 若方法名和变量名相等,则修改变量名的值的时候需要在对象引用前加上_

  2. 若要修改的的变量是一个静态编写的时候,则需要使用Java.use eg:

    Java.use("com.test.static_value").static_value.value = "change it ~";
    
&¥%……&……%……%¥……%……
frida 中的 int 表示 int.class
Integer.class.getField("TYPE").get(null);

small objection

const Class = Java.use("java.lang.Class");
function inspectObject(obj){
	const obj_class = Java.cast(obj.class, Class);
	const fields = obj_class.getDeclaredFields();
	const methods = obj_class.getMethods();
	console.log("Inspecting " + obj.getClass().toString());
	console.log("\tFields:");
	for(var i in fields)
		console.log("\t\t" + fileds[i].toString());
	console.log("\tMethods:");
	for(var i in methods)
		console.log("\t\t" + methods[i].toString());
}

special write

function  java_invoke(){
	Java.perform(function(){
		var cls2 = Java.use("com.lzy.dobbytest.test");
		
		var String = Java.use("java.lang.String");
		var Integer = Java.use("java.lang.Integer");
		var type_int = Java.use("java.lang.Class").getPrimitiveClass("int");			// 拿到基本数据类型
		
		// String 参数的方法
		var method = cl22.class.getDeclaredMethod("showLog", Java.array("java.lang.Object", [String.class]));
		method.invoke(cls2.$new(), Java.array("java.lang.Object", ["test"]))
		// Int 参数的方法
		var method = cls2.class.getDeclaredMethod("showLog", Java.array("java.lang.Object", [type_int]))
		console.log(method); 			// 这里的method是没有问题,下面的invoke 的参数有问题
		method.invoke(cls2.$new, Java.array("java.lang.Object", [123]))
		method.invoke(cls2.$new, Java.array("java.lang.Object", [Integer.parseInt('123')]))
		// 同理 char/byte 也不行
		
	})
}