🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

Injecting entry point in current process

Started by
3 comments, last by irreversible 4 years, 4 months ago

What: I want to hijack the current process' startup procedure to run pre-initialization startup code, which would allow my framework backend to function without any setup by the programmer.

Why: I want to initialize my application shell backend (including any managers) before the program enters but after statics are initialized.

What's the problem: Well, there's two….

1) Identifying the actual entry point. I'm currently focusing solely on Windows, but even here I have multiple possible entry points to handle. Most importantly, WinMainA() and WinMainW(), but also main(), wmain(), etc. I'm not a sure what would be the best way to locate and handle the entry point.

2) The worse thing is that I get sporadic crashes. I'm not 100% sure why, but these don't happen in some cases but do happen when the code is changed.

Here's the code I'm using to inject and restore WinMainW. I do not know what the Assembly instructions do. These are from the internet:

#ifdef DEF64
//define the jump for 64 bit
uint8_t jmpTrap[] = { 0x48, 0xb8, 0x78, 0x56, 0x34, 0x12, 0x78, 0x56, 0x34, 0x12, 0xff, 0xe0 }; //mov rax,64-bit; jmp raz
#else
uint8_t jmpTrap[] = { 0xB8, 0, 0, 0, 0, 0xff, 0xe0 }; //mov eax,32-bit; jmp eax
#endif


void								Inject() {
					DWORD dPermission				= 0;
					VirtualProtect((LPVOID)oldFunction, sizeof(restore), PAGE_EXECUTE_READWRITE, &dPermission);
					UINT64 i						= (UINT64)newFunction;

					if(sizeof(void*) == 4)			{ memcpy(&jmpTrap[1], &i, sizeof(void*)); }
					else if(sizeof(void*) == 8)		{ memcpy(&jmpTrap[2], &i, sizeof(void*)); }

					// store the original so as to restore it later
					memcpy(restore, oldFunction, sizeof(restore));
					memcpy((LPVOID)oldFunction, jmpTrap, sizeof(jmpTrap));
				}

				void								Restore() {
					DWORD dPermission				= 0;
					VirtualProtect((LPVOID)oldFunction, sizeof(jmpTrap), PAGE_EXECUTE_READWRITE, &dPermission);
					memcpy((LPVOID)oldFunction, restore, sizeof(restore));
				}

Here's how I use the above code.

int WINAPI											wInjectedWinMain(
	_In HINSTANCE									hInstance,
	_In HINSTANCE									hPrevInstance,
	_In LPWSTR										lpCmdLine,
	_In int											nCmdShow)
{
	sys::startup::OnMain();
	injWinMainW.Restore();
	return											wWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}

I guess my question is: if the code is correct, what am I doing wrong. Or if there's something amiss, what might it be? ?

Advertisement

At link time, this may work: https://docs.microsoft.com/en-us/cpp/build/reference/entry-entry-point-symbol

At compile time, it'd be better to just have your users call your function at the top of their main/WinMain/etc method.

What's wrong with your code could be simply a problem in the injector function. I did some research on this topic for an anti-cheat/ copy-protection scenario what you should really read is the post about MinHook. The author is writing about the instruction sizes that may occur in Windows functions and how to handle them properly.

Also what @nypyren says, you shouldn't do such things and provide some kind of macro instead your users can define to tell your code what they want you to do. I did something similar because I had to setup the default allocator before main and cleanup anything after. I got inspired from Urho3D and their macro

Much appreciated. I'll have a closer look at MinHook, although I also noticed that the mentioned 10k cost of Detours no longer applies and the library seems to have been open sourced under MIT.

To be honest, calling the backend startup routine from the top of the user entry point isn't a big deal. But it IS cleaner this way.

This topic is closed to new replies.

Advertisement