如何使用 Logos 優雅的注入 Mac app

0x11901發表於2018-05-19

前言

Logos 是 Theos 的一個元件,它允許程式設計師使用一組特殊的前處理器指令來編寫鉤子,簡潔高效。 做過 iOS 逆向開發的朋友應該非常熟悉,這裡筆者將介紹如何在 Mac app 上使用 Logos。

可能用到的工具

  1. Theos
  2. optool/insert_dylib
  3. unsign (optional)

一個簡單的例子

  • 編寫一個簡單的 demo,大概就是 軟體正中一個按鈕,點選之後 alert("hi!")。核心程式碼如下:

    #import "ViewController.h"
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        // Do any additional setup after loading the view.
    }
    
    - (IBAction)sayHi:(NSButton *)sender {
        NSAlert *alert = NSAlert.new;
        alert.messageText = @"hi!";
        alert.alertStyle = NSAlertStyleInformational;
        [alert runModal];
    }
    
    - (void)setRepresentedObject:(id)representedObject {
        [super setRepresentedObject:representedObject];
    
        // Update the view, if already loaded.
    }
    
    @end
    複製程式碼

    效果圖

我們的目標是注入 sayHi 這個方法,使點選按鈕之後不再說“hi!”,而是“hello world!”

  • 編寫 Logos

    %config(generator=internal)
    
    // 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.
    #import <Foundation/Foundation.h>
    
    %hook ViewController
    
    // Hooking an instance method with an argument.
    - (void)sayHi:(id)argument {
        NSAlert *r15 = [[NSAlert alloc] init];
        [r15 setMessageText:@"hello world!"];
        [r15 setAlertStyle:0x1];
        [r15 runModal];
    }
    
    // Always make sure you clean up after yourself; Not doing so could have grave consequences!
    %end
    
    %ctor {
        NSLog(@"!!!!!!inject success!!!!!!!");
    }
    複製程式碼

將以上程式碼儲存為一個 Tweak.xm 檔案(名字字尾名隨意),放在與 SayHi.app 同級目錄下,便於後續操作。

  • 然後我們使用 Theos 的語法分析來把 Logos 轉換成普通程式碼 $THEOS/bin/logos.pl Tweak.xm > abc.mm 注意 abc 應該有 mm 作為字尾名,用於告訴 clang 目標語言型別

  • 使用 clang 編譯轉換後的普通程式碼,並將結果放到 app 包內 clang -shared -undefined dynamic_lookup -o ./SayHi.app/Contents/MacOS/lib.dylib ./abc.mm

  • 使用 optool/insert_dylib 往 SayHi 的 MachO 頭部新增我們剛剛編譯的 lib.dylib optool install -c load -p @executable_path/lib.dylib -t ./SayHi.app/Contents/MacOS/SayHi

如果你的 Mac app 沒有簽名的話,此時應該已經達成我們的需求了。但是實踐中我們肯定不是對自己匯出的未簽名 Mac app 下黑手。所以需要去掉這個簽名或重簽名。因為筆者沒有錢買開發者賬號,故不知道如何重簽名。

  • 使用 codesign 去除簽名 codesign --remove-signature SayHi.app

此時我們的需求已經達成

大成功

但是 codesign 有一個 bug,在刪除程式碼簽名之後沒有修復 MachO Header 的偏移,會導致生成的 MachO 檔案畸形。筆者曾經就遇見一個不到 1m 的小程式在移除簽名後膨脹到 2g 大小。 所以筆者建議使用開源社群的代替方案——unsign

後記

筆者把上面的繁瑣命令列操作整合為一個指令碼,在這裡也順便分享出來

#!/usr/bin/env bash

#將xm和檔案app包放在同一個目錄,執行本腳步進行注入

path=`ls | grep *.app | head -1`
tweak=`ls | grep *.xm | head -1`
temp='x11901'
name=${path%.app}

$THEOS/bin/logos.pl "./${tweak}" > "./${temp}.m"
clang -shared -undefined dynamic_lookup -o "./${path}/Contents/MacOS/lib.dylib" "./${temp}.mm"
optool install -c load -p @executable_path/lib.dylib -t "./${path}/Contents/MacOS/${name}"

rm -f ${temp}.m

# 使用unsign效果可能更好,codesign --remove-signature 在刪除程式碼簽名之後沒有修復MachO Header的偏移,導致生成的MachO檔案畸形
# codesign --remove-signature ${name}
if [ ! -e "./${path}/Contents/MacOS/${name}.ori" ]; then
    unsign "./${path}/Contents/MacOS/${name}"
    mv "./${path}/Contents/MacOS/${name}" "./${path}/Contents/MacOS/${name}.ori"
    mv "./${path}/Contents/MacOS/${name}.unsigned" "./${path}/Contents/MacOS/${name}"
fi

open "./${path}/Contents/MacOS/${name}"
複製程式碼

下載 Demo

相關文章