汇编 汇编寻址 (逆向分析)

目录

汇编选择基础知识点

先理解什么是"栈帧"?

堆栈结构

什么是汇编寻址方式?

汇编寻址的目的

段寻址

[8086 CPU 内存寻址机制](#8086 CPU 内存寻址机制)

[1. 为什么引入分段寻址?](#1. 为什么引入分段寻址?)

[2. 物理地址计算公式](#2. 物理地址计算公式)

[3. 主要段寄存器](#3. 主要段寄存器)

段地址计算实际案例

段重叠现象

寻址方式

立即数寻址

寄存器寻址

[基址寻址 / 直接寻址](#基址寻址 / 直接寻址)

寄存器间接寻址

寄存器相对寻址

[基址 + 变址寻址](#基址 + 变址寻址)

[相对基址 + 变址寻址](#相对基址 + 变址寻址)

比例因子寻址

总结


汇编选择基础知识点

先理解什么是"栈帧"?

这个在前面已经提到了很多次

函数调用时,会在栈中为该函数分配一块内存区域来存储:

  • 函数的局部变量

  • 函数的参数

  • 返回地址、保存的 ebp

这块区域就叫做栈帧(Stack Frame),由 ebp (基址指针)+ esp (栈顶指针)管理。

堆栈结构

复制代码
         ↑
         |         ← esp(栈顶)
局部变量3 [ebp-0Ch]
局部变量2 [ebp-08h]
局部变量1 [ebp-04h]
旧的ebp  [ebp]      ← ebp(基址)
返回地址 [ebp+4]
参数1    [ebp+8]
参数2    [ebp+0Ch]
         |
         ↓

什么是汇编寻址方式?

  • 汇编寻址方式 (Addressing Modes)是指 CPU 执行指令时,如何找到操作数(数据)所在位置的方法。它定义了指令中操作数的来源和访问方式,是汇编语言中最核心的概念之一。

  • 在机器指令中,除了操作码(做什么),还必须明确操作数在哪里。寻址方式就是解决"数据在哪里"的问题,不同的寻址方式决定了指令的灵活性、执行速度和适用场景。

汇编寻址的目的

  • 提高程序的灵活性:能够灵活访问寄存器、立即数、内存变量、数组、结构体等。

  • 优化执行效率:不同场景选择最合适的寻址方式,能显著减少指令周期和内存访问次数。

  • 实现高级语言特性:指针、数组、结构体、栈帧等功能在底层都是通过不同寻址方式实现的。

  • 支撑复杂数据结构处理:如数组遍历、链表操作、函数调用等。

段寻址

8086 CPU 内存寻址机制

1. 为什么引入分段寻址?

8086 是 16 位 CPU,但地址总线为 20 位,可寻址 1MB 内存(2²⁰ = 1,048,576 字节)。

由于寄存器只有 16 位,无法直接表示 20 位地址,因此采用 "段 + 偏移" 的分段内存管理模式。

2. 物理地址计算公式

物理地址 = (段基址 << 4) + 偏移地址

  • << 4 等价于段基址 × 16(左移 4 位,低 4 位补 0)

  • 段基址:16 位,存放在段寄存器中

  • 偏移地址:16 位,最大范围 64KB(一个段的大小)

3. 主要段寄存器

|--------|---------------|------------|
| 段寄存器 | 英文全称 | 主要用途 |
| CS | Code Segment | 代码段(存放指令) |
| DS | Data Segment | 数据段(存放数据) |
| SS | Stack Segment | 栈段(存放栈数据) |
| ES | Extra Segment | 附加段(辅助数据段) |

FS 和 GS 在 80386 及以后新增,常用于线程局部存储等。

段地址计算实际案例

复制代码
; 示例:初始化数据段
mov ax, @data      ; @data 是汇编器提供的当前数据段基址
mov ds, ax         ; 将段基址加载到 DS 寄存器

; 访问数据
mov al, [szHello]  ; 从 DS:szHello 偏移处取一个字节

详细计算示例

假设段基址 DS = 1230H,偏移地址 BX = 00C8H

  1. 段起始物理地址 = 1230H × 16 = 12300H

  2. 物理地址 = 12300H + 00C8H = 123C8H

汇编代码演示

复制代码
mov ax, 1230H
mov ds, ax
mov bx, 00C8H
mov al, [bx]       ; 实际访问物理地址 123C8H

段重叠现象

同一物理地址可以有多种逻辑地址表示,这是分段机制的重要特点。

重叠示例

  • 段基址 1230H + 偏移 00C8H = 物理地址 123C8H

  • 段基址 122CH + 偏移 0108H = 物理地址 123C8H(同一位置)

当然段不能跨段寻址这里涉及到段保护的问题 这个后面再说

寻址方式

立即数寻址

立即数寻址是指操作数直接嵌入在指令中,不需要从内存或寄存器中读取。

  • 这种方式简单高效,常用于初始化常量值。

  • mov eax, 111h 展示了这一方式:

  • 直接将十六进制值 111h 移动到 EAX 寄存器中。

  • 原理在于,CPU 在执行时直接从指令码中提取立即数,无需额外寻址周期,提高执行速度。

  • 这是最快的寻址方式,适用于常量初始化、标志设置等场景。

    mov eax, 111h ; EAX = 0x111
    mov ebx, 12345678h ; 32位立即数
    add ecx, 100 ; 立即数参与运算
    push 0 ; 压入立即数到栈
    mov dword ptr [var], 5 ; 直接给内存变量赋值

寄存器寻址

  • 寄存器寻址使用 CPU 内部寄存器作为操作数来源或目标。

  • 这种方式速度最快,因为寄存器位于 CPU 内部,无需访问内存。

  • 在源码中,mov ebx, 111h 先将立即数加载到 EBX,然后 mov ebx, eax 将 EAX 的值复制到 EBX。

  • 原理是寄存器间的直接数据传输,通过内部总线完成,适用于临时计算和数据传递。

    mov eax, 111h
    mov ebx, eax ; EBX ← EAX
    add ecx, ebx ; ECX ← ECX + EBX
    xchg eax, edx ; 交换寄存器内容
    inc esi ; ESI 自增
    sub eax, eax ; EAX 清零(推荐写法)

基址寻址 / 直接寻址

  • 基址寻址使用一个基址寄存器(如 EAX)来存储内存地址,然后访问该地址的内容。

  • 源码中 lea eax, dwIndex 使用 LEA 指令将变量 dwIndex 的地址加载到 EAX 中。

  • 原理是通过计算有效地址(Effective Address),将基址直接作为偏移,实现对内存变量的间接访问,常用于数组或结构体的基地址计算。

    .data
    dwIndex DWORD 0A5A5A5A5h

    .code
    lea eax, dwIndex ; EAX = 变量地址
    mov eax, dwIndex ; 直接读取变量内容
    mov dwIndex, 12345678h ; 直接写入变量

寄存器间接寻址

  • 寄存器间接寻址通过寄存器中的值作为内存地址来访问数据。

  • 源码中 lea ebx, dwIndex 先加载地址到 EBX,然后 mov esi, [ebx] 从该地址读取值到 ESI。

  • 原理是寄存器内容被解释为指针,CPU 通过内存管理单元(MMU)访问相应内存位置,适用于指针操作。

    lea ebx, dwValue ; EBX = 内存地址
    mov esi, [ebx] ; ESI ← [EBX] 指向的内容
    mov dword ptr [ebx], 88888888h ; 通过指针写入
    mov al, byte ptr [ebx] ; 读取单字节

寄存器相对寻址

  • 寄存器相对寻址在基址寄存器基础上添加偏移量。

  • 源码中涉及栈帧操作:mov ebp, esp 设置基指针,

  • mov esi, [ebp + 4] 访问栈上偏移4字节的位置,然后恢复栈。

  • 原理是使用 EBP 作为栈帧基址,加上偏移访问局部变量或参数,常用于函数调用中的栈管理。

    push ebp
    mov ebp, esp

    mov eax, [ebp + 8] ; 第1个参数
    mov ebx, [ebp - 4] ; 局部变量
    mov ecx, [ebp + 12] ; 第2个参数

    mov esp, ebp
    pop ebp
    ret

基址 + 变址寻址

  • 这种寻址结合基址和变址寄存器,实现动态偏移。

  • 源码中 mov eax, [ebx + esi] 将 EBX(基址)+ ESI(变址)处的内存值加载到 EAX。

  • 原理是通过地址计算单元(AGU)相加两个寄存器值,形成有效地址,适用于数组遍历(如数组[base + index])。

    lea ebx, array ; EBX = 数组基地址
    mov esi, 2 ; 索引
    mov eax, [ebx + esi*4] ; array[2]

相对基址 + 变址寻址

  • 类似于上一种,但添加常量偏移。

  • 源码中 mov eax, [eax + ecx + 4] 使用 EAX(基址)+ ECX(变址)+ 4(偏移)访问内存。

  • 原理是扩展寻址灵活性,AGU 计算总偏移,常见于结构体成员访问。

    lea eax, structArray
    mov ecx, 3

    mov edx, [eax + ecx*8 + 4] ; structArray[3].val (假设结构体大小8字节)

比例因子寻址

  • 比例因子寻址允许变址寄存器乘以一个比例(如 2、4、8),用于数组元素大小调整。

  • 源码中 mov eax, [eax + ecx * 4] 表示 EAX + (ECX * 4) 处的内存访问。

  • 原理是模拟数组索引(如 int array[ecx],每个元素4字节),AGU 处理乘法和加法,提高数据结构访问效率。

    mov ecx, 5

    mov eax, [intArray + ecx4] ; intArray[5] (4字节元素)
    mov al, [byteArray + ecx] ; byteArray[5] (比例因子1可省略)
    mov edx, [structArray + ecx
    16 + 8] ; 结构体数组成员

  • x86 最强大的寻址模式,由硬件直接支持 1/2/4/8 倍乘法,是数组和结构体数组访问的最优选择。

总结

|----|----------|---------------------------------|---------------|
| 序号 | 寻址方式 | 典型指令形式 | 主要应用场景 |
| 1 | 立即数寻址 | mov eax, 123h | 常量初始化 |
| 2 | 寄存器寻址 | mov eax, ebx | 临时计算、高速操作 |
| 3 | 直接/基址寻址 | mov eax, var / lea eax, var | 全局变量访问 |
| 4 | 寄存器间接寻址 | mov eax, [ebx] | 指针、动态内存 |
| 5 | 寄存器相对寻址 | mov eax, [ebp+8] | 函数参数与局部变量 |
| 6 | 基址+变址寻址 | mov eax, [ebx+esi] | 简单数组遍历 |
| 7 | 基址+变址+偏移 | mov eax, [ebx+esi+4] | 结构体成员访问 |
| 8 | 比例因子寻址 | mov eax, [ebx+esi*4] | 数组与结构体数组(最高效) |

相关推荐
浩浩测试一下21 小时前
汇编 位运算 (逆向分析)
汇编·逆向·位运算·asm·windows编程·二进制逆向
浩浩测试一下21 小时前
汇编 高低八位寄存器数据存储方式(逆向分析)
汇编·网络安全·逆向·二进制·免杀·寄存器·windows编程
a83331961 天前
C语言嵌入汇编详解
汇编·单片机·语言
yoyo_zzm2 天前
汇编到PHP:五大编程语言核心特性全解析
开发语言·汇编·php
ComputerInBook4 天前
X64 汇编 MOVSD 的两种用法
汇编·汇编指令·movsd
YangWeiminPHD5 天前
金水32051编译器下的AI8051U单片机入门:从点亮LED到“你好,世界,我来了!”
c语言·汇编·51单片机·编译器
九思十安7 天前
HNU2026-计算机系统-笔记 5 汇编进阶
汇编·笔记
九思十安7 天前
HNU2026-计算机系统-笔记 4 汇编初步
汇编·笔记
这猪好帅8 天前
协程原理与实现
汇编