从猫蛇之战再看内核戏 CPU

栏目: C · 发布时间: 7年前

内容简介:连续写了几天的代码,有些疲倦,吃过晚饭,换个工作方式,继续和大家聊猫蛇之战。蛇不仅丑陋,而且可能伤人害命,是邪恶的象征。猫与蛇战,代表着讨伐奸邪,是正义之战。猫与蛇战,技艺娴熟,举重若轻,叫人拍手叫绝,看着过瘾,想起也心情舒畅。

连续写了几天的代码,有些疲倦,吃过晚饭,换个工作方式,继续和大家聊猫蛇之战。

从猫蛇之战再看内核戏 CPU

蛇不仅丑陋,而且可能伤人害命,是邪恶的象征。猫与蛇战,代表着讨伐奸邪,是正义之战。猫与蛇战,技艺娴熟,举重若轻,叫人拍手叫绝,看着过瘾,想起也心情舒畅。

从猫蛇之战再看内核戏 CPU

儿时听过很多关于猫的故事,比如传说猫武艺高强,是老虎的老师,教老虎武功时,怕其背叛师门,反目攻击老师,就故意留了一项会爬树的技艺。因为此,老虎是不会爬树的。

再比如,家里吃面条时,不要把面条喂给猫,怕它没有面条吃时,去把蛇当作面条吃。在外面吃完了还好,如果叼回家里来吃就吓人了。

在搜索猫蛇之战的照片时,确实看到了猫吃蛇的照片,有点血腥。

线下版本的猫蛇战先聊到这,我们继续谈计算机世界的猫蛇之战。上回书说到调试器在访问内存时,会使用特殊的probe函数来访问,访问之前会禁止页错误。 但是很多问题还没有说透,比如:

Q1. 这样禁止了后,访问非法内存时,CPU硬件真的不报异常了么?

Q2. 如果要读很长一段内存,那么probe函数会访问一次发现不行就停了,还是像猫与蛇战那样连续作战呢?

Q3. probe函数发现不能访问时,会返回一个名为EFAULT的错误码(-14),它是怎么知道访问失败的呢?这个问题有点别扭,需要这样来体会。如果普通函数访问非法内存,一碰就爆炸了,这个函数立刻失去控制,根本没有机会知道刚才发生了什么。

为了以生动的方式(想想猫与蛇战,何其生动,应该感谢猫“类”啊)回答上面的问题,老雷想了个办法。

先写一点代码,调用一下probe函数,让它充当我们的“试验品”。

static voidge_probe(void)

{

char data[32] = {0};

long addr = 0x880;

long ret = probe_kernel_read(data, addr, sizeof(data));

printk("probe %lx got %ld\n", addr, ret);

}

封装一个简单的函数调用probe_kernel_read,故意指定0x880这个无效的线性地址。 0x880肯定无效么?肯定的,小于4K的地址都是无效的。

在proc虚文件的写回调函数里调用这个函数。

else if(strncmp(cmd, "probe", 5) == 0)

{

ge_probe();

}

再写一个不用probe的野人方法,作为对照。

else if(strncmp(cmd, "nullp", 5) == 0)

{

*(int*)(long)0x880 = 0x88888888;

}

把这点代码放到一个内核模块中,比如老雷常用的llaolao 模块。llaolao代表“ 刘姥姥”,取“刘姥姥进内核这个大观园”之意,感谢雪芹前辈,为我们的文化宝库增添了这样一个生动的角色。

尝试触发执行上面的两种方法,直接做非法访问时,系统大怒,CPU发出异常,向操作系统告状,操作系统追查叛逆,严惩不贷,当前的bash进程会被kill掉。

从猫蛇之战再看内核戏 CPU

而执行probe方法时,则风平浪静,一且安好。

为了能观察其中的细节,我们将使用KGDB双机内核调试,用强大的调试器做控制, 探微索隐

可能有看官说,还可以这样玩啊?是的,想想猫蛇之战,如果功夫不够,那可能被蛇吃掉的。对于今天的计算机来说, CPU在以光速奔跑, CPU执行一条指令的时间,光也只能行进几个厘米 。猫之所以敢与蛇正面 交锋,靠的是反应速度要比蛇快很多倍。而人的反应速度要比CPU慢不知道多少倍,如果不依靠调试器,怎么看的清楚?

Linus大神喜欢读源代码和加print,但那不是老雷的风格。

说话间,两个虚拟机都跑起来了,一个叫GE64,是调试目标,另一个叫GD64,跑调试器,二者通过虚拟串口通信,已经建立了内核调试会话。

从猫蛇之战再看内核戏 CPU

在目标机器中,编译加载llaolao模块,然后执行如下命令让目标机中断到调试器怀抱。

echo g > /proc/sysrq-trigger

目标机应声中断,在调试器中执行如下命令对处理页错误异常的do_page_fault函数设置一个断点。

从猫蛇之战再看内核戏 CPU

这样设置断点后,恢复执行,发现断点会频繁命中,没法继续玩下去。

为了避免这样的问题,要改变断点的设置方法,先对 do_page_fault函数做反汇编,找到访问cr2寄存器的地方。

(gdb) disassemble do_page_fault

Dump of assembler code for function do_page_fault:

=> 0xffffffff8106b650 <+0>:push   %rbp

0xffffffff8106b651 <+1>:mov    %rsp,%rbp

0xffffffff8106b654 <+4>:push   %r13

0xffffffff8106b656 <+6>:mov    %rsi,%r13

0xffffffff8106b659 <+9>:push   %r12

0xffffffff8106b65b <+11>:mov    %rdi,%r12

0xffffffff8106b65e <+14>:push   %rbx

0xffffffff8106b65f <+15>:mov    %cr2,%rax

0xffffffff8106b662 <+18>:nopl   0x0(%rax)

     然后对这个位置下断点 :  b *0xffffffff8106b662

还要再附加上一个条件: cond 2 $ax==0x880

告诉调试器,只有因为访问0x880触发页错误时才中断给我们看。

这样做好准备后,恢复目标执行,结果怎么样?

目标机还是无法操作,虽然在GDB中指定了条件,但是目标系统中一旦有页错误还是会中断到GDB,GDB判断条件不符合,立刻恢复执行,但是因为反复中断和恢复,目标机还是太慢了。

怎么办呢?修改内核源代码,在do_page_fault函数中插入几行代码,如下图所示。

从猫蛇之战再看内核戏 CPU

这样修改后,等一下就可以把断点设置在条件块内部了,那么就可以精准命中我们希望的条件,又不需要频繁中断到GDB了。

如此修改后,执行make bzImage以增量方式构建内核。

从猫蛇之战再看内核戏 CPU

有些看官可能又惊诧了,这么麻烦啊?

对于 Java 同行来说,重编内核可能是有点吓人。其实没那么可怕,特别是如果经常这么做的话,其实是很便捷的,修改代码,编译,编译好的复制到boot目录,以新换旧,一杯茶还没有喝好的功夫就搞定了。

使用新的内核重启目标系统,中断到GDB,反汇编 do_page_fault函数, 寻找新修改的代码地址:

从猫蛇之战再看内核戏 CPU

x86汇编很是浅显易懂,+22的位置是与0x880比较,+31的je指令是条件跳转,如果相等就跳到0xffffffff8106b686 。那么我们只要对这个 0xffffffff8106b686地址设断点就可以了。

b *0xffffffff8106b686

这样埋好断点后,恢复目标执行,目标系统活蹦乱跳,灵活自如了,不像刚才那样动弹不得。如此看来,能够重新编译内核真是好,可以在高特权的内核空间里安排自己的兵力。

闲言打住,在目标系统中,加载刘姥姥模块,然后执行如下命令触发调用probe动作:

echo probe > /proc/llaolao

断点如期命中,美哉GDB!

执行bt命令观察CPU的执行过程:

从猫蛇之战再看内核戏 CPU

上面一图值二两白银,在清代时可以买一块地。

这幅图的价值在于,它抓到了一个非常难以抓到的状态,把风驰电掣般飞奔的CPU“停”在了一个非常敏感的位置:因为有人违反系统规则,非要访问不可以访问的地址,CPU硬件发起异常,保存基本的位置信息后投到内核怀中上述。

上面截图中#16和#17中的信息其实就是CPU硬件压入栈的执行非法访问的“黑手”地址,即CS:RIP。0x10是内核代码段的段选择子,RIP指向copy_user_generic_string函数,它正是probe函数中调用的。

感谢强大的调试技术,它帮助我们把CPU停在我们希望仔细观察的地方,让我们可以细细体会,证实了我们的推理。君子戒慎乎其所不睹,恐惧乎其所不闻,亲眼目睹,亲手实践,何其重要也!

继续观察寄存器信息:

从猫蛇之战再看内核戏 CPU

可以看到,rax的值正是0x880,确认这次中断就是我们在llaolao驱动中通过probe函数访问0x880时导致的。

如此看来,问题1的答案有了,使用probe函数时,CPU还是会报异常的,CPU还是会进入到do_page_fault这个处理页错误的内核函数,有调试器抓到的现场为证,千真万确。

如此一来,除了前面提到的第二个和第三个问题,还有其它问题了,CPU在报告页错误时,会回滚状态,把程序指针回退到导致错误的那条指令,而调用probe函数时又能顺利返回到调用者,是谁悄悄调整了程序指针呢?

短文已经不短 ,不仅作者写的有点累了,大家读着可能也累了,就此打住,再找时间继续。顺便感谢转发和宣传上一篇文章的同行们,特别是 LINUX 圈里很有名气的陈老师也转载拙文,让笔者很受鼓舞。也感谢已经报名庐山研习班的格友们,期待与大家相会在庐山山南。

***********************************************************

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生。

欢迎关注格友公众号

从猫蛇之战再看内核戏 CPU


以上所述就是小编给大家介绍的《从猫蛇之战再看内核戏 CPU》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Don't Make Me Think

Don't Make Me Think

Steve Krug / New Riders Press / 18 August, 2005 / $35.00

Five years and more than 100,000 copies after it was first published, it's hard to imagine anyone working in Web design who hasn't read Steve Krug's "instant classic" on Web usability, but people are ......一起来看看 《Don't Make Me Think》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码