Android 5 HOOK Technology Research ADBI Project 02

Source: Internet
Author: User
Tags array length sprintf strtok

SOURCE Analysis

Hijack.c

This file implements an injection tool that can inject a so into the process specified by the-p parameter.

To achieve this effect, first of all, we need to get the address of several functions of the target process, such as the Dlopen function, and secondly, we need to be able to affect the normal execution flow of the target process, let it execute dlopen load the specified library sometime, and finally, the function in the memory can be overwritten with the function of dynamically loaded so.

Here is how to get the address of the target process to specify the function, the first thing to get is the address of the Dlopen function, ADBI is doing this:

    void *LDL = Dlopen ("libdl.so", Rtld_lazy);    If (LDL) {        = (unsigned long) dlsym (LDL, "Dlopen")//dlopenaddr The address of the Dlopen function that holds the process         Dlclose (LDL);    }    unsigned long int lkaddr;    unsigned long int lkaddr2;    Find_linker (Getpid (), &lkaddr) find_linker (PID, &= lkaddr2 + (DLOPENADDR-LKADDR);// DLOPENADDR address of the Dlopen function that holds the target process      

The above code is to get the Dlopen function address of the target process.

First, Dlopen loads the libdl.so, since the process starts libdl.so will certainly load well first, so here returns the libdl.so map that has been loaded in the start address space of the process, and then calls Dlsym to return the Dlopen function address of the process.

Next, the Find_linker function takes advantage of the/proc/pid/maps file to get the address space of the process PID and then the libdl.so map to the start address of the memory, where the initial address of the libdl.so mapping of the injection process is LKADDR, The target process is LKADDR2

Finally, the migration of the code in the Libdl.so dynamic library using the Dlopen function is fixed (the injection process and the injected process use the same libdl.so), DLOPENADDR-LKADDR first calculates the offset value, LKADDR2 The above offset value is the address of the Dlopen function of the target process.

Maps files on Linux and Android address block naming some differences, General Linux on the libdl.so map address is like this

7f6a96672000-7f6a96695000 R-xp 00000000 08:01 397502                  /lib/x86_64-linux-gnu/ld-2.19.so

The life on Android is called Linker.

The Find_linker function calls the Load_memmap function and the find_linker_mem function,

static int Find_linker (pid_t pid, unsigned long *addr) {    struct mm mm[1000];    unsigned long libcaddr;    int nmm;    Char libc[256];    symtab_t s;    Load_memmap (PID, MM, &nmm)) {printf ("Cannot read memory map\n"); return-1find_linker_mem(libc, sizeof (LIBC), &  LIBCADDR, MM, nmm)) {printf ("Cannot find libc\n"), return-1;} *addr = libcaddr; return 1;} 

Basic flow of the LOAD_MEMMAP function: Open the maps file, parse it into an array according to the format of the maps file, each item holds the name of a dynamic library and its mapping to the start and end addresses in memory

static intLoad_memmap (pid_t pid, struct mm *mm, int *NMMP) {char raw[80000];//This depends on the number of libraries an executable uses CharName[max_name_len]; char *P unsigned longStart, end; struct mm *M int NMM = 0; IntFD, RV; IntI sprintf (Raw, "/proc/%d/maps", PID); FD =Open (raw, o_rdonly); if (0 >FD) {printf ("Can ' t open%s for reading\n", raw); Return-1; }/* Zero to ensure data is null terminated */memset (Raw, 0, sizeof(raw)); p =Raw while (1) {RV = read (FD, p, sizeof (RAW)-(P-Raw)); if (0 >RV) {//perror ("read"); return-1; } if (0 = =RV) Break; p + =rv if (P-raw >= sizeof(raw)) {printf ("Too Many memory mapping\n"); Return-1; }} close (FD); p = strtok (raw, "\ n")); m =mm While(p) {/* Parse current map line */RV = sscanf (P, "%08lx-%08lx%*s%*s%*s%*s%s\n", &start, &  end, name); p = strtok (NULL, "\ n" ), if (rv = = 2 ) {m = &mm[nmm++ ]; m->start =  start; m->end =  End strcpy (m->  name, memory_only); continue ;} if (Strstr (name, "stack")! = 0 ) {stack_start =  start ; Stack_end =  End,}/* Search backward for and mapping with same name */for (i = nmm-1; I >= 0; i--) {m = &  mm[i]; if (!strcmp (m->  name, name)) break ;} if (i >= 0 ) {if (Start < M->  Start) M->start =  start, if (End > M->  end) M->end =  End;} else  {/* new entry */ M = &mm[nmm++ ]; m->start =  start; m->end =  end; strcpy (M->  name, name);}} *NMMP =< span> nmm; return 0 ;}                

Find_linker_mem function Flow: Traverse the above array, according to the dynamic library name matching, you can get libdl.so corresponding array elements, so that libdl.so in the process of the start and end of the address, the code is not posted here.

Above, is the method that gets the real address of a function within a dynamic library of the target process in the target process. So the target process, the address of the non-dynamic library function How to get it?

=== ==

Next, we will study the second question, how to affect the execution flow of the target process, we must introduce the Ptrace function.

Ptrace

Synopsis       #include <sys/ptrace.h>       long ptrace (enum __ptrace_request request, pid_t pid,                   void *addr, void *data);D escription       the  ptrace ()  system  Call  provides  a  means by  which one       process (the "tracer") may observe and control the  execution       of another process (the "Tracee"), and Examin E and change the       Tracee ' s memory and  registers.   It  is  primarily  used  to       implement breakpoint debugging and system call tracing.

Dynamic library injection technology generally relies on the ptrace mechanism, Ptrace is a system call implemented by Linux kernel in order to support the application-layer debug feature, which provides a mechanism for "let a process be associated to a B process and dynamically modify the memory and registers of B processes". Because a process can dynamically modify the memory space and register value of the B process, all can affect the execution sequence of the B process, such as inserting a piece of code into a normal execution sequence, and letting the B process load a dynamic library.

Below is a study of how ADBI uses Ptrace to achieve its goal:

First, attach to the target process

  if (0 > Ptrace (ptrace_attach, PID, 0, 0)) {        printf ("Cannot ATTACH to%d, error!\n", PID);        Exit (1);    }    Waitpid (PID, NULL, 0);  

Second, get the target process current register

Ptrace (Ptrace_getregs, PID, 0, &regs);

Next, the new register value is constructed, and this step is key.

Array SC holds initialization instructions

unsigned int sc[] = {0xe59f0040,//Ldr r0, [pc, #64]; <.text+0x48>0xe3a01000,//MOV r1, #0; 0x00xe1a0e0 0f,//mov LR, pc0xe59ff038,//Ldr pc, [pc, #56]; 4c <.text+0x4c>0xe59fd02c,//Ldr SP, [pc, #44]; <.text+0x44>0xe59f0010,//Ldr r0, [pc, #20]; <.text+0x30>0xe59f1010,//LDR R1, [pc, #20]; <.text+0x34>0xe59f2010,//LDR R2, [pc, #20]; <.text+0x38>0xe59f3010,//LDR R3, [PC, #20]; 3c <.text+0x3c>0xe59fe010,//Ldr LR, [pc, #20]; <.text+0x40>0xe59ff010,//Ldr pc, [pc, #20]; <.text+0x44>0xe1a00000,//NOP r00xe1a00000,//NOP R1 0XE1                     a00000,//NOP R2 0xe1a00000,//NOP R3 0xe1a00000,//NOP LR 0xe1a00000,//NOP pc0xe1a00000,//NOP sp0xe1a00000,//NOP addr of libname0xe1a00000,// NOP dlopenaddr};

The following register values obtained using Ptrace are populated with 11 to 17 of the SC array,

    SC[11] = regs. Arm_r0;    SC[12] = regs. ARM_R1;    SC[13] = regs. ARM_R2;    SC[14] = regs. ARM_R3;    SC[15] = regs. ARM_LR;    SC[16] = regs. ARM_PC; SC[17] = regs. ARM_SP;

The address of the Dlopen function of the target process is then populated to the 19th position, 18 bits holds the address of the name string of the dynamic library, and the WIRTE_MEM function is called to write the name string of the dynamic library to the memory area specified by the LIBADDR address.

SC[19] = dlopenaddr;        Push library name to stack    libaddr = regs. Arm_sp-n*4-sizeof(SC);    SC[18] = libaddr;          Write library name to stack    if (0 > Write_mem (PID, (unsigned long*) arg, N, libaddr)) {        printf ("Cannot Write library name (%s) to Stack, error!\n ", arg);        Exit (1);}

where n is so, the number of bytes in the dynamic library (such as/data/local/tmp/libexample.so) is +1, then divided by 4, and if the remainder, the result is added 1. In fact, the resulting n is a value of ' 4 bytes ' units, so the main is the implementation of the WRITE_MEM function, see below. In combination with the above code, this string will be written to the libaddr corresponding memory, this memory address is so calculated:

Case ' l ':                                n = strlen (optarg) +1;                                n = n/4 + (n%4? 1:0);                                arg = malloc (n*sizeof (unsigned long));                                memcpy (ARG, Optarg, n*4);                                break;    

Here's how Write_mem writes a piece of data to the memory address of the target process:

static intWrite_mem (pid_t pid, unsigned long *buf, int nlong, unsigned long pos) {        unsigned long *p;        int i;        for (p = buf, i = 0; i < Nlong; p++, i++)                if (0 > Ptrace (ptrace_poketext, PID, (void *) (pos+ (I*4)), (void *) *p)) return-1; return 0;}       

The PID is the target process identifier, BUF is the block of data to write to the target process memory, Nlong is the length of ' 4 bytes ', and POS is the address to write. Because the data buf is a long array, the loop writes 4 bytes of data at a time. Finally, the Ptrace function is called, and the first parameter is written for the Ptrace_poketext implementation.

Next, write the new instruction data (that is, the SC array) to the target process:

Write code to stack    codeaddr = regs. Arm_sp-sizeof(SC);    if (0 > Write_mem (PID, (unsigned long*) &sc, sizeof (SC)/sizeof (long), codeaddr)) {        printf ("Cannot write Code, error!\n ");        Exit (1);    } Calc Stack pointer   

As you can see, the method is similar to the above write dynamic library name string, the destination address to write = the stack top pointer-sc array length, and then call the Write_mem function to write the array Sc

Next, move the stack top pointer to the new stack top (move the SC array length + dynamic library name string length to the lower address), and then, depending on whether there are mprotect calls, there are two execution flows: if there is no mprotect, the value of the PC register is changed to the position where the SC array begins. That is, the instruction to execute the SC array directly. Otherwise, the value of the PC register is set to the Mprotect function, and the LR register is set to the SC array. The R0,R1,R2 parameter is set to the parameter of the Mprotect call, so that the Mprotect function is first executed to set the R0,R1 specified memory range to R2 specified permission, in this case, the target memory is set to RWX, and then the SC is executed after the mprotect is executed. An array of instructions.

    Regs. ARM_SP = regs. Arm_sp-n*4-sizeof(SC);    Call Mprotect () to make stack executable    regs. Arm_r0 = Stack_start; Want to make stack executable    //printf ("R0%x\n", regs. ARM_R0);    Regs. ARM_R1 = Stack_end-stack_start; Stack size    //printf ("Mprotect (%x,%d, all) \ n", regs. Arm_r0, Regs. ARM_R1);    Regs. ARM_R2 = prot_read| prot_write| Prot_exec; Protections    //Normal mode, first call Mprotect    if (Nomprotect = = 0) {        if (Debug)            printf (" Calling mprotect\n ");        Regs. ARM_LR = codeaddr; Points to loading and fixing code        regs. ARM_PC = mprotectaddr; Execute Mprotect ()    }    //No need to execute mprotect on old Android versions    else
         
          //Just execute the ' shellcode '}
              

After these settings, the new register values are as follows:

In conjunction with the above diagram, the new instruction stream is executed as follows (refer to "Reference 1" below)

It is important to note that for ARM processors, the value of the PC register is not the address of the current executing instruction, but the address of the second instruction.

Well, we formally begin to analyze the meaning of the code, and the instruction will be executed from low to high from the position indicated by CODEADDR.

The first instruction adds 64 to the value of the PC register, reads the contents of that place (4 bytes), and then puts it in the register R0. Just now, the PC register value points to the current instruction position plus 8 bytes, which means that this instruction actually reads the current instruction position down to 72 bytes. Since the SC array is of type int, it is the current element position of the array down to 18 elements. Count, just the position of libaddr. So this instruction is for the R0 register to point to the. So shared library pathname string.

The second instruction is a simple one, which assigns 0 to the register R1.

The third instruction is used to save the PC register value to the LR register, which is done in order to invoke the Dlopen () function to return, jump to the instruction "Ldr sp, [pc, #56]" at the end.

The fourth instruction is to load the PC plus 56 values into the PC, where is the pc+56? The current instruction position is 64 bytes, 16 elements, just the call address of the Dlopen () function. So, this command is actually called the Dlopen () function, the passed parameter is the shared library path name that the R0 register points to, and the other is 0 in the R1 register.

Call Dlopen () returns and will continue to execute all of the following instructions, I will not analyze each, the role is to restore the target process the value of the original register. First the SP, then the r0, R1, R2, R3, and LR, and finally restore the value of the original PC, and continue to execute the instructions before the pause, as if nothing had happened.

=====

Finally, using Ptrace to set a new register value into the target memory, the hook starts to take effect

  Ptrace (Ptrace_setregs, PID, 0, &regs);        Ptrace (Ptrace_detach, PID, 0, (void *) sigcont);

Finally, if the parameter has-S, the following process is performed:

    if (appname) {if (Ptrace (Ptrace_setoptions, PID, (void*) 1, (void*) (ptrace_o_tracefork))) {Prin            TF ("FATAL error:ptrace (ptrace_setoptions, ...)");        return-1;        } ptrace (Ptrace_cont, PID, (void*) 1, 0);        int t;        int stat;        int child_pid = 0; for (;;) {t = waitpid ( -1, &stat, __wall|            wuntraced);                if (t! = 0 && T = = child_pid) {char fname[256];                sprintf (fname, "/proc/%d/cmdline", child_pid);                int fp = open (fname, o_rdonly);                    if (FP < 0) {Ptrace (Ptrace_syscall, child_pid, 0, 0);                Continue                } read (FP, fname, sizeof (fname));                Close (FP); if (strcmp (fname, appname) = = 0) {//Detach from Zygote ptrace (Ptrace_detach, PID, 0                    , (void *) sigcont); Now perform on new process PID =Child_pid;                Break                    } else {ptrace (ptrace_syscall, child_pid, 0, 0);                Continue  }} if (wifstopped (stat) && (wstopsig (stat) = = SIGTRAP)) {if (stat >>                    & Ptrace_event_fork) {if (Debug > 1) printf ("fork\n"); int b = t;                     Save parent PID Ptrace (ptrace_geteventmsg, T, 0, &child_pid);                              t = child_pid;                    Ptrace (Ptrace_cont, B, (void*) 1, 0);                Ptrace (Ptrace_syscall, child_pid, 0, 0);        }}}} if (zygote) {int i = 0; for (i = 0; i < zygote; i++) {//--zygote fix---//we had to wait until the Syscall was compl            eted, important!            Ptrace (Ptrace_syscall, PID, 0, 0);          if (Debug > 1)      printf ("/");            Waitpid (PID, NULL, 0);                Ptrace (Ptrace_getregs, PID, 0, &regs); if (regs.                Arm_ip! = 0) {if (Debug > 1) printf ("Not a syscall entry, wait for entry\n");                Ptrace (Ptrace_syscall, PID, 0, 0);            Waitpid (PID, NULL, 0);            } ptrace (Ptrace_syscall, PID, 0, 0);            if (Debug > 1) printf ("\ \");                 Waitpid (PID, NULL, 0); }    }

Reference

http://blog.csdn.net/roland_sun/article/details/34109569

Android 5 HOOK Technology Research ADBI Project 02

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.