线程的调用就会有函数的调用,就会把调用地址压入栈中,所以就可以从栈中获取调用地址。
通过[NSThread callStackReturnAddresses]
,可以获取线程的调用地址:
#include <dlfcn.h>
NSArray *address = [NSThread callStackReturnAddresses];
Dl_info info = {0};
dladdr((void *)[address[0] longLongValue], &info);
printf("address-0 name:%s adress:%p nearAdress:%p\n", info.dli_fname, info.dli_fbase, info.dli_saddr);
dladdr((void *)[address[1] longLongValue], &info);
printf("address-1 name:%s adress:%p nearAdress:%p\n", info.dli_fname, info.dli_fbase, info.dli_saddr);
......
address 表示线程被压入栈中的地址的数组,
address[0]
:表示最顶层的线程调用地址,就是当前执行地点的函数的初始地址,这里
info.dli_fbase
表示当前执行文件的基地址,
info.dli_saddr
表示当前执行函数的函数地址。
例如:
void otherFunc() {
NSArray *address = [NSThread callStackReturnAddresses];
Dl_info info = {0};
dladdr((void *)[address[0] longLongValue], &info);
printf("address-0 name:%s adress:%p nearAdress:%p\n", info.dli_fname, info.dli_fbase, info.dli_saddr);
dladdr((void *)[address[1] longLongValue], &info);
printf("address-1 name:%s adress:%p nearAdress:%p\n", info.dli_fname, info.dli_fbase, info.dli_saddr);
dladdr((void *)[address[2] longLongValue], &info);
printf("address-2 name:%s adress:%p nearAdress:%p\n", info.dli_fname, info.dli_fbase, info.dli_saddr);
if (dladdr((void *)[address[3] longLongValue], &info) != 0) {
printf("address-3 name:%s adress:%p nearAdress:%p\n", info.dli_fname, info.dli_fbase, info.dli_saddr);
}
}
NSString * getSomeFunc()
{
otherFunc();
NSLog(@"getSomeFunc");
return @"12";
}
- (void)viewDidLoad {
[super viewDidLoad];
getSomeFunc();
}
控制台输出:
po (void *)getSomeFunc
0x0000000103ebdeb0
address-0 name:/Users/.../Library/Developer/CoreSimulator/Devices/30C9CE64-9A7C-47D6-BFBC-8C86195F9CAA/data/Containers/Bundle/Application/AC3A1321-292A-4212-8AC5-0EBEBE8A53C7/DrawSomething.app/DrawSomething adress:0x103ebb000 nearAdress:0x103ebdc70
address-1 name:/Users/.../Library/Developer/CoreSimulator/Devices/30C9CE64-9A7C-47D6-BFBC-8C86195F9CAA/data/Containers/Bundle/Application/AC3A1321-292A-4212-8AC5-0EBEBE8A53C7/DrawSomething.app/DrawSomething adress:0x103ebb000 nearAdress:0x103ebdeb0
address-2 name:/Users/.../Library/Developer/CoreSimulator/Devices/30C9CE64-9A7C-47D6-BFBC-8C86195F9CAA/data/Containers/Bundle/Application/AC3A1321-292A-4212-8AC5-0EBEBE8A53C7/DrawSomething.app/DrawSomething adress:0x103ebb000 nearAdress:0x103ebe460
address-3 name:/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 10.0.simruntime/Contents/Resources/RuntimeRoot/System/Library/Frameworks/UIKit.framework/UIKit adress:0x105cbb000 nearAdress:0x105e83b83
2018-12-18 13:45:45.293 DrawSomething[2432:113183] getSomeFunc
上面的输出很清楚的表明了:
0x103ebb000
是执行文件DrawSomething的基地址,
0x103ebdc70
是函数void otherFunc()的函数地址,
0x103ebdeb0
是函数NSString * getSomeFunc()的函数地址,
0x103ebe460
是方法(本质还是函数)- (void)viewDidLoad
的函数地址,
0x105e83b83
是UIKit调用viewDidLoad的调用地址。
如果一个执行文件未发生改变,那么其中的函数所对应的偏移地址是不变的,那么就可以根据 偏移地址 + 基地址 来获取函数调用地址,从而调用函数。
long long funcBasePoint = (long long)((void *)getSomeFunc);
NSArray *address = [NSThread callStackReturnAddresses];
Dl_info info = {0};
dladdr((void *)[address[0] longLongValue], &info);
long long basePoint = (long long)(info.dli_fbase);
long offsetPoint = funcBasePoint - basePoint;
void* addr = (void*)(offsetPoint + basePoint);
((void(*)(void))addr)();
当你知道别人执行文件的某个函数的偏移地址和基地址,你就可以调用别人的函数了。