Hello,

C'est un sujet qui touche à la fois au C++ et à l'assembleur, mais je pense qu'il à sa place ici. Si ce n'est pas le cas, n'hésitez pas à le déplacer dans la catégorie asm.

Je fais un peu de reversing actuellement (et je galère comme pas possible ),
et je suis tombé sur des fonctions un peu surprenante en assembleur.

Je bosse sur un exe écrit à la base en C++ et compilé avec VS2010 (en release).

Il y a des fonctions vides type
Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
sub_vide proc near
   retn
sub_vide endp
Et ces fonctions vides sont appelées directement dans le code (sans passer par une vtable)
Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
sub_xxx proc near
   // ...
   call sub_vide
   // ...
proc_xxx endp
Ainsi que des fonctions non inlinées mais qui devraient l'être
Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
sub_xxx proc near
   call sub_yyy
   retn
sub_xxx endp

J'ai essayé de faire un code minimal (en C++) reproduisant ça, mais je n'y arrive pas
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
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
struct Base {
	virtual ~Base() { }
 
	virtual void foo() { }
};
 
struct Derivee: Base {
	virtual void foo() { std::cout << "should be easy to find me in assembly" << std::endl; }
};
 
 
void bar() {
	std::cout << "before" << std::endl;
	Base b;
	b.foo();
	std::cout << "after" << std::endl;
}
 
int main() {
 
	bar();
 
	Base *b = new Base;
	b->foo();
 
	Base *d = new Derivee;
	d->foo();
 
	return 0;
}
Mais aucun appel direct à Base::foo n'est présent dans l'exe généré, la fonction Base::foo est bien vide, mais seule la vtable de Base y fait référence (.rdata:00403214o)
Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
.text:00401280 ; void __thiscall Base__foo(Base *this)
.text:00401280 ?foo@Base@@UAEXXZ proc near             ; DATA XREF: .rdata:00403214o
.text:00401280 this = ecx
.text:00401280                 retn
.text:00401280 ?foo@Base@@UAEXXZ endp
Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
.rdata:00403210 ; const Base::`vftable'
.rdata:00403210 ??_7Base@@6B@   dd offset ??_EDerivee@@UAEPAXI@Z
.rdata:00403210                                         ; DATA XREF: Base::~Base(void)o
.rdata:00403210                                         ; Derivee::`vector deleting destructor'(uint)+Ao ...
.rdata:00403210                                         ; Derivee::`vector deleting destructor'(uint)
.rdata:00403214                 dd offset ?foo@Base@@UAEXXZ ; Base::foo(void)
.rdata:00403218                 dd offset ??_R4_Generic_error_category@std@@6B@ ; const std::_Generic_error_category::`RTTI Complete Object Locator'
Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
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
.text:004012E0 ; =============== S U B R O U T I N E =======================================
.text:004012E0
.text:004012E0 ; Attributes: bp-based frame
.text:004012E0
.text:004012E0 ; int __cdecl main()
.text:004012E0 _main           proc near               ; CODE XREF: __tmainCRTStartup+F8p
.text:004012E0
.text:004012E0 var_14          = dword ptr -14h
.text:004012E0 var_C           = dword ptr -0Ch
.text:004012E0 var_4           = dword ptr -4
.text:004012E0
.text:004012E0                 push    ebp
.text:004012E1                 mov     ebp, esp
.text:004012E3                 and     esp, 0FFFFFFF8h
.text:004012E6                 push    0FFFFFFFFh
.text:004012E8                 push    offset __ehhandler$_main
.text:004012ED                 mov     eax, large fs:0
.text:004012F3                 push    eax
.text:004012F4                 sub     esp, 0Ch
.text:004012F7                 mov     eax, ___security_cookie
.text:004012FC                 xor     eax, esp
.text:004012FE                 push    eax
.text:004012FF                 lea     eax, [esp+1Ch+var_C]
.text:00401303                 mov     large fs:0, eax
.text:00401309                 push    ds:__imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z ; std::endl(std::basic_ostream<char,std::char_traits<char>> &)
.text:0040130F                 mov     ecx, ds:__imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ; _Ostr
.text:00401315                 mov     edx, offset aBefore ; "before"
.text:0040131A                 call    ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char const *)
.text:0040131F                 mov     ecx, eax
.text:00401321                 call    ds:__imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z ; std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
.text:00401327                 mov     [esp+1Ch+var_14], offset ??_7Base@@6B@ ; const Base::`vftable'
.text:0040132F                 mov     [esp+1Ch+var_4], 0
.text:00401337                 push    ds:__imp_?endl@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@1@AAV21@@Z ; std::endl(std::basic_ostream<char,std::char_traits<char>> &)
.text:0040133D                 mov     ecx, ds:__imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A ; _Ostr
.text:00401343                 mov     edx, offset aAfter ; "after"
.text:00401348                 call    ??$?6U?$char_traits@D@std@@@std@@YAAAV?$basic_ostream@DU?$char_traits@D@std@@@0@AAV10@PBD@Z ; std::operator<<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,char const *)
.text:0040134D                 mov     ecx, eax
.text:0040134F                 call    ds:__imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@P6AAAV01@AAV01@@Z@Z ; std::basic_ostream<char,std::char_traits<char>>::operator<<(std::basic_ostream<char,std::char_traits<char>> & (*)(std::basic_ostream<char,std::char_traits<char>> &))
.text:00401355                 push    4
.text:00401357                 mov     [esp+20h+var_4], 0FFFFFFFFh
.text:0040135F                 call    ds:__imp_??2@YAPAXI@Z ; operator new(uint)
.text:00401365                 mov     ecx, eax
.text:00401367                 add     esp, 4
.text:0040136A                 test    ecx, ecx
.text:0040136C                 jz      short loc_401376
.text:0040136E                 mov     dword ptr [ecx], offset ??_7Base@@6B@ ; const Base::`vftable'
.text:00401374                 jmp     short loc_401378
.text:00401376 ; ---------------------------------------------------------------------------
.text:00401376
.text:00401376 loc_401376:                             ; CODE XREF: _main+8Cj
.text:00401376                 xor     ecx, ecx
.text:00401378
.text:00401378 loc_401378:                             ; CODE XREF: _main+94j
.text:00401378                 mov     eax, [ecx]
.text:0040137A                 call    dword ptr [eax+4]
.text:0040137D                 push    4
.text:0040137F                 call    ds:__imp_??2@YAPAXI@Z ; operator new(uint)
.text:00401385                 mov     ecx, eax
.text:00401387                 add     esp, 4
.text:0040138A                 test    ecx, ecx
.text:0040138C                 jz      short loc_401396
.text:0040138E                 mov     dword ptr [ecx], offset ??_7Derivee@@6B@ ; const Derivee::`vftable'
.text:00401394                 jmp     short loc_401398
.text:00401396 ; ---------------------------------------------------------------------------
.text:00401396
.text:00401396 loc_401396:                             ; CODE XREF: _main+ACj
.text:00401396                 xor     ecx, ecx
.text:00401398
.text:00401398 loc_401398:                             ; CODE XREF: _main+B4j
.text:00401398                 mov     eax, [ecx]
.text:0040139A                 call    dword ptr [eax+4]
.text:0040139D                 xor     eax, eax
.text:0040139F                 mov     ecx, [esp+1Ch+var_C]
.text:004013A3                 mov     large fs:0, ecx
.text:004013AA                 pop     ecx
.text:004013AB                 mov     esp, ebp
.text:004013AD                 pop     ebp
.text:004013AE                 retn
.text:004013AE _main           endp
Ici bar est inline, l'objet temporaire Base b est déclaré sur la pile
Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
.text:00401327                 mov     [esp+1Ch+var_14], offset ??_7Base@@6B@ ; const Base::`vftable'
mais aucune fonction n'est appelé (ctor par defaut, et Base::foo, supprimés par optimisation).

Les appels b->foo(); et d->foo(); sont identiques et passent par la vtable correspondante (ce qui est normal)
Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
.text:0040136E                 mov     dword ptr [ecx], offset ??_7Base@@6B@ ; const Base::`vftable' -> fin de Base *b = new Base; : on récupère l'adresse de la vtable de Base
.text:00401374                 jmp     short loc_401378
; ..
.text:00401378 loc_401378:                             ; CODE XREF: _main+94j
.text:00401378                 mov     eax, [ecx]
.text:0040137A                 call    dword ptr [eax+4] ; b->foo();
; ...
.text:0040138E                 mov     dword ptr [ecx], offset ??_7Derivee@@6B@ ; const Derivee::`vftable'
.text:00401394                 jmp     short loc_401398
; ...
.text:00401398 loc_401398:                             ; CODE XREF: _main+B4j
.text:00401398                 mov     eax, [ecx]
.text:0040139A                 call    dword ptr [eax+4] ; d->foo();
Bref, comment est-ce possible que des fonctions vides passent la phase d'optimisation et se retrouvent appelées directement ?
Même question pour les fonctions qui devraient être inline ?