欢迎重视微信公众号:FSA全栈举动
一、简介
Frida
是一个跨平台的轻量级 Hook
框架,支撑 MacOS
、Linux
和 Windows
操作体系,供给了精简的 Python
接口和功能丰厚的 JS
接口,除了能够运用自身的控制台交互以外,还能够运用 Python
将 JS
脚本注入到运转程序中,经过 Frida
能够获取程序的具体信息、阻拦和调用指定函数、注入代码、修正参数等。
Frida
源代码托管:github.com/frida
二、装置
iOS
增加软件源 https://build.frida.re/
,然后查找 Frida
装置即可。
装置完结后可能经过 ps
看到 frida-server
后台程序则阐明装置成功,若没有能够重启手机后再看看
lxf-iPad:~ root# ps -ax | grep frida
26717 ?? 0:00.08 /usr/sbin/frida-server
26731 ttys000 0:00.01 grep frida
MacOS
运用 pip
进行装置
pip install frida-tools # CLI tools
pip install frida # Python bindings
后续需求升级的话,能够运用 --upgrade
参数
pip install frida-tools --upgrade
pip install frida --upgrade
假如报 command not found:pip
错误,阐明当时体系没有装置 pip
,能够运用下方指令装置
brew install wget
wget https://bootstrap.pypa.io/get-pip.py
python3 get-pip.py
履行完结后会提示你将对应 python
版本的 bin
途径增加到 PATH
中,如:
export PATH=~/Library/Python/3.8/bin:$PATH
当然,假如你也有用 pyenv
,则能够疏忽上述的 pip
装置流程,由于 pyenv
自带了 pip
。
你能够用 which
指令检查你的 pip
的装置方位。
➜ ~ which pip
/usr/local/var/pyenv/shims/pip
三、入门
除了 frida
主程序外,frida-tools
里还供给了五个实用东西,它们位于 /usr/local/bin/
目录下
ls -al /usr/local/bin/frida-*
假如运用的是 pyenv
的 pip
装置的 frida
,则它们会被装置到 /usr/local/var/pyenv/shims/
目录下
ls -al /usr/local/var/pyenv/shims/frida-*
-rwxr-xr-x 1 lxf admin 180 3 19 22:05 /usr/local/var/pyenv/shims/frida-apk
-rwxr-xr-x 1 lxf admin 180 3 19 22:05 /usr/local/var/pyenv/shims/frida-create
-rwxr-xr-x 1 lxf admin 180 3 19 22:05 /usr/local/var/pyenv/shims/frida-discover
-rwxr-xr-x 1 lxf admin 180 3 19 22:05 /usr/local/var/pyenv/shims/frida-join
-rwxr-xr-x 1 lxf admin 180 3 19 22:05 /usr/local/var/pyenv/shims/frida-kill
-rwxr-xr-x 1 lxf admin 180 3 19 22:05 /usr/local/var/pyenv/shims/frida-ls-devices
-rwxr-xr-x 1 lxf admin 180 3 19 22:05 /usr/local/var/pyenv/shims/frida-ps
-rwxr-xr-x 1 lxf admin 180 3 19 22:05 /usr/local/var/pyenv/shims/frida-trace
1、检查可用的设备列表
frida-ls-devices
用于获取可用的设备列表,在多设备交互的情况下会非常有用
➜ ~ frida-ls-devices
Id Type Name
---------------------------------------- ------ ------------
local local Local System
d007dc58edd70caad950ff01b41ebf73cfa49fbe usb iPad
socket remote Local Socket
2、获取设备的进程列表
frida-ps
用于获取进程列表信息
➜ ~ frida-ps --help
usage: frida-ps [options]
options:
-h, --help show this help message and exit
-D ID, --device ID connect to device with the given ID
-U, --usb connect to USB device
-R, --remote connect to remote frida-server
-H HOST, --host HOST connect to remote frida-server on HOST
--certificate CERTIFICATE
speak TLS with HOST, expecting CERTIFICATE
--origin ORIGIN connect to remote server with “Origin” header set to
ORIGIN
--token TOKEN authenticate with HOST using TOKEN
--keepalive-interval INTERVAL
set keepalive interval in seconds, or 0 to disable
(defaults to -1 to auto-select based on transport)
--p2p establish a peer-to-peer connection with target
--stun-server ADDRESS
set STUN server ADDRESS to use with --p2p
--relay address,username,password,turn-{udp,tcp,tls}
add relay to use with --p2p
-O FILE, --options-file FILE
text file containing additional command line options
--version show program's version number and exit
-a, --applications list only applications
-i, --installed include all installed applications
-j, --json output results as JSON
这儿阐明一下常用的指令参数
参数 | 描绘 |
---|---|
-U |
连接到 USB 设备 |
-D |
假如当时有多台 USB 设备,能够运用该参数指定设备的 UDID (frida-ls-devices 列出的那些 id ) |
-R/-H |
连接到长途 frida-server ,首要用于长途调试 |
-a |
仅显示正在运转的运用 |
-i |
显示一切已装置的运用(包括 AppStore 装置的运用和体系运用) |
具体运用如下:
连接到 USB
设备检查进程列表
~ frida-ps -U
PID Name
----- ---------------------------------------------------
25226 Cydia
26745 Twitter
21611 邮件
25055 AppPredictionWidget
20944 AppleCredentialManagerDaemon
1687 AssetCacheLocatorService
23387 CMFSyncAgent
...
连接到 USB
设备检查正在运转的运用
➜ ~ frida-ps -U -a
PID Name Identifier
----- ----------- --------------------
25226 Cydia com.saurik.Cydia
26745 Twitter com.atebits.Tweetie2
21611 邮件 com.apple.mobilemail
➜ ~
连接到 USB
设备检查一切装置的运用
➜ ~ frida-ps -U -a -i
PID Name Identifier
----- --------------------------- ------------------------------------------
25226 Cydia com.saurik.Cydia
26745 Twitter com.atebits.Tweetie2
21611 邮件 com.apple.mobilemail
- App Store com.apple.AppStore
- FaceTime 通话 com.apple.facetime
- LXFProtocolTool_Example org.cocoapods.demo.LXFProtocolTool-Example
- Photo Booth com.apple.Photo-Booth
- Safari 浏览器 com.apple.mobilesafari
- Substitute com.ex.substitute.settings
- SwiftyFitsize_Swift org.cocoapods.demo.SwiftyFitsize-Swift
- iTunes Store com.apple.MobileStore
- 信息 com.apple.MobileSMS
- 查找 iPhone com.apple.mobileme.fmip1
- 设置 com.apple.Preferences
....
连接到指定的 USB
设备检查正在运转的运用
➜ ~ frida-ps -D d007dc58edd70caad950ff01b41ebf73cfa49fbe -a
PID Name Identifier
----- ----------- --------------------
25226 Cydia com.saurik.Cydia
26745 Twitter com.atebits.Tweetie2
21611 邮件 com.apple.mobilemail
➜ ~
3、杀死进程
frida-kill
用来完毕设备上的指定进程
➜ ~ frida-kill --help
usage: frida-kill [options] process
options:
-h, --help show this help message and exit
-D ID, --device ID connect to device with the given ID
-U, --usb connect to USB device
-R, --remote connect to remote frida-server
-H HOST, --host HOST connect to remote frida-server on HOST
--certificate CERTIFICATE
speak TLS with HOST, expecting CERTIFICATE
--origin ORIGIN connect to remote server with “Origin” header set to
ORIGIN
--token TOKEN authenticate with HOST using TOKEN
--keepalive-interval INTERVAL
set keepalive interval in seconds, or 0 to disable
(defaults to -1 to auto-select based on transport)
--p2p establish a peer-to-peer connection with target
--stun-server ADDRESS
set STUN server ADDRESS to use with --p2p
--relay address,username,password,turn-{udp,tcp,tls}
add relay to use with --p2p
-O FILE, --options-file FILE
text file containing additional command line options
--version show program's version number and exit
举个例子,杀掉 PID
为 26745
的 Twitter
frida-kill -U 26745
frida-kill -U Twitter
frida-kill -D d007dc58edd70caad950ff01b41ebf73cfa49fbe 26745
frida-kill -D d007dc58edd70caad950ff01b41ebf73cfa49fbe Twitter
4、盯梢函数/办法的调用
frida-trace
用于盯梢函数或办法的调用。
➜ ~ frida-trace --help
usage: frida-trace [options] target
positional arguments:
args extra arguments and/or target
options:
-h, --help show this help message and exit
-D ID, --device ID connect to device with the given ID
-U, --usb connect to USB device
-R, --remote connect to remote frida-server
-H HOST, --host HOST connect to remote frida-server on HOST
--certificate CERTIFICATE
speak TLS with HOST, expecting CERTIFICATE
--origin ORIGIN connect to remote server with “Origin” header set to ORIGIN
--token TOKEN authenticate with HOST using TOKEN
--keepalive-interval INTERVAL
set keepalive interval in seconds, or 0 to disable (defaults to -1 to auto-select based
on transport)
--p2p establish a peer-to-peer connection with target
--stun-server ADDRESS
set STUN server ADDRESS to use with --p2p
--relay address,username,password,turn-{udp,tcp,tls}
add relay to use with --p2p
-f TARGET, --file TARGET
spawn FILE
-F, --attach-frontmost
attach to frontmost application
-n NAME, --attach-name NAME
attach to NAME
-p PID, --attach-pid PID
attach to PID
-W PATTERN, --await PATTERN
await spawn matching PATTERN
--stdio {inherit,pipe}
stdio behavior when spawning (defaults to “inherit”)
--aux option set aux option when spawning, such as “uid=(int)42” (supported types are: string, bool,
int)
--realm {native,emulated}
realm to attach in
--runtime {qjs,v8} script runtime to use
--debug enable the Node.js compatible script debugger
--squelch-crash if enabled, will not dump crash report to console
-O FILE, --options-file FILE
text file containing additional command line options
--version show program's version number and exit
-I MODULE, --include-module MODULE
include MODULE
-X MODULE, --exclude-module MODULE
exclude MODULE
-i FUNCTION, --include FUNCTION
include [MODULE!]FUNCTION
-x FUNCTION, --exclude FUNCTION
exclude [MODULE!]FUNCTION
-a MODULE!OFFSET, --add MODULE!OFFSET
add MODULE!OFFSET
-T INCLUDE_IMPORTS, --include-imports INCLUDE_IMPORTS
include program's imports
-t MODULE, --include-module-imports MODULE
include MODULE imports
-m OBJC_METHOD, --include-objc-method OBJC_METHOD
include OBJC_METHOD
-M OBJC_METHOD, --exclude-objc-method OBJC_METHOD
exclude OBJC_METHOD
-j JAVA_METHOD, --include-java-method JAVA_METHOD
include JAVA_METHOD
-J JAVA_METHOD, --exclude-java-method JAVA_METHOD
exclude JAVA_METHOD
-s DEBUG_SYMBOL, --include-debug-symbol DEBUG_SYMBOL
include DEBUG_SYMBOL
-q, --quiet do not format output messages
-d, --decorate add module name to generated onEnter log statement
-S PATH, --init-session PATH
path to JavaScript file used to initialize the session
-P PARAMETERS_JSON, --parameters PARAMETERS_JSON
parameters as JSON, exposed as a global named 'parameters'
-o OUTPUT, --output OUTPUT
dump messages to file
4.1 盯梢函数调用
➜ ~ frida-trace -U -i compress -i "recv*" -x "recvmsg*" Twitter
Instrumenting...
compress: Auto-generated handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libz.1.dylib/compress.js"
recvfrom$NOCANCEL: Auto-generated handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_kernel.dylib/recvfrom_NOCANCEL.js"
recvfrom: Auto-generated handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_kernel.dylib/recvfrom.js"
recv: Auto-generated handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_c.dylib/recv.js"
recv$NOCANCEL: Auto-generated handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_c.dylib/recv_NOCANCEL.js"
Started tracing 5 functions. Press Ctrl+C to stop.
参数阐明
参数 | 描绘 |
---|---|
-i |
包括某个函数,支撑含糊匹配 |
-x |
扫除某个函数,支撑含糊匹配 |
注:进行含糊匹配时,需求运用双引号进行包裹!
上述指令的意思:盯梢名为 compress
和以 recv
最初的函数,且扫除以 recvmsg
最初的函数。
当盯梢的函数被触发时,会输出以下日志:
/* TID 0x1bb43 */
36078 ms recv$NOCANCEL()
36078 ms | recvfrom$NOCANCEL()
36081 ms recv$NOCANCEL()
36081 ms | recvfrom$NOCANCEL()
36082 ms recv$NOCANCEL()
36082 ms | recvfrom$NOCANCEL()
36083 ms recv$NOCANCEL()
36083 ms | recvfrom$NOCANCEL()
36083 ms recv$NOCANCEL()
36083 ms | recvfrom$NOCANCEL()
指令在履行后会在当时目录下会生成一个名为 __handlers__
的文件夹,里面寄存的是自动生成的脚本文件
.
└── __handlers__
├── libsystem_c.dylib
│ ├── recv.js
│ └── recv_NOCANCEL.js
├── libsystem_kernel.dylib
│ ├── recvfrom.js
│ └── recvfrom_NOCANCEL.js
└── libz.1.dylib
└── compress.js
上述指令是在方针 App
翻开后履行的,假如咱们需求强制发动 App
来进行盯梢,能够运用 -f 运用的BundleID
参数,如:
➜ ~ frida-trace -U -i compress -i "recv*" -x "recvmsg*" -f "com.atebits.Tweetie2"
Instrumenting...
compress: Loaded handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libz.1.dylib/compress.js"
recvfrom$NOCANCEL: Loaded handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_kernel.dylib/recvfrom_NOCANCEL.js"
recvfrom: Loaded handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_kernel.dylib/recvfrom.js"
recv: Loaded handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_c.dylib/recv.js"
recv$NOCANCEL: Loaded handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/libsystem_c.dylib/recv_NOCANCEL.js"
Started tracing 5 functions. Press Ctrl+C to stop.
注:frida-trace
履行时不会掩盖已有的脚本文件(即 __handlers__
文件夹下的脚本),所以能够进行恣意修正这些 JS
文件来增加想要的功能。
4.2 盯梢 OC
办法的调用
➜ Test frida-trace -U -m "-[T1HomeTimelineItemsViewController _load*]" -M "-[T1HomeTimelineItemsViewController _loadBottomWithSource:]" Twitter
Instrumenting...
-[T1HomeTimelineItemsViewController _loadTopWithSource:]: Loaded handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/T1HomeTimelineItemsViewController/_loadTopWithSource_.js"
-[T1HomeTimelineItemsViewController _loadGap:withSource:]: Loaded handler at "/Users/lxf/Desktop/LXF/reverse/Test/__handlers__/T1HomeTimelineItemsViewController/_loadGap_withSource_.js"
Started tracing 2 functions. Press Ctrl+C to stop.
/* TID 0x303 */
15600 ms -[T1HomeTimelineItemsViewController _loadTopWithSource:0xc8]
参数阐明
参数 | 描绘 |
---|---|
-m |
包括某个办法,支撑含糊匹配 |
-M |
扫除某个办法,支撑含糊匹配 |
4.3 盯梢调用栈
只需求在 JS
文件中增加如下代码片段即可盯梢某个办法的调用栈
console.log('\tBacktrace:\n\t' + Thread.backtrace(this.context, Backtracer.ACCURATE).map(DebugSymbol.fromAddress).join('\n\t'));
想了解具体的接口阐明能够在 Frida
官网链接:frida.re/docs/javasc… 上找到。
5、交互形式
frida
供给了两种进入交互形式的方式
5.1 经过运用名或 PID
附加
运用于 App
已翻开的情况下附加的情景
frida -U 运用名
frida -U -p PID
当运用 PID
进行附加时,-p
可加可不加
举例:
frida -U Twitter
frida -U 26984
frida -U -p 26984
5.2 发动运用进入交互形式
运用于 App
未翻开的情景
➜ Test frida -U -f com.atebits.Tweetie2
____
/ _ | Frida 15.1.17 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to iPad (id=d007dc58edd70caad950ff01b41ebf73cfa49fbe)
Spawned `com.atebits.Tweetie2`. Use %resume to let the main thread start executing!
[iPad::com.atebits.Tweetie2 ]-> %resume
注:需求自己额定再输入 %resume
,不然方针运用将一直处于暂停的状况。
假如发动运用后被强制退出或不想再额定输入 %resume
,能够加上 --no-pause
frida -U -f com.atebits.Tweetie2 --no-pause
四、实战
针对上图中的【翻译推文】,咱们来把这个标题和点击事情给修正掉
首要咱们要做的便是视图组件定位,在这个页面下,运用 FLEX
东西便可轻松定位到
点击右侧的感叹号,能够看到该视图的特点和办法
然后经过如下代码,确认其是否为咱们想要 hook
的办法
if (ObjC.available) {
var didTap = ObjC.classes.T1TranslateButton['- _didTap:forEvent:']
var setTitle = ObjC.classes.T1TranslateButton['- setTitleText:']
Interceptor.attach(setTitleOldImp, {
onEnter: function(args) {
console.log("args 0 -- ", ObjC.Object(args[0]))
console.log("args 2 -- ", ObjC.Object(args[2]))
}
})
didTap.implementation = ObjC.implement(setTitle, function(handle, selector, arg1, arg2) {
var self = ObjC.Object(handle)
console.log("self -- ", self)
})
}
翻开 Twitter
后,履行如下指令 frida -U -l Twitter.js Twitter
➜ frida -U -l Twitter.js Twitter
____
/ _ | Frida 15.1.17 - A world-class dynamic instrumentation toolkit
| (_| |
> _ | Commands:
/_/ |_| help -> Displays the help system
. . . . object? -> Display information about 'object'
. . . . exit/quit -> Exit
. . . .
. . . . More info at https://frida.re/docs/home/
. . . .
. . . . Connected to iPad (id=d007dc58edd70caad950ff01b41ebf73cfa49fbe)
[iPad::Twitter ]->
Twitter
进入到指定页面后输出:
args 0 -- <T1TranslateButton: 0x122e46f80; baseClass = UIButton; frame = (0 0; 66 22); opaque = NO; layer = <CALayer: 0x28372a760>>
args 2 -- 翻译推文
点一下【翻译推文】按钮输出:
[iPad::Twitter ]-> self -- <T1TranslateButton: 0x122e46f80; baseClass = UIButton; frame = (0 0; 66 22); opaque = NO; layer = <CALayer: 0x28372a760>>
看来是没错了,那接下来,咱们把标题和点击事情进行修正,完好代码如下:
if (ObjC.available) {
const { NSString } = ObjC.classes;
var UIAlertController = ObjC.classes.UIAlertController;
var UIAlertAction = ObjC.classes.UIAlertAction;
var UIApplication = ObjC.classes.UIApplication;
// 弹窗
function showAlert() {
var alertHandler = new ObjC.Block({ retType: 'void', argTypes: ['object'], implementation: function () {} });
ObjC.schedule(ObjC.mainQueue, function () {
var alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_('LinXunFeng', '欢迎重视公众号:FSA全栈举动\n博客:https://fullstackaction.com', 1);
var defaultAction = UIAlertAction.actionWithTitle_style_handler_('OK', 0, alertHandler);
alert.addAction_(defaultAction);
UIApplication.sharedApplication().keyWindow().rootViewController().presentViewController_animated_completion_(alert, true, NULL);
})
}
// 播映体系声响
function playSystemSound() {
var playSound = new NativeFunction(Module.findExportByName('AudioToolbox', 'AudioServicesPlaySystemSound'), 'void', ['int'])
playSound(1111)
}
var didTap = ObjC.classes.T1TranslateButton['- _didTap:forEvent:']
var setTitle = ObjC.classes.T1TranslateButton['- setTitleText:']
// 保存旧完成
var didTapOldImp = didTap.implementation
// hook
Interceptor.attach(setTitleOldImp, {
onEnter: function(args) {
args[2] = ptr(NSString.stringWithString_("Hello LinXunFeng,点击我来弹个窗和听个曲吧"))
}
})
// 掩盖完成
didTap.implementation = ObjC.implement(setTitle, function(handle, selector, arg1, arg2) {
// 调用旧完成
// didTapOldImp(handle, selector, arg1, arg2)
playSystemSound()
showAlert()
})
}
五、进阶
1、Python
交互
Frida
供给了 Python
和 JS
脚本的交互
1.1、获取设备
import frida
if __name__ == '__main__':
deviceManager = frida.get_device_manager()
# 枚举一切连接的设备
print(deviceManager.enumerate_devices())
# 根据 UDID 获取设备
print(deviceManager.get_device("d007dc58edd70caad950ff01b41ebf73cfa49fbe"))
# 获取当时 USB 连接的设备
print(frida.get_usb_device())
运转成果:
[Device(id="local", name="Local System", type='local'), Device(id="socket", name="Local Socket", type='remote'), Device(id="d007dc58edd70caad950ff01b41ebf73cfa49fbe", name="iPad", type='usb')]
Device(id="d007dc58edd70caad950ff01b41ebf73cfa49fbe", name="iPad", type='usb')
Device(id="d007dc58edd70caad950ff01b41ebf73cfa49fbe", name="iPad", type='usb')
1.2、附加进程
运用 attach()
附加进程,得到 Session
实例
if __name__ == '__main__':
device = frida.get_usb_device()
session = device.attach("Twitter") # 进程名
# session = device.attach(27489) # PID
print(session)
输出内容:
Session(pid=27489)
1.3、发动进程
运用 spawn
能够发动进程,不过会进入挂起状况,需求合作 resume()
办法才能唤醒
if __name__ == '__main__':
device = frida.get_usb_device()
pid = device.spawn("com.atebits.Tweetie2")
# session = device.attach(pid)
device.resume(pid)
spawn
可带着参数运转
如下方代码所示,运转 Safari
并翻开 FSA全栈举动
博客: https://fullstackaction.com
pid = device.spawn("com.apple.mobilesafari", url="https://fullstackaction.com/")
device.resume(pid)
1.4、脱离进程
得到 Session
并完结一切操作后,需求运用 detach()
脱离进程
if __name__ == '__main__':
device = frida.get_usb_device()
pid = device.spawn("com.atebits.Tweetie2")
session = device.attach(pid)
device.resume(pid)
session.detach() # 脱离进程
1.5、注入 JS
脚本
得到 Session
实例后,就能够调用其 create_script
办法创立一个脚本对象,再调用该脚本对象的 load
办法进行脚本注入
if __name__ == '__main__':
device = frida.get_usb_device()
pid = device.spawn("com.atebits.Tweetie2")
session = device.attach(pid)
device.resume(pid)
script = session.create_script("""
if (ObjC.available) {
var NSHomeDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSHomeDirectory")), 'pointer', []);
var path = new ObjC.Object(NSHomeDirectory());
console.log(path);
}
""")
script.load()
session.detach()
JS
脚本能够保存到本地文件中再进行读取:
with codecs.open('./xxx.js', 'r', 'utf-8') as f:
source = f.read()
script = session.create_script(source)
1.6、Python
与 JS
交互
向 JS
端传递参数,JS
端处理完结后将成果回来给 Python
端,这种场景还是很常见的,那应该怎么做呢?
示例代码如下:
import frida
import threading
g_event = threading.Event() # 同步
def payload_message(payload):
# print("payload_message -- ", payload)
if "msg" in payload:
print(payload["msg"])
if 'status' in payload:
if payload['status'] == 'success':
g_event.set()
def on_message(message, data):
# print("on_message message -- ", message)
if message['type'] == 'send':
payload_message(message['payload'])
elif message['type'] == 'error':
print(message['stack'])
SCRIPT_JS = ("""
function handleMessage(message) {
var cmd = message['cmd']
if (cmd == 'GetDirectory') {
var name = message['name']
var path;
switch (name) {
case 'home':
var NSHomeDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSHomeDirectory")), 'pointer', []);
path = new ObjC.Object(NSHomeDirectory());
break;
case 'tmp':
var NSTemporaryDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSTemporaryDirectory")), 'pointer', []);
path = new ObjC.Object(NSTemporaryDirectory());
break;
default:
path = "写的啥呀"
}
if (path) send({msg: path.toString()});
}
send({status: 'success'});
}
recv(handleMessage);
""")
# 根据姓名获取对应的沙盒途径
def getDirectory(target_process, name):
device = frida.get_usb_device()
session = device.attach(target_process)
script = session.create_script(SCRIPT_JS)
script.on('message', on_message)
script.load()
script.post({'cmd': 'GetDirectory', 'name': name})
g_event.wait()
session.detach()
if __name__ == '__main__':
# getDirectory('Twitter', 'home')
getDirectory('Twitter', 'tmp')
-
g_event
是为了保证同步 -
JS
端能够设置recv()
的回调接纳Python
端的音讯 -
Python
端经过script.on()
设置回调,再运用script.post()
将参数传递给JS
端,然后调用g_event.wait()
进入等待状况 -
JS
端内部处理完结后,运用send()
将{status: 'success'}
传递给Python
端 - 在
on_message
回调中取到JS
端回来的数据,当识别到status
为success
后,调用g_event.set()
使主线程继续履行
2、阻拦某个类的一切办法
假如想对某个类的一切办法进行批量阻拦,能够运用 ApiResolver
接口,它能够根据正则表达式获取符合条件的一切办法
var resolver = new ApiResolver('objc')
resolver.enumerateMatches('*[T1TranslateButton *]', {
onMatch: function (match) {
console.log(match['name'] + ":" + match['address'])
},
onComplete: function () {}
})
输出成果:
+[T1TranslateButton tfn_defaultShouldFlipForRightToLeftTransform]:0x101be4014
+[T1TranslateButton button]:0x101be2774
-[T1TranslateButton tapActionBlock]:0x101be4280
-[T1TranslateButton setTapActionBlock:]:0x101be4290
-[T1TranslateButton translationSource]:0x101be4250
-[T1TranslateButton setLogoTapActionBlock:]:0x101be42ac
-[T1TranslateButton setShowingTranslation:]:0x101be2c10
-[T1TranslateButton setOriginalLanguage:]:0x101be2b58
-[T1TranslateButton setTranslationSource:]:0x101be2cec
-[T1TranslateButton _didTap:forEvent:]:0x101be296c
-[T1TranslateButton _t1_didHover:]:0x101be4140
-[T1TranslateButton setSelectionPadding:]:0x101be42d8
-[T1TranslateButton touchRect]:0x101be42f8
-[T1TranslateButton _t1_isTouchingLogo:]:0x101be2a8c
-[T1TranslateButton touchLogoRect]:0x101be4328
-[T1TranslateButton _t1_buttonTitle]:0x101be2f28
-[T1TranslateButton autoTranslationExpanded]:0x101be4270
-[T1TranslateButton _t1_imageHeightOffsetForLogo:]:0x101be3164
-[T1TranslateButton _t1_titleRectWithTitleString:origin:]:0x101be32b8
-[T1TranslateButton _t1_imageRectWithOrigin:]:0x101be3394
-[T1TranslateButton _t1_drawRectFor:]:0x101be3f94
-[T1TranslateButton setTouchRect:]:0x101be4310
-[T1TranslateButton _t1_drawHighlightWithContext:andRect:]:0x101be401c
-[T1TranslateButton setTouchLogoRect:]:0x101be4340
-[T1TranslateButton setAutoTranslationExpanded:]:0x101be2cc4
-[T1TranslateButton originalLanguage]:0x101be4240
-[T1TranslateButton showingTranslation]:0x101be4260
-[T1TranslateButton logoTapActionBlock]:0x101be429c
-[T1TranslateButton selectionPadding]:0x101be42c8
-[T1TranslateButton _dynamicColorsDidReload:]:0x101be3ed0
-[T1TranslateButton titleText]:0x101be42b8
-[T1TranslateButton _titleColor]:0x101be2ea8
-[T1TranslateButton logoImage]:0x101be42e8
-[T1TranslateButton _logoImage]:0x101be2d14
-[T1TranslateButton dealloc]:0x101be28e4
-[T1TranslateButton .cxx_destruct]:0x101be4358
-[T1TranslateButton initWithFrame:]:0x101be27dc
-[T1TranslateButton sizeThatFits:]:0x101be3408
-[T1TranslateButton setHighlighted:]:0x101be2b08
-[T1TranslateButton drawRect:]:0x101be3798
-[T1TranslateButton setTitleText:]:0x101be2c38
3、替换原办法
Interceptor.attach()
能够在阻拦方针后,打印参数,修正回来值,但无法阻止原办法的履行
咱们能够给原办法的 implementation
进行赋值,然后掩盖其完成
var didTap = ObjC.classes.T1TranslateButton['- _didTap:forEvent:']
var didTapOldImp = didTap.implementation
// 掩盖完成
didTap.implementation = ObjC.implement(setTitle, function(handle, selector, arg1, arg2) {
var self = ObjC.Object(handle)
console.log("self -- ", self)
// 调用旧完成
// didTapOldImp(handle, selector, arg1, arg2)
})
这儿需求留意的是,像 _didTap:forEvent:
这儿需求传递两个参数,则 ObjC.implement
的回调中也需求写明两个参数(arg1
、arg2
),即需求多少参数就写多少,没有则不必写
4、RPC
调用
RPC
:即 Remote Procedure Call
,长途过程调用,开发人员能够将封装好的恣意函数指定为 RPC
函数,以供给给 Python
运用。
利用 rpc.exports = {}
导出 RPC
函数,多个函数以逗号分隔,留意:办法名需求全小写!
function getHomeDirectory() {
var NSHomeDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSHomeDirectory")), 'pointer', [])
var path = new ObjC.Object(NSHomeDirectory());
return path.toString()
}
function openUrl(url) {
var UIApplication = ObjC.classes.UIApplication.sharedApplication()
var toOpen = ObjC.classes.NSURL.URLWithString_(url)
return UIApplication.openURL_(toOpen)
}
function playSystemSound() {
var playSound = new NativeFunction(Module.findExportByName('AudioToolbox', 'AudioServicesPlaySystemSound'), 'void', ['int'])
playSound(1111)
}
// 导出 RPC 函数
rpc.exports = {
openurl: function (url) {
openUrl(url)
},
sound: function () {
playSystemSound()
},
alert: function () {
showAlert()
},
homedirectory: function () { // homedirectory 必须小写
return getHomeDirectory()
}
}
Python
端运用 rpc.js
import codecs
import frida
if __name__ == '__main__':
device = frida.get_usb_device()
session = device.attach('Twitter')
# 读取 JS 脚本
with codecs.open('./rpc.js', 'r', 'utf-8') as f:
source = f.read()
script = session.create_script(source)
script.load()
rpc = script.exports
rpc.openurl("https://fullstackaction.com")
rpc.sound()
print(rpc.homeDirectory())
# print(rpc)
session.detach()
六、最后
以上代码现已上传至:github.com/LinXunFeng/…
Frida
供给的 API
接口非常丰厚,这儿只提到了常用的内容,更多内容还是需求咱们一起去阅览官方文档:frida.re/docs/javasc…
除此之外,codeshare.frida.re 上供给很多共享脚本,我们能够用来学习和引入运用