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 | |