cydia substrate目前也支持android了,cydia substrate是一个代码修改平台。它可以修改任何主进程的代码,官方网址:http://www.cydiasubstrate.com/
在正式学习之前,需要做如下准备:
1. root你的手机
2. 下载官方的apk,并且安装进手机,点击运行,点击按钮,允许操作。
3. 更新sdk
打开sdkmanager
http://asdk.cydiasubstrate.com/addon.xml
添加完成之后,在extra下回多了一个选项,点击勾选下载
下载完成之后,会在sdk/extra目录下生成我们需要的文件:
在正式开始之前,我们先来学习下cydia中重要的api。
//该方法实现在指定的类被加载的时候发出通知
void hookClassLoad(String name, MS.ClassLoadHook hook);
参数 | 描述 |
---|---|
name | 包名+类名,使用java的.符号 |
hook | MS.ClassLoadHook的一个实例,当这个类被加载的时候,它的 classLoaded 方法会被执行。 |
- MS.hookMethod
//该API允许开发者提供一个回调函数替换原来的方法,这个回调函数是一个实现了MS.MethodHook接口的对象,是一个典型的匿名内部类。它包含一个invoked函数
void hookMethod(Class _class, Member member, MS.MethodHook hook, MS.MethodPointer old);
参数 | 描述 |
---|---|
_class | 加载的目标类,为classLoaded传下来的类参数 |
member | 通过反射得到的需要hook的方法(或构造函数)。不能够hook字段 |
hook | MS.MethodHook的一个实例,其包含的invoked方法会被调用,用以代替member中的代码 |
将SDK中的substrate-api.jar
这里由于不需要acitivity,所以讲manifest中额activity相关配置全部删除。如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.rectnativetest">
<application>
<meta-data android:name="com.saurik.substrate.main" android:value=".Main"/>
</application>
<uses-permission android:name="cydia.permission.SUBSTRATE"/>
</manifest>
接下来就是创建Main.java,并且需要包含一个static void initialize()方法。
// 当插件被加载的时候,该方法中的代码就会运行
static void initialize() {}
这里我们按照官网上给出的栗子,hook类android.content.res.Resources
MS.hookClassLoad("android.content.res.Resources", new MS.ClassLoadHook() {
public void classLoaded(Class<?> resources) {
}
});
这里更改android.content.res.Resources类的getColor方法的返回值,将其返回红色。
Method getColor;
try {
getColor = resources.getMethod("getColor", Integer.TYPE);
} catch (NoSuchMethodException e) {
getColor = null;
}
if (getColor != null) {
final MS.MethodPointer old = new MS.MethodPointer();
MS.hookMethod(resources, getColor, new MS.MethodHook() {
public Object invoked(Object resources, Object... args)
throws Throwable
{
int color = (Integer) old.invoke(resources, args);
return color & ~0x0000ff00 | 0x00ff0000;
}
}, old);
}
可以发现整个手机系统都已经改变为红色的字体了。
这里,我自己写了一个简单应用,只有登录功能,逻辑如下:
if (name.equals("zhangsan") && pass.equals("123")) {
return true;
} else {
return false;
}
这里只有用户名为zhangsan以及密码为123,时候,才返回true,即表示登录成功,否则返回false。
同样在Main类的initialize方法里,增加一个hookClassLoad方法,来hook当前登录的activity
MS.hookClassLoad("reacthello.myapplication.MainActivity", new MS.ClassLoadHook() {
public void classLoaded(Class<?> resources) {
Method checkLogin;
try {
checkLogin = resources.getMethod("checkLogin", new Class[]{String.class,String.class});
} catch (NoSuchMethodException e) {
checkLogin = null;
}
if (checkLogin != null) {
final MS.MethodPointer old = new MS.MethodPointer();
MS.hookMethod(resources, checkLogin, new MS.MethodHook() {
public Object invoked(Object resources, Object... args)
throws Throwable
{
return true;
}
}, old);
}
}
});
这里的checklogin是刚才登录逻辑的判断,这里,在hook之后横返回true,也就是无论如何都会登录成功,效果如下:
现在大家应该都已经知道了可以通过hookClassLoad和hookMethod来更改指定方法运行的代码,以及返回值。现在我们来更改一下手机系统的IMEI值。
首先,需要知道如何通过代码获取IMEI值
android.telephony.TelephonyManager.getDeviceId();
代码:
MS.hookClassLoad("android.telephony.TelephonyManager", new MS.ClassLoadHook() {
public void classLoaded(Class<?> resources) {
Method getDeviceId;
try {
getDeviceId = resources.getMethod("getDeviceId",null);
} catch (NoSuchMethodException e) {
getDeviceId = null;
}
if (getDeviceId != null) {
final MS.MethodPointer old = new MS.MethodPointer();
MS.hookMethod(resources, getDeviceId, new MS.MethodHook() {
public Object invoked(Object resources, Object... args)
throws Throwable
{
String imei = (String)old.invoke(resources, args);
imei = "aaaaaaaaaaaaaaa";
return imei;
}
}, old);
}
}
});
这里更改的android.telephony.TelephonyManager类下的getDeviceId方法。
此时我们通过:
String imei = telephonyManager.getDeviceId();
Toast.makeText(MainActivity.this,"imei is :"+imei,Toast.LENGTH_SHORT).show();
获得的就是aaaaaaaaaa了
最后一次尝试一下当我打开一个应用的时候,自动打开一个毫不相干的应用,比如当我每次打开今日头条的时候,系统会跳转到网易新闻,首先我们需要知道“今日头条”和”网易新闻”的主界面的activity,使用如下命令可以获得当前界面的activity:
dumpsys activity | grep mFocusedActivity
在adb shell下可以通过如下代码启动activity
Runtime.getRuntime().exec("am start -n com.netease.newsreader.activity/com.netease.nr.biz.ad.AdActivity");
完整代码:
MS.hookClassLoad("com.ss.android.article.news.activity.MainActivity", new MS.ClassLoadHook() {
public void classLoaded(Class<?> resources) {
Log.d("hookstart","com.ss.android.article.news.activity.MainActivity loaded");
Method onCreate;
try {
onCreate = resources.getMethod("onCreate",Bundle.class);
} catch (NoSuchMethodException e) {
onCreate = null;
}
if (onCreate != null) {
final MS.MethodPointer old = new MS.MethodPointer();
MS.hookMethod(resources, onCreate, new MS.MethodHook() {
public Object invoked(Object resources, Object... args)
throws Throwable
{
Log.d("hang.liu","the ooooooo");
Object object = old.invoke(resources, args);
try {
Log.d("hookstart","onCreate hooked.");
Runtime.getRuntime().exec("am start -n com.netease.newsreader.activity/com.netease.nr.biz.ad.AdActivity");
} catch (Exception e) {
Log.d("hookstart","start activity exception happend...");
}
return object;
}
}, old);
}
}
});
ok,可以看到,到目前为止,已经完成了我们想要的效果了。但是还是有很多限制最大的就是手机必须要有root权限,在5.0之后的版本已经不能使用。
源码下载