操作系统调试环境

推荐vscode作为ide,毕竟是宇宙最强ide

然后需要搭建调试环境,得熟悉linux下常用命令

环境搭建

优先考虑原生ubuntu,如果割舍不了瘟到死,那么也可以使用win10后出现的wslg

具体参考win11开启linux图形子系统wslg

汇编器

安装nasm

sudo apt-get install nasm

关于汇编调试环境,可以参考这篇文章从零开始学汇编

Bochs硬件模拟

Bochs是一个x86硬件平台的开源模拟器。它可以模拟各种硬件的配置。Bochs模拟的是整个PC平台,包括I/O设备、内存和BIOS。更为有趣的是,甚至可以不使用PC硬件来运行Bochs。事实上,它可以在任何编译运行Bochs的平台上模拟x86硬件。通过改变配置,可以指定使用的CPU(386、486或者586),以及内存大小等。一句话,Bochs是电脑里的“PC”。根据需要,Bochs还可以模拟多台PC,此外,它甚至还有自己的电源按钮。

bochs2.7下载

  1. 安装依赖sudo apt-get install libx11-dev libc6-dev build-essential xorg-dev libgtk2.0-dev libreadline-dev
  2. 生成配置./configure --with-x11 --with-x --enable-all-optimizations --enable-readline --enable-debugger-gui --enable-x86-debugger --enable-a20-pin --enable-fast-function-calls --enable-debugger
  3. 编译安装make -j4&&sudo make install

mac下bochs手动编译


#安装依赖
brew install gcc sdl gtk+ libxrandr
brew remove bochs
#下载bochs源码
wget https://udomain.dl.sourceforge.net/project/bochs/bochs/2.7/bochs-2.7.tar.gz
#下载有点慢,可以自己下载
tar bochs-2.7.tar.gz
cd bochs-2.7
./configure --enable-ne2000 \
--enable-all-optimizations \
--enable-cpu-level=6 \
--enable-x86-64 \
--enable-x86-debugger \
--enable-vmx=2 \
--enable-pci \
--enable-usb \
--enable-usb-ohci \
--enable-e1000 \
--enable-debugger \
--disable-debugger-gui \
--with-sdl2

make -j8&&make install

QEMU处理器模拟

QEMU是一套由法布里斯·贝拉(Fabrice Bellard)所编写的以GPL许可证分发源码的模拟处理器软件,在GNU/Linux平台上使用广泛。

Bochs,PearPC等与其类似,但不具备其许多特性,比如高速度及跨平台的特性,通过KQEMU这个闭源的加速器,QEMU能模拟至接近真实电脑的速度。

0.9.1及之前版本的qemu可以使用kqemu加速器。在qemu1.0之后的版本,都无法使用kqemu,主要利用qemu-kvm加速模块,并且加速效果以及稳定性明显比kqemu好。

在ubuntu 16.04中,安装qemu只需要sudo apt-get install qemu即可安装qemu相关所有软件(进程级的虚拟机qemu-ARCH,系统级的虚拟机qemu-system-ARCH,以及qemu工具比如qemu-img,其中ARCH对应架构名)

升级到20.04后,qemu的发展导致qemu需要分几个包来安装,按需安装。

  1. 如果需要系统级别的虚拟机,就是sudo apt-get install qemu-system-mips qemu-system-arm qemu-system-misc qemu-system-ppc qemu-system-sparc qemu-system-x86
  2. 如果需要进程级别的虚拟机,就是sudo apt-get install qemu qemu-user
  3. 如果需要qemu的一些工具比如qemu-img,就是sudo apt-get install qemu-utils qemu-kvm

sudo apt-get install qemu qemu-user qemu-utils qemu-kvm qemu-system-mips qemu-system-arm qemu-system-misc qemu-system-ppc qemu-system-sparc qemu-system-x86 gcc-multilib g++-multilib gdb-multilib

VsCode调试IDE

vscode配置里面有用的变量如下

${workspaceRoot} 当前打开的文件夹的绝对路径+文件夹的名字

${workspaceRootFolderName} 当前打开的文件夹的名字

${file} 当前打开正在编辑的文件名,包括绝对路径,文件名,文件后缀名

${relativeFile} 从当前打开的文件夹到当前打开的文件的路径

${fileBasename} 当前打开的文件名+后缀名,不包括路径

${fileBasenameNoExtension} 当前打开的文件的文件名,不包括路径和后缀名

${fileDirname} 当前打开的文件所在的绝对路径,不包括文件名

${fileExtname} 当前打开的文件的后缀名

${cwd} the task runner's current working directory on startup

${lineNumber} 当前打开的文件,光标所在的行数

如下配置task.json。这里调用了make指令,makefile预置了参数判断,所以添加了自定义参数debug

{
"version": "2.0.0",
"tasks": [
{
"type": "cppbuild",
"label": "makedebug",
"command": "make",
"args": [
"debug=1",
],
"options": {
"cwd": "${workspaceFolder}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
//"detail": "编译器: /usr/bin/g++"
},
{
"type": "cppbuild",
"label": "makerelease",
"command": "make",
"args": [
//"debug",
],
"options": {
//"cwd": "${fileDirname}"
},
"problemMatcher": [
"$gcc"
],
"group": {
"kind": "build",
"isDefault": true
},
//"detail": "编译器: /usr/bin/g++"
}
]
}

调试配置launch.json如下。这里设置了环境变量LD_LIBRARY_PATH

首先在远程机上使用gdbserver IP:PORT EXE开启调试服务。qemu调试模式会自动开启这个

然后在本地机器设置miDebuggerServerAddress远程调试地址,program填上需要调试地内核

其实就相当于gdb下的调试指令target remote IP:PORT

{
"version": "0.2.0",
"configurations": [
{
"name": "gdbtest",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/hd.img",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/output",
"environment": [
{"name": "LD_LIBRARY_PATH","value": "./"}
],
"externalConsole": false,
"MIMode": "gdb",
"miDebuggerServerAddress": "127.0.0.1:1234",
"setupCommands": [
{
"description": "为 gdb 启用整齐打印",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "将反汇编风格设置为 Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
],
"preLaunchTask": "makedebug"
}

]
}

接着,就可以F5愉快地调试了,慢着!系统默认的GDB在调试内核时会出现“Remote ‘g’ packet reply is too long”的错误

网上对此说法多数聚焦到一个问题:cpu arch的问题,导致register读取了超长的信息,但解决方案莫衷一是。

经过分析,是因为内核架构和cpu架构不一样,gdb默认采用内核的架构,而远程内核在cpu上运行后架构变成了cpu的架构,在target remote时就匹配不上了

所以,解决方法有两个,一个是内核和cpu要匹配,32位cpu(比如qemu-system-i386)运行32位内核,64位cpu(比如qemu-system-x86_64)运行64位内核

另一个方式,手动指定gdb客户端架构,不默认采用内核架构,通过在gdb环境中中输入指令set architecture i386:x86-64:intel设置arch,或者在gdb命令行里添加参数-gdb-set architecture i386:x86-64

在vscode的调试控制台可以输入gdb指令查看调试信息,需要在gdb指令之前加上 -exec

具体指令可以参考从零开始学gdb

工具学习

汇编语言学习

学习汇编有利于理解c语言本质,也是研究内核必备

同时可用户逆向领域,比如病毒与反病毒,外挂与反外挂,破解与反破解

从零开始学汇编

编译器gcc

我们需要编译用c写的源文件,linux下使用大名鼎鼎的gcc

从零开始学gcc

调试器gdb

gdb是一款优秀的linux调试工具,可以帮我们分析代码,反汇编代码

从零开始学gdb

makefile工程配置

操作系统会有许多源代码,我们使用makefile来建立工程结构

从零开始学makefile

制作虚拟磁盘

bximage创建磁盘

使用bximage可制作虚拟硬盘。bximage -q -hd=16 -func=create -sectsize=512 -imgmode=flat $(BUILD)/$(HD_IMG_NAME)

也可不用一次键入一整条语句,直接在命令行中键入“bximage”,然后根据bximage程序的提示制作镜像。

在命令行中的bximage命令的第一个参数:hd 硬盘镜像 fd 软盘镜像

如果选择硬盘镜像:第二个参数:flat、sparse或growing,默认flat 第三个参数键入硬盘大小(MB) 第四个参数键入镜像文件名称

如果选择软盘镜像:第二个参数选择软盘尺寸(英寸),默认1.44 第三个参数键入镜像文件名称

dd写入磁盘

if是输入文件,由代码汇编生成的二进制文件;of是输出文件,即上一步生成的虚拟磁盘。

如果只有bootsect一个程序,键入以下这行代码即可。

dd if=boot.bin of=a.img bs=512 count=1 conv=notrunc

Linux dd 命令用于读取、转换并输出数据。可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出。

参数 说明
if=文件名 输入文件名,默认为标准输入。即指定源文件。
of=文件名 输出文件名,默认为标准输出。即指定目的文件。
ibs=bytes 一次读入bytes个字节,即指定一个块大小为bytes个字节。
obs=bytes 一次输出bytes个字节,即指定一个块大小为bytes个字节。
bs=bytes 同时设置读入/输出的块大小为bytes个字节。
cbs=bytes 一次转换bytes个字节,即指定转换缓冲区大小。
skip=blocks 从输入文件开头跳过blocks个块后再开始复制。
seek=blocks 从输出文件开头跳过blocks个块后再开始复制。
count=blocks 仅拷贝blocks个块,块大小等于ibs指定的字节数。
conv=<关键字> 关键字可以有以下11种
关键字 说明
conversion 用指定的参数转换文件。
ascii 转换ebcdic为ascii
ebcdic 转换ascii为ebcdic
ibm 转换ascii为alternate ebcdic
block 把每一行转换为长度为cbs,不足部分用空格填充
unblock 使每一行的长度都为cbs,不足部分用空格填充
lcase 把大写字符转换为小写字符
ucase 把小写字符转换为大写字符
swap 交换输入的每对字节
noerror 出错时不停止
notrunc 不截短输出文件
sync 将每个输入块填充到ibs个字节,不足部分用空(NUL)字符补齐。
#在Linux 下制作启动盘
dd if=boot.img of=/dev/fd0 bs=1440k

#将testfile文件中的所有英文字母转换为大写,然后转成为testfile_1文件
dd if=testfile_2 of=testfile_1 conv=ucase

#由标准输入设备读入字符串,并将字符串转换成大写后,再输出到标准输出设备
dd conv=ucase

注意事项

写入到mbr的二进制文件,不能是elf格式,只能使用nasm编译出来的纯bin格式

bochsrc

bochs -q生成运行配置

主要需要改

  • display_library:x,options="gui_debug",开启gui调式
  • floppya: image="a.img", status=inserted,装入软盘
  • magic_break:enabled = 1, 开启魔术断点(汇编中插入断点语法xchg bx,bx)
# configuration file generated by Bochs
plugin_ctrl: unmapped=true, biosdev=true, speaker=true, extfpuirq=true, parallel=true, serial=true
config_interface: textconfig

# display_library: x
# gdbstub: enabled=1, port=1234, text_base=0, data_base=0, bss_base=0

# 这两个一起开启
magic_break: enabled=1
display_library: x, options="gui_debug"

memory: host=32, guest=32
romimage: file="/usr/local/share/bochs/BIOS-bochs-latest", address=0x00000000, options=none
vgaromimage: file="/usr/local/share/bochs/VGABIOS-lgpl-latest"
boot: disk
floppy_bootsig_check: disabled=0
# no floppya
floppya: image="a.img", status=inserted
# no floppyb
ata0: enabled=true, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="./build/hd.img", mode=flat
ata0-slave: type=none
ata1: enabled=true, ioaddr1=0x170, ioaddr2=0x370, irq=15
ata1-master: type=none
ata1-slave: type=none
ata2: enabled=false
ata3: enabled=false
optromimage1: file=none
optromimage2: file=none
optromimage3: file=none
optromimage4: file=none
optramimage1: file=none
optramimage2: file=none
optramimage3: file=none
optramimage4: file=none
pci: enabled=1, chipset=i440fx
vga: extension=vbe, update_freq=5, realtime=1
cpu: count=1, ips=4000000, model=bx_generic, reset_on_triple_fault=1, cpuid_limit_winnt=0, ignore_bad_msrs=1, mwait_is_nop=0
cpuid: level=6, stepping=3, model=3, family=6, vendor_string="GenuineIntel", brand_string=" Intel(R) Pentium(R) 4 CPU "
cpuid: mmx=true, apic=xapic, simd=sse2, sse4a=false, misaligned_sse=false, sep=true
cpuid: movbe=false, adx=false, aes=false, sha=false, xsave=false, xsaveopt=false, smep=false
cpuid: smap=false, mwait=true
print_timestamps: enabled=0
# no gdb stub
port_e9_hack: enabled=0
private_colormap: enabled=0
clock: sync=none, time0=local, rtc_sync=0
# no cmosimage
log: -
logprefix: %t%e%d
debug: action=ignore
info: action=report
error: action=report
panic: action=ask
keyboard: type=mf, serial_delay=250, paste_delay=100000, user_shortcut=none
mouse: type=ps2, enabled=false, toggle=ctrl+mbutton
speaker: enabled=true, mode=system
parport1: enabled=true, file=none
parport2: enabled=false
com1: enabled=true, mode=null
com2: enabled=false
com3: enabled=false
com4: enabled=false