《Android安全技术揭秘与防范》—第8章8.4节Hook原生应用程序

8.4 Hook原生应用程序
之前我们演示过了如何在Java层Hook系统的API方法,但是我们都知道很多安全级别较高的操作我们都不会在Java层来完成,而且Java层很多的API都是通过JNI的方式在Native层完成的,所以对Java层的API方法Hook意义不是很大。本节我们就具体来说说在Android中如何使用CydiaSubstrate框架完成Native层的Hook操作。

8.4.1 CydiaSubstrate框架针对Native层Hook的支持
对于CydiaSubstrate框架来说,其给我们提供了类似在Java中的API方法,如在Native层的MSJavaHookClassLoad函数(类似Java中的hookClassLoad方法)、MSJavaHookMethod函数(类似Java中的hookMethod)。作者的意图就是为了让我们能够在Native层使用JNI完成Java函数的Hook。其中两个函数的具体定义如下:

/**
* 通过JNI Hook Java中的ClassLoad
 *
 * @jni jni指针
 * @name 待Hook的类,字符串形式
 * @callback Hook后的回调
 * @data 自定义参数数据
 */
voidMSJavaHookClassLoad(JNIEnv *jni, constchar*name, void(*callback)(JNIEnv *, jclass, void*), void*data);

/**
 * 通过JNI Hook Java中的指定方法
 *
 * @jni jni指针
 * @_class jclass
 * @methodId 待Hook方法ID
 * @hook Hook后待替换的函数
 * @old Hook前原函数的指针
 */
voidMSJavaHookMethod(JNIEnv *jni, jclass _class, jmethodID methodId, void*hook, void**old);

上述的两个函数确实比较有用,但是却不是我们最想要的结果。在Native层Hook我们还是希望针对原生函数进行Hook操作。其实针对Native层的Hook原理,我们在本章的开头已经给各位读者介绍了。CydiaSubstrate只是针对其做了一个良好的封装操作,让我们更方便地使用。下面是CydiaSubstrate框架提供的Hook函数方法。

* 根据具体的地址路径加载动态库
 * 类似于dlopen
 *
 * @return 动态库ImageRef
 */
MSImageRef MSGetImageByName(constchar*file);

/**
 * 根据指定库找到其中函数的偏移量
 * 类似于dlsym
 *
 * @image 指定的动态库
 * @name 指定函数的名称
 * @return 指定函数的指针(兼容ARM/Thumb)找不到返回NULL
 */
void*MSFindSymbol(MSImageRef image, constchar*name);

/**
 * Hook Native层中的指定函数
 *
 * @symbol 待Hook函数指针
 * @hook Hook后待替换的函数指针
 * @old Hook前函数指针
 */
voidMSHookFunction(void*symbol, void*hook, void**old);

看到上面的函数说明估计读者们都跃跃欲试了,而且相信很多读者已经能够猜出如何使用CydiaSubstrate框架了。下面我们还是详细地说明一下,除了了解其提供的API函数之外,使用CydiaSubstrate框架还需要注意的一些注意事项。

基于CydiaSubstrate框架的动态库必须以“.cy.so”(系统默认是“.so”)作为后缀。
在AndroidManifest.xml文件中需要声明cydia.permission.SUBSTRATE权限。
需要在Native C/CPP方法前添加Config配置。
MSConfig(MSFilterExecutable, "/system/bin/app_process")
好了介绍完毕,下面我们具体地操作一次。

8.4.2 通过JNI改变系统颜色
与之前的尝试Hook系统API小节一样,如我们希望完成Hook Android系统中的Resources类,并将系统中的颜色都改为紫罗兰色。思路也是一样的,我们只需要拿到系统中Resources类的getColor方法,将其返回值做修改即可。那么我们使用原生方法实现,需要完成以下几个步骤。

1.在AndroidManifest.xml中声明权限与安装方式

因为是系统组件代码,我们需要设置其安装方式是internalOnly与hasCode=“false”,这样能够方便CydiaSubstrate框架获取我们的逻辑。当然还需要声明SUBSTRATE权限,具体的操作如下AndroidManifest.xml内容所示。

<?xml version="1.0" encoding="utf-8"?>

<!-- internalOnly 系统内部安装,禁止安装到sd卡 -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.hooknative"
  android:installLocation="internalOnly"
  android:versionCode="1"
  android:versionName="1.0" >

<!-- 声明Substrate权限 -->
<uses-permission android:name="cydia.permission.SUBSTRATE" />
<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="21" />

<!-- hasCode=false,系统组件,不运行APP中的逻辑 -->
<application android:hasCode="false" >
</application>

</manifest>

2.新创建项目的cpp文件,导入所需的库

这里我们新创建一个原生代码文件HookNative.cy.cpp(后缀必须为.cy.cpp,编译后则会出现.cy.so文件),并将CydiaSubstrate的库文件libsubstrate.so、libsubstrate-dvm.so、substrate.h一起复制到jni目录下(这里需要根据不同平台选择,我们这里选择的是ARM平台的库),jni目录如图8-20所示。

当然,我们还需要编写Makefile文件Android.mk,指定Substrate库参与编译,并引入一些必要的库。内容如下所示。

LOCAL_PATH := $(call my-dir)

# substrate-dvm 库
include $(CLEAR_VARS)
LOCAL_MODULE:= substrate-dvm
LOCAL_SRC_FILES := libsubstrate-dvm.so
include $(PREBUILT_SHARED_LIBRARY)

# substrate 库
include $(CLEAR_VARS)
LOCAL_MODULE:= substrate
LOCAL_SRC_FILES := libsubstrate.so
include $(PREBUILT_SHARED_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE  := HookNative.cy
LOCAL_SRC_FILES := HookNative.cy.cpp

LOCAL_LDLIBS+= -L$(SYSROOT)/usr/lib -llog
LOCAL_LDLIBS+= -L$(LOCAL_PATH) -lsubstrate-dvm -lsubstrate
include $(BUILD_SHARED_LIBRARY)

3.载入配置文件与CydiaSubstrate入口

在HookNative.cy.cpp代码文件中,使用CydiaSubstrate框架的API,还需要在其中声明一些东西,如MSConfig配置app_process的路径,声明MSInitialize作为一个CydiaSubstrate插件的入口。我们还会应用一些开发中必要的头文件与LOG声明,如这里我们的HookNative.cy.cpp内容为:

#include<android/log.h>
#include<substrate.h>

#defineLOG_TAG "native_hook"
#defineLOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

// 载入配置文件
MSConfig(MSFilterExecutable, "/system/bin/app_process")

// Cydia初始化入口
MSInitialize {
}

4.Hook并替换其方法

其修改方法与上一节的Hook Java中的方法类似,我们只需要修改相关的函数完成Hook即可。如这里我们使用MSJavaHookClassLoad方法Hook系统的Resources类,并使用MSJavaHookMethod方法Hook其getColor方法,替换其方法。具体实现如下所示。

#include<android/log.h>
#include<substrate.h>

#defineLOG_TAG "native_hook"

#defineLOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

// 载入配置文件
MSConfig(MSFilterExecutable, "/system/bin/app_process")

// getColor方法Hook前原函数指针
staticjint (*_Resources$getColor)(JNIEnv *jni, jobject _this, ...);

// getColor方法Hook后被替换的函数
staticjint $Resources$getColor(JNIEnv *jni, jobject _this, jint rid) {
  jint color = _Resources$getColor(jni, _this, rid);
returncolor & ~0x0000ff00 | 0x00ff0000;
}

// Hook住Resources class的回调
staticvoidOnResources(JNIEnv *jni, jclass resources, void*data) {
  // hook其对应的getColor方法
  jmethodID method = jni->GetMethodID(resources, "getColor", "(I)I");
if(method != NULL)
    MSJavaHookMethod(jni, resources, method,
&$Resources$getColor, &_Resources$getColor);
}

// Cydia初始化入口
MSInitialize {
  // Hook Java中的Resources
  MSJavaHookClassLoad(NULL, "android/content/res/Resources", &OnResources);
}

5.编译、安装、重启验证

同样,因为CydiaSubstrate是Hook Zygote进程,而且我们Hook的又是系统的Resources方法,所以我们希望验证都需要重启一下设备。我们可以选择CydiaSubstrate中的软重启,这里我们对系统的设置页面Hook前后都做了一个截图,对比截图如图8-21所示。

本例中我们继续之前Java中Hook的思想,完成了在原生代码中使用JNI针对Java中的API进行Hook操作。因为,CydiaSubstrate框架中的hookClassLoad方法、hookMethod方法底层实现也是如此,所以我们使用起来很类似。

8.4.3 Hook后替换指定应用中的原生方法
讨论了太久的Java层面的API Hook工作,也举了很多例子,本节中我们就看看如何使用CydiaSubstrate框架完成原生函数的Hook。

例如,现在我们有一个应用程序(包名为:com.example. testndklib),其主要功能就是按下界面上的“test”按钮后,通过JNI调用Native的test函数,在系统的Log中输入一个当前我有多少钱的整数值。界面如图8-22所示。

使用JNI调用的test函数,写在NDK库testNDKlib中,会调用一个名叫getMoney的函数,显示我当前有多少钱。当然,这里我们直接硬编码了,返回值为100的整数。中间,我们还将 getMoney 函数的地址通过 Log 打印出来。testNDKlib.cpp内容如下所示。

#include<stdio.h>
#include<jni.h>
#include<android/log.h>
extern"C" {
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "cydia_native", __VA_ARGS__)

/**
 * 测试函数getMoney,返回一个整数
 */
intgetMoney(void) {
  // 打印方法函数地址
  LOGI(" getMoney() function address in : %p\n", &getMoney);
  return100;
}

// 一个JNI的test函数
jstring Java_com_example_testndklib_MainActivity_test(JNIEnv* env,
    jobject thiz) {
  LOGI(" I have %d money.\n", getMoney());
  return0;
}
}

运行一下程序,单击“test”按钮,拿到了系统输出的 Log,与我们输出的预期一样。笔者将DDMS上输出的Log截图,如图8-23所示。

现在我们希望Hook此so文件,找到其中的getMoney函数,替换它让它给我们返回整数值999999(类似一个游戏修改金币的外挂)。针对之前我们讨论的Hook的原理,我们需要做如下几步操作。
(1)加载原生库,libtestNDKlib.so。

(2)找到对应的函数符号地址,MSFindSymbol。

(3)替换函数,MSHookFunction。

这里我们在完成1、2步骤的时候,我们同时也用dlopen与dlsym方式实现给大家演示一下。之前的环境配置逻辑以及权限声明逻辑与上一个例子类似,这里我们不做赘述,直接看一下cpp文件中的内容,具体如下:

#include<android/log.h>
#include<substrate.h>
#include<stdio.h>

#defineLOG_TAG "cydia_native"

#defineLOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

// 初始化CydiaSubstrate
MSConfig(MSFilterExecutable, "/system/bin/app_process")

// 原函数指针
int(*original_getMoney)(void);

/**
 * 替换后的函数
 */
intreplaced_getMoney(void)
{

  LOGI(" replaced_getMoney() function address in : %p\n", &replaced_getMoney);
  return999999;
}

/**
 *
 * 找到指定链接库中的函数地址
 *
 * @libraryname 链接库地址
 * @symbolname 函数名
 * @return 对应的函数地址
 */
void* lookup_symbol(char* libraryname, char* symbolname)
{
  // dlopen打开指定库,获得句柄
  void*imagehandle = dlopen(libraryname, RTLD_GLOBAL | RTLD_NOW);
  if(imagehandle != NULL) {
    // 获得具体函数
    void* sym = dlsym(imagehandle, symbolname);
    if(sym != NULL) {
      returnsym;
    } else{
      LOGI("(lookup_symbol) dlsym didn't work");
      returnNULL;
    }
  } else{
    LOGI("(lookup_symbol) dlerror: %s", dlerror());
    returnNULL;
  }
}

//初始化
MSInitialize
{

  // 获得libtestNDKlib.so动态库中getMoney函数的地址
  MSImageRef image;
  image = MSGetImageByName(
      "/data/data/com.example.testndklib/lib/libtestNDKlib.so");
  void* getAgeSym = MSFindSymbol(image, "getMoney");

  // MSImageRef与 MSFindSymbol 也可以写为如下所示找到getMoney函数的地址
  //
  // void * getAgeSym = lookup_symbol(
  //      "/data/data/com.example.testndklib/lib/libtestNDKlib.so",
  //      "getMoney");

  // 将getMoney函数替换为 replaced_getMoney函数
  MSHookFunction(getAgeSym, (void*) &replaced_getMoney,
      (void**) &original_getMoney);

}

编译后安装到已经安装了CydiaSubstrate框架的系统中,重启Android设备。如果在整个系统编译与配置没有什么错误的情况下,我们发现CydiaSubstrate框架会打出Log说Loding什么什么 so 文件了。这里我们看见,LodinglibnativeHook.cy.so 说明我们之前开发的 Hook 其中的getMoney方法已经生效了,如图8-24所示。

这个时候我们继续运行程序,进入我们刚才的test应用程序。单击“test”按钮,获取调用JNI中的test函数打印我有多少钱。我们能够在DDMS中清楚地看到,getMoney函数已经被一个名为“replace_getMoney”的函数替换了,其地址也已经被替换了。我们也看到使用替换后的值输出为“I have 999999 money”,如图8-
25所示。

8.4.4 使用Hook进行广告拦截
对于Android操作系统我们知道,Java层都是建立在原生C/C++语言上的,特别是针对一些系统级别的API函数。上面我们演示了如何对用户自定义函数进行Hook,下面我们演示一下如何对Native层的系统API进行Hook。

如这里我们希望对系统中的网络请求API进行Hook,然后过滤掉一些广告相关的请求,完成广告拦截功能,其具体的流程如图8-26所示。

这里我们查看Android操作系统的POSIX定义的源码Poxis.Java文件,发现其针对网络请求函数的定义是在native完成的,如图8-27所示。

对于此类的问题,我们就不得不在native完成Hook与函数替换工作了。所以我们还是使用CydiaSubstrate框架,对系统的API函数connect进行Hook与替换。如下代码所示,我们使用MSHookFunction对native层中的connect函数进行Hook替换至newConnect函数。

#defineLOG_TAG "cydia_native"
#defineLOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

// 初始化CydiaSubstrate
MSConfig(MSFilterExecutable, "/system/bin/app_process")

// 原connect函数指针
int*(*oldConnect)(int,
 constsockaddr *, socklen_t);
int*newConnect(intsocket, constsockaddr *address, socklen_t length) {

  charip[128] = { 0 };
  intport = -1;
  if(address->sa_family == AF_INET) {
    sockaddr_in *address_in = (sockaddr_in*) address;
    // 获取 ip
    inet_ntop(AF_INET, (void*) (structsockaddr*) &address_in->sin_addr, ip,
        128);
    // 获取端口
    port = ntohs(address_in->sin_port);

    // 过滤掉172.22.156.129的请求
    if(strcmp(ip, "172.22.156.129") == 0) {

      LOGI("发现广告请求");
      structsockaddr_in my_addr;
      intmy_len = sizeof(structsockaddr_in);
      bzero(&my_addr, sizeof(my_addr));
      my_addr.sin_family = AF_INET;
      my_addr.sin_port = htons(80);
      my_addr.sin_addr.s_addr = inet_addr("127.0.0.1");

      returnoldConnect(socket, (constsockaddr*) &my_addr,
          sizeof(my_addr));
    }
    returnoldConnect(socket, address, length);

  }
}

//初始化
MSInitialize{
  MSHookFunction((void*) &connect, (void*) &newConnect,
      (void**) &oldConnect);
}

这里我们只是简单地将IP为172.22.156.129的请求重定向到本地127.0.0.1,即让类似的广告请求拿不到数据。此类方式的广告过滤属于比较原始暴力类型的过滤方法,但却也简单有效。熟悉connect的读者应该会发现,如果已经能够替换掉connect函数,其实我们能做到的事情远远比拦截一个广告请求大得多,比如跳转至钓鱼网站,收集私密发送数据等。

时间: 2024-10-01 19:23:07

《Android安全技术揭秘与防范》—第8章8.4节Hook原生应用程序的相关文章

《Android安全技术揭秘与防范》—第8章8.节什么是Hook技术

第8章 动态注入技术 Android安全技术揭秘与防范 我们在讨论动态注入技术的时候,APIHook的技术由来已久,在操作系统未能提供所需功能的情况下,利用APIHook的手段来实现某种必需的功能也算是一种不得已的办法.在Windows平台下开发电子词典的光标取词功能,这项功能就是利用Hook API的技术把系统的字符串输出函数替换成了电子词典中的函数,从而能得到屏幕上任何位置的字符串.无论是16位的Windows95,还是32位的Windws NT,都有办法向整个系统或特定的目标进程中"注入&

《Android安全技术揭秘与防范》—第2章2.1节钱从哪里来

第2章 Android地下产业链分析 Android安全技术揭秘与防范 目前Android设备已经遍布全球,人们就会想着各种方法从这海量的用户里面捞取利益.创业者们会想着如何做出一款让大家都喜欢的.解决大家实际问题的 App:游戏开发商们想着如何让Android用户将自己的碎片时间都用在玩自己所开发的手机游戏上:手机制造商们想着如何让自己所生产的Android手机销量更好:而黑客们,却想着怎么样用自己的技术通过非正常手段获取利益. 如果你还认为黑客们的手段就是拨打欺诈电话.发送欺诈短信,那你就落

《Android安全技术揭秘与防范》——第2章,第2.1节钱从哪里来

第2章 Android地下产业链分析 Android安全技术揭秘与防范 目前Android设备已经遍布全球,人们就会想着各种方法从这海量的用户里面捞取利益.创业者们会想着如何做出一款让大家都喜欢的.解决大家实际问题的 App:游戏开发商们想着如何让Android用户将自己的碎片时间都用在玩自己所开发的手机游戏上:手机制造商们想着如何让自己所生产的Android手机销量更好:而黑客们,却想着怎么样用自己的技术通过非正常手段获取利益. 如果你还认为黑客们的手段就是拨打欺诈电话.发送欺诈短信,那你就落

《Android安全技术揭秘与防范》—第1章1.1节Android的发展历史

第1章 Android简介Android安全技术揭秘与防范近年来我们对"Android"这个词已经不再陌生.在过去的几年时间里,Android的快速发展已经影响到了每个人的日常生活.如今Android不仅仅意味着一台手机.一部平板电脑,也可能是一台电视.一只手表.一部智能汽车.一副眼镜.然而,在一个生态系统形成的同时,总会有一群人希望通过一些不常规的手段谋取利益. 本章主要从Android黑色产业链与破解人员的动机来分析Android的安全问题. 1.1 Android的发展历史And

《Android安全技术揭秘与防范》——第1章,第1.1节Android的发展历史

第1章 Android简介Android安全技术揭秘与防范近年来我们对"Android"这个词已经不再陌生.在过去的几年时间里,Android的快速发展已经影响到了每个人的日常生活.如今Android不仅仅意味着一台手机.一部平板电脑,也可能是一台电视.一只手表.一部智能汽车.一副眼镜.然而,在一个生态系统形成的同时,总会有一群人希望通过一些不常规的手段谋取利益. 本章主要从Android黑色产业链与破解人员的动机来分析Android的安全问题. 1.1 Android的发展历史And

《Android安全技术揭秘与防范》目录—导读

作者简介 Android安全技术揭秘与防范 周圣韬,曾任职于金山.360公司做Android开发.安全审计工作,现在为百度手机助手客户端高级开发工程师. 专业书评 出一本安卓系统安全方面的书,挺不简单的!360是做安全的,周圣韬从360出来还能做安全方面的事儿,说明他跟360还有缘分.这本书我仔细看了一下,挺全的,是一本不错的工具书. --360公司创始人董事长兼CEO.知名天使投资人,周鸿祎 本书卖点 360公司创始人董事长兼CEO.知名天使投资人,周鸿祎推荐 36个功防案例的实战演示,详细剖

《Android安全技术揭秘与防范》——第1.1节Android的发展历史

第1章 Android简介Android安全技术揭秘与防范近年来我们对"Android"这个词已经不再陌生.在过去的几年时间里,Android的快速发展已经影响到了每个人的日常生活.如今Android不仅仅意味着一台手机.一部平板电脑,也可能是一台电视.一只手表.一部智能汽车.一副眼镜.然而,在一个生态系统形成的同时,总会有一群人希望通过一些不常规的手段谋取利益. 本章主要从Android黑色产业链与破解人员的动机来分析Android的安全问题. 1.1 Android的发展历史And

《Android安全技术揭秘与防范》—第8章8.3节HookAndroid应用

8.3 HookAndroid应用 前面我们介绍过Cydiasubstrate框架提供在Java层Hook的能力,其中主要是提供了三个比较重要的方法,MS.hookClassLoad.MS.hookMethod.MS.moveUnderClassLoader.三个方法的具体介绍如表8-2所示. 几个方法的具体参数与返回值,我们可以看如下的方法具体定义. * Hook一个指定的Class * * @paramname Class的包名+类名,如android.content.res.Resourc

《Android安全技术揭秘与防范》——导读

目 录前 言 第1章 Android简介1.1节Android的发展历史1.2节Android系统进化史1.3节Android和iOS系统对比第2章 Android地下产业链分析 2.1节钱从哪里来2.2节安全的发展趋势第3章 理解Android系统第4章 Root你的设备第5章 APK静态分析第6章 ARM汇编速成第7章 APK动态分析第8章 动态注入技术 第9章 应用加固与渗透测试第10章 系统安全措施第11章 内核攻击与防护附录A ARM指令集附录B ARM伪指令集