pwn.college: Building a Web Server
来来来,你告诉我,什么TMD叫Web,什么TMD叫Asm。这TM八岁 ?_?
Building a Web Server
level1
正常的exit汇编
1 |
|
level2
要实现socket,其系统调用为41。原型int socket(int domain, int type, int protocol);
socket(AF_INET, SOCK_STREAM,IPPROTO_IP)=3
也就是说,domain为AF_INET
,type为SOCK_STREAM
,protocol为IPPROTO_IP
其中,关于第一个参数说明如下:
AF_INET
(2): IPv4 地址族。AF_INET6
(10): IPv6 地址族。AF_UNIX
(1): Unix 套接字。 这些套接字用于进程间通信,通常在本地主机上使用。 它们不是基于网络的。AF_BLUETOOTH
(12): 用于蓝牙通信的套接字类型。AF_PACKET
(17): 允许应用程序访问底层网络帧。AF_NETLINK
(16): 用于内核模块和用户空间进程之间的消息传递。AF_AX25
(11): 用于 AX.25 协议的套接字类型。AF_X25
(15): 用于 X.25 协议的套接字类型。AF_ATMPVC
(21): 用于 ATM (Asynchronous Transfer Mode) 协议的虚拟通道 (PVC)。AF_APPLETALK
(23): 用于 AppleTalk 协议的套接字类型。AF_NETBIOS
(24): 用于 NetBIOS 协议的套接字类型。
关于第二个参数说明如下:
SOCK_STREAM
(1): 面向连接的流式套接字。数据被可靠地传输,以字节流的形式发送,顺序保证。 这类似于 TCP。SOCK_DGRAM
(2): 无连接的数据报套接字。数据包可能会丢失或顺序颠倒。 这类似于 UDP。 数据被发送成独立的数据报,没有可靠性保证。SOCK_SEQPACKET
(3): 面向连接的序列包套接字。它试图提供 TCP 的可靠性但不需要三次握手,在某些情况下性能更好。 这种类型比较少见。SOCK_RAW
(3): 原始套接字。允许应用程序访问底层网络协议。程序员可以访问网络协议的详细信息,控制传输的整个过程。这需要对网络协议有深入的了解,使用风险比较高。
关于第三个参数说明如下:
IPPROTO_IP
(0): IP 协议。 这个值通常在使用 TCP 或 UDP 时使用,因为它在底层协议栈中就已经存在了。 它不是一个独立的协议,而是 IP 协议栈的一部分。IPPROTO_TCP
(6): 传输控制协议 (TCP)。 这指定了使用 TCP 协议。IPPROTO_UDP
(17): 用户数据报协议 (UDP)。 这指定了使用 UDP 协议。IPPROTO_ICMP
(1): 因特网控制消息协议 (ICMP)。 ICMP 用于错误报告和查询。IPPROTO_IGMP
(2): 因特网组管理协议 (IGMP)。 IGMP 用于多播。IPPROTO_IPIP
(4): IP over IP。 这允许一个 IP 数据报封装在另一个 IP 数据报中。IPPROTO_RSVP
(46): 资源预留协议 (RSVP)。 用于保证多媒体应用程序的带宽。IPPROTO_GRE
(47): 通用路由封装 (GRE)。 允许将不同协议的数据包封装在 IP 数据报中。IPPROTO_AH
(51): 认证头 (AH)。 用于安全通信。
那么最终的exp为:
1 |
|
level3
实现bind,bind(3, {sa_family=AF_INET, sin_port=htons(<bind_port>), sin_addr=inet_addr("<bind_address>")}, 16) = 0
bind原型:int bind(int sockfd, const struct sockaddr addr, socklen_t addrlen)
好吧,我想不出来。看别人的wp,很好,用到了.data段定义这个结构体。然后端口是0x5000,是80的小端序
1 |
|
绑定的地址为0.0.0.0即bind(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
level4
要完成listen(3, 0) = 0
,这个简单啊,listen系统调用号为50
1 |
|
level5
要完成accept(3, NULL, NULL) = 4
accept的系统调用号为43
1 |
|
level6
这一关有点东西,要完成三个东西:read(4, <read_request>, <read_request_count>) = <read_request_result>
,write(4, "HTTP/1.0 200 OK\r\n\r\n", 19) = 19
和close(4) = 0
1 |
|
这一关的目的是,能够接收客户端发送来的消息,并反馈200状态码。
level7
那么这一关,就响应客户端的请求,将它要的文件,发送给它。那么就有个问题了,第一个read会读取客户端发来的请求,然后这个请求很长,该怎么精准拿到这个文件呢?
1 |
|

我发现了一个简单的方法用变量存起来就好了,然后截断第一个空格。
1 |
|
level8
这一关就是加一个accept调用即可。
1 |
|
level9
这道题要用fork,经过第七题的拷打之后,简单一些了。
1 |
|
level10
这一关,还挺折磨的。对于客户端发来的信息:
1 |
|
需要解析出length和这个随机字符串。因为后续需要通过write写入文件/tmp/xxx
。所以,我想到的方法是将这个字符串存入buffer,然后从buffer的最后一个字节开始逆序遍历,到第一个\n
停止。这样可以拿到这个随机字符串的逆序,以及用一个寄存器记录一下长度。后续再通过这个长度来再一次逆序这个随机字符串,从而写入/tmp/xxx
。最终代码如下:
1 |
|
总共三个循环,还是挺复杂的。主要是有更简单的方法。!
使用\n
的索引即可。如下:
1 |
|
level11
这道题的关键就是多线程,也就是父进程需要不断地接收新的连接,并fork出子进程来处理这个连接,然后再接收新的连接,再fork出子进程来处理。因此,子进程部分就是level9和level10的结合。额外加一个循环,让父进程不断地accept和fork就好了。
1 |
|