情况
WSL(Ubuntu 22.04)
创建磁盘映像
可以使用fallocate为磁盘映像分配一块空间,或者使用dd if=/dev/zero of=$img bs=1M count=$size_in_MB直接得到一个大小为$size_in_MB大小的文件。
使用mkfs.ext4格式化映像文件,并使用mount -o loop $img mnt将文件挂载。
如果想要在磁盘映像中分区,则可以先使用fdisk或cfdisk对磁盘映像进行分区,然后使用losetup -fP $img将文件挂载为回环设备。这里-f参数表现自动寻找可以挂载的回环设备号,-P参数表现探测文件中的分区并分别挂载为回环设备。挂载为回环设备后,再使用mount $loop1 $mnt1等下令挂载回环设备。
构建busybox
下载busybox源码并构建,这里使用的是busybox-1.36.1版本
这里采用的构建选项有
构建静态文件:- Symbol: STATIC [=y]
- Prompt: Build static binary (no shared libs)
- Defined at Config.in:362
- Location:
- -> Settings
复制代码 这个版本默认支持了Unicode,可以不用更改- Symbol: UNICODE_SUPPORT [=y]
- Prompt: Support Unicode
- Defined at libbb/Config.in:311
- Location:
- -> Settings
复制代码 添加了Unicode宽字符支持- Symbol: UNICODE_WIDE_WCHARS [=y]
- Prompt: Allow wide Unicode characters on output
- Defined at libbb/Config.in:390
- Depends on: UNICODE_SUPPORT
- Location:
- -> Settings
- -> Support Unicode (UNICODE_SUPPORT [=y])
复制代码 其他构建选项均可以不更改
使用make构建后,再使用make install即可将完整的busybox、busybox符号链接等文件安装到busybox源码目次下的_install目次内。或者可以通过make install CONFIG_PREFIX=$install将busybox安装到指定目次中。好比这里我们可以使用make install CONFIG_PREFIX=$mnt将busybox安装到已经挂载的磁盘映像中。
构建Linux内核
下载Linux内核源码,这里使用Linux-6.12.7版本
根据自己喜好配置即可
创建rootfs
这里需要创建一个rootfs来作为Linux运行的情况。
查看busybox的安装目次可以发现,如今只有bin,sbin和usr三个目次和linuxrc一个符号链接。对比我们自己的Linux根目次可以发现,我们大概有以下目次- bin boot dev etc home lib mnt opt proc root run sbin sys tmp usr var
复制代码 那么我们在$mnt目次下创建这些目次即可。
由于mount需要sudo,$mnt目次下的文件很大概是root权限,后面一系列操作大概都需要root权限。
如今可以chroot到$mnt目次下试试能否使用shell。
运行假造机
这里我们使用qemu假造机。
将启动下令写成一个脚本- #!/bin/sh
- /usr/bin/qemu-system-x86_64\
- -kernel path/to/bzImage\
- -hda path/to/rootfs.img\
- -nographic\
- -append "console=ttyS0 root=/dev/sda init=/linuxrc"
复制代码
- -kernel选项表现设置Linux kernel为bzImage
- -hda选项表现选择磁盘映像
- -nographic表现不使用qemu窗口,而是将输出重定向到终端
- -append表现传递给Linux内核的参数
- console=ttyS0表现将输出重定向到串口设备ttyS0,这将使qemu将启动阶段的信息输出到终端
- root=/dev/sda表现根文件体系的位置,假造机中一般是sda
- init=linuxrc表现使用linuxrc作为init进程,也就是Linux下的第一个进程启动,这个linuxrc其实就是我们的busybox
启动配置
此时如果直接运行脚本启动假造机大概会报错,由于我们没有配置busybox作为init进程时的活动。
linuxrc会读取/etc/inittab文件,我们将该文件配置如下- ::sysinit:/etc/init.d/rcS
- ::respawn:-/bin/sh
- ::restart:/sbin/init
- ::ctrlaltdel:/sbin/reboot
- ::shutdown:/bin/umount -a -r
复制代码 该文件内每行有四个字段,格式为:::
- 指编号,不重复即可
- 指运行级别,可以不指定,指定时表现运行级别为n时激活改行的规则
- 包含一系列动作,表现对登记的在肯定条件下执行的动作
- 即要运行的进程,前面加上-表现以交互方式运行
包含以下动作
action含义respawn当process停止后立刻启动一个新的wait当进入指定的runlevels后process才会启动一次,而且到离开这个runlevels停止initdefault设定默认的运行级别,即我们开机之后默认进入的运行级别,不能是0,6,你懂的sysinit体系初始化,只有体系开机或重新启动的时候,这个process才会被执行一次powerwait当init吸收到电源失败信号的时候执行相应的process,而且如果init有进程在运行,会等候这个进程完成之后,再执行相应的processpowerfail当init吸收到电源失败信号的时候执行相应的process,而且如果init有进程在运行,不会等候这个进程完成,它会直接执行相应的processpowerokwait电源已经故障,但是在等候执行对应操作的时候突然来电了就执行对应的processpowerfailnow当电源故障而且init被通知UPS电源已经快耗尽执行相对应的processctrlaltdel当用户按下ctrl+alt+del这个组合键的时候执行对应的processboot只有在引导过程中,才执行该进程,但不等候该进程的竣事;当该进程死亡时,也不重新启动该进程bootwait只有在引导过程中,才执行该进程,并等候进程的竣事;当该进程死亡时,也不重新启动该进程off如果process正在运行,那么就发出一个警告信号,等候20秒后,再通过杀死信号强行停止该process。如果process并不存在那么就忽略该登记项once启动相应的进程,但不等候该进程竣事便继续处理/etc/inittab文件中的下一个登记项;当该进程死亡时,init也不重新启动该进程inittab第一行表如今体系启动时,运行/etc/init.d/rcS脚本里的内容。这也是没有inittab时linuxrc的默认动作。
接下来我们配置/etc/init.d/rcS脚本的内容- #!/bin/sh
- PATH=/sbin:/bin:/usr/sbin:/usr/bin:$PATH
- LD_LIBRARY_PATH=/lib:/usr/lib:$LD_LIBRARY_PATH
- runlevel=S
- umask 022
- export PATH LD_LIBRARY_PATH runlevel
- # devices
- mount -a
- mkdir /dev/pts
- mount -t devpts devpts /dev/pts
- mount -o remount,rw /
- mdev -s
复制代码 我们的脚本配置了情况变量,设备等,需要在体系启动时进行的配置,开启的服务,都可以在该文件中进行配置。
配置完成后肯定要赋予/etc/init.d/rcS运行权限,否则启动过程中会报错。
此时启动假造机可以看到,我们已经进入了shell。
其他配置文件
虽然我们的Linux已经正常启动,但是不要高兴的太早。
我们在shell中执行export PS1='\u@\h \W',重新登陆,我们预期会显示root@host ~,但是,这里并没有我们的用户名和主机名。
此时我们执行id和hostname下令会发现,我们如今虽然是uid=0 gid=0的用户,但是我们没有用户名,主机名也是(none)。执行ifconfig会发现,我们也没有可用网络。
接下来我们将进行这些方面的配置。
我们的Linux已经可以启动,而且busybox内置了vi作为编辑器,接下来的配置可以不通过宿主机,直接在假造机中完成。
用户配置
由于root用户本来就存在,我们不能用adduser创建用户,于是我们手动创建用户属性文件。
Linux通过辨认/etc/passwd中的用户来判断用户名,我们手动创建这个文件。
添加以下内容- root:x:0:0::/root:/bin/sh
复制代码 这个文件有7个字段,格式为::::::。
其中字段内容为加密后的密码,如果设为空则表现不需要密码也可以登录,如果为x表现密码存储在/etc/shadow文件中。
如果我们不创建/etc/shadow文件,passwd下令会将加密的密码存储在/etc/passwd中,所以我们打算创建一个/etc/passwd。
我们的Linux和busybox都支持剖析/etc/shadow文件,接下来我们手动创建这个文件。
添加以下内容这个文件内每行9个字段,格式为login:encyrptedpassword:lastchangedate:min_age:max_age:warning:inactivity:expiration_date:reserved,第一个字段为用户名,第二个字段为加密后的密码,如果为空会登录失败,为*或!时情况不确定,Linux console上写*和!表现没有密码,但现实测试后发现,为这两个符号时,busybox的login会提示bad salt。
后面的几个字段都与密码修改时间有关,分别为
- lastchange表现前次修改密码的日期的时间,如果该值为0,则表现用户下次登录时必须更改密码
- minage表现更改密码的间隔日期,为空或为0表现随时可以更改密码
- maxage表现必须更改密码的日期
- warning表如今密码到期前n天警告用户需要更改密码
- inactivity表现密码逾期后,n天内可以再更改密码
- expiration_date表现到期日期,到期后无法再登录
- reserved最后一个字段为保留字段
有这个文件后我们就可以使用passwd下令更改密码,然后再查看/etc/shadow可以发现密码已经改变了。
然后我们就可以通过登录的方式进入操作体系。
更改/etc/inittab如下- ::sysinit:/etc/init.d/rcS
- ::respawn:/sbin/getty -L console 0 vt100
- ::restart:/sbin/init
- ::ctrlaltdel:/sbin/reboot
- ::shutdown:/bin/umount -a -r
复制代码 这表现不直接打开一个shell,而是在console这个tty上打开一个login。
主机配置
一般我们将主机名写在/etc/hostname中,但是busybox不自动读取这个文件。
于是我们添加配置到/etc/init.d/rcS中- #!/bin/sh
- ...
- # hostname
- hostname -F /etc/hostname
复制代码 这代表从/etc/hostname加载主机名
网络配置
同样在/etc/init.d/rcS中添加以下配置- # network
- ifconfig lo up
- ifconfig eth0 192.168.1.100 netmask 255.255.255.0 up
- route add default gw 192.168.1.1 eth0
复制代码 ip地址随意填写,网关地址填写为qemu外部提供的网卡地址
网卡配置
在WSL中,需要创建一张假造网卡设备作为假造机的网关。
我们创建一张tap设备,向网卡配置脚本中写入以下内容- ip tuntap add dev tap0 mode tap
- ip link set dev tap0 up
- ip a add dev tap0 192.168.1.1/24
- iptables -t nat -A POSTROUTING -o eth0 -s 192.168.1.1/24 -j MASQUERADE
- echo 1 > /proc/sys/net/ipv4/ip_forward
复制代码 这个脚本创建了一张tap0网卡,并分配了ip地址192.168.1.1,就是我们的假造机的网关地址。
iptables下令创建了一条nat规则,将内部发出的源地址为192.168.1.0/24网段的数据包改为从eth0发出,这样就可以让假造机连接到外部网络了。
此时进入假造机,执行ping 192.168.1.1发现有网络连接。
然后执行cat nameserver 8.8.8.8 > /etc/resolv.conf配置域名剖析服务器。
此时执行ping www.baidu.com就可以ping通了。
由于busybox没有自带curl,执行echo -e "GET / HTTP/1.1\r\nHost:www.baidu.com\r\n\r\n" | nc www.baidu.com 80代替,可以收到html网页内容。
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |