PostC++ Code Caving (Injecting a function into another process) | video tutorial

Posts 1–15 of 20 · Page 1 of 2
C++ Code Caving (Injecting a function into another process) | video tutorial
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;
}
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.
Quote Originally Posted by InunoTaishou View Post
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.
Thanks! Feels good to hear I've not just been wasting my time to write useless code (y) !
That's actually my youtube channel, if you're interested in malware, reverse-engineering or general "hacking (lol)"
please go check it out! There is more to come!
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;
}
Code:
WriteProcessMemory(hProcess, pRemoteThread, (LPVOID)RemoteThread, sizeof(cavedata), 0);
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" ?
If i'm not mistaken, you're assuming the size of the compiled function "RemoteThread" is <= sizeof( CodeCave ) structure?
Quote Originally Posted by Hitokiri~ View Post
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;
}
Code:
WriteProcessMemory(hProcess, pRemoteThread, (LPVOID)RemoteThread, sizeof(cavedata), 0);
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" ?
If i'm not mistaken, you're assuming the size of the compiled function "RemoteThread" is <= sizeof( CodeCave ) structure?
Yes, that's what I am assuming.
Quote Originally Posted by ZER0MEM0RY View Post
Yes, that's what I am assuming.
Code:
size_t len = 0;
char* code = (char*)RemoteThread;
while( *code++ != 0xc3 ) len++;
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.

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
Quote Originally Posted by Hitokiri~ View Post

Code:
size_t len = 0;
char* code = (char*)RemoteThread;
while( *code++ != 0xc3 ) len++;
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.

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
Won't actually do the trick (atleast for x64 intel & 64bit windows 7)
0xC3 is the equivalent of ret, right? Yes, but some functions could have multiple ret instructions in them, so you would only get a part of the size of a function.
Just crashes the injector process.
Quote Originally Posted by ZER0MEM0RY View Post
Won't actually do the trick (atleast for x64 intel & 64bit windows 7)
0xC3 is the equivalent of ret, right? Yes, but some functions could have multiple ret instructions in them, so you would only get a part of the size of a function.
Just crashes the injector process.
I
Said
For
A
Simple
Process
Such
As
This.
Not
A
Complex
Function.
Quote Originally Posted by ZER0MEM0RY View Post
Yes, that's what I am assuming.

You can also use this trick when calculating size of functions:

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;
Because these functions are defined consecutively, you can get the difference from the two function pointers and see the size of the first function.
Few people share their knowledge as you , thank you friend
Quote Originally Posted by R3DDOT View Post
these functions are defined consecutively
It doesn't always especially with optimization.
Or you could use a disassembly framework to implement your own GetFunctionSize.
Quote Originally Posted by ~BlackMaster~ View Post
Or you could use a disassembly framework to implement your own GetFunctionSize.
Personally I use a method that works because of how functions are aligned against each other.

Just use some sort of length disassembler and check if opcode length == 1 and opcode byte = 0xCC ( padding byte )
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" );
}
Posts 1–15 of 20 · Page 1 of 2

Post a Reply

Similar Threads

Tags for this Thread

Talk with us