add basic support for x64

This commit is contained in:
Akash Mozumdar 2018-08-04 22:27:10 -04:00
parent 959d7c2dd5
commit aba5dadc6a
10 changed files with 627 additions and 7 deletions

View File

@ -29,5 +29,9 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/Build)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
add_subdirectory(host)
add_subdirectory(vnrhook)
if (${x64})
add_subdirectory(vnrhook64)
else(${x64})
add_subdirectory(vnrhook)
endif(${x64})
add_subdirectory(GUI)

View File

@ -27,10 +27,10 @@
"name": "x64-Debug",
"generator": "Ninja",
"configurationType": "Debug",
"inheritEnvironments": [ "msvc_x86_x64" ],
"inheritEnvironments": [ "msvc_x64" ],
"buildRoot": "${workspaceRoot}\\Builds\\${name}",
"installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "",
"cmakeCommandArgs": "-Dx64=1",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
},
@ -38,10 +38,10 @@
"name": "x64-Release",
"generator": "Ninja",
"configurationType": "Release",
"inheritEnvironments": [ "msvc_x86_x64" ],
"inheritEnvironments": [ "msvc_x64" ],
"buildRoot": "${workspaceRoot}\\Builds\\${name}",
"installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
"cmakeCommandArgs": "",
"cmakeCommandArgs": "-Dx64=1",
"buildCommandArgs": "-v",
"ctestCommandArgs": ""
}

View File

@ -1,4 +1,5 @@
#pragma once
#include "types.h"
// vnrhook/const.h
// 8/23/2013 jichi
@ -50,7 +51,7 @@ enum { MAX_HOOK = 64 }; // must be larger than HOOK_FUN_COUNT
//enum { HOOK_SECTION_SIZE = 0x2000 }; // default ITH value
// jichi 1/16/2015: Change to a very large number to prevent crash
//enum { MAX_HOOK = 0x100 }; // must be larger than HookFunCount
enum { HOOK_SECTION_SIZE = MAX_HOOK * 0x100 }; // default ITH value is 0x2000 for 32 hook (0x100 per hook)
enum { HOOK_SECTION_SIZE = MAX_HOOK * sizeof(Hook) * 2 }; // default ITH value is 0x2000 for 32 hook (0x100 per hook)
// jichi 375/2014: Add offset of pusha/pushad
// http://faydoc.tripod.com/cpu/pushad.htm

View File

@ -340,7 +340,7 @@ DWORD WINAPI ReaderThread(LPVOID threadParam)
{
TextHook* hook = (TextHook*)threadParam;
BYTE buffer[PIPE_BUFFER_SIZE] = {};
char testChar;
char testChar = 0;
unsigned int changeCount = 0;
const char* currentAddress = (char*)hook->hp.address;
while (true)

46
vnrhook64/CMakeLists.txt Normal file
View File

@ -0,0 +1,46 @@
project(vnrhook)
include_directories(../vnrhook)
set(vnrhook_src
../vnrhook/include/const.h
../vnrhook/include/defs.h
../vnrhook/include/types.h
src/main.cc
src/main.h
src/pipe.cc
src/hijack/texthook.cc
src/hijack/texthook.h
)
add_library(vnrhook SHARED ${vnrhook_src})
set_source_files_properties(
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
PROPERTIES
# CMAKE_ASM_MASM_FLAGS /safeseh # CMake bug 14711: http://www.cmake.org/Bug/view.php?id=14711
COMPILE_FLAGS /safeseh
)
set_target_properties(vnrhook PROPERTIES
LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO"
)
target_compile_options(vnrhook PRIVATE
/EHa
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
set(vnrhook_libs
Version.lib
)
target_link_libraries(vnrhook ${vnrhook_libs})
target_compile_definitions(vnrhook
PRIVATE
ITH_HAS_CRT
ITH_HAS_SEH
_CRT_NON_CONFORMING_SWPRINTFS
)

View File

@ -0,0 +1,187 @@
// texthook.cc
// 8/24/2013 jichi
// Branch: ITH_DLL/texthook.cpp, rev 128
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
# pragma warning (disable:4018) // C4018: sign/unsigned mismatch
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
#endif // _MSC_VER
#include "../main.h"
#include "texthook.h"
#include "include/const.h"
//#include "winseh/winseh.h"
//#define ConsoleOutput(...) (void)0 // jichi 9/17/2013: I don't need this ><
// - Global variables -
// 10/14/2014 jichi: disable GDI hooks
static bool gdi_hook_enabled_ = true; // enable GDI by default
static bool gdiplus_hook_enabled_ = false; // disable GDIPlus by default
bool GDIHooksEnabled() { return ::gdi_hook_enabled_; }
bool GDIPlusHooksEnabled() { return ::gdiplus_hook_enabled_; }
void EnableGDIHooks() { ::gdi_hook_enabled_ = true; }
void EnableGDIPlusHooks() { ::gdiplus_hook_enabled_ = true; }
void DisableGDIHooks() { ::gdi_hook_enabled_ = false; }
void DisableGDIPlusHooks() { ::gdiplus_hook_enabled_ = false; }
//FilterRange filter[8];
DWORD flag,
enter_count;
TextHook *hookman,
*current_available;
// - TextHook methods -
// jichi 12/2/2013: This function mostly return 0.
// It return the hook address only for auxiliary case.
// However, because no known hooks are auxiliary, this function always return 0.
//
// jichi 5/11/2014:
// - dwDataBase: the stack address
// - dwRetn: the return address of the hook
int TextHook::InsertHook()
{
int ok = 1;
//ConsoleOutput("vnrcli:InsertHook: enter");
WaitForSingleObject(hmMutex, 0);
if (hp.type & DIRECT_READ) ok = InsertReadCode();
else
{
ConsoleOutput("only /R (read) codes supported in 64 bit");
}
ReleaseMutex(hmMutex);
//ConsoleOutput("vnrcli:InsertHook: leave");
return ok;
}
DWORD WINAPI ReaderThread(LPVOID threadParam)
{
TextHook* hook = (TextHook*)threadParam;
BYTE buffer[PIPE_BUFFER_SIZE] = {};
char testChar = 0;
unsigned int changeCount = 0;
const char* currentAddress = (char*)hook->hp.address;
while (true)
{
Sleep(50);
if (testChar == *currentAddress)
{
changeCount = 0;
continue;
}
testChar = *currentAddress;
if (++changeCount > 10)
{
ConsoleOutput("NextHooker: memory constantly changing, useless to read");
ConsoleOutput("NextHooker: remove read code");
break;
}
int dataLen;
if (hook->hp.type & USING_UNICODE)
dataLen = wcslen((const wchar_t*)currentAddress) * 2;
else
dataLen = strlen(currentAddress);
*(DWORD*)buffer = hook->hp.address;
*(DWORD*)(buffer + 4) = 0;
*(DWORD*)(buffer + 8) = 0;
memcpy(buffer + HEADER_SIZE, currentAddress, dataLen);
DWORD unused;
WriteFile(::hookPipe, buffer, dataLen + HEADER_SIZE, &unused, nullptr);
if (hook->hp.offset == 0) continue;
currentAddress += dataLen + hook->hp.offset;
testChar = *currentAddress;
}
hook->ClearHook();
return 0;
}
int TextHook::InsertReadCode()
{
hp.hook_len = 0x40;
//Check if the new hook range conflict with existing ones. Clear older if conflict.
TextHook *it = hookman;
for (int i = 0; i < currentHook; it++) {
if (it->Address())
i++;
if (it == this)
continue;
if ((it->Address() >= hp.address && it->Address() < hp.hook_len + hp.address) || (it->Address() <= hp.address && it->Address() + it->Length() > hp.address))
it->ClearHook();
}
//if (!IthGetMemoryRange((LPCVOID)hp.address, 0, 0))
//{
// ConsoleOutput("cannot access read address");
// return no;
//}
hp.readerHandle = CreateThread(nullptr, 0, ReaderThread, this, 0, nullptr);
return yes;
}
int TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag)
{
WaitForSingleObject(hmMutex, 0);
hp = h;
hp.type |= set_flag;
if (name && name != hook_name) {
SetHookName(name);
}
currentHook++;
current_available = this+1;
while (current_available->Address())
current_available++;
ReleaseMutex(hmMutex);
return 1;
}
int TextHook::RemoveReadCode()
{
if (!hp.address) return no;
TerminateThread(hp.readerHandle, 0);
CloseHandle(hp.readerHandle);
return yes;
}
int TextHook::ClearHook()
{
int err;
WaitForSingleObject(hmMutex, 0);
ConsoleOutput("vnrcli:RemoveHook: enter");
err = RemoveReadCode();
NotifyHookRemove(hp.address);
if (hook_name) {
delete[] hook_name;
hook_name = nullptr;
}
memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH
//if (current_available>this)
// current_available = this;
currentHook--;
ConsoleOutput("vnrcli:RemoveHook: leave");
ReleaseMutex(hmMutex);
return err;
}
int TextHook::SetHookName(LPCSTR name)
{
name_length = strlen(name) + 1;
if (hook_name)
delete[] hook_name;
hook_name = new char[name_length];
//ITH_MEMSET_HEAP(hook_name, 0, sizeof(wchar_t) * name_length); // jichi 9/26/2013: zero memory
strcpy(hook_name, name);
return 0;
}
// EOF

View File

@ -0,0 +1,78 @@
#pragma once
// texthook.h
// 8/24/2013 jichi
// Branch: IHF_DLL/IHF_CLIENT.h, rev 133
//
// 8/24/2013 TODO:
// - Clean up this file
// - Reduce global variables. Use namespaces or singleton classes instead.
#include <string>
#include <unordered_map>
#include "include/types.h"
#include <windows.h>
extern int currentHook;
extern WCHAR dll_mutex[];
//extern WCHAR dll_name[];
extern DWORD trigger;
//extern DWORD current_process_id;
// jichi 6/3/2014: Get memory range of the current module
extern DWORD processStartAddress,
processStopAddress;
void InitFilterTable();
// jichi 9/25/2013: This class will be used by NtMapViewOfSectionfor
// interprocedure communication, where constructor/destructor will NOT work.
class TextHook : public Hook
{
int InsertHookCode();
int InsertReadCode();
int UnsafeInsertHookCode();
DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn);
int RemoveHookCode();
int RemoveReadCode();
int SetHookName(LPCSTR name);
public:
int InsertHook();
int InitHook(const HookParam &hp, LPCSTR name = 0, WORD set_flag = 0);
DWORD Send(DWORD dwDataBase, DWORD dwRetn);
int ClearHook();
int GetLength(DWORD base, DWORD in); // jichi 12/25/2013: Return 0 if failed
};
extern TextHook *hookman,
*current_available;
//void InitDefaultHook();
struct FilterRange { DWORD lower, upper; };
extern FilterRange *filter;
extern bool running,
live;
extern HANDLE hookPipe,
hmMutex;
DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter);
DWORD WINAPI CommandPipe(LPVOID lpThreadParameter);
DWORD WINAPI PipeManager(LPVOID unused);
//void RequestRefreshProfile();
//typedef DWORD (*InsertHookFun)(DWORD);
//typedef DWORD (*IdentifyEngineFun)();
//typedef DWORD (*InsertDynamicHookFun)(LPVOID addr, DWORD frame, DWORD stack);
//extern IdentifyEngineFun IdentifyEngine;
//extern InsertDynamicHookFun InsertDynamicHook;
// jichi 9/28/2013: Protect pipeline in wine
void CliLockPipe();
void CliUnlockPipe();
enum : int { yes = 0, no = 1 };
// EOF

150
vnrhook64/src/main.cc Normal file
View File

@ -0,0 +1,150 @@
// main.cc
// 8/24/2013 jichi
// Branch: ITH_DLL/main.cpp, rev 128
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
#endif // _MSC_VER
#include "main.h"
#include "hijack/texthook.h"
#include "include/defs.h"
// Global variables
// jichi 6/3/2014: memory range of the current module
DWORD processStartAddress,
processStopAddress;
enum { HOOK_BUFFER_SIZE = MAX_HOOK * sizeof(TextHook) };
//#define MAX_HOOK (HOOK_BUFFER_SIZE/sizeof(TextHook))
DWORD hook_buff_len = HOOK_BUFFER_SIZE;
WCHAR hm_section[0x100];
HANDLE hSection;
bool running;
int currentHook = 0,
user_hook_count = 0;
HANDLE
hFile,
hMutex,
hmMutex;
HMODULE currentModule;
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID unused)
{
static HANDLE pipeThread;
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
{
static bool attached_ = false;
if (attached_) // already attached
{
return TRUE;
}
attached_ = true;
DisableThreadLibraryCalls(hModule);
swprintf(hm_section, ITH_SECTION_ L"%d", GetCurrentProcessId());
// jichi 9/25/2013: Interprocedural communication with vnrsrv.
hSection = CreateFileMappingW(INVALID_HANDLE_VALUE, nullptr, PAGE_EXECUTE_READWRITE, 0, HOOK_SECTION_SIZE, hm_section);
::hookman = nullptr;
// Artikash 6/20/2018: This crashes certain games (https://vndb.org/v7738). No idea why.
::hookman = (TextHook*)MapViewOfFile(hSection, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, HOOK_SECTION_SIZE / 2);
::processStartAddress = (DWORD)GetModuleHandleW(nullptr);
{
wchar_t hm_mutex[0x100];
swprintf(hm_mutex, ITH_HOOKMAN_MUTEX_ L"%d", GetCurrentProcessId());
::hmMutex = CreateMutexW(nullptr, FALSE, hm_mutex);
}
{
wchar_t dll_mutex[0x100];
swprintf(dll_mutex, ITH_PROCESS_MUTEX_ L"%d", GetCurrentProcessId());
DWORD exists;
::hMutex = CreateMutexW(nullptr, TRUE, dll_mutex); // jichi 9/18/2013: own is true, make sure the injected dll is singleton
if (GetLastError() == ERROR_ALREADY_EXISTS)
return FALSE;
}
::running = true;
::current_available = ::hookman;
::currentModule = hModule;
pipeThread = CreateThread(nullptr, 0, PipeManager, 0, 0, nullptr);
} break;
case DLL_PROCESS_DETACH:
{
static bool detached_ = false;
if (detached_) // already detached
return TRUE;
detached_ = true;
// jichi 10/2/2103: Cannot use __try in functions that require object unwinding
//ITH_TRY {
::running = false;
if (pipeThread) {
WaitForSingleObject(pipeThread, TIMEOUT);
CloseHandle(pipeThread);
}
for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++)
man->ClearHook();
//if (ith_has_section)
UnmapViewOfFile(::hookman);
CloseHandle(hSection);
CloseHandle(hMutex);
CloseHandle(hmMutex);
//} ITH_EXCEPT {}
} break;
}
return TRUE;
}
//extern "C" {
DWORD NewHook(const HookParam &hp, LPCSTR lpname, DWORD flag)
{
std::string name = lpname;
int current = ::current_available - ::hookman;
if (current < MAX_HOOK) {
//flag &= 0xffff;
//if ((flag & HOOK_AUXILIARY) == 0)
flag |= HOOK_ADDITIONAL;
if (name[0] == '\0')
{
name = "UserHook" + std::to_string(user_hook_count++);
}
ConsoleOutput(("vnrcli:NewHook: try inserting hook: " + name).c_str());
// jichi 7/13/2014: This function would raise when too many hooks added
::hookman[current].InitHook(hp, name.c_str(), flag & 0xffff);
if (::hookman[current].InsertHook() == 0) {
ConsoleOutput(("vnrcli:NewHook: inserted hook: " + name).c_str());
NotifyHookInsert(hp, name.c_str());
} else
ConsoleOutput("vnrcli:NewHook:WARNING: failed to insert hook");
}
return 0;
}
DWORD RemoveHook(DWORD addr)
{
for (int i = 0; i < MAX_HOOK; i++)
if (::hookman[i].Address ()== addr) {
::hookman[i].ClearHook();
return 0;
}
return 0;
}
// EOF

27
vnrhook64/src/main.h Normal file
View File

@ -0,0 +1,27 @@
#pragma once
// main.h
// 8/23/2013 jichi
// Branch: ITH/IHF_DLL.h, rev 66
#include <Windows.h>
#include "include/const.h"
#include "include/types.h"
void ConsoleOutput(LPCSTR text); // jichi 12/25/2013: Used to return length of sent text
void NotifyHookInsert(HookParam hp, LPCSTR name);
void NotifyHookRemove(DWORD addr);
DWORD NewHook(const HookParam &hp, LPCSTR name, DWORD flag = HOOK_ENGINE);
DWORD RemoveHook(DWORD addr);
DWORD SwitchTrigger(DWORD on);
DWORD GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name);
// 10/14/2014 jichi: disable GDI hooks
void EnableGDIHooks();
void EnableGDIPlusHooks();
void DisableGDIHooks();
void DisableGDIPlusHooks();
bool GDIHooksEnabled();
bool GDIPlusHooksEnabled();
// EOF

127
vnrhook64/src/pipe.cc Normal file
View File

@ -0,0 +1,127 @@
// pipe.cc
// 8/24/2013 jichi
// Branch: ITH_DLL/pipe.cpp, rev 66
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
#endif // _MSC_VER
#include "main.h"
#include "include/defs.h"
#include "hijack/texthook.h"
HANDLE hookPipe;
extern HMODULE currentModule;
DWORD WINAPI PipeManager(LPVOID unused)
{
enum { STANDARD_WAIT = 50 };
while (::running)
{
DWORD count;
BYTE buffer[PIPE_BUFFER_SIZE];
HANDLE hostPipe = ::hookPipe = INVALID_HANDLE_VALUE,
pipeAcquisitionMutex = CreateMutexW(nullptr, TRUE, ITH_GRANTPIPE_MUTEX);
while (::hookPipe == INVALID_HANDLE_VALUE || hostPipe == INVALID_HANDLE_VALUE)
{
Sleep(STANDARD_WAIT);
if (::hookPipe == INVALID_HANDLE_VALUE)
{
::hookPipe = CreateFileW(ITH_TEXT_PIPE, GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
}
if (hostPipe == INVALID_HANDLE_VALUE)
{
hostPipe = CreateFileW(ITH_COMMAND_PIPE, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr);
}
}
*(DWORD*)buffer = GetCurrentProcessId();
WriteFile(::hookPipe, buffer, sizeof(DWORD), &count, nullptr);
ReleaseMutex(pipeAcquisitionMutex);
CloseHandle(pipeAcquisitionMutex);
ConsoleOutput("vnrcli:WaitForPipe: pipe connected");
ConsoleOutput("only read codes are supported on x64: engine disabled");
while (::running)
{
if (!ReadFile(hostPipe, buffer, PIPE_BUFFER_SIZE / 2, &count, nullptr)) // Artikash 5/21/2018: why / 2? wchar_t?
{
break;
}
DWORD command = *(DWORD*)buffer;
switch (command)
{
case HOST_COMMAND_NEW_HOOK:
buffer[count] = 0;
NewHook(*(HookParam *)(buffer + sizeof(DWORD)), // Hook parameter
(LPSTR)(buffer + 4 + sizeof(HookParam)), // Hook name
0
);
break;
case HOST_COMMAND_REMOVE_HOOK:
{
TextHook *in = hookman;
for (int i = 0; i < currentHook; in++)
{
if (in->Address()) i++;
if (in->Address() == *(DWORD *)(buffer + sizeof(DWORD))) // Hook address
{
break;
}
}
if (in->Address())
{
in->ClearHook();
}
}
break;
case HOST_COMMAND_DETACH:
::running = false;
break;
}
}
CloseHandle(::hookPipe);
CloseHandle(hostPipe);
}
FreeLibraryAndExitThread(::currentModule, 0);
return 0;
}
void ConsoleOutput(LPCSTR text)
{
BYTE buffer[PIPE_BUFFER_SIZE];
*(DWORD*)buffer = HOST_NOTIFICATION;
*(DWORD*)(buffer + sizeof(DWORD)) = HOST_NOTIFICATION_TEXT;
strcpy((char*)buffer + sizeof(DWORD) * 2, text);
DWORD unused;
WriteFile(::hookPipe, buffer, strlen(text) + sizeof(DWORD) * 2, &unused, nullptr);
}
void NotifyHookInsert(HookParam hp, LPCSTR name)
{
BYTE buffer[PIPE_BUFFER_SIZE];
*(DWORD*)buffer = HOST_NOTIFICATION;
*(DWORD*)(buffer + sizeof(DWORD)) = HOST_NOTIFICATION_NEWHOOK;
*(HookParam*)(buffer + sizeof(DWORD) * 2) = hp;
strcpy((char*)buffer + sizeof(DWORD) * 2 + sizeof(HookParam), name);
DWORD unused;
WriteFile(::hookPipe, buffer, strlen(name) + sizeof(DWORD) * 2 + sizeof(HookParam), &unused, nullptr);
return;
}
void NotifyHookRemove(DWORD addr)
{
BYTE buffer[sizeof(DWORD) * 3];
*(DWORD*)buffer = HOST_NOTIFICATION;
*(DWORD*)(buffer + sizeof(DWORD)) = HOST_NOTIFICATION_RMVHOOK;
*(DWORD*)(buffer + sizeof(DWORD) * 2) = addr;
DWORD unused;
WriteFile(::hookPipe, buffer, sizeof(DWORD) * 3, &unused, nullptr);
return;
}
// EOF