本帖最后由 JackIO 于 2015-1-22 13:09 编辑
【说明】这是转过来的一篇文章,原文是一家老外公司写的博文,虽然文中是用来对APP做安全检测,但是如果利用好了,很可能让我们的破解和逆向事半功倍。至于怎么利用,仁者见仁、智者见智。欢迎大家跟帖讨论利用方法。
在这篇博文中,我将带领大家学习如何将调试器应用到一个Android应用程序上,并利用第一次反编译得到的信息来分析应用的方法调用过程。比较独特的一点是,本方法并不需要获取安卓设备的root权限。
该方法在移动应用渗透测试时可以派上用场,因为在应用程序运行过程中,我们可以一步步进入到它的逻辑内部,并有可能获得该应用的关键信息,而通常其他方法却无法做到这一点。例如,在加密前拦截数据流,程序运行时获取加密密钥,获取密码以及其他的敏感数据。对移动应用渗透测试员和那些想深入了解安卓平台上的攻击方式的开发者来说,这篇博文将是非常有趣的。
1、所需工具 下面列举了本次攻击演示所需要的所有工具: Windows/Mac OS X/Linux
Java(推荐1.7版本)
IDE(Eclipse,IntelliJ IDEA,Android Studio,三者任选其一)
Android SDK(https://developer.android.com/sdk/index.html?hl=i)
APKTool(https://code.google.com/p/android-apktool/)或者APK Studio(http://apkstudio.codeplex.com)
安卓设备/模拟器
Dex2Jar(https://code.google.com/p/dex2jar/)
JD-GUI(http://jd.benow.ca/)
在这篇博文中,我将使用Windows 8系统、APK Studio和IntelliJ IDEA来演示。我所使用的安卓手机是普通的Nexus 4,系统版本是Android 4.4.4。推荐大家将所有工具都添加到path环境变量中,这样用起来比较方便。
2、安卓设备设置 接下来我们就先设置一下手机,为下面的步骤做好准备。 设定开发者选项
首先我们需要做的就是确保我们的安卓手机启用了USB调试功能,这也是我们可以使用Android SDK工具集来与手机通信的前提条件。要做到这一点,我们需要启用开发者选项功能。如果你正在运行一个普通的安卓设备,那么你可以通过导航到“Settings”=>“About phone”,然后单击“Build number”,最终手机应该提示开发者选项已启用。(其实这里直接进行“设置”=>“开发者选项”=>“开启开发者选项”就可以了。[译者注]) http://image.3001.net/images/20150121/14218158657227.jpg!small http://image.3001.net/images/20150121/14218158809554.jpg!small 启用USB调试 下一步,我们通过进行“Settings”=>“Developer options”=>”USB debugging”来访问开发人员选项并启用USB调试功能。 http://image.3001.net/images/20150121/14218159774068.jpg!small 通过USB链接手机和电脑并使用ADB 将手机与电脑通过USB数据线连接之后,会提示“USB调试已经连接上设备”。接下来,我们要确保ADB(安卓调试桥)可以连接到手机。ADB是Android SDK工具集内包含的一个软件工具。我们在Windows系统控制台(cmd)上输入以下代码: adb devices正常情况下将会出现下图所示的结果: http://image.3001.net/images/20150121/14218160343777.jpeg!small 如果你的电脑上没有提示上图中的内容,那么很可能是系统中没有安装正确的驱动程序。你需要重新安装该驱动,驱动程序可以从AndroidSDK或者手机制造商的网站获得。
3、判断应用可调试性 当调试安卓应用程序的时候,我们首先要检查该应用程序是否开启了调试功能。可以通过以下几种方法来检查是否开启了调试功能。 第一种方法是打开安卓设备监视器(Eclipse中的是DDMS),我们可以在设备区域窗口中看到我们的设备列表。 http://image.3001.net/images/20150121/14218160927368.jpeg!small 如果安卓手机里面的某个应用设置为可调试,那么该应用程序会出现在该列表中。在这里我创建了一个测试应用程序,我们可以看到该程序并没有出现在该列表中,表明该程序我设置为了不可调试。 第二种方法是,我们可以通过检查APK文件中的AndroidManifest.xml文件来确定应用是否可以调试。APK文件本质上是一个包含应用所有信息的zip压缩文件。 如果你没有目标应用的APK文件,那么就必须先从手机中导出该APK文件。每当我们从Google Play 商店中下载应用时,系统都自动下载了该应用的APK文件,并将其存储在了安卓设备上。这些APK文件的位置通常在手机中/data/app目录下。如果你的手机还没有root过,可能你将不能查看该目录下的文件。然而,如果你知道目标APK的名字,那么也可以通过adb工具将该APK文件转存到电脑中。为了查找目标APK文件,我们可以打开一个Windows shell并输入以下指令: adb shell这时将产生一个连接安卓设备的shell,然后输入: pm listpackages –f这条指令将列出安卓设备上所有应用的安装包。 http://image.3001.net/images/20150121/1421816149127.jpeg!small 通过查看结果列表,我们就能找到目标应用。 http://image.3001.net/images/20150121/14218161732132.jpeg!small 接下来,需要将该APK文件导入到电脑中。为此,我们需要新打开一个shell并输入以下指令: adb pull/data/app/[.apk file] [location]http://image.3001.net/images/20150121/14218162169326.jpeg!small 到这里,我们已经得到了目标APK文件。然后我们将打开该文件并检查里面的AndroidManifest.xml文件。不幸的是,我们不能直接解压该APK并查看xml文件,因为它是经过二进制编码处理过的,所以首先必须解码才可以。目前针对该问题最受欢迎的工具是apktool。不过,最近我一直在使用APK Studio软件,因为它有一个易于操作的友好界面。所以,在接下来的演示中我都使用APK Studio。 为了开始使用APK Studio,单击绿色的安卓小图标。然后在弹出的窗口中填写项目名称并选择待分析的APK文件,最后设置文件保存的位置。 http://image.3001.net/images/20150121/1421816262350.jpeg!small 打开APK文件之后,选择AndroidManifest.xml文件,然后查找该应用的节点。如果该文件中没有“android:debuggable”标记,那么说明该APK文件是不可调试的。如果有一个标记为‘android:debuggable=”false”’,同样说明该APK文件是不可调试的。 http://image.3001.net/images/20150121/14218162967400.jpeg!small
4、修改AndroidManifest.xml文件开启应用调试功能 apktool和APK Studio比较好的一点是,可以编辑任何反编译的APK文件并重新编译它们。接下来,我们通过添加“android:debuggable”标记来启用该应用的调试功能。编辑AndroidManifest.xml文件,然后在里面添加‘android:debuggable=”true”’。 http://image.3001.net/images/20150121/14218163583947.jpeg!small 添加该标记之后,通过单击菜单上的锤子图标重新创建APK文件。我们重建的APK文件会被保存在build/apk目录中。 http://image.3001.net/images/20150121/14218164298991.jpg!small 重建的APK文件只有在签名之后才能够安装到设备上。所有的安卓应用都需要签名,但大多数应用程序并不检查它们是否是用原始的证书进行签名的。如果此时应用程序检查了证书的原始性,那么直接签名可能就没效果了,除非我们同时也编辑了验证数字证书的相关代码。 接下来,我们需要安装刚刚重建的APK文件。首先,卸载手机中原有的应用程序。然后使用adb重新安装重建的APK文件,输入以下指令: adbinstall [.apk file]http://image.3001.net/images/20150121/14218164731279.jpeg!small 然后检查并确保重新安装的应用在安卓设备上正确地运行。如果一切正常,那么我们回到安卓设备监视器界面并刷新一下,此时可以看到我们的应用已经显示在列表中了。 http://image.3001.net/images/20150121/14218164983012.jpeg!small
5、IDE准备 既然现在我们的应用已经可以调试了,那么接下来可以将调试器附加在它上面。在做这之前,我们需要设置一下IDE。在这篇博文中,我使用IntelliJ IDEA。首先,我创建一个新的安卓项目,该项目名字可以随意取,但是包名字必须跟APK文件的包结构相同。 http://image.3001.net/images/20150121/14218166471544.jpg!small 以上这些都很容易实现。但是,如果你还不确定,可以查看下APK Studio中该APK文件的包结构。对于我的这个应用来说,包结构就是APK的名字“com.netspi.egruber.test”。可以在APK Studio中查看如下图所示。 http://image.3001.net/images/20150121/14218167109759.jpg!small 取消选中“Create HelloWorld Activity”项,其它的地方使用默认设置,这就完成了项目的创建。此时,你的项目结构看起来应该是这样:
http://image.3001.net/images/20150121/14218167421701.jpeg!small 现在已经创建好了项目,就可以将APK文件的源代码导入到该项目中。我们之所以要这么做,是因为调试器知道关于该应用的所有符号名字、方法、变量等信息。安卓应用程序比较好的一点是,它们可以轻松地通过反编译来得到正确的java源代码。接下来我们就需要这么做,然后将源代码导入到IDE项目中。
6、Dump apk文件并反编译得到源代码 首先,为了得到源代码,需要将APK文件转换到jar文件。然后,可以使用一个java反编译器来恢复到java源代码,我们使用dex2jar工具来进行此步骤。Dex2jar包含一个d2j-dex2jar.bat文件,通过执行该文件就可以将APK文件转换成jar文件。它的语法很简单: d2j-dex2jar.bat[.apk file]http://image.3001.net/images/20150121/1421816806732.jpeg!small 到这里,你应该得到了一个jar文件。下一步,我们将使用工具JD-GUI来打开该jar文件。 http://image.3001.net/images/20150121/14218168339094.jpeg!small 现在你应该可以看到jar文件的包结构啦,包里面都是可读的java源代码文件。接下来我们通过选择“文件”=>“保存所有文件”来将源代码保存为一个zip文件。 http://image.3001.net/images/20150121/14218168609346.jpeg!small 保存源代码之后,将其解压到当前目录中。 http://image.3001.net/images/20150121/14218168862166.jpeg!small 现在我们需要将这两个目录导入到之前创建的安卓项目中。对于IntelliJ IDEA来说,进入到项目的src文件夹并将这两个目录粘贴到这里。 http://image.3001.net/images/20150121/14218169171662.jpeg!small 在IntelliJ中,如果我们返回到项目中,则项目结构将会刷新。 http://image.3001.net/images/20150121/14218170049312.jpg!small 单击其中一个导入的活动文件将会在右边的窗口中显示该文件中的源代码。在下面的截图中可以看到,我导入的源代码是使用ProGuard混淆过的。 http://image.3001.net/images/20150121/14218170427218.jpeg!small
7、附加调试器 现在我们的项目中已经填充了应用程序的源代码,我们就可以开始设置断点。在这个例子中,我在一个方法处设置一个断点,当向一个文本输入框中输入内容的时候程序就会调用该方法。即使代码是经过混淆过的,也可以正常工作。 http://image.3001.net/images/20150121/14218170861399.jpeg!small 设置断点后,单击主界面右上角的小屏幕图标弹出安卓设备中的进程列表,该列表只列出可以调试的进程,然后单击目标进程将调试器附加到该进程。当然,不同的IDE对应的功能图标不同。 http://image.3001.net/images/20150121/14218171143461.jpeg!small http://image.3001.net/images/20150121/14218171277463.jpeg!small 选中目标进程之后,调试器就连接到了安卓设备。 http://image.3001.net/images/20150121/14218171616837.jpeg!small 在我测试的应用中,我将向文本输入框中输入数字42,别忘了之前我们在这里设置了断点。 http://image.3001.net/images/20150121/14218172108053.jpg!small 在单击“Enter Code”按键后,进程执行到断点处时会暂停。之所以这样能够正常工作,原因是调试器知道在安卓设备上该调用什么程序。编译后的安卓应用中包含了调试信息,例如变量名字,任何理解java调试线协议(JDWP)的调试器都可以识别这些信息。如果一个安卓应用允许调试,那么一个兼容JDWP的调试器,例如大多数的Java IDE,将能够连接安卓虚拟机并读取和执行调试指令。 http://image.3001.net/images/20150121/14218172342226.jpeg!small 此时,我们就能够在变量区域窗中查看到我们向应用中输入的内容。 http://image.3001.net/images/20150121/14218172635530.jpeg!small
8、结论 在这里,我们不仅能够从应用中读取数据,而且还能够插入自己的数据。如果我们想中断程序流程或者绕过逻辑判断,那么本文中的方法将可以起到作用。通过调试,我们可以更好地理解安卓应用如何执行某些操作。尤其是当我们需要查看加密功能是如何使用,或者确定动态密钥的值时,该方法将可以派上用场。当调试函数与文件系统或者数据库进行交互时,此方法也能帮助我们查看信息是如何存储的。最主要的是,不需要root权限,我们就能在安卓设备上执行这种类型的测试。
|