2.3.2 字符串的存储方式
字符串是由一系列按照一定的编码顺序线性排列的字符组成的。在图形中,两点可以确定一直线;在程序中,只要知道字符串的首地址和结束地址就可以确定字符串的长度和大小。字符串的首地址很容易确定,因为在定义字符串的时候都会先指定好首地址。结束地址如何确定呢?有两种做法,一种是在首地址的4字节中保存字符串的总长度;另一种是在字符串的结尾处使用一个规定好的特殊字符,即结束符,这两种做法各有优缺点。
保存总长度
优点:获取字符串长度时,不用遍历字符串中的每个字符,取得首地址的前n字节就可以得到字符串的长度。(n的取值一般是1、2、4)
缺点:字符串长度不能超过n字节的表示范围,且要多开销n字节空间保存长度。如果涉及通信,双方交互前必须事先知道通信字符串的长度。
结束符
优点:没有记录长度的开销;另外,如果涉及通信,通信字符串可以根据实际情况随时结束,结束时附上结束符即可。
缺点:获取字符串长度需要遍历所有字符,寻找特殊结尾字符,在某些情况下处理效率低。
C++使用结束符'\0'作为字符串结束标志。ASCII编码使用一个字节'\0',Unicode编码使用两个字节'\0'。需要注意的是,不能使用处理ASCII编码的函数对Unicode编码进行处理,因为如果Unicode编码中出现了只占用一字节的字符,就会发生解释错误。ASCII与Unicode内存数据对比如图2-6所示。
在程序中,都会使用一个存放地址的变量来存放字符串中第一个字符的地址,以便查找使用字符串。在Microsoft Visual C++中使用字符型指针char*、wchar_t*、TCHAR*来保存字符串首地址。
图 2-6 ASCII与Unicode内存数据对比
图2-6为ASCII字符串pcChar与Unicode字符串pwChar的内存数据对比。pcChar所在地址0x00423020以ASCII字符进行组合;pwChar所在地址0x00423030以Unicode字符进行组合,两个字节为一个字符。
字符串的识别也相对简单,同样是结合上下文,查看调用地址处对该地址的处理过程。在通常情况下,OllyDBG与IDA都会自动识别出程序中的字符串。在使用IDA的过程中,有时会无法识别字符串,可手动修改,如图2-7所示。
图 2-7 未识别的字符串数据
在图2-7中,这段数据明显为一个字符串,但是IDA并没有分析出来,这时可以选中将要分析的字符串的首地址,使用快捷键A,便可将从分析地址处到'\0'解释为字符串。如图2-8所示为识别后的字符串数据。
图 2-8 识别后的字符串数据