Why won't this stack string print in x64 NASM on macOS? - string

I've been able to successfully print a string using the sys_write to stdout on macOS. However, I cannot get this stack string to print using execve syscall with echo:
global _main
default rel
section .text
_main:
mov rbp, rsp
sub rsp, 32
mov rax, 'this a t'
mov [rbp-16], rax
mov rax, 'est'
mov [rbp-8], rax
mov rax, '/bin/ech'
mov [rbp-32], rax
xor rax, rax
mov al, 'o'
mov [rbp-24], rax
push 0
mov rax, 0
mov [rbp], rax
exit_program:
;rdi filename
;rsi argv
;rdx envp
lea rdi, [rbp-32]
lea rsi, [rbp-32]
mov rdx, 0
mov rax, 0x200003b
syscall
Currently, my return is EFAULT status code from execve.
The memory layout as shown in the screenshot is the string "This is a test" followed by null bytes for termination.
UPDATE: Trace output: execve("/bin/echo", [0x6863652f6e69622f, 0x6f, 0x7420612073696874, 0x747365], NULL) = -1 EFAULT (Bad address)

execve takes 3 args: a char* and two char *[] arrays, each terminated by a NULL pointer.
Your first arg is fine. It points to a zero-terminated array of ASCII characters which are a valid path.
Your argv is a char[], not char *[], because you passed the same value as your first arg! So when the system call interprets the data as an array of pointers to copy into the new process's arg array, it finds an invalid pointer 0x6863652f6e69622f as the first one. (The bytes of that pointer are ASCII codes.)
The trace output makes that pretty clear.
Your 3rd is NULL, not a pointer to NULL. Linux supports this, treating a NULL as an empty array. I don't know if MacOS does or not; if you still get EFAULT after passing a valid argv[] set RDX to a pointer to a qword 0 somewhere on the stack.
Keeping your existing setup code, you could change the last part to
lea rdi, [rbp-32] ; pointer to "/bin/echo"
push 0 ; NULL terminator
mov rdx, rsp ; envp = empty array
push some_reg ; holding a pointer to "this is a test"
push rdi ; pointer to "/bin/echo" = argv[0]
mov rsi, rsp ; argv
syscall
Note that envp[] and argv[] are terminated by the same NULL pointer. If you wanted a non-empty envp you couldn't do that.
If this is supposed to be shellcode, you're going to need to replace the push 0 with pushing an xor-zeroed register, and it looks like you could simplify some of the other stuff. But get it working first.

Related

how to properly display a value from the stack using a system call [duplicate]

I am trying to use the write syscall in order to reproduce the putchar function behavior which prints a single character. My code is as follows,
asm_putchar:
push rbp
mov rbp, rsp
mov r8, rdi
call:
mov rax, 1
mov rdi, 1
mov rsi, r8
mov rdx, 1
syscall
return:
mov rsp, rbp
pop rbp
ret
From man 2 write, you can see the signature of write is,
ssize_t write(int fd, const void *buf, size_t count);
It takes a pointer (const void *buf) to a buffer in memory. You can't pass it a char by value, so you have to store it to memory and pass a pointer.
(Don't print one char at a time unless you only have one to print, that's really inefficient. Construct a buffer in memory and print that. e.g. this x86-64 Linux NASM function: How do I print an integer in Assembly Level Programming without printf from the c library?)
A NASM version of GCC: putchar(char) in inline assembly:
; x86-64 System V calling convention: input = byte in DIL
; clobbers: RDI, RSI, RDX, RCX, R11 (last 2 by syscall itself)
; returns: RAX = write return value: 1 for success, -1..-4095 for error
writechar:
mov byte [rsp-4], dil ; store the char from RDI
mov edi, 1 ; EDI = fd=1 = stdout
lea rsi, [rsp-4] ; RSI = buf
mov edx, edi ; RDX = len = 1
syscall ; rax = write(1, buf, 1)
ret
If you do pass an invalid pointer in RSI, such as '2' (integer 50), the system call will return -EFAULT (-14) in RAX. (The kernel returns error codes on bad pointers to system calls, instead of delivering a SIGSEGV like it would if you deref in user-space).
See also What are the return values of system calls in Assembly?
Instead of writing code to check return values, in toy programs / experiments you should just run them under strace ./a.out, especially if you're writing your own _start without libc there won't be any other system calls during startup that you don't make yourself, so it's very easy to read the output. How should strace be used?

Passing an array (argv) to a syscall in assembly x64

I am learning to create shellcode and having a great time. I mostly understand what to do. I can create asm code that will actually generate the shell. However, I was going to verify my ability by trying another syscall, namely cat .
I am using the method of building the stack from the registers. However, I am running into an issue where I need to pass an array to the 'argv' parameter. This is simple enough when doing a shell, I can just pass the address of the address of the /bin/sh string on the stack. But with cat I need to pass both the name of the function /bin/cat and the argument for cat ie /etc/issue.
I know that the layout for a syscall is:
rax : syscall ID
rdi : arg0
rsi : arg1
rdx : arg2
r10 : arg3
r8 : arg4
r9 : arg5
What I can't decipher is how to pass {"cat","/etc/issue"} into a single register, namely rsi.
My assembly:
global _start
section .text
_start:
;third argument
xor rdx,rdx
;second array member
xor rbx,rbx
push rbx ;null terminator for upcoming string
;push string in 2 parts
mov rbx,6374652f ;python '/etc/issue'[::-1].encode().hex()
push rbx
xor rbx,rbx
mov rbx, 0x65757373692f
push rbx
;first array member
xor rcx,rcx ;null terminator for upcoming string
add rcx,0x746163 ;python 'cat'[::-1].encode().hex()
push rcx
;first argument
xor rdi,rdi
push rdi ;null terminator for upcoming string
add rdi,7461632f6e69622f ;python '/bin/cat'[::-1].encode().hex()
push rdi
mov rdi,rsp
;execve syscall
xor rax,rax
add rax,59
;exit call
xor rdi,rdi
xor rax,rax
add rax,60
It runs but (as expected) aborts when a NULL is passed as argv.
I even tried just writing a C app that creates an array and quits and debugged that but I still didn't really understand what it was doing to create the array.
You're making this way more complicated than you need to. Here's all you need to do:
jmp .afterdata
.pathname:
db '/bin/' ; note lack of null terminator
.argv0:
db 'cat'
.endargv0:
db 1 ; we'll have to change the last byte to a null manually
.argv1:
db '/etc/issue'
.endargv1:
db 1 ; we'll have to change the last byte to a null manually
.afterdata:
xor eax, eax ; the null terminator for argv and envp
push rax
mov rdx, rsp ; rdx = envp
dec byte [rel .endargv1] ; change our 1 byte to a null byte
lea rax, [rel .argv1]
push rax
dec byte [rel .endargv0] ; change our 1 byte to a null byte
lea rax, [rel .argv0]
push rax
mov rsi, rsp ; rsi = argv
lea rdi, [rel .pathname]
xor eax, eax
mov al, 59 ; SYS_execve
syscall
; if you wanted to do an exit in case the execve fails, you could here, but for shellcode I don't see the point
You don't need to do any hex-encoding or reversing of strings by hand. You can just stick the strings you need right at the end of your shellcode, and push their addresses onto the stack with rip-relative addressing. The only hoops we jump through are making sure the data is before the instructions that use it, so there's no null bytes there, and having to add in the null terminators on the string at runtime.
Also, you generally want shellcode to be short. Notice how I point into the cat that's part of /bin/cat instead of having it an extra time, and reuse the null at the end of argv for envp.
By the way, if you want to try this as a standalone program, you'll need to pass -Wl,-N and -static to GCC, since the bytes it's modifying will be in the .text section (which is normally read-only). This won't be a problem when you're actually using it as shellcode, since it'll still be writable by whatever means you got it into memory in the first place.

Writing a putchar in Assembly for x86_64 with 64 bit Linux?

I am trying to use the write syscall in order to reproduce the putchar function behavior which prints a single character. My code is as follows,
asm_putchar:
push rbp
mov rbp, rsp
mov r8, rdi
call:
mov rax, 1
mov rdi, 1
mov rsi, r8
mov rdx, 1
syscall
return:
mov rsp, rbp
pop rbp
ret
From man 2 write, you can see the signature of write is,
ssize_t write(int fd, const void *buf, size_t count);
It takes a pointer (const void *buf) to a buffer in memory. You can't pass it a char by value, so you have to store it to memory and pass a pointer.
(Don't print one char at a time unless you only have one to print, that's really inefficient. Construct a buffer in memory and print that. e.g. this x86-64 Linux NASM function: How do I print an integer in Assembly Level Programming without printf from the c library?)
A NASM version of GCC: putchar(char) in inline assembly:
; x86-64 System V calling convention: input = byte in DIL
; clobbers: RDI, RSI, RDX, RCX, R11 (last 2 by syscall itself)
; returns: RAX = write return value: 1 for success, -1..-4095 for error
writechar:
mov byte [rsp-4], dil ; store the char from RDI
mov edi, 1 ; EDI = fd=1 = stdout
lea rsi, [rsp-4] ; RSI = buf
mov edx, edi ; RDX = len = 1
syscall ; rax = write(1, buf, 1)
ret
If you do pass an invalid pointer in RSI, such as '2' (integer 50), the system call will return -EFAULT (-14) in RAX. (The kernel returns error codes on bad pointers to system calls, instead of delivering a SIGSEGV like it would if you deref in user-space).
See also What are the return values of system calls in Assembly?
Instead of writing code to check return values, in toy programs / experiments you should just run them under strace ./a.out, especially if you're writing your own _start without libc there won't be any other system calls during startup that you don't make yourself, so it's very easy to read the output. How should strace be used?

nasm segmentation fault while using arg

extern puts
global main
section .text
main:
mov rax, rdi
label:
test rax, rax
je exit
push rsi
mov rdi, [rsi]
call puts
pop rsi
dec rax
add rsi, 8
jmp label
exit:
pop rsi
ret
I wrote nasm code like that. However segmentation fault occur in last. I can't understand why segmentation fault is occur.
rax is not guaranteed to be preserved across function calls, as it is used to return integer results from functions (in the case of puts "a nonnegative number on success, or EOF on error") You need to save the value of rax before calling puts, like you're doing with rsi, and restore it afterwards.
Obviously you want to get the command line parameters in a GCC environment on a 64-bit Linux, where they are passed according to the GCC calling convention which follows the Linux calling convention "System V AMD64 ABI".
Let's translate the program logic to C:
#include <stdio.h>
int main ( int argc, char** argv )
{
if (argc != 0)
{
do
{
puts (*argv);
argc--;
argv++;
} while (argc);
}
return;
}
The asm program doesn't return an exit code. That exit code should be in RAX when the function returns. BTW: argc is always >0 since the first string of argv holds the program name.
The main function is both "caller" (calls puts) and "callee" (returns to the GCC environment). As caller it has to preserve RAX and RSI before the call to puts and restore them when it needs them. A callee-saved register is not used. Don't forget to align the stack by 16.
This works:
extern puts
global main
section .text
main: ; RDI: argc, RSI: argv, stack is unaligned by 8
mov rax, rdi
label:
test rax, rax
je exit
push rbx ; Push 8 bytes to align the stack before the call
push rax ; Save it (caller-saved)
push rsi ; Save it (caller-saved)
mov rdi, [rsi] ; Argument for puts
call puts
pop rsi ; Restore it
pop rax ; Restore it
pop rbx ; "Unalign" the stack
dec rax
add rsi, 8
jmp label
exit:
; pop rsi ; Once too much
xor eax, eax ; RAX = 0 (return 0)
ret ; RAX: return value

How to read and write a byte from and to stdin/stdout using Syscall in x86 with NASM assembler?

I am trying to read a string from stdin and print it back out using x86, NASM, and Syscalls. Reading in a byte will be a function, and writing out a byte will be a function. I am reading the string from stdin and putting each char into an array. Here is my initial idea:
;read_writer.asm
section .data
arr times 100 db 0 ; array of 100 elements initialzed to 0
ptr dd 0
section .text
global _start
_start:
push ebp ; setup stack
mov ebp, esp ; setup stack
push, 0x10 ; allocate space for potential local variables
call readin: ;call read in func
push eax ;return char from readin will be in eax, push it for writeout
call writeout:
leave
mov eax, 1 ;exit
mov ebx, 0
int 0x80
readin:
push ebp
mov ebp, esp ; setup stack
mov eax, 3 ;read
mov ebx, 1 ;stdin
; i feel like i am missing code here bit not sure what
leave ;reset stack
ret ;return eax
writeout:
push ebp
mov ebp, esp ; setup stack
push eax ;push eax since it was pushed earlier
mov eax, 4 ;write
mov ebx, 1 ;stdout
; i feel like i am missing code here bit not sure what
leave ;reset stack
ret ;return eax
Sample input:
Hello World
Sample output:
Hello World
The functions should be used with cdecl, which I dont think I am doing correctly. I also realised I am not putting the chars into arr.
For starters, you're missing the int 0x80 in both readin and writeout.
And, as you can see here, both sys_read and sys_write expect a (const) char* in ecx. That address should point to the buffer where the bytes to write are stored / the read bytes should be stored.The value of edx should be set to the number of bytes to read / write.
So in the readin example you want something like:
mov eax, 3 ;read
mov ebx, 0 ;stdin. NOTE: stdin is 0, not 1
sub esp,4 ; Allocate some space on the stack
mov ecx,esp ; Read characters to the stack
mov edx,1
int 0x80
movzx eax,byte [esp] ; Place the character in eax, which is used for function return values
add esp,4
And similarly for writeout.

Resources