内存布局

在源码里这样写的

_Alloc_hider	_M_dataplus;
size_type		_M_string_length;

enum { _S_local_capacity = 15 / sizeof(_CharT) };

union
{
      _CharT           _M_local_buf[_S_local_capacity + 1];
      size_type        _M_allocated_capacity;
};

image.png

在内存中_M_dataplus指向字符串内存的开头,_M_string_length记录字符串长度

而下面是一个Union结构体

当字符串长度≤0xf的时候,_M_local_buf保存字符串

而字符串长度>0xf的时候,则向堆管理器申请内存,此时_M_allocated_capacity记录堆块大小

ida内可导入这个结构体

struct string {
      uint8_t *data_ptr;
      size_t length;
      union {
            char localbuf[15];
            size_t capacity;
      };
}

相关常见函数

字符串变量声明

构建入口点长这样

std::string::basic_string<std::allocator<char>>(&a1, "Hello")

__int64 __fastcall std::string::basic_string<std::allocator<char>>(string *a1, char *a2)
{
  __int64 v2; // rax
  char *v4; // [rsp+20h] [rbp-30h]

  v2 = std::string::_M_local_data(a1);
  std::string::_Alloc_hider::_Alloc_hider(a1, v2);// string->data_ptr = string->localbuf
  if ( !a2 )
    std::__throw_logic_error("basic_string: construction from null is not valid");
  v4 = &a2[std::char_traits<char>::length(a2)]; // dataend
  return std::string::_M_construct<char const*>(a1, a2, v4);
}

构建时先把string→data_ptr指向localbuf,然后再调用_M_construct

unsigned __int64 __fastcall std::string::_M_construct<char const*>(string *a1, uint8_t *a2, uint8_t *a3)
{
  __int64 v3; // rax
  uint8_t *dataptr; // rax
  string *v7; // [rsp+70h] [rbp-20h] BYREF
  unsigned __int64 length; // [rsp+78h] [rbp-18h] BYREF
  uint8_t *v9; // [rsp+80h] [rbp-10h]
  unsigned __int64 canary; // [rsp+88h] [rbp-8h]

  canary = __readfsqword(0x28u);
  v9 = a2;
  length = a3 - a2;
  if ( (unsigned __int64)(a3 - a2) > 0xF )      // if data length < 0xf, copy to localbuf
  {
    v3 = std::string::_M_create(a1, &length, 0LL);// alloc memory
    std::string::_M_data(a1, v3);
    std::string::_M_capacity(a1, length);
  }
  std::string::_M_construct<char const*>(char const*,char const*,std::forward_iterator_tag)::_Guard::_Guard(&v7, a1);
  dataptr = std::string::_M_data(a1);
  std::string::_S_copy_chars<char const*>(dataptr, a2, a3);// if length == 1, use std::assign else use std::copy
  v7 = 0LL;
  std::string::_M_set_length(a1, length);
  std::string::_M_construct<char const*>(char const*,char const*,std::forward_iterator_tag)::_Guard::~_Guard(&v7);
  return __readfsqword(0x28u);
}

这里首先判断data长度是否大于0xf,如果大于,需要调用_M_create进一步申请内存空间,然后进一步设置dataptr以及capacity

__int64 __fastcall std::string::_M_create(string *a1, unsigned __int64 *a2, unsigned __int64 a3)
{
  __int64 allocator; // rdi
  unsigned __int64 v5; // [rsp+0h] [rbp-30h]
  unsigned __int64 v6; // [rsp+10h] [rbp-20h]

  v6 = *a2;
  if ( v6 > std::string::max_size(a1) )         // length could not larger than 0x7fffffffffffffff-1
    std::__throw_length_error("basic_string::_M_create");
  if ( *a2 > a3 && *a2 < 2 * a3 )
  {
    *a2 = 2 * a3;                               // x 2
    v5 = *a2;
    if ( v5 > std::string::max_size(a1) )
      *a2 = std::string::max_size(a1);
  }
  allocator = std::string::_M_get_allocator(a1);
  return std::string::_S_allocate(allocator, *a2 + 1);// using new(arg2)
}

后面通过std::string::_S_copy_chars<char const*> 将数据复制到string中