This was a request from a friend of mine last night (he wanted to be able to execute stored procedures depending on the value of "/cmd", for his custom staff commands), so I came up with this. Obviously it could be expanded on more and cleaned up a bit, but it should give a decent idea on how it works.
I used the ps_game located

, so you might need to modify the offsets if you are using something different.
Media:
Hook.hpp
Code:
#ifndef SHAIYA_GAME_HOOK_HPP
#define SHAIYA_GAME_HOOK_HPP
// Include the Windows header
#include <Windows.h>
// Include a queue and mutex
#include <queue>
#include <mutex>
// Definition of the hook address
DWORD HOOK_ADDRESS = 0x474A80;
DWORD HOOK_RETURN = 0x474A85;
// The analyzed packet instance
struct Packet {
// The user id
int user_uid;
// The character name
char char_name[32];
// The packet data
char packet_data[512];
};
// The character name
char* char_name;
// The user uid
int user_uid;
// The data
char* data;
// The mutex for adding and removing from the queue
std::mutex mutex;
// The queue of packets to be sent down the pipe
std::queue<Packet*> packet_queue;
// Declaration of the hook initialisation method
void init_hook();
// Process the handling of packets
void process();
// Handle the analzying of packets
void analyze(Packet*);
// The add_queue method
void add_to_queue();
#endif
Hook.cpp
Code:
#include "Hook.hpp"
#include <iostream>
#include <fstream>
/**
* The entry point for the hook, which is called after the DLL is injected to the ps_game process.
*/
BOOL APIENTRY DllMain(HMODULE module, DWORD reason, LPVOID reserved) {
// If the reason for calling this function, is a process attach
if (reason == DLL_PROCESS_ATTACH) {
// Initialise the processing function on a new thread
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) &process, NULL, 0, NULL);
// Initialise the hook
init_hook();
}
// Return a successful execution
return TRUE;
}
/**
* The detour method, which we will use to analyze incoming packets from the game client.
*/
void __declspec(naked) detour() {
// Since we are over writing the check for handling character related packets, we need to manually write it ourselves!
__asm {
// Push all general purpose registers to the stack
PUSHAD
// Push the EFLAGS register
PUSHFD
// The ebx register holds the character instance, so we retrieve the user uid and char name
movzx ecx, [ebx + 0x582C]
mov user_uid, ecx
lea ecx, [ebx + 0x184]
mov char_name, ecx
// Retrieve the packet data
mov data, edi
}
// Call the queue function (we can't do too much in a naked function!)
add_to_queue();
__asm {
// Pop the registers and flags
POPFD
POPAD
// We overwrote this when inserting our JMP call
MOV EDX, 00000001
// Jump to the return address
JMP HOOK_RETURN
}
}
/**
* Analyze the packet instance
*/
void analyze(Packet* packet) {
// The opcode of the packet
int opcode = (packet->packet_data[0] & 0xFF) + ((packet->packet_data[1] & 0xFF) << 8);
// Operate on the packet, depending on the opcode
switch (opcode) {
// The /cmd command
case 0xF603:
// The length of the message sent
int msg_length = (packet->packet_data[2] & 0xFF);
// The message array
char message[32];
// Read the message sent
for (int i = 0; i < msg_length; i++)
message[i] = packet->packet_data[3 + i];
// Log the packet
std::ofstream log_file;
log_file.open("cmd.log");
log_file << "[Opcode=" << opcode << ", UserUID=" << packet->user_uid << ", Charname=" << packet->char_name << ", MsgLen=" << msg_length << ", Message=" << &message[0] << "]" << std::endl;
log_file.close();
break;
}
}
/**
* Begin hooking and detouring the server sided packet processing function
*/
void init_hook() {
// The current protection level
DWORD temp;
// Allow reading and writing of the code segment
VirtualProtect((void*) HOOK_ADDRESS, 5, PAGE_EXECUTE_READWRITE, &temp);
// Modify the first instruction to be a JMP
*(BYTE *)(HOOK_ADDRESS) = 0xE9;
// Next should be the address of our function
*(DWORD *)(HOOK_ADDRESS + 1) = (DWORD)&detour - HOOK_ADDRESS - 5;
}
/**
* Attempt to add the packet instance to the queue, which will be processed later.
*/
void add_to_queue() {
// The packet structure
auto packet = new Packet();
// Define the values
memcpy(&packet->user_uid, &user_uid, sizeof(packet->user_uid));
memcpy(&packet->char_name, char_name, sizeof(packet->char_name));
memcpy(&packet->packet_data, data, sizeof(packet->packet_data));
// Lock the mutex
mutex.lock();
// Add the packet to the queue
packet_queue.push(packet);
// Unlock the mutex
mutex.unlock();
}
// Process the queue of packets
void process() {
// Loop while the hook is running
while (true) {
// Lock the mutex
mutex.lock();
// If there is a packet to be processed
if (packet_queue.size() != 0) {
// The packet instance
auto packet = packet_queue.front();
// Operate on the packet
analyze(packet);
// Pop the packet from the queue
packet_queue.pop();
}
// Unlock the mutex
mutex.unlock();
// Throttle the thread
Sleep(50);
}
}