计算机汇编语言笔记

计算机汇编语言笔记

什么是汇编

汇编就是寄存器跟寄存器,寄存器跟内存,内存跟寄存器之间来回移动数据,没有内存跟内存 如果要操作有特殊的指令操作。(所有指令都是操作内存跟寄存器)

寄存器

编号32位16位8位寄存器
0EAXAXAL累加/通用
1ECXCXCL计数/通用
2EDXDXDL数据/通用
3EBXBXBL基址
4ESPSPAH堆栈(栈顶)
5EBPBPCH堆栈(栈底)
6ESISIDH字符串
7EDIDIBH字符串
 EIPIP 指令指针
 EFLAGSFLAGS 标志

寄存器之间关系

图片[1]-计算机汇编语言笔记-彩虹网络博客

VC6快捷键

    • F7 编译

    • F9 断点调试

    • F5 断到断点处

    • F10 步过

    • F11 步入

    • Shift+F5 退出调试

    • Alt+8 反汇编

    • F12 转到定义处

汇编书写格式

_asm//汇编格式
{
   //这里只能写汇编指令
}

如何分辨参数

看到MOV r/m8,r8 r代表register m代表memory r8代表寄存器
imm代表立即数 r代表寄存器 m代表内存

ib—>byte imm(imm8)

iw—>word imm(imm16)

id—->dword imm(imm32)

0xC0000005 内存访问错误

数据只有反复利用才会产生意义

只要执行指令EIP都会发生变化因为EIP就是指令指针

双斜杠代表注释 斜杠+星范围之间全部注释

汇编 读写内存

内存格式与书写方式

[ ] 内存是由两个括号代表的 可以是寄存器,立即数。

                                         dis32
mov eax,dword ptr ds:[ ebx+ecx * 1 + 0x12345678 ]
                                 2
                                 4
                                 8

//ebx--->基址   ecx--->INDEX s索引   0x12345678--->偏移

//内存里面寄存器都是32位因为内存数据都是4个字节4个字节对齐

ds--->段寄存器   prt--->指针   dword--->4个字节

为什么是1  2  4  8 因为2的二次方

//读内存
mov eax,0x0012ff34
mov ecx,dword ptr ds:[eax]

//写内存
mov eax,0x0012ff34
mov ecx,0x12345678
mov dword ptr ds:[eax],ecx

内存是4个字节4个字节对齐 4G空间 2的32次方刚好是4G

内存注意事项

0012FF34	A6 F5 AF 0F 3F 9B	---》0x9b3f0fafa6

观察内存首先前面是内存地址,后面是内存里面的内容,内存的数据是反着存储。右高左低

esp ebp不能随意修改 因为函数放到堆栈里面的,esp ebp是对堆栈操作的,如果改变了堆栈失去平衡,就会出现错误

段寄存器(96位)

编号96位扩展
0esesi edi
1cseip
2ssesp ebp
3ds通用
4fs 
5gs 
↑ //普通段↑ //普通段↑ //普通段
↓ //系统段↓ //系统段↓ //系统段
6ldtr 
7tr 

段寄存器结构体

struct Sengment//96
{
  WORD Selector//选择子
  WORD Attributes//属性
  DWORD Base//基址
  DWORD Limit//界限
}

无符号类型

QWORD—> 8个字节
FWORD—> 6个字节
DWORD—> 4个字节 //32位
WORD—> 2个字节 //16位
BYTE—> 1个字节 //8位

有符号类型(C语言)

int—> 4个字节
long—> 4个字节
short—> 2个字节
char—> 1个字节

浮点类型(C语言)

float—> 4个字节
double—> 8字节

汇编指令

MOV 移动指令

mov 目标操作数,源操作数—>源操作数移动到目标操作数

目标操作数—> r/m
源操作数—> imm/r/m

源操作数和目标操作数不能同时为内存 (根据编译器而定)

mov ecx,0x11111111
mov eax,0x22222222
mov eax,ecx

ADD 加法指令

add 目标操作数,源操作数—>目标操作数+源操作数 把结果放到目标操作数

目标操作数—> r/m
源操作数—> imm/r/m

mov eax,0x12345678
mov ecx,0x11111111
add eax,ecx

SUB 减法指令

sub 目标操作数,源操作数—>目标操作数-源操作数 把结果放到目标操作数

目标操作数—>r/m
源操作数—> imm/r/m

mov eax,0x12345678
mov ecx,0x11111111
sub  eax,ecx

INC 自加一指令

inc 目标操作数—>目标操作数加一

目标操作数—> r/m

mov al,0x12
inc al

DEC 自减一指令

dec 目标操作数—>目标操作数减一

目标操作数—> r/m

mov cl,0xff
dec cl

逻辑指令 位与位之间的运算

    1. 位与位之间没有任何联系

    1. 不进位,取最大值

OR 或运算

or 目标操作数,源操作数—>结果存储目标操作数。算加法两个操作数有一个1就为1,否则为0。C语言符号( | )

目标操作数—> r/m
源操作数—>imm/r/m

mov al,0x8
mov cl,0x9
or al,cl

目标操作数        1000
源操作数	        1001
运算结果	        1001

AND 与运算

and 目标操作数,源操作数—>结果存储目标操作数。算乘法两个操作数对应的位为1才为1,否则为0。C语言符号( & )

目标操作数—> r/m
源操作数—> imm/r/m

mov al,0x5
mov cl,0x6
and al,cl

目标操作数: 0101
源操作数:   0110
运算结果:   0100

XOR 异或

xor 目标操作数,源操作数—>结果存储目标操作数。两个操作数相同为0,不同为1。C语言符号( ^ )

目标操作数—> r/m
源操作数—> imm/r/m

mov al,0x8
mov cl,0x9
xor al,cl

目标操作数:1000
源操作数:  1001
运算结果:  0001

NOT 取反

not 目标操作数—>结果存储目标操作数,操作数对应的位是0就为1,是1就为0。(快捷方式用F去减注意宽度) C语言符号( ~/! )

目标操作数—> r/m

mov cl,0x9 -->根据位宽用比如CL 用FF去减
not cl

直接用F去减
0x5739752bac482432
0xa8c68ad453b7dbcd

SAR 右移

sar 目标操作数,源操作数—>对目标操作数除以2的幂(源操作数)结果存储目标操作数

目标操作数—> r/m
源操作数—>寄存器cl,imm8立即数

mov al,0x8
mov cl,0x2
sar al,cl

SHL 左移

shl 目标操作数,源操作数—>对目标操作数乘以2的幂(源操作数)结果存储目标操作数

目标操作数—> r/m
源操作数—> 寄存器cl,imm8立即数

mov al,0x4
shl al,0x1

LEA 获取内存地址

lea 目标操作数,源操作数—>功能:获取内存地址

目标操作数—>必须是 r16/r32 寄存器Register
源操作数—>必须是内存 [ ]

mov ecx,dword ptr ds:[eax] 
lea edx, dword ptr ds:[eax]---> mov edx,eax 值是一样的

MOV 和 LEA指令的区别

    • MOV 获取内存地址里面的内容

    • LEA 获取内存地址

PUSH 把值压入堆栈

push 目标操作数—>功能:压栈

目标操作数—> imm/r/m/reg

分解成两条指令,即使同时改变寄存器也改变内存

push eax	//先减4在给值
lea esp,dword ptr ds:[esp-0x4]
mov dword ptr ds:[esp],eax

push esp	//通用方法  反
mov dword ptr ds:[esp-0x4],esp
lea esp,dword ptr ds:[esp-0x4]

POP 将值弹出堆栈

pop 目标操作数—>功能:将值弹出堆栈

目标操作数—> r16/r32/m/reg

pop ecx	//先弹赋值在加4
mov ecx,dword ptr ds:[esp]
lea esp,dword ptr ds:[esp+0x4]

pop esp //通用方法	先加4在弹值
lea esp,dword ptr ds:[esp+0x4]
mov esp,dword ptr ds:[esp-0x4]

XCHG 交换

xchg 目标操作数,源操作数—>功能:目标操作数和源操作数的值交换内容

目标操作数—> r/m
源操作数—> r/m

xchg eax,eax // 自己跟自己交换没有意义,所以演变成一个新的指令 NOP

mov eax,0x11111111
xchg eax,dword ptr ds:[esp]

NOP 无操作

nop —>不执行操作

MUL 无符号乘法

操作数大小源操作数 1源操作数 2目标操作数
字节ALr/m8AX
AXr/m16DX:AX
双字EAXr/m32EDX:EAX

mul 源操作数—>目标操作数 × 源操作数 执行无符号乘法,结果存储到目标操作数。默认 AL、AX 或 EAX 寄存器的值(取决于操作数的大小),结果存储到 AX、DX:AX 或 EDX:EAX 寄存器。(取决于操作数的大小)

源操作数—> r/m

mov al,0x8
mov cl,0x2
mul	cl 

IMUL 有符号乘法

单操作数形式 —> 单操作数形式 只有一个操作数跟无符号乘法(mul)一样, 默认 AL、AX 或 EAX 寄存器。结果存储到 AX、DX:AX 或 EDX:EAX 寄存器。(取决于操作数的大小)

mov al,0x8
mov cl,0x2
imul cl 

双操作数形式 —>目标操作数乘以源操作数,目标操作数是通用寄存器,源操作数可以是立即数、通用寄存器或内存,结果放到目标操作数。(如果没有用到EAX ,溢出的位放到EFLAGS里去)

mov cx,0xf
mov dx,0x2
imul cx,dx

三操作数形式 —>(指定目标操作数)两个源操作数相乘,结果放到目标操作数(通用寄存器),源操作数1(可以是通用寄存器或内存),源操作数2(立即数)。

mov ax,0x0000
mov cx,0x2222
imul ax,cx,0x2

DIV 无符号除法

操作数大小被除数除数余数商的范围
字/字节AXr/m8ALAH-128 到 +127
双字/字DX:AXr/m16AXDX-32,768 到 +32,767
四字/双字EDX:EAXr/m32EAXEDX-231 到 232 – 1

div 将 AL、AX 或 EAX 寄存器中的值除以(无符号)源操作数,结果存储到 AX、DX:AX 或 EDX:EAX 寄存器。

源操作数—> r/m

mov al,0x10
mov cl,0x2
cbw
div cl

mov ax,0x7000
mov cx,0x4
cwd
div cx

mov eax,0x40000000
mov ecx,0x20
cdq
div ecx

IDIV 有符号除法

idiv 将 AL、AX 或 EAX 寄存器中的值除以(有符号)源操作数,结果存储到 AX、DX:AX 或 EDX:EAX 寄存器。

源操作数—> r/m

mov ax,0xff
mov cx,0x2
cwd
idiv cx

扩展CBW CWD CDQ

按高位扩展最高位是为几那就扩展成几

CBW—> 1个字节转换2个字节 AX←AL
CWD—> 2个字节转换4个字节 DX:AX←AX
CDQ—> 4个字节转8个字节 EDX:EAX ← EAX

ABC 带进位加法

abc 目标操作数,源操作数—>功能:指令将两个操作数相加,如果 CF 标志是 0 ,则将结果存储到目标操作数。如果 CF 标志是 1,加法的结果递增 1,然后存储到目标操作数。

目标操作数—> imm/r/m
源操作数—> r/m

mov ax,0xffff
add ax,0x1
mov cx,0x1
adc cx,0x1

SBB 带借位整数减法

sbb 目标操作数,源操作数—>源操作数+CF进位标志-目标操作数 结果存储目标操作数 不区分有符号或无符号操作数

目标操作数—> imm/r/m
源操作数—> r/m

mov ax,0xffff
add ax,0x1
mov cl,0x8
mov al,0x3
SBB cl,al

DAA 加之后AL十进制调整

执行add加法后对al进行十进制调整(0-F)

如果AL的低4位大于9或AF=1,则AL的内容加06H,并将AF置1;
如果AL的高4位大于9或CF=1,则AL的内容加60H,且将CF置1。

mov al,0x9
add al,0x1
daa

DAS 减之后 AL 十进制调整

执行sub减法后对al进行十进制调整(0-F)

如果AL低四位>9或AF=1 ,则AL的值减06h,并置AF=1
如果AL高四位>9或CF=1 ,则AL的值减60h ,且置CF=1

mov al,0x10
sub al,0x1
das

AAA 加之后 ASCII 调整

aaa 执行add后对al进行ASCII调整

mov al,0x5
add al,0x5
aaa

AAS 减之后 ASCII 调整

AAA 执行sub后对al进行ASCII调整

mov al,0x5
sub al,0x1
aas

JMP 跳转

jmp 目标操作数—>改变EIP寄存器,想当于mov eip(eip不能随意修改,除非英特尔提供特殊指令)

目标操作数—> imm/r/m

mov eax,0x0012ff34
jmp eax

LAB 标签

	lea ecx,lab
	jmp ecx
        xor eax,eax
lab:
	mov  eax,0x12345678

CALL 调用过程

call 目标操作数—>调用过程 先push后jmp

目标操作数—> imm/r/m

call edx
push IL	//esp+len(il)下一条指令的地址-->当前EIP+本条指令的长度
jmp edx


call edx

lea eax,leb
push eax
jmp edx

leb:
mov eax,0x0

RET 从CALL返回

ret 无或iw—>从过程返回

ret
lea esp,dword ptr ds:[esp+0x4]
jmp dword ptr ds:[esp-0x4]---》返回 CALL的下一条指令

ret iw
lea esp,dword ptr ds:[esp+0x4+iw]
jmp dword ptr ds:[esp-0x4-iw]---》返回 CALL的下一条指令

注意:CALL指令跟RET是配套使用的

JCC 条件转移指令

jcc 目标操作数—>立即数(地址)

指令条件
jccf=1
jnccf=0
jppf=1
jpepf=1
jnppf=0
jpopf=0
jzzf=1
jezf=1
jnzzf=0
jnezf=0
jssf=1
jnssf=0
joof=1
jnoof=0
CF PF ZF SF OF –》设置标志位是否满足条件,决定是否跳转

指令条件
jg>
jl<
je=
jge>=
jle<=
jng<=
jnl>=
jnle>
jnge<
有符号

指令条件
ja>
jb<
je=
jae>=
jbe<=
jna<=
jnb>=
jnae<
jnbe>
无符号

CMP 比较两个操作数

cmp 目标操作数,源操作数—>功能:比较两个操作数,相当于SUB指令结果丢弃,影响标志位

目标操作数—> r/m
源操作数—> imm/r/m

leb:
mov eax,0x5
mov edx,0x4
cmp eax,edx
jae leb

TEST 逻辑比较

test 目标操作数,源操作数—>功能:相当于AND指令(CF OF标志清零),根据结果设置PF SF ZF

目标操作数—> r/m
源操作数—> imm/r/m

leb:
//案例1
mov eax,0x1
mov ecx,0x1
test eax,ecx

//案例2
mov eax,0x800000000
xor eax,0x4

jnc leb

CMOVCC 条件移动 类似JCC指令系列

cmovcc 目标操作数,源操作数—>条件移动

目标操作数—> r
源操作数—> r/m

mov ax,0xffff
mov cx,0x2222
cmovg ax,cx

SETCC 按条件设置字节 类似JCC指令系列

setcc 目标操作数—>功能:标志位满足条件,将目标操作数设置为 0 或 1。

目标操作数—> r/m

mov al,0xff
add al,0x2
setc cl

MOVS系列 数据从字符串移到字符串

movs 目标操作数(m8/m16/m32),源操作数(m8/m16/m32) (ESI和EDI地址不能相同)

目标操作数—> [ EDI ]
源操作数—>[ ESI ]

movsd—>ESI和EDI同时加4(看DF位)在把dword ptr ds:[ESI]—->dword ptr ds:[EDI]
movsw—>ESI和EDI同时加4(看DF位)在把word ptr ds:[ESI]—->word ptr ds:[EDI]
movsb—>ESI和EDI同时加4(看DF位)在把byte ptr ds:[ESI]—->byte ptr ds:[EDI]

如果 DF 标志为 0,则 (E)SI 与 (E)DI 寄存器递增)
如果 DF 标志为 1,则 (E)SI 与 (E)DI 寄存器递减)

mov esi,0x0012FD84
mov edi,0x0012FDB8

mov dword ptr ds:[esi],0x12345678
mov dword ptr ds:[edi],0x11111111

movsd

movs dword ptr ds:[edi],dowrd ptr ds:[esi] //MOVSD分解
add esi,0x4
add edi,0x4

CLD 清除方向标志

cld—>功能:DF 标志 清除为0

mov esi,0x0012FD84
mov edi,0x0012FDB8

mov dword ptr ds:[esi],0x12345678
mov dword ptr ds:[edi],0x11111111
cld
movsd

STD 设置方向标志

std—>功能:DF 标志设置为 1

mov esi,0x0012FD84
mov edi,0x0012FDB8

mov dword ptr ds:[esi],0x12345678
mov dword ptr ds:[edi],0x11111111
std
movsd

STOS系列 存储字符串

stosd stosw stosb 目标操作数(源操作数默认操作 EAX AX AL)

目标操作数—>[ EDI ]

stosb—>al—>byte ptr ds:[EDI] (看DF位)EDI+1或EDI-1
stosw—>al—>word ptr ds:[EDI] (看DF位)EDI+2或EDI-2
stosd—>al—>dword ptr ds:[EDI] (看DF位)EDI+4或EDI-4

如果 DF 标志为 0,则 (E)SI 与 (E)DI 寄存器递增)
如果 DF 标志为 1,则 (E)SI 与 (E)DI 寄存器递减)

mov eax,0x12345678
mov edi,0x0012FF34
stosd

mov dword ptr ds:[edi],eax	//stosd 分解
add edi,0x4

REP 重复指令 注意(MOVS和STOS以上指令配套使用)

rep —> 功能:重复操作,看ECX值。 依赖ECX ECX=0 结束

重复前缀终止条件 1终止条件 2
REPECX=0
REPE/REPZECX=0ZF=0
REPNE/REPNZECX=0ZF=1

//rep stosd 案例
mov edi,0x0012FE70
mov eax,0x11111111
mov ecx,0x10
rep stosd


//rep movsd 案例
mov esi,0x0012FE30
mov edi,0x0012FE34

mov dword ptr ds:[esi],0x11111111
mov dword ptr ds:[edi],0x22222222
mov ecx,0x10
rep movsd

标志寄存器

1514131211109876543210
    OFDFIFTFSFZF AF PF CF

    • 第1组6个标志位 CF PF AF ZF SF OF 称为运算结果标志,主要受加减运算和逻辑运算结果的影响。

    • 第2组3个标志位 TF IF DF 称为状态控制标志。

    • 空是保留的。

CF进位标志:主要用于反映运算是否产生进位或借位。如果运算结果的最高位,产生进位或借位,CF = 1 否则 CF = 0。

0010 	0000	001					0<---CF

mov al,0xff
add al,0x1

PF 奇偶标志:运算结果中 1 的个数,如果 1 的个数为偶数(二进制位限制8位) PF = 1 否则 PF = 0。

0010	0000	0						0<--PF					1	0

mov al,0x2
add al,0x8

AF辅助进位标志:低半字节向高半字节产生进位或借位 AF = 1 否则 AF = 0。(注意

0010	000								0<--AF	  0	0	1	0

mov al,0x8
add al,0x8

ZF 零标志:反映运算结果是否为0,如果运算结果为0 ZF = 1 否则 ZF = 0。

0010	0						0<--ZF		0	0	0	0	1	0

mov eax,0x8
sub eax,0x8

SF 符号标志:运算结果最高位为1 SF = 1 否则 SF = 0。

0010					0<--SF		0	0	0	0	0	1	0

mov al,0xff
sub al,0x0

OF 溢出标志:用于有符号加减运算中,如果运算结果字节(大于0x7f-小于0x80)字(大于0x7fff-小于0x8000)双字(大于0x7fffffff-小于0x80000000)大于或者小于 OF=1 否则OF=0

处理器计算两种数据类型的结果。并分别设置OF 与 CF标志以表示有符号或无符号结果的进位。SF标志表示有符号结果的符号。

0<--OF				0	1	0	0	0	0	0	0	0	1	0

mov al,0x7f
add al,0x1

mov al,0x80
add al,0xff

DF 方向标志

IF 中断允许标志

TF 追踪标志

函数框架

函数构成

    1. 函数的类型—> 决定了返回值

    1. 函数的名字

    1. 函数的参数—>参数可以有很多个

    1. 函数的返回值

int main(int argc, char* argv[])
{
    //;--->变量,语法,数据类型
    int c;//类型 类型名字;反汇编:dword ptr ds:[ebp-0x4]
	return 0;
}
//int-->代表函数的类型(函数返回类型) //main-->函数的名字 //()-->阔号代表参数 //return 0;--->代表返回值
//dword  word byte  int char short long   void 空类型--->没有返回值

函数框架与堆栈图

0012FEE0	0x0012FF80 edi	---》恢复EDI
0012FEE4	0x7C939B3F esi	---》恢复ESI
0012FEE8	0x0012FEE8 ebx	---》恢复EBX
0012FEEC	0xCCCCCCCC	---》sub esp-0x40
0012FEF0	0xCCCCCCCC
0012FEF4	0xCCCCCCCC
0012FEF8	0xCCCCCCCC
0012FEFC	0xCCCCCCCC
0012FF00	0xCCCCCCCC
0012FF04	0xCCCCCCCC
0012FF08	0xCCCCCCCC
0012FF0C	0xCCCCCCCC
0012FF10	0xCCCCCCCC
0012FF14	0xCCCCCCCC
0012FF18	0xCCCCCCCC
0012FF1C	0xCCCCCCCC
0012FF20	0xCCCCCCCC
0012FF24	0xCCCCCCCC
0012FF28	0xCCCCCCCC
0012FF2C	0x0012FF80 ebp	---》恢复EBP
0012FF30	0x0040106D 下一条指令地址
0012FF34	esp

//函数头
00401020   push        ebp			--->保存EBP
00401021   mov         ebp,esp		--->保存ESP
00401023   sub         esp,0x40		
00401026   push        ebx			--->保存EBX
00401027   push        esi			--->保存ESI
00401028   push        edi			--->保存EDI
00401029   lea         edi,dword ptr ds:[ebp-0x40]
0040102C   mov         ecx,0x10
00401031   mov         eax,0xCCCCCCCC
00401036   rep stosd	以上三条指令都在为本条指令做准备

//函数尾
0040103A   pop         edi			---》恢复EDI
0040103B   pop         esi			---》恢复ESI
0040103C   pop         ebx			---》恢复EBX
0040103D   mov         esp,ebp		---》恢复ESP
0040103F   pop         ebp			---》恢复EBP
00401040   ret

保存顺序:ebp esp ebx esi edi

恢复顺序:edi esi ebx esp ebp

为什么减0x40?

sub	esp,0x40

    1. 函数跟函数之间方便隔开

    1. 参数(因为他觉得16个参数够了)

    1. 因为是16进制每一项4个字节刚好是40

    1. 为什么赋值是CCCCCCCC 为了安全 防止出错 因为CCC是INT3 断点

怎么画堆栈图:谁改变ESP这段内存就把谁记录下来

只要调用函数都会产生 _chkesp() 检查堆栈平衡

只要是函数肯定用到call指令

_declspec(naked) 裸汇编 自己生成函数头 函数尾

word特殊之处

因为word比较特殊,因为内存是4个字节4个字节对齐,如果我用内存的话4个对齐,首先从2个字节扩展成4个字节,扩展完了还要进行对齐 所以说这样比较麻烦,编译器干脆把word这种类型把他放到全局变量就是short这种 2个字节的数据 放到全局变量里面 我直接从这个地址,我把全局变量的地址记录下来,然后从这个地址中去取(word是放到堆里面,运行速度快)

参数存放位置

参数是从右往左push 最左边是第一个参数 最右边的最后一个

参数是放到[EBP+0x8]位置 第1个参数放到[EBP+0x8] 第2个参数放到[EBP+0xC] 第3个参数放到[EBP+0x10] 依此类推

int fun(int a, int b, int c)
{
	return 0;
}

fun(1,2,3);

//反汇编 fun(1,2,3);
00401068 6A 03                push        3
0040106A 6A 02                push        2
0040106C 6A 01                push        1
0040106E E8 9C FF FF FF       call        @ILT+10(fun) (0040100f)
00401073 83 C4 0C             add         esp,0Ch //外平栈

变量存放位置

放到EBP-0x4这段内存里面第1个变量放到[EBP-0x4] 第2个变量放到[EBP-0x8] 第3个变量放到[EBP-0xC] 依此类推

返回值

函数返回值是放到EAX这个寄存器里面的

逆向三要素

    1. 变量

    1. 返回值

    1. 参数

© 版权声明
THE END
喜欢就支持一下吧
点赞15 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容