Skip to content

Commit

Permalink
new std patching
Browse files Browse the repository at this point in the history
  • Loading branch information
JaanDev committed Jun 29, 2024
1 parent fcf42f4 commit f1e4b97
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 18 deletions.
186 changes: 184 additions & 2 deletions api/stringPatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ namespace gdl {
req.operands[0].reg.value = instruction.operands[0].reg.value;

req.operands[1].type = ZYDIS_OPERAND_TYPE_IMMEDIATE;
req.operands[1].imm.u = (uintptr_t)strings.back();
req.operands[1].imm.u = (uintptr_t)str;

ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH];
ZyanUSize encoded_length = sizeof(encoded_instruction);
Expand Down Expand Up @@ -188,6 +188,186 @@ namespace gdl {
return patchStdString(str, base::get() + allocSizeInsn, base::get() + sizeInsn, base::get() + capacityInsn, assignInsns);
}

bool patchStdString2(const char* str, const std::vector<PatchBlock>& blocks, uintptr_t bufAssignInsn) {
log::debug("===================================================");

if (blocks.size() == 0 || blocks[0].len < 5) {
log::error("invalid block data");
return false;
}

// disasm the assign insn
ZydisDisassembledInstruction instruction;
if (ZYAN_FAILED(ZydisDisassembleIntel(ZYDIS_MACHINE_MODE_LONG_64, bufAssignInsn, (void*)bufAssignInsn, ZYDIS_MAX_INSTRUCTION_LENGTH, &instruction))) {
log::error("failed to disassemble the instruction at 0x{:X}", bufAssignInsn);
return false;
}

log::debug("{}", instruction.text);

if (instruction.operands[0].type != ZYDIS_OPERAND_TYPE_MEMORY) {
log::error("not mem op");
return false;
}

auto size = strlen(str);
auto capacity = std::max(0x10ull, size + 1);

// call:
// mov rcx, <cap>
// call sub_140039BE0
// add rsp, 8 ; stack pointer is less now bc we called
// <buf assign insn>
// mov [<buf mem op> + 16], <size>
// mov [<buf mem op> + 24], <cap>
// sub rsp, 8 ; return the stack pointer
// mov r8, <size+1>
// mov rdx, <str>
// mov rcx, rax
// call memcpy
// ret
// jmp <offset> ; to skip orig code

// encode size insn
ZydisEncoderRequest req;

memset(&req, 0, sizeof(req));
req.mnemonic = ZYDIS_MNEMONIC_MOV;
req.machine_mode = ZYDIS_MACHINE_MODE_LONG_64;
req.operand_count = 2;

req.operands[0].type = ZYDIS_OPERAND_TYPE_MEMORY;
req.operands[0].mem.base = instruction.operands[0].mem.base;
req.operands[0].mem.displacement = instruction.operands[0].mem.disp.value + 16;
req.operands[0].mem.size = 8;

req.operands[1].type = ZYDIS_OPERAND_TYPE_IMMEDIATE;
req.operands[1].imm.u = size;

ZyanU8 encoded_instruction1[ZYDIS_MAX_INSTRUCTION_LENGTH];
ZyanUSize encoded_length1 = sizeof(encoded_instruction1);

if (ZYAN_FAILED(ZydisEncoderEncodeInstruction(&req, encoded_instruction1, &encoded_length1))) {
log::error("Failed to encode instruction [1]!");
return false;
}

{
std::string zvzv = "";
for (auto i = 0; i < encoded_length1; i++) {
zvzv += std::format("{:02X} ", encoded_instruction1[i]);
}
log::debug("{}", zvzv);
}

memset(&req, 0, sizeof(req));
req.mnemonic = ZYDIS_MNEMONIC_MOV;
req.machine_mode = ZYDIS_MACHINE_MODE_LONG_64;
req.operand_count = 2;

req.operands[0].type = ZYDIS_OPERAND_TYPE_MEMORY;
req.operands[0].mem.base = instruction.operands[0].mem.base;
req.operands[0].mem.displacement = instruction.operands[0].mem.disp.value + 24;
req.operands[0].mem.size = 8;

req.operands[1].type = ZYDIS_OPERAND_TYPE_IMMEDIATE;
req.operands[1].imm.u = capacity;

ZyanU8 encoded_instruction2[ZYDIS_MAX_INSTRUCTION_LENGTH];
ZyanUSize encoded_length2 = sizeof(encoded_instruction2);

if (ZYAN_FAILED(ZydisEncoderEncodeInstruction(&req, encoded_instruction2, &encoded_length2))) {
log::error("Failed to encode instruction [2]!");
return false;
}

{
std::string zvzv = "";
for (auto i = 0; i < encoded_length2; i++) {
zvzv += std::format("{:02X} ", encoded_instruction2[i]);
}
log::debug("{}", zvzv);
}

// clang-format off
auto tramp = PageManager::get().getMemoryForSize(0x2e + instruction.info.length + encoded_length1 + encoded_length2);
auto offset = 0;

tramp[offset] = 0x48; tramp[offset+1] = 0xc7; tramp[offset+2] = 0xc1;
*(uint32_t*)(tramp + offset + 3) = capacity;
offset += 7;

tramp[offset] = 0xe8;
auto relAddr = (uint64_t)(base::get() + 0x39BE0) - ((uint64_t)tramp + offset + 5);
*(int32_t*)(tramp + offset + 1) = (int32_t)relAddr;
offset += 5;

tramp[offset] = 0x48; tramp[offset+1] = 0x83; tramp[offset+2] = 0xc4; tramp[offset+3] = 0x08;
offset += 4;

memcpy(tramp + offset, (void*)bufAssignInsn, instruction.info.length);
offset += instruction.info.length;

memcpy(tramp + offset, encoded_instruction1, encoded_length1);
offset += encoded_length1;

memcpy(tramp + offset, encoded_instruction2, encoded_length2);
offset += encoded_length2;

tramp[offset] = 0x48; tramp[offset+1] = 0x83; tramp[offset+2] = 0xec; tramp[offset+3] = 0x08;
offset += 4;

tramp[offset] = 0x49; tramp[offset+1] = 0xc7; tramp[offset+2] = 0xc0;
*(uint32_t*)(tramp + offset + 3) = size + 1;
offset += 7;

tramp[offset] = 0x48; tramp[offset+1] = 0xba;
*(uintptr_t*)(tramp + offset + 2) = (uintptr_t)str;
offset += 10;

tramp[offset] = 0x48; tramp[offset+1] = 0x89; tramp[offset+2] = 0xc1;
offset += 3;

tramp[offset] = 0xe8;
relAddr = (uint64_t)(base::get() + 0x4A49F0) - ((uint64_t)tramp + offset + 5);
*(int32_t*)(tramp + offset + 1) = (int32_t)relAddr;
offset += 5;

tramp[offset] = 0xc3;
// clang-format on

{
std::string zvzv = "";
for (auto i = 0; i < offset + 1; i++) {
zvzv += std::format("{:02X} ", tramp[i]);
}
log::debug("{}", zvzv);
}

uint8_t patch[] = {0xe8, 0x00, 0x00, 0x00, 0x00, 0xe9, 0x00, 0x00, 0x00, 0x00}; // call, jmp

relAddr = (uint64_t)tramp - ((uint64_t)blocks[0].start + 5);
*(int32_t*)(patch + 1) = (int32_t)relAddr;

*(int32_t*)(patch + 6) = blocks[0].len - 5 - 5;

if (auto p = Mod::get()->patch((void*)blocks[0].start, ByteVector(patch, patch + sizeof(patch))); p.isErr()) {
log::error("patch error {}", p.error());
return false;
}

for (auto i = 1; i < blocks.size(); i++) {
uint8_t patch2[] = {0xe9, 0x00, 0x00, 0x00, 0x00};
*(int32_t*)(patch2 + 1) = blocks[i].len - 5;
if (auto p = Mod::get()->patch((void*)blocks[i].start, ByteVector(patch2, patch2 + sizeof(patch2))); p.isErr()) {
log::error("patch error {}", p.error());
return false;
}
}

return true;
}

bool patchStdString(const char* str_, uintptr_t allocSizeInsn, uintptr_t sizeInsn, uintptr_t capacityInsn, std::vector<uintptr_t> assignInsns) {
// clang-format off
// 1. patch the alloc_data function
Expand Down Expand Up @@ -226,7 +406,9 @@ namespace gdl {
ZydisDisassembledInstruction disasmInsn;
auto stringLen = strlen(str);
auto stringLenFull = stringLen + 1; // with \0
auto capacity = std::max(stringLenFull, 0x10ull); // use bigger capacity to ensure that smaller strings (<= 15 bytes in length) work properly (if new string is < 16 chars and the orig is >= 16 chars)
auto capacity =
std::max(stringLenFull,
0x10ull); // use bigger capacity to ensure that smaller strings (<= 15 bytes in length) work properly (if new string is < 16 chars and the orig is >= 16 chars)
auto allocatingSize = capacity + 1;

log::debug("string len {}, full {}, capacity {}, allocatingSize {}", stringLen, stringLenFull, capacity, allocatingSize);
Expand Down
2 changes: 1 addition & 1 deletion api/stringPatch.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ namespace gdl {
};

// all addrs are absolute
[[nodiscard]] bool patchStdString2(const char* str, const std::vector<PatchBlock>& blocks);
[[nodiscard]] bool patchStdString2(const char* str, const std::vector<PatchBlock>& blocks, uintptr_t bufAssignInsn);
#elif defined(GEODE_IS_ANDROID32)
[[nodiscard]] GDLAPI_DLL bool patchString(const uintptr_t srcAddr, const char* str);
#endif
Expand Down
1 change: 1 addition & 0 deletions src/hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class $modify(MultilineBitmapFont) {
}

bool initWithFont(const char* p0, gd::string p1, float p2, float p3, cocos2d::CCPoint p4, int p5, bool colorsDisabled) {
log::debug("string!!! {} {} {}", p1.size(), p1.capacity(), p1.c_str());
m_fields->m_textScale = p2;
m_fields->m_fontName = p0;
m_fields->m_maxWidth = p3;
Expand Down
2 changes: 1 addition & 1 deletion src/menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class $modify(GDLOptionsLayer, OptionsLayer) {
return;

auto languageBtn = CCMenuItemSpriteExtra::create(ButtonSprite::create("language"_gdl, "goldFont.fnt", "GJ_button_01.png"), this, SEL_MenuHandler(&GDLOptionsLayer::onLanguage));
languageBtn->setPosition(buttonsMenu->getContentWidth() / 2, -115);
languageBtn->setPosition(0, -115);
buttonsMenu->addChild(languageBtn);
}

Expand Down
28 changes: 14 additions & 14 deletions src/patches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ void patches::fixCyrillicP() {
}

void patches::patchStrings() {
static std::vector<std::string> strings;

for (auto p : Mod::get()->getPatches()) {
if (p->isEnabled()) {
auto res = p->disable().err();
if (res != std::nullopt) {
log::warn("Failed to disable patch at {}, error: {}", p->getAddress() - base::get(), res);
}
}
}

patches::fixCyrillicP();

#ifdef GEODE_IS_WINDOWS64
// bool res3;
// res3 = gdl::patchCString(base::get() + 0x3151D5, "Привет, мир!");
Expand All @@ -43,23 +56,10 @@ void patches::patchStrings() {
// log::debug("res {}", res2);

bool res2;
res2 = gdl::patchStdString2("Hello world! This is a long enough string!", {{base::get() + 0x31561F, 0x4F}});
res2 = gdl::patchStdString2("Hello world! This is a long enough string!", {{base::get() + 0x31561F, 0x50}}, base::get() + 0x31562A);
log::debug("res {}", res2);
#endif

static std::vector<std::string> strings;

for (auto p : Mod::get()->getPatches()) {
if (p->isEnabled()) {
auto res = p->disable().err();
if (res != std::nullopt) {
log::warn("Failed to disable patch at {}, error: {}", p->getAddress() - base::get(), res);
}
}
}

patches::fixCyrillicP();

auto languageID = gdl::getCurrentLanguage();

if (languageID == gdl::GDL_ENGLISH) {
Expand Down

0 comments on commit f1e4b97

Please sign in to comment.