iOS Swift5逆向——Demangle过程中的偏移计算

当Swift进入ABI稳定时代和后ABI稳定时代之后,编译器都将遵守mangle编码规则。这个规则是复杂的,具体可以参考Swift的ABI相关文档:https://github.com/swiftlang/swift/blob/main/docs/ABI/Mangling.rst


让我们先看一下IDA里面经常出现的一个函数: swift_getTypeByMangledNameInContext

 这个函数的声明如下:

const void * _Nullable swift_getTypeByMangledNameInContext(
                        const char * _Nullable typeNameStart,
                        int typeNameLength,
                        const void * _Nullable context,
                        const void * _Nullable const * _Nullable genericArgs);

在实际的逆向过程中,碰到好多函数最开始都会调用这个函数,xref了一下,发现有上万个xref。

__int64 __fastcall sub_1000B6ED0(__int64 *a1)
{
  __int64 result; // x0

  result = *a1;
  if ( result < 0 )
  {
    result = swift_getTypeByMangledNameInContext((char *)a1 + (int)result, -(result >> 32), 0LL, 0LL);
    *a1 = result;
  }
  return result;
}

  

第一个基本块里还有TBNZ指令判断是否只有一个问号,这个问号是为空类型。用python模拟这两条指令,

NEG             X1, X0,ASR#32
ADD             X0, X19, W0,SXTW

我们根据第二个基本块前面两个指令计算,实现De-Mangle,这样就可以给sub开头的方法做备注了。

以下面这个ref为例:

v8 = sub_1000B6ED0(&unk_1039839C0);
__data:00000001039839C0 unk_1039839C0   DCB 0x1C                ; DATA XREF: sub_1000219E0+34↑o
__data:00000001039839C0                                         ; sub_1000219E0+88↑o ...
__data:00000001039839C1                 DCB 0x80
__data:00000001039839C2                 DCB 0x4D ; M
__data:00000001039839C3                 DCB 0xFF
__data:00000001039839C4                 DCB 0xF9
__data:00000001039839C5                 DCB 0xFF
__data:00000001039839C6                 DCB 0xFF
__data:00000001039839C7                 DCB 0xFF
__data:00000001039839C8 unk_1039839C8   DCB 0xB8                ; DATA XREF: sub_10010950C+18↑o
__data:00000001039839C8                                         ; sub_1002C06B0↑o ...
__data:00000001039839C9                 DCB 0x2A ; *
__data:00000001039839CA                 DCB 0x4B ; K
__data:00000001039839CB                 DCB 0xFF
__data:00000001039839CC                 DCB 0xFA
__data:00000001039839CD                 DCB 0xFF
__data:00000001039839CE                 DCB 0xFF
__data:00000001039839CF                 DCB 0xFF

 unk_1039839C0这个整理出来,地址对应X19寄存器,值对应X0寄存器

X0 = 0xFFFFFFF9FF4D801C
X19 = 0x1039839C0

写个python模拟计算一下: 

def asr(value, shift):
    # 算术右移函数
    if value & (1 << 63):
        # 如果最高位是1,表示负数,需要填充1
        return (value >> shift) | (0xFFFFFFFFFFFFFFFF << (64 - shift))
    else:
        # 否则直接右移
        return value >> shift
    
def sxtw(value):
    # 符号扩展32位到64位
    if value & (1 << 31):
        # 如果最高位是1,表示负数,需要填充1
        return value | 0xFFFFFFFF00000000
    else:
        # 否则直接返回
        return value
    
# 初始值
X0 = 0xFFFFFFF9FF4D801C
X19 = 0x1039839C0
# 算术右移32位
shifted_value = asr(X0, 32)

# 取负值并确保结果在64位范围内
X1 = (-shifted_value) & 0xFFFFFFFFFFFFFFFF

# 打印结果
print(f"X1 = 0x{X1:016X}")

# 符号扩展W0到64位
extended_W0 = sxtw(X0 & 0xFFFFFFFF)

# 相加并确保结果在64位范围内
X0 = (X19 + extended_W0) & 0xFFFFFFFFFFFFFFFF

# 打印结果
print(f"X0 = 0x{X0:016X}")

输出结果: 

X1 = 0x0000000000000007
X0 = 0x0000000102E5B9DC

所以这个类型声明是在X0处的长度为X1的地方。我们看byte_102E5B9DC这个字段的长度也正好是7

 这个名字IDA自动给出了Demangle之后的内容。使用命令行也可以自己解析:

swift demangle s10Foundation3URLVMn                   
$s10Foundation3URLVMn ---> nominal type descriptor for Foundation.URL

重点来了:

另一种情况,当你跳转到X0所示的地址的时候,发现在swift5_typedef的这段当中,并没有找到上面那样直接带有名称的可见内容。只有一堆数据。

这两个颜色标注了开始和结束的位置。根据刚才上面的脚本计算的。

 我们在2的下一个位置,一直按D,转换成DCD数据类型:

这里就是偏移地址。我们计算: 

 0x102E4C5CB+0x3BB525=0x103207af0

__got:0000000103207AF0 _$ss23_ContiguousArrayStorageCMn_ptr DCQ _$ss23_ContiguousArrayStorageCMn ; nominal type descriptor for _ContiguousArrayStorage

到达got表了。 

这个方法是从刚才这个URL的地方反推出来的:

0x10045910D+0x6AF03=0x1004c4010 

__got:00000001004C4010 _$s10Foundation3URLVMn_ptr DCQ _$s10Foundation3URLVMn ; nominal type descriptor for URL

我们可以写一个idapython脚本来快速实现定位:

import ida_kernwin
import idc
import ida_bytes
import idaapi

def asr(value, shift):
    # 算术右移函数
    if value & (1 << 63):
        # 如果最高位是1,表示负数,需要填充1
        return (value >> shift) | (0xFFFFFFFFFFFFFFFF << (64 - shift))
    else:
        # 否则直接右移
        return value >> shift
    
def sxtw(value):
    # 符号扩展32位到64位
    if value & (1 << 31):
        # 如果最高位是1,表示负数,需要填充1
        return value | 0xFFFFFFFF00000000
    else:
        # 否则直接返回
        return value
    

def demangle_content_addr(content, addr):
    # 算术右移32位
    X0 = content
    X19 = addr
    shifted_value = asr(X0, 32)

    # 取负值并确保结果在64位范围内
    X1 = (-shifted_value) & 0xFFFFFFFFFFFFFFFF

    # 打印结果
    print(f"type_name_length = 0x{X1:016X}")

    # 符号扩展W0到64位
    extended_W0 = sxtw(X0 & 0xFFFFFFFF)

    # 相加并确保结果在64位范围内
    X0 = (X19 + extended_W0) & 0xFFFFFFFFFFFFFFFF

    # 打印结果
    print(f"type_name_start = 0x{X0:016X}")
    address = X0
    print(f"Try Jump To [ 0x{address:016X} ] and look for mangled plain text.")
    idaapi.execute_sync(lambda: idc.jumpto(address), idaapi.MFF_WRITE)
    end_address = address + X1
    idc.set_color(address, idc.CIC_ITEM, 0x00FF00)
    idc.set_color(end_address, idc.CIC_ITEM, 0xFFFF00)



# 定义一个函数,用于打印当前偏移地址
def swift5_demangle():
    current_offset = idc.here()
    print("当前偏移地址: 0x{:X}".format(current_offset))
    # 检查当前偏移地址是否是一个qword字段
    if idc.get_item_size(current_offset) == 8 and idc.is_qword(idc.get_full_flags(current_offset)):
        # 读取qword字段的值
        qword_value = ida_bytes.get_qword(current_offset)
        print("qword值: 0x{:X}".format(qword_value))
        demangle_content_addr(content=qword_value, addr=current_offset)
        return qword_value
    elif idc.get_item_size(current_offset) == 4 and idc.is_dword(idc.get_full_flags(current_offset)):
        # 读取dword字段的值
        dword_value = ida_bytes.get_dword(current_offset)
        print("dword值: 0x{:X}".format(dword_value))
        address = current_offset + dword_value
        print(f"Try Jump To [ 0x{address:016X} ] and look for mangled plain text.")
        idaapi.execute_sync(lambda: idc.jumpto(address), idaapi.MFF_WRITE)
        idc.set_color(address, idc.CIC_ITEM, 0xFF0000)
    else:
        print("当前偏移地址不是一个qword字段")
        return None

# 定义一个回调函数,用于绑定快捷键
class PrintOffsetHandler(ida_kernwin.action_handler_t):
    def activate(self, ctx):
        swift5_demangle()
        return 1

    def update(self, ctx):
        return ida_kernwin.AST_ENABLE_ALWAYS

# 注册快捷键动作
ACTION_NAME = "swift5_demangle:action"
ACTION_LABEL = "Swift5 Demangle"
SHORTCUT_KEY = "Ctrl-Shift-A"  # 根据需要更改快捷键

# 检查是否已注册动作,如果有则先取消
ida_kernwin.unregister_action(ACTION_NAME)

# 创建并注册动作描述
action_desc = ida_kernwin.action_desc_t(
    ACTION_NAME,
    ACTION_LABEL,
    PrintOffsetHandler(),
    None,
    None,
    0
)

if ida_kernwin.register_action(action_desc):
    ida_kernwin.attach_action_to_menu(
        "Edit/Other/Swift5 Demangle",  # 使用菜单路径使动作更容易访问
        ACTION_NAME,
        ida_kernwin.SETMENU_APP
    )

# 绑定快捷键到动作
ida_kernwin.update_action_shortcut(ACTION_NAME, SHORTCUT_KEY)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值