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
, 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内部使用,执行被钩住(hook)的函数的原始代码。
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
//软件作者 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
Label com.openssh.sshd Program /usr/libexec/sshd-keygen-wrapper ProgramArguments /usr/sbin/sshd -i SessionCreate Sockets Listeners SockServiceName ssh StandardErrorPath /dev/null inetdCompatibility Wait
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
#import #import @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