关于C++中虚函数与普通函数的区别,大家可以参考:《深度探索C++对象模型》。
对于Hook普通的C++函数,与Hook C函数没什么太大区别,主要的区别在于C++有名称改编,原理是在函数入口加入跳转指令。
这块儿在Windows平台上有微软的Detours,也有开源的EasyHook。在Mac平台上有MobileSubstrate,相信做过越狱开发的对这个都比较熟悉。
所有上面列到的工具在Hook C++普通函数时都需要修改Segment __TEXT,而这个段在iOS平台上只读的,具体权限为:RX。因此在非越狱的设备上没法修改函数入口,也就无法Hook。
但是我们是可以Hook Objective-C函数的,虽然Objective-C的运行时提供了一些方法,但是主要的原因在于:针对OC的方法调用需要查询一个数据映射表,将消息转换为真正的C函数调用,而这个映射表是放在可修改的数据段中的。
这点与C++的虚函数有几分类似,下面一起分析下看看是否有可能Hook C++的虚函数。
Demo程序如下:
// // main.cpp // VTLocation // // Created by Proteas on 14-8-12. // Copyright (c) 2014年 Proteas. All rights reserved. // #include <stdio.h> class Base { public: virtual ~Base() {}; public: virtual void sayHello() { printf("hello from base\r"); }; }; class Child : public Base { public: virtual ~Child() {}; public: virtual void sayHello() { printf("hello from child\r"); }; }; int main (int argc, const char * argv[]) { Child *p = new Child; p->sayHello(); delete p; return 0; }
export DEVELOPER_DIR := $(shell xcode-select --print-path) SDK_VER_IOS=7.1 SDK_IOS_DEVICE="$(DEVELOPER_DIR)/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS$(SDK_VER_IOS).sdk" MIN_VER_IOS=-miphoneos-version-min=5.0 ARCH_IOS=-arch armv7 CC_IOS=xcrun -sdk "iphoneos" gcc all: main.cpp $(CC_IOS) -o main main.cpp $(ARCH_IOS) $(MIN_VER_IOS) -isysroot $(SDK_IOS_DEVICE) -I$(SDK_IOS_DEVICE)/usr/include -I. -lc++ -lstdc++ ldid -S main clean: rm -f main
在得到可执行程序后,我们首先要找到虚函数表所在的区段,因此使用IDA 加载 main 查找虚函数表。
然后我们选择数据段:
因为Demo程序很简单,跳转到数据段后,直接就看到了虚函数表:
直接可以看到虚函数表的各个表项,这里需要注意的是:非纯虚函数还是发生了名称改编。没验证纯虚函数,不过印象中纯虚函数是没有名称改编的,因为微软的COM依赖这一特性。
下面我们使用MachOView加载可执行程序,查看0xC060所载区段的属性。
因为0xC060正好是区段开始的地方,所以正好命中,下面看下这个包含这个Section的Segment的属性:
可以看到这个Segment的属性为:RW,也就是可以改的,因此是可Hook的。
同时基于这个理论:C++的多态特性是运行时特性,运行时本身也是需要修改的,因此也可得到其是可Hook的。