📊
CTF-All-In-One
  • 简介
  • 前言
  • 一、基础知识篇
    • 1.1 CTF 简介
    • 1.2 学习方法
    • 1.3 Linux 基础
    • 1.4 Web 安全基础
      • 1.4.1 HTML 基础
      • 1.4.2 HTTP 协议基础
      • 1.4.3 JavaScript 基础
      • 1.4.4 常见 Web 服务器基础
      • 1.4.5 OWASP Top Ten Project 漏洞基础
      • 1.4.6 PHP 源码审计基础
    • 1.5 逆向工程基础
      • 1.5.1 C/C++ 语言基础
      • 1.5.2 汇编基础
      • 1.5.3 Linux ELF
      • 1.5.4 Windows PE
      • 1.5.5 静态链接
      • 1.5.6 动态链接
      • 1.5.7 内存管理
      • 1.5.8 glibc malloc
      • 1.5.9 Linux 内核
      • 1.5.10 Windows 内核
      • 1.5.11 jemalloc
    • 1.6 密码学基础
      • 1.6.1 密码学导论
      • 1.6.2 流密码
      • 1.6.3 分组密码
      • 1.6.4 公钥密码
      • 1.6.5 消息认证和哈希函数
      • 1.6.6 数字签名
      • 1.6.7 密码协议
      • 1.6.8 密钥分配与管理
      • 1.6.9 数字货币
    • 1.7 Android 安全基础
      • 1.7.1 Android 环境搭建
      • 1.7.2 Dalvik 指令集
      • 1.7.3 ARM 汇编基础
      • 1.7.4 Android 常用工具
  • 二、工具篇
    • 虚拟化分析环境
      • 2.1.1 VirtualBox
      • 2.1.2 QEMU
      • 2.1.3 Docker
      • 2.1.4 Unicorn
    • 静态分析工具
      • 2.2.1 radare2
      • 2.2.2 IDA Pro
      • 2.2.3 JEB
      • 2.2.4 Capstone
      • 2.2.5 Keystone
      • 2.2.6 Ghidra
    • 动态分析工具
      • 2.3.1 GDB
      • 2.3.2 OllyDbg
      • 2.3.3 x64dbg
      • 2.3.4 WinDbg
      • 2.3.5 LLDB
    • 其他工具
      • 2.4.1 pwntools
      • 2.4.2 zio
      • 2.4.3 metasploit
      • 2.4.4 binwalk
      • 2.4.5 Burp Suite
      • 2.4.6 Wireshark
      • 2.4.7 Cuckoo Sandbox
  • 三、分类专题篇
    • Pwn
      • 3.1.1 格式化字符串漏洞
      • 3.1.2 整数溢出
      • 3.1.3 栈溢出
      • 3.1.4 返回导向编程(ROP)(x86)
      • 3.1.5 返回导向编程(ROP)(ARM)
      • 3.1.6 Linux 堆利用(一)
      • 3.1.7 Linux 堆利用(二)
      • 3.1.8 Linux 堆利用(三)
      • 3.1.9 Linux 堆利用(四)
      • 3.1.10 内核 ROP
      • 3.1.11 Linux 内核漏洞利用
      • 3.1.12 Windows 内核漏洞利用
      • 3.1.13 竞争条件
      • 3.1.14 虚拟机逃逸
    • Reverse
      • 3.2.1 patch 二进制文件
      • 3.2.2 脱壳技术(PE)
      • 3.2.3 脱壳技术(ELF)
      • 3.2.4 反调试技术(PE)
      • 3.2.5 反调试技术(ELF)
      • 3.2.6 指令混淆
    • Web
      • 3.3.1 SQL 注入利用
      • 3.3.2 XSS 漏洞利用
    • Crypto
    • Misc
      • 3.5.1 Lsb
    • Mobile
  • 四、技巧篇
    • 4.1 Linux 内核调试
    • 4.2 Linux 命令行技巧
    • 4.3 GCC 编译参数解析
    • 4.4 GCC 堆栈保护技术
    • 4.5 ROP 防御技术
    • 4.6 one-gadget RCE
    • 4.7 通用 gadget
    • 4.8 使用 DynELF 泄露函数地址
    • 4.9 shellcode 开发
    • 4.10 跳转导向编程(JOP)
    • 4.11 利用 mprotect 修改栈权限
    • 4.12 利用 __stack_chk_fail
    • 4.13 利用 _IO_FILE 结构
    • 4.14 glibc tcache 机制
    • 4.15 利用 vsyscall 和 vDSO
  • 五、高级篇
    • 5.0 软件漏洞分析
    • 5.1 模糊测试
      • 5.1.1 AFL fuzzer
      • 5.1.2 libFuzzer
    • 5.2 动态二进制插桩
      • 5.2.1 Pin
      • 5.2.2 DynamoRio
      • 5.2.3 Valgrind
    • 5.3 符号执行
      • 5.3.1 angr
      • 5.3.2 Triton
      • 5.3.3 KLEE
      • 5.3.4 S²E
    • 5.4 数据流分析
      • 5.4.1 Soot
    • 5.5 污点分析
      • 5.5.1 TaintCheck
    • 5.6 LLVM
      • 5.6.1 Clang
    • 5.7 程序切片
    • 5.8 SAT/SMT
      • 5.8.1 Z3
    • 5.9 基于模式的漏洞分析
    • 5.10 基于二进制比对的漏洞分析
    • 5.11 反编译技术
      • 5.11.1 RetDec
  • 六、题解篇
    • Pwn
      • 6.1.1 pwn HCTF2016 brop
      • 6.1.2 pwn NJCTF2017 pingme
      • 6.1.3 pwn XDCTF2015 pwn200
      • 6.1.4 pwn BackdoorCTF2017 Fun-Signals
      • 6.1.5 pwn GreHackCTF2017 beerfighter
      • 6.1.6 pwn DefconCTF2015 fuckup
      • 6.1.7 pwn 0CTF2015 freenote
      • 6.1.8 pwn DCTF2017 Flex
      • 6.1.9 pwn RHme3 Exploitation
      • 6.1.10 pwn 0CTF2017 BabyHeap2017
      • 6.1.11 pwn 9447CTF2015 Search-Engine
      • 6.1.12 pwn N1CTF2018 vote
      • 6.1.13 pwn 34C3CTF2017 readme_revenge
      • 6.1.14 pwn 32C3CTF2015 readme
      • 6.1.15 pwn 34C3CTF2017 SimpleGC
      • 6.1.16 pwn HITBCTF2017 1000levels
      • 6.1.17 pwn SECCONCTF2016 jmper
      • 6.1.18 pwn HITBCTF2017 Sentosa
      • 6.1.19 pwn HITBCTF2018 gundam
      • 6.1.20 pwn 33C3CTF2016 babyfengshui
      • 6.1.21 pwn HITCONCTF2016 Secret_Holder
      • 6.1.22 pwn HITCONCTF2016 Sleepy_Holder
      • 6.1.23 pwn BCTF2016 bcloud
      • 6.1.24 pwn HITCONCTF2016 House_of_Orange
      • 6.1.25 pwn HCTF2017 babyprintf
      • 6.1.26 pwn 34C3CTF2017 300
      • 6.1.27 pwn SECCONCTF2016 tinypad
      • 6.1.28 pwn ASISCTF2016 b00ks
      • 6.1.29 pwn Insomni'hack_teaserCTF2017 The_Great_Escape_part-3
      • 6.1.30 pwn HITCONCTF2017 Ghost_in_the_heap
      • 6.1.31 pwn HITBCTF2018 mutepig
      • 6.1.32 pwn SECCONCTF2017 vm_no_fun
      • 6.1.33 pwn 34C3CTF2017 LFA
      • 6.1.34 pwn N1CTF2018 memsafety
      • 6.1.35 pwn 0CTF2018 heapstorm2
      • 6.1.36 pwn NJCTF2017 messager
      • 6.1.37 pwn sixstarctf2018 babystack
      • 6.1.38 pwn HITCONCMT2017 pwn200
      • 6.1.39 pwn BCTF2018 house_of_Atum
      • 6.1.40 pwn LCTF2016 pwn200
      • 6.1.41 pwn PlaidCTF2015 PlaidDB
      • 6.1.42 pwn hacklu2015 bookstore
      • 6.1.43 pwn 0CTF2018 babyheap
      • 6.1.44 pwn ASIS2017 start_hard
      • 6.1.45 pwn LCTF2016 pwn100
    • Reverse
      • 6.2.1 re XHPCTF2017 dont_panic
      • 6.2.2 re ECTF2016 tayy
      • 6.2.3 re CodegateCTF2017 angrybird
      • 6.2.4 re CSAWCTF2015 wyvern
      • 6.2.5 re PicoCTF2014 Baleful
      • 6.2.6 re SECCONCTF2017 printf_machine
      • 6.2.7 re CodegateCTF2018 RedVelvet
      • 6.2.8 re DefcampCTF2015 entry_language
    • Web
      • 6.3.1 web HCTF2017 babycrack
    • Crypto
    • Misc
    • Mobile
  • 七、实战篇
    • CVE
      • 7.1.1 CVE-2017-11543 tcpdump sliplink_print 栈溢出漏洞
      • 7.1.2 CVE-2015-0235 glibc __nss_hostname_digits_dots 堆溢出漏洞
      • 7.1.3 CVE-2016-4971 wget 任意文件上传漏洞
      • 7.1.4 CVE-2017-13089 wget skip_short_body 栈溢出漏洞
      • 7.1.5 CVE–2018-1000001 glibc realpath 缓冲区下溢漏洞
      • 7.1.6 CVE-2017-9430 DNSTracer 栈溢出漏洞
      • 7.1.7 CVE-2018-6323 GNU binutils elf_object_p 整型溢出漏洞
      • 7.1.8 CVE-2010-2883 Adobe CoolType SING 表栈溢出漏洞
      • 7.1.9 CVE-2010-3333 Microsoft Word RTF pFragments 栈溢出漏洞
    • Malware
  • 八、学术篇
    • 8.1 The Geometry of Innocent Flesh on the Bone: Return-into-libc without Function Calls (on the x86)
    • 8.2 Return-Oriented Programming without Returns
    • 8.3 Return-Oriented Rootkits: Bypassing Kernel Code Integrity Protection Mechanisms
    • 8.4 ROPdefender: A Detection Tool to Defend Against Return-Oriented Programming Attacks
    • 8.5 Data-Oriented Programming: On the Expressiveness of Non-Control Data Attacks
    • 8.7 What Cannot Be Read, Cannot Be Leveraged? Revisiting Assumptions of JIT-ROP Defenses
    • 8.9 Symbolic Execution for Software Testing: Three Decades Later
    • 8.10 AEG: Automatic Exploit Generation
    • 8.11 Address Space Layout Permutation (ASLP): Towards Fine-Grained Randomization of Commodity Softwa
    • 8.13 New Frontiers of Reverse Engineering
    • 8.14 Who Allocated My Memory? Detecting Custom Memory Allocators in C Binaries
    • 8.21 Micro-Virtualization Memory Tracing to Detect and Prevent Spraying Attacks
    • 8.22 Practical Memory Checking With Dr. Memory
    • 8.23 Evaluating the Effectiveness of Current Anti-ROP Defenses
    • 8.24 How to Make ASLR Win the Clone Wars: Runtime Re-Randomization
    • 8.25 (State of) The Art of War: Offensive Techniques in Binary Analysis
    • 8.26 Driller: Augmenting Fuzzing Through Selective Symbolic Execution
    • 8.27 Firmalice - Automatic Detection of Authentication Bypass Vulnerabilities in Binary Firmware
    • 8.28 Cross-Architecture Bug Search in Binary Executables
    • 8.29 Dynamic Hooks: Hiding Control Flow Changes within Non-Control Data
    • 8.30 Preventing brute force attacks against stack canary protection on networking servers
    • 8.33 Under-Constrained Symbolic Execution: Correctness Checking for Real Code
    • 8.34 Enhancing Symbolic Execution with Veritesting
    • 8.38 TaintEraser: Protecting Sensitive Data Leaks Using Application-Level Taint Tracking
    • 8.39 DART: Directed Automated Random Testing
    • 8.40 EXE: Automatically Generating Inputs of Death
    • 8.41 IntPatch: Automatically Fix Integer-Overflow-to-Buffer-Overflow Vulnerability at Compile-Time
    • 8.42 Dynamic Taint Analysis for Automatic Detection, Analysis, and Signature Generation of Exploits
    • 8.43 DTA++: Dynamic Taint Analysis with Targeted Control-Flow Propagation
    • 8.44 Superset Disassembly: Statically Rewriting x86 Binaries Without Heuristics
    • 8.45 Ramblr: Making Reassembly Great Again
    • 8.46 FreeGuard: A Faster Secure Heap Allocator
    • 8.48 Reassembleable Disassembling
  • 九、附录
    • 9.1 更多 Linux 工具
    • 9.2 更多 Windows 工具
    • 9.3 更多资源
    • 9.4 Linux 系统调用表
    • 9.5 python2到3字符串转换
    • 9.6 幻灯片
Powered by GitBook
On this page
  • Dalvik 虚拟机
  • Dalvik 指令集
  • 指令格式
  • 寄存器
  • 类型、方法和字段
  • 空操作指令
  • 数据操作指令
  • 返回指令
  • 数据定义指令
  • 锁指令
  • 实例操作指令
  • 数组操作指令
  • 异常指令
  • 跳转指令
  • 比较指令
  • 字段操作指令
  • 方法调用指令
  • 数据转换指令
  • 数据运算指令
  • smali 语法
  • 循环语句
  • switch 语句
  • try-catch 语句
  • 更多资料

Was this helpful?

  1. 一、基础知识篇
  2. 1.7 Android 安全基础

1.7.2 Dalvik 指令集

Previous1.7.1 Android 环境搭建Next1.7.3 ARM 汇编基础

Last updated 3 years ago

Was this helpful?

Dalvik 虚拟机

Android 程序运行在 Dalvik 虚拟机中,它与传统的 Java 虚拟机不同,完全基于寄存器架构,数据通过直接通过寄存器传递,大大提高了效率。Dalvik 虚拟机属于 Android 运行时环境,它与一些核心库共同承担 Android 应用程序的运行工作。Dalvik 虚拟机有自己的指令集,即 smali 代码,下面会详细介绍它们。

Dalvik 指令集

指令格式

Dalvik 指令语法由指令的位描述与指令格式标识来决定。

位描述约定如下:

  • 每 16 位使用空格分隔。

  • 每个字母占 4 位,按照顺序从高字节到低字节排列。

  • 顺序采用 A~Z 的单个大写字母作为一个 4 位的操作码,op 表示一个 8 位的操作码。

  • ”∅“来表示这字段所有位为0值。

指令格式约定如下:

  • 指令格式标识大多由三个字符组成,前两个是数字,最后一个是字母。

  • 第一个数字表示指令有多少个 16 位的字组成。

  • 第二个数字表示指令最多使用寄存器的个数。

  • 第三个字母为类型码,表示指令用到的额外数据的类型。

寄存器

Dalvik 寄存器都是 32 位的,如果是 64 位的数据,则使用相邻的两个寄存器来表示。

寄存器有两种命名法:v 命名法和 p 命名法。如果一个函数使用到 M 个寄存器,其中有 N 个参数,那么参数会使用最后的 N 个寄存器,而局部变量使用从 v0 开始的前 M-N 个寄存器。在 v 命名法中,不管寄存器中是参数还是局部变量,都以 v 开头。而 p 命名法中,参数命名从 p0 开始,依次递增,在代码比较复杂的时候,使用 p 命名法可以清楚地区分开参数和局部变量,大多数工具使用的也是 p 命名法。

类型、方法和字段

Dalvik 字节码只有基本类型和引用类型两种。除了对象类型和数组类型是引用类型外,其余的都是基本类型:

语法
含义

V

void

Z

boolean

B

byte

S

short

C

char

I

int

J

long

F

float

D

double

L

对象类型

[

数组类型

  • 对象类型格式是 L<包名>/<类名>;,如 String 表示为 Ljava/lang/String;。

  • 数组类型格式是 [ 加上类型,如 int[] 表示为 [I,int[][] 表示为 [[I。

Dalvik 使用方法名、类型参数和返回值来描述一个方法。方法格式如下:

Lpackage/name/ObjectName;->MethodName(III)Z

例如把下面的 Java 代码转换成 smali:

# Java
String method(int, int [][], int, String, Object[])

# smali
.method method(I[[IILjava/lang/String;[Ljava/lang/Object;)Ljava/lang/String;
.end method

字段格式如下:

Lpackage/name/ObjectName;->FieldName:Ljava/lang/String;

空操作指令

空操作指令的助记符为 nop,值为 00,通常用于对齐代码。

数据操作指令

数据操作指令为 move,原型为 move destination, source。

  • move vA, vB:vB -> vA,都是 4 位

  • move/from16 vAA, vBBBB:vBBBB -> vAA,源寄存器 16 位,目的寄存器 8 位

  • move/16 vAAAA, vBBBB:vBBBB -> vAAAA,都是 16 位

  • move-wide vA, vB:4 位的寄存器对赋值,都是 4 位

  • move-wide/from16vAA, vBBBB、move-wide/16 vAAAA, vBBBB:与 move-wide 相同

  • move-object vA, vB:对象赋值,都是 4 位

  • move-object/from16 vAA, vBBBB:对象赋值,源寄存器 16 位,目的寄存器 8 位

  • move-object/16 vAAAA, vBBBB:对象赋值,都是 16 位

  • move-result vAA:将上一个 invoke 类型指令操作的单字非对象结果赋值给 vAA 寄存器

  • move-result-wide vAA:将上一个 invoke 类型指令操作的双字非对象结果赋值给 vAA 寄存器

  • move-result-object vAA:将上一个 invoke 类型指令操作的对象结果赋值给 vAA 寄存器

  • move-exception vAA:保存一个运行时发生的异常到 vAA 寄存器

返回指令

基础字节码为 return。

  • return-void:从一个 void 方法返回

  • return vAA:返回一个 32 位非对象类型的值,返回值寄存器位 8 位的寄存器 vAA

  • return-wide vAA:返回一个 64 位非对象类型的值,返回值寄存器为 8 位的 vAA

  • return-object vAA:返回一个对象类型的值,返回值寄存器为 8 位的 vAA

数据定义指令

基础字节码为 const。

  • const/4 vA, #+B:将数值符号扩展为 32 位后赋值给寄存器 vA

  • const/16 vAA, #+BBBB:将数值符号扩展为 32 位后赋值给寄存器 vAA

  • const vAA, #+BBBBBBBB:将数值赋值给寄存器 vAA

  • const/high16 vAA, #+BBBB0000:将数值右边零扩展为 32 位后赋值给寄存器 vAA

  • const-wide/16 vAA, #+BBBB:将数值符号扩展为 64 位后赋值给寄存器 vAA

  • const-wide/32 vAA, #+BBBBBBBB:将数值符号扩展为 64 位后赋值给寄存器 vAA

  • const-wide vAA, #+BBBBBBBBBBBBBBBB:将数值赋给寄存器对 vAA

  • const-wide/high16 vAA, #+BBBB000000000000:将数值右边零扩展为 64 位后赋值给寄存器对 vAA

  • const-string vAA, string@BBBB:通过字符串索引构造一个字符串并赋值给寄存器 vAA

  • const-string/jumbo vAA, string@BBBBBBBB:通过字符串索(较大)引构造一个字符串并赋值给寄存器 vAA

  • const-class vAA, type@BBBB:通过类型索引获取一个类型引用并赋值给寄存器 vAA

  • const-class/jumbo vAAAA, type@BBBBBBBB:通过给定的类型索引获取一个类引用并赋值给寄存器 vAAAA。这条指令占用两个字节,值为 0x00ff

锁指令

用在多线程程序中对同一对象操作。

  • monitor-enter vAA:为指定的对象获取锁

  • monitor-exit vAA:释放指定的对象的锁

实例操作指令

  • check-cast vAA, type@BBBB

  • check-cast/jumbo vAAAA, type@BBBBBBBB:将 vAA 寄存器中的对象引用转换成指定的类型,如果失败会抛出 ClassCastException 异常。如果类型 B 指定的是基本类型,对于非基本类型的 A 来说,运行始终会失败

  • instance-of vA, vB, type@CCCC

  • instance-of vAAAA, vBBBB, type@CCCCCCCC:判断 vB 寄存器中的对象引用是否可以转换成指定的类型,如果可以 vA 寄存器赋值为 1,否则 vA 寄存器赋值为 0

  • new-instance vAA, type@BBBB

  • new-instance vAAAA, type@BBBBBBBB:构造一个指定类型对象的新实例,并将对象引用赋值给 vAA 寄存器,类型符 type 指定的类型不能是数组类

数组操作指令

  • array-length vA, vB:获取vB寄存器中数组的长度并将值赋给vA寄存器。

  • new-array vA, vB, type@CCCC

  • new-array/jumbo vAAAA, vBBBB, type@CCCCCCCC:构造指定类型(type@CCCCCCCC)与大小(vBBBB)的数组,并将值赋给 vAAAA 寄存器

  • filled-new-array {vC, vD, vE, vF, vG}, type@BBBB:构造指定类型(type@BBBB)和大小(vA)的数组并填充数组内容。vA 寄存器是隐含使用的,处理指定数组的大小外还指定了参数的个数,vC~vG 是使用的参数寄存器列表。

  • filled-new-array/range {vCCCC .. vNNNN}, type@BBBB:同上,只是参数寄存器使用 range 字节码后缀指定了取值范围,vC 是第一个参数寄存器,N=A+C-1。

  • fill-array-data vAA, +BBBBBBBB:用指定的数据来填充数组,vAA 寄存器为数组引用,引用必须为基础类型的数组,在指令后面紧跟一个数据表。

  • arrayop vAA, vBB, vCC:对 vBB 寄存器指定的数组元素进行取值和赋值。vCC 寄存器指定数组元素索引,vAA 寄存器用来存放读取的或需要设置的数组元素的值。读取元素使用 aget 类指令,元素赋值使用 aput 类指令。

异常指令

  • throw vAA:抛出 vAA 寄存器中指定类型的异常

跳转指令

有三种跳转指令:无条件跳转(goto)、分支跳转(switch)和条件跳转(if)。

  • goto +AA

  • goto/16 +AAAA

  • goto/32 +AAAAAAAA:无条件跳转到指定偏移处,不能为 0

  • packed-switch vAA, +BBBBBBBB:分支跳转指令。vAA 寄存器为 switch 分支中需要判断的值,BBBBBBBB 指向一个 packed-switch-payload 格式的偏移表,表中的值是有规律递增的

  • sparse-switch vAA, +BBBBBBBB:分支跳转指令。vAA 寄存器为 switch 分支中需要判断的值,BBBBBBBB 指向一个 sparse-switch-payload 格式的偏移表,表中的值是无规律的偏移量

  • if-test vA, vB, +CCCC:条件跳转指令。比较 vA 寄存器与 vB 寄存器的值,如果比较结果满足就跳转到 CCCC 指定的偏移处,CCCC 不能为 0。if-test 类型的指令有:

    • if-eq:if(vA==vB)

    • if-ne:if(vA!=vB)

    • if-lt:if(vA<vB)

    • if-ge:if(vA>=vB)

    • if-gt:if(vA>vB)

    • if-le:if(vA<=vB)

  • if-testz vAA, +BBBB:条件跳转指令。拿 vAA 寄存器与 0 比较,如果比较结果满足或值为 0 就跳转到 BBBB 指定的偏移处,BBBB 不能为 0。if-testz 类型的指令有:

    • if-eqz:if(!vAA)

    • if-nez:if(vAA)

    • if-ltz:if(vAA<0)

    • if-gez:if(vAA>=0)

    • if-gtz:if(vAA>0)

    • if-lez:if(vAA<=0)

比较指令

对两个寄存器的值进行比较,格式为 cmpkind vAA, vBB, vCC,其中 vBB 和 vCC 寄存器是需要比较的两个寄存器或两个寄存器对,比较的结果放到 vAA 寄存器。指令集中共有5条比较指令:

  • cmpl-float

  • cmpl-double:如果 vBB 寄存器大于 vCC 寄存器,结果为 -1,相等结果为 0,小于结果为 1

  • cmpg-float

  • cmpg-double:如果 vBB 寄存器大于 vCC 寄存器,结果为 1,相等结果为 0,小于结果为 -1

  • cmp-long:如果 vBB 寄存器大于 vCC 寄存器,结果为 1,相等结果为 0,小于结果为 -1

字段操作指令

用于对对象实例的字段进行读写操作。对普通字段与静态字段操作有两种指令集,分别是 iinstanceop vA, vB, field@CCCC 与 sstaticop vAA, field@BBBB。扩展为 iinstanceop/jumbo vAAAA, vBBBB, field@CCCCCCC 与 sstaticop/jumbo vAAAA, field@BBBBBBBB。

普通字段指令的指令前缀为 i,静态字段的指令前缀为 s。字段操作指令后紧跟字段类型的后缀。

方法调用指令

用于调用类实例的方法,基础指令为 invoke,有 invoke-kind {vC, vD, vE, vF, vG}, meth@BBBB 和 invoke-kind/range {vCCCC .. vNNNN}, meth@BBBB 两类。扩展为 invoke-kind/jumbo {vCCCC .. vNNNN}, meth@BBBBBBBB 这类指令。

根据方法类型的不同,共有如下五条方法调用指令:

  • invoke-virtual 或 invoke-virtual/range:调用实例的虚方法

  • invoke-super 或 invoke-super/range:调用实例的父类方法

  • invoke-direct 或 invoke-direct/range:调用实例的直接方法

  • invoke-static 或 invoke-static/range:调用实例的静态方法

  • invoke-interface 或 invoke-interface/range:调用实例的接口方法

方法调用的返回值必须使用 move-result* 指令来获取,如:

invoke-static {}, Landroid/os/Parcel;->obtain()Landroid/os/Parcel;
move-result-object v0

数据转换指令

格式为 unop vA, vB,vB 寄存器或vB寄存器对存放需要转换的数据,转换后结果保存在 vA 寄存器或 vA寄存器对中。

  • 求补

    • neg-int

    • neg-long

    • neg-float

    • neg-double

  • 求反

    • not-int

    • not-long

  • 整型数转换

    • int-to-long

    • int-to-float

    • int-to-double

  • 长整型数转换

    • long-to-int

    • long-to-float

    • long-to-double

  • 单精度浮点数转换

    • float-to-int

    • float-to-long

    • float-to-double

  • 双精度浮点数转换

    • double-to-int

    • double-to-long

    • double-to-float

  • 整型转换

    • int-to-byte

    • int-to-char

    • int-to-short

数据运算指令

包括算术运算符与逻辑运算指令。

数据运算指令有如下四类:

  • binop vAA, vBB, vCC:将 vBB 寄存器与 vCC 寄存器进行运算,结果保存到 vAA 寄存器。以下类似

  • binop/2addr vA, vB

  • binop/lit16 vA, vB, #+CCCC

  • binop/lit8 vAA, vBB, #+CC

第一类指令可归类为:

  • add-type:vBB + vCC

  • sub-type:vBB - vCC

  • mul-type:vBB * vCC

  • div-type:vBB / vCC

  • rem-type:vBB % vCC

  • and-type:vBB AND vCC

  • or-type:vBB OR vCC

  • xor-type:vBB XOR vCC

  • shl-type:vBB << vCC

  • shr-type:vBB >> vCC

  • ushr-type:(无符号数)vBB >> vCC

smali 语法

类声明:

.class <访问权限> [修饰关键字] <类名>
.super <父类名>
.source <源文件名>

字段声明:

# static fields
.field <访问权限> static [修饰关键字] <字段名>:<字段类型>

# instance fields
.field <访问权限> [修饰关键字] <字段名>:<字段类型>

方法声明:

# direct methods
.method <访问权限> [修饰关键字] <方法原型>
    [.locals]
    [.param]
    [.prologue]
    [.line]
<代码体>
.end method

# virtual methods
.method <访问权限> [修饰关键字] <方法原型>
    [.locals]
    [.param]
    [.prologue]
    [.line]
<代码体>
.end method

需要注意的是,在一些老教程中,会看到 .parameter,表示使用的寄存器个数,但在最新的语法中已经不存在了,取而代之的是 .param,表示方法参数。

接口声明:

# interfaces
.implements <接口名>

注释声明:

# annotations
.annotation [注释属性] <注释类名>
    [注释字段 = 值]
.end annotation

循环语句

# for
Iterator<对象> <对象名> = <方法返回一个对象列表>;
for(<对象> <对象名>:<对象列表>){
    [处理单个对象的代码体]
}

# while
Iterator<对象> <迭代器> = <方法返回一个迭代器>;
while(<迭代器>.hasNext()){
    <对象> <对象名> = <迭代器>.next();
    [处理单个对象的代码体]
}

比如下面的 Java 代码:

public void encrypt(String str) {
    String ans = "";
    for (int i = 0 ; i < str.length(); i++){
        ans += str.charAt(i);
    }
    Log.e("ans:", ans);
}

对应下面的 smali:

# public void encrypt(String str) {
.method public encrypt(Ljava/lang/String;)V
.locals 4
.parameter p1, "str"    # Ljava/lang/String;
.prologue

# String ans = "";
const-string v0, ""
.local v0, "ans":Ljava/lang/String;

# for (int i  0 ; i < str.length(); i++){
# int i=0 =>v1
const/4 v1, 0x0
.local v1, "i":I
:goto_0     # for_start_place

# str.length()=>v2
invoke-virtual {p1}, Ljava/lang/String;->length()I
move-result v2

# i<str.length()
if-ge v1, v2, :cond_0

# ans += str.charAt(i);
# str.charAt(i) => v2
new-instance v2, Ljava/lang/StringBuilder;
invoke-direct {v2}, Ljava/lang/StringBuilder;-><init>()V
invoke-virtual {v2, v0}, Ljava/lang/StringBuilder;->append(Ljava/lang/String;)Ljava/lang/StringBuilder;
move-result-object v2

#str.charAt(i) => v3
invoke-virtual {p1, v1}, Ljava/lang/String;->charAt(I)C
move-result v3

# ans += v3 =>v0
invoke-virtual {v2, v3}, Ljava/lang/StringBuilder;->append(C)Ljava/lang/StringBuilder;
move-result-object v2
invoke-virtual {v2}, Ljava/lang/StringBuilder;->toString()Ljava/lang/String;
move-result-object v0

# i++
add-int/lit8 v1, v1, 0x1
goto :goto_0

# Log.e("ans:", ans);
:cond_0
const-string v2, "ans:"
invoke-static {v2, v0}, Landroid/util/Log;->e(Ljava/lang/String;Ljava/lang/String;)I
return-void
.end method

switch 语句

public void encrypt(int flag) {
        String ans = null;
        switch (flag){
            case 0:
                ans = "ans is 0";
                break;
            default:
                ans = "noans";
                break;
        }
        Log.v("ans:", ans);
    }

对应下面的 smali:

# public void encrypt(int flag) {
.method public encrypt(I)V
    .locals 2
    .param p1, "flag"    # I
    .prologue

# String ans = null;
    const/4 v0, 0x0
    .local v0, "ans":Ljava/lang/String;

# switch (flag){
    packed-switch p1, :pswitch_data_0   # pswitch_data_0指定case区域的开头及结尾

# default: ans="noans"
    const-string v0, "noans"

# Log.v("ans:", ans)
    :goto_0
    const-string v1, "ans:"
    invoke-static {v1, v0}, Landroid/util/Log;->v(Ljava/lang/String;Ljava/lang/String;)I
    return-void

# case 0: ans="ans is 0"
    :pswitch_0            # pswitch_<case的值>
    const-string v0, "ans is 0"
    goto :goto_0          # break
    nop
    :pswitch_data_0 #case区域的结束
    .packed-switch 0x0    # 定义case的情况
        :pswitch_0   #case 0
    .end packed-switch
.end method

根据 switch 语句的不同,case 也有两种方式:

# packed-switch
packed-switch p1, :pswitch_data_0
...
:pswitch_data_0
.packed-switch 0x0
    :pswitch_0
    :pswitch_1

# spase-switch
sparse-switch p1,:sswitch_data_0
...
sswitch_data_0
.sparse-switch
    0xa -> : sswitch_0
    0xb -> : sswitch_1 # 字符会转化成数组

try-catch 语句

public void encrypt(int flag) {
    String ans = null;
    try {
        ans = "ok!";
    } catch (Exception e){
        ans = e.toString();
    }
    Log.d("error", ans);
}

对应的下面的 smali:

# public void encrypt(int flag) {
.method public encrypt(I)V
    .locals 3
    .param p1, "flag"    # I
    .prologue

# String ans = null;
    const/4 v0, 0x0
    .line 20
    .local v0, "ans":Ljava/lang/String;

# try { ans="ok!"; }
    :try_start_0    # 第一个try开始,
    const-string v0, "ok!"
    :try_end_0      # 第一个try结束(主要是可能有多个try)
    .catch Ljava/lang/Exception; {:try_start_0 .. :try_end_0} :catch_0

# Log.d("error", ans);
    :goto_0
    const-string v2, "error"
    invoke-static {v2, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I
    return-void

# catch (Exception e){ans = e.toString();}
    :catch_0        #第一个catch
    move-exception v1
    .local v1, "e":Ljava/lang/Exception;
    invoke-virtual {v1}, Ljava/lang/Exception;->toString()Ljava/lang/String;
    move-result-object v0
    goto :goto_0
.end method

更多资料

  • 《Android软件安全与逆向分析》

Dalvik opcodes
android逆向分析之smali语法
Dalvik 虚拟机
Dalvik 指令集
指令格式
寄存器
类型、方法和字段
空操作指令
数据操作指令
返回指令
数据定义指令
锁指令
实例操作指令
数组操作指令
异常指令
跳转指令
比较指令
字段操作指令
方法调用指令
数据转换指令
数据运算指令
smali 语法
循环语句
switch 语句
try-catch 语句
更多资料