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:
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 stubCó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 } }
- 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.
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: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);
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:unsigned long threadID; HANDLE hThread; threadID = GetTargetThreadIdFromProcname(PROC_NAME); hThread = OpenThread((THREAD_GET_CONTEXT | THREAD_SET_CONTEXT | THREAD_SUSPEND_RESUME), false, threadID );
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: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);
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; }
Dll Injection Using Code Caves, by: Darawk
最新推荐文章于 2024-09-03 07:39:12 发布