.class public LHello;
.super Ljava/lang/Object;
.source “Hello.java”

static fields

.field private static HELLO_WORLD:Ljava/lang/String;

direct methods

.method static constructor ()V
.registers 1

.prologue
.line 3
const-string v0, “Hello World!”

sput-object v0, LHello;->HELLO_WORLD:Ljava/lang/String;

return-void
.end method

.method public constructor ()V
.registers 1

.prologue
.line 1
invoke-direct {p0}, Ljava/lang/Object;->()V

return-void
.end method

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

.prologue
.line 6
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

sget-object v1, LHello;->HELLO_WORLD:Ljava/lang/String;

invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

.line 7
return-void
.end method

文件头

首先看一下文件头部分:

.class public LHello; // 类名
.super Ljava/lang/Object; // 父类名
.source “Hello.java” // 源文件名称

.class 后面是 访问修饰符和当前类,这里类名用 LHello 表示。那么这个 L 代表什么呢?其实之前的 Class 文件中也出现过这种表示方法,JVM 的字节码指令和 Dalvik 的字节码指令有很多地方都是类似的。Java 中分为基本类型和引用类型,DalviK 对这两种类型分别有不同的描述方法。对于基本类型和 Void 类型,都是用一个大写字母表示。对于引用类型,使用字母 L 加上对象类型的全限定名来表示。具体规则如下表所示:

Java 类型 类型描述符
char C
byte B
short S
int I
long J
float F
double D

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

boolean | Z |
| void | V |
| 对象 | L |
| 数组 | [ |

基本类型的表示很简单,int 用 I 表示即可。对象的表示,如上图中父类 Object 的表示方法 Ljava/lang/Object;,再比如 String 类型,就用 Ljava/lang/String 表示。

对于数组,DalviK 有特殊的表示方法 [ 后面跟上数组元素的类型。int[] 的表示方式就是 [I, String[] 的表示方法是 [Ljava/lang/String;。二维数组用 [[ 表示,[[Ljava/lang/String 就是指 String[][],以此类推。

字段表示

static fields

.field private static HELLO_WORLD:Ljava/lang/String;

smali 中的字段以 .field 开头,并有 # static field(静态字段) 或者 # instance field(实例字段) 的注释。.field 之后分别是 访问修饰符,字段名称,冒号以及字段类型描述符。这句 smali 就声明了一个 String 类型名称为 HELLO_WORLD 的私有静态字段。

方法表示

smali 中的方法以 .method 开头。Hello.smali 中包含了三个方法,clinit , initmain 方法。main 方法是我们自己编写的,而 clinitinit 方法则是 javac 编译时生成的。下面进行逐一分析:

clinit

.method static constructor ()V
.registers 1

.prologue
.line 3
const-string v0, “Hello World!”

sput-object v0, LHello;->HELLO_WORLD:Ljava/lang/String;

return-void
.end method

clinit 方法会进行静态变量的初始化,静态代码块的执行等操作,该方法在类被加载的时候调用。逐行分析该方法的执行逻辑:

  • .registers 1 : 该方法需要使用的寄存器数量。之前已经提到,DalviK VM 是基于寄存器的,字节码可以使用的虚拟寄存器个数可达 65536 个,每个寄存器 32 位,64 位的数据使用相邻两个寄存器表示。最终,所有的虚拟寄存器都会被映射到真实的物理寄存器上。一般情况下,我们使用字母 v 表示局部变量使用的寄存器,使用字母 p 表示参数所使用的寄存器,且局部变量使用的寄存器排列在前,参数使用的寄存器排列在后。这里就表示 clinit 方法仅使用了一个寄存器。

  • .prologue : 表示逻辑代码的开始处

  • .line 3 : 表示 java 源文件中的行数

  • const-string v0, "Hello World!" : 将字符串 Hello World! 的引用移到寄存器 v0 中。

  • sput-object v0, LHello;->HELLO_WORLD:Ljava/lang/String; : 前缀 ssputsget 指令用于静态字段的读写操作。将寄存器 v0 存储的字符串引用赋值给 HELLO_WORLD 字段,结合上一句字节码,这里完成了静态变量 HELLO_WORLD 的赋值工作,也验证了 clinit 方法的确进行了静态变量的初始化。

  • return-void : 表示该方法无返回值

  • .end method : 表示方法执行结束

到这里,clinit 方法就执行结束了。下面分析 init 方法。

init

.method public constructor ()V
.registers 1

.prologue
.line 1
invoke-direct {p0}, Ljava/lang/Object;->()V

return-void
.end method

其余各项与 clinit 方法相同,我们直接看执行的代码逻辑:

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

invoke-direct 用于调用非 static 直接方法(也就是说,本质上不可覆盖的实例方法,即 private 实例方法或构造函数)。显然,这里调用的是默认构造函数。

main

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

.prologue
.line 6
sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;

sget-object v1, LHello;->HELLO_WORLD:Ljava/lang/String;

invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

.line 7
return-void
.end method

最后是 main 方法,从上述 smali 代码我们可以看到 main 方法使用了 3 个寄存器,无返回值(那是肯定的),执行的具体代码是下面三行:

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

sget-object v1, LHello;->HELLO_WORLD:Ljava/lang/String;

invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V

sget 的用法在 clinit 方法中解释过,表示静态字段的读取。第一句代码,获取类 System 的静态字段 out,其类型是 Ljava/io/PrintStream,并将其引用赋给寄存器 v0。第二句代码获取在 clinit 方法中已经初始化的静态字段 HELLO_WORLD,并将其引用赋给寄存器 v1。第三句中使用了 invoke-virtual 指令,invoke-virtual 调用正常的虚方法(该方法不是 private、static 或 final,也不是构造函数),之后通常会跟上 {}{}之中的第一个寄存器通常是指向当前实例对象,如 v0 就是指向 System.out 对象,后面的内容才是该方法真正的参数,如 v1{}, 之后就是要执行的方法的描述,如 Ljava/io/PrintStream;->println(Ljava/lang/String;)V ,指的就是 PrintStream 对象的 println 方法。综上,这三句字节码执行的就是 System.out.println(HELLO_WORLD);

到这里,Hello.smali 文件就解析完了。当然,我们在反编译过程中遇到的任何一个 smali 文件肯定都要比这个复杂的多。Android 官网也对 Dalvik 字节码的指令集进行了归纳,地址是 source.android.google.cn/devices/tec…。在阅读过程中遇到不熟悉的指令,都可以在这个页面进行查找。

最后再介绍一个 javasmali 的快捷方式,在 IDEA 或者 Android Studo 中安装插件 java2smali,在 Build 菜单栏下会出现 Compile to smali选项,可以迅速将 java 代码转化成 smali 代码。在我们学习 smali 的过程中,碰到不确定的内容,可以先写好 java 代码,再转成 smali 代码进行对照学习。

最后贴一个完整的带注释的 Hello.smali 文件:

.class public LHello; // 类名
.super Ljava/lang/Object; // 父类名
.source “Hello.java” // 源文件名称

static fields // 表示静态字段 private static String HELLO_WORLD

.field private static HELLO_WORLD:Ljava/lang/String;

direct methods

.method static constructor ()V // clinit 方法
.registers 1 // 使用一个寄存器 v0

.prologue // 方法开始
.line 3 // 源代码行数
const-string v0, “Hello World!” // 将 "Hello World!"放入寄存器 v0

// 静态字段赋值,将寄存器v0存储的值赋给 HELLO_WORLD

Smali 语法解析——Hello World,android原生开发技术相关推荐

  1. Java开发微信dat文件解析工具,android原生开发笔记应用

    //保存路径(输出) String savePath="C:\Users\Administrator\Desktop"; //开始解析 convert(wxPath, savePa ...

  2. Kotlin 风险高、RxJava 已过时,Android 原生开发现状分析!

    当你好不容易学会了某个框架或者工具,觉得它很好用的时候,它或许就要过时了. 英文:The State of Native Android Development 作者:Vasiliy Zukanov, ...

  3. 敢问路在何方?国外安卓大神对Android原生开发现状剖析

    点击上方"终端研发部",选择"星标" 回复"资源",领取全网最火的Java核心知识总结~ 前言 原文地址:The State of Nati ...

  4. AndroidStudio_安卓原生开发_蓝牙扫描设备_另一种方法---Android原生开发工作笔记145

    下面的一个方法是之前写的,但是那种方法有时候会有扫描不到的情况,现在再写一种,这种方法,更简单有效一些. AndroidStudio安卓原生开发_Android扫描附近指定的蓝牙设备_通过设备名称过滤 ...

  5. AndroidStudio_下载和安装---Android原生开发工作笔记67

    以前写的那个教程到66,是用eclipse,安装插件来开发Android原生程序的,那个已经是,8年前才那么做的, 现在公司让做Android原生开发...再来学一学,这个新的AndroidStudi ...

  6. Android原生开发--模拟器检测工具包

    Android原生开发–模拟器检测工具包 模拟器检测工具包使用例子 //使用方法 Context context=getBaseContext(); boolean isEmn= EasyProtec ...

  7. Android开发工具链ppt,介绍Android原生开发工具包r14

    原标题:介绍Android原生开发工具包r14 现在,可以下载最新版本 Android 原生开发工具包 (NDK)--Android NDK r14.也可以通过 Android Studio 在 SD ...

  8. 介绍Android原生开发工具包r14

    现在,可以下载最新版本 Android 原生开发工具包 (NDK)--Android NDK r14.也可以通过 Android Studio 在 SDK 管理器中下载此版本: https://dev ...

  9. 【Android原生开发】个人小助手

    一. 项目开发的背景 用于安卓手机的个人日常管理系统的设计与开发,实现个人收支管理.日程管理.闹钟提醒.日程数据库的添删改查等功能,系统使用SQLite数据库实现了日程记录数据的管理,挥SQLite占 ...

最新文章

  1. java runnable 启动_Java开发笔记(九十七)利用Runnable启动线程
  2. 数据结构,堆和栈和队列的概念
  3. LeetCode 55跳跃游戏56合并区间57插入区间
  4. WindowsServer2008防火墙配置命令
  5. 美国第一大移动运营商的5G战略:已进入预商用测试
  6. 洛谷 P2695 骑士的工作
  7. 剑指Offer - 面试题64. 求1+2+…+n(递归)
  8. Python使用matplotlib.pyplot绘图时设置坐标轴刻度
  9. 如何打开电脑上的安全策略
  10. mapreduce程序调用各个类的功能
  11. 试分析家用变频空调的计算机控制原理,习 题 五
  12. 计算机网络技术 网络参数配置与常用网络命令使用和网络仿真工具的使用
  13. OpenCV/Python/dlib眨眼检测
  14. 注册CSDN七年才发布第一篇博文是什么感觉
  15. 完美解决:“已损坏,无法打开。 您应该将它移到废纸篓。”
  16. 【vue-清除默认样式-完结】
  17. YouTube引流技巧教程!
  18. installshield打包mysql_实现Installshield对Php+mysql+apache做的程序在WINDOWS下打包
  19. 骗了马云 10 亿被骂 4 年后,院士王坚留下 4 条人生启示
  20. 基于opencv的模板匹配详解

热门文章

  1. 3D建模在线展示/三维模型可视化线上VR智慧城市
  2. #1531:德国心脏病
  3. java刷新透视表数据源,Excel数据透视表过滤源更新时更改的值
  4. 【】每日360题,2019.11.05日21点财会类考试习题答案
  5. 嵌入式linux收银机重装系统,收银机重装系统。没有光驱,用U盘怎么装?详细点的。谢了!...
  6. 低频数字式相位测试仪的原理与使用
  7. STM32+W5500以太网模块
  8. 条码打印软件如何连接SQL Server数据库制作条形码
  9. Nwafu-OJ-1428 Problem Y C语言实习题五——3.数据倒置
  10. pgsql按varchar字段排序