[其它语言] 四android学习笔记(转贴 动态分析例子)

2360 0
狼毛 2023-1-23 11:06:03 | 显示全部楼层 |阅读模式
本帖最后由 狼毛 于 2023-1-23 11:07 编辑

转贴自看雪精华



    我计划定期写一些DIY系列的文章,目标不仅在于了解恶意软件、漏洞和利用,也与我们的读者分享一些技术和工具,他们可以使用自己“知彼”。我也期待听到你在分析类似应用的经验,相似或更有效的工具和技术。

    好吧,让我们正式开始,OBAD已被确认为安卓恶意软件最精妙作品之一  ,你可以找到各种分析结果。在这个系列中,我们将它拆开了解其功能和各种技术,它是很强大的能免卸载也很难分析。在第一部分中,我们将看到如何使用动态代码分析,使用jdb调试,smali(Dalvik的反汇编源代码)级别的调试,使用JDB命令来了解被调用的反射代码和字符串解密,使用IDA理解可视化控制和数据流,修改AOSP代码和构建定制系统映像来绕过OBAD使用的反模拟器技巧。

一、从哪里得到它

   我们将使用示例MD5:e1064bfd836e4c895b569b2de4700284(VirusTotal analysis )。你可以从Contagio Mobile下载它

二、查看APK文件的内容

    apk文件可以简单地压缩如zip文件一样,其中包括二进制格式的 manifest文件,这通常可以转换成一个可读的文件,下载AXMLPrinter2.jar并运行


Shelljava –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:

Shellmobisec@Mobisec:/opt/mobisec/devtools/android-sdk/tools$ emulator-arm -avd Android_4.0.3 -scale 0.75 -debug all -logcat all -no-boot-anim

    一旦模拟器运行,你可以安装样本如下:

Shellmobisec@Mobisec-VM:~$  adb install Malware/OBad/E1064BFD836E4C895B569B2DE4700284.apk 147 KB/s (84306 bytes in 0.558s)pkg: /data/local/tmp/E1064BFD836E4C895B569B2DE4700284.apkSuccess

    在这之后,如果你去查看应用程序安装列表是看不到任何新启动器图标的。让我们看看一些日志记录,我们可以使用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.apkD/dalvikvm( 493): GC_CONCURRENT freed 400K, 8% free 6526K/7047K, paused 11ms+4msW/ActivityManager( 80): No content provider found for permission revoke: file:///data/local/tmp/e1064bfd.apkI/PackageManager( 80): Running dexopt on: com.android.system.adminD/dalvikvm( 753): DexOpt: 'Lcom/android/internal/telephony/IExtendedNetworkService;' has an earlier definition; blocking outD/dalvikvm( 753): DexOpt: not verifying/optimizing 'Lcom/android/internal/telephony/IExtendedNetworkService;': multiple definitionsD/dalvikvm( 753): DexOpt: load 77ms, verify+opt 1198msI/ActivityManager( 80): Force stopping package com.android.system.admin uid=10042D/PackageManager( 80): New package installed in /data/app/com.android.system.admin-1.apkW/PackageManager( 80): Unknown permission android.permission.READ_EXTERNAL_STORAGE in package com.android.system.adminW/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.adminD/dalvikvm( 80): GC_CONCURRENT freed 345K, 8% free 8789K/9543K, paused 9ms+13msI/AppSecurityPermissions( 223): Ignoring unknown permission:android.permission.READ_EXTERNAL_STORAGEI/AppSecurityPermissions( 223): Ignoring unknown permission:android.permission.ACCESS_BLUETOOTH_SHARED/PackageManager( 80): generateServicesMap(android.accounts.AccountAuthenticator): 2 services unchangedD/PackageManager( 80): generateServicesMap(android.content.SyncAdapter): 4 services unchangedD/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控制台,您可以尝试这些以获取有关包的一些信息:

Shellmobisec@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工具启动应用程序

Shelldz#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文件

Shellstop 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.ThrowableSet deferred uncaught java.lang.ThrowableInitializing jdb ...*** Reading commands from /home/mobisec/.jdbrcDeferring 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 > > excludejava.*,javax.*,sun.*,com.sun.*,

    当我们运行跟踪go方法时没有看到从包中输入/输出的方法。

    那么让我们来尝试在java.lang.System.exit设置一个断点,我们已经有一个虚拟机快照可以恢复,编辑.jdbrc文件(放在“stop in java.lang.System.exit(int) “),再次运行该应用程序,JDB附加,这样我们可以看到:

Shellmobisec@Mobisec-VM:~$ vi ~/.jdbrc;jdb -attach localhost:8700Set uncaught java.lang.ThrowableSet deferred uncaught java.lang.ThrowableInitializing jdb ...*** Reading commands from /home/mobisec/.jdbrcSet breakpoint java.lang.System.exit(int)> > contNothing 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 methodsa=0;// .method public onCreate()Va=0;// .locals 10a=0;//a=0;// invoke-super {p0}, Landroid/app/Application;->onCreate()Va=0;//a=0;// invoke-direct {p0}, Lcom/android/system/admin/COcCccl;->oIOccOcl()Za=0;//a=0;// move-result v0a=0;//a=0;// #v0=(Boolean);a=0;// if-eqz v0, :cond_0a=0;//a=0;// const/4 v0, 0x1a=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文件而没有更多的错误报告,可以继续重新打包为:

ShellD:\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 keystoreD:\>"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 <ass> -alias myXFKey -sigalg MD5withRSA -keyalg RSA -keysize 1024 -v # signing apkD:\>"c:\Program Files\Java\jdk1.7.0_07\bin\jarsigner.exe" -keystore d:\downloads\MYKEYSTORE.keystore -storepass <keyPass> -keypass <ass> -digestalg SHA1 -sigalg MD5withRSA -verbose -certs E1064BFD836E4C895B569B2DE4700284_rebuilt_apktool_2_dbg.apk myXFKey #zipalign - for optimizationD:\>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变量

Shellmobisec@Mobisec-VM:~$ jdb -attach localhost:8700Set uncaught java.lang.ThrowableSet deferred uncaught java.lang.ThrowableInitializing jdb ...*** Reading commands from /home/mobisec/.jdbrcSet breakpoint java.lang.System.exit(int)> > > contNothing 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] list5,754 a=0;//5,755 a=0;// const/4 v0, 0x05,756 a=0;//5,757 a=0;// #v0=(Null);5,758 => a=0;// invoke-static {v0}, Ljava/lang/System;->exit(I)V5,759 a=0;//5,760 a=0;// :cond_45,761 a=0;// #v0=(Boolean);5,762 a=0;// sget-object v0, Lcom/android/system/admin/COcCccl;->oCIlCllandroid/content/Context;5,763 a=0;//<1> main[2] localsMethod argumentsocal variables:v9 = "dmBt"v8 = instance of android.os.PowerManager(id=830019453032)v6 = -12v2 = instance of byte[3] (id=830019585672)v4 = 354v5 = -12v3 = "6311450ddea7b49349a92eeda1d528a5"v1 = "sdk"v0 = null

    现在我们可以看看文件smali/com/android/system/admin/COcCccl.java的内容,这个是由apktool生成的,我们看到:

Javaa=0;// invoke-static {v2}, Lcom/android/system/admin/lOClOOI;->oIlclcIc([B)[Ba=0;//a=0;// move-result-object v2a=0;//a=0;// invoke-direct {v1, v2}, Ljava/lang/String;-><init>([B)Va=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 v0a=0;//a=0;// #v0=(Boolean);a=0;// if-eqz v0, :cond_4a=0;//a=0;// const/4 v0, 0x0a=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来自字符串解密和反射:

JavaCODE:0004A4C8 invoke-static {v0, v1, v2}, <ref COcCccl.oCIlCll(int, int, int) COcCccl_oCIlCll@LIII>CODE:0004A4CE move-result-object v0CODE:0004A4D0 invoke-static {v0}, <ref Class.forName(ref) imp. @ _def_Class_forName@LL>CODE:0004A4D6 move-result-object v0CODE:0004A4D8 const/16 v1, -0xECODE:0004A4DC const/16 v2, -0x27CODE:0004A4E0 const/16 v3, 0x163CODE:0004A4E4 invoke-static {v1, v2, v3}, <ref COcCccl.oCIlCll(int, int, int) COcCccl_oCIlCll@LIII>CODE:0004A4EA move-result-object v1CODE:0004A4EC invoke-virtual {v0, v1}, <ref Class.getField(ref) imp. @ _def_Class_getField@LL>CODE:0004A4F2 move-result-object v0CODE:0004A4F4 const/4 v1, 0CODE: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 thismonitor localsmonitor wheremonitor suspendmonitor contmonitor resumestop 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 accessedgrep -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 = "RODUCT"<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 accessedD:\>grep -E "name| \[1\]| \[2]" OBAD_Reflection_Related_Code_Entries_Params_ST.txtname = "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 = "RODUCT"[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进行了如下修改:

diffzashraf@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.orig18d17< 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 subdirectorydiff -r android-18/source.properties android-18_customized/source.properties8c8< Platform.Version=4.3---> Platform.Version=4.3_Custom14c14< AndroidVersion.ApiLevel=18---> AndroidVersion.ApiLevel=18_custom and in system-images:diff -r android-18/source.properties android-18_customized/source.properties8c8< Platform.Version=4.3---> Platform.Version=4.3_Custom14c14< 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 seeid: XX or "android-18"Name: Android 4.3_CustomType: PlatformAPI level: 18Revision: 2

    你可以这样启动模拟器:

Shellemulator-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.MODELandroid.os.Build.MODEL = "yakju"

    请注意,正如我们刚才修改的java代码,通过getprop命令返回的值不会改变。

    如果你再在这个新的AVD安装OBAD,然后手动开始启动activity,您将看到以下获取设备管理员访问权限的画面。为什么你需要手动开始启动activity,以及为什么它不会在安装时候要求设备管理员权限,这些是在下一期博客文章中也许会让你看到。

[img=768,1280][/img]
请求设备管理员权限

    顺便说一句,我们并不需要修改AOSP代码来让OBAD工作,你可以从getprop的输出看到AOSP,build.model的返回值默认情况下不是“SDK”,不管怎样现在你已经知道可以自定义android的Java库代码了。

PS:

    相对于上一份OBAD的分析,这篇显得更接地气了,虽然不少东西都知道,但也有没接触的,比如JDB(对java不熟,所以通常是读smali).

    可惜作者并没有写出后面的部分,那才是最想看到的.

第二部分:

它比我打算在 OBAD 分析的后续帖子中花费的时间要长得多,但迟到总比没有好。在这篇文章中,我们将看看以下内容:
让我们给奥巴德一个机会
上次我们讨论了各种分析工具,设置要在 jdb 中调试的应用程序,识别反模拟器代码,黑客攻击和编译 AOSP 代码,然后使用我们修改的系统映像运行模拟器以绕过 antivm 检查。因此,现在您可以绕过反虚拟机检查,如果您在模拟器中运行 OBAD,您将看到它要求以设备管理员身份启用它
[img][/img]" style="box-sizing: border-box; max-width: 100%; display: block !important;">
因此,它为自己使用标签“系统”,如果用户拒绝请求,它将继续请求它。一旦它被激活为设备管理员,启动器图标就会消失,它甚至不会在设备管理员列表中显示为设备管理员,可以通过设置->安全->设备管理员看到。
[img][/img]" style="box-sizing: border-box; max-width: 100%; display: block !important;">
上图显示 OBAD 没有可见的应用程序启动器图标,这就是为什么在我们之前的帖子中我们提到您必须手动启动活动,因为我们看不到它的启动器图标。如果您去获取模拟器/设备的设备管理员列表,您也不会在那里看到它。下图显示了您希望如何看到它被列为设备管理员
[img][/img]" style="box-sizing: border-box; max-width: 100%; display: block !important;">
以下是您在易受攻击的Android中看到的内容(稍后将详细介绍该漏洞)
[img][/img]" style="box-sizing: border-box; max-width: 100%; display: block !important;">
所以我们没有启动器图标,我们在设备管理员的列表中也没有它,可能是我们的AV产品检测到并删除了它?让我们在应用程序列表(设置 -> 应用程序)中检查它,您会在那里找到它,也许我们可以从那里卸载它,可以吗?让我们看一下下图:
[img][/img]" style="box-sizing: border-box; max-width: 100%; display: block !important;">
卸载按钮被禁用,但有人可能会说“嘿!等一下,我比这更好,我可以使用“adb uninstall”命令,如果您尝试这样做,那么您将失败,如果您查看 logcat 输出,您将学到以下内容:
mobisec@Mobisec-VM:~/Malware/OBAD$ adb uninstall com.android.system.admin
Failure
mobisec@Mobisec-VM:~/Malware/OBAD$ adb logcat -d -b main -b events | grep admin | tail -1
W/PackageManager( 277): Not removing package com.android.system.admin: has active device admin
我们无法从“设置”菜单中停用设备管理员,因为它甚至没有列为设备管理员,因此我们无法卸载它。让我们分析一下它是如何做到这一切的。
如何避免进程因活动无响应 (ANR) 而被终止
在我之前用于分析 OBAD 的博客文章中,我们展示了如何跟踪进入和退出的方法,在处理 AntiVM 检查后,如果您尝试再次跟踪方法,您将看到您的进程被杀死,您将在 logcat 输出中执行以下操作
W/ActivityManager( 291): Timeout executing service: ServiceRecord{421b03d8 u0 com.android.system.admin/.MainService}
...
W/ActivityManager( 291): Killing ProcessRecord{421b77a0 1129:com.android.system.admin/u0a10053}: background ANR
以下是修补Android代码以避免它的方法:
zashraf@ubuntu-10-x64:~/Android/src_4.3_r3$ diff ./frameworks/base/services/java/com/android/server/am/ActiveServices.java.modified ./frameworks/base/services/java/com/android/server/am/ActiveServices.java.orig
1852,1857c1852
<  //Slog.w(TAG, " .... Hijacked ANR appnotresponding call for debugged app");
<  if (!proc.debugging)
<   mAm.appNotResponding(proc, null, null, false, anrMessage);
<  else
<   Slog.w(TAG, "prevented ANR on debuggee app - Hijacked ANR appnotresponding call for debugged app");
<
---
>  mAm.appNotResponding(proc, null, null, false, anrMessage);
=> for broadcast ANR
zashraf@ubuntu-10-x64:~/Android/src_4.3_r3/frameworks/base/services/java/com/android/server$ diff am/BroadcastQueue.java am/BroadcastQueue.java.orig
956,959c956
< if (!app.debugging)
< mHandler.post(new AppNotResponding(app, anrMessage));
< 其他
< Slog.w(TAG,“阻止 ANR on (广播) 调试对象应用程序 - 劫持的 ANR appnot响应调试应用程序的调用”);
---
> mHandler.post(new AppNotResponding(app, anrMessage));
启动器图标是如何消失的?
我按照上一篇文章中的描述跟踪方法条目,并在 jdb 中使用以下 exclude 命令来避免跟踪标准 java 代码,但仍跟踪反射调用的方法。
exclude android.os.*,org.apache.*,java.lang.D*,java.lang.N*,java.lang.P*,java.lang.U*,java.lang.F*,java.lang.Ru*,java.lang.E*,java.lang.T*,java.lang.V*,java.lang.I*,java.lang.A*,java.lang.S*,java.lang.B*,java.lang.ref.*,java.lang.C*,java.lang.O*,java.lang.S*,java.lang.V*,javax.*,sun.*,com.sun.*,java.s*,java.u*,java.s*,java.n*,java.i*,java.lang.reflect.A*,java.lang.reflect.C*,java.lang.reflect.F*,java.lang.reflect.Method.g*,java.lang.reflect.Method.<*
我注意到对android.app.ApplicationPackageManager.setComponentEnabledSetup的调用是通过反射完成的,这可以通过设置断点在jdb中进一步检查。
Breakpoint hit: "thread=<1> main", android.app.ApplicationPackageManager.setComponentEnabledSetting(), line=1,262 bci=0
<1> main[1] wherei
[1] android.app.ApplicationPackageManager.setComponentEnabledSetting (ApplicationPackageManager.java:1,262), pc = 0
[2] java.lang.reflect.Method.invokeNative (native method)
[3] java.lang.reflect.Method.invoke (Method.java:525), pc = 17
[4] com.android.system.admin.cCoIOIOo.ollIIIc (null), pc = 748
[5] com.android.system.admin.cCoIOIOo.onActivityResult (null), pc = 9
[6] android.app.Activity.dispatchActivityResult (Activity.java:5,322), pc = 7
[7] android.app.ActivityThread.deliverResults (ActivityThread.java:3,363), pc = 38
[8] android.app.ActivityThread.handleSendResult (ActivityThread.java:3,410), pc = 172
[9] android.app.ActivityThread.access$1100 (ActivityThread.java:141), pc = 0
[10] android.app.ActivityThread$H.handleMessage (ActivityThread.java:1,304), pc = 229
[11] android.os.Handler.dispatchMessage (Handler.java:99), pc = 20
[12] android.os.Looper.loop (Looper.java:137), pc = 84
[13] android.app.ActivityThread.main (ActivityThread.java:5,103), pc = 56
[14] java.lang.reflect.Method.invokeNative (native method)
[15] java.lang.reflect.Method.invoke (Method.java:525), pc = 17
[16] com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:737), pc = 11
[17] com.android.internal.os.ZygoteInit.main (ZygoteInit.java:553), pc = 70
[18] dalvik.system.NativeStart.main (native method)
<1> main[1] locals
Method arguments:
componentName = instance of android.content.ComponentName(id=830033869864)
Local variables:
newState = 2
flags = 1
<1> main[1] print componentname
com.sun.tools.example.debug.expr.ParseException: Name unknown: componentname
componentname = null
<1> main[1] print componentName
componentName = "ComponentInfo{com.android.system.admin/com.android.system.admin.cCoIOIOo}"
<1> main[1]
为什么我们没有看到 OBAD 列为设备管理员?
如果您使用最新版本的Android之一尝试此操作,您会注意到OBAD实际上被列为设备管理员,原因是此漏洞已被修复。要了解漏洞,我们可以简单地查看补丁日志并分析补丁,我认为在不查看补丁的情况下查看它会更有趣。那么,当您打开“设置”-“安全性”-“设备管理员”时,会执行哪些代码>>?您可以打开它,然后查看事件缓冲区中logcat的输出,您将看到:
adb logcat -d -b events
...
I/am_new_intent( 276): [0,1106566944,17,com.android.settings/.Settings,android.intent.action.MAIN,NULL,NULL,274726912]
I/am_resume_activity( 276): [0,1106900904,17,com.android.settings/.Settings]
I/am_on_resume_called( 1118): [0,com.android.settings.Settings]
I/am_create_activity( 276): [0,1107200744,17,com.android.settings/.SubSettings,android.intent.action.MAIN,NULL,NULL,0]
I/am_pause_activity( 276): [0,1106900904,com.android.settings/.Settings]
I/am_on_paused_called( 1118): [0,com.android.settings.Settings]
I/am_restart_activity( 276): [0,1107200744,17,com.android.settings/.SubSettings]
因此,这告诉我们从哪里开始查找列出设备管理员的代码,我们可以在androidxref上搜索它并找到设置.java在那里您会发现R.id.security_settings的使用,如果您搜索它,您将在settings_headers.xml中找到它:
<!-- Security -->
127 <header
128  android:<b>fragment=</b>"com.android.settings.SecuritySettings"
129  android:<b>icon=</b>"@drawable/ic_settings_security"
130  android:<b>title=</b>"@string/security_settings_title"
131  android:<b>id=</b>"@+id/security_settings" />
因此,让我们看一下com.android.settings.SecuritySettings,您将看到它创建设备管理员首选项类别为:
PreferenceGroup deviceAdminCategory= (PreferenceGroup)
261    root.findPreference(KEY_DEVICE_ADMIN_CATEGORY);
如果您跟踪KEY_DEVICE_ADMIN_CATEGORY您将到达security_settings_misc.xml并查看
43  <reference android:title="@string/manage_device_admin"
44    android:summary="@string/manage_device_admin_summary"
45    android:persistent="false"
46    android:fragment="com.android.settings.DeviceAdminSettings"/>
因此,下一步是进入设备管理员设置,浏览您将进入updateList(注意:我将带您进入updateList的未修补版本),您会注意到要在设备管理员列表中显示某些内容,它必须满足以下条件:
1. 应在返回的列表中:
getActivity().getPackageManager().queryBroadcastReceivers(
    new Intent(DeviceAdminReceiver.ACTION_DEVICE_ADMIN_ENABLED),
    PackageManager.GET_META_DATA | PackageManager.GET_DISABLED_UNTIL_USED_COMPONENTS);
2. 应可见或活跃:
DeviceAdminInfo dpi = new DeviceAdminInfo(getActivity(), ri);
    if (dpi.isVisible() || mActiveAdmins.contains(dpi.getComponent())) {
mAvailableAdmins.add(dpi);
通过一些琐碎的调试,您会意识到 queryBroadcastReceiver 没有返回 OBAD 设备管理组件,进一步的调试最终会导致您获得 OBAD 的清单文件,说:
<receiver "System" =".OCllCoO">
   <meta-data "android.app.device_admin" ="@2130968576">
   </meta-data>
   <intent-filter>
    <action name="com.strain.admin.DEVICE_ADMIN_ENABLED">
    </action>
   </intent-filter>
</receiver>
请注意com.strain.admin.DEVICE_ADMIN_ENABLED的使用,开发人员文档说DeviceAdministrator组件应该注册以接收android.app.action.DEVICE_ADMIN_ENABLED的,但是坏人不必听,他们也不听,这就是他们隐藏设备管理员的方式。由于它没有注册接收此类广播,因此在 updateList 中检索它的接收器时不会出现(如上所述)。
[img][/img]" style="box-sizing: border-box; max-width: 100%; display: block !important;">
环顾四周,我发现了一些有趣的东西,我将很快(希望在两周内)在我的下一篇文章中分享,所以请继续关注并思考这个问题:你给朋友你的王国/宫殿/房子的钥匙,他把他的给你,但你给他的是假的。
直到下次保持安全。


转贴自:

[翻译]DIY:Android恶意应用分析-分解OBAD(1)-Android安全-看雪论坛-安全社区|安全招聘|bbs.pediy.com (kanxue.com)
DIY:安卓恶意软件分析 - 拆解 OBAD(第 1 部分) (securityintelligence.com)
DIY: Android Malware Analysis – Taking Apart OBAD (Part 2) - Security Intelligence
  








您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

狼毛

精英红客

关注
  • 206
    主题
  • 2
    粉丝
  • 1
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行