shellcode汇编复习
- 一、 汇编代码复习
- 二、 基础寄存器
- 1. EAX (Accumulator Register)
- 2. EBX (Base Register)
- 3. ECX (Count Register)
- 4. EDX (Data Register)
- 5. ESI (Source Index Register)
- 6. EDI (Destination Index Register)
- 二、 基础指令
- 1. `mov` - 数据传送
- 2. `add` - 加法
- 3. `sub` - 减法
- 4. `inc` - 自增
- 5. `dec` - 自减
- 6. `lea` - Load Effective Address
- 7. `jmp` - Jump
- 8. `cmp` - Compare
- 9. `push` - Push
- 10. `pop` - Pop
- 11. `xor` - Exclusive OR
- 12. `xchg` - Exchange
- 13. jz (Jump if Zero)
- 14. call
一、 汇编代码复习
EAX,EBX,ECX,EDX,ESI和EDI使用详解。
二、 基础寄存器
在计算机体系结构中,特别是在x86架构中,通用寄存器(General Purpose Registers, GPRs)是CPU中用于存储临时数据或地址的关键组成部分。这些寄存器对于执行各种算术、逻辑和数据转移操作至关重要。在x86架构中,EAX、EBX、ECX、EDX、ESI和EDI是32位模式下的主要通用寄存器,它们在64位模式下(如x86-64)通常扩展为RAX、RBX、RCX、RDX、RSI和RDI,但保持相似的用途和特性。这里,我们将专注于32位模式下的这些寄存器。
1. EAX (Accumulator Register)
EAX寄存器通常用作累加器,在算术和逻辑运算中扮演主要角色。它经常用于存储操作数、结果以及中间值。在函数调用中,EAX也常用于返回函数的结果。
2. EBX (Base Register)
EBX通常用作基址寄存器,特别是在数组和字符串操作中,它可以用来存储数组或字符串的基地址。此外,在调用操作系统函数时,EBX有时也用于传递参数。
3. ECX (Count Register)
ECX通常用作计数器寄存器,在循环操作中尤其重要。它存储了循环的迭代次数,很多指令(如REP前缀的字符串操作指令)都会递减ECX的值来控制循环次数。
4. EDX (Data Register)
EDX是一个数据寄存器,它在执行双操作数指令时与EAX协同工作。对于较大的数据操作(如64位除法),EDX用于存储高位结果。此外,在多字节操作中,EDX也常用于存储扩展的数据。
5. ESI (Source Index Register)
ESI作为源索引寄存器,在字符串操作中非常有用。它通常用于存储源字符串或数据数组的起始地址,在字符串指令(如MOVSB, CMPSB等)中自动递增,以便按顺序处理数据。
6. EDI (Destination Index Register)
EDI作为目的索引寄存器,与ESI相对应,在字符串操作中用于存储目标字符串或数据数组的起始地址。与ESI相似,EDI也在字符串指令中自动递增,以接收来自源地址的数据。
二、 基础指令
1. mov
- 数据传送
mov
指令用于将一个数据(可以是立即数、寄存器内容、内存地址中的数据)传送到另一个地方(寄存器或内存地址)。
语法:
mov destination, source
示例:
mov eax, 10 ; 将立即数 10 传送到寄存器 eax
mov ebx, eax ; 将寄存器 eax 的内容传送到寄存器 ebx
mov [var], eax ; 假设 var 是一个内存地址,将 eax 的内容传送到该地址
2. add
- 加法
add
指令用于将两个操作数相加,并将结果存储在第一个操作数中(即目标操作数)。
语法:
add destination, source
示例:
mov eax, 5
add eax, 3 ; eax 的值变为 8
add [var], eax ; 假设 var 是一个内存地址,将其内容与 eax 相加,结果存回 var
3. sub
- 减法
sub
指令用于从第一个操作数中减去第二个操作数,并将结果存储在第一个操作数中。
语法:
sub destination, source
示例:
mov eax, 10
sub eax, 3 ; eax 的值变为 7
sub [var], eax ; 假设 var 是一个内存地址,从其内容中减去 eax,结果存回 var
4. inc
- 自增
inc
指令用于将指定操作数的值加 1。
语法:
inc operand
示例:
mov eax, 5
inc eax ; eax 的值变为 6
inc [var] ; 假设 var 是一个内存地址,将其内容加 1
5. dec
- 自减
dec
指令用于将指定操作数的值减 1。
语法:
dec operand
示例:
mov eax, 5
dec eax ; eax 的值变为 4
dec [var] ; 假设 var 是一个内存地址,将其内容减 1
当然可以,下面我将详细解释汇编命令 lea
、jmp
、cmp
、push
、pop
的使用方法,并给出对应的使用示例。请注意,这些示例是基于常见的x86汇编语言格式,但不同的汇编器(如NASM、MASM等)可能在语法细节上有所不同。
6. lea
- Load Effective Address
lea
指令用于加载有效地址(即将地址计算的结果存储到目标寄存器中,而不是执行内存访问)。这通常用于复杂的地址计算,但不希望实际从内存中加载数据。
语法:
lea destination, source
示例:
; 假设有一个数组 array,其基地址为 array_base
; 我们想要计算 array[index] 的地址
mov index, 4 ; 索引为 4
lea eax, [array_base + index * 4] ; 假设每个元素是 4 字节的,计算 array[4] 的地址并存储到 eax
; 此时 eax 中存储的是 array[4] 的地址
注意:在上面的示例中,[array_base + index * 4]
实际上是一个地址计算表达式,而 lea
指令将其结果(即地址)存储到 eax
中,而不实际访问该地址处的内存。
7. jmp
- Jump
jmp
指令用于无条件
跳转到程序中的另一个位置执行。
语法:
- 直接跳转:
jmp label
- 间接跳转:
jmp register
(寄存器中存储的是跳转目标的地址)
示例:
; 直接跳转
label1:
; 一些代码
jmp label2
label2:
; 跳转到这里执行
; 间接跳转
mov eax, offset label3
jmp eax
label3:
; 跳转到这里执行(通过寄存器跳转)
8. cmp
- Compare
cmp
指令用于比较两个操作数,并根据比较结果设置条件码(如零标志ZF、符号标志SF、溢出标志OF等)。它不存储比较结果,但随后的条件跳转指令(如 je
、jne
等)会根据这些条件码来决定是否跳转。
语法:
cmp operand1, operand2
示例:
mov eax, 5
mov ebx, 10
cmp eax, ebx
; 此时 ZF=0(不相等),SF、OF 等可能根据具体值设置
; 接下来可以用 je、jne 等条件跳转指令
9. push
- Push
push
指令用于将一个操作数(通常是寄存器或立即数)压入堆栈。堆栈是一种后进先出(LIFO)的数据结构,用于临时存储数据。
语法:
push operand
示例:
push eax ; 将 eax 的值压入堆栈
push 10 ; 将立即数 10 压入堆栈
10. pop
- Pop
pop
指令用于从堆栈中弹出一个值,并将其存储到指定的寄存器或内存中。
语法:
pop destination
示例:
; 假设之前已经用 push 指令压入了数据
pop eax ; 将堆栈顶部的值弹出并存储到 eax
pop [var] ; 假设 var 是一个内存地址,将堆栈顶部的值弹出并存储到该地址
在x86汇编语言中,xor
、lodsd
(或更常见的lodsd
的变体,如lodsd
本身通常用于字符串操作,但可能指的是movsd
或其他与加载双字相关的指令,因为lodsd
不是标准x86指令集中的直接指令)和xchg
是几种有用的指令,用于不同的目的。不过,为了回答,我将解释xor
和xchg
,并假设你想要了解的是与lodsd
相似的双字加载操作(比如movsd
),因为lodsd
不是标准指令。
11. xor
- Exclusive OR
xor
指令执行两个操作数的按位异或(XOR)操作,并将结果存储在第一个操作数中。异或操作的特点是,当两个比较的位不相同时,结果为1;相同时,结果为0。此外,xor
指令经常用于清零寄存器,因为任何数与自身异或的结果都是0。
语法:
xor destination, source
示例:
; 使用 xor 清零寄存器
xor eax, eax ; eax 的值变为 0
; 使用 xor 翻转寄存器的位
mov eax, 0xFF ; eax = 1111 1111b
xor eax, 0x55 ; eax = 0101 0101b (因为 1 ^ 1 = 0, 0 ^ 0 = 0, 1 ^ 0 = 1)
12. xchg
- Exchange
xchg
指令交换两个操作数的值。这两个操作数可以是寄存器或内存地址。
语法:
xchg operand1, operand2
示例:
; 交换两个寄存器的值
mov eax, 10
mov ebx, 20
xchg eax, ebx ; 现在 eax = 20, ebx = 10
; 交换寄存器与内存中的值
mov [var], 30
xchg eax, [var] ; 假设 var 是一个内存地址,现在 eax = 30, [var] = 10
13. jz (Jump if Zero)
jz
指令用于条件跳转。当紧接在jz
之前的指令(通常是某种算术或逻辑操作,如add
、sub
、cmp
等)影响到的标志寄存器中的零标志(ZF)被设置为1时(即,结果为零时),jz
会导致程序控制流跳转到指定的标签(label)处继续执行。如果ZF为0,则跳过跳转,继续顺序执行下一条指令。
语法:
jz label
这里,label
是程序中定义的一个标签,用于指定跳转的目标位置。
示例:
section .text
global _start
_start:
mov eax, 1
sub eax, 1
jz zero_found ; eax现在是0,ZF被设置,跳转到zero_found
; 如果上面的jz没有执行,则会继续执行下面的指令
; 这里可以添加一些未找到零时执行的代码
; ...
zero_found:
; 如果执行到这里,说明找到了零(或者某个操作导致了ZF被设置)
; 这里可以添加找到零时执行的代码
; ... 结束程序或继续其他操作
14. call
call
指令用于函数调用。它将当前指令的地址(即,紧接在call
指令之后的那条指令的地址)压入堆栈,然后跳转到由call
指令后紧跟的操作数指定的地址执行。这个操作数可以是标签、函数名、或是一个直接地址。函数执行完毕后,通常通过ret
指令返回,ret
会从堆栈中弹出之前保存的返回地址,并跳转到那个地址继续执行。
语法:
call label/function_name/direct_address
示例:
section .text
global _start
print_hello:
; 假设这里有一段打印"Hello, World!"的代码
; 注意:实际的系统调用依赖于操作系统和具体架构
; 这里只是一个示意
ret
_start:
; 调用print_hello函数
call print_hello
; 接下来可以是程序的其他部分
; ...
; 注意:在实际的Linux程序中,你需要通过系统调用来退出程序
; 这里为了示例的简洁性省略了