Skip to content

Instantly share code, notes, and snippets.

@DGivney
Forked from xenomuta/httpd.asm
Last active August 29, 2024 12:31
Show Gist options
  • Save DGivney/5917914 to your computer and use it in GitHub Desktop.
Save DGivney/5917914 to your computer and use it in GitHub Desktop.
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 0x64334e77 ; d3Nw
push dword 0x503e3168 ; P>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
@prushik
Copy link

prushik commented Sep 1, 2014

It's assembly language, you wouldn't compile it at all. You can assemble it using an assembler such as NASM or YASM on Linux x86. Something as simple as "yasm httpd.asm" should work. That will give you object code (httpd.o), which you can then make into an executable with a linker such as ld.
yasm httpd.asm
ld -o httpd httpd.o
./httpd

@lattice0
Copy link

lattice0 commented Jan 9, 2020

hi, just came from https://asmtutor.com/

would be very nice if there was a PDF so I could read on kindle

thank you so much!

@pcordes
Copy link

pcordes commented May 8, 2021

You'd build this into a 32-bit static executable for Linux with

 nasm -felf32 httpd.asm  &&
 ld -melf_i386 httpd.o -o httpd

 strip httpd           # optional

(I have an "asm-link" script that would do this with asm-link -m32 httpd.asm. It's included in my answer on https://stackoverflow.com/questions/36861903/assembling-32-bit-binaries-on-a-64-bit-system-gnu-toolchain/36901649#36901649)

@pcordes
Copy link

pcordes commented May 8, 2021

Note that this is buggy; the bind system call passes sa_family=AF_UNIX, and doesn't end up binding to a specific TCP port, instead letting the kernel randomly pick which one it listens on.
https://stackoverflow.com/questions/67445637/why-doesnt-this-assembly-http-server-work

$ 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

@xenomuta
Copy link

Note that this is buggy; the bind system call passes sa_family=AF_UNIX, and doesn't end up binding to a specific TCP port, instead letting the kernel randomly pick which one it listens on.
https://stackoverflow.com/questions/67445637/why-doesnt-this-assembly-http-server-work

$ 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

please, refer to answer on original Gist here

@vinodkmwt
Copy link

guys i randomly stumbled upon this code. I wish I had the brains to understand this.

@xenomuta
Copy link

@vinod-somebody You do, my friend. Just have to get the basics.
Read about basic linux system calls, basic calling convention, and basic assembly.

@ludolpif
Copy link

Hi, I get the same bad strace result as @pcordes with invalid syscalls. I use 6.1.0-22-amd64 kernel on debian. I fixed the asm by changing :

- push word bx ; 1 AF_FILE
+ push word 0x02 ; 2 AF_INET

Then I use this Makefile :

.PHONY: all clean

all: httpd

clean:
        rm -f httpd

httpd: httpd.o
        ld -m elf_i386 -n -o httpd httpd.o
        strip httpd

httpd.o: httpd.asm
        yasm -f ELF httpd.asm

And I get a working 436 bytes x86 (32 bits) binary :

$ ls -l httpd
-rwxr-xr-x 1 ludolpif ludolpif 436 29 août  14:24 httpd
$ strace ./httpd 
execve("./httpd", ["./httpd"], 0x7ffc46bb40f0 /* 48 vars */) = 0
[ Process PID=429633 runs in 32 bit mode. ]
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
bind(3, {sa_family=AF_INET, sin_port=htons(8800), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 1)                            = 0
accept(3, NULL, NULL)                   = 4
fork()                                  = 429921
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=429921, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
listen(3, 1)                            = 0
accept(3, NULL, NULL
[waiting next connection]

On client side :

$ nc -v localhost 8800
localhost [127.0.0.1] 8800 (?) open
HTTP/1.0 200 OK
Content-Type: text/html

<h1>PwN3d!</h1>
^C

Remarks : this code send http reply right after TCP syn/ack, without waiting a http request like "GET /".

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment