申请会员ID:约你一起偷西瓜【未报到,已注销】
申请标题:申请会员ID:约你一起偷西瓜1、申 请 I D:约你一起偷西瓜
2、个人邮箱:axhlzy@live.cn
3、原创技术文章:基于inlinehook免重打包实现持久化NativeHook
#### 两个主角函数:
- (https://cs.android.com/android/platform/superproject/+/master:external/libxml2/os400/dlfcn/dlfcn.c;l=1156?q=dlopen)
- 功能是以指定模式打开指定的动态链接库文件
- 返回一个句柄给(https://baike.baidu.com/item/dlsym/6603915)的调用进程
- 使用(https://baike.baidu.com/item/dlclose/9066991)来卸载打开的库
- (https://cs.android.com/android/platform/superproject/+/master:external/libxml2/os400/dlfcn/dlfcn.c;l=986;drc=master?q=dlsym&sq=&ss=android%2Fplatform%2Fsuperproject)
- 根据动态链接库操作句柄与符号,返回符号对应的地址
- 获取变量地址
#### 目的:
通过这两个函数,以及inlinehook我们可以实现root下不修改smali,不重打包完成持久化native hook
#### 实现原理(arm32):
众所周知,System.loadLibrary()可以根据名称加载/data/app/com.xxx/lib/arm/目录下的so,而inlinehook也是通过加载so来动态修改原来的汇编代码,正常使用的话我们考虑修改smali代码加上一句System.loadLibrary()来实现自定义so的加载,但是我们不正常!所以我们想着能不能不修改smali完成加载
我们使用inlinehook编译一个so并把名称改为他原来的so的名称,把他原来的so改个名字,由我们编译的so来加载,嗯 。。。这就是本文的大概原理
#### 需要处理的问题:
(这里涉及了两个so,统一一下名称便于后文描述,原app的so称为 '**原so**' ,我们用inlinehook生成的so称为 '**新so**' )
1.替换以后我们需要知道由系统调用的dlopen获得的 **新so** 的handle
2.替换以后原本的函数不管是静态注册还是动态注册的必然在新so里面是找不到的
#### 解决办法:
1.最开始想尝试使用frida去实践一下想法
- ①hook android_dlopen_ext
- ②第一个参数路径包含 **新so**,记录handle
- ③第一个参数路径包含 **原so**,拦截dlsym()替换第一个参数为上handle
发现死活不行,想到可能是frida性能问题(欢迎大佬给我普及一下为什么)
所以又想着用inlinehook去实现
但是系统连新so都还没加载进去,谈何hook,emmm
最后想着我在调用一次dlopen加载新so不就行了么……
2.我们需要稍微了解一下他为什么调用不起来
![调用细节看图](https://upload-images.jianshu.io/upload_images/9548468-1b894c73246118c1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
其实注意的就是刚才说的dlopen返回的handle,dlsym传递的两个参数,第一个就是dlopen返回的handle,第二个是我们需要调用的函数符号
这个时候使用inlinehook去hook一下这两个函数,在手动加载 **原so** 后,触发一下原so的Jni_onload(),再启动inlinehook当dlsym第一个参数是 **新so** 的dlopen handle时就替换为 **原so** 的handle
#### 举例
我们这里就用最右(cn.xiaochuangkeji.tieba)来举例
![替换以前](https://upload-images.jianshu.io/upload_images/9548468-47aabbf73cc26108.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
##### 操作步骤
- 把原来的libmarsxlog.so改名为libmarsxlogcp.so,
- 然后编译出so,修改名称为libmarsxlog.so,并移动到该目录下
- 启动查看日志
inlinehook替换函数前8字节(2条arm指令或者4条thumb指令)为跳转,而frida hook在这里是enter时候的参数自然是原来的,所以看起来是用了错误的handle加载function还不崩掉
![替换以后](https://upload-images.jianshu.io/upload_images/9548468-b71891c01c86bc94.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
这里这个手机没有开全局调试只有将就用frida hook看一下打印日志
```
function hookLog() {
var isFirst = true
Interceptor.attach(Module.findExportByName("liblog.so","__android_log_print"), {
onEnter: function (args) {
if(isFirst) {
console.log("\n")
isFirst = false
}
if(args.readCString().indexOf("ZZZ")!=-1)
console.log(args.readCString()+"\t"+args.readCString()+"\t"+args+"\t"+args+"\t"+args)
}
});
}
```
这里__android_log_print是变长参数为了能展示全部参数,只好多填几个参数,各自眼神忽略把
![替换参数](https://upload-images.jianshu.io/upload_images/9548468-704cf833a0e7e408.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
至于这里为什么dlopen头文件定义的两个参数,在这里为什么是三个参数,就是第二个参数才是路径,看汇编F5,同理dlsym
!(https://upload-images.jianshu.io/upload_images/9548468-3d6c1d3016d98eb0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
对加壳的app可以使用这种方式来hook关键点
OpenCommon / OpenMemory
C实现,替换原so就能在启动时hook从而dumpdex,这种基于inlinehook的操作不像xp框架,frida之类的容易被反调试,这种操作基本上没有什么反调试,比较香
附源码 ((https://github.com/ele7enxxh/Android-Inline-Hook)
)
```
#include <stdio.h>
#include <jni.h>
#include "../inlineHook/include/inlineHook.h"
#include <android/log.h>
#include <unistd.h>
#include <stdlib.h>
#include <dlfcn.h>
#define LOG_TAG "ZZZ"
#define LOGD(fmt, args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,fmt, ##args)
#define LOGE(fmt, args...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG,fmt, ##args)
//需要Hook的函数地址
unsigned int func_dlopen = NULL;
unsigned int func_dlsym = NULL;
//新so handle
void *p0;
//原so handle
void *p1;
int *env;
static unsigned long find_module_by_name(char *soName) {
char filename;
char cmdline;
sprintf(filename, "/proc/%d/maps", getpid());
LOGD("filename = %s", filename);
FILE *fp = fopen(filename, "r");
unsigned long revalue = 0;
if (fp) {
while (fgets(cmdline, 256, fp)) //逐行读取
{
if (strstr(cmdline, soName) && strstr(cmdline, "r-xp"))//筛选
{
LOGD("cmdline = %s", cmdline);
char *str = strstr(cmdline, "-");
if (str) {
*str = '\0';
char num;
sprintf(num, "0x%s", cmdline);
revalue = strtoul(num, NULL, 0);
LOGD("revalue = %lu", revalue);
return revalue;
}
}
memset(cmdline, 0, 256); //清零
}
fclose(fp);
}
return 0L;
}
//原函数指针
void* (*old_func_dlopen)(const char* filename, int flags, const void* caller_addr,void* s) = NULL;
void* (*old_fun_dlsym)(void* /*handle*/, const char* /*symbol*/) = NULL;
void* new_func_dlopen(const char *filename, int flags, const void *caller_addr, void *s) {
void* p = old_func_dlopen(filename,flags,caller_addr,s);
LOGD("%p = __loader_dlopen('%s','%d','%p')",p,filename,flags,caller_addr);
return p;
}
void* new_func_dlsym(void *handle, const char *symbol){
if(handle == p0 && strstr(symbol,"JNI_OnLoad") == NULL){
handle = p1;
LOGD("change handle from %p to %p",p0,p1);
}
void* ret = old_fun_dlsym(handle,symbol);
LOGD("%p = __loader_dlsym('%p','%s')",ret,handle,symbol);
return ret;
}
jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved) {
LOGE("------------------- JNI_OnLoad -------------------");
if (vm->GetEnv( (void**)&env, JNI_VERSION_1_6) == JNI_OK) {
LOGD("GetEnv OK");
}
char *lib_name = const_cast<char *>("linker");
unsigned int base = find_module_by_name(lib_name);
LOGD("Find %s at 0x%x ", lib_name,base);
//计算需要Hook的函数地址偏移 (baseAddress + offset + thumb ? 1 : 0)
//这里的地址是pull出linker导出函数里面找的,不同系统版本不一样
//这里使用的是Neux 6P 安卓9
func_dlopen = base + 0x75A4+1;
func_dlsym = base + 0x76F0+1;
LOGE("------------------- InlineHook -------------------");
//注册Hook信息
registerInlineHook((uint32_t) func_dlopen, (uint32_t) new_func_dlopen, (uint32_t **) &old_func_dlopen)==ELE7EN_OK ?
LOGD("Success Hook func_dlopen at 0x%x",func_dlopen):LOGE("Fail Hook func_dlopen at 0x%x",func_dlopen);
registerInlineHook((uint32_t) func_dlsym, (uint32_t) new_func_dlsym, (uint32_t **) &old_fun_dlsym)==ELE7EN_OK ?
LOGD("Success Hook func_dlsym at 0x%x",func_dlsym):LOGE("Fail Hook func_dlsym at 0x%x",func_dlsym);
inlineHookAll();
//新so
p0 = dlopen("/data/data/cn.xiaochuankeji.tieba/lib/libmarsxlog.so", 0);
LOGD("dlopen libmarsxlog.so handle = 0x%p",p0);
//原so
p1 = dlopen("/data/data/cn.xiaochuankeji.tieba/lib/libmarsxlogcp.so", 0);
LOGD("dlopen libmarsxlogcp.so handle =0x%p",p1);
//手动调用原so的JNI_OnLoad()
void *p2 = dlsym(p1, "JNI_OnLoad");
LOGD("called dlsym JNI_OnLoad 0x%p",p2);
LOGE("-------------------Function-------------------");
return JNI_VERSION_1_6;
}
``` I D:约你一起偷西瓜
邮箱:axhlzy@live.cn
申请通过,欢迎光临吾爱破解论坛,期待吾爱破解有你更加精彩,ID和密码自己通过邮件密码找回功能修改,请即时登陆并修改密码!
登陆后请在一周内在此帖报道,否则将删除ID信息。
PS:登陆后把文章整理一下发到移动安全区吧。 未报到,已注销。 我怎么重新申请呀还是重新发一下这篇文章么 ? 约你一起偷西瓜 发表于 2020-10-23 16:45
我怎么重新申请呀还是重新发一下这篇文章么 ?
这个渠道对你来说已经关闭,你可以通过其他渠道注册,申请渠道没有第二次机会了。
页:
[1]