Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions Hook.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import re
from pathlib import Path

ADDRESS_RE = re.compile(r"^#(0[xX][0-9A-Fa-f]{6,8})\:$")
FUNCTION_NAME_RE = re.compile(r"@([a-zA-Z\_][a-zA-Z0-9\_]+)")


class Section:

def __init__(self, address: str, lines: list[str]) -> None:
self._address: str = address
self._lines: list[str] = lines

def lines_to_cpp(self):
s = ""
for line in self._lines:
line = line.translate(str.maketrans({"\"": r"\"", "\\": r"\\", }))

if FUNCTION_NAME_RE.findall(line):
line = FUNCTION_NAME_RE.sub(r'"QU(\1)"', line)

s += f'"{line};"\n'
return s

def to_cpp(self, index: int) -> str:
if self._address is None:
return self.lines_to_cpp()
return f'SECTION({index:X}, {self._address})\n{self.lines_to_cpp()}'


class Hook:
def __init__(self, sections: list[Section]) -> None:
self._sections: list[Section] = sections

def to_cpp(self):
s = '#include "../asm.h"\n#include "../define.h"\n'
sections_lines = (section.to_cpp(i).split("\n")
for i, section in enumerate(self._sections))
s += f"asm(\n{''.join((f" {line}\n" for lines in sections_lines for line in lines))});"
return s


def load_hook(file_path: Path) -> Hook:
sections: list[Section] = []
lines = []
address = None
with open(file_path) as f:
for line in f.readlines():
# remove comments
line = line.split("//")[0]
if not line.startswith("#"):
line = line.split("#")[0]
line = line.strip()
if not line:
continue

if match := ADDRESS_RE.match(line):
sections.append(Section(address, lines))
lines = []
address = match.group(1)
continue
lines.append(line)
sections.append(Section(address, lines))
return Hook(sections)
14 changes: 12 additions & 2 deletions Patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Optional
import struct
import itertools
import Hook


CLANG_FLAGS = " ".join(["-pipe -m32 -Os -nostdlib -nostartfiles -w -masm=intel -std=c++20 -march=core2 -c",
Expand Down Expand Up @@ -380,6 +381,14 @@ def create_defines_file(path: Path, addresses: dict[str, str]):
f.write(f"#define {name} {address}\n")
create_defines_file(target_path / "define.h", addresses)

def generate_hook_files(folder_path: Path):
for file_path in list_files_at(folder_path, "*.hook"):
hook = Hook. load_hook(folder_path/file_path)
with open(folder_path/(Path(file_path).name+".cpp"), "w") as f:
f.write(hook.to_cpp())

generate_hook_files(target_path/"hooks")

if run_system(
f"""cd {build_folder_path} &
{gcc_compiler_path} -c {GCC_FLAGS} ../hooks/*.cpp"""):
Expand Down Expand Up @@ -417,7 +426,7 @@ def create_defines_file(path: Path, addresses: dict[str, str]):
for hook in hooks:
for sect in hook.sects:
pld.writelines([
f" .h{hi} 0x{sect.offset:x} : SUBALIGN(1) {{\n",
f" .h{hi:X} 0x{sect.offset:x} : SUBALIGN(1) {{\n",
f" {hook.name}({sect.name})\n",
" }\n",
])
Expand Down Expand Up @@ -457,7 +466,7 @@ def replace_data(new_data, offset):
print(f"No hooks in {hook.name}")
continue
for sect in hook.sects:
psect = patch_pe.find_sect(f".h{hi}")
psect = patch_pe.find_sect(f".h{hi:X}")
size = sect.size
replace_data(
patch_pe.data[psect.f_offset:psect.f_offset + size], psect.v_offset)
Expand Down Expand Up @@ -507,3 +516,4 @@ def save_new_base_data(data: bytearray):
save_new_base_data(base_file_data)

remove_files_at(build_folder_path, "**/*.o")
remove_files_at(target_path/"hooks", "*.hook.cpp")
50 changes: 50 additions & 0 deletions Stats.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
from dataclasses import dataclass


@dataclass
class Function:
start: int
end: int
name: str
type: str


functions: list[Function] = []

TYPE_TO_NAME = {
"O": "Original",
"U": "Unused",
"R": "Replicated",
"T": "To Be Replicated"
}

with open("stats.txt", "r") as f:
for line in f.readlines():
if line.startswith("#") or not line.strip():
continue
start, end, type, name = line.split(" ", 3)
start = int(start, 16)
end = int(end, 16)
functions.append(
Function(start, end, name, TYPE_TO_NAME[type.upper()]))

TEXT_START = 0x00401000
TEXT_END = 0x00C0F000


def compute_total(type):
global functions
total = 0
for func in functions:
if func.type == type:
total += func.end - func.start
return total


ratio = (compute_total("Replicated") + compute_total("Original")) / \
(TEXT_END-TEXT_START-compute_total("Unused"))
print(f"Replicated: {ratio*100:.3f}%")
print(f"Unused functions bytes:\t\t{compute_total("Unused")}")
print(f"Replicated functions bytes:\t{compute_total("Replicated")}")
print(f"Original functions bytes:\t{compute_total("Original")}")
print(f"To be replicated:\t\t{compute_total("To Be Replicated")}")
40 changes: 40 additions & 0 deletions stats.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# LuaObject
009072F0 00907310 R LuaPlus::LuaObject::IsNil(LuaPlus::LuaObject *this)
00907310 00907320 R LuaObject::IsTable(LuaPlus::LuaObject *this)
00907320 00907340 R LuaPlus::LuaObject::IsUserData(LuaPlus::LuaObject *this)
00907340 00907350 R LuaObject::IsCFunction(LuaPlus::LuaObject *this)
00907580 009075A0 R LuaObject::RemoveFromUsedList
009075A0 009075D0 U ?
009075D0 009075F0 R LuaPlus::LuaObject::~LuaObject(LuaPlus::LuaObject *this)
009075F0 00907610 R LuaPlus::LuaObject::Reset(LuaPlus::LuaObject *this)
009072A0 009072B0 R LuaPlus::LuaObject::LuaObject(LuaPlus::LuaObject *this)
00908810 00908890 R LuaPlus::LuaObject::SetObject
009089C0 009089F0 R LuaObject::LuaObject(LuaState *state, int index)
009089F0 00908A40 R LuaObject::LuaObject(LuaState *state, const TObject *obj)
00908A40 00908A70 R LuaObject::LuaObject(const LuaObject &obj)
00908AB0 00908B00 R LuaPlus::LuaObject::operator=(LuaPlus::LuaObject *this, LuaPlus::LuaObject *obj)
00908B00 00908B50 R LuaPlus::LuaObject::operator=(LuaPlus::LuaObject *this, LuaPlus::LuaStackObject *a2)
00908B50 00908BA0 R LuaPlus::LuaObject::TypeName
00908DF0 00908E70 R LuaPlus::LuaObject::GetByIndex
00908E70 00908EE0 R LuaPlus::LuaObject::GetByObject
00909AF0 00909CE0 R LuaPlus::LuaObject::Insert
00909CE0 00909EB0 R LuaPlus::LuaObject::Insert2
00909EB0 0090A020 U LuaPlus::LuaObject::Remove
0090A020 0090A160 U LuaPlus::LuaObject::Sort
0090A180 0090A410 U LuaPlus::LuaObject::Clone

# general
00958B20 00958C40 O malloc
00958C40 00958D60 O free
00958D60 00958DC0 O getheapinfo
00957EA0 00957EF0 O msize
00A825B9 00A8262F O new
00957B00 00957BA0 O realloc
00A89110 00A89190 O memset
00A89190 00A89525 O memcpy
00A96630 00A966B8 O strcmp
00A94450 00A944E0 O strlen
00A89950 00A899A0 O CxxThrowException

# win
004F2730 004F2800 O Moho::WIN_CopyToClipboard(const wchar_t*)