2009-10-09 13 views
11

Me gustaría saber cómo cambiar la dirección de Test que está en la tabla virtual con la de HackedVTable.¿Cómo hackear la tabla virtual?

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 respuesta será muy apreciada.

Gracias de antemano.

+5

No me importa por qué quieres hacer esto. Es una mala idea. No lo hagas – abelenky

+5

@abelenky, esto es solo para fines educativos. Yo quería saber cómo funcionan las cosas. :) – mahesh

+0

@ El comentario de Martin es el mejor consejo que puede obtener con fines educativos. de lo contrario, simplemente ríndete, el compilador es mejor para este trabajo. – LiraNuna

Respuesta

15

Esto funciona para compilaciones MSVC de 32 bits (es una versión muy simplificada de un código de producción que se ha utilizado durante más de un año). Tenga en cuenta que su método de reemplazo debe especificar explícitamente el parámetro this (puntero).

// 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

Muchas gracias. :) – mahesh

+1

NP. Tuve que usarlo para corregir una validación de parámetros defectuosos que apareció en la configuración de mi máquina en una aplicación de terceros que causaba un bloqueo cada vez que intentaba usar el programa. –

8

La V-Table es un detalle de implementación.

No es necesario que el compilador utilice uno (simplemente resulta ser la forma más fácil de implementar funciones virtuales). Pero diciendo que cada compilador puede (y lo hace) implementarlo de manera ligeramente diferente, como resultado, no hay respuesta a su pregunta.

Si le preguntas ¿cómo hackear una vtable para un programa construido con:

Compilador < X> Versión < Y> Construir < Z>

Entonces alguien puede saber la respuesta.

+3

Bien dicho. Además, incluso los compiladores que usan una V-Table a veces producen código con enlace estático si la semántica del programa lo permite. – mjv

2

No creo que haya una manera portátil. Principalmente debido a la optimización del compilador y diferentes arquitecturas ABI entre cada objetivo.

Pero C++ le proporciona exactamente la misma capacidad, ¿por qué no usarla?

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

Quería saber para cambiar la dirección en vtable – mahesh

+2

No desea saber. Incluso para fines educativos, la teoría es * buena * en este caso. El compilador sabe mucho más que usted sobre su programa de lo que podría pensar, así que jugar con una tabla que puede existir o no (en este caso, nunca, ya que no está en uso) es una mala idea y solo hará que piense eres un mal programador segbanding demasiado. No lo hagas – LiraNuna

0

es bastante fácil de entender. Buscar el puntero VTAble (En Visual Studio son los primeros 4/8 bytes). Luego ingrese en una llamada normal de Prueba (en el ensamblador) y verá que salta al Vtable y luego a su función de prueba. Para anular la prueba, simplemente reemplace el puntero desde donde saltó en la VTable.

+0

¿Quiere decir que MSVC no optimiza las anulaciones obvias (si no se usan, por ejemplo)? – 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, muchas gracias ... simple y elegante. :) – mahesh

+1

@mahesh, esto es * funcionalidad * completamente diferente que la mía. Aquí, reemplaza las funciones para una sola instancia (b1). En el mío, reemplaza las funciones para * todas * instancias de un tipo específico. –

1

Otra forma de lograr lo mismo es por cheking código similar:

GObject:

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

GLib:

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

Vala:

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

Esos tipos quería trabajar con un objeto y el lenguaje de programación orientado a clase, pero "C++", no se ajuste a sus requisitos. Luego, tomaron "plain C" y simularon objetos con registros & punteros, incluidas tablas de métodos virtuales. Finalmente obtuve un lenguaje similar llamado "Vala", su propio lenguaje "C++" (con su propio V.M.T.).

Otros enlaces relacionados:

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

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

aplausos.

1

En Mac OS X 10.10.3 + gcc 4.8.3, el siguiente código funciona bien.

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; 
} 

Su salida:

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

I probar el mismo código bajo Ubuntu 12.04 + g ++ 4.9.0. Sin embargo, no funciona y surge una falla de segmentación. Parece que Linux asigna la tabla de funciones virtuales en un área de solo lectura (por ejemplo, rodata) para prohibir la piratería.

-1

No creo que la vTable esté en el área de solo lectura porque está dinámicamente poblada. La única forma en que puede fallar es cuando el compilador está absolutamente seguro de qué implementación se llamará en tiempo de compilación y omita la búsqueda de vTable con llamada a función directa (de-virtualización).

+0

Lo sentimos, pero todavía no se puede publicar un comentario. Siéntase libre de eliminar esta publicación si no es útil ... –

0

Esto se suele llamar "enganche de mesa virtual" o algo así. Si vas a usarlo mucho, sugiero la biblioteca SourceHook. Fue desarrollado para hackear motores de juegos de código cerrado en modificaciones de juegos. Por ejemplo, se usó en The Dark Mod antes de que idTech4 se convirtiera en código abierto.

Cuestiones relacionadas