1. 程式人生 > >GUN C內聯彙編

GUN C內聯彙編

#一、背景

  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.任何暫存器,記憶體或者立即數操作都是被允許的,但是,暫存器必須是通用暫存器。