Anton Troskie (10-28-2016),Cosmo_ (07-02-2015),InunoTaishou (07-01-2015),RoPMadM (07-07-2015)
Code Caving is basically executing YOUR code in the address space of another process.
Firstly, you will need to allocate for some space in the remote target process (VirtualAllocEx), after that
write your function to the allocated space in the process' heap (WriteProcessMemory).
After that, creating a new thread to the process and placing the function in it's stack
(CreateRemoteThread) Lastly, free the allocated space used to execute your code
(VirtualFree)
Video Tutorial:
source code:
Code:/* NOTE!!! 32bit process can't interfere with 64bit process or vice versa! */ /* You must be using the same architecture as your target process uses!*/ #define _CRT_SECURE_NO_WARNINGS //let's us use "unsafe" functions in the crt #include <iostream> #include <Windows.h> #include <TlHelp32.h> typedef int(__stdcall *__MessageBoxA)(HWND, LPCSTR, LPCSTR, UINT); class cavedata // store all of our remote data to one object { public: char chMessage[256]; //here we store it's message! char chTitle[256]; //here we will store our messagebox's title DWORD paMessageBoxA; //pa = Procedure address in memory }; DWORD GetProcId(char* procname) { PROCESSENTRY32 pe; HANDLE hSnap; pe.dwSize = sizeof(PROCESSENTRY32); hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); if (Process32First(hSnap, &pe)) { do { if (strcmp(pe.szExeFile, procname) == 0) break; } while (Process32Next(hSnap, &pe)); } return pe.th32ProcessID; } DWORD __stdcall RemoteThread(cavedata *cData) //thread that will be spawned in our target process { __MessageBoxA MsgBox = (__MessageBoxA)cData->paMessageBoxA; //initialize our function MsgBox(NULL, cData->chMessage, cData->chTitle, MB_ICONINFORMATION); //call it return EXIT_SUCCESS; } int main() { cavedata CaveData; //declare cave data object ZeroMemory(&CaveData, sizeof(cavedata)); // fill it with zeros strcpy(CaveData.chMessage, "Hi, I was called from remote process!"); //set the variables inside it strcpy(CaveData.chTitle, "Hello from code cave!"); HINSTANCE hUserModule = LoadLibrary("user32.dll"); //load the user32.dll CaveData.paMessageBoxA = (DWORD)GetProcAddress(hUserModule, "MessageBoxA"); //find MessageBox procedure from it // and pass it to paMessageBox. FreeLibrary(hUserModule); //open our target process for writing HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcId("calc.exe")); //allocate memory for our procedure in the remote process' address space LPVOID pRemoteThread = VirtualAllocEx(hProcess, NULL, sizeof(cavedata), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); //write our thread to the target process WriteProcessMemory(hProcess, pRemoteThread, (LPVOID)RemoteThread, sizeof(cavedata), 0); //allocate memory for our cavedata object in the remote process cavedata *pData = (cavedata*)VirtualAllocEx(hProcess, NULL, sizeof(cavedata), MEM_COMMIT, PAGE_READWRITE); //Write it to the target process WriteProcessMemory(hProcess, pData, &CaveData, sizeof(cavedata), NULL); //spawn/create our procedure/thread in the remote process HANDLE hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)pRemoteThread, pData, 0, 0); //close thread handle CloseHandle(hThread); //free the now un-used memory from the remote process' heap VirtualFreeEx(hProcess, pRemoteThread, sizeof(cavedata), MEM_RELEASE); //close process' handle CloseHandle(hProcess); /*...*/ getchar(); return 0; }
Anton Troskie (10-28-2016),Cosmo_ (07-02-2015),InunoTaishou (07-01-2015),RoPMadM (07-07-2015)
Pretty good, I liked it! Contributing to the C/C++ community.
And we write our scopes the same way. I thought I was one of the few that started the curly brace on the same line for statements but curly brace on the next line for everything else.
One thing I don't quite understand:
Code:DWORD __stdcall RemoteThread(cavedata *cData) //thread that will be spawned in our target process { __MessageBoxA MsgBox = (__MessageBoxA)cData->paMessageBoxA; //initialize our function MsgBox(NULL, cData->chMessage, cData->chTitle, MB_ICONINFORMATION); //call it return EXIT_SUCCESS; }Basically, you're writing the pointer to the function defined locally to the remote process, ok but how are you determining the size of the function "RemoteThread" ?Code:WriteProcessMemory(hProcess, pRemoteThread, (LPVOID)RemoteThread, sizeof(cavedata), 0);
If i'm not mistaken, you're assuming the size of the compiled function "RemoteThread" is <= sizeof( CodeCave ) structure?
No response @ZER0MEM0RY ?
Since it's a simple process such as that with no conditions, you can do that instead to determine the size. I say this because the procedure might be located at the end of the page and if you happen to exceed the page ( Which might sometimes happen when reading a large block using RPM ) and stumble across another page boundary without the necessary read access, you'll crash. That way is at least far more reliable.Code:size_t len = 0; char* code = (char*)RemoteThread; while( *code++ != 0xc3 ) len++;
Optionally, you can inject pure assembly.
Code:push 40h ; MB_ICONINFORMATION lea eax, [ esp + 4 ] ; title push eax lea eax, [ esp + 264 ] ; msg push eax mov eax, [ esp + 524 ] ; paMessageBoxA call eax mov eax, 0 retn
Last edited by Hitokiri~; 07-05-2015 at 07:32 AM.
Few people share their knowledge as you , thank you friend![]()
You can also use this trick when calculating size of functions:
Because these functions are defined consecutively, you can get the difference from the two function pointers and see the size of the first function.Code:DWORD __stdcall RemoteThread(cavedata *cData) //thread that will be spawned in our target process { __MessageBoxA MsgBox = (__MessageBoxA)cData->paMessageBoxA; //initialize our function MsgBox(NULL, cData->chMessage, cData->chTitle, MB_ICONINFORMATION); //call it return EXIT_SUCCESS; } void __stdcall RemoteThread_end(){} Calculating the size: sizeof(RemoteThread) = (DWORD_PTR)RemoteThread_end - (DWORD_PTR)RemoteThread;
Or you could use a disassembly framework to implement your own GetFunctionSize.
I'd much rather do syscalls directly instead of calling wrappers which are most of the time hooked by Anti-Cheats. If you look at the exported Nt_____ functions on ntdll.dll it should be pretty obvious what you should do.
Example: Instead of calling VirtualAlloc(Ex) you can do this and call it instead:
Code:__declspec( naked ) NTSTATUS NtAllocateVirtualMemory( HANDLE ProcessHandle, PVOID *BaseAddress, ULONG ZeroBits, PULONG AllocationSize, ULONG AllocationType, ULONG Protect ) { __asm { MOV EAX, 0x17; CALL fs : [0xC0]; RETN 0x18; } } //somewhere... if( NT_ERROR( NtAllocateVirtualMemory( GetCurrentProcess(), &m_pBuffer, NULL, &dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE ) ) ) { throw Exceptions::MemoryAllocationException( "Unable to alocate memory for the image" ); }
Last edited by MarkHC; 07-19-2015 at 09:35 PM.
CoD Minion from 09/19/2012 to 01/10/2013