Akvicor
Akvicor
发布于 2025-03-25 / 2 阅读
0
0

偶遇bug,强如怪物,拼尽全力无法战胜...

哦对的对的,哎呀不对不对,对...对吗?

并非全力

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>:

可以看到编译器不在通过计算得出函数入口,而是直接把入口地址放入寄存器。


评论