线程、函数的调用地址

线程的调用就会有函数的调用,就会把调用地址压入栈中,所以就可以从栈中获取调用地址。

通过[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的函数地址,
0x105e83b83UIKit调用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)();

当你知道别人执行文件的某个函数的偏移地址和基地址,你就可以调用别人的函数了。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容