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

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

2021-4-1 12:54
9064

去年下半年由于业务的需求,希望能够将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


[培训] 优秀毕业生寄语:恭喜id咸鱼炒白菜拿到远超3W月薪的offer,《安卓高级研修班》火热招生!!!

最后于 2021-4-1 15:29 被svengong编辑 ,原因: 修改标题
收藏
点赞5
打赏
分享
最新回复 (11)
雪    币: 8344
活跃值: 活跃值 (3427)
能力值: ( LV9,RANK:163 )
在线值:
发帖
回帖
粉丝
neilwu 活跃值 1 2021-4-1 14:03
2
0
tql 在不刷机的情况下 提供了frida持久化的方案 
雪    币: 272
活跃值: 活跃值 (158)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
linden 活跃值 2021-4-1 14:30
3
0
快速试用了一下,干货给力!
雪    币: 26
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
mb_yvvzfcdo 活跃值 2021-4-1 15:43
4
0
这个和寒冰的差不多看着  不过更简洁了
雪    币: 361
活跃值: 活跃值 (668)
能力值: ( LV4,RANK:44 )
在线值:
发帖
回帖
粉丝
xjklewh 活跃值 2021-4-1 20:47
5
0
大佬
雪    币: 4767
活跃值: 活跃值 (2423)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
挤蹭菌衣 活跃值 1 2021-4-2 16:51
6
0
强大  学习了
雪    币: 2275
活跃值: 活跃值 (599)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
十年后 活跃值 2021-4-3 08:57
7
0
牛x,谢谢分享!
雪    币: 2238
活跃值: 活跃值 (1468)
能力值: ( LV6,RANK:95 )
在线值:
发帖
回帖
粉丝
number_Z 活跃值 2 2021-4-3 11:22
8
0
tql 膜拜大佬
雪    币: 101
活跃值: 活跃值 (310)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xxRea 活跃值 2021-4-26 09:07
9
0
tql 膜拜大佬
雪    币: 611
活跃值: 活跃值 (573)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mb_foyotena 活跃值 2021-4-26 14:06
10
0
frida本意就是为了动态调试吧
雪    币: 233
活跃值: 活跃值 (581)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
kakasasa 活跃值 2021-7-1 14:11
11
0
感谢分享
雪    币: 213
活跃值: 活跃值 (644)
能力值: ( LV3,RANK:27 )
在线值:
发帖
回帖
粉丝
svengong 活跃值 2021-7-10 19:24
12
0
mb_foyotena frida本意就是为了动态调试吧
看样子用gadget更好
游客
登录 | 注册 方可回帖
返回