mireado

starting commit

#pragma once
// ith/common/defs.h
// 8/23/2013 jichi
// DLL files
//#define ITH_SERVER_DLL L"vnrsrv.dll"
//#define ITH_CLIENT_DLL L"vnrcli.dll"
//#define ITH_CLIENT_XP_DLL L"vnrclixp.dll"
////#define ITH_CLIENT_UX_DLL L"vnrcliux.dll"
//#define ITH_ENGINE_DLL L"vnreng.dll"
//#define ITH_ENGINE_XP_DLL L"vnrengxp.dll"
//#define ITH_ENGINE_UX_DLL L"vnrengux.dll"
#define ITH_DLL L"vnrhook.dll"
#define ITH_DLL_XP L"vnrhookxp.dll"
// Pipes
#define ITH_TEXT_PIPE L"\\??\\pipe\\VNR_TEXT"
#define ITH_COMMAND_PIPE L"\\??\\pipe\\VNR_COMMAND"
// Sections
#define ITH_SECTION_ L"VNR_SECTION_" // _%d
// Mutex
#define ITH_PROCESS_MUTEX_ L"VNR_PROCESS_" // ITH_%d
#define ITH_HOOKMAN_MUTEX_ L"VNR_HOOKMAN_" // ITH_HOOKMAN_%d
#define ITH_DETACH_MUTEX_ L"VNR_DETACH_" // ITH_DETACH_%d
#define ITH_GRANTPIPE_MUTEX L"VNR_GRANT_PIPE" // ITH_GRANT_PIPE
//#define ITH_ENGINE_MUTEX L"VNR_ENGINE" // ITH_ENGINE
#define ITH_CLIENT_MUTEX L"VNR_CLIENT" // ITH_DLL_RUNNING
#define ITH_SERVER_MUTEX L"VNR_SERVER" // ITH_RUNNING
#define ITH_SERVER_HOOK_MUTEX L"VNR_SERVER_HOOK" // original
// Events
#define ITH_REMOVEHOOK_EVENT L"VNR_REMOVE_HOOK" // ITH_REMOVE_HOOK
#define ITH_MODIFYHOOK_EVENT L"VNR_MODIFY_HOOK" // ITH_MODIFY_HOOK
#define ITH_PIPEEXISTS_EVENT L"VNR_PIPE_EXISTS" // ITH_PIPE_EXIST
// EOF
#pragma once
// ith/common/except.h
// 9/17/2013 jichi
#define ITH_RAISE (*(int*)0 = 0) // raise C000005, for debugging only
#ifdef ITH_HAS_SEH
# define ITH_TRY __try
# define ITH_EXCEPT __except(EXCEPTION_EXECUTE_HANDLER)
# define ITH_WITH_SEH(...) \
ITH_TRY { __VA_ARGS__; } ITH_EXCEPT {}
#else // for old msvcrt.dll on Windows XP that does not have exception handler
// Currently, only with_seh is implemented. Try and catch are not.
# define ITH_TRY if (true)
# define ITH_EXCEPT else
# include "winseh/winseh.h"
# define ITH_WITH_SEH(...) seh_with(__VA_ARGS__)
#endif // ITH_HAS_SEH
// EOF
#pragma once
// ith/common/growl.h
// 9/17/2013 jichi
//#ifdef ITH_HAS_GROWL
#include <windows.h>
#include "ith/common/string.h"
#define ITH_MSG_A(_msg) MessageBoxA(nullptr, _msg, "VNR Message", MB_OK)
#define ITH_MSG(_msg) MessageBoxW(nullptr, _msg, L"VNR Message", MB_OK)
#define ITH_WARN(_msg) MessageBoxW(nullptr, _msg, L"VNR Warning", MB_OK)
#define ITH_ERROR(_msg) MessageBoxW(nullptr, _msg, L"VNR Error", MB_OK)
inline void ITH_GROWL_DWORD(DWORD value)
{
WCHAR buf[100];
swprintf(buf, L"DWORD: %x", value);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD2(DWORD v, DWORD v2)
{
WCHAR buf[100];
swprintf(buf, L"DWORD2: %x,%x", v, v2);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD3(DWORD v, DWORD v2, DWORD v3)
{
WCHAR buf[100];
swprintf(buf, L"DWORD3: %x,%x,%x", v, v2, v3);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD4(DWORD v, DWORD v2, DWORD v3, DWORD v4)
{
WCHAR buf[100];
swprintf(buf, L"DWORD4: %x,%x,%x,%x", v, v2, v3, v4);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD5(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5)
{
WCHAR buf[100];
swprintf(buf, L"DWORD5: %x,%x,%x,%x,%x", v, v2, v3, v4, v5);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD6(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6)
{
WCHAR buf[100];
swprintf(buf, L"DWORD6: %x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD7(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7)
{
WCHAR buf[100];
swprintf(buf, L"DWORD7: %x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD8(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8)
{
WCHAR buf[100];
swprintf(buf, L"DWORD8: %x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD9(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8, DWORD v9)
{
WCHAR buf[100];
swprintf(buf, L"DWORD9: %x,%x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8, v9);
ITH_MSG(buf);
}
inline void ITH_GROWL(DWORD v) { ITH_GROWL_DWORD(v); }
inline void ITH_GROWL(LPCWSTR v) { ITH_MSG(v); }
inline void ITH_GROWL(LPCSTR v) { ITH_MSG_A(v); }
//#endif // ITH_HAS_GROWL
// EOF
#pragma once
// ith/common/memory.h
// 8/23/2013 jichi
// Branch: ITH/mem.h, revision 66
#ifndef ITH_HAS_HEAP
# define ITH_MEMSET_HEAP(...) ::memset(__VA_ARGS__)
#else
# define ITH_MEMSET_HEAP(...) (void)0
// Defined in kernel32.lilb
extern "C" {
// PVOID RtlAllocateHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ SIZE_T Size);
__declspec(dllimport) void * __stdcall RtlAllocateHeap(void *HeapHandle, unsigned long Flags, unsigned long Size);
// BOOLEAN RtlFreeHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ PVOID HeapBase);
__declspec(dllimport) int __stdcall RtlFreeHeap(void *HeapHandle, unsigned long Flags, void *HeapBase);
} // extern "C"
//NTSYSAPI
//BOOL
//NTAPI
//RtlFreeHeap(
// _In_ HANDLE hHeap,
// _In_ DWORD dwFlags,
// _In_ LPVOID lpMem
//);
extern void *hHeap; // defined in ith/sys.cc
inline void * __cdecl operator new(size_t lSize)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa366597%28v=vs.85%29.aspx
// HEAP_ZERO_MEMORY flag is critical. All new objects are assumed with zero initialized.
enum { HEAP_ZERO_MEMORY = 0x00000008 };
return RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize);
}
inline void __cdecl operator delete(void *pBlock)
{ RtlFreeHeap(::hHeap, 0, pBlock); }
inline void __cdecl operator delete[](void *pBlock)
{ RtlFreeHeap(::hHeap, 0, pBlock); }
#endif // ITH_HAS_HEAP
#pragma once
// ith/common/string.h
// 8/9/2013 jichi
// Branch: ITH/string.h, rev 66
#ifdef ITH_HAS_CRT // ITH is linked with msvcrt dlls
# include <cstdio>
# include <cstring>
#else
# define _INC_SWPRINTF_INL_
# define CRT_IMPORT __declspec(dllimport)
#include <windows.h> // for wchar_t
extern "C" {
CRT_IMPORT int swprintf(wchar_t *src, const wchar_t *fmt, ...);
CRT_IMPORT int sprintf(char *src, const char *fmt, ...);
CRT_IMPORT int swscanf(const wchar_t *src, const wchar_t *fmt, ...);
CRT_IMPORT int sscanf(const char *src, const char *fmt, ...);
CRT_IMPORT int wprintf(const wchar_t *fmt, ...);
CRT_IMPORT int printf(const char *fmt, ...);
CRT_IMPORT int _wputs(const wchar_t *src);
CRT_IMPORT int puts(const char *src);
CRT_IMPORT int _stricmp(const char *x, const char *y);
CRT_IMPORT int _wcsicmp(const wchar_t *x, const wchar_t *y);
//CRT_IMPORT size_t strlen(const char *);
//CRT_IMPORT size_t wcslen(const wchar_t *);
//CRT_IMPORT char *strcpy(char *,const char *);
//CRT_IMPORT wchar_t *wcscpy(wchar_t *,const wchar_t *);
CRT_IMPORT void *memmove(void *dst, const void *src, size_t sz);
CRT_IMPORT const char *strchr(const char *src, int val);
CRT_IMPORT int strncmp(const char *x, const char *y, size_t sz);
} // extern "C"
#endif // ITH_HAS_CRT
#pragma once
// ith/common/types.h
// 8/23/2013 jichi
// Branch: ITH/common.h, rev 128
#include <windows.h> // needed for windef types
/** jichi 3/7/2014: Add guessed comment
*
* DWORD addr absolute or relative address
* DWORD split esp offset of the split character
*
* http://faydoc.tripod.com/cpu/pushad.htm
* http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
* The order is the same as pushd
* EAX, ECX, EDX, EBX, ESP (original value), EBP, ESI, and EDI (if the current operand-size attribute is 32) and AX, CX, DX, BX, SP
* Negative values of 'data_offset' and 'sub_offset' refer to registers:-4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI
*/
struct HookParam {
// jichi 8/24/2013: For special hooks. Original name: DataFun
typedef void (*text_fun_t)(DWORD esp, HookParam *hp, BYTE index, DWORD *data, DWORD *split, DWORD *len);
// jichi 10/24/2014: Add filter function. Return the if skip the text
typedef bool (*filter_fun_t)(LPVOID str, DWORD *len, HookParam *hp, BYTE index);
// jichi 10/24/2014: Add generic hook function, return false if stop execution.
typedef bool (*hook_fun_t)(DWORD esp, HookParam *hp);
DWORD addr; // absolute or relative address
DWORD off, // offset of the data in the memory
ind, // ?
split, // esp offset of the split character = pusha offset - 4
split_ind; // ?
DWORD module, // hash of the module
function;
text_fun_t text_fun;
filter_fun_t filter_fun;
hook_fun_t hook_fun;
DWORD type; // flags
WORD length_offset; // index of the string length
BYTE hook_len, // ?
recover_len; // ?
// 2/2/2015: jichi number of times - 1 to run the hook
BYTE extra_text_count;
BYTE _unused; // jichi 2/2/2015: add a BYTE type to make to total sizeof(HookParam) even.
// 7/20/2014: jichi additional parameters for PSP games
DWORD user_flags,
user_value;
};
// jichi 6/1/2014: Structure of the esp for extern functions
struct HookStack
{
// pushad
DWORD edi, // -0x24
esi, // -0x20
ebp, // -0x1c
esp, // -0x18
ebx, // -0x14
edx, // -0x10
ecx, // -0xc
eax; // -0x8
// pushfd
DWORD eflags; // -0x4
DWORD retaddr; // 0
DWORD args[1]; // 0x4
};
struct SendParam {
DWORD type;
HookParam hp;
};
struct Hook { // size: 0x80
HookParam hp;
LPWSTR hook_name;
int name_length;
BYTE recover[0x68 - sizeof(HookParam)];
BYTE original[0x10];
DWORD Address() const { return hp.addr; }
DWORD Type() const { return hp.type; }
WORD Length() const { return hp.hook_len; }
LPWSTR Name() const { return hook_name; }
int NameLength() const { return name_length; }
};
// EOF
#pragma once
// dllconfig.h
// 8/23/2013 jichi
#include "ith/common/memory.h"
#include "ith/common/string.h"
#include "ntdll/ntdll.h"
// EOF
# dllconfig.pri
# 8/9/2013 jichi
# For linking ITH injectable dlls.
# The dll is self-containd and Windows-independent.
CONFIG += dll noqt #noeh nosafeseh
CONFIG -= embed_manifest_dll # dynamically load dlls
win32 {
CONFIG(eh): DEFINES += ITH_HAS_SEH # Do have exception handler in msvcrt.dll on Windows Vista and later
CONFIG(noeh): DEFINES -= ITH_HAS_SEH # Do not have exception handler in msvcrt.dll on Windows XP and before
}
include(../../../config.pri)
#win32 {
# CONFIG(noeh): include($$LIBDIR/winseh/winseh_safe.pri)
#}
# jichi 11/24/2013: Disable manual heap
DEFINES -= ITH_HAS_HEAP
# jichi 11/13/2011: disable swprinf warning
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
## Libraries
#LIBS += -lkernel32 -luser32 -lgdi32
LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
#LIBS += -L$$WDK7_HOME/lib/crt/i386 -lmsvcrt
#QMAKE_LFLAGS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # This will leave runtime flags in the dll
#LIBS += -L$$WDK8_HOME/lib/winv6.3/um/x86 -lntdll
HEADERS += $$PWD/dllconfig.h
# EOF
# hook.pro
# CONFIG += eh eha
# include(../dllconfig.pri)
# hookxp.pro
# CONFIG += noeh
# include(../dllconfig.pri)
# dllconfig.pri
# include(../../../config.pri)
# win32 {
# CONFIG(eh): DEFINES += ITH_HAS_SEH
# CONFIG(noeh): DEFINES -= ITH_HAS_SEH
# }
# config.pri
# CONFIG(eha) {
# message(CONFIG eha)
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
# QMAKE_CXXFLAGS_STL_ON += /EHa
# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
# }
#
# CONFIG(noeh) { # No Exception handler
# QMAKE_CXXFLAGS += /GR-
# QMAKE_CXXFLAGS_RTTI_ON -= /GR
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
# }
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set(vnrhook_src
cli.h
config.h
hook.h
main.cc
engine/engine.cc
engine/engine.h
engine/hookdefs.h
engine/match.cc
engine/match.h
engine/pchooks.cc
engine/pchooks.h
engine/util.cc
engine/util.h
hijack/texthook.cc
rpc/pipe.cc
tree/avl.h
${PROJECT_SOURCE_DIR}/ccutil/ccmacro.h
${PROJECT_SOURCE_DIR}/cpputil/cpplocale.h
${PROJECT_SOURCE_DIR}/cpputil/cppmarshal.h
${PROJECT_SOURCE_DIR}/cpputil/cppmath.h
${PROJECT_SOURCE_DIR}/cpputil/cpppath.h
${PROJECT_SOURCE_DIR}/cpputil/cppstring.h
${PROJECT_SOURCE_DIR}/cpputil/cpptype.h
${PROJECT_SOURCE_DIR}/cpputil/cppunicode.h
${PROJECT_SOURCE_DIR}/disasm/disasm.cc
${PROJECT_SOURCE_DIR}/memdbg/memdbg.h
${PROJECT_SOURCE_DIR}/memdbg/memsearch.cc
${PROJECT_SOURCE_DIR}/memdbg/memsearch.h
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.cc
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.h
${PROJECT_SOURCE_DIR}/winversion/winversion.cc
${PROJECT_SOURCE_DIR}/winversion/winversion.h
${common_src}
${import_src}
)
source_group("common" FILES ${common_src})
source_group("import" FILES ${import_src})
add_library(vnrhook SHARED ${vnrhook_src})
set(vnrhookxp_src ${vnrhook_src}
${PROJECT_SOURCE_DIR}/winseh/winseh.cc
${PROJECT_SOURCE_DIR}/winseh/winseh_safe.cc
${PROJECT_SOURCE_DIR}/winseh/winseh.h
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
)
enable_language(ASM_MASM)
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
)
add_library(vnrhookxp SHARED ${vnrhookxp_src})
set_target_properties(vnrhook vnrhookxp PROPERTIES
LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO"
)
target_compile_options(vnrhook PRIVATE
/EHa
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
target_compile_options(vnrhookxp PRIVATE
/GR-
# /EHs-c- # disable exception handling # CMake bug 15243: http://www.cmake.org/Bug/view.php?id=15243
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
if(TARGET vnrhookxp)
STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
endif(TARGET vnrhookxp)
set(vnrhook_libs
vnrsys
${WDK_HOME}/lib/wxp/i386/ntdll.lib
Version.lib
)
target_link_libraries(vnrhook ${vnrhook_libs})
target_link_libraries(vnrhookxp ${vnrhook_libs})
target_compile_definitions(vnrhook
PRIVATE
-DITH_HAS_SEH
)
target_compile_definitions(vnrhookxp
PRIVATE
)
install(TARGETS vnrhook vnrhookxp RUNTIME
DESTINATION .
CONFIGURATIONS Release
)
#pragma once
// cli.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 <windows.h>
//#define IHF
#include "config.h"
#include "hook.h"
// jichi 12/25/2013: Header in each message sent to vnrsrv
// There are totally three elements
// - 0x0 dwAddr hook address
// - 0x4 dwRetn return address
// - 0x8 dwSplit split value
#define HEADER_SIZE 0xc
extern int current_hook;
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;
template <class T, class D, class fComp, class fCopy, class fLength>
class AVLTree;
struct FunctionInfo {
DWORD addr;
DWORD module;
DWORD size;
LPWSTR name;
};
struct SCMP;
struct SCPY;
struct SLEN;
extern AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN> *tree;
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 UnsafeInsertHookCode();
DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn);
public:
int InsertHook();
int InsertHookCode();
int InitHook(const HookParam &hp, LPCWSTR name = 0, WORD set_flag = 0);
int InitHook(LPVOID addr, DWORD data, DWORD data_ind,
DWORD split_off, DWORD split_ind, WORD type, DWORD len_off = 0);
DWORD Send(DWORD dwDataBase, DWORD dwRetn);
int RecoverHook();
int RemoveHook();
int ClearHook();
int ModifyHook(const HookParam&);
int SetHookName(LPCWSTR name);
int GetLength(DWORD base, DWORD in); // jichi 12/25/2013: Return 0 if failed
void CoolDown(); // jichi 9/28/2013: flush instruction cache on wine
};
extern TextHook *hookman,
*current_available;
//void InitDefaultHook();
struct FilterRange { DWORD lower, upper; };
extern FilterRange *filter;
extern bool running,
live;
extern HANDLE hPipe,
hmMutex;
DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter);
DWORD WINAPI CommandPipe(LPVOID lpThreadParameter);
//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();
// EOF
#pragma once
// config.h
// 8/23/2013 jichi
// The first header file that are included by all source files.
#define IHF // for dll import
#include "ith/dllconfig.h"
// EOF
This diff could not be displayed because it is too large.
#pragma once
// engine/engine.h
// 8/23/2013 jichi
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
#include "config.h"
struct HookParam; // defined in ith types.h
namespace Engine {
// Global variables
extern wchar_t process_name_[MAX_PATH], // cached
process_path_[MAX_PATH]; // cached
extern DWORD module_base_,
module_limit_;
//extern LPVOID trigger_addr;
typedef bool (* trigger_fun_t)(LPVOID addr, DWORD frame, DWORD stack);
extern trigger_fun_t trigger_fun_;
bool InsertMonoHooks(); // Mono
// Wii engines
bool InsertGCHooks(); // Dolphin
bool InsertVanillawareGCHook();
// PS2 engines
bool InsertPCSX2Hooks(); // PCSX2
bool InsertMarvelousPS2Hook(); // http://marvelous.jp
bool InsertMarvelous2PS2Hook(); // http://marvelous.jp
bool InsertTypeMoonPS2Hook(); // http://typemoon.com
//bool InsertNamcoPS2Hook();
// PSP engines
void SpecialPSPHook(DWORD esp_base, HookParam *hp, DWORD *data, DWORD *split, DWORD *len); // General PSP extern hook
bool InsertPPSSPPHooks(); // PPSSPPWindows
bool InsertPPSSPPHLEHooks();
bool InsertOtomatePPSSPPHook(); // PSP otomate.jp, 0.9.9.0 only
bool Insert5pbPSPHook(); // PSP 5pb.jp
bool InsertAlchemistPSPHook(); // PSP Alchemist-net.co.jp, 0.9.8 only
bool InsertAlchemist2PSPHook(); // PSP Alchemist-net.co.jp
bool InsertBandaiNamePSPHook(); // PSP Bandai.co.jp
bool InsertBandaiPSPHook(); // PSP Bandai.co.jp
bool InsertBroccoliPSPHook(); // PSP Broccoli.co.jp
bool InsertFelistellaPSPHook(); // PSP felistella.co.jp
bool InsertCyberfrontPSPHook(); // PSP CYBERFRONT (closed)
bool InsertImageepochPSPHook(); // PSP Imageepoch.co.jp
bool InsertImageepoch2PSPHook();// PSP Imageepoch.co.jp
bool InsertKadokawaNamePSPHook(); // PSP Kadokawa.co.jp
bool InsertKonamiPSPHook(); // PSP Konami.jp
bool InsertTecmoPSPHook(); // PSP Koeitecmo.co.jp
//bool InsertTypeMoonPSPHook(); // PSP Typemoon.com
bool InsertOtomatePSPHook(); // PSP Otomate.jp, 0.9.8 only
//bool InsertOtomate2PSPHook(); // PSP otomate.jp >= 0.9.9.1
bool InsertIntensePSPHook(); // PSP Intense.jp
bool InsertKidPSPHook(); // PSP Kid-game.co.jp
bool InsertNippon1PSPHook(); // PSP Nippon1.jp
bool InsertNippon2PSPHook(); // PSP Nippon1.jp
bool InsertYetiPSPHook(); // PSP Yetigame.jp
bool InsertYeti2PSPHook(); // PSP Yetigame.jp
// PC engines
bool Insert2RMHook(); // 2RM - Adventure Engine
bool Insert5pbHook(); // 5pb.jp, PSP/PS3 games ported to PC
bool InsertAB2TryHook(); // Yane@AkabeiSoft2Try: YaneSDK.dll.
bool InsertAbelHook(); // Abel
bool InsertAdobeAirHook(); // Adobe AIR
bool InsertAdobeFlash10Hook(); // Adobe Flash Player 10
bool InsertAliceHook(); // System40@AliceSoft; do not work for latest alice games
bool InsertAmuseCraftHook(); // AMUSE CRAFT: *.pac
bool InsertAnex86Hook(); // Anex86: anex86.exe
bool InsertAOSHook(); // AOS: *.aos
bool InsertApricoTHook(); // Apricot: arc.a*
bool InsertArtemisHook(); // Artemis Engine: *.pfs
bool InsertAtelierHook(); // Atelier Kaguya: message.dat
bool InsertBGIHook(); // BGI: BGI.*
bool InsertC4Hook(); // C4: C4.EXE or XEX.EXE
bool InsertCaramelBoxHook(); // Caramel: *.bin
bool InsertCandyHook(); // SystemC@CandySoft: *.fpk
bool InsertCatSystemHook(); // CatSystem2: *.int
bool InsertCMVSHook(); // CMVS: data/pack/*.cpz; do not support the latest cmvs32.exe and cmvs64.exe
bool InsertCotophaHook(); // Cotopha: *.noa
bool InsertDebonosuHook(); // Debonosu: bmp.bak and dsetup.dll
bool InsertEaglsHook(); // E.A.G.L.S: EAGLES.dll
bool InsertEMEHook(); // EmonEngine: emecfg.ecf
bool InsertEushullyHook(); // Eushully: AGERC.DLL
bool InsertExpHook(); // EXP: http://www.exp-inc.jp
bool InsertFocasLensHook(); // FocasLens: Dat/*.arc, http://www.fo-lens.net
bool InsertGesen18Hook(); // Gsen18: *.szs
bool InsertGXPHook(); // GXP: *.gxp
bool InsertHorkEyeHook(); // HorkEye: resource string
bool InsertKAGParserHook(); // plugin/KAGParser.dll
bool InsertKAGParserExHook(); // plugin/KAGParserEx.dll
bool InsertKiriKiriHook(); // KiriKiri: *.xp3, resource string
bool InsertKiriKiriZHook(); // KiriKiri: *.xp3, resource string
bool InsertLeafHook(); // Leaf: *.pak
bool InsertLiveHook(); // Live: live.dll
bool InsertLunaSoftHook(); // LunaSoft: Pac/*.pac
bool InsertMalieHook(); // Malie@light: malie.ini
bool InsertMajiroHook(); // Majiro: *.arc
bool InsertMarineHeartHook(); // Marine Heart: SAISYS.exe
bool InsertMBLHook(); // MBL: *.mbl
bool InsertMEDHook(); // MED: *.med
bool InsertMinkHook(); // Mink: *.at2
//bool InsertMonoHook(); // Mono (Unity3D): */Mono/mono.dll
bool InsertNeXASHook(); // NeXAS: Thumbnail.pac
bool InsertNextonHook(); // NEXTON: aInfo.db
bool InsertNexton1Hook();
bool InsertNitroPlusHook(); // NitroPlus: *.npa
bool InsertPensilHook(); // Pensil: PSetup.exe
bool InsertQLIEHook(); // QLiE: GameData/*.pack
//bool InsertRai7Hook(); // Rai7puk: rai7.exe
bool InsertRejetHook(); // Rejet: Module/{gd.dat,pf.dat,sd.dat}
bool InsertRUGPHook(); // rUGP: rUGP.exe
bool InsertRetouchHook(); // Retouch: resident.dll
bool InsertRREHook(); // RunrunEngine: rrecfg.rcf
bool InsertShinaHook(); // ShinaRio: Rio.ini
bool InsertShinyDaysHook(); // ShinyDays
bool InsertElfHook(); // elf: Silky.exe
bool InsertScenarioPlayerHook();// sol-fa-soft: *.iar && *.sec5
bool InsertSiglusHook(); // SiglusEngine: SiglusEngine.exe
bool InsertSideBHook(); // SideB: Copyright side-B
bool InsertSyuntadaHook(); // Syuntada: dSoh.dat
bool InsertSystem43Hook(); // System43@AliceSoft: AliceStart.ini
bool InsertSystemAoiHook(); // SystemAoi: *.vfs
bool InsertTanukiHook(); // Tanuki: *.tak
bool InsertTaskforce2Hook(); // Taskforce2.exe
bool InsertTencoHook(); // Tenco: Check.mdx
bool InsertTriangleHook(); // Triangle: Execle.exe
bool InsertYukaSystem2Hook(); // YukaSystem2: *.ykc
bool InsertYurisHook(); // YU-RIS: *.ypf
bool InsertWillPlusHook(); // WillPlus: Rio.arc
bool InsertWolfHook(); // Wolf: Data.wolf
void InsertBrunsHook(); // Bruns: bruns.exe
void InsertIronGameSystemHook();// IroneGameSystem: igs_sample.exe
void InsertLucifenHook(); // Lucifen@Navel: *.lpk
void InsertRyokuchaHook(); // Ryokucha: _checksum.exe
void InsertRealliveHook(); // RealLive: RealLive*.exe
void InsertStuffScriptHook(); // Stuff: *.mpk
void InsertTinkerBellHook(); // TinkerBell: arc00.dat
void InsertWaffleHook(); // WAFFLE: cg.pak
// CIRCUS: avdata/
bool InsertCircusHook1();
bool InsertCircusHook2();
} // namespace Engine
// EOF
#pragma once
// engine/hookdefs.h
// 7/20/2014 jichi
#include "config.h"
// For HookParam user flags
enum HookParamFlag : unsigned long {
HPF_Null = 0 // never used
, HPF_IgnoreSameAddress = 1 // ignore the last same text address
};
// EOF
// eng/match.cc
// 8/9/2013 jichi
// Branch: ITH_Engine/engine.cpp, revision 133
#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 "engine/match.h"
#include "engine/engine.h"
#include "engine/pchooks.h"
#include "engine/util.h"
#include "hook.h"
#include "ith/sys/sys.h"
#include "ith/common/except.h"
#include "ith/common/growl.h"
#include "ccutil/ccmacro.h"
//#define ConsoleOutput(...) (void)0 // jichi 8/18/2013: I don't need ConsoleOutput
enum { MAX_REL_ADDR = 0x200000 }; // jichi 8/18/2013: maximum relative address
// - Global variables -
namespace Engine {
WCHAR process_name_[MAX_PATH], // cached
process_path_[MAX_PATH]; // cached
DWORD module_base_,
module_limit_;
//LPVOID trigger_addr;
trigger_fun_t trigger_fun_;
} // namespace Engine
// - Methods -
namespace Engine { namespace { // unnamed
// jichi 7/17/2014: Disable GDI hooks for PPSSPP
bool DeterminePCEngine()
{
if (IthFindFile(L"PPSSPP*.exe")) { // jichi 7/12/2014 PPSSPPWindows.exe, PPSSPPEX.exe PPSSPPSP.exe
InsertPPSSPPHooks();
return true;
}
if (IthFindFile(L"pcsx2*.exe")) { // jichi 7/19/2014 PCSX2.exe or PCSX2WX.exe
if (!InsertPCSX2Hooks()) { // don't forget to rebuild vnrcli to inject SSE
// Always insert PC hooks so that user could add PCSX2 to VNR.
// TO BE REMOVED after more PS2 engines are added.
PcHooks::hookGDIFunctions();
PcHooks::hookLstrFunctions();
}
return true;
}
if (IthFindFile(L"Dolphin.exe")) { // jichi 7/20/2014
if (!InsertGCHooks()) {
// Always insert PC hooks so that user could add PCSX2 to VNR.
// TO BE REMOVED after more PS2 engines are added.
PcHooks::hookGDIFunctions();
PcHooks::hookLstrFunctions();
}
return true;
}
//if (IthFindFile(L"*\\Mono\\mono.dll")) { // jichi 4/21/2014: Mono
//if (IthCheckFile(L"bsz2_Data\\Mono\\mono.dll")) { // jichi 4/21/2014: Mono
// InsertMonoHook();
// return true;
//}
if (::GetModuleHandleA("mono.dll")) {
InsertMonoHooks();
// 3/20/2015 jichi
// Always insert GDI hooks even for Mono games
// For example: 新世黙示録 need GetGlyphOutlineA
PcHooks::hookGDIFunctions();
return true;
}
// PC games
PcHooks::hookGDIFunctions();
return false;
}
bool DetermineEngineByFile1()
{
if (IthFindFile(L"*.xp3") || Util::SearchResourceString(L"TVP(KIRIKIRI)")) {
if (Util::SearchResourceString(L"TVP(KIRIKIRI) Z ")) { // TVP(KIRIKIRI) Z CORE
// jichi 11/24/2014: Disabled that might crash VBH
//if (IthCheckFile(L"plugin\\KAGParser.dll"))
// InsertKAGParserHook();
//else if (IthCheckFile(L"plugin\\KAGParserEx.dll"))
// InsertKAGParserExHook();
if (InsertKiriKiriZHook())
return true;
}
InsertKiriKiriHook();
return true;
}
// 8/2/2014 jichi: Game name shown as 2RM - Adventure Engine
if (Util::SearchResourceString(L"2RM") && Util::SearchResourceString(L"Adventure Engine")) {
Insert2RMHook();
return true;
}
// 8/2/2014 jichi: Copyright is side-B, a conf.dat will be generated after the game is launched
// It also contains lua5.1.dll and lua5.dll
if (Util::SearchResourceString(L"side-B")) {
InsertSideBHook();
return true;
}
if (IthFindFile(L"bgi.*")) {
InsertBGIHook();
return true;
}
if (IthCheckFile(L"AGERC.DLL")) { // 6/1/2014 jichi: Eushully, AGE.EXE
InsertEushullyHook();
return true;
}
if (IthFindFile(L"data*.arc") && IthFindFile(L"stream*.arc")) {
InsertMajiroHook();
return true;
}
// jichi 5/31/2014
if (//IthCheckFile(L"Silkys.exe") || // It might or might not have Silkys.exe
// data, effect, layer, mes, music
IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"mes.arc")) {
InsertElfHook();
return true;
}
if (IthFindFile(L"data\\pack\\*.cpz")) {
InsertCMVSHook();
return true;
}
// jichi 10/12/2013: Restore wolf engine
// jichi 10/18/2013: Check for data/*.wolf
if (IthFindFile(L"data.wolf") || IthFindFile(L"data\\*.wolf")) {
InsertWolfHook();
return true;
}
if (IthCheckFile(L"advdata\\dat\\names.dat")) {
InsertCircusHook1();
return true;
}
if (IthCheckFile(L"advdata\\grp\\names.dat")) {
InsertCircusHook2();
return true;
}
if (IthFindFile(L"*.noa")) {
InsertCotophaHook();
return true;
}
if (IthFindFile(L"*.pfs")) { // jichi 10/1/2013
InsertArtemisHook();
return true;
}
if (IthFindFile(L"*.int")) {
InsertCatSystemHook();
return true;
}
if (IthCheckFile(L"message.dat")) {
InsertAtelierHook();
return true;
}
if (IthCheckFile(L"Check.mdx")) { // jichi 4/1/2014: AUGame
InsertTencoHook();
return true;
}
// jichi 12/25/2013: It may or may not be QLIE.
// AlterEgo also has GameData/sound.pack but is not QLIE
if (IthFindFile(L"GameData\\*.pack") && InsertQLIEHook())
return true;
if (IthFindFile(L"*.pac")) {
// jichi 6/3/2014: AMUSE CRAFT and SOFTPAL
// Selectively insert, so that lstrlenA can still get correct text if failed
if (IthCheckFile(L"dll\\resource.dll") && IthCheckFile(L"dll\\pal.dll") && InsertAmuseCraftHook())
return true;
if (IthCheckFile(L"Thumbnail.pac")) {
//ConsoleOutput("vnreng: IGNORE NeXAS");
InsertNeXASHook(); // jichi 7/6/2014: GIGA
return true;
}
if (Util::SearchResourceString(L"SOFTPAL")) {
ConsoleOutput("vnreng: IGNORE SoftPal UNiSONSHIFT");
return true;
}
}
// jichi 12/27/2014: LunaSoft
if (IthFindFile(L"Pac\\*.pac")) {
InsertLunaSoftHook();
return true;
}
// jichi 9/16/2013: Add Gesen18
if (IthFindFile(L"*.szs") || IthFindFile(L"Data\\*.szs")) {
InsertGesen18Hook();
return true;
}
// jichi 12/22/2013: Add rejet
if (IthCheckFile(L"gd.dat") && IthCheckFile(L"pf.dat") && IthCheckFile(L"sd.dat")) {
InsertRejetHook();
return true;
}
// Only examined with version 1.0
//if (IthFindFile(L"Adobe AIR\\Versions\\*\\Adobe AIR.dll")) { // jichi 4/15/2014: FIXME: Wildcard not working
if (IthCheckFile(L"Adobe AIR\\Versions\\1.0\\Adobe AIR.dll")) { // jichi 4/15/2014: Adobe AIR
InsertAdobeAirHook();
return true;
}
return false;
}
bool DetermineEngineByFile2()
{
if (IthCheckFile(L"resident.dll")) {
InsertRetouchHook();
return true;
}
if (IthCheckFile(L"Malie.ini") || IthCheckFile(L"Malie.exe")) { // jichi: 9/9/2014: Add malie.exe in case malie.ini is missing
InsertMalieHook();
return true;
}
if (IthCheckFile(L"live.dll")) {
InsertLiveHook();
return true;
}
// 9/5/2013 jichi
if (IthCheckFile(L"aInfo.db")) {
InsertNextonHook();
return true;
}
if (IthFindFile(L"*.lpk")) {
InsertLucifenHook();
return true;
}
if (IthCheckFile(L"cfg.pak")) {
InsertWaffleHook();
return true;
}
if (IthCheckFile(L"Arc00.dat")) {
InsertTinkerBellHook();
return true;
}
if (IthFindFile(L"*.vfs")) { // jichi 7/6/2014: Better to test AoiLib.dll? ja.wikipedia.org/wiki/ソフトハウスキャラ
InsertSystemAoiHook();
return true;
}
if (IthFindFile(L"*.mbl")) {
InsertMBLHook();
return true;
}
// jichi 8/1/2014: YU-RIS engine, lots of clockup game also has this pattern
if (IthFindFile(L"pac\\*.ypf") || IthFindFile(L"*.ypf")) {
// jichi 8/14/2013: CLOCLUP: "ノーブレスオブリージュ" would crash the game.
if (!IthCheckFile(L"noblesse.exe"))
InsertYurisHook();
return true;
}
if (IthFindFile(L"*.npa")) {
InsertNitroPlusHook();
return true;
}
return false;
}
bool DetermineEngineByFile3()
{
//if (IthCheckFile(L"libscr.dll")) { // already checked
// InsertBrunsHook();
// return true;
//}
// jichi 10/12/2013: Sample args.txt:
// See: http://tieba.baidu.com/p/2631413816
// -workdir
// .
// -loadpath
// .
// am.cfg
if (IthCheckFile(L"args.txt")) {
InsertBrunsHook();
return true;
}
if (IthCheckFile(L"emecfg.ecf")) {
InsertEMEHook();
return true;
}
if (IthCheckFile(L"rrecfg.rcf")) {
InsertRREHook();
return true;
}
if (IthFindFile(L"*.fpk") || IthFindFile(L"data\\*.fpk")) {
InsertCandyHook();
return true;
}
if (IthFindFile(L"arc.a*")) {
InsertApricoTHook();
return true;
}
if (IthFindFile(L"*.mpk")) {
InsertStuffScriptHook();
return true;
}
if (IthCheckFile(L"Execle.exe")) {
InsertTriangleHook();
return true;
}
// jichi 2/28/2015: No longer work for "大正×対称アリス episode I" from Primula
//if (IthCheckFile(L"PSetup.exe")) {
// InsertPensilHook();
// return true;
//}
if (IthCheckFile(L"Yanesdk.dll")) {
InsertAB2TryHook();
return true;
}
if (IthFindFile(L"*.med")) {
InsertMEDHook();
return true;
}
return false;
}
bool DetermineEngineByFile4()
{
if (IthCheckFile(L"EAGLS.dll")) { // jichi 3/24/2014: E.A.G.L.S
//ConsoleOutput("vnreng: IGNORE EAGLS");
InsertEaglsHook();
return true;
}
if (IthCheckFile(L"bmp.pak") && IthCheckFile(L"dsetup.dll")) {
InsertDebonosuHook();
return true;
}
if (IthCheckFile(L"C4.EXE") || IthCheckFile(L"XEX.EXE")) {
InsertC4Hook();
return true;
}
if (IthCheckFile(L"Rio.arc") && IthFindFile(L"Chip*.arc")) {
InsertWillPlusHook();
return true;
}
if (IthFindFile(L"*.tac")) {
InsertTanukiHook();
return true;
}
if (IthFindFile(L"*.gxp")) {
InsertGXPHook();
return true;
}
if (IthFindFile(L"*.aos")) { // jichi 4/2/2014: AOS hook
InsertAOSHook();
return true;
}
if (IthFindFile(L"*.at2")) { // jichi 12/23/2014: Mink, sample files: voice.at2, voice.det, voice.nme
InsertMinkHook();
return true;
}
if (IthFindFile(L"*.ykc")) { // jichi 7/15/2014: YukaSystem1 is not supported, though
//ConsoleOutput("vnreng: IGNORE YKC:Feng/HookSoft(SMEE)");
InsertYukaSystem2Hook();
return true;
}
if (IthFindFile(L"model\\*.hed")) { // jichi 9/8/2014: EXP
InsertExpHook();
return true;
}
// jichi 2/6/2015 平安亭
// dPi.dat, dPih.dat, dSc.dat, dSch.dat, dSo.dat, dSoh.dat, dSy.dat
//if (IthCheckFile(L"dSoh.dat")) { // no idea why this file does not work
if (IthCheckFile(L"dSch.dat")) {
InsertSyuntadaHook();
return true;
}
// jichi 2/28/2015: Delay checking Pensil in case something went wrong
// File pattern observed in [Primula] 大正×対称アリス episode I
// - PSetup.exe no longer exists
// - MovieTexture.dll information shows MovieTex dynamic library, copyright Pensil 2013
// - ta_trial.exe information shows 2XT - Primula Adventure Engine
if (IthFindFile(L"PSetup.exe") || IthFindFile(L"MovieTexture.dll") || Util::SearchResourceString(L"2XT -")) {
InsertPensilHook();
return true;
}
return false;
}
bool DetermineEngineByProcessName()
{
WCHAR str[MAX_PATH];
wcscpy(str, process_name_);
_wcslwr(str); // lower case
if (wcsstr(str,L"reallive") || IthCheckFile(L"Reallive.exe")) {
InsertRealliveHook();
return true;
}
// jichi 8/19/2013: DO NOT WORK for games like「ハピメア」
//if (wcsstr(str,L"cmvs32") || wcsstr(str,L"cmvs64")) {
// InsertCMVSHook();
// return true;
//}
// jichi 8/17/2013: Handle "~"
if (wcsstr(str, L"siglusengine") || !wcsncmp(str, L"siglus~", 7) || IthCheckFile(L"SiglusEngine.exe")) {
InsertSiglusHook();
return true;
}
if (wcsstr(str, L"taskforce2") || !wcsncmp(str, L"taskfo~", 7) || IthCheckFile(L"Faskforce2.exe")) {
InsertTaskforce2Hook();
return true;
}
if (wcsstr(str,L"rugp") || IthCheckFile(L"rugp.exe")) {
InsertRUGPHook();
return true;
}
// jichi 8/17/2013: Handle "~"
if (wcsstr(str, L"igs_sample") || !wcsncmp(str, L"igs_sa~", 7) || IthCheckFile(L"igs_sample.exe")) {
InsertIronGameSystemHook();
return true;
}
if (wcsstr(str, L"bruns") || IthCheckFile(L"bruns.exe")) {
InsertBrunsHook();
return true;
}
if (wcsstr(str, L"anex86") || IthCheckFile(L"anex86.exe")) {
InsertAnex86Hook();
return true;
}
// jichi 8/17/2013: Handle "~"
if (wcsstr(str, L"shinydays") || !wcsncmp(str, L"shinyd~", 7) || IthCheckFile(L"ShinyDays.exe")) {
InsertShinyDaysHook();
return true;
}
// jichi 10/3/2013: FIXME: Does not work
// Raise C0000005 even with admin priv
//if (wcsstr(str, L"bsz")) { // BALDRSKY ZERO
// InsertBaldrHook();
// return true;
//}
if (wcsstr(process_name_, L"SAISYS") || IthCheckFile(L"SaiSys.exe")) { // jichi 4/19/2014: Marine Heart
InsertMarineHeartHook();
return true;
}
DWORD len = wcslen(str);
// jichi 8/24/2013: Checking for Rio.ini or $procname.ini
//wcscpy(str+len-4, L"_?.war");
//if (IthFindFile(str)) {
// InsertShinaHook();
// return true;
//}
if (InsertShinaHook())
return true;
// jichi 8/10/2013: Since *.bin is common, move CaramelBox to the end
str[len - 3] = L'b';
str[len - 2] = L'i';
str[len - 1] = L'n';
str[len] = 0;
if (IthCheckFile(str) || IthCheckFile(L"trial.bin")) { // jichi 7/8/2014: add trial.bin
InsertCaramelBoxHook();
return true;
}
// This must appear at last since str is modified
wcscpy(str + len - 4, L"_checksum.exe");
if (IthCheckFile(str)) {
InsertRyokuchaHook();
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games
InsertScenarioPlayerHook();
return true;
}
return false;
}
bool DetermineEngineOther()
{
if (InsertAliceHook())
return true;
// jichi 1/19/2015: Disable inserting Lstr for System40
// See: http://sakuradite.com/topic/618
if (IthCheckFile(L"System40.ini")) {
ConsoleOutput("vnreng: IGNORE old System40.ini");
return true;
}
// jichi 12/26/2013: Add this after alicehook
if (IthCheckFile(L"AliceStart.ini")) {
InsertSystem43Hook();
return true;
}
// jichi 8/24/2013: Move into functions
static BYTE static_file_info[0x1000];
if (IthGetFileInfo(L"*01", static_file_info))
if (*(DWORD*)static_file_info == 0) {
static WCHAR static_search_name[MAX_PATH];
LPWSTR name=(LPWSTR)(static_file_info+0x5E);
int len = wcslen(name);
name[len - 2] = L'*';
name[len - 1] = 0;
wcscpy(static_search_name, name);
IthGetFileInfo(static_search_name, static_file_info);
union {
FILE_BOTH_DIR_INFORMATION *both_info;
DWORD addr;
};
both_info = (FILE_BOTH_DIR_INFORMATION *)static_file_info;
//BYTE* ptr=static_file_info;
len = 0;
while (both_info->NextEntryOffset) {
addr += both_info->NextEntryOffset;
len++;
}
if (len > 3) {
InsertAbelHook();
return true;
}
}
return false;
}
// jichi 8/17/2014
// Put the patterns that might break other games at last
bool DetermineEngineAtLast()
{
if (IthFindFile(L"data\\*.cpk")) { // jichi 12/2/2014
Insert5pbHook();
return true;
}
// jichi 7/6/2014: named as ScenarioPlayer since resource string could be: scenario player program for xxx
// Do this at last as it is common
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) { // jichi 4/18/2014: Other game engine could also have *.iar such as Ryokucha
InsertScenarioPlayerHook();
return true;
}
//if (IthCheckFile(L"arc0.dat") && IthCheckFile(L"script.dat") // jichi 11/14/2014: too common
if (Util::SearchResourceString(L"HorkEye")) { // appear in copyright: Copyright (C) HorkEye, http://horkeye.com
InsertHorkEyeHook();
return true;
}
if (IthCheckFile(L"comnArc.arc") // jichi 8/17/2014: this file might exist in multiple files
&& InsertNexton1Hook()) // old nexton game
return true;
if (IthCheckFile(L"arc.dat") // jichi 9/27/2014: too common
&& InsertApricoTHook())
return true;
if (IthFindFile(L"*.pak") // jichi 12/25/2014: too common
&& InsertLeafHook())
return true;
// jichi 10/31/2014
// File description: Adobe Flash Player 10.2r153
// Product name: Shockwave Flash
// Original filename: SAFlashPlayer.exe
// Legal trademarks: Adobe Flash Player
// No idea why, this must appear at last or it will crash
if (Util::SearchResourceString(L"Adobe Flash Player 10")) {
InsertAdobeFlash10Hook(); // only v10 might be supported. Otherwise, fallback to Lstr hooks
return true;
}
if (IthFindFile(L"dat\\*.arc")) { // jichi 2/6/2015
InsertFocasLensHook(); // Touhou
return true;
}
return false;
}
// jichi 6/1/2014
bool DetermineEngineGeneric()
{
bool ret = false;
if (IthCheckFile(L"AlterEgo.exe")) {
ConsoleOutput("vnreng: AlterEgo, INSERT WideChar hooks");
ret = true;
} else if (IthFindFile(L"data\\Sky\\*")) {
ConsoleOutput("vnreng: TEATIME, INSERT WideChar hooks");
ret = true;
}
//} else if (IthFindFile(L"image\\*.po2") || IthFindFile(L"image\\*.jo2")) {
// ConsoleOutput("vnreng: HarukaKanata, INSERT WideChar hooks"); // はるかかなた
// ret = true;
//}
if (ret)
PcHooks::hookWcharFunctions();
return ret;
}
bool DetermineNoEngine()
{
//if (IthFindFile(L"*\\Managed\\UnityEngine.dll")) { // jichi 12/3/2013: Unity (BALDRSKY ZERO)
// ConsoleOutput("vnreng: IGNORE Unity");
// return true;
//}
//if (IthCheckFile(L"bsz_Data\\Managed\\UnityEngine.dll") || IthCheckFile(L"bsz2_Data\\Managed\\UnityEngine.dll")) {
// ConsoleOutput("vnreng: IGNORE Unity");
// return true;
//}
// jichi 2/14/2015: Guilty+ RIN×SEN (PK)
if (IthCheckFile(L"rio.ini") || IthFindFile(L"*.war")) {
ConsoleOutput("vnreng: IGNORE unknown ShinaRio");
return true;
}
if (IthCheckFile(L"AdvHD.exe") || IthCheckFile(L"AdvHD.dll")) {
ConsoleOutput("vnreng: IGNORE Adv Player HD");
return true;
}
if (IthCheckFile(L"ScrPlayer.exe")) {
ConsoleOutput("vnreng: IGNORE ScrPlayer");
return true;
}
if (IthCheckFile(L"nnnConfig2.exe")) {
ConsoleOutput("vnreng: IGNORE Nya NNNConfig");
return true;
}
//if (IthCheckFile(L"AGERC.DLL")) { // jichi 3/17/2014: Eushully, AGE.EXE
// ConsoleOutput("vnreng: IGNORE Eushully");
// return true;
//}
if (IthCheckFile(L"game_sys.exe")) {
ConsoleOutput("vnreng: IGNORE Atelier Kaguya BY/TH");
return true;
}
if (IthFindFile(L"*.bsa")) {
ConsoleOutput("vnreng: IGNORE Bishop");
return true;
}
// jichi 3/19/2014: Escude game
// Example: bgm.bin gfx.bin maou.bin script.bin snd.bin voc.bin
if (IthCheckFile(L"gfx.bin") && IthCheckFile(L"snd.bin") && IthCheckFile(L"voc.bin")) {
ConsoleOutput("vnreng: IGNORE Escude");
return true;
}
// jichi 2/18/2015: Ignore if there is Nitro+ copyright
if (Util::SearchResourceString(L"Nitro+")) {
ConsoleOutput("vnreng: IGNORE unknown Nitro+");
return true;
}
// jichi 12/28/2014: "Chartreux Inc." in Copyright.
// Sublimary brands include Rosebleu, MORE, etc.
// GetGlyphOutlineA already works.
if (Util::SearchResourceString(L"Chartreux")) {
ConsoleOutput("vnreng: IGNORE Chartreux");
return true;
}
if (wcsstr(process_name_, L"lcsebody") || !wcsncmp(process_name_, L"lcsebo~", 7) || IthCheckFile(L"lcsebody")) { // jichi 3/19/2014: LC-ScriptEngine, GetGlyphOutlineA
ConsoleOutput("vnreng: IGNORE lcsebody");
return true;
}
wchar_t str[MAX_PATH];
DWORD i;
for (i = 0; process_name_[i]; i++) {
str[i] = process_name_[i];
if (process_name_[i] == L'.')
break;
}
*(DWORD *)(str + i + 1) = 0x630068; //.hcb
*(DWORD *)(str + i + 3) = 0x62;
if (IthCheckFile(str)) {
ConsoleOutput("vnreng: IGNORE FVP"); // jichi 10/3/2013: such like アトリエかぐや
return true;
}
return false;
}
// 12/13/2013: Declare it in a way compatible to EXCEPTION_PROCEDURE
EXCEPTION_DISPOSITION ExceptHandler(PEXCEPTION_RECORD ExceptionRecord, LPVOID, PCONTEXT, LPVOID)
{
if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) {
module_limit_ = ExceptionRecord->ExceptionInformation[1];
//OutputDWORD(module_limit_);
__asm
{
mov eax,fs:[0x30] // jichi 12/13/2013: get PEB
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov ecx,module_limit_
sub ecx,module_base_
mov [eax+0x20],ecx
}
}
//ContextRecord->Esp = recv_esp;
//ContextRecord->Eip = recv_eip;
//return ExceptionContinueExecution; // jichi 3/11/2014: this will still crash. Not sure why ITH use this. Change to ExceptionContinueSearch
return ExceptionContinueSearch; // an unwind is in progress,
}
// jichi 9/14/2013: Certain ITH functions like FindEntryAligned might raise exception without admin priv
// Return if succeeded.
bool UnsafeDetermineEngineType()
{
return DeterminePCEngine()
|| DetermineEngineByFile1()
|| DetermineEngineByFile2()
|| DetermineEngineByFile3()
|| DetermineEngineByFile4()
|| DetermineEngineByProcessName()
|| DetermineEngineOther()
|| DetermineEngineAtLast()
|| DetermineEngineGeneric()
|| DetermineNoEngine()
;
}
// jichi 10/21/2014: Return whether found the game engine
bool DetermineEngineType()
{
// jichi 9/27/2013: disable game engine for debugging use
#ifdef ITH_DISABLE_ENGINE
PcHooks::hookLstrFunctions();
return false;
#else
bool found = false;
#ifdef ITH_HAS_SEH
__try { found = UnsafeDetermineEngineType(); }
__except(ExceptHandler((GetExceptionInformation())->ExceptionRecord, 0, 0, 0)) {}
#else // use my own SEH
seh_with_eh(ExceptHandler,
found = UnsafeDetermineEngineType());
#endif // ITH_HAS_SEH
if (!found) // jichi 10/2/2013: Only enable it if no game engine is detected
PcHooks::hookLstrFunctions();
else
ConsoleOutput("vnreng: found game engine, IGNORE non gui hooks");
return found;
#endif // ITH_DISABLE_ENGINE
}
// __asm
// {
// mov eax,seh_recover
// mov recv_eip,eax
// push ExceptHandler
// push fs:[0]
// mov fs:[0],esp
// pushad
// mov recv_esp,esp
// }
// DetermineEngineType();
// status++;
// __asm
// {
//seh_recover:
// popad
// mov eax,[esp]
// mov fs:[0],eax
// add esp,8
// }
// if (status == 0)
// ConsoleOutput("Fail to identify engine type.");
// else
// ConsoleOutput("Initialized successfully.");
//}
}} // namespace Engine unnamed
// - API -
bool Engine::IdentifyEngine()
{
// jichi 12/18/2013: Though FillRange could raise, it should never raise for he current process
// So, SEH is not used here.
FillRange(process_name_, &module_base_, &module_limit_);
return DetermineEngineType();
}
DWORD Engine::InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{ return trigger_fun_ ? !trigger_fun_(addr, frame, stack) : 0; }
void Engine::match(LPVOID lpThreadParameter)
{
CC_UNUSED(lpThreadParameter);
Util::GetProcessName(process_name_); // Initialize process name
Util::GetProcessPath(process_path_); // Initialize process path
::engine_registered = true;
//::RegisterEngineModule((DWORD)IdentifyEngine, (DWORD)InsertDynamicHook);
}
// EOF
/*
extern "C" {
// http://gmogre3d.googlecode.com/svn-history/r815/trunk/OgreMain/src/WIN32/OgreMinGWSupport.cpp
// http://forum.osdev.org/viewtopic.php?f=8&t=22352
//#pragma data_seg()
//#pragma comment(linker, "/merge:.CRT=.data") // works fine in visual c++ 6
//#pragma data_seg()
//#pragma comment(linker, "/merge:.CRT=.rdata")
// MSVC libs use _chkstk for stack-probing. MinGW equivalent is _alloca.
//void _alloca();
//void _chkstk() { _alloca(); }
// MSVC uses security cookies to prevent some buffer overflow attacks.
// provide dummy implementations.
//void _fastcall __security_check_cookie(intptr_t i) {}
void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie) {}
}
*/
#pragma once
// engine/match.h
// 8/23/2013 jichi
// TODO: Clean up the interface to match game engines.
// Split the engine match logic out of hooks.
// Modify the game hook to allow replace functions for arbitary purpose
// instead of just extracting text.
#include "config.h"
namespace Engine {
void match(LPVOID lpThreadParameter);
// jichi 10/21/2014: Return whether found the engine
bool IdentifyEngine();
// jichi 10/21/2014: Return 0 if failed
DWORD InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack);
} // namespace Engine
// EOF
// pchooks.cc
// 8/1/2014 jichi
#include "engine/pchooks.h"
#include "hook.h"
#define DEBUG "vnrcli"
#define DPRINT(cstr) ConsoleOutput(DEBUG ":" __FUNCTION__ ":" cstr) // defined in vnrcli
// 8/1/2014 jichi: Split is not used.
// Although split is specified, USING_SPLIT is not assigned.
// Use LPASTE to convert to wchar_t
// http://bytes.com/topic/c/answers/135834-defining-wide-character-strings-macros
#define LPASTE(s) L##s
#define L(s) LPASTE(s)
#define NEW_HOOK(_fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
{ \
HookParam hp = {}; \
hp.addr = (DWORD)_fun; \
hp.off = _data; \
hp.ind = _data_ind; \
hp.split = _split_off; \
hp.split_ind = _split_ind; \
hp.type = _type; \
hp.length_offset = _len_off; \
NewHook(hp, L(#_fun)); \
}
// jichi 7/17/2014: Renamed from InitDefaultHook
void PcHooks::hookGDIFunctions()
{
DPRINT("enter");
// int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
//
// jichi 9/8/2013: Guessed meaning
// - data(off): 4 * the n-th (base 1) parameter representing the data of the string
// - len_off:
// - the n-th (base 1) parameter representing the length of the string
// - or 1 if is char
// - or 0 if detect on run time
// - type: USING_STRING if len_off != 1 else BIG_ENDIAN or USING_UNICODE
//
// Examples:
// int WINAPI lstrlenA(LPCSTR lpString)
// - data: 4 * 1 = 4, as lpString is the first
// - len_off: 0, as no parameter representing string length
// - type: BIG_ENDIAN, since len_off == 1
// BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
// - data: 4 * 2 = 0x8, as lpString is the second
// - len_off: 3, as nCount is the 3rd parameter
// - type: USING_STRING, since len_off != 1
//
// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
enum stack {
s_retaddr = 0
, s_arg1 = 4 * 1 // 0x4
, s_arg2 = 4 * 2 // 0x8
, s_arg3 = 4 * 3 // 0xc
, s_arg4 = 4 * 4 // 0x10
, s_arg5 = 4 * 5 // 0x14
, s_arg6 = 4 * 6 // 0x18
};
//#define _(Name, ...) \
// hookman[HF_##Name].InitHook(Name, __VA_ARGS__); \
// hookman[HF_##Name].SetHookName(names[HF_##Name]);
// Always use s_arg1 = hDC as split_off
// 7/26/2014 jichi: Why there is no USING_SPLIT type?
// gdi32.dll
NEW_HOOK(GetTextExtentPoint32A, s_arg2, 0,s_arg1,0, USING_STRING, 3) // BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
NEW_HOOK(GetGlyphOutlineA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // DWORD GetGlyphOutline(HDC hdc, UINT uChar, UINT uFormat, LPGLYPHMETRICS lpgm, DWORD cbBuffer, LPVOID lpvBuffer, const MAT2 *lpmat2);
NEW_HOOK(ExtTextOutA, s_arg6, 0,s_arg1,0, USING_STRING, 7) // BOOL ExtTextOut(HDC hdc, int X, int Y, UINT fuOptions, const RECT *lprc, LPCTSTR lpString, UINT cbCount, const INT *lpDx);
NEW_HOOK(TextOutA, s_arg4, 0,s_arg1,0, USING_STRING, 5) // BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cchString);
NEW_HOOK(GetCharABCWidthsA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharABCWidths(HDC hdc, UINT uFirstChar, UINT uLastChar, LPABC lpabc);
NEW_HOOK(GetTextExtentPoint32W, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(GetGlyphOutlineW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
NEW_HOOK(ExtTextOutW, s_arg6, 0,s_arg1,0, USING_UNICODE|USING_STRING, 7)
NEW_HOOK(TextOutW, s_arg4, 0,s_arg1,0, USING_UNICODE|USING_STRING, 5)
NEW_HOOK(GetCharABCWidthsW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
// user32.dll
NEW_HOOK(DrawTextA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawText(HDC hDC, LPCTSTR lpchText, int nCount, LPRECT lpRect, UINT uFormat);
NEW_HOOK(DrawTextExA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawTextEx(HDC hdc, LPTSTR lpchText,int cchText, LPRECT lprc, UINT dwDTFormat, LPDRAWTEXTPARAMS lpDTParams);
NEW_HOOK(DrawTextW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(DrawTextExW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
//#undef _
DPRINT("leave");
}
// jichi 10/2/2013
// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
void PcHooks::hookLstrFunctions()
{
DPRINT("enter");
// int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
enum stack {
s_retaddr = 0
, s_arg1 = 4 * 1 // 0x4
//, s_arg2 = 4 * 2 // 0x8
//, s_arg3 = 4 * 3 // 0xc
//, s_arg4 = 4 * 4 // 0x10
//, s_arg5 = 4 * 5 // 0x14
//, s_arg6 = 4 * 6 // 0x18
};
// http://msdn.microsoft.com/en-us/library/78zh94ax.aspx
// int WINAPI lstrlen(LPCTSTR lpString);
// Lstr functions usually extracts rubbish, and might crash certain games like 「Magical Marriage Lunatics!!」
// Needed by Gift
// Use arg1 address for both split and data
NEW_HOOK(lstrlenA, s_arg1, 0,s_arg1,0, USING_STRING, 0) // 9/8/2013 jichi: int WINAPI lstrlen(LPCTSTR lpString);
NEW_HOOK(lstrlenW, s_arg1, 0,s_arg1,0, USING_UNICODE|USING_STRING, 0) // 9/8/2013 jichi: add lstrlen
// size_t strlen(const char *str);
// size_t strlen_l(const char *str, _locale_t locale);
// size_t wcslen(const wchar_t *str);
// size_t wcslen_l(const wchar_t *str, _locale_t locale);
// size_t _mbslen(const unsigned char *str);
// size_t _mbslen_l(const unsigned char *str, _locale_t locale);
// size_t _mbstrlen(const char *str);
// size_t _mbstrlen_l(const char *str, _locale_t locale);
// http://msdn.microsoft.com/en-us/library/ex0hs2ad.aspx
// Needed by 娘姉妹
//
// <tchar.h>
// char *_strinc(const char *current, _locale_t locale);
// wchar_t *_wcsinc(const wchar_t *current, _locale_t locale);
// <mbstring.h>
// unsigned char *_mbsinc(const unsigned char *current);
// unsigned char *_mbsinc_l(const unsigned char *current, _locale_t locale);
//_(L"_strinc", _strinc, 4, 0,4,0, USING_STRING, 0) // 12/13/2013 jichi
//_(L"_wcsinc", _wcsinc, 4, 0,4,0, USING_UNICODE|USING_STRING, 0)
DPRINT("leave");
}
void PcHooks::hookWcharFunctions()
{
DPRINT("enter");
// 12/1/2013 jichi:
// AlterEgo
// http://tieba.baidu.com/p/2736475133
// http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page355
//
// MultiByteToWideChar
// http://blgames.proboards.com/thread/265
//
// WideCharToMultiByte
// http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page156
//
// int MultiByteToWideChar(
// _In_ UINT CodePage,
// _In_ DWORD dwFlags,
// _In_ LPCSTR lpMultiByteStr, // hook here
// _In_ int cbMultiByte,
// _Out_opt_ LPWSTR lpWideCharStr,
// _In_ int cchWideChar
// );
// int WideCharToMultiByte(
// _In_ UINT CodePage,
// _In_ DWORD dwFlags,
// _In_ LPCWSTR lpWideCharStr,
// _In_ int cchWideChar,
// _Out_opt_ LPSTR lpMultiByteStr,
// _In_ int cbMultiByte,
// _In_opt_ LPCSTR lpDefaultChar,
// _Out_opt_ LPBOOL lpUsedDefaultChar
// );
enum stack {
s_retaddr = 0
//, s_arg1 = 4 * 1 // 0x4
//, s_arg2 = 4 * 2 // 0x8
, s_arg3 = 4 * 3 // 0xc
//, s_arg4 = 4 * 4 // 0x10
//, s_arg5 = 4 * 5 // 0x14
//, s_arg6 = 4 * 6 // 0x18
};
// 3/17/2014 jichi: Temporarily disabled
// http://sakuradite.com/topic/159
NEW_HOOK(MultiByteToWideChar, s_arg3, 0,4,0, USING_STRING, 4)
NEW_HOOK(WideCharToMultiByte, s_arg3, 0,4,0, USING_UNICODE|USING_STRING, 4)
DPRINT("leave");
}
// EOF
#pragma once
// pchooks.h
// 8/1/2014 jichi
#include "config.h"
namespace PcHooks {
void hookGDIFunctions();
void hookLstrFunctions();
void hookWcharFunctions();
} // namespace PcHooks
// EOF
// util/util.cc
// 8/23/2013 jichi
// Branch: ITH_Engine/engine.cpp, revision 133
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
#include "engine/util.h"
#include "ith/sys/sys.h"
namespace { // unnamed
// jichi 4/19/2014: Return the integer that can mask the signature
DWORD SigMask(DWORD sig)
{
__asm
{
xor ecx,ecx
mov eax,sig
_mask:
shr eax,8
inc ecx
test eax,eax
jnz _mask
sub ecx,4
neg ecx
or eax,-1
shl ecx,3
shr eax,cl
}
}
} // namespace unnamed
// jichi 8/24/2013: binary search?
DWORD Util::GetCodeRange(DWORD hModule,DWORD *low, DWORD *high)
{
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
DWORD dwReadAddr;
IMAGE_SECTION_HEADER *shdr;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
dwReadAddr = hModule + DosHdr->e_lfanew;
NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr;
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
shdr = (PIMAGE_SECTION_HEADER)((DWORD)(&NtHdr->OptionalHeader) + NtHdr->FileHeader.SizeOfOptionalHeader);
while ((shdr->Characteristics & IMAGE_SCN_CNT_CODE) == 0)
shdr++;
*low = hModule + shdr->VirtualAddress;
*high = *low + (shdr->Misc.VirtualSize & 0xfffff000) + 0x1000;
}
}
return 0;
}
DWORD Util::FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig)
{
//WCHAR str[0x40];
enum { reverse_length = 0x800 };
DWORD t, l;
DWORD mask = SigMask(sig);
bool flag2;
for (DWORD i = 0x1000; i < size-4; i++) {
bool flag1 = false;
if (*(BYTE *)(pt + i) == 0xe8) {
flag1 = flag2 = true;
t = *(DWORD *)(pt + i + 1);
} else if (*(WORD *)(pt + i) == 0x15ff) {
flag1 = true;
flag2 = false;
t = *(DWORD *)(pt + i + 2);
}
if (flag1) {
if (flag2) {
flag1 = (pt + i + 5 + t == fun);
l = 5;
} else if (t >= pt && t <= pt + size - 4) {
flag1 = fun == *(DWORD *)t;
l = 6;
} else
flag1 = false;
if (flag1)
//swprintf(str,L"CALL addr: 0x%.8X",pt + i);
//OutputConsole(str);
for (DWORD j = i; j > i - reverse_length; j--)
if ((*(WORD *)(pt + j)) == (sig & mask)) //Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",pt + j);
//OutputConsole(str);
return pt + j;
else
i += l;
}
}
//OutputConsole(L"Find call and entry failed.");
return 0;
}
DWORD Util::FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp)
{
BYTE sig = (jmp) ? 0xe9 : 0xe8;
for (DWORD i = 0x1000; i < size - 4; i++)
if (sig == *(BYTE *)(pt + i)) {
DWORD t = *(DWORD *)(pt + i + 1);
if(fun == pt + i + 5 + t)
//OutputDWORD(pt + i);
return pt + i;
else
i += 5;
}
return 0;
}
DWORD Util::FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp)
{
WORD sig = jmp ? 0x25ff : 0x15ff;
for (DWORD i = 0x1000; i < size - 4; i++)
if (sig == *(WORD *)(pt + i)) {
DWORD t = *(DWORD *)(pt + i + 2);
if (t > pt && t < pt + size) {
if (fun == *(DWORD *)t)
return pt + i;
else
i += 5;
}
}
return 0;
}
DWORD Util::FindCallBoth(DWORD fun, DWORD size, DWORD pt)
{
for (DWORD i = 0x1000; i < size - 4; i++) {
if (*(BYTE *)(pt + i) == 0xe8) {
DWORD t = *(DWORD *)(pt + i + 1) + pt + i + 5;
if (t == fun)
return i;
}
if (*(WORD *)(pt + i) == 0x15ff) {
DWORD t = *(DWORD *)(pt + i + 2);
if (t >= pt && t <= pt + size - 4) {
if (*(DWORD *)t == fun)
return i;
else
i += 6;
}
}
}
return 0;
}
DWORD Util::FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig)
{
//WCHAR str[0x40];
enum { reverse_length = 0x800 };
DWORD mask = SigMask(sig);
for (DWORD i = 0x1000; i < size - 4; i++)
if (*(WORD *)(pt + i) == 0x15ff) {
DWORD t = *(DWORD *)(pt + i + 2);
if (t >= pt && t <= pt + size - 4) {
if (*(DWORD *)t == fun)
//swprintf(str,L"CALL addr: 0x%.8X",pt + i);
//OutputConsole(str);
for (DWORD j = i ; j > i - reverse_length; j--)
if ((*(DWORD *)(pt + j) & mask) == sig) // Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",pt + j);
//OutputConsole(str);
return pt + j;
} else
i += 6;
}
//OutputConsole(L"Find call and entry failed.");
return 0;
}
DWORD Util::FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig)
{
//WCHAR str[0x40];
enum { reverse_length = 0x800 };
if (DWORD i = FindCallOrJmpRel(fun, size, pt, false)) {
DWORD mask = SigMask(sig);
for (DWORD j = i; j > i - reverse_length; j--)
if (((*(DWORD *)j) & mask) == sig) //Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",j);
//OutputConsole(str);
return j;
//OutputConsole(L"Find call and entry failed.");
}
return 0;
}
DWORD Util::FindEntryAligned(DWORD start, DWORD back_range)
{
start &= ~0xf;
for (DWORD i = start, j = start - back_range; i > j; i-=0x10) {
DWORD k = *(DWORD *)(i-4);
if (k == 0xcccccccc
|| k == 0x90909090
|| k == 0xccccccc3
|| k == 0x909090c3
)
return i;
DWORD t = k & 0xff0000ff;
if (t == 0xcc0000c2 || t == 0x900000c2)
return i;
k >>= 8;
if (k == 0xccccc3 || k == 0x9090c3)
return i;
t = k & 0xff;
if (t == 0xc2)
return i;
k >>= 8;
if (k == 0xccc3 || k == 0x90c3)
return i;
k >>= 8;
if (k == 0xc3)
return i;
}
return 0;
}
DWORD Util::FindImportEntry(DWORD hModule, DWORD fun)
{
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
DWORD IAT, end, pt, addr;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
IAT = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
end = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
IAT += hModule;
end += IAT;
for (pt = IAT; pt < end; pt += 4) {
addr = *(DWORD *)pt;
if (addr == fun)
return pt;
}
}
}
return 0;
}
// Search string in rsrc section. This section usually contains version and copyright info.
bool Util::SearchResourceString(LPCWSTR str)
{
DWORD hModule = Util::GetModuleBase();
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
DWORD rsrc, size;
//__asm int 3
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
rsrc = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
if (rsrc) {
rsrc += hModule;
if (IthGetMemoryRange((LPVOID)rsrc, &rsrc ,&size) &&
SearchPattern(rsrc, size - 4, str, wcslen(str) << 1))
return true;
}
}
}
return false;
}
// jichi 4/15/2014: Copied from GetModuleBase in ITH CLI, for debugging purpose
DWORD Util::FindModuleBase(DWORD hash)
{
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov esi,[eax+0x14]
mov edi,_wcslwr
listfind:
mov edx,[esi+0x28]
test edx,edx
jz notfound
push edx
call edi
pop edx
xor eax,eax
calc:
movzx ecx, word ptr [edx]
test cl,cl
jz fin
ror eax,7
add eax,ecx
add edx,2
jmp calc
fin:
cmp eax,[hash]
je found
mov esi,[esi]
jmp listfind
notfound:
xor eax,eax
jmp termin
found:
mov eax,[esi+0x10]
termin:
}
}
// EOF
#pragma once
// util/util.h
// 8/23/2013 jichi
#include "config.h"
namespace Util {
DWORD GetCodeRange(DWORD hModule,DWORD *low, DWORD *high);
DWORD FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig);
DWORD FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp);
DWORD FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp);
DWORD FindCallBoth(DWORD fun, DWORD size, DWORD pt);
DWORD FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig);
DWORD FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig);
DWORD FindEntryAligned(DWORD start, DWORD back_range);
DWORD FindImportEntry(DWORD hModule, DWORD fun);
// jichi 4/15/2014: Copied from ITH CLI, for debugging purpose
DWORD FindModuleBase(DWORD hash);
bool SearchResourceString(LPCWSTR str);
/**
* @param name process name without path deliminator
*/
inline void GetProcessName(wchar_t *name)
{
//assert(name);
PLDR_DATA_TABLE_ENTRY it;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
}
::wcscpy(name, it->BaseDllName.Buffer);
}
/**
* @param path with process name and directy name
*/
inline void GetProcessPath(wchar_t *path)
{
//assert(path);
PLDR_DATA_TABLE_ENTRY it;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
}
::wcscpy(path, it->FullDllName.Buffer);
}
/**
* @return HANDLE module handle
*/
inline DWORD GetModuleBase()
{
__asm
{
mov eax,fs:[0x18]
mov eax,[eax+0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov eax,[eax+0x18]
}
}
} // namespace Util
// EOF
// 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 "cli.h"
#include "engine/match.h"
#include "ith/common/except.h"
//#include "ith/common/growl.h"
#include "ith/sys/sys.h"
#include "disasm/disasm.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_disabled_ = false;
void DisableGDIHooks()
{ ::gdi_hook_disabled_ = true; }
static bool IsGDIFunction(LPCVOID addr)
{
static LPVOID funcs[] = { HOOK_GDI_FUNCTION_LIST };
for (size_t i = 0; i < sizeof(funcs)/sizeof(*funcs); i++)
if (addr == funcs[i])
return true;
return false;
}
//FilterRange filter[8];
DWORD flag,
enter_count;
TextHook *hookman,
*current_available;
// - Unnamed helpers -
namespace { // unnamed
//provide const time hook entry.
int userhook_count;
#if 0 // 3/6/2015 jichi: this hook is not used and hence disabled
const byte common_hook2[] = {
0x89, 0x3c,0xe4, // mov [esp],edi
0x60, // pushad
0x9c, // pushfd
0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
0x8b,0x32, // mov esi,[edx] ; return address
0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook
0xe8, 0,0,0,0, // call @hook
0x9d, // popfd
0x61, // popad
0x5f, // pop edi ; skip return address on stack
}; //...
#endif // 0
const BYTE common_hook[] = {
0x9c, // pushfd
0x60, // pushad
0x9c, // pushfd
0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
0x8b,0x32, // mov esi,[edx] ; return address
0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TexHhook
0xe8, 0,0,0,0, // call @hook
0x9d, // popfd
0x61, // popad
0x9d // popfd
};
/**
* jichi 7/19/2014
*
* @param original_addr
* @param new_addr
* @param hook_len
* @param original_len
* @return -1 if failed, else 0 if ?, else ?
*/
int MapInstruction(DWORD original_addr, DWORD new_addr, BYTE &hook_len, BYTE &original_len)
{
int flag = 0;
DWORD l = 0;
const BYTE *r = (const BYTE *)original_addr; // 7/19/2014 jichi: original address is not modified
BYTE *c = (BYTE *)new_addr; // 7/19/2014 jichi: but new address might be modified
while((r - (BYTE *) original_addr) < 5) {
l = ::disasm(r);
if (l == 0) {
ConsoleOutput("vnrcli:MapInstruction: FAILED: failed to disasm");
return -1;
}
::memcpy(c, r, l);
if (*r >= 0x70 && *r < 0x80) {
c[0] = 0xf;
c[1] = *r + 0x10;
c += 6;
__asm
{
mov eax,r
add eax,2
movsx edx,byte ptr [eax-1]
add edx,eax
mov eax,c
sub edx,eax
mov [eax-4],edx
}
} else if (*r == 0xeb) {
c[0] = 0xe9;
c += 5;
__asm
{
mov eax,r
add eax,2
movsx edx,[eax-1]
add edx,eax
mov eax,c
sub edx,eax
mov [eax-4],edx
}
if (r - (BYTE *)original_addr < 5 - l) {
ConsoleOutput("vnrcli:MapInstruction: not safe to move instruction right after short jmp");
return -1; // Not safe to move instruction right after short jmp.
} else
flag = 1;
} else if (*r == 0xe8 || *r == 0xe9) {
c[0]=*r;
c += 5;
flag = (*r == 0xe9);
__asm
{
mov eax,r
add eax,5
mov edx,[eax-4]
add edx,eax
mov eax,c
sub edx,eax
mov [eax-4],edx
}
} else if (*r == 0xf && (*(r + 1) >> 4) == 0x8) {
c += 6;
__asm
{
mov eax,r
mov edx,dword ptr [eax+2]
add eax,6
add eax,edx
mov edx,c
sub eax,edx
mov [edx-4],eax
}
}
else
c += l;
r += l;
}
original_len = r - (BYTE *)original_addr;
hook_len = c - (BYTE *)new_addr;
return flag;
}
//copy original instruction
//jmp back
DWORD GetModuleBase(DWORD hash)
{
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov esi,[eax+0x14]
mov edi,_wcslwr
listfind:
mov edx,[esi+0x28]
test edx,edx
jz notfound
push edx
call edi
pop edx
xor eax,eax
calc:
movzx ecx, word ptr [edx]
test cl,cl
jz fin
ror eax,7
add eax,ecx
add edx,2
jmp calc
fin:
cmp eax,[hash]
je found
mov esi,[esi]
jmp listfind
notfound:
xor eax,eax
jmp termin
found:
mov eax,[esi+0x10]
termin:
}
}
DWORD GetModuleBase()
{
__asm
{
mov eax, fs:[0x18]
mov eax, [eax + 0x30]
mov eax, [eax + 0xc]
mov eax, [eax + 0xc]
mov eax, [eax + 0x18]
}
}
//void NotifyHookInsert()
//{
// if (live)
// {
// BYTE buffer[0x10];
// *(DWORD*)buffer=-1;
// *(DWORD*)(buffer+4)=1;
// IO_STATUS_BLOCK ios;
// NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0);
// }
//}
__declspec(naked) void SafeExit() // Return to eax
{
__asm
{
mov [esp+0x24], eax
popfd
popad
retn
}
}
#if 0
// jichi 12/2/2013: This function mostly return 0.
// But sometimes return the hook address from TextHook::Send
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
{
//with_seh(hook->Send(dwDataBase, dwRetn));
seh_push_(seh_exit, 0, eax, ebx) // jichi 12/13/2013: only eax and ebx are available. ecx and edx are used.
__asm
{
push esi
push edx
call TextHook::UnsafeSend
test eax, eax
jz seh_exit // label in seh_pop
mov ecx, SafeExit
mov [esp + 8], ecx // jichi 12/13/2013: change exit point if Send returns non-zero, not + 8 beause two elements has been pused
}
seh_pop_(seh_exit)
__asm retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
}
#endif // 0
#if 1
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
{
// jichi 12/17/2013: The function parameters here are meaning leass. The parameters are in esi and edi
__asm
{
push esi
push edx
call TextHook::Send
test eax, eax
jz ok // label in seh_pop
mov ecx, SafeExit
mov [esp], ecx // jichi 12/13/2013: change exit point if Send returns non-zero
ok:
retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
}
}
#endif // 1
// jichi 12/13/2013: return if the retn address is within the filter dlls
inline bool HookFilter(DWORD retn)
{
for (DWORD i = 0; ::filter[i].lower; i++)
if (retn > ::filter[i].lower && retn < ::filter[i].upper)
return true;
return false;
}
} // unnamed namespace
// - 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
DWORD TextHook::Send(DWORD dwDataBase, DWORD dwRetn)
{
DWORD ret = 0;
//char b[0x100];
//::wcstombs(b, hook_name, 0x100);
//ConsoleOutput(b);
ITH_WITH_SEH(ret = UnsafeSend(dwDataBase, dwRetn));
return ret;
}
DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn)
{
enum { SMALL_BUFF_SIZE = 0x80 };
enum { MAX_DATA_SIZE = 0x10000 }; // jichi 12/25/2013: The same as the original ITH
DWORD dwCount,
dwAddr,
dwDataIn,
dwSplit;
BYTE *pbData,
pbSmallBuff[SMALL_BUFF_SIZE];
DWORD dwType = hp.type;
if (!live)
return 0;
if ((dwType & NO_CONTEXT) == 0 && HookFilter(dwRetn))
return 0;
// jichi 10/24/2014: Skip GDI functions
if (::gdi_hook_disabled_ && ::IsGDIFunction((LPCVOID)hp.addr))
return 0;
dwAddr = hp.addr;
/** jichi 12/24/2014
* @param addr function address
* @param frame real address of the function, supposed to be the same as addr
* @param stack address of current stack - 4
* @return If success, which is reverted
*/
if (::trigger)
::trigger = Engine::InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
// jichi 10/21/2014: Directly invoke engine functions.
//if (trigger) {
// if (InsertDynamicHook)
// trigger = InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
// else
// trigger = 0;
//}
#if 0 // diasble HOOK_AUXILIARY
// jichi 12/13/2013: None of known hooks are auxiliary
if (dwType & HOOK_AUXILIARY) {
//Clean hook when dynamic hook finished.
//AUX hook is only used for a foothold of dynamic hook.
if (!trigger) {
ClearHook();
// jichi 12/13/2013: This is the only place where this function could return non-zero value
// However, I non of the known hooks are auxiliary
return dwAddr;
}
return 0;
}
#endif // 0
// jichi 10/24/2014: generic hook function
if (hp.hook_fun && !hp.hook_fun(dwDataBase, &hp))
hp.hook_fun = nullptr;
if (dwType & HOOK_EMPTY) // jichi 10/24/2014: dummy hook only for dynamic hook
return 0;
// jichi 2/2/2015: Send multiple texts
for (BYTE textIndex = 0; textIndex <= hp.extra_text_count; textIndex++) {
dwCount = 0;
dwSplit = 0;
dwDataIn = *(DWORD *)(dwDataBase + hp.off); // default value
//if (dwType & EXTERN_HOOK) {
if (hp.text_fun) { // jichi 10/24/2014: remove EXTERN_HOOK
//DataFun fun=(DataFun)hp.text_fun;
//auto fun = hp.text_fun;
hp.text_fun(dwDataBase, &hp, textIndex, &dwDataIn, &dwSplit, &dwCount);
//if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
// return 0;
if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
dwSplit -= ::processStartAddress;
} else {
if (dwDataIn == 0)
return 0;
if (dwType & FIXING_SPLIT)
dwSplit = FIXED_SPLIT_VALUE; // fuse all threads, and prevent floating
else if (dwType & USING_SPLIT) {
dwSplit = *(DWORD *)(dwDataBase + hp.split);
if (dwType & SPLIT_INDIRECT) {
if (IthGetMemoryRange((LPVOID)(dwSplit + hp.split_ind), 0, 0))
dwSplit = *(DWORD *)(dwSplit + hp.split_ind);
else
return 0;
}
if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
dwSplit -= ::processStartAddress;
}
if (dwType & DATA_INDIRECT) {
if (IthGetMemoryRange((LPVOID)(dwDataIn + hp.ind), 0, 0))
dwDataIn = *(DWORD *)(dwDataIn + hp.ind);
else
return 0;
}
//if (dwType & PRINT_DWORD) {
// swprintf((WCHAR *)(pbSmallBuff + HEADER_SIZE), L"%.8X ", dwDataIn);
// dwDataIn = (DWORD)pbSmallBuff + HEADER_SIZE;
//}
dwCount = GetLength(dwDataBase, dwDataIn);
}
// jichi 12/25/2013: validate data size
if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
return 0;
size_t sz = dwCount + HEADER_SIZE;
if (sz >= SMALL_BUFF_SIZE)
pbData = new BYTE[sz];
//ITH_MEMSET_HEAP(pbData, 0, sz * sizeof(BYTE)); // jichi 9/26/2013: zero memory
else
pbData = pbSmallBuff;
if (hp.length_offset == 1) {
if (dwType & STRING_LAST_CHAR) {
LPWSTR ts = (LPWSTR)dwDataIn;
dwDataIn = ts[::wcslen(ts) -1];
}
dwDataIn &= 0xffff;
if ((dwType & BIG_ENDIAN) && (dwDataIn >> 8))
dwDataIn = _byteswap_ushort(dwDataIn & 0xffff);
if (dwCount == 1)
dwDataIn &= 0xff;
*(WORD *)(pbData + HEADER_SIZE) = dwDataIn & 0xffff;
}
else
::memcpy(pbData + HEADER_SIZE, (void *)dwDataIn, dwCount);
// jichi 10/14/2014: Add filter function
if (hp.filter_fun && !hp.filter_fun(pbData + HEADER_SIZE, &dwCount, &hp, textIndex) || dwCount <= 0) {
if (pbData != pbSmallBuff)
delete[] pbData;
return 0;
}
*(DWORD *)pbData = dwAddr;
if (dwType & (NO_CONTEXT|FIXING_SPLIT))
dwRetn = 0;
else if (dwRetn && (dwType & RELATIVE_SPLIT))
dwRetn -= ::processStartAddress;
*((DWORD *)pbData + 1) = dwRetn;
*((DWORD *)pbData + 2) = dwSplit;
if (dwCount) {
IO_STATUS_BLOCK ios = {};
IthCoolDown(); // jichi 9/28/2013: cool down to prevent parallelization in wine
//CliLockPipe();
if (STATUS_PENDING == NtWriteFile(hPipe, 0, 0, 0, &ios, pbData, dwCount + HEADER_SIZE, 0, 0)) {
NtWaitForSingleObject(hPipe, 0, 0);
NtFlushBuffersFile(hPipe, &ios);
}
//CliUnlockPipe();
}
if (pbData != pbSmallBuff)
delete[] pbData;
}
return 0;
}
int TextHook::InsertHook()
{
//ConsoleOutput("vnrcli:InsertHook: enter");
NtWaitForSingleObject(hmMutex, 0, 0);
int ok = InsertHookCode();
IthReleaseMutex(hmMutex);
if (hp.type & HOOK_ADDITIONAL) {
NotifyHookInsert(hp.addr);
//ConsoleOutput(hook_name);
//RegisterHookName(hook_name,hp.addr);
}
//ConsoleOutput("vnrcli:InsertHook: leave");
return ok;
}
int TextHook::InsertHookCode()
{
enum : int { yes = 0, no = 1 };
DWORD ret = no;
// jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7
ITH_WITH_SEH(ret = UnsafeInsertHookCode());
//if (ret == no)
// ITH_WARN(L"Failed to insert hook");
return ret;
}
int TextHook::UnsafeInsertHookCode()
{
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter");
enum : int { yes = 0, no = 1 };
// MODULE_OFFSET is set, but there's no module address
// this means that this is an absolute address found on Windows 2000/XP
// we make the address relative to the process base
// we also store the original address in the function field because normally there can not
// exist a function address without a module address
if (hp.type & MODULE_OFFSET && !hp.module) {
DWORD base = GetModuleBase();
hp.function = hp.addr;
hp.addr -= 0x400000;
hp.addr += base;
hp.type &= ~MODULE_OFFSET;
}
else if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address.
if (DWORD base = GetModuleBase(hp.module)) {
if (hp.function && (hp.type & FUNCTION_OFFSET)) {
base = GetExportAddress(base, hp.function);
if (base)
hp.addr += base;
else {
current_hook--;
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: function not found in the export table");
return no;
}
}
else {
hp.addr += base;
}
hp.type &= ~(MODULE_OFFSET | FUNCTION_OFFSET);
}
else {
current_hook--;
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: module not present");
return no;
}
}
{
TextHook *it = hookman;
for (int i = 0; (i < current_hook) && it; it++) { // Check if there is a collision.
if (it->Address())
i++;
//it = hookman + i;
if (it == this)
continue;
if (it->Address() <= hp.addr &&
it->Address() + it->Length() > hp.addr) {
it->ClearHook();
break;
}
}
}
// Verify hp.addr.
MEMORY_BASIC_INFORMATION info = {};
NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)hp.addr, MemoryBasicInformation, &info, sizeof(info), nullptr);
if (info.Type & PAGE_NOACCESS) {
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: page no access");
return no;
}
// Initialize common routine.
memcpy(recover, common_hook, sizeof(common_hook));
BYTE *c = (BYTE *)hp.addr,
*r = recover;
BYTE inst[8]; // jichi 9/27/2013: Why 8? Only 5 bytes will be written using NtWriteVirtualMemory
inst[0] = 0xe9; // jichi 9/27/2013: 0xe9 is jump, see: http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Hackers_Manual
__asm
{
mov edx,r // r = recover
mov eax,this
mov [edx+0xa],eax // push TextHook*, resolve to correspond hook.
lea eax,[edx+0x13]
mov edx,ProcessHook
sub edx,eax
mov [eax-4],edx // call ProcessHook
mov eax,c
add eax,5
mov edx,r
sub edx,eax
lea eax,inst+1
mov [eax],edx // jichi 12/17/2013: the parameter of jmp is in edx. So, ProcessHook must be naked.
}
r += sizeof(common_hook);
hp.hook_len = 5;
//bool jmpflag=false; // jichi 9/28/2013: nto used
// Copy original code.
switch (MapInstruction(hp.addr, (DWORD)r, hp.hook_len, hp.recover_len)) {
case -1:
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: failed to map instruction");
return no;
case 0:
__asm
{
mov ecx,this
movzx eax,[ecx]hp.hook_len
movzx edx,[ecx]hp.recover_len
add edx,[ecx]hp.addr
add eax,r
add eax,5
sub edx,eax
mov [eax-5],0xe9 // jichi 9/27/2013: 0xe9 is jump
mov [eax-4],edx
}
}
// jichi 9/27/2013: Save the original instructions in the memory
memcpy(original, (LPVOID)hp.addr, hp.recover_len);
//Check if the new hook range conflict with existing ones. Clear older if conflict.
{
TextHook *it = hookman;
for (int i = 0; i < current_hook; it++) {
if (it->Address())
i++;
if (it == this)
continue;
if (it->Address() >= hp.addr &&
it->Address() < hp.hook_len + hp.addr) {
it->ClearHook();
break;
}
}
}
// Insert hook and flush instruction cache.
enum {c8 = 0xcccccccc};
DWORD int3[] = {c8, c8};
DWORD t = 0x100,
old,
len;
// jichi 9/27/2013: Overwrite the memory with inst
// See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Memory%20Management/Virtual%20Memory/NtProtectVirtualMemory.html
// See: http://doxygen.reactos.org/d8/d6b/ndk_2mmfuncs_8h_af942709e0c57981d84586e74621912cd.html
DWORD addr = hp.addr;
NtProtectVirtualMemory(NtCurrentProcess(), (PVOID *)&addr, &t, PAGE_EXECUTE_READWRITE, &old);
NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.addr, inst, 5, &t);
len = hp.recover_len - 5;
if (len)
NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.addr + 5, int3, len, &t);
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)::hookman, 0x1000);
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: leave: succeed");
return 0;
}
int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind,
DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
{
NtWaitForSingleObject(hmMutex, 0, 0);
hp.addr = (DWORD)addr;
hp.off = data;
hp.ind = data_ind;
hp.split = split_off;
hp.split_ind = split_ind;
hp.type = type;
hp.hook_len = 0;
hp.module = 0;
hp.length_offset = len_off & 0xffff;
current_hook++;
if (current_available >= this)
for (current_available = this + 1; current_available->Address(); current_available++);
IthReleaseMutex(hmMutex);
return this - hookman;
}
int TextHook::InitHook(const HookParam &h, LPCWSTR name, WORD set_flag)
{
NtWaitForSingleObject(hmMutex, 0, 0);
hp = h;
hp.type |= set_flag;
if (name && name != hook_name) {
SetHookName(name);
}
current_hook++;
current_available = this+1;
while (current_available->Address())
current_available++;
IthReleaseMutex(hmMutex);
return 1;
}
int TextHook::RemoveHook()
{
enum : int { yes = 1, no = 0 };
if (!hp.addr)
return no;
ConsoleOutput("vnrcli:RemoveHook: enter");
const LONGLONG timeout = -50000000; // jichi 9/28/2012: in 100ns, wait at most for 5 seconds
NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout);
DWORD l = hp.hook_len;
//with_seh({ // jichi 9/17/2013: might crash ><
// jichi 12/25/2013: Actually, __try cannot catch such kind of exception
ITH_TRY {
NtWriteVirtualMemory(NtCurrentProcess(), (LPVOID)hp.addr, original, hp.recover_len, &l);
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
} ITH_EXCEPT {}
//});
hp.hook_len = 0;
IthReleaseMutex(hmMutex);
ConsoleOutput("vnrcli:RemoveHook: leave");
return yes;
}
int TextHook::ClearHook()
{
NtWaitForSingleObject(hmMutex, 0, 0);
int err = RemoveHook();
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;
current_hook--;
IthReleaseMutex(hmMutex);
return err;
}
int TextHook::ModifyHook(const HookParam &hp)
{
//WCHAR name[0x40];
DWORD len = 0;
if (hook_name)
len = wcslen(hook_name);
LPWSTR name = 0;
if (len) {
name = new wchar_t[len + 1];
//ITH_MEMSET_HEAP(name, 0, sizeof(wchar_t) * (len + 1)); // jichi 9/26/2013: zero memory
name[len] = 0;
wcscpy(name, hook_name);
}
ClearHook();
InitHook(hp,name);
InsertHook();
if (name)
delete[] name;
return 0;
}
int TextHook::RecoverHook()
{
if (hp.addr) {
// jichi 9/28/2013: Only enable TextOutA to debug Cross Channel
//if (hp.addr == (DWORD)TextOutA)
InsertHook();
return 1;
}
return 0;
}
int TextHook::SetHookName(LPCWSTR name)
{
name_length = wcslen(name) + 1;
if (hook_name)
delete[] hook_name;
hook_name = new wchar_t[name_length];
//ITH_MEMSET_HEAP(hook_name, 0, sizeof(wchar_t) * name_length); // jichi 9/26/2013: zero memory
hook_name[name_length - 1] = 0;
wcscpy(hook_name, name);
return 0;
}
int TextHook::GetLength(DWORD base, DWORD in)
{
if (base == 0)
return 0;
int len;
switch (hp.length_offset) {
default: // jichi 12/26/2013: I should not put this default branch to the end
len = *((int *)base + hp.length_offset);
if (len >= 0) {
if (hp.type & USING_UNICODE)
len <<= 1;
break;
}
else if (len != -1)
break;
//len == -1 then continue to case 0.
case 0:
if (hp.type & USING_UNICODE)
len = wcslen((const wchar_t *)in) << 1;
else
len = strlen((const char *)in);
break;
case 1:
if (hp.type & USING_UNICODE)
len = 2;
else {
if (hp.type & BIG_ENDIAN)
in >>= 8;
len = LeadByteTable[in & 0xff]; //Slightly faster than IsDBCSLeadByte
}
break;
}
// jichi 12/25/2013: This function originally return -1 if failed
//return len;
return max(0, len);
}
// EOF
//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
/*
DWORD recv_esp, recv_addr;
EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
{
//WCHAR str[0x40],
// name[0x100];
//ConsoleOutput(L"Exception raised during hook processing.");
//swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
//ConsoleOutput(str);
//MEMORY_BASIC_INFORMATION info;
//if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemoryBasicInformation,&info,sizeof(info),0)) &&
// NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemorySectionName,name,0x200,0))) {
// swprintf(str, L"Exception offset: 0x%.8X:%s",
// ContextRecord->Eip-(DWORD)info.AllocationBase,
// wcsrchr(name,L'\\')+1);
// ConsoleOutput(str);
//}
ContextRecord->Esp = recv_esp;
ContextRecord->Eip = recv_addr;
return ExceptionContinueExecution;
}
//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
DWORD recv_esp, recv_addr;
EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
{
//WCHAR str[0x40],
// name[0x100];
//ConsoleOutput(L"Exception raised during hook processing.");
//swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
//ConsoleOutput(str);
//MEMORY_BASIC_INFORMATION info;
//if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemoryBasicInformation,&info,sizeof(info),0)) &&
// NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemorySectionName,name,0x200,0))) {
// swprintf(str, L"Exception offset: 0x%.8X:%s",
// ContextRecord->Eip-(DWORD)info.AllocationBase,
// wcsrchr(name,L'\\')+1);
// ConsoleOutput(str);
//}
ContextRecord->Esp = recv_esp;
ContextRecord->Eip = recv_addr;
return ExceptionContinueExecution;
}
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
{
__asm
{
mov eax,seh_recover
mov recv_addr,eax
push ExceptHandler
push fs:[0]
mov recv_esp,esp
mov fs:[0],esp
push esi
push edx
call TextHook::Send
test eax,eax
jz seh_recover
mov ecx,SafeExit
mov [esp + 0x8], ecx // change exit point
seh_recover:
pop dword ptr fs:[0]
pop ecx
retn
}
}
*/