【逆向】汇编相关知识

汇编

cpu整体的工作分为三类:

  1. 读取指令
  2. 指令译码
  3. 执行单元(计算、读写内存、设置寄存器、跳转)

读取指令、内存读写都需要CPU控制其他硬件,比如:内存、显卡

寄存器

32位CPU所含有的寄存器

  • 4个数据寄存器(EAX、EBX、ECX和EDX)
  • 2个变址和指针寄存器(ESI和EDI)
  • 2个指针寄存器(ESP和EBP)
  • 6个段寄存器(ES、CS、SS、DS、FS和GS)
  • 1个指令指针寄存器(EIP)
  • 1个标志寄存器(EFlag)

内存大小:BYTE(1个字节)、WORD(2个字节)、DWORD(4个字节)、QWORD(8个字节)

32位通用寄存器

寄存器序号存储数据范围描述
EAX00-0xFFFFFFFF数据寄存器(累加器)
ECX10-0xFFFFFFFF数据寄存器(计数寄存器)
EDX20-0xFFFFFFFF数据寄存器(数据寄存器)
EBX30-0xFFFFFFFF数据寄存器(基地址寄存器)
ESP40-0xFFFFFFFF指针寄存器(栈顶指针)
EBP50-0xFFFFFFFF指针寄存器(栈底指针)
ESI60-0xFFFFFFFF变址寄存器 (用于存放存储单元在段内的偏移量)
EDI70-0xFFFFFFFF变址寄存器(用于存放存储单元在段内的偏移量)
32位16位8位
EAXAX(EAX的低16位)AH、AL
ECXCX(ECX的低16位)CH、CL
EDXDX(EDX的低16位)DH、DL
EBXBX(EBX的低16位)BH、BL
ESPSP(ESP的低16位)-
EBPBP(EBP的低16位)-
ESISI(ESI的低16位)-
EDIBI(EBI的低16位)-

段寄存器

段寄存器描述
CS代码段
DS数据段
SS堆栈段
ES辅助段寄存器
FS辅助段寄存器
GS辅助段寄存器

指令指针寄存器(EIP)

EIP:保存程序当前即将要执行的指令的地址

  1. 不可随意修改
  2. 如果要修改的话,要使用jcc指令

标志寄存器(EFLAG)

EFLAG:

1514131211109876543210
OFDFIFTFSFZFAFPFCF
溢出方向中断陷阱符号辅助奇偶进位

注意只有运算指令(算术运算,逻辑运算)才能改变下面标志位,mov指令不算运算指令

  • CF(Carry Flag):进位标志,当运算结果的最高有效位有进位(借位)时,进位标志为1,否则为0

mov al, 0x80; add al, 0x80; 此时CF会置为1
mov al, 0x80; add ax, 0x80; CF则不会置为1

  • OF(OverFlow Flag):溢出标志,若运算结果有溢出则OF=1否则=0。注意:溢出是相对于有符号数的。例如:

Mov al,0x80 ;SUB AL,0X1 就会产生溢出,因为AL是8位寄存器 对于有符号数也就是-128-127
0x80就是-128 再减1就会产生溢出
判断方法:按无符号数计算,看最高位是否变化,如果变了则置1,否则置0。

  • ZF(Zero Flag):零标志,若运算结果为0,则ZF=1否则ZF=0
  • SF(Sgin Flag):符号标志,运算结果符号位为1,则SF=1,否则SF=0;(有符号数二进制位最高有效位表示符号)

汇编指令

汇编指令集

  • X64指令集
  • X64程序使用的是X64指令集,64位指令集有AMD64、EM64T、IA-64三种。
  • AMD64是最早推出的,Intel与惠普联合推出了IA-64,但是没用起来。
  • Intel后来直接拷贝AMD的指令集推出了IA-32E后改名为EM64T,也就是Intel64。
  • 这两套指令集其实是完全一样的,我们统称X64指令集。
  • X64与X86的区别:
  • X86中原有的寄存器在X64中均扩展为64位,且名称的第一个字母E改为R。但是我们依然可以使用低位的寄存器 RAX、EAX、AX、AL、AH
  • EIP 变成 RIP
  • EFLAG 变成 RFLAG
  • X64多了8个64位通用寄存器:R8-R15。每个寄存器也都可以拆分为 32位16位8位例如:R8D R8W R8B
  • X64程序变化:
  • 32位程序中的函数调用约定被废除了
  • x64应用程序只有1种调用约定 类似于fastcall。前4个参数使用寄存器传递,如果参数超过4个,多余的参数就放在栈里,人栈顺序为从右到左,由函数调用方平衡栈空间。前4个参数存放的寄存器是固定的,分别是第1个参数RCX、第2个参数RDX、第3个参数R8、第4个参数R9,其他参数从右往左依次人栈。
  • x64应用程序调用函数时参数会放到rsp-rbp段,局部变量会存放到rbp+xxx中。而32位程序中参数会放到ebp+xxx段,局部变量会放到esp-ebp段中。

汇编指令由操作码和操作数组成, 以下是全部操作数形式:

操作数描述举例
r88位寄存器
r1616位寄存器
r3232位寄存器
reg任意寄存器
seg段寄存器
m88位内存空间
m1616位内存空间
m3232位内存空间
mem任意内存空间
i88位立即数
i1616位立即数
i3232位立即数
imm任意大小立即数

数据传输类型指令

在这里插入图片描述

  • mov

mov a,b:

  1. a不能取立即数
  2. a和b的位数要一致
  3. a和b不能同时取内存地址
  4. 使用地址的时候要慎重,不能操作无法操作的内存空间
  • mov eax, 0x123 : 立即数
  • mov eax, ecx :寄存器
  • mov bh, al
  • mov eax, dword ptr ds:[0x12121212]:内存([]表示地址,dword表示要从该内存取4个字节的内容)
  • mov ax, word [0x12121212]
  • mov word ptr ds:[0x12121212], ax
  • movzx

movzx a,b

  1. a必须是寄存器,b可以是寄存器或内存。a,b都不能是立即数
  2. b的位数要小于a的位数
  3. 不足的部分用0填充
  • movzx eax, ax
  • movsx

movsx a,b

  1. a必须是寄存器,b可以是寄存器或内存。a,b都不能是立即数
  2. b的位数要小于a的位数
  3. 不足的部分用b的符号位填充
  • movsx eax, ax
  • lea

lea a,b

  1. a和b不能是立即数
  2. a和b的位数要相同
  3. a和b不能同时取内存地址
  4. 使用地址的时候要慎重,不能操作无法操作的内存空间
  • lea ecx, dword ptr ds:[eax]:将eax中的值赋值给ecx(而不是把eax当作地址),相当于 mov ecx, eax
  • lea cx,word ptr ds:[edx]

算数运算指令

在这里插入图片描述
在这里插入图片描述

  • add

add a,b:

  1. a不能是立即数
  2. a和b的位数要一致。若a的位数多于b的位数,则b的高位填充0;若b的位数多于a的位数,则错误
  3. a和b不能同时取内存地址
  4. 使用地址的时候要慎重,不能操作无法操作的内存空间
  • add ecx, 0x12345678
  • add eax, ecx
  • add dword ptr ds:[0x12121212], eax
  • sub

sub a,b:

  1. a不能是立即数
  2. a和b的位数要一致。若a的位数多于b的位数,则b的高位填充0;若b的位数多于a的位数,则错误
  3. a和b不能同时取内存地址
  4. 使用地址的时候要慎重,不能操作无法操作的内存空间
  • sub ecx, 0x12345678
  • sub eax, ecx
  • sub dword ptr ds:[0x12121212], eax

位运算指令

在这里插入图片描述

  • and

and a,b:

  1. a不能是立即数
  2. a和b的位数要一致。若a的位数多于b的位数,则b的高位填充0;若b的位数多于a的位数,则错误
  3. a和b不能同时取内存地址
  4. 使用地址的时候要慎重,不能操作无法操作的内存空间
  • and ecx, 0x12345678
  • and eax, ecx
  • and dword ptr ds:[0x12121212], eax
  • or

or a,b:

  1. a不能是立即数
  2. a和b的位数要一致。若a的位数多于b的位数,则b的高位填充0;若b的位数多于a的位数,则错误
  3. a和b不能同时取内存地址
  4. 使用地址的时候要慎重,不能操作无法操作的内存空间
  • or ecx, 0x12345678
  • or eax, ecx
  • or dword ptr ds:[0x12121212], eax
  • xor

xor a,b:

  1. a不能是立即数
  2. a和b的位数要一致。若a的位数多于b的位数,则b的高位填充0;若b的位数多于a的位数,则错误
  3. a和b不能同时取内存地址
  4. 使用地址的时候要慎重,不能操作无法操作的内存空间

应用:

  1. 一般用于清零操作:xor eax, eax
  2. 一般用于加密解密:xor eax, 0x51515151; xor eax, 0x51515151; eax两次异或同一个值,eax会恢复为原来的值。
  • xor ecx, 0x12345678
  • xor eax, ecx
  • xor dword ptr ds:[0x12121212], eax
  • not

not a:

  1. a不能是立即数
  2. 使用地址的时候要慎重,不能操作无法操作的内存空间
  • not eax
  • not word ptr ds:[0x12121212]

逻辑运算指令cmp、test

在这里插入图片描述

JCC指令

在这里插入图片描述
在这里插入图片描述

CALL、RETN指令

call指令:

Call指令类似于JMP指令,也是用来修改EIP的值的,CALL指令后面跟一个操作数,操作数可以是寄存器、内存地址、立即数。

  • CALL指令与JMP区别:
    它除了修改EIP寄存器之外,还修改了ESP ,等价于:

JMP 函数起始地址;
push CALL指令的下一条指令;(保存主程序运行位置)

RETN 指令:

类似于 POP EIP 指令。把栈顶的值(之前主程序运行位置)弹出到EIP中。

MOVS、STOS指令

MOVS指令:
在这里插入图片描述

只有以下三种写法:

  • MOVS BYTE PTR ES:[EDI],BYTE PTR ES:[ESI] == 简写:MOVSB
  • MOVS WORD PTR ES:[EDI],WORD PTR ES:[ESI]== 简写:MOVSW
  • MOVS DWORD PTR ES:[EDI],DWORD PTR ES:[ESI]== 简写:MOVSD

STOS指令:
在这里插入图片描述

只有以下三种写法:

  • STOS BYTE PTR ES:[EDI] 简写:STOSB
  • STOS WORD PTR ES:[EDI] 简写:STOSW
  • STOS DWORD PTR ES:[EDI] 简写:STOSD

REP:
重复操作前缀。通常会加到 MOVS、STOS等指令前面,表示重复执行后面的指令,重复的次数由ECX寄存器的值决定。

  • rep movsb
  • rep stosb

寻址方式

操作数寻址方式举例
数字立即数寻址mov eax,0x12345678
寄存器寄存器寻址mov eax,ecx
内存直接寻址mov eax,[0x12345678]
[reg]寄存器间接寻址mov eax,[ecx]
[reg+imm]寄存器相对寻址mov eax,[eax+0xc]
[reg+reg+imm]相对基址变址寻址mov eax,[ecx+ebx+4]
[reg+reg*1,2,4,8]带比例存储器寻址mov eax,[ecx+ebx*2]
  • lea

lea a,b

  1. a和b不能是立即数
  2. a和b的位数要相同
  3. a和b不能同时取内存地址
  4. 使用地址的时候要慎重,不能操作无法操作的内存空间
  • lea ecx, dword ptr ds:[eax]:将eax中的值赋值给ecx(而不是把eax当作地址),相当于 mov ecx, eax
  • lea cx,word ptr ds:[edx]

堆栈

堆栈的概念和操作

堆栈:指的就是内存空间中的栈内存

栈内存:

  1. 不需要主动申请,由系统自动分配
  2. 不需要主动释放,系统自动释放
  3. 存放局部变量,函数参数

数据结构上的特点:

  1. 后进先出
  2. 从高地址往低地址存储(栈底(EBP寄存器):高地址;栈顶(ESP寄存器):低地址)

栈内存访问方式:

  1. 栈顶 + 偏移
  2. 栈底 - 偏移

栈内存的操作:

压入栈:

第一种方式:
lea ecx, dword ptr ds:[esp-0x4];
mov dword ptr ds:[ecx], 0x12345678;
sub esp, 0x4;

第二种方式:
sub esp, 0x4;
mov dword ptr ds:[esp], 0x12345678;

取出栈内存的值:

第一种方式(栈顶 + 偏移):
mov eax, dword ptr ds:[esp + 0x4]

第二种方式(栈底 - 偏移)
mov eax, dword ptr ds:[ebp - 0x4]

弹出栈:

mov ecx, dword ptr ds:[esp];
add esp, 0x4;

  • push

push a(压入栈):

  1. a最小为16位
  2. 只要执行了push,就会占4个字节的空间
  • push eax;
  • push 0x12;
  • push ax;
  • push al;(这种不行)
  • push word ptr ds:[0x12121212]
  • push dword ptr ds:[0x12121212]
  • pop

pop a(弹出栈):

  1. 相当于取出栈顶元素,存放到a中
  2. a只能是容器(即寄存器或者内存)
  3. a最小为16位
  • pop ax
  • pop eax
  • pop word ptr ds:[0x12121212]
  • pop dword ptr ds:[0x12121212]
  • pushad

pushad

  1. 将所有寄存器的内容按照序号压入栈中,首先压入eax,最后压入ebi(注意压入的esp的值是压栈之前的esp的值)
  2. 无操作数
  3. 一次扩大4*8字节空间

应用:保存程序中寄存器的值

  • popad

popad

  1. 从栈顶将内容按照序号弹出到寄存器中。首先弹出的数值存放在ebi,最后弹出到eax
  2. 无操作数
  3. 一次减少4*8字节空间

应用:保存程序中寄存器的值

函数调用约定

  1. 传参,通过push指令先将参数压入堆栈(c语言:从右向左压入)
  2. call指令函数入口地址(跳转到函数,并将下一条语句地址push到堆栈中)
  3. push ebp(保存栈底指针,用于后面恢复栈底)
  4. mov ebp, esp(提升栈底指针,准备给函数分配栈空间)
  5. sub esp, xxx(提升栈顶指针,开辟专属该函数的栈空间,共该函数局部变量使用)
  6. push 寄存器(保存进入该函数前的寄存器的值,保证函数调用完后可以恢复原始值)
  7. 执行函数代码
  8. pop 寄存器
  9. mov esp, ebp(降低栈顶指针,释放专属该函数的栈空间)
  10. pop ebp(降低栈底指针,恢复成原始值)
  11. retn(pop eip,此时栈顶指针指向的位置刚好保存着进入函数之前的下一条语句的地址)
  12. add esp, xxx(由于调用函数前push参数导致栈顶指针提升,因此调用完要释放掉参数栈空间)

问题:

  • 为什么要进行堆栈平衡?

因为Windows操作系统应用层堆栈大小默认是1M,每次调用函数都会开辟一段堆栈空间,用完如果不平衡,调用几次函数就凉凉了

  • 函数的返回值放哪里了?

大部分情况返回值会放到eax中,但不是绝对的。

  • 传参只能通过push到堆栈的方式吗?

也可以通过寄存器传参。

函数调用约定:

  • __cdecl: C/C++里中默认调用方式

特点1:push参数,顺序从右往左。
特点2:外部平衡堆栈。(即在retn之后(函数外部)释放参数栈空间)

  • __stdcall:windows API函数的调用方式 用了WINAPI的宏进行代替

特点1:push参数,顺序从右往左。
特点2:内部平衡堆栈。(即在retn之前(函数内部)释放参数栈空间)

  • __fastcall:快速调用方式 这种方式选择将参数优先从寄存器传入

特点1:寄存器传参,edx,ecx (如果参数多于2个,则用push),顺序从右向左
特点2:内部平衡堆栈。(即在retn之前(函数内部)释放参数栈空间)

  • X64程序变化:
  • 32位程序中的函数调用约定被废除了
  • x64应用程序只有1种调用约定 类似于fastcall。前4个参数使用寄存器传递,如果参数超过4个,多余的参数就放在栈里,人栈顺序为从右到左,由函数调用方平衡栈空间。前4个参数存放的寄存器是固定的,分别是第1个参数RCX、第2个参数RDX、第3个参数R8、第4个参数R9,其他参数从右往左依次人栈。
  • x64应用程序调用函数时参数会放到rsp-rbp段,局部变量会存放到rbp+xxx中。而32位程序中参数会放到ebp+xxx段,局部变量会放到esp-ebp段中。
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
嗨!对于逆向学习汇编语言的学习笔记,我可以给你一些基本的指导。首先,汇编语言是一种低级语言,它与计算机的底层硬件密切相关逆向工程则是通过分析和理解已编译的程序来获取程序的内部信息。 以下是一些学习汇编语言和逆向工程的建议: 1. 学习基础知识:了解计算机体系结构、寄存器、内存和指令集等基础概念是必要的。可以先阅读相关的书籍或在线教程,掌握这些基本概念。 2. 掌握汇编语言的语法和指令集:每种计算机体系结构都有自己的汇编语言语法和指令集。选择一种你感兴趣的体系结构(如x86、ARM等),并学习它的汇编语言。 3. 练习编写和调试汇编代码:通过编写简单的汇编代码来熟悉语法和指令集。使用调试器来单步执行代码并观察寄存器和内存的变化。 4. 分析已编译程序:选择一个目标程序进行逆向分析。使用反汇编器将程序转换为汇编代码,并分析代码的逻辑和功能。这有助于理解程序的结构和运行过程。 5. 使用调试器进行动态分析:通过调试器来动态地执行程序,并观察程序在运行时的行为。使用断点、内存查看器和寄存器查看器等工具来分析程序的状态和数据。 6. 学习逆向工程工具和技术:了解常用的逆向工程工具和技术,如IDA Pro、OllyDbg、Ghidra等。掌握这些工具的使用可以提高你的逆向分析能力。 7. 参考优秀资源:阅读与逆向工程和汇编语言相关的书籍、论文和博客,关注相关的社区和论坛。与其他逆向工程师交流经验也是很有帮助的。 记住,逆向工程是一个需要耐心和实践的过程。持续学习和实践将帮助你提高逆向分析的技能。祝你在学习汇编语言和逆向工程的过程中取得好成果!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值