The use of root in Android to implement the System_server in the binder of the IOCTL call interception

Source: Internet
Author: User

The use of root in Android to implement the System_server in the binder of the IOCTL call interceptionCategory: Android2013-06-19 18:09 779 people read comments (0) favorite reports Author: Passion
Time: 2012-10-18,13:53:53
Links: http://bbs.pediy.com/showthread.php?t=157419

The use of root in Android to implement the System_server in the binder of the IOCTL call interception
Passion
2012-10-18
Keywords: Android, Hook, API, Binder, inject, intercept
(reproduced please specify the source)

======================================================================
0. Introduction
======================================================================

There are many articles and code implementations for remote injection and hook on Linux, but there are not many hooks for Android platforms. After more than two weeks of research, I initially realized the interception of binder communication in System_server on the Android 2.3 platform with root privileges and wrote it down to share.

======================================================================
I. Dynamic LINK mechanism
======================================================================

First look at the dynamic linking mechanism on a Linux platform when a module a needs to invoke a function in another module B:

1, module A during compilation, the name of the module B to be referenced and the function name to write their own symbol table.
2, the runtime module A call, the calling process from the calling code to the PLT table to the Got table and then jump into the module B.

And how to ensure that the code of module A can jump from its plt/got to the correct module B entrance, this is what the linker does.
The standard Linux linker is ld.so, which supports lazy binding, that is, the original code of the module a called Module B generated during compilation, the process is from the calling code to the PLT table to the linker. Runtime first tuning module B, the first to enter the linker, the linker according to the call Information loading module B search its symbol and the found function address filled in the Got table, followed by the subsequent call flow directly to the Plt/got table. This mechanism reduces the overhead of loading, and is used for Linux distributions.

Android Although the kernel is based on Linux, but its dynamic link mechanism is not ld.so but comes with the linker, does not support lazy binding. In other words, if the above module is on the Android platform, when the module is loaded, linker will load module B and search for the required function address and pre-populate the Got table according to the contents of the. rel.plt table and the string table in module A. The calling process then goes straight to the Plt/got table, and no longer goes into the LINKER,PLT table and skips to the linker code, which is similar to "hard-working" bindings, but provides a bit of convenience for interception. If the module B has not yet been loaded and the address is not found when blocking the lazy bound entry, the interception will not be able to proceed.

To intercept the call of module A to B, the general idea is to inject and load a new interception module to module A by ptrace, and search the Got table of module A, find the call address of module B, change to a function address within the module, and then the function in the new module after its own processing, then jump to module B.

The different links between Android and Linux lead to differences in memory layout, and also lead to the online popular Linux injection and hook method is not feasible. The online method is to search the Pltgot area of the dynamic section by Ptrace injection, and go inside to fetch LINK_MAP to traverse the module loaded by this process to search for the function address that needs to be hook. But on Android, the Pltgot area of the section of dynamic is empty, there is no link_map this data structure, only through the analysis/proc/<pid>/maps to traverse the module.

======================================================================
Second, binder interception site
======================================================================

Binder is a lightweight cross-process communication mechanism on Andorid, composed of user-space libbinder.so and binder-driven collaboration of the kernel. The process of a complete binder call (Take the System_ An example of a service invocation in server is the libbinder.so from the user process to the user process loading to the IOCTL to the binder driver and blocking, the service side waits for the driver to receive the call request through libbinder.so, the data is completed after the Libbinde The r.so is then returned to the driver via the IOCTL, and the previously blocked ioctl of the client receives the reply and returns, returning to Libbinder.so and returning to the user process, thus completing a complete call request.

Note that the libbinder.so loaded on the libbinder.so and System_server side of the user process space here is logically not the same thing. Just because it's not the same thing, we can target system_. The libbinder.so, loaded in the server process, intercepts calls to the IOCTL in its got table, thus knowing in advance what the service is going to return (if you want to change it, you need to analyze the binder data and then change it). This IOCTL is where the interception is located.

======================================================================
Third, the concrete realization
======================================================================

----------------------------------------------------------------------
3.1 Realization Ideas
----------------------------------------------------------------------

After trying out various ideas and failing many times, the idea of finally identifying the binder communication in the System_server process is as follows:

1, run the injection program as root, stop and attach system_server through Ptrace.
2. Remotely inject shellcode, load the injected shared library and unbind it to invoke a specific function in the shared library.
3. This particular function writes the new function address of the replacement ioctl in the library and the real address of the IOCTL to the property of Android for external use.
4. The injection program obtains the original address of the IOCTL and the new function address of the IOCTL via the property of Android.
5, the injection program again through the Ptrace additional system_server, locate the libbinder.so in the section named. Got, and search its entry to find the original address of the IOCTL.
6. After locating the original address in the Got table, replace it with the new function address for the IOCTL.
7, remove the additional system_server let it run again, complete the interception.

Among them, 1 and 2 have a ready-made realization on the net, is a package called Libinject, which has inject.c/h and android.mk, there is a Daniel gives shellcode.s. However, this shellcode load shared library and call will immediately dlclose Uninstall, does not meet our resident needs, so I wrote a new shared library let Shellcode load shared library call, a step more. This library eventually resides in System_server memory.

----------------------------------------------------------------------
3.2 New function implementations in the injected shared library
----------------------------------------------------------------------

In this shared library of the resident System_server process, only a few simple functions are implemented, in which the Do_hook function is called by the outside world, and it does not do the specific hook action, Simply write the required two function addresses to the Android property for external use:

Code:
    //  writes old and new IOCTL addresses to Andorid's property for external use     int do_hook (void  * param)     {        old_ioctl =  ioctl;        printf ("ioctl addr: %p. new  Addr %p\n ",  ioctl, new_ioctl);         char value[ property_value_max] = {'};        snprintf (value,  property_value_max,  "%u",  ioctl);         property_set (PROP_ Old_ioctl_addr, value);         snprintf (value, PROPERTY_ value_max,  "%u",  new_ioctl);         property_set (PROP_NEW_ Ioctl_addr, value);        return 0;     }   &nbspThe;//  global variable is used to save the old IOCTL address, but it can also be directly ioctl    int  (*OLD_IOCTL)   (INT&NBSP;__FD, &NBSP;UNSIGNED&NBSP;LONG&NBSP;INT&NBSP;__REQUEST,&NBSP;VOID&NBSP;*&NBSP;ARG)  = 0;     //  to take over the new function address of the IOCTL, which internally calls the old ioctl    int new_ioctl  (INT&NBSP;__FD, &NBSP;UNSIGNED&NBSP;LONG&NBSP;INT&NBSP;__REQUEST,&NBSP;VOID&NBSP;*&NBSP;ARG)     {         if  ( __request == BINDER_WRITE_READ )          {             call_count++;            char value[ property_value_max] = {'};             snprintf (value, property_value_max,  "%d",  call_count);             property_set (Prop_ioctl_call_count, value);        }         int res =  (*OLD_IOCTL) (__fd, __request, arg );         return res;    }
New_ioctl function, determine whether the call parameter is a binder_write_read communication command, the words increase the count, and the count is written to the property, so that the outside world can see the call count, only to know that the interception succeeded.

----------------------------------------------------------------------
Implementation of the search mechanism of 3.3 injection program
----------------------------------------------------------------------

The process of injecting the program after the fourth step above is the core of this article. The program has to use elf.h under Linux because it involves elf parsing.

Since setting the property's Property_set is an asynchronous procedure, after calling the set-property function in the shared library, the inject program needs to have the loop wait attribute set, similar to the following:

Code:
Char Value[property_value_max] = {'} '};        Do {sleep (0);    Property_get (prop_old_ioctl_addr, Value, "0");    } while (strcmp (value, "0") = = 0); unsigned long old_ioctl_addr = atoi (value);
Then by calling Get_module_base, parse the/proc/<system_server pid>/maps file, get the libbinder.so load base (get_module_ Base is also available in the Libinject).

Code:
void * Binder_addr = Get_module_base (Target_pid, Binder_lib_path);
After getting the old and new IOCTL address and libbinder.so base, search for libbinder.so's got table for matches. Search libbinder.so can either search the contents of/system/lib/libbinder.so files, or through Ptrace peektext to search System_ The server is loaded in the memory of the libbinder.so image, I chose the former, because the latter seems to be a problem in the implementation, read the section content is not very correct, I am not sure whether it is the problem of the program or by linker to change.
Open the/system/lib/libbinder.so file to get its elf header.

Code:
Read (FD, EHDR, sizeof (ELF32_EHDR));
Get the address, number, and size of its section Header table, and get the index number of the string table sections:

Code:
unsigned long shdr_addr = ehdr->e_shoff;    int shnum = ehdr->e_shnum;    int shent_size = ehdr->e_shentsize; unsigned long stridx = ehdr->e_shstrndx;
Read the contents of the string table in advance to iterate over the section rather than its name:

Code:
Read the description of the string table in the section header to get its size and position Lseek (FD, SHDR_ADDR + stridx * shent_size, Seek_set);    Read (FD, SHDR, shent_size);    Allocates memory according to dimensions char * string_table = (char *) malloc (shdr->sh_size);    Lseek (FD, Shdr->sh_offset, Seek_set); Reads the contents of the string table into read (FD, string_table, shdr->sh_size);
Then re-traverse the section Header to find the section named. Got table:

Code:
    lseek (Fd, shdr_addr, seek_set);    int i;     for  ( i = 0; i < shnum; i++ )      {        read (fd, shdr, shent_size);         if  ( shdr->sh_type == SHT_PROGBITS )          {             int name_idx = shdr->sh_name;             if  ( strcmp (& (String_table[name_idx]),  ". Got")  == 0 )             {                 /*  is  GOT  watch!  */       &nbsP;        *out_addr = base_addr + shdr->sh_ Offset;                *out_ size = shdr->sh_size;                 return 0;             }        }    }
In this way, out_addr and Out_size are the location and length of the Got table in System_server libbinder.so.
Then the search and the hook are good:

Code:
    for  ( i = 0; i < out_size; i ++)      {        ptrace_readdata (TARGET_PID,&NBSP;OUT_ADDR,  &got_item, 4);        if  ( got_item ==  old_ioctl_addr )         {             /* !!!   Got the  ioctl  address  !!!   Change to our.  */            ptrace_writedata (Target_pid,  out_addr, &new_ioctl_addr, sizeof (NEW_IOCTL_ADDR));             break;        }         else if  ( got_item == new_ioctl_addr )         &nbsp {            /*  is already ours, do not repeat the hook.  */            break;         }        out_addr++;    }
Write Android.mk will be compiled under the Android 2.3 source tree, ADB push up (a total injection program, Shellcode used shared library, our shared library three files), run the injection program, prompting injection success. If you run again, the hint has been injected.
Run the command again:

# Getprop Persist.sys.ioctl.callcount
Getprop Persist.sys.ioctl.callcount
502

Random mobile phone, the number is increasing, but a bit of impact on performance.

======================================================================
Iv. Additional Information
======================================================================

1, each loaded module, whether executable program or shared library, has its own independent plt and got table. So intercept the external invocation of this module got, does not affect other modules.
2, this article only realizes the interception module's recall to other module's action, the other module's call-in does not involve (may also involve the more complex relocation operation).
3, System_server is the system user, not have the authority to write all the names of the property, here with Persist.sys. The name of the attribute that begins with the Persist.sys. The properties that begin with are saved to disk, so performance will be almost.
4, the IOCTL although the substantive declaration is a variable parameter: int new_ioctl (int __fd, unsigned long int __request,/*void * arg*/...), the function of this declaration is to directly and transparently pass parameters from the old function to the new function like It is not feasible, searched a lot of information also did not find. Fortunately, the search for a libbinder.so source, inside the parameters of the IOCTL call is three, simply do not deal with the variable-length form.
5. If you do not run the injection program as root, the ptrace will fail when attached.
6, most of the Andriod system service is running in the system_server process, can intercept. However, some custom user service in the user process, such as need to intercept, you have to ptrace to that user process, the interception method is similar.
7, as for the interception of Binder data analysis and modification, it is the content of the next article.

======================================================================
V. References
======================================================================

Http://wenku.baidu.com/view/222f348f84868762caaed558.html
http://blog.csdn.net/lingfong_cool/article/details/7976112
http://blog.csdn.net/ylyuanlu/article/details/6638825
Http://os.pku.edu.cn:8080/gaikuang/submission/TN05.ELF.Format.Summary.pdf
http://blog.csdn.net/fengkehuan/article/details/6223406
http://blog.csdn.net/innost/article/details/6124685

There are many, unable to write all down, here to all selfless sharing their research results of the people to extend my sincere thanks.

The use of root in Android to implement the System_server in the binder of the IOCTL call interception

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.