首页 >> 百科

dosbox怎么用汇编(dosbox怎么用masm)

2022-07-09 百科 341 作者:admin

结合我在清华大学的学习经历,说说我的深刻体会。初见时,我以为汇编语言是一种助记符,一种低级语言。它直接面对指令并以人类的便利代替了二进制指令。以特殊格式为前缀的记忆字符串。每条汇编指令对应一条二进制指令。根据内核架构,不同的指令有不同的长度和格式。起初,大多数人认为汇编语言本身很简单,通用指令很少,语法规则也很少。看起来他们可以在几个小时的数据后看到,但事实并非如此。汇编的背后是架构,架构是编程除了各种高级形式之外最根本、最本质的解释。工作多年,除了长期从事安全工作的同学,我认为没有人精通装配。以及如何掌握编译?

1 早些年,我在汇编中手工编写病毒。比如在处理指令重定位的时候,真的是用汇编来计算指令的地址,然后push调用来实现函数调用。

长期病毒木马的二进制分析。了解各种恶意软件的原理,实现检测和预防。个别病毒需要修复。

3 漏洞挖掘。漏洞发现、详细的汇编级分析、漏洞利用编译、武器化漏洞利用、一站式流程。

4 种类型的逆向分析。好东西是没有代码的,在IDA里都能找到。

5 结交朋友。从不小看打印模式下的错误本地化。所有问题都在调试器中分析和理解,绝不是猜测。

6 编译器后端研究。什么指令选择、指令调度、寄存器分配都研究了。

7 低级开发、操作系统、设备驱动程序和虚拟化都在进行中。 X86好不好? ARM,再学习一下,比较一下。经过这一切,我终于敢于说话并了解大会。推荐使用以下文档,这些文档基本上对组装级别的许多常见计算机问题进行了全面的解释。 《x86-64 汇编语言 GNU/Linux 计算机组合简介》

学习组装并不意味着您需要做很多事情。关键是,全面的组装知识将使您真正了解计算机。另一方面,如上所述,您迟早会在工作的某个阴暗角落相遇。不管你承认与否,目前的 CPU 并不直接运行高级语言,甚至虚拟机也有类似汇编的指令集。当涉及到崩溃分析、性能优化甚至编译器耗尽时,汇编是您最后的手段。

先说一下汇编语言的基本内容:

目前国内的汇编语言教科书大都拿出大量的CPU、总线、寄存器、标志位……然后教汇编语言编程。这种类似字典的书写方式对初学者来说是非常不利的,因为在不知道它们的用途的情况下,往往很难将它们全部记住。但是这些概念仍然在编程中使用,所以我们不得不再次将书往前翻,这就导致了一个循环。

其实,学习汇编语言可以和学习高级语言一样。因为汇编语言是按照CPU的工作原理运行的,所以所有的代码都必须从CPU和内存的角度来考虑。了解了内存级别指令的执行过程后,编程就水到渠成了。

最简单的第一种:给定两个数A和B,让CPU做加法,结果存入C.C输出。

用 C 编写这个程序:

int a = 3;

int b = 4;

int c;

int main()

c = a+b;

("%d", c);

返回 0;

(注意:如果写成int c=3+4的话,一行就可以搞定。不过,这里不做,而是先统一声明所有变量,然后在执行操作的main函数。后面你可以看到,这种编程习惯符合二进制数据在内存中的存储规律。)

如果是用汇编语言写的,应该怎么写?

再重复一遍问题:给定两个数A和B,让CPU做加法,结果存入C.C输出。

原则上,要写好这个过程,必须解决以下问题:

①如何存储数据A和B ②如何相加 ③如何存储结果 ④如何输出结果。

下面将分别解决这四个问题。

1.汇编语言程序结构

首先,我们需要知道二进制信号在内存中的存储规则。众所周知,只有二进制信号才能被计算机直接处理。这些信号以高低电平存储在内存中,可以作为程序的指令或数据。存储在内存区的二进制信号是指令还是数据,取决于对应的命令。

CPU读取指令/数据时,每读取一条指令/数据,内存位置指针就加1,指向下一条指令/数据的内存地址。这就产生了一个问题:数据和指令应该在内存中分区并连续存储。否则,如果内存位置指针不知道下一个位置是数据还是代码,会给内存位置指针的寻址带来很大的不便。因此,在汇编程序中,需要手动将内存分为数据段、代码段、堆栈段和额外段。

经过这个除法之后,我们只需要告诉内存位置指针内存中每个段的起始地址,内存位置指针就可以成功寻址了。怎么讲?在 CPU 中,使用一组特殊的段寄存器来存储每个段的起始地址。它们是:DS(数据段的起始地址)、CS(代码段的起始地址)、ss(堆栈段的起始地址)、ES(附加段的起始地址)。编程时,程序员需要手动指定这些段寄存器对应于程序中的哪个段。

有了段落的概念,我们可以写出汇编器的基本框架如下:

数据段;定义了一个称为数据的段。数据既是段的名称,也是段的地址。不过这里并没有说明段是数据段、代码段还是别的什么。

线段结束;表示段的结束。 ENDS 是 END 的缩写。

堆栈段;定义一个叫做STACK的段,这个段的地址用STACK表示。

线段结束;段结束

代码段;定义一个叫做CODE的段,这个段的地址用CODE表示。

假设:CS:代码,DS:数据,SS:段;告诉编译器将代码中写入的每个段映射到每个段寄存器。这句话应该放在要用作代码段的段的开头。

线段结束;段结束

好的。回到我们的问题:如何存储 A 和 B?在数据部分声明变量如下:

数据段

A DW 03H 定义了一个双字节(即1 个字)的数据,命名为A,DW 是Word 的缩写。在十六进制末尾添加 h。

这相当于C语言中的int A=3,只不过int的范围比DW大很多。

B DW 04H定义了一个名为B的双字节数据。由于B是紧跟在A之后定义的,根据数据段的连续性,B在数据段中的偏移地址就是A在数据段中的偏移量移位地址+A的长度,由于A是双字节数据,所以A的长度是2个字。

线段结束

2.CPU运行模式及运行结果判断

第二个问题:加法怎么做?

CPU 只能处理电平信号。学过模拟电子学的人都知道,有一种东西叫做“加法器”。当输入两个电压信号时,经过运算放大器后,将得到这两个信号的和。所以CPU做加法的方式就是将两个二进制信号输入加法器,得到结果。

问题似乎已解决。但突然间,我们发现这样的结果几乎毫无意义,因为我们无法知道结果的性质。例如,如果结果超过允许的最大位数(溢出)会怎样? CPU 不提示。或者,如果我们想比较两个数字的大小,我们必须将它们相减。然而,结果是否定的?我们无从得知。

为了了解运算结果的性质,在CPU中设置了一个“符号寄存器”,专门用来存放运算结果的各种符号。所有这些都在电路中实现。例如:

CF(进位标志)用于标记无符号数运算是否产生进位。当产生进位时,CF=1,否则CF=0。特别是 CF 标志的值对于有符号数的运算没有任何意义。

OF(溢出标志)用于标记有符号算术运算是否发生溢出。当发生溢出时,OF=1,否则OF=0。同样,OF 标志的值对于无符号数的运算也是没有意义的。

SF(Sign Flag)用于标记正负结果。当结果为负(SF)时,SF=1。否则 SF=0。

回到我们的问题:如何做加法?或者更笼统地说,如何进行手术?

我们不需要关心具体电路实现的细节,只需执行相应的操作指令即可。运算完成后,不仅会得到结果,而且每个标志位的值也可能会发生相应的变化,这有利于我们对结果的判断。例如:

添加斧头,BX;将 AX 和 BX 的内容相加,并将结果存储在 AX 中。如果 ax 和 bx 是有符号数,则 of = 1 的值。CF 不确定何时发生溢出。结果为负时,SF=1。

3.内存和寄存器的关系

内存 (RAM) 是存储各种数据和指令的地方。根据不同的用途,可以分为不同的段。寄存器是 CPU 中临时存储操作结果的地方。与更大的存储器相比,寄存器非常小(每个寄存器只有 16 位),数量有限(只有少数),用途单一(每个寄存器有不同的用途来存储不同的结果)。例如,上述的段寄存器(DS、CS、SS、ES)用于存储段的起始地址。除了段寄存器外,CPU 中还有通用寄存器(AX、BX、CX、DX...)。它们都有自己的特殊用途,也可以用于存储数据或计算结果而不会发生冲突。

通用寄存器的用途简述如下:(所有通用寄存器都有16位容量)

轴:①用于存储数据或计算结果。

②AX的高8位AH用于与DOS操作系统通信。将DOS系统的指令代码加载到AH中并执行。有些操作可以在DOS系统下完成,比如在屏幕上输出字符。

DX:①用于存储数据或计算结果。

②与AH的DOS屏幕输出指令码配合使用,存储要输出到屏幕的数据。

CX:在有循环的程序中,用来存储循环的次数。相当于for循环中的count变量I。

BX、SI、Di:①用于存储数据或运算结果。

(2)用于存放段内数据段的偏移地址。

一般来说,要计算的数据是存储在内存中的。在 CPU 的指令下,它们的位置由指针确定并读入寄存器。运算完成后,将结果返回到内存中保存的结果位置。

回到我们的问题,内存的数据段中存储了两个双字节数据A=3和B=4。将它们读入寄存器进行加法,然后将结果写入内存。为了读取寄存器,首先需要获取内存数据段中A和B的偏移地址。确定它们的地址后,根据它们的地址读入寄存器(这里可以选择两个寄存器),然后执行操作指令。操作完成后,将存储在寄存器中的结果写入内存数据段中的保留位置。源到目标分配是使用“MOV 目标,源”指令完成的。代码如下:

数据段

A DW 03H

B DW 04H

C DW? ;?声明但不分配。相当于 int c;

线段结束

堆栈段

线段结束

代码片段

假设:CS:代码,DS:数据,SS:段

开始:;指定程序的入口位置(当然这个位置必须在代码段中)并命名为START。

移动轴,数据

MOV DS, AX;这两行表示将数据段的起始地址(由句柄数据表示)送入数据段寄存器DS,以通用寄存器AX为中介。在需要使用数据段的程序中,这一步是必须的,否则CPU无法确定数据段的位置。注意:是伪代码,只是告诉编译器每个段和段寄存器的对应关系,不存储每个段的地址。 (由于电路结构,AX不能直接给DS赋值,不能直接写MOV DS数据。)

LEA SI, A;注意:与数据段中数据的含义不同,数据段中 DW 03H 中的 A 只代表变量名,不代表变量的地址。和 int a=3 类似,a 只是一个名字,应该用 a 来获取地址。在汇编中,地址的格式为 LEA BX/SI/DI,a。注:BX、SI、DI寄存器一般用于存放数据的偏移地址。

MOV AX, [SI];将【SI在内存数据段中的偏移地址的内容】发送到通用寄存器AX。 AX也可以换成BX、CX等。

INC SISI 值加 1。SI++。

INC SI 因为A是双字节数据,所以SI需要在下一个数据b的偏移地址上加2,不过这两条也可以换成Lea Si,b。

MOV·BX[SI];将【SI在内存数据段中的偏移地址的内容】——即B,发送到通用寄存器BX中。

添加BX,斧头;添加到 BX,结果存储在 BX 中。当然也可以写BX·plus AX。但是不存储在AX中的原因是后面的输出需要AH来避免冲突。

莱迪点

MOV[di], BX;这两个函数将存储在 BX 中的结果写回数据段中的位置 C。

BX MOV DX;准备在屏幕上输出结果。屏幕的输出是寄存器DX的内容。

加DX,30H 文本输出到屏幕时,数字+30H必须转换成对应的ASCII码。

MOV ah, 02H 将控制DOS系统输出值的代码02H加载到AH中。这一步可以理解为()中的“%d”

INT = 中断,中断,执行 DOS 命令。执行返回程序。

MOV 啊,4CHCH是结束程序返回DOS系统的命令。将命令加载到 AH 中并等待执行。

INT 21H被中断,DOS系统执行4CH命令。程序结束。这两个步骤类似于“返回0”

线段结束

结束开始;在程序结束时,告诉 MASM(宏程序集)编译器程序的入口位置运行在标签 START 处。结果如下:

输出为7。当然,你可以使用其他汇编IDE集成开发环境代替MASM编译器。

最后,希望这篇短文可以帮助你更好的理解、理解和学习这门语言。

关于我们

最火推荐

小编推荐

联系我们


Copyright 8S新商盟 Rights Reserved.
联系YY号:2949821684
邮箱:chenjing919994@sohu.com
备案号:浙ICP备2023016511号-1