Hook简介:
Hook就是钩子,在安卓中,就是在事件传送到终点前截获并监控事件的传输,像个钩子勾上事件一样,并且能够在勾上事件时,处理一些自己特定的事件。
Cydia Substrate的官网定义:The powerful code modification platform behind Cydia.
Cydia Substrate是一个代码修改平台,它可以修改任何主进程的代码,不管是用Java还是C/C++(native代码)编写的。
注:Cydia Substrate框架对于inline Hook的操作目前还是存在一些bug,使用的时候可能会出现崩溃的现象,部分使用了国内定制的ROM的设备在使用Cydia Substrate框架时会造成设备无法重新启动或无法Hook的现象。
使用Cydia Substrate的步骤:
第一步:
安装Cydia Substrate框架Android本地服务
首先就是在Android设备中安装Cydia Substrate框架的本地服务应用substrate.apk
然后,需要"Link Substrate Files"(连接本地的Substrate服务文件),这一步是需要Root权限的,连接后还需要重启设备才能够生效。
第二步:
下载使用Cydia Substrate库(直接去官网下载)。下载完成后,将得到的所有文件(很多的jar包与so库),都拷贝到Android项目下的libs文件夹中,就可以直接使用了。其中的substrate.h头文件与lib文件夹下的so文件是提供在使用NDK(Native Development Kit,原生开发工具)进行原生Hook程序开发中的函数支持库。
那么Cydia Substrate怎么用呢?
其实很简单,Cydia Substrate提供了三个静态的方法工具类,我们只需要学会使用它就好。
1、MS.hookClassLoad 拿到指定Class载入时的通知
2、MS.hookMethod 使用一个Java方法去替换另一个Java方法
3、MS.moveUnderClassLoader 使用不同的ClassLoader重载对象
具体说明如下:
/**
* Hook一个指定的Class
*
* @param name Class的包名+类名,如android.content.res.Resources
* @param hook 成功Hook一个Class后的回调
*/
void hookClassLoad(String name, MS.ClassLoadHook hook);
/**
* Hook一个指定的方法,并替换方法中的代码
*
* @param _class Hook的calss
* @param member Hook class的方法参数
* @param hook 成功Hook方法后的回调
* @param old Hook前方法,类似C中的方法指针
*/
void hookMethod(Class _class, Member member, MS.MethodHook hook, MS.MethodPointer old);
/**
* Hook一个指定的方法,并替换方法中的代码
*
* @param _class Hook的calss
* @param member Hook class的方法参数
* @param alteration
*/
void hookMethod(Class _class, Member member, MS.MethodAlteration alteration);
/**
* 使用一个ClassLoader重载一个对象
*
* @param loader 使用的ClassLoader
* @param object 带重载的对象
* @return 重载后的对象
*/
<T> T moveUnderClassLoader(ClassLoader loader, T object);
如何Hook一个应用程序?
案例:我们针对Android操作系统的浏览器应用,Hook其首页Activity的onCreate方法,并在其中注入我们的广告。
思路:
1、我们根据某广告平台的规定,在我们的AndroidManifest.xml文件中填入一些广告相关的ID。
2、在AndroidManifest.xml文件中填写一些使用Cydia Substrate相关的配置与权限。
3、声明一个广告的Activity,并设置此Activity为背景透明的Activity。
实际操作:
其AndroidManifest.xml文件的部分内容如下所示:
<!-- 广告相关的权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<!-- 加入substrate权限 -->
<uses-permission android:name="cydia.permission.SUBSTRATE" />
<!-- 广告相关参数 -->
<meta-data
android:name="APP_ID"
android:value="c62bd976138fa4f2ec853bb408bb38af" />
<meta-data
android:name="APP_PID"
android:value="DEFAULT" />
<!-- 声明substrate的注入Main类 -->
<meta-data
android:name="com.saurik.substrate.main"
android:value="com.example.hookad.Main" />
<!-- 透明无动画的广告Activity -->
<activity
android:name="com.example.hookad.MainActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar" >
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<!-- 广告的action -->
<action android:name="com.example.hook.AD" />
</intent-filter>
</activity>
对于Cydia Substrate的主入口Main类,依照之前的步骤新建一个包含有initialize方法的Main类。
这里我们使用adb shell下使用dumpsys activity命令找到浏览器主页的Activity名称为com.android.browser.BrowserActivity。
使用MS.hookClassLoad方法获取了BrowserActivity之后再hook其onCreate方法,在其中启动一个含有广告的Activity。Main类的代码如下所示:
public class Main { /** * substrate 初始化后的入口 */ static void initialize() { //Hook 浏览器的主Activity,BrowserActivity MS.hookClassLoad("com.android.browser.BrowserActivity", new MS.ClassLoadHook() { public void classLoaded(Class<?> resources) { Log.e("test", "com.android.browser.BrowserActivity"); // 获取BrowserActivity的onCreate方法 Method onCreate; try { onCreate = resources.getMethod("onCreate", Bundle.class); } catch (NoSuchMethodException e) { onCreate = null; } if (onCreate != null) { final MS.MethodPointer old = new MS.MethodPointer(); // hook onCreate方法 MS.hookMethod(resources, onCreate, new MS.MethodHook() { public Object invoked(Object object, Object...args) throws Throwable { Log.e("test", "show ad"); // 执行Hook前的onCreate方法,保证浏览器正常启动 Object result = old.invoke(object, args); // 没有Context // 执行一个shell 启动我们的广告Activity CMD.run("am start -a com.example.hook.AD"); return result; } }, old); } } }); } }