mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-11-26 23:34:01 +08:00
some
This commit is contained in:
parent
8592939737
commit
623b208549
@ -175,15 +175,6 @@ bool isPauseKeyPressed()
|
||||
{
|
||||
return WinKey::isKeyControlPressed() || WinKey::isKeyShiftPressed() && !WinKey::isKeyReturnPressed();
|
||||
}
|
||||
inline UINT64 djb2_n2(const unsigned char *str, size_t len, UINT64 hash = 5381)
|
||||
{
|
||||
int i = 0;
|
||||
while (len--)
|
||||
{
|
||||
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
std::unordered_map<UINT64, std::wstring> translatecache;
|
||||
bool check_is_thread_selected(const ThreadParam &tp)
|
||||
{
|
||||
@ -200,7 +191,7 @@ bool check_embed_able(const ThreadParam &tp)
|
||||
bool waitforevent(UINT32 timems, const ThreadParam &tp, const std::wstring &origin)
|
||||
{
|
||||
char eventname[1000];
|
||||
sprintf(eventname, LUNA_EMBED_notify_event, GetCurrentProcessId(), djb2_n2((const unsigned char *)(origin.c_str()), origin.size() * 2));
|
||||
sprintf(eventname, LUNA_EMBED_notify_event, GetCurrentProcessId(), simplehash::djb2_n2((const unsigned char *)(origin.c_str()), origin.size() * 2));
|
||||
auto event = win_event(eventname);
|
||||
while (timems)
|
||||
{
|
||||
|
@ -38,59 +38,10 @@ bool InsertEMEHook()
|
||||
namespace
|
||||
{
|
||||
|
||||
// LRU template class, recv two type params: key & value
|
||||
template <typename Key>
|
||||
class LRUCache
|
||||
{
|
||||
|
||||
private:
|
||||
// cache capacity
|
||||
size_t _capacity = 0;
|
||||
// list _keys中key的指向位置
|
||||
std::unordered_map<Key, typename std::list<Key>::iterator> _cache;
|
||||
std::list<Key> _keys;
|
||||
|
||||
public:
|
||||
// construct function
|
||||
LRUCache(size_t size) : _capacity(size){};
|
||||
|
||||
bool contains(Key key)
|
||||
{
|
||||
auto it = _cache.find(key);
|
||||
if (it == _cache.end())
|
||||
{
|
||||
return false;
|
||||
}; // 返回默认值
|
||||
_keys.splice(_keys.begin(), _keys, it->second);
|
||||
return true;
|
||||
}
|
||||
|
||||
void put(Key key)
|
||||
{
|
||||
auto it = _cache.find(key);
|
||||
if (it != _cache.end())
|
||||
{
|
||||
_keys.splice(_keys.begin(), _keys, it->second);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_keys.size() == _capacity)
|
||||
{
|
||||
Key oldKey = _keys.back();
|
||||
_keys.pop_back();
|
||||
_cache.erase(oldKey);
|
||||
}
|
||||
|
||||
_keys.push_front(key);
|
||||
_cache[key] = _keys.begin();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
bool takeout()
|
||||
{
|
||||
//https://vndb.org/v6187
|
||||
//みちくさ~Loitering on the way~
|
||||
// https://vndb.org/v6187
|
||||
// みちくさ~Loitering on the way~
|
||||
|
||||
trigger_fun = [](LPVOID addr, hook_stack *stack)
|
||||
{
|
||||
@ -108,11 +59,8 @@ public:
|
||||
hp.filter_fun = [](LPVOID data, size_t *size, HookParam *)
|
||||
{
|
||||
auto xx = std::string((char *)data, *size);
|
||||
static LRUCache<std::string> last(10);
|
||||
if (last.contains(xx))
|
||||
return false;
|
||||
last.put(xx);
|
||||
return true;
|
||||
static lru_cache<std::string> last(10);
|
||||
return !last.touch(xx);
|
||||
};
|
||||
return NewHook(hp, "takeout");
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include"LunaSoft.h"
|
||||
#include "LunaSoft.h"
|
||||
/** jichi 12/27/2014 LunaSoft
|
||||
* Sample game: [141226] [LunaSoft] 悪堕ラビリンス -- /hsn8@46C5EF
|
||||
*
|
||||
@ -55,7 +55,7 @@
|
||||
*/
|
||||
// Remove: \n\s*
|
||||
// This is dangerous since \n could appear within SJIS
|
||||
//static bool LunaSoftFilter(LPVOID data, size_t *size, HookParam *)
|
||||
// static bool LunaSoftFilter(LPVOID data, size_t *size, HookParam *)
|
||||
//{
|
||||
// size_t len = *size;
|
||||
// char *str = reinterpret_cast<char *>(data),
|
||||
@ -83,82 +83,63 @@ bool InsertLunaSoftHook()
|
||||
0xcc, // 0046c57e cc int3
|
||||
0xcc, // 0046c57f cc int3
|
||||
0x55, // 0046c580 55 push ebp ; jichi: text in arg1
|
||||
0x8b,0xec, // 0046c581 8bec mov ebp,esp
|
||||
0x83,0xec, 0x08, // 0046c583 83ec 08 sub esp,0x8
|
||||
0x89,0x4d, 0xf8, // 0046c586 894d f8 mov dword ptr ss:[ebp-0x8],ecx
|
||||
0x8b,0x4d, 0xf8, // 0046c589 8b4d f8 mov ecx,dword ptr ss:[ebp-0x8]
|
||||
0x83,0xc1, 0x1c, // 0046c58c 83c1 1c add ecx,0x1c
|
||||
0x8b, 0xec, // 0046c581 8bec mov ebp,esp
|
||||
0x83, 0xec, 0x08, // 0046c583 83ec 08 sub esp,0x8
|
||||
0x89, 0x4d, 0xf8, // 0046c586 894d f8 mov dword ptr ss:[ebp-0x8],ecx
|
||||
0x8b, 0x4d, 0xf8, // 0046c589 8b4d f8 mov ecx,dword ptr ss:[ebp-0x8]
|
||||
0x83, 0xc1, 0x1c, // 0046c58c 83c1 1c add ecx,0x1c
|
||||
0xe8 // 0046c58f e8 2cebf9ff call .0040b0c0
|
||||
};
|
||||
enum { addr_offset = 2 };
|
||||
enum
|
||||
{
|
||||
addr_offset = 2
|
||||
};
|
||||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStopAddress);
|
||||
//GROWL(addr);
|
||||
if (!addr) {
|
||||
// GROWL(addr);
|
||||
if (!addr)
|
||||
{
|
||||
ConsoleOutput("LunaSoft: pattern not found");
|
||||
return false;
|
||||
}
|
||||
HookParam hp;
|
||||
hp.address = addr + addr_offset;
|
||||
hp.offset =get_stack(1);
|
||||
hp.offset = get_stack(1);
|
||||
hp.type = USING_STRING;
|
||||
//hp.filter_fun = LunaSoftFilter; // remove \n
|
||||
// hp.filter_fun = LunaSoftFilter; // remove \n
|
||||
ConsoleOutput("INSERT LunaSoft");
|
||||
return NewHook(hp, "LunaSoft");
|
||||
|
||||
// There are no GDI functions anyway
|
||||
//ConsoleOutput("LunaSoft: disable GDI hooks");
|
||||
// ConsoleOutput("LunaSoft: disable GDI hooks");
|
||||
//
|
||||
}
|
||||
bool InsertXXkata(){
|
||||
//アイリスフィールド
|
||||
bool InsertXXkata()
|
||||
{
|
||||
// アイリスフィールド
|
||||
|
||||
//素晴らしき国家の築き方
|
||||
//浮遊都市の作り方
|
||||
//正しい性奴隷の使い方
|
||||
// 素晴らしき国家の築き方
|
||||
// 浮遊都市の作り方
|
||||
// 正しい性奴隷の使い方
|
||||
|
||||
//HSNc@0:user32.dll:wsprintfA
|
||||
auto addr = GetProcAddress(GetModuleHandleW(L"user32.dll"),"wsprintfA");
|
||||
if (addr == 0)return false;
|
||||
// HSNc@0:user32.dll:wsprintfA
|
||||
auto addr = GetProcAddress(GetModuleHandleW(L"user32.dll"), "wsprintfA");
|
||||
if (addr == 0)
|
||||
return false;
|
||||
HookParam hp;
|
||||
hp.address=(uint64_t)addr ;
|
||||
hp.type=USING_STRING|NO_CONTEXT;
|
||||
hp.offset=get_stack(3);
|
||||
hp.address = (uint64_t)addr;
|
||||
hp.type = USING_STRING | NO_CONTEXT;
|
||||
hp.offset = get_stack(3);
|
||||
hp.filter_fun = all_ascii_Filter;
|
||||
return NewHook(hp, "XXkata");
|
||||
}
|
||||
|
||||
|
||||
|
||||
namespace { // unnamed
|
||||
namespace ScenarioHook {
|
||||
namespace Private {
|
||||
class DataCache // LRU policy, hashtable not used for simplicity
|
||||
namespace
|
||||
{ // unnamed
|
||||
namespace ScenarioHook
|
||||
{
|
||||
int capacity_;
|
||||
std::list<std::string> stack_; // priority stack
|
||||
public:
|
||||
explicit DataCache(int capacity = 100)
|
||||
: capacity_(capacity) {} //{ stack_.reserve(capacity); }
|
||||
|
||||
bool contains(const std::string &data) const
|
||||
{ return stack_.end() != std::find(stack_.begin(), stack_.end(), data); }
|
||||
|
||||
std::string retain(const std::string &data)
|
||||
namespace Private
|
||||
{
|
||||
auto p = std::find(stack_.begin(), stack_.end(), data);
|
||||
if (p == stack_.end()) {
|
||||
if (stack_.size() == capacity_)
|
||||
stack_.pop_back();
|
||||
stack_.push_front(data);
|
||||
return data;
|
||||
} else {
|
||||
if (p != stack_.begin())
|
||||
stack_.splice(stack_.begin(), stack_, p);
|
||||
return *p;
|
||||
}
|
||||
}
|
||||
};
|
||||
DataCache cache_; // this is used to make sure that same translation will have the same address
|
||||
lru_cache<std::string> cache_(100);
|
||||
|
||||
/**
|
||||
* Sample game: 悪堕ラビリンス, scenario return address: 0x42f6dc
|
||||
@ -177,34 +158,34 @@ namespace Private {
|
||||
* 0042F6EB E9 5E010000 JMP lus004.0042F84E
|
||||
* 0042F6F0 8B4D 10 MOV ECX,DWORD PTR SS:[EBP+0x10]
|
||||
*/
|
||||
bool hookBefore(hook_stack*s,void* data1, size_t* len,uintptr_t*role)
|
||||
bool hookBefore(hook_stack *s, void *data1, size_t *len, uintptr_t *role)
|
||||
{
|
||||
auto text = (LPCSTR)s->stack[1]; // arg1
|
||||
if (!text || !*text) // || Util::allAscii(text))
|
||||
return 0;
|
||||
std::string oldData = text;
|
||||
if (cache_.contains(oldData))
|
||||
if (cache_.exists(oldData))
|
||||
return 0;
|
||||
// 0042F6DC 8B45 10 MOV EAX,DWORD PTR SS:[EBP+0x10] ; jichi: retaddr
|
||||
// 0042F6DF 50 PUSH EAX
|
||||
ULONG retaddr = s->stack[0];
|
||||
* role = Engine::OtherRole;
|
||||
*role = Engine::OtherRole;
|
||||
if (*(DWORD *)retaddr == 0x5010458b)
|
||||
*role = Engine::ScenarioRole;
|
||||
|
||||
write_string_overwrite(data1,len,oldData);
|
||||
write_string_overwrite(data1, len, oldData);
|
||||
return 1;
|
||||
|
||||
}
|
||||
void hookafter1(hook_stack*s,void* data1, size_t len){
|
||||
void hookafter1(hook_stack *s, void *data1, size_t len)
|
||||
{
|
||||
static std::string newData;
|
||||
newData=std::string((char*)data1,len);
|
||||
newData = cache_.retain(newData);
|
||||
newData = std::string((char *)data1, len);
|
||||
newData = cache_.put(newData).first;
|
||||
s->stack[1] = (ULONG)newData.c_str(); // arg1
|
||||
}
|
||||
} // namespace Private
|
||||
} // namespace Private
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sample game: 悪堕ラビリンス
|
||||
*
|
||||
* Debugging method: Hook to all function that accessing the text
|
||||
@ -492,14 +473,14 @@ namespace Private {
|
||||
* 0012FEC4 |00BF0752
|
||||
* 0012FEC8 |00000113
|
||||
*/
|
||||
bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario
|
||||
{
|
||||
bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario
|
||||
{
|
||||
ULONG addr1, addr2;
|
||||
{
|
||||
const uint8_t bytes1[] = {
|
||||
0x89,0x88, 0xac,0x02,0x00,0x00, // 00487c1d 8988 ac020000 mov dword ptr ds:[eax+0x2ac],ecx
|
||||
0x8b,0x55, 0xfc, // 00487c23 8b55 fc mov edx,dword ptr ss:[ebp-0x4]
|
||||
0xd9,0xee // 00487c26 d9ee fldz
|
||||
0x89, 0x88, 0xac, 0x02, 0x00, 0x00, // 00487c1d 8988 ac020000 mov dword ptr ds:[eax+0x2ac],ecx
|
||||
0x8b, 0x55, 0xfc, // 00487c23 8b55 fc mov edx,dword ptr ss:[ebp-0x4]
|
||||
0xd9, 0xee // 00487c26 d9ee fldz
|
||||
};
|
||||
addr1 = MemDbg::findBytes(bytes1, sizeof(bytes1), startAddress, stopAddress);
|
||||
if (!addr1)
|
||||
@ -507,14 +488,14 @@ bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario
|
||||
addr1 = MemDbg::findEnclosingAlignedFunction(addr1);
|
||||
if (!addr1)
|
||||
return false;
|
||||
//addr1 = 0x00487c10;
|
||||
// addr1 = 0x00487c10;
|
||||
}
|
||||
{
|
||||
const uint8_t bytes2[] = {
|
||||
0x83,0xe0, 0x0c, // 0042b266 83e0 0c and eax,0xc
|
||||
0x83,0xf8, 0x04, // 0042b269 83f8 04 cmp eax,0x4
|
||||
0x83, 0xe0, 0x0c, // 0042b266 83e0 0c and eax,0xc
|
||||
0x83, 0xf8, 0x04, // 0042b269 83f8 04 cmp eax,0x4
|
||||
0x75, 0x07, // 0042b26c 75 07 jnz short lus004.0042b275
|
||||
0xd9,0xee // 0042b26e d9ee fldz
|
||||
0xd9, 0xee // 0042b26e d9ee fldz
|
||||
};
|
||||
addr2 = MemDbg::findBytes(bytes2, sizeof(bytes2), startAddress, stopAddress);
|
||||
if (!addr2)
|
||||
@ -522,29 +503,27 @@ bool attach(ULONG startAddress, ULONG stopAddress) // attach scenario
|
||||
addr2 = MemDbg::findEnclosingAlignedFunction(addr2);
|
||||
if (!addr2)
|
||||
return false;
|
||||
//addr2 = 0x0042b1f0;
|
||||
// addr2 = 0x0042b1f0;
|
||||
}
|
||||
HookParam hp;
|
||||
hp.address=addr1;
|
||||
hp.hook_before=Private::hookBefore;
|
||||
hp.hook_after=Private::hookafter1;
|
||||
hp.type=EMBED_ABLE|EMBED_DYNA_SJIS;
|
||||
auto succ=NewHook(hp,"EMBEDLUNA");
|
||||
hp.address=addr2;
|
||||
succ|=NewHook(hp,"EMBEDLUNA");
|
||||
|
||||
hp.address = addr1;
|
||||
hp.hook_before = Private::hookBefore;
|
||||
hp.hook_after = Private::hookafter1;
|
||||
hp.type = EMBED_ABLE | EMBED_DYNA_SJIS;
|
||||
auto succ = NewHook(hp, "EMBEDLUNA");
|
||||
hp.address = addr2;
|
||||
succ |= NewHook(hp, "EMBEDLUNA");
|
||||
|
||||
return succ;
|
||||
}
|
||||
} // namespace ScenarioHook
|
||||
}
|
||||
} // namespace ScenarioHook
|
||||
} // unnamed namespace
|
||||
|
||||
bool LunaSoft::attach_function()
|
||||
{
|
||||
|
||||
|
||||
bool LunaSoft::attach_function() {
|
||||
|
||||
bool b1= InsertLunaSoftHook();
|
||||
bool b2=InsertXXkata();
|
||||
bool embed=ScenarioHook::attach(processStartAddress, processStopAddress);
|
||||
return b1||b2||embed;
|
||||
bool b1 = InsertLunaSoftHook();
|
||||
bool b2 = InsertXXkata();
|
||||
bool embed = ScenarioHook::attach(processStartAddress, processStopAddress);
|
||||
return b1 || b2 || embed;
|
||||
}
|
@ -954,25 +954,7 @@ struct TextArgument // first argument of the scenario hook
|
||||
&& !::strstr(text, "\x81\x5e"); // "/"
|
||||
}
|
||||
};
|
||||
enum : UINT64 { djb2_hash0 = 5381 };
|
||||
inline UINT64 djb2(const UINT8 *str, UINT64 hash = djb2_hash0)
|
||||
{
|
||||
UINT8 c;
|
||||
while ((c = *str++))
|
||||
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||
return hash;
|
||||
}inline UINT64 djb2_n2(const char* str, size_t len, UINT64 hash = djb2_hash0)
|
||||
{
|
||||
while (len--)
|
||||
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
|
||||
return hash;
|
||||
}
|
||||
inline UINT64 hashByteArraySTD(const std::string& b, UINT64 h = djb2_hash0)
|
||||
{
|
||||
return djb2_n2(b.c_str(), b.size(), h);
|
||||
}
|
||||
inline UINT64 hashCharArray(const void *lp)
|
||||
{ return djb2(reinterpret_cast<const UINT8 *>(lp)); }
|
||||
|
||||
namespace ScenarioHook {
|
||||
|
||||
namespace Private {
|
||||
@ -1049,7 +1031,7 @@ namespace Private {
|
||||
arg->size = data_.size() + 1;
|
||||
arg->capacity = arg->size;
|
||||
|
||||
hashes_.insert(hashCharArray(arg->text));
|
||||
hashes_.insert(simplehash::hashCharArray(arg->text));
|
||||
}
|
||||
bool hookBefore(hook_stack*s,void* data, size_t* len1,uintptr_t*role)
|
||||
{
|
||||
@ -1072,7 +1054,7 @@ namespace Private {
|
||||
return false;
|
||||
auto arg = (TextArgument *)s->stack[0]; // arg1
|
||||
if (!arg || !arg->isValid()
|
||||
|| hashes_.find(hashCharArray(arg->text)) != hashes_.end())
|
||||
|| hashes_.find(simplehash::hashCharArray(arg->text)) != hashes_.end())
|
||||
return false;
|
||||
if (arg->size < 0xf && split > 0 && !isOtherText(arg->text))
|
||||
return false;
|
||||
|
@ -1,4 +1,4 @@
|
||||
#include"Unicorn.h"
|
||||
#include "Unicorn.h"
|
||||
/**
|
||||
* jichi 9/16/2013: a-unicorn / gesen18
|
||||
* See (CaoNiMaGeBi): http://tieba.baidu.com/p/2586681823
|
||||
@ -16,72 +16,40 @@ bool InsertUnicornHook()
|
||||
{
|
||||
// pattern: 2bce8bf8
|
||||
const BYTE bytes[] = {
|
||||
0x2b,0xce, // sub ecx,esi ; hook here
|
||||
0x8b,0xf8 // mov edi,eax
|
||||
0x2b, 0xce, // sub ecx,esi ; hook here
|
||||
0x8b, 0xf8 // mov edi,eax
|
||||
};
|
||||
//enum { addr_offset = 0 };
|
||||
// enum { addr_offset = 0 };
|
||||
ULONG range = min(processStopAddress - processStartAddress, MAX_REL_ADDR);
|
||||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), processStartAddress, processStartAddress + range);
|
||||
if (!addr) {
|
||||
if (!addr)
|
||||
{
|
||||
ConsoleOutput("Unicorn: pattern not exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
HookParam hp;
|
||||
hp.type = NO_CONTEXT | DATA_INDIRECT;
|
||||
hp.offset=get_reg(regs::edi);
|
||||
hp.offset = get_reg(regs::edi);
|
||||
hp.address = addr;
|
||||
|
||||
//index = SearchPattern(processStartAddress, size,ins, sizeof(ins));
|
||||
//GROWL_DWORD2(base, index);
|
||||
// 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.
|
||||
class TextHashCache
|
||||
{
|
||||
int capacity_;
|
||||
std::list<uint64_t> hashes_;
|
||||
public:
|
||||
explicit TextHashCache(int capacity) : capacity_(capacity) {}
|
||||
namespace
|
||||
{ // unnamed
|
||||
// A simple but very inefficient implementation for LRU cache.
|
||||
|
||||
bool contains(uint64_t h) const
|
||||
{ return std::find(hashes_.begin(), hashes_.end(), h) != hashes_.end(); }
|
||||
|
||||
void add(uint64_t h)
|
||||
namespace ScenarioHook
|
||||
{
|
||||
if (hashes_.size() == capacity_)
|
||||
hashes_.pop_back();
|
||||
hashes_.push_front(h);
|
||||
}
|
||||
|
||||
};
|
||||
enum : UINT64 { djb2_hash0 = 5381 };
|
||||
inline UINT64 djb2(const UINT8 *str, UINT64 hash = djb2_hash0)
|
||||
{
|
||||
UINT8 c;
|
||||
while ((c = *str++))
|
||||
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||
return hash;
|
||||
}inline UINT64 djb2_n2(const char* str, size_t len, UINT64 hash = djb2_hash0)
|
||||
{
|
||||
while (len--)
|
||||
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
|
||||
return hash;
|
||||
}
|
||||
inline UINT64 hashByteArraySTD(const std::string& b, UINT64 h = djb2_hash0)
|
||||
{
|
||||
return djb2_n2(b.c_str(), b.size(), h);
|
||||
}
|
||||
inline UINT64 hashCharArray(const void *lp)
|
||||
{ return djb2(reinterpret_cast<const UINT8 *>(lp)); }
|
||||
namespace ScenarioHook {
|
||||
lru_cache<uint64_t> textCache_(30); // capacity = 30
|
||||
|
||||
TextHashCache textCache_(30); // capacity = 30
|
||||
|
||||
namespace Private {
|
||||
namespace Private
|
||||
{
|
||||
|
||||
class TextStorage
|
||||
{
|
||||
@ -90,12 +58,15 @@ namespace Private {
|
||||
newData_;
|
||||
int lineCount_;
|
||||
bool saved_;
|
||||
|
||||
public:
|
||||
TextStorage()
|
||||
: text_(nullptr), lineCount_(0), saved_(false) {}
|
||||
|
||||
bool isEmpty() const
|
||||
{ return lineCount_ == 0; }
|
||||
{
|
||||
return lineCount_ == 0;
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
@ -117,7 +88,7 @@ namespace Private {
|
||||
|
||||
std::string sourceData_;
|
||||
LPSTR targetText_;
|
||||
bool hookBefore(hook_stack*s,void* data, size_t* len1,uintptr_t*role)
|
||||
bool hookBefore(hook_stack *s, void *data, size_t *len1, uintptr_t *role)
|
||||
{
|
||||
// Sample game: 三極姫4 ~天華繚乱 天命の恋絵巻~
|
||||
// 004B76BB 51 PUSH ECX
|
||||
@ -161,28 +132,30 @@ namespace Private {
|
||||
// 0049A85D 0F8E E3000000 JLE .0049A946
|
||||
|
||||
auto retaddr = s->stack[0];
|
||||
* role = 0;
|
||||
//if (retaddr == 0x4b7728)
|
||||
*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 (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
|
||||
// else
|
||||
// return true;
|
||||
if (*role != Engine::ScenarioRole && !textStorage_.isEmpty()) {
|
||||
if (*role != Engine::ScenarioRole && !textStorage_.isEmpty())
|
||||
{
|
||||
textStorage_.restore();
|
||||
textStorage_.clear();
|
||||
}
|
||||
if (!*role)
|
||||
return false;
|
||||
|
||||
auto text = (LPSTR)*(DWORD *)(s->ecx + textOffset_); // [ecx+0x114]
|
||||
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()) {
|
||||
if (!textStorage_.isEmpty())
|
||||
{
|
||||
textStorage_.restore();
|
||||
textStorage_.clear();
|
||||
}
|
||||
@ -196,35 +169,37 @@ namespace Private {
|
||||
|
||||
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);
|
||||
// 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){
|
||||
void hookafter2(hook_stack *s, void *data, size_t len)
|
||||
{
|
||||
|
||||
auto newData =std::string((char*)data,len);
|
||||
auto newData = std::string((char *)data, len);
|
||||
auto retaddr = s->stack[0];
|
||||
int role = 0;
|
||||
//if (retaddr == 0x4b7728)
|
||||
// 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 (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
|
||||
// else
|
||||
// return true;
|
||||
if (role != Engine::ScenarioRole && !textStorage_.isEmpty()) {
|
||||
if (role != Engine::ScenarioRole && !textStorage_.isEmpty())
|
||||
{
|
||||
textStorage_.restore();
|
||||
textStorage_.clear();
|
||||
}
|
||||
if (!role)
|
||||
return ;
|
||||
auto text = (LPSTR)*(DWORD *)(s->ecx + textOffset_); // [ecx+0x114]
|
||||
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()) {
|
||||
return;
|
||||
if (!textStorage_.isEmpty())
|
||||
{
|
||||
textStorage_.restore();
|
||||
textStorage_.clear();
|
||||
}
|
||||
@ -236,31 +211,32 @@ namespace Private {
|
||||
oldData = text;
|
||||
if (role == Engine::NameRole)
|
||||
strReplace(oldData, "\x81\x40", "");
|
||||
//oldData.replace("\x81\x40", ""); // remove spaces in the middle of names
|
||||
if (oldData == newData) {
|
||||
// oldData.replace("\x81\x40", ""); // remove spaces in the middle of names
|
||||
if (oldData == newData)
|
||||
{
|
||||
if (textStorageEnabled)
|
||||
textStorage_.clear();
|
||||
return ;
|
||||
return;
|
||||
}
|
||||
if (textStorageEnabled)
|
||||
textStorage_.save();
|
||||
sourceData_ = newData;
|
||||
targetText_ = (LPSTR)s->stack[1]; // arg1
|
||||
textCache_.add(hashByteArraySTD(newData));
|
||||
|
||||
textCache_.put(simplehash::hashByteArraySTD(newData));
|
||||
}
|
||||
bool hookAfter(hook_stack*s,void* data, size_t* len1,uintptr_t*role)
|
||||
bool hookAfter(hook_stack *s, void *data, size_t *len1, uintptr_t *role)
|
||||
{
|
||||
if (targetText_)
|
||||
{
|
||||
if (targetText_) {
|
||||
::strcpy(targetText_, sourceData_.c_str());
|
||||
targetText_ = nullptr;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Private
|
||||
} // namespace Private
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sample text
|
||||
*
|
||||
* Sample game: 三極姫4 ~天華繚乱 天命の恋絵巻~
|
||||
@ -320,58 +296,58 @@ namespace Private {
|
||||
* 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)
|
||||
{
|
||||
// 三極姫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)) {
|
||||
for (; isTextSeparator(p); p += ::strlen(p))
|
||||
{
|
||||
lineCount_++;
|
||||
p += 12;
|
||||
data.append(p);
|
||||
}
|
||||
oldData_ = std::string(text, p - text);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
void Private::TextStorage::save()
|
||||
{
|
||||
void Private::TextStorage::save()
|
||||
{
|
||||
if (lineCount_ <= 1)
|
||||
return;
|
||||
LPSTR p = text_ + ::strlen(text_);
|
||||
while (isTextSeparator(p)) {
|
||||
while (isTextSeparator(p))
|
||||
{
|
||||
p += 12 + 1; // +1 for the extra 0xa
|
||||
if (size_t size = ::strlen(p)) {
|
||||
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()))
|
||||
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.
|
||||
@ -476,16 +452,16 @@ bool Private::TextStorage::restore()
|
||||
* 004A6CD8 CC INT3
|
||||
* 004A6CD9 CC INT3
|
||||
*/
|
||||
bool attach(ULONG startAddress, ULONG stopAddress)
|
||||
{
|
||||
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
|
||||
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
|
||||
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)
|
||||
@ -505,44 +481,46 @@ bool attach(ULONG startAddress, ULONG stopAddress)
|
||||
// 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");
|
||||
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 ScenarioHook
|
||||
|
||||
namespace OtherHook {
|
||||
namespace Private {
|
||||
namespace OtherHook
|
||||
{
|
||||
namespace Private
|
||||
{
|
||||
|
||||
//bool isSkippedText(LPCSTR text)
|
||||
// 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)
|
||||
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)
|
||||
// if (*(DWORD *)retaddr != 0xeb0cc483)
|
||||
// return true;
|
||||
//retaddr = s->stack[7]; // parent caller
|
||||
// retaddr = s->stack[7]; // parent caller
|
||||
|
||||
// Scenario/name/other threads to skip:
|
||||
// - 0x404062 // there are so many other texts in this thread
|
||||
@ -565,29 +543,31 @@ namespace Private {
|
||||
// 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)
|
||||
// if (retaddr != 0x4769f8 && retaddr != 0x4135ba)
|
||||
// return true;
|
||||
switch (*(WORD *)retaddr) {
|
||||
switch (*(WORD *)retaddr)
|
||||
{
|
||||
case 0xeed9: // 004769F8 D9EE FLDZ
|
||||
case 0x08eb: // 004135BA EB 08 JMP SHORT .004135C4
|
||||
break;
|
||||
default: return false;
|
||||
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_.contains(hashCharArray(text)))
|
||||
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 };
|
||||
enum
|
||||
{
|
||||
role = Engine::OtherRole
|
||||
};
|
||||
|
||||
return write_string_overwrite(data,len,text);
|
||||
/* //oldData.replace("\\n", "\n"); // Remove new line. FIXME: automatically adjust line width
|
||||
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;
|
||||
@ -597,18 +577,18 @@ namespace Private {
|
||||
return true;*/
|
||||
}
|
||||
|
||||
void hookafter(hook_stack*s,void* data, size_t len){
|
||||
void hookafter(hook_stack *s, void *data, size_t len)
|
||||
{
|
||||
|
||||
auto newData =std::string((char*)data,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
|
||||
} // namespace Private
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sample game: 戦極姫6
|
||||
* Function found by debugging caller of GetGlyphOutlineA.
|
||||
* 0052F2DC CC INT3
|
||||
@ -788,17 +768,17 @@ namespace Private {
|
||||
* 08BCF97C 004A5710 .004A5710
|
||||
* 08BCF980 088D5448
|
||||
*/
|
||||
bool attach(ULONG startAddress, ULONG stopAddress)
|
||||
{
|
||||
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]
|
||||
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
|
||||
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
|
||||
0xC2, 0x08, 0x00 // 00403C4F C2 0800 RETN 0x8
|
||||
};
|
||||
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), startAddress, stopAddress);
|
||||
if (!addr)
|
||||
@ -806,47 +786,50 @@ bool attach(ULONG startAddress, ULONG stopAddress)
|
||||
addr = MemDbg::findEnclosingAlignedFunction(addr);
|
||||
if (!addr)
|
||||
return false;
|
||||
//addr = 0x00403BB0;
|
||||
// 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);
|
||||
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");
|
||||
}
|
||||
return InsertUnicornHook()||embed;
|
||||
|
||||
} // 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(){
|
||||
bool Unicorn_Anesen::attach_function()
|
||||
{
|
||||
//[060908][あねせん] あまからツインズ~双姉といっしょ~
|
||||
//[071012][あねせん] おしえて巫女先生弐
|
||||
//[071214][あねせん] おしえて巫女先生弐 外伝~ハーレム編~
|
||||
const BYTE bytes[] = {
|
||||
0x83 ,0xFF ,0x20,
|
||||
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;
|
||||
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.offset = get_stack(4);
|
||||
hp.address = addr;
|
||||
|
||||
return NewHook(hp, "Unicorn_Anesen");
|
||||
|
@ -1772,6 +1772,48 @@ namespace
|
||||
return write_string_overwrite(data, len, s);
|
||||
}
|
||||
|
||||
bool F010005F00E036000_1(void *data, size_t *len, HookParam *hp)
|
||||
{
|
||||
static lru_cache<std::string> cache(5);
|
||||
static std::string last;
|
||||
auto s = std::string((char *)data, *len);
|
||||
|
||||
if (endWith(last, s))
|
||||
{
|
||||
last = s;
|
||||
return false;
|
||||
}
|
||||
if (cache.touch(s))
|
||||
{
|
||||
last = s;
|
||||
return false;
|
||||
}
|
||||
last = s;
|
||||
return write_string_overwrite(data, len, s);
|
||||
}
|
||||
bool F010005F00E036000(void *data, size_t *len, HookParam *hp)
|
||||
{
|
||||
if (!F010005F00E036000_1(data, len, hp))
|
||||
return false;
|
||||
static std::string last;
|
||||
auto s = std::string((char *)data, *len);
|
||||
|
||||
auto parse = [](std::string &s)
|
||||
{
|
||||
strReplace(s, u8"㊤", u8"―");
|
||||
strReplace(s, u8"㊥", u8"―");
|
||||
strReplace(s, u8"㊦", u8"―");
|
||||
return s;
|
||||
};
|
||||
if (startWith(s, last))
|
||||
{
|
||||
write_string_overwrite(data, len, parse(s.substr(last.size(), s.size() - last.size())));
|
||||
last = s;
|
||||
return true;
|
||||
}
|
||||
last = s;
|
||||
return write_string_overwrite(data, len, parse(s));
|
||||
}
|
||||
bool F0100FC2019346000(void *data, size_t *len, HookParam *hp)
|
||||
{
|
||||
StringFilter((char *)data, len, "#n", 2);
|
||||
@ -3143,6 +3185,8 @@ namespace
|
||||
{0x801f67c0, {CODEC_UTF32, 1, 0, 0, F01007FD00DB20000, "01007FD00DB20000", "1.0.0"}},
|
||||
{0x802a76c0, {CODEC_UTF32, 0, 0, 0, F01007FD00DB20000, "01007FD00DB20000", "1.0.0"}},
|
||||
{0x8031fc80, {CODEC_UTF32, 1, 0, 0, F01007FD00DB20000, "01007FD00DB20000", "1.0.0"}},
|
||||
// 真 流行り神1・2パック
|
||||
{0x80072720, {CODEC_UTF8, 1, 0, 0, F010005F00E036000, "010005F00E036000", "1.0.0"}},
|
||||
// 真流行り神3
|
||||
{0x80082F70, {0, 0xb, 0, TF0100AA1013B96000, 0, "0100AA1013B96000", nullptr}}, //"1.0.0", "1.0.1"
|
||||
|
||||
|
@ -164,15 +164,6 @@ C_LUNA_API void Luna_useembed(DWORD pid, uint64_t address, uint64_t ctx1, uint64
|
||||
}
|
||||
}
|
||||
|
||||
inline UINT64 djb2_n2(const unsigned char *str, size_t len, UINT64 hash = 5381)
|
||||
{
|
||||
int i = 0;
|
||||
while (len--)
|
||||
{
|
||||
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
C_LUNA_API void Luna_embedcallback(DWORD pid, LPCWSTR text, LPCWSTR trans)
|
||||
{
|
||||
auto sm = Host::GetEmbedSharedMem(pid);
|
||||
@ -180,7 +171,7 @@ C_LUNA_API void Luna_embedcallback(DWORD pid, LPCWSTR text, LPCWSTR trans)
|
||||
return;
|
||||
wcsncpy(sm->text, trans, ARRAYSIZE(sm->text));
|
||||
char eventname[1000];
|
||||
sprintf(eventname, LUNA_EMBED_notify_event, pid, djb2_n2((const unsigned char *)(text), wcslen(text) * 2));
|
||||
sprintf(eventname, LUNA_EMBED_notify_event, pid, simplehash::djb2_n2((const unsigned char *)(text), wcslen(text) * 2));
|
||||
win_event event1(eventname);
|
||||
event1.signal(true);
|
||||
}
|
89
include/lrucache.hpp
Normal file
89
include/lrucache.hpp
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* File: lrucache.hpp
|
||||
* Author: Alexander Ponomarev
|
||||
*
|
||||
* Created on June 20, 2013, 5:09 PM
|
||||
*/
|
||||
|
||||
#ifndef _LRUCACHE_HPP_INCLUDED_
|
||||
#define _LRUCACHE_HPP_INCLUDED_
|
||||
|
||||
#include <unordered_map>
|
||||
#include <list>
|
||||
#include <cstddef>
|
||||
#include <stdexcept>
|
||||
|
||||
template <typename key_t, typename value_t = int>
|
||||
class lru_cache
|
||||
{
|
||||
public:
|
||||
typedef typename std::pair<key_t, value_t> key_value_pair_t;
|
||||
typedef typename std::list<key_value_pair_t>::iterator list_iterator_t;
|
||||
|
||||
lru_cache(size_t max_size) : _max_size(max_size)
|
||||
{
|
||||
}
|
||||
const key_value_pair_t &put(const key_t &key, const value_t &&value = {})
|
||||
{
|
||||
auto it = _cache_items_map.find(key);
|
||||
_cache_items_list.emplace_front(key_value_pair_t(key, value));
|
||||
if (it != _cache_items_map.end())
|
||||
{
|
||||
_cache_items_list.erase(it->second);
|
||||
_cache_items_map.erase(it);
|
||||
}
|
||||
_cache_items_map[key] = _cache_items_list.begin();
|
||||
|
||||
if (_cache_items_map.size() > _max_size)
|
||||
{
|
||||
auto last = _cache_items_list.end();
|
||||
last--;
|
||||
_cache_items_map.erase(last->first);
|
||||
_cache_items_list.pop_back();
|
||||
}
|
||||
return *_cache_items_list.begin();
|
||||
}
|
||||
const value_t &get(const key_t &key)
|
||||
{
|
||||
auto it = _cache_items_map.find(key);
|
||||
if (it == _cache_items_map.end())
|
||||
{
|
||||
throw std::range_error("There is no such key in cache");
|
||||
}
|
||||
else
|
||||
{
|
||||
_cache_items_list.splice(_cache_items_list.begin(), _cache_items_list, it->second);
|
||||
return it->second->second;
|
||||
}
|
||||
}
|
||||
|
||||
bool exists(const key_t &key) const
|
||||
{
|
||||
return _cache_items_map.find(key) != _cache_items_map.end();
|
||||
}
|
||||
bool touch(const key_t &key)
|
||||
{
|
||||
try
|
||||
{
|
||||
auto _ = get(key);
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
put(key);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return _cache_items_map.size();
|
||||
}
|
||||
|
||||
private:
|
||||
std::list<key_value_pair_t> _cache_items_list;
|
||||
std::unordered_map<key_t, list_iterator_t> _cache_items_map;
|
||||
size_t _max_size;
|
||||
};
|
||||
|
||||
#endif /* _LRUCACHE_HPP_INCLUDED_ */
|
@ -42,3 +42,4 @@
|
||||
#include "hookcode.h"
|
||||
#include "texthook.h"
|
||||
#include "winevent.hpp"
|
||||
#include "lrucache.hpp"
|
@ -51,7 +51,7 @@ inline std::vector<StringT> strSplit_impl(const StringT &s, const StringT &delim
|
||||
template <class StringT>
|
||||
inline bool endWith_impl(const StringT &s, const StringT &s2)
|
||||
{
|
||||
if ((s.size() >= s2.size()) && (s.substr(s.size() - s2.size(), s2.size()) == s2))
|
||||
if (s2.size() && (s.size() >= s2.size()) && (s.substr(s.size() - s2.size(), s2.size()) == s2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -61,7 +61,7 @@ inline bool endWith_impl(const StringT &s, const StringT &s2)
|
||||
template <class StringT>
|
||||
inline bool startWith_impl(const StringT &s, const StringT &s2)
|
||||
{
|
||||
if ((s.size() >= s2.size()) && (s.substr(0, s2.size()) == s2))
|
||||
if (s2.size() && (s.size() >= s2.size()) && (s.substr(0, s2.size()) == s2))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -215,7 +215,7 @@ size_t u32strlen(uint32_t *data)
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string wcasta(const std::wstring& x)
|
||||
std::string wcasta(const std::wstring &x)
|
||||
{
|
||||
std::string xx;
|
||||
for (auto c : x)
|
||||
@ -223,7 +223,7 @@ std::string wcasta(const std::wstring& x)
|
||||
return xx;
|
||||
}
|
||||
|
||||
std::wstring acastw(const std::string& x)
|
||||
std::wstring acastw(const std::string &x)
|
||||
{
|
||||
std::wstring xx;
|
||||
for (auto c : x)
|
||||
|
@ -142,3 +142,31 @@ struct SafeFptr
|
||||
return ptr(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
namespace simplehash
|
||||
{
|
||||
enum : UINT64
|
||||
{
|
||||
djb2_hash0 = 5381
|
||||
};
|
||||
inline UINT64 djb2(const UINT8 *str, UINT64 hash = djb2_hash0)
|
||||
{
|
||||
UINT8 c;
|
||||
while ((c = *str++))
|
||||
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||
return hash;
|
||||
}
|
||||
inline UINT64 djb2_n2(const unsigned char *str, size_t len, UINT64 hash = djb2_hash0)
|
||||
{
|
||||
while (len--)
|
||||
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
|
||||
return hash;
|
||||
}
|
||||
inline UINT64 hashByteArraySTD(const std::string &b, UINT64 h = djb2_hash0)
|
||||
{
|
||||
return djb2_n2((const unsigned char *)b.c_str(), b.size(), h);
|
||||
}
|
||||
inline UINT64 hashCharArray(const void *lp)
|
||||
{
|
||||
return djb2(reinterpret_cast<const UINT8 *>(lp));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user