首页
社区
课程
招聘
[原创]Frida持久化方案(Xcube)之方案二——基于xposed
发表于: 2021-4-1 12:54 26125

[原创]Frida持久化方案(Xcube)之方案二——基于xposed

2021-4-1 12:54
26125

去年下半年由于业务的需求,希望能够将frida的js脚本持久化到手机,只要APP启动就可以自行运行指定的脚本;一来希望可以脱离pc,二来可以为xposed增加没有的native hook能力,三就是部署以后方便通过命令行来更新脚本;

我想到了两个方案

好消息是两个方案目前都已经实现。方案一由于是去年完成的,使用的riru23版本的模板,现在的riru版本号已经25了,不知道是否能够兼容;再有就是开发比较早,几位同学觉得上手不容易,所以早早结束了开发,虽然功能完整但未达预期。因此今天先介绍方案二并开源。

我们大的产品叫做Xcube,而这个模块就叫做xcubebase;

frida-gumjs是frida武器库的js封装;官方提供了libfrida-gumjs.a的下载,可惜的是文档资料极少,代码看到吐血也只是摸清了一些关键函数的使用;官方提供了这个库的头文件,我们平时hook用的js脚本都可以用这个库来运行;
frida-gumjs.cpp中的代码如下:

再添加一个jni入口函数

将以上libfrida-gumjs.a和相关代码使用ndk编译为我们app可以加载的so

这样就得到了libxcubebase.so

为了方便使用,我把libxcubebase.so集成到了xposed的插件中,插件上有初始化功能,可以一键将libxcubebase.so、配置文件xcube.yaml导入到/data/local/tmp。
实现比较麻烦基本就是先从assets拷贝到/data/app/packagename/cache目录下,再复制到/data/local/tmp/。复制到/data/local/tmp这一步需要su权限,所以如果有提示su权限需要允许;
下面的代码是app加载so、执行js的核心代码

xcube.yaml是配置文件,规定了我们要hook的app及其要运行的js脚本,需要自行修改

源码今天会放出来。稍等一个午休
代码出来了,工程里libfrida-gumjs.a太大传不上去,我给压缩了,自己解压一下
https://github.com/svengong/xcubebase.git

 
 
gumjsHook(const char *scriptpath) {
    ...
    ...
    script = gum_script_backend_create_sync(backend, "example", js, cancellable, &error);
    g_assert (error == NULL);
    gum_script_set_message_handler(script, on_message, NULL, NULL);
    gum_script_load_sync(script, cancellable);
    //执行脚本
    context = g_main_context_get_thread_default();
    while (g_main_context_pending(context))
        g_main_context_iteration(context, FALSE);
...
}
gum_script_backend_create_sync中的js参数就是我们的hook脚本
gumjsHook(const char *scriptpath) {
    ...
    ...
    script = gum_script_backend_create_sync(backend, "example", js, cancellable, &error);
    g_assert (error == NULL);
    gum_script_set_message_handler(script, on_message, NULL, NULL);
    gum_script_load_sync(script, cancellable);
    //执行脚本
    context = g_main_context_get_thread_default();
    while (g_main_context_pending(context))
        g_main_context_iteration(context, FALSE);
...
}
gum_script_backend_create_sync中的js参数就是我们的hook脚本
extern "C"
JNIEXPORT void JNICALL Java_org_xtgo_xcube_base_XcubeBase_gumjsHook(
        JNIEnv *env, jclass clazz, jstring script) {
    const char *scriptpath = env->GetStringUTFChars(script, 0);
    gumjsHook(scriptpath);
    env->ReleaseStringUTFChars(script, scriptpath);
}
extern "C"
JNIEXPORT void JNICALL Java_org_xtgo_xcube_base_XcubeBase_gumjsHook(
        JNIEnv *env, jclass clazz, jstring script) {
    const char *scriptpath = env->GetStringUTFChars(script, 0);
    gumjsHook(scriptpath);
    env->ReleaseStringUTFChars(script, scriptpath);
}
# 编译gumjs
add_library(gumjs STATIC IMPORTED)
set_target_properties(gumjs
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libfrida-gumjs.a)
 
 
# 添加到apk
add_library(xcubebase SHARED frida-gumjs.cpp NativeEntry.cpp)
target_link_libraries(xcubebase gumjs ${log-lib})
# 编译gumjs
add_library(gumjs STATIC IMPORTED)
set_target_properties(gumjs
        PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libfrida-gumjs.a)
 
 
# 添加到apk
add_library(xcubebase SHARED frida-gumjs.cpp NativeEntry.cpp)
target_link_libraries(xcubebase gumjs ${log-lib})
private static String configPath = "/data/local/tmp/xcube/xcube.yaml";
public static boolean hooked = false;
 
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
    String remoteName = Utils.getRemoteName();
    XcubeConfig config = new XcubeConfig(configPath);
    if (!config.active || !config.contains(loadPackageParam.packageName)) {
        return;
    }
    String script = config.getScriptPath(loadPackageParam.packageName);
    Log.e(TAG, "current app packageName : " + loadPackageParam.packageName + ":" + remoteName);
 
    XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            Log.d(TAG, "attach beforeHookedMethod script:" + script);
            if (hooked || !remoteName.isEmpty()) {
                //只hook主进程一次
                return;
            }
            try {
                Context base = (Context) param.args[0];
                File toPath = base.getDir("libs", Context.MODE_PRIVATE);
                String libpath = "/data/local/tmp/xcube/";
                String ABI = android.os.Process.is64Bit() ? "arm64-v8a" : "armeabi-v7a";
                //System.loadlibrary使用的classloader是当前classloader,而param.args[0]是目标应用的classloader,二者不同
                Log.d(TAG, "attach beforeHookedMethod toPath:" + toPath);
                LoadLibraryUtil.loadSoFile(this.getClass().getClassLoader(), libpath + ABI, toPath);
                System.loadLibrary("xcubebase");
                Log.d(TAG, "script path :" + script);
                gumjsHook(script);
                hooked = true;
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
 
        }
 
 
    });
    XposedHelpers.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodHook() {
        @Override
        protected void afterHookedMethod(MethodHookParam param) throws Throwable {
            ((Activity) param.thisObject).getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
    });
}
private static String configPath = "/data/local/tmp/xcube/xcube.yaml";
public static boolean hooked = false;
 
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam loadPackageParam) throws Throwable {
    String remoteName = Utils.getRemoteName();
    XcubeConfig config = new XcubeConfig(configPath);
    if (!config.active || !config.contains(loadPackageParam.packageName)) {
        return;
    }
    String script = config.getScriptPath(loadPackageParam.packageName);
    Log.e(TAG, "current app packageName : " + loadPackageParam.packageName + ":" + remoteName);
 
    XposedHelpers.findAndHookMethod(Application.class, "attach", Context.class, new XC_MethodHook() {
        @Override
        protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
            Log.d(TAG, "attach beforeHookedMethod script:" + script);
            if (hooked || !remoteName.isEmpty()) {
                //只hook主进程一次

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2021-4-1 15:29 被svengong编辑 ,原因: 修改标题
收藏
免费 11
支持
分享
最新回复 (19)
雪    币: 10941
活跃值: (7324)
能力值: ( LV12,RANK:219 )
在线值:
发帖
回帖
粉丝
2
tql 在不刷机的情况下 提供了frida持久化的方案 
2021-4-1 14:03
0
雪    币: 273
活跃值: (191)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
3
快速试用了一下,干货给力!
2021-4-1 14:30
0
雪    币: 334
活跃值: (392)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
这个和寒冰的差不多看着  不过更简洁了
2021-4-1 15:43
0
雪    币: 403
活跃值: (798)
能力值: ( LV4,RANK:58 )
在线值:
发帖
回帖
粉丝
5
大佬
2021-4-1 20:47
0
雪    币: 5235
活跃值: (3260)
能力值: ( LV10,RANK:175 )
在线值:
发帖
回帖
粉丝
6
强大  学习了
2021-4-2 16:51
0
雪    币: 8206
活跃值: (4161)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
牛x,谢谢分享!
2021-4-3 08:57
0
雪    币: 2244
活跃值: (1901)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
8
tql 膜拜大佬
2021-4-3 11:22
0
雪    币: 120
活跃值: (1597)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
tql 膜拜大佬
2021-4-26 09:07
0
雪    币: 477
活跃值: (1412)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
frida本意就是为了动态调试吧
2021-4-26 14:06
0
雪    币: 576
活跃值: (2035)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
感谢分享
2021-7-1 14:11
0
雪    币: 225
活跃值: (1089)
能力值: ( LV3,RANK:27 )
在线值:
发帖
回帖
粉丝
12
mb_foyotena frida本意就是为了动态调试吧
看样子用gadget更好
2021-7-10 19:24
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
大佬我问下,我日志应该在哪查看?我用DDMS里面的logcat也看不到我在js写的打印日志,VS code里面也没有?请问下我这个是什么问题?
2021-11-24 18:01
0
雪    币: 220
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
 我跟了下代码,没回调,beforeHookedMethod执行进来hook都没hook到,这个是什么问题呢
2021-11-24 18:50
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
15
老哥我这方案支持不 我测试貌似没有执行 
用的VirtualApp :https://github.com/ServenScorpion/VirtualApp 

va本身支持xposed,并且我给了xcub 权限以及xposed启用

测试下来是没生效
2022-1-11 17:06
0
雪    币: 199
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
16
大佬 请问下         LoadLibraryUtil.loadSoFile(this.getClass().getClassLoader(), libpath + ABI, toPath); 这行的目的是啥?  去掉就不行了  我没有一样名字的so
2022-6-3 10:19
0
雪    币: 120
活跃值: (1597)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
感谢分析
2023-4-5 22:58
0
雪    币: 3070
活跃值: (30876)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
18
感谢分享
2023-4-6 00:08
1
雪    币: 174
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
19
感谢感谢分享,我们是用的VA没有生效、、
2023-4-12 09:09
0
雪    币: 893
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
感谢大佬,成功用上了
2023-6-21 17:48
0
游客
登录 | 注册 方可回帖
返回
//