LunaHook-mirror/LunaHook/engine32/Pal.cpp

272 lines
10 KiB
C++
Raw Normal View History

2024-02-07 20:59:24 +08:00
#include"Pal.h"
#include"embed_util.h"
/** jichi 6/1/2014 AMUSE CRAFT
* Related brands: http://erogetrailers.com/brand/2047
* Sample game: <EFBFBD> * See: http://sakuradite.com/topic/223
* Sample H-code: /HBN-4*0:18@26159:MAJOKOI_try.exe (need remove context, though)
*
* Sample games:
* - <EFBFBD>
* - <EFBFBD> *
* /HBN-4*0:18@26159:MAJOKOI_TRY.EXE
* - addr: 155993
* - length_offset: 1
* - module: 104464j455
* - off: 4294967288 = 0xfffffff8
* - split: 24 = 0x18
* - type: 1112 = 0x458
*
* Call graph:
* - hook reladdr: 0x26159, fun reladdr: 26150
* - scene fun reladdr: 0x26fd0
* - arg1 and arg3 are pointers
* - arg2 is the text
* - scenairo only reladdr: 0x26670
* Issue for implementing embeded engine: two functions are needed to be hijacked
*
* 013c614e cc int3
* 013c614f cc int3
* 013c6150 /$ 55 push ebp ; jichi: function starts, this function seems to process text encoding
* 013c6151 |. 8bec mov ebp,esp
* 013c6153 |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 013c6156 |. 0fb608 movzx ecx,byte ptr ds:[eax]
* 013c6159 |. 81f9 81000000 cmp ecx,0x81 ; jichi: hook here
* 013c615f |. 7c 0d jl short majokoi_.013c616e
* 013c6161 |. 8b55 08 mov edx,dword ptr ss:[ebp+0x8]
* 013c6164 |. 0fb602 movzx eax,byte ptr ds:[edx]
* 013c6167 |. 3d 9f000000 cmp eax,0x9f
* 013c616c |. 7e 1c jle short majokoi_.013c618a
* 013c616e |> 8b4d 08 mov ecx,dword ptr ss:[ebp+0x8]
* 013c6171 |. 0fb611 movzx edx,byte ptr ds:[ecx]
* 013c6174 |. 81fa e0000000 cmp edx,0xe0
* 013c617a |. 7c 30 jl short majokoi_.013c61ac
* 013c617c |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 013c617f |. 0fb608 movzx ecx,byte ptr ds:[eax]
* 013c6182 |. 81f9 fc000000 cmp ecx,0xfc
* 013c6188 |. 7f 22 jg short majokoi_.013c61ac
* 013c618a |> 8b55 08 mov edx,dword ptr ss:[ebp+0x8]
* 013c618d |. 0fb642 01 movzx eax,byte ptr ds:[edx+0x1]
* 013c6191 |. 83f8 40 cmp eax,0x40
* 013c6194 |. 7c 16 jl short majokoi_.013c61ac
* 013c6196 |. 8b4d 08 mov ecx,dword ptr ss:[ebp+0x8]
* 013c6199 |. 0fb651 01 movzx edx,byte ptr ds:[ecx+0x1]
* 013c619d |. 81fa fc000000 cmp edx,0xfc
* 013c61a3 |. 7f 07 jg short majokoi_.013c61ac
* 013c61a5 |. b8 01000000 mov eax,0x1
* 013c61aa |. eb 02 jmp short majokoi_.013c61ae
* 013c61ac |> 33c0 xor eax,eax
* 013c61ae |> 5d pop ebp
* 013c61af \. c3 retn
*/
static bool InsertOldPalHook() // this is used in case the new pattern does not work
{
const BYTE bytes[] = {
0x55, // 013c6150 /$ 55 push ebp ; jichi: function starts
0x8b,0xec, // 013c6151 |. 8bec mov ebp,esp
0x8b,0x45, 0x08, // 013c6153 |. 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
0x0f,0xb6,0x08, // 013c6156 |. 0fb608 movzx ecx,byte ptr ds:[eax]
0x81,0xf9 //81000000 // 013c6159 |. 81f9 81000000 cmp ecx,0x81 ; jichi: hook here
};
enum { addr_offset = sizeof(bytes) - 2 };
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL_DWORD(reladdr); // supposed to be 0x21650
//GROWL_DWORD(reladdr + addr_offset);
//reladdr = 0x26159; // 魔女こいにっ<E381AB>trial
if (!addr) {
ConsoleOutput("AMUSE CRAFT: pattern not found");
return false;
}
HookParam hp;
hp.address = addr + addr_offset;
//hp.type = NO_CONTEXT|USING_SPLIT|DATA_INDIRECT; // 0x418
//hp.type = NO_CONTEXT|USING_SPLIT|DATA_INDIRECT|RELATIVE_SPLIT; // Use relative address to prevent floating issue
hp.type = NO_CONTEXT|USING_SPLIT|DATA_INDIRECT;
hp.offset=get_reg(regs::eax); // eax
ConsoleOutput("INSERT AMUSE CRAFT");
return NewHook(hp, "Pal");
}
namespace{
template <typename strT>
strT trim(strT text, int *size)
{
//int length = ::strlen(text);
auto length = *size;
if (text[0] == '<' && text[1] == 'c') {
auto p = ::strchr(text + 2, '>');
if (!p)
return text;
p++;
length -= p - text;
text = p; // skip leading '<c .. >'
}
if (text[length - 1] == '>' && text[length - 2] == 'c' && text[length - 3] == '/' && text[length - 4] == '<')
length -= 4; // skip the trailing </c>'
*size = length;
return text;
}
LPSTR trimmedText;int trimmedSize;
bool before(hook_stack*s,void* data, size_t* len,uintptr_t*role){
auto text = (LPSTR)s->stack[2]; // text in arg2
if (!text || !*text)
return false;
int size = ::strlen(text);
trimmedSize = size;
trimmedText = trim(text, &trimmedSize);
if (trimmedSize <= 0 || !trimmedText || !*trimmedText)
return false;
auto retaddr = s->stack[0];
if (*(WORD *)(retaddr - 8) == 0x088b) // 8b08 mov ecx,dword ptr ds:[eax]
*role = s->stack[3] ? Engine::ScenarioRole : Engine::NameRole;
std::string oldData(trimmedText, trimmedSize);
strcpy((char*)data,oldData.c_str());
*len=oldData.size();
return true;
}
void after(hook_stack*s,void* data, size_t len){
std::string newData((char*)data, len);
auto text = (LPSTR)s->stack[2]; // text in arg2
int prefixSize = trimmedText - text;
int size = ::strlen(text);
int suffixSize = size - prefixSize - trimmedSize;
//if (prefixSize)
// newData.prepend(text, prefixSize);
if (suffixSize)
newData.append(trimmedText + trimmedSize, suffixSize);
::strcpy(trimmedText, newData.c_str());
}
std::string rubyRemove( std::string text) {
std::regex rx("<r(.*?)>(.*?)</r>");
text= std::regex_replace(text, rx, "$2");
std::regex rx2("<c(.*?)>(.*?)</c>");
text= std::regex_replace(text, rx2, "$2");
std::regex rx3("<s(.*?)>(.*?)</s>");
text= std::regex_replace(text, rx3, "$2");
return text;
}
}
static bool InsertNewPal1Hook()
{
//有乱码,无法处理。并且遇到某些中文字符会闪退
const BYTE bytes[] = {
0x55, // 002c6ab0 55 push ebp
0x8b,0xec, // 002c6ab1 8bec mov ebp,esp
0x83,0xec, 0x78, // 002c6ab3 83ec 78 sub esp,0x78
0xa1, XX4, // 002c6ab6 a1 8c002f00 mov eax,dword ptr ds:[0x2f008c]
0x33,0xc5, // 002c6abb 33c5 xor eax,ebp
0x89,0x45, 0xf8 // 002c6abd 8945 f8 mov dword ptr ss:[ebp-0x8],eax ; mireado : small update
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Pal1: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_stack(2); // arg2
hp.type = USING_STRING|EMBED_ABLE;
hp.hook_before=before;
hp.hook_after=after;
hp.filter_fun=[](void* data, size_t* len, HookParam* hp){
auto s=std::string((char*)data,*len);
s=rubyRemove(s);
strcpy((LPSTR)data,s.c_str());*len=s.size();
return true;
};
hp.hook_font=F_CreateFontIndirectA|F_CreateFontA;
ConsoleOutput("INSERT Pal1");
return NewHook(hp, "Pal");
}
// Eguni 2016/11/06
// Supporting new Pal engine, tested with 恋×シンアイ彼女
static bool InsertNewPal2Hook()
{
const BYTE bytes[] = {
0x55, // 0124E220 55 push ebp; doesn't works... why?
0x8b,0xec, // 0124E221 8bec mov ebp,esp
0x83,0xec, 0x7c, // 0124E223 83ec 7c sub esp,0x7C
0xa1, XX4, // 0124E226 a1 788D2901 mov eax,dword ptr ds:[0x2f008c]
0x33,0xc5, // 0124E22B 33c5 xor eax,ebp
0x89,0x45, 0xfc, // 0124E22D 8945 FC mov dword ptr ss:[ebp-0x8],eax ; mireado : small update
0xe8 // 0136e230 e8 call 01377800
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("Pal2: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_stack(2); // arg2
hp.type = USING_STRING;
ConsoleOutput("INSERT Pal2");
return NewHook(hp, "Pal");
}
namespace{
bool redcheris(){
const BYTE bytes[] = {
//int __usercall sub_44E1E0@<eax>(
// char *a1@<edx>,
//if ( *(_DWORD *)a1 == 1047683644 )
0x8B,0x06,
0x3D,0x3C,0x62,0x72,0x3E ,
0x75,0x10
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) return false;
addr=MemDbg::findEnclosingAlignedFunction(addr);
if (!addr) return false;
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::edx);
hp.type = USING_STRING|EMBED_ABLE|EMBED_BEFORE_SIMPLE|EMBED_AFTER_NEW;
//无法编码的字符无法显示若开启dyna则会直接略过这个字还不如不开。
//[230929] [ユニゾンシフト] 恋とHしかしていない
hp.newlineseperator=L"<br>";
hp.filter_fun=[](void* data, size_t* len, HookParam* hp){
auto s=std::string((char*)data,*len);
s=rubyRemove(s);
strcpy((LPSTR)data,s.c_str());*len=s.size();
return true;
};
return NewHook(hp, "Pal");
}
}
bool InsertPalHook() // use Old Pal first, which does not have ruby
{
PcHooks::hookOtherPcFunctions();
auto succ=false;
for (auto func : { "PalSpriteCreateTextEx","PalSpriteCreateText","PalFontDrawText" }) {
HookParam hp;
hp.type = USING_STRING | MODULE_OFFSET | FUNCTION_OFFSET;
wcscpy_s(hp.module, L"Pal.dll");
strcpy_s(hp.function, func);
hp.offset=get_stack(2);
succ|=NewHook(hp, func);
}
bool embed= InsertNewPal1Hook() ;
bool b1= InsertOldPalHook() || InsertNewPal2Hook();
bool b2=redcheris();
return b1||b2||embed||succ;
}
bool Pal::attach_function() {
return InsertPalHook();
}