Tutorial: ‘My First Inject DLL’
In a bid to compete with xadet’s blog I have realised I must post something useful on here, so I have come up with the idea of this tutorial which is rather patronisingly title ‘My First Inject DLL’, how whimsical of me. Also this allows me to continue the TRose.exe name colour tutorial properly, which expect to come very soon (I can’t be bothered to wait for Ruff to come back up, I am just going to do it without running ROSE
)
In this tutorial we will make an EXE to inject our DLL into ROSE Online, or any other program, but of course this tutorial will focus on ROSE, we will also learn how to do hook code and how to call functions in the ROSE online exe, how exciting does that sound? Very.
So go ahead and create a new solution and add a win32 console application project, tick Empty Project so visual studio does not create worthless code that we do not want! Note that this first project will be for the EXE so name it something applicable, such as ‘My First Injector’!
Obviously the first thing to do with our new project is add a main.cpp file and put in the default code:
int main(int argc, char** argv){
return 0;
}
Brilliant start I would say, now we have to add #include
So here is a snippet showing you how to use it:
char* exename = "TRose.exe @TRIGGER_SOFT@ _server 127.0.0.1"
STARTUPINFOA si = { sizeof( si ) };
PROCESS_INFORMATION pi;
if(!CreateProcessA(NULL, exename, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi)){
printf("Failed to CreateProcess for trose.exe!\n");
return 0;
}
Once we have called CreateProcessA, if it succeeds, it will return the new process handle in ‘PROCESS_INFORMATION pi;’ struct, we can now use this handle for injecting our dll, I went ahead and put the actual injection code in a separate function, ill paste the code below and then explain it to you:
bool InjectDLL(HANDLE hProcess, const char* dll){
DWORD dwWritten;
LPVOID pStringInRemoteProcess;
if(!hProcess) return false;
pStringInRemoteProcess = VirtualAllocEx(hProcess, 0, strlen(dll)+1, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if(pStringInRemoteProcess == NULL) return false;
WriteProcessMemory(hProcess, pStringInRemoteProcess, dll, strlen(dll)+1, &dwWritten);
if(dwWritten != strlen(dll)+1) return false;
CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)GetProcAddress(GetModuleHandleA("kernel32.dll"), "LoadLibraryA"), pStringInRemoteProcess, 0, 0);
return true;
}
So yes this looks complicated, but it is all required! Basically, we use VirtualAllocEx so we can allocate memory in the remote process (TRose.exe) with which to write the dll name, as obviously we cannot call a function across processes with its parameters coming from our process! WriteProcessMemory then writes this dll name to the previously allocated memory. Now the main code, CreateRemoteThread, this does as named, it creates a new thread in the remote process of TRose.exe! This code automatically runs the function LoadLibraryA which is imported from kernel32.dll, and passes it the argument of the pointer to the previously allocated memory which contains our dll’s name!
That was complicated and it is not important that you understand it as the windows API is funtastic, the main point of this tutorial is to teach you about the actual inject dll, not the exe which injects it!
So my final code for the main procedure is:
int main(int argc, char** argv){
const char* dllname = "myfirstinject.dll";
char* exename = "TRose.exe @TRIGGER_SOFT@ _server 127.0.0.1";
STARTUPINFOA si = { sizeof( si ) };
PROCESS_INFORMATION pi;
if(!CreateProcessA(NULL, exename, NULL, NULL, FALSE, NULL, NULL, NULL, &si, &pi)){
printf("Failed to CreateProcess for trose.exe!\n");
return 0;
}
if(!InjectDLL(pi.hProcess, dllname)){
printf("Failed to inject the dll!\n");
return 0;
}
printf("Successfully injected the dll to TRose.exe!\n");
return 0;
}
You should customise that code to include your dll’s name and the IP of the server you will be connecting to!
Now we shall add a new project to the solution for the dll, set it as a win32 console application but then in the wizard that comes up select DLL for the application type and tick the empty project again
.
Once again add a main.cpp or whatever you like to call it and add the entry point code, it is slightly different for a dll:
#include <windows.h>
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved){
UNREFERENCED_PARAMETER(hModule);
UNREFERENCED_PARAMETER(lpReserved);
switch(ul_reason_for_call){
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
};
return TRUE;
}
So now is where the magic happens, the main point in this tutorials existence and it only took 730 words to get here! Yay!
I am sure you can decipher the code above, UNREFERENCED_PARAMETER is a macro used to prevent the compiler giving me warnings about unreferenced formal parameters, from the switch cases I believe it is simple that the code we will be writing will be in the ATTACH section but I have included the detach case if you wanted to do any clean up or dumping
.
The first thing we are going to do is rival xadet’s recent post on sending chat messages to ROSE but hopefully our end result will be prettier because we are using C++ and not that .NET managed rubbish.
We are going to hide the code in a header file so it can be reused in your other applications, so go ahead and create a file called ‘RoseAPI.hpp’ or whatever you want, but that is what I am calling mine
.
Because the function which adds text messages to the chatbox is actually a class function we need to create a fake class, following the naming convention from xadet’s tutorial I shall call it CIT_MGR, it will be defined simply by adding a small declaration to the header, I have also added a function which returns the IT_MGR class from the TRose memory by casting 0×697AD0 (which is the location of this class in TRose.exe) to a CIT_MGR* pointer.
class CIT_MGR {};
static inline CIT_MGR* IT_MGR(){
return reinterpret_cast<CIT_MGR*>(0x697AD0);
}
That was simple, now it gets a bit more complicated, we must define the function declaration which is:
typedef void (__thiscall IT_MGR::*PTR_AddChatText)(const char* message, int type, unsigned int customColour);
As you can see it uses the __thiscall mechanism which states it is a call to a class function, this looks like a typical function pointer, you can read more about them here.
The next part requires some trickery due to how class function pointers work, due to the nature of class pointers we cannot directly convert an address to a class pointer which is why I have found this rediculous method:
const int addr_TR_AddChatText = 0x48D890;
static const PTR_AddChatText TR_AddChatText = *((PTR_AddChatText*)&addr_TR_AddChatText);
#define AddChatText (IT_MGR()->*TR_AddChatText)
This may look silly but oh well, the advantage we have over xadet’s methods is the pure usability of my code, no ASM required, just pure C++ trickery going on here. The first variable has the address to the function, this is then followed by the function pointer, as you can see I have crazy pointer casting going on as this is the only way to prevent any compiler errors about converting an int to a class function pointer. The define I have added is for usability, it allows the code to be easily called, thus allowing me the functionality of:
AddChatText("I am a chat message", ChatType::ALL, 0);
How handy and neat and tidy and stuff is that final code, the method to get it may have been messy due to the use of class function pointers but I am sure you will let me off with that as it is hidden away in a header file anyway!
There is only one problem with this, we have no way of really using this AddChatText as we can’t add the text when the DLL is attached as the game is only just starting! We need some sort of trigger… Some sort of ‘hook’…
How about when the client uses a / command?! Genius plan.
To do this we need to place a hook where our code will be executed whenever the client attempts to send a chat message beginning with a ‘/’, to do this we are going to hook the send packet location!
Hurray.
First off I am going to provide you with a generic function used to hook code:
void ApplyJmpHook(unsigned char* code, unsigned char* location, int nops = 0){
DWORD oldProtect;
VirtualProtect(code, 5 + nops, PAGE_EXECUTE_READWRITE, &oldProtect);
code[0] = 0xE9;
*(int*)(code+1) = (int)location - ((int)code + 5);
if(nops > 0) memset(code + 5, 0x90, nops);
VirtualProtect(code, 5 + nops, oldProtect, &oldProtect);
}
ApplyJmpHook takes 3 parameters, the code to place the hook and where this hook will redirect to, it also allows a specified amount of NOP commands to be added after the JMP. In this function you can see VirtualProtect being used, this is because the executable memory in an exe is usually protected against writing, VirtualProtect allows us to write over the code memory. The rest of the code is merely writing the code to memory, 0xE9 is the prefix for the JMP command, then I write the address for the JMP, then 0×90 is a NOP command.
Beautiful. Now this function can be used in such a way like ApplyJmpHook(roseCode, &MyFunction, 1); In order to apply this hook we need to know where the SendPacket code is, I am not going to show you how to find it as that would be another long tutorial, instead I am kind and provide you with this address to use: 0×00402D5E and I shall also inform you that this hook needs 0 NOPs
. (NOP is used to allow OllyDBG to read the code properly after we have modified it, if our code overwrites only half of a command we need to NOP the other half of it!)
There is just one more problem with this, we can’t make it JMP directly to a C++ function as it will not have the correct arguments and will lead to errors! So first we must make a little wrapper function like the one below:
static const char* TROSE_PacketEncryption = reinterpret_cast<const char*>(0x00405020);
static _declspec(naked) void ASMOnSendPacket(){
_asm{
PUSH ECX
PUSH ESI
CALL CppOnSendPacket
POP ECX
CALL TROSE_PacketEncryption
POP ESI
RETN 0x04
}
}
Hurray, now we can declare a function CppOnSendPacket and hook the ROSE code using:
(Note that we must apply the hook in the DLL_ATTACH code!)
bool CppOnSendPacket(char* packet);
ApplyJmpHook((unsigned char*)0x00402D5E, (unsigned char*)&ASMOnSendPacket, 0);
Now it is just a matter of reading the packet, detecting if it is a chat packet and reading the chat message to look for our / commands!
bool __stdcall CppOnSendPacket(char* packet){
unsigned short size = *(unsigned short*)packet;
unsigned short command = *(unsigned short*)(packet + 2);
if(command == 0x783 && size > 7){
char* message = packet+6;
if(_strcmpi(message, "/hello") == 0){
AddChatText("Hello Mr. ROSE Man", ChatType::SYSTEM, 0);
}
}
return true;
}
WOOOT! So now when a player types /hello in game they should be presented with a friendly message saying “Hello Mr. ROSE Man”, how lucky are they!
That is the end of this absolutely massive tutorial! At this point it is currently 1820 words long and took a long time to write haha
oh well. This tutorial was needed as it is a prerequisite for other tutorials such as the name colour tutorial part 3 (which I have already wrote 500 words of but realised I needed an inject dll to continue!) and the upcoming tutorial on creating 100% custom dialogs in ROSE!
As usual the source for this tutorial is available, download it here.
1909 words, nice.
C++, Open Source, Programming, ROSE Online, Reverse Engineering, Tutorials













