Forum > Coding >

Fixing relative addresses when copying functions at runtime

Posts: 224

Threads: 43

Joined: May, 2022

Reputation: -5

Posted

Let's say you want to copy a function at runtime, you may think of just copy and pasting the function by looping through it, finding the end, and then just allocating memory for it and boom std::memcpy comes to make it easy for you.

 

Well, this would be a good idea, if functions used absolute address calls every time they want to call a function, which isn't the case.

Most CALLs and JMPs you see, take relative addresses as operands. That means there's a calculation that occurs in order to make the JMP / CALL which binds it to be relative to the current location in memory of the IP ( instruction pointer ).

 

Today I'll be going over that calculation and I'll be showing you how to properly copy a function.

 

The CALL / JMP formula goes as following: target absolute address - address of the instruction after the relative CALL / JMP instruction.

 

Seems simple right? That's because it is.

Now, you may see why this becomes an issue when copy and pasting functions, that same relative address is never changed, which means when we try to call a copied function that had relative JMPs or CALLs in it ( which is quite often, I'd say around 95-99% of the time lol )

without fixing the relative addresses, we're guaranteed to crash.

 

Here's my implementation of how to recalculate and fix relative addresses when copying functions with commented code to further explain:

#include <iostream>
#include <Windows.h>
#include <thread>
#include <vector>
#include <array>
#include <unordered_map>

std::vector< std::uint8_t > ret_function_bytes( void* address )
{
	auto byte = static_cast< std::uint8_t* >( address );
	std::vector< std::uint8_t > function_bytes{ };

	static const std::unordered_map< std::uint8_t, std::uint8_t > ret_bytes_map
	{
		{
			0xC2,
			0x03
		},
		{
			0xC3,
			0x01
		}
	};

	static const auto alignment_bytes = std::to_array< std::uint8_t >
	( 
		{ 0xCC, 0x90 } 
	);

	while( true )
	{
		function_bytes.push_back( *byte );

		for( const auto& ret_byte : ret_bytes_map )
		{
			const auto& [ opcode, opcode_sz ] = ret_byte;
			if( *byte == opcode )
			{
				for( const auto& alignment_byte : alignment_bytes )
				{
					if( *( byte + opcode_sz ) == alignment_byte )
						return function_bytes;
				}
			}
			
		}
		++byte;
	}
	
}

void* copy_function( void* function_address )
{
	// MSVC specific way of copying functions. not standardized.
	const auto function_instrs = ret_function_bytes( function_address );

	// throw exception in case virtualalloc fails
	const auto allocated_func_mem = VirtualAlloc( nullptr, function_instrs.size( ), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE );
	if( !allocated_func_mem )
		throw std::runtime_error( "failed to allocate memory for function" );
	
	// relative calls address re-calculation adjusted for the "new" memory location of the function.
	for( auto it = function_instrs.begin( ); it < function_instrs.end( ); ++it )
	{
		if( *it == 0xE9 || *it == 0xE8 )
		{
			// getting the offset of bytes from the beginning of the function 
			// so we can add it later to get the next instruction address
			// of both the original function and our new function.

			const auto dist = std::distance( function_instrs.begin( ), it );
			// old offset used by the old function to make a relative CALL or JMP  
			const auto o_rel_offset = *reinterpret_cast< std::uint32_t* >( it._Ptr + 1 );
			// the address of the instruction after the relative CALL instruction 
			const auto o_next_instr = reinterpret_cast< std::uint32_t >( function_address ) + dist + 5;
			// absolute address of the callee
			const auto callee_abs_address = o_next_instr + o_rel_offset;
			// same as before except it's the next instruction of the new function
			const auto next_instr = reinterpret_cast< std::uint32_t >( allocated_func_mem ) + dist + 5;
			// relative address calculation
			const auto rel_addr = callee_abs_address - next_instr;
			// setting the new relative address operand value
			*reinterpret_cast< std::uint32_t* >( it._Ptr + 1 ) = rel_addr;
		}
	}
	
	// copying the new function with the fixed relative addresses to our allocated memory for it
	std::memcpy( allocated_func_mem, function_instrs.data( ), function_instrs.size( ) );
	return allocated_func_mem;

}

void main_thread( HMODULE dll_module )
{
	const auto base = reinterpret_cast< std::uint32_t >( GetModuleHandleA( nullptr ) );
	constexpr auto function_rva = 0x1030;
	
	const auto new_func_address = copy_function( reinterpret_cast< void* >( base + function_rva ) );
	const auto new_func = static_cast< void( __cdecl * )( const char* ) >( new_func_address );
	std::printf( "%p\n", new_func );
	new_func( "exprssn is godly\n" );
	FreeLibrary( dll_module );
}


bool __stdcall DllMain( HMODULE dll_module, const std::uint32_t reason_for_call, void* )
{
	if( reason_for_call == DLL_PROCESS_ATTACH )
		std::thread{ main_thread, dll_module }.detach( );
	return true;
}

https://github.com/expressiongz/runtime-copy-function

  • 0

https://media.discordapp.net/attachments/1044764388546068510/1051935933836050482/Signature_4.png

Posts: 1590

Threads: 166

Joined: Apr, 2021

Reputation: 13

Replied

expression literally became the god of C++ in the forum :kekw:

  • 0

Random quote here...

Medusa

WeAreTruth

vip

Posts: 119

Threads: 11

Joined: Jul, 2022

Reputation: -26

Replied

I'm not reading this lmao but I'll comment so it gets boosted 

  • 0

https://media.discordapp.net/attachments/1064332722065117204/1067596913781784606/Frame_2_1.png

pepsi

PePsIDeveloper

vip

Posts: 259

Threads: 4

Joined: Apr, 2021

Reputation: 13

Replied

vouch my guy expression is turning into depression if u do not vouch..

  • 0

https://cdn.discordapp.com/attachments/661621789591470090/1013919752294498314/Untitled_1366_768_px_1546_202_px_2.gif

Posts: 1590

Threads: 166

Joined: Apr, 2021

Reputation: 13

Replied

@pepsi bro chill wdym

  • 0

Random quote here...

pepsi

PePsIDeveloper

vip

Posts: 259

Threads: 4

Joined: Apr, 2021

Reputation: 13

Replied

@VoidableMethod Bro what you said is absolutely cringe... Expression is  good at what he likes to do. But you cant call him a god over this ☠

  • 0

https://cdn.discordapp.com/attachments/661621789591470090/1013919752294498314/Untitled_1366_768_px_1546_202_px_2.gif

Posts: 1590

Threads: 166

Joined: Apr, 2021

Reputation: 13

Replied

@pepsi I'm just saying he's really good at it. I never said he should stop or is bad at it...

  • 0

Random quote here...

pepsi

PePsIDeveloper

vip

Posts: 259

Threads: 4

Joined: Apr, 2021

Reputation: 13

Replied

@VoidableMethod Go slide for von/

  • 0

https://cdn.discordapp.com/attachments/661621789591470090/1013919752294498314/Untitled_1366_768_px_1546_202_px_2.gif

Posts: 224

Threads: 43

Joined: May, 2022

Reputation: -5

Replied

@_realnickk

 

https://github.com/expressiongz/out-of-body-function-caller/blob/main/dllmain.cpp

It'd be something like this 

  • 0

https://media.discordapp.net/attachments/1044764388546068510/1051935933836050482/Signature_4.png

SeizureSalad

i love femboys

Posts: 1012

Threads: 73

Joined: Mar, 2021

Reputation: 37

Replied

@VoidableMethod and you became god of uh

 

uh

 

hm this one's a toughie

  • 0

"Questionable intellegence, but I like the mystery" - CubeFaces

https://cdn.discordapp.com/attachments/1136067487847415848/1138948596679589898/sig.png

Posts: 1590

Threads: 166

Joined: Apr, 2021

Reputation: 13

Replied

@SeizureSalad I am not a King i am not a god

I am a person lol

  • 0

Random quote here...

Users viewing this thread:

( Members: 0, Guests: 0, Total: 0 )