##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