iOS逆向记录(三)

96
Flonger
2017.08.30 14:34* 字数 3244

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内部使用,执行被钩住(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<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
    
iOS逆向
Web note ad 1