前端時(shí)間看了一些逆向工程誉尖,掃了一眼'小黃書'辛掠,不得不感嘆runtime的強(qiáng)大之處。在不越獄情況下注入一些dylib來hook定位释牺,步數(shù)等系統(tǒng)函數(shù)后萝衩,發(fā)現(xiàn)用到的工具都是高度封裝好的,就回頭寫了一個(gè)runtime的小例子;
①動(dòng)態(tài)添加屬性
建立公開類的Category没咙,此處以UIWebView
為例子猩谊;
引如objc/runtime.h
,聲明屬性:
#import "UIWebView+Swizzling.h"
#import <objc/runtime.h>
@interface UIWebView ()
@property(nonatomic,copy)NSString* associatedProp;
@end
用runtime
的objc_setAssociatedObject
實(shí)現(xiàn)setter
祭刚,objc_getAssociatedObject
實(shí)現(xiàn)getter
:
-(void)setAssociatedProp:(NSString *)associatedProp{
objc_setAssociatedObject(self, &associatedPropKey, associatedProp, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString*)associatedProp{
return objc_getAssociatedObject(self, &associatedPropKey);
}
使用:
[self.webView setValue:@"associatedPropSuccess" forKey:@"associatedProp"];
NSLog(@"\nassociatedProp:%@",[self.webView valueForKey:@"associatedProp"]);
打优平荨:
②hook公開類的public||private方法
建立公開類的Category,此處以UIWebView
的loadRequest:
和webView:didFinishLoadForFrame:
為例子涡驮;
使用class-dump
可以查看很多私有方法暗甥;
在+(void)load
方法中如下(為什么在load
中?可以想一下):
@implementation UIWebView (Swizzling)
+ (void)load
{
//hook公開類的public方法
Method loadRequest = class_getInstanceMethod([self class], NSSelectorFromString(@"loadRequest:"));
Method swizzling_loadRequest = class_getInstanceMethod([self class], @selector(swizzling_loadRequest:));
method_exchangeImplementations(loadRequest, swizzling_loadRequest);
//hook公開類&private方法
Method didFinishLoadForFrame = class_getInstanceMethod([self class], NSSelectorFromString(@"webView:didFinishLoadForFrame:"));
Method swizzling_didFinishLoadForFrame = class_getInstanceMethod([self class], @selector(swizzling_webView:didFinishLoadForFrame:));
method_exchangeImplementations(didFinishLoadForFrame, swizzling_didFinishLoadForFrame);
}
可以看到這個(gè)方法獲取了UIWebView
中兩個(gè)方法的入口imp并且交換了捉捅,效果從A->a B->b
變成了A->b B->a
;
下面只需要實(shí)現(xiàn)我們自己的方法:
- (void)swizzling_loadRequest:(id)request;
{
NSLog(@"\nswizzling_loadRequest\narg1:%@\n",request);
[self swizzling_loadRequest:request];
}
-(void)swizzling_webView:(id)webView didFinishLoadForFrame:(id)frame{
NSLog(@"\nswizzling_didFinishLoadForFrame\narg1:%@\narg2:%@\n",webView,frame);
[self swizzling_webView:webView didFinishLoadForFrame:frame];
}
由于imp互換撤防,當(dāng)UIWebView
調(diào)用loadRequest
時(shí)會(huì)進(jìn)入我們的swizzling_loadRequest:(id)request
,而我們調(diào)用時(shí)候也是如此棒口,不會(huì)產(chǎn)生遞歸寄月;
打庸枷ァ:
③hook未知類的方法,如代理
如果我們想要hook一個(gè)代理方法漾肮,甚至一個(gè)未知viewController
的viewDidLoad
厂抖,又要怎么做呢?
這里我們以UIWebView
的webViewDidStartLoad:
舉例克懊;
(webView:??????)
步驟如上忱辅,不過要先考慮一下實(shí)現(xiàn)代理方法前提是一定遵循了代理,那我們可以從setDelegate:
入手:
+ (void)load{
Method setDegelagte = class_getInstanceMethod([self class], NSSelectorFromString(@"setDelegate:"));
Method swizzling_setDelegate = class_getInstanceMethod([self class], @selector(swizzling_setDelegate:));
method_exchangeImplementations(setDegelagte, swizzling_setDelegate);
}
const char *type = "v@:@@";
//v==void,@==object,:=selector 詳查Type Encoding
-(void)swizzling_setDelegate:(id)delegate{
[self swizzling_setDelegate:delegate];
Class vc = [delegate class];
NSLog(@"\nswizzling_delegateClass\nclass:%@\n",vc);
if ([vc isSubclassOfClass:[UIViewController class]]) {
//不同于load方法谭溉,設(shè)置單例保證交換1次
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//生命周期不同 所以先加入目標(biāo)類中 再在目標(biāo)類中進(jìn)行exchange
class_addMethod(vc, @selector(swizzling_webViewDidStartLoad:), class_getMethodImplementation([self class], @selector(swizzling_webViewDidStartLoad:)), type);
Method webViewDidStartLoad = class_getInstanceMethod(vc, NSSelectorFromString(@"webViewDidStartLoad:"));
Method swizzling_webViewDidStartLoad = class_getInstanceMethod(vc, @selector(swizzling_webViewDidStartLoad:));
method_exchangeImplementations(webViewDidStartLoad, swizzling_webViewDidStartLoad);
NSLog(@"addMethodOK?:%d",class_addMethod(vc, @selector(addMethod), class_getMethodImplementation([self class], @selector(addMethod)), type));;
});
}
}
我們首先讓方法順利執(zhí)行耕蝉,然后就可以獲取到delegate的class,但是此時(shí)機(jī)不唯一夜只,所以要在單例中進(jìn)行exchange垒在;
由于exchange方法的類不是UIWebView
,所以要先把方法add到delegate class
扔亥,然后再進(jìn)行exchange场躯;
同時(shí)在目標(biāo)類中加入了一個(gè)方法;
接下來實(shí)現(xiàn):
-(void)swizzling_webViewDidStartLoad:(id)webView{
NSLog(@"\nswizzling_webViewDidStartLoad\n");
//執(zhí)行添加的方法 此處已進(jìn)入目標(biāo)類 直接執(zhí)行self addMethod
[self addMethod];
[self swizzling_webViewDidStartLoad:webView];
}
-(void)addMethod{
NSLog(@"addMethodRunSuccess");
[self performSelector:NSSelectorFromString(@"back:") withObject:nil afterDelay:1];
}
再看看打勇眉贰:
至此我們已經(jīng)利用
UIWebView
的delegate
成功獲取到目標(biāo)class并hook代理方法踢关,添加了自定義方法并調(diào)用;
小結(jié)
可以猜測(cè)逆向工程正是利用runtime
粘茄,先編譯Category到dylib签舞,再把動(dòng)態(tài)的dylib壓到二進(jìn)制文件讀取列表中,再進(jìn)行重簽名柒瓣,在app運(yùn)行時(shí)會(huì)加載我們的Category儒搭,從而完成hook等操作;
runtime
和Category了解的還是不夠多芙贫,有時(shí)間真的要把runtime
中的api研究一下搂鲫。。磺平。
一點(diǎn)小的見解魂仍,還望各位多多交流和教導(dǎo)。