ARM的GCC下内联汇编的基本使用手册

简要

突然不知道怎么的想踩这个坑。然后rootkit隐藏文件就咕咕咕了。不过还好,这个坑踩完了,接下来就能滚回去填坑了。

汇编不难,蛋疼的是各种稀奇古怪又十分蛋疼的调用约定和很麻烦的调试

x64下,基本寄存器和win32没啥区别,可以更具我之前的一篇汇编基础文章来看。

文章地址:汇编学习笔记

arm下是强制使用fastcall,也就是说前四个参数是强制使用寄存器传参的,所以在调用之前一定要记得push。

同时AT&T的语法是从左到右,而intel的语法是从又到左。

同时,GCC内联汇编的语法和win也不怎么一样,调用怎么说呢,说简单也不简单,说麻烦也不麻烦

详情可以参考基本文档:GCC内联汇编

以及几种常用的寻址方式

(1) 直接寻址

movl ADDRESS, %eax

ADDRESS其实就相当于"地址或偏移"里的地址,反正就是一个数字。

(2)寄存器寻址

其实上面的例子也包括了寄存器寻址,顾名思义%eax就是寄存器寻址,代表对这个寄存器本身的写入或读出。

(3)立即寻址

movl $2, %ebx

我一直觉得立即寻址算不算寻址,反正它的意思就是把2这个数字写入%eax寄存器,$2就是立即寻址,其实就是立即数。

(4)间接寻址

movl (%eax), %ebx

(%eax)就是间接寻址了,意思就是访问eax寄存器里的数值所代表的地址。相当于通用公式里的%基址或偏移量寄存器。

(5)索引寻址(变址寻址)

movl 0xFFFF0000(,%eax,4), %ebx

0xFFFF0000(,%eax,4)就是索引寻址,意思是从0xFFFF0000地址开始,加上%eax * 4作为索引的最终地址。请自行匹配上面的通用公式。

(6)基址寻址

movl 4(%eax), %ebx

4(%eax)就是基址寻址,意思是以eax寄存器里的数值作为基址,加上4得到最终地址。也可以匹配到上面的通用公式,而且这个是很常用的寻址方式

接下来就是代码实现

基本代码实现

    #include<stdio.h>
    
    void pt(int number){
        printf("echo:%d\n",number);
    }
    //简单的数值相加
    void easyAdd(void){
        int foo = 10, bar = 15;
        __asm__ __volatile__ ( "addl %%ecx, %%eax;"
                                : "=%eax"(foo)
                                : "a"(foo), "c"(bar)
                                );
        printf("foo+bar=%d\n", foo);
    }
    // 简单的乘法
    int easyRide(int i,int ride){
        __asm__ __volatile__(
            "mov %0,%%eax;"
            "mov %1,%%ebx;"// mul source ,把source的值和eax的值相乘,自带指令的参数都是用寄存器而不是压栈
            "mul %%ebx;"//计算结果粗存在rax中
            "pop %%rax;"
            "pop %%rbp;"
            "ret;"
            :
            :"r"(i),"r"(ride)
            :
        );
        return 0;
    }
    // 简单的调用函数
    void easyCall(int n){
        __asm__ __volatile__(
            "movl %0,%%edi;"
            "call pt;"
            :
            :"r"(n)
            :
            );
    }
    // 修改返回值
    void changeReturn(void){
        //所有的返回值其实就是给eax复制
        //然后把栈内的rbp返回成原函数的栈
        __asm__ __volatile__("movl $4,%%eax;"
            "mov %%rbp, %%rsp;"
            "pop %%rbp;"
            "ret;"
            ::
            );
        
    }
    /*
    获取参数
    不好用,虽说参数永远在栈顶,但是你只在程序内用内联汇编完全无法知道栈顶位置
    int getParm(int par){
        //
        int a =par;
        int b=par;
        int c = par;
        int d=par;
        int f = par;
        __asm__ __volatile__(
            
            "mov %%rbp, %%rsp;"
    
            "mov -20(%%rbp) ,%%rax;"
            "pop %%rbp;"
            "ret;"
            :
            :
            :);
    }
    */
    
    
    //变量循环
    int arrayLoop(void){
        int a;
        int b[5]={1,2,3,4,5};
        a = sizeof(b)/sizeof(int);
        //计算数组长度
        __asm__ __volatile__(
            // "mov %0,%%rbx;"
            "mov $0 ,%%rdx;"//初始化
            "LP2:"
                "push %%rcx;"
                "push %%rdx;"//寄存器不够了,听47姐姐的,压栈!
                "mov $4,%%rax;"
                "mul %%rdx;"
                "mov %%rax,%%rdx;"
                //执行i * 4生成偏移量,为下一步偏移做准备
                //因为int是4字节所以×4,其他类型自行sizeof查看
                "lea (%%rbx),%%rax;"
                "add %%rax,%%rdx;"
                //把数组变量地址+偏移地址得到数据
                //储存寄存器的数值,然后调用printf函数
                "mov (%%rdx),%%rdi;"
                "call pt;"
                "pop %%rdx;"
                "pop %%rcx;"
                "inc %%rdx;"
                //还原寄存器的值
            "loop LP2;"
            :
            :"b"(b),"c"(a)
            :
            );
        return 1;
    }
    // 简单的循环
    int easyLoop(void){
        __asm__ __volatile__(
            "mov $4,%%rcx;"
            "LP:"
                "push %%rcx;"
                // 因为是x64强制使用寄存器传参
                // 所以得先push rcx来保存rcx的值
                // 否则就会在调用函数的时候被修改导致loop失败
                "mov %%ecx,%%edi;"
                // "push $4;"
                "call pt;"
                "pop %%rcx;"
                //回复rcx的值
            "loop LP;"
            
            :::);
        return 0;
    }
Tags: 汇编, arm, linux

添加新评论 »