Win32Asm快速教程

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

win32asm:Win32Asm快速教程
疯狂代码 / ĵ:http://Security/Article71758.html
Win32Asm快速教程 ; ;翻译:taowen
这是我Win32汇编教程它总是在创建中我会不停地添加内容通过上面next和prev链接你可以转到后面和前面页
导言
先来对这个教程做个小小介绍Win32Asm不是个非常流行编程语言而且只有为数不多(但很好)教程大多数教程都集中在编程win32部分(例如WinAPI标准Windows编程技术使用等)而不是汇编语言本身例如伪代码(opcodes)寄存器(registers)使用等虽然你能在其他教程中找到这些内容但那些教程通常是解释Dos编程它当然可以帮你学习汇编语言但在Windows中编程你不再需要了解Dos中断(errupt)和端口(port)In/Out在
Windows中WindowsAPI提供了你可在你中使用标准后面还会对此有更多内容这份教程目是在解释用汇编编Win32同时学习汇编语言本身
1.0-介绍汇编语言
汇编语言是创造出来代替原始只能由处理器理解 2进制代码很久以前尚没有任何高级语言都是用汇编写汇编代码直接描述处理器可以执行代码例如:
add eax,edx
add这条指令把两个值加到起eax和edx被称为寄存器它们可以在处理器内部保存值这条代码被转换为66 03
c2(16进制)处理器阅读这行代码并执行它所代表指令像C这样高级语言把它们自己语言翻译为汇编语言而汇编器又把它转换为 2进制代码:
C 代码
a = a + b; >> C编译器 >> 汇编语言
add eax, edx >>汇编器>> 原始输出(十 6进制)
66 03 C2
(注意该处汇编语言代码被简化了实际输出决定于C代码上下文)
1.1-为什么?(Why?)
既然用汇编写很困难那么为什么你用A汇编而不是C或者别什么??-汇编产生更小而且更快在像如有人工智能般非常高级编程语言中编译器要产生输出代码比起汇编来更困难虽然编译器变得越来越好编译器仍然必须指出最快(或最小)方式产生汇编代码而且你自己来写(汇编)代码(包括可选代码优化)能生成更小更快代码但是当然这
比使用高级语言难多了还有另个和某些使用运行时dll高级语言区别地方它们在大多数时运行良好但有时由于dll(dll hell)而产生问题用户总是要安装这些Dll对于Visual C这不是个问题它们是和Windows同安装而Visual Basic甚至不把自己语言转换为汇编语言(虽然5以及更高版本进行了些这样转换但不完全)它高度依赖
msvbvm50.dll-Visual Baisc虚拟机由VB产生exe文件仅仅存在简单代码和许多对这些dll这就是vb慢原因汇编是所有中最快它仅仅用系统dll如Kernel32.dll, User32.dll等
译者注:dll hell是指由于dll新版本被旧版本给代替了由于使用了dll新版本仍然新导致了致命
另个误解是许多人认为汇编不可能用来编程当然它难但不是不可能用汇编创建大工程确很难我只是用它来写小用于需要速度代码被写在能被其他语言导入dll中而且Dos和Windows还有个很大区别Dos把中断当“”用像中断10用于显示中断13用于文件存储等在Windows中API只有名字(比如MessageBox, CreateWindowsEx)你能导入库(DLL)并使用其中这使得用asm写简单多了你将在下章中学习更多有关这方面知识
2.0-开始前准备
介绍已经够多了现在让我们开始吧要用汇编写你需要些工具下面你能看到我将在本教程中使用哪些工具我建议你安装同样工具这样你能跟着教程试验文中例子我也给出其他些选择虽然其中大部分你都可以选择但是要警告是在汇编器(masmtasm和nasm)中有很大区别在这个教程中将使用masm它有许多很有用功能(例如invoke)它使得编程更容易当然你可以自己选择你更喜欢汇编器但这将使你跟着教程走难些而且你不得不把教程中例子进行转换使它可以在你用汇编器中运行
汇编器
我选择:Masm(在win32asm包中)
网址:
描述:个把伪代码(opcodes)翻译为给处理器读原始输出(object文件)汇编器
相关内容:Masm宏(macro)汇编器是个有很多有用特色汇编器像“invoke”它可以简化对API并对数据类型进行检查你将在本教程后面学习这些如果你读了上面文字你就知道本教程推荐使用masm
供选择:Tasm[dl],nasm[dl]
链接器
我选择:微软Incremental链接器(link.exe)
网址:(在win32asm包中)
描述:链接器把目标(object)文件和库文件(用于导入DLL中)“链接”到起输出最终可执行文件
有关:我用IczelionWin32asm包中link.exe但大多数链接器都可以用
供选择:Tasm linker[dl]
资源编辑器
我选择:Borland Resource Workshop
网址:
描述:用于创建资源(图形对话框位图菜单等)资源编辑器
有关:大多数编辑器都行我个人爱好是resource workshop但你可以用你喜欢注意由于resource workshop创建资源文件有时给资源编译带来麻烦如果你想使用这个编辑器你应当把tasm起下下来他里面包含了用于编译borland式资源brc32.exe
供选择:Symantec资源编辑器Resource Builder等等
文本编辑器
我选择:ultraedit
网址:
描述:个文本编辑器需要介绍说明吗?
有关:文本编辑器选择是十分个性化我非常喜欢ultraedit你可以下载我为ultraedit写语法文件它可以使汇编代码语法高亮但至少选个支持语法高亮文本编辑器(关键字会自动标色)这非常有用而且它使你代码更容易读和写Ultraedit还有个可以使你在代码中快速跳转到某个列表
供选择:数百万文本编辑器中个
参考手册
我选择:win32员参考手册
网址:(或搜索互联网)
描述:你需要参考些API使用方法最重要是“win32员参考手册”(win32.hlp)这是个大文件大约24mb(些版本是12mb但不全)在这个文件中对所有系统dll(kernelusergdishell等)都做了介绍说明你至少需要这个文件其他参考(sock2.hlp, mmedia.hlp, ole.hlp等)也是有帮助但不定需要
供选择:N/A
(译者注:该教程写成较早现在有极好MSDN供选择)
2.1-安装工具
现在你已经得到这些工具了把它们安装到你硬盘某个角落吧这有几个值得注意地方:
把masm包安装到你打算写汇编源那个分区这保证了包含文件路径正确性把masm(和tasm)bin目录加到autoexec.batpath中并重新启动
如果你用是ultraedit使用你可以在前面下载语法文件并启用function-listview(列表视图)
2.2-为你源文件准备目录
在某个地方创建个win32文件夹(或其他你喜欢名字)并为你创建每个工程创建个子文件夹
3.0-汇编基础知识
这章将教你汇编语言基础知识
3.1-伪代码(opcodes)
汇编是用伪代码创建个伪代码是条处理器可以理解指令例如:
ADD
Add指令把两个数加到起大部分伪代码有参数
ADD eax, edx
ADD有两个参数在加法情况下个源个目标它把源值加到目标值中并把结果保存在目标中参数有很多区别类型:寄存器内存地址直接数值(immediate values)参见下文
3.2-寄存器
有几种大小寄存器:8位16位32位(在MMX处理器中有更多)在16位中你仅能使用16位和8位寄存器在32位中你可以使用32位寄存器
些寄存器是别寄存器部分:例如如果EAX保存了值EA7823BBh这里是其他寄存器值
EAX EA 78 23 BB
AX EA 78 23 BB
AH EA 78 23 BB
AL EA 78 23 BB
axahal是eax部分eax是个32位寄存器(仅在386以上存在)ax包含了eax低16位(2字节)ah包含了ax高字节而al包含了ax低字节因而ax是16位al和ax是8位在上面例子中,这些是那些寄存器值:
eax = EA7823BB (32-bit)
ax = 23BB (16-bit)
ah = 23 (8-bit)
al = BB (8-bit)
使用寄存器例子(不要管那些伪代码只看寄存器介绍说明)
mov eax, 12345678h
;Mov把个值载入寄存器(注意:12345678h是个十 6进制值h这个后缀
mov cl, ah
;把ax高字节移入cl
sub cl, 10
;从cl值中减去10(十进制)
mov al, cl
;并把cl存入eax最低字节
让我们来分析上面代码:
mov指令可以把个值从寄存器内存和直接数值移入另个寄存器在上面例子中eax包含了12345678h然后ah值(eax左数第 3个字节)被复制入了cl中(ecx寄存器最低字节)然后cl减10并移回al中(eax最低字节)
寄存器区别类型:
全功能(General Purpose)
这些32位(它们组成部分为16/8位)寄存器可以用来做任何事情:
eax (ax/ah/al) 加法器
ebx (bx/bh/bl) 基(base)
ecx (cx/ch/cl) 计数器
edx (dx/dh/dl) 数据
虽然它们有名字但是你可以用它们做任何事
段(Segment)寄存器
段寄存器定义了哪段内存被使用你可能在win32asm中用不着它们windows有个平坦(flat)内存系统在Dos中内存被分为64kb段因而如果你想要定个内存地址你指定个段并用个off(偏移址)(像0172:0500(segment:off))在windows中段有4GB大小所以你在Windows中不需要段段总是16位寄存器
CS 代码段
DS 数据段
SS 栈段
ES 扩展段
FS (only 286+) 全功能段
GS (only 386+) 全功能段
指针寄存器
实际上你可以把指针寄存器当作全功能寄存器来使用(除了eip)只要你保存并恢复它们原始值指针寄存器的所以这么叫是它们经常被用来存储内存地址些伪代码(movbscasb等)也要用它们
esi (si) 源索引
edi (di) 目标索引
eip (ip) 指令指针
eip(在16位编程中为ip)包含了指向处理器将要执行下条指令指针因而你不能把eip当作全功能寄存器来用
栈寄存器
有2个栈寄存器:esp和ebpesp装有内存中当前栈位置(在下章中对此有更多内容)Ebp在中被用成指向局部变量指针
esp (sp) 栈指针
ebp (bp) 基(base)指针
4.0-内存
这部分将解释在Windows中内存是如何被管理
4.1-Dos和Win3.xx
在运行于Dos和Win3.xx16位中内存被分成许多个段这些段大小为64kb为了存储内存需要个段指针和个偏移址指针段指针标明要使用是哪个段off(偏移址)指针标明在段位置看下图:
内存
段 1 (64kb) 段 2 (64kb) 段 3 (64kb) 段 4(64kb) 更多
注意下面有关16位解释后面有更多有关32位内容(但不要跳过这部分要理解32位内存管理这部分很重要)上表是全部内存被划分成了多个64kb段最多有65536个段现在取出段:
段 1(64kb)
Off 1 Off 2 Off 3 Off 4 Off 5 更多
为了指向段中位置需要使用off个off是段内部个位置每个段最多有65536个off内存中地址记法是:
SEGMENT:OFFSET
例如:
0030:4012(均为16进制)
它意思是:段30off4012为了查看那个地址中有什么你先要到段30然后到该段off4012在前章中你已经学过了段和指针寄存器例如段寄存器有:
CS 代码段
DS 数据段
SS 栈段
ES 扩展段
FS (only 286+) 全功能段
GS (only 386+) 全功能段
顾名思义:代码段(CS)包括了当前代码执行到了哪部分数据段是用来标明在哪段中取出数据栈指栈段(后面有更多)ESFS, GS是全功能寄存器并且可以用于任何段(虽然在Windows中不是如此)
指针寄存器大多数时装有off但全功能寄存器(ax, bx, cx, dx等)也可以这么用IP标明当前指令执行到了哪个offSp保存了当前栈在ss(栈段中)off
4.2-32位Windows
你可能已经注意到了有关段切是乏味在16位编程中段是必不可少幸运是这个问题已经在32位Windows(95及以上)中得到解决你仍然有段但不用管他们了它们不再是64kb而是4GB你如果尝试着改变段寄存器中个
windows甚至会崩溃这称为平坦(flat)内存模式只有off而且是32位因而范围从0到4,294,967,295内存中每个地址都是用off表示这真是32位胜于16位最大优点所以你现在可以忘了段寄存器并把精神集中在其他寄存器上
5.0-伪代码
伪代码是给处理器指令它实际上是原始十 6进制代码可读版因此汇编是最低级编程语言汇编中所有东西被直接翻译为十 6进制码换句话说你没有把高级语言翻译为低级语言编译器上烦恼汇编器仅仅把汇编代码转化为原始数据
本章将讨论些用来运算位操作等伪代码还有跳转指令比较等伪代码在后面介绍
5.1-些基本计算伪代码
MOV
这条指令用来把个地方移往(事实上是复制到)另个地方这个地方可以是寄存器内存地址或是直接数值(当然只能作为源值)Mov指令语法是:
mov 目标源
你可把个寄存器移往另个(注意指令是在复制那个值到目标中尽管“mov”这个名字是移意思)
mov edx, ecx
上面这条指令把ecx内容复制到了ecx中源和目标大小应该致例如这个指令是非法:
mov al, ecx;非法
这条伪代码试图把个DWORD(32位)值装入个字节(8位)寄存器中这不能个由mov指令来完成(有其他指令干这事)但这些指令是允许源和目标在大小上并没有什么区别:
mov al, bl
mov cl, dl
mov cx, dx
mov ecx, ebx
内存地址由off指示(在win32中前章中有更多信息)你也能从地址某个地方获得个值并把它放入个寄存器中下面有个例子:
off 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42
data 0D 0A 50 32 44 57 25 7A 5E 72 EF 7D FF AD C7
每个块代表个字节
off值这里是用字节形式表示但它事实上是32位值比如3A(这不是个常见off值但如果不这样简写表格装不下)这也是个32位值:0000003Ah只是为了节省空间使用了些不常见低位off所有值均为16进制
看上表off 3A那个off数据是25, 7A, 5E, 72, EF等例如要把这个位于3A值用mov放入寄存器中:
mov eax, dword ptr[0000003Ah]
(h后缀表明这是个十 6进制值)
mov eax, dword ptr[0000003Ah]这条指令意思是:把位于内存地址3ADWORD大小值放入eax寄存器执行了这条指令后eax包含了值725E7A25h可能你注意到了这是在内存中时反转结果:25 7A 5E 72这是存储在内存中值使用了little endian格式这意味着越靠右字节位数越高:字节顺序被反转了我想些例子可以使你把这个搞清楚
十 6进制dword(32位)值放在内存中时是这样:40, 30, 20, 10(每个值占个字节(8位))
十 6进制word(16位)值放在内存中时是这样:50, 40
回到前面例子你也可以对其他大小值这么做:
mov cl, ptr [34h] ; cl得到值0Dh(参考上表)
mov dx, word ptr [3Eh] ; dx将得到值 7DEFh (看上表记住反序)
大小有时不是必须
Mov eax,[00403045h]
eax是32位寄存器编译器假定(也只能这么做)它应该从地址403045(十 6进制)取个32位值
可以直接使用数值:
mov edx, 5006
这只是使得edx寄存器装有值5006综括号[和]用来从括号间内存地址处取值没有括号就只是这个值寄存器和内存地址也可以(他应该是32位中32位寄存器):
mov eax,403045h;使eax装有值403045h(十 6进制)
mov cx,[eax];把位于内存地址eaxword大小值(403045)移入cx寄存器
在mov cx, [eax]中处理器会先查看eax装有什么值(=内存地址)然后在那个内存地址中有什么值并把这个word(16位目标-cx-是个16位寄存器)移入cx
ADD, SUB, MUL, DIV
许多伪代码做计算工作你可以猜出它们中大多数名字:add(加)sub(减)mul(乘)div(除)等
Add伪代码有如下语法:
Add 目标源
执行运算是 目标=目标+源下面格式是允许
目标 源 例子
Register Register add ecx, edx
Register Memory add ecx, dword ptr [104h] / add ecx, [edx]
Register Immediate value add eax, 102
Memory Immediate value add dword ptr [401231h], 80
Memory Register add dword ptr [401231h], edx
这条指令非常简单它只是把源值加到目标值中并把结果保存在目标中其他数学指令有:
sub 目标源(目标=目标-源)
mul 目标源(目标=目标×源)
div 源(eax=eax/源edx=余数)
减法和加法样做乘法是目标=目标×源除法有点区别寄存器是整数值(注意绕回数不是浮点数)除法结果被分为商和余数例如:
28/6->商=4余数=4
30/9->商=3余数=3
97/10->商=9余数=7
18/6->商=3余数=0
现在取决于源大小商(部分)被存在eax中余数(部分)在edx:
源大小 除法 商存于 余数存于
BYTE (8-bits) ax / source AL AH
WORD (16-bits) dx:ax* / source AX DX
DWORD (32-bits) edx:eax* / source EAX EDX
*:例如如果dx=2030h而ax=0040hdx:ax=20300040hdx:ax是个双字值其中高字代表dx低字代表
axEdx:eax是个 4字值(64位)其高字是edx低字是eax
Div伪代码源可以是
an 8-bit register (al, ah, cl,...)
a 16-bit register (ax, dx, ...)
a 32-bit register (eax, edx, ecx...)
an 8-bit memory value ( ptr [xxxx])
a 16-bit memory value (word ptr [xxxx])
a 32-bit memory value (dword ptr [xxxx])
源不可以是直接数值处理器不能决定源参数大小
位操作
这些指令都由源和目标除了“NOT”指令目标中每位和源中每位作比较并看是那个指令决定是0还是1放入目标
位中
指令 AND OR XOR NOT
源位 0 0 1 1 0 0 1 1 0 0 1 1 0 1
目标位 0 1 0 1 0 1 0 1 0 1 0 1 X X
输出位 0 0 0 1 0 1 1 1 0 1 1 0 1 0
如果源和目标均为1AND把输出位设为1
如果源和目标中有个为1OR把输出位设为1
如果源和目标位不样XOR把输出位设为1
NOT反转源位
个例子:
mov ax, 3406
mov dx, 13EAh
xor ax,dx
ax=3406(十 6进制)是 2进制0000110101001110
dx=13EA(十 6进制)是 2进制0001001111101010
对这些位进行xor操作:
源 0001001111101010 (dx)
目标 0000110101001110 (ax)
输出 0001111010100100 ( ax)
新dx是0001111010100100 (十进制7845, 十 6进制1EA4)
另个例子:
mov ecx, FFFF0000h
not ecx
FFFF0000在 2进制中是11111111111111110000000000000000(16个116个0)如果反转每位会得到
00000000000000001111111111111111(16个016个1)在十 6进制中是0000FFFF因而执行NOT操作后ecx是0000FFFFh
步增/减
有两个很简单指令DEC和INC这些指令使内存地址和寄存器步增或步减就是这样:
inc reg -> reg = reg + 1
dec reg -> reg = reg - 1
inc dword ptr [103405] -> 位于103405值步增
dec dword ptr [103405] -> 位于103405值步减
NOP
这条指令什么都不干它仅仅占用空间和时间它用作填充或给代码打补丁目
移位(Bit Rotation 和 shiting)
注意:下面大部分例子使用8位数但这只是为了使目清楚
Shting
SHL 目标计数(count)
SHR 目标计数(count)
SHL和SHR在寄存器内存地址中像左或向右移动定数目(count)位
例如:
;这儿al=01011011( 2进制)
shr al, 3
它意思是:把al寄存器中所有位向右移 3个位置因而al会变成为00001011左边字节用0填充而右边字节被移出最后个被移出位保存在carry-flag中Carry-flag是处理器标志寄存器位它不是像eax或ecx样你可以访问寄存器(虽然有伪代码干这活)但它值决定于该指令结构它(carry-flag)会在后面解释你要记住唯件事是carry是标志寄存器位且它可以被打开或者关闭这个位等于最后个移出位
shl和shr样只不过是向左移
;这儿bl=11100101( 2进制)
shl bl, 2
执行了指令后bl是10010100( 2进制)最后两个位是由0填充carry-flag是1最后移出位是1
还有两个伪代码:
SAL 目标, 计数(算术左移)
SAR 目标, 计数(算术右移)
SAL和SHL样但SAR不完全和SHR样SAR不是用0来填充移出位而是复制MSB(最高位)例如:
al = 10100110
sar al, 3
al = 11110100
sar al, 2
al = 11101001
bl = 00100110
sar bl, 3
bl = 00000100
Rotation(循环移动)
Rol 目标计数;循环左移
Ror 目标计数;循环右移
Rcl 目标计数;通过carry循环左移
Rcr 目标计数;通过carry循环右移
循环移动(Rotation)看上去就像移(Shting)只是移出位又到了另边
例如:ror(循环右移)
Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0
移位的前 1 0 0 1 1 0 1 1
循环移位, 计数= 3 1 0 0 1 1 0 1 1 (被移出)
结果 0 1 1 1 0 0 1 1
如你在上图所见位循环了注意每个被推出位又移到了另边和Shting样carry位装有最后被移出位Rcl和Rcr实际上和RolRcr样它们名字暗示了它们用carry位来表明最后移出位但和Rol和Ror干同样事情它们没有什么区别
交换
XCHG指令也非常简单它同在两个寄存器和内存地址的间交换:
eax = 237h
ecx = 978h
xchg eax, ecx
eax = 978h
ecx = 237h
6.0-文件结构
汇编源文件被分成了几个部分这些部分是codedata未化dataconstantsresource和relocations资源部分是资源文件创建后面会有更多讨论Relocation部分对我们不重要(它包含了使PE-loader可以在内存区别位置装载入信息)重要部分是codedata未化data和constants可能你已经猜到code部分包含了代码Data装有数据并有读写
权限整个data部分被包括在exe文件并可以用数据化
未化data在启动时没有内容甚至没有包括在exe文件本身它只是由Windows“保留”部分内存这部分也有读写权限Constants和data部分样但只读虽然这部分可用作常数但把常数定义在包含文件中更简单也更快捷并用作直接数值
6.1-代表各部分符号
在你源文件(*.asm)中你可以用部分标识符定义各部分:
.code;代码部分由此开始
.data;数据部分由此开始
.data?;未化数据部分由此开始
.const;常量部分由此开始
可执行文件(*.exe,*.dll和其他)是(在win32中)可移植执行格式(PE)我不会详细讨论它但是有几点是重要部分(Sections)些属性定义在PE头中:
Section名RVAoff原始大小虚拟大小和标志Rva(相对虚拟地址)是将要装入section部分相对内存地址这里相对意思是相对于载入基地址这个地址也在PE头中但可以由PE-loader改变(使用relocation部分)Off是化数据所在exe文件本身原始off虚拟大小是在内存中将达到大小标志是读/写/可执行等
6.2-例子
这有个举例:
.data
Number1 dd 12033h
Number2 dw 100h,200h,300h,400h
Number3 db "blabla",0
.data?
Value dd ?
.code
mov eax, Number1
mov ecx, off Number2
add ax, word ptr [ecx+4]
mov Value, eax
这个不能编译但没关系
在你汇编中你放入“部分”中所有东西都会进入exe文件而且当被载入内存时位于某个内存地址在上面data部分有3个标签:Number1, Number2, Number3这些标签会保存它们在中off因而你可以在你中使用它们来指示位置
DD直接把个dword放在那DW是Word而DB是你也可以用db放串它实际上是串值在例子中data部分会变成内存中这样:
33,20,01,00,00,01,00,02,00,03,00,04,62,6c,61,62,6c,61,00(均为十 6进制值)
(每个值位)
我给其中些数字上了色Number1指向 33所在内存地址Number2指向红色00位置Number3是绿色62现在如果你在你中这么写:
mov eax, Number1
它实际意为:
mov ecx, dword ptr[12033h所在内存地址]
但这样:
mov ecx, off Number1
意为:
mov ecx, 12033h所在内存地址
在第个例子中ecx会得到Number1内存地址值在第 2个中ecx会称为内存地址(off)本身下面两个例子有相同效果:
(1)
mov ecx, Number1
(2)
mov ecx, off Number1
mov ecx, dword ptr [ecx] ( or mov ecx, [ecx])
现在让我们回到前面例子中:
.data
Number1 dd 12033h
Number2 dw 100h,200h,300h,400h
Number3 db "blabla",0
.data?
Value dd ?
.code
mov eax, Number1
mov ecx, off Number2
add ax, word ptr [ecx+4]
mov Value, eax
标签可以使用像Number1Number2和Number3等值但它启动时包含0它在未化data部分这样优点是你在
.data?中定义所有东西不在可执行文件中而在内存中
.data?
ManyBytes1 db 5000 dup (?)
.data
ManyBytes2 db 5000 dup (0)
(5000dup意为:5000个副本值db 4,4,4,4,4,4,4和值db 7dup(4)样)
ManyBytes1不会在文件本身只是5000个预分配在内存中字节但Manys2会在可执行文件中使文件变大5000个字节虽然你文件会包含5000个零但并没有什么用
Code部分被汇编(翻译为原始代码)并放入可执行文件中去(当然载入后在内存中)
7.0-条件跳转
在Code部分你可以看到像这样标签:
.code
mov eax, edx
sub eax, ecx
cmp eax, 2
jz loc1
xor eax, eax
jmp loc2
loc1:
xor eax, eax
inc eax
loc2:
(xor eax, eax意为:eax=0)
让我们来看看这些代码:
mov eax, edx;把edx放入eax中
sub eax, ecx;eax-ecx
cmp eax, 2
这有条新指令:cmpCmp意为compare(比较)它能比较两个值(寄存器内存直接数值)并设置Z-flag(零标志)零标志很像carry也是内部标志寄存器位
Jz loc1
这也是条新它是条件跳转指令Jz=jump zero(如果设置了零标志就跳转)Loc1是个标记指令“xor eax,eax|inc eax”内存开始处off标签因而jz loc1=如果设置了零标志跳往位于loc1指令
Cmp eax, 2;如果eax=2设置零标志
Jz loc1;如果设置了零标志就跳转
=
如果eax等于2跳往位于loc1指令
然后有jmp loc2.这也好似个跳转但是是个无条件跳转:它总是执行上面代码就是:
((edx-ecx)2)
{
eax = 1;
}
{
eax = 0;
}
或者Basic版:
IF (edx-ecx)=2 THEN
EAX = 1
ELSE
EAX = 0
END IF
7.1-标志寄存器
标志寄存器有套标志它们设不设置取决于计算或其他时间我不会讨论它们全部只拣几个重要说:
ZF(零标志) 当计算结果是零时该标志被设置(compare实际上是只设置标志不保存结构减法)
SF(符号标志) 结果为负就设置
CF(carry标志) Carry标志中存放计算后最右位
OF(溢出标志) 标明个溢出了计算如结构和目标不匹配
还有更多标志(Parity, Auxiliary, Trap, Interrupt, Direction, IOPL, Nested Task, Resume, & Virtual Mode)但我们不用它们所以我不解释
7.2-跳转系列
有整套条件跳转而且它们跳转和否均取决于标志状态但由于大部分跳转指令有明白名字你甚至无需知道哪个标志要设置例如:“如果大于等于就跳转”(jge)和“符号标志=溢出标志”样而“如果零就跳转”和“如果零标志=1就跳转”样
在下表中“意思”指是什么样计算结果该跳转“如果大于就跳转”意为:
cmp x, y
jmp 如果 x 比 y大
伪代码 意思 条件
JA Jump above CF=0 & ZF=0
JAE Jump above or equal CF=0
JB Jump below CF=1
JBE Jump below or equal CF=1 or ZF=1
JC Jump carry CF=1
JCXZ Jump CX=0 register CX=0
JE (is the same as JZ) Jump equal ZF=1
JG Jump greater (signed) ZF=0 & SF=OF
JGE Jump greater or equal (signed) SF=OF
JL Jump less (signed) SF != OF
JLE Jump less or equal (signed) ZF=1 or SF!=OF
JMP Unconditional Jump -
JNA Jump not above CF=1 or ZF=1
JNAE Jump not above or equal CF=1
JNB Jump not below CF=0
JNBE Jump not below or equal CF=1 & ZF=0
JNC Jump not carry CF=0
JNE Jump not equal ZF=0
JNG Jump not greater (signed) ZF=1 or SF!=OF
JNGE Jump not greater or equal (signed) SF!=OF
JNL Jump not less (signed) SF=OF
JNLE Jump not less or equal (signed) ZF=0 & SF=OF
JNO Jump not overflow (signed) OF=0
JNP Jump no parity PF=0
JNS Jump not signed (signed) SF=0
JNZ Jump not zero ZF=0
JO Jump overflow (signed) OF=1
JP Jump parity PF=1
JPE Jump parity even PF=1
JPO Jump paity odd PF=0
JS Jump signed (signed) SF=1
JZ Jump zero ZF=1
所有跳转指令需要个参数:要跳往off
8.0-有关数些事情
在大多数编程语言中使用整数还是浮点数只取决于变量声明在汇编语言中完全区别浮点数计算是由特别伪代码和FPU协处理器(浮点单元)完成浮点指令将会在后面讨论先来看看些有关整数事情在c语言中有signed(有符号)整数和unsigned(无符号)整数Signed是意为数有符号(+或-)Unsigned总是正找出下表中区别(再次这是个例子它在其他大小时也同样工作)
值 00 01 02 03 ... 7F 80 ... FC FD FE FF
无符号意义 00 01 02 03 ... 7F 80 ... FC FD FE FF
有符号意义 00 01 02 03 ... 7F -80 ... -04 -03 -02 -01
因此在有符号数中个被分为两段:0~7F用于正值80~FF用于负值对于dword值它也样:0~7FFFFFFFh为正80000000~FFFFFFFFh为负正如你可能已经注意到样负值最高位有个集合它们比80000000h大这位被称为符号位
3.1-有符号或无符号?
你和处理器都不能看出个值是signed还是unsigned好消息是对于加法和减法来说个数是signed还是unsigned没有关系
计算:-4+9
FFFFFFFC+00000009=00000005(这是对)
计算:5-(-9)。

相关文档
最新文档