汇编语言也如其他编程语言一样,是一门可以进行开发作业的语言。然而与其他“高级”语言不同的是,汇编语言是可以直接工作于硬件之上的。并且不需要像其他高级语言一样,先要翻译成处理器可以执行的指令才能被执行。如果我们足够了解硬件系统,那么我们可以使用汇编做到许多其他语言做不到的事情。
汇编语言因为最初的开发目的就是代替近乎无法识别的机器语言,并且年代久远。所以也许并不如现代的诸多高级语言那么接近于自然语言。汇编语言是以助记符形式存在的,可能在刚开始的学习中并不是那么的友好,但是在深入过后你会发现,汇编语言的威力以及魅力是其他程序语言无法代替的。
汇编语言和其它可以跨平台的编程语言(例如C、C++、python)只需要更改少量代码,甚至不需要修改代码就可以运行在其他平台不同。每个平台的汇编语言都对应着该平台的机器语言指令集,并且助记符并不尽相同,所以并没有办法直接移植到其他平台。
现代汇编主要被应用于底层、硬件操作,驱动开发、嵌入式系统以及用于高度依赖效率程序的关键代码优化。(添加实际例子,说明每种方向都有哪些成名作)
但是我们学习汇编的目的基于另一个目的,就是为我们学习信息安全所服务,在二进制安全的学习中,我们往往得不到源代码,这也就是说,我们需要对程序进行反汇编,来进行代码分析,这个时候我们的汇编知识,系统知识,硬件知识有多扎实,就可以体现出来了,虽然有人会说IDA pro可以直接转换汇编成伪C代码。但是当一个程序加了壳之后,这就是完全做不到的,也许还会有人说脱壳,那么很好,这个时候又需要动态调试了,代码依然是汇编代码。
学习本套课程需要具有基本的计算机常识,如果学习过一门编程语言,可以理解编程思想,那么是最棒的。当然,如果没有学过其他编程语言,也是可以直接学习本门课程的。
SDK(软件工具包)Software Development Kit一般都是一些软件工程师为特定的软件包、软件框架、硬件平台、操作系统等建立应用软件时的开发工具的集合。
API(Application Programming Interface,应用编程接口)其实就是操作系统留给应用程序的一个调用接口,应用程序通过调用操作系统的 API而使操作系统去执行应用程序的命令
所以Windows的SDK就是微软提供给我们的软件工具包,我们可以通过API调用其中的命令,执行相关的操作。
本套课程在学习x86汇编的时候将会使用Windows系统以及Visual Studio 集成开发环境,16位以及x64汇编等到学习的时候再决定使用什么环境。
说到汇编语言的产生,首先要讲一下机器语言。机器语言是机器指令的集合。机器指令展开来讲就是一台机器可以正确执行的命令。电子计算机的机器指令是一列二进制数字。计算机将之转变为一列高低电平,以使计算机的电子器件受到驱动,进行运算。
上面所说的计算机指的是可以执行机器指令,进行运算的机器。这是早期计算机的概念。在我们常用的PC机中,有一个芯片来完成上面所说的计算机的功能。这个芯片就是我们常说的CPU(Central Processing Unit,中央处理单元)。每一种微处理器,由于硬件设计和内部结构的不同,就需要用不同的电平脉冲来控制,使它工作。所以每一种微处理器都有自己的机器指令集,也就是机器语言。
早期的程序设计均使用机器语言。程序员们将用0, 1数字编成的程序代码打在纸带或卡片上,1打孔,0不打孔,再将程序通过纸带机或卡片机输入计算机,进行运算。这样的机器语言由纯粹的0和1构成,十分复杂,不方便阅读和修改,也容易产生错误。程序员们很快就发现了使用机器语言带来的麻烦,它们难于辨别和记忆,给整个产业的发展带来了障碍,于是汇编语言产生了。
汇编语言的主体是汇编指令。汇编指令和机器指令的差别在于指令的表示方法上。汇编指令是机器指令便于记忆的书写格式。
1234567 操作:寄存器BX的内容送到AX中1000100111011000 机器指令mov ax,bx 汇编指令
此后,程序员们就用汇编指令编写源程序。可是,计算机能读懂的只有工作过程
机器指令,那么如何让计算机执行程序员用汇编指令编写的程序呢?这时,就需要有一个能够将汇编指令转换成机器指令的翻译程序,这样的程序我们称其为编译器。程序员用汇编语言写出源程序,再用汇编编译器将其编译为机器码,由计算机最终执行。
.386 .MODEL flat, stdcall option casemap:none includelib user32.lib includelib kernel32.lib ExitProcess PROTO, dwExitCode : DWORD MessageBoxA PROTO hWnd : DWORD, lpText : BYTE, lpCaption : BYTE, uType : DWORD .data Number DWORD 0 text db "shellcode",0 .code main proc push 0 push offset text push offset text push 0 call MessageBoxA sub esp,16 call ExitProcess main ENDP END main
Win32环境工作在80386级以上的处理器中,所以.386这一句必不可少
.model 内存模式[,语言模式][,其他模式]
.model flat,stdcall
flat代表的内存使用方式:Win32程序使用的模式,代码和数据段使用同一个4GB段
stacall通常用于win32API,该方式由被调用者清理栈
在Win32汇编程序中,需要定义的只有option casemap:none,这个语句定义了程序中的变量名和子程序是否对大小写敏感,由于Win32 API中的API名称是区分大小写的,所以必须指定这个选项,否则在调用API的时候会有问题
.data,.data?和.const都是数据段,.code是代码段,.stack是堆栈段,这些都是分段伪指令,因为在windows汇编中不需要考虑堆栈,系统会为程序分配一个向下扩展的,足够大的段作为堆栈段,所以.stack段定义常常被忽略.
.data,.data?和.const定义的是数据段,分别对应不同方式的数据定义,在最后生成的可执行文件中也分别放在不同的节区(Section)中,程序中的数据定义一般可以归为三类.
4.1.1
第一类是可读可写的已定义变量,这些数据在源程序中已经被定义了初始值,而且在程序的执行中有可能被改变,如一些标志等,这些数据必须定义在.data中,.data段是已初始化数据段,其中定义的数据是可读可写的,在程序装入完成的时候,这些值就已经在内存中了,.data段一般存放在客户自行文件的_DATA节区里.
4.1.2
第二类是可读可写的未定义变量.这些变量一般是当做缓冲区或者在程序执行后才开始使用的,这些数据可以定义在.data段中,也可以定义在.data?段中,.但一般它放到.data?段中.虽然定义这两种段都可以正常使用,单定义在.data?段中不会增大.exe文件的大小.比如,需要一个100KB的缓冲区,可以使用下面的语句来定义:
szBuffer db 100 * 1024 dup (?)
这个语句如果放在.data段中,编译器认为这些数据在程序装入时就必须有效,所以它在生成可执行文件的时候保留了所有的100KB的内容,即使它们是全零!假设程序其他部分的大小是50KB,那么最后程序的总大小就会是150KB大小,如果缓冲区定义为1M,那么.exe文件的大小就会增加到1050KB..data段则不同,其中内容编译器会认为程序在开始执行后才会用到,所以在生成客户自行文件的时候只保留了大小信息,不会为它浪费磁盘空间,在与上面的相同情况下,即使缓冲区的定义为1M,可执行文件的大小也只会有50KB!总之,.data?段是未初始化数据段,其中的数据也是可读可写的,但在可执行文件中不占空间,.data?段在可执行文件中一般存放在_BSS节区中.
4.1.3
第三类数据是一些常量.如一些要显示的字符串信息,它们在程序装入的时候也已经有效,但是在整个执行过程中不需要修改,这些数据可以放在.const段中,.const段是常量段,它是可读不可写的.为了方便起见,在小程序中常常把常量一起定义到.data段中,而不另外定义一个.const段,在程序中不小心用了.const段中的数据做写操作的指令,会引起保护错误,windows会弹出错误提示框并且结束程序.
如果不怕可读性不佳的话,也可以把.const段中定义的内容混到.const段中去也可以正常使用,因为.code段也是可以读的.
.code段是代码段,所有指令都必须写在代码段中,在可执行文件中,代码段一般都是放在_TEXT节区中的.Win32环境中的数据段是不可执行的只有代码段有可执行的属性,对于工作在特权3的应用程序来说,.code段是不可写的,在编写DOS汇编程序的时候,一些好事的程序员往往有个习惯,就是靠改动代码段中的代码来做一些反跟踪的事情,如果企图在Win32汇编做同样的事情,那么只会有一个下场,弹出报错框,非法操作.
在程序不必要定义堆栈段,系统会自动分配空间,唯一值得一提的就是,堆栈段的内存属性是可读可写可执行的.
在C语言中,程序员不必显式的指定程序由哪里开始执行,编译器已经约定好从main开始执行函数,而在汇编中,并没有main函数.程序员可以在代码段的任意位置开始执行,这个地方由程序的最后一句end语句来指定
end[开始地址]
end start
上述两句程序从start这个标号 开始执行程序
但是,一个源程序不必非要指定入口标号,这时候可以把开始地址忽略不写,这种情况发生在编写多模块程序的单个模块的时候,当分开写多个程序模块时,每个模块的源代码里都可以包括各种分段,只是其他模块最后的end语句必须不带开始地址,当最后把多个模块链接在一起的时候,只有一个主模块指定入口地址,在多个模块中指定入口地址,或者没有一个模块指定入口地址,链接程序都会报错