8 minutes
汇编语言 笔记
汇编语言
概述
编程形式
开关–>打孔–>输入设备
汇编语言的出现
mov eax, 5
mov ebx, 6
add eax, ebx
汇编程序的执行
汇编代码 -> 汇编程序 -> 处理器可识别 01010101 -> 处理器执行
学习汇编的意义
- 开发时调试
- 逆向时候的代码阅读
- 某些特殊技术的使用(如shellcode、壳)
shellcode:可以运行在任意位置的代码(汇编语言)
壳:加壳或脱壳都需要用的汇编语言
环境配置
-
Ollydbg
-
Visual Studio 2015
x16 汇编
通用寄存器
16位寄存器 | 高8位 | 低8位 |
---|---|---|
AX | AH | AL |
BX | BH | BL |
CX | CH | CL |
DX | DH | DL |
SI | \ | \ |
DI | \ | \ |
SP | \ | \ |
BP | \ | \ |
内存字节序
:chestnut: 0x12345678
每个地址只有存储1字节
大端序 | 小端序 |
---|---|
12 | 78 |
34 | 56 |
56 | 34 |
78 | 12 |
CC | CC |
CC | CC |
CC | CC |
段的概念
CS段只有16位,8086有20根地址线,那么地址如何存储?
简单粗暴 除以0x10,因为只有以零结尾的地址才能作为段地址
CS:代码段
DS:数据段
ES:扩展段
SS:堆栈段
16位汇编基本框架
assume cs:code ;设置代码段
code segment ;代码段开始
rkmain proc
mov ax,0
mov cx, 10H
rk:
inc ax
loop rk
mov ax, 4c00H
int 21H
rkmain endp
start: call rkmain ;指定开始位置
code ends ;代码段结束
end start
add
加
int
自增
sub
减
dec
自减
loop
循环
函数传参
- 寄存器传参
- 堆栈传参
- 内存传参
assume cs:code
code segment
addx proc
push bp
mov bp,sp
mov si,[bp+4]
mov di,[bp+6]
add si,di
mov ax,si
pop bp
ret
addx endp
rkmain proc
mov ax,5
mov bx,6
push ax
push bx
call addx
add sp,4
mov bx,ax
mov ax, 4c00H
int 21H
rkmain endp
start: call rkmain
code ends
end start
FLAG寄存器
状态标志
CF:进位位
PF:奇偶位
AF:辅助进位位
ZF:零值位
SF:符号位
OF:溢出位
CMP指令
CMP OPRD1, OPRD2
SUB
影响标志位
mov ax,2
mov bx,1
cmp ax.bx
相等
AX-BX = 0 ZF=1
不等
AX-BX !=0 ZF=0
AX < BX:
AX-BX = ? CF=1
AX > BX:
AX-BX=? CF=0 ZF=0
AX <= BX:
AX-BX=? CF=1 || ZF=1
AX >= BX:
AX-BX=? CF=0 || ZF=1
JCC指令
一类指令 跳转
jmp address
je address ;等于则跳转 ZF=1
jne address ;不等于则跳转 ZF=0
jb address ;低于则跳转 CF=1
ja address ;高于则跳转 CF=0 && ZF=0
运算指令
四则运算
mov ax,16
mov bx,5
add ax,5
sub ax,3
mul bx ; >16bit DX:AX 32bit 默认与ax操作
div bx ; DX->余数 AX->商 默认与ax操作
位运算
xor ax,bx
and ax,bx
or ax,bx
not ax
x86汇编
通用寄存器
寄存器 |
---|
EAX |
EBX |
ECX |
EDX |
ESI |
EDI |
ESP |
EBP |
段
段 |
---|
CS |
DS |
ES |
SS |
FS |
GS |
段选择子
CS 代码段 1B
DS ES SS 数据段 23
FS 3B
-
Index:3
-
Ti:0
-
RPL:3
段描述符
代码段:
00cffb00 0000ffff
1100 1111 1111 1011 0000 0000
Base:
0000 0000
Limit:
ffffff
ffffff * 0x1000
FFFFFF000+0X1000
10 0000 0000 - 1
FFFF FFFF
G = 1 Limit单位是页
G = 0 Limit单位是字节
页
- 大页 4M
- 小页 4K
寻址空间:
0000 0000 ~ FFFF FFFF
数据段
00cff300 0000ffff
BASE:
0000 0000
Limit:
FFFF FFFF
FS段
0040f300 00000fff
G=0 字节
0000 0000 0100 0000 1111 0011 0000 0000
fff * 1
fff
字节
0 ~ fff
4GB空间 虚拟的4GB空间
- 低2GB是每个进程独享的
- 高2GB是内核空间,是共享的
base+offset = 虚拟地址
页
内存的一种管理模式
29912
CR3
PDPTE -> PTE - PDE -> 物理页 (4k)
76B26000 E9 A1 B0 76
01 (PDPTE) (1 * 8)
110110101(PTE) (1B5 * 8 )
100100110(PDE) (126 * 8)
00000000000(页内偏移) (000)
R3 用户层 TEB(线程环境块)
R0 系统内核 驱动 KPCR(CPU状态块)
调用约定
cdecl
stdcall
fastcall
thiscall
lib
静态链接库
dll
动态链接库
cdecl
C语言调用约定
push eax
push eax
call printf
add esp,8
stdcall
Win32调用约定
push 1
push 2
push 3
push 4
call messagebox
fastcall
x64 调用约定
前四个参数使用寄存器传参,后面使用堆栈
rcx rdx r8 r9
x86基本框架
.586
.model flat,stdcall
option casemap:none
includelib ucrt.lib
includelib legacy_stdio_definitions.lib
includelib User32.lib
includelib Kernel32.lib
MessageBoxA PROTO hWnd:DWORD,lpText:BYTE,lpCaption:BYTE,uType:DWORD
ExitProcess PROTO uType:DWORD
extern printf:proc
extern scanf:proc
extern putchar:proc
.data
szStr db 'Hello World!',0
.code
main proc
lea eax,szStr
push eax
call printf
add esp,4
push 0
call ExitProcess
add esp,4
main endp
end
内联汇编
单行内联汇编
_ASM/ mov nNum, 100;
多行内联汇编
_ASM/ {
mov nNum,100
mov eax,nNum
push eax
mov eax,fNum
push eax
call printf
add esp,8
}
遍历数组
按结尾数据遍历
#include <stdio.h>
#include <stdlib.h>
int main()
{
int nNum;
char* fNum = "%d\n";
int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
_ASM/ {
xor esi,esi
jmp loopX
loopM:
inc esi
loopX:
mov edi,[arr+esi*4]
push edi
mov eax,fNum
push eax
call printf
add esp,8
cmp edi,0
jne loopM
}
system("pause");
return 0;
}
按长度进行遍历
#include <stdio.h>
#include <stdlib.h>
int main()
{
int nNum;
char* fNum = "%d\n";
int arr[10];
for (size_t i = 0; i < 10; i++)
{
arr[i] = i;
}
_ASM/ {
xor edi,edi
xor esi,esi
xor edx,edx
mov edi,9h
jmp Mloop
Xloop:
inc esi
Mloop:
mov edx, [arr + esi * 4]
push edx
mov eax,fNum
push eax
call printf
add esp,8
cmp edi,esi
jne Xloop
}
system("pause");
return 0;
}
结构体和API
汇编语言结构体
.586
.model flat,stdcall
.data
info struct ;声明结构体
x dword ?
y dword ?
info ends
m_info info <> ;初始化结构体
.code
main proc
mov eax,5
mov m_info.x,eax
mov ebx,m_info.x
mov eax,eax
main endp
end main
x64汇编
x64和x86汇编寄存器区别
x86 :
eax ebx ecx edx esi edi ebp esp eip eflags
x64 :
rax rbx rcx rdx rsi rdi rbp rsp rip rflags
x64扩展 :
r8 r9 r10 r11 r12 r13 r14 r15
汇编代码剽窃小技巧
- 所有的jcc死地址,都要改成标号
- 所有的call,都要确定,是系统函数,还是自己的函数,想办法call过去
- 一切检查或者初始化类的无用代码,全部去掉
- 一些必要逻辑里的不合理东西,去掉这些,然后自己写代码,让他合理化
16位/32位/64位汇编的区别
区别
16位:实模式,16位处理器内的内部,最多可以处理存储的长度为16位。
32位:保护模式,32位处理器内的内部,最多可以处理存储的长度为32位。
64位:保护模式,64位处理器内的内部,最多可以处理存储的长度为64位。
通用寄存器简介
位数 | 通用寄存器 | 扩展 |
---|---|---|
16 | AX BX CX DX SI DI BP SP | R8W R9W R10W R11W R12W R13W R14W R15W |
32 | EAX EBX ECX EDX ESI EDI EBP ESP | R8D R9D R10D R11D R12D R13D R14D R15D |
64 | RAX RBX RCX RDX RSI RDI RBP RSP | R8 R9 R10 R11 R12 R13 R14 R15 |
基本执行环境
32位 | 64位 |
---|---|
8个32位通用寄存器 | 16个64位通用寄存器 |
标志寄存器EFLAGS | 标志寄存器RFLAGS |
指令寄存器EIP | 指令寄存器RIP |
EFLAGS寄存器
Eflags寄存器,Eflags寄存器包含了独立的二进制位,用于控制CPU操作。或是反应一些CPU操作的结果。有些指令可以测试和控制这些单独的处理器标志位。
MMX寄存器
在实现高级多媒体和通信应用时,MMX技术提高了Intel处理器的性能。8个64位MMX寄存器支持成为SIMD的特殊指令。顾名思义,MMX指令对MMX寄存器中的数据值直接进行并行操作。虽然它们看上去是独立的寄存器,但是MMX寄存器实际上是浮点单元中使用的同样寄存器的别名。
通用寄存器
eax:累加器,操作数和结果数据累加器,返回值运算结果一般都存储在这里
ebx:基地址,DS段的数据指针,在内存寻址的时候存放基地址
ecx:计数器,字符串和循环操作的计数器
edx:用于存储部分乘法结果和部分除法被除数
ebp:基址指针,SS段的数据指针
esp:栈帧指针,一般指向栈顶,所以也被称为栈顶指针
edi:字符串操作的目标指针,ES段的数据指针
esi:字符串操作的源指针,SS段的数据指针
冯诺依曼
计算机科学的奠基者
- 艾伦·麦锡森·图灵 - - - 图灵机
- 约翰·冯·诺依曼 - - - 数据存储的体系结构
约翰·冯·诺依曼
1903年 出生犹太家庭
1926年 布达佩斯大学数学博士学位
1930年 接受了普林斯顿大学客座教授的职位
1931年 普林斯顿大学终身教授
1933年 普林斯顿高等研究院
1937年 美国公民
1938年 获博修奖
1954年 美国原子能委员会委员
1957 在华盛顿德里医院去世
冯诺依曼体系
计算机由控制器、运算器、存储器、输入设备、输出设备五部分组成。
冯诺依曼提出的计算机体系结构,奠定了现代计算机的结构理念
内存基础
什么是内存
在冯诺依曼结构中用来存储程序和数据的部件叫做存储器,我们的计算机可以正常的运行,主要依靠的就是存储器的记忆能力。存储器分为主存储器和辅助存储器,主存储器也叫内存储器,也就是我们常说的内存
内存寻址范围
现在主流的系统是32位系统和64位系统
32位系统内存的寻址范围是0x00000000-0xFFFFFFFF
32位系统内存最大寻址范围是0xFFFFFFFF+1(4294967296) - - - 4GB
64位内存的寻址范围是0x0000000000000000-0xFFFFFFFFFFFFFFFF
内存和寄存器的区别
寄存器:数量少,在CPU内部,速度极快,但是价格昂贵
内存:数量庞大,相对寄存器而言,速度较慢,但是价格便宜
内存的五种表现形式
立即数:
MOV EAX, DWORD PTR DS:[0x???????]
寄存器:
MOV EBX, 0x????????
MOV EAX, DWORD PTR DS:[EBX]
寄存器+立即数
MOV EBX, 0x???????
MOV EAX, DWORD PTR DS:[EBX+4]
比例因子:[REG+REG*{1,2,4,8}]
数组元素地址=数组首地址+元素索引*数组元素占用空间
MOV EAX, 0x????????
MOV EBX, 0x2
MOV ECX, DWORD PTR DS:[EAX+EBX*4]
比例因子+立刻数:
MOV EAX, 0x????????
MOV EBX, 0x2
MOV ECX, DWORD PTR DS:[EAX+EBX*4+1]
数据存储模式
大端序:数据高位在内存低位,数据低位在内存高位
小端序:数据高位在内存高位,数据低位在内存低位
地址0x77 66 55 44
大端序:念的时候是77 66 55 44
小端序:念的时候是44 55 66 77
大端序常用于ARM架构
小端序常用于x86、AMD64架构
EFLAGS寄存器
CF:进位借位标志位
PF:奇偶标志位
AF:辅助进位标志位
ZF:0标志位
SF:符号标志位
TF:单目标志位
IF:中断标志位
DF:方向标志位
OF:溢出标志位
数学运算
加法
加法指令 ADD(Addition)
格式:ADD OPRD1, OPRD2
功能:两数相加
加法指令运算的结果对CF、SF、OF、PF、ZF、AF都会有影响
不允许OPRD1与OPRD2同时为存储器
带进位加法指令 ADC(Addition Carry)
格式:ADC OPRD1, OPRD2
功能:OPRD1 = OPRD1 + OPRD2 + CF
减法
减法指令 SUB(Subtract)
格式:SUB OPRD1, OPRD2
功能:两个操作数的相减,即从OPRD1中减去OPRD2,其结果放在OPRD1中。指令的类型及对标志位的影响与ADD指令相同,注意立即数不能用于目的操作数,两个存储器操作数之间不能直接相减,操作数可为8位或16位的无符号数或带符号数。
带借位减法指令 SBB(Subtraction with Borrow)
格式:SBB OPRD1, OPRD2
功能:进行两个操作数的相减再减去CF进位标志位,即从OPRD1 = OPRD1 - OPRD2 -CF,其结果放在OPDR1中。
乘法
无符号数乘法指令 MUL(Multiply)
格式:MUL OPRD
带符号数乘法指令 IMUL(Integer Multiply)
格式:IMUL OPRD、
功能:乘法操作
OPRD为通用寄存器或寄存器操作数
本指令影响标志位CF及OF
除法
无符号数除法指令 DIV(Division)
格式:DIV OPRD
功能:实现两个无符号二进制数除法运算
带符号数除法指定 IDIV(Integer Division)
格式:IDIV OPRD
功能:这实现两个带符号数的二进制除法运算
16bit,分存2个8bit AH:AL 商AL 余AH
32bit,分存2个16bit DX:AX 商AX 余DX
64bit,分存2个32bit EDX:EAX 商EAX 余EDX
128bit,分存2个64bit RDX:RAX 商RAX 余RDX
自增
加1指令 INC(Increment by 1)
格式:INC OPRD
功能:OPRD = OPRD + 1
自减
减1指令 DEC(Decrement by 1)
格式:DEC OPRD
功能:OPRD = OPRD - 1
逻辑运算
与
逻辑与运算指令 AND
格式:AND OPRD1, OPRD2
功能:对两个操作数实现按位逻辑与运算,结果送至目的操作数。本指令可以进行字节或字的 ‘与‘ 运算,OPRD1 < - - OPRD1 and OPRD2.
本指令影响标志位PF、SF、ZF,使CF=0、OF=0.例如,在同一个通用寄存器自身相与时, 操作数虽不变,但使CF置零。本指令主要用于修改操作数或置某些位为零。
或
逻辑或指令 OR
格式:OR OPRD1, OPRD2
功能:OR指令完成对两个操作数按位的 ‘或’ 运算,结果送至目的操作数中,本指令可以进 - - - - 行字节或字的 ‘或’ 运算。OPRD1 < - - OPRD1 OR OPRD2。
异或操作
逻辑异或运算指令 XOR
格式:XOR OPRD1, OPRD2
功能:实现两个操作数按位 ‘异或’ 运算,结果送至目的操作数中。OPRD1 < - - OPRD1 XOR OPRD2
非运算
逻辑操作符 NOT
格式:NOT exp
功能:按位求反
堆栈操作
什么是堆栈
- 栈是一个后进先出的存储区域,位于堆栈段中,SS段寄存器描述的就是堆栈段的地址
- 栈的数据出口位于栈顶,也就是esp寄存器所指向的位置
- 栈顶是低位,也就是地址较小的一侧,由ebp寄存器指向的栈底,并不会改变
栈操作指令
PUSH:压栈操作,32位汇编首先ESP-4,留出一个空间,然后把要压入栈中的内容压入
POP:出栈指令,32位汇编首先将栈顶的数据弹出给指定的目标,然后ESP+4,清掉空间
栈的作用
- 存储少量数据
- 保存寄存器环境
- 传递参数
数据移动指令
MOV指令
数据传送指令MOV
格式:MOV OPRD1, OPRD2
功能:本指令将一个源操作数送到目的操作数中,即OPRD1 < - - OPRD2
说明:
- OPRD1为目的操作数,可以是寄存器、存储器、累加器
- OPRD2为源操作数,可以是寄存器、存储器、累加器和立即数
LEA指令
有效地址传送指令 LEA
格式:LEA OPRD1,OPRD2
功能:将源操作数给出的有效地址传送到指定的寄存器中
OPRD1必须是寄存器
XCHG指令
数据交换指令 XCHG
格式:XCHG OPRD1, OPRD2 其中的OPRD1为目的操作数,OPRD2为源操作数
功能:将两个操作数相互交换位置,该指令把源操作数OPRD2与目的操作数OPRD1交换
比较指令
CMP指令
比较指令 CMP(Compare)
格式:CMP OPRD1, OPRD2
功能:对两数进行相减,进行比较
TEST指令
测试指令 TEST
格式:TEST OPRD1,OPRD2
功能:其中OPRD1、OPRD2的含义同AND指令一样,也是对两个操作数进行按位的 ‘与’ 运算,- - - - 唯一不同之处是不将 ‘与’ 的结果送目的操作数,即本指令对两个操作数的内容均不进行修改,仅是在逻辑与操作后,对标志位重新置位
JCC条件转移指令
常用的JCC指令
JMP:无条件跳转
JZ/JE:ZF = 1 等于0或相等跳转
JNZ/JNE:ZF = 0 不等于0或者不相等跳转
JBE/JNA:CF = 1/ZF = 1 低于等于/不高于跳转
JNBE/JA:CF = 0/ ZF = 0 不低于等于/高于跳转
JL/JNGE:SF!=OF 小于/不大于等于跳转
JNL/JGE:SF = OF 不小于/大于等于跳转
JCC表
JCC指令 | 中文含义 | 英文原意 | 检查符号位 | 典型C应用 |
---|---|---|---|---|
JZ/JE | 若为0则跳转;若相等则跳转 | jump if zero;jump if equal | ZF=1 | if (i == j);if (i == 0); |
JNZ/JNE | 若不为0则跳转;若不相等则跳转 | jump if not zero;jump if not equal | ZF=0 | if (i != j);if (i != 0); |
JS | 若为负则跳转 | jump if sign | SF=1 | if (i < 0); |
JNS | 若为正则跳转 | jump if not sign | SF=0 | if (i > 0); |
JP/JPE | 若1出现次数为偶数则跳转 | jump if Parity (Even) | PF=1 | (null) |
JNP/JPO | 若1出现次数为奇数则跳转 | jump if not parity (odd) | PF=0 | (null) |
JO | 若溢出则跳转 | jump if overflow | OF=1 | (null) |
JNO | 若无溢出则跳转 | jump if not overflow | OF=0 | (null) |
JC/JB/JNAE | 若进位则跳转;若低于则跳转;若不高于等于则跳转 | jump if carry;jump if below;jump if not above equal | CF=1 | if (i < j); |
JNC/JNB/JAE | 若无进位则跳转;若不低于则跳转;若高于等于则跳转; | jump if not carry;jump if not below;jump if above equal | CF=0 | if (i >= j); |
JBE/JNA | 若低于等于则跳转;若不高于则跳转 | jump if below equal;jump if not above | ZF=1或CF=1 | if (i <= j); |
JNBE/JA | 若不低于等于则跳转;若高于则跳转 | jump if not below equal;jump if above | ZF=0或CF=0 | if (i > j); |
JL/JNGE | 若小于则跳转;若不大于等于则跳转 | jump if less;jump if not greater equal | SF != OF | if (si < sj); |
JNL/JGE | 若不小于则跳转;若大于等于则跳转; | jump if not less;jump if greater equal | SF = OF | if (si >= sj); |
JLE/JNG | 若小于等于则跳转;若不大于则跳转 | jump if less equal;jump if not greater | ZF != OF 或 ZF=1 | if (si <= sj); |
JNLE/JG | 若不小于等于则跳转;若大于则跳转 | jump if not less equal;jump if greater | SF=0F 且 ZF=0 | if(si>sj) |
串操作指令
MOVS指令
字符串传送指令 MOVS
格式:MOVS OPRD1, OPRD2 MOVSB MOVSW
功能:OPRD1 < - - OPRD2
说明:
- 其中OPRD2为源串符号地址,OPRD1为目的串符号地址
STOS指令
字符串存储指令 STOS
格式:STOS OPRD
功能:把AL(字节)或AX(字)中的数据存储到DI为目的串地址指针所寻址的存储器单元中去指针DI将根据DF的值进行自动调整
REP指令
重复前缀说明
格式:
- REP ;CX<>0 重复执行字符串指令
- REPZ/REPE ;CX<>0 且ZF=1重复执行字符串指令
- REPNZ/REPNE ;CX<>0 且ZF=0重复执行字符串指令
功能:在串操作指令前加上重复前缀,可以对字符串进行重复处理。由于加上重复前缀后,对应的指令代码是不同的,所以指令的功能便具有重复处理的功能,重复的次数存放在CX寄存器中
CALL与RETN
CALL指令
过程调用指令 CALL
格式:CALL OPRD
功能:过程调用指令
相当于:
push eip
jmp OPRD
RETN指令
返回指令,相当于:
pop eip
jmp eip
函数
过程调用-函数
function proc
code
function end
参数传递方式:
- 寄存器传参
- 堆栈传参
WIN32汇编入门
什么是API?
API(Application Programming Interface, 应用程序接口)是一些预先定义的函数,或指软件系统不同组成部分衔接的约定。目的是提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问原码,或理解内部工作机制的细节
Windows系统除了协调应用程序的执行、内存的分配、系统资源的管理外,同时他也是一个很大的服务中心。调用这个服务中心的各种服务(每一种服务就是一个函数)可以帮助应用程序达到开启视窗、描绘图形和使用周边设备等目的,由于这些函数服务的对象是应用程序,所以称之为Application Programming Interface, 简称API函数。WIN32 API也就是Microsoft Windows 32位平台的应用程序编程接口
凡是在 Windows 工作环境底下执行的应用程序,都可以调用Windows API