mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-12-25 12:44:13 +08:00
238 lines
8.9 KiB
C++
238 lines
8.9 KiB
C++
#include"YukaSystem2.h"
|
||
/** jichi 7/6/2014 YukaSystem2
|
||
* Sample game: セミラミスの天秤
|
||
*
|
||
* Observations from Debug:
|
||
* - Ollydbg got UTF8 text memory address
|
||
* - Hardware break points have loops on 0x4010ED
|
||
* - The hooked function seems to take 3 parameters, and arg3 is the right text
|
||
* - The text appears character by character
|
||
*
|
||
* Runtime stack:
|
||
* - return address
|
||
* - arg1 pointer's pointer
|
||
* - arg2 text
|
||
* - arg3 pointer's pointer
|
||
* - code address or -1, maybe a handle
|
||
* - unknown pointer
|
||
* - return address
|
||
* - usually zero
|
||
*
|
||
* 0040109d cc int3
|
||
* 0040109e cc int3
|
||
* 0040109f cc int3
|
||
* 004010a0 /$ 55 push ebp
|
||
* 004010a1 |. 8bec mov ebp,esp
|
||
* 004010a3 |. 8b45 14 mov eax,dword ptr ss:[ebp+0x14]
|
||
* 004010a6 |. 50 push eax ; /arg4
|
||
* 004010a7 |. 8b4d 10 mov ecx,dword ptr ss:[ebp+0x10] ; |
|
||
* 004010aa |. 51 push ecx ; |arg3
|
||
* 004010ab |. 8b55 0c mov edx,dword ptr ss:[ebp+0xc] ; |
|
||
* 004010ae |. 52 push edx ; |arg2
|
||
* 004010af |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8] ; |
|
||
* 004010b2 |. 50 push eax ; |arg1
|
||
* 004010b3 |. e8 48ffffff call semirami.00401000 ; \semirami.00401000
|
||
* 004010b8 |. 83c4 10 add esp,0x10
|
||
* 004010bb |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
|
||
* 004010be |. 5d pop ebp
|
||
* 004010bf \. c3 retn
|
||
* 004010c0 /$ 55 push ebp
|
||
* 004010c1 |. 8bec mov ebp,esp
|
||
* 004010c3 |. 8b45 14 mov eax,dword ptr ss:[ebp+0x14]
|
||
* 004010c6 |. 50 push eax ; /arg4
|
||
* 004010c7 |. 8b4d 10 mov ecx,dword ptr ss:[ebp+0x10] ; |
|
||
* 004010ca |. 51 push ecx ; |arg3
|
||
* 004010cb |. 8b55 0c mov edx,dword ptr ss:[ebp+0xc] ; |
|
||
* 004010ce |. 52 push edx ; |arg2
|
||
* 004010cf |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8] ; |
|
||
* 004010d2 |. 50 push eax ; |arg1
|
||
* 004010d3 |. e8 58ffffff call semirami.00401030 ; \semirami.00401030
|
||
* 004010d8 |. 83c4 10 add esp,0x10
|
||
* 004010db |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
|
||
* 004010de |. 5d pop ebp
|
||
* 004010df \. c3 retn
|
||
* 004010e0 /$ 55 push ebp ; jichi: function begin, hook here, bp-based frame, arg2 is the text
|
||
* 004010e1 |. 8bec mov ebp,esp
|
||
* 004010e3 |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8] ; jichi: ebp+0x8 = arg2
|
||
* 004010e6 |. 8b4d 0c mov ecx,dword ptr ss:[ebp+0xc] ; jichi: arg3 is also a pointer of pointer
|
||
* 004010e9 |. 8a11 mov dl,byte ptr ds:[ecx]
|
||
* 004010eb |. 8810 mov byte ptr ds:[eax],dl ; jichi: eax is the data
|
||
* 004010ed |. 5d pop ebp
|
||
* 004010ee \. c3 retn
|
||
* 004010ef cc int3
|
||
*/
|
||
|
||
// Ignore image and music file names
|
||
// Sample text: "Voice\tou00012.ogg""運命論って云うのかなあ……神さまを信じてる人が多かったからだろうね、何があっても、それ<E3819D>神さまが<E381BE>刁<EFBFBD>ちに与えられた試練なんだって、そ<E3819D>ってたみたい。勿論、今でもそ<E3819D><E38180>てあ<E381A6>人はぁ<E381AF>ぱぁ<E381B1>るん<E3828B>けど<E38191>
|
||
// Though the input string is UTF-8, it should be ASCII compatible.
|
||
static bool _yk2garbage(const char *p)
|
||
{
|
||
//Q_ASSERT(p);
|
||
while (char ch = *p++) {
|
||
if (!(
|
||
ch >= '0' && ch <= '9' ||
|
||
ch >= 'A' && ch <= 'z' || // also ignore ASCII 91-96: [ \ ] ^ _ `
|
||
ch == '"' || ch == '.' || ch == '-' || ch == '#'
|
||
))
|
||
return false;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
// Get text from arg2
|
||
static void SpecialHookYukaSystem2(hook_stack* stack, HookParam *hp, uintptr_t *data, uintptr_t *split, size_t*len)
|
||
{
|
||
DWORD arg2 = stack->stack[2], // [esp+0x8]
|
||
arg3 = stack->stack[3]; // [esp+0xc]
|
||
//arg4 = argof(4, esp_base); // there is no arg4. arg4 is properlly a function pointer
|
||
LPCSTR text = (LPCSTR)arg2;
|
||
if (*text && !_yk2garbage(text)) { // I am sure this could be null
|
||
*data = (DWORD)text;
|
||
*len = ::strlen(text); // UTF-8 is null-terminated
|
||
if (arg3)
|
||
*split = *(DWORD *)arg3;
|
||
}
|
||
}
|
||
|
||
|
||
bool InsertYukaSystem2Hook()
|
||
{
|
||
const BYTE bytes[] = {
|
||
0x55, // 004010e0 /$ 55 push ebp ; jichi; hook here
|
||
0x8b,0xec, // 004010e1 |. 8bec mov ebp,esp
|
||
0x8b,0x45, 0x08, // 004010e3 |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8] ; jichi: ebp+0x8 = arg2
|
||
0x8b,0x4d, 0x0c, // 004010e6 |. 8b4d 0c mov ecx,dword ptr ss:[ebp+0xc]
|
||
0x8a,0x11, // 004010e9 |. 8a11 mov dl,byte ptr ds:[ecx]
|
||
0x88,0x10, // 004010eb |. 8810 mov byte ptr ds:[eax],dl ; jichi: eax is the address to text
|
||
0x5d, // 004010ed |. 5d pop ebp
|
||
0xc3 // 004010ee \. c3 retn
|
||
};
|
||
//enum { addr_offset = 0 };
|
||
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||
//GROWL_DWORD(addr); // supposed to be 0x4010e0
|
||
if (!addr) {
|
||
ConsoleOutput("YukaSystem2: pattern not found");
|
||
return false;
|
||
}
|
||
|
||
HookParam hp;
|
||
hp.address = addr;
|
||
hp.offset=get_stack(1);
|
||
hp.split=get_stack(2);
|
||
hp.type =USING_SPLIT| USING_STRING|CODEC_UTF8; // UTF-8, though
|
||
hp.filter_fun=[](void* data, size_t* len, HookParam* hp){
|
||
//セミラミスの天秤
|
||
//セミラミスの天秤 Fated Dolls
|
||
if(data==0)return false;
|
||
|
||
if(all_ascii(reinterpret_cast<char*>(data),*len))return false;
|
||
auto str=std::string(reinterpret_cast<char*>(data),*len);
|
||
|
||
str = std::regex_replace(str, std::regex(R"(@r\((.*?),(.*?)\))"), "$1");
|
||
|
||
auto wstr=StringToWideString(str);
|
||
|
||
if(wstr.size()==1)return false;
|
||
|
||
for(auto wc:wstr){
|
||
if((wc>='A' && wc<='z')||
|
||
(wc>='0' && wc<='9')||
|
||
(wc=='"')||(wc=='.')||(wc=='-')||(wc=='#')||
|
||
(wc==65533)||(wc==2))return false;
|
||
}
|
||
|
||
*len = (str.size()) ;
|
||
strcpy(reinterpret_cast<char*>(data), str.c_str());
|
||
return true;
|
||
};
|
||
//hp.text_fun = SpecialHookYukaSystem2;
|
||
ConsoleOutput("INSERT YukaSystem2");
|
||
return NewHook(hp, "YukaSystem2");
|
||
}
|
||
namespace{
|
||
bool hook2(){
|
||
//君を仰ぎ乙女は姫に
|
||
//ずっとつくしてあげるの!
|
||
const BYTE bytes[] = {
|
||
0x0F,0xB6,0x07,
|
||
0x83,0xE8,0x40,
|
||
0x75,XX,
|
||
0x0F,0xB6,0x47,0x01,
|
||
0x83,0xE8,0x67,
|
||
0x8D,0x4F,0x01,
|
||
0x75,XX,
|
||
0x0F,0xB6,0x41,0x01,
|
||
0x83,0xC1,0x01,
|
||
0x83,0xE8,0x66,
|
||
0x74,XX
|
||
};
|
||
//enum { addr_offset = 0 };
|
||
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||
//GROWL_DWORD(addr); // supposed to be 0x4010e0
|
||
if (!addr) return false;
|
||
addr = MemDbg::findEnclosingAlignedFunction(addr);
|
||
if (!addr) return false;
|
||
HookParam hp;
|
||
hp.address = addr;
|
||
hp.offset=get_stack(2);
|
||
hp.type = USING_SPLIT|DATA_INDIRECT;
|
||
hp. index=0;
|
||
hp.split=get_stack(1);
|
||
return NewHook(hp, "YukaSystem2");
|
||
}
|
||
}
|
||
namespace __{
|
||
bool YukaSystem1Filter(LPVOID data, size_t *size, HookParam *)
|
||
{
|
||
auto text = reinterpret_cast<LPSTR>(data);
|
||
auto len = reinterpret_cast<size_t *>(size);
|
||
|
||
if (*len == 0) return false;
|
||
|
||
// if acii add a space at the end of the sentence overwriting null terminator
|
||
if (*len >=2 && text[*len-2]>0)
|
||
text[(*len)++] = ' ';
|
||
|
||
if (cpp_strnstr(text, "@r(", *len)) {
|
||
StringFilterBetween(text, len, "@r(", 3, ")", 1); // @r(2,はと)
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool InsertYukaSystem1Hook()
|
||
{
|
||
/*
|
||
* Sample games:
|
||
* https://vndb.org/r71601
|
||
* https://vndb.org/v7507
|
||
*/
|
||
const BYTE bytes[] = {
|
||
0x80, 0x3D, XX4, 0x01, // cmp byte ptr [kimihime.exe+16809C],01 << hook here
|
||
0x75, 0x11, // jne kimihime.exe+42D74
|
||
0xB9, XX4, // mov ecx,kimihime.exe+C7F8C
|
||
0xC6, 0x05, XX4, 0x00 // mov byte ptr [kimihime.exe+1516C5],00
|
||
};
|
||
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||
if (!addr) {
|
||
ConsoleOutput("YukaSystem1: pattern not found");
|
||
return false;
|
||
}
|
||
|
||
HookParam hp;
|
||
hp.address = addr;
|
||
hp.offset=get_reg(regs::eax);
|
||
hp.type = USING_STRING | KNOWN_UNSTABLE;
|
||
hp.filter_fun = YukaSystem1Filter;
|
||
ConsoleOutput("INSERT YukaSystem1");
|
||
return NewHook(hp, "YukaSystem1");
|
||
}
|
||
}
|
||
|
||
bool YukaSystem2::attach_function() {
|
||
bool _1=__::InsertYukaSystem1Hook();
|
||
return InsertYukaSystem2Hook()||hook2()||_1;
|
||
}
|