前言
本篇文章将介绍逆向实战
中经常使用的一门语言 Cycript
,它是由Cydia创始人Saurik
推出的一款脚本语言
,Cycript
混合了OC、JavaScript
语法的解释器,这意味着我们能够在一个命令中使用OC
或者JavaScript
,甚至两者并用
。它能够挂钩
正在运行的进程
,能够在运行时
修改很多东西。
一、Cycript安装
Cycript官网,目前最新版本0.9.594
。直接下载SDK
解压放在/opt
目录中
接着在~/.bash_profile
、~/.zshrc
中配置环境变量
export PATH=/opt/cycript_0.9.594/cycript
接着验证一下
cycript
进入cy#
表示配置成功。
⚠️注意:如果已经安装了
Monkey
则不需要
配置和安装cycript
了。因为monkey自带了cycript
,并且配置环境变量应改为
export CY=/opt/cycript_0.9.594/
export PATH=/opt/MonkeyDev/bin:$PATH:$CY
二、基本使用
只要将cycript
注入到应用中,我们就可以调用其中的命令了,Monkey重签名
注入的时候帮我们注入了,例如我们之前文章15-Hook原理(二)反Hook防护 & MokeyDev
中的例子MonKeyDemo
app包里面就包含了libcycript.dylib
,当Cycript
注入到目标应用,应用进程就会调用Cycript
的方法,开启
相应的端口
,以供第三方监听
。第三方可通过端口链接
进程,进入cy
环境,HOOK
当前进程
中的内存数据
。
2.1 进入Cycript环境
cycript
2.2 退出Cycript环境
control + d
2.3 附加进程
连接进程进入Cycript环境,例如
cycript -r 192.168.3.127:6666
这里手机和电脑要在同一wifi
,并且进入应用程序进程。ip为手机的ip
。端口号默认为6666
。
2.4 cycript调试命令
以上附加进程
链接好了后,就可以使用cycript
调试命令查看信息了,例如
- 获取
keyWindow
UIWindow.keyWindow()
-------------------------
#"; layer = >"
- 获取
UIApplication
单例对象
UIApp
-------------------------
#""
- 定义变量并赋值
var keyWd = UIWindow.keyWindow()
-------------------------
#"; layer = >"
keyWd.rootViewController
-------------------------
#" ChildViewControllers:(\n \"\"\n)"
当程序的
进程结束
,定义的所有变量
会一起释放
。
-
#对象地址
拿到该对象,可用于调用方法
#0x1270e9200
-------------------------
#" ChildViewControllers:(\n \"\"\n)"
-
*对象
可以取出对象的成员变量
*keyWd
-------------------------
{isa:iConsoleWindow,_responderFlags:@error,_constraintsExceptingSubviewAutoresizingConstraints:null,...
- 查看
视图结构
keyWd.recursiveDescription()
-------------------------
@"; layer = >\n | >\n...
也可格式化
打印,遇\n
换行
keyWd.recursiveDescription().toString()
-------------------------
`; layer = >
| >
| | >
| | | ; layer = >
| | | | >
| | | | | >
...
- 查询
当前进程
中指定类型
的对象
choose (UIButton)
-------------------------
[#">",#">",...
这和LLDB
中search
很像。
三、进阶
3.1 脚本自动连接
- 创建
cyShell目录
- 在
cyShell目录
下,创建cyConnect.sh
脚本 - 打开cyConnect.sh脚本,写入以下代码
cycript -r 10.165.44.19:6666
- 增加
可执行
权限
chmod +x cyConnect.sh
- 配置环境变量
export cyShell=/Users/Aron/cyShell/
export PATH=/opt/MonkeyDev/bin:$cyShell:$PATH
这样能全局使用
cyConnect.sh
cy#
直接就能自动连接
了。
3.2 Cycript 修改内存
接下来通过几个案例,使用Cycript
脚本,看看如何修改内存。
案例一:修改应用图标的红点数
- 使用
MonkeyDev
安装并运行wx8.0.2.ipa
- 使用
cyConnect.sh
附加进程 - 修改
BadgeString
[UIApp setApplicationBadgeString: @"999"]
⚠️注意:修改当前内存中的数据,应用
重复挂起唤醒
,数据就会被刷新。
案例二:修改红包金额
例如:正常的红包内容如下
- 首先通过
choose(UILabel)
找到红包金额
choose(UILabel).toString()
搜索1.00
得到地址0x149eaef00
- 修改红包的金额
cy# #0x149eaef00.text = @"¥88888888.00"
同样的方式,找到借款
UILabel的地址 0x14c33b460
,将text改为还款
cy# #0x14c33b460.text = @"还款"
- 修改后的内容
案例三:修改聊天内容
有2种方式
- 通过
cycript
搜索控件进行修改 - 通过
Xcode
的view debug
找到控件进行修改
可以看到内容在accessibility
的description
中
接着我们在logos文件夹下的.xm
文件中,使用logos
语法hook方法,代码如下
%hook RichTextView
- (_Bool)setPrefixContent:(id)arg1 TargetContent:(NSString *)arg2 TargetParserString:(id)arg3 SuffixContent:(id)arg4 {
// arg2 为文案
if([arg2 isEqualToString:@"钱已经借给你了。"]) {
arg2 = @"钱已经换给你了,请查收!";
}
return %orig;
}
%end
至此整个内容就修改完成了
3.3 高级用法
- 获取
bundle identifier
使用APPID
命令
cy# APPID
-------------------------
@"com.xxxxxx.MonKeyDemo"
- 获取视图层级
使用pviews()
命令
pviews()
-------------------------
`; layer = >
| >
| | >
| | | ; layer = >
| | | | >
| | | | | >
| | | | | | >
| | | | | | | >
| | | | | | | | >
| | | | | | | >
| | | | | | | | >
| | | | | | | | | >
| | | | | | | | >
| | | | | | | | | >
| | | | | | | >
| | | | | | | | >
| | | | >
| | | | | <_UIBarBackground: 0x113794170; frame = (0 -20; 375 64); userInteractionEnabled = NO; layer = >
| | | | | | >
| | | | | | <_UIBarBackgroundShadowView: 0x1137957a0; frame = (0 64; 375 0); layer = > clientRequestedContentView effect=none
| | | | | | | <_UIBarBackgroundShadowContentImageView: 0x113795b10; frame = (0 0; 375 0); opaque = NO; autoresize = W+H; userInteractionEnabled = NO; layer = >
| | | | | <_UINavigationBarContentView: 0x113794350; frame = (0 0; 375 44); layer = > layout=0x1137945d0
| | | | | | <_UITAMICAdaptorView: 0x11378e920; frame = (187 4; 1 36); autoresizesSubviews = NO; layer = >
| | | | | | | >
| | | | | | | | >
| | | | | >
| | | | | >`
也可以查看pviews方法
的代码实现
pviews
-------------------------
function (){return UIApp.keyWindow.recursiveDescription().toString()}
- 获取视图控制器
使用pvcs()
命令
cy# pvcs()
-------------------------
", state: appeared, view: \n | , state: appeared, view: "
pvcs方法的代码实现
pvcs
-------------------------
function (){return UIWindow.keyWindow().rootViewController._printHierarchy().toString()}
上面这些功能,Cycript
脚本也有,但是这些命令却不是Cycript
自带的,那么它们是哪里来的呢?
在Monkey工程我们可以在
Config->MDConfig.plist
中发现Cycript
下有ms
和md
两个.cy
文件
这些命令就封装在这两个文件中(工程运行的时候自动从网络上下载)。
ms.cy
md.cy
使用
Cycript
找到指定控制器
的某个控件
,过程比较繁琐
。
更好的方式 在非越狱设备上,使用应用重签名
,通过Debug View Hierarchy
快速定位控件,找到对应的地址
,然后使用Cycrip
t对其进行修改
。
四、cy文件的封装
案例一:封装简单案例
- 在
MokeyDemo
项目中,创建test.cy
文件 - 打开
test.cy
文件,写入以下代码
sum = function(a,b){
return a + b;
}
- 将
test.cy
文件拖入项目,在MokeyDemo
项目中,使用Copy Files
添加test.cy
⚠️注意:
test.cy
是脚本文件,不是MachO
,不需要
勾选签名
。
重新运行项目,使用
cyConnect.sh
附加进程导入脚本
@import test
-------------------------
{}
调用sum方法
sum(10,20)
-------------------------
30
案例二:实现获取当前控制器
- 修改
test.cy
文件的代码
(function(exports){
APPID = [NSBundle mainBundle].bundleIdentifier,
APPPATH = [NSBundle mainBundle].bundlePath,
APPHOME = NSHomeDirectory(),
rootVC = function(){
return UIApp.keyWindow.rootViewController;
};
keyWindow = function(){
return UIApp.keyWindow;
};
getCurrentVC = function(rootVC){
var currentVC;
if([rootVC presentedViewController]){
rootVC = [rootVC presentedViewController];
}
if([rootVC isKindOfClass:[UITabBarController class]]){
currentVC = getCurrentVC(rootVC.selectedViewController);
}
else if([rootVC isKindOfClass:[UINavigationController class]]){
currentVC = getCurrentVC(rootVC.visibleViewController);
}
else{
currentVC = rootVC;
}
return currentVC;
};
currentVC = function(){
return getCurrentVC(rootVC());
};
})(exports);
- 重新运行项目,使用
cyConnect.sh
附加进程 - 同样导入脚本
@import test
-------------------------
{}
- 调用方法
- 获取
APPID
APPID
-------------------------
@"com.xxxxxx.MokeyDemo"
- 获取
APPPATH
APPPATH
-------------------------
@"/private/var/containers/Bundle/Application/D620C178-5030-48E4-9276-981150FF7299/MokeyDemo.app"
- 获取
APPHOME
APPHOME
-------------------------
@"/var/mobile/Containers/Data/Application/C2ED1E99-47C4-4C29-8AE6-9C5C136CEE04"
- 调用
currentVC()
currentVC()
-------------------------
#""
总结
- Cycipt
- 是一种
脚本语言
,兼容了多种语法
(支持多种语法的解释器
) - Cycipt可以
附加
到进程,用来动态调试
- 也可
自定义脚本
,配置
到环境变量
中,方便执行
- 也可
- 将常用功能封装为
.cy文件
- 是一种