The code cave method

Instead of exploiting a windows API function to force the process to load our Dll, this time we'll allocate a little chunk memory inside the target application, and inject a little stub that loads our dll. The advantage of this approach is that it will work on any version of windows, and it's also the least detectable of any of the methods mentioned thus far. Our stub will look like this:

Código:
__declspec(naked) loadDll(void) 
{ 
   _asm{ 
      //   Placeholder for the return address 
      push 0xDEADBEEF 

      //   Save the flags and registers 
      pushfd 
      pushad 

      //   Placeholder for the string address and LoadLibrary 
      push 0xDEADBEEF 
      mov eax, 0xDEADBEEF 

      //   Call LoadLibrary with the string parameter 
      call eax 

      //   Restore the registers and flags 
      popad 
      popfd 
       
      //   Return control to the hijacked thread 
      ret 
   } 
}
0xDEADBEEF is just there to mark addresses that we can't know beforehand, and have to patch-in at runtime. Ok, let's make a list of the things that we need to do to make this work: - Allocate space for the stub
- Allocate space for the name of the dll
- Suspend the main thread of our target
- Get the address of the next instruction to be executed(need this for the next step)
- Patch the proper address to return to in the stub
- Patch the address of the dll name
- Patch the address of LoadLibrary
- Set the address of the next instruction to be executed in our target's thread, to the address of the beginning of our stub
- Resume the target's thread



To allocate space inside the target, we'll use VirtualAllocEx(). We'll need to open a handle to the process
with the VM_OPERATION privelege specified, in order to do this. For our dllName string, we'll only need read and write priveleges. For the stub however, we'll need read, write, and execute priveleges. Then we'll write in our dllName string, so that we can reference it from the stub once it's inserted.

Código:
void *dllString, *stub; 
unsigned long wowID; 
HANDLE hProcess 
    
//See Appendix A for 
//this function 
wowID     = GetTargetProcessIdFromProcname(PROC_NAME);       

hProcess  = OpenProcess((PROCESS_VM_WRITE | PROCESS_VM_OPERATION), false, wowID); 

dllString = VirtualAllocEx(hProcess, 
                           NULL, 
                           (strlen(DLL_NAME) + 1), 
                           MEM_COMMIT, 
                           PAGE_READWRITE
                          ); 
stub      = VirtualAllocEx(hProcess, 
                           NULL, 
                           stubLen, 
                           MEM_COMMIT, 
                           PAGE_EXECUTE_READWRITE
                          ); 
WriteProcessMemory(hProcess, dllString, DLL_NAME, strlen(DLL_NAME), NULL);
To accomplish our next few tasks, we'll need a handle to one of our target's threads. We can use the functions from Appendix B to get the ID of one such thread, and then use the OpenThread API to get a handle. We'll need to be able to get and set context, and also suspend and resume the thread.

Código:
unsigned long threadID; 
HANDLE hThread; 

threadID  = GetTargetThreadIdFromProcname(PROC_NAME); 
hThread   = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME), 
                        false, 
                        threadID
                       );
Now, we need to pause the thread in order to get it's "context". The context of a thread is the current state of all of it's registers, as well as other peripheral information. However, we're mostly concerned with the EIP register, which points to the next instruction to be executed. So, if we don't suspend the thread before retrieving its context information, it'll continue executing and by the time we get the information, it'll be invalid. Once we've paused the thread, we'll retrieve it's context information using the GetThreadContext() function. We'll grab the value of the current next instruction to be executed, so that we know where our stub should return to. Then it's just a matter of patching up the stub to have all of the proper pointers, and forcing the thread to execute it:
Código:
SuspendThread(hThread); 

ctx.ContextFlags = CONTEXT_CONTROL; 
GetThreadContext(hThread, &ctx); 
oldIP   = ctx.Eip; 

//Set the EIP of the context to the address of our stub 
ctx.Eip = (DWORD)stub; 
ctx.ContextFlags = CONTEXT_CONTROL; 

//Right now loadDll is code, which isn't writable.  We need 
//to change that. 
VirtualProtect(loadDll, stubLen, PAGE_EXECUTE_READWRITE, &oldprot);    

//Patch the first push instruction 
memcpy((void *)((unsigned long)loadDll + 1), &oldIP, 4);    
//Patch the 2nd push instruction 
memcpy((void *)((unsigned long)loadDll + 8), &dllString, 4);    
//Patch the mov eax, 0xDEADBEEF to mov eax, LoadLibrary 
memcpy((void *)((unsigned long)loadDll + 13), &loadLibAddy, 4); 

//Write the stub into the target 
WriteProcessMemory(hProcess, stub, loadDll, stubLen, NULL);         

//Set the new context of the target's thread 
SetThreadContext(hThread, &ctx); 

//Let the target thread continue execution, starting at our stub 
ResumeThread(hThread);
All that's left now, is to cleanup the evidence. Before we do that though, we should pause the injector for a bit, to be sure that the target has time to execute our stub(don't want any nasty race conditions). We'll use Sleep() to pause for 8 seconds before unmapping the memory that we allocated, and exiting the injector.
Código:
Sleep(8000); 
VirtualFreeEx(hProcess, dllString, strlen(DLL_NAME), MEM_DECOMMIT); 
VirtualFreeEx(hProcess, stub, stubLen, MEM_DECOMMIT); 
CloseHandle(hProcess); 
CloseHandle(hThread);

This method should work on any version of windows, and should be the least likely to trigger any A/V alarms or cause the program to malfunction. If you can understand it and implement it properly, this is definitely the best of the three methods.

Complete code cave example source code

Código:
#include  
#include  
#include  

#define PROC_NAME "target.exe" 
#define DLL_NAME "injected.dll" 

unsigned long GetTargetProcessIdFromProcname(char *procName); 
unsigned long GetTargetThreadIdFromProcname(char *procName); 

__declspec(naked) loadDll(void) 
{ 
   _asm{ 
      //   Placeholder for the return address 
      push 0xDEADBEEF 

      //   Save the flags and registers 
      pushfd 
      pushad 

      //   Placeholder for the string address and LoadLibrary 
      push 0xDEADBEEF 
      mov eax, 0xDEADBEEF 

      //   Call LoadLibrary with the string parameter 
      call eax 

      //   Restore the registers and flags 
      popad 
      popfd 
       
      //   Return control to the hijacked thread 
      ret 
   } 
} 

__declspec(naked) loadDll_end(void) 
{ 
} 

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow) 
{ 
   void *dllString; 
   void *stub; 
   unsigned long wowID, threadID, stubLen, oldIP, oldprot, loadLibAddy; 
    HANDLE hProcess, hThread; 
   CONTEXT ctx; 
    
   stubLen = (unsigned long)loadDll_end - (unsigned long)loadDll; 
    
   loadLibAddy = (unsigned long)GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA"); 

   wowID    = GetTargetProcessIdFromProcname(PROC_NAME); 
   hProcess = OpenProcess((PROCESS_VM_WRITE | PROCESS_VM_OPERATION), false, wowID); 

dllString = VirtualAllocEx(hProcess, 
                           NULL, 
                           (strlen(DLL_NAME) + 1), 
                           MEM_COMMIT, 
                           PAGE_READWRITE
                          ); 
stub      = VirtualAllocEx(hProcess, 
                           NULL, 
                           stubLen, 
                           MEM_COMMIT, 
                           PAGE_EXECUTE_READWRITE
                          ); 
   WriteProcessMemory(hProcess, dllString, DLL_NAME, strlen(DLL_NAME), NULL); 
    
   threadID = GetTargetThreadIdFromProcname(PROC_NAME); 
   hThread   = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME), 
                        false, 
                        threadID
                       );
   SuspendThread(hThread); 

   ctx.ContextFlags = CONTEXT_CONTROL; 
   GetThreadContext(hThread, &ctx); 
   oldIP   = ctx.Eip; 
   ctx.Eip = (DWORD)stub; 
   ctx.ContextFlags = CONTEXT_CONTROL; 

   VirtualProtect(loadDll, stubLen, PAGE_EXECUTE_READWRITE, &oldprot); 
   memcpy((void *)((unsigned long)loadDll + 1), &oldIP, 4); 
   memcpy((void *)((unsigned long)loadDll + 8), &dllString, 4); 
   memcpy((void *)((unsigned long)loadDll + 13), &loadLibAddy, 4); 

    WriteProcessMemory(hProcess, stub, loadDll, stubLen, NULL); 
   SetThreadContext(hThread, &ctx); 

   ResumeThread(hThread); 

   Sleep(8000); 

   VirtualFreeEx(hProcess, dllString, strlen(DLL_NAME), MEM_DECOMMIT); 
   VirtualFreeEx(hProcess, stub, stubLen, MEM_DECOMMIT); 
   CloseHandle(hProcess); 
   CloseHandle(hThread); 

    return 0; 
} 


unsigned long GetTargetProcessIdFromProcname(char *procName) 
{ 
   PROCESSENTRY32 pe; 
   HANDLE thSnapshot; 
   BOOL retval, ProcFound = false; 

   thSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 

   if(thSnapshot == INVALID_HANDLE_VALUE) 
   { 
      MessageBox(NULL, 
                 "Error: unable to create toolhelp snapshot", 
                 "Loader", 
                 NULL
                ); 
      return false; 
   } 

   pe.dwSize = sizeof(PROCESSENTRY32); 

    retval = Process32First(thSnapshot, &pe); 

   while(retval) 
   { 
      if(StrStrI(pe.szExeFile, procName) ) 
      { 
         ProcFound = true; 
         break; 
      } 

      retval    = Process32Next(thSnapshot,&pe); 
      pe.dwSize = sizeof(PROCESSENTRY32); 
   } 

   CloseHandle(thSnapshot); 
   return pe.th32ProcessID; 
} 

unsigned long GetTargetThreadIdFromProcname(char *procName) 
{ 
   PROCESSENTRY32 pe; 
   HANDLE thSnapshot, hProcess; 
   BOOL retval, ProcFound = false; 
   unsigned long pTID, threadID; 

   thSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 

   if(thSnapshot == INVALID_HANDLE_VALUE) 
   { 
      MessageBox(NULL, 
                 "Error: unable to create toolhelp snapshot", 
                 "Loader", 
                 NULL
                ); 
      return false; 
   } 

   pe.dwSize = sizeof(PROCESSENTRY32); 

    retval = Process32First(thSnapshot, &pe); 

   while(retval) 
   { 
      if(StrStrI(pe.szExeFile, procName) ) 
      { 
         ProcFound = true; 
         break; 
      } 

      retval    = Process32Next(thSnapshot,&pe); 
      pe.dwSize = sizeof(PROCESSENTRY32); 
   } 

   CloseHandle(thSnapshot); 
    
   _asm { 
      mov eax, fs:[0x18] 
      add eax, 36 
      mov [pTID], eax 
   } 

   hProcess = OpenProcess(PROCESS_VM_READ, false, pe.th32ProcessID); 
   ReadProcessMemory(hProcess, (const void *)pTID, &threadID, 4, NULL); 
   CloseHandle(hProcess); 

   return threadID; 
}