首页
社区
课程
招聘
[原创]从0开始实现一个简易的主动调用框架
发表于: 2021-10-29 14:32 26404

[原创]从0开始实现一个简易的主动调用框架

2021-10-29 14:32
26404

<!-- @import "[TOC]" {cmd="toc" depthFrom=1 depthTo=6 orderedList=false} -->

<!-- code_chunk_output -->

<!-- /code_chunk_output -->

根据寒冰老师的frida manage和0.0.0.0大佬的内置frida rom,发现可以直接用System.load加绝对路径的方式将so加载到内存中,那么这样我们就可以在app启动之前执行代码,那么就可以执行hook等一系列操作了,代码都能自己定制那么就没那么多特征可以检测了

这里依然以ActivityThread中的函数handleBindApplication作为加载时机,我这里想到一个简单的方案,就是将sdcard中的so复制到程序的私有目录,然后修改文件的读写权限,通过System.load来用绝对路径加载so,然后通过JNI_Onload或init来执行代码,这里贴一下寒冰老师分析handleBindApplication的代码(太清楚了)
https://bbs.pediy.com/thread-252630.htm

我们可以从这里找一个时机来加载我们的so,我这里选择了创建ContextImpl对象之后直接加载我们的so,这里代码大部分来自0.0.0.0大佬的内置frida,

那么我们的so可以通过System.load加载到内存中了,我们要如何主动调用函数呢?其实就是在于我们要如何在so层执行java或者native方法,所以前面我写了inlinehook,javahook,都可以集成到这套方案里面,只要将我们制作的so放到/sdcard/r0.so,那么所有的逻辑都可以由我们来控制,比如我这里举一个例子,能否在每个程序打开前输出其进程名,这里我们就可以通过反射的方式来执行java中的函数,看一下效果

进入app目录,将app编译后解压,将我们的so送入指定目录当然要打开sdcard权限,否则读不了我们的插件

随便打开一个app看一下效果,不错还能用

由于部分Native函数的参数较难构造,所以这里可以hook住,它的注册函数然后通过更改参数的方式来进行主动调用,使用之前搞得inlinehook框架,
https://bbs.pediy.com/thread-269757.htm

然后再过滤函数名,如果是我们的函数就改掉它的注册值,由于我的inlinehook框架不完善所以这里暂且只能使用x18做栈的传值的方式更改参数

看一下效果,嗯效果不错,改完之后主动调用的结果为11111标签下的值和之前的值不一样

可以成功的调用任意的Java函数和Native函数(当然so里面的函数也是和Native函数一样),Native函数有一个难点就是要在它注册之后再调用,所以我直接选择了hook libart.so 中的RegisterNative函数,其实比较难解决的也是classloader的问题,有的动态加载的dex需要很困难才能拿到jclass(当然我们也可以模仿frida实现一个枚举classloader也是很简单的),而且这个插件的写法过于麻烦,但是稳定性是较高的而且完全自己定制就可以随意的更改指纹比较难检测到

https://bbs.pediy.com/thread-266767.htm
https://bbs.pediy.com/thread-252630.htm

 
private void handleBindApplication(AppBindData data) {
    //step 1: 创建LoadedApk对象
    data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
    ...
    //step 2: 创建ContextImpl对象;
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
 
    //step 3: 创建Instrumentation
    mInstrumentation = new Instrumentation();
 
    //step 4: 创建Application对象;在makeApplication函数中调用了newApplication,在该函数中又调用了app.attach(context),在attach函数中调用了Application.attachBaseContext函数
    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    mInitialApplication = app;
 
    //step 5: 安装providers
    List<ProviderInfo> providers = data.providers;
    installContentProviders(app, providers);
 
    //step 6: 执行Application.Create回调
    mInstrumentation.callApplicationOnCreate(app);
}
private void handleBindApplication(AppBindData data) {
    //step 1: 创建LoadedApk对象
    data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
    ...
    //step 2: 创建ContextImpl对象;
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
 
    //step 3: 创建Instrumentation
    mInstrumentation = new Instrumentation();
 
    //step 4: 创建Application对象;在makeApplication函数中调用了newApplication,在该函数中又调用了app.attach(context),在attach函数中调用了Application.attachBaseContext函数
    Application app = data.info.makeApplication(data.restrictedBackupMode, null);
    mInitialApplication = app;
 
    //step 5: 安装providers
    List<ProviderInfo> providers = data.providers;
    installContentProviders(app, providers);
 
    //step 6: 执行Application.Create回调
    mInstrumentation.callApplicationOnCreate(app);
}
    public static void mycopy(String srcFileName, String trcFileName) {
        InputStream in = null;
        OutputStream out = null;
        try {
            // in = File.open(srcFileName);
            in = new FileInputStream(srcFileName);
            out = new FileOutputStream(trcFileName);
            byte[] bytes = new byte[1024];
            int i;
            while ((i = in.read(bytes)) != -1)
                out.write(bytes, 0, i);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null)
                    in.close();
                if (out != null){
                    out.flush();
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
 
        }
    }
private void handleBindApplication(AppBindData data) {
......
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
    ContextImpl context = appContext;
    ActivityManager mAm = (ActivityManager) context.getSystemService("activity");
    String activity_packageName = mAm.getRunningTasks(1).get(0).topActivity.getPackageName();//获得私有目录
    if (activity_packageName.indexOf("com.android") < 0) {//不包括系统目录
        String tagPath = "/data/data/" + activity_packageName + "/r0.so";//64位so的目录
        String tagPath2 = "/data/data/" + activity_packageName + "/r032.so";//32位的so目录
        File file1 = new File(tagPath);
        File file2 = new File(tagPath2);
        mycopy("/sdcard/r0.so", tagPath);//复制so到私有目录
        mycopy("/sdcard/r032.so", tagPath2);
        int perm = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO;
        FileUtils.setPermissions(tagPath, perm, -1, -1);//将权限改为777
        FileUtils.setPermissions(tagPath2, perm, -1, -1);
// com.android.systemui
    if (file1.exists()) {
        Log.e("r0ysue", System.getProperty("os.arch"));//判断是64位还是32
        if (System.getProperty("os.arch").indexOf("64") >= 0) {
                System.load(tagPath);
                file1.delete();//用完就删否则不会更新
        } else {
                System.load(tagPath2);
                file2.delete();
            }
 
}
}
......
}
    public static void mycopy(String srcFileName, String trcFileName) {
        InputStream in = null;
        OutputStream out = null;
        try {
            // in = File.open(srcFileName);
            in = new FileInputStream(srcFileName);
            out = new FileOutputStream(trcFileName);
            byte[] bytes = new byte[1024];
            int i;
            while ((i = in.read(bytes)) != -1)
                out.write(bytes, 0, i);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null)
                    in.close();
                if (out != null){
                    out.flush();
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
 
        }
    }
private void handleBindApplication(AppBindData data) {
......
    final ContextImpl appContext = ContextImpl.createAppContext(this, data.info);
    ContextImpl context = appContext;
    ActivityManager mAm = (ActivityManager) context.getSystemService("activity");
    String activity_packageName = mAm.getRunningTasks(1).get(0).topActivity.getPackageName();//获得私有目录
    if (activity_packageName.indexOf("com.android") < 0) {//不包括系统目录
        String tagPath = "/data/data/" + activity_packageName + "/r0.so";//64位so的目录
        String tagPath2 = "/data/data/" + activity_packageName + "/r032.so";//32位的so目录
        File file1 = new File(tagPath);
        File file2 = new File(tagPath2);
        mycopy("/sdcard/r0.so", tagPath);//复制so到私有目录
        mycopy("/sdcard/r032.so", tagPath2);
        int perm = FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO;
        FileUtils.setPermissions(tagPath, perm, -1, -1);//将权限改为777
        FileUtils.setPermissions(tagPath2, perm, -1, -1);
// com.android.systemui
    if (file1.exists()) {
        Log.e("r0ysue", System.getProperty("os.arch"));//判断是64位还是32
        if (System.getProperty("os.arch").indexOf("64") >= 0) {
                System.load(tagPath);
                file1.delete();//用完就删否则不会更新
        } else {
                System.load(tagPath2);
                file2.delete();
            }
 
}
}
......
}
const char * getprocessname(JNIEnv* env){
    jclass ActivityThread=env->FindClass("android/app/ActivityThread");
    jmethodID currentProcessName=env->GetStaticMethodID(ActivityThread,"currentProcessName","()Ljava/lang/String;");
    jstring name= static_cast<jstring>(env->CallStaticObjectMethod(ActivityThread,currentProcessName));
    const char * name1=env->GetStringUTFChars(name,0);
    return name1;
 
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) {
    JNIEnv* env= nullptr;
    vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4);
    const char* myname= getprocessname(env);
    __android_log_print(6,"r0ysue","i am from %s",myname);
        return JNI_VERSION_1_4;
}
const char * getprocessname(JNIEnv* env){
    jclass ActivityThread=env->FindClass("android/app/ActivityThread");
    jmethodID currentProcessName=env->GetStaticMethodID(ActivityThread,"currentProcessName","()Ljava/lang/String;");
    jstring name= static_cast<jstring>(env->CallStaticObjectMethod(ActivityThread,currentProcessName));
    const char * name1=env->GetStringUTFChars(name,0);
    return name1;
 
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) {
    JNIEnv* env= nullptr;
    vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_4);
    const char* myname= getprocessname(env);
    __android_log_print(6,"r0ysue","i am from %s",myname);
        return JNI_VERSION_1_4;
}
unzip app-debug.apk
cd lib/arm64-v8a/
adb push libnative-lib.so /sdcard/r0.so
unzip app-debug.apk
cd lib/arm64-v8a/
adb push libnative-lib.so /sdcard/r0.so
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) {
 
 
    mainfun("_ZN3art9ArtMethod14RegisterNativeEPKvb", "libart.so",
            reinterpret_cast<void *>(regist));
 
    return JNI_VERSION_1_4;
 
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) {
 
 
    mainfun("_ZN3art9ArtMethod14RegisterNativeEPKvb", "libart.so",
            reinterpret_cast<void *>(regist));
 
    return JNI_VERSION_1_4;
 
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 4
支持
分享
最新回复 (1)
雪    币: 100
活跃值: (165)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
不错学习了
2021-11-18 18:16
0
游客
登录 | 注册 方可回帖
返回
//