dlopen 和 dlsym 動態調用函數
iOS/MacOSX/移動安全 exchen 2年前 (2018-08-20) 2997瀏覽 0評論
Linux/unix 提供了使用 dlopen 和 dlsym 方法動態加載庫和調用函數,這套方法在 macOS 和 iOS 上也支持。
dlopen 打開一個庫,獲取句柄。
dlsym 在打開的庫中查找符號的值。
dlclose 關閉句柄。
dlerror 返回一個描述最後一次調用dlopen、dlsym,或 dlclose 的錯誤信息的字符串。
動態調用 printf 函數,編寫測試代碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#import <dlfcn.h>
typedef int (*printf_func_pointer) (const char * __restrict, ...);
void dynamic_call_function(){
//動態庫路徑 char *dylib_path = "/usr/lib/libSystem.dylib";
//打開動態庫 void *handle = dlopen(dylib_path, RTLD_GLOBAL | RTLD_NOW); if (handle == NULL) { //打開動態庫出錯 fprintf(stderr, "%s\n", dlerror()); } else {
//獲取 printf 地址 printf_func_pointer printf_func = dlsym(handle, "printf");
//地址獲取成功則調用 if (printf_func) { int num = 100; printf_func("Hello exchen.net %d\n", num); printf_func("printf function address 0x%lx\n", printf_func); }
dlclose(handle); //關閉句柄 } }
int main(int argc, char * argv[]) { @autoreleasepool {
dynamic_call_function(); return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); } } |
在手機上運行的輸出結果如下:
1 2 |
Hello exchen.net 100 printf function address 0x189f0da78 |
轉載請註明:exchen's blog » [iOS Hacker] dlopen 和 dlsym 動態調用函數
補充dlsym():
獲取新符號
進程可以使用 dlsym(3C) 獲取特定符號的地址。此函數採用句柄和符號名稱,並將符號地址返回給調用方。該句柄通過以下方式指示符號搜索:
-
可通過指定目標文件的 dlopen(3C) 返回句柄。該句柄允許從指定目標文件及定義其依賴項樹的目標文件獲取符號。使用模式 RTLD_FIRST 返回的句柄僅允許從指定目標文件獲取符號。
-
可通過其值爲 0 的路徑名的 dlopen(3C) 返回句柄。該句柄允許從關聯鏈接映射的啓動目標文件及定義其依賴項樹的目標文件獲取符號。通常,啓動目標文件爲動態可執行文件。對於關聯鏈接映射,該句柄還允許從通過 dlopen(3C) 獲取且模式爲 RTLD_GLOBAL 的任何目標文件獲取符號。使用模式 RTLD_FIRST 返回的句柄僅允許從關聯鏈接映射的啓動目標文件獲取符號。
-
特殊句柄 RTLD_DEFAULT 和 RTLD_PROBE 允許從關聯鏈接映射的啓動目標文件及定義其依賴項樹的目標文件獲取符號。此句柄還允許從通過 dlopen(3C) 獲取且與調用方同屬一組的任何目標文件獲取符號。 使用 RTLD_DEFAULT 或 RTLD_PROBE 時採用在解析調用目標文件中的符號重定位時所用的同一模型。
在以下可能很常見的示例中,應用程序首先會將其他目標文件添加到其地址空間。然後,應用程序會使用 dlsym(3C) 來查找函數或數據符號。接下來,應用程序將使用這些符號來調用這些新目標文件中提供的服務。文件 main.c 包含以下代碼:
#include <stdio.h> #include <dlfcn.h> main() { void * handle; int * dptr, (* fptr)(); if ((handle = dlopen("foo.so.1", RTLD_LAZY)) == NULL) { (void) printf("dlopen: %s\n", dlerror()); exit (1); } if (((fptr = (int (*)())dlsym(handle, "foo")) == NULL) || ((dptr = (int *)dlsym(handle, "bar")) == NULL)) { (void) printf("dlsym: %s\n", dlerror()); exit (1); } return ((*fptr)(*dptr)); } |
首先會在文件 foo.so.1 中搜索符號 foo 和 bar,然後在與此文件關聯的所有依賴項中搜索。接下來,在 return() 語句中使用單個參數 bar 調用函數 foo。
使用前面的文件 main.c 生成的應用程序 prog 包含下列依賴項。