过签名校验(2) -- MT 的 IO 重定向实践

查看 156|回复 11
作者:2016976438   
过签名校验(2) -- MT 的 IO 重定向
本来是想照着敲一遍 MT 的 github 上面的代码 ,但个人c ++ 以及 arm 汇编 实在有点吃力。说白了就是个人水平不够。
所以砸门的目标就变成了.  
[ol]
  • 能看懂就行
  • 可以编译 mt github源码 成 apk
  • 复制 so 和 classX.dex 到我们要去签名的应用上
    [/ol]
    ​     
    运行 MT 的 去签名的 github 源码
    [ol]
  • 先从github下下来
    [/ol]


    2023-01-04_195210.png (119.57 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传

    网络差的建议通过油猴转到网盘下载
  • 导入到android studio
    [/ol]


    2023-01-04_195503.png (31.44 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传

    导入后您可以看到两个模块
    app (测试demo)  killer(去签模块)
    运行后 app 您应该能够得到


    2023-01-04_201558.png (17.66 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传

    使用zip 解压 其中重要的就是
    app-debug.apk/lib/  里面的 so hook 的 IO 重定向
    classesXXX.dex   这个得看 KillerApplication 在哪里了
    了解Killer 模块
    我们主要目的是看 killer 去签的那个模块 , 先从 KillerApplication
    public class KillerApplication extends Application {
       static {
            String packageName = "bin.mt.signature";
            killOpen(packageName);
        }
         private static void killOpen(String packageName) {
            try {
                //1.加载 so 层 hook
                System.loadLibrary("SignatureKiller");
            } catch (Throwable e) {
                System.err.println("Load SignatureKiller library failed");
                return;
            }
            //2. 通过 当前 /proc/self/maps 获取apk路径
            String apkPath = getApkPath(packageName);
            if (apkPath == null) {
                System.err.println("Get apk path failed");
                return;
            }
            //3. 获取的路径是 /data/app/{PackageName}-xxx/base.apk 这个是当前 apk文件
            File apkFile = new File(apkPath);
            //4.将当前 apk 中 origin.apk 文件提取出去 作为我们的 IO重定向文件
            File repFile = new File("/data/data/" + packageName + "/origin.apk");
            //5. 由于 apk实际是个 zip 所以我们可以解压缩 提取 origin.apk
            try (ZipFile zipFile = new ZipFile(apkFile)) {
                //6. apkFile  抽取 origin.apk  到 repFile变量 指定的位置
                String name = "assets/SignatureKiller/origin.apk";
                ZipEntry entry = zipFile.getEntry(name);
                if (entry == null) {
                    System.err.println("Entry not found: " + name);
                    return;
                }
                //7.把 /data/app/{PackageName}-xxx/base.apk/origin.apk 转换到 /data/data/packageName/origin.apk
                if (!repFile.exists() || repFile.length() != entry.getSize()) {
                    try (InputStream is = zipFile.getInputStream(entry); OutputStream os = new FileOutputStream(repFile)) {
                        byte[] buf = new byte[102400];
                        int len;
                        while ((len = is.read(buf)) != -1) {
                            os.write(buf, 0, len);
                        }
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
            //8. 传入到 底层 so hook
            hookApkPath(apkFile.getAbsolutePath(), repFile.getAbsolutePath());
        }
         private static native void hookApkPath(String apkPath, String repPath);
    }   
    KillerApplication  Java层 主要是 获取 当前 app 里面 我们放入的原始的  origin.apk 文件 , origin.apk 是没有改过签的正常 app
    在我们编译的apk 的 :  app-debug.apk/assets/SignatureKiller/origin.apk
    但是我们 android 安装后 路径会变成: /data/app/{包名}-XXX/ base.apk


    2023-01-04_202445.png (95.25 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传

    获取apk 路径 通过 /proc/self/maps 获取 。
    # ps -ef|grep mt
    u0_a63        7864  1464 0 20:10:12 ?     00:00:00 bin.mt.signature
    # cat /proc/7864/maps | grep base.apk
    7ffff4080000-7ffff4082000 r--s 00009000 08:12 919883                     /data/app/bin.mt.signature-zJwiS40NJvMBt5AUPmgKRA==/base.apk
    7ffff7a88000-7ffff7a89000 r--s 00019000 08:12 919883                     /data/app/bin.mt.signature-zJwiS40NJvMBt5AUPmgKRA==/base.apk
    /data/app/bin.mt.signature-zJwiS40NJvMBt5AUPmgKRA==/base.apk 就是 安装包的路径了
    为了方便, MT 将 base.apk 里面的 origin.apk 提取到  /data/data/包名 下面
    # cd /data/data/包名
    # ll
    drwxrws--x 2 u0_a63 u0_a63_cache  4096 2023-01-04 19:36 cache
    drwxrws--x 2 u0_a63 u0_a63_cache  4096 2023-01-04 19:36 code_cache
    drwxrwxr-x 5 u0_a63 u0_a63        4096 2023-01-04 19:47 lldb
    -rw------- 1 u0_a63 u0_a63       22989 2023-01-04 19:38 origin.apk
    然后传到 hookApkPath 进行 HOOK
    XHook 分析
    xhook 是针对 so 层 拦截的一种框架。
    github 在 :  
    https://github.com/iqiyi/xHook/blob/master/README.zh-CN.md
    //注册hook
    xhook_register(".*\\.so$", "hook的函数", 到我们自定义的 ,  NULL);
    //执行 hook!
    xhook_refresh(1);


    2023-01-04_203404.png (34.03 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传

    咋们只用看 mt_jni.c 就行了
    ``` c++
    //1. 从 java 层传入的
    // apkPath 是 原来的路径
    const char apkPath__;
    // repPath 要重定向的路径
    const char repPath__;
    //2. hook open 函数
    int (old_open)(const char , int, mode_t);
    static int openImpl(const char *pathname, int flags, mode_t mode) {
    //如果 打开是原路径 就替换成 repPath
    if (strcmp(pathname, apkPath__) == 0){
    return old_open(repPath__, flags, mode);
    }
    return old_open(pathname, flags, mode);
    }
    //3. hook openat 函数
    int (old_openat)(int, const char, int, mode_t);
    static int openatImpl(int fd, const char *pathname, int flags, mode_t mode) {
    //如果 打开是原路径 就替换成 repPath
    if (strcmp(pathname, apkPath__) == 0){
    return old_openat(fd, repPath__, flags, mode);
    }
    return old_openat(fd, pathname, flags, mode);
    }
    JNIEXPORT void JNICALL
    Java_bin_mt_signature_KillerApplication_hookApkPath(JNIEnv env, attribute((unused)) jclass clazz, jstring apkPath, jstring repPath) {
    //4. 获取从 java传入来的 原路径和重定向路径
    apkPath__ = (env)->GetStringUTFChars(env, apkPath, 0);
    repPath__ = (*env)->GetStringUTFChars(env, repPath, 0);
    //5. 使用 xhook 进行 重定向 hook
    xhook_register(".*\\.so$", "openat64", openat64Impl, (void **) &old_openat64);
    xhook_register(".*\\.so$", "openat", openatImpl, (void **) &old_openat);
    xhook_register(".*\\.so$", "open64", open64Impl, (void **) &old_open64);
    xhook_register(".*\\.so$", "open", openImpl, (void **) &old_open);
    xhook_refresh(0);
    }
    # 应用去签名
    我就直接用 之前 有点遗憾的 七猫小说 做为这次的示例。
    之前说过,但我们签名后,七猫就会显示网络异常


    2023-01-04_212447.png (84.74 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传

    所以我们步骤如下:
    1. 根据 mt 中 获取 原始包的路径, 我们在 **assets/SignatureKiller/** 创建我们的原始 apk
    ```java
    private static void killOpen(String packageName) {
          ....
            File repFile = new File("/data/data/" + packageName + "/origin.apk");
           //5. 由于 apk实际是个 zip 所以我们可以解压缩 提取 origin.apk
           try (ZipFile zipFile = new ZipFile(apkFile)) {
               //6. apkFile  抽取 origin.apk  到 repFile变量 指定的位置
               String name = "assets/SignatureKiller/origin.apk";
        ....   
    }


    2023-01-04_213237.png (50.62 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传
  • 我们通过 jadx 看下 编译的 KillerApplication 在哪个 xxxx.dex 中,顺带用 mt 把 包名和签名都成 原始七猫的
    [/ol]


    2023-01-04_213532.png (92.95 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传
  • 移动到 七猫里面去
    [/ol]


    2023-01-04_213933.png (163.56 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传

    (注意还有个 lsposed 的 dex 依赖 也得弄进去,这个就交给你们自己找了)


    2023-01-04_215327.png (21.87 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传
  • 看看 androidManifest.xml  文件 ,看看 application 在哪里
    [/ol]


    2023-01-04_215207.png (176.26 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:40 上传

    记住这个 SophixStubApplication 我们待会会用到
  • 复制 so文件,先把 arm64 的删掉,咋们就留一份arm32,免得不兼容。
    [/ol]


    2023-01-04_232306.png (104.78 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:41 上传

    app-debug.apk/lib/armeabi-v7a 里面找到我们得so 复制到 七猫里面去


    2023-01-04_232544.png (22.42 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:41 上传



    2023-01-04_232730.png (192.25 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:41 上传
  • com.km.sophix.SophixStubApplication 中加入我们的 KillerApplication
    [/ol]
    (在上篇 PM 过签中我是通过 static 代码块插入的) 但在 正己大佬 视频里 MT 一键是通过 继承的方式, 这种需要面向对象基础才能理解。 但这种更为简单,但是得一直找父类最上层 就如下图,最上层父类是 SophixApplication


    2023-01-04_233423.png (106.25 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:41 上传

    可以看到这个继承 最终的 Application


    2023-01-04_233528.png (85.85 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:41 上传

    咋们修改成 super  KillerApplication  让他继承我们过签名的 MT 的 application


    2023-01-04_233818.png (96.97 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:41 上传

    转换成 java 看看


    2023-01-04_233952.png (91.97 KB, 下载次数: 0)
    下载附件
    2023-1-5 23:41 上传

    至此算是完毕了。
    不过跟PM管理器一样,模拟器不行,但是 手机可以。
    个人觉得应该是 hook 不支持 x86_64 android 内部的  hook 。 不让我实在不理解为啥,管他的,谁又会用模拟器看小说呢。
    尾言
    其实说这么多,也就是MT 一键过签的事情。不过实践一下感觉也不错,起码知道了思路,下次这一方法不行,来了个新家伙,砸门也可以尝试上手改改。不至于只能等待 工具的 更新
    在大佬树下乘凉就是不错,168 直接开通 MT 会员 感觉也能接受了。
    c++ 和 so 对我来说还是过于困难,感觉我的android  逆向之路会被这两座大山给拦住。
    不过算了,要是什么都会,那还要大佬干什么,跟着大佬混饭吃也不错。

    下载次数, 下载附件

  • 2016976438
    OP
      


    Poorwood 发表于 2023-1-6 09:45
    这个工具的原理,有大佬讲讲吗?我看,就是不停地替换dex和so文件?

    排版太难了, 前面排版乱了 所以不太方便看
    您可以下载 mt 开源的代码看看 里面有通过 openAt 获取签名的代码 不过 arm 汇编 和 c++ 个人水平有限
    #define STR_HELPER(x) #x
    #define STR(x) STR_HELPER(x)
    intptr_t openAt(intptr_t fd, const char *path, intptr_t flag) {
    #if defined(__arm__)
        intptr_t r;
        asm volatile(
    #ifndef OPTIMIZE_ASM
        "mov r0, %1\n\t"
        "mov r1, %2\n\t"
        "mov r2, %3\n\t"
    #endif
        "mov ip, r7\n\t"
        ".cfi_register r7, ip\n\t"
        "mov r7, #" STR(__NR_openat) "\n\t"
        "svc #0\n\t"
        "mov r7, ip\n\t"
        ".cfi_restore r7\n\t"
    #ifndef OPTIMIZE_ASM
        "mov %0, r0\n\t"
    #endif
        : "=r" (r)
        : "r" (fd), "r" (path), "r" (flag));
        return r;
    #elif defined(__aarch64__)
        intptr_t r;
        asm volatile(
    #ifndef OPTIMIZE_ASM
        "mov x0, %1\n\t"
        "mov x1, %2\n\t"
        "mov x2, %3\n\t"
    #endif
        "mov x8, #" STR(__NR_openat) "\n\t"
        "svc #0\n\t"
    #ifndef OPTIMIZE_ASM
        "mov %0, x0\n\t"
    #endif
        : "=r" (r)
        : "r" (fd), "r" (path), "r" (flag));
        return r;
    #else
        return (intptr_t) syscall(__NR_openat, fd, path, flag);
    #endif
    }
    JNIEXPORT jint JNICALL
    Java_bin_mt_test_MainActivity_openAt(JNIEnv *env, __attribute__((unused)) jclass clazz, jstring path) {
        const char* p = (*env)->GetStringUTFChars(env, path, 0);
        __android_log_print(ANDROID_LOG_INFO, "openAt", "path=%s", p);
        intptr_t fd = openAt(AT_FDCWD, p, O_RDONLY);
        return fd;
    }
    sopcast   


    2016976438 发表于 2023-1-6 22:51
    算能 了理解 大佬的不容易 。 想写好看点真难, 加个图片 排版全乱了, 改来改去 吾爱给我限制 ip了...

    是时候学一下md语法了,写帖子事半功倍
    aa2923821a   

    感谢分析学习了
    aonima   

    感谢分享!!
    lianquke   

    不错,学习了
    markdik31   

    感谢分享,其实mt的hook支持x86_64,用AS自带的虚拟机加上Bin的demo apk试试就行,应该是七猫不兼容,或者二者混合发生了奇怪反应。
    大大连连   

    谢谢大神分享,小弟来学习学习
    zkq970413   

    这个教程实用,感谢感谢
    yun520530   

    感谢分享~我记得我买的时候MT永久会员还不是168,没想到现在这么贵了啊。
    您需要登录后才可以回帖 登录 | 立即注册

    返回顶部