FRIDA-HOOK note
0x1 java层hook
function hook_java(){
Java.perform(function(){
var LoginActivity = Java.use("com.example.androiddemo.Activity.LoginActivity");
LoginActivity.a.overload('java.lang.String', 'java.lang.String').implementation = function(str , str2){
var result = this.a(str,str2);
console.log("LoginActivity.a:",str,str2,result);
return result;
};
});
}
function main(){
hook_java();
}
setImmediate(main);
Java.perform 附加线程
Java.use 反射获取类
.overload 出现同名函数时使用
0x2 扫描java类
function call_FridaActivity2(){
Java.perform(function (){
var FridaActivity2 = Java.use("com.example.androiddemo.Activity.FridaActivity2");
FridaActivity2.setStatic_bool_var();
Java.choose("com.example.androiddemo.Activity.FridaActivity2", {
onMatch: function (instance){
instance.setBool_var();
},
onComplete: function (){
}
});
});
}
Java.choose(className, callback)
在内存中扫描 Java 堆,枚举 Java 对象(className)实例。比如可以使用 java.lang.String 扫描内存中的字符串。callbacks 提供两个参数:onMatch(instance) 和 onComplete,分别是找到匹配对象和扫描完成调用
function call_FridaActivity3(){
Java.perform(function(){
var FridaActivity3 = Java.use("com.example.androiddemo.Activity.FridaActivity3");
FridaActivity3.static_bool_var.value =true;
Java.choose("com.example.androiddemo.Activity.FridaActivity3",{
onMatch: function(instance){
instance.bool_var.value=true;
//相同名称开头要加下划线
instance._same_name_bool_var.value=true;
},
onComplete: function(){
}
})
});
}
参数名相同前面需要加下划线
0x3 遍历所有类方法找到自己想要的类方法
function call_FridaActivity4(){
Java.perform(function(){
var class_name = "com.example.androiddemo.Activity.FridaActivity4$InnerClasses";
var InnerClasses = Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses");
var all_methonds = InnerClasses.class.getDeclaredMethods();
for(var i=0 ;i < all_methonds.length;i++){
var methods=(all_methonds[i]);
var methods_str = methods.toString();
var methodnames = methods_str.substr(methods_str.indexOf(class_name) + class_name.length + 1);
var methodname = methodnames.substr(0,methodnames.indexOf("("));
console.log(methodname);
InnerClasses[methodname].implementation = function(){
return true;
}
}
});
}
getMethods:获取当前类或父类或父接口的 public 修饰的字段;包含接口中 default 修饰的方法
getDeclaredMethods: 获取当前类的所有方法;包括 protected/默认/private 修饰的方法;不包括父类 、接口 public 修饰的方法
0x4 hook 动态dex
function hook_dyn_dex(){
Java.perform(function(){
var FridaActivity5 = Java.use("com.example.androiddemo.Activity.FridaActivity5");
Java.choose("com.example.androiddemo.Activity.FridaActivity5",{
onMatch: function(instance){
console.log(instance.getDynamicDexCheck().$classname);
},
onComplete: function(){
}
});
Java.enumerateClassLoaders({
onMatch: function(loader){
try{
if (loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")){
console.log(loader);
Java.classFactory.loader = loader;
}
} catch(error){
}
}, onComplete:function(){
}
});
var DynamicCheck = Java.use("com.example.androiddemo.Dynamic.DynamicCheck");
DynamicCheck.check.implementation = function(){
console.log("DynamicCheck.check");
return true;
}
});
}
enumerateClassLoaders 枚举 Java VM 中存在的类加载器
0x5 枚举已经加载的类
function hook_mul_class(){
Java.perform(function(){
Java.enumerateLoadedClasses({
onMatch: function(name,handle) {
if (name.indexOf("com.example.androiddemo.Activity.Frida6") >=0){
console.log(name);
var fridaclass6 = Java.use(name);
fridaclass6.check.implementation = function(){
console.log("frida 6 check:",this);
return true;
};
}
},onComplete: function(){
}
})
});
}
enumerateLoadedClasses(callbacks) 注意与上方的不是同一个函数
枚举当前已加载的类。callbacks
参数是一个对象,需要提供两个回调函数—— onMatch(className)
和 onComplete
。每次找到一个类就会调用一次 onMatch
,全部找完之后,调用 onComplete
0x6 打印调用栈
function print_stack(){
Java.perform(function(){
var Exception = Java.use("java.lang.Exception");
var instance = Exception.$new("print_stack");
var stack = instance.getStackTrace();
console.log(stack);
instance.$dispose();
});
}
通过app自带的Exception中的getstackTrace 直接打印
0x7 加载并调用自写dex中的函数
先是用安卓studio写自己想要用的函数编译出class文件
jar -cvf dex.jar DecodeUtils.class 用jar把class转换成jar
然后这里可以有两个方法一是安卓sdk中的工具dx –dex –output=ddex.dex dex.jar 将jar转换成dex
第二种就是用ApkToolBox直接jar转dex都是可以的
最后用adb命令把dex push到真机或模拟器中
var ddex2 = Java.openClassFile("/data/local/tmp/dd");
Java.perform(function(){
ddex2.load();
var DecodeUtils = Java.use("com.example.myapplication.DecodeUtils");
console.log("DecodeUtils.decode_p:", DecodeUtils.decode_p());
console.log("r to hex", DecodeUtils.r_to_hex());
var a = Java.use("com.tlamb96.kgbmessenger.b.a");
// hook 构造函数用$init
a.$init.implementation = function(i,str,str2,z){
this.$init(i,str,str2,z);
console.log("a.$init:",i,str,str2,z);
print_stack();
};
hook 在java层的构造函数需要前面加$init
0x8 hook so层
function hook_native() {
//获取模块的基址
var base_myjni = Module.findBaseAddress("libmyjni.so");
if (base_myjni) {
//获取模块的导出函数
var n2 = Module.findExportByName("libmyjni.so", "n2");
//thumb的函数,0x000011F8, 实际地址0xdba461f9
console.log("base_myjni:", base_myjni, "n2:", n2);
//hook模块的导出函数
Interceptor.attach(n2, {
onEnter: function (args) {
console.log("n2 onEnter:", args[0], args[1], args[2]);
}, onLeave: function (retval) {
}
});
}
}
Interceptor.attach(target, callbacks)
在target指定的位置进行函数调用拦截,target是一个NativePointer参数,用来指定你想要拦截的函数的地址。callbacks参数是一个对象:
onEnter: function(args): 被拦截函数调用之前回调,其中原始函数的 参数使用args数组(NativePointer对象数组)来表示,可以在这里修改函数的调用参数。
onLeave: function(retval): 被拦截函数调用之后回调,其中retval表示原始函数的返回值,retval是从NativePointer继承来的,是对原始返回值的一个封装,可以使用retval.replace(1337)调用来修改返回值的内容。注意:retval对象只在 onLeave函数作用域范围内有效,因此如果你要保存这个对象以备后续使用的话,一定要使用深拷贝来保存对象,比如:ptr(retval.toString())
0x9 hook libc函数
function hook_libc() {
//hook libc的函数
var strcmp = Module.findExportByName("libc.so", "strcmp");
console.log("strcmp:", strcmp);
Interceptor.attach(strcmp, {
onEnter: function (args) {
var str_2 = ptr(args[1]).readCString();
if (str_2 == "EoPAoY62@ElRD") {
console.log("strcmp:", ptr(args[0]).readCString(),
ptr(args[1]).readCString());
}
}, onLeave: function (retval) {
}
});
}
0x10 frida api进行读写
function write_reg_dat() {
//frida 的api来写文件
var file = new File("/sdcard/reg.dat", "w");
file.write("EoPAoY62@ElRD");
file.flush();
file.close();
}
0x11 把C函数定义为NativeFunction来写文件
function write_reg_dat2() {
//把C函数定义为NativeFunction来写文件
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/reg.dat");
var open_mode = Memory.allocUtf8String("w+");
var file = fopen(filename, open_mode);
console.log("fopen file:", file);
var buffer = Memory.allocUtf8String("EoPAoY62@ElRD");
var ret = fputs(buffer, file);
console.log("fputs ret:", ret);
fclose(file);
}
0x12 hook_native
function hook_native() {
var base_hello_jni = Module.findBaseAddress("libhello-jni.so");
if (base_hello_jni) {
//ollvm默认的字符串混淆,静态的时候没法看见字符串
//执行起来之后,先调用.init_array里面的函数来解密字符串
//解密完之后,内存中的字符串就是明文状态了。
var addr_37070 = base_hello_jni.add(0x37070);
console.log("addr_37070:", ptr(addr_37070).readCString());
var addr_37080 = base_hello_jni.add(0x37080);
console.log("addr_37080:", ptr(addr_37080).readCString());
}
}
hook so的时候加地址使用.add
0x12 hook_RegisterNatives
function hook_libart() {
var module_libart = Process.findModuleByName("libart.so");
var symbols = module_libart.enumerateSymbols(); //枚举模块的符号
var addr_RegisterNatives = null;
for (var i = 0; i < symbols.length; i++) {
var name = symbols[i].name;
if (name.indexOf("art") >= 0) {
if ((name.indexOf("CheckJNI") == -1) && (name.indexOf("JNI") >= 0)) {
if (name.indexOf("RegisterNatives") >= 0) {
console.log(name);
addr_RegisterNatives = symbols[i].address;
}
}
}
}
if (addr_RegisterNatives) {
Interceptor.attach(addr_RegisterNatives, {
onEnter: function (args) {
console.log("addr_RegisterNatives:", hexdump(args[2]));
console.log("addr_RegisterNatives name:", ptr(args[2]).readPointer().readCString())
console.log("addr_RegisterNatives sig:", ptr(args[2]).add(Process.pointerSize).readPointer().readCString());
}, onLeave: function (retval) {
}
});
}
}
hexdump 16进制打印
readPointer() 指定要读取的指针的地址
0x13 inline_hook
function inline_hook() {
var base_hello_jni = Module.findBaseAddress("libhello-jni.so");
console.log("base_hello_jni:", base_hello_jni);
if (base_hello_jni) {
console.log(base_hello_jni);
//inline hook
var addr_07320 = base_hello_jni.add(0x07320);
Interceptor.attach(addr_07320, {
onEnter: function (args) {
console.log("addr_07320 x13:", this.context.x13);
}, onLeave: function (retval) {
}
});
}
}
inline_hook 和上面的hook一样 this.context 后面跟的是寄存器 代表程序某个位置时某个寄存器的值
0x14 hook_dlopen
function hook_dlopen() {
var dlopen = Module.findExportByName(null, "dlopen");
Interceptor.attach(dlopen, {
onEnter: function (args) {
this.call_hook = false;
var so_name = ptr(args[0]).readCString();
if (so_name.indexOf("libhello-jni.so") >= 0) {
console.log("dlopen:", ptr(args[0]).readCString());
this.call_hook = true;
}
}, onLeave: function (retval) {
if (this.call_hook) {
inline_hook();
}
}
});
// 高版本Android系统使用android_dlopen_ext
var android_dlopen_ext = Module.findExportByName(null, "android_dlopen_ext");
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
this.call_hook = false;
var so_name = ptr(args[0]).readCString();
if (so_name.indexOf("libhello-jni.so") >= 0) {
console.log("android_dlopen_ext:", ptr(args[0]).readCString());
this.call_hook = true;
}
}, onLeave: function (retval) {
if (this.call_hook) {
inline_hook();
}
}
});
}
因为hook动态注册so时 还没加载so就frida注入了 所以需要先hook dlopen运行它加载后判断一下再hook,安卓8.0以下hook dlopen,8.0以上hook android_dlopen_ext