0%

C++反汇编与逆向分析 - 结构体

C语言中的结构体是一种复合数据结构,可以将不同数据类型的变量进行封装

运行环境:

  • 操作系统: Windows 7家庭版
  • 编译器:VC6 VS2013

结构体

初始化

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
19:   int main(int argc, char* argv[])
20: {
00401010 push ebp
00401011 mov ebp,esp
00401013 sub esp,6Ch
00401016 push ebx
00401017 push esi
00401018 push edi
00401019 lea edi,[ebp-6Ch]
0040101C mov ecx,1Bh
00401021 mov eax,0CCCCCCCCh
00401026 rep stos dword ptr [edi]
21: tagPerson p = {
22: "jack", //+0
00401028 mov eax,[string "jack" (00426024)] ;内存地址赋值给eax寄存器
0040102D mov dword ptr [ebp-28h],eax
00401030 mov cl,byte ptr [string "jack"+4 (00426028)] ;补个\0,对齐
00401036 mov byte ptr [ebp-24h],cl
23: 15, //+8
00401039 mov dword ptr [ebp-20h],0Fh ;赋值
24: 250.0, //+16
00401040 mov dword ptr [ebp-18h],0
00401047 mov dword ptr [ebp-14h],406F4000h ;double类型占8字节,所以需要两次赋值
25: 172, //+24
0040104E mov word ptr [ebp-10h],offset main+42h (00401052)
26: 3.14, //+28
00401054 mov dword ptr [ebp-0Ch],4048F5C3h
27: 'M' //+32
28: };
0040105B mov byte ptr [ebp-8],4Dh
29: struct tagPerson *pPer = &p;
0040105F lea edx,[ebp-28h]
00401062 mov dword ptr [ebp-2Ch],edx ;将p的地址赋值给指针变量pPer
30: printf("%d\n",sizeof(p));
00401065 push 28h
00401067 push offset string "%d\n" (00426020)
0040106C call printf (004010e0)
00401071 add esp,8
31: printf("%d\n",p.wHeight); //+24
00401074 movsx eax,word ptr [ebp-10h]
00401078 push eax
00401079 push offset string "%d\n" (00426020)
0040107E call printf (004010e0)
00401083 add esp,8
32: printf("%f\n",pPer->f); //+28
00401086 mov ecx,dword ptr [ebp-2Ch]
00401089 fld dword ptr [ecx+1Ch]
0040108C sub esp,8
0040108F fstp qword ptr [esp]
00401092 push offset string "%f\n" (0042601c)
00401097 call printf (004010e0)
0040109C add esp,0Ch
33: return 0;
0040109F xor eax,eax
34: }

结构指针做函数参数

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
21:       printf("%d\n",tagPoint->nAge);
00401028 mov eax,dword ptr [ebp+8] ;找到参数将参数地址放入eax中
0040102B mov ecx,dword ptr [eax+8] ;在内存中加偏移
0040102E push ecx
0040102F push offset string "%d\n" (00426020)
00401034 call printf (004010e0)
00401039 add esp,8
22: printf("%d\n",tagPoint->wHeight); //+24
0040103C mov edx,dword ptr [ebp+8]
0040103F movsx eax,word ptr [edx+18h]
00401043 push eax
00401044 push offset string "%d\n" (00426020)
00401049 call printf (004010e0)
0040104E add esp,8
23: printf("%f\n",tagPoint->f); //+28
00401051 mov ecx,dword ptr [ebp+8]
00401054 fld dword ptr [ecx+1Ch] ;push浮点指令
00401057 sub esp,8
0040105A fstp qword ptr [esp] ;pop浮点指令
0040105D push offset string "%f\n" (0042601c)
00401062 call printf (004010e0)
00401067 add esp,0Ch


38: Show(&p);
00410A05 lea eax,[ebp-28h] ;结构体首地址
00410A08 push eax
00410A09 call @ILT+5(Show) (0040100a)
00410A0E add esp,4

对比数组与结构体指针做参数

如果是结构指针做参数,并且结构体中都为相同类型,那么和数组看不出任何差异;

;结构指针做参数

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



struct tagTest2
{
int m_1;
int m_2;
int m_3;
};

void Show2(struct tagTest2 *p)
{
printf("%d\n",p->m_1);
printf("%d\n",p->m_2);
printf("%d\n",p->m_3);
}

struct tagTest2 Test = {1,2,3};
Show2(&Test);

;函数调用反汇编代码略

.text:00410B08 mov eax, [ebp+arg_0] ; 寻找到参数,参数存入eax寄存器中
.text:00410B0B mov ecx, [eax] ; 在内存中加偏移
.text:00410B0D push ecx
.text:00410B0E push offset aD ; "%d\n"
.text:00410B13 call printf
.text:00410B18 add esp, 8
.text:00410B1B mov edx, [ebp+arg_0]
.text:00410B1E mov eax, [edx+4] ; 第二个结构体成员
.text:00410B21 push eax
.text:00410B22 push offset aD ; "%d\n"
.text:00410B27 call printf
.text:00410B2C add esp, 8
.text:00410B2F mov ecx, [ebp+arg_0]
.text:00410B32 mov edx, [ecx+8] ; 第三个结构体成员
.text:00410B35 push edx
.text:00410B36 push offset aD ; "%d\n"
.text:00410B3B call printf

数组做函数参数:

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
	

void Show3(int *ary)
{
printf("%d\n",ary[0]);
printf("%d\n",ary[1]);
printf("%d\n",ary[2]);
}
int Array[3] = {1,2,3};

.text:004010C8 mov eax, [ebp+arg_0]
.text:004010CB mov ecx, [eax]
.text:004010CD push ecx
.text:004010CE push offset aD ; "%d\n"
.text:004010D3 call printf
.text:004010D8 add esp, 8
.text:004010DB mov edx, [ebp+arg_0]
.text:004010DE mov eax, [edx+4]
.text:004010E1 push eax
.text:004010E2 push offset aD ; "%d\n"
.text:004010E7 call printf
.text:004010EC add esp, 8
.text:004010EF mov ecx, [ebp+arg_0]
.text:004010F2 mov edx, [ecx+8]
.text:004010F5 push edx
.text:004010F6 push offset aD ; "%d\n"
.text:004010FB call printf

如果细心一些,马么我们可以发现,结构体指针做参数和数组指针做参数是没有差异的,这样的条件十分苛刻;

结构体变量做函数参数

简单类型

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

struct tagTest2
{
int m_1;
int m_2;
};

void Show(struct tagTest2 Test)
{
printf("%d\n",Test.m_1);
printf("%d\n",Test.m_2);
}
int main(int argc, char* argv[])
{
struct tagTest2 Test = {1,2};
Show(Test);
printf("Hello World!\n");
return 0;
}

21: Show(Test);
004010C6 mov eax,dword ptr [ebp-4]
004010C9 push eax
004010CA mov ecx,dword ptr [ebp-8]
004010CD push ecx
004010CE call @ILT+5(Show) (0040100a)
004010D3 add esp,8



13: void Show(struct tagTest2 Test)
14: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
15: printf("%d\n",Test.m_1);
00401038 mov eax,dword ptr [ebp+8] ;处理为参数,用ebp+8来寻址
0040103B push eax
0040103C push offset string "%d\n" (0042201c)
00401041 call printf (00401130)
00401046 add esp,8
16: printf("%d\n",Test.m_2);
00401049 mov ecx,dword ptr [ebp+0Ch]
0040104C push ecx
0040104D push offset string "%d\n" (0042201c)
00401052 call printf (00401130)
00401057 add esp,8
17: }

当作简单的参数还原也可以;

复杂类型

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
48:       Show2(p);
00401198 add esp,0DCh ;给栈顶留空间,结构体做参数特殊处理
0040119B mov ecx,0Ah
004011A0 lea esi,[ebp-28h] ;结构体传参
004011A3 mov edi,esp
004011A5 rep movs dword ptr [edi],dword ptr [esi] ;memcpy
004011A7 call @ILT+0(Show2) (00401005)
004011AC add esp,28h


30: void Show2(struct tagPerson Test)
31: {
004010C0 push ebp
004010C1 mov ebp,esp
004010C3 sub esp,40h
004010C6 push ebx
004010C7 push esi
004010C8 push edi
004010C9 lea edi,[ebp-40h]
004010CC mov ecx,10h
004010D1 mov eax,0CCCCCCCCh
004010D6 rep stos dword ptr [edi]
32: printf("%d\n",Test.nAge);
004010D8 mov eax,dword ptr [ebp+10h]
004010DB push eax
004010DC push offset string "%d\n" (00426020)
004010E1 call printf (004011f0)
004010E6 add esp,8
33: printf("%f\n",Test.dwlWeight);
004010E9 mov ecx,dword ptr [ebp+1Ch]
004010EC push ecx
004010ED mov edx,dword ptr [ebp+18h]
004010F0 push edx
004010F1 push offset string "%f\n" (0042601c)
004010F6 call printf (004011f0)
004010FB add esp,0Ch
34: printf("%f\n",Test.f);
004010FE fld dword ptr [ebp+24h]
00401101 sub esp,8
00401104 fstp qword ptr [esp]
00401107 push offset string "%f\n" (0042601c)
0040110C call printf (004011f0)
00401111 add esp,0Ch
35: }

遇到这样的代码定式,一定有一个结构体:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
.text:004010CE                 sub     esp, 24h
.text:004010D1 mov ecx, 0Ah
.text:004010D6 lea esi, [esp+58h+var_28]
.text:004010DA mov edi, esp ; 栈顶有结构体
.text:004010DC rep movsd

;高版本编译器中是这样的:
.text:00401105 sub esp, 24h
.text:00401108 mov eax, esp
.text:0040110A movups xmmword ptr [eax], xmm0 ; eax相当于栈顶
.text:0040110D movups xmm0, [ebp+var_1C]
.text:00401111 movups xmmword ptr [eax+10h], xmm0
.text:00401115 movq xmm0, [ebp+var_C]
.text:0040111A movq qword ptr [eax+20h], xmm0

;高版本不适用mm0
.text:00401132 sub esp, 200h
.text:00401138 lea esi, [ebp+var_214]
.text:0040113E mov ecx, 84h
.text:00401143 mov edi, esp
.text:00401145 rep movsd

返回对象

返回对象或返回结构体变量时产生临时对象;

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
91:       struct tagTest2 Test = GetTest2();		//临时对象
0040133D call @ILT+10(GetTest2) (0040100f)
00401342 mov dword ptr [ebp-70h],eax
00401345 mov dword ptr [ebp-6Ch],edx //6c开始的两个DWORD就是临时对象
00401348 mov edx,dword ptr [ebp-70h]
0040134B mov dword ptr [ebp-40h],edx
0040134E mov eax,dword ptr [ebp-6Ch]
00401351 mov dword ptr [ebp-3Ch],eax



92: struct tagPerson Test1 = GetTest3();临时对象
00401354 lea ecx,[ebp-0C0h] ;
0040135A push ecx
0040135B call @ILT+5(GetTest3) (0040100a)
00401360 add esp,4
00401363 mov esi,eax
00401365 mov ecx,0Ah
0040136A lea edi,[ebp-98h]
00401370 rep movs dword ptr [edi],dword ptr [esi] ;返回值给临时对象
00401372 mov ecx,0Ah
00401377 lea esi,[ebp-98h]
0040137D lea edi,[ebp-68h]
00401380 rep movs dword ptr [edi],dword ptr [esi] ;返回给局部变量

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
__int64 GetInt64()
{
return 8765432187654321L;
}

float GetFolat()
{
return 3.14f;
}

double GetDouble()
{
return 3.14;
}


struct tagTest2 GetTest2()
{
struct tagTest2 Test= {1,2};
return Test;
}


struct tagPerson GetTest3()
{
struct tagPerson p = {
"jack", //+0
15, //+8
250.0, //+16
172, //+24
3.14, //+28
'M' //+32
};
return p;
}





int main(int argc, char* argv[])
{
struct tagPerson p = {
"jack", //+0
15, //+8
250.0, //+16
172, //+24
3.14, //+28
'M' //+32
};

int n = GetInt64();
float n1 = GetFolat();
double dbl = GetDouble();
struct tagTest2 Test = GetTest2();
struct tagPerson Test1 = GetTest3();
printf("Hello World!\n");
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
92:       struct tagPerson Test1 = GetTest3();
00401354 lea ecx,[ebp-0C0h]
0040135A push ecx ;复杂对象就是返回到内存中,使用参数返回到内存中
0040135B call @ILT+5(GetTest3) (0040100a)
00401360 add esp,4
00401363 mov esi,eax
00401365 mov ecx,0Ah
0040136A lea edi,[ebp-98h]
00401370 rep movs dword ptr [edi],dword ptr [esi]
00401372 mov ecx,0Ah
00401377 lea esi,[ebp-98h]
0040137D lea edi,[ebp-68h]
00401380 rep movs dword ptr [edi],dword ptr [esi]


;函数中返回
0040129F mov ecx,0Ah
004012A4 lea esi,[ebp-28h] ;参数给esi
004012A7 mov edi,dword ptr [ebp+8] ;
004012AA rep movs dword ptr [edi],dword ptr [esi] ;memcpy
004012AC mov eax,dword ptr [ebp+8] ;将结果返回到参数所指向的内存块中

memcpy(pRet,xxx,xxx)
return pRet