網上看到一篇不錯的介紹shellcode的入門文章,我就大致翻譯一下,算是自己真正跨入二進制安全相關領域的學習吧。原文地址:http://www.primalsecurity.net/0x0-shellcoding-tutorial-introduction-to-asm/
以下爲翻譯內容:(非逐句翻譯)
彙編代碼介紹:
彙編語言是一種爲了方便與微處理器交互而設計的低級編程語言。該語言是與處理器系列相關聯的,如Intel、ARM等。在理解彙編的時候,體系結構發揮了重要的作用,因爲在32位和64位之間存在很大的不同。在這裏我們主要集中到Linux下的Intel(IA-32)。
今天我們看到的CPU寄存器爲EAX、EBX、ECX和EDX。在最初設計時,這些寄存器擁有一般的功能。但是基於我們的目的,我們可以在每個時序存儲任何我們喜歡的數據。這些寄存器的標準用法如下:
EAX |
“累加器”通常用於算術運算 |
EBX |
基址寄存器,作爲數據指針 |
ECX |
“計算器”,用於循環的索引 |
EDX |
數據寄存器,充當一個I/O指針 |
在後續文章中,我們會介紹其他一些寄存器。
操作我們的寄存器:
首先,我們會利用之前提到的寄存器創建一個基本的“hello world”的彙編語言腳本。要做到這一點,我們先創建一個名爲“helloworld.asm”的新文件(可以取任何你想取的名字),然後在文本編輯器中創建‘.text’和‘.data’兩個段,如下所示:
section .text global _start ;default entry point for linking _start: ; entry point for commands section .data
.data段我們將用於存儲字符串(這可以用於變量等),.text段將創建ELF鏈接的入口,我們的指令用於操作寄存器設置我們的系統調用(多個),以及我們的指令給內核執行我們的系統調用。
首先,我們需要使用define byte或者db把我們的字符串添加到.data段中:
msg: db “Hello World!:,0x0a ; the string, followed by a new line character
接下來,我們需要決定什麼系統調用將用於我們的彙編指令。爲了查看可用的系統調用,我們需要查看“uninstd_32.h”文件,一般存在於“/usr/include/i386-linux-gnu/asm/”或者可能在其他位置。我們可以打開這個文件查看可用的調用:
立即看到兩個我們利用的系統調用,exit函數(#define __NR_exit 1)和write函數(#define __NR_write 4)。注意着兩個系統調用號因爲我們會在後面使用到。我們可以使用“man 2”來查看關於這些系統調用的細節。(例如:man 2 write):
查看man文件,看到我們需要使用多個字段,‘int fd’(字段描述符),‘const void *buf’(緩衝區),‘size_t count’(字符串大小)。在這個例子中,我們的字段描述符指示我們將要寫入的位置(0代表標準輸入,1代表標準輸出,2代表標準錯誤)。在這裏,我們的緩衝區,就是‘Hello World!’字符串,計數器就是緩衝區的長度。總括來說,我們有幾下幾點:
- syscall:4;系統調用號代表我們的write命令
- fd:1;字段描述符指示我們的字符串將被寫到標準輸出
- *buf:msg;我們在.data段中創建的hello world字符串
- count:13;我們緩衝區的長度12加上一個換行符
現在,我們已經標識的必要的信息,我們可以開始操作寄存器了。要做到這一點,我們將使用Intel系統結構的寄存器操作的mov命令:
mov [destination],
我們將重複mov與四個字段的每一個,依次爲EAX,EBX,ECX和EDX寄存器,後面再加上”int 0x80”命令來執行系統調用。
section .text global _start ;default entry point for linking _start: ; entry point for commands ; use the write syscall to print 'Hello world!' to stdout mov eax, 4 ; move syscall 4(write) to the eax register mov ebx, 1 ; move field descriptor for stdout to ebx mov ecx, msg ; move the memory address of our string to ecx mov edx, 13 ; move the length of the string to edx int 0x80 ; execute the syscall section .data msg: db “Hello world!”, 0x0a ; the string, followed by a new line character
現在,我們已經小心的編寫了write系統調用。我們需要遵循相同的步驟,乾淨執行程序。要做到這一點,我們將使用前面提到的“exit”的系統調用.這一次,我們僅需要利用”int status“,下面的步驟用於exit系統調用後,你的代碼將和下面類似:
section .text global _start ;default entry point for linking _start: ; entry point for commands ; use the write syscall to print 'Hello world!' to stdout mov eax, 4 ; move syscall 4(write) to the eax register mov ebx, 1 ; move field descriptor for stdout to ebx mov ecx, msg ; move the memory address of our string to ecx mov edx, 13 ; move the length of the string to edx int 0x80 ; execute the syscall ; use the exit syscall to exit the program with a status code of 0 mov eax, 1 ; mov syscall 1(exit) to the eax register) mov ebx, 0 ; move status code to ebx int 0x80 ; execute the syscall section .data msg: db “Hello world!”, 0x0a ; the string, followed by a new line character
創建我們的可執行程序:
現在,我們的彙編代碼已經創建了,接下來將要把它編譯稱爲目標文件,然後使用鏈接器創建我們的ELF可執行文件,我們使用如下的NASM命令來創建我們的目標文件:
nasm -f elf32 -o <output object file> <input assembly file>
現在我們有了一個成功的目標文件,我們可以使用ld來鏈接它,然後創建最後的執行文件。我們使用如下命令:
ld -o <output file> <input object file>
假設這種情況成功了,我們應該有了一個全功能的ELF可執行程序。現在,我們可以執行我們的文件,並且保證正確執行。
附上NASM的下載地址:http://www.nasm.us/pub/nasm/releasebuilds/2.11.08/