This page looks best with JavaScript enabled

操作系统课程设计

 ·  ☕ 13 min read · 👀... views

前言

操作系统课设上来就是远古版本2.4kernel,原地怀疑题目是不是复用了十多年了。虽然疯狂吐槽 还是想编译试试看.

要求添加系统调用,想着是准备做一个内核流量捕获的简单工具,于是简单的了解了一下捕获流量的方法,看了一下libpcap和tcpdump的源码,了解了mapped memory和ring buffer的使用直接从网卡驱动摘下流量极大减少系统负载,但是由于事件仓促,最后却只是草草地写了个简单的系统调用版本并编译进内核使用qemu调试配合ping进行测试。

Begin

要增加对某部分功能的支持,比如网络之类,可以把相应部分编译到内核中(build-in),也可以把该部分编译成模块(module),动态调用。如果编译到内核中,在内核启动时就可以自动支持相应部分的功能,这样的优点是方便、速度快,机器一启动,你就可以使用这部分功能了;缺点是会使内核变得庞大起来,不管你是否需要这部分功能,它都会存在,建议经常使用的部分直接编译到内核中,比如网卡。如果编译成模块,就会生成对应的.o文件,在使用的时候可以动态加载,优点是不会使内核过分庞大,缺点是你得自己来调用这些模块。

环境

1
Linux ruokeqx 4.4.0-142-generic #168~14.04.1-Ubuntu SMP Sat Jan 19 11:26:28 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux

克隆内核源码(第二个链接会快点)

1
2
git clone https://git.kernel.org/pub/scm/linux/kernel/git/wtarreau/linux-2.4.git/
git clone https://kernel.googlesource.com/pub/scm/linux/kernel/git/wtarreau/linux-2.4

安装编译环境

1
sudo apt install git fakeroot build-essential ncurses-dev xz-utils libssl-dev bc flex libelf-dev bison

make menuconfig将打开一个配置工具,它可以让你遍历每个可用模块,然后启用或者禁用你需要或者不需要的模块。此处选择默认直接保存。

尝试编译,报错如下:

1
#error "GCC >= 4.2 miscompiles kernel 2.4, do not use it!"

需要版本小于4.2的GCC

编译

编译安装GCC4.1.2

GCC官网下载源码http://ftp.gnu.org/gnu/gcc/gcc-4.1.2/gcc-4.1.2.tar.bz2

安装依赖

1
sudo apt-get install libc6-dev libgmp-dev libmpfr-dev texinfo
1
2
3
4
5
6
7
8
9
cd /usr/include
sudo ln -s x86_64-linux-gnu/bits bits
sudo ln -s x86_64-linux-gnu/gnu gnu
sudo ln -s x86_64-linux-gnu/sys sys
sudo ln -s x86_64-linux-gnu/asm asm
cd /usr/lib
sudo ln -s x86_64-linux-gnu/crt1.o crt1.o
sudo ln -s x86_64-linux-gnu/crti.o crti.o
sudo ln -s x86_64-linux-gnu/crtn.o crtn.o

编译GCC

此处前半部分尝试使用ubuntu18.04 gcc7.x编译,后来问题很多改用ubuntu14.04 gcc4.8.4编译通过。

1
2
3
4
5
$ cd gcc-4.0.0
$ mkdir build
$ cd build
$ ../configure --prefix=/usr/local --program-prefix=sse- --libexecdir=/usr/local/lib --enable-shared --enable-threads=posix --enable-__cxa_atexit --enable-clocale=gnu --disable-multilib --enable-languages=c,c++
$ make bootstrap MAKEINFO=makeinfo
1
2
3
4
../../gcc/toplev.c:547:1: error: redefinition of ‘floor_log2’
 floor_log2 (unsigned HOST_WIDE_INT x)
 ../../gcc/toplev.c:582:1: error: redefinition of ‘exact_log2’
 exact_log2 (unsigned HOST_WIDE_INT x)

https://stackoverflow.com/questions/34569458/error-occurred-compiling-gcc-from-source-code

修改Makefile

1
2
CC = gcc -fgnu89-inline
CXX = g++ -fgnu89-inline

Make再次出错

1
2
3
4
In function ‘open’,
    inlined from ‘collect_execute’ at ../../gcc/collect2.c:1577:20:
/usr/include/x86_64-linux-gnu/bits/fcntl2.h:50:4: error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments
    __open_missing_mode ();
1
2
3
 ~/project/gcc-4.0.0/gcc > vim collect2.c +1577

redir_handle = open (redir, O_WRONLY | O_TRUNC | O_CREAT, 0777); 

(后续还有好多问题,暂时放弃。。

折回原位,选用ubuntu14.04,自带gcc版本4.8.4,按照上面的修改Makefile两行即可直接编译一遍过。

1
2
3
sudo make install
sudo ln -sf /usr/local/bin/x86_64-unknown-linux-gnu-gcc-4.1.2 /usr/bin/gcc
sudo ln -sf /usr/local/bin/x86_64-unknown-linux-gnu-sse-g++ /usr/bin/g++

验证安装

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
ruokeqx@ruokeqx:~$ gcc --version
gcc (GCC) 4.1.2
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ruokeqx@ruokeqx:~$ g++ --version
g++ (GCC) 4.1.2
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

ruokeqx@ruokeqx:~$ 

编译linux-2.4

此前已经clone好2.4的源码,并已经在编译gcc时创建部分软链接

下面部分代码创建软链接在我看的教程中说是确保这些链接是指向要升级的内核源代码(他事先删除了这个目录自带的),我的系统刚装没有动过就直接用自带的 linuxscsi的目录了(asm好像是上面编译gcc时创建的软链接)

1
2
3
4
5
6
7
8
# 四舍五入这里用不到(doge
cd /usr/include
# rm -r asm linux scsi
# 这里软链接还是用之前编译gcc用到的asm,下面这条编译会报错没有asm/boot.h
# sudo ln -s /usr/src/linux-headers-4.4.0-142/include/asm-generic asm
# 这里下面两个系统自带了
# sudo ln -s /usr/src/linux-headers-4.4.0-142/include/linux linux
# sudo ln -s /usr/src/linux-headers-4.4.0-142/include/scsi scsi

配置内核,这部分还是特别重要且相对麻烦的 [具体看链接8],但是由于与本次实验无关,所以直接使用默认值了。

1
2
3
4
make config		#(基于文本的最为传统的配置界面,不推荐使用)
make menuconfig	#(基于文本选单的配置界面,字符终端下推荐使用)
make xconfig	#(基于图形窗口模式的配置界面,Xwindow下推荐使用)
make oldconfig	#(如果只想在原来内核配置的基础上修改一些小地方,会省去不少麻烦)

编译

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 读取配置生成的配置文件 来创建对应于配置的依赖关系树
make dep
# 删除前面步骤留下的文件
make clean
# 完全编译内核
make bzImage
# 后面三个命令只有在你进行配置的过程中,在回答Enable loadable module support (CONFIG_MODULES)时选了"Yes"才是必要的,默认是[*] built-in
# make modules和make modules_install分别生成相应的模块和把模块拷贝到需要的目录中
make modules
sudo make modules_install
# 生成模块间的依赖关系,启动新内核之后,使用modprobe命令加载模块时就能正确地定位模块
depmod -a

编译时报错如下,变量未声明,github搜变量名看到值都是0X7F00,于是编辑源码加上一句,编译成功。

1
2
3
4
tools/build.c:155:error: ‘DEF_SYSSIZE’ undeclared (first use in this function)

vim [source path]/arch/x86_64/boot/tools/build.c +37
#define DEF_SYSSIZE 0x7F00

编译成功(也只能说仅仅是编译成功了,具体继续往下看),文件路径为

1
2
[source path]/System.map
[source path]/arch/x86_64/boot/bzImage

qemu内核运行及调试

安装qemu

1
2
cp linux-2.4/arch/x86_64/boot/bzImage ./
sudo apt install qemu

制作initrd(initial ramdisk), 在内核启动的时候会先去加载的一种文件系统[见参考14]

[If you have Linux Kernel 2.4 or later, you already have support of ramdisk built in.] kernel2.4刚好是支持ramdisk的最早版本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>

int main()
{
        printf("hello ruokeqx");
        printf("hello ruokeqx");
        printf("hello ruokeqx");
        fflush(stdout);
        while(1);
        return 0;
}

静态编译并运行

1
2
gcc -static -o helloworld hello.c
echo helloworld | cpio -o --format=newc > rootfs

常规操作应该是使用busybox:[嵌入式linux的瑞士军刀,包含基本命令] 来构建文件系统,然后再qemu运行。

但是 qemu-kernel-initrd能够绕过bootload直接对指定的kernelramdisk进行加载,上面echo那行命令就是创建cpio-initrd的过程。

运行中的亿点问题

1
2
3
4
5
6
> qemu-system-x86_64 -kernel ./bzImage -initrd ./rootfs -append "root=/dev/ram rdinit=/helloworld"
# 报错如下
RAMDISK: Couldn't find valid RAM disk image starting at 0.
VFS: Cannot open root device "ram" or 08:01
Please append a correct "root=" boot option
Kernel panic: VFS: Unable to mount root fs on 08:01

发现我的系统没有/dev/ram,查找/dev下设备文件意义得到如下

1 block   RAM disk   [已过时,请用TMPFS]
          0 = /dev/ram0        第1个 RAM disk
          1 = /dev/ram1        第2个 RAM disk
[说明]将/dev/ram0用作initrd的做法已过时(因为它仅针对image-initrd格式),当下的主流是cpio-initrd格式。

以下目录被保留用于挂载特殊的文件系统。这些特殊的文件系统只提供内核接口而不提供标准的设备节点。
/dev/shm        tmpfs           提供对 POSIX 共享内存的直接访问

于是尝试

1
sudo qemu-system-x86_64 -kernel ./bzImage -initrd ./rootfs -append "root=/dev/shm rdinit=/helloworld"

还是相同报错,只不过上面的ram变成了shm.

尝试过换到ubuntu18.04,依旧是一堆报错

1
2
3
4
5
WARNING: Image format was not specified for 'rootfs' and probing guessed raw.
         Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
         Specify the 'raw' format explicitly to remove the restrictions.
         
#	-drive format=raw,file=rootfs

虚拟化问题

1
2
3
warning: TCG doesn't support requested feature: CPUID.01H:ECX.vmx [bit 5]

# 虚拟机处理器开启 虚拟化 Intel VT-x/EPT 或 AMD-V/RVI(V)

制作磁盘镜像

1
2
3
4
5
6
# qemu-img 创建一个磁盘镜像文件
qemu-img create -f raw disk.raw 512M
# 格式化为ext4
mkfs -t ext4 ./disk.raw
# 挂在磁盘镜像文件,这样就可以操作磁盘镜像文件中的内容了
sudo mount -o loop ./disk.raw ./img
1
2
3
4
Kernel panic: Attempted to kill the idle task!
In idle task - not syncing

Kernel panic: Attempted to kill init!

放弃initrd运行kernel2.4

一堆问题,自闭了。

看了一道朋友博客的kernelpwn题,拿到附件用他的环境尝试运行我的程序,发现成功。步骤如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# 现成内核和rootfs.cpio运行编译好的程序helloworld
ls
babydriver.i64  babydriver.tar.xz  boot.sh  bzImage  rootfs.cpio
mkdir tmp
cp rootfs.cpio ./tmp/rootfs.cpio.gz
cd tmp/
gunzip ./rootfs.cpio.gz
cpio -idmv < rootfs.cpio
....
ls
....
cp ../../helloworld ./
echo /helloworld > init
find . | cpio -o --format=newc > rootfs.cpio
mv rootfs.cpio ../
cd ..
./boot.sh

然后用他的bzImgae替换我的程序后成功运行

1
2
3
4
5
6
echo helloworld | cpio -o --format=newc > rootfs
cp kernelpwn/bzImage ./
qemu-system-x86_64 \
	-kernel ./bzImage \
	-initrd ./rootfs \
	-append "root=/dev/ram rdinit=/helloworld"

image-20211126155106914

那么现在可以确定是我编译的内核有问题(

看了https://wiki.debian.org/Initrd后,发现kernel2.4只支持block-disk image,不支持cpio archive,需要用mkinitrd创建镜像,而ubuntu14.04都只自带initramfs而没mkinitrd,似乎连apt源中都移除了initrd-tools;好像也跟发行版有关系,centos7中还使用mkinitrd.

Kernel up to 2.4 were expecting the initrd file to be a (compressed) block-disk image (i.e formated as ext2 or minix fs). the initrd was created using mkinitrd.
Kernel 2.6 is expecting the initrd file to be a (compressed) cpio archive, to be uncompressed in a ramdisk, known as initramfs.

至少可以确定的是在debian系近十年的发行版中编译并用initrd运行kernel2.4几乎是不可能的(

尝试使用busybox

1
2
3
4
5
6
7
qemu-img create -f raw disk.raw 512M
mkfs -t ext2 ./disk.raw
mkdir img
sudo mount -o loop ./disk.raw ./img
# 刚才kernelpwn的题目自带的busybox直接拷贝过来
sudo cp -r tmp/* ./img/
qemu-system-x86_64 -m 512M -smp 4 -kernel bzImage.2.4 -drive format=raw,file=./disk.raw --append "init=/linuxrc root=/dev/sda"

kernel2.4仍然报错

reiserfs_read_super can not find reiserfs on ramdisk(1,0)


Kernel panic: Attempted to kill init!

尝试kernel2.6(这里的镜像是后面initrd运行kernel2.6编译的)

1
2
3
4
5
qemu-system-x86_64 -m 512M -smp 4 -kernel bzImage.2.6 -drive format=raw,file=./disk.raw --append "init=/linuxrc root=/dev/sda"

can't open /dev/tty2: No such file or directory
can't open /dev/tty3: No such file or directory
can't open /dev/tty4: No such file or directory

busybox init启动后会扫描/etc/inittab ,没有的话就用下面的默认配置,所以会tty报错

1
2
3
4
5
6
7
8
9
::sysinit:/etc/init.d/rcS
::askfirst:/bin/sh
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
tty2::askfirst:/bin/sh
tty3::askfirst:/bin/sh
tty4::askfirst:/bin/sh

我们创建文件内容

1
2
3
4
5
6
7
8
9
# /etc/inittab
::sysinit:/etc/init.d/rcS
::askfirst:/bin/ash
::ctrlaltdel:/sbin/reboot
::shutdown:/sbin/swapoff -a
::shutdown:/bin/umount -a -r
::restart:/sbin/init
# /etc/init.d/rcS
#!/bin/sh

kernel2.6成功运行

image-20211126203526682

尝试initrd运行kernel2.6

gcc换回4.8.4直接一遍编译成功

1
2
3
4
5
6
make menuconfig
make
make bzImage
cp arch/x86_64/boot/bzImage ./
cp bzImage ../bzImage2.6
qemu-system-x86_64 -kernel ./bzImage.2.6 -initrd ./rootfs -append "root=/dev/shm rdinit=/helloworld"

image-20211126192047362

编写shell脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/sh

cd linux-5.15.6
if [ $1 ]; then
	make -j12
fi
# make bzImage
cp arch/x86/boot/bzImage ../
cd ..

gcc -static -o fs/ruokeqx check_syscall.c -g
#gcc -static -o fs/ruokeqx cap.c -g

cd fs
find . -print0 | cpio --null -ov --format=newc | gzip -9 > ../init.cpio.gz
# find ruokeqx | cpio -o --format=newc > rootfs.cpio
cd ..

qemu-system-x86_64 \
	-kernel bzImage  \
	-initrd init.cpio.gz \
	-nographic \
	-S -s \
	-net nic \
	-net tap,ifname=tap0,script=no,downscript=no \
	-append "console=ttyS0 nokaslr" \
	# -S -s # gdbserver
	#-append "console=ttyS0 rdinit=/ruokeqx"

内核源码阅读

1
2
3
4
5
6
7
8
9
# linux-5.9/include/uapi/linux/if_ether.h
struct ethhdr {} __attribute__((packed));
# linux-5.9/include/uapi/linux/ip.h
struct iphdr {};
# linux-5.9/include/uapi/linux/if_arp.h
struct arphdr {};
# linux-5.9/net/socket.c
int __sys_recvfrom(int fd, void __user *ubuf, size_t size, unsigned int flags,
		   struct sockaddr __user *addr, int __user *addr_len)

添加系统调用

要添加系统调用大体需要三步
1.先在arch/x86/entry/syscalls/syscall_64.tbl中分配调用号
2.在include/linux/syscalls.h定义系统调用
3.在内核任意区域添加SYSCALL_DEFINE并给出具体实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 系统调用表
// linux-5.9/arch/x86/entry/syscalls/syscall_64.tbl
// # The format is:
// # <number> <abi> <name> <entry point>
// #    45	   64  recvfrom sys_recvfrom
// 系统调用定义文件
// linux-5.9/kernel/sys.c
// 服务例程的原型声明
// linux-5.9/include/linux/syscalls.h
/* net/socket.c */
asmlinkage long sys_socket(int, int, int);
asmlinkage long sys_socketpair(int, int, int, int __user *);
asmlinkage long sys_bind(int, struct sockaddr __user *, int);
asmlinkage long sys_listen(int, int);
asmlinkage long sys_accept(int, struct sockaddr __user *, int __user *);
asmlinkage long sys_connect(int, struct sockaddr __user *, int);
asmlinkage long sys_getsockname(int, struct sockaddr __user *, int __user *);
asmlinkage long sys_getpeername(int, struct sockaddr __user *, int __user *);
asmlinkage long sys_sendto(int, void __user *, size_t, unsigned,
				struct sockaddr __user *, int);
asmlinkage long sys_recvfrom(int, void __user *, size_t, unsigned,
				struct sockaddr __user *, int __user *);
asmlinkage long sys_setsockopt(int fd, int level, int optname,
				char __user *optval, int optlen);
asmlinkage long sys_getsockopt(int fd, int level, int optname,
				char __user *optval, int __user *optlen);
asmlinkage long sys_shutdown(int, int);
asmlinkage long sys_sendmsg(int fd, struct user_msghdr __user *msg, unsigned flags);
asmlinkage long sys_recvmsg(int fd, struct user_msghdr __user *msg, unsigned flags);

注意点

kernel
不同的函数
printk()
kmalloc(size,mem_type)
kfree()

for(int i=0;i<10;++i)的写法是错的 c语言版本不支持在for中定义


查看内核日志
sudo dmesg

跟踪程序系统调用
sudo strace sudo ./check_syscall

源码

net/socket.c

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
#include <linux/if_ether.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <linux/udp.h>
#include <linux/if_arp.h>
// #include <stdio.h>
// #include <stdlib.h>



//参考内核 struct ethhdr 定义 (include/uapi/linux/if_ether.h)
//参考内核 struct iphdr 定义(include/uapi/linux/ip.h)
//参考内核 struct tcphdr 定义(include/uapi/linux/tcp.h)
//参考内核 struct udphdr 定义(include/uapi/linux/udp.h)
//参考内核 struct arphdr 定义(include/uapi/linux/if_arp.h)
struct struct_packet {
	unsigned char	mac_dest[ETH_ALEN];
	unsigned char	mac_source[ETH_ALEN];
	__be16		eth_proto;		/* packet type ID field	*/
	__be32	ip_source;
	__be32	ip_dest;
	// tools/include/uapi/linux/in.h
	// 1icmp/2igmp/6tcp/17udp
	__u8	ip_proto;
	__be16	port_source;
	__be16	port_dest;
};


// unsigned int htonl(unsigned int	x)
// {
// 	unsigned int  y;
// 	((unsigned char*)&y)[0]= ((unsigned char*)&x)[3];
// 	((unsigned char*)&y)[1]= ((unsigned char*)&x)[2];
// 	((unsigned char*)&y)[2]= ((unsigned char*)&x)[1];
// 	((unsigned char*)&y)[3]= ((unsigned char*)&x)[0];
// 	return y;
// }
// unsigned short htons(unsigned short x)
// {
// 	unsigned short y;
// 	((unsigned char*)&y)[0]= ((unsigned char*)&x)[1];
// 	((unsigned char*)&y)[1]= ((unsigned char*)&x)[0];
// 	return y;
// }



/*
	char * memp pointer for data
	int caplen cap number of packet
	return -1 on error 0 for success
*/
int __sys_cap(char * memp, int caplen)
{
#ifndef BUFFER_MAX
#define BUFFER_MAX 4096
#endif
	int len,i;
	int	sock;
	// https://www.kernel.org/doc/htmldocs/kernel-api/API-kmalloc.html
	// https://www.kernel.org/doc/htmldocs/kernel-api/API-kfree.html
	char * buffer = kmalloc(BUFFER_MAX, GFP_KERNEL);

	struct struct_packet *s_packet;
	struct ethhdr *mac_hdr;
	struct iphdr *ip_hdr;
	struct tcphdr *tcp_hdr;
	struct udphdr *udp_hdr;
	struct arphdr *arp_hdr;

	if( (sock = __sys_socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL))) < 0 ){
		printk("Create socket error.\n");
		return -1;
		// exit(0);
	}

	while(caplen--){
		len = __sys_recvfrom(sock, buffer, BUFFER_MAX, 0, NULL, NULL);
		printk("len:%d",len);
		for(i=0;i<len;++i){
			printk("%02x ",(unsigned char)buffer[i]);
		}
		if (len < 46) {
			printk("Catch packet length error.\n" );
			__sys_shutdown(sock, 2);
			// close(sock);
			return -1;
			// exit(0);
		}

		// copy_to_user(memp, ,);
		// printk("截获内容长度 %d\n", len);


		// sizeof(struct ethhdr) = 14
		// sizeof(struct ipdhr) = 20
		// sizeof(struct arpdhr) = 28	6 4 Sender_ip 6 4 Target_ip
		mac_hdr = (struct ethhdr *)buffer;
		s_packet = (struct struct_packet *)mac_hdr;


		/*
		s_packet->mac_dest[0] = mac_hdr->h_dest[0];
		s_packet->mac_dest[1] = mac_hdr->h_dest[1];
		s_packet->mac_dest[2] = mac_hdr->h_dest[2];
		s_packet->mac_dest[3] = mac_hdr->h_dest[3];
		s_packet->mac_dest[4] = mac_hdr->h_dest[4];
		s_packet->mac_dest[5] = mac_hdr->h_dest[5];

		s_packet->mac_source[0] = mac_hdr->h_source[0];
		s_packet->mac_source[1] = mac_hdr->h_source[1];
		s_packet->mac_source[2] = mac_hdr->h_source[2];
		s_packet->mac_source[3] = mac_hdr->h_source[3];
		s_packet->mac_source[4] = mac_hdr->h_source[4];
		s_packet->mac_source[5] = mac_hdr->h_source[5];

		s_packet->eth_proto = mac_hdr->h_proto;
		*/
		switch(s_packet->eth_proto){
			case htons(0x0800):	// ip
				ip_hdr = (struct iphdr *)(buffer + sizeof(struct ethhdr));
				s_packet->ip_source = ip_hdr->saddr;
				s_packet->ip_dest = ip_hdr->daddr;
				// p = (char*)&ip_hdr->saddr;
				// printk("源 IP: %d.%d.%d.%d",
				// 		(u_char)p[0], 
				// 		(u_char)p[1],
				// 		(u_char)p[2],
				// 		(u_char)p[3]
				// 	);

				// p = (char*)&ip_hdr->daddr;
				// printk(" ==> 目的 IP: %d.%d.%d.%d\n",
				// 		(u_char)p[0], 
				// 		(u_char)p[1], 
				// 		(u_char)p[2],
				// 		(u_char)p[3]
				// 	);

				s_packet->ip_proto = ip_hdr->protocol;

				switch(s_packet->ip_proto) {
					case IPPROTO_ICMP:
						s_packet->port_source = 0;
						s_packet->port_dest = 0;
						break;
					case IPPROTO_IGMP:
						s_packet->port_source = 0;
						s_packet->port_dest = 0;
						break;
					case IPPROTO_IPIP:
						break;
					case IPPROTO_TCP:
						tcp_hdr = (struct tcphdr *)(buffer + sizeof(struct ethhdr) + sizeof(struct iphdr));
						s_packet->port_source = tcp_hdr->source;
						s_packet->port_dest = tcp_hdr->dest;
						break;
					case IPPROTO_UDP:
						udp_hdr = (struct udphdr *)(buffer + sizeof(struct ethhdr) + sizeof(struct iphdr));
						s_packet->port_source = udp_hdr->source;
						s_packet->port_dest = udp_hdr->dest;
						break;
					case IPPROTO_RAW:
						break;
					default:
						break;
				}
				// printk("协议类型:");
				// switch(ip_hdr->protocol) {
				// 	case IPPROTO_ICMP:
				// 		printk("ICMP");
				// 		break;
				// 	case IPPROTO_IGMP:
				// 		printk("IGMP"); 
				// 		break;
				// 	case IPPROTO_IPIP:
				// 		printk("IPIP"); 
				// 		break;
				// 	case IPPROTO_TCP:  
				// 		printk("TCP"); 
				// 		break;
				// 	case IPPROTO_UDP:  
				// 		printk("UDP"); 
				// 		break;
				// 	case IPPROTO_RAW: 
				// 		printk("RAW"); 
				// 		break;
				// 	default: 
				// 		printk("Unknown type"); 
				// 		break;
				// }
				break;
			case htons(0x0806): // arp
				arp_hdr = (struct arphdr *)(buffer + sizeof(struct ethhdr));

				// however #if 0 in kernel if_arp.h ip not compiled
				// RFC 826  ARP packets are variable in size
				// https://sites.uclouvain.be/SystInfo/usr/include/net/if_arp.h.html
				// s_packet->ip_source = (__be32 *)arp_hdr->ar_sip;

				// s_packet->ip_dest = (__be32 *)arp_hdr->ar_tip;

				s_packet->ip_source = 0;
				s_packet->ip_dest = 0;
				s_packet->ip_proto = 0;
				s_packet->port_source = 0;
				s_packet->port_dest = 0;
				break;
			default:
				s_packet->ip_source = 0;
				s_packet->ip_dest = 0;
				s_packet->ip_proto = 0;
				s_packet->port_source = 0;
				s_packet->port_dest = 0;
				break;
		}
		memcpy(memp, s_packet, sizeof(struct struct_packet));
		memp += sizeof(struct struct_packet);
	}

	kfree(buffer);
	// how 0 read/1 trans/2 read and trans
	__sys_shutdown(sock, 2);
	// close(sock);
	return 0;
}

SYSCALL_DEFINE2(cap, char * , memp , int, caplen)
{
	return __sys_cap(memp, caplen);
}



// arch/x86/entry/syscalls/syscall_64.tbl
// 335 64  cap             sys_cap

// include/linux/syscalls.h
// asmlinkage long sys_cap(FILE *f, int caplen)

check_syscall.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <linux/if_ether.h>
#include <linux/in.h>
#include <sys/syscall.h>
#include <unistd.h>
#define SYSCALL_CAP 335

struct struct_packet {
	unsigned char	mac_dest[ETH_ALEN];
	unsigned char	mac_source[ETH_ALEN];
	__be16		eth_proto;
	__be32	ip_source;
	__be32	ip_dest;
	__u8	ip_proto;
	__be16	port_source;
	__be16	port_dest;
};


int main()
{
    int caplen = 10;
    int memsize = caplen * sizeof(struct struct_packet);
    char * memp = malloc(memsize);
    int res = syscall(SYSCALL_CAP, memp, caplen);
    
    // int res = cap(memp, caplen);
    if (res){
        printf("error:%d\n",res);
        exit(1);
    }
    for (int i=0;i<10;++i){
        for (int y=0;y<10;++y){
            printf("%02x ",(unsigned char)memp++);
        }
        printf("\n");
    }
    return 0;
}

调试内核

前置:qemu及pwndbg的安装

gdb配合qemu调试 qemu参数-S -s启动gdbserver,gdb连上remote即可

1
2
3
qemu-system-x86_64 ... -S -s
gdb vmlinux
target remote:1234
1
2
3
4
5
6
7
int __sys_recvfrom(int fd, void __user *ubuf, size_t size, unsigned int flags,
		   struct sockaddr __user *addr, int __user *addr_len){
	err = import_single_range(READ, ubuf, size, &iov, &msg.msg_iter);
    // -14 kernel
    // 0 user
}
0xffff888005549000

gdb调内核发现内存写入失败

image-20211218215217714

image-20211218221607098

image-20211218223232407

网络环境

1
2
3
4
5
apt-get update
# 虚拟网桥工具
apt-get install bridge-utils
# UML(User-mode linux)工具
apt-get install uml-utilities

qemu默认支持的是e1000网卡,内核中需要开启如下的配置项来支持e1000

否则启动ifconfig -a只有一个lo设备

1
2
3
4
5
Device Drivers  --->
	[*] Network device support  --->
		[*]   Ethernet driver support  --->
			[*]   Intel devices
			<*>     Intel(R) PRO/1000 Gigabit Ethernet support
# 十六进制打印a开始的16个byte类型内存
x/16xb a

添加文件加密

dm-crypt/cryptsetup

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
➜  ~ cryptsetup benchmark
# Tests are approximate using memory only (no storage IO).
PBKDF2-sha1      1705001 iterations per second for 256-bit key
PBKDF2-sha256    2111935 iterations per second for 256-bit key
PBKDF2-sha512    1600879 iterations per second for 256-bit key
PBKDF2-ripemd160  894689 iterations per second for 256-bit key
PBKDF2-whirlpool  661979 iterations per second for 256-bit key
argon2i       6 iterations, 1048576 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
argon2id      7 iterations, 1048576 memory, 4 parallel threads (CPUs) for 256-bit key (requested 2000 ms time)
#     Algorithm |       Key |      Encryption |      Decryption
        aes-cbc        128b      1068.9 MiB/s      2818.5 MiB/s
    serpent-cbc        128b        94.4 MiB/s       673.9 MiB/s
    twofish-cbc        128b       210.6 MiB/s       375.6 MiB/s
        aes-cbc        256b       846.1 MiB/s      2375.5 MiB/s
    serpent-cbc        256b        96.7 MiB/s       695.6 MiB/s
    twofish-cbc        256b       217.7 MiB/s       391.6 MiB/s
        aes-xts        256b      2632.3 MiB/s      2713.3 MiB/s
    serpent-xts        256b       673.6 MiB/s       687.9 MiB/s
    twofish-xts        256b       374.1 MiB/s       369.8 MiB/s
        aes-xts        512b      2248.3 MiB/s      2264.3 MiB/s
    serpent-xts        512b       685.4 MiB/s       693.5 MiB/s
    twofish-xts        512b       371.9 MiB/s       375.6 MiB/s

参考文献

[1]https://git.kernel.org/pub/scm/linux/kernel/git/wtarreau/linux-2.4.git/[source]
[2]https://kernel.googlesource.com/pub/scm/linux/kernel/git/wtarreau/linux-2.4.git/[source]
[3]https://github.com/torvalds/linux[kernel]
[4]https://linux.cn/article-9665-1.html[kernel make]
[5]https://www.linux.com/topic/desktop/how-compile-linux-kernel-0/[kernel make]
[6]https://blog.csdn.net/Magic_Ninja/article/details/89058323[gcc]
[7]https://stackoverflow.com/questions/34569458/error-occurred-compiling-gcc-from-source-code[gcc]
[8]https://www.cnblogs.com/evilzy/archive/2008/03/30/1130179.html[kernel]
[9]https://www.cnblogs.com/hellogc/p/7482066.html[qemu kernel std]
[10]https://busybox.net/about.html[busybox]
[11]https://blog.csdn.net/sinat_22597285/article/details/53783221?spm=1001.2014.3001.5501[qemu initrd]
[12]https://blog.csdn.net/sinat_36184075/article/details/71598834[/dev/*]
[13]https://fmyy.pro/2020/05/04/Kernel/Linux_Kernel_Pwn(%E4%B8%80)/#%E6%80%9D%E8%B7%AF[fmyy kernelpwn]
[14]https://wiki.debian.org/Initrd[2.4’s initrd/2.6’s initramfs]
[15]https://securitronlinux.com/debian-testing/compiling-and-installing-a-2-4-kernel-on-debian-gnu-linux-sarge/[make initrd 2.4/2.6]
[16]https://www.centennialsoftwaresolutions.com/post/5-minute-user-space-hello-world-arm-cross-compile-copy-to-a-rootfs-and-run-on-qemu[busybox initramfs]
[17]https://www.cnblogs.com/tsruixi/p/10777242.html [syscall hdu]
[18]https://zhuanlan.zhihu.com/p/445453676 [vscode kernel dbg]
[19]https://wzt.ac.cn/2021/05/28/QEMU-networking/ [qemu networking]
[20]https://blog.csdn.net/gengzhikui1992/article/details/103142848 [libpcap speed]
[21]https://program-think.medium.com/%E6%89%AB%E7%9B%B2-dm-crypt-%E5%A4%9A%E5%8A%9F%E8%83%BD-linux-%E7%A3%81%E7%9B%98%E5%8A%A0%E5%AF%86%E5%B7%A5%E5%85%B7-%E5%85%BC%E5%AE%B9-truecrypt-veracrypt-80a932ae3492[dm-crypt]
[22]https://www.cnblogs.com/pionice/p/13288942.html [os fs blog]
[23]https://github.com/744717164/OSsciByGZHU/blob/master/OSscienceDesign/OsDesignByGama.cpp [osfilesystem]
[24]https://blog.csdn.net/GerZhouGengCheng/article/details/106103039 [aes-256-cbc]
[25]https://blog.csdn.net/u012234115/article/details/72762045 [md5 sha256]
[26]https://www.runoob.com/cplusplus/cpp-files-streams.html [c++ fstream]

Share on

ruokeqx
WRITTEN BY
ruokeqx