-
-
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 |
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)
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
Note that this is buggy; the
bind
system call passessa_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
guys i randomly stumbled upon this code. I wish I had the brains to understand this.
@vinod-somebody You do, my friend. Just have to get the basics.
Read about basic linux system calls, basic calling convention, and basic assembly.
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 /".
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!