一、简介: dlsym根据动态链接库操作句柄与符号,返回符号对应的地址。使用这个函数不但可以获取函数地址,也可以获取变量地址。 二、使用 void (*test_funcp)() dlhandle = dlopen(“testlib.so”, flag) test_funcp = dlsym(dlhandle,”testfunc
dlsym dlsym,dlvsym – 从一个动态链接库或者可执行文件中获取到符号地址。 用法 #include <dlfcn.h> void *dlsym(void *handle, const char *symbol); #define _GNU_SOURCE #include <dlfcn.h 详解 函数dlsym()的第一个参数是一个指向已经加载的动态目标的句柄,这个句柄可以是dlopen()函数返回的。 其中symbol参数是一个以null结尾的符号名。 如果这个符号 在指定的目标 或者 在由dlopen(3)装载指定的目标时自动装载的其他共享目标中都没有找到,dlsym()返回NULL指针。(dlsym在这些动态目标中执行广度优先搜索)。 所以,必须通过dlerror(3)函数以清理掉之前的错误状态,然后调用dlsym(),最后调用dlerror(3),然后将其返回值保存到一个变量,最后检查是否这个保存的变量值不为NULL。
2.2 重写 dlsym HAMi-core 中重写了 dlsym 函数,以劫持 NVIDIA 动态链接库(如 CUDA 和 NVML)的调用,特别是针对以 cu 和 nvml 开头的函数进行拦截。 ("into dlsym %s",symbol); pthread_once(&dlsym_init_flag,init_dlsym); // 先初始化dlsym(可以理解为CUDA Driver ) if (real_dlsym == NULL) { real_dlsym = dlvsym(RTLD_NEXT,"dlsym","GLIBC_2.2.5"); LOG_ERROR("real dlsym not found"); real_dlsym = _dl_sym(RTLD_NEXT, "dlsym", dlsym); 2.4 nvml 前缀处理 __dlsym_hook_section_nvml 定义了对于 nvml 开头的符号,具体处理如下: void* __dlsym_hook_section_nvml(void
文章目录 一、dlsym 函数简介 二、获取 目标进程 linker 中的 dlsym 函数地址 三、远程调用 目标进程 linker 中的 dlsym 函数 获取 注入的 libbridge.so 动态库中的 load 函数地址 四、远程调用 目标进程 中的 libbridge.so 动态库中的 load 函数 一、dlsym 函数简介 ---- dlsym 是 Dynamic Library Symbol 该函数的作用是 根据 动态链接库 句柄 和 符号 , 返回对应 符号的地址 , 这个符号可以是方法名 , 也可以是变量名 ; 包含头文件 : #include<dlfcn.h> 函数原型 : void* dlsym 动态链接库 的返回值; ② constchar* symbol : 函数名称 / 全局变量名称 ; void* 返回值 : 返回对应 函数 / 变量 地址 ; 二、获取 目标进程 linker 中的 dlsym 本地进程 函数地址 ; ⑤ 根据 本地进程 函数地址 + 本地进程 与 远程进程 的 动态库 地址 偏移量 , 计算出 远程进程 动态库 的 函数地址 ; 三、远程调用 目标进程 linker 中的 dlsym
2.1、dlsym()函数获取共享对象或可执行文件中符号的地址。 如果在指定对象或加载对象时dlopen()自动加载的任何共享对象中找不到该符号,dlsym()将返回NULL。(dlsym()执行的搜索是通过这些共享对象的依赖关系树进行的广度优先搜索。) 由于符号的值实际上可能是NULL(因此,dlsym()的NULL返回值不必指示错误),因此测试错误的正确方法是调用dlerror()以清除任何旧的错误条件,然后调用dlsym。 函数dlvsym()的作用与dlsym()相同,但使用版本字符串作为附加参数。返回值:成功时,这些函数返回与符号关联的地址。失败时,返回NULL;可以使用dlerror()诊断错误的原因。 hook使用:(1)定义与目标函数一样的类型;(2)具体函数实现,函数名与目标函数名一致;(3)调用dlsym()函数,初始化hook。死锁检测可以使用图算法,通过检测有向图是否有环判断是否有死锁。
这些函数包括: evaluate_dlsym: 这个函数模拟了 dlsym 函数的实现,它接受一个 EvalContext 实例和 dlsym 函数的参数,通过获取共享对象中的符号地址来模拟 dlsym Dlsym 是一个枚举类型,定义了多个与 dlsym 函数相关的变体,包括: Dlsym:表示正常的 dlsym 函数调用。 Symbol:表示对共享对象中特定符号的引用。 EvalContextExt trait中定义了与dlsym相关的方法,用于模拟dlsym函数的行为。这些方法包括dlsym、dlsym_with_handle和get_foreign_fn等。 通过实现这些方法,可以在Mirai解释器中执行dlsym的功能。 Dlsym enum:这个enum定义了dlsym函数中可能的错误情况。 register_with_dlsym:将符号名称及其对应的函数指针注册到dlsym的模拟执行环境中。 Dlsym enum: 该enum定义了使用dlsym函数时可能出现的不同情况。
(xl->fops = dlsym (handle, "fops"))) { gf_log ("xlator", GF_LOG_WARNING, "dlsym(fops) (xl->cbks = dlsym (handle, "cbks"))) { gf_log ("xlator", GF_LOG_WARNING, "dlsym(cbks) , "dlsym(init) on %s", dlerror ()); goto out; "dlsym(dumpops) on %s -- neglecting", dlerror ()); } if (! (xl->fops = dlsym (handle, "fops"))) { gf_log ("xlator", GF_LOG_WARNING, "dlsym(fops)
uv_dlerror(lib)); continue; } init_plugin_function init_plugin; if (uv_dlsym (lib, "initialize", (void **) &init_plugin)) { fprintf(stderr, "dlsym error: %s\n", uv_dlerror dlclose(lib->handle); lib->handle = NULL; } } // 获取句柄指定方法 注意第三个参数 修改指传入ptr指针所指向 函数指针 int uv_dlsym (uv_lib_t* lib, const char* name, void** ptr) { dlerror(); /* Reset error status. */ *ptr = dlsym
." << endl; typedef void (*hi_t)(); // 错误 dlerror(); hi_t hi = (hi_t) dlsym(handle, "hi"); const char *dlsym_error = dlerror(); if (dlsym_error) { cerr << "Cannot load symbol 'hi': " << dlsym_error << endl; dlclose(handle); return 1; } cout << "Calling ..." << endl; typedef int (*add_t)(int, int); // 错误 dlerror(); add_t add = (add_t) dlsym (handle, "add"); if (dlsym_error) { cerr << "Cannot load symbol 'add': " << dlsym_error <
Native层的so库动态加载的实现 在Native层的C/C++代码环境,so库动态加载是使用dlopen()、dlsym()和dlclose()这三个函数实现的。 dlopen函数的使用需要兼容C++ dlopen、dlclose、dlsym函数是C语言库里面的函数,自身是没有考虑到C++的支持的,调用dlopen无法直接加载C++的类及其成员函数。 这是因为C语言直接把函数名当做符号名,dlsym直接用符号名就能加载相对应的目标库内的函数,但是由于C++有类和类成员函数的概念,符号名的生成采用了”name managing”的方式,把函数名、类定义 (*libHandler, "create_SubClass"); const char* dlsym_error = dlerror(); if (dlsym_error) { "destroy_SubClass"); const char* dlsym_error = dlerror(); if (dlsym_error) { return ERROR
在Linux系统中,动态加载库其实很容易,只要用两个API 就可以了,即 dlopen 和 dlsym 。 (void* handle, const char* symbol); 其中,dlopen 用于将动态库加载到内存中;dlsym 用于查找被加载到内存中的动态库的函数或变量的地址。 接下来我们再来看看如何使用 dlopen 及 dlsym 将上面生成的库动态库加载到内存中,并调用它的 add() 方法吧。 \n"); return -1; } FUNC func = (FUNC)dlsym(handler, "add"); int r = func(10, 20); printf create_p *create = (create_p*) dlsym(plugin, "create"); ......
) calling dlsym(Java_com_sina_weibo_sdk_net_HttpManager_calcOauthSignNative__Landroid_content_Context (%s)", mangleCM); func = dlsym(pLib->handle, mangleCM); if (func == NULL) { mangleSig (%s)", mangleCMSig); func = dlsym(pLib->handle, mangleCMSig); if (func ! = NULL) { ALOGV("Found '%s' with dlsym", mangleCMSig); } } else { ALOGV ("Found '%s' with dlsym", mangleCM); } 所以,这里我们看到了,默认的函数名映射的规则是:Java_you_pakcage_ClassName_MethodName
|--|--|--|--SharedLibrary.FindSymbol; |--|--|--|--|--|--SharedLibrary.FindSymbolWithoutNativeBridge(dlsym dlopen、dlsym 使用dlopen,dlsym调用JNI_OnLoad方法; dlopen以指定模式打开指定的动态连接库文件,并返回一个句柄给调用进程; dlerror返回出现的错误; dlsym dlopen ( const char *file, int mode ); DLFCN_EXPORT int dlclose(void *handle); DLFCN_EXPORT void *dlsym
这里对该函数进行封装 , 是因为在 Android 7.0 之后 , 这些函数都受 命名空间限制 , 对于某些函数库可能执行失败 ; 然后 , 调用 hook\dlfcn\dlfcn_compat.h 中的 dlsym_compat 函数 在 " libc.so " 函数库 中的地址 , 第一个参数是 " libc.so " 函数库的基地址 , 第二个参数是函数名称即 " execve " , 该函数的原型如下 : void *dlsym_compat (void *handle, const char *symbol); 该操作也可以使用 dlsym 函数 , 这里对该函数进行封装 , 是因为在 Android 7.0 之后 , 这些函数都受 命名空间 = dlopen_compat("libc.so", RTLD_NOW); // 查找 execve 在 libc.so 函数库的偏移地址 void *execve_addr = dlsym_compat
注入过程如下: 0x01 获取目标进程的pid,关联目标进程; 0x02 获取并保存目标进程寄存器值; 0x03 获取目标进程的dlopen,dlsym 0x04 获取并保存目标进程的堆栈,设置dlopen函数的相关参数,将要注入的SO的绝对路径压栈; 0x05 调用dlopen函数; 0x06 调用dlsym 0x03 获取目标进程的dlopen,dlsym函数的绝对地址: 大概思路是这样的:首先通过遍历/proc/pid/maps文件分别得到本进程中dlopen函数所在动态库的基地址local_module_base dlsym同理,不再详述。 dlopen_addr为0x03获取到的目标进程dlopen函数的绝对地址,ARM_lr = 0的目的在于当目标进程执行完dlopen函数,使目标进程发生异常,从而让本进程重新获得控制权) 0x06 调用dlsym
你丫说的不会是dlsym()和dlopen()这几个函数吧?他说:是的。[阴险][阴险] 我说:NB,会包装就是不一样,插件式!你小子离征服世界不远了! dlopen()和dlsym(),dlopen用来指定动态库,并使用dlsym来获取动态库里面的某一函数(比如hava_meal),这样一来,只要传给main的参数argv[1]不同,就可以获取和链接不同的动态库
System.loadLibrary 从而绕过Android N的classloader-namespace限制,将系统/system/lib中任意so库加载到maps中,然后再通过fake dlopen的方式去dlsym iOS 虽然ios可以直接使用dlopen,但是审核上会有风险,苹果有可能会对提交AppStore的app扫描相关dlopen/dlsym等调用,来判断是否存在一些敏感的私有调用。 为了在通过调用一些私有接口的时候避免被苹果检测到,byOpen也通过自己实现dlopen/dlsym直接从已经加载进来的images列表里面直接查找对应symbol地址来调用。 by_pointer_t handle = by_dlopen("libcurl.so", BY_RTLD_LAZY); if (handle) { by_pointer_t addr = by_dlsym
使用时链接 为了使用这种方式,需要使用几个函数dlopen,dlsym,dlclose,dlerror,其原型分别如下: #include <dlfcn.h> void *dlopen(const char *filename, int flags); void *dlsym(void *handle, const char *symbol); int dlclose(void *handle); char dlsym函数用于从动态库中查找需要使用的函数; dlclose函数用于卸载已加载的动态库; dlerror函数用于打印动态库相关错误。 printf("open error:%s\n",error); return -1; } /*返回类型为函数指针*/ void (*fun)() = dlsym (handle); printf("end to call test\n"); return ; } 这种方式的动态库使用可以大致分为以下几个步骤: 使用dlopen打开动态库 使用dlsym
remote[0xb770f000], ret_addr[0xb77101f0], local_addr[0xb77231f0] [+] Get imports: dlopen: 0xb7710450, dlsym in target process. [+] Target process returned from dlopen, return value=b2c287b4, pc=0 [+] Calling dlsym in target process. [+] Target process returned from dlsym, return value=a36044e0, pc=0 hook_entry_addr
一、C++如何实现so动态加载 C++框架实现so的动态加载比较简单,通过dlopen得到加载的so的句柄(void *),dlsym获得函数地址。 一般为: 框架中维护一个so到句柄的map,dlopen,dlsym成功后,将新的句柄替换至map; 新的流量请求通过map转发到新的so函数; 待老的so没流量之后,将老的so卸载(dlclose)即可完成 libproxy.so中会维护一个map, key为Java框架中传入的String,value为包含dlopen返回的句柄,dlsym拿到的函数地址以及相关的上下文信息。 JavaServer会监听so目录,但so发生变更后,JavaServer通过JNI调用libproxy.so的reload方法,并将该so对应的key及路径传入; libproxy.so完成dlopen及dlsym