为什么这个程序集 HTTP 服务器不起作用?
Why doesn't this assembly HTTP server work?
我遇到了 arguably the smallest HTTP server in docker(用汇编语言编写),我很想看看它的实际应用!
我认为他们从 https://gist.github.com/DGivney/5917914 获取代码:
section .text
global _start
_start:
xor eax, eax ; init eax 0
xor ebx, ebx ; init ebx 0
xor esi, esi ; init esi 0
jmp _socket ; jmp to _socket
_socket_call:
mov al, 0x66 ; invoke SYS_SOCKET (kernel opcode 102)
inc byte bl ; increment bl (1=socket, 2=bind, 3=listen, 4=accept)
mov ecx, esp ; move address arguments struct into ecx
int 0x80 ; call SYS_SOCKET
jmp esi ; esi is loaded with a return address each call to _socket_call
_socket:
push byte 6 ; push 6 onto the stack (IPPROTO_TCP)
push byte 1 ; push 1 onto the stack (SOCK_STREAM)
push byte 2 ; push 2 onto the stack (PF_INET)
mov esi, _bind ; move address of _bind into ESI
jmp _socket_call ; jmp to _socket_call
_bind:
mov edi, eax ; move return value of SYS_SOCKET into edi (file descriptor for new socket, or -1 on error)
xor edx, edx ; init edx 0
push dword edx ; end struct on stack (arguments get pushed in reverse order)
push word 0x6022 ; move 24610 dec onto stack
push word bx ; move 1 dec onto stack AF_FILE
mov ecx, esp ; move address of stack pointer into ecx
push byte 0x10 ; move 16 dec onto stack
push ecx ; push the address of arguments onto stack
push edi ; push the file descriptor onto stack
mov esi, _listen ; move address of _listen onto stack
jmp _socket_call ; jmp to _socket_call
_listen:
inc bl ; bl = 3
push byte 0x01 ; move 1 onto stack (max queue length argument)
push edi ; push the file descriptor onto stack
mov esi, _accept ; move address of _accept onto stack
jmp _socket_call ; jmp to socket call
_accept:
push edx ; push 0 dec onto stack (address length argument)
push edx ; push 0 dec onto stack (address argument)
push edi ; push the file descriptor onto stack
mov esi, _fork ; move address of _fork onto stack
jmp _socket_call ; jmp to _socket_call
_fork:
mov esi, eax ; move return value of SYS_SOCKET into esi (file descriptor for accepted socket, or -1 on error)
mov al, 0x02 ; invoke SYS_FORK (kernel opcode 2)
int 0x80 ; call SYS_FORK
test eax, eax ; if return value of SYS_FORK in eax is zero we are in the child process
jz _write ; jmp in child process to _write
xor eax, eax ; init eax 0
xor ebx, ebx ; init ebx 0
mov bl, 0x02 ; move 2 dec in ebx lower bits
jmp _listen ; jmp in parent process to _listen
_write:
mov ebx, esi ; move file descriptor into ebx (accepted socket id)
push edx ; push 0 dec onto stack then push a bunch of ascii (http headers & reponse body)
push dword 0x0a0d3e31 ; [\n][\r]>1
push dword 0x682f3c21 ; h/<!
push dword 0x6f6c6c65 ; ello
push dword 0x683e3148 ; H<1h
push dword 0x3c0a0d0a ; >[\n][\r][\n]
push dword 0x0d6c6d74 ; [\r]lmt
push dword 0x682f7478 ; h/tx
push dword 0x6574203a ; et :
push dword 0x65707954 ; epyT
push dword 0x2d746e65 ; -tne
push dword 0x746e6f43 ; tnoC
push dword 0x0a4b4f20 ; \nKO
push dword 0x30303220 ; 002
push dword 0x302e312f ; 0.1/
push dword 0x50545448 ; PTTH
mov al, 0x04 ; invoke SYS_WRITE (kernel opcode 4)
mov ecx, esp ; move address of stack arguments into ecx
mov dl, 64 ; move 64 dec into edx lower bits (length in bytes to write)
int 0x80 ; call SYS_WRITE
_close:
mov al, 6 ; invoke SYS_CLOSE (kernel opcode 6)
mov ebx, esi ; move esi into ebx (accepted socket file descriptor)
int 0x80 ; call SYS_CLOSE
mov al, 6 ; invoke SYS_CLOSE (kernel opcode 6)
mov ebx, edi ; move edi into ebx (new socket file descriptor)
int 0x80 ; call SYS_CLOSE
_exit:
mov eax, 0x01 ; invoke SYS_EXIT (kernel opcode 1)
xor ebx, ebx ; 0 errors
int 0x80 ; call SYS_EXIT
我可以 assemble 和 link 代码没有任何错误。
但是当我运行它时,似乎什么也没有发生。
我需要做什么才能在浏览器中看到程序集 HTTP 服务器的输出?
虽然玛格丽特·布鲁姆 (Margaret Bloom) 注意到它有很多问题,但对我来说似乎几乎不起作用。 (它在 运行dom 端口上侦听,因为它进行了错误的 bind
系统调用。可能为 sa_family
传递了错误的数字)
在使用 nasm -felf32
/ ld -melf_i386
构建/链接后,我 运行 在 strace 下查看它做了什么。
$ strace ./httpd
execve("./httpd", ["./httpd"], 0x7ffde685ac10 /* 54 vars */) = 0
[ Process PID=615796 runs in 32 bit mode. ]
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
bind(3, {sa_family=AF_UNIX, sun_path="\"`"}, 16) = -1 EAFNOSUPPORT (Address family not supported by protocol)
syscall_0xffffffffffffff66(0x4, 0xffd53c58, 0, 0x8049043, 0x3, 0) = -1 ENOSYS (Function not implemented)
syscall_0xffffffffffffff66(0x5, 0xffd53c4c, 0, 0x804904d, 0x3, 0) = -1 ENOSYS (Function not implemented)
syscall_0xffffffffffffff02(0x5, 0xffd53c4c, 0, 0xffffffda, 0x3, 0) = -1 ENOSYS (Function not implemented)
listen(3, 1) = 0
accept(3, NULL, NULL
mov al, callnum
保存字节的技巧假定 EAX 的高位字节仍然为 0。如果它们不是(来自 -errno
return 的全一),下一个很少有系统调用带有无效的调用号。但最终它会 listen(3,1)
和 accept
,所以它 是 在某处监听。我用 ps
找到它的 PID,然后 使用 lsof
找出它正在监听的端口:
$ lsof -p 615796
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
httpd 615796 peter cwd DIR 0,55 940 1 /tmp
httpd 615796 peter rtd DIR 0,27 158 256 /
httpd 615796 peter txt REG 0,55 5412 56241 /tmp/httpd
httpd 615796 peter 0u CHR 136,20 0t0 23 /dev/pts/20
httpd 615796 peter 1u CHR 136,20 0t0 23 /dev/pts/20
httpd 615796 peter 2u CHR 136,20 0t0 23 /dev/pts/20
httpd 615796 peter 3u IPv4 86480691 0t0 TCP *:36047 (LISTEN)
使用 nc
(netcat) 连接到该端口使其转储其固定字符串有效负载并保持连接打开:
$ nc localhost 36047
HTTP/1.0 200 OK
Content-Type: text/html
<h1>PwN3d!</h1>
CONTROL-C
$
将 Chromium 指向 http://localhost:36047/ 也加载了一个页面,但由于连接保持打开状态,它仍在旋转等待更多数据。
几个连接后的 strace 输出已经增长到
accept(3, NULL, NULL) = 4
fork() = 615904
listen(3, 1) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=615904, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
accept(3, NULL, NULL) = 5
fork() = 615986
listen(3, 1) = 0
accept(3, NULL, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=615986, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
accept(3, NULL, NULL
顺便说一句,另一个最小的 HTTPD,由于其构建脚本而构建成一个更小的二进制文件,https://github.com/Francesco149/nolibc-httpd. There's a C version (which calls handwritten asm wrappers for system calls, instead of just using inline asm). See 关于它。
正如我在那里提到的,通过切换到 clang -Oz
并使用内联 asm 进行系统调用,我将 C 版本从 1.2k 减少到 992 字节,因此整个事情可以是叶函数。 (不需要 save/restore 注册任何东西。)https://github.com/pcordes/nolibc-httpd/commit/ad3a80b89b98379304f1525339fa71700bf1a15d
我遇到了 arguably the smallest HTTP server in docker(用汇编语言编写),我很想看看它的实际应用!
我认为他们从 https://gist.github.com/DGivney/5917914 获取代码:
section .text
global _start
_start:
xor eax, eax ; init eax 0
xor ebx, ebx ; init ebx 0
xor esi, esi ; init esi 0
jmp _socket ; jmp to _socket
_socket_call:
mov al, 0x66 ; invoke SYS_SOCKET (kernel opcode 102)
inc byte bl ; increment bl (1=socket, 2=bind, 3=listen, 4=accept)
mov ecx, esp ; move address arguments struct into ecx
int 0x80 ; call SYS_SOCKET
jmp esi ; esi is loaded with a return address each call to _socket_call
_socket:
push byte 6 ; push 6 onto the stack (IPPROTO_TCP)
push byte 1 ; push 1 onto the stack (SOCK_STREAM)
push byte 2 ; push 2 onto the stack (PF_INET)
mov esi, _bind ; move address of _bind into ESI
jmp _socket_call ; jmp to _socket_call
_bind:
mov edi, eax ; move return value of SYS_SOCKET into edi (file descriptor for new socket, or -1 on error)
xor edx, edx ; init edx 0
push dword edx ; end struct on stack (arguments get pushed in reverse order)
push word 0x6022 ; move 24610 dec onto stack
push word bx ; move 1 dec onto stack AF_FILE
mov ecx, esp ; move address of stack pointer into ecx
push byte 0x10 ; move 16 dec onto stack
push ecx ; push the address of arguments onto stack
push edi ; push the file descriptor onto stack
mov esi, _listen ; move address of _listen onto stack
jmp _socket_call ; jmp to _socket_call
_listen:
inc bl ; bl = 3
push byte 0x01 ; move 1 onto stack (max queue length argument)
push edi ; push the file descriptor onto stack
mov esi, _accept ; move address of _accept onto stack
jmp _socket_call ; jmp to socket call
_accept:
push edx ; push 0 dec onto stack (address length argument)
push edx ; push 0 dec onto stack (address argument)
push edi ; push the file descriptor onto stack
mov esi, _fork ; move address of _fork onto stack
jmp _socket_call ; jmp to _socket_call
_fork:
mov esi, eax ; move return value of SYS_SOCKET into esi (file descriptor for accepted socket, or -1 on error)
mov al, 0x02 ; invoke SYS_FORK (kernel opcode 2)
int 0x80 ; call SYS_FORK
test eax, eax ; if return value of SYS_FORK in eax is zero we are in the child process
jz _write ; jmp in child process to _write
xor eax, eax ; init eax 0
xor ebx, ebx ; init ebx 0
mov bl, 0x02 ; move 2 dec in ebx lower bits
jmp _listen ; jmp in parent process to _listen
_write:
mov ebx, esi ; move file descriptor into ebx (accepted socket id)
push edx ; push 0 dec onto stack then push a bunch of ascii (http headers & reponse body)
push dword 0x0a0d3e31 ; [\n][\r]>1
push dword 0x682f3c21 ; h/<!
push dword 0x6f6c6c65 ; ello
push dword 0x683e3148 ; H<1h
push dword 0x3c0a0d0a ; >[\n][\r][\n]
push dword 0x0d6c6d74 ; [\r]lmt
push dword 0x682f7478 ; h/tx
push dword 0x6574203a ; et :
push dword 0x65707954 ; epyT
push dword 0x2d746e65 ; -tne
push dword 0x746e6f43 ; tnoC
push dword 0x0a4b4f20 ; \nKO
push dword 0x30303220 ; 002
push dword 0x302e312f ; 0.1/
push dword 0x50545448 ; PTTH
mov al, 0x04 ; invoke SYS_WRITE (kernel opcode 4)
mov ecx, esp ; move address of stack arguments into ecx
mov dl, 64 ; move 64 dec into edx lower bits (length in bytes to write)
int 0x80 ; call SYS_WRITE
_close:
mov al, 6 ; invoke SYS_CLOSE (kernel opcode 6)
mov ebx, esi ; move esi into ebx (accepted socket file descriptor)
int 0x80 ; call SYS_CLOSE
mov al, 6 ; invoke SYS_CLOSE (kernel opcode 6)
mov ebx, edi ; move edi into ebx (new socket file descriptor)
int 0x80 ; call SYS_CLOSE
_exit:
mov eax, 0x01 ; invoke SYS_EXIT (kernel opcode 1)
xor ebx, ebx ; 0 errors
int 0x80 ; call SYS_EXIT
我可以 assemble 和 link 代码没有任何错误。
但是当我运行它时,似乎什么也没有发生。
我需要做什么才能在浏览器中看到程序集 HTTP 服务器的输出?
虽然玛格丽特·布鲁姆 (Margaret Bloom) 注意到它有很多问题,但对我来说似乎几乎不起作用。 (它在 运行dom 端口上侦听,因为它进行了错误的 bind
系统调用。可能为 sa_family
传递了错误的数字)
在使用 nasm -felf32
/ ld -melf_i386
构建/链接后,我 运行 在 strace 下查看它做了什么。
$ strace ./httpd
execve("./httpd", ["./httpd"], 0x7ffde685ac10 /* 54 vars */) = 0
[ Process PID=615796 runs in 32 bit mode. ]
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
bind(3, {sa_family=AF_UNIX, sun_path="\"`"}, 16) = -1 EAFNOSUPPORT (Address family not supported by protocol)
syscall_0xffffffffffffff66(0x4, 0xffd53c58, 0, 0x8049043, 0x3, 0) = -1 ENOSYS (Function not implemented)
syscall_0xffffffffffffff66(0x5, 0xffd53c4c, 0, 0x804904d, 0x3, 0) = -1 ENOSYS (Function not implemented)
syscall_0xffffffffffffff02(0x5, 0xffd53c4c, 0, 0xffffffda, 0x3, 0) = -1 ENOSYS (Function not implemented)
listen(3, 1) = 0
accept(3, NULL, NULL
mov al, callnum
保存字节的技巧假定 EAX 的高位字节仍然为 0。如果它们不是(来自 -errno
return 的全一),下一个很少有系统调用带有无效的调用号。但最终它会 listen(3,1)
和 accept
,所以它 是 在某处监听。我用 ps
找到它的 PID,然后 使用 lsof
找出它正在监听的端口:
$ lsof -p 615796
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
httpd 615796 peter cwd DIR 0,55 940 1 /tmp
httpd 615796 peter rtd DIR 0,27 158 256 /
httpd 615796 peter txt REG 0,55 5412 56241 /tmp/httpd
httpd 615796 peter 0u CHR 136,20 0t0 23 /dev/pts/20
httpd 615796 peter 1u CHR 136,20 0t0 23 /dev/pts/20
httpd 615796 peter 2u CHR 136,20 0t0 23 /dev/pts/20
httpd 615796 peter 3u IPv4 86480691 0t0 TCP *:36047 (LISTEN)
使用 nc
(netcat) 连接到该端口使其转储其固定字符串有效负载并保持连接打开:
$ nc localhost 36047
HTTP/1.0 200 OK
Content-Type: text/html
<h1>PwN3d!</h1>
CONTROL-C
$
将 Chromium 指向 http://localhost:36047/ 也加载了一个页面,但由于连接保持打开状态,它仍在旋转等待更多数据。
几个连接后的 strace 输出已经增长到
accept(3, NULL, NULL) = 4
fork() = 615904
listen(3, 1) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=615904, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
accept(3, NULL, NULL) = 5
fork() = 615986
listen(3, 1) = 0
accept(3, NULL, NULL) = ? ERESTARTSYS (To be restarted if SA_RESTART is set)
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=615986, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
accept(3, NULL, NULL
顺便说一句,另一个最小的 HTTPD,由于其构建脚本而构建成一个更小的二进制文件,https://github.com/Francesco149/nolibc-httpd. There's a C version (which calls handwritten asm wrappers for system calls, instead of just using inline asm). See
正如我在那里提到的,通过切换到 clang -Oz
并使用内联 asm 进行系统调用,我将 C 版本从 1.2k 减少到 992 字节,因此整个事情可以是叶函数。 (不需要 save/restore 注册任何东西。)https://github.com/pcordes/nolibc-httpd/commit/ad3a80b89b98379304f1525339fa71700bf1a15d