[翻译]DIY:Android恶意应用分析-分解OBAD(1)
原文:DIY: Android Malware Analysis – Taking apart OBAD (part 1)
http://securityintelligence.com/diy-android-malware-analysis-taking-apart-obad-part-1/
“知己知彼,百战不殆;不知彼而知己,一胜一负;不知彼,不知己,每战必殆。”
-孙子兵法
我计划定期写一些DIY系列的文章,目标不仅在于了解恶意软件、漏洞和利用,也与我们的读者分享一些技术和工具,他们可以使用自己“知彼”。我也期待听到你在分析类似应用的经验,相似或更有效的工具和技术。
好吧,让我们正式开始,OBAD已被确认为安卓恶意软件最精妙作品之一 ,你可以找到各种分析结果。在这个系列中,我们将它拆开了解其功能和各种技术,它是很强大的能免卸载也很难分析。在第一部分中,我们将看到如何使用动态代码分析,使用jdb调试,smali(Dalvik的反汇编源代码)级别的调试,使用JDB命令来了解被调用的反射代码和字符串解密,使用IDA理解可视化控制和数据流,修改AOSP代码和构建定制系统映像来绕过OBAD使用的反模拟器技巧。
一、从哪里得到它
我们将使用示例MD5:e1064bfd836e4c895b569b2de4700284(VirusTotal analysis )。你可以从Contagio Mobile下载它
二、查看APK文件的内容
apk文件可以简单地压缩如zip文件一样,其中包括二进制格式的 manifest文件,这通常可以转换成一个可读的文件,下载AXMLPrinter2.jar并运行
Shell java –jar AXMLPrinter2.jar AndroidManifest.xml
解压缩后apk文件后,你将获得一个classes.dex文件,该文件是一个包含应用程序代码的 dex文件 。
三、初见 - 让它运行在安全的环境
为了加快速度,我建议使用Mobisec,但它需要大量升级工作,但确实是个不错的选择能让事情进展更加迅速。它配备了大量的预装工具,你可以让它在虚拟机上运行(VirtualBox是一个漂亮整洁和强大的免费虚拟化解决方案)。在虚拟机中这样做的另一个好处是,你可以在想要的时间间隔拍摄快照,分析过程中机器可以随时恢复,并尝试分析其他线路。
我已经创建了一个简短的文本来记录mobisec VM升级android sdk的步骤,下载python 2.7并安装drozer(搜了一下发现原名Mercury,这个非虫书中介绍过了)——一个非常强大的框架用来测试和分析android应用程序。你可以从Upgrading_SDK_in_mobisec_and_installing_drozer得到它
你能做的第一件事就是向一些在线分析服务提交,然后得到样品的概述(记住,如果你不注意这可能会成为一个有针对性的攻击,通过提交在线服务你可以警告攻击者)。这里有一些在线分析服务:
* 非常强大的动态分析工具由JoeSecurity支持的沙箱和分析技术, 这是他们提供的免费APK Analyzer分析服务
* 一个不错的在线静态分析和图形工具dexter
这样你可以使用这些工具继续尝试OBAD样品,或者还是让我们自己看看可以发现什么。
如果你还不熟悉Android SDK,我建议你花一些时间来看看这些:
* Exploring SDK
* AVD
* Emulator
* ADB
我开始用AVD启动模拟器, SDK为Android 4.0.3 running api android-15 r3:
Shell mobisec@Mobisec:/opt/mobisec/devtools/android-sdk/tools$ emulator-arm -avd Android_4.0.3 -scale 0.75 -debug all -logcat all -no-boot-anim
一旦模拟器运行,你可以安装样本如下:
Shell mobisec@Mobisec-VM:~$ adb install Malware/OBad/E1064BFD836E4C895B569B2DE4700284.apk 147 KB/s (84306 bytes in 0.558s) pkg: /data/local/tmp/E1064BFD836E4C895B569B2DE4700284.apk Success
在这之后,如果你去查看应用程序安装列表是看不到任何新启动器图标的。让我们看看一些日志记录,我们可以使用logcat,通过运行“adb logcat”可以看到下面的相关信息(想要更多细节,您可以运行“adb logcat -d - v long”:
Shell
$> adb shell logcat | grep -Ei "E1064B|system.admin"
W/ActivityManager( 80): No content provider found for permission revoke: file:///data/local/tmp/e1064bfd.apk
D/dalvikvm( 493): GC_CONCURRENT freed 400K, 8% free 6526K/7047K, paused 11ms+4ms
W/ActivityManager( 80): No content provider found for permission revoke: file:///data/local/tmp/e1064bfd.apk
I/PackageManager( 80): Running dexopt on: com.android.system.admin
D/dalvikvm( 753): DexOpt: 'Lcom/android/internal/telephony/IExtendedNetworkService;' has an earlier definition; blocking out
D/dalvikvm( 753): DexOpt: not verifying/optimizing 'Lcom/android/internal/telephony/IExtendedNetworkService;': multiple definitions
D/dalvikvm( 753): DexOpt: load 77ms, verify+opt 1198ms
I/ActivityManager( 80): Force stopping package com.android.system.admin uid=10042
D/PackageManager( 80): New package installed in /data/app/com.android.system.admin-1.apk
W/PackageManager( 80): Unknown permission android.permission.READ_EXTERNAL_STORAGE in package com.android.system.admin
W/PackageManager( 80): Not granting permission android.permission.MODIFY_PHONE_STATE to package com.android.system.admin (protectionLevel=3 flags=0x8be44)
W/PackageManager( 80): Not granting permission android.permission.WRITE_SECURE_SETTINGS to package com.android.system.admin (protectionLevel=3 flags=0x8be44)
W/PackageManager( 80): Unknown permission android.permission.ACCESS_BLUETOOTH_SHARE in package com.android.system.admin
D/dalvikvm( 80): GC_CONCURRENT freed 345K, 8% free 8789K/9543K, paused 9ms+13ms
I/AppSecurityPermissions( 223): Ignoring unknown permission:android.permission.READ_EXTERNAL_STORAGE
I/AppSecurityPermissions( 223): Ignoring unknown permission:android.permission.ACCESS_BLUETOOTH_SHARE
D/PackageManager( 80): generateServicesMap(android.accounts.AccountAuthenticator): 2 services unchanged
D/PackageManager( 80): generateServicesMap(android.content.SyncAdapter): 4 services unchanged
D/BackupManagerService( 80): Received broadcast Intent { act=android.intent.action.PACKAGE_ADDED dat=package:com.android.system.admin ***=0x10000010 (has extras) }
V/BackupManagerService( 80): addPackageParticipantsLocked: com.android.system.admin我们如何知道这个apk安装的软件包名称是什么?你可以通过静态分析工具,或者仅仅是从logcat看最近条目,或通过查看安装包列表之前和之后的差异(“adb shell pm list packages”将派上用场,或drozer的 app.package.list)
因此,让我们试目以获取有关我们刚刚安装的软件包的一些信息,很多信息可以通过SDK中的adb工具提取出来,我也喜欢用drozer 工具来获取任何有关的信息。
一旦你在drozer控制台,您可以尝试这些以获取有关包的一些信息:
Shell mobisec@Mobisec-VM:~$ adb forward tcp:31415 tcp:31415 mobisec@Mobisec-VM:~$ sudo drozer console connect dz> cd app.package dz#app.package> run info -a com.android.system.admin dz#app.package> run attacksurface com.android.system.admin dz#app.package> run manifest com.android.system.admin # More interestingly: dz#app.package> run launchintent com.android.system.admin tells us that the launcher activity for this package com.android.system.admin.CCOIoll # Now if we wanted to manually launch this activity we can do so via: dz#app.activity> run start --component com.android.system.admin com.android.system.admin.CCOIoll # if we want to use the sdk tools only we can start this activity as: mobisec@Mobisec:~$ adb shell am start -a android.intent.category.LAUNCHER -n com.android.system.admin/.CCOIoll
四、调试应用程序
我现在将描述如何去调试这个应用程序。你可以在这里读到调试的基本用法和可用的选项。我使用jdb命令行调试。注意,我们已经运行了模拟器并安装了apk文件。于是我开始使用监控工具DDMS。
Shell # start the monitor tool /opt/mobisec/devtools/android-sdk/tools/monitor & # this has DDMS that can port forward any VM's specific debugging port to the standard port used by jdb which is 8700
在仿真器中,转到应用程序视图中,单击devtools,然后选择“开发设置”,在“调试应用程序”,点击应用程序的名称(通常是没有默认情况下),然后从应用程序列表中向下滚动并选择com.android。 system.admin。也可以选择“等待调试器”。
模拟器进入应用程序视图,点击devtools然后移选择“Development Settings”,点击应用名称(通常没有默认情况下)在“Debug app”,然后从应用程序列表向下滚动并选择com.android.system.admin,也可以选择“wait for debugger”

现在,您可以使用drozer或SDK工具启动应用程序
Shell dz#app.activity> run start --component com.android.system.admin com.android.system.admin.CCOIoll OR mobisec@Mobisec:~$ adb shell am start -a android.intent.category.LAUNCHER -n com.android.system.admin/.CCOIoll
然后,应用程序将等待附加调试器,应用程序的调试端口将被转发到默认端口8700。
我使用的命令行调试工具JDB。请注意,一旦一个调试器附加到应用程序,了waitForDebugger方法检查时看到的最后一次活动发生在调试器,如果没有事情发生在一定的时间段,然后恢复它的应用程序,以便它设置任何断点等等英寸jdbrc是很重要的文件在读取加多宝在启动时的主目录。
我使用命令行调试工具jdb。注意,一旦调试器连接到应用程序,waitForDebugger方法会检查发生在调试器的最后一个活动,如果在一定时间什么都没有发生,调试器就会恢复应用,所以在jdb读取启动的主目录jdbrc文件设置断点等是很重要的。
jdb可以被附加到应用程序等待调试器:
mobisec@Mobisec:~$ jdb -attach localhost:8700
在附加调试器之前,这将是一个创建一个虚拟机快照很好的点,这样我们就可以在随后的应用运行时尝试不同的调试断点。
我试图通过设置一个launcher activity CCOIoll的onCreate上的断点来调试这个程序,但它从来没有被加载上。所以我尝试以下操作:
五、获取该应用程序所有方法导入导出表:
这可以通过在在用户的home目录.jdbrc文件中下面一行来完成。
trace go methods
但这并不能正常工作,因为它引起太多的调试活动而应用程序也无法走出android.os.Debug.waitForDebugger(),只要退出超过一个设置时间lastDebuggerActivity()的时间周期已结束。所以后来我意识到让我们尝试打破应用程序入口点,即为我们的例子中添加了以下.jdbrc文件
Shell stop in com.android.system.admin.COcCccl.onCreate and attached the jdb to the app: mobisec@Mobisec-VM:~$ jdb -attach localhost:8700 Set uncaught java.lang.Throwable Set deferred uncaught java.lang.Throwable Initializing jdb ... *** Reading commands from /home/mobisec/.jdbrc Deferring breakpoint com.android.system.admin.COcCccl.onCreate. It will be set after the class is loaded. > > Set deferred breakpoint com.android.system.admin.COcCccl.onCreate Breakpoint hit: "thread=<1> main", com.android.system.admin.COcCccl.onCreate(), line=4,327 bci=0 <1> main[1]
当这个断点附加上了,那么你可以这样运行
<1> main[1] trace go methods <1> main[1] cont [One can also run trace go methods for just the main thread]
这给了我们一个代码执行哪些部分的想法,通过比较出入列表我们可以看到进入了com.android.system.admin.COcCccl.onCreate()但没退出,所以那里有些东西让我们退出应用程序。我们看不到任何方法输入,这告诉我们一些反VM检查正在运行不让我们看到任何java方法,然后导致了应用程序关闭。如下原因之一:
Shell "exclude" feature in jdb, running help in jdb and we see exclude [<class pattern>, ... | "none"] -- do not report step or method events for specified classes issuing the exclude command on jdb prompt we see > > exclude java.*,javax.*,sun.*,com.sun.*,
当我们运行跟踪go方法时没有看到从包中输入/输出的方法。
那么让我们来尝试在java.lang.System.exit设置一个断点,我们已经有一个虚拟机快照可以恢复,编辑.jdbrc文件(放在“stop in java.lang.System.exit(int) “),再次运行该应用程序,JDB附加,这样我们可以看到:
Shell mobisec@Mobisec-VM:~$ vi ~/.jdbrc;jdb -attach localhost:8700 Set uncaught java.lang.Throwable Set deferred uncaught java.lang.Throwable Initializing jdb ... *** Reading commands from /home/mobisec/.jdbrc Set breakpoint java.lang.System.exit(int) > > cont Nothing suspended. > Breakpoint hit: "thread=<1> main", java.lang.System.exit(), line=181 bci=0 <1> main[1] wherei [1] java.lang.System.exit (System.java:181), pc = 0 [2] com.android.system.admin.COcCccl.onCreate (null), pc = 1,041 [3] android.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:969), pc = 0 [4] android.app.ActivityThread.handleBindApplication (ActivityThread.java:3,954), pc = 729 [5] android.app.ActivityThread.access$1300 (ActivityThread.java:123), pc = 0 [6] android.app.ActivityThread$H.handleMessage (ActivityThread.java:1,185), pc = 177 [7] android.os.Handler.dispatchMessage (Handler.java:99), pc = 20 [8] android.os.Looper.loop (Looper.java:137), pc = 122 [9] android.app.ActivityThread.main (ActivityThread.java:4,424), pc = 34 [10] java.lang.reflect.Method.invokeNative (native method) [11] java.lang.reflect.Method.invoke (Method.java:511), pc = 17 [12] com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:784), pc = 11 [13] com.android.internal.os.ZygoteInit.main (ZygoteInit.java:551), pc = 66 [14] dalvik.system.NativeStart.main (native method) <1> main[1]
是时候深入到源代码了。
六、对于Android应用源代码和smali代码调试
还记得前面我们谈到了从apk文件得到的dex文件。Android的代码通常是用Java编写的,Java类文件都是转换成.dex(即Dalvik可执行的)文件,该文件在运行 Dalvik Android虚拟机。
1、你可以对.dex文件做些什么?
我喜欢下列选项(也有可能是其他):
* IDA pro能够反汇编.dex文件,显示图形并提供其他分析功能
* 使用dex2jar可以从.dex文件得到一个java的jar文件,然后使用一个Java反编译器得到Java源文件(如JD-GUI)
* 使用商用的Android反编译器,从apk文件得到Java源代码
* 您可以直接使用smlai得到Dalvik汇编代码,这是为DEX格式提供的一个汇编/反汇编器用来直接看smali代码,以避免从Java反编译器由于恶意软件引起的任何java反编译错误和误导输出。
* apktool是一个非常强大的Android应用逆向工具,能修改的Dalvik代码,apk重打包,源代码级的smali代码调试等
2、smali源码级调试
apktool v2相当不错的支持smali调试,在写这篇文章的时候,第2版没有公布,但可以从源代码构建
一旦你已经建立了apktool v2,您可以执行以下操作:
Shell
#decompile the apk with -d (debugging)
c:\downloads\apktool_2\Apktool\brut.apktool\apktool-cli\build\libs>java -jar apktool-cli-2.0.0-Beta5.jar d -d -o decompiled_with_apktool_2_with_debug d:\OBad\E1064BFD836E4C895B569B2DE4700284.apk
This will give you (among other things) java source files with smali code, e.g.
you will find COcCccl.java in decompiled_with_apktool_2_with_debug\smali\com\android\system\admin
and if you look at the code for onCreate you would see it as:
a=0;// # virtual methods
a=0;// .method public onCreate()V
a=0;// .locals 10
a=0;//
a=0;// invoke-super {p0}, Landroid/app/Application;->onCreate()V
a=0;//
a=0;// invoke-direct {p0}, Lcom/android/system/admin/COcCccl;->oIOccOcl()Z
a=0;//
a=0;// move-result v0
a=0;//
a=0;// #v0=(Boolean);
a=0;// if-eqz v0, :cond_0
a=0;//
a=0;// const/4 v0, 0x1
a=0;//
a=0;// #v0=(One);
a=0;// invoke-static {v0}, Ljava/lang/System;->exit(I)V我们将讨论如何在短时间内让调试起作用,让我们来看看如何重打包一个apk文件。如果你喜欢,你可以重打包之前修改的smali代码。
3、apk重打包
重打包之前要删除MANIFEST文件夹其中包含了签名和证书,这样你才可以重新签名。
请注意,由于OBAD使用混淆工具而成非标准的Andr??oid manifest文件,你必须先解决它
想要确定你的manifest文件是否有效,您可以使用AAPT工具(在你的android SDK安装:
aapt p --debug-mode -M d:\OBad\E1064BFD836E4C895B569B2DE4700284.apk\decompiled_with_apktool_2_with_debug\AndroidManifest.xml
要解决错误你可以参考这里,一旦你已经编辑过XML文件而没有更多的错误报告,可以继续重新打包为:
Shell D:\apktool_2\Apktool\brut.apktool\apktool-cli\build\libs>java -jar apktool-cli-2.0.0-Beta5.jar b -d -o E1064BFD836E4C895B569B2DE4700284_rebuilt_with_apktool_2_with_debug.apk d:\OBad\decompiled_with_apktool_2_with_debug # signing your apk - you can read the details here (below is what I did) # creating keystore D:\>"c:\Program Files\Java\jdk1.7.0_07\bin\keytool.exe" -genkeypair -validity 10000 -dname "CN=IBM-XF,C=CA" -keystore d:\downloads\MYKEYSTORE.keystore -storepass <keyPass> -keypass <Pass> -alias myXFKey -sigalg MD5withRSA -keyalg RSA -keysize 1024 -v # signing apk D:\>"c:\Program Files\Java\jdk1.7.0_07\bin\jarsigner.exe" -keystore d:\downloads\MYKEYSTORE.keystore -storepass <keyPass> -keypass <Pass> -digestalg SHA1 -sigalg MD5withRSA -verbose -certs E1064BFD836E4C895B569B2DE4700284_rebuilt_apktool_2_dbg.apk myXFKey #zipalign - for optimization D:\>zipalign -v 4 "d:\E1064BFD836E4C895B569B2DE4700284_rebuilt_with_apktool_2_with_debug.apk" "d:\E1064BFD836E4C895B569B2DE4700284_rebuilt_with_apktool_2_with_debug_aligned.apk" ==> verifying jar signature - D:\>"c:\Program Files\Java\jdk1.7.0_07\bin\jarsigner.exe" -verify -verbose -certs E1064BFD836E4C895B569B2DE4700284_rebuilt_apktool_2_dbg_aligned.apk
现在我们已经重编译/重打包apk,安装并附加jdb。附加jdb之前再次备份虚拟机快照是很有必要的。在.jdbrc文件中添加“stop in java.lang.System.exit(int)”声明,这样便在jdb中访问smali代码
use /home/mobisec/Malware/OBAD/decompiled_with_apktool_2_with_debug/smali/ <use the appropriate path for your setup, recall earlier we mentioned where the .java files containing smali code are>
现在我们可以使用那些regualar文件在java文件特定行号设置断点和检查smali变量对应的java变量
Shell
mobisec@Mobisec-VM:~$ jdb -attach localhost:8700
Set uncaught java.lang.Throwable
Set deferred uncaught java.lang.Throwable
Initializing jdb ...
*** Reading commands from /home/mobisec/.jdbrc
Set breakpoint java.lang.System.exit(int)
> > > cont
Nothing suspended.
>
Breakpoint hit: "thread=<1> main", java.lang.System.exit(), line=181 bci=0
# use wherei to get stack trace
<1> main[1] wherei
[1] java.lang.System.exit (System.java:181), pc = 0
[2] com.android.system.admin.COcCccl.onCreate (COcCccl.java:5,758), pc = 1,041
[3] android.app.Instrumentation.callApplicationOnCreate (Instrumentation.java:969), pc = 0
[4] android.app.ActivityThread.handleBindApplication (ActivityThread.java:3,954), pc = 729
[5] android.app.ActivityThread.access$1300 (ActivityThread.java:123), pc = 0
[6] android.app.ActivityThread$H.handleMessage (ActivityThread.java:1,185), pc = 177
[7] android.os.Handler.dispatchMessage (Handler.java:99), pc = 20
[8] android.os.Looper.loop (Looper.java:137), pc = 122
[9] android.app.ActivityThread.main (ActivityThread.java:4,424), pc = 34
[10] java.lang.reflect.Method.invokeNative (native method)
[11] java.lang.reflect.Method.invoke (Method.java:511), pc = 17
[12] com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:784), pc = 11
[13] com.android.internal.os.ZygoteInit.main (ZygoteInit.java:551), pc = 66
[14] dalvik.system.NativeStart.main (native method)
<1> main[1]
# change frames, list source code, and examine variables
<1> main[1] up
<1> main[2] list
5,754 a=0;//
5,755 a=0;// const/4 v0, 0x0
5,756 a=0;//
5,757 a=0;// #v0=(Null);
5,758 => a=0;// invoke-static {v0}, Ljava/lang/System;->exit(I)V
5,759 a=0;//
5,760 a=0;// :cond_4
5,761 a=0;// #v0=(Boolean);
5,762 a=0;// sget-object v0, Lcom/android/system/admin/COcCccl;->oCIlCll:Landroid/content/Context;
5,763 a=0;//
<1> main[2] locals
Method arguments:
Local variables:
v9 = "dmBt"
v8 = instance of android.os.PowerManager(id=830019453032)
v6 = -12
v2 = instance of byte[3] (id=830019585672)
v4 = 354
v5 = -12
v3 = "6311450ddea7b49349a92eeda1d528a5"
v1 = "sdk"
v0 = null现在我们可以看看文件smali/com/android/system/admin/COcCccl.java的内容,这个是由apktool生成的,我们看到:
Java
a=0;// invoke-static {v2}, Lcom/android/system/admin/lOClOOI;->oIlclcIc([B)[B
a=0;//
a=0;// move-result-object v2
a=0;//
a=0;// invoke-direct {v1, v2}, Ljava/lang/String;-><init>([B)V
a=0;//
a=0;// #v1=(Reference,Ljava/lang/String;);
a=0;// invoke-virtual {v0, v1}, Ljava/lang/String;->equals(Ljava/lang/Object;)Z //v0:v0 = "sdk" ,v1 = "sdk"
a=0;//
a=0;// move-result v0
a=0;//
a=0;// #v0=(Boolean);
a=0;// if-eqz v0, :cond_4
a=0;//
a=0;// const/4 v0, 0x0
a=0;//
a=0;// #v0=(Null);
a=0;// invoke-static {v0}, Ljava/lang/System;->exit(I)V我们可以在IDA中看这段代码,那样能更好的观看控制流和数据流。这可以通过解压缩apktool新打包的apk文件,然后在IDA Pro中加载classes.dex文件来完成。当我这样做时遇到了以下错误:
开发者都意识到这一点所以这是可以被修复的,但如果你遇到这个错误,您可以通过修复dex文件版本解决此问题。可以在任何十六进制编辑器打开dex文件,并将从0×36到0×35的十六进制值偏移6,保存修改后的classes.dex文件,并在IDA打开它。(0×30是'0的ASCII码',0×33是'3',0×36是'6')
注意字节码index / PC的值,我们在jdb的堆栈跟踪中看到的行数是不同的十六进制地址/或者是IDA视图方法开始相对位置,IDA反汇编视图dex字节。不过如何,我们可以找到代码区,可以在IDA寻找一个方法并分析,所以手动退出调用可以看到:

IDA显示exit的代码逻辑
从这里可以看到退出,如果字符串比较结果为true但并没有采取分支中的if-eqz,而是直接调用System.exit().(上一段里面直接手动退出了,未经if-eqz和转跳)
invoke-virtual {v0, v1}, <boolean String.equals(ref) imp. @ _def_String_equals@ZL>
我们也可以看到,JDB和IDA图里“locals”命令输出的v1 =“sdk”告诉我们,v0来自字符串解密和反射:
Java
CODE:0004A4C8 invoke-static {v0, v1, v2}, <ref COcCccl.oCIlCll(int, int, int) COcCccl_oCIlCll@LIII>
CODE:0004A4CE move-result-object v0
CODE:0004A4D0 invoke-static {v0}, <ref Class.forName(ref) imp. @ _def_Class_forName@LL>
CODE:0004A4D6 move-result-object v0
CODE:0004A4D8 const/16 v1, -0xE
CODE:0004A4DC const/16 v2, -0x27
CODE:0004A4E0 const/16 v3, 0x163
CODE:0004A4E4 invoke-static {v1, v2, v3}, <ref COcCccl.oCIlCll(int, int, int) COcCccl_oCIlCll@LIII>
CODE:0004A4EA move-result-object v1
CODE:0004A4EC invoke-virtual {v0, v1}, <ref Class.getField(ref) imp. @ _def_Class_getField@LL>
CODE:0004A4F2 move-result-object v0
CODE:0004A4F4 const/4 v1, 0
CODE:0004A4F6 invoke-virtual {v0, v1}, <ref Field.get(ref) imp. @ _def_Field_get@LL>
CODE:0004A4FC move-result-object v0这似乎是一些反虚拟机/模拟器检查,下一步我要做是获取所有反射有关的调用信息确定恶意软件想要做什么,然后注明每个调用返回,所有这些信息将方便进一步的分析。
这个.jdbrc用于收集反射相关信息:(请注意不同的样本,你也可以grep smali代码进行相关反射调用和设置相应的断点)
.jdbrc: use /home/mobisec/decompiled_with_apktool_2_with_debug/smali/ monitor print this monitor locals monitor where monitor suspend monitor cont monitor resume stop in java.lang.Class.getDeclaredField(java.lang.String) stop in java.lang.Class.getDeclaredMethod(java.lang.String,java.lang.Class[]) stop in java.lang.Class.getField(java.lang.String) stop in java.lang.reflect.AccessibleObject.setAccessible(boolean) stop in java.lang.Runtime.exec(java.lang.String) stop in java.lang.Runtime.exec(java.lang.String[]) stop in java.lang.Runtime.exec(java.lang.String[],java.lang.String[]) stop in java.lang.Runtime.exec(java.lang.String[],java.lang.String[],java.io.File) stop in java.lang.Runtime.exec(java.lang.String,java.lang.String[],java.io.File) stop in java.lang.Runtime.exec(java.lang.String,java.lang.String[]) stop in java.lang.System.exit(int)
我使用暂停,恢复命令只是会造成额外的延迟,这样所有的输出被正确打印,而不是交错,注意exec上使用断点,这可以告诉你如果应用调用外部程序,很有趣的如果你尝试一下,你会看到OBAD调用外部程序,并使用结果来检查一些东西,我将让你发现它。
你可以使用.jdbrc运行jdb重定向输出到文件中供以后分析。有趣的反射调用只是java. lang . class.getField,这是我们发现被getField调用的类和字段的名称。
# fields accessed grep -E "name|this" OBAD_Reflection_Related_Code_Entries_Params_ST.txt <1> main[1] this = "class android.app.ActivityManager$RunningAppProcessInfo" name = "processName" <1> main[1] this = "class android.app.ActivityManager$RunningAppProcessInfo" name = "RELEASE" <1> main[1] this = "class android.os.Build$VERSION" name = "BOARD" this = "class android.os.Build" name = "BRAND" <1> main[1] this = "class android.os.Build" name = "DEVICE" <1> main[1] this = "class android.os.Build" name = "HOST" <1> main[1] this = "class android.os.Build" name = "ID" <1> main[1] this = "class android.os.Build" name = "MODEL" <1> main[1] this = "class android.os.Build" name = "PRODUCT" <1> main[1] this = "class android.os.Build" name = "TAGS" <1> main[1] this = "class android.os.Build" name = "TYPE" <1> main[1] this = "class android.os.Build" name = "USER" <1> main[1] this = "class android.os.Build" name = "ANDROID_ID" <1> main[1] this = "class android.provider.Settings$Secure" name = "applicationInfo" this = "class android.content.pm.PackageInfo" name = "flags" <1> main[1] this = "class android.content.pm.ApplicationInfo" name = "MODEL" # code places where os/dev specific fields were accessed D:\>grep -E "name| \[1\]| \[2]" OBAD_Reflection_Related_Code_Entries_Params_ST.txt name = "processName" <1> main[1] [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.oIOccOcl (COcCccl.java:3,776) name = "RELEASE" <1> main[1] [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.onCreate (COcCccl.java:4,965) name = "BOARD" <1> main[1] [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,023) name = "BRAND" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,070) name = "DEVICE" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,117) name = "HOST" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,165) name = "ID" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,213) name = "MODEL" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,260) name = "PRODUCT" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,307) name = "TAGS" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,354) name = "TYPE" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,401) name = "USER" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,448) name = "ANDROID_ID" <1> main[1] [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.cIoCcIo (COcCccl.java:2,765) name = "applicationInfo" <1> main[1] [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.OOIlIcCc (COcCccl.java:1,904) name = "flags" <1> main[1] [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.OOIlIcCc (COcCccl.java:1,943) name = "MODEL" [1] java.lang.Class.getField (Class.java:782) [2] com.android.system.admin.COcCccl.onCreate (COcCccl.java:5,683)
注意,这告诉我们被调用的信息以及来自哪里,但不是返回值,因此可以在调用返回和dumping locals等后通过设置断点获得。从输出我们看到这行COcCccl.java:5683调用getField 获得android.os.Build.MODEL,结果在“sdk”模拟器镜像,因此我们退出。
七、愚弄反模拟器检查
有各种字符串和设置可以告诉我们系统、os、硬件等信息,其中大部分可以通过在shell中运行getprop看到,如“adb shell getprop”。仅供参考,您可以通过链接文件查看正在运行getprop的几个配置结果。
* 真实Galaxy Nexus设备: getprop.galaxy.nexus
* android sdk 模拟器运行api 18 v2 系统镜像:getprop_Android_sdk_18_rev_2
* android sdk 模拟器运行android4.3开源工程代码构建的系统镜像:getprop_AOSP_4.3_modified_src
有各种不同的技巧可以绕过恶意软件使用的反VM技术:
* 修改smali代码忽略检查,因为在此建议InsomniHack 2012演示文稿由@ cryptax
* 使用ldpreloadhook , BlachHat会上呈现的反模拟器部分DexEducation-PracticingSafeDex 通过@ timstrazz
* 根据需要 修改AOSP代码,编译并使用Android SDK仿真器运行生成的系统映像。(这是我以下想谈的)
1、修改,编译和使用与模拟器编译的系统映像
这里可以找到下载并打造AOSP的介绍。
Class android.os.build fields such as model are populated within android/os/Build.java(这句不太理解),所以我拿来AOSP代码分支4.3_r3进行了如下修改:
diff
zashraf@ubuntu-10-x64:~/Android/src_4.3_r3$ diff ./frameworks/base/core/java/android/os/Build.java.modified ./frameworks/base/core/java/android/os/Build.java.orig
18d17
< import android.util.Log;
471,484c470,471
< public static String getString(String property) {
< String p = SystemProperties.get(property, UNKNOWN);
< Log.i("XF_IBM", "getString called for "+ property +" returning :" + p );
< if (!property.equals("ro.product.model") && !p.equals("sdk"))
< {
< p = "Galaxy Nexus";
< Log.i("XF_IBM", " Hooking return of SDK");
< }
< if (!property.equals("ro.product.name") && !p.equals("sdk"))
< {
< p = "yakju";
< Log.i("XF_IBM", " Hooking return of SDK") ;
< }
< return p;
---
> private static String getString(String property) {
> return SystemProperties.get(property, UNKNOWN);我选择的目标“aosp_arm-ENG”,编译和输出/目标/产品/通用/来获得一个新鲜的system.img文件,这就是我将用在模拟器上并??安装OBAD的。
2、创建一个新的AVD的模拟器来运行定制的system.img
* 在系统映像和平台下的SDK子目录创建的android-18的副本。(我命名副本为android-18_customized)
* 系统映像文件夹中复制新建的system.img副本(我mobisec默认配置是/ opt/mobisec/??devtools/android-sdk/system-images/android-18_customized/armeabi-v7)
diff * Making the following edits: * in platform subdirectory diff -r android-18/source.properties android-18_customized/source.properties 8c8 < Platform.Version=4.3 --- > Platform.Version=4.3_Custom 14c14 < AndroidVersion.ApiLevel=18 --- > AndroidVersion.ApiLevel=18_custom and in system-images: diff -r android-18/source.properties android-18_customized/source.properties 8c8 < Platform.Version=4.3 --- > Platform.Version=4.3_Custom 14c14 < AndroidVersion.ApiLevel=18 --- > AndroidVersion.ApiLevel=18_custom
* 创建AVD - 通过运行并选择新创建的API-18_customized为新的虚拟设备配置目标。
我命名设备为Nexus_4_on_4.3_api_18_custom。一切都应该小心注意,但由于某些原因,要么是SDK灵敏还是我错过了什么,所以一定要确保编辑AVD配置文件,并将system.img指向您的自定义系统映像
配置文件是/ home/mobisec/??.android/avd/Nexus_4_on_4.3_api_18_custom.avd/config.ini
After this if you run android list target you should see id: XX or "android-18" Name: Android 4.3_Custom Type: Platform API level: 18 Revision: 2
你可以这样启动模拟器:
Shell emulator-arm -avd Nexus_4_on_4.3_abi_18 -scale 0.75 -debug all -logcat all -no-boot-anim
如果你在jdb设置一个断点并尝试,你会看到修改后的java代码做的工作:
<1> main[1] print android.os.Build.MODEL android.os.Build.MODEL = "yakju"
请注意,正如我们刚才修改的java代码,通过getprop命令返回的值不会改变。
如果你再在这个新的AVD安装OBAD,然后手动开始启动activity,您将看到以下获取设备管理员访问权限的画面。为什么你需要手动开始启动activity,以及为什么它不会在安装时候要求设备管理员权限,这些是在下一期博客文章中也许会让你看到。

请求设备管理员权限
顺便说一句,我们并不需要修改AOSP代码来让OBAD工作,你可以从getprop的输出看到AOSP,build.model的返回值默认情况下不是“SDK”,不管怎样现在你已经知道可以自定义android的Java库代码了。
PS:
相对于上一份OBAD的分析,这篇显得更接地气了,虽然不少东西都知道,但也有没接触的,比如JDB(对java不熟,所以通常是读smali).
可惜作者并没有写出后面的部分,那才是最想看到的.
