/* * bindump v0.1 - simple program to iterate over binder debug data * and display who uses what * * From: "Android Internals - A Confectioner's Cookbook" * * http://www.newandroidbook.com/ * * By Jonathan Levin, 08/2014 * * Loosely based on "service" from AOSP. Yours to use and abuse * * Not production worthy code. Quick and dirty, to illustrate a point. C'est tout. * */ #include <binder/Parcel.h> #include <binder/ProcessState.h> #include <binder/IServiceManager.h> #include <binder/TextOutput.h> #include <fcntl.h> #include <dirent.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/time.h> using namespace android; #define DETAILS_MASK_OWNER 0x1 #define DETAILS_MASK_USERS 0x2 void writeString16(Parcel& parcel, const char* string) { if (string != NULL) { parcel.writeString16(String16(string)); } else { parcel.writeInt32(-1); } } // get the name of the generic interface we hold a reference to static String16 get_interface_name(sp<IBinder> service) { if (service != NULL) { Parcel data, reply; status_t err = service->transact(IBinder::INTERFACE_TRANSACTION, data, &reply); if (err == NO_ERROR) { return reply.readString16(); } } return String16(); } static String8 good_old_string(const String16& src) { String8 name8; char ch8[2]; ch8[1] = 0; for (unsigned j = 0; j < src.size(); j++) { char16_t ch = src[j]; if (ch < 128) ch8[0] = (char)ch; name8.append(ch8); } return name8; } int g_verbose = 0; sp<IServiceManager> sm ; int getNodeForService(const char *ServiceName) { sp<IBinder> service = sm->checkService(String16(ServiceName)); char binderDebugData[1024]; int rc = 0 ; if (service != NULL) { // We have the service - sleep here for a sec String16 ifName = get_interface_name(service); aout <<ifName ; char name[200]; sprintf (name,"/sys/kernel/debug/binder/proc/%d", getpid()); int fd = open (name, O_RDONLY); if (fd < 0 ) { perror ("Can't open Binder debug data\n"); exit(1); } rc = read (fd, binderDebugData, 1024); if (rc > 0) { // look for node if (g_verbose) fprintf (stderr,"%s\n", binderDebugData); // First node entry is the context mgr char *node = strstr (binderDebugData, "node"); if (!node) { fprintf (stderr, "Didn't find any node refs in debug data. This is odd. Maybe format changed!\n"); exit(3); } // Otherwise, we need the *next* node ref node = strstr (node + 1, "node"); if (!node) { fprintf (stderr, "Didn't find second node refs in debug data. This is odd. Maybe format changed!\n"); } int nodeNum= 0; rc = sscanf (node + 4, "%d", &nodeNum); if (rc != 1) { fprintf(stderr,"Can't figure out the node ref\n"); exit(6); } printf ("Service: %s node ref: %d\n", ServiceName, nodeNum); return (nodeNum); } } else printf ("Service '%s' not found - use service list to get list of available services\n", ServiceName); } char *getProcName (char *pid) { // Attempt to open /proc/pid/cmdline static char pathName[1024]; sprintf (pathName, "/proc/%s/cmdline", pid); int fd = open (pathName, O_RDONLY); if (fd < 0 ) return ((char *) "??"); read (fd, pathName, 1024); close(fd); // cmdline is argv[] and null terminated. that's a bonus. return (pathName); } void findUsersOfNode (int nodeNum, int DetailsMask) { // Iterate over all nodes in /sys/kernel/debug/binder // and find users of this node DIR *dirp = opendir("/sys/kernel/debug/binder/proc"); struct dirent *dirent; char nodeInFile[80]; sprintf (nodeInFile, "node %d", nodeNum); while (dirent = readdir(dirp)) { char path[10240]; memset(path,'\0',10240); sprintf(path,"/sys/kernel/debug/binder/proc/%s", dirent->d_name); int fd = open (path, O_RDONLY); if (fd < 0) { /* this could happen if a process terminates */ /* by the time we get to open it. So no biggy */ continue; } int rc = read (fd, path, 10240); // reuse path, what the heck :-) if (rc < 0) { /* again, this could be normal.. */ continue; } // For users, we look for a line of the form // ref 4: desc 0 node %d s 1 w 1 d ... // For owners, we look for a line of the form // node %d: u002d70b0 c002d7704 ... blah blah... proc pid1 pid2 ... char *p = path; while (p = strstr (p, nodeInFile)) { switch (p[strlen(nodeInFile)]) { case ' ': // User if (atoi(dirent->d_name) != getpid()) { if ( (DetailsMask & DETAILS_MASK_USERS)) printf ("User: PID %5s\t%s\n", dirent->d_name,getProcName(dirent->d_name)); } break; case ':': // Owner if ( (DetailsMask & DETAILS_MASK_OWNER)) printf ("Owner: PID %5s\t%s\n", dirent->d_name, getProcName(dirent->d_name)); break; default: // False Positive (part of longer num, probably) ;; } // end switch p++; } // end while close (fd); }; } #define MAX_BINDER_DATA_SIZE (1024 *1024) void enumerateNodes (int pid) { char name[80]; char buf[MAX_BINDER_DATA_SIZE]; // Do we even have a PID? sprintf (name, "/proc/%d", pid); if (access (name, X_OK) != 0) { fprintf (stderr, "PID %d - no such process or thread\n", pid); return; } sprintf (name,"/sys/kernel/debug/binder/proc/%d", pid); int fd = open (name, O_RDONLY); if (fd < 0) { sprintf (name, "%d", pid); fprintf (stderr, "PID %d (%s) is not using Binder\n", pid,getProcName(name)); return; } int rc = read (fd, buf, MAX_BINDER_DATA_SIZE); if (rc < 0) { fprintf (stderr, "Unable to read Binder debug data for pid %d\n", pid); return; } // Otherwise we have the data... return ; char *firstRef = strstr(buf, "ref "); char *ref = firstRef; char *eol = NULL; while (ref) { eol = strchr (ref, '\n'); if (!eol) { fprintf (stderr,"Binder data for PID %d malformed!\n", pid); return;} *eol = '\0'; // printf ("Got Ref %s\n", ref); ref = strstr (eol + 1, "ref "); } } // enumerateNodes int main(int argc, char* const argv[]) { sm = defaultServiceManager(); int detailsMask = 0xFF; if (argc < 2) { fprintf (stderr, "Usage: %s [owner|users] _servicename_\n", argv[0]); fprintf (stderr, "Can also use \"all\" for all services\n"); exit(11); } if (argc == 3) { if (strcmp(argv[1], "owner") == 0) detailsMask = DETAILS_MASK_OWNER; if (strcmp(argv[1], "users") == 0) detailsMask = DETAILS_MASK_USERS; } if (strcmp(argv[argc-1], "all") == 0) { int s = 0; // First enumerate services, then for each Vector<String16> services = sm->listServices(); for (s = 0; s < services.size(); s++) { int nodeNum = getNodeForService (String8(services[s])); findUsersOfNode(nodeNum,detailsMask); } return (0); } // Is it a PID? int pid = atoi(argv[argc - 1 ]); if (pid) /* != 0 */ { enumerateNodes(pid); return (0); }; // Otherwise, it's a service name.. int nodeNum = getNodeForService (argv[argc-1]); findUsersOfNode(nodeNum,detailsMask); return (0); }