iOS逆向之TheOS


TheOS 被設計為一個在基於 Unix 平台 (Mac OS X、IOS…) 和大多數 的Linux 平台下開發 iOS 程序的集成開發環境。說是集成開發環境,其實就是給我們准備好了一些代碼模板、預置一些基本的 Makefile 腳本,我們還是要在 終端命令行 中完成一些關鍵操作。

1.安裝和配置

第一步:安裝 dpkg 和 LDID。

brew install dpkg ldid

第二步:安裝 TheOS。

sudo git clone --recursive https://github.com/theos/theos.git /opt/theos

指令中的 --recursive ,表示遞歸執行。

第三步:配置權限。

sudo chown $(id -u):$(id -g) /opt/theos

第四步:配置環境變量。修改 bash_profile 文件,在文件中添加兩行: THEOS 目錄 和 bin。

export THEOS=/opt/theos
export PATH=/opt/theos/bin/:$PATH

【說明】:環境變量配置,可以查看OS X修改環境變量

2.使用Theos

2.1創建工程

在終端輸入如下指令:

nic.pl

如果提示如下錯誤:

執行以下指令即可:

source .bash_profile

執行完“nic.pl”指令后,會出現如下選擇模板的界面:

在逆向工程的初級階段,所開發程序的主要類型是 tweak,因此輸入 15,按 Enter 鍵繼續。接下來和 Xcode 創建工程一樣,會有一系列的步驟:

  1. 輸入工程名字(Project Name (required),必填項);
  2. 輸入包名(Package Name [com.yourcompany.helloworld]);
  3. 輸入作者的名字(Author/Maintainer Name [LeeGof]);
  4. 要注入程序的BundleID(MobileSubstrate Bundle filter [com.apple.springboard]):要注入程序的BundleID,默認的進程為com.apple.springboard,為了演示方便,在此先使用默認進程作為注入對象。

2.2工程文件說明

Tweak 工程新建完成后,其工程目錄下的文件,主要有 4 個:

2.2.1 control 文件

該文件記錄了工程的基本信息,會被打進 deb 包中,字段內容如下: 

Package: com.gof.-
Name: GofWechat
Depends: mobilesubstrate
Version: 0.0.1
Architecture: iphoneos-arm
Description: An awesome MobileSubstrate tweak!
Maintainer: LeeGof
Author: LeeGof
Section: Tweaks
  • Package 字段:用於描述這個 deb 包的名字,采用的命名方式和 bundle identifier 類似,可以按需更改;
  • Name 字段:用於描述這個工程的名字,可以按需更改;
  • Depends 字段:用於描述這個 deb 包的“依賴”。“依賴”指的是這個程序運行的基本條件,可以填寫固件版本或其他程序,如果當前 iOS 不滿足“依賴”中所定義的條件,則此 tweak 無法正常工作,可以按需更改。例如:
Depends: mobilesubstrate, firmware (>=9.0)

表示當前iOS版本必須在 9.0 以上,且必須安裝 MobileSubstrate,才能正常運行這個 tweak。

  • Version 字段:用於描述這個 deb 包的版本號,可以按需更改;
  • Architecture 字段:用於描述 deb 包安裝的目標設備架構,不要更改
  • Description 字段:deb 包的簡單介紹,可以按需更改;
  • Maintainer 字段:用於描述 deb 包的維護人,即 deb 包的制作者而非 tweak 的作者,可以按需更改;
  • Author 字段:用於描述 tweak 的作者,可以按需更改;
  • Section 字段:用於描述 deb 包所屬的程序類別,不要更改

control 文件中可以自定義的字段還有很多,一般上面的信息就已經足夠了。更全面的可以查看官方網站:http://www.debian.org/doc/debian-policy/ch-controlfields.html。值得注意的是:Theos 在打包 deb 時會對 control 文件做進一步處理。比如更改 Version 字段為:0.0.1-2,標識 Theos 的打包次數,方便管理;增加 Installed-Size 字段,用於描述 deb 包安裝后的估算大小,與實際大小可能有偏差,不要更改。

2.2.2 Makefile

該文件用來指定工程編譯和鏈接要用到的文件、框架、庫等信息,將整個過程自動化,字段內容如下:

export THEOS_DEVICE_IP=127.0.0.1
export THEOS_DEVICE_PORT=2222

include $(THEOS)/makefiles/common.mk

TWEAK_NAME = GofWechat
GofWechat_FILES = Tweak.xm
GofWechat_CFLAGS = -fobjc-arc

include $(THEOS_MAKE_PATH)/tweak.mk

after-install::
    install.exec "killall -9 SpringBoard"
  • 前面兩行配置了 USB 連接對應的 IP 和 端口,根據實際情況配置;
  • 第 3 行的 include 字段指定了工程的 common.mk 文件,固定寫法,不要修改
  • TWEAK_NAME 字段填入的是建立工程時命令行輸入的 Project Name,與 control 文件中的“Name”字段對應,不要更改
  • GofWechat_FILES 字段指定工程包含的源文件,如果工程中需要用到多個源文件則用空格將各個文件名分開,可以按需更改;
  • include 字段指定工程的 mk 文件,這里新建的是 tweak 工程,所以填入的是 tweak.mk 文件,還可以根據需求填入 application.mk 以及 tool.mk 文件;
  • 最后一行 after-install 字段指定安裝程序后需要執行的操作,這里注入 Wechat 進程並執行自己的代碼,需要重啟 SpringBoard進程,好讓 MobileSubstrate 加載對應的 dylib。

Makefile文件除了自動生成的這些字段外,還可以根據功能手動添加其他字段:

  • ARCHS 字段可以用來指定處理器架構,一般情況下填寫“ARCHS = armv7 arm64”即可;
  • TARGET 字段用來指定SDK版本,例如:
TARGET = iphone:7.0
  • framework 字段可以指定要導入的框架,比如 “GofWechat_FRAMEWORKS = UIKit”,可以導入 UIKit 框架,另一方面,還可以通過 GofWechat_PRIVATE_FRAMEWORKS 字段指定要導入的私有庫,格式不變。例如:
GofWechat_FRAMEWORKS = UIKit CoreTelephony CoreAudio
GofWechat_PRIVATE_FRAMEWORKS = AppSupport ChatKit

2.2.3 GofWechat.plist

plist 文件記錄工程的配置信息,主要作用是指定程序的作用范圍,內容如下:

從上面可以看到,該文件的內容是一系列的 Dictionary,最外層為 Root 鍵,Root 鍵下為 Filter 鍵,Filter 鍵下的 array 即為要設置的部分。該部分分為三類:

  • 第一類是 Bundles,即所編寫程序的作用對象,這個類型的字段就是所要注入的程序 Bundle 名,這里要注入的進程為 Wechat,所以填寫 com.tencent.xin,可以指定多個作用對象;
  • 第二類是 Classes,即指定要注入的類名,同樣是根據填入的字符串來篩選注入的類名;
  • 第三類是 Executables,即指定要注入的可執行文件名。

這三類 Array 可以根據需要來設定。但按照混合配置方式,一個文件只有滿足 Filter 中所有 Array 下的至少一個條件,tweak 才能生效。這樣顯然不合理。所以額外有一個 Mode 鍵,將其值設置為 Any,那么文件滿足 Filter 中的任一條件就能成為 tweak 的作用對象。

當 Filter 下的 array 只有一類時,不需要添加 Mode 和 Any 鍵值對。

2.2.4 Tweak.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
*/

以上的注釋展示了基本的Logos語法,具體可以分為三類:

  • 第一類是 %hook 和 %end,其中 %hook 后面指定要 hook 的類名,另一方面,hook 的整塊邏輯完成后結尾要加上 %end,在 hook 邏輯中可以添加要 hook 的函數,並在函數體內部實現想要添加的代碼邏輯;
  • 第二類是 %orig,該語句代表執行原函數邏輯,即完成 hook 操作后可以選擇是否調用原函數的代碼,若需要調用則加上“%orig;”即可;
  • 第三類是 %log,這類代碼的作用是在 log 中打印 hook 的函數的類名、參數等信息。

除了注釋中展示的三種語法外,Logos 還支持 %group、%init、%ctor 等語法,更多的 Logos 語法可查看這篇文章

這里我們 Hook 微信的登錄按鈕事件,獲取密碼並打印,代碼如下:

%hook WCAccountLoginLastUserViewController

- (void)onNext{
    UIView *view = MSHookIvar<UIView *>(self,"m_passwordView");
    UITextField *tfPwd = view.subviews[0].subviews[0].subviews[0];
    //NSLog(@"\nLeeGof 密碼:%@", tfPwd.text);
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Gof" message:tfPwd.text delegate:self cancelButtonTitle:@"確定" otherButtonTitles:nil];
    [alert show];

    //%orig;
}

%end

2.3編譯&打包&安裝

2.3.1編譯

Theos 采用與 debian 相同的 make 命令來編譯 Theos 工程。執行如下指令:

~/Desktop/GofWechat/gofwechat > make
> Making all for tweak GofWechat…
==> Preprocessing Tweak.xm…
==> Compiling Tweak.xm (armv7)…
==> Linking tweak GofWechat (armv7)…
ld: warning: building for iOS, but linking in .tbd file (/opt/theos/vendor/lib/CydiaSubstrate.framework/CydiaSubstrate.tbd) built for iOS Simulator
==> Generating debug symbols for GofWechat…
==> Preprocessing Tweak.xm…
==> Compiling Tweak.xm (arm64)…
==> Linking tweak GofWechat (arm64)…
ld: warning: building for iOS, but linking in .tbd file (/opt/theos/vendor/lib/CydiaSubstrate.framework/CydiaSubstrate.tbd) built for iOS Simulator
==> Generating debug symbols for GofWechat…
rm /Users/GofLee/Desktop/GofWechat/gofwechat/.theos/obj/debug/arm64/Tweak.xm.mm
==> Preprocessing Tweak.xm…
==> Compiling Tweak.xm (arm64e)…
==> Linking tweak GofWechat (arm64e)…
ld: warning: building for iOS, but linking in .tbd file (/opt/theos/vendor/lib/CydiaSubstrate.framework/CydiaSubstrate.tbd) built for iOS Simulator
==> Generating debug symbols for GofWechat…
rm /Users/GofLee/Desktop/GofWechat/gofwechat/.theos/obj/debug/arm64e/Tweak.xm.mm
==> Merging tweak GofWechat…
==> Signing GofWechat…

從上面的輸出結果可以看到,Theos 完成了預處理、編譯、簽名等一系列操作,此時目錄下多了一個 .theos 文件夾。在這個目錄下,有一個文件:.theos/obj/debug/GofWechat.dylib,這個文件實際上是編譯之后生成的動態庫。

這里有一個注意點:路徑只能是英文,如果是中文的話,有可能會報錯失敗。

2.3.2打包

使用如下指令打包:

~/Desktop/GofWechat/gofwechat > make package > Making all for tweak GofWechat…
make[2]: Nothing to be done for `internal-library-compile'.
> Making stage for tweak GofWechat…
dm.pl: building package `com.gof.-:iphoneos-arm' in `./packages/com.gof.-_0.0.1-1+debug_iphoneos-arm.deb'

命令執行完成后,目錄下多了一個 packages 文件夾,里面有一個 deb 文件,這是安裝到手機上的插件格式,它和編譯階段生成的 .theos/obj/debug/GofWechat.dylib 關聯。

有時候可能遇到如下所示錯誤信息:

解決方法:修改 opt/theos/makefiles/package/deb.mk 文件的第六行:

將 THEOSPLATFORM_DPKG_DEB_COMPRESSION ?= lzma,最后的 lzma 改成 xz 就可以了。

2.3.3安裝

執行指令:

~/Desktop/GofWechat/gofwechat > make package install
> Making all for tweak GofWechat…
make[2]: Nothing to be done for `internal-library-compile'.
> Making stage for tweak GofWechat…
dm.pl: building package `com.gof.-:iphoneos-arm' in `./packages/com.gof.-_0.0.1-3+debug_iphoneos-arm.deb'
==> Installing…
Selecting previously unselected package com.gof.-.
(Reading database ... 2471 files and directories currently installed.)
Preparing to unpack /tmp/_theos_install.deb ...
Unpacking com.gof.- (0.0.1-3+debug) ...
Setting up com.gof.- (0.0.1-3+debug) ...
install.exec "killall -9 SpringBoard"

可以看到已經成功安裝。這時候我們打開 Cydia,可以看到在 Cydia 中有了我們自己定義的插件:

安裝成功后,打開微信,輸入密碼,點擊登錄,可以看到我們寫的 Alert 框,正常彈出了。這里,大家可能會好奇:我們寫的這個插件,是在設備的哪里安裝的?

我們可以把移動設備中的微信可執行文件導出,可以看到 Mach-O 文件並沒有做修改。那這個動態庫是怎么生效的呢?微信在啟動時,dyld 根據 DYLD_INSERT_LIBRARIES 環境變量進行注入的。

有時會遇到如下報錯:

這種情況一般是deb.mk文件版本不正確,這里更新為下面的版本就可以了。

ifeq ($(_THEOS_PACKAGE_FORMAT_LOADED),)
_THEOS_PACKAGE_FORMAT_LOADED := 1 _THEOS_DEB_PACKAGE_CONTROL_PATH := $(or $(wildcard $(THEOS_PROJECT_DIR)/control),$(wildcard $(THEOS_PROJECT_DIR)/layout/DEBIAN/control)) _THEOS_DEB_CAN_PACKAGE := $(if $(_THEOS_DEB_PACKAGE_CONTROL_PATH),$(_THEOS_TRUE),$(_THEOS_FALSE)) _THEOS_DEB_HAS_DPKG_DEB := $(call __executable,dpkg-deb) ifneq ($(_THEOS_DEB_HAS_DPKG_DEB),$(_THEOS_TRUE)) internal-package-check:: @echo "$(MAKE) package requires dpkg-deb."; exit 1 endif ifeq ($(_THEOS_DEB_CAN_PACKAGE),$(_THEOS_TRUE)) # Control file found (or layout/ found.) THEOS_PACKAGE_NAME := $(shell grep -i "^Package:" "$(_THEOS_DEB_PACKAGE_CONTROL_PATH)" | cut -d' ' -f2-) THEOS_PACKAGE_ARCH := $(shell grep -i "^Architecture:" "$(_THEOS_DEB_PACKAGE_CONTROL_PATH)" | cut -d' ' -f2-) THEOS_PACKAGE_BASE_VERSION := $(shell grep -i "^Version:" "$(_THEOS_DEB_PACKAGE_CONTROL_PATH)" | cut -d' ' -f2-) $(_THEOS_ESCAPED_STAGING_DIR)/DEBIAN: $(ECHO_NOTHING)mkdir -p "$(THEOS_STAGING_DIR)/DEBIAN"$(ECHO_END) ifeq ($(_THEOS_HAS_STAGING_LAYOUT),1) # If we have a layout/ directory, copy layout/DEBIAN to the staging directory. $(ECHO_NOTHING)[ -d "$(THEOS_PROJECT_DIR)/layout/DEBIAN" ] && rsync -a "$(THEOS_PROJECT_DIR)/layout/DEBIAN/" "$(THEOS_STAGING_DIR)/DEBIAN" $(_THEOS_RSYNC_EXCLUDE_COMMANDLINE) || true$(ECHO_END) endif # _THEOS_HAS_STAGING_LAYOUT $(_THEOS_ESCAPED_STAGING_DIR)/DEBIAN/control: $(_THEOS_ESCAPED_STAGING_DIR)/DEBIAN $(ECHO_NOTHING)sed -e '/^[Vv]ersion:/d' "$(_THEOS_DEB_PACKAGE_CONTROL_PATH)" > "$@"$(ECHO_END) $(ECHO_NOTHING)echo "Version: $(_THEOS_INTERNAL_PACKAGE_VERSION)" >> "$@"$(ECHO_END) $(ECHO_NOTHING)echo "Installed-Size: $(shell du $(_THEOS_PLATFORM_DU_EXCLUDE) DEBIAN -ks "$(THEOS_STAGING_DIR)" | cut -f 1)" >> "$@"$(ECHO_END) before-package:: $(_THEOS_ESCAPED_STAGING_DIR)/DEBIAN/control _THEOS_DEB_PACKAGE_FILENAME = $(THEOS_PACKAGE_DIR)/$(THEOS_PACKAGE_NAME)_$(_THEOS_INTERNAL_PACKAGE_VERSION)_$(THEOS_PACKAGE_ARCH).deb internal-package:: $(ECHO_NOTHING)COPYFILE_DISABLE=1 $(FAKEROOT) -r dpkg-deb -Zgzip -b "$(THEOS_STAGING_DIR)" "$(_THEOS_DEB_PACKAGE_FILENAME)" $(STDERR_NULL_REDIRECT)$(ECHO_END) # This variable is used in package.mk after-package:: __THEOS_LAST_PACKAGE_FILENAME = $(_THEOS_DEB_PACKAGE_FILENAME) else # _THEOS_DEB_CAN_PACKAGE == 0 internal-package:: @echo "$(MAKE) package requires you to have a layout/ directory in the project root, containing the basic package structure, or a control file in the project root describing the package."; exit 1 endif # _THEOS_DEB_CAN_PACKAGE endif # _THEOS_PACKAGE_FORMAT_LOADED

也可以先將deb文件拷貝到設備,然后cd到設備對應的目錄,執行如下指令:

dpkg -i *.deb

2.3.4卸載

如果需要卸載安裝包,執行如下指令即可:

dpkg -r com.gof.-

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM