mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-11-27 15:54:03 +08:00
395 lines
15 KiB
C++
395 lines
15 KiB
C++
#include"HorkEye.h"
|
||
|
||
|
||
|
||
/** 10/20/2014 jichi: HorkEye, http://horkeye.com
|
||
* Sample game: [150226] 結城友奈<E58F8B>勀<EFBFBD><E58B80>ある 体験版
|
||
*
|
||
* No GDI functions are used by this game.
|
||
*
|
||
* Debug method:
|
||
* There are two matched texts.
|
||
* The one having fixed address is used to insert hw breakpoints.
|
||
*
|
||
* I found are two functions addressing the address, both of which seems to be good.
|
||
* The first one is used:
|
||
*
|
||
* 013cda60 8d4c24 1c lea ecx,dword ptr ss:[esp+0x1c]
|
||
* 013cda64 51 push ecx
|
||
* 013cda65 68 48a8c201 push .01c2a848 ; ascii "if"
|
||
* 013cda6a e8 d1291600 call .01530440
|
||
* 013cda6f 83c4 0c add esp,0xc
|
||
* 013cda72 6a 01 push 0x1
|
||
* 013cda74 83ec 1c sub esp,0x1c
|
||
* 013cda77 8bcc mov ecx,esp
|
||
* 013cda79 896424 30 mov dword ptr ss:[esp+0x30],esp
|
||
* 013cda7d 6a 10 push 0x10
|
||
* 013cda7f c741 14 0f000000 mov dword ptr ds:[ecx+0x14],0xf
|
||
* 013cda86 c741 10 00000000 mov dword ptr ds:[ecx+0x10],0x0
|
||
* 013cda8d 68 80125601 push .01561280
|
||
* 013cda92 c601 00 mov byte ptr ds:[ecx],0x0
|
||
* 013cda95 e8 5681ffff call .013c5bf0
|
||
* 013cda9a e8 717a0900 call .01465510
|
||
* 013cda9f 83c4 20 add esp,0x20
|
||
* 013cdaa2 b8 01000000 mov eax,0x1
|
||
* 013cdaa7 8b8c24 b8000000 mov ecx,dword ptr ss:[esp+0xb8]
|
||
* 013cdaae 5f pop edi
|
||
* 013cdaaf 5e pop esi
|
||
* 013cdab0 5d pop ebp
|
||
* 013cdab1 5b pop ebx
|
||
* 013cdab2 33cc xor ecx,esp
|
||
* 013cdab4 e8 c7361600 call .01531180
|
||
* 013cdab9 81c4 ac000000 add esp,0xac
|
||
* 013cdabf c3 retn
|
||
* 013cdac0 83ec 40 sub esp,0x40
|
||
* 013cdac3 a1 24805d01 mov eax,dword ptr ds:[0x15d8024]
|
||
* 013cdac8 8b15 c4709901 mov edx,dword ptr ds:[0x19970c4]
|
||
* 013cdace 8d0c00 lea ecx,dword ptr ds:[eax+eax]
|
||
* 013cdad1 a1 9c506b01 mov eax,dword ptr ds:[0x16b509c]
|
||
* 013cdad6 0305 18805d01 add eax,dword ptr ds:[0x15d8018]
|
||
* 013cdadc 53 push ebx
|
||
* 013cdadd 8b5c24 48 mov ebx,dword ptr ss:[esp+0x48]
|
||
* 013cdae1 55 push ebp
|
||
* 013cdae2 8b6c24 50 mov ebp,dword ptr ss:[esp+0x50]
|
||
* 013cdae6 894c24 34 mov dword ptr ss:[esp+0x34],ecx
|
||
* 013cdaea 8b0d 20805d01 mov ecx,dword ptr ds:[0x15d8020]
|
||
* 013cdaf0 894424 18 mov dword ptr ss:[esp+0x18],eax
|
||
* 013cdaf4 a1 1c805d01 mov eax,dword ptr ds:[0x15d801c]
|
||
* 013cdaf9 03c8 add ecx,eax
|
||
* 013cdafb 56 push esi
|
||
* 013cdafc 33f6 xor esi,esi
|
||
* 013cdafe d1f8 sar eax,1
|
||
* 013cdb00 45 inc ebp
|
||
* 013cdb01 896c24 24 mov dword ptr ss:[esp+0x24],ebp
|
||
* 013cdb05 897424 0c mov dword ptr ss:[esp+0xc],esi
|
||
* 013cdb09 894c24 18 mov dword ptr ss:[esp+0x18],ecx
|
||
* 013cdb0d 8a0c1a mov cl,byte ptr ds:[edx+ebx] jichi: here
|
||
* 013cdb10 894424 30 mov dword ptr ss:[esp+0x30],eax
|
||
* 013cdb14 8a441a 01 mov al,byte ptr ds:[edx+ebx+0x1]
|
||
* 013cdb18 57 push edi
|
||
* 013cdb19 897424 14 mov dword ptr ss:[esp+0x14],esi
|
||
* 013cdb1d 3935 c8709901 cmp dword ptr ds:[0x19970c8],esi
|
||
*
|
||
* The hooked place is only accessed once.
|
||
* 013cdb0d 8a0c1a mov cl,byte ptr ds:[edx+ebx] jichi: here
|
||
* ebx is the text to be base address.
|
||
* edx is the offset to skip character name.
|
||
*
|
||
* 023B66A0 81 79 89 C4 EA A3 2C 53 30 30 35 5F 42 5F 30 30 【夏偾,S005_B_00
|
||
* 023B66B0 30 32 81 7A 81 75 83 6F 81 5B 83 65 83 62 83 4E 02】「バーッ<E383BC>ク
|
||
* 023B66C0 83 58 82 CD 82 B1 82 C1 82 BF 82 CC 93 73 8D 87 スはこっちの都<E381AE> * 023B66D0 82 C8 82 C7 82 A8 8D 5C 82 A2 82 C8 82 B5 81 63 などお構いなし…
|
||
*
|
||
* There are garbage in character name.
|
||
*
|
||
* 1/15/2015
|
||
* Alternative hook that might not need a text filter:
|
||
* http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page753
|
||
* /HA-4@552B5:姉小路直子と銀色の死<E381AE>exe
|
||
* If this hook no longer works, try that one instead.
|
||
|
||
* Artikash 12/26/2018: Old HorkEye hook can't be found in shukusei no girlfriend https://vndb.org/v22880
|
||
* This function can be used instead. Hook code: /HS4@funcaddr
|
||
0022DD80 - 83 EC 44 - sub esp,44 { 68 }
|
||
0022DD83 - A1 3C704400 - mov eax,[0044703C] { [0000001C] }
|
||
0022DD88 - 8B 0D 34704400 - mov ecx,[00447034] { [00000014] }
|
||
0022DD8E - 03 C0 - add eax,eax
|
||
0022DD90 - 8B 54 24 48 - mov edx,[esp+48]
|
||
0022DD94 - 89 44 24 2C - mov [esp+2C],eax
|
||
0022DD98 - A1 C87E5500 - mov eax,[00557EC8] { [00000002] }
|
||
0022DD9D - 03 05 30704400 - add eax,[00447030] { [00000014] }
|
||
0022DDA3 - 89 44 24 18 - mov [esp+18],eax
|
||
0022DDA7 - A1 38704400 - mov eax,[00447038] { [00000008] }
|
||
0022DDAC - 03 C1 - add eax,ecx
|
||
0022DDAE - D1 F9 - sar ecx,1
|
||
0022DDB0 - 53 - push ebx
|
||
0022DDB1 - 55 - push ebp
|
||
0022DDB2 - 56 - push esi
|
||
0022DDB3 - 8B 74 24 58 - mov esi,[esp+58]
|
||
0022DDB7 - 33 DB - xor ebx,ebx
|
||
0022DDB9 - 89 4C 24 48 - mov [esp+48],ecx
|
||
0022DDBD - 46 - inc esi
|
||
0022DDBE - 8B 0D 5CA28300 - mov ecx,[0083A25C] { [00000000] }
|
||
0022DDC4 - 57 - push edi
|
||
0022DDC5 - 8B 3D 887E5500 - mov edi,[00557E88] { [00000040] }
|
||
0022DDCB - 89 74 24 2C - mov [esp+2C],esi
|
||
0022DDCF - 89 44 24 34 - mov [esp+34],eax
|
||
0022DDD3 - 89 5C 24 18 - mov [esp+18],ebx
|
||
0022DDD7 - 8A 24 11 - mov ah,[ecx+edx]
|
||
0022DDDA - 8A 44 11 01 - mov al,[ecx+edx+01]
|
||
0022DDDE - 89 7C 24 20 - mov [esp+20],edi
|
||
0022DDE2 - 39 1D 60A28300 - cmp [0083A260],ebx { [00000000] }
|
||
0022DDE8 - 0F85 DD000000 - jne 0022DECB
|
||
0022DDEE - 80 FC 5B - cmp ah,5B { 91 }
|
||
0022DDF1 - 0F85 9C000000 - jne 0022DE93
|
||
0022DDF7 - 8B C1 - mov eax,ecx
|
||
0022DDF9 - 3B C6 - cmp eax,esi
|
||
0022DDFB - 7D 10 - jnl 0022DE0D
|
||
0022DDFD - 0F1F 00 - nop [eax]
|
||
0022DE00 - 80 3C 10 5D - cmp byte ptr [eax+edx],5D { 93 }
|
||
0022DE04 - 74 79 - je 0022DE7F
|
||
0022DE06 - 40 - inc eax
|
||
0022DE07 - 3B 44 24 2C - cmp eax,[esp+2C]
|
||
0022DE0B - 7C F3 - jl 0022DE00
|
||
0022DE0D - A1 BC7E5500 - mov eax,[00557EBC] { [00000001] }
|
||
0022DE12 - 85 C0 - test eax,eax
|
||
0022DE14 - 0F84 A7000000 - je 0022DEC1
|
||
0022DE1A - BE 02000000 - mov esi,00000002 { 2 }
|
||
0022DE1F - 89 74 24 1C - mov [esp+1C],esi
|
||
0022DE23 - 89 35 68A28300 - mov [0083A268],esi { [00000000] }
|
||
0022DE29 - 83 F8 01 - cmp eax,01 { 1 }
|
||
0022DE2C - 0F85 A3000000 - jne 0022DED5
|
||
0022DE32 - 83 3D C07E5500 00 - cmp dword ptr [00557EC0],00 { 0 }
|
||
0022DE39 - 8B 2D 506D5500 - mov ebp,[00556D50] { [00000028] }
|
||
0022DE3F - 75 2D - jne 0022DE6E
|
||
0022DE41 - 8B C7 - mov eax,edi
|
||
0022DE43 - 8D 8D 50855100 - lea ecx,[ebp+00518550]
|
||
0022DE49 - C1 E0 0A - shl eax,0A { 10 }
|
||
0022DE4C - 03 C8 - add ecx,eax
|
||
0022DE4E - 66 A1 58704400 - mov ax,[00447058] { [00004081] }
|
||
0022DE54 - 83 C5 02 - add ebp,02 { 2 }
|
||
0022DE57 - 89 2D 506D5500 - mov [00556D50],ebp { [00000028] }
|
||
0022DE5D - 66 89 01 - mov [ecx],ax
|
||
0022DE60 - A0 5A704400 - mov al,[0044705A] { [0] }
|
||
0022DE65 - 88 41 02 - mov [ecx+02],al
|
||
0022DE68 - 8B 0D 5CA28300 - mov ecx,[0083A25C] { [00000000] }
|
||
...
|
||
*/
|
||
// Skip text between "," and "<22>, and remove [n]
|
||
// ex:【夏偾,S005_B_0002】「バーッ<E383BC>ク
|
||
static bool HorkEyeFilter(LPVOID data, size_t *size, HookParam *)
|
||
{
|
||
size_t len = *size;
|
||
char *str = reinterpret_cast<char *>(data),
|
||
*start,
|
||
*stop;
|
||
|
||
// Remove text between , and ]
|
||
// FIXME: This does not work well because of the ascii encoding
|
||
if ((start = (char *)::memchr(str, ',', len)) &&
|
||
(stop = cpp_strnstr(start, "\x81\x7a", len - (start - str))) &&
|
||
(len -= stop - start)) // = u'<27>.encode('sjis')
|
||
::memmove(start, stop, len - (start - str));
|
||
|
||
// Remove [n]
|
||
enum { skip_len = 3 }; // = length of "[n]"
|
||
while (len >= skip_len &&
|
||
(start = cpp_strnstr(str, "[n]", len)) &&
|
||
(len -= skip_len))
|
||
::memmove(start, start + skip_len, len - (start - str));
|
||
|
||
*size = len;
|
||
return true;
|
||
}
|
||
namespace{
|
||
template <typename strT>
|
||
strT ltrim(strT text)
|
||
{
|
||
strT lastText = nullptr;
|
||
while (*text && text != lastText) {
|
||
lastText = text;
|
||
if (text[0] == 0x20)
|
||
text++;
|
||
if ((UINT8)text[0] == 0x81 && (UINT8)text[1] == 0x40) // skip space \u3000 (0x8140 in sjis)
|
||
text += 2;
|
||
if (text[0] == '\\') {
|
||
text++;
|
||
while (::islower(text[0]) || text[0] == '@')
|
||
text++;
|
||
}
|
||
}
|
||
while ((signed char)text[0] > 0 && text[0] != '[') // skip all leading ascii characters except "[" needed for ruby
|
||
text++;
|
||
return text;
|
||
}
|
||
template<int offset=1>
|
||
bool hookBefore(hook_stack*s,void* data, size_t* len1,uintptr_t*role){
|
||
auto str=(LPSTR)(s->stack[offset]);//stack-2:eax
|
||
int len=strlen(str);//s->ecx;
|
||
char *stop;
|
||
if ((stop = cpp_strnstr(str, "\x81\x7a", len )) &&
|
||
(len -= (stop - str+2))){
|
||
str=stop+2;
|
||
} // = u'<27>.encode('sjis')
|
||
auto old=std::string(str,len);
|
||
strcpy((char*)data,old.c_str());*len1=old.size();
|
||
return true;
|
||
|
||
}
|
||
template<int offset=1>
|
||
void hookafter(hook_stack*s,void* data, size_t len1){
|
||
|
||
auto newData =std::string((char*)data,len1);
|
||
auto str=(LPSTR)(s->stack[offset]);//stack-2:eax
|
||
int len=strlen(str);//s->ecx;
|
||
int lensave=len;
|
||
char *stop;
|
||
if ( (stop = cpp_strnstr(str, "\x81\x7a", len )) &&
|
||
(len -= (stop - str+2))){
|
||
auto old=std::string(str,stop+2-str);
|
||
newData=old+newData;
|
||
}
|
||
for(int i=0;i<lensave-newData.size();i++)newData.push_back(' ');
|
||
memcpy((void*)str,newData.c_str(),lensave);
|
||
// s->ecx=newData.size(); 修改ecx没用
|
||
}
|
||
}
|
||
bool InsertHorkEyeHook()
|
||
{
|
||
const BYTE bytes[] = {
|
||
0x89,0x6c,0x24, 0x24, // 013cdb01 896c24 24 mov dword ptr ss:[esp+0x24],ebp
|
||
0x89,0x74,0x24, 0x0c, // 013cdb05 897424 0c mov dword ptr ss:[esp+0xc],esi
|
||
0x89,0x4c,0x24, 0x18, // 013cdb09 894c24 18 mov dword ptr ss:[esp+0x18],ecx
|
||
0x8a,0x0c,0x1a // 013cdb0d 8a0c1a mov cl,byte ptr ds:[edx+ebx] jichi: here
|
||
};
|
||
enum { addr_offset = sizeof(bytes) - 3 }; // 8a0c1a
|
||
;
|
||
if (ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress)) {
|
||
HookParam hp;
|
||
hp.address = addr + addr_offset;
|
||
hp.offset=get_reg(regs::ebx);
|
||
hp.type = USING_STRING| NO_CONTEXT|FIXING_SPLIT|EMBED_ABLE|EMBED_DYNA_SJIS;
|
||
hp.hook_before=hookBefore<-4-1>;
|
||
hp.hook_after=hookafter<-4-1>;
|
||
hp.filter_fun = HorkEyeFilter;
|
||
hp.newlineseperator=L"[n]";
|
||
ConsoleOutput("INSERT HorkEye");
|
||
|
||
return NewHook(hp, "HorkEye");
|
||
}
|
||
|
||
memcpy(spDefault.pattern, Array<BYTE>{ 0xcc, 0xcc, 0xcc, XX, 0xec }, spDefault.length = 5);
|
||
spDefault.offset = 3;
|
||
|
||
const BYTE bytes2[] =
|
||
{
|
||
0x83, 0xec, XX, // sub esp,??
|
||
0xa1, XX4, // mov eax,??
|
||
0x8b, 0x0d, XX4, // mov ecx,??
|
||
0x03, 0xc0 // add eax,eax
|
||
};
|
||
|
||
for (auto addr : Util::SearchMemory(bytes2, sizeof(bytes2),PAGE_EXECUTE_READWRITE,processStartAddress, processStopAddress))
|
||
{
|
||
HookParam hp;
|
||
hp.address = addr;
|
||
hp.offset=get_stack(1);
|
||
hp.type = USING_STRING| EMBED_ABLE|EMBED_DYNA_SJIS;
|
||
hp.hook_before=hookBefore<1>;
|
||
hp.hook_after=hookafter<1>;
|
||
|
||
return NewHook(hp, "HorkEye2");
|
||
}
|
||
|
||
ConsoleOutput("HorkEye: pattern not found");
|
||
return false;
|
||
|
||
}
|
||
|
||
bool InsertHorkEye3Hook()
|
||
{
|
||
const BYTE bytes2[] =
|
||
{
|
||
0x55,
|
||
0x8d,0xac,0x24,XX4,
|
||
0x81,0xec,XX4,
|
||
0x6a,0xff,
|
||
0x68,XX4,
|
||
0x64,0xa1,0x00,0x00,0x00,0x00,
|
||
0x50,
|
||
0x83,0xec,0x38, //必须是0x38,不能是XX,否则有重的。
|
||
|
||
//.text:0042E7F0 55 push ebp
|
||
//.text : 0042E7F1 8D AC 24 24 FF FF FF lea ebp,[esp - 0DCh]
|
||
//.text : 0042E7F8 81 EC DC 00 00 00 sub esp, 0DCh
|
||
//.text : 0042E7FE 6A FF push 0FFFFFFFFh
|
||
//.text : 0042E800 68 51 1E 5C 00 push offset SEH_42E7F0
|
||
//.text : 0042E805 64 A1 00 00 00 00 mov eax, large fs : 0
|
||
//.text : 0042E80B 50 push eax
|
||
//.text : 0042E80C 83 EC 38 sub esp, 38h
|
||
//.text : 0042E80F A1 24 D0 64 00 mov eax, ___security_cookie
|
||
//.text : 0042E814 33 C5 xor eax, ebp
|
||
//.text : 0042E816 89 85 D8 00 00 00 mov[ebp + 0DCh + var_4], eax
|
||
};
|
||
|
||
auto addr=MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStopAddress);
|
||
if (addr == 0)return false;
|
||
HookParam hp;
|
||
hp.address = addr;
|
||
hp.offset=get_stack(1);
|
||
hp.type = USING_STRING| EMBED_ABLE|EMBED_DYNA_SJIS;
|
||
hp.hook_before=hookBefore<1>;
|
||
hp.hook_after=hookafter<1>;
|
||
|
||
return NewHook(hp, "HorkEye3");
|
||
|
||
}
|
||
|
||
bool InsertHorkEye4Hook()
|
||
{
|
||
//辻堂さんのバージンロード
|
||
//辻堂さんの純愛ロード
|
||
const BYTE bytes2[] =
|
||
{
|
||
0xf7,0xd8,
|
||
0x1b,0xc0,
|
||
0x83,0xc0,0x02
|
||
};
|
||
auto addr = MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStopAddress);
|
||
if (addr == 0)return false;
|
||
const BYTE bytebetter[] = {
|
||
0x8b,XX,XX,XX,
|
||
0xa1,XX4,
|
||
0x83,0xc4,XX,
|
||
0x8b,XX
|
||
};
|
||
auto addr1 = MemDbg::findBytes(bytebetter, sizeof(bytebetter), addr - 0x100, addr);
|
||
if (addr1)
|
||
addr = addr1;
|
||
else
|
||
addr = MemDbg::findEnclosingAlignedFunction(addr);
|
||
if (addr == 0)return false;
|
||
HookParam hp;
|
||
hp.address = addr;
|
||
hp.offset=get_reg(regs::eax);
|
||
hp.type = USING_STRING| NO_CONTEXT|EMBED_ABLE|EMBED_DYNA_SJIS;
|
||
hp.hook_before=hookBefore<-1-1>;
|
||
hp.hook_after=hookafter<-1-1>;
|
||
|
||
return NewHook(hp, "HorkEye4");
|
||
|
||
}
|
||
|
||
bool InsertHorkEye6Hook()
|
||
{
|
||
//みなとカーニバルFD
|
||
|
||
const BYTE bytes2[] =
|
||
{
|
||
0x83,0xc2,0x6c,
|
||
0x52,
|
||
0xe8
|
||
};
|
||
auto addr = MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStopAddress);
|
||
if (addr == 0)return false;
|
||
ConsoleOutput("hk6 %p", addr);
|
||
const BYTE start[] = { 0x6A ,0xFF };
|
||
addr = reverseFindBytes(start, sizeof(start), addr - 0x1000, addr);
|
||
if (addr == 0)return false;
|
||
ConsoleOutput("hk6 %p", addr);
|
||
HookParam hp;
|
||
hp.address = addr;
|
||
hp.offset=get_stack(3);
|
||
hp.type = CODEC_ANSI_BE ;
|
||
ConsoleOutput("INSERT HorkEye6 %p", addr);
|
||
|
||
return NewHook(hp, "HorkEye6");
|
||
|
||
}
|
||
|
||
bool HorkEye::attach_function() {
|
||
bool b1=InsertHorkEyeHook();
|
||
bool b2=InsertHorkEye3Hook();
|
||
bool b3=InsertHorkEye4Hook();
|
||
bool b4=InsertHorkEye6Hook();
|
||
return b1||b2||b3||b4;
|
||
}
|