每一个程序员都会经历的“烫烫烫”

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/WANG__RONGWEI/article/details/79372791

0x00 说明

“烫”这个汉字在计算机程序执行出错时老是出现,特别有意思,尤其在Windows环境下执行越界访问的程序,会打印出一串“烫烫烫…”,作者以前在VS下写程序的时候就出现过一直打印“烫”,当时没有去仔细研究研究为什么是“烫”这个汉字,现在特把这个原因说明。


0x01 “烫”的GBK编码

首先,不得不提到“烫”的GBK编码为“0xCCCC”,同时在x86系统中“0xCC”代表汇编指令”INT 3“的机器码。
INT 3:一个中断指令,例如设置软中断与这个指令相关,那么一段内存全是“CCCCCC…”,就会一直产生“烫”。
另外还有一个例子,在往单片机的液晶显示上显示汉字的时候,有时候乱码也会出现“烫”,因为对于有些液晶,如12864,其汉字库就是GBK编码。

0x02 较简单的测试程序

  • 运行环境
    VS2013

  • C代码

#include "stdafx.h"


void foo(void)
{
    int a=0, b=0, c=2;
    printf("hello,world\n");
}


int main()
{
    int i=0, j=0;
    char array[10];//声明一个数组,没有初始化
    printf("%s\n",array);//使用字符串的形式打印

    foo();

    return 0;
}

  • 汇编程序
int main()
{
01011440  push        ebp  
01011441  mov         ebp,esp  
01011443  sub         esp,0F0h  
01011449  push        ebx  
0101144A  push        esi  
0101144B  push        edi  
0101144C  lea         edi,[ebp-0F0h]  
01011452  mov         ecx,3Ch  
01011457  mov         eax,0CCCCCCCCh  ##使用0xccccc....填充
0101145C  rep stos    dword ptr es:[edi]  
0101145E  mov         eax,dword ptr ds:[01018000h]  
01011463  xor         eax,ebp  
01011465  mov         dword ptr [ebp-4],eax  
    int i=0, j=0;
01011468  mov         dword ptr [i],0  
0101146F  mov         dword ptr [j],0  
    char array[10];

    printf("%s\n",array);
01011476  mov         esi,esp  
01011478  lea         eax,[array]  
0101147B  push        eax  ##将形参入栈
0101147C  push        1015868h  //将“%s\n”入栈
01011481  call        dword ptr ds:[1019114h]  
01011487  add         esp,8  
0101148A  cmp         esi,esp  
0101148C  call        __RTC_CheckEsp (0101113Bh)  ##调用类类似printf/scanf函数后,需要检查缓冲区溢出

    foo();
01011491  call        foo (010110F0h)  

    return 0;

其实从程序内存的角度去观察程序的运行过程,才能感受到程序的本质。
上面这段汇编是main函数的反汇编代码,与最上面的C语言代码对应,可以发现好多汇编语句是C语言里边发现不了的,尤其是堆栈指针的操作。对于每一个函数,它们都有一个堆栈,如下代码:

01011440  push        ebp  
01011441  mov         ebp,esp  
01011443  sub         esp,0F0h  
01011449  push        ebx  
0101144A  push        esi  
0101144B  push        edi  
0101144C  lea         edi,[ebp-0F0h]  
01011452  mov         ecx,3Ch  
01011457  mov         eax,0CCCCCCCCh  ##使用0xccccc....填充
0101145C  rep stos    dword ptr es:[edi]  
0101145E  mov         eax,dword ptr ds:[01018000h] 

一个函数内部,esp指针和ebp指针之间的空间就是函数的堆栈区间,进入该函数后,都会使用0xCCCC填充它的堆栈区间,那么如果我们声明一个数组,不加初始化的采用printf(“%s”)的形式打印这个数组,在终端就会打印出“烫烫烫”,一个字节就是0xCC,所以如果我们声明一个char型变量,也不加初始化的打印,便是0xCC=-52,或者以十六进制打印也可。这只是验证上面的说法:函数一开始就以0xCCCC填充堆栈区,如果不初始化,就是0xCC(就看是几个字节的变量)。
(补充:在VS下做相关实验,需要关闭sdl检查)

  • 内存视界
    丢一个函数调用的堆栈视图:
    函数调用堆栈视图
展开阅读全文

没有更多推荐了,返回首页