; Program for playing on PC-Speaker.
; For GNU/Linux 64 bit version. Root privileges needed.

format ELF64 dynamic 3 at 0

sys_write       equ 1       ; the linux WRITE syscall
sys_lseek       equ 8
SEEK_END        equ 2
sys_exit        equ 60      ; the linux EXIT syscall
sys_stdout      equ 1       ; the file descriptor for standard output (to print/write to)
sys_getuid      equ 102     ; syscall for getuid
sys_geteuid     equ 107     ; syscall for geteuid
sys_setuid      equ 105     ; syscall for setuid
sys_execve      equ 59      ; syscall for execve (to execute killall)

entry start

; NX bit:
segment gnustack

segment readable executable writeable

start:
    ; Get and set UID/EUID
    call get_and_set_uid

    ; Check for --stop argument
    pop rcx                ; Get argc (number of arguments)
    pop rsi                ; Skip argv[0] (program name)
    dec rcx                ; Decrement argc
    jz no_stop             ; If no arguments, skip stop check

    ; Compare first argument with "--stop"
    pop rsi                ; Get argv[1]
    lea rdi, [stop_arg]    ; Load address of "--stop" string
    call strcmp            ; Compare strings
    test rax, rax          ; Check if strings are equal
    jz stop_program        ; If equal, jump to stop_program

no_stop:
    ; Continue with normal program flow
    call writehelp
    call ioperm
    call startsignal

    pop r8              ; pop the number of arguments from the stack
    cmp r8, 1
    jne arg
    call spkon
    mov bx, 1000
    call spk
    mov ax, 1000
    call sle
    call spkoff
    jmp exit

arg:
    pop rsi             ; discard the program name, since we only want the commandline arguments
    dec r8

loop1:
    cmp r8, 0           ; check if we have to print more arguments
    jz exit             ; if not, jump to the 'end' label

    mov rax, sys_write  ; set the rax register to the syscall number we want to execute (WRITE)
    mov rdi, sys_stdout ; specify the file we want to write to (standard output in this case)
    pop rsi             ; pop a pointer to the string we want to print from the stack
    call getlength
    syscall             ; execute the system call

    push rax
    push rdi
    push r8
    push rsi
    push rdx
    call playfile
    call spkoff
    mov ax, 500
    call sle
    pop rdx
    pop rsi
    pop r8
    pop rdi
    pop rax

    mov rax, sys_write  ; print a newline
    mov rdi, sys_stdout
    lea rsi, [linebreak]
    mov rdx, 1
    syscall

    dec r8              ; count down every time we print an argument until there are none left
    jmp loop1           ; jump back to the top of the loop

exit:
    mov rax, sys_exit   ; load the EXIT syscall number into rax
    xor rdi, rdi        ; the program return code
    syscall             ; execute the system call

stop_program:
    ; Execute killall gorg64_spkplay
    mov rax, sys_execve
    lea rdi, [killall_cmd]
    lea rsi, [killall_args]
    lea rdx, [envp]
    syscall

    ; Exit the program
    mov rax, sys_exit
    xor rdi, rdi
    syscall

get_and_set_uid:
    ; Get UID
    mov rax, sys_getuid
    syscall
    mov [uid], rax

    ; Get EUID
    mov rax, sys_geteuid
    syscall
    mov [euid], rax

    ; Print UID and EUID
    mov rax, sys_write
    mov rdi, sys_stdout
    lea rsi, [uid_msg]
    mov rdx, uid_msg_len
    syscall

    ; Check if EUID is root (0)
    cmp qword [euid], 0
    jne .done

    ; Set UID to 0 (root)
    mov rax, sys_setuid
    xor rdi, rdi
    syscall

    ; Check if setuid was successful
    cmp rax, 0
    jne .setuid_error

    ; Print new UID and EUID
    mov rax, sys_write
    mov rdi, sys_stdout
    lea rsi, [uid_set_msg]
    mov rdx, uid_set_msg_len
    syscall

.done:
    ret

.setuid_error:
    ; Print error message
    mov rax, sys_write
    mov rdi, sys_stdout
    lea rsi, [setuid_error_msg]
    mov rdx, setuid_error_msg_len
    syscall
    jmp exit

; String comparison function
strcmp:
    mov al, [rsi]
    cmp al, [rdi]
    jne .not_equal
    test al, al
    jz .equal
    inc rsi
    inc rdi
    jmp strcmp
.equal:
    xor rax, rax
    ret
.not_equal:
    mov rax, 1
    ret

; Rest of the code remains unchanged...

segment readable writeable

linebreak       db 0x0A    ; ASCII character 10, a line break

uid_msg         db 'UID=', 0
uid             dq 0
euid            dq 0
uid_msg_len     equ $ - uid_msg

uid_set_msg     db 'Now UID=', 0
uid_set_msg_len equ $ - uid_set_msg

setuid_error_msg db 'Error set UID', 0
setuid_error_msg_len equ $ - setuid_error_msg

stop_arg        db '--stop', 0
killall_cmd     db '/usr/bin/killall', 0
killall_args    dq killall_cmd, gorg64_spkplay, 0
gorg64_spkplay  db 'gorg64_spkplay', 0
envp            dq 0

; Rest of the data section remains unchanged...