关于某些环境下第二次调用disp_str产生乱码的问题解析

前言 标题中的”某些环境“包括笔者和一些同学的64位系统。 笔者验证时数次得到不符合预期的结果,于是数次推翻之前的结论,一开始以为这仅仅是一个与开关PIC选项有关的问题,后来发现可能是开关PIE的问题,再去实验发现同样的选项在不同编译器版本下行为也不同,可能是不同编译器的行为不同。 本文中关于PIC、PIE的分析比较粗糙,并且笔者的相关知识也很浅薄,可能会有很多错误,希望读者包涵并指正。 问题描述 在进行orange chapter5的第i个实验时,第二个disp_str的输出会变成乱码。原本笔者以为是对输出字符串的修改导致了错误,但经过许多尝试发现即使不做任何修改,只要编译一下原始的代码,第二次disp_str的输出就是乱码。更诡异的是原本附件中的a.img的运行结果是正常的,那么这有可能是编译环境不同,导致出现了问题。 图中可以看到最后一行输出是乱码。 问题溯源 为了方便查看,笔者只将start.c编译到start.o,其中第一次调用disp_str的部分是这样的: 比较有意思的是__x86_get_pc_thunk_bx函数,它的内容是这样的: public __x86_get_pc_thunk_bx __x86_get_pc_thunk_bx proc near ; __unwind { mov ebx, [esp+0] retn ; } 这个函数跟软件安全课程中介绍的病毒获取自身代码段位置的代码一样,是一种实现PIE(position-independent Executable,位置无关可执行文件),因为call指令相当于push ip; jmp。下一句紧接着执行mov ebx, [esp+0];retn就会将刚压进栈的ip的值赋给ebx。此时ebx的值是add ebx, (offset _GLOBAL_OFFSET_TABLE)的地址。所以下一句的add就会让ebx的值变为_GLOBAL_OFFSET_TABLE的值了,后续的字符串都是相对此时的ebx寻址的。 从lea指令开始为第一次调用disp_str布置参数,将[ebx]+aCstartBegins-_GLOBAL_OFFSET_TABLE_也就是aCstartBegins的实际地址压入栈中。 这段代码这么做是因为aCstartBegins和_GLOBAL_OFFSET_TABLE_的相对偏移是固定的,但程序本身的装载地址会发生变化,为了实现位置无关需要运行时获取代码所在的地址。 于是第一次调用十分正常,在disp_str中下断点,在bochs中调试: 看到正常的ebx的值应该是0x32ff4,此时aCstartBegins的地址是0x31000。 继续到第二次调用disp_str,用调试器或者静态分析都可以看到,正确的字符串aCstartEnds地址应该是aCstartBegins+0x2a=0x3102a。 ebx的值变成了0x32fa0=0x32ff4-0x54,导致disp_str显示字符串的地址变成了0x30fd6=0x3102a-0x54。 既然ebx的值改动了,浏览start.o的反汇编结果也没有发现ebx被改动,并且两次disp_str调用紧邻着也会输出乱码,那就只有可能是disp_str中本身修改了ebx. disp_str: push ebp mov ebp, esp xchg bx, bx # magic_break for debug mov esi, [ebp + 8] ; pszInfo mov edi, [disp_pos] mov ah, 0Fh .1: lodsb test al, al jz .2 cmp al, 0Ah jnz .3 push eax mov eax, edi mov bl, 160 ; <--------------修改bl div bl and eax, 0FFh inc eax mov bl, 160 ; <--------------修改bl mul bl mov edi, eax pop eax jmp .1 .3: mov [gs:edi], ax add edi, 2 jmp .1 .2: mov [disp_pos], edi pop ebp ret 发现代码中两次修改ebx的值,都是将bl(ebx低8位)改为160,也就是16进制的0xa0. 再根据先前的ebx从0x32ff4变成0x32fa0,正好是低8位从0xf4变成了0xa0,出错过程完全分析清楚。 ...

October 22, 2024 · 1 min · 206 words · JuicyMio

操作系统学习笔记

0xFE 序言 赶上吾爱破解十五周年开放注册, 注册了个账号. 随便翻了翻精品帖子, 看到从0到-1写一个操作系统-0xFF-!!完结撒花!!, 感觉还不错, 于是开始动手实践, 开一个博客记录, 也是督促自己坚持下去. 0xFF 操作系统的启动 实模式 刚开机时CPU进入实模式, 因为此时只能使用物理地址. 实模式地址的分布是固定的,以8086为例, BIOS入口在0xFFFF0到0xFFFFF之间, 其中是一个jmp指令, 跳转到真正的BIOS位置. 中断向量表在0x00000到0x003FF. MBR加载地址在0x7C00到0x7DFF. BIOS(Basic Input/Output System) 基本输入输出系统. 早期存储在ROM中, 现在存储在主板上的一个或多个芯片中. 目前其继承者UEFI(Unified Extensible Firmware Interface)正在全面取代BIOS. BIOS首先进行加电自检(Power-On Self-Test), 缩写为POST, 检查CPU, 内存, 主板, 硬盘, 显卡等设备. POST结束后系统BIOS调用其他设备的BIOS对各个设备进行检测和初始化. 如果自检没有出现问题, 将执行启动程序. Boot BOOT(靴子), 意为"to load a program into a computer from a disk; to start or ready for use especially by booting a program." BOOTSTRAP(鞋带) pull oneself up by one’s bootstraps 靠自己自立自强 by one’s own bootstraps 自己努力, 自强 ...

March 14, 2023 · 1 min · 208 words · JuicyMio