本文作者:杉木@涂鸦智能安全实验室
Frida 常用js API
Frida hook基础
之前介绍了frida的部署运行,以及简单的实操;详细查看通过一道CTF题目来认识一下Frida_frida ctf-CSDN博客,这次来了解一下frida的其他常见使用;这里参考了Frida Android hook | Sakuraのblog
不过原作者没有放出这里的题目,只有关键地方的源码,但是也没关系,只看源码也很好理解,通过对关键点分析,并熟悉firda的功能函数调用;后面的分析会有题目,可以后面再来练手;
打印参数、主动调用、修改返回值
//打印参数Java.use("com.example.androiddemo.Activity.LoginActivity").a.overload('java.lang.String', 'java.lang.String').implementation = function (str, str2){var result = this.a(str, str2);console.log("args0:"+str+" args1:"+str2+" result:"+result);return result;//主动调用var login = Java.use("com.example.androiddemo.Activity.LoginActivity")var result = login.a("1234","1234")console.log(result)//修改返回值Java.use("com.example.androiddemo.Activity.FridaActivity1").a.implementation = function (x) {return "666"}
主动调用静态/非静态函数
被hook对象java代码
public class FridaActivity2 extends BaseFridaActivity {private static boolean static_bool_var = false;private boolean bool_var = false;public String getNextCheckTitle() {return "当前第2关";}//关键点
//静态函数private static void setStatic_bool_var() {static_bool_var = true;}
//动态函数private void setBool_var() {this.bool_var = true;}public void onCheck() {if (!static_bool_var || !this.bool_var) {super.CheckFailed();return;}CheckSuccess();startActivity(new Intent(this, FridaActivity3.class));finishActivity(0);}
}
这一关的关键在于下面的if判断要为false,则static_bool_var
和this.bool_var
都要为true。
这样就要调用setBool_var
和setStatic_bool_var
两个函数了。
function ch2() {Java.perform(function () {console.log("start")var FridaActivity2 = Java.use("com.example.androiddemo.Activity.FridaActivity2")//hook静态函数直接调用FridaActivity2.setStatic_bool_var()//hook动态函数,找到instance实例,从实例调用函数方法Java.choose("com.example.androiddemo.Activity.FridaActivity2", {onMatch: function (instance) {instance.setBool_var()},onComplete: function () {console.log("end")}})})
}
setImmediate(ch2)
设置静态/非静态成员变量的值
public class FridaActivity3 extends BaseFridaActivity {private static boolean static_bool_var = false;private boolean bool_var = false;private boolean same_name_bool_var = false;public String getNextCheckTitle() {return "当前第3关";}private void same_name_bool_var() {Log.d("Frida", static_bool_var + " " + this.bool_var + " " + this.same_name_bool_var);}public void onCheck() {//关键点if (!static_bool_var || !this.bool_var || !this.same_name_bool_var) {super.CheckFailed();return;}CheckSuccess();startActivity(new Intent(this, FridaActivity4.class));finishActivity(0);}
}
关键还是让if (!static_bool_var || !this.bool_var || !this.same_name_bool_var)
为false,则三个变量都要为true
function ch3() {Java.perform(function () {console.log("start")var FridaActivity3 = Java.use("com.example.androiddemo.Activity.FridaActivity3")FridaActivity3.static_bool_var.value = trueJava.choose("com.example.androiddemo.Activity.FridaActivity3", {onMatch: function (instance) {instance.bool_var.value = trueinstance._same_name_bool_var.value = true},onComplete: function () {console.log("end")}})})
}
这里要注意类里有一个成员函数和成员变量都叫做same_name_bool_var
,这种时候在成员变量前加一个_
,修改值的形式为xx.value = yy
总结:
- 静态函数直接use class然后调用方法,非静态函数需要先choose实例然后调用
- 设置成员变量的值,写法是
xx.value = yy
,其他方面和函数一样。 - 如果有一个成员变量和成员函数的名字相同,则在其前面加一个
_
,如_xx.value = yy
内部类,枚举类的函数并hook-$
public class FridaActivity4 extends BaseFridaActivity {public String getNextCheckTitle() {return "当前第4关";}//关键点private static class InnerClasses {public static boolean check1() {return false;}public static boolean check2() {return false;}public static boolean check3() {return false;}public static boolean check4() {return false;}public static boolean check5() {return false;}public static boolean check6() {return false;}private InnerClasses() {}}public void onCheck() {if (!InnerClasses.check1() || !InnerClasses.check2() || !InnerClasses.check3() || !InnerClasses.check4() || !InnerClasses.check5() || !InnerClasses.check6()) {super.CheckFailed();return;}CheckSuccess();startActivity(new Intent(this, FridaActivity5.class));finishActivity(0);}
}
var InnerClasses = Java.use("com.example.androiddemo.Activity.FridaActivity4$InnerClasses")console.log("start")InnerClasses.check1.implementation = function () {return true}
public static boolean com.example.androiddemo.Activity.FridaActivity4$InnerClasses.check1()
总结:
- 对于内部类,通过
类名$内部类名
去use或者choose - 对use得到的clazz应用反射,如
clazz.class.getDeclaredMethods()
可以得到类里面声明的所有方法,即可以枚举类里面的所有函数。
外部类,枚举class,hook并修改返回值-enumerateLoadedClasses
import com.example.androiddemo.Activity.Frida6.Frida6Class0;
import com.example.androiddemo.Activity.Frida6.Frida6Class1;
import com.example.androiddemo.Activity.Frida6.Frida6Class2;public class FridaActivity6 extends BaseFridaActivity {public String getNextCheckTitle() {return "当前第6关";}//关键点public void onCheck() {if (!Frida6Class0.check() || !Frida6Class1.check() || !Frida6Class2.check()) {super.CheckFailed();return;}CheckSuccess();startActivity(new Intent(this, FridaActivity7.class));finishActivity(0);}
}
这关是import了一些类,然后调用类里的静态方法,所以我们枚举所有的类,然后过滤一下,并把过滤出来的结果hook上,改掉其返回值。
function ch6() {Java.perform(function () {Java.enumerateLoadedClasses({onMatch: function (name, handle){if (name.indexOf("com.example.androiddemo.Activity.Frida6") != -1) {console.log("name:" + name + " handle:" + handle)Java.use(name).check.implementation = function () {return true}}},onComplete: function () {console.log("end")}})})
}
总结:
- 通过
Java.enumerateLoadedClasses
来枚举类,然后name.indexOf(str)
过滤一下并hook。
**hook动态加载的dex,与查找interface-**loader.findClass
package com.example.androiddemo.Dynamic;public interface CheckInterface {boolean check();
}
...
//关键点
public class DynamicCheck implements CheckInterface {public boolean check() {return false;}
}
...
public class FridaActivity5 extends BaseFridaActivity {private CheckInterface DynamicDexCheck = null;...public CheckInterface getDynamicDexCheck() {if (this.DynamicDexCheck == null) {loaddex();}return this.DynamicDexCheck;}/* access modifiers changed from: protected */public void onCreate(Bundle bundle) {super.onCreate(bundle);loaddex();//this.DynamicDexCheck = (CheckInterface) new DexClassLoader(str, filesDir.getAbsolutePath(), (String) null, getClassLoader()).loadClass("com.example.androiddemo.Dynamic.DynamicCheck").newInstance();}public void onCheck() {if (getDynamicDexCheck() == null) {Toast.makeText(this, "onClick loaddex Failed!", 1).show();} else if (getDynamicDexCheck().check()) {CheckSuccess();startActivity(new Intent(this, FridaActivity6.class));finishActivity(0);} else {super.CheckFailed();}}
}
function ch5() {Java.perform(function () {console.log("start")Java.enumerateClassLoaders({onMatch: function (loader) {try {if(loader.findClass("com.example.androiddemo.Dynamic.DynamicCheck")){console.log("Successfully found loader")console.log(loader);Java.classFactory.loader = loader ;}}catch(error){console.log("find error:" + error)}},onComplete: function () {console.log("end1")}})Java.use("com.example.androiddemo.Dynamic.DynamicCheck").check.implementation = function () {return true}console.log("end2")})
}
setImmediate(ch5)
总结:
- 通过
enumerateClassLoaders
来枚举加载进内存的classloader,再loader.findClass(xxx)
寻找是否包括我们想要的interface的实现类,最后通过Java.classFactory.loader = loader
来切换classloader,从而加载该实现类。
搜索interface的具体实现类
利用反射得到类里面实现的interface数组,并打印出来。
function more() {Java.perform(function () {Java.enumerateLoadedClasses({onMatch: function (class_name){if (class_name.indexOf("com.example.androiddemo") < 0) {return}else {var hook_cls = Java.use(class_name)var interfaces = hook_cls.class.getInterfaces()if (interfaces.length > 0) {console.log(class_name + ": ")for (var i in interfaces) {console.log("\t", interfaces[i].toString())}}}},onComplete: function () {console.log("end")}})})
}
练手题
题目地址:https://github.com/tlamb96/kgb_messenger
官方js调用api
JavaScript API | Frida • A world-class dynamic instrumentation toolkit
漏洞悬赏计划:涂鸦智能安全响应中心(https://src.tuya.com)欢迎白帽子来探索。