逆向-数据结构

string

源码:

void test(int n) {
	// 样本测试,测试初始化string对象的函数
	string str1 = "hello";
	string str2 = "hello123";
	string str3 = "hello123456";
	string str4 = "hello123456789";
	string str5 = "hello123456789123";
	string str6 = "hello123456789123456";
	string str7 = "hello123456789123456789";
}

int main()
{
	int n = 1;
	scanf_s("%d", &n);
	test(n);
}

反汇编查看 

我们现在把ecx放到数据窗口查看 发现都被填充成CC

 调用构造函数后的地址  我们看00ab560就是string的首地址的四个字节是什么

我们过来后发现是一个堆空间 有两个数据(FD是堆的结尾) 0059f880发现又指向了我们的string的地址 这样他可能更方便的找到自己

我们接着看

 这个字符串的长度为0E 缓冲区最大为0f

 如果我们继续扩大我们的字符串string会怎么存储我们的字符串哪???

 我们的字符串长度为11 缓冲区最大存储的字节数为1F 主要是第二个字段 已经不是我们的字符串了 变成一个地址了 

 我们跟过去看 保存了我们的字符串 

  所以可以定义以下结构

struct MyString {
	struct MyString* pSelf;
	union MyUnion
	{
		char szString[16];
		char* pString;
	}u;
	int nStringLen;
	int nMaxStringLen;
};

//定义此结构指针去操作他
string str1 = "hello";
MyString* pString = (MyString*)&str1;
	strcpy(pString->u.szString, "hello");
	pString->nStringLen = 9;

CString

相对来说就很简单

他的对象就只有4个字节。

是一个指针,指向堆空间,堆中存储的是字符串。 过于简单就不看反汇编了

 过来看就是我们的字符串

list

void testList() {
	// 双向循环链表, 节点
	list<int> listObj;

	listObj.push_back(1);
	listObj.push_back(2);
	listObj.push_back(3);
	listObj.pop_back();

	listObj.push_back(4);
}

 调用初始化函数后 第一次初始化

 list容器会调用两次初始化函数  第一次清零 第二次会有数据

 两次初始化后的地址

 接着push_back一个数据看变化

 元素个数变成1 接下来我们看00e9cd98链表表头的地址

 一共两个元素 就是链表的前驱和后继  我们看链表的前驱和后继都指向了一个地方 是因为我们就只有一个元素 可见这是一个双向循环链表

 因为添加一个元素不好看出什么 让我们在添加一个

 看内存 一共两个元素 就是我们push_back 的1和2

表头的位置 现在前驱和后继就不一样了

 我们顺着链表继续看

 还是前驱后继

2就是我们的数据  继续向后

是我们的数据1 前面依旧是前驱后继

 继续向后 又回到我们的表头

所以结构是这样的

所以可以定义以下的结构

struct MyNode
{
	struct MyNode* pNext;
	struct MyNode* pPrev;
	int nData;
};

struct MyList
{
	struct MyList* pSelf;
	struct MyNode* pRoot;
	int nNodeCount;
};
	MyList* pList = (MyList*)&listObj;
	int size = pList->nNodeCount;
	MyNode* pNode = pList->pRoot;
	while (pNode->pNext != pList->pRoot)
	{
		pNode = pNode->pNext;
		int n = pNode->nData;
		printf("元素=%d\n", n);
	}

  Vector

void testVector() {
	// 动态数组,数据存储在堆内存中
	// 当元素发生改变之后,会动态增加内存。
	vector<int> vecObj;

	vecObj.push_back(1);
	vecObj.push_back(2);
	vecObj.push_back(3);
	vecObj.pop_back();
	vecObj.push_back(4);
	vecObj.push_back(5);
	vecObj.push_back(6);
	vecObj.push_back(7);
	//-------------------------------------
	// 遍历vector
	for (size_t i = 0; i < vecObj.size(); i++)
	{
		printf("vecObj[%d] = %d", i, vecObj[i]);
	}

	//-------------------------------------
	// 遍历vector
	vector<int>::iterator iter = vecObj.begin();
	while (iter != vecObj.end())
	{
		int n = *iter;
		printf("vecObj i = %d", n);
		iter++;  // 有临时对象产生
		//++iter;// 无临时对象
	}

}

 

 第二次初始化 会填入指向自己的指针

 

 push_back了两个元素

 第一个是指向自己的指针

第二个是指向缓冲区的指针

第三个是指向元素后一个字节的指针

第四个是指向申请的缓冲区后一个字节的指针

 

 0134e148也就是数组首地址两个元素1和2  上图的后两个指针都指向了FD的位置 

 

 在添加一个数据

 在添加一个数据后发现原来放数据的缓冲区被释放了 

 

我们的动态数组的第二个字段的缓冲区也变位置了

我们就可以猜测,在push_back的时候,动态申请新的空间,将数据拷贝。

如果空间不够了,再申请新的更大的空间,将原来的数据拷贝进来。

又执行了pop_back

我们看指向数组最后一个元素后面的指针发生了变化 因为数组元素变少了

 其实数据还在 但是他变动了指针 也就是有效数据是1和2

 再继续添加4 5 6 7 

当前动态数组的结构 我们看0134F4E8存储数据的位置

 上图的后两个指针分别指向了有效元素的后一个字节和有效缓冲区的后一个字节

结构是这样的

 可以定义此结构

struct MyVector
{
	struct MyVector* pSelf;
	int* pDataStart;
	int* pDataEnd;
	int* pBufEnd;
};
	// 定义结构,操作vector
	MyVector* pVector = (MyVector*)&vecObj;
	int size = ((int)pVector->pDataEnd - (int)pVector->pDataStart) / sizeof(int);
	for (size_t i = 0; i < size; i++)
	{
		//pVector->pDataStart[i] = 3;
		int n = pVector->pDataStart[i];
		printf("元素=%d\n", n);
	}

 

接下来我们看下遍历动态数组 我们看源码 他会调用三个函数 一个是size一个是[]运算符号重载

还有一个printf 普通的循环遍历

	// 遍历vector
	for (size_t i = 0; i < vecObj.size(); i++)
	{
		printf("vecObj[%d] = %d", i, vecObj[i]);
	}

 接下来看下迭代器遍历

	//-------------------------------------
	// 遍历vector
	vector<int>::iterator iter = vecObj.begin();
	while (iter != vecObj.end())
	{
		int n = *iter;
		printf("vecObj i = %d", n);
		iter++;  // 有临时对象产生
		//++iter;// 无临时对象
	}

两次初始化  我们看内存

0134E0E0指向了两个四字节的数据

第一个四个字节指向的是vector的地址

第二个四字节指向的是自己

00000000没用

0134f4e8指向了存放数据的缓冲区

 我们到0134e0e0看下

再看0134F4E8就是我们存储的数据

 

 所以结构是这样的

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值