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