Android逆向工程:Smali语法解析完整指南

Smali语言基础语法 定义、声明、调用 Smali数据类型 定义:与Java相同,Smali中也有一套数据类型体系,且该体系实质上是同Java一一对照的。 示例1:Smali——Java数据类型对照表 PS:如上图所示,Smali语言中的数据类型的关键词基本上为Java中同名数据类型的首字母大写,需要着重记忆的例外为:1.long类型的关键词为大写J;2.boolean类型的关键词为大写Z;3.数组的关键词为左半边中括号[;4.object类型(各种类)的关键词为大写L加上该类的全类名路径,路径中的层级用/分割。 阅读详情

完整Smali语法指南 & 一些个人理解

参考来源:CTF Wiki - Smali

目录


1. Smali基础介绍

1.1 什么是Smali

Smali 是 Android 应用程序反编译后的一种中间表示形式:

  • dex文件 → baksmali工具 → smali文件(人类可读)
    backSmail 工具按照一定格式解析的dex二进制文件,再转成的smail格式文件~
  • smali文件 → smali工具 → dex文件(虚拟机执行)

2. 数据类型系统

2.1 基础数据类型

Smali标识 Java类型 描述 示例
V void 空类型 方法返回值
Z boolean 布尔类型 true/false
B byte 8位字节 -128~127
S short 16位短整型 -32768~32767
C char 16位字符 Unicode字符
I int 32位整型 标准整数
J long 64位长整型 大整数
F float 32位浮点 单精度小数
D double 64位浮点 双精度小数

2.2 对象类型

格式 说明 Java等价 Smali示例
Lpackage/Class; 完整类名 package.Class Ljava/lang/String;
[type 数组类型 type[] [I (int数组)
[[type 二维数组 type[][] [[Ljava/lang/String;

2.3 方法签名格式

方法名(参数类型...)返回类型

示例对照:

// Java方法
public String test(int a, boolean b) {
   
    ... }
# Smali签名
test(IZ)Ljava/lang/String;

3. 字节码语法

3.1 字段语法

.field [访问修饰符] [字段名]:[类型]

示例:

.field private TAG:Ljava/lang/String;
.field private running:Z

3.2 方法语法

.method [访问修饰符] [方法名](参数类型)返回类型
    [.registers N]          # 寄存器声明
    [.parameter "参数名"]    # 参数注释
    [.locals N]             # 本地变量声明
    
    .prologue              # 方法开始标记
    [.line 行号]            # 源码行号对应
    
    # 方法体指令
    [指令 寄存器, 参数...]
    
    [return指令]           # 返回语句
.end method

示例:

.method public constructor <init>()V
    .locals 1

    .prologue
    .line 8
    # 调用Activity中的init()方法
    invoke-direct {
   
   p0}, Landroid/app/Activity;-><init>()V

    .line 10
    const-string v0, "MainActivity"
    iput-object v0, p0, Lcom/social_touch/demo/MainActivity;->TAG:Ljava/lang/String;

    .line 13
    const/4 v0, 0x0
    iput-boolean v0, p0, Lcom/social_touch/demo/MainActivity;->running:Z

    return-void
.end method

4. 寄存器概念

4.1 寄存器声明

.registers N    # 声明使用N个寄存器(v0 ~ vN-1)
.locals M       # 声明M个本地变量寄存器

4.2 寄存器类型

本地寄存器 (v0-vN)
.registers 4    # 可用寄存器:v0, v1, v2, v3
const/4 v0, 0x5    # v0 = 5
const/4 v1, 0x3    # v1 = 3
add-int v2, v0, v1 # v2 = v0 + v1 = 8
参数寄存器 (p0-pN)

静态方法:

# Java: public static void test(int a, String b)
.method public static test(ILjava/lang/String;)V
    .registers 3
    # p0 = 第一参数(int a)
    # p1 = 第二参数(String b)
.end method

实例方法:

# Java: public void test(int a, String b)
.method public test(ILjava/lang/String;)V
    .registers 4
    # p0 = this (当前对象)
    # p1 = 第一参数(int a)  
    # p2 = 第二参数(String b)
.end method

5. 方法语法结构

5.1 方法声明语法

.method [访问修饰符] [方法名](参数类型)返回类型
    [.registers N]          # 寄存器声明
    [.parameter "参数名"]    # 参数注释
    [.locals N]             # 本地变量声明
    
    .prologue              # 方法开始标记
    [.line 行号]            # 源码行号对应
    
    # 方法体指令
    [指令 寄存器, 参数...]
    
    [return指令]           # 返回语句
.end method

5.2 访问修饰符

修饰符 说明 示例
public 公开访问 .method public test()V
private 私有访问 .method private test()V
protected 受保护访问 .method protected test()V
static 静态方法 .method public static test()V
final 最终方法 .method public final test()V
abstract 抽象方法 .method public abstract test()V
synchronized 同步方法 .method public synchronized test()V

5.3 特殊方法

方法名 作用 Java等价
<init> 构造方法 public ClassName() { ... }
<clinit> 静态初始化块 static { ... }

6. 字段语法结构

6.1 字段声明语法

.field [访问修饰符] [字段名]:[类型]

示例:

.field private TAG:Ljava/lang/String;
.field private running:Z

6.2 访问修饰符

修饰符 说明 示例
public 公开访问 .field public TAG:Ljava/lang/String;
private 私有访问 .field private TAG:Ljava/lang/String;
protected 受保护访问 .field protected TAG:Ljava/lang/String;
static 静态字段 .field public static TAG:Ljava/lang/String;
final 最终字段 .field public final TAG:Ljava/lang/String;

7. 指令语法详解

7.1 常量指令

整数常量
const/4 vAA, #+B          # 4位整数 (-8~7)
const/16 vAA, #+BBBB      # 16位整数 (-32768~32767)
const vAA, #+BBBBBBBB     # 32位整数
const/high16 vAA, #+BBBB0000  # 高16位

# 示例
const/4 v0, 0x5           # v0 = 5
const/16 v1, 0x1000       # v1 = 4096
const v2, 0x12345678      # v2 = 0x12345678
字符串常量
const-string vAA, "string"    # 字符串常量
const-class vAA, Ltype;       # 类对象常量

# 示例
const-string v0, "Hello World"
const-class v1, Ljava/lang/String;

7.1.1 const/high16 指令深度解析

7.1.1.1 什么是"高位"和"低位"?

想象一个32位数字 0x12345678:

高位(High Bits):值较大的部分 → 0x1234(前16位)
低位(Low Bits):值较小的部分 → 0x5678(后16位)

    高16位         低16位
   ┌────────┬────────────┐
   │ 0x1234 │   0x5678   │  ← 32位数值
   └────────┴────────────┘
   ▲                    ▲
   MSB                  LSB

更直观的字节视图:

高位             低位
┌────┬────┬────┬────┐
│ 12 │ 34 │ 56 │ 78 │  ← 字节序列
└────┴────┴────┴────┘
▲              ▲
MSB            LSB   (最高位 ←→ 最低位)
7.1.1.2 字节序(Endianness):数据在内存中的存储顺序

1. 小端序(Little-Endian) → Android/Intel 使用

规则:低位字节在前(低地址),高位字节在后(高地址)

示例:0x12345678 在内存中的存储:

内存地址: 0x1000  0x1001  0x1002  0x1003
          ┌─────┬─────┬─────┬─────┐
存储内容: │ 78  │ 56  │ 34  │ 12  │  ← 低位到高位
          └─────┴─────┴─────┴─────┘

2. 大端序(Big-Endian) → 网络传输/部分嵌入式系统使用

规则:高位字节在前(低地址),低位字节在后(高地址)

示例:0x12345678 存储为:

内存地址: 0x1000  0x1001  0x1002  0x1003
          ┌─────┬─────┬─────┬─────┐
存储内容: │ 12  │ 34  │ 56  │ 78  │  ← 高位到低位
          └─────┴─────┴─────┴─────┘
7.1.1.3 为什么需要 const/high16?

Android 指令设计需节省空间 → 用 16 位指令加载 32 位常量的高半部分:

const/high16 v0, 0x10000000  # 仅加载高16位:0x1000 → v0 = 0x10000000
const/16 v0, 0x0000         # 加载低16位:0x0000 → 需后续合并

合并逻辑(伪代码):

uint32_t value = (high16_value << 16) | low16_value;
// 0x10000000 | 0x0000 = 0x10000000

实战:小端序下的数据解析

假设内存中存储 78 56 34 12(小端序):

按字节读取:

地址0: 0x78
地址1: 0x56
地址2: 0x34
地址3: 0x12

组合为32位值:

value = (byte3 << 24) | (byte2 << 16) | (byte1 << 8) | byte0
      = (0x12 << 24) | (0x34 << 16) | (0x56 << 8) | 0x78
      = 0x12345678  // 正确值
7.1.1.4 32位常量加载完整原理(以 0x12345678 为例)

步骤 1:拆分高低位

原始值:0x12345678
高16位:0x1234
低16位:0x5678

步骤 2:两条指令分别加载

# 加载高16位 → 存入寄存器 v0
const/high16 v0, 0x12340000  # 注意:必须补零到32位格式!

# 加载低16位 → 存入寄存器 v1
const/16 v1, 0x5678

步骤 3:合并操作(编译器自动插入)

# 编译器生成的隐藏指令(实际字节码)
or-int/lit16 v0, v0, 0x5678  # v0 = v0 | 0x5678 → 0x12345678

关键点:const/high16 加载的值末尾 16 位是 0(0x12340000),后续通过 or-int/lit16 将低16位(0x5678)合并进去。

7.1.1.5 为什么要这样的设计?

问题:高位在前,低位在后,在Android中的dex文件二进制是小端序列,也就是需要从后往前读,大端序列就是从前往后读,这一个理解了,但是为什么要这样做呢?另外读取了16位的指令加载了32位常量的前面的一半部分,那么后面剩下的一半呢?

解答:

1. 指令空间压缩

  • 32 位常量需 4 字节存储 → 但 Dalvik 指令长度仅 2 字节
  • 解决方案:拆成两条 2 字节指令(const/high16 + const/16)

2. 性能平衡

  • 全量加载(如 const v0, 0x12345678)需 6 字节(操作码 2字节 + 常量 4字节)
  • 分片加载 仅需 4 字节(两条 2 字节指令) → 节省 33% 空间

核心理解:

  • const/high16 不是独立操作 → 它只是 32 位常量加载的第一步
  • 后续必跟合并指令(or-int/lit16 等)→ 将低16位"焊接"到高位值的尾部
  • 设计本质:牺牲少量指令数(2→3条),换取存储空间优化(6字节→4字节)
7.1.1.6 空间优化的深度分析

疑问:这样操作好处是存储空间的优化,分批加载,然后再合并,可是合并之后的长度不依然是很大吗?

解答:加载const指令的两种方式对比 — 空间优化的本质(指令长度和常量长度)

1. 传统全量加载(const 指令)

const v0, 0x12345678  # 指令长度:6字节
  • 结构:操作码(2字节) + 常量值(4字节)
  • 总长:6 字节(恒定)

2. 分片加载(const/high16 + const/16 + 合并)

const/high16 v0, 0x12340000  # 2字节
const/16 v1, 0x5678           # 2字节
or-int/lit16 v0, v0, 0x5678   # 2字节(合并指令)
  • 总长:6 字节(3条指令 × 2字节)
  • 空间未减少?表面看无优势?
7.1.1.7 分片加载的隐藏收益

1. 高频小值的极致压缩

对常见小数字(如 0、1、-1):

全量加载(浪费)

const v0, 0x1  # 6字节 → 操作码2字节 + 常量4字节(其中3.75字节为0)

分片加载(高效)

const/4 v0, 0x1  # 仅需
smali语法从入门到精通 Smali是Android虚拟机的反汇编语言。Android虚拟机的可执行文件并不是普通的class文件,而是再重新整合打包后生成的dex文件。dex文件反编译之后就是Smali代码,所以说,Smali语言是Android虚拟机的反汇编语言。本教程让你1小时了解Smali的基础知识,让你很快从入门到精通。 阅读详情

相关推荐

Java层逆向--Dalvik指令集

往期推荐 Java层逆向分析-Dalvik字节码 修改资源去广告 修改包名实现分身 篡改Apk名称、图标 AndroidKiller介绍与使用 Dalvik的指令格式: 基础字节码 - 名称后缀/字节码后缀 目的寄存器 源寄存器 (连接符号“- ”在有的指令里是可以不存在的) 名称后缀是wide,表示数据宽度为64位 字节码后缀是from16,表示源寄存器为16位 例如:move-wide/from16 vAA,VBBBB: move为基础字节码,即opcode。 wide为名称后缀,标识指令操作的数据

YJJYXM的博客 1257

Android逆向工程入门:APK反编译与Smali语法实战解析

在移动应用开发与安全领域,逆向工程是一项关键技术,它通过分析已编译的程序来理解其内部结构与运行逻辑。其核心原理在于将机器码或字节码转换回更易读的中间表示形式,从而揭示应用的功能实现与潜在逻辑。这项技术的价值在于赋能安全研究、漏洞挖掘、代码审计以及疑难问题调试,是深入理解Android应用不可或缺的技能。在具体实践中,APK反编译与Smali语法分析构成了Android逆向的基石。通过使用**Apktool**、**JADX**等工具链,可以将APK文件解包并反编译为可读的Smali代码——一种Dalvik/

weixin_34221073的博客 435

app安卓逆向之smali代码log注入与原代码修改

文章目录1.背景分析2.概述3.开始3.1smali代码基础知识普及3.2apktool的基本使用3.3构建个人Log类并且反编译成Smali文件注入到某电商App中3.4重编译修改后的App项目,安装到模拟器中运行并使用注入的代码打印日志总结 1.背景分析 在安卓逆向的过程中常常会面临三个场景 想要了解某个方法是否有被调用 经过常规的代码分析后,想要知道App某些方法内部某些变量的值 App在Jave层的一些安全检测代码在我们的研究阶段需要屏蔽及修改 以上第一、第二个场景使用Xposed、

weixin_43900244的博客 6004

Windows平台安卓逆向工程:从APK拆解到动态调试的完整工具链指南

安卓逆向工程是深入理解移动应用内部机制的关键技术,它通过静态与动态分析相结合的方法,揭示应用的代码逻辑、数据流和通信协议。其核心原理在于对APK文件进行解包、反编译和运行时监控,从而还原应用的设计思路与实现细节。这项技术的价值不仅在于安全评估和漏洞挖掘,也为开发者学习优秀架构、进行兼容性调试提供了独特视角。在应用场景上,它广泛用于移动安全研究、协议分析、恶意软件检测以及合法合规的第三方功能扩展。本文聚焦于Windows环境下的实战流程,详细解析了以Apktool、JADX和Frida为核心的工具链搭建与使用

889

【Windows】安卓逆向工程实战:从APK到源码的完整工具链解析

本文详细解析了在Windows平台上进行安卓应用逆向工程的完整工具链。从环境准备开始,逐步介绍了如何使用apktool拆解APK获取资源与清单,利用dex2jar将DEX文件转换为JAR格式,并通过JD-GUI反编译查看近似Java源码。文章还涵盖了处理VDEX/ODEX等复杂情况的进阶方法,并强调了学习过程中的安全合规指引,为安全研究和学习提供了一套清晰的实战路径。

cicd6pipeline的博客 763

Android逆向工程实战:Apktool工具链与Smali代码修改

在移动应用开发和安全研究领域,逆向工程是一项关键技术,它允许开发者深入分析已编译应用的内在结构和逻辑。其核心原理是通过反编译工具将二进制文件还原为可读的中间代码或资源文件,从而实现对应用行为的理解和修改。这项技术的价值在于支持安全审计、漏洞挖掘、兼容性调试以及学习优秀应用的设计模式。典型的应用场景包括分析第三方库、排查运行时崩溃问题,以及进行合法的安全评估。本文聚焦于Android平台,详细介绍了以Apktool为核心的工具链搭建,并深入探讨了Smali代码的修改技巧,例如通过调整跳转指令绕过授权验证逻辑,

weixin_34248705的博客 398

Android逆向工程:Smali指令集实战解析与优化技巧

本文深入解析Android逆向工程中的Smali指令集,提供从环境搭建、核心语法到实战分析与优化的完整指南。通过具体案例演示如何阅读和修改Smali代码以洞察应用逻辑,并分享性能优化与调试技巧,帮助开发者将Smali从“天书”变为逆向分析的利器。

weixin_29245767的博客 972

Android Killer入门指南:安卓逆向工程图形化工具实战解析

安卓逆向工程是分析移动应用内部机制、理解其工作原理的关键技术,广泛应用于安全评估、漏洞挖掘与学习研究。其核心原理在于通过反编译、静态分析与动态调试等技术,将已编译的APK文件还原为可读的代码与资源,从而洞察应用逻辑。掌握这项技术不仅能深化对安卓系统运行机制的理解,更能提升开发者的代码安全与防御能力。在众多工具中,Android Killer以其图形化集成环境脱颖而出,它无缝整合了Apktool、dex2jar、jd-gui等经典命令行工具,将复杂的反编译、代码查看、打包签名流程可视化,极大降低了逆向工程的学

dieqiao8331的博客 448

Android逆向工程工具Smali2Java实战解析

Android逆向工程是指通过对APK安装包的反编译、解析与代码还原,深入分析其内部逻辑与实现机制的技术过程。它广泛应用于安全漏洞挖掘、功能复用、竞品分析以及恶意代码检测等领域。逆向工程的核心对象是APK文件,其内部包含(Dalvik字节码)、、资源文件等关键组件。通过分析这些内容,开发者可以理解程序的运行机制,甚至修改其行为。然而,面对代码混淆(如ProGuard)、加壳保护等技术,逆向分析也面临诸多挑战。

weixin_42509513的博客 1978

用Multisim复刻经典收音机:从零搭建超外差中波调幅接收机(附仿真源文件)

本文详细介绍了如何使用Multisim软件从零搭建超外差中波调幅接收机,复刻经典收音机的设计过程。通过模块化搭建、稳定性优化和增益分配等实战指南,帮助读者掌握超外差架构的核心技术,并附赠仿真源文件供实践参考。

weixin_28744423的博客 473

AI辅助发明专利撰写实战:30分钟高效生成高质量技术专利文档

在技术专利撰写领域,尤其是软件、算法或系统架构相关的发明专利,如何将创新的技术构思快速、规范地转化为符合《专利法》要求的申请文件,是许多开发者和技术团队面临的共同挑战。传统的人工撰写方式不仅耗时耗力,对撰写人的法律和技术双重背景要求极高,还容易因格式或术语不规范导致审查意见增多。本文将分享一套利用 AI 辅助工具,系统化、高效率地完成一篇高质量发明专利说明书撰写的实战流程。通过清晰的步骤拆解、可复制的提示词(Prompt)工程以及关键注意事项,即使是首次接触专利撰写的开发者,也能在短时间内掌握从技术方案梳理

子夜轻风 950

Windows Server安装Intel I219-V网卡驱动完整教程

在Windows Server系统上安装Intel I219-V网卡驱动的方法。

小白成长历程 5172

化工管道设计软件:CAESAR II二次开发_(6).API接口与函数库

自定义函数库是开发者根据特定需求编写的函数集合。这些函数可以扩展CAESAR II的功能,实现更复杂的管道设计任务。自定义函数库通常需要开发者具备一定的编程基础,能够理解和编写与CAESAR II接口兼容的代码。定义函数:在Python或其他编程语言中定义所需的函数。注册函数:将自定义函数注册到CAESAR II的函数库中。调用函数:在CAESAR II中调用自定义函数,实现特定功能。假设我们需要创建一个自定义函数库,用于批量修改管道节点的坐标。定义函数:在Python中定义一个函数,用于批量修改节点坐标。

kkchenjj的博客 1728

Axure RP9.0 教程:左侧菜单列表导航 ( 点击父级菜单,子菜单自动收缩或展开)【响应式的菜单导航】

1. 设置目标原件时,选择切换,这样就可以实现,当原件展示时,点击自动隐藏,隐藏时自动显示; 2. 展开/收起原件(推动/拉动原件)设置为方向**向下**

专注于计算机研发知识和资讯分享 1378

20、微电网系统的系统框架、建模与控制架构解析

本文详细解析了微电网系统的系统框架、建模方法以及控制架构。文章首先介绍了在系统的系统(SoS)框架下,微电网如何通过微控制器(MC)和微电网中央控制器(MGCC)协调运行,并支持并网与孤岛模式下的高效能量管理。随后,针对微电网的关键组件,包括微涡轮机、光伏太阳能电池和风力涡轮机,提供了详细的数学建模分析。最后,探讨了微电网的控制架构设计,涵盖分层控制和共识控制策略,以实现系统的高效、稳定运行,满足日益增长的能源需求与市场要求。

elastic6hunter的博客 206

23、资产分散化与有效前沿:投资组合优化的核心策略

本文深入探讨了现代投资组合理论的核心概念,包括分散投资的重要性、均值-方差优化框架以及有效前沿的构建与应用。通过案例分析和数学模型,阐述了如何通过科学方法降低投资组合风险并实现最优风险收益权衡。文章还介绍了实际应用中的求解方法与决策流程,为投资者提供系统的投资组合优化指导。

cc789的博客 114

基于STM32、HAL库的SC16IS752IPW总线转UART驱动程序设计

/ FIFO空间不足。// 设置LCR寄存器的DLAB位为1以访问DLL/DLH。// 设置LCR寄存器,先访问DLL/DLH。// 设置LCR寄存器(不包含DLAB位)

corlin6688的博客 815

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

上一篇: Html&Css 基础总结(基础好了才是最能打的)五-大结局
下一篇: Android逆向工程:Dex 文件格式解析详解
@王永超
博客等级 码龄10年 186粉丝 88原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值