C++逆向


前言

C++ 是面向对象语言,C++ 的面向对象机制在多个维度增加了由 C++ 编写的程序的逆向难度。对于逆向工程人员来说,掌握 C++ 的核心概念的底层表示形式非常重要。

C++ 严格来说是 C 的超集,向下兼容 C 很多地方和 C 是相同的。

进行 C++ 的逆向,是必须了解 C++ 的基本语法的。

严格来说C++逆向是一个很大也很难的东西,但是也是比赛中非常多的一个考点。

这里默认你已经了解了。

1
2
3
4
5
6
7
8
9
10
#include <string>
#include <iostream>

using namespace std;

class Animal{
int age;
double weight;
}

类内存布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>

class MyClass{
public:
    MyClass(){
        var1=1;
        var2=2;
        var3=3;
    };
    int get_var1(){return var1;}
private:
int var1;
int var2;
int var3;
};



int main(){
    MyClass my;
    return 0;
}

MSVC利用RCX传递this指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# License: MSVC Proprietary
# The use of this compiler is only permitted for internal evaluation purposes and is otherwise governed by the MSVC License Agreement.
# See https://visualstudio.microsoft.com/license-terms/vs2022-ga-community/

this$ = 8
MyClass::MyClass(void) PROC                                ; MyClass::MyClass, COMDAT
        mov     QWORD PTR [rsp+8], rcx
        mov     rax, QWORD PTR this$[rsp]
        mov     DWORD PTR [rax], 1
        mov     rax, QWORD PTR this$[rsp]
        mov     DWORD PTR [rax+4], 2
        mov     rax, QWORD PTR this$[rsp]
        mov     DWORD PTR [rax+8], 3
        mov     rax, QWORD PTR this$[rsp]
        ret     0
MyClass::MyClass(void) ENDP                                ; MyClass::MyClass

my$ = 32
main    PROC
$LN3:
        sub     rsp, 56                             ; 00000038H
        lea     rcx, QWORD PTR my$[rsp]
        call    MyClass::MyClass(void)           ; MyClass::MyClass
        xor     eax, eax
        add     rsp, 56                             ; 00000038H
        ret     0
main    ENDP

含有虚函数的内存布局

1
2
3
4
5
6
class MyClassVirtual{
int var1;
public:
virtual int get_num(int x,int y);
virtual void reset_values() {var1=7;}
};

内部布局

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# License: MSVC Proprietary
# The use of this compiler is only permitted for internal evaluation purposes and is otherwise governed by the MSVC License Agreement.
# See https://visualstudio.microsoft.com/license-terms/vs2022-ga-community/
MyClassVirtual::`RTTI Base Class Descriptor at (0,-1,0,64)' DD imagerel MyClassVirtual `RTTI Type Descriptor' ; MyClassVirtual::`RTTI Base Class Descriptor at (0,-1,0,64)'
        DD      00H
        DD      00H
        DD      0ffffffffH
        DD      00H
        DD      040H
        DD      imagerel MyClassVirtual::`RTTI Class Hierarchy Descriptor'
MyClassVirtual::`RTTI Base Class Array' DD imagerel MyClassVirtual::`RTTI Base Class Descriptor at (0,-1,0,64)' ; MyClassVirtual::`RTTI Base Class Array'
        ORG $+3
MyClassVirtual::`RTTI Class Hierarchy Descriptor' DD 00H                     ; MyClassVirtual::`RTTI Class Hierarchy Descriptor'
        DD      00H
        DD      01H
        DD      imagerel MyClassVirtual::`RTTI Base Class Array'
MyClassVirtual `RTTI Type Descriptor' DQ FLAT:const type_info::`vftable'   ; MyClassVirtual `RTTI Type Descriptor'
        DQ      0000000000000000H
        DB      '. ?? AVMyClassVirtual', 00H
const MyClassVirtual::`RTTI Complete Object Locator' DD 01H               ; MyClassVirtual::`RTTI Complete Object Locator'
        DD      00H
        DD      00H
        DD      imagerel MyClassVirtual `RTTI Type Descriptor'
        DD      imagerel MyClassVirtual::`RTTI Class Hierarchy Descriptor'
        DD      imagerel const MyClassVirtual::`RTTI Complete Object Locator'
const MyClassVirtual::`vftable' DQ FLAT:const MyClassVirtual::`RTTI Complete Object Locator' ; MyClassVirtual::`vftable'
        DQ      FLAT:virtual int MyClassVirtual::get_num(int,int)
        DQ      FLAT:virtual void MyClassVirtual::reset_values(void)
this$ = 8
virtual void MyClassVirtual::reset_values(void) PROC          ; MyClassVirtual::reset_values, COMDAT
        mov     QWORD PTR [rsp+8], rcx
        mov     rax, QWORD PTR this$[rsp]
        mov     DWORD PTR [rax+8], 7
        ret     0
virtual void MyClassVirtual::reset_values(void) ENDP          ; MyClassVirtual::reset_values

vtable ptr 指向虚表,虚是一个函数地址 表,虚表指针在构造函数中初始化

1
2
3
const MyClassVirtual::`vftable' DQ FLAT:const MyClassVirtual::`RTTI Complete Object Locator' ; MyClassVirtual::`vftable'
        DQ      FLAT:virtual int MyClassVirtual::get_num(int,int)
        DQ      FLAT:virtual void MyClassVirtual::reset_values(void)

单继承内存布局

1
2
3
4
5
6
7
8
9
10
11
12
class MyClassVirtual{
int val;
public:
virtual int get_num(int x,int y);
virtual void reset_values(){var1=7;}
};

class MyClassVirtual2:public MyClassVirtual{
int var1;
public:
int get_values(){return var1;}
};

调用父类构造函数

1
2
3
4
5
6
7
8
9
main    PROC
$LN3:
        sub     rsp, 56                             ; 00000038H
        lea     rcx, QWORD PTR v$[rsp]
        call    MyClassVirtual::MyClassVirtual(void)
        xor     eax, eax
        add     rsp, 56                             ; 00000038H
        ret     0
main    ENDP

多继承内存布局

1

STL函数识别

C++异常处理机制

C++异常处理是通过try...catch来实现的,语法结构:

1
2
3
4
5
6
7
8
9
10
try{
语句组
}
catch(异常类型){
异常处理代码
}
...
catch(异常类型){
异常处理代码
}

C++的异常处理try catch执行过程

  • 执行try块中的语句,如果执行的过程中没有异常抛出,那么执行完后就执行最后一个catch块后面的语句,所以catch块中的语句都不会被执行。
  • 如果try块执行的过程中抛出了异常,那么抛出异常后立即跳转到第一个 “异常类型” 和抛出的异常类型匹配的catch块中执行(称作异常被该catch块 “捕获”),执行完后再跳转到最后一个catch块后面继续执行
  • C++异常的再抛出:如果一个函数在执行过程中抛出的异常在本函数内就被catch块捕获并处理,那么该异常就不会抛给这个函数的调用者,如果异常在本函数中没有被处理,则它会被抛给上一层的函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <string>
#include <stdio.h>
#include <iostream>

void catched_by_self(int i){
try{
}
catch(char){
}
catch(int)
}
void catched_by_before1(int i){
throw std::string("Fuck You\n");
printf("被抛到上层之后,函数后面的语句就不会被执行\n");
}

void catched_by_before2(){
thrwo 666;
printf("被抛到上层之后,函数后面的语句就不会被执行\n");
}

int mainf(){
int testnum=0x123;
try{
}
catch
}
  • thiscall
  • 虚表
  • 子类构造

例题

2024YLCTF

2024DASCTF ezcpp

2024Moectf

2024Newstar