C++反汇编与逆向分析技术揭秘(第2版)
上QQ阅读APP看书,第一时间看更新

2.3 字符和字符串

字符串是由多个字符按照一定排列顺序组成的,在C++中,以'\0'作为字符串结束标记。每个字符都记录在一张表中,它们各自对应一个唯一编号,系统通过这些编号查找到对应的字符并显示。字符表格中的编号便是字符的编码格式。

2.3.1 字符的编码

在C++中,字符的编码格式分为两种:ASCII和Unicode。Unicode是ASCII的升级编码格式,它弥补了ASCII的不足,也是编码格式的发展趋势。

ASCII编码在内存中占1字节,由0~255之间的数字组成。每个数字表示一个符号,具体表示方式可查看ASCII表。由于ASCII编码也是由数字组成的,所以可以和整数互相转换,但整数不可超过ASCII的最大表示范围,因为多余部分将被舍弃。

由于ASCII原来的表示范围太小,只能表示26个英文字母和常用符号。在亚洲,ASCII的表示范围完全不够用,仅汉字就可以轻易占满ASCII编码。因此,占双字节、表示范围为0~65535的Unicode编码产生了。Unicode编码是世界通用的编码,ASCII编码也包含在其中。

使用char定义ASCII编码格式的字符,使用wchar_t定义Unicode编码格式的字符。wchar_t中保存ASCII编码,不足位补0,如字符'a'的ASCII编码为0x61,Unicode编码为0x0061。汉字的编码方式有些特殊,ASCII与Unicode都有与之匹配的编码格式。

在程序中使用中文、韩文、日文等时,经常出现显示的内容都是乱码的情况。这是因为系统缺少该语种字符表,这个字符表用于解释所需语种的字符编码,所以程序中的字符编码错误地对应到其他字符表中,显示出的文字是其他语种字符表中的信息。

ASCII编码与Unicode编码都可以用来存储汉字,但是它们对汉字的编码方式各不相同,所以存储同样的汉字,它们在内存中的编码是不同的,如图2-5所示。

图2-5 汉字字符串

ASCII使用GB2312-80,又名汉字国标码,保存了6763个常用汉字编码,用两个字节表示一个汉字。在GB2312-80中用区和位来定位,第一个字节保存每个区,共94个区;第二个字节保存每个区中的位,共94位。详细信息可查看GB 2312-80编码的说明。

Unicode使用UCS-2编码格式,最多可存储65536个字符。汉字博大精深,其中有简体字、繁体字,还有网络中流行的火星文,它们的总和远远超过了UCS-2的存储范围,所以UCS-2编码格式中只保存了常用字。为了将所有的汉字都容纳进来,Unicode也采用了与ASCII类似的方式——用两个Unicode编码解释一个汉字,一般称之为UCS-4编码格式。UCS-2编码表的使用和ASCII编码表的使用是一样的,每个数字编号在表中对应一个汉字,从0x4E00到0x9520为汉字编码区。例如,在UCS-2中,“烫”字的编码为0x70EB。更多关于UCS-2编码的信息可查看随书文件的UCS-2编码表。

2.3.2 字符串的存储方式

字符串是由一系列按照一定的编码顺序线性排列的字符组成的。在图形中,两点可以确定一条直线;在程序中,只要知道字符串的首地址和结束地址就可以确定字符串的长度和大小。字符串的首地址很容易确定,因为在定义字符串的时候都会先指定好首地址。但是结束地址如何确定呢?有两种方法,一种是在首地址的4字节中保存字符串的总长度;另一种是在字符串的结尾处使用一个规定好的特殊字符,即结束符,下面分析这两种方法的优缺点。

  • 保存总长度

优点:获取字符串长度时,不用遍历字符串中的每个字符,取得首地址的前n字节就可以得到字符串的长度(n的取值一般是1、2、4)。

缺点:字符串长度不能超过n字节的表示范围,且要多开销n字节空间保存长度。如果涉及通信,双方交互前必须事先知道通信字符串的长度。

  • 结束符

优点:没有记录长度的开销,即不需要存储空间记录长度信息;另外,如果涉及通信,通信字符串可以根据实际情况随时结束,结束时附上结束符即可。

缺点:获取字符串长度需要遍历所有字符,寻找特殊结尾字符,在某些情况下处理效率低。

C++使用结束符'\0'作为字符串结束标志。ASCII编码使用一个字节'\0',Unicode编码使用两个字节'\0'。需要注意的是,不能使用处理ASCII编码的函数对Unicode编码进行处理,因为如果Unicode编码中出现了只占用1字节的字符,就会发生解释错误。ASCII与Unicode内存数据对比如图2-6所示。

图2-6 ASCII与Unicode内存数据对比

在程序中,一般都会使用一个变量来存放字符串中第一个字符的地址,以便于查找使用字符串。在程序中使用字符型指针char*、wchar_t*来保存字符串首地址。

图2-6为ASCII字符串ch与Unicode字符串wch的内存数据对比。ch所在地址0x01177b30以ASCII字符进行组合;wch所在地址0x01177bf4以Unicode字符进行组合,两个字节为一个字符。

字符串的识别也相对简单,同样是结合上下文,查看调用地址处对该地址的处理过程。在通常情况下,OllyDbg和IDA都会自动识别出程序中的字符串。在使用IDA的过程中,有时会无法识别字符串,此时可进行手动修改,如图2-7所示。

图2-7 未识别的字符串数据

在图2-7中,这段数据明显为一个字符串,但是IDA并没有分析出来,这时可以选中要分析的字符串的首地址,使用快捷键A,便可将分析地址到'\0'解释为字符串。图2-8所示为识别后的字符串数据。

图2-8 识别后的字符串数据