diff --git a/GUI/host/textthread.cpp b/GUI/host/textthread.cpp index df3b796..7377542 100644 --- a/GUI/host/textthread.cpp +++ b/GUI/host/textthread.cpp @@ -58,7 +58,10 @@ void TextThread::Flush() std::vector sentences; queuedSentences->swap(sentences); for (auto& sentence : sentences) + { + sentence.erase(std::remove(sentence.begin(), sentence.end(), L'\0')); if (Output(*this, sentence)) storage->append(sentence); + } std::scoped_lock lock(bufferMutex); if (buffer.empty()) return; diff --git a/GUI/host/util.cpp b/GUI/host/util.cpp index 7b1be64..3b77ab5 100644 --- a/GUI/host/util.cpp +++ b/GUI/host/util.cpp @@ -27,6 +27,13 @@ namespace } RCode.erase(0, 1); + // [null_length<] + if (std::regex_search(RCode, match, std::wregex(L"^([0-9]+)<"))) + { + hp.null_length = std::stoi(match[1]); + RCode.erase(0, match[0].length()); + } + // [codepage#] if (std::regex_search(RCode, match, std::wregex(L"^([0-9]+)#"))) { @@ -104,6 +111,13 @@ namespace } HCode.erase(0, 1); + // [null_length<] + if ((hp.type & USING_STRING) && std::regex_search(HCode, match, std::wregex(L"^([0-9]+)<"))) + { + hp.null_length = std::stoi(match[1]); + HCode.erase(0, match[0].length()); + } + // [N] if (HCode[0] == L'N') { @@ -181,10 +195,12 @@ namespace if (hp.type & USING_UNICODE) { RCode << "Q"; + if (hp.null_length != 0) RCode << hp.null_length << "<"; } else { RCode << "S"; + if (hp.null_length != 0) RCode << hp.null_length << "<"; if (hp.codepage != 0) RCode << hp.codepage << "#"; } @@ -213,6 +229,9 @@ namespace else if (hp.type & BIG_ENDIAN) HCode << "A"; else HCode << "B"; } + + if (hp.null_length != 0) HCode << hp.null_length << "<"; + if (hp.type & NO_CONTEXT) HCode << "N"; if (hp.text_fun || hp.filter_fun || hp.hook_fun) HCode << "X"; // no AGTH equivalent @@ -287,7 +306,8 @@ namespace Util std::optional StringToWideString(const std::string& text, UINT encoding) { std::vector buffer(text.size() + 1); - if (MultiByteToWideChar(encoding, 0, text.c_str(), -1, buffer.data(), buffer.size())) return buffer.data(); + if (int length = MultiByteToWideChar(encoding, 0, text.c_str(), text.size() + 1, buffer.data(), buffer.size())) + return std::wstring(buffer.data(), length - 1); return {}; } diff --git a/include/types.h b/include/types.h index 1763eea..3b7248b 100644 --- a/include/types.h +++ b/include/types.h @@ -29,7 +29,8 @@ struct HookParam int offset, // offset of the data in the memory index, // deref_offset1 split, // offset of the split character - split_index; // deref_offset2 + split_index, // deref_offset2 + null_length; union { wchar_t module[MAX_MODULE_SIZE]; diff --git a/text.cpp b/text.cpp index 1167fd4..f0084fd 100644 --- a/text.cpp +++ b/text.cpp @@ -21,15 +21,16 @@ const char* CODE_INFODUMP = u8R"(Search for text S[codepage#]text OR Enter read code -R{S|Q|V}[codepage#][*deref_offset]@addr +R{S|Q|V}[null_length<][codepage#][*deref_offset]@addr OR Enter hook code -H{A|B|W|S|Q|V}[N][codepage#]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]] +H{A|B|W|S|Q|V}[null_length<][N][codepage#]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]] All numbers except codepage in hexadecimal Default codepage is 932 (Shift-JIS) but this can be changed in settings A/B: codepage char little/big endian W: UTF-16 char S/Q/V: codepage/UTF-16/UTF-8 string +null_length: length of null terminator used for string Negatives for data/split offset refer to registers -4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI * means dereference pointer+deref_offset)"; diff --git a/texthook/texthook.cc b/texthook/texthook.cc index 6ea9357..d803db4 100644 --- a/texthook/texthook.cc +++ b/texthook/texthook.cc @@ -229,14 +229,14 @@ DWORD WINAPI TextHook::Reader(LPVOID hookPtr) { TextHook* This = (TextHook*)hookPtr; BYTE buffer[TEXT_BUFFER_SIZE] = {}; - int changeCount = 0, dataLen = 0; + int changeCount = 0, dataLen = 1; __try { uint64_t currentAddress = This->address; while (WaitForSingleObject(This->readerEvent, 500) == WAIT_TIMEOUT) { if (This->hp.type & DATA_INDIRECT) currentAddress = *(uintptr_t*)This->address + This->hp.index; - if (memcmp(buffer, (void*)currentAddress, dataLen + 2) == 0) + if (memcmp(buffer, (void*)currentAddress, dataLen) == 0) { changeCount = 0; continue; @@ -248,10 +248,8 @@ DWORD WINAPI TextHook::Reader(LPVOID hookPtr) break; } - if (This->hp.type & USING_UNICODE) dataLen = wcslen((wchar_t*)currentAddress) * 2; - else dataLen = strlen((char*)currentAddress); - if (dataLen > TEXT_BUFFER_SIZE - 2) dataLen = TEXT_BUFFER_SIZE - 2; - memcpy(buffer, (void*)currentAddress, dataLen + 2); + dataLen = min(This->HookStrlen((BYTE*)currentAddress), TEXT_BUFFER_SIZE); + memcpy(buffer, (void*)currentAddress, dataLen); TextOutput({ GetCurrentProcessId(), This->address, 0, 0 }, buffer, dataLen); } } @@ -308,10 +306,7 @@ int TextHook::GetLength(uintptr_t base, uintptr_t in) break; //len == -1 then continue to case 0. case 0: - if (hp.type & USING_UNICODE) - len = wcslen((const wchar_t *)in) << 1; - else - len = strlen((const char *)in); + len = HookStrlen((BYTE*)in); break; case 1: if (hp.type & USING_UNICODE) @@ -328,4 +323,12 @@ int TextHook::GetLength(uintptr_t base, uintptr_t in) return max(0, len); } +int TextHook::HookStrlen(BYTE* data) +{ + BYTE* orig = data; + for (int nullsRemaining = hp.null_length ? hp.null_length : hp.type & USING_UNICODE ? 2 : 1; nullsRemaining >= 0; ++data) + if (*data == 0) nullsRemaining -= 1; + return data - orig; +} + // EOF diff --git a/texthook/texthook.h b/texthook/texthook.h index 2295a5a..e6dd620 100644 --- a/texthook/texthook.h +++ b/texthook/texthook.h @@ -34,6 +34,7 @@ private: bool InsertReadCode(); void Send(uintptr_t dwDatabase); int GetLength(uintptr_t base, uintptr_t in); // jichi 12/25/2013: Return 0 if failed + int HookStrlen(BYTE* data); void RemoveHookCode(); void RemoveReadCode();