Shellcoding教程:介绍ASM

      网上看到一篇不错的介绍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/

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。