diff --git a/Makefile b/Makefile index a91c825..d6a27ee 100644 --- a/Makefile +++ b/Makefile @@ -30,6 +30,7 @@ OBJS := \ obj/dll/rugburn/main.o \ obj/hooks/kernel32/inject.o \ obj/hooks/msvcr100/msvcr100.o \ + obj/hooks/projectg/us852/ranking.o \ obj/hooks/user32/window.o \ obj/hooks/ws2_32/redir.o \ obj/hooks/wininet/netredir.o \ @@ -38,6 +39,7 @@ OBJS := \ obj/bootstrap.o \ obj/common.o \ obj/config.o \ + obj/hex.o \ obj/ijlfwd.o \ obj/json.o \ obj/patch.o \ diff --git a/rugburn.vcxproj b/rugburn.vcxproj index e571d45..bbd19bd 100644 --- a/rugburn.vcxproj +++ b/rugburn.vcxproj @@ -141,7 +141,6 @@ false MultiThreaded $(IntDir)%(RelativeDir) - stdcpp17 Console @@ -150,7 +149,7 @@ true exportvs.def DllMain - kernel32.lib;user32.lib;shlwapi.lib;libvcruntime.lib;libcmt.lib;libucrt.lib;%(AdditionalDependencies) + kernel32.lib;user32.lib;shlwapi.lib;%(AdditionalDependencies) @@ -193,7 +192,7 @@ - + @@ -204,4 +203,4 @@ - \ No newline at end of file + diff --git a/scripts/setup-clangd.sh b/scripts/setup-clangd.sh index a5a7110..364580f 100755 --- a/scripts/setup-clangd.sh +++ b/scripts/setup-clangd.sh @@ -22,5 +22,5 @@ cp ".tmp/h/sdkddkve.h" ".tmp/h/sdkddkver.h" echo "-I$PWD/.tmp/h" > compile_flags.txt echo "-D__va_list=__builtin_va_list" >> compile_flags.txt echo "-D_exception_code=__exception_code" >> compile_flags.txt +echo "-D__stdcall=" >> compile_flags.txt echo "-fms-extensions" >> compile_flags.txt - diff --git a/src/bootstrap.c b/src/bootstrap.c index a9e4920..767c7ff 100644 --- a/src/bootstrap.c +++ b/src/bootstrap.c @@ -85,7 +85,7 @@ typedef struct _TEB { #else // x86 PTEB tebPtr = (PTEB)__readfsdword(OFFSETOF(NT_TIB, Self)); #endif - return tebPtr; + return tebPtr; } #endif diff --git a/src/config.c b/src/config.c index 5e0aef4..365a218 100644 --- a/src/config.c +++ b/src/config.c @@ -58,8 +58,8 @@ void ReadJsonPatchAddressMap(LPSTR *json, LPCSTR key) { FatalError("Reached maximum number of Patch address!"); } - Config.PatchAddress[Config.NumPatchAddress].addr = ReadDword(key); - TranslateHexInText(value, Config.PatchAddress[Config.NumPatchAddress].patch, sizeof(Config.PatchAddress[Config.NumPatchAddress].patch), &Config.PatchAddress[Config.NumPatchAddress].patch_len); + Config.PatchAddress[Config.NumPatchAddress].addr = ParseAddress(key); + ParsePatch(value, &Config.PatchAddress[Config.NumPatchAddress].patch, &Config.PatchAddress[Config.NumPatchAddress].patchLen); Config.NumPatchAddress++; } @@ -141,15 +141,17 @@ BOOL RewriteAddr(LPSOCKADDR_IN addr) { } void PatchAddress() { - int i; - - for (i = 0; i < Config.NumPatchAddress; i++) { - - if (Config.PatchAddress[i].addr != 0 && Config.PatchAddress[i].patch_len > 0 && Config.PatchAddress[i].patch[0] != '\0') { - - Patch((LPVOID)Config.PatchAddress[i].addr, Config.PatchAddress[i].patch, Config.PatchAddress[i].patch_len); - - Log("PatchAddress: 0x%08lX, Len: %d, Value: %s\r\n", Config.PatchAddress[i].addr, Config.PatchAddress[i].patch_len, Config.PatchAddress[i].patch); - } - } + int i; + for (i = 0; i < Config.NumPatchAddress; i++) { + if (Config.PatchAddress[i].addr == 0) { + Warning("Patch %d at address 0 will be ignored.", i); + continue; + } + if (Config.PatchAddress[i].patchLen == 0) { + Warning("Patch %d is empty.", i); + continue; + } + Patch((LPVOID)Config.PatchAddress[i].addr, Config.PatchAddress[i].patch, Config.PatchAddress[i].patchLen); + Log("PatchAddress: 0x%08lX, Len: %d, Value: %s\r\n", Config.PatchAddress[i].addr, Config.PatchAddress[i].patchLen, Config.PatchAddress[i].patch); + } } diff --git a/src/config.h b/src/config.h index 2938f77..61ac0ee 100644 --- a/src/config.h +++ b/src/config.h @@ -21,8 +21,8 @@ typedef struct _PORTREWRITERULE { typedef struct _PATCHADDRESS { DWORD addr; - CHAR patch[1024]; - int patch_len; + LPSTR patch; + DWORD patchLen; } PATCHADDRESS, *LPPATCHADDRESS; typedef struct _RUGBURNCONFIG { diff --git a/src/dll/rugburn/main.c b/src/dll/rugburn/main.c index 966af52..8aea7d5 100644 --- a/src/dll/rugburn/main.c +++ b/src/dll/rugburn/main.c @@ -14,10 +14,10 @@ #include "../../bootstrap.h" #include "../../common.h" #include "../../config.h" +#include "../../hooks/hooks.h" #include "../../ijlfwd.h" #include "../../patch.h" -#include "../../hooks/hooks.h" -#include "../../patch_usa_852.h" +#include "../../hooks/projectg/us852/ranking.h" /** * InitEnvironment configures the PANGYA_ARG environment to avoid needing to @@ -44,9 +44,9 @@ static VOID InitEnvironment() { * Implements the GameGuard patches for Pangya US 852.00. */ static DWORD STDCALL PatchGG_US852(PVOID unused) { - while(1) { + while (1) { // TODO(john): Remove hardcoded addresses. - if (*(DWORD*)0x00A495E0 == 0x8F143D83) { + if (*(DWORD *)0x00A495E0 == 0x8F143D83) { Patch((LPVOID)0x00A495E0, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A49670, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A49690, "\xC3\x90\x90\x90\x90\x90\x90", 7); @@ -56,35 +56,36 @@ static DWORD STDCALL PatchGG_US852(PVOID unused) { Log("Patched GG check routines (US 852)\r\n"); Patch((LPVOID)0x00A6ECC9, "\x30\xC0", 2); - Log("Patched Cookie Point Item (US 852)\r\n"); - - Patch((LPVOID)0x005FB990, "\x80\xB9\x40\x02\x00\x00\x00\x0F\x85\x0D\x00\x00\x00\x8B\x89\x8C\x01\x00\x00\x8B\x01\x8B\x50\x4C\xFF\xD2\xC2\x04\x00", 29); - Log("Patched Cookie Btn in onCallback that's disabled (US 852)\r\n"); - - Patch((LPVOID)0x005FB9AD, "\xB3\x01\x31\xFF\x90\x53\xBA\xB4\x6A\xCE\x00\xE9\xC9\x31\x00\x00", 16); - Patch((LPVOID)0x005FEB7E, "\xE9\x2A\xCE\xFF\xFF", 5); - Patch((LPVOID)0x005FEB8D, "\x53", 1); - Patch((LPVOID)0x005FEB9A, "\x53", 1); - Patch((LPVOID)0x005FB9BD, "\x6A\x01\xBA\xB4\x6A\xCE\x00\x8B\xCE\xE8\xF5\x2C\x00\x00\x6A\x01\xBA\xB0\x69\xCE\x00\xE9\x29\x32\x00\x00", 26); - Patch((LPVOID)0x005FEBF9, "\xE9\xBF\xCD\xFF\xFF", 5); - Log("Patched Btn Cookie, Gacha and Scratch disabled (US 852)\r\n"); - - Patch((LPVOID)0x008BC729, "\x01", 1); - Patch((LPVOID)0x008C1495, "\xEB\x0C", 2); - Patch((LPVOID)0x008C14A3, "\xE8\xF8\xB2\xFF\xFF\x88\x86\xE4\x00\x00\x00\x5E\xC3", 13); - Log("Patched Btn Change Nickname disabled (US 852)\r\n"); - - unsigned char jmp_to_patch_ranking[5] = { 0xE9u, 0u, 0u, 0u, 0u }; - - DWORD relAddr = (DWORD)OnUnderBar_RankingUp - 0x00655630u - 5u; - - memcpy(&jmp_to_patch_ranking[1], &relAddr, 4u); - - Patch((LPVOID)0x00655630, jmp_to_patch_ranking, sizeof(jmp_to_patch_ranking)); - Log("Patched Ranking System disabled (US 852)\r\n"); + Log("Patched Cookie Point Item (US 852)\r\n"); + + Patch((LPVOID)0x005FB990, + "\x80\xB9\x40\x02\x00\x00\x00\x0F\x85\x0D\x00\x00\x00\x8B\x89\x8C\x01\x00\x00\x8B" + "\x01\x8B\x50\x4C\xFF\xD2\xC2\x04\x00", + 29); + Log("Patched Cookie Btn in onCallback that's disabled (US 852)\r\n"); + + Patch((LPVOID)0x005FB9AD, + "\xB3\x01\x31\xFF\x90\x53\xBA\xB4\x6A\xCE\x00\xE9\xC9\x31\x00\x00", 16); + Patch((LPVOID)0x005FEB7E, "\xE9\x2A\xCE\xFF\xFF", 5); + Patch((LPVOID)0x005FEB8D, "\x53", 1); + Patch((LPVOID)0x005FEB9A, "\x53", 1); + Patch((LPVOID)0x005FB9BD, + "\x6A\x01\xBA\xB4\x6A\xCE\x00\x8B\xCE\xE8\xF5\x2C\x00\x00\x6A\x01\xBA\xB0\x69\xCE" + "\x00\xE9\x29\x32\x00\x00", + 26); + Patch((LPVOID)0x005FEBF9, "\xE9\xBF\xCD\xFF\xFF", 5); + Log("Patched Btn Cookie, Gacha and Scratch disabled (US 852)\r\n"); + + Patch((LPVOID)0x008BC729, "\x01", 1); + Patch((LPVOID)0x008C1495, "\xEB\x0C", 2); + Patch((LPVOID)0x008C14A3, "\xE8\xF8\xB2\xFF\xFF\x88\x86\xE4\x00\x00\x00\x5E\xC3", 13); + Log("Patched Btn Change Nickname disabled (US 852)\r\n"); + + InitUS852RankingHook(); + Log("Patched Ranking System disabled (US 852)\r\n"); return TRUE; } - if (*(DWORD*)0x00A49580 == 0x8F143D83) { + if (*(DWORD *)0x00A49580 == 0x8F143D83) { Patch((LPVOID)0x00A49580, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A49670, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A49690, "\xC3\x90\x90\x90\x90\x90\x90", 7); @@ -94,18 +95,18 @@ static DWORD STDCALL PatchGG_US852(PVOID unused) { Log("Patched GG check routines (US 824)\r\n"); return TRUE; } - Delay(5); + Delay(5); } - return FALSE; + return FALSE; } /** * Implements the GameGuard patches for Pangya JP 972.00. */ static DWORD STDCALL PatchGG_JP972(PVOID unused) { - while(1) { + while (1) { // TODO(john): Remove hardcoded addresses. - if (*(DWORD*)0x00A5CD10 == 0x1BA43D83) { + if (*(DWORD *)0x00A5CD10 == 0x1BA43D83) { Patch((LPVOID)0x00A5CD10, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A5CDA0, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A5CDC0, "\xC3\x90\x90\x90\x90\x90\x90", 7); @@ -115,7 +116,7 @@ static DWORD STDCALL PatchGG_JP972(PVOID unused) { Log("Patched GG check routines (JP 972)\r\n"); return TRUE; } - if (*(DWORD*)0x00A5CF80 == 0x1BA43D83) { + if (*(DWORD *)0x00A5CF80 == 0x1BA43D83) { Patch((LPVOID)0x00A5CF80, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A5C010, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A5C030, "\xC3\x90\x90\x90\x90\x90\x90", 7); @@ -125,7 +126,7 @@ static DWORD STDCALL PatchGG_JP972(PVOID unused) { Log("Patched GG check routines (JP 974)\r\n"); return TRUE; } - if (*(DWORD*)0x00A5CF80 == 0x1C143D83) { + if (*(DWORD *)0x00A5CF80 == 0x1C143D83) { Patch((LPVOID)0x00A5CF80, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A5C010, "\xC3\x90\x90\x90\x90\x90\x90", 7); Patch((LPVOID)0x00A5C030, "\xC3\x90\x90\x90\x90\x90\x90", 7); @@ -135,20 +136,22 @@ static DWORD STDCALL PatchGG_JP972(PVOID unused) { Log("Patched GG check routines (JP 983)\r\n"); return TRUE; } - Delay(5); + Delay(5); } - return FALSE; + return FALSE; } static VOID STDCALL PatchDynamicAndGG(LPTHREAD_START_ROUTINE patchThread) { - if (!patchThread) { - Warning("It looks like no patch exists for this version of PangYa™.\nThe game will likely exit a couple minutes after detecting GameGuard is not present."); - PatchAddress(); - }else { - if (patchThread(NULL) == TRUE) - PatchAddress(); - } + if (!patchThread) { + Warning("It looks like no patch exists for this version of PangYa™.\nThe game will likely " + "exit a couple minutes after detecting GameGuard is not present."); + PatchAddress(); + } else { + if (patchThread(NULL) == TRUE) { + PatchAddress(); + } + } } /** @@ -202,7 +205,7 @@ extern BOOL STDCALL SlipstrmDllMain(HANDLE hInstance, DWORD dwReason, LPVOID res PFNDLLMAINPROC pSlipstreamOep; BOOL bOepResult; - pSlipstreamOep = (PFNDLLMAINPROC)((*(DWORD*)((DWORD)hInstance + 0x40))+(DWORD)hInstance); + pSlipstreamOep = (PFNDLLMAINPROC)((*(DWORD *)((DWORD)hInstance + 0x40)) + (DWORD)hInstance); // Call OEP; return failure if it fails. bOepResult = pSlipstreamOep(hInstance, dwReason, reserved); diff --git a/src/hex.c b/src/hex.c index c8e1468..e306ca7 100644 --- a/src/hex.c +++ b/src/hex.c @@ -1,130 +1,122 @@ -#include "hex.h" - -DWORD ReadDword(LPCSTR _text) { - - if (_text == NULL) { - FatalError("Hex - Read Dword hex in text, parameters invalid!"); - } - - if (_text[0] == '\0') - return 0; - - LPSTR pos = (LPSTR)_text; - DWORD dw = 0; - int len = 0; - - if ((pos = strstr(pos, "0x")) == NULL) { - FatalError("Hex - Read Dword hex in text, invalid dword hex!"); - } - - while (isxdigit(pos[len + 2])) - len++; - - if (len > 0) { - - CHAR ctmp = pos[len + 2]; - pos[len + 2] = '\0'; - - dw = strtol(pos + 2, NULL, 16); - } - - return dw; -} - -void TranslateHexInText(LPCSTR _text, LPSTR _result, int _size_result, int* _size_out) { - - if (_text == NULL || _result == NULL || _size_result == 0 || _size_out == NULL) { - FatalError("Hex - Translate hex in text, parameters invalid!"); - } - - *_size_out = 0; - - if (_text[0] == '\0') { - - _result[*_size_out++] = '\0'; - - return; - } - - LPSTR pos = (LPSTR)_text; - LPSTR rest = NULL; - int len = 0, num = 0; - - do { - - rest = strstr(pos, "\\x"); - - if (rest != NULL) { - - len = rest - pos; - - if (len > 0) { - - if ((*_size_out + len) > _size_result) { - FatalError("Hex - Translate hex in text, overflow result buffer!"); - } - - memcpy(_result + *_size_out, pos, len); - *_size_out += len; - - pos = rest; - } - - len = 0; - - while (len < 2 && isxdigit(pos[len + 2])) - len++; - - if (len > 0) { - - CHAR ctmp = pos[len + 2]; - pos[len + 2] = '\0'; - - num = strtol(pos + 2, NULL, 16); - - if ((*_size_out + 1) > _size_result) { - FatalError("Hex - Translate hex in text, overflow result buffer!"); - } - - pos += len + 2; - *pos = ctmp; - - _result[*_size_out] = (CHAR)num; - - *_size_out += 1; - - }else { - - if ((*_size_out + 1) > _size_result) { - FatalError("Hex - Translate hex in text, overflow result buffer!"); - } - - _result[*_size_out] = *pos++; - - *_size_out += 1; - } - - }else { - - len = strlen(pos); - - if ((*_size_out + len) > _size_result) { - FatalError("Hex - Translate hex in text, overflow result buffer!"); - } - - memcpy(_result + *_size_out, pos, len); - *_size_out += len; - - pos += len; - } - - } while(*pos != '\0'); - - if ((*_size_out + 1) > _size_result) { - FatalError("Hex - Translate hex in text, overflow result buffer!"); - } - - _result[*_size_out] = '\0'; - - *_size_out += 1; -} +#include "hex.h" + +static BOOL ParseHex(CHAR ch, LPDWORD pOutValue) { + if (ch >= '0' && ch <= '9') { + *pOutValue = ch - '0'; + } else if (ch >= 'a' && ch <= 'f') { + *pOutValue = ch - 'a' + 10; + } else if (ch >= 'A' && ch <= 'F') { + *pOutValue = ch - 'A' + 10; + } else { + return FALSE; + } + return TRUE; +} + +DWORD ParseAddress(LPCSTR lpszText) { + LPCSTR pos = lpszText; + DWORD hexDigitVal; + DWORD result = 0; + + if (lpszText == NULL) { + FatalError("ParseAddress: Received unexpected NULL string"); + } + + if (lpszText[0] == '\0') { + return 0; + } + + if (pos[0] != '0' || pos[1] != 'x' || pos[2] == 0) { + FatalError("ParseAddress: Invalid input, expected 0x-prefixed hex number"); + } + + pos += 2; + + while (ParseHex(*pos, &hexDigitVal) == TRUE) { + result <<= 4; + result |= hexDigitVal; + pos++; + } + + if (*pos) { + FatalError("ParseAddress: Unexpected text remaining after address: %s", pos); + } + + return result; +} + +void ParsePatch(LPCSTR lpszText, LPSTR *pDataOut, DWORD *pSizeOut) { + DWORD hexDigitVal; + DWORD hexOctetVal; + LPCSTR inPos; + LPSTR outPos; + *pSizeOut = 0; + + // Calculate length + inPos = lpszText; + while (*inPos) { + switch (*inPos++) { + case '\\': + switch (*inPos++) { + case 'x': + if (ParseHex(*inPos++, &hexDigitVal) == FALSE) { + FatalError("ParsePatch: Bad hex escape near %s", inPos); + } + if (ParseHex(*inPos++, &hexDigitVal) == FALSE) { + FatalError("ParsePatch: Bad hex escape near %s", inPos); + } + (*pSizeOut)++; + break; + case '\\': + (*pSizeOut)++; + break; + case '\0': + FatalError("ParsePatch: Unexpected end of string in escape code"); + break; + default: + FatalError("ParsePatch: Unknown escape code: %s", inPos); + } + default: + (*pSizeOut)++; + } + inPos++; + } + + // Allocate memory + *pDataOut = AllocMem(*pSizeOut); + + // Parse + inPos = lpszText; + outPos = *pDataOut; + while (*inPos) { + switch (*inPos) { + case '\\': + inPos++; + switch (*inPos++) { + case 'x': + if (!ParseHex(*inPos++, &hexDigitVal)) { + FatalError("ParsePatch: Bad hex escape near %s", inPos); + } + hexOctetVal = hexDigitVal << 4; + if (!ParseHex(*inPos++, &hexDigitVal)) { + FatalError("ParsePatch: Bad hex escape near %s", inPos); + } + hexOctetVal |= hexDigitVal; + *outPos++ = (CHAR)(BYTE)hexOctetVal; + break; + case '\\': + (*pSizeOut)++; + *outPos++ = '\\'; + break; + case '\0': + FatalError("ParsePatch: Unexpected end of string in escape code"); + break; + default: + FatalError("ParsePatch: Unknown escape code: %s", inPos); + } + default: + *(outPos++) = *(inPos++); + } + inPos++; + } +} diff --git a/src/hex.h b/src/hex.h index 57ab4ac..e6189c8 100644 --- a/src/hex.h +++ b/src/hex.h @@ -1,9 +1,9 @@ -#ifndef HEX_H -#define HEX_H - -#include "common.h" - -DWORD ReadDword(LPCSTR _text); -void TranslateHexInText(LPCSTR _text, LPSTR _result, int _size_result, int* _size_out); - -#endif \ No newline at end of file +#ifndef HEX_H +#define HEX_H + +#include "common.h" + +DWORD ParseAddress(LPCSTR lpszText); +void ParsePatch(LPCSTR lpszText, LPSTR *pDataOut, DWORD *pSizeOut); + +#endif diff --git a/src/hooks/projectg/us852/ranking.c b/src/hooks/projectg/us852/ranking.c new file mode 100644 index 0000000..e4027d9 --- /dev/null +++ b/src/hooks/projectg/us852/ranking.c @@ -0,0 +1,206 @@ +#include "ranking.h" +#include "../../../patch.h" + +typedef struct _stMsgObject { + LPVOID m_pIActor; + DWORD m_id; + DWORD m_com[5]; + DWORD m_time; +} stMsgObject; + +inline stMsgObject MakeMsgObject(LPVOID _pIActor, DWORD _id, DWORD _c0, DWORD _c1, DWORD _c2, DWORD _c3, DWORD _c4) { + stMsgObject obj; + obj.m_pIActor = _pIActor; + obj.m_id = _id; + obj.m_com[0] = _c0; + obj.m_com[1] = _c1; + obj.m_com[2] = _c2; + obj.m_com[3] = _c3; + obj.m_com[4] = _c4; + obj.m_time = 0u; + return obj; +} + +#define GETTHISPOINTERMEMBER(_type_, _addr_, _this_) (_type_ *)(((BYTE *)(_this_)) + (_addr_)) +#define GETVTABLEFN(_this_, _addr_) ((LPVOID)(*((LPCSTR *)(_this_)) + (_addr_))) + +typedef LPVOID (__cdecl *PFNMAKEINSTANCEFRLOGINRSDLG)(void); +typedef LPCSTR (STDCALL *PFNBRASILTRANSLATESTRINGPROC)(LPCSTR _str); +typedef VOID (STDCALL *PFNFRLOGINRSDLGSETSTATEPROC)(DWORD _state); + +typedef LPVOID (STDCALL *PFNFRFORM_INITPROC)(LPVOID, LPVOID, LPVOID, LPCSTR, LPVOID); +typedef LPVOID (STDCALL *PFNIOBJECTDYNAMICCASTPROC)(LPVOID, LPCVOID); +typedef LPVOID (STDCALL *PFNFRFORMLOGINRSDLGSCALARDELETINGDESTRUCTORPROC)(LPVOID, DWORD); + +typedef DWORD64 (STDCALL *PFNCSHAREDDOCISCONTROLSERVERSERVICEPROC)(LPVOID, DWORD64); +typedef VOID (STDCALL *PFNVIRTUALIACTORHANDLEMSGPROC)(LPVOID, stMsgObject *); +typedef VOID (STDCALL *PFNVIRTUALIACTORCLOSEALLDIALOGSPROC)(LPVOID); +typedef BOOL (STDCALL *PFNWNETWORKSYSTEMISCONNECTEDPROC)(LPVOID, DWORD); +typedef BOOL (STDCALL *PFNWNETWORKSYSTEMINITPROC)(LPVOID, DWORD); +typedef BOOL (STDCALL *PFNWNETWORKSYSTEMSENDMESSAGEAPROC)(LPVOID, DWORD, DWORD); +typedef VOID (STDCALL *PFNCONSTRUCTORWSENDPACKETPROC)(LPVOID, DWORD); +typedef VOID (STDCALL *PFNDESTRUCTORWSENDPACKETPROC)(LPVOID); +typedef VOID (STDCALL *PFNWSENDPACKETSENDPROC)(LPVOID, DWORD); +typedef VOID (STDCALL *PFNCLOBBYMAINCLEARTOOLTIPPROC)(LPVOID, LPVOID); +typedef BOOL (STDCALL *PFNFRLOGINRSDLGONLOGINRSDLGRESULTPROC)(LPVOID, DWORD, LPVOID); +typedef BOOL (STDCALL *PFNFRFORMOPENPROC)(LPVOID, LPVOID, DWORD, DWORD, DWORD, DWORD); + +static PFNMAKEINSTANCEFRLOGINRSDLG pMakeInstanceFrLoginRsDlg = NULL; +static PFNFRFORM_INITPROC p_Init = NULL; +static PFNIOBJECTDYNAMICCASTPROC pDynamicCast = NULL; + +static PFNCSHAREDDOCISCONTROLSERVERSERVICEPROC pIsControlServerService = NULL; +static PFNBRASILTRANSLATESTRINGPROC pBrasilTranslateString = NULL; +static PFNWNETWORKSYSTEMISCONNECTEDPROC pIsConnected = NULL; +static PFNWNETWORKSYSTEMINITPROC pInit = NULL; +static PFNWNETWORKSYSTEMSENDMESSAGEAPROC pSendMessageA = NULL; +static PFNCONSTRUCTORWSENDPACKETPROC pConstructorWSendPacket = NULL; +static PFNDESTRUCTORWSENDPACKETPROC pDestructorWSendPacket = NULL; +static PFNWSENDPACKETSENDPROC pSend = NULL; +static PFNCLOBBYMAINCLEARTOOLTIPPROC pClearToolTip = NULL; +static PFNFRLOGINRSDLGSETSTATEPROC pSetState = NULL; +static LPVOID pOnUnderBar_RankingUpThunk = NULL; +static LPVOID pOnLoginRsDlgResultThunk = NULL; + +static void STDCALL OnUnderBar_RankingUp(LPVOID _this); +static BOOL STDCALL OnLoginRsDlgResult(LPVOID _this, DWORD exit_code, LPVOID _pFrForm); + +VOID InitUS852RankingHook() { + pMakeInstanceFrLoginRsDlg = (PFNMAKEINSTANCEFRLOGINRSDLG)0x00AC4680u; + p_Init = (PFNFRFORM_INITPROC)BuildStdcallToThiscallThunk((LPVOID)0x00BCDD40u); + pDynamicCast = (PFNIOBJECTDYNAMICCASTPROC)BuildStdcallToThiscallThunk((LPVOID)0x0047EFF0u); + + pIsControlServerService = + (PFNCSHAREDDOCISCONTROLSERVERSERVICEPROC)BuildStdcallToThiscallThunk((LPVOID)0x0062E4A0); + pBrasilTranslateString = + (PFNBRASILTRANSLATESTRINGPROC)BuildStdcallToThiscallThunk((LPVOID)0x00B3A7E0u); + pIsConnected = + (PFNWNETWORKSYSTEMISCONNECTEDPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3AEB0u); + pInit = (PFNWNETWORKSYSTEMINITPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3AD40u); + pSendMessageA = + (PFNWNETWORKSYSTEMSENDMESSAGEAPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3ADB0u); + pConstructorWSendPacket = + (PFNCONSTRUCTORWSENDPACKETPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3BEE0u); + pDestructorWSendPacket = + (PFNDESTRUCTORWSENDPACKETPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3BCD0u); + pSend = (PFNWSENDPACKETSENDPROC)BuildStdcallToThiscallThunk((LPVOID)0x00A3B340u); + pClearToolTip = (PFNCLOBBYMAINCLEARTOOLTIPPROC)BuildStdcallToThiscallThunk((LPVOID)0x00656770u); + pSetState = (PFNFRLOGINRSDLGSETSTATEPROC)BuildStdcallToThiscallThunk((LPVOID)0x00AC4380u); + pOnUnderBar_RankingUpThunk = BuildThiscallToStdcallThunk((LPVOID)OnUnderBar_RankingUp); + pOnLoginRsDlgResultThunk = BuildThiscallToStdcallThunk((LPVOID)OnLoginRsDlgResult); + InstallHook((LPVOID)0x00655630, (LPVOID)pOnUnderBar_RankingUpThunk); +} + +static LPVOID CreateFormFrLoginRsDlg(LPVOID _pFrWndManager, LPVOID _pFrCmdTarget, LPCSTR _name, + LPVOID _pFrWnd) { + static LPVOID g_pFrLoginRsDlgObject = NULL; + + LPCVOID pFrLoginRsDlg_m_RTTI = (LPCVOID)0x00ECA148u; + + LPVOID pFrLoginRsDlgObject = pMakeInstanceFrLoginRsDlg(); + + LPVOID pFrLoginRsDlgObjectAfter; + + if (pFrLoginRsDlgObject == NULL) { + return NULL; + } + + pFrLoginRsDlgObjectAfter = + p_Init(pFrLoginRsDlgObject, _pFrWndManager, _pFrCmdTarget, _name, _pFrWnd); + + if (pFrLoginRsDlgObjectAfter != NULL) { + // Test to see if it saves the value in tls as it is in pangya + g_pFrLoginRsDlgObject = pFrLoginRsDlgObjectAfter; + + return pDynamicCast(pFrLoginRsDlgObjectAfter, pFrLoginRsDlg_m_RTTI); + } + + if (pFrLoginRsDlgObject != NULL) { + // TODO: need virtual thunk + PFNFRFORMLOGINRSDLGSCALARDELETINGDESTRUCTORPROC pPerformLoginRsDlgScalarDeletingDestructor = + (PFNFRFORMLOGINRSDLGSCALARDELETINGDESTRUCTORPROC)BuildStdcallToThiscallThunk( + GETVTABLEFN(pFrLoginRsDlgObject, 0x04u)); + pPerformLoginRsDlgScalarDeletingDestructor(pFrLoginRsDlgObject, 1u); + FreeMem(pPerformLoginRsDlgScalarDeletingDestructor); + } + + return NULL; +} + +static BOOL STDCALL OnLoginRsDlgResult(LPVOID _this, DWORD exit_code /*I think*/, LPVOID _pFrForm) { + if (_this != NULL) { + *GETTHISPOINTERMEMBER(LPVOID, 0x160u, _this) = NULL; + } + + return TRUE; +} + +static void STDCALL OnUnderBar_RankingUp(LPVOID _this) { + LPCSTR gRankingDisabledServiceMsg = (LPCSTR)0x00D244E8u; + LPVOID gCSharedDocInstance = (*(LPVOID *)0x00E10FACu); + LPVOID gWNetworkSystemInstance = (*(LPVOID *)0x00E3CFD0); + LPVOID gFresh = (*(LPVOID *)0x00EC9640); + LPVOID *FormWndObject = NULL; + + if (pIsControlServerService(gCSharedDocInstance, 0x10000u) != 0u) { + LPCSTR translatedMsgStr; + stMsgObject msg; + + if (_this == NULL) { + return; + } + + translatedMsgStr = pBrasilTranslateString(gRankingDisabledServiceMsg); + msg = MakeMsgObject(_this, 0x23u, (DWORD)translatedMsgStr, 0u, 0u, 0u, 0u); + + // TODO: need virtual thunk + { + PFNVIRTUALIACTORHANDLEMSGPROC pIActorHandleMsg = + (PFNVIRTUALIACTORHANDLEMSGPROC)BuildStdcallToThiscallThunk(GETVTABLEFN(_this, 0x2Cu)); + pIActorHandleMsg(_this, &msg); + FreeMem(pIActorHandleMsg); + return; + } + } + + // TODO: need virtual thunk + { + PFNVIRTUALIACTORCLOSEALLDIALOGSPROC pIActorCloseAllDialogs = + (PFNVIRTUALIACTORCLOSEALLDIALOGSPROC)BuildStdcallToThiscallThunk( + GETVTABLEFN(_this, 0x3Cu)); + pIActorCloseAllDialogs(_this); + FreeMem(pIActorCloseAllDialogs); + } + + if (pIsConnected(gWNetworkSystemInstance, 0) == FALSE) { + pInit(gWNetworkSystemInstance, 3); + pSendMessageA(gWNetworkSystemInstance, 3, 0); + } else { + BYTE WSendPacketObject[40u]; + pConstructorWSendPacket((LPVOID)WSendPacketObject, 0x47u); + pSend((LPVOID)WSendPacketObject, 0); + pDestructorWSendPacket((LPVOID)WSendPacketObject); + } + + FormWndObject = GETTHISPOINTERMEMBER(void *, 0x160u, _this); + + if (*FormWndObject == NULL) { + *FormWndObject = CreateFormFrLoginRsDlg(*GETTHISPOINTERMEMBER(void *, 0x40u, gFresh), + (void *)GETTHISPOINTERMEMBER(void *, 0x18, _this), + "login_gs", NULL); + + pSetState(1u); + + // TODO: need virtual thunk + { + PFNFRFORMOPENPROC pFrFormOpen = + (PFNFRFORMOPENPROC)BuildStdcallToThiscallThunk(GETVTABLEFN(*FormWndObject, 0x64u)); + pFrFormOpen(*FormWndObject, pOnLoginRsDlgResultThunk, 0xFFFFFFE8u, 0, 0, 0x41); + FreeMem(pFrFormOpen); + } + } + + pClearToolTip(_this, NULL); + + return; +} diff --git a/src/hooks/projectg/us852/ranking.h b/src/hooks/projectg/us852/ranking.h new file mode 100644 index 0000000..0e75745 --- /dev/null +++ b/src/hooks/projectg/us852/ranking.h @@ -0,0 +1,8 @@ +#ifndef HOOKS_PROJECTG_US852_RANKING_H +#define HOOKS_PROJECTG_US852_RANKING_H + +#include "../../../common.h" + +VOID InitUS852RankingHook(); + +#endif diff --git a/src/patch.c b/src/patch.c index a90e59c..cb32870 100644 --- a/src/patch.c +++ b/src/patch.c @@ -143,3 +143,77 @@ PVOID HookProc(HMODULE hModule, LPCSTR szName, PVOID pfnTargetProc) { return pfnTrampolineProc; } + +/** + * Creates a thunk that translates from MSVC thiscall to stdcall calling + * convention. The returned function pointer can be used in MSVC ABI vtables. + */ +PVOID BuildThiscallToStdcallThunk(PVOID pfnProc) { + DWORD thunkLen = 9; + DWORD oldProtect; + DWORD relAddr; + PCHAR codeblock; + + // Calculate the jump address. + relAddr = (DWORD)pfnProc - (DWORD)codeblock; + + // Allocate data for thunk. + codeblock = AllocMem(thunkLen); + + // Create calling convention thunk. + // We want to put the this pointer, from ecx, onto the stack at the + // left-most argument. Since we were called via stdcall, return address is + // the current value on the stack, followed by left-most argument. So, pop + // the return address, push the this pointer, and then push the return + // address back onto the stack. + codeblock[0] = 0x58; // pop eax + codeblock[1] = 0x51; // push ecx + codeblock[2] = 0x50; // push eax + codeblock[3] = 0xE9; // jmp + memcpy(&codeblock[4], &relAddr, 4); + + // ...and a return at the end, for good measure. + codeblock[8] = 0xC3; + + // Mark the codeblock as executable. + pVirtualProtect(codeblock, thunkLen, PAGE_EXECUTE_READWRITE, &oldProtect); + + return codeblock; +} + +/** + * Creates a thunk that translates from stdcall to MSVC thiscall calling + * convention. The returned function pointer can be called using stdcall. + */ +PVOID BuildStdcallToThiscallThunk(PVOID pfnProc) { + DWORD thunkLen = 9; + DWORD oldProtect; + DWORD relAddr; + PCHAR codeblock; + + // Calculate the jump address. + relAddr = (DWORD)pfnProc - (DWORD)codeblock; + + // Allocate data for thunk. + codeblock = AllocMem(thunkLen); + + // Create calling convention thunk. + // We want to put the this pointer, from the stack at the left-most + // argument, into ecx. Since we were called via thiscall, return address is + // the current value on the stack, followed by the this pointer argument. + // So, pop the return address, pop the this pointer to ecx, and then push + // the return address back onto the stack. + codeblock[0] = 0x58; // pop eax + codeblock[1] = 0x59; // pop ecx + codeblock[2] = 0x50; // push eax + codeblock[3] = 0xE9; // jmp + memcpy(&codeblock[4], &relAddr, 4); + + // ...and a return at the end, for good measure. + codeblock[8] = 0xC3; + + // Mark the codeblock as executable. + pVirtualProtect(codeblock, thunkLen, PAGE_EXECUTE_READWRITE, &oldProtect); + + return codeblock; +} diff --git a/src/patch.h b/src/patch.h index c4a55e0..630ed1f 100644 --- a/src/patch.h +++ b/src/patch.h @@ -10,5 +10,7 @@ DWORD CountOpcodeBytes(FARPROC fn, DWORD minBytes); PCHAR BuildTrampoline(DWORD fn, DWORD prefixLen); PVOID HookFunc(PVOID pfnProc, PVOID pvTargetProc); PVOID HookProc(HMODULE hModule, LPCSTR szName, PVOID pfnTargetProc); +PVOID BuildThiscallToStdcallThunk(PVOID pfnProc); +PVOID BuildStdcallToThiscallThunk(PVOID pfnProc); #endif diff --git a/src/patch_usa_852.c b/src/patch_usa_852.c deleted file mode 100644 index 37c1b90..0000000 --- a/src/patch_usa_852.c +++ /dev/null @@ -1,143 +0,0 @@ -#include "patch_usa_852.h" - -typedef struct _stMsgObject { - void* m_pIActor; - uint32_t m_id; - uint32_t m_com[5]; - uint32_t m_time; -} stMsgObject; - -inline stMsgObject MakeMsgObject(void* _pIActor, uint32_t _id, uint32_t _c0, uint32_t _c1, uint32_t _c2, uint32_t _c3, uint32_t _c4) { - return (stMsgObject) { _pIActor, _id, _c0, _c1, _c2, _c3, _c4, 0u }; -} - -#define MAKEPROTOTYPETHISFUNCTION(_return_, _fnPrototype_, _type_this_, ...) typedef _return_ (__thiscall *_fnPrototype_)(_type_this_, uint32_t/*register EDX*/, __VA_ARGS__) -#define CALLTHISFUNCTION(_func_, _this_, ...) _func_(_this_, 0u/*register EDX*/, __VA_ARGS__) - -#define MAKEVIRTUALCALL(_fnPrototype_, _addr_, _this_) (*(_fnPrototype_*)(*((uint8_t**)(_this_)) + (_addr_))) -#define CALLTHISVIRTUALCALL(_fnPrototype_, _addr_, _this_, ...) CALLTHISFUNCTION(MAKEVIRTUALCALL(_fnPrototype_, _addr_, _this_), _this_, __VA_ARGS__) - -#define GETTHISPOINTERMEMBER(_type_, _addr_, _this_) (_type_*)(((uint8_t*)(_this_)) + (_addr_)) - -typedef void* (__cdecl *PFNMAKEINSTANCEFRLOGINRSDLG)(void); -typedef const char* (__fastcall *PFNBRASILTRANSLATESTRINGPROC)(const char* _str); -typedef void(__fastcall *PFNFRLOGINRSDLGSETSTATEPROC)(uint32_t _state); - -MAKEPROTOTYPETHISFUNCTION(void*, PFNFRFORM_INITPROC, void*, void*, void*, const char*, void*); -MAKEPROTOTYPETHISFUNCTION(void*, PFNIOBJECTDYNAMICCASTPROC, void*, const void*); -MAKEPROTOTYPETHISFUNCTION(void*, PFNFRFORMLOGINRSDLGSCALARDELETINGDESTRUCTORPROC, void*, uint32_t); - -MAKEPROTOTYPETHISFUNCTION(uint64_t, PFNCSHAREDDOCISCONTROLSERVERSERVICEPROC, void*, uint64_t); -MAKEPROTOTYPETHISFUNCTION(void, PFNVIRTUALIACTORHANDLEMSGPROC, void*, stMsgObject*); -MAKEPROTOTYPETHISFUNCTION(void, PFNVIRTUALIACTORCLOSEALLDIALOGSPROC, void*); -MAKEPROTOTYPETHISFUNCTION(BOOL, PFNWNETWORKSYSTEMISCONNECTEDPROC, void*, uint32_t); -MAKEPROTOTYPETHISFUNCTION(BOOL, PFNWNETWORKSYSTEMINITPROC, void*, uint32_t); -MAKEPROTOTYPETHISFUNCTION(BOOL, PFNWNETWORKSYSTEMSENDMESSAGEAPROC, void*, uint32_t, uint32_t); -MAKEPROTOTYPETHISFUNCTION(void, PFNCONSTRUCTORWSENDPACKETPROC, void*, uint32_t); -MAKEPROTOTYPETHISFUNCTION(void, PFNDESTRUCTORWSENDPACKETPROC, void*); -MAKEPROTOTYPETHISFUNCTION(void, PFNWSENDPACKETSENDPROC, void*, uint32_t); -MAKEPROTOTYPETHISFUNCTION(void, PFNCLOBBYMAINCLEARTOOLTIPPROC, void*, void*); -MAKEPROTOTYPETHISFUNCTION(BOOL, PFNFRLOGINRSDLGONLOGINRSDLGRESULTPROC, void*, int32_t, void*); -MAKEPROTOTYPETHISFUNCTION(BOOL, PFNFRFORMOPENPROC, void*, PFNFRLOGINRSDLGONLOGINRSDLGRESULTPROC, uint32_t, uint32_t, uint32_t, uint32_t); - -void* CreateFormFrLoginRsDlg(void* _pFrWndManager, void* _pFrCmdTarget, const char* _name, void* _pFrWnd) { - - static void* g_pFrLoginRsDlgObject = NULL; - - PFNMAKEINSTANCEFRLOGINRSDLG pMakeInstanceFrLoginRsDlg = (PFNMAKEINSTANCEFRLOGINRSDLG)0x00AC4680u; - PFNFRFORM_INITPROC p_Init = (PFNFRFORM_INITPROC)0x00BCDD40u; - PFNIOBJECTDYNAMICCASTPROC pDynamicCast = (PFNIOBJECTDYNAMICCASTPROC)0x0047EFF0u; - - const void* pFrLoginRsDlg_m_RTTI = (const void*)0x00ECA148u; - - void* pFrLoginRsDlgObject = pMakeInstanceFrLoginRsDlg(); - - if (pFrLoginRsDlgObject == NULL) - return NULL; - - void* pFrLoginRsDlgObjectAfter = CALLTHISFUNCTION(p_Init, pFrLoginRsDlgObject, _pFrWndManager, _pFrCmdTarget, _name, _pFrWnd); - - if (pFrLoginRsDlgObjectAfter != NULL) { - - // Test to see if it saves the value in tls as it is in pangya - g_pFrLoginRsDlgObject = pFrLoginRsDlgObjectAfter; - - return CALLTHISFUNCTION(pDynamicCast, pFrLoginRsDlgObjectAfter, pFrLoginRsDlg_m_RTTI); - } - - if (pFrLoginRsDlgObject != NULL) - CALLTHISVIRTUALCALL(PFNFRFORMLOGINRSDLGSCALARDELETINGDESTRUCTORPROC, 0x04u, pFrLoginRsDlgObject, 1u); - - return NULL; -} - -BOOL __thiscall OnLoginRsDlgResult(void* _this, uint32_t _EDX, int32_t exit_code/*I think*/, void* _pFrForm) { - (_EDX); // UNREFERENCED_PARAMETER - - if (_this != NULL) - *GETTHISPOINTERMEMBER(void*, 0x160u, _this) = NULL; - - return TRUE; -} - -void __thiscall OnUnderBar_RankingUp(void* _this) { - - PFNCSHAREDDOCISCONTROLSERVERSERVICEPROC pIsControlServerService = (PFNCSHAREDDOCISCONTROLSERVERSERVICEPROC)0x0062E4A0; - PFNBRASILTRANSLATESTRINGPROC pBrasilTranslateString = (PFNBRASILTRANSLATESTRINGPROC)0x00B3A7E0u; - PFNWNETWORKSYSTEMISCONNECTEDPROC pIsConnected = (PFNWNETWORKSYSTEMISCONNECTEDPROC)0x00A3AEB0u; - PFNWNETWORKSYSTEMINITPROC pInit = (PFNWNETWORKSYSTEMINITPROC)0x00A3AD40u; - PFNWNETWORKSYSTEMSENDMESSAGEAPROC pSendMessageA = (PFNWNETWORKSYSTEMSENDMESSAGEAPROC)0x00A3ADB0u; - PFNCONSTRUCTORWSENDPACKETPROC pConstructorWSendPacket = (PFNCONSTRUCTORWSENDPACKETPROC)0x00A3BEE0u; - PFNDESTRUCTORWSENDPACKETPROC pDestructorWSendPacket = (PFNDESTRUCTORWSENDPACKETPROC)0x00A3BCD0u; - PFNWSENDPACKETSENDPROC pSend = (PFNWSENDPACKETSENDPROC)0x00A3B340u; - PFNCLOBBYMAINCLEARTOOLTIPPROC pClearToolTip = (PFNCLOBBYMAINCLEARTOOLTIPPROC)0x00656770u; - PFNFRLOGINRSDLGSETSTATEPROC pSetState = (PFNFRLOGINRSDLGSETSTATEPROC)0x00AC4380u; - - const char* gRankingDisabledServiceMsg = (const char*)0x00D244E8u; - void* gCSharedDocInstance = (*(void**)0x00E10FACu); - void* gWNetworkSystemInstance = (*(void**)0x00E3CFD0); - void* gFresh = (*(void**)0x00EC9640); - - if (CALLTHISFUNCTION(pIsControlServerService, gCSharedDocInstance, 0x10000u) != 0u) { - - if (_this == NULL) - return; - - stMsgObject msg = MakeMsgObject(_this, 0x23u, (uint32_t)pBrasilTranslateString(gRankingDisabledServiceMsg), 0u, 0u, 0u, 0u); - - CALLTHISVIRTUALCALL(PFNVIRTUALIACTORHANDLEMSGPROC, 0x2Cu, _this, &msg); - - return; - } - - CALLTHISVIRTUALCALL(PFNVIRTUALIACTORCLOSEALLDIALOGSPROC, 0x3Cu, _this); - - if (CALLTHISFUNCTION(pIsConnected, gWNetworkSystemInstance, 0) == FALSE) { - - CALLTHISFUNCTION(pInit, gWNetworkSystemInstance, 3); - CALLTHISFUNCTION(pSendMessageA, gWNetworkSystemInstance, 3, 0); - - }else { - - uint8_t WSendPacketObject[40u]; - - CALLTHISFUNCTION(pConstructorWSendPacket, (void*)WSendPacketObject, 0x47u); - CALLTHISFUNCTION(pSend, (void*)WSendPacketObject, 0); - CALLTHISFUNCTION(pDestructorWSendPacket, (void*)WSendPacketObject); - } - - void** FormWndObject = GETTHISPOINTERMEMBER(void*, 0x160u, _this); - - if (*FormWndObject == NULL) { - - *FormWndObject = CreateFormFrLoginRsDlg(*GETTHISPOINTERMEMBER(void*, 0x40u, gFresh), (void*)GETTHISPOINTERMEMBER(void*, 0x18, _this), "login_gs", NULL); - - pSetState(1u); - - CALLTHISVIRTUALCALL(PFNFRFORMOPENPROC, 0x64u, *FormWndObject, OnLoginRsDlgResult, 0xFFFFFFE8u, 0, 0, 0x41); - } - - CALLTHISFUNCTION(pClearToolTip, _this, NULL); - - return; -} diff --git a/src/patch_usa_852.h b/src/patch_usa_852.h deleted file mode 100644 index 23b3b5a..0000000 --- a/src/patch_usa_852.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef PATCH_USA_852_H -#define PATCH_USA_852_H - -#include "common.h" -#include - -// C not have __thiscall -#define __thiscall __fastcall - -void __thiscall OnUnderBar_RankingUp(void* _this); - -#endif