当前位置: 首页 > article >正文

iOS Swift逆向——被编译优化后的函数参数调用约定修复

头文件导入:

typedef long long s64;
typedef unsigned long long u64;

typedef s64 Int;
typedef u64 Bool;

struct Swift::String
{
  u64 _countAndFlagsBits;
  void *_object;
};

union Swift_ElementAny {
    Swift::String stringElement;
};

struct Swift_Any {
    Swift_ElementAny element;
    u64 unknown;
    s64 type;
};

struct Swift_ArrayAny {
    s64 length;
    Swift_Any *items;
};

 https://github.com/doronz88/swift_reversing

 https://github.com/doronz88/ida-scripts/blob/main/swift.py

Swift <=> OC的兼容层

小gadget(片段)

; void sub_101A34B60()
sub_101A34B60
MOV             X0, X20 ; id
B               _objc_release
; End of function sub_101A34B60

因为_objc_release的参数只要X0。修复为:

void __usercall sub_101A34B60(__int64 a1@<X20>)

根据swift官方文档,X20是self。所以这是内存引用计数-1。

https://github.com/swiftlang/swift/blob/13da4c2ad3077282f7c8922b293a74c32e79e428/docs/ABI/CallConvSummary.rst#L151


字符串操作

字符串对象由两个寄存器构成。

以String.append为例进行修复调用约定: 

X20是self。

Swift::Void __usercall String.append(_:)(void *a1@<X20>, Swift::String a2@<X0:X1>)
{
  __imp__$sSS6appendyySSF(a2._object, a2._countAndFlagsBits);
}
Swift::String __usercall dispatch thunk of CustomStringConvertible.description.getter@<X0:X1>(
        void *a1@<X20>,
        __int64 *a2@<X0>,
        __int64 *a3@<X1>)

 a=100;print("myIntVariable1= \(a)")的整理后IDA结果:

  v11 = 0xD000000000000010LL;                   // myIntVariable1= 
  v12 = (id)0x800000010000CDF0LL;
  v10 = 100LL;
  v3 = dispatch thunk of CustomStringConvertible.description.getter(
         &v10,
         &type metadata for Int,
         &protocol witness table for Int);
  String.append(_:)(&v11, (Swift::String)__PAIR128__(v3._countAndFlagsBits, (unsigned __int64)v3._object));
  swift_bridgeObjectRelease(v3._object);
  v4 = v11;
  v5 = v12;
  *(_QWORD *)(v2 + 56) = &type metadata for String;
  *(_QWORD *)(v2 + 32) = v4;
  *(_QWORD *)(v2 + 40) = v5;
  v15._countAndFlagsBits = 32LL;
  v15._object = (void *)0xE100000000000000LL;
  v18._countAndFlagsBits = 10LL;
  v18._object = (void *)0xE100000000000000LL;
  print(_:separator:terminator:)((Swift_ArrayAny *)v2, v15, v18);
  swift_release(v2);

字符串Array

Swift::String __usercall BidirectionalCollection___joined_separator__@<X0:X1>(Swift_ArrayAny@<X20:X21>, Swift::String@<X0:X1>, __int64@<X2>, __int64@<X3>)


栈内存偏移计算相关小函数 

原先的函数点开里面是空的。 

sub_101A348B8
LDUR            X8, [X0,#-8]
LDR             X8, [X8,#0x40]
MOV             X9, SP
ADD             X8, X8, #0xF
AND             X8, X8, #0xFFFFFFFFFFFFFFF0
SUB             X19, X9, X8
MOV             SP, X19
RET
; End of function sub_101A348B8

 定义结构体:

00000000 stack_offset_struc struc ; (sizeof=0x18, copyof_7619)
00000000 offset          DCQ ?
00000008 sp              DCQ ?
00000010 addr            DCQ ?
00000018 stack_offset_struc ends

 定义调用约定。F5之后,函数体原来出现内容。

stack_offset_struc __usercall __spoils<> sub_101A348B8@<0:X8, 8:X9, 16:X19>(__int64 a1@<X0>)
{
  __int64 *v1; // x9
  __int64 v2; // x8
  char *v3; // x19
  __int64 v4; // [xsp+0h] [xbp+0h] BYREF
  stack_offset_struc result; // 0:x8.16,16:x19.8

  v1 = &v4;
  v2 = (*(_QWORD *)(*(_QWORD *)(a1 - 8) + 64LL) + 15LL) & 0xFFFFFFFFFFFFFFF0LL;
  v3 = (char *)&v4 - v2;
  result.addr = (__int64)v3;
  result.sp = (__int64)v1;
  result.offset = v2;
  return result;
}

另一个函数也进行修复:

sub_101A34B84
LDUR            X28, [X0,#-8]
LDR             X8, [X28,#0x40]
MOV             X9, SP
ADD             X8, X8, #0xF
AND             X8, X8, #0xFFFFFFFFFFFFFFF0
RET
stack_offset_struc __usercall __spoils<> sub_101A34B84@<0:X8, 8:X9, 16:X28>(__int64 a1@<X0>)
{
  __int64 v1; // x28
  __int64 *v2; // x9
  __int64 v3; // x8
  __int64 v4; // [xsp+0h] [xbp+0h] BYREF
  stack_offset_struc result; // 0:x8.16,16:x28.8

  v1 = *(_QWORD *)(a1 - 8);
  v2 = &v4;
  v3 = (*(_QWORD *)(v1 + 64) + 15LL) & 0xFFFFFFFFFFFFFFF0LL;
  result.addr = v1;
  result.sp = (__int64)v2;
  result.offset = v3;
  return result;
}

这两个小函数,其实是完全一样的。

可以发现第二个函数sub_101A34B84相比较于sub_101A348B8少了最后的SUB。其实这个SUB在上层调用者函数里面,看最后两行汇编:

STP             X28, X27, [SP,#-0x10+var_50]!
STP             X26, X25, [SP,#0x50+var_40]
STP             X24, X23, [SP,#0x50+var_30]
STP             X22, X21, [SP,#0x50+var_20]
STP             X20, X19, [SP,#0x50+var_10]
STP             X29, X30, [SP,#0x50+var_s0]
ADD             X29, SP, #0x50
SUB             SP, SP, #0x70
MOV             X20, X3
MOV             X23, X2
MOV             X24, X1
MOV             X25, X0
ADRL            X21, unk_1039839C0
MOV             X0, X21
BL              sub_1000B6ED0
BL              sub_101A348B8
BL              sub_101A34D54
MOV             X22, X0
BL              sub_101A34B84
SUB             X27, X9, X8
MOV             SP, X27

修复完成后,返回调用该函数的上层函数,在按F5。可以看到恢复之后的代码。

参考IDA官网博客里面的内容: 

 https://hex-rays.com/blog/author/igor-skochinsky


 https://github.com/swiftlang/swift/blob/65f7c9eeb50ebb2586fce4c4808f069cfb584b79/stdlib/public/core/EmbeddedRuntime.swift#L326

大部分函数的调用约定可以在源代码里面直接看到:

@_cdecl("swift_bridgeObjectRetain")
public func swift_bridgeObjectRetain(object: Builtin.RawPointer) -> Builtin.RawPointer {
  return swift_bridgeObjectRetain_n(object: object, n: 1)
}


http://www.kler.cn/a/369196.html

相关文章:

  • 【算法刷题指南】双指针
  • python学习之for i in...
  • Zookeeper面试整理-分布式系统知识
  • linux指令笔记
  • maven之pom.xml文件解读
  • error Unexpected mutation of “xxxxx“ prop
  • C#中的事件
  • 029_Common_Plots_Matlab常见二维绘图
  • 【阅读笔记】Instruction-based Hypergraph Pretraining
  • PHP如何实现字符串翻转
  • 【实战案例】Django框架表单处理及数据库交互
  • 【YOLOv11[基础]】实例分割 + 跟踪
  • 二叉树习题其六【力扣】【算法学习day.13】
  • 基于KV260的基础视频链路通路(MIPI+Demosaic+VDMA)
  • Page Cache(页缓存)的大小如何确定
  • Win11安装基于WSL2的Ubuntu
  • 大型语言模型与人类价值观对齐:去中心化开放数据获取平台
  • NVR管理平台EasyNVR多个NVR同时管理汇聚方案
  • STM32 RTC时间无法设置和读取
  • 【K8s】Kubernetes 证书管理工具 Cert-Manager
  • 【mysql 进阶】2-1. MySQL 服务器介绍
  • 小米面试题:多级缓存一致性问题怎么解决
  • UDP 实现的 Echo Server 和 Echo Client 回显程序
  • 雷池社区版OPEN API使用教程
  • 模拟 DDoS 攻击与防御实验
  • 链表学习的疑惑