☰
基于AOSP定制ROM绕过Momo与AIDA64检测的实战指南
2026/10/5 1:33:25 网站建设 项目流程

搞安卓底层开发的朋友,应该都知道一个尴尬的现状:很多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. 环境搭建:从零开始编译AOSP

2.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 a+x ~/bin/repo export PATH=~/bin:$PATH git config --global user.name "Your Name" git config --global user.email "you@example.com"

2.2 拉取AOSP源码与分支选择

这里有个决策点:到底拉哪个安卓版本。我之前用Android 12(android-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 -j16

repo 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、等等。

编译完原版后,先整一个干净的基线。后面每次修改,我都遵循三步流程:

  1. 修改源码文件
  2. 重新执行make
  3. 用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.mk(Pixel 4的内部代号flame),里面有PRODUCT_MODEL等定义,但是很多ro.*属性是在build/make/core/sysprop.mk等地方默认生成的。为了精确改,我找到/system/build.prop里的关键行,再反查源码变量。

最终我改的字段是这个列表:

Build属性原值(Pixel 4)修改后(假装一加7 Pro)
ro.product.modelPixel 4OnePlus7Pro
ro.product.brandgoogleoneplus
ro.product.nameflameOnePlus7Pro
ro.product.deviceflameOnePlus7Pro
ro.product.manufacturerGoogleOnePlus
ro.build.fingerprintgoogle/flame/flame:10/QD1A.190821.007/...oneplus/OnePlus7Pro/OnePlus7Pro:10/QKQ1.190716.003/...
ro.build.version.release1010
ro.build.version.security_patch2020-01-052020-05-01
ro.build.display.idQD1A.190821.007QKQ1.190716.003
ro.build.flavorflame-userdebugOnePlus7Pro-user
ro.com.google.clientidbaseandroid-googleandroid-oneplus
ro.oem_unlock_supported10

这里坑很多。只改model和manufacturer没用,AIDA64的“设备信息”页面还会读取ro.product.name和ro.product.device,Momo则通过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.model=OnePlus7Pro \ ro.product.brand=oneplus \ ro.product.name=OnePlus7Pro \ ro.product.device=OnePlus7Pro \ ro.product.manufacturer=OnePlus \ ro.build.fingerprint=oneplus/OnePlus7Pro/OnePlus7Pro:10/QKQ1.190716.003/28H.191007.113:user/release-keys \ ro.build.display.id=QKQ1.190716.003 \ ro.build.version.security_patch=2020-05-01

注意fingerprint里的版本字符串日期必须和ro.build.version.*匹配,AIDA64虽然不校验,但Momo会读Build.VERSION的各个字段做内部一致性判断。

3.2 修改内核信息、SELinux状态和调试标记

Build属性只是表面,Momo最恶心的一点是会通过/proc自己读内核参数。几个关键节点:

  • /proc/cmdline:里面写了androidboot.veritymode=enforcing之类的,Momo会检查有没有可疑参数
  • /proc/version:Linux内核编译信息,包含编译器版本、内核版本号
  • /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 (oneplus@swdev) (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.selinux=enforcing BOARD_VERITY_MODE := enforcing

另外还在device.mk中删除了debug相关的persist属性,把ro.debuggable从1改成0,ro.secure改成1,ro.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读取MemTotal
  • GPU信息:从/sys/kernel/gpu读取
  • 屏幕分辨率:从DisplayInfo或者读取/sys/class/graphics/fb0参数

我实践下来优先级最高的是/proc/cpuinfo。很多检测App都拿它做基准。Pixel 4用的是骁龙855,cpuinfo里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里加mem=8192M可以强制限制内存大小,但往大了加没门。所以改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不同于硬件信息工具,它的重点在“系统环境异常”。我实测下来,想让它报绿色,下面几点必须做到:

  1. 必须使用user编译类型,而不是userdebug或eng。userdebug自带root漏洞、ro.debuggable=1,Momo对这些敏感度极高。把lunch的目标切到aosp_flame-user即可,同时把persist.sys.root_access设为0。

  2. 去掉所有root相关二进制。AOSP默认没有su,但很多定制ROM都会有。确认整个system/vendor里没有su二进制,关键文件如/system/xbin/su、/system/bin/su、/system/app/Superuser.apk都不存在。

  3. 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。

  4. 系统分区必须只读且验证启动正常。Momo会检查system分区是不是rw挂载,检查verifyboot状态。定制ROM如果关闭了dm-verity,很容易被标记。所以要么保留原版bootloader的verity,要么自己生成匹配的vbmeta签名,否则fastboot会显示orange状态,Momo一样报告bootloader已解锁。这里我踩了大坑,后面单独讲。

  5. 避免系统应用context异常。比如你加了一个新系统应用,但没给它写sepolicy,它运行时SELinux报avc denied,Momo可能去检查应用的安全上下文异常,很容易告警。解决方法是额外封装一个系统应用,给它分配一个已有的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,必须自己生成一套可信的AVB(Android 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,我用了mem=6G内核参数,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 编译失败和产物异常怎么排查

我遇到过最频繁的编译问题是:

  1. No rule to make target xxx,多半是驱动包没有正确解压到vendor目录。重新执行驱动脚本后make clean再试。
  2. Out of memory,加swap,或者降低-j参数。
  3. Jack server相关报错在老版本上常见,Android 10默认用Jack编译Java代码,需要设置JACK_SERVER_VM_ARGUMENTS=-Xmx4096m,或者干脆升级到更高版本避免这问题。
  4. 修改了build.prop后编译不生效,因为product包缓存。执行make clean然后重新编译镜像。
  5. 烧录后开机无限重启,很可能是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 = b"Linux version 4.14.190-g3c131a0c" new = b"Linux 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:idx+len(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.debuggable=1userdebug/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这件事,最要紧的是耐心,别指望一次全绿,每一版改动都记录好,出问题能快速回退,这比什么优化配置都重要。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询