Quick-start guide

For the impatient, here’s how to do function tracing with Frida:

~ $ pip install frida-tools
~ $ frida-trace -i "recv*" -i "read*" twitter
recv: Auto-generated handler: …/recv.js
# (snip)
recvfrom: Auto-generated handler: …/recvfrom.js
Started tracing 21 functions. Press Ctrl+C to stop.
    39 ms	recv()
   112 ms	recvfrom()
   128 ms	recvfrom()
   129 ms	recvfrom()

So as you can see, Frida injected itself into Twitter, enumerated the loaded shared libraries and hooked all the functions whose names start with either recv or read. It also generated some boilerplate scripts for taking care of inspecting the function calls as they happen. Now, those scripts are just examples that you are meant to edit to taste, and will be automatically reloaded as they change on the filesystem. By default they just print the name of the function, as you can see in the output above.

Now, let’s have a look at the generated recvfrom.js:

/*
 * Auto-generated by Frida. Please modify to match the
 * signature of recvfrom.
 *
 * This stub is somewhat dumb. Future verions of Frida
 * could auto-generate based on OS API references, manpages,
 * etc. (Pull-requests appreciated!)
 *
 * For full API reference, see:
 * https://frida.re/docs/javascript-api/
 */

{
    /**
     * Called synchronously when about to call recvfrom.
     *
     * @this {object} - Object allowing you to store state for
     * use in onLeave.
     * @param {function} log - Call this function with a string
     * to be presented to the user.
     * @param {array} args - Function arguments represented as
     * an array of NativePointer objects.
     * For example use args[0].readUtf8String() if the first
     * argument is a pointer to a C string encoded as UTF-8.
     * It is also possible to modify arguments by assigning a
     * NativePointer object to an element of this array.
     * @param {object} state - Object allowing you to keep
     * state across function calls.
     * Only one JavaScript function will execute at a time, so
     * do not worry about race-conditions. However, do not use
     * this to store function arguments across onEnter/onLeave,
     * but instead use "this" which is an object for keeping
     * state local to an invocation.
     */
    onEnter(log, args, state) {
        log("recvfrom()");
    },

    /**
     * Called synchronously when about to return from recvfrom.
     *
     * See onEnter for details.
     *
     * @this {object} - Object allowing you to access state
     * stored in onEnter.
     * @param {function} log - Call this function with a string
     * to be presented to the user.
     * @param {NativePointer} retval - Return value represented
     * as a NativePointer object.
     * @param {object} state - Object allowing you to keep
     * state across function calls.
     */
    onLeave(log, retval, state) {
    }
}

Now, replace the log() line with the following:

log("recvfrom(socket=" + args[0].toInt32()
    + ", buffer=" + args[1]
    + ", length=" + args[2].toInt32()
    + ", flags=" + args[3]
    + ", address=" + args[4]
    + ", address_len=" + args[5].readPointer().toInt32()
    + ")");

Save the file (it will be reloaded automatically) and perform some action in your Twitter application to trigger some network activity. You should now see something along the lines of:

  8098 ms	recvfrom(socket=70,
                         buffer=0x32cc018, length=65536,
                         flags=0x0,
                         address=0xb0420bd8, address_len=16)

That’s nothing, though. The real magic happens when you start building your own tools using the Python API that frida-trace is built on top of.