簡單代碼注入

1、代碼注入目的

  • 瞭解密碼學代碼/APP簽名原理及重籤技術後,能夠對其餘的應用進行重籤、調試,這並非最終目的,咱們要作的是在別人的應用中添加本身的代碼,並讓APP執行咱們的代碼,這樣咱們才能理解如何作到惡意代碼注入,並清楚知道如何去防禦。
  • APP 運行時執行的文件
    • 1.系統的框架,系統庫的Macho可執行文件;
      • 非越獄手機沒法修改系統庫的方法和實現;
      • 越獄手機能夠修改(暫時作不到,以觀後效);
    • 2.應用包內對應的應用的Macho可執行文件;
      • 能夠直接修改Macho文件的內容(暫時不會,須要學習完彙編後直接修改其二進制文件)
    • 3.應用中添加/依賴的framework,dylib等庫編譯產生的Macho文件;
      • 添加一個本身的framework/dylib庫,相對簡單,本篇文章將着手於此,簡單介紹如何經過添加framework/dylib庫,打到注入代碼的目的,同時也會介紹相關知識和工具;

2、代碼注入原理和實現

  • dyld(the dynamic link editor)是蘋果的動態連接器,是蘋果操做系統一個重要組成部分,在系統讀取程序MachO文件的Header段信息,作好程序準備工做以後,交由dyld負責加載動態庫和靜態庫以及其餘工做(後面章節會詳細介紹)。linux

  • 動態庫:連接時不復制,在程序啓動後用動態加載,而後再決議符號,因此理論上動態庫只用存在一份,好多個程序均可以動態連接到這個動態庫上面,達到了節省內存(不是磁盤是內存中只有一份動態庫),還有另一個好處,因爲動態庫並不綁定到可執行程序上,因此咱們想升級這個動態庫就很容易,windows和linux上面通常插件和模塊機制都是這樣實現的。git

  • 靜態庫:連接時會被完整的複製到可執行文件中,因此若是兩個程序都用了某個靜態庫,那麼每一個二進制可執行文件裏面其實都含有這份靜態庫的代碼。github

  • 通常修改原始的程序,是利用代碼注入的方式,注入代碼就會選擇利用FrameWork或者Dylib等三方庫的方式注入;windows

  • 一、FrameWork 注入:bash

    • 1.重籤後,經過Xcode新建Framwork,將庫安裝進入APP包,可是程序運行時不會調用執行咱們FrameWork庫內的代碼;
    • 2.將本身的FrameWork添加到MachO文件中的loadCommands中,須要用工具yololib,將本身寫的FrameWork編譯後產生的MachO文件寫入APP的MachO文件的loadCommands中,經過yololib注入Framwork庫路徑;
      • 命令:$yololib(空格)MachO文件路徑(空格)庫路徑
      • 全部的Framwork加載都是由DYLD加載進入內存被執行的
      • 注入成功的庫路徑會寫入到MachO文件的LC_LOAD_DYLIB字段中
  • 二、Dylib注入框架

    • 經過Xcode新建Dylib庫(注意:Dylib屬於MacOS因此須要修改屬性)工具

      • 1.修改Base SDK 爲iOS;
      • 2.簽名信息修改成iOS Developer
    • 添加Target依賴,讓Xcode將自定義Dylib文件打包進入APP包。post

      • 1.項目target的Build Phases 添加Copy Files 依賴庫,依賴庫中依賴剛新建的dylib庫
    • 利用yololib進行注入。學習

3、利用 RunTime 進行代碼注入,交換原來的實現

  • 一、Method Swizzle

    • 利用OC的Runtime特性,動態改變SEL(方法編號)和IMP(方法實現)的對應關係,達到OC方法調用流程改變的目的。主要用於OC方法。
    • 正向開發時,通常寫在相應類的分類中,主要API:
1.動態添加方法
///添加方法
class_addMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
複製代碼
2.交換方法
///獲取實例方法
class_getInstanceMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>)
///獲取類方法
class_getClassMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>)
///交換方法
method_exchangeImplementations(<#Method _Nonnull m1#>, <#Method _Nonnull m2#>)
複製代碼
3.替換方法實現
/// 替換方法實現
class_replaceMethod(<#Class _Nullable __unsafe_unretained cls#>, <#SEL _Nonnull name#>, <#IMP _Nonnull imp#>, <#const char * _Nullable types#>)
複製代碼
4.設置/獲取方法實現
/// 設置方法實現
method_setImplementation(<#Method _Nonnull m#>, <#IMP _Nonnull imp#>)
/// 獲取方法實現
method_getImplementation(<#Method _Nonnull m#>)
複製代碼
  • 二、Hook的內容

    • 1.重籤後運行到手機上的應用,斷點調試,找到相應的類名和方法名;
    • 2.利用class-dump將MachO文件內的全部類的描述拷貝出來,即全部.h頭文件;
      • 命令:class-dump -H 文件名 -o 輸出文件的路徑;eg:$class-dump -H WeChat -o ./headers/
    • 3.在LLDB中經過 valueForKey: 獲取相應UI控件;
  • 三、多種Hook方式

    • 1.method_exchangeImplementations 方法交換的方式不行
      • oc方法默認參數self是方法的調用者,_cmd方法編號;
      • 沒法獲取當前類,就沒法寫成分類;
      • 寫在frameWork中的代碼,交換時原類或其對象沒法調用當前方法;
+ (void)load{
    ///一、方法交換 找不到方法報錯
    Method oldMethod =  class_getInstanceMethod(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    Method newMethod = class_getInstanceMethod(self, @selector(nasy_loginAction:));
    method_exchangeImplementations(oldMethod, newMethod);
}
/// 一、方法交換
-(void)nasy_loginAction:(id)btn {
    NSLog(@"\n\n\nusername == %@......\npassword == %@......\n\n\n\n",[[[self valueForKey:@"_inputView"] subviews][0] valueForKey:@"text"],[[[self valueForKey:@"_inputView"] subviews][2] valueForKey:@"text"]);
    [self nasy_loginAction:nil];
}
複製代碼
    • 2.class_addMethod方式:
      • 利用AddMethod方式,讓原始方法能夠被調用,不至於由於找不到SEL而崩潰
///二、添加方法
+ (void)load{
    Method oldMethod =  class_getInstanceMethod(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    BOOL isAdd = class_addMethod(objc_getClass("JDNewLoginViewController"), @selector(nasy_loginAction:), new_loginAction, "v@:");

    if (isAdd) {
        Method newMethod =  class_getInstanceMethod(objc_getClass("JDNewLoginViewController"), @selector(nasy_loginAction:));
        method_exchangeImplementations(oldMethod, newMethod);
    }

}
void new_loginAction(id self , SEL _cmd){

    NSLog(@"\n\n\nusername == %@......\npassword == %@......\n\n\n\n",[[[self valueForKey:@"_inputView"] subviews][0] valueForKey:@"text"],[[[self valueForKey:@"_inputView"] subviews][2] valueForKey:@"text"]);
    [self performSelector:@selector(nasy_loginAction:)  withObject:nil];
}
複製代碼
    • 3.class_replaceMethod方式:
      • 利用class_replaceMethod,直接給原始的方法替換IMP
/// 三、交換方法實現
+ (void)load{
    oldIMP = class_getMethodImplementation(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    class_replaceMethod(objc_getClass("JDNewLoginViewController"), @selector(loginAction:), new_loginAction, "v@:");
}
IMP ( * oldIMP)(id self,SEL _cmd);
void new_loginAction(id self , SEL _cmd){

    NSLog(@"\n\n\nusername == %@......\npassword == %@......\n\n\n\n",[[[self valueForKey:@"_inputView"] subviews][0] valueForKey:@"text"],[[[self valueForKey:@"_inputView"] subviews][2] valueForKey:@"text"]);
    oldIMP(self,_cmd);
}

複製代碼
    • 4.method_setImplementation方式:
      • 利用method_setImplementation,直接從新賦值原始的新的IMP
/// 四、set  get IMP
+ (void)load{
   oldIMP = class_getMethodImplementation(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    Method oldMethod =  class_getInstanceMethod(objc_getClass("JDNewLoginViewController"), @selector(loginAction:));
    method_setImplementation(oldMethod, new_loginAction);
}
IMP ( * oldIMP)(id self,SEL _cmd);

void new_loginAction(id self , SEL _cmd){

   NSLog(@"\n\n\nusername == %@......\npassword == %@......\n\n\n\n",[[[self valueForKey:@"_inputView"] subviews][0] valueForKey:@"text"],[[[self valueForKey:@"_inputView"] subviews][2] valueForKey:@"text"]);
   oldIMP(self,_cmd);
}

複製代碼
相關文章
相關標籤/搜索