mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-12-25 12:44:13 +08:00
307 lines
12 KiB
C++
307 lines
12 KiB
C++
#include"Majiro.h"
|
||
|
||
/** jichi 12/28/2014: new Majiro hook pattern
|
||
*
|
||
* Different function starts:
|
||
*
|
||
* Old Majiro:
|
||
* enum { sub_esp = 0xec81 }; // caller pattern: sub esp = 0x81,0xec byte
|
||
*
|
||
* New Majiro since [141128] [アトリエさくら] 流され妻、綾<E38081>“ネトラレ”<E383AC><E2809D>体験版
|
||
* 003e9230 55 push ebp
|
||
* 003e9231 8bec mov ebp,esp
|
||
* 003e9233 83ec 64 sub esp,0x64
|
||
*
|
||
* Also, function addresses are fixed in old majiro, but floating in new majiro.
|
||
* In the old Majiro game, caller's address could be used as split.
|
||
* In the new Majiro game, the hooked function is invoked by the same caller.
|
||
*
|
||
* Use a split instead.
|
||
* Sample stack values are as follows.
|
||
* - Old majiro: arg3 is text, arg1 is font name
|
||
* - New majiro: arg3 is text, arg4 is font name
|
||
*
|
||
* Name:
|
||
* 0038f164 003e8163 return to .003e8163 from .003e9230
|
||
* 0038f168 00000000
|
||
* 0038f16c 00000000
|
||
* 0038f170 08b04dbc ; jichi: arg3, text
|
||
* 0038f174 006709f0 ; jichi: arg4, font name
|
||
* 0038f178 006dace8
|
||
* 0038f17c 00000000
|
||
* 0038f180 00000013
|
||
* 0038f184 006fcba8
|
||
* 0038f188 00000078 ; jichi: 0x24, alternative split
|
||
* 0038f18c 00000078
|
||
* 0038f190 00000018
|
||
* 0038f194 00000002
|
||
* 0038f198 08b04dbc
|
||
* 0038f19c 006709f0
|
||
* 0038f1a0 00000000
|
||
* 0038f1a4 00000000
|
||
* 0038f1a8 00000078
|
||
* 0038f1ac 00000018
|
||
* 0038f1b0 08aa0130
|
||
* 0038f1b4 01b6b6c0
|
||
* 0038f1b8 beff26e4
|
||
* 0038f1bc 0038f1fc
|
||
* 0038f1c0 004154af return to .004154af from .00415400 ; jichi: 0x52, could be used as split
|
||
* 0038f1c4 0000000e
|
||
* 0038f1c8 000001ae
|
||
* 0038f1cc 00000158
|
||
* 0038f1d0 00000023
|
||
* 0038f1d4 beff2680
|
||
* 0038f1d8 0038f208
|
||
* 0038f1dc 003ecfda return to .003ecfda from .00415400
|
||
*
|
||
* Scenario:
|
||
* 0038e57c 003e8163 return to .003e8163 from .003e9230
|
||
* 0038e580 00000000
|
||
* 0038e584 00000000
|
||
* 0038e588 0038ee4c ; jichi: arg3, text
|
||
* 0038e58c 004d5400 .004d5400 ; jichi: arg4, font name
|
||
* 0038e590 006dace8
|
||
* 0038e594 0038ee6d
|
||
* 0038e598 004d7549 .004d7549
|
||
* 0038e59c 00000000
|
||
* 0038e5a0 00000180 ; jichi: 0x24, alternative hook
|
||
* 0038e5a4 00000180
|
||
* 0038e5a8 00000018
|
||
* 0038e5ac 00000002
|
||
* 0038e5b0 0038ee4c
|
||
* 0038e5b4 004d5400 .004d5400
|
||
* 0038e5b8 00000000
|
||
* 0038e5bc 00000000
|
||
* 0038e5c0 00000180
|
||
* 0038e5c4 00000018
|
||
* 0038e5c8 006a0180
|
||
* 0038e5cc 0038e5f8
|
||
* 0038e5d0 0041fc87 return to .0041fc87 from .0041fc99
|
||
* 0038e5d4 0038e5f8
|
||
* 0038e5d8 00418165 return to .00418165 from .0041fc81 ; jichi: used as split
|
||
* 0038e5dc 004d7549 .004d7549
|
||
* 0038e5e0 0038ee6d
|
||
* 0038e5e4 0038e608
|
||
* 0038e5e8 00419555 return to .00419555 from .0041814e
|
||
* 0038e5ec 00000000
|
||
* 0038e5f0 004d7549 .004d7549
|
||
* 0038e5f4 0038ee6d
|
||
*
|
||
* 12/4/2014: Add split for furigana.
|
||
* Sample game: [141128] [チュアブルソフト] 残念な俺達<E4BFBA>青春事情
|
||
* Following are memory values after arg4 (font name)
|
||
*
|
||
* Surface: <20> * 00EC5400 82 6C 82 72 20 82 6F 83 53 83 56 83 62 83 4E 00 <20><> <20>ゴシヂ<E382B7>.
|
||
* 00EC5410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||
* 00EC5420 01 00 00 00 00 00 00 00 1C 00 00 00 0D 00 00 00 ....... .......
|
||
* 00EC5430 (2D)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00 -...<2E><><EFBFBD>.... .... ; jichi: first byte as split in parenthesis
|
||
* 00EC5440 00(00 00 00)60 F7 3F 00 F0 D8 FF FF 00 00 00 00 ....`・. .... ; jichi: first word without first byte as split
|
||
*
|
||
* 00EC5450 32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00 2 ......<2E> ..・..
|
||
* 00EC5460 00 00 00 00 01 00 00 00 00 00 00 00 32 01 00 00 .... .......2 ..
|
||
* 00EC5470 14 00 00 00 01 00 00 00 82 6C 82 72 20 82 6F 83 ... ...<2E><> <20>・ ; MS P Gothic
|
||
* 00EC5480 53 S
|
||
*
|
||
* Furigana: そ<>
|
||
* 00EC5400 82 6C 82 72 20 83 53 83 56 83 62 83 4E 00 4E 00 <20><> ゴシヂ<E382B7>.N.
|
||
* 00EC5410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||
* 00EC5420 01 00 00 00 00 00 00 00 0E 00 00 00 06 00 00 00 ....... ... ...
|
||
* 00EC5430 (16)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00 ...<2E><><EFBFBD>.... ....
|
||
* 00EC5440 00(00 00 00)60 F7 3F 00 F0 D8 FF FF 00 00 00 00 ....`・. ....
|
||
*
|
||
* 00EC5450 32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00 2 ......<2E> ..・..
|
||
* 00EC5460 00 00 00 00 00 00 00 00 00 00 00 00 32 01 00 00 ............2 ..
|
||
* 00EC5470 14 00 00 00 01 00 00 00 82 6C 82 72 20 82 6F 83 ... ...<2E><> <20>・ ; MS P Gothic
|
||
* 00EC5480 53 S
|
||
*
|
||
* Furigana: そ<>
|
||
* 00EC5400 82 6C 82 72 20 82 6F 83 53 83 56 83 62 83 4E 00 <20><> <20>ゴシヂ<E382B7>.
|
||
* 00EC5410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||
* 00EC5420 01 00 00 00 00 00 00 00 0E 00 00 00 06 00 00 00 ....... ... ...
|
||
* 00EC5430 (2D)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00 -...<2E><><EFBFBD>.... ....
|
||
* 00EC5440 00(00 00 00)60 F7 3F 00 2B 01 00 00 06 00 00 00 ....`・.+ .. ...
|
||
*
|
||
* 00EC5450 32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00 2 ......<2E> ..・..
|
||
* 00EC5460 00 00 00 00 00 00 00 00 00 00 00 00 32 01 00 00 ............2 ..
|
||
* 00EC5470 14 00 00 00 01 00 00 00 82 6C 82 72 20 82 6F 83 ... ...<2E><> <20>・ ; MS P Gothic
|
||
* 00EC5480 53 S
|
||
*
|
||
* ---- need to split the above and below case
|
||
*
|
||
* Text: <20> * 00EC5400 82 6C 82 72 20 82 6F 83 53 83 56 83 62 83 4E 00 <20><> <20>ゴシヂ<E382B7>.
|
||
* 00EC5410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||
* 00EC5420 01 00 00 00 00 00 00 00 1C 00 00 00 0D 00 00 00 ....... .......
|
||
* 00EC5430 (2D)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00 -...<2E><><EFBFBD>.... .... ; jichi: first byte as split in parenthesis
|
||
* 00EC5440 FF(FF FF FF)60 F7 3F 00 32 01 00 00 14 00 00 00 <20><><EFBFBD><EFBFBD>`・.2 .. ... ; jichi: first word without first byte as split
|
||
*
|
||
* 00EC5450 32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00 2 ......<2E> ..・..
|
||
* 00EC5460 00 00 00 00 01 00 00 00 00 00 00 00 32 01 00 00 .... .......2 ..
|
||
* 00EC5470 14 00 00 00 00 00 00 00 82 6C 82 72 20 82 6F 83 .......<2E><> <20>・ ; MS P Gothic
|
||
* 00EC5480 53 S
|
||
*
|
||
* Text: らには、一人の少女<E5B091> * 00EC5400 82 6C 82 72 20 82 6F 83 53 83 56 83 62 83 4E 00 <20><> <20>ゴシヂ<E382B7>.
|
||
* 00EC5410 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
|
||
* 00EC5420 01 00 00 00 00 00 00 00 1C 00 00 00 0D 00 00 00 ....... .......
|
||
* 00EC5430 (2D)00 00 00 FF FF FF 00 00 00 00 02 00 00 00 00 -...<2E><><EFBFBD>.... ....
|
||
* 00EC5440 FF(FF FF FF)60 F7 3F 00 4D 01 00 00 14 00 00 00 <20><><EFBFBD><EFBFBD>`・.M .. ...
|
||
*
|
||
* 00EC5450 32 01 00 00 0C 00 00 00 A0 02 00 00 88 00 00 00 2 ......<2E> ..・..
|
||
* 00EC5460 00 00 00 00 01 00 00 00 00 00 00 00 32 01 00 00 .... .......2 ..
|
||
* 00EC5470 14 00 00 00 00 00 00 00 82 6C 82 72 20 82 6F 83 .......<2E><> <20>・ ; MS P Gothic
|
||
* 00EC5480 53 S
|
||
*/
|
||
|
||
namespace { // unnamed
|
||
|
||
// These values are the same as the assembly logic of ITH:
|
||
// ([eax+0x28] & 0xff) | (([eax+0x48] >> 1) & 0xffffff00)
|
||
// 0x28 = 10 * 4, 0x48 = 18 / 4
|
||
inline DWORD MajiroOldFontSplit(const DWORD *arg) // arg is supposed to be a string, though
|
||
{ return (arg[10] & 0xff) | ((arg[18] >> 1) & 0xffffff00); }
|
||
|
||
// Remove lower bytes use 0xffffff00, which are different for furigana
|
||
inline DWORD MajiroNewFontSplit(const DWORD *arg) // arg is supposed to be a string, though
|
||
{ return (arg[12] & 0xff) | (arg[16] & 0xffffff00); }
|
||
|
||
void SpecialHookMajiro(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len)
|
||
{
|
||
DWORD arg3 = stack->stack[3]; // text
|
||
*data = arg3;
|
||
*len = ::strlen((LPCSTR)arg3);
|
||
// IsBadReadPtr is not needed for old Majiro game.
|
||
// I am not sure if it is needed by new Majiro game.
|
||
if (hp->user_value) { // new majiro
|
||
if (DWORD arg4 = stack->stack[4]) // old majiro
|
||
*split = MajiroNewFontSplit((LPDWORD)arg4);
|
||
else
|
||
*split = *(DWORD *)(stack->base + 0x5c); // = 4 * 23, caller's caller
|
||
} else if (DWORD arg1 = stack->stack[1]) // old majiro
|
||
*split = MajiroOldFontSplit((LPDWORD)arg1);
|
||
}
|
||
} // unnamed namespace
|
||
bool InsertMajiroHook()
|
||
{
|
||
// jichi 4/19/2014: There must be a function in Majiro game which contains 6 TextOutA.
|
||
// That function draws all texts.
|
||
//
|
||
// jichi 11/28/2014: Add new function signature
|
||
const DWORD funcs[] = { // caller patterns
|
||
0xec81, // sub esp = 0x81,0xec byte old majiro
|
||
0x83ec8b55, // mov ebp,esp, sub esp,* new majiro
|
||
|
||
0x5348ec83
|
||
// sub esp, 48h, push ebx
|
||
//MOON CHILDe
|
||
//https://vndb.org/v1568
|
||
|
||
};
|
||
enum { FunctionCount = sizeof(funcs) / sizeof(*funcs) };
|
||
ULONG addr = MemDbg::findMultiCallerAddress((ULONG)::TextOutA, funcs, FunctionCount, processStartAddress, processStopAddress);
|
||
//ULONG addr = MemDbg::findCallerAddress((ULONG)::TextOutA, 0x83ec8b55, processStartAddress, processStopAddress);
|
||
if (!addr) {
|
||
ConsoleOutput("Majiro: failed");
|
||
return false;
|
||
}
|
||
|
||
bool newMajiro = 0x55 == *(BYTE *)addr;
|
||
|
||
HookParam hp;
|
||
//hp.type|=USING_STRING|USING_SPLIT|SPLIT_INDIRECT;
|
||
hp.address = addr;
|
||
hp.text_fun = SpecialHookMajiro;
|
||
hp.user_value = newMajiro;
|
||
if (newMajiro) {
|
||
hp.type = NO_CONTEXT; // do not use return address for new majiro
|
||
ConsoleOutput("INSERT Majiro2");
|
||
return NewHook(hp, "Majiro2");
|
||
} else {
|
||
ConsoleOutput("INSERT Majiro");
|
||
return NewHook(hp, "Majiro");
|
||
}
|
||
//RegisterEngineType(ENGINE_MAJIRO);
|
||
}
|
||
bool InsertMajiroHook3x() {
|
||
const BYTE bytes[] = {
|
||
0x8b,0x08,
|
||
0x0f,0xbf,0x19,
|
||
0x83,0xc1,0x02,
|
||
};
|
||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
|
||
|
||
if (addr == 0)return false;
|
||
HookParam hp;
|
||
hp.address = addr+8;
|
||
hp.offset=get_reg(regs::ecx);
|
||
hp.type = USING_STRING | NO_CONTEXT;//|EMBED_ABLE|EMBED_BEFORE_SIMPLE|EMBED_AFTER_OVERWRITE|EMBED_DYNA_SJIS;
|
||
//可以内嵌,但是必须保持「」,且DynamicEncoding编码的文字会被自动替换成引擎内的某的字符,导致可读性低。
|
||
//hp.hook_font=F_TextOutA|F_GetTextExtentPoint32A;
|
||
//https://vndb.org/v17376
|
||
//私が好きなら「好き」って言って!
|
||
hp.text_fun= [](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len){
|
||
auto str=(BYTE*)(*data);
|
||
*len=strlen((char*)str);
|
||
if(((*len)>2)&&(str[0]==0x81)&&(str[1]==0x79))*split=0;
|
||
else *split=1;
|
||
|
||
};
|
||
return NewHook(hp, "majiro3");
|
||
}
|
||
bool InsertMajiro2Hookx() {
|
||
//Scarlett~スカーレット~
|
||
const BYTE bytes[] = {
|
||
0x83,0xE2,0x03,0x03,0xC2,0xC1,0xF8,0x02,0x81,0xF9,0x00,0x01,0x00,0x00
|
||
};
|
||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
|
||
|
||
if (addr == 0)return false;
|
||
addr = MemDbg::findEnclosingAlignedFunction(addr);
|
||
if (addr == 0)return false;
|
||
HookParam hp;
|
||
hp.address = addr ;
|
||
hp.offset=get_stack(2);
|
||
hp.type = USING_STRING ;
|
||
ConsoleOutput("INSERT majiro4 %p",addr);
|
||
return NewHook(hp, "majiro4");
|
||
}
|
||
bool InsertMajiro3Hook()
|
||
{
|
||
|
||
/*
|
||
* Sample games:
|
||
* Narcissu 10th Anniversary Anthology Project
|
||
* https://vndb.org/v10
|
||
* https://vndb.org/v70
|
||
* https://vndb.org/v18738
|
||
* https://vndb.org/v18739
|
||
* https://vndb.org/v18736
|
||
*/
|
||
const BYTE bytes[] = {
|
||
0xC1, 0xE9, 0x02, // shr ecx,02 << hook here
|
||
0xF3, 0xA5, // repe movsd
|
||
0x8B, 0xCA, // mov ecx,edx
|
||
0x8D, 0x95, XX4 // lea edx,[ebp-00000404]
|
||
};
|
||
|
||
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||
if (!addr) {
|
||
ConsoleOutput("Majiro3: pattern not found");
|
||
return false;
|
||
}
|
||
|
||
HookParam hp;
|
||
hp.address = addr;
|
||
hp.offset=get_reg(regs::esi);
|
||
hp.type = USING_STRING;
|
||
ConsoleOutput("INSERT Majiro3");
|
||
ConsoleOutput("Majiro3: To separate the text between lines flag the \"Flush delay string spacing\" option");
|
||
return NewHook(hp, "Majiro3");
|
||
}
|
||
bool Majiro::attach_function() {
|
||
|
||
bool b1= InsertMajiroHook();
|
||
bool b2=InsertMajiroHook3x();
|
||
bool b3=InsertMajiro2Hookx();
|
||
bool b4=InsertMajiro3Hook();
|
||
return b1||b2||b3||b4;
|
||
}
|