摘要: 最近分析了一个名为xaingce.apk(md5:4bc87096a1c5a33479f1ca8b82f3c130)的恶意apk样本,其主要恶意行为是窃取用户隐私,包括通讯录和短信等。样本采用了一些对抗静态分析的技术,本文就如何通过调试发现其对抗的方法进行介绍...
最近分析了一个名为xaingce.apk(md5:4bc87096a1c5a33479f1ca8b82f3c130)的恶意apk样本,其主要恶意行为是窃取用户隐私,包括通讯录和短信等。样本采用了一些对抗静态分析的技术,本文就如何通过调试发现其对抗的方法进行介绍。分析用到Apktool,Jeb和Eclipse等工具,所涉及到的使用步骤也将会加以说明。
1 问题
通常分析apk样本,最常用的工具就是Jeb。打开样本后,首先查看Manifest,明确Activity等关健组件,以及申请了哪些比较有威胁的权限(如:READ_CONTACTS,READ_SMS)。
其次,可以查看Resources,看一下是否有感兴趣的内容,特别是 values/strings.xml中的字符串。然而这个样本在Jeb打开后,Manifest标签中的内容却是空的,Resources标签也不存在。
Manifest和Resource肯定在样本文件中是存在的,那么在Jeb中没能显示出来,一定是解析出了问题。 当然,这其实也不会使分析进行不下去,因为Java源码还是可以逆出来的。但是出于研究的目的,还是希望能找出其中的原因。
细心的读者可能会提出疑问,若是AndroidManifest.xml损坏了,这样的apk还能正常安装和运行吗?
对于该样本,答案是YES。
所以,应该是程序作者利用了Android的这种较强的容错能力,来对抗分析工具。 这种情况我在之前分析word样本时也是很常遇到的。
2 搭建调试环境
这里我们要调试的不是Jeb,而是Apktool,一方面当然是因为Apktool开源,另外Jeb在About的第三方软件列表中也列有Apktool,所以我猜测Jeb逆向apk用的也是Apktool(未确认)。
2.1 下载和编译Apktool
- 下载源码 $ git clone https://github.com/iBotPeaches/Apktool $ cd Apktool
- 应用smali补丁,创建brut.apktool.smali目录 $ ./gradlew applyPatches [注] 这一步要联网下载文件,需要视网络状况,等待一段时间。
- 编译 $ ./gradlew build fatJar [注] 在我的环境中,build会因为部分test不通过而失败,此时单独再执行”./gradlew fatJar”也是可以成功的。
2.2 导入项目到Eclipse中
我用的Eclipse版本为4.4.2,并安装gradle插件(https://marketplace.eclipse.org/content/gradle-integration-eclipse-0)
- 在File->Import对话框选择Gradle->Gradle Project
- 下一步,选择Apktool源码所在目录,执行Build Model,然后选中所有Project
- 完成导入
3 调试分析
3.1 配置debug参数
- 在Project Explorer中的apktool-cli项目上单击右键,选择Debug As->Debug Configuration
- 在Java Application条目上单击右键,选择New
- 选择Main class,单击Search,选择”Main – brut.apktool”
- 配置启动参数,”d -f -s -o /tmp/xaingce /tmp/xaingce.apk”
参数说明:
d decode
-f Force delete destination directory.
-s Do not decode sources.
-o The name of folder that gets written.
3.2 第一次试错
配置好debug参数后,可以直接单击Debug开始调试,这时可能会提示smali和apktool-lib项目中存在错误,忽略即可。 然后,我们会在Console中得到以下的异常信息:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | Exception in thread "main" brut.androlib.AndrolibException: Could not decode XML at brut.androlib.res.decoder.XmlPullStreamDecoder.decode(XmlPullStreamDecoder.java:147) at brut.androlib.res.decoder.XmlPullStreamDecoder.decodeManifest(XmlPullStreamDecoder.java:153) at brut.androlib.res.decoder.ResFileDecoder.decodeManifest(ResFileDecoder.java:140) at brut.androlib.res.AndrolibResources.decodeManifestWithResources(AndrolibResources.java:208) at brut.androlib.Androlib.decodeManifestWithResources(Androlib.java:140) at brut.androlib.ApkDecoder.decode(ApkDecoder.java:103) at brut.apktool.Main.cmdDecode(Main.java:165) at brut.apktool.Main.main(Main.java:81) Caused by: java.io.IOException: Expected: 0x00080003, got: 0x00080000 at brut.util.ExtDataInput.skipCheckInt(ExtDataInput.java:48) at brut.androlib.res.decoder.AXmlResourceParser.doNext(AXmlResourceParser.java:839) at brut.androlib.res.decoder.AXmlResourceParser.next(AXmlResourceParser.java:96) at brut.androlib.res.decoder.AXmlResourceParser.nextToken(AXmlResourceParser.java:106) at org.xmlpull.v1.wrapper.classic.XmlPullParserDelegate.nextToken(XmlPullParserDelegate.java:105) at brut.androlib.res.decoder.XmlPullStreamDecoder.decode(XmlPullStreamDecoder.java:140) ... 7 more |
在此就不卖关子了,很显然存在一处预期的值是0x00080003,而得到的值是0x00080000,实际上这就是axml格式的前4个bytes,是固定值。
下面来说一下如何用apktool来重打包,然后继续后面的分析。
3.3 使用Apktool重打包
这个过程在Shell中操作比较方便,在编译过Apktool之后,我们可以在brut.apktool/apktool-cli/build/libs目录下得到 apktool-cli.jar,只要使用”java -jar apktool-cli.jar”就可以执行相应命令行操作了。
- decode
$ java -jar apktool-cli d -f -r -s -o /tmp/xaingce /tmp/xaingce.apk
这里比调试时多了”-r Do not decode resources.”参数,可以避免解包出错。
- 修改AndroidManifest.xml
注意此时的AndroidManifest.xml并不是明文格式,而是axml格式。
我们可以使用010Editor(或其它二进制编辑工具)来修改,将第1个字节的”00″修改为”03″。
- build
$ java -jar apktool-cli b -f -o /tmp/xaingce_re.apk /tmp/xaingce.apk
参数说明:
1 2 3 4 | b build -f Skip changes detection and build all files. -o The name of apk that gets written. |
这样我们得到重打包后的xaingce_re.apk。
3.4 继续调试分析
现在我们把Debug的启动参数修改为”d -f -s -o /tmp/xaingce /tmp/xaingce_re.apk”,再次运行。 当然,本应该是不知道会不会再次异常,不过如果运行正常,本文也就没有再写下去的必要了:)
此时得到的异常是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | Exception in thread "main" brut.androlib.AndrolibExceptio Could not decode XML at brut.androlib.res.decoder.XmlPullStreamDecoder.decode(XmlPullStreamDecoder.java:147) at brut.androlib.res.decoder.XmlPullStreamDecoder.decodeManifest(XmlPullStreamDecoder.java:153) at brut.androlib.res.decoder.ResFileDecoder.decodeManifest(ResFileDecoder.java:140) at brut.androlib.res.AndrolibResources.decodeManifestWithResources(AndrolibResources.java:208) at brut.androlib.Androlib.decodeManifestWithResources(Androlib.java:140) at brut.androlib.ApkDecoder.decode(ApkDecoder.java:103) at brut.apktool.Main.cmdDecode(Main.java:165) at brut.apktool.Main.main(Main.java:81) Caused by: java.io.EOFException at java.io.DataInputStream.readFully(DataInputStream.java:197) at com.mindprod.ledatastream.LEDataInputStream.readFully (LEDataInputStream.java:201) at brut.util.DataInputDelegate.readFully(DataInputDelegate.java:69) at brut.androlib.res.decoder.StringBlock.read(StringBlock.java:66) at brut.androlib.res.decoder.AXmlResourceParser.doNext (AXmlResourceParser.java:845) at brut.androlib.res.decoder.AXmlResourceParser.next(AXmlResourceParser.java:96) at brut.androlib.res.decoder.AXmlResourceParser.nextToken(AXmlResourceParser.java:106) at org.xmlpull.v1.wrapper.classic.XmlPullParserDelegate.nextToken(XmlPullParserDelegate.java:105) at brut.androlib.res.decoder.XmlPullStreamDecoder.decode(XmlPullStreamDecoder.java:140) ... 7 more |
接下来我们将EOFException加入到Breakpoints中。
单击Breakpoints工具栏上的”Add Java Exception Breakpoint”图标,搜索EOFException并添加。
重新再运行,这时第一次断下来的位置,经分析是有异常处理的,在此不做赘述。 继续(F8),当再次断下来,查看调用栈和变量:
我们可以从上至下逐步查看调用,当查看到StringBlock.read,注意到block.m_strings的size是134217564,一个非常大的值。
然后再看size是如何得到的(StringBlock.java的64行):
1 2 | int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset; |
那么,stylesOffset的值是134217728,换成16进制为0x80000000,这明显已经超出了文件的总长度。 这样,也就定位到问题的原因了。
3.5 第二次重打包
之前已经解开过apk文件了,所以只要将错误的地方修改后打包即可。
关健是修改的偏移和数值,这涉及到axml文件格式的说明,属于另外文章要讲述的内容。
在此推荐一个010Editor模板,应用模板后就很容易定位这个位置了。
模板下载: https://github.com/tomken/010_template_for_android
实际上该StringBlock中并不包含styleString,因为styleCount的值为0。 所以,只要将styleOffset修改为0就正确了。
具体来说,就是将0x23位置的”08″改为”00″。
- build
$ java -jar apktool-cli b -f -o /tmp/xaingce_ok.apk /tmp/xaingce.apk
3.6 验证结果
经过以上的修改,使用Jeb打开xaingce_ok.apk,此时已能够正确查看Manifest和Resources了。
4 样本行为分析
最后,我们还是来关心一下这个样本有什么恶意行为,那么还涉及到一些额外要做的工作。
4.1 提取样本中包含的apk
这个样本的assets中包含另一个apk,样本运行后会自动提取并安装。
我们可以从之前解包的目录中获取这个名为com.android.Trogoogle.apk的文件。
这个apk同样采用了之前所讲的反静态分析方法,对应修改后就能够正常使用Jeb分析了。
这个样本人工分析起来并不算复杂,不过,这次我们使用”翠鸟“来动态分析。
因为初始样本会令androgurad异常,使得”翠鸟”的分析流程因为无法得到一些关健数据而中断,修改了文件格式后就没有这个问题了。
但是,由于使用Apktool重打包的文件数字签名不正确,将无法安装,所以还需要重新签名。
4.2 签名
详细说明请参看: http://developer.android.com/tools/publishing/app-signing.html
我们只要签debug模式的签名就可以了:
1 2 3 4 5 | $ jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore ~ /.android/debug.keystore \ com.android.Trogoogle.apk androiddebugkey Enter Passphrase for keystore: <=== Passphrase为"android" |
4.3 恶意行为
在文章开始,其实就已经揭示了该样本的恶意行为,主要是窃取短信和通讯录。那么从”翠鸟”的分析报告中也清楚的看到这些恶意行为。
另外我们还注意到,该样本请求解析”smtp.21cn.com”这个域名:
然后我们从网络数据的记录中果然查了样本上传隐私数据的邮箱账号及密码。
登录后:
5 小结
恶意样本通过修改二进制格式文件中的一些offset或size等数值,来破坏分析程序的解析,但又不影响其在真实系统中运行,以此来逃避自动分析。
当然,反之也可以通过对应的格式解析和校验,来检测这类恶意样本。
如果您需要了解更多内容,可以
加入QQ群:486207500