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

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

2021-4-1 12:54
24169

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

 

我想到了两个方案

  • 方案一. 将frida-gumjs编译进一个riru插件,通过面具刷入手机,这样每个进程都具备加载frida js的能力;
  • 方案二. 使用xposed,在app启动时将frida-gumjs载入app进程内;

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

 

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

原理实现

编译frida-gumjs为so

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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脚本

再添加一个jni入口函数

1
2
3
4
5
6
7
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);
}

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

1
2
3
4
5
6
7
8
9
10
# 编译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})

这样就得到了libxcubebase.so

使用xposed让app加载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的核心代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
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);
        }
    });
}

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cmi:/data/local/tmp/xcube # ls -l
total 10
drwxrwxrwx 2 root root 3488 2021-03-30 18:28 arm64-v8a
drwxrwxrwx 2 root root 3488 2021-03-30 18:28 armeabi-v7a
-rwxrwxrwx 1 root root  348 2021-03-30 18:28 xcube.yaml
cmi:/data/local/tmp/xcube # ls armeabi-v7a/ -l
total 51232
-rwxrwxrwx 1 root root   144692 2021-03-30 18:28 libhellofrida.so
-rwxrwxrwx 1 root root 52161248 2021-03-30 18:28 libxcubebase.so
-rwxrwxrwx 1 root root    95564 2021-03-30 18:28 shellcmd
cmi:/data/local/tmp/xcube # cat xcube.yaml
# 是否开启xposed加载frida js hook 引擎功能
active: true
 
# 要hook的app包名和要使用的js脚本
packageConfigList:
 com.tencent.mtt: /data/local/tmp/mtt.js
 org.xtgo.xcube.forxcubetest: /data/local/tmp/myscript.js
 com.tencent.zgqyz: /data/local/tmp/myscript.js
 org.xtgo.xcube.verification: /data/local/tmp/myscript.js

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

使用

  1. 安装Xcubebase.apk,启动该程序,点击初始化按钮,需要赋予su权限
  2. 在xposed中启用该插件,重启应用
  3. 修改xcube.yaml,指定你想hook的apk和脚本
  4. 启动目标app即可在logcat中看到js脚本的输出内容

feature

  1. frida hook java时稳定性不好,尤其是锁屏再回来。这个问题是frida本身java hook方案的问题,暂时无解。因此推荐javahook用xposed ,nativehook 用frida脚本
  2. 目前这个框架中,frida脚本目前还不能动态更新,只能重启app脚本才能生

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


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

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

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

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