App逆向百例|18|某A系防护SO跳转修复

简介: App逆向百例|18|某A系防护SO跳转修复

观前提示:

本文章仅供学习交流,切勿用于非法通途,如有侵犯贵司请及时联系删除

样本:aHR0cHM6Ly9wYW4uYmFpZHUuY29tL3MvMWl5Q21NNVd1WC1NTkNsRkh2X0w3TVE/cHdkPWxpbm4=

0x0 前言

最近比较忙,拖更了一段时间,不过好在事情在逐步逐步地减少,毕设搞完了,论文答辩了,毕业照也拍了,出来社会当上社畜了,所以更新频率会降低很多,不过我还是会将工作上遇到的东西拿出部分来做案例分享,可能会比较简单甚至出错,大佬们轻点喷。

0x1 手动分析

大姐姐打开libsgmainso-6.5.55.so

直接在导出表找到JNI_OnLoad进来

可以看到没有正常的解析出预想的代码 而是出现了

__asm { BR              X11 }

跳回汇编看看

SUB             SP, SP, #0x50
STP             X25, X23, [SP,#0x40+var_30]
STP             X22, X21, [SP,#0x40+var_20]
STP             X20, X19, [SP,#0x40+var_10]
STP             X29, X30, [SP,#0x40+var_s0]
ADD             X29, SP, #0x40
MRS             X21, #3, c13, c0, #2
LDR             X8, [X21,#0x28]
MOV             W9, #0x8A
ADRL            X10, dword_166320
STR             X8, [SP,#0x40+var_38]
STR             W9, [SP,#0x40+var_3C]
LDRB            W9, [X10,#(byte_166352 - 0x166320)]
MOV             X20, X0
ADD             X10, SP, #0x40+var_3C
MOV             W8, #0xCE
ADD             W9, W9, #0x8A
ADRP            X22, #0x166000
STR             W9, [SP,#0x40+var_3C]
ADR             X11, loc_1E10C
LDRSW           X3, =0xFFFFFEF7
LDRSW           X25, [X10]
ADD             X3, X3, X25
ADD             X11, X11, X3
MOV             X9, #0x16
BR              X11

根据上面的汇编代码可知 最终需要跳转的地址放在X11寄存器里面 而寄存器是通过计算得到的 除去入栈出栈的操作 最终可以简化为

X10 = 0x166320
W9  = X10 + 0x32 = 0x166352=>0xab
W9  = W9  + 0x8a = 0x135
X25 = W9
X3  = 0xFFFFFEF7 + X25 = 0x2c
X11 = 0x1E10C + X3 = 0x1e138

得到X11的地址手动Patch一下看看效果

确认后发现对应的地址是一堆常量 那就需要我们手动将对应地址回归undefined状态(快捷键 U)

接着再到对应的地址创建Function(快捷键 P)

最后回到伪代码界面 按下F5让代码重新分析即可

这里被IDA识别为sub方法 不过问题不大 只需保存Patch文件 然后IDA重新打开即可

接回原来的话 手动修复跳转后 可以看到大段的代码 说明目的已经达到了

但是一个so里面不止一处的出现跳转 况且还有好几个so需要跳转修复 这将是一处大工程 手动还原是不现实的

0x2 脚本修复

这里我将尝试使用flare_emu代替手动计算工作 为此创建一个脚本

将范围限制在[0x1E0B8-0x1E120]

import flare_emu
myEH= flare_emu.EmuHelper()
myEH.emulateRange(
  startAddr=0x1E0B8,
  endAddr=0x1E120
)
myEH.getRegVal("X11")

将脚本放到IDA里面运行 得到X11的结果为0x1e08d

这和我们前面计算的0x1e138相差甚远

那么问题出现在哪里呢?

通过分析发现 手动获取0x166352得到

myEH.getEmuBytes(0x166352,1)
Out[9]: bytearray(b'\x00')

而在unidbg中拿到0x166352却是有值的

那么问题就显而易见了 如果要解决可以使用dump_so去拿到初始化后的so文件

但是我并没有那么做 这次我使用unidbg去做跳转修复(有点杀鸡用牛刀的感觉了)

0x3 unidbg跳转修复

前面已经分析了那么多 我就直接上脚本了(学习借鉴了一篇看雪的文章)

首先你得补出一份可以跑得通的unidbg环境

主要思想就是tracecode 并实时保存10条汇编数据 只要出现BR跳转指令 就进入判断特征 符合我们筛选的条件后 记录地址和真实跳转地址 等待完全执行完后 将记录下来的所有数据替换进原始SO文件输出修复后的SO文件

public void find_junk(long base_addr, long so_size) {
        patchlist = new ArrayList<>();
        emulator.getBackend().hook_add_new(new CodeHook() {
            int count_code = 0;
            String[] opcode = new String[10];
            Capstone capstone = new Capstone(Capstone.CS_ARCH_ARM64, Capstone.CS_MODE_ARM);
            @Override
            public void hook(Backend backend, long address, int size, Object user) {
                byte[] bytes = emulator.getBackend().mem_read(address, 4);
                Instruction[] disasm = capstone.disasm(bytes, 0);
                String mnemonic = disasm[0].getMnemonic();
                String opStr = disasm[0].getOpStr();
                opcode[(count_code % 10)] = mnemonic;
                count_code++;
                if (mnemonic.indexOf("br") != -1) {
                    List<String> list = Arrays.asList(opcode);
                    if (list.contains("add") && list.contains("ldrsw")) {
                        int reg = 0;
                        switch (opStr) {
                            case "x0":
                                reg = 199;
                                break;
                            case "x1":
                                reg = 200;
                                break;
                            case "x2":
                                reg = 201;
                                break;
                            case "x3":
                                reg = 202;
                                break;
                            case "x4":
                                reg = 203;
                                break;
                            case "x5":
                                reg = 204;
                                break;
                            case "x6":
                                reg = 205;
                                break;
                            case "x7":
                                reg = 206;
                                break;
                            case "x8":
                                reg = 207;
                                break;
                            case "x9":
                                reg = 208;
                                break;
                            case "x10":
                                reg = 209;
                                break;
                            case "x11":
                                reg = 210;
                                break;
                            case "x12":
                                reg = 211;
                                break;
                            case "x13":
                                reg = 212;
                                break;
                            case "x14":
                                reg = 213;
                                break;
                            case "x15":
                                reg = 214;
                                break;
                            case "x16":
                                reg = 215;
                                break;
                            case "x17":
                                reg = 216;
                                break;
                            case "x18":
                                reg = 217;
                                break;
                            case "x19":
                                reg = 218;
                                break;
                            case "x20":
                                reg = 219;
                                break;
                            case "x21":
                                reg = 220;
                                break;
                            case "x22":
                                reg = 221;
                                break;
                            case "x23":
                                reg = 222;
                                break;
                            case "x24":
                                reg = 223;
                                break;
                            case "x25":
                                reg = 224;
                                break;
                            case "x26":
                                reg = 225;
                                break;
                            case "x27":
                                reg = 226;
                                break;
                            case "x28":
                                reg = 227;
                                break;
                        }
                        long value = (long) emulator.getBackend().reg_read(reg);
                        int TRUE_JUMP = (int) (value - base_addr);
                        if (TRUE_JUMP < so_size) {
                            PatchIns patchIns = new PatchIns();
                            patchIns.setAddr(address - base_addr);
                            patchIns.setIns("b 0x" + Integer.toHexString(TRUE_JUMP));
                            patchlist.add(patchIns);
                        }
                    }
                }
            }
            @Override
            public void onAttach(UnHook unHook) {}
            @Override
            public void detach() {}
        }, base_addr, base_addr + so_size, null);
    }
    public void save_fix(String so_path, String so_fix_path) {
        Keystone keystone = new Keystone(KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian);
        try {
            File sgmain = new File(so_path);
            FileInputStream fileInputStream = new FileInputStream(sgmain);
            byte[] data = new byte[(int) sgmain.length()];
            fileInputStream.read(data);
            fileInputStream.close();
            for (PatchIns patchins : patchlist) {
                KeystoneEncoded assemble = keystone.assemble(patchins.ins, (int) patchins.addr);
                for (int i = 0; i < assemble.getMachineCode().length; i++) {
                    data[(int) patchins.addr + i] = assemble.getMachineCode()[i];
                }
            }
            File sgmain_fix = new File(so_fix_path);
            FileOutputStream fileOutputStream = new FileOutputStream(sgmain_fix);
            fileOutputStream.write(data);
            fileOutputStream.flush();
            fileOutputStream.close();
            System.out.println("fix finish,fix count->" + patchlist.toArray().length);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

最终修复效果

还原效果还是很不错的 只要运行到了的 基本被修复好了

看看doCommandNative入口 效果也可以

不过也会出现例外的情况 例如极少量的误判没有还原到 不过更大的可能性是 没有执行到这个位置 所以没做出修复操作 例如下面这种分支

不过总的来说效果还是ok的

0x3 参考文章

《记一次基于unidbg模拟执行的去除ollvm混淆》 https://mp.weixin.qq.com/s/KuWi39Grl9lrhI8iY_S8pw


小linnnn



相关文章
|
7月前
|
存储 Java API
Android 浅度解析:mk预置AAR、SO文件、APP包和签名
Android 浅度解析:mk预置AAR、SO文件、APP包和签名
966 0
|
7月前
|
移动开发 小程序
微信小程序web-view嵌入uni-app H5页面,通过H5页面跳转企业微信客户聊天窗口如何操作?
微信小程序web-view嵌入uni-app H5页面,通过H5页面跳转企业微信客户聊天窗口如何操作?
|
4月前
|
运维 Kubernetes 容器
【Azure K8S】演示修复因AKS密钥过期而导致创建服务不成功的问题(The provided client secret keys for app ****** are expired)
【Azure K8S】演示修复因AKS密钥过期而导致创建服务不成功的问题(The provided client secret keys for app ****** are expired)
【Azure K8S】演示修复因AKS密钥过期而导致创建服务不成功的问题(The provided client secret keys for app ****** are expired)
|
3月前
|
小程序 开发工具
app跳转微信小程序,使用明文scheme拉起
app跳转微信小程序,使用明文scheme拉起
722 4
|
4月前
|
存储 SQL 安全
【Azure 应用服务】App Services 恶意软件防护相关
【Azure 应用服务】App Services 恶意软件防护相关
路由不跳转,只留在首页,写的样式写到了App.vue中,没使用router-view
路由不跳转,只留在首页,写的样式写到了App.vue中,没使用router-view
路由不跳转,只留在首页,写的样式写到了App.vue中,没使用router-view
|
7月前
uni-app 188修复弹框问题
uni-app 188修复弹框问题
38 2
|
7月前
uni-app 151修复删除好友朋友圈记录问题
uni-app 151修复删除好友朋友圈记录问题
40 1
|
7月前
|
Java C# Android开发
Xamarin.Android | 界面跳转到手机自带的自启动管理界面,引导用户将APP加入自启动
为了帮助用户在使用 APP 时提高其稳定性和使用体验,有时候我们需要让安卓手机的界面跳转到手机自带的自启动管理界面,以此来引导用户将 APP 加入自启动,确保应用程序在后台运行时不被系统杀死,从而保证应用程序服务的稳定性和可靠性。同时,这也可以提高用户的使用体验,使用户能够更好地享受应用程序的功能和服务。
376 0
Xamarin.Android | 界面跳转到手机自带的自启动管理界面,引导用户将APP加入自启动
uni-app提交表单成功之后跳转首页
uni-app提交表单成功之后跳转首页
69 1

热门文章

最新文章

下一篇
DataWorks