LunaHook-mirror/LunaHook/engine32/Pal.cpp
2024-02-07 20:59:24 +08:00

272 lines
10 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"Pal.h"
#include"embed_util.h"
/** jichi 6/1/2014 AMUSE CRAFT
* Related brands: http://erogetrailers.com/brand/2047
* Sample game: 魔女こいにっ<E381AB> * See: http://sakuradite.com/topic/223
* Sample H-code: /HBN-4*0:18@26159:MAJOKOI_try.exe (need remove context, though)
*
* Sample games:
* - 時計仕掛け<E68E9B>レイライン
* - きみと僕との騎士の日<E381AE> *
* /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();
}