Skip to content

Instantly share code, notes, and snippets.

@oleavr
Last active January 23, 2026 22:52
Show Gist options
  • Select an option

  • Save oleavr/0788840e8f33bd1888a4450540cfbc58 to your computer and use it in GitHub Desktop.

Select an option

Save oleavr/0788840e8f33bd1888a4450540cfbc58 to your computer and use it in GitHub Desktop.
How to debug zymbiote.c

Steps

  1. Apply patch.
  2. Build the helper blob, e.g.: make -C src/linux/helpers FRIDA_HOST=android-x86_64
diff --git a/src/linux/helpers/zymbiote.c b/src/linux/helpers/zymbiote.c
index bffe8bde..8f3d55fc 100644
--- a/src/linux/helpers/zymbiote.c
+++ b/src/linux/helpers/zymbiote.c
@@ -2,6 +2,7 @@
#include <jni.h>
#include <signal.h>
#include <stdbool.h>
+#include <android/log.h>
#include <sys/socket.h>
#include <sys/un.h>
@@ -23,6 +24,7 @@ struct _FridaApi
ssize_t (* recv) (int sockfd, void * buf, size_t len, int flags);
int (* close) (int fd);
int (* raise) (int sig);
+ int (* __android_log_print) (int prio, const char * tag, const char * fmt, ...);
};
static volatile const FridaApi frida =
@@ -50,13 +52,18 @@ frida_zymbiote_replacement_set_argv0 (JNIEnv * env, jobject clazz, jstring name)
socklen_t addrlen;
unsigned int name_len;
+ frida.__android_log_print (ANDROID_LOG_INFO, "Frida", "Zymbiote speaking");
+
*frida.art_method_slot = frida.original_set_argv0;
frida.original_set_argv0 (env, clazz, name);
fd = frida.socket (AF_UNIX, SOCK_STREAM, 0);
if (fd == -1)
+ {
+ frida.__android_log_print (ANDROID_LOG_INFO, "Frida", "socket() failed");
goto beach;
+ }
addr.sun_family = AF_UNIX;
addr.sun_path[0] = '\0';
@@ -77,7 +84,10 @@ frida_zymbiote_replacement_set_argv0 (JNIEnv * env, jobject clazz, jstring name)
addrlen = (socklen_t) (offsetof (struct sockaddr_un, sun_path) + 1u + name_len);
if (frida_connect (fd, (const struct sockaddr *) &addr, addrlen) == -1)
+ {
+ frida.__android_log_print (ANDROID_LOG_INFO, "Frida", "connect() failed");
goto beach;
+ }
{
const char * name_utf8;
@@ -104,8 +114,14 @@ frida_zymbiote_replacement_set_argv0 (JNIEnv * env, jobject clazz, jstring name)
iov[1].iov_base = (void *) name_utf8;
iov[1].iov_len = header.package_name_len;
+ frida.__android_log_print (ANDROID_LOG_INFO, "Frida", "Sending hello with pid=%u ppid=%u package_name=\"%s\"",
+ header.pid, header.ppid, name_utf8);
+
if (!frida_sendmsg_all (fd, iov, 2, MSG_NOSIGNAL))
+ {
+ frida.__android_log_print (ANDROID_LOG_INFO, "Frida", "sendmsg() failed");
goto beach;
+ }
(*env)->ReleaseStringUTFChars (env, name, name_utf8);
}
@@ -114,12 +130,17 @@ frida_zymbiote_replacement_set_argv0 (JNIEnv * env, jobject clazz, jstring name)
uint8_t rx;
if (frida_recv (fd, &rx, 1, 0) != 1)
+ {
+ frida.__android_log_print (ANDROID_LOG_INFO, "Frida", "recv() failed");
goto beach;
+ }
}
success = true;
beach:
+ frida.__android_log_print (ANDROID_LOG_INFO, "Frida", "beach reached => fd=%d success=%s", fd, success ? "true" : "false");
+
if (fd != -1)
frida.close (fd);
diff --git a/src/linux/linux-host-session.vala b/src/linux/linux-host-session.vala
index db4c078c..9bfff34f 100644
--- a/src/linux/linux-host-session.vala
+++ b/src/linux/linux-host-session.vala
@@ -981,6 +981,12 @@ namespace Frida {
if (runtime_entry == null)
throw new Error.NOT_SUPPORTED ("Unable to detect libandroid_runtime.so entry");
+ var liblog_entry = ProcMapsSoEntry.find_by_path (pid, "/system/lib64/liblog.so");
+ if (liblog_entry == null)
+ liblog_entry = ProcMapsSoEntry.find_by_path (pid, "/system/lib/liblog.so");
+ if (liblog_entry == null)
+ throw new Error.NOT_SUPPORTED ("Unable to detect liblog.so entry");
+
Gum.ElfModule libc;
try {
libc = new Gum.ElfModule.from_file (libc_path);
@@ -995,6 +1001,13 @@ namespace Frida {
throw new Error.NOT_SUPPORTED ("Unable to parse libandroid_runtime.so: %s", e.message);
}
+ Gum.ElfModule liblog;
+ try {
+ liblog = new Gum.ElfModule.from_file (liblog_entry.path);
+ } catch (Gum.Error e) {
+ throw new Error.NOT_SUPPORTED ("Unable to parse liblog.so: %s", e.message);
+ }
+
uint64 set_argv0_address = 0;
runtime.enumerate_exports (e => {
if (e.name == "_Z27android_os_Process_setArgV0P7_JNIEnvP8_jobjectP8_jstring") {
@@ -1097,6 +1110,15 @@ namespace Frida {
cursor += pointer_size;
}
+ liblog.enumerate_exports (e => {
+ if (e.name == "__android_log_print") {
+ payload.write_pointer (cursor, liblog_entry.base_address + e.address);
+ cursor += pointer_size;
+ return false;
+ }
+ return true;
+ });
+
return new ZymbiotePrepResult () {
process_memory = fd,
already_patched = already_patched,
@@ -1140,7 +1162,10 @@ namespace Frida {
private async void handle_zymbiote_connection (ZymbioteConnection connection) {
try {
+ printerr ("[handle_zymbiote_connection %p] >>>\n", connection);
var hello = yield connection.read_hello (io_cancellable);
+ printerr ("[handle_zymbiote_connection %p]\tGot hello with pid=%u ppid=%u package_name=\"%s\"\n",
+ connection, hello.pid, hello.ppid, hello.package_name);
connection.patches_to_revert = zymbiote_patches[hello.ppid];
@@ -1148,20 +1173,26 @@ namespace Frida {
Promise<uint> spawn_request;
if (spawn_requests.unset (hello.package_name, out spawn_request)) {
+ printerr ("[handle_zymbiote_connection %p]\t Matches spawn request\n", connection);
spawn_request.resolve (hello.pid);
needs_resume = true;
} else if (spawn_gating_enabled) {
+ printerr ("[handle_zymbiote_connection %p]\t Delegating to spawn gating\n", connection);
var spawn_info = HostSpawnInfo (hello.pid, hello.package_name);
pending_spawn[hello.pid] = spawn_info;
spawn_added (spawn_info);
needs_resume = true;
+ } else {
+ printerr ("[handle_zymbiote_connection %p]\t Ignoring this one (resuming right away)\n", connection);
}
if (needs_resume)
zymbiote_connections[hello.pid] = connection;
else
connection.resume.begin (io_cancellable);
+ printerr ("[handle_zymbiote_connection %p] <<<\n", connection);
} catch (GLib.Error e) {
+ printerr ("[handle_zymbiote_connection %p] <<< Oops: %s\n", connection, e.message);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment