Skip to content

Commit

Permalink
better solution for the orbs issue
Browse files Browse the repository at this point in the history
  • Loading branch information
Mr-Auto committed Sep 17, 2023
1 parent 2f18491 commit fae59fd
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 42 deletions.
56 changes: 51 additions & 5 deletions src/game_api/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,15 +82,18 @@ LPVOID alloc_mem_rel32(size_t addr, size_t size)
{
const size_t limit_addr = Memory::get().exe_ptr;
LPVOID new_array = nullptr;
size_t test_addr;

test_addr = addr + 0x10000; // dunno why
if (test_addr <= INT32_MAX)
test_addr = 8;
size_t test_addr = addr + 0x10000; // dunno why, without this it can get address that is more than 32bit away

if (test_addr <= INT32_MAX) // redunded check as you probably won't get address that is less than INT32_MAX from "zero"
test_addr = 8; // but i did it just in case so you can't get overflow, also can't feed 0 to the VirtualAlloc as that just means: find memory wherever
else
test_addr -= INT32_MAX;

for (; test_addr < limit_addr; test_addr += 0x100000)
// align to 4KB memory page size
test_addr = (test_addr + 0xFFF) & ~0xFFF;

for (; test_addr < limit_addr; test_addr += 0x1000) // add 4KB memory page size
{
new_array = VirtualAlloc((LPVOID)test_addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (new_array)
Expand Down Expand Up @@ -145,3 +148,46 @@ void recover_mem(std::string name, size_t addr)
write_mem_prot(it.address, std::string_view{it.old_data, it.size}, it.prot_used);
}
}

size_t patch_and_redirect(size_t addr, size_t replace_size, std::string_view payload, bool just_nop, size_t return_to_addr)
{
constexpr auto jump_size = 5;

if (replace_size < jump_size)
return 0;

size_t data_size_to_move = replace_size;
if (just_nop)
{
data_size_to_move = 0;
}
const size_t target = std::max(return_to_addr, addr);
const auto new_memory_size = payload.size() + data_size_to_move + jump_size;

auto new_code = (char*)alloc_mem_rel32(target, new_memory_size);
if (new_code == nullptr)
return 0;

if (!just_nop)
{
std::memcpy(new_code, (void*)addr, data_size_to_move);
}
std::memcpy(new_code + data_size_to_move, payload.data(), payload.size());

size_t return_addr = addr;
if (return_to_addr != 0)
{
return_addr = return_to_addr;
}
int32_t rel_back = static_cast<int32_t>(return_addr - (size_t)(new_code + new_memory_size));
const std::string jump_back = fmt::format("\xE9{}"sv, to_le_bytes(rel_back));
std::memcpy(new_code + payload.size() + data_size_to_move, jump_back.data(), jump_size);

DWORD dummy;
VirtualProtect(new_code, new_memory_size, PAGE_EXECUTE_READ, &dummy);

int32_t rel = static_cast<int32_t>((size_t)new_code - (addr + jump_size));
const std::string redirect_code = fmt::format("\xE9{}{}"sv, to_le_bytes(rel), std::string(replace_size - jump_size, '\x90'));
write_mem_prot(addr, redirect_code, true);
return (size_t)new_code;
}
5 changes: 5 additions & 0 deletions src/game_api/memory.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ size_t function_start(size_t off, uint8_t outside_byte = '\xcc');
void write_mem_recoverable(std::string name, size_t addr, std::string_view payload, bool prot);
void recover_mem(std::string name, size_t addr = NULL);

// similar to ExecutableMemory but writes automatic jump from and back, moves the code it replaces etc.
// it needs at least 5 bytes to move, use just_nop = true to nuke the oryginal code
// make sure that the first 5 bytes are not a destination for some jump (it's fine if it's exacly at the addr)
size_t patch_and_redirect(size_t addr, size_t replace_size, std::string_view payload, bool just_nop = false, size_t return_to_addr = 0);

template <typename T>
requires std::is_trivially_copyable_v<T>
std::string_view to_le_bytes(const T& payload)
Expand Down
48 changes: 11 additions & 37 deletions src/game_api/rpc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1889,47 +1889,21 @@ void patch_orbs_limit()

auto memory = Memory::get();
const auto function_offset = get_virtual_function_address(VTABLE_OFFSET::ITEM_FLOATING_ORB, (uint32_t)VIRT_FUNC::ENTITY_KILL);
if (function_offset == 0)
return;

uint16_t particle_ids[5][3] = {
{204, 203, 202},
{207, 206, 205},
{210, 209, 208},
{213, 212, 211},
{216, 215, 214},
};

size_t last_offset = function_offset;
std::array<size_t, 5> rel_offsets{0};
for (uint8_t idx = 0; idx < rel_offsets.size(); ++idx)
{
std::string name = fmt::format("Orbs patch in kill virtual ({})", idx);
auto instance = find_inst(memory.exe(), "\x48\x8D\x05"sv, last_offset, function_offset + 0x622, name);
if (instance == 0) // not found, don't crash the game
return;

rel_offsets[idx] = memory.at_exe(instance) + 3; // 3 pattern size
last_offset = instance + 7;
}
// this is quite an array, but other solution would be to write assembly code and jump to and from it the to original
// maybe TODO: write a code that automatically writes correct jump instructions so you only need to write the insert assembly
// finding exit of the loop that counts orbs, after jump there is unused code so we have enogh space for a jump
auto instance = find_inst(memory.exe(), "\xF3\x75"sv, function_offset, function_offset + 0x622, "Orbs patch in kill virtual");
auto offset = memory.at_exe(instance) + 3;

uint16_t* new_array = (uint16_t*)alloc_mem_rel32(rel_offsets[4], (size_t)255 * 5 * sizeof(uint16_t)); // 5 arrays of 255 elements of 2 byte size
if (new_array == nullptr)
if (instance == 0) // not found, don't crash the game
return;

for (uint8_t idx = 0; idx < rel_offsets.size(); ++idx)
{
auto current_array = new_array + 255 * idx;
auto loop_exit_jump = memory_read<int8_t>(offset + 1);

// +2 to skip the first two elements that we will set separately
std::fill(current_array + 2, current_array + 255, particle_ids[idx][2]);
// set r8b to 2 if it's more than 2
std::string_view new_code{
"\x41\x80\xf8\x02"sv // cmp r8b,0x2
"\x72\x03"sv // jb <end>
"\x41\xb0\x02"sv}; // mov r8b,0x2

*current_array = particle_ids[idx][0];
*(current_array + 1) = particle_ids[idx][1];
int32_t rel = static_cast<int32_t>((size_t)current_array - (rel_offsets[idx] + 4));
write_mem_prot(rel_offsets[idx], rel, true);
}
patch_and_redirect(offset, 8, new_code, true, offset + 2 + loop_exit_jump);
once = true;
}

0 comments on commit fae59fd

Please sign in to comment.