2012-05-04 26 views
6

Tengo un problema con las desviaciones. Los desvíos, como todos saben, solo pueden moverse entre 5 bytes de espacio (es decir, una llamada 'jmp' y una dirección de 4 bytes). Debido a esto, es imposible tener la función 'gancho' en una clase (un método), no puede suministrar el puntero 'esto' porque simplemente no hay suficiente espacio (here's el problema más explicado). Así que he estado buscando ideas todo el día para encontrar una solución, y ahora quiero sus pensamientos sobre el tema, así que no empiezo un proyecto de 3 a 5 días sin saber si sería posible o no.Funciones dinámicas C++ y TOTALMENTE

Tenía inicialmente 3 objetivos, quería que las funciones de 'gancho' fueran métodos de clase, quería que todo el enfoque estuviera orientado a objetos (sin funciones estáticas u objetos globales) y, la parte peor/más difícil, ser completamente dinámico. Esta es mi solución (en teoría); con el ensamblaje uno puede modificar funciones en tiempo de ejecución (un ejemplo perfecto es cualquier método de desvío). Entonces, como puedo modificar las funciones de forma dinámica, ¿no debería ser capaz de crearlas dinámicamente? Por ejemplo; Asigno memoria para, digamos ~ 30 bytes (a través de malloc/nuevo). ¿No sería posible simplemente reemplazar todos los bytes con números binarios correspondientes a diferentes operadores de ensamblaje (como 0xE9 es 'jmp') y luego llamar directamente a la dirección (ya que contendría una función)?

NOTA: Sé de antemano el valor de retorno, y todos los argumentos para todas las funciones que quiero desviar, y como estoy usando GCC, la convención thiscall es prácticamente idéntica a la de _cdecl.

Así que esta es mi idea/próxima implementación; Creo una clase 'Función'. Este constructor toma una cantidad variada de argumentos (excepto el primer argumento, que describe el valor de retorno de la función objetivo).

Cada argumento es una descripción de los argumentos que el gancho recibirá (el tamaño, y si es un puntero o no). Entonces digamos que quiero crear una clase de Función para un int * RandomClass::IntCheckNum(short arg1);. Entonces solo tendría que hacer esto: Function func(Type(4, true), Type(4, true), Type(2, false));. Donde 'Tipo' se define como Type(uint size, bool pointer). Luego, a través del ensamblaje pude crear dinámicamente la función (nota: esto sería todo usando la convención de llamadas _cdecl) ya que puedo calcular el número de argumentos y el tamaño total.

EDIT: Con el ejemplo, Type(4, true) es el valor de retorno (int *), el scond Type(4, true) es el RandomClass este puntero y Type(2, false) describe el primer argumento (corto arg1).

Con esta implementación podría tener fácilmente métodos de clase como devoluciones de llamada, pero requeriría una gran cantidad de código de ensamblado (que ni siquiera tengo experiencia). Al final, la única cosa no dinámica serían los métodos en mi clase de devolución de llamada (que también requeriría devoluciones de llamada previas y posteriores).

Así que quería saber; ¿es posible? ¿Cuánto trabajo requeriría, y estoy por encima de mi cabeza aquí?

EDIT: Perdón si presenté todo un poco borroso, pero si hay algo que quieras explicar con más detalle, ¡pregunta!

EDIT2: ¿Me gustaría saber si puedo encontrar los valores hexadecimales para todos los operadores de ensamblaje en alguna parte? ¡Una lista ayudaría muchísimo! Y/o si es posible de alguna manera 'guardar' el asm (""); código en una dirección de memoria (que dudo mucho).

+0

¿Por qué usar desvíos en absoluto? ¿No puedes usar una solución pura de C++ como 'std :: function' o me falta algo? –

+0

No como si pudiera ayudarte solo para aclarar las cosas. ¿Desea una función regrabable en una clase? (Es decir, puede cambiarlos en tiempo de ejecución) Si es así, creo que (cuando termine) podría abrir oportunidades gigantes para la programación de IA en C++. +1 – akaltar

+0

@akaltar Esto se conoce como [programación genética] (http://en.wikipedia.org/wiki/Genetic_programming) y en realidad no necesita funciones regrabables. –

Respuesta

4

Lo que describes se suele llamar "thunking" y se implementa con bastante frecuencia. Históricamente, el objetivo más común ha sido el mapeo entre códigos de 16 y 32 bits (mediante la generación automática de una nueva función de 32 bits que llama a una existente de 16 bits o viceversa). Creo que algunos compiladores de C++ generan funciones similares para ajustar punteros de clase base a punteros de subclase en herencia múltiple, también.

Definitivamente parece una solución viable a su problema, y ​​no preveo problemas importantes. Solo asegúrese de asignar la memoria con los indicadores necesarios en su sistema operativo para asegurarse de que la memoria sea ejecutable (la mayoría de los sistemas operativos modernos entregan memoria no ejecutable de forma predeterminada).

Usted puede encontrar este enlace útil, sobre todo si se trabaja en Win32: http://www.codeproject.com/Articles/16785/Thunking-in-Win32-Simplifying-Callbacks-to-Non-sta

En cuanto a la búsqueda de los valores hexadecimales de las operaciones de montaje, la mejor referencia que conozco es el apéndice del Manual del ensamblador NASM (y yo no digas eso solo porque ayudé a escribirlo). Hay una copia disponible aquí: http://www.posix.nl/linuxassembly/nasmdochtml/nasmdoca.html

+0

Wow excelentes enlaces! Fue realmente interesante leer sobre el proceso de thunk (demasiado malo, fue Win32). Ahora disculpe si sueno tonto, pero como mencioné anteriormente, no tengo mucha experiencia con el montaje (solo conozco un poco la sintaxis de AT & T), así que tuve que preguntar sobre el ensamblador NASM al que hizo referencia. Tengo 2 preguntas; ¿todos los operadores de ASM solo usan 1 byte? Y en segundo lugar, dado que hay muchos valores diferentes especificados para cada operador, ¿en cuál me interesa? Supongo que depende del tamaño de mis variables, pero para 'empujar' hay 13 valores diferentes, ¿cómo sé cuál quiero? –

+1

Estas son variantes diferentes para diferentes tipos de instrucciones de inserción (tipos de registros, valores inmediatos, referencias indirectas de memoria). La parte superior de la guía tiene una descripción de todos los modos diferentes, así que úselo para averiguar cuál quiere, luego solo mire hacia abajo para encontrar el formato de instrucción que necesita. Digamos que quiere presionar EBX: eso es un reg32, entonces quiere la segunda variante, que es "o32 50 + r". o32 es un prefijo de tamaño de operando, que se ignora si se ejecuta en código de 32 bits; 50 + r es 50 hex más el código para el registro (3, están listados en la parte superior), entonces 53h es tu código. – Jules

+1

En respuesta a su primera pregunta, no hay instrucciones que tienen más de un byte de longitud, y algunas instrucciones varían en tamaño según el contexto (consulte el ejemplo PUSH anterior: el prefijo 'o32' no genera ningún código en 32 bits modo, sin embargo, si está produciendo código de 16 bits, sería un byte de 66h adicional que aparece al comienzo de la instrucción). Sin embargo, todas las instrucciones más comunes son de un solo byte. – Jules

Cuestiones relacionadas