Frida experience of source code analysis of Android jar / Library

Time:2021-1-15

catalog:Source code analysis of Android jar / Library 

Frida experience:

effect:

Android mobile phone can hook Java and so layer code, monitor data and process memory data.

Dynamic tool kit for developers, reverse engineers and security researchers.

Chestnut:

Operation steps:

    1、https://github.com/frida/frida/releasesDownload the executable program suitable for the running environment. This is arm64, so I downloaded the latest version of Frida 64 bit.

2. On the PC side, install the python environment. You can download a python installation package directly from the official website to install it. What I install here is Python 3. The installation package will configure its own environment variables. CMD runs Python to see if there are any. If there are, the environment variables are installed.

3. Use PIP install Frida to install Frida environment based on python. Then install PIP install Frida tools, I think most of them are in accordance with. Tools.

4. Everything is OK. Start running,,, copy the executable file obtained in step 1 to the mobile phone ⁃ ADB push D: ⁃ XXX ⁃ XXX ⁃ frixxxxx / data / local / tmp/

5. Because the executable file on the push has no permission by default, the next step is to authorize.. The ADB shell enters the “Su” of the mobile phone and gets the root permission.. CD / data / local enter the local folder. Chmod 777 – R * authorization is executable.. CD TMP enter TMP and execute: Enter / frxxx to run exe

The effect is as follows:

    

6. Port forwarding, because the data of the mobile phone and the local computer need to be interconnected, you need to set port forwarding, two ports: execute two commands

    

adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043

7. Then you can allow python.

8. Code example:

import frida, sys

jsCode = """

Java.perform(function () {
    var impl = Java.use("com.android.test.TestUtil");
    impl.test1.implementation = function () {
        send("test1 called!");
        this.test1();
    };

    impl.test2.implementation = function () {
        send("test2 called!");
    };

    impl.test3.implementation = function (a) {
        send("test3 called!");
    };

    impl.test4.implementation = function (a,b,c) {
        send("test4 called!");
        return this.test4(a,b,c);
    };

    impl.test5.overload("int").implementation = function (a) {
        send("test5 1 called!");
    };

    impl.test5.overload("int", "java.lang.String").implementation = function (a,b) {
        send("test5 2 called!");
    };

});

"""
def message(message, data):
    if message["type"] == 'send':
        print(u"[*] {0}".format(message['payload']))
    else:
        print(message)

process = frida.get_remote_device().attach("com.android.test")
script= process.create_script(jsCode)
script.on("message", message)
script.load()
sys.stdin.read()

In the code, JS is written to a string variable. Multiple lines are in this fixed format.

Hook the test1 method. At the same time, call the test1 method later. If not, the method will not be executed. Refer to test2

Test3 is for a single parameter, and test4 is for multiple parameters. Test5 is overloaded for different parameters, and the parameter type needs to be specified, which is dealt with above.

Execute the send method of JS, return the content, send it to, and bind the scriptp. Def message (message, data) is used for output. You can also use it in JS console.log Output parameters.

For the case of complex type, you can directly get the variable of complex type by a.xxxx.

8.2. Other JS

//Traverse all JS. Specify the name and filter
Java.perform(function(){
    Java.enumerateLoadedClasses({
        onMatch: function(className) {
            if(className.toString().indexOf("down") >= 0){
                send(className+'\"));');
            }
        },
        onComplete:function(){
            send("done");
        }
    });
});

9: So, hook processing.

  

import frida, sys

jsCode3 = """
Java.perform(function(){
    
    var exports = Module.enumerateExportsSync("xxx.so");
    for(var i = 0; i < exports.length; i++) {
        if(exports[i] && exports[i].name != undefined){
            if(exports[i].name.indexOf("rapidjson")>=0){
                var nativePointer = new NativePointer(exports[i].address);
                send("exports " + exports[i].name + " nativePointer " + nativePointer);
                Interceptor.attach(nativePointer, {
                    onEnter: function(args) {
                        send(this.context.pc + " called! ");
                        send("context " + JSON.stringify(this.context));
                        
                        //Save the length and call in the return method
                        this.fd = args[1];
                        // send(nativePointer + " arg0 " +args[0] + " arg1 " +args[1] + " arg2 " +args[2]);
                    },
                    onLeave:function(retval){
                        send(this.context.pc + " retval " +retval);
                        
                        //Dump content
                        var size = parseInt(this.fd);
                        if(size > 0){
                            const r = Memory.alloc();
                                
                            //Copy to module.base The first 10 bytes of the address must be 7F 45 4C 46... Because of the magic attribute of an ELF file.
                            Memory.copy(r,ptr(retval),size);
                            console.log(hexdump(r, {
                                offset: 0,
                                length: size,
                                header: true,
                                ansi: false
                            }));
                        }
                    }
                });
            }
        }
    }
});

""";

jsCode2 = """
Java.perform(function(){
    //Traverse all modules
    Process.enumerateModules({
        onMatch: function(exp){
            //Get the module object of so first
            var module = Process.findModuleByName(exp.name); 
            
            //It's a wildcard
            var pattern = "11 22 33 44 55 66"; //
            //Base address
            // console.log("base:" + module.base)
            //The search starts from the base address of the so, and the search size is the size of the so file. The data of the specified condition 03 49?? 50 20 44 is searched
            var res = Memory.scan(module.base, module.size, pattern, {
                onMatch: function(address, size){
                    //Search success
                    console.log ('found '+ pattern + "address is:+ address.toString ());  
                }, 
                onError: function(reason){
                    //Search failed
                    // console.log ('search failed '+ reason));
                },
                onComplete: function(){
                    //Search complete
                    // console.log (search complete)
                }
            });
        },
        onComplete: function(){
            send('stop');
        }
    });

    //Import function
    var imports = Module.enumerateImportsSync("xxx.so");
    for(var i = 0; i < imports.length; i++) {
        if(imports[i].name == 'strncat'){
            send(imports[i].name + ": " + imports[i].address);
            break;
        }
    }
    
    //Export function
    var exports = Module.enumerateExportsSync("xxx.so");
    for(var i = 0; i < exports.length; i++) {
        if(exports[i].name.indexOf('add') != -1){
            send(exports[i].name + ": " + exports[i].address);
            break;
        }
    }
});

""";
    
jsCode = """

var module = Process.findModuleByName("xxx.so"); 
var nativePointer = new NativePointer(module.base.add(0x1740));
send("nativePointer " + nativePointer);
Interceptor.attach(nativePointer, {
    onEnter: function(args) {
        send("mou sub_method called!");
    },
    onLeave:function(retval){
    
    }
});

var nativePointer = new NativePointer(ptr(0x00001122));
send("nativePointer " + nativePointer);
Interceptor.attach(nativePointer, {
    onEnter: function(args) {
        send("mou sub_method 2 called!");
    },
    onLeave:function(retval){
    
    }
});

"""
def message(message, data):
    if message["type"] == 'send':
        print(u"[*] {0}".format(message['payload']))
    else:
        print(message)

process = frida.get_remote_device().attach("com.android.test")
script= process.create_script(jsCode2)
script.on("message", message)
script.load()
sys.stdin.read()

Various methods are provided:

a> Traverse all so modules to find the specified keyword.

b> Traverse all the exported functions of a module. The name contains

c> Traverse all the import functions of a module, and the name contains.

d> Directly through the module to get the memory address and offset, hook

e> By using the member variable of this., in the result, dump a value of the input parameter to dump the content.

f> Output this.context Object.

Source code interpretation:

To be added

reference resources:

        https://www.anquanke.com/post/id/195215

  https://www.anquanke.com/member/131652

Source code:https://frida.re/

introduce:

 

Recommended Today

020_CSS3

catalog How to learn CSS What is CSS History of development quick get start Advantages of CSS Three ways to import CSS Expansion: two ways of writing external style selector Basic selector Hierarchy selector Structure pseudo class selector attribute selectors Beautify web page elements Why beautify web pages Span label: for the text that needs […]