汇编基础最后一篇
这是汇编语言基础最后一篇,以后还会更新更高级的汇编知识,并且这部分知识会应用到
逆向编程的环节,这一章介绍汇编基础--机器指令。
一个16比特位的汇编指令:
opcode操作码占用3个比特位,可以表示2的3次方为8种操作
寄存器占用2个比特位,可表示2的2次方为4种可用寄存器
地址空间为2的11次方为2048个可能的内存单元地址可用。
inc指令和dec指令
从图中可以看出这些指令长度仅为一字节,因为每个指令地址相差为1字节。
inceax这条指令的机器码为40, 40为16进制表示,转为二进制为01000000
incecx这条指令机器码为41,41位16进制表示,转换为二进制为01000001
依次展开会发现机器码的规律,开头都为01000XXX
而XXX实际就是寄存器在机器中表示的二进制机器码。
下面是各个寄存器的机器码:
下面看下dec寄存器指令图:
dec寄存器指令的格式可以总结为
01001xxx,xxx为寄存器二进制指令格式。
mov 指令
moveax,reg指令图
每个指令长度为2字节,从地址偏移可以看出。
将上面指令机器码转为二进制
movecx,eax 十六进制为8BC8,二进制表示第二个字节11001000
movecx,ecx 十六进制为8BC9,二进制表示第二个字节为11001001
通过对比二进制,发现第一个字节都为8B,第二个字节分别为C0,C1,C2,C3...C9
movreg,reg指令格式为
10001011 11XXXYYY
XXX为目的寄存器,YYY为源寄存器
movreg,imm即将一个立即数移动到寄存器中指令的机器码会是什么样呢?
下图为将立即数移动到寄存器的图示:
moveax,1指令机器码为B800000001
movecx,10指令机器码为B90000000A
两条指令地址相差5个字节,每个字节8bit,可计算出每条指令为40bit长度。
即10个十六进制数表示。而 B800000001和 B90000000A恰巧为10个16进制数字组成。
机器码B800000001 从左向右数,去掉B8占用的一个字节,剩下的四个字节可以看出用来表示
立即数1。同样的道理,可以看出 B90000000A也是这个原理。
如果移动的为负数,怎么表示呢?
movedx,-1这个指令我们分析一下-1在机器中的表现形式
负数在机器中以补码的形式表现,-1的补码计算规则为:
1的源码为00000000000000000000 000000000001
按位取反为11111111111111111111111111111110
末尾+1位11111111111111111111111111111111
转换为十六进制为FFFFFFFF恰好就是机器码的最后三个字节表示。
同样的道理适用于movebx,-10
下面分析前两个字节BA,BB,B8,B9分别有什么关联。
同步对比可以看出前几个比特位是一样的,都为10111
后三个比特位分别为000, 001,010,011,这四个二进制码恰好为
几个寄存器的二进制表示方法。所以
movreg,imm机器指令为
10111XXX YYYYYYYY YYYYYYYYYYYYYYYYYYYYYYYY
movreg,mem将内存数据移动到寄存器中的机器指令怎么表示?
movmem,reg 将寄存器中的数据移动到内存中,机器指令如何表示?
下图定义了变量num1,num2,num3
num1地址为00000000
num2地址为00000004
num3地址为00000008
下图为指令对应的机器码:
除去A1,A3开始的一个字节,剩下的4个字节分别为十六进制4和十六进制8,分别为num2的地址
和num3的地址。
下面分析第一个字节A1和A3规律:
可以得出结论,无论将内存数据移动到eax中,还是将eax中的数据移动到内存中,
最后的4个字节表示的都是内存的地址,第一个字节表示的不同,用来表示两种移动方式的区别。
总结规律如下:
moveax,mem10100001YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY
movmem ,eax10100011YYYYYYYY YYYYYYYY YYYYYYYY YYYYYYYY
下面图表表示了ebx,ecx,edx三种寄存器和内存数据移动指令
可以看出其他寄存器(eax,ecx,edx)和内存之间移动数据的操作指令大小为6字节,多出的为第二字节,
0D,15,1D。
第一字节8B表示从内存移动数据到寄存器,89表示从寄存器移动到内存,如下图所示:
第二个字节图表如下:
通过二进制可以看出前两位都为00,中间三位为001,010,011分别表示ecx,edx,ebx
最后三位为101,其实这个字节不仅仅用于表示移动,还可以表示很多操作,因为ecx为循环控制,
ebx为基址寄存器,edx可用于存余数等等,所以前两位为00,且最后三位为101,这个组合表示移动
操作。中间三位表示操作的寄存器是什么。
该字节概括为如下图所示:
mod字段为00,且r/m字段值为101,它表示地址模式数据置换,也就是指向内存地址模式。
add指令sub指令
add指令很简单,给出图表读者自己分析。
字节转为二进制可以看出从右往左数第3到1位为第二个操作的寄存器,
从右往左数第6到4位表示第一个操作的寄存器,两个寄存器操作模式为第7~16位所表示。
movoffset指令 lea指令
num2的值为5,地址为00000004,分别将num2的内容移动到esi和edi,再通过lea指令将num2地址
放入esi和edi
通过对比可以看出后四个字节都为00000004,但是前两个字节是不一样的。将前两个字节展开
为二进制
由于movesi,num2是将num2数据存入esi,而leaesi,num2是将num2地址放入esi,所以
第一个字节的倒数第二位不同,第一个字节分别为10001011 ,10001101,mov和lea第二个字节是相同的。
下面对比两个lea指令前两个字节 ,第一个字节是相同的,第二个字节为00110101 和00111101
第二个字节中间三位不同,分别为110(esi),111(edi)表示寄存器。
所以可以总结一下,mov指令和lea指令区别在于第一个字节,计算机用第一个字节区别mov和lea指令。
计算机用第二个字节中间三位区别lea指令操作的不同寄存器。
第二个字节和我们上面说过的:
mod字段为00,且r/m字段值为101,它表示地址模式数据置换,也就是指向内存地址模式。
下面看一下movesi,offsetmem和movedi,offsetmem两条指令。
可以看出movoffset指令为5个字节,比mov和lea指令少了一个字节,因为movoffset仅仅在编译的时候加载地址,
所以不需要lea的第二个字节表示数据移动操作。movoffset是静态的。
movoffset指令esi和edi区别仅仅在第一个字节,展开后可以看到:
第一个字节的后三位分别为110(esi),111(edi)。
可以得出结论movoffset的指令第一个字节后三位区别esi还是edi,其余不变。
jmp指令
看一则jmp指令操作
jmp指令机器码为EB+相对偏移地址,
如jmp around为EB04,通知计算机跳转到当前指令指针位置+4字节的位置,
需要普及一个知识,当程序运行的时候,指令指针或者CPU中的指令指针指向下一条将要取到CPU中
被后续执行的指令。当运行到jumparound时,指令指针实际指向了地址000000D8,指令above:nop的位置,
EB04指向为000000DB加上4个字节地址即为000000DC,恰好是around:nop指令地址。
符合逻辑。
下面看下jumpabove指令会跳转到哪里。
EBFC指令FC为11111100,该数值为某个负数的补码,负数补码的计算规则为
符号位不变,其他位按位取反末位+1。
同样的道理,负数补码转为原码,符号位不变,按位取反末位+1
11111100符号位不变,按位取反10000011,末位+1,
变为10000100表示-4.
jmpabove机器指令EBFC跳转到指令指针地址向前移动四个字节的位置。
jmpabove指令运行时,指令指针指向下一条将要取出的指令位置,即000000DC,
000000DC-4为00000008,即above:nop的位置。
到此为止机器指令的知识介绍完毕,以后会介绍高级汇编和反汇编的知识,
汇编基础介绍告一段落。
我的微信公众号,谢谢关注。