
此時我就在想,一次行程收費2元,再多交10元那么我為什么不叫出租車呢(而且還更舒適,吐槽美團電動車避震真的不行),于是我就使用了一款虛擬定位軟件把我的定位修改到附近的停車點,然后點擊還車,成功還車不需要多交10元。
第二日
喝完酒回家后第二天醒來,我就在想美團是否只是驗證了手機定位來判斷你是否在停車點內,于是帶著驗證的想法我就開始了逆向美團App
逆向全過程
首先我們從越獄手機上dump出文件
iproxy 2222 22
./dump.py 美團
成功dump出ipa文件后解壓文件(ipa就是個壓縮包)然后dump出頭文件
class-dump -S -s -H Payload/imeituan.app -o ./Header
這時候我們就得到了27528個頭文件,但是這個量太多了,不可能一個個看過去,得縮小范圍,于是我使用Lookin打開分析美團電單車頁面,找到該頁面的VC為MBKHomeViewController

于是我們打開頭文件里名為MBKHomeViewController.h的文件查看

在該頭文件里發(fā)現(xiàn)一個很可疑屬性
@property(retain, nonatomic) MBKMapManager *mapManager;
從這個英文命名來MBKMapManager看應該是管理地圖的控制類,這時候大廠的優(yōu)勢就體現(xiàn)出來了,命名規(guī)范也方便了逆向... 咳咳咳,也算是副作用吧,于是我們跳進MBKMapManager這個類進去看看

在這個類中我們依然發(fā)現(xiàn)了一個很可疑的函數(shù)
- (void)mt_mapView:(id)arg1 didSelectAnnotationView:(id)arg2;
因為我們在選擇停車點的時候是要點擊界面上的???圖標的,而Annotation翻譯過來是“注解、批注”的意思,那么按照理解這個方法應該是點擊了頁面上的注解圖標的,于是我們先來寫個插件驗證下,代碼如下:
%hook MBKMapManager
- (void)mt_mapView:(id)arg1 didSelectAnnotationView:(id)arg2 {
%orig;
NSLog(@"iOSRE: mt_mapView: %@ didSelectAnnotationView: %@", [arg1 class], [arg2 class]);
}
%end
編譯安裝至手機后,重新打開美團App到電單車頁面,然后點擊一下地圖上的???圖標,控制臺輸出了
iOSRE: mt_mapView: MTMapView didSelectAnnotationView: MBKGeoFencingAnnotationView
那么說明我們的判斷正確,這個就是點擊事件,而從輸出來看第一個參數(shù)是MapView自身,第二個就是這個停車點的對象,于是我們跳進這個類看看

但是這個類并沒有太多內容,可能屬性及方法都在父類,于是我們跳進他的父類看看

很遺憾,這個父類并沒有關于定位的任何相關屬性或方法,于是我們再跳入這個類的父類看看

很幸運,在這個類我們終于發(fā)現(xiàn)了一個比較可疑的屬性,還記得我們剛才翻譯過的“Annotation”單詞么,說明這個屬性很可能就是記錄這個停車點位置的對象,于是我們跳進這個屬性進去看看
@property(retain, nonatomic) id <MTAnnotation> annotation; // @dynamic annotation;
不過這個屬性的類型為id <MTAnnotation>,從做了一年的iOS開發(fā)的我來思考,他很可能是個協(xié)議(Protocol),而且頭文件里也有MTAnnotation-Protocol.h這個類,于是我們跳進去看看

作為有著一年iOS開發(fā)經(jīng)驗的我來說,一看這個coordinate就是定位信息
@protocol MTAnnotation <NSObject>
@property(readonly, nonatomic) struct CLLocationCoordinate2D coordinate;
這已經(jīng)非常的明顯了,接下來我們梳理一下流程
拿到定位信息的流程
MBKHomeViewController -> mapManager -> -[mt_mapView:didSelectAnnotationView:]
-> 第二個參數(shù)MBKGeoFencingAnnotationView -> annotation -> coordinate
于是我們在剛才的插件上繼續(xù)編寫代碼來進行驗證,代碼如下:
%hook MBKMapManager
- (void)mt_mapView:(id)arg1 didSelectAnnotationView:(id)arg2 {
%orig;
if ( [arg2 isKindOfClass:[%c(MBKGeoFencingAnnotationView) class] ]) {
id annotation = [arg2 valueForKey:@"annotation"];
NSLog(@"iOSRE: annotation: %@", annotation);
id coor = [annotation valueForKey:@"coordinate"];
NSLog(@"iOSRE: coor: %@ - %@", [coor class], coor);
}
}
%end
然后編譯安裝到手機,重新打開美團App進入電單車頁面并點擊地圖上???圖標,控制臺輸出了
iOSRE: annotation: <MBKGeoFencingAnnotation: 0x28156b3a0>
iOSRE: coor: NSConcreteValue - {length = 16, bytes = 0x99666b43860d3a4087bed79d6acd5d40}
看到這里,發(fā)現(xiàn)有點不對,按照我們剛才梳理的邏輯,這個coor應該是CLLocationCoordinate2D類型才對,但是從打印結果來看他是NSConcreteValue,這時候有點懵,按照class-dump導出的頭文件來看他正常應該是CLLocationCoordinate2D類型才對,難道是class-dump導出錯誤或者是我們分析錯誤?
在查閱了大量資料以及詢問其他iOS開發(fā)后,我發(fā)現(xiàn)他可能是NSKeyValueCoding,于是抱著死馬當活馬醫(yī)的態(tài)度我打算進行嘗試,畢竟我做iOS開發(fā)也沒多久沒太多經(jīng)驗,于是編寫代碼如下:
%hook MBKMapManager
- (void)mt_mapView:(id)arg1 didSelectAnnotationView:(id)arg2 {
%orig;
if ( [arg2 isKindOfClass:[%c(MBKGeoFencingAnnotationView) class] ]) {
id annotation = [arg2 valueForKey:@"annotation"];
NSLog(@"iOSRE: annotation: %@", annotation);
id coor = [annotation valueForKey:@"coordinate"];
NSLog(@"iOSRE: coor: %@ - %@", [coor class], coor);
struct CLLocationCoordinate2D cll;
[coor getValue:&cll];
NSLog(@"iOSRE: arg2: cll: latitude: %f longitude: %f", cll.latitude, cll.longitude);
}
%end
再次編譯安裝到手機,運行美團App進入電單車頁面點擊地圖上???圖標,控制臺輸出了
iOSRE: annotation: <MBKGeoFencingAnnotation: 0x28156b3a0>
iOSRE: coor: NSConcreteValue - {length = 16, bytes = 0x99666b43860d3a4087bed79d6acd5d40}
iOSRE: arg2: cll: latitude: 26.xxxxx longitude: 119.xxxxxx
oooooooooh!! 看到這最后一行輸出,難道我們成功了么,于是我們改造代碼如下:
%hook MBKMapManager
- (void)mt_mapView:(id)arg1 didSelectAnnotationView:(id)arg2 {
%orig;
if ( [arg2 isKindOfClass:[%c(MBKGeoFencingAnnotationView) class] ]) {
id annotation = [arg2 valueForKey:@"annotation"];
id coor = [annotation valueForKey:@"coordinate"];
struct CLLocationCoordinate2D cll;
[coor getValue:&cll];
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"當前選擇位置" message:[NSString stringWithFormat:@"緯度: %f\n經(jīng)度: %f\n\n建議/bug反饋QQ: 1451925/656469762", cll.longitude, cll.latitude] preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction * setparking = [UIAlertAction actionWithTitle:@"設置為???" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
lat = cll.latitude;
lng = cll.longitude;
}];
UIAlertAction * unSetparking = [UIAlertAction actionWithTitle:@"取消???" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
lat = 0;
lng = 0;
}];
UIAlertAction * copy1 = [UIAlertAction actionWithTitle:@"復制緯度" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
UIPasteboard.generalPasteboard.string = [NSString stringWithFormat:@"%f", cll.latitude];
}];
UIAlertAction * copy2 = [UIAlertAction actionWithTitle:@"復制經(jīng)度" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
UIPasteboard.generalPasteboard.string = [NSString stringWithFormat:@"%f", cll.longitude];
}];
UIAlertAction * cancel = [UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:nil];
[alert addAction:setparking];
[alert addAction:unSetparking];
[alert addAction:copy1];
[alert addAction:copy2];
[alert addAction:cancel];
[UIApplication.sharedApplication.delegate.window.rootViewController presentViewController:alert animated:YES completion:nil];
}
}
%end
再次編譯安裝到手機,此時點擊地圖上的???圖標,彈窗正常彈出來了
此時點擊“設置為???”后,看見我們的定位確確實實改變到了點擊的停車點位置,并且在出門實驗后在一個不能還車的地點進行操作后還車,也確實不需要收取10元手續(xù)費; 到這里已經(jīng)成功破解美團的停車點限制了,再次吐槽美團不嚴謹,僅僅只是驗證手機定位。
Ps: 由于涉及內容過多,上述的代碼均為部分代碼,非完整代碼,不過會逆向的應該能自己Copy一份書來。
最后插件下載地址: https://wwa.lanzous.com/iSYmgfqmxqh
(不保證該插件能使用多久,畢竟公開出來肯定就會被處理)
我是亞里亞,我的夢想是成為一個不禿頭的安全工程師,那么下期見。