Créer un serveur de fichiers HTTP en Assembleur x64 sur OpenIndiana
Simple, efficace, sans indexation possible.
Par contre, pas d'accents, ni d'espaces, ni de caractères spéciaux dans les noms des fichiers (seul le "." est autorisé pour les extensions de fichiers).
On place des fichiers dans /opt/WEB/DOWNLOADS et en tapant le nom du fichier à la fin de l'URL, le fichier se télécharge.
Il est aisé de cacher les fichiers en ne plaçant pas de fichiers directement dans /opt/WEB/DOWNLOADS mais dans une sous-arborescence avec des noms de dossiers complexes et aléatoires. Il devient alors quasi-impossible de trouver les ressources sans avoir le chemin exact.
Toutes les instructions sont dans le programme ci-dessous :
; /opt/WEB/server_illumos.asm --- mini serveur de fichiers (à télécharger) (illumos/OpenIndiana, amd64, NASM)
;
; Identifiez-vous en tant que root
; su -
;
; Installer NASM et tmux
; pkg install pkg://openindiana.org/developer/assembler/nasm pkg:/terminal/tmux
;
; Créer les dossiers pour le serveur web :
; mkdir -p /opt/WEB/DOWNLOADS
;
; Copier tout ce fichier dans le fichier /opt/WEB/server_illumos.asm
;
; Build :
; nasm -felf64 /opt/WEB/server_illumos.asm -o /opt/WEB/server_illumos.o && ld -o /opt/WEB/server_illumos /opt/WEB/server_illumos.o
;
; Créer un utilisateur et un groupe dédiés :
; useradd -m -s /usr/bin/sh web && groupadd web && usermod -g web web
;
; Placer le(s) fichier(s) à servir dans /opt/WEB/DOWNLOADS
;
; Donner les droits et propriétés :
; chown -R web:web /opt/WEB && chmod -R 755 /opt/WEB
;
; Lancer le serveur :
; su - web -c 'nohup /opt/WEB/server_illumos >/var/tmp/server_illumos.log 2>&1 &'
;
; Voir le serveur lancé :
; ps -fu web && netstat -an -f inet | grep 'LISTEN' | grep 8080
;
; Loguer en arrière-plan avec truss quelques temps pour s'assurer que tout va bien :
; tmux new -d -s debug "truss -p \$(pgrep server_illumos) 2> /opt/WEB/log_truss"
;
; Télécharger le(s) fichier(s) servi(s) :
; Se rendre sur l'URL : "http://IP-SERVEUR:8080/FICHIER" pour le télécharger directement.
;
; Tuer le serveur :
; pkill -u web -f /opt/WEB/server_illumos
;
BITS 64
default rel
global _start
; ---------------- NUMÉROS SYS ILLUMOS ----------------
%define SYS_REXIT 1
%define SYS_READ 3
%define SYS_WRITE 4
%define SYS_CLOSE 6
%define SYS_LSEEK 19
%define SYS_SO_SOCKET 230
%define SYS_BIND 232
%define SYS_LISTEN 233
%define SYS_ACCEPT 234
%define SYS_SETSOCKOPT 246
%define SYS_SIGACTION 98
%define SYS_OPENAT 68
%define SYS_SHUTDOWN 236
; ---------------- CONSTANTES ----------------
%define AF_INET 2
%define SOCK_STREAM 2
%define IPPROTO_IP 0
%define SOV_DEFAULT 80
%define SOL_SOCKET 0xFFFF
%define SO_REUSEADDR 0x0004
%define SO_KEEPALIVE 0x0008
%define SO_SNDTIMEO 0x1005
%define SO_RCVTIMEO 0x1006
%define INADDR_ANY 0
%define SHUT_WR 1
%define SIGPIPE 13
%define SIG_IGN 1
; errno utiles
%define EINTR 4
%define EAGAIN 11
%define ENOENT 2
%define EACCES 13
%define EISDIR 21
%define EINVAL 22
; openat / lseek
%define O_RDONLY 0
%define AT_FDCWD -100
%define SEEK_SET 0
%define SEEK_END 2
; ---------------- DONNÉES ----------------
section .data
one: dd 1
; timeouts (LP64 timeval = 16 octets)
rcvto_sec: dq 3
rcvto_usec: dq 0
sndto_sec: dq 5
sndto_usec: dq 0
; <<< RÉPERTOIRE DE BASE POUR LES FICHIERS SERVIS >>>
base_dir: db "/opt/WEB/DOWNLOADS",0
; En-têtes HTTP (réponse succès uniquement)
ok_header: db "HTTP/1.1 200 OK",13,10
db "Content-Type: application/octet-stream",13,10
db "Content-Disposition: attachment",13,10
db "Content-Length: "
ok_hdr_len equ $-ok_header
ok_suffix: db 13,10,"Connection: close",13,10,13,10
ok_sfx_len equ $-ok_suffix
; Messages d'erreur stderr (debug)
err_socket: db "ERR socket",10
err_socket_len equ $-err_socket
err_setsock: db "ERR setsockopt",10
err_setsock_len equ $-err_setsock
err_bind: db "ERR bind",10
err_bind_len equ $-err_bind
err_listen: db "ERR listen",10
err_listen_len equ $-err_listen
err_accept: db "ERR accept",10
err_accept_len equ $-err_accept
err_file: db "ERR open/size file",10
err_file_len equ $-err_file
section .rodata
; sockaddr_in (16 octets) --- port 8080 (htons 8080 = 0x1F90 -> imm LE 0x901F)
addr:
dw AF_INET
dw 0x901F
dd INADDR_ANY
dq 0
addr_len equ $-addr
; ---------------- BSS ----------------
section .bss
reqbuf: resb 4096 ; requête HTTP + espace pour digits CL (fin du tampon)
abs_path: resb 1024 ; chemin absolu final: base_dir + "/" + path
sa: resq 4 ; sigaction
filebuf: resb 8192 ; buffer I/O fichier (sert aussi au "read test" 1 octet)
; ---------------- CODE ----------------
section .text
_start:
; ignorer SIGPIPE
mov qword [sa], SIG_IGN
mov rax, SYS_SIGACTION
mov rdi, SIGPIPE
lea rsi, [rel sa]
xor rdx, rdx
syscall
; socket
mov rax, SYS_SO_SOCKET
mov rdi, AF_INET
mov rsi, SOCK_STREAM
mov rdx, IPPROTO_IP
xor r10, r10
mov r8, SOV_DEFAULT
syscall
jc .e_socket
mov r12, rax
; setsockopt listener
mov rax, SYS_SETSOCKOPT ; REUSEADDR
mov rdi, r12
mov rsi, SOL_SOCKET
mov rdx, SO_REUSEADDR
lea r10, [rel one]
mov r8, 4
mov r9, SOV_DEFAULT
syscall
jc .e_setsock
mov rax, SYS_SETSOCKOPT ; KEEPALIVE
mov rdi, r12
mov rsi, SOL_SOCKET
mov rdx, SO_KEEPALIVE
lea r10, [rel one]
mov r8, 4
mov r9, SOV_DEFAULT
syscall
mov rax, SYS_SETSOCKOPT ; RCVTIMEO
mov rdi, r12
mov rsi, SOL_SOCKET
mov rdx, SO_RCVTIMEO
lea r10, [rel rcvto_sec]
mov r8, 16
mov r9, SOV_DEFAULT
syscall
mov rax, SYS_SETSOCKOPT ; SNDTIMEO
mov rdi, r12
mov rsi, SOL_SOCKET
mov rdx, SO_SNDTIMEO
lea r10, [rel sndto_sec]
mov r8, 16
mov r9, SOV_DEFAULT
syscall
; bind/listen
mov rax, SYS_BIND
mov rdi, r12
lea rsi, [rel addr]
mov rdx, addr_len
mov r10, SOV_DEFAULT
xor r8, r8
xor r9, r9
syscall
jc .e_bind
mov rax, SYS_LISTEN
mov rdi, r12
mov rsi, 64
mov rdx, SOV_DEFAULT
xor r10, r10
xor r8, r8
xor r9, r9
syscall
jc .e_listen
.accept_loop:
.accept_retry:
mov rax, SYS_ACCEPT
mov rdi, r12
xor rsi, rsi
xor rdx, rdx
mov r10, SOV_DEFAULT
xor r8, r8
xor r9, r9
syscall
jnc .after_accept
cmp rax, EINTR
je .accept_retry
cmp rax, EAGAIN
je .accept_retry
jmp .e_accept
.after_accept:
mov r13, rax ; connfd
; lire la requête
mov rax, SYS_READ
mov rdi, r13
lea rsi, [rel reqbuf]
mov rdx, 4096
syscall
; RAX = nb d'octets lus (peut être <=0). En cas de doute -> silence.
test rax, rax
jle .finish_silent
; ---- parser "GET /path HTTP" ----
lea rbx, [rel reqbuf] ; RBX = start
mov r8, rax ; r8 = len lue
cmp r8, 14
jb .finish_silent
; vérifier "GET "
cmp byte [rbx+0], 'G'
jne .finish_silent
cmp byte [rbx+1], 'E'
jne .finish_silent
cmp byte [rbx+2], 'T'
jne .finish_silent
cmp byte [rbx+3], ' '
jne .finish_silent
; trouver début du path (après l'espace)
lea rsi, [rbx+4] ; rsi -> path
mov rcx, r8
sub rcx, 4 ; rcx = max chars restants
mov rdx, rsi ; rdx = cursor
.find_sp:
cmp rcx, 0
je .finish_silent
cmp byte [rdx], ' '
je .got_path_end
cmp byte [rdx], '?'
je .got_path_end
cmp byte [rdx], 13
je .got_path_end
cmp byte [rdx], 10
je .got_path_end
inc rdx
dec rcx
jmp .find_sp
.got_path_end:
; RSI = start, RDX = end (non-inclus)
; refuse ".." dans le path
mov r9, rsi
.scan_dotdot:
cmp r9, rdx
jae .ok_path_sanit
cmp byte [r9], '.'
jne .adv1
cmp r9, rdx
jae .ok_path_sanit
cmp byte [r9+1], '.'
jne .adv1
jmp .finish_silent
.adv1:
inc r9
jmp .scan_dotdot
.ok_path_sanit:
; construire abs_path = base_dir + "/" + (path sans le 1er '/')
lea rdi, [rel abs_path] ; dst
lea rax, [rel base_dir] ; src
lea r15, [rel abs_path+1023]; fin du buffer
.copy_base:
cmp rdi, r15
jae .finish_silent
mov bl, [rax]
mov [rdi], bl
inc rax
inc rdi
test bl, bl
jne .copy_base
dec rdi
; ajouter "/" si base_dir ne finit pas par "/"
cmp byte [rdi-1], '/'
je .skip_slash
mov byte [rdi], '/'
inc rdi
.skip_slash:
; sauter les "/" en début de path
.skip_leading:
cmp rsi, rdx
jae .finish_silent ; GET / -> silence
cmp byte [rsi], '/'
jne .copy_path
inc rsi
jmp .skip_leading
.copy_path:
cmp rsi, rdx
jae .path_copied
cmp rdi, r15
jae .finish_silent ; chemin trop long -> silence
mov bl, [rsi]
mov [rdi], bl
inc rsi
inc rdi
jmp .copy_path
.path_copied:
mov byte [rdi], 0 ; NUL-terminate
; si le chemin final finit par '/', c'est un répertoire -> silence
cmp rdi, abs_path
jbe .finish_silent
cmp byte [rdi-1], '/'
je .finish_silent
; ---- openat(AT_FDCWD, abs_path, O_RDONLY, 0)
mov rax, SYS_OPENAT
mov rdi, AT_FDCWD
lea rsi, [rel abs_path]
mov rdx, O_RDONLY
xor r10, r10
syscall
jc .finish_silent ; ENOENT/EACCES/whatever -> silence
mov r14, rax ; fd fichier
; ---- TEST LECTURE 1 OCTET pour écarter répertoires & fd illisibles
mov rax, SYS_READ
mov rdi, r14
lea rsi, [rel filebuf]
mov rdx, 1
syscall
jc .silent_close_file ; erreur (EISDIR, etc.) -> silence
; succès: repositionner au début
mov rax, SYS_LSEEK
mov rdi, r14
xor rsi, rsi
mov rdx, SEEK_SET
syscall
jc .silent_close_file
; ---- taille = lseek(fd, 0, SEEK_END), puis rewind
mov rax, SYS_LSEEK
mov rdi, r14
xor rsi, rsi
mov rdx, SEEK_END
syscall
jc .silent_close_file
mov rbx, rax
mov rax, SYS_LSEEK
mov rdi, r14
xor rsi, rsi
mov rdx, SEEK_SET
syscall
jc .silent_close_file
; ---- écrire l'en-tête 200 + Content-Length
mov rax, SYS_WRITE
mov rdi, r13
lea rsi, [rel ok_header]
mov rdx, ok_hdr_len
syscall
; convertir RBX (taille) en décimal
mov rax, rbx
mov rcx, 10
lea rsi, [rel reqbuf+4096]
mov r8, 0
test rax, rax
jnz .cl_loop
dec rsi
mov byte [rsi], '0'
mov r8, 1
jmp .cl_done
.cl_loop:
xor rdx, rdx
div rcx
add dl, '0'
dec rsi
mov [rsi], dl
inc r8
test rax, rax
jnz .cl_loop
.cl_done:
mov rax, SYS_WRITE
mov rdi, r13
mov rdx, r8
syscall
; suffixe (CRLF + Connection: close + CRLFCRLF)
mov rax, SYS_WRITE
mov rdi, r13
lea rsi, [rel ok_suffix]
mov rdx, ok_sfx_len
syscall
; ---- copier le fichier par blocs
.file_loop:
mov rax, SYS_READ
mov rdi, r14
lea rsi, [rel filebuf]
mov rdx, 8192
syscall
test rax, rax
jle .file_done
mov rdx, rax
mov rax, SYS_WRITE
mov rdi, r13
lea rsi, [rel filebuf]
syscall
jmp .file_loop
.file_done:
mov rax, SYS_CLOSE
mov rdi, r14
syscall
jmp .finish_conn
.silent_close_file:
mov rax, SYS_CLOSE
mov rdi, r14
syscall
jmp .finish_silent
.finish_conn:
; shutdown + close (voie polie après envoi)
mov rax, SYS_SHUTDOWN
mov rdi, r13
mov rsi, SHUT_WR
mov rdx, SOV_DEFAULT
xor r10, r10
xor r8, r8
xor r9, r9
syscall
mov rax, SYS_CLOSE
mov rdi, r13
syscall
jmp .accept_loop
.finish_silent:
; fermeture silencieuse (rien envoyé)
mov rax, SYS_CLOSE
mov rdi, r13
syscall
jmp .accept_loop
; ---------------- ERREURS FATALES (log stderr, exit) ----------------
.e_socket:
mov rax, SYS_WRITE
mov rdi, 2
lea rsi, [rel err_socket]
mov rdx, err_socket_len
syscall
jmp .fatal
.e_setsock:
mov rax, SYS_WRITE
mov rdi, 2
lea rsi, [rel err_setsock]
mov rdx, err_setsock_len
syscall
jmp .fatal
.e_bind:
mov rax, SYS_WRITE
mov rdi, 2
lea rsi, [rel err_bind]
mov rdx, err_bind_len
syscall
jmp .fatal
.e_listen:
mov rax, SYS_WRITE
mov rdi, 2
lea rsi, [rel err_listen]
mov rdx, err_listen_len
syscall
jmp .fatal
.e_accept:
mov rax, SYS_WRITE
mov rdi, 2
lea rsi, [rel err_accept]
mov rdx, err_accept_len
syscall
jmp .fatal
.e_file:
mov rax, SYS_WRITE
mov rdi, 2
lea rsi, [rel err_file]
mov rdx, err_file_len
syscall
jmp .fatal
.fatal:
mov rdi, 1
mov rax, SYS_REXIT
syscall
↑ Haut de page