2010-03-04 17 views
7

me encontré con el siguiente trozo de code.Imagine raro que tiene la siguiente typedef:punteros de función y número indeterminado de argumentos en C++

typedef int (*MyFunctionPointer)(int param_1, int param_2); 

Y luego, en una función, que están tratando de ejecutar una función desde un archivo DLL de la siguiente manera:

LPCWSTR DllFileName; //Path to the dll stored here 
LPCSTR _FunctionName; // (mangled) name of the function I want to test 

MyFunctionPointer functionPointer; 

HINSTANCE hInstLibrary = LoadLibrary(DllFileName); 
FARPROC functionAddress = GetProcAddress(hInstLibrary, _FunctionName); 

functionPointer = (MyFunctionPointer) functionAddress; 

//The values are arbitrary 
int a = 5; 
int b = 10; 
int result = 0; 

result = functionPointer(a, b); //Possible error? 

el problema es que no hay ninguna manera de saber si el functon cuya dirección nos dieron, con LoadLibrary toma dos enteros arguments.The nombre de la DLL es proporcionada por el usuario en tiempo de ejecución, se enumeran los nombres de las funciones exportadas y el usuario se toma contacto con el que lo prueba (nuevamente, en tiempo de ejecución: S: S). Entonces, al hacer la llamada de función en la última línea, ¿no estamos abriendo la puerta a posibles daños en la pila? Sé que esto compila, pero ¿qué tipo de error de tiempo de ejecución va a ocurrir en el caso de que estemos pasando argumentos incorrectos a la función a la que estamos apuntando?

+0

Si no está seguro de qué funciones exactas exporta un dll (tipo exacto, pero * no * la convención de llamada), use Dependency Walker. –

Respuesta

4

Hay tres errores que se me ocurre si el esperado y el número o el tipo de parámetros y la convención de llamada utilizados difieren:

  • si la convención de llamada es diferente, los valores de parámetros erróneos serán leídos
  • si la función realmente espera más parámetros que los dados, los valores aleatorios se usarán como parámetros (te dejaré imaginar las consecuencias si los punteros están involucrados)
  • En cualquier caso, la dirección de devolución será completa, por lo que el código aleatorio con el azar los datos se ejecutarán tan pronto como la función regrese.

En dos palabras: Undefined behavior

4

Me temo que no hay forma de saberlo: el programador debe conocer el prototipo de antemano al obtener el puntero de la función y usarlo.

Si no conoce el prototipo de antemano, entonces supongo que debe implementar algún tipo de protocolo con la DLL, donde puede enumerar los nombres de las funciones y sus parámetros llamando funciones conocidas en la DLL. Por supuesto, la DLL debe escribirse para cumplir con este protocolo.

+0

Ese no es mi punto: sé que no debo llamar a la función de esa manera, sino exactamente ¿qué tipo de error va a causar? Es algo que se puede manejar, o voy a estropear mi pila de llamadas y bloquear la aplicación. –

+1

No puede manejarlo. Es posible que no arruine la pila si la función es una función C, pero los parámetros serán incorrectos, lo que por supuesto puede causar bloqueos. – Cthutu

0

Generalmente, si llama a LoadLibrary y GetProcByAddrees, tiene documentación que le indica el prototipo. Incluso más comúnmente como con todos los windows.dll se le proporciona un archivo de cabecera. Si bien esto causará un error si es incorrecto, generalmente es muy fácil de observar y no el tipo de error que se colará en la producción.

+0

Tengo razones para creer que el tipo que escribió esto no tenía CUALQUIER información acerca de la función a la que se llama, además de su nombre. Así que, en el caso de que realmente elijamos una cantidad incorrecta de argumentos, ¿qué va a pasar? –

+0

Cualquier cosa puede suceder. ¿Qué pasa si la función toma tres argumentos o una cadena y un int? Llamar a una función con el número/tipo incorrecto de argumentos no está definido. – lilburne

1

No lo creo, es una buena pregunta, la única disposición es que DEBE saber cuáles son los parámetros para que funcione el puntero a la función, si no lo hace y rellena los parámetros a ciegas y lo llama, se estrellará o saltará al bosque para no volver a verse nunca más ... Depende del programador transmitir el mensaje sobre lo que espera la función y el tipo de parámetros, afortunadamente podría desmontarlo y descubrirlo al mirar la pila puntero y dirección esperada a través del 'puntero de pila' (sp) para averiguar el tipo de parámetros.

Usando PE Explorer, por ejemplo, puede averiguar qué se utilizan las funciones y examinar el volcado desmontaje ...

Espero que esto ayude, Saludos cordiales, Tom .

0

La mayoría de los compiladores C/C++ tienen la persona que llama configura la pila antes de la llamada, y luego reajusta el puntero de la pila. Si la función llamada no utiliza punteros o argumentos de referencia, no habrá corrupción de memoria, aunque los resultados no tendrán ningún valor. Y como dice la repetición, los errores de puntero/referencia casi siempre aparecen con un mínimo de prueba.

+0

Hmm, pensé que con stdcall era responsabilidad del destinatario reajustar la pila después de la llamada a la función. Por lo tanto, si pasamos el número incorrecto de argumentos, ¿eso no arrojaría el SP? –

+0

No. I compiladores C/C++ normales, la persona que llama es responsable. Así es como funciones variadas como printf pueden funcionar con un grado razonable de seguridad. –

+0

Sí, pero una vez más, la convención de llamadas predeterminada para la mayoría de los compiladores C/C++ es _cdecl, no _stdcall, por lo que sé –

1

O bien se bloqueará en el código DLL (ya que se pasó los datos corruptos), o: Creo que Visual C++ agrega código en las compilaciones de depuración para detectar este tipo de problema. Dirá algo así como: "El valor de ESP no se guardó en una llamada de función", y apuntará al código cerca de la llamada. Ayuda, pero no es totalmente robusto: no creo que le impida pasar el argumento incorrecto pero del mismo tamaño (por ejemplo, int en lugar de un parámetro char * en x86). Como dicen otras respuestas, solo tienes que saber, realmente.

+0

Eso sería bastante útil. Hay alguna forma de detectar el problema y no obtener la pantalla de error ? Porque si ese es el caso, podría solucionar el problema de alguna manera. No estoy realmente preocupado por pasar parámetros incorrectos del mismo tamaño, siempre y cuando evite la corrupción de la pila fuera de la función. –

+0

No sé cómo detectarlo usted mismo; Mi mejor opción sería averiguar qué hace la compilación de depuración y agregar su propio ensamblado en línea para hacer la misma verificación en una compilación de lanzamiento. Suena difícil sin embargo. – AshleysBrain

3

Si Es una función __stdcall y que han dejado el nombre de mangling intactos (tanto grandes condiciones, pero ciertamente posible no obstante) el nombre tendrá @nn al final, donde nn es un número. Ese número es el número de bytes que la función espera como argumentos, y borrará la pila antes de que regrese.

Por lo tanto, si es una preocupación importante, puede ver el nombre sin procesar de la función y verificar que la cantidad de datos que está colocando en la pila coincida con la cantidad de datos que eliminará de la pila.

Tenga en cuenta que esto sigue siendo solo una protección contra Murphy, no contra Maquiavelo. Cuando está creando una DLL, puede usar un archivo de exportación para cambiar los nombres de las funciones. Esto se usa con frecuencia para quitarle el nombre a los cambios, pero estoy bastante seguro de que también le permitirá cambiar el nombre de una función de xxx @ 12 a xxx @ 16 (o lo que sea) para engañar al lector sobre los parámetros que espera.

Editar: (principalmente en respuesta al comentario de msalters): es cierto que no puede aplicar __stdcall a algo así como una función miembro, pero ciertamente puede usarlo en cosas como funciones globales, ya sea que estén escritas en C o C++.

Para cosas como las funciones de miembro, el nombre exportado de la función se truncará. En ese caso, puede usar UndecorateSymbolName para obtener su firma completa. Usar eso no es algo trivial, pero tampoco exageradamente complejo.

+0

La pregunta está etiquetada como C++, no como C. Pero describe el nombre que cambia las funciones de C. – MSalters

1

No hay una respuesta general. El Estándar exige que se den ciertas excepciones en ciertas circunstancias, pero aparte de eso describe cómo se ejecutará un programa conforme, y algunas veces dice que ciertas violaciones deben resultar en un diagnóstico. (Puede haber algo más específico aquí o allá, pero ciertamente no recuerdo ninguno.)

Lo que el código está haciendo no está de acuerdo con el Estándar, y dado que hay un molde, el compilador tiene derecho a seguir adelante y hacer cualquier cosa estúpida que el programador quiere sin quejarse. Por lo tanto, esto sería un problema de implementación.

Puede consultar la documentación de implementación, pero probablemente tampoco esté allí. Puede experimentar o estudiar cómo se realizan las llamadas de función en su implementación.

Desafortunadamente, la respuesta es muy probable que arruine algo sin ser inmediatamente obvio.