CTF权威指南 笔记 -第三章汇编基础-3.2-x86/x64汇编基础

这节介绍PC最常见的架构 x86和扩展 x64框架

CPU操作模式

对x86处理器而言 有三个最主要的保护模式

保护模式
实地址模式
系统管理模式

还有一个保护模式的子模式

虚拟8086模式

保护模式

保护模式是处理原生状态 这个时候所有指令和特性都是可以使用的 
分配给程序的独立内存区域 叫做内存段
处理器阻止程序使用自身段以外的内存区域
同时 寄存器扩展到32位 解决内存寻址空间的问题

并且各个指令区分特权等级 危险的只有较高权限才能执行
增加CPU对内存地址的访问的权限控制

实地址模式

是早起intel的编程环境 该模式程序可以直接访问硬件和其实际地址
没有经过虚拟地址的映射
方便了程序开发 

没有权限的说法
程序发往内存是直接读写 没有审核
CPU可以执行任何程序 内存之间没有隔离保护

系统管理模式

为操作系统提供电源管理 安全保护等特性机制

对于x86_64的处理器

还有一个操作模式

IA_32E

这个模式包含着两个子模式

兼容模式:
现有的32位和16位不需要重新编译


64位模式:
处理器将在64位的地址空间下运行程序

语法风格

x86 有两个语法风格

AT&T风格和Intel风格

gcc gdb objdump都默认使用AT&T风格

 这里我们关注一下

间接寻址的格式

AT&A
%sreg:disp(%base,index,scale)

%sreg:是段寄存器 用于制定段地址

cs:

disp是偏移量 是相对于基地址的偏移 可以是在程序计算出来的值

cs:4

%base是基地址寄存器 存储内存块的开始 

cs:4(%eax,)

index为变址寄存器 表示偏移地址的计算

cs:4(%eax,%ebx)

scale为比例因子 可以为 1,2,4,8等 表示 index*scale作为偏移地址

cs:4(%eax,%ebx,2)

mov %ecx,4(%eax,%ebx,2)
表示 从ecx的地方读取 存入 eax+2*ebx+4的地址去 

intel就很简单
mov [esi+eax*4+0x10], ebx

这里就可以发现ebx存入 esi+eax*4-0x10的地址去

操作位数

AT&T
指令+l/w/b是什么意思呢
我们先知道l/w/b代表什么
l为32位整数双字
w为16位整数单词
b为8位整数字节
举例

addl $0x12345678,%ebx
把双字数据0x12345678存入ebx中 并且大小为32位
但是实际操作数为 33位
addw $0x1234, %ah
把字数据0x1234存入 ah中 大小为16位
实际操作数为17位

addb 0x11,%al
把字节数据 0x11存入al中 大小为8位
实际操作数为9位
不加的话默认64位



Intel
qword ptr 为64位操作数

dword ptr 为32位操作数

寄存器和数据类型

寄存器

从8位处理器到16位 再到32和64位 寄存器名字也不一样

 在 64位模式下 操作数大小还是32位 如果我们需要把他变为64位 只需要赋值给64位的寄存器

mov rax, eax

并且还增加了 8个带符号的通用寄存器(R8~R15)

数据常量

对于整数常量 1234 可以是10进制 16进制 或者8进制 所以需要后缀进行区分

如果16进制中包含了字母 为了防止 汇编器把他当汇编指令或标识符

需要再字母开头的十六进制中+0

例如 0ABCh

浮点数常量

也叫做实数常量

x86有单独的浮点数寄存器和浮点数指令 对浮点数进行操作

我们通常以10进制表示浮点数

16进制表示编码浮点数

一个浮点数应该包含一个十进制整数和一个十进制小数点

1. 
+2.3
-3.1415
26.E5
都是合法的

字符串常量

字符串常量是用 ''/ ""括起来的字符序列(包含空格符)

"hello world",'he says "hello"',"he's a funny man" 这是合法的 字符串嵌套

 

 字符串处理在内存中是以整数字节序列保存的

字符串"ABCDEFGH'’在gdb中的显示样子为

 

这个是以16进制打印 

 这里能发现是小端序 这个我们后面再学

数据传送与访问

MOV

MOV是指令是最基本的数据传输指令 几乎所有程序都在使用

可以在一个程序中只使用mov就可以完成 证明了 图灵完备

MOV指令的基本格式 第一个参数为目的操作数 第二个参数为源操作数

MOV EAC,ECX 就是把ECX的值拷贝到EAX中


MOV 支持从寄存器到寄存器 
从内存到寄存器
从寄存器到内存
从立即数到内存
从立即数到寄存器的数据传输


但是!!!不支持内存到内存的传输

想要实现这个 就需要一个寄存器作为中转
MOV EAX,0        EAX= 00000000h
MOV AL,78h       AL = 00000078h
MOV AX,1234h     AX = 00001234h
MOV EAX,12345678h  EAX=12345678h

如果我们要把操作数扩展到大的操作数的话 我们需要通过全零扩展和符号扩展

全零扩展和符号扩展

我们使用例子来说明全零扩展和符号扩展

unsigned char x = 0xC1; // 11000001


全零扩展  直接把c1存到底8位 剩下24位通过 0 填充

x = 0x000000c1

符号扩展 因为0xc1 为 11000001 最高位为1(符号位)所以通过1复制到高24位

x=0xffffffc1

XGHG

数据访问内存指令还有 XCHG 允许我们交换两个操作数的值

可以是寄存器到寄存器

内存到寄存器

或者寄存器到内存

数组

在x86中 使用变量名和偏移量来代表一个 直接偏移量操作数

.data 
    testarray BYTE 99h, 98h, 97h, 96h
.code
    MOV AL,testarray        al=99h
    MOV BL,[testarray+1]    bl=98h
    MOV CL,[testarray+2]    cl=97h

双字数组的汇编代码段 需要使用符合数组元素的偏移量才可以正确表示数组元素的位置

就是需要准确的偏移量

.data 
    testarrayW WORD 99h, 98h, 97h
    testarrayD DWORD 1000h,2000h,3000h
.code
    MOV AX,testarrayW        AX=99h
    MOV BX,[testarrayW+2]    BX=98h
    MOV ECX,testarrayD        ECX=1000h
    MOV EDX,[testarrayD+4]    EDX=2000h

算数运算和逻辑运算

最简单的算数运算指令 INC和DEC 分别用于操作数+1 操作数-1

这两个指令的操作数既可以是寄存器 也可以是内存

.data 
    testWord WORD 1000h
.code
    INC EAX
    DEC testWord

补码

计算机底层数据是以补码表示的

两个机器数相加的补码相加可以先对两个机器数补码 然后相加

在采用补码形式表示时

进行加法运算 就可以把符号位和数值位一起进行运算 符号位有进位直接舍弃

结果之和为两个数的补码之和

ADD

长度相同的操作数进行相加

.data
    testData Dword 10000h
    testData2 Dword 20000h
.code
    MOV EAX, testData       EAX=10000h
    ADD EAX, testData2      EAX=30000h

SUB

从目的操作数中减去源操作数

.data
    testData DWORD 20000h
    testData2 DWORD 10000h
.code
    MOV EAX, testData     eax=20000h
    SUB EAX, testData2    eax=10000h

NEG

NGS是把操作数转化为二进制补码 并且把操作数的符号位取反

跳转指令和循环指令

CPU是顺序加载并且执行程序的

但是 指令集中会存在 条件型指令

将根据CPU的标志位寄存器决定程序控制流的走向

在x86中 每一个条件指令都暗含着跳转指令

跳转指令分成
有条件跳转 和 无条件跳转

JMP

为无条件跳转

    JMP labe11
    MOV EBX, 0 
labe11:
    MOV EAX, 0 

LOOP

loop创建 循环代码块

ECX寄存器为循环的计数器

CX是 loop指令和loopw指令的默认循环计数器
ECX寄存器是loopd指令的循环计数器
64位的x86 loop指令使用RCX为默认循环计数器

经过一次循环

ECX将-1

MOV AX, 0 
MOV ECX, 3
L1:
INC AX
LOOP L1
XOR EAX,EBX

循环一次 ECX-1 并且和0进行判断

如果ECX=0 就不进行跳转 执行 XOR EAX,EBX

如果ECX!=0 就进行跳转 重新执行 L1

注意

如果在loop开始前 EAX设置为0 
-1 就会跳转到 FFFFFFFFh
非常大的循环

栈和函数的调用

CALL

 相当于PUSH 返回地址   JMP 函数地址的指令序列

RET

相当于POP 返回地址 JMP 返回地址的指令序列

如果要存储变量的话

这些部分我以前写过

直接给出

1.栈的介绍-C语言调用函数(一)_双层小牛堡的博客-CSDN博客

1.栈的介绍-C语言调用函数(二)_双层小牛堡的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值