2024-02-07 20:59:24 +08:00

307 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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;
}