繁体   English   中英

为什么这个组件 HTTP 服务器不起作用?

[英]Why doesn't this assembly HTTP server work?

我在 docker 中遇到了可以说是最小的 HTTP 服务器(用汇编语言编写),我很想看到它的实际应用!

我认为他们从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

我可以组装和链接代码而不会出现任何错误。

但是当我运行它时,似乎什么也没有发生。

我需要做什么才能在我的浏览器中从 HTTP 服务器中看到 output?

似乎对我来说几乎没有什么工作,尽管玛格丽特布鲁姆注意到它有问题。 (它在随机端口上侦听,因为它进行了错误的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返回的全一),那么接下来的几个系统调用将具有无效的调用号。 但最终它确实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 output 已经增长到

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 ,由于它的构建脚本,它可以构建为更小的二进制文件。 有一个 C 版本(它为系统调用调用手写的 asm 包装器,而不仅仅是使用内联 asm)。 请参阅没有 libc 的 C 程序如何工作? 关于它。

正如我在那里提到的,通过切换到clang -Oz并使用内联汇编进行系统调用,我将 C 版本从 1.2k 降低到 992 字节,所以整个事情可能是一片叶子 ZC1C42525268E68A785D14 (不需要保存/恢复任何东西的寄存器。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM