哦对的对的,哎呀不对不对,对...对吗?
并非全力
0x500 处的内存不要碰,否则可能导致程序跑飞
可能是由于BIOS占用了这个区间的一部分内存,随便改写导致异常。
但是实模式内存分布图上显示这一块确实是随便用。
LBA读取磁盘扇区后,fs失去4GB寻址能力
fs通过切换到保护模式加载了段选择子,拥有了4GB寻址能力。在返回实模式后仍然拥有4GB寻址能力,但是不能再对fs段寄存器进行赋值操作,否则将失去4GB寻址能力。猜测LBA读取磁盘时可能对fs段寄存器进行了修改,导致失去4GB寻址能力。
并非无法战胜
printk因调用strlen导致打印出错
首先给出printk调用,功能为输出VBE的OEM字符串及其地址。
char * pv = (char *)((((ul)VbeInfo->OemStringSegment)<<4)+VbeInfo->OemStringOffset);
printk(YELLOW, BLACK, "%lx %s\n", pv, pv);原始strlen源码,输出为 字符串1 LGPL VGABIOS Developers Bochs VBE ...
inline uint strlen(const char *__s) {
register int __res;
__asm__ __volatile__ (
"xor %[result], %[result] \n"
"decl %[result] \n"
"xor %%al, %%al \n"
"repnz scasb \n"
"notl %[result] \n"
"decl %[result] \n"
:
[result]"=c"(__res)
:
[input]"D"(__s)
:
);
return __res;
}修改后strlen源码,输出为 字符串2 Bochs VBE (C) 2002-2020 ...
inline uint strlen(const char *__s) {
register int __res;
__asm__ __volatile__ (
"xor %[result], %[result] \n"
"decl %[result] \n"
"xor %%al, %%al \n"
"repnz scasb \n"
"notl %[result] \n"
"decl %[result] \n"
:
[result]"=c"(__res),
// [!code highlight]
[input]"=&D"(__s)
:
// [!code highlight]
"[input]"(__s)
:
);
return __res;
}通过分析可知,由于strlen在开启-O优化后将作为内联函数展开到调用部分,而在strlen中对di寄存器的值进行了修改导致di指向了字符串末端。通过查看内存可知,字符串1 紧跟在 字符串2 后面。
若不开启内联,则不会出现此问题
代码被移动后无法正确计算地址
在切换到 user level 的代码段后,无法正确执行printk(函数入口计算错误)
在分析反汇编代码配合bochs单步调试后,发现gcc8默认在调用函数时会通过计算来确定所调用的函数入口地址,在计算过程中便用到了 GLOBALOFFSET_TABLE_ ,而我将代码通过memcpy转移到了其他的物理内存地址处,进而导致计算出了错误的函数入口地址。
太好了, 是反汇编, 我们有救了!
ffff80000010c043 <user_level_function>:
ffff80000010c043:»»»55 »»»push %rbp
ffff80000010c044:»»»48 89 e5 »»»mov %rsp,%rbp
ffff80000010c047:»»»41 57 »»»push %r15
ffff80000010c049:»»»48 83 ec 08 »»»sub $0x8,%rsp
// [!code highlight:2]
ffff80000010c04d:»»»48 8d 0d f9 ff ff ff »»»lea -0x7(%rip),%rcx # rcx 值 = ffff80000010c04d
ffff80000010c054:»»»49 bb d3 36 00 00 00 »»»movabs $0x36d3,%r11 # r11 值 = 与_GLOBAL_OFFSET_TABLE_的差值
ffff80000010c05b:»»»00 00 00
// [!code highlight]
ffff80000010c05e:»»»4c 01 d9 »»»add %r11,%rcx # # rcx 值 = ffff80000010f720
ffff80000010c061:»»»48 b8 50 0e 00 00 00 »»»movabs $0xe50,%rax
ffff80000010c068:»»»00 00 00
ffff80000010c06b:»»»48 8d 14 01 »»»lea (%rcx,%rax,1),%rdx
ffff80000010c06f:»»»be 00 00 00 00 »»»mov $0x0,%esi
ffff80000010c074:»»»bf 00 ff 00 00 »»»mov $0xff00,%edi
ffff80000010c079:»»»49 89 cf »»»mov %rcx,%r15
ffff80000010c07c:»»»b8 00 00 00 00 »»»mov $0x0,%eax
// [!code highlight]
ffff80000010c081:»»»49 b8 5e 6b ff ff ff »»»movabs $0xffffffffffff6b5e,%r8 # r8 值 = ffffffffffff6b5e
ffff80000010c088:»»»ff ff ff
// [!code highlight]
ffff80000010c08b:»»»49 01 c8 »»»add %rcx,%r8 # r8 值 = 10627e 为printk入口地址
ffff80000010c08e:»»»41 ff d0 »»»callq *%r8
ffff80000010c091:»»»eb fe »»»jmp ffff80000010c091 <user_level_function+0x4e>
// [!code highlight]
ffff80000010f720 <_GLOBAL_OFFSET_TABLE_>:
// [!code highlight]
ffff80000010627e <printk>:解决方案为: 在Makefile文件的CFLAGS变量中添加-fno-pic即可。
添加后编译结果的反编译结果
ffff80000010aa2f <user_level_function>:
ffff80000010aa2f:»»»55 »»»push %rbp
ffff80000010aa30:»»»48 89 e5 »»»mov %rsp,%rbp
ffff80000010aa33:»»»48 ba f8 eb 10 00 00 »»»movabs $0xffff80000010ebf8,%rdx
ffff80000010aa3a:»»»80 ff ff
ffff80000010aa3d:»»»be 00 00 00 00 »»»mov $0x0,%esi
ffff80000010aa42:»»»bf 00 ff 00 00 »»»mov $0xff00,%edi
ffff80000010aa47:»»»b8 00 00 00 00 »»»mov $0x0,%eax
// [!code highlight]
ffff80000010aa4c:»»»48 b9 09 5d 10 00 00 »»»movabs $0xffff800000105d09,%rcx
ffff80000010aa53:»»»80 ff ff
ffff80000010aa56:»»»ff d1 »»»callq *%rcx
ffff80000010aa58:»»»eb fe »»»jmp ffff80000010aa58 <user_level_function+0x29>
// [!code highlight]
ffff800000105d09 <printk>:可以看到编译器不在通过计算得出函数入口,而是直接把入口地址放入寄存器。