Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: [C++] VTABLE

DrDeath [07.07.2008 00:44:47]

#

Miten virtuaali funktio taulukon voi löytää disasm-ohjelmalla?

Metabolix [07.07.2008 19:42:15]

#

Tuo lienee kääntäjäsidonnaista. Sijainnillahan ei sinänsä ole minkäänlaisia vaatimuksia. Seuraa empiirinen tutkimus, jossa on siis yksinkertainen C++-ohjelma ja sitä vastaava GCC:n tuottama x86-assembly (g++-4.2 koe.cpp -Os -m32 -fno-rtti -fno-exceptions -S).

Kirjoitan havainnotkin jo tähän valmiiksi, jottei tarvitse alempaa selata: Luontifunktiosta nähdään, että muuttujattomalle luokalle varataan osoittimen verran tilaa. Tähän tilaan tallennetaan osoitin funktiotauluun. Funktiotaulu taas sisältää osoittimet virtuaalifunktioihin. En asiaa erikseen kokeillut, mutta järki sanoo, että taulun alussa olevat kaksi kohtaa viittaisivat normaalisti luonti- ja tuhoamisfunktioihin.

Luultavasti siis paras johtolanka disassemblerin kanssa on juuri tämä luominen. Etsi luontifunktio eli new-kutsu ja katso, mikä osoite varatun muistin alkuun sijoitetaan.

// Abstrakti luokka
class PARENT {
public:
	virtual int FUNK_A(int PARAMETRI) = 0;
	virtual int FUNK_B(int PARAMETRI) = 0;
};

// Luokan toteutus
class CHILD : public PARENT {
public:
	virtual int FUNK_A(int PARAMETRI) {return PARAMETRI;}
	virtual int FUNK_B(int PARAMETRI) {return PARAMETRI + 1;}
};

// Funktio, josta nähdään, kuinka objekti luodaan
PARENT *PALAUTUS() {
	return new CHILD();
}
	.file	"koe.cpp"

;;; Perityn luokan funktio FUNK_A
	.section	.text._ZN5CHILD6FUNK_AEi,"axG",@progbits,_ZN5CHILD6FUNK_AEi,comdat
	.align 2
	.weak	_ZN5CHILD6FUNK_AEi
	.type	_ZN5CHILD6FUNK_AEi, @function
_ZN5CHILD6FUNK_AEi:
	pushl	%ebp
	movl	%esp, %ebp
	movl	12(%ebp), %eax
	popl	%ebp
	ret
	.size	_ZN5CHILD6FUNK_AEi, .-_ZN5CHILD6FUNK_AEi

;;; Perityn luokan funktio FUNK_B
	.section	.text._ZN5CHILD6FUNK_BEi,"axG",@progbits,_ZN5CHILD6FUNK_BEi,comdat
	.align 2
	.weak	_ZN5CHILD6FUNK_BEi
	.type	_ZN5CHILD6FUNK_BEi, @function
_ZN5CHILD6FUNK_BEi:
	pushl	%ebp
	movl	%esp, %ebp
	movl	12(%ebp), %eax
	popl	%ebp
	incl	%eax
	ret
	.size	_ZN5CHILD6FUNK_BEi, .-_ZN5CHILD6FUNK_BEi

;;; Luontifunktio
	.text
	.align 2
.globl _Z8PALAUTUSv
	.type	_Z8PALAUTUSv, @function
_Z8PALAUTUSv:
	pushl	%ebp
	movl	%esp, %ebp
	subl	$20, %esp
	pushl	$4
	call	_Znwj
	movl	$_ZTV5CHILD+8, (%eax)
	leave
	ret
	.size	_Z8PALAUTUSv, .-_Z8PALAUTUSv

;;; Perityn luokan funktiotaulu
	.weak	_ZTV5CHILD
	.section	.rodata._ZTV5CHILD,"aG",@progbits,_ZTV5CHILD,comdat
	.align 8
	.type	_ZTV5CHILD, @object
	.size	_ZTV5CHILD, 16
_ZTV5CHILD:
	.long	0
	.long	0
	.long	_ZN5CHILD6FUNK_AEi
	.long	_ZN5CHILD6FUNK_BEi

;;; Pääluokan funktiotaulu
	.weak	_ZTV6PARENT
	.section	.rodata._ZTV6PARENT,"aG",@progbits,_ZTV6PARENT,comdat
	.align 8
	.type	_ZTV6PARENT, @object
	.size	_ZTV6PARENT, 16
_ZTV6PARENT:
	.long	0
	.long	0
	.long	__cxa_pure_virtual
	.long	__cxa_pure_virtual
	.ident	"GCC: (GNU) 4.2.3 (Ubuntu 4.2.3-2ubuntu7)"
	.section	.note.GNU-stack,"",@progbits

DrDeath [11.07.2008 14:30:18]

#

Kiitos vastauksesta!
Kokeilin tehdä oman 'empiirisen' tutkimuksen tuolla tavalla ja tässä on hahmottamani rakenne:

[this]
 | *points to*
 v
[ADDRESS OF VTABLE]
variable_1    |
variable_2    |
variable_3    |
              |
       .------*
       |
       V
[ADDRESS OF FUNC_1]
[ADDRESS OF FUNC_2]
[ADDRESS OF FUNC_3]

Kääntäjä on vc++:n kääntäjä. Pääsinköhän nyt oikeille raiteille?

Metabolix [11.07.2008 14:50:35]

#

Jokseenkin noin. Varmista toki asia tulostelemalla oletettuja osoittimia ja vertaamalla niitä funktioiden oikeisiin osoitteisiin.

Itse ilmaisisin kaavion ennemmin suoraan koodina, jolla dataakin voi tarkastella. Jotenkin näin siis:

typedef int function(int p1, ...);
struct functions {
  function *f_1;
  function *f_2;
  function *f_n;
};
struct virtual_class {
  functions *vtable;
  int member_1, member_2, member_n;
};
virtual_class *vclass = (virtual_class *) this;

// Tulostetaan osoitteita
printf("vtable @ %p\n", vclass->vtable);
printf("f_1 @ %p\n", vclass->f_1);

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta