#include"Lucifen.h" #include"embed_util.h" /******************************************************************************************** Lucifen hook: Game folder contains *.lpk. Used by Navel games. Hook is same to GetTextExtentPoint32A, use ESP to split name. ********************************************************************************************/ bool InsertLucifenHook() { // BOOL GetTextExtentPoint32( // _In_ HDC hdc, // _In_ LPCTSTR lpString, // _In_ int c, // _Out_ LPSIZE lpSize // ); HookParam hp; hp.address = (DWORD)::GetTextExtentPoint32A; hp.offset=get_stack(2); // arg2 lpString hp.split = get_reg(regs::esp); hp.length_offset = 3; hp.type = USING_STRING|USING_SPLIT; ConsoleOutput("INSERT Lucifen"); return NewHook(hp, "Lucifen"); //RegisterEngineType(ENGINE_LUCIFEN); } namespace{ bool hook(){ //まじかるカナン -RISEA- auto oldoutline=(ULONG)GetProcAddress(GetModuleHandle(L"gdi32.dll"),"GetGlyphOutline"); auto addr=MemDbg::findCallerAddress(oldoutline, 0xec8b55,processStartAddress, processStopAddress); if (addr == 0) addr=MemDbg::findCallerAddress((ULONG)GetGlyphOutlineA, 0xec8b55,processStartAddress, processStopAddress); if (addr == 0) return false; HookParam hp; hp.address = addr; hp.offset=get_stack(1); hp.split=get_stack(6); hp.type = CODEC_ANSI_BE |USING_SPLIT; return NewHook(hp, "Lucifen2"); } } bool hookBefore_navel(hook_stack*s,void* data, size_t* len,uintptr_t*role) { auto text = std::string((char*)s->stack[1]); // text in arg1 if(text.find("$&")!=text.npos){ text=text.substr(text.find("$&")+2); } if(text[text.size()-1]=='$') text=text.substr(0,text.size()-1); write_string_overwrite(data,len,text); return true; } void hookafter_navel(hook_stack*s,void* data, size_t len) { auto text = std::string((char*)s->stack[1]); // text in arg1 auto split = s->stack[0]; // retaddr std::string newData = std::string((char*)data,len); if(text.find("$&")!=text.npos){ newData=text.substr(0,text.find("$&")+2)+newData; } if(text[text.size()-1]=='$') newData=newData+"$"; strcpy((char*)s->stack[1], newData.c_str()); //s->stack[1] = (ULONG)newData.data(); } bool attach_navel(ULONG startAddress, ULONG stopAddress) // attach scenario { // 通过搜索3C 9F(i > 0x9Fu shiftjis范围判断)找到。 // int __thiscall sub_455AB0(int this, _BYTE *a2) // { // LPCSTR **v2; // ebx // int v3; // edi // _BYTE *v4; // ebp // char v5; // cl // _BYTE *v6; // ebx // int v7; // esi // unsigned __int8 v8; // al // char v9; // al // const CHAR **v10; // ebx // bool v11; // zf // const CHAR *v12; // eax // unsigned int v13; // esi // char *v14; // eax // char *v16; // ecx // unsigned __int8 v17; // al // char v18; // al // const CHAR ***v19; // ebp // const CHAR *v20; // esi // int v21; // eax // unsigned __int8 v22; // al // char v23; // cl // int v24; // esi // LPCSTR **j; // ebp // char v26; // al // LPCSTR **v27; // ebx // char v28; // al // char v29; // al // char v30; // al // unsigned int v31; // esi // unsigned __int8 *v32; // eax // char v33; // al // int v34; // eax // unsigned __int8 *v35; // ebx // unsigned __int8 v36; // al // char v37; // al // const CHAR ***v38; // ebp // const CHAR *v39; // esi // int v40; // eax // CHAR *v41; // edi // char v42; // al // unsigned __int8 v43; // al // unsigned __int8 v44; // al // unsigned __int16 *v45; // ebp // unsigned __int16 *v46; // edi // unsigned int v47; // eax // __int16 v48; // dx // unsigned __int16 *v49; // esi // unsigned int v51; // [esp+14h] [ebp-4h] // char *i; // [esp+1Ch] [ebp+4h] // unsigned int v53; // [esp+1Ch] [ebp+4h] const uint8_t bytes[] = { 0x50, 0xff,0x15,0xfc,0xd0,0x4e,0x00, 0x03,0xf0, 0x83,0xc3,0x04, 0xb1,0x01 }; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); if (addr == 0)return false; addr = MemDbg::findEnclosingAlignedFunction(addr); if (!addr) return false; HookParam hp; hp.address = addr; hp.type = EMBED_ABLE|EMBED_DYNA_SJIS; hp.hook_before=hookBefore_navel; hp.hook_after=hookafter_navel; hp.hook_font=F_GetGlyphOutlineA|F_GetTextExtentPoint32A; return NewHook(hp, "LucifenEmbed"); } #include"dyncodec/dynsjis.h" namespace { // unnamed namespace ScenarioHook { std::unordered_set textHashes_; namespace Private { ULONG scenarioOffset_, nameOffset_; std::string replaceNewLines(const std::string &data) { std::string ret; //ret.replace("\n", 1, "\x00\x5b\x0c\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00", 0xc + 2); for (auto p = data.c_str(); *p;) if (*p == '\n') { ret.append("\x00\x5b\x0c\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00", 0xc + 2); p++; } else { ret.push_back(*p++); if (*p && dynsjis::isleadbyte(p[-1])) ret.push_back(*p++); } //std::string ret; //do { // ret.append(start, p - start); // if (dynsjis::prevchar(p, start) == p - 1) { // ret.append("\x00\x5b\x0c\x00\x00\x00\x0e\x00\x00\x00\x00\x00\x00\x00", 0xc + 2); // p++; // } else { // start = p; // p = ::strchr(p, '\n'); // } //} while (p && *p); return ret; } /** * Sample game: 猫撫ディストーション * * 0x5b is the text to skip next character * * Ruby: * 014BB52C 81 77 8C F5 00 5B 1C 00 00 00 1B 00 00 00 01 00 『光.[....... * 014BB53C 00 00 03 0B 00 00 00 83 72 83 62 83 4F 83 6F 83 .. ...ビッグバ・ * 014BB54C 93 00 81 78 82 CC 91 4F 81 5C 81 5C 00 5B 0C 00 ・』の前――.[.. * 014BB55C 00 00 0E 00 00 00 00 00 00 00 82 C2 82 DC 82 E8 .........つまり * 014BB56C 81 41 89 46 92 88 82 AA 90 B6 82 DC 82 EA 82 E9 、宇宙が生まれる * 014BB57C 91 4F 82 A9 82 E7 82 A0 82 C1 82 BD 82 E0 82 CC 前からあったもの * 014BB58C 81 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 。.............. * 014BB59C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * * No ruby: * 014BB52C 82 B6 82 E1 82 A0 81 41 81 77 8C BE 97 74 81 78 じゃあ、『言葉』 * 014BB53C 82 C1 82 C4 89 BD 82 C8 82 F1 82 BE 81 48 6F 83 って何なんだ?o・ * 014BB54C 93 00 81 78 82 CC 91 4F 81 5C 81 5C 00 5B 0C 00 ・』の前――.[.. * 014BB55C 00 00 0E 00 00 00 00 00 00 00 82 C2 82 DC 82 E8 .........つまり * 014BB56C 81 41 89 46 92 88 82 AA 90 B6 82 DC 82 EA 82 E9 、宇宙が生まれる * 014BB57C 91 4F 82 A9 82 E7 82 A0 82 C1 82 BD 82 E0 82 CC 前からあったもの * 014BB58C 81 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 。.............. * 014BB59C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014BB5AC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014BB5BC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014BB5CC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * * 014BB52C 96 85 82 CC 8B D5 00 5B 16 00 00 00 1B 00 00 00 妹の琴.[...... * 014BB53C 01 00 00 00 03 05 00 00 00 82 B1 82 C6 00 8E 71 ......こと.子 * 014BB54C 00 5B 14 00 00 00 1B 00 00 00 01 00 00 00 03 03 .[......... * 014BB55C 00 00 00 82 B1 00 82 CD 82 BB 82 A4 8C BE 82 C1 ...こ.はそう言っ * 014BB56C 82 BD 81 42 82 C6 82 A2 82 A4 88 D3 96 A1 82 F0 た。という意味を * 014BB57C 97 5E 82 A6 82 BD 82 CC 81 76 82 BD 82 E0 82 CC 与えたの」たもの * 014BB58C 81 42 00 00 00 00 00 00 00 00 00 00 00 00 00 00 。.............. * 014BB59C 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014BB5AC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014BB5BC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014BB5CC 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * * New line: * 014D7D39 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7D49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7D59 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7D69 00 00 00 00 00 01 00 E6 01 00 00 54 01 00 00 00 ......・..T... * 014D7D79 00 00 00 B0 11 52 00 D8 CD 4D 01 44 EE E9 07 D8 ...ーR.リヘMD鵫リ * 014D7D89 CD 4D 01 00 00 00 00 00 00 00 00 00 00 00 00 00 ヘM............. * 014D7D99 00 00 00 F0 50 4E 01 0C 53 4E 01 F0 54 4E 01 10 ...N.SNN * 014D7DA9 00 00 00 00 00 00 00 82 BB 82 B5 82 C4 89 B4 82 .......そして俺・ * 014D7DB9 C9 82 E0 81 41 00 5B 0C 00 00 00 0E 00 00 00 00 ノも、.[........ * 014D7DC9 00 00 00 90 7E 96 5B 82 CC 82 B1 82 EB 82 A9 82 ...厨房のころか・ * 014D7DD9 E7 8E 6C 94 4E 8A D4 81 41 96 88 93 FA 91 B1 82 邇l年間、毎日続・ * 014D7DE9 AF 82 C4 82 A2 82 E9 82 B1 82 C6 82 AA 82 A0 82 ッていることがあ・ * 014D7DF9 E9 81 42 00 00 00 00 00 00 00 00 00 00 00 00 00 驕B............. * 014D7E09 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7E19 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7E29 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7E39 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7E49 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7E59 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7E69 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7E79 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7E89 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ * 014D7E99 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ */ template strT ltrimScenarioText(strT p) { while (p[0] == 0 && p[1] == 0x5b && p[2] > 0) p += p[2] + 2; return p; } std::string parseScenarioText(const char *p, const char *end) { int size = ::strlen(p); if (end > p && end - p < size) size = end - p; std::string ret; if (size) ret=std::string(p, size); //if ((uint8_t)p[ret.size() - 1] == 0x93 && (uint8_t)p[ret.size() - 1] == 0x83)// trim encindg \x83\x93 // return ret.left(ret.size() - 2); for (p += ret.size(); (!end || p < end) && p[1] == 0x5b && p[2] > 0; p += ret.size()) { //if (p[2] == 0xc && p[6] == 0xe) { // ret.push_back('\n'); // ret.push_back('\n'); // insert double new lines //} p += p[2] + 2; size = ::strlen(p); if (end > p && end - p < size) size = end - p; ret.append(p, size); } return ret; } bool dispatchNameText(char *text, ULONG split,hook_stack*s,void* data, size_t* len1,uintptr_t*role) { enum { capacity = 0x10 }; // excluding '\0' *role = Engine::NameRole ; if (!*text) return false; write_string_overwrite(data,len1,text); return true; } bool dispatchScenarioText(char *text, ULONG split,hook_stack*s,void* data, size_t* len1,uintptr_t*role) { // text[0] could be \0 * role = Engine::ScenarioRole ; auto scenarioEndAddress = (LPSTR *)(text + 0x1000); auto scenarioEnd = *scenarioEndAddress; if (!Engine::isAddressReadable(scenarioEnd)) scenarioEnd = nullptr; //DOUT("warning: scenario end NOT FOUND"); text = ltrimScenarioText(text); if (!*text) return false; std::string oldData = parseScenarioText(text, scenarioEnd); write_string_overwrite(data,len1,oldData); return true; } bool dispatchNameTextafter(char *text, ULONG split,hook_stack*s,void* data, uintptr_t len1 ) { std::string oldData = text; auto newData=std::string((char*)data,len1); enum { capacity = 0x10 }; // excluding '\0' int size = newData.size(); if (size > capacity) size = capacity; else if (size < oldData.size()) ::memset(text + size, 0, oldData.size() - size); ::memcpy(text, newData.c_str(), size); return true; } void dispatchScenarioTextafter(char *text, ULONG split,hook_stack*s,void* data, uintptr_t len1 ) { auto scenarioEndAddress = (LPSTR *)(text + 0x1000); auto scenarioEnd = *scenarioEndAddress; if (!Engine::isAddressReadable(scenarioEnd)) scenarioEnd = nullptr; //DOUT("warning: scenario end NOT FOUND"); text = ltrimScenarioText(text); if (!*text) return; std::string oldData = parseScenarioText(text, scenarioEnd); auto newData=std::string((char*)data,len1); if (newData.empty() || newData == oldData) return; if (newData.find('\n')!=newData.npos) newData = replaceNewLines(newData); if (scenarioEnd > text && scenarioEnd - text > newData.size()) ::memset(text + newData.size(), 0, scenarioEnd - text - newData.size()); else if (oldData.size() > newData.size()) ::memset(text + newData.size(), 0, oldData.size() - newData.size()); //::strcpy(text, newData.constData()); ::memcpy(text, newData.c_str(), newData.size() + 1); *scenarioEndAddress = text + newData.size(); // FIXME: THis sometimes does not work } bool hookBefore(hook_stack*s,void* data, size_t* len1,uintptr_t*role) { auto self = (LPSTR)s->ecx; ULONG retaddr = s->stack[0]; // bool b1= dispatchNameText(self + nameOffset_, retaddr,s,data,len1,role); bool b2=dispatchScenarioText(self + scenarioOffset_, retaddr,s,data,len1,role); return b2; } void hookafter(hook_stack*s,void* data, uintptr_t len1) { auto self = (LPSTR)s->ecx; ULONG retaddr = s->stack[0]; // dispatchNameTextafter(self + nameOffset_, retaddr,s,data,len1); dispatchScenarioTextafter(self + scenarioOffset_, retaddr,s,data,len1); } } // namespace Private /** * Debugging method: * - Hijack GetGlyphOutlineA * There is only one GetGlyphOutlineA * - Find all text in memory * There are two matches. * One is current text with fixed address * One is all text with fixed address * - Find all text address on the stack * There is one function use it as arg1 and as future text * ecx is the current text instead * * Sample game: プリズム・プリンセス * name = ecx + 0xadd1 * scenario = ecx + 0xae48 * scenario end = ecx + 0xbe48 * * 00441E3F 90 NOP * 00441E40 83EC 1C SUB ESP,0x1C * 00441E43 53 PUSH EBX * 00441E44 56 PUSH ESI * 00441E45 8BF1 MOV ESI,ECX * 00441E47 8B9E 48BE0000 MOV EBX,DWORD PTR DS:[ESI+0xBE48] * 00441E4D 2BDE SUB EBX,ESI * 00441E4F 81EB 48AE0000 SUB EBX,0xAE48 * 00441E55 75 0B JNZ SHORT .00441E62 * 00441E57 5E POP ESI * 00441E58 B8 01000000 MOV EAX,0x1 * 00441E5D 5B POP EBX * 00441E5E 83C4 1C ADD ESP,0x1C * 00441E61 C3 RETN * 00441E62 8B86 AC040000 MOV EAX,DWORD PTR DS:[ESI+0x4AC] * 00441E68 55 PUSH EBP * 00441E69 57 PUSH EDI * 00441E6A 50 PUSH EAX * 00441E6B 8BCE MOV ECX,ESI * 00441E6D E8 9E6CFFFF CALL .00438B10 * 00441E72 8A96 DE050000 MOV DL,BYTE PTR DS:[ESI+0x5DE] * 00441E78 8B8E 909E0000 MOV ECX,DWORD PTR DS:[ESI+0x9E90] * 00441E7E 8BBE 489E0000 MOV EDI,DWORD PTR DS:[ESI+0x9E48] * 00441E84 84D2 TEST DL,DL * 00441E86 0F94C0 SETE AL * 00441E89 84C0 TEST AL,AL * 00441E8B 884424 13 MOV BYTE PTR SS:[ESP+0x13],AL * 00441E8F C741 20 00000000 MOV DWORD PTR DS:[ECX+0x20],0x0 * 00441E96 74 0D JE SHORT .00441EA5 * 00441E98 8BCE MOV ECX,ESI * * 00441E9A E8 4136FFFF CALL .004354E0 * 00441E9F 8987 A8030000 MOV DWORD PTR DS:[EDI+0x3A8],EAX * 00441EA5 8D86 48AE0000 LEA EAX,DWORD PTR DS:[ESI+0xAE48] ; jichi: this is the scenari text * 00441EAB 53 PUSH EBX * 00441EAC 50 PUSH EAX * 00441EAD 8BCF MOV ECX,EDI * 00441EAF E8 EC6B0000 CALL .00448AA0 * 00441EB4 8D9E E2AD0000 LEA EBX,DWORD PTR DS:[ESI+0xADE2] ; jichi: this is the character name * 00441EBA 8D86 D1AD0000 LEA EAX,DWORD PTR DS:[ESI+0xADD1] ; jichi: this is the name text * 00441EC0 53 PUSH EBX * 00441EC1 50 PUSH EAX * 00441EC2 8BCF MOV ECX,EDI * 00441EC4 894424 1C MOV DWORD PTR SS:[ESP+0x1C],EAX * 00441EC8 E8 836B0000 CALL .00448A50 * * 00441ECD 8A4424 13 MOV AL,BYTE PTR SS:[ESP+0x13] * 00441ED1 84C0 TEST AL,AL * 00441ED3 74 30 JE SHORT .00441F05 * 00441ED5 6A 01 PUSH 0x1 * 00441ED7 8BCF MOV ECX,EDI * 00441ED9 E8 726D0000 CALL .00448C50 * 00441EDE 803B 00 CMP BYTE PTR DS:[EBX],0x0 * 00441EE1 74 22 JE SHORT .00441F05 * 00441EE3 8B86 00AE0000 MOV EAX,DWORD PTR DS:[ESI+0xAE00] * 00441EE9 85C0 TEST EAX,EAX * 00441EEB 75 18 JNZ SHORT .00441F05 * 00441EED 8B86 AC040000 MOV EAX,DWORD PTR DS:[ESI+0x4AC] * 00441EF3 8D97 D1030000 LEA EDX,DWORD PTR DS:[EDI+0x3D1] * 00441EF9 8996 00AE0000 MOV DWORD PTR DS:[ESI+0xAE00],EDX * 00441EFF 8986 C0040000 MOV DWORD PTR DS:[ESI+0x4C0],EAX * 00441F05 8A86 30A60000 MOV AL,BYTE PTR DS:[ESI+0xA630] * 00441F0B 84C0 TEST AL,AL * 00441F0D 0F84 DB000000 JE .00441FEE * 00441F13 8B86 C0A00000 MOV EAX,DWORD PTR DS:[ESI+0xA0C0] * 00441F19 85C0 TEST EAX,EAX * 00441F1B 0F84 CD000000 JE .00441FEE * 00441F21 8B96 E0A00000 MOV EDX,DWORD PTR DS:[ESI+0xA0E0] * 00441F27 8DAE E0A00000 LEA EBP,DWORD PTR DS:[ESI+0xA0E0] * 00441F2D 6A 00 PUSH 0x0 * 00441F2F 8BCD MOV ECX,EBP * 00441F31 FF92 B4000000 CALL DWORD PTR DS:[EDX+0xB4] * 00441F37 8B86 489E0000 MOV EAX,DWORD PTR DS:[ESI+0x9E48] * 00441F3D 8D8E 5C470000 LEA ECX,DWORD PTR DS:[ESI+0x475C] * 00441F43 8D96 14680000 LEA EDX,DWORD PTR DS:[ESI+0x6814] * 00441F49 898E E4050000 MOV DWORD PTR DS:[ESI+0x5E4],ECX * 00441F4F 894424 18 MOV DWORD PTR SS:[ESP+0x18],EAX * 00441F53 89AE 489E0000 MOV DWORD PTR DS:[ESI+0x9E48],EBP * 00441F59 C686 D8A00000 01 MOV BYTE PTR DS:[ESI+0xA0D8],0x1 * 00441F60 8996 E8050000 MOV DWORD PTR DS:[ESI+0x5E8],EDX * 00441F66 8B87 B4030000 MOV EAX,DWORD PTR DS:[EDI+0x3B4] * 00441F6C 6A 01 PUSH 0x1 * 00441F6E 8D4C24 20 LEA ECX,DWORD PTR SS:[ESP+0x20] * 00441F72 6A 01 PUSH 0x1 * 00441F74 51 PUSH ECX * 00441F75 50 PUSH EAX * 00441F76 8BCD MOV ECX,EBP * 00441F78 E8 935D0000 CALL .00447D10 * 00441F7D 8B5424 18 MOV EDX,DWORD PTR SS:[ESP+0x18] * 00441F81 8D8E EC050000 LEA ECX,DWORD PTR DS:[ESI+0x5EC] * 00441F87 8996 489E0000 MOV DWORD PTR DS:[ESI+0x9E48],EDX * 00441F8D 8D96 A4260000 LEA EDX,DWORD PTR DS:[ESI+0x26A4] * 00441F93 85C0 TEST EAX,EAX * 00441F95 C686 D8A00000 00 MOV BYTE PTR DS:[ESI+0xA0D8],0x0 * 00441F9C 898E E4050000 MOV DWORD PTR DS:[ESI+0x5E4],ECX * 00441FA2 8996 E8050000 MOV DWORD PTR DS:[ESI+0x5E8],EDX * 00441FA8 7E 44 JLE SHORT .00441FEE * 00441FAA 8A86 31A60000 MOV AL,BYTE PTR DS:[ESI+0xA631] * 00441FB0 84C0 TEST AL,AL * 00441FB2 74 0A JE SHORT .00441FBE * 00441FB4 33C0 XOR EAX,EAX * 00441FB6 8A86 32A60000 MOV AL,BYTE PTR DS:[ESI+0xA632] * 00441FBC EB 02 JMP SHORT .00441FC0 * 00441FBE 33C0 XOR EAX,EAX * 00441FC0 8B4C24 28 MOV ECX,DWORD PTR SS:[ESP+0x28] * 00441FC4 8B6C24 20 MOV EBP,DWORD PTR SS:[ESP+0x20] * 00441FC8 8B97 B8030000 MOV EDX,DWORD PTR DS:[EDI+0x3B8] * 00441FCE 50 PUSH EAX * 00441FCF 8B4424 18 MOV EAX,DWORD PTR SS:[ESP+0x18] * 00441FD3 2BCD SUB ECX,EBP * 00441FD5 53 PUSH EBX * 00441FD6 83C1 04 ADD ECX,0x4 * 00441FD9 50 PUSH EAX * 00441FDA 8B87 B4030000 MOV EAX,DWORD PTR DS:[EDI+0x3B4] * 00441FE0 51 PUSH ECX * 00441FE1 52 PUSH EDX * 00441FE2 50 PUSH EAX * 00441FE3 8D8E B8A00000 LEA ECX,DWORD PTR DS:[ESI+0xA0B8] * 00441FE9 E8 72290000 CALL .00444960 * 00441FEE 8B4C24 14 MOV ECX,DWORD PTR SS:[ESP+0x14] * 00441FF2 8D86 48AE0000 LEA EAX,DWORD PTR DS:[ESI+0xAE48] * 00441FF8 5F POP EDI * 00441FF9 8986 48BE0000 MOV DWORD PTR DS:[ESI+0xBE48],EAX * 00441FFF 5D POP EBP * 00442000 C603 00 MOV BYTE PTR DS:[EBX],0x0 * 00442003 5E POP ESI * 00442004 C601 00 MOV BYTE PTR DS:[ECX],0x0 * 00442007 33C0 XOR EAX,EAX * 00442009 5B POP EBX * 0044200A 83C4 1C ADD ESP,0x1C * 0044200D C3 RETN * 0044200E 90 NOP * 0044200F 90 NOP * * Sample game: 猫撫ディストーション * name = ecx + 0xc60f * scenario = ecx + 0xc684 * scenario end = ecx + 0xd684 * * 0043E11E 90 NOP * 0043E11F 90 NOP * 0043E120 83EC 18 SUB ESP,0x18 * 0043E123 53 PUSH EBX * 0043E124 55 PUSH EBP * 0043E125 56 PUSH ESI * 0043E126 8BF1 MOV ESI,ECX * 0043E128 57 PUSH EDI * 0043E129 8BAE 84D60000 MOV EBP,DWORD PTR DS:[ESI+0xD684] ; jichi: overall offset is around 0xD684 * 0043E12F 2BEE SUB EBP,ESI * 0043E131 81ED 84C60000 SUB EBP,0xC684 * 0043E137 896C24 10 MOV DWORD PTR SS:[ESP+0x10],EBP * 0043E13B 75 0D JNZ SHORT .0043E14A * 0043E13D 5F POP EDI * 0043E13E 5E POP ESI * 0043E13F 5D POP EBP * 0043E140 B8 01000000 MOV EAX,0x1 * 0043E145 5B POP EBX * 0043E146 83C4 18 ADD ESP,0x18 * 0043E149 C3 RETN * 0043E14A 8B86 A8040000 MOV EAX,DWORD PTR DS:[ESI+0x4A8] * 0043E150 8BCE MOV ECX,ESI * 0043E152 50 PUSH EAX * 0043E153 E8 3875FFFF CALL .00435690 * 0043E158 8B9E F4B20000 MOV EBX,DWORD PTR DS:[ESI+0xB2F4] * 0043E15E 8BBE D8B10000 MOV EDI,DWORD PTR DS:[ESI+0xB1D8] * 0043E164 8B43 14 MOV EAX,DWORD PTR DS:[EBX+0x14] * 0043E167 85C0 TEST EAX,EAX * 0043E169 7D 7C JGE SHORT .0043E1E7 * 0043E16B 8B8E 70040000 MOV ECX,DWORD PTR DS:[ESI+0x470] * 0043E171 6A 00 PUSH 0x0 * 0043E173 8D96 20C60000 LEA EDX,DWORD PTR DS:[ESI+0xC620] ; jichi: 0xc620 is the nearest position * 0043E179 6A 00 PUSH 0x0 * 0043E17B 52 PUSH EDX * 0043E17C 6A FE PUSH -0x2 * 0043E17E E8 ED93FEFF CALL .00427570 * 0043E183 8BE8 MOV EBP,EAX * 0043E185 85ED TEST EBP,EBP * 0043E187 7C 0D JL SHORT .0043E196 * 0043E189 45 INC EBP * 0043E18A 83FD 08 CMP EBP,0x8 * 0043E18D 7C 09 JL SHORT .0043E198 * 0043E18F BD 07000000 MOV EBP,0x7 * 0043E194 EB 02 JMP SHORT .0043E198 * 0043E196 33ED XOR EBP,EBP * 0043E198 396B 1C CMP DWORD PTR DS:[EBX+0x1C],EBP * 0043E19B 74 46 JE SHORT .0043E1E3 * 0043E19D 8B8F 4C020000 MOV ECX,DWORD PTR DS:[EDI+0x24C] * 0043E1A3 85C9 TEST ECX,ECX * 0043E1A5 75 0D JNZ SHORT .0043E1B4 * 0043E1A7 5F POP EDI * 0043E1A8 5E POP ESI * 0043E1A9 5D POP EBP * 0043E1AA B8 02000000 MOV EAX,0x2 * 0043E1AF 5B POP EBX * 0043E1B0 83C4 18 ADD ESP,0x18 * 0043E1B3 C3 RETN * 0043E1B4 8BC5 MOV EAX,EBP * 0043E1B6 6A 00 PUSH 0x0 * 0043E1B8 C1E0 04 SHL EAX,0x4 * 0043E1BB 03C5 ADD EAX,EBP * 0043E1BD 6A 00 PUSH 0x0 * 0043E1BF 6A 00 PUSH 0x0 * 0043E1C1 6A 00 PUSH 0x0 * 0043E1C3 8D94C6 48BA0000 LEA EDX,DWORD PTR DS:[ESI+EAX*8+0xBA48] * 0043E1CA 52 PUSH EDX * 0043E1CB E8 E0DD0200 CALL .0046BFB0 * 0043E1D0 896B 1C MOV DWORD PTR DS:[EBX+0x1C],EBP * 0043E1D3 8B07 MOV EAX,DWORD PTR DS:[EDI] * 0043E1D5 6A 01 PUSH 0x1 * 0043E1D7 6A 01 PUSH 0x1 * 0043E1D9 6A 01 PUSH 0x1 * 0043E1DB 8BCF MOV ECX,EDI * 0043E1DD FF90 4C010000 CALL DWORD PTR DS:[EAX+0x14C] * 0043E1E3 8B6C24 10 MOV EBP,DWORD PTR SS:[ESP+0x10] * 0043E1E7 8BCE MOV ECX,ESI * 0043E1E9 C743 20 00000000 MOV DWORD PTR DS:[EBX+0x20],0x0 * * 0043E1F0 E8 3B46FFFF CALL .00432830 * 0043E1F5 8987 A0030000 MOV DWORD PTR DS:[EDI+0x3A0],EAX * 0043E1FB 8D86 84C60000 LEA EAX,DWORD PTR DS:[ESI+0xC684] ; jichi: this is scenario * 0043E201 55 PUSH EBP * 0043E202 50 PUSH EAX * 0043E203 8BCF MOV ECX,EDI * 0043E205 E8 765F0000 CALL .00444180 * 0043E20A 8D9E 20C60000 LEA EBX,DWORD PTR DS:[ESI+0xC620] ; jichi: this is the chara name, such as KOT0 * 0043E210 8D86 0FC60000 LEA EAX,DWORD PTR DS:[ESI+0xC60F] ; jichi: this is the name address * 0043E216 53 PUSH EBX * 0043E217 50 PUSH EAX * 0043E218 8BCF MOV ECX,EDI * 0043E21A 894424 18 MOV DWORD PTR SS:[ESP+0x18],EAX * 0043E21E E8 0D5F0000 CALL .00444130 * * 0043E223 6A 01 PUSH 0x1 * 0043E225 8BCF MOV ECX,EDI * 0043E227 E8 04600000 CALL .00444230 * 0043E22C 8A86 40BA0000 MOV AL,BYTE PTR DS:[ESI+0xBA40] * 0043E232 84C0 TEST AL,AL * 0043E234 0F84 DB000000 JE .0043E315 * 0043E23A 8B86 18B50000 MOV EAX,DWORD PTR DS:[ESI+0xB518] * 0043E240 85C0 TEST EAX,EAX * 0043E242 0F84 CD000000 JE .0043E315 * 0043E248 8B96 38B50000 MOV EDX,DWORD PTR DS:[ESI+0xB538] * 0043E24E 8DAE 38B50000 LEA EBP,DWORD PTR DS:[ESI+0xB538] * 0043E254 6A 00 PUSH 0x0 * 0043E256 8BCD MOV ECX,EBP * 0043E258 FF92 B4000000 CALL DWORD PTR DS:[EDX+0xB4] * 0043E25E 8B86 D8B10000 MOV EAX,DWORD PTR DS:[ESI+0xB1D8] * 0043E264 8D8E 70460000 LEA ECX,DWORD PTR DS:[ESI+0x4670] * 0043E26A 8D96 28670000 LEA EDX,DWORD PTR DS:[ESI+0x6728] * 0043E270 898E F8040000 MOV DWORD PTR DS:[ESI+0x4F8],ECX * 0043E276 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX * 0043E27A 89AE D8B10000 MOV DWORD PTR DS:[ESI+0xB1D8],EBP * 0043E280 C686 30B50000 01 MOV BYTE PTR DS:[ESI+0xB530],0x1 * 0043E287 8996 FC040000 MOV DWORD PTR DS:[ESI+0x4FC],EDX * 0043E28D 8B87 AC030000 MOV EAX,DWORD PTR DS:[EDI+0x3AC] * 0043E293 6A 01 PUSH 0x1 * 0043E295 8D4C24 1C LEA ECX,DWORD PTR SS:[ESP+0x1C] * 0043E299 6A 01 PUSH 0x1 * 0043E29B 51 PUSH ECX * 0043E29C 50 PUSH EAX * 0043E29D 8BCD MOV ECX,EBP * 0043E29F E8 DC570000 CALL .00443A80 * 0043E2A4 8B5424 14 MOV EDX,DWORD PTR SS:[ESP+0x14] * 0043E2A8 8D8E 00050000 LEA ECX,DWORD PTR DS:[ESI+0x500] * 0043E2AE 8996 D8B10000 MOV DWORD PTR DS:[ESI+0xB1D8],EDX * 0043E2B4 8D96 B8250000 LEA EDX,DWORD PTR DS:[ESI+0x25B8] * 0043E2BA 85C0 TEST EAX,EAX * 0043E2BC C686 30B50000 00 MOV BYTE PTR DS:[ESI+0xB530],0x0 * 0043E2C3 898E F8040000 MOV DWORD PTR DS:[ESI+0x4F8],ECX * 0043E2C9 8996 FC040000 MOV DWORD PTR DS:[ESI+0x4FC],EDX * 0043E2CF 7E 44 JLE SHORT .0043E315 * 0043E2D1 8A86 41BA0000 MOV AL,BYTE PTR DS:[ESI+0xBA41] * 0043E2D7 84C0 TEST AL,AL * 0043E2D9 74 0A JE SHORT .0043E2E5 * 0043E2DB 33C0 XOR EAX,EAX * 0043E2DD 8A86 42BA0000 MOV AL,BYTE PTR DS:[ESI+0xBA42] * 0043E2E3 EB 02 JMP SHORT .0043E2E7 * 0043E2E5 33C0 XOR EAX,EAX * 0043E2E7 8B4C24 24 MOV ECX,DWORD PTR SS:[ESP+0x24] * 0043E2EB 8B6C24 1C MOV EBP,DWORD PTR SS:[ESP+0x1C] * 0043E2EF 8B97 B0030000 MOV EDX,DWORD PTR DS:[EDI+0x3B0] * 0043E2F5 50 PUSH EAX * 0043E2F6 8B4424 14 MOV EAX,DWORD PTR SS:[ESP+0x14] * 0043E2FA 2BCD SUB ECX,EBP * 0043E2FC 53 PUSH EBX * 0043E2FD 83C1 04 ADD ECX,0x4 * 0043E300 50 PUSH EAX * 0043E301 8B87 AC030000 MOV EAX,DWORD PTR DS:[EDI+0x3AC] * 0043E307 51 PUSH ECX * 0043E308 52 PUSH EDX * 0043E309 50 PUSH EAX * 0043E30A 8D8E 10B50000 LEA ECX,DWORD PTR DS:[ESI+0xB510] * 0043E310 E8 7B270000 CALL .00440A90 * 0043E315 803B 00 CMP BYTE PTR DS:[EBX],0x0 * 0043E318 74 0C JE SHORT .0043E326 * 0043E31A 81C7 C9030000 ADD EDI,0x3C9 * 0043E320 89BE 3CC60000 MOV DWORD PTR DS:[ESI+0xC63C],EDI * 0043E326 8B4C24 10 MOV ECX,DWORD PTR SS:[ESP+0x10] * 0043E32A 8D86 84C60000 LEA EAX,DWORD PTR DS:[ESI+0xC684] * 0043E330 8986 84D60000 MOV DWORD PTR DS:[ESI+0xD684],EAX * 0043E336 5F POP EDI * 0043E337 5E POP ESI * 0043E338 C603 00 MOV BYTE PTR DS:[EBX],0x0 * 0043E33B 5D POP EBP * 0043E33C C601 00 MOV BYTE PTR DS:[ECX],0x0 * 0043E33F 33C0 XOR EAX,EAX * 0043E341 5B POP EBX * 0043E342 83C4 18 ADD ESP,0x18 * 0043E345 C3 RETN * 0043E346 90 NOP * 0043E347 90 NOP * 0043E348 90 NOP * 0043E349 90 NOP * 0043E34A 90 NOP * 0043E34B 90 NOP */ bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario { const uint8_t bytes[] = { 0xe8, XX4, // 0043e1f0 e8 3b46ffff call .00432830 0x89,0x87, XX4, // 0043e1f5 8987 a0030000 mov dword ptr ds:[edi+0x3a0],eax 0x8d,0x86, XX4, // 0043e1fb 8d86 84c60000 lea eax,dword ptr ds:[esi+0xc684] ; jichi: this is scenario // 0043e201 55 push ebp // 0043e202 50 push eax XX4, // 0043e203 8bcf mov ecx,edi 0xe8, XX4, // 0043e205 e8 765f0000 call .00444180 0x8d,0x9e, XX4, // 0043e20a 8d9e 20c60000 lea ebx,dword ptr ds:[esi+0xc620] ; jichi: this is the chara name, such as kot0 0x8d,0x86, XX4, // 0043e210 8d86 0fc60000 lea eax,dword ptr ds:[esi+0xc60f] ; jichi: this is the name address 0x53, // 0043e216 53 push ebx 0x50, // 0043e217 50 push eax 0x8b,0xcf, // 0043e218 8bcf mov ecx,edi 0x89,0x44,0x24, XX, // 0043e21a 894424 18 mov dword ptr ss:[esp+0x18],eax 0xe8 //, XX4 // 0043e21e e8 0d5f0000 call .00444130 }; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); if (!addr) return false; Private::scenarioOffset_ = *(DWORD *)(addr + 2 + 0x0043e1fb - 0x0043e1f0); Private::nameOffset_ = *(DWORD *)(addr + 2 + 0x0043e210 - 0x0043e1f0); if ((Private::scenarioOffset_ >> 16) || // offset high bits are zero (Private::nameOffset_ >> 16)) return false; addr = MemDbg::findEnclosingAlignedFunction(addr); if (!addr) return false; HookParam hp; hp.address=addr; hp.type=EMBED_ABLE|EMBED_DYNA_SJIS; hp.hook_before=Private::hookBefore; hp.hook_after=Private::hookafter; hp.hook_font=F_GetGlyphOutlineA|F_GetTextExtentPoint32A; return NewHook(hp,"EmbedLucifen"); } } // namespace ScenarioHook namespace ChoiceHook { namespace Private { bool hookBefore(hook_stack*s,void* data, size_t* len1,uintptr_t*role) { static std::string data_; auto text = (LPCSTR)s->stack[0]; // arg1 is text if (!text || !*text) return text; *role=Engine::ChoiceRole; write_string_overwrite(data,len1,text); return true; } void hookafter(hook_stack*s,void* data, size_t len1){ auto newData =std::string((char*)data,len1); strcpy((char*)s->stack[0], newData.c_str()); } } // namespace Private /** * Debugging method: * - Hijack GetGlyphOutlineA * - Backtrack stack to find text that used as argument * * Sample game: プリズム・プリンセス * * Text in arg1. * * The function is only called by one caller. * I suspect it is a virtual function, and hence caller is hooked. * * 0044235E 90 NOP * 0044235F 90 NOP * 00442360 83EC 08 SUB ESP,0x8 * 00442363 53 PUSH EBX * 00442364 56 PUSH ESI * 00442365 8BF1 MOV ESI,ECX * 00442367 BB 01000000 MOV EBX,0x1 * 0044236C 8A86 E2050000 MOV AL,BYTE PTR DS:[ESI+0x5E2] * 00442372 84C0 TEST AL,AL * 00442374 75 14 JNZ SHORT .0044238A * 00442376 889E BD040000 MOV BYTE PTR DS:[ESI+0x4BD],BL * 0044237C E8 BFFAFFFF CALL .00441E40 * 00442381 85C0 TEST EAX,EAX * 00442383 0F94C0 SETE AL * 00442386 84C0 TEST AL,AL * 00442388 74 16 JE SHORT .004423A0 * 0044238A 53 PUSH EBX * 0044238B 6A 00 PUSH 0x0 * 0044238D 8BCE MOV ECX,ESI * 0044238F E8 2C80FFFF CALL .0043A3C0 * 00442394 85C0 TEST EAX,EAX * 00442396 74 16 JE SHORT .004423AE * 00442398 5E POP ESI * 00442399 5B POP EBX * 0044239A 83C4 08 ADD ESP,0x8 * 0044239D C2 0400 RETN 0x4 * 004423A0 8B86 88040000 MOV EAX,DWORD PTR DS:[ESI+0x488] * 004423A6 8BCE MOV ECX,ESI * 004423A8 50 PUSH EAX * 004423A9 E8 32120700 CALL .004B35E0 * 004423AE 8B96 949E0000 MOV EDX,DWORD PTR DS:[ESI+0x9E94] * 004423B4 55 PUSH EBP * 004423B5 8DAE 949E0000 LEA EBP,DWORD PTR DS:[ESI+0x9E94] * 004423BB 57 PUSH EDI * 004423BC 8BCD MOV ECX,EBP * 004423BE C686 BD040000 00 MOV BYTE PTR DS:[ESI+0x4BD],0x0 * 004423C5 FF92 80000000 CALL DWORD PTR DS:[EDX+0x80] * 004423CB 8B86 44040000 MOV EAX,DWORD PTR DS:[ESI+0x444] * 004423D1 85C0 TEST EAX,EAX * 004423D3 74 05 JE SHORT .004423DA * 004423D5 83C0 18 ADD EAX,0x18 * 004423D8 EB 02 JMP SHORT .004423DC * 004423DA 33C0 XOR EAX,EAX * 004423DC 8B8E A0A00000 MOV ECX,DWORD PTR DS:[ESI+0xA0A0] * 004423E2 8B7C24 1C MOV EDI,DWORD PTR SS:[ESP+0x1C] * 004423E6 8B55 00 MOV EDX,DWORD PTR SS:[EBP] * 004423E9 51 PUSH ECX * 004423EA 8B4F 4C MOV ECX,DWORD PTR DS:[EDI+0x4C] * 004423ED 51 PUSH ECX * 004423EE 50 PUSH EAX * 004423EF 8BCD MOV ECX,EBP * 004423F1 FF92 AC000000 CALL DWORD PTR DS:[EDX+0xAC] * 004423F7 B8 02000000 MOV EAX,0x2 * 004423FC 8D4F 08 LEA ECX,DWORD PTR DS:[EDI+0x8] * 004423FF 8339 00 CMP DWORD PTR DS:[ECX],0x0 * 00442402 74 0B JE SHORT .0044240F * 00442404 83C0 02 ADD EAX,0x2 * 00442407 83C1 08 ADD ECX,0x8 * 0044240A 83F8 12 CMP EAX,0x12 * 0044240D ^7C F0 JL SHORT .004423FF * 0044240F D1F8 SAR EAX,1 * 00442411 48 DEC EAX * 00442412 8BF8 MOV EDI,EAX * 00442414 8A86 30A60000 MOV AL,BYTE PTR DS:[ESI+0xA630] * 0044241A 84C0 TEST AL,AL * 0044241C 897C24 14 MOV DWORD PTR SS:[ESP+0x14],EDI * 00442420 89BE 9CA00000 MOV DWORD PTR DS:[ESI+0xA09C],EDI * 00442426 0F84 B9000000 JE .004424E5 * 0044242C 8B86 C0A00000 MOV EAX,DWORD PTR DS:[ESI+0xA0C0] * 00442432 85C0 TEST EAX,EAX * 00442434 0F84 AB000000 JE .004424E5 * 0044243A 57 PUSH EDI * 0044243B 8D8E B8A00000 LEA ECX,DWORD PTR DS:[ESI+0xA0B8] * 00442441 885C24 17 MOV BYTE PTR SS:[ESP+0x17],BL * 00442445 E8 46270000 CALL .00444B90 * 0044244A 33DB XOR EBX,EBX * 0044244C 85FF TEST EDI,EDI * 0044244E 7E 64 JLE SHORT .004424B4 * 00442450 8B5424 1C MOV EDX,DWORD PTR SS:[ESP+0x1C] * 00442454 8D7A 0C LEA EDI,DWORD PTR DS:[EDX+0xC] * 00442457 8A941E B8040000 MOV DL,BYTE PTR DS:[ESI+EBX+0x4B8] * 0044245E 8B45 00 MOV EAX,DWORD PTR SS:[EBP] * 00442461 6A 00 PUSH 0x0 * 00442463 6A 00 PUSH 0x0 * 00442465 84D2 TEST DL,DL * 00442467 8B17 MOV EDX,DWORD PTR DS:[EDI] * 00442469 6A 00 PUSH 0x0 * 0044246B 0F954424 28 SETNE BYTE PTR SS:[ESP+0x28] * 00442470 8B4C24 28 MOV ECX,DWORD PTR SS:[ESP+0x28] * 00442474 6A 00 PUSH 0x0 * 00442476 6A FF PUSH -0x1 * 00442478 6A 00 PUSH 0x0 * 0044247A 6A FF PUSH -0x1 * 0044247C 51 PUSH ECX * 0044247D 6A 00 PUSH 0x0 * 0044247F 52 PUSH EDX * 00442480 8BCD MOV ECX,EBP * 00442482 FF90 84000000 CALL DWORD PTR DS:[EAX+0x84] ; .004BBD00 ; jichi: text called here, text on the top * 00442488 8A4424 13 MOV AL,BYTE PTR SS:[ESP+0x13] * 0044248C 84C0 TEST AL,AL * 0044248E 74 18 JE SHORT .004424A8 * 00442490 8A5424 1C MOV DL,BYTE PTR SS:[ESP+0x1C] * 00442494 8B0F MOV ECX,DWORD PTR DS:[EDI] * 00442496 84D2 TEST DL,DL * 00442498 0F94C0 SETE AL * 0044249B 50 PUSH EAX * 0044249C 51 PUSH ECX * 0044249D 8D8E B8A00000 LEA ECX,DWORD PTR DS:[ESI+0xA0B8] * 004424A3 E8 48280000 CALL .00444CF0 * 004424A8 8B4424 14 MOV EAX,DWORD PTR SS:[ESP+0x14] * 004424AC 83C7 08 ADD EDI,0x8 * 004424AF 43 INC EBX * 004424B0 3BD8 CMP EBX,EAX * 004424B2 ^7C A3 JL SHORT .00442457 * 004424B4 8A4424 13 MOV AL,BYTE PTR SS:[ESP+0x13] * 004424B8 5F POP EDI * 004424B9 84C0 TEST AL,AL * 004424BB 5D POP EBP * 004424BC 74 12 JE SHORT .004424D0 * 004424BE 8D96 34A60000 LEA EDX,DWORD PTR DS:[ESI+0xA634] * 004424C4 8D8E B8A00000 LEA ECX,DWORD PTR DS:[ESI+0xA0B8] * 004424CA 52 PUSH EDX * 004424CB E8 B0280000 CALL .00444D80 * 004424D0 33C0 XOR EAX,EAX * 004424D2 81C6 B8040000 ADD ESI,0x4B8 * 004424D8 8906 MOV DWORD PTR DS:[ESI],EAX * 004424DA 8846 04 MOV BYTE PTR DS:[ESI+0x4],AL * 004424DD 5E POP ESI * 004424DE 5B POP EBX * 004424DF 83C4 08 ADD ESP,0x8 * 004424E2 C2 0400 RETN 0x4 * 004424E5 C64424 13 00 MOV BYTE PTR SS:[ESP+0x13],0x0 * 004424EA ^E9 5BFFFFFF JMP .0044244A * 004424EF 90 NOP * 004424F0 8B4424 04 MOV EAX,DWORD PTR SS:[ESP+0x4] * 004424F4 8B40 04 MOV EAX,DWORD PTR DS:[EAX+0x4] * 004424F7 85C0 TEST EAX,EAX * 004424F9 7C 0D JL SHORT .00442508 * 004424FB 83F8 05 CMP EAX,0x5 * 004424FE 7D 08 JGE SHORT .00442508 * 00442500 C68408 B8040000 >MOV BYTE PTR DS:[EAX+ECX+0x4B8],0x1 * 00442508 33C0 XOR EAX,EAX * 0044250A C2 0400 RETN 0x4 * 0044250D 90 NOP * 0044250E 90 NOP */ bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario { const uint8_t bytes[] = { 0xff,0x90, 0x84,0x00,0x00,0x00, // 00442482 ff90 84000000 call dword ptr ds:[eax+0x84] ; .004bbd00 ; jichi: text called here, text on the top 0x8a,0x44,0x24, 0x13 // 00442488 8a4424 13 mov al,byte ptr ss:[esp+0x13] }; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); if(addr==0)return false; HookParam hp; hp.address=addr; hp.type=EMBED_ABLE|EMBED_DYNA_SJIS; hp.hook_before=Private::hookBefore; hp.hook_after=Private::hookafter; hp.hook_font=F_GetGlyphOutlineA|F_GetTextExtentPoint32A; return NewHook(hp,"lucifen_choice"); } } // namespace ChoiceHook size_t countZero(const char *s, size_t limit=1500) { size_t count = 0; for (auto p = s; !*p && count < limit; p++, count++); return count == limit ? 0 : count; }bool hookBefore(hook_stack*s,void* data, size_t* len1,uintptr_t*role) { auto text = (LPSTR)s->stack[1]; // arg1 is text if (!text || ::strlen(text) <= 2 ) return false; *role=Engine::OtherRole; std::string oldData =text; strcpy((char*)data,oldData.c_str());*len1=oldData.size(); return true; } void hookafter(hook_stack*s,void* data, size_t len1){ auto text = (LPSTR)s->stack[1]; // arg1 is text enum { role = Engine::OtherRole };std::string oldData = text ; auto split = s->stack[0]; auto newData =std::string((char*)data,len1); size_t capacity = countZero(text + oldData.size()); if (!capacity) return ; capacity += oldData.size() - 1; if (newData.size() > capacity) newData = newData.substr(0,capacity); if (newData.size() < oldData.size()) ::memset(text + newData.size(), 0, oldData.size() - newData.size()); ::strcpy(text, newData.c_str()); return ; } bool attach11(ULONG startAddress, ULONG stopAddress) // attach scenario { //这个的对话都是一个个字的,但是名字是连续的。 const uint8_t bytes[] = { 0x83,0xec, 0x14, // 00461ca0 83ec 14 sub esp,0x14 0x33,0xd2, // 00461ca3 33d2 xor edx,edx 0x55, // 00461ca5 55 push ebp 0x56, // 00461ca6 56 push esi 0x8b,0x74,0x24, 0x20, // 00461ca7 8b7424 20 mov esi,dword ptr ss:[esp+0x20] 0x8b,0xe9, // 00461cab 8be9 mov ebp,ecx 0x3b,0xf2, // 00461cad 3bf2 cmp esi,edx 0x0f,0x84, 0x55,0x02,0x00,0x00, // 00461caf 0f84 55020000 je .00461f0a 0x39,0x55, 0x08, // 00461cb5 3955 08 cmp dword ptr ss:[ebp+0x8],edx 0x0f,0x84, 0x4c,0x02,0x00,0x00, // 00461cb8 0f84 4c020000 je .00461f0a 0x8b,0x85, 0x74,0x20,0x00,0x00 // 00461cbe 8b85 74200000 mov eax,dword ptr ss:[ebp+0x2074] }; ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress); if(addr==0)return false; HookParam hp; hp.address=addr; hp.offset=get_stack(1); hp.type=EMBED_ABLE|EMBED_DYNA_SJIS; hp.hook_after=hookafter; hp.hook_before=hookBefore; hp.hook_font=F_GetGlyphOutlineA|F_GetTextExtentPoint32A; return NewHook(hp,"Embedlucifen2"); } } bool Lucifen::attach_function() { bool b1=ScenarioHook::attach(processStartAddress,processStopAddress)|| attach_navel(processStartAddress,processStopAddress); if(b1){ ChoiceHook::attach(processStartAddress,processStopAddress); attach11(processStartAddress,processStopAddress); } bool succ=InsertLucifenHook(); succ|=hook(); return succ; }