void dlopen(const char filename, int flag)
void dlsym(void handle, const char * symbol)
通过这两个函数,以及inlinehook我们可以实现root下不修改smali,不重打包完成持久化native hook
众所周知,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去实践一下想法
发现死活不行,想到可能是连续调用导致frida性能问题(欢迎大佬给我普及一下为什么),后面改用Module.findExportByName()返回的pointer直接去替换dlsym返回值,这就行了(没读过frida源码,但是这里看参数和结果似乎frida的这个api就是调用的dlsym返回) 然后后面又想着用inlinehook去实现 但是系统连新so都还没加载进去,谈何hook,emmm 最后想着我在调用一次dlopen加载新so不就行了么……
2.我们需要稍微了解一下他为什么调用不起来 其实注意的就是刚才说的dlopen返回的handle,dlsym传递的两个参数,第一个就是dlopen返回的handle,第二个是我们需要调用的函数符号 这个时候使用inlinehook去hook一下这两个函数,在手动加载 原so 后,触发一下原so的Jni_onload(),再启动inlinehook当dlsym第一个参数是 新so 的dlopen handle时就替换为 原so 的handle
我们这里就用最右(cn.xiaochuangkeji.tieba)来举例
inlinehook替换函数前8字节(2条arm指令或者4条thumb指令)为跳转,而frida hook在这里是enter时候的参数自然是原来的,所以看起来是用了错误的handle加载function还不崩掉
这里这个手机没有开全局调试 只有将就用frida hook看一下打印日志
这里__android_log_print是变长参数为了能展示全部参数,只好多填几个参数,各自眼神忽略把
至于这里为什么dlopen头文件定义的两个参数,在这里为什么是三个参数,就是第二个参数才是路径,看汇编F5,同理dlsym
对加壳的app可以使用这种方式来hook关键点 OpenCommon / OpenMemory C实现,替换原so就能在启动时hook从而dumpdex,这种基于inlinehook的操作不像xp框架,frida之类的容易被反调试,这种操作基本上没有什么反调试,比较香
附源码 inlinehook )
这么看来基于PLT的hook似乎也能做点事情了
关于dlopen以及dlsym函数地址的获取,目前这里是用的基地址加偏移,或者是采用写一个函数只用于调用dlopen,这样再去hook这个函数拿到一个类似于dlopen的代理地址,算是比较通用
还有一个最通用的方法 用typedef重定义一个dlopen,重新声明一个函数指针,并将这个函数指针指向dlopen并强转为重定义的dlopen
my_dlopen = func_dlopen
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[
1
].readCString().indexOf(
"ZZZ"
)!
=
-
1
)
console.log(args[
1
].readCString()
+
"\t"
+
args[
2
].readCString()
+
"\t"
+
args[
3
]
+
"\t"
+
args[
4
]
+
"\t"
+
args[
5
])
}
});
}
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[
1
].readCString().indexOf(
"ZZZ"
)!
=
-
1
)
console.log(args[
1
].readCString()
+
"\t"
+
args[
2
].readCString()
+
"\t"
+
args[
3
]
+
"\t"
+
args[
4
]
+
"\t"
+
args[
5
])
}
});
}
/
/
需要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[
32
];
char cmdline[
256
];
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[
32
];
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);
/
/
手动调用原so的JNI_OnLoad()
p2(vm,reserved);
LOGE(
"------------------- Function -------------------"
);
return
JNI_VERSION_1_6;
}
/
/
需要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[
32
];
char cmdline[
256
];
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[
32
];
sprintf(num,
"0x%s"
, cmdline);
revalue
=
strtoul(num, NULL,
0
);
LOGD(
"revalue = %lu"
, revalue);
return
revalue;
}
}
memset(cmdline,
0
,
256
);
/
/
清零
}
fclose(fp);
}
return
0L
;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2020-10-8 10:18
被唱过阡陌编辑
,原因: 更新 纠正错误