From 348ef1929439f8f383127c3936a88a1080fb4f41 Mon Sep 17 00:00:00 2001 From: JoshuaMKW <60854312+JoshuaMKW@users.noreply.github.com> Date: Mon, 24 Aug 2020 05:10:23 -0500 Subject: [PATCH] Improved hook related features --- GeckoLoader.py | 33 ++-- dolreader.py | 28 ++-- kernel.py | 106 ++++++------ loader.cpp | 429 ++++++++++++++++++++++++++++++------------------- setup.py | 19 +++ 5 files changed, 377 insertions(+), 238 deletions(-) create mode 100644 setup.py diff --git a/GeckoLoader.py b/GeckoLoader.py index ea9696a..0de4f31 100644 --- a/GeckoLoader.py +++ b/GeckoLoader.py @@ -90,16 +90,15 @@ if __name__ == "__main__": default='FULL', choices=['MINI', 'FULL'], metavar='TYPE') - parser.add_argument('--codehook', - help='''Choose where the codeHandler hooks to, needs to exist at a blr instruction''', + parser.add_argument('--hooktype', + help='''The type of hook used for the RAM search. VI or GX are recommended, + although PAD can work just as well. VI is the default hook used''', + default='VI', + choices=['VI', 'GX', 'PAD'], + metavar='HOOK') + parser.add_argument('--hookaddress', + help='Choose where the codeHandler hooks to, overrides auto hooks', metavar='ADDRESS') - parser.add_argument('-q', '--quiet', - help='Print nothing to the console', - action='store_true') - parser.add_argument('-v', '--verbose', - help='Print extra info to the console', - default=0, - action='count') parser.add_argument('-o', '--optimize', help='''Optimizes the codelist by directly patching qualifying ram writes into the dol file, and removing them from the codelist''', @@ -118,6 +117,13 @@ if __name__ == "__main__": parser.add_argument('--encrypt', help='Encrypts the codelist on compile time, helping to slow the snoopers', action='store_true') + parser.add_argument('-q', '--quiet', + help='Print nothing to the console', + action='store_true') + parser.add_argument('-v', '--verbose', + help='Print extra info to the console', + default=0, + action='count') if len(sys.argv) == 1: version = __version__.rjust(9, ' ') @@ -128,7 +134,7 @@ if __name__ == "__main__": if os.path.splitext(__file__)[1].lower() == ".py": helpMessage = 'Try the command: python GeckoLoader.py -h'.center(64, ' ') else: - helpMessage = 'Try the command: .\GeckLoader.exe -h'.center(64, ' ') + helpMessage = 'Try the command: .\GeckoLoader.exe -h'.center(64, ' ') logo = [' ', ' ╔═══════════════════════════════════════════════════════════╗ ', @@ -190,12 +196,12 @@ if __name__ == "__main__": else: _allocation = None - if args.codehook: - if 0x80000000 > int(args.codehook, 16) >= 0x81800000: + if args.hookaddress: + if 0x80000000 > int(args.hookaddress, 16) >= 0x81800000: parser.error(color_text('The codeHandler hook address was beyond bounds\n', defaultColor=TREDLIT)) else: try: - _codehook = int(args.codehook, 16) + _codehook = int(args.hookaddress, 16) except ValueError: parser.error(color_text('The codeHandler hook address was invalid\n', defaultColor=TREDLIT)) else: @@ -222,6 +228,7 @@ if __name__ == "__main__": codeHandler = CodeHandler(handler) codeHandler.allocation = _allocation codeHandler.hookAddress = _codehook + codeHandler.hookType = args.hooktype codeHandler.includeAll = args.txtcodes with open(resource_path(os.path.join('bin', 'geckoloader.bin')), 'rb') as kernelfile: diff --git a/dolreader.py b/dolreader.py index 725fa15..8c43d7e 100644 --- a/dolreader.py +++ b/dolreader.py @@ -20,11 +20,8 @@ class DolFile: self.bssAddress = 0 self.bssSize = 0 self.entryPoint = 0x80003000 - - self._currentEnd = None - if f is None: - return + if f is None: return # Read text and data section addresses and sizes for i in range(self.maxTextSections + self.maxDataSections): @@ -106,32 +103,31 @@ otherwise it returns None''' # Unsupported: Reading an entire dol file # Assumption: A read should not go beyond the current section - def read(self, size): + def read(self, _size): _, address, size, data = self.resolve_address(self._currLogicAddr) - if self._currLogicAddr + size > address + size: + if self._currLogicAddr + _size > address + size: raise RuntimeError("Read goes over current section") - self._currLogicAddr += size - return data.read(size) + self._currLogicAddr += _size + return data.read(_size) # Assumption: A write should not go beyond the current section - def write(self, data): + def write(self, _data): offset, address, size, data = self.resolve_address(self._currLogicAddr) - if self._currLogicAddr + len(data) > address + size: + if self._currLogicAddr + len(_data) > address + size: raise RuntimeError("Write goes over current section") - data.write(data) - self._currLogicAddr += len(data) + data.write(_data) + self._currLogicAddr += len(_data) def seek(self, where, whence=0): if whence == 0: - offset, address, size, data = self.resolve_address(where) + _, address, _, data = self.resolve_address(where) data.seek(where - address) self._currLogicAddr = where - self._currentEnd = address + size elif whence == 1: - offset, address, size, data = self.resolve_address(self._currLogicAddr + where) + _, address, _, data = self.resolve_address(self._currLogicAddr + where) data.seek((self._currLogicAddr + where) - address) self._currLogicAddr += where @@ -256,7 +252,7 @@ otherwise it returns None''' return True def insert_branch(self, to, _from, lk=0): - self.write(((to - _from) & 0x3FFFFFF | 0x48000000 | lk).to_bytes(4, byteorder='big', signed=False)) + tools.write_uint32(self, (to - _from) & 0x3FFFFFF | 0x48000000 | lk) if __name__ == "__main__": # Example usage (reading some enemy info from the Pikmin 2 demo from US demo disc 17) diff --git a/kernel.py b/kernel.py index c0fce67..ab73e09 100644 --- a/kernel.py +++ b/kernel.py @@ -182,10 +182,17 @@ class CodeHandler: self.handlerLength = tools.get_size(f) self.initAddress = 0x80001800 self.startAddress = 0x800018A8 + self.wiiVIHook = b'\x7C\xE3\x3B\x78\x38\x87\x00\x34\x38\xA7\x00\x38\x38\xC7\x00\x4C' self.gcnVIHook = b'\x7C\x03\x00\x34\x38\x83\x00\x20\x54\x85\x08\x3C\x7C\x7F\x2A\x14\xA0\x03\x00\x00\x7C\x7D\x2A\x14\x20\xA4\x00\x3F\xB0\x03\x00\x00' + self.wiiGXDrawHook = b'\x3C\xA0\xCC\x01\x38\x00\x00\x61\x3C\x80\x45\x00\x98\x05\x80\x00' + self.gcnGXDrawHook = b'\x38\x00\x00\x61\x3C\xA0\xCC\x01\x3C\x80\x45\x00\x98\x05\x80\x00' + self.wiiPADHook = b'\x3A\xB5\x00\x01\x3A\x73\x00\x0C\x2C\x15\x00\x04\x3B\x18\x00\x0C' + self.gcnPADHook = b'\x3A\xB5\x00\x01\x2C\x15\x00\x04\x3B\x18\x00\x0C\x3B\xFF\x00\x0C' + self.allocation = None self.hookAddress = None + self.hookType = None self.geckoCodes = None self.includeAll = False self.optimizeList = False @@ -211,7 +218,7 @@ class CodeHandler: continue if state is None: - if line.startswith('$'): + if line.startswith('$') or line.startswith('['): state = 'Dolphin' else: state = 'OcarinaM' @@ -338,10 +345,7 @@ class KernelLoader: elif sample == b'HOOK': #Found keyword "HOOK". Goes with the codehandler hook self._rawData.seek(-4, 1) - if codeHandler.hookAddress == None: - tools.write_uint32(self._rawData, 0) - else: - tools.write_uint32(self._rawData, codeHandler.hookAddress) + tools.write_uint32(self._rawData, codeHandler.hookAddress) elif sample == b'CRPT': #Found keyword "CRPT". Boolean of the encryption self._rawData.seek(-4, 1) @@ -438,6 +442,8 @@ class KernelLoader: if dolFile.get_full_size() < 0x100: parser.error(tools.color_text('DOL header is corrupted. Please provide a clean file\n', defaultColor=tools.TREDLIT), exit=False) return + + oldStart = dolFile.entryPoint '''Initialize our codes''' @@ -510,15 +516,16 @@ class KernelLoader: if self.codeLocation == 'LEGACY': codeHandler.allocation = 0x80003000 - (codeHandler.initAddress + codeHandler.handlerLength) + hooked = determine_codehook(dolFile, codeHandler, True) status = self.patch_legacy(codeHandler, dolFile) - if status is False: - hooked, msg = determine_codehook(dolFile, codeHandler) - if not hooked: - parser.error(tools.color_text(msg, defaultColor=tools.TREDLIT)) legacy = True else: + hooked = determine_codehook(dolFile, codeHandler, False) status = self.patch_arena(codeHandler, dolFile) legacy = False + + if not hooked: + parser.error(tools.color_text('Failed to find a hook address. Try using option --codehook to use your own address\n', defaultColor=tools.TREDLIT)) if status is False: parser.error(tools.color_text('Not enough text sections to patch the DOL file! Potentially due to previous mods?\n', defaultColor=tools.TREDLIT), exit=False) @@ -541,34 +548,26 @@ class KernelLoader: if self.verbosity >= 2: print('') if legacy == False: - info = [f' :: GeckoLoader set at address 0x{self.initAddress:X}, start of game modified to address 0x{self.initAddress:X}', - f' :: Game function "__init_registers()" located at address 0x{dolFile.entryPoint:X}', - f' :: Code allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}', + info = [f' :: Start of game modified to address 0x{self.initAddress:X}', + f' :: Game function "__init_registers()" located at address 0x{oldStart:X}', + f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}', + f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}', f' :: Codehandler is of type "{codeHandler.type}"', - f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} were already used'] - if codeHandler.hookAddress is not None: - info.insert(2, f' :: Codehandler hooked at 0x{codeHandler.hookAddress:08X}') - + f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used'] else: - info = [f' :: Game function "__init_registers()" located at address 0x{dolFile.entryPoint:X}', - f' :: Code allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}', + info = [f' :: Game function "__init_registers()" located at address 0x{oldStart:X}', + f' :: Allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}', + f' :: Codehandler hooked at 0x{codeHandler.hookAddress:X}', f' :: Codehandler is of type "{codeHandler.type}"', - f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} were already used'] - if codeHandler.hookAddress is not None: - info.insert(1, f' :: Codehandler hooked at 0x{codeHandler.hookAddress:08X}') + f' :: Of the 7 text sections in this DOL file, {len(dolFile.textSections)} are now being used'] for bit in info: print(tools.color_text(bit, defaultColor=tools.TGREENLIT)) elif self.verbosity >= 1: print('') - if legacy == False: - info = [f' :: GeckoLoader set at address 0x{self.initAddress:X}', - f' :: Codehandler is of type "{codeHandler.type}"', - f' :: Code allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}'] - else: - info = [f' :: Codehandler is of type "{codeHandler.type}"', - f' :: Code allocation is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}'] - + info = [f' :: GeckoLoader set at address 0x{self.initAddress:X}', + f' :: Legacy size is 0x{codeHandler.allocation:X}; codelist size is 0x{codeHandler.geckoCodes.size:X}', + f' :: Codehandler is of type "{codeHandler.type}"'] for bit in info: print(tools.color_text(bit, defaultColor=tools.TGREENLIT)) @@ -580,30 +579,50 @@ def resource_path(relative_path: str): base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__))) return os.path.join(base_path, relative_path) -def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler): +def determine_codehook(dolFile: DolFile, codeHandler: CodeHandler, hook=False): if codeHandler.hookAddress == None: - return assert_code_hook(dolFile, codeHandler) - else: - return insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress) + if not assert_code_hook(dolFile, codeHandler): + return False + + if hook: + insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress) + return True -def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler): - for _, address, size in dolFile.textSections: +def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler, hook=False): + for _, address, size, _, in dolFile.textSections: dolFile.seek(address, 0) sample = dolFile.read(size) - result = sample.find(codeHandler.gcnVIHook) + if codeHandler.hookType == 'VI': + result = sample.find(codeHandler.gcnVIHook) + elif codeHandler.hookType == 'GX': + result = sample.find(codeHandler.gcnGXDrawHook) + elif codeHandler.hookType == 'PAD': + result = sample.find(codeHandler.gcnPADHook) + else: + raise NotImplementedError(f'Unsupported hook type specified ({codeHandler.hookType})') + if result >= 0: dolFile.seek(address, 0) dolFile.seek(result, 1) else: - result = sample.find(codeHandler.wiiVIHook) + if codeHandler.hookType == 'VI': + result = sample.find(codeHandler.wiiVIHook) + elif codeHandler.hookType == 'GX': + result = sample.find(codeHandler.wiiGXDrawHook) + elif codeHandler.hookType == 'PAD': + result = sample.find(codeHandler.wiiPADHook) + else: + raise NotImplementedError(f'Unsupported hook type specified ({codeHandler.hookType})') + if result >= 0: dolFile.seek(address, 0) dolFile.seek(result, 1) else: continue + print(f'{dolFile._currLogicAddr:X}, {address:X}, {result:X}') sample = tools.read_uint32(dolFile) while sample != 0x4E800020: sample = tools.read_uint32(dolFile) @@ -611,15 +630,10 @@ def assert_code_hook(dolFile: DolFile, codeHandler: CodeHandler): dolFile.seek(-4, 1) codeHandler.hookAddress = dolFile.tell() - return insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress) - return False, 'Failed to find a hook address. Try using option --codehook to use your own address\n' + if hook: insert_code_hook(dolFile, codeHandler, codeHandler.hookAddress) + return True + return False def insert_code_hook(dolFile: DolFile, codeHandler: CodeHandler, address: int): dolFile.seek(address) - - if tools.read_uint32(dolFile) != 0x4E800020: - return False, 'Codehandler hook given is not a blr\n' - - dolFile.seek(-4, 1) - dolFile.insert_branch(codeHandler.startAddress, address, lk=0) - return True, '' \ No newline at end of file + dolFile.insert_branch(codeHandler.startAddress, address, lk=0) \ No newline at end of file diff --git a/loader.cpp b/loader.cpp index b323360..aef3a45 100644 --- a/loader.cpp +++ b/loader.cpp @@ -1,4 +1,11 @@ -/*Credits to riidefi for hook code, cache asm, and teaching me C*/ +//#include +//#include + +//using std::array; +//using std::memcpy; +//using std::memcmp; +//using std::memset; +//using std::string; #define dcbst(_val) asm volatile("dcbst 0, %0" \ : \ @@ -11,38 +18,54 @@ : "r"(_val)) #define call(addr) ((void (*)(...))addr) -#define MEM1_START 0x80000000 -#define MEM1_END 0x81800000 #define CODEHANDLER 0x800018A8 #define GCT_MAGIC 0x00D0C0DE -typedef unsigned int u32; -typedef unsigned short u16; -typedef unsigned char u8; -typedef int s32; -typedef short s16; -typedef char s8; +using u32 = unsigned int; +using u16 = unsigned short; +using u8 = unsigned char; +using s32 = int; +using s16 = short; +using s8 = char; +using f32 = float; +using f64 = double; __attribute__((noreturn)) int main(); struct CodeList { - u16 mBaseASM; - u16 mUpperBase; - u16 mOffsetASM; - u16 mLowerOffset; + + u16 mBaseASM; + u16 mUpperBase; + u16 mOffsetASM; + u16 mLowerOffset; + }; struct Info { - const u32 allocsize; - const u32 loaderSize; - const u32 handlerSize; - const u32 codeSize; - const u32* codehandlerHook; - const u32 wiiVIHook[4]; - const u32 gcnVIHook[8]; + + const u32 allocsize; + const u32 loaderSize; + const u32 handlerSize; + const u32 codeSize; + const u32* codehandlerHook; + const u32 crypted; + }; -struct DiscInfo { +class DiscHeader { + +public: + + enum class TVMODE { + NTSC, + PAL, + DEBUG, + DEBUGPAL, + MPAL, + PAL60 + }; + struct MetaData { + const u8 mDiscID; //0x0000 const u16 mGameCode; //0x0001 const u8 mRegionCode; //0x0003 @@ -54,180 +77,260 @@ struct DiscInfo { const u8 _00[12]; //0x000A const u32 mWiiMagic; //0x0018 const u32 mGCNMagic; //0x001C - const u32 _01[2]; //0x0020 - u32 mRAMSize; //0x0028 - const u32 _02[2]; //0x002C - u32* mOSArenaHi; //0x0034 - u32* mFstPointer; //0x0038 + const u32 mNinBootCode; //0x0020 + const u32 mAppVersion; //0x0024 + const u32 mPhysicalRAMSize; //0x0028 + const u32 mBoardModel; //0x002C + u8* mOSArenaLo; //0x0030 + u8* mOSArenaHi; //0x0034 + u32* mFstStart; //0x0038 u32 mFstSize; //0x003C - u32 _03[0xB4 / 4]; //0x0040 - u32* mTranslation; //0x00F4 - u32 _04[0x3018 / 4]; - u32 mWiiHeap; + u32 mDebuggerPresent; //0x0040 + const u32 mDebuggerExceptionMask; //0x0044 + void* mExceptionHookDest; //0x0048 + const u32 mExceptionReturn; //0x004C + u32 _01[0x10 / 4]; //0x0050 + u32 mDebuggerHook[0x24 / 4]; //0x0060 + u32 _02[0x3C / 4]; //0x0084 + u32 mCurrentOSContext; //0x00C0 + u32 mPreviousOSMask; //0x00C4 + u32 mCurrentOSMask; //0x00C8 + TVMODE mTVMode; //0x00CC + u32 mARAMSize; //0x00D0 + void* mCurOSContextLogical; //0x00D4 + void* mDefaultOSThreadLogical; //0x00D8 + u32* mThreadQueueHead; //0x00DC + u32* mThreadQueueTail; //0x00E0 + u32* mCurrentOSThread; //0x00E4 + u32 mDebuggerSize; //0x00E8 + u32* mDebuggerMonitorLoc; //0x00EC + u32 mSimulatedMemSize; //0x00F0 + u8* mBi2HeaderLoc; //0x00F4 + u32 mBusClockSpeed; //0x00F8 + u32 mCPUClockSpeed; //00x00FC + u32 _04[0x3010 / 4]; + u8* mWiiHeap; //0x3110 + + }; + + static MetaData sMetaData; + + enum class CONSOLETYPE { Gamecube, Wii, Unknown }; + + inline u32 getGameID() { return ((u32)sMetaData.mDiscID << 24) | ((u32)sMetaData.mGameCode << 8) | ((u32)sMetaData.mRegionCode); } + inline u16 getMakerID() { return sMetaData.mMakerCode; } + inline u8 getDiscNumber() { return sMetaData.mDiscNumber; } + inline u8 getDiscVersion() { return sMetaData.mDiscVersion; } + + CONSOLETYPE detectHomeConsole() + { + if (sMetaData.mGCNMagic) { + return CONSOLETYPE::Gamecube; + } + else if (sMetaData.mWiiMagic) { + return CONSOLETYPE::Wii; + } + + return CONSOLETYPE::Unknown; + } + + inline void setHeap(u32 alloc) + { + if (sMetaData.mBi2HeaderLoc < sMetaData.mOSArenaHi) { + sMetaData.mOSArenaHi = sMetaData.mBi2HeaderLoc - alloc; + if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) { + sMetaData.mWiiHeap = sMetaData.mBi2HeaderLoc - alloc; + } + } + else { + sMetaData.mOSArenaHi = sMetaData.mWiiHeap - alloc; + if (this->detectHomeConsole() == DiscHeader::CONSOLETYPE::Wii) { + sMetaData.mWiiHeap -= alloc; + } + } + } }; +static DiscHeader sDisc; Info gpModInfo = { - 0, /*This is the code allocation*/ - 0, /*This is the size of the GeckoLoader*/ - 0, /*This is the size of the codehandler*/ - 0, /*This is the size of the GeckoLoader + the codelist*/ - 0, /*This is the codehandler hook address*/ - { 0x7CE33B78, 0x38870034, 0x38A70038, 0x38C7004C }, - { 0x7C030034, 0x38830020, 0x5485083C, 0x7C7F2A14, 0xA0030000, 0x7C7D2A14, 0x20A4003F, 0xB0030000 }, + 0x48454150, + 0x4C53495A, + 0x4853495A, + 0x4353495A, + (const u32*)0x484F4F4B, + 0x43525054, }; -DiscInfo* gpDiscResources = (DiscInfo*)MEM1_START; +namespace Memory { -static inline void flushAddr(void* addr) -{ - dcbf(addr); - icbi(addr); -} + static void memcpy(u8* to, u8* from, s32 size) + { + for (s32 i = 0; i < size; ++i) { + *to++ = *from++; + } + } -static inline void directWrite(u32* addr, u32 value) -{ - *addr = value; - flushAddr(addr); -} + namespace Cache { -/*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/ -static inline void directBranchEx(void* addr, void* ptr, bool lk) -{ - directWrite((u32*)(addr), ((((u32)(ptr) - (u32)(addr)) & 0x3ffffff) | 0x48000000 | !!lk)); -} + static inline void flushAddr(void* addr) + { + dcbf(addr); + icbi(addr); + } -static inline u32* findArrayInstance(u32* start, const u32 end, u32 arrayLength, const u32* hookData) -{ - u32 index = 0; + static void flushRange(u8* addr, s32 size) + { + size += 31 + (((u32)addr & 31) > 0); - /*Loop through the games RAM, make sure we don't find our own hook data by accident*/ - for (u32 i = 0; (u32)&start[i] < end; ++i) { + for (u32 i = 0; i < (size >> 5); ++i) { + flushAddr((void*)(addr + (i << 5))); + } + } + + } + + namespace Direct { + + template + static inline void write(T* addr, T value) + { + *addr = value; + Cache::flushAddr(addr); + } + + /*This constructs a branch instruction. &TO = ((TO - FROM) & MAX_OFFSET) | BRANCH_TYPE | !!isLink*/ + static inline void branch(void* addr, void* to, bool lk) + { + Direct::write((u32*)(addr), ((((u32)(to)-(u32)(addr)) & 0x3ffffff) | 0x48000000 | lk)); + } + + } + + namespace Search { + + static u32* array(u32* start, u32* end, u32 arrayLength, const u32* hookData) + { + u32 index = 0; + + /*Loop through the games RAM, make sure we don't find our own hook data by accident*/ + for (u32 i = 0; &start[i] < end; ++i) { /*If the data matches, increase the index counter and continue search, - else set index to 0 and continue searching*/ + else set index to 0 and continue searching*/ if (start[i] == hookData[index]) - ++index; + ++index; else - index = 0; + index = 0; /*If the data has matched the whole array, return the address of the match*/ - if (index >= (arrayLength) && ((u32)&start[i] < (u32)&gpModInfo || (u32)&start[i] > (u32)&gpModInfo + sizeof(Info))) - return (u32*)&start[i]; + if (index >= (arrayLength) && (&start[i] < (u32*)&gpModInfo || &start[i] > (u32*) & gpModInfo + sizeof(Info))) + return &start[i]; + } + return nullptr; } - return nullptr; -} -static inline u32* findU32Instance(u32* start, u32 end, u32 hookData) -{ - for (u32 i = 0; (u32)&start[i] < end; ++i) { - if (start[i] == hookData) { - return (u32*)&start[i]; - } - } - return nullptr; -} - -/*Find VI hook for Game*/ -static inline u32* findVIHook(u32* start, const u32 end) -{ - const u32* hookData; - u32 arrayLength; - - /*If the game is built for the Wii, set the hookdata to be the Wii variant*/ - if (gpDiscResources->mWiiMagic) { - hookData = (const u32*)gpModInfo.wiiVIHook; - arrayLength = sizeof(gpModInfo.wiiVIHook) / sizeof(u32); - } else /*The game is built for the GCN, set the hookdata to be the GCN variant*/ + template + static T* single(T* start, T* end, T hookData) { - hookData = (const u32*)gpModInfo.gcnVIHook; - arrayLength = sizeof(gpModInfo.gcnVIHook) / sizeof(u32); - } - return findArrayInstance(start, end, arrayLength, hookData); -} - -/*Call this after findFunction, finds the address of the first instance -of value hookInstruction, and hooks it to the pointer hookTo*/ -static inline void hookFunction(u32* start, u32 hookInstruction, u32 hookTo, bool isLink) -{ - int i = 0; - while (start[i] != hookInstruction) { - ++i; - } - directBranchEx((u32*)(&start[i]), (void*)(hookTo), isLink); -} - -/*Reallocate the games internal memory heap based on the console -the game is for, to make space for our codes*/ -static inline void setHeap(u32 alloc) -{ - if (gpDiscResources->mTranslation < gpDiscResources->mOSArenaHi) { - if (gpDiscResources->mWiiMagic) { - gpDiscResources->mTranslation = (u32*)((u32)gpDiscResources->mTranslation - alloc); - gpDiscResources->mWiiHeap = (u32)gpDiscResources->mTranslation; - } else { - gpDiscResources->mTranslation = (u32*)((u32)gpDiscResources->mTranslation - alloc); - } - } else { - if (gpDiscResources->mWiiMagic) { - gpDiscResources->mOSArenaHi = (u32*)((u32)gpDiscResources->mWiiHeap - alloc); - gpDiscResources->mWiiHeap = (u32)gpDiscResources->mOSArenaHi; - } else { - gpDiscResources->mOSArenaHi = (u32*)((u32)gpDiscResources->mOSArenaHi - alloc); + for (u32 i = 0; &start[i] < end; ++i) { + if (start[i] == hookData) { + return &start[i]; } + } + return nullptr; } - + + /*Call this after viHook, finds the address of the first instance + of targetVal, and hooks it to the pointer hookTo*/ + static inline void hookFunction(u32* start, u32 targetVal, void* hookTo, bool lk) + { + Direct::branch(Search::single(start, start + 0x500, targetVal), hookTo, lk); + } + + } + + class Crypt { + + private: + u32 key; + + u32 getKey() + { + u32 b1 = (this->key >> 24) & 0xFF; + u32 b2 = (this->key >> 16) & 0xFF; + u32 b3 = (this->key >> 8) & 0xFF; + u32 b4 = this->key & 0xFF; + b1 ^= b2; + b2 ^= b3; + b3 ^= b4; + return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1; + } + + void setKey(u32 key) + { + u32 b1 = key & 0xFF; + u32 b2 = (key >> 8) & 0xFF; + u32 b3 = (key >> 16) & 0xFF; + u32 b4 = (key >> 24) & 0xFF; + b3 ^= b4; + b2 ^= b3; + b1 ^= b2; + this->key = (b1 << 24) | (b2 << 16) | (b3 << 8) | b4; + } + + public: + Crypt(u32 key) + { + this->key = key; + } + + inline void xorCrypt(u32* dest, u32* buffer, u32 size) + { + u32 key = this->getKey(); + + for (u32 i = 0; i < size; ++i) { + dest[i] = buffer[i] ^ key; + key += i ^ key; + } + } + }; + + enum class Space : u32 { Start = 0x80000000, End = 0x81800000, Size = 0x1800000 }; + } -static inline void memCopy(u32* to, u32* from, s32 size) +Memory::Crypt gpCryptor = { 0x43595054 }; + +static bool initMods() { - for (s32 i = 0; i < size; ++i) { - *to++ = *from++; - } -} + sDisc.setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/ -static inline void flushCacheRange(u8* addr, s32 size) -{ - if ((u32)addr & 31) size += 32; - else size += 31; + /*Change codelist pointer to the new address in the allocation*/ + CodeList* codelistPointer = (CodeList*)((u32)&gpModInfo + sizeof(Info) + 0xFC); + codelistPointer->mUpperBase = (((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF; + codelistPointer->mLowerOffset = ((u32)sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF; - for (u32 i = 0; i < (size >> 5); ++i) { - flushAddr((void*)addr); - addr += 32; - } -} + /*Update the cache, so that the instructions fully update*/ + Memory::Cache::flushAddr(&codelistPointer->mBaseASM); -static inline bool initMods(DiscInfo* gpDiscResources) -{ - setHeap(gpModInfo.allocsize); /*Reallocate the internal heap*/ + /*Copy codelist to the new allocation*/ + if (gpModInfo.crypted) { + Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8*)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize); + gpCryptor.xorCrypt((u32*)(sDisc.sMetaData.mOSArenaHi + gpModInfo.handlerSize), (u32*)((u8*)&gpModInfo + sizeof(Info) + gpModInfo.handlerSize + 4), gpModInfo.codeSize >> 2); + } + else { + Memory::memcpy(sDisc.sMetaData.mOSArenaHi, (u8*)&gpModInfo + sizeof(Info) + 4, gpModInfo.handlerSize + gpModInfo.codeSize); + } - /*Change codelist pointer to the new address in the allocation*/ - CodeList* codelistPointer = (CodeList*)((u32)&gpModInfo + sizeof(gpModInfo) + 0xFC); - codelistPointer->mUpperBase = (((u32)gpDiscResources->mOSArenaHi + gpModInfo.handlerSize) >> 16) & 0xFFFF; - codelistPointer->mLowerOffset = ((u32)gpDiscResources->mOSArenaHi + gpModInfo.handlerSize) & 0xFFFF; + Memory::Direct::branch((void*)gpModInfo.codehandlerHook, (void*)((u32)sDisc.sMetaData.mOSArenaHi + 0xA8), false); - /*Copy codelist to the new allocation*/ - memCopy(gpDiscResources->mOSArenaHi, (u32*)((u32)&gpModInfo + sizeof(gpModInfo) + 4), (gpModInfo.handlerSize + gpModInfo.codeSize) >> 2); - - /*Update the cache, so that the instructions fully update*/ - flushAddr(&codelistPointer->mBaseASM); - - if (!gpModInfo.codehandlerHook || *gpModInfo.codehandlerHook != 0x4E800020) { - u32* functionAddr = findVIHook((u32*)MEM1_START, MEM1_END); - if (functionAddr == nullptr) - return false; - - hookFunction(functionAddr, 0x4E800020, (u32)gpDiscResources->mOSArenaHi + 0xA8, false); - } else { - directBranchEx((void*)gpModInfo.codehandlerHook, (void*)((u32)gpDiscResources->mOSArenaHi + 0xA8), false); - } - - flushCacheRange((u8*)gpDiscResources->mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize); - return true; + Memory::Cache::flushRange((u8*)sDisc.sMetaData.mOSArenaHi, gpModInfo.handlerSize + gpModInfo.codeSize); + return true; } int main() { - if ((gpDiscResources->mWiiMagic || gpDiscResources->mGCNMagic) && initMods(gpDiscResources) == true) - call((void*)((u32)(gpDiscResources->mOSArenaHi) + 0xA8))(); /*Call the codehandler if successful*/ + if ((sDisc.detectHomeConsole() != DiscHeader::CONSOLETYPE::Unknown) && initMods() == true) + call((void*)((u32)(sDisc.sMetaData.mOSArenaHi) + 0xA8))(); /*Call the codehandler if successful*/ - call(0xDEADBEEF)(); /*Call the game start*/ + call(0x4948494C)(); /*Call the game start*/ } \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e600532 --- /dev/null +++ b/setup.py @@ -0,0 +1,19 @@ +import sys +from cx_Freeze import setup, Executable + +options = { + 'build_exe': { + 'excludes': ['tkinter'] + } +} + +executables = [ + Executable('GeckoLoader.py'), +] + +setup(name='GeckoLoader', + version='v6.0.0', + description='DOL Patcher for extending the codespace of Wii/GC games', + executables=executables, + options=options + ) \ No newline at end of file