This commit is contained in:
恍兮惚兮 2024-08-15 04:50:33 +08:00
parent 3cc416c29f
commit 05c65a095e
2 changed files with 813 additions and 747 deletions

View File

@ -1,4 +1,4 @@
#include"Wolf.h"
#include "Wolf.h"
/**
* jichi 10/12/2014
* P.S.: Another approach
@ -39,56 +39,64 @@
*
* Again, drop the N to split dialogue and menu text into separate threads.
*/
namespace { // WolfRPG
// jichi 10/13/2013: restored
bool InsertOldWolfHook()
{
namespace
{ // WolfRPG
// jichi 10/13/2013: restored
bool InsertOldWolfHook()
{
// jichi 10/12/2013:
// Step 1: find the address of GetTextMetricsA
// Step 2: find where this function is called
// Step 3: search "sub esp, XX" after where it is called
enum { sub_esp = 0xec81 }; // jichi: caller pattern: sub esp = 0x81,0xec
enum
{
sub_esp = 0xec81
}; // jichi: caller pattern: sub esp = 0x81,0xec
if (DWORD c1 = Util::FindCallAndEntryAbs((DWORD)GetTextMetricsA, processStopAddress - processStartAddress, processStartAddress, sub_esp))
if (DWORD c2 = Util::FindCallOrJmpRel(c1, processStopAddress - processStartAddress, processStartAddress, 0)) {
union {
if (DWORD c2 = Util::FindCallOrJmpRel(c1, processStopAddress - processStartAddress, processStartAddress, 0))
{
union
{
DWORD i;
WORD *k;
};
DWORD j;
for (i = c2 - 0x100, j = c2 - 0x400; i > j; i--)
if (*k == 0xec83) { // jichi 10/12/2013: 83 EC XX sub esp, XX See: http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20120312.txt
if (*k == 0xec83)
{ // jichi 10/12/2013: 83 EC XX sub esp, XX See: http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20120312.txt
HookParam hp;
hp.address = i;
hp.offset=get_reg(regs::ecx);
hp.offset = get_reg(regs::ecx);
hp.split = get_reg(regs::esp);
hp.type = DATA_INDIRECT|USING_SPLIT;
//GROWL_DWORD(hp.address); // jichi 6/5/2014: 淫乱勀<E4B9B1><E58B80>フィのRPG = 0x50a400
hp.type = DATA_INDIRECT | USING_SPLIT;
// GROWL_DWORD(hp.address); // jichi 6/5/2014: 淫乱勀<E4B9B1><E58B80>フィのRPG = 0x50a400
ConsoleOutput("INSERT WolfRPG");
return NewHook(hp, "WolfRPG");
}
}
//ConsoleOutput("Unknown WolfRPG engine.");
// ConsoleOutput("Unknown WolfRPG engine.");
ConsoleOutput("WolfRPG: failed");
return false;
}
}
//example-game:妹!せいかつ~ファンタジー~ by:iov
bool InsertWolf3Hook()
{
const BYTE bytes[] = { 0xC7,0x45,0xFC,0x00,0x00,0x00,0x00,0x8B,0x45,0x94,0x83,0xE0,0x01 };
// example-game:妹!せいかつ~ファンタジー~ by:iov
bool InsertWolf3Hook()
{
const BYTE bytes[] = {0xC7, 0x45, 0xFC, 0x00, 0x00, 0x00, 0x00, 0x8B, 0x45, 0x94, 0x83, 0xE0, 0x01};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
if (!addr)
{
ConsoleOutput("WolfRPG: pattern3 not found");
return false;
}
HookParam myhp;
myhp.address = addr+41;
myhp.address = addr + 41;
myhp.type = USING_STRING | NO_CONTEXT;
myhp.offset=get_reg(regs::eax);
myhp.offset = get_reg(regs::eax);
myhp.type |= DATA_INDIRECT;
myhp.index = 4;
@ -97,13 +105,15 @@ bool InsertWolf3Hook()
ConsoleOutput("Insert: WolfRPG_String_Copy Hook");
return NewHook(myhp, nameForUser);
}
}
bool InsertWolf4Hook() {
const BYTE bytes[] = {0xC6,0x45,0xFC,0x29,0x8B,0x8D,0xE0,0xEF,0xFF,0xFF,0xE8,XX4,0x50,0x8B,0x4D,0xE8,0x2B,0x4D,0xEC };
bool InsertWolf4Hook()
{
const BYTE bytes[] = {0xC6, 0x45, 0xFC, 0x29, 0x8B, 0x8D, 0xE0, 0xEF, 0xFF, 0xFF, 0xE8, XX4, 0x50, 0x8B, 0x4D, 0xE8, 0x2B, 0x4D, 0xEC};
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
if (!addr) {
if (!addr)
{
ConsoleOutput("WolfRPG: pattern4 not found");
return false;
}
@ -112,7 +122,7 @@ bool InsertWolf4Hook() {
myhp.address = addr + 16;
myhp.type = USING_STRING | NO_CONTEXT;
myhp.offset=get_reg(regs::eax);
myhp.offset = get_reg(regs::eax);
// myhp.type |= DATA_INDIRECT;
// myhp.index = 4;
@ -121,9 +131,7 @@ bool InsertWolf4Hook() {
ConsoleOutput("Insert: WolfRPG4 Hook");
return NewHook(myhp, nameForUser);
}
}
} // WolfRPG namespace
@ -132,115 +140,128 @@ bool InsertWolfHook()
// return InsertOldWolfHook(), InsertWolf2Hook(), InsertWolf3Hook(), InsertWolf4Hook();
return InsertOldWolfHook(), InsertWolf3Hook(), InsertWolf4Hook();
}
namespace{
namespace
{
bool commonfilter(void* data, size_t* len, HookParam* hp){
auto str=std::string(reinterpret_cast<LPSTR>(data),*len);
bool checkchaos=WideStringToString(StringToWideString(str))!=str;
if(checkchaos)return false;
bool check1= str.find("/")!=str.npos||str.find("\\")!=str.npos;
auto hashsuffix=[str](){
auto filterpath={
".png",".jpg",".bmp",
".mp3",".ogg",
".webm",".mp4",
".otf",".mps"
};
for(auto _ :filterpath)
if(str.find(_)!=str.npos)
bool commonfilter(void *data, size_t *len, HookParam *hp)
{
auto str = std::string(reinterpret_cast<LPSTR>(data), *len);
bool checkchaos = WideStringToString(StringToWideString(str)) != str;
if (checkchaos)
return false;
bool check1 = str.find("/") != str.npos || str.find("\\") != str.npos;
auto hashsuffix = [str]()
{
auto filterpath = {
".png", ".jpg", ".bmp",
".mp3", ".ogg",
".webm", ".mp4",
".otf", ".mps"};
for (auto _ : filterpath)
if (str.find(_) != str.npos)
return true;
return false;
};
bool check2=hashsuffix();
bool check3=all_ascii((const char *)data,*len);
if(check1&&(check2||check3))return false;
bool check2 = hashsuffix();
bool check3 = all_ascii((const char *)data, *len);
if (check1 && (check2 || check3))
return false;
return true;
}
bool hook5_1(DWORD addr_1){
//RJ338582
//妹せいかつ ファンタジー1.4.5
bool hook5_1(DWORD addr_1)
{
// RJ338582
// 妹せいかつ ファンタジー1.4.5
const BYTE bytes[] = {
0x6a,0x01,
0x68,XX4,
0x68,XX4,
0x6a,0x01,
0x6a,0x00,
0xFF,0x77,0x10,
0xFF,0x77,0x18,
0xE8
};
0x6a, 0x01,
0x68, XX4,
0x68, XX4,
0x6a, 0x01,
0x6a, 0x00,
0xFF, 0x77, 0x10,
0xFF, 0x77, 0x18,
0xE8};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if(addr==0)return false;
auto off=(*((DWORD*)(sizeof(bytes)+addr)));
auto _calladdr=addr+sizeof(bytes)+4+off;
if(addr_1!=_calladdr)return false;
if (addr == 0)
return false;
auto off = (*((DWORD *)(sizeof(bytes) + addr)));
auto _calladdr = addr + sizeof(bytes) + 4 + off;
if (addr_1 != _calladdr)
return false;
HookParam hp;
hp.address = addr+sizeof(bytes)-1;
hp.offset =get_stack(7);
hp.type =USING_STRING|CODEC_UTF8|EMBED_ABLE|EMBED_AFTER_OVERWRITE|EMBED_BEFORE_SIMPLE;
hp.filter_fun=commonfilter;
hp.address = addr + sizeof(bytes) - 1;
hp.offset = get_stack(7);
hp.type = USING_STRING | CODEC_UTF8 | EMBED_ABLE | EMBED_AFTER_OVERWRITE | EMBED_BEFORE_SIMPLE;
hp.filter_fun = commonfilter;
return NewHook(hp, "Wolf5_1");
}
bool hook5(){
bool hook5()
{
//[220901][あせろら] 寝取られ新妻モニカツンデレな奥さんのHなお仕事
const BYTE bytes[] = {
0x80,0x38,0x40,
0x0f,0x85,XX4,
0x80, 0x38, 0x40,
0x0f, 0x85, XX4,
0x57,
0x68,XX4,
0x8d,XX2,
0xe8
};
0x68, XX4,
0x8d, XX2,
0xe8};
auto addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
if(addr==0)return false;
addr=MemDbg::findEnclosingAlignedFunction(addr);
if (addr == 0)
return false;
addr = MemDbg::findEnclosingAlignedFunction(addr);
if(addr==0)return false;
if(hook5_1(addr))return true;
if (addr == 0)
return false;
if (hook5_1(addr))
return true;
HookParam hp;
hp.address = addr;
hp.offset =get_stack(8);
hp.type =USING_STRING|CODEC_UTF8|EMBED_ABLE|EMBED_AFTER_OVERWRITE|EMBED_BEFORE_SIMPLE;
hp.filter_fun=commonfilter;
hp.offset = get_stack(8);
hp.type = USING_STRING | CODEC_UTF8 | EMBED_ABLE | EMBED_AFTER_OVERWRITE | EMBED_BEFORE_SIMPLE;
hp.filter_fun = commonfilter;
return NewHook(hp, "Wolf5");
}
bool hook6(){
bool hook6()
{
//[220901][あせろら] 寝取られ新妻モニカツンデレな奥さんのHなお仕事
const BYTE bytes[] = {
0xB8,0x00,0x00,0x00,0x80,
0x83,0xC0,0x23
};
bool ok=false;
auto addrs =Util::SearchMemory(bytes, sizeof(bytes),PAGE_EXECUTE, processStartAddress, processStopAddress);
for (auto addr : addrs) {
addr=MemDbg::findEnclosingAlignedFunction(addr);
0xB8, 0x00, 0x00, 0x00, 0x80,
0x83, 0xC0, 0x23};
bool ok = false;
auto addrs = Util::SearchMemory(bytes, sizeof(bytes), PAGE_EXECUTE, processStartAddress, processStopAddress);
for (auto addr : addrs)
{
addr = MemDbg::findEnclosingAlignedFunction(addr);
if(addr==0)continue;
if (addr == 0)
continue;
HookParam hp;
hp.address = (DWORD)addr;
hp.offset=get_stack(3);
hp.type =USING_STRING|CODEC_UTF8;
hp.filter_fun=commonfilter;
ok|=NewHook(hp, "Wolf6");
hp.offset = get_stack(3);
hp.type = USING_STRING | CODEC_UTF8;
hp.filter_fun = commonfilter;
ok |= NewHook(hp, "Wolf6");
}
return ok;
}
bool hook56(){
bool _1=hook5();
bool _2=hook6();
return _1||_2;
bool hook56()
{
bool _1 = hook5();
bool _2 = hook6();
return _1 || _2;
}
}
namespace
{ // unnamed
namespace { // unnamed
namespace ScenarioHook
{
namespace ScenarioHook {
namespace Private {
namespace Private
{
struct TextListElement // ecx, this structure saved a list of element
{
@ -253,12 +274,13 @@ namespace Private {
capacity; // 0xe8, capacity of the data including \0
bool isScenarioText() const
{ return flag1 == 0 && flag2 == 0 && flag3 == 0 && flag4 == 0; }
{
return flag1 == 0 && flag2 == 0 && flag3 == 0 && flag4 == 0;
}
bool isValid() const
{
return size > 0 && size <= capacity
&& Engine::isAddressReadable(text, capacity) && size == ::strlen(text);
return size > 0 && size <= capacity && Engine::isAddressReadable(text, capacity) && size == ::strlen(text);
}
};
@ -270,26 +292,31 @@ namespace Private {
return s;
}
std::unordered_set<std::string> dataSet_;
bool hookBefore(hook_stack*s,void* data1, size_t* len1,uintptr_t*role)
bool hookBefore(hook_stack *s, void *data1, size_t *len1, uintptr_t *role)
{
//enum { DataQueueCapacity = 30 };
// enum { DataQueueCapacity = 30 };
auto self = (TextListElement *)s->ecx; // ecx is actually a list of element
if (self->isValid()) {
if (self->isValid())
{
char *text = ltrim(self->text);
if (*text) {
if (*text)
{
std::string data = text;
if (dataSet_.find(data)==dataSet_.end()) {
if (dataSet_.find(data) == dataSet_.end())
{
auto role = text == self->text && self->isScenarioText() ? Engine::ScenarioRole : Engine::OtherRole;
auto split = s->stack[0]; // retaddr
// auto sig = Engine::hashThreadSignature(role, split);
enum { SendAllowed = true };
enum
{
SendAllowed = true
};
bool timeout;
int prefixSize = text - self->text,
capacity = self->capacity - prefixSize;
return write_string_overwrite(data1,len1,data);
return write_string_overwrite(data1, len1, data);
// data = EngineController::instance()->dispatchTextASTD(data, role, sig, capacity, SendAllowed, &timeout);
// if (timeout)
@ -304,26 +331,33 @@ namespace Private {
}
return 0;
}
void hookafter2(hook_stack*s,void* data1, size_t len){
void hookafter2(hook_stack *s, void *data1, size_t len)
{
auto newData =std::string((char*)data1,len);
auto newData = std::string((char *)data1, len);
auto self = (TextListElement *)s->ecx; // ecx is actually a list of element
if (self->isValid()) {
if (self->isValid())
{
char *text = ltrim(self->text);
if (*text) {
if (*text)
{
std::string data = text;
if (dataSet_.find(data)==dataSet_.end()) {
if (dataSet_.find(data) == dataSet_.end())
{
auto role = text == self->text && self->isScenarioText() ? Engine::ScenarioRole : Engine::OtherRole;
auto split = s->stack[0]; // retaddr
// auto sig = Engine::hashThreadSignature(role, split);
enum { SendAllowed = true };
enum
{
SendAllowed = true
};
bool timeout;
int prefixSize = text - self->text,
capacity = self->capacity - prefixSize;
data=newData;
data = newData;
dataSet_.insert(data);
::memcpy(text, data.c_str(), min(data.size() + 1, capacity));
@ -332,9 +366,9 @@ namespace Private {
}
}
}
} // namespace Private
} // namespace Private
/**
/**
* Sample game: DRAGON SLAVE
*
* This function is very long and contains many CharNextA.
@ -825,30 +859,60 @@ namespace Private {
* 00471898 CC INT3
* 00471899 CC INT3
*/
bool attach(ULONG startAddress, ULONG stopAddress) // attach other text
{
bool attach(ULONG startAddress, ULONG stopAddress) // attach other text
{
ULONG addr = MemDbg::findCallerAddressAfterInt3((ULONG)::CharNextA, startAddress, stopAddress);
//addr = MemDbg::findNearCallAddress(addr, startAddress, stopAddress);
//if (!addr)
// addr = MemDbg::findNearCallAddress(addr, startAddress, stopAddress);
// if (!addr)
// return false;
if(addr==0)return 0;
if (addr == 0)
return 0;
HookParam hp;
hp.address=addr;
hp.offset=get_reg(regs::ecx);
hp.address = addr;
hp.offset = get_reg(regs::ecx);
hp.index = 4;
hp.hook_before=Private::hookBefore;
hp.hook_after=Private::hookafter2;
hp.type=USING_STRING|DATA_INDIRECT|EMBED_ABLE|EMBED_DYNA_SJIS;
hp.hook_font=F_GetGlyphOutlineA;
return NewHook(hp,"EmbedWolf");
}
hp.hook_before = Private::hookBefore;
hp.hook_after = Private::hookafter2;
hp.type = USING_STRING | DATA_INDIRECT | EMBED_ABLE | EMBED_DYNA_SJIS;
hp.hook_font = F_GetGlyphOutlineA;
return NewHook(hp, "EmbedWolf");
}
} // namespace ScenarioHook
} // namespace ScenarioHook
} // unnamed namespace
bool Wolf::attach_function() {
auto _=ScenarioHook::attach(processStartAddress,processStopAddress);
return InsertWolfHook()||hook56()||_;
namespace
{
bool wolf7()
{
BYTE sig[] = {
0x52,
0x8b, 0x4d, 0xf4,
0xe8, XX4,
0x03, 0x45, 0x08,
0x03, 0x45, 0x0c,
0x50,
0x8b, 0x4d, 0xf4,
0xe8, XX4,
0x03, 0x45, 0x08,
0x03, 0x45, 0x14,
0x50,
0xe8, XX4,
0x83, 0xc4, 0x0c,
0x8b, 0x45, 0x14};
auto addr = MemDbg::findBytes(sig, sizeof(sig), processStartAddress, processStopAddress);
if (!addr)
return false;
addr += 31;
HookParam hp;
hp.address = addr;
hp.offset = get_stack(1);
hp.type = USING_STRING | NO_CONTEXT;
return NewHook(hp, "Wolf7");
}
}
bool Wolf::attach_function()
{
auto _ = ScenarioHook::attach(processStartAddress, processStopAddress);
return InsertWolfHook() || hook56() || _ || wolf7();
}

View File

@ -1,11 +1,13 @@
class Wolf:public ENGINE{
public:
Wolf(){
check_by=CHECK_BY::FILE_ANY;
check_by_target=check_by_list{L"data.wolf",L"data\\*.wolf",L"data\\basicdata\\cdatabase.dat"};
class Wolf : public ENGINE
{
public:
Wolf()
{
is_engine_certain = false;
check_by = CHECK_BY::FILE_ANY;
check_by_target = check_by_list{L"data.wolf", L"data\\*.wolf", L"data\\basicdata\\cdatabase.dat"};
};
bool attach_function();
};