Ricky Hao

Linux 下 Shellcode 编写入门

##0x00
由于某些原因,突然对shellcode有了些兴趣。从Google翻到了这篇文章Shellcoding for Linux and Windows Tutorial,给予了我很大的帮助。

在这里我就简单地介绍下简单shellcode的编写。

##0x01
第一步大概就是是用汇编写源程序。当然,使用C也可以,但是会更复杂些。
这里就大致演示下Hello World的shellcode的编写。
x86环境下。

global _start

_start:
    jmp MESSAGE

    CALLBACK:
    xor eax, eax
    xor ebx, ebx
    xor ecx, ecx
    xor edx, edx
    ;使用xor来将寄存器置零,防止出现00字节

    mov al, 4
    ;通过访问eax的al位来防止赋值的时候出现多余的00字节
    ;这里的4代表着Linux的syscall的write
    mov bl, 1
    ;这里的1指stdout
    pop ecx
    ;从堆栈中得到MESSAGE块的地址(即字符串的地址)
    mov dl, 12
    ;12指字符串的长度
    int 0x80
    ;特权指令

    xor eax, eax
    mov al, 1
    ;1指exit syscall
    xor ebx, ebx
    int 0x80

    MESSAGE:
    call CALLBACK
    ;这里的call调用callback函数,使得MESSAGE的地址被放在堆栈顶,从而可以在接下来被ecx取得。
    db 'Hello world.'

编译运行

nasm -f elf 1.asm
ld -o 1 1.asm
./1

#0x02
当可以正常运行之后,我们使用objdump可以得到机器码。

objdump -d -M intel -S 1

从这里我们可以看到,没有一个00字节(可以防止在注入payload的时候被截断)。

Disassembly of section .text:

08048060 <_start>:
 8048060:       eb 19                   jmp    804807b <MESSAGE>

08048062 <CALLBACK>:
 8048062:       31 c0                   xor    eax,eax
 8048064:       31 db                   xor    ebx,ebx
 8048066:       31 c9                   xor    ecx,ecx
 8048068:       31 d2                   xor    edx,edx
 804806a:       b0 04                   mov    al,0x4
 804806c:       b3 01                   mov    bl,0x1
 804806e:       59                      pop    ecx
 804806f:       b2 0c                   mov    dl,0xc
 8048071:       cd 80                   int    0x80
 8048073:       31 c0                   xor    eax,eax
 8048075:       b0 01                   mov    al,0x1
 8048077:       31 db                   xor    ebx,ebx
 8048079:       cd 80                   int    0x80

0804807b <MESSAGE>:
 804807b:       e8 e2 ff ff ff          call   8048062 <CALLBACK>
 8048080:       48                      dec    eax
 8048081:       65 6c                   gs ins BYTE PTR es:[edi],dx
 8048083:       6c                      ins    BYTE PTR es:[edi],dx
 8048084:       6f                      outs   dx,DWORD PTR ds:[esi]
 8048085:       20 77 6f                and    BYTE PTR [edi+0x6f],dh
 8048088:       72 6c                   jb     80480f6 <MESSAGE+0x7b>
 804808a:       64                      fs
 804808b:       2e                      cs

我们将其中的机器码提取处理下即可。

\xeb\x19\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\x59
\xb2\x0c\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff
\xff\xff\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x2e

#0x03
我们可以通过简单地C程序来测试我们的ShellCode

char code[]="shellcode";
void main()
{
        (*(void(*)())code)();
}

将上面生成的shellcode放入code之后,编译运行就可以了。

gcc -g -z execstack -o code code.c

记得开启堆栈执行。

#0x04
这里作者提到了一种“可打印字符”形式的payload。主要因为很多安全软件都会查杀shellcode,因为大部分shellcode存在大量的不可打印字符。在这里我们使用一种技巧来将shellcode全部转化为可打印的形式。
首先,有下面几种指令可以被打印出来

sub eax, 0xHEXINRANGE
push eax
pop eax
push esp
pop esp
and eax, 0xHEXINRANGE

也许你认为光靠这几个指令是写不出shellcode的。实际上我们可以精心构造这几个指令,让其等效于我们需要的指令。
如:

and eax, 0x454e4f4a ;将eax清零
and eax, 0x3a313035


push esp
pop eax
sub eax, 0x39393333 ;在栈上创建860byte的空间
sub eax, 0x72727550
sub eax, 0x54545421

push eax        ; 保存到esp
pop esp
点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据