Improve the Security of Android* Applications Using Hooking Techniques: Part 2

Download PDF [PDF 1.2 MB]

Contents


Study of the PIC Code in libtest_PIC.so

If the object is compiled in PIC mode, relocation is implemented differently. By observing the sections information of the libtest_PIC.so which is shown in Figure 17, the printf() relocation information is located in two relocation sections: .rel.dyn and .rel.plt. Two new relocation types R_386_GLOB_DAT and R_386_JMP_SLOT are used, and the absolute 32-bit address of the substituted function should be filled in with these offset addresses.

 Relocation section of libtest_PIC.so
Figure 1:Relocation section of libtest_PIC.so

The Figure 18 shows the assembly code of function libtest2() which is compiled in non-PIC mode. The entry addresses of printf() marked with red color are specified in the relocation sections .rel.dyn and .rel.plt in Figure 17.

 Disassemble code of libtest2(), compiled with -PIC parameter
Figure 2:Disassemble code of libtest2(), compiled with -PIC parameter

 Working flow of 'printf("libtest2: 1st call to the original printf()\n");'
Figure 3:Working flow of 'printf("libtest2: 1st call to the original printf()\n");'

 Working flow of 'global_printf2("libtest2: global_printf2()\n");'
Figure 4:Working flow of 'global_printf2("libtest2: global_printf2()\n");'

 Working flow of 'local_printf("libtest2: local_printf()\n");'
Figure 5:Working flow of 'local_printf("libtest2: local_printf()\n");'

From Figures 19-21, it can be seen that when working with the dynamic library generated with the -PIC parameter, the code in libtest2() will jump to the address placed in offset addresses 0x1fe0, 0x2010, and 0x2000, which are the entrances to printf().

Hook Solution

If the hook module wants to intercept the calls to printf() and redirect to another function, it should write the redirected function address to the offset addresses of the symbol ‘printf’ defined in the relocation sections, after the linker loaded the dynamic library into memory.

To replace the call of the printf() function with the call of the redirected hooked_printf() function, as shown in the software flow diagram in Figure 22, a hook function should be implemented between the dlopen() and libtest() calls. The hook function will first get the offset address of symbol printf, which is 0x1fe0 from the relocation section named .rel.dyn. The hook function then writes the absolute address of hooked_printf() function to the offset address. After that, when the code in libtest2() calls into the printf(), it will enter the hooked_printf() instead.

 Example of how the hook function intercepts the call to printf() and reroutes the call to hooked_printf(). The original function calling process is described in Figure 21.
Figure 6:Example of how the hook function intercepts the call to printf() and reroutes the call to hooked_printf(). The original function calling process is described in Figure 21.

To consider all the possible cases previously listed, the entire flow chart of the hook function is shown in Figure 23. And the part of the change in main() function is depicted in Figure 24.

 The flow chart of ELF hook module
Figure 7: The flow chart of ELF hook module

 Code in main() after hooking
Figure 8:Code in main() after hooking

The output of the program is shown in Figure 25, you can see that when the first call to libtest1()/libtest2() executes, the printf() is called inside the functions. When calling the two functions again, after the hook functions are executed, the calls to the printf() are redirected to the hooked_printf() function. The hooked_printf() function will attach the string “is HOOKED” at the end of the normal printed string. Figure 26 shows the program running flow after hooking, compare with the original flow shown in Figure 8, the hooked_printf() has been injected into libtest1() and libtest2().

 Output of the test program, printf() has been hooked
Figure 9:Output of the test program, printf() has been hooked

 The running flow of the test project after hooking
Figure 10:The running flow of the test project after hooking

Case Study – a Hook-Based Protection Scheme in Android

Based on the studies of the hooking technique in the previous sections, we developed a plug-in to help Android application developers improve the security of their applications. Developers need to add only one Android native library to their projects and add one line of Java code to load this native library at start-up time. Then this library injects some protection code to other third-party libraries in the application. The protection code will aid encrypting the local file's input/output stream, as well as bypass the function __android_log_print() to avoid some user privacy leakage by printing debugging information through Logcat.

To verify the effectiveness of the protection plug-in, we wrote an Android application to simulate a scene of an application that contains a third-party library. In the test application, the third party library does two things:

  1. When an external Java instruction calls the functions in the library, it will print some information by calling __android_log_print().
  2. In the library, the code creates a file (/sdcard/data.dat) to save data in local storage without encryption, then reads it back and prints it on the screen. This action is to simulate the application trying to save some sensitive information in the local file system.

Figures 27-30 compare the screenshots of the test program, output of Logcat, and the content of the saving file in the device’s local file system before and after hooking.

 The Android* platform is Teclast X89HD, Android 4.2.2
Figure 11:The Android* platform is Teclast X89HD, Android 4.2.2

 App output - no change after hooking
Figure 12:App output - no change after hooking

 Logcat output - empty after hooking
Figure 13:Logcat output - empty after hooking

 Local file ‘data.dat’ at /sdcard has been encrypted after hooking
Figure 14:Local file ‘data.dat’ at /sdcard has been encrypted after hooking

As the figures show, the running flow of the program after hooking is the same as the one without hooking. However, the Logcat cannot catch any output from the native library after hooking. Further, the content of the local file is no longer stored in a plain text format.

The plug-in helps the test application improve security against malicious attacks on collecting information via Logcat, as well as offline attacks to the local file system.

Conclusion

The hooking technique can be used in many development scenarios, providing seamless security protection to Android applications. Hook-based protection schemes can not only be used on Android, but also can be expanded to other operating systems such as Windows*, Embedded Linux, or other operating systems designed for Internet of Things (IoT) devices. It can significantly reduce the development cycle as well as maintenance costs. Developers can develop their own hook-based security scheme or use the professional third-party security solutions available on the market.

References

Redirecting functions in shared ELF libraries
Apriorit Inc, Anthony Shoumikhin, 25 Jul 2013
http://www.codeproject.com/Articles/70302/Redirecting-functions-in-shared-ELF-libraries

x86 API Hooking Demystified
Jurriaan Bremer
http://jbremer.org/x86-api-hooking-demystified/

Android developer guide
http://developer.android.com/index.html

Android Open Source Project
https://source.android.com/

About the Author

Jianjun Gu is a senior application engineer in the Intel Software and Solutions Group (SSG), Developer Relations Division, Mobile Enterprise Enabling team. He focuses on the security and manageability of enterprise application.

For more complete information about compiler optimizations, see our Optimization Notice.