企业在业务安全与数据合规过程中有哪些实践与挑战?戳此了解 了解详情
写点什么

只需双击,老铁!debugserver 快速启动插件

  • 2020 年 4 月 10 日
  • 本文字数:2346 字

    阅读完需:约 8 分钟

只需双击,老铁!debugserver快速启动插件

debugserver+lldb 很好用,但启动起来太麻烦?我们开发了一款 iOS SpringBoard tweak 小插件,简化 debugserver 启动过程。老铁,请双击!


0x00 懒是第一生产力

我们经常要通过 debugserver 对 App 进行调试,有书籍和论坛对相关的技术和实践进行了说明,但实际应用起来还是有些麻烦。先要重签拷贝,再要启动终端 ssh 到 iPhone 启动 debugserver,各种 ls 加 grep 找到想调试的应用,敲命令启动 debugserver,然后 Mac 本地终端启动 lldb。这样折腾下来,至少要开两个终端,有的时候甚至更多。GitHub 上有个 issh 工具对上述操作有封装和优化,但是还是需要敲命令找 App,再运行 debugserver。


所以做个 tweak 提升一下生产力。只需 双击应用图标,即可一键启动 debugserver


代码见:Github https://github.com/TalkingData/tap2debug


运行界面



我们所用的开发环境是 iOS 13.3,但是并没有用到特殊版本的 API,低版本手机应该也 OK。


下面简单分享开发过程:


0x01 通过图标找到应用执行路径

从界面找逻辑,逆向发现 SpringBoard 的图标是 SBIconView。并且有一个叫属性 applicationBundleIdentifierForShortcuts 返回的是图标对应的 App 的 Bundle ID。通过 Bundle ID 构造 LSApplicationProxy 对象,并且获得 canonicalExecutablePath 属性,也就是应用的可执行文件路径。


Class LSApplicationProxy_class = objc_getClass("LSApplicationProxy");NSObject* proxyObj = [LSApplicationProxy_class performSelector:@selector(applicationProxyForIdentifier:) withObject:bundle];NSString * canonicalExecutablePath = [proxyObj performSelector:@selector(canonicalExecutablePath)];
复制代码


0x02 寻找注入点添加扩展

接续看 SBIconView,图标上有两个手势对象:


  • 单击,用来启动 App。

  • 长按,进入编辑状态,执行删除和排列图标等操作。


所以,我们来给图标交互加个双击扩展。


%hook SBIconView


- (void)didMoveToWindow{    %orig;    UITapGestureRecognizer *doubleTap = [[UITapGestureRecognizer alloc]initWithTarget:self action:@selector(handleDoubleClick:)];    [doubleTap setNumberOfTapsRequired:2];    [self addGestureRecognizer:doubleTap];    NSArray * ges = self.gestureRecognizers;    for(UITapGestureRecognizer * each in ges){        if([each isKindOfClass:[UITapGestureRecognizer class]]){            [each requireGestureRecognizerToFail: doubleTap];        }    }}
复制代码


这里额外说一句,[each requireGestureRecognizerToFail: doubleTap] 添加了双击手势指挥,由于 iOS 内部维护了手势的状态机,我们进行单击操作的时候,其实产生了两种 Possible State。第一种是识别为单击,然后结束。第二种是识别为双击的第一下并等待第二下的发生,然后根据两次点击之时间间隔阈值来判断是不是合法的双击。


所以我们手动增加了约束,相当于指定了识别的优先级,只有双击失败了,才继续执行单击回调。这种操作会带来一点几乎无感的瑕疵:单击后等待双击识别失败的延迟,延迟的值就是双击识别执行的阈值(大约零点几秒)。


0x03 debugserver 启动和关闭

debugserver 是一个二进制文件,狗神的教程里有如何重签,issh 把这些过程给简化了。先看一下 debugserver 的权限:


-rwxr-xr-x 1 root admin 9876848 Jan 19 11:28 /iOSRE/tools/debugserver*


再来看一下 SpringBoard 的权限:


-rwxr-xr-x 1 root wheel 71264 Dec 5 13:15 SpringBoard*


属主用户都是 root,没毛病。找个函数调用一下:


  1. system 函数

  2. posix_spawn 函数

  3. NSTask ,面向对象方便管理,异步执行,不会 block UI,就用它了。


代码如下:


task = [[NSTask alloc]init];[task setLaunchPath:bin_serverpath];[task setArguments:args];[task launch];
复制代码


每次 server 在 launch 之前,要把之前的 task 结束掉。


- (void)interrupt; // Not always possible. Sends SIGINT.
复制代码


- (void)terminate; // Not always possible. Sends SIGTERM.
复制代码


NSTask 头文件里竟然告诉我 Not always possible。事实上,在调用的时候,还真的不怎么 possible,实际测试第一次 server 正常启动,后续由于没成功关闭,所以第二次就没法启动了。


所以还是换种方式关闭吧。简单粗暴的 kill 函数:


NSTask * task = [TaskManager sharedManager].runningTask;if(task){    kill(task.processIdentifier,SIGKILL);    task = nil;}
复制代码


0x04 添加 UI 交互

直接用 Alert,又有按钮又有输入框,不过 UIAlertView 已经被废弃掉了,需要用 UIAlertController。由于弹出 Controller 需要父 Controller,通过 View 找到当前的 Controller,做正向的应该都写过这段代码吧:



@implementation UIView(find)-(UIViewController*)findViewController{ UIResponder* target= self; while (target) { target = target.nextResponder; if ([target isKindOfClass:[UIViewController class]]) { break; } } return (UIViewController*)target;}@end
复制代码


0x05 优化一下用户体验

输入框里的 IP 和 debugserver 的 path,每个人都不一样,所以在第一次输入完成之后,把这些值用 NSUserDefault 持久化存储起来,下次直接读取填充。


0x06 后记

之前在相关技术论坛读到讨论用 Root 身份运行 App 的帖子,学习完帖子里的技巧,增强对操作系统的理解以及实践之后,发现如果真的想 RootApp 运行,其实 SpringBoard 本身就是一个 RootApp,我们把 SpringBoard 当做 RootViewController,很容易把一些系统工具做出界面,从而提升生产力。比如砸壳、重签、拷贝 App 等。


参考资料:


书籍:https://book.douban.com/subject/25826902/


论坛:http://bbs.iosre.com/t/debugserver-lldb-gdb/65


issh 工具:https://github.com/4ch12dy/issh


2020 年 4 月 10 日 18:33268

评论

发布
暂无评论
发现更多内容

对PageRank的理解

2流程序员

克隆虚拟机

yuanhang

如何在一台电脑上管理多Github账号

Matrix Chan

git GitHub 版本管理工具 开发日志

模板方法模式——看看 JDK 和 Spring 是如何优雅复用代码的

简爱W

Java 程序员 java架构

Hessian Bug修复

心平气和

php 序列化 hessian

服务化反面案例

心平气和

服务化 权限

Fettley全球共识共享智能合约资产计划 掀起行业追捧热潮

InfoQ_967a83c6d0d7

限量版Netty纯手打笔记,年薪80W架构师耗时1个月整理出

Java架构师迁哥

干货来袭!20例JavaWeb项目开发精粹;(内含源码)

Java架构师迁哥

架构师训练营第 0 期第 13 周作业

无名氏

你应该了解的MySQL锁分类

X先生

MySQL 数据库 sql 数据库优化

如何在企业微信上搭建一款活动报名 App

Ceelog

企业微信 Go 语言

防止APP窃取用户隐私问题,OPPO安全在行动

OPPO安全

App 安全 隐私保护 数据隐私 sdk

​程序员离职后收到原公司 2400 元,被告违反竞业协议赔 18 万

程序员生活志

程序员 职场

超详细:完整的推荐系统架构设计

博文视点Broadview

架构 算法 推荐系统

抗疫代码入国家博物馆, 程序员的巅峰时刻!

程序员生活志

程序员

开源流数据公司 StreamNative 正式加入 CNCF,积极推动云原生策略发展

Apache Pulsar

云原生 pulsar Apache Pulsar 消息系统 消息中间件

解Bug之路-Nginx 502 Bad Gateway

无毁的湖光

nginx Linux TCP

全票通过!易观开源项目DolphinScheduler进入Apache孵化器

易观大数据

为什么一个还没毕业的大学生能够把 IO 讲的这么好?

cxuan

Java 后端 io

大数据

yuanhang

Centos 7 虚拟机

「查缺补漏」巩固你的RocketMQ知识体系

Kerwin

Java RocketMQ

大专生拿到阿里offer,却担心背调不过,网友:985在你面前黯淡无光

程序员生活志

面试 职场 阿里

Redis单线程为什么能做到高性能和io多路复用它是个什么鬼

Java架构师迁哥

架构师训练营第十三周总结

Hanson

Spring 5 中文解析核心篇-集成测试之TestContext(上)

青年IT男

单元测试 Spring5 JUnit

PageRank 算法

周冬辉

超级TV盒子,超清秒播,解锁全网VIP资源!

程序员生活志

资源

oeasy 教您玩转linux 之010208 满屏乱码 bb

o

字节小组长无意中得知整个部门的薪资,自己28K,手下却有35K,怎么办?

程序员生活志

字节跳动 职场 薪资

架构师训练营第十三周作业

Hanson

WAVE SUMMIT 2022 深度学习开发者峰会

WAVE SUMMIT 2022 深度学习开发者峰会

只需双击,老铁!debugserver快速启动插件_文化 & 方法_TalkingData小张同学_InfoQ精选文章