Successfully reported this slideshow.
Your SlideShare is downloading. ×

Native hook mechanism in Android Bionic linker

Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad
Ad

Check these out next

1 of 90 Ad

Native hook mechanism in Android Bionic linker

Download to read offline

It is an introduction to the Native Hook mechanism, which is a function hook mechanism in Android. It is implemented in Bionic linker, which is the dynamic linker in Android. With Native Hook mechanism enabled, you can hook any native function with another one, without any modification to the existing libraries.

It is an introduction to the Native Hook mechanism, which is a function hook mechanism in Android. It is implemented in Bionic linker, which is the dynamic linker in Android. With Native Hook mechanism enabled, you can hook any native function with another one, without any modification to the existing libraries.

Advertisement
Advertisement

More Related Content

Slideshows for you (20)

Advertisement

Similar to Native hook mechanism in Android Bionic linker (20)

Recently uploaded (20)

Advertisement

Native hook mechanism in Android Bionic linker

  1. 1. Android Dynamic Framework : Native Hook Mechanism in Bionic Linker Mai-Hsuan Chia Shih-Wei Liao Department of Computer Science and Information Engineering National Taiwan University
  2. 2. Outline ● Background ● Motivation ● Native Hook Mechanism ● Experiment ● Applications ● Future works ● Conclusion
  3. 3. Background ● JNI ● Android Dynamic Framework ● Bionic
  4. 4. JNI ● Enable Java code can call or can be called by native applications
  5. 5. JNI Java method JNI Native functionC/C++ Java
  6. 6. Java calls native class HelloWorld { private native void print(); // print() is native function public static void main(String[] args) { new HelloWorld().print(); } static { System.loadLibrary("hello"); // This loads libhello.so } }
  7. 7. ● A framework which is able to dynamically replace Java methods in ART Runtime without modifying APKs. Android Dynamic Framework
  8. 8. Android Dynamic Framework Class A Class B HookTable ... class linker Method A1 Method A2 Method B1 Method B2
  9. 9. Android Dynamic Framework Class A Class B HookTable ... class linker Method A1 Method A2 Method B1 Method B2 0. Do linking
  10. 10. Android Dynamic Framework Class A Class B HookTable ... class linker Method A1 Method A2 Method B1 Method B2 1. Query HookTable
  11. 11. Android Dynamic Framework Class A Class B HookTable ... class linker Method A1 Method A2 Method B1 Method B2 Replace ClassA::A1 with ClassB::B1 1. Query HookTable
  12. 12. Android Dynamic Framework Class A Class B HookTable ... class linker Method A2 Method B1 Method B2 Method B1 2. Do method hooking
  13. 13. ● C library in Android ● Forked from BSDs rather than from GNU/Linux ○ To avoid license problems ● Smaller ● Faster Bionic
  14. 14. ● Components ○ libc ○ libm ○ libdl (written from scratch) ○ dynamic linker ■ /system/bin/linker (written from scratch) Bionic
  15. 15. Motivation ● Only Java methods can be replaced in Android Dynamic Framework
  16. 16. Class A Method A2 Method B1 Class B Method B1 Method B2 JNI libd.so Func D1 Func D2 libe.so Func E1 Func E2 (1) method hook Method A3 libc.so Func C1 Func C2 native call hooking path
  17. 17. Class A Method A2 Method B1 Class B Method B1 Method B2 JNI libd.so Func D1 Func D2 libe.so Func E1 Func E2 (1) method hook Method A3 libc.so Func C1 Func C2 native call hooking path
  18. 18. Class A Method A2 Method B1 Class B Method B1 Method B2 JNI libd.so Func D1 Func D2 libe.so Func E1 Func E2 (1) method hook Method A3 libc.so Func D1 Func C2 native call hooking path (2) dlopen native hook (1) method hook
  19. 19. Class A Method A2 Method B1 Class B Method B1 Method B2 JNI libd.so Func D1 Func D2 libe.so Func E1 Func E2 (1) method hook Method A3 libc.so Func D1 Func C2 native call hooking path (2) dlopen native hook (1) method hook (2) dlopen native hook
  20. 20. Class A Method A2 Method B1 Class B Method B1 Method B2 JNI libd.so Func D1 Func E2 libe.so Func E1 Func E2 (1) method hook Method A3 libc.so Func D1 Func C2 native call hooking path (2) dlopen native hook (1) method hook (2) dlopen native hook (3) native to native hook
  21. 21. Motivation ● (1) method hook can be done in the existing Android Dynamic Framework ● However, (2) dlopen native hook and (3) native to native hook cannot not be done.
  22. 22. Motivation ● Native hook mechanism can do both (2) dlopen native hook and (3) native to native hook
  23. 23. Motivation With Native hook mechanism integrated, Android Dynamic Framework can be more complete and powerful
  24. 24. Native hook mechanism ● Implemented in Bionic Linker
  25. 25. Review ● How Bionic Linker loads an executable ● Dynamic linking flow ● Dynamic loading flow
  26. 26. How Bionic Linker loads an executable
  27. 27. OS creates a process image ● Based on the interpreter’s segments. high low Memory space /system/bin/linker linker
  28. 28. Linker links itself ● __linker_init() high low Memory space /system/bin/linker
  29. 29. Load the executable ● __linker_init_post_relocation() /system/bin/linker high low Memory space exe executable
  30. 30. Get needed libraries names ● __linker_init_post_relocation() /system/bin/linker high low Memory space executable exe .dynamic DT_NEEDED ptr_to_liba1.so_name DT_NEEDED ptr_to_liba2.so_name … DT_NULL
  31. 31. Get needed libraries names ● __linker_init_post_relocation() /system/bin/linker high low Memory space executable exe DT_NEEDED ptr_to_liba1.so_name DT_NEEDED ptr_to_liba2.so_name … DT_NULL .dynamic char needed_libraries_names[] = { “liba1.so”, “liba2.so” }
  32. 32. Load needed libraries ● find_libaries(exe, needed_libraries_names) ○ step 1 : load libraries and build dependencies tree
  33. 33. Load needed libraries ● find_libaries(exe, needed_libraries_names) ○ step 1 : load libraries and build dependencies tree exe liba1.so liba2.so Loaded Not Loaded p.s.
  34. 34. Load needed libraries ● find_libaries(exe, needed_libraries_names) ○ step 1 : load libraries and build dependencies tree exe liba1.so liba2.so Loaded Not Loaded p.s.
  35. 35. Load needed libraries ● find_libaries(exe, needed_libraries_names) ○ step 1 : load libraries and build dependencies tree exe liba1.so liba2.so libb1.so libb2.so libb3.so libb4.so ... ... Loaded Not Loaded p.s.
  36. 36. Load needed libraries ● find_libaries(exe, needed_libraries_names) ○ step 1 : load libraries and build dependencies tree exe liba1.so liba2.so libb2.so libb3.so libb4.so ... ... Loaded Not Loaded p.s. libb1.so
  37. 37. Load needed libraries ● find_libaries(exe, needed_libraries_names) ○ step 1 : load libraries and build dependencies tree exe liba1.so liba2.so libb2.so libb3.so libb4.so ... ... ... ... ... ... ... Loaded Not Loaded p.s. libb1.so
  38. 38. Load needed libraries liba1.soexe liba2.so libb1.so libb2.so ... ● find_libaries(exe, needed_libraries_names) ○ step 2 : turn dependencies tree into libraries_list in Breadth First Search(BFS) order libraries_list dependencies tree
  39. 39. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries foreach lib in libraries_list { foreach rel in lib.dynamic_relocation_table { symbol = rel.sym; soinfo_do_lookup(symbol, lib, libraries_list); } }
  40. 40. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries liba1.soexe liba2.so libb1.so libb2.so ... libraries_list exe sym_1 sym_n ... if sym_1 is defined in lib: sym_1 = lib.find(sym) else: lib = lib->next;
  41. 41. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries liba1.soexe liba2.so libb1.so libb2.so ... libraries_list exe sym_1 sym_n ... if sym_1 is defined in lib: sym_1 = lib.find(sym) else: lib = lib->next; NOT FOUND
  42. 42. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries liba1.soexe liba2.so libb1.so libb2.so ... libraries_list exe sym_1 sym_n ... if sym_1 is defined in lib: sym_1 = lib.find(sym) else: lib = lib->next; NOT FOUND
  43. 43. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries liba1.soexe liba2.so libb1.so libb2.so ... libraries_list exe sym_1 sym_n ... if sym_1 is defined in lib: sym_1 = lib.find(sym) else: lib = lib->next; FOUND ok
  44. 44. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries liba1.soexe liba2.so libb1.so libb2.so ... libraries_list exe sym_1 sym_n ... if sym_k is defined in lib: sym_k = lib.find(sym) else: lib = lib->next; ok
  45. 45. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries liba1.soexe liba2.so libb1.so libb2.so ... libraries_list exe sym_1 sym_n ... ok ok
  46. 46. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries liba1.soexe liba2.so libb1.so libb2.so ... libraries_list liba1.so sym_1 sym_n ... if sym_1 is defined in lib: sym_1 = lib.find(sym) else: lib = lib->next;
  47. 47. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries liba1.soexe liba2.so libb1.so libb2.so ... libraries_list ... sym_1 sym_n ... if sym_1 is defined in lib: sym_1 = lib.find(sym) else: lib = lib->next;
  48. 48. Link the application and all libraries ● find_libaries(exe, needed_libraries_names) ○ step 3 : relocate all to-be-relocated symbols in the application and libraries liba1.soexe liba2.so libb1.so libb2.so ... libraries_list ... sym_1 sym_n ... if sym_1 is defined in lib: sym_1 = lib.find(sym) else: lib = lib->next; It is DONE until all libraries are linked
  49. 49. Jump to the application’s entry /system/bin/linker high low Memory space executable liba1.so liba2.so libb1.so ... ● jump to executable’s _start.
  50. 50. The executable is loaded successfully ● And start to execute /system/bin/linker high low Memory space liba1.so liba2.so libb1.so ... .text section _start: …. …. executable
  51. 51. Bionic linker linking & loading flow ● Dynamic linking flow ● Dynamic loading flow
  52. 52. __linker_init_post_relocation Dynamic linking dlopen_ext do_dlopen find_library find_libraries find_library_internal load_library Dynamic loading ... load all libraries … relocate all symbols
  53. 53. Native hook mechanism Modified codes are mainly in two parts ● Load hooking libraries in find_libraries() ○ Init native_hook_table ○ Look up native_hook_table ○ Load hooking_library ● Replace hooked_symbol with hooking_symbol in soinfo_do_lookup() ○ Look up native_hook_table ○ Replace every hooked_symbol in hooked_library with hooking_symbol in hooking_library
  54. 54. Native hook file format in /system/nh_file.txt < hooked_lib_name:hooked_symbol:hooking_lib_name:hooking_symbol >
  55. 55. System flow hooking lib nh_file ROM /system/bin/linker __linker_init_post_relocation find_libraries init native_hook_table look up native hook table soinfo_do_lookup look up native hook table replace hooked symbol with hooking symbol New Process load hooking library
  56. 56. Load hooking libraries linkerexe liba1.so liba2.so Loaded Not Loaded p.s. liba1.so:hi:libhooking.so:ha ... Native Hook Table
  57. 57. Load hooking libraries linkerexe liba1.so liba2.so Loaded Not Loaded p.s. liba1.so:hi:libhooking.so:ha ... Native Hook Table 0. load liba1.so
  58. 58. Load hooking libraries linkerexe liba1.so liba2.so Loaded Not Loaded p.s. liba1.so:hi:libhooking.so:ha ... Native Hook Table 1. look up the native hook table HOOKED LIB “liba1.so” FOUND
  59. 59. Load hooking libraries linkerexe liba1.so liba2.so Loaded Not Loaded p.s. liba1.so:hi:libhooking.so:ha ... Native Hook Table 2. load libhooking.so libhooking. so
  60. 60. Replace hooked_symbol with hooking_symbol liba1.soexe liba2.so libhooking.s o libraries_list liba1.so:hi:libhooking.so:ha ... Native Hook Table exe hi hi ha linker 0. relocate symbol
  61. 61. Replace hooked_symbol with hooking_symbol liba1.soexe liba2.so libhooking.s o libraries_list liba1.so:hi:libhooking.so:ha ... Native Hook Table exe hi ha linker NOT FOUND hi
  62. 62. Replace hooked_symbol with hooking_symbol liba1.soexe liba2.so libhooking.s o libraries_list liba1.so:hi:libhooking.so:ha ... Native Hook Table exe hi ha linker FOUND hi
  63. 63. Replace hooked_symbol with hooking_symbol liba1.soexe liba2.so libhooking.s o libraries_list liba1.so:hi:libhooking.so:ha ... Native Hook Table exe hi ha linker FOUND hi 1. look up native hook table liba1.so:hi is to be hooked
  64. 64. Replace hooked_symbol with hooking_symbol liba1.soexe liba2.so libhooking.s o libraries_list liba1.so:hi:libhooking.so:ha ... Native Hook Table exe hi ha linker hi 1. look up native hook table 2. find libhooking.so:ha
  65. 65. Replace hooked_symbol with hooking_symbol liba1.soexe liba2.so libhooking.s o libraries_list liba1.so:hi:libhooking.so:ha ... Native Hook Table exe hi ha linker ha 3. relocate hooked_symbol “hi” with the hooking_symbol “ha”
  66. 66. // in libnativehook.so #include “native_hook.h” void* find_lib_symbol(char* lib_name, char* symbol) { // Using dl_iterate_phdr() to get the symbol’s address // in the loaded library whose name is lib_name. … return ptr_to_symbol; } Before/After hook SDK
  67. 67. How find_lib_symbol() works ? With the following facts, we can get the hooked_symbol in hooked_library with dl_iterate_phdr(callback, void* data) ● hooked_lib is loaded in the memory ● dl_iterate_phdr()iterates all loaded libraries in the process, and get each library’s program header and base address. ● With library’s program header, we can get .dynamic segment, and therefore we get .dynstr and .dynsym section ● With .dynsym and .dynstr, we can find the offset of hooked_symbol in hooked_lib. ● hooked_symbol_addr = base address + offset
  68. 68. // in libmine.so #include “native_hook.h” double my_sin(double x) { char hooked_lib[] = "/system/lib/libm.so"; char hooked_symbol[] = "sin"; double (*hooked_sin)(double) = find_lib_symbol(hooked_lib, hooked_symbol); /* before hook : you can do something before calling hooked_func */ double result = hooked_sin(x); /* after hook : you can do something after calling hooked_func */ result += 5566; return result; } After hook example
  69. 69. After hook example // in main.c #include <math.h> #include <stdio.h> #define PI 3.14159265 int main(void) { double angle = 30.0; double result = sin((angle * PI) / 180); printf(“sin(%lf) = %lfn”, angle, result); return 0; } libm.so:sin:libmine.so:my_sin ... Native Hook Table $ ./main sin(30.000000) = 5566.500000
  70. 70. double my_sin(double x) { char hooked_lib[] = "/system/lib/libm.so"; char hooked_symbol[] = "sin"; static void* cache_ptr = NULL; double (*hooked_sin)(double) = NULL; if (cache_ptr) { hooked_sin = cache_ptr; } else { hooked_sin = find_lib_symbol(hooked_lib, hooked_symbol); } if (hooked_sin) { cache_ptr = (void*)hooked_sin; } double result = hooked_sin(x); result += 5566; return result; } Before/After hook with cache
  71. 71. Experiment 1,000 100,000 1,000,000 10,000,000 Baseline 0.10 0.14 0.52 4.07 Normal hook 0.20 0.23 0.60 4.15 Before/After hook without cache 0.25 1.9 17.12 169.03 Before/After hook with cache 0.22 0.24 0.69 4.77 iterations
  72. 72. Experiment 169.03
  73. 73. Applications ● Profiling ● Boosting apps performance ● Security sandbox
  74. 74. Profiling Target function Before hook After hook ● Input Distribution Analysis ● Function call Analysis ● Output Analysis
  75. 75. ● Hook functions that affect the performance of applications in Android ● Scenario ○ Functions in libm.so are not good enough for some special purpose, we can hook the function with the optimized one. Boosting apps performance
  76. 76. libm_opt.so optimized_sin: ... libbenchmark.so getScore: … call <sin> App libm.so sin: ... JNI
  77. 77. libm_opt.so optimized_sin: ... libbenchmark.so getScore: … call <sin> App libm.so sin: ... JNI Replace ‘sin’ with ‘optimized_sin’
  78. 78. Security sandbox ● Use “before hook” to hook the open()in libc ● Examine the filename and other parameters in advance ○ If the to-be-written file is a critical file, we let the app open another file to write without consciousness.
  79. 79. Security sandbox f = open(“/data/critical.txt”, ‘w’); ... modifying critical.txt ... ... App Sandbox
  80. 80. Security sandbox f = open(“/data/critical.txt”, ‘w’); ... modifying critical.txt ... ... App Sandbox /data/critical.txt should not be modified.
  81. 81. Security sandbox f = open(“/data/critical.txt”, ‘w’); ... modifying critical.txt ... ... App Sandbox f = open(“/data/another.txt”, ‘w’); In the sandbox, app is deceived to write to “/data/another.txt” instead of “/data/critical.txt”.
  82. 82. Security sandbox App Sandbox f = open(“/data/another.txt”, ‘w’); f = open(“/data/another.txt”, ‘w’); ... modifying another.txt ... ...
  83. 83. ● Provide more easy-to-use API for Native Hook in Android ○ Native Hook SDK Future works
  84. 84. ● Completely integrate Native Hook into Android Dynamic Framework ○ Provide hooking between Java method and native functions. Future works Integrated Hook Table liba.so:funca:libb.so:funcb # hook native to native classA:methoda:classB:methodb # hook java to java classA:methoda:libb.so:funcb # hook java to native libb.so:funcb:classA:methoda # hook native to java ...
  85. 85. Conclusion ● Native Hook mechanism is a strong and useful framework in Android allowing developers to replace native functions at runtime without modifying the existing functions. ● Native Hook is more powerful than Java method hook mechanisms because it is implemented in Bionic Linker. ● With Before/After hook mechanism, you can do whatever you want before/after any existing function. ● With Native Hook enabled, it suffers only little overhead to load nh_file and hooking libraries.
  86. 86. Q & A
  87. 87. Thank you for your listening
  88. 88. Backup slides
  89. 89. void* find_lib_symbol(char* lib_name, char* symbol) { // Using dl_iterate_phdr() to get the symbol’s address // in the loaded library whose name is lib_name. static void* unordered_map<std::string, void*> cache = nullptr; std::string lib_symbol = std::string(lib_name) + symbol; if (cache) { unordered_map<std::string, void*>::iterator it = cache.find(lib_symbol); if (it != cache.end()) { return it->second; } } … // find ptr_to_symbol if (ptr_to_symbol) { cache[lib_symbol] = ptr_to_symbol; } return ptr_to_symbol; } Before/After hook with cache in find_lib_symbol
  90. 90. Replace hooked_symbol with hooking_symbol liba1.soexe liba2.so libhooking.s o libraries_list liba1.so:hi:libhooking.so:ha ... Native Hook Table exe hi ha linker FOUND hi 1. look up native hook table liba1.so:hi is to be hooked 2. find libhooking.so:ha

×