unity烟尘_用烟尘检测Android应用

unity烟尘

In this blog post, I describe how to use Soot to read an Android APK (without the source code), change its methods and classes(even add a new class), and write the new code into a working APK. A few notes:

在此博客文章中,我描述了如何使用Soot读取Android APK(无源代码),更改其方法和类(甚至添加新类),以及将新代码写入有效的APK中。 一些注意事项:

介绍 (Intro)

One way to analyze Android apps is by running them on a device (or an emulator) and observe the logs to capture some desired information. If you have the app’s source code, you can log whatever you want, e.g., record the execution of a method at its beginning. However, in the cases that the source code is not available, such as in security analysis where you are dealing with possible malicious apps, you need to instrument the APK, which is compiled in Dalvik byte code. And as you may have guessed, Soot is going to save the day.

分析Android应用程序的一种方法是在设备(或仿真器)上运行它们,并观察日志以捕获一些所需信息。 如果您拥有应用程序的源代码,则可以记录所需的任何内容,例如,在方法开始时记录其执行情况。 但是,在源代码不可用的情况下(例如,在处理可能的恶意应用程序的安全性分析中),您需要检测以Dalvik字节码编译的APK。 就像您可能已经猜到的那样,Soot将挽救这一天。

The following figure shows an overview of how Soot reads/modifies/writes an APK. First, Soot uses Dexpler to convert the Dalvik byte code to Jimple bodies. Then it runs Whole Packs that can transform, optimize, and annotate the whole program (for example, call graph generation). Next, the Jimple Transformation Packs will run on each Jimple body (this is the part that we modify the code). Finally, Soot converts all Jimple bodies to Baf (a low-level intermediate representation in Soot), and using Dexpler the whole code will be compiled into an APK. The instrumented APK can be installed on an Android device (you just need to sign it first). Now, let’s instrument some apps!

下图概述了Soot如何读取/修改/写入APK。 首先,Soot使用Dexpler 将Dalvik字节码转换为Jimple主体。 然后,它运行可以对整个程序进行转换,优化和注释的Whole Pack (例如,调用图生成)。 接下来,Jimple Transformation Packs将在每个Jimple主体上运行(这是我们修改代码的部分)。 最后,Soot将所有Jimple主体转换为Baf(Soot中的低级中间表示),然后使用Dexpler将整个代码编译成APK。 可以将检测到的APK安装在Android设备上(您只需要先对其进行签名)。 现在,让我们来介绍一些应用程序!

Android记录器 (Android Logger)

We are going to add a simple statement(System.out.println("Beginning of method: " + METHOD_NAME)) at the beginning of each APK method using a BodyTransfomer. Before reading further, please clone the SootTutorial repository and have AndroidLogger.java in front of you.

我们将使用System.out.println("Beginning of method: " + METHOD_NAME)在每个APK方法的开头添加一个简单的语句( System.out.println("Beginning of method: " + METHOD_NAME) )。 在继续阅读之前,请克隆SootTutorial存储库并在您前面安装AndroidLogger.java

建立 (Setup)

In order to analyze an Android APK with Soot, you need to install the Android SDK in your machine. You can either use the SootTutorial docker image (docker run -it noidsirius/soot_tutorial:lates) or follow this link.

为了使用Soot分析Android APK,您需要在计算机上安装Android SDK。 您可以使用SootTutorial码头工人镜像( docker run -it noidsirius/soot_tutorial:lates )或点击此链接

Soot needs a special configuration for analyzing Android apps. The following code shows the options that I used for the instrumentation. Each option accompanies with a comment that describes it.

Soot需要特殊的配置才能分析Android应用。 以下代码显示了我用于检测的选项。 每个选项均附带描述它的注释。

public static void setupSoot(String androidJar, String apkPath, String outputPath) {
    // Reset the Soot settings (it's necessary if you are analyzing several APKs)
    G.reset();
    // Generic options
    Options.v().set_allow_phantom_refs(true);
    Options.v().set_whole_program(true);
    Options.v().set_prepend_classpath(true);
    // Read (APK Dex-to-Jimple) Options
    Options.v().set_android_jars(androidJar); // The path to Android Platforms
    Options.v().set_src_prec(Options.src_prec_apk); // Determine the input is an APK
    Options.v().set_process_dir(Collections.singletonList(apkPath)); // Provide paths to the APK
    Options.v().set_process_multiple_dex(true);  // Inform Dexpler that the APK may have more than one .dex files
    Options.v().set_include_all(true);
    // Write (APK Generation) Options
    Options.v().set_output_format(Options.output_format_dex);
    Options.v().set_output_dir(outputPath);
    Options.v().set_validate(true); // Validate Jimple bodies in each transofrmation pack
    // Resolve required classes 
    Scene.v().addBasicClass("java.io.PrintStream",SootClass.SIGNATURES);
    Scene.v().addBasicClass("java.lang.System",SootClass.SIGNATURES);
    Scene.v().loadNecessaryClasses();
}

The last part of the setup code is not setting an option but resolving the required classes for the instrumentation. Recall that we want to add a new statement at the beginning of each APK method, which its Jimple representation is:

设置代码的最后一部分不是设置选项,而是解决了仪器所需的类。 回想一下,我们想在每个APK方法的开头添加一个新语句,其Jimple表示为:

$r1 = <java.lang.System: java.io.PrintStream out>
virtualinvoke $r1.<java.io.PrintStream: void println(java.lang.String)>("<SOOT_TUTORIAL> Beginning of method METHOD_NAME")

Since these statements require classes java.lang.System and java.io.PrintStream, we should resolve them in Soot, which is done in lines 20–21 in setupSoot.

由于这些语句需要java.lang.Systemjava.io.PrintStream类,因此我们应该在Soot中解决它们,这在setupSoot第20-21行中setupSoot

身体转变 (Body Transformation)

Now, we’re ready to write a BodyTransformer to modify the code. The following code shows myLogger BodyTransformer. Note that the transformer is added to the jtp pack and it will be applied to all methods’ bodies.

现在,我们准备编写一个BodyTransformer来修改代码。 以下代码显示了myLogger 。 请注意,转换器已添加到jtp包中,并将应用于所有方法的主体。

PackManager.v().getPack("jtp").add(new Transform("jtp.myLogger", new BodyTransformer() {
    @Override
    protected void internalTransform(Body b, String phaseName, Map<String, String> options) {
        // First we filter out Android framework methods
        if(InstrumentUtil.isAndroidMethod(b.getMethod()))
            return;
        JimpleBody body = (JimpleBody) b;
        UnitPatchingChain units = b.getUnits();
        List<Unit> generatedUnits = new ArrayList<>();
        // The message that we want to log
        String content = String.format("%s Beginning of method %s", InstrumentUtil.TAG, body.getMethod().getSignature());
        // In order to call "System.out.println" we need to create a local containing "System.out" value
        Local psLocal = InstrumentUtil.generateNewLocal(body, RefType.v("java.io.PrintStream"));
        // Now we assign "System.out" to psLocal
        SootField sysOutField = Scene.v().getField("<java.lang.System: java.io.PrintStream out>");
        AssignStmt sysOutAssignStmt = Jimple.v().newAssignStmt(psLocal, Jimple.v().newStaticFieldRef(sysOutField.makeRef()));
        generatedUnits.add(sysOutAssignStmt);
        // Create println method call and provide its parameter
        SootMethod printlnMethod = Scene.v().grabMethod("<java.io.PrintStream: void println(java.lang.String)>");
        Value printlnParamter = StringConstant.v(content);
        InvokeStmt printlnMethodCallStmt = Jimple.v().newInvokeStmt(Jimple.v().newVirtualInvokeExpr(psLocal, printlnMethod.makeRef(), printlnParamter));
        generatedUnits.add(printlnMethodCallStmt);
        // Insert the generated statement before the first  non-identity stmt
        units.insertBefore(generatedUnits, body.getFirstNonIdentityStmt());
        // Validate the body to ensure that our code injection does not introduce any problem (at least statically)
        b.validate();
    }
}));

First of all, we need to filter out non-APK methods (lines 5–6), because Soot loads these methods and may not be aware that they belong to Android libraries (checkisAndroidMethod). Then, we can create the content that we want to log (line 11, the method’s signature is body.getMethod().getSignature()). We add a tag to the content so we can retrieve these logs later. Recall that Jimple statements are three-address code; so, in order to invoke System.out.println we need to first create a local variable, psLocals and $r1in the Jimple code, that points to System.out (lines 13–16), then invoke the println method using that local variable (lines 19–21).

首先,我们需要过滤掉非APK方法(第5-6行),因为Soot会加载这些方法,并且可能不知道它们属于Android库(请检查isAndroidMethod )。 然后,我们可以创建要记录的内容(第11行,方法的签名为body.getMethod().getSignature() )。 我们将标记添加到内容,以便稍后可以检索这些日志。 回想一下,Jimple语句是三地址代码。 因此,为了调用System.out.println我们需要首先在Jimple代码中创建一个局部变量psLocals$r1 ,该变量指向System.out (第13-16行),然后使用该局部变量调用println方法变量(第19-21行)。

For adding a local variable we input the body and the type of the local variable to a LocalGenerator (can be found here). To create a Jimple statement, we use the singleton Jimple.v() . For example, line 16 creates an AssignStmt equivalent to $r1 = <java.lang.System: java.io.PrintStream out> (note that you need to pass the reference of a SootField or SootMethod). Similarly, line 21 creates an InvokeStmt that has a virtual invoke expression equivalent to virtualinvoke $r1.<java.io.PrintStream: void println(java.lang.String)>("<SOOT_TUTORIAL> Beginning of method METHOD_NAME") . Note that the parameter of this invocation must be a Value; therefore, we use StringConstant to create a String constantValue equal to the content.

为了添加局部变量,我们将主体和局部变量的类型输入到LocalGenerator(可以在此处找到)。 要创建一个Jimple语句,我们使用单例Jimple.v() 。 例如,线16将创建一个AssignStmt相当于$r1 = <java.lang.System: java.io.PrintStream out> (注意,需要传递一个SootField或SootMethod的参考)。 类似地,第21行创建一个InvokeStmt ,它具有一个等效于virtualinvoke $r1.<java.io.PrintStream: void println(java.lang.String)>("<SOOT_TUTORIAL> Beginning of method METHOD_NAME")的虚拟调用表达式。 注意,该调用的参数必须是一个Value ; 因此,我们使用StringConstant创建一个字符串常量Value等于内容。

So far, we have created two Jimple statements (lines 16 and 21). Note that, the IdentitiyStmts of Jimple bodies (that determines the parameters and this pointer) must appear at the beginning; therefore, we need to find the first non-identity statements and insert our code before it (look at lines 17, 22, and 24). Finally, we have to validate the new modified body to make sure no problems exist (at least statically). The final step is to just run the packs (PackManager.v().runPacks()) and write the output into an APK (PackManager.v().writeOutput())

到目前为止,我们已经创建了两个Jimple语句(第16和21行)。 请注意,Jimple主体的IdentitiyStmts(确定参数和this指针)必须出现在开头。 因此,我们需要找到第一个非身份声明,并在其之前插入我们的代码(请看第17、22和24行)。 最后,我们必须验证新的修改主体,以确保不存在任何问题(至少是静态存在)。 最后一步是只运行包( PackManager. v ().runPacks()和写入输出到APK( PackManager. v ().writeOutput()

登录并运行! (Sign and Run!)

That’s it! You add a Java statement at the beginning of all APK methods under 20 lines of code in Soot (actually it should be less than 10 if I didn’t expand all statements to write comments :) ). However, there is one more step before you can install the instrumented APK: you need to sign it. You can use the bash script sign.sh that basically first run zipalign (that aligns the APK) and then run apksigner using a keyset.

而已! 您在Soot的20行代码下的所有APK方法的开头添加了一条Java语句(实际上,如果我没有展开所有语句来写注释,则它应该少于10条):)。 但是,在安装已检测的APK之前,还有一个步骤:您需要对其进行签名。 您可以使用bash脚本sign.sh ,该脚本首先运行zipalign (对齐APK),然后使用键集运行apksigner

In summary, run AndroidLogger , either in Intellij or CLI by running ./gradlew run --args="AndroidLogger" . It should create demo/Android/Instrumented/calc.apk and you can sign and install it by running the following commands (don’t forget to connect your device to your machine or run an Android emulator):

总之,运行AndroidLogger通过运行,无论是在的Intellij或CLI ./gradlew run --args="AndroidLogger" 它会创建demo/Android/Instrumented/calc.apk ,您可以通过运行以下命令进行签名和安装(不要忘记将设备连接到计算机或运行Android模拟器):

cd ./demo/Android
./sign.sh Instrumented/calc.apk key "android"
adb install -r -t Instrumented/calc.apk

To see the logs, run adb logcat | grep -e "<SOOT_TUTORIAL>" and then use your device and open Numix Calculator. You should see something like this on your terminal:

要查看日志,请运行adb logcat | grep -e "<SOOT_TUTORIAL>" adb logcat | grep -e "<SOOT_TUTORIAL>" ,然后使用您的设备并打开Numix Calculator 。 您应该在终端上看到以下内容:

07-07 11:41:26.570 32487 32487 I System.out: <SOOT_TUTORIAL> Beginning of method <com.numix.calculator.view.CalculatorDisplay: void onSizeChanged(int,int,int,int)>
07-07 11:41:26.571 32487 32487 I System.out: <SOOT_TUTORIAL> Beginning of method <com.numix.calculator.view.ScrollableDisplay: void onLayout(boolean,int,int,int,int)>
07-07 11:41:26.571 32487 32487 I System.out: <SOOT_TUTORIAL> Beginning of method <com.numix.calculator.view.ScrollableDisplay: com.numix.calculator.view.AdvancedDisplay getView()>
07-07 11:41:26.589 32487 32487 I System.out: <SOOT_TUTORIAL> Beginning of method <com.numix.calculator.view.ScrollableDisplay: void scrollTo(int,int)>

If you’re interested in some specific methods you can filter it by piping grep -e "METHOD_SIGNATURE" to adb logcat

如果您对某些特定方法感兴趣,可以通过将grep -e "METHOD_SIGNATURE"传递给adb logcat过滤

Android类注入器 (Android Class Injector)

Let’s do some more existing things. Assume in the previous example, instead of just logging the name of the method, we wanted to keep track of the number of methods that have been called so far (or simply count the executed method). One way to do this is to have a static integer field and increase it by one when a method is executed. Now, I show how to create a new class, field, and method from scratch, add them to the APK, and more importantly, use them in other methods. The whole code can be found in AndroidClassInjector.java and InstrumentUtil.java and the following code shows creating a class:

让我们做一些现有的事情。 假设在前面的示例中,我们不仅要记录方法的名称,还希望跟踪到目前为止已被调用的方法的数量(或仅对已执行的方法进行计数)。 一种方法是具有一个静态整数字段,并在执行方法时将其增加一个。 现在,我展示如何从头开始创建新的类,字段和方法,将它们添加到APK,更重要的是,在其他方法中使用它们。 整个代码可在AndroidClassInjector.javaInstrumentUtil.java中找到,以下代码显示如何创建一个类:

SootClass createCounterClass(String packageName) {
    // The new class must be inside the APK package.
    String staticCounterSignature =  packageName+".SootTutorialStaticCounter";
    SootClass staticCounterClass = new SootClass(staticCounterSignature, Modifier.PUBLIC);
    staticCounterClass.setSuperclass(Scene.v().getSootClass("java.lang.Object"));
    staticCounterClass.setApplicationClass();
    return staticCounterClass;
}

The new class must be in the same package of APK in order that other methods could access it (line 3). Also, it should be public (Modifier at line 4), a subclass of Object (line 5), and a Soot Application class (line 6). At the end of createCounterClass the generated class has been created and added to the Scene. Now let’s create a static integer field for this class:

新类必须与APK位于同一包中,以便其他方法可以访问它(第3行)。 同样,它应该是公共的(第4行的修饰符),Object的子类(第5行)和Soot Application类(第6行)。 在createCounterClass的末尾,已创建生成的类并将其添加到Scene 。 现在让我们为此类创建一个静态整数字段:

SootField addCounterFieldToClass(SootClass staticCounterClass) {
    // counterField is a static integer field in StaticCounter class.
    SootField counterField = new SootField("counter", IntType.v(), Modifier.PUBLIC | Modifier.STATIC);
    staticCounterClass.addField(counterField);
    return counterField;
}

As can be seen, it’s so simple: just instantiate a SootField, provide its name, type, and its modifiers (note that the modifiers are aggregated by or, | binary operator). Then we add this field to the class. Now, we are going to create a method that increments this field and prints it:

可以看到,它是如此简单:只需实例化SootField ,提供其名称,类型及其修饰符(请注意,修饰符由or, |二进制运算符聚合)。 然后,我们将此字段添加到类中。 现在,我们将创建一个增加该字段并打印的方法:

SootMethod addIncNLogMethod(SootClass staticCounterClass, SootField counterField){
    // incrementAndLog method increment counterField and prints its value.
    SootMethod incMethod = new SootMethod("incrementAndLog",
            Arrays.asList(new Type[]{}),
            VoidType.v(), Modifier.PUBLIC | Modifier.STATIC);
    staticCounterClass.addMethod(incMethod);
    JimpleBody body = Jimple.v().newBody(incMethod);


    UnitPatchingChain units = body.getUnits();
    // Increment counterField by one
    Local counterLocal = InstrumentUtil.generateNewLocal(body, IntType.v());
    units.add(Jimple.v().newAssignStmt(counterLocal, Jimple.v().newStaticFieldRef(counterField.makeRef())));
    units.add(Jimple.v().newAssignStmt(counterLocal,
            Jimple.v().newAddExpr(counterLocal, IntConstant.v(1))));
    units.add(Jimple.v().newAssignStmt(Jimple.v().newStaticFieldRef(counterField.makeRef()),counterLocal));


    // Log the counter value
    units.addAll(InstrumentUtil.generateLogStmts(body, "Counter's value: ", counterLocal));


    // The method should be finished by a return
    Unit returnUnit = Jimple.v().newReturnVoidStmt();
    units.add(returnUnit);
    body.validate();
    incMethod.setActiveBody(body);
    return incMethod;
}

It’s a little bit more complex than creating the field. For instantiating a SootMethod you need to pass name, parameters’ types, return type, and modifier (lines 3–5). In addition to that, a method needs to have a body (line 7) that represents the functionality of the method. First, we increment the counter field by assigning its value to a temporary local variable (recall Jimple has three-addressed statements), add the local by one, and reassign the field to the incremented number (lines 11–15). Next, we log this number similarly to AndroidLogger; however, since we have two things to print (the string "Counter's value" and the local counterLocal ) we need to contact them using StringBuilder (for more info look at the code in InstrumentUtil.java). The final statement must be a return statement (line 21). Next, we validate body and make it the active body of incMethod (lines 23-24).

它比创建字段要复杂一些。 为了实例化SootMethod您需要传递名称,参数的类型,返回类型和修饰符(第3–5行)。 除此之外,方法还需要具有表示该方法功能的主体(第7行)。 首先,我们通过将counter字段的值分配给一个临时的局部变量来递增counter字段(回想起Jimple有三个地址的语句),将该counter字段加1,然后将该字段重新分配给递增的数字(第11-15行)。 接下来,我们类似于AndroidLogger记录此数字。 但是,由于要打印两件事(字符串"Counter's value"和本地counterLocal ),我们需要使用StringBuilder与它们联系(有关更多信息,请参见InstrumentUtil.java中的代码)。 最后的声明必须是return声明(第21行)。 接下来,我们验证body并将其incMethod的活动主体(第23-24行)。

We’re done! A new class, field, and method have been created and added to the APK. To invoke incrementAndLog method we use a similar approach to AndroidLoggger and you can find the corresponding BodyTransformer here. Once you run AndroidClassInjector.java, sign the instrumented app, install and open it, you should see something like this in the adb logcat:

大功告成! 新的类,字段和方法已创建并添加到APK。 要调用crementAndAndLog方法,我们使用与AndroidLoggger类似的方法,您可以在此处找到相应的BodyTransformer。 一旦运行AndroidClassInjector.java ,对已检测的应用程序进行签名,安装并打开它,您应该在adb logcat中看到以下内容:

07-07 14:33:03.177  3967  3967 I <SOOT_TUTORIAL>: Beginning of method <com.numix.calculator.view.ScrollableDisplay: void onMeasure(int,int)>
07-07 14:33:03.177 3967 3967 I <SOOT_TUTORIAL>: Counter's value: 3935
07-07 14:33:03.177 3967 3967 I <SOOT_TUTORIAL>: Beginning of method <com.numix.calculator.view.ScrollableDisplay: void onMeasure(int,int)>
07-07 14:33:03.177 3967 3967 I <SOOT_TUTORIAL>: Counter's value: 3936
07-07 14:33:03.177 3967 3967 I <SOOT_TUTORIAL>: Beginning of method <com.numix.calculator.view.ScrollableDisplay: void onMeasure(int,int)>
07-07 14:33:03.177 3967 3967 I <SOOT_TUTORIAL>: Counter's value: 3937
07-07 14:33:03.177 3967 3967 I <SOOT_TUTORIAL>: Beginning of method <com.numix.calculator.view.ScrollableDisplay: void onMeasure(int,int)>
07-07 14:33:03.177 3967 3967 I <SOOT_TUTORIAL>: Counter's value: 3938
07-07 14:33:03.177 3967 3967 I <SOOT_TUTORIAL>: Beginning of method <com.numix.calculator.view.ScrollableDisplay: void onMeasure(int,int)>

结论 (Conclusion)

In this blog post, we reviewed how Soot can reads/modifies/writes an Android APK and get familiar with Soot packs, in particular, Jimple Transformer. Feel free and play with the code, especially AndroidClassInjector.java, to create more interesting applications. For example, you can log the running thread or count the execution of each method separately.

在此博客文章中,我们回顾了Soot如何读取/修改/编写Android APK并熟悉Soot包,尤其是Jimple Transformer。 随意使用代码,尤其是AndroidClassInjector.java ,以创建更多有趣的应用程序。 例如,您可以记录正在运行的线程或分别计算每个方法的执行。

If you’re interested in this work and you want to use it in a real project, I suggest you take a look at Android Soot Instrumentor repository which is designed for CLI and is more configurable. I created this repository based on my experience in Software Engineering research projects that I was involved and it would be great if you can collaborate to make it more useful.

如果您对这项工作感兴趣,并且想在实际项目中使用它,建议您看一下专为CLI设计且可配置性更高的Android Soot Instrumentor存储库 。 我根据参与的软件工程研究项目的经验创建了此存储库,如果您可以协作使其更有用,那就太好了。

翻译自: https://medium.com/swlh/instrumenting-android-apps-with-soot-dd6f146ff4d2

unity烟尘

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值