enumerateThreads only returning the current thread?
d-millar opened this issue · 6 comments
Hi, am trying to enumerate the threads in the current process (which for my test target /usr/bin/xclock should be 6 threads, including the frida-agent thread). Depending on when I do this, I get a different thread id but only one, which I am assuming is the current thread. Initially, I was doing this by executing Process.enumerateThreads from a script. I tried explicitly calling gum_process_enumerate_threads, but it seems I am getting a null pointer write on line 506 of gumprocess-linux.c. This appears to be because I am processing a non-current thread (which I was happy to see) but am unable to allocate the "tls" variable via gum_alloc_n_pages.
Any suggestions as to what I might be doing wrong, either regarding why, generally, I can't enumerate non-current threads or why, specifically, I am failing the allocation?
Hey,
Frida's agent notifies Gum, through the Gum.Cloak API, about its threads (and other resources) so that APIs such as Gum.Process.enumerate_threads()
can skip those threads. This is so that in-process introspection can be done without the injected agent seeing its own threads, memory ranges, etc. So if you attach to a process with only one thread, you should only see that thread, and not Frida's.
As for Gum's C API, perhaps you forgot to initialize the library?
As noted above, xclock on Ubuntu 20 has five threads plus the agent thread. I can verify this with gdb. Process.enumerateThreads never returns more than one thread on my machine, regardless of the target and the its actual state. The thread returned is always the thread I've injected into.
With regard to the Gum C API, my code looks something like:
var m = Module.load('libfrida-core.so');
var x = m.findExportByName('gum_process_enumerate_threads');
const fn = new NativeFunction(x, 'void', ['pointer','pointer']);
var cb = new NativeCallback( (details, userdata) => {return true;}, 'bool', ['pointer', 'pointer']);
try {
fn(cb, NULL);
} catch (e) { console.log(e.type); }
m, x, and cb are all non-null.
Have also tried compiling the callback in C and loading a wrapper function that calls gum_process_enumerate_threads. Same results in either case. Is there some other piece of initialization I need to perform?
OK, so looking into this in a little more detail, it appears every thread is added to cloaked threads in gum_cloak_add_thread. The first thread is added in the final clause at line 127. Every subsequent thread is added in the first clause at line 121. For reasons I don't quite understand yet, gum_cloak_index_of_thread returns -1 for that first element, so it's data is returned by gum_process_enumerate_threads. Every other thread's details are ignored because gum_emit_thread_if_not_cloaked short-circuits at line 119 (which presumably is the desired behavior).
Ah, the first thread is added with id NULL, so it passes the check in gum_cloak_index_of_thread.
I could work on a fix for this if you’re interested, but I must admit I don’t really understand how gum_cloak_add_thread is supposed to work. What is being used to identify the agent thread?
@d-millar That would be great! The way it works is that frida-agent/frida-gadget both use the ThreadIgnoreScope
helper class which does this. Also, since GLib is our "standard library" that implements cross-platform threading primitives, we have patched it to support specifying a set of callbacks to be notified of threading events. We make user of this in gum_init_embedded()
here so we can automatically cloak threads spawned by Frida.