首页
社区
课程
招聘
[原创]某银行 App 对抗实录
发表于: 8小时前 210

[原创]某银行 App 对抗实录

8小时前
210

说明:本文是基于乐佬的两篇某企业壳逆向帖子,我实践后对遇到的问题做的记录
分析环境:Pixel 6 自编译 AOSP 15 。

1. 谁在杀我

在实践乐佬的逆企业壳的帖子时,一打开最新版的某银行App,9秒后就闪退,上了反反调试的root检测,自编译特征去除后还是被杀。出师未接身先死,立马就激起我的斗志。

adb logcat -b crash -b main
logcat 含义
System.exit called, status: 0 + Process ... has died 某 SDK 主动静默退出(非崩溃)
Fatal signal 11 (SIGSEGV) / tombstone native 崩溃
ANR / lowmemorykiller 卡死 / 内存不足

抓到的关键片段(脱敏):

W/Risk-thread-Tas: avc denied  read /proc/version, /proc/tty/drivers    ← 风控读环境特征
W/getprop:         avc denied  read ro.debuggable / userdebug_or_eng_prop
I/<pkg>:           System.exit called, status: 0                          ← ★风控:主动静默退出
I/ActivityManager: Process <pkg> ... has died: fg TOP

在这个启动过程中,手机的去特征frida是没有开启的,也没有安装面具,LsPosed等。从日志中已暴露出是读了系统的debug属性闪退,本以为发现了就可以解决,哪想到...

修改system_property_api.cpp

在三个出口拦截:

  • __system_property_get
  • __system_property_read_callback(getprop 子进程走这条)
  • __system_property_read
// 只对目标 app 的 uid 返伪值,全局物理值不动
if (getuid() == 目标uid && name 命中 ro.debuggable 等) {
    return "0";      
}

我自己实现了一套类似xposed的框架,可以对属性进行拦截。

做了以上操作后,打开银行APP还是在9s内闪退,看样子不仅仅是这块检测。

2. 上 eBPF

既然 frida 走不通、窗口又只有几秒,正确姿势是不进入目标进程,从内核侧监控风控读了什么。我是使用的是eBPF uprobe/kprobe

2.1 为什么选择 eBPF uprobe/kprobe

  • 通过uprobe 挂 bionic libc 可以导出函数__system_property_get/findaccess/faccessat/stat/lstat/fstatat/openatexecve/posix_spawn/forkreadlink*,可以通过过滤目标 app uid ,libc syscall 包装函数能直接拿路径参数 char*
  • kprobe 挂内核 syscall 实现do_sys_openat2 / vfs_statx / do_group_exit / __arm64_sys_ptrace / tgkill对 uprobe 功能的补充——VMP 代码会用直接 svc 绕过 libc,libc-uprobe 看不到,kprobe 在内核层能看到全部访问。

GKI 5.10 踩坑:本机环境用 Debian chroot + bpftrace,两个必踩坑:① 必须 mount --rbind /apex $ROOT/apex(uprobe 要命中 app 实际映射的真实 libc inode,拷副本 inode 不同不触发);② export TMPDIR=/root(chroot 默认 /tmp 不可写,否则解 kheaders 直接崩)。

2.2 新的问题:时序反调试

使用ebpf方案监控APP操作后出现以下的现象:

  • 15 探针 uprobe → 死亡时间从 ~9.5s 提前到 ~1.5s
  • 探针(2 个)/ kprobe → 保持 ~9.5s

说明它会测操作耗时,插桩拖慢了时间,应用就会自杀。**所以检测要使用最少的探针 kprobe。

2.3 观测到的检测项

脱敏后简表:

检测位置 行为 备注
① 系统属性 bare getprop 子进程全量 dump(数十次)→ Java 层 grep;进程内 __system_property_getro.debuggable/ro.secure/ro.boot.qemu/ro.build.fingerprint...;指纹↔安全补丁交叉校验 per-app 属性 hook 有效,但不阻止自杀
② 内核/启动参数 /proc/version(被 SELinux 挡)、/sys/fs/selinux/context/sys/kernel/pid_max /proc/version 对 untrusted_app 零售机也被挡 → 非判别点
③ 文件/挂载 access()72 条 su/magisk 路径;execve busybox/su/which 遍历 PATH;exec mount + 读 /proc/mounts(overlayfs 审计) su 被 DAC 拦住;
④ 内存/进程 /proc/self/maps ×24(找非官方 .so);/proc/self/task/*/status ×678 线程巡检(线程名 + 每线程 TracerPid);ptrace 反注入 frida 注入即在此被抓 → SIGSEGV
⑤ 行为指纹 RustDeskDetector connect 127.0.0.1:37527(远控端口探测);时序反调试

观测能力边界:eBPF 能看到「读了什么」,但通过对这些读的值分析发现各项属性读取的都是正常值,让我一度怀疑是通过这些值拼接了出我的设备画像,通过服务端判断,然后自杀(通过关网验证并不是)。


3. 上unidbg

在这里我将APP使用的某梆加固最为重点攻破对象,在疯狂的补完环境后,发现并不是在这里检测到环境异常而自杀。在这里一度想放弃。其中心酸不表。

3.1 罪魁祸首是某盾 libtong*

某盾直接读 /dev/properties,属性 API hook 对它全失效。
在此之前,我已经用 per-app bionic hookro.debuggable 等对目标 APP 返回0,属性 API 层确实生效但自杀照旧。 为什么?

unidbg 跑某盾 init 时抓到:

  • init(INIT_ARRAY 构造器)里直接 open("/dev/__properties__"),把系统属性共享内存 mmap 进来、走自实现的 trie 解析读属性

即:同盾不走 bionic 属性 API,直接读属性区原始共享内存。

结论:任何在 __system_property_get 函数层做的 hook(per-app bionic 伪装、frida hook 该导出、Xposed 改 SystemProperties.get)——对某盾这类 SDK 一律无效,它读到的是内核/init 写进 /dev/__properties__真值


3.2 问题修复

设备属性值:

ro.boot.verifiedbootstate = orange     ← ★解锁标志(售卖机器 = green)
ro.boot.flash.locked      = 0          ←   售卖机器 = 1
sys.oem_unlock_allowed    = 1
ro.debuggable             = 1   
ro.secure = 1

init 把 androidboot.* → ro.boot.* 映射写进属性区的那一步动手,在属性区里写入值 green

  • patch system/core/init/property_service.cpp,在 androidboot.verifiedbootstate → ro.boot.verifiedbootstate 映射处改写为 green;顺带 flash.locked → 1verifiedbooterror → ""(源在 /proc/bootconfig)。
  • 因为改的是属性区内容真值,无论上层走 __system_property_get 还是直读 /dev/__properties__,读到的都是 green —— 对某盾的自解析同样有效

至此问题修复,应用启动不自杀


以上仅供安全研究与加固对抗技术交流,请勿用于任何非法用途。


[招生]科锐逆向工程师培训(2026年7月3日实地,远程教学同时开班, 第56期)!

收藏
免费 2
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回