Computing-101笔记
Computing 101 概述
这部分是pwncollege下的一个dojo
这里做一部分笔记,以后慢慢补全
首先还是这部分很有趣,你会发现以前在python,在C中使用的那些接口,在汇编层面的体现,怎么直接和系统与硬件交互。
C语言中的循环,判断,跳转,switch语句……是在底层如何实现的,进一步地,为什么C语言是这么写出来的,为什么要这么规定。
为什么要有各种各样的指针?(这次是新的角度的理解!)
为什么有些设计被弃用,为什么有些设计又被沿用?
socket,bind,listen,accept,为什么建立一个连接要有这么多调用接口与规范?
而HTTP server在底层是如何解析请求的?
……
从而一瞥机器的各种运行原理,理解上层接口设定缘由,设计思想。
“计算机的世界里没有魔法!” ——by nju jyy
“你们(作为985院校的专业学生)和其它一般的程序员etc,的一个重要区别在于,你们具有很好的读文档,查阅文档的能力。这将是你们专业性的体现。” ——by nju jyy
另一个,这部分内容提供了一个机会去更加专业化地掌握相关工具:gdb,Linux commands,systemcall……
而且这部分也是pwn的基础。学了不知道多久都没开始pwn过。。。😭出去不敢说自己是学安全的。
https://jyywiki.cn/OS/manuals/sysv-abi.pdf
https://en.wikipedia.org/wiki/X86
第一个链接的文档更适合在开发环境中使用,例如编写系统级代码、调试驱动程序或进行性能优化。
第二个链接的页面更适合在学习阶段使用,帮助读者快速了解 x86 架构的基本概念和历史背景。
在维基百科中详细说了x86系列的历史和相关内容,这里主要关注的是x86-64架构,查阅的文档就是System V ABI
x86-64 是 x86 架构的 64 位扩展,由 AMD 首先提出,Intel 随后也支持。
System V ABI:是针对特定架构(如 AMD64)的二进制接口规范,详细描述了系统调用、内存布局和函数调用约定。

System V AMD64 ABI 参考
In the System V AMD64 ABI, which is commonly used in Linux, the first few integer or pointer arguments to a function are passed in specific registers:
First argument: rdi
Second argument: rsi
Third argument: rdx
Fourth argument: rcx
Fifth argument: r8
Sixth argument: r9
1 | |
Build A Web Server
我想在笔记中记录方法和思考。所以内容可能会变得迷惑~
SOURCE
WRITEUP
TOOLS-系统调用表
- socket
- bind
- listen
- accept
套接字系统调用-Socket
1 | |
socket()创建一个通信端点,返回指向这个端点的文件描述符。
- domain:指定一个通信域;选择协议簇
- type:指定通信语义
- protocol:指定套接字使用的协议
为了建立Socket system call,首先需要找到相关参数的值(AF_INET)(SOCK_STREAM)
Great question! In C programming, these constants are defined in header files like <sys/socket.h>. However, when working in assembly, you need to use their numeric values directly. These values are standardized across systems:
AF_INET is usually defined as 2.
SOCK_STREAM is usually defined as 1.
You can find these definitions in the C header files on your system. For example, you can look at /usr/include/x86_64-linux-gnu/bits/socket.h or similar files depending on your system architecture.
If you’re curious, you can use the grep command to search for these definitions in your system’s include directories.
1 | |
更便捷的办法可以是用python的pwn.constants速查
1 | |
1 | |
之后就是查找系统调用表。
注意:存入寄存器中的系统调用号必须是十六进制
系统调用-Bind
Q: 既然Socket是通信的端点,那为什么有了Socket还要Bind?
A: 套接字需要被分配到网络实体,这包括具体的IP地址和端口,还要接受收到的数据(这就意味着要与内存交互)
因此,Bind要求有3个参数:
1 | |
- sockfd:指向套接字的文件描述符
- *addr:指向分配到套接字的地址(需要一个结构体)
- addrlen:指定由addr指向的地址结构的大小,以字节为单位。
1 | |
更具体地,
{sa_family=AF_INET,
sin_port=htons(
sin_addr=inet_addr(“
写成汇编代码:
1 | |
在这里可以看到,Port80,是按照大端存储的,这在网络通信中常见,而小端存储为内存中的操作带来了不少便捷
1 | |
我还记得在《CSAPP》中,老师说过想清楚lea指令是关键。
之前只是知道是这样,但这次看到了实际使用=>指向地址的指针变量赋值用lea
lea指令专门用于计算地址,而mov指令用于加载数据值,两者的用途不同。
例如,lea rsi, [rip+sockaddr]会计算rip+sockaddr的地址,并将这个地址加载到rsi寄存器中。
系统调用-Listen
1 | |
- sockfd:socket的文件描述符
- backlog:指定sockfd的待处理连接队列长度的最大值
如何保存rax=3
rax中存储的套接字描述符会被Bind Syscall的返回值覆盖
=>使用栈去保存
push rax
pop rdi
之后将backlog设置为0,这里不需要队列
mov rdi,0
mov rax,0x32
syscall
系统调用-Accept
用accept系统调用,它会等待客户端的连接。当建立连接时,它会返回一个新的套接字文件描述符,专门用于与该客户端通信,并且会用客户端的详细信息填充一个提供的地址结构(例如struct sockaddr_in)。这一过程是将你的服务器从被动监听者转变为积极通信者的关键步骤。
1 | |
- sockfd
- addr:指向sockaddr结构体的指针
- addrlen:addr指向的结构体的长度
1 | |
到现在,已经是一个可以建立连接的服务器了了。
静态响应数据-系统调用read&write
如何为尝试进行连接的客户端发送一个静态的HTTP响应?
HTTP/1.0 200 OK\r\n\r\n
This exercise is important because it teaches you how to format and deliver data over the network.
动态响应数据-系统调用open
这次,服务器程序需要对HTTP GET请求返回动态的数据。
- read 来自HTTP请求的客户套接字
- 解析请求内容
- open解析到的请求的文件
- read文件
- write 将文件内容write回套接字
- close关闭文件
- exit
(这次可以从汇编层面看到HTTP请求内容的解析过程)
解析请求内容
承接上一关在这里,accept系统调用返回的套接字文件操作符在rax中
从trace记录中看到这次读到的内容包含了文件名——一个随机字符串。

所以在write之前要先想办法解析请求
解析什么?
1 | |
- *pathname:指向需要被打开的文件的指针
- flags:必须包含文件被打开的模式(O_RDONLY,O_WRONLY,or O_RDWR etc)
- mode:如果文件是新创建的,指定文件权限
假设这里需要先确认下grep命令行工具的用法:
man grep
搜索-r
添加-r选项,递归地搜索整个目录下的文件内容
1 | |
在 Unix 和 Linux 系统中,fcntl.h 文件中定义的文件打开标志(如 O_RDONLY、O_WRONLY 等)通常使用八进制表示。这是因为八进制表示在位操作中更加直观。
可以查到用数字0设置O_RDONLY的值
=> mov rsi,0
以上过程由LLM完成会很方便,但这是先前的方法。
于是这里确定,要写汇编代码解析出文件名=>
- 一个指向文件名字符串开头的指针(寄存器)
- 一个存放着文件名长度的寄存器
完整的GET请求:
前一条read将内容读到了栈上(rsp)
1 | |

这里从r10开始使用更多的寄存器。
1 | |
1 | |