Day 1 《进制》
#
(一)进制意义:
1.定义:
二进制:0 1 逢2进1
八进制:0 1 2 3 4 5 6 7 逢8进1
十进制:0 1 2 3 4 5 6 7 8 9 逢10进1
十六进制:0 1 2 3 4 5 6 7 8 9 A B C D E F 逢16进1
当然,除了常规的,还能3进制、4进制…..甚至,进制的符号不一定是常规的123的顺序;
我们可以这样定义一个5进制: 9 7 5 3 1 逢5进1; 则该进制的79 = 6(10进制),57 = 11(10进制),39 = 15(10进制);
(如果将上面的符号用于加密,将会给解密者带来极大的麻烦!!!)
2.二进制与十六进制:
计算机中的任何信息都是以二进制的形式存储的。
由于二进制书写复杂,大部分软件都将计算机中的数据以16进制展示。
任何 1 个十六进制的符号都有一个对应的 4 个二进制符号。
且16进制是2进制的简写,1个16进制数 == 4个2进制数:
二进制: 0 1 10 11 100 101 110 111 1000 1001 1010 1011 1100 1101 1110 1111
十六进制: 0 1 2 3 4 5 6 7 8 9 A B C D E F
3.练习:
a. 2+3 = 1,成立吗?说明理由:
答:成立,假如定义一个4进制数 4 2 3 1 5 ,逢5进1,则4 = 0(10进制),2 = 1(10进制),3 = 2(10进制),1 = 3(10进制);
那么2+3=1 => 1+2=3;
b. 将下面的二进制数用16进制表示:
1100 1011 0101 0100 1110 1011 0101 0111 1011 0100 1010 1011
答: C B 5 4 E B 5 7 B 8 A B
c. 将下面的十六进制数用2进制数表示:
4 8 7 F D C 1 2 0 A C E 6 9 B 9 5 3 F E
答:0100 1000 0111 1111 1101 1100 0001 0010 0000 1010 1100 1110 0110 1001 1011 1001 0101 0011 1111 1110
d.二进制从0写到100(是100个数,每行写10个)
答: 1、 10、 11、 100、 101、 110、 111、 1000、 1001、 1010
1011、 1100、 1101、 1110、 1111、 10000、 10001、10010、10011、10100
10101、10110、10111、11000、11001、11010、11011、11100、11101、11110
11111、100000、100001、100010、100011、100100、100101、100110、100111、101000
101001、101010、101011、101100、101101、101110、101111、110000、110001、110010
110011、110100、110101、110110、110111、111000、111001、111010、111011、111100
111101、111110、111111、1000000、1000001、1000010、1000011、1000100、1000101、1000110
1000111、1001000、1001001、1001010、1001011、1001100、1001101、1001110、1001111、1010000
1010001、1010010、1010011、1010100、1010101、1010110、1010111、1011000、1011001、1011010
1011011、1011100、1011101、1011110、1011111、1100000、1100001、1100010、1100011、1100100
(二)进制运算法:
1.八进制:
加法表:
1+1=2 1+2=3 1+3=4 1+4=5 1+5=6 1+6=7 1+7=10
2+2=4 2+3=5 2+4=6 2+5=7 2+6=10 2+7=11
3+3=6 3+4=7 3+5=10 3+6=11 3+7=12
4+4=10 4+5=11 4+6=12 4+7=13
5+5=12 5+6=13 5+7=14
6+6=14 6+7=15
7+7=16
乘法表:
1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 1*6=6 1*7=7
2*2=4 2*3=6 2*4=10 2*5=12 2*6=14 2*7=16
3*3=11 3*4=14 3*5=17 3*6=22 3*7=25
4*4=20 4*5=24 4*6=30 4*7=34
5*5=31 5*6=36 5*7=43
6*6=44 6*7=52
7*7=61
2.加减乘除法则:
3.二进制:
4.练习:
1. 9进制定义:由9个符号组成,分别是2、9、1、7、6、5、4、8、3,逢9进1;
计算: 123+234 = 725;
2. 10进制定义:由10个符号组成,分别是:! @
计算:@
$B + %AC& = &!@%;
3. 3进制定义:由3个符号组成,分别是2、0、1,逢3进1;
2 0 1
02 00 01
12 10 11
022
计算:12 + 02 = 022;
4. 7进制的定义:由7个符号组成,分别是:8、3、4、2、9、5、6,逢7进1;
8 3 4 2 9 5 6
38 33 34 32 39 35 36
48 43 44 42 49 45 46
28 23 24 22 29 25 26
98 93 94 92 99 95 96
58 53 54 52 59 55 56
388 383
计算:92 + 39 = 68;
2017/5/10 17:37:35
Day 2 《数据宽度&逻辑运算》
#
(一)数据宽度:
1.数据宽度意义:
a.计算机中,受制于硬件条件,数据都是有长度的,超过最大宽度的数据会被丢失;
b.其中重要的有这几种数据宽度,类型为:
BYTE 字节 8bit 1字节
WORD 字 16bit 2字节
DWORD 双字 32bit 4字节c.数据类型图解:
2.逻辑运算:
a.或运算(or |) 只要有一个为1,则为1; 0101 | 0001 = 0101;
b.与运算(and &) 必须要两个为1,才为1; 0101 & 0001 = 0001;
c.异或(xor ^) 两者互为不同,才为1; 0101 ^ 0001 = 0100;
d.非(not !) 1是0,0是1; !0101 = 1010; !0001 = 1110;
3.cpu的加法计算:
4.简单加密与解密:
5.本节作业:
八进制数2-5在计算器中的结果是:177777777777777777777777775,为什么?
答:
使用异或对87AD6进行加密后再进行解密,加密密钥:5;
答:
只用逻辑运算计算 2-3 = ?(涉及内容:逻辑运算、移位、数据宽度)
答:
6.寄存器
2017/5/11 16:11:16
Day 3 《寄存器》
(一)通用寄存器
1.含义:
通用寄存器是寄存器中的一种,有些寄存器是专用的,而没有指定专门用途的寄存器,就是通用寄存器;
汇编就是:不停的移动数据,在内存和寄存器之间,寄存器和寄存器之间,移动得越复杂,程序越复杂。如果弄清楚这些数据的流动,就可以知道程序的流程,逆向就是要知道数据如何移动的;
2.32位寄存器分类:
(二)寄存器操作语法
1.MOV 目标操作数, 源操作数
作用:拷贝源操作数到目标操作数
#include <stdio.h>
int main()
{
unsigned char m8 = 0;
short m16 = 0;
int m32 = 0;
//r:通用寄存器 m:内存 imm:立即数 r8:8位通用寄存器 m8:8位内存 imm8:8位立即数
__asm {
mov ah, 0x12 //MOV r8, imm8
mov ax, 0xabcf //MOV r16, imm16
mov eax, 0x13579BDF //MOV r32, imm32
mov al,ah //MOV r/m8, r8
mov m8,ah
mov bx,ax //MOV r/m16, r16
mov m16,ax
mov ebx,eax //MOV r/m32, r32
mov m32,eax
mov bl,al //MOV r8, r/m8
mov al,m8
mov bx,ax //MOV r16, r/m16
mov ax,m16
mov ebx,eax //MOV r32, r/m32
mov eax,m32
}
return 0;
}
1.源操作数可以是立即数、通用寄存器、段寄存器、内存单元
2.目标操作数可以是通用寄存器、段寄存器或者内存单元
3.操作数的宽度必须一样
4.源操作数和目标操作数不能同时为内存单元
2.ADD 目标操作数, 源操作数
作用:将源操作数值加到目标操作数上
#include <stdio.h>
int main()
{
unsigned char m8 = 5;
short m16 = 4;
int m32 = 3;
//r:通用寄存器 m:内存 imm:立即数 r8:8位通用寄存器 m8:8位内存 imm8:8位立即数
__asm {
add al, 0x10 //ADD r/m8, imm8
add m8, al
add ax, 0x1000 //ADD r/m16, imm16
add m16, ax
add eax, 0x10000000 //ADD r/m32, imm32
add m32, eax
add ax, 0x10 //ADD r/m16, imm8
add m16, 0x10
add eax, 0x10 //ADD r/m32, imm8
add m32, 0x10
add ah, al //ADD r/m8, r8
add m8, al
add bx, ax //ADD r/m16, r16
add m16, ax
add ebx, eax //ADD r/m32, r32
add m32, eax
add bh, m8 //ADD r8, r/m8
add bx, m16 //ADD r16, r/m16
add ebx, m32 //ADD r32, r/m32
}
return 0;
}
1.目标操作数的宽度和源操作数的宽度必须一样,当源操作数为立即数时,可不一致。
2.立即数作为源操作数,可以比目标数小,也可以比目标数大。超出的位丢失,缺少的位补0。
3.SUB 目标操作数, 源操作数
作用:目标操作数减去源操作数
#include <stdio.h>
int main()
{
unsigned char m8 = 5;
short m16 = 4;
int m32 = 3;
//r:通用寄存器 m:内存 imm:立即数 r8:8位通用寄存器 m8:8位内存 imm8:8位立即数
__asm {
sub al, 0x10 //SUB r/m8, imm8
sub m8, al
sub ax, 0x1000 //SUB r/m16, imm16
sub m16, ax
sub eax, 0x10000000 //SUB r/m32, imm32
sub m32, eax
sub ax, 0x10 //SUB r/m16, imm8
sub m16, 0x10
sub eax, 0x10 //SUB r/m32, imm8
sub m32, 0x10
sub ah, al //SUB r/m8, r8
sub m8, al
sub bx, ax //SUB r/m16, r16
sub m16, ax
sub ebx, eax //SUB r/m32, r32
sub m32, eax
sub bh, m8 //SUB r8, r/m8
sub bx, m16 //SUB r16, r/m16
sub ebx, m32 //SUB r32, r/m32
}
return 0;
}
1.目标操作数与源操作数的宽度一定要一致,立即数除外;
2.立即数作为源操作数,可以比目标数小,也可以比目标数大。超出的位丢失,缺少的位补0;
4.AND 目标操作数, 源操作数
作用:位运算&,将目标操作数和源操作数进行&运算,并将值存给目标操作数;
#include <stdio.h>
int main()
{
unsigned char m8 = 5;
short m16 = 4;
int m32 = 3;
//r:通用寄存器 m:内存 imm:立即数 r8:8位通用寄存器 m8:8位内存 imm8:8位立即数
__asm {
mov eax,0xFFFFFFFF
and al, 0x11 //AND r/m8, imm8
and m8, al
and ax, 0x1111 //AND r/m16, imm16
and m16, ax
and eax, 0x11111111 //AND r/m32, imm32
and m32, eax
/*源操作数为立即数时,其宽度可大于可小于目标操作数的宽度*/
and ax, 0x10 //AND r/m16, imm8
and m16, 0x10
and eax, 0x10 //AND r/m32, imm8
and m32, 0x10
and ah, al //AND r/m8, r8
and m8, al
and bx, ax //AND r/m16, r16
and m16, ax
and ebx, eax //AND r/m32, r32
and m32, eax
and bh, m8 //AND r8, r/m8
and bx, m16 //AND r16, r/m16
and ebx, m32 //AND r32, r/m32
}
return 0;
}
5.OR 目标操作数, 源操作数
作用:位运算|,将目标操作数和源操作数进行 | 运算,并将值存给目标操作数;
#include <stdio.h>
int main()
{
unsigned char m8 = 5;
short m16 = 4;
int m32 = 3;
//r:通用寄存器 m:内存 imm:立即数 r8:8位通用寄存器 m8:8位内存 imm8:8位立即数
__asm {
mov eax, 0x00000000
or al, 0x11 //OR r/m8, imm8
or m8, al
or ax, 0x1111 //OR r/m16, imm16
or m16, ax
or eax, 0x11111111 //OR r/m32, imm32
or m32, eax
/*源操作数为立即数时,其宽度可大于可小于目标操作数的宽度*/
or ax, 0x10 //OR r/m16, imm8
or m16, 0x10
or eax, 0x10 //OR r/m32, imm8
or m32, 0x10
or ax, 0x1000
or m8, 0x1000
or ah, al //OR r/m8, r8
or m8, al
or bx, ax //OR r/m16, r16
or m16, ax
or ebx, eax //OR r/m32, r32
or m32, eax
or bh, m8 //OR r8, r/m8
or bx, m16 //OR r16, r/m16
or ebx, m32 //OR r32, r/m32
}
return 0;
}
6.XOR 目标操作数, 源操作数
作用:位运算^,将目标操作数和源操作数进行 ^ 运算,并将值存给目标操作数;
#include <stdio.h>
int main()
{
unsigned char m8 = 5;
short m16 = 4;
int m32 = 3;
//r:通用寄存器 m:内存 imm:立即数 r8:8位通用寄存器 m8:8位内存 imm8:8位立即数
__asm {
mov eax, 0x00000000
xor al, 0x11 //XOR r/m8, imm8
xor m8, al
xor ax, 0x1111 //XOR r/m16, imm16
xor m16, ax
xor eax, 0x11111111 //XOR r/m32, imm32
xor m32, eax
/*源操作数为立即数时,其宽度可大于可小于目标操作数的宽度*/
xor ax, 0x10 //XOR r/m16, imm8
xor m16, 0x10
xor eax, 0x10 //XOR r/m32, imm8
xor m32, 0x10
xor ax, 0x1000
xor m8, 0x1000
xor ah, al //XOR r/m8, r8
xor m8, al
xor bx, ax //XOR r/m16, r16
xor m16, ax
xor ebx, eax //XOR r/m32, r32
xor m32, eax
xor bh, m8 //XOR r8, r/m8
xor bx, m16 //XOR r16, r/m16
xor ebx, m32 //XOR r32, r/m32
}
return 0;
}
7.NOT 目标操作数
作用:对目标操作数进行取反
#include <stdio.h>
int main()
{
unsigned char m8 = 5;
short m16 = 4;
int m32 = 3;
__asm {
mov eax, 0x00000000
not al //NOT r/m8
not m8
not ax //NOT r/m16
not m16
not eax //NOT r/m32
not m32
}
return 0;
}
(三)寄存器的知识
1.寄存器与内存的区别
- 寄存器位于CPU内部,执行速度快,但成本高;
- 内存速度相对较慢,但成本低,可以大量使用;
- 寄存器和内存并无本质区别,都是用于存储数据的容器,都是定宽的;
- 基层其常用的有8个:EAX、ECX、EDX、EBX、ESP、EBP、ESI、EDI;
- 计算机中的几个常用计量单位:BYTE、WORD、DWORD;
2.内存(这里并不是指磁盘/硬盘的存储大小)
- 计算机说的32位指的是寻址编号,即寻址宽度。每一个编号表示一个字节。0xFFFFFFFF+1=100000000,这是计算机存储的最大的量,其10进制数:4294967296字节,即4G。(32位系统可以打补丁编程,64位的识别更多内存);
- 内存的数量特别庞大,无法为每个单元都起一个名字,因此用编号来代替。说32位计算机是因为其寄存器是32位的,但并不准确,还有很多寄存器是大于32位的。
3.内存格式:
1. 每一块内存单元都有一个唯一的编号,每个内存单元的宽度为8bit,即1个字节;
2. [编号]称为地址;
3. 地址的作用:当我们想从内存中读取数据或者想向内存中写入数据时需要用到;
4.从指定内存中写入/读取数据:
#include <stdio.h>
int main()
{
__asm {
mov eax,esp
mov dword ptr ds:[eax],0xabcdabcd //使用esp的值作为地址
mov eax,dword ptr ds:[eax]
}
__asm {
mov dword ptr ds:[0x012FF9AC],0xabcdabcd //此处报错,无法写入
mov eax,dword ptr ds:[0x012FF9AC]
}
return 0;
}
dword: 表示要读/写多少宽度,这里是4字节;
ptr: 表示后面是一个指针(即里面存的是一个地址,不是一个普通的数值);
ds: 数据段寄存器;
0x012FF9AC: 内存编号(必须是32位的),前面0可以省略;
注意:地址编号不要随便写,因为内存是有保护的,并不是所有内存都可以直接读写(需特别处理),可以使用esp中的数据作内存使用。
2017/5/12 19:01:39
Day 4 《内存地址和堆栈》
(一)数据存储与寻址
(1)数据窗口与堆栈窗口
上图为数据窗口,在vs中看不到堆栈窗口。
0x006FF890内存单元对应的是20这块内存,从左往右依次是0x006FF891、0x006FF892…0x006FF89F;
堆栈窗口可从VTDeBug软件中查看,其对应的内存单元正好是相反的,要从右往左看;
(2)寻址公式
#include<stdio.h>
int main()
{
unsigned char m8 = 8;
short m16 = 16;
int m32 = 32;
__asm {
mov ebx, 0x10
//读取内存的值
//mov eax, dword ptr ds : [0xA7777] //[立即数]
mov eax, dword ptr ds : [esp] //[reg]
mov eax, dword ptr ds : [esp + 0x10] //[reg + 立即数]
mov eax, dword ptr ds : [esp + ebx*2] //[reg + reg*{1,2,3,8}]
mov eax, dword ptr ds : [esp + ebx*2 + 0x10] //[reg + reg*{1,2,3,8} + 立即数]
//向内存中写入数据
//mov dword ptr ds : [0xA7777], 0x11111111 //[立即数]
mov dword ptr ds : [esp], 0x12345678 //[reg]
mov dword ptr ds : [esp + 0x10], 0x87654321 //[reg + 立即数]
mov dword ptr ds : [esp + ebx*2], 0x99999999 //[reg + reg*{1,2,3,8}]
mov dword ptr ds : [esp + ebx*2 + 0x10], 0x22222222 //[reg + reg*{1,2,3,8} + 立即数]
//获取内存编号
lea eax, dword ptr ds : [esp]
lea eax, dword ptr ds : [esp + 10]
lea eax, dword ptr ds : [esp + eax * 2]
}
}
lea 和 mov的区别:lea是将源操作数的地址传递给目标操作数;mov是将源操作数的值传递给目标操作数;
(3)模拟堆栈
#include<stdio.h>
int main()
{
unsigned char m8 = 8;
short m16 = 16;
int m32 = 32;
__asm {
mov ebx, esp //BASE
mov edx, esp //TOP
//压入数据
//方式1:
mov dword ptr ds : [edx - 4], 0xaaaaaaaa //压入数据,栈顶上移
sub edx, 4
//方式2:
//sub edx,4
//mov dword ptr ds:[edx],0xbbbbbbbb
//方式3:
//mov dword ptr ds:[edx-4],0xdddddddd
// lea edx,dword ptr ds:[edx-4]
//方式4:
//lea edx,dword ptr ds:[edx-4]
//mov dword ptr ds:[edx],0xeeeeeeee
//读取数据
mov esi, dword ptr ds : [ebx - 8] //通过栈底偏移
mov edi, dword ptr ds : [edx + 4] //通过栈顶偏移
//弹出数据
//方式1:
mov ecx, dword ptr ds : [edx] //弹出数据,栈顶下移
lea edx, dword ptr ds : [edx + 4]
//方式2:
//mov esi, dword ptr ds : [edx]
//add edx, 4
//方式3:
//lea edx, dword ptr ds : [edx + 4]
//mov edi, dword ptr ds : [edx - 4]
push eax //使用push,esp当栈顶,ebp当栈底
push ax //不能是8位寄存器
}
}
(4)PUSH指令与POP指令
#include<stdio.h>
int main()
{
unsigned char m8 = 8;
short m16 = 16;
int m32 = 32;
//PUSH指令 & POP指令
__asm {
//PUSH r16/r32/m16/m32
//POP r16/r32/m16/m32
push m16
push m32
push ax
push ebx
POP m32 //这里获取的是原ebx的值
POP m16 //这里获取的是原ax的值
POP ebx //这里获取的是原m32的值
POP ax //这里获取的是原m16的值
}
}
PUSH与POP指令,都会改变esp的值,即系统将esp当作栈顶;
PUSH与POP指令,都不能使用8位寄存器;
并不是每次使用都减4,而是根据压入和弹出的数据类型决定;
(5)PUSHAD指令预POPAD指令
#include<stdio.h>
int main()
{
unsigned char m8 = 8;
short m16 = 16;
int m32 = 32;
//PUSH指令 & POP指令
__asm {
pushad //将8个通用寄存器的值存入栈中
mov eax,0x0
mov ebx,0x0
popad //复原8个通用寄存器的值
}
}
这里的pushad指令与popad指令都会改变栈顶esp的值;
(6)练习
int main()
{
__asm {
mov ebx, esp
mov edx, esp
//1.使用ebx存储栈底地址,edx存储栈顶地址,并连续存储5个不同的数
lea edx, byte ptr ds : [edx - 1]
mov byte ptr ds : [edx], 0x11
lea edx, word ptr ds : [edx - 2]
mov word ptr ds : [edx], 0x2222
lea edx, dword ptr ds : [edx - 4]
mov dword ptr ds : [edx], 0x33333333
lea edx, dword ptr ds : [edx - 4]
mov dword ptr ds : [edx], 0x44444444
lea edx, dword ptr ds : [edx - 4]
mov dword ptr ds : [edx], 0x55555555
//2.分别使用栈底加偏移、栈顶加偏移的方式读取这5个数,并存到寄存器中
mov eax, dword ptr ds : [edx] //eax:55555555
mov eax, dword ptr ds : [edx + 4] //eax:44444444
mov eax, dword ptr ds : [edx + 8] //eax:33333333
mov al, byte ptr ds : [ebx - 1] //eax:33333311
mov ax, word ptr ds : [ebx - 3] //eax:33332222
//3.弹出这5个数,回复栈顶到原来的位置
mov eax, dword ptr ds : [edx]
lea edx, dword ptr ds : [edx + 4]
mov eax, dword ptr ds : [edx]
lea edx, dword ptr ds : [edx + 4]
mov eax, dword ptr ds : [edx]
lea edx, dword ptr ds : [edx + 4]
mov ax, word ptr ds : [edx]
lea edx, word ptr ds : [edx + 2]
mov al, byte ptr ds : [edx]
lea edx, byte ptr ds : [edx + 1]
//4.使用2种方式实现:push ecx
lea esp, dword ptr ds : [esp - 4]
mov dword ptr ds : [esp], ecx
lea esp, [esp - 4]
mov[esp], ecx
//5.使用2种方式实现:pop ecx
mov ecx, dword ptr ds : [esp]
lea esp, dword ptr ds : [esp + 4]
mov ecx, [esp]
lea esp, [esp + 4]
//6.使用2种不同方式实现:push esp 后面这两题不太懂!!!!
lea esp, dword ptr ds : [esp - 4]
mov dword ptr ds : [esp], esp
lea esp, [esp - 4]
mov[esp], esp
//7.使用2种不同方式实现:pop esp
mov esp, dword ptr ds : [esp]
lea esp, dword ptr ds : [esp + 4]
mov esp, [esp]
lea esp, [esp + 4]
}
}
2017/5/14 3:09:55
Day 5 《标志寄存器》
(一)32位标志寄存器EFLAGS
- 标志寄存器用来保存在一条指令执行完成后,CPU所处状态的信息及运算结果的特征。
- 标志寄存器是有顺序的,必须记住。
(二)运算结果标志位
1.进位标志CF(CarryFlag):第0位
如果运算结果的最高位产生了一个进位或者借位,那么其值为1,否则为0;
2.奇偶标志PF(ParityFlag):第2位
用于反映运算结果中“1”的个数的奇偶性。如果“1”的个数为偶数,则PF=1,否则为0;
3.辅助进位标志AF(AuxiliaryCarryFlag):第4位
在发生下列情况时,辅助位标识AF的值被置为1,否则其值为0:
(1)在字操作时,发生低位字节向高位字节进位或借位时;
(2)在字节操作时,发生低4位向高4位进位或借位时;
4.零标志ZF(ZeroFlag):第6位
如果运算结果为0,则其值为1,否则为0;在判断运算结果是否为0时,可使用此标志位;
5.符号标志SF(SignFlag):第7位
反映运算结果的符号位,它与运算结果的最高位相同;
__asm {
xor eax, eax //SF=0
sub eax, 0x1 //SF=1
add eax, 0x1 //SF=1
}
6.溢出标志OF(OverflowFlag):第11位
反映有符号数加减运算所得结果是否溢出,如果运算结果超过当前运算位数所能表示范围,则称为溢出,OF=1,否则OF=0;
这里要注意: 与进位标志的区别;
最高位进位与溢出的区别:
1.进位标志表示无符号数运算结果是否超出范围;溢出标志表示有符号数运算结果是否超出范围;
2.正+正=正,如果结果是负数,则说明有溢出;负+负=负,如果结果是正数,说明有溢出;
3.正+负永远不会有溢出;CF位是我们做无符号运算时候需要关注的。有符号运算时我们要关注OF位;
unsigned short um16 = 0x8;
short m16 = 0x8;
__asm {
//无符号不进位,有符号不溢出
add um16, 8 //cf=0
add m16, 8 //of=0
//无符号进位、有符号不溢出
mov um16, 0Xffff
mov m16, 0xffff
add um16, 2 //cf=1
add m16, 2 //of=0
//无符号不进位,有符号溢出
mov um16,0x7FFF
mov m16, 0x7FFF
add um16, 2 //cf=0
add m16, 2 //of=1
//无符号进位、有符号溢出
mov um16,0xFFFE
mov m16,0xFFFE
add um16,0x8000 //cf=1
add m16,0x8000 //of=1
}
(三)相关指令
1.ADC指令:带进位加法
格式: ADC R/M,R/M/IMM 两边不能同时为内存,宽度要一致
与ADD唯一的不同是,ADC执行加法运算时,会将CF位的值一起加到目标操作数中。
所以ADC常用于处理以下情况:
如果必须处理非常大的、不能存放到双字数据长度(ADD可以使用的最大长度)中的整数,可以把值分割为多个双字数据元素,并且对每个元素执行独立的加法操作。
为了正确完成这个操作,必须检测每个加法操作的进位标志,如果进位标志被设置为1,就必须进位到下一对相加的数据元素。
short um16 = 1;
__asm{
mov al,1
mov cl,2
add um16,0xffff
adc al,cl //al=4
}
2.SBB指令:带借位的减法
格式:SBB R/M,R/M/IMM 两边不能同时为内存,宽度要一致
unsigned short m16 = 0xFFFF;
__asm {
mov al,4
mov cl,2
add m16,1
sbb al,cl //al=1
}
操作对象1=操作对象1-操作对象2-CF 其中CF为进位的值
3.XCHG指令:交换数据
格式:XCHG R/M,R/M 两边不能同时为内存,宽度要一致
__asm {
xchg al,cl
xchg dword ptr ds:[esp-4],eax
xchg byte ptr ds:[esp-4-1],al
}
4.MOVS指令:移动数据 内存-内存
注意:MOVS R/M,R/M 这个指令两边都可以是内存
MOVS指令是用来复制一个数据项(字节,字或双字)从源字符串到目标字符串。源字符串指出由DS:SI和ES:DI指向目标字符串。
movs 指令(串传送)(字符串操作指令)( MOVSB 传送字符. MOVSW 传送字. MOVSD 传送双字. )
这里MOVS拷贝4个字节的内容,即DWORD,另外一种书写形式为:MOVSD。
与之对应的还有拷贝两个字节的MOVSW指令和拷贝一个字节的MOVSB指令。
请注意ESI、EDI拷贝的方向,拷贝的方向取决于方向标志位D。
5.STOS指令:将AL/AX/EAX的值储存到[EDI]指定的内存单元
__asm {
mov eax,0x12345678
stos dword ptr es:[edi] //edi 0x00DDF898 78 56 34 12 00 00 00 00
stosw //edi 0x00DDF898 78 56 34 12 78 56 00 00
stosb //edi 0x00DDF898 78 56 34 12 78 56 78 00
}
我们可以使用REP前缀,还有每次操作两个字节的STOSW指令和每次操作一个字节的STOSB指令。
6.REF指令:按计数寄存器(ECX)中指定的次数重复执行字符串命令
__asm {
mov eax, 0x12345678
mov ecx, 5
//edi 0x008FFBF4 08 fc 8f 00 7e 1d b3 00
rep stosb //edi 0x008FFBF4 78 78 78 78 78 1d b3 00
mov ecx,5
//esi 0x00E11046 e9 45 0d 00 00 e9 52 3a 00 00
//edi 0x00F1F769 1d e1 00 01 00 00 00 90 3e 1a
rep movsw //edi 0x00F1F769 e9 45 0d 00 00 e9 52 3a 00 00
}
这里要注意,每次ref执行之后,ecx都会清零;
(四)练习
char m8 = 8;
unsigned char um8 = 88;
short m16 = 16;
unsigned short um16 = 1616;
int m32 = 32;
unsigned int um32 = 3232;
__asm {
//1.熟练记住CF/PF/AF/ZF/SF/OF
//2.写汇编指令,只影响CF位的值 (不改变其他标志位的值)
mov um8, 0xf0
add um8, 0x11 //EFL=203 = 0010 0000 0011
mov um16, 0xf000
add um16, 0x1001 //EFL=203 = 0010 0000 0011
mov um32, 0xf0000000
add um32, 0x10000001 //EFL=203 = 0010 0000 0011
//3.写汇编指令,只影响PF位的值
mov um8, 0x01
add um8, 0x1 //EFL=202 = 0010 0000 0010
add um8, 0x1 //EFL=206 = 0010 0000 0110
//4.写汇编指令,只影响AF位的值
mov um8, 0x01
add um8, 0x0f //EFL=212 = 0010 0001 0010 低4位向高4位进位,AF=1
mov um16, 0xccfe
add um16, 0x1110 //EFL=280 = 0010 1000 0000 低字节向高字节进位,AF=0,这里我也不太明白,字操作时,明明第字节向高字节进位
mov um32, 0xeeeeffee
add um32, 0x00001001 //EFL=282 = 0010 1000 0010 低字向高字进位,AF=0
//5.写汇编指令,只影响SF位的值
mov um8, 0x7F
add um8, 0x10 //EFL=A82 = 1001 1000 0010 最高为1,与SF位保持一致
//6.写汇编指令,只影响OF的值
mov m8, 0x7F
add m8, 0x10 //EFL=A82 = 1001 1000 0010 溢出了,OF=1
//7.用MOVS指令分别移动5个字节、5个字、5个双字
movs byte ptr es : [edi], byte ptr ds : [esi]
movs word ptr es : [edi], word ptr ds : [esi]
movs dword ptr es : [edi], dword ptr ds : [esi]
movsb
movsw
movsd
//8.用STOS指令分别存储5个字节、5个字、5个双字
stos byte ptr es : [edi]
stos word ptr es : [edi]
stos dword ptr es : [edi]
stosb
stosw
stosd
//9.使用REP指令重写第7,8题
mov ecx,5
rep movsb
mov ecx,5
rep movsw
mov ecx,5
rep movsd
mov ecx,5
rep stosb
mov ecx,5
rep stosw
mov ecx,5
rep stosd
}
2017/5/14 12:17:24