版权声明:转载请注明出处:http://blog.csdn.net/hursing

方法一,hook已有公开头文件的类:

首先写一个Utility函数:

  1. #import <objc/runtime.h>
  2. void exchangeMethod(Class aClass, SEL oldSEL, SEL newSEL)
  3. {
  4. Method oldMethod = class_getInstanceMethod(aClass, oldSEL);
  5. assert(oldMethod);
  6. Method newMethod = class_getInstanceMethod(aClass, newSEL);
  7. assert(newMethod);
  8. method_exchangeImplementations(oldMethod, newMethod);
  9. }

现在,目标是hook UIWebView没公开的函数

  1. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;

因为已知类的声明,所以可以使用category:

  1. @interface UIWebView (Hook)
  2. + (void)hook;
  3. - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
  4. @end
  5. @implementation UIWebView (Hook)
  6. + (void)hook
  7. {
  8. // hook UIWebView中表示一个HTML的frame加载完毕的函数
  9. exchangeMethod([UIWebView class],
  10. @selector(webView:didFinishLoadForFrame:),
  11. @selector(hook_webView:didFinishLoadForFrame:));
  12. }
  13. - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2
  14. {
  15. // 因为交换了selector和implementation的映射,原样调一下本函数实际会调用被hook的函数。
  16. [self hook_webView:arg1 didFinishLoadForFrame:arg2];
  17. NSLog(@"webView:didFinishLoadForFrame:");
  18. }

在程序启动的时候调用一下 [UIWebView hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。

方法二,hook没有公开头文件的类,需要另建一个类作为新函数载体,然后先为被hook的类增加函数,再替换。
UIWebView体系中有一个类叫UIWebBrowserView,它是真正显示网页的UIView,并有部分函数是作为WebCore向外发送回调信息的接收者。
现我们去hook UIWebBrowserView的这个函数:

  1. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;

嗯,是的,这个函数和UIWebView的一样,实际上就是UIWebBrowserView会再调用通知到UIWebView的同名函数。
创建一个类,不要与被hook的类同名,例如加了个Hook前缀:

  1. @interface UIWebBrowserViewHook : NSObject
  2. + (void)hook;
  3. - (void)hook_webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
  4. @end

其中以hook_为前缀的新增函数的实现与方法一中相同,差别在类函数中:

  1. @implementation UIWebBrowserViewHook
  2. + (void)hook
  3. {
  4. Class aClass = objc_getClass("UIWebBrowserView");
  5. SEL sel = @selector(hook_webView:didFinishLoadForFrame:);
  6. // 为UIWebBrowserView增加函数
  7. class_addMethod(aClass, sel, class_getMethodImplementation([self class], sel), "v@:@@");
  8. // 交换实现
  9. exchangeMethod(aClass, @selector(webView:didFinishLoadForFrame:), sel);
  10. }

在程序启动的时候调用一下 [UIWebBrowserViewHook hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。

方法三,hook没有公开头文件的类,另建一个类作为新函数载体,用新函数替换旧函数,并把旧函数保存到静态变量里:

继续以UIWebBrowserView为例子。注意新函数可以与被hook的函数同名

  1. @interface UIWebBrowserViewHook : NSObject
  2. + (void)hook;
  3. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2;
  4. @end

需要用到另一个Utility函数:

  1. inline void replaceImplementation(Class newClass, Class hookedClass, SEL sel, IMP& oldImp)
  2. {
  3. Method old = class_getInstanceMethod(hookedClass, sel);
  4. IMP newImp = class_getMethodImplementation(newClass, sel);
  5. oldImp = method_setImplementation(old, newImp);
  6. }

当两个selector不同名时,以上函数再增加一个参数即可。

下面是实现:

  1. @implementation UIWebBrowserViewHook
  2. static IMP webView_didFinishLoadForFrame = NULL;
  3. + (void)hook
  4. {
  5. Class hookedClass = objc_getClass("UIWebBrowserView");
  6. SEL sel = @selector(webView:didFinishLoadForFrame:);
  7. replaceImplementation([self class], hookedClass, sel, webView_didFinishLoadForFrame);
  8. }
  9. - (void)webView:(id)arg1 didFinishLoadForFrame:(id)arg2
  10. {
  11. // 需要这样来调用被替换掉的原实现
  12. webView_didFinishLoadForFrame(self, @selector(webView:didFinishLoadForFrame:), arg1, arg2);
  13. NSLog(@"webView:didFinishLoadForFrame:");
  14. }
  15. @end

在程序启动的时候调用一下 [UIWebBrowserViewHook hook] 即可。使用一个UIWebView打开一个网页,即会打印NSLog。

三种方法的比较:

最方便的当然是第一种,但需要是hook有公开头文件的类。

方法二和方法三都新建了一个类,方法二需要描述selector的types,这个比较麻烦,如例子中的"v@:@@"表示返回值为void,第一和第二个参数都是id。方法三不用types,但要增加全局变量。

Objective-C的runtime参考资料:
http://developer.apple.com/library/iOS/#documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40008048

 
 

利用Objective-C运行时hook函数的三种方法的更多相关文章

  1. 微信小程序使用函数的三种方法

    使用来自不同页面的函数 函数写在util.js页面 function formatTime(date) { var year = date.getFullYear() var month = date ...

  2. javascript函数 (二 定义函数的三种方法)

    javascript定义函数(声明函数)可以有三种方法:正常方法.构造函数.函数直接量 <html><head></head><body> <sc ...

  3. matlab拟合函数的三种方法

    方法一:多项式拟合polyfit 1 x=[1 2 3 4 5 6 7 8 9]; 2 3 y=[9 7 6 3 -1 2 5 7 20]; 4 P= polyfit(x, y, 3) %三阶多项式拟 ...

  4. jQuery添加自定义函数的三种方法

    原文链接:http://caibaojian.com/284.html 方法一: jQuery.fn.setApDiv=function () { //apDiv浮动层显示位置居中控制 var whe ...

  5. 利用matlab求图像均值和方差的几种方法

    一.求均值 % 求一副灰度图像的均值 close all; clear; clc; i=imread('d:/lena.jpg'); %载入真彩色图像 i=rgb2gray(i); %转换为灰度图 i ...

  6. Objective C运行时(runtime)

    #import <objc/runtime.h> void setBeingRemoved(id __self, SEL _cmd) { NSLog(@"------------ ...

  7. js字符串转换为数字的三种方法。(转换函数)(强制类型转换)(利用js变量弱类型转换)

    js字符串转换为数字的三种方法.(转换函数)(强制类型转换)(利用js变量弱类型转换) 一.总结 js字符串转换为数字的三种方法(parseInt("1234blue"))(Num ...

  8. 痞子衡嵌入式:在IAR开发环境下将关键函数重定向到RAM中执行的三种方法

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家分享的是在IAR开发环境下将关键函数重定向到RAM中执行的三种方法. 嵌入式项目里应用程序代码正常是放在 Flash 中执行的,但有时候也需要将 ...

  9. (转)在网页中JS函数自动执行常用三种方法

    原文:http://blog.sina.com.cn/s/blog_6f6b4c3c0100nxx8.html 在网页中JS函数自动执行常用三种方法 在网页中JS函数自动执行常用三种方法 在HTML中 ...

随机推荐

  1. 柯尔莫可洛夫-斯米洛夫检验(Kolmogorov–Smirnov test,K-S test)

    柯尔莫哥洛夫-斯米尔诺夫检验(Колмогоров-Смирнов检验)基于累计分布函数,用以检验两个经验分布是否不同或一个经验分布与另一个理想分布是否不同. 在进行cumulative probab ...

  2. Intel系列CPU的流水线技术的发展

    Intel系列CPU的流水线技术的发展 CPU(Central processing Unit),又称“微处理器(Microprocessor)”,是现代计算机的核心部件.对于PC而言,CPU的规格与 ...

  3. 微信公众平台消息接口开发之微信浏览器HTTP_USER_AGENT判断

    在微信公众平台的开发过程中,我们有时需要开发网页并判断是否是是来自微信浏览器访问,本文介绍如何做出这一判断. 一.$_SERVER数组 $_SERVER 是一个包含了诸如头信息(header).路径( ...

  4. android 布局之scrollview

    今天在布局页面的时候后犯了难,我要显示的内容一个页面展示不完,怎么办呢? 于是随便找了个app点开一看,哎呀原来还能翻动啊!这是啥布局呢?原来是ScrollView 官方api相关的内容全是英文,这可 ...

  5. 1121高性能MySQL之运行机制

    本文来自于拜读<高性能MySQL(第三版)>时的读书笔记作者:安明哲转载时请注明部分内容来自<高性能MySQL(第三版)> MySQL的逻辑构架 MySQL服务器逻辑架构 最上 ...

  6. 二叉树的建立与递归遍历C语言版

    </pre><pre name="code" class="cpp">#include <stdio.h> #include ...

  7. 分布式搜索引擎Elasticsearch PHP类封装 使用原生api

    //官方的 php  api写的鸡肋了,下面这个类可以使用 es api 操作. <?php class ElasticSearch { public $index; function __co ...

  8. tomcat报错

    错误日志如下: 十月 10, 2016 10:44:57 上午 org.apache.catalina.core.StandardWrapperValve invoke严重: Servlet.serv ...

  9. Dubbo系列_概述

    一.本文目的         学习使用Dubbo也有一段时间了,准备写一个系列文章介绍Dubbo的相关知识和使用,供自己以后回顾和他人学习.有兴趣的同学可以加入群:74085440一起探讨 二.书写计 ...

  10. Linux_RPM_查询

    一.查询是否安装 rpm  -q 包名 选项:     -q  查询(query) 查询所有已安装的RPM包 rpm -qa      -a  所有(all) 二.插叙软件包的详细信息 rpm -qi ...