逆向分析C++代码时,总是会遇到一些stl代码,分析起来有些麻烦。所以有必要研究一下stl代码的原理。我将在接下来的几篇文章中讨论几个常见的stl容器。这篇先从std::string开始。
一 内存布局
std::string的源码运用了大量模板技术,看起来比较麻烦。然而对于逆向分析而言,我们只关心它的内存布局。下面是从string源码截取的部分代码,反映了string的内存布局。
template<class _Alloc_types>
class _String_alloc
{ // base class for basic_string to hold allocator
public:
//省略了函数相关代码
private:
_Compressed_pair<_Alty, _String_val<_Val_types> > _Mypair;
};
std::string中只有一个成员变量_Mypair,它是模板类变量,展开之后与_String_val的内存布局是一致的(这里面涉及到模板编程相关的技术,可以搜索Empty base optimization了解相关技术)。
template<class _Val_types>
class _String_val
: public _Container_base
{ // base class for basic_string to hold data
public:
//省略了函数相关代码
union _Bxty
{ // storage for small buffer or pointer to larger one
value_type _Buf[_BUF_SIZE];//_BUF_SIZE = 16
pointer _Ptr;
char _Alias[_BUF_SIZE]; // to permit aliasing
} _Bx;
size_type _Mysize; // current length of string
size_type _Myres; // current storage reserved for string
};
从上面的代码来看,string包含三个成员变量:_Bx,_Mysize和_Myres。_Mysize和_Myres分别是字符串的长度,预留空间的大小。_Bx是共用体,当字符串长度小于16字节时, 字符串直接存在_Buf中,如果字符串上图大于16个字节时,_Bx存放字符串指针。所以string在内存中占用16+4 + 4 = 24个字节。
二 逆向分析
接下来写个测试代码,并反编译看一下。
//(编译环境 :VS2015(v140) + X86 + Release)
int main()
{
std::string s0 ;
std::cin >> s0;
printf("%s", s0.c_str());
return 0;
}
编译一下,用IDA打开F5一下:
代码比较简单,注释给出了简要分析。应该很容易明白。实际逆向分析中string还是比较识别的,因为可以很容看到字符串。另外,string编译后一个很明显的特征是上图中13行代码,总是存在跟0x10比较的代码。
三 总结
逆向分析中,string的识别比较简单,其实没必要过多的讨论,只要记住string的内存布局即可。