分享

GUN C内联汇编

 立志德美 2019-04-10

#一、背景

  1. 在Linux内核的代码中,大部分以C内联汇编编写。

  2. 在编写病毒时,也会常常用到,比如,要编写一个不依赖libc的注入代码时,需要调用mmap进行内存申请时,就要使用到syscall进行系统调用。这时就需要使用到C语言的内联汇编。

static inline volatile int evil_open(const char *path, unsigned long flags)
{
    long ret;
    __asm__ volatile(
        "mov %0, %%rdi\n"
        "mov %1, %%rsi\n"
        "mov $2, %%rax\n"
        "syscall" : : "g"(path), "g"(flags));
    asm ("mov %%rax, %0" : "=r"(ret));
    return ret;
}

#二、内联汇编基本格式

asm [ volatile ] (  
    assembler template 				     汇编代码。通常分行编写
    [ : output operands ]                /* optional */ 输出操作数
    [ : input operands  ]                /* optional */ 输入操作数
    [ : list of clobbered registers ]    /* optional */ 指定不允许gcc编译器使用的寄存器列表
    );
  1. 由于避免命名冲突,asm与__asm__等价,volatile 与 __volatile__等价

  2. volatile 用于指示,当添加此关键字时,不允许gcc编译器对assmbly code进行代码优化。

  3. 以上中括号,代表可选参数。也就是可以完全只包含汇编代码,而不包含,输入操作数和输出操作数以及不允许gcc编译器使用的寄存器列表。

#三、实例
下面的代码就是使用C语言的内联汇编,syscall进行系统调用。在屏幕上输出“I am HotIce0”。并且调用exit(0)退出程序,这是一段可以编译为位置独立的注入代码。

long _write(long fd, char *buf, unsigned long len)
{
    long ret;
    asm volatile (
        "mov %0, %%rdi\n"
        "mov %1, %%rsi\n"
        "mov %2, %%rdx\n"
        "mov $1, %%rax\n"
        "syscall"
        :
        :"g"(fd), "g"(buf), "g"(len)
    );
    asm("mov %%rax, %0":"=r"(ret));
    return ret;
}
void _exit(long status)
{
    asm(
        "mov $60, %%rax\n"
        "syscall"
        :
        :"r"(status)
    );
}

_start()
{
    _write(1, "I am HotIce0\n", sizeof("I am HotIce0\n"));
    _exit(0);
}
  1. 其中用到的%0 , %1这样的数字,是用来引用,输入参数和输出参数。
    编号的方式是,%0是第一个输出参数,%n是最后一个输入参数。

  2. 其中的$60,是立即数,也就是60。十六进制需要使用0x10这样的写法。

  3. 能操作的寄存器包括以下寄存器。

寄存器操作数约束:r (register operand constraint):gcc可以将变量保存在任何可用的GPR中g
GPR寄存器表 (通用目标寄存器)
±–±-------------------+
| r | Register(s) |
±–±-------------------+
| a | %eax, %ax, %al |
| b | %ebx, %bx, %bl |
| c | %ecx, %cx, %cl |
| d | %edx, %dx, %dl |
| S | %esi, %si |
| D | %edi, %di |

  1. 其中的"g"(fd)这种叫约束。
    这样的约束有很多种。比如以下常用的约束。

  • = 代表输出变量用作输出,原来的值会被新值替换。

  • + 代表即可用作输入,也可用作输出。

  • “m” : A memory operand is allowed, with any kind of address that the machine supports in general.允许内存操作通过使用任何机器支持的地址类型。

  • “o” : A memory operand is allowed, but only if the address is offsettable. ie, adding a small offset to the address gives a valid address.允许使用内存操作数,但前提是该地址是可偏移的。 即,向地址添加一个小偏移量会给出一个有效的地址。

  • “V” : A memory operand that is not offsettable. In other words, anything that would fit the m’ constraint but not theo’constraint.不可偏移的内存操作,就是,任何的操作都适合用m修饰,但是不一定适用于o约束。

  • “i” : An immediate integer operand (one with constant value) is allowed. This includes symbolic constants whose values will be known only at assembly time.立即数操作

  • “n” : An immediate integer operand with a known numeric value is allowed. Many systems cannot support assembly-time constants for operands less than a word wide. Constraints for these operands should use ’n’ rather than ’i’.一个立即数操作使用一个已知的数值是被允许的,很多系统不支持为操作少于一个字大小的对象汇编时构建。这个时候,应该使用n而不是i。

  • “g” : Any register, memory or immediate integer operand is allowed, except for registers that are not general registers.任何寄存器,内存或者立即数操作都是被允许的,但是,寄存器必须是通用寄存器。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多