【Android】Frida Java Hook + unzip 操作Hook

Preface

The most popular features of Frida Hook are undoubtedly Java layer function Hook and Native function Hook. The previous article talks about file reading and writing, which is indeed the Native layer function Hook.

Therefore, this article introduces in detail how the Java layer uses Frida to perform Hook.

related information

1. Java.perform() 

Java.perform() A function is a function in the Frida JavaScript API that is used to execute JavaScript code in the context of the target application. It is a static method that can be called directly from JavaScript.

Java.perform() The method's parameter is an anonymous function that contains code that needs to be executed in the context of the new Java virtual machine.

java.perform(function(){});

It should be noted that Java.perform() the JavaScript code in the method is executed in a new Java virtual machine context, not in the current JavaScript context. This means that  Java.perform() variables and functions defined in a method will no longer exist after execution. Therefore,  Java.perform() variables and functions defined in a method can only be used in the parameter function of the method and cannot be used outside the method.

2. Anonymous functions

In JavaScript, anonymous functions can   be defined using function keywords and arrow functions  .=>

Anonymous functions are often used for logic that only needs to be executed once, or passed as arguments to other functions, for example:

// 将匿名函数作为参数传递给 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]

In the above code, the Array.prototype.map() method is used to square each element in an array and store the result in a new array. map() The method's parameter is an anonymous function that passes each element as a parameter and returns the squared value of that element.

It should be noted that anonymous functions can also be executed directly, but usually we use anonymous functions by assigning them to variables or passing them as parameters to other functions. For example, in the following code, an anonymous function is used to directly execute a function:

(function () {
    console.log('Hello, world!');
})();

In the above code, an anonymous function is used to define a function body and  () operators are used to execute it directly. This method can be used to define some logic that only needs to be executed once.

3. Java.use() 

Used to load a Java class in the target application and return a proxy object of the class

Can be used to access and operate the static properties and methods of the class, or create instance objects of the class

Can be  Java.perform() used in

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() is used to search for qualified Java objects in the target application, and execute a callback function when a qualified object is found. You can use keywords to refer to the selected object and  this perform some operations on the object. .

Java.choose('com.example.MyClass', {
    onMatch: function (instance) {
        console.log('[*] Found instance: ' + instance);

        // 调用对象的方法
        instance.myMethod('Hello, world!');

        // ...
    },
    onComplete: function () {
        console.log('[*] Search completed');
    }
});

It should be noted that  the main difference between and is that the former is used to load a Java class and return the proxy object of the class, while the latter is used to search for Java objects that meet the conditions and execute Java.use() a  Java.choose()Callback. Therefore, their usage scenarios and modes of operation may vary.

5. overload()

Proxy objects can be used to access and operate the static properties and methods of the class, or to create instance objects of the class. However, if you need to Hook a method of this class, you need to use the  overload() and  implementation() method to complete it.

overload() Method is used to obtain a reference to all overloaded versions of a method in the class. For example, if you need  a method com.example.MyClass in a Hook class  myMethod() , you can use  overload() a method to get a reference to the method, for example:

var MyClass = Java.use('com.example.MyClass');
var myMethodOverloads = MyClass.myMethod.overload('int', 'java.lang.String');

In the above code, use  overload() the method to get  a reference to the method com.example.MyClass in the class  myMethod() and specify the parameter list of the method  (int, java.lang.String).

6.  implementation() 

implementation() Method is used to replace the implementation of a method. For example, if you need  a method com.example.MyClass from the Hook class  myMethod() , you can  implementation() replace the method's implementation with a method, for example:

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;
};

In the above code, use  implementation() method to replace  the implementation of the method myMethodOverloads that the variable refers to  myMethod() . In the new implementation, the method's parameters are printed first, and then the original method's implementation is called and the return value is modified.

unzip operation Hook

Based on the above knowledge points, let's implement a requirement. I want to monitor the decompression file operation of the application, because if the compressed package is controllable, it may cause a path traversal file overwriting vulnerability. You can write the following frida script:

First collect classes that may be decompressed

  • 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);
    };
});


 Afterword

At this point, the basic operation part of how Frida hooks Java functions has been recorded. It also introduces an example of how to monitor the unzip operation to assist in vulnerability mining.

Guess you like

Origin blog.csdn.net/xiru9972/article/details/131118298