本文翻译自 <<Windows Debugging Practical Foundations>>
第一章:内存,寄存器和简单的运算
Chapter 1: Memory, Registers and Simple Arithmetic
计算机中理想化的内存和寄存器
Memory and Registers inside an Idealized Computer
计算机的内存是由连续的内存单元组成的。每一个内存单元具有唯一的地址。每一个单元可以包含一个“数值”,我们把这个“数值”叫做在这个内存单元地址的内容。内存的访问相对于计算指令来说是比较慢的,为了提高速度,需要用内存来存储临时的结果以加速复杂的操作,这个用来临时存储的内存叫做寄存器。寄存器是独立的内存单元,寄存器的名字就是它的地址。
图1.1
Intel 32位pc 上的内存和寄存器
Memory and Registers inside Intel 32-bit PC
在这里内存位置的地址是数字组成。通常内存地址是由彼此相差4的整数组成。我们在在下图右侧显示了两个寄存器EAX和EDX。
图1.2
因为内存单元可以存储数字,所以接下来我们创建一个工程来做一个简单的运算:让电脑计算两个数字的和,我们通过这个工程来看看内存和寄存器是如何存储和如何改变他们的值的。我们给这个工程起名叫做“Arithmetic”。
“Arithmetic”项目: 内存布局和寄存器
“Arithmetic” Project: Memory Layout and Registers
在我们的这个项目中有两个内存地址:“a” 和“b”。我们可以把“a” 和 “b”想象成它们各自的地址的名称。现在我们引入一个特殊的记号标记:[a] 代表内存地址“a”中的内容。如果我们使用C和C++语言来实现这个项目,我们可以声明和定义内存地址“a” 和 “b” 如下:
static int a, b;
当我们载入程序,静态内存中的内容被初始化为“0”。下面是载入这个程序后的内存层次结构图。
图1.3
“Arithmetic”项目:一个计算机程序
“Arithmetic” Project: A Computer Program
我们可以把计算机程序想象成一系列用来操纵内存单元和寄存器的指令。比如说一个加法操作:将12号内存单元中的内容和14号内存单元中的内容相加。伪代码表示如下:
[14]:= [14] + [12]
下表左面显示的是我们的第一个程序的伪代码:
[a] := 1 [b] := 1 [b] := [b] + [a] ;[b] = 2 [b] :=[b] * 2 ;[b] = 4 |
这里我们先把每条汇编指令写成相应的伪码 |
伪码中“:=”的含义是对需要替换的内存地址中的内容进行赋值。“;” 是注释标识,分号后面是注释内容。
用高级语言写的程序代码会被编译器编译成机器语言。如果机器语言被某种助记系统表示成相应的助记符号,则机器语言也就具有了可读性。我们把这个助记系统叫做汇编语言。举个例子: INC [a],表示将内存地址a中存储的值加1。
“Arithmetic”项目:给内存地址赋值
“Arithmetic” Project: Assigning Numbers to Memory Locations
我们提到“a”代表内存单元的位置(地址),同时它也是地址 00428504的名称(参见图1.3),[a]代表存储在地址“a”中的内容。
如果我们使用C/C++语言,”a” 被称作”变量a”,对”a”赋值用C/C++语言这样写:
a = 1;
用Intel汇编语言我们这样写:
mov [a], 1
在WinDbg反汇编输出中我们可以看到如下的代码:
mov dword ptr [ArithmeticProject!a (00428504)], 1
变量”a”以!作为前缀,ArithmeticProject是可以执行文件ArithmeticProject.exe 的名字。
在下面表格右面列出了前两行伪代码对应的汇编代码:
[a] := 1 [b]:= 1 [b] := [b] + [a]; [b] = 2 [b] :=[b] * 2; [b] = 4
|
mov [a], 1 mov [b], 1 |
执行完前两行汇编指令,和图1.3比较,内存中的布局如下图1.4