简介:这份PDF文献面向Android客户端开发与移动安全方向的学习者、研究人员及安全从业者,围绕Android平台木马的设计与实现展开系统论述,帮助读者理解移动端隐私窃取与系统漏洞利用的技术原理,并探讨相应的防范思路。资源包内仅含1个PDF文件,压缩包整体约774KB,篇幅精炼,适合作为移动安全课程参考文献或毕业设计选题的入门资料。文中以木马系统总体框架为主线,拆解植入模块、木马实体与后台服务器三部分,详细分析通过浏览器及内核漏洞提权至root、静默安装恶意APK的植入流程,并阐述信息获取与数据回传的实现方法,同时结合2015年移动安全报告数据说明病毒爆发的严峻态势。目前已有227人学习下载,读者可借此了解Android系统在权限控制与内存保护方面的缺陷,为安全加固与木马防范研究提供有价值的参考。
1. 从“木马设计与实现”这个标题说起:Android 安全研究到底在研究什么
很多人第一次看到“Android 智能手机木马设计与实现”这类标题,第一反应是去找一份能直接跑的源码。但真正做过移动端安全研究的人会告诉你,这个标题背后其实是一整套 Android 应用层与系统层机制的拆解:权限模型、组件通信、后台保活、数据落盘路径、签名校验、ADB 调试通道。它解决的不是“写一个能用的恶意程序”,而是回答一个更基础的问题——一个普通 APK 在 Android 上究竟能拿到多少能力,边界在哪里,检测方又该从哪里切入。
适合读这篇的人有三类:做移动端安全测试的工程师、需要评估自家 App 被逆向和二次打包风险的安全负责人、以及想系统理解 Android 组件与权限机制的中高级开发者。下面按“机制 → 最小验证 → 检测与加固 → 进阶技巧”的顺序展开,所有代码都只用于本地授权环境的机制验证,不涉及任何真实攻击目标。
2. Android 权限模型与组件机制:木马类行为为什么能成立
2.1 从 AndroidManifest 看能力声明
Android 的能力边界几乎全部写在 AndroidManifest.xml 里。一个应用能做什么,先看它声明了什么权限、注册了哪些组件。理解这一点,比看任何源码都重要。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.labdemo">
<!-- 危险权限:运行时还需动态申请 -->
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<!-- 普通权限:安装即授予 -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application android:allowBackup="true">
<!-- 开机自启接收器,是后台驻留的常见入口 -->
<receiver android:name=".BootReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
</application>
</manifest>
逻辑说明: READ_SMS 、 ACCESS_FINE_LOCATION 属于危险权限,Android 6.0 之后必须在运行时通过 requestPermissions 动态申请,用户拒绝就拿不到数据。 INTERNET 和 RECEIVE_BOOT_COMPLETED 是普通权限,安装即生效。 BootReceiver 监听开机广播,是后台驻留最经典的入口之一。
参数说明: android:exported 决定组件能否被其他应用调用,Android 12 起凡是带 intent-filter 的组件必须显式声明该属性,否则安装直接失败。这是很多人移植旧项目时踩的第一个坑。
2.2 四大组件的攻击面差异
| 组件 | 典型风险 | 检测关注点 |
|---|---|---|
| Activity | 界面劫持、钓鱼覆盖 | exported 且无权限保护 |
| Service | 后台常驻、静默执行 | 前台服务通知是否真实 |
| BroadcastReceiver | 监听系统事件触发 | 是否监听敏感广播 |
| ContentProvider | 数据越权读写 | grantUriPermissions 配置 |
这张表是安全评估时的第一张检查清单。常见做法是用 aapt dump xmltree 或 apkanalyzer 直接导出清单,逐条核对 exported 属性。
2.3 用 ADB 做最小化行为验证
在授权测试机上,用 Android Debug Bridge 观察应用行为是最直接的手段。
# 查看已安装应用的权限授予情况
adb shell dumpsys package com.example.labdemo | grep -A 20 "grantedPermissions"
# 实时观察组件启动与广播
adb logcat -b events | grep -E "am_proc_start|am_broadcast"
# 查看应用私有目录落盘情况(需 root 或 debuggable 应用)
adb shell run-as com.example.labdemo ls -R /data/data/com.example.labdemo/
逻辑说明:第一条命令确认权限是否真的被授予;第二条从 events 缓冲区抓组件启动记录,比主 logcat 干净;第三条用 run-as 进入应用私有目录,只有 debuggable 应用或 root 设备可用。
参数说明: -b events 指定日志缓冲区,避免被业务日志淹没; grep -A 20 表示匹配行后再显示 20 行上下文。注意 Android 高版本对 run-as 限制更严,遇到 package not debuggable 就说明该应用无法这样查看。
提示:所有验证都应在自己拥有或获得书面授权的设备上进行,未授权测试他人设备属于违法行为。
3. 后台驻留与数据落盘:从机制到可复现的验证代码
3.1 前台服务与保活机制的真实边界
后台驻留是这类标题里被讨论最多的部分。Android 8.0 之后后台服务被严格限制,前台服务成为主要手段,但必须显示常驻通知。
public class LabService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 必须创建通知渠道,否则 Android 8.0+ 会抛异常
String channelId = "lab_channel";
NotificationManager nm = getSystemService(NotificationManager.class);
if (nm.getNotificationChannel(channelId) == null) {
NotificationChannel channel = new NotificationChannel(
channelId, "Lab", NotificationManager.IMPORTANCE_LOW);
nm.createNotificationChannel(channel);
}
Notification notification = new Notification.Builder(this, channelId)
.setContentTitle("运行中").setSmallIcon(R.drawable.ic_stat).build();
// 前台服务类型在 Android 14 必须声明,否则启动失败
startForeground(1, notification);
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) { return null; }
}
逻辑说明: startForeground 把服务提升为前台,系统不会轻易回收,但代价是必须挂一个用户可见的通知。 START_STICKY 表示服务被系统杀死后尝试重建。
参数说明: IMPORTANCE_LOW 让通知不发声,减少打扰;Android 14 起 startForeground 需要配合清单里的 foregroundServiceType ,比如 dataSync ,否则抛 MissingForegroundServiceTypeException 。这是近两年移植老代码最常见的崩溃点。
3.2 数据落盘路径与 content:// URI 的实际含义
热词里频繁出现 content://com.tencent.wework.fileprovider/... 这类 URI,它本质是 FileProvider 暴露的受控文件访问通道,不是任意路径。
// 通过 FileProvider 生成可共享的 content URI
File file = new File(getFilesDir(), "report.txt");
Uri uri = FileProvider.getUriForFile(
this,
getPackageName() + ".fileprovider", // 必须与清单中 authorities 一致
file);
Intent share = new Intent(Intent.ACTION_SEND);
share.putExtra(Intent.EXTRA_STREAM, uri);
share.setType("text/plain");
share.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // 临时授权读
startActivity(Intent.createChooser(share, "分享"));
逻辑说明: getUriForFile 把应用私有目录下的文件映射成一个 content:// URI,其他应用通过这个 URI 读取时受 FileProvider 的路径白名单约束。 FLAG_GRANT_READ_URI_PERMISSION 只授予本次读取权限,用完即失效。
参数说明:authorities 必须和清单里 <provider android:authorities="..."> 完全一致,写错会抛 IllegalArgumentException 。路径白名单在 res/xml/file_paths.xml 里配置,常见的是 external-path 和 files-path 。
3.3 用 Python 批量解析 APK 清单做静态筛查
安全评估阶段,批量看清单比逐个手点高效得多。
from androguard.misc import AnalyzeAPK
def scan_manifest(apk_path):
a, d, dx = AnalyzeAPK(apk_path)
print("包名:", a.get_package())
# 列出所有 exported 组件
for tag in ["activity", "service", "receiver", "provider"]:
for comp in a.get_elements(tag):
name = comp.get("android:name")
exported = comp.get("android:exported")
if exported == "true":
print(f"[{tag}] {name} exported=true")
scan_manifest("labdemo.apk")
逻辑说明: AnalyzeAPK 解析 APK 得到清单对象 a ,遍历四类组件,筛出 exported=true 的条目。这些是外部可触达的入口,是评估重点。
参数说明: get_elements 返回指定标签的所有节点; get 取属性值,注意属性名带 android: 前缀。androguard 需要单独安装,解析大型 APK 时内存占用较高,建议逐个处理。
注意:静态筛查只能发现声明层面的问题,实际行为仍需动态验证,两者不能互相替代。
4. 检测与加固:签名校验、完整性检查与常见绕过点
4.1 应用签名与 SHA1 值的获取
二次打包是这类风险的核心场景,签名校验是第一道防线。先拿到自己的签名指纹。
# 从 keystore 提取签名信息
keytool -list -v -keystore my.jks -alias mykey
# 从已安装 APK 提取签名(需 apksigner)
apksigner verify --print-certs app-release.apk
逻辑说明: keytool -list -v 输出证书的 SHA1、SHA256 指纹; apksigner verify --print-certs 直接读 APK 里的签名块。两者结果应一致。
参数说明: -v 表示详细输出; --print-certs 打印证书链。SHA1 值在接入第三方 SDK(如地图、推送)时经常要填,务必用 release 签名而非 debug 签名。
4.2 在代码里做签名与完整性校验
public static boolean checkSignature(Context ctx) {
try {
PackageInfo info = ctx.getPackageManager().getPackageInfo(
ctx.getPackageName(), PackageManager.GET_SIGNATURES);
Signature[] signs = info.signatures;
String current = sha256(signs[0].toByteArray());
// 与预置的合法签名哈希比对
return EXPECTED_SHA256.equals(current);
} catch (Exception e) {
return false;
}
}
逻辑说明:运行时读取自身签名,计算哈希后与硬编码的期望值比对,不一致说明被重打包。
参数说明: GET_SIGNATURES 在 Android 9 之后逐步被 GET_SIGNING_CERTIFICATES 取代,新项目建议用后者。硬编码的期望值本身也可能被逆向修改,所以通常还要配合代码混淆和完整性校验。
4.3 常见绕过点与加固思路
| 绕过手段 | 对应加固 |
|---|---|
| 反编译改签名校验返回值 | 校验逻辑分散、native 层校验 |
| 动态调试附加进程 | 反调试、检测调试器 |
| Hook 关键方法 | 完整性自检、多路径校验 |
| 重打包替换资源 | 资源哈希校验 |
这张表说明单点校验很容易被绕过。常见做法是把校验拆到多处,并在 native 层做一次,提高逆向成本。加固不是绝对安全,而是抬高攻击成本。
5. 进阶技巧:用动态分析与日志关联定位真实行为
静态看清单只能得到“声明了什么”,要确认“实际做了什么”,得把动态日志和文件系统变化关联起来。一个实用技巧是同时抓 events 缓冲区和应用私有目录快照,做前后对比。
# 抓取一段时间内的组件与进程事件
adb logcat -b events -v time > events.log &
LOGPID=$!
sleep 30
kill $LOGPID
# 对比应用私有目录变化
adb shell run-as com.example.labdemo find /data/data/com.example.labdemo -newer /data/data/com.example.labdemo > changed.txt
逻辑说明:后台抓 events 日志 30 秒,同时记录这段时间内被修改的文件。把两份数据按时间对齐,就能看出某个广播触发后应用写了哪些文件、启动了哪些组件。
参数说明: -v time 给日志加时间戳,便于对齐; find -newer 以某个文件为基准找更新的文件,这里用目录自身做基准是个简化写法,实际可先 touch 一个基准文件。注意 run-as 对非 debuggable 应用无效。
另一个技巧是关注 content:// URI 的授权传播。用 dumpsys activity providers 可以看当前有哪些 provider 处于活跃状态、授予了哪些 URI 权限,这对理解跨应用数据流动很关键。把这些观察和清单里的 exported 声明对照,往往能发现声明与实际行为不一致的地方,这正是安全评估最有价值的产出。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/weixin_29186249/article/details/165978915



