Llamar a una funcion de una DLL cargada dinamicamente

Iniciado por PHREDA, 12 Julio 2021, 03:08 AM

0 Miembros y 2 Visitantes están viendo este tema.

PHREDA

Hola:

Estoy haciendo un interprete y necesito llamar a una funcion tomada de una dll cargada dinamicamente, esto es asi ya que quiero que el programa interpretado cargue la dll y no el interprete. Ademas necesito que los parametros se encuentren en un pila de enteros.

aqui tengo un fragmento del codigo que estoy probando


HANDLE hStdout;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

HMODULE hLib=LoadLibraryA("KERNEL32.DLL");
if (!hLib) {
     printf("Load Error");
     return 0;
   }else{
     printf("Load OK");
     hFunc=(int64_t)GetProcAddress(hLib,"WriteConsole");
     printf("Address=%x ",hFunc);

// WriteConsole(hStdout, "coso", 5, 0, 0);  // Esto funciona correctamente

// **** quiero que funcione de esta manera ***
      hFunc=(int64_t)(* (int64_t(*)(int64_t,int64_t,int64_t,int64_t,int64_t))hFunc)((int64_t)hStdout,(int64_t)"coso",5,0,0) ;

printf("ok?");
   }


la llamada comentada funciona ok ya que no lo esta haciendo por la direccion de la funcion (hFunc)

pero quiero llamarla por esta direccion y le hago un cast para poder llamarla, aqui quiero aclara que necesito que todos los valores sean pasados int64, me imagina que la funcion a la que llame interpretara el valor segun le corresponda (esto no se como funciona)

Alguien sabe como puedo hacer esto?, desde ya gracias.

Eternal Idol

#1
Las funciones que reciben cadenas como parametros en realidad terminan en A si son ANSI o en W si son widechar, asi que hasta ahora tu GetProcAddress devuelve 0 siempre. Con eso te deberia funcionar relativamente bien en x64 aunque no estas especificando la convencion de llamada que en este caso deberia ser stdcall.

No entiendo muy bien la razon de usar un entero de 64 bits con signo en lugar de un void * para hFunc (idem para los parametros, al menos que el entero sea sin signo y que sea de 4 bytes para codigo de 32 bits) y %x deberia ser %p para corresponderse con un puntero al usar printf.

¿hFunc es un puntero a funcion y despues se le asigna el valor de retorno de WriteConsoleA?

En principio yo lo hubiera hecho asi:
Código (c++) [Seleccionar]
BOOL b = ((BOOL (__stdcall *)(HANDLE, const VOID *, DWORD, LPDWORD, LPVOID))hFunc)(hStdout, "coso", 5, 0, 0);

O para generalizarlo y que funcione siempre con el tamaño de un puntero como parametro:
Código (c++) [Seleccionar]
((BOOL (__stdcall *)(PVOID, PVOID, PVOID, PVOID, PVOID))hFunc)((PVOID)hStdout, (PVOID)"coso", (PVOID)5, 0, 0);

Como la funcion maneja los parametros depende de la convencion de llamada que especificara los registros y/o la pila para acceder a ellos.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

PHREDA

#2
Ante todo, gracias por la respuesta.

efectivamente me da 0 el getprocaddress, pero parece que no existe la version A, en todo caso mas raro aun es que dentro del interprete parace estar funcionando ya que obtengo valores

el codigo del interprete que ejecuta estas funciones:


       case LOADLIB: // "" -- hmo
TOS=(int64_t)LoadLibraryA((char*)TOS);goto next;
case GETPROCA: // hmo "" -- ad
TOS=(int64_t)GetProcAddress((HMODULE)*NOS,(char*)TOS);NOS--;goto next;
case SYSCALL0: // adr -- rs
TOS=sys0(TOS);goto next;


y el codigo de sys0 que es la llamada con tus indicaciones


int64_t sys0(int64_t fn) { return ((int64_t (__stdcall *)())fn)(); }


estoy compilando esto en gcc-4.9.2 64 bits en dev-ccp

el main de prueba es este
////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{

int64_t hFunc;
HMODULE hLib=LoadLibraryA((LPCSTR)"Kernel32.dll");
if (!hLib) {
    printf("Load Error\n");
    return 0;
}else{
    //printf("Load OK\n");
    hFunc=(int64_t)GetProcAddress((HMODULE)hLib,"WriteConsole");
    printf("Address=%p ",hFunc);
   
HANDLE hStdout;
hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
   
// hFunc=(int64_t)(* (int64_t(*)(int64_t,int64_t,int64_t,int64_t,int64_t))hFunc)((int64_t)hStdout,(int64_t)"coso",5,0,0) ;
//sys5((int64_t)hFunc,(int64_t)hStdout,(int64_t)"coso",5,0,0);
WriteConsole(hStdout, "coso", 5, 0, 0);

printf("ok?");
  }      

char filename[1024];
if (argc>1)
strcpy(filename,argv[1]);
else
strcpy(filename,"main.r3");
if (!r3compile(filename)) return -1;
runr3(boot);
return 0;
}


la salida del codigo en
Address=0000000000000000 coso ok?
r3vm - PHREDA
compile:main.r3... ok.
inc:0 - words:24 - code:0Kb - data:0Kb - free:1023Kb

hola fe400000 fe425290 fe41e0a0 fe41d490 fe424ee0 fe424fd0 fe4252f0 fe4253a0 0 allocfe425290
--------------------------------
Process exited after 2.451 seconds with return value 3221225477
Press any key to continue . . .


efectivamente sigue obteniedo 0 pero los numeros que aparecen despues de hola son los  valores que obtiene dentro del interprete

Seguire intentando alternativas.

La razon por el que los parametros son todos interos es que en forth todos los parametros son pasados por una pila de numeros.





recien me di cuenta, WriteConsole, da 0 el resto no..

seguire probando..
gracias compañero !




Lo encontre!, no estaba guardando los 64bits en la memoria que almacena las direcciones de las funciones...

Eternal Idol

Código (c++) [Seleccionar]
hFunc=(int64_t)GetProcAddress((HMODULE)hLib,"WriteConsoleA");

Cita de: msdnA pointer to a buffer that contains characters to be written to the console screen buffer. This is expected to be an array of either char for WriteConsoleA or wchar_t for WriteConsoleW.

https://docs.microsoft.com/en-us/windows/console/writeconsole

Me alegro si conseguiste hacerlo funcionar :)
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

PHREDA

habia entendido mal !
trataba de llamar a GetProcAddressA !!!!


Eternal Idol

Ahh, claro, tiene su logica pero como solo se exportan las funciones en ANSI en un PE no hizo falta la pareja de GetProcAddressA/W  ::)
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

RayR

#6
En general, no le deberías pasar a una función argumentos de tipos distintos a los que espera de la manera en que lo estás haciendo (o sea, mediante puntero a función con cast en los parámetros). Independientemente de cómo los interprete, el verdadero problema es que los tamaños podrían ser diferentes. Por ejemplo, el tercer parámetro de WriteConsole es de tipo DWORD, que ocupa 32 bits, pero al hacer el cast a int64_t le estarías pasando 64. En este caso imagino que funciona, pero es casi por casualidad. Estás compilando para 64 bits, por lo que los primeros 4 argumentos se van a pasar en registros. Como son independientes, no tienes problemas, pero si se pasaran en la pila, podría fallar. Esto pasaría si intentaras hacerlo en programas de 32 bits, o llamar funciones que reciban más parámetros. Por darte un ejemplo, no podrías llamar de manera confiable a CreateProcess, ni siquiera en 64 bits.

Imagino que lo estás haciendo para practicar, y está bien, pero obviamente, no sería buena idea hacer algo así en algo más "formal".

Eternal Idol

#7
Efectivamente eso es lo que pasa con su ejemplo compilado en 32 bits, los parametros que la funcion necesita son de 4 y no 8 bytes pero asi como en Windows en modo protegido en 32 bits los parametros son todos de 4 bytes en Windows en modo largo en x64 los parametros son todos de 8 bytes. Por eso le dije que en principio usara los tipos de parametros de la funcion y de ultima usara PVOID que al menos es el tamaño de un puntero y este se debe poder pasar en un registro y pila "nativamente".

En realidad si podes llamar a CreateProcessA sin problemas, cada parametro en pila ocupa 64 bits de por si, aunque despues la funcion llamada solo lea los primeros 8, 16 o 32 bits. Si bien hay una sutil diferencia en el codigo generado en ambos casos cada parametro ocupa 8 bytes (notese como el  quinto - 1 - y sexto parametros - 0x1230 - se mueven como DWORD o QWORD pero el siguiente parametro siempre esta a la misma distancia, 8 bytes):

Código (asm) [Seleccionar]
; 45   : ((BOOL (__stdcall *)(LPCSTR, LPSTR, LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION))CreateProcessA_fn)
; 46   : (NULL, argv[1], NULL, NULL, TRUE, 0x1230, NULL, NULL, &si, &pi);
mov eax, 8
imul rax, rax, 1
lea rcx, QWORD PTR pi$3[rsp]
mov QWORD PTR [rsp+72], rcx
lea rcx, QWORD PTR si$5[rsp]
mov QWORD PTR [rsp+64], rcx
mov QWORD PTR [rsp+56], 0
mov QWORD PTR [rsp+48], 0
mov DWORD PTR [rsp+40], 4656 ; 00001230H
mov DWORD PTR [rsp+32], 1
xor r9d, r9d
xor r8d, r8d
mov rcx, QWORD PTR argv$[rsp]
mov rdx, QWORD PTR [rcx+rax]
xor ecx, ecx
call QWORD PTR CreateProcessA_fn$2[rsp]


Código (asm) [Seleccionar]
; 48   : ((BOOL (__stdcall *)(PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID, PVOID))CreateProcessA_fn)
; 49   : (NULL, argv[1], NULL, NULL, (PVOID)TRUE, (PVOID)0x1230, NULL, NULL, &si, &pi);
mov eax, 8
imul rax, rax, 1
lea rcx, QWORD PTR pi$3[rsp]
mov QWORD PTR [rsp+72], rcx
lea rcx, QWORD PTR si$5[rsp]
mov QWORD PTR [rsp+64], rcx
mov QWORD PTR [rsp+56], 0
mov QWORD PTR [rsp+48], 0
mov QWORD PTR [rsp+40], 4656 ; 00001230H
mov QWORD PTR [rsp+32], 1
xor r9d, r9d
xor r8d, r8d
mov rcx, QWORD PTR argv$[rsp]
mov rdx, QWORD PTR [rcx+rax]
xor ecx, ecx
call QWORD PTR CreateProcessA_fn$2[rsp]


Si podria tener problemas con tipos de coma flotante que se pasan en otros registros pero no recuerdo ninguna API de Windows que los use.




Y de cualquier manera si coincido en que es algo a evitar de ser posible.
La economía nunca ha sido libre: o la controla el Estado en beneficio del Pueblo o lo hacen los grandes consorcios en perjuicio de éste.
Juan Domingo Perón

PHREDA

Gracias RayR por tus comentarios.
Se trata de un interprete, o sea, en tiempo de ejecucion va a cargar un dll y llamar a funciones, no es ningun ejemplo para practicar.
Es cierto que no se que pasara cuando el parametro en punto flotante, veremos cuando use una dll para graficos 3d y como podria solucionar lo
aca subi el proyecto porque ya funciona !
https://github.com/phreda4/r3evm

Bien por el codigo generadp por Eternal Idol, cuando construya el compilador voy a necesitar realizar esto automaticamente, aunque quizas tome el mismo camino que el interprete al inicio, una funcion por cada cantidad de parametros.
El compilador de las versiones anteriores no usaba este mecanismo.


RayR

Cierto, me olvidaba de que para x86-64 los parámetros en la pila se alinean a 64 bits.