/* * extentionloader.c * * Created on: Aug 19, 2013 * Author: eric * * Used for loading an module that will extend the ceserver * client<-->ceserver<-->extention * * It doesn't HAVE to be used if the forced module load method works (Do not assume so) * * How it works: * Ptrace the target (this means it must be loaded BEFORE the debugger is attached) * Cause a stop and make sure it's runnable (Not sure if it executes if it's suspended for some reason. e.g: wait for event/critical section that may never happen) * Change the current instruction pointer to the beginning of dlopen and the register/stack state setup to execute * Set the return addres to an invalid return address (e.g 0x0ce0) * Execute it and wait till a sigtrap/sigseg happens on that specific invalid address * Then restore the state back * * On arm: Bit J and T in CPSR define the current execution state * J T * 0 0 = ARM * 0 1 = Thumb * 1 0 = Jazelle (java...) * 1 1 = ThumbEE* * * If ARM so set to 0 0 and restore that as well * Note that The least significant bit in an address specifier also determines if it's THUMB or ARM * It doesn't seem to matter if you set the least significant bit in the PC register. It will ignore that but on execute. (probably a good idea to clear that bit anyhow) * * * Problem: It doesn't return properly when the registers are changed when it's waiting in a syscall, so only change it when outside of a syscall * Better solution: It seems it failed because the stop was at a syscall, so the program counter was decremented tithe the size of the syscall * To prevent this RESTART change EAX to 0 so it won't do the restart. Also works on ARM * * Problem2: In android dlopen is in /system/bin/linker but not using a symbol (so ce's symbollist can't be used to find the address) * * dlopen("libdl.so", RTLD_NOW) actually works in android and dlsym as well. (point to the linker version) * This is useful since this makes it cross compatible with normal linux. * for some reason getting the address of dlopen in x86 returns a local stub and I don't know yet how to prevent those stubs * * so, to find dlopen find address range dlopen is in in this process (/proc/selfpid/maps), get the base address of that specific module * and then add that offset to the same named module in the target process * */ #include #include #include #include #include #include #ifdef HAS_LINUX_USER_H #include #else #include #endif #include #include #include #include #include #include #ifdef __aarch64__ #include #endif #include "porthelp.h" #include "api.h" #include "ceserver.h" #ifndef SUN_LEN //missing in android (copy from linux sys/un.h) /* Evaluate to actual length of the `sockaddr_un' structure. */ # define SUN_LEN(ptr) ((size_t) (((struct sockaddr_un *) 0)->sun_path) \ + strlen ((ptr)->sun_path)) #endif int WaitForPid() { int status; int pid=-1; while (pid==-1) { pid=waitpid(-1, &status, __WALL); if ((pid==-1) && (errno!=EINTR)) { printf("LoadExtension wait fail. :%d\n", errno); return -1; //something bad happened } } return pid; } int showRegisters(int pid) { #ifdef __aarch64__ struct user_pt_regs regs; #else #ifdef __arm__ struct pt_regs r; #else struct user_regs_struct r; #endif #endif /* int result=ptrace(PTRACE_GETREGS, pid, 0, &r); if (result!=0) { printf("PTRACE_GETREGS FAILED (%d)\n", result); return result; } #ifdef __arm__ printf("r0=%lx\n", r.ARM_r0); printf("orig_r0=%lx\n", r.ARM_ORIG_r0); printf("pc=%lx\n", r.ARM_pc); #else #if defined(__x86_64__) printf("RAX=%lx\n", r.rax); printf("orig_rax=%lx\n", r.orig_rax); printf("rip=%lx\n", r.rip); #endif #if defined(__i386__) printf("EAX=%lx\n", r.eax); printf("orig_eax=%lx\n", r.orig_eax); printf("eip=%lx\n", r.eip); #endif #endif */ } uintptr_t finddlopen(int pid) { void *libdl; void *realdlopen; libdl=dlopen("libdl.so", RTLD_NOW); printf("libdl=%p\n", libdl); realdlopen=dlsym(libdl,"dlopen"); printf("dlopen=%p\n", dlopen); printf("realdlopen=%p\n", realdlopen); #ifndef __arm__ if (dlopen==realdlopen) printf("Please tell db what you did to get this to function (excluding manually editing this if statement)\n"); #endif //open /proc/self/maps and look up the region that holds realdlopen FILE *maps=fopen("/proc/self/maps", "r"); char x[200]; char currentmodule[256]; char modulepath[256]; unsigned long long currentmodulestart; currentmodule[0]=0; while (fgets(x, 200, maps)) { unsigned long long start; unsigned long long stop; printf("%s", x); sscanf(x, "%llx-%llx %*s %*s %*s %*s %s\n", &start, &stop, modulepath); if (strcmp(modulepath, currentmodule)!=0) { strcpy(currentmodule, modulepath); currentmodulestart=start; } if ( (((uintptr_t)realdlopen)>=start) && (((uintptr_t)realdlopen)isDebugged) { printf("this process id being debugged\n"); //make sure this is executed by the debugger thread if (p->debuggerThreadID!=pthread_self()) { printf("Not the debugger thread. Switching...\n"); //tell the debugger thread to do this int result=0; #pragma pack(1) struct { uint8_t command; uint32_t pHandle; } lx; #pragma pack() lx.command=CMD_LOADEXTENSION; lx.pHandle=hProcess; if (pthread_mutex_lock(&debugsocketmutex) == 0) { sendall(p->debuggerClient, &lx, sizeof(lx), 0); WakeDebuggerThread(); recvall(p->debuggerClient, &result, sizeof(result), MSG_WAITALL); printf("Returned from debugger thread. Result:%d\n", result); pthread_mutex_unlock(&debugsocketmutex); } return result; } else printf("This is the debugger thread\n"); } if (p->hasLoadedExtension==0) { char modulepath[256], modulepath2[256]; int l; memset(modulepath, 0, 256); memset(modulepath2, 0, 256); char *mp; l=readlink("/proc/self/exe", modulepath2, 256); if (l!=-1) { modulepath2[l]=0; printf("modulepath2=%s\n", modulepath2); sscanf(modulepath2,"%s", modulepath); //sometimes it has a (deleted) text after it printf("modulepath=%s\n", modulepath); mp=dirname(modulepath); printf("after dirname: %s\n", mp); strcpy(modulepath, mp); strcat(modulepath, "/libceserver-extension"); #ifdef __i386__ strcat(modulepath, "_x86"); #endif #ifdef __aarch64__ strcat(modulepath, "_arm64"); #endif #ifdef __arm__ strcat(modulepath, "_arm"); #endif strcat(modulepath,".so"); } else { strcpy(modulepath, "libceserver-extension"); #ifdef __i386__ strcat(modulepath, "_x86"); #endif #ifdef __x86_64__ strcat(modulepath, "_x86_64"); #endif #ifdef __aarch64__ strcat(modulepath, "_arm64"); #endif #ifdef __arm__ strcat(modulepath, "_arm"); #endif strcat(modulepath,".so"); } printf("modulepath = %s\n", modulepath); if (p->isDebugged) { printf("This process is being debugged. Checking if it's already loaded\n"); pthread_mutex_lock(&p->extensionMutex); p->hasLoadedExtension=openExtension(p->pid, &p->extensionFD); pthread_mutex_unlock(&p->extensionMutex); } // else if (p->hasLoadedExtension) printf("The extension is already loaded\n"); { pthread_mutex_lock(&p->extensionMutex); if (p->hasLoadedExtension==0) //still 0 { if (p->neverForceLoadExtension==0) { printf("Calling loadExtension\n"); p->hasLoadedExtension=loadExtension(p->pid, modulepath, p->isDebugged); printf("p->hasLoadedExtension=%d\n", p->hasLoadedExtension); } if (p->hasLoadedExtension) p->hasLoadedExtension=openExtension(p->pid, &p->extensionFD); } pthread_mutex_unlock(&p->extensionMutex); } } else printf("Already loaded\n"); return p->hasLoadedExtension; } else { printf("Invalid handle type"); return 0; } }