去年下半年由于业务的需求,希望能够将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);
}
add_library(gumjs STATIC IMPORTED)
set_target_properties(gumjs
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}
/
libs
/
${ANDROID_ABI}
/
libfrida
-
gumjs.a)
add_library(xcubebase SHARED frida
-
gumjs.cpp NativeEntry.cpp)
target_link_libraries(xcubebase gumjs ${log
-
lib})
add_library(gumjs STATIC IMPORTED)
set_target_properties(gumjs
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}
/
libs
/
${ANDROID_ABI}
/
libfrida
-
gumjs.a)
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主进程一次
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2021-4-1 15:29
被svengong编辑
,原因: 修改标题