ratel,让Xposed模块在免root的环境下跑起来
我一直觉得,如果只是在进程内使用xposed的话,是不是可以不用root,不替换受精卵。
后来我知道了有app加固这个东西,那就是给app套了一层代码,这样的话我是不是也可以类似加固这样给apk套一个壳,在别人的apk运行之前执行某些特殊逻辑。
然后,我知道droidPlugin和virtualAPP这个东西,原来我们可以欺骗Android系统,直接让一个apk文件run起来。
最好,还有一个virtualXposed,竟然可以在virtualAPP的环境下,跑起xposed。
virtualXposed的出现,我觉得理论上一个app,肯定是可以在非root的环境下run起来的,只是基于app容器引擎确实有点重。
所以,我抄了抄virtualXposed,把virtualApp替换掉,直接采用一个轻量级的壳用来驱动各个模块,然后貌似尽然可以行通。
怎么做:
1. 做一个壳划线标题
@Override
protected void attachBaseContext(Context base) {
//第一步需要call supper,否则Application并不完整,还无法作为context使用,当然此时base context是可用状态
super.attachBaseContext(base);
//exposed框架,在driver下面定义,所以需要在替换classloader之前,完成exposed框架的so库加载
ExposedBridge.initOnce(this, getApplicationInfo(), getClassLoader());
if (!checkSupport()) {
throw new IllegalStateException("epic 不支持的版本");
}
//释放两个apk,一个是xposed模块,一个是原生的apk,原生apk替换为当前的Application作为真正的宿主,xposed模块apk在Application被替换之前作为补丁代码注入到当前进程
releaseApkFiles();
//替换classloader
Class<?> contextImplClazz = XposedHelpers.findClassIfExists("android.app.ContextImpl", base.getClassLoader());
Object contextImpl = XposedHelpers.callStaticMethod(contextImplClazz, "getImpl", base);
Object loadApk = XposedHelpers.getObjectField(contextImpl, "mPackageInfo");
ClassLoader parentClassLoader = RetalDriverApplication.class.getClassLoader();
try {
// Class<?> aClass = XposedHelpers.findClass("android.app.LoadedApk", RetalDriverApplication.class.getClassLoader());
parentClassLoader = (ClassLoader) XposedHelpers.getObjectField(loadApk, "mClassLoader");
} catch (Exception e) {
//ignore
}
String originApkSourceDir = new File(ratelWorkDir(this), originAPKFileName).getAbsolutePath();
PathClassLoader originClassLoader = new PathClassLoader(originApkSourceDir, parentClassLoader);
XposedHelpers.setObjectField(loadApk, "mClassLoader", originClassLoader);
//context中的resource,仍然绑定在老的apk环境下,现在把他们迁移
ApplicationInfo appinfoInLoadedApk = (ApplicationInfo) XposedHelpers.getObjectField(loadApk, "mApplicationInfo");
appinfoInLoadedApk.sourceDir = originApkSourceDir;
XposedHelpers.setObjectField(loadApk, "mAppDir", originApkSourceDir);
XposedHelpers.setObjectField(loadApk, "mResDir", originApkSourceDir);
XposedHelpers.setObjectField(loadApk, "mResources", null);
Resources resources = (Resources) XposedHelpers.callMethod(loadApk, "getResources", currentActivityThread());
if (resources != null) {
XposedHelpers.setObjectField(contextImpl, "mResources", resources);
}
//替换之后,再也无法访问容器apk里面的资源了,容器中的所有资源全部被替换为原始apk的资源
loadResources(originApkSourceDir);
}
在我的壳运行的时候,初始化xposed框架环境,然后加载指定xposed模块,然后根据一般apk的壳的套路,将控制权交给原始apk。
2. 留两个插桩,分别等待xposed模块apk和原始apk归位
3. 清单文件,从原始apk文件复制过来,放到壳对应的apk下面
AndroidManifest.xml中很多配置,都是需要Android系统可见的,如果不提前定义,那么很多功能无法使用,或者使用virtualAPP这样提前弄好插桩。我在想如果我只是正对于单个app做AOP的话,其实这个时候配置都是已知的了,所以把所有定义全部copy过来。
4. 重新打包
使用我们自己的的壳作为入口apk,那么dex文件需要使用我们提供的壳的dex,但是AndroidManifes.xml需要使用我们将要包装那个apk的。AndroidManifes.xml里面的代码入口(application),需要修正到我们的壳入口。当然资源也需要使用原始apk的,毕竟我们的壳目前来看没有啥资源需要用的。
这些资源拼凑好之后,用apktool重新打包(没法用AndroidStudio,编译都不会过)。由于我们不会修改代码,所以apktool不会走到smali这一层,所以其实这里用apktool,我觉得还是很稳定的。
5. 签名、发布、安装
其实,我们就是把三个apk揉成一个apk。
原始apk,由于有壳或者各种混淆,各种反逆向导致很难重打包,所以我们并不会真正对原始apk进行拆包。
xposed模块apk,他是作用在原始apk上面的,但是必须使用原始apk的进程身份注入。在没有xposed的环境下,做不到,所以现在这个工种交给了我们的壳(driver)。
ratel-driver,我们的壳apk。他将会是产出的apk的代码入口点,他会在坏事情做好之后,加载原始apk。
由于其实使用沙盒的方式run起来的,并没有对原始apk进行拆包,所以我觉得不需要考虑过签名问题,即使有xposed都进来了,还不能把他给栏了么。
产生的新包的packageName和原始apk的package相同,所以签名后安装的时候,肯定和之前安装在系统的那个apk的证书不一致,把之前那个卸了再装这个就好了。
另外,我现在只是把一个demo跑起来了,还没有各种验证。估计还是有些兼容性问题的。
我把demo开源了,代码地址:
https://gitee.com/virjar/ratel
再讲讲效果,测试的apk:
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
protected void onResume() {
super.onResume();
TextView textView = findViewById(R.id.indexTextView);
textView.setText(text());
}
private String text() {
return "原始文本";
}
}
会首先显示:“原始文本”这个文案:
public class DemoAppHooker implements IXposedHookLoadPackage {
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
Class<?> aClass = XposedHelpers.findClass("com.virjar.ratel.demoapp.MainActivity", lpparam.classLoader);
XposedHelpers.findAndHookMethod(aClass, "text", XC_MethodReplacement.returnConstant("被hook后的文本"));
}
}
然后我hook了他,把文案给改了。最好,打开app,如下图:
手机刚刚装了一个新系统,没有root,没有xposed环境。
放一下github地址:
https://github.com/virjar/ratel
- End -
看雪ID:virjar
https://bbs.pediy.com/user-791488.htm
本文由看雪论坛 virjar 原创
转载请注明来自看雪社区
热门图书推荐:
热门技术文章推荐:
公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com