[toc]

一、全局对象的分析

  1. 全局对象的构造

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    #include <stdio.h>
    // 分析过程中需要查看源码,所以设置成静态编译(MT MTd)
    class CObjA
    {
    public:
    CObjA()
    { printf("CObjA::CObjA();\n"); }
    };
    class CObjB
    {
    public:
    CObjB()
    { printf("CObjB::CObjB();\n"); }
    };
    // 一个全局变量,会在 main 函数之前被初始化
    CObjA g_object1;
    CObjB g_object2;
    // 8B 4D FC 8B 11 89 55 F8 8B 4D F8 FF 15 ?? ?? ?? ?? FF 55 F8 EB CF
    int main()
    {
    return 0;
    }
    • 通过堆栈查看调用过程

      image-20191221081048522

      在全局变量的构造函数中设置断点,通过栈回溯找到函数的调用过程进行分析

    • initterm 函数的原码

      image-20191221081417735

      initterm 函数接收两个参数,是一个范围,保存函数指针,遍历所有的函数并且调用函数

    • initterm 函数的汇编

      image-20191221083040014

      可以通过提取特征码的方式快速找到初始化对象的位置

      调用构造函数的位置,这个函数指针的第一次调用通常是无意义的,对于一个全局对象的构造,一般会用到的代码是 mov ecx,xxxx call init 函数

    • initterm 函数的调用位置(源码)

      image-20191221081844837

      invoke_main 函数的调用前,查看汇编指令的特征,若存在连续的两个函数调用,且参数都是两个全局变量,就可以猜测第二个是 initterm 函数

    • initterm 函数的调用位置(汇编)

      image-20191221082140244

      根据第一个函数的执行结果,设置返回值为 0xFF(255)

    • 提取 initterm 中调用初始化函数位置的特征a

      1
      8B 4D FC 8B 11 89 55 F8 8B 4D F8 FF 15 ?? ?? ?? ?? FF 55 F8 EB CF
    • C:\Program Files (x86)\Windows Kits\10\Source\SDK版本\ucrt\startup 下可以查看源码

  1. 全局对象的析构

    • 通过栈回溯查看调用过程

      image-20191221084109052

      对于析构函数即使找到了调用堆栈及源码和汇编代码也没有太多特征可以参考,故应该直接提取特征值

    • 析构函数调用的源头

      image-20191221084324317

      析构函数的调用位于 exit 函数之下的10层,应该尝试提取特征

    • 调用释放函数的汇编

      image-20191221084637598

      1
      89 45 C4 8B 4D EC 8B 55 D4 89 11 8B 45 C4 89 45 D0 8B 4D D0 FF 15 ?? ?? ?? ?? FF 55 D0
  1. 全局对象的操作

    • 无论是构造还是析构,对于全局变量,操作的指令通常都是 mov ecx, 0x????????
    • 如果是局部变量,操作方式通常是 lea ecx, [ebp - 0x??],需要进行区分

数据结构分析

  1. 字符串分析

    CString 对象,占用4字节大小,使用前需先初始化空间再调用构造函数

    image-20191221092039293

    大小是 0x1C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    struct my_string
    {
    my_string* self; // 指向自己的指针
    union {
    char str[0x10]; // 当字符个数少于 16
    char* point; // 当字符个数大于 15
    };
    int length; // 当前的字符个数
    int size; // 缓冲区的大小-1(空字符)
    };
  1. vector 分析

    调用过程:

    image-20191221095034718

    对应数据结构大小:0x10

    1
    2
    3
    4
    5
    6
    7
    8
    template<class T>
    struct my_vector
    {
    my_vector* self; // 指向自己的指针
    T* fisrt; // 指向数据的首地址
    T* last; // 最后一个元素的下一个位置
    T* end; // 指向堆空间的结尾(end 迭代器的指向)
    };
  1. List 分析

    image-20191221102423036

    对应数据结构大小:0x0C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    template <class T>
    struct my_list_node
    {
    my_list_node* next; // 下一个节点
    my_list_node* prev; // 上一个节点
    T data; // 数据域
    };

    template <class T>
    struct my_list
    {
    my_list* self; // 指向自己的指针
    my_list_node<T>* header; // 指向头节点,头节点不存储数据
    int size; // 元素个数
    };
  1. map 分析

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // map 的实现是一个红黑树
    struct _Tree_node
    {
    _Tree_node* _Left; // left subtree, or smallest element if head
    _Tree_node* _Parent; // parent, or root of tree if head
    _Tree_node* _Right; // right subtree, or largest element if head

    char _Color; // _Red or _Black, _Black if head
    char _Isnil; // true only if head (also nil) node; TRANSITION, should be bool

    // value_type _Myval; // 键值对,通常是一个结构体,大小 = sizeof(键) + sizeof(值)
    };
  1. 迭代器分析

    如何获取一个迭代器:初始化迭代器 + 将迭代器作为参数调用 begin 之类的函数

    image-20191221103444302

    执行 beign++ 操作

    image-20191221103738069

    迭代器的数据结构:大小是 0x0C

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    template <class T>
    struct point
    {
    T* target; // 目标容器的指针
    my_iterator* self; // 自己的地址
    };

    template <class T>
    struct my_iterator
    {
    point<T>* p; // 一个指针
    my_iterator* prev; // 上一个迭代器,如果是首元素就为 0
    T* data; // 指向的是数据, *iter 就是从这里获取数据
    };

MFC 程序的分析

  • 直接的获取到特征码: 不同版本的 VS 对应的特征码不同,所以需要自己分析,不同的事件对应的特征码也是不同

    image-20191221105610177

  • 特征码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    VS19-按钮: 8B 4D 08 FF 55 B8 3B F4
    0091852B 8B 4D 08 mov ecx,dword ptr [pTarget]
    0091852E FF 55 B8 call dword ptr [ebp-48h]
    00918531 3B F4 cmp esi,esp

    Debug 动态、静态编译
    CALL DWORD PTR SS:[EBP-8]

    Release 静态编译
    CALL DWORD PTR SS:[EBP+14]

    Release 动态编译
    CALL DWORD PTR SS:[EBP+0C]