[原创]linux下调用共享库非导出函数
在逆向共享库(.so)时,有时候需要逐一的验证函数的正确性,需要单独调用共享库中的函数(解决该问题,可以更方便的验证逆向结果),对于导出函数,没什么可说,使用dlopen、dlsym等,可以直接调用,那么非导出函数怎么调用呢?
解决该问题,可以从两方面入手:
1.修改.so文件中各个函数的属性,把所有函数全部导出
2.尝试获取到非导出函数在虚拟内存中的地址,使用函数地址调用该函数
由于.so可能面临加密等问题,我们暂时不讨论第一种方法,本文先介绍第二种方法。导出函数和非导出函数的差别不外乎就是函数的visibility属性,直观上,我们可以获取到库的基址和导出函数的相对地址,这是我们的已知信息。
先做一下准备工作,1.建库---2.获取地址信息
共享库:share
#include <stdio.h>
///print大写的ABCD
void PrintABCD()//__attribute__((visibility("hidden")))
{
printf("ABCD\n");
sleep(-1);
}
///print小写的abcd
__attribute__((visibility("hidden"))) void Printabcd()//
{
printf("abcd\n");
sleep(-1);
}
///根据参数来确定print ABCD or abcd
void PrintIt(int isPrintCapital)
{
if(isPrintCapital)
{
PrintABCD();
}
else
{
Printabcd();
}
printf("over!\n");
}
生成共享库 share.so
gl-linux@ubuntu:~/Desktop/AYR$ gcc -fPIC -shared -o share.so share.c
测试代码:test.c
#include <stdio.h>
#include <dlfcn.h>
#include<unistd.h>
#define DLL_FILE_NAME "/home/gl-linux/Desktop/AYR/share.so"
int main()
{
long long addr = 0;
void (*func)(int);
void *handle = dlopen(DLL_FILE_NAME, RTLD_NOW);
if (handle == NULL)
{
fprintf(stderr, "Failed to open libaray %s error:%s\n", DLL_FILE_NAME, dlerror());
return -1;
}
addr = (long long)dlsym(handle, "PrintABCD");
printf("%lld ",addr);
addr = (long long)dlsym(handle, "Printabcd");
printf("%lld ",addr);
addr = (long long)dlsym(handle, "PrintIt");
printf("%lld ",addr);
func = addr;
func(1);
dlclose(handle);
sleep(-1);
return 0;
}
gl-linux@ubuntu:~/Desktop/AYR$ gcc test.c -o test -ldl
运行可执行文件
gl-linux@ubuntu:~/Desktop/AYR$ ./test
139725025953621 0 139725025953687 ABCD
可以看到,三个函数的地址中,有一个是0,这个就是内部函数,后者我们称之为非导出函数
查看导出函数偏移:
gl-linux@ubuntu:~/Desktop/AYR$ nm -D share.so
0000000000201048 B __bss_start
w __cxa_finalize
0000000000201048 D _edata
0000000000201050 B _end
00000000000007cc T _fini
w __gmon_start__
00000000000005e8 T _init
w _ITM_deregisterTMCloneTable
w _ITM_registerTMCloneTable
w _Jv_RegisterClasses
0000000000000755 T PrintABCD
0000000000000797 T PrintIt
U puts
U sleep
gl-linux@ubuntu:~/Desktop/
已知导出函数的偏移地址,又知道模块基址,怎么能知道非导出函数的地址呢?
这时候我们需要借助IDA工具查看文件在内存中的情况(二进制指令是不变的)
PrintIt(int)入口地址片段
.text:0000000000000797 ; Attributes: bp-based frame
Printabcd()入口地址片段
.text:0000000000000776 ; Attributes: bp-based frame
PrintABCD()入口地址片段
.text:0000000000000755 ; Attributes: bp-based frame
通过IDA的显示,可以知道:非导出函数Printabcd()和导出函数PrintIt(int)地址偏移33,139725025953687 - 33即是非导出函数Printabcd()的虚拟地址,修改测试程序如下:
使用计算后的地址调用非导出函数Printabcd()
#include <stdio.h>
#include <dlfcn.h>
#include<unistd.h>
#define DLL_FILE_NAME "/home/gl-linux/Desktop/AYR/share.so"
int main()
{
long long addr = 0;
void (*func)();
void *handle = dlopen(DLL_FILE_NAME, RTLD_NOW);
if (handle == NULL)
{
fprintf(stderr, "Failed to open libaray %s error:%s\n", DLL_FILE_NAME, dlerror());
return -1;
}
addr = (long long)dlsym(handle, "PrintABCD");
printf("%lld ",addr);
addr = (long long)dlsym(handle, "Printabcd");
printf("%lld ",addr);
addr = (long long)dlsym(handle, "PrintIt");
printf("%lld ",addr);
func = addr-33;
func();
dlclose(handle);
sleep(-1);
return 0;
}
运行结果:
gl-linux@ubuntu:~/Desktop/AYR$ ./TEST
139670664996693 0 139670664996759 abcd
