mireado

starting commit

1 +#pragma once
2 +
3 +// ith/common/defs.h
4 +// 8/23/2013 jichi
5 +
6 +// DLL files
7 +
8 +//#define ITH_SERVER_DLL L"vnrsrv.dll"
9 +//#define ITH_CLIENT_DLL L"vnrcli.dll"
10 +//#define ITH_CLIENT_XP_DLL L"vnrclixp.dll"
11 +////#define ITH_CLIENT_UX_DLL L"vnrcliux.dll"
12 +//#define ITH_ENGINE_DLL L"vnreng.dll"
13 +//#define ITH_ENGINE_XP_DLL L"vnrengxp.dll"
14 +//#define ITH_ENGINE_UX_DLL L"vnrengux.dll"
15 +
16 +#define ITH_DLL L"vnrhook.dll"
17 +#define ITH_DLL_XP L"vnrhookxp.dll"
18 +
19 +// Pipes
20 +
21 +#define ITH_TEXT_PIPE L"\\??\\pipe\\VNR_TEXT"
22 +#define ITH_COMMAND_PIPE L"\\??\\pipe\\VNR_COMMAND"
23 +
24 +// Sections
25 +
26 +#define ITH_SECTION_ L"VNR_SECTION_" // _%d
27 +
28 +// Mutex
29 +
30 +#define ITH_PROCESS_MUTEX_ L"VNR_PROCESS_" // ITH_%d
31 +#define ITH_HOOKMAN_MUTEX_ L"VNR_HOOKMAN_" // ITH_HOOKMAN_%d
32 +#define ITH_DETACH_MUTEX_ L"VNR_DETACH_" // ITH_DETACH_%d
33 +
34 +#define ITH_GRANTPIPE_MUTEX L"VNR_GRANT_PIPE" // ITH_GRANT_PIPE
35 +
36 +//#define ITH_ENGINE_MUTEX L"VNR_ENGINE" // ITH_ENGINE
37 +#define ITH_CLIENT_MUTEX L"VNR_CLIENT" // ITH_DLL_RUNNING
38 +#define ITH_SERVER_MUTEX L"VNR_SERVER" // ITH_RUNNING
39 +#define ITH_SERVER_HOOK_MUTEX L"VNR_SERVER_HOOK" // original
40 +
41 +// Events
42 +
43 +#define ITH_REMOVEHOOK_EVENT L"VNR_REMOVE_HOOK" // ITH_REMOVE_HOOK
44 +#define ITH_MODIFYHOOK_EVENT L"VNR_MODIFY_HOOK" // ITH_MODIFY_HOOK
45 +#define ITH_PIPEEXISTS_EVENT L"VNR_PIPE_EXISTS" // ITH_PIPE_EXIST
46 +
47 +// EOF
1 +#pragma once
2 +
3 +// ith/common/except.h
4 +// 9/17/2013 jichi
5 +
6 +#define ITH_RAISE (*(int*)0 = 0) // raise C000005, for debugging only
7 +
8 +#ifdef ITH_HAS_SEH
9 +
10 +# define ITH_TRY __try
11 +# define ITH_EXCEPT __except(EXCEPTION_EXECUTE_HANDLER)
12 +# define ITH_WITH_SEH(...) \
13 + ITH_TRY { __VA_ARGS__; } ITH_EXCEPT {}
14 +
15 +#else // for old msvcrt.dll on Windows XP that does not have exception handler
16 +
17 +// Currently, only with_seh is implemented. Try and catch are not.
18 +# define ITH_TRY if (true)
19 +# define ITH_EXCEPT else
20 +# include "winseh/winseh.h"
21 +# define ITH_WITH_SEH(...) seh_with(__VA_ARGS__)
22 +
23 +#endif // ITH_HAS_SEH
24 +
25 +// EOF
1 +#pragma once
2 +
3 +// ith/common/growl.h
4 +// 9/17/2013 jichi
5 +
6 +//#ifdef ITH_HAS_GROWL
7 +
8 +#include <windows.h>
9 +#include "ith/common/string.h"
10 +
11 +#define ITH_MSG_A(_msg) MessageBoxA(nullptr, _msg, "VNR Message", MB_OK)
12 +#define ITH_MSG(_msg) MessageBoxW(nullptr, _msg, L"VNR Message", MB_OK)
13 +#define ITH_WARN(_msg) MessageBoxW(nullptr, _msg, L"VNR Warning", MB_OK)
14 +#define ITH_ERROR(_msg) MessageBoxW(nullptr, _msg, L"VNR Error", MB_OK)
15 +
16 +inline void ITH_GROWL_DWORD(DWORD value)
17 +{
18 + WCHAR buf[100];
19 + swprintf(buf, L"DWORD: %x", value);
20 + ITH_MSG(buf);
21 +}
22 +
23 +inline void ITH_GROWL_DWORD2(DWORD v, DWORD v2)
24 +{
25 + WCHAR buf[100];
26 + swprintf(buf, L"DWORD2: %x,%x", v, v2);
27 + ITH_MSG(buf);
28 +}
29 +
30 +inline void ITH_GROWL_DWORD3(DWORD v, DWORD v2, DWORD v3)
31 +{
32 + WCHAR buf[100];
33 + swprintf(buf, L"DWORD3: %x,%x,%x", v, v2, v3);
34 + ITH_MSG(buf);
35 +}
36 +
37 +inline void ITH_GROWL_DWORD4(DWORD v, DWORD v2, DWORD v3, DWORD v4)
38 +{
39 + WCHAR buf[100];
40 + swprintf(buf, L"DWORD4: %x,%x,%x,%x", v, v2, v3, v4);
41 + ITH_MSG(buf);
42 +}
43 +
44 +inline void ITH_GROWL_DWORD5(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5)
45 +{
46 + WCHAR buf[100];
47 + swprintf(buf, L"DWORD5: %x,%x,%x,%x,%x", v, v2, v3, v4, v5);
48 + ITH_MSG(buf);
49 +}
50 +
51 +inline void ITH_GROWL_DWORD6(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6)
52 +{
53 + WCHAR buf[100];
54 + swprintf(buf, L"DWORD6: %x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6);
55 + ITH_MSG(buf);
56 +}
57 +
58 +inline void ITH_GROWL_DWORD7(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7)
59 +{
60 + WCHAR buf[100];
61 + swprintf(buf, L"DWORD7: %x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7);
62 + ITH_MSG(buf);
63 +}
64 +
65 +inline void ITH_GROWL_DWORD8(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8)
66 +{
67 + WCHAR buf[100];
68 + swprintf(buf, L"DWORD8: %x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8);
69 + ITH_MSG(buf);
70 +}
71 +
72 +inline void ITH_GROWL_DWORD9(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8, DWORD v9)
73 +{
74 + WCHAR buf[100];
75 + swprintf(buf, L"DWORD9: %x,%x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8, v9);
76 + ITH_MSG(buf);
77 +}
78 +
79 +inline void ITH_GROWL(DWORD v) { ITH_GROWL_DWORD(v); }
80 +inline void ITH_GROWL(LPCWSTR v) { ITH_MSG(v); }
81 +inline void ITH_GROWL(LPCSTR v) { ITH_MSG_A(v); }
82 +
83 +//#endif // ITH_HAS_GROWL
84 +
85 +// EOF
1 +#pragma once
2 +
3 +// ith/common/memory.h
4 +// 8/23/2013 jichi
5 +// Branch: ITH/mem.h, revision 66
6 +
7 +#ifndef ITH_HAS_HEAP
8 +# define ITH_MEMSET_HEAP(...) ::memset(__VA_ARGS__)
9 +#else
10 +# define ITH_MEMSET_HEAP(...) (void)0
11 +
12 +// Defined in kernel32.lilb
13 +extern "C" {
14 +// PVOID RtlAllocateHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ SIZE_T Size);
15 +__declspec(dllimport) void * __stdcall RtlAllocateHeap(void *HeapHandle, unsigned long Flags, unsigned long Size);
16 +
17 +// BOOLEAN RtlFreeHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ PVOID HeapBase);
18 +__declspec(dllimport) int __stdcall RtlFreeHeap(void *HeapHandle, unsigned long Flags, void *HeapBase);
19 +} // extern "C"
20 +
21 +//NTSYSAPI
22 +//BOOL
23 +//NTAPI
24 +//RtlFreeHeap(
25 +// _In_ HANDLE hHeap,
26 +// _In_ DWORD dwFlags,
27 +// _In_ LPVOID lpMem
28 +//);
29 +
30 +extern void *hHeap; // defined in ith/sys.cc
31 +
32 +inline void * __cdecl operator new(size_t lSize)
33 +{
34 + // http://msdn.microsoft.com/en-us/library/windows/desktop/aa366597%28v=vs.85%29.aspx
35 + // HEAP_ZERO_MEMORY flag is critical. All new objects are assumed with zero initialized.
36 + enum { HEAP_ZERO_MEMORY = 0x00000008 };
37 + return RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize);
38 +}
39 +
40 +inline void __cdecl operator delete(void *pBlock)
41 +{ RtlFreeHeap(::hHeap, 0, pBlock); }
42 +
43 +inline void __cdecl operator delete[](void *pBlock)
44 +{ RtlFreeHeap(::hHeap, 0, pBlock); }
45 +
46 +#endif // ITH_HAS_HEAP
1 +#pragma once
2 +
3 +// ith/common/string.h
4 +// 8/9/2013 jichi
5 +// Branch: ITH/string.h, rev 66
6 +
7 +#ifdef ITH_HAS_CRT // ITH is linked with msvcrt dlls
8 +# include <cstdio>
9 +# include <cstring>
10 +
11 +#else
12 +# define _INC_SWPRINTF_INL_
13 +# define CRT_IMPORT __declspec(dllimport)
14 +
15 +#include <windows.h> // for wchar_t
16 +extern "C" {
17 +CRT_IMPORT int swprintf(wchar_t *src, const wchar_t *fmt, ...);
18 +CRT_IMPORT int sprintf(char *src, const char *fmt, ...);
19 +CRT_IMPORT int swscanf(const wchar_t *src, const wchar_t *fmt, ...);
20 +CRT_IMPORT int sscanf(const char *src, const char *fmt, ...);
21 +CRT_IMPORT int wprintf(const wchar_t *fmt, ...);
22 +CRT_IMPORT int printf(const char *fmt, ...);
23 +CRT_IMPORT int _wputs(const wchar_t *src);
24 +CRT_IMPORT int puts(const char *src);
25 +CRT_IMPORT int _stricmp(const char *x, const char *y);
26 +CRT_IMPORT int _wcsicmp(const wchar_t *x, const wchar_t *y);
27 +//CRT_IMPORT size_t strlen(const char *);
28 +//CRT_IMPORT size_t wcslen(const wchar_t *);
29 +//CRT_IMPORT char *strcpy(char *,const char *);
30 +//CRT_IMPORT wchar_t *wcscpy(wchar_t *,const wchar_t *);
31 +CRT_IMPORT void *memmove(void *dst, const void *src, size_t sz);
32 +CRT_IMPORT const char *strchr(const char *src, int val);
33 +CRT_IMPORT int strncmp(const char *x, const char *y, size_t sz);
34 +} // extern "C"
35 +
36 +#endif // ITH_HAS_CRT
1 +#pragma once
2 +
3 +// ith/common/types.h
4 +// 8/23/2013 jichi
5 +// Branch: ITH/common.h, rev 128
6 +
7 +#include <windows.h> // needed for windef types
8 +
9 + /** jichi 3/7/2014: Add guessed comment
10 + *
11 + * DWORD addr absolute or relative address
12 + * DWORD split esp offset of the split character
13 + *
14 + * http://faydoc.tripod.com/cpu/pushad.htm
15 + * http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
16 + * The order is the same as pushd
17 + * 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
18 + * 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
19 + */
20 +struct HookParam {
21 + // jichi 8/24/2013: For special hooks. Original name: DataFun
22 + typedef void (*text_fun_t)(DWORD esp, HookParam *hp, BYTE index, DWORD *data, DWORD *split, DWORD *len);
23 +
24 + // jichi 10/24/2014: Add filter function. Return the if skip the text
25 + typedef bool (*filter_fun_t)(LPVOID str, DWORD *len, HookParam *hp, BYTE index);
26 +
27 + // jichi 10/24/2014: Add generic hook function, return false if stop execution.
28 + typedef bool (*hook_fun_t)(DWORD esp, HookParam *hp);
29 +
30 + DWORD addr; // absolute or relative address
31 + DWORD off, // offset of the data in the memory
32 + ind, // ?
33 + split, // esp offset of the split character = pusha offset - 4
34 + split_ind; // ?
35 + DWORD module, // hash of the module
36 + function;
37 + text_fun_t text_fun;
38 + filter_fun_t filter_fun;
39 + hook_fun_t hook_fun;
40 + DWORD type; // flags
41 + WORD length_offset; // index of the string length
42 + BYTE hook_len, // ?
43 + recover_len; // ?
44 +
45 + // 2/2/2015: jichi number of times - 1 to run the hook
46 + BYTE extra_text_count;
47 + BYTE _unused; // jichi 2/2/2015: add a BYTE type to make to total sizeof(HookParam) even.
48 +
49 + // 7/20/2014: jichi additional parameters for PSP games
50 + DWORD user_flags,
51 + user_value;
52 +};
53 +
54 +// jichi 6/1/2014: Structure of the esp for extern functions
55 +struct HookStack
56 +{
57 + // pushad
58 + DWORD edi, // -0x24
59 + esi, // -0x20
60 + ebp, // -0x1c
61 + esp, // -0x18
62 + ebx, // -0x14
63 + edx, // -0x10
64 + ecx, // -0xc
65 + eax; // -0x8
66 + // pushfd
67 + DWORD eflags; // -0x4
68 + DWORD retaddr; // 0
69 + DWORD args[1]; // 0x4
70 +};
71 +
72 +struct SendParam {
73 + DWORD type;
74 + HookParam hp;
75 +};
76 +
77 +struct Hook { // size: 0x80
78 + HookParam hp;
79 + LPWSTR hook_name;
80 + int name_length;
81 + BYTE recover[0x68 - sizeof(HookParam)];
82 + BYTE original[0x10];
83 +
84 + DWORD Address() const { return hp.addr; }
85 + DWORD Type() const { return hp.type; }
86 + WORD Length() const { return hp.hook_len; }
87 + LPWSTR Name() const { return hook_name; }
88 + int NameLength() const { return name_length; }
89 +};
90 +
91 +// EOF
1 +#pragma once
2 +
3 +// dllconfig.h
4 +// 8/23/2013 jichi
5 +
6 +#include "ith/common/memory.h"
7 +#include "ith/common/string.h"
8 +#include "ntdll/ntdll.h"
9 +
10 +// EOF
1 +# dllconfig.pri
2 +# 8/9/2013 jichi
3 +# For linking ITH injectable dlls.
4 +# The dll is self-containd and Windows-independent.
5 +
6 +CONFIG += dll noqt #noeh nosafeseh
7 +CONFIG -= embed_manifest_dll # dynamically load dlls
8 +win32 {
9 + CONFIG(eh): DEFINES += ITH_HAS_SEH # Do have exception handler in msvcrt.dll on Windows Vista and later
10 + CONFIG(noeh): DEFINES -= ITH_HAS_SEH # Do not have exception handler in msvcrt.dll on Windows XP and before
11 +}
12 +include(../../../config.pri)
13 +#win32 {
14 +# CONFIG(noeh): include($$LIBDIR/winseh/winseh_safe.pri)
15 +#}
16 +
17 +# jichi 11/24/2013: Disable manual heap
18 +DEFINES -= ITH_HAS_HEAP
19 +
20 +# jichi 11/13/2011: disable swprinf warning
21 +DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
22 +
23 +## Libraries
24 +
25 +#LIBS += -lkernel32 -luser32 -lgdi32
26 +LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
27 +LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
28 +#LIBS += -L$$WDK7_HOME/lib/crt/i386 -lmsvcrt
29 +#QMAKE_LFLAGS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # This will leave runtime flags in the dll
30 +
31 +#LIBS += -L$$WDK8_HOME/lib/winv6.3/um/x86 -lntdll
32 +
33 +HEADERS += $$PWD/dllconfig.h
34 +
35 +# EOF
1 +# hook.pro
2 +# CONFIG += eh eha
3 +# include(../dllconfig.pri)
4 +
5 +# hookxp.pro
6 +# CONFIG += noeh
7 +# include(../dllconfig.pri)
8 +
9 +# dllconfig.pri
10 +# include(../../../config.pri)
11 +# win32 {
12 +# CONFIG(eh): DEFINES += ITH_HAS_SEH
13 +# CONFIG(noeh): DEFINES -= ITH_HAS_SEH
14 +# }
15 +
16 +# config.pri
17 +# CONFIG(eha) {
18 +# message(CONFIG eha)
19 +# QMAKE_CXXFLAGS_STL_ON -= /EHsc
20 +# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
21 +# QMAKE_CXXFLAGS_STL_ON += /EHa
22 +# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
23 +# }
24 +#
25 +# CONFIG(noeh) { # No Exception handler
26 +# QMAKE_CXXFLAGS += /GR-
27 +# QMAKE_CXXFLAGS_RTTI_ON -= /GR
28 +# QMAKE_CXXFLAGS_STL_ON -= /EHsc
29 +# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
30 +# }
31 +
32 +include_directories(${CMAKE_CURRENT_SOURCE_DIR})
33 +
34 +set(vnrhook_src
35 + cli.h
36 + config.h
37 + hook.h
38 + main.cc
39 + engine/engine.cc
40 + engine/engine.h
41 + engine/hookdefs.h
42 + engine/match.cc
43 + engine/match.h
44 + engine/pchooks.cc
45 + engine/pchooks.h
46 + engine/util.cc
47 + engine/util.h
48 + hijack/texthook.cc
49 + rpc/pipe.cc
50 + tree/avl.h
51 + ${PROJECT_SOURCE_DIR}/ccutil/ccmacro.h
52 + ${PROJECT_SOURCE_DIR}/cpputil/cpplocale.h
53 + ${PROJECT_SOURCE_DIR}/cpputil/cppmarshal.h
54 + ${PROJECT_SOURCE_DIR}/cpputil/cppmath.h
55 + ${PROJECT_SOURCE_DIR}/cpputil/cpppath.h
56 + ${PROJECT_SOURCE_DIR}/cpputil/cppstring.h
57 + ${PROJECT_SOURCE_DIR}/cpputil/cpptype.h
58 + ${PROJECT_SOURCE_DIR}/cpputil/cppunicode.h
59 + ${PROJECT_SOURCE_DIR}/disasm/disasm.cc
60 + ${PROJECT_SOURCE_DIR}/memdbg/memdbg.h
61 + ${PROJECT_SOURCE_DIR}/memdbg/memsearch.cc
62 + ${PROJECT_SOURCE_DIR}/memdbg/memsearch.h
63 + ${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.cc
64 + ${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.h
65 + ${PROJECT_SOURCE_DIR}/winversion/winversion.cc
66 + ${PROJECT_SOURCE_DIR}/winversion/winversion.h
67 + ${common_src}
68 + ${import_src}
69 +)
70 +
71 +source_group("common" FILES ${common_src})
72 +source_group("import" FILES ${import_src})
73 +
74 +add_library(vnrhook SHARED ${vnrhook_src})
75 +
76 +set(vnrhookxp_src ${vnrhook_src}
77 + ${PROJECT_SOURCE_DIR}/winseh/winseh.cc
78 + ${PROJECT_SOURCE_DIR}/winseh/winseh_safe.cc
79 + ${PROJECT_SOURCE_DIR}/winseh/winseh.h
80 + ${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
81 +)
82 +
83 +enable_language(ASM_MASM)
84 +
85 +set_source_files_properties(
86 + ${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
87 + PROPERTIES
88 + # CMAKE_ASM_MASM_FLAGS /safeseh # CMake bug 14711: http://www.cmake.org/Bug/view.php?id=14711
89 + COMPILE_FLAGS /safeseh
90 +)
91 +
92 +add_library(vnrhookxp SHARED ${vnrhookxp_src})
93 +
94 +set_target_properties(vnrhook vnrhookxp PROPERTIES
95 + LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO"
96 +)
97 +
98 +target_compile_options(vnrhook PRIVATE
99 + /EHa
100 + $<$<CONFIG:Release>:>
101 + $<$<CONFIG:Debug>:>
102 +)
103 +
104 +target_compile_options(vnrhookxp PRIVATE
105 + /GR-
106 +# /EHs-c- # disable exception handling # CMake bug 15243: http://www.cmake.org/Bug/view.php?id=15243
107 + $<$<CONFIG:Release>:>
108 + $<$<CONFIG:Debug>:>
109 +)
110 +
111 +if(TARGET vnrhookxp)
112 + STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
113 +endif(TARGET vnrhookxp)
114 +
115 +set(vnrhook_libs
116 + vnrsys
117 + ${WDK_HOME}/lib/wxp/i386/ntdll.lib
118 + Version.lib
119 +)
120 +
121 +target_link_libraries(vnrhook ${vnrhook_libs})
122 +target_link_libraries(vnrhookxp ${vnrhook_libs})
123 +
124 +target_compile_definitions(vnrhook
125 + PRIVATE
126 + -DITH_HAS_SEH
127 +)
128 +target_compile_definitions(vnrhookxp
129 + PRIVATE
130 +)
131 +
132 +install(TARGETS vnrhook vnrhookxp RUNTIME
133 + DESTINATION .
134 + CONFIGURATIONS Release
135 +)
1 +#pragma once
2 +
3 +// cli.h
4 +// 8/24/2013 jichi
5 +// Branch: IHF_DLL/IHF_CLIENT.h, rev 133
6 +//
7 +// 8/24/2013 TODO:
8 +// - Clean up this file
9 +// - Reduce global variables. Use namespaces or singleton classes instead.
10 +
11 +//#include <windows.h>
12 +//#define IHF
13 +#include "config.h"
14 +#include "hook.h"
15 +
16 +// jichi 12/25/2013: Header in each message sent to vnrsrv
17 +// There are totally three elements
18 +// - 0x0 dwAddr hook address
19 +// - 0x4 dwRetn return address
20 +// - 0x8 dwSplit split value
21 +#define HEADER_SIZE 0xc
22 +
23 +extern int current_hook;
24 +extern WCHAR dll_mutex[];
25 +//extern WCHAR dll_name[];
26 +extern DWORD trigger;
27 +//extern DWORD current_process_id;
28 +
29 +// jichi 6/3/2014: Get memory range of the current module
30 +extern DWORD processStartAddress,
31 + processStopAddress;
32 +
33 +template <class T, class D, class fComp, class fCopy, class fLength>
34 +class AVLTree;
35 +struct FunctionInfo {
36 + DWORD addr;
37 + DWORD module;
38 + DWORD size;
39 + LPWSTR name;
40 +};
41 +struct SCMP;
42 +struct SCPY;
43 +struct SLEN;
44 +extern AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN> *tree;
45 +
46 +void InitFilterTable();
47 +
48 +// jichi 9/25/2013: This class will be used by NtMapViewOfSectionfor
49 +// interprocedure communication, where constructor/destructor will NOT work.
50 +class TextHook : public Hook
51 +{
52 + int UnsafeInsertHookCode();
53 + DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn);
54 +public:
55 + int InsertHook();
56 + int InsertHookCode();
57 + int InitHook(const HookParam &hp, LPCWSTR name = 0, WORD set_flag = 0);
58 + int InitHook(LPVOID addr, DWORD data, DWORD data_ind,
59 + DWORD split_off, DWORD split_ind, WORD type, DWORD len_off = 0);
60 + DWORD Send(DWORD dwDataBase, DWORD dwRetn);
61 + int RecoverHook();
62 + int RemoveHook();
63 + int ClearHook();
64 + int ModifyHook(const HookParam&);
65 + int SetHookName(LPCWSTR name);
66 + int GetLength(DWORD base, DWORD in); // jichi 12/25/2013: Return 0 if failed
67 + void CoolDown(); // jichi 9/28/2013: flush instruction cache on wine
68 +};
69 +
70 +extern TextHook *hookman,
71 + *current_available;
72 +
73 +//void InitDefaultHook();
74 +
75 +struct FilterRange { DWORD lower, upper; };
76 +extern FilterRange *filter;
77 +
78 +extern bool running,
79 + live;
80 +
81 +extern HANDLE hPipe,
82 + hmMutex;
83 +
84 +DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter);
85 +DWORD WINAPI CommandPipe(LPVOID lpThreadParameter);
86 +
87 +//void RequestRefreshProfile();
88 +
89 +//typedef DWORD (*InsertHookFun)(DWORD);
90 +//typedef DWORD (*IdentifyEngineFun)();
91 +//typedef DWORD (*InsertDynamicHookFun)(LPVOID addr, DWORD frame, DWORD stack);
92 +//extern IdentifyEngineFun IdentifyEngine;
93 +//extern InsertDynamicHookFun InsertDynamicHook;
94 +
95 +// jichi 9/28/2013: Protect pipeline in wine
96 +void CliLockPipe();
97 +void CliUnlockPipe();
98 +
99 +// EOF
1 +#pragma once
2 +
3 +// config.h
4 +// 8/23/2013 jichi
5 +// The first header file that are included by all source files.
6 +
7 +#define IHF // for dll import
8 +#include "ith/dllconfig.h"
9 +
10 +// EOF
This diff could not be displayed because it is too large.
1 +#pragma once
2 +
3 +// engine/engine.h
4 +// 8/23/2013 jichi
5 +// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
6 +
7 +#include "config.h"
8 +
9 +struct HookParam; // defined in ith types.h
10 +
11 +namespace Engine {
12 +
13 +// Global variables
14 +extern wchar_t process_name_[MAX_PATH], // cached
15 + process_path_[MAX_PATH]; // cached
16 +extern DWORD module_base_,
17 + module_limit_;
18 +
19 +//extern LPVOID trigger_addr;
20 +typedef bool (* trigger_fun_t)(LPVOID addr, DWORD frame, DWORD stack);
21 +extern trigger_fun_t trigger_fun_;
22 +
23 +bool InsertMonoHooks(); // Mono
24 +
25 +// Wii engines
26 +
27 +bool InsertGCHooks(); // Dolphin
28 +bool InsertVanillawareGCHook();
29 +
30 +// PS2 engines
31 +
32 +bool InsertPCSX2Hooks(); // PCSX2
33 +bool InsertMarvelousPS2Hook(); // http://marvelous.jp
34 +bool InsertMarvelous2PS2Hook(); // http://marvelous.jp
35 +bool InsertTypeMoonPS2Hook(); // http://typemoon.com
36 +//bool InsertNamcoPS2Hook();
37 +
38 +// PSP engines
39 +
40 +void SpecialPSPHook(DWORD esp_base, HookParam *hp, DWORD *data, DWORD *split, DWORD *len); // General PSP extern hook
41 +
42 +bool InsertPPSSPPHooks(); // PPSSPPWindows
43 +
44 +bool InsertPPSSPPHLEHooks();
45 +bool InsertOtomatePPSSPPHook(); // PSP otomate.jp, 0.9.9.0 only
46 +
47 +bool Insert5pbPSPHook(); // PSP 5pb.jp
48 +bool InsertAlchemistPSPHook(); // PSP Alchemist-net.co.jp, 0.9.8 only
49 +bool InsertAlchemist2PSPHook(); // PSP Alchemist-net.co.jp
50 +bool InsertBandaiNamePSPHook(); // PSP Bandai.co.jp
51 +bool InsertBandaiPSPHook(); // PSP Bandai.co.jp
52 +bool InsertBroccoliPSPHook(); // PSP Broccoli.co.jp
53 +bool InsertFelistellaPSPHook(); // PSP felistella.co.jp
54 +
55 +bool InsertCyberfrontPSPHook(); // PSP CYBERFRONT (closed)
56 +bool InsertImageepochPSPHook(); // PSP Imageepoch.co.jp
57 +bool InsertImageepoch2PSPHook();// PSP Imageepoch.co.jp
58 +bool InsertKadokawaNamePSPHook(); // PSP Kadokawa.co.jp
59 +bool InsertKonamiPSPHook(); // PSP Konami.jp
60 +bool InsertTecmoPSPHook(); // PSP Koeitecmo.co.jp
61 +//bool InsertTypeMoonPSPHook(); // PSP Typemoon.com
62 +
63 +bool InsertOtomatePSPHook(); // PSP Otomate.jp, 0.9.8 only
64 +//bool InsertOtomate2PSPHook(); // PSP otomate.jp >= 0.9.9.1
65 +
66 +bool InsertIntensePSPHook(); // PSP Intense.jp
67 +bool InsertKidPSPHook(); // PSP Kid-game.co.jp
68 +bool InsertNippon1PSPHook(); // PSP Nippon1.jp
69 +bool InsertNippon2PSPHook(); // PSP Nippon1.jp
70 +bool InsertYetiPSPHook(); // PSP Yetigame.jp
71 +bool InsertYeti2PSPHook(); // PSP Yetigame.jp
72 +
73 +// PC engines
74 +
75 +bool Insert2RMHook(); // 2RM - Adventure Engine
76 +bool Insert5pbHook(); // 5pb.jp, PSP/PS3 games ported to PC
77 +bool InsertAB2TryHook(); // Yane@AkabeiSoft2Try: YaneSDK.dll.
78 +bool InsertAbelHook(); // Abel
79 +bool InsertAdobeAirHook(); // Adobe AIR
80 +bool InsertAdobeFlash10Hook(); // Adobe Flash Player 10
81 +bool InsertAliceHook(); // System40@AliceSoft; do not work for latest alice games
82 +bool InsertAmuseCraftHook(); // AMUSE CRAFT: *.pac
83 +bool InsertAnex86Hook(); // Anex86: anex86.exe
84 +bool InsertAOSHook(); // AOS: *.aos
85 +bool InsertApricoTHook(); // Apricot: arc.a*
86 +bool InsertArtemisHook(); // Artemis Engine: *.pfs
87 +bool InsertAtelierHook(); // Atelier Kaguya: message.dat
88 +bool InsertBGIHook(); // BGI: BGI.*
89 +bool InsertC4Hook(); // C4: C4.EXE or XEX.EXE
90 +bool InsertCaramelBoxHook(); // Caramel: *.bin
91 +bool InsertCandyHook(); // SystemC@CandySoft: *.fpk
92 +bool InsertCatSystemHook(); // CatSystem2: *.int
93 +bool InsertCMVSHook(); // CMVS: data/pack/*.cpz; do not support the latest cmvs32.exe and cmvs64.exe
94 +bool InsertCotophaHook(); // Cotopha: *.noa
95 +bool InsertDebonosuHook(); // Debonosu: bmp.bak and dsetup.dll
96 +bool InsertEaglsHook(); // E.A.G.L.S: EAGLES.dll
97 +bool InsertEMEHook(); // EmonEngine: emecfg.ecf
98 +bool InsertEushullyHook(); // Eushully: AGERC.DLL
99 +bool InsertExpHook(); // EXP: http://www.exp-inc.jp
100 +bool InsertFocasLensHook(); // FocasLens: Dat/*.arc, http://www.fo-lens.net
101 +bool InsertGesen18Hook(); // Gsen18: *.szs
102 +bool InsertGXPHook(); // GXP: *.gxp
103 +bool InsertHorkEyeHook(); // HorkEye: resource string
104 +bool InsertKAGParserHook(); // plugin/KAGParser.dll
105 +bool InsertKAGParserExHook(); // plugin/KAGParserEx.dll
106 +bool InsertKiriKiriHook(); // KiriKiri: *.xp3, resource string
107 +bool InsertKiriKiriZHook(); // KiriKiri: *.xp3, resource string
108 +bool InsertLeafHook(); // Leaf: *.pak
109 +bool InsertLiveHook(); // Live: live.dll
110 +bool InsertLunaSoftHook(); // LunaSoft: Pac/*.pac
111 +bool InsertMalieHook(); // Malie@light: malie.ini
112 +bool InsertMajiroHook(); // Majiro: *.arc
113 +bool InsertMarineHeartHook(); // Marine Heart: SAISYS.exe
114 +bool InsertMBLHook(); // MBL: *.mbl
115 +bool InsertMEDHook(); // MED: *.med
116 +bool InsertMinkHook(); // Mink: *.at2
117 +//bool InsertMonoHook(); // Mono (Unity3D): */Mono/mono.dll
118 +bool InsertNeXASHook(); // NeXAS: Thumbnail.pac
119 +bool InsertNextonHook(); // NEXTON: aInfo.db
120 +bool InsertNexton1Hook();
121 +bool InsertNitroPlusHook(); // NitroPlus: *.npa
122 +bool InsertPensilHook(); // Pensil: PSetup.exe
123 +bool InsertQLIEHook(); // QLiE: GameData/*.pack
124 +//bool InsertRai7Hook(); // Rai7puk: rai7.exe
125 +bool InsertRejetHook(); // Rejet: Module/{gd.dat,pf.dat,sd.dat}
126 +bool InsertRUGPHook(); // rUGP: rUGP.exe
127 +bool InsertRetouchHook(); // Retouch: resident.dll
128 +bool InsertRREHook(); // RunrunEngine: rrecfg.rcf
129 +bool InsertShinaHook(); // ShinaRio: Rio.ini
130 +bool InsertShinyDaysHook(); // ShinyDays
131 +bool InsertElfHook(); // elf: Silky.exe
132 +bool InsertScenarioPlayerHook();// sol-fa-soft: *.iar && *.sec5
133 +bool InsertSiglusHook(); // SiglusEngine: SiglusEngine.exe
134 +bool InsertSideBHook(); // SideB: Copyright side-B
135 +bool InsertSyuntadaHook(); // Syuntada: dSoh.dat
136 +bool InsertSystem43Hook(); // System43@AliceSoft: AliceStart.ini
137 +bool InsertSystemAoiHook(); // SystemAoi: *.vfs
138 +bool InsertTanukiHook(); // Tanuki: *.tak
139 +bool InsertTaskforce2Hook(); // Taskforce2.exe
140 +bool InsertTencoHook(); // Tenco: Check.mdx
141 +bool InsertTriangleHook(); // Triangle: Execle.exe
142 +bool InsertYukaSystem2Hook(); // YukaSystem2: *.ykc
143 +bool InsertYurisHook(); // YU-RIS: *.ypf
144 +bool InsertWillPlusHook(); // WillPlus: Rio.arc
145 +bool InsertWolfHook(); // Wolf: Data.wolf
146 +
147 +void InsertBrunsHook(); // Bruns: bruns.exe
148 +void InsertIronGameSystemHook();// IroneGameSystem: igs_sample.exe
149 +void InsertLucifenHook(); // Lucifen@Navel: *.lpk
150 +void InsertRyokuchaHook(); // Ryokucha: _checksum.exe
151 +void InsertRealliveHook(); // RealLive: RealLive*.exe
152 +void InsertStuffScriptHook(); // Stuff: *.mpk
153 +void InsertTinkerBellHook(); // TinkerBell: arc00.dat
154 +void InsertWaffleHook(); // WAFFLE: cg.pak
155 +
156 +// CIRCUS: avdata/
157 +bool InsertCircusHook1();
158 +bool InsertCircusHook2();
159 +
160 +} // namespace Engine
161 +
162 +// EOF
1 +#pragma once
2 +
3 +// engine/hookdefs.h
4 +// 7/20/2014 jichi
5 +
6 +#include "config.h"
7 +
8 +// For HookParam user flags
9 +enum HookParamFlag : unsigned long {
10 + HPF_Null = 0 // never used
11 + , HPF_IgnoreSameAddress = 1 // ignore the last same text address
12 +};
13 +
14 +// EOF
1 +// eng/match.cc
2 +// 8/9/2013 jichi
3 +// Branch: ITH_Engine/engine.cpp, revision 133
4 +
5 +#ifdef _MSC_VER
6 +# pragma warning (disable:4100) // C4100: unreference formal parameter
7 +//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
8 +#endif // _MSC_VER
9 +
10 +#include "engine/match.h"
11 +#include "engine/engine.h"
12 +#include "engine/pchooks.h"
13 +#include "engine/util.h"
14 +#include "hook.h"
15 +#include "ith/sys/sys.h"
16 +#include "ith/common/except.h"
17 +#include "ith/common/growl.h"
18 +#include "ccutil/ccmacro.h"
19 +
20 +//#define ConsoleOutput(...) (void)0 // jichi 8/18/2013: I don't need ConsoleOutput
21 +
22 +enum { MAX_REL_ADDR = 0x200000 }; // jichi 8/18/2013: maximum relative address
23 +
24 +// - Global variables -
25 +
26 +namespace Engine {
27 +
28 +WCHAR process_name_[MAX_PATH], // cached
29 + process_path_[MAX_PATH]; // cached
30 +
31 +DWORD module_base_,
32 + module_limit_;
33 +
34 +//LPVOID trigger_addr;
35 +trigger_fun_t trigger_fun_;
36 +
37 +} // namespace Engine
38 +
39 +// - Methods -
40 +
41 +namespace Engine { namespace { // unnamed
42 +
43 +// jichi 7/17/2014: Disable GDI hooks for PPSSPP
44 +bool DeterminePCEngine()
45 +{
46 + if (IthFindFile(L"PPSSPP*.exe")) { // jichi 7/12/2014 PPSSPPWindows.exe, PPSSPPEX.exe PPSSPPSP.exe
47 + InsertPPSSPPHooks();
48 + return true;
49 + }
50 +
51 + if (IthFindFile(L"pcsx2*.exe")) { // jichi 7/19/2014 PCSX2.exe or PCSX2WX.exe
52 + if (!InsertPCSX2Hooks()) { // don't forget to rebuild vnrcli to inject SSE
53 + // Always insert PC hooks so that user could add PCSX2 to VNR.
54 + // TO BE REMOVED after more PS2 engines are added.
55 + PcHooks::hookGDIFunctions();
56 + PcHooks::hookLstrFunctions();
57 + }
58 +
59 + return true;
60 + }
61 +
62 + if (IthFindFile(L"Dolphin.exe")) { // jichi 7/20/2014
63 + if (!InsertGCHooks()) {
64 + // Always insert PC hooks so that user could add PCSX2 to VNR.
65 + // TO BE REMOVED after more PS2 engines are added.
66 + PcHooks::hookGDIFunctions();
67 + PcHooks::hookLstrFunctions();
68 + }
69 +
70 + return true;
71 + }
72 +
73 + //if (IthFindFile(L"*\\Mono\\mono.dll")) { // jichi 4/21/2014: Mono
74 + //if (IthCheckFile(L"bsz2_Data\\Mono\\mono.dll")) { // jichi 4/21/2014: Mono
75 + // InsertMonoHook();
76 + // return true;
77 + //}
78 + if (::GetModuleHandleA("mono.dll")) {
79 + InsertMonoHooks();
80 +
81 + // 3/20/2015 jichi
82 + // Always insert GDI hooks even for Mono games
83 + // For example: 新世黙示録 need GetGlyphOutlineA
84 + PcHooks::hookGDIFunctions();
85 + return true;
86 + }
87 +
88 + // PC games
89 + PcHooks::hookGDIFunctions();
90 + return false;
91 +}
92 +
93 +bool DetermineEngineByFile1()
94 +{
95 + if (IthFindFile(L"*.xp3") || Util::SearchResourceString(L"TVP(KIRIKIRI)")) {
96 + if (Util::SearchResourceString(L"TVP(KIRIKIRI) Z ")) { // TVP(KIRIKIRI) Z CORE
97 + // jichi 11/24/2014: Disabled that might crash VBH
98 + //if (IthCheckFile(L"plugin\\KAGParser.dll"))
99 + // InsertKAGParserHook();
100 + //else if (IthCheckFile(L"plugin\\KAGParserEx.dll"))
101 + // InsertKAGParserExHook();
102 + if (InsertKiriKiriZHook())
103 + return true;
104 + }
105 + InsertKiriKiriHook();
106 + return true;
107 + }
108 + // 8/2/2014 jichi: Game name shown as 2RM - Adventure Engine
109 + if (Util::SearchResourceString(L"2RM") && Util::SearchResourceString(L"Adventure Engine")) {
110 + Insert2RMHook();
111 + return true;
112 + }
113 + // 8/2/2014 jichi: Copyright is side-B, a conf.dat will be generated after the game is launched
114 + // It also contains lua5.1.dll and lua5.dll
115 + if (Util::SearchResourceString(L"side-B")) {
116 + InsertSideBHook();
117 + return true;
118 + }
119 + if (IthFindFile(L"bgi.*")) {
120 + InsertBGIHook();
121 + return true;
122 + }
123 + if (IthCheckFile(L"AGERC.DLL")) { // 6/1/2014 jichi: Eushully, AGE.EXE
124 + InsertEushullyHook();
125 + return true;
126 + }
127 + if (IthFindFile(L"data*.arc") && IthFindFile(L"stream*.arc")) {
128 + InsertMajiroHook();
129 + return true;
130 + }
131 + // jichi 5/31/2014
132 + if (//IthCheckFile(L"Silkys.exe") || // It might or might not have Silkys.exe
133 + // data, effect, layer, mes, music
134 + IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"mes.arc")) {
135 + InsertElfHook();
136 + return true;
137 + }
138 + if (IthFindFile(L"data\\pack\\*.cpz")) {
139 + InsertCMVSHook();
140 + return true;
141 + }
142 + // jichi 10/12/2013: Restore wolf engine
143 + // jichi 10/18/2013: Check for data/*.wolf
144 + if (IthFindFile(L"data.wolf") || IthFindFile(L"data\\*.wolf")) {
145 + InsertWolfHook();
146 + return true;
147 + }
148 + if (IthCheckFile(L"advdata\\dat\\names.dat")) {
149 + InsertCircusHook1();
150 + return true;
151 + }
152 + if (IthCheckFile(L"advdata\\grp\\names.dat")) {
153 + InsertCircusHook2();
154 + return true;
155 + }
156 + if (IthFindFile(L"*.noa")) {
157 + InsertCotophaHook();
158 + return true;
159 + }
160 + if (IthFindFile(L"*.pfs")) { // jichi 10/1/2013
161 + InsertArtemisHook();
162 + return true;
163 + }
164 + if (IthFindFile(L"*.int")) {
165 + InsertCatSystemHook();
166 + return true;
167 + }
168 + if (IthCheckFile(L"message.dat")) {
169 + InsertAtelierHook();
170 + return true;
171 + }
172 + if (IthCheckFile(L"Check.mdx")) { // jichi 4/1/2014: AUGame
173 + InsertTencoHook();
174 + return true;
175 + }
176 + // jichi 12/25/2013: It may or may not be QLIE.
177 + // AlterEgo also has GameData/sound.pack but is not QLIE
178 + if (IthFindFile(L"GameData\\*.pack") && InsertQLIEHook())
179 + return true;
180 +
181 + if (IthFindFile(L"*.pac")) {
182 + // jichi 6/3/2014: AMUSE CRAFT and SOFTPAL
183 + // Selectively insert, so that lstrlenA can still get correct text if failed
184 + if (IthCheckFile(L"dll\\resource.dll") && IthCheckFile(L"dll\\pal.dll") && InsertAmuseCraftHook())
185 + return true;
186 +
187 + if (IthCheckFile(L"Thumbnail.pac")) {
188 + //ConsoleOutput("vnreng: IGNORE NeXAS");
189 + InsertNeXASHook(); // jichi 7/6/2014: GIGA
190 + return true;
191 + }
192 +
193 + if (Util::SearchResourceString(L"SOFTPAL")) {
194 + ConsoleOutput("vnreng: IGNORE SoftPal UNiSONSHIFT");
195 + return true;
196 + }
197 + }
198 + // jichi 12/27/2014: LunaSoft
199 + if (IthFindFile(L"Pac\\*.pac")) {
200 + InsertLunaSoftHook();
201 + return true;
202 + }
203 + // jichi 9/16/2013: Add Gesen18
204 + if (IthFindFile(L"*.szs") || IthFindFile(L"Data\\*.szs")) {
205 + InsertGesen18Hook();
206 + return true;
207 + }
208 + // jichi 12/22/2013: Add rejet
209 + if (IthCheckFile(L"gd.dat") && IthCheckFile(L"pf.dat") && IthCheckFile(L"sd.dat")) {
210 + InsertRejetHook();
211 + return true;
212 + }
213 + // Only examined with version 1.0
214 + //if (IthFindFile(L"Adobe AIR\\Versions\\*\\Adobe AIR.dll")) { // jichi 4/15/2014: FIXME: Wildcard not working
215 + if (IthCheckFile(L"Adobe AIR\\Versions\\1.0\\Adobe AIR.dll")) { // jichi 4/15/2014: Adobe AIR
216 + InsertAdobeAirHook();
217 + return true;
218 + }
219 + return false;
220 +}
221 +
222 +bool DetermineEngineByFile2()
223 +{
224 + if (IthCheckFile(L"resident.dll")) {
225 + InsertRetouchHook();
226 + return true;
227 + }
228 + if (IthCheckFile(L"Malie.ini") || IthCheckFile(L"Malie.exe")) { // jichi: 9/9/2014: Add malie.exe in case malie.ini is missing
229 + InsertMalieHook();
230 + return true;
231 + }
232 + if (IthCheckFile(L"live.dll")) {
233 + InsertLiveHook();
234 + return true;
235 + }
236 + // 9/5/2013 jichi
237 + if (IthCheckFile(L"aInfo.db")) {
238 + InsertNextonHook();
239 + return true;
240 + }
241 + if (IthFindFile(L"*.lpk")) {
242 + InsertLucifenHook();
243 + return true;
244 + }
245 + if (IthCheckFile(L"cfg.pak")) {
246 + InsertWaffleHook();
247 + return true;
248 + }
249 + if (IthCheckFile(L"Arc00.dat")) {
250 + InsertTinkerBellHook();
251 + return true;
252 + }
253 + if (IthFindFile(L"*.vfs")) { // jichi 7/6/2014: Better to test AoiLib.dll? ja.wikipedia.org/wiki/ソフトハウスキャラ
254 + InsertSystemAoiHook();
255 + return true;
256 + }
257 + if (IthFindFile(L"*.mbl")) {
258 + InsertMBLHook();
259 + return true;
260 + }
261 + // jichi 8/1/2014: YU-RIS engine, lots of clockup game also has this pattern
262 + if (IthFindFile(L"pac\\*.ypf") || IthFindFile(L"*.ypf")) {
263 + // jichi 8/14/2013: CLOCLUP: "ノーブレスオブリージュ" would crash the game.
264 + if (!IthCheckFile(L"noblesse.exe"))
265 + InsertYurisHook();
266 + return true;
267 + }
268 + if (IthFindFile(L"*.npa")) {
269 + InsertNitroPlusHook();
270 + return true;
271 + }
272 + return false;
273 +}
274 +
275 +bool DetermineEngineByFile3()
276 +{
277 + //if (IthCheckFile(L"libscr.dll")) { // already checked
278 + // InsertBrunsHook();
279 + // return true;
280 + //}
281 +
282 + // jichi 10/12/2013: Sample args.txt:
283 + // See: http://tieba.baidu.com/p/2631413816
284 + // -workdir
285 + // .
286 + // -loadpath
287 + // .
288 + // am.cfg
289 + if (IthCheckFile(L"args.txt")) {
290 + InsertBrunsHook();
291 + return true;
292 + }
293 + if (IthCheckFile(L"emecfg.ecf")) {
294 + InsertEMEHook();
295 + return true;
296 + }
297 + if (IthCheckFile(L"rrecfg.rcf")) {
298 + InsertRREHook();
299 + return true;
300 + }
301 + if (IthFindFile(L"*.fpk") || IthFindFile(L"data\\*.fpk")) {
302 + InsertCandyHook();
303 + return true;
304 + }
305 + if (IthFindFile(L"arc.a*")) {
306 + InsertApricoTHook();
307 + return true;
308 + }
309 + if (IthFindFile(L"*.mpk")) {
310 + InsertStuffScriptHook();
311 + return true;
312 + }
313 + if (IthCheckFile(L"Execle.exe")) {
314 + InsertTriangleHook();
315 + return true;
316 + }
317 + // jichi 2/28/2015: No longer work for "大正×対称アリス episode I" from Primula
318 + //if (IthCheckFile(L"PSetup.exe")) {
319 + // InsertPensilHook();
320 + // return true;
321 + //}
322 + if (IthCheckFile(L"Yanesdk.dll")) {
323 + InsertAB2TryHook();
324 + return true;
325 + }
326 + if (IthFindFile(L"*.med")) {
327 + InsertMEDHook();
328 + return true;
329 + }
330 + return false;
331 +}
332 +
333 +bool DetermineEngineByFile4()
334 +{
335 + if (IthCheckFile(L"EAGLS.dll")) { // jichi 3/24/2014: E.A.G.L.S
336 + //ConsoleOutput("vnreng: IGNORE EAGLS");
337 + InsertEaglsHook();
338 + return true;
339 + }
340 + if (IthCheckFile(L"bmp.pak") && IthCheckFile(L"dsetup.dll")) {
341 + InsertDebonosuHook();
342 + return true;
343 + }
344 + if (IthCheckFile(L"C4.EXE") || IthCheckFile(L"XEX.EXE")) {
345 + InsertC4Hook();
346 + return true;
347 + }
348 + if (IthCheckFile(L"Rio.arc") && IthFindFile(L"Chip*.arc")) {
349 + InsertWillPlusHook();
350 + return true;
351 + }
352 + if (IthFindFile(L"*.tac")) {
353 + InsertTanukiHook();
354 + return true;
355 + }
356 + if (IthFindFile(L"*.gxp")) {
357 + InsertGXPHook();
358 + return true;
359 + }
360 + if (IthFindFile(L"*.aos")) { // jichi 4/2/2014: AOS hook
361 + InsertAOSHook();
362 + return true;
363 + }
364 + if (IthFindFile(L"*.at2")) { // jichi 12/23/2014: Mink, sample files: voice.at2, voice.det, voice.nme
365 + InsertMinkHook();
366 + return true;
367 + }
368 + if (IthFindFile(L"*.ykc")) { // jichi 7/15/2014: YukaSystem1 is not supported, though
369 + //ConsoleOutput("vnreng: IGNORE YKC:Feng/HookSoft(SMEE)");
370 + InsertYukaSystem2Hook();
371 + return true;
372 + }
373 + if (IthFindFile(L"model\\*.hed")) { // jichi 9/8/2014: EXP
374 + InsertExpHook();
375 + return true;
376 + }
377 + // jichi 2/6/2015 平安亭
378 + // dPi.dat, dPih.dat, dSc.dat, dSch.dat, dSo.dat, dSoh.dat, dSy.dat
379 + //if (IthCheckFile(L"dSoh.dat")) { // no idea why this file does not work
380 + if (IthCheckFile(L"dSch.dat")) {
381 + InsertSyuntadaHook();
382 + return true;
383 + }
384 +
385 + // jichi 2/28/2015: Delay checking Pensil in case something went wrong
386 + // File pattern observed in [Primula] 大正×対称アリス episode I
387 + // - PSetup.exe no longer exists
388 + // - MovieTexture.dll information shows MovieTex dynamic library, copyright Pensil 2013
389 + // - ta_trial.exe information shows 2XT - Primula Adventure Engine
390 + if (IthFindFile(L"PSetup.exe") || IthFindFile(L"MovieTexture.dll") || Util::SearchResourceString(L"2XT -")) {
391 + InsertPensilHook();
392 + return true;
393 + }
394 + return false;
395 +}
396 +
397 +bool DetermineEngineByProcessName()
398 +{
399 + WCHAR str[MAX_PATH];
400 + wcscpy(str, process_name_);
401 + _wcslwr(str); // lower case
402 +
403 + if (wcsstr(str,L"reallive") || IthCheckFile(L"Reallive.exe")) {
404 + InsertRealliveHook();
405 + return true;
406 + }
407 +
408 + // jichi 8/19/2013: DO NOT WORK for games like「ハピメア」
409 + //if (wcsstr(str,L"cmvs32") || wcsstr(str,L"cmvs64")) {
410 + // InsertCMVSHook();
411 + // return true;
412 + //}
413 +
414 + // jichi 8/17/2013: Handle "~"
415 + if (wcsstr(str, L"siglusengine") || !wcsncmp(str, L"siglus~", 7) || IthCheckFile(L"SiglusEngine.exe")) {
416 + InsertSiglusHook();
417 + return true;
418 + }
419 +
420 + if (wcsstr(str, L"taskforce2") || !wcsncmp(str, L"taskfo~", 7) || IthCheckFile(L"Faskforce2.exe")) {
421 + InsertTaskforce2Hook();
422 + return true;
423 + }
424 +
425 + if (wcsstr(str,L"rugp") || IthCheckFile(L"rugp.exe")) {
426 + InsertRUGPHook();
427 + return true;
428 + }
429 +
430 + // jichi 8/17/2013: Handle "~"
431 + if (wcsstr(str, L"igs_sample") || !wcsncmp(str, L"igs_sa~", 7) || IthCheckFile(L"igs_sample.exe")) {
432 + InsertIronGameSystemHook();
433 + return true;
434 + }
435 +
436 + if (wcsstr(str, L"bruns") || IthCheckFile(L"bruns.exe")) {
437 + InsertBrunsHook();
438 + return true;
439 + }
440 +
441 + if (wcsstr(str, L"anex86") || IthCheckFile(L"anex86.exe")) {
442 + InsertAnex86Hook();
443 + return true;
444 + }
445 +
446 + // jichi 8/17/2013: Handle "~"
447 + if (wcsstr(str, L"shinydays") || !wcsncmp(str, L"shinyd~", 7) || IthCheckFile(L"ShinyDays.exe")) {
448 + InsertShinyDaysHook();
449 + return true;
450 + }
451 +
452 + // jichi 10/3/2013: FIXME: Does not work
453 + // Raise C0000005 even with admin priv
454 + //if (wcsstr(str, L"bsz")) { // BALDRSKY ZERO
455 + // InsertBaldrHook();
456 + // return true;
457 + //}
458 +
459 + if (wcsstr(process_name_, L"SAISYS") || IthCheckFile(L"SaiSys.exe")) { // jichi 4/19/2014: Marine Heart
460 + InsertMarineHeartHook();
461 + return true;
462 + }
463 +
464 + DWORD len = wcslen(str);
465 +
466 + // jichi 8/24/2013: Checking for Rio.ini or $procname.ini
467 + //wcscpy(str+len-4, L"_?.war");
468 + //if (IthFindFile(str)) {
469 + // InsertShinaHook();
470 + // return true;
471 + //}
472 + if (InsertShinaHook())
473 + return true;
474 +
475 + // jichi 8/10/2013: Since *.bin is common, move CaramelBox to the end
476 + str[len - 3] = L'b';
477 + str[len - 2] = L'i';
478 + str[len - 1] = L'n';
479 + str[len] = 0;
480 + if (IthCheckFile(str) || IthCheckFile(L"trial.bin")) { // jichi 7/8/2014: add trial.bin
481 + InsertCaramelBoxHook();
482 + return true;
483 + }
484 +
485 + // This must appear at last since str is modified
486 + wcscpy(str + len - 4, L"_checksum.exe");
487 + if (IthCheckFile(str)) {
488 + InsertRyokuchaHook();
489 +
490 + if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games
491 + InsertScenarioPlayerHook();
492 + return true;
493 + }
494 +
495 + return false;
496 +}
497 +
498 +bool DetermineEngineOther()
499 +{
500 + if (InsertAliceHook())
501 + return true;
502 + // jichi 1/19/2015: Disable inserting Lstr for System40
503 + // See: http://sakuradite.com/topic/618
504 + if (IthCheckFile(L"System40.ini")) {
505 + ConsoleOutput("vnreng: IGNORE old System40.ini");
506 + return true;
507 + }
508 + // jichi 12/26/2013: Add this after alicehook
509 + if (IthCheckFile(L"AliceStart.ini")) {
510 + InsertSystem43Hook();
511 + return true;
512 + }
513 +
514 + // jichi 8/24/2013: Move into functions
515 + static BYTE static_file_info[0x1000];
516 + if (IthGetFileInfo(L"*01", static_file_info))
517 + if (*(DWORD*)static_file_info == 0) {
518 + static WCHAR static_search_name[MAX_PATH];
519 + LPWSTR name=(LPWSTR)(static_file_info+0x5E);
520 + int len = wcslen(name);
521 + name[len - 2] = L'*';
522 + name[len - 1] = 0;
523 + wcscpy(static_search_name, name);
524 + IthGetFileInfo(static_search_name, static_file_info);
525 + union {
526 + FILE_BOTH_DIR_INFORMATION *both_info;
527 + DWORD addr;
528 + };
529 + both_info = (FILE_BOTH_DIR_INFORMATION *)static_file_info;
530 + //BYTE* ptr=static_file_info;
531 + len = 0;
532 + while (both_info->NextEntryOffset) {
533 + addr += both_info->NextEntryOffset;
534 + len++;
535 + }
536 + if (len > 3) {
537 + InsertAbelHook();
538 + return true;
539 + }
540 + }
541 + return false;
542 +}
543 +
544 +// jichi 8/17/2014
545 +// Put the patterns that might break other games at last
546 +bool DetermineEngineAtLast()
547 +{
548 + if (IthFindFile(L"data\\*.cpk")) { // jichi 12/2/2014
549 + Insert5pbHook();
550 + return true;
551 + }
552 + // jichi 7/6/2014: named as ScenarioPlayer since resource string could be: scenario player program for xxx
553 + // Do this at last as it is common
554 + if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) { // jichi 4/18/2014: Other game engine could also have *.iar such as Ryokucha
555 + InsertScenarioPlayerHook();
556 + return true;
557 + }
558 + //if (IthCheckFile(L"arc0.dat") && IthCheckFile(L"script.dat") // jichi 11/14/2014: too common
559 + if (Util::SearchResourceString(L"HorkEye")) { // appear in copyright: Copyright (C) HorkEye, http://horkeye.com
560 + InsertHorkEyeHook();
561 + return true;
562 + }
563 + if (IthCheckFile(L"comnArc.arc") // jichi 8/17/2014: this file might exist in multiple files
564 + && InsertNexton1Hook()) // old nexton game
565 + return true;
566 + if (IthCheckFile(L"arc.dat") // jichi 9/27/2014: too common
567 + && InsertApricoTHook())
568 + return true;
569 + if (IthFindFile(L"*.pak") // jichi 12/25/2014: too common
570 + && InsertLeafHook())
571 + return true;
572 + // jichi 10/31/2014
573 + // File description: Adobe Flash Player 10.2r153
574 + // Product name: Shockwave Flash
575 + // Original filename: SAFlashPlayer.exe
576 + // Legal trademarks: Adobe Flash Player
577 + // No idea why, this must appear at last or it will crash
578 + if (Util::SearchResourceString(L"Adobe Flash Player 10")) {
579 + InsertAdobeFlash10Hook(); // only v10 might be supported. Otherwise, fallback to Lstr hooks
580 + return true;
581 + }
582 + if (IthFindFile(L"dat\\*.arc")) { // jichi 2/6/2015
583 + InsertFocasLensHook(); // Touhou
584 + return true;
585 + }
586 + return false;
587 +}
588 +
589 +// jichi 6/1/2014
590 +bool DetermineEngineGeneric()
591 +{
592 + bool ret = false;
593 +
594 + if (IthCheckFile(L"AlterEgo.exe")) {
595 + ConsoleOutput("vnreng: AlterEgo, INSERT WideChar hooks");
596 + ret = true;
597 + } else if (IthFindFile(L"data\\Sky\\*")) {
598 + ConsoleOutput("vnreng: TEATIME, INSERT WideChar hooks");
599 + ret = true;
600 + }
601 + //} else if (IthFindFile(L"image\\*.po2") || IthFindFile(L"image\\*.jo2")) {
602 + // ConsoleOutput("vnreng: HarukaKanata, INSERT WideChar hooks"); // はるかかなた
603 + // ret = true;
604 + //}
605 + if (ret)
606 + PcHooks::hookWcharFunctions();
607 + return ret;
608 +}
609 +
610 +bool DetermineNoEngine()
611 +{
612 + //if (IthFindFile(L"*\\Managed\\UnityEngine.dll")) { // jichi 12/3/2013: Unity (BALDRSKY ZERO)
613 + // ConsoleOutput("vnreng: IGNORE Unity");
614 + // return true;
615 + //}
616 + //if (IthCheckFile(L"bsz_Data\\Managed\\UnityEngine.dll") || IthCheckFile(L"bsz2_Data\\Managed\\UnityEngine.dll")) {
617 + // ConsoleOutput("vnreng: IGNORE Unity");
618 + // return true;
619 + //}
620 +
621 + // jichi 2/14/2015: Guilty+ RIN×SEN (PK)
622 + if (IthCheckFile(L"rio.ini") || IthFindFile(L"*.war")) {
623 + ConsoleOutput("vnreng: IGNORE unknown ShinaRio");
624 + return true;
625 + }
626 +
627 + if (IthCheckFile(L"AdvHD.exe") || IthCheckFile(L"AdvHD.dll")) {
628 + ConsoleOutput("vnreng: IGNORE Adv Player HD");
629 + return true;
630 + }
631 +
632 + if (IthCheckFile(L"ScrPlayer.exe")) {
633 + ConsoleOutput("vnreng: IGNORE ScrPlayer");
634 + return true;
635 + }
636 +
637 + if (IthCheckFile(L"nnnConfig2.exe")) {
638 + ConsoleOutput("vnreng: IGNORE Nya NNNConfig");
639 + return true;
640 + }
641 +
642 + //if (IthCheckFile(L"AGERC.DLL")) { // jichi 3/17/2014: Eushully, AGE.EXE
643 + // ConsoleOutput("vnreng: IGNORE Eushully");
644 + // return true;
645 + //}
646 +
647 + if (IthCheckFile(L"game_sys.exe")) {
648 + ConsoleOutput("vnreng: IGNORE Atelier Kaguya BY/TH");
649 + return true;
650 + }
651 +
652 + if (IthFindFile(L"*.bsa")) {
653 + ConsoleOutput("vnreng: IGNORE Bishop");
654 + return true;
655 + }
656 +
657 + // jichi 3/19/2014: Escude game
658 + // Example: bgm.bin gfx.bin maou.bin script.bin snd.bin voc.bin
659 + if (IthCheckFile(L"gfx.bin") && IthCheckFile(L"snd.bin") && IthCheckFile(L"voc.bin")) {
660 + ConsoleOutput("vnreng: IGNORE Escude");
661 + return true;
662 + }
663 +
664 + // jichi 2/18/2015: Ignore if there is Nitro+ copyright
665 + if (Util::SearchResourceString(L"Nitro+")) {
666 + ConsoleOutput("vnreng: IGNORE unknown Nitro+");
667 + return true;
668 + }
669 +
670 + // jichi 12/28/2014: "Chartreux Inc." in Copyright.
671 + // Sublimary brands include Rosebleu, MORE, etc.
672 + // GetGlyphOutlineA already works.
673 + if (Util::SearchResourceString(L"Chartreux")) {
674 + ConsoleOutput("vnreng: IGNORE Chartreux");
675 + return true;
676 + }
677 +
678 + if (wcsstr(process_name_, L"lcsebody") || !wcsncmp(process_name_, L"lcsebo~", 7) || IthCheckFile(L"lcsebody")) { // jichi 3/19/2014: LC-ScriptEngine, GetGlyphOutlineA
679 + ConsoleOutput("vnreng: IGNORE lcsebody");
680 + return true;
681 + }
682 +
683 + wchar_t str[MAX_PATH];
684 + DWORD i;
685 + for (i = 0; process_name_[i]; i++) {
686 + str[i] = process_name_[i];
687 + if (process_name_[i] == L'.')
688 + break;
689 + }
690 + *(DWORD *)(str + i + 1) = 0x630068; //.hcb
691 + *(DWORD *)(str + i + 3) = 0x62;
692 + if (IthCheckFile(str)) {
693 + ConsoleOutput("vnreng: IGNORE FVP"); // jichi 10/3/2013: such like アトリエかぐや
694 + return true;
695 + }
696 + return false;
697 +}
698 +
699 +// 12/13/2013: Declare it in a way compatible to EXCEPTION_PROCEDURE
700 +EXCEPTION_DISPOSITION ExceptHandler(PEXCEPTION_RECORD ExceptionRecord, LPVOID, PCONTEXT, LPVOID)
701 +{
702 + if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) {
703 + module_limit_ = ExceptionRecord->ExceptionInformation[1];
704 + //OutputDWORD(module_limit_);
705 + __asm
706 + {
707 + mov eax,fs:[0x30] // jichi 12/13/2013: get PEB
708 + mov eax,[eax+0xc]
709 + mov eax,[eax+0xc]
710 + mov ecx,module_limit_
711 + sub ecx,module_base_
712 + mov [eax+0x20],ecx
713 + }
714 + }
715 + //ContextRecord->Esp = recv_esp;
716 + //ContextRecord->Eip = recv_eip;
717 + //return ExceptionContinueExecution; // jichi 3/11/2014: this will still crash. Not sure why ITH use this. Change to ExceptionContinueSearch
718 + return ExceptionContinueSearch; // an unwind is in progress,
719 +}
720 +
721 +// jichi 9/14/2013: Certain ITH functions like FindEntryAligned might raise exception without admin priv
722 +// Return if succeeded.
723 +bool UnsafeDetermineEngineType()
724 +{
725 + return DeterminePCEngine()
726 + || DetermineEngineByFile1()
727 + || DetermineEngineByFile2()
728 + || DetermineEngineByFile3()
729 + || DetermineEngineByFile4()
730 + || DetermineEngineByProcessName()
731 + || DetermineEngineOther()
732 + || DetermineEngineAtLast()
733 + || DetermineEngineGeneric()
734 + || DetermineNoEngine()
735 + ;
736 +}
737 +
738 +// jichi 10/21/2014: Return whether found the game engine
739 +bool DetermineEngineType()
740 +{
741 + // jichi 9/27/2013: disable game engine for debugging use
742 +#ifdef ITH_DISABLE_ENGINE
743 + PcHooks::hookLstrFunctions();
744 + return false;
745 +#else
746 + bool found = false;
747 +#ifdef ITH_HAS_SEH
748 + __try { found = UnsafeDetermineEngineType(); }
749 + __except(ExceptHandler((GetExceptionInformation())->ExceptionRecord, 0, 0, 0)) {}
750 +#else // use my own SEH
751 + seh_with_eh(ExceptHandler,
752 + found = UnsafeDetermineEngineType());
753 +#endif // ITH_HAS_SEH
754 + if (!found) // jichi 10/2/2013: Only enable it if no game engine is detected
755 + PcHooks::hookLstrFunctions();
756 + else
757 + ConsoleOutput("vnreng: found game engine, IGNORE non gui hooks");
758 + return found;
759 +#endif // ITH_DISABLE_ENGINE
760 +}
761 +
762 +// __asm
763 +// {
764 +// mov eax,seh_recover
765 +// mov recv_eip,eax
766 +// push ExceptHandler
767 +// push fs:[0]
768 +// mov fs:[0],esp
769 +// pushad
770 +// mov recv_esp,esp
771 +// }
772 +// DetermineEngineType();
773 +// status++;
774 +// __asm
775 +// {
776 +//seh_recover:
777 +// popad
778 +// mov eax,[esp]
779 +// mov fs:[0],eax
780 +// add esp,8
781 +// }
782 +// if (status == 0)
783 +// ConsoleOutput("Fail to identify engine type.");
784 +// else
785 +// ConsoleOutput("Initialized successfully.");
786 +//}
787 +
788 +}} // namespace Engine unnamed
789 +
790 +// - API -
791 +
792 +bool Engine::IdentifyEngine()
793 +{
794 + // jichi 12/18/2013: Though FillRange could raise, it should never raise for he current process
795 + // So, SEH is not used here.
796 + FillRange(process_name_, &module_base_, &module_limit_);
797 + return DetermineEngineType();
798 +}
799 +
800 +DWORD Engine::InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
801 +{ return trigger_fun_ ? !trigger_fun_(addr, frame, stack) : 0; }
802 +
803 +void Engine::match(LPVOID lpThreadParameter)
804 +{
805 + CC_UNUSED(lpThreadParameter);
806 + Util::GetProcessName(process_name_); // Initialize process name
807 + Util::GetProcessPath(process_path_); // Initialize process path
808 + ::engine_registered = true;
809 + //::RegisterEngineModule((DWORD)IdentifyEngine, (DWORD)InsertDynamicHook);
810 +}
811 +
812 +// EOF
813 +
814 +/*
815 +extern "C" {
816 + // http://gmogre3d.googlecode.com/svn-history/r815/trunk/OgreMain/src/WIN32/OgreMinGWSupport.cpp
817 + // http://forum.osdev.org/viewtopic.php?f=8&t=22352
818 + //#pragma data_seg()
819 + //#pragma comment(linker, "/merge:.CRT=.data") // works fine in visual c++ 6
820 + //#pragma data_seg()
821 + //#pragma comment(linker, "/merge:.CRT=.rdata")
822 + // MSVC libs use _chkstk for stack-probing. MinGW equivalent is _alloca.
823 + //void _alloca();
824 + //void _chkstk() { _alloca(); }
825 +
826 + // MSVC uses security cookies to prevent some buffer overflow attacks.
827 + // provide dummy implementations.
828 + //void _fastcall __security_check_cookie(intptr_t i) {}
829 + void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie) {}
830 +}
831 +*/
1 +#pragma once
2 +
3 +// engine/match.h
4 +// 8/23/2013 jichi
5 +// TODO: Clean up the interface to match game engines.
6 +// Split the engine match logic out of hooks.
7 +// Modify the game hook to allow replace functions for arbitary purpose
8 +// instead of just extracting text.
9 +
10 +#include "config.h"
11 +
12 +namespace Engine {
13 +
14 +void match(LPVOID lpThreadParameter);
15 +
16 +// jichi 10/21/2014: Return whether found the engine
17 +bool IdentifyEngine();
18 +
19 +// jichi 10/21/2014: Return 0 if failed
20 +DWORD InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack);
21 +
22 +} // namespace Engine
23 +
24 +// EOF
1 +// pchooks.cc
2 +// 8/1/2014 jichi
3 +
4 +#include "engine/pchooks.h"
5 +#include "hook.h"
6 +
7 +#define DEBUG "vnrcli"
8 +#define DPRINT(cstr) ConsoleOutput(DEBUG ":" __FUNCTION__ ":" cstr) // defined in vnrcli
9 +
10 +// 8/1/2014 jichi: Split is not used.
11 +// Although split is specified, USING_SPLIT is not assigned.
12 +
13 +// Use LPASTE to convert to wchar_t
14 +// http://bytes.com/topic/c/answers/135834-defining-wide-character-strings-macros
15 +#define LPASTE(s) L##s
16 +#define L(s) LPASTE(s)
17 +#define NEW_HOOK(_fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
18 + { \
19 + HookParam hp = {}; \
20 + hp.addr = (DWORD)_fun; \
21 + hp.off = _data; \
22 + hp.ind = _data_ind; \
23 + hp.split = _split_off; \
24 + hp.split_ind = _split_ind; \
25 + hp.type = _type; \
26 + hp.length_offset = _len_off; \
27 + NewHook(hp, L(#_fun)); \
28 + }
29 +
30 +// jichi 7/17/2014: Renamed from InitDefaultHook
31 +void PcHooks::hookGDIFunctions()
32 +{
33 + DPRINT("enter");
34 + // int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
35 + //
36 + // jichi 9/8/2013: Guessed meaning
37 + // - data(off): 4 * the n-th (base 1) parameter representing the data of the string
38 + // - len_off:
39 + // - the n-th (base 1) parameter representing the length of the string
40 + // - or 1 if is char
41 + // - or 0 if detect on run time
42 + // - type: USING_STRING if len_off != 1 else BIG_ENDIAN or USING_UNICODE
43 + //
44 + // Examples:
45 + // int WINAPI lstrlenA(LPCSTR lpString)
46 + // - data: 4 * 1 = 4, as lpString is the first
47 + // - len_off: 0, as no parameter representing string length
48 + // - type: BIG_ENDIAN, since len_off == 1
49 + // BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
50 + // - data: 4 * 2 = 0x8, as lpString is the second
51 + // - len_off: 3, as nCount is the 3rd parameter
52 + // - type: USING_STRING, since len_off != 1
53 + //
54 + // Note: All functions does not have NO_CONTEXT attribute and will be filtered.
55 +
56 + enum stack {
57 + s_retaddr = 0
58 + , s_arg1 = 4 * 1 // 0x4
59 + , s_arg2 = 4 * 2 // 0x8
60 + , s_arg3 = 4 * 3 // 0xc
61 + , s_arg4 = 4 * 4 // 0x10
62 + , s_arg5 = 4 * 5 // 0x14
63 + , s_arg6 = 4 * 6 // 0x18
64 + };
65 +
66 +//#define _(Name, ...) \
67 +// hookman[HF_##Name].InitHook(Name, __VA_ARGS__); \
68 +// hookman[HF_##Name].SetHookName(names[HF_##Name]);
69 +
70 + // Always use s_arg1 = hDC as split_off
71 + // 7/26/2014 jichi: Why there is no USING_SPLIT type?
72 +
73 + // gdi32.dll
74 + NEW_HOOK(GetTextExtentPoint32A, s_arg2, 0,s_arg1,0, USING_STRING, 3) // BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
75 + 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);
76 + 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);
77 + NEW_HOOK(TextOutA, s_arg4, 0,s_arg1,0, USING_STRING, 5) // BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cchString);
78 + NEW_HOOK(GetCharABCWidthsA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharABCWidths(HDC hdc, UINT uFirstChar, UINT uLastChar, LPABC lpabc);
79 + NEW_HOOK(GetTextExtentPoint32W, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
80 + NEW_HOOK(GetGlyphOutlineW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
81 + NEW_HOOK(ExtTextOutW, s_arg6, 0,s_arg1,0, USING_UNICODE|USING_STRING, 7)
82 + NEW_HOOK(TextOutW, s_arg4, 0,s_arg1,0, USING_UNICODE|USING_STRING, 5)
83 + NEW_HOOK(GetCharABCWidthsW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
84 +
85 + // user32.dll
86 + NEW_HOOK(DrawTextA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawText(HDC hDC, LPCTSTR lpchText, int nCount, LPRECT lpRect, UINT uFormat);
87 + 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);
88 + NEW_HOOK(DrawTextW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
89 + NEW_HOOK(DrawTextExW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
90 +//#undef _
91 + DPRINT("leave");
92 +}
93 +
94 +// jichi 10/2/2013
95 +// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
96 +void PcHooks::hookLstrFunctions()
97 +{
98 + DPRINT("enter");
99 + // int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
100 +
101 + enum stack {
102 + s_retaddr = 0
103 + , s_arg1 = 4 * 1 // 0x4
104 + //, s_arg2 = 4 * 2 // 0x8
105 + //, s_arg3 = 4 * 3 // 0xc
106 + //, s_arg4 = 4 * 4 // 0x10
107 + //, s_arg5 = 4 * 5 // 0x14
108 + //, s_arg6 = 4 * 6 // 0x18
109 + };
110 +
111 + // http://msdn.microsoft.com/en-us/library/78zh94ax.aspx
112 + // int WINAPI lstrlen(LPCTSTR lpString);
113 + // Lstr functions usually extracts rubbish, and might crash certain games like 「Magical Marriage Lunatics!!」
114 + // Needed by Gift
115 + // Use arg1 address for both split and data
116 + NEW_HOOK(lstrlenA, s_arg1, 0,s_arg1,0, USING_STRING, 0) // 9/8/2013 jichi: int WINAPI lstrlen(LPCTSTR lpString);
117 + NEW_HOOK(lstrlenW, s_arg1, 0,s_arg1,0, USING_UNICODE|USING_STRING, 0) // 9/8/2013 jichi: add lstrlen
118 +
119 + // size_t strlen(const char *str);
120 + // size_t strlen_l(const char *str, _locale_t locale);
121 + // size_t wcslen(const wchar_t *str);
122 + // size_t wcslen_l(const wchar_t *str, _locale_t locale);
123 + // size_t _mbslen(const unsigned char *str);
124 + // size_t _mbslen_l(const unsigned char *str, _locale_t locale);
125 + // size_t _mbstrlen(const char *str);
126 + // size_t _mbstrlen_l(const char *str, _locale_t locale);
127 +
128 + // http://msdn.microsoft.com/en-us/library/ex0hs2ad.aspx
129 + // Needed by 娘姉妹
130 + //
131 + // <tchar.h>
132 + // char *_strinc(const char *current, _locale_t locale);
133 + // wchar_t *_wcsinc(const wchar_t *current, _locale_t locale);
134 + // <mbstring.h>
135 + // unsigned char *_mbsinc(const unsigned char *current);
136 + // unsigned char *_mbsinc_l(const unsigned char *current, _locale_t locale);
137 + //_(L"_strinc", _strinc, 4, 0,4,0, USING_STRING, 0) // 12/13/2013 jichi
138 + //_(L"_wcsinc", _wcsinc, 4, 0,4,0, USING_UNICODE|USING_STRING, 0)
139 + DPRINT("leave");
140 +}
141 +
142 +void PcHooks::hookWcharFunctions()
143 +{
144 + DPRINT("enter");
145 + // 12/1/2013 jichi:
146 + // AlterEgo
147 + // http://tieba.baidu.com/p/2736475133
148 + // http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page355
149 + //
150 + // MultiByteToWideChar
151 + // http://blgames.proboards.com/thread/265
152 + //
153 + // WideCharToMultiByte
154 + // http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page156
155 + //
156 + // int MultiByteToWideChar(
157 + // _In_ UINT CodePage,
158 + // _In_ DWORD dwFlags,
159 + // _In_ LPCSTR lpMultiByteStr, // hook here
160 + // _In_ int cbMultiByte,
161 + // _Out_opt_ LPWSTR lpWideCharStr,
162 + // _In_ int cchWideChar
163 + // );
164 + // int WideCharToMultiByte(
165 + // _In_ UINT CodePage,
166 + // _In_ DWORD dwFlags,
167 + // _In_ LPCWSTR lpWideCharStr,
168 + // _In_ int cchWideChar,
169 + // _Out_opt_ LPSTR lpMultiByteStr,
170 + // _In_ int cbMultiByte,
171 + // _In_opt_ LPCSTR lpDefaultChar,
172 + // _Out_opt_ LPBOOL lpUsedDefaultChar
173 + // );
174 +
175 + enum stack {
176 + s_retaddr = 0
177 + //, s_arg1 = 4 * 1 // 0x4
178 + //, s_arg2 = 4 * 2 // 0x8
179 + , s_arg3 = 4 * 3 // 0xc
180 + //, s_arg4 = 4 * 4 // 0x10
181 + //, s_arg5 = 4 * 5 // 0x14
182 + //, s_arg6 = 4 * 6 // 0x18
183 + };
184 +
185 + // 3/17/2014 jichi: Temporarily disabled
186 + // http://sakuradite.com/topic/159
187 + NEW_HOOK(MultiByteToWideChar, s_arg3, 0,4,0, USING_STRING, 4)
188 + NEW_HOOK(WideCharToMultiByte, s_arg3, 0,4,0, USING_UNICODE|USING_STRING, 4)
189 + DPRINT("leave");
190 +}
191 +
192 +// EOF
1 +#pragma once
2 +
3 +// pchooks.h
4 +// 8/1/2014 jichi
5 +
6 +#include "config.h"
7 +
8 +namespace PcHooks {
9 +
10 +void hookGDIFunctions();
11 +void hookLstrFunctions();
12 +void hookWcharFunctions();
13 +
14 +} // namespace PcHooks
15 +
16 +// EOF
1 +// util/util.cc
2 +// 8/23/2013 jichi
3 +// Branch: ITH_Engine/engine.cpp, revision 133
4 +// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
5 +
6 +#include "engine/util.h"
7 +#include "ith/sys/sys.h"
8 +
9 +namespace { // unnamed
10 +
11 +// jichi 4/19/2014: Return the integer that can mask the signature
12 +DWORD SigMask(DWORD sig)
13 +{
14 + __asm
15 + {
16 + xor ecx,ecx
17 + mov eax,sig
18 +_mask:
19 + shr eax,8
20 + inc ecx
21 + test eax,eax
22 + jnz _mask
23 + sub ecx,4
24 + neg ecx
25 + or eax,-1
26 + shl ecx,3
27 + shr eax,cl
28 + }
29 +}
30 +
31 +} // namespace unnamed
32 +
33 +// jichi 8/24/2013: binary search?
34 +DWORD Util::GetCodeRange(DWORD hModule,DWORD *low, DWORD *high)
35 +{
36 + IMAGE_DOS_HEADER *DosHdr;
37 + IMAGE_NT_HEADERS *NtHdr;
38 + DWORD dwReadAddr;
39 + IMAGE_SECTION_HEADER *shdr;
40 + DosHdr = (IMAGE_DOS_HEADER *)hModule;
41 + if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
42 + dwReadAddr = hModule + DosHdr->e_lfanew;
43 + NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr;
44 + if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
45 + shdr = (PIMAGE_SECTION_HEADER)((DWORD)(&NtHdr->OptionalHeader) + NtHdr->FileHeader.SizeOfOptionalHeader);
46 + while ((shdr->Characteristics & IMAGE_SCN_CNT_CODE) == 0)
47 + shdr++;
48 + *low = hModule + shdr->VirtualAddress;
49 + *high = *low + (shdr->Misc.VirtualSize & 0xfffff000) + 0x1000;
50 + }
51 + }
52 + return 0;
53 +}
54 +
55 +DWORD Util::FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig)
56 +{
57 + //WCHAR str[0x40];
58 + enum { reverse_length = 0x800 };
59 + DWORD t, l;
60 + DWORD mask = SigMask(sig);
61 + bool flag2;
62 + for (DWORD i = 0x1000; i < size-4; i++) {
63 + bool flag1 = false;
64 + if (*(BYTE *)(pt + i) == 0xe8) {
65 + flag1 = flag2 = true;
66 + t = *(DWORD *)(pt + i + 1);
67 + } else if (*(WORD *)(pt + i) == 0x15ff) {
68 + flag1 = true;
69 + flag2 = false;
70 + t = *(DWORD *)(pt + i + 2);
71 + }
72 + if (flag1) {
73 + if (flag2) {
74 + flag1 = (pt + i + 5 + t == fun);
75 + l = 5;
76 + } else if (t >= pt && t <= pt + size - 4) {
77 + flag1 = fun == *(DWORD *)t;
78 + l = 6;
79 + } else
80 + flag1 = false;
81 + if (flag1)
82 + //swprintf(str,L"CALL addr: 0x%.8X",pt + i);
83 + //OutputConsole(str);
84 + for (DWORD j = i; j > i - reverse_length; j--)
85 + if ((*(WORD *)(pt + j)) == (sig & mask)) //Fun entry 1.
86 + //swprintf(str,L"Entry: 0x%.8X",pt + j);
87 + //OutputConsole(str);
88 + return pt + j;
89 + else
90 + i += l;
91 + }
92 + }
93 + //OutputConsole(L"Find call and entry failed.");
94 + return 0;
95 +}
96 +
97 +DWORD Util::FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp)
98 +{
99 + BYTE sig = (jmp) ? 0xe9 : 0xe8;
100 + for (DWORD i = 0x1000; i < size - 4; i++)
101 + if (sig == *(BYTE *)(pt + i)) {
102 + DWORD t = *(DWORD *)(pt + i + 1);
103 + if(fun == pt + i + 5 + t)
104 + //OutputDWORD(pt + i);
105 + return pt + i;
106 + else
107 + i += 5;
108 + }
109 + return 0;
110 +}
111 +
112 +DWORD Util::FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp)
113 +{
114 + WORD sig = jmp ? 0x25ff : 0x15ff;
115 + for (DWORD i = 0x1000; i < size - 4; i++)
116 + if (sig == *(WORD *)(pt + i)) {
117 + DWORD t = *(DWORD *)(pt + i + 2);
118 + if (t > pt && t < pt + size) {
119 + if (fun == *(DWORD *)t)
120 + return pt + i;
121 + else
122 + i += 5;
123 + }
124 + }
125 + return 0;
126 +}
127 +
128 +DWORD Util::FindCallBoth(DWORD fun, DWORD size, DWORD pt)
129 +{
130 + for (DWORD i = 0x1000; i < size - 4; i++) {
131 + if (*(BYTE *)(pt + i) == 0xe8) {
132 + DWORD t = *(DWORD *)(pt + i + 1) + pt + i + 5;
133 + if (t == fun)
134 + return i;
135 + }
136 + if (*(WORD *)(pt + i) == 0x15ff) {
137 + DWORD t = *(DWORD *)(pt + i + 2);
138 + if (t >= pt && t <= pt + size - 4) {
139 + if (*(DWORD *)t == fun)
140 + return i;
141 + else
142 + i += 6;
143 + }
144 + }
145 + }
146 + return 0;
147 +}
148 +
149 +DWORD Util::FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig)
150 +{
151 + //WCHAR str[0x40];
152 + enum { reverse_length = 0x800 };
153 + DWORD mask = SigMask(sig);
154 + for (DWORD i = 0x1000; i < size - 4; i++)
155 + if (*(WORD *)(pt + i) == 0x15ff) {
156 + DWORD t = *(DWORD *)(pt + i + 2);
157 + if (t >= pt && t <= pt + size - 4) {
158 + if (*(DWORD *)t == fun)
159 + //swprintf(str,L"CALL addr: 0x%.8X",pt + i);
160 + //OutputConsole(str);
161 + for (DWORD j = i ; j > i - reverse_length; j--)
162 + if ((*(DWORD *)(pt + j) & mask) == sig) // Fun entry 1.
163 + //swprintf(str,L"Entry: 0x%.8X",pt + j);
164 + //OutputConsole(str);
165 + return pt + j;
166 +
167 + } else
168 + i += 6;
169 + }
170 + //OutputConsole(L"Find call and entry failed.");
171 + return 0;
172 +}
173 +
174 +DWORD Util::FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig)
175 +{
176 + //WCHAR str[0x40];
177 + enum { reverse_length = 0x800 };
178 + if (DWORD i = FindCallOrJmpRel(fun, size, pt, false)) {
179 + DWORD mask = SigMask(sig);
180 + for (DWORD j = i; j > i - reverse_length; j--)
181 + if (((*(DWORD *)j) & mask) == sig) //Fun entry 1.
182 + //swprintf(str,L"Entry: 0x%.8X",j);
183 + //OutputConsole(str);
184 + return j;
185 + //OutputConsole(L"Find call and entry failed.");
186 + }
187 + return 0;
188 +}
189 +DWORD Util::FindEntryAligned(DWORD start, DWORD back_range)
190 +{
191 + start &= ~0xf;
192 + for (DWORD i = start, j = start - back_range; i > j; i-=0x10) {
193 + DWORD k = *(DWORD *)(i-4);
194 + if (k == 0xcccccccc
195 + || k == 0x90909090
196 + || k == 0xccccccc3
197 + || k == 0x909090c3
198 + )
199 + return i;
200 + DWORD t = k & 0xff0000ff;
201 + if (t == 0xcc0000c2 || t == 0x900000c2)
202 + return i;
203 + k >>= 8;
204 + if (k == 0xccccc3 || k == 0x9090c3)
205 + return i;
206 + t = k & 0xff;
207 + if (t == 0xc2)
208 + return i;
209 + k >>= 8;
210 + if (k == 0xccc3 || k == 0x90c3)
211 + return i;
212 + k >>= 8;
213 + if (k == 0xc3)
214 + return i;
215 + }
216 + return 0;
217 +}
218 +
219 +DWORD Util::FindImportEntry(DWORD hModule, DWORD fun)
220 +{
221 + IMAGE_DOS_HEADER *DosHdr;
222 + IMAGE_NT_HEADERS *NtHdr;
223 + DWORD IAT, end, pt, addr;
224 + DosHdr = (IMAGE_DOS_HEADER *)hModule;
225 + if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
226 + NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
227 + if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
228 + IAT = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
229 + end = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
230 + IAT += hModule;
231 + end += IAT;
232 + for (pt = IAT; pt < end; pt += 4) {
233 + addr = *(DWORD *)pt;
234 + if (addr == fun)
235 + return pt;
236 + }
237 + }
238 + }
239 + return 0;
240 +}
241 +
242 +// Search string in rsrc section. This section usually contains version and copyright info.
243 +bool Util::SearchResourceString(LPCWSTR str)
244 +{
245 + DWORD hModule = Util::GetModuleBase();
246 + IMAGE_DOS_HEADER *DosHdr;
247 + IMAGE_NT_HEADERS *NtHdr;
248 + DosHdr = (IMAGE_DOS_HEADER *)hModule;
249 + DWORD rsrc, size;
250 + //__asm int 3
251 + if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
252 + NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
253 + if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
254 + rsrc = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
255 + if (rsrc) {
256 + rsrc += hModule;
257 + if (IthGetMemoryRange((LPVOID)rsrc, &rsrc ,&size) &&
258 + SearchPattern(rsrc, size - 4, str, wcslen(str) << 1))
259 + return true;
260 + }
261 + }
262 + }
263 + return false;
264 +}
265 +
266 +// jichi 4/15/2014: Copied from GetModuleBase in ITH CLI, for debugging purpose
267 +DWORD Util::FindModuleBase(DWORD hash)
268 +{
269 + __asm
270 + {
271 + mov eax,fs:[0x30]
272 + mov eax,[eax+0xc]
273 + mov esi,[eax+0x14]
274 + mov edi,_wcslwr
275 +listfind:
276 + mov edx,[esi+0x28]
277 + test edx,edx
278 + jz notfound
279 + push edx
280 + call edi
281 + pop edx
282 + xor eax,eax
283 +calc:
284 + movzx ecx, word ptr [edx]
285 + test cl,cl
286 + jz fin
287 + ror eax,7
288 + add eax,ecx
289 + add edx,2
290 + jmp calc
291 +fin:
292 + cmp eax,[hash]
293 + je found
294 + mov esi,[esi]
295 + jmp listfind
296 +notfound:
297 + xor eax,eax
298 + jmp termin
299 +found:
300 + mov eax,[esi+0x10]
301 +termin:
302 + }
303 +}
304 +
305 +// EOF
1 +#pragma once
2 +
3 +// util/util.h
4 +// 8/23/2013 jichi
5 +
6 +#include "config.h"
7 +
8 +namespace Util {
9 +
10 +DWORD GetCodeRange(DWORD hModule,DWORD *low, DWORD *high);
11 +DWORD FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig);
12 +DWORD FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp);
13 +DWORD FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp);
14 +DWORD FindCallBoth(DWORD fun, DWORD size, DWORD pt);
15 +DWORD FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig);
16 +DWORD FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig);
17 +DWORD FindEntryAligned(DWORD start, DWORD back_range);
18 +DWORD FindImportEntry(DWORD hModule, DWORD fun);
19 +
20 +// jichi 4/15/2014: Copied from ITH CLI, for debugging purpose
21 +DWORD FindModuleBase(DWORD hash);
22 +
23 +bool SearchResourceString(LPCWSTR str);
24 +
25 +/**
26 + * @param name process name without path deliminator
27 + */
28 +inline void GetProcessName(wchar_t *name)
29 +{
30 + //assert(name);
31 + PLDR_DATA_TABLE_ENTRY it;
32 + __asm
33 + {
34 + mov eax,fs:[0x30]
35 + mov eax,[eax+0xc]
36 + mov eax,[eax+0xc]
37 + mov it,eax
38 + }
39 + ::wcscpy(name, it->BaseDllName.Buffer);
40 +}
41 +
42 +/**
43 + * @param path with process name and directy name
44 + */
45 +inline void GetProcessPath(wchar_t *path)
46 +{
47 + //assert(path);
48 + PLDR_DATA_TABLE_ENTRY it;
49 + __asm
50 + {
51 + mov eax,fs:[0x30]
52 + mov eax,[eax+0xc]
53 + mov eax,[eax+0xc]
54 + mov it,eax
55 + }
56 + ::wcscpy(path, it->FullDllName.Buffer);
57 +}
58 +
59 +/**
60 + * @return HANDLE module handle
61 + */
62 +inline DWORD GetModuleBase()
63 +{
64 + __asm
65 + {
66 + mov eax,fs:[0x18]
67 + mov eax,[eax+0x30]
68 + mov eax,[eax+0xc]
69 + mov eax,[eax+0xc]
70 + mov eax,[eax+0x18]
71 + }
72 +}
73 +
74 +} // namespace Util
75 +
76 +// EOF
1 +// texthook.cc
2 +// 8/24/2013 jichi
3 +// Branch: ITH_DLL/texthook.cpp, rev 128
4 +// 8/24/2013 TODO: Clean up this file
5 +
6 +#ifdef _MSC_VER
7 +# pragma warning (disable:4100) // C4100: unreference formal parameter
8 +# pragma warning (disable:4018) // C4018: sign/unsigned mismatch
9 +//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
10 +#endif // _MSC_VER
11 +
12 +#include "cli.h"
13 +#include "engine/match.h"
14 +#include "ith/common/except.h"
15 +//#include "ith/common/growl.h"
16 +#include "ith/sys/sys.h"
17 +#include "disasm/disasm.h"
18 +//#include "winseh/winseh.h"
19 +
20 +//#define ConsoleOutput(...) (void)0 // jichi 9/17/2013: I don't need this ><
21 +
22 +// - Global variables -
23 +
24 +// 10/14/2014 jichi: disable GDI hooks
25 +static bool gdi_hook_disabled_ = false;
26 +void DisableGDIHooks()
27 +{ ::gdi_hook_disabled_ = true; }
28 +
29 +static bool IsGDIFunction(LPCVOID addr)
30 +{
31 + static LPVOID funcs[] = { HOOK_GDI_FUNCTION_LIST };
32 + for (size_t i = 0; i < sizeof(funcs)/sizeof(*funcs); i++)
33 + if (addr == funcs[i])
34 + return true;
35 + return false;
36 +}
37 +
38 +//FilterRange filter[8];
39 +
40 +DWORD flag,
41 + enter_count;
42 +
43 +TextHook *hookman,
44 + *current_available;
45 +
46 +// - Unnamed helpers -
47 +
48 +namespace { // unnamed
49 +//provide const time hook entry.
50 +int userhook_count;
51 +
52 +#if 0 // 3/6/2015 jichi: this hook is not used and hence disabled
53 +const byte common_hook2[] = {
54 + 0x89, 0x3c,0xe4, // mov [esp],edi
55 + 0x60, // pushad
56 + 0x9c, // pushfd
57 + 0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
58 + 0x8b,0x32, // mov esi,[edx] ; return address
59 + 0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook
60 + 0xe8, 0,0,0,0, // call @hook
61 + 0x9d, // popfd
62 + 0x61, // popad
63 + 0x5f, // pop edi ; skip return address on stack
64 +}; //...
65 +#endif // 0
66 +
67 +const BYTE common_hook[] = {
68 + 0x9c, // pushfd
69 + 0x60, // pushad
70 + 0x9c, // pushfd
71 + 0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
72 + 0x8b,0x32, // mov esi,[edx] ; return address
73 + 0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TexHhook
74 + 0xe8, 0,0,0,0, // call @hook
75 + 0x9d, // popfd
76 + 0x61, // popad
77 + 0x9d // popfd
78 +};
79 +
80 +/**
81 + * jichi 7/19/2014
82 + *
83 + * @param original_addr
84 + * @param new_addr
85 + * @param hook_len
86 + * @param original_len
87 + * @return -1 if failed, else 0 if ?, else ?
88 + */
89 +int MapInstruction(DWORD original_addr, DWORD new_addr, BYTE &hook_len, BYTE &original_len)
90 +{
91 + int flag = 0;
92 + DWORD l = 0;
93 + const BYTE *r = (const BYTE *)original_addr; // 7/19/2014 jichi: original address is not modified
94 + BYTE *c = (BYTE *)new_addr; // 7/19/2014 jichi: but new address might be modified
95 + while((r - (BYTE *) original_addr) < 5) {
96 + l = ::disasm(r);
97 + if (l == 0) {
98 + ConsoleOutput("vnrcli:MapInstruction: FAILED: failed to disasm");
99 + return -1;
100 + }
101 +
102 + ::memcpy(c, r, l);
103 + if (*r >= 0x70 && *r < 0x80) {
104 + c[0] = 0xf;
105 + c[1] = *r + 0x10;
106 + c += 6;
107 + __asm
108 + {
109 + mov eax,r
110 + add eax,2
111 + movsx edx,byte ptr [eax-1]
112 + add edx,eax
113 + mov eax,c
114 + sub edx,eax
115 + mov [eax-4],edx
116 + }
117 + } else if (*r == 0xeb) {
118 + c[0] = 0xe9;
119 + c += 5;
120 + __asm
121 + {
122 + mov eax,r
123 + add eax,2
124 + movsx edx,[eax-1]
125 + add edx,eax
126 + mov eax,c
127 + sub edx,eax
128 + mov [eax-4],edx
129 + }
130 + if (r - (BYTE *)original_addr < 5 - l) {
131 + ConsoleOutput("vnrcli:MapInstruction: not safe to move instruction right after short jmp");
132 + return -1; // Not safe to move instruction right after short jmp.
133 + } else
134 + flag = 1;
135 + } else if (*r == 0xe8 || *r == 0xe9) {
136 + c[0]=*r;
137 + c += 5;
138 + flag = (*r == 0xe9);
139 + __asm
140 + {
141 + mov eax,r
142 + add eax,5
143 + mov edx,[eax-4]
144 + add edx,eax
145 + mov eax,c
146 + sub edx,eax
147 + mov [eax-4],edx
148 + }
149 + } else if (*r == 0xf && (*(r + 1) >> 4) == 0x8) {
150 + c += 6;
151 + __asm
152 + {
153 + mov eax,r
154 + mov edx,dword ptr [eax+2]
155 + add eax,6
156 + add eax,edx
157 + mov edx,c
158 + sub eax,edx
159 + mov [edx-4],eax
160 + }
161 + }
162 + else
163 + c += l;
164 + r += l;
165 + }
166 + original_len = r - (BYTE *)original_addr;
167 + hook_len = c - (BYTE *)new_addr;
168 + return flag;
169 +}
170 +
171 +//copy original instruction
172 +//jmp back
173 +DWORD GetModuleBase(DWORD hash)
174 +{
175 + __asm
176 + {
177 + mov eax,fs:[0x30]
178 + mov eax,[eax+0xc]
179 + mov esi,[eax+0x14]
180 + mov edi,_wcslwr
181 +listfind:
182 + mov edx,[esi+0x28]
183 + test edx,edx
184 + jz notfound
185 + push edx
186 + call edi
187 + pop edx
188 + xor eax,eax
189 +calc:
190 + movzx ecx, word ptr [edx]
191 + test cl,cl
192 + jz fin
193 + ror eax,7
194 + add eax,ecx
195 + add edx,2
196 + jmp calc
197 +fin:
198 + cmp eax,[hash]
199 + je found
200 + mov esi,[esi]
201 + jmp listfind
202 +notfound:
203 + xor eax,eax
204 + jmp termin
205 +found:
206 + mov eax,[esi+0x10]
207 +termin:
208 + }
209 +}
210 +
211 +DWORD GetModuleBase()
212 +{
213 + __asm
214 + {
215 + mov eax, fs:[0x18]
216 + mov eax, [eax + 0x30]
217 + mov eax, [eax + 0xc]
218 + mov eax, [eax + 0xc]
219 + mov eax, [eax + 0x18]
220 + }
221 +}
222 +
223 +//void NotifyHookInsert()
224 +//{
225 +// if (live)
226 +// {
227 +// BYTE buffer[0x10];
228 +// *(DWORD*)buffer=-1;
229 +// *(DWORD*)(buffer+4)=1;
230 +// IO_STATUS_BLOCK ios;
231 +// NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0);
232 +// }
233 +//}
234 +
235 +__declspec(naked) void SafeExit() // Return to eax
236 +{
237 + __asm
238 + {
239 + mov [esp+0x24], eax
240 + popfd
241 + popad
242 + retn
243 + }
244 +}
245 +
246 +#if 0
247 +// jichi 12/2/2013: This function mostly return 0.
248 +// But sometimes return the hook address from TextHook::Send
249 +__declspec(naked) // jichi 10/2/2013: No prolog and epilog
250 +int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
251 +{
252 + //with_seh(hook->Send(dwDataBase, dwRetn));
253 + seh_push_(seh_exit, 0, eax, ebx) // jichi 12/13/2013: only eax and ebx are available. ecx and edx are used.
254 + __asm
255 + {
256 + push esi
257 + push edx
258 + call TextHook::UnsafeSend
259 + test eax, eax
260 + jz seh_exit // label in seh_pop
261 + mov ecx, SafeExit
262 + mov [esp + 8], ecx // jichi 12/13/2013: change exit point if Send returns non-zero, not + 8 beause two elements has been pused
263 + }
264 + seh_pop_(seh_exit)
265 + __asm retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
266 +}
267 +#endif // 0
268 +
269 +#if 1
270 +__declspec(naked) // jichi 10/2/2013: No prolog and epilog
271 +int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
272 +{
273 + // jichi 12/17/2013: The function parameters here are meaning leass. The parameters are in esi and edi
274 + __asm
275 + {
276 + push esi
277 + push edx
278 + call TextHook::Send
279 + test eax, eax
280 + jz ok // label in seh_pop
281 + mov ecx, SafeExit
282 + mov [esp], ecx // jichi 12/13/2013: change exit point if Send returns non-zero
283 + ok:
284 + retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
285 + }
286 +}
287 +#endif // 1
288 +
289 + // jichi 12/13/2013: return if the retn address is within the filter dlls
290 +inline bool HookFilter(DWORD retn)
291 +{
292 + for (DWORD i = 0; ::filter[i].lower; i++)
293 + if (retn > ::filter[i].lower && retn < ::filter[i].upper)
294 + return true;
295 + return false;
296 +}
297 +
298 +} // unnamed namespace
299 +
300 +// - TextHook methods -
301 +
302 +// jichi 12/2/2013: This function mostly return 0.
303 +// It return the hook address only for auxiliary case.
304 +// However, because no known hooks are auxiliary, this function always return 0.
305 +//
306 +// jichi 5/11/2014:
307 +// - dwDataBase: the stack address
308 +// - dwRetn: the return address of the hook
309 +DWORD TextHook::Send(DWORD dwDataBase, DWORD dwRetn)
310 +{
311 + DWORD ret = 0;
312 + //char b[0x100];
313 + //::wcstombs(b, hook_name, 0x100);
314 + //ConsoleOutput(b);
315 + ITH_WITH_SEH(ret = UnsafeSend(dwDataBase, dwRetn));
316 + return ret;
317 +}
318 +
319 +DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn)
320 +{
321 + enum { SMALL_BUFF_SIZE = 0x80 };
322 + enum { MAX_DATA_SIZE = 0x10000 }; // jichi 12/25/2013: The same as the original ITH
323 + DWORD dwCount,
324 + dwAddr,
325 + dwDataIn,
326 + dwSplit;
327 + BYTE *pbData,
328 + pbSmallBuff[SMALL_BUFF_SIZE];
329 + DWORD dwType = hp.type;
330 + if (!live)
331 + return 0;
332 + if ((dwType & NO_CONTEXT) == 0 && HookFilter(dwRetn))
333 + return 0;
334 +
335 + // jichi 10/24/2014: Skip GDI functions
336 + if (::gdi_hook_disabled_ && ::IsGDIFunction((LPCVOID)hp.addr))
337 + return 0;
338 +
339 + dwAddr = hp.addr;
340 +
341 + /** jichi 12/24/2014
342 + * @param addr function address
343 + * @param frame real address of the function, supposed to be the same as addr
344 + * @param stack address of current stack - 4
345 + * @return If success, which is reverted
346 + */
347 + if (::trigger)
348 + ::trigger = Engine::InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
349 + // jichi 10/21/2014: Directly invoke engine functions.
350 + //if (trigger) {
351 + // if (InsertDynamicHook)
352 + // trigger = InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
353 + // else
354 + // trigger = 0;
355 + //}
356 +#if 0 // diasble HOOK_AUXILIARY
357 + // jichi 12/13/2013: None of known hooks are auxiliary
358 + if (dwType & HOOK_AUXILIARY) {
359 + //Clean hook when dynamic hook finished.
360 + //AUX hook is only used for a foothold of dynamic hook.
361 + if (!trigger) {
362 + ClearHook();
363 + // jichi 12/13/2013: This is the only place where this function could return non-zero value
364 + // However, I non of the known hooks are auxiliary
365 + return dwAddr;
366 + }
367 + return 0;
368 + }
369 +#endif // 0
370 + // jichi 10/24/2014: generic hook function
371 + if (hp.hook_fun && !hp.hook_fun(dwDataBase, &hp))
372 + hp.hook_fun = nullptr;
373 +
374 + if (dwType & HOOK_EMPTY) // jichi 10/24/2014: dummy hook only for dynamic hook
375 + return 0;
376 +
377 + // jichi 2/2/2015: Send multiple texts
378 + for (BYTE textIndex = 0; textIndex <= hp.extra_text_count; textIndex++) {
379 + dwCount = 0;
380 + dwSplit = 0;
381 + dwDataIn = *(DWORD *)(dwDataBase + hp.off); // default value
382 +
383 + //if (dwType & EXTERN_HOOK) {
384 + if (hp.text_fun) { // jichi 10/24/2014: remove EXTERN_HOOK
385 + //DataFun fun=(DataFun)hp.text_fun;
386 + //auto fun = hp.text_fun;
387 + hp.text_fun(dwDataBase, &hp, textIndex, &dwDataIn, &dwSplit, &dwCount);
388 + //if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
389 + // return 0;
390 + if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
391 + dwSplit -= ::processStartAddress;
392 + } else {
393 + if (dwDataIn == 0)
394 + return 0;
395 + if (dwType & FIXING_SPLIT)
396 + dwSplit = FIXED_SPLIT_VALUE; // fuse all threads, and prevent floating
397 + else if (dwType & USING_SPLIT) {
398 + dwSplit = *(DWORD *)(dwDataBase + hp.split);
399 + if (dwType & SPLIT_INDIRECT) {
400 + if (IthGetMemoryRange((LPVOID)(dwSplit + hp.split_ind), 0, 0))
401 + dwSplit = *(DWORD *)(dwSplit + hp.split_ind);
402 + else
403 + return 0;
404 + }
405 + if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
406 + dwSplit -= ::processStartAddress;
407 + }
408 + if (dwType & DATA_INDIRECT) {
409 + if (IthGetMemoryRange((LPVOID)(dwDataIn + hp.ind), 0, 0))
410 + dwDataIn = *(DWORD *)(dwDataIn + hp.ind);
411 + else
412 + return 0;
413 + }
414 + //if (dwType & PRINT_DWORD) {
415 + // swprintf((WCHAR *)(pbSmallBuff + HEADER_SIZE), L"%.8X ", dwDataIn);
416 + // dwDataIn = (DWORD)pbSmallBuff + HEADER_SIZE;
417 + //}
418 + dwCount = GetLength(dwDataBase, dwDataIn);
419 + }
420 +
421 + // jichi 12/25/2013: validate data size
422 + if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
423 + return 0;
424 +
425 + size_t sz = dwCount + HEADER_SIZE;
426 + if (sz >= SMALL_BUFF_SIZE)
427 + pbData = new BYTE[sz];
428 + //ITH_MEMSET_HEAP(pbData, 0, sz * sizeof(BYTE)); // jichi 9/26/2013: zero memory
429 + else
430 + pbData = pbSmallBuff;
431 +
432 + if (hp.length_offset == 1) {
433 + if (dwType & STRING_LAST_CHAR) {
434 + LPWSTR ts = (LPWSTR)dwDataIn;
435 + dwDataIn = ts[::wcslen(ts) -1];
436 + }
437 + dwDataIn &= 0xffff;
438 + if ((dwType & BIG_ENDIAN) && (dwDataIn >> 8))
439 + dwDataIn = _byteswap_ushort(dwDataIn & 0xffff);
440 + if (dwCount == 1)
441 + dwDataIn &= 0xff;
442 + *(WORD *)(pbData + HEADER_SIZE) = dwDataIn & 0xffff;
443 + }
444 + else
445 + ::memcpy(pbData + HEADER_SIZE, (void *)dwDataIn, dwCount);
446 +
447 + // jichi 10/14/2014: Add filter function
448 + if (hp.filter_fun && !hp.filter_fun(pbData + HEADER_SIZE, &dwCount, &hp, textIndex) || dwCount <= 0) {
449 + if (pbData != pbSmallBuff)
450 + delete[] pbData;
451 + return 0;
452 + }
453 +
454 + *(DWORD *)pbData = dwAddr;
455 + if (dwType & (NO_CONTEXT|FIXING_SPLIT))
456 + dwRetn = 0;
457 + else if (dwRetn && (dwType & RELATIVE_SPLIT))
458 + dwRetn -= ::processStartAddress;
459 +
460 + *((DWORD *)pbData + 1) = dwRetn;
461 + *((DWORD *)pbData + 2) = dwSplit;
462 + if (dwCount) {
463 + IO_STATUS_BLOCK ios = {};
464 +
465 + IthCoolDown(); // jichi 9/28/2013: cool down to prevent parallelization in wine
466 + //CliLockPipe();
467 + if (STATUS_PENDING == NtWriteFile(hPipe, 0, 0, 0, &ios, pbData, dwCount + HEADER_SIZE, 0, 0)) {
468 + NtWaitForSingleObject(hPipe, 0, 0);
469 + NtFlushBuffersFile(hPipe, &ios);
470 + }
471 + //CliUnlockPipe();
472 + }
473 + if (pbData != pbSmallBuff)
474 + delete[] pbData;
475 + }
476 + return 0;
477 +
478 +}
479 +
480 +int TextHook::InsertHook()
481 +{
482 + //ConsoleOutput("vnrcli:InsertHook: enter");
483 + NtWaitForSingleObject(hmMutex, 0, 0);
484 + int ok = InsertHookCode();
485 + IthReleaseMutex(hmMutex);
486 + if (hp.type & HOOK_ADDITIONAL) {
487 + NotifyHookInsert(hp.addr);
488 + //ConsoleOutput(hook_name);
489 + //RegisterHookName(hook_name,hp.addr);
490 + }
491 + //ConsoleOutput("vnrcli:InsertHook: leave");
492 + return ok;
493 +}
494 +
495 +int TextHook::InsertHookCode()
496 +{
497 + enum : int { yes = 0, no = 1 };
498 + DWORD ret = no;
499 + // jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7
500 + ITH_WITH_SEH(ret = UnsafeInsertHookCode());
501 + //if (ret == no)
502 + // ITH_WARN(L"Failed to insert hook");
503 + return ret;
504 +}
505 +
506 +int TextHook::UnsafeInsertHookCode()
507 +{
508 + //ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter");
509 + enum : int { yes = 0, no = 1 };
510 + // MODULE_OFFSET is set, but there's no module address
511 + // this means that this is an absolute address found on Windows 2000/XP
512 + // we make the address relative to the process base
513 + // we also store the original address in the function field because normally there can not
514 + // exist a function address without a module address
515 + if (hp.type & MODULE_OFFSET && !hp.module) {
516 + DWORD base = GetModuleBase();
517 + hp.function = hp.addr;
518 + hp.addr -= 0x400000;
519 + hp.addr += base;
520 + hp.type &= ~MODULE_OFFSET;
521 + }
522 + else if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address.
523 + if (DWORD base = GetModuleBase(hp.module)) {
524 + if (hp.function && (hp.type & FUNCTION_OFFSET)) {
525 + base = GetExportAddress(base, hp.function);
526 + if (base)
527 + hp.addr += base;
528 + else {
529 + current_hook--;
530 + ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: function not found in the export table");
531 + return no;
532 + }
533 + }
534 + else {
535 + hp.addr += base;
536 + }
537 + hp.type &= ~(MODULE_OFFSET | FUNCTION_OFFSET);
538 + }
539 + else {
540 + current_hook--;
541 + ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: module not present");
542 + return no;
543 + }
544 + }
545 +
546 + {
547 + TextHook *it = hookman;
548 + for (int i = 0; (i < current_hook) && it; it++) { // Check if there is a collision.
549 + if (it->Address())
550 + i++;
551 + //it = hookman + i;
552 + if (it == this)
553 + continue;
554 + if (it->Address() <= hp.addr &&
555 + it->Address() + it->Length() > hp.addr) {
556 + it->ClearHook();
557 + break;
558 + }
559 + }
560 + }
561 +
562 + // Verify hp.addr.
563 + MEMORY_BASIC_INFORMATION info = {};
564 + NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)hp.addr, MemoryBasicInformation, &info, sizeof(info), nullptr);
565 + if (info.Type & PAGE_NOACCESS) {
566 + ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: page no access");
567 + return no;
568 + }
569 +
570 + // Initialize common routine.
571 + memcpy(recover, common_hook, sizeof(common_hook));
572 + BYTE *c = (BYTE *)hp.addr,
573 + *r = recover;
574 + BYTE inst[8]; // jichi 9/27/2013: Why 8? Only 5 bytes will be written using NtWriteVirtualMemory
575 + inst[0] = 0xe9; // jichi 9/27/2013: 0xe9 is jump, see: http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Hackers_Manual
576 + __asm
577 + {
578 + mov edx,r // r = recover
579 + mov eax,this
580 + mov [edx+0xa],eax // push TextHook*, resolve to correspond hook.
581 + lea eax,[edx+0x13]
582 + mov edx,ProcessHook
583 + sub edx,eax
584 + mov [eax-4],edx // call ProcessHook
585 + mov eax,c
586 + add eax,5
587 + mov edx,r
588 + sub edx,eax
589 + lea eax,inst+1
590 + mov [eax],edx // jichi 12/17/2013: the parameter of jmp is in edx. So, ProcessHook must be naked.
591 + }
592 + r += sizeof(common_hook);
593 + hp.hook_len = 5;
594 + //bool jmpflag=false; // jichi 9/28/2013: nto used
595 + // Copy original code.
596 + switch (MapInstruction(hp.addr, (DWORD)r, hp.hook_len, hp.recover_len)) {
597 + case -1:
598 + ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: failed to map instruction");
599 + return no;
600 + case 0:
601 + __asm
602 + {
603 + mov ecx,this
604 + movzx eax,[ecx]hp.hook_len
605 + movzx edx,[ecx]hp.recover_len
606 + add edx,[ecx]hp.addr
607 + add eax,r
608 + add eax,5
609 + sub edx,eax
610 + mov [eax-5],0xe9 // jichi 9/27/2013: 0xe9 is jump
611 + mov [eax-4],edx
612 + }
613 + }
614 + // jichi 9/27/2013: Save the original instructions in the memory
615 + memcpy(original, (LPVOID)hp.addr, hp.recover_len);
616 + //Check if the new hook range conflict with existing ones. Clear older if conflict.
617 + {
618 + TextHook *it = hookman;
619 + for (int i = 0; i < current_hook; it++) {
620 + if (it->Address())
621 + i++;
622 + if (it == this)
623 + continue;
624 + if (it->Address() >= hp.addr &&
625 + it->Address() < hp.hook_len + hp.addr) {
626 + it->ClearHook();
627 + break;
628 + }
629 + }
630 + }
631 + // Insert hook and flush instruction cache.
632 + enum {c8 = 0xcccccccc};
633 + DWORD int3[] = {c8, c8};
634 + DWORD t = 0x100,
635 + old,
636 + len;
637 + // jichi 9/27/2013: Overwrite the memory with inst
638 + // See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Memory%20Management/Virtual%20Memory/NtProtectVirtualMemory.html
639 + // See: http://doxygen.reactos.org/d8/d6b/ndk_2mmfuncs_8h_af942709e0c57981d84586e74621912cd.html
640 + DWORD addr = hp.addr;
641 + NtProtectVirtualMemory(NtCurrentProcess(), (PVOID *)&addr, &t, PAGE_EXECUTE_READWRITE, &old);
642 + NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.addr, inst, 5, &t);
643 + len = hp.recover_len - 5;
644 + if (len)
645 + NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.addr + 5, int3, len, &t);
646 + NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
647 + NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)::hookman, 0x1000);
648 + //ConsoleOutput("vnrcli:UnsafeInsertHookCode: leave: succeed");
649 + return 0;
650 +}
651 +
652 +int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind,
653 + DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
654 +{
655 + NtWaitForSingleObject(hmMutex, 0, 0);
656 + hp.addr = (DWORD)addr;
657 + hp.off = data;
658 + hp.ind = data_ind;
659 + hp.split = split_off;
660 + hp.split_ind = split_ind;
661 + hp.type = type;
662 + hp.hook_len = 0;
663 + hp.module = 0;
664 + hp.length_offset = len_off & 0xffff;
665 + current_hook++;
666 + if (current_available >= this)
667 + for (current_available = this + 1; current_available->Address(); current_available++);
668 + IthReleaseMutex(hmMutex);
669 + return this - hookman;
670 +}
671 +
672 +int TextHook::InitHook(const HookParam &h, LPCWSTR name, WORD set_flag)
673 +{
674 + NtWaitForSingleObject(hmMutex, 0, 0);
675 + hp = h;
676 + hp.type |= set_flag;
677 + if (name && name != hook_name) {
678 + SetHookName(name);
679 + }
680 + current_hook++;
681 + current_available = this+1;
682 + while (current_available->Address())
683 + current_available++;
684 + IthReleaseMutex(hmMutex);
685 + return 1;
686 +}
687 +
688 +int TextHook::RemoveHook()
689 +{
690 + enum : int { yes = 1, no = 0 };
691 + if (!hp.addr)
692 + return no;
693 + ConsoleOutput("vnrcli:RemoveHook: enter");
694 + const LONGLONG timeout = -50000000; // jichi 9/28/2012: in 100ns, wait at most for 5 seconds
695 + NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout);
696 + DWORD l = hp.hook_len;
697 + //with_seh({ // jichi 9/17/2013: might crash ><
698 + // jichi 12/25/2013: Actually, __try cannot catch such kind of exception
699 + ITH_TRY {
700 + NtWriteVirtualMemory(NtCurrentProcess(), (LPVOID)hp.addr, original, hp.recover_len, &l);
701 + NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
702 + } ITH_EXCEPT {}
703 + //});
704 + hp.hook_len = 0;
705 + IthReleaseMutex(hmMutex);
706 + ConsoleOutput("vnrcli:RemoveHook: leave");
707 + return yes;
708 +}
709 +
710 +int TextHook::ClearHook()
711 +{
712 + NtWaitForSingleObject(hmMutex, 0, 0);
713 + int err = RemoveHook();
714 + if (hook_name) {
715 + delete[] hook_name;
716 + hook_name = nullptr;
717 + }
718 + memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH
719 + //if (current_available>this)
720 + // current_available = this;
721 + current_hook--;
722 + IthReleaseMutex(hmMutex);
723 + return err;
724 +}
725 +
726 +int TextHook::ModifyHook(const HookParam &hp)
727 +{
728 + //WCHAR name[0x40];
729 + DWORD len = 0;
730 + if (hook_name)
731 + len = wcslen(hook_name);
732 + LPWSTR name = 0;
733 + if (len) {
734 + name = new wchar_t[len + 1];
735 + //ITH_MEMSET_HEAP(name, 0, sizeof(wchar_t) * (len + 1)); // jichi 9/26/2013: zero memory
736 + name[len] = 0;
737 + wcscpy(name, hook_name);
738 + }
739 + ClearHook();
740 + InitHook(hp,name);
741 + InsertHook();
742 + if (name)
743 + delete[] name;
744 + return 0;
745 +}
746 +
747 +int TextHook::RecoverHook()
748 +{
749 + if (hp.addr) {
750 + // jichi 9/28/2013: Only enable TextOutA to debug Cross Channel
751 + //if (hp.addr == (DWORD)TextOutA)
752 + InsertHook();
753 + return 1;
754 + }
755 + return 0;
756 +}
757 +
758 +int TextHook::SetHookName(LPCWSTR name)
759 +{
760 + name_length = wcslen(name) + 1;
761 + if (hook_name)
762 + delete[] hook_name;
763 + hook_name = new wchar_t[name_length];
764 + //ITH_MEMSET_HEAP(hook_name, 0, sizeof(wchar_t) * name_length); // jichi 9/26/2013: zero memory
765 + hook_name[name_length - 1] = 0;
766 + wcscpy(hook_name, name);
767 + return 0;
768 +}
769 +
770 +int TextHook::GetLength(DWORD base, DWORD in)
771 +{
772 + if (base == 0)
773 + return 0;
774 + int len;
775 + switch (hp.length_offset) {
776 + default: // jichi 12/26/2013: I should not put this default branch to the end
777 + len = *((int *)base + hp.length_offset);
778 + if (len >= 0) {
779 + if (hp.type & USING_UNICODE)
780 + len <<= 1;
781 + break;
782 + }
783 + else if (len != -1)
784 + break;
785 + //len == -1 then continue to case 0.
786 + case 0:
787 + if (hp.type & USING_UNICODE)
788 + len = wcslen((const wchar_t *)in) << 1;
789 + else
790 + len = strlen((const char *)in);
791 + break;
792 + case 1:
793 + if (hp.type & USING_UNICODE)
794 + len = 2;
795 + else {
796 + if (hp.type & BIG_ENDIAN)
797 + in >>= 8;
798 + len = LeadByteTable[in & 0xff]; //Slightly faster than IsDBCSLeadByte
799 + }
800 + break;
801 + }
802 + // jichi 12/25/2013: This function originally return -1 if failed
803 + //return len;
804 + return max(0, len);
805 +}
806 +
807 +// EOF
808 +
809 +//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
810 +
811 +/*
812 +DWORD recv_esp, recv_addr;
813 +EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
814 + void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
815 +{
816 + //WCHAR str[0x40],
817 + // name[0x100];
818 + //ConsoleOutput(L"Exception raised during hook processing.");
819 + //swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
820 + //ConsoleOutput(str);
821 + //MEMORY_BASIC_INFORMATION info;
822 + //if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
823 + // MemoryBasicInformation,&info,sizeof(info),0)) &&
824 + // NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
825 + // MemorySectionName,name,0x200,0))) {
826 + // swprintf(str, L"Exception offset: 0x%.8X:%s",
827 + // ContextRecord->Eip-(DWORD)info.AllocationBase,
828 + // wcsrchr(name,L'\\')+1);
829 + // ConsoleOutput(str);
830 + //}
831 + ContextRecord->Esp = recv_esp;
832 + ContextRecord->Eip = recv_addr;
833 + return ExceptionContinueExecution;
834 +}
835 +
836 +
837 +//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
838 +
839 +DWORD recv_esp, recv_addr;
840 +EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
841 + void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
842 +{
843 + //WCHAR str[0x40],
844 + // name[0x100];
845 + //ConsoleOutput(L"Exception raised during hook processing.");
846 + //swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
847 + //ConsoleOutput(str);
848 + //MEMORY_BASIC_INFORMATION info;
849 + //if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
850 + // MemoryBasicInformation,&info,sizeof(info),0)) &&
851 + // NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
852 + // MemorySectionName,name,0x200,0))) {
853 + // swprintf(str, L"Exception offset: 0x%.8X:%s",
854 + // ContextRecord->Eip-(DWORD)info.AllocationBase,
855 + // wcsrchr(name,L'\\')+1);
856 + // ConsoleOutput(str);
857 + //}
858 + ContextRecord->Esp = recv_esp;
859 + ContextRecord->Eip = recv_addr;
860 + return ExceptionContinueExecution;
861 +}
862 +
863 +__declspec(naked) // jichi 10/2/2013: No prolog and epilog
864 +int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
865 +{
866 + __asm
867 + {
868 + mov eax,seh_recover
869 + mov recv_addr,eax
870 + push ExceptHandler
871 + push fs:[0]
872 + mov recv_esp,esp
873 + mov fs:[0],esp
874 + push esi
875 + push edx
876 + call TextHook::Send
877 + test eax,eax
878 + jz seh_recover
879 + mov ecx,SafeExit
880 + mov [esp + 0x8], ecx // change exit point
881 +seh_recover:
882 + pop dword ptr fs:[0]
883 + pop ecx
884 + retn
885 + }
886 +}
887 +*/