当我在探索一个新的安卓设备的时候,我经常会遇到暴露了 binder 接口的服务的情况,也就是所谓的“bound Services”。与 unbound Services 不同,我们不能
当我在探索一个新的安卓设备的时候,我经常会遇到暴露了 binder 接口的服务的情况,也就是所谓的“bound Services”。与 unbound Services 不同,我们不能只使用 “am” 命令来与该服务进行交互;相反,我们需要创建一个安卓应用程序。即使有 bound Services 的源代码,创建一个用来连接该服务的客户端也并非易事,要是没有源码那就更加困难了。很多博客和课程都讨论了怎么创建 bound Services ,但我发现并没有文章是从攻击者的角度来讲解与 bound Services 的交互的。
在这篇文章中我将会创建一个暴露 bound Services 的应用程序,向你展示如何用该应用程序反编译后的 Smali 代码来理解 bound Services,并发布一个自动进行 AIDL 重建的工具。本文不会深入地解释 bound Services。
用 Java创建 bound Service
首先,我们将创建一个应用程序,用 “com.jakev.boundserver” 来定义我们暴露的 bound Service。这个 bound Service 有一个叫“exec()”的方法,接受一个字符串命令并执行,然后返回输出结果(输出结果用于调试,在你的程序中切误加入这个)。这个工程的源码(和 APK)可以从本文最后的链接中下载。
我们将分3步创建 bound Service:
•在 “AndroidManifest.xml” 中声明一个新组件
•创建安卓接口定义语言(AIDL)接口
•创建一个新的服务类,执行 “onBind()” 方法
在我们的 AndroidManifest.xml 文件中声明一个新的暴露的服务 “.ITestService”:
接下来,我们创建一个 AIDL 接口,叫作 “com.jakev.boundserver.aidl.TestInterface”。如上文所述,我们的 “exec()" 方法会接收一个 String类型的参数,再返回一个 String 类型的值:
最后,我们创建一个叫作 “com.jakev.boudserver.ITestService.java” 的服务类。我们需要实现 “onBind()” 方法,并向我们上面创建的 “TestInterface” 接口返回一个实例:
为了与这个服务进行交互,我们需要创建一个包括上面的 AIDL 文件的应用程序。如果你是开发人员,那么你很可能会分发一个包含 “TestInterface.aidl” 文件和其他支撑类的 JAR 库,以便于其他开发人员的应用程序与你的新服务交互。但如果你是攻击者,你就不必在这上面浪费心思了。
探索服务 APK
假设我们找到了安装在设备上的 “com.jakev.boundserver” 应用程序,现在现在我们需要搞定这个 “.ITestService” 服务。我们没有这个应用程序的源代码,只有它的 APK。我们把这个应用从设备中pull下来,并用 “apktool” 来解压它:
第一步,我们来看 “.ITestService" 服务类,它在 “smali/com/jakev/boundserver/ITestService.smali” 中。“onBind()” 方法很简单,我们可以看到这个服务正在实例化一个内部类 “ITestService$1” 并返回它。这就是我们上面的源代码中 “new TestInterface.Stub() { }”这段代码干的事。
打开 “smali/com/jakev/boundserver/ITestService$1.smali”,我们会看到它正在执行一个叫 “com.jakev.boundserver.aidl.TestInterface$Stub” 的类,这个类我们之前没有看过。在虚拟方法部分,我们可以看到 “exec()” 的实际运行过程。作为一个逆向工程师,你很有可能从这个方法入手,去确定 “exec()” 方法的功能。
接下来,看看我们的 AIDL 文件发生了什么变化。现在有3个不同的类,分别叫做 “TestInterface.smali”、"TestInterface$Stub.smali" 和 “TestInterface$Stub$Proxy.smali”,如下图所示:
Android Studio 在我们创建应用程序时就帮我们生成了这三个类。许多论文都讨论了它们所扮演的角色,所以我在此就不赘述了。几个关键点如下:
•“TestInterface” 实现了 “android.os.IInterface" 。它包含了所有 Binder 抽象方法的定义。
•“TestInterface$Stub” 实现了 “android.os.Binder” ,它包含了存储为字段的 Binder 事务标识符,命名规则参照 “TRANSACTION_{methodName}”。这些 ID 字段跟用 “service” 命令行程序调用服务时使用的是一样的。
•“TestInterface$Stub$Proxy" 类没有实现或集成任何接口或方法,但它包含了每个 Binder 方法的返回类型和参数信息。
如果你在看一个新的应用程序并想列举它的 Binder 接口和方法执行情况,那么你可以用下面的搜索方法:
搜索 AIDL 类:
grep -r "super.*IInterface" ./smali
搜索 AIDL 的执行情况:
grep -r "super.*${AIDL class from above}" ./smali
利用这些信息,我们可以重建 AIDL,并把它加入到我们的客户端程序。你可以手动地操作这一步,也可以使用我做的 “GenerateAIDL.jar” 工具。这个工具用 DexLib2 库来分析应用程序的 DEX 代码并输出 AIDL 文件,帮助你快速地将 AIDL 文件添加到你的客户端。
如果我们打开这个文件,我们应该可以看到一个 AIDL 接口,跟我们上面创建的那个很相似。
记住这只是一个很小的例子。在真实环境中可能有不止一个方法(还很可能会有一些导入的库)。
攻击 bound Services
现在我们要利用刚刚生成的 AIDL 文件来创建客户端 “com.jakev.serverclient" 了,它与 “.ITestService" 进行交互。我尽可能地让这个工程简单一些,这样易于重复使用。
第一步是创建或导入我们的 AIDL 文件。我们希望将上面生成的 AIDL 文件导入一个特定的文件夹,注意将包名称写对:
第二步,我们要在我之前创建的模版 “MainActivty" 类的基础上加以修改。首先以导入的方式添加 AIDL,然后添加一个名为 “service" 的对象:
接着,改变 “initService()” 方法中 Intent 的类名,改成你要与之交互的服务的名字:
最后一步,修改本地 “ServiceConnection" 类中的 “onServiceConnect()" 方法,来实现与 bound Services 的交互。在这个例子中,我们调用其中的 “exec()" 方法,提供命令“id”,并且将它返回结果记录在安卓日志里。
如果我们创建、安装并运行我们的客户端应用程序,我们应该会看到日志中输出的 “id” 命令:
总结
如果有足够的权限,攻击者可以跟其他组件一样与 bound Services 交互。所以重要的是,你要对你的应用程序罗列一下暴露的组件,确保这些都是你想要暴露的。对于任何执行敏感操作的组件(比如执行命令),你都要确保对它们应用了正确权限。
资源:
•http://stackoverflow.com/questions/15330233/remote-service-with-aidl-file-serviceconnection-never-called
•http://developer.android.com/reference/android/content/ServiceConnection.html
•http://developer.android.com/guide/components/bound-services.html
•http://developer.android.com/guide/components/aidl.html
•https://newcircle.com/s/post/48/implementing_remote_interface_using_aidl
下载:
•TestBoundServer Studio Project [source] [APK]
•TemplateServiceClient Studio Project [source] [APK]
•GenerateAIDL.jar [source] [JAR]