从可利用程序运行时Shellcode分段错误错误

时间:2018-01-30 20:16:41

标签: c linux assembly x86-64 shellcode

BITS 64
section     .text
global      _start

_start:
jmp short two

one:
pop     rbx
xor     al,al
xor     cx,cx
mov     al,8
mov     cx,0755
int     0x80
xor     al,al
inc     al
xor     bl,bl                               
int     0x80

two:
call one
db  'H'`

这是我的汇编代码。 然后我用了两个命令。 " nasm -f elf64 newdir.s -o newdir.o"并且" ld newdir.o -o newdir"。我运行./newdir并且工作正常,但是当我提取操作代码并尝试使用以下c程序测试此shellcode时。它没有工作(没有分段错误)。我使用cmd编译 gcc newdir -z execstack

#include <stdio.h>
char sh[]="\xeb\x16\x5b\x30\xc0\x66\x31\xc9\xb0\x08\x66\xb9\xf3\x02\xcd\x80\x30\xc0\xfe\xc0\x30\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x48";
void main(int argc, char **argv)
{
    int (*func)();
    func = (int (*)()) sh;
    (int)(*func)();
}

objdump -d newdir

newdir:     file format elf64-x86-64


Disassembly of section .text:

0000000000400080 <_start>:
  400080:   eb 16                   jmp    400098 <two>

0000000000400082 <one>:
  400082:   5b                      pop    %rbx
  400083:   30 c0                   xor    %al,%al
  400085:   66 31 c9                xor    %cx,%cx
  400088:   b0 08                   mov    $0x8,%al
  40008a:   66 b9 f3 02             mov    $0x2f3,%cx
  40008e:   cd 80                   int    $0x80
  400090:   30 c0                   xor    %al,%al
  400092:   fe c0                   inc    %al
  400094:   30 db                   xor    %bl,%bl
  400096:   cd 80                   int    $0x80

0000000000400098 <two>:
  400098:   e8 e5 ff ff ff          callq  400082 <one>
  40009d:   48                      rex.W

当我跑./a.out时,我得到像照片中的东西。我附上照片是因为我无法解释发生了什么。image

P.S-我的问题得到了解决。但我想知道出了什么问题。所以我使用了调试器,结果如下 `

(gdb) list
1   char shellcode[] = "\xeb\x16\x5b\x30\xc0\x66\x31\xc9\xb0\x08\x66\xb9\xf3\x02\xcd\x80\x30\xc0\xfe\xc0\x30\xdb\xcd\x80\xe8\xe5\xff\xff\xff\x48";
2   int main (int argc, char **argv)
3   {
4           int (*ret)();              
5           ret = (int(*)())shellcode; 
6                                      
7           (int)(*ret)();   
8           }           (gdb) disassemble main
Dump of assembler code for function main:
   0x00000000000005fa <+0>: push   %rbp
   0x00000000000005fb <+1>: mov    %rsp,%rbp
   0x00000000000005fe <+4>: sub    $0x20,%rsp
   0x0000000000000602 <+8>: mov    %edi,-0x14(%rbp)
   0x0000000000000605 <+11>:    mov    %rsi,-0x20(%rbp)
   0x0000000000000609 <+15>:    lea    0x200a20(%rip),%rax        # 0x201030 <shellcode>
   0x0000000000000610 <+22>:    mov    %rax,-0x8(%rbp)
   0x0000000000000614 <+26>:    mov    -0x8(%rbp),%rdx
   0x0000000000000618 <+30>:    mov    $0x0,%eax
   0x000000000000061d <+35>:    callq  *%rdx
   0x000000000000061f <+37>:    mov    $0x0,%eax
   0x0000000000000624 <+42>:    leaveq 
   0x0000000000000625 <+43>:    retq   
End of assembler dump.
(gdb) b 7
Breakpoint 1 at 0x614: file test.c, line 7.
(gdb) run
Starting program: /root/Desktop/Progs/shell/a.out 

Breakpoint 1, main (argc=1, argv=0x7fffffffe2b8) at test.c:7
7           (int)(*ret)();   
(gdb) info registers rip
rip            0x555555554614   0x555555554614 <main+26>
(gdb) x/5i $rip
=> 0x555555554614 <main+26>:    mov    -0x8(%rbp),%rdx
   0x555555554618 <main+30>:    mov    $0x0,%eax
   0x55555555461d <main+35>:    callq  *%rdx
   0x55555555461f <main+37>:    mov    $0x0,%eax
   0x555555554624 <main+42>:    leaveq 
(gdb) s
(Control got stuck here, so i pressed ctrl+c)
^C
 Program received signal SIGINT, Interrupt.
 0x0000555555755048 in shellcode ()
(gdb) x/5i 0x0000555555755048 
=> 0x555555755048 <shellcode+24>:   callq  0x555555755032 <shellcode+2>
   0x55555575504d <shellcode+29>:   rex.W add %al,(%rax)
   0x555555755050:  add    %al,(%rax)
   0x555555755052:  add    %al,(%rax)
   0x555555755054:  add    %al,(%rax)

这是调试信息。我无法找到控件出错的地方。如果需要更多信息,请询问。

2 个答案:

答案 0 :(得分:2)

下面是一个使用x86-64的工作示例;可以进一步优化尺寸。为了执行shellcode,最后的0x00 null是可以的。

组装&amp;链接:

$ nasm -felf64 -g -F dwarf pushpam_001.s -o pushpam_001.o && ld pushpam_001.o -o pushpam_001

代码:

BITS 64
section     .text
global      _start

_start:
jmp short two

one:
        pop   rdi               ; pathname

        xor rax, rax
        add al, 85              ; creat syscall 64-bit Linux

        xor rsi, rsi
        add si, 0755            ; mode - octal
        syscall

        xor rax, rax
        add ax, 60
        xor rdi, rdi
        syscall

two:
call one
db  'H',0

objdump:

pushpam_001:     file format elf64-x86-64
0000000000400080 <_start>:
  400080:       eb 1c                   jmp    40009e <two>
0000000000400082 <one>:
  400082:       5f                      pop    rdi
  400083:       48 31 c0                xor    rax,rax
  400086:       04 55                   add    al,0x55
  400088:       48 31 f6                xor    rsi,rsi
  40008b:       66 81 c6 f3 02          add    si,0x2f3
  400090:       0f 05                   syscall
  400092:       48 31 c0                xor    rax,rax
  400095:       66 83 c0 3c             add    ax,0x3c
  400099:       48 31 ff                xor    rdi,rdi
  40009c:       0f 05                   syscall
000000000040009e <two>:
  40009e:       e8 df ff ff ff 48 00               

             .....H.

编码提取:还有很多其他方法可以做到这一点。

$ for i in `objdump  -d pushpam_001  | grep "^ " | cut -f2`; do echo -n '\x'$i; done; echo
\xeb\x1c\x5f\x48\x31\xc0\x04\x55\x48\x31\xf6\x66\x81\xc6\xf3\x02\x0f\x05\x48\x31\xc0\x66\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdf\xff\xff\xff\x48\x00\x.....H.

C shellcode.c - partial

...
unsigned char code[] = \
"\xeb\x1c\x5f\x48\x31\xc0\x04\x55\x48\x31\xf6\x66\x81\xc6\xf3\x02\x0f\x05\x48\x31\xc0\x66\x83\xc0\x3c\x48\x31\xff\x0f\x05\xe8\xdf\xff\xff\xff\x48\x00";  
...

最后:

./shellcode 

--wxrw---t 1 david david     0 Jan 31 12:25 H   

答案 1 :(得分:2)

如果int 0x80 in 64-bit code是唯一的问题,使用gcc -fno-pie -no-pie构建C测试会有效,因为char sh[]将位于虚拟地址空间的低32位,因此系统调用截断指向32位的指针仍然有效。

strace下运行您的程序,以查看它实际进行的系统调用。 (除了strace在64位代码中错误地解码int 0x80系统调用,解码就好像你使用了64位syscall ABI。调用号和arg寄存器是不同的。)但至少你可以看到系统调用返回值(对于带有截断的64位指针的32位-EFAULT,它将是creat。)

您也可以gdb单步执行并检查系统调用返回值。有strace解码系统调用输入是非常好的,所以我建议移植你的代码使用64位ABI,然后它就可以了。

此外,它实际上能够利用64位进程,其中缓冲区溢出位于低32位以外的地址的内存中。 (例如像堆栈一样)。所以,是的,您应该停止使用int 0x80或坚持使用32位代码。

您还需要在代码运行之前将寄存器归零,就像它们在流程启动时一样,但在从其他任何地方调用时都不会。

xor al,al之前的

mov al,8完全没有意义,因为xor-zeroing al不会清除高位字节。写32位寄存器会清除高32位,但不会写入8位或16位寄存器。如果确实如此,那么在使用也是只写的mov之前,你不需要xor-zeroing。

如果要在机器代码中设置RAX = 8且没有任何零字节,则可以

  • push 8 / pop rax(3个字节)
  • xor eax,eax / mov al,8(4个字节)
  • 或者给出一个归零的rcx寄存器,lea eax, [rcx+8](3个字节)

将CX设置为0755并非如此简单,因为常量不适合imm8。您的16位mov是一个不错的选择(如果您先将rcx归零,则可能会这样做。

xor  ecx,ecx
lea  eax, [rcx+8]   ; SYS_creat = 8 from unistd_32.h
mov  cx, 0755       ; mode
int  0x80           ; invoke 32-bit ABI

xor  ebx,ebx
lea  eax, [rbx+1]   ; SYS_exit = 1
int  0x80