線程的調(diào)用就會有函數(shù)的調(diào)用,就會把調(diào)用地址壓入棧中断部,所以就可以從棧中獲取調(diào)用地址猎贴。
通過[NSThread callStackReturnAddresses]
,可以獲取線程的調(diào)用地址:
#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 表示線程被壓入棧中的地址的數(shù)組,
address[0]
:表示最頂層的線程調(diào)用地址她渴,就是當(dāng)前執(zhí)行地點(diǎn)的函數(shù)的初始地址达址,這里
info.dli_fbase
表示當(dāng)前執(zhí)行文件的基地址,
info.dli_saddr
表示當(dāng)前執(zhí)行函數(shù)的函數(shù)地址趁耗。
例如:
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
是執(zhí)行文件DrawSomething的基地址沉唠,
0x103ebdc70
是函數(shù)void otherFunc()的函數(shù)地址,
0x103ebdeb0
是函數(shù)NSString * getSomeFunc()的函數(shù)地址苛败,
0x103ebe460
是方法(本質(zhì)還是函數(shù))- (void)viewDidLoad
的函數(shù)地址满葛,
0x105e83b83
是UIKit調(diào)用viewDidLoad的調(diào)用地址。
如果一個(gè)執(zhí)行文件未發(fā)生改變著拭,那么其中的函數(shù)所對應(yīng)的偏移地址是不變的纱扭,那么就可以根據(jù) 偏移地址 + 基地址 來獲取函數(shù)調(diào)用地址牍帚,從而調(diào)用函數(shù)儡遮。
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)();
當(dāng)你知道別人執(zhí)行文件的某個(gè)函數(shù)的偏移地址和基地址,你就可以調(diào)用別人的函數(shù)了暗赶。