C++逆向学习(一) string

栏目: C++ · 发布时间: 6年前

内容简介:此文为原创文章作者:ret2nullptr@先知社区

C++逆向学习(一) string

此文为原创文章

作者:ret2nullptr@先知社区

恭喜作者获得

价值100元的天猫超市享淘卡一张

欢迎更多优质原创、翻译作者加入

ASRC文章奖励计划

欢迎多多投稿到先知社区

每天一篇优质技术好文

点滴积累促成质的飞跃

今天也要进步一点点呀

CTF比赛中C++的题越来越多,题目中经常出现 stringvector 等,而实际上手时发现常常迷失在"库函数"中,比如跟进了空间配置器相关函数

最近研究一下关于这些的底层机制与逆向,应该会写成一个系列

string

内存布局

visual studio 的调试实在是太好用了,因此用它举例

C++逆向学习(一) string

其中, size 是当前字符串长度, capacity 是最大的容量

可以发现, capacitysize 大的多

allocator 是空间配置器,可以看到单独的字符显示

原始视图中可以得知,字符串的首地址

C++逆向学习(一) string

可以看到, abcd 字符串在内存中也是以 \x00 结尾的

扩容机制

正是由于 capacity 开辟了更多需要的空间,来具体研究一下它的策略

#include<iostream>
#include<string>
#include<stdlib.h>
#include<windows.h>

using namespace std;

int main(int argc, char** argv) {
    string str;
    for (int i = 0; i < 100; i++) {
        str += 'a';
        std::cout << "size : " << str.size() << "   capacity : " << str.capacity() << std::endl;
    }
    system("pause");
    return 0;
}

从输出结果发现, capacity 的变化为 15 -> 31 -> 47 -> 70 -> 105

注意到15是二进制的 1111 ,而31是二进制的 11111 ,可能是设计成这样的?...

只有第一次变化不是1.5倍扩容,后面都是乘以1.5

当长度为15时,如下,两个 0x0f 表示长度,而第一行倒数第三个 0f 则表示的是当前的 capacity

再次 +='a'

原先的 capacity 已经从0x0f变成了0x1f,长度也变成了16

而原先存储字符串的一部分内存也已经被杂乱的字符覆盖了

新的字符串被连续存储在另一块地址

C++逆向学习(一) string

vs的调试中,红色代表刚刚改变的值

不过原先使用的内存里还有一些 aaaa... ,可能是因为还没有被覆盖到

IDA视角

测试程序1

#include<iostream>
#include<string>

using namespace std;

int main(int argc, char** argv) {
    string input;
    cin >> input;
    for (int i = 0; i < 3; i++) {
        input += 'a';
    }
    for (int i = 0; i < 3; i++) {
        input.append("12345abcde");
    }
    cout << input << endl;
    return 0;
}

//visual studio 2019 x64 release

我用的IDA7.0,打开以后发现IDA似乎并没有对 string 的成员进行适合读代码的命名,只好自己改一下

C++逆向学习(一) string

第一块逻辑,当 size>capacity 时,调用 Rellocate_xxx 函数

否则,就直接在 str_addr 后追加一个97,也就是 a

C++逆向学习(一) string

第二块逻辑,这次因为用的是 append() ,每次追加10个字符,即使是一个 QWORD 也无法存放,所以看到的是 memmove_0 函数

最后是 v9[10] = 0 ,也是我们在vs中看到的,追加后,仍然会以 \x00 结尾

一开始我没想明白, +='a' 为什么没有设置 \x00结尾

后来才发现,*(_WORD*)&str_addr[_size] = 97;

这是一个 WORD ,2个byte,考虑小端序, \x00 已经被写入了

至于其中的 Reallocate_xxx 函数,有点复杂...而且感觉也没必要深入了,刚刚已经在vs里了解扩容机制了

最后还有一个 delete 相关的

C++逆向学习(一) string

之前在做题时经常分不清作者写的代码、库函数代码,经常靠动态调试猜,多分析之后发现清晰了不少

测试程序2

#include<iostream>
#include<string>

using namespace std;

int main(int argc, char** argv) {
    string input1;
    string input2;
    string result;
    std::cin >> input1;
    std::cin >> input2;
    result = input1 + input2;
    std::cout << result;

    return 0;
}

//g++-4.7 main.cpp

这次用g++编译,发现逻辑很简明,甚至让我怀疑这是C++吗...

C++逆向学习(一) string

调用了一次 operator+ ,然后 operator= 赋值,最后输出

但是用vs编译,IDA打开就很混乱...下次再仔细分析一下

测试程序3

#include<iostream>
#include<string>

using namespace std;

int main(int argc, char** argv) {
    string input1;
    string input2;
    std::cin >> input1;
    std::cin >> input2;

    //语法糖
    for(auto c:input2){
        input1 += c;    
    }

    std::cout << input1;
    return 0;
}

//g++-4.7 main.cpp -std=c++11

仍然是g++编译的,IDA打开后虽然没有友好的命名,需要自己改,但是逻辑很清晰

C++逆向学习(一) string

for(auto c:input2) 这句是一个"语法糖",迭代地取出每一个字符,追加到 input1

IDA中可以看到,迭代器 begin和end ,通过循环中的 operator!= 判断是否已经结束,再通过 operator+= 追加,最后通过 operator++ 来改变迭代器 input2_begin 的值

这里命名应该把 input2_begin 改成 iterator 更好一些,因为它只是一开始是 begin

小总结

逆向水深...动态调试确实很容易发现程序逻辑,但是有反调试的存在

多练习纯静态分析也有助于解题,看得多了也就能分辨库函数代码和作者的代码了

C++逆向学习(一) string

C++逆向学习(一) string

C++逆向学习(一) string

请猛戳右边二维码

Twitter:AsrcSecurity

公众号ID

阿里安全响应中心

C++逆向学习(一) string


以上所述就是小编给大家介绍的《C++逆向学习(一) string》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Tagging

Tagging

Gene Smith / New Riders / 2007-12-27 / GBP 28.99

Tagging is fast becoming one of the primary ways people organize and manage digital information. Tagging complements traditional organizational tools like folders and search on users desktops as well ......一起来看看 《Tagging》 这本书的介绍吧!

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具