逆向笔记
Mach-O为Mach object文件格式的缩写,它是一种可执行文件、目标代码、动态库的文件格式,作为a.out格式的代替,Mach-O提供了更强的扩展性。
-
Mack-O文件常见的格式
- 目标文件.o
- 库文件
- .a
- .dylib
- Framework
- .nib
- 可执行文件
- dyld
- dsym
通用二进制文件:苹果公司提出的一种程序代码,能够适用于多种架构的二进制文件,所以文件的包比单一平台的二进制程序文件要大,但由于其中的部分文件是通用的,所以大小并不会倍数增加,执行时也不会占用额外的运行内存
MachO文件被内核读取,最终加载是dyld
Mach-O文件结构
- Header包含该二进制文件的一般信息
- 字节顺序、架构类型、加载指令的数量等
- 是的可以快速确认一些信息,比如当前文件用于32位还是64位,对应的处理器是什么、文件类型是什么。
- Load commands一张包含很多内容的表
- 内容包括区域的位置、符号表、动态符号表等。
- Data通常是对象文件中最大的部分
- 包含Segement的具体数据
指令集合
file name //查看文件
lipo -info MachOName //查看文件支持的架构类型
lipo mackoName -thin armA -output armAName //将通用二进制文件拆分为单一架构的文件
lipo -create armA armB -output armAB //将A、B两种架构合并为AB架构
otool指令
otool -f MachO //查看胖二进制文件macho文件的头信息
otool -l MachOName //查看通过查看输出的cryptid可以判断是否加壳
dyld(苹果动态连接器)
- dyld是苹果的动态连接器,是苹果操作系统一个重要组成部分,在系统内核做好程序准备工作后,交给dyld负责余下的工作。
- load在main之前调用,dyld加载在load之前
- ASLR 偏移,苹果防护的机制之一
Hook概述
- Method Swizzle 利用OC的Runtime特性,动态改变SEL(方法编号)和IMP(方法实现)的对应关系,达到OC方法调用流程改变的目的
- fishhook 它是facebook提供的一个动态修改连接mach-o文件的工具,利用MachO文件加载原理,通过修改懒加载和费懒加载两个表的指针达到C函数HOOK的目的。
- Cydia Substrate ,他的主要作用是针对OC方法、C函数以及函数地址行进HOOK操作,安卓也可以使用
- PIC 位置独立代码
越狱工具的使用
- Chisel 依附于xcode查看界面层级的插件
Chisel指令列表
plass viewName 输出控件的层级结构
pviews viewName 打印view层次
methods view 打印view下的方法列表
pinternals viewAds 打印view的属性等
fvc name 通过名称查找控制器对象
fvc -v ctlAds 查找控制器对象信息
pvc 打印出当前控制器层级结构
taplog 将点击存在相应事件的控件打印输出
flicker viewAds 使对应的控件闪烁
vs viewAds 进入一种可以切换界面的状态
LLDB
- 依附在xcode中进行app的分析
search class 查找对象
sbt 输出恢复符号的栈信息
b -a methedAds 通过地址给方法设置断点
Cycript
- 可以附加到某个进程上面进行界面分析
cycript -r 10.37.34.175:6666 通过网络附加于手机上的某个进程
UIWindow.keyWindow() 输出当前的window
[UIApplication sharedApplication] 输出sharedApplication对象
*#address 获取对象的成员变量
keyWindow.recursicveDescription() 查看所有的子view
pactions(#address) 输出地址对象所关联的方法
rp(#address) 输出地址对象的响应者链条
UIApp.keyWindow.recursiveDescription().toString() 有格式的输出当前子控件
#0x149c07620.allTargets() 输出按钮地址为0x149c07620的target
#0x149c07620.allControlEvents() 输出地址为0x149c07620按钮的事件
[#btnAds actionsForTarget:#targetAds forControlEvent:btnEvent]; 输出当点击btn、target为targetAds时调用的方法
class-dump -H MachO -o 输出的头文件 取出macho文件的头文件并输出
#0x1262a40a0.nextResponder() 查找0x1262a40a0的下一级相应者
#0x1262a40a0.subviews() 查找0x1262a40a0的子控件
Logos语法
%group groupName1
分组,需要进行初始化
%end
%group groupName2
分组,需要进行初始化
%end
------------
构造函数
%ctor {
//可在此处根据应用信息进行分类加载
%init(groupName1)%init(groupName2)
}
-----------
%log 输出栈所带的函数
%orig 调用原始方法
%new 给某个对象添加方法,不需要%end
插件使用
- Apple File Conduit"2" 可获得访问根目录权限
- AppSync Unified 绕过签名监测,可通过电脑直接安装已经越狱的ipa包
- adc-cmds 可以支持在电脑端使用命令行工具
SSH登陆流程
- 客户端发起连接请求
- 服务端收到连接请求后返回公钥给客户端
- 客户端使用公钥对密码进行加密并传递给服务端
- 服务端通过私钥解密客户端传来的信息判断是否允许登陆
- 密码是服务端随机生成的,在一定程度上提升了安全性,但是一旦公钥被劫持就无法保证登陆的安全性
- 为了保证安全,服务端可将公钥的哈希值公布,当客户端拿到公钥后和公布的哈希值相对比,如果相同则安全,由于中间人没有私钥,所以无法破解信息,所以可以安全传输
ssh指令
ssh-keygen -R ip 删除ip对应的公钥
免密登录流程
- 用户将自己的公钥存储在服务器中
- 登陆时远程服务器想用户发送一段随机字符串,用户用私钥加密后再返回来
- 远程主机用事先存储的公钥进行解密,如果成功解析后判断是否允许登陆
scp 文件名 目标路径 将目标文件copy带目标路径文件中
ps -A | grep appName app在手机中的路径
otool -l MachOName | grep crypt 查看macho文件是否为加密
砸壳
- 当ipa提交给Appstore发布时官方会进行加密操作,这样可以保证机器上运行的应用都是苹果审核通过的,也可以管理软件授权等,经过加密的ipa我们无法通过Hopper等反编译静态分析,也无法Class-Dump,在逆向中需要对加密的二进制文件进行解密才可以进行静态分析,这一过程称为砸壳(脱壳)
- 应用砸壳
- 静态砸壳:静态砸壳就是再已经账务和了解到了壳应用的加密算法和逻辑后再不运行可应用的前提下将壳应用程序进行解密处理,静态脱壳的方法难度大,而且加密放发现应用被破解后就可能改变加密方式采用更加高级切复杂的加密技术
- 动态砸壳:动态砸壳就是从运行在进程内存空间中的可执行程序影像(image)入手,来将内存中的内容进行转存(dump)处理来实现脱壳处理,这种方法实现起来相对简单,切不必关心使用的是何种加密技术
脱壳工具
- Clutch 需要copy到手机上
r 读权限
w 写权限
x 执行权限
chmod +x Clutch 给Clutch添加执行权限
Clutch -i 列出可砸壳的应用
Clutch -d 应用id 对手机中的某个应用砸壳
Device/var/mobile/Documents/Dumped 砸过壳的ipa所在位置
当Clutch2.4移动到/usr/bin目录下后要重命名为Clutch
- dumpdecrypted
编译后将dumpdecrypted.dylib文件导入到手机的根目录
直接运行
DYLD_INSERT_LIBRARIES=应用路径 就可在根目录获得砸壳后的应用
- frida-ios-dump
Theos 使用
ps -A 列出手机中所有的进程
ps -A | grep AppName 输出当前手机中名字为AppName的应用
cycript -p AppName 将应用依附在AppName上
- $THEOS/bin/nic.pl 启动theos,并填写配置信息
- 到生成的tweak文件夹中,在Makefile中添加配置信息
export THEOS_DEVICE_IP=127.0.0.1
export THEOS_DEVICE_PATH=12345
- 在Tweak.x中添加hook代码
- 在tweak文件夹目录下执行make、make package、make install
debugserver
debugsever *:12346 -a HelloTrip 手机输入进入等待连接状态
lldb 电脑端输入lldb进入调试状态
process connect connect://10.37.34.175:12346 电脑端输入连接手机(wifi连接)
//通过usb连接
python tcprelay.py -t 22:12345 12346:12346 在启动ssh连接时增加映射端口
process connect connect://localhost:12346 在电脑端进行lldb连接时执行的命令
process interrupt 在lldb连接的情况下进入断点状态
使用MonkeyDev中的tweak来hook项目
- 在DynamicLibraries下的Bundles中添加item并将需要被hook的项目bundle id加入到item对应的value中
- 在Build Setting中搜索monkey,将MonkeyDevDeviceIP设置为localhost(ssh usb连接),MonkeyDevDevicePassword设置alpine(如果没修改的话),MonkeyDevDevicePort填写映射的端口,MonkeyDevkillProcessOnInstall填写当装载成功时重启的应用
- 点击运行即可将tweak安装到手机上
tweak原理
- 通过环境变量插入的方式进行hook的
- make时theos会将我们编写的文件大包为.dylib库文件
- make install时会将.dylib文件导入到手机Device/Library/MobileSubstrate/DynamicLibraries文件夹下
dyld代码分析
- 在运行代码时,程序会在MachO文件中寻找__RESTRICT所对应的字段,如果字段值为__restrict时,那么该程序拒绝插入,防止被直接hook
配置monkeydev调用方法输出
- 将MDConfig.plist中的文件配置为如图的设置
[图片上传失败...(image-c7b586-1565677706001)]
恢复符号表
- cd 到restore-symbol目录下,执行
make restore-symbol
- 将想要恢复的MachO文件拖到restore-symbol文件夹中
- 执行指令
./restore-symbol MachOName -o outputMachOName
- 将获得的MachO文件替换之前ipa中的MachO文件
logs语句
MSHookIvar<变量类型 *>(class, "成员变量名称") //hook成员变量
ptrace防护
- ptrace文件的获取:创建MacOS下的命令行工程,导入
#import
进入之后复制其中的代码并粘贴到自己创建的头文件中
使用方法导入自己创建的头文件
/**
arg1:ptrace 要做的事情 PT_DENY_ATTACH为禁止附加
arg2:要操作的进程的id 0代表本进程
arg3:地址
arg4:数据
*/
ptrace(PT_DENY_ATTACH, 0, 0, 0);
- 防护特点
- xcode重签名之后闪退
- 手动打开正常运行
- 通过debug/attach to projrect/project进行附加的时候会附加失败
debug调试检测
- 通过检测项目是否处于debug状态从而判断应用是否被破解逆向
问题------------------
- 当该应用被插入第三方库时显示的结果?
混淆
- pch文件混淆
//隐藏UserInfoClass字段在反汇编工具中的展示可在pch文件中声明,如此则在代码中不会显示UserInfoClass的字段
#define UserInfoClass fhdjskafd
- 重要信息的隐藏
//eg:如果一个字符串为GPF的一个字符串不想让别人获取到可提高反汇编难度,通过以下方法保存的字符串不会在常量区直接显示,增加了汇编的难度
#define STRING_ENCRYPT_KEY 0xAC
static NSString * AES_KEYINFO() {
unsigned char key[] = {
(STRING_ENCRYPT_KEY ^ 'G'),
(STRING_ENCRYPT_KEY ^ 'P'),
(STRING_ENCRYPT_KEY ^ 'F'),
(STRING_ENCRYPT_KEY ^ '\0'),
};
unsigned char * p = key;
while (((*p) ^= STRING_ENCRYPT_KEY) != '\0') {
p++;
}
return [NSString stringWithUTF8String:(const char *)key];
}
防护之ptrace隐藏
- 方法一dlopen
//通过该方法调用的ptrace无法被fishhook所hook
#import
void dlopenAction() {
unsigned char funcStr[] = {
('a' ^ 'p'),
('a' ^ 't'),
('a' ^ 'r'),
('a' ^ 'a'),
('a' ^ 'c'),
('a' ^ 'e'),
('a' ^ '\0'),
};
unsigned char * p = funcStr;
while (((*p) ^= 'a') != '\0') p++;
//通过dlopen拿到句柄
void * handle = dlopen("/usr/lib/system/libsystem_kernel.dylib", RTLD_LAZY);
//定义函数指针
int (* old_ptrace)(int _request, pid_t _pid, caddr_t _addr, int _data);
if (handle) {
old_ptrace = dlsym(handle, (const char *)funcStr);
if (old_ptrace) {
old_ptrace(PT_DENY_ATTACH, 0, 0, 0);
}
}
}
- 方法二syscall
- 内核提供用户空间程序与内核空间进行交互的一套标准接口,这些接口让用户态程序能受限访问硬件设备,比如申请系统资源,操作设备读写,创建新进程等。用户空间发生请求,内核空间负责执行,这些接口便是用户空间和内核空间共同识别的桥梁,这里提到两个字“受限”,是由于为了保证内核稳定性,而不能让用户空间程序随意更改系统,必须是内核对外开放的且满足权限的程序才能调用相应接口。在用户空间和内核空间之间,有一个叫做Syscall(系统调用, system call)的中间层,是连接用户态和内核态的桥梁。这样即提高了内核的安全型,也便于移植,只需实现同一套接口即可。通过该方法调用ptrace无法被符号断点阶段
syscall(26, 31, 0, 0); //26为ptrace在syscall中的方法编号, 31位ptrace的第一个参数
- 方法三 通过汇编调用syscall,防止syscall被hook,改代码无法被断点断掉
asm volatile (
"mov x0,#26\n"
"mov x1,#31\n"
"mov x2,#0\n"
"mov x3,#0\n"
"mov x16,#0\n"
"svc #0x80\n" //触发中断
);
- 方法四 直接通过汇编调用ptrace
asm volatile (
"mov x0,#31\n"
"mov x1,#0\n"
"mov x2,#0\n"
"mov x3,#0\n"
"mov x16,#26\n"
"svc #0x80\n" //触发中断
);
越狱检测
char * dlname = getenv("DYLD_INSERT_LIBRARIES");
NSLog(@"%s", dlname);
当dlname有值的时候为越狱状态,无值的时候为非越狱状态
防止重签名
- bundile id的检测,很容易被绕过
- 正常从appstroe下载的应用已经没有描述性文件,重签之后会出现描述性文件,可以通过描述性文件来检测