# arminject **Repository Path**: Fang3s/arminject ## Basic Information - **Project Name**: arminject - **Description**: An application to dynamically inject a shared object into a running process on ARM architectures. mirror from https://github.com/evilsocket/arminject - **Primary Language**: Android - **License**: BSD-3-Clause - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2015-11-04 - **Last Updated**: 2024-06-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ARM Inject === An application to dynamically inject a shared object into a running process on ARM architectures and hook API calls. Read more on: - [Dynamically inject a shared library into a running process on Android/ARM](http://www.evilsocket.net/2015/05/01/dynamically-inject-a-shared-library-into-a-running-process-on-androidarm/). - [Android Native API Hooking with Library Injection and ELF Introspection](http://www.evilsocket.net/2015/05/04/android-native-api-hooking-with-library-injecto/) ## How to Test In order to test this, you'll need the Android NDK installed and a device connected to your USB port, then simply run: make test This will launch a new Chrome browser instance and inject libhook into it. Once injected the library will hook the **open** function and print every call to it to the logcat. @ Pushing files to /data/local/tmp ... @ Starting com.android.chrome/com.google.android.apps.chrome.Main ... @ Injection into PID 18233 starting ... I/LIBHOOK (18233): LIBRARY LOADED FROM PID 18233. I/LIBHOOK (18233): Found 104 loaded modules. I/LIBHOOK (18233): Installing 12 hooks. I/LIBHOOK (18233): [0xA0861000] Hooking /data/app/com.android.chrome-2/lib/arm/libchrome.so ... I/LIBHOOK (18233): [0xA0A68000] Hooking /data/app/com.android.chrome-2/lib/arm/libchrome.so ... I/LIBHOOK (18233): [0xAB8A9000] Hooking /system/vendor/lib/egl/libGLESv2_adreno.so ... I/LIBHOOK (18233): [0xAB9EC000] Hooking /system/vendor/lib/egl/libGLESv1_CM_adreno.so ... I/LIBHOOK (18233): [0xABA20000] Hooking /system/vendor/lib/libgsl.so ... I/LIBHOOK (18233): open - 0xb6f31951 -> 0xa446577c I/LIBHOOK (18233): write - 0xb6f55ec8 -> 0xa4464d5c I/LIBHOOK (18233): read - 0xb6f56964 -> 0xa4464c70 I/LIBHOOK (18233): close - 0xb6f552e8 -> 0xa4464e54 I/LIBHOOK (18233): connect - 0xb6f30365 -> 0xa44657fc I/LIBHOOK (18233): sendto - 0xb6f562a0 -> 0xa4465020 I/LIBHOOK (18233): recvfrom - 0xb6f5679c -> 0xa4465318 I/LIBHOOK (18233): shutdown - 0xb6f566ac -> 0xa4465518 I/LIBHOOK (18233): send - 0xb6f33851 -> 0xa4464f28 I/LIBHOOK (18233): recvmsg - 0xb6f560c0 -> 0xa446542c I/LIBHOOK (18233): sendmsg - 0xb6f55de0 -> 0xa4465134 ... ... I/LIBHOOK (18233): [18233] open('/dev/ashmem', 2) -> 18 I/LIBHOOK (18233): [18233] close( '/dev/ashmem' ) -> 0 I/LIBHOOK (18233): [18233] open('/dev/ashmem', 2) -> 18 I/LIBHOOK (18233): [18233] close( '/dev/ashmem' ) -> 0 I/LIBHOOK (18233): [18233] open('/data/data/com.android.chrome/shared_prefs/com.google.android.apps.chrome.omaha.xml', 0) -> 18 I/LIBHOOK (18233): [18233] open('/dev/ashmem', 2) -> 19 I/LIBHOOK (18233): [18233] close( '/dev/ashmem' ) -> 0 I/LIBHOOK (18233): [18233] open('/dev/ashmem', 2) -> 19 I/LIBHOOK (18233): [18233] close( '/dev/ashmem' ) -> 0 I/LIBHOOK (18233): [18233] read( '/data/data/com.android.chrome/shared_prefs/com.google.android.apps.chrome.omaha.xml', 0xb007c00c, 16384 ) -> 655 I/LIBHOOK (18233): [18233] close( '/data/data/com.android.chrome/shared_prefs/com.google.android.apps.chrome.omaha.xml' ) -> 0 I/LIBHOOK (18233): [18233] write( 'pipe:[4020814]', W, 1, 2147483647 ) -> 1 I/LIBHOOK (18233): [18233] write( '(14)', 18306, 5, -1601827487 ) -> 5 I/LIBHOOK (18233): [18233] open('/dev/ashmem', 2) -> 22 I/LIBHOOK (18233): [18233] close( '/dev/ashmem' ) -> 0 I/LIBHOOK (18233): [18233] open('/dev/ashmem', 2) -> 22 I/LIBHOOK (18233): [18233] close( '/dev/ashmem' ) -> 0 I/LIBHOOK (18233): [18233] close( '(22)' ) -> 0 I/LIBHOOK (18233): [18233] read( '(18)', 0xa0860b6c, 16 ) -> 1 I/LIBHOOK (18233): [18233] close( '(24)' ) -> 0 I/LIBHOOK (18233): [18233] close( '(22)' ) -> 0 I/LIBHOOK (18233): [18233] open('/dev/ashmem', 2) -> 22 I/LIBHOOK (18233): [18233] recvfrom( 'socket:[4043146]', nysv, 2400, 64, 0x0, 0 ) -> 24 I/LIBHOOK (18233): [18233] recvfrom( 'socket:[4043146]', nysv, 2400, 64, 0x0, 0 ) -> -1 I/LIBHOOK (18233): [18233] read( '(18)', 0xa0860b6c, 16 ) -> 1 I/LIBHOOK (18233): [18233] write( 'pipe:[4020814]', W, 1, 2147483647 ) -> 1 I/LIBHOOK (18233): [18233] recvfrom( 'socket:[4043184]', , 2264, 64, 0x0, 0 ) -> -1 I/LIBHOOK (18233): [18233] write( 'pipe:[4043980]', W, 1, -1 ) -> 1 ... ... @ CTRL+C detected, killing process ... ## Note Most of the ELF manipulation code inside the file hook.cpp of libhook was taken from the **Andrey Petrov**'s blog post "[Android hacking: hooking system functions used by Dalvik](http://shadowwhowalks.blogspot.it/2013/01/android-hacking-hooking-system.html )" and fixed by me ( the original source code didn't work due to page align, memory protection, etc ). ## License Released under the BSD license. Copyright © 2015, Simone Margaritelli All rights reserved. ##Articles - [Android hacking: hooking system functions used by Dalvik](http://shadowwhowalks.blogspot.it/2013/01/android-hacking-hooking-system.html ) _random hacks and fun things I want to share Thursday, January 31, 2013_ **Android hacking: hooking system functions used by Dalvik** Let's say you need to alter behavior of some Android app you do not have source code for. For example, you want to redirect all socket connect()s made to badhost.com to myhost.com or perhaps even 127.0.0.1. How this can be achieved (without having to root the device and recompile system libs) ? On normal Linux systems one can easily use dynamic linker LD_PRELOAD environment variable to let ld change symbol resolution order and thus inject any code you want. However, on Android it is not possible for following reasons: * LD_PRELOAD is not useful because zygote already forked JVM * Once an app is running, JVM is already started * It not possible to (easily) modify environment variables for JVM invokation So, how do we do it? By patching PLT (.rel.plt) (procedure linkage tables) of a running process in-flight! I will not be going into describing how PLT works and details on ELF binary format. It is suffice to say that compiled code which imports symbols from other .so-s needs to figure out where these functions are located in memory. Since libraries can be loaded at a different place in memory for different processes, it is impossible to know imported functions addresses at compile time. Detailed description can be found here. However, Android's Bionic library is different, and code specific to glib/gcc dynamic linker won't work. Fortunately, it is easily possible to access ld's internal structures and fish out relevant data. Consider signature of regular POSIX dlopen call: ```c void *dlopen(const char *filename, int flag); ``` Interestingly, this gives us void* abstract 'handle'. In practice, this handle is pointer to a struct soinfo which contains all the information we ever need to override PLT tables. From linker/linker.cpp: ```c soinfo* do_dlopen(const char* name, int flags) ``` Now, all we need to is to simply re-dlopen() shared library we want (in my case it libandroid_runtime.so) and walk through plt table, patch connect() method to our own. Here is the complete hooking code: ```c /* Copyright (C) 2013 Andrey Petrov Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #define ANDROID_ARM_LINKER #include #include #include #include #include #include #include #include #include #include #include #include "linker.h" // get it from bionic static unsigned elfhash(const char *_name) { const unsigned char *name = (const unsigned char *) _name; unsigned h = 0, g; while(*name) { h = (h << 4) + *name++; g = h & 0xf0000000; h ^= g; h ^= g >> 24; } return h; } static Elf32_Sym *soinfo_elf_lookup(soinfo *si, unsigned hash, const char *name) { Elf32_Sym *s; Elf32_Sym *symtab = si->symtab; const char *strtab = si->strtab; unsigned n; n = hash % si->nbucket; for(n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]){ s = symtab + n; if(strcmp(strtab + s->st_name, name)) continue; return s; } return NULL; } int hook_call(char *soname, char *symbol, unsigned newval) { soinfo *si = NULL; Elf32_Rel *rel = NULL; Elf32_Sym *s = NULL; unsigned int sym_offset = 0; if (!soname || !symbol || !newval) return 0; si = (soinfo*) dlopen(soname, 0); if (!si) return 0; s = soinfo_elf_lookup(si, elfhash(symbol), symbol); if (!s) return 0; sym_offset = s - si->symtab; rel = si->plt_rel; /* walk through reloc table, find symbol index matching one we've got */ for (int i = 0; i < si->plt_rel_count; i++, rel++) { unsigned type = ELF32_R_TYPE(rel->r_info); unsigned sym = ELF32_R_SYM(rel->r_info); unsigned reloc = (unsigned)(rel->r_offset + si->base); unsigned oldval = 0; if (sym_offset == sym) { switch(type) { case R_ARM_JUMP_SLOT: /* we do not have to read original value, but it would be good idea to make sure it contains what we are looking for */ oldval = *(unsigned*) reloc; *((unsigned*)reloc) = newval; return 1; default: return 0; } } } return 0; } ``` So, in order to hook connect call you would need to call: ```c hook_call("libandroid_runtime.so", "connect", &my_connect); ``` All you now need is to write appropriate my_connect function which will inspect then modify input parameters and then delegate call to real connect() Next thing to worry about is how to get this code to be executed on target app start, but perhaps it is a topic for a separate post. Stay tuned ;-) **update**: some people told me this interception does not work anymore. This works fine for 2.3.x and 4.0.x. For >4.1.x you would want to intercept libjavacore.so. Thanks for Madhavi Rao for figuring this out **comments**: Aron DiehlDecember 1, 2014 at 4:54 AM in 4.2.2, when calling *((unsigned*)reloc) = newval using libjavacore.so, I get a segfault. Does anyone else have this problem? Is there a solution for newer android systems? Thx /---------------------------------------- Snow LJanuary 29, 2015 at 11:55 PM Maybe you can reference this http://stackoverflow.com/questions/23443848/how-to-hook-system-calls-of-my-android-app-non-rooted-device/27099442#27099442 /=================================================== - Vladimir ShcryabetsMarch 4, 2015 at 5:31 AM Also on android 4.1 memory near the "*(unsigned*) reloc" is protected. And operation: ```c *((unsigned*)reloc) = newval; ``` causes "signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 4e8afd08". So to make it work i've changed code: ```c mprotect(aligned_pointer, pagesize, PROT_WRITE | PROT_READ); oldval = *(unsigned*) reloc; *((unsigned*)reloc) = newval; mprotect(aligned_pointer, pagesize, PROT_READ); ``` /----------------------------------------------------------- Vladimir ShcryabetsMarch 4, 2015 at 5:37 AM Forgot to write: ```c size_t pagesize = sysconf(_SC_PAGESIZE); const void* aligned_pointer = (const void*)(reloc & ~(pagesize - 1)); ``` /============================================================ ## Android bionic Bionic是Android的C/C++ library, libc是GNU/Linux以及其他类Unix系统的基础函数库,最常用的就是GNU的libc,也叫glibc。Android之所以采用bionic而不是glibc,有几个原因: - 版权问题,因为glibc是LGPL - 库的体积和速度,bionic要比glibc小很多。 - 提供了一些Android特定的函数,getprop LOGI等 > Core Philosophy: > > The core idea behind Bionic's design is: KEEP IT REALLY SIMPLE. > > This implies that the C library should only provide lightweight wrappers > around kernel facilities and not try to be too smart to deal with edge cases. > > The name "Bionic" comes from the fact that it is part-BSD and part-Linux: > its source code consists in a mix of BSD C library pieces with custom > Linux-specific bits used to deal with threads, processes, signals and a few > others things. > > All original BSD pieces carry the BSD copyright disclaimer. Bionic-specific > bits carry the Android Open Source Project copyright disclaimer. And > everything is released under the BSD license. bionic: - libc(c 库) - libdl(动态链接库访问接口 dlopen dlsym dlerror dlclose dladdr的实现) - libm(C数学函数库, 提供了常见的数序函数和浮点运算) - libstdc++(standard c++ lib) - libthread_db(线程调试库,可以利用此库对多线程程序进行调试) - linker(Android dynamic linker)