App逆向百例|16|某交友App花指令分析

简介: App逆向百例|16|某交友App花指令分析

观前提示:

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

样本:aHR0cHM6Ly9wYW4uYmFpZHUuY29tL3MvMTRWbXVfeHlDN2lGYk9qYjBtOHpYc3c/cHdkPWxpbm4=

0x1 花指令分析

大姐姐打开libsoulpower.so

直接跳转到0xaa73c

如果发现这里没被正常识别

直接在0xaa73c处快捷键P即可

直接F5反编译看伪代码

然后就会发现不明代码

转到汇编

这里是将R0的值作为地址 跳转到该地址处

BX              R0

那么R0从哪里来呢 继续往上看

这里调用了sub_AECF0并且将返回值放在了R0

BL              sub_AECF0

所以真实的跳转地址可能就是在sub_AECF0中计算得出的

继续往上看还能看到入参的操作

LDR             R0, =(off_2C1B64 - 0xAA7A4)
MOV             R1, #0
STR             R1, [SP,#160]
MOV             R1, #1
ADD             R0, PC, R0 ; off_2C1B64

这里IDA已经帮我们识别好了伪代码 直接看就行

sub_AECF0(&off_2C1B64, 1)

sub_AECF0看是啥操作

int __fastcall sub_AECF0(int a1, int a2)
{
  return *(_DWORD *)(a1 + 4 * a2);
}

很简单啊 直接复现一下

0x2C1B64 + 4 * 1 = 0x2C1B68

得到0x2C1B68后 跳转过去

002C1B68                 DCD sub_AA7B8

可以看到0x2C1B68对应的就是sub_AA7B8

所以 R0 = 0xAA7B8

而IDA并不会去执行这些操作 所以达到花指令的目标

0x2 手动去花

前面计算了R0的真实跳转地址

拿到真实地址后就可以在0xAA7B4处手动Patch下图0xAA7B4给错正确0xAA7B8

Patch完事之后再次F5反编译

反编译完发现代码仅仅多了一行

转到汇编 这里同样出现BX R0

所以这个样本应该是多处插花 那就继续还原

按上述操作 找到俩个传入参数 然后手动计算出真实地址

0x2C1B64 + 4 * 2 = 0x2C1B6C

002C1B6C                 DCD sub_AA9F8

R0 = 0xAA9F8

继续重复操作Patch

代码又多了一点 然后不出意料 又是一处插花

LDR             R0, =(off_2C1B64 - 0xAAA08)
MOV             R1, #3
ADD             R0, PC, R0 ; off_2C1B64
BL              sub_AECF0
BX              R0

继续重复几次操作后 代码多了这么多 但是还没完全去花

同时在视图中可以发现ollvm 后面还不知道会出现啥 继续Patch

停!!!

怎么可能 量很大 年轻人 你把握不住的 该上脚本了!

0x3 脚本去花

首先明确要干啥先

  1. 找出所有的插花方法
  2. 找出所有插花方法的调用处
  3. 拿到俩个参数
  4. 计算正确地址
  5. Patch

前面我们知道 每次计算都会进入sub_AECF0

但是我猜测可能不止一个sub_AECF0

也许是同操作 但是方法名不一样 这里做个验证

拿到方法HEX01 01 90 E7 1E FF 2F E1

01 01 90 E7                 LDR             R0, [R0,R1,LSL#2]
1E FF 2F E1                 BX              LR

然后Binary search

搜索得出五个结果

查看每个方法的交叉引用都有很多处

那可以使用idautils.CodeRefsTo遍历交叉引用

但是我担心有可能存在漏识别 所以我采用了从头到尾的遍历方法

import capstone
from keystone import *
import flare_emu
import ida_ida
import idautils
import idaapi
import ida_segment
import idc
import ida_bytes
# 初始化架构和模式
CS = capstone.Cs(capstone.CS_ARCH_ARM, capstone.CS_MODE_ARM)
# 设置为详细反汇编模式
CS.detail = True
# 设置反汇编跳过数据
CS.skipdata = True
# 获取.text段的范围
def getAddrRange():
    start = ida_ida.inf_get_min_ea()
    size = ida_ida.inf_get_max_ea() - start
    # 将地址范围限定于text节
    for seg in idautils.Segments():
        seg = idaapi.getseg(seg)
        segName = ida_segment.get_segm_name(seg)
        if segName == ".text":
            start = seg.start_ea
            size = seg.size()
    return start, size
def binSearch(start, end, pattern):
    matches = []
    addr = start
    if end == 0:
        end = idc.BADADDR
    if end != idc.BADADDR:
        end = end + 1
    while True:
        addr = ida_bytes.bin_search(addr, end, bytes.fromhex(pattern), None, idaapi.BIN_SEARCH_FORWARD,
                                    idaapi.BIN_SEARCH_NOCASE)
        if addr == idc.BADADDR:
            break
        else:
            matches.append(addr)
            addr = addr + 1
    return matches
# 获取代码段的起始地址和长度
start, size = getAddrRange()
codebytes = idc.get_bytes(start, size)
sub_matches = binSearch(0, 0, "01 01 90 E7 1E FF 2F E1")
disasmCodes = list(CS.disasm_lite(codebytes, start, size))
for i in range(len(disasmCodes)):
    (address, size, mnemonic, op_str) = disasmCodes[i]
    if mnemonic == 'bl':
        sub_addr = int(op_str.replace('#', ''), 16)
        if sub_addr in sub_matches:
            print(hex(address))

效果很不错 找出了很多的调用处

找出调用处随便跳转几个看能不能找到规律匹配参数

这里发现 参数的位置并不固定 不好做匹配

但是又可以发现 参数位置 调用位置 跳转位置 都是处于同一个block中

知道了这一点 就只需要匹配出相对应的block就可以了

def find_block(blocks, addr):
    for block in blocks:
        start_ea = block.start_ea
        end_ea = block.end_ea
        if start_ea < addr < end_ea:
            return block
    return None
func = idaapi.get_func(address)
  if func:
    block = find_block(idaapi.FlowChart(func), address)

即使匹配出了地址块 也不好匹配出参数 那咋办呢

其实 这里我匹配地址块的原因就是不想再写一堆匹配代码找出R0R1

所以我打算交给unicorn去做

而我又懒得写很多代码 所以 我又使用了封装好的flare_emu

例如 我匹配出了一个block 我仅需要把block的起始地址匹配到方法执行前即可

测试看结果

import flare_emu
myEH = flare_emu.EmuHelper()
myEH.emulateRange(
  startAddr=0x804DC,
  endAddr=0x804FC
)
Out[31]: <unicorn.unicorn.Uc at 0x169c478b908>
myEH.getRegVal("R0")
Out[32]: 0x2c14d0 (OCSP_SIGNATURE_it + 0x2dc)
myEH.getRegVal("R1")
Out[33]: 1

效果完美

后面仅需自己写个计算方法 将参数传入计算即可

def calcu_addr(a, b):
    return a + 4 * b

得到真实跳转地址后 还得匹配出跳转地址存储的地址

idc.print_operand 刚好能实现这个操作

idc.print_operand(0x2c14d4,0)
Out[34]: 'sub_80508'

拿到这个真实地址 最后Patch就行

不过这里需要注意的是 我们不知道后面第几行是需要Patch的BX

但是我们前面就得到了块的起始地址 同样的也可以得到块的结束地址

只需要(结束地址-方法调用地址)/4 然后遍历就能找到BX所在位置

但是但是 我觉得不优雅

从这张图 可以知道BX一直在块的最后一行 那我何必去遍历呢 直接那最后一行的地址不就行了

一下子就优雅起来了

最后写下patch

code = f"B {hex(b_addr)}"
b_code = generate(code, block.end_ea - 4)
# Patch 1 处理花指令
ida_bytes.patch_bytes(block.end_ea - 4, bytes(b_code))

将所有代码串起来 执行测试一下效果

执行完后应用更改

最后重新打开so

跳转到最开始分析的0xaa73c

成功识别到N多行代码了

去花工作完成 处理脚本和对应的so文件放在样本中



如果你想学习关于so反混淆相关的知识

例如IDA脚本开发 字符串加密处理 花指令处理 龙龙的星球里都写得很详细

龙龙老卷王了 内容丰富  很多东西我都是跟着龙龙学的 让我受益匪浅

所以 我吹爆! 



感谢各位大佬观看

感谢大佬们的文章分享 

如有错误 还请海涵

共同进步 带带弟弟


点赞 在看 分享是你对我最大的支持

逆向lin狗

相关文章
|
8月前
|
算法 数据安全/隐私保护 索引
App逆向百例|09|某App hkey分析还原
App逆向百例|09|某App hkey分析还原
203 0
|
16小时前
|
数据采集 小程序 网络安全
云擎技术---分析工信部APP备案的“传闻”
APP备案并非新事物,自2005年起已有非经营性互联网信息服务备案制度。备案针对的是网站主办者,而非用户,不涉及个人用户网络访问。网络接入服务提供者包括ISP和IDC,不限于三大运营商。通知要求不为未备案网站提供接入,但不影响国外软件使用。个人开发者不能涉及经营性内容,备案审核时长1-20个工作日。境内服务器和国内应用商店需备案,境外则无需。手机厂商不会开启白名单制,仅实行黑名单制。APP备案与民营经济发展壮大意见不冲突,工信部有权颁布相关规定。该政策不存在逐步试探底线情况,所有解读均有法律依据。
32 3
云擎技术---分析工信部APP备案的“传闻”
|
16小时前
|
网络协议 算法 Android开发
安卓逆向 -- 实战某峰窝APP(动态分析)
安卓逆向 -- 实战某峰窝APP(动态分析)
42 4
|
16小时前
|
算法
某圈app算法分析
某圈app算法分析
20 0
|
16小时前
|
算法 安全 数据安全/隐私保护
某影视APP算法逆向分析
某影视APP算法逆向分析
20 0
|
16小时前
|
算法 Java
某江app算法分析
某江app算法分析
17 0
|
16小时前
|
算法 数据挖掘 数据安全/隐私保护
某合伙人app算法分析
某合伙人app算法分析
16 0
|
8月前
|
算法 Java 数据安全/隐私保护
App逆向百例|12|某电商App Sign分析
App逆向百例|12|某电商App Sign分析
249 0
|
8月前
|
数据安全/隐私保护
App逆向百例|10|某App x-zse-96分析
App逆向百例|10|某App x-zse-96分析
239 0
|
17小时前
|
数据可视化 数据挖掘
【数据分析与可视化】使用pyecharts对App下载量数据进行可视化分析(附源码)
【数据分析与可视化】使用pyecharts对App下载量数据进行可视化分析(附源码)
68 0

热门文章

最新文章