summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--makefile9
-rwxr-xr-xshbin0 -> 1664 bytes
-rw-r--r--sh.asm465
-rw-r--r--sh.s388
4 files changed, 473 insertions, 389 deletions
diff --git a/makefile b/makefile
index 04a37e8..dece0b1 100644
--- a/makefile
+++ b/makefile
@@ -1,5 +1,12 @@
all:
- nasm sh.s -f elf64 -o sh.o
+ nasm sh.asm -f elf64 -o sh.o
ld sh.o -o sh
+ strip sh
+ rm sh.o
+dbg:
+ nasm sh.asm -f elf64 -o sh.o
+ ld sh.o -o sh
+ rm sh.o
+ gdb -q --eval-command="layout asm" -tui sh
clean:
rm -f sh.o sh
diff --git a/sh b/sh
new file mode 100755
index 0000000..eda9e13
--- /dev/null
+++ b/sh
Binary files differ
diff --git a/sh.asm b/sh.asm
new file mode 100644
index 0000000..369157f
--- /dev/null
+++ b/sh.asm
@@ -0,0 +1,465 @@
+section .data
+ sigs dd 0x2
+ cpid dd 0x0
+ eol db `\n`
+ eol_mask db `\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`,`\n`
+ env_str db "/etc/environment", 0x0
+
+ prompt_str db "[0xdeadbeef] "
+ prompt_str_len equ $-prompt_str
+
+ not_func_str db "Error: program not found.", `\n`
+ not_func_str_len equ $-not_func_str
+
+ no_dir_str db "cd: Unknown directory.",`\n`
+ no_dir_str_len equ $-no_dir_str
+
+ invalid_int_str db "exit: Invalid integer.",`\n`
+ invalid_int_str_len equ $-invalid_int_str
+ ;Flag indicating ability to execute
+ X_OK equ 0x1
+
+ ;Indicates that a file can be accessed in the way specified
+ F_OK equ 0x0
+
+ ;option for waitid
+ P_PGID equ 2
+
+ ;Special file descriptors
+ fd_stdin equ 0x00
+ fd_stdout equ 0x01
+ fd_stderr equ 0x02
+
+ ;Syscall constants
+ sys_read equ 0x00
+ sys_write equ 0x01
+ sys_open equ 0x02
+ sys_close equ 0x03
+ sys_access equ 0x15
+ stub_fork equ 0x39
+ stub_execve equ 0x3b
+ sys_exit equ 0x3c
+ sys_wait4 equ 0x3d
+ sys_kill equ 0x3e
+ sys_chdir equ 0x50
+ sys_waitid equ 0xf7
+
+section .bss
+ ;These must all be divisible by 16!
+ input_buffer_size equ 4096
+ input_buffer resb input_buffer_size
+
+ path_buffer_size equ 4096
+ path_buffer resb path_buffer_size
+
+ concat_buffer_size equ 8192
+ concat_buffer resb concat_buffer_size ;Double so the path and input can never overflow concat_buffer when combined
+
+section .text
+ global _start
+
+_start:
+ ; TODO: handle interrupt signal
+
+ mov r15, input_buffer ;stdin input
+ mov r9, path_buffer ;PATH variable reader
+ mov rbx, concat_buffer ;Concatenated directory and command name
+
+ ; input: none
+ ; output: r9 (path elements), r10 (length)
+ ; clobbered: r8, r12
+ ; other: r15 (buffer)
+ _parse_path:
+ mov rax, sys_open
+ mov rdi, env_str
+ xor rsi, rsi
+ syscall
+ test rax, rax ;check if rax is 0
+ jl _quit
+
+ call _bzero
+
+ _parse_path2:
+ mov rdi, rax
+ mov rax, sys_read
+ mov rsi, r9
+ mov rdx, path_buffer_size
+ syscall
+ test rax, rax
+ jl _quit
+ mov r8, r9
+ dec r8
+
+ ; Reads the PATH variable
+ mov r10, `\0\0\0PATH=`
+ _pathfinder:
+ inc r8
+
+ ;Check that we've found the PATH variable
+ mov rax, [r8]
+ shl rax, 24
+ cmp rax, r10
+ jne _pathfinder ;if we haven't, let's move right one and see if it's there
+ cmp byte [r8], `"`
+ jne _pathfinder_continue
+ inc r8
+ cmp byte [r8], 0
+ jz _quit
+ _pathfinder_continue:
+ add r8, 0x5
+ mov r10, 0x1
+ push 0x0
+ push r8
+ dec r8
+
+ _pathender:
+ inc r8
+ mov al, [r8]
+ cmp al, `:`
+ je _pathender1
+ cmp al, `"`
+ je _pathender_exit
+ cmp al, `\n`
+ je _pathender_exit
+ cmp al, 0
+ jz _pathender_exit
+ jmp _pathender
+ _pathender_exit:
+ mov byte [r8], 0x0
+ mov r9, rsp
+ jmp _read_loop
+
+ _pathender1:
+ inc r10
+ mov byte [r8], 0x0
+ lea rcx, [r8+0x1]
+ push rcx
+ jmp _pathender
+
+_read_loop:
+ mov rax, sys_write
+ mov rdi, fd_stdout
+ mov rsi, prompt_str
+ mov rdx, prompt_str_len
+ syscall
+
+ call _bzero
+
+_read_loopr:
+ mov rax, sys_read
+ xor rdi, rdi ;stdin
+ mov rsi, r15
+ mov rdx, input_buffer_size
+ syscall
+
+ ; check for _quit
+ mov r12, _quit
+ cmp byte [r15], 0x0
+ je _weol
+ ;Start using r12 to hold the number of args
+ xor r12,r12
+
+ ; call _parse
+ jmp _parse
+
+_exec:
+ ; fork so we don't hurt ourselves
+ mov rax, stub_fork
+ syscall
+ mov dword [cpid], eax
+ test eax, eax ;checks if cpid is 0
+ jne _wait_for_proc
+
+ ; now that we know what to execute, do so
+ mov rax, stub_execve
+ mov rdi, r13 ;Filename
+ mov rsi, r14 ;argv
+ xor rdx, rdx ;envp
+ syscall
+
+ ; and now kill the child process
+ jmp _quit
+
+;waits for the process to close
+_wait_for_proc:
+ mov r12, r10 ;backup
+
+ mov rsi, rax
+ mov rax, sys_waitid
+ xor rdi, rdi
+ xor rdx, rdx
+ mov r10, P_PGID
+ xor r8, r8
+ syscall
+
+ mov r10, r12 ;restore
+
+ mov dword [cpid], -0x1
+ jmp _read_loop
+
+;Writes 0s in buffer r15 until r8 reaches input_buffer_size
+;input: r15 (buffer)
+;output: none
+_bzero:
+ xor r8, r8
+ _bzero_main:
+ movups [r15+r8], xmm0
+ add r8, 16
+ cmp r8, input_buffer_size
+ jl _bzero_main
+ ret
+
+;input: r15 (buffer)
+;output: r13 (path), r14 (arguments)
+;clobbered: r8, rax, rbx, rcx
+_parse:
+ ;syscall 80 is chdir, so do that first, it's easiest
+ ;It takes a path string.
+ ;also needs pipes (|, >, <)
+ xor r13, r13 ;for _strlen
+ push r13 ;push 0
+ call _strlen
+
+;r8 holds strlen so we start at the end and parse backwards
+; go through and sub/push
+_parse1:
+ cmp byte [r15+r8], ` `
+ je _subzp
+ cmp byte [r15+r8], `\n`
+ je _subz
+ cmp byte [r15+r8], `/`
+ je _sabsf
+ cmp byte [r15+r8], `.`
+ je _sabsf
+
+;Controls the parsing of the directory, keeps parsing each bit of the dir until
+;the end of the string or directory name
+_parse1r:
+ dec r8
+ jl _parse2
+ jmp _parse1
+
+_sabsf:
+ cmp r8, 1
+ jg _parse1r
+ mov r13, 1
+ jmp _parse1r
+
+_subz:
+ mov byte [r8+r15], 0x0
+ jmp _parse1
+
+;Splits at the current position and puts the address of the argument on the stack
+_subzp:
+ mov byte [r8+r15], 0x0
+ lea rax, [r8+r15+1]
+ push rax
+ inc r12
+ jmp _parse1
+
+_parse2:
+ push rbx
+ mov r14, rsp
+ test r13, r13
+ jg _parse7
+ mov r8, -0x1
+
+_parse3:
+ inc r8
+ cmp r8, r10
+ je _parse4
+ ; Builtins to add: export, eval
+ ;r15 holds the name that the user entered, so we'll use that for shell builtins
+ mov rcx,`\0\0\0exit\0`
+ mov r13, [r15]
+ shl r13, 24 ;Remove the last character in r13, because it's an arg not the name of it
+ cmp r13, rcx
+ je _builtin_exit
+ mov rcx,`\0\0\0\0\0cd\0`
+ shl r13, 16 ;Remove the extra characters for cd, note how we're doing the
+ ;longest function names first then the shorter later
+ cmp r13, rcx
+ je _builtin_cd
+ ;END OF BUILTINS
+ ;mov r13, r15
+ mov rdi, [r9+r8*8]
+ push r8
+ ; input: rdi (a), r15 (b)
+ ; output: rbx (string)
+ ; clobbered: r8, r11
+ _concat_dir:
+ xor r8,r8 ;new string's length (and for _dirty_strcpy_1, also the
+ ;initial string length)
+ ;Copies the contents of rdi to rbx in blocks of
+ ;16, doesn't bother to clean up stuff past the null
+ ;so moves that too, but it doesn't matter, just make
+ ;sure the buffer is big enough
+ _dirty_strcpy_1:
+ movDqU xmm2, [rdi+r8]
+ movDqU [rbx+r8],xmm2
+
+ pcmpistri xmm1, xmm2, 122
+ jz _dirty_strcpy_1_exit
+
+ PcmpIstrI xmm0, xmm2, 122 ;if there was a null in the
+ ;copied string exit loop
+ jz _dirty_strcpy_1_exit
+ add r8,15
+ jmp _dirty_strcpy_1
+ _dirty_strcpy_1_exit:
+ add r8,rcx
+ inc r8
+ mov byte [rbx+r8], '/'
+ inc r8
+ xor r11, r11
+ _dirty_strcpy_2:
+ movdqu xmm2, [r15+r11]
+ movdqu [rbx+r8], xmm2
+
+ pcmpistri xmm1, xmm2, 122
+ jz _parse5
+
+ pcmpistri xmm0, xmm2, 122
+ jz _parse5
+
+ add r8, 15
+ add r11, 15
+ jmp _dirty_strcpy_2
+
+_parse4: ; Function not found
+ mov rax, sys_write
+ mov rdi, fd_stdout
+ mov rsi, not_func_str
+ mov rdx, not_func_str_len
+ syscall
+ jmp _read_loop
+
+; Checks that the program rbx can be executed
+_parse5:
+ mov rax, sys_access
+ mov rdi, rbx
+ mov rsi, X_OK
+ syscall
+
+ pop r8
+ ;Check if it can be executed
+ cmp rax, F_OK
+ jl _parse3
+
+; We'll fall through to here if the file is accessible, therefore we should execute it
+_parse6:
+ mov r13, rbx
+ jmp _exec
+
+_parse7: ; absolute path
+ mov rax, sys_access
+ mov rdi, r15
+ mov rsi, X_OK
+ syscall
+
+ cmp rax, F_OK
+ jl _parse4
+
+ mov r13, r15
+ jmp _exec
+
+
+;input: r15 (buffer)
+;Clobbered: rsi
+;output: r8 (length)
+_strlen:
+ xor r8, r8
+ xchg rsi, rcx ;bacup rcx, let rsi get clobbered
+ MovDqU xmm1, [eol_mask]
+ _strlen1:
+ PcmpIstrI xmm1, [r15+r8], 122
+ jz _strlen_end
+ PcmpIstrI xmm0, [r15+r8], 122
+ jz _strlen_end
+ add r8, 15
+ jmp _strlen1
+ _strlen_end:
+ inc rcx
+ add r8, rcx
+ xchg rsi,rcx
+ ret
+
+; interrupt signal handler
+_sigint:
+ cmp dword [cpid], 0x0
+ jl _sigint_nc
+
+__sigint_c: ; kill the child
+ mov rax, sys_kill
+ mov rdi, [cpid]
+ mov rsi, 0x2
+ syscall
+
+_sigint_nc:
+ mov r12, _read_loop
+ jmp _weol
+
+; input: r12 (return address)
+; output: none
+; Writes end of line to the terminal
+_weol:
+ mov rax, sys_write
+ mov rdi, fd_stdout
+ mov rsi, eol
+ mov rdx, 0x1
+ syscall
+ jmp r12
+
+_quit:
+ mov rax, sys_exit
+ xor rdi, rdi
+ syscall
+
+;cd [dir (rax)]
+;changes to the given directory
+_builtin_cd:
+ mov rdi,rax
+ mov rax, sys_chdir
+ syscall
+ test rax, rax
+ jz _read_loop
+ mov rax, sys_write
+ mov rdi, fd_stdout
+ mov rsi, no_dir_str
+ mov rdx, no_dir_str_len
+ syscall
+ jmp _read_loop
+;usage: exit [status]
+;exits with the given status or 0 if one hasn't been provided
+_builtin_exit:
+ ;convert first arg to an int if present
+ ;exit with that code
+ _string_to_int:
+ xor rdi, rdi
+ ;accumulate in rdi since it's also the register used to provide the status
+ ;to sys_exit
+ xor rdx, rdx
+ test r12, r12
+ jz _string_to_int_end
+ _string_to_int_loop:
+ mov dl, [rax] ; Convert this to a number
+ test dl, dl ;Checks for NULL
+ jz _string_to_int_end
+ sub dl, `0` ;sub because we'll need to do this anyway
+ jl _invalid_int
+ cmp dl, 9 ;since we've already subtracted we can just compase with 9
+ jg _invalid_int
+ imul rdi, 10
+ add rdi, rdx
+ inc rax
+ jmp _string_to_int_loop
+ _string_to_int_end:
+ mov rax, sys_exit
+ syscall
+_invalid_int:
+ mov rax,sys_write
+ mov rdi, fd_stdout
+ mov rsi, invalid_int_str
+ mov rdx, invalid_int_str_len
+ syscall
+ jmp _read_loop
diff --git a/sh.s b/sh.s
deleted file mode 100644
index edc6710..0000000
--- a/sh.s
+++ /dev/null
@@ -1,388 +0,0 @@
-section .text
- global _start
-
-_start:
- ; TODO: handle interrupt signal
-
- push rbp
- mov rbp, rsp
- sub rsp, 0x110
- mov r15, rsp
- sub rsp, 0x110
- mov r9, rsp
- sub rsp, 0x110
- mov rbx, rsp
-
- mov rax, 0x1
- mov rdi, 0x1
- mov rsi, msg
- mov rdx, 0x1b
- syscall
-
- jmp parse_path
-
-rloop:
- mov rax, 0x1
- mov rdi, 0x1
- mov rsi, prompt
- mov rdx, 0xd
- syscall
-
- mov r8, -0x1
- mov r12, rloopr
- jmp bzero
-
-rloopr:
- mov rax, 0x0
- mov rdi, 0x0
- mov rsi, r15
- mov rdx, 0xff
- syscall
-
- ; check for quit
- cmp byte [r15], 0x0
- mov r12, quit
- je weol
-
- ; call parse
- jmp parse
-
-exec:
- ; fork so we don't hurt ourselves
- mov rax, 0x39
- syscall
- mov dword [cpid], eax
- cmp dword [cpid], 0x0
- jne wait4_it
-
- ; now that we know what to execute, do so
- mov rax, 0x3b
- mov rdi, r13
- mov rsi, r14
- mov rdx, 0x0
- syscall
-
- ; and now kill the child process
- jmp quit
-
-wait4_it:
- mov rdi, rax
- mov rax, 0x3d
- xor rsi, rsi ; null status pointer
- xor rdx, rdx
- xor rcx, rcx ; null rusage pointer
- syscall
-
- mov dword [cpid], -0x1
- jmp rloop
-
-; input: none
-; output: r9 (path elements), r10 (length)
-; clobbered: r8, r12
-; other: r15 (buffer)
-parse_path:
- mov rax, 0x2
- mov rdi, env
- mov rsi, 0x0
- syscall
- cmp rax, 0x0
- jl quit
-
- mov r8, -0x1
- mov r12, parse_path2
- jmp bzero
-
-parse_path2:
- mov rdi, rax
- mov rax, 0x0
- mov rsi, r9
- mov rdx, 0xff
- syscall
- cmp rax, 0x0
- jl quit
- mov r8, r9
- dec r8
-
-pathfinder:
- inc r8
- xor rax, rax
- mov byte al, [r8]
- cmp al, 0x50
- jne pathfinder
- mov byte al, [r8+1]
- cmp al, 0x41
- jne pathfinder
- mov byte al, [r8+2]
- cmp al, 0x54
- jne pathfinder
- mov byte al, [r8+3]
- cmp al, 0x48
- jne pathfinder
- mov byte al, [r8+4]
- cmp al, 0x3d
- jne pathfinder
- mov byte al, [r8+5]
- cmp al, 0x22
- jne pathfinder
- cmp al, 0x0
- je quit
- add r8, 0x6
- mov r10, 0x1
- push 0x0
- push r8
- dec r8
-
-pathender:
- inc r8
- mov al, [r8]
- cmp al, 0x3a
- je pathender1
- cmp al, 0x22
- jne pathender
- mov byte [r8], 0x0
- mov r9, rsp
- jmp rloop
-
-pathender1:
- inc r10
- mov byte [r8], 0x0
- lea rcx, [r8+0x1]
- push rcx
- jmp pathender
-
-;input: r8 (iterator), r15 (buffer), r12 (return address)
-;output: none
-bzero:
- inc r8
- mov byte [r8+r15], 0x0
- cmp r8, 0xff
- jle bzero
- jmp r12
-
-;input: r15 (buffer)
-;output: r13 (path), r14 (arguments)
-;clobbered: r8, rax, rbx, rcx
-parse: ; needs special commands: cd exit export echo eval pwd
- ;also needs pipes (|, >, <)
- push 0x0
- xor r13, r13
- jmp strlen
-
-; go through and sub/push
-parse1:
- cmp byte [r8+r15], 0x20
- je subzp
- cmp byte [r8+r15], 0x0a
- je subz
- cmp byte [r8+r15], 0x2f
- je sabsf
- cmp byte [r8+r15], 0x2e
- je sabsf
-
-parse1r:
- dec r8
- cmp r8, 0x0
- jl parse2
- jmp parse1
-
-sabsf:
- cmp r8, 0x1
- jg parse1r
- mov r13, 0x1
- jmp parse1r
-
-subz:
- mov byte [r8+r15], 0x0
- jmp parse1
-
-subzp:
- mov byte [r8+r15], 0x0
- lea rax, [r8+r15+0x1]
- push rax
- jmp parse1
-
-parse2:
- push rbx
- mov r14, rsp
- cmp r13, 0x0
- jg parse7
- mov r8, -0x1
-
-parse3:
- inc r8
- cmp r8, r10
- je parse4
- mov r13, r15
- mov rcx, [r9+r8*8]
- push r8
- jmp concat
-
-parse4: ; not found
- mov rax, 0x1
- mov rdi, 0x1
- mov rsi, nfe
- mov rdx, 0x1a
- syscall
- jmp rloop
-
-parse5: ; test rbx
- pop r8
-
- mov rax, 0x2
- mov rdi, rbx
- xor rsi, rsi
- xor rdx, rdx
- syscall
-
- cmp rax, 0x0
- jl parse3
-
- mov rdi, rax
- mov rax, 0x3
- syscall
-
-parse6: ; found it
- mov r13, rbx
- jmp exec
-
-parse7: ; absolute path
- mov rax, 0x2
- mov rdi, r15
- xor rsi, rsi
- xor rdx, rdx
- syscall
-
- cmp rax, 0x0
- jl parse4
-
- mov rdi, rax
- mov rax, 0x3
- syscall
-
- mov r13, r15
- jmp exec
-
-
-;input: r12 (return address), r15 (buffer)
-;output: r8 (length)
-strlen:
- xor r8, r8
-
-strlen1:
- cmp byte [r8+r15], 0x0
- je parse1
- cmp byte [r8+r15], 0xa
- je parse1
- inc r8
- jmp strlen1
-
-; input: rcx (a), r13 (b)
-; output: rbx (string)
-; clobbered: r8, r11
-concat:
- mov r8, -0x1
- jmp jlen1
-
-jlen1:
- inc r8
- cmp byte [r8+rcx], 0x0
- je jlen02
- cmp byte [r8+rcx], 0xa
- je jlen02
- jmp jlen1
-
-jlen02:
- dec r8
- mov r11, -0x1
-
-jlen2:
- inc r8
- inc r11
- cmp byte [r11+r13], 0x0
- je treg
- cmp byte [r11+r13], 0xa
- je treg
- jmp jlen2
-
-treg:
- cmp r8, 0xff
- jg boe
- xchg r15, rbx
- mov r8, -0x1
- mov r12, rsinc
- jmp bzero
-
-rsinc:
- xchg r15, rbx
- mov r8, -0x1
- mov r11, -0x1
-
-join1:
- inc r8
- cmp byte [r8+rcx], 0x0
- je join02
- cmp byte [r8+rcx], 0xa
- je join02
- mov al, [r8+rcx]
- mov [r8+rbx], al
- jmp join1
-
-join02:
- mov byte [r8+rbx], 0x2f
-join2:
- inc r8
- inc r11
- cmp byte [r11+r13], 0x0
- je parse5
- cmp byte [r11+r13], 0xa
- je parse5
- mov al, [r11+r13]
- mov [r8+rbx], al
- jmp join2
-
-boe:
- mov rax, 0x1
- mov rdi, 0x1
- mov rsi, boem
- mov rdx, 0x1f
- jmp quit
-
-; interrupt signal handler
-sigint:
- cmp dword [cpid], 0x0
- jl sigint_nc
-
-sigint_c: ; kill the child
- mov rax, 0x3e
- mov rdi, [cpid]
- mov rsi, 0x2
- syscall
-
-sigint_nc:
- mov r12, rloop
- jmp weol
-
-; input: r12 (return address)
-; output: none
-weol:
- mov rax, 0x1
- mov rdi, 0x1
- mov rsi, eol
- mov rdx, 0x1
- syscall
- jmp r12
-
-quit:
- mov rax, 0x3c
- mov rdi, 0x0
- syscall
-
-section .data
- sigs: dd 0x2
- cpid: dd 0x0
- eol: db 0xa
- msg: db "Welcome to deadbeef shell", 0x21, 0x0a
- prompt: db "[0xdeadbeef]", 0x20
- nfe: db "Error: program not found", 0x2e, 0x0a
- env: db "/etc/environment", 0x0
- boem: db "Error: input overflows buffer", 0x2e, 0x0a