1626 lines
66 KiB
C++
Raw Normal View History

2024-02-07 20:59:24 +08:00
#include"KiriKiri.h"
/********************************************************************************************
KiriKiri hook:
Usually there are xp3 files in the game folder but also exceptions.
Find TVP(KIRIKIRI) in the version description is a much more precise way.
KiriKiri1 correspond to AGTH KiriKiri hook, but this doesn't always work well.
Find call to GetGlyphOutlineW and go to function header. EAX will point to a
structure contains character (at 0x14, [EAX+0x14]) we want. To split names into
different threads AGTH uses [EAX], seems that this value stands for font size.
Since KiriKiri is compiled by BCC and BCC fastcall uses EAX to pass the first
parameter. Here we choose EAX is reasonable.
KiriKiri2 is a redundant hook to catch text when 1 doesn't work. When this happens,
usually there is a single GetTextExtentPoint32W contains irregular repetitions which
is out of the scope of KS or KF. This time we find a point and split them into clean
text threads. First find call to GetTextExtentPoint32W and step out of this function.
Usually there is a small loop. It is this small loop messed up the text. We can find
one ADD EBX,2 in this loop. It's clear that EBX is a string pointer goes through the
string. After the loop EBX will point to the end of the string. So EBX-2 is the last
char and we insert hook here to extract it.
********************************************************************************************/
#if 0 // jichi 11/12/2013: not used
static void SpecialHookKiriKiri(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t*len)
{
DWORD p1 = *(DWORD *)(esp_base - 0x14),
p2 = *(DWORD *)(esp_base - 0x18);
if ((p1>>16) == (p2>>16)) {
if (DWORD p3 = *(DWORD *)p1) {
p3 += 8;
for (p2 = p3 + 2; *(WORD *)p2; p2 += 2);
*len = p2 - p3;
*data = p3;
p1 = *(DWORD *)(esp_base - 0x20);
p1 = *(DWORD *)(p1 + 0x74);
*split = p1 | *(DWORD *)(esp_base + 0x48);
} else
*len = 0;
} else
*len=0;
}
#endif // 0
bool FindKiriKiriHook(DWORD fun, DWORD size, DWORD pt, DWORD flag) // jichi 10/20/2014: change return value to bool
{
enum : DWORD {
// jichi 10/20/2014: mov ebp,esp, sub esp,*
kirikiri1_sig = 0xec8b55,
// jichi 10/20/2014:
// 00e01542 53 push ebx
// 00e01543 56 push esi
// 00e01544 57 push edi
kirikiri2_sig = 0x575653
};
enum : DWORD { StartAddress = 0x1000 };
enum : DWORD { StartRange = 0x6000, StopRange = 0x8000 }; // jichi 10/20/2014: ITH original pattern range
// jichi 10/20/2014: The KiriKiri patterns exist in multiple places of the game.
//enum : DWORD { StartRange = 0x8000, StopRange = 0x9000 }; // jichi 10/20/2014: change to a different range
//WCHAR str[0x40];
DWORD sig = flag ? kirikiri2_sig : kirikiri1_sig;
DWORD t = 0;
for (DWORD i = StartAddress; i < size - 4; i++)
if (*(WORD *)(pt + i) == 0x15ff) { // jichi 10/20/2014: call dword ptr ds
DWORD addr = *(DWORD *)(pt + i + 2);
// jichi 10/20/2014: There are multiple function calls. The flag+1 one is selected.
// i.e. KiriKiri1: The first call to GetGlyphOutlineW is selected
// KiriKiri2: The second call to GetTextExtentPoint32W is selected
if (addr >= pt && addr <= pt + size - 4
&& *(DWORD *)addr == fun)
t++;
if (t == flag + 1) // We find call to GetGlyphOutlineW or GetTextExtentPoint32W.
//swprintf(str, L"CALL addr:0x%.8X",i+pt);
//ConsoleOutput(str);
for (DWORD j = i; j > i - StartAddress; j--)
if (((*(DWORD *)(pt + j)) & 0xffffff) == sig) {
if (flag) { // We find the function entry. flag indicate 2 hooks.
t = 0; // KiriKiri2, we need to find call to this function.
for (DWORD k = j + StartRange; k < j + StopRange; k++) // Empirical range.
if (*(BYTE *)(pt + k) == 0xe8) {
if (k + 5 + *(DWORD *)(pt + k + 1) == j)
t++;
if (t == 2) {
//for (k+=pt+0x14; *(WORD*)(k)!=0xC483;k++);
//swprintf(str, L"Hook addr: 0x%.8X",pt+k);
//ConsoleOutput(str);
HookParam hp;
hp.address = pt + k + 0x14;
hp.offset=get_reg(regs::ebx);
hp.index = -0x2;
hp.split = get_reg(regs::ecx);
hp.type = CODEC_UTF16|NO_CONTEXT|USING_SPLIT|DATA_INDIRECT;
ConsoleOutput("INSERT KiriKiri2");
return NewHook(hp, "KiriKiri2");
}
}
} else {
//swprintf(str, L"Hook addr: 0x%.8X",pt+j);
//ConsoleOutput(str);
HookParam hp;
hp.address = (DWORD)pt + j;
hp.offset=get_reg(regs::eax);
hp.index = 0x14;
hp.split = get_reg(regs::eax);
hp.type = CODEC_UTF16|DATA_INDIRECT|USING_SPLIT|SPLIT_INDIRECT;
ConsoleOutput("INSERT KiriKiri1");
return NewHook(hp, "KiriKiri1");
}
return false;
}
//ConsoleOutput("KiriKiri: FAILED to find function entry");
}
if (flag)
ConsoleOutput("KiriKiri2: failed");
else
ConsoleOutput("KiriKiri1: failed");
return false;
}
bool InsertKiriKiriHook() // 9/20/2014 jichi: change return type to bool
{
bool k1 = FindKiriKiriHook((DWORD)GetGlyphOutlineW, processStopAddress - processStartAddress, processStartAddress, 0), // KiriKiri1
k2 = FindKiriKiriHook((DWORD)GetTextExtentPoint32W, processStopAddress - processStartAddress, processStartAddress, 1); // KiriKiri2
//RegisterEngineType(ENGINE_KIRIKIRI);
if (k1 && k2) {
ConsoleOutput("KiriKiri1: disable GDI hooks");
}
return k1 || k2;
}
/** 10/20/2014 jichi: KAGParser
* Sample game: [141128] Venus Blood -HYPNO- <EFBFBD>
*
* drawText and drawGlyph seem to be the right function to look at.
* However, the latest source code does not match VenusBlood.
*
* Debug method:
* Pre-compute: hexstr <EFBFBD>utf16, got: 96894c756e304d304b306a304430
* Use ollydbg to insert hardware break point before the scene is entered.
* It found several places either in game or KAGParser, and the last one is as follows.
* It tries to find "[" (0x5b) in the memory.
*
* 1. It cannot find character name.
* 2. It will extract [r].
*
* 6e562270 75 0a jnz short kagparse.6e56227c
* 6e562272 c705 00000000 00>mov dword ptr ds:[0],0x0
* 6e56227c ffb424 24010000 push dword ptr ss:[esp+0x124]
* 6e562283 ff9424 24010000 call dword ptr ss:[esp+0x124]
* 6e56228a 8b8c24 20010000 mov ecx,dword ptr ss:[esp+0x120]
* 6e562291 890d 14ed576e mov dword ptr ds:[0x6e57ed14],ecx
* 6e562297 68 3090576e push kagparse.6e579030 ; unicode "[r]"
* 6e56229c 8d46 74 lea eax,dword ptr ds:[esi+0x74]
* 6e56229f 50 push eax
* 6e5622a0 ffd1 call ecx
* 6e5622a2 8b4e 50 mov ecx,dword ptr ds:[esi+0x50]
* 6e5622a5 8b46 54 mov eax,dword ptr ds:[esi+0x54]
* 6e5622a8 66:833c48 5b cmp word ptr ds:[eax+ecx*2],0x5b ; jichi: hook here
* 6e5622ad 75 06 jnz short kagparse.6e5622b5
* 6e5622af 8d41 01 lea eax,dword ptr ds:[ecx+0x1]
* 6e5622b2 8946 50 mov dword ptr ds:[esi+0x50],eax
* 6e5622b5 ff46 50 inc dword ptr ds:[esi+0x50]
* 6e5622b8 ^e9 aebcffff jmp kagparse.6e55df6b
* 6e5622bd 8d8c24 88030000 lea ecx,dword ptr ss:[esp+0x388]
* 6e5622c4 e8 b707ffff call kagparse.6e552a80
* 6e5622c9 84c0 test al,al
* 6e5622cb 75 0f jnz short kagparse.6e5622dc
* 6e5622cd 8d8424 88030000 lea eax,dword ptr ss:[esp+0x388]
* 6e5622d4 50 push eax
* 6e5622d5 8bce mov ecx,esi
* 6e5622d7 e8 149bffff call kagparse.6e55bdf0
* 6e5622dc 8d8c24 80030000 lea ecx,dword ptr ss:[esp+0x380]
* 6e5622e3 e8 9807ffff call kagparse.6e552a80
* 6e5622e8 84c0 test al,al
* 6e5622ea 75 0f jnz short kagparse.6e5622fb
* 6e5622ec 8d8424 80030000 lea eax,dword ptr ss:[esp+0x380]
* 6e5622f3 50 push eax
* 6e5622f4 8bce mov ecx,esi
* 6e5622f6 e8 35a0ffff call kagparse.6e55c330
* 6e5622fb 8d8c24 c0030000 lea ecx,dword ptr ss:[esp+0x3c0]
* 6e562302 c68424 c0040000 >mov byte ptr ss:[esp+0x4c0],0x3c
* 6e56230a e8 81edfeff call kagparse.6e551090
* 6e56230f 8d8c24 80030000 lea ecx,dword ptr ss:[esp+0x380]
* 6e562316 c68424 c0040000 >mov byte ptr ss:[esp+0x4c0],0x3b
* 6e56231e e8 8deefeff call kagparse.6e5511b0
* 6e562323 8d8c24 88030000 lea ecx,dword ptr ss:[esp+0x388]
* 6e56232a e9 d7000000 jmp kagparse.6e562406
* 6e56232f 66:837c24 20 00 cmp word ptr ss:[esp+0x20],0x0
* 6e562335 75 10 jnz short kagparse.6e562347
* 6e562337 ff46 4c inc dword ptr ds:[esi+0x4c]
* 6e56233a c746 50 00000000 mov dword ptr ds:[esi+0x50],0x0
* 6e562341 c646 5c 00 mov byte ptr ds:[esi+0x5c],0x0
*
* Runtime regisers:
* EAX 09C1A626 text address
* ECX 00000000 0 or other offset
* EDX 025F1368 this value seems does not change. it is always pointed to 0
* EBX 0000300C
* ESP 0029EB7C
* EBP 0029F044
* ESI 04EE4150
* EDI 0029F020
*
* <EFBFBD>KAGParserEx.dll
* 10013948 68 14830210 push _3.10028314 ; UNICODE "[r]"
* 1001394d 83c2 7c add edx,0x7c
* 10013950 52 push edx
* 10013951 ffd0 call eax
* 10013953 8b75 08 mov esi,dword ptr ss:[ebp+0x8]
* 10013956 eb 02 jmp short _3.1001395a
* 10013958 8bf2 mov esi,edx
* 1001395a 8b46 58 mov eax,dword ptr ds:[esi+0x58]
* 1001395d 8b4e 5c mov ecx,dword ptr ds:[esi+0x5c]
* 10013960 66:833c41 5b cmp word ptr ds:[ecx+eax*2],0x5b ; jichi: hook here
* 10013965 75 06 jnz short _3.1001396d
* 10013967 83c0 01 add eax,0x1
* 1001396a 8946 58 mov dword ptr ds:[esi+0x58],eax
* 1001396d 8346 58 01 add dword ptr ds:[esi+0x58],0x1
* 10013971 807e 7a 00 cmp byte ptr ds:[esi+0x7a],0x0
* 10013975 ^0f85 b5a7ffff jnz _3.1000e130
* 1001397b 8b45 08 mov eax,dword ptr ss:[ebp+0x8]
* 1001397e 83b8 90000000 ff cmp dword ptr ds:[eax+0x90],-0x1
* 10013985 0f84 68040000 je _3.10013df3
* 1001398b 8bd8 mov ebx,eax
* 1001398d ^e9 a1a7ffff jmp _3.1000e133
* 10013992 8d7c24 78 lea edi,dword ptr ss:[esp+0x78]
* 10013996 8d7424 54 lea esi,dword ptr ss:[esp+0x54]
* 1001399a e8 e16fffff call _3.1000a980
*/
#if 0 // not used, as KiriKiriZ is sufficient, and most KiriKiriZ games use KAGParserEx instead of KAGParser.
namespace { // unnamed
bool KAGParserFilter(LPVOID data, size_t *size, HookParam *)
{
StringFilter(reinterpret_cast<LPWSTR>(data), reinterpret_cast<size_t *>(size), L"[r]", 3);
return true;
}
void SpecialHookKAGParser(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t*len)
{
// 6e5622a8 66:833c48 5b cmp word ptr ds:[eax+ecx*2],0x5b
DWORD eax = regof(eax, esp_base),
ecx = regof(ecx, esp_base);
if (eax && !ecx) { // skip string when ecx is not zero
*data = eax;
*len = ::wcslen((LPCWSTR)eax) * 2; // 2 == sizeof(wchar_t)
*split = FIXED_SPLIT_VALUE; // merge all threads
}
}
void SpecialHookKAGParserEx(hook_stack* stack, HookParam *, uintptr_t *data, uintptr_t *split, size_t*len)
{
// 10013960 66:833c41 5b cmp word ptr ds:[ecx+eax*2],0x5b
DWORD eax = regof(eax, esp_base),
ecx = regof(ecx, esp_base);
if (ecx && !eax) { // skip string when ecx is not zero
*data = ecx;
*len = ::wcslen((LPCWSTR)ecx) * 2; // 2 == sizeof(wchar_t)
*split = FIXED_SPLIT_VALUE; // merge all threads
}
}
} // unnamed namespace
bool InsertKAGParserHook()
{
ULONG processStartAddress, processStopAddress;
if (!NtInspect::getModuleMemoryRange(L"KAGParser.dll", &startAddress, &stopAddress)) {
ConsoleOutput("KAGParser: failed to get memory range");
return false;
}
const wchar_t *patternString = L"[r]";
const size_t patternStringSize = ::wcslen(patternString) * 2;
ULONG addr = MemDbg::findBytes(patternString, patternStringSize, processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("KAGParser: [r] global string not found");
return false;
}
// Find where it is used as function parameter
addr = MemDbg::findPushAddress(addr, processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("KAGParser: push address not found");
return false;
}
const BYTE ins[] = {
0x66,0x83,0x3c,0x48, 0x5b // 6e5622a8 66:833c48 5b cmp word ptr ds:[eax+ecx*2],0x5b ; jichi: hook here
};
enum { range = 0x20 }; // 0x6e5622a8 - 0x6e562297 = 17
addr = MemDbg::findBytes(ins, sizeof(ins), addr, addr + range);
if (!addr) {
ConsoleOutput("KAGParser: instruction pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.text_fun = SpecialHookKAGParser;
hp.filter_fun = KAGParserFilter;
hp.type = CODEC_UTF16|FIXING_SPLIT|NO_CONTEXT; // Fix the split value to merge all threads
ConsoleOutput("INSERT KAGParser");
return NewHook(hp, "KAGParser");
}
bool InsertKAGParserExHook()
{
ULONG processStartAddress, processStopAddress;
if (!NtInspect::getModuleMemoryRange(L"KAGParserEx.dll", &startAddress, &stopAddress)) {
ConsoleOutput("KAGParserEx: failed to get memory range");
return false;
}
const wchar_t *patternString = L"[r]";
const size_t patternStringSize = ::wcslen(patternString) * 2;
ULONG addr = MemDbg::findBytes(patternString, patternStringSize, processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("KAGParserEx: [r] global string not found");
return false;
}
// Find where it is used as function parameter
addr = MemDbg::findPushAddress(addr, processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("KAGParserEx: push address not found");
return false;
}
const BYTE ins[] = {
0x66,0x83,0x3c,0x41, 0x5b // 10013960 66:833c41 5b cmp word ptr ds:[ecx+eax*2],0x5b ; jichi: hook here
};
enum { range = 0x20 }; // 0x10013960 - 0x10013948 = 24
addr = MemDbg::findBytes(ins, sizeof(ins), addr, addr + range);
if (!addr) {
ConsoleOutput("KAGParserEx: instruction pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.text_fun = SpecialHookKAGParserEx;
hp.filter_fun = KAGParserFilter;
hp.type = CODEC_UTF16|FIXING_SPLIT|NO_CONTEXT; // Fix the split value to merge all threads
ConsoleOutput("INSERT KAGParserEx");
return NewHook(hp, "KAGParserEx");
}
#endif // 0
/** 10/24/2014 jichi: New KiriKiri hook
* Sample game: [141128] Venus Blood -HYPNO- <EFBFBD>
*
* This engine will hook to the caller of caller of the first GetGlyphOutlineW (totally three).
* The logic is quite similar to KiriKiri1 except it backtrack twice to get the function call.
*
* 1/31/2015: If the game no longer invoke GDI functions by default, one way to find the hook
* is to click the <EFBFBD>in the menu to force triggering GetGlyphOutlineW function.
*
* KiriKiriZ:
* https://github.com/krkrz/krkrz
* http://krkrz.github.io
*
* KiriKiri API: http://devdoc.kikyou.info/tvp/docs/kr2doc/contents/f_Layer.html
*
* See: krkrz/src/core/visual/LayerIntf.cpp
* API: http://devdoc.kikyou.info/tvp/docs/kr2doc/contents/f_Layer_drawText.html
*
* Debug method:
* Backtrack from GetGlyphOutlineW, and find the first function that is invoked more
* times than (cached) GetGlyphOutlineW.
*
* - Find function calls to GetGlyphOutlineW (totally three)
*
* - Find the caller of the first GetGlyphOutlineW
* Using MemDbg::findCallerAddressAfterInt3()
*
* - Find the caller of the above caller
* Since the function address is dynamic, the function is found using KiriKiriZHook
*
* 00377c44 8b01 mov eax,dword ptr ds:[ecx]
* 00377c46 ff75 10 push dword ptr ss:[ebp+0x10]
* 00377c49 ff75 0c push dword ptr ss:[ebp+0xc]
* 00377c4c 53 push ebx
* 00377c4d ff50 1c call dword ptr ds:[eax+0x1c] ; jichi: called here
* 00377c50 8bf0 mov esi,eax
* 00377c52 8975 e4 mov dword ptr ss:[ebp-0x1c],esi
* 00377c55 ff46 04 inc dword ptr ds:[esi+0x4]
* 00377c58 c745 fc 04000000 mov dword ptr ss:[ebp-0x4],0x4
*
* Then, the UTF8 two-byte character is at [ecx]+0x14
* 0017E950 16 00 00 00 00 02 00 00 00 00 00 00 98 D2 76 02
* 0017E960 E0 8E 90 D9 42 7D 00 00 00 02 00 00 01 00 00 00
* up: text here
* 0017E970 01 00 01 FF 00 00 00 00 00 00 00 00 C8
*
* 1/30/2015:
* The hooked function in Venus Blood -HYPNO- is as follows.
* Since <EFBFBD> (150226), KiriKiriZ no longer invokes GetGlyphOutlineW.
* Try to extract instruction patterns from the following function instead.
*
* 011a7a3c cc int3
* 011a7a3d cc int3
* 011a7a3e cc int3
* 011a7a3f cc int3
* 011a7a40 55 push ebp
* 011a7a41 8bec mov ebp,esp
* 011a7a43 6a ff push -0x1
* 011a7a45 68 dbaa3101 push .0131aadb
* 011a7a4a 64:a1 00000000 mov eax,dword ptr fs:[0]
* 011a7a50 50 push eax
* 011a7a51 83ec 14 sub esp,0x14
* 011a7a54 53 push ebx
* 011a7a55 56 push esi
* 011a7a56 57 push edi
* 011a7a57 a1 00593d01 mov eax,dword ptr ds:[0x13d5900]
* 011a7a5c 33c5 xor eax,ebp
* 011a7a5e 50 push eax
* 011a7a5f 8d45 f4 lea eax,dword ptr ss:[ebp-0xc]
* 011a7a62 64:a3 00000000 mov dword ptr fs:[0],eax
* 011a7a68 8965 f0 mov dword ptr ss:[ebp-0x10],esp
* 011a7a6b 8bd9 mov ebx,ecx
* 011a7a6d 803d 00113e01 00 cmp byte ptr ds:[0x13e1100],0x0
* 011a7a74 75 17 jnz short .011a7a8d
* 011a7a76 c745 e8 1cb83d01 mov dword ptr ss:[ebp-0x18],.013db81c
* 011a7a7d 8d45 e8 lea eax,dword ptr ss:[ebp-0x18]
* 011a7a80 50 push eax
* 011a7a81 e8 4ae2f0ff call .010b5cd0
* 011a7a86 c605 00113e01 01 mov byte ptr ds:[0x13e1100],0x1
* 011a7a8d 33c9 xor ecx,ecx
* 011a7a8f 384b 21 cmp byte ptr ds:[ebx+0x21],cl
* 011a7a92 0f95c1 setne cl
* 011a7a95 33c0 xor eax,eax
* 011a7a97 3843 20 cmp byte ptr ds:[ebx+0x20],al
* 011a7a9a 0f95c0 setne al
* 011a7a9d 33c8 xor ecx,eax
* 011a7a9f 334b 10 xor ecx,dword ptr ds:[ebx+0x10]
* 011a7aa2 0fb743 14 movzx eax,word ptr ds:[ebx+0x14]
* 011a7aa6 33c8 xor ecx,eax
* 011a7aa8 8b7b 1c mov edi,dword ptr ds:[ebx+0x1c]
* 011a7aab 33f9 xor edi,ecx
* 011a7aad 337b 18 xor edi,dword ptr ds:[ebx+0x18]
* 011a7ab0 897d e4 mov dword ptr ss:[ebp-0x1c],edi
* 011a7ab3 57 push edi
* 011a7ab4 53 push ebx
* 011a7ab5 e8 06330000 call .011aadc0
* 011a7aba 8bf0 mov esi,eax
* 011a7abc 85f6 test esi,esi
* 011a7abe 74 26 je short .011a7ae6
* 011a7ac0 56 push esi
* 011a7ac1 e8 ba330000 call .011aae80
* 011a7ac6 8d46 2c lea eax,dword ptr ds:[esi+0x2c]
* 011a7ac9 85c0 test eax,eax
* 011a7acb 74 19 je short .011a7ae6
* 011a7acd 8b08 mov ecx,dword ptr ds:[eax]
* 011a7acf ff41 04 inc dword ptr ds:[ecx+0x4]
* 011a7ad2 8b00 mov eax,dword ptr ds:[eax]
* 011a7ad4 8b4d f4 mov ecx,dword ptr ss:[ebp-0xc]
* 011a7ad7 64:890d 00000000 mov dword ptr fs:[0],ecx
* 011a7ade 59 pop ecx
* 011a7adf 5f pop edi
* 011a7ae0 5e pop esi
* 011a7ae1 5b pop ebx
* 011a7ae2 8be5 mov esp,ebp
* 011a7ae4 5d pop ebp
* 011a7ae5 c3 retn
* 011a7ae6 8b4d 08 mov ecx,dword ptr ss:[ebp+0x8]
* 011a7ae9 85c9 test ecx,ecx
* 011a7aeb 0f84 47010000 je .011a7c38
* 011a7af1 0fb743 14 movzx eax,word ptr ds:[ebx+0x14]
* 011a7af5 50 push eax
* 011a7af6 e8 b5090300 call .011d84b0
* 011a7afb 8bf0 mov esi,eax
* 011a7afd 8975 ec mov dword ptr ss:[ebp-0x14],esi
* 011a7b00 85f6 test esi,esi
* 011a7b02 0f84 30010000 je .011a7c38
* 011a7b08 6a 34 push 0x34
* 011a7b0a e8 29621300 call .012ddd38
* 011a7b0f 83c4 04 add esp,0x4
* 011a7b12 8bf8 mov edi,eax
* 011a7b14 897d e0 mov dword ptr ss:[ebp-0x20],edi
* 011a7b17 c745 fc 00000000 mov dword ptr ss:[ebp-0x4],0x0
* 011a7b1e 85ff test edi,edi
* 011a7b20 74 1d je short .011a7b3f
* 011a7b22 c747 2c 41000000 mov dword ptr ds:[edi+0x2c],0x41
* 011a7b29 c647 32 00 mov byte ptr ds:[edi+0x32],0x0
* 011a7b2d c747 04 01000000 mov dword ptr ds:[edi+0x4],0x1
* 011a7b34 c707 00000000 mov dword ptr ds:[edi],0x0
* 011a7b3a 8945 e8 mov dword ptr ss:[ebp-0x18],eax
* 011a7b3d eb 05 jmp short .011a7b44
* 011a7b3f 33ff xor edi,edi
* 011a7b41 897d e8 mov dword ptr ss:[ebp-0x18],edi
* 011a7b44 c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
* 011a7b4b 0fb746 04 movzx eax,word ptr ds:[esi+0x4]
* 011a7b4f 8947 1c mov dword ptr ds:[edi+0x1c],eax
* 011a7b52 0fb746 06 movzx eax,word ptr ds:[esi+0x6]
* 011a7b56 8947 20 mov dword ptr ds:[edi+0x20],eax
* 011a7b59 0fbf46 0c movsx eax,word ptr ds:[esi+0xc]
* 011a7b5d 8947 10 mov dword ptr ds:[edi+0x10],eax
* 011a7b60 0fbf46 0e movsx eax,word ptr ds:[esi+0xe]
* 011a7b64 8947 14 mov dword ptr ds:[edi+0x14],eax
* 011a7b67 0fbf46 08 movsx eax,word ptr ds:[esi+0x8]
* 011a7b6b 0345 0c add eax,dword ptr ss:[ebp+0xc]
* 011a7b6e 8947 08 mov dword ptr ds:[edi+0x8],eax
* 011a7b71 0fbf46 0a movsx eax,word ptr ds:[esi+0xa]
* 011a7b75 8b4d 10 mov ecx,dword ptr ss:[ebp+0x10]
* 011a7b78 2bc8 sub ecx,eax
* 011a7b7a 894f 0c mov dword ptr ds:[edi+0xc],ecx
* 011a7b7d 0fb643 20 movzx eax,byte ptr ds:[ebx+0x20]
* 011a7b81 8847 30 mov byte ptr ds:[edi+0x30],al
* 011a7b84 c647 32 00 mov byte ptr ds:[edi+0x32],0x0
* 011a7b88 0fb643 21 movzx eax,byte ptr ds:[ebx+0x21]
* 011a7b8c 8847 31 mov byte ptr ds:[edi+0x31],al
* 011a7b8f 8b43 1c mov eax,dword ptr ds:[ebx+0x1c]
* 011a7b92 8947 28 mov dword ptr ds:[edi+0x28],eax
* 011a7b95 8b43 18 mov eax,dword ptr ds:[ebx+0x18]
* 011a7b98 8947 24 mov dword ptr ds:[edi+0x24],eax
* 011a7b9b c745 fc 01000000 mov dword ptr ss:[ebp-0x4],0x1
* 011a7ba2 837f 1c 00 cmp dword ptr ds:[edi+0x1c],0x0
* 011a7ba6 74 64 je short .011a7c0c
* 011a7ba8 8b47 20 mov eax,dword ptr ds:[edi+0x20]
* 011a7bab 85c0 test eax,eax
* 011a7bad 74 5d je short .011a7c0c
* 011a7baf 0fb776 04 movzx esi,word ptr ds:[esi+0x4]
* 011a7bb3 4e dec esi
* 011a7bb4 83e6 fc and esi,0xfffffffc
* 011a7bb7 83c6 04 add esi,0x4
* 011a7bba 8977 18 mov dword ptr ds:[edi+0x18],esi
* 011a7bbd 0fafc6 imul eax,esi
* 011a7bc0 50 push eax
* 011a7bc1 8bcf mov ecx,edi
* 011a7bc3 e8 b8f6ffff call .011a7280
* 011a7bc8 56 push esi
* 011a7bc9 ff37 push dword ptr ds:[edi]
* 011a7bcb ff75 ec push dword ptr ss:[ebp-0x14]
* 011a7bce 8b4d 08 mov ecx,dword ptr ss:[ebp+0x8]
* 011a7bd1 e8 3a090300 call .011d8510
* 011a7bd6 807b 21 00 cmp byte ptr ds:[ebx+0x21],0x0
* 011a7bda 74 0d je short .011a7be9
* 011a7bdc ff77 28 push dword ptr ds:[edi+0x28]
* 011a7bdf ff77 24 push dword ptr ds:[edi+0x24]
* 011a7be2 8bcf mov ecx,edi
* 011a7be4 e8 d70affff call .011986c0
* 011a7be9 897d ec mov dword ptr ss:[ebp-0x14],edi
* 011a7bec ff47 04 inc dword ptr ds:[edi+0x4]
* 011a7bef c645 fc 02 mov byte ptr ss:[ebp-0x4],0x2
* 011a7bf3 8d45 ec lea eax,dword ptr ss:[ebp-0x14]
* 011a7bf6 50 push eax
* 011a7bf7 ff75 e4 push dword ptr ss:[ebp-0x1c]
* 011a7bfa 53 push ebx
* 011a7bfb e8 50280000 call .011aa450
* 011a7c00 c645 fc 01 mov byte ptr ss:[ebp-0x4],0x1
* 011a7c04 8d4d ec lea ecx,dword ptr ss:[ebp-0x14]
* 011a7c07 e8 84280000 call .011aa490
* 011a7c0c c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
* 011a7c13 8bc7 mov eax,edi
* 011a7c15 8b4d f4 mov ecx,dword ptr ss:[ebp-0xc]
* 011a7c18 64:890d 00000000 mov dword ptr fs:[0],ecx
* 011a7c1f 59 pop ecx
* 011a7c20 5f pop edi
* 011a7c21 5e pop esi
* 011a7c22 5b pop ebx
* 011a7c23 8be5 mov esp,ebp
* 011a7c25 5d pop ebp
* 011a7c26 c3 retn
* 011a7c27 8b4d e8 mov ecx,dword ptr ss:[ebp-0x18]
* 011a7c2a e8 81f6ffff call .011a72b0
* 011a7c2f 6a 00 push 0x0
* 011a7c31 6a 00 push 0x0
* 011a7c33 e8 93cb1300 call .012e47cb
* 011a7c38 a1 dc8a3d01 mov eax,dword ptr ds:[0x13d8adc]
* 011a7c3d 8b0c85 88b93f01 mov ecx,dword ptr ds:[eax*4+0x13fb988]
* 011a7c44 8b01 mov eax,dword ptr ds:[ecx]
* 011a7c46 ff75 10 push dword ptr ss:[ebp+0x10]
* 011a7c49 ff75 0c push dword ptr ss:[ebp+0xc]
* 011a7c4c 53 push ebx
* 011a7c4d ff50 1c call dword ptr ds:[eax+0x1c]
* 011a7c50 8bf0 mov esi,eax
* 011a7c52 8975 e4 mov dword ptr ss:[ebp-0x1c],esi
* 011a7c55 ff46 04 inc dword ptr ds:[esi+0x4]
* 011a7c58 c745 fc 04000000 mov dword ptr ss:[ebp-0x4],0x4
* 011a7c5f 8d45 e4 lea eax,dword ptr ss:[ebp-0x1c]
* 011a7c62 50 push eax
* 011a7c63 57 push edi
* 011a7c64 53 push ebx
* 011a7c65 e8 a62c0000 call .011aa910
* 011a7c6a a1 388b3f01 mov eax,dword ptr ds:[0x13f8b38]
* 011a7c6f 8b0d 448b3f01 mov ecx,dword ptr ds:[0x13f8b44]
* 011a7c75 3bc1 cmp eax,ecx
* 011a7c77 76 08 jbe short .011a7c81
* 011a7c79 2bc1 sub eax,ecx
* 011a7c7b 50 push eax
* 011a7c7c e8 1f2e0000 call .011aaaa0
* 011a7c81 c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
* 011a7c88 8b46 04 mov eax,dword ptr ds:[esi+0x4]
* 011a7c8b 83f8 01 cmp eax,0x1
* 011a7c8e 75 2c jnz short .011a7cbc
* 011a7c90 8b06 mov eax,dword ptr ds:[esi]
* 011a7c92 85c0 test eax,eax
* 011a7c94 74 09 je short .011a7c9f
* 011a7c96 50 push eax
* 011a7c97 e8 3b621300 call .012dded7
* 011a7c9c 83c4 04 add esp,0x4
* 011a7c9f 56 push esi
* 011a7ca0 e8 335e1300 call .012ddad8
* 011a7ca5 83c4 04 add esp,0x4
* 011a7ca8 8bc6 mov eax,esi
* 011a7caa 8b4d f4 mov ecx,dword ptr ss:[ebp-0xc]
* 011a7cad 64:890d 00000000 mov dword ptr fs:[0],ecx
* 011a7cb4 59 pop ecx
* 011a7cb5 5f pop edi
* 011a7cb6 5e pop esi
* 011a7cb7 5b pop ebx
* 011a7cb8 8be5 mov esp,ebp
* 011a7cba 5d pop ebp
* 011a7cbb c3 retn
* 011a7cbc 48 dec eax
* 011a7cbd 8946 04 mov dword ptr ds:[esi+0x4],eax
* 011a7cc0 8bc6 mov eax,esi
* 011a7cc2 8b4d f4 mov ecx,dword ptr ss:[ebp-0xc]
* 011a7cc5 64:890d 00000000 mov dword ptr fs:[0],ecx
* 011a7ccc 59 pop ecx
* 011a7ccd 5f pop edi
* 011a7cce 5e pop esi
* 011a7ccf 5b pop ebx
* 011a7cd0 8be5 mov esp,ebp
* 011a7cd2 5d pop ebp
* 011a7cd3 c3 retn
* 011a7cd4 cc int3
* 011a7cd5 cc int3
* 011a7cd6 cc int3
* 011a7cd7 cc int3
* 011a7cd8 cc int3
*
* Here's the hooked function in <EFBFBD> (150226).
* I randomly picked a pattern from VBH:
*
* 011a7a95 33c0 xor eax,eax
* 011a7a97 3843 20 cmp byte ptr ds:[ebx+0x20],al
* 011a7a9a 0f95c0 setne al
* 011a7a9d 33c8 xor ecx,eax
* 011a7a9f 334b 10 xor ecx,dword ptr ds:[ebx+0x10]
* 011a7aa2 0fb743 14 movzx eax,word ptr ds:[ebx+0x14]
*
* i.e: 33c03843200f95c033c8334b100fb74314
*
* The new hooked function in <EFBFBD> is as follows.
*
* 012280dc cc int3
* 012280dd cc int3
* 012280de cc int3
* 012280df cc int3
* 012280e0 55 push ebp
* 012280e1 8bec mov ebp,esp
* 012280e3 6a ff push -0x1
* 012280e5 68 3b813d01 push .013d813b
* 012280ea 64:a1 00000000 mov eax,dword ptr fs:[0]
* 012280f0 50 push eax
* 012280f1 83ec 14 sub esp,0x14
* 012280f4 53 push ebx
* 012280f5 56 push esi
* 012280f6 57 push edi
* 012280f7 a1 00694901 mov eax,dword ptr ds:[0x1496900]
* 012280fc 33c5 xor eax,ebp
* 012280fe 50 push eax
* 012280ff 8d45 f4 lea eax,dword ptr ss:[ebp-0xc]
* 01228102 64:a3 00000000 mov dword ptr fs:[0],eax
* 01228108 8965 f0 mov dword ptr ss:[ebp-0x10],esp
* 0122810b 8bd9 mov ebx,ecx
* 0122810d 803d e82d4a01 00 cmp byte ptr ds:[0x14a2de8],0x0
* 01228114 75 17 jnz short .0122812d
* 01228116 c745 e8 d8d44901 mov dword ptr ss:[ebp-0x18],.0149d4d8
* 0122811d 8d45 e8 lea eax,dword ptr ss:[ebp-0x18]
* 01228120 50 push eax
* 01228121 e8 aadbf0ff call .01135cd0
* 01228126 c605 e82d4a01 01 mov byte ptr ds:[0x14a2de8],0x1
* 0122812d 33c9 xor ecx,ecx
* 0122812f 384b 21 cmp byte ptr ds:[ebx+0x21],cl
* 01228132 0f95c1 setne cl
* 01228135 33c0 xor eax,eax
* 01228137 3843 20 cmp byte ptr ds:[ebx+0x20],al
* 0122813a 0f95c0 setne al
* 0122813d 33c8 xor ecx,eax
* 0122813f 334b 10 xor ecx,dword ptr ds:[ebx+0x10]
* 01228142 0fb743 14 movzx eax,word ptr ds:[ebx+0x14]
* 01228146 33c8 xor ecx,eax
* 01228148 8b7b 1c mov edi,dword ptr ds:[ebx+0x1c]
* 0122814b 33f9 xor edi,ecx
* 0122814d 337b 18 xor edi,dword ptr ds:[ebx+0x18]
* 01228150 897d e4 mov dword ptr ss:[ebp-0x1c],edi
* 01228153 57 push edi
* 01228154 53 push ebx
* 01228155 e8 06330000 call .0122b460
* 0122815a 8bf0 mov esi,eax
* 0122815c 85f6 test esi,esi
* 0122815e 74 26 je short .01228186
* 01228160 56 push esi
* 01228161 e8 ba330000 call .0122b520
* 01228166 8d46 2c lea eax,dword ptr ds:[esi+0x2c]
* 01228169 85c0 test eax,eax
* 0122816b 74 19 je short .01228186
* 0122816d 8b08 mov ecx,dword ptr ds:[eax]
* 0122816f ff41 04 inc dword ptr ds:[ecx+0x4]
* 01228172 8b00 mov eax,dword ptr ds:[eax]
* 01228174 8b4d f4 mov ecx,dword ptr ss:[ebp-0xc]
* 01228177 64:890d 00000000 mov dword ptr fs:[0],ecx
* 0122817e 59 pop ecx
* 0122817f 5f pop edi
* 01228180 5e pop esi
* 01228181 5b pop ebx
* 01228182 8be5 mov esp,ebp
* 01228184 5d pop ebp
* 01228185 c3 retn
* 01228186 8b4d 08 mov ecx,dword ptr ss:[ebp+0x8]
* 01228189 85c9 test ecx,ecx
* 0122818b 0f84 47010000 je .012282d8
* 01228191 0fb743 14 movzx eax,word ptr ds:[ebx+0x14]
* 01228195 50 push eax
* 01228196 e8 950f0300 call .01259130
* 0122819b 8bf0 mov esi,eax
* 0122819d 8975 ec mov dword ptr ss:[ebp-0x14],esi
* 012281a0 85f6 test esi,esi
* 012281a2 0f84 30010000 je .012282d8
* 012281a8 6a 34 push 0x34
* 012281aa e8 297c1300 call .0135fdd8
* 012281af 83c4 04 add esp,0x4
* 012281b2 8bf8 mov edi,eax
* 012281b4 897d e0 mov dword ptr ss:[ebp-0x20],edi
* 012281b7 c745 fc 00000000 mov dword ptr ss:[ebp-0x4],0x0
* 012281be 85ff test edi,edi
* 012281c0 74 1d je short .012281df
* 012281c2 c747 2c 41000000 mov dword ptr ds:[edi+0x2c],0x41
* 012281c9 c647 32 00 mov byte ptr ds:[edi+0x32],0x0
* 012281cd c747 04 01000000 mov dword ptr ds:[edi+0x4],0x1
* 012281d4 c707 00000000 mov dword ptr ds:[edi],0x0
* 012281da 8945 e8 mov dword ptr ss:[ebp-0x18],eax
* 012281dd eb 05 jmp short .012281e4
* 012281df 33ff xor edi,edi
* 012281e1 897d e8 mov dword ptr ss:[ebp-0x18],edi
* 012281e4 c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
* 012281eb 0fb746 04 movzx eax,word ptr ds:[esi+0x4]
* 012281ef 8947 1c mov dword ptr ds:[edi+0x1c],eax
* 012281f2 0fb746 06 movzx eax,word ptr ds:[esi+0x6]
* 012281f6 8947 20 mov dword ptr ds:[edi+0x20],eax
* 012281f9 0fbf46 0c movsx eax,word ptr ds:[esi+0xc]
* 012281fd 8947 10 mov dword ptr ds:[edi+0x10],eax
* 01228200 0fbf46 0e movsx eax,word ptr ds:[esi+0xe]
* 01228204 8947 14 mov dword ptr ds:[edi+0x14],eax
* 01228207 0fbf46 08 movsx eax,word ptr ds:[esi+0x8]
* 0122820b 0345 0c add eax,dword ptr ss:[ebp+0xc]
* 0122820e 8947 08 mov dword ptr ds:[edi+0x8],eax
* 01228211 0fbf46 0a movsx eax,word ptr ds:[esi+0xa]
* 01228215 8b4d 10 mov ecx,dword ptr ss:[ebp+0x10]
* 01228218 2bc8 sub ecx,eax
* 0122821a 894f 0c mov dword ptr ds:[edi+0xc],ecx
* 0122821d 0fb643 20 movzx eax,byte ptr ds:[ebx+0x20]
* 01228221 8847 30 mov byte ptr ds:[edi+0x30],al
* 01228224 c647 32 00 mov byte ptr ds:[edi+0x32],0x0
* 01228228 0fb643 21 movzx eax,byte ptr ds:[ebx+0x21]
* 0122822c 8847 31 mov byte ptr ds:[edi+0x31],al
* 0122822f 8b43 1c mov eax,dword ptr ds:[ebx+0x1c]
* 01228232 8947 28 mov dword ptr ds:[edi+0x28],eax
* 01228235 8b43 18 mov eax,dword ptr ds:[ebx+0x18]
* 01228238 8947 24 mov dword ptr ds:[edi+0x24],eax
* 0122823b c745 fc 01000000 mov dword ptr ss:[ebp-0x4],0x1
* 01228242 837f 1c 00 cmp dword ptr ds:[edi+0x1c],0x0
* 01228246 74 64 je short .012282ac
* 01228248 8b47 20 mov eax,dword ptr ds:[edi+0x20]
* 0122824b 85c0 test eax,eax
* 0122824d 74 5d je short .012282ac
* 0122824f 0fb776 04 movzx esi,word ptr ds:[esi+0x4]
* 01228253 4e dec esi
* 01228254 83e6 fc and esi,0xfffffffc
* 01228257 83c6 04 add esi,0x4
* 0122825a 8977 18 mov dword ptr ds:[edi+0x18],esi
* 0122825d 0fafc6 imul eax,esi
* 01228260 50 push eax
* 01228261 8bcf mov ecx,edi
* 01228263 e8 a8f6ffff call .01227910
* 01228268 56 push esi
* 01228269 ff37 push dword ptr ds:[edi]
* 0122826b ff75 ec push dword ptr ss:[ebp-0x14]
* 0122826e 8b4d 08 mov ecx,dword ptr ss:[ebp+0x8]
* 01228271 e8 1a0f0300 call .01259190
* 01228276 807b 21 00 cmp byte ptr ds:[ebx+0x21],0x0
* 0122827a 74 0d je short .01228289
* 0122827c ff77 28 push dword ptr ds:[edi+0x28]
* 0122827f ff77 24 push dword ptr ds:[edi+0x24]
* 01228282 8bcf mov ecx,edi
* 01228284 e8 870affff call .01218d10
* 01228289 897d ec mov dword ptr ss:[ebp-0x14],edi
* 0122828c ff47 04 inc dword ptr ds:[edi+0x4]
* 0122828f c645 fc 02 mov byte ptr ss:[ebp-0x4],0x2
* 01228293 8d45 ec lea eax,dword ptr ss:[ebp-0x14]
* 01228296 50 push eax
* 01228297 ff75 e4 push dword ptr ss:[ebp-0x1c]
* 0122829a 53 push ebx
* 0122829b e8 50280000 call .0122aaf0
* 012282a0 c645 fc 01 mov byte ptr ss:[ebp-0x4],0x1
* 012282a4 8d4d ec lea ecx,dword ptr ss:[ebp-0x14]
* 012282a7 e8 84280000 call .0122ab30
* 012282ac c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
* 012282b3 8bc7 mov eax,edi
* 012282b5 8b4d f4 mov ecx,dword ptr ss:[ebp-0xc]
* 012282b8 64:890d 00000000 mov dword ptr fs:[0],ecx
* 012282bf 59 pop ecx
* 012282c0 5f pop edi
* 012282c1 5e pop esi
* 012282c2 5b pop ebx
* 012282c3 8be5 mov esp,ebp
* 012282c5 5d pop ebp
* 012282c6 c3 retn
* 012282c7 8b4d e8 mov ecx,dword ptr ss:[ebp-0x18]
* 012282ca e8 71f6ffff call .01227940
* 012282cf 6a 00 push 0x0
* 012282d1 6a 00 push 0x0
* 012282d3 e8 83eb1300 call .01366e5b
* 012282d8 a1 e89a4901 mov eax,dword ptr ds:[0x1499ae8]
* 012282dd 8b0c85 f0d64b01 mov ecx,dword ptr ds:[eax*4+0x14bd6f0]
* 012282e4 8b01 mov eax,dword ptr ds:[ecx]
* 012282e6 ff75 10 push dword ptr ss:[ebp+0x10]
* 012282e9 ff75 0c push dword ptr ss:[ebp+0xc]
* 012282ec 53 push ebx
* 012282ed ff50 1c call dword ptr ds:[eax+0x1c]
* 012282f0 8bf0 mov esi,eax
* 012282f2 8975 e4 mov dword ptr ss:[ebp-0x1c],esi
* 012282f5 ff46 04 inc dword ptr ds:[esi+0x4]
* 012282f8 c745 fc 04000000 mov dword ptr ss:[ebp-0x4],0x4
* 012282ff 8d45 e4 lea eax,dword ptr ss:[ebp-0x1c]
* 01228302 50 push eax
* 01228303 57 push edi
* 01228304 53 push ebx
* 01228305 e8 a62c0000 call .0122afb0
* 0122830a a1 a0a84b01 mov eax,dword ptr ds:[0x14ba8a0]
* 0122830f 8b0d aca84b01 mov ecx,dword ptr ds:[0x14ba8ac]
* 01228315 3bc1 cmp eax,ecx
* 01228317 76 08 jbe short .01228321
* 01228319 2bc1 sub eax,ecx
* 0122831b 50 push eax
* 0122831c e8 1f2e0000 call .0122b140
* 01228321 c745 fc ffffffff mov dword ptr ss:[ebp-0x4],-0x1
* 01228328 8b46 04 mov eax,dword ptr ds:[esi+0x4]
* 0122832b 83f8 01 cmp eax,0x1
* 0122832e 75 2c jnz short .0122835c
* 01228330 8b06 mov eax,dword ptr ds:[esi]
* 01228332 85c0 test eax,eax
* 01228334 74 09 je short .0122833f
* 01228336 50 push eax
* 01228337 e8 3b7c1300 call .0135ff77
* 0122833c 83c4 04 add esp,0x4
* 0122833f 56 push esi
* 01228340 e8 33781300 call .0135fb78
* 01228345 83c4 04 add esp,0x4
* 01228348 8bc6 mov eax,esi
* 0122834a 8b4d f4 mov ecx,dword ptr ss:[ebp-0xc]
* 0122834d 64:890d 00000000 mov dword ptr fs:[0],ecx
* 01228354 59 pop ecx
* 01228355 5f pop edi
* 01228356 5e pop esi
* 01228357 5b pop ebx
* 01228358 8be5 mov esp,ebp
* 0122835a 5d pop ebp
* 0122835b c3 retn
* 0122835c 48 dec eax
* 0122835d 8946 04 mov dword ptr ds:[esi+0x4],eax
* 01228360 8bc6 mov eax,esi
* 01228362 8b4d f4 mov ecx,dword ptr ss:[ebp-0xc]
* 01228365 64:890d 00000000 mov dword ptr fs:[0],ecx
* 0122836c 59 pop ecx
* 0122836d 5f pop edi
* 0122836e 5e pop esi
* 0122836f 5b pop ebx
* 01228370 8be5 mov esp,ebp
* 01228372 5d pop ebp
* 01228373 c3 retn
* 01228374 cc int3
* 01228375 cc int3
* 01228376 cc int3
* 01228377 cc int3
* 01228378 cc int3
*/
namespace { // unnamed
// Skip individual L'\n' which might cause repetition.
//bool NewLineWideCharSkipper(LPVOID data, DWORD *size, HookParam *)
//{
// LPCWSTR text = (LPCWSTR)data;
// if (*size == 2 && *text == L'\n')
// return false;
// return true;
//}
//
bool NewKiriKiriZHook(DWORD addr)
{
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::ecx);
hp.split = hp.offset; // the same logic but diff value as KiriKiri1, use [ecx] as split
hp.index = 0x14; // the same as KiriKiri1
hp.type = CODEC_UTF16|DATA_INDIRECT|USING_SPLIT|SPLIT_INDIRECT;
//hp.filter_fun = NewLineCharFilterW;
ConsoleOutput("INSERT KiriKiriZ");
ConsoleOutput("KiriKiriZ: disable GDI hooks");
return NewHook(hp, "KiriKiriZ");
}
bool InsertKiriKiriZHook1()
{
ULONG addr = MemDbg::findCallerAddressAfterInt3((DWORD)::GetGlyphOutlineW, processStartAddress, processStopAddress);
if (!addr) {
ConsoleOutput("KiriKiriZ1: could not find caller of GetGlyphOutlineW");
return false;
}
HookParam hp;
hp.address = addr;
2024-02-17 14:33:26 +08:00
hp.text_fun =
[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len)
{
hp->text_fun=nullptr;
hp->type=HOOK_EMPTY;
DWORD addr = stack->stack[0]; // retaddr
addr = MemDbg::findEnclosingAlignedFunction(addr, 0x400); // range is around 0x377c50 - 0x377a40 = 0x210
if (!addr) {
ConsoleOutput("KiriKiriZ: failed to find enclosing function");
return;
}
NewKiriKiriZHook(addr);
ConsoleOutput("KiriKiriZ1 inserted");
};
2024-02-07 20:59:24 +08:00
ConsoleOutput("INSERT KiriKiriZ1 empty hook");
return NewHook(hp, "KiriKiriZ Hook");
}
// jichi 1/30/2015: Add KiriKiriZ2 for サバウィッ<E382A3>
// It inserts to the same location as the old KiriKiriZ, but use a different way to find it.
bool InsertKiriKiriZHook2()
{
const BYTE bytes[] = {
0x38,0x4b, 0x21, // 0122812f 384b 21 cmp byte ptr ds:[ebx+0x21],cl
0x0f,0x95,0xc1, // 01228132 0f95c1 setne cl
0x33,0xc0, // 01228135 33c0 xor eax,eax
0x38,0x43, 0x20, // 01228137 3843 20 cmp byte ptr ds:[ebx+0x20],al
0x0f,0x95,0xc0, // 0122813a 0f95c0 setne al
0x33,0xc8, // 0122813d 33c8 xor ecx,eax
0x33,0x4b, 0x10, // 0122813f 334b 10 xor ecx,dword ptr ds:[ebx+0x10]
0x0f,0xb7,0x43, 0x14 // 01228142 0fb743 14 movzx eax,word ptr ds:[ebx+0x14]
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
//GROWL_DWORD(addr);
if (!addr) {
ConsoleOutput("KiriKiriZ2: pattern not found");
return false;
}
// 012280e0 55 push ebp
// 012280e1 8bec mov ebp,esp
addr = MemDbg::findEnclosingAlignedFunction(addr, 0x100); // 0x0122812f-0x012280e0 = 0x4F
enum : BYTE { push_ebp = 0x55 }; // 011d4c80 /$ 55 push ebp
if (!addr || *(BYTE *)addr != push_ebp) {
ConsoleOutput("KiriKiriZ2: pattern found but the function offset is invalid");
return false;
}
NewKiriKiriZHook(addr);
ConsoleOutput("KiriKiriZ2 inserted");
return true;
}
} // unnamed namespace
// jichi 1/30/2015: Do KiriKiriZ2 first, which might insert to the same location as KiriKiri1.
bool KiriKiriZ_msvcFilter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPWSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
static std::wstring prevText;
if (!*len)
return false;
text[*len/sizeof(wchar_t)] = L'\0'; // clean text
if (!prevText.compare(text))
return false;
prevText = text;
StringCharReplacer(text, len, L"\\n", 2, L' ');
if (cpp_wcsnstr(text, L"%", *len/sizeof(wchar_t))) {
StringFilterBetween(text, len, L"%", 1, L";", 1);
}
return true;
}
bool Krkrtextrenderdll () {
HMODULE module = GetModuleHandleW(L"textrender.dll");
if (module == 0)return false;
if (GetProcAddress(module, "V2Link") == 0)return false;
bool b1=[module]() {
auto [minAddress, maxAddress] = Util::QueryModuleLimits(module);
BYTE bytes[] = {
0x81,0xEC,0xFC,0x00,0x00,0x00
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), minAddress, maxAddress);
if (addr == 0)return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)return false;
ConsoleOutput("textrender %p", addr);
HookParam hp;
hp.address = (DWORD)addr;
hp.offset=get_stack(2);
hp.type = CODEC_UTF16;
return NewHook(hp, "krkr_textrender");
}();
bool b2=[module]() {
auto [minAddress, maxAddress] = Util::QueryModuleLimits(module);
BYTE bytes[] = {
0xFF, XX,
0x88, XX, XX, XX,
XX, XX, XX, XX,
XX, XX,
0x74, XX,
XX, XX, XX, XX,
XX,
XX,
0xE8, XX, XX, XX, XX,
0xB0, 0x01,
0xC3
};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), minAddress, maxAddress);
if (addr == 0)return false;
ConsoleOutput("textrender %p", addr);
HookParam hp;
hp.address = addr -0xb;
hp.offset=get_reg(regs::eax);
hp.type = CODEC_UTF16 | USING_STRING;
hp.filter_fun = KiriKiriZ_msvcFilter;
return NewHook(hp, "krkr_textrender");
}();
return b1||b2;
}
bool KiriKiriZ3Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPWSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
CharFilter(text, len, L'\x000A');
if (cpp_wcsnstr(text, L"%", *len/sizeof(wchar_t))) {
StringFilterBetween(text, len, L"%", 1, L"%", 1);
}
return true;
}
bool InsertKiriKiriZHook3()
{
/*
* Sample games:
* https://vndb.org/r109253
*/
const BYTE bytes[] = {
0x66, 0x83, 0x3F, 0x00, // cmp word ptr [edi],00 << hook here
0x75, 0x06, // jne Imouto_no_Seiiki.exe+195C1
0x33, 0xDB, // xor ebx,ebx
0x89, 0x1E, // mov [esi],ebx
0xEB, 0x1B // jmp Imouto_no_Seiiki.exe+195DC
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("KiriKiriZ3: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset =get_reg(regs::edi);
hp.split = get_reg(regs::edx);
hp.type = NO_CONTEXT | CODEC_UTF16 | USING_STRING | USING_SPLIT;
hp.filter_fun = KiriKiriZ3Filter;
ConsoleOutput("INSERT KiriKiriZ3");
return NewHook(hp, "KiriKiriZ3");
}
bool KiriKiriZ4Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPWSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
if (text[0] == L' ' || text[0] == L':' || text[0] == L'@' || text[0] == L'[' || text[0] == L']')
return false;
if (cpp_wcsnstr(text, L"[", *len/sizeof(wchar_t))) {
StringCharReplacer(text, len, L"[r]", 3, L' ');
StringFilterBetween(text, len, L"[", 1, L"]", 1);
}
return true;
}
bool InsertKiriKiriZHook4()
{
/*
* Sample games:
* https://vndb.org/r111774
* https://vndb.org/v38021
*/
const BYTE bytes[] = {
0xE8, 0xE8, 0xBA, 0xFE, 0xFF, // call Shironagasu.exe+227B0 << hook here
0xC7, 0x45, 0xFC, XX4, // mov [ebp-04],00000000
0xC7, 0x45, 0xF0, XX4, // mov [ebp-10],00000001
0x8B, 0x45, 0x08 // mov eax,[ebp+08]
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("KiriKiriZ4: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset = get_reg(regs::ebx);
hp.type = NO_CONTEXT | CODEC_UTF16 | USING_STRING;
hp.filter_fun = KiriKiriZ4Filter;
ConsoleOutput("INSERT KiriKiriZ4");
return NewHook(hp, "KiriKiriZ4");
}
bool InsertKiriKiriZHook()
{
auto ok=Krkrtextrenderdll();
ok = InsertKiriKiriZHook3() || ok;
ok = InsertKiriKiriZHook4() || ok;
return InsertKiriKiriZHook2() || InsertKiriKiriZHook1()|| ok;
}
namespace{
int type=0;std::wstring saveend=L"";
void hookafter(hook_stack*s,void* data, size_t len){
auto newText =std::wstring((wchar_t*)data,len/2);// EngineController::instance()->dispatchTextWSTD(innner, Engine::ScenarioRole, 0);
newText=newText+L"[plc]";
if(type==2){
newText=L"[x]"+newText;
}
else if(type==1){
newText=std::regex_replace(newText, std::wregex(L"\u300c"), L"\\[\u300c\\]");
newText=std::regex_replace(newText, std::wregex(L"\u300d"), L"\\[\u300d\\]");
}
newText+=saveend;
auto text = (LPWSTR)s->esi;
wcscpy(text,newText.c_str());
}
bool hookBefore(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
//シロガネオトメ
auto text = (LPWSTR)s->esi;
if ( !text || !*text)
return false;
if (all_ascii(text,wcslen(text)))return false;
std::wstring wstext=text;
//[「]ぱ、ぱんつなんてどうしてそんなに気になるの。ゆきちゃんだってはいてるでしょ[」][plc] ->对话
//[x]彼女は言葉通りに、お風呂上がりにパンツを穿き忘れてそのまま一日過ごしかけたりすることがあった。ボクはそれをまじめに心配していたのだ(開き直り)。[plc] ->旁白
/*
//算了,改人名容易出问题
//[name name="?/翼"] ->人名
//[name name="翼"]
auto checkisname=std::regex_replace(wstext, std::wregex(L"\\[name name=\"(.*?)\"\\]"), L"");
if(wstext!=L"" && checkisname==L""){
auto name=std::regex_replace(wstext, std::wregex(L"\\[name name=\"(.*?)\"\\]"), L"$1");
auto _idx=name.find(L'\uff0f');
std::wstring end=L"";
if(_idx!=name.npos){
name=name.substr(0,_idx);
end=name.substr(_idx);
}
name = EngineController::instance()->dispatchTextWSTD(name, Engine::NameRole, 0);
name+=end;
name=L"[name name=\""+name+L"\"]";
wcscpy(text,name.c_str());
return true;
}
*/
if(wstext.size()<5||(wstext.substr(wstext.size()-5)!=L"[plc]"))return false;
type=0;
if(wstext.substr(0,3)==L"[x]"){
type=1;
wstext=wstext.substr(3);
}
else if (wstext.substr(0,3)==L"[\u300c]"){ //「 」
type=2;
wstext=std::regex_replace(wstext, std::wregex(L"\\[\u300c\\]"), L"\u300c");
wstext=std::regex_replace(wstext, std::wregex(L"\\[\u300d\\]"), L"\u300d");
}
if(type==0)return false;//未知类型
saveend=L"";
auto innner=wstext.substr(0,wstext.size()-5);
innner=std::regex_replace(innner, std::wregex(L"\\[eruby text=(.*?) str=(.*?)\\]"), L"$2");
if(innner[innner.size()-1]==L']'){
//「ボクの身体をあれだけ好き勝手しておいて、いまさらカマトトぶっても遅いよ。ほら、正直になりなよ」[waitsd layer=&CHAR6]
for(int i=innner.size();i>0;i--){
if(innner[i]=='['){
saveend=innner.substr(i);
innner=innner.substr(0,i);
break;
}
}
}
2024-03-21 17:57:04 +08:00
return write_string_overwrite(data,len,innner);
2024-02-07 20:59:24 +08:00
}
bool attachkr2(ULONG startAddress, ULONG stopAddress)
{
//シロガネオトメ
// .text:005D288D 66 8B 06 mov ax, [esi]
// .text:005D2890 66 83 F8 3B cmp ax, 3Bh ; ';'
// .text:005D2894 0F 84 AA 06 00 00 jz loc_5D2F44
// .text:005D2894
// .text:005D289A 66 83 F8 2A cmp ax, 2Ah ; '*'
// .text:005D289E 0F 85 DF 02 00 00 jnz loc_5D2B83
//修改v3的值
// v3 = *(const wchar_t **)(*(_DWORD *)(a1 + 100) + 8 * *(_DWORD *)(a1 + 116));
// if ( *v3 != 59 )
// {
// if ( *v3 == 42 )
const uint8_t bytes[] = {
0x66,0x8B,0x06,0x66,0x83,0xF8,0x3B,0x0F,XX,XX4,0x66,0x83,0xF8,0x2A,0x0F
};
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
if (!addr) return false;
HookParam hp;
hp.address = addr;
hp.type = EMBED_ABLE|CODEC_UTF16;
hp.hook_before=hookBefore;
hp.hook_after=hookafter;
return NewHook(hp, "EmbedKrkr2");
}
} // namespace Private
namespace Private {
std::wstring ConvertToFullWidth(const std::wstring& str) {
std::wstring fullWidthStr;
for (wchar_t c : str) {
if (c >= 32 && c <= 126) {
fullWidthStr += static_cast<wchar_t>(c + 65248);
} else {
fullWidthStr += c;
}
}
return fullWidthStr;
}
bool hookBeforez(hook_stack*s,void* data, size_t* len,uintptr_t*role)
{
auto text = (LPCSTR)s->ecx;
if ( !text || !*text)
return false;
2024-02-17 12:06:27 +08:00
if(strlen(text)>2000)return false;
2024-02-07 20:59:24 +08:00
if (all_ascii(text,strlen(text)))return false;
//"。」』?―!、"
auto chatflags={"\xe3\x80\x82", "\xe3\x80\x8d","\xe3\x80\x8f","\xef\xbc\x9f","\xe2\x80\x95","\xef\xbc\x81","\xe3\x80\x81"};
bool ok=false;
for (auto f:chatflags){
if(strstr(text,f))ok=true;
}
if(ok==false)return false;
// auto role = Engine::ScenarioRole ;
//auto split = s->edx;
//auto sig = Engine::hashThreadSignature(role, split);
enum { sig = 0 }; // split not used
std::string utf8save=text;
strReplace(utf8save, "%51;", "\\-");
strReplace(utf8save, "%164;", "\\+\\+");
strReplace(utf8save, "%123;", "\\+");
strReplace(utf8save, "%205;", "\\+\\+\\+");
strReplace(utf8save, "#000033ff;", "\\#0033FF");
strReplace(utf8save, "#;", "\\#FFFFFF");
strReplace(utf8save, "#00ff0000;", "\\#FF0000");
strReplace(utf8save, "%p-1;%f\xef\xbc\xad\xef\xbc\xb3 \xe3\x82\xb4\xe3\x82\xb7\xe3\x83\x83\xe3\x82\xaf;", ""); //"%p-1;%f ゴシック;"
strReplace(utf8save, "%p;%fuser;", "");
2024-03-21 17:57:04 +08:00
return write_string_overwrite(data,len,utf8save);
2024-02-07 20:59:24 +08:00
}
void after(hook_stack*s,void* data, size_t len){
std::string res= std::string((char*)data,len);// EngineController::instance()->dispatchTextWSTD(innner, Engine::ScenarioRole, 0);
strReplace(res, "\\-", "%51;");
strReplace(res, "\\+\\+", "%164;");
strReplace(res, "\\+", "%123;");
strReplace(res, "\\+\\+\\+", "%205;");
strReplace(res, "\\#0033FF", "#000033ff;");
strReplace(res, "\\#FFFFFF", "#;");
strReplace(res, "\\#FF0000", "#00ff0000;");
res=WideStringToString(ConvertToFullWidth((StringToWideString(res))));
2024-03-21 17:57:04 +08:00
write_string_new(&s->ecx,0,res);
2024-02-07 20:59:24 +08:00
}
bool attach(ULONG startAddress, ULONG stopAddress)
{
//findbytes搜索1长度BYTE[]时有问题。
//mashiro_fhd
// BYTE sig0[]={0x8B,XX};//mov esi,ecx
//ecx->XXX->esi->al/bl/cl/dl
/*
eax c1
ebx d9
ebp e9
edx d1
edi f9
esi f1
*/
// BYTE sig01[]={0x8A,XX};//mov al, [esi]
/*
al 06
bl 1e
cl 0e
dl 16
*/
#define sigs(n,N) BYTE sig1##n[]={0x3C,N};BYTE sig2##n[]={0x80,XX,N};
#define addsig(n) {sig1##n,sig2##n},
sigs(1,0x80)sigs(2,0xc2)sigs(3,0xE0)sigs(4,0xF0)sigs(5,0xF8)sigs(6,0xFC)sigs(7,0xFE)
// BYTE sig1[]={0x3C,0x80,XX};//0x73//0x0f
// BYTE sig2[]={0x3C,0xC2,XX};
// BYTE sig3[]={0x3C,0xE0,XX};
// BYTE sig4[]={0x3C,0xF0,XX};
// BYTE sig5[]={0x3C,0xF8,XX};
// BYTE sig6[]={0x3C,0xFC,XX};
// BYTE sig7[]={0x3C,0xFE,XX};
ULONG addr =startAddress;
bool succ=false;
while(addr){
// MessageBox(0,xx,L"",0);
addr=[](DWORD addr,DWORD stopAddress){
for(;addr<stopAddress ;addr++)
if((*(BYTE*)addr)==0x8b)
switch (*(BYTE*)(addr+1))
{
case 0xc1:case 0xd9:case 0xe9:case 0xd1:case 0xf9: case 0xf1: return addr; default:continue;
}
return (DWORD)0;
}(addr+1,stopAddress);
// ConsoleOutput("%p",0x400000+addr-startAddress);
if(addr==0)continue;
auto check=[](DWORD addr,DWORD stopAddress){
for(;addr<stopAddress;addr++)
if((*(BYTE*)addr)==0x8a)
switch (*(BYTE*)(addr+1))
{
case 0x06:case 0x1e:case 0x0e:case 0x16: return addr; default:continue;
}
return (DWORD)0;
}(addr,addr+0x10);
if(check==0)continue;
switch (*(BYTE*)(check+1))
{
case 0x06:case 0x1e:case 0x0e:case 0x16: break; default:continue;
}
bool ok=true;
for(auto p:std::vector<std::pair<BYTE*,BYTE*>>{
addsig(1)addsig(2)addsig(3)addsig(4)addsig(5)addsig(6)addsig(7)
}){
auto check1=MemDbg::findBytes(p.first, 2, check, check+0x1000);
auto check2=MemDbg::findBytes(p.second, 3, check, check+0x1000);
check=min(check1,check2);
if(check==0)check=max(check1,check2);
if(check==0){
ok=false;break;
}
}
if(ok){
HookParam hp;
hp.address = addr;
hp.type = EMBED_ABLE|CODEC_UTF8;
hp.hook_before=hookBeforez;
hp.hook_after=after;
succ|=NewHook(hp, "EmbedKrkrZ");
// return true;
}
}
return succ;
}
} // namespace ScenarioHook
namespace{
bool wcslen_wcscpy(){
//LOVELY×CATION
const uint8_t bytes2[] = {
//wcscpy 唯一
0x55,0x8b,0xec,
0x53,0x56,0x8b,0x75,0x0c,0x56,0xe8,XX,0xFF,0xFF,0xFF,//call wcslen距离很近故均为ff
0x59,0x8b,0xd8,0x33,XX,0x8b,0x45,0x08
};
const uint8_t bytes[] = {
//wcslen 有多个,可以修改任意一个,但是会造成困扰
0x55,0x8b,0xec,
0x33,XX,
0x8b,0x45,0x08,
0xeb,0x04,
XX,
0x83,0xc0,0x02,
0x66,0x83,0x38,0x00,
0x75,0xf6,
0x8b,XX,
0x5d,0xc3
};
ULONG addr = MemDbg::findBytes(bytes2, sizeof(bytes2), processStartAddress, processStopAddress);
static int off;
off=8;
if (addr==0){
addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
off=4;
}
if(addr==0)return false;
HookParam hp;
hp.address = addr;
if(off==8)
hp.type = CODEC_UTF16|USING_STRING|NO_CONTEXT|EMBED_ABLE|EMBED_BEFORE_SIMPLE;
else
hp.type = CODEC_UTF16|USING_STRING|EMBED_ABLE|EMBED_BEFORE_SIMPLE;
hp.offset=off;
hp.filter_fun=[](LPVOID data, size_t *size, HookParam *){
auto t=std::wstring((wchar_t*)data,*size/2);
if(all_ascii(t.c_str(),t.size()))return false;
if(t.find(L".ks")!=t.npos ||t.find(L".tjs")!=t.npos ||t.find(L".xp3")!=t.npos || t.find(L"/")!=t.npos||t.find(L"\\")!=t.npos||t[0]==L'@')return false; //脚本路径或文件路径
//if(t.find(L"[\u540d\u524d]")!=t.npos)return false; //[名前],翻译后破坏结构
if(t.find(L"\u8aad\u307f\u8fbc\u307f")!=t.npos)return false; //読み込み
if(t.size()>4&&t.substr(t.size()-4)==L"[np]")t=t.substr(0,t.size()-4);
if(t.size()>4&&t.substr(t.size()-3)==L"[r]")t=t.substr(0,t.size()-3); //揺り籠より天使まで
t=std::regex_replace(t,std::wregex(L"\\[\ruby text=\"(.*?)\"\\]"),L"");
t=std::regex_replace(t,std::wregex(L"\\[ruby text=\"(.*?)\"\\]"),L"");
t=std::regex_replace(t,std::wregex(L"\\[ch text=\"(.*?)\"\\]"),L"$1");
if(std::any_of(t.begin(),t.end(),[](wchar_t c){return (c<=127)&&((c!=L'[')||c!=L']');}))return false;
2024-03-21 17:57:04 +08:00
return write_string_overwrite(data,size,t);
2024-02-07 20:59:24 +08:00
};
hp.hook_after=[](hook_stack*s,void* data, size_t len){
auto t=std::wstring((wchar_t*)s->stack[off/4]);
auto newText =std::wstring((wchar_t*)data,len/2);
if(t.size()>4&&t.substr(t.size()-4)==L"[np]")newText=newText+L"[np]";
if(t.size()>3&&t.substr(t.size()-3)==L"[r]")newText=newText+L"[r]"; //揺り籠より天使まで
wcscpy((wchar_t*)s->stack[off/4],newText.c_str());
} ;
hp.hook_font=F_GetTextExtentPoint32W|F_GetGlyphOutlineW;
return NewHook(hp, "Krkr2wcs");
}
}
bool KiriKiri3Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPWSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
static std::wstring prevText;
if (!*len)
return false;
text[*len/sizeof(wchar_t)] = L'\0'; // clean text
if (!prevText.compare(text))
return false;
prevText = text;
if (cpp_wcsnstr(text, L"[", *len/sizeof(wchar_t))) {
StringCharReplacer(text, len, L"[r]", 3, L' ');
StringFilterBetween(text, len, L"[", 1, L"]\\", 2);
// ruby type 1
StringFilterBetween(text, len, L"[mruby r=", 9, L"\" text=\"", 8); // [mruby r="ゆきみ" text="由紀美"]
// ruby type 2
StringFilterBetween(text, len, L"[ruby text=", 11, L"]", 1); // [ruby text="せんがわ" align="e"][ch text="仙川"]
StringFilter(text, len, L"[ch text=\"", 10); // [ruby text="せんがわ" align="e"][ch text="仙川"]
// ruby type 1-2
StringFilter(text, len, L"\"]", 2);
// end ruby
StringFilter(text, len, L"[heart]", 7);
}
StringCharReplacer(text,len,L"\uff0f",1,L'\n');
if (cpp_wcsnstr(text, L"[", *len/sizeof(wchar_t))) // detect garbage sentence. [ruby text=%r][ch text=%text][macropop]
return false;
return true;
}
bool InsertKiriKiri3Hook()
{
/*
* Sample games:
* https://vndb.org/v16190
* https://vndb.org/v43048
* https://vndb.org/v46112
* https://vndb.org/v20491
* https://vndb.org/v28695
* https://vndb.org/v5549
* https://vndb.org/v28513
* https://vndb.org/v46499
*/
const BYTE bytes[] = {
0x75, 0x09, // jne GAME.EXE+1D5B37
0x8B, 0x85, XX4, // mov eax,[ebp-00000254]
0xFF, 0x40, 0x78 // inc [eax+78]
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("KiriKiri3: pattern not found");
return false;
}
HookParam hp;
hp.address = addr;
hp.offset=get_reg(regs::ecx);
hp.index = 0;
hp.split = get_reg(regs::eax);
hp.split_index = 0;
hp.type = CODEC_UTF16 | USING_STRING | USING_SPLIT;
hp.filter_fun = KiriKiri3Filter;
ConsoleOutput("INSERT KiriKiri3");
return NewHook(hp, "KiriKiri3");
}
2024-03-12 17:02:41 +08:00
bool KiriKiri4Filter(LPVOID data, size_t *size, HookParam *)
{
auto text = reinterpret_cast<LPWSTR>(data);
auto len = reinterpret_cast<size_t *>(size);
if (text[0] == L'[' || text[0] == L'@' || (*len<=2 && text[0] == L' '))
return false;
if (cpp_wcsnstr(text, L"[", *len/sizeof(wchar_t))) {
StringCharReplacer(text, len, L"[r]", 3, L' ');
StringFilterBetween(text, len, L"[", 1, L"]\\", 2);
// ruby type 1
StringFilterBetween(text, len, L"[mruby r=", 9, L"\" text=\"", 8); // [mruby r="ゆきみ" text="由紀美"]
// ruby type 2
StringFilterBetween(text, len, L"[ruby text=", 11, L"]", 1); // [ruby text="せんがわ" align="e"][ch text="仙川"]
2024-03-15 16:08:16 +08:00
StringFilterBetween(text, len, L"[Ruby text", 10, L"]", 1); // [Ruby text = "Sawano"][ch text="沢野"]
2024-03-12 17:02:41 +08:00
StringFilter(text, len, L"[ch text=\"", 10); // [ruby text="せんがわ" align="e"][ch text="仙川"]
// ruby type 1-2
StringFilter(text, len, L"\"]", 2);
// end ruby
StringFilterBetween(text, len, L"[", 1, L"]", 1);
}
return true;
}
bool InsertKiriKiri4Hook()
{
/*
* Sample games:
* https://vndb.org/r114393
* https://vndb.org/v2916
* https://vndb.org/r117083
* https://vndb.org/v3851
* https://vndb.org/v7804
* https://vndb.org/v11123
* https://vndb.org/v18650
2024-03-15 16:08:16 +08:00
* https://vndb.org/v38034
2024-03-12 17:02:41 +08:00
*/
const BYTE bytes[] = {
0xE8, XX4, // call Kansen1._GetExceptDLLinfo+67B <-- hook here
0x8D, 0x45, 0xA4, // lea eax,[ebp-5C]
0xFF, 0x45, 0x9C, // inc [ebp-64]
0xE8, XX4 // call Kansen1.exe+1D561C
};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
ConsoleOutput("KiriKiri4: pattern not found");
return false;
}
HookParam hp = {};
hp.address = addr;
hp.offset = get_reg(regs::edx);
hp.type = NO_CONTEXT | CODEC_UTF16 | USING_STRING;
hp.filter_fun = KiriKiri4Filter;
ConsoleOutput(" INSERT KiriKiri4");
NewHook(hp, "KiriKiri4");
return true;
}
2024-02-07 20:59:24 +08:00
bool KiriKiri::attach_function() {
if (Util::SearchResourceString(L"TVP(KIRIKIRI) Z ")) { // TVP(KIRIKIRI) Z CORE
// jichi 11/24/2014: Disabled that might crash VBH
//if (Util::CheckFile(L"plugin\\KAGParser.dll"))
// InsertKAGParserHook();
//else if (Util::CheckFile(L"plugin\\KAGParserEx.dll"))
// InsertKAGParserExHook();
bool krz=Private::attach(processStartAddress,processStopAddress);
if (InsertKiriKiriZHook()||krz)
return true;
}
bool b1=attachkr2(processStartAddress,processStopAddress);
bool _3=wcslen_wcscpy();
auto _= InsertKiriKiriHook() || InsertKiriKiriZHook()||b1||_3;
2024-03-12 17:02:41 +08:00
return (InsertKiriKiri4Hook()|InsertKiriKiri3Hook())||_;
2024-02-07 20:59:24 +08:00
}