STL 数据结构 Hex-Rays

逆向工程 艾达 反编译 C++ 六线谱 微信
2021-07-07 10:59:42

我们都知道逆向 STL 代码并不好玩。为方便起见,我通常会创建一些结构,以便我可以在另一个自定义结构中引用创建的 STL 结构。

例如,msvc - std::string 结构:

00000000 std::string     struc ; (sizeof=0x1C)
00000000 alloc           dd ?                    ; void *
00000004 Buffer          db 16 dup(?)            ; string(C)
00000014 Length          dd ?                    ; int
00000018 LengthMax       dd ?                    ; int
0000001C std::string     ends

或 std::vector:

00000000 std::vector     struc ; (sizeof=0x10)
00000000 alloc           dd ?                    ; void *
00000004 NumberOfItems   dd ?                    ; int
00000008 MaxSize         dd ?                    ; int
0000000C Data            dd ?                    ; 
00000010 std::vector     ends

这样做,然后使用这些结构进行反编译,会使代码更具可读性。

我有时发现很难找出其他结构,即std::queue,查看实现我可以看到它的容器类型实际上是一个 typedef to std::deque,其内容为std::_Compressed_pair.

有没有办法重新创建这些 STL 类型,并像我为向量和字符串所做的那样正确命名,以便我可以在其他结构中引用它们以便进行更清晰的反编译?一个std::queue结构会是什么样子?

非常感谢。

2个回答

正如我评论的那样,我使用 windbg 转储这些结构

用于遍历的示例代码

#include <iostream>                                                     //01
#include <list>                                                         //02
#include <vector>                                                       //03
#include <map>                                                          //04
#include <queue>                                                        //05
#include <string>                                                       //06
int main(void)                                                          //07
{                                                                       //08
    std::string             ms;                                         //09
    std::wstring            mw;                                         //10
    std::map< char,int >    mm;                                         //11
    std::vector<int>        mv1,mv2,mv3,mv4;                            //12
    std::queue<std::vector<int>> mq;                                    //13
                                                                        //14
    ms = "This is a big Ascii string for that matter to avoid sso";     //15
    mw = L"This is a big Wide string for that matter to avoid sso";     //16
    mm = {{'a',0x61},{'b',0x62},{'c',0x63},{'d',0x64}};                 //17
    mv1.insert(mv1.end(), {mm['a'],mm['b'],mm['c'],mm['d']});           //18
    mv2.insert(mv2.end(), {mm['b'],mm['c'],mm['a'],mm['d']});           //19
    mv3.insert(mv3.end(), {mm['d'],mm['b'],mm['a'],mm['c']});           //20
    mv4.insert(mv4.end(), {mm['c'],mm['b'],mm['a'],mm['d']});           //21
    mq.push(mv1);                                                       //22
    mq.push(mv2);                                                       //23
    mq.push(mv3);                                                       //24
    mq.push(mv4);                                                       //25
    std::cout << ms << "\n";                                            //26
    std::wcout << mw << "\n";                                           //27
    std::cout << mm.begin()->first << "\n";                             //28
    std::cout << *mv1.begin() << "\n";                                  //29
    std::cout << *mq.front().begin() << "\n";                           //30
    std::cout << *mq.back().begin() << "\n";                            //31
}                                                                       //32

在 vs2017 devcmd 提示符下编译并链接为 x86

:>cat complink.bat
pushd .
call "c:\Program Files\Microsoft Visual Studio\2017\Community\Common7\Tools\vsdevcmd.bat"
popd
cl /Zi /W4 /analyze /EHsc /nologo /Od nsstd.cpp /link /release
:>

将其加载到windbg 中执行直到第26 行以初始化所有变量。
运行下面给出的脚本来转储队列的->vector's->maps->integer 值并退出。

Windbg 脚本

.for(r$t0=0;@$t0<4;r$t0=@$t0+1) 
{
    .for(r $t1=0;@$t1<4;r$t1 =@$t1+1) 
    {
        .printf "%c\t" , @@c++( mq.c._Mypair._Myval2._Map[@$t0]->_Mypair._Myval2._Myfirst[@$t1] )
    }
    .printf "\n"
}

结果

:>cdb -c "g `nsstd!nsstd.cpp:26`;$$>a< dumpq.wds;q" nsstd.exe | awk "/Reading/,/quit/"
0:000> cdb: Reading initial command 'g `nsstd!nsstd.cpp:26`;$$>a< dumpq.wds;q'
WARNING: Line information loading disabled
ModLoad: 6c700000 6c703000   C:\Windows\system32\api-ms-win-core-synch-l1-2-0.DLL
a       b       c       d
b       c       a       d
d       b       a       c
c       b       a       d
quit:

你创造了这些结构还是你从某个地方拉出来的?
基本上这些是模板类而不是结构

你从哪里得到 0x1c 的 std::string 大小
它是 0x18
0x10 缓冲区或指针(静态小字符串或 malloced() bigstring )
0x4 实际大小
或_Mysize 0x4 保留大小或_Myres

喜欢

typedef struct _FOO {
union _Bx {
char buff[16];
Pointer ptr;
};
ULONG _Mysize;
ULONG _Myres;
}Foo,*PFoo;

这些类在 xstring 、 vector 等头文件中定义,例如 string_val 在 xstring 中定义

C:\Program Files\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.16.27023\include\xstring

:\>grep -r -n -i  class.*string_val *
xstring:1768:           // CLASS TEMPLATE _String_val
xstring:1770:   class _String_val

同样明智的你可以得到队列也可以在双端队列头中查找 queue_val

0:000> ?? myqueue

class std::queue<std::vector<int,std::allocator<int> >,std::deque<std::vector<int,std::allocator<int> >,std::allocator<std::vector<int,std::allocator<int> > > > >
   +0x000 c                : std::deque<std::vector<int,std::allocator<int> >,std::allocator<std::vector<int,std::allocator<int> > > >

0:000> ?? sizeof(myqueue)
unsigned int 0x14

0:000> ?? myqueue.c._Mypair._Myval2
class std::_Deque_val<std::_Deque_simple_types<std::vector<int,std::allocator<int> > > >
   +0x000 _Myproxy         : 0x0057b598 std::_Container_proxy
   +0x004 _Map             : 0x0057b960  -> 0x0057b998 std::vector<int,std::allocator<int> >
   +0x008 _Mapsize         : 8
   +0x00c _Myoff           : 0
   +0x010 _Mysize          : 1