Banner 1

Inyección de código en procesos

0×01 | Intro
0×02 | API’s
0×03 | Inyectando con DLL
0×04 | Inyectando sin DLL

0×01 | Intro

Voy a explicar mas o menos como inyectar nuestro propio código en otro proceso. Primero inyectaremos una dll, después lo haremos a pelo.

0×02 | API’s

Primero necesitamos conocer algunas APIs de windows:
OpenProcess
  1. HANDLE WINAPI OpenProcess(
  2. __in  DWORD dwDesiredAccess,
  3. __in  BOOL bInheritHandle,
  4. __in  DWORD dwProcessId
  5. );
Abre el proceso especificando su PID en dwProcessId, con los privilegios deseados. Retorna un manejador
al proceso, o NULL en caso de error.
VirtualAllocEx:
  1. LPVOID WINAPI VirtualAllocEx(
  2. __in      HANDLE hProcess,
  3. __in_opt  LPVOID lpAddress,
  4. __in      SIZE_T dwSize,
  5. __in      DWORD flAllocationType,
  6. __in      DWORD flProtect
  7. );
Reserva memoria en el espacio de otro proceso, comenzando en lpAddress (si se le pasa NULL el sistema elige por nosotros), con el tamaño dwSize y con los permisos que le asignemos (usaremos PAGE_EXECUTE_READWRITE).
Devuelve un puntero a esa memoriam o NULL en caso de error.
WriteProcessMemory:
  1. BOOL WINAPI WriteProcessMemory(
  2. __in   HANDLE hProcess,
  3. __in   LPVOID lpBaseAddress,
  4. __in   LPCVOID lpBuffer,
  5. __in   SIZE_T nSize,
  6. __out  SIZE_T *lpNumberOfBytesWritten
  7. );
Escribe en la memoria dada por lpBaseAddress el contenido apuntado por lpBuffer, hasta nSize bytes. Devuelve FALSE en caso de error.
CreateRemoteThread:
  1. HANDLE WINAPI CreateRemoteThread(
  2. __in   HANDLE hProcess,
  3. __in   LPSECURITY_ATTRIBUTES lpThreadAttributes,
  4. __in   SIZE_T dwStackSize,
  5. __in   LPTHREAD_START_ROUTINE lpStartAddress,
  6. __in   LPVOID lpParameter,
  7. __in   DWORD dwCreationFlags,
  8. __out  LPDWORD lpThreadId
  9. );
Lanza un hilo en el proceso hProcess (abierto con OpenProcess), estando en ese hilo la funcion apuntada por lpStartAddress con los argumentos apuntados por lpParameter.
También usaremos Tlhelp32.h para obtener el PID de un proceso sabiendo su nombre.
  1. HANDLE processList=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  2. PROCESSENTRY32 pInfo;
  3. BOOL st=TRUE;
  4. pInfo.dwSize=sizeof(PROCESSENTRY32);
  5. Process32First(processList, &pInfo);
  6. int myPid=0;
  7. do
  8. {
  9. if(strcmp(pInfo.szExeFile, "test.exe")==0)
  10. {
  11. myPid=pInfo.th32ProcessID;
  12. break;
  13. }
  14. Process32Next(lista, &pInfo);
  15. }
  16. while(st!=FALSE);
Ver MSDN para mas info.

0×03 | Inyectando con DLL

Primero necesitamos una DLL que inyectar, la cual al ser programada por nosotros hará lo que queramos.
  1. #include ;
  2. #include ;
  3. BOOL APIENTRY DllMain (HINSTANCE hInst,
  4. DWORD reason,
  5. LPVOID reserved)
  6. {
  7. switch (reason)
  8. {
  9. case DLL_PROCESS_ATTACH:
  10. MessageBoxA(NULL, "Hi!", "Hey!", 0);
  11. break;
  12. }
  13. return TRUE;
  14. }
Ahora tenemos que hacer al proceso cargar esa DLL. Para ello:
  • Abrimos el proceso, reservamos memoria en él, y escribimos en esa memoria la ruta de la DLL.
  • Obtener la dirección de LoadLibrary con GetProcAddress. LoadLibrary se encuentra en kernel32.dll, y su dirección es compartida, por lo que podemos cargarla desde el inyector y usarla desde el inyectado.
  • Lanzar un hilo en el proceso abierto, dando como punto de entrada la dirección de LoadLibrary y como argumento la dirección del nombre de la DLL previamente escrito.
Todo junto queda así:
  1. #include ;
  2. #include ;
  3. #include ;
  4.  
  5. void error(char *err);
  6.  
  7. HANDLE myProc=NULL;
  8.  
  9. int main(int argc, char *argv[])
  10. {
  11.  
  12. HANDLE processList=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  13. PROCESSENTRY32 pInfo;
  14. BOOL st=TRUE;
  15. pInfo.dwSize=sizeof(PROCESSENTRY32);
  16. Process32First(processList, &pInfo);
  17. int myPid=0;
  18. do
  19. {
  20. if(strcmp(pInfo.szExeFile, "test.exe")==0)
  21. {
  22. myPid=pInfo.th32ProcessID;
  23. break;
  24. }
  25. Process32Next(processList, &pInfo);
  26. }
  27. while(st!=FALSE);
  28.  
  29. // Abrir el proceso
  30. printf("[+] Opening process %i\n", myPid);
  31. myProc=OpenProcess(PROCESS_ALL_ACCESS, FALSE, myPid);
  32. if(myProc==NULL) error("[-] Error abriendo proceso.\n");
  33. else printf("[+] Proceso abierto.\n");
  34.  
  35. // Reservar memoria para el argumento (ruta de la DLL)
  36. char thData[]="dll.dll";
  37. LPVOID dirToArg=VirtualAllocEx(myProc, NULL, strlen(thData), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  38. if(dirToArg==NULL) error("[-] Error reservando memoria para argumento.\n");
  39. else printf("[+] Memoria reservada para argumento (%i bytes).\n", strlen(thData));
  40. // Escribir la ruta de la DLL en la memoria reservada
  41. SIZE_T written=0;
  42. if(WriteProcessMemory(myProc, dirToArg, (LPVOID)&thData, strlen(thData), &written)==0) error("[-] Error escribiendo memoria.\n");
  43. else printf("[+] Memoria escrita (arg %i bytes).\n", written);
  44.  
  45. // Lanzar un hilo con LoadLibrary
  46. HANDLE rThread=CreateRemoteThread(myProc, NULL, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(LoadLibrary("Kernel32.dll"), "LoadLibraryA"), dirToArg, 0, NULL);
  47. if(rThread==NULL) error("[-] Error creando el hilo.\n");
  48. else printf("[+] Hilo creado.\n");
  49.  
  50. CloseHandle(myProc);
  51. }
  52.  
  53. void error(char *err)
  54. {
  55. if(myProc!=NULL) CloseHandle(myProc);
  56. printf("%s", err);
  57. exit(0);
  58. }

0×04 | Inyectando sin DLL

Aquí la cosa se complica. Las diferencias entre un método y otro son:
  • Ahora no podemos utilizar cadenas de texto directamente como argumentos a funciones. Ya veremos por qué.
  • No podemos llamar a ninguna función que no carguemos previamente con LoadLibrary + GetProcAddress. Esas dos están cargadas en todos los ejecutables de Windows desde kernel32, así que podemos usar un puntero a ellas.
  • Tenemos que escribir todo el código de nuestras funciones en el espacio de memoria del proceso.
  • Necesitaremos estructuras mas complejas como argumento de nuestros hilos remotos.
Así que el proceso de inyección quedaría así:
  • Crear una estructura de datos con TODAS las cadenas de texto que vayamos a usar en el código inyectado, y con un puntero a LoadLibrary y GetProcAddress.
  • Abrir el proceso.
  • Reservar memoria para la estructura de datos. Escribirla.
  • Reservar memoria para nuestro código. Escribirlo.
  • Lanzar el hilo remoto, dando como punto de entrada el puntero a neustro código y como argumento el puntero a nuestra estructura de datos.
Y el código quedaría así:
  1. #include 
  2. #include ;
  3. #include ;
  4.  
  5. void error(char *err);
  6. static DWORD WINAPI myFunc(LPVOID data);
  7.  
  8. HANDLE myProc=NULL;
  9.  
  10. // Con esto cargaremos punteros a LoadLibrary y GetProcAddress en nuestra estrucura de datos
  11. typedef int (WINAPI *datLoadLibrary)(LPCTSTR);
  12. typedef int (WINAPI *datGetProcAddress)(HMODULE, LPCSTR);
  13.  
  14. int main(int argc, char *argv[])
  15. {
  16. if(argc<2) error("Uso: hook.exe PROCESO\n");     // Esta es nuestra estructura de argumentos
  17. struct {
  18. char lnUser32[50];                                 // ->; "User32.dll"
  19. char fnMessageBoxA[50];                       // -> "MessageBoxA"
  20. datLoadLibrary apiLoadLibrary;               // Puntero a LoadLibrary
  21. datGetProcAddress apiGetProcAddress;  // Puntero a GetProcAddress
  22. char Msg[50];                                  // Texto que usaremos en MessageBoxA
  23. } thData;
  24. strcpy(thData.lnUser32, "User32.dll");
  25. strcpy(thData.fnMessageBoxA, "MessageBoxA");
  26. strcpy(thData.Msg, "Hola!");
  27. thData.apiLoadLibrary=GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
  28. thData.apiGetProcAddress=GetProcAddress(GetModuleHandle("kernel32.dll"), "GetProcAddress");
  29.  
  30. int funcSize=600; // El tamaño de nuestra función. Podria calcularse de forma exacta, pero como ejemplo
  31. // exagerado nos vale .
  32.  
  33. HANDLE processList=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  34. PROCESSENTRY32 pInfo;
  35. BOOL st=TRUE;
  36. pInfo.dwSize=sizeof(PROCESSENTRY32);
  37. Process32First(processList, &amp;pInfo);
  38. int myPid=0;
  39. do
  40. {
  41. if(strcmp(pInfo.szExeFile, argv[1])==0)
  42. {
  43. myPid=pInfo.th32ProcessID;
  44. break;
  45. }
  46. Process32Next(processList, &amp;pInfo);
  47. }
  48. while(st!=FALSE);
  49.  
  50. // Abrir proceso
  51. printf("[+] Abriendo proceso %i\n", myPid);
  52. myProc=OpenProcess(PROCESS_ALL_ACCESS, FALSE, myPid);
  53. if(myProc==NULL) error("[-] Error abriendo proceso.\n");
  54. else printf("[+] Proceso abierto.\n");
  55.  
  56. // Reservar memoria para argumentos
  57. LPVOID dirToArg=VirtualAllocEx(myProc, NULL, sizeof(thData), MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  58. if(dirToArg==NULL) error("[-] Error reservando memoria para arg.\n");
  59. else printf("[+] Memoria reservada para arg (%i bytes).\n", sizeof(thData));
  60. // Escribir argumentos
  61. SIZE_T written=0;
  62. if(WriteProcessMemory(myProc, dirToArg, (LPVOID)&amp;thData, sizeof(thData), &amp;written)==0) error("[-] Error escribiendo la estructura de datos.\n");
  63. else printf("[+] Memoria escrita (arg %i bytes).\n", written);
  64.  
  65. // Reservar memoria para la funcion
  66. LPVOID dirToWrite=VirtualAllocEx(myProc, NULL, funcSize, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  67. if(dirToWrite==NULL) error("[-] Error rreservando memoria para codigo.\n");
  68. else printf("[+] Memoria reservada para codigo (%i bytes).\n", funcSize);
  69. // Escribimos el codigo de nuestra funcion
  70. if(WriteProcessMemory(myProc, dirToWrite, (LPVOID)myFunc, funcSize, &amp;written) == 0) error("[-]Error escribiendo memoria.\n");
  71. else printf("[+] Memoria escrita (codigo).\n");
  72.  
  73. // Lanzamos el hilo
  74. HANDLE rThread=CreateRemoteThread(myProc, NULL, 0, (LPTHREAD_START_ROUTINE)dirToWrite, dirToArg, 0, NULL);
  75. if(rThread==NULL) error("[-] Error lanzando hilo.\n");
  76. else printf("[+] Hilo lanzado.\n");
  77. CloseHandle(myProc);
  78.  
  79. return 0;
  80. }
  81.  
  82. void error(char *err)
  83. {
  84. if(myProc!=NULL) CloseHandle(myProc);
  85. printf("%s", err);
  86. exit(0);
  87. }
  88.  
  89. static DWORD WINAPI myFunc(LPVOID data)
  90. {
  91. // Cargamos nuestros datos en una estructura como la que hicimos
  92. struct {
  93. char lnUser32[50];
  94. char fnMessageBoxA[50];
  95. datLoadLibrary apiLoadLibrary;
  96. datGetProcAddress apiGetProcAddress;
  97. char MSG[50];
  98. } *thData;
  99. thData=data;
  100. // Podemos conseguir cualquier API con estas dos, siempre que tengamos su nombre en la estructura
  101. void *apiDir=(void *)thData->apiGetProcAddress((HANDLE)thData->apiLoadLibrary(thData->lnUser32), thData->fnMessageBoxA);
  102. // Puntero a funcion similar a MessageBoxA
  103. INT WINAPI (*myMessageBox)(HWND, LPCSTR, LPCSTR, UINT);
  104. myMessageBox=apiDir;
  105. myMessageBox(NULL, thData->MSG, thData->MSG, 0);
  106. return;
  107. }
Creo que todo está mas o menos bien explicado. Yo tampoco soy un experto en ésto así que puede que se me haya colado algún gazapo, cualquier cosa avisadme. Bytez.

AGRADECIMIENTOS TOTALES A:

http://blog.ka0labs.org

No hay comentarios:

Powered by Bad Robot
Helped by Blackubay