内存地址

反汇编窗口和寄存器窗口数据存储都是从高位到低位,数据区则是由低位到高位

寻址公式一:[立即数]

  • 读取内存的值:

    1
    2
    MOV EAX, DWORD PTR DS:[0x13FFC4]
    MOV EAX, DWORD PTR DS:[0x13FFC8]
  • 向内存中写入数据:

    1
    2
    MOV DWORD PTR DS:[0x13FFC4], eax
    MOV DWORD PTR DS:[0x13FFC8], ebx
  • 获取内存编号:

    1
    2
    LEA EAX, DWORD PTR DS:[0x13FFC4]
    LEA EAX, DWORD PTR DS:[ESP+8]

    LEA 拿的是内存的编号

寻址公式二:[reg] reg代表寄存器,可以是 8 个通用寄存器中的任意一个

  • 读取内存的值:

    1
    2
    MOV EAX, 0x13FFD0
    MOV EAX, DWORD PTR DS:[ECX]
  • 向内存中写入数据:

    1
    2
    MOV EDX, 0x13FFD8
    MOV DWORD PTR DS:[EDX], 0x87654321
  • 获取内存编号:

    1
    2
    MOV EAX, DWORD PTR DS:[EDX] 
    LEA EAX, DWORD PTR DS:[EDX]

    LEA 执行过后 EAX = EDX

寻址公式三:[reg+立即数]

  • 读取内存的值:

    1
    2
    MOV ECX, 0x13FFD0
    MOV EAX, DWORD OTR DS:[ECX+4]
  • 向内存中写入数据:

    1
    2
    MOV EDX, 0x13FFD8
    MOV DWORD PTR DS:[EDX+0xC], 0x87654321
  • 获取内存编号:

    1
    2
    MOV EAX, DWORD PTR DS:[EDX+4] 
    LEA EAX, DWORD PTR DS:[EDX+4]

寻址公式四:[reg+reg*{1,2,4,8}]

  • 读取内存的值:

    1
    2
    3
    MOV EAX, 0x13FFC4
    MOV ECX, 2
    MOV EDX, DWORD PTR DS:[EAX+ECX*4]
  • 向内存中写入数据:

    1
    2
    3
    MOV EAX, 0x13FFC4
    MOV ECX, 2
    MOV DWORD PTR DS:[EAX+ECX*4], 0x87654321
  • 获取内存编号:

    1
    LEA EAX, DWORD PTR DS:[EAX+ECX*4]

寻址公式五:[reg+reg*{1,2,4,8}+立即数]

  • 读取内存的值:

    1
    2
    3
    MOV EAX, 0x13FFC4
    MOV ECX, 2
    MOV EDX, DWORD PTR DS:[EAX+ECX*4+4]
  • 向内存中写入数据:

    1
    2
    3
    MOV EAX, 0x13FFC4
    MOV ECX, 2
    MOV DWORD PTR DS:[EAX+ECX*4+4], 0x87654321
  • 获取内存编号:

    1
    LEA EAX, DWORD PTR DS:[EAX+ECX*4+2]

堆栈

堆栈的优点:临时存储大量的数据,便于查找

1
2
MOV EBX, 0x13FFDC    BASE
MOV EDX, 0x13FFDC TOP
  1. 压入数据

    • 方式一:

      1
      2
      MOV DWORD PTR DS:[EDX-4], 0xAAAAAAAA
      SUB EDX, 4
    • 方式二:

      1
      2
      SUB EDX, 4
      MOV DWORD PTR DS:[EDX], 0xBBBBBBBB
    • 方式三:

      1
      2
      MOV DWORD PTR DS:[EDX-4], 0xDDDDDDDD
      LEA EDX, DWORD PTR DS:[EDX-4]
    • 方式四:

      1
      2
      LEA EDX, DWORD PTR DS:[EDX-4]
      MOV DWORD PTR DS:[EDX], 0xEEEEEEEE
  2. 读取第 N 个数

    • 方式一:通过 BASE 加偏移来读取

      1
      2
      3
      4
      读第一个压入的数据:
      MOV ESI, DWORD PTR DS:[EBX-4]
      读第四个压入的数据:
      MOV ESI, DWORD PTR DS:[EBX-0x10]
    • 方式二:通过 TOP 加偏移来读取

      1
      2
      3
      4
      读第二个压入的数据:
      MOV EDI, DWORD PTR DS:[EDX+8]
      读第三个压入的数据:
      MOV EDI, DWORD PTR DS:[EDX+4]
  3. 弹出数据

    • 方式一:

      1
      2
      MOV ECX, DWORD PTR DS:[EDX]
      LEA EDX, DWORD PTR DS:[EDX+4]
    • 方式二:

      1
      2
      MOV ESI, DWORD PTR DS:[EDX]
      ADD EDX, 4
    • 方式三:

      1
      2
      LEA EDX, DWORD PTR DS:[EDX+4]
      MOV EDI, DWORD PTR DS:[EDX-4]

PUSH 指令:

1
2
3
4
5
PUSH r32   执行后栈顶指针+4
PUSH r16 执行后栈顶指针+2
PUSH m16
PUSH m32
PUSH imm8/imm16/imm32

POP 指令:

1
2
3
4
POP r32
POP r16
POP m16
POP m32

PUSH/POP 一个立即数,永远都是esp ± 4;16 位 r/m – esp ± 2; 32 位 ± 4

PUSH 与 POP 操作没有跟 8 位寄存器的可能

PUSHAD 指令:把所有通用寄存器压入堆栈

POPAD 指令:恢复现场,把所有寄存器的值恢复

保护现场用,在 pushad 后可以随便动寄存器,调试后用 popad 恢复现场

课后作业

  1. 使用 EBX 存储栈底地址,EDX 存储栈顶地址,连续存储 5 个不同的数

    运行后:

  2. 分别使用栈底加偏移、栈顶加偏移的方式读取这 5 个数,并存储到寄存器

    两个方式各取一个数,运行后:

  3. 弹出这 5 个数,恢复栈顶到原来的位置

    运行后:

  4. 使用 2 种方式实现:push ecx

    1
    2
    MOV DWORD PTR DS:[ESP-4], ECX
    SUB ESP, 4
    1
    2
    MOV DWORD PTR DS:[ESP-4], ECX
    LEA ESP, DWORD PTR DS:[ESP-4]
  5. 使用 2 种方式实现:pop ecx

    1
    2
    MOV ECX, DWORD PTR DS:[ESP]
    ADD ESP, 4
    1
    2
    MOV ECX, DWORD PTR DS:[ESP]
    LEA ESP, DWORD PTR DA:[ESP+4]
  6. 使用 2 种方式实现:push esp

    1
    2
    MOV DWORD PTR DS:[ESP-4], ESP
    SUB ESP, 4
    1
    2
    MOV DWORD PTR DS:[ESP-4], ESP
    LEA ESP, DWORD PTR DS:[ESP-4]
  7. 使用 2 种方式实现:pop esp

    1
    MOV ESP, DWORD PTR DS:[ESP]
    1
    2
    LEA ESP, DWORD PTR DS:[ESP+4]
    MOV ESP, DWORD PTR DS:[ESP-4]