用C来调用so
0x0 建立工程
- 建立Android.mk
配置好NDK环境后,直接使用ndk-build来快速生成原生程序。其中mk的配置如下
- LOCAL_PATH := $(call my-dir) //默认
- include $(CLEAR_VARS) //默认
- LOCAL_ARM_MODE := arm //arm表示使用32位指令系统
- LOCAL_MODULE := openso //生成的原生程序名
- LOCAL_SRC_FILES := openso.c //源文件名称
- include $(BUILD_EXECUTABLE)
// BUILD_EXECUTABLE表示生成可执行文件(无后缀)
// BUILD_SHARED_LIBRARY生成动态库.so文件
// BUILD_STATIC_LIBRARY生成静态库.a文件
2. 建立openso.c(注意跟mk里面命名一致)
记录几个比较容易错的地方,
- 因为是利用SO,我们一般是看IDA里面的伪C代码,
比如
这里为RC4流密码

这里的char v4;
__gnu_Unwind_2((int)&v1, 10, (int)&v4);
假如也像IDA这样用,假如真的是访问(&v4)这个地址的一个字节,是没问题的(但也没有初始化),但是这里并不是只访问一个字节的内容,还会访问接下来的内容,且会对接下来的内存做改变。这样写代码多半会引发Segmentation fault,有关这个错误,更多可访问Segmentation Fault in Linux。
所以需要自己分配内存,char * v4 = (char *)malloc(0x102);将&v4替换为v4;
- 大端小端
大端模式是指高字节数据MSB存放在低地址处,低字节数据放在高地址处。
小端模式是指低字节数据LSB存放在低地址处,高字节数据放在高地址处。
这里的key保存为
v1 = 0x89884546; [sp+4h]
v2 = 0x87879998; [sp+8h]
v3 = 0x8765u; [sp+Ch] //v3的地址最高
那么写程序时key以小端形式保存,就需要变为
unsigned char key[] = { 0x46,0x45,0x88,0x89,0x98,0x99,0x87,0x87,0x65,0x87 };
//数组入栈,从右往左压
具体这方面的知识参考,函数调用方式,CPU中的字节序
- dlsym的问题
void * dlsym(void * handle,char * symbol)
若查找的符号是个函数,那么就返回函数的地址;
若是个变量,它返回变量的地址;
如果这个符号是个常量,那么它返回的是该常量的值。
在IDA中需要看Exports表,才是真实的符号
也可以通过在linux环境下,objdump -T myELFfile 来查看符号表
- 有关于调用sub_开头的函数
这个似乎是没有导出符号的,具体原因暂时不知道,但是我们可以通过有符号的函数与它的偏移量来调用它
比如: .text : 00001234 exportfunc
而sub_1818为: .text :00001818 sub_1818
sub_1818 = exportfunc + 0x5E4
(大致就是这个意思)
3. ndk-build
- 将上述的两个文件,放到一个jni文件夹下,cd 到该目录,直接ndk-build,可自动识别,编译。
- 成功后会发现新出现两个目录libs, obj,所需要的文件就在libs下,这里自动生成了所有的平台的ELF文件,可以根据手机所支持的CPU架构来选择相应ELF
- 可以用adb shell getprop | grep cpu查看手机所支持的架构
4. 代码
a) 方案一 是运行解密函数,还需要自己找秘钥
b) 方案二 运行函数后,直接dump内存就行
方案1:
#include<stdio.h> #include<stdlib.h> #include<dlfcn.h> int main() { void *handle; char *error; int (*func2) (int, signed int, int); char * (*func3) (char *, int, int); char *v4 = malloc(0x102); unsigned char key[] = { 0x46,0x45,0x88,0x89,0x98,0x99,0x87,0x87,0x65,0x87 }; handle = dlopen("/data/local/tmp/runso.so", RTLD_LAZY); if ((error = dlerror()) != NULL) { printf("%s",error); printf("open so error!\n"); return -1; } func2 = dlsym(handle, "_Z14__gnu_Unwind_2PhiP5str_1"); if ((error = dlerror()) != NULL) { printf("open func2 error!\n"); return -1; } func2((int)key, 10, (int)v4); char * data = dlsym(handle, "__gnu_Unwind_15"); if ((error = dlerror()) != NULL) { printf("open data error!\n"); return -1; } func3 = dlsym(handle, "_Z14__gnu_Unwind_3PhiP5str_1"); if ((error = dlerror()) != NULL) { printf("open func3 error!\n"); return -1; } func3(data, 0x55BF, (int)v4); FILE * outfile = fopen("/data/local/tmp/out.jar", "wb"); if (outfile != NULL) { fwrite(data, 1, 0x55BF, outfile); } else { printf("can't create file"); return -1; } return 0; }
方案二:
#include<stdio.h> #include<dlfcn.h> int main() { void *handle; char *error; void *(*func)();//建立一个函数指针 void *(*sub_func)(); handle = dlopen("/data/local/tmp/runso.so", RTLD_LAZY); if ((error = dlerror()) != NULL) { printf("%s", error); printf("open so error!\n"); return -1; } func = dlsym(handle, "_Z15__gnu_Unwind_16v");//随意找了个函数,来定位sub开头的函数 if ((error = dlerror()) != NULL) { printf("%s", error); printf("open offset func error!\n"); return -1; } int offset = 0x160;//计算出偏移量 sub_func = func - offset; sub_func();//调用sub_1818 char * data = dlsym(handle, "__gnu_Unwind_15"); if ((error = dlerror()) != NULL) { printf("open data error!\n"); return -1; } FILE * outfile = fopen("/data/local/tmp/out.jar", "wb"); if (outfile != NULL) { fwrite(data, 1, 0x55BF, outfile); } else { printf("can't create file"); return -1; } return 0; }
浙公网安备 33010602011771号