frida / frida-python

Frida Python bindings
Other
787 stars 148 forks source link

Stalker errors #32

Closed v-p-b closed 7 years ago

v-p-b commented 9 years ago

Following up on #14 with the master branch.

Test code:

import frida
import sys
import os
import random

stalker="""
  [%s].forEach(function(tid){
        send(tid);
         Stalker.follow(tid, {
              events: {
                call: false, // CALL instructions: yes please
                ret: true, // RET instructions: no thanks
                exec: true // all instructions: no thanks
                },
              onReceive: function onReceive(events) {
                            //send("receive",events);
              },
              onCallSummary: function onCallSummary(summary) {
                            send(summary);
             }
         }

        ); 
 });
"""

pid=frida.spawn(["/bin/ls","."])
process = frida.attach(pid)

def on_message(message, data):
  print repr(message)
  print repr(data)

# Workaround Frida bug...

tasks0=os.listdir("/proc/%d/task"%pid)

tasks=[]
for t in tasks0:
  print t
  if "tracing stop" in open("/proc/%d/task/%s/status" % (pid,t)).read():
    print "Tracing stop!"
    continue
  tasks.append(t)

print "This is how the final script should look like:"
print stalker % (','.join(tasks))

script = process.create_script(stalker % (','.join(tasks)))  
script.on('message', on_message)
script.load()
frida.resume(pid)
sys.stdin.read() # Don't let the parent exit

Output:

29849
Tracing stop!
29850
29852
29853
29854
29855
This is how the final script should look like:

  [29850,29852,29853,29854,29855].forEach(function(tid){
        send(tid);
         Stalker.follow(tid, {
              events: {
                call: false, // CALL instructions: yes please
                ret: true, // RET instructions: no thanks
                exec: true // all instructions: no thanks
                },
              onReceive: function onReceive(events) {
                            //send("receive",events);
              },
              onCallSummary: function onCallSummary(summary) {
                            send(summary);
             }
         }

        ); 
 });

{u'type': u'send', u'payload': 29850}
None
{u'type': u'send', u'payload': 29852}
None
Traceback (most recent call last):
  File "test.py", line 51, in <module>
    script.load()
frida.TransportError: timeout was reached

Tracing only a single thread:

$ python test.py 
29931
Tracing stop!
29932
29934
29935
29936
29937
This is how the final script should look like:

  [29932].forEach(function(tid){
        send(tid);
         Stalker.follow(tid, {
              events: {
                call: false, // CALL instructions: yes please
                ret: true, // RET instructions: no thanks
                exec: true // all instructions: no thanks
                },
              onReceive: function onReceive(events) {
                            //send("receive",events);
              },
              onCallSummary: function onCallSummary(summary) {
                            send(summary);
             }
         }

        ); 
 });

{u'type': u'send', u'payload': 29932}
None

(process:29922): GLib-GIO-WARNING **: _g_dbus_worker_do_read_cb: error determining bytes needed: Unable to determine message blob length - given blob is malformed
oleavr commented 9 years ago

Hmm, I'm afraid this isn't a supported use-case. Enumerating threads externally means you'll also end up tracing Frida's own threads, which is largely uncharted territory (never tested that as it's not really a meaningful thing to do). Process.enumerateThreads() does however omit Frida's own threads, so I would recommend using that.

v-p-b commented 9 years ago

Thanks, I knew I implemented that workaround for a reason, now I know what it was! Will try with the supported enumeration method and update the ticket!

oleavr commented 9 years ago

Great, thanks! If there's an issue with the enumeration it's a really good time to get that fixed (4.0.0 is due very soon). :)

v-p-b commented 9 years ago

So here's my new code:

stalker="""
            send("Loading");
            Process.enumerateThreads({
                onMatch:
                    function onMatch(thread){
                        send(thread.id); // Thread IDs are reported correctly
                        Stalker.follow(thread.id, {
                            events: {
                                call: false, 
                                ret: true, 
                                exec: true 
                            },
                            onReceive: function onReceive(events) {
                                       send("receive",events); 
                            },
                            onCallSummary: function onCallSummary(summary) {
                                        send(summary); 
                            }
                        }

                        );
                    },
        onComplete:function onComplete(){}});
"""

pid=int(sys.argv[1])
process = frida.attach(pid)

def on_message(message, data):
  print repr(message)
  print repr(data)

script = process.create_script(stalker)  
script.on('message', on_message)
script.load()
print "Loaded"
sys.stdin.read() # Don't let the parent exit

The following exception is raised after the script properly reports a number of enumerated threads (so thread enumeration seems to work correctly):

Traceback (most recent call last):
  File "test.py", line 41, in <module>
    script.load()
frida.TransportError: timeout was reached

I once managed to get a single message from onReceive(), but nothing else in the following tests.

Python 2.7.3, Linux x64.

oleavr commented 9 years ago

Very interesting. Could you try if this works better:

"use strict";

function initialize() {
    const threads = [];
    Process.enumerateThreads({
        onMatch: function onMatch(thread) {
            threads.push(thread);
        },
        onComplete: function () {
            threads.forEach(function (thread) {
                Stalker.follow(thread.id, {
                    events: {
                        call: true, 
                        ret: false, 
                        exec: false 
                    },
                    onCallSummary: function (summary) {
                        send(summary); 
                    }
                });
            });
        }
    });
}

setTimeout(initialize, 0);

(Untested, but should work.)

By the way, you should only define onReceive or onCallSummary, not both (the latter gives you periodic summaries and is cheaper).

v-p-b commented 9 years ago

It seems onComplete() never runs in your code. If I move the Stalker code to onMatch() (that runs numerous times) I get no messages even if I lower queueCapacity to 512. Testing with Firefox.

oleavr commented 7 years ago

This should be improved in the latest Frida 9.x. Please file any issues in frida-gum. By the way, work is underway to port Stalker to arm64 🎉

Be-P commented 5 years ago

I'm using Windows 10, checking frida version i get

 C:\Python27\Scripts\frida.exe --version
12.2.30

I'm trying to run a simple base.exe program compiled with gcc base.c -o base.exe that contains a simple loop:

#include <stdio.h>
#include <unistd.h>

int main(){
    printf("[+] Starting.\n");

    while(1){
        printf("    [+]Still Running..\n");
        sleep(1);
    }
}

The python script contains the following code

import frida
import sys
session = frida.attach("base.exe")
with open("explore.js","r") as f:
    code = f.read()
script = session.create_script(code)
script.load()
sys.stdin.read()

and the explore.js file contains, as you suggested, the following code:

"use strict";

function initialize() {
    const threads = [];
    Process.enumerateThreads({
        onMatch: function onMatch(thread) {
            threads.push(thread);
        },
        onComplete: function () {
            threads.forEach(function (thread) {
                Stalker.follow(thread.id, {
                    events: {
                        call: true, 
                        ret: false, 
                        exec: false 
                    },
                    onCallSummary: function (summary) {
                        send(summary); 
                    }
                });
            });
        }
    });
}

setTimeout(initialize, 0);

The process of base.exe terminates silently and the python script doesn't display any output/error messages.