#include"engine.h" #include"util/util.h" #include"psputils.hpp" #include"specialgames.hpp" // See: https://github.com/hrydgard/ppsspp // Core/HLE (High Level Emulator) // - sceCcc // #void sceCccSetTable(u32 jis2ucs, u32 ucs2jis) // int sceCccUTF8toUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr) // int sceCccUTF8toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr) // int sceCccUTF16toUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr) // int sceCccUTF16toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr) // int sceCccSJIStoUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr) // int sceCccSJIStoUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr) // int sceCccStrlenUTF8(u32 strAddr) // int sceCccStrlenUTF16(u32 strAddr) // int sceCccStrlenSJIS(u32 strAddr) // u32 sceCccEncodeUTF8(u32 dstAddrAddr, u32 ucs) // void sceCccEncodeUTF16(u32 dstAddrAddr, u32 ucs) // u32 sceCccEncodeSJIS(u32 dstAddrAddr, u32 jis) // u32 sceCccDecodeUTF8(u32 dstAddrAddr) // u32 sceCccDecodeUTF16(u32 dstAddrAddr) // u32 sceCccDecodeSJIS(u32 dstAddrAddr) // int sceCccIsValidUTF8(u32 c) // int sceCccIsValidUTF16(u32 c) // int sceCccIsValidSJIS(u32 c) // int sceCccIsValidUCS2(u32 c) // int sceCccIsValidUCS4(u32 c) // int sceCccIsValidJIS(u32 c) // int sceCccIsValidUnicode(u32 c) // #u32 sceCccSetErrorCharUTF8(u32 c) // #u32 sceCccSetErrorCharUTF16(u32 c) // #u32 sceCccSetErrorCharSJIS(u32 c) // u32 sceCccUCStoJIS(u32 c, u32 alt) // u32 sceCccJIStoUCS(u32 c, u32 alt) // - sceFont: search charCode // int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) // int sceFontGetShadowInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr) // int sceFontGetCharImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr) // int sceFontGetShadowImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr) // int sceFontGetCharGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr) // int sceFontGetCharGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) // #int sceFontSetAltCharacterCode(u32 fontLibHandle, u32 charCode) // int sceFontGetShadowGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr) // int sceFontGetShadowGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight) // - sceKernelInterrupt // u32 sysclib_strcat(u32 dst, u32 src) // int sysclib_strcmp(u32 dst, u32 src) // u32 sysclib_strcpy(u32 dst, u32 src) // u32 sysclib_strlen(u32 src) // // Sample debug string: // 006EFD8E PUSH PPSSPPWi.00832188 ASCII "sceCccEncodeSJIS(%08x, U+%04x)" // Corresponding source code in sceCcc: // ERROR_LOG(HLE, "sceCccEncodeSJIS(%08x, U+%04x): invalid pointer", dstAddrAddr, jis); struct PPSSPPFunction { const char *hookName; // hook name int argIndex; // argument index uint64_t hookType; // hook parameter type int hookSplit; // hook parameter split, positive: stack, negative: registers const char *pattern; // debug string used within the function }; uint64_t findleaaddr(uint64_t addr,uint64_t start,uint64_t end) { for(auto _addr=start;_addr{ //https://github.com/hrydgard/ppsspp/blob/master/Core/HLE/sceCcc.cpp { "sceCccStrlenSJIS", GETARG1, USING_STRING, 0, "sceCccStrlenSJIS(" } , { "sceCccStrlenUTF8", GETARG1, CODEC_UTF8|USING_STRING, 0, "sceCccStrlenUTF8(" } , { "sceCccStrlenUTF16", GETARG1, CODEC_UTF16|USING_STRING, 0, "sceCccStrlenUTF16(" } , { "sceCccSJIStoUTF8", GETARG3, USING_STRING, 0, "sceCccSJIStoUTF8(" } , { "sceCccSJIStoUTF16", GETARG3, USING_STRING, 0, "sceCccSJIStoUTF16(" } , { "sceCccUTF8toSJIS", GETARG3, CODEC_UTF8|USING_STRING, 0, "sceCccUTF8toSJIS(" } , { "sceCccUTF8toUTF16", GETARG3, CODEC_UTF8|USING_STRING, 0, "sceCccUTF8toUTF16(" } , { "sceCccUTF16toSJIS", GETARG3, CODEC_UTF16|USING_STRING, 0, "sceCccUTF16toSJIS(" } , { "sceCccUTF16toUTF8", GETARG3, CODEC_UTF16|USING_STRING, 0, "sceCccUTF16toUTF8(" } , //https://github.com/hrydgard/ppsspp/blob/master/Core/HLE/sceFont.cpp { "sceFontGetCharInfo", GETARG2, CODEC_UTF16, GETARG1, "sceFontGetCharInfo(" } , { "sceFontGetShadowInfo", GETARG2, CODEC_UTF16, GETARG1, "sceFontGetShadowInfo("} , { "sceFontGetCharImageRect", GETARG2, CODEC_UTF16, GETARG1, "sceFontGetCharImageRect(" } , { "sceFontGetShadowImageRect", GETARG2, CODEC_UTF16, GETARG1, "sceFontGetShadowImageRect(" } , { "sceFontGetCharGlyphImage", GETARG2, CODEC_UTF16, GETARG1, "sceFontGetCharGlyphImage(" } , { "sceFontGetCharGlyphImage_Clip", GETARG2, CODEC_UTF16, GETARG1, "sceFontGetCharGlyphImage_Clip(" } , { "sceFontGetShadowGlyphImage", GETARG2, CODEC_UTF16, GETARG1, "sceFontGetShadowGlyphImage(" } , { "sceFontGetShadowGlyphImage_Clip", GETARG2, CODEC_UTF16, GETARG1, "sceFontGetShadowGlyphImage_Clip(" } , //https://github.com/hrydgard/ppsspp/blob/master/Core/HLE/sceKernelInterrupt.cpp { "sysclib_strcat", GETARG2, USING_STRING, 0, "Untested sysclib_strcat(" } , { "sysclib_strcpy", GETARG2, USING_STRING, 0, "Untested sysclib_strcpy(" } , { "sysclib_strlen", GETARG1, USING_STRING, 0, "Untested sysclib_strlen(" } // Disabled as I am not sure how to deal with the source string //, { "sceCccEncodeSJIS", 2, USING_STRING, 0, "sceCccEncodeSJIS(" } //, { "sceCccEncodeUTF8", 2, CODEC_UTF8, 0, "sceCccEncodeUTF8(" } //, { "sceCccEncodeUTF16", 2, CODEC_UTF16, 0, "sceCccEncodeUTF16(" } //, { "sysclib_strcmp", 2, USING_STRING, 0, "Untested sysclib_strcmp(" } }; auto succ=false; for (auto&& function :functions) { auto addr = MemDbg::findBytes(function.pattern, ::strlen(function.pattern), processStartAddress, processStopAddress); if(!addr)continue; #ifndef _WIN64 addr=MemDbg::findPushAddress(addr, processStartAddress, processStopAddress); if(!addr)continue; addr=SafeFindEnclosingAlignedFunction(addr, 0x200); #else addr=findleaaddr(addr, processStartAddress, processStopAddress); if(!addr)continue; BYTE sig1[]={ 0xCC, 0x48,0x89,XX,0x24,XX, }; BYTE sig2[]={ 0xC3, 0x48,0x89,XX,0x24,XX, }; BYTE sig3[]={ 0xCC, 0x89,XX,0x24,XX, }; BYTE sig4[]={ 0xC3, 0x89,XX,0x24,XX, }; int idx=0; uintptr_t maxaddr=0; for(auto sig:{sig1,sig2,sig3,sig4}) { idx+=1; maxaddr=max(maxaddr,reverseFindBytes(sig,(idx>2)?5:6,addr-0x500,addr,1,true)); } maxaddr=max(maxaddr,MemDbg::findEnclosingAlignedFunction_strict(addr,0x500)); addr=maxaddr; #endif if(!addr)continue; HookParam hp; hp.address=addr; hp.type = function.hookType; hp.offset=function.argIndex; hp.split = function.hookSplit; if (hp.split)hp.type |= USING_SPLIT; succ|=NewHook(hp, function.hookName); } return succ; } #if 0 bool PPSSPPinithooksearch(){ bool found = false; SYSTEM_INFO systemInfo; GetNativeSystemInfo(&systemInfo); for (BYTE* probe = NULL; probe < systemInfo.lpMaximumApplicationAddress;) { MEMORY_BASIC_INFORMATION info; if (!VirtualQuery(probe, &info, sizeof(info))) { probe += systemInfo.dwPageSize; } else { if (info.RegionSize == 0x1f00000 && info.Protect == PAGE_READWRITE && info.Type == MEM_MAPPED) { found = true; ConsoleOutput("PPSSPP memory found: searching for hooks should yield working hook codes"); #ifndef _WIN64 // PPSSPP 1.8.0 compiles jal to sub dword ptr [ebp+0x360],?? memcpy(spDefault.pattern, Array{ 0x83, 0xAD, 0x60, 0x03, 0x00, 0x00 }, spDefault.length = 6); #else // PPSSPP 1.8.0 compiles jal to sub dword ptr [r14+0x360],?? memcpy(spDefault.pattern, Array{ 0x41, 0x83, 0xae, 0x60, 0x03, 0x00, 0x00 }, spDefault.length = 7); #endif spDefault.offset = 0; spDefault.minAddress = 0; spDefault.maxAddress = -1ULL; spDefault.padding = (uintptr_t)probe - 0x8000000; spDefault.hookPostProcessor = [](HookParam& hp) { hp.type |= NO_CONTEXT | USING_SPLIT | SPLIT_INDIRECT; #ifndef _WIN64 hp.split = get_reg(regs::ebp); hp.split_index =get_reg(regs::eax); // this is where PPSSPP 1.8.0 stores its return address stack #else hp.split = get_reg(regs::r14); hp.split_index = -8; // this is where PPSSPP 1.8.0 stores its return address stack #endif }; } probe += info.RegionSize; } } return found; } #endif uintptr_t getDoJitAddress() { #ifndef _WIN64 auto string1="Jump target too far away, needs indirect register"; auto string2="Jump target too far away, needs force5Bytes = true"; auto addr1 = MemDbg::findBytes(string1, ::strlen(string1), processStartAddress, processStopAddress); auto addr2 = MemDbg::findBytes(string2, ::strlen(string2), processStartAddress, processStopAddress); if(addr1==0||addr2==0)return 0; //都是被push两次,但是都是第一个 addr1=MemDbg::findPushAddress(addr1, processStartAddress, processStopAddress); addr2=MemDbg::findPushAddress(addr2, processStartAddress, processStopAddress); if(addr1==0||addr2==0)return 0; addr1=MemDbg::findEnclosingAlignedFunction_strict(addr1,0x100); addr2=MemDbg::findEnclosingAlignedFunction_strict(addr2,0x100); if(addr1==0||addr2==0||addr1!=addr2)return 0; auto xrefs=findxref_reverse_checkcallop(addr1,processStartAddress,processStopAddress,0xe8); if(xrefs.size()<28)return 0; addr1=MemDbg::findEnclosingAlignedFunction_strict(xrefs[xrefs.size()-1-3],0x400); addr2=MemDbg::findEnclosingAlignedFunction_strict(xrefs[xrefs.size()-1-4],0x400); if(addr1==0||addr2==0||addr1!=addr2)return 0; return addr1; #else auto DoJitSig1 = "C7 83 ?? 0? 00 00 11 00 00 00 F6 83 ?? 0? 00 00 01 C7 83 ?? 0? 00 00 E4 00 00 00"; auto first=find_pattern(DoJitSig1,processStartAddress,processStopAddress); if (first) { auto beginSubSig1 = "55 41 ?? 41 ?? 41"; auto lookbackSize = 0x400; auto address=first-lookbackSize; auto subs=find_pattern(beginSubSig1,address,address+lookbackSize); if(subs){ return subs; } } else { auto DoJitSig2 = "C7 83 ?? 0? 00 00 11 00 00 00 F6 83 ?? 0? 00 00 01 ?? ?? ?? ?? ?? ?? ?? C7 83 ?? 0? 00 00 E4 00 00 00"; first=find_pattern(DoJitSig2,processStartAddress,processStopAddress); if (first) { first=MemDbg::findEnclosingAlignedFunction_strict(first,0x400); return first; } } #endif return 0; } namespace ppsspp{ bool checkiscurrentgame(const emfuncinfo& em){ auto wininfos=get_proc_windows(); for(auto&& info:wininfos){ if(info.title.find(acastw(em._id))!=info.title.npos)return true; } return false; } std::unordered_setbreakpoints; inline bool IsValidAddress(const uintptr_t address) { if ((address & 0x3E000000) == 0x08000000) { return true; } else if ((address & 0x3F800000) == 0x04000000) { return true; } else if ((address & 0xBFFFC000) == 0x00010000) { return true; } else if ((address & 0x3F000000) >= 0x08000000){// && (address & 0x3F000000) < 0x08000000 + g_MemorySize) { return true; } else { return false; } } void dohookemaddr(uintptr_t em_address,uintptr_t ret){ jitaddraddr(em_address,ret,JITTYPE::PPSSPP); if(emfunctionhooks.find(em_address)==emfunctionhooks.end())return; if(!(checkiscurrentgame(emfunctionhooks.at(em_address))))return; auto op=emfunctionhooks.at(em_address); ConsoleOutput("jit function addr %p",ret); #ifndef _WIN64 BYTE sig[]={ 0x8b,XX2,//mov reg,[ebp-off] 0x8b,0xc6,//mov eax,esi 0x25,0xff,0xff,0xff,0x3f,//and eax,0x3fffffff 0x89,XX,XX4,//mov [eax+base+off],reg }; auto findbase=MemDbg::findBytes(sig,sizeof(sig),ret,ret+0x20); if(!findbase) findbase=MemDbg::findBytes(sig,sizeof(sig),ret-0x1000,ret+0x1000); if(!findbase) ConsoleOutput("can't find emu_baseaddr"); PPSSPP::x86_baseaddr=(*(DWORD*)(findbase+12))&0xffff0000; ConsoleOutput("x86 base addr %p",PPSSPP::x86_baseaddr); #endif HookParam hpinternal; hpinternal.address=ret; hpinternal.emu_addr=em_address;//用于生成hcode hpinternal.type=USING_STRING|NO_CONTEXT|BREAK_POINT|op.type; hpinternal.text_fun=(decltype(hpinternal.text_fun))op.hookfunc; hpinternal.filter_fun=(decltype(hpinternal.filter_fun))op.filterfun; hpinternal.argidx=op.argidx; hpinternal.padding=op.padding; hpinternal.jittype=JITTYPE::PPSSPP; NewHook(hpinternal,op._id); } namespace{ typedef DWORD u32; typedef BYTE u8; typedef WORD u16; const int MAX_JIT_BLOCK_EXITS = 8; namespace Memory { struct Opcode { Opcode() { } explicit Opcode(u32 v) : encoding(v) { } u32 operator & (const u32& arg) const { return encoding & arg; } u32 operator >> (const u32& arg) const { return encoding >> arg; } bool operator == (const u32& arg) const { return encoding == arg; } bool operator != (const u32& arg) const { return encoding != arg; } u32 encoding; }; } typedef Memory::Opcode MIPSOpcode; struct JitBlock { bool ContainsAddress(u32 em_address) const; const u8* checkedEntry; // const, we have to translate to writable. const u8* normalEntry; u8* exitPtrs[MAX_JIT_BLOCK_EXITS]; // to be able to rewrite the exit jump u32 exitAddress[MAX_JIT_BLOCK_EXITS]; // 0xFFFFFFFF == unknown u32 originalAddress; MIPSOpcode originalFirstOpcode; //to be able to restore uint64_t compiledHash; u16 codeSize; u16 originalSize; u16 blockNum; bool invalid; bool linkStatus[MAX_JIT_BLOCK_EXITS]; #ifdef USE_VTUNE char blockName[32]; #endif // By having a pointer, we avoid a constructor/destructor being generated and dog slow // performance in debug. std::vector* proxyFor; bool IsPureProxy() const { return originalFirstOpcode.encoding == 0x68FF0000; } void SetPureProxy() { // Magic number that won't be a real opcode. originalFirstOpcode.encoding = 0x68FF0000; } }; } void unsafeoncegetJitBlockCache(hook_stack* stack){ //class JitBlockCache : public JitBlockCacheDebugInterface { //... //JitBlock *blocks_ = nullptr; //std::unordered_multimap proxyBlockMap_; ->64 //int num_blocks_ = 0; #ifdef _WIN64 auto num_blocks_=*(uint32_t*)(stack->rcx+72+16+88); auto blocks_=(JitBlock*)*(uintptr_t*)(stack->rcx+72+16+88-64-8); #else auto num_blocks_=*(uint32_t*)(stack->ecx+88); auto blocks_=(JitBlock*)*(uintptr_t*)(stack->ecx+88-32-4); #endif int checkvalid=0; num_blocks_-=1;//last one is now dojiting for(int i=0;iTHISCALLARG1; *(uintptr_t*)(hp->user_value)=em_address; HookParam hpinternal; hpinternal.user_value=hp->user_value; hpinternal.address=stack->retaddr; hpinternal.text_fun=[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){ auto em_address=*(uintptr_t*)(hp->user_value); if(!IsValidAddress(em_address))return; [&](){ auto ret=stack->RETADDR; if(breakpoints.find(ret)!=breakpoints.end())return; breakpoints.insert(ret); dohookemaddr(em_address,ret); }(); delayinsertNewHook(em_address); }; static auto once=NewHook(hpinternal,"DoJitPtrRet"); }; return NewHook(hp,"PPSSPPDoJit"); } } bool InsertPPSSPPcommonhooks() { auto succ=InsertPPSSPPHLEHooks(); succ|=ppsspp::hookPPSSPPDoJit(); return succ; }