之前我们讨论了去除签名校验的方法,无法去除最新的某60加固,始终是我心中的大山。我心有不甘,想看看这加固的签名校验的强度,去体会移动安全开发工程师在代码里书写的智慧与浪漫。于是就有了这篇文章。
基础知识的文章请看之前的文章--→https://bbs.kanxue.com/thread-285647.htm
我认为良好的环境是分析提高效率的关键
在频繁的使用ida 对app进行分析调试,经常会打开,可以编写一份脚本快速打开ida的server
这里面的 ida9.1 是你的ida server。还有frida 快速启动脚本
将“hluda-server-16.2.1-android-arm64” 修改为 你自己的的server就好了
这里我分析的环境是 piexl 5,Android 12 的版本。
样本是 6月最新的加固。
准备好你的ida , frida 基本环境,与我一起分析吧!

现在最新的加固样本的so文件是加密状态,我们要先进行初步的dump获取基本的符号信息,现在的符号信息空空如也。
这里使用frida dump 再修复
再使用SoFixer64 进行修复
774K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6r3z5p5I4q4c8W2c8Q4x3V1k6e0L8@1k6A6P5r3g2J5i4K6u0r3M7X3g2D9k6h3q4K6k6i4x3`.
这样就具备了基本的so 信息符号了

在最新的加固样本里面 外壳的so 似乎都用的c++代码编译了,对抗了之前所有的文章的地方
按照之前的文章和方法几乎 无法 将 内部so 进行dump 。
这里我再想 实现linker的步骤里面是无法脱离 dlopen这个函数的,我尝试搜索相关的引用,发现并不是linker 的预连接逻辑
而这个方法里面调用了线程相关的函数
pthread_rwlock_rdlock
那么我就要大胆的猜测,它是不是通过线程来进行通信,来实现linker的!
我引用信号相关的函数 kill
发现了之前 预连接的方法里面了? 是不是很巧妙?(其实是千疮百孔的调试分析得到的!)

那就使用我之前文章里面的脱壳脚本,进行脱壳
然后 按照我之前的文章内容 修复就可以了
https://bbs.kanxue.com/thread-285539.htm
加固样本 里面普遍使用 自己实现的svc 指令来进行 系统函数的调用。
比如: openat 调用
它的实现方式很有趣: 先mmap 一块可运行的内存,在把加密的svc代码解密放进去,再调用函数
这样做的好处是 直接断绝了——通过内存扫描的形式,对svc指令的hook!
在java 层
对包路径 进行两次不同的方法进行获取
调用了常规的签名信息,进行 hashcode
java层的强度 似乎不太高,我建议构造binder 通讯ams
native的强度还是可以的!
svc openat打开包
readklinkat 读取包路径
fstat 读取包的权限
都是以svc的方式实现的
这里反调试是我最大的困惑,也是最精彩的地方。
它是怎样实现反调试的呢?
经典 status 文件里面的 trace pid 的值 和 state 进程状态
在status 里面 还检测了 NoNewPrivs , 这个值很有意思! 你可以百度或者ai问一下!
通过fork 子进程 来进行 测试ptrace ,wait 来校验 进程信号是否被 调试
fork 的子进程与父进程通过管道 通讯 ,子进程调用 excel 函数 cat /proc/self/status ,观察 进程名 是否正确?
这里还对 /proc 的文件进行 mmap 查看它的error 值 ,如果我们替换了 就会成功 ! error 的值为0
我使用的lspant 的hook 来对java 进行 hook ,hook点在我上一篇文章有提到。
这里我有两种方案 一种是
优点: hook 比较简单就能实现, 也是大多数 多开app 使用方案
缺点: 稳定性差,遇到基于信号的检测就完了!还有就是对于低版本的内核 有很多功能实现不了!
优点: ptrace hook 的稳定性 对比上一个好,而且有很好的项目可以学习 proot , 有兴趣可以看我之前的proot 分析
缺点: 必须处理反调试检测
我的选择是 ptrace的方案,这里有很多资料可以学习。
这里的方案 来自 proot 和abyss
17dK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6H3M7X3!0G2N6q4)9J5k6r3#2W2i4K6u0r3M7s2u0G2L8%4b7`.
ca5K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6A6L8$3k6G2L8h3!0Q4x3V1k6S2j5Y4W2K6M7H3`.`.
包路径 这里的替换,这里可以参考插件化开发技术!
替换loadapk里面application 的sourice 的值就可以了
使用io重定向 修改读取的apk 为原来没有签名打包的apk.
readlink ,fstat 也是修改替换返回值.达到欺骗的效果。
也是对 status stat 这几个目录进行io重定向 主要就是修改 一些文件的值
然后将ptrace的值 返回为0 ,
就基本完成了 对抗! 呼出一口气。
最后,我要感谢
空壳的作者 Tiony.bo 也是我的前辈,感谢你对我插件化开发的指点,以及abyss的开源!
感谢 珍惜Any,王麻子本人 等等 大佬关于 ptrace的文章 让我有所启蒙!
参考的链接:
https://bbs.kanxue.com/thread-285339.htm
https://bbs.kanxue.com/thread-273160.htm
https://bbs.kanxue.com/thread-275511.htm
19bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6A6L8$3k6G2L8h3!0Q4x3V1k6S2j5Y4W2K6M7H3`.`.
98bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6H3M7X3!0G2N6q4)9J5k6r3#2W2i4K6u0r3M7s2u0G2L8%4b7`.
#!/bin/bash
# 设置端口转发
if
! adb forward tcp:4567 tcp:4567; then
echo
"[错误] 端口转发失败"
exit
2
fi
echo
"IDA服务已在端口4567启动"
# 启动IDA服务
if
! adb shell
"su -c 'cd /data/local/tmp && ./ida9.1 -p4567 &'"
; then
echo
"[错误] 无法启动IDA服务"
exit
1
fi
# 等待服务初始化
echo
"ok..."
#!/bin/bash
# 设置端口转发
if
! adb forward tcp:4567 tcp:4567; then
echo
"[错误] 端口转发失败"
exit
2
fi
echo
"IDA服务已在端口4567启动"
# 启动IDA服务
if
! adb shell
"su -c 'cd /data/local/tmp && ./ida9.1 -p4567 &'"
; then
echo
"[错误] 无法启动IDA服务"
exit
1
fi
# 等待服务初始化
echo
"ok..."
#!/bin/bash
adb shell
"su -c 'cd /data/local/tmp && ./hluda-server-16.2.1-android-arm64'"
#!/bin/bash
adb shell
"su -c 'cd /data/local/tmp && ./hluda-server-16.2.1-android-arm64'"
function dump_so(so_name) {
var libso = Process.getModuleByName(so_name);
console.
log
(
"[name]:"
, libso.name);
console.
log
(
"[base]:"
, libso.base);
console.
log
(
"[size]:"
, ptr(libso.size));
console.
log
(
"[path]:"
, libso.path);
var file_path =
"/data/data/"
+get_self_process_name()+
"/"
+ libso.name +
"_"
+ libso.base +
"_"
+ ptr(libso.size) +
".so"
;
var file_handle =
new
File(file_path,
"wb"
);
if
(file_handle && file_handle != null) {
Memory.protect(ptr(libso.base), libso.size,
'rwx'
);
var libso_buffer = ptr(libso.base).readByteArray(libso.size);
file_handle.write(libso_buffer);
file_handle.flush();
file_handle.close();
console.
log
(
"[dump]:"
, file_path);
}
}
function get_self_process_name() {
var openPtr = Module.getExportByName(
'libc.so'
,
'open'
);
var open =
new
NativeFunction(openPtr,
'int'
, [
'pointer'
,
'int'
]);
var readPtr = Module.getExportByName(
"libc.so"
,
"read"
);
var read =
new
NativeFunction(readPtr,
"int"
, [
"int"
,
"pointer"
,
"int"
]);
var closePtr = Module.getExportByName(
'libc.so'
,
'close'
);
var close =
new
NativeFunction(closePtr,
'int'
, [
'int'
]);
var path = Memory.allocUtf8String(
"/proc/self/cmdline"
);
var fd = open(path, 0);
if
(fd != -1) {
var buffer = Memory.alloc(0x1000);
var result = read(fd, buffer, 0x1000);
close(fd);
result = ptr(buffer).readCString();
return
result;
}
return
"-1"
;
}
function hook_dlopen() {
var dlopen = Module.findExportByName(null,
"dlopen"
);
Interceptor.attach(dlopen, {
onEnter: function (args) {
this
.call_hook =
false
;
var so_name = ptr(args[0]).readCString();
if
(so_name.indexOf(
"libshell-super.2019.so"
) >= 0) {
console.
log
(
"dlopen:"
, ptr(args[0]).readCString());
this
.call_hook =
true
;
}
}, onLeave: function (retval) {
if
(
this
.call_hook) {
inline_hook();
}
}
});
var android_dlopen_ext = Module.findExportByName(null,
"android_dlopen_ext"
);
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
this
.call_hook =
false
;
var so_name = ptr(args[0]).readCString();
if
(so_name.indexOf(
"libshell-super.2019.so"
) >= 0) {
console.
log
(
"android_dlopen_ext:"
, ptr(args[0]).readCString());
this
.call_hook =
true
;
}
}, onLeave: function (retval) {
if
(
this
.call_hook) {
inline_hook();
}
}
});
}
function main (){
Java.perform(
function(){
var tart_Name =
"libjiagu_64.so"
dump_so(tart_Name)
}
);
}
setImmediate(main)
function dump_so(so_name) {
var libso = Process.getModuleByName(so_name);
console.
log
(
"[name]:"
, libso.name);
console.
log
(
"[base]:"
, libso.base);
console.
log
(
"[size]:"
, ptr(libso.size));
console.
log
(
"[path]:"
, libso.path);
var file_path =
"/data/data/"
+get_self_process_name()+
"/"
+ libso.name +
"_"
+ libso.base +
"_"
+ ptr(libso.size) +
".so"
;
var file_handle =
new
File(file_path,
"wb"
);
if
(file_handle && file_handle != null) {
Memory.protect(ptr(libso.base), libso.size,
'rwx'
);
var libso_buffer = ptr(libso.base).readByteArray(libso.size);
file_handle.write(libso_buffer);
file_handle.flush();
file_handle.close();
console.
log
(
"[dump]:"
, file_path);
}
}
function get_self_process_name() {
var openPtr = Module.getExportByName(
'libc.so'
,
'open'
);
var open =
new
NativeFunction(openPtr,
'int'
, [
'pointer'
,
'int'
]);
var readPtr = Module.getExportByName(
"libc.so"
,
"read"
);
var read =
new
NativeFunction(readPtr,
"int"
, [
"int"
,
"pointer"
,
"int"
]);
var closePtr = Module.getExportByName(
'libc.so'
,
'close'
);
var close =
new
NativeFunction(closePtr,
'int'
, [
'int'
]);
var path = Memory.allocUtf8String(
"/proc/self/cmdline"
);
var fd = open(path, 0);
if
(fd != -1) {
var buffer = Memory.alloc(0x1000);
var result = read(fd, buffer, 0x1000);
close(fd);
result = ptr(buffer).readCString();
return
result;
}
return
"-1"
;
}
function hook_dlopen() {
var dlopen = Module.findExportByName(null,
"dlopen"
);
Interceptor.attach(dlopen, {
onEnter: function (args) {
this
.call_hook =
false
;
var so_name = ptr(args[0]).readCString();
if
(so_name.indexOf(
"libshell-super.2019.so"
) >= 0) {
console.
log
(
"dlopen:"
, ptr(args[0]).readCString());
this
.call_hook =
true
;
}
}, onLeave: function (retval) {
if
(
this
.call_hook) {
inline_hook();
}
}
});
var android_dlopen_ext = Module.findExportByName(null,
"android_dlopen_ext"
);
Interceptor.attach(android_dlopen_ext, {
onEnter: function (args) {
this
.call_hook =
false
;
var so_name = ptr(args[0]).readCString();
if
(so_name.indexOf(
"libshell-super.2019.so"
) >= 0) {
console.
log
(
"android_dlopen_ext:"
, ptr(args[0]).readCString());
this
.call_hook =
true
;
}
}, onLeave: function (retval) {
if
(
this
.call_hook) {
inline_hook();
}
}
});
}
function main (){
Java.perform(
function(){
var tart_Name =
"libjiagu_64.so"
dump_so(tart_Name)
}
);
}
setImmediate(main)
function my_hook_dlopen(soName =
''
) {
Interceptor.attach(Module.findExportByName(null,
"android_dlopen_ext"
),
{
onEnter: function (args) {
var pathptr = args[0];
if
(pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
if
(path.indexOf(soName) >= 0) {
this
.is_can_hook =
true
;
}
}
},
onLeave: function (retval) {
if
(
this
.is_can_hook) {
hook();
}
}
}
);
}
function addr_in_so(addr){
var process_Obj_Module_Arr = Process.enumerateModules();
for
(var i = 0; i < process_Obj_Module_Arr.length; i++) {
if
(addr>process_Obj_Module_Arr[i].base && addr<process_Obj_Module_Arr[i].base.add(process_Obj_Module_Arr[i].size)){
console.
log
(addr.toString(16),
"is in"
,process_Obj_Module_Arr[i].name,
"offset: 0x"
+(addr-process_Obj_Module_Arr[i].base).toString(16));
}
}
}
function hook_addr(){
var module = Process.findModuleByName(
"libjiagu_64.so"
);
Interceptor.attach(module.base.add(0x5E6C), {
onEnter: function (args) {
console.
log
(hexdump(args[0], {
offset: 0,
length: 0x38*0x6+0x20,
header:
true
,
ansi:
true
}));
console.
log
(args[1])
console.
log
(args[2])
console.
log
(`base = ${module.base}`)
},
onLeave: function (ret) {
}
});
}
function hook() {
console.
log
(
"hook fun dump so body"
);
var libjiagu = Process.getModuleByName(
"libjiagu_64.so"
);
if
(!libjiagu) {
console.error(
"未能找到libjiagu_64.so模块"
);
return
;
}
var soinfo;
var module = Process.findModuleByName(
"libjiagu_64.so"
);
Interceptor.attach(module.base.add(0xB6D8), {
onEnter: function (args) {
console.
log
(
"hook B6D8 method ! "
);
soinfo = args[0];
console.
log
(
"soinfo:"
,soinfo);
var base = ptr(soinfo.add(0x190)).readPointer();
var ph = ptr(soinfo.add(0xE8)).readPointer();
var phnum = ptr(soinfo.add(0xF8)).readLong();
var rela = ptr(soinfo.add(0x130)).readPointer();
var plt_rela = ptr(soinfo.add(0x120)).readPointer();
var size = (base).add(0x28).readLong();
var dymagic = ptr(soinfo.add(0x100)).readPointer();
var dy_size =0x290;
console.
log
(
"base:"
,base);
console.
log
(
"so size:"
,size);
console.
log
(
"ph:"
,ph);
console.
log
(
"phnum:"
,phnum);
console.
log
(
"rela:"
,rela);
console.
log
(
"plt_rela:"
,plt_rela);
console.
log
(hexdump((ptr(dymagic)), {
offset: 0,
length: dy_size,
header:
true
,
ansi:
true
}));
console.
log
(
"elf in ori offset: "
,ptr(Number(base) -Number(module.base)))
dump_so_body(base,size,
"so.so_0x12b000"
);
dump_so_body(ph,phnum*0x38,
"ph.so"
);
dump_so_body(rela,0x25080,
"rela_0x25080.so"
);
dump_so_body(plt_rela,0x1818,
"plt_rela_0x1818.so"
);
dump_so_body(dymagic,dy_size,
"dymagic.so"
);
},
onLeave: function (ret) {
var relasz = ptr(soinfo.add(0x138)).readPointer();
console.
log
(
"relasz:"
,relasz);
var plt_relasz = ptr(soinfo.add(0x128)).readPointer();
console.
log
(
"plt_relasz:"
,plt_relasz);
}
});
}
setImmediate(my_hook_dlopen,
"libjiagu"
);
function dump_so_body(addr, size, body_name) {
var file_path =
"/data/data/"
+get_self_process_name()+
"/"
+body_name +
"_"
+ addr +
"_"
+ size +
".body"
;
var file_handle =
new
File(file_path,
"wb"
);
if
(file_handle && file_handle != null) {
Memory.protect(addr,size,
'rwx'
);
var libso_buffer = ptr(addr).readByteArray(size);
file_handle.write(libso_buffer);
file_handle.flush();
file_handle.close();
console.
log
(
"[dump]:"
, file_path);
}
}
function get_self_process_name() {
var openPtr = Module.getExportByName(
'libc.so'
,
'open'
);
var open =
new
NativeFunction(openPtr,
'int'
, [
'pointer'
,
'int'
]);
var readPtr = Module.getExportByName(
"libc.so"
,
"read"
);
var read =
new
NativeFunction(readPtr,
"int"
, [
"int"
,
"pointer"
,
"int"
]);
var closePtr = Module.getExportByName(
'libc.so'
,
'close'
);
var close =
new
NativeFunction(closePtr,
'int'
, [
'int'
]);
var path = Memory.allocUtf8String(
"/proc/self/cmdline"
);
var fd = open(path, 0);
if
(fd != -1) {
var buffer = Memory.alloc(0x1000);
var result = read(fd, buffer, 0x1000);
close(fd);
result = ptr(buffer).readCString();
return
result;
}
return
"-1"
;
}
function my_hook_dlopen(soName =
''
) {
Interceptor.attach(Module.findExportByName(null,
"android_dlopen_ext"
),
{
onEnter: function (args) {
var pathptr = args[0];
if
(pathptr !== undefined && pathptr != null) {
var path = ptr(pathptr).readCString();
if
(path.indexOf(soName) >= 0) {
this
.is_can_hook =
true
;
}
}
},
onLeave: function (retval) {
if
(
this
.is_can_hook) {
hook();
}
}
}
);
}
function addr_in_so(addr){
var process_Obj_Module_Arr = Process.enumerateModules();
for
(var i = 0; i < process_Obj_Module_Arr.length; i++) {
if
(addr>process_Obj_Module_Arr[i].base && addr<process_Obj_Module_Arr[i].base.add(process_Obj_Module_Arr[i].size)){
console.
log
(addr.toString(16),
"is in"
,process_Obj_Module_Arr[i].name,
"offset: 0x"
+(addr-process_Obj_Module_Arr[i].base).toString(16));
}
}
}
function hook_addr(){
var module = Process.findModuleByName(
"libjiagu_64.so"
);
Interceptor.attach(module.base.add(0x5E6C), {
onEnter: function (args) {
console.
log
(hexdump(args[0], {
offset: 0,
length: 0x38*0x6+0x20,
header:
true
,
ansi:
true
}));
console.
log
(args[1])
console.
log
(args[2])
console.
log
(`base = ${module.base}`)
},
onLeave: function (ret) {
}
});
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
最后于 1天前
被逆天而行编辑
,原因: