程序的机器级表示(包含大量IA32-Intel格式汇编代码!!!)
程序的机器级表示
寻址方式
立即寻址
直接寻址
寄存器寻址
寄存器间接寻址
基址寻址
变址寻址
……
IA32
汇编指令
通用寄存器
前四个:a ->
add
, b ->base
, c ->count
, d -> 'data'si: s ->
source
-> 源地址, di ->destination
-> 目的地址bp ->
base pointer
, sp ->stack pointer
ATT
& Intel
寻址方式
常用指令
数据传输指令
mov
指令数据传递方向
ATT
->
Intel
<-
- lea:load effective address,加载有效地址,将一个内存地址加载到目的寄存器;
- push:将数据压入栈,同时 esp 减去数据长度;
- pop:将栈顶数据弹出栈,同时 esp 加上数据长度;
算术和逻辑运算指令
除法指令,它们都只有一个操作数,表示除数,被除数则放在 edx : eax 中;得到的结果商放在 eax 中,余数放在 edx 中:
- div:无符号除
- idiv:有符号除
转移指令
- 重置条件码
e.g
选择结构机器级表示
C程序
1 |
|
使用goto
1 |
|
汇编
使用vs2022 x86反汇编
1 |
|
循环结构机器级表示
do - while
循环
C程序
1 |
|
使用goto
1 |
|
汇编
1 |
|
while
循环
1 |
|
1 |
|
1 |
|
for
循环
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
过程调用的机器级表示
使用栈实现
流程
- 程序代码和数据:主要包括 只读代码段 和 读写段(.data 和 .bss)。对所有进程来说,代码都是从固定地址开始,紧接着就是 C 语言中的全局和静态数据。其中 .data 中是已初始化的全局和静态 C 变量,而 .bss 中是未初始化的全局和静态变量。
- 堆(Heap):用于运行时的动态内存分配,向上(高地址)生长。代码和数据区,在进程开始运行时就被指定了大小;而通过调用 malloc 和 free 这样的 C 标准库函数,可以让堆区动态地扩展和收缩。
- 共享库的内存映射区:用户区的中间部分是一块内存映射区域,用来存放像 C 标准库这样的共享库。(
printf
)- 用户栈(Stack):位于虚拟地址空间用户区顶部,向下(低地址)生长。一般用来存储局部变量和函数参数,结合堆栈指针可以方便地实现函数的调用和返回。
从可执行文件加载
执行P指令
P函数局部变量入栈
调用Q函数
问题:
- 参数能够传递给Q函数
- 执行完Q函数能够返回到跳转语句的下一句
- P函数中使用的寄存器的值应该被保存
依次由如下解决方案:
在P函数的栈帧中压入参数构造区和调用指令的下一条指令
eip寄存器传入跳转的指令的位置
在Q函数的栈帧中P寄存器的值入栈
1 |
|
main
栈帧
1 |
|
EAX = 0050C008 EBX = 02636000 ECX = 0050C008 EDX = 00000001 ESI = 02636000
EDI =
028FF6BC
EIP = 00501904 ESP = 028FF5CC EBP = 028FF6BC EFL = 00000246
1 |
|
EAX =
0000001F
EBX = 02636000 ECX =00000017
EDX = 00000001 ESI = 02636000EDI = 028FF6BC EIP = 0050190C ESP =
028FF5C4
EBP = 028FF6BC EFL = 00000246
栈顶指针对应的内存地址
1 |
|
ESP = 028FF5C0

call
位置1
2
3
4
5
600501023 jmp 00501870
---
# 跳转到add函数起始位置
int add(int x, int y) {
00501870 push ebp
1 |
|
ESP = 028FF5BC
EBP = 028FF6BC

Q的栈帧开始
1 |
|
初态: ESP = 028FF5BC EBP = 028FF6BC
末态: ESP = 028FF5BC EBP = 028FF5BC
1 |
|
ESP = 028FF4F0
1 |
|
P寄存器值
EBX = 02636000 ESI = 02636000 EDI = 028FF6BC
此时:ESP = 028FF4E4
1 |
|



1 |
|
1 |
|
实践(归并排序的机器级表示)
C
1 |
|
main
1 |
|
ECX = 0x00AFF75C

printArray
1 |
|
打印第一个数:
i传递给eax
mov eax,dword ptr [ebp-8]

arr的有效地址传递给ecx
mov ecx,dword ptr [ebp+8]


取出数组的第一个数赋给edx寄存器(使用比例变址寻址)
mov edx,dword ptr [ecx+eax*4]
1 |
|
main
1 |
|
mergeSort
1 |
|
1 |
|

1 |
|
1 |
|
merge
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
1 |
|
后记
写了一整个晚上,虽然还有很多没有复习,马上的考试确实很慌,但是这篇博客真的是越写越激动!
回过头来,其实不难发现,整个汇编语言就是一双手能数的过来的常用指令反复的cv,同时也能看出这世上也没有什么尽善尽美,一来编写汇编不是一个优雅的过程,很多反复出现的代码编写;二来,即使是经过这么多年发展的编译器编译出来的汇编指令仍然有许多冗余,也不够优雅。
害!