美文网首页Android进阶逆向
Android实现so的hook(Android-Inline-

Android实现so的hook(Android-Inline-

作者: carrys17 | 来源:发表于2019-03-20 22:50 被阅读0次

    需求:给一个目标apk,要求hook它的native层代码,但是不能修改它原本的so文件。

    实现方法:通过/proc/pid/maps查看目标so文件加载到内存的基址,然后利用ida查看目标函数在so文件的内存偏移,两个数字相加得到目标函数的内存地址,然后利用Android-Inline-Hook框架编写c文件,编译生成so文件,再修改apk中的smali文件,加载我们的so文件,从而达到hook的效果。

    实例演示:

    1、首先看一下我们要hook的apk,这里的apk是我自己写的一个小demo而已,它的MainActivity包含一个native的int方法,同时在setText()方法中调用它。

    public class MainActivity extends AppCompatActivity {
    
        // Used to load the 'native-lib' library on application startup.
        static {
            System.loadLibrary("native-lib");
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            // Example of a call to a native method
            TextView tv = (TextView) findViewById(R.id.sample_text);
            tv.setText("calc() -- "+calc());
        }
    
        public native int calc();
    }
    

    native方法的实现很简单,只是返回666而已。

    #include <jni.h>
    #include <string>
    
    extern "C" JNIEXPORT jint JNICALL
    Java_com_example_shang_nativehookdemo_MainActivity_calc(
            JNIEnv *env,
            jobject /* this */) {
        return 666;
    }
    

    2、这里我们需要hook的就是这个calc()方法,我想让它的值返回的是777而不是666。我选择使用的框架是Android-Inline-Hook (https://github.com/ele7enxxh/Android-Inline-Hook)。这里框架的使用相对比较简单,我们可以看一下GitHub上提供的例子。

    #include <stdio.h>
    
    #include "inlineHook.h"
    
    int (*old_puts)(const char *) = NULL;
    
    int new_puts(const char *string)
    {
        old_puts("inlineHook success");
    }
    
    int hook()
    {
        if (registerInlineHook((uint32_t) puts, (uint32_t) new_puts, (uint32_t **) &old_puts) != ELE7EN_OK) {
            return -1;
        }
        if (inlineHook((uint32_t) puts) != ELE7EN_OK) {
            return -1;
        }
    
        return 0;
    }
    
    int unHook()
    {
        if (inlineUnHook((uint32_t) puts) != ELE7EN_OK) {
            return -1;
        }
    
        return 0;
    }
    
    int main()
    {
        puts("test");
        hook();
        puts("test");
        unHook();
        puts("test");
    }
    

    可以看到在hook()方法中,registerInlineHook就是注册hook函数相关的了,参入的参数第一个就是你要hook的目标函数地址,第二个是自己的替换函数指针,第三个是保留函数原来的指针。

    然后通过inlineHook正式hook到目标函数。如果想要解除hook的话就通过inlineUnHook方法来实现。

    回到我们这个例子,这里主要的就是拿到calc()这个函数在内存中的地址,因为apk运行时会把so文件的内容加载到内存中去,这个内存基址我们可以通过/proc/pid/maps来得到,再通过ida查看calc()函数在内存中的偏移,两个数值相加即为这里的第一个参数(注意:有时需要加1,因为在寻找对应的方法地址时,要注意是否时Thumb指令,是的话要地址+1,还有的情况是函数地址(二进制)最后一位为0,即arm指令时,需要 +1 );
    第二个参数是替换的函数指针,我们需要定义一个跟目标函数类型一致的方法,然后在里面实现我们替换的逻辑。
    第三个是目标函数的类型。

    3、首先下载GitHub上代码,https://github.com/ele7enxxh/Android-Inline-Hook,适当做一些修改。

    2.jpg

    把example的hooktest.c拿出来,修改Android.mk和Application.mk文件。

    Android.mk

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    LOCAL_MODULE    := hook
    LOCAL_SRC_FILES := inlineHook.c relocate.c hooktest.c
    
    LOCAL_LDLIBS += -lz
    
    LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)/include
    
    LOCAL_LDLIBS += -llog
    
    include $(BUILD_SHARED_LIBRARY)
    

    Application.mk

    APP_ABI := armeabi-v7a 
    APP_PIE:= true
    

    这里我只生成armeabi-v7a的so文件。

    修改hooktest.c文件,首先编写一个得到指定so文件内存基址的函数

    static unsigned long find_database_of(char* soName)//获取目标so内存基址
    {
      char filename[32];
      char cmdline[256];
      sprintf(filename, "/proc/%d/maps", getpid());
      LOGD("filename = %s", filename);
      FILE *fp = fopen(filename, "r");
      unsigned long revalue = 0;
      if (fp)
      {
        while(fgets(cmdline, 256, fp)) //逐行读取
        {
          if(strstr(cmdline, soName) && strstr(cmdline, "r-xp"))//筛选
          {
            LOGD("cmdline = %s",cmdline);
            char *str = strstr(cmdline,"-");
            if(str)
            {
              *str='\0';
              char num[32];
              sprintf(num, "0x%s", cmdline);
              revalue = strtoul( num, NULL, 0 );
              LOGD("revalue = %lu", revalue);
              return revalue;
            }
          }
          memset(cmdline,0,256); //清零
        }
        fclose(fp);
      }
      return 0L;
    }
    

    然后用apktool反编译apk,用ida打开lib下的so文件,查找calc()函数在so文件的偏移地址0x590。


    3.jpg

    完整的testhook.c文件如下

    #include <stdio.h>
    #include <jni.h>
    
    #include "include/inlineHook.h"
    #include <android/log.h>
    
    #include <sys/types.h>
    #include <unistd.h>
    
    #define LOG_TAG "xyz"
    
    #define LOGD(fmt,args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,fmt, ##args)
    
    
    static unsigned long find_database_of(char* soName)//获取libcocos2dlua.so内存基址
    {
      char filename[32];
      char cmdline[256];
      sprintf(filename, "/proc/%d/maps", getpid());
      LOGD("filename = %s", filename);
      FILE *fp = fopen(filename, "r");
      unsigned long revalue = 0;
      if (fp)
      {
        while(fgets(cmdline, 256, fp)) //逐行读取
        {
          if(strstr(cmdline, soName) && strstr(cmdline, "r-xp"))//筛选
          {
            LOGD("cmdline = %s",cmdline);
            char *str = strstr(cmdline,"-");
            if(str)
            {
              *str='\0';
              char num[32];
              sprintf(num, "0x%s", cmdline);
              revalue = strtoul( num, NULL, 0 );
              LOGD("revalue = %lu", revalue);
              return revalue;
            }
          }
          memset(cmdline,0,256); //清零
        }
        fclose(fp);
      }
      return 0L;
    }
    
    
    
    unsigned long func = NULL;
    
    int (*old_calc)(void* env,void* jobject) = NULL;
    
    
    int new_CalcFunc(void* env,void* jobject)
    { 
      int ret = old_calc(env,jobject);
      LOGD("修改前的ret = %d", ret);
      return 777;
    }
    
    
    int hookCalcFunc()
    {
      LOGD("func = %x", func);
        if (registerInlineHook((uint32_t) func, (uint32_t) new_CalcFunc, (uint32_t **) &old_calc) != ELE7EN_OK) {
            return -1;
        }
        if (inlineHook((uint32_t) func) != ELE7EN_OK) {
            return -1;
        }
        LOGD("hookCalcFunc-------");
        return 0;
    }
    
    int unHookCalcFunc()
    {
        if (inlineUnHook((uint32_t) func) != ELE7EN_OK) {
            return -1;
        }
        return 0;
    }
    
    
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM*vm, void* reserved){
    
        LOGD("enter JNI_OnLoad");
    
        unsigned long base = find_database_of("libnative-lib.so");
        LOGD("base = %x ",base);
    
        if (base > 0L) {
            func = base + 0x590 + 1;
            void* func1 = (void*)(base + 0x590 + 1);
            LOGD("FUNC = %x", func);
            hookCalcFunc();
        }   
    
      return JNI_VERSION_1_6;
    }
    
    

    这里要注意的就是jni方法的编写,默认都是带两个参数的,所以new_CalcFunc(void* env,void* jobject)才会有两个参数,因为其实这两个都没有用到,所以用void* 代替即可。

    4、通过ndk-build命令生成so文件


    1553090016(1).jpg

    注意生成的包是在当前目录的上一个目录的libs目录下


    image.png

    将生成的so文件放到反编译后的lib目录下, 同时修改MainActivity.smali文件,加载我们的so文件。


    image.png

    5、重新打包,签名,运行即可看到calc()被hook后并且修改了。


    image.png image.png image.png

    附上下载地址:
    https://github.com/carrys17/Study-Notes/tree/master/NativeHookDemo%E2%80%94%E2%80%94resource

    相关文章

      网友评论

        本文标题:Android实现so的hook(Android-Inline-

        本文链接:https://www.haomeiwen.com/subject/myxxvqtx.html