Gateway to your function

This tutorial is a few years old. Please don’t ask for any source code because I don’t have any as I don’t do this kind of stuff anymore.

What is this tutorial about?

In this tutorial I want to show you how to modify another application to call a function in your injected DLL. I use the game Ragnarok Online as an example. I want a function in my DLL to be called when I open a shop in the game. In my last tutorial you can see how I found the piece of code that is executed when a shop is opened.

The method

This is how it will work when we are finished:

  1. At the address that you specify we jump to our code.
  2. There we will call our “event” function.
  3. Then we will execute the original code that we had to replace for the jump to our code…
  4. …and finally jump back.

Here is a simple flash that illustrates the method I just described.

Now these are the steps I follow to code it.

  1. Allocate memory for the gateway.
  2. Write a call to your function into the allocated space.
  3. Copy the piece of code where you want to create the link to your gateway into the allocated memory.
  4. Write a jump back to the address after the link.
  5. Create the link to the gateway.

This was the short version, there is a lot you have to take care of as it is very critical to modify memory. One wrong command or wrong address will crash the application.

The code

I will give you my code for the gateway right away and explain it then.

The code is pretty easy but it is very important that you really get how the whole thing works.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
void SetJumpgate (BYTE *targetAdd, BYTE *funcAdd, const int len)
{
	BYTE *myAdd = (BYTE*)malloc(len+10);
 
	DWORD origProt;
	VirtualProtect(targetAdd, len, PAGE_READWRITE, &origProt);
 
	myAdd[0] = 0xE8;
	*(DWORD*)(myAdd + 1) = (DWORD)(funcAdd - myAdd) - 5;
	myAdd += 5;
 
	memcpy(myAdd, targetAdd, len);
	myAdd += len;
 
	myAdd[0] = 0xE9;
	*(DWORD*)(myAdd + 1) = (DWORD)(targetAdd + len - myAdd) - 5;
 
	targetAdd[0] = 0xE9;
	*(DWORD*)(targetAdd + 1) = (DWORD)(myAdd - 5 - len - targetAdd) - 5;
	targetAdd += 5;
 
	if (len > 5)
		for (int i = 0; i < len - 5; i++)
			targetAdd[i] = 0x90;
 
	VirtualProtect(targetAdd, len, origProt, &origProt);
}

Explanation

BYTE *myAdd = (BYTE*)malloc(len + 10);

Here we allocate memory for the gateway (see step 1).

Remember: we want to write a call to our function (length 5), the code we are going to replace (length len) and a jump back (length 5) into the memory. Thus the length has to be 10 for the calls + len.

DWORD origProt;
VirtualProtect(targetAdd, len, PAGE_READWRITE, &origProt);

Now we are preparing to read/write at our target address. We save the original protection in origProt to restore it later.

myAdd[0] = 0xE8;
*(DWORD*)(myAdd + 1) = (DWORD)(funcAdd - myAdd) - 5;
myAdd += 5;

This is the call to our function (see step 2). The first byte 0xE8 is the opcode for a CALL command. The four following bytes are the offset to our function. Finally we jump 5 bytes forward so we won´t overwrite what we have just written.

memcpy(myAdd, targetAdd, len);
myAdd += len;

Here we copy the original code that we are going to replace into our allocated memory after the call (see step 3).

myAdd[0] = 0xE9;
*(DWORD*)(myAdd + 1) = (DWORD)(targetAdd + len - myAdd) - 5;

And now the jump back to our target address (see step 4). This is similar to the CALL we have already written with the difference of the first byte being 0xE9 this time which is the opcode for a JMP.

targetAdd[0] = 0xE9;
*(DWORD*)(targetAdd + 1) = (DWORD)(myAdd - 5 - len - targetAdd) - 5;
targetAdd += 5;

This is the jump to our allocated memory/gateway (see step 5).

if (len > 5)
	for (int i = 0; i < len - 5; i++)
		targetAdd[i] = 0x90;
 
VirtualProtect(targetAdd, len, origProt, &origProt);

The jump to our gateway has the length of 5 bytes. If the original code at the target address is longer than 5 byte our jump won´t replace the whole code and some garbage is left over. So we have to fill it with 0x90 which is the opcode for NOP (no operation). If we don’t do this, the program might crash.
Finally we restore the protection.

Usage

Please note that the code you want to replace must have at least the size of 5 bytes!

You can call the function like this:

SetJumpgate((BYTE*)0x004A42C2, (BYTE*)OnOpenShop, 6);

This is how my OnOpenShop looks like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void OnOpenShop (void)
{
	__asm
	{
		pushfd
		pushad
	}
 
	AddChat("Opening a shop eh?");
 
	__asm
	{
		popad
		popfd
	}
}

You need the asm there because you would change the stack of the game if you would not have it there and that would lead to a crash.

And this is my result:

Leave a Reply