2024-02-07 20:59:24 +08:00

395 lines
15 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"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;
}