Building a Web Server in Assembly
What You Will Learn
- How socket communication works at the system call level
- The sequence of C socket API calls to create a server
- How to map C socket functions to Linux syscalls
- Why this knowledge is valuable for exploit development
What Is It?
Building a web server from scratch — using sockets in C or directly in assembly — teaches you exactly how network communication works at the lowest level. This knowledge is fundamental for exploit development, writing shellcode that connects back, and understanding network programs.
Why It Matters
- Shellcode for reverse shells uses these exact syscalls
- Understanding socket code helps you read and modify exploits
- Binary analysis of network programs requires knowing the socket API
- CTF challenges often involve writing custom network clients
How Sockets Communicate
For two computers to communicate over a network, both nodes need to specify how they will send and receive data. One node listens (the server) while the other connects (the client).
The server-side socket creation process:
socket()
|
bind()
|
listen()
|
accept()
|
read()/write()
Socket API in C
Creating a Socket
int socket(int domain, int type, int protocol);
Example:
int sfd = socket(AF_INET, SOCK_STREAM, 0);
// AF_INET = IPv4, SOCK_STREAM = TCP, 0 = default protocol
Binding the Socket
struct sockaddr_in my_addr;
my_addr.sin_family = AF_INET;
my_addr.sin_port = htons(8080);
my_addr.sin_addr.s_addr = INADDR_ANY;
bind(sfd, (struct sockaddr *)&my_addr, sizeof(my_addr));
The sockaddr_in structure:
struct sockaddr_in {
sa_family_t sin_family; // address family: AF_INET
in_port_t sin_port; // port in network byte order
struct in_addr sin_addr; // internet address
};
Listening for Connections
int listen(int sockfd, int backlog);
// backlog = max number of pending connections in the queue
Accepting a Connection
struct sockaddr_in client_addr;
socklen_t client_len = sizeof(client_addr);
int conn_fd = accept(sfd, (struct sockaddr *)&client_addr, &client_len);
Reading and Writing
char buf[1024];
int n = read(conn_fd, buf, sizeof(buf));
write(conn_fd, "HTTP/1.1 200 OK\r\n\r\nHello!", 25);
close(conn_fd);
Complete Minimal HTTP Server
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <netinet/in.h>
int main() {
int sfd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr = {0};
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(sfd, (struct sockaddr *)&addr, sizeof(addr));
listen(sfd, 5);
while (1) {
int conn = accept(sfd, NULL, NULL);
char buf[4096];
read(conn, buf, sizeof(buf));
char response[] = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\nHello, World!";
write(conn, response, sizeof(response));
close(conn);
}
return 0;
}
Linux Syscall Numbers for Sockets
On x86-64 Linux, socket operations map to syscall number 41:
socket = 41
bind = 49
listen = 50
accept = 43
connect = 42
send = 44
recv = 45
close = 3
Reference: Linux x86-64 System Call Table
Socket Syscall in Assembly
; socket(AF_INET=2, SOCK_STREAM=1, 0) = sockfd
mov rax, 41 ; sys_socket
mov rdi, 2 ; AF_INET
mov rsi, 1 ; SOCK_STREAM
mov rdx, 0 ; protocol
syscall