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

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

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

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

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

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

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

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

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

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

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

static void ge_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 <do_page_fault+54>。那么我们只要对这个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相关推荐

  1. 从猫蛇之战三看内核戏CPU

    庐山归来,终于有些空闲,见缝插针,今天赶紧把没有写完的"猫蛇之战"补齐. 如果没有读过前两篇或者想复习一下的,请点击: 从猫蛇之战看内核戏CPU 从猫蛇之战再看内核戏CPU 先说明 ...

  2. 阳朔中学屏蔽手机信号 校园手机战再升级

    http://hechi.xjwy.cn/news/bencandy.php?fid=4&id=8459 阳朔中学屏蔽手机信号 "校园手机战"再升级 阳朔中学屏蔽手机信号 ...

  3. qtdesigner怎么实现菜单栏跳转_3种公众号菜单栏设置类型,手把手教你做,不会的话那就再看一遍...

    常见的菜单栏设置怎么去设置呢?在我们的公众号左侧的菜单栏中,你可以找到我们的自定义菜单,这个功能,点击进去之后,你就可以看到菜单的内容,它可以有三种类型可选:一种叫发送消息,一种叫跳转网页,一种叫跳转 ...

  4. 长得类似铁甲小宝的机器人_铁甲小宝:小时候只顾看机器人忽略重点,长大后再看:是我太天真...

    铁甲小宝:小时候只顾看机器人忽略重点,长大后再看:是我太天真 铁甲小宝相亲大家都是看过的,作为早期的三大人特摄之一,铁甲小宝针对的完全就是儿童,小时候我们也是很喜欢这部作品,只是现在在荧幕上已经很难看 ...

  5. OpenCV学习笔记(四十一)——再看基础数据结构core OpenCV学习笔记(四十二)——Mat数据操作之普通青年、文艺青年、暴力青年 OpenCV学习笔记(四十三)——存取像素值操作汇总co

    OpenCV学习笔记(四十一)--再看基础数据结构core 记得我在OpenCV学习笔记(四)--新版本的数据结构core里面讲过新版本的数据结构了,可是我再看这部分的时候,我发现我当时实在是看得太马 ...

  6. android 智能指针的学习先看邓凡平的书扫盲 再看前面两片博客提升

    android 智能指针的学习先看邓凡平的书扫盲 再看前面两片博客提升 转载于:https://www.cnblogs.com/jeanschen/p/3507512.html

  7. 张萍萍山东大学计算机科学系毕业生,并行驰骋,放“码”来战!看先导杯大奖赛上山大风采...

    原标题:并行驰骋,放"码"来战!看先导杯大奖赛上山大风采 近日,山东大学计算机科学与技术学院2018级硕士研究生杨林.2020级硕士研究生李威宇组成的团队获得中科院"先导 ...

  8. 再看数据库——(2)视图

    概念 *是从用户使用数据库的观点来说的. *从一个或多个表(视图)中导出来的 *一个虚表,或者说查询表 为什么要用视图呢? 一是简单,看到的就是需要的.视图不仅可以简化用户对数据的理解,也可以简化他们 ...

  9. 以前看书时记得一些笔记(二),很早了,现在再看都有些看不懂了

    MFC学习: 1.CObject类为MFC总类,该类下面有一个重要的类CCmdTarget.而CCmdTarget类下面又有四个重要的继承类,分别为:CWinThread.CDocument.CDoc ...

最新文章

  1. ViewBag对象的更改
  2. Centos6.8下搭建SVN服务器
  3. boost::units模块实现带有转换的用户定义单位的测试程序
  4. APP引导页UI设计素材模板|轻松留下完美的第一印象
  5. Zabbix添加Windows server 2012
  6. 13 医疗挂号系统_【 微信登录】
  7. 如何用Flutter实现跨平台移动开发
  8. 【51CTO学院三周年】初识51cto到习惯打开51cto
  9. 41岁职场中年人深度劝告:一定要从小公司往大公司走
  10. CentOS7.6安装weblogic14.1.1.0.0
  11. 黑群晖Aria2下载BT,磁力amp;PT自用详细设置
  12. 【随记】没Windows咋办?看看国产桌面操作系统Deepin
  13. js根据关键词数组匹配违禁词并标红显示
  14. Java数据库中台项目,电商,CMS轻松实现,包含数据库源文件
  15. 电脑CPU/GPU处理器知识普及
  16. IMX6Q的官方uboot移植,并且把1G的DDR3升级成2G的DDR3,修改网络驱动
  17. 6. 多列布局column
  18. umi中AssertionError [ERR_ASSERTION]: filePath not found of
  19. 手把手的教你用MapABC的地图API制作自己的免费地图
  20. AI day04(2020 8/3)

热门文章

  1. 二维数组与二级指针是好朋友吗?
  2. react native报错:undefined Unable to resolve module 'Dimensions' from 'App.js
  3. 英特尔结盟40公司抢滩家庭娱乐叫板思科
  4. 2022电工(初级)操作证考试题库及在线模拟考试
  5. Azure云服务反向DNS
  6. 11.15中移在线面试
  7. java中负数的二进制表示,【Java基础】15、负数的二进制表示方法
  8. android系统触摸屏虚拟按键
  9. Global Average Pooling与FC、Global Max Pooling的比较
  10. linux系统安装matlabR2021b