LunaHook-mirror/LunaHook/engine32/Candy.cpp

365 lines
11 KiB
C++
Raw Normal View History

2024-08-12 16:46:39 +08:00
#include "Candy.h"
2024-02-07 20:59:24 +08:00
/********************************************************************************************
CandySoft hook:
Game folder contains many *.fpk. Engine name is SystemC.
I haven't seen this engine in other company/brand.
AGTH /X3 will hook lstrlenA. One thread is the exactly result we want.
But the function call is difficult to located programmatically.
I find a equivalent points which is more easy to search.
The script processing function needs to find 0x5B'[',
so there should a instruction like cmp reg,5B
Find this position and navigate to function entry.
The first parameter is the string pointer.
This approach works fine with game later than <EFBFBD>
But the original <EFBFBD>is quite different. I handle this case separately.
********************************************************************************************/
2024-08-12 16:46:39 +08:00
namespace
{
// https://vndb.org/v23666
2024-02-29 16:25:16 +08:00
//(18禁ゲーム) [180928] [INTERHEART glossy] はらかつ3 ~子作りビジネス廃業の危機!?~ (iso+mds+rr3)
2024-08-12 16:46:39 +08:00
// https://vndb.org/v47957
2024-02-29 16:25:16 +08:00
//[240222][1261652][DESSERT Soft] 二股野郎とパパ活姉妹 パッケージ版 (mdf+mds)
2024-08-12 16:46:39 +08:00
// https://vndb.org/v20368
2024-02-29 16:25:16 +08:00
//[170224] [Sweet HEART] アイドル★クリニック 恋の薬でHな処方 (iso+mds+rr3)
2024-08-12 16:46:39 +08:00
bool filter(LPVOID data, size_t *size, HookParam *)
2024-02-29 16:25:16 +08:00
{
2024-08-12 16:46:39 +08:00
StringFilter((char *)data, size, "$L", 2);
StringFilter((char *)data, size, "$M", 2);
StringFilter((char *)data, size, "$S", 2);
StringFilterBetween((char *)data, size, "[", 1, "]", 1);
StringFilterBetween((char *)data, size, "&", 1, ";", 1);
2024-02-29 16:25:16 +08:00
return true;
// else
// {
// v18 = *v16++;
// switch ( v18 )
// {
// case '$':
// switch ( *v16 )
// {
// case 0:
// goto LABEL_44;
// case 76:
// v15 = 3;
// break;
// case 77:
// if ( v15 < 2 )
// v15 = 2;
// break;
// default:
// if ( *v16 == 83 && !v15 )
// v15 = 1;
// break;
// }
// break;
// case '[':
// for ( i = *v16; i; i = *++v16 )
// {
// if ( i == 93 )
// break;
// }
// break;
// case '&':
// for ( j = *v16; j; j = *++v16 )
// {
// if ( j == 59 )
// break;
// }
// break;
// default:
// goto LABEL_43;
// }
// ++v16;
// }
}
uintptr_t hh()
{
2024-08-12 16:46:39 +08:00
// void __usercall sub_425580(char *a1@<edx>, int a2@<ecx>, int a3)
BYTE bytes[] = {
//clang-format off
0x3c, 0x24,
0x75, XX,
0x80, 0x7e, 0x01, 0x00,
0x74, XX,
0x83, XX, 0x02,
0x83, XX, 0x02,
//clang-format on
2024-02-29 16:25:16 +08:00
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
2024-08-12 16:46:39 +08:00
if (!addr)
return 0;
addr = findfuncstart(addr, 0x400);
2024-02-29 16:25:16 +08:00
return addr;
}
}
2024-02-07 20:59:24 +08:00
2024-08-12 16:46:39 +08:00
namespace
{ // unnamed Candy
2024-02-07 20:59:24 +08:00
2024-08-12 16:46:39 +08:00
// jichi 8/23/2013: split into two different engines
// if (_wcsicmp(processName, L"systemc.exe")==0)
// Process name is "SystemC.exe"
bool InsertCandyHook1()
{
for (DWORD i = processStartAddress + 0x1000; i < processStopAddress - 4; i++)
if ((*(DWORD *)i & 0xffffff) == 0x24f980) // cmp cl,24
for (DWORD j = i, k = i - 0x100; j > k; j--)
if (*(DWORD *)j == 0xc0330a8a)
{ // mov cl,[edx]; xor eax,eax
HookParam hp;
hp.address = j;
hp.offset = get_reg(regs::edx);
hp.type = USING_STRING;
ConsoleOutput("INSERT SystemC#1");
2024-02-07 20:59:24 +08:00
2024-08-12 16:46:39 +08:00
// RegisterEngineType(ENGINE_CANDY);
return NewHook(hp, "SystemC");
}
ConsoleOutput("CandyHook1: failed");
return false;
2024-02-29 16:25:16 +08:00
}
2024-08-12 16:46:39 +08:00
uintptr_t __InsertCandyHook2()
{
for (DWORD i = processStartAddress + 0x1000; i < processStopAddress - 4; i++)
if (*(WORD *)i == 0x5b3c || // cmp al,0x5b
(*(DWORD *)i & 0xfff8fc) == 0x5bf880) // cmp reg,0x5B
for (DWORD j = i, k = i - 0x100; j > k; j--)
if ((*(DWORD *)j & 0xffff) == 0x8b55)
{ // push ebp, mov ebp,esp, sub esp,*
return j;
}
return 0;
2024-02-29 16:25:16 +08:00
}
2024-08-12 16:46:39 +08:00
// jichi 8/23/2013: Process name is NOT "SystemC.exe"
bool InsertCandyHook2()
{
auto addr1 = hh(); // 新版本的candy但是有时会和旧版在同一个地址。当是同一个地址时避让5个字节
auto addr2 = __InsertCandyHook2();
HookParam hp;
hp.type = USING_STRING;
hp.filter_fun = filter;
if (addr2 == 0 && addr1 == 0)
return false;
else if (addr2 == 0 && addr1 != 0)
{
hp.address = addr1;
hp.offset = get_reg(regs::edx);
return NewHook(hp, "SystemC");
}
else if (addr2 != 0 && addr1 == 0)
{
hp.address = addr2;
hp.offset = get_stack(1); // jichi: text in arg1
return NewHook(hp, "SystemC");
}
else
{
if (addr1 == addr2)
{
addr1 += 5;
}
hp.address = addr1;
hp.offset = get_reg(regs::edx);
auto succ = NewHook(hp, "SystemC");
hp.address = addr2;
hp.offset = get_stack(1);
succ |= NewHook(hp, "SystemC");
return succ;
2024-02-29 16:25:16 +08:00
}
}
2024-02-07 20:59:24 +08:00
2024-08-12 16:46:39 +08:00
/** jichi 10/2/2013: CHECKPOINT
*
* [5/31/2013] Hもお勉強も<EFBFBD><EFBFBD>
* base = 0xf20000
* + : /HSN-4@104A48:ANEBU.EXE
* - off: 4294967288 = 0xfffffff8 = -8
, - type: 1025 = 0x401
* + : /HSN-4@104FDD:ANEBU.EXE
* - off: 4294967288 = 0xfffffff8 = -8
* - type: 1089 = 0x441
*/
// bool InsertCandyHook3()
//{
// return false; // CHECKPOINT
// const BYTE ins[] = {
// 0x83,0xc4, 0x0c, // add esp,0xc ; hook here
// 0x0f,0xb6,0xc0, // movzx eax,al
// 0x85,0xc0, // test eax,eax
// 0x75, 0x0e // jnz XXOO ; it must be 0xe, or there will be duplication
// };
// enum { addr_offset = 0 };
// ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
// ULONG reladdr = SearchPattern(processStartAddress, range, ins, sizeof(ins));
// reladdr = 0x104a48;
// GROWL_DWORD(processStartAddress);
// //GROWL_DWORD3(reladdr, processStartAddress, range);
// if (!reladdr)
// return false;
//
// HookParam hp;
// hp.address = processStartAddress + reladdr + addr_offset;
// hp.offset=get_reg(regs::eax);
// hp.type = USING_STRING|NO_CONTEXT;
// NewHook(hp, "Candy");
// return true;
// }
2024-02-07 20:59:24 +08:00
} // unnamed Candy
2024-08-12 16:46:39 +08:00
namespace
2024-02-07 20:59:24 +08:00
{
2024-08-12 16:46:39 +08:00
bool candy3()
{
// お母さんは俺専用!~あなたの初めてを…母さんが貰ってア・ゲ・ル~
// 茉莉子さん家の性事情 ~伯母さんは僕のモノ~
const BYTE bytes[] = {
0x24, // XX||XX2
0x75};
for (auto addr : Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE))
{
ConsoleOutput("%x", addr);
if ((*(BYTE *)(addr - 1) == 0x3c) || ((*(BYTE *)(addr - 2) == 0x83) && (*(BYTE *)(addr - 1) == 0xf9)))
{
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)
continue;
ConsoleOutput("!%x", addr);
HookParam hp;
hp.type = USING_STRING;
if (*(BYTE *)addr == 0x55)
hp.offset = get_stack(1);
else if (*(BYTE *)addr == 0x56)
hp.offset = get_reg(regs::eax);
else
continue;
hp.address = addr;
return NewHook(hp, "candy3");
}
}
return false;
}
bool InsertCandyHook3()
{
2024-02-07 20:59:24 +08:00
/*
2024-08-12 16:46:39 +08:00
* Sample games:
* https://vndb.org/v24878
*/
const BYTE bytes[] = {
0xCC, // int 3
0x55, // push ebp << hook here
0x8B, 0xEC, // mov ebp,esp
0x6A, 0xFF, // push -01
0x68, XX4, // push iinari-omnibus.exe+C4366
0x64, 0xA1, 0x00, 0x00, 0x00, 0x00, // mov eax,fs:[00000000]
0x50, // push eax
0x83, 0xEC, 0x74, // sub esp,74
0x53, // push ebx
0x56, // push esi
0x57 // push edi
};
2024-02-07 20:59:24 +08:00
2024-08-12 16:46:39 +08:00
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr)
return false;
HookParam hp;
hp.address = addr + 1;
hp.offset = get_stack(4);
hp.type = USING_STRING | CODEC_UTF16;
ConsoleOutput("INSERT SystemC#3");
return NewHook(hp, "SystemC#3");
}
2024-02-07 20:59:24 +08:00
}
// jichi 10/2/2013: Add new candy hook
bool InsertCandyHook()
{
2024-08-12 16:46:39 +08:00
// if (0 == _wcsicmp(processName, L"systemc.exe"))
2024-02-07 20:59:24 +08:00
if (Util::CheckFile(L"SystemC.exe"))
2024-08-12 16:46:39 +08:00
return InsertCandyHook1() || candy3();
else
{
// return InsertCandyHook2();
2024-02-29 16:25:16 +08:00
bool b2 = InsertCandyHook2();
b2 |= InsertCandyHook3();
return b2;
2024-02-07 20:59:24 +08:00
}
}
2024-08-12 16:46:39 +08:00
namespace
{
bool willowsoft()
{
const BYTE bytes[] = {
// https://vndb.org/v5761
// まません
2024-02-07 20:59:24 +08:00
2024-08-12 16:46:39 +08:00
0xA1, XX4,
0x89, 0x45, 0xF8,
0x83, 0x7D, 0xF8, 0x10,
0x74, XX,
0x83, 0x7D, 0xF8, 0x18,
0x74, XX,
0x83, 0x7D, 0xF8, 0x20,
0x74, XX};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr == 0)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr, 0x20);
if (addr == 0)
return false;
HookParam hp;
hp.type = USING_STRING;
hp.offset = get_stack(2);
hp.type = USING_STRING;
hp.address = addr;
return NewHook(hp, "WillowSoft");
}
}
bool Candy::attach_function()
{
2024-02-07 20:59:24 +08:00
2024-08-12 16:46:39 +08:00
auto b1 = InsertCandyHook();
if (b1)
PcHooks::hookOtherPcFunctions();
else
{
b1 = b1 || willowsoft();
if (!b1)
PcHooks::hookOtherPcFunctions();
}
return b1;
}
2024-02-07 20:59:24 +08:00
2024-08-12 16:46:39 +08:00
bool WillowSoft::attach_function()
{
// お母さんがいっぱい!!限定ママBOX
2024-02-07 20:59:24 +08:00
const BYTE bytes[] = {
2024-08-12 16:46:39 +08:00
0xF7, 0xC2, 0x00, 0x00, 0xFF, 0x00,
2024-02-07 20:59:24 +08:00
XX2,
2024-08-12 16:46:39 +08:00
0xF7, 0xC2, 0x00, 0x00, 0x00, 0xFF,
XX2};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if (addr == 0)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)
return false;
2024-02-07 20:59:24 +08:00
HookParam hp;
hp.type = USING_STRING;
2024-08-12 16:46:39 +08:00
hp.offset = get_stack(2);
2024-02-07 20:59:24 +08:00
hp.type |= DATA_INDIRECT;
2024-08-12 16:46:39 +08:00
hp.index = 0;
2024-02-07 20:59:24 +08:00
hp.address = addr;
2024-08-12 16:46:39 +08:00
2024-02-07 20:59:24 +08:00
return NewHook(hp, "WillowSoft");
}