首页
社区
课程
招聘
[原创]android so文件攻防实战-某团libmtguard.so反混淆
发表于: 2022-3-13 11:59 23170

[原创]android so文件攻防实战-某团libmtguard.so反混淆

2022-3-13 11:59
23170

计划是写一个android中so文件反混淆的系列文章,目前这是第二篇。
第一篇:android so文件攻防实战-百度加固免费版libbaiduprotect.so反混淆
样本在附件。我偷个懒,就不完整分析整个so文件了,只分析一下JNI_OnLoad。打开IDA,JNI_OnLoad长这个样子:

BEQ和BNE是一对条件相反的跳转,这之前计算出接下来指令的地址0x3430赋值给R4,loc_340C处的代码通过POP指令将R4的值给PC,看0x3430处的代码。


接下来的代码很多都是这种动态计算跳转地址的。0x3432处的指令从0x3438中取一个dword给R0,跳到0x3440然后再跳到0x407C。0x3444处开始不是指令而是一个跳转表,索引就是R0,值就是接下来指令地址的偏移,0x407C开始的代码就是取这个偏移。所以我们可以写一个脚本patch,计算出偏移,在0x3434处就直接跳过去。

同时还会有一些跳转到0x4094的指令,像上面这样0x3464跳到0x4094之后会返回到0x346C,从0x3462到0x346C这些指令我都直接patch成NOP了。
修复脚本:

修复结束之后跟踪指令,0x3434处的代码跳到了0x345A,0x345A处的代码主要逻辑是在0x34C6跳到0x34EC通过0x34EC动态计算地址跳到了0x3520,0x3524处的代码跳到了0x365C,0x366E处的代码跳到了0x35A8,0x35A8是JNI_OnLoad的主要逻辑。

如上图所示,0x35A8基本就干了两件事:FindClass(com/meituan/android/common/mtguard/NBridge)和RegisterNatives(com/meituan/android/common/mtguard/NBridge, main, 1)。

sub_3680里面调用sub_36B4通过异或解密了一些字符串,上图我已经把解密之后的结果patch进去了。
当然用unidbg是可以直接跑JNI_OnLoad的:

不过这个系列文章主要是想讨论一下so混淆和反混淆,有精力的话还可以对这个so进行完整分析,混淆应该没有啥新的了。

import keystone
from capstone import *
import idc
import ida_bytes
import subprocess
 
arch = keystone.KS_ARCH_ARM
mode = keystone.KS_MODE_THUMB
ks = keystone.Ks(arch, mode)
md = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
 
def is_BLX_sub407C(ea):
    ldr_addr = ea
    ldr_flags = idc.get_full_flags(ldr_addr)
    if not idc.is_code(ldr_flags):
        return False
 
    if idc.print_insn_mnem(ldr_addr) != 'BLX':
        return False
 
    if idc.print_operand(ldr_addr, 0) != 'sub_407C':
        return False
 
    return True
 
def is_BLX_sub4094(ea):
    ldr_addr = ea
    ldr_flags = idc.get_full_flags(ldr_addr)
    if not idc.is_code(ldr_flags):
        return False
 
    if idc.print_insn_mnem(ldr_addr) != 'BLX':
        return False
 
    if idc.print_operand(ldr_addr, 0) != 'sub_4094':
        return False
 
    if idc.print_insn_mnem(ldr_addr - 2) != 'PUSH':
        return False
 
    if idc.print_insn_mnem(ldr_addr + 8) != 'POP':
        return False
 
    return True
 
def func_patch():
 
    ins_addr = idc.next_head(0)
    while ins_addr != idc.BADADDR:
 
         if is_BLX_sub407C(ins_addr):
            for i in CodeRefsTo(ins_addr, False):
                if idc.get_wide_word(i + 4) == 18112:
                    index = idc.get_wide_word(i + 6)
                    patch_qword(i + 6, 0x46C046C0)
                    idc.create_insn(i + 6)
                else:
                    index = idc.get_wide_word(i + 4)
                    patch_qword(i + 4, 0x46C046C0)
                    idc.create_insn(i + 4)
                print("i:" + hex(i))
                index = index * 4 + ins_addr + 4
                offset = ida_bytes.get_dword(index)
                target = ins_addr + 0x4 + offset
                command = "BL " + hex(target)
                print("command:" + command)
                pi = subprocess.Popen(['D:\\keystone-0.9.2-win64\\kstool.exe', 'thumb', command, \
                hex(i)], shell=True, stdout=subprocess.PIPE)
                output = pi.stdout.read()
                ins = str(output[-15:-4])[2:-1]
                ins = ins.split(" ")
                ins = "0x" + ins[3] + ins[2] + ins[1] + ins[0]
                print("ins:" + ins)
                patch_dword(i, int(ins, 16))
 
         if is_BLX_sub4094(ins_addr):
            patch_dword(ins_addr - 2, 0xbf00)
            patch_dword(ins_addr, 0xbf00)
            patch_dword(ins_addr + 2, 0xbf00)
            patch_dword(ins_addr + 4, 0xbf00)
            patch_dword(ins_addr + 6, 0xbf00)
            patch_dword(ins_addr + 8, 0xbf00)
 
         ins_addr = idc.next_head(ins_addr)
 
func_patch()
import keystone
from capstone import *
import idc
import ida_bytes
import subprocess
 
arch = keystone.KS_ARCH_ARM
mode = keystone.KS_MODE_THUMB
ks = keystone.Ks(arch, mode)
md = Cs(CS_ARCH_ARM, CS_MODE_THUMB)
 
def is_BLX_sub407C(ea):
    ldr_addr = ea
    ldr_flags = idc.get_full_flags(ldr_addr)
    if not idc.is_code(ldr_flags):
        return False
 
    if idc.print_insn_mnem(ldr_addr) != 'BLX':
        return False
 
    if idc.print_operand(ldr_addr, 0) != 'sub_407C':
        return False
 
    return True
 
def is_BLX_sub4094(ea):
    ldr_addr = ea
    ldr_flags = idc.get_full_flags(ldr_addr)
    if not idc.is_code(ldr_flags):
        return False
 
    if idc.print_insn_mnem(ldr_addr) != 'BLX':
        return False
 
    if idc.print_operand(ldr_addr, 0) != 'sub_4094':
        return False
 
    if idc.print_insn_mnem(ldr_addr - 2) != 'PUSH':
        return False
 
    if idc.print_insn_mnem(ldr_addr + 8) != 'POP':
        return False

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

上传的附件:
收藏
免费 6
支持
分享
最新回复 (5)
雪    币: 624
活跃值: (501)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
沙发
2022-3-13 18:21
0
雪    币: 14
活跃值: (887)
能力值: ( LV3,RANK:24 )
在线值:
发帖
回帖
粉丝
3
牛啊
2022-3-16 17:09
0
雪    币: 365
活跃值: (480)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
这个so是哪个版本的
2022-3-16 17:12
0
雪    币: 2710
活跃值: (1848)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5

学习了

最后于 2022-3-22 15:13 被乐活编辑 ,原因:
2022-3-22 14:45
0
雪    币: 5932
活跃值: (4825)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6

loc_340C处的代码通过POP指令将R4的值给PC

我不知道哪里搞错了,楼主能否帮忙看下?感激

2022-4-3 21:24
0
游客
登录 | 注册 方可回帖
返回
//