對iOS的注入,已經有一定了解了。對于Mac app的注入,如何下手呢?
來寫個簡單的小例子吧。
目標app是自己寫的一個Mac app,要求hook按鈕的點擊事件,在處理事件之前,打印一下“hooked”表面hook成功。
1.第一步,先class_dump出文件頭,找到目標函數。我們先明確一下目標:
類名:AppKitView
函數:
- (void)btnAction:(id)arg1;
2.在xcode中新建一個framework項目,取名hook。
3.在新建的項目中,引入hook的框架。也就是2個文件,如圖:
在github的地址:
https://github.com/rentzsch/jrswizzle
在hook.h中,引入框架,編輯宏定義
//
// hook.h
// hook
//
#import <Cocoa/Cocoa.h>
#import "JRSwizzle.h"
//! Project version number for hook.
FOUNDATION_EXPORT double hookVersionNumber;
//! Project version string for hook.
FOUNDATION_EXPORT const unsigned char hookVersionString[];
// In this header, you should import all the public headers of your framework using statements like #import <hook/PublicHeader.h>
#define CBGetClass(classname) objc_getClass(#classname)
#define CBRegisterClass(superclassname, subclassname) { Class class = objc_allocateClassPair(CBGetClass(superclassname), #subclassname, 0);objc_registerClassPair(class); }
#define CBHookInstanceMethod(classname, ori_sel, new_sel) { NSError *error; [CBGetClass(classname) jr_swizzleMethod:ori_sel withMethod:new_sel error:&error]; if(error) NSLog(@"%@", error); }
#define CBHookClassMethod(classname, ori_sel, new_sel) { NSError *error; [CBGetClass(classname) jr_swizzleClassMethod:ori_sel withClassMethod:new_sel error:&error]; if(error) NSLog(@"%@", error); }
#define CBGetInstanceValue(obj, valuename) object_getIvar(obj, class_getInstanceVariable([obj class], #valuename))
#define CBSetInstanceValue(obj, valuename, value) object_setIvar(obj, class_getInstanceVariable([obj class], #valuename), value)
3.項目中引入目標頭文件
4.新建Main.h 和Main.mm,內容如下
Main.h
//
// Main.h
// hook
//
#import <objc/runtime.h>
#import "hook.h"
Main.mm
//
// Main.m
// hook
//
#import "Main.h"
#import "AppKitView.h"
@implementation NSObject (hook)
#pragma mark - AppKitView
- (void)cb_btnAction:(id)arg1 {
[self cb_btnAction:arg1];
NSLog(@"cb_btnAction --- hooked");
}
@end
static void __attribute__((constructor)) initialize(void) {
NSLog(@"++++++++ loaded ++++++++");
// CBHookClassMethod();
CBHookInstanceMethod(AppKitView, @selector(btnAction:), @selector(cb_btnAction:));
}
上述代碼中,cb_btnAction 是我們定義的新函數,函數內部調用自己就等于是調用被hook前的原函數(因為方法被交換了)。
交換方法的代碼就是通過以下代碼實現的。
CBHookInstanceMethod(AppKitView, @selector(btnAction:), @selector(cb_btnAction:));
5.編寫腳本,用于自動注入
在xcode的targets->BuildPhases里添加一個腳本。
腳本內容:
#!/bin/bash
app_name="ColorPicker"
framework_name="hook"
app_bundle_path="/Applications/${app_name}.app/Contents/MacOS"
app_executable_path="${app_bundle_path}/${app_name}"
app_executable_backup_path="${app_executable_path}_"
framework_path="${app_bundle_path}/${framework_name}.framework"
# 備份原始可執行文件
if [ ! -f "$app_executable_backup_path" ]
then
cp "$app_executable_path" "$app_executable_backup_path"
fi
cp -r "${BUILT_PRODUCTS_DIR}/${framework_name}.framework" ${app_bundle_path}
/opt/iOSOpenDev/bin/insert_dylib --all-yes "${framework_path}/${framework_name}" "$app_executable_backup_path" "$app_executable_path"
主要內容就是,定義好目標app的路徑,備份好原有二進制文件。將編譯好的framework文件拷貝到目標app目錄,用insert_dylib這個工具進行注入。
insert_dylib這個工具是需要自己去編譯下載的。
github地址:
https://github.com/Tyilo/insert_dylib
到這里,我們做的工作大概有這些:
根據我們的代碼,編譯生成一個framework。編譯完成后執行xcode里面配置的腳本,將我們的framework復制到目標app目錄,然后用insert_dylib工具將framework注入到app里。最后一步,就是要打開這個app。編輯schema的excutable,選擇目標app
好,再次編譯一下項目。你就會發現,自動編譯、注入、打開了app。我們點擊一下按鈕事件,看下控制臺,注入成功了!
2017-03-15 15:58:56.418731 ColorPicker[18284:7540684] cb_btnAction --- hooked
2017-03-15 15:59:08.467269 ColorPicker[18284:7540684] cb_btnAction --- hooked
2017-03-15 15:59:08.771605 ColorPicker[18284:7540684] cb_btnAction --- hooked
2017-03-15 15:59:09.099117 ColorPicker[18284:7540684] cb_btnAction --- hooked
2017-03-15 15:59:09.419099 ColorPicker[18284:7540684] cb_btnAction --- hooked
2017-03-15 15:59:09.731221 ColorPicker[18284:7540684] cb_btnAction --- hooked