/* Binary Instrumentation Tool/Framework for Android * Collin Mulliner * http://www.mulliner.org/android/ * * (c) 2012 * * License: GPL v2 * */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libt.h" #include "uthash.h" // contains macro of my_init() // autogenerated #include "hijacks.h" char tag[256]; struct mm { char name[256]; unsigned long start, end; }; int android_version_ID = ANDROID_V_INVALID; unsigned int mname_offset=0x35; unsigned int mname_this_offset=0; unsigned int mClient_thisP_offset=0x14; unsigned int mPid_mClient_offset=0x10; unsigned int mStramType_this_offset=0; unsigned int mState_offset=0x2c; unsigned int sample_rate_offset=0x30; unsigned int mCblk_this_offset = 0x1c; static int log_baseaddress = 0; // communication socket, a pty :) int coms; void __attribute__ ((constructor)) my_init(void); void my_cacheflush(unsigned int begin, unsigned int end) { const int syscall = 0xf0002; __asm __volatile ( "mov r0, %0\n" "mov r1, %1\n" "mov r7, %2\n" //"mov r2, #0x0\n" "mov r2, #0x0\n" "svc 0x00000000\n" : : "r" (begin), "r" (end), "r" (syscall) : "r0", "r1", "r7" ); } /* hook_t holds hook metadata hookf is the hook callback */ int help(struct hook_t *hook_hash, int pid, char *libname, char *funcname, void *hookf, int by_name, unsigned int raw_address) { unsigned long int addr = 0; int i; int find_name_result; struct hook_t *hook = (struct hook_t*) malloc(sizeof(struct hook_t)); #ifdef DEBUG_LIBT log("hooking - hash table 0x%x\n", help_hash1) #endif if( by_name ) { // 1 - fetch function address and put it into addr find_name_result = find_name(pid, funcname, libname, &addr); #ifdef DEBUG_LIBT log("find_name_result %d\n", find_name_result); #endif if ( find_name_result < 0) { #ifdef DEBUG_LIBT log("can't find: %s - error code %d\n", funcname, find_name_result) #endif return 0; } } else { #ifdef DEBUG_LIBT log("specified raw address: %x\n", raw_address) #endif funcname = "z";//"raw address"; /* // find lib where raw address is contained in */ struct mm mm[450]; struct mm *m; int nmm; int load_memmap_return, mprotect_return; char *slash; load_memmap_return = load_memmap(pid, mm, &nmm); if( load_memmap_return < 0 ) { #ifdef DEBUG_LIBT log("failed memmap\n") #endif exit(1); } #ifdef DEBUG_LIBT log("[*] searching vaddr for function %s - target relative address 0x%x\n", libname, raw_address) #endif for( i = 0, m = mm; i < nmm; i++, m++){ // awful code, just to print dbg infos slash = strstr(m->name, ".so"); if (!slash) continue; //log("[*] %s start: 0x%8x end: 0x%08x\n", m->name, m->start, m->end); if( strstr(m->name, libname) ) { addr = m->start + raw_address; if( !log_baseaddress) { #ifdef DEBUG_LIBT log("[*] Base address 0x%x - 0x%x\n", m->start, m->end) #endif log_baseaddress = 1; base_address = m->start; } #ifdef DEBUG_LIBT log("[*]\tFound address: 0x%8x\n", addr); #endif global_base_address = m->start; #ifdef DEBUG_LIBT log("[D] base address: %p\n", global_base_address); #endif mprotect_return = mprotect((void*)m->start, m->end - m->start, PROT_READ|PROT_WRITE|PROT_EXEC); if( mprotect_return == 0 ) { #ifdef DEBUG_LIBT log("[*]\t+rwx memory region ok\n") #endif } else { #ifdef DEBUG_LIBT log("[*]\t mprotect failed\n") #endif exit(1); } break; } } } if( addr == 0) { #ifdef DEBUG_LIBT log("[*] Could not find specified library %s\n", libname) #endif exit(1); } #ifdef DEBUG_LIBT log("[*] Hooking %s = %x hook = %x target:", funcname, addr, hookf) #endif strncpy(hook->name, funcname, sizeof(hook->name)-1); // 2 - hook either ARM or THUMB code if (addr % 4 == 0) { #ifdef DEBUG_LIBT log("ARM\n") #endif hook->thumb = 0; hook->patch = (unsigned int)hookf; hook->orig = addr; hook->jump[0] = 0xe59ff000; // LDR pc, [pc, #0] hook->jump[1] = hook->patch; hook->jump[2] = hook->patch; for (i = 0; i < 3; i++) hook->store[i] = ( (int*)hook->orig )[i]; for (i = 0; i < 3; i++) ((int*)hook->orig)[i] = hook->jump[i]; } else { hook->thumb = 1; #ifdef DEBUG_LIBT log("THUMB\n") #endif //h->jumpt[13] = 0xdf; //h->jumpt[12] = 0xfe; // bp hook->patch = (unsigned int) hookf; hook->orig = addr; /* patch without sub address in r12 */ /* h->jumpt[1] = 0x46; */ /* h->jumpt[0] = 0xac; */ /* h->jumpt[3] = 0xa5; */ /* h->jumpt[2] = 0x02; */ /* h->jumpt[5] = 0x68; */ /* h->jumpt[4] = 0x2d; */ /* h->jumpt[7] = 0xb4; */ /* h->jumpt[6] = 0x20; */ /* h->jumpt[9] = 0x46; */ /* h->jumpt[8] = 0x65; */ /* h->jumpt[11] = 0xbd; */ /* h->jumpt[10] = 0x00; */ /* patch with sub address + 0xd in r12 */ hook->jumpt[1] = 0x46; hook->jumpt[0] = 0xac; hook->jumpt[3] = 0xa5; hook->jumpt[2] = 0x03; hook->jumpt[5] = 0x68; hook->jumpt[4] = 0x2d; hook->jumpt[7] = 0xb4; hook->jumpt[6] = 0x20; hook->jumpt[9] = 0x46; hook->jumpt[8] = 0x65; hook->jumpt[11] = 0x46; hook->jumpt[10] = 0xfc; hook->jumpt[13] = 0xbd; hook->jumpt[12] = 0x00; hook->jumpt[15] = 0x46; // nop hook->jumpt[14] = 0xe4; memcpy(&hook->jumpt[12], (unsigned char*)&hook->patch, sizeof(unsigned int)); unsigned int orig = addr - 1; //char* cmd_disas_format = "arm-none-eabi-objdump -D --target binary -Mforce-thumb -marm %s"; //char cmd_disas[256]; unsigned char dump_buffer[20]; //remove("tmp_stolen.bin"); //FILE *stolen_bytes_dump = fopen("tmp_stolen.bin", "w"); // Save stolen bytes #ifdef DEBUG_LIBT log("[*] Saving stolen bytes\n\t") #endif for (i = 0; i < 20; i++) { //log("%d:", i) hook->storet[i] = ((unsigned char*)orig)[i]; dump_buffer[i] = ((unsigned char*)orig)[i]; #ifdef DEBUG_LIBT //log("%0.2x ", hook->storet[i]) #endif } #ifdef DEBUG_LIBT log("\n") #endif //fprintf(stolen_bytes_dump, dump_buffer); //fflush(stolen_bytes_dump); //fclose(stolen_bytes_dump); //remove("tmp_patch.bin"); //FILE *patch_bytes_dump = fopen("tmp_patch.bin", "w"); // write the patch to mem #ifdef DEBUG_LIBT log("[*] Patching target address\n\t") #endif for (i = 0; i < 20; i++) { ( (unsigned char*) orig)[i] = hook->jumpt[i]; dump_buffer[i] = hook->jumpt[i]; #ifdef DEBUG_LIBT // log("%0.2x ", ((unsigned char*)orig)[i]) #endif } //fprintf(patch_bytes_dump, dump_buffer); //fflush(patch_bytes_dump); //fclose(patch_bytes_dump); #ifdef DEBUG_LIBT log("\n") #endif /* char out_buffer[200]; */ /* snprintf(cmd_disas, 100, cmd_disas_format, "tmp_stolen.bin"); */ /* log("[*] Launching command %s\n", cmd_disas) */ /* FILE *objdump = popen(cmd_disas, "r"); */ /* while(fgets(out_buffer,200,objdump) != NULL) { */ /* log("asdf\n") */ /* log(out_buffer) */ /* } */ /* pclose(objdump); */ } #ifdef DEBUG_LIBT log("[*]Adding 0x%x to hash table 0x%x\n", hook->orig, hook_hash) #endif HASH_ADD(hh, help_hash1, orig, sizeof(int), hook); my_cacheflush((unsigned int)hook->orig, (unsigned int)hook->orig+20); #ifdef DEBUG_LIBT log("hooking finished\n") #endif return 1; } int help_no_hash(struct hook_t *hook, int pid, char *libname, char *funcname, void *hookf, int by_name, unsigned int raw_address) { unsigned long int addr = 0; int i; int find_name_result; if( by_name ) { // 1 - fetch function address and put it into addr find_name_result = find_name(pid, funcname, libname, &addr); #ifdef DEBUG_LIBT log("find_name_result %d\n", find_name_result); #endif if ( find_name_result < 0) { #ifdef DEBUG_LIBT log("can't find: %s - error code %d\n", funcname, find_name_result) #endif return 0; } } else { #ifdef DEBUG_LIBT log("[*] specified raw address: %x\n", raw_address) #endif funcname = "zz"; //"raw address"; /* // find lib where raw address is contained in */ struct mm mm[450]; struct mm *m; int nmm; int load_memmap_return, mprotect_return; char *slash; load_memmap_return = load_memmap(pid, mm, &nmm); if( load_memmap_return < 0 ) { #ifdef DEBUG_LIBT log("failed memmap\n") #endif exit(1); } #ifdef DEBUG_LIBT log("[*] searching vaddr for function %s - target relative address 0x%x\n", libname, raw_address) #endif for( i = 0, m = mm; i < nmm; i++, m++){ // awful code, just to print dbg infos slash = strstr(m->name, ".so"); if (!slash) continue; //log("[*] %s start: 0x%8x end: 0x%08x\n", m->name, m->start, m->end); if( strstr(m->name, libname) ) { addr = m->start + raw_address; if( !log_baseaddress) { #ifdef DEBUG_LIBT log("[*] Base address 0x%x - 0x%x\n", m->start, m->end) #endif log_baseaddress = 1; base_address = m->start; } #ifdef DEBUG_LIBT log("[*]\tFound address: 0x%8x\n", addr); #endif global_base_address = m->start; #ifdef DEBUG_LIBT log("[D] base address: %p\n", global_base_address); #endif mprotect_return = mprotect((void*)m->start, m->end - m->start, PROT_READ|PROT_WRITE|PROT_EXEC); if( mprotect_return == 0 ) { #ifdef DEBUG_LIBT log("[*]\t+rwx memory region ok\n") #endif } else { #ifdef DEBUG_LIBT log("[*]\t mprotect failed\n") #endif exit(1); } break; } } } if( addr == 0) { #ifdef DEBUG_LIBT log("[*] Could not find specified library %s\n", libname) #endif exit(1); } #ifdef DEBUG_LIBT log("[*] Hooking %s = %x hook = %x target:", funcname, addr, hookf) #endif strncpy(hook->name, funcname, sizeof(hook->name)-1); // 2 - hook either ARM or THUMB code if (addr % 4 == 0) { #ifdef DEBUG_LIBT log("ARM\n") #endif hook->thumb = 0; hook->patch = (unsigned int)hookf; hook->orig = addr; hook->jump[0] = 0xe59ff000; // LDR pc, [pc, #0] hook->jump[1] = hook->patch; hook->jump[2] = hook->patch; for (i = 0; i < 3; i++) hook->store[i] = ( (int*)hook->orig )[i]; for (i = 0; i < 3; i++) ((int*)hook->orig)[i] = hook->jump[i]; } else { hook->thumb = 1; #ifdef DEBUG_LIBT log("THUMB\n") #endif //h->jumpt[13] = 0xdf; //h->jumpt[12] = 0xfe; // bp hook->patch = (unsigned int) hookf; hook->orig = addr; hook->jumpt[1] = 0x46; hook->jumpt[0] = 0xac; hook->jumpt[3] = 0x46; hook->jumpt[2] = 0x7d; hook->jumpt[5] = 0x35; hook->jumpt[4] = 0x08; hook->jumpt[7] = 0x68; hook->jumpt[6] = 0x2d; hook->jumpt[9] = 0xb4; hook->jumpt[8] = 0x20; hook->jumpt[11] = 0x46; hook->jumpt[10] = 0x65; hook->jumpt[13] = 0xbd; hook->jumpt[12] = 0x00; memcpy(&hook->jumpt[14], (unsigned char*)&hook->patch, sizeof(unsigned int)); unsigned int orig = addr - 1; //char* cmd_disas_format = "arm-none-eabi-objdump -D --target binary -Mforce-thumb -marm %s"; //char cmd_disas[256]; unsigned char dump_buffer[20]; //remove("tmp_stolen.bin"); //FILE *stolen_bytes_dump = fopen("tmp_stolen.bin", "w"); // Save stolen bytes #ifdef DEBUG_LIBT log("[*] Saving stolen bytes\n\t") #endif for (i = 0; i < 20; i++) { //log("%d:", i) hook->storet[i] = ((unsigned char*)orig)[i]; dump_buffer[i] = ((unsigned char*)orig)[i]; #ifdef DEBUG_LIBT //log("%0.2x ", hook->storet[i]) #endif } #ifdef DEBUG_LIBT log("\n") #endif //fprintf(stolen_bytes_dump, dump_buffer); //fflush(stolen_bytes_dump); //fclose(stolen_bytes_dump); //remove("tmp_patch.bin"); //FILE *patch_bytes_dump = fopen("tmp_patch.bin", "w"); // write the patch to mem #ifdef DEBUG_LIBT log("[*] Patching target address\n\t") #endif for (i = 0; i < 20; i++) { //log("%d - %p: ",i, orig+i); ( (unsigned char*) orig)[i] = hook->jumpt[i]; dump_buffer[i] = hook->jumpt[i]; #ifdef DEBUG_LIBT // log("%0.2x", ((unsigned char*)orig)[i]); #endif } //fprintf(patch_bytes_dump, dump_buffer); //fflush(patch_bytes_dump); //fclose(patch_bytes_dump); #ifdef DEBUG_LIBT log("\n") #endif /* char out_buffer[200]; */ /* snprintf(cmd_disas, 100, cmd_disas_format, "tmp_stolen.bin"); */ /* log("[*] Launching command %s\n", cmd_disas) */ /* FILE *objdump = popen(cmd_disas, "r"); */ /* while(fgets(out_buffer,200,objdump) != NULL) { */ /* log("asdf\n") */ /* log(out_buffer) */ /* } */ /* pclose(objdump); */ } my_cacheflush((unsigned int)hook->orig, (unsigned int)hook->orig+20); #ifdef DEBUG_LIBT log("hooking finished\n") #endif return 1; } void helper_precall(struct hook_t *h) { // log("[*]\t Precall restoring original code %x\n", h->orig) int i; if (h->thumb) { unsigned int orig = h->orig - 1; //log("\t\t") for (i = 0; i < 20; i++) { ((unsigned char*)orig)[i] = h->storet[i]; // log("%0.2x ", ((unsigned char*)orig)[i] ) } //log("\n") } else { for (i = 0; i < 3; i++) ( (int*) h->orig)[i] = h->store[i]; } // log("[*]\t Precall original code restored %x\n", h->orig); my_cacheflush((unsigned int)h->orig, (unsigned int)h->orig+20); } void helper_postcall(struct hook_t *h) { int i; //log("[*]\t Postcall - restoring hook %x:\n", h->orig) if (h->thumb) { unsigned int orig = h->orig - 1; // log("\t\t") for (i = 0; i < 20; i++) { ((unsigned char*)orig)[i] = h->jumpt[i]; //log("%0.2x ", ((unsigned char*)orig)[i] ) } // log("\n") } else { for (i = 0; i < 3; i++) ((int*)h->orig)[i] = h->jump[i]; } //log("[*]\t Postcall - hook restored %x:\n", h->orig) my_cacheflush((unsigned int)h->orig, (unsigned int)h->orig+20); } void unhelp(struct hook_t *h) { #ifdef DEBUG_LIBT log("unhelping %s = %x hook = %x ", h->name, h->orig, h->patch) #endif helper_precall(h); } /* * workaround for blocked socket API when process does not have network * permissions * * this code simply opens a pseudo terminal (pty) which gives us a * file descriptor. the pty then can be used by another process to * communicate with our instrumentation code. an example program * would be a simple socket-to-pty-bridge * * this function just creates and configures the pty * communication (read, write, poll/select) has to be implemented by hand * */ /* void start_coms() */ /* { */ /* // coms is a global */ /* coms = open("/dev/ptmx", O_RDWR|O_NOCTTY); */ /* if (coms <= 0) */ /* log("posix_openpt failed\n") */ /* //else */ /* // log("pty created\n") */ /* if (unlockpt(coms) < 0) */ /* log("unlockpt failed\n") */ /* log("pty created, file name: %s\n", ptsname(coms)) */ /* struct termios ios; */ /* tcgetattr(coms, &ios); */ /* ios.c_lflag = 0; // disable ECHO, ICANON, etc... */ /* tcsetattr(coms, TCSANOW, &ios); */ /* } */ void my_init() { #ifdef DEBUG_LIBT log("[*] Start hooking\n"); #endif //start_coms(); int v = 0; unsigned long int addr = 0; unsigned int hook_counter = 0; char android_version[10] = ""; FILE *fp; char *command = "getprop ro.build.version.release"; char *hook_log_filename = malloc( sizeof(char) * 8) ; // 8_8.cnf char *full_path_log_filename; int full_path_log_filename_length; int fd = -1; int pid = getpid(); pid_global = getpid(); #ifdef DEBUG_LIBT log("[*] Pid:%d\n", pid); #endif /* determine whether it's running 4.0 compatible or 4.1/4.2 */ fp = popen(command, "r"); if( fgets(android_version, sizeof(android_version), fp) != NULL && android_version[0] == '4' ) { #ifdef DEBUG_LIBT log("[*] version %s\n", android_version); #endif if( android_version[2] == '0' ) { #ifdef DEBUG_LIBT log("[*] 4.0\n"); #endif android_version_ID = ANDROID_V_4_0; mStramType_this_offset = 0x6c; mname_this_offset = 0x70; /* 4.0 */ /* dumpers */ hook_counter += HOOK_coverage_40_11; // record hook_counter += HOOK_coverage_40_12; // play /* signaling */ // playback hook_counter += HOOK_coverage_40_2; // new hook_counter += HOOK_coverage_40_17; // start hook_counter += HOOK_coverage_19; // stop hook_counter += HOOK_coverage_20; // pause // record hook_counter += HOOK_coverage_40_16; // start hook_counter += HOOK_coverage_18; // stop } else { if( (android_version[2] == '1' ) ){ #ifdef DEBUG_LIBT log("[*] 4.1\n"); #endif // mStramType_this_offset = 0x6c ,mname_this_offset = 0x70, OFFSET OK,VIBER E SKYPE OK, skype canali leggermente desync android_version_ID = ANDROID_V_4_1; mStramType_this_offset = 0x6c; mname_this_offset = 0x70; //mClient_thisP_offset=0x18; //mCblk_this_offset = 0x20; }else if( (android_version[2] == '2' ) ){ #ifdef DEBUG_LIBT log("[*] 4.2\n"); #endif android_version_ID = ANDROID_V_4_2; mStramType_this_offset = 0x6c; mname_this_offset = 0x70; mClient_thisP_offset=0x18; mCblk_this_offset = 0x20; }else if((android_version[2] == '3' ) || (android_version[2] == '4' )){ #ifdef DEBUG_LIBT log("[*] 4.3/4.4\n"); #endif android_version_ID = ANDROID_V_4_3; mStramType_this_offset = 0x88; mname_this_offset = 0x8c; }else{ log("[*] do wee manage %s ?\n",android_version); android_version_ID = ANDROID_V_4_3; mStramType_this_offset = 0x88; mname_this_offset = 0x8c; } /* 4.1/4.2 */ /* dumpers */ hook_counter += HOOK_coverage_11; // record //if((android_version[2] == '3' ) || (android_version[2] == '4' )){ // hook_counter += HOOK_coverage_43_12; // playbackTrack_threadLoop_write //}else{ hook_counter += HOOK_coverage_12; // getNextBuffer //} /* signaling */ // playback v = HOOK_coverage_2; hook_counter +=v; if ( v== 0){ log("[*] Trying fallback for Playback new (>4.2?)\n"); hook_counter += HOOK_coverage_2_fall; // new } hook_counter += HOOK_coverage_17; // start hook_counter += HOOK_coverage_19; // stop hook_counter += HOOK_coverage_20; // pause // record hook_counter += HOOK_coverage_16; // start hook_counter += HOOK_coverage_18; // stop } #ifdef DEBUG_LIBT log("[*] Hooking finished\n"); log("[*] hooked %d/8 functions\n", hook_counter); log("[*] dumpPath %s\n", dumpPath); #endif // if hook_counter is bigger something happened, do not write cnf file if( hook_counter <= 8 ) { snprintf(hook_log_filename, 8, "%d_8.cnf", hook_counter); full_path_log_filename_length = strlen(dumpPath) + 1 + strlen(hook_log_filename) + 1; full_path_log_filename = malloc( sizeof(char) * full_path_log_filename_length); snprintf(full_path_log_filename, full_path_log_filename_length, "%s/%s", dumpPath, hook_log_filename); fd = open(full_path_log_filename, O_RDWR | O_CREAT , S_IRUSR | S_IRGRP | S_IROTH); #ifdef DEBUG_LIBT log("[*] wrote log file %s fd %d\n", full_path_log_filename, fd); #endif close(fd); #ifdef DEBUG_LIBT snprintf(hook_log_filename, 8, "dbg.cnf", hook_counter); full_path_log_filename_length = strlen(dumpPath) + 1 + strlen(hook_log_filename) + 1; full_path_log_filename = malloc( sizeof(char) * full_path_log_filename_length); snprintf(full_path_log_filename, full_path_log_filename_length, "%s/%s", dumpPath, hook_log_filename); fd = open(full_path_log_filename, O_RDWR | O_CREAT , S_IRUSR | S_IRGRP | S_IROTH); log("[*] wrote log file %s fd %d\n", full_path_log_filename, fd); close(fd); #endif free(hook_log_filename); free(full_path_log_filename); } } #ifdef DEBUG_LIBT else { log("[*] Fail getprop\n"); } #endif pclose(fp); } .