最近接触iOS逆向开发,看了各式各样的自动抢红包Tweak之后,于是自己也来动手实践一下Tweak~
一、准备
- 越狱手机一台 - 主要用于脱壳(32Bit最佳,Hopper Disassembler能直接看伪代码)
- Hopper Disassembler - 反编译工具,能够将二进制执行文件反编译出伪代码
- ldid - 签名工具
- chisel - LLDB增强
- class-dump - 导出可执行文件的Header
- dumpdecrypted - 脱壳工具
- Clutch - 高级脱壳工具
- theos - 越狱开发工具包,安装与配置可参考Theos安装与配置
二、debugserver与LLDB
debugserver为LLDB服务端,接收LLDB所提供的命令,并且进行相应的执行。
获取debugserver
- debugserver存放于iPhone设备上
/Developer/usr/bin
目录,如果没有,打开Xcode并捅一下手机就能安装上 - 通过
scp
或者其它任何手段复制到电脑上
处理debugserver
保存以下xml并命名为ent.xml
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>com.apple.springboard.debugapplications</key> <true/> <key>get-task-allow</key> <true/> <key>task_for_pid-allow</key> <true/> <key>run-unsigned-code</key> <true/> </dict> </plist>
- 执行命令:
ldid -Sent.xml debugserver
- 通过
scp
或者其它任何手段复制到设备/usr/bin
目录下 - 并加上执行权限(chmod +x)
- 执行
debugserver
命令无报错即可
LLDB连接debugserver
- 打开微信客户端
- 通过ssh连接至设备,执行命令:
debugserver *:6666 -a 'WeChat'
- 电脑中运行lldb并执行命令:
process connect connect://devcie_ip:6666
- 设备处于process interrupted状态,连接成功
补充知识
在进行LLDB调试之前需要了解一些概念:
- 基址:模块在内存中的起始地址
- ASLR:虚拟内存起始地址与模块基地址的偏移量(通过image list -o -f查看)
因此:内存地址 = ASLR + 基址
Hopper Disassembler进行静态分析中显示的都是基址,LLDB操作的都是内存地址,因此要进行换算。
三、定位函数
数据提交时机
想要实现任意地区修改,首先要知道数据提交的时机,来hook相关的方法修改提交的参数。观察微信客户端修改个人资料页,大概猜测出两种修改机制:
- 每修改一项信息,在Pop一个ViewController的时候提交数据保存
- 修改好所有的信息,在Pop回至TopViewController的时候提交数据保存
验证猜想也很简单,找多一台设备进行观察就可以了,结果猜想2是正确的!
找出对应Controller
- 打开个人信息页面并执行命令:
process interrupt
- 执行命令
pviews
输出UI层级树,不难找出MMTableView
及其内存地址 - 执行命令
presponder MMTableView内存地址
输出响应层级树,找出个人信息页面的Controller为SettingMyProfileViewController
- 重复以上步骤找出地区选择页面的Controller
找出相关方法
利用dumpdecrypted
脱壳,再通过class-dump
Dump出所有.h
文件(过程就不再赘述),要关注的是所有与SettingMyProfileViewController
和MMRegionPickerViewController
有关的.h
文件。
其中发现MMRegionPickerViewControllerDelegate-Protocol.h
文件,显然地区选择页面是通过Delegate回调的方式进行传值,并且在SettingMyProfileViewController.h
文件中找到Delegate对应的方法,因此可以确定SettingMyProfileViewController
就是实现了对应的Delegate,接受地区参数。
动态调试
既然已经基本上确定了参数传递的方法,那就可以通过Hopper Disassembler + LLDB进行开刀!
- 在Hopper Disassembler中直接搜索
SettingMyProfileViewController MMRegionPickerDidChoosRegion
,找出方法入口基址 -
根据ASLR与基址计算出内存地址,通过LLDB设置断点:
br s -a 内存地址
-
选择任意地区触发断点
-
执行命令:
po [$r2 class]
查看参数类型 -
执行命令:
po $r2
查看参数值
根据输出,发现是NSArray
类型!并且数组内元素分别为:语言类型、国家、地区。
因此关键方法和传递参数已经确定,只要对其进行Hook就能实现修改~
四、编写Tweak
通过theos创建Tweak模板,编辑Tweak.xm
:
%hook MMRegionPickerViewController - (void)viewDidLoad { %orig; [[self navigationItem] setRightBarButtonItem:[[UIBarButtonItem alloc] initWithTitle:@"自定义" style:UIBarButtonItemStylePlain target:self action:@selector(setCustomRegion)]]; } %new - (void)setCustomRegion { UIAlertView *alert = [[UIAlertView alloc] init]; alert.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput; alert.delegate = self; alert.title = @"设置自定义地区"; [alert addButtonWithTitle:@"取消"]; [alert addButtonWithTitle:@"确定"]; [alert textFieldAtIndex:0].secureTextEntry = NO; [alert textFieldAtIndex:0].placeholder = @"国家"; [alert textFieldAtIndex:0].keyboardType = UIKeyboardTypeDefault; [alert textFieldAtIndex:1].secureTextEntry = NO; [alert textFieldAtIndex:1].placeholder = @"地区"; [alert textFieldAtIndex:1].keyboardType = UIKeyboardTypeDefault; [alert show]; } %new - (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex { if (buttonIndex == 1) { NSString *country = [alertView textFieldAtIndex:0].text; NSString *province = [alertView textFieldAtIndex:1].text; [[self delegate] MMRegionPickerDidChoosRegion:@[@"", country, province]]; } } %end
五、安装Tweak
- 编辑
Makefile
中THEOS_DEVICE_IP
为设备IP -
执行命令:
make package install
-
查看效果