新手上路
- 资源币
- 19
- 积分
- 16
- 贡献
- 0
- 在线时间
- 2 小时
- 注册时间
- 2020-2-20
- 最后登录
- 2020-5-2
|
零基础安卓逆向学习之旅(六-下)
解释Dalvik字节码
下载,配置smali反编译器
1.下载工具baksmali的相关文件
访问以下链接:
https://bitbucket.org/JesusFreke/smali/downloads
下载baksmali[version].jar文件的最新版本及baksmali脚本,并保存在同一目录下。
2.配置baksmali工具
将下载下来的baksmali的jar文件改名为baksmali.jar
mv baksmali-[version-number].jar baksmali.jar
确认baksmali脚本拥有可执行权限
chmod +x 700 baksmali
运用baksmali反编译.dex文件
baksmali[Dex filename].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;->outjava/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;
->outjava/io/PrintStream;
官网描述erform 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文件
[path-of-Dex2Jar]/dex2jar.shExample.dex //生成Example_dex2jar.jar文件
3.在JD-GUI上打开上边生成的.jar文件,查看java源代码
java-jar jd-gui-[version] //打开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中路径
[android-ndk-path]/toolchains/arm-linux-androideabi-[version]/prebuilt/[arch]/bin/在该目录下找到objdump工具:./arm-linux-androideabi-objdump。
3.反编译原生库
arm-linux-androideabi-objdump–D [native library].so
使用GDB server调试Android进程
1.将Android NDK下的gdbserver文件拷贝到设备上
这需要一部已经root的Android设备或一台Android模拟器。
emulator-avd [avd name] //启动AVD
adbshell //进入设备shell
重新挂载系统目录,以读-写权限mount系统目录/system
mount //查看当前各个块设备的挂载信息,确定/system的挂载信息(mount | grep system)
mount -o rw,remount [device] /system //重新挂载/system,改为读-写
gdbserver位于[NDK-path]/prebuit/android-arm64/gdbserver/gdbserver
pushgdbserver /system/bin/. //将gdbserver拷贝到设备上
2.运用gdbserver抓取进程
ps //查看设备正在运行的进程信息
选取com.android.email进程作为实验进程,其PID为1009
gdbserver:[tcp-port number] –attach [PID] //抓取进程,连接TCP端口
gdbserver:31337 –attach 1009
3.在本地主机上运行gdb进行调试
adb forward tcp:[device port-number] tcp:[local port-number] //端口交互
gdb位于[NDK-path]/prebuilt/[system]/bin/gdb
运行gdb
进入gdb后,运行下边命令,调试目标进程
target remote :[PID] //[PID]为本机TCP交互端口
现在便可与运行在Android设备上的进程中的内存段和寄存器进行交互了。。。。。。
|
|