不混淆so文件_Android OLLVM反混淆实战

b2a820306a676057a174effba02550a3.png

前言

小弟我最近在研究ollvm平坦化的反混淆,但网上的参考资料大多是x86的反混淆,关于arm的少之又少,现正好手头有个app样本中运用了这种混淆技术,所以拿来练一练手。

app样本:douyin9.9.0
so样本:libcms.so
逆向工具:ida,jadx

观察函数控制流程图(CFG)

所要获取的签名算法位于leviathan方法中,该方法是个native方法,在libcms.so中没有静态关联函数,所以我们需要找到其动态注册的位置。

2606e412eb99ae2dd0104d78c8f9bc8f.png

首先,分析Jni_OnLoad函数,Jni函数的动态注册一般都在这里进行:

012d46ac93ecb0c2025336dbdc91e237.png

关于ollvm的基本原理,大家可以在 https://github.com/obfuscator-llvm/obfuscator 上进行详细了解,在这里不做过多说明。

可以看到 JNI_OnLoad已经被彻底地混淆,通过流程图结构,我们可以初步推断是ollvm的指令平坦,进一步观察发现大部分流程块都会跳转至0x46FE,从而可以得出0x46FE是主分发器所在块,r0寄存器作为保存索引值的寄存器(在这里我将混淆里用于索引到相关块的常量值称为索引值)。

29de6d986857b09389c8345cdd9d9733.png

去除一般混淆

仔细观察函数内容,发现其中还包含另外一种混淆结构,如下图所示:

738297864ff280ac5b1248493b3d6b0c.png

从0x478c的位置PUSH {R0-R3}开始是另一个混淆结构的开始,功能比较简单,分析之后其作用是直接跳转到另一个地址,跳转的位置是到POP {R0-R3}指令的下一个地址,即0x47E4的位置。
去除这中混淆的方式也比较简单,直接把0x478c处修改成到0x47E4的跳转即可,中间的混淆代码可以用nop填充掉,鉴于整个函数有多处这样的混淆,可以写个ida脚本批量处理,通过搜索特征字节码来定位混淆代码的起始与结束位置,例如混淆开始处的opcode为0F B4 78 46 79 46(为了避免错误识别可以多判断几个字节码),结束处的opcode为0F BC。

脚本内容如下:

from idc import *from idautils import *from idaapi import *from keystone import *ks=Ks(KS_ARCH_ARM,KS_MODE_THUMB)def ks_disasm(dis_str):    global ks    encoding,count=ks.asm(dis_str)    return encodingfunc_start=0x44c8func_end=0x498cpatch_start = Nonepatch_end = Nonefor i in range(func_start, func_end):    #PUSH{R0-R3} or PUSH{R0-R3,R7}    #MOV R0,PC    #MOV R1,PC    if get_bytes(i,6,0) == b'x0fxb4x78x46x79x46' or get_bytes(i,6,0) == b'x8fxb4x78x46x79x46':        patch_start = i    #POP{R0-R3}    if get_bytes(i,2,0) == b'x0fxbc':        if patch_start != None:            patch_end = i + 2    #POP{R7},POP{R0-R3}    if get_bytes(i,4,0) == b'x07xbcx88xbc':        if patch_start != None:            patch_end = i + 4    if nop_start != None and nop_end != None:        for i in range(0, patch_end - patch_start, 2):            patch_byte(nop_start+i,0x00)            patch_byte(nop_start+i+1,0xbf)        dis_str = 'b #{}-{}'.format(patch_end, patch_start)        jmp_addr.append(patch_start)        encoding = ks_disasm(dis_str)        for item in encoding:            print('{}'.format(hex(item)))        for j in range(len(encoding)):            patch_byte(patch_start+j,encoding[j])        patch_start = None        patch_end = None
寻找相关块

准备工作已完成,正式进入我们的主题,还原ollvm的混淆代码——关于如何还原流程平坦化,有三个问题需要解决:

一、找出流程里所有的相关块

二、找出各个相关块之间执行的先后顺序

三、使用跳转指令将各个相关块连接起来

第一个问题,通过以下规律找到差不多所有的相关块:

1、后继是分发器块的,一般是相关块(注意是一般,因为有的次分发器也会跳转至主分发器

2、相关块的前继块中,有且只有当前相关块一个后继块的,也是相关块

Github上有许多二进制分析框架(angr,barg,miasm等等),可以对函数生成CFG(控制流程图),由于barg和miasm对arm-v7a指令的支持不是很完全,有些代码无法反汇编,最终我选用了angr。(github地址

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 混淆SO文件是指对Android native执行文件(.so文件)中的函数和数据进行加密和混淆,以保护代码的安全性和隐私性。与Java代码混淆不同的是,SO文件混淆是在编译阶段完成的,而不是在打包和发布阶段。 在Android开发中,NDK(Native Development Kit)是一款可用于编写本地代码的工具集。使用NDK开发的应用程序使用SO文件来提供C/C++函数和库。由于NDK代码不会在应用程序中进行编译,因此它们没有类似于Java代码的混淆和加密工具。因此,为了加强SO文件的安全性,需要使用其他的工具来进行混淆。 现在有许多开源的SO混淆工具可供使用。例如,Android NDK提供了一个名为LLVM混淆器的工具,它使用一些与Java代码混淆器不同的技术来优化指令流和掩盖代码逻辑。此外,其他SO混淆工具如YASC、MOVfuscator、O-LLVM等也可以用于混淆SO文件。 SO文件混淆可以通过多种方式实现。例如,针对代码文件进行重命名或修改,删除未引用的函数和数据,加密和混淆重要内容等。此外,也可以通过将硬编码的值替换为映射的值,使用一些外部定义的数据结构来防止汇编和静态分析等技术来实现混淆。无论采用何种方法,SO文件混淆对于保护代码的安全性和隐私性都具有重要意义。 ### 回答2: 使用Android Studio中的ProGuard工具可以对一个应用程序的Java class文件进行混淆,但是它并不支持对Native代码的混淆。由于so文件是编译后的二进制文件,没有人可读的,对so文件进行混淆通常都是为了防止逆向工程而不是优化程序的运行或缩小它的大小。 现在有一些第三方工具可以用来对so文件进行混淆,比如Bangcle、Qihoo 360、Baidu和信鸽。这些工具通过改变so文件的代码结构、函数名称和变量名等方法来混淆so文件,使之更加难以阅读和理解。 混淆so文件是有一定风险的,因为这可能会导致应用程序的崩溃和功能异常。此外,混淆so文件可能会和某些平台实现产生不兼容的问题。但是如果您确实担心你的so文件受到攻击,那么混淆so文件可能是一个值得考虑的安全策略。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值