带你玩转汇编

发布时间:2024年01月13日

一.寄存器分类

在这里插入图片描述

控制寄存器控制运行模式

二.通用寄存器

1、理论知识

在这里插入图片描述

rax          64位  8B   只有64位CPU才有
   eax       32位  4B   32位,64位CPU都有
      ax     16位  2B   16位,32位,64位CPU都有
        al   8位  1B   8位,16位,32位,64位CPU都有
        ah   8位  1B   8位,16位,32位,64位CPU都有

注意:只有rax,rbx,rcx,rdx有ah/bh/ch/dh高八位寄存器

在这里插入图片描述

特殊用途:

1.rax:用于函数返回值的接收
2.rcx:循环次数,this指针(C++)
3.rsi:source 源数据
4.rdi:destination 目的数据
5.rbp,rsp:栈寄存器
6.esp:栈顶
7.ebp:栈底

2.小例子

使用汇编写出以下代码:
int a=10;
a +=1;

1.用多个寄存器实现

xor eax,eax
mov eax,0x0A
add eax,1
mov [ebp-8],eax

2.只用一个寄存器实现
xor eax,eax
xor ebx,ebx
mov eax,0x0A
mov ebx,0x01
add eax,ebx
mov [ebp-8],eax

3.跳转指令

跳转指令:

短跳转:call,jmp,jcc
长跳转:中断,跨段

三.执行流基本结构

函数调用前后栈是不变的

int main()
{
00831750  push        ebp  
00831751  mov         ebp,esp  
00831753  sub         esp,0CCh  
------------------------------------------开辟栈栈帧

00831759  push        ebx  
0083175A  push        esi  
0083175B  push        edi  
-----------------------------------------保存现场

0083175C  lea         edi,[ebp-0Ch]  
0083175F  mov         ecx,3  
00831764  mov         eax,0CCCCCCCCh  
00831769  rep stos    dword ptr es:[edi]  
0083176B  mov         ecx,offset _FEA7E5C2_main@c (083C008h)  
00831770  call        @__CheckForDebuggerJustMyCode@4 (0831307h)  
-----------------------------------------初始化开辟的栈空间

	int a = 0;
00831775  mov         dword ptr [a],0  
	return 0;
0083177C  xor         eax,eax  

}
0083177E  pop         edi  
0083177F  pop         esi  
00831780  pop         ebx 
-----------------------------------------恢复现场 

00831781  add         esp,0CCh  
00831787  cmp         ebp,esp  
00831789  call        __RTC_CheckEsp (0831230h)  


0083178E  mov         esp,ebp  
00831790  pop         ebp  
-----------------------------------------恢复栈帧

00831791  ret     ;和call对应,拿到返回地址
-----------------------------------------返回

esp:栈顶
ebp:栈底

mov esp,ebp
pop ebp
leave可以代替这两条,命令(复杂指令集)

四.栈指令

运行模式:
实模式       16      2B
保护模式     32      4B
长模式       64      8B

1.push:把数据压入栈,栈后移(减小)

栈顶指针先移动,然后赋值
 sub esp,4
 mov dword ptr[esp],10

2.pop:取出数据,栈前移(增大)

先取值,栈顶指针在移动
mov eax,[esp]
add esp,4

3.jmp:无条件跳转

jmp  地KERNEL_ARRD             段内跳  短跳
jmp  CODE_SELECTOR:KERNEL_ARRD 跨段跳  长跳

4.call

1.会把call指令的下一条指令地址压入栈
2.跳转到函数里面

5.ret

 1.取到栈顶的地址,且栈前移(增大)
 2.回到地址的位置

五.小案例

给出一下C语言,转化成汇编语言

void text() {
	int a = 9;
	if (10 == a)
	{
		a = 100;
	}
	else
	{
		a = 1000;
	}

	
}


int main(int argc,char* argv[])
{
	text();
	return 0;
}

汇编:

global text



text:
	push ebp     ;保存main的栈底
	mov  ebp,esp ;将main栈顶赋值给text栈底
	
	mov [esp-8],0x0A
	mov eax,[esp-8]
	cmp eax,0X0A
	jne .s
	mov [esp-8],OX64
	jmp .r

.s
	mov [esp-8],0X3E8
.r      
	mov eax,[esp-8] ;保存返回值到eax寄存器中
	leave           ;mov esp,ebp;popebp
	ret             ;返回
	

main:
	call text     ;将下一条指令的地址压入栈中,跳转到text函数
	ret

六.函数调用约定

堆栈平衡(平栈):

(1)调用者平栈:外平栈      add esp,4
(2)被调用者平栈:内平栈    ret 8

调用约定:

研究调用约定主要研究,传参方式(栈传参,寄存器,栈+寄存器)和传参顺序,平栈方式

32Bit:
1.__cdecl:(X86)默认约定

text(0x11,0x22,0x33);
002D1DE1  push        33h  
002D1DE3  push        22h  
002D1DE5  push        11h  
002D1DE7  call        _text (02D13BBh)  
002D1DEC  add         esp,0Ch    ;一个参数4字节一共12字节

栈传参,从右到左,外平栈

2.__stdcall:windows动态链接库

00BF1DE1  push        33h  
00BF1DE3  push        22h  
00BF1DE5  push        11h  
00BF1DE7  call        _text@12 (0BF13C0h)

栈传参,从右到左,内平栈

3.__fastcall

00AC3E71  push        33h  
00AC3E73  mov         edx,22h  
00AC3E78  mov         ecx,11h  
00AC3E7D  call        @text@12 (0AC13C5h) 

前两个寄存器,其余的栈传参,从右到左,内平栈

64Bit:
Windows:__fastcall

text(0x11,0x22,0x33,0x44,0x55,0x66,0x77);
00007FF7142E1B83  mov         dword ptr [rsp+30h],77h  
00007FF7142E1B8B  mov         dword ptr [rsp+28h],66h  
00007FF7142E1B93  mov         dword ptr [rsp+20h],55h  
00007FF7142E1B9B  mov         r9d,44h  
00007FF7142E1BA1  mov         r8d,33h  
00007FF7142E1BA7  mov         edx,22h  
00007FF7142E1BAC  mov         ecx,11h  
00007FF7142E1BB1  call        text (07FF7142E13A2h)  

前六个寄存器,其余的栈传参,从左到右,内平栈
Linux: AMD64 ABI

七.内联汇编

使用场景:

1.直接访问底层硬件
2.使用特定的处理器指令
3.性能优化
4.实现特殊的程序逻辑

Linux内联语法:

在 Linux 中,内联汇编语法通常使用 asm 关键字来嵌入汇编代码到 C/C++ 程序中。内联汇编语法的基本格式如下:

asm("assembly code" : output : input : clobbered);

其中,assembly code 是要嵌入的汇编代码,output 是输出操作数,input 是输入操作数,clobbered 是会被修改的寄存器。

  • assembly code:这里放置你的汇编代码。
  • output:声明输出变量,可以是寄存器或内存地址。
  • input:声明输入变量,可以是寄存器或内存地址。
  • clobbered:声明可能被修改的寄存器。

示例1:

int myFunction(int arg1, int arg2);

int main() {
    int arg1 = 1;
    int arg2 = 4;
    int add = 0;
    asm volatile (
        "push $100\n"         // 将 arg2 压入栈
        "push $200\n"         // 将 arg1 压入栈
        "call myFunction\n"   // 调用 myFunction函数
        "add $8, %%esp\n"     // 平栈
        :"=eax"(add)          //接收返回值
        : 
        : 
    );
    printf("%d\n",add);
    return 0;
}

int myFunction(int arg1, int arg2) {
    return arg1-arg2;
}

示例2


int myFunction(int arg1, int arg2);

int main() {
    int arg1 = 1;
    int arg2 = 4;
    int add = 0;
    asm volatile (
        "push %[arg2]\n"                     // 将 arg2 压入栈
        "push %[arg1]\n"                     // 将 arg1 压入栈
        "call myFunction\n"                  // 调用 myFunction
        "add $8, %%esp\n"                    // 平栈
        :"=eax"(add)                         //接受返回值
        : [arg2] "r" (arg2),[arg1] "r" (arg1)    //传入值重名[],随机寄存器
        : 
    );
    printf("%d\n",add);
    return 0;
}

int myFunction(int arg1, int arg2) {
    return arg1-arg2;
}


文章来源:https://blog.csdn.net/m0_73731708/article/details/135569379
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。