c++反汇编揭秘

Contents

4. 观察各种表达式的求值过程

4.1 加法

release版

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>
 
int main(int argc, char* argv[]) {
    int n1 = argc;
    int n2 = argc;         //复写传播:n2等价于引用argc, n2则被删除
    n1 = n1 + 1;         //下一句n1重新赋值了,所以这句被删除了   
    n1 = 1 + 2;            //常量折叠:n1 = 3
    n1 = n1 + n2;        //常量传播和复写传播: n1 = 3 + argc
    printf("n1 = %d\n", n1);
    return 0;
}

ida

1
2
3
4
5
int __cdecl main(int argc, const char **argv, const char **envp)
{
  printf("n1 = %d\n", argc + 3);
  return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 mov     eax, [ebp+argc]
.text:00401046                 add     eax, 3
.text:00401049                 push    eax
.text:0040104A                 push    offset _Format  ; "n1 = %d\n"
.text:0040104F                 call    _printf
.text:00401054                 add     esp, 8
.text:00401057                 xor     eax, eax
.text:00401059                 pop     ebp
.text:0040105A                 retn
.text:0040105A _main           endp

4.2 减法

release版

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>

int main(int argc, char* argv[]) {
	int n1 = argc;
	int n2 = 0;
	scanf_s("%d", &n2);
	n1 = n1 - 100;
	n1 = n1 + 5 - n2;       //n1 = n1 -95 - n2
	printf("n1 = %d \r\n", n1);
	return 0;
}

ida

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int n2; // [esp+0h] [ebp-4h]

  n2 = 0;
  scanf_s("%d", &n2);
  printf("n1 = %d \r\n", argc - n2 - 95);
  return 0;
}
 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
.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401090 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401090
.text:00401090 n2              = dword ptr -4
.text:00401090 argc            = dword ptr  8
.text:00401090 argv            = dword ptr  0Ch
.text:00401090 envp            = dword ptr  10h
.text:00401090
.text:00401090                 push    ebp
.text:00401091                 mov     ebp, esp
.text:00401093                 push    ecx
.text:00401094                 lea     eax, [ebp+n2]
.text:00401097                 mov     [ebp+n2], 0
.text:0040109E                 push    eax
.text:0040109F                 push    offset _Format  ; "%d"
.text:004010A4                 call    _scanf_s
.text:004010A9                 mov     eax, [ebp+argc]
.text:004010AC                 sub     eax, [ebp+n2]
.text:004010AF                 sub     eax, 5Fh
.text:004010B2                 push    eax
.text:004010B3                 push    offset aN1D     ; "n1 = %d \r\n"
.text:004010B8                 call    _printf
.text:004010BD                 add     esp, 10h
.text:004010C0                 xor     eax, eax
.text:004010C2                 mov     esp, ebp
.text:004010C4                 pop     ebp
.text:004010C5                 retn
.text:004010C5 _main           endp

4.3 乘法

release

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>
int main(int argc, char* argv[]) {
    int n1 = argc;
    int n2 = argc;
 
    printf("n1 * 15 = %d\n", n1 * 15);       //变量乘常量 ( 常量值为非 2 的幂 )
    printf("n1 * 16 = %d\n", n1 * 16);       //变量乘常量 ( 常量值为 2 的幂 )
    printf("2 * 2 = %d\n", 2 * 2);           //两常量相乘
    printf("n2 * 4 + 5 = %d\n", n2 * 4 + 5); //混合运算
    printf("n1 * n2 = %d\n", n1 * n2);       //两变量相乘
    return 0;
}

ida

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
  printf("n1 * 15 = %d\n", 15 * argc);
  printf("n1 * 16 = %d\n", 16 * argc);
  printf("2 * 2 = %d\n", 4);
  printf("n2 * 4 + 5 = %d\n", 4 * argc + 5);
  printf("n1 * n2 = %d\n", argc * argc);
  return 0;
}
 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
36
37
38
39
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 push    esi
.text:00401044                 mov     esi, [ebp+argc]
.text:00401047                 mov     eax, esi
.text:00401049                 shl     eax, 4
.text:0040104C                 sub     eax, esi
.text:0040104E                 push    eax
.text:0040104F                 push    offset _Format  ; "n1 * 15 = %d\n"
.text:00401054                 call    _printf
.text:00401059                 mov     eax, esi
.text:0040105B                 shl     eax, 4
.text:0040105E                 push    eax
.text:0040105F                 push    offset aN116D   ; "n1 * 16 = %d\n"
.text:00401064                 call    _printf
.text:00401069                 push    4
.text:0040106B                 push    offset a22D     ; "2 * 2 = %d\n"
.text:00401070                 call    _printf
.text:00401075                 lea     eax, ds:5[esi*4]
.text:0040107C                 push    eax
.text:0040107D                 push    offset aN245D   ; "n2 * 4 + 5 = %d\n"
.text:00401082                 call    _printf
.text:00401087                 imul    esi, esi
.text:0040108A                 push    esi
.text:0040108B                 push    offset aN1N2D   ; "n1 * n2 = %d\n"
.text:00401090                 call    _printf
.text:00401095                 add     esp, 28h
.text:00401098                 xor     eax, eax
.text:0040109A                 pop     esi
.text:0040109B                 pop     ebp
.text:0040109C                 retn
.text:0040109C _main           endp

4.4除法

常用指令

  • cdq:把eax的最高位填充到edx,如果eax ≥ 0edx = 0,如果eax < 0edx = 0xFFFFFFFF
  • sar:算术右移
  • shr:逻辑右移
  • neg:将操作数取反+1
  • div:无符号数除法
  • idiv:有符号除法
  • mul:无符号数乘法
  • imul:有符号数乘法

4.5 取模

release

1
2
3
4
5
int main(int argc, char* argv[]) {
	printf("%d", argc % 8); //变量模常量,常量为2的幂
	printf("%d", argc % 9); //变量模常量,常量为非2的幂
	return 0;
}

ida

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
  printf("%d", argc % 8);
  printf("%d", argc % 9);
  return 0;
}
 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
36
37
38
39
1040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 push    esi
.text:00401044                 mov     esi, [ebp+argc]
.text:00401047                 mov     eax, esi        ; = argc
.text:00401049                 and     eax, 80000007h  ; eax = argc & 7 (最高位1为了检查负数),统计低位保留多少1,即可得到k值
.text:0040104E                 jns     short loc_401055 ; if(argc >= 0) 则跳转
.text:00401050                 dec     eax             ; 将寄存器eax中的值减1
.text:00401051                 or      eax, 0FFFFFFF8h ; 统计低位保存多少个0,即可得到k值
.text:00401054                 inc     eax             ; if(argc < 0) eax = ((argc & 7) - 1 | -7) + 1
.text:00401055
.text:00401055 loc_401055:                             ; CODE XREF: _main+E↑j
.text:00401055                 push    eax
.text:00401056                 push    offset _Format  ; "%d"
.text:0040105B                 call    _printf
.text:00401060                 mov     eax, 38E38E39h
.text:00401065                 imul    esi
.text:00401067                 sar     edx, 1
.text:00401069                 mov     eax, edx
.text:0040106B                 shr     eax, 1Fh
.text:0040106E                 add     eax, edx
.text:00401070                 lea     eax, [eax+eax*8]
.text:00401073                 sub     esi, eax
.text:00401075                 push    esi
.text:00401076                 push    offset _Format  ; "%d"
.text:0040107B                 call    _printf
.text:00401080                 add     esp, 10h
.text:00401083                 xor     eax, eax
.text:00401085                 pop     esi
.text:00401086                 pop     ebp
.text:00401087                 retn
.text:00401087 _main           endp

总结:

  1. 第一种对2的k次方取余:and eax,80000007h,去掉最高位保留低位,统计低位一个保留了多少1(7的二进制位0111,保留了3个1),即可得到k的值为3,然后得到结果:2^3 = 8
  2. 第二种对非2的k次方求余:[eax+eax*8] = eax*9,即可得到结果9

4.6 条件跳转指令表

4.7 条件表达式

4.7.1 相差为1

release

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(int argc, char* argv[])
{
	printf("%d\r\n", argc == 5 ? 5 : 6);

	return 0;
}

ida

1
2
3
4
5
int __cdecl main(int argc, const char **argv, const char **envp)
{
  printf("%d\r\n", (argc != 5) + 5);
  return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 xor     eax, eax
.text:00401045                 cmp     [ebp+argc], 5
.text:00401049                 setnz   al              ; if(argc == 5) a1 = 0, else a1 = 1
.text:0040104C                 add     eax, 5          ; 结合前面,可得:if(argc == 5) eax = 5, else exa = 5 + 1
.text:0040104F                 push    eax
.text:00401050                 push    offset _Format  ; "%d\r\n"
.text:00401055                 call    _printf
.text:0040105A                 add     esp, 8
.text:0040105D                 xor     eax, eax
.text:0040105F                 pop     ebp
.text:00401060                 retn
.text:00401060 _main           endp

4.7.2 相差大于1

release

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(int argc, char* argv[])
{
	printf("%d\r\n", argc == 5 ? 4 : 10);

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // eax

  v3 = 10;
  if ( argc == 5 )
    v3 = 4;
  printf("%d\r\n", v3);
  return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 cmp     [ebp+argc], 5
.text:00401047                 mov     ecx, 4
.text:0040104C                 mov     eax, 0Ah
.text:00401051                 cmovz   eax, ecx        ; if(ZF == 1) eax = ecx = 4, else eax = 10
.text:00401054                 push    eax
.text:00401055                 push    offset _Format  ; "%d\r\n"
.text:0040105A                 call    _printf
.text:0040105F                 add     esp, 8
.text:00401062                 xor     eax, eax
.text:00401064                 pop     ebp
.text:00401065                 retn
.text:00401065 _main           endp

4.7.3 变量表达式

release

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(int argc, char* argv[]) {
	int n1, n2;
	scanf_s("%d %d", &n1, &n2);
	printf("%d\n", argc ? n1 : n2);
	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax
  int n1; // [esp+0h] [ebp-8h]
  int n2; // [esp+4h] [ebp-4h]

  scanf_s("%d %d", &n1, &n2);
  v3 = n2;
  if ( argc )
    v3 = n1;
  printf("%d\n", v3);
  return 0;
}
 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
.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401090 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401090
.text:00401090 n1              = dword ptr -8
.text:00401090 n2              = dword ptr -4
.text:00401090 argc            = dword ptr  8
.text:00401090 argv            = dword ptr  0Ch
.text:00401090 envp            = dword ptr  10h
.text:00401090
.text:00401090                 push    ebp
.text:00401091                 mov     ebp, esp
.text:00401093                 sub     esp, 8
.text:00401096                 lea     eax, [ebp+n2]
.text:00401099                 push    eax
.text:0040109A                 lea     eax, [ebp+n1]
.text:0040109D                 push    eax
.text:0040109E                 push    offset _Format  ; "%d %d"
.text:004010A3                 call    _scanf_s
.text:004010A8                 mov     eax, [ebp+n2]
.text:004010AB                 cmp     [ebp+argc], 0
.text:004010AF                 cmovnz  eax, [ebp+n1]   ; if(argc != 0) eax = n1, else eax = n2
.text:004010B3                 push    eax
.text:004010B4                 push    offset aD       ; "%d\n"
.text:004010B9                 call    _printf
.text:004010BE                 add     esp, 14h
.text:004010C1                 xor     eax, eax
.text:004010C3                 mov     esp, ebp
.text:004010C5                 pop     ebp
.text:004010C6                 retn
.text:004010C6 _main           endp

4.7.4 表达式无优化使用分支

release

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(int argc, char* argv[]) {
	int n1, n2;
	scanf_s("%d %d", &n1, &n2);
	printf("%d\n", argc ? n1 : n2 + 3);
	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int n2; // [esp+0h] [ebp-8h]
  int n1; // [esp+4h] [ebp-4h]

  scanf_s("%d %d", &n1, &n2);
  if ( argc )
    printf("%d\n", n1);
  else
    printf("%d\n", n2 + 3);
  return 0;
}
 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
36
37
38
39
40
41
42
43
44
.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401090 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401090
.text:00401090 n2              = dword ptr -8
.text:00401090 n1              = dword ptr -4
.text:00401090 argc            = dword ptr  8
.text:00401090 argv            = dword ptr  0Ch
.text:00401090 envp            = dword ptr  10h
.text:00401090
.text:00401090                 push    ebp
.text:00401091                 mov     ebp, esp
.text:00401093                 sub     esp, 8
.text:00401096                 lea     eax, [ebp+n2]
.text:00401099                 push    eax
.text:0040109A                 lea     eax, [ebp+n1]
.text:0040109D                 push    eax
.text:0040109E                 push    offset _Format  ; "%d %d"
.text:004010A3                 call    _scanf_s
.text:004010A8                 add     esp, 0Ch
.text:004010AB                 cmp     [ebp+argc], 0
.text:004010AF                 jz      short loc_4010C8 ; avgc == 0 的流程
.text:004010B1                 mov     eax, [ebp+n1]   ; avgc != 0 的流程
.text:004010B4                 push    eax
.text:004010B5                 push    offset aD       ; "%d\n"
.text:004010BA                 call    _printf
.text:004010BF                 add     esp, 8
.text:004010C2                 xor     eax, eax
.text:004010C4                 mov     esp, ebp
.text:004010C6                 pop     ebp
.text:004010C7                 retn
.text:004010C8 ; ---------------------------------------------------------------------------
.text:004010C8
.text:004010C8 loc_4010C8:                             ; CODE XREF: _main+1F↑j
.text:004010C8                 mov     eax, [ebp+n2]   ; avgc == 0 的流程
.text:004010CB                 add     eax, 3
.text:004010CE                 push    eax
.text:004010CF                 push    offset aD       ; "%d\n"
.text:004010D4                 call    _printf
.text:004010D9                 add     esp, 8
.text:004010DC                 xor     eax, eax
.text:004010DE                 mov     esp, ebp
.text:004010E0                 pop     ebp
.text:004010E1                 retn
.text:004010E1 _main           endp

5. 流程控制语句的识别

5.1 if语句

release

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main(int argc, char* argv[]) {
	if (argc == 0) {
		printf("argc == 0");
	}

	return 0;
}

ida

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
  if ( !argc )
    printf("argc == 0");
  return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F5↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 cmp     [ebp+argc], 0
.text:00401047                 jnz     short loc_401056 ; if(argc != 0)  则跳转
.text:00401049                 push    offset _Format  ; "argc == 0"
.text:0040104E                 call    _printf
.text:00401053                 add     esp, 4
.text:00401056
.text:00401056 loc_401056:                             ; CODE XREF: _main+7↑j
.text:00401056                 xor     eax, eax
.text:00401058                 pop     ebp
.text:00401059                 retn
.text:00401059 _main           endp

通过汇编可以发现,if语句转换的条件跳转指令与if语句的判断结果是相反的。

根据这一特性,如果将if语句中的比较条件argc == 0修改为if(argc > 0),则其对应的汇编语言使用的条件跳转指令会是“小于等于0”。

release

1
2
3
4
5
6
7
8
9
#include <stdio.h>

int main(int argc, char* argv[]) {
	if (argc > 0) {
		printf("%d\n", argc);
	}

	return 0;
}

ida

1
2
3
4
5
6
int __cdecl main(int argc, const char **argv, const char **envp)
{
  if ( argc > 0 )
    printf("%d\n", argc);
  return 0;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F5↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 mov     eax, [ebp+argc]
.text:00401046                 test    eax, eax        ; 检测eax的值是否为零
.text:00401048                 jle     short loc_401058 ; if(argc <= 0) 则跳转
.text:0040104A                 push    eax
.text:0040104B                 push    offset _Format  ; "%d\n"
.text:00401050                 call    _printf
.text:00401055                 add     esp, 8
.text:00401058
.text:00401058 loc_401058:                             ; CODE XREF: _main+8↑j
.text:00401058                 xor     eax, eax
.text:0040105A                 pop     ebp
.text:0040105B                 retn
.text:0040105B _main           endp

总结:

5.2 if else语句

release

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>
 
int main(int argc, char* argv[]) {
    if (argc > 0) {
        printf("argc > 0");
    }
    else if (argc == 0) {
        printf("argc == 0");
    }
    else {
        printf("argc < 0");
    }
    return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax

  if ( argc <= 0 )
  {
    if ( argc )
      printf("argc < 0");
    else
      printf("argc == 0");
    result = 0;
  }
  else
  {
    printf("argc > 0");
    result = 0;
  }
  return result;
}
 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
36
37
38
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 mov     eax, [ebp+argc]
.text:00401046                 test    eax, eax
.text:00401048                 jle     short loc_40105B ; argc <= 0 跳转
.text:0040104A                 push    offset _Format  ; "argc > 0"
.text:0040104F                 call    _printf
.text:00401054                 add     esp, 4
.text:00401057                 xor     eax, eax
.text:00401059                 pop     ebp
.text:0040105A                 retn
.text:0040105B ; ---------------------------------------------------------------------------
.text:0040105B
.text:0040105B loc_40105B:                             ; CODE XREF: _main+8↑j
.text:0040105B                 jnz     short loc_40106E ; argc != 0 跳转
.text:0040105D                 push    offset aArgc0_0 ; "argc == 0"
.text:00401062                 call    _printf
.text:00401067                 add     esp, 4
.text:0040106A                 xor     eax, eax
.text:0040106C                 pop     ebp
.text:0040106D                 retn
.text:0040106E ; ---------------------------------------------------------------------------
.text:0040106E
.text:0040106E loc_40106E:                             ; CODE XREF: _main:loc_40105B↑j
.text:0040106E                 push    offset aArgc0_1 ; "argc < 0"
.text:00401073                 call    _printf
.text:00401078                 add     esp, 4
.text:0040107B                 xor     eax, eax
.text:0040107D                 pop     ebp
.text:0040107E                 retn
.text:0040107E _main           endp

5.3 switch语句

5.3.1 分支少于4个

release

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

int main(int argc, char* argv[]) {
	int n = 1;
	scanf_s("%d", &n);

	switch (n) {
	case 1:
		printf("n == 1");
		break;
	case 3:
		printf("n == 3");
		break;
	case 100:
		printf("n == 100");
		break;
	}

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int n; // [esp+0h] [ebp-4h]

  n = 1;
  scanf_s("%d", &n);
  switch ( n )
  {
    case 1:
      printf("n == 1");
      break;
    case 3:
      printf("n == 3");
      return 0;
    case 100:
      printf("n == 100");
      return 0;
  }
  return 0;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401090 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401090
.text:00401090 n               = dword ptr -4
.text:00401090 argc            = dword ptr  8
.text:00401090 argv            = dword ptr  0Ch
.text:00401090 envp            = dword ptr  10h
.text:00401090
.text:00401090                 push    ebp
.text:00401091                 mov     ebp, esp
.text:00401093                 push    ecx
.text:00401094                 lea     eax, [ebp+n]
.text:00401097                 mov     [ebp+n], 1      ; n = 1
.text:0040109E                 push    eax
.text:0040109F                 push    offset _Format  ; "%d"
.text:004010A4                 call    _scanf_s
.text:004010A9                 mov     eax, [ebp+n]    ; eax = n
.text:004010AC                 add     esp, 8
.text:004010AF                 sub     eax, 1
.text:004010B2                 jz      short loc_4010E4 ; eax == 1 跳转
.text:004010B4                 sub     eax, 2
.text:004010B7                 jz      short loc_4010D1 ; eax == 3 跳转
.text:004010B9                 sub     eax, 61h
.text:004010BC                 jnz     short loc_4010F1 ; eax != 100 跳转
.text:004010BE                 push    offset aN100    ; "n == 100"
.text:004010C3                 call    _printf
.text:004010C8                 add     esp, 4
.text:004010CB                 xor     eax, eax
.text:004010CD                 mov     esp, ebp
.text:004010CF                 pop     ebp
.text:004010D0                 retn
.text:004010D1 ; ---------------------------------------------------------------------------
.text:004010D1
.text:004010D1 loc_4010D1:                             ; CODE XREF: _main+27↑j
.text:004010D1                 push    offset aN3      ; "n == 3"
.text:004010D6                 call    _printf
.text:004010DB                 add     esp, 4
.text:004010DE                 xor     eax, eax
.text:004010E0                 mov     esp, ebp
.text:004010E2                 pop     ebp
.text:004010E3                 retn
.text:004010E4 ; ---------------------------------------------------------------------------
.text:004010E4
.text:004010E4 loc_4010E4:                             ; CODE XREF: _main+22↑j
.text:004010E4                 push    offset aN1      ; "n == 1"
.text:004010E9                 call    _printf
.text:004010EE                 add     esp, 4
.text:004010F1
.text:004010F1 loc_4010F1:                             ; CODE XREF: _main+2C↑j
.text:004010F1                 xor     eax, eax
.text:004010F3                 mov     esp, ebp
.text:004010F5                 pop     ebp
.text:004010F6                 retn
.text:004010F6 _main           endp

5.3.2 分支大于4个且值连续

会对case语句块制作地址表,以减少比较跳转次数

release

 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
#include <stdio.h>

int main(int argc, char* argv[]) {
	int n = 1;
	scanf_s("%d", &n);
	switch (n) {
	case 1:
		printf("n == 1");
		break;
	case 2:
		printf("n == 2");
		break;
	case 3:
		printf("n == 3");
		break;
	case 5:
		printf("n == 5");
		break;
	case 6:
		printf("n == 6");
		break;
	case 7:
		printf("n == 7");
		break;
	}

	return 0;
}

ida

 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
36
37
38
39
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int n; // [esp+0h] [ebp-4h]

  n = 1;
  scanf_s("%d", &n);
  switch ( n )
  {
    case 1:
      printf("n == 1");
      result = 0;
      break;
    case 2:
      printf("n == 2");
      result = 0;
      break;
    case 3:
      printf("n == 3");
      result = 0;
      break;
    case 5:
      printf("n == 5");
      result = 0;
      break;
    case 6:
      printf("n == 6");
      result = 0;
      break;
    case 7:
      printf("n == 7");
      goto LABEL_8;
    default:
LABEL_8:
      result = 0;
      break;
  }
  return result;
}
  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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401090 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401090
.text:00401090 n               = dword ptr -4
.text:00401090 argc            = dword ptr  8
.text:00401090 argv            = dword ptr  0Ch
.text:00401090 envp            = dword ptr  10h
.text:00401090
.text:00401090                 push    ebp
.text:00401091                 mov     ebp, esp
.text:00401093                 push    ecx
.text:00401094                 lea     eax, [ebp+n]
.text:00401097                 mov     [ebp+n], 1      ; n = 1
.text:0040109E                 push    eax
.text:0040109F                 push    offset _Format  ; "%d"
.text:004010A4                 call    _scanf_s
.text:004010A9                 mov     eax, [ebp+n]
.text:004010AC                 add     esp, 8
.text:004010AF                 dec     eax             ; exa -= 1
.text:004010B0                 cmp     eax, 6          ; switch 7 cases
.text:004010B3                 ja      short default_case ; jumptable 004010B5 default case
.text:004010B5                 jmp     ds:off_401130[eax*4] ; switch jump
.text:004010BC ; ---------------------------------------------------------------------------
.text:004010BC
.text:004010BC case_1:                                 ; CODE XREF: _main+25↑j
.text:004010BC                                         ; DATA XREF: _main:off_401130↓o
.text:004010BC                 push    offset aN1      ; jumptable 004010B5 case 0
.text:004010C1                 call    _printf
.text:004010C6                 add     esp, 4
.text:004010C9                 xor     eax, eax
.text:004010CB                 mov     esp, ebp
.text:004010CD                 pop     ebp
.text:004010CE                 retn
.text:004010CF ; ---------------------------------------------------------------------------
.text:004010CF
.text:004010CF case_2:                                 ; CODE XREF: _main+25↑j
.text:004010CF                                         ; DATA XREF: _main:off_401130↓o
.text:004010CF                 push    offset aN2      ; jumptable 004010B5 case 1
.text:004010D4                 call    _printf
.text:004010D9                 add     esp, 4
.text:004010DC                 xor     eax, eax
.text:004010DE                 mov     esp, ebp
.text:004010E0                 pop     ebp
.text:004010E1                 retn
.text:004010E2 ; ---------------------------------------------------------------------------
.text:004010E2
.text:004010E2 case_3:                                 ; CODE XREF: _main+25↑j
.text:004010E2                                         ; DATA XREF: _main:off_401130↓o
.text:004010E2                 push    offset aN3      ; jumptable 004010B5 case 2
.text:004010E7                 call    _printf
.text:004010EC                 add     esp, 4
.text:004010EF                 xor     eax, eax
.text:004010F1                 mov     esp, ebp
.text:004010F3                 pop     ebp
.text:004010F4                 retn
.text:004010F5 ; ---------------------------------------------------------------------------
.text:004010F5
.text:004010F5 case_5:                                 ; CODE XREF: _main+25↑j
.text:004010F5                                         ; DATA XREF: _main:off_401130↓o
.text:004010F5                 push    offset aN5      ; jumptable 004010B5 case 4
.text:004010FA                 call    _printf
.text:004010FF                 add     esp, 4
.text:00401102                 xor     eax, eax
.text:00401104                 mov     esp, ebp
.text:00401106                 pop     ebp
.text:00401107                 retn
.text:00401108 ; ---------------------------------------------------------------------------
.text:00401108
.text:00401108 case_6:                                 ; CODE XREF: _main+25↑j
.text:00401108                                         ; DATA XREF: _main:off_401130↓o
.text:00401108                 push    offset aN6      ; jumptable 004010B5 case 5
.text:0040110D                 call    _printf
.text:00401112                 add     esp, 4
.text:00401115                 xor     eax, eax
.text:00401117                 mov     esp, ebp
.text:00401119                 pop     ebp
.text:0040111A                 retn
.text:0040111B ; ---------------------------------------------------------------------------
.text:0040111B
.text:0040111B case_7:                                 ; CODE XREF: _main+25↑j
.text:0040111B                                         ; DATA XREF: _main:off_401130↓o
.text:0040111B                 push    offset aN7      ; jumptable 004010B5 case 6
.text:00401120                 call    _printf
.text:00401125                 add     esp, 4
.text:00401128
.text:00401128 default_case:                           ; CODE XREF: _main+23↑j
.text:00401128                                         ; _main+25↑j
.text:00401128                                         ; DATA XREF: ...
.text:00401128                 xor     eax, eax        ; jumptable 004010B5 default case
.text:0040112A                 mov     esp, ebp
.text:0040112C                 pop     ebp
.text:0040112D                 retn
.text:0040112D ; ---------------------------------------------------------------------------
.text:0040112E                 align 10h               ; 将当前位置调整为16字节对齐
.text:00401130 off_401130      dd offset case_1        ; DATA XREF: _main+25↑r
.text:00401130                 dd offset case_2        ; jump table for switch statement
.text:00401130                 dd offset case_3
.text:00401130                 dd offset default_case
.text:00401130                 dd offset case_5
.text:00401130                 dd offset case_6
.text:00401130                 dd offset case_7
.text:00401130 _main           endp

5.3.3 分支大于4个,值不连续,且最大case值和case值的差小于256

有两张表

  • case语句块地址表:每一项保存一个case语句块的首地址,有几个case就有几项,default也在里面。
  • case语句块索引表:保存地址表的编号,索引表的大小等于最大case值和最小case值的差。

release

 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 <stdio.h>
 
int main(int argc, char* argv[]) {
    int n = 1;
    scanf_s("%d", &n);
    switch (n) {
    case 1:
        printf("n == 1");
        break;
    case 2:
        printf("n == 2");
        break;
    case 3:
        printf("n == 3");
        break;
    case 5:
        printf("n == 5");
        break;
    case 6:
        printf("n == 6");
        break;
    case 255:
        printf("n == 255");
        break;
    }
    return 0;
}

ida

 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
36
37
38
39
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int n; // [esp+0h] [ebp-4h]

  n = 1;
  scanf_s("%d", &n);
  switch ( n )
  {
    case 1:
      printf("n == 1");
      result = 0;
      break;
    case 2:
      printf("n == 2");
      result = 0;
      break;
    case 3:
      printf("n == 3");
      result = 0;
      break;
    case 5:
      printf("n == 5");
      result = 0;
      break;
    case 6:
      printf("n == 6");
      result = 0;
      break;
    case 255:
      printf("n == 255");
      goto LABEL_8;
    default:
LABEL_8:
      result = 0;
      break;
  }
  return result;
}
  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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401090 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401090
.text:00401090 n               = dword ptr -4
.text:00401090 argc            = dword ptr  8
.text:00401090 argv            = dword ptr  0Ch
.text:00401090 envp            = dword ptr  10h
.text:00401090
.text:00401090                 push    ebp
.text:00401091                 mov     ebp, esp
.text:00401093                 push    ecx
.text:00401094                 lea     eax, [ebp+n]
.text:00401097                 mov     [ebp+n], 1      ; n = 1
.text:0040109E                 push    eax
.text:0040109F                 push    offset _Format  ; "%d"
.text:004010A4                 call    _scanf_s
.text:004010A9                 mov     eax, [ebp+n]
.text:004010AC                 add     esp, 8
.text:004010AF                 dec     eax             ; exa -= 1
.text:004010B0                 cmp     eax, 0FEh       ; switch 255 cases
.text:004010B5                 ja      short default_case ; n > 255 直接跳转到 default
.text:004010B7                 movzx   eax, byte ptr ds:Index_table[eax] ; 将源操作数中的数据复制到目标操作数中,并将目标操作数的高位清零
.text:004010BE                 jmp     dword ptr ds:Address_table[eax*4] ; switch jump
.text:004010C5 ; ---------------------------------------------------------------------------
.text:004010C5
.text:004010C5 case_1:                                 ; CODE XREF: _main+2E↑j
.text:004010C5                                         ; DATA XREF: _main:Address_table↓o
.text:004010C5                 push    offset aN1      ; jumptable 004010BE case 0
.text:004010CA                 call    _printf
.text:004010CF                 add     esp, 4
.text:004010D2                 xor     eax, eax
.text:004010D4                 mov     esp, ebp
.text:004010D6                 pop     ebp
.text:004010D7                 retn
.text:004010D8 ; ---------------------------------------------------------------------------
.text:004010D8
.text:004010D8 case_2:                                 ; CODE XREF: _main+2E↑j
.text:004010D8                                         ; DATA XREF: _main:Address_table↓o
.text:004010D8                 push    offset aN2      ; jumptable 004010BE case 1
.text:004010DD                 call    _printf
.text:004010E2                 add     esp, 4
.text:004010E5                 xor     eax, eax
.text:004010E7                 mov     esp, ebp
.text:004010E9                 pop     ebp
.text:004010EA                 retn
.text:004010EB ; ---------------------------------------------------------------------------
.text:004010EB
.text:004010EB case_3:                                 ; CODE XREF: _main+2E↑j
.text:004010EB                                         ; DATA XREF: _main:Address_table↓o
.text:004010EB                 push    offset aN3      ; jumptable 004010BE case 2
.text:004010F0                 call    _printf
.text:004010F5                 add     esp, 4
.text:004010F8                 xor     eax, eax
.text:004010FA                 mov     esp, ebp
.text:004010FC                 pop     ebp
.text:004010FD                 retn
.text:004010FE ; ---------------------------------------------------------------------------
.text:004010FE
.text:004010FE case_5:                                 ; CODE XREF: _main+2E↑j
.text:004010FE                                         ; DATA XREF: _main:Address_table↓o
.text:004010FE                 push    offset aN5      ; jumptable 004010BE case 4
.text:00401103                 call    _printf
.text:00401108                 add     esp, 4
.text:0040110B                 xor     eax, eax
.text:0040110D                 mov     esp, ebp
.text:0040110F                 pop     ebp
.text:00401110                 retn
.text:00401111 ; ---------------------------------------------------------------------------
.text:00401111
.text:00401111 case_6:                                 ; CODE XREF: _main+2E↑j
.text:00401111                                         ; DATA XREF: _main:Address_table↓o
.text:00401111                 push    offset aN6      ; jumptable 004010BE case 5
.text:00401116                 call    _printf
.text:0040111B                 add     esp, 4
.text:0040111E                 xor     eax, eax
.text:00401120                 mov     esp, ebp
.text:00401122                 pop     ebp
.text:00401123                 retn
.text:00401124 ; ---------------------------------------------------------------------------
.text:00401124
.text:00401124 case_255:                               ; CODE XREF: _main+2E↑j
.text:00401124                                         ; DATA XREF: _main:Address_table↓o
.text:00401124                 push    offset aN255    ; jumptable 004010BE case 254
.text:00401129                 call    _printf
.text:0040112E                 add     esp, 4
.text:00401131
.text:00401131 default_case:                           ; CODE XREF: _main+25↑j
.text:00401131                                         ; _main+2E↑j
.text:00401131                                         ; DATA XREF: ...
.text:00401131                 xor     eax, eax        ; jumptable 004010BE default case
.text:00401133                 mov     esp, ebp
.text:00401135                 pop     ebp
.text:00401136                 retn
.text:00401136 ; ---------------------------------------------------------------------------
.text:00401137                 align 4                 ; 将当前位置调整为4字节对齐
.text:00401138 Address_table:                          ; DATA XREF: _main+2E↑r
.text:00401138                 dd offset case_1, offset case_2, offset case_3, offset case_5 ; jump table for switch statement
.text:00401138                 dd offset case_6, offset case_255, offset default_case
.text:00401154 Index_table:                            ; DATA XREF: _main+27↑r
.text:00401154                 db 0, 1, 2, 6, 3, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 ; indirect table for switch statement
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
.text:00401154                 db 6, 6, 5
.text:00401154 _main           endp

5.3.4 分支大于4个,值不连续,且最大case值和case值的差大于256

将每个case值作为一个节点,找到这些节点的中间值作为跟节点,形成一颗平衡二叉树,以每个节点作为判定值,大于和小于关系分别对应左子树和右子树。

release

 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
#include <stdio.h>

int main(int argc, char* argv[]) {
	int n = 0;
	scanf_s("%d", &n);
	switch (n) {
	case 2:
		printf("n == 2\n");
		break;
	case 3:
		printf("n == 3\n");
		break;
	case 8:
		printf("n == 8\n");
		break;
	case 10:
		printf("n == 10\n");
		break;
	case 35:
		printf("n == 35\n");
		break;
	case 37:
		printf("n == 37\n");
		break;
	case 666:
		printf("n == 666\n");
		break;
	default:
		printf("default\n");
		break;
	}
	return 0;
}

ida

 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
36
37
38
39
40
41
42
43
44
45
46
47
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int n; // [esp+0h] [ebp-4h]

  n = 0;
  scanf_s("%d", &n);
  if ( n <= 10 )
  {
    switch ( n )
    {
      case 10:
        printf("n == 10\n");
        return 0;
      case 2:
        printf("n == 2\n");
        return 0;
      case 3:
        printf("n == 3\n");
        return 0;
      case 8:
        printf("n == 8\n");
        return 0;
    }
    goto default;
  }
  switch ( n )
  {
    case 35:
      printf("n == 35\n");
      result = 0;
      break;
    case 37:
      printf("n == 37\n");
      result = 0;
      break;
    case 666:
      printf("n == 666\n");
      result = 0;
      break;
    default:
default:
      printf("default\n");
      return 0;
  }
  return result;
}
  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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401090 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401090
.text:00401090 n               = dword ptr -4
.text:00401090 argc            = dword ptr  8
.text:00401090 argv            = dword ptr  0Ch
.text:00401090 envp            = dword ptr  10h
.text:00401090
.text:00401090                 push    ebp
.text:00401091                 mov     ebp, esp
.text:00401093                 push    ecx
.text:00401094                 lea     eax, [ebp+n]
.text:00401097                 mov     [ebp+n], 0      ; n = 1
.text:0040109E                 push    eax
.text:0040109F                 push    offset _Format  ; "%d"
.text:004010A4                 call    _scanf_s
.text:004010A9                 mov     eax, [ebp+n]    ; exa = n
.text:004010AC                 add     esp, 8
.text:004010AF                 cmp     eax, 0Ah
.text:004010B2                 jg      short select_subcase
.text:004010B4                 jz      short case_10
.text:004010B6                 sub     eax, 2
.text:004010B9                 jz      short case_2
.text:004010BB                 sub     eax, 1
.text:004010BE                 jz      short case_3
.text:004010C0                 sub     eax, 5
.text:004010C3                 jnz     short default
.text:004010C5                 push    offset aN8      ; "n == 8\n"
.text:004010CA                 call    _printf
.text:004010CF                 add     esp, 4
.text:004010D2                 xor     eax, eax
.text:004010D4                 mov     esp, ebp
.text:004010D6                 pop     ebp
.text:004010D7                 retn
.text:004010D8 ; ---------------------------------------------------------------------------
.text:004010D8
.text:004010D8 case_3:                                 ; CODE XREF: _main+2E↑j
.text:004010D8                 push    offset aN3      ; "n == 3\n"
.text:004010DD                 call    _printf
.text:004010E2                 add     esp, 4
.text:004010E5                 xor     eax, eax
.text:004010E7                 mov     esp, ebp
.text:004010E9                 pop     ebp
.text:004010EA                 retn
.text:004010EB ; ---------------------------------------------------------------------------
.text:004010EB
.text:004010EB case_2:                                 ; CODE XREF: _main+29↑j
.text:004010EB                 push    offset aN2      ; "n == 2\n"
.text:004010F0                 call    _printf
.text:004010F5                 add     esp, 4
.text:004010F8                 xor     eax, eax
.text:004010FA                 mov     esp, ebp
.text:004010FC                 pop     ebp
.text:004010FD                 retn
.text:004010FE ; ---------------------------------------------------------------------------
.text:004010FE
.text:004010FE case_10:                                ; CODE XREF: _main+24↑j
.text:004010FE                 push    offset aN10     ; "n == 10\n"
.text:00401103                 call    _printf
.text:00401108                 add     esp, 4
.text:0040110B                 xor     eax, eax
.text:0040110D                 mov     esp, ebp
.text:0040110F                 pop     ebp
.text:00401110                 retn
.text:00401111 ; ---------------------------------------------------------------------------
.text:00401111
.text:00401111 select_subcase:                         ; CODE XREF: _main+22↑j
.text:00401111                 sub     eax, 23h
.text:00401114                 jz      short case_35
.text:00401116                 sub     eax, 2
.text:00401119                 jz      short case_37
.text:0040111B                 sub     eax, 275h
.text:00401120                 jz      short case_666
.text:00401122
.text:00401122 default:                                ; CODE XREF: _main+33↑j
.text:00401122                 push    offset aDefault ; "default\n"
.text:00401127                 call    _printf
.text:0040112C                 add     esp, 4
.text:0040112F                 xor     eax, eax
.text:00401131                 mov     esp, ebp
.text:00401133                 pop     ebp
.text:00401134                 retn
.text:00401135 ; ---------------------------------------------------------------------------
.text:00401135
.text:00401135 case_666:                               ; CODE XREF: _main+90↑j
.text:00401135                 push    offset aN666    ; "n == 666\n"
.text:0040113A                 call    _printf
.text:0040113F                 add     esp, 4
.text:00401142                 xor     eax, eax
.text:00401144                 mov     esp, ebp
.text:00401146                 pop     ebp
.text:00401147                 retn
.text:00401148 ; ---------------------------------------------------------------------------
.text:00401148
.text:00401148 case_37:                                ; CODE XREF: _main+89↑j
.text:00401148                 push    offset aN37     ; "n == 37\n"
.text:0040114D                 call    _printf
.text:00401152                 add     esp, 4
.text:00401155                 xor     eax, eax
.text:00401157                 mov     esp, ebp
.text:00401159                 pop     ebp
.text:0040115A                 retn
.text:0040115B ; ---------------------------------------------------------------------------
.text:0040115B
.text:0040115B case_35:                                ; CODE XREF: _main+84↑j
.text:0040115B                 push    offset aN35     ; "n == 35\n"
.text:00401160                 call    _printf
.text:00401165                 add     esp, 4
.text:00401168                 xor     eax, eax
.text:0040116A                 mov     esp, ebp
.text:0040116C                 pop     ebp
.text:0040116D                 retn
.text:0040116D _main           endp

5.4 do while语句

release

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>

int main(int argc, char* argv[]) {
	int sum = 0;
	int i = 0;
	do {
		sum += i;
		i++;
	} while (i <= argc);

	return sum;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax
  int v4; // ecx

  result = 0;
  v4 = 0;
  do
    result += v4++;
  while ( v4 <= argc );
  return result;
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401000
.text:00401000 argc            = dword ptr  8
.text:00401000 argv            = dword ptr  0Ch
.text:00401000 envp            = dword ptr  10h
.text:00401000
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 mov     edx, [ebp+argc] ; edx = argc
.text:00401006                 xor     eax, eax        ; eax = 0
.text:00401008                 xor     ecx, ecx        ; ecx = 0
.text:0040100A                 nop     word ptr [eax+eax+00h]
.text:00401010
.text:00401010 loc_401010:                             ; CODE XREF: _main+15↓j
.text:00401010                 add     eax, ecx        ; sum += i
.text:00401012                 inc     ecx             ; i++
.text:00401013                 cmp     ecx, edx
.text:00401015                 jle     short loc_401010 ; i <= argc 则跳转
.text:00401017                 pop     ebp
.text:00401018                 retn
.text:00401018 _main           endp

5.5 while语句

release

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <stdio.h>

int main(int argc, char* argv[])
{
	int sum = 0;
	int i = 0;
	while (i <= 100)
	{
		sum = sum + i;
		i++;
	}

	printf("%d\r\n", sum);

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ecx
  int v4; // edx
  signed int v5; // esi
  signed int v6; // eax

  v3 = 0;
  v4 = 0;
  v5 = 0;
  v6 = 0;
  do
  {
    v3 += v6;
    v4 += v6 + 1;
    v6 += 2;
  }
  while ( v6 <= 99 );
  if ( v6 <= 100 )
    v5 = v6;
  printf("%d\r\n", v5 + v4 + v3);
  return 0;
}
 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
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040
.text:00401040                 push    esi
.text:00401041                 xor     ecx, ecx
.text:00401043                 xor     edx, edx
.text:00401045                 xor     esi, esi
.text:00401047                 xor     eax, eax
.text:00401049                 nop     dword ptr [eax+00000000h]
.text:00401050
.text:00401050 loc_401050:                             ; CODE XREF: _main+1B↓j
.text:00401050                 inc     edx
.text:00401051                 add     ecx, eax
.text:00401053                 add     edx, eax
.text:00401055                 add     eax, 2          ; 步长为2
.text:00401058                 cmp     eax, 63h
.text:0040105B                 jle     short loc_401050
.text:0040105D                 cmp     eax, 64h
.text:00401060                 cmovle  esi, eax        ; if(eax <= 64h) esi = eax
.text:00401063                 lea     eax, [edx+ecx]
.text:00401066                 add     eax, esi
.text:00401068                 push    eax
.text:00401069                 push    offset _Format  ; "%d\r\n"
.text:0040106E                 call    _printf
.text:00401073                 add     esp, 8
.text:00401076                 xor     eax, eax
.text:00401078                 pop     esi
.text:00401079                 retn
.text:00401079 _main           endp

5.6 for语句

release

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <stdio.h>

int main(int argc, char* argv[])
{
    int sum = 0;
 
    //内部会优化,把步长改为4,减少循环次数
    for (int n = 1; n <= 100; n++)
    {
        sum = sum + n;
    }
 
    printf("%d\r\n", sum);
 
    return 0;
}

ida

 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
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // ecx
  int v4; // edx
  int v5; // esi
  signed int v6; // eax
  int v7; // edi

  v3 = 0;
  v4 = 0;
  v5 = 0;
  v6 = 1;
  v7 = 0;
  do
  {
    v3 += v6;
    v7 += v6 + 1;
    v5 += v6 + 2;
    v4 += v6 + 3;
    v6 += 4;
  }
  while ( v6 <= 100 );
  printf("%d\r\n", v7 + v4 + v5 + v3);
  return 0;
}
 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
36
37
38
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040
.text:00401040                 push    esi
.text:00401041                 push    edi
.text:00401042                 xor     ecx, ecx
.text:00401044                 xor     edx, edx
.text:00401046                 xor     esi, esi
.text:00401048                 mov     eax, 1
.text:0040104D                 xor     edi, edi
.text:0040104F                 nop
.text:00401050
.text:00401050 loc_401050:                             ; CODE XREF: _main+25↓j
.text:00401050                 inc     edi
.text:00401051                 add     esi, 2
.text:00401054                 add     edx, 3
.text:00401057                 add     ecx, eax
.text:00401059                 add     edi, eax
.text:0040105B                 add     esi, eax
.text:0040105D                 add     edx, eax
.text:0040105F                 add     eax, 4          ; 步长为4,减少循环次数
.text:00401062                 cmp     eax, 64h
.text:00401065                 jle     short loc_401050
.text:00401067                 lea     eax, [edx+esi]
.text:0040106A                 add     eax, edi
.text:0040106C                 add     ecx, eax
.text:0040106E                 push    ecx
.text:0040106F                 push    offset _Format  ; "%d\r\n"
.text:00401074                 call    _printf
.text:00401079                 add     esp, 8
.text:0040107C                 xor     eax, eax
.text:0040107E                 pop     edi
.text:0040107F                 pop     esi
.text:00401080                 retn
.text:00401080 _main           endp

6. 函数的工作原理

6.1 栈帧的形成和关闭

栈在内存中是一块特殊的存储空间,它的存储原则是“先进后出”,即最先被存储的数据最后被释放。汇编过程通常使用PUSH指令与POP指令对栈空间执行数据压入和数据弹出的操作。

栈结构在内存中占用一段连续的存储空间,通过esp和ebp这两个栈指针寄存器保存当前栈的起始地址与结束地址(又称为栈顶与栈底)。在32位程序的栈结构中,每4字节的栈空间保存一个数据;在64位程序的栈结构中,每8字节的栈空间保存一个数据。像这样的栈顶到栈底之间的存储空间被称为栈帧

栈帧是如何形成的呢?当栈顶指针esp小于栈底指针ebp时,就形成了栈帧。通常,在C++中,栈帧可以寻址局部变量、函数返回地址、函数参数等数据。

不同的两次函数调用,形成的栈帧也不相同。当一个函数调用另一个函数时,就会针对调用的函数开辟所需的栈空间,形成此函数的栈帧。当这个函数结束调用时,需要清除它使用的栈空间,关闭栈帧,我们把这一过程称为栈平衡。

如果某一函数开辟了新的栈空间后没有进行恢复,或者过度恢复,就会造成栈空间上溢或下溢,极有可能给程序带来致命错误。

6.2 各种调用方式的考察

C++环境下的调用约定有3种:

  • _cdecl:C\C++默认的调用方式,调用方平衡栈,不定参数的函数可以使用这种方式。(外平栈,按从右至左的顺序压参数入栈)
  • _stdcall:被调方平衡栈,不定参数的函数无法使用这种方式。(内平栈,按从右至左的顺序压参数入栈)
  • _fastcall::寄存器方式传参,被调方平衡栈,不定参数的函数无法使用这种方式。(前两个参数用ecx和edx传参,其余参数通过栈传参方式,按从右至左的顺序压参数入栈)

当函数参数个数为0时,无须区分调用方式,使用_cdecl和_stdcall都一样。而大部分函数都是有参数的,通过查看平衡栈即可还原对应的调用方式。我们通过代码清单6-1分析_cdecl与_stdcall这两种调用方式的区别。

6.3 函数的参数

release

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>

void addNumber(int n1) {
	n1 += 1;
	printf("%d\n", n1);
}

int main(int argc, char* argv[]) {
	int n = 0;
	scanf_s("%d", &n); // 防止变量被常量扩散优化
	addNumber(n);

	return 0;
}

ida

1
2
3
4
5
6
7
8
9
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int n; // [esp+0h] [ebp-4h]

  n = 0;
  scanf_s("%d", &n);
  printf("%d\n", n + 1);
  return 0;
}
 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
.text:00401090 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401090 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401090
.text:00401090 n               = dword ptr -4
.text:00401090 argc            = dword ptr  8
.text:00401090 argv            = dword ptr  0Ch
.text:00401090 envp            = dword ptr  10h
.text:00401090
.text:00401090                 push    ebp
.text:00401091                 mov     ebp, esp
.text:00401093                 push    ecx
.text:00401094                 lea     eax, [ebp+n]
.text:00401097                 mov     [ebp+n], 0      ; n = 0
.text:0040109E                 push    eax
.text:0040109F                 push    offset _Format  ; "%d"
.text:004010A4                 call    _scanf_s
.text:004010A9                 mov     eax, [ebp+n]    ; exa = n
.text:004010AC                 inc     eax
.text:004010AD                 push    eax
.text:004010AE                 push    offset aD_0     ; "%d\n"
.text:004010B3                 call    _printf
.text:004010B8                 add     esp, 10h
.text:004010BB                 xor     eax, eax
.text:004010BD                 mov     esp, ebp
.text:004010BF                 pop     ebp
.text:004010C0                 retn
.text:004010C0 _main           endp

7. 变量在内存中的位置和访问方式

变量的作用域

  • 全局变量:属于进程作用域,整个进程都能够访问到
  • 静态变量:属于文件作用域,在当前源码文件内可以访问到
  • 局部变量:属于函数作用域,在函数内可以访问到

7.1 全局变量和局部变量的区别

全局变量和局部变量的区别

  • 全局变量:可以在程序中的任何位置使用
  • 局部变量:局限于函数作用域内,若超出作用域,则由栈平衡操作释放局方局部变量的空间
  • 局部变量:通过申请栈空间存放,利用栈指针ebp或esp间接访问,其地址是一个未知可变值
  • 全局变量:与常量类似,通过立即数访问

7.2 局部静态变量的工作方式

局部静态变量

  • 存放在静态存储区
  • 作用域:所定义的函数
  • 生命周期:持续到程序结束
  • 只初始化一次

release版

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>
 
void showStatic(int n) {
    static int g_static = n;    //定义局部静态变量,赋值为参数
    printf("%d\n", g_static);    //显示静态变量
}
int main(int argc, char* argv[]) {
    for (int i = 0; i < 5; i++) {
        showStatic(i);        //循环调用显示局部静态变量的函数,每次传入不同值
    }
    return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // esi
  int v4; // edi

  v3 = 0;
  v4 = *(_DWORD *)__readfsdword(0x2Cu);
  do
  {
    if ( pOnce > *(_DWORD *)(v4 + 4) )
    {
      _Init_thread_header(&pOnce);
      if ( pOnce == -1 )
      {
        dword_4033B0 = v3;
        _Init_thread_footer(&pOnce);
      }
    }
    printf("%d\n", dword_4033B0);
    ++v3;
  }
  while ( v3 < 5 );
  return 0;
}
 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
36
37
38
39
40
41
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040
.text:00401040                 mov     eax, large fs:2Ch
.text:00401046                 push    esi
.text:00401047                 push    edi
.text:00401048                 xor     esi, esi        ; i = 0
.text:0040104A                 mov     edi, [eax]
.text:0040104C                 nop     dword ptr [eax+00h]
.text:00401050
.text:00401050 loc_401050:                             ; CODE XREF: _main+5D↓j
.text:00401050                 mov     eax, pOnce
.text:00401055                 cmp     eax, [edi+4]
.text:0040105B                 jle     short loc_401086
.text:0040105D                 push    offset pOnce    ; pOnce
.text:00401062                 call    __Init_thread_header
.text:00401067                 add     esp, 4
.text:0040106A                 cmp     pOnce, 0FFFFFFFFh ; 检查局部变量是否初始化的标志
.text:00401071                 jnz     short loc_401086 ; 如果不等于0FFFFFFFFh,表示局部静态变量已初始化,跳转到输出
.text:00401073                 push    offset pOnce    ; pOnce
.text:00401078                 mov     g_static, esi   ; g_static = i;
.text:0040107E                 call    __Init_thread_footer
.text:00401083                 add     esp, 4
.text:00401086
.text:00401086 loc_401086:                             ; CODE XREF: _main+1B↑j
.text:00401086                                         ; _main+31↑j
.text:00401086                 push    g_static
.text:0040108C                 push    offset _Format  ; "%d\n"
.text:00401091                 call    _printf
.text:00401096                 inc     esi
.text:00401097                 add     esp, 8
.text:0040109A                 cmp     esi, 5
.text:0040109D                 jl      short loc_401050 ; i < 5 跳转
.text:0040109F                 pop     edi
.text:004010A0                 xor     eax, eax
.text:004010A2                 pop     esi
.text:004010A3                 retn
.text:004010A3 _main           endp

7.3 堆变量

  • 使用malloc和new申请堆空间,返回的数据是申请的堆空间地址
  • 使用free和delete释放堆空间

release

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {
	char* buffer1 = (char*)malloc(10);    // 申请堆空间
	char* buffer2 = new char[10];    // 申请堆空间

	if (buffer2 != NULL) {
		delete[] buffer2;    // 释放堆空间
		buffer2 = NULL;
	}

	if (buffer1 != NULL) {
		free(buffer1);        // 释放堆空间
		buffer1 = NULL;
	}

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
int __cdecl main(int argc, const char **argv, const char **envp)
{
  void *v3; // esi
  void *v4; // eax

  v3 = _malloc(0xAu);
  v4 = operator new[](0xAu);
  if ( v4 )
    operator delete[](v4);
  if ( v3 )
    _free(v3);
  return 0;
}
 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
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401000
.text:00401000 argc            = dword ptr  8
.text:00401000 argv            = dword ptr  0Ch
.text:00401000
.text:00401000                 push    esi
.text:00401001                 push    0Ah             ; Size
.text:00401003                 call    ds:__imp__malloc
.text:00401009                 push    0Ah             ; size
.text:0040100B                 mov     esi, eax
.text:0040100D                 call    ??_U@YAPAXI@Z   ; operator new[](uint)
.text:00401012                 add     esp, 8
.text:00401015                 test    eax, eax
.text:00401017                 jz      short loc_401022
.text:00401019                 push    eax             ; block
.text:0040101A                 call    ??_V@YAXPAX@Z   ; operator delete[](void *)
.text:0040101F                 add     esp, 4
.text:00401022
.text:00401022 loc_401022:                             ; CODE XREF: _main+17↑j
.text:00401022                 test    esi, esi
.text:00401024                 jz      short loc_401030
.text:00401026                 push    esi             ; Memory
.text:00401027                 call    ds:__imp__free
.text:0040102D                 add     esp, 4
.text:00401030
.text:00401030 loc_401030:                             ; CODE XREF: _main+24↑j
.text:00401030                 xor     eax, eax
.text:00401032                 pop     esi
.text:00401033                 retn
.text:00401033 _main           endp

8. 数组和指针的寻址

8.1 数组在函数内

在函数内定义数组

  • 去其它声明,该数组即为局部变量,拥有局部变量的所有特性
  • 数组名称表示该数组的首地址
  • 占用的内存空间大小为:sizeof(数据类型)x数组中元素个数
  • 数组的各元素应为同一数据类型,以此可以区分局部变量与数组

字符数组初始化为字符串

release

1
2
3
4
5
6
7
8
#include <stdio.h>

int main(int argc, char* argv[]) {
	char s[] = "Hello World!";
	printf("%s", s);

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[13]; // [esp+0h] [ebp-14h]

  *(_DWORD *)&s[8] = 0x21646C72;                // rld!
  s[12] = 0;                                    // '\0'
  *(_QWORD *)s = *(_QWORD *)"Hello World!";     // Hello Wo
  printf("%s", s);
  return 0;
}
 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
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 s               = byte ptr -14h
.text:00401040 var_4           = dword ptr -4
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 sub     esp, 14h
.text:00401046                 mov     eax, ___security_cookie
.text:0040104B                 xor     eax, ebp
.text:0040104D                 mov     [ebp+var_4], eax
.text:00401050                 mov     eax, dword ptr ds:aHelloWorld+8 ; eax = "rld!"
.text:00401055                 movq    xmm0, qword ptr ds:aHelloWorld ; xmm0 = "Hello Wo"
.text:0040105D                 mov     dword ptr [ebp+s+8], eax
.text:00401060                 mov     al, byte ptr ds:aHelloWorld+0Ch ; al = '\0'
.text:00401065                 mov     [ebp+s+0Ch], al
.text:00401068                 lea     eax, [ebp+s]
.text:0040106B                 push    eax
.text:0040106C                 push    offset _Format  ; "%s"
.text:00401071                 movq    qword ptr [ebp+s], xmm0 ; s = "Hello Wo"
.text:00401076                 call    _printf
.text:0040107B                 mov     ecx, [ebp+var_4]
.text:0040107E                 add     esp, 8
.text:00401081                 xor     ecx, ebp        ; cookie
.text:00401083                 xor     eax, eax
.text:00401085                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:0040108A                 mov     esp, ebp
.text:0040108C                 pop     ebp
.text:0040108D                 retn
.text:0040108D _main           endp

8.2 数组作为参数

8.2.1 strlen()函数

release

1
2
3
4
5
6
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
	return strlen(argv[0]);
}

ida

1
2
3
4
5
int __cdecl main(int argc, const char **argv, const char **envp)
{
  *argv;
  return strlen(*argv);
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
.text:00401000 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401000 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401000
.text:00401000 argc            = dword ptr  8
.text:00401000 argv            = dword ptr  0Ch
.text:00401000 envp            = dword ptr  10h
.text:00401000
.text:00401000                 push    ebp
.text:00401001                 mov     ebp, esp
.text:00401003                 mov     eax, [ebp+argv]
.text:00401006                 mov     eax, [eax]      ; eax = 字符串首地址
.text:00401008                 lea     edx, [eax+1]    ; edx = 字符串首地址的下一位置
.text:0040100B                 nop     dword ptr [eax+eax+00h]
.text:00401010
.text:00401010 loc_401010:                             ; CODE XREF: _main+15↓j
.text:00401010                 mov     cl, [eax]       ; 获取字符
.text:00401012                 inc     eax             ; 偏移量
.text:00401013                 test    cl, cl          ; 检测空字符
.text:00401015                 jnz     short loc_401010 ; 获取字符
.text:00401017                 sub     eax, edx        ; 字符串结束地址(空字符的位置) - 字符串起始地址 + 1 = 字符串长度(不包含空字符)
.text:00401019                 pop     ebp
.text:0040101A                 retn
.text:0040101A _main           endp

8.2.2 strcpy()函数

在字符串初始化时,利用xmm寄存器初始化数组的值,一次可以初始化16字节,效率更高。

release

1
2
3
4
5
6
7
8
9
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
	char buffer[20] = { 0 }; //字符数组定义
	strcpy(buffer, argv[0]); //字符串复制
	printf(buffer);
	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
int __cdecl main(int argc, const char **argv, const char **envp)
{
  const char *v3; // eax
  int v4; // edx
  char v5; // cl
  char buffer[20]; // [esp+0h] [ebp-18h]

  buffer[0] = 0;
  *(_OWORD *)&buffer[1] = 0i64;
  *(_WORD *)&buffer[17] = 0;
  v3 = *argv;
  v4 = buffer - *argv;
  buffer[19] = 0;
  do
  {
    v5 = *v3++;
    v3[v4 - 1] = v5;
  }
  while ( v5 );
  printf(buffer);
  return 0;
}
 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
36
37
38
39
40
41
42
43
44
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 buffer          = byte ptr -18h
.text:00401040 var_4           = dword ptr -4
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 sub     esp, 18h
.text:00401046                 mov     eax, ___security_cookie
.text:0040104B                 xor     eax, ebp
.text:0040104D                 mov     [ebp+var_4], eax
.text:00401050                 mov     eax, [ebp+argv] ; eax = argv
.text:00401053                 lea     edx, [ebp+buffer] ; edx = buffer
.text:00401056                 xorps   xmm0, xmm0      ; xmm0 = 0
.text:00401059                 mov     [ebp+buffer], 0 ; buffer[0] = 0
.text:0040105D                 movups  xmmword ptr [ebp+buffer+1], xmm0 ; buffer[1 ~ 16] 设置为0
.text:00401061                 mov     word ptr [ebp+buffer+11h], 0 ; buffer[17 ~ 18] 设置为0
.text:00401067                 mov     eax, [eax]      ; eax = argv[0]
.text:00401069                 sub     edx, eax        ; edx = buffer - *argc  两个地址的差
.text:0040106B                 mov     [ebp+buffer+13h], 0 ; buffer[19] = 0
.text:0040106F                 nop
.text:00401070
.text:00401070 loc_401070:                             ; CODE XREF: _main+3B↓j
.text:00401070                 mov     cl, [eax]       ; *ptr_char
.text:00401072                 lea     eax, [eax+1]    ; 通过字符指针(ptr_char)来进行偏移
.text:00401075                 mov     [edx+eax-1], cl ; 复制字符,通过argv[0]的地址差值算出buffer的地址
.text:00401079                 test    cl, cl          ; 空字符结束循环
.text:0040107B                 jnz     short loc_401070 ; *ptr_char
.text:0040107D                 lea     eax, [ebp+buffer]
.text:00401080                 push    eax             ; _Format
.text:00401081                 call    _printf
.text:00401086                 mov     ecx, [ebp+var_4]
.text:00401089                 add     esp, 4
.text:0040108C                 xor     ecx, ebp        ; cookie
.text:0040108E                 xor     eax, eax
.text:00401090                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:00401095                 mov     esp, ebp
.text:00401097                 pop     ebp
.text:00401098                 retn
.text:00401098 _main           endp

8.3 存放指针类型数组的数组

release

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include <stdio.h>
#include <string.h>

int main(int argc, char* argv[]) {
	const char* ary[3] = { "Hello ", "World ", "!\n" };//字符串指针数组定义
	for (int i = 0; i < 3; i++) {
		printf(ary[i]); //显示输出字符串数组中的各项
	}

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
int __cdecl main(int argc, const char **argv, const char **envp)
{
  signed int v3; // esi
  const char *ary[3]; // [esp+4h] [ebp-10h]

  ary[0] = "Hello ";
  v3 = 0;
  ary[1] = "World ";
  ary[2] = "!\n";
  do
    printf(ary[v3++]);
  while ( v3 < 3 );
  return 0;
}
 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
36
37
38
.text:00401040 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401040 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401040
.text:00401040 ary             = dword ptr -10h
.text:00401040 var_4           = dword ptr -4
.text:00401040 argc            = dword ptr  8
.text:00401040 argv            = dword ptr  0Ch
.text:00401040 envp            = dword ptr  10h
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 sub     esp, 10h
.text:00401046                 mov     eax, ___security_cookie
.text:0040104B                 xor     eax, ebp
.text:0040104D                 mov     [ebp+var_4], eax
.text:00401050                 push    esi
.text:00401051                 mov     [ebp+ary], offset aHello ; "Hello "
.text:00401058                 xor     esi, esi        ; esi = 0
.text:0040105A                 mov     [ebp+ary+4], offset aWorld ; "World "
.text:00401061                 mov     [ebp+ary+8], offset asc_402108 ; "!\n"
.text:00401068                 nop     dword ptr [eax+eax+00000000h]
.text:00401070
.text:00401070 loc_401070:                             ; CODE XREF: _main+40↓j
.text:00401070                 push    [ebp+esi*4+ary] ; _Format
.text:00401074                 call    _printf
.text:00401079                 inc     esi
.text:0040107A                 add     esp, 4
.text:0040107D                 cmp     esi, 3
.text:00401080                 jl      short loc_401070
.text:00401082                 mov     ecx, [ebp+var_4]
.text:00401085                 xor     eax, eax
.text:00401087                 xor     ecx, ebp        ; cookie
.text:00401089                 pop     esi
.text:0040108A                 call    @__security_check_cookie@4 ; __security_check_cookie(x)
.text:0040108F                 mov     esp, ebp
.text:00401091                 pop     ebp
.text:00401092                 retn
.text:00401092 _main           endp

8.4 函数指针

release

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
#include <stdio.h>

int _stdcall show(int n) { //函数定义
	printf("show : %d\n", n);
	return n;
}

int main(int argc, char* argv[]) {
	int(_stdcall * pfn)(int) = show; //函数指针定义并初始化
	int ret = pfn(5); //使用函数指针调用函数并获取返回值
	printf("ret = %d\n", ret);

	return 0;
}

ida

1
2
3
4
5
6
7
8
int __cdecl main(int argc, const char **argv, const char **envp)
{
  int v3; // eax

  v3 = show(5);
  printf("ret = %d\n", v3);
  return 0;
}
 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
36
37
38
39
40
41
.text:00401040 ; =============== S U B R O U T I N E =======================================
.text:00401040
.text:00401040 ; Attributes: bp-based frame
.text:00401040
.text:00401040 ; int __stdcall show(int n)
.text:00401040 ?show@@YGHH@Z   proc near               ; CODE XREF: _main+2↓p
.text:00401040
.text:00401040 n               = dword ptr  8
.text:00401040
.text:00401040                 push    ebp
.text:00401041                 mov     ebp, esp
.text:00401043                 push    [ebp+n]
.text:00401046                 push    offset _Format  ; "show : %d\n"
.text:0040104B                 call    _printf
.text:00401050                 mov     eax, [ebp+n]
.text:00401053                 add     esp, 8
.text:00401056                 pop     ebp
.text:00401057                 retn    4
.text:00401057 ?show@@YGHH@Z   endp
.text:00401057
.text:00401057 ; ---------------------------------------------------------------------------
.text:0040105A                 align 10h
.text:00401060
.text:00401060 ; =============== S U B R O U T I N E =======================================
.text:00401060
.text:00401060
.text:00401060 ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:00401060 _main           proc near               ; CODE XREF: __scrt_common_main_seh+F4↓p
.text:00401060
.text:00401060 argc            = dword ptr  8
.text:00401060 argv            = dword ptr  0Ch
.text:00401060
.text:00401060                 push    5               ; n
.text:00401062                 call    ?show@@YGHH@Z   ; show(int)
.text:00401067                 push    eax
.text:00401068                 push    offset aRetD    ; "ret = %d\n"
.text:0040106D                 call    _printf
.text:00401072                 add     esp, 8
.text:00401075                 xor     eax, eax
.text:00401077                 retn
.text:00401077 _main           endp

9. 结构体和类

9.1 对象的内存布局

  1. 空类

空类的长度位1字节

  1. 内存对齐

结构体中的数据成员类型最大值为M,指定对齐值为N,则实际对齐值为q=min(M,N)

  1. 静态数据成员

类中的数据成员被修饰为静态时,它与局部静态变量类似,存放的位置和全局变量一致

9.2 this指针

对象调用成员的方法以及取出数据成员的过程

  • 利用寄存器ecx保存对象的首地址
  • 以寄存器传参的方式将其传递到成员函数中

debug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

class Person {
public:
	void setAge(int age) { //公有成员函数
		this->age = age;
	}
public:
	int age; //公有数据成员
};

int main(int argc, char* argv[]) {
	Person person;
	person.setAge(5); //调用成员函数
	printf("Person : %d\n", person.age); //获取数据成员

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
void __thiscall Person::setAge(Person *this, int age)
{
  this->age = age;
}

__int64 __cdecl main()
{
  int v0; // edx
  __int64 v1; // ST00_8
  Person person; // [esp+D0h] [ebp-Ch]

  Person::setAge(&person, 5);
  j__printf("Person : %d\n", person.age);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
.text:004118A0 ; int __cdecl main(int argc, char **argv)
.text:004118A0 _main           proc near               ; CODE XREF: _main_0↑j
.text:004118A0
.text:004118A0 var_D0          = byte ptr -0D0h
.text:004118A0 person          = Person ptr -0Ch
.text:004118A0 var_4           = dword ptr -4
.text:004118A0 argc            = dword ptr  8
.text:004118A0 argv            = dword ptr  0Ch
.text:004118A0
.text:004118A0                 push    ebp
.text:004118A1                 mov     ebp, esp
.text:004118A3                 sub     esp, 0D0h
.text:004118A9                 push    ebx
.text:004118AA                 push    esi
.text:004118AB                 push    edi
.text:004118AC                 lea     edi, [ebp+var_D0]
.text:004118B2                 mov     ecx, 34h
.text:004118B7                 mov     eax, 0CCCCCCCCh
.text:004118BC                 rep stosd
.text:004118BE                 mov     eax, ___security_cookie
.text:004118C3                 xor     eax, ebp
.text:004118C5                 mov     [ebp+var_4], eax
.text:004118C8                 push    5               ; age
.text:004118CA                 lea     ecx, [ebp+person] ; this
.text:004118CD                 call    j_?setAge@Person@@QAEXH@Z ; Person::setAge(int)
.text:004118D2                 mov     eax, [ebp+person.age]
.text:004118D5                 push    eax
.text:004118D6                 push    offset _Format  ; "Person : %d\n"
.text:004118DB                 call    j__printf
.text:004118E0                 add     esp, 8
.text:004118E3                 xor     eax, eax
.text:004118E5                 push    edx
.text:004118E6                 mov     ecx, ebp        ; frame
.text:004118E8                 push    eax
.text:004118E9
.text:004118E9 loc_4118E9:                             ; DATA XREF: .text:off_41111D↑o
.text:004118E9                 lea     edx, v          ; v
.text:004118EF                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:004118F4                 pop     eax
.text:004118F5                 pop     edx
.text:004118F6                 pop     edi
.text:004118F7                 pop     esi
.text:004118F8                 pop     ebx
.text:004118F9                 mov     ecx, [ebp+var_4]
.text:004118FC                 xor     ecx, ebp        ; cookie
.text:004118FE                 call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:00411903                 add     esp, 0D0h
.text:00411909                 cmp     ebp, esp
.text:0041190B                 call    j___RTC_CheckEsp
.text:00411910                 mov     esp, ebp
.text:00411912                 pop     ebp
.text:00411913                 retn
.text:00411913 ; ---------------------------------------------------------------------------
.text:00411914 ; _RTC_framedesc v
.text:00411914 v               _RTC_framedesc <1, offset dword_41191C>
.text:00411914                                         ; DATA XREF: _main:loc_4118E9↑o
.text:0041191C dword_41191C    dd 0FFFFFFF4h, 4        ; DATA XREF: _main:v↑o
.text:00411924                 dd offset aPerson       ; "person"
.text:00411928 aPerson         db 'person',0           ; DATA XREF: _main+84↑o
.text:00411928 _main           endp
.text:00411928
.text:0041192F                 align 80h
.text:00411980
.text:00411980 ; =============== S U B R O U T I N E =======================================
.text:00411980
.text:00411980 ; Attributes: bp-based frame
.text:00411980
.text:00411980 ; void __thiscall Person::setAge(Person *this, int age)
.text:00411980 ?setAge@Person@@QAEXH@Z proc near       ; CODE XREF: Person::setAge(int)↑j
.text:00411980
.text:00411980 var_CC          = byte ptr -0CCh
.text:00411980 this            = dword ptr -8
.text:00411980 age             = dword ptr  8
.text:00411980
.text:00411980                 push    ebp
.text:00411981                 mov     ebp, esp
.text:00411983                 sub     esp, 0CCh
.text:00411989                 push    ebx
.text:0041198A                 push    esi
.text:0041198B                 push    edi
.text:0041198C                 push    ecx
.text:0041198D                 lea     edi, [ebp+var_CC]
.text:00411993                 mov     ecx, 33h
.text:00411998                 mov     eax, 0CCCCCCCCh
.text:0041199D                 rep stosd
.text:0041199F                 pop     ecx
.text:004119A0                 mov     [ebp+this], ecx ; ecx保存person对象的首地址
.text:004119A3                 mov     eax, [ebp+this] ; eax = this
.text:004119A6                 mov     ecx, [ebp+age]  ; ecx = age
.text:004119A9                 mov     [eax], ecx      ; this->age = age
.text:004119AB                 pop     edi
.text:004119AC                 pop     esi
.text:004119AD                 pop     ebx
.text:004119AE                 mov     esp, ebp
.text:004119B0                 pop     ebp
.text:004119B1                 retn    4
.text:004119B1 ?setAge@Person@@QAEXH@Z endp

9.3 对象作为函数参数

debug

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

class Person {
public:
	int age;
	int height;
};

void show(Person person) { //参数为类Person的对象
	printf("age = %d , height = %d\n", person.age, person.height);
}

int main(int argc, char* argv[]) {
	Person person;
	person.age = 1;
	person.height = 2;
	show(person);

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
void __cdecl show(Person person)
{
  j__printf("age = %d , height = %d\n", person.age, person.height);
}

__int64 __cdecl main()
{
  int v0; // edx
  __int64 v1; // ST00_8

  show((Person)0x200000001i64);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
.text:004118A0 ; int __cdecl main(int argc, char **argv)
.text:004118A0 _main           proc near               ; CODE XREF: _main_0↑j
.text:004118A0
.text:004118A0 var_D0          = byte ptr -0D0h
.text:004118A0 person          = Person ptr -0Ch
.text:004118A0 argc            = dword ptr  8
.text:004118A0 argv            = dword ptr  0Ch
.text:004118A0
.text:004118A0                 push    ebp
.text:004118A1                 mov     ebp, esp
.text:004118A3                 sub     esp, 0D0h
.text:004118A9                 push    ebx
.text:004118AA                 push    esi
.text:004118AB                 push    edi
.text:004118AC                 lea     edi, [ebp+var_D0]
.text:004118B2                 mov     ecx, 34h
.text:004118B7                 mov     eax, 0CCCCCCCCh
.text:004118BC                 rep stosd
.text:004118BE                 mov     [ebp+person.age], 1
.text:004118C5                 mov     [ebp+person.height], 2
.text:004118CC                 mov     eax, [ebp+person.height]
.text:004118CF                 push    eax
.text:004118D0                 mov     ecx, [ebp+person.age]
.text:004118D3                 push    ecx             ; person
.text:004118D4                 call    j_?show@@YAXVPerson@@@Z ; show(Person)
.text:004118D9                 add     esp, 8
.text:004118DC                 xor     eax, eax
.text:004118DE                 push    edx
.text:004118DF                 mov     ecx, ebp        ; frame
.text:004118E1                 push    eax
.text:004118E2                 lea     edx, v          ; v
.text:004118E8
.text:004118E8 loc_4118E8:                             ; DATA XREF: .text:off_41111D↑o
.text:004118E8                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:004118ED                 pop     eax
.text:004118EE                 pop     edx
.text:004118EF                 pop     edi
.text:004118F0                 pop     esi
.text:004118F1                 pop     ebx
.text:004118F2                 add     esp, 0D0h
.text:004118F8                 cmp     ebp, esp
.text:004118FA                 call    j___RTC_CheckEsp
.text:004118FF                 mov     esp, ebp
.text:00411901                 pop     ebp
.text:00411902                 retn
.text:00411902 ; ---------------------------------------------------------------------------
.text:00411903                 align 4
.text:00411904 ; _RTC_framedesc v
.text:00411904 v               _RTC_framedesc <1, offset dword_41190C>
.text:00411904                                         ; DATA XREF: _main+42↑o
.text:0041190C dword_41190C    dd 0FFFFFFF4h, 8        ; DATA XREF: _main:v↑o
.text:00411914                 dd offset aPerson       ; "person"
.text:00411918 aPerson         db 'person',0           ; DATA XREF: _main+74↑o
.text:00411918 _main           endp
.text:00411918
.text:0041191F                 align 80h
.text:00411980
.text:00411980 ; =============== S U B R O U T I N E =======================================
.text:00411980
.text:00411980 ; Attributes: bp-based frame
.text:00411980
.text:00411980 ; void __cdecl show(Person person)
.text:00411980 ?show@@YAXVPerson@@@Z proc near         ; CODE XREF: .text:004113C5↑j
.text:00411980                                         ; show(Person)↑j
.text:00411980
.text:00411980 var_C0          = byte ptr -0C0h
.text:00411980 person          = Person ptr  8
.text:00411980
.text:00411980                 push    ebp
.text:00411981                 mov     ebp, esp
.text:00411983                 sub     esp, 0C0h
.text:00411989                 push    ebx
.text:0041198A                 push    esi
.text:0041198B                 push    edi
.text:0041198C                 lea     edi, [ebp+var_C0]
.text:00411992                 mov     ecx, 30h
.text:00411997                 mov     eax, 0CCCCCCCCh
.text:0041199C                 rep stosd
.text:0041199E                 mov     eax, [ebp+person.height]
.text:004119A1                 push    eax
.text:004119A2                 mov     ecx, [ebp+person.age]
.text:004119A5                 push    ecx
.text:004119A6                 push    offset _Format  ; "age = %d , height = %d\n"
.text:004119AB                 call    j__printf
.text:004119B0                 add     esp, 0Ch
.text:004119B3                 pop     edi
.text:004119B4                 pop     esi
.text:004119B5                 pop     ebx
.text:004119B6                 add     esp, 0C0h
.text:004119BC                 cmp     ebp, esp
.text:004119BE                 call    j___RTC_CheckEsp
.text:004119C3                 mov     esp, ebp
.text:004119C5                 pop     ebp
.text:004119C6                 retn
.text:004119C6 ?show@@YAXVPerson@@@Z endp

含有数组数据成员的对象传参

debug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <stdio.h>
#include <string.h>

class Person {
public:
	int age;
	int height;
	char name[32]; //定义数组类型的数据成员
};

void show(Person person) {
	printf("age = %d , height = %d name:%s\n", person.age,
		person.height, person.name);
}

int main(int argc, char* argv[]) {
	Person person;
	person.age = 1;
	person.height = 2;
	strcpy(person.name, "tom"); //赋值数据成员数组
	show(person);

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void __cdecl show(Person person)
{
  j__printf("age = %d , height = %d name:%s\n", person.age, person.height, person.name);
}

__int64 __cdecl main()
{
  int v0; // edx
  Person v2; // [esp-28h] [ebp-128h]
  int v3; // [esp-8h] [ebp-108h]
  int v4; // [esp-4h] [ebp-104h]
  Person person; // [esp+D0h] [ebp-30h]

  person.age = 1;
  person.height = 2;
  j__strcpy(person.name, "tom");
  qmemcpy(&v2, &person, sizeof(v2));
  show(v2);
  v4 = v0;
  v3 = 0;
  return *(_QWORD *)&v3;
}
  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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
00000000 Person          struc ; (sizeof=0x28, align=0x4, copyof_573)
00000000                                         ; XREF: ?show@@YAXVPerson@@@Z/r
00000000                                         ; _main/r ...
00000000 age             dd ?                    ; XREF: _main+28/w
00000000                                         ; show(Person)+26/r
00000004 height          dd ?                    ; XREF: _main+2F/w
00000004                                         ; show(Person)+22/r
00000008 name            db 32 dup(?)            ; XREF: _main+3B/o
00000008                                         ; show(Person)+1E/o
00000028 Person          ends

.text:004118A0 ; int __cdecl main(int argc, char **argv)
.text:004118A0 _main           proc near               ; CODE XREF: _main_0↑j
.text:004118A0
.text:004118A0 var_F4          = byte ptr -0F4h
.text:004118A0 person          = Person ptr -30h
.text:004118A0 var_4           = dword ptr -4
.text:004118A0 argc            = dword ptr  8
.text:004118A0 argv            = dword ptr  0Ch
.text:004118A0
.text:004118A0                 push    ebp
.text:004118A1                 mov     ebp, esp
.text:004118A3                 sub     esp, 0F4h
.text:004118A9                 push    ebx
.text:004118AA                 push    esi
.text:004118AB                 push    edi
.text:004118AC                 lea     edi, [ebp+var_F4]
.text:004118B2                 mov     ecx, 3Dh
.text:004118B7                 mov     eax, 0CCCCCCCCh
.text:004118BC                 rep stosd
.text:004118BE                 mov     eax, ___security_cookie
.text:004118C3                 xor     eax, ebp
.text:004118C5                 mov     [ebp+var_4], eax
.text:004118C8                 mov     [ebp+person.age], 1
.text:004118CF                 mov     [ebp+person.height], 2
.text:004118D6                 push    offset Source   ; "tom"
.text:004118DB                 lea     eax, [ebp+person.name]
.text:004118DE                 push    eax             ; Dest
.text:004118DF                 call    j__strcpy
.text:004118E4                 add     esp, 8
.text:004118E7
.text:004118E7 loc_4118E7:                             ; DATA XREF: .text:off_41111D↑o
.text:004118E7                 sub     esp, 28h
.text:004118EA                 mov     ecx, 0Ah        ; 设置循环的次数
.text:004118EF                 lea     esi, [ebp+person] ; esi保存person对象的首地址
.text:004118F2                 mov     edi, esp        ; 设置edi为当前栈顶
.text:004118F4                 rep movsd               ; 执行ecx(10)次4字节内存复制,将esi所指向的数据复制到edi指向的存储区域中
.text:004118F6                 call    j_?show@@YAXVPerson@@@Z ; show(Person)
.text:004118FB                 add     esp, 28h
.text:004118FE                 xor     eax, eax
.text:00411900                 push    edx
.text:00411901                 mov     ecx, ebp        ; frame
.text:00411903                 push    eax
.text:00411904                 lea     edx, v          ; v
.text:0041190A                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:0041190F                 pop     eax
.text:00411910                 pop     edx
.text:00411911                 pop     edi
.text:00411912                 pop     esi
.text:00411913                 pop     ebx
.text:00411914                 mov     ecx, [ebp+var_4]
.text:00411917                 xor     ecx, ebp        ; cookie
.text:00411919                 call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:0041191E                 add     esp, 0F4h
.text:00411924                 cmp     ebp, esp
.text:00411926                 call    j___RTC_CheckEsp
.text:0041192B                 mov     esp, ebp
.text:0041192D                 pop     ebp
.text:0041192E                 retn
.text:0041192E ; ---------------------------------------------------------------------------
.text:0041192F                 align 10h
.text:00411930 ; _RTC_framedesc v
.text:00411930 v               _RTC_framedesc <1, offset dword_411938>
.text:00411930                                         ; DATA XREF: _main+64↑o
.text:00411938 dword_411938    dd 0FFFFFFD0h, 28h      ; DATA XREF: _main:v↑o
.text:00411940                 dd offset aPerson       ; "person"
.text:00411944 aPerson         db 'person',0           ; DATA XREF: _main+A0↑o
.text:00411944 _main           endp
.text:00411944
.text:0041194B                 align 40h
.text:00411980
.text:00411980 ; =============== S U B R O U T I N E =======================================
.text:00411980
.text:00411980 ; Attributes: bp-based frame
.text:00411980
.text:00411980 ; void __cdecl show(Person person)
.text:00411980 ?show@@YAXVPerson@@@Z proc near         ; CODE XREF: .text:004113C5↑j
.text:00411980                                         ; show(Person)↑j
.text:00411980
.text:00411980 var_C0          = byte ptr -0C0h
.text:00411980 person          = Person ptr  8
.text:00411980
.text:00411980                 push    ebp
.text:00411981                 mov     ebp, esp
.text:00411983                 sub     esp, 0C0h
.text:00411989                 push    ebx
.text:0041198A                 push    esi
.text:0041198B                 push    edi
.text:0041198C                 lea     edi, [ebp+var_C0]
.text:00411992                 mov     ecx, 30h
.text:00411997                 mov     eax, 0CCCCCCCCh
.text:0041199C                 rep stosd
.text:0041199E                 lea     eax, [ebp+person.name]
.text:004119A1                 push    eax
.text:004119A2                 mov     ecx, [ebp+person.height]
.text:004119A5                 push    ecx
.text:004119A6                 mov     edx, [ebp+person.age]
.text:004119A9                 push    edx
.text:004119AA                 push    offset _Format  ; "age = %d , height = %d name:%s\n"
.text:004119AF                 call    j__printf
.text:004119B4                 add     esp, 10h
.text:004119B7                 pop     edi
.text:004119B8                 pop     esi
.text:004119B9                 pop     ebx
.text:004119BA                 add     esp, 0C0h
.text:004119C0                 cmp     ebp, esp
.text:004119C2                 call    j___RTC_CheckEsp
.text:004119C7                 mov     esp, ebp
.text:004119C9                 pop     ebp
.text:004119CA                 retn
.text:004119CA ?show@@YAXVPerson@@@Z endp

9.4 对象作为返回值

debug

 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
#include <stdio.h>
#include <string.h>

class Person {
public:
	int count;
	int buffer[10]; //定义两个数据成员,该类的大小为44字节
};

Person getPerson() {
	Person person;
	person.count = 10;
	for (int i = 0; i < 10; i++) {
		person.buffer[i] = i + 1;
	}
	return person;
}

int main(int argc, char* argv[]) {
	Person person;
	person = getPerson();
	printf("%d %d %d", person.count, person.buffer[0], person.buffer[9]);

	return 0;
}

ida

 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
Person *__cdecl getPerson(Person *result)
{
  int i; // [esp+D0h] [ebp-3Ch]
  Person person; // [esp+DCh] [ebp-30h]

  person.count = 10;
  for ( i = 0; i < 10; ++i )
    person.buffer[i] = i + 1;
  qmemcpy(result, &person, sizeof(Person));
  return result;
}

__int64 __cdecl main()
{
  int v0; // edx
  __int64 v1; // ST08_8
  Person result; // [esp+10h] [ebp-158h]
  char v4; // [esp+44h] [ebp-124h]
  Person person; // [esp+138h] [ebp-30h]

  qmemcpy(&v4, getPerson(&result), 0x2Cu);
  qmemcpy(&person, &v4, sizeof(person));
  j__printf("%d %d %d", person.count, person.buffer[0], person.buffer[9]);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
  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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
.text:004118A0 ; int __cdecl main(int argc, char **argv)
.text:004118A0 _main           proc near               ; CODE XREF: _main_0↑j
.text:004118A0
.text:004118A0 var_15C         = byte ptr -15Ch
.text:004118A0 result          = Person ptr -158h
.text:004118A0 var_124         = byte ptr -124h
.text:004118A0 person          = Person ptr -30h
.text:004118A0 argc            = dword ptr  8
.text:004118A0 argv            = dword ptr  0Ch
.text:004118A0
.text:004118A0                 push    ebp
.text:004118A1                 mov     ebp, esp
.text:004118A3                 sub     esp, 15Ch
.text:004118A9                 push    ebx
.text:004118AA                 push    esi
.text:004118AB                 push    edi
.text:004118AC                 lea     edi, [ebp+var_15C]
.text:004118B2                 mov     ecx, 57h
.text:004118B7                 mov     eax, 0CCCCCCCCh
.text:004118BC                 rep stosd
.text:004118BE                 lea     eax, [ebp+result] ; 返回的对象
.text:004118C4                 push    eax             ; result
.text:004118C5                 call    j_?getPerson@@YA?AVPerson@@XZ ; getPerson(void)
.text:004118CA                 add     esp, 4
.text:004118CD                 mov     ecx, 0Bh        ; 设置循环的次数
.text:004118D2                 mov     esi, eax        ; 返回对象的首地址
.text:004118D4                 lea     edi, [ebp+var_124] ; 临时对象
.text:004118DA                 rep movsd               ; 每次从返回对象中复制4字节数据到临时对象中,共11次
.text:004118DC                 mov     ecx, 0Bh        ; 再次设置循环次数,临时对象 =》 person对象
.text:004118E1                 lea     esi, [ebp+var_124]
.text:004118E7
.text:004118E7 loc_4118E7:                             ; DATA XREF: .text:off_41111D↑o
.text:004118E7                 lea     edi, [ebp+person]
.text:004118EA                 rep movsd               ; 将临时对象拷贝到person对象
.text:004118EC                 mov     eax, 4          ; 偏移4字节,buffer在对象的第5字节处
.text:004118F1                 imul    ecx, eax, 9
.text:004118F4                 mov     edx, [ebp+ecx+person.buffer] ; person.buffer[9]
.text:004118F8                 push    edx
.text:004118F9                 mov     eax, 4          ; 偏移4字节
.text:004118FE                 imul    ecx, eax, 0
.text:00411901                 mov     edx, [ebp+ecx+person.buffer] ; person.buffer[0]
.text:00411905                 push    edx
.text:00411906                 mov     eax, [ebp+person.count] ; person.count
.text:00411909                 push    eax
.text:0041190A                 push    offset _Format  ; "%d %d %d"
.text:0041190F                 call    j__printf
.text:00411914                 add     esp, 10h
.text:00411917                 xor     eax, eax
.text:00411919                 push    edx
.text:0041191A                 mov     ecx, ebp        ; frame
.text:0041191C                 push    eax
.text:0041191D                 lea     edx, v          ; v
.text:00411923                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:00411928                 pop     eax
.text:00411929                 pop     edx
.text:0041192A                 pop     edi
.text:0041192B                 pop     esi
.text:0041192C                 pop     ebx
.text:0041192D                 add     esp, 15Ch
.text:00411933                 cmp     ebp, esp
.text:00411935                 call    j___RTC_CheckEsp
.text:0041193A                 mov     esp, ebp
.text:0041193C                 pop     ebp
.text:0041193D                 retn
.text:0041193D ; ---------------------------------------------------------------------------
.text:0041193E                 align 10h
.text:00411940 ; _RTC_framedesc v
.text:00411940 v               _RTC_framedesc <1, offset dword_411948>
.text:00411940                                         ; DATA XREF: _main+7D↑o
.text:00411948 dword_411948    dd 0FFFFFFD0h, 2Ch      ; DATA XREF: _main:v↑o
.text:00411950                 dd offset aPerson       ; "person"
.text:00411954 aPerson         db 'person',0           ; DATA XREF: _main+B0↑o
.text:00411954 _main           endp
.text:00411954
.text:0041195B                 align 40h
.text:00411980
.text:00411980 ; =============== S U B R O U T I N E =======================================
.text:00411980
.text:00411980 ; Attributes: bp-based frame
.text:00411980
.text:00411980 ; Person *__cdecl getPerson(Person *result)
.text:00411980 ?getPerson@@YA?AVPerson@@XZ proc near   ; CODE XREF: .text:004113C5↑j
.text:00411980                                         ; .text:004113CA↑j ...
.text:00411980
.text:00411980 var_100         = byte ptr -100h
.text:00411980 i               = dword ptr -3Ch
.text:00411980 person          = Person ptr -30h
.text:00411980 result          = dword ptr  8
.text:00411980
.text:00411980                 push    ebp
.text:00411981                 mov     ebp, esp
.text:00411983                 sub     esp, 100h
.text:00411989                 push    ebx
.text:0041198A                 push    esi
.text:0041198B                 push    edi
.text:0041198C                 lea     edi, [ebp+var_100]
.text:00411992                 mov     ecx, 40h
.text:00411997                 mov     eax, 0CCCCCCCCh
.text:0041199C                 rep stosd
.text:0041199E                 mov     [ebp+person.count], 0Ah
.text:004119A5                 mov     [ebp+i], 0      ; i = 0
.text:004119AC                 jmp     short loc_4119B7 ; 跳转到for循环
.text:004119AE ; ---------------------------------------------------------------------------
.text:004119AE
.text:004119AE loc_4119AE:                             ; CODE XREF: getPerson(void)+4A↓j
.text:004119AE                 mov     eax, [ebp+i]
.text:004119B1                 add     eax, 1
.text:004119B4                 mov     [ebp+i], eax
.text:004119B7
.text:004119B7 loc_4119B7:                             ; CODE XREF: getPerson(void)+2C↑j
.text:004119B7                 cmp     [ebp+i], 0Ah
.text:004119BB                 jge     short loc_4119CC
.text:004119BD                 mov     eax, [ebp+i]
.text:004119C0                 add     eax, 1          ; i + 1
.text:004119C3                 mov     ecx, [ebp+i]
.text:004119C6                 mov     [ebp+ecx*4+person.buffer], eax ; person.buffer[i] = i + 1;
.text:004119CA                 jmp     short loc_4119AE
.text:004119CC ; ---------------------------------------------------------------------------
.text:004119CC
.text:004119CC loc_4119CC:                             ; CODE XREF: getPerson(void)+3B↑j
.text:004119CC                 mov     ecx, 0Bh
.text:004119D1                 lea     esi, [ebp+person] ; person对象的地址
.text:004119D4                 mov     edi, [ebp+result] ; 返回对象的首地址
.text:004119D7                 rep movsd               ; 将person对象拷贝到返回对象中
.text:004119D9                 mov     eax, [ebp+result] ; 将返回对象的地址保存到eax中,作为返回值
.text:004119DC                 push    edx
.text:004119DD                 mov     ecx, ebp        ; frame
.text:004119DF                 push    eax
.text:004119E0                 lea     edx, stru_4119F4 ; v
.text:004119E6                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:004119EB                 pop     eax
.text:004119EC                 pop     edx
.text:004119ED                 pop     edi
.text:004119EE                 pop     esi
.text:004119EF                 pop     ebx
.text:004119F0                 mov     esp, ebp
.text:004119F2                 pop     ebp
.text:004119F3                 retn
.text:004119F3 ; ---------------------------------------------------------------------------
.text:004119F4 ; _RTC_framedesc stru_4119F4
.text:004119F4 stru_4119F4     _RTC_framedesc <1, offset dword_4119FC>
.text:004119F4                                         ; DATA XREF: getPerson(void)+60↑o
.text:004119FC dword_4119FC    dd 0FFFFFFD0h, 2Ch      ; DATA XREF: getPerson(void):stru_4119F4↑o
.text:00411A04                 dd offset aPerson_0     ; "person"
.text:00411A08 aPerson_0       db 'person',0           ; DATA XREF: getPerson(void)+84↑o
.text:00411A08 ?getPerson@@YA?AVPerson@@XZ endp

10. 构造函数和析构函数

根据生命周期将对象进行分类,分析各类对象构造函数和析构函数的调用时机

  • 局部对象
  • 堆对象
  • 参数对象
  • 返回对象
  • 全局对象
  • 静态对象

10.1 构造函数的出现时机

10.1.1 局部对象

debug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
#include <stdio.h>

class Person {
public:
	Person() { //无参构造函数
		age = 20;
	}
	int age;
};

int main(int argc, char* argv[]) {
	Person person; //类对象定义

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void __thiscall Person::Person(Person *this)
{
  this->age = 20;
}

__int64 __cdecl main()
{
  int v0; // edx
  __int64 v1; // ST00_8
  Person person; // [esp+D0h] [ebp-Ch]

  Person::Person(&person);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
.text:00411690 ; void __thiscall Person::Person(Person *this)
.text:00411690 ??0Person@@QAE@XZ proc near             ; CODE XREF: Person::Person(void)↑j
.text:00411690
.text:00411690 var_CC          = byte ptr -0CCh
.text:00411690 this            = dword ptr -8
.text:00411690
.text:00411690                 push    ebp
.text:00411691                 mov     ebp, esp
.text:00411693                 sub     esp, 0CCh
.text:00411699                 push    ebx
.text:0041169A                 push    esi
.text:0041169B                 push    edi
.text:0041169C                 push    ecx
.text:0041169D                 lea     edi, [ebp+var_CC]
.text:004116A3                 mov     ecx, 33h
.text:004116A8                 mov     eax, 0CCCCCCCCh
.text:004116AD                 rep stosd
.text:004116AF                 pop     ecx
.text:004116B0                 mov     [ebp+this], ecx ; 传进来的this指针
.text:004116B3                 mov     eax, [ebp+this]
.text:004116B6                 mov     dword ptr [eax], 14h ; 将对象的前4字节设置为20,即this->age = 20;
.text:004116BC                 mov     eax, [ebp+this] ; 将this指针传入eax中作为返回值
.text:004116BF                 pop     edi
.text:004116C0                 pop     esi
.text:004116C1                 pop     ebx
.text:004116C2                 mov     esp, ebp
.text:004116C4                 pop     ebp
.text:004116C5                 retn
.text:004116C5 ??0Person@@QAE@XZ endp
.text:004116C5
.text:004116C5 ; ---------------------------------------------------------------------------
.text:004116C6                 align 20h
.text:004116E0
.text:004116E0 ; =============== S U B R O U T I N E =======================================
.text:004116E0
.text:004116E0 ; Attributes: bp-based frame
.text:004116E0
.text:004116E0 ; int __cdecl main(int argc, char **argv)
.text:004116E0 _main           proc near               ; CODE XREF: _main_0↑j
.text:004116E0
.text:004116E0 var_D0          = byte ptr -0D0h
.text:004116E0 person          = Person ptr -0Ch
.text:004116E0 var_4           = dword ptr -4
.text:004116E0 argc            = dword ptr  8
.text:004116E0 argv            = dword ptr  0Ch
.text:004116E0
.text:004116E0                 push    ebp
.text:004116E1                 mov     ebp, esp
.text:004116E3                 sub     esp, 0D0h
.text:004116E9                 push    ebx
.text:004116EA                 push    esi
.text:004116EB                 push    edi
.text:004116EC                 lea     edi, [ebp+var_D0]
.text:004116F2                 mov     ecx, 34h
.text:004116F7                 mov     eax, 0CCCCCCCCh
.text:004116FC                 rep stosd
.text:004116FE                 mov     eax, ___security_cookie
.text:00411703                 xor     eax, ebp
.text:00411705                 mov     [ebp+var_4], eax
.text:00411708                 lea     ecx, [ebp+person] ; this
.text:0041170B                 call    j_??0Person@@QAE@XZ ; Person::Person(void)
.text:00411710                 xor     eax, eax
.text:00411712                 push    edx
.text:00411713                 mov     ecx, ebp        ; frame
.text:00411715                 push    eax
.text:00411716                 lea     edx, v          ; v
.text:0041171C                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:00411721                 pop     eax
.text:00411722                 pop     edx
.text:00411723                 pop     edi
.text:00411724                 pop     esi
.text:00411725                 pop     ebx
.text:00411726                 mov     ecx, [ebp+var_4]
.text:00411729                 xor     ecx, ebp        ; cookie
.text:0041172B                 call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:00411730                 add     esp, 0D0h
.text:00411736                 cmp     ebp, esp
.text:00411738                 call    j___RTC_CheckEsp
.text:0041173D                 mov     esp, ebp
.text:0041173F                 pop     ebp
.text:00411740                 retn
.text:00411740 ; ---------------------------------------------------------------------------
.text:00411741                 align 4
.text:00411744 ; _RTC_framedesc v
.text:00411744 v               _RTC_framedesc <1, offset dword_41174C>
.text:00411744                                         ; DATA XREF: _main+36↑o
.text:0041174C dword_41174C    dd 0FFFFFFF4h, 4        ; DATA XREF: _main:v↑o
.text:00411754                 dd offset aPerson       ; "person"
.text:00411758 aPerson         db 'person',0           ; DATA XREF: _main+74↑o
.text:00411758 _main           endp

总结:局部对象构造函数的必要条件

  • 该成员函数是这个对象在作用域内调用的第一个成员函数,根据this指针可以区分每个对象
  • 这个成员函数通过thiscall方式调用
  • 这个函数返回this指针

10.1.2 堆对象

debug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

class Person {
public:
	Person() {
		age = 20;
	}
	int age;
};

int main(int argc, char* argv[]) {
	Person* p = new Person;
	//为了突出本节讨论的问题,这里没有检查new运算的返回值
	p->age = 21;
	printf("%d\n", p->age);

	return 0;
}

ida

 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
void __thiscall Person::Person(Person *this)
{
  this->age = 20;
}

int __cdecl main()
{
  _DWORD *v0; // eax
  _DWORD *v2; // [esp+10h] [ebp-F4h]
  Person *v3; // [esp+18h] [ebp-ECh]

  v3 = (Person *)operator new(4u);
  if ( v3 )
  {
    Person::Person(v3);
    v2 = v0;
  }
  else
  {
    v2 = 0;
  }
  *v2 = 21;
  j__printf("%d\n", *v2);
  return 0;
}
  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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
.text:00411820 ; void __thiscall Person::Person(Person *this)
.text:00411820 ??0Person@@QAE@XZ proc near             ; CODE XREF: Person::Person(void)↑j
.text:00411820
.text:00411820 var_CC          = byte ptr -0CCh
.text:00411820 this            = dword ptr -8
.text:00411820
.text:00411820                 push    ebp
.text:00411821                 mov     ebp, esp
.text:00411823                 sub     esp, 0CCh
.text:00411829                 push    ebx
.text:0041182A                 push    esi
.text:0041182B                 push    edi
.text:0041182C                 push    ecx
.text:0041182D                 lea     edi, [ebp+var_CC]
.text:00411833                 mov     ecx, 33h
.text:00411838                 mov     eax, 0CCCCCCCCh
.text:0041183D                 rep stosd
.text:0041183F                 pop     ecx
.text:00411840                 mov     [ebp+this], ecx
.text:00411843                 mov     eax, [ebp+this]
.text:00411846                 mov     dword ptr [eax], 14h
.text:0041184C                 mov     eax, [ebp+this]
.text:0041184F                 pop     edi
.text:00411850                 pop     esi
.text:00411851                 pop     ebx
.text:00411852                 mov     esp, ebp
.text:00411854                 pop     ebp
.text:00411855                 retn
.text:00411855 ??0Person@@QAE@XZ endp

.text:00411930 ; int __cdecl main(int argc, char **argv)
.text:00411930 _main           proc near               ; CODE XREF: _main_0↑j
.text:00411930
.text:00411930 var_F4          = dword ptr -0F4h
.text:00411930 var_EC          = dword ptr -0ECh
.text:00411930 var_E0          = dword ptr -0E0h
.text:00411930 p               = dword ptr -14h
.text:00411930 var_C           = dword ptr -0Ch
.text:00411930 var_4           = dword ptr -4
.text:00411930 argc            = dword ptr  8
.text:00411930 argv            = dword ptr  0Ch
.text:00411930
.text:00411930 ; FUNCTION CHUNK AT .text:004162C0 SIZE 00000012 BYTES
.text:00411930
.text:00411930 ; __unwind { // __ehhandler$_main
.text:00411930                 push    ebp
.text:00411931                 mov     ebp, esp
.text:00411933                 push    0FFFFFFFFh
.text:00411935                 push    offset __ehhandler$_main
.text:0041193A                 mov     eax, large fs:0
.text:00411940                 push    eax
.text:00411941                 sub     esp, 0E8h
.text:00411947                 push    ebx
.text:00411948                 push    esi
.text:00411949                 push    edi
.text:0041194A                 lea     edi, [ebp+var_F4]
.text:00411950                 mov     ecx, 3Ah
.text:00411955                 mov     eax, 0CCCCCCCCh
.text:0041195A                 rep stosd
.text:0041195C                 mov     eax, ___security_cookie
.text:00411961                 xor     eax, ebp
.text:00411963                 push    eax
.text:00411964                 lea     eax, [ebp+var_C]
.text:00411967                 mov     large fs:0, eax
.text:0041196D                 push    4               ; size
.text:0041196F                 call    j_??2@YAPAXI@Z  ; operator new(uint)
.text:00411974                 add     esp, 4
.text:00411977                 mov     [ebp+var_EC], eax ; 申请空间的首地址
.text:0041197D ;   try {
.text:0041197D                 mov     [ebp+var_4], 0
.text:00411984                 cmp     [ebp+var_EC], 0
.text:0041198B                 jz      short loc_4119A0 ; 只有申请空间的首地址不为0时,才调用Person对象的构造函数
.text:0041198D                 mov     ecx, [ebp+var_EC] ; this
.text:00411993                 call    j_??0Person@@QAE@XZ ; Person::Person(void)
.text:00411998                 mov     [ebp+var_F4], eax ; 构造函数返回的this指针,保存到临时变量中
.text:0041199E                 jmp     short loc_4119AA
.text:004119A0 ; ---------------------------------------------------------------------------
.text:004119A0
.text:004119A0 loc_4119A0:                             ; CODE XREF: _main+5B↑j
.text:004119A0                 mov     [ebp+var_F4], 0 ; 申请空间失败,设置临时对象指向为0
.text:004119AA
.text:004119AA loc_4119AA:                             ; CODE XREF: _main+6E↑j
.text:004119AA                 mov     eax, [ebp+var_F4]
.text:004119B0                 mov     [ebp+var_E0], eax
.text:004119B0 ;   } // starts at 41197D
.text:004119B6                 mov     [ebp+var_4], 0FFFFFFFFh
.text:004119BD                 mov     ecx, [ebp+var_E0] ; Person* p
.text:004119C3                 mov     [ebp+p], ecx
.text:004119C6                 mov     eax, [ebp+p]
.text:004119C9                 mov     dword ptr [eax], 15h ; 设置Person对象的前4字节为21,即:this->age = 21
.text:004119CF                 mov     eax, [ebp+p]
.text:004119D2                 mov     ecx, [eax]
.text:004119D4                 push    ecx
.text:004119D5                 push    offset _Format  ; "%d\n"
.text:004119DA                 call    j__printf
.text:004119DF                 add     esp, 8
.text:004119E2                 xor     eax, eax
.text:004119E4                 mov     ecx, [ebp+var_C]
.text:004119E7                 mov     large fs:0, ecx
.text:004119EE                 pop     ecx
.text:004119EF                 pop     edi
.text:004119F0                 pop     esi
.text:004119F1                 pop     ebx
.text:004119F2                 add     esp, 0F4h
.text:004119F8                 cmp     ebp, esp
.text:004119FA                 call    j___RTC_CheckEsp
.text:004119FF                 mov     esp, ebp
.text:00411A01                 pop     ebp
.text:00411A02                 retn
.text:00411A02 ; } // starts at 411930
.text:00411A02 _main           endp

总结:

  • 使用new申请堆空间之后,需要调用构造函数来完成对象数据成员的初始化
  • 如果堆空间申请失败,则不调用构造函数
  • 如果new执行成功,返回值是对象的首地址
  • 识别堆对象的构造函数:重点分析new的双分支结构,在判定new成功的分支迅速定位并得到构造函数

10.1.3 参数对象

当对象作为函数参数时,会调用赋值构造函数

debug

 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
36
37
38
39
40
41
42
#include <stdio.h>
#include <string.h>

class Person {
public:
	Person() {
		name = NULL;//无参构造函数,初始化指针
	}

	Person(const Person& obj) {
		// 注:如果在复制构造函数中直接复制指针值,那么对象内的两个成员指针会指向同一个资源,这属于浅拷贝
			// this->name = obj.name;
			// 为实参对象中的指针所指向的堆空间制作一份副本,这就是深拷贝了
		int len = strlen(obj.name);
		this->name = new char[len + sizeof(char)]; // 为便于讲解,这里没有检查指针
		strcpy(this->name, obj.name);
	}

	void setName(const char* name) {
		int len = strlen(name);
		if (this->name != NULL) {
			delete[] this->name;
		}
		this->name = new char[len + sizeof(char)]; // 为便于讲解,这里没有检查指针
		strcpy(this->name, name);
	}

public:
	char* name;
};

void show(Person person) { // 参数是对象类型,会触发复制构造函数
	printf("name:%s\n", person.name);
}

int main(int argc, char* argv[]) {
	Person person;
	person.setName("Hello");
	show(person);

	return 0;
}

ida

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
void __thiscall Person::Person(Person *this)
{
  this->name = 0;
}

void __thiscall Person::Person(Person *this, Person *obj)
{
  Person *v2; // STF0_4
  int len; // STE4_4

  v2 = this;
  len = j__strlen(obj->name);
  v2->name = (char *)operator new[](len + 1);
  j__strcpy(v2->name, obj->name);
}

void __thiscall Person::__autoclassinit2(Person *this, unsigned int Size)
{
  j__memset(this, 0, Size);
}

void __thiscall Person::setName(Person *this, const char *name)
{
  int len; // [esp+E8h] [ebp-14h]
  Person *thisa; // [esp+F4h] [ebp-8h]

  thisa = this;
  len = j__strlen(name);
  if ( thisa->name )
    operator delete[](thisa->name);
  thisa->name = (char *)operator new[](len + 1);
  j__strcpy(thisa->name, name);
}

int __cdecl main()
{
  char *v0; // ecx
  Person v2; // [esp-4h] [ebp-ECh]
  Person person; // [esp+DCh] [ebp-Ch]

  Person::__autoclassinit2(&person, 4u);
  Person::Person(&person);
  Person::setName(&person, "Hello");
  v2.name = v0;
  Person::Person(&v2, &person);
  show(v2);
  return 0;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
.text:00414D20 ; int __cdecl main(int argc, char **argv)
.text:00414D20 _main           proc near               ; CODE XREF: _main_0↑j
.text:00414D20
.text:00414D20 var_DC          = byte ptr -0DCh
.text:00414D20 person          = Person ptr -0Ch
.text:00414D20 var_4           = dword ptr -4
.text:00414D20 argc            = dword ptr  8
.text:00414D20 argv            = dword ptr  0Ch
.text:00414D20
.text:00414D20                 push    ebp
.text:00414D21                 mov     ebp, esp
.text:00414D23                 sub     esp, 0DCh
.text:00414D29                 push    ebx
.text:00414D2A                 push    esi
.text:00414D2B                 push    edi
.text:00414D2C                 lea     edi, [ebp+var_DC]
.text:00414D32                 mov     ecx, 37h
.text:00414D37                 mov     eax, 0CCCCCCCCh
.text:00414D3C                 rep stosd
.text:00414D3E                 mov     eax, ___security_cookie
.text:00414D43                 xor     eax, ebp
.text:00414D45                 mov     [ebp+var_4], eax
.text:00414D48                 push    4               ; unsigned int
.text:00414D4A                 lea     ecx, [ebp+person] ; this
.text:00414D4D                 call    j_?__autoclassinit2@Person@@QAEXI@Z ; 将person对象的变量初始化为默认值或者自定义值,这里设置为0
.text:00414D52                 lea     ecx, [ebp+person] ; this
.text:00414D55                 call    j_??0Person@@QAE@XZ ; Person::Person(void)
.text:00414D5A                 push    offset name     ; "Hello"
.text:00414D5F                 lea     ecx, [ebp+person] ; this
.text:00414D62                 call    j_?setName@Person@@QAEXPBD@Z ; Person::setName(char const *)
.text:00414D67                 push    ecx             ; person
.text:00414D68                 mov     ecx, esp        ; this
.text:00414D6A                 lea     eax, [ebp+person] ; 调用show方法,通过传参会调用拷贝构造函数:Person(const Person& obj)
.text:00414D6D                 push    eax             ; obj
.text:00414D6E                 call    j_??0Person@@QAE@ABV0@@Z ; Person::Person(Person const &)
.text:00414D73                 call    j_?show@@YAXVPerson@@@Z ; show(Person)
.text:00414D78                 add     esp, 4
.text:00414D7B                 xor     eax, eax
.text:00414D7D                 push    edx
.text:00414D7E                 mov     ecx, ebp        ; frame
.text:00414D80                 push    eax
.text:00414D81                 lea     edx, v          ; v
.text:00414D87                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:00414D8C                 pop     eax
.text:00414D8D                 pop     edx
.text:00414D8E                 pop     edi
.text:00414D8F                 pop     esi
.text:00414D90                 pop     ebx
.text:00414D91                 mov     ecx, [ebp+var_4]
.text:00414D94                 xor     ecx, ebp        ; cookie
.text:00414D96                 call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:00414D9B                 add     esp, 0DCh
.text:00414DA1                 cmp     ebp, esp
.text:00414DA3                 call    j___RTC_CheckEsp
.text:00414DA8                 mov     esp, ebp
.text:00414DAA                 pop     ebp
.text:00414DAB                 retn
.text:00414DAB ; ---------------------------------------------------------------------------
.text:00414DAC ; _RTC_framedesc v
.text:00414DAC v               _RTC_framedesc <1, offset dword_414DB4>
.text:00414DAC                                         ; DATA XREF: _main+61↑o
.text:00414DB4 dword_414DB4    dd 0FFFFFFF4h, 4        ; DATA XREF: _main:v↑o
.text:00414DBC                 dd offset aPerson       ; "person"
.text:00414DC0 aPerson         db 'person',0           ; DATA XREF: _main+9C↑o
.text:00414DC0 _main           endp

10.1.4 返回对象

返回对象与参数对象类似,都会使用赋值构造函数。但是,两者使用时机不同

  • 当对象为参数时,在进入函数前使用赋值构造函数
  • 返回对象则在函数返回时使用赋值构造函数

debug

 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
36
37
#include <stdio.h>
#include <string.h>

class Person {
public:
	Person() {
		name = NULL;//无参构造函数,初始化指针
	}
	Person(const Person& obj) {
		// 注:如果在复制构造函数中直接复制指针值,那么对象内的两个成员指针会指向同一个资源,这属于浅拷贝
			// this->name = obj.name;
			// 为实参对象中的指针所指向的堆空间制作一份副本,这就是深拷贝了
		int len = strlen(obj.name);
		this->name = new char[len + sizeof(char)]; // 为便于讲解,这里没有检查指针
		strcpy(this->name, obj.name);
	}
	void setName(const char* name) {
		int len = strlen(name);
		if (this->name != NULL) {
			delete[] this->name;
		}
		this->name = new char[len + sizeof(char)]; // 为便于讲解,这里没有检查指针
		strcpy(this->name, name);
	}
public:
	char* name;
};

Person getObject() {
	Person person;
	person.setName("Hello");
	return person; //返回类型为对象
}
int main(int argc, char* argv[]) {
	Person person = getObject();
	return 0;
}

ida

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
void __thiscall Person::__autoclassinit2(Person *this, unsigned int Size)
{
  j__memset(this, 0, Size);
}

void __thiscall Person::Person(Person *this)
{
  this->name = 0;
}

void __thiscall Person::Person(Person *this, Person *obj)
{
  Person *v2; // STF0_4
  int len; // STE4_4

  v2 = this;
  len = j__strlen(obj->name);
  v2->name = (char *)operator new[](len + 1);
  j__strcpy(v2->name, obj->name);
}

void __thiscall Person::setName(Person *this, const char *name)
{
  int len; // [esp+E8h] [ebp-14h]
  Person *thisa; // [esp+F4h] [ebp-8h]

  thisa = this;
  len = j__strlen(name);
  if ( thisa->name )
    operator delete[](thisa->name);
  thisa->name = (char *)operator new[](len + 1);
  j__strcpy(thisa->name, name);
}

unsigned __int64 __cdecl getObject(Person *result)
{
  unsigned int v1; // edx
  Person person; // [esp+D0h] [ebp-Ch]

  Person::__autoclassinit2(&person, 4u);
  Person::Person(&person);
  Person::setName(&person, "Hello");
  Person::Person(result, &person);
  return __PAIR__(v1, (unsigned int)result);
}

__int64 __cdecl main()
{
  int v0; // edx
  __int64 v1; // ST00_8
  Person person; // [esp+D0h] [ebp-Ch]

  Person::__autoclassinit2(&person, 4u);
  getObject(&person);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
.text:00411B10 ; int __cdecl main(int argc, char **argv)
.text:00411B10 _main           proc near               ; CODE XREF: _main_0↑j
.text:00411B10
.text:00411B10 var_D0          = byte ptr -0D0h
.text:00411B10 person          = Person ptr -0Ch
.text:00411B10 var_4           = dword ptr -4
.text:00411B10 argc            = dword ptr  8
.text:00411B10 argv            = dword ptr  0Ch
.text:00411B10
.text:00411B10                 push    ebp
.text:00411B11                 mov     ebp, esp
.text:00411B13                 sub     esp, 0D0h
.text:00411B19                 push    ebx
.text:00411B1A                 push    esi
.text:00411B1B                 push    edi
.text:00411B1C                 lea     edi, [ebp+var_D0]
.text:00411B22                 mov     ecx, 34h
.text:00411B27                 mov     eax, 0CCCCCCCCh
.text:00411B2C                 rep stosd
.text:00411B2E                 mov     eax, ___security_cookie
.text:00411B33                 xor     eax, ebp
.text:00411B35                 mov     [ebp+var_4], eax
.text:00411B38                 push    4               ; unsigned int
.text:00411B3A                 lea     ecx, [ebp+person] ; this
.text:00411B3D                 call    j_?__autoclassinit2@Person@@QAEXI@Z ; 将person对象的变量初始化
.text:00411B42                 lea     eax, [ebp+person]
.text:00411B45                 push    eax             ; result
.text:00411B46                 call    j_?getObject@@YA?AVPerson@@XZ ; 将person对象作为 getObject方法的返回对象
.text:00411B4B                 add     esp, 4
.text:00411B4E                 xor     eax, eax
.text:00411B50                 push    edx
.text:00411B51                 mov     ecx, ebp        ; frame
.text:00411B53                 push    eax
.text:00411B54                 lea     edx, stru_411B80 ; v
.text:00411B5A                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:00411B5F                 pop     eax
.text:00411B60                 pop     edx
.text:00411B61                 pop     edi
.text:00411B62                 pop     esi
.text:00411B63                 pop     ebx
.text:00411B64                 mov     ecx, [ebp+var_4]
.text:00411B67                 xor     ecx, ebp        ; cookie
.text:00411B69                 call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:00411B6E                 add     esp, 0D0h
.text:00411B74                 cmp     ebp, esp
.text:00411B76                 call    j___RTC_CheckEsp
.text:00411B7B                 mov     esp, ebp
.text:00411B7D                 pop     ebp
.text:00411B7E                 retn
.text:00411B7E ; ---------------------------------------------------------------------------
.text:00411B7F                 align 10h
.text:00411B80 ; _RTC_framedesc stru_411B80
.text:00411B80 stru_411B80     _RTC_framedesc <1, offset dword_411B88>
.text:00411B80                                         ; DATA XREF: _main+44↑o
.text:00411B88 dword_411B88    dd 0FFFFFFF4h, 4        ; DATA XREF: _main:stru_411B80↑o
.text:00411B90                 dd offset aPerson_0     ; "person"
.text:00411B94 aPerson_0       db 'person',0           ; DATA XREF: _main+80↑o
.text:00411B94 _main           endp

10.2 析构对象的出现时机

10.2.1 局部对象

重点考察作用域的结束处,当对象所在作用域结束后,将销毁作用域所有变量的栈空间,此时便是析构函数出现的时机。析构函数同样属于成员函数,因此在调用的过程中也需要传递this指针。

debug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <stdio.h>

class Person {
public:
	Person() {
		age = 1;
	}
	~Person() {
		printf("~Person()\n");
	}
private:
	int age;
};

int main(int argc, char* argv[]) {
	Person person;

	return 0; //退出函数后调用析构函数
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void __thiscall Person::Person(Person *this)
{
  this->age = 1;
}

void Person::~Person()
{
  j__printf("~Person()\n");
}

__int64 __cdecl main()
{
  int v0; // edx
  __int64 v1; // ST00_8
  Person person; // [esp+DCh] [ebp-Ch]

  Person::Person(&person);
  Person::~Person(&person);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
.text:00411970 ; int __cdecl main(int argc, char **argv)
.text:00411970 _main           proc near               ; CODE XREF: _main_0↑j
.text:00411970                                         ; .text:00411325↑j
.text:00411970
.text:00411970 var_DC          = byte ptr -0DCh
.text:00411970 var_D8          = dword ptr -0D8h
.text:00411970 person          = Person ptr -0Ch
.text:00411970 var_4           = dword ptr -4
.text:00411970 argc            = dword ptr  8
.text:00411970 argv            = dword ptr  0Ch
.text:00411970
.text:00411970                 push    ebp
.text:00411971                 mov     ebp, esp
.text:00411973                 sub     esp, 0DCh
.text:00411979                 push    ebx
.text:0041197A                 push    esi
.text:0041197B                 push    edi
.text:0041197C                 lea     edi, [ebp+var_DC]
.text:00411982                 mov     ecx, 37h
.text:00411987                 mov     eax, 0CCCCCCCCh
.text:0041198C                 rep stosd
.text:0041198E                 mov     eax, ___security_cookie
.text:00411993                 xor     eax, ebp
.text:00411995                 mov     [ebp+var_4], eax
.text:00411998                 lea     ecx, [ebp+person] ; this
.text:0041199B                 call    j_??0Person@@QAE@XZ ; Person::Person(void)
.text:004119A0                 mov     [ebp+var_D8], 0
.text:004119AA                 lea     ecx, [ebp+person] ; this
.text:004119AD                 call    j_??1Person@@QAE@XZ ; Person::~Person(void)
.text:004119B2                 mov     eax, [ebp+var_D8]
.text:004119B8                 push    edx
.text:004119B9                 mov     ecx, ebp        ; frame
.text:004119BB                 push    eax
.text:004119BC                 lea     edx, v          ; v
.text:004119C2                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:004119C7                 pop     eax
.text:004119C8                 pop     edx
.text:004119C9                 pop     edi
.text:004119CA                 pop     esi
.text:004119CB                 pop     ebx
.text:004119CC                 mov     ecx, [ebp+var_4]
.text:004119CF                 xor     ecx, ebp        ; cookie
.text:004119D1                 call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:004119D6                 add     esp, 0DCh
.text:004119DC                 cmp     ebp, esp
.text:004119DE                 call    j___RTC_CheckEsp
.text:004119E3                 mov     esp, ebp
.text:004119E5                 pop     ebp
.text:004119E6                 retn
.text:004119E6 ; ---------------------------------------------------------------------------
.text:004119E7                 align 4
.text:004119E8 ; _RTC_framedesc v
.text:004119E8 v               _RTC_framedesc <1, offset dword_4119F0>
.text:004119E8                                         ; DATA XREF: _main+4C↑o
.text:004119F0 dword_4119F0    dd 0FFFFFFF4h, 4        ; DATA XREF: _main:v↑o
.text:004119F8                 dd offset aPerson       ; "person"
.text:004119FC aPerson         db 'person',0           ; DATA XREF: _main+88↑o
.text:004119FC _main           endp

10.2.2 堆对象

用detele释放对象所在的空间,delete的使用便是找到堆对象调用析构的关键点

debug

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Person {
public:
	Person() {
		age = 20;
	}
	~Person() {
		printf("~Person()\n");
	}
	int age;
};

int main(int argc, char* argv[]) {
	Person* person = new Person();
	person->age = 21; //为了便于讲解,这里没检查指针
	printf("%d\n", person->age);
	delete person;

	return 0;
}

ida

 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
36
37
38
39
40
41
42
43
void __thiscall Person::Person(Person *this)
{
  this->age = 20;
}

void Person::~Person()
{
  j__printf("~Person()\n");
}

Person *__thiscall Person::scalar deleting destructor(Person *this, unsigned int a2)
{
  Person *thisa; // [esp+D0h] [ebp-8h]

  thisa = this;
  Person::~Person(this);
  if ( a2 & 1 )
    operator delete(thisa, 4u);
  return thisa;
}

int __cdecl main()
{
  Person *v0; // eax
  Person *v2; // [esp+10h] [ebp-10Ch]
  Person *v3; // [esp+30h] [ebp-ECh]

  v3 = (Person *)operator new(4u);
  if ( v3 )
  {
    Person::Person(v3);
    v2 = v0;
  }
  else
  {
    v2 = 0;
  }
  v2->age = 21;
  j__printf("%d\n", v2->age);
  if ( v2 )
    Person::scalar deleting destructor(v2, 1u);
  return 0;
}
  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
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
.text:004118E0 ; void *__thiscall Person::`scalar deleting destructor'(Person *this, unsigned int)
.text:004118E0 ??_GPerson@@QAEPAXI@Z proc near         ; CODE XREF: Person::`scalar deleting destructor'(uint)↑j
.text:004118E0
.text:004118E0 var_CC          = byte ptr -0CCh
.text:004118E0 this            = dword ptr -8
.text:004118E0 arg_0           = dword ptr  8
.text:004118E0
.text:004118E0                 push    ebp
.text:004118E1                 mov     ebp, esp
.text:004118E3                 sub     esp, 0CCh
.text:004118E9                 push    ebx
.text:004118EA                 push    esi
.text:004118EB                 push    edi
.text:004118EC                 push    ecx
.text:004118ED                 lea     edi, [ebp+var_CC]
.text:004118F3                 mov     ecx, 33h
.text:004118F8                 mov     eax, 0CCCCCCCCh
.text:004118FD                 rep stosd
.text:004118FF                 pop     ecx
.text:00411900                 mov     [ebp+this], ecx ; 设置this指针
.text:00411903                 mov     ecx, [ebp+this] ; this
.text:00411906                 call    j_??1Person@@QAE@XZ ; Person::~Person(void)
.text:0041190B                 mov     eax, [ebp+arg_0]
.text:0041190E                 and     eax, 1
.text:00411911                 jz      short loc_411921
.text:00411913                 push    4               ; __formal
.text:00411915                 mov     eax, [ebp+this] ; operator delete(this, 4)
.text:00411918                 push    eax             ; block
.text:00411919                 call    j_??3@YAXPAXI@Z ; operator delete(void *,uint)
.text:0041191E                 add     esp, 8
.text:00411921
.text:00411921 loc_411921:                             ; CODE XREF: Person::`scalar deleting destructor'(uint)+31↑j
.text:00411921                 mov     eax, [ebp+this]
.text:00411924                 pop     edi
.text:00411925                 pop     esi
.text:00411926                 pop     ebx
.text:00411927                 add     esp, 0CCh
.text:0041192D                 cmp     ebp, esp
.text:0041192F                 call    j___RTC_CheckEsp
.text:00411934                 mov     esp, ebp
.text:00411936                 pop     ebp
.text:00411937                 retn    4
.text:00411937 ??_GPerson@@QAEPAXI@Z endp

.text:00411A10 ; int __cdecl main(int argc, char **argv)
.text:00411A10 _main           proc near               ; CODE XREF: _main_0↑j
.text:00411A10
.text:00411A10 var_10C         = dword ptr -10Ch
.text:00411A10 var_104         = dword ptr -104h
.text:00411A10 var_F8          = dword ptr -0F8h
.text:00411A10 var_EC          = dword ptr -0ECh
.text:00411A10 var_E0          = dword ptr -0E0h
.text:00411A10 person          = dword ptr -14h
.text:00411A10 var_C           = dword ptr -0Ch
.text:00411A10 var_4           = dword ptr -4
.text:00411A10 argc            = dword ptr  8
.text:00411A10 argv            = dword ptr  0Ch
.text:00411A10
.text:00411A10 ; FUNCTION CHUNK AT .text:004163E0 SIZE 00000012 BYTES
.text:00411A10
.text:00411A10 ; __unwind { // __ehhandler$_main
.text:00411A10                 push    ebp
.text:00411A11                 mov     ebp, esp
.text:00411A13                 push    0FFFFFFFFh
.text:00411A15                 push    offset __ehhandler$_main
.text:00411A1A                 mov     eax, large fs:0
.text:00411A20                 push    eax
.text:00411A21                 sub     esp, 100h
.text:00411A27                 push    ebx
.text:00411A28                 push    esi
.text:00411A29                 push    edi
.text:00411A2A                 lea     edi, [ebp+var_10C]
.text:00411A30                 mov     ecx, 40h
.text:00411A35                 mov     eax, 0CCCCCCCCh
.text:00411A3A                 rep stosd
.text:00411A3C                 mov     eax, ___security_cookie
.text:00411A41                 xor     eax, ebp
.text:00411A43                 push    eax
.text:00411A44                 lea     eax, [ebp+var_C]
.text:00411A47                 mov     large fs:0, eax
.text:00411A4D                 push    4               ; size
.text:00411A4F                 call    j_??2@YAPAXI@Z  ; operator new(uint)
.text:00411A54                 add     esp, 4
.text:00411A57                 mov     [ebp+var_EC], eax ; new[4] person对象的首地址
.text:00411A5D ;   try {
.text:00411A5D                 mov     [ebp+var_4], 0
.text:00411A64                 cmp     [ebp+var_EC], 0 ; 判断new方法是否成功返回
.text:00411A6B                 jz      short loc_411A80 ; 如果new调用失败,设置指向new的临时对象指向0
.text:00411A6D                 mov     ecx, [ebp+var_EC] ; this
.text:00411A73                 call    j_??0Person@@QAE@XZ ; Person::Person(void)
.text:00411A78                 mov     [ebp+var_10C], eax
.text:00411A7E                 jmp     short loc_411A8A
.text:00411A80 ; ---------------------------------------------------------------------------
.text:00411A80
.text:00411A80 loc_411A80:                             ; CODE XREF: _main+5B↑j
.text:00411A80                 mov     [ebp+var_10C], 0 ; 如果new调用失败,设置指向new的临时对象指向0
.text:00411A8A
.text:00411A8A loc_411A8A:                             ; CODE XREF: _main+6E↑j
.text:00411A8A                 mov     eax, [ebp+var_10C]
.text:00411A90                 mov     [ebp+var_E0], eax
.text:00411A90 ;   } // starts at 411A5D
.text:00411A96                 mov     [ebp+var_4], 0FFFFFFFFh
.text:00411A9D                 mov     ecx, [ebp+var_E0]
.text:00411AA3                 mov     [ebp+person], ecx ; Person* person = new Person()
.text:00411AA6                 mov     eax, [ebp+person]
.text:00411AA9                 mov     dword ptr [eax], 15h ; person.age = 21
.text:00411AAF                 mov     eax, [ebp+person]
.text:00411AB2                 mov     ecx, [eax]      ; person->age
.text:00411AB4                 push    ecx
.text:00411AB5                 push    offset aD       ; "%d\n"
.text:00411ABA                 call    j__printf
.text:00411ABF                 add     esp, 8
.text:00411AC2                 mov     eax, [ebp+person]
.text:00411AC5                 mov     [ebp+var_104], eax
.text:00411ACB                 mov     ecx, [ebp+var_104]
.text:00411AD1                 mov     [ebp+var_F8], ecx
.text:00411AD7                 cmp     [ebp+var_F8], 0 ; if(person != 0)
.text:00411ADE                 jz      short loc_411AF5 ; 设置指向new返回的临时对象为0
.text:00411AE0                 push    1               ; unsigned int
.text:00411AE2                 mov     ecx, [ebp+var_F8] ; this
.text:00411AE8                 call    j_??_GPerson@@QAEPAXI@Z ; Person::`scalar deleting destructor'(uint)
.text:00411AED                 mov     [ebp+var_10C], eax ; 设置指向new返回的临时对象为 this
.text:00411AF3                 jmp     short loc_411AFF
.text:00411AF5 ; ---------------------------------------------------------------------------
.text:00411AF5
.text:00411AF5 loc_411AF5:                             ; CODE XREF: _main+CE↑j
.text:00411AF5                 mov     [ebp+var_10C], 0 ; 设置指向new返回的临时对象为0
.text:00411AFF
.text:00411AFF loc_411AFF:                             ; CODE XREF: _main+E3↑j
.text:00411AFF                 xor     eax, eax
.text:00411B01                 mov     ecx, [ebp+var_C]
.text:00411B04                 mov     large fs:0, ecx
.text:00411B0B                 pop     ecx
.text:00411B0C                 pop     edi
.text:00411B0D                 pop     esi
.text:00411B0E                 pop     ebx
.text:00411B0F                 add     esp, 10Ch
.text:00411B15                 cmp     ebp, esp
.text:00411B17                 call    j___RTC_CheckEsp
.text:00411B1C                 mov     esp, ebp
.text:00411B1E                 pop     ebp
.text:00411B1F                 retn
.text:00411B1F ; } // starts at 411A10
.text:00411B1F _main           endp

11. 虚函数

对于具有虚函数的类而言,构造函数和析构函数的识别过程更加简单。而且,在类中定义虚函数后,如果没有提供

构造函数,编译器会生成默认的构造函数。

对象的多态需要通过虚表和虚指针完成,虚表指针被定义在对象首地址处,因此虚函数必须作为成员函数使用。

11.1 虚函数的机制

当类中定义有虚函数,编译器会将给类中所有虚函数的首地址保存在一张地址表,这张表被称为虚函数地址表,简称虚表。同时还会在类中添加一个隐藏数据成员,称为虚表指针,该指针保存虚表的首地址,用于记录和查找虚函数。

11.1.1 默认构造函数初始化虚表指针的过程

  • 没有编写构造函数时,编译器默认提供构造函数,以完成虚表指针的初始化
  • 虚表中虚函数的地址排列顺序:先声明的虚函数的地址会被排列在虚表靠前的位置
  • 第一个被声明的虚函数的地址在虚表的首地址处

debug

 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>

class Person {
public:
	virtual int getAge() { //虚函数定义
		return age;
	}
	virtual void setAge(int age) { //虚函数定义
		this->age = age;
	}
private:
	int age;
};

int main(int argc, char* argv[]) {
	Person person;
	//int size = sizeof(Person);
	//定义了虚函数后,因为还含有隐藏数据成员虚表指针,所以Person大小为8
	//printf("%d",size);   8

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
void __thiscall Person::Person(Person *this)
{
  this->vfptr = (PersonVtbl *)&Person::`vftable';
}

__int64 __cdecl main()
{
  int v0; // edx
  __int64 v1; // ST00_8
  Person person; // [esp+D0h] [ebp-10h]

  Person::Person(&person);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// 虚表
.rdata:00416B34 ; const Person::`vftable'
.rdata:00416B34 ??_7Person@@6B@ dd offset j_?getAge@Person@@UAEHXZ
.rdata:00416B34                                         ; DATA XREF: Person::Person(void)+26↑o
.rdata:00416B34                                         ; Person::getAge(void)
.rdata:00416B38                 dd offset j_?setAge@Person@@UAEXH@Z ; Person::setAge(int)
.rdata:00416B3C                 align 10h
.rdata:00416B40                 dd offset ??_R4type_info@@6B@ ; const type_info::`RTTI Complete Object Locator'
.rdata:00416B44 ; const type_info::`vftable'

.text:004116E0 ; void __thiscall Person::Person(Person *this)
.text:004116E0 ??0Person@@QAE@XZ proc near             ; CODE XREF: Person::Person(void)↑j
.text:004116E0
.text:004116E0 var_CC          = byte ptr -0CCh
.text:004116E0 this            = dword ptr -8
.text:004116E0
.text:004116E0                 push    ebp
.text:004116E1                 mov     ebp, esp
.text:004116E3                 sub     esp, 0CCh
.text:004116E9                 push    ebx
.text:004116EA                 push    esi
.text:004116EB                 push    edi
.text:004116EC                 push    ecx
.text:004116ED                 lea     edi, [ebp+var_CC]
.text:004116F3                 mov     ecx, 33h
.text:004116F8                 mov     eax, 0CCCCCCCCh
.text:004116FD                 rep stosd
.text:004116FF                 pop     ecx
.text:00411700                 mov     [ebp+this], ecx ; 设置this指针
.text:00411703                 mov     eax, [ebp+this]
.text:00411706                 mov     dword ptr [eax], offset ??_7Person@@6B@ ; 在person对象前4字节设置虚表
.text:0041170C                 mov     eax, [ebp+this]
.text:0041170F                 pop     edi
.text:00411710                 pop     esi
.text:00411711                 pop     ebx
.text:00411712                 mov     esp, ebp
.text:00411714                 pop     ebp
.text:00411715                 retn
.text:00411715 ??0Person@@QAE@XZ endp

.text:004117C0 ; int __cdecl main(int argc, char **argv)
.text:004117C0 _main           proc near               ; CODE XREF: _main_0↑j
.text:004117C0
.text:004117C0 var_D4          = byte ptr -0D4h
.text:004117C0 person          = Person ptr -10h
.text:004117C0 var_4           = dword ptr -4
.text:004117C0 argc            = dword ptr  8
.text:004117C0 argv            = dword ptr  0Ch
.text:004117C0
.text:004117C0                 push    ebp
.text:004117C1                 mov     ebp, esp
.text:004117C3                 sub     esp, 0D4h
.text:004117C9                 push    ebx
.text:004117CA                 push    esi
.text:004117CB                 push    edi
.text:004117CC                 lea     edi, [ebp+var_D4]
.text:004117D2                 mov     ecx, 35h
.text:004117D7                 mov     eax, 0CCCCCCCCh
.text:004117DC                 rep stosd
.text:004117DE                 mov     eax, ___security_cookie
.text:004117E3                 xor     eax, ebp
.text:004117E5                 mov     [ebp+var_4], eax
.text:004117E8                 lea     ecx, [ebp+person] ; this
.text:004117EB                 call    j_??0Person@@QAE@XZ ; Person::Person(void)
.text:004117F0                 xor     eax, eax
.text:004117F2                 push    edx
.text:004117F3                 mov     ecx, ebp        ; frame
.text:004117F5                 push    eax
.text:004117F6                 lea     edx, v          ; v
.text:004117FC                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:00411801                 pop     eax
.text:00411802                 pop     edx
.text:00411803                 pop     edi
.text:00411804                 pop     esi
.text:00411805                 pop     ebx
.text:00411806                 mov     ecx, [ebp+var_4]
.text:00411809                 xor     ecx, ebp        ; cookie
.text:0041180B                 call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:00411810                 add     esp, 0D4h
.text:00411816                 cmp     ebp, esp
.text:00411818                 call    j___RTC_CheckEsp
.text:0041181D                 mov     esp, ebp
.text:0041181F                 pop     ebp
.text:00411820                 retn
.text:00411820 ; ---------------------------------------------------------------------------
.text:00411821                 align 4
.text:00411824 ; _RTC_framedesc v
.text:00411824 v               _RTC_framedesc <1, offset dword_41182C>
.text:00411824                                         ; DATA XREF: _main+36↑o
.text:0041182C dword_41182C    dd 0FFFFFFF0h, 8        ; DATA XREF: _main:v↑o
.text:00411834                 dd offset aPerson       ; "person"
.text:00411838 aPerson         db 'person',0           ; DATA XREF: _main+74↑o
.text:00411838 _main           endp

11.1.2 调用自身类的虚函数

直接通过对象调用自身的成员虚函数,编译器使用了直接调用函数方式,没有访问虚表指针,而是间接获取虚函数地址。

debug

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

class Person {
public:
	virtual int getAge() { //虚函数定义
		return age;
	}
	virtual void setAge(int age) { //虚函数定义
		this->age = age;
	}
private:
	int age;
};

int main(int argc, char* argv[]) {
	Person person;
	person.setAge(20);
	printf("%d\n", person.getAge());

	return 0;
}

ida

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
__int64 __cdecl main()
{
  int v0; // eax
  int v1; // edx
  __int64 v2; // ST00_8
  Person person; // [esp+D0h] [ebp-10h]

  Person::Person(&person);
  Person::setAge(&person, 20);
  v0 = Person::getAge(&person);
  j__printf("%d\n", v0);
  HIDWORD(v2) = v1;
  LODWORD(v2) = 0;
  return v2;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
.text:004141F0 ; int __cdecl main(int argc, char **argv)
.text:004141F0 _main           proc near               ; CODE XREF: _main_0↑j
.text:004141F0
.text:004141F0 var_D4          = byte ptr -0D4h
.text:004141F0 person          = Person ptr -10h
.text:004141F0 var_4           = dword ptr -4
.text:004141F0 argc            = dword ptr  8
.text:004141F0 argv            = dword ptr  0Ch
.text:004141F0
.text:004141F0                 push    ebp
.text:004141F1                 mov     ebp, esp
.text:004141F3                 sub     esp, 0D4h
.text:004141F9                 push    ebx
.text:004141FA                 push    esi
.text:004141FB                 push    edi
.text:004141FC                 lea     edi, [ebp+var_D4]
.text:00414202                 mov     ecx, 35h
.text:00414207                 mov     eax, 0CCCCCCCCh
.text:0041420C                 rep stosd
.text:0041420E                 mov     eax, ___security_cookie
.text:00414213                 xor     eax, ebp
.text:00414215                 mov     [ebp+var_4], eax
.text:00414218                 lea     ecx, [ebp+person] ; this
.text:0041421B                 call    j_??0Person@@QAE@XZ ; Person::Person(void)
.text:00414220                 push    14h             ; age
.text:00414222                 lea     ecx, [ebp+person] ; this
.text:00414225                 call    j_?setAge@Person@@UAEXH@Z ; Person::setAge(int)
.text:0041422A                 lea     ecx, [ebp+person] ; this
.text:0041422D                 call    j_?getAge@Person@@UAEHXZ ; Person::getAge(void)
.text:00414232                 push    eax
.text:00414233                 push    offset aD       ; "%d\n"
.text:00414238                 call    j__printf
.text:0041423D                 add     esp, 8
.text:00414240                 xor     eax, eax
.text:00414242                 push    edx
.text:00414243                 mov     ecx, ebp        ; frame
.text:00414245                 push    eax
.text:00414246                 lea     edx, v          ; v
.text:0041424C                 call    j_@_RTC_CheckStackVars@8 ; _RTC_CheckStackVars(x,x)
.text:00414251                 pop     eax
.text:00414252                 pop     edx
.text:00414253                 pop     edi
.text:00414254                 pop     esi
.text:00414255                 pop     ebx
.text:00414256                 mov     ecx, [ebp+var_4]
.text:00414259                 xor     ecx, ebp        ; cookie
.text:0041425B                 call    j_@__security_check_cookie@4 ; __security_check_cookie(x)
.text:00414260                 add     esp, 0D4h
.text:00414266                 cmp     ebp, esp
.text:00414268                 call    j___RTC_CheckEsp
.text:0041426D                 mov     esp, ebp
.text:0041426F                 pop     ebp
.text:00414270                 retn
.text:00414270 ; ---------------------------------------------------------------------------
.text:00414271                 align 4
.text:00414274 ; _RTC_framedesc v
.text:00414274 v               _RTC_framedesc <1, offset dword_41427C>
.text:00414274                                         ; DATA XREF: _main+56↑o
.text:0041427C dword_41427C    dd 0FFFFFFF0h, 8        ; DATA XREF: _main:v↑o
.text:00414284                 dd offset aPerson       ; "person"
.text:00414288 aPerson         db 'person',0           ; DATA XREF: _main+94↑o
.text:00414288 _main           endp

11.1.3 析构函数分析

执行析构函数时,实际上是在还原虚表指针,让其指向自身的虚表首地址,防止在析构函数中调用虚函数时取到非自身虚表,从而导致函数调用错误。

debug

 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
#include <stdio.h>

class Person {
public:
	~Person() {
		printf("~Person()\n");
	}
public:
	virtual int getAge() { //虚函数定义
		return age;
	}
	virtual void setAge(int age) { //虚函数定义
		this->age = age;
	}
private:
	int age;
};

int main(int argc, char* argv[]) {
	Person person;
	person.setAge(20);
	printf("%d\n", person.getAge());

	return 0;
}

ida

 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
void __thiscall Person::~Person(Person *this)
{
  Person *v1; // STD4_4

  v1 = this;
  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  v1->vfptr = (PersonVtbl *)Person::`vftable';
  j__printf("~Person()\n");
}

__int64 __cdecl main()
{
  int v0; // eax
  int v1; // edx
  __int64 v2; // ST00_8
  Person person; // [esp+E0h] [ebp-1Ch]
  int v5; // [esp+F8h] [ebp-4h]

  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  Person::Person(&person);
  v5 = 0;
  Person::setAge(&person, 20);
  v0 = Person::getAge(&person);
  j__printf("%d\n", v0);
  v5 = -1;
  Person::~Person(&person);
  HIDWORD(v2) = v1;
  LODWORD(v2) = 0;
  return v2;
}
 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
36
37
38
39
40
.text:00411800 ; void __thiscall Person::~Person(Person *this)
.text:00411800 ??1Person@@QAE@XZ proc near             ; CODE XREF: Person::~Person(void)↑j
.text:00411800
.text:00411800 var_C           = byte ptr -0Ch
.text:00411800 this            = dword ptr -8
.text:00411800
.text:00411800                 push    ebp
.text:00411801                 mov     ebp, esp
.text:00411803                 sub     esp, 0CCh
.text:00411809                 push    ebx
.text:0041180A                 push    esi
.text:0041180B                 push    edi
.text:0041180C                 push    ecx
.text:0041180D
.text:0041180D __$EncStackInitStart:
.text:0041180D                 lea     edi, [ebp+var_C]
.text:00411810                 mov     ecx, 3
.text:00411815                 mov     eax, 0CCCCCCCCh
.text:0041181A                 rep stosd
.text:0041181C
.text:0041181C __$EncStackInitEnd:
.text:0041181C                 pop     ecx
.text:0041181D                 mov     [ebp+this], ecx ; 设置this指针
.text:00411820                 mov     ecx, offset _E4C34A80_main@cpp ; JMC_flag
.text:00411825                 call    j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
.text:0041182A                 mov     eax, [ebp+this]
.text:0041182D                 mov     dword ptr [eax], offset ??_7Person@@6B@ ; 设置虚表指针
.text:00411833                 push    offset _Format  ; "~Person()\n"
.text:00411838                 call    j__printf
.text:0041183D                 add     esp, 4
.text:00411840                 pop     edi
.text:00411841                 pop     esi
.text:00411842                 pop     ebx
.text:00411843                 add     esp, 0CCh
.text:00411849                 cmp     ebp, esp
.text:0041184B                 call    j___RTC_CheckEsp
.text:00411850                 mov     esp, ebp
.text:00411852                 pop     ebp
.text:00411853                 retn
.text:00411853 ??1Person@@QAE@XZ endp

11.2.虚函数的识别

判断是否为虚函数

  • 类中隐式定义了一个数据成员
  • 该数据成员在首地址处,占一个指针大小
  • 构造函数会将此数据成员初始化为某个数组的首地址
  • 这个地址属于数据区,是相当固定的地址
  • 在这个数组中,每个元素都是函数地址
  • 这些函数被调用时,第一个参数是this指针
  • 在这些函数内部,很有可能堆this指针使用间接的访问方式

12. 从内存角度看继承和多重继承

12.1 识别类与类之间的关系

debug

 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
36
37
38
#include <stdio.h>
#include <stdio.h>

class Base { //基类定义
public:
	Base() {
		printf("Base\n");
	}
	~Base() {
		printf("~Base\n");
	}
	void setNumber(int n) {
		base = n;
	}
	int getNumber() {
		return base;
	}
public:
	int base;
};

class Derive : public Base { //派生类定义
public:
	void showNumber(int n) {
		setNumber(n);
		derive = n + 1;
		printf("%d\n", getNumber());
		printf("%d\n", derive);
	}
public:
	int derive;
};

int main(int argc, char* argv[]) {
	Derive derive;
	derive.showNumber(argc);
	return 0;
}

ida

 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
36
37
38
39
40
41
42
void Base::Base()
{
  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  j__printf("Base\n");
}

void __thiscall Derive::Derive(Derive *this)
{
  Base::Base((Base *)&this->base);
}

void __thiscall Derive::showNumber(Derive *this, int n)
{
  Derive *v2; // STD8_4
  int v3; // eax

  v2 = this;
  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  Base::setNumber((Base *)&v2->base, n);
  v2->derive = n + 1;
  v3 = Base::getNumber((Base *)&v2->base);
  j__printf("%d\n", v3);
  j__printf("%d\n", v2->derive);
}

__int64 __cdecl main(int argc)
{
  int v1; // edx
  __int64 v2; // ST00_8
  Derive derive; // [esp+E0h] [ebp-1Ch]
  int v5; // [esp+F8h] [ebp-4h]

  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  Derive::Derive(&derive);
  v5 = 0;
  Derive::showNumber(&derive, argc);
  v5 = -1;
  Derive::~Derive(&derive);
  HIDWORD(v2) = v1;
  LODWORD(v2) = 0;
  return v2;
}

虚函数的调用过程是间接寻址方式:

debug

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <stdio.h>

class Person { // 基类——“人”类
public:
	Person() {}
	virtual ~Person() {}
	virtual void showSpeak() {} // 这里用纯虚函数更好,相关的知识点后面会讲到
};

class Chinese : public Person { // 中国人:继承自人类
public:
	Chinese() {}
	virtual ~Chinese() {}
	virtual void showSpeak() { // 覆盖基类虚函数
		printf("Speak Chinese\r\n");
	}
};

class American : public Person { //美国人:继承自人类
public:
	American() {}
	virtual ~American() {}
	virtual void showSpeak() { //覆盖基类虚函数
		printf("Speak American\r\n");
	}
};

class German : public Person { //德国人:继承自人类
public:
	German() {}
	virtual ~German() {}
	virtual void showSpeak() { //覆盖基类虚函数
		printf("Speak German\r\n");
	}
};

void speak(Person* person) { //根据虚表信息获取虚函数首地址并调用
	person->showSpeak();
}

int main(int argc, char* argv[]) {
	Chinese chinese;
	American american;
	German german;
	speak(&chinese);
	speak(&american);
	speak(&german);

	return 0;
}

ida

 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
void __cdecl speak(Person *person)
{
  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  person->vfptr->showSpeak(person);
}

__int64 __cdecl main()
{
  int v0; // edx
  __int64 v1; // ST00_8
  German german; // [esp+E0h] [ebp-30h]
  American american; // [esp+ECh] [ebp-24h]
  Chinese chinese; // [esp+F8h] [ebp-18h]
  int v6; // [esp+10Ch] [ebp-4h]

  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  Chinese::Chinese(&chinese);
  v6 = 0;
  American::American(&american);
  LOBYTE(v6) = 1;
  German::German(&german);
  LOBYTE(v6) = 2;
  speak((Person *)&chinese.vfptr);
  speak((Person *)&american.vfptr);
  speak((Person *)&german.vfptr);
  LOBYTE(v6) = 1;
  German::~German(&german);
  LOBYTE(v6) = 0;
  American::~American(&american);
  v6 = -1;
  Chinese::~Chinese(&chinese);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
 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
36
37
38
39
.text:00414470 ; void __cdecl speak(Person *person)
.text:00414470 ?speak@@YAXPAVPerson@@@Z proc near      ; CODE XREF: speak(Person *)↑j
.text:00414470
.text:00414470 person          = dword ptr  8
.text:00414470
.text:00414470                 push    ebp
.text:00414471                 mov     ebp, esp
.text:00414473                 sub     esp, 0C0h
.text:00414479                 push    ebx
.text:0041447A                 push    esi
.text:0041447B                 push    edi
.text:0041447C
.text:0041447C __$EncStackInitStart_14:
.text:0041447C                 mov     edi, ebp
.text:0041447E                 xor     ecx, ecx
.text:00414480                 mov     eax, 0CCCCCCCCh
.text:00414485                 rep stosd
.text:00414487
.text:00414487 __$EncStackInitEnd_14:                  ; JMC_flag
.text:00414487                 mov     ecx, offset _E4C34A80_main@cpp
.text:0041448C                 call    j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
.text:00414491                 mov     eax, [ebp+person]
.text:00414494                 mov     edx, [eax]      ; 虚表:vfptr
.text:00414496                 mov     esi, esp
.text:00414498                 mov     ecx, [ebp+person]
.text:0041449B                 mov     eax, [edx+4]    ; 调用showSpeak方法,因为showSpeak都放在它们各自虚表起始字节为5的地方,前4字节都放着自己的析构函数
.text:0041449E                 call    eax
.text:004144A0                 cmp     esi, esp
.text:004144A2                 call    j___RTC_CheckEsp
.text:004144A7                 pop     edi
.text:004144A8                 pop     esi
.text:004144A9                 pop     ebx
.text:004144AA                 add     esp, 0C0h
.text:004144B0                 cmp     ebp, esp
.text:004144B2                 call    j___RTC_CheckEsp
.text:004144B7                 mov     esp, ebp
.text:004144B9                 pop     ebp
.text:004144BA                 retn
.text:004144BA ?speak@@YAXPAVPerson@@@Z endp

12.2 多重继承

debug

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <stdio.h>

class Sofa {
public:
	Sofa() {
		color = 2;
	}
	virtual ~Sofa() { // 沙发类虚析构函数
		printf("virtual ~Sofa()\n");
	}
	virtual int getColor() { // 获取沙发颜色
		return color;
	}
	virtual int sitDown() { // 沙发可以坐下休息
		return printf("Sit down and rest your legs\r\n");
	}
protected:
	int color; // 沙发类成员变量
};

//定义床类
class Bed {
public:
	Bed() {
		length = 4;
		width = 5;
	}
	virtual ~Bed() { //床类虚析构函数
		printf("virtual ~Bed()\n");
	}
	virtual int getArea() { //获取床面积
		return length * width;
	}
	virtual int sleep() { //床可以用来睡觉
		return printf("go to sleep\r\n");
	}
protected:
	int length; //床类成员变量
	int width;
};

//子类沙发床定义,派生自Sofa类和Bed类
class SofaBed : public Sofa, public Bed {
public:
	SofaBed() {
		height = 6;
	}
	virtual ~SofaBed() { //沙发床类的虚析构函数
		printf("virtual ~SofaBed()\n");
	}
	virtual int sitDown() { //沙发可以坐下休息
		return printf("Sit down on the sofa bed\r\n");
	}
	virtual int sleep() { //床可以用来睡觉
		return printf("go to sleep on the sofa bed\r\n");
	}
	virtual int getHeight() {
		return height;
	}
protected:
	int height;
};

int main(int argc, char* argv[]) {
  // 局部变量,不会在调用析构方法后再调用delete方法
	SofaBed sofabed;

	return 0;
}

ida

 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
struct vfptr_SofaBed_1 {
  dword ptr => SofaBed::des_SofaBed(SofaBed *this, unsigned int a2)
  dword ptr => Sofa::getColor(void)
  dword ptr => SofaBed::sitDown(void)
  dword ptr => SofaBed::getHeight(void)
}

struct vfptr_SofaBed_1 {
  dword ptr => SofaBed::jump_des_SofaBed(SofaBed *this, unsigned int a2)
  dword ptr => Bed::getArea(void)
  dword ptr => SofaBed::sleep(void)
}

void __thiscall SofaBed::SofaBed(SofaBed *this)
{
  SofaBed *v1; // STD4_4

  v1 = this;
  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  Sofa::Sofa((Sofa *)&v1->vfptr);
  Bed::Bed((Bed *)&v1->vfptr);
  v1->vfptr = (SofaVtbl *)SofaBed::`vftable'{for `Sofa'};
  v1->vfptr = (BedVtbl *)SofaBed::`vftable'{for `Bed'};
  v1->height = 6;
}

void __thiscall SofaBed::~SofaBed(SofaBed *this)
{
  SofaBed *v1; // STD4_4

  v1 = this;
  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  v1->vfptr = (SofaVtbl *)vfptr_SofaBed_1;
  v1->vfptr = (BedVtbl *)vfptr_SofaBed_2;
  j__printf("virtual ~SofaBed()\n");
  Bed::~Bed((Bed *)&v1->vfptr);
  Sofa::~Sofa((Sofa *)&v1->vfptr);
}

//SofaBed *__thiscall SofaBed::`vector deleting destructor'(SofaBed *this, unsigned int a2)
SofaBed *__thiscall SofaBed::des_SofaBed(SofaBed *this, unsigned int a2)
{
  SofaBed *thisa; // [esp+D0h] [ebp-8h]

  thisa = this;
  SofaBed::~SofaBed(this);
  if ( a2 & 1 )
    delete(thisa, 0x18u);
  return thisa;
}

// this - 8
//SofaBed *__thiscall SofaBed::`vector deleting destructor'(char *this, unsigned int a2)
SofaBed *__thiscall SofaBed::jump_des_SofaBed(char *this, unsigned int a2)
{
  return des_SofaBed((SofaBed *)(this - 8), a2);
}

__int64 __cdecl main()
{
  int v0; // edx
  __int64 v1; // ST00_8
  SofaBed sofabed; // [esp+DCh] [ebp-20h]

  __CheckForDebuggerJustMyCode(&E4C34A80_main_cpp);
  SofaBed::SofaBed(&sofabed);
  SofaBed::~SofaBed(&sofabed);
  HIDWORD(v1) = v0;
  LODWORD(v1) = 0;
  return v1;
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
.text:00415690 ; void __thiscall SofaBed::SofaBed(SofaBed *this)
.text:00415690 ??0SofaBed@@QAE@XZ proc near            ; CODE XREF: SofaBed::SofaBed(void)↑j
.text:00415690
.text:00415690 var_18          = byte ptr -18h
.text:00415690 this            = dword ptr -14h
.text:00415690 var_C           = dword ptr -0Ch
.text:00415690 var_4           = dword ptr -4
.text:00415690
.text:00415690                 push    ebp
.text:00415691                 mov     ebp, esp
.text:00415693                 push    0FFFFFFFFh
.text:00415695                 push    offset __ehhandler$??0SofaBed@@QAE@XZ
.text:0041569A                 mov     eax, large fs:0
.text:004156A0                 push    eax
.text:004156A1                 sub     esp, 0CCh
.text:004156A7                 push    ebx
.text:004156A8                 push    esi
.text:004156A9                 push    edi
.text:004156AA                 push    ecx
.text:004156AB
.text:004156AB __$EncStackInitStart_16:
.text:004156AB                 lea     edi, [ebp+var_18]
.text:004156AE                 mov     ecx, 3
.text:004156B3                 mov     eax, 0CCCCCCCCh
.text:004156B8                 rep stosd
.text:004156BA
.text:004156BA __$EncStackInitEnd_16:
.text:004156BA                 pop     ecx
.text:004156BB                 mov     eax, ___security_cookie
.text:004156C0                 xor     eax, ebp
.text:004156C2                 push    eax
.text:004156C3                 lea     eax, [ebp+var_C]
.text:004156C6                 mov     large fs:0, eax
.text:004156CC                 mov     [ebp+this], ecx ; 设置this指针
.text:004156CF                 mov     ecx, offset _E4C34A80_main@cpp ; JMC_flag
.text:004156D4                 call    j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
.text:004156D9                 mov     ecx, [ebp+this] ; this
.text:004156DC                 call    j_??0Sofa@@QAE@XZ ; Sofa::Sofa(void)
.text:004156E1                 mov     [ebp+var_4], 0
.text:004156E8                 mov     ecx, [ebp+this] ; 现在this对象前4个字节保存Sofa的虚表,再之后的4个字节保存Sofa的成员变量color
.text:004156EB                 add     ecx, 8          ; this
.text:004156EE                 call    j_??0Bed@@QAE@XZ ; Bed::Bed(void)
.text:004156F3                 mov     eax, [ebp+this] ; 现在this对象前4个字节保存Sofa的虚表,再之后的4个字节保存Sofa的成员变量color
.text:004156F3                                         ; 再4个字节Bed的虚表,之后就是Bed的两个成员变量:length、width
.text:004156F6                 mov     dword ptr [eax], offset vfptr_SofaBed_1 ; 将this对象前4个字节保存SofaBed_1的虚表
.text:004156FC                 mov     eax, [ebp+this]
.text:004156FF                 mov     dword ptr [eax+8], offset vfptr_SofaBed_2 ; 将this对象前8个字节之后的4字节空间保存vfptr_SofaBed_2的虚表
.text:00415706                 mov     eax, [ebp+this]
.text:00415709                 mov     dword ptr [eax+14h], 6 ; this->height = 6
.text:00415710                 mov     [ebp+var_4], 0FFFFFFFFh
.text:00415717                 mov     eax, [ebp+this]
.text:0041571A                 mov     ecx, [ebp+var_C]
.text:0041571D                 mov     large fs:0, ecx
.text:00415724                 pop     ecx
.text:00415725                 pop     edi
.text:00415726                 pop     esi
.text:00415727                 pop     ebx
.text:00415728                 add     esp, 0D8h
.text:0041572E                 cmp     ebp, esp
.text:00415730                 call    j___RTC_CheckEsp
.text:00415735                 mov     esp, ebp
.text:00415737                 pop     ebp
.text:00415738                 retn
.text:00415738 ??0SofaBed@@QAE@XZ endp

通过反汇编分析,sofabed对象前4个字节保存SofaBed_1的虚表,再之后的4个字节保存Sofa的成员变量color,再之后的4个字节保存vfptr_SofaBed_2的虚表,之后就是Bed的两个成员变量:length、width;最后4个字节保存SofaBed成员变量height,所以对象的大小为24字节。

单继承类和多重继承类特征总结

单继承

  • 在类对象占用的内存空间中,只保存一份虚表指针。
  • 因为只有一个虚表指针,所以只有一个虚表。
  • 虚表中各项保存了类中各虚函数的首地址。
  • 构造时先构造父类,再构造自身,并且只调用一次父类构造函数。
  • 析构时先析构自身,再析构父类,并且只调用一次父类析构函数

多重继承

  • 在类对象占用内存空间中,根据继承父类(有虚函数)个数保存对应的虚表指针。
  • 根据保存的虚表指针的个数,产生相应个数的虚表。
  • 转换父类指针时,需要调整到对象的首地址。
  • 构造时需要调用多个父类构造函数。
  • 构造时先构造继承列表中的第一个父类,然后依次调用到最后一个继承的父类构造函数。
  • 析构时先析构自身,然后以构造函数相反的顺序调用所有父类的析构函数。
  • 当对象作为成员时,整个类对象的内存结构和多重继承相似。当类中无虚函数时,整个类对象内存结构和多重继承完全一样,可酌情还原。当父类或成员对象存在虚函数时,通过观察虚表指针的位置和构造、析构函数中填写虚表指针的数目、顺序及目标地址,还原继承或成员关系。

12.3 抽象类

debug

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

class AbstractBase {
public:
	AbstractBase() {
		printf("AbstractBase()");
	}
	virtual void show() = 0; //定义纯虚函数
};

class VirtualChild : public AbstractBase { //定义继承抽象类的子类
public:
	virtual void show() { //实现纯虚函数
		printf("抽象类分析\n");
	}
};

int main(int argc, char* argv[]) {
	VirtualChild obj;
	obj.show();

	return 0;
}

ida

1
2
3
4
5
6
void __thiscall AbstractBase::AbstractBase(AbstractBase *this)
{
  __CheckForDebuggerJustMyCode(&070E5FD3_main_cpp);
  this->__vftable = (AbstractBase_vtbl *)AbstractBase::`vftable';
  j__printf("AbstractBase()");
}
 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
36
37
38
39
40
41
42
43
44
45
46
47
48
.rdata:00417B34 ??_7AbstractBase@@6B@ dd offset j___purecall
.rdata:00417B34                                         ; DATA XREF: AbstractBase::AbstractBase(void)+2D↑o
.rdata:00417B38                 db    0
.rdata:00417B39                 db    0
.rdata:00417B3A                 db    0
.rdata:00417B3B                 db    0

.text:004117D0 ; void __thiscall AbstractBase::AbstractBase(AbstractBase *this)
.text:004117D0 ??0AbstractBase@@QAE@XZ proc near       ; CODE XREF: AbstractBase::AbstractBase(void)↑j
.text:004117D0
.text:004117D0 var_C           = byte ptr -0Ch
.text:004117D0 this            = dword ptr -8
.text:004117D0
.text:004117D0                 push    ebp
.text:004117D1                 mov     ebp, esp
.text:004117D3                 sub     esp, 0CCh
.text:004117D9                 push    ebx
.text:004117DA                 push    esi
.text:004117DB                 push    edi
.text:004117DC                 push    ecx
.text:004117DD
.text:004117DD __$EncStackInitStart:
.text:004117DD                 lea     edi, [ebp+var_C]
.text:004117E0                 mov     ecx, 3
.text:004117E5                 mov     eax, 0CCCCCCCCh
.text:004117EA                 rep stosd
.text:004117EC
.text:004117EC __$EncStackInitEnd:
.text:004117EC                 pop     ecx
.text:004117ED                 mov     [ebp+this], ecx ; 设置this指针
.text:004117F0                 mov     ecx, offset _070E5FD3_main@cpp ; JMC_flag
.text:004117F5                 call    j_@__CheckForDebuggerJustMyCode@4 ; __CheckForDebuggerJustMyCode(x)
.text:004117FA                 mov     eax, [ebp+this]
.text:004117FD                 mov     dword ptr [eax], offset ??_7AbstractBase@@6B@ ; const AbstractBase::`vftable'
.text:00411803                 push    offset _Format  ; "AbstractBase()"
.text:00411808                 call    j__printf
.text:0041180D                 add     esp, 4
.text:00411810                 mov     eax, [ebp+this]
.text:00411813                 pop     edi
.text:00411814                 pop     esi
.text:00411815                 pop     ebx
.text:00411816                 add     esp, 0CCh
.text:0041181C                 cmp     ebp, esp
.text:0041181E                 call    j___RTC_CheckEsp
.text:00411823                 mov     esp, ebp
.text:00411825                 pop     ebp
.text:00411826                 retn
.text:00411826 ??0AbstractBase@@QAE@XZ endp

在抽象类的虚表信息中,因为纯虚函数没有实现代码,所以没有首地址,编译器为了防止误调用虚函数,将虚表中保存的纯虚函数的首地址项替换成函数__purecall,用于结束程序。在分析过程中,一旦在虚表中发现函数地址为__purecall函数时,就可以高度怀疑此虚表对应的类是一个抽象类。

0%