Skip to content

Commit

Permalink
at least some paching std string works
Browse files Browse the repository at this point in the history
  • Loading branch information
JaanDev committed Jun 19, 2024
1 parent 2dbd3d8 commit c7632f6
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 27 deletions.
123 changes: 107 additions & 16 deletions api/stringPatch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ class PageManager {
}
}

void* getMemoryForSize(size_t size) {
auto& page = getPageForSize(size);
auto ret = page.getOffsetAddress();
page.reserve(size);
return ret;
}

private:
Page& allocNewPage() {
SYSTEM_INFO sysInfo;
Expand Down Expand Up @@ -135,8 +142,9 @@ namespace gdl {

// 1.

auto& page = PageManager::get().getPageForSize(14); // mov + lea + ret = 14 bytes
auto pageAddr = page.getOffsetAddress();
// auto& page = PageManager::get().getPageForSize(14); // mov + lea + ret = 14 bytes
// auto pageAddr = page.getOffsetAddress();
auto pageAddr = PageManager::get().getMemoryForSize(14);

uint8_t buffer[14];

Expand Down Expand Up @@ -181,7 +189,7 @@ namespace gdl {
buffer[13] = 0xc3;

memcpy(pageAddr, buffer, sizeof(buffer));
page.reserve(14);
// page.reserve(14);

// 3.

Expand Down Expand Up @@ -236,12 +244,16 @@ namespace gdl {

// =========================================

std::vector<std::shared_ptr<Patch>> patches;

// 0
ZydisDisassembledInstruction disasmInsn;
auto stringLen = strlen(str);
auto stringLenFull = stringLen + 1; // with \0
auto capacity = (int)(stringLen * 1.25f);
auto allocatingSize = capacity + 1;

log::debug("string len 0x{:X}, full 0x{:X}", stringLen, stringLenFull);
log::debug("string len {}, full {}, capacity {}, allocatingSize {}", stringLen, stringLenFull, capacity, allocatingSize);

// 1.1
{
Expand All @@ -259,7 +271,7 @@ namespace gdl {
}

if (disasmInsn.info.length < 5) { // mov ecx, <size> wouldn't fit
if (stringLenFull > 0x7f) {
if (allocatingSize > 0x7f) {
// uh oh, we don't have enough space
// we need to allocate some bytes and `call` them

Expand All @@ -278,7 +290,7 @@ namespace gdl {

req.operands[1].type = ZYDIS_OPERAND_TYPE_MEMORY;
req.operands[1].mem.base = disasmInsn.operands[1].mem.base;
req.operands[1].mem.displacement = stringLenFull;
req.operands[1].mem.displacement = allocatingSize;
req.operands[1].mem.size = 8;

ZyanU8 encoded_instruction[ZYDIS_MAX_INSTRUCTION_LENGTH];
Expand All @@ -294,14 +306,16 @@ namespace gdl {
zvzv += std::format("{:02X} ", encoded_instruction[i]);
}
log::debug("{}", zvzv);

patches.push_back(Patch::create((void*)allocSizeInsn, ByteVector(encoded_instruction, encoded_instruction + encoded_length)));
}
} else {
log::error("todo");
return false;
}
}

auto helpme = [&](uintptr_t insnAddr, size_t immValue) {
auto helpme = [&disasmInsn, &patches](uintptr_t insnAddr, size_t immValue) {
if (ZYAN_FAILED(ZydisDisassembleIntel(ZYDIS_MACHINE_MODE_LONG_64, insnAddr, (void*)insnAddr, ZYDIS_MAX_INSTRUCTION_LENGTH, &disasmInsn))) {
log::error("failed to disasm instruction at 0x{:X}", insnAddr);
return false;
Expand Down Expand Up @@ -352,6 +366,8 @@ namespace gdl {
if (encoded_length < disasmInsn.info.length) {
log::error("we dont fit =(");
return false;
} else {
patches.push_back(Patch::create((void*)insnAddr, ByteVector(encoded_instruction, encoded_instruction + encoded_length)));
}

return true;
Expand All @@ -362,26 +378,101 @@ namespace gdl {
return false;

// 1.3
if (!helpme(capacityInsn, stringLenFull))
if (!helpme(capacityInsn, capacity))
return false;

// 2
{
if (assignInsns.size() == 0) {
log::error("you didnt specify any assign instructions");
return false;
}

// 2.3
bool hasCallPatch = false;
uintptr_t callPatchAddr = 0;

for (auto i = 0; i < assignInsns.size(); i++) {
auto addr = assignInsns[i];

if (ZYAN_FAILED(ZydisDisassembleIntel(ZYDIS_MACHINE_MODE_LONG_64, addr, (void*)addr, ZYDIS_MAX_INSTRUCTION_LENGTH, &disasmInsn))) {
log::error("failed to disasm instruction for step 2.3 at 0x{:X}", addr);
return false;
}

log::debug("{}", disasmInsn.text);
log::debug("{} ops, {} {}", disasmInsn.info.operand_count, (int)disasmInsn.operands[0].type, (int)disasmInsn.operands[1].type);

if (disasmInsn.info.operand_count != 2) {
log::error("not 2 operands");
return false;
}

auto instructionLength = disasmInsn.info.length;

if (!hasCallPatch) { // add the call patch
if (instructionLength >= 5) { // 5 bytes to fit the `call` instruction
callPatchAddr = addr;
hasCallPatch = true;

if (instructionLength > 5) { // fill the rest of it with nops
patches.push_back(Patch::create((void*)(addr + 5), ByteVector(instructionLength - 5, 0x90)));
}

continue;
}
}

patches.push_back(Patch::create((void*)addr, ByteVector(instructionLength, 0x90)));
}

if (!hasCallPatch) {
log::error("all instructions cant fit call :(");
return false;
}

// 2.1
// clang-format off
uint8_t bytes[] = {
0x49, 0xC7, 0xC0, 0x44, 0x33, 0x22, 0x11, // mov r8, string len
0x48, 0xBA, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, // mov rdx, string addr
0x49, 0xC7, 0xC0, 0x00, 0x00, 0x00, 0x00, // mov r8, string len
0x48, 0xBA, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // mov rdx, string addr
0x48, 0x89, 0xC1, // mov rcx, rax
0xE8, 0xA3, 0xE5, 0x21, 0x00 // call memcpy
0xE8, 0x00, 0x00, 0x00, 0x00, // call memcpy
0xC3 // ret
};
// clang-format on
*(uint32_t*)(bytes + 3) = static_cast<uint32_t>(stringLenFull);
*(uintptr_t*)(bytes + 9) = (uintptr_t)str;

// 2.2
// huh
auto pageAddr = PageManager::get().getMemoryForSize(sizeof(bytes));

// 2.3
// aaaaaaaaaaaaaaaaa
const auto memcpyAddress = base::get() + 0x4A49F0;
const auto relAddrMemcpy = (uint64_t)memcpyAddress - ((uint64_t)pageAddr + 20 + 5);
log::debug("0x{:X}", relAddrMemcpy);
*(int32_t*)(bytes + 21) = (int32_t)relAddrMemcpy;
memcpy(pageAddr, bytes, sizeof(bytes));

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

uint8_t callBytes[] = {0xe8, 0x00, 0x00, 0x00, 0x00};
const auto relAddr = (uint64_t)pageAddr - ((uint64_t)callPatchAddr + sizeof(callBytes));
*(int32_t*)(callBytes + 1) = (int32_t)relAddr;
patches.push_back(Patch::create((void*)callPatchAddr, ByteVector(callBytes, callBytes + sizeof(callBytes))));

for (auto p : patches) {
const auto& b = p->getBytes();

std::string zvzv = "";
for (auto i = 0; i < b.size(); i++) {
zvzv += std::format("{:02X} ", b[i]);
}

auto patchRes = Mod::get()->claimPatch(p);
log::debug("Patch at 0x{:X}: {} {}", p->getAddress(), zvzv, patchRes.isOk());
}
}

return true;
Expand Down
21 changes: 10 additions & 11 deletions src/patches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,19 @@ void patchStrings() {
if (res.isErr())
log::warn("Failed to patch the Рррр fix ({}), be aware that CommentCell with cyrillic comments may crash!", res.error());

// bool res;
// res = gdl::patchCString(base::get() + 0x3151D5, "Привет, мир!");
// log::debug("{}", res);
// res = gdl::patchCString(base::get() + 0x350598, "Привет, мир 2!");
// log::debug("{}", res);
// res = gdl::patchCString(base::get() + 0x3505F1, "Привет, мир 3!");
// log::debug("{}", res);
// res = gdl::patchStdString(base::get() + 0x315641, "Hello world!");
// log::debug("{}", res);
#ifdef GEODE_IS_WINDOWS64
// bool res3;
// res3 = gdl::patchCString(base::get() + 0x3151D5, "Привет, мир!");
// log::debug("{}", res3);
// res3 = gdl::patchCString(base::get() + 0x350598, "Привет, мир 2!");
// log::debug("{}", res3);
// res3 = gdl::patchCString(base::get() + 0x3505F1, "Привет, мир 3!");
// log::debug("{}", res3);

bool res2;

res2 = gdl::patchStdStringRel("Hello world!", 0x31561F, 0x31562F, 0x315638, {});
res2 = gdl::patchStdStringRel("This is a very very long string!", 0x31561F, 0x31562F, 0x315638, {0x315641, 0x315648, 0x31564B, 0x315652, 0x315656, 0x31565C, 0x31565F, 0x315666, 0x31566A});
log::debug("res {}", res2);
#endif

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

Expand Down

0 comments on commit c7632f6

Please sign in to comment.