├── README.md └── jni ├── .DS_Store ├── Android.mk ├── injector.c └── shellcode.S /README.md: -------------------------------------------------------------------------------- 1 | # android_injector 2 | android 注入代码,修复了对zygote的注入 3 | -------------------------------------------------------------------------------- /jni/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stefan00lpf/android_injector/884b6f5b568ee680cbc3dc26254bf33f0176d37a/jni/.DS_Store -------------------------------------------------------------------------------- /jni/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | LOCAL_SRC_FILES:= \ 5 | injector.c \ 6 | shellcode.S 7 | 8 | LOCAL_C_INCLUDES := $(LOCAL_PATH) 9 | 10 | LOCAL_MODULE:= injector 11 | 12 | LOCAL_LDLIBS := -ldl -llog 13 | 14 | LOCAL_STATIC_LIBRARIES := libc 15 | 16 | LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES) 17 | LOCAL_MODULE_TAGS := debug 18 | 19 | include $(BUILD_EXECUTABLE) 20 | -------------------------------------------------------------------------------- /jni/injector.c: -------------------------------------------------------------------------------- 1 | /** 2 | * filename : injector.c 3 | * description : shared libraries injection helper 4 | * author : guhe 5 | * reference : http://bbs.pediy.com/showthread.php?t=141355 & 6 | * http://www.debugman.com/thread/6260/1/1 7 | * created : 8 | * modified by : pingfang.liu@flamingo.comc.n 9 | */ 10 | 11 | /* 12 | 更新其对 zygote的注入支持 13 | 主要对其ptace阻塞的修复 14 | */ 15 | 16 | 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #define ENABLE_DEBUG 0 36 | 37 | #define PTRACE_PEEKTEXT 1 38 | #define PTRACE_POKETEXT 4 39 | #define PTRACE_ATTACH 16 40 | #define PTRACE_CONT 7 41 | #define PTRACE_DETACH 17 42 | #define PTRACE_SYSCALL 24 43 | #define CPSR_T_MASK ( 1u << 5 ) 44 | 45 | #define MAX_PATH 0x100 46 | 47 | #define REMOTE_ADDR( addr, local_base, remote_base ) \ 48 | ( (uint32_t)(addr) + (uint32_t)(remote_base) - (uint32_t)(local_base) ) 49 | 50 | const char *libc_path = "/system/lib/libc.so"; 51 | const char *linker_path = "/system/bin/linker"; 52 | 53 | #if ENABLE_DEBUG 54 | #define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, "inject-kkk", __VA_ARGS__)) 55 | #else 56 | #define LOGD(format,args...) 57 | #endif 58 | 59 | /** 60 | * read data from src and write to buf 61 | */ 62 | int ptrace_readdata( pid_t pid, uint8_t *src, uint8_t *buf, size_t size ) 63 | { 64 | uint32_t i, j, remain; 65 | uint8_t *laddr; 66 | 67 | union u { 68 | long val; 69 | char chars[sizeof(long)]; 70 | } d; 71 | 72 | j = size / 4; 73 | remain = size % 4; 74 | 75 | laddr = buf; 76 | 77 | for ( i = 0; i < j; i ++ ) 78 | { 79 | d.val = ptrace( PTRACE_PEEKTEXT, pid, src, 0 ); 80 | memcpy( laddr, d.chars, 4 ); 81 | src += 4; 82 | laddr += 4; 83 | } 84 | 85 | if ( remain > 0 ) 86 | { 87 | d.val = ptrace( PTRACE_PEEKTEXT, pid, src, 0 ); 88 | memcpy( laddr, d.chars, remain ); 89 | } 90 | 91 | return 0; 92 | } 93 | 94 | /** 95 | * write data to dest 96 | */ 97 | int ptrace_writedata( pid_t pid, uint8_t *dest, uint8_t *data, size_t size ) 98 | { 99 | uint32_t i, j, remain; 100 | uint8_t *laddr; 101 | 102 | union u { 103 | long val; 104 | char chars[sizeof(long)]; 105 | } d; 106 | 107 | j = size / 4; 108 | remain = size % 4; 109 | 110 | laddr = data; 111 | 112 | for ( i = 0; i < j; i ++ ) 113 | { 114 | memcpy( d.chars, laddr, 4 ); 115 | ptrace( PTRACE_POKETEXT, pid, dest, d.val ); 116 | 117 | dest += 4; 118 | laddr += 4; 119 | } 120 | 121 | if ( remain > 0 ) 122 | { 123 | d.val = ptrace( PTRACE_PEEKTEXT, pid, dest, 0 ); 124 | for ( i = 0; i < remain; i ++ ) 125 | { 126 | d.chars[i] = *laddr ++; 127 | } 128 | 129 | ptrace( PTRACE_POKETEXT, pid, dest, d.val ); 130 | 131 | } 132 | 133 | return 0; 134 | } 135 | 136 | /** 137 | * write a string to dest 138 | */ 139 | int ptrace_writestring( pid_t pid, uint8_t *dest, char *str ) 140 | { 141 | return ptrace_writedata( pid, dest, str, strlen(str)+1 ); 142 | } 143 | 144 | /** 145 | * call function at addr in target process 146 | * pid pid of target process 147 | * addr address of the function you want to call 148 | * num_params the number of parameters 149 | * regs registers' status 150 | */ 151 | int ptrace_call( pid_t pid, uint32_t addr, long *params, uint32_t num_params, struct pt_regs* regs ) 152 | { 153 | uint32_t i; 154 | 155 | for ( i = 0; i < num_params && i < 4; i ++ ) // ?? 156 | { 157 | regs->uregs[i] = params[i]; 158 | } 159 | 160 | // 161 | // push remained params into stack 162 | // 163 | if ( i < num_params ) 164 | { 165 | regs->ARM_sp -= (num_params - i) * sizeof(long) ; 166 | ptrace_writedata( pid, (void *)regs->ARM_sp, (uint8_t *)¶ms[i], (num_params - i) * sizeof(long) ); 167 | } 168 | 169 | regs->ARM_pc = addr; 170 | if ( regs->ARM_pc & 1 ) // thumb 171 | { 172 | regs->ARM_pc &= (~1u); 173 | regs->ARM_cpsr |= CPSR_T_MASK; 174 | } 175 | else // arm 176 | { 177 | regs->ARM_cpsr &= ~CPSR_T_MASK; 178 | } 179 | 180 | regs->ARM_lr = 0; // 181 | 182 | if ( ptrace_setregs( pid, regs ) == -1 || ptrace_continue( pid ) == -1 ) 183 | { 184 | return -1; 185 | } 186 | 187 | waitpid( pid, NULL, WUNTRACED ); 188 | 189 | return 0; 190 | } 191 | 192 | /** 193 | * read registers' status 194 | */ 195 | int ptrace_getregs( pid_t pid, struct pt_regs* regs ) 196 | { 197 | if ( ptrace( PTRACE_GETREGS, pid, NULL, regs ) < 0 ) 198 | { 199 | perror( "ptrace_getregs: Can not get register values" ); 200 | return -1; 201 | } 202 | 203 | return 0; 204 | } 205 | 206 | /** 207 | * set registers' status 208 | */ 209 | int ptrace_setregs( pid_t pid, struct pt_regs* regs ) 210 | { 211 | if ( ptrace( PTRACE_SETREGS, pid, NULL, regs ) < 0 ) 212 | { 213 | perror( "ptrace_setregs: Can not set register values" ); 214 | return -1; 215 | } 216 | 217 | return 0; 218 | } 219 | 220 | /** 221 | * continue running 222 | */ 223 | int ptrace_continue( pid_t pid ) 224 | { 225 | if ( ptrace( PTRACE_CONT, pid, NULL, 0 ) < 0 ) 226 | { 227 | perror( "ptrace_cont" ); 228 | return -1; 229 | } 230 | 231 | return 0; 232 | } 233 | 234 | 235 | static void* connect_to_zygote(void* arg){ 236 | int s, len; 237 | struct sockaddr_un remote; 238 | 239 | LOGD("[+] wait 2s..."); 240 | sleep(2); 241 | 242 | if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) != -1) { 243 | remote.sun_family = AF_UNIX; 244 | strcpy(remote.sun_path, "/dev/socket/zygote"); 245 | len = strlen(remote.sun_path) + sizeof(remote.sun_family); 246 | LOGD("[+] start to connect zygote socket"); 247 | connect(s, (struct sockaddr *) &remote, len); 248 | LOGD("[+] close socket"); 249 | close(s); 250 | } 251 | 252 | return NULL ; 253 | } 254 | 255 | 256 | /** 257 | * attach to target process 258 | */ 259 | int ptrace_attach( pid_t pid, int zygote) 260 | { 261 | if ( ptrace( PTRACE_ATTACH, pid, NULL, 0 ) < 0 ) 262 | { 263 | perror( "ptrace_attach" ); 264 | return -1; 265 | } 266 | 267 | waitpid( pid, NULL, WUNTRACED ); 268 | 269 | /* 270 | * Restarts the stopped child as for PTRACE_CONT, but arranges for 271 | * the child to be stopped at the next entry to or exit from a sys‐ 272 | * tem call, or after execution of a single instruction, respec‐ 273 | * tively. 274 | */ 275 | if ( ptrace( PTRACE_SYSCALL, pid, NULL, 0 ) < 0 ) 276 | { 277 | perror( "ptrace_syscall" ); 278 | return -1; 279 | } 280 | 281 | waitpid( pid, NULL, WUNTRACED ); 282 | 283 | if (zygote) { 284 | connect_to_zygote(NULL); 285 | } 286 | 287 | if (ptrace(PTRACE_SYSCALL, pid, NULL, NULL ) < 0) { 288 | LOGD("ptrace_syscall"); 289 | return -1; 290 | } 291 | 292 | waitpid(pid, NULL, WUNTRACED); 293 | 294 | return 0; 295 | } 296 | 297 | /** 298 | * detach from target process 299 | */ 300 | int ptrace_detach( pid_t pid ) 301 | { 302 | if ( ptrace( PTRACE_DETACH, pid, NULL, 0 ) < 0 ) 303 | { 304 | perror( "ptrace_detach" ); 305 | return -1; 306 | } 307 | 308 | return 0; 309 | } 310 | 311 | 312 | 313 | /** 314 | * get the base address of specific module with a given name 315 | */ 316 | void* get_module_base( pid_t pid, const char* module_name ) 317 | { 318 | FILE *fp; 319 | long addr = 0; 320 | char *pch; 321 | char filename[32]; 322 | char line[1024]; 323 | 324 | if ( pid < 0 ) 325 | { 326 | /* self process */ 327 | snprintf( filename, sizeof(filename), "/proc/self/maps", pid ); 328 | } 329 | else 330 | { 331 | snprintf( filename, sizeof(filename), "/proc/%d/maps", pid ); 332 | } 333 | 334 | fp = fopen( filename, "r" ); 335 | 336 | if ( fp != NULL ) 337 | { 338 | while ( fgets( line, sizeof(line), fp ) ) 339 | { 340 | if ( strstr( line, module_name ) ) 341 | { 342 | pch = strtok( line, "-" ); 343 | addr = strtoul( pch, NULL, 16 ); 344 | 345 | if ( addr == 0x8000 ) 346 | addr = 0; 347 | 348 | break; 349 | } 350 | } 351 | 352 | fclose( fp ) ; 353 | } 354 | 355 | return (void *)addr; 356 | } 357 | 358 | /** 359 | * calculate the address of the module in target process 360 | */ 361 | void* get_remote_addr( pid_t target_pid, const char* module_name, void* local_addr ) 362 | { 363 | void* local_module_base, *remote_module_base; 364 | 365 | // local and remote process both have a module named module_name 366 | local_module_base = get_module_base( -1, module_name ); 367 | remote_module_base = get_module_base( target_pid, module_name ); 368 | 369 | LOGD( "[+] get_remote_addr: local[%x], remote[%x]\n", \ 370 | local_module_base, remote_module_base ); 371 | 372 | // symbols in module each have a fixed offset 373 | // for example, mmap() has a fixed offset in libc.so 374 | return (void *)REMOTE_ADDR( local_addr, local_module_base, remote_module_base ); 375 | } 376 | 377 | /** 378 | * find the pid of a process 379 | */ 380 | int find_pid_of( const char *process_name ) 381 | { 382 | int id; 383 | pid_t pid = -1; 384 | DIR *dir; 385 | FILE *fp; 386 | char filename[32]; 387 | char cmdline[256]; 388 | 389 | struct dirent * entry; 390 | 391 | if ( process_name == NULL ) 392 | return -1; 393 | 394 | dir = opendir( "/proc" ); 395 | if ( dir == NULL ) 396 | return -1; 397 | 398 | while( (entry = readdir( dir )) != NULL ) 399 | { 400 | id = atoi( entry->d_name ); 401 | if ( id != 0 ) 402 | { 403 | sprintf( filename, "/proc/%d/cmdline", id ); 404 | fp = fopen( filename, "r" ); 405 | if ( fp ) 406 | { 407 | fgets( cmdline, sizeof(cmdline), fp ); 408 | fclose( fp ); 409 | 410 | if ( strcmp( process_name, cmdline ) == 0 ) // process found 411 | { 412 | pid = id; 413 | break; 414 | } 415 | } 416 | } 417 | } 418 | 419 | closedir( dir ); 420 | 421 | return pid; 422 | } 423 | 424 | 425 | const char* get_process_name(pid_t pid) { 426 | static char buffer[255]; 427 | FILE* f; 428 | char path[255]; 429 | 430 | snprintf(path, sizeof(path), "/proc/%d/cmdline", pid); 431 | if ((f = fopen(path, "r")) == NULL) { 432 | return NULL; 433 | } 434 | 435 | if (fgets(buffer, sizeof(buffer), f) == NULL) { 436 | return NULL; 437 | } 438 | 439 | fclose(f); 440 | return buffer; 441 | } 442 | 443 | /** 444 | * inject shared library to target process 445 | */ 446 | int inject_remote_process( pid_t target_pid, const char *library_path, const char *function_name, void *param, size_t param_size ) 447 | { 448 | int ret = -1; 449 | void *mmap_addr, *dlopen_addr, *dlsym_addr, *dlclose_addr; 450 | void *local_handle, *remote_handle, *dlhandle; 451 | uint8_t *map_base; 452 | uint8_t *dlopen_param1_ptr, *dlsym_param2_ptr, *saved_r0_pc_ptr, *inject_param_ptr, *remote_code_ptr, *local_code_ptr; 453 | 454 | struct pt_regs regs, original_regs; 455 | 456 | // declared in shellcode.s 457 | extern uint32_t _dlopen_addr_s, _dlopen_param1_s, _dlopen_param2_s, _dlsym_addr_s, \ 458 | _dlsym_param2_s, _dlclose_addr_s, _inject_start_s, _inject_end_s, _inject_function_param_s, \ 459 | _saved_cpsr_s, _saved_r0_pc_s, _hook_entry_addr_s; 460 | 461 | uint32_t code_length; 462 | 463 | long parameters[10]; 464 | 465 | LOGD( "[+] Injecting process: %d\n", target_pid ); 466 | 467 | //解析是否是zygote 468 | const char* process_name = get_process_name(target_pid); 469 | 470 | if ( ptrace_attach( target_pid , strstr(process_name,"zygote")) == -1 ) 471 | return EXIT_SUCCESS; 472 | 473 | if ( ptrace_getregs( target_pid, ®s ) == -1 ) 474 | goto exit; 475 | 476 | // save original registers 477 | memcpy( &original_regs, ®s, sizeof(regs) ); 478 | 479 | mmap_addr = get_remote_addr( target_pid, "/system/lib/libc.so", (void *)mmap ); 480 | 481 | LOGD( "[+] Remote mmap address: %x\n", mmap_addr ); 482 | 483 | // call mmap 484 | parameters[0] = 0; // addr 485 | parameters[1] = 0x4000; // size 486 | parameters[2] = PROT_READ | PROT_WRITE | PROT_EXEC; // prot 487 | parameters[3] = MAP_ANONYMOUS | MAP_PRIVATE; // flags 488 | parameters[4] = 0; //fd 489 | parameters[5] = 0; //offset 490 | 491 | LOGD( "[+] Calling mmap in target process.\n" ); 492 | 493 | if ( ptrace_call( target_pid, (uint32_t)mmap_addr, parameters, 6, ®s ) == -1 ) 494 | goto exit; 495 | 496 | if ( ptrace_getregs( target_pid, ®s ) == -1 ) 497 | goto exit; 498 | 499 | LOGD( "[+] Target process returned from mmap, return value=%x, pc=%x \n", regs.ARM_r0, regs.ARM_pc ); 500 | 501 | map_base = (uint8_t *)regs.ARM_r0; 502 | 503 | // get address of dlopen(), dlsym() and dlclose() in target process 504 | dlopen_addr = get_remote_addr( target_pid, linker_path, (void *)dlopen ); 505 | dlsym_addr = get_remote_addr( target_pid, linker_path, (void *)dlsym ); 506 | dlclose_addr = get_remote_addr( target_pid, linker_path, (void *)dlclose ); 507 | 508 | LOGD( "[+] Get imports: dlopen: %x, dlsym: %x, dlclose: %x\n", dlopen_addr, dlsym_addr, dlclose_addr ); 509 | 510 | remote_code_ptr = map_base + 0x3C00; 511 | local_code_ptr = (uint8_t *)&_inject_start_s; 512 | 513 | _dlopen_addr_s = (uint32_t)dlopen_addr; 514 | _dlsym_addr_s = (uint32_t)dlsym_addr; 515 | _dlclose_addr_s = (uint32_t)dlclose_addr; 516 | 517 | LOGD( "[+] Inject code start: %x, end: %x\n", local_code_ptr, &_inject_end_s ); 518 | 519 | code_length = (uint32_t)&_inject_end_s - (uint32_t)&_inject_start_s; 520 | dlopen_param1_ptr = local_code_ptr + code_length + 0x20; // 0x20 == 32 521 | dlsym_param2_ptr = dlopen_param1_ptr + MAX_PATH; 522 | saved_r0_pc_ptr = dlsym_param2_ptr + MAX_PATH; 523 | inject_param_ptr = saved_r0_pc_ptr + MAX_PATH; 524 | 525 | /* dlopen parameter 1: library name */ 526 | strcpy( dlopen_param1_ptr, library_path ); 527 | _dlopen_param1_s = REMOTE_ADDR( dlopen_param1_ptr, local_code_ptr, remote_code_ptr ); 528 | LOGD( "[+] _dlopen_param1_s: %x\n", _dlopen_param1_s ); 529 | 530 | /* dlsym parameter 2: function name */ 531 | strcpy( dlsym_param2_ptr, function_name ); 532 | _dlsym_param2_s = REMOTE_ADDR( dlsym_param2_ptr, local_code_ptr, remote_code_ptr ); 533 | LOGD( "[+] _dlsym_param2_s: %x\n", _dlsym_param2_s ); 534 | 535 | /* saved cpsr */ 536 | _saved_cpsr_s = original_regs.ARM_cpsr; 537 | 538 | /* saved r0-pc */ 539 | memcpy( saved_r0_pc_ptr, &(original_regs.ARM_r0), 16 * 4 ); // r0 ~ r15 540 | _saved_r0_pc_s = REMOTE_ADDR( saved_r0_pc_ptr, local_code_ptr, remote_code_ptr ); 541 | LOGD( "[+] _saved_r0_pc_s: %x\n", _saved_r0_pc_s ); 542 | 543 | /* Inject function parameter */ 544 | memcpy( inject_param_ptr, param, param_size ); 545 | _inject_function_param_s = REMOTE_ADDR( inject_param_ptr, local_code_ptr, remote_code_ptr ); 546 | LOGD( "[+] _inject_function_param_s: %x\n", _inject_function_param_s ); 547 | 548 | LOGD( "[+] Remote shellcode address: %x\n", remote_code_ptr ); 549 | ptrace_writedata( target_pid, remote_code_ptr, local_code_ptr, 0x400 ); 550 | 551 | memcpy( ®s, &original_regs, sizeof(regs) ); 552 | regs.ARM_sp = (long)remote_code_ptr; 553 | 554 | // change pc to execute instructions at remote_code_ptr 555 | regs.ARM_pc = (long)remote_code_ptr; 556 | 557 | LOGD( "[+] hook_entry address: %x\n", _hook_entry_addr_s); 558 | 559 | ptrace_setregs( target_pid, ®s ); 560 | 561 | ptrace_detach( target_pid ); 562 | 563 | // inject succeeded 564 | ret = 0; 565 | 566 | exit: 567 | return ret; 568 | } 569 | 570 | void print_usage( const char *pname, int exit_code ) 571 | { 572 | printf( "Usage: %s -p pid -l libpath\n", pname ); 573 | printf( " -h --help Display this usage information.\n" 574 | " -p --pid PID of target process.\n" 575 | " -l --libpath Absolute path of the shared library that will be injected.\n" ); 576 | 577 | exit( exit_code ); 578 | } 579 | 580 | int main( int argc, char** argv ) 581 | { 582 | int target_pid; 583 | char *libpath; 584 | 585 | const char *pname = strrchr( argv[0], '/' ) + 1; 586 | if (argc < 2) 587 | print_usage(pname, 1); 588 | 589 | int next_opt; 590 | const char *short_opts = "hp:l:"; 591 | const struct option long_opts[] = { 592 | {"help", 0, NULL, 'h'}, 593 | {"pid", 1, NULL, 'p'}, 594 | {"libpath", 1, NULL, 'l'}, 595 | {NULL, 0, NULL, 0 } 596 | }; 597 | 598 | do 599 | { 600 | next_opt = getopt_long( argc, argv, short_opts, long_opts, NULL ); 601 | switch ( next_opt ) 602 | { 603 | case 'h': 604 | print_usage( pname, 0 ); 605 | case 'p': 606 | target_pid = atoi( optarg ); 607 | break; 608 | case 'l': 609 | libpath = optarg; 610 | break; 611 | case '?': 612 | printf("\n"); 613 | print_usage( pname, 1 ); 614 | case -1: 615 | break; 616 | default: 617 | ; 618 | } 619 | } while ( next_opt != -1 ); 620 | 621 | char *param = ""; 622 | 623 | inject_remote_process( target_pid, libpath, "so_entry", param, strlen(param) ); 624 | 625 | return 0; 626 | } 627 | -------------------------------------------------------------------------------- /jni/shellcode.S: -------------------------------------------------------------------------------- 1 | .global _dlopen_addr_s 2 | .global _dlopen_param1_s 3 | .global _dlopen_param2_s 4 | 5 | .global _dlsym_addr_s 6 | .global _dlsym_param2_s 7 | 8 | .global _dlclose_addr_s 9 | 10 | .global _inject_start_s 11 | .global _inject_end_s 12 | 13 | .global _hook_entry_addr_s 14 | 15 | .global _inject_function_param_s 16 | 17 | .global _saved_cpsr_s 18 | .global _saved_r0_pc_s 19 | 20 | .data 21 | 22 | _hook_entry_addr_s: 23 | .word 0x01 24 | 25 | _inject_start_s: 26 | @ debug loop 27 | 3: 28 | @sub r1, r1, #0 29 | @B 3b 30 | 31 | @dlopen 32 | ldr r1, _dlopen_param2_s 33 | ldr r0, _dlopen_param1_s 34 | ldr r3, _dlopen_addr_s 35 | blx r3 36 | subs r4, r0, #0 37 | beq 2f 38 | 39 | @dlsym 40 | ldr r1, _dlsym_param2_s 41 | ldr r3, _dlsym_addr_s 42 | blx r3 43 | subs r3, r0, #0 44 | @beq 1f 45 | 46 | @call our function 47 | ldr r0, _inject_function_param_s 48 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 49 | @ldr r5, =_hook_entry_addr_s; 50 | @str r3, [r5] 51 | @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 52 | blx r3 @function's address is stored in r3 53 | subs r0, r0, #0 54 | beq 2f 55 | 56 | 2: 57 | @restore context 58 | ldr r1, _saved_cpsr_s 59 | msr cpsr_cf, r1 60 | ldr sp, _saved_r0_pc_s 61 | ldmfd sp, {r0-pc} 62 | 63 | _dlopen_addr_s: 64 | .word 0x11111111 65 | 66 | _dlopen_param1_s: 67 | .word 0x11111111 68 | 69 | _dlopen_param2_s: 70 | .word 0x2 71 | 72 | _dlsym_addr_s: 73 | .word 0x11111111 74 | 75 | _dlsym_param2_s: 76 | .word 0x11111111 77 | 78 | _dlclose_addr_s: 79 | .word 0x11111111 80 | 81 | _inject_function_param_s: 82 | .word 0x11111111 83 | 84 | _saved_cpsr_s: 85 | .word 0x11111111 86 | 87 | _saved_r0_pc_s: 88 | .word 0x11111111 89 | 90 | _inject_end_s: 91 | 92 | .space 0x400, 0 93 | 94 | .end --------------------------------------------------------------------------------