2009-10-09 11 views
11

Mi piacerebbe sapere come modificare l'indirizzo di Test che si trova nella tabella virtuale con quello di HackedVTable.Come hackerare il tavolo virtuale?

void HackedVtable() 
{ 
    cout << "Hacked V-Table" << endl; 
} 

class Base 
{  
public: 
    virtual Test() { cout <<"base"; } 
    virtual Test1() { cout << "Test 1"; } 
    void *prt; 
    Base(){} 
}; 

class Derived : public Base 
{ 
public: 
    Test() 
    { 
     cout <<"derived"; 
    } 
}; 

int main() 
{  
    Base b1; 

    b1.Test(); // how to change this so that `HackedVtable` should be called instead of `Test`? 

    return 0; 
} 

La risposta sarà molto apprezzata.

Grazie in anticipo.

+5

Non mi interessa perché vuoi farlo. È una cattiva idea. Non farlo – abelenky

+5

@abelenky, questo è solo per scopi educativi. volevo sapere come funzionano le cose. :) – mahesh

+0

Il commento di @ Martin è il miglior consiglio che puoi ottenere a scopo didattico. altrimenti - basta rinunciare, il compilatore è meglio per questo lavoro. – LiraNuna

risposta

15

Questo funziona per build MSVC a 32 bit (è una versione molto semplificata di un codice di produzione in uso da più di un anno). Si noti che il metodo di sostituzione deve specificare esplicitamente il parametro this (puntatore).

// you can get the VTable location either by dereferencing the 
// first pointer in the object or by analyzing the compiled binary. 
unsigned long VTableLocation = 0U; 
// then you have to figure out which slot the function is in. this is easy 
// since they're in the same order as they are declared in the class definition. 
// just make sure to update the index if 1) the function declarations are 
// re-ordered and/or 2) virtual methods are added/removed from any base type. 
unsigned VTableOffset = 0U; 
typedef void (__thiscall Base::*FunctionType)(const Base*); 
FunctionType* vtable = reinterpret_cast<FunctionType*>(VTableLocation); 

bool hooked = false; 
HANDLE process = ::GetCurrentProcess(); 
DWORD protection = PAGE_READWRITE; 
DWORD oldProtection; 
if (::VirtualProtectEx(process, &vtable[VTableOffset], sizeof(int), protection, &oldProtection)) 
{ 
    vtable[VTableOffset] = static_cast<FunctionType>(&ReplacementMethod); 

    if (::VirtualProtectEx(process, &vtable[VTableOffset], sizeof(int), oldProtection, &oldProtection)) 
     hooked = true; 
} 
+0

Grazie mille. :) – mahesh

+1

NP. Ho dovuto usarlo per correggere una convalida dei parametri errata che si presentava sulla configurazione della mia macchina in un'applicazione di terze parti che causava un crash ogni volta che provavo ad usare il programma. –

8

La V-Table è un dettaglio di implementazione.

Il compilatore non è tenuto a utilizzarne uno (è solo il modo più semplice per implementare le funzioni virtuali). Ma dire che ogni compilatore può (e lo fa) implementarlo in modo leggermente diverso, di conseguenza non c'è una risposta alla tua domanda.

Se si chiede come faccio a incidere un vtable per un programma costruito con:

Compiler < X> Versione < Y> Crea < Z>

Poi qualcuno potrebbe conoscere la risposta.

+3

Ben detto. Inoltre, anche i compilatori che usano una V-Table a volte producono codice con collegamenti statici se la semantica del programma lo consente. – mjv

2

Non penso che ci sia un modo portatile. Principalmente a causa dell'ottimizzazione del compilatore e dell'architettura ABI diversa tra tutti i target.

Ma C++ fornisce esattamente la stessa funzionalità, perché non utilizzarlo?

void HackedVtable() 
{ 
    cout << "Hacked V-Table" << endl; 
} 

class Base 
{ 
public: 
     virtual Test() { cout <<"base"; } 
     virtual Test1() { cout << "Test 1"; } 
     void *prt; 
     Base(){} 
}; 

class Derived : public Base 
{ 
    public: 
      Test() 
      { 
       HackedVtable(); // <-- NOTE 
      } 
}; 

int main() 
{ 
    Derived b1; // <-- NOTE 

    b1.Test(); 

    return 0; 
} 
+0

Volevo sapere di cambiare l'indirizzo in vtable – mahesh

+2

Non si vuole sapere. Anche per scopi didattici, la teoria è * bene * in questo caso. Il compilatore conosce molto più del tuo programma di quanto tu possa pensare, quindi fare casino con una tabella che può o non può esistere (in questo caso, mai, poiché non è in uso) è una cattiva idea e ti farà solo pensare sei un programmatore brutto segmentando troppo. Non farlo. – LiraNuna

0

bene è abbastanza facile da capire. Trova il puntatore VTAble (In Visual Studio è il primo 4/8 byte). Quindi entra in una normale chiamata di Test (nell'assemblatore) e vedrai che salta al Vtable e poi alla tua funzione di test. Per sostituire il test basta sostituire il puntatore nel punto in cui è saltato da VTable.

+0

Significa che MSVC non ottimizza gli overrides evidenti (se non utilizzati, ad esempio)? – LiraNuna

7
void HackedVtable() 
{ 
    cout << "Hacked V-Table" << endl; 
} 

class Base 
{ 

public: 
     virtual Test() { cout <<"base"; } 
     virtual Test1() { cout << "Test 1"; } 
     void *prt; 
     Base(){} 
}; 

class Derived:public Base 
{ 
    public: 
      Test() 
      { 
        cout <<"derived"; 
      } 
}; 

typedef void (*FUNPTR)(); 
typedef struct 
{ 
    FUNPTR funptr; 
} VTable; 


int main() 
{ 

    Base b1; 
    Base *b1ptr = &b; 

    VTable vtable; 
    vtable.funptr = HackedVtable; 

    VTable *vptr = &vtable; 
    memcpy (&b1, &vptr, sizeof(long)); 

    b1ptr->Test(); 

    //b1.Test(); // how to change this so that HackedVtable() should be called instead of Test() 

    return 0; 
} 
+0

@ Ganesh, Grazie mille..semplice ed elegante. :) – mahesh

+1

@mahesh, questa è * completamente * diversa funzionalità risultante della mia. Qui, sostituisce le funzioni per una singola istanza (b1). Nella mia, sostituisce le funzioni per * tutte * le istanze di un tipo specifico. –

1

Un altro modo per ottenere la stessa cosa è di cheking codice simile:

GObject:

http://en.wikipedia.org/wiki/Gobject

GLib:

http://en.wikipedia.org/wiki/GLib

Vala:

http://en.wikipedia.org/wiki/Vala_%28programming_language%29

Quei ragazzi voleva lavorare con un oggetto e la classe linguaggio di programmazione orientato, ma "C++", non si adattano alle loro requisiti. Quindi, hanno preso "plain C" e simulano oggetti con record &, incluse le tabelle dei metodi virtuali. Alla fine ha ottenuto un linguaggio simile chiamato "Vala", il proprio linguaggio "C++" allo stesso modo (con il proprio V.M.T.).

Un altro link correlati:

http://en.wikipedia.org/wiki/Virtual_method_table

http://www.artima.com/insidejvm/ed2/jvmP.html

applausi.

1

Sotto Mac OS X 10.10.3 + gcc 4.8.3, il codice seguente funziona correttamente.

void HackedVtable() 
{ 
    cout << "Hacked V-Table" << endl; 
} 

class Base 
{  
public: 
    virtual void Test() { cout << "base" << endl; } 
    virtual void Test1() { cout << "Test 1" << endl; } 
    void *prt; 
    Base(){} 
}; 

class Derived : public Base 
{ 
public: 
    void Test() 
    { 
     cout << "derived" << endl; 
    } 
}; 

int main() 
{  
    Base b1; 
    Base* pb1 = &b1; 

    *(*(void***)pb1) = (void*) HackedVtable; 
    pb1->Test(); 

    //It works for all the Base instance 
    Base b2; 
    Base* pb2 = &b2; 
    pb2->Test(); 

    //But Derived's virtual function table is separated from Base's 
    Derived d1; 
    Derived* pd1 = &d1; 
    pd1->Test(); 
    *(*(void***)pd1) = (void*) HackedVtable; 
    pd1->Test(); 

    return 0; 
} 

sua uscita:

$ g++ h.cpp; ./a.out 
Hacked V-Table 
Hacked V-Table 
derived 
Hacked V-Table 

I testare lo stesso codice sotto Ubuntu 12.04 + g ++ 4.9.0. Tuttavia, non funziona e si verifica un errore di segmentazione. Sembra che Linux assegni la tabella delle funzioni virtuali in un'area di sola lettura (ad es. Rodata) per proibire l'hacking.

-1

Non penso che il vTable sia in sola lettura perché è popolato dinamicamente. L'unico modo in cui può fallire è quando il compilatore è assolutamente sicuro di quale implementazione verrà chiamata in fase di compilazione e saltare la ricerca vTable con la chiamata alla funzione diretta (de-virtualization).

+0

Siamo spiacenti ma non riesco ancora a pubblicare un commento. Sentiti libero di cancellare questo post se non è utile ... –

0

Questo è solitamente chiamato "aggancio di una tabella virtuale" o qualcosa del genere. Se si intende utilizzarlo molto, suggerisco la libreria SourceHook. È stato sviluppato per l'hacking di motori di gioco a sorgente chiusa in mod di gioco. Ad esempio, è stato utilizzato in The Dark Mod prima che idTech4 diventasse open source.