####################### *Stalk* ing the process ####################### .. index:: Stalker The ``Stalker`` module has a very particular purpouse: To inspect what's going on on a particular thread __________________ Following a thread __________________ Using the ``Stalker`` module we can ``follow`` a thread to track certain events - f.e. function ``call`` s .. code-block:: JavaScript :linenos: var main_thread = Process.getCurrentThreadId(); Stalker.follow(main_thread, { events: { call: true, // Track the CALL instruction ret: false, // Track the RET instruction exec: false, // Track all instructions block: false, // Track executed blocks compile: false // Track compiled blocks }, onReceive: function(event){ // Explained below }, onCallSummary: function(summary){ // Explained below }, transform: function(instruction_iterator){ // Explained below } }); **WARNING: Tracking the `exec` event is discouraged as it will log a lot of data and heavily impact the performance.** _____________ *onReceive* _____________ The ``onReceive`` callback is called each time an event is logged - from the ones specified in the ``events`` attribute. This may be used to create a calltrace for a thread. The function we specify as the ``onReceive`` callback receives an event object as the only argument. In order to get what we need from this object (A GumEvent struct) we have to parse it using the ``Stalker.parse(event)`` api call which will return a tuple. The format of the tuple will vary depending on the event we are parsing. For more details about the fields you can consult the GumEvent definition: `gumevent.h link`_. Lets say we have an obfuscated code that makes ``call`` s to some dynamically calculated addresses, instead of spending lots of time trying to debug the application or attempting tedious static analisys we can inject Frida, find the thread that calls the ``obfuscated_function`` and trace all ``call`` s and ``ret`` s from that point. In our case we are going to be logging for just 5 seconds. .. code-block:: JavaScript :linenos: var log = new File('calltrace.log', 'w'); var got_thread_id = false; // Flag to know when to start logging var thread = null; /* Attach to our target function */ Interceptor.attach(ptr(obfuscated_function), { onEnter: function(args){ /* Get the thread id */ thread = Process.getCurrentThreadId(); /* Make our callback to stop stalking the thread after 5s */ setTimeout(function(){ Stalker.unfollow(thread); log.flush(); log.close(); }, 5000 ); /* Detach our interceptor to avoid logging incorrect addresses */ Interceptor.revert(ptr(obfuscated_function)); Stalker.follow(thread, { events:{ call: true, ret: true }, onReceive: function(events){ var calls = Stalker.parse(events); for(var i in calls){ log.write('\t'.repeat(calls[i][3]) + ' ' + calls[i][1] + ':' + calls[i][2] + '\n'); } } }); } } The result would be something like: .. code-block:: JavaScript :linenos: call 0x7fdc96c0f2ba:0x7fdc96b63162 ret 0x7fdc96c0f2e3:0x7fdc96c03b29 ret 0x7fdc96c03b33:0x7fdc96c0dae7 ret 0x7fdc96c0db01:0x7fdc96bd5c34 call 0x7fdc96bd5c68:0x7fdc96bdbc64 call 0x7fdc96bd5c76:0x7fdc96bdc43e ret 0x7fdc96bd5d57:0x7fdc9dc02076 ret 0x7fdc9dc020a5:0x7fdc9dc0271e call 0x7fdc9d2ec170:0x7fdc9d30c6e0 ret 0x7fdc9d30c70b:0x7fdc9d2ec175 call 0x7fdc9d2ec197:0x7fdc9d30c740 ret 0x7fdc9d30c76f:0x7fdc9d2ec19c ret 0x7fdc9d2ec1a9:0x7fdc9dc02713 call 0x7fdc9dc0216a:0x7fdc96bd5d58 ret 0x7fdc9dc0219e:0x560f689e16f9 ret 0x560f689e16d7:0x560f689e19e8 call 0x560f689e18c7:0x560f689dfe90 call 0x7fdc9d2f0be1:0x7fdc9d30c6e0 ret 0x7fdc9d30c70b:0x7fdc9d2f0be6 call 0x7fdc9d2f0c08:0x7fdc9d30c740 ret 0x7fdc9d30c76f:0x7fdc9d2f0c0d ret 0x7fdc9d2f0c19:0x560f689e18cc call 0x560f689e1c6f:0x560f689e1790 call 0x560f689e17a7:0x560f689dffa8 call 0x7fdc9d2ec0a0:0x7fdc9d30c6e0 ret 0x7fdc9d30c70b:0x7fdc9d2ec0a5 call 0x7fdc9d2ec0c4:0x7fdc9d30c740 ret 0x7fdc9d30c76f:0x7fdc9d2ec0c9 ret 0x7fdc9d2ec0d6:0x560f689e17ac ret 0x560f689e17c7:0x560f689e1c74 call 0x560f689e18c7:0x560f689dfe90 call 0x7fdc9d2f0be1:0x7fdc9d30c6e0 ret 0x7fdc9d30c70b:0x7fdc9d2f0be6 call 0x7fdc9d2f0c08:0x7fdc9d30c740 ret 0x7fdc9d30c76f:0x7fdc9d2f0c0d Of course, as we have started stalking at some arbitrary point ``call`` s and ``ret`` s may not match on the first round, but we'll see that it gets aligned on following iterations. _________________ *onCallSummary* _________________ The ``onCallSummary`` callback is useful when we are only interested in getting a list of all the called functions or how many times each one was called. The object sent to this function is a simple JavaScript ``Object`` with the address of the function called as the key and the number of calls as the associated value. Reusing our previous example, we'll log what functions were called and how many times at the end of our calltrace log: .. code-block:: JavaScript :linenos: var log = new File('calltrace.log', 'w'); var got_thread_id = false; // Flag to know when to start logging var thread = null; /* Attach to our target function */ Interceptor.attach(ptr(obfuscated_function), { onEnter: function(args){ /* Get the thread id */ thread = Process.getCurrentThreadId(); /* Make our callback to stop stalking the thread after 5s */ setTimeout(function(){ Stalker.unfollow(thread); log.flush(); log.close(); }, 5000 ); /* Detach our interceptor to avoid logging incorrect addresses */ Interceptor.revert(ptr(obfuscated_function)); Stalker.follow(thread, { events:{ call: true, ret: true }, onReceive: function(events){ var calls = Stalker.parse(events); for(var i in calls){ log.write('\t'.repeat(calls[i][3]) + ' ' + calls[i][1] + ':' + calls[i][2] + '\n'); } }, onCallSummary: function(summary){ log.write('\n\n'+'-'.repeat(30)+'\n\nSummary:\n\n'); Object.entries(summary).forEach(([key, value]) => { log.write('\t'+key+': '+str(value)); }); } }); } } ___________ *transform* ___________ The ``transform`` callback is an advanced callback that allows to inspect and modify the assembly code on the fly. It receives a ``iterator`` object that iterates over the instruction set. .. TODO: Add reference to the X86Writer documentation In fact, each time we get the ``next()`` item of the iterator it returns an ``Instruction`` object pointing to the address of the next instruction. To keep this example as simple as possible we'll be just using it to log every instruction executed: .. code-block:: JavaScript :linenos: var flag = false; var thread = null; /* Get the address space of the application * to avoid logging instructions from imported modules */ var app = Process.enumerateModulesSync()[0]; var appStart = app.base; var appEnd = app.base.add(app.size); /* Attach to our target function */ Interceptor.attach(ptr(obfuscated_function), { onEnter: function(args){ if(flag) return; /* Get the thread id */ thread = Process.getCurrentThreadId(); console.log('Got thread id') /* Make our callback to stop stalking the thread after 5s */ setTimeout(function(){ Stalker.unfollow(thread); }, 5000 ); console.log('Timeout set'); /* Detach our interceptor to avoid logging incorrect addresses */ Interceptor.revert(ptr(obfuscated_function)); Stalker.follow(thread, { transform: function(iterator){ var instruction = null; var isApp = false; while((instruction = iterator.next())!==null){ isApp = instruction.address.compare(instruction.address)>=0 && instruction.address.compare(appEnd) === -1; if(isApp) console.log(instruction.mnemonic + ' ' + instruction.opStr); /* If we don't call `iterator.keep()` it will drop the instruction */ iterator.keep(); } } }); console.log('Stalker following'); flag = true; } }); Which will return an output like this .. code-block:: JavaScript :linenos: Timeout set Stalker following mov rbx, rax cmp rbx, -1 jne 0x559941a6065a cmp ebp, 2 jne 0x559941a60740 test rbx, rbx jle 0x559941a606cc cmp ebp, 2 je 0x559941a60670 test ebp, ebp jle 0x559941a606a8 mov rdx, qword ptr [r12] sub rdx, rbx test rdx, rdx jle 0x559941a606c8 mov qword ptr [r12], rdx mov rax, rbx pop rbx pop rbp pop r12 pop r13 pop r14 ret .. TODO: Add references to the ``Code Writing`` part of the guide in the ``Advanced topics`` section *Note: To view some examples on how to overwrite instructions and other capabilities take a look at ``Instruction`` and ``Code Writers``* .. _gumevent.h link: https://github.com/frida/frida-gum/blob/master/gum/gumevent.h