mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-11-30 09:05:39 +08:00
836 lines
36 KiB
C++
836 lines
36 KiB
C++
#include "Unicorn.h"
|
||
/**
|
||
* jichi 9/16/2013: a-unicorn / gesen18
|
||
* See (CaoNiMaGeBi): http://tieba.baidu.com/p/2586681823
|
||
* Pattern: 2bce8bf8
|
||
* 2bce sub ecx,esi ; hook here
|
||
* 8bf8 mov eds,eax
|
||
* 8bd1 mov edx,ecx
|
||
*
|
||
* /HBN-20*0@xxoo
|
||
* - length_offset: 1
|
||
* - off: 4294967260 (0xffffffdc)
|
||
* - type: 1032 (0x408)
|
||
*/
|
||
bool InsertUnicornHook()
|
||
{
|
||
// pattern: 2bce8bf8
|
||
const BYTE bytes[] = {
|
||
0x2b, 0xce, // sub ecx,esi ; hook here
|
||
0x8b, 0xf8 // mov edi,eax
|
||
};
|
||
// enum { addr_offset = 0 };
|
||
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||
if (!addr)
|
||
{
|
||
ConsoleOutput("Unicorn: pattern not exist");
|
||
return false;
|
||
}
|
||
|
||
HookParam hp;
|
||
hp.type = NO_CONTEXT | DATA_INDIRECT;
|
||
hp.offset = get_reg(regs::edi);
|
||
hp.address = addr;
|
||
|
||
// index = SearchPattern(processStartAddress, size,ins, sizeof(ins));
|
||
// GROWL_DWORD2(base, index);
|
||
|
||
ConsoleOutput("INSERT Unicorn");
|
||
return NewHook(hp, "Unicorn");
|
||
}
|
||
namespace
|
||
{ // unnamed
|
||
// A simple but very inefficient implementation for LRU cache.
|
||
|
||
namespace ScenarioHook
|
||
{
|
||
|
||
lru_cache<uint64_t> textCache_(30); // capacity = 30
|
||
|
||
namespace Private
|
||
{
|
||
|
||
class TextStorage
|
||
{
|
||
LPSTR text_;
|
||
std::string oldData_,
|
||
newData_;
|
||
int lineCount_;
|
||
bool saved_;
|
||
|
||
public:
|
||
TextStorage()
|
||
: text_(nullptr), lineCount_(0), saved_(false) {}
|
||
|
||
bool isEmpty() const
|
||
{
|
||
return lineCount_ == 0;
|
||
}
|
||
|
||
void clear()
|
||
{
|
||
text_ = nullptr;
|
||
lineCount_ = 0;
|
||
saved_ = false;
|
||
oldData_.clear();
|
||
newData_.clear();
|
||
}
|
||
|
||
std::string load(char *textAddress);
|
||
void save();
|
||
bool restore(); // recover old text
|
||
} textStorage_;
|
||
|
||
// Hook
|
||
|
||
ULONG textOffset_; // = 0x114;
|
||
|
||
std::string sourceData_;
|
||
LPSTR targetText_;
|
||
bool hookBefore(hook_stack *s, void *data, size_t *len1, uintptr_t *role)
|
||
{
|
||
// Sample game: 三極姫4 ~天華繚乱 天命の恋絵巻~
|
||
// 004B76BB 51 PUSH ECX
|
||
// 004B76BC 8BCB MOV ECX,EBX
|
||
// 004B76BE 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX
|
||
// 004B76C2 E8 89A5FFFF CALL Sangokuh.004B1C50 ; jichi: name caller
|
||
// 004B76C7 E8 44A5FFFF CALL Sangokuh.004B1C10
|
||
// 004B76CC 85C0 TEST EAX,EAX
|
||
// 004B76CE 0F8E F6000000 JLE Sangokuh.004B77CA
|
||
// 004B76D4 8BF8 MOV EDI,EAX
|
||
// 004B76D6 EB 08 JMP SHORT Sangokuh.004B76E0
|
||
// 004B76D8 8DA424 00000000 LEA ESP,DWORD PTR SS:[ESP]
|
||
// 004B76DF 90 NOP
|
||
// 004B76E0 33C0 XOR EAX,EAX
|
||
// 004B76E2 B9 0F000000 MOV ECX,0xF
|
||
// 004B76E7 898C24 FC000000 MOV DWORD PTR SS:[ESP+0xFC],ECX
|
||
// 004B76EE 898424 F8000000 MOV DWORD PTR SS:[ESP+0xF8],EAX
|
||
// 004B76F5 888424 E8000000 MOV BYTE PTR SS:[ESP+0xE8],AL
|
||
// 004B76FC 898C24 18010000 MOV DWORD PTR SS:[ESP+0x118],ECX
|
||
// 004B7703 898424 14010000 MOV DWORD PTR SS:[ESP+0x114],EAX
|
||
// 004B770A 888424 04010000 MOV BYTE PTR SS:[ESP+0x104],AL
|
||
// 004B7711 8D9424 84040000 LEA EDX,DWORD PTR SS:[ESP+0x484]
|
||
// 004B7718 52 PUSH EDX
|
||
// 004B7719 8BCB MOV ECX,EBX
|
||
// 004B771B C68424 AC060000 01 MOV BYTE PTR SS:[ESP+0x6AC],0x1
|
||
// 004B7723 E8 28A5FFFF CALL Sangokuh.004B1C50 ; jichi: scenario caller
|
||
// 004B7728 8D8424 84040000 LEA EAX,DWORD PTR SS:[ESP+0x484]
|
||
// 004B772F 50 PUSH EAX
|
||
// 004B7730 8D8C24 E8000000 LEA ECX,DWORD PTR SS:[ESP+0xE8]
|
||
//
|
||
// Sample game: 天極姫 ~新世大乱・双界の覇者達~
|
||
// Name caller:
|
||
// 0049A83B E8 D0AFFFFF CALL .00495810
|
||
// 0049A840 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX
|
||
// 0049A844 8D8424 EC010000 LEA EAX,DWORD PTR SS:[ESP+0x1EC]
|
||
// 0049A84B 50 PUSH EAX
|
||
// 0049A84C E8 DFAFFFFF CALL .00495830 ; jichi: name caller
|
||
// 0049A851 E8 9AAFFFFF CALL .004957F0
|
||
// 0049A856 BD 0F000000 MOV EBP,0xF
|
||
// 0049A85B 85C0 TEST EAX,EAX
|
||
// 0049A85D 0F8E E3000000 JLE .0049A946
|
||
|
||
auto retaddr = s->stack[0];
|
||
*role = 0;
|
||
// if (retaddr == 0x4b7728)
|
||
if ((*(DWORD *)(retaddr - 5 - 8) & 0x00ffffff) == 0x2484c6) // 004B771B C68424 AC060000 01 MOV BYTE PTR SS:[ESP+0x6AC],0x1
|
||
*role = Engine::ScenarioRole;
|
||
// else if (retaddr == 0x4b76c7)
|
||
else if ((*(DWORD *)(retaddr - 5 - 8) & 0x00ffffff) == 0x0024848d // 0049A844 8D8424 EC010000 LEA EAX,DWORD PTR SS:[ESP+0x1EC]
|
||
|| (*(DWORD *)(retaddr - 5 - 4) & 0x00ffffff) == 0x00244489) // 004B76BE 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX
|
||
*role = Engine::NameRole;
|
||
// else
|
||
// return true;
|
||
if (*role != Engine::ScenarioRole && !textStorage_.isEmpty())
|
||
{
|
||
textStorage_.restore();
|
||
textStorage_.clear();
|
||
}
|
||
if (!*role)
|
||
return false;
|
||
|
||
auto text = (LPSTR) * (DWORD *)(s->ecx + textOffset_); // [ecx+0x114]
|
||
if (!*text || all_ascii(text)) // allspaces is only needed when textstorage is enabled though
|
||
return false;
|
||
|
||
if (!textStorage_.isEmpty())
|
||
{
|
||
textStorage_.restore();
|
||
textStorage_.clear();
|
||
}
|
||
|
||
bool textStorageEnabled = *role == Engine::ScenarioRole && Engine::isAddressWritable(text);
|
||
std::string oldData;
|
||
if (textStorageEnabled)
|
||
oldData = textStorage_.load(text);
|
||
else
|
||
oldData = text;
|
||
|
||
if (*role == Engine::NameRole)
|
||
strReplace(oldData, "\x81\x40", "");
|
||
// oldData.replace("\x81\x40", ""); // remove spaces in the middle of names
|
||
|
||
return write_string_overwrite(data, len1, oldData);
|
||
}
|
||
void hookafter2(hook_stack *s, void *data, size_t len)
|
||
{
|
||
|
||
auto newData = std::string((char *)data, len);
|
||
auto retaddr = s->stack[0];
|
||
int role = 0;
|
||
// if (retaddr == 0x4b7728)
|
||
if ((*(DWORD *)(retaddr - 5 - 8) & 0x00ffffff) == 0x2484c6) // 004B771B C68424 AC060000 01 MOV BYTE PTR SS:[ESP+0x6AC],0x1
|
||
role = Engine::ScenarioRole;
|
||
// else if (retaddr == 0x4b76c7)
|
||
else if ((*(DWORD *)(retaddr - 5 - 8) & 0x00ffffff) == 0x0024848d // 0049A844 8D8424 EC010000 LEA EAX,DWORD PTR SS:[ESP+0x1EC]
|
||
|| (*(DWORD *)(retaddr - 5 - 4) & 0x00ffffff) == 0x00244489) // 004B76BE 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX
|
||
role = Engine::NameRole;
|
||
// else
|
||
// return true;
|
||
if (role != Engine::ScenarioRole && !textStorage_.isEmpty())
|
||
{
|
||
textStorage_.restore();
|
||
textStorage_.clear();
|
||
}
|
||
if (!role)
|
||
return;
|
||
auto text = (LPSTR) * (DWORD *)(s->ecx + textOffset_); // [ecx+0x114]
|
||
if (!*text || all_ascii(text)) // allspaces is only needed when textstorage is enabled though
|
||
return;
|
||
if (!textStorage_.isEmpty())
|
||
{
|
||
textStorage_.restore();
|
||
textStorage_.clear();
|
||
}
|
||
bool textStorageEnabled = role == Engine::ScenarioRole && Engine::isAddressWritable(text);
|
||
std::string oldData;
|
||
if (textStorageEnabled)
|
||
oldData = textStorage_.load(text);
|
||
else
|
||
oldData = text;
|
||
if (role == Engine::NameRole)
|
||
strReplace(oldData, "\x81\x40", "");
|
||
// oldData.replace("\x81\x40", ""); // remove spaces in the middle of names
|
||
if (oldData == newData)
|
||
{
|
||
if (textStorageEnabled)
|
||
textStorage_.clear();
|
||
return;
|
||
}
|
||
if (textStorageEnabled)
|
||
textStorage_.save();
|
||
sourceData_ = newData;
|
||
targetText_ = (LPSTR)s->stack[1]; // arg1
|
||
textCache_.put(simplehash::hashByteArraySTD(newData));
|
||
}
|
||
bool hookAfter(hook_stack *s, void *data, size_t *len1, uintptr_t *role)
|
||
{
|
||
if (targetText_)
|
||
{
|
||
::strcpy(targetText_, sourceData_.c_str());
|
||
targetText_ = nullptr;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
} // namespace Private
|
||
|
||
/**
|
||
* Sample text
|
||
*
|
||
* Sample game: 三極姫4 ~天華繚乱 天命の恋絵巻~
|
||
*
|
||
* 01FE881C 81 40 92 6A 81 40 00 01 81 75 82 BB 81 41 82 BB 男 .「そ、そ
|
||
* 01FE882C 82 F1 82 C8 81 63 81 63 82 BB 82 EA 82 AA 8D C5 んな……それが最
|
||
* 01FE883C 8C E3 82 CC 90 48 97 BF 82 C8 82 CC 82 C9 81 63 後の食料なのに…
|
||
* 01FE884C 81 63 81 49 81 76 00 00 00 00 FF FF FF FF FF FF …!」....
|
||
* 01FE885C FF FF 11 19 00 1B 00 0F 19 00 1D 00 03 00 00 00 .......
|
||
* 01FE886C 03 00 00 00 00 01 97 AA 92 44 81 5C 81 5C 00 00 ....略奪――..
|
||
*
|
||
* 01FE8758 01 00 00 00 01 00 00 00 93 90 81 40 91 AF 00 02 ......盗 賊.
|
||
* 01FE8768 81 75 82 C7 82 A4 82 B9 82 B1 82 EA 82 C1 82 DB 「どうせこれっぽ
|
||
* 01FE8778 82 C1 82 BF 82 CC 90 48 97 BF 82 AA 82 A0 82 C1 っちの食料があっ
|
||
* 01FE8788 82 BD 82 C6 82 B1 82 EB 82 C5 81 41 8B 51 82 A6 たところで、飢え
|
||
* 01FE8798 82 C4 8E 80 00 00 00 00 FF FF FF FF FF FF FF FF て死....
|
||
* 01FE87A8 0A 82 CA 82 CC 82 CD 93 AF 82 B6 82 BE 82 EB 81 .ぬのは同じだろ・
|
||
* 01FE87B8 49 81 40 82 D9 82 E7 91 53 95 94 82 E6 82 B1 82 I ほら全部よこ・
|
||
* 01FE87C8 B9 82 C1 81 49 81 76 00 00 00 00 FF FF FF FF FF ケっ!」....
|
||
* 01FE87D8 FF FF FF 11 19 00 16 00 19 19 00 18 00 32 00 00 ....2..
|
||
* 01FE87E8 00 44 61 74 61 5C 76 6F 69 63 65 5C 65 74 63 5C .Data\voice\etc\
|
||
* 01FE87F8 65 74 63 4A 5F 70 63 41 5F 30 30 30 31 2E 76 6F etcJ_pcA_0001.vo
|
||
* 01FE8808 69 00 00 00 00 00 00 0F 19 00 19 00 02 00 00 00 i...........
|
||
*
|
||
* Sample game: 戦極姫6
|
||
*
|
||
* 023AF0E8 82 BB 82 CC 90 BA 82 F0 95 B7 82 AB 81 41 90 B0 その声を聞き、晴
|
||
* 023AF0F8 90 4D 82 CD 82 B7 82 C1 82 C6 95 5C 8F EE 82 F0 信はすっと表情を
|
||
* 023AF108 88 F8 82 AB 92 F7 82 DF 82 BD 81 42 00 00 00 00 引き締めた。....
|
||
* 023AF118 BE BE BE FF FF FF FF FF 11 0E 00 1E 00 0F 0E 00 セセセ...
|
||
* 023AF128 20 00 03 00 00 00 03 00 00 00 95 90 93 63 90 4D .......武田信
|
||
* 023AF138 94 C9 00 01 81 75 90 4D 8C D5 97 6C 82 CD 81 41 繁.「信虎様は、
|
||
* 023AF148 97 5C 92 E8 82 C7 82 A8 82 E8 82 BE 82 BB 82 A4 予定どおりだそう
|
||
* 023AF158 82 BE 81 76 00 00 00 00 BE BE BE FF FF FF FF FF だ」....セセセ
|
||
* 023AF168 11 0E 00 22 00 0F 0E 00 24 00 04 00 00 00 04 00 ."..$.....
|
||
* 023AF178 00 00 00 02 95 94 89 AE 82 C9 82 CD 82 A2 82 C1 ...部屋にはいっ
|
||
* 023AF188 82 C4 82 AB 82 BD 90 4D 94 C9 82 CD 81 41 90 B0 てきた信繁は、晴
|
||
* 023AF198 90 4D 82 CC 91 4F 82 D6 82 C6 8D 98 82 F0 82 A8 信の前へと腰をお
|
||
* 023AF1A8 82 EB 82 B5 8C FC 82 A9 00 00 00 00 BE BE BE FF ろし向か....セセセ
|
||
* 023AF1B8 FF FF FF FF 0A 82 A2 82 A0 82 A4 81 42 00 00 00 .いあう。...
|
||
* 023AF1C8 00 BE BE BE FF FF FF FF FF 11 0E 00 27 00 01 0E .セセセ.'.
|
||
* 023AF1D8 00 2A 00 84 D9 07 00 02 00 00 00 E8 18 00 00 01 .*.・....・..
|
||
* 023AF1E8 60 00 00 00 E9 18 00 00 01 5B 00 00 00 19 0E 00 `...・..[....
|
||
* 023AF1F8 2C 00 06 00 00 00 44 61 74 61 5C 76 6F 69 63 65 ,....Data\voice
|
||
* 023AF208 5C 73 69 6E 67 65 6E 5C 73 69 6E 67 65 6E 5F 30 \singen\singen_0
|
||
* 023AF218 30 34 33 2E 76 6F 69 00 00 00 00 00 00 0F 0E 00 043.voi.......
|
||
*
|
||
* Sample game: 天極姫 ~新世大乱・双界の覇者達~
|
||
* 0211F8AA 82 91 80 82 BD 82 BF 82 CD 82 B1 82 CC 90 A2 8A qたちはこの世・
|
||
* 0211F8BA 45 82 C9 93 CB 91 52 8C BB 82 EA 82 BD 81 42 82 Eに突然現れた。・
|
||
* 0211F8CA BB 82 B5 82 C4 82 B1 82 B1 82 CC 96 AF 82 BD 82 サしてここの民た・
|
||
* 0211F8DA BF 82 CD 00 00 00 00 BE BE BE FF FF FF FF FF 0A ソは....セセセ.
|
||
* 0211F8EA 91 82 91 80 82 BD 82 BF 82 F0 81 41 92 B7 82 AD 曹操たちを、長く
|
||
* 0211F8FA 91 B1 82 A2 82 BD 90 ED 97 90 82 F0 8F 49 82 ED 続いた戦乱を終わ
|
||
* 0211F90A 82 E7 82 B9 82 E9 89 70 97 59 82 C6 81 41 96 7B らせる英雄と、本
|
||
* 0211F91A 8B 43 82 C5 00 00 00 00 BE BE BE FF FF FF FF FF 気で....セセセ
|
||
* 0211F92A 0A 90 4D 82 B6 82 C4 82 A2 82 E9 82 C6 82 A2 82 .信じているとい・
|
||
* 0211F93A A4 82 B1 82 C6 82 BE 82 C1 82 BD 81 42 00 00 00 、ことだった。...
|
||
*/
|
||
// 三極姫4: 00 00 00 00 ff ff ff ff ff ff ff ff 0a
|
||
// 戦極姫6: 00 00 00 00 be be be ff ff ff ff ff 0a
|
||
// enum { TextSeparatorSize = 12 };
|
||
static inline bool isTextSeparator(LPCSTR text)
|
||
{
|
||
// return 0 == ::memcmp(p, "\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x0a", 13);
|
||
return 0 == ::memcmp(text, "\x00\x00\x00\x00", 4) && 0 == ::memcmp(text + 8, "\xff\xff\xff\xff\x0a", 5);
|
||
}
|
||
std::string Private::TextStorage::load(char *text)
|
||
{
|
||
text_ = text;
|
||
std::string data = text;
|
||
lineCount_ = 1;
|
||
LPCSTR p = text + ::strlen(text);
|
||
for (; isTextSeparator(p); p += ::strlen(p))
|
||
{
|
||
lineCount_++;
|
||
p += 12;
|
||
data.append(p);
|
||
}
|
||
oldData_ = std::string(text, p - text);
|
||
return data;
|
||
}
|
||
|
||
void Private::TextStorage::save()
|
||
{
|
||
if (lineCount_ <= 1)
|
||
return;
|
||
LPSTR p = text_ + ::strlen(text_);
|
||
while (isTextSeparator(p))
|
||
{
|
||
p += 12 + 1; // +1 for the extra 0xa
|
||
if (size_t size = ::strlen(p))
|
||
{
|
||
::memset(p, ' ', size);
|
||
p += size;
|
||
}
|
||
}
|
||
newData_ = std::string(text_, p - text_);
|
||
}
|
||
|
||
bool Private::TextStorage::restore()
|
||
{
|
||
if (!saved_ || !Engine::isAddressWritable(text_, oldData_.size()) || ::memcmp(text_, newData_.c_str(), newData_.size()))
|
||
return false;
|
||
if (::memcmp(text_, oldData_.c_str(), oldData_.size()))
|
||
::memcpy(text_, oldData_.c_str(), oldData_.size());
|
||
saved_ = false;
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* Sample game: 三極姫4 ~天華繚乱 天命の恋絵巻~
|
||
*
|
||
* Function found by hardware breakpoint scenario text.
|
||
*
|
||
* The memory copy function:
|
||
* 004B1C4D CC INT3
|
||
* 004B1C4E CC INT3
|
||
* 004B1C4F CC INT3
|
||
* 004B1C50 8B81 14010000 MOV EAX,DWORD PTR DS:[ECX+0x114] ; jichi: source text in eax, beforeAddress
|
||
* 004B1C56 8B5424 04 MOV EDX,DWORD PTR SS:[ESP+0x4] ; jichi: target address in edx
|
||
* 004B1C5A 56 PUSH ESI
|
||
* 004B1C5B 33F6 XOR ESI,ESI
|
||
* 004B1C5D 8038 00 CMP BYTE PTR DS:[EAX],0x0
|
||
* 004B1C60 74 1D JE SHORT Sangokuh.004B1C7F
|
||
* 004B1C62 8B81 14010000 MOV EAX,DWORD PTR DS:[ECX+0x114]
|
||
* 004B1C68 8A00 MOV AL,BYTE PTR DS:[EAX]
|
||
* 004B1C6A 8802 MOV BYTE PTR DS:[EDX],AL
|
||
* 004B1C6C FF81 14010000 INC DWORD PTR DS:[ECX+0x114]
|
||
* 004B1C72 8B81 14010000 MOV EAX,DWORD PTR DS:[ECX+0x114]
|
||
* 004B1C78 42 INC EDX
|
||
* 004B1C79 46 INC ESI
|
||
* 004B1C7A 8038 00 CMP BYTE PTR DS:[EAX],0x0
|
||
* 004B1C7D ^75 E3 JNZ SHORT Sangokuh.004B1C62
|
||
* 004B1C7F 8B81 14010000 MOV EAX,DWORD PTR DS:[ECX+0x114]
|
||
* 004B1C85 8A00 MOV AL,BYTE PTR DS:[EAX]
|
||
* 004B1C87 8802 MOV BYTE PTR DS:[EDX],AL
|
||
* 004B1C89 FF81 14010000 INC DWORD PTR DS:[ECX+0x114]
|
||
* 004B1C8F 8BC6 MOV EAX,ESI ; jichi: copied count
|
||
* 004B1C91 5E POP ESI
|
||
* 004B1C92 C2 0400 RETN 0x4 ; jichi: afterAddress
|
||
* 004B1C95 CC INT3
|
||
* 004B1C96 CC INT3
|
||
* 004B1C97 CC INT3
|
||
*
|
||
* The very large caller function:
|
||
*
|
||
* 004B76AB 894424 1C MOV DWORD PTR SS:[ESP+0x1C],EAX
|
||
* 004B76AF E8 7CA5FFFF CALL Sangokuh.004B1C30
|
||
* 004B76B4 8D8C24 7C030000 LEA ECX,DWORD PTR SS:[ESP+0x37C]
|
||
* 004B76BB 51 PUSH ECX
|
||
* 004B76BC 8BCB MOV ECX,EBX
|
||
* 004B76BE 894424 14 MOV DWORD PTR SS:[ESP+0x14],EAX
|
||
* 004B76C2 E8 89A5FFFF CALL Sangokuh.004B1C50 ; jichi: name caller
|
||
* 004B76C7 E8 44A5FFFF CALL Sangokuh.004B1C10
|
||
* 004B76CC 85C0 TEST EAX,EAX
|
||
* 004B76CE 0F8E F6000000 JLE Sangokuh.004B77CA
|
||
* 004B76D4 8BF8 MOV EDI,EAX
|
||
* 004B76D6 EB 08 JMP SHORT Sangokuh.004B76E0
|
||
* 004B76D8 8DA424 00000000 LEA ESP,DWORD PTR SS:[ESP]
|
||
* 004B76DF 90 NOP
|
||
* 004B76E0 33C0 XOR EAX,EAX
|
||
* 004B76E2 B9 0F000000 MOV ECX,0xF
|
||
* 004B76E7 898C24 FC000000 MOV DWORD PTR SS:[ESP+0xFC],ECX
|
||
* 004B76EE 898424 F8000000 MOV DWORD PTR SS:[ESP+0xF8],EAX
|
||
* 004B76F5 888424 E8000000 MOV BYTE PTR SS:[ESP+0xE8],AL
|
||
* 004B76FC 898C24 18010000 MOV DWORD PTR SS:[ESP+0x118],ECX
|
||
* 004B7703 898424 14010000 MOV DWORD PTR SS:[ESP+0x114],EAX
|
||
* 004B770A 888424 04010000 MOV BYTE PTR SS:[ESP+0x104],AL
|
||
* 004B7711 8D9424 84040000 LEA EDX,DWORD PTR SS:[ESP+0x484]
|
||
* 004B7718 52 PUSH EDX
|
||
* 004B7719 8BCB MOV ECX,EBX
|
||
* 004B771B C68424 AC060000 01 MOV BYTE PTR SS:[ESP+0x6AC],0x1
|
||
* 004B7723 E8 28A5FFFF CALL Sangokuh.004B1C50 ; jichi: scenario caller
|
||
* 004B7728 8D8424 84040000 LEA EAX,DWORD PTR SS:[ESP+0x484]
|
||
* 004B772F 50 PUSH EAX
|
||
* 004B7730 8D8C24 E8000000 LEA ECX,DWORD PTR SS:[ESP+0xE8]
|
||
*
|
||
* Sample game: 戦極姫6
|
||
* 004A6C88 CC INT3
|
||
* 004A6C89 CC INT3
|
||
* 004A6C8A CC INT3
|
||
* 004A6C8B CC INT3
|
||
* 004A6C8C CC INT3
|
||
* 004A6C8D CC INT3
|
||
* 004A6C8E CC INT3
|
||
* 004A6C8F CC INT3
|
||
* 004A6C90 8B81 14010000 MOV EAX,DWORD PTR DS:[ECX+0x114]
|
||
* 004A6C96 8B5424 04 MOV EDX,DWORD PTR SS:[ESP+0x4]
|
||
* 004A6C9A 56 PUSH ESI
|
||
* 004A6C9B 33F6 XOR ESI,ESI
|
||
* 004A6C9D 8038 00 CMP BYTE PTR DS:[EAX],0x0
|
||
* 004A6CA0 74 1D JE SHORT .004A6CBF
|
||
* 004A6CA2 8B81 14010000 MOV EAX,DWORD PTR DS:[ECX+0x114]
|
||
* 004A6CA8 8A00 MOV AL,BYTE PTR DS:[EAX]
|
||
* 004A6CAA 8802 MOV BYTE PTR DS:[EDX],AL
|
||
* 004A6CAC FF81 14010000 INC DWORD PTR DS:[ECX+0x114]
|
||
* 004A6CB2 8B81 14010000 MOV EAX,DWORD PTR DS:[ECX+0x114]
|
||
* 004A6CB8 42 INC EDX
|
||
* 004A6CB9 46 INC ESI
|
||
* 004A6CBA 8038 00 CMP BYTE PTR DS:[EAX],0x0
|
||
* 004A6CBD ^75 E3 JNZ SHORT .004A6CA2
|
||
* 004A6CBF 8B81 14010000 MOV EAX,DWORD PTR DS:[ECX+0x114]
|
||
* 004A6CC5 8A00 MOV AL,BYTE PTR DS:[EAX]
|
||
* 004A6CC7 8802 MOV BYTE PTR DS:[EDX],AL
|
||
* 004A6CC9 FF81 14010000 INC DWORD PTR DS:[ECX+0x114]
|
||
* 004A6CCF 8BC6 MOV EAX,ESI
|
||
* 004A6CD1 5E POP ESI
|
||
* 004A6CD2 C2 0400 RETN 0x4
|
||
* 004A6CD5 CC INT3
|
||
* 004A6CD6 CC INT3
|
||
* 004A6CD7 CC INT3
|
||
* 004A6CD8 CC INT3
|
||
* 004A6CD9 CC INT3
|
||
*/
|
||
bool attach(ULONG startAddress, ULONG stopAddress)
|
||
{
|
||
ULONG beforeAddress;
|
||
{
|
||
const uint8_t bytes[] = {
|
||
0x8b, 0x81, XX4, // 004b1c50 8b81 14010000 mov eax,dword ptr ds:[ecx+0x114] ; jichi: source text in eax
|
||
0x8b, 0x54, 0x24, 0x04, // 004b1c56 8b5424 04 mov edx,dword ptr ss:[esp+0x4] ; jichi: target address in edx
|
||
0x56, // 004b1c5a 56 push esi
|
||
0x33, 0xf6, // 004b1c5b 33f6 xor esi,esi
|
||
0x80, 0x38, 0x00 // 004b1c5d 8038 00 cmp byte ptr ds:[eax],0x0
|
||
};
|
||
beforeAddress = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
|
||
if (!beforeAddress)
|
||
return false;
|
||
}
|
||
|
||
ULONG afterAddress;
|
||
{
|
||
// 004B1C92 C2 0400 RETN 0x4 ; jichi: afterAddress
|
||
// 004B1C95 CC INT3
|
||
DWORD bytes = 0xcc0004c2;
|
||
afterAddress = MemDbg::findBytes(&bytes, sizeof(bytes), beforeAddress, stopAddress);
|
||
if (!afterAddress || afterAddress - beforeAddress > 0x200) // should within 0x42
|
||
return false;
|
||
}
|
||
|
||
// 004b1c50 8b81 14010000 mov eax,dword ptr ds:[ecx+0x114] ; jichi: source text in eax
|
||
Private::textOffset_ = *(DWORD *)(beforeAddress + 2); // 0x114
|
||
HookParam hp;
|
||
hp.address = beforeAddress;
|
||
hp.hook_before = Private::hookBefore;
|
||
hp.hook_after = Private::hookafter2;
|
||
hp.offset = get_stack(1);
|
||
hp.newlineseperator = L"\\n";
|
||
hp.type = EMBED_ABLE | EMBED_DYNA_SJIS;
|
||
hp.hook_font = F_GetGlyphOutlineA;
|
||
auto suc = NewHook(hp, "EMbedUnicorn");
|
||
hp.address = afterAddress;
|
||
hp.type = HOOK_EMPTY | EMBED_ABLE;
|
||
hp.hook_before = Private::hookAfter;
|
||
suc |= NewHook(hp, "EMbedUnicorn");
|
||
return suc;
|
||
}
|
||
|
||
} // namespace ScenarioHook
|
||
|
||
namespace OtherHook
|
||
{
|
||
namespace Private
|
||
{
|
||
|
||
// bool isSkippedText(LPCSTR text)
|
||
//{
|
||
// return 0 == ::strcmp(text, "\x82\x6c\x82\x72\x20\x83\x53\x83\x56\x83\x62\x83\x4e"); // "MS ゴシック"
|
||
// }
|
||
|
||
/**
|
||
* Sample game: 戦極姫6
|
||
*
|
||
*/
|
||
bool hookBefore(hook_stack *s, void *data, size_t *len, uintptr_t *role)
|
||
{
|
||
static std::string data_;
|
||
auto retaddr = s->stack[0];
|
||
// 0052FDCE 83C4 0C ADD ESP,0xC
|
||
// 0052FDD1 ^EB C1 JMP SHORT .0052FD94
|
||
// if (*(DWORD *)retaddr != 0xeb0cc483)
|
||
// return true;
|
||
// retaddr = s->stack[7]; // parent caller
|
||
|
||
// Scenario/name/other threads to skip:
|
||
// - 0x404062 // there are so many other texts in this thread
|
||
//
|
||
// Other thread to keep:
|
||
// - 0x4769f8: message
|
||
// - 0x4135ba: in-game text that split into lines
|
||
//
|
||
// 004769E9 2BC7 SUB EAX,EDI
|
||
// 004769EB 50 PUSH EAX
|
||
// 004769EC 51 PUSH ECX
|
||
// 004769ED 8D8E C4080000 LEA ECX,DWORD PTR DS:[ESI+0x8C4]
|
||
// 004769F3 E8 B8D1F8FF CALL .00403BB0 ; jichi; message
|
||
// 004769F8 D9EE FLDZ
|
||
// 004769FA 8B6C24 18 MOV EBP,DWORD PTR SS:[ESP+0x18]
|
||
// 004769FE D996 04090000 FST DWORD PTR DS:[ESI+0x904]
|
||
//
|
||
// 004135B1 52 PUSH EDX
|
||
// 004135B2 8D4E 3C LEA ECX,DWORD PTR DS:[ESI+0x3C]
|
||
// 004135B5 E8 F605FFFF CALL .00403BB0 ; jichi: in-game caller
|
||
// 004135BA EB 08 JMP SHORT .004135C4
|
||
// 004135BC 8D4E 3C LEA ECX,DWORD PTR DS:[ESI+0x3C]
|
||
// if (retaddr != 0x4769f8 && retaddr != 0x4135ba)
|
||
// return true;
|
||
switch (*(WORD *)retaddr)
|
||
{
|
||
case 0xeed9: // 004769F8 D9EE FLDZ
|
||
case 0x08eb: // 004135BA EB 08 JMP SHORT .004135C4
|
||
break;
|
||
default:
|
||
return false;
|
||
}
|
||
auto text = (LPCSTR)s->stack[1]; // arg1
|
||
int size = s->stack[2]; // arg2
|
||
if (!text || size <= 2 // avoid painting individual character
|
||
|| ::strlen(text) != size || all_ascii(text) || ScenarioHook::textCache_.exists(simplehash::hashCharArray(text)))
|
||
//|| !q->isTextDecodable(text)) // avoid re-translation
|
||
//|| isascii(text[::strlen(text) - 2])
|
||
//|| isSkippedText(text))
|
||
return false;
|
||
enum
|
||
{
|
||
role = Engine::OtherRole
|
||
};
|
||
|
||
return write_string_overwrite(data, len, text);
|
||
/* //oldData.replace("\\n", "\n"); // Remove new line. FIXME: automatically adjust line width
|
||
std::string newData = EngineController::instance()->dispatchTextASTD(oldData, role, retaddr);
|
||
if (newData == oldData)
|
||
return true;
|
||
data_ = newData;
|
||
s->stack[1] = (ULONG)data_.c_str();
|
||
s->stack[2] = data_.size();
|
||
return true;*/
|
||
}
|
||
|
||
void hookafter(hook_stack *s, void *data, size_t len)
|
||
{
|
||
|
||
auto newData = std::string((char *)data, len);
|
||
static std::string data_;
|
||
data_ = newData;
|
||
s->stack[1] = (ULONG)data_.c_str();
|
||
s->stack[2] = data_.size();
|
||
}
|
||
} // namespace Private
|
||
|
||
/**
|
||
* Sample game: 戦極姫6
|
||
* Function found by debugging caller of GetGlyphOutlineA.
|
||
* 0052F2DC CC INT3
|
||
* 0052F2DD CC INT3
|
||
* 0052F2DE CC INT3
|
||
* 0052F2DF CC INT3
|
||
* 0052F2E0 55 PUSH EBP
|
||
* 0052F2E1 8BEC MOV EBP,ESP
|
||
* 0052F2E3 57 PUSH EDI
|
||
* 0052F2E4 56 PUSH ESI
|
||
* 0052F2E5 8B75 0C MOV ESI,DWORD PTR SS:[EBP+0xC] ; jichi: arg2, source text
|
||
* 0052F2E8 8B4D 10 MOV ECX,DWORD PTR SS:[EBP+0x10] ; jichi: arg3, count?
|
||
* 0052F2EB 8B7D 08 MOV EDI,DWORD PTR SS:[EBP+0x8] ; jichi: arg1, target location
|
||
* 0052F2EE 8BC1 MOV EAX,ECX
|
||
* 0052F2F0 8BD1 MOV EDX,ECX
|
||
* 0052F2F2 03C6 ADD EAX,ESI
|
||
* 0052F2F4 3BFE CMP EDI,ESI
|
||
* 0052F2F6 76 08 JBE SHORT .0052F300
|
||
* 0052F2F8 3BF8 CMP EDI,EAX
|
||
* 0052F2FA 0F82 A4010000 JB .0052F4A4
|
||
* 0052F300 81F9 00010000 CMP ECX,0x100 ; jichi: 0x100 is the threshold
|
||
* 0052F306 72 1F JB SHORT .0052F327
|
||
* 0052F308 833D 6472D800 00 CMP DWORD PTR DS:[0xD87264],0x0
|
||
* 0052F30F 74 16 JE SHORT .0052F327
|
||
* 0052F311 57 PUSH EDI
|
||
* 0052F312 56 PUSH ESI
|
||
* 0052F313 83E7 0F AND EDI,0xF
|
||
* 0052F316 83E6 0F AND ESI,0xF
|
||
* 0052F319 3BFE CMP EDI,ESI
|
||
* 0052F31B 5E POP ESI
|
||
* 0052F31C 5F POP EDI
|
||
* 0052F31D 75 08 JNZ SHORT .0052F327
|
||
* 0052F31F 5E POP ESI
|
||
* 0052F320 5F POP EDI
|
||
* 0052F321 5D POP EBP
|
||
* 0052F322 E9 7C5F0000 JMP .005352A3
|
||
* 0052F327 F7C7 03000000 TEST EDI,0x3
|
||
* 0052F32D 75 15 JNZ SHORT .0052F344
|
||
* 0052F32F C1E9 02 SHR ECX,0x2
|
||
* 0052F332 83E2 03 AND EDX,0x3
|
||
* 0052F335 83F9 08 CMP ECX,0x8
|
||
* 0052F338 72 2A JB SHORT .0052F364
|
||
* 0052F33A F3:A5 REP MOVS DWORD PTR ES:[EDI],DWORD PTR DS>
|
||
* 0052F33C FF2495 54F45200 JMP DWORD PTR DS:[EDX*4+0x52F454]
|
||
* 0052F343 90 NOP
|
||
*
|
||
* Here's its parent parent caller:
|
||
* - arg1: jichi: source text
|
||
* - arg2: jichi: source size
|
||
*
|
||
* 00403BAB CC INT3
|
||
* 00403BAC CC INT3
|
||
* 00403BAD CC INT3
|
||
* 00403BAE CC INT3
|
||
* 00403BAF CC INT3
|
||
* 00403BB0 55 PUSH EBP
|
||
* 00403BB1 8B6C24 08 MOV EBP,DWORD PTR SS:[ESP+0x8]
|
||
* 00403BB5 56 PUSH ESI
|
||
* 00403BB6 57 PUSH EDI
|
||
* 00403BB7 8BF1 MOV ESI,ECX
|
||
* 00403BB9 85ED TEST EBP,EBP
|
||
* 00403BBB 74 46 JE SHORT .00403C03
|
||
* 00403BBD 8B56 18 MOV EDX,DWORD PTR DS:[ESI+0x18]
|
||
* 00403BC0 8D46 04 LEA EAX,DWORD PTR DS:[ESI+0x4]
|
||
* 00403BC3 83FA 10 CMP EDX,0x10
|
||
* 00403BC6 72 04 JB SHORT .00403BCC
|
||
* 00403BC8 8B08 MOV ECX,DWORD PTR DS:[EAX]
|
||
* 00403BCA EB 02 JMP SHORT .00403BCE
|
||
* 00403BCC 8BC8 MOV ECX,EAX
|
||
* 00403BCE 3BE9 CMP EBP,ECX
|
||
* 00403BD0 72 31 JB SHORT .00403C03
|
||
* 00403BD2 83FA 10 CMP EDX,0x10
|
||
* 00403BD5 72 04 JB SHORT .00403BDB
|
||
* 00403BD7 8B08 MOV ECX,DWORD PTR DS:[EAX]
|
||
* 00403BD9 EB 02 JMP SHORT .00403BDD
|
||
* 00403BDB 8BC8 MOV ECX,EAX
|
||
* 00403BDD 8B7E 14 MOV EDI,DWORD PTR DS:[ESI+0x14]
|
||
* 00403BE0 03F9 ADD EDI,ECX
|
||
* 00403BE2 3BFD CMP EDI,EBP
|
||
* 00403BE4 76 1D JBE SHORT .00403C03
|
||
* 00403BE6 83FA 10 CMP EDX,0x10
|
||
* 00403BE9 72 02 JB SHORT .00403BED
|
||
* 00403BEB 8B00 MOV EAX,DWORD PTR DS:[EAX]
|
||
* 00403BED 8B4C24 14 MOV ECX,DWORD PTR SS:[ESP+0x14]
|
||
* 00403BF1 51 PUSH ECX
|
||
* 00403BF2 2BE8 SUB EBP,EAX
|
||
* 00403BF4 55 PUSH EBP
|
||
* 00403BF5 56 PUSH ESI
|
||
* 00403BF6 8BCE MOV ECX,ESI
|
||
* 00403BF8 E8 D3FEFFFF CALL .00403AD0
|
||
* 00403BFD 5F POP EDI
|
||
* 00403BFE 5E POP ESI
|
||
* 00403BFF 5D POP EBP
|
||
* 00403C00 C2 0800 RETN 0x8
|
||
* 00403C03 8B7C24 14 MOV EDI,DWORD PTR SS:[ESP+0x14]
|
||
* 00403C07 83FF FE CMP EDI,-0x2
|
||
* 00403C0A 76 05 JBE SHORT .00403C11
|
||
* 00403C0C E8 B94F1500 CALL .00558BCA
|
||
* 00403C11 8B46 18 MOV EAX,DWORD PTR DS:[ESI+0x18]
|
||
* 00403C14 3BC7 CMP EAX,EDI
|
||
* 00403C16 73 20 JNB SHORT .00403C38
|
||
* 00403C18 8B56 14 MOV EDX,DWORD PTR DS:[ESI+0x14]
|
||
* 00403C1B 52 PUSH EDX
|
||
* 00403C1C 57 PUSH EDI
|
||
* 00403C1D 8BCE MOV ECX,ESI
|
||
* 00403C1F E8 5CFDFFFF CALL .00403980
|
||
* 00403C24 85FF TEST EDI,EDI
|
||
* 00403C26 76 56 JBE SHORT .00403C7E
|
||
* 00403C28 8B4E 18 MOV ECX,DWORD PTR DS:[ESI+0x18]
|
||
* 00403C2B 53 PUSH EBX
|
||
* 00403C2C 8D5E 04 LEA EBX,DWORD PTR DS:[ESI+0x4]
|
||
* 00403C2F 83F9 10 CMP ECX,0x10
|
||
* 00403C32 72 2C JB SHORT .00403C60
|
||
* 00403C34 8B03 MOV EAX,DWORD PTR DS:[EBX]
|
||
* 00403C36 EB 2A JMP SHORT .00403C62
|
||
* 00403C38 85FF TEST EDI,EDI
|
||
* 00403C3A ^75 EA JNZ SHORT .00403C26
|
||
* 00403C3C 897E 14 MOV DWORD PTR DS:[ESI+0x14],EDI
|
||
* 00403C3F 83F8 10 CMP EAX,0x10
|
||
* 00403C42 72 0E JB SHORT .00403C52
|
||
* 00403C44 8B46 04 MOV EAX,DWORD PTR DS:[ESI+0x4]
|
||
* 00403C47 5F POP EDI
|
||
* 00403C48 C600 00 MOV BYTE PTR DS:[EAX],0x0
|
||
* 00403C4B 8BC6 MOV EAX,ESI
|
||
* 00403C4D 5E POP ESI
|
||
* 00403C4E 5D POP EBP
|
||
* 00403C4F C2 0800 RETN 0x8
|
||
* 00403C52 8D46 04 LEA EAX,DWORD PTR DS:[ESI+0x4]
|
||
* 00403C55 5F POP EDI
|
||
* 00403C56 C600 00 MOV BYTE PTR DS:[EAX],0x0
|
||
* 00403C59 8BC6 MOV EAX,ESI
|
||
* 00403C5B 5E POP ESI
|
||
* 00403C5C 5D POP EBP
|
||
* 00403C5D C2 0800 RETN 0x8
|
||
* 00403C60 8BC3 MOV EAX,EBX
|
||
* 00403C62 57 PUSH EDI
|
||
* 00403C63 55 PUSH EBP
|
||
* 00403C64 51 PUSH ECX
|
||
* 00403C65 50 PUSH EAX
|
||
* 00403C66 E8 19C11200 CALL .0052FD84 ; jichi: actual paint function
|
||
* 00403C6B 83C4 10 ADD ESP,0x10
|
||
* 00403C6E 837E 18 10 CMP DWORD PTR DS:[ESI+0x18],0x10
|
||
* 00403C72 897E 14 MOV DWORD PTR DS:[ESI+0x14],EDI
|
||
* 00403C75 72 02 JB SHORT .00403C79
|
||
* 00403C77 8B1B MOV EBX,DWORD PTR DS:[EBX]
|
||
* 00403C79 C6043B 00 MOV BYTE PTR DS:[EBX+EDI],0x0
|
||
* 00403C7D 5B POP EBX
|
||
* 00403C7E 5F POP EDI
|
||
* 00403C7F 8BC6 MOV EAX,ESI
|
||
* 00403C81 5E POP ESI
|
||
* 00403C82 5D POP EBP
|
||
* 00403C83 C2 0800 RETN 0x8
|
||
* 00403C86 CC INT3
|
||
* 00403C87 CC INT3
|
||
* 00403C88 CC INT3
|
||
* 00403C89 CC INT3
|
||
* 00403C8A CC INT3
|
||
* 00403C8B CC INT3
|
||
*
|
||
* 08BCF938 00403C6B RETURN to .00403C6B from .0052FD84
|
||
* 08BCF93C 088DC7F0 ; jichi: target location
|
||
* 08BCF940 0000001F ; jichi: target capacity
|
||
* 08BCF944 08BCFC68 ; jichi: source size
|
||
* 08BCF948 00000010 ; jichi: source size
|
||
* 08BCF94C 00000001
|
||
* 08BCF950 08BCFC69
|
||
* 08BCF954 08BCFC68
|
||
* 08BCF958 0000000F
|
||
* 08BCF95C 00404870 RETURN to .00404870 from .00403BB0
|
||
* 08BCF960 08BCFC68 ; jichi: source text
|
||
* 08BCF964 00000010 ; jichi: source size
|
||
* 08BCF968 0000000F ; jichi: extra capacity
|
||
* 08BCF96C 008B68F8 .008B68F8
|
||
* 08BCF970 004AC441 RETURN to .004AC441 from .00404850
|
||
* 08BCF974 08BCFC68
|
||
* 08BCF978 2AE30C3B
|
||
* 08BCF97C 004A5710 .004A5710
|
||
* 08BCF980 088D5448
|
||
*/
|
||
bool attach(ULONG startAddress, ULONG stopAddress)
|
||
{
|
||
const uint8_t bytes[] = {
|
||
0x72, 0x0E, // 00403C42 72 0E JB SHORT .00403C52
|
||
0x8B, 0x46, 0x04, // 00403C44 8B46 04 MOV EAX,DWORD PTR DS:[ESI+0x4]
|
||
0x5F, // 00403C47 5F POP EDI
|
||
0xC6, 0x00, 0x00, // 00403C48 C600 00 MOV BYTE PTR DS:[EAX],0x0
|
||
0x8B, 0xC6, // 00403C4B 8BC6 MOV EAX,ESI
|
||
0x5E, // 00403C4D 5E POP ESI
|
||
0x5D, // 00403C4E 5D POP EBP
|
||
0xC2, 0x08, 0x00 // 00403C4F C2 0800 RETN 0x8
|
||
};
|
||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
|
||
if (!addr)
|
||
return false;
|
||
addr = MemDbg::findEnclosingAlignedFunction(addr);
|
||
if (!addr)
|
||
return false;
|
||
// addr = 0x00403BB0;
|
||
HookParam hp;
|
||
hp.address = addr;
|
||
hp.hook_before = Private::hookBefore;
|
||
hp.hook_after = Private::hookafter;
|
||
hp.type = EMBED_ABLE | EMBED_DYNA_SJIS;
|
||
hp.newlineseperator = L"\\n";
|
||
hp.hook_font = F_GetGlyphOutlineA;
|
||
return NewHook(hp, "EMbedUnicornOther");
|
||
}
|
||
|
||
} // namespace OtherHook
|
||
} // unnamed namespace
|
||
bool Unicorn::attach_function()
|
||
{
|
||
auto embed = ScenarioHook::attach(processStartAddress, processStopAddress);
|
||
if (embed)
|
||
{
|
||
OtherHook::attach(processStartAddress, processStopAddress);
|
||
}
|
||
return InsertUnicornHook() || embed;
|
||
}
|
||
|
||
bool Unicorn_Anesen::attach_function()
|
||
{
|
||
//[060908][あねせん] あまからツインズ~双姉といっしょ~
|
||
//[071012][あねせん] おしえて巫女先生弐
|
||
//[071214][あねせん] おしえて巫女先生弐 外伝~ハーレム編~
|
||
const BYTE bytes[] = {
|
||
0x83, 0xFF, 0x20,
|
||
XX2,
|
||
0x0F, 0x84, XX4,
|
||
0x81, 0xFF, 0x40, 0x81, 0x00, 0x00,
|
||
0x0F, 0x84};
|
||
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
|
||
if (addr == 0)
|
||
return false;
|
||
addr = MemDbg::findEnclosingAlignedFunction(addr);
|
||
if (addr == 0)
|
||
return false;
|
||
|
||
HookParam hp;
|
||
hp.type = USING_STRING;
|
||
hp.offset = get_stack(4);
|
||
hp.address = addr;
|
||
|
||
return NewHook(hp, "Unicorn_Anesen");
|
||
} |