OpCode
汇编语言指令格式

汇编语言指令格式汇编语言是一种低级别的计算机编程语言,它直接与计算机的体系结构和指令集相关。
每个计算机体系结构都有其自己的指令集架构和对应的汇编语言。
下面是一些通用的汇编语言指令格式元素,具体的格式会根据不同的体系结构而有所差异:1. 操作码(Opcode):-操作码是指令的基本操作,用于指定要执行的操作,比如加法、减法、移动数据等。
不同的操作码对应不同的指令。
2. 操作数(Operands):-操作数是指令要处理的数据或地址。
有些指令可能不需要操作数,而另一些指令可能需要一个或多个操作数。
3. 寻址模式(Addressing Mode):-寻址模式指定了操作数在内存中的寻址方式。
不同的体系结构支持不同的寻址模式,例如直接寻址、寄存器寻址、间接寻址等。
4. 注释(Comments):-注释是用来解释指令的文本,对于程序员来说是可选的,但对于代码的可读性和理解很有帮助。
以下是一个简单的示例,展示了一个通用的汇编语言指令的可能格式:```assembly; 注释部分,解释指令的作用MOV AX, 42 ; 将值42 移动到寄存器AX 中ADD BX, AX ; 将寄存器AX 的值加到寄存器BX 中SUB CX, 10 ; 从寄存器CX 中减去值10JMP label1 ; 无条件跳转到标签label1 处```在上面的示例中:- `MOV` 是一个操作码,表示数据移动的指令。
- `AX`、`BX`、`CX` 是寄存器,用于存储数据。
- `42` 和`10` 是立即数,即直接给定的数值。
- `JMP` 是一个跳转指令,用于实现程序的控制流。
需要注意的是,不同的体系结构和编译器可能有不同的语法和指令集,上述示例仅用于说明基本的汇编语言指令格式。
在实际编写汇编语言程序时,需要参考具体体系结构的文档和规范。
OPCode详解

OPCode详解OpCode操作码(Operation Code, OPCode):描述机器语⾔指令中,指令要执⾏某种操作的机器码OPCode在不同的场合中通常具有不同的含义,例如PHP虚拟机(Zend VM)、java虚拟机(JVM)以及⼀些软件保护虚拟机中的最⼩操作单元都可以称之为OPCode。
常⽤字节常⽤单字节OPCode概览A -- 40~4Fopcode asm using0x40 inc eax emit(0x40)0x41 inc ecx emit(0x41)0x42 inc edx emit(0x42)0x43 inc ebx emit(0x43)0x44 inc esp emit(0x44)0x45 inc ebp emit(0x45)0x46 inc esi emit(0x46)0x47 inc edi emit(0x47)0x48 dec eax emit(0x48)0x49 dec ecx emit(0x49)0x4a dec ebx emit(0x4a)0x4b dec ebx emit(0x4b)0x4c dec esp emit(0x4c)0x4d dec ebp emit(0x4d)0x4e dec esi emit(0x4e)0x4f dec edi emit(0x4f)常⽤单字节OPCode概览B -- 50~5Fopcode asm using0x50 push eax emit(0x50)0x51 push ecx emit(0x51)0x52 push edx emit(0x52)0x53 push ebx emit(0x53)0x54 push esp emit(0x54)0x55 push ebp emit(0x55)0x56 push esi emit(0x56)0x57 push edi emit(0x57)0x58 pop eax emit(0x58)0x59 pop ecx emit(0x59)0x5a pop edx emit(0x5a)0x5b pop ebx emit(0x5b)0x5c pop esp emit(0x5c)0x5d pop ebp emit(0x5d)0x5e pop esi emit(0x5e)0x5f pop edi emit(0x5f)常⽤单字节OPCode概览C -- 70~7Fopcode asm using0x70 0x12 Jo 0x12 {_emit(0x70)} {_emit(0x12)}0x71 ... Jno ... ... ...0x72 ... Jb ... ... ...0x73 ... Jae ... ... ...0x74 ... Je ... ... ...0x75 ... Jne ... ... ...0x76 ... Jbe ... ... ...0x77 ... Ja ... ... ...0x78 ... Js ... ... ...0x79 ... Jns ... ... ...0x7a ... Jp ... ... ...0x7b ... Jnp ... ... ...0x7c ... Jl ... ... ...0x7d ... Jge ... ... ...0x7e ... Jle ... ... ...0x7f ... Jg ... ... ...常⽤单字节OPCode概览D -- 90~9FOpcode asm Using0x90 Nop/xchg eax,eax _emit(0x90)0x91 Xchg eax,ecx0x92 Xchg eax,edx0x93 Xchg eax,ebx0x94 Xchg eax,esp0x95 Xchg eax,ebp0x96 Xchg eax,esi0x97 Xchg eax,ediOPCode与指令的对应关系同类型的指令OPCode不⼀定相同B8 01000000 mov eax, 18B C3 mov eax, ebx8B C7 mov eax, ediOPCode相同的情况下指令也不⼀定相同90 nop90 xchg ax, ax90 xchg eax, eax结论: OPCode与汇编指令并⾮是单纯的对应关系那么它是如何进⾏解释的呢?⾸先它分为6个主要数据域,其中只有代码是必须存在的,指令长度在1-16个字节所以指令独此⼀份,不可能为其他机器码x86与x86-64指令集的指令的格式为:指令前缀指令码ModR/M SIB偏移直接数Instruction Prefixes Opcode Displacement Immediate可选。
汇编语言是一种什么程序设计语言

汇编语言是一种什么程序设计语言汇编语言,也称为汇编程序设计语言,是一种低级的程序设计语言,用于编写计算机程序。
它与机器语言一一对应,使用助记符(mnemonics)表示计算机的指令和操作码(opcode),并且能够直接控制计算机硬件。
汇编语言是一种面向机器的语言,与高级语言相比,更加接近计算机底层的指令集和硬件结构。
使用汇编语言编程可以对计算机进行细粒度的控制,使程序在执行效率和内存管理方面具有更高的优势。
与高级语言相比,汇编语言具有以下特点:1. 直接操作硬件:汇编语言充分利用了计算机的底层硬件资源,可以直接访问寄存器、内存地址和输入输出设备等,对硬件资源有较好的掌控能力。
2. 高效性:由于汇编语言可以直接操作硬件,在性能要求较高的场景下,能够比高级语言更加高效地利用计算机的资源。
3. 灵活性:汇编语言具有更高的灵活性,可以编写特定的指令序列来实现特定的功能,适用于一些对实时性要求较高、底层接口较复杂的应用场景。
然而,汇编语言也存在一些局限性和不足之处:1. 可读性差:汇编语言以助记符和操作码为基础,相较于高级语言,可读性较差,需要开发者具备深入的底层计算机知识。
2. 开发效率低:由于汇编语言编写的代码需要详细地指明操作码和寄存器等硬件细节,编写复杂程序会消耗更多的时间和精力。
3. 可移植性差:汇编语言对于不同的计算机和处理器架构存在差异,不同的平台需要编写不同的汇编语言代码,因此可移植性较差。
总结而言,汇编语言是一种底层的程序设计语言,具有直接操作硬件、高效性和灵活性等特点。
但由于可读性差、开发效率低和可移植性差等限制,现在在软件开发领域中使用较为有限,更多地被用于编写底层驱动程序、操作系统和嵌入式系统等领域。
opcode 翻译

opcode 翻译Opcode是指操作码,又称为指令码或机器码。
它是计算机体系结构中的一部分,用于指定计算机执行的操作。
每个操作码都有一个唯一的二进制表示。
它通常作为计算机指令的一部分存储在计算机的内存中,通过程序计数器来指示下一条要执行的指令。
操作码用于告诉计算机执行特定的操作,如加法、减法、乘法、除法、逻辑运算等。
不同的计算机体系结构和处理器都有自己独特的操作码集合。
例如,x86体系结构中的一些常见操作码包括ADD (加法)、SUB(减法)、MUL(乘法)、DIV(除法)等。
操作码通常与操作数一起使用,操作数是指操作码所操作的数据。
操作数可以是寄存器、内存地址或立即数。
例如,在执行加法操作时,操作码指定加法操作,而操作数指定要相加的数据。
以下是一些常见的操作码的中英文对照例句:1. ADD(加法):- Opcode: 0000 1100- Assembly Language: ADD AX, BX (将BX寄存器的值与AX 寄存器的值相加)2. SUB(减法):- Opcode: 0010 0010- Assembly Language: SUB CX, DX (将DX寄存器的值从CX 寄存器的值中减去)3. MUL(乘法):- Opcode: 1111 0111- Assembly Language: MUL AL (将AL寄存器的值与累加器相乘)4. DIV(除法):- Opcode: 1111 0101- Assembly Language: DIV BL (将BL寄存器的值除以累加器)操作码是计算机体系结构中非常重要的一部分,它决定了计算机可以执行的指令和操作。
通过了解不同体系结构的操作码,程序员可以编写有效的机器代码,实现各种计算和逻辑操作。
x86指令(1)

Change DEFAULT operand size. (66) Change DEFAULT address size. (67) Repeat prefixes. (F2, F3) Segment override prefixes(change DEFAULT segment). (2E, 36, 3E, 26, 64, 65) LOCK prefix. (F0)
◦ 对于写内存的一些指令增加了锁地址总线的功能,这些写 内存的指令如常见的 sub,add 等指令,通过 Lock prefix 来实现这功能,使用 Lock prefix 将会使 procesor 产生 LOCK# 信号锁地址总线。Lock prefix 仅 使用在一些对内存进行 read-modify-write 操作的指令 上,如:add, sub, and 等指令。
Prefix 2E 36 3E 26 64 65
Explanation CS segment override prefix SS segment override prefix DS segment override prefix ES segment override prefix FS segment override prefix GS segment override prefix
I‘ll be back!
-- Arnold Schwarzenegger, The Movie "Terminator"(1984)
只需要知道一点,Code的操作数决定是否需要使用 ModR/M 进行寻址。
ModR/M
233格式
16进制 B7 3A 2进制的4:4格式 1011 0111 0011 1010 2进制的2:3:3格式 10 110 111 00 111 010
opcode_相对偏移量_概述说明以及解释

opcode 相对偏移量概述说明以及解释1. 引言1.1 概述引言部分将对本文的主题进行概述。
本文将介绍opcode相对偏移量的概念、定义和作用,以及在计算机程序中的应用和重要性。
同时,还将探讨相对偏移量在提高代码执行效率、实现程序跳转和控制流程以及编译器优化和代码压缩技术中的具体应用。
1.2 文章结构为了使读者更好地理解本文内容,文章结构将按照以下顺序进行组织:- 第一部分是引言部分,概括介绍本文的主题和目的。
- 第二部分是opcode相对偏移量的概述说明,包括opcode的定义和作用,相对偏移量的概念和含义,以及在计算机程序中的应用。
- 第三部分是解释opcode相对偏移量的重要性,详细描述如何通过使用相对偏移量来提高代码执行效率、实现程序跳转和控制流程,并介绍其在编译器优化和代码压缩技术中的应用。
- 第四部分是实例分析opcode相对偏移量的具体应用场景,通过案例分析展示如何在条件分支语句、循环结构及虚拟机和解释器中利用相对偏移量来优化性能。
- 最后,第五部分是结论部分,总结opcode相对偏移量的重要性和作用,并展望未来的发展方向和研究方向。
1.3 目的本文的目的在于全面介绍opcode相对偏移量及其在计算机程序中的应用。
通过深入讨论相对偏移量的重要性和作用,读者将能够更好地理解以及运用这一概念。
同时,本文还希望为读者提供实际案例和应用场景,以便更好地理解如何使用相对偏移量来提升代码性能和优化程序流程。
最后,通过展望未来发展方向和研究方向,本文也为相关领域的学习者或从业者提供了一些建议和思路。
2. opcode 相对偏移量概述说明:在计算机程序中,opcode(操作码)是指指令集中用于表示不同操作和功能的二进制代码。
相对偏移量则是一种表示内存地址差值的方式,它表示当前位置与目标位置之间的地址距离。
2.1 opcode的定义和作用:Opcode是由硬件体系结构所定义的一组二进制代码,用于执行特定的操作或操作序列。
汇编指令之OpCode快速入门
汇编指令之OpCode快速入门:最近一直被一些初学者问及有关于汇编指令的长度问题,因此为此专门撰写本文,以求为不知OpCode为何物,或者正为汇编长短不一的指令而烦恼的朋友一个最为快速的指引。
其实,OpCode并不复杂,在本文中我不打算细致入微的告诉大家OpCode的原理,不会为大家带来一大堆有关于什么是定长指令、什么是变长指令的理论知识,更不会带着各位读者玩OpCode Hacking,我只会告诉你“怎么了”、“为什么”以及“如何解决”。
1、我的汇编指令怎么了?哦,天啊!怎么我今天突然发现汇编指令竟然是长短不一的!你还没发现吗?那么请过目:1E831880000CALL00430B862E917FEFFFF JMP0042817138B442404MOV EAX,DWORD PTR SS:[ESP+4]485C0TEST EAX,EAX556PUSH ESI68BF1MOV ESI,ECX我们可以看见“CALL00430B86”这条汇编指令竟然占用了5个字节,而“PUSH ESI”则只占用了1个字节,汇编指令的脾气犹如一只滑头的猴子一样让你摸不到头脑,它很明显的告诉了你“嘿!兄弟,你别想搞懂我!”你也许会感到很郁闷,但是我并不这么想,因为如果我要想自己搞一个反汇编引擎,或者是我要在我的壳里加上代码混淆功能……嗯,算了,就算是我想娱乐一下搞搞免杀吧,那么我终归是要搞懂它的,为什么?因为如果搞懂它的话,那么我就没办法做到这些!很明显我们的汇编指令继承了Intel工程师的狡猾本质,为了尽可能的减少体积,所以它们的体积被设计的不尽相同。
哇哦!很多读者此时似乎已经想明白是怎么回事了,肯定是不同的指令对应的字节数不一样,恩……这样只要我们搞到一张表就可以了!不是吗?一张可以描述每个指令所用二进制码的表格,然后我们就万事大吉了。
但是很不幸,我在初次接触OpCode时也想出了这个“超级点子”,但是很可惜我的“超级点子”与各位读者的一样,并没有为我解决任何问题,请过目:7B801000000MOV EAX,188BC3MOV EAX,EBX98BC7MOV EAX,EDI看到了吗,一样的指令,一样的目的操作数,得到的确是完全不同的机器码……2、这是为什么?嗯,我想这个问题是很明显的,源操作数如果是一个寄存器的话,那么能有几种可能呢?按照规则来讲貌似只有不超过50种可能,那么如果被操作数是一个数值呢?你想想,32位能表示多少数,将其乘以2就是最终的可能性了,这么多的可能性一定不是区区两个16位数就能表示过来的。
php执行opcodePHP编译原理之Opcode(OperationCode)PHP代
php执行opcodePHP编译原理之Opcode(OperationCode)PHP代Opcode(操作码)是一种特殊的机器指令,用于执行计算机程序中的操作,例如算术运算、逻辑运算、地址操作等。
在PHP编译原理中,Opcode在PHP解释器中起着关键的作用,它是由PHP解释器生成的一种中间代码,用于在执行PHP程序时进行解释和执行。
在PHP解释器的编译过程中,PHP代码首先进行词法分析和语法分析,将PHP代码转换为一个个有意义的语义单位,例如变量、函数、表达式等。
接下来,PHP解释器将这些语义单位转换为Opcode序列,构成了程序的中间表示。
PHP解释器生成的Opcode通常是一系列有序的指令,每个指令都对应着一个特定的操作,例如赋值、函数调用、条件判断等。
每个Opcode都有一个唯一的编号和对应的操作数,用于指定执行的具体操作。
PHP解释器通过执行Opcode序列来实现PHP代码的执行。
在执行过程中,PHP解释器会根据当前指令的操作码,判断下一步应该执行哪个指令。
当执行一个指令时,PHP解释器会根据指令的操作码,执行相应的操作,例如执行算术运算、修改变量的值、调用函数等。
通过使用Opcode作为中间代码,PHP解释器可以将PHP代码高效地转换为计算机能够理解和执行的指令序列。
Opcode的生成和执行过程隐藏了PHP代码的细节,提高了PHP的执行效率,同时也提供了对PHP代码的优化和调试支持。
总结起来,Opcode是PHP解释器生成的一种中间代码,用于在执行PHP程序时进行解释和执行。
它通过一系列有序的指令,实现了PHP代码的高效执行。
Opcode的使用隐藏了PHP代码的细节,提高了PHP的执行效率,为PHP的优化和调试提供了支持。
OgreOpcode
OgreOpcode碰撞检测使用概述Table of ContentsOgreOpcode几乎是不使用物理引擎时Ogre下的唯一选择,但是这个天杀的库文档实在是太少了,约等于没有。
下面把这两天使用它的经验整理一下。
下载&编译OgreOpcode没有任何Release,获取它的唯一办法是从SVN中下载源代码进行编译,SVN地址如下:https:///svnroot/ogreconglo/ogreopcode/trunk在script文件夹找到自己用的编译器的项目文件,直接编译就可以了。
需要注意的是,SVN中的exmples是无法正确编译的--这些例子运行所需要的一些文件没有在SVN里面!另外就是doc目录中的Doxygen配置文件中居然用的是绝对路径,不经修改是无法运行的。
此外我在使用VS2005的时候发现,它的项目文件中少包含了一个叫OgreCapsuleMeshCollisionShape.cpp的文件,导致编译无法通过。
需要自己把它加到OgreOpcode 项目中才可以编译成功。
另外在项目的“生成后事件”中会执行一个拷贝的命令,但是在svn的文件夹结构中目标文件夹是不存在的!这也会导致编译失败。
实在是被这个项目的维护人员打败了...这么多纰漏...我知道是个人都会觉得从svn里面编译很麻烦,所以我吧它编译好了上传上来了,点这里下载ogreopcodebin。
里面包括编译好的debug和release版的dll。
使用要使用OgreOpcode,基本上可以分为这么几步:1.创建一个Manager2.创建一个Context3.加入参加碰撞的物体4.碰撞检测初始化// 创建Manager。
这个Manager使用Singleton实现CollisionManager* mCollisionMgr = new CollisionManager(sceneMgr);// 创建context 不同的context中可以添加不同的物体,发生不同的碰撞关系mCollisionContext = mCollisionMgr->createContext( "test" );// 创建碰撞类,并且设置不同类型之间碰撞的检测方式mCollisionMgr->addCollClass("charactor");mCollisionMgr->addCollClass("scene");// COLLTYPE_QUICK 表示两个charactor之间的碰撞使用简单的球体进行检测mCollisionMgr->addCollType("charactor", "charactor", COLLTYPE_QUICK);// COLLTYPE_IGNORE 表示忽略场景物体之间的碰撞mCollisionMgr->addCollType("scene", "scene", COLLTYPE_IGNORE);// COLLTYPE_EXACT 表示计算charactor和scene碰撞时的所有交点mCollisionMgr->addCollType("charactor", "scene", COLLTYPE_EXACT);加入物体OgreOpcode中的物体主要有以下几种:Box, Capsule, Sphere, Terrain, Mesh, Entity, Ptr,调用CollisionManager::createXXXXCollisionShape函数来创建。
unrecognized opcoad
未识别的操作码(Unrecognized Opcode)简介在计算机科学和计算机工程领域,操作码(Opcode)是指用于指示计算机硬件执行特定操作的二进制代码。
操作码通常包含在指令中,而指令是计算机程序中的基本单位。
然而,有时候会出现未识别的操作码,也就是计算机无法理解或执行的操作码。
产生原因产生未识别的操作码可能有多种原因:1.软件错误:编写或编译程序时出现错误,导致生成了无效或不合法的操作码。
2.硬件故障:计算机硬件出现故障,导致无法正确解析或执行某些操作码。
3.版本兼容性问题:某些旧版本的软件可能包含了新版本硬件不支持或不认识的操作码。
4.恶意软件:恶意软件可能会使用未知的、特定于某个系统或芯片组的操作码来执行危险或非法操作。
影响当计算机遇到未识别的操作码时,通常会发生以下情况之一:1.崩溃和异常终止:如果计算机无法处理未识别的操作码,它可能会崩溃或异常终止,导致数据丢失或系统不稳定。
2.无响应:计算机可能会无法对未识别的操作码作出任何响应,导致程序停滞或死锁。
3.不正确的结果:如果计算机尝试解析未识别的操作码并执行相关操作,但错误地解释了其含义,可能会导致不正确的结果。
解决方法遇到未识别的操作码时,可以采取以下几种方法来解决问题:1.更新软件和固件:确保使用最新版本的软件和固件,以修复可能存在的已知问题和漏洞。
2.检查硬件完整性:定期检查计算机硬件是否存在故障或损坏,并及时修复或更换受损部件。
3.调试和排查错误:通过调试工具和技术,对程序进行逐步调试,以确定是否存在未识别的操作码,并找到引发问题的具体原因。
4.使用安全软件:使用可信赖的安全软件来检测和清除恶意软件,以防止其使用未知操作码对系统进行攻击。
5.联系技术支持:如果遇到频繁出现未识别操作码的问题,并且无法自行解决,建议联系计算机或软件的技术支持团队,寻求专业帮助。
预防措施除了采取解决方法来应对未识别的操作码问题外,还可以采取一些预防措施以减少其发生的可能性:1.规范编程实践:遵循良好的编程实践,编写健壮和可靠的代码,减少潜在的错误和漏洞。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
The Svin的OpCode教程说明:1.这篇教程原文分为8帖,它们的地址分别为:Opcode#1/board/index.php?topic=8963Opcode#2/board/index.php?topic=8967Opcode#3/board/index.php?topic=8982Opcode#4/board/index.php?topic=9062Opcode#5/board/index.php?topic=9063Opcode#6/board/index.php?topic=9741Opcode#7Opcode#8翻译)2.3.4.5.6.感谢朋友7.Xiep2008.08.19目录1.概览------------------------------------------------------------------------------------------42.OpCode的结构-----------------------------------------------------------------------------63.开始和结束---------------------------------------------------------------------------------104.Prefixes66h-----------------------------------------------------------------------------------145.Prefixes67h,F2h,F3h------------------------------------------------------------------------176.Prefixes Segment override and LOCK----------------------------------------------------197.Bit filed----------------------------------------------------------------------------------------25一概览这本书为那些像本人一样渴求知识而不得的人而写.这套教程的风格是通过练习得出结论,而不是依据结论来做练习.换句话说,这篇教程包含很多的例子和练习.深入学习本教程理论最好的方法是做所有的练习,以及试验所有的例子.这本教程并不讲汇编语言编程,我们讲的是"OpCode".What"OpCode"is?这是课程要解决的主要问题,现在我给一个简短的回答.当我们在源代码中写入"lodsb",在编译阶段汇编器(如ml.exe)遇到"lodsb",它会在可执行文件(或.obj文件)中用一个字节ACh来替代它.ACh就是所谓的OpCode,"lodsb"则是助记符(mnemonic).助记符"lodsb"对汇编器ml.exe说,"给我用字节ACh替换lodsb".处理器并不知道lodsb是什么.当寄存器EIP指向ACh字节时,处理器解码器对字节ACh解码,通过这个字节处理器就知道程序要将寄存器ESI指向的一个字节的内容送入AL寄存器.OpCode就是如此简单.你可能会说:一个助记符是某个特定的操作码的别称.但是,事实并没有如此简单.对于"lodsb"这个特定的助记符来说,它确实对应ACh,并且操作码ACh也唯一确定助记符"lodsb".然而并非所有的情形都是这样.我们很快会见到"为什么不"的例子.就像现实生活中一个名称往往并不唯一对应一个事物一样,OpCode和助记符的关系如下:不同的OpCode可能有同样的助记符;一个OpCode可能有几个助记符对应.有些事情可能很简单但却会吓倒初学者,在不久的将来我将会做详细的解释.查看OpCode或mnemonics的方法有很多种,我认为最快且最好的方法是使用调试器.我将在OllyDbg中试验所有的例子.Exersize1.在调试器中输入助记符和OpCode1.用OD打开任意一个Win32可执行文件,加载完成后在代码窗口中双击某一行,在弹出的指令输入对话框中输入lodsb回车,你将看到(类似下面的内容): 0040108C>AC LODS[BYTE DS:ESI]第一列(0040108c)是指令在内存中的地址,第二列(AC)是OpCode,第三列(LODS[BYTE DS:ESI])是OpCode AC的助记符.2.你可以试着输入一些你其他的助记符,并观察他们的OpCode.3.按CTRL+e,你将看到一个对话框,在HEX+00对应的文本框中输入AC 回车,可以看到第二列和第三列如下:AC LODS[BYTE DS:ESI]4.试着输入一些其他的OpCode,并观察对应的助记符.Exersize2.一个助记符对应一个OpCode吗?1.按CTRL+e,输入OpCode90,回车,可以看到OD将90认为是NOP:0040108E90NOP2.输入助记符"NOP",同样看到:0040108E90NOP3.输入助记符XCHG EAX,EAX糟糕!OllyDbg并没有插入我们的指令,而是用NOP代替!!!不用着急,这是OllyDbg的问题,当看到OpCode90的时候它总是显示NOP,而不是xchg eax,eax.对我们来说这两个助记符"xchg eax,eax"和"nop"都显示90h.由此可见,一个事物(OpCode)不只对应一个名字(助记符).我再重复一次:在计算机世界里有的只是OpCode,助记符只是这些OpCode的名字,这些助记符组成的系统就是汇编语言,助记符并不完美,因为没有完美的语言----任何语言的描述和现实总是有或多或少的区别的.在我们得出某些结论之前,我们先来做第二部分的练习.Exersize3.1.输入助记符add eax,1,你将看到0040108E83C001add eax,12.输入OpCode0501000000,我们将惊奇的发现0040108E0501000000add eax,1同样的助记符但是不一样的OpCode!事实上它们不仅大小不同,结构也不一样.第一个(83C001)3字节长,包含3个域;第二个(0501000000)5字节长,但只有两个域.在我们学习这些域之前,我们先来想一想:当你看着这两个OpCOde:83C0010501000000你是否想到:第一个OpCode中01和第二个OpCode中01000000是加到EAX的立即数?既然助记符向OpCode可以有多种转换的方式,那么到底什么OpCode将会被插入exe文件中?这是由什么决定的呢?答案是:这是由汇编器决定的.当我们在数据段中写入一些值时,我们往往会使用:somevar db0AH,0DH;而在代码段中我们不需要输入名字而只需定义值:db ACH.所以你可以用16进制写代码段,比如用Mov esi,offset somedata Mov esi,offset comedataLodsb代替db ACH好了,又到了我断开网络的时候了,我必须把我所写发送出去,否则我什么也不能提交.下次我们将从OpCode的结构开始,只是一个介绍而已,还涉及OpCOde的一些重要而一般的特性.你将发现这是多么的易学和实用.二OpCode结构机器指令回答处理器3个问题:1.What to do?2.With what to do?3.How to do?其中第三个问题是可选的.要回答这些问题,机器指令可能有多个逻辑块.在列举这些逻辑块之前我们先来做一些练习.为了更好的试验,在开始这些练习之前我们先来定制一个特别的程序:让程序变得小一点,这样OllyDbg可以更快的加载我们的程序;用一个字节的助记符填充它,这样就可以把代码段当作白纸一样使用;我们可以利用批处理文件或者设定快捷键等方式,更方便地加载我们的程序.下面是这个程序的源代码:.codestart:rept256nopendmcall ExitProcessend start它将在程序中插入256个nop指令.既然现在你已经知道在call ExitProcess之前有256个90h,我们是否可以用16进制来编码?.codestart:rept256db90hendmcall ExitProcessend start或者我们检验一下看看db90hnopxchg eax,eax这三个是不是一样的.我们可以这样改写程序:.codestart:rept20nopendmrept118xchg eax,eaxendmrept118db90hendmcall ExitProcessend start当我们用OllyDbg加载编译好的可执行文件后,就可以看到在call ExitProcess 之前全部是90nop.OpCode域简介有6个域是OpCode可能会用到的,它们的名字是什么这并不重要,重要的是它们的排列顺序.它们是:1.Prefixes2.Code3.byte Modr/m4.Byte SIB5.Offset in command6.imm.Operand并不是这所有的6个域都会被用到,但是有一项却是一定会有的,那就是第2项Code,有些指令甚至只会用到这一项.在试验程序中输入C32F90AD所有输入的这些OpCode都只有Code块.(提示:它们对应的助记符依次为:retn,das,nop,lod**.)现在我们知道lod**的OpCode是ACh.我们来看看是不是可以增加额外的域来扩展它的功能呢.我们输入F3AC可以看到rep lod**.我们可以确定AC是Code域,F3AC则是[Prefixes][Code],所以F3是Prifixes 域.F3表示的是Rep Prefixes,它也能与mov**,sto**等指令连用.现在请试着输入一些可以使用rep prefixes的助记符,并观察对应的OpCode;然后再在Ctrl+e窗口中输入这些以F3开头的OpCode,观察对应的助记符.我们首先应该记住:OpCode由6个域组成,它们不必都用上,但是Code是一定会有的.我们再来看六个BCD码运算指令,输入DAADASAASAAAAAMAAD在Intel的文档中,这些都是只包含1个域的指令,这意味着它们都只有Code域.然而最后两条指令却是2字节的.我们可以通过观察这些指令,然后断定这些指令的格式和附加的域.先不要看下面的答案,试着自己想一想AAM和AAD和别的指令有什么不同……27DAA2F DAS3F AAS37AAAD40A AAMD50A AAD很明显我们可以看到:1.AAM和AAD都是2字节的,然而其余的4条指令都是1字节的.2.AAM和AAD的OpCode的第2个字节都是0Ah不知你是否想起这两条指令的描述:AAM:divide AL by10商放在AH里余数放在AL里AAD:AL=AL*10+AL两者的操作都与10有关,而且它们的第二个字节都是10(0Ah).那么0Ah会不会是偶然的呢?会不会是操作数的一部分呢?那AAM和AAD的指令格式会不会不是AAM:D40AAAD:D50A而是:AAM:D4imm8AAD:D5imm8以及imm8可以是任何别的值呢?我们可以验证一下,输入:Mov al,8D407;作用和D40A相同,只是除以7而不是10如果我们是正确的,那么按F8单步执行刚输入的指令后AH(商)=1,AL(余数) =1.现在,我们又知道了一种新的指令格式:[CODE][imm](域2和域6)同时我们也发现某些OpCode没有对应的助记符.在这里我得说一下,Oleh (OllyDbg的作者)允许使用立即数作为AAM和AAD的操作数.理解了OpCode的规则,将有助于底层程序员明白一些鲜为人知的事情----一些未在文档上列出的OpCode,这也是掌握OpCode的另一个好处.现在我们来看一些基本的规则:虽然并不是6个域都是必要的,但是,它们的排列顺序绝对不能乱,必须严格按照上面的顺序进行.有些域或许不会出现,但只要出现了,编号小的域就绝对不允许出现在编号大的域的后面,反之亦然.例如,[Prefixes][code]的顺序绝不允许变为[code][Prefixes].输入0110和1001你将看到它们的不同.(提示:0110----add dword ptr[eax],edx1001----adc byte ptr[ecx], al)下次我们将讨论处理器是如何确认OpCode的开始和结束的,我们也会看到2 +2是怎样等于3的.三开始和结束虽然这部分基于一些非常简单的事实,但并不是每一个程序员都能看到这些事实背后的本质.所以我们最好不要太过于轻视它.用OllyDbg加载上次编写好的"nop"程序.看着左列的指令地址,起始的指令地址是多少,在我的程序里是:00401000>90nop再看看右边的寄存器窗口,EIP的值又是什么,在我的程序里是EIP:00401000EIP寄存器的值是内存中某个字节的地址,这个地址将被认为是某条指令的开始地址.当内存中的字节未被处理器加载并解码,还不知道这条指令到何处结束.现在我们按F8执行,可以看到0040100190nopEIP:00401001EIP指向了内存中下一个字节并把它当作是指令.这里的下一个字节是指紧接上一条指令的结束地址的那个字节.如果你认为我没有写"EIP指向下一条指令"是因为我糟糕的英语,那你就错了.尽管你并不是错在我糟糕的英语^_^(原作者是俄罗斯人).如果一切正常的话,这些字节将被解码然后被当作下一条指令执行.事实上这些字节可能仅仅是一些垃圾,而处理器则不能正确的对其解码而产生异常.在程序的当前代码行(高亮显示的那一行)输入OpCodeFFFFOllyDbg不会把它当作是一条指令,你将在助记符那一栏看到"???".同时EIP 指向我们刚刚输入的FFFFh.所以如果不管这里的垃圾----对于处理器来说是"非法指令"----的话,处理器将解码并执行它.我们按下F8键,可以看到提示的错误信息(在OllyDbg下方的状态栏),处理器产生了异常.相应的,OllyDbg中断并询问如何继续.我们将FFFFh替换为9090h,然后输入不同的助记符,比如长指令或者使用lea 指令,单步执行并仔细观察EIP和指令长度的变化.你将看到处理器识别出指令的尺寸,且EIP现在指向紧接着刚输入的指令即nop.处理器是如何做到这个的呢?事实上处理器是通过解码来正确识别指令的.通过分析指令格式可以知道组成指令的域以及域的大小,不同的位指示了某个大小的域被使用.例如:短跳转指令EB:imm8为两个字节,EB是CODE域,当EIP指向的内容是EB09,处理器通过解码第一个字节将得知EB的指令长度是2个字节.所以问题的一个初步回答是:1.开始:处理器认为当前EIP指向的内存单元中的第一个字节就是指令的开始.2.结束:处理器通过对OpCode进行解码(大多数情况是根据[code]域),从而得知哪里是结束3.如果指令不是"控制类型"的,那么下一条指令的开始将紧接当前指令的末尾.注意:除非被处理器加载,处理器是没有办法确定这些内容是否是真正的合法指令.现在回顾一下第三点,什么是"控制类型"指令?一般来说它们是ret,call,jmp,jcc,int等.它们之间的共同点是什么呢?不要回答我:"它们都跳转到某个地址".或者更加糟糕的答案:"它们跳转到另外某条指令开始的地方".我们在程序的当前行输入EB00,并按F8执行.它将跳到哪里?我们看到处理器停在下一条指令开始的地方.我们再输入EB FE,运行,但是并没有跳转到任何地方,就像所有的事情都停止响应一样.就算是普通的指令执行后,也会步向下一条指令,而现在却既不跳转也不步进到下一条指令.真正的底层程序员应该理解指令的本质,而不单单从指令的字面意义上去理解它.真正的底层程序员不会说cmp指令比较操作数;他会说cmp指令是用第一个操作数减去第二个操作数,由此来设置相应的标志位.同时,我们关心的只是标志位,并不关心键操作后的结果,所以不需要把减操作的结果储存到第一个操作数中.这才是cmp指令的本质.如果知道了它是如何工作的,你就可以用cmp指令来做任何它擅长的事情,而不仅仅是比较.其实并没有什么cmp指令,有的只是简单的逻辑的,数学和数据转换\写入\读取的OpCode.让我们回到"控制指令".它们真正做了什么样的操作呢?答案是:它们改变EIP 寄存器.我们知道EIP指向的内存数据被处理器当作是指令的开始.通过这些控制指令我们又可以将什么值赋给EIP呢?答案是:如果你非常了解OpCode和这些指令的算法,那么可以是任何32位的值.所以我们可以强迫处理器把内存中任意某个字节当作指令的开始.输入OpCode04AC你将看到0040100804AC ADD AL,0AC我们也知道ACh是lod**的OpCode,地址00401008是OpCode04AC的起始地址,但是被地址00401009指向的指令是什么呢?如果我们使EIP指向00401009,那么处理器将不会把ACh当作是04:imm8的操作数,而是把它看做一个OpCode ACh->lod**?如果现在我们需要实现一个算法if ZF=0add al,AChelselod**请试着用助记符写下这个算法.请看下面的汇编代码:jne$+1add al,ACh这就是我们所需要的.请不要试着运行它,除非你清楚的知道[esi]指向的地方是什么内容.如果ZF=1,EIP将指向指令add al,ACh的第二个字节,因为我们知道第二个字节的值是ACh,而操作码是ACh->lod**,我们实现这个算法仅用了4个字节!!!004010057501JNZ SHORT0040100704AC ADD AL,0AC我们一起来看看各个字节都表示什么意思.75:imm是7501的域格式,75是JNZ的OpCode,imm在这里是01,会被加到EIP里面去,整个7501表示:如果这条指令被执行了,则EIP会指向下一条指令的第二个字节.04AC的域格式:04:imm,04->OpCode,AC->imm.两条指令都有两个域,格式为:[code][imm].如果ZF=0,7501这条指令就会把EIP中下一条指令的起始地址+1,使得EIP 指向下一条指令的第二个字节,处理器将认为AC所在的地址才是下一条指令的开始,这时AC将被认为是一个新的OpCode;否则EIP将指向04AC所在的地址,开始的字节是04h,处理器将认出格式域:04:imm8(add al,imm8),这时AC将被当成操作数,而不是操作码.现在我们看看谁是最快最聪明的程序员.1.实现一个4字节的算法:if ZF=1inc eaxelsemov al,40h2.另一个测试(5字节):if PF=1set all bits in ax to0elseset all bits in eax to03.再一个测试(5字节):if PF=1set all bits in eax to1elseset all bits in ax to1(1提示:je$+1Mov al,40h74h,01h,B0h,40h)(2提示:jpe$+3Xor eax,eax74h,01h,66h,31h,C0h)(3自己动手啦~~)四Prefixes66h用OllyDbg加载试验用程序,输入or eax,-1mov ecx,edxand ebx,ebp接着对上面每一个OpCode再按这种方式输入一次,如第一个OpCode第一个字节:66然后是OpCode:83F8FF对剩余的两条指令用同样的方式输入.可以看到我们用这种方式产生的指令对3条指令都适用,除了16位寄存器指令,如or ax,-1mov cx,dxand bx,bp66h就是所谓的Prefix.Prefixes是产生OpCode的第一个域.回忆一下OpCode的6个域:1.prefixes2.Code3.byte modr/m4.byte sib5.offset in command6.imm.Operand记住:在实际的应用中,并不是所有的这6个域都会被用到,但是有一项是一定会有的,即第二项Code,这6个域的顺序绝对不能乱,必须严格按照上面的顺序进行.Prefixes是所有域中最容易理解的一个,请先明了它的一些特性:1.它是唯一的一个可能出现在Code之前的域2.所有的Prefixes都只有一个字节3.在一个OpCode中可能会有多个PrefixesPrefix66的意思是“切换默认的操作数的大小”.例如在有的系统中有两种默认的操作数大小:16位和32位.在Win32编程环境中默认的操作数的大小是32位,但操作数有可能会被写成16位或者32位,唯一的区分方法是看它有没有Prefix 66.我们来看一些例子,输入下面这些单字节指令:LOD**LODSWLODSD啊!LODSW和LODSD使用同样的code域AD!其实,LODSW和LODSD这两条指令是同一个指令,只不过它们的操作数的大小不一样而已—LODSW使用WORD(不是Win32默认操作数大小)作为操作数,而LODSD则使用DWORD(是Win32默认操作数大小)作为操作数.所以对于LODSW指令需要用prefix66切换操作数大小.请注意我们并没有说“指定”而是说“切换”,反映到这个例子中,就是“切换默认的32位操作数到16位”,而不是“指定操作数的大小为16位“.如果默认的操作数大小是WORD,那么切换后就是DWORD;反之,如果默认的操作数大小是DWORD,那么切换后就是WORD.那些没有使用WORD或DWORD的指令(BYTES,QWORDS等),不会对操作数的大小做任何改变.输入:mov al,0ffmov al,cl接着在这两条指令之前加上66h,可以看到66:B0FF MOV AL,0FF66:8A C1MOV AL,CL什么都没有被改变.记住:Prefix66仅对操作数为WORD和DWORD的指令起作用.我们在使用BYTE操作数的指令前加入prefix66,是不是产生了一条非法指令呢?嗯,事实并不是这样子的.运行指令66:B0FF MOV AL,0FF66:8A C1MOV AL,CL没有任何问题,它们和MOV AL,0FFMOV AL,CL的功能是一样的.如果Prefixes不能对随它之后的OpCode起作用,那么处理器将忽略它.另外的一个Prefix rep的作用是让处理器对随后的指令循环执行ecx(cx)次,指令inc eax的OpCode是40,我们来看看如何使用prefix F3(rep)使inc eax连续执行3次.在我们的试验程序里输入xor eax,eaxmov ecx,3rep inc eax然后运行.你将看到两点:1.最后eax=1,这意味着prefix F3并没有起作用----它被忽略了;2.没有任何异常(execption)产生.在OllyDbg里面我们可能会遇到以下两个问题:1.如果你输入rep inc eax它将提示“无法识别的指令”,我们试下看,输入F340行不行.奥!OllyDbg还是没有正确的识别!~~如果按F8单步执行我们刚刚输入的指令,这样运行的结果是:1)Eax=12)没有任何异常发生,处理器忽略了prefix F3.如果Prefixes不能对随它之后的OpCode起作用,那么处理器将忽略它.输入包含两个prefix的指令rep lodsw66:F3:AD(REP LODS[WORD DS:ESI])在这条指令中我们看到两个prefix,66和F3.所以Prefixes域的另一个重要的特性是:一条指令可能只有一个CODE域,一个mod r/m域,或者一个offset域等,但是可以有多个Prefixes.有关prefix66h的最后的“新闻”是:你可能错误的认为在实模式下默认的操作数大小是WORD,而在保护模式下是DWORD,事实并不完全这样.我们唯一可以确定的是----当在Win32环境下默认操作数DWORD.因此,你在Win32下使用的任何操作字(WORD)的指令都会变长一个字节(prefix66),并且需要多花一个时钟周期去执行.这个并不是一点好处也没有,当你习惯于计算数学问题,你可以通过取舍指令大小和运行速度来确定是不是用WORD更好一些.任何使用字的指令将多花你一个字节和一个用来解码的时钟周期.那么默认操作数大小是如何确定的呢----这是被段描述符的D位确定的.在实模式下它被赋值位0,所以在实模式下默认的操作数往往是WORD.在保护模式下可能是0或者1(在Win32应用程序中是1).所以:If(PROTECTED MODE&&BIT D==1)AD=LODSD66AD=LODSWElseAD=LODSW66AD=LODSD这就是我们之前所说的:不同的OpCode可以有同样的助记符,一个OpCode可能有多个不同的助记符.我们也看到某些OpCode可能有两种不同的功能,而这依赖一些条件.如果你认为指令可以和prefix66配合使用,你将明白这里的条件和功能分别指的什么.下次我们继续学习OpCode的第一个域Prefixes.同时我推荐读者查找一下x86指令集查询手册,上面有所有的OpCode对应的助记符和指令构成五Prefixes67h,F2h,F3h在上一篇教程里我们学习了有关Prefixes的一般特性:1.所有的Prefixes都只有1个字节.2.在一个OpCode中可能会有多个Prefixes.3.如果Prefixes不能对随它之后的OpCode起作用,那么它会被处理器忽略.我们举例学习了Prefixes66h的作用和用法----切换默认操作数大小.现在我们学习另外的Prefixes,看看它们是否有什么好玩的地方可以影响我们的应用程序的执行.Prefixes可以被划分为5个集合:1.切换默认操作数大小(change DEFAULT operand size)(66h)2.切换默认地址大小(change DEFAULT address size/segment override)(67h)prefixprefix)(67h)3.重复(Rep)(F3,F2)4.切换默认段(change DEFAULT segment)(2e,36,3e,26,64,65)5.总线锁定(Bus lock)(F0)Prefixes66H我们已经在之前的教程里面学习过.一个测试题:下面哪一个指令可以在32位应用程序中拥有66H Prefix:sca**scaswscasd你可以在调试器里输入这些操作码来检验你的答案.(提示:答案是scasw)Prefixes67H----改变默认地址大小.输入助记符mov al,[eax]可以看到8A00MOV AL,[BYTE DS:EAX]现在再输入以67H开头的OpCode.67:8A00MOV AL,[BYTE DS:BX+SI]1.可以看到相应的字节已经被16位的寄存器bx,si寻址.在32位寻址模式中所有的数据都被32位的地址确定,包括32位基址,32位索引,32位偏移.在16位模式中这些都是16位的.2.可能你还注意到地址部分并不是变为mov al,[ax],而是变成了[bx][si].为什么呢?我们将在学习[modr/m]和[sib]的时候给出答案.现在我们可以暂时认为,在16位地址模式中无法像32位地址模式一样使用所有的基址寄存器和索引寄存器,OpCode用来指定寻址的寄存器的位域也是不同的.不知道你是否需要常在32位环境下使用16位的寻址模式,不用担心,我们会深入的学习这个.在32位环境下使用16位的寻址模式可能会非常高效,不过这个需要你确定控制的总的地址范围不能超过0FFFF(一个内存页面的大小4kb).Rep.Prefixes F2,F3如果你了解串操作指令(如movs,scas,lods等),你一定知道什么时候使用rep\repe\repne前缀.一些串操作指令只能使用前缀rep,如mov**,lod**----在计数器e(cx)=0的时候终止;另一些则可以使用repe或repne----在计数器变为0并且ZF标志位不满足前缀指定的条件的时候结束.换句话说repne在ZF=1而repe在ZF=0的时候终止.当然它们都会在满足ecx=0而当ZF=0或1的时候各有不同.有两点规则:1.你可以看到有3种Rep prefixes助记符:rep,repe,repne,但是只有2个OpCode:F2,F32.如果某些指令只使用前缀rep,那么这里的rep可以用repe或者repne来代替.这种情况下Rep Lod**Repe Lod**Repne Lod**按照同样的方式运行:它们会重复运行指令LOD**共ecx(cx)次,而不管prefix 是F2还是F3.我们可以验证一下:xor eax,eax;使ZF=0mov esi,esp;esi指向栈顶mov ecx,10repe lod**;opcode with repe and rep indentical-F3mov esi,espmov ecx,10repne lod**按F7或F8运行后可以看到它们的作用是一样的.只有在可能会改变某些标志位的重复串指令时,F2和F3才会表现出不同,如sca**.在这种情况下,有些指令与重复前缀操作搭配使用,F2和F3会把最后一位与标志位ZF进行比较,如果它们不相同,则重复串指令的操作将会结束.而有些指令不用进行这个比较的操作,因此标志位ZF对这些指令的运行结果无影响.只需将F2(11110010)和F3(11110011)用二进制表示你就会明白我的意思.(提示:F2(11110010)的最后一位是0,指令执行的时候CPU会将F2的最后一位0与ZF标志位比较,如果此时e(cx)为0,并且ZF为1与F2最后一位不同,则指令不再重复;否则重复.F3同理)下次我们将完成对prefixes的讨论并且我们将进一步学习改变EIP的OpCode.六Prefixes Segment override and LOCK 这次我们将结束对prefixes的学习.剩下的两个Prefixes是"Segment override"和"LOCK"prefixes.术语"Segment override"可能会让某些初学者感到困惑,但它却像是为Intel指令系统定制的.打开我们之前的试验用程序.输入:mov eax,[ebx]可以看到8B03MOV EAX,[DWORD DS:EBX]输入mov eax,GS:[ebx]可以看到65:8B03MOV EAX,[DWORD GS:EBX]65就是一个"segment override prefix",用来改变默认的段为GS.事实上,在使用内存中的数据时,处理器必须首先知道它的段地址和偏移量,但是如果在每个地方都要显式的直接指出段地址,那么在OpCode格式中就必须额外增加一个域,这将会比现有的OpCode体系多占用大量的字节,而且要处理器必须多花费额外的时钟周期来进行解码----无论在空间上还是时间上,都不值得!因此,为了解决这个问题,一个方案诞生了.指令按不同的定义被划分为不同的组,每个组各自有一个默认的段:CS:EIP寄存器ES:目的操作数是内存单元的串指令(movs,cmps等),在这里源操作数是储存在段DS里面.SS:堆栈操作(push,pop等)DS:剩下的数据操作指令.有了这个规则,处理器识别当前应该用哪个段将会变得非常简单而直接:如果有“Segment override prefix”,那么就使用这个prefix所指定的段;否则就使用默认的段.输入AC3E AC可以看到AC LODS[BYTE DS:ESI]3E:AC LODS[BYTE DS:ESI]3E是表示段DS,但是实际上在这里即使不直接指明3E,处理器也是会使用DS 的,因为DS是指令LODS的默认段.对于程序员来说,使用非默认的段将多占用1个字节,并且多花费1个时钟周期去解码.对于其他的那些改变默认内容的指令也如此(如66,67).编写Win32用户态汇编程序往往不需要去改变段寄存器,但底层程序员可能会用到.现在我们总结一下Win32用户态模式段寄存器的内容:CS:对于所有的用户态程序这个是一样的,在NT系列操作系统中是1Bh,而在9x中则为227h.如果你记得关于绝对地址跳转的宏,我可以给出一个简单的绝对地址跳转指令格式,但这个在9x中不同.我们一起看看远跳转的OpCode.EA--byte"code",这个字节告诉处理器这是一个直接远跳转的OpCode,当处理器遇到EA,他将得知后面会跟着一个48位地址,低。