8.第一個逆向程序
-
創建tweak工程
? iOS /opt/theos/bin/nic.pl NIC 2.0 - New Instance Creator ------------------------------ [1.] iphone/activator_event [2.] iphone/application_modern [3.] iphone/cydget [4.] iphone/flipswitch_switch [5.] iphone/framework [6.] iphone/ios7_notification_center_widget [7.] iphone/library [8.] iphone/notification_center_widget [9.] iphone/preference_bundle_modern [10.] iphone/tool [11.] iphone/tweak [12.] iphone/xpc_service //選擇tweak工程 Choose a Template (required): 11 //工程名稱 Project Name (required): MyFirstReProject //deb包的名字(類似于bundle identifier) Package Name [com.yourcompany.myfirstreproject]: com.iosre.myfirstreproject //tweak作者 Author/Maintainer Name [System Administrator]: Flonger //tweak作用對象的bundle identifier [iphone/tweak] MobileSubstrate Bundle filter [com.apple.springboard]: com.apple.springboard //tweak安裝完成后需要重啟的應用 [iphone/tweak] List of applications to terminate upon installation (space-separated, '-' for none) [SpringBoard]: SpringBoard Instantiating iphone/tweak in myfirstreproject/... Done.
工程文件結構介紹
-
Makefile
//工程包含的通用頭文件 include $(THEOS)/makefiles/common.mk //創建工程時指定的“Project Name,指定好之后一般不要再更改 TWEAK_NAME = MyFirstReProject //tweak包含的源文件,指定多個文件時用空格隔開 MyFirstReProject_FILES = Tweak.xm //tweak工程的頭文件,一般有application.mk、tweak.mk和tool.mk幾類 include $(THEOS_MAKE_PATH)/tweak.mk //指定tweak安裝之后,需要做的事情,這里是殺掉SpringBoard進程 after-install:: install.exec "killall -9 SpringBoard" 補充: //編譯debug或者release DEBUG = 0 //越獄iPhone的ip地址 THEOS_DEVICE_IP = 192.168.1.113 //指定支持的處理器架構 ARCHS = armv7 arm64 //指定需要的SDK版本iphone:Base SDK:Deployment Target TARGET = iphone:latest:8.0 //最新的SDK,程序發布在iOS8.0以上 //導入框架,多個框架時用空格隔開 MyFirstReProject_FRAMEWORKS = UIKit MyFirstReProject_PRIVATE_FRAMEWORKS = AppSupport //鏈接libsqlite3.0.dylib、libz.dylib和dylib1.o MyFirstReProject_LDFLAGS = -lz –lsqlite3.0 –dylib1.o //make clean clean:: rm -rf ./packages/*
-
tweak文件
“xm”中的“x”代表這個文件支持Logos語法,如果后綴名是單獨一個“x”,說明源文件支持Logos和C語法;如果后綴名是“xm”
,說明源文件支持Logos和C/C++語法。/* How to Hook with Logos Hooks are written with syntax similar to that of an Objective-C @implementation. You don't need to #include <substrate.h>, it will be done automatically, as will the generation of a class list and an automatic constructor. %hook ClassName // Hooking a class method + (id)sharedInstance { return %orig; } // Hooking an instance method with an argument. - (void)messageName:(int)argument { %log; // Write a message about this call, including its class, name and arguments, to the system log. %orig; // Call through to the original function with its original arguments. %orig(nil); // Call through to the original function with a custom argument. // If you use %orig(), you MUST supply all arguments (except for self and _cmd, the automatically generated ones.) } // Hooking an instance method with no arguments. - (id)noArguments { %log; id awesome = %orig; [awesome doSomethingElse]; return awesome; } // Always make sure you clean up after yourself; Not doing so could have grave consequences! %end */
%hook 指定需要hook的class,必須以%end結尾
%log 該指令在%hook內部使用,將函數的類名、參數等信息寫入syslog
Cydia內搜索安裝syslogd%orig該指令在%hook內部使用,執行被鉤?。╤ook)的函數的原始代碼。
control
control文件記錄了deb包管理系統所需的基本信息,會被打包進deb包里。-
編譯工程
-
tweakxm 文件
%hook SpringBoard - (void)applicationDidFinishLaunching:(id)application { %orig; UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Hello,Tanzhou!" message:nil delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alert show]; } - (void)_menuButtonDown:(id)down { NSLog(@"x=%d, y=%d", 10, 20); %log((NSString *)@"iOSRE", (NSString *)@"Debug"); %orig; // call the original _menuButtonDown: } %end %hook SBLockScreenDateViewController - (void)setCustomSubtitleText:(id)arg1 withColor:(id)arg2 { /* NSDate *date=[NSDate date]; NSDateFormatter *format1=[[NSDateFormatter alloc]init]; [format1 setDateFormat:@"yyyy/MM/dd HH:mm:ss"]; NSString *str1=[format1 stringFromDate:date]; */ struct tm *loctime; char timeBuf[1024] = {0}; time_t now = time(NULL); loctime = localtime(&now); strftime(timeBuf, 30, "[%Y/%m/%d %H:%M:%S]", loctime); %orig([NSString stringWithUTF8String:timeBuf],arg2); } %end
-
MakeFile文件
DEBUG = 0 THEOS_DEVICE_IP = 10.171.4.22 ARCHS = armv7 arm64 TARGET = iphone:latest:8.0 include $(THEOS)/makefiles/common.mk TWEAK_NAME = MyFirstReProject MyFirstReProject_FILES = Tweak.xm MyFirstReProject_FRAMEWORKS = UIKit include $(THEOS_MAKE_PATH)/tweak.mk after-install:: install.exec "killall -9 SpringBoard" clean:: rm -rf ./packages/*
-
control文件
Package: com.iosre.myfirstreproject Name: MyFirstReProject Depends: mobilesubstrate Version: 1.0.1 Architecture: iphoneos-arm Description: My first reproject! Maintainer: Flonger Author: Flonger Section: Tweaks Homepage: https://www.baidu.com
-
-
編譯命令
make //編譯 make package //打包 make install //安裝
驗證結果
9.deb包介紹
官網:http://www.debian.org/doc/debian-policy/
deb包本質是一個壓縮包文件。里面包含一些特定的目錄和文件。安裝過程就是dpkg程序按照指定的規則去拷貝文件和執行腳本。
dpkg -c xxxx.deb //查看deb包的目錄結構
-
DEBIAN目錄
存放control文件、及安裝和卸載時需要執行的腳本等- control文件導出。。
//deb包的名字,卸載和查詢包信息都用這個名字 Package: com.iosre.myfirstreproject //工程名字(產品名字) Name: MyFirstReProject //依賴包(可以指定多個,用','分割) Depends: mobilesubstrate, firmware (>=8.0) //deb包版本號 Version: 1.0.1 //描述軟件所支持的平臺架構 Architecture: iphoneos-arm //deb包簡介 Description: My first reproject! //deb包維護人和聯系方式 Maintainer: Flonger<xue@flonger.com> //軟件作者 Author: Fonger //deb包歸屬類別 Section: Tweaks //軟件主頁 Homepage: https://www.baidu.com
- 腳本文件
preinst 在Deb包文件解包之前,將會運行該腳本。許多“preinst”腳本的任務是停止作用于待升級軟件包的服務,直到軟件包安裝或升級完成。 postinst 該腳本的主要任務是完成安裝包時的配置工作。許多“postinst”腳本負責執行有關命令為新安裝或升級的軟件重啟服務。 prerm 該腳本負責停止與軟件包相關聯的daemon服務。它在刪除軟件包關聯文件之前執行。 postrm 該腳本負責修改軟件包鏈接或文件關聯,或刪除由它創建的文件。
-
dpkg打包時會復制當前目錄下layout目錄下的所有文件和目錄
這些文件和目錄會鏡像到目標設備上(layout相對于設備的根目錄)//發布時的Makefile DEBUG = 0 THEOS_DEVICE_IP = 10.171.4.22 ARCHS = armv7 arm64 TARGET = iphone:latest:8.0 include $(THEOS)/makefiles/common.mk TWEAK_NAME = MyFirstReProject MyFirstReProject_FILES = Tweak.xm MyFirstReProject_FRAMEWORKS = UIKit include $(THEOS_MAKE_PATH)/tweak.mk clean:: rm -rf ./packages/* before-package:: cp ./script/postinst ./.theos/_/DEBIAN/ cp ./script/postrm ./.theos/_/DEBIAN/
10. 常見Logos語法介紹
維基百科:http://iphonedevwiki.net/index.php/Logos
10.1 Block-level
-
%hook
指定需要hook的class,必須以%end結尾。可以被%group包含%hook SBApplicationController -(void)uninstallApplication:(SBApplication *)application { NSLog(@"Hey, we're hooking uninstallApplication:!"); %orig; // Call the original implementation of this method return; } %end
-
%group
該指令用于將%hook分組,便于代碼管理及按條件初始化分組,必須以%end結尾。
一個%group可以包含多個%hook,所有不屬于某個自定義group的%hook會被隱式歸類到%group_ungrouped中。%group iOS8 %hook IOS8_SPECIFIC_CLASS // your code here %end // end hook %end // end group ios8 %group iOS9 %hook IOS9_SPECIFIC_CLASS // your code here %end // end hook %end // end group ios9 %ctor { if (kCFCoreFoundationVersionNumber > 1200) { %init(iOS9); } else { %init(iOS8); } }
-
%new
在%hook內部使用,給一個現有class添加新函數,功能與class_addMethod相同。
注: Objective-C的category與class_addMethod的區別: 前者是靜態的而后者是動態的。%hook SBApplicationController -(void)uninstallApplication:(SBApplication *)application { NSLog(@"Hey, we're hooking uninstallApplication:!"); %orig; // Call the original implementation of this method return; } %new - (void)namespaceNewMethod { NSLog(@"We've added a new method to SpringBoard."); } %end
10.2 Top level
%ctor
tweak的構造函數,完成初始化工作;如果不顯示定義,Theos會自動生成一個%ctor,并在其中調用%init(_ungrouped)。%dtor
tweak的構造函數,完成收尾。如果不顯示定義,Theos會自動生成一個%dtor。
10.3 Function level
- %init
該指令用于初始化某個%group,必須在%hook或%ctor內調用;如果帶參數,則初始化指定的group,如果不帶參數,則初始化_ungrouped.
注: 切記,只有調用了%ini,對應的%group才能起作用!
%ctor {
if (kCFCoreFoundationVersionNumber > 1200) %init(iOS9);
else %init(iOS8);
}
- %c
該指令的作用等同于objc_getClass或NSClassFromString,即動態獲取一個類的定義,在%hook或%ctor內使用 。%hook SpringBoard - (void)_menuButtonDown:(id)down { %orig; SBScreenShotter *shotter = [%c(SBScreenShotter) sharedInstance]; [shotter saveScreenshot:YES]; } %end@
- %log
該指令在%hook內部使用,將函數的類名、參數等信息寫入syslog,可以%log([(),…..])的格式追加其他打印信息。
tail -f /var/log/syslog | grep WeChat
- %orig
該指令在%hook內部使用,執行被hook的函數的原始代碼;也可以用%orig更改原始函數的參數。
//練習
@interface SBScreenshotter: NSObject
+ (id)sharedInstance;
- (void)saveScreenshot: (BOOL)arg1;
@end
@interface SpringBoard
+ (void)_AutoScreenSave2;
- (void)_AutoScreenSave;
@end
%hook SpringBoard
- (void)applicationDidFinishLaunching:(id)application
{
%orig;
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:@"Hello,Tanzhou!"
message:nil
delegate:self cancelButtonTitle:@"OK"
otherButtonTitles:nil];
[alert show];
}
%new
- (void)_AutoScreenSave
{
NSLog(@"instance method");
SBScreenShotter *shotter = [%c(SBScreenShotter) sharedInstance];
[shotter saveScreenshot:YES];
}
%new
+ (void)_AutoScreenSave2
{
NSLog(@"class method");
SBScreenShotter *shotter = [%c(SBScreenShotter) sharedInstance];
[shotter saveScreenshot:YES];
}
- (void)_menuButtonDown:(id)down
{
//SBScreenShotter *shotter = [%c(SBScreenShotter) sharedInstance];
//[shotter saveScreenshot:YES];
//[self _AutoScreenSave];
[%c(SpringBoard) _AutoScreenSave2];
NSLog(@"x=%d, y=%d", 10, 20);
%log((NSString *)@"iOSRE", (NSString *)@"Debug");
%orig; // call the original _menuButtonDown:
}
%end
%hook SBLockScreenDateViewController
- (void)setCustomSubtitleText:(id)arg1 withColor:(id)arg2
{
/*
NSDate *date=[NSDate date];
NSDateFormatter *format1=[[NSDateFormatter alloc]init];
[format1 setDateFormat:@"yyyy/MM/dd HH:mm:ss"];
NSString *str1=[format1 stringFromDate:date];
*/
struct tm *loctime;
char timeBuf[1024] = {0};
time_t now = time(NULL);
loctime = localtime(&now);
strftime(timeBuf, 30, "[%Y/%m/%d %H:%M:%S]", loctime);
%orig([NSString stringWithUTF8String:timeBuf],arg2);
}
%end
/*
%group HookTest
%hook SpringBoard
- (void)_lockButtonDown:(struct __IOHIDEvent *)arg1 fromSource:(int)arg2
{
NSLog(@"_lockButtonDown");
}
- (void)_lockButtonUp:(struct __IOHIDEvent *)arg1 fromSource:(int)arg2
{
NSLog(@"_lockButtonUp");
}
- (void)powerDownCanceled:(id)arg1
{
NSLog(@"powerDownCanceled");
%orig;
}
- (void)powerDown
{
NSLog(@"powerDown");
}
- (void)powerDownRequested:(id)arg1
{
NSLog(@"powerDownRequested");
}
%end
%end
*/
%ctor
{
%init(_ungrouped);
//%init(HookTest);
}
11.Tweak工作原理
1.Cydia Substrate 和 Mobile Substrate
Cydia Substrate 原名為 Mobile Substrate 已經正式更名為 Cydia Substrate。
它是越獄后cydia插件/軟件(主要指theos開發的tweak)運行的一個基礎依賴包。提供軟件運行的公共庫,可以用來動態替換內存中的代碼、數據等所以iOS系統越獄環境下安裝絕大部分插件,必須首先安裝Cydia Substrate。
Cydia Substrate主要由3部分組成:MobileHooker,MobileLoader 和 safe mode。
2.MobileHooker
-
MobileHooker用于替換覆蓋系統的方法,這個過程被稱為Hooking(掛鉤)
它主要包含兩個函數:
void MSHookMessageEx(Class class, SEL selector, IMP replacement, IMP *result);void MSHookFunction(voidfunction,void replacement,void** p_original);
MSHookMessageEx 主要作用于Objective-C函數
MSHookFunction 主要作用于C和C++函數
Logos語法%hook就是對此函數做了一層封裝,讓編寫hook代碼變的更直觀,上面的例子用的就是logos語法。
-
MSHookMessageEx
http://www.cydiasubstrate.com/api/c/MSHookMessageEx/void MSHookMessageEx(Class _class, SEL message, IMP hook, IMP *old);
NSString *(*oldDescription)(id self, SEL _cmd); // implicit self and _cmd are explicit with IMP ABI NSString *newDescription(id self, SEL _cmd) { NSString *description = (*oldDescription)(self, _cmd); description = [description stringByAppendingString:@"!"]; return description; } MSHookMessageEx( [NSObject class], @selector(description),&newDescription, &oldDescription );
-
MSHookFunction
http://www.cydiasubstrate.com/api/c/MSHookFunction/void MSHookFunction(void *symbol, void *hook, void **old);
void *(*oldConnect)(int, const sockaddr *, socklen_t); void *newConnect( int socket, const sockaddr *address, socklen_t length ) { if (address->sa_family == AF_INET) { sockaddr_in *address_in = address; if (address_in->sin_port == htons(6667)) { sockaddr_in copy = *address_in; address_in->sin_port = htons(7001); return oldConnect(socket, ?, length); } } return oldConnect(socket, address, length); } MSHookFunction(&connect, &newConnect, &oldConnect);
3.MobileLoader
MobileLoader 將tweak插件注入到第三方應用程序中(動態注入:ptrace)
-
啟動時MobileLoader會根據/Library/MobileSubstrate/DynamicLibraries/目錄中plist文件指定的作用范圍,
有選擇的在第三方進程空間里通過dlopen函數加載同名的dylib。每一個.dylib文件都會有一個同名的.plist文件。.plist文件的作用就是用來指定tweak插件的作用對象。
Flongers-iphone:/Library/MobileSubstrate/DynamicLibraries root# pwd /Library/MobileSubstrate/DynamicLibraries Flongers-iphone:/Library/MobileSubstrate/DynamicLibraries root# ls AppList.dylib@ MFService.dylib* MyFirstReProject.plist afc2dService.dylib* AppList.plist MFService.plist* PreferenceLoader.dylib* afc2dService.plist MFAccelerator.dylib* MFServiceEx.dylib* PreferenceLoader.plist patcyh.dylib@ MFAccelerator.plist* MFServiceEx.plist* RHRevealLoader.dylib* patcyh.plist MFHongbaoRobot.dylib* MobileSafety.dylib* RHRevealLoader.plist MFHongbaoRobot.plist* MobileSafety.plist RocketBootstrap.dylib@ MFService.bundle/ MyFirstReProject.dylib* RocketBootstrap.plist
4.safe mode
因為APP程序質量參差不齊崩潰再所難免,tweak本質是dylib,寄生在別人進程里,如果注入Springboard等。
系統進程一旦出錯,可能導致整個進程崩潰,崩潰后就會造成iOS癱瘓。所以CydiaSubstrate引入了安全模式,在安全模式下所有基于CydiaSubstratede 的三方dylib都會被禁用,便于查錯與修復。
建議:
- ssh到iPhone
- dpkg -r com.iosre.myfirstreprojec 可以刪除對應的Tweak插件包
12. Tweak練習
1.定位目標文件
- ps方法
ps -e | grep WeChat
-
find方法
find -name sshd
-
固定目錄中查找
AppStore App全部位于“/var/mobile/Containers/Bundle/Application/”下, 系統App全部位于“/Applications/”下 daemon的配置文件均位于 “/System/Library/LaunchDaemons/” “/Library/LaunchDaemons” “/Library/LaunchAgents/” 是一個plist格式的文件。其中的“ProgramArguments”字段,即是daemon可執行文件的絕對路徑 Flongers-iphone:/Library/LaunchDaemons root# cat com.openssh.sshd.plist <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>Label</key> <string>com.openssh.sshd</string> <key>Program</key> <string>/usr/libexec/sshd-keygen-wrapper</string> <key>ProgramArguments</key> <array> <string>/usr/sbin/sshd</string> <string>-i</string> </array> <key>SessionCreate</key> <true/> <key>Sockets</key> <dict> <key>Listeners</key> <dict> <key>SockServiceName</key> <string>ssh</string> </dict> </dict> <key>StandardErrorPath</key> <string>/dev/null</string> <key>inetdCompatibility</key> <dict> <key>Wait</key> <false/> </dict> </dict> </plist>
2.獲取頭文件信息和bundleid
- 砸殼
- 通過class-dump獲取頭文件
- 獲取bundleid
codesign -dvvv WeChat
3.分析頭文件編寫tweak代碼
-
Makefile文件
THEOS_DEVICE_IP = 192.168.1.113 DEBUG = 1 ARCHS = armv7 arm64 TARGET = iphone:latest:8.0 include $(THEOS)/makefiles/common.mk TWEAK_NAME = WeChatReProject WeChatReProject_FILES = Tweak.xm WeChatReProject_FRAMEWORKS = UIKit include $(THEOS_MAKE_PATH)/tweak.mk after-install:: install.exec "killall -9 WeChat" clean:: rm -rf ./packages/*
-
control文件
Package: com.iosre.wechatreproject Name: WeChatReProject Depends: mobilesubstrate Version: 0.0.1 Architecture: iphoneos-arm Description: WeChat Tweak Maintainer: FLonger Author: Flonger Section: Tweaks Homepage: https://www.baidu.com
plist文件
{ Filter = { Bundles = ( "com.tencent.xin" ); }; }
- tweak.xm文件
#import<UIKit/UIKit.h> #import <CoreLocation/CoreLocation.h> #import <CoreLocation/CLLocation.h> @interface SeePeopleNearByLogicController - (void)onRetrieveLocationOK:(id)arg1; @end %hook SeePeopleNearByLogicController - (void)onRetrieveLocationOK:(id)arg1 { CLLocation *location = [[CLLocation alloc] initWithLatitude:31.154352 longitude:121.42562]; %orig(location); UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[@"onRetrieveLocationOK" stringByAppendingString:[[NSString alloc] initWithFormat:@"location is %@", location]] message:nil delegate:self cancelButtonTitle:@"ok" otherButtonTitles:nil]; [alertView show]; } %end