安卓逆向培训 发表于 2020-2-24 00:11:11

零基础安卓逆向学习之旅(六-下)

零基础安卓逆向学习之旅(六-下)

解释Dalvik字节码

下载,配置smali反编译器

1.下载工具baksmali的相关文件

访问以下链接:

https://bitbucket.org/JesusFreke/smali/downloads





下载baksmali.jar文件的最新版本及baksmali脚本,并保存在同一目录下。

2.配置baksmali工具

    将下载下来的baksmali的jar文件改名为baksmali.jar

    mv baksmali-.jar baksmali.jar

    确认baksmali脚本拥有可执行权限

    chmod +x 700 baksmali

    运用baksmali反编译.dex文件

    baksmali.dex

    并在生成的out目录下查看反编译后的.smali文件




解释字节码

首先是开头几行代码

.class public LExample;

.super Ljava/lang/Object;

.source "Example.java"

这是被编译后类的元数据,给出了类名,父类以及源文件,由于所有的java类都是继承自java.lang.Object的,所以即使在Example.java的代码中,我们没有显式声明继承这个类,编译后也会显示出来。

接着是Example.java的构造函数的smali代码

#direct methods

.method public constructor <init>()V

    .registers 1

    .prologue

    .line 1

    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    return-void

.end method

.method public constructor <init>()V//这个方法的声明

即这个方法名为init,返回一个void类型,访问标志位是public。

.registers1

这个方法只使用一个寄存器,一个方法在运行前都需要确定所需寄存器的数量。

.prologue

声明接下来是一个prologue方法,这是每个java方法都有的,它保证这个方法是以继承形式被调用的,这就是在下边一行调用了另一个名为init的方法,这是java.lang.Object的init方法。

invoke-direct{p0}, Ljava/lang/Object;-><init>()V

invoke-direct指令需要两个参数:p0寄存器和被调用方法的指针。

invoke-direct是用来调用一个非静态直接方法(non-static direct method),一个不可重写的,private实例方法或构造方法。

上边代码即是调用java.lang.Object类的构造方法这个非静态直接方法。

return-void   返回一个void类型,并退出当前函数。

.endmethod标志着这个方法的结束。

    接下来则是main方法的smali代码:

.method public static main([Ljava/lang/String;)V

    .registers 4

    .prologue

    .line 3

    sget-object v0,Ljava/lang/System;->out:Ljava/io/PrintStream;

    const-string v1, "Hello World!\n"

    const/4 v2, 0x0

    new-array v2, v2, [Ljava/lang/Object;

    invoke-virtual {v0, v1, v2},Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;

    .line 4

    return-void

.end method

第一行:.method public static main([Ljava/lang/String;)V

这个方法接收一个java.lang.String类型的数组,返回void类型,且这方法是静态方法,访问属性为public。

sget-object操作:sget-object v0, Ljava/lang/System;

->out:Ljava/io/PrintStream;

官网描述:Perform the identified object static field operation withthe identified static field, loading or storing into the value register.

执行获取对象静态成员的操作,找静态成员,将其加载/存储到存放值的寄存器中

sget-object接收两个参数:

一个寄存器,用于存储操作的结果

一个是将被存储到上边的寄存器的对象的引用

即这行代码是获取一个对象实例,并将其存放到寄存器中,v0是这方法的栈帧里的第一个寄存器。

const-string指令:const-string v1, "Hello World!\n"

获取一个字符串并将其存储在第一参数指定的寄存器中。

const/4v2, 0x0

将常数0放入第三个寄存器v2中。

new-arrayv2, v2, [Ljava/lang/Object;

new-array指令是构造一个指定类型和元素个数的数组,并将它存放在左起第一个寄存器中,这指令执行后,v2寄存器中存放的应是一个元素个数为0,java.lang.Object类型的数组.上一行const/4v2, 0x0先将0放入v2的原因。

最后是非常常见的指令 :

invoke-virtual{v0, v1, v2},Ljava/io/PrintStream;->printf(Ljava/lang/String;[Ljava/lang/Object;)Ljava/io/PrintStream;

invoke-virtual指令的官网定义"invoke-virtual is used to invoke a normal virtualmethod (a method that is not private, static, or final, and is also not a constructor)."

invoke-virtual用来调用一个普通的虚方法(方法属性不为private,static或final,且不是构造方法)

其参数格式:

invoke-kind{vC, vD, vE, vF, vG}, meth@BBBB

其中,vC,vD,vE,vF,vG是用来向被调用的方法传递参数的寄存器,方法的具体代码由参数meth@BBBB指定,每个B表示4bit,即这个指令接收一个16位的方法引用

所以实例中的代码,是调用一个名为java.io.PrintStream.printf方法,接收参数:java.lang.String对象和java.lang.Object类型的数组,最后返回java.io.PrintStream类型的对象 。

将DEX反编译为java

1.下载相关工具

Dex2Jarhttps://sourceforge.net/projects/dex2jar/?source=typ_redirect下载最新.zip包

JD-GUI:访问http://jd.benow.ca/下载最新.jar。

2.运用Dex2Jar将.dex文件转成.jar文件

    /dex2jar.shExample.dex   //生成Example_dex2jar.jar文件





3.在JD-GUI上打开上边生成的.jar文件,查看java源代码

java-jar jd-gui-//打开JD-GUI    点击File->Open。



选择上边的.jar文件打开,查看java源代码。


反编译app的原生库

1.获取Android原生库文件(.so)。

Sieve – A password manager app, showcasing some common Android vulnerabilities at

https://www.mwrinfosecurity.com/system/assets/380/original/sieve.apk

下载实例apk文件,将文件改名为sieve.zip,并解压为sieve,在sieve/lib/armeabi/*.so路径下便可找到Android原生库文件。

2.确认工具objdump在AndroidNDK中路径

/toolchains/arm-linux-androideabi-/prebuilt//bin/在该目录下找到objdump工具:./arm-linux-androideabi-objdump。






3.反编译原生库

arm-linux-androideabi-objdump–D .so




使用GDB server调试Android进程

1.将Android NDK下的gdbserver文件拷贝到设备上

这需要一部已经root的Android设备或一台Android模拟器。

emulator-avd //启动AVD

adbshell//进入设备shell

重新挂载系统目录,以读-写权限mount系统目录/system

mount//查看当前各个块设备的挂载信息,确定/system的挂载信息(mount | grep system)



mount -o rw,remount /system//重新挂载/system,改为读-写




gdbserver位于/prebuit/android-arm64/gdbserver/gdbserver

pushgdbserver /system/bin/.   //将gdbserver拷贝到设备上



2.运用gdbserver抓取进程

ps//查看设备正在运行的进程信息




选取com.android.email进程作为实验进程,其PID为1009


gdbserver: –attach //抓取进程,连接TCP端口

gdbserver:31337 –attach 1009

3.在本地主机上运行gdb进行调试

adb forward tcp: tcp://端口交互

gdb位于/prebuilt//bin/gdb

运行gdb



进入gdb后,运行下边命令,调试目标进程

target remote ://为本机TCP交互端口




现在便可与运行在Android设备上的进程中的内存段和寄存器进行交互了。。。。。。





a114543 发表于 2020-9-19 08:38:15

祝资源共享吧越来越火!
页: [1]
查看完整版本: 零基础安卓逆向学习之旅(六-下)