Search code examples
cmacosassemblyshellcode

Return from Shellcode instead of exit


I'm working on a shared mem assignment for uni and I've 'borrowed' and 'massaged' some shellcode I've seen in posts I've read here and other places. I've been able to construct an example that runs on my MacBook (Mojave) and almost does what I want.

However, since I'm not that experienced with assembly programming in an OS environment (MacOS in this case), and because I don't fully understand the assembly below, I need a little help to overcome my last issue.

In my C boilerplate wrapper, I have a loop that calls my shellcode periodically, but the loop only executes one iteration. This leads me to the conclusion that the second syscall (in the code below) is performing an exit 0, thus terminating the process.

How can I modify the assembly to return instead of exit?

Note if asked, I can post more information about the wrapper code and tools I'm using.

bits 64

Section .text
  global start

start:
  jmp       short MESSAGE     ;00000000  EB24              jmp short 0x26

GOBACK:
  mov       eax, 0x2000004    ;00000002  B804000002        mov eax,0x2000004  ; write
  mov       edi, 0x1          ;00000007  BF01000000        mov edi,0x1
  lea       rsi, [rel msg]    ;0000000C  488D3518000000    lea rsi,[rel 0x2b]
  mov       edx, 0xf          ;00000013  BA0F000000        mov edx,0xf
  syscall                     ;00000018  0F05              syscall

  mov       eax,0x2000001     ;0000001A  B801000002        mov eax,0x2000001  ;exit
  mov       edi,0x0           ;0000001F  BF00000000        mov edi,0x0
  syscall                     ;00000024  0F05              syscall


MESSAGE:
  call      GOBACK
  msg:      db      "Hello, world!", 0dh, 0ah
  .len:     equ     $ - msg

Here is my C boilerplate code, which includes the shellcode from above.

#include <string.h>
#include <sys/mman.h>
#include <unistd.h>

const char shellcode[] = "\xeb\x24\xb8\x04\x00\x00\x02\xbf\x01\x00\x00\x00\x48\x8d\x35\x18\x00\x00\x00\xba\x0f\x00\x00\x00\x0f\x05\xb8\x01\x00\x00\x02\xbf\x00\x00\x00\x00\x0f\x05\xe8\xd7\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x0d\x0a";

int main(int argc, char **argv)
{
  void *mem = mmap(0, sizeof(shellcode), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);

  memcpy(mem, shellcode, sizeof(shellcode));

  mprotect(mem, sizeof(shellcode), PROT_READ|PROT_WRITE|PROT_EXEC);

  for (int i = 0; i < 10; i++) {
    int (*func)();
    func = (int (*)())mem;
    (int)(*func)();
    sleep(5);
  }

  munmap(mem, sizeof(shellcode));

  return 0;
}

Solution

  • I have a loop that calls my shellcode

    So if it's an actual call then you are good to go and the only change you need to do is to remove the 3 last instructions (mov, mov and syscall) and replaced them with a ret since the return address will be on the stack already. But we need to additionally clean up the stack a little bit since the address of the text string is put there for the sake of syscall, we want to get rid of it before the return.

    Also since you shellcode contains an offset (the first jmp opcode with the value of 0x24) it's safer to not delete anything (as it will change the offsets) but to replace with NOP. Unless you modify the shellcode's source code and generate it every time - than it's fine to remove stuff.

    So what I would actually propose to do is to replace the bytes that constitutes for the last 3 opcodes with 0x90(NOP) and the last 2 bytes from that replace with pop rax\ret so bytes 0x58 and 0xc3. The final shellcode could look like this:

    const char shellcode[] = "\xeb\x24\xb8\x04\x00\x00\x02\xbf\x01\x00\x00\x00\x48\x8d\x35\x18\x00\x00\x00\xba\x0f\x00\x00\x00\x0f\x05\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x58\xc3\xe8\xd7\xff\xff\xff\x48\x65\x6c\x6c\x6f\x2c\x20\x77\x6f\x72\x6c\x64\x21\x0d\x0a";