内容简介:连续写了几天的代码,有些疲倦,吃过晚饭,换个工作方式,继续和大家聊猫蛇之战。蛇不仅丑陋,而且可能伤人害命,是邪恶的象征。猫与蛇战,代表着讨伐奸邪,是正义之战。猫与蛇战,技艺娴熟,举重若轻,叫人拍手叫绝,看着过瘾,想起也心情舒畅。
连续写了几天的代码,有些疲倦,吃过晚饭,换个工作方式,继续和大家聊猫蛇之战。
蛇不仅丑陋,而且可能伤人害命,是邪恶的象征。猫与蛇战,代表着讨伐奸邪,是正义之战。猫与蛇战,技艺娴熟,举重若轻,叫人拍手叫绝,看着过瘾,想起也心情舒畅。
儿时听过很多关于猫的故事,比如传说猫武艺高强,是老虎的老师,教老虎武功时,怕其背叛师门,反目攻击老师,就故意留了一项会爬树的技艺。因为此,老虎是不会爬树的。
再比如,家里吃面条时,不要把面条喂给猫,怕它没有面条吃时,去把蛇当作面条吃。在外面吃完了还好,如果叼回家里来吃就吓人了。
在搜索猫蛇之战的照片时,确实看到了猫吃蛇的照片,有点血腥。
线下版本的猫蛇战先聊到这,我们继续谈计算机世界的猫蛇之战。上回书说到调试器在访问内存时,会使用特殊的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掉。
而执行probe方法时,则风平浪静,一且安好。
为了能观察其中的细节,我们将使用KGDB双机内核调试,用强大的调试器做控制, 探微索隐 。
可能有看官说,还可以这样玩啊?是的,想想猫蛇之战,如果功夫不够,那可能被蛇吃掉的。对于今天的计算机来说, CPU在以光速奔跑, CPU执行一条指令的时间,光也只能行进几个厘米 。猫之所以敢与蛇正面 交锋,靠的是反应速度要比蛇快很多倍。而人的反应速度要比CPU慢不知道多少倍,如果不依靠调试器,怎么看的清楚?
Linus大神喜欢读源代码和加print,但那不是老雷的风格。
说话间,两个虚拟机都跑起来了,一个叫GE64,是调试目标,另一个叫GD64,跑调试器,二者通过虚拟串口通信,已经建立了内核调试会话。
在目标机器中,编译加载llaolao模块,然后执行如下命令让目标机中断到调试器怀抱。
echo g > /proc/sysrq-trigger
目标机应声中断,在调试器中执行如下命令对处理页错误异常的do_page_fault函数设置一个断点。
这样设置断点后,恢复执行,发现断点会频繁命中,没法继续玩下去。
为了避免这样的问题,要改变断点的设置方法,先对 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函数中插入几行代码,如下图所示。
这样修改后,等一下就可以把断点设置在条件块内部了,那么就可以精准命中我们希望的条件,又不需要频繁中断到GDB了。
如此修改后,执行make bzImage以增量方式构建内核。
有些看官可能又惊诧了,这么麻烦啊?
对于 Java 同行来说,重编内核可能是有点吓人。其实没那么可怕,特别是如果经常这么做的话,其实是很便捷的,修改代码,编译,编译好的复制到boot目录,以新换旧,一杯茶还没有喝好的功夫就搞定了。
使用新的内核重启目标系统,中断到GDB,反汇编 do_page_fault函数, 寻找新修改的代码地址:
x86汇编很是浅显易懂,+22的位置是与0x880比较,+31的je指令是条件跳转,如果相等就跳到0xffffffff8106b686
b *0xffffffff8106b686
这样埋好断点后,恢复目标执行,目标系统活蹦乱跳,灵活自如了,不像刚才那样动弹不得。如此看来,能够重新编译内核真是好,可以在高特权的内核空间里安排自己的兵力。
闲言打住,在目标系统中,加载刘姥姥模块,然后执行如下命令触发调用probe动作:
echo probe > /proc/llaolao
断点如期命中,美哉GDB!
执行bt命令观察CPU的执行过程:
上面一图值二两白银,在清代时可以买一块地。
这幅图的价值在于,它抓到了一个非常难以抓到的状态,把风驰电掣般飞奔的CPU“停”在了一个非常敏感的位置:因为有人违反系统规则,非要访问不可以访问的地址,CPU硬件发起异常,保存基本的位置信息后投到内核怀中上述。
上面截图中#16和#17中的信息其实就是CPU硬件压入栈的执行非法访问的“黑手”地址,即CS:RIP。0x10是内核代码段的段选择子,RIP指向copy_user_generic_string函数,它正是probe函数中调用的。
感谢强大的调试技术,它帮助我们把CPU停在我们希望仔细观察的地方,让我们可以细细体会,证实了我们的推理。君子戒慎乎其所不睹,恐惧乎其所不闻,亲眼目睹,亲手实践,何其重要也!
继续观察寄存器信息:
可以看到,rax的值正是0x880,确认这次中断就是我们在llaolao驱动中通过probe函数访问0x880时导致的。
如此看来,问题1的答案有了,使用probe函数时,CPU还是会报异常的,CPU还是会进入到do_page_fault这个处理页错误的内核函数,有调试器抓到的现场为证,千真万确。
如此一来,除了前面提到的第二个和第三个问题,还有其它问题了,CPU在报告页错误时,会回滚状态,把程序指针回退到导致错误的那条指令,而调用probe函数时又能顺利返回到调用者,是谁悄悄调整了程序指针呢?
短文已经不短 ,不仅作者写的有点累了,大家读着可能也累了,就此打住,再找时间继续。顺便感谢转发和宣传上一篇文章的同行们,特别是 LINUX 圈里很有名气的陈老师也转载拙文,让笔者很受鼓舞。也感谢已经报名庐山研习班的格友们,期待与大家相会在庐山山南。
***********************************************************
正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生。
欢迎关注格友公众号
以上所述就是小编给大家介绍的《从猫蛇之战再看内核戏 CPU》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 内核必须懂(六): 使用kgdb调试内核
- Linux内核如何替换内核函数并调用原始函数
- Linux内核工程师是怎么步入内核殿堂的?
- Linux内核工程师是怎么步入内核殿堂的?
- 面试官:说说操作系统微内核和 Dubbo 微内核?
- Linux 内核的核心维护者表示鲜有手机供应商更新内核
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Starfish and the Spider
Ori Brafman、Rod A. Beckstrom / Portfolio Hardcover / 2006-10-05 / USD 24.95
Understanding the amazing force that links some of today's most successful companies If you cut off a spider's leg, it's crippled; if you cut off its head, it dies. But if you cut off a st......一起来看看 《The Starfish and the Spider》 这本书的介绍吧!