背景
c++层,有时候提供的第三方so接口不满足需求,需要自定义调用so中的函数,大致分为:
- 调用未导出函数
- 调用导出函数
基础
获取so基地址
- 通过maps文件获取
/**
* 获取so文件基地址
* @param pid:pid module_name:soName
* @return 地址
* @invoke get_module_base(getpid(),"libtest.so")
**/
unsigned long get_module_base( pid_t pid, const char* module_name )
{
FILE *fp;
unsigned long addr = 0;
char *pch;
char filename[32];
char line[1024];
if ( pid < 0 )
{
/* self process */
snprintf( filename, sizeof(filename), "/proc/self/maps", pid );
}
else
{
snprintf( filename, sizeof(filename), "/proc/%d/maps", pid );
}
fp = fopen( filename, "r" );
if ( fp != NULL )
{
while ( fgets( line, sizeof(line), fp ) )
{
if ( strstr( line, module_name ) )
{
pch = strtok( line, "-" );
addr = strtoul( pch, NULL, 16 );
break;
}
}
fclose( fp ) ;
}
return addr;
}
- 通过获取so中某个导出函数,然后减去其偏移地址获取
unsigned long get_module_base_by_fun(char* soPath,char* funName){
auto handler = dlopen(soPath,RTLD_LAZY);
auto fun_addr = dlsym(handler,funName);
// 注意地址的 +1 -1
// 0xAAAA为传进来funName在so中的偏移地址
auto base = (unsigned long)fun_addr - 0xAAAA ;
return base;
}
调用未导出函数
// 0xE9E78为init函数的偏移地址,注意+1-1
auto init = (unsigned long) base + 0xE9E78 + 1;
/**方式一**/
// 获取函数地址指针
auto init_sym = (int (*)(char *, char *, int)) init;
//调用函数
int result = init_sym(p1, p2, 0);
/**方式二**/
// 获取函数地址指针
auto init_sym = reinterpret_cast<int (*)(char*,char*,int)>(init);
//调用函数
int result = reinterpret_cast<int>((*init_sym)(p1, p2, 0));
调用导出函数
// 地址获取
auto recognize = reinterpret_cast<int (*)(JNIEnv*,jclass,jobject)>(dlsym(handler,"nativeRecognize"));
// 函数调用
auto imgdata = reinterpret_cast<jbyteArray>((*recognize)(env, clazz, bitmap));