1023 lines
44 KiB
C++
Raw Normal View History

2024-02-07 20:59:24 +08:00
#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);
strcpy((char*)data,text.c_str());
*len=text.size();
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<UINT64> 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.SNN
* 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 <typename strT>
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;
std::string oldData = text;
strcpy((char*)data,oldData.c_str());*len1=oldData.size();
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);
strcpy((char*)data,oldData.c_str());*len1=oldData.size();
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;
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 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;
}