├── .gitignore ├── README.md ├── Twitter.js └── study ├── 01_get_device.py ├── 02_attach.py ├── 03_spawn.py ├── 04_detach.py ├── 05_js_script.py ├── 06_py_js_interaction.py ├── 07_api_resolver.js ├── 08_rpc.py └── rpc.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 相关文章 4 | 5 | |文章|链接|| 6 | |-|-|-| 7 | |《iOS逆向 - 运行时分析(三)Frida》|[掘金](https://juejin.cn/post/7079726534096846862/)|[FSA博客](https://fullstackaction.com/pages/fc665a/)| 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Twitter.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | // if (ObjC.available) { 4 | // // for (var className in ObjC.classes) { 5 | // // if (className.toLowerCase().indexOf('translate' != -1)) { 6 | // // console.log("[#] className -- " + className); 7 | // // } 8 | // // } 9 | 10 | // var methods = ObjC.classes.T1HomeTimelineItemsViewController.$methods 11 | // var methods = ObjC.classes.T1TranslateButton.$methods 12 | // for (var i = 0; i < methods.length; i++) { 13 | // var method = methods[i] 14 | // // console.log(method.toLowerCase()) // translate 15 | // if (method.toLowerCase().indexOf('didTap') !== -1) { 16 | // console.log("[-] " + method); 17 | // } 18 | // } 19 | // } 20 | 21 | // if (ObjC.available) { 22 | // var className = 'T1TweetDetailsFocalStatusView' 23 | // var methodsNames = ['- visibleAutoTranslateBodyView', '- visibleTranslateTweetView'] 24 | 25 | // for (var index in methodsNames) { 26 | // var methodsName = methodsNames[index] 27 | // var hook = eval('ObjC.classes.' + className + '["' +methodsName+ '"]') 28 | 29 | // Interceptor.attach(hook.implementation, { 30 | // onEnter: function (args) { 31 | // console.log("---- onEnter ----") 32 | // console.log("[*] Class Name: " + className) 33 | // console.log("[*] Method Name: " + methodsName) 34 | // // console.log("\t[-] args: " + args) 35 | // console.log("-----------------") 36 | // }, 37 | // onLeave: function (retval) { 38 | // console.log("---- onLeave ----") 39 | // console.log("[*] Class Name: " + className) 40 | // console.log("[*] Method Name: " + methodsName) 41 | // console.log("\t[-] Return Value: " + retval) 42 | // console.log("-----------------") 43 | // }, 44 | // }) 45 | // } 46 | // } 47 | 48 | 49 | // (void)setTitleText:(id) 50 | // -(void)_didTap:(id) forEvent:(id) 51 | 52 | if (ObjC.available) { 53 | const { NSString } = ObjC.classes; 54 | var UIAlertController = ObjC.classes.UIAlertController; 55 | var UIAlertAction = ObjC.classes.UIAlertAction; 56 | var UIApplication = ObjC.classes.UIApplication; 57 | 58 | function showAlert() { 59 | var alertHandler = new ObjC.Block({ retType: 'void', argTypes: ['object'], implementation: function () {} }); 60 | 61 | ObjC.schedule(ObjC.mainQueue, function () { 62 | var alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_('LinXunFeng', '欢迎关注公众号:FSA全栈行动\n博客:https://fullstackaction.com', 1); 63 | var defaultAction = UIAlertAction.actionWithTitle_style_handler_('OK', 0, alertHandler); 64 | alert.addAction_(defaultAction); 65 | UIApplication.sharedApplication().keyWindow().rootViewController().presentViewController_animated_completion_(alert, true, NULL); 66 | }) 67 | } 68 | 69 | function playSystemSound() { 70 | var playSound = new NativeFunction(Module.findExportByName('AudioToolbox', 'AudioServicesPlaySystemSound'), 'void', ['int']) 71 | playSound(1111) 72 | } 73 | 74 | var didTap = ObjC.classes.T1TranslateButton['- _didTap:forEvent:'] 75 | var setTitle = ObjC.classes.T1TranslateButton['- setTitleText:'] 76 | var translateView = ObjC.classes.T1TweetDetailsFocalStatusView['- visibleTranslateTweetView'] 77 | 78 | // 保留旧实现 79 | var setTitleOldImp = setTitle.implementation 80 | var translateViewOldImp = translateView.implementation 81 | var didTapOldImp = didTap.implementation 82 | 83 | // hook 84 | Interceptor.attach(setTitleOldImp, { 85 | onEnter: function(args) { 86 | args[2] = ptr(NSString.stringWithString_("Hello LinXunFeng,点击我来弹个窗和听个曲吧")) 87 | // console.log("args 0 -- ", ObjC.Object(args[0])) 88 | // console.log("args 2 -- ", ObjC.Object(args[2])) 89 | } 90 | }) 91 | 92 | // 覆盖实现 93 | didTap.implementation = ObjC.implement(setTitle, function(handle, selector, arg1, arg2) { 94 | 95 | // var self = ObjC.Object(handle) 96 | // var sel = ObjC.Object(selector) , " sel -- " , sel 97 | // var a = ObjC.Object(args) 98 | // console.log("self -- ", self, " args -- " , args) 99 | 100 | // 调用旧实现 101 | // didTapOldImp(handle, selector, arg1, arg2) 102 | 103 | playSystemSound() 104 | showAlert() 105 | }) 106 | 107 | translateView.implementation = ObjC.implement(setTitle, function(handle, selector) { 108 | return translateViewOldImp(handle, selector) 109 | }) 110 | } 111 | -------------------------------------------------------------------------------- /study/01_get_device.py: -------------------------------------------------------------------------------- 1 | import frida 2 | 3 | # 获取设备 4 | if __name__ == '__main__': 5 | 6 | deviceManager = frida.get_device_manager() 7 | 8 | # 枚举所有连接的设备 9 | print(deviceManager.enumerate_devices()) 10 | 11 | # 根据 UDID 获取设备 12 | print(deviceManager.get_device("d007dc58edd70caad950ff01b41ebf73cfa49fbe")) 13 | 14 | # 获取当前 USB 连接的设备 15 | print(frida.get_usb_device()) 16 | -------------------------------------------------------------------------------- /study/02_attach.py: -------------------------------------------------------------------------------- 1 | import frida 2 | 3 | # 附加进程 4 | if __name__ == '__main__': 5 | device = frida.get_usb_device() 6 | session = device.attach("Twitter") # 进程名 7 | # session = device.attach(27489) # PID 8 | print(session) 9 | -------------------------------------------------------------------------------- /study/03_spawn.py: -------------------------------------------------------------------------------- 1 | import frida 2 | 3 | # 启动进程 4 | if __name__ == '__main__': 5 | device = frida.get_usb_device() 6 | pid = device.spawn("com.atebits.Tweetie2") 7 | # session = device.attach(pid) 8 | device.resume(pid) 9 | 10 | # 携带参数运行 11 | # pid = device.spawn("com.apple.mobilesafari", url="https://fullstackaction.com/") 12 | # device.resume(pid) 13 | -------------------------------------------------------------------------------- /study/04_detach.py: -------------------------------------------------------------------------------- 1 | import frida 2 | 3 | # 脱离进程 4 | if __name__ == '__main__': 5 | device = frida.get_usb_device() 6 | pid = device.spawn("com.atebits.Tweetie2") 7 | session = device.attach(pid) 8 | device.resume(pid) 9 | 10 | session.detach() 11 | -------------------------------------------------------------------------------- /study/05_js_script.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import frida 3 | 4 | # js脚本注入 5 | if __name__ == '__main__': 6 | device = frida.get_usb_device() 7 | pid = device.spawn("com.atebits.Tweetie2") 8 | session = device.attach(pid) 9 | device.resume(pid) 10 | 11 | script = session.create_script(""" 12 | if (ObjC.available) { 13 | var NSHomeDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSHomeDirectory")), 'pointer', []); 14 | var path = new ObjC.Object(NSHomeDirectory()); 15 | console.log(path); 16 | } 17 | """) 18 | 19 | # 加载本地 JS 脚本 20 | # with codecs.open('./xxx.js', 'r', 'utf-8') as f: 21 | # source = f.read() 22 | # script = session.create_script(source) 23 | 24 | script.load() 25 | session.detach() 26 | -------------------------------------------------------------------------------- /study/06_py_js_interaction.py: -------------------------------------------------------------------------------- 1 | import frida 2 | import threading 3 | 4 | # Python 与 JS 交互 5 | 6 | g_event = threading.Event() # 同步 7 | 8 | 9 | def payload_message(payload): 10 | # print("payload_message -- ", payload) 11 | if "msg" in payload: 12 | print(payload["msg"]) 13 | 14 | if 'status' in payload: 15 | if payload['status'] == 'success': 16 | g_event.set() 17 | 18 | 19 | def on_message(message, data): 20 | # print("on_message message -- ", message) 21 | if message['type'] == 'send': 22 | payload_message(message['payload']) 23 | elif message['type'] == 'error': 24 | print(message['stack']) 25 | 26 | 27 | SCRIPT_JS = (""" 28 | function handleMessage(message) { 29 | var cmd = message['cmd'] 30 | if (cmd == 'GetDirectory') { 31 | var name = message['name'] 32 | var path; 33 | switch (name) { 34 | case 'home': 35 | var NSHomeDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSHomeDirectory")), 'pointer', []); 36 | path = new ObjC.Object(NSHomeDirectory()); 37 | break; 38 | case 'tmp': 39 | var NSTemporaryDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSTemporaryDirectory")), 'pointer', []); 40 | path = new ObjC.Object(NSTemporaryDirectory()); 41 | break; 42 | default: 43 | path = "写的啥呀" 44 | } 45 | if (path) send({msg: path.toString()}); 46 | } 47 | send({status: 'success'}); 48 | } 49 | recv(handleMessage); 50 | """) 51 | 52 | 53 | # 根据名字获取对应的沙盒路径 54 | def getDirectory(target_process, name): 55 | device = frida.get_usb_device() 56 | session = device.attach(target_process) 57 | script = session.create_script(SCRIPT_JS) 58 | script.on('message', on_message) 59 | script.load() 60 | script.post({'cmd': 'GetDirectory', 'name': name}) 61 | g_event.wait() 62 | session.detach() 63 | 64 | 65 | if __name__ == '__main__': 66 | getDirectory('Twitter', 'home') 67 | # getDirectory('Twitter', 'tmp') 68 | -------------------------------------------------------------------------------- /study/07_api_resolver.js: -------------------------------------------------------------------------------- 1 | // 拦截类的所有方法 2 | var resolver = new ApiResolver('objc') 3 | resolver.enumerateMatches('*[T1TranslateButton *]', { 4 | onMatch: function (match) { 5 | console.log(match['name'] + ":" + match['address']) 6 | }, 7 | onComplete: function () {} 8 | }) -------------------------------------------------------------------------------- /study/08_rpc.py: -------------------------------------------------------------------------------- 1 | import codecs 2 | import frida 3 | 4 | if __name__ == '__main__': 5 | device = frida.get_usb_device() 6 | session = device.attach('Twitter') 7 | 8 | # 读取 JS 脚本 9 | with codecs.open('./rpc.js', 'r', 'utf-8') as f: 10 | source = f.read() 11 | 12 | script = session.create_script(source) 13 | script.load() 14 | 15 | rpc = script.exports 16 | # rpc.openurl("https://fullstackaction.com") 17 | rpc.sound() 18 | # rpc.alert() 19 | print(rpc.homeDirectory()) 20 | # print(rpc) 21 | 22 | session.detach() 23 | -------------------------------------------------------------------------------- /study/rpc.js: -------------------------------------------------------------------------------- 1 | function getHomeDirectory() { 2 | var NSHomeDirectory = new NativeFunction(ptr(Module.findExportByName("Foundation", "NSHomeDirectory")), 'pointer', []) 3 | var path = new ObjC.Object(NSHomeDirectory()); 4 | return path.toString() 5 | } 6 | 7 | function openUrl(url) { 8 | var UIApplication = ObjC.classes.UIApplication.sharedApplication() 9 | var toOpen = ObjC.classes.NSURL.URLWithString_(url) 10 | return UIApplication.openURL_(toOpen) 11 | } 12 | 13 | function showAlert() { 14 | var UIAlertController = ObjC.classes.UIAlertController; 15 | var UIAlertAction = ObjC.classes.UIAlertAction; 16 | var UIApplication = ObjC.classes.UIApplication; 17 | var alertHandler = new ObjC.Block({ retType: 'void', argTypes: ['object'], implementation: function () {} }); 18 | 19 | ObjC.schedule(ObjC.mainQueue, function () { 20 | var alert = UIAlertController.alertControllerWithTitle_message_preferredStyle_('LinXunFeng', '欢迎关注公众号:FSA全栈行动\n博客:https://fullstackaction.com', 1); 21 | var defaultAction = UIAlertAction.actionWithTitle_style_handler_('OK', 0, alertHandler); 22 | alert.addAction_(defaultAction); 23 | UIApplication.sharedApplication().keyWindow().rootViewController().presentViewController_animated_completion_(alert, true, NULL); 24 | }) 25 | } 26 | 27 | function playSystemSound() { 28 | var playSound = new NativeFunction(Module.findExportByName('AudioToolbox', 'AudioServicesPlaySystemSound'), 'void', ['int']) 29 | playSound(1111) 30 | } 31 | 32 | // 导出 RPC 函数 33 | rpc.exports = { 34 | openurl: function (url) { 35 | openUrl(url) 36 | }, 37 | sound: function () { 38 | playSystemSound() 39 | }, 40 | alert: function () { 41 | showAlert() 42 | }, 43 | homedirectory: function () { // homedirectory 必须小写 44 | return getHomeDirectory() 45 | } 46 | } --------------------------------------------------------------------------------