根据《逆向工程核心原理》大致目录总结下自学iOS逆向工程所掌握的知识
小端序标记法
计算机基本都是小端读取数据,怎么解释呢? 如下
10 20 30 40 a0 b0 c0 d0 · · · · · ·
读取1个字节数据 (int8) data = 0x10
读取4个字节数据 (int32) data = 0x40302010
读取8个字节数据 (int64) data = 0xd0c0b0a040302010
寄存器
寄存器是cpu数据的一种储存设备,储存着cpu运行的数据(包括cpu下一个运行指令的地址,pc/rip寄存器)
x86寄存器的作用
arm常用寄存器:
x0 函数第1个参数/储存函数返回值
x1 函数第2个参数
x2 函数第3个参数
x3 函数第4个参数
x4 函数第5个参数
x5 函数第6个参数
x6 函数第7个参数
x7 函数第8个参数
(后面的参数存储在栈中)
fp 栈帧(函数)
sp 栈顶(线程)
pc 当前线程所在CPU的核心运行的指令地址
lldb寄存器read and set:
register read 读取所有寄存器
register read x0 读x0寄存器
register set x0 1 将x0寄存器的值赋值为1
栈
初始化
线程是系统执行的最小单元,每个线程对象初始化时都会分配自己的栈空间储存函数执行过程中传递的参数和产生的变量 (当然上下文切换时,线程对象还会储存每个寄存器的值)
线程栈最关键的两个寄存器就是rbp和rsp(arm对应fp和sp),栈的特点,数据从高地址向低地址填充(push入栈)
rsp: 当前线程栈空间的栈顶,从栈地址到rsp地址的空间都是没使用过的
rbp: 当前线程函数栈使用的空间地址,即rsp到rbp这部分空间是该函数使用的空间(函数参数或者函数中的声明的变量)
以下是我之前写的创建线程的一个demo的一部分
创建线程栈
1
2
3
4var stack_size: vm_size_t = vm_page_size * 8
var stack_address: vm_address_t = 0
let ret1 = vm_allocate(mach_task_self_, &stack_address, stack_size, VM_MEMORY_STACK | VM_FLAGS_ANYWHERE)
let ret1_1 = vm_protect(mach_task_self_, stack_address, stack_size, 1, VM_PROT_READ | VM_PROT_WRITE)vm_allocate创建一块虚拟内存区域,并设置为可读可写
设置线程对象寄存器变量rsp指向栈顶,因为stack_address是低地址,需要加上栈大小
1
2
3
4
5// 6 是xnu中_STRUCT_X86_THREAD_STATE64结构的rbp变量偏移
state_64[6] = UInt64(stack_address+stack_size) - 4*0x8
// 7 是rsp变量偏移
state_64[7] = UInt64(stack_address+stack_size) - 7*0x8
调用
举个小函数调用栗子
1 | func sum(a: Int, b: Int) -> Int { |
伪指令
1 | 0x100000 call sum_label |
执行到 call sum_label 指令时,会先将当前指令的后面一条指令地址0x100004储存到栈上,在进行跳转
执行到push rbp 指令,将main函数的栈帧进行入栈保存,此时rsp = rsp - 8
mov rsp rbp, 设置sum函数的栈帧
sub 0x40 rsp,rsp = rsp - 0x40, 给当前sum函数的参数和变量分配空间
mov x0 -0x8(%rbp); mov x1 -0x16(%rbp) , 将参数a, b入栈,保存参数
add x0 x1, a + b并将值保存到a(x0)中, 返回值储存在x0中
add 0x40 rsp, 收回sum函数使用的栈空间
pop rbp,rbp出栈,rbp = main函数的栈帧, rsp = rsp + 8
ret,pc = *(rsp) 0x100004, rsp = rsp + 8(栈溢出攻击点或者泄露指令地址(0x100004),得到ASLR)
可执行文件格式
iOS
可执行文件格式和装载
符号动态链接
- Symbol Dynamic Linking 我写的AntiFishHook 分析
- Export Symbol SRouter 这是我利用iOS动态库的symbol export实现的路由方案; dlsym函数原理
- Rebase 由于ASLR将代码段的数据在装载时整体进行随机偏移了vm_slide,因此要将函数指针的值加上vm _slide(数据段同理)
- Symbol Table 可执行文件/动态库符号的信息,每个符号包括符号名偏移(String Table Index), 符号类型,符号所在的段和区,符号值,符号描述信息
调试(LLDB)
原理
debugserver(拥有task _for _pid权限)通过系统调用task _for _pid获取到被调试的进程pid的task端口
得到目标进程task端口,就能通过系统调用干下面的事了(XNU)
通信 :
1 .mach _msg系列函数进行进程通信
线程 :
thread _get _state读取寄存器(register read), thread _set _state修改寄存器(register write)
thread _(get)set _state的第二个参数thread _state _flavor _t 寄存器类型
可以设置不同类型的寄存器(线程寄存器,debug寄存器,异常寄存器等), br断点操作设置了debug寄存器。xnu的x86 _debug _state结构
thread _suspend暂停线程, thread _resume恢复线程
thread _info获取线程信息
动态库信息 :
1. task_info, task _get _dyld _image _infos等系列函数获取进程依赖库信息(image list)
虚拟内存 :
1. mach _vm _region获取被调试进程虚拟内存区域
2. mach _vm _read,mach _vm _write读写进程的内存
反编译 :
通过动态库信息和虚拟内存读取代码段内存内容以及符号表内容,再通过llvm反编译代码(lldb是llvm项目的一部分)
常用命令
我平常用的
1 | dis -a address |
Hook
函数指针hook
修改数据段,替换函数指针
objc imp hook
rumtime API
symbol pointer hook
内联Hook(inline hook)
修改代码段, 一般替换函数头几个指令
MSHookFuntion
MSHookFuntion and AntiMSHookFunction
动态库注入
原理
父进程通过posix _spawn fork出子进程,通过hook posix _spawn函数插入环境变量DYLD _INSERT _LIBRARIES,在dyld加载的时候会判断该环境变量是否有值,如果有,则加载该变量的值(注入的动态库)
或者hook dyld的_main函数,修改环境变量参数envp
命令
1 | DYLD_INSERT_LIBRARIES = insert.dylib |
重签名
原理
iOS签名
非对称加密,苹果后台拥有私钥,手机持有公钥
加密签名信息储存在可执行文件的Code Signature段中,通过手机本地公钥进行解密获取签名信息
命令
1 | codesign -f -s identity ***.dylib |
OLLVM混淆
wait to do
额外
书籍
《iOS应用逆向与安全》
《程序员的自我修养》
《深入解析Mac OS X & iOS操作系统》
源码
dyld
xnu
持续学习更新补充中。。。