2020年 IOS 逆向 反編譯 注入修改遊戲或APP的調用參數新手系列教程——用bfinject脫殼、注入自己的動態framework、cycript的使用

開篇

本篇文章是繼上一篇文章:2020年 IOS 逆向 反編譯 注入修改遊戲或APP的調用參數新手系列教程——按鍵精靈腳本來模擬合成燈籠後本繼續分享的教程,一天最多寫一篇了,有時候太懶了沉迷打遊戲就沒寫了~

網上很多教程講了一大堆話,最終翻來翻去不知道是想實現什麼功能和效果,我覺得一開始把需求&最終效果展示能讓讀者瞭解個大概和引起興趣,不會不知所以然。後面我會按這種文章思路來分享,先把結果呈現,再詳敘過程,我想是個不錯的分享思路😁。

需求&最終效果

bfinject對正在運行的APP脫殼打包成.ipa

脫殼打包成.ipa文件

注入自己的動態framework

動態注入後的效果

cycript的使用

cycript的使用

環境要求與即將使用的工具

之前我的MacOSX版本是10.10,只能安裝xcode7以下的版本,xcode7以下的版本沒有真機調試功能,於是升級了版本到MacOSX Catalina 10.15.4也安裝了最新版xcode,但是這個版本太卡而且有些問題,很多軟件兼容不了了,想以後降級。

環境 版本
操作系統 MacOSX Catalina 10.15.4 版本太新了不太好用很多工具用不了,我後面打算降級
手機系統 Iphone7 IOS11 需要越獄
bfinject 最新版
手機助手傳輸工具或SSH連接操作 -
xcode 11.5
電腦上的cycript 最新版
手機終端工具比如terminal -
電腦工具class-dump用來導出頭文件查看 最新

工具介紹

bfinject是一款注入工具,安裝後坑挺多。可以注入xcode開發的framework,也可以注入ios10以前人們用的cycript工具,因爲ios11已經不支持cycript的使用了,只能通過這個工具來執行cycript的全部命令,然後用電腦的cycript連接手機cycript提供出來的端口來操作。
電腦上的cycript安裝教程參考這篇文章:https://www.jianshu.com/p/d93e9fccef4b,這玩意安裝後坑很多,一一填坑吧,而且官網打開好慢~。
cycript是一款動態注入工具,可以動態執行cy代碼,常用來打印ui界面和調試。
ios11的ssh本人用不了,從cydia安裝了openssh,但是用命令行執行ssh報無法打開二進制文件的英文錯誤,不知道爲何,誰能在ios11運行ssh並且電腦連接手機ssh的麻煩告知我一下謝謝。

實現過程

bfinject對正在運行的APP脫殼打包成.ipa

安裝bfinject

首先電腦下載bfinject,然後用手機助手等工具把二進制文件bfinject拷貝到iphone手機的隨意位置下,我是放在/User/Media/目錄下。bfinject下載和安裝教程參考github。
這個bfinject的坑還是很多的安裝後執行會出現很多報錯~比如electra和bootstrap目錄問題的坑;還有md5: command not found的報錯,我的做法是把md5sum這個命令複製一個改成md5執行就不報錯,網上有填坑例子,遇到的可以看看。另外說明,這個bfinject的執行需要關閉Tweaks才能運行成功:打開越獄工具Electra,把Tweaks選項禁用,然後重新啓動。
這個bfinject的執行需要關閉Tweaks才能運行成功:打開越獄工具Electra,把Tweaks選項禁用,然後重新啓動。
這個bfinject的執行需要關閉Tweaks才能運行成功:打開越獄工具Electra,把Tweaks選項禁用,然後重新啓動。

手機使用bfinject

以下及下文所有手機命令都是用root用戶來操作。
打開terminal到/User/Media/目錄下執行:

bash bfinject -P test1.app -L test

上圖界面的app是我自己隨便寫的一個demo app ,安裝在了iphone裏,我調試用的,這個app叫test1.app,這裏拿來演示,-L test 是指調用bfinject內置的framework來注入,用來確定bfinject是否安裝成功和生效,成功界面如下:
運行
打開到app的結果

打包

接下來用這個命令來導出ipa

bash bfinject -P test1.app -L decrypt

回到app界面
脫殼打包成.ipa文件
打包完畢!我們選擇No它會把包存儲到App的文檔目錄。
我們把包找出來:

find /var/mobile/Containers/Data/Application/ -name decrypted-app.ipa

結果
其中某個decrypted-app.ipa就是我們打包出來的路徑了,把這個包/User/Media/目錄下,然後用電腦手機助手工具就能拿到啦。接下來就是提取頭文件了。

注入自己的動態framework

提取頭文件

class-dump安裝比較簡單我就不說了,是用來提取match-o格式文件的工具,可以把ios開發的app的頭文件導出來,就能知道app裏面的類和方法、變量名。以此來注入指定方法。
電腦命令:

class-dump -H test1 -o test1Headers

test1是我把decrypted-app.ipa解壓后里面的match-o文件,test1Headers是指輸出所有頭文件到這個文件夾。

好了,我打開其中一個頭文件
頭文件
因爲這個app是我自己寫的,我知道down是其中一個Button按鈕是點擊後輸出一個彈窗的功能,我就拿這個方法來演示注入把,就是實現點擊Button按鈕後再注入一個彈窗。當然其他app就要靠經驗分析啦,可以用hooper等反編譯工具分析。

編寫注入代碼

打開xcode,新建工程選擇IOS中的Framework & Library 中的Framework。
Product Name 等信息你們自己填,我的命名是snakeGameHacker
創建好工程目錄後,snakeGameHacker.h就是頭文件,我的這樣寫:


#import <UIKit/UIKit.h>

//! Project version number for snakeGameHacker.
FOUNDATION_EXPORT double snakeGameHackerVersionNumber;

//! Project version string for snakeGameHacker.
FOUNDATION_EXPORT const unsigned char snakeGameHackerVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import 
#import <HackerLoader.h>
#import <NSObject+Hacker.h>

然後再創建Cocoa Touch class 文件,HackerLoader,會自動生成HackerLoader.h和HackerLoader.m文件。
HackerLoader.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@interface HackerLoader : NSObject

@end

HackerLoader.m


#import "HackerLoader.h"
#import "NSObject+Hacker.h"
#import <objc/runtime.h>
@implementation HackerLoader
static void __attribute__((constructor)) entry(void) {
    NSLog(@">>>>> Code Injected 哈哈哈3哈<<<<<");  
    NSObject *obj = [[NSObject alloc] init];
    [obj hack];
}

@end

再創建objective-c File文件,類型選擇Category,名字Hacker
最終產生:NSObject+Hacker.h和NSObject+Hacker.m
NSObject+Hacker.h


#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@interface NSObject (Hacker)

- (void)hack;

@end

NSObject+Hacker.m
這就是我們的核心文件了

#import "NSObject+Hacker.h"
#import <objc/runtime.h>
@implementation NSObject (Hacker)

- (void)hack {
    NSLog(@">>>>> Code Injected powerby maimai <<<<<");
    NSString *className = @"ViewController";
    [self hookMethod:@"down" ofClass:className hookMethodName:@"down2"];
}

// 封裝方法掛載函數
- (void)hookMethod:(NSString *)oriMethodName ofClass:(NSString *)ClassName hookMethodName:(NSString *)hookMethodName   {
    
    NSLog(@"掛載方法。。。。");
    
           static dispatch_once_t onceToken;
       dispatch_once(&onceToken, ^{
           Class oriMethodClass = NSClassFromString(ClassName);

           Class class = [self class];

          
           SEL originalSelector = NSSelectorFromString([oriMethodName stringByAppendingString:@":"]);
           SEL swizzledSelector = NSSelectorFromString([hookMethodName stringByAppendingString:@":"]);
    
           Method originalMethod = class_getInstanceMethod(oriMethodClass, originalSelector);
           Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
    
            BOOL didAddMethod = class_addMethod(oriMethodClass,originalSelector,method_getImplementation(swizzledMethod),method_getTypeEncoding(swizzledMethod));
    
           if (didAddMethod) {// 判斷是否已經有這個方法了
    
               class_replaceMethod(oriMethodClass,swizzledSelector,method_getImplementation(originalMethod),method_getTypeEncoding(originalMethod));
    
           } else {
    
               method_exchangeImplementations(originalMethod, swizzledMethod);
    
          }
       });
}



// 按鈕按下
- (void)down2:(id)sender{
    NSLog(@"----down2----weirui3----");
    [self showError:@"我在app的方法裏注入自己的代碼啦!"];
    return [self down2:sender];
}


- (UIViewController *)_topViewController:(UIViewController *)vc {
    if ([vc isKindOfClass:[UINavigationController class]]) {
        return [self _topViewController:[(UINavigationController *)vc topViewController]];
    } else if ([vc isKindOfClass:[UITabBarController class]]) {
        return [self _topViewController:[(UITabBarController *)vc selectedViewController]];
    } else {
        return vc;
    }
    return nil;
}


- (UIViewController *)topViewController {
    UIViewController *resultVC;
    resultVC = [self _topViewController:[[UIApplication sharedApplication].keyWindow rootViewController]];
    while (resultVC.presentedViewController) {
        resultVC = [self _topViewController:resultVC.presentedViewController];
    }
    return resultVC;
}



- (void)showError:(NSString *)errorMsg {
    UIViewController *uvc = [self topViewController];
    NSLog(@"----weirui3----當前vc%@", NSStringFromClass([uvc class]));
    // 1.彈框提醒
    // 初始化對話框
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"提示" message:errorMsg preferredStyle:UIAlertControllerStyleAlert];
    [alert addAction:[UIAlertAction actionWithTitle:@"確定" style:UIAlertActionStyleDefault handler:nil]];
    // 彈出對話框
    [uvc presentViewController:alert animated:true completion:nil];
    NSLog(@"uvc show");
}

@end

代碼講解

static void attribute((constructor)) entry(void) {}
這個方法會被進程注入的時候執行到這裏。我們實例化一個NSObject執行NSObject+Hacker.m.hack()方法,hack方法裏面有一段

NSString *className = @"ViewController";
    [self hookMethod:@"down" ofClass:className hookMethodName:@"down2"];
    

hookMethod是我封裝的函數調用hook方法,是利用OC語言中swizzledMethod實現方法替換和注入,theos 中tweak的%orig原理就是用了swizzledMethod實現hook。後面我會分享tweak注入的教程,我覺得比bfinject穩定靠譜。
意思是ViewController類下面的down()方法掛載一個我們指定的方法down2()
接下來我們編寫down2方法。

// 按鈕按下
- (void)down2:(id)sender{
    NSLog(@"----down2----weirui3----");
    [self showError:@"我在app的方法裏注入自己的代碼啦!"];
    return [self down2:sender];
}

攔截了down的執行,插入了一段代碼 [self showError:@“我在app的方法裏注入自己的代碼啦!”];
就是彈窗代碼,在本頁面彈窗的實現,具體你們自己看彈窗實現代碼吧,這個網上很多類似代碼。然後再 return [self down2:sender];
就是真正的%orig那段代碼了,看似是遞歸調用,實際不會。

編譯

我們先測試一下報錯沒有吧。如果要用單例測試要讓編譯目標 改爲 iOS Simulators,我選ios8,然後按command+U,運行,證實不報錯跑通到
hack()方法裏面:
單例測試
當然down2方法是不會被執行的,因爲down2是動態執行,需要利用bfinject注入後啓動對應app觸發down那個方法纔會被執行。
我們編譯代碼。
注意,編譯目標 切換爲Generic iOS Device。
注意,編譯目標 切換爲Generic iOS Device
注意,編譯目標 切換爲Generic iOS Device
注意,編譯目標 切換爲Generic iOS Device
其他證書相關配置改爲None,目標版本改爲你手機能執行的版本,等一些手續。
然後按下Command+B編譯。
Products/目錄下產生snakeGameHacker.framework目錄,用finder打開進入找到裏面的snakeGameHacker的 Unix可執行文件,就是我們注入的對象了。

注入

把snakeGameHacker上傳到手機目錄。
手機裏執行

bash bfinject -P test1.app -l snakeGameHacker

注意這裏的 -l 和上面不同,這裏的是小寫的。
執行後回到app按一下Button按鈕(down方法的執行觸發)
結果注入成功:
動態注入後的效果
至此,framework注入講解完畢。

cycript的使用

手機執行:

bash bfinject -P test1.app -L cycript

cycript的使用
然後電腦打開終端,輸入對應地址:

cycript -r 192.168.0.101:1337

不過我經常連接好久或失敗,感覺不好用。多試幾次才能成功。
本來想寫幾個命令的,現在連接不上,算了~

結束

此教程僅做學習交流和知識記錄方便以後查看使用,如果涉及到利益相關的請告知本人進行刪帖處理。

本人想通過這些博客記錄自己這兩週內折騰的過程以及分享最後成功的成果。另外想結交志同道合對IT行業感興趣的盆友,互相交流學習。可以通過博客聯繫我或加QQ號:1321691245

今天就先寫到這,後面我再把我學習的其他相關知識分享給大家。下一篇就介紹theos 中編寫tweak實現修改遊戲數據的文章吧。

博文主索引目錄入口

我會把這系列的文章更新到這個入口裏面,分享我的心得,大家互相學習。
2020年 IOS 逆向 反編譯 注入修改遊戲或APP的調用參數新手系列教程主目錄入口

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章