Skip to content
This repository has been archived by the owner on Jan 2, 2022. It is now read-only.

Use CDOTAChatChannelBase::ProcessMsg to read incoming messages #3

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 74 additions & 67 deletions Injectee/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,29 @@
#include <comdef.h>
#include <Psapi.h>

// The function prologue for CDOTA_SF_Hud_Chat::MessagePrintf
const byte gcSignature[] = { 0x48,0x8B,0xC4,0x55,0x56,0x41,0x55,0x41,0x56,0x41,0x57 };
// The function prologue for CDOTAChatChannelBase::ProcessMsg
const byte gcProcessMsgSignature[] = { 0x48,0x89,0x5C,0x24,0x10,0x48,0x89,0x6C,0x24,0x18,0x48,0x89,0x74,0x24,0x20,0x57,0x41,0x56,0x41,0x57,0xB8,0x30,0x18,0x00,0x00 };
// and the the function prologue for CDOTA_SF_Hud_Chat::MessagePrintf
const byte gcPrintChatSignature[] = { 0x48,0x8B,0xC4,0x55,0x56,0x41,0x55,0x41,0x56,0x41,0x57 };

// Unknown areas are represented as byte arrays
struct CMsgDOTAChatMessage {
void* vtable;
byte u1[40];
char* message;
byte u4[96];
byte u5[128];
};

int64_t(*gOriginalPrintChat)(void *, int, wchar_t*, int);
int64_t(*gBypassAddr)(void *, int, wchar_t*, int);
void *gDotaChatAreas;
int64_t(*gOriginalProcessMsg)(void*, CMsgDOTAChatMessage*, char);
int64_t(*gProcessMsgBypassAddr)(void*, CMsgDOTAChatMessage*, char);
int64_t(*gOriginalPrintChat)(void*, int, wchar_t *, int);
int64_t(*gPrintChatBypassAddr)(void*, int, wchar_t *, int);

struct SavedPrintChatParams {
void* a1;
int a2;
int a4;
};

std::fstream* gLogFile;
HOOK_TRACE_INFO ghHook;
HOOK_TRACE_INFO ghHook1, ghHook2;
HANDLE ghPipe1, ghPipe2;

void Log(char* msg) {
Expand All @@ -38,8 +47,8 @@ void LogError() {
#endif
}

void WriteChatMsgToNamedPipe(wchar_t* msg) {
short len = (short)(wcslen(msg) * sizeof(wchar_t));
void WriteChatMsgToNamedPipe(char* msg) {
short len = (short)(strlen(msg) * sizeof(char));
#ifdef _DEBUG
*gLogFile << "Byte length: " << (int)len << "\r\n";
gLogFile->flush();
Expand All @@ -50,41 +59,39 @@ void WriteChatMsgToNamedPipe(wchar_t* msg) {
LogError();
}

void WriteMessageParamsToNamedPipe(void *a1, int a2, int a4) {
SavedPrintChatParams params;
short size = sizeof(params);

params.a1 = a1;
params.a2 = a2;
params.a4 = a4;
int64_t __fastcall ProcessMsgHook(void *a1, CMsgDOTAChatMessage *message, char a3) {
#ifdef _DEBUG
*gLogFile << "ProcessMsg Called!\r\n";
*gLogFile << "Message: ";
*gLogFile << message->message << "\r\n";
gLogFile->flush();
#endif
// Don't translate translated messages
if (strstr(message->message, "(Translated from "))
return 0;

if (!WriteFile(ghPipe1, &size, sizeof(size), NULL, NULL))
LogError();
if (!WriteFile(ghPipe1, &params, size, NULL, NULL))
LogError();
WriteChatMsgToNamedPipe(message->message);
return gOriginalProcessMsg(a1, message, a3);
}

int64_t __fastcall PrintChatHook(void *a1, int a2, wchar_t* message, int a4) {
WriteChatMsgToNamedPipe(message);
WriteMessageParamsToNamedPipe(a1, a2, a4);
int64_t __fastcall PrintChatHook(void *a1, int a2, wchar_t *a3, int a4) {
Log("PrintChat called!");
#ifdef _DEBUG
*gLogFile << "Status at send:\r\n";
*gLogFile << "a1: " << std::hex << a1;
*gLogFile << "\r\na2: " << std::hex << a2;
*gLogFile << "\r\na4: " << std::hex << a4 << "\r\n";
*gLogFile << "a2: " << a2 << "\r\n";
*gLogFile << "a4: " << a4 << "\r\n";
gLogFile->flush();
#endif
return gOriginalPrintChat(a1, a2, message, a4);
gDotaChatAreas = a1;
return gPrintChatBypassAddr(a1, a2, a3, a4);
}

DWORD WINAPI ListenPipeAndPrint(LPVOID lpParam) {
Log("Client pipe listener ready");
if (!gBypassAddr) {
if (!gPrintChatBypassAddr) {
Log("Bad hook bypass address");
return 0;
}


while (true) {
short numBytes;
if (!ReadFile(ghPipe2, &numBytes, 2, NULL, NULL)) {
Expand All @@ -97,41 +104,30 @@ DWORD WINAPI ListenPipeAndPrint(LPVOID lpParam) {
gLogFile->flush();
#endif

byte* buf = new byte[numBytes + 2];
size_t len = (numBytes + 2) / sizeof(wchar_t);
wchar_t* buf = new wchar_t[len];
if (!ReadFile(ghPipe2, buf, numBytes, NULL, NULL)) {
Log("Can't read from pipe");
LogError();
return 0;
}
buf[numBytes] = 0;
buf[numBytes + 1] = 0;

buf[len - 1] = (wchar_t)0;
Log("Message read from pipe successfully");
size_t l = wcslen((wchar_t *)buf);

// Retrive the message parameters
SavedPrintChatParams params;
if (!ReadFile(ghPipe2, &params, sizeof(params), NULL, NULL))
LogError();

Log("Message parameters read from pipe successfully");
#ifdef _DEBUG
*gLogFile << "Status at recv:\r\n";
*gLogFile << "a1: " << std::hex << params.a1;
*gLogFile << "\r\na2: " << std::hex << params.a2;
*gLogFile << "\r\na4: " << std::hex << params.a4 << "\r\n";
gLogFile->flush();
#endif
if (gDotaChatAreas) {
gPrintChatBypassAddr(gDotaChatAreas, 0, buf, 0xffffffff);
Log("Translated message injected successfully.");
}
else
Log("gDotaChatAreas not initialized yet, skipping injection.");

gBypassAddr(params.a1, params.a2, (wchar_t *)buf, params.a4);
delete buf;
Log("Injected translated message successfully");
}

return 0;
}

void *FindPrintChatAddr(MODULEINFO *mi) {
void *FindAddr(MODULEINFO *mi, const byte* sig, size_t len) {
byte *baseAddr = (byte *)mi->lpBaseOfDll;
DWORD dllSize = mi->SizeOfImage;

Expand All @@ -141,17 +137,17 @@ void *FindPrintChatAddr(MODULEINFO *mi) {
}

for (DWORD i = 0; i < dllSize; i++) {
for (int j = 0; j < sizeof(gcSignature); j++) {
if (*(baseAddr + j) != gcSignature[j])
for (int j = 0; j < len; j++) {
if (*(baseAddr + j) != sig[j])
break;

if (j == sizeof(gcSignature) - 1)
if (j == len - 1)
return (void *)baseAddr;
}
baseAddr++;
}

Log("Could not find PrintChat!");
Log("Could not find by signature!");
return 0;
}

Expand All @@ -177,25 +173,36 @@ extern "C" void __declspec(dllexport) __stdcall NativeInjectionEntryPoint(REMOTE
return;
}

gOriginalPrintChat = (int64_t(*) (void *, int, wchar_t*, int))FindPrintChatAddr(&mi);
if (!gOriginalPrintChat)
gOriginalProcessMsg = (int64_t(*) (void*, CMsgDOTAChatMessage*, char))FindAddr(&mi, gcProcessMsgSignature, sizeof(gcProcessMsgSignature));
gOriginalPrintChat = (int64_t(*) (void*, int, wchar_t*, int))FindAddr(&mi, gcPrintChatSignature, sizeof(gcPrintChatSignature));

if (!gOriginalProcessMsg || !gOriginalPrintChat)
return;

ghHook = { NULL };
ghHook1 = { NULL };
ghHook2 = { NULL };

#ifdef _DEBUG
*gLogFile << "Hooking printChat at: 0x" << std::hex << gOriginalPrintChat << "\r\n";
*gLogFile << "Hooking CDOTAChatChannelBase::ProcessMsg at: 0x" << std::hex << gOriginalProcessMsg << "\r\n";
*gLogFile << "Hooking CDOTA_SF_Hud_Chat::MessagePrintf at: 0x" << std::hex << gOriginalPrintChat << "\r\n";
#endif

NTSTATUS didHook = LhInstallHook(gOriginalPrintChat, PrintChatHook, NULL, &ghHook);
NTSTATUS didHook = LhInstallHook(gOriginalProcessMsg, ProcessMsgHook, NULL, &ghHook1);
if (FAILED(didHook))
Log("Failed to hook ProcessMsg");
else
Log("Hooked ProcessMsg successfully");
didHook = LhInstallHook(gOriginalPrintChat, PrintChatHook, NULL, &ghHook2);
if (FAILED(didHook))
Log("Failed to hook printChat :(");
Log("Failed to hook PrintChat");
else
Log("Hooked printChat successfully");
Log("Hooked PrintChat successfully");

ULONG ACLEntries[1] = { 0 };
LhSetExclusiveACL(ACLEntries, 1, &ghHook);
LhGetHookBypassAddress(&ghHook, (void ***)&gBypassAddr);
LhSetExclusiveACL(ACLEntries, 1, &ghHook1);
LhSetExclusiveACL(ACLEntries, 1, &ghHook2);
LhGetHookBypassAddress(&ghHook1, (void ***)&gProcessMsgBypassAddr);
LhGetHookBypassAddress(&ghHook2, (void ***)&gPrintChatBypassAddr);
CreateThread(0, 0, ListenPipeAndPrint, 0, 0, 0);
}

Expand Down
24 changes: 12 additions & 12 deletions Translator/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,28 +103,31 @@ static async void ListenPipeAndTranslate(NamedPipeServerStream server1, NamedPip
if (numBytes == 0)
continue;

byte[] messageParams = new byte[100];
server1.Read(messageParams, 0, 2);
numBytes = BitConverter.ToUInt16(messageParams, 0);
server1.Read(messageParams, 0, numBytes);
Array.Resize(ref messageParams, numBytes);

TranslateAndPrint(Encoding.Unicode.GetString(buffer), server2, messageParams);
TranslateAndPrint(Encoding.UTF8.GetString(buffer), server2);
}
}
catch (Exception e)
{
#if DEBUG
Console.WriteLine("Exception: " + e.Message);
#endif
Console.WriteLine("DotA 2 has exited - press any key to close.");
return;
}
} while (true);
}

static async void TranslateAndPrint(string message, NamedPipeServerStream sw, byte[] messageParams)
static async void TranslateAndPrint(string message, NamedPipeServerStream sw)
{
// These messages are already in the user's language
// They contain HTML too, so I'm not gonna bother translate them
if (message.Contains("<img") || message.Contains("<font")) { return; }
if (message.Contains("<img") || message.Contains("<font")) {
#if DEBUG
Console.Write("Ignored message: ");
Console.WriteLine(message);
#endif
return;
}

try
{
Expand All @@ -146,9 +149,6 @@ static async void TranslateAndPrint(string message, NamedPipeServerStream sw, by
sw.Write(size, 0, 2);
// Write the UTF-16 encoded message
sw.Write(sendBytes, 0, sendBytes.Length);
// Write the message parameters
sw.Write(messageParams, 0, messageParams.Length);
sw.Flush();
Console.WriteLine(toSend);
}
}
Expand Down