前言
Frida Hook比较多的点无疑是Java层函数Hook和Native函数Hook。前序文章讲文件读写确实就是Native层函数Hook。
故本文详细介绍Java层如何使用Frida进行Hook。
相关知识
1. Java.perform()
Java.perform()
函数是 Frida JavaScript API 中的一个函数,用于在目标应用程序的上下文中执行 JavaScript 代码。它是一个静态方法,可以在 JavaScript 中直接调用。
Java.perform()
方法的参数是一个匿名函数,该函数中包含需要在新的 Java 虚拟机上下文中执行的代码。
java.perform(function(){});
需要注意的是,Java.perform()
方法中的 JavaScript 代码是在一个新的 Java 虚拟机上下文中执行的,而不是在当前的 JavaScript 上下文中执行。这意味着,在 Java.perform()
方法中定义的变量和函数在执行完毕后将不再存在。因此,在 Java.perform()
方法中定义的变量和函数,只能在该方法的参数函数中使用,不能在该方法的外部使用。
2. 匿名函数
在 JavaScript 中,匿名函数可以使用 function
关键字和箭头函数 =>
来定义。
匿名函数通常用于一些只需要执行一次的逻辑,或者将其作为参数传递给其他函数,例如:
// 将匿名函数作为参数传递给 Array.prototype.map() 方法
var numbers = [1, 2, 3, 4, 5];
var squaredNumbers = numbers.map(function (n) {
return n * n;
});
console.log(squaredNumbers); // 输出:[1, 4, 9, 16, 25]
在上述代码中,使用 Array.prototype.map() 方法将一个数组中的每个元素平方,并将结果存储在一个新的数组中。map()
方法的参数是一个匿名函数,该函数将每个元素作为参数传递,并返回该元素的平方值。
需要注意的是,匿名函数也可以直接执行,但通常情况下,我们通过将其赋值给变量或者作为参数传递给其他函数来使用匿名函数。例如,在下面的代码中,使用匿名函数直接执行一个函数:
(function () {
console.log('Hello, world!');
})();
在上述代码中,使用一个匿名函数来定义一个函数体,并使用 ()
运算符将其直接执行。这种方式可以用于定义一些只需要执行一次的逻辑。
3. Java.use()
用于加载目标应用程序中的 Java 类,并返回该类的一个代理对象
可以用于访问和操作该类的静态属性和方法,或者创建该类的实例对象
可以在 Java.perform()
中使用
Java.perform(function () {
var MyClass = Java.use('com.example.MyClass');
// 访问静态属性
console.log('[*] MyStaticField = ' + MyClass.MyStaticField.value);
// 调用静态方法
MyClass.MyStaticMethod('Hello, world!');
// 创建实例对象
var instance = MyClass.$new();
console.log('[*] Instance created: ' + instance);
// 访问实例属性
console.log('[*] myField = ' + instance.myField.value);
// 调用实例方法
instance.myMethod('Hello, world!');
});
4. Java.choose()
Java.choose()用于在目标应用程序中搜索符合条件的 Java 对象,并在搜索到符合条件的对象时执行一个回调函数,可以使用 this
关键字引用被选择的对象,执行一些针对该对象的操作。
Java.choose('com.example.MyClass', {
onMatch: function (instance) {
console.log('[*] Found instance: ' + instance);
// 调用对象的方法
instance.myMethod('Hello, world!');
// ...
},
onComplete: function () {
console.log('[*] Search completed');
}
});
需要注意的是,Java.use()
和 Java.choose()
的主要区别在于,前者是用于加载 Java 类,并返回该类的代理对象,而后者是用于搜索符合条件的 Java 对象,并在搜索到符合条件的对象时执行一个回调函数。因此,它们的使用场景和操作方式可能会有所不同。
5. overload()
代理对象可以用于访问和操作该类的静态属性和方法,或者创建该类的实例对象。但是,如果需要 Hook 该类的某个方法,需要使用 overload()
和 implementation()
方法来完成。
overload()
方法用于获取该类中所有重载版本的某个方法的引用。例如,如果需要 Hook com.example.MyClass
类中的 myMethod()
方法,可以使用 overload()
方法获取该方法的引用,例如:
var MyClass = Java.use('com.example.MyClass');
var myMethodOverloads = MyClass.myMethod.overload('int', 'java.lang.String');
在上述代码中,使用 overload()
方法获取 com.example.MyClass
类中的 myMethod()
方法的引用,并指定该方法的参数列表为 (int, java.lang.String)
。
6. implementation()
implementation()
方法用于替换某个方法的实现。例如,如果需要 Hook com.example.MyClass
类中的 myMethod()
方法,可以使用 implementation()
方法来替换该方法的实现,例如:
var MyClass = Java.use('com.example.MyClass');
var myMethodOverloads = MyClass.myMethod.overload('int', 'java.lang.String');
myMethodOverloads.implementation = function (arg1, arg2) {
console.log('[*] myMethod called with arguments: ' + arg1 + ', ' + arg2);
// 调用原始方法的实现
var result = this.myMethod(arg1, arg2);
// 修改返回值
result = result + ' - Hooked';
return result;
};
在上述代码中,使用 implementation()
方法替换 myMethodOverloads
变量引用的 myMethod()
方法的实现。在新的实现中,首先打印方法的参数,然后调用原始方法的实现,并修改返回值。
unzip操作Hook
基于上述的知识点,我们来实现一个需求,我想监控应用的解压文件操作,因为如果压缩包可控,则有可能造成路径穿越文件覆盖的漏洞发生。可以写下如下frida 脚本:
先收集可能解压的类
java.util.zip.ZipFile
java.util.zip.ZipInputStream
java.util.zip.ZipOutputStream
java.util.jar.JarFile
java.util.jar.JarInputStream
java.util.jar.JarOutputStream
在根据不同类的方法进行hook即可,下面以java.util.zip.ZipFile为例,具体情况可以通过逆向进行进一步确认,当然也可以同时全部Hook进行观察,其实我想要的信息就是什么堆栈触发了unzip操作,具体unzip的文件路径是什么即可。然后去找对应的代码片段进行分析,看看有没有对文件存在性和文件名进行路径穿越判断即可。
Java.perform(function () {
var ZipFile = Java.use('java.util.zip.ZipFile');
// Hook ZipFile类的构造函数
ZipFile.$init.overload('java.io.File').implementation = function (file) {
// 打印调用栈
console.log('ZipFile constructor called with file:', file);
console.log(Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Exception').$new()));
// 调用原始构造函数
this.$init(file);
};
ZipFile.getEntry.overload('java.lang.String').implementation = function (name) {
console.log('ZipFile.getEntry called with name:', name);
console.log(Java.use('android.util.Log').getStackTraceString(Java.use('java.lang.Exception').$new()));
return this.getEntry(name);
};
});
后话
至此,Frida如何hook Java函数基础操作部分也记录完成了,同时也介绍了一种如何监听unzip操作进行漏洞挖掘辅助的实例。