View Single Post
Fake bot ping (SV_CalcPing() function injecting)
Old
  (#1)
Immortal_BLG
Member
 
Status: Offline
Posts: 171
Join Date: Nov 2007
Location: Russian Federation
Default Fake bot ping (SV_CalcPing() function injecting) - 05-02-2011

Code:
const unsigned char Pointer32Size (sizeof (void *));
const unsigned char WildCard (0x2A);	// Used for signature scanning.
const unsigned char Push32 (0x68);	// push '....' (encoding is <imm32>)
const unsigned char JumpImmediately32 (0xE9);	// jmp '....' (encoding is imm32)
const unsigned char JumpImmediately32Size (sizeof (JumpImmediately32) + Pointer32Size);	// size of the jump-to instruction

struct ModuleInformation_t
{
	void         *baseAddress;
	unsigned int  memorySize;

	inline ModuleInformation_t (void) :
		baseAddress (NULL),
		memorySize (0u)
	{ /* VOID */ }
};
inline const bool GetModuleInformation (const void *const libraryPointer, ModuleInformation_t &dynamicLibraryInformation)
{
	// Get base address of the module (dynamicLibraryInformation.baseAddress) and get its ending offset (dynamicLibraryInformation.memorySize)

	// First - reset dynamic library information structure....
	dynamicLibraryInformation.baseAddress = NULL;
	dynamicLibraryInformation.memorySize = 0u;

	// Reliability check.
	if (libraryPointer == NULL)	// GetModuleInformation() failed!
		return false;

	MEMORY_BASIC_INFORMATION info;

	// Reliability check.
	if (VirtualQuery (libraryPointer, &info, sizeof (info)) == 0)
		return false;

	/// @note THIS CHECK IS ADDED FROM CSigMngr.cpp
	if (info.AllocationBase == NULL)
		return false;

	// All this is for our insane sanity checks :O
	const IMAGE_DOS_HEADER *const dos (static_cast <IMAGE_DOS_HEADER *> (info.AllocationBase));
	const IMAGE_NT_HEADERS *const pe (reinterpret_cast <IMAGE_NT_HEADERS *> (reinterpret_cast <unsigned int> (info.AllocationBase) + dos->e_lfanew));

	// Check PE magic and signature
	/// @note IsBadReadPtr() CHECKS ARE ADDED FROM CSigMngr.cpp
	if
	(
		IsBadReadPtr (dos, sizeof (IMAGE_DOS_HEADER)) ||
		dos->e_magic != IMAGE_DOS_SIGNATURE           ||
		IsBadReadPtr (pe, sizeof (IMAGE_NT_HEADERS))  ||	// pe points to a bad location?
		pe->Signature != IMAGE_NT_SIGNATURE           ||
		pe->OptionalHeader.Magic != IMAGE_NT_OPTIONAL_HDR32_MAGIC
	)
		return false;

	/* Check architecture, which is 32-bit/x86 right now
	* Should change this for 64-bit if Valve gets their act together
	*/
	if (pe->FileHeader.Machine != IMAGE_FILE_MACHINE_I386)
		return false;

	// Finally, we can do this
	dynamicLibraryInformation.baseAddress = info.AllocationBase;
	dynamicLibraryInformation.memorySize = pe->OptionalHeader.SizeOfImage;

	return true;
}
inline const bool CompareMemoryWithoutWildCard (const unsigned char *const startPointer, const unsigned char *const pattern, const unsigned char length)
{
	// Compare each byte, except wildcards....
	for (unsigned char index (0u); startPointer[index] == pattern[index]; /* Empty */)
		if ((index += sizeof (unsigned char)) == length)	// If 'index' reached the end, we know we have a match!
			return true;	// Found a match.

	return false;
}
inline void *const FindPatternWithoutWildCard (void *startPointer, const unsigned int searchLength, const unsigned char *const pattern, const unsigned char length, const unsigned char stepSize = sizeof (unsigned char))
{
	// Scan for the signature in memory then return the starting position's address.

	// Reliability checks.
	if (startPointer == NULL || searchLength < length || length < 2u)
		return NULL;

	const unsigned char *const endPointer (static_cast <unsigned char *> (startPointer) + searchLength);	// prevent a crash maybe?

	do
	{
		// Compare each byte, except wildcards....
		if (CompareMemoryWithoutWildCard (static_cast <unsigned char *> (startPointer), pattern, length))
			return startPointer;	// Found a match.
	} while ((reinterpret_cast <unsigned char *&> (startPointer) += stepSize/* search memory in an aligned manner, or not */) + length <= endPointer);	// prevent a crash maybe?

	return NULL;
}
inline const bool CompareMemory (const unsigned char *const startPointer, const unsigned char *const pattern, const unsigned char length, const unsigned char wildCard = WildCard)
{
	// Compare each byte, except wildcards....
	for (unsigned char index (0u); pattern[index] == wildCard || startPointer[index] == pattern[index]; /* Empty */)
		if ((index += sizeof (unsigned char)) == length)	// If 'index' reached the end, we know we have a match!
			return true;	// Found a match.

	return false;
}
/**
* @brief Searches for a pattern of bytes within the memory of a dynamic library.
*
* @param startPointer:	Pointer to memory to search for.
* @param searchLength:	Length of sequence of memory to search for.
* @param pattern:		Pattern of bytes to search for. 0x2A can be used as a wildcard.
* @param length:		Size of the pattern in bytes.
* @return				Pointer to pattern found in memory, NULL if not found.
*/
inline void *const FindPattern (void *startPointer, const unsigned int searchLength, const unsigned char *const pattern, const unsigned char length, const unsigned char stepSize = sizeof (unsigned char), const unsigned char wildCard = WildCard)
{
	// Scan for the signature in memory then return the starting position's address.

	// Reliability checks.
	if (startPointer == NULL || searchLength < length || length < 2u)
		return NULL;

	const unsigned char *const endPointer (static_cast <unsigned char *> (startPointer) + searchLength);	// prevent a crash maybe?

	do
	{
		// Compare each byte, except wildcards....
		if (CompareMemory (static_cast <unsigned char *> (startPointer), pattern, length, wildCard))
			return startPointer;	// Found a match.
	} while ((reinterpret_cast <unsigned char *&> (startPointer) += stepSize/* search memory in an aligned manner, or not */) + length <= endPointer);	// prevent a crash maybe?

	return NULL;
}
inline void *const FindMemoryChunkReference32 (void *const startPointer, const unsigned int searchLength, const void *const address, const unsigned char instruction)
{
	const union /* Unnamed */
	{
		const void    *pointer;
		unsigned char  byte[Pointer32Size];
	} pointerToByte = {address/*, Do not initialize me! */};
	const unsigned char pattern[sizeof (instruction) + Pointer32Size] =
	{
		instruction, pointerToByte.byte[0u], pointerToByte.byte[1u], pointerToByte.byte[2u], pointerToByte.byte[3u]	// instruction 'address'
	};

	return FindPattern (startPointer, searchLength, pattern, sizeof (pattern));
}
inline void *const GetRealAddressOfRelativeAddress32 (const void *const relativeAddress)
{
	// Reliability check.
	if (relativeAddress == NULL)
		return NULL;

	return reinterpret_cast <void *> (static_cast <const unsigned int *const> (relativeAddress)[0u] + reinterpret_cast <const unsigned int> (relativeAddress) + Pointer32Size);
}

struct Client_t;
typedef const unsigned int (*SV_CalcPingFunction_t) (const Client_t *const client);

inline SV_CalcPingFunction_t GetSV_CalcPingFunction (void)
{
	ModuleInformation_t engineModuleInformation;

	if (!GetModuleInformation (gEngfuncs.pfnGetPlayerUserId, engineModuleInformation))
		return NULL;

	const unsigned char string[] = {"%4i %s\n"};	// From Host_Ping_f() function.
	unsigned char *address (static_cast <unsigned char *> (FindPatternWithoutWildCard (engineModuleInformation.baseAddress, engineModuleInformation.memorySize, string, sizeof (string) - sizeof ('\0'))));

	if (address == NULL)
		return NULL;

	address = static_cast <unsigned char *> (FindMemoryChunkReference32 (engineModuleInformation.baseAddress, engineModuleInformation.memorySize, address, Push32));

	if (address == NULL)
		return NULL;

	address -= sizeof ("\x4D\x93\x04\x00\x83\xC4\x04\x50") - sizeof ('\0');

	return static_cast <SV_CalcPingFunction_t> (GetRealAddressOfRelativeAddress32 (address));
}

inline const unsigned int SV_CalcPing_IfFakeClient (const Client_t *const fakeClient)
{
	edict_t *const fakeClientEdict (reinterpret_cast <edict_t **> (fakeClient)[4839u/* For 4554 build! */]);

	gEngfuncs.pfnServerPrint ("SV_CalcPing_IfFakeClient(%s): called!", STRING (fakeClientEdict->v.netname));

	return 999u;	// YOUR CODE HERE!!!!
}

#define CALL_SV_CalcPing_IfFakeClient_FUNCTION								\
	{ push eax }						/* just for safety */				\
	{ push ecx }						/* push the fake client pointer */	\
	{ call SV_CalcPing_IfFakeClient }	/* call our function */				\
	{ add  esp, 8 }						/* correct stack */					\

inline const unsigned int __declspec (naked) SV_CalcPing_IfFakeClient_Gate_WithEpilogueOfListenServer (const Client_t *const fakeClient)
{
	__asm
	{
		CALL_SV_CalcPing_IfFakeClient_FUNCTION;

		pop  ebp;
		pop  ecx;

		retn;	// Return Near from Procedure
	}
}
inline const unsigned int __declspec (naked) SV_CalcPing_IfFakeClient_Gate_WithEpilogueOfDedicatedServer (const Client_t *const fakeClient)
{
	/// @todo THIS FUNCTION WORKS FINE, BUT I'M NOT SHURE....

	__asm
	{
		CALL_SV_CalcPing_IfFakeClient_FUNCTION;

		pop  esi;
		mov  esp, ebp;
		pop  ebp;

		retn;	// Return Near from Procedure
	}
}
inline void InjectInSV_CalcPingFunction (void)
{
	SV_CalcPingFunction_t SV_CalcPingFunction (GetSV_CalcPingFunction ());

	if (SV_CalcPingFunction == NULL)
	{
		gEngfuncs.pfnServerPrint ("InjectInSV_CalcPingFunction(): Can't find target function!");

		return;
	}

	const unsigned char signatureIfFakeClient[] = {"\x39\x2A\x48\x25\x00\x00"};	// cmp [ecx+2548h], (ebp/esi)	// Compare Two Operands (2548h - offset to Client_t::fakeclient member)
	unsigned char *addressIfFakeClient (static_cast <unsigned char *> (FindPattern (SV_CalcPingFunction, sizeof ("\x55\x8B\xEC\x51\x8B\x4D\x08\x56\x33\xF6\x39\xB1\x48\x25\x00\x00\x74\x07\x33\xC0\x5E\x8B\xE5\x5D\xC3") - sizeof ('\0')/* Signature from swds.dll (longer than in hw.dll) */, signatureIfFakeClient, sizeof (signatureIfFakeClient) - sizeof ('\0'))));

	// Reliability check.
	if (addressIfFakeClient == NULL)
		return;

	// Skip "cmp [ecx+2548h], (ebp/esi)" and "jz short loc_1D9B089" instructions....
	addressIfFakeClient += sizeof (signatureIfFakeClient) - sizeof ('\0') + sizeof ("\x74\x07")/* jz short loc_1D9B089 */ - sizeof ('\0');

	// Reliability checks.
	if (addressIfFakeClient[0u] != 0x33 || addressIfFakeClient[1u] != 0xC0)	// xor eax, eax	// Logical Exclusive OR
		return;

	unsigned long dummyOldProtectFlags;

	if (!VirtualProtect (addressIfFakeClient, JumpImmediately32Size, PAGE_EXECUTE_READWRITE, &dummyOldProtectFlags))
	{
		gEngfuncs.pfnServerPrint ("InjectInSV_CalcPingFunction(): VirtualProtect() filed!");

		return;
	}

	addressIfFakeClient[0u] = JumpImmediately32;

	reinterpret_cast <unsigned int *> (addressIfFakeClient + sizeof (JumpImmediately32))[0u] = reinterpret_cast <unsigned char *> (gEngfuncs.pfnIsDedicatedServer () ? SV_CalcPing_IfFakeClient_Gate_WithEpilogueOfDedicatedServer : SV_CalcPing_IfFakeClient_Gate_WithEpilogueOfListenServer) - addressIfFakeClient - JumpImmediately32Size;

	gEngfuncs.pfnServerPrint ("InjectInSV_CalcPingFunction(): Complete!");
}
It works fine for me!

P.S. 1 SORRY FOR BAD ENGLISH!
P.S. 2 With this method you can see fake ping for example in 'status' command
P.S. 3 I'm not good in ASM!!!
P.S. 4 If you need full Client_t structure I can post it. (But with changed names of structures)

Last edited by Immortal_BLG; 05-02-2011 at 13:44..
  
Reply With Quote