Giving yourself a window to debug a shared library before DT_INIT – With Frida, on Android

G

Hello folks!!! Today I’m gonna share an interesting approach i’ve found to give my self a space to debug before initializations.

Let’s spend 2 words about the problem and the goal:

Certain compilers and obfuscators take advantages of the init and init_array, which is a pointer and an array of pointers, which point to functions, which are executed at some point during the loading time. It’s impossible to attach to these functions, simply because the user space is still not “aware” of the base of our target and even more, we need a specific moment to attach/hook.

Understanding how a library got loaded and mapped into the system was of course the first step. The Android Linker in the Bionic repo provide an easy way to notify gdb about a module being loaded or even more, there is a debug flag on malloc which can be used on rooted device. Both of the solutions sucks, imho. GDB in 2018 is prevented in 2018 different funny ways with different funny tricks. Dynamic instrumentation should always be the way to go, I use Frida a lot, there are a lot of Frameworks which are built exactly for those purposes.

Let’s get back to the main focus. Digging through Android Linker sources highlights the exact point where those initializations subs are used.

To achieve our goal, we actually need a spot in the loading cycle where we are sure that the system is being reading the library. For that purpose, i’ve choosen the open plt from libc. Once we are sure that the library got opened, it’s a good time to attach to the linker, exactly in that method which retrieve the pointers of initializations stuffs (soinfo::prelink_image).

A quick look at the registers once prelink image got hooked, highlighted (even without debugging or understanding in the deep how the eco-system works) that R1 hold the address + 0x34 of the target base. A following hook to the first sub of initializations array of Clash Royale got hit with the following code… which is very raw and can be tweaked as hell. Enjoy <3

var test = false;
Interceptor.attach(Module.findExportByName("libc.so", "open"), {
    onEnter: function() {
        var what = Memory.readUtf8String(this.context.r0);
        if (what.indexOf("libg.so") >= 0) {
            test = true;
        }
    }, 
    onLeave: function(ret) {
        if (test) {
            test = false;
            var symb = Module.enumerateSymbolsSync("linker");
            var pp = 0;
            for (var sym in symb) {
                if (symb[sym].name.indexOf("prelink") >= 0) {
                    pp = symb[sym].address
                }
            }
            Interceptor.attach(pp, function() {
                console.log('leaked base before init array -> ' + this.context.r1.sub(0x34));
                Interceptor.attach(this.context.r1.sub(0x34).add(0x003928A4 + 1), function() {
                    console.log("hello first sub of init array");
                });
            });
            
            Interceptor.attach(Module.findExportByName("libc.so", "dlopen"), function() {
                    console.log('libg base confirm: ' + Process.findModuleByName("libg.so").base);
                    Interceptor.detachAll();                    
            });
        }            
    }
});
leaked base before init array -> 0xc93ce000
hello first sub of init array
libg base confirm: 0xc93ce000

 

About the author

GiovanniRocca

2 comments

This site uses Akismet to reduce spam. Learn how your comment data is processed.

  • Hi,

    thanks for this post. I tried to play a bit with your example and I couldn’t figure out what you meant with R1 = address + 0x34. I checked the source code http://androidxref.com/8.0.0_r4/xref/bionic/linker/linker.cpp#2822 but one can notice is that R1, which is the second argument of phdr_table_get_dynamic_section, is phnum, not load_bias.

    Here is my working version inspired from your example https://pastebin.com/cfEVDsnv. Now I can hook the global constructor with Frida!

    Thanks again for your example

    • Hi dude!yeah this was very primordial and wasn’t working on all os’s. 3 days ago we figured out that hooking phdr_table_get_dynamic_section made the job for everyone! It holds base in R2! It’s already built into Frick pull requested by MISHA-CRDEV