Some other good reasons to try Dwarf and make it better – winning code step and trace with Frida

Hello malware analyst, reverse engineers, crackers and researchers from all around the world, this is a blog post which is meant to document the result of the effort spent on my tool in the past weeks.

Let’s start with the kind-of-boring things, which meant to be said.

Run Dwarf anywhere

Now you can setup Dwarf. It will have the update features disabled as it rely on github, so it will be up to you to update the source and re-install. This will allow you to run Dwarf anywhere.

sudo python3 setup.py install

cd ~
dwarf -s work/myagent.js /bin/man open 

After that short info, we can start with the cool things.

Java tracer

Was a private api used by the core. The data returned was then rendered into the ui. Now, you can add your callbacks – and if you are familiar with Frida I’m sure you will like it

const classes = ['com.targerclass.first', 'com.targetclass.second', '...'];

// use ui
startJavaTracer(classes);

// use custom callbacks
startJavaTracer(classes, {
    onEnter: function(args) {
    },
    onLeave: function(ret) {
        return 'patched';
    }
});

Native code step and tracer

My baby Dwarf is now able to step and trace native code. This thanks to the huge work from Ole (that commit) which makes now possible to detach the Stalker without crashing the target process.

The real challenge, which touch both of the features, was to figure out the first instruction that we are interested at – let me explain you better.

—- target space —- start stalker – frida setup the first basic block in it’s own space
—- target space —- jump on that first basic block – frida replace the target first basic block with the jump, and move the target basic block in it’s space to be executed before returning to target space
—- frida space —- frida do it’s things
—- frida space —- execute first target basic block
—- frida space —- jump back on target space
—- target space —- keep traced execution

Now – you should got the problem. If we want to step, the first instruction we are interested will be in the frida space unless it’s a single instruction block – so that, we can’t rely on the address to understand when placing the breakpoint for the step.

The quickest and less painful solution that i’ve found is to count ret instructions. When we run in frida space, there are a total of 5 returns in both arm64 and x64 with the unique difference that arm64 will execute one more block of code, instead x64 will immediatly execute target code after the 5th return. Here is the relevant code stripped of Dwarf things.

            var retCount = 0;
            var arm64BlockCount = 0;
            var firstInstructionExec = false;
            var firstBlockCallout = false;
            var calloutHandled = false;

            Stalker.follow(tid, {
                transform: function (iterator) {
                    var instruction;

                    while ((instruction = iterator.next()) !== null) {
                        iterator.keep();

                        if (!calloutHandled) {
                            if (retCount > 4) {
                                if (isArm64 && arm64BlockCount < 2) {
                                    continue;
                                }

                                if (!firstInstructionExec) {
                                    firstInstructionExec = true;
                                    continue;
                                }

                                calloutHandled = true;
                                firstBlockCallout = true;                           
                                iterator.putCallout(callout);
                            }

                            if (instruction.mnemonic === 'ret') {
                                retCount++;
                            }
                        } else {
                            iterator.putCallout(callout);
                        }
                    }

                    if (retCount > 4 && isArm64) {
                        arm64BlockCount += 1;
                    }

                    if (firstBlockCallout) {
                        firstBlockCallout = false;
                    }
                }
            });

In the top menu “Process” you can now find 3 more actions: step – to step single instruction, step call – to step to the next call and step block – to step to the next block.

In addition, an api to trace is now available which if used with brain can also replace strace.

startNativeTracer(function() {
    console.log(this.context);
    console.log(this.instruction);
    if (this.instruction.mnemonic === 'svc') {
        breakpoint();
    } else if (this.context.pc === 0x10000) {
        this.stop();
    }
});

For whose already use Dwarf, that breakpoint() could sound pretty new. That api has been made public to handle similar scenarios – when you are outside the context (the address you hooked) but you want to switch the control to the ui, breakpoint() is that api you need.

To conclude, due to the nature of the software (multiarch, multios, multiwhatever) there are bugs here and there – and it’s the free time project. Feel free to get into the slack if your setup is not working.

Leave a Reply

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