frida gadget

Android

1.下载gadget

1
2
frida-gadget-xxxxx-android-arm.so.xz
frida-gadget-xxxxx-android-arm64.so.xz

2. APK解包

1
apktool d -r xx.apk  //-r 不反编译资源文件

3.添加lib和修改smali

3.1.1 将gadget.so拷贝到解包后lib/xxx/目录下

3.1.2 修改入口smali的construct method

3.1.3 将locals加1

3.1.4 寄存器名称v0的数字使用locals数量的最后一个

1
2
const-string v0, "frida-gadget"
invoke-static {v0}, Ljava/lang/System;->loadLibrary(Ljava/lang/String;)V

4.添加权限

在AndroidManifest.xml中添加权限,一般的APK都会申请下面两个权限
,所以不用添加

1
2
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission>

5.打包

直接apktool b xxx打包经常会报资源错误,可以尝试以下方式:

  • 直接删除出错的layout文件

  • 修改出错的layout文件

  • 不解包资源文件,也就是在解包的时候加入-r选项
    通过-f -p 选项使得打包时忽略这些资源
    apktool b -f -p . xxx -o output.apk

    -f => –force-all
    -p => resource path

6.签名/安装

iOS

1.下载gadget

1
frida-gadget-15.0.18-ios-universal.dylib.gz

2.脱壳后的应用包

3.新建MonkeyApp 工程

4. 引入 FridaGadget.dylib

5.编译运行

其他方式

RIru加载gadget

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
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <jni.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include<pthread.h>
#include <unistd.h>
#include <android/log.h>
#include <dlfcn.h>

extern "C" {

#define EXPORT __attribute__((visibility("default"))) __attribute__((used))
#define LOG_TAG "C_COLA"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
static char* loadPkgNameStr = NULL;

char *loadPkgName(JNIEnv *env, jstring appDataDir) {
if (!appDataDir)
return NULL;

const char *app_data_dir = env->GetStringUTFChars(appDataDir, NULL);
int user = 0;
static char package_name[256];
if (sscanf(app_data_dir, "/data/%*[^/]/%d/%s", &user, package_name) != 2) {
if (sscanf(app_data_dir, "/data/%*[^/]/%s", package_name) != 1) {
package_name[0] = '\0';
return NULL;
}
}
env->ReleaseStringUTFChars(appDataDir, app_data_dir);
return package_name;
}

void getPkgNameFromFile(char* hookPackageName){
FILE* file = NULL;
file = fopen("/data/local/tmp/pkg_name", "r");
if (file != NULL) {
fgets(hookPackageName,200,file);
fclose(file);
}
}

void *hack_thread(void *arg)
{

LOGD("hook thread :%d", gettid());
sleep(0);//部分APP运行后会立即检测,延迟注入可以绕过检测
const char* soPath_32 = "/system/lib/libcola.so";
const char* soPath_64 = "/system/lib64/libcola.so";
void* handle_64 = dlopen( soPath_64, RTLD_LAZY );
if( !handle_64 )
{
LOGD("[%s](%d) dlopen 64bit lib get error: %s\n", __FILE__, __LINE__, dlerror());
void* handle_32 = dlopen( soPath_32, RTLD_LAZY );
if( !handle_32 )
{
LOGD("[%s](%d) dlopen 32bit lib get error: %s\n", __FILE__, __LINE__, dlerror());
}

}
LOGD("so inject finish");
return NULL;
}



EXPORT void nativeForkAndSpecializePre(
JNIEnv *env, jclass clazz, jint *_uid, jint *gid, jintArray *gids, jint *runtimeFlags,
jobjectArray *rlimits, jint *mountExternal, jstring *seInfo, jstring *niceName,
jintArray *fdsToClose, jintArray *fdsToIgnore, jboolean *is_child_zygote,
jstring *instructionSet, jstring *appDataDir, jboolean *isTopApp, jobjectArray *pkgDataInfoList,
jobjectArray *whitelistedDataInfoList, jboolean *bindMountAppDataDirs, jboolean *bindMountAppStorageDirs) {

loadPkgNameStr = loadPkgName(env, *appDataDir);

}

EXPORT int nativeForkAndSpecializePost(JNIEnv *env, jclass clazz, jint res) {
if (res == 0) {
// in app process
if (loadPkgNameStr != NULL) {
char hookPkgName[200] = " ";
getPkgNameFromFile(hookPkgName);
if(!strncmp(hookPkgName,loadPkgNameStr,strlen(loadPkgNameStr))){
int ret;
pthread_t ntid;
// LOGD("begin sleep 15000ms");
// sleep(15000);
if((ret = pthread_create(&ntid, NULL, hack_thread, NULL))) {
LOGD("can't create thread: %s\n", strerror(ret));
}
}
}
} else {
// in zygote process, res is child pid
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
}
return 0;
}

EXPORT __attribute__((visibility("default"))) void specializeAppProcessPre(
JNIEnv *env, jclass clazz, jint *_uid, jint *gid, jintArray *gids, jint *runtimeFlags,
jobjectArray *rlimits, jint *mountExternal, jstring *seInfo, jstring *niceName,
jboolean *startChildZygote, jstring *instructionSet, jstring *appDataDir,
jboolean *isTopApp, jobjectArray *pkgDataInfoList, jobjectArray *whitelistedDataInfoList,
jboolean *bindMountAppDataDirs, jboolean *bindMountAppStorageDirs) {
// added from Android 10, but disabled at least in Google Pixel devices
}

EXPORT __attribute__((visibility("default"))) int specializeAppProcessPost(
JNIEnv *env, jclass clazz) {
// added from Android 10, but disabled at least in Google Pixel devices
return 0;
}

EXPORT void nativeForkSystemServerPre(
JNIEnv *env, jclass clazz, uid_t *uid, gid_t *gid, jintArray *gids, jint *runtimeFlags,
jobjectArray *rlimits, jlong *permittedCapabilities, jlong *effectiveCapabilities) {

}

EXPORT int nativeForkSystemServerPost(JNIEnv *env, jclass clazz, jint res) {
if (res == 0) {
// in system server process
} else {
// in zygote process, res is child pid
// don't print log here, see https://github.com/RikkaApps/Riru/blob/77adfd6a4a6a81bfd20569c910bc4854f2f84f5e/riru-core/jni/main/jni_native_method.cpp#L55-L66
}
return 0;
}

EXPORT int shouldSkipUid(int uid) {
// by default, Riru only call module functions in "normal app processes" (10000 <= uid % 100000 <= 19999)
// false = don't skip
return false;
}

EXPORT void onModuleLoaded() {
// called when the shared library of Riru core is loaded
}
}

ROM定制加载gadget

APP启动流程的任意一点,都可以加载gadget.so

1
2
3
dlopen
System.load
System.loadLibrary

objection自动添加gadget

1
2
objection patchapk -s  xxxxx.apk 
objection patchipa -s xxxxx.ipa