处理器执行xor指令时间xor eax eax后,标志Cf.Zf.of和pf状态依次是什么

单步走遇到CALL就进
单不走,遇到CALL鈈进
直到出现RETxor指令时间时断下
若进入系统模块领空此命令可回到用户进程领空
    • 内存访问断点(对内存执行、写入等都算访问。执行即把機器码当成程序执行时被断下;写入即改写这部分机器码时被断下)
    • 内存写入断点(对内存写入)
    • 硬件执行断点(运行到指定代码处触发斷点)
    • 硬件访问断点(对内存执行、写入等都算访问执行即把机器码当成程序执行时被断下;写入即改写这部分机器码时被断下)
    • 硬件寫入断点(对内存写入)
数据窗口十六进制字节格式查看指定地址
数据窗口 UNICODE 格式查看指定地址
  1. 大部分函数的返回值基本上都在eax里
  2. 如何判断CALL囿多少个参数?
  • 看CALL前面的有几个PUSH假设有3个,就是有3个参数
  • 进入CALL看CALL尾部retnxor指令时间 是几,假设是:retn 4 说明该CALL有1个参数假设retn 0c 说明有3个参数,0c(┿六进制)=12(10进制)之所以4为一个参数,是因为1个PUSH 占用4字节
    Cdecl调用约定:先判断出CALL有几个参数,平衡堆栈就是用add esp,参数个数*4 16进制格式
    StdCall调用约定或Pascal調用约定:先判断出CALL有几个参数平衡堆栈是函数内部用ret 参数个数*4 16进制格式

对汇编设立不变OD汇编死码与二进制死码

因为游戏更新基址,较夶的偏移会变化为了方便,
所以设立OD死码方便快速获取游戏更新后的相关数据。
什么是OD汇编死码与OD二进制死码

OD汇编死码指的就是OD反彙编中游戏更新后也不变的汇编
OD二进制死码指的就是OD反汇编中游戏更新后也不变的二进制
如何设立OD汇编死码?
条件:最多8条汇编因為OD里ctrl+s搜的时候最多只能8条
哪些汇编不能用于OD死码:

如何设立OD二进制死码?
根据上面不能用于死码的情况来设立二进制死码:
具体设立是:ODΦ选中一块汇编右键-二进制-二进制复制。


IA-32架构中一共有4个32位寄存器用于保存临时数据,它们分别是EAX、EBX、ECX和EDX
这4个32位寄存器的通用寄存器名字前面都有一个“E”字母,含义是“Expand”扩展这是由于在16位的时代,这4个通用寄存器的名字是AX、BX、CX和DX到了32位后就在它们的名字前面加个“E”来区别是32位还是16位
这4个32位的通用寄存器可以当作16位使用,也可以当作8位使用当作8位使用时,就将AX折开为AH和ALAH中的“H”代表“high”,意思是高位的意思AL中的“L”代表“low”,意思是地位的意思同理,BX、CX和DX可折开为BH、BL、CH、CL、DH、DL来使用


  1. EAX寄存器:EAX称为累加器,常用于算數运算、布尔操作、逻辑操作、返回函数结果等

  2. EBX寄存器:EBX称为基址寄存器,常用于存放内存地址

  3. ECX寄存器:ECX称为计数寄存器,常用于存放循环语句的循环次数字符串操作中也常用。

  4. EDX寄存器:称为数据寄存器常常和EAX一起使用,例如MUL命令结果超过32位则高32位使用EDX寄存器保存,EAX寄存器则保存低32位

注意:上面所述的4个通用寄存器的专门用途不是一成不变的,编译器在编译程序的时候会根据很多因素例如编譯器、编译条件、操作系统等做出相应的改变,读者要知道着手研究的程序是用什么编译器编译然后针对具体的编译器参考该编译器的說明。

顾名思义变址的含义是内存地址会变动的,也就是说变址寄存器中存放在变动的内存地址80386架构中有两个变址寄存器,分别是ESI和EDI

  1. ESI:ESI称为源变址寄存器,通常存放要处理的数据的内存地址

  2. EDI:EDI称为目的变址寄存器,通常存放处理后的数据的内存地址

ESI和EDI寄存器的用途:
ESI和EDI常用来配合使用完成数据的赋值操作,下面是一个ESI和EDI配合使用的例子

意思就是将ESI指向的地址的值以4字节方式拷贝到EDI指向的地址中,重复执行ECX次每次执行后ESI+4,EDI+4ECX-1,OD中在这段代码中下断后按F7单步步入就可以观察到这3个寄存器的变化

80386的指针寄存器有基址寄存器EBP堆栈指針寄存器ESP和xor指令时间指针寄存器EIP。只需要了解基址寄存器EBP和堆栈指针寄存器ESP即可xor指令时间指针寄存器EIP总是指向下一条要执行的xor指令时间嘚地址,一般情况下无需修改EIP

  1. EBP:EBP称为基址寄存器,可作为通用寄存器用于存放操作数常用来代替堆栈指针访问堆栈中的数据

  2. ESP:ESP称为堆棧指针寄存器,不可作为通用寄存器使用ESP存放当前堆栈栈顶的地址,一般情况下ESP和EBP联合使用来访问函数中的参数和局部变量

EBP和ESP寄存器嘚用途:
EBP和ESP常配合使用完成堆栈的访问,下面是一段常见的堆栈访问xor指令时间

标志寄存器EFLAGS一共有32位,在这32位中大部分是保留和给编写操莋系统的人用的一般情况下只需知道32位的低16位中的8位即可,下图列出了标志寄存器EFLAGS中需要了解的8个位的位置

  • DF (Direction Flag):方向标志,在串处理xor指囹时间中控制信息的方向
  • SF (Sign Flag):符号标志,结果为负时置1否则置0。
  • PF (Parity Flag):奇偶标志结果操作数中1的个数为偶数时置1,否则置0

EFLAGS寄存器的用途:
正如上面所说EFLAGS是实现条件判断和逻辑判断的一种机制,在汇编语言中一般不直接访问EFLAGS寄存器而是通过xor指令时间的操作隐含访问EFLAGS寄存器,下面是一个利用EFLAGS寄存器的例子

jz ; 如果ZF等于1,则跳转到

作用:通常用于处理变量

作用:地址在寄存器中。

作用:常用于访问数组和结构

(6)相对基址加变址寻址。

作用:常用于访问结构

数据结构与80386寻址方式的关系

MOVxor指令时间将源操作数SRC传送到目的操作数DST中,
传送的数据格式可以是8字节、16字节和32字节

XCHG:称为交换xor指令时间,XCHG实现寄存器间和内存间的数据交换
交换的数据可以是8字节、16字节和32字节,其中SRC和DST必須格式相同

PUSHxor指令时间和POPxor指令时间是匹配出现的,上面的代码有多少个PUSH下面的代码就有多少个POP否则堆栈就会不平衡。

PUSHxor指令时间将源操作數SRC压入堆栈同时ESP-4,而POP恰恰相反POPxor指令时间从堆栈的顶部弹出4字节的数值然后放入DST。在32位的Windows操作系统上PUSH和POPxor指令时间的操作是以4字节为单位的。

PUSH和POPxor指令时间常用于向函数传递参数

80x86有3条地址传送xor指令时间,分别是LEALDS和LES。其实LDS和LESxor指令时间和段寄存器有关在32位的Windows操作系统上,┅般的程序员都不需要管理段寄存器所以相对而言,LDS和LES寄存器使用得比较少一般情况下常见的只有LEAxor指令时间。
LEA:称为地址传送xor指令时間格式是LEA DST,ADDR。LEAxor指令时间将ADDR地址加载到DST其中ADDR可以是内存,也可以是寄存器而DST必须是一个通用寄存器。
LEAxor指令时间相当于C语言中的“&”and操作苻需要注意的是LEA和MOV是不同的,前者传送的是地址后者传送的是操作数。

还有一点需要注意LEAxor指令时间可用于算法运算

ADD EAX,ESI //将EAX寄存器的值加仩ESI寄存器的值,得出的结果保存在EAX寄存器中
ADD EBX,DWORD PTR [] //将EBX寄存器的值加上内存地址为所指的4字节值,得出的结构保存在EBX寄存器中其中DWORD PTR的意思是显礻说明按多少字节来操作,DWORD是DOUBLE WORD的缩写也就是两个WORD的意思。
SUB ECX,4H //将ECX寄存器的值减去4H得出的结果保存在EAX寄存器中。
SUB BYTE PTR[EAX],CH //将内存地址为EAX所指向的数据按字节为单位和CH寄存器相减得出的结果按字节为单位保存在EAX所指向的地方。

INCxor指令时间将操作数OPER加1得出的结果保存在OPER中。

INC EAX //将EAX寄存器的值加1得出的结果存放在原来的地方。

称为减1xor指令时间格式是EDC OPER
DECxor指令时间将操作数OPER减1得出的结果保存在OPER中。

DEC EDX //将EDX寄存器的值减1得出的结果存放在原来的地方

CMPxor指令时间将OPER1减去OPER2,得出的结果不保存只是相应地设置寄存器EFLAGS的CF、PF、ZF、AF、SF和OF。也就是说可以通过测试寄存器EFLAGS相关的标誌的值得知CMPxor指令时间执行后的结果

CMP EAX,56H //将EAX寄存器的值减去56H,得出的结果不保存并且设置寄存器EFLAGS相关的标志位。

NEGxor指令时间将OPER操作数按位取反简而言之就是将零减去OPER操作数,得出的结果保存在OPER自身中
在计算机的CPU中没有减法的机制,减法是用加法实现的例如:100-55这个操作,CPU实際执行的是100+(-55)而-55相当于是求55的相反数,求相反数的时候NEGxor指令时间正好派上用场了

NEG EAX //将EAX寄存器的值取反,得出的结果保存在EAX中
NEG WORD PTR [] //将内存地址为所指向的数据以WORD为单位取反得出的结果以WORD为单位,保存在内存地址为

MUL:称为无符号乘法xor指令时间格式是MUL OPER
MULxor指令时间隐含了一个参加运算的操作数EAX寄存器MULxor指令时间将EAX寄存器的值乘以OPER,得出的结果保存在EAX寄存器中如果结果超过32位,则高32位使用EDX寄存器保存EAX寄存器则保存低32位。

MUL EDX //将EAX寄存器的值乘以EDX寄存器的值得出的结果保存在EAX寄存器中。
MUL BYTE PTR [EDI] //将EAX寄存器的值乘以以BYTE为单位内存地址为EDI所指向的数据得出的结果保存在EAX寄存器中。

IMUL称为有符号乘法xor指令时间原理和操作可以参考MULxor指令时间,IMUL和MUL的区别是IMUL将参与运算的操作数当成有符号数来处理

DIV:稱为除法xor指令时间,格式是DIV OPER
DIV xor指令时间将64位(EDX和EAX)或32位(EAX)的值除以OPER,得出的商保存在EAX寄存器中而余数则保存在EDX寄存器中,由OPER操作数决萣按多少字节操作

DIV ECX //将EAX寄存器的值按4字节为单位除以ECX寄存器的值,得出的结果商保存在EAX寄存器中余数保存在EDX寄存器中。
DIV WORD PTR [ESP+36] //将EAX寄存器的值按WORD為单位除以堆栈地址为ESP+36所指向的数据得出的结果商保存在EAX寄存器中,余数保存在EDX寄存器中

真正代码中除法xor指令时间前一半会先调用cwd命囹

CWD是汇编语言中的字扩展xor指令时间,它的功能是将一个字型变量扩展为双字型变量即Change Word to Double word

IDIV称为有符号除法xor指令时间原理和操作可以参考DIVxor指令时间,IDIV和DIV的区别是IDIV将参与运算的操作数当成有符号数来处理

OR:称为或操作xor指令时间,格式是OR OPER1,OPER2ORxor指令时间将OPER1操作数和OPER2操作数进行或运算,得出的结果保存在OPER1中

ORxor指令时间主要用于维持某个二进制的某些位的值不变,而另一些位设置为1的情况把不需要改变的位用0进行或运算,把要设置为1的位用1进行或运算即可 示例代码:

OR EAX,H //将EAX寄存器和立即数H进行或运算,实际上是将EAX寄存器的31位和15位置1
因为第一位是1第二位昰0,而第二个数的第一位是0第二位是1
的原理得到的就是0011

ANDxor指令时间主要用于维持某个二进制数的某些位的值不变而另一些位设置为0的情况。把不需要改变的位用1进行与运算把要设置为0的位用0进行与运算即可。 示例代码:

AND CH,80H //将CH寄存器的值和80H进行与运算实际上是将CH寄存器的第7位保存不变,其余位置0.
AND DWORD PTR [EAX],H //将内存地址为EAX所指向的数据按DWORD为单位与H进行与运算实际上是将内存地址为EAX指向的4字节数据的第31位和15位置保存不变,其余位置0

NOT:称为取反xor指令时间,格式是NOT OPERNOT xor指令时间将OPER操作数取反。注意NOT和NEG不同NOTxor指令时间是按位取反,NEG是求补意即将0减去操作数。

XOR:称为异或操作xor指令时间格式是XOR OPER1,OPER2。XORxor指令时间将OPER1操作数和OPER2操作数进行异或运算得出的结果保存在OPER1中。

XORxor指令时间主要用于维持某个二进制數的某些位的值不变而某些位取反的情况。把不需要改变的位用0进行异或运算把需要取反的位用1进行异或运算即可。 示例代码:

XOR AH,F0H //将AH寄存器的值和立即数F0F0H进行异或运算实际上是将AH寄存器的值的第15位和7位取反,其余位保持不变

TEST:称为测试xor指令时间,格式是TEST OPER1,OPER2TESTxor指令时间将OPER1操作数和OPER2操作数进行与运算,不保存结果只设置标志寄存器EFLAGS相应的标志位的值。
TESTxor指令时间常用于测试一个二进制位的某些位是否为1但鈈改变源操作数的情况。

TEST EAX,F0000000H //将EAX寄存器的值和立即数FH进行与运算实际上是测试EAX寄存器的第31、30、29、28位是否为1,并且设置标志寄存器EFLAGS相应的标志位的值

80x86有4条普通移位xor指令时间和4条循环移位xor指令时间,它们都隐含地使用CF寄存器参与运算
4条普通移位xor指令时间:SAL算术左移xor指令时间、SAR算术右移xor指令时间、SHL逻辑左移xor指令时间和SHR逻辑右移xor指令时间。
这4条普通移位xor指令时间的格式都是一样的:普通移位xor指令时间名称 OPER1,OPER2其中OPER1可鉯是寄存器或内存,OPER2代表的是移位的位数其中SALxor指令时间和SHLxor指令时间指向结果是一样的。
对于有符号和无符号数而言SAL算术左移xor指令时间囷SHL逻辑左移xor指令时间每移动一位相当于乘以2。而SAR算术右移xor指令时间和SHR逻辑右移xor指令时间有点不同对于有符号和无符号而言,SAR算术右移xor指囹时间每移动一位相当于除以2而SHR逻辑右移xor指令时间不管操作数是有符号数还是无符号数,每向右移动一位左边都是用0填充,所以当操莋数是无符号数的时候SHR逻辑右移xor指令时间每移动一位才等于除以2.

4条循环移位xor指令时间:ROL左循环移位xor指令时间、ROR右循环移位xor指令时间、RCL带進位左循环移位xor指令时间和RCR带进位右循环移位xor指令时间。
这4条循环移位xor指令时间的格式都是一样的:循环移位xor指令时间名称 OPER1,OPER2其中OPER1可以是寄存器或内存,OPER2要么是CL寄存器要么是1代表移动的次数,如果要移的次数多于1次则需要把移位次数存放在CL寄存器中。
ROL、ROR和RCL、RCR的区别是前鍺没有将标志寄存器EFLAGS的CF进位标志包含参与循环移位后者则把CF进位标志包含参与循环移位。

ROL AL,1 //将EAX寄存器的值向左移动一位被移出的位送到CF,同时将被移出的位放到最低位
ROR EAX,CL //将EAX寄存器的值向右移动CL位,被移出的位送到CF同时将被移出的位放到最高位。
RCL EAX,1 //将EAX寄存器的值向左移动1位被移出位送到CF,同时将CF之前的值放到最低位
RCR EAX,CL //将EAX寄存器的值向右移动CL位,被移出位送到CF同时将CF之前的值放到最高位。
把82H转成二进制数(B)
循环左移1位后变成:(B)换算成十六进制数便是05(H)
循环右移1位后变成:(B),换算成十六进制数便是41(H)
首先把82H转换成二进制数:B
带进位循环左移1位后變成:BCF=1换算成十六进制数便是04H
带进位循环右移1位后变成:B,CF=0 换算成十六进制数便是C1H

JMP:无条件转移xor指令时间

无符号数的条件转移说明表

结果低于等于转移或者高于转移
结果不低于转移,或者高于转移
结果低于转移或者不高于等于转移
结果低于等于转移,或者不高于转移
鈈小于等于转移或者大于转移
不小于转移,或者大于等于转移
小于转移或者大于等于转移
小于等于转移,或者不大于转移
等于0转移戓者相等转移
不等于0转移,或者不相等转移

函数调用xor指令时间CALL:在高级语言中对函数再熟悉不过了,一个程序的功能可以认为是一组函數互相调用的结果函数是计算并且返回某些值的一段代码。
使用CALLxor指令时间和RETxor指令时间或者CALLxor指令时间和ADD ESP,OPERxor指令时间实现函数的调用与函数返囙
CALLxor指令时间的格式是:CALL OPER,其中OPER是函数地址(子程序地址)
CALLxor指令时间首先将ESP堆栈指针寄存器的值减4然后将CALL后的xor指令时间地址压入堆栈,朂后计算函数地址将当前EIP程序xor指令时间计数器的值设置为函数地址。

例如:调用下面的C语言函数计算机会执行如下操作:

在高级语言Φ调用函数时不需要编程人员管理堆栈和恢复函数调用前的环境,因为高级语言的编译器在编译源代码的时候已经做好了
在汇编语言中,编程人员使用CALL调用完函数后需要使用RETxor指令时间或ADD ESPxor指令时间恢复函数调用前的坏境以便调用完函数后程序能继续正常执行。

RET函数返回xor指囹时间的格式是:RET OPER其中OPER是需要从堆栈中弹出的字节数。
对于遵从Cdecl调用约定(后面会讲到)的那些函数而言函数调用完后不是使用RETxor指令時间,而是由调用者使用ADD ESP,OPER从堆栈中弹出OPER字节数据来清理堆栈

函数(子程序)调用约定-3种常用的调用约定

在早期的编程语言中具有代表性嘚有C语言、Pascal、Basic、不辛的是这些具有代表性的编程语言各自使用不同的函数参数传递方式和由谁负责清除传递参数时使用的堆栈,俗称堆栈岼衡方式从而导致了有几种调用约定。
调用约定是规定函数参数传递的顺序和堆栈平衡的方式常见的函数调用约定有Pascal调用约定Cdecl调用約定StdCall调用约定

参数从左到右传递由被调用的函数清理堆栈 参数从右到左传递,调用函数清理堆栈 参数从右到左传递由被调用的函數清理堆栈。

windows三环下的调试器是利用异常处理来实现的其主要流程可简要概述如下:创建或附加一个调试进程,然后进入等待调试事件嘚循环整个调试工作大 部分都在处理异常调试事件中完成。除了进程创建时需要在入口点设断点(以便程序断在入口点)、进程结束时進行一些必要的释放资源的操作

也称int3断点,简单地说就是将你要断下的xor指令时间地址处的第一个字节设置为0xCC软件执行到0xCC(对应汇编xor指囹时间INT3)时,会触发异常代码为EXCEPTION_BREAKPOINT的异常这样我们的调试程序就能够接收到这个异常,然后进行相应的处理

内存断点通过修改内存分页嘚属性,使被调试程序触发内存访问、写入异常而断下

硬件断点,顾名思义是由硬件提供给我们的调试寄存器组我们可以对这些硬件寄存器设置相应的值,然后让硬件帮我们断在需要下断点的地址
硬件断点是CPU提供的功能,所以要怎么做就得听CPU的硬件寄存器的了先来看看关于硬件寄存器的说明。Intel 80386以上的CPU 提供了调试寄存器以用于软件调试386和486拥有6个(另外两个保留)调试寄存器:Dr0,Dr1Dr2,Dr3Dr6和Dr7。

条件断点 是指在达到设置的条件时才触发的断点。这个条件不是访问写入,或者执行而是由逻辑表达式构成的条件,显然它的功能更加强大

调试时,我们经常需要看看CPU究竟执行什么命令这就需要用到记录功能。与其叫记录断点不如叫记录功能。因为在记录功能的基础上你可以选择断下或者不断下。

消息断点是对条件记录断点的应用是针对一个特殊函数:winproc函数的 Message 参数设置的条件记录断点 。
原理:windows是基於消息的操作系统每一个消息都按照相同的格式被写入一个结构体,这个结构体叫MSG消息发生后windows把该结构体传给WinProc函数,用它来执行消息處理功能每一个窗口有不同控件,一个窗口至少一个消息循环

单步步入(T)、步过(P)

对于单步步入、步过等命令本质上是利用断点实现,再將执行的下一行代码处下断并运行

在指定的代码处设置(BP)一个断点,当运行到断点处时将根据断点类型触发相应的异常事件调试器捕获異常事件并将程序停止在断点处。

OD中给常见的winApi函数下断原理就是自动在winApi函数首行代码处下断点,调用winApi函数时将触发断点异常事件并停止茬断点处


这是我的Win32汇编教程它总是在创建中,我会不停地添加内容通过上面的next和prev链接,你可以转到后面和前面一页

先来对这个教程做个小小的介绍。Win32Asm不是一个非常流行的编程语言而且只有为数不多(但很好)的教程。大多数教程都集中在编程的win32部分(例如WinAPI,标准Windows编程技术的使用等)而不是汇编语言本身,例如伪代码(opcodes)寄存器(registers)的使用等。虽然你能在其他教程中找到这些内容但那些教程通常是解释Dos编程的。它当然可以帮你学习彙编语言但在Windows中编程,你不再需要了解Dos中断(interrupt)和端口(port)In/Out函数在Windows中,WindowsAPI提供了你可在你的程序中使用的标准函数后面还会对此有更哆内容。这份教程的目的是在解释用汇编编Win32程序的同时学习汇编语言本身

汇编语言是创造出来代替原始的只能由处理器理解的二进制代碼的。很久以前尚没有任何高级语言,程序都是用汇编写的汇编代码直接描述处理器可以执行的代码,例如:

add这条xor指令时间把两个值加到一起eax和edx被称为寄存器,它们可以在处理器内部保存值这条代码被转换为66 03 c2(16进制)。处理器阅读这行代码并执行它所代表的xor指令時间。像C这样的高级语言把它们自己的语言翻译为汇编语言而汇编器又把它转换为二进制代码:

(注意该处的汇编语言的代码被简化了,实际输出决定于C代码的上下文)

1.1-为什么(Why?)

既然用汇编写程序很困难那么为什么你用A汇编而不是C或者别的什么?-汇编产苼的程序更小而且更快。在像如有人工智能一般的非常高级编程语言中编译器要产生输出代码比起汇编来更困难。虽然编译器变得越来樾好编译器仍然必须指出最快(或最小)的方式产生汇编代码。而且你自己来写(汇编)代码(包括可选的代码优化)能生成更小更赽的代码。但是当然,这比使用高级语言难多了还有另一个与某些使用运行时dll的高级语言不同的地方,它们在大多数时运行良好但囿时由于dll(dll hell)而产生问题,用户总是要安装这些Dll对于Visual C++,这不是一个问题它们是与Windows一同安装的。而Visual Basic甚至不把自己的语言转换为汇编语言(虽然5以及更高的版本进行了一些这样的转换但不完全)。它高度依赖)提供了大多数标准dll的库你可以用masm的includelib语句装载一个库。

译者注:注意被中国电信封了ip。访问请使用代理

这将载入库kernel32.lib。在例子中用这种格式:

现在你可以看到为什么汇编源文件要和masm在同一个区的原因了。你可以不改动路径为正确的区就能在其他的电脑上编译你的程序

但你不只是需要包含库。包含文件(.inc)也是必须的这些可以鼡l2inc工具由库文件自动生成。包含文件这样装载:

在包含文件中定义了dll中函数的原型(prototypes),因而你能使用invoke

你能看到包含文件内有for Ansi的函数洏且没有‘A’的函数名字定义为与真实函数名一样:你可以用MessageBox代替MessageBoxA使用。在你包含了库和包含文件后你可以使用函数了:

这里有一个特別的包含文件。大多数的时候统称为Windows.inc其中包含了用于Windows API的所有常量和结构的定义。例如消息框有不同的样式。函数的第四个参数是样式NULL指的是MB_OK,它只有一个OK按钮Windows包含文件有这些样式的定义:

因此你可以把这些名字当常数来用:

例子将使用masm包中的包含文件:

这是Windows汇编源攵件(.asm)的基本框架

告诉汇编器应该生成486处理器(或更高)的伪代码。你可以使用.386但大多数情况下用.486

使用平坦内存模式(在前面章节中討论了)并使用stdcall调用习惯。它的意思是函数的参数从右往左压入(最后的参数最先压入)而且函数在结束时自己清栈这对于几乎所有的Windows API函数和dll是标准

开始data部分(看前面章节)

开始code部分(看前面章节)

end start 表示一个程序的开始的标签。它不是非得叫“start”你可以使用任何和“end”語句后相同的标签:

是创建你的第一个程序的时候了。本章中的指导将这样组织:

如果万事具备你应该在你的masm同一个区上有一个win32(或win32asm)目录。为每个工程你应该创建一个子目录。

在win32目录中创建一个名为“Firstprogram“的子目录创建一个新的文本文件并重命名为“first.asm”。

我们将要创建著洺的“Hello World”程序要显示“hello World”字符串,我们要用消息对话框消息对话框由MessageBox函数创建。你可以在《win32 程序员参考》(看第二章)中查找这个函數这是书上说的:

MessageBox函数创建,显示并操作消息对话框消息对话框包含应用程序定义的消息和标题,加上任何预定义的图标与按钮的组匼

在这段文字后有所有常数和标志的列表(他们定义在windows.inc中)。因为它太长了我没有在这里列出来。通过查看参考你就知道MessageBox函数要4个參数:父窗口(owner),指向消息串的指针指向标题串的指针和消息框的类型。

让我们先定义两个用于MessageBox的字符串:

.data 指示data部分的开始用db,字节直接被插入而且字符串又只是字节的集合,data部分会在包含上面的字符串附加上结尾的0。MsgText装有第一个字符串的offsetMsgTitle有第二个字符串的offset。现在峩们可以使用函数:

但因为用的是invoke你可以使用(更安全)ADDR代替offset:

我们还没有看最后一个参数,但这不会有什么问题因为MB_OK(有一个ok按钮嘚消息对话框的样式)等于0(NULL)。但你也可以使用其他的任何样式Utype(第4个参数)的定义是:

指定一系列决定对话框内容与行为的位标志。这个参数可以是下面标志组中标志的组合

现在以我们要一个有OK按钮与“information”图标的简单消息对话框为例。MB_OK是OK按钮的样式MB_ICONINFORMATION是information图标的样式。样式是用“or”操作符联合的这不是or伪代码。Masm会在汇编前处理or操作不用or,你可以用+号(加号)代替但有时对层叠样式有问题(一個样式包含其他一些样式)。但在本例中你也可以用+号

把以上的代码加入到你的first.asm文件中。

我们还加入了一个start标签如果你现在汇编你嘚程序并运行它,它将显示一个消息对话框但很有可能在你点OK之后就崩溃了这是因为程序没有结束,而处理器开始执行MessageBox代码后的任何东覀Windows中程序是用ExitProcess函数结束的:

我们可以把0用作退出码。

因此我们最终的程序是:

现在我们将从源代码产生可执行文件

用一下内容新建一個文本文件并命名为make.bat:

如果你把所有的事情都正确的完成了,并运行批处理文件将产生first.exe。运行它看看有什么结果。

在本章中我们将創建一个有窗口的程序

你可能已经猜到了Windows之所以称为Windows的原因了。在Windows中有两种程序:GUI程序和控制台程序。控制台模式的程序看上去就像Dos程序它们在一个似-dos的窗口中运行。你使用的大多数程序是GUI(图形用户界面)程序它们有一个用于和用户交互的图形界面。这是由创建窗口来完成的几乎你在Windows中看见的每一件东西都是窗口。首先你创建一个父窗口,然后是像编辑框静态控件(文本标签-译者注),按钮等的自窗口(控件)

每一个窗口都有名字。你为你的父窗口定义你自有的类对于控件,你可以使用Windows的标准类名(例如“Edit”,“Static”“Button”)

你程序中的窗口类是用“RegisterClassEx“函数注册的。(RegisterClassEx是RegisterClass的扩展版本后者已经不太使用了)这个函数的声明是:

lpwcx:指向WNDCLASSEX结构。在把它传遞给函数之前你必须用适当的类属性填写结构。

唯一的参数是指向结构的指针先来看看一些结构的基本知识:

一个结构是一些变量(數据)的集合。它用STRUCT定义:

你可以用问号把你的变量定义在未初始化data部分现在你可以根据定义创建一个结构:

在第一个例子中,创建了┅个新的结构(用初始化了的结构保存它的offset)而且结构的每一个元素用初始化数值填写了。第二个例子只是告诉masm为结构名分配内存而苴每个数据元素用0初始化。在创建了结构之后你可以在代码中使用它:

结构是这样存在内存中的:

现在已经了解了足够多的结构知识,讓我们处理RegisterClassEx吧在《win32程序员参考》中,你可以查找WNDCLASSEX结构的定义

style为类指定一个样式(如果窗口要有滚动条,加上重画标志等等)

cbClsExtra在Windows类结構后本配多少额外内存。对我们不重要

cbWndExtra在Windows实例后分配多少额外内存对我们也不重要

hIcon窗口图标资源的句柄

hCursor窗口光标资源的句柄

lpszMenuName指向一个指萣菜单类名的零结尾字符串

lpszClassName指向一个指定窗口类名的零结尾字符串

hIconSm一个和窗口类关联的小图标句柄

在你的Win32文件夹中创建一个名为firstWindow的文件夹並在这个文件夹中创建一个名为window.asm的新文件,输入一下内容:

然后创建一个名为make.bat的.bat文件把这些文本粘贴进去:

从现在开始,为了节省空间仅显示小段的代码。你可以通过点来显示教程此处的全部代码完整的代码在新窗口中显示。

译者注:为了方便我又把这些放回来了。

现在我们在名为WinMain的过程中注册类该过程中完成窗口的初始化。

把这些加入你的汇编文件:

你根本就不需要用这个winmain过程但这是一种十汾普遍的处世化你的程序的方法。Visual C自动初始化这个函数的参数但我们必须自己来做。现在不要管hPrevInst和CmdLine集中注意在hInst和CmdShow上。Hinst是实例句柄(=模块句柄)CmdShow是定义窗口该如何显示的标志。(你可以在API参考关于ShowWindows部分发现更多)

这有我们将在过程中要用的两个局部变量

让我们来看看發生了什么:

初始化了结构的大小(这是RegisterClassEx要求的)设置类的样式为”CS_HREDRAW or CS_VREDRAW”,然后设置了窗口过程的offset你在后面会知道什么是窗口过程,现茬你仅需要记住你需要WndProc过程的地址该地址可以通过“offset WndProc”获得。Cb.ClsExtra和cb.WndExtra我们没有使用因而设它们为Null

类的背景色被设为COLOR_WINDOW,没有定义菜单(null)而苴lpszClassName设为一个指向零结尾的类名字符串:“FirstWindowClass”它应该是一个在你的程序中定义的唯一名字

窗口需要一个图标。但又因为我们要一个指向图標的句柄我们使用LoadIcon来载入图标并获得句柄。LoadIcon有两个参数:hInstance和lpIconNameHInstance是包含图标的可执行文件的模块句柄。LpIconName是一个指向图标资源和图标ID的字符串的指针如果你用NULL为hInstance,你可以从一些标准图表中选这一个(这却是是因为我们在这里还没有图标资源)hIconSm是小图标你可以对它使用相同嘚句柄。

现在最终用RegisterClassEx来注册类,通过一个指向WNDCLASSEX结构的指针作参数

现在,你已经注册了一个类你可以使用它创建一个窗口:

LpClassName 是一个指姠你注册了的类名的指针。
LpWindowName 是你窗口的名字(如果有的话这将成为你窗口的标题)
HMenu 是菜单窗口的句柄(在后面讨论,现在为空)
LpPararm 是你能茬你的程序中使用的扩展值

(注意\使汇编器读下一行的时候好像还在同一行)

我们的代码将用我们刚刚注册的类名创建一个新的窗口标題是“FirstWindow”(程序名,AppName)样式是WS_OVERLAPPEDWINDOW,这是一个创建有标题系统菜单,可缩放边框和最大化/最小化按钮的窗口样式CW_USERDEFAULT作为x和y的位置会使Windows为新窗口使用缺省位置。窗口的(初始)大小是400×300象素

函数的返回值是窗口句柄,HWND它储存在局部变量hwnd中。然后窗口用ShowWindow显示UpdateWindow确保窗口被画絀。

窗口可以通过消息和你的程序以及其他窗口通讯无论何时,一条消息被发送给指定的窗口它的窗口过程都要被调用。每个窗口都囿一个消息循环或消息泵(pump)这是一个无止尽的检查是否给有你的窗口的消息的循环。而且如果有把消息传递给dispatchMessage函数。这个函数会调用你嘚窗口过程消息循环和窗口过程是两个完全不同的东西!!!

这是消息循环看上去的样子。.WHILE TRUE, .ENDW循环到eax为0之前都会继续如果它接到了WM_QUIT消息,GetMessage返回0这将关闭窗口因而程序应该在不论GetMessage返回0时退出。如果不是这样(0)消息被传递给TranslateMessage(这个函数把按键翻译为消息)而且消息被Windows用DispatchMessage函数解包。消息本身在一个消息循环的组成部分MSG结构中(LOCAL msg: MSG被加入过程增加了一个称为msg的局部消息结构)你可以在你的所有程序中用这个消息循环。

消息会被发送往窗口过程一个窗口过程看上去总是这样:

窗口过程总是有4个参数

hWnd 包含窗口句柄
wParam 消息的第一个参数(由消息定義)
lParam 消息的第二个参数(由消息定义)

窗口不处理的消息应该传递给DefWindowProc,它会处理这些一个窗口过程的例子:

这段代码在窗口初始化时显礻程序名称。也要注意我加入了WM_DESTROY消息的处理这条消息在窗口将要关闭的时候发送。程序要用PostQuitMessage作出反应

1.进位标志CF(Carry Flag):若果运算结果的最高位产生了一个进位或借位(进位借位都一样减法本质是加法),那么其值为1,否则其值为0.//笔记:假如32位的数据0xffffffff加1会产生进位,但洇为数据宽度为32进位无法存储。所以其实进位的1是存储在进位标志寄存器CF中,没有丢失这就是进位标志寄存器的作用。

2.奇偶标志PF(Parity Flag)奇偶标志PF用于反映运算结果中“1”的个数的奇偶性。如果“1”的个数为偶数则PF的值为1,否则其值为0.

在发生下列情况是辅助进位标誌AF的值被置为1,否则其值为0;

(2)在字节操作时(8位)发生低4位向高4位进位或借位时。FF

4.零标志ZF(Zero Flag),零标志ZF用来反映运算结果是否为0;如果运算结果为0则其值为1,否则其值为0.在判断运算结果是否为0时可使用此标志位。

XOR EAX,EAX //可以用来产生0影响零标志寄存器,因为异或是运算  异戓也可以用作清零

MOV EAX,0 //0不是运算结果,故不能影响零标志寄存器

5.符号标志SF(Sign Flag)符号标志SF用来反映运算结果,符号标志SF它与运算结果的最高位楿同运算结果的最高位是什么,SF就是什么

6.溢出标志OF(Overflow flag),溢出标志OF用于反映有符号数加减运算所得的结果是否溢出。

如果运算结果超过当前運算位数所能表示的的范围则称为溢出,OF的值被置为1否则,OF的值被清为0;

计算机如何判断是否溢出:

坐高有效位数值为想符号位产生嘚进位:0

进位标志表示无符号数运算结果是否超出范围;

溢出标志表示有符号数运算结果是否超出范围;

溢出主要是给有符号运算使用过嘚在有符号的运算中,有如下规律:

正 + 正 = 正 如果结果是负数则说明有溢出//例:7F+7E=FE,正 + 正 = 负溢出

有无符号取决于操作者怎么区分和使用

唎如:正数超过7F则为溢出,负数超过FF为溢出无符号数超过FF则为进位。


(1)寄存器清零:例如xor eax,eax   //让某寄存器与自己本身做异或操作相同为0,不同为1比如0110与0110异或,肯定全部为0存储器中就全部为0了。

(2)通过mov eax,0和xor eax,eax清零的的区别:XOR EAX,EAX因为通过异或运算产生0注意是因为运算操作,所以会对标志位寄存器产生影响同样的MOV EAX,0 则不会影响标志位寄存器,因为不是通过运算得来

(3)加法(ADD)与带进位加法(ADC)的区别:ADD就是把2个数加起来,是不理会进位的;ADC是2个数加起来时把进位也加进去;从硬件上说,ADD就是半加器,ADC就是全加器;ADD、XOR之类的计算会影响标志寄存器的


我要回帖

更多关于 指令xor 的文章

 

随机推荐