mirror of
https://github.com/HIllya51/LunaHook.git
synced 2024-11-27 15:54:03 +08:00
150 lines
5.5 KiB
C++
150 lines
5.5 KiB
C++
#include"V8.h"
|
|
|
|
/**
|
|
* Artikash 7/15/2018: Insert Tyranobuilder hook
|
|
* Sample game: https://vndb.org/v22252: /HWN-8:-1C@233A54:yuika_t.exe
|
|
* Artikash 9/11/2018: This is more than just Tyranobuilder. It's actually a hook for the V8 JavaScript runtime
|
|
* Sample game: https://www.freem.ne.jp/win/game/9672: /HQ8@2317A0:Prison.exe This new hook seems more reliable
|
|
* Nevermind both of those, just hook v8::String::Write https://v8docs.nodesource.com/node-0.8/d2/db3/classv8_1_1_string.html
|
|
* v8::String::Write - 55 - push ebp
|
|
* v8::String::Write+1- 8B EC - mov ebp,esp
|
|
* v8::String::Write+3- 8B 45 14 - mov eax,[ebp+14]
|
|
* v8::String::Write+6- 8B 55 10 - mov edx,[ebp+10]
|
|
* v8::String::Write+9- 50 - push eax
|
|
* v8::String::Write+A- 8B 45 0C - mov eax,[ebp+0C]
|
|
* v8::String::Write+D- 52 - push edx
|
|
* v8::String::Write+E- 8B 55 08 - mov edx,[ebp+08]
|
|
* v8::String::Write+11- 50 - push eax
|
|
* v8::String::Write+12- 52 - push edx
|
|
* v8::String::Write+13- 51 - push ecx
|
|
* v8::String::Write+14- E8 B7C7FFFF - call 6EF630 ; actual writing happens in this function, hooking after is possible
|
|
* v8::String::Write+19- 83 C4 14 - add esp,14 { 20 }
|
|
* v8::String::Write+1C- 5D - pop ebp
|
|
* v8::String::Write+1D- C2 1000 - ret 0010 { 16 }
|
|
*/
|
|
void SpecialHookV8String(hook_stack*, HookParam *hp, uintptr_t* data, uintptr_t* split, size_t* len)
|
|
{
|
|
DWORD ecx = *data;
|
|
DWORD strPtr = *(DWORD*)ecx;
|
|
*data = strPtr + 0xb;
|
|
*len = *(short*)(strPtr + 7);
|
|
if(wcslen((wchar_t*)*data)*2<*len)*len=0;
|
|
|
|
//if (*len < 12) *split = 1; // To ensure this is caught by cyclic repetition detection, split if there's 6+ wide chars
|
|
//*split = *(DWORD*)((BYTE*)hp->split + dwDatabase);
|
|
}
|
|
|
|
bool InsertV8Hook(HMODULE module)
|
|
{
|
|
auto [minAddress, maxAddress] = Util::QueryModuleLimits(module);
|
|
for (const auto& pattern : Array<const BYTE[3]>{ { 0x55, 0x8b, 0xec }, { 0x55, 0x89, 0xe5 } })
|
|
{
|
|
int matches = Util::SearchMemory(pattern, sizeof(pattern), PAGE_EXECUTE, minAddress, maxAddress).size(), requiredRecords = matches * 20;
|
|
if (matches > 10'000 && requiredRecords > spDefault.maxRecords)
|
|
{
|
|
memcpy(spDefault.pattern, pattern, spDefault.length = sizeof(pattern));
|
|
spDefault.maxRecords = requiredRecords;
|
|
}
|
|
}
|
|
std::tie(spDefault.minAddress, spDefault.maxAddress) = std::tuple{ minAddress, maxAddress };
|
|
ConsoleOutput("JavaScript hook is known to be low quality: try searching for hooks if you don't like it");
|
|
HookParam hp;
|
|
hp.address = (DWORD)GetProcAddress(module, "?Write@String@v8@@QBEHPAGHHH@Z");
|
|
hp.offset=get_reg(regs::ecx);
|
|
hp.type = CODEC_UTF16 | USING_STRING;
|
|
hp.text_fun = SpecialHookV8String;
|
|
auto succ=NewHook(hp, "JavaScript");
|
|
const BYTE bytes[] = {
|
|
0x83, 0xc4, XX, // add esp,XX
|
|
0x5d, // pop ebp
|
|
0xc2 // ret
|
|
};
|
|
if (DWORD addr = MemDbg::findBytes(bytes, sizeof(bytes), hp.address, hp.address + 0x30))
|
|
{
|
|
hp.address = addr;
|
|
hp.offset = 0x8 + *(BYTE*)(addr + 2); // second argument + amount that the stack pointer is offset from arguments
|
|
hp.type = CODEC_UTF16 | USING_STRING | NO_CONTEXT;
|
|
hp.length_offset = (0x10 + *(BYTE*)(addr + 2)) / 4; // fourth argument + amount that the stack pointer is offset from arguments
|
|
hp.text_fun = nullptr;
|
|
succ|=NewHook(hp, "JavaScript2");
|
|
}
|
|
return succ;
|
|
}
|
|
bool hookv8addr(HMODULE module) {
|
|
auto [minAddress, maxAddress] = Util::QueryModuleLimits(module);
|
|
const BYTE bytes[] = {
|
|
0x89,0xc1,
|
|
0x0f,0xb7,0xd8,
|
|
0x81,0xe1,0x00,0xfc,0x00,0x00,
|
|
0x81,0xf9,0x00,0xd8,0x00,0x00
|
|
};
|
|
ULONG addr = MemDbg::findBytes(bytes, sizeof(bytes), minAddress, maxAddress);
|
|
if (!addr) {
|
|
return false;
|
|
}
|
|
HookParam hp;
|
|
hp.address = addr;
|
|
|
|
hp.offset=get_reg(regs::eax);
|
|
|
|
hp.type = CODEC_UTF16 | NO_CONTEXT;
|
|
|
|
return NewHook(hp, "electronW");
|
|
}
|
|
|
|
bool hookv8exports(HMODULE module) {
|
|
|
|
auto addr = GetProcAddress(module, "?Write@String@v8@@QBEHPAVIsolate@2@PAGHHH@Z");
|
|
if (addr == 0)return false;
|
|
HookParam hp;
|
|
hp.address = (uint64_t)addr;
|
|
hp.type = USING_STRING | CODEC_UTF16 | DATA_INDIRECT;
|
|
hp.offset=get_reg(regs::ecx);
|
|
hp.padding = 11;
|
|
hp.index = 0;
|
|
return NewHook(hp, "Write@String@v8");
|
|
}
|
|
|
|
namespace{
|
|
bool hookstringlength(HMODULE hm){
|
|
auto Length=GetProcAddress(hm,"?Length@String@v8@@QBEHXZ");
|
|
static uintptr_t WriteUtf8;
|
|
static uintptr_t Utf8Length;
|
|
WriteUtf8=(uintptr_t)GetProcAddress(hm,"?WriteUtf8@String@v8@@QBEHPADHPAHH@Z");
|
|
Utf8Length=(uintptr_t)GetProcAddress(hm,"?Utf8Length@String@v8@@QBEHXZ");
|
|
if(Length==0||WriteUtf8==0||Utf8Length==0)return false;
|
|
HookParam hp;
|
|
hp.address=(uintptr_t)Length;
|
|
hp.type=USING_STRING|CODEC_UTF8;
|
|
hp.text_fun=
|
|
[](hook_stack* stack, HookParam* hp, uintptr_t* data, uintptr_t* split, size_t* len)
|
|
{
|
|
auto length=((size_t(__thiscall*)(void*))Utf8Length)((void*)stack->ecx);
|
|
if(!length)return;
|
|
auto u8str=new char[length+1];
|
|
int writen;
|
|
((size_t(__thiscall*)(void*,char*,int,int*,int))WriteUtf8)((void*)stack->ecx,u8str,length,&writen,0);
|
|
*data=(uintptr_t)u8str;
|
|
*len=length;
|
|
|
|
};
|
|
return NewHook(hp,"v8::String::Length");
|
|
}
|
|
}
|
|
bool V8::attach_function_() {
|
|
for (const wchar_t* moduleName : { (const wchar_t*)NULL, L"node.dll", L"nw.dll" }) {
|
|
auto hm=GetModuleHandleW(moduleName);
|
|
if(hm==0)continue;
|
|
if (GetProcAddress(hm, "?Write@String@v8@@QBEHPAGHHH@Z")==0)continue;
|
|
|
|
bool b1= InsertV8Hook(hm);
|
|
bool b2=hookv8addr(hm);
|
|
bool b3=hookv8exports(hm);
|
|
b1=hookstringlength(hm)||b1;
|
|
if(b1||b2||b3){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|