当前位置:网站首页>[免费专栏] Android安全之APK动态方式逆向应用【三种Smali注入方法】

[免费专栏] Android安全之APK动态方式逆向应用【三种Smali注入方法】

2022-08-09 17:59:00 菠萝_橙留香


欢迎新同学的光临
… …
人若无名,便可专心练剑


我不是一条咸鱼,而是一条死鱼啊!


0x01 前言

动态调试是在没有软件源代码的情况下,通过调试程序来跟踪分析汇编代码,查看寄存器的值,了解软件的执行流程,反馈程序执行时的中间结果

动态调试一般与静态分析结合使用,尤其是在静态分析难以取得突破时,需要使用动态调试进行辅助分析。主流调试工具包括DDMS、IDA Pro、AndBug、gdb等,主要涉及两种调试方法:

  • 通过方法跟踪的方式,对Android SDK开发的Java层程序进行动态调试
  • 通过附加进程的方式,对Android NDK开发的原生动态链接库进行调试

1.2 动态分析技巧

打印Log法:

  • 设置应用可调试
  • 插入Log
  • 回编译并安装运行
  • 查看Log信息

栈跟踪法:

  • 在关键位置插入代码
  • logcat中查看信息

Method Profiling:

  • 运行Device Monitor
  • 监控方法

UI查看:

  • adb命令查看
  • Device Monitor查看

1.2.1 信息反馈法( 资源id / 字符串 )

所谓信息反馈法,是指先运行目标程序,然后根据程序运行时给出的反馈信息作为突破口寻找关键代码。例如运行目标程序并输入错误的注册码时,会弹出提示“无效用户名或注册码”,这就是程序反馈给我们的信息。通常情况下,程序中用到的字符串会存储在String.xml文件或者硬编码到程序代码中,如果是前者的话,字符串在程序中会以id 的形式访问,只需在反汇编代码中搜索字符串的id 值即可找到调用代码处;如果是后者的话,在反汇编代码中直接搜索字符串即可。

如果想快速找到某一个按钮或者某个界面的相关代码,即仍旧是找到ID值,再通过JEB等工具进行查找,那么如何找到id值,这里可以使用工具uiautomatorviewer.bat(SDK)

通俗的来说就是,根据程序正常运行的提示信息进行定位,例如:错误提示,运行提示等等。可以在程序中直接搜索字符串,当提示信息在 String.xml 资源文件中的时候,可以根据 R.java 的映射文件,查找对应的资源id,然后在 smali 或者在 ida 窗口中进行搜索

1.2.2 特征函数法( api 函数 )

这种定位代码的方法与信息反馈法类似。在信息反馈法中,无论程序给出什么样的反馈信息,终究是需要调用Android SDK 中提供的相关API 函数来完成的。比如,弹出注册码错误的提示信息就需要调用Toast.MakeText().Show()方法,在反汇编代码中直接搜索Toast应该很快就能定位到调用代码,如果 Toast在程序中有多处的话,可能需要分析人员逐个甄别

通俗的来说就是,根据程序提示信息的方法,在对应的 Android 的 API 下断点,通过 API 来检索相关的代码

1.2.3 顺序查看法(分析程序执行流程 / 病毒分析)

顺序查看法是指从软件的启动代码开始,逐行的向下分析,掌握软件的执行流程,这种分析方法在病毒分析时经常用到

通俗的来说就是,从 AndroidManifest.xml 中找到 主 Activity 界面,然后顺序分析代码,通常在分析病毒软件时使用。( 通过 UI 进行分析, adb shell dumpsys activity top )

1.2.4 代码注入法( 动态调试 / 插入log / 查看logcat / 分析加解密 )

动态调试,又叫插桩,即在关键反汇编代码处插入可以输出 logcat 调试信息的代码

例如,smali代码注入或者动态修改程序,一般都可归结为代码注入

1.2.5 栈跟踪法( 动态调试 / 函数调用流程 )

栈跟踪法属于动态调试的方法,原理是输出运行时栈调用跟踪信息,然后查看函数调用序列来理解方法的执行流程

通俗的来说就是,利用输出时的栈跟踪信息,通过查看栈上的函数调用序列来理解方法的执行流程

1.2.6 Method Profiling( 动态调试 / 热点分析 / 函数调用流程 )

Method Profiling 动态调试方法,主要用于热点分析和性能优化。除了可以记录每个函数赵勇的CPU时间外,还可以跟踪所有的函数调用关系,并提供比栈跟踪法更详细的函数调用序列报告

Method Profiling 动态调试方法,主要是使用ptrace来进行,相应的有hook等等技术

0x02 工具+环境

常见环境:

  • apktool+eclipse/idea/android studio/netbeans

IDA pro + DDMS 主要用来调试so文件,当然也可以调试dex文件

linux环境:

  • andbug/gdb+gdb_server

其他:

  • gikdbg/cygwin+ndk-gdb

Hook技术:

  • Xposed
  • Cydia Substrate
  • Frida
  • 自定义hook

0x03 什么是Smali注入?

Smali注入又称Smali插桩(Smali Instrumentation),WIKI解释:它是在保证被测程序原有逻辑完整性的基础上在程序中插入一些探针(又称为“探测仪”),通过探针的执行并抛出程序运行的特征数据,通过对这些数据的分析,可以获得程序的控制流和数据流信息,进而得到逻辑覆盖等动态信息,从而实现测试目的的方法。这几个词(保证程序原有逻辑性、插入探针、抛出特征数据)也是samli注入需要关注的几个关键点所在

0x04 什么是Log类?

SDK提供Android.util.Log 类输出调试信息,总共有五种调试方法(v(),d(),i(),w(),e()) ,六种调试状态(verbose,debug,info,warning,error,assert) 。在插入时可以插入对应的方法进行调试,如果仅是输出对象的值,可以是v或者d()方法,这里需要注意,六种调试状态存在等级之分,依顺序排序,高一级的状态会覆盖低级的状态,这个如果对java语言比较熟悉的话,可以类比java语言的exception异常类

Android中的日志工具类是Log(android.util.Log),这个类中提供了如下5个方法打印日志:

  • Log.v():调试颜色为黑色的,任何消息都会输出。用于打印那些最为琐碎的、意义最小的日志信息。对应级别verbose,是Android日志里面级别最低的一种
  • Log.d():输出颜色是蓝色的,仅输出debug调试的意思,但他会输出上层的信息,过滤起来可以通过DDMS的Logcat标签来选择。用于打印一些调试信息,这些信息对你调试程序和分析问题应该是有帮助的。对应级别debug,比verbose高一级
  • Log.i():输出为绿色,一般提示性的消息information,它不会输出Log.v和Log.d的信息,但会显示i、w和e的信息。用于打印一些比较重要的数据,这些数据应该是你非常想看到的、可以帮你分析用户行为数据。对应级别info,比debug高一级
  • Log.w():输出为橙色,可以看作为warning警告,一般需要我们注意优化Android代码,同时选择它后还会输出Log.e的信息。用于打印一些警告信息,提示程序在这个地方可能会有潜在的风险,最好去修复一下这些出现警告的地方。对应级别warn,比info高一级
  • Log.e():输出为红色,可以想到error错误,这里仅显示红色的错误信息,这些错误就需要我们认真的分析,查看栈的信息了。用于打印程序中的错误信息,比如程序进入到了catch语句当中。当有错误信息打印出来的时候,一般都代表你的程序出现严重问题了,必须尽快修复。对应级别error,比warn高一级

0x05 插入Log类的动态调试方法

5.1 直接插入

  • 直接在smali代码插入log类,寻找到需要插入的代码
  • 缺点:
    • 直接插入smali代码的话,简单的逻辑可能对寄存器没有什么严格的要求,如果是复杂的逻辑结构,或者程序本身使用的寄存器比较少,那么可能没法使用该方法

5.1.1 Android Studio创建APK demo

  • Android Studio创建一个简单的项目(Enpty Activity),然后新建一个class,将其命名为MyCrackTest,添加如下代码
package com.example.mytest;

import android.util.Log;

public class MyCrackTest {
    
    public static void Logd(String v0){
    
        Log.d ("hash", "testtest"+v0);
    }
}

PS:要用static进行修饰,之后我们反编译代码后会在onCreate中进行调用,如下smali:

const-string v0, "The Orangey blog welcomes you!!!-test"

invoke-static {
    v0}, LMyCrackTest;->Logd(Ljava/lang/String;)V

smali对应的java代码如下:

MyCrackTest.Logl("The Orangey blog welcomes you!!!-test");
  • Android Studio打包成APK(自动会签名),我们先来运行下没有改动之前的APK,在打开logcat的情况下搜索hash 这个关键字是没有看到的我们想要的信息的,接下来我们会在smali插入代码来达到调用对应输出信息的目的

在这里插入图片描述
在这里插入图片描述

5.1.2 反编译APK

  • 用apktool将APK进行反编译,反编译成功后会得到app-debug 文件
java -jar apktool.jar d -f app-debug.apk -o app-debug

5.1.3 插入smali代码

  • 进入app-debug文件的app-debug\smali\com\example\mytest下,将MyCrackTest.smali放到smali根目录下

在这里插入图片描述
在这里插入图片描述

  • MyCrackTest.smali 在onCreate 里面添加调试的smali代码

在这里插入图片描述

5.1.4 回编译APK并签名

java -jar apktool_2.6.1.jar b app-debug -o app-debug1.apk
java -jar  signapk.jar testkey.x509.pem testkey.pk8 app-debug1.apk app-debug_qm.apk
  • 开启logcat,然后丢到模拟器运行
adb logcat

运行结果如下:

在这里插入图片描述

5.2 增加寄存器法

  • 一般在方法的前面都会声明寄存器的个数。例如,只要增加一个寄存器,即.local 11那么就可以使用v10寄存器来代替上面的v8寄存器
  • 优点:
    • 不需要考虑使用哪个寄存器来做tag,减少了对程序的干扰
  • 缺点:
    • 仍旧对程序产生一定干扰

案例APK下载:https://www.wandoujia.com/apps/280375/history_v100991744

在这里插入图片描述

5.2.1 反编译APK

将下载好的APK重命名为bd.apk

java -jar apktool.jar d -f bd.apk -o bd

在这里插入图片描述

打开AndroidManifest.xml文件,找到App最先启动的Activity

在这里插入图片描述

通过查看AndroidManifest.xml中的Activity的配置信息,可以判定LogoActivity就是最先启动的Activity,也就是后面我们所需要修改的欢迎界面

那么,如何判断是否为最先启动的呢?可以看有没有如下内容:

<intent-filter>
    <action android:name="android.intent.action.MAIN"/>
    <category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
# 决定应用的入口Activity,也就是我们启动应用时首先显示哪一个Activity
android.intent.action.MAIN

# 表示activity应该被列入系统的启动器(launcher)(允许用户启动它)
# Launcher是安卓系统中的桌面启动器,是桌面UI的统称
android.intent.category.LAUNCHER

intent-filter 标签中这两个值:

  • android.intent.action.MAIN
  • android.intent.category.LAUNCHER

intent-filter 标签中(MAIN和LAUNCHER)具体作用如下:

  • MAIN指定了应用入口地址
  • LAUNCHER是应用在手机桌面上的图标
  • 如果只设置MAIN,没有设置LAUNCHER,应用可以被安装到手机,但是在桌面看不到APP的图标,所以就无法启动APP
  • 如果只设置了LAUNCHER,而没有设置MAIN,系统不知道应用从哪个Activity启动,所以也就不会在桌面显示图标
  • 如果给多个Activity设置了MAIN和LAUNCHER,桌面会显示多个APP图标,点击图标会分别进入设置的Activity中

5.2.2 定位LogoActivity的onCreate方法进行代码注入

打开\smali\com\baidu\tieba\LogoActivity.smali文件,定位到onCreate方法

在这里插入图片描述

  • 修改onCreate方法中的本地寄存器的个数

为什么要修改呢?因为smali是基于寄存器的,所有的操作都要经过寄存器,所以在进行smali代码注入时首先要适当修改寄存器的个数。因为构造和显示Toast函数至少需要3个寄存器,所以我们应该先将onCreate方法中的.locals值由7修改为10

android.widget.Toast 是android提供的一个用于快显信息的类

在这里插入图片描述

  • 在onCreate方法中注入Toast代码,需要定位到invoke-super{p0,p1},Lcom/baidu/tbadk/BaseActivity;->onCreate(Landroid/os/Bundle;)V 这条语句后面添加构造和显示Toast的代码
invoke-super {
    p0, p1}, Lcom/baidu/tbadk/BaseActivity;->onCreate(Landroid/os/Bundle;)V
    
const-string v7, "The Orangey blog welcomes you!!!" # 构造toast显示的字符串 
    
const/4 v8, 0x1   # 构造Toast 的显示时间,0表示Toast.LENGTH_SHORT;1表示Toast.LENGTH_LONG
    
invoke-static {
    p0, v7, v8}, Landroid/widget/Toast;->makeText(Landroid/content/Context;Ljava/lang/CharSequence;I)Landroid/widget/Toast; # 调用Toast.makeText方法

move-result-object v9   # 将前面的Toast.makeText方法的返回值保存到v9寄存器中

invoke-virtual {
    v9}, Landroid/widget/Toast;->show()V  # 调用Toast.show方法弹出Toast信息

在这里插入图片描述

5.2.3 回编译APK并签名

apktool b out -o 3.apk

java -jar apktool.jar b bd -o bd_log1.apk

在这里插入图片描述

java -jar .\sign\signapk.jar .\sign\testkey.x509.pem .\sign\testkey.pk8 bd_log1.apk bd_log1_qm.apk

在这里插入图片描述在这里插入图片描述

丢到模拟器安装

在这里插入图片描述在这里插入图片描述

5.3 自定义log类

编写自定义的log类的samli代码,然后只要将其放入smali文件的根目录文件夹下,然后,在smali代码中插入一句调用代码即可(步骤跟上面说的Android Studio 差不多,只是多了个编写log类的APK反编译后把log类的smali代码放到我们需要插入log类代码的APK的smali文件下)

具体内容,请前往之前写的文章有介绍:

[车联网安全自学篇] 二十三. Android安全之静态方式逆向APK应用浅析【手动注入smali+】+【IDA Pro静态分析so文件】+【IDA Pro基础使用讲解】

参考链接

https://blog.csdn.net/pengyan0812/article/details/44568147

http://www.tasfa.cn/index.php/2016/05/18/smali-instrumentation/


我自横刀向天笑,去留肝胆两昆仑


原网站

版权声明
本文为[菠萝_橙留香]所创,转载请带上原文链接,感谢
https://orangey.blog.csdn.net/article/details/126219891