小谷秃头合集ios
小谷的公司每月能够迟到3次,小谷5月的时候考勤居然迟到了4次。很难受。为了杜绝这种状况。毅然决然的研究一波偷偷打卡😃
git
小谷用的设备是iPhone XS Max
,iOS 14.3(越狱
),目前最新的钉钉:6.0.16
。markdown
用Windows
作逆向
的兄弟们,常常使用IDA和Reveal
还有终端debugserver附加进程
,今天小谷要用iOS开发的兄弟们都比较喜欢的Xcode和hopper
。😆(其实都差很少。)函数
先看效果图post
蹂躏第一步,首先要装酷学习
frida-ios-dump
,小谷写过一篇砸壳和Frida的博客.这样
砸过壳
的钉钉.ipa
就出来了,先放这,咱们先无论他ui
iOS
兄弟们超级喜欢的,Xcode
调试(曾经有兄弟问过我怎么附加的。我这里画下流程图)这样就清晰不少了spa
定位打卡
的代码了啊 (找到打卡界面,viewdebug
调试)我滴天啊。是个
WKWebview
,定位不到!!插件
这样就结束了吗?怎么可能呢,那之后小谷要是迟到,没有办法弥补了~·
debug
整理下思路和线索
WKWebview
界面H5
会有变化,说明是有地方告诉H5
定位成功告诉H5
的,那么他们就必定有交互代码
交互
,咱们知道hook住
这个交互
就能够了(看里面的回传
,经过改变值可能作到)locationManager: didUpdateLocations:
H5交互的时候开始定位
,而后回调定位信息
那么咱们接下来的任务就是
定位交互的代码
和打印回传的信息
了!! 搞起,搞起~
咱们刚开始导出了钉钉.ipa
,解压
,拿MachO
文件
而后经过class-dump
取出header
class-dump -H DingTalk -o dingHeader
Hopper
(搜索locationManager: didUpdateLocations:
)一共就
5
个,咱们就把这几个hook
一下,看下log
了,我猜想应该就能够定位到了(若是他调用的原生的
)
Monkey
写Tweak插件
了(固然也能够用THEOS
,主要咱们此次可能要好几回调试才能定位,我就用Monkey
方便点~)钉钉的APPID
,并配置5
个,hook
一下#import <UIKit/UIKit.h>
%hook AMapLocationCLMDelegate
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
%log;
%orig;
}
%end
%hook AMapLocationManager
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
%log;
%orig;
}
%end
%hook LALocationManager
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
%log;
%orig;
}
%end
%hook DTCLocationManager
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
%log;
%orig;
}
%end
%hook MAMapView
- (void)locationManager:(id)arg1 didUpdateLocations:(id)arg2{
%log;
%orig;
}
%end
复制代码
而后咱们安装插件,看下
log
AMapLocationCLMDelegate
和AMapLocationManager
都是成对出现的,其余的都没有走class-dump
出来的header
文件。发现AMapLocationCLMDelegate
里面都是一些代理回调这个时候,小谷又能够推测一波了: 在
AMapLocationManager
获取的位置,而后参数在把参数传给H5交互
AMapLocationManager
里面的方法都打一遍log
,使用logify.pl
log
看到了不错的信息。咱们要继续开始
Xcode调试附加
了
Hopper
找到偏移位置Xcode
附加下,能够找到ASLR
0x104e70000 + 0x36b4a8 = 0x1051DB4A8
函数调用栈
了!!有没有那么一丢丢爽歪歪~ (若是没有符号也没有关系,咱们就经过
Hopper
地址定位!)
hook
下这个函数%hook LAPluginInstanceCollector
- (void)handleJavaScriptRequest:(id)arg1 callback:(id)arg2{
%log;
%orig;
}
%end
复制代码
callback
是个block
,说明arg1
是参数,arg2
是回调回去的参数block
的参数类型。就要看看他的签名
了~经过
内存平移
来拿他的签名
~
Hopper
拿地址。而后Xcode附加下断点
0x100f50000 + 0x488f110 = 0x1057DF110
b 0x1057DF110
断住它,而后分析他
hook
了,看看他里面的参数
是啥%hook LAPluginInstanceCollector
- (void)handleJavaScriptRequest:(id)arg1 callback:(void (^) (id))arg2{
//咱们能够自定义个block看下。
id xg_callback = ^(id argCb){
//能够先把传入的参数打印下,看看有没有啥触发器
NSLog(@"xg_callback arg1:%@",arg1);
//而后把传入的参数打印下,也看下他是什么类型的
NSLog(@"xg_callback argCbClass:%@, argCb:%@",[argCb class],argCb);
arg2(argCb);
};
//xg_callback替换arg2。(只作了一个转接的过程)
%orig(arg1,xg_callback);
}
%end
复制代码
我好像找到了,可是这里面还有好多其余跟他相关连的信息。(好比地址,城市,经纬度啥的)
咱们知道他类型是个字典。
action=start
的时候触发
%hook LAPluginInstanceCollector
- (void)handleJavaScriptRequest:(id)arg1 callback:(void (^) (id))arg2{
id xg_callback = ^(id argCb){
//小谷作的时候其实打了好多log。。
NSDictionary *arg1Dic = (NSDictionary *)arg1;
if([arg1Dic[@"action"] isEqualToString:@"start"]){
NSLog(@"xg_callback text:start");
NSMutableDictionary * CbDic = [NSMutableDictionary dictionaryWithDictionary:argCb];
if (CbDic[@"result"][@"latitude"] && CbDic[@"result"][@"longitude"]){
NSLog(@"xg_callback text:result");
NSMutableDictionary * resultDic = [NSMutableDictionary dictionaryWithDictionary:CbDic[@"result"]];
resultDic[@"latitude"] = @"40.0361208767361";
resultDic[@"longitude"] = @"116.4161067708333";
[CbDic setValue:resultDic forKey:@"result"];
}
arg2(CbDic);
}else{
arg2(argCb);
}
};
//xg_callback替换arg2。(只作了一个转接的过程)
%orig(arg1,xg_callback);
}
%end
复制代码
主要功能写的差很少了。咱们再来设置一个开关来控制一下:若是
开关开启
,就走打卡插件逻辑
,若是开关关闭
,走原来的逻辑
。
Xcode
好强大~
dataSource
吧tableview
啊,把他的代码一hook
加行cell
,不是很easy
吗)@interface DTTableViewHandler : NSObject
- (long long)numberOfSectionsInTableView:(UITableView *)tableView;
@end
%hook DTTableViewHandler
%new
-(void)xg_switchChang:(UISwitch *)switchView{
[XGDefaults setBool:switchView.isOn forKey:XGSWITCHKEY];
[XGDefaults synchronize];
}
- (long long)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
// 若是是最后一组,增长一行
if([tableView.nextResponder .nextResponder isKindOfClass:%c(DTSettingListViewController)] && (section == [self numberOfSectionsInTableView:tableView]-1)){
return 1;
}
return %orig;
}
- (id)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
//最后一组,最后一行,设置一个cell
if([tableView.nextResponder .nextResponder isKindOfClass:%c(DTSettingListViewController)] && ([indexPath section] == [self numberOfSectionsInTableView:tableView]-1)){
UITableViewCell * cell = nil;
if([indexPath row] == 0){
static NSString * swCell = @"SWCELL";
cell = [tableView dequeueReusableCellWithIdentifier:swCell];
if(!cell){
cell = [[UITableViewCell alloc] initWithStyle:(UITableViewCellStyleDefault) reuseIdentifier:nil];
}
cell.textLabel.text = @"偷偷打卡开启!!";
UISwitch * switchView = [[UISwitch alloc] init];
switchView.on = [XGDefaults boolForKey:XGSWITCHKEY];
[switchView addTarget:self action:@selector(xg_switchChang:) forControlEvents:(UIControlEventValueChanged)];
cell.accessoryView = switchView;
cell.backgroundColor = [UIColor whiteColor];
return cell;
}
return nil;
}else{
return %orig;
}
}
- (double)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
//设置最后一组最后一行的行高
if([tableView.nextResponder .nextResponder isKindOfClass:%c(DTSettingListViewController)] && ([indexPath section] == [self numberOfSectionsInTableView:tableView]-1)){
return 45;
}
return %orig;
}
- (long long)numberOfSectionsInTableView:(UITableView *)tableView {
//增长一组
if([tableView.nextResponder .nextResponder isKindOfClass:%c(DTSettingListViewController)]){
return %orig + 1;
}
return %orig;
}
%end
复制代码
if([arg1Dic[@"action"] isEqualToString:@"start"] && [XGDefaults boolForKey:XGSWITCHKEY]){
这篇博客禁止作商业用途。纯属为了学习,若是有法律责任与本人无关!!
这篇博客是小谷自创的,若是转载请标明出处!
小谷原本想直接HOOK
-iOS回调
的那个函数
改值,不过感受会有误伤
好了兄弟们。逆向插件
开发了一波。小谷准备下一篇写个基础安防
的
不过不能写这么长了。博客写太长,兄弟们估计不想看。我尽可能精简
最后祝兄弟们,愈来愈帅!!!! 还有你们永不迟到~