ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于AOSP定制ROM绕过Momo与AIDA64检测的实战指南

基于AOSP定制ROM绕过Momo与AIDA64检测的实战指南 搞安卓底层开发的朋友应该都知道一个尴尬的现状很多App对运行环境的检测越来越“狠”尤其是银行类、支付类、游戏类应用。过去靠Xposed模块改改build.prop就能蒙混过关现在基本行不通了。Momo这个App会检测一堆系统异常AIDA64则能把你的真实硬件信息翻个底朝天。想要真正改掉设备指纹最彻底的办法就是从系统镜像入手直接编译一个属于自己的AOSP定制ROM从底层把设备信息“重新捏造”一遍。这篇文章就完整记录我基于AOSP定制ROM绕过Momo和AIDA64检测的实战过程从Ubuntu编译环境搭建、AOSP源码下载编译到改Build属性、改内核信息、调SELinux策略、最终刷机验证全程带细节和踩坑记录。适合已经会基本刷机、想深入理解安卓设备指纹原理或者正在做系统安全研究的朋友。另外我先把话说在前面所有操作请放在自己的测试机上在合法合规的场景下进行本文内容是纯粹的技术研究和设备隐私保护探讨。1. 项目背景与核心思路1.1 为什么非要定制ROM而不是用Xposed模块一键解决很多新手上来就装个Magisk插件、打个Xposed模块比如“设备模拟器”之类的想靠Hook函数返回假数据。这思路确实绕过了部分Java层检测但对底层信息的检测几乎无效。原因很简单Momo和AIDA64这类工具不仅能读到常规的Build属性ro.product.model这种东西还会去遍历Linux内核信息、SELinux状态、SELinux上下文、init进程的环境变量、su文件是否存在、系统应用签名甚至通过sysfs节点读取真实硬件路径和序列号。这些信息在Java层Hook很难完全覆盖而且检测方也会交叉比对比如Build里写的型号是某台厂商机但内核版本字符串却不对传感器节点路径对不上或者SELinux没有强制模式一眼就露馅。所以真正彻底的方案就是直接改ROM。AOSP是开源的把系统源码拉下来修改你想改的所有文件重新编译出一个干净的镜像刷进设备后系统从底层呈现的就是你想要的样子。这不需要Hook不需要root暴露整条链路都是自洽的。1.2 Momo与AIDA64到底在检测什么先拆目标。Momo是一款知名的安卓环境检测工具它主要检查这几点root状态包括su二进制、Magisk、Superuser、特定包名、init进程不安全属性等系统安全SELinux是否Enforcing、系统分区是否可写、SELinux上下文是否异常异常Activity非官方系统界面、Bootloader解锁状态开发调试点ADB是否开放、USB调试是否开启、开发者选项状态存在可疑的Xposed或Riru/Zygisk模块AIDA64则完全是另一条路线它通过查询系统属性、读取/proc/cpuinfo、/proc/meminfo、/sys/devices等节点把CPU型号、内核版本、存储序列号、屏幕分辨率、传感器型号、电池信息、摄像头型号全扒出来。如果你想改设备画像必须把这些信息和Build里的型号一致。我的目标是让Momo显示“未检测到异常”同时让AIDA64展示一套完整的、逻辑自洽的“虚拟设备信息”。项目环境是Pixel 4目标镜像换成一加7 Pro的画像。2. 环境搭建从零开始编译AOSP2.1 编译机配置和系统准备AOSP编译是个吃硬件的事千万别拿笔记本贸然开整很浪费时间。我用的是一台二手服务器配置是E5-2680 v4双路、64GB内存、1TB NVMe固态Ubuntu 20.04 LTS。内存低于16GB会卡到怀疑人生最少建议32GB磁盘至少400GB空闲空间。另外网络必须能稳定访问Google的源码仓库这个前置条件我就不展开说了懂的都懂自己解决网络问题但绝对不要用非法手段。装完系统后先更新基础依赖包AOSP官方要求装一堆库直接执行这些命令sudo apt-get update sudo apt-get install -y git-core gnupg flex bison build-essential zip curl zlib1g-dev \ libc6-dev-i386 libncurses5 lib32ncurses5-dev x11proto-core-dev libx11-dev \ lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip fontconfig python3注意Ubuntu 20.04默认没有libncurses5只有libncurses6所以需要单独加源或者手动装老包否则编到一半报错找不到ncurses头文件。另外要安装repo工具和配置Git身份mkdir ~/bin curl https://storage.googleapis.com/git-repo-downloads/repo ~/bin/repo chmod ax ~/bin/repo export PATH~/bin:$PATH git config --global user.name Your Name git config --global user.email youexample.com2.2 拉取AOSP源码与分支选择这里有个决策点到底拉哪个安卓版本。我之前用Android 12android-12.1.0_r16做过一版但Momo对这种版本检测特别严格长期安全性补丁会暴露系统太旧的问题。后来发现跟我的目标设备也脱节——目标一加7 Pro出厂是Android 10所以我把源码切到了对应的Android 10分支稳定的android-10.0.0_r47版本。拉取命令mkdir aosp cd aosp repo init -u https://android.googlesource.com/platform/manifest -b android-10.0.0_r47 repo sync -j16repo sync时间取决于网速我拉了将近3小时包括git历史接近80GB。建议用repo sync -c跳过历史只同步当前分支的代码能省一半空间和时间repo sync -c -j16拉取完成后先测试能否正常编译原版镜像。这一步很重要先保证环境没问题再改东西否则后期排查起来分不清是环境问题还是改动问题。此时需要下载对应设备的驱动二进制。因为我是Pixel 4对应的驱动包可以在Google官方页面下载解压后是一个Shell脚本执行后会生成vendor目录。source build/envsetup.sh lunch aosp_flame-userdebug make -j32第一次编译大概2小时CPU满负载中间可能会有内存不足被OOM杀掉的情况。建议确认swap开个16GB编译指令后面加-j32或者-j16不要盲目用-j64。耐心等到看到#### build completed successfully说明底层环境通了。2.3 定制ROM的编译流程AOSP编译输出的img文件在out/target/product/flame/目录下包括boot.img、system.img、vendor.img、vbmeta.img等。定制ROM时真正高频修改的是system分区和vendor分区所以咱们核心关注system里的build.prop、framework、等等。编译完原版后先整一个干净的基线。后面每次修改我都遵循三步流程修改源码文件重新执行make用fastboot刷对应分区验证但要注意AOSP增量编译有时候不生效因为很多属性已经打进img里。稳妥起见在修改build.prop或framework后执行make systemimage或make -j16它会自动rebuilt依赖的部分不要天天clean否则编译时间直接翻倍。3. 设备指纹修改核心实现3.1 修改Build属性让AIDA64看到假“皮”AIDA64读取的第一层信息就是系统Build属性也就是/system/build.prop文件。AOSP源码中这些默认属性定义在build/make/target/product/下的mk文件中。最直接的方法是修改device支持目录下的mk文件也可以后期直接改生成的build.prop再重新打包system.img。我推荐用源码级修改可维护性更强。对应的设备mk文件路径是device/google/flame/flume.mkPixel 4的内部代号flame里面有PRODUCT_MODEL等定义但是很多ro.*属性是在build/make/core/sysprop.mk等地方默认生成的。为了精确改我找到/system/build.prop里的关键行再反查源码变量。最终我改的字段是这个列表Build属性原值Pixel 4修改后假装一加7 Proro.product.modelPixel 4OnePlus7Proro.product.brandgoogleoneplusro.product.nameflameOnePlus7Proro.product.deviceflameOnePlus7Proro.product.manufacturerGoogleOnePlusro.build.fingerprintgoogle/flame/flame:10/QD1A.190821.007/...oneplus/OnePlus7Pro/OnePlus7Pro:10/QKQ1.190716.003/...ro.build.version.release1010ro.build.version.security_patch2020-01-052020-05-01ro.build.display.idQD1A.190821.007QKQ1.190716.003ro.build.flavorflame-userdebugOnePlus7Pro-userro.com.google.clientidbaseandroid-googleandroid-oneplusro.oem_unlock_supported10这里坑很多。只改model和manufacturer没用AIDA64的“设备信息”页面还会读取ro.product.name和ro.product.deviceMomo则通过Build.FINGERPRINT交叉校验整条指纹链。如果fingeprint里写的brand是google但product.name不是flame它就会怀疑是修改过的。所以我直接把fingerprint整条替换成了真实一加7 Pro的指纹串同时把ro.product.flavor改成OnePlus7Pro-user。这样系统自洽性更好。修改位置我统一放在device下的device.mk中通过PRODUCT_PROPERTY_OVERRIDES强制覆盖。举个例子PRODUCT_PROPERTY_OVERRIDES \ ro.product.modelOnePlus7Pro \ ro.product.brandoneplus \ ro.product.nameOnePlus7Pro \ ro.product.deviceOnePlus7Pro \ ro.product.manufacturerOnePlus \ ro.build.fingerprintoneplus/OnePlus7Pro/OnePlus7Pro:10/QKQ1.190716.003/28H.191007.113:user/release-keys \ ro.build.display.idQKQ1.190716.003 \ ro.build.version.security_patch2020-05-01注意fingerprint里的版本字符串日期必须和ro.build.version.*匹配AIDA64虽然不校验但Momo会读Build.VERSION的各个字段做内部一致性判断。3.2 修改内核信息、SELinux状态和调试标记Build属性只是表面Momo最恶心的一点是会通过/proc自己读内核参数。几个关键节点/proc/cmdline里面写了androidboot.veritymodeenforcing之类的Momo会检查有没有可疑参数/proc/versionLinux内核编译信息包含编译器版本、内核版本号/proc/selinux下相关节点检查selinux的状态和上下文内核版本字符串是在编译内核时生成的。AOSP的内核源码和系统源码是分开的Pixel 4的内核在kernel/google/msm-4.14但通常是由预先编译好的内核二进制放到vendor里。要改/proc/version要么重新编译内核要么直接修改kernel二进制里的字符串我试过后一种方法strings kernel | grep Linux version找到类似“Linux version 4.14.190-g3c131a0c”的字符串用010 Editor或Python脚本替换成目标内核字符串注意保持长度一致否则会破坏二进制的符号表和字符串表。这个操作有风险建议在虚拟机里备份kernel文件再操作。我替换成了Linux version 4.14.180-g1f4f4d1 (oneplusswdev) (Android clang version 9.0.3) #1 SMP PREEMPT Thu May 21 09:23:03 CST 2020长度与原串不同时我用十六进制编辑器小心地填充到相同长度或者干脆在源码级别编译一个内核。如果你用的是已经root的设备还可以直接把修改后的/proc/version字符串强制挂载隐藏但ROM定制里还是建议改二进制。SELinux状态方面Momo会检测SELinux是否处于Enforcing或者getenforce返回值。AOSP的userdebug/eng版本默认SELinux是Permissive或部分Permissive这就会触发Momo告警。我直接把目标设备定义成user版本同时修改BoardConfig.mk里的SELinux定义BOARD_KERNEL_CMDLINE androidboot.selinuxenforcing BOARD_VERITY_MODE : enforcing另外还在device.mk中删除了debug相关的persist属性把ro.debuggable从1改成0ro.secure改成1ro.adb.secure改成1。否则Momo检测到adb调试开启、Android Debug属性为1直接标红。3.3 修改硬件信息把AIDA64的所有传感器都归一AIDA64能读出主板型号、内存大小、存储容量、电池状态、屏幕分辨率、摄像头型号、传感器列表这些信息不全在Build属性里很多是通过sysfs节点从内核暴露出来的。比如/sys/devices/soc下的soc_id/sys/block/mmcblk0/device/等存储信息摄像头型号在/sys/devices/platform/下。这些节点的修改最耗时间因为你得先逐一查看原设备和目标设备读取到的值。我建议直接暴力一点把目标设备对应的sysfs信息做成一个映射表在ROM里把原有设备的节点内容替换掉。但节点的读写权限由SELinux管理直接改文件内容需要root。所以我在kernel里通过修改dts设备树和驱动代码来改变节点输出。这么说吧如果只是想骗过AIDA64不需要每个节点都一样AIDA64主要靠Linux标准接口比如CPU型号从/proc/cpuinfo中的Hardware和Processor字段读取主板信息从/sys/devices/soc/soc_id读取内存大小从/proc/meminfo读取MemTotalGPU信息从/sys/kernel/gpu读取屏幕分辨率从DisplayInfo或者读取/sys/class/graphics/fb0参数我实践下来优先级最高的是/proc/cpuinfo。很多检测App都拿它做基准。Pixel 4用的是骁龙855cpuinfo里Hardware为sm8150而一加7 Pro同款骁龙855实际Hardware都是sm8150所以不用改。但如果你用Pixel 4模拟一台骁龙888的设备那就得改内核的machine_descriptor或dts里的compatible字段工作量非常大。建议选同平台的目标设备进行伪装这样CPU、GPU、soc相关信息天然一致只需要改掉品牌型号就够。内存大小方面AIDA64显示的总内存和/proc/meminfo的MemTotal一致但MemTotal是内核根据实际情况计算出来的修改它需要给内核传参mem命令或者改dts的memory节点。例如内核cmdline里加mem8192M可以强制限制内存大小但往大了加没门。所以改ROM时要选内存比目标机小或相等的硬件平台否则AIDA64露馅。比如Pixel 4有6GB RAM想伪装成12GB的一加7 Pro单纯刷入的RO属性根本没用内核看到的MemTotal是6GB检测方一算就知道对不上。正确思路是选一个目标机型它的内存自己物理内存。一加7 Pro的6GB版就勉强可行但要把内核cmdline中强制指定为6GB否则还是8GB原值。其他的传感器和摄像头AIDA64通常只显示设备型号这些信息走的也底层。想省事的话可以在RRO叠加层或者framework层拦截查询但这就又变成Hook了非ROM内置方案Momo扫Hook更容易出问题。所以我的策略是放弃在AIDA64的“摄像头”标签页做假信息直接通过修改AOSP的CameraProvider配置把非法camera设备屏蔽掉让AIDA64显示“No supported cameras”或只显示前置、后置官方命名。检测工具看到没有摄像头反而不会警觉因为它确实是一台“开发机”。3.4 绕过Momo检测的关键点Momo不同于硬件信息工具它的重点在“系统环境异常”。我实测下来想让它报绿色下面几点必须做到必须使用user编译类型而不是userdebug或eng。userdebug自带root漏洞、ro.debuggable1Momo对这些敏感度极高。把lunch的目标切到aosp_flame-user即可同时把persist.sys.root_access设为0。去掉所有root相关二进制。AOSP默认没有su但很多定制ROM都会有。确认整个system/vendor里没有su二进制关键文件如/system/xbin/su、/system/bin/su、/system/app/Superuser.apk都不存在。SELinux必须是Enforcing且上下文不能异常。Momo会检查getenforce结果还检查/sys/fs/selinux/enforce节点。另外还会检查avcdenied日志数量如果SELinux一直Permissive大量SELinux日志堆积必然暴露。所以不能单纯把selinux置为enforcing还要保证策略正确、无大量denied。这对AOSP定制ROM其实是个挑战因为我们改了不少东西新的策略可能需要调整。我的做法是在framework/base/services/core/java/com/android/server/pm/SELinuxMMAC.java等尽量用默认策略不改动系统源码的SELinux domains否则很容易产生一堆denied log。系统分区必须只读且验证启动正常。Momo会检查system分区是不是rw挂载检查verifyboot状态。定制ROM如果关闭了dm-verity很容易被标记。所以要么保留原版bootloader的verity要么自己生成匹配的vbmeta签名否则fastboot会显示orange状态Momo一样报告bootloader已解锁。这里我踩了大坑后面单独讲。避免系统应用context异常。比如你加了一个新系统应用但没给它写sepolicy它运行时SELinux报avc deniedMomo可能去检查应用的安全上下文异常很容易告警。解决方法是额外封装一个系统应用给它分配一个已有的platform签名并添加相应的sepolicy。理论上做完这些Momo应该能到全绿。但实际很多ROM连目标机型的vendor指纹都对不上所以我需要把vendor的build.prop也一并改了。其实vendor分区里也有一个build.prop路径/vendor/build.prop包含了ro.vendor.*属性。AIDA64和Momo同样会读取这里。我在源码里把device/google/flame/device.mk和device/google/flame/board-info.txt都做了修改让vendor的属性也回到一加7 Pro的vendor build信息。修改完这些源码我再执行source build/envsetup.sh lunch aosp_flame-user make -j32等编译输出后用fastboot刷入。4. 刷机验证与效果评估4.1 刷机流程与bootloader解锁风险Pixel 4解锁bootloader很简单但注意解锁后bootloader状态就是unlocked任何Open Bootloader状态对Momo来说都是原罪。这就是自制ROM和原厂ROM最大的区别——原厂解锁是有记录的Momo检测到bootloader unlocked会亮红。要绕过这一点不能在最后验证时保持unlocked需要重新锁定bootloader但锁定后没法刷自制镜像所以常规方案是刷机阶段用fastboot刷完所有分区并且使用fastboot oem lock重新上锁上锁后的设备启动时验证签名如果签名不匹配就会变砖所以如果你要做一个自洽的、让Momo认为bootloader已锁定的ROM必须自己生成一套可信的AVBAndroid Verified Boot密钥并把公钥烧进bootloader/keymaster这个操作风险较高且设备商家不认第三方密钥。我这次不敢对主力机做这么深的修改所以用另一台设备做了验证验证过程中保持bootloader解锁状态那么Momo的bootloader项会亮黄但这已经代表绝大多数系统环境通过了剩下的偏差是解锁状态本身的物理体现。如果真想彻底绿可以研究“伪造locked状态”的方案修改init进程开机时读取的/sys/devices/soc下的bootloader状态节点。但这涉及把关键function挂到hook里又是Hook路线容易被扫描。所以我的结论是Momo全绿需要理论上具备生成合法签名的密钥体系否则较难完美。4.2 Momo检测结果对比刷完我的定制ROM我直接安装最新版Momo打开一看root检查全绿系统检查全绿SELinux检查全绿开发调试点因为我关了adb调试绿异常Activity绿bootloader状态黄整体相比原版Pixel 4刷魔改Magisk时一堆红已经改善很多。虽然没有全绿但这个黄是上锁问题说明系统环境已经几乎无可挑剔。4.3 AIDA64设备画像对比AIDA64读出的信息基本“以假乱真”。设备型号成了OnePlus7Pro品牌成了OnePlus硬件信息里的平台依旧是Qualcomm SM8150和真实一加7 Pro一致。内存显示6GB我用了mem6G内核参数CPU信息和原机一致存储型号则是闪存本身的真实厂商因为AIDA64读取的是底层的/sys/block/mmcblk0/device/vendor等节点这个我没改所以显示为Samsung和一加7 Pro普遍用的Samsung闪存倒也对得上。屏幕分辨率方面Pixel 4是1080x2340而一加7 Pro是1440x3120。AIDA64直接读驱动层的显示模式我没改dts里的display panel所以显示的还是Pixel 4规格。这个如果想改得替换整个display panel驱动工作量很大。Momo不关注这个但AIDA64披着一个一加7Pro的外壳屏幕参数确实是最大破绽。摄像头和传感器同理显示出来是Pixel 4的型号。所以从严格意义上讲我这个ROM的“设备画像”并非100%复制一加7 Pro而是在主要表层参数上做了同化。对于一般的风控检测来说最重要的品牌型号、指纹串、系统状态、CPU主板这些核心点都已经通过了。5. 常见问题与避坑5.1 编译失败和产物异常怎么排查我遇到过最频繁的编译问题是No rule to make target xxx多半是驱动包没有正确解压到vendor目录。重新执行驱动脚本后make clean再试。Out of memory加swap或者降低-j参数。Jack server相关报错在老版本上常见Android 10默认用Jack编译Java代码需要设置JACK_SERVER_VM_ARGUMENTS-Xmx4096m或者干脆升级到更高版本避免这问题。修改了build.prop后编译不生效因为product包缓存。执行make clean然后重新编译镜像。烧录后开机无限重启很可能是SELinux策略问题先用fastboot -w清数据不行就回滚到改动之前刷。5.2 修改内核字符串导致kernel panic替换/proc/version字符串时我不小心改错了长度导致内核启动崩溃。这个没有什么好办法劝导所有人都要备份原kernel并且用Python脚本控制长度一致再写入。举个例子import binascii, re, sys with open(kernel, rb) as f: data bytearray(f.read()) old bLinux version 4.14.190-g3c131a0c new bLinux version 4.14.180-g1f4f4d1 # 长度一致不足补空格 if len(old) ! len(new): raise SystemExit(length mismatch) idx data.find(old) if idx -1: raise SystemExit(string not found) data[idx:idxlen(old)] new with open(kernel_new, wb) as f: f.write(data)这里的kernel是boot.img中解包出来的内核镜像。解包工具用mkbootimg或magiskboot均可。替换完再用mkbootimg --kernel kernel_new --ramdisk ramdisk.img --cmdline ... --base 0x...重新打包成boot.img。注意boot.img的dtb和header参数不同直接用原boot.img解包再打包最稳妥。5.3 Momo检测的红黄项各自怎么处理我自己遇到的检测项和对策整理成一张速查表方便大家对照检测项触发原因对策建议su文件检测/system/bin/su或Magisk相关残留彻底移除root方案或使用magisk deny list但ROM内最好无SELinux状态Permissive模式切换到Enforcing并修复策略ro.debuggable1userdebug/eng版本使用user版编译adb调试开启persist.sys.usb.config含adb改为mtp关闭开发者选项系统分区可写remount过重新上锁dm-verity用原始ro状态内核异常自编译内核版本字符串不匹配和系统指纹日期对齐Bootloader解锁bootloader的unlocked状态测试机保持unlock不计入正式结论5.4 后续还能扩展的方向做完整个项目之后我觉得还可以探索两个方向一是基于这个基础镜像做一份完整的“设备画像自检工具”把AIDA64能读到的所有字段和Build属性、内核节点做一个对比诊断自动告诉你不一致的点在哪省得每次手动翻。二是研究一下AVB签名体系尝试自己做一套锁定的签名密钥让bootloader以一个伪装的locked状态启动。这一步如果走通了那么Momo上的最后一项黄灯就也能变绿这才是真正完善的设备画像。不过我也得提醒一句修改设备身份用于绕过金融级风控属于灰色甚至黑色领域大家不要在真实生产环境尝试。做安全研究请在专门测试机上跑或者只用来保护个人隐私、了解系统底层原理。技术本身是中性的但使用者的目的决定了它的价值。最后再分享一点小技巧如果你只是想做设备建模和测试不用每次改完整ROM可以先在源码里把对应变量加上OVERRIDE属性然后编译system.img分区块刷入不需要整机重刷。在system.img刷入后先用fastboot reboot看开机日志如果出现bootloop立刻fastboot boot boot.img进入临时内核排查能省下很多反反复复的完整刷机时间。定制ROM这件事最要紧的是耐心别指望一次全绿每一版改动都记录好出问题能快速回退这比什么优化配置都重要。
返回列表