专栏首页二进制漏洞研究linux内核启动过程分析

linux内核启动过程分析

gdb的基本调试命令

  • r(run) : 开始运行程序;
  • c(continue) : 继续运行一直到断点停止
  • b(break) : 设置程序断点;
  • p(print) : 打印出变量值;如 p var,会把var变量的值输出
  • s(step) : 单步跟踪,会进入函数内部
  • n(next) : 单步跟踪,不进入函数
  • finish : 跳出函数调试,并打印返回时的信息
  • u(until) : 跳出循环体
  • q(quit) : 退出gdb
  • l(list) : 显示当前行后面的源程序
  • bt (backtrace) : 查看堆栈信息
  • info : 查看各类gdb信息以及环境信息,比如:info break 可以查看断点信息
  • clear : 清除全部已定义的断点
  • delete : 删除指点的断点号,后面接断点号
  • gdb -tui main或者在启动gdb后,输入命令focus或layout: 能够在运行时间的同时显示代码

实验环境搭建

下载内核源代码编译内核

cd ~/LinuxKernel/
wget https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.18.6.tar.xz
xz -d linux-3.18.6.tar.xz
tar -xvf linux-3.18.6.tar
cd linux-3.18.6
make i386_defconfig
make # 一般要编译很长时间

制作根文件系统

cd ~/LinuxKernel/
mkdir rootfs
git clone https://github.com/mengning/menu.git  
cd menu
gcc -o init linktable.c menu.c test.c -m32 -static –lpthread
cd ../rootfs
cp ../menu/init ./
find . | cpio -o -Hnewc |gzip -9 > ../rootfs.img

启动MenuOS系统

cd ~/LinuxKernel/
qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img

重新配置编译Linux使之携带调试信息

  • 在原来配置的基础上,make menuconfig选中如下选项重新配置Linux,使之携带调试信息
  • kernel hacking—> [*] compile the kernel with debug info
  • make重新编译

使用gdb跟踪调试内核

qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img -s -S # 关于-s和-S选项的说明:
# -S freeze CPU at startup (use ’c’ to start execution)
# -s shorthand for -gdb tcp::1234 若不想使用1234端口,则可以使用-gdb tcp:xxxx来取代-s选项

使用gdb调试

gdb
(gdb)file linux-3.18.6/vmlinux # 在gdb界面中targe remote之前加载符号表
(gdb)target remote:1234 # 建立gdb和gdbserver之间的连接,按c 让qemu上的Linux继续运行
(gdb)break start_kernel # 断点的设置可以在target remote之前,也可以在之后

运行启动

调试代码并分析

总的流程图:

首先在start_kernel下断点,运行后,在断点停下:

set_task_stack_end_magic

start_kernel是内核启动阶段的入口,通过单步调试,可以发现它是linux内核执行的第一个init,我们单步进入看看它做了哪些操作:

其中end_of_stack在include/linux/sched.h中,它的意思是获取栈边界地址。然后把栈底地址设置为STACK_END_MAGIC,这个作为栈溢出的标记。STACK_END_MAGIC就是设置在thread_info结构的上面。比如如果你写了一个无限循环,导致栈使用不断增长了,那么,一旦把这个标记未修改了,就导致了栈溢出的错误

local_irq_disable函数

上面两条指令修改了中断寄存器中的IF标志位,sti是中断标志置1指令,使IF=1,cli是中断标志置0指令,使IF=0。所以这里native_irq_disable是关中断,屏蔽中断,接下来的操作就不允许中断对其产生影响。并设置标志位

early_boot_irqs_disabled = true;

page_address_init

初始化高端内存的映射表函数,高端内存是相对于低端内存而存在的,那么先要理解一下低端内存了。在32位的系统里,最多能访问的总内存是4G,其中3G空间给应用程序,而内核只占用1G的空间。因此,内核能映射的内存空间,只有1G大小,但实际上比这个还要小一些,大概是896M,另外128M空间是用来映射高端内存使用的。因此0到896M的内存空间,就叫做低端内存,而高于896M的内存,就叫高端内存了。如果系统是64位系统,当然就没未必要有高端内存存在了,因为64位有足够多的地址空间给内核使用,访问的内存可以达到10G都没有问题。在32位系统里,内核为了访问超过1G的物理内存空间,需要使用高端内存映射表。比如当内核需要读取1G的缓存数据时,就需要分配高端内存来使用,这样才可以管理起来。使用高端内存之后,32位的系统也可以访问达到64G内存。

linux_banner变量保存着linux内核的版本号:

trap_init函数

该函数作用是构建中断描述符表

set_intr_gate(X86_TRAP_DE, divide_error); //除零错误                
set_intr_gate_ist(X86_TRAP_NMI, &nmi, NMI_STACK);//不可屏蔽中断          
 /* int4 can be called from all */                          
set_system_intr_gate(X86_TRAP_OF, &overflow);//溢出              
set_intr_gate(X86_TRAP_BR, bounds);//边界检查错误                        
set_intr_gate(X86_TRAP_UD, invalid_op);//无效指令                               set_intr_gate(X86_TRAP_NM, device_not_available);//无效设备  
……

我们可以参照linux-0.11版本的内核来看这段代码:

Linux-0.11

#set_trap_gate(0,&divide_error)//除零错误
……
#define set_trap_gate(n,addr) \
_set_gate(&idt[n],15,0,addr)//0表示特权级

sched_init函数

对系统的调度机制进行初始化。先是对每个可用CPU上的runqueque进行初始化

然后初始化0号进程

用__sched_fork产生0号进程,并把0号进程的状态设置为TASK_RUNNING,设为系统的idle进程,即系统空闲时占据CPU进程

console_init函数

在窗口输出信息,之前的内存分配信息也打印出来了。

rest_init

这里具体函数分析见上面的流程图,这里主要是fork了一个新进程,并发生进程调度和切换。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • gdb 调试笔记

    和break命令非常相似。其参数可以是源代码行,函数名或者目标程序的某个地址,trace

    0x222进制
  • CVE-2017-1000112-UFO 学习总结

    今天看到有人发了CVE-2017-1000112-UFO的分析,就把之前的学习报告整理一下,做个对比学习。 别人的分析报告: https://securing...

    0x222进制
  • Return-to-libc

    (3)编译exploit.c文件,生成exploit文件,执行exploit,生成badfile文件

    0x222进制
  • MySQL指南之基础知识

    张风捷特烈
  • flask第32篇——模板赋值set

    用这种方法赋值,那么整个文件这个变量都是可见的,如果不想因为某个地方的赋值而污染全局变量,我们也可以进行局部赋值:

    用户2149234
  • 看完陆奇训话百度工程师的“五大信条”,无人车创业者头疼了?

    最近陆奇在百度内部Engineering Leadership Talk上的内部讲话被曝光,这是百度技术线员工的培训分享活动,陆奇给百度工程师提出了五条关于工作...

    罗超频道
  • 可以用 Python 编程语言做哪些神奇好玩的事情?

    可以画画啊!可以画画啊!可以画画啊! 对,有趣的事情需要讲三遍。 事情是这样的,通过python的深度学习算法包去训练计算机模仿世界名画的风格,然后应用到另一...

    小小科
  • MySQL添加用户、删除用户与授权

      mysql> insert into mysql.user(Host,User,Password) values("localhost","test",pa...

    Sindsun
  • MySQL添加用户、删除用户与授权

    1.新建用户   1.1 登录MYSQL:   @>mysql -u root -p   @>密码   1.2 创建用户:   mysql> CREATE US...

    Java中文社群_老王
  • 数据库MySQL-数据类型

    整形支持显示宽度,显示宽带是最小的显示位数,如int(11)表示整形最少用11位表示,如果不够位数用0填充。显示宽度默认不起作用,必须结合zerofill才起作...

    cwl_java

扫码关注云+社区

领取腾讯云代金券