mireado

update to 3.5640.1

update to ITHVNR 3.5640.1
and translation
Showing 140 changed files with 18351 additions and 1108 deletions
1 -# common.pri
2 -# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
3 -
4 -# config.pri
5 -# win32 {
6 -# DEFINES += _SECURE_SCL=0 _SCL_SECURE_NO_WARNINGS
7 -# DEFINES += _CRT_SECURE_NO_WARNINGS
8 -# QMAKE_CXXFLAGS += -wd4819
9 -# }
10 -
11 -# config.pri
12 -# win32 {
13 -# CONFIG(nocrt) {
14 -# message(CONFIG nocrt)
15 -# QMAKE_CFLAGS -= /MD /MDd
16 -# QMAKE_CFLAGS_DEBUG -= /MD /MDd
17 -# QMAKE_CFLAGS_RELEASE -= /MD /MDd
18 -# QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO -= /MD /MDd
19 -# QMAKE_CXXFLAGS -= /MD /MDd
20 -# QMAKE_CXXFLAGS_DEBUG -= /MD /MDd
21 -# QMAKE_CXXFLAGS_RELEASE -= /MD /MDd
22 -# QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= /MD /MDd
23 -# }
24 -# CONFIG(eha) {
25 -# message(CONFIG eha)
26 -# QMAKE_CXXFLAGS_STL_ON -= /EHsc
27 -# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
28 -# QMAKE_CXXFLAGS_STL_ON += /EHa
29 -# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
30 -# }
31 -# CONFIG(noeh) {
32 -# message(CONFIG noeh)
33 -# QMAKE_CXXFLAGS += /GR-
34 -# QMAKE_CXXFLAGS_RTTI_ON -= /GR
35 -# QMAKE_CXXFLAGS_STL_ON -= /EHsc
36 -# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
37 -# CONFIG(dll) {
38 -# QMAKE_LFLAGS += /ENTRY:"DllMain"
39 -# }
40 -# }
41 -# CONFIG(nosafeseh) {
42 -# message(CONFIG nosafeseh)
43 -# QMAKE_LFLAGS += -safeseh:no
44 -# }
45 -# }
46 -
47 -# dllconfig.pri
48 -# win32 {
49 -# CONFIG(eh): DEFINES += ITH_HAS_SEH
50 -# CONFIG(noeh): DEFINES -= ITH_HAS_SEH
51 -# }
52 -
53 cmake_minimum_required(VERSION 2.8) 1 cmake_minimum_required(VERSION 2.8)
54 2
55 set(CMAKE_CONFIGURATION_TYPES Debug Release) 3 set(CMAKE_CONFIGURATION_TYPES Debug Release)
...@@ -66,10 +14,11 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release") ...@@ -66,10 +14,11 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release")
66 14
67 set(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION ON) 15 set(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION ON)
68 16
17 +
69 set(CPACK_GENERATOR "ZIP") 18 set(CPACK_GENERATOR "ZIP")
70 set(CPACK_PACKAGE_VERSION_MAJOR 3) 19 set(CPACK_PACKAGE_VERSION_MAJOR 3)
71 -set(CPACK_PACKAGE_VERSION_MINOR 4152) 20 +set(CPACK_PACKAGE_VERSION_MINOR 5640)
72 -set(CPACK_PACKAGE_VERSION_PATCH 0) 21 +set(CPACK_PACKAGE_VERSION_PATCH 1)
73 set(CPACK_SOURCE_GENERATOR "ZIP") 22 set(CPACK_SOURCE_GENERATOR "ZIP")
74 set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\.svn/;/\\\\.bzr/;/\\\\.hg/;/\\\\.git/;\\\\.swp$;\\\\.#;/#" ".*\\\\.user$" "\\\\.gitignore$" "\\\\.gitmodules$" "\\\\.git$") 23 set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\.svn/;/\\\\.bzr/;/\\\\.hg/;/\\\\.git/;\\\\.swp$;\\\\.#;/#" ".*\\\\.user$" "\\\\.gitignore$" "\\\\.gitmodules$" "\\\\.git$")
75 include(CPack) 24 include(CPack)
...@@ -84,24 +33,20 @@ add_compile_options( ...@@ -84,24 +33,20 @@ add_compile_options(
84 ) 33 )
85 34
86 add_definitions( 35 add_definitions(
87 - -D_SECURE_SCL=0 # config.pri 36 + /D_SECURE_SCL=0 # config.pri
88 - -D_SCL_SECURE_NO_WARNINGS # config.pri 37 + /D_SCL_SECURE_NO_WARNINGS # config.pri
89 - -D_CRT_SECURE_NO_WARNINGS # config.pri 38 + /D_CRT_SECURE_NO_WARNINGS # config.pri
90 - -DUNICODE # config.pri 39 + /DUNICODE # config.pri
91 - -D_UNICODE 40 + /D_UNICODE
92 - -D_CRT_NON_CONFORMING_SWPRINTFS # common.pri 41 + /D_CRT_NON_CONFORMING_SWPRINTFS # common.pri
93 - -DITH_HAS_CRT 42 + /DITH_HAS_CRT
94 ) 43 )
95 44
96 -include_directories(${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/vnr ${CMAKE_BINARY_DIR}/gui) 45 +include_directories(
97 - 46 + .
98 -set(common_src 47 + vnr
99 - vnr/ith/common/const.h 48 + vnr/texthook
100 - vnr/ith/common/defs.h 49 + ${CMAKE_BINARY_DIR}/gui
101 - vnr/ith/common/except.h
102 - vnr/ith/common/growl.h
103 - vnr/ith/common/memory.h
104 - vnr/ith/common/types.h
105 ) 50 )
106 51
107 set(resource_src 52 set(resource_src
...@@ -119,13 +64,8 @@ set(ithvnr_src ...@@ -119,13 +64,8 @@ set(ithvnr_src
119 gui/main.cpp 64 gui/main.cpp
120 gui/ProcessWindow.cpp 65 gui/ProcessWindow.cpp
121 gui/ProcessWindow.h 66 gui/ProcessWindow.h
122 - gui/Profile.cpp
123 - gui/Profile.h
124 gui/ProfileManager.cpp 67 gui/ProfileManager.cpp
125 gui/ProfileManager.h 68 gui/ProfileManager.h
126 - gui/pugiconfig.hpp
127 - gui/pugixml.cpp
128 - gui/pugixml.hpp
129 gui/resource.h 69 gui/resource.h
130 gui/utility.cpp 70 gui/utility.cpp
131 gui/utility.h 71 gui/utility.h
...@@ -135,17 +75,15 @@ set(ithvnr_src ...@@ -135,17 +75,15 @@ set(ithvnr_src
135 gui/window.h 75 gui/window.h
136 gui/TextBuffer.cpp 76 gui/TextBuffer.cpp
137 gui/TextBuffer.h 77 gui/TextBuffer.h
138 - ${common_src}
139 ${resource_src} 78 ${resource_src}
140 ) 79 )
141 80
142 -source_group("common" FILES ${common_src})
143 -
144 source_group("Resource Files" FILES ${resource_src}) 81 source_group("Resource Files" FILES ${resource_src})
145 82
146 add_executable(${PROJECT_NAME} ${ithvnr_src}) 83 add_executable(${PROJECT_NAME} ${ithvnr_src})
147 84
148 add_subdirectory(vnr) 85 add_subdirectory(vnr)
86 +# add_subdirectory(profile)
149 87
150 set_target_properties(${PROJECT_NAME} PROPERTIES 88 set_target_properties(${PROJECT_NAME} PROPERTIES
151 LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFESTDEPENDENCY:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"" 89 LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFESTDEPENDENCY:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\""
...@@ -158,8 +96,9 @@ target_compile_definitions(${PROJECT_NAME} ...@@ -158,8 +96,9 @@ target_compile_definitions(${PROJECT_NAME}
158 ) 96 )
159 97
160 target_link_libraries(${PROJECT_NAME} 98 target_link_libraries(${PROJECT_NAME}
99 + profile
161 vnrhost 100 vnrhost
162 - vnrsys 101 + ithsys
163 ${WDK_HOME}/lib/wxp/i386/ntdll.lib 102 ${WDK_HOME}/lib/wxp/i386/ntdll.lib
164 comctl32.lib 103 comctl32.lib
165 psapi.lib 104 psapi.lib
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
16 */ 16 */
17 17
18 #pragma once 18 #pragma once
19 +
19 #include "ITH.h" 20 #include "ITH.h"
20 21
21 typedef void (*CustomFilterCallBack) (WORD, PVOID); 22 typedef void (*CustomFilterCallBack) (WORD, PVOID);
......
...@@ -15,7 +15,8 @@ ...@@ -15,7 +15,8 @@
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 #pragma once 17 #pragma once
18 -#include <windows.h> 18 +
19 +#include <Windows.h>
19 #include <string> 20 #include <string>
20 #include <sstream> 21 #include <sstream>
21 #include <ios> 22 #include <ios>
...@@ -29,9 +30,8 @@ ...@@ -29,9 +30,8 @@
29 #include <map> 30 #include <map>
30 #include <CommCtrl.h> 31 #include <CommCtrl.h>
31 #include <intrin.h> 32 #include <intrin.h>
32 -#include <WindowsX.h>
33 #include <sstream> 33 #include <sstream>
34 #include <regex> 34 #include <regex>
35 #include <set> 35 #include <set>
36 -#include "pugixml.hpp" 36 +#include "profile/pugixml.hpp"
37 #pragma warning(disable: 4146) 37 #pragma warning(disable: 4146)
......
1 -// Generated by ResEdit 1.6.3 1 +// Generated by ResEdit 1.6.5
2 -// Copyright (C) 2006-2014 2 +// Copyright (C) 2006-2015
3 // http://www.resedit.net 3 // http://www.resedit.net
4 4
5 #include <windows.h> 5 #include <windows.h>
...@@ -7,9 +7,6 @@ ...@@ -7,9 +7,6 @@
7 #include <richedit.h> 7 #include <richedit.h>
8 #include "resource.h" 8 #include "resource.h"
9 9
10 -#define WC_LISTVIEW L"SysListView32"
11 -
12 -
13 10
14 11
15 12
...@@ -22,14 +19,14 @@ STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYS ...@@ -22,14 +19,14 @@ STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYS
22 CAPTION "Process Explorer" 19 CAPTION "Process Explorer"
23 FONT 8, "MS Shell Dlg", 400, 0, 1 20 FONT 8, "MS Shell Dlg", 400, 0, 1
24 { 21 {
25 - DEFPUSHBUTTON "확인", IDOK, 281, 189, 53, 14, 0, WS_EX_LEFT 22 + DEFPUSHBUTTON "확인", IDOK, 281, 189, 53, 14, 0, WS_EX_LEFT
26 - PUSHBUTTON "프로필 제거", IDC_BUTTON6, 226, 189, 53, 14, 0, WS_EX_LEFT 23 + PUSHBUTTON "프로필 제거", IDC_BUTTON6, 226, 189, 53, 14, 0, WS_EX_LEFT
27 - CONTROL "", IDC_LIST1, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_ALIGNLEFT | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT, 7, 20, 327, 164, WS_EX_LEFT 24 + CONTROL "", IDC_LIST1, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_ALIGNLEFT | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT, 7, 20, 327, 164, WS_EX_LEFT
28 - LTEXT "프로세스", IDC_STATIC, 7, 7, 65, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT 25 + LTEXT "프로세스", IDC_STATIC, 7, 7, 65, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
29 - PUSHBUTTON "부착", IDC_BUTTON2, 61, 189, 53, 14, 0, WS_EX_LEFT 26 + PUSHBUTTON "부착", IDC_BUTTON2, 61, 189, 53, 14, 0, WS_EX_LEFT
30 - PUSHBUTTON "탈착", IDC_BUTTON3, 116, 189, 53, 14, 0, WS_EX_LEFT 27 + PUSHBUTTON "탈착", IDC_BUTTON3, 116, 189, 53, 14, 0, WS_EX_LEFT
31 - PUSHBUTTON "프로필 추가", IDC_BUTTON5, 171, 189, 53, 14, 0, WS_EX_LEFT 28 + PUSHBUTTON "프로필 추가", IDC_BUTTON5, 171, 189, 53, 14, 0, WS_EX_LEFT
32 - PUSHBUTTON "새로고침", IDC_BUTTON1, 7, 189, 53, 14, 0, WS_EX_LEFT 29 + PUSHBUTTON "새로고침", IDC_BUTTON1, 7, 189, 53, 14, 0, WS_EX_LEFT
33 } 30 }
34 31
35 32
...@@ -40,22 +37,22 @@ STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYS ...@@ -40,22 +37,22 @@ STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYS
40 CAPTION "Option" 37 CAPTION "Option"
41 FONT 8, "MS Shell Dlg", 400, 0, 1 38 FONT 8, "MS Shell Dlg", 400, 0, 1
42 { 39 {
43 - DEFPUSHBUTTON "확인", IDOK, 8, 164, 50, 14, 0, WS_EX_LEFT 40 + DEFPUSHBUTTON "확인", IDOK, 8, 164, 50, 14, 0, WS_EX_LEFT
44 - PUSHBUTTON "취소", IDCANCEL, 65, 164, 50, 14, 0, WS_EX_LEFT 41 + PUSHBUTTON "취소", IDCANCEL, 65, 164, 50, 14, 0, WS_EX_LEFT
45 - EDITTEXT IDC_EDIT1, 60, 7, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT 42 + EDITTEXT IDC_EDIT1, 60, 7, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
46 - LTEXT "문단나누기", IDC_STATIC, 7, 7, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT 43 + LTEXT "문단나누기", IDC_STATIC, 7, 7, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
47 - EDITTEXT IDC_EDIT2, 60, 25, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT 44 + EDITTEXT IDC_EDIT2, 60, 25, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
48 - LTEXT "프로세스 대기", IDC_STATIC, 7, 26, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT 45 + LTEXT "프로세스 대기", IDC_STATIC, 7, 26, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
49 - EDITTEXT IDC_EDIT3, 60, 45, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT 46 + EDITTEXT IDC_EDIT3, 60, 45, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
50 - LTEXT "인젝션 대기", IDC_STATIC, 7, 45, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT 47 + LTEXT "인젝션 대기", IDC_STATIC, 7, 45, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
51 - EDITTEXT IDC_EDIT4, 60, 65, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT 48 + EDITTEXT IDC_EDIT4, 60, 65, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
52 - LTEXT "삽입 대기", IDC_STATIC, 7, 65, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT 49 + LTEXT "삽입 대기", IDC_STATIC, 7, 65, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
53 - AUTOCHECKBOX "자동 부착", IDC_CHECK1, 7, 87, 54, 10, 0, WS_EX_LEFT 50 + AUTOCHECKBOX "자동 부착", IDC_CHECK1, 7, 87, 54, 10, 0, WS_EX_LEFT
54 - AUTOCHECKBOX "자동 삽입", IDC_CHECK2, 62, 87, 50, 10, 0, WS_EX_LEFT 51 + AUTOCHECKBOX "자동 삽입", IDC_CHECK2, 62, 87, 50, 10, 0, WS_EX_LEFT
55 - AUTOCHECKBOX "클립보드로 자동 복사", IDC_CHECK3, 7, 103, 88, 10, 0, WS_EX_LEFT 52 + AUTOCHECKBOX "클립보드로 자동 복사", IDC_CHECK3, 7, 103, 88, 10, 0, WS_EX_LEFT
56 - AUTOCHECKBOX "자동 반복문 제거", IDC_CHECK4, 7, 119, 95, 10, 0, WS_EX_LEFT 53 + AUTOCHECKBOX "자동 반복문 제거", IDC_CHECK4, 7, 119, 95, 10, 0, WS_EX_LEFT
57 - AUTOCHECKBOX "문자필터 초기화", IDC_CHECK6, 7, 149, 81, 8, 0, WS_EX_LEFT 54 + AUTOCHECKBOX "문자필터 초기화", IDC_CHECK6, 7, 149, 81, 8, 0, WS_EX_LEFT
58 - AUTOCHECKBOX "글로벌 필터 활성화", IDC_CHECK5, 7, 134, 75, 10, 0, WS_EX_LEFT 55 + AUTOCHECKBOX "글로벌 필터 활성화", IDC_CHECK5, 7, 134, 75, 10, 0, WS_EX_LEFT
59 } 56 }
60 57
61 58
...@@ -65,3 +62,21 @@ FONT 8, "MS Shell Dlg", 400, 0, 1 ...@@ -65,3 +62,21 @@ FONT 8, "MS Shell Dlg", 400, 0, 1
65 // 62 //
66 LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN 63 LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN
67 IDI_ICON1 ICON "icon1.ico" 64 IDI_ICON1 ICON "icon1.ico"
65 +
66 +
67 +
68 +//
69 +// Version Information resources
70 +//
71 +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
72 +1 VERSIONINFO
73 + FILEVERSION 0,0,0,0
74 + PRODUCTVERSION 0,0,0,0
75 + FILEOS VOS_UNKNOWN
76 + FILETYPE VFT_UNKNOWN
77 + FILESUBTYPE VFT2_UNKNOWN
78 + FILEFLAGSMASK 0
79 + FILEFLAGS 0
80 +{
81 +
82 +}
......
1 #include "ProcessWindow.h" 1 #include "ProcessWindow.h"
2 #include "resource.h" 2 #include "resource.h"
3 -#include "ith/host/srv.h" 3 +#include "host/host.h"
4 -#include "ith/host/hookman.h" 4 +#include "host/hookman.h"
5 #include "ProfileManager.h" 5 #include "ProfileManager.h"
6 -#include "Profile.h" 6 +#include "profile/Profile.h"
7 7
8 extern HookManager* man; // main.cpp 8 extern HookManager* man; // main.cpp
9 extern ProfileManager* pfman; // ProfileManager.cpp 9 extern ProfileManager* pfman; // ProfileManager.cpp
...@@ -22,6 +22,8 @@ ProcessWindow::ProcessWindow(HWND hDialog) : hDlg(hDialog) ...@@ -22,6 +22,8 @@ ProcessWindow::ProcessWindow(HWND hDialog) : hDlg(hDialog)
22 ListView_SetExtendedListViewStyleEx(hlProcess, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); 22 ListView_SetExtendedListViewStyleEx(hlProcess, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
23 InitProcessDlg(); 23 InitProcessDlg();
24 RefreshProcess(); 24 RefreshProcess();
25 + EnableWindow(hbDetach, FALSE);
26 + EnableWindow(hbAttach, FALSE);
25 } 27 }
26 28
27 void ProcessWindow::InitProcessDlg() 29 void ProcessWindow::InitProcessDlg()
...@@ -73,30 +75,33 @@ void ProcessWindow::RefreshProcess() ...@@ -73,30 +75,33 @@ void ProcessWindow::RefreshProcess()
73 void ProcessWindow::AttachProcess() 75 void ProcessWindow::AttachProcess()
74 { 76 {
75 DWORD pid = GetSelectedPID(); 77 DWORD pid = GetSelectedPID();
76 - if (IHF_InjectByPID(pid) != -1) 78 + if (Host_InjectByPID(pid))
79 + {
80 + Host_HijackProcess(pid);
77 RefreshThreadWithPID(pid, true); 81 RefreshThreadWithPID(pid, true);
82 + }
78 } 83 }
79 84
80 void ProcessWindow::DetachProcess() 85 void ProcessWindow::DetachProcess()
81 { 86 {
82 DWORD pid = GetSelectedPID(); 87 DWORD pid = GetSelectedPID();
83 - if (IHF_ActiveDetachProcess(pid) == 0) 88 + if (Host_ActiveDetachProcess(pid) == 0)
84 RefreshThreadWithPID(pid, false); 89 RefreshThreadWithPID(pid, false);
85 } 90 }
86 91
87 -void ProcessWindow::AddCurrentToProfile() 92 +void ProcessWindow::CreateProfileForSelectedProcess()
88 { 93 {
89 DWORD pid = GetSelectedPID(); 94 DWORD pid = GetSelectedPID();
90 auto path = GetProcessPath(pid); 95 auto path = GetProcessPath(pid);
91 if (!path.empty()) 96 if (!path.empty())
92 { 97 {
93 - Profile* pf = pfman->AddProfile(path, pid); 98 + Profile* pf = pfman->CreateProfile(pid);
94 - pfman->FindProfileAndUpdateHookAddresses(pid, path); 99 + pfman->SaveProfiles();
95 RefreshThread(ListView_GetSelectionMark(hlProcess)); 100 RefreshThread(ListView_GetSelectionMark(hlProcess));
96 } 101 }
97 } 102 }
98 103
99 -void ProcessWindow::RemoveCurrentFromProfile() 104 +void ProcessWindow::DeleteProfileForSelectedProcess()
100 { 105 {
101 DWORD pid = GetSelectedPID(); 106 DWORD pid = GetSelectedPID();
102 auto path = GetProcessPath(pid); 107 auto path = GetProcessPath(pid);
...@@ -132,7 +137,7 @@ void ProcessWindow::RefreshThreadWithPID(DWORD pid, bool isAttached) ...@@ -132,7 +137,7 @@ void ProcessWindow::RefreshThreadWithPID(DWORD pid, bool isAttached)
132 137
133 DWORD ProcessWindow::GetSelectedPID() 138 DWORD ProcessWindow::GetSelectedPID()
134 { 139 {
135 - LVITEM item={}; 140 + LVITEM item = {};
136 item.mask = LVIF_PARAM; 141 item.mask = LVIF_PARAM;
137 item.iItem = ListView_GetSelectionMark(hlProcess); 142 item.iItem = ListView_GetSelectionMark(hlProcess);
138 ListView_GetItem(hlProcess, &item); 143 ListView_GetItem(hlProcess, &item);
......
1 #pragma once 1 #pragma once
2 +
2 #include "ITH.h" 3 #include "ITH.h"
3 4
4 class ProcessWindow 5 class ProcessWindow
...@@ -9,8 +10,8 @@ public: ...@@ -9,8 +10,8 @@ public:
9 void RefreshProcess(); 10 void RefreshProcess();
10 void AttachProcess(); 11 void AttachProcess();
11 void DetachProcess(); 12 void DetachProcess();
12 - void AddCurrentToProfile(); 13 + void CreateProfileForSelectedProcess();
13 - void RemoveCurrentFromProfile(); 14 + void DeleteProfileForSelectedProcess();
14 void RefreshThread(int index); 15 void RefreshThread(int index);
15 private: 16 private:
16 void RefreshThreadWithPID(DWORD pid, bool isAttached); 17 void RefreshThreadWithPID(DWORD pid, bool isAttached);
......
1 #include "ProfileManager.h" 1 #include "ProfileManager.h"
2 -#include "Profile.h" 2 +#include "profile/Profile.h"
3 -#include "ith/host/srv.h" 3 +#include "host/host.h"
4 -#include "ith/host/hookman.h" 4 +#include "host/hookman.h"
5 -#include "ith/common/types.h" 5 +#include "vnrhook/include/types.h"
6 -#include "ith/common/const.h" 6 +#include "vnrhook/include/const.h"
7 +#include "utility.h"
8 +#include "profile/misc.h"
7 9
8 extern HookManager* man; // main.cpp 10 extern HookManager* man; // main.cpp
9 extern LONG auto_inject, auto_insert, inject_delay; // main.cpp 11 extern LONG auto_inject, auto_insert, inject_delay; // main.cpp
...@@ -12,21 +14,16 @@ bool MonitorFlag; ...@@ -12,21 +14,16 @@ bool MonitorFlag;
12 ProfileManager* pfman; 14 ProfileManager* pfman;
13 15
14 DWORD WINAPI MonitorThread(LPVOID lpThreadParameter); 16 DWORD WINAPI MonitorThread(LPVOID lpThreadParameter);
15 -void AddHooksToProfile(Profile& pf, const ProcessRecord& pr);
16 -void AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid);
17 -DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread& thread);
18 -void MakeHookRelative(const ProcessRecord& pr, HookParam& hp);
19 -std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address);
20 -void GetHookNameToAddressMap(const ProcessRecord& pr, std::map<std::wstring, DWORD>& hookNameToAddress);
21 17
22 -ProfileManager::ProfileManager(): 18 +ProfileManager::ProfileManager() :
23 hMonitorThread(IthCreateThread(MonitorThread, 0)) 19 hMonitorThread(IthCreateThread(MonitorThread, 0))
24 { 20 {
25 - LoadProfile(); 21 + LoadProfiles();
26 } 22 }
23 +
27 ProfileManager::~ProfileManager() 24 ProfileManager::~ProfileManager()
28 { 25 {
29 - SaveProfile(); 26 + SaveProfiles();
30 WaitForSingleObject(hMonitorThread.get(), 0); 27 WaitForSingleObject(hMonitorThread.get(), 0);
31 } 28 }
32 29
...@@ -42,7 +39,7 @@ Profile* ProfileManager::GetProfile(DWORD pid) ...@@ -42,7 +39,7 @@ Profile* ProfileManager::GetProfile(DWORD pid)
42 return NULL; 39 return NULL;
43 } 40 }
44 41
45 -bool ProfileManager::AddProfile(pugi::xml_node game) 42 +bool ProfileManager::CreateProfile(pugi::xml_node game)
46 { 43 {
47 auto file = game.child(L"File"); 44 auto file = game.child(L"File");
48 auto profile = game.child(L"Profile"); 45 auto profile = game.child(L"Profile");
...@@ -56,13 +53,17 @@ bool ProfileManager::AddProfile(pugi::xml_node game) ...@@ -56,13 +53,17 @@ bool ProfileManager::AddProfile(pugi::xml_node game)
56 auto pf = new Profile(title); 53 auto pf = new Profile(title);
57 if (!pf->XmlReadProfile(profile)) 54 if (!pf->XmlReadProfile(profile))
58 return false; 55 return false;
59 - AddProfile(path.value(), profile_ptr(pf)); 56 + CSLock lock(cs);
60 - return true; 57 + auto& oldProfile = profile_tree[path.value()];
58 + if (!oldProfile)
59 + oldProfile.swap(profile_ptr(pf));
60 + return true;
61 } 61 }
62 62
63 -Profile* ProfileManager::AddProfile(const std::wstring& path, DWORD pid) 63 +Profile* ProfileManager::CreateProfile(DWORD pid)
64 { 64 {
65 CSLock lock(cs); 65 CSLock lock(cs);
66 + auto path = GetProcessPath(pid);
66 auto& pf = profile_tree[path]; 67 auto& pf = profile_tree[path];
67 if (!pf) 68 if (!pf)
68 { 69 {
...@@ -72,15 +73,6 @@ Profile* ProfileManager::AddProfile(const std::wstring& path, DWORD pid) ...@@ -72,15 +73,6 @@ Profile* ProfileManager::AddProfile(const std::wstring& path, DWORD pid)
72 return pf.get(); 73 return pf.get();
73 } 74 }
74 75
75 -Profile* ProfileManager::AddProfile(const std::wstring& path, profile_ptr new_profile)
76 -{
77 - CSLock lock(cs);
78 - auto& pf = profile_tree[path];
79 - if (!pf)
80 - pf.swap(new_profile);
81 - return pf.get();
82 -}
83 -
84 void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node root) 76 void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node root)
85 { 77 {
86 auto game = root.append_child(L"Game"); 78 auto game = root.append_child(L"Game");
...@@ -96,7 +88,7 @@ void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi ...@@ -96,7 +88,7 @@ void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi
96 } 88 }
97 } 89 }
98 90
99 -void ProfileManager::LoadProfile() 91 +void ProfileManager::LoadProfiles()
100 { 92 {
101 pugi::xml_document doc; 93 pugi::xml_document doc;
102 UniqueHandle hFile(IthCreateFile(L"ITH_Profile.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING)); 94 UniqueHandle hFile(IthCreateFile(L"ITH_Profile.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
...@@ -112,10 +104,10 @@ void ProfileManager::LoadProfile() ...@@ -112,10 +104,10 @@ void ProfileManager::LoadProfile()
112 if (!root) 104 if (!root)
113 return; 105 return;
114 for (auto game = root.begin(); game != root.end(); ++game) 106 for (auto game = root.begin(); game != root.end(); ++game)
115 - AddProfile(*game); 107 + CreateProfile(*game);
116 } 108 }
117 109
118 -void ProfileManager::SaveProfile() 110 +void ProfileManager::SaveProfiles()
119 { 111 {
120 pugi::xml_document doc; 112 pugi::xml_document doc;
121 auto root = doc.append_child(L"ITH_Profile"); 113 auto root = doc.append_child(L"ITH_Profile");
...@@ -138,44 +130,14 @@ void ProfileManager::DeleteProfile(const std::wstring& path) ...@@ -138,44 +130,14 @@ void ProfileManager::DeleteProfile(const std::wstring& path)
138 profile_tree.erase(profile_tree.find(path)); 130 profile_tree.erase(profile_tree.find(path));
139 } 131 }
140 132
141 -void ProfileManager::FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path) 133 +Profile* ProfileManager::GetProfile(const std::wstring& path)
142 { 134 {
143 if (path.empty()) 135 if (path.empty())
144 - return; 136 + return nullptr;
145 auto it = profile_tree.find(path); 137 auto it = profile_tree.find(path);
146 if (it == profile_tree.end()) 138 if (it == profile_tree.end())
147 - return; 139 + return nullptr;
148 - auto& pf = it->second; 140 + return it->second.get();
149 - const ProcessRecord* pr = man->GetProcessRecord(pid);
150 - if (pr == NULL)
151 - return;
152 - // hook name -> hook address
153 - std::map<std::wstring, DWORD> hookNameToAddress;
154 - GetHookNameToAddressMap(*pr, hookNameToAddress);
155 - for (auto thread_profile = pf->Threads().begin(); thread_profile != pf->Threads().end();
156 - ++thread_profile)
157 - {
158 - auto it = hookNameToAddress.find((*thread_profile)->HookName());
159 - if (it != hookNameToAddress.end())
160 - (*thread_profile)->HookAddress() = it->second;
161 - }
162 -}
163 -
164 -void GetHookNameToAddressMap(const ProcessRecord& pr,
165 - std::map<std::wstring, DWORD>& hookNameToAddress)
166 -{
167 - WaitForSingleObject(pr.hookman_mutex, 0);
168 - auto hooks = (const Hook*)pr.hookman_map;
169 - for (DWORD i = 0; i < MAX_HOOK; ++i)
170 - {
171 - if (hooks[i].Address() == 0)
172 - continue;
173 - auto& hook = hooks[i];
174 - std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]);
175 - if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength() * 2, NULL))
176 - hookNameToAddress[name.get()] = hook.Address();
177 - }
178 - ReleaseMutex(pr.hookman_mutex);
179 } 141 }
180 142
181 bool ProfileManager::HasProfile(const std::wstring& path) 143 bool ProfileManager::HasProfile(const std::wstring& path)
...@@ -183,7 +145,7 @@ bool ProfileManager::HasProfile(const std::wstring& path) ...@@ -183,7 +145,7 @@ bool ProfileManager::HasProfile(const std::wstring& path)
183 return profile_tree.find(path) != profile_tree.end(); 145 return profile_tree.find(path) != profile_tree.end();
184 } 146 }
185 147
186 -DWORD ProfileManager::ProfileCount() 148 +DWORD ProfileManager::CountProfiles()
187 { 149 {
188 return profile_tree.size(); 150 return profile_tree.size();
189 } 151 }
...@@ -194,7 +156,7 @@ DWORD WINAPI InjectThread(LPVOID lpThreadParameter) ...@@ -194,7 +156,7 @@ DWORD WINAPI InjectThread(LPVOID lpThreadParameter)
194 Sleep(inject_delay); 156 Sleep(inject_delay);
195 if (man == NULL) 157 if (man == NULL)
196 return 0; 158 return 0;
197 - DWORD status = IHF_InjectByPID(pid); 159 + DWORD status = Host_InjectByPID(pid);
198 if (!auto_insert) 160 if (!auto_insert)
199 return status; 161 return status;
200 if (status == -1) 162 if (status == -1)
...@@ -206,7 +168,10 @@ DWORD WINAPI InjectThread(LPVOID lpThreadParameter) ...@@ -206,7 +168,10 @@ DWORD WINAPI InjectThread(LPVOID lpThreadParameter)
206 SendParam sp; 168 SendParam sp;
207 sp.type = 0; 169 sp.type = 0;
208 for (auto hp = pf->Hooks().begin(); hp != pf->Hooks().end(); ++hp) 170 for (auto hp = pf->Hooks().begin(); hp != pf->Hooks().end(); ++hp)
209 - IHF_InsertHook(pid, const_cast<HookParam*>(&(*hp)->HP()), (*hp)->Name().c_str()); 171 + {
172 + std::string name = toMultiByteString((*hp)->Name());
173 + Host_InsertHook(pid, const_cast<HookParam*>(&(*hp)->HP()), name.c_str());
174 + }
210 } 175 }
211 return status; 176 return status;
212 } 177 }
...@@ -215,7 +180,7 @@ DWORD WINAPI MonitorThread(LPVOID lpThreadParameter) ...@@ -215,7 +180,7 @@ DWORD WINAPI MonitorThread(LPVOID lpThreadParameter)
215 { 180 {
216 while (MonitorFlag) 181 while (MonitorFlag)
217 { 182 {
218 - DWORD aProcesses[1024], cbNeeded, cProcesses; 183 + DWORD aProcesses[1024], cbNeeded, cProcesses;
219 if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded)) 184 if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
220 break; 185 break;
221 cProcesses = cbNeeded / sizeof(DWORD); 186 cProcesses = cbNeeded / sizeof(DWORD);
...@@ -237,116 +202,17 @@ DWORD WINAPI MonitorThread(LPVOID lpThreadParameter) ...@@ -237,116 +202,17 @@ DWORD WINAPI MonitorThread(LPVOID lpThreadParameter)
237 202
238 DWORD SaveProcessProfile(DWORD pid) 203 DWORD SaveProcessProfile(DWORD pid)
239 { 204 {
240 - const ProcessRecord* pr = man->GetProcessRecord(pid);
241 - if (pr == NULL)
242 - return 0;
243 std::wstring path = GetProcessPath(pid); 205 std::wstring path = GetProcessPath(pid);
244 if (path.empty()) 206 if (path.empty())
245 return 0; 207 return 0;
208 + pugi::xml_document doc;
209 + pugi::xml_node profile_node = doc.append_child(L"Profile");
210 + man->GetProfile(pid, profile_node);
246 Profile* pf = pfman->GetProfile(pid); 211 Profile* pf = pfman->GetProfile(pid);
247 if (pf != NULL) 212 if (pf != NULL)
248 pf->Clear(); 213 pf->Clear();
249 else 214 else
250 - pf = pfman->AddProfile(path, pid); 215 + pf = pfman->CreateProfile(pid);
251 - AddHooksToProfile(*pf, *pr); 216 + pf->XmlReadProfile(profile_node);
252 - AddThreadsToProfile(*pf, *pr, pid);
253 return 0; 217 return 0;
254 } 218 }
255 -
256 -void AddHooksToProfile(Profile& pf, const ProcessRecord& pr)
257 -{
258 - WaitForSingleObject(pr.hookman_mutex, 0);
259 - auto hooks = (const Hook*)pr.hookman_map;
260 - for (DWORD i = 0; i < MAX_HOOK; ++i)
261 - {
262 - if (hooks[i].Address() == 0)
263 - continue;
264 - auto& hook = hooks[i];
265 - DWORD type = hook.Type();
266 - if ((type & HOOK_ADDITIONAL) && (type & HOOK_ENGINE) == 0)
267 - {
268 - std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]);
269 - if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength() * 2, NULL))
270 - {
271 - if (hook.hp.module)
272 - {
273 - HookParam hp = hook.hp;
274 - MakeHookRelative(pr, hp);
275 - pf.AddHook(hp, name.get());
276 - }
277 - else
278 - pf.AddHook(hook.hp, name.get());
279 - }
280 - }
281 - }
282 - ReleaseMutex(pr.hookman_mutex);
283 -}
284 -
285 -void MakeHookRelative(const ProcessRecord& pr, HookParam& hp)
286 -{
287 - MEMORY_BASIC_INFORMATION info;
288 - VirtualQueryEx(pr.process_handle, (LPCVOID)hp.addr, &info, sizeof(info));
289 - hp.addr -= (DWORD)info.AllocationBase;
290 - hp.function = 0;
291 -}
292 -
293 -void AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid)
294 -{
295 - man->LockHookman();
296 - ThreadTable* table = man->Table();
297 - for (int i = 0; i < table->Used(); ++i)
298 - {
299 - TextThread* tt = table->FindThread(i);
300 - if (tt == NULL || tt->GetThreadParameter()->pid != pid)
301 - continue;
302 - //if (tt->Status() & CURRENT_SELECT || tt->Link() || tt->GetComment())
303 - if (tt->Status() & CURRENT_SELECT || tt->Link())
304 - AddThreadToProfile(pf, pr, *tt);
305 - }
306 - man->UnlockHookman();
307 -}
308 -
309 -DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread& thread)
310 -{
311 - const ThreadParameter* tp = thread.GetThreadParameter();
312 - std::wstring hook_name = GetHookNameByAddress(pr, tp->hook);
313 - if (hook_name.empty())
314 - return -1;
315 - auto thread_profile = new ThreadProfile(hook_name, tp->retn, tp->spl, 0, 0,
316 - THREAD_MASK_RETN | THREAD_MASK_SPLIT, L"");
317 - DWORD threads_size = pf.Threads().size();
318 - int thread_profile_index = pf.AddThread(thread_ptr(thread_profile));
319 - if (thread_profile_index == threads_size) // new thread
320 - {
321 - WORD iw = thread_profile_index & 0xFFFF;
322 - if (thread.Status() & CURRENT_SELECT)
323 - pf.SelectedIndex() = iw;
324 - if (thread.Link())
325 - {
326 - WORD to_index = AddThreadToProfile(pf, pr, *(thread.Link())) & 0xFFFF;
327 - if (iw >= 0)
328 - pf.AddLink(link_ptr(new LinkProfile(iw, to_index)));
329 - }
330 - }
331 - return thread_profile_index; // in case more than one thread links to the same thread.
332 -}
333 -
334 -std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address)
335 -{
336 - std::wstring hook_name;
337 - WaitForSingleObject(pr.hookman_mutex, 0);
338 - auto hooks = (const Hook*)pr.hookman_map;
339 - for (int i = 0; i < MAX_HOOK; ++i)
340 - {
341 - auto& hook = hooks[i];
342 - if (hook.Address() == hook_address)
343 - {
344 - std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]);
345 - if (ReadProcessMemory(pr.process_handle, hooks[i].Name(), name.get(), hook.NameLength() * 2, NULL))
346 - hook_name = name.get();
347 - break;
348 - }
349 - }
350 - ReleaseMutex(pr.hookman_mutex);
351 - return hook_name;
352 -}
......
1 #pragma once 1 #pragma once
2 +
2 #include "ITH.h" 3 #include "ITH.h"
3 #include "utility.h" // UniqueHandle, CriticalSection 4 #include "utility.h" // UniqueHandle, CriticalSection
4 5
...@@ -9,14 +10,14 @@ class ProfileManager ...@@ -9,14 +10,14 @@ class ProfileManager
9 public: 10 public:
10 ProfileManager(); 11 ProfileManager();
11 ~ProfileManager(); 12 ~ProfileManager();
12 - Profile* AddProfile(const std::wstring& path, DWORD pid); 13 + Profile* CreateProfile(DWORD pid);
13 - void DeleteProfile(const std::wstring& path); 14 + Profile* GetProfile(DWORD pid);
14 - void LoadProfile(); 15 + Profile* GetProfile(const std::wstring& path);
15 - void SaveProfile(); 16 + void LoadProfiles();
16 - void FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path); 17 + void SaveProfiles();
18 + void DeleteProfile(const std::wstring& path);
19 + void UpdateHookAddresses(DWORD pid);
17 bool HasProfile(const std::wstring& path); 20 bool HasProfile(const std::wstring& path);
18 - Profile* GetProfile(DWORD pid);
19 - DWORD ProfileCount();
20 private: 21 private:
21 typedef std::unique_ptr<Profile> profile_ptr; 22 typedef std::unique_ptr<Profile> profile_ptr;
22 typedef std::map<std::wstring, profile_ptr> profile_map; 23 typedef std::map<std::wstring, profile_ptr> profile_map;
...@@ -24,8 +25,8 @@ private: ...@@ -24,8 +25,8 @@ private:
24 ProfileManager(const ProfileManager&); 25 ProfileManager(const ProfileManager&);
25 ProfileManager operator=(const ProfileManager&); 26 ProfileManager operator=(const ProfileManager&);
26 27
27 - bool AddProfile(pugi::xml_node game); 28 + DWORD CountProfiles();
28 - Profile* AddProfile(const std::wstring& path, profile_ptr new_profile); 29 + bool CreateProfile(pugi::xml_node game);
29 void WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node doc); 30 void WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node doc);
30 // locate profile with executable path 31 // locate profile with executable path
31 profile_map profile_tree; 32 profile_map profile_tree;
......
...@@ -3,8 +3,8 @@ ...@@ -3,8 +3,8 @@
3 DWORD WINAPI FlushThread(LPVOID lParam); // window.cpp 3 DWORD WINAPI FlushThread(LPVOID lParam); // window.cpp
4 4
5 TextBuffer::TextBuffer(HWND edit) : hThread(IthCreateThread(FlushThread, (DWORD)this)), 5 TextBuffer::TextBuffer(HWND edit) : hThread(IthCreateThread(FlushThread, (DWORD)this)),
6 - hEdit(edit), 6 +hEdit(edit),
7 - running(true) 7 +running(true)
8 { 8 {
9 } 9 }
10 10
......
1 #pragma once 1 #pragma once
2 +
2 #include "ITH.h" 3 #include "ITH.h"
3 #include "utility.h" // UniqueHandle, CriticalSection 4 #include "utility.h" // UniqueHandle, CriticalSection
4 5
......
...@@ -16,157 +16,16 @@ ...@@ -16,157 +16,16 @@
16 */ 16 */
17 17
18 #include "ITH.h" 18 #include "ITH.h"
19 -#include "ith/host/srv.h" 19 +#include "host/host.h"
20 -#include "ith/common/const.h" 20 +#include "vnrhook/include/const.h"
21 -#include "ith/common/types.h" 21 +#include "vnrhook/include/types.h"
22 #include "language.h" 22 #include "language.h"
23 #include "utility.h" 23 #include "utility.h"
24 +#include "profile/misc.h"
24 25
25 extern HookManager* man; 26 extern HookManager* man;
26 extern HWND hwndProcessComboBox; 27 extern HWND hwndProcessComboBox;
27 28
28 -bool Parse(const std::wstring& cmd, HookParam& hp)
29 -{
30 - using std::wregex;
31 - using std::regex_search;
32 - // /H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]
33 - wregex rx(L"^X?([ABWSQ])(N)?", wregex::icase);
34 - std::match_results<std::wstring::const_iterator> m;
35 - auto start = cmd.begin();
36 - auto end = cmd.end();
37 - bool result = regex_search(start, end, m, rx);
38 - if (!result)
39 - return result;
40 - start = m[0].second;
41 - if (m[2].matched)
42 - hp.type |= NO_CONTEXT;
43 -
44 - switch (m[1].first[0])
45 - {
46 - case L's':
47 - case L'S':
48 - hp.type |= USING_STRING;
49 - break;
50 - case L'e':
51 - case L'E':
52 - hp.type |= STRING_LAST_CHAR;
53 - case L'a':
54 - case L'A':
55 - hp.type |= BIG_ENDIAN;
56 - hp.length_offset = 1;
57 - break;
58 - case L'b':
59 - case L'B':
60 - hp.length_offset = 1;
61 - break;
62 - case L'h':
63 - case L'H':
64 - hp.type |= PRINT_DWORD;
65 - case L'q':
66 - case L'Q':
67 - hp.type |= USING_STRING | USING_UNICODE;
68 - break;
69 - case L'l':
70 - case L'L':
71 - hp.type |= STRING_LAST_CHAR;
72 - case L'w':
73 - case L'W':
74 - hp.type |= USING_UNICODE;
75 - hp.length_offset = 1;
76 - break;
77 - default:
78 - break;
79 - }
80 -
81 - // [data_offset[*drdo]]
82 - std::wstring data_offset(L"(-?[[:xdigit:]]+)"), drdo(L"(\\*-?[[:xdigit:]]+)?");
83 - rx = wregex(L"^"+ data_offset + drdo, wregex::icase);
84 - result = regex_search(start, end, m, rx);
85 - if (result)
86 - {
87 - start = m[0].second;
88 - hp.off = std::stoul(m[1].str(), NULL, 16);
89 - if (m[2].matched)
90 - {
91 - hp.type |= DATA_INDIRECT;
92 - hp.ind = std::stoul(m[2].str().substr(1), NULL, 16);
93 - }
94 - }
95 -
96 - // [:sub_offset[*drso]]
97 - std::wstring sub_offset(L"(-?[[:xdigit:]]+)"), drso(L"(\\*-?[[:xdigit:]]+)?");
98 - rx = wregex(L"^:" + sub_offset + drso, wregex::icase);
99 - result = regex_search(start, end, m, rx);
100 - if (result)
101 - {
102 - start = m[0].second;
103 - hp.type |= USING_SPLIT;
104 - hp.split = std::stoul(m[1].str(), NULL, 16);
105 - if (m[2].matched)
106 - {
107 - hp.type |= SPLIT_INDIRECT;
108 - hp.split_ind = std::stoul(m[2].str().substr(1), NULL, 16);
109 - }
110 - }
111 - // @addr
112 - rx = wregex(L"^@[[:xdigit:]]+", wregex::icase);
113 - result = regex_search(start, end, m, rx);
114 - if (!result)
115 - return false;
116 - start = m[0].second;
117 - hp.addr = std::stoul(m[0].str().substr(1), NULL, 16);
118 - if (hp.off & 0x80000000)
119 - hp.off -= 4;
120 - if (hp.split & 0x80000000)
121 - hp.split -= 4;
122 -
123 - // [:[module[:{name|#ordinal}]]]
124 - // ":" ->
125 - // "" -> MODULE_OFFSET && module == NULL && function == addr
126 - // ":GDI.dll" -> MODULE_OFFSET && module != NULL
127 - // ":GDI.dll:strlen" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
128 - // ":GDI.dll:#123" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
129 - std::wstring module(L"([[:graph:]]+)"), name(L"[[:graph:]]+"), ordinal(L"\\d+");
130 - rx = wregex(L"^:(" + module + L"(:" + name + L"|#" + ordinal + L")?)?$", wregex::icase);
131 - result = regex_search(start, end, m, rx);
132 - if (result) // :[module[:{name|#ordinal}]]
133 - {
134 - if (m[1].matched) // module
135 - {
136 - hp.type |= MODULE_OFFSET;
137 - std::wstring module = m[2];
138 - std::transform(module.begin(), module.end(), module.begin(), ::towlower);
139 - hp.module = Hash(module);
140 - if (m[3].matched) // :name|#ordinal
141 - {
142 - hp.type |= FUNCTION_OFFSET;
143 - hp.function = Hash(m[3].str().substr(1));
144 - }
145 - }
146 - }
147 - else
148 - {
149 - rx = wregex(L"^!([[:xdigit:]]+)(!([[:xdigit:]]+))?$", wregex::icase);
150 - result = regex_search(start, end, m, rx);
151 - if (result)
152 - {
153 - hp.type |= MODULE_OFFSET;
154 - hp.module = std::stoul(m[1].str(), NULL, 16);
155 - if (m[2].matched)
156 - {
157 - hp.type |= FUNCTION_OFFSET;
158 - hp.function = std::stoul(m[2].str().substr(1), NULL, 16);
159 - }
160 - }
161 - else
162 - {
163 - hp.type |= MODULE_OFFSET;
164 - hp.function = hp.addr;
165 - }
166 - }
167 - return true;
168 -}
169 -
170 DWORD ProcessCommand(const std::wstring& cmd, DWORD pid) 29 DWORD ProcessCommand(const std::wstring& cmd, DWORD pid)
171 { 30 {
172 using std::wregex; 31 using std::wregex;
...@@ -175,40 +34,40 @@ DWORD ProcessCommand(const std::wstring& cmd, DWORD pid) ...@@ -175,40 +34,40 @@ DWORD ProcessCommand(const std::wstring& cmd, DWORD pid)
175 34
176 if (regex_match(cmd, m, wregex(L"/pn(.+)", wregex::icase))) 35 if (regex_match(cmd, m, wregex(L"/pn(.+)", wregex::icase)))
177 { 36 {
178 - pid = IHF_GetPIDByName(m[1].str().c_str()); 37 + pid = Host_GetPIDByName(m[1].str().c_str());
179 if (pid == 0) 38 if (pid == 0)
180 return 0; 39 return 0;
181 - IHF_InjectByPID(pid); 40 + Host_InjectByPID(pid);
182 } 41 }
183 else if (regex_match(cmd, m, wregex(L"/p(\\d+)", wregex::icase))) 42 else if (regex_match(cmd, m, wregex(L"/p(\\d+)", wregex::icase)))
184 { 43 {
185 pid = std::stoul(m[1].str()); 44 pid = std::stoul(m[1].str());
186 - IHF_InjectByPID(pid); 45 + Host_InjectByPID(pid);
187 } 46 }
188 - else if (regex_match(cmd, m, wregex (L"/h(.+)", wregex::icase))) 47 + else if (regex_match(cmd, m, wregex(L"/h(.+)", wregex::icase)))
189 { 48 {
190 HookParam hp = {}; 49 HookParam hp = {};
191 if (Parse(m[1].str(), hp)) 50 if (Parse(m[1].str(), hp))
192 - IHF_InsertHook(pid, &hp); 51 + Host_InsertHook(pid, &hp);
193 } 52 }
194 - else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㅇ|연|l|)([[:xdigit:]]+)(?:-| )([[:xdigit:]]+)", wregex::icase))) 53 + else if (regex_match(cmd, m, wregex(L"(?::|)(?:|연|l|)([[:xdigit:]]+)(?:-| )([[:xdigit:]]+)", wregex::icase)))
195 { 54 {
196 DWORD from = std::stoul(m[1].str(), NULL, 16); 55 DWORD from = std::stoul(m[1].str(), NULL, 16);
197 DWORD to = std::stoul(m[2].str(), NULL, 16); 56 DWORD to = std::stoul(m[2].str(), NULL, 16);
198 - IHF_AddLink(from, to); 57 + Host_AddLink(from, to);
199 } 58 }
200 - else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㅎ|해|해제|u)([[:xdigit:]]+)", wregex::icase))) 59 + else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㅎ|해|해제|u)([[:xdigit:]]+)", wregex::icase)))
201 { 60 {
202 DWORD from = std::stoul(m[1].str(), NULL, 16); 61 DWORD from = std::stoul(m[1].str(), NULL, 16);
203 - IHF_UnLink(from); 62 + Host_UnLink(from);
204 } 63 }
205 - else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㄷ|도|도움|도움말|h|help)", wregex::icase))) 64 + else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㄷ|도|도움|도움말|h|help)", wregex::icase)))
206 { 65 {
207 ConsoleOutput(Usage); 66 ConsoleOutput(Usage);
208 } 67 }
209 else 68 else
210 { 69 {
211 - ConsoleOutput(L"알 수 없는 명령어. 도움말을 보시려면, :h 나 :help를 입력하세요."); 70 + ConsoleOutput(L"알 수 없는 명령어. 도움말을 보시려면, :h 나 :help를 입력하세요.");
212 } 71 }
213 return 0; 72 return 0;
214 } 73 }
......
...@@ -14,44 +14,44 @@ ...@@ -14,44 +14,44 @@
14 * You should have received a copy of the GNU General Public License 14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */ 16 */
17 -const wchar_t* Warning=L"경고!"; 17 +const wchar_t* Warning = L"경고!";
18 //command.cpp 18 //command.cpp
19 -const wchar_t* ErrorSyntax=L"명령어 오류"; 19 +const wchar_t* ErrorSyntax = L"명령어 오류";
20 -const wchar_t* Usage = L"명령어:\r\n\ 20 +const wchar_t* Usage = L"령어:\r\n\
21 \r\n\ 21 \r\n\
22 -도움말 //도움말을 출력합니다\r\n\ 22 +도움말 //도움말을 출력합니다\r\n\
23 -출발 도착 // '출발'스레드에서 '도착'스레드로 연결합니다\r\n\ 23 +출발 도착 // '출발'스레드에서 '도착'스레드로 연결합니다\r\n\
24 -ㅎ출발 // '출발'스레드에 연결된 링크를 해제합니다\r\n\ 24 +ㅎ출발 // '출발'스레드에 연결된 링크를 해제합니다\r\n\
25 \r\n\ 25 \r\n\
26 -'출발'과 '도착'에는 16진법(헥사코드) 스레드번호를 입력합니다. 스레드 번호는 맨 앞에 있는 첫 번째 숫자열입니다.\r\n\ 26 +'출발'과 '도착'에는 16진법(헥사코드) 스레드번호를 입력합니다. 스레드 번호는 맨 앞에 있는 첫 번째 숫자열입니다.\r\n\
27 \r\n\ 27 \r\n\
28 -로더 옵션:\r\n\ 28 +로더 옵션:\r\n\
29 -/P[{process_id|Nprocess_name}] //프로세스에 부착\r\n\ 29 +/P[{process_id|Nprocess_name}] //프로세스에 부착\r\n\
30 \r\n\ 30 \r\n\
31 -H코드 후킹 옵션:\r\n\ 31 +H코드 후킹 옵션:\r\n\
32 /H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:module[:{name|#ordinal}]]\r\n\ 32 /H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:module[:{name|#ordinal}]]\r\n\
33 \r\n\ 33 \r\n\
34 -(서수를 제외한) /H코드의 모든 숫자는 아무것도 처리되지 않은 16진법(헥사코드)입니다"; 34 +(서수를 제외한) /H코드의 모든 숫자는 아무것도 처리되지 않은 16진법(헥사코드)입니다";
35 35
36 const wchar_t* ExtendedUsage = L"/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]\r\n\ 36 const wchar_t* ExtendedUsage = L"/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]\r\n\
37 \r\n\ 37 \r\n\
38 -추가 사용자정의 후킹설정\r\n\ 38 +추가 사용자정의 후킹설정\r\n\
39 \r\n\ 39 \r\n\
40 -후킹 종류 :\r\n\ 40 +후킹 종류 :\r\n\
41 -A - DBCS 문자\r\n\ 41 +A - DBCS 문자\r\n\
42 -B - DBCS 문자(big-endian)\r\n\ 42 +B - DBCS 문자(big-endian)\r\n\
43 -W - UCS2 문자\r\n\ 43 +W - UCS2 문자\r\n\
44 -S - MBCS 문자열\r\n\ 44 +S - MBCS 자열\r\n\
45 -Q - UTF-16 문자열\r\n\ 45 +Q - UTF-16 자열\r\n\
46 \r\n\ 46 \r\n\
47 -매개변수:\r\n\ 47 +개변수:\r\n\
48 -X - 하드웨어 구획점 사용\r\n\ 48 +X - 하드웨어 구획점 사용\r\n\
49 -N - 문법을 사용하지 않음\r\n\ 49 +N - 문법을 사용하지 않음\r\n\
50 data_offset - stack offset to char / string pointer\r\n\ 50 data_offset - stack offset to char / string pointer\r\n\
51 drdo - add a level of indirection to data_offset\r\n\ 51 drdo - add a level of indirection to data_offset\r\n\
52 sub_offset - stack offset to subcontext\r\n\ 52 sub_offset - stack offset to subcontext\r\n\
53 drso - add a level of indirection to sub_offset\r\n\ 53 drso - add a level of indirection to sub_offset\r\n\
54 -addr - 후킹할 주소\r\n\ 54 +addr - 후킹할 주소\r\n\
55 module - name of the module to use as base for 'addr'\r\n\ 55 module - name of the module to use as base for 'addr'\r\n\
56 name - name of the 'module' export to use as base for 'addr'\r\n\ 56 name - name of the 'module' export to use as base for 'addr'\r\n\
57 ordinal - number of the 'module' export ordinal to use as base for 'addr'\r\n\ 57 ordinal - number of the 'module' export ordinal to use as base for 'addr'\r\n\
...@@ -64,70 +64,70 @@ Negative values of 'data_offset' and 'sub_offset' refer to registers: \r\n\ ...@@ -64,70 +64,70 @@ Negative values of 'data_offset' and 'sub_offset' refer to registers: \r\n\
64 All numbers except ordinal are hexadecimal without any prefixes"; 64 All numbers except ordinal are hexadecimal without any prefixes";
65 65
66 //inject.cpp 66 //inject.cpp
67 -const wchar_t* ErrorRemoteThread=L"원격 스레드를 생성할 수 없음."; 67 +const wchar_t* ErrorRemoteThread = L"원격 스레드를 생성할 수 없음.";
68 -const wchar_t* ErrorOpenProcess=L"프로세스를 열 수 없음."; 68 +const wchar_t* ErrorOpenProcess = L"프로세스를 열 수 없음.";
69 -const wchar_t* ErrorNoProcess=L"프로세스를 찾을 수 없음"; 69 +const wchar_t* ErrorNoProcess = L"프로세스를 찾을 수 없음";
70 -const wchar_t* SelfAttach=L"ITH.exe에 부착하지 말아 주세요"; 70 +const wchar_t* SelfAttach = L"ITH.exe에 부착하지 말아 주세요";
71 -const wchar_t* AlreadyAttach=L"프로세스가 이미 부착됨."; 71 +const wchar_t* AlreadyAttach = L"프로세스가 이미 부착됨.";
72 -const wchar_t* FormatInject=L"프로세스 %d에 인젝션. 모듈 기반 %.8X"; 72 +const wchar_t* FormatInject = L"프로세스 %d에 인젝션. 모듈 기반 %.8X";
73 //main.cpp 73 //main.cpp
74 -const wchar_t* NotAdmin=L"SeDebugPrevilege을 활성화 할 수 없습니다. ITH가 제대로 작동하지 못합니다.\r\n\ 74 +const wchar_t* NotAdmin = L"SeDebugPrevilege을 활성화 할 수 없습니다. ITH가 제대로 작동하지 못합니다.\r\n\
75 -관리자 계정으로 실행하시거나 UAC를 끄시고 ITH를 실행해 주세요."; 75 +관리자 계정으로 실행하시거나 UAC를 끄시고 ITH를 실행해 주세요.";
76 //pipe.cpp 76 //pipe.cpp
77 -const wchar_t* ErrorCreatePipe=L"텍스트 파이프를 생성할 수 없거나, 요청이 너무 많습니다."; 77 +const wchar_t* ErrorCreatePipe = L"텍스트 파이프를 생성할 수 없거나, 요청이 너무 많습니다.";
78 -const wchar_t* FormatDetach=L"프로세스 %d가 탈착됨."; 78 +const wchar_t* FormatDetach = L"프로세스 %d가 탈착됨.";
79 -const wchar_t* ErrorCmdQueueFull=L"명령어 대기열이 가득참."; 79 +const wchar_t* ErrorCmdQueueFull = L"명령어 대기열이 가득참.";
80 -const wchar_t* ErrorNoAttach=L"프로세스가 부착되지 않음."; 80 +const wchar_t* ErrorNoAttach = L"프로세스가 부착되지 않음.";
81 81
82 //profile.cpp 82 //profile.cpp
83 -const wchar_t* ErrorMonitor=L"프로세스를 감시할 수 없음."; 83 +const wchar_t* ErrorMonitor = L"프로세스를 감시할 수 없음.";
84 //utility.cpp 84 //utility.cpp
85 -const wchar_t* InitMessage=L"Copyright (C) 2010-2012 kaosu <qiupf2000@gmail.com>\r\n\ 85 +const wchar_t* InitMessage = L"Copyright (C) 2010-2012 kaosu <qiupf2000@gmail.com>\r\n\
86 Copyright (C) 2015 zorkzero <zorkzero@hotmail.com>\r\n\ 86 Copyright (C) 2015 zorkzero <zorkzero@hotmail.com>\r\n\
87 -소스코드 <https://code.google.com/p/interactive-text-hooker/>\r\n\ 87 +소스코드 <https://code.google.com/p/interactive-text-hooker/>\r\n\
88 -일반토론 <https://groups.google.com/forum/?fromgroups#!forum/interactive-text-hooker>\r\n\ 88 +반토론 <https://groups.google.com/forum/?fromgroups#!forum/interactive-text-hooker>\r\n\
89 -한글화 @mireado<https://twitter.com/mireado>"; 89 +한글화 @mireado<https://twitter.com/mireado>";
90 -const wchar_t* BackgroundMsg=L"도움말을 보시려면, \"help\", \"도움말\"이나 \"도움\"을 입력하세요."; 90 +const wchar_t* BackgroundMsg = L"도움말을 보시려면, \"help\", \"도움말\"이나 \"도움\"을 입력하세요.";
91 -const wchar_t* ErrorLinkExist=L"연결이 존재함."; 91 +const wchar_t* ErrorLinkExist = L"연결이 존재함.";
92 -const wchar_t* ErrorCylicLink=L"연결실패. 순환연결은 허용되지 않습니다."; 92 +const wchar_t* ErrorCylicLink = L"연결실패. 순환연결은 허용되지 않습니다.";
93 -const wchar_t* FormatLink=L"출발스레드%.4x에서 도착스레드%.4x로 연결."; 93 +const wchar_t* FormatLink = L"출발스레드%.4x에서 도착스레드%.4x로 연결.";
94 -const wchar_t* ErrorLink=L"연결실패. 출발/도착 스레드를 찾을 수 없음."; 94 +const wchar_t* ErrorLink = L"연결실패. 출발/도착 스레드를 찾을 수 없음.";
95 -const wchar_t* ErrorDeleteCombo=L"글상자에서 지우기 실패."; 95 +const wchar_t* ErrorDeleteCombo = L"글상자에서 지우기 실패.";
96 96
97 //window.cpp 97 //window.cpp
98 -const wchar_t* ClassName=L"ITH"; 98 +const wchar_t* ClassName = L"ITH";
99 -const wchar_t* ClassNameAdmin=L"ITH (관리자)"; 99 +const wchar_t* ClassNameAdmin = L"ITH (관리자)";
100 -const wchar_t* ErrorNotSplit=L"먼저 문단 나누기를 활성화해주세요!"; 100 +const wchar_t* ErrorNotSplit = L"먼저 문단 나누기를 활성화해주세요!";
101 -const wchar_t* ErrorNotModule=L"먼저 모듈을 활성화해주세요!"; 101 +const wchar_t* ErrorNotModule = L"먼저 모듈을 활성화해주세요!";
102 //Main window buttons 102 //Main window buttons
103 -const wchar_t* ButtonTitleProcess=L"프로세스"; 103 +const wchar_t* ButtonTitleProcess = L"프로세스";
104 -const wchar_t* ButtonTitleThread=L"스레드"; 104 +const wchar_t* ButtonTitleThread = L"스레드";
105 -const wchar_t* ButtonTitleHook=L"후킹"; 105 +const wchar_t* ButtonTitleHook = L"후킹";
106 -const wchar_t* ButtonTitleProfile=L"프로필"; 106 +const wchar_t* ButtonTitleProfile = L"프로필";
107 -const wchar_t* ButtonTitleOption=L"옵션"; 107 +const wchar_t* ButtonTitleOption = L"옵션";
108 -const wchar_t* ButtonTitleClear=L"지우기"; 108 +const wchar_t* ButtonTitleClear = L"지우기";
109 -const wchar_t* ButtonTitleSave=L"저장"; 109 +const wchar_t* ButtonTitleSave = L"저장";
110 -const wchar_t* ButtonTitleTop=L"항상위"; 110 +const wchar_t* ButtonTitleTop = L"항상위";
111 //Hook window 111 //Hook window
112 -const wchar_t* SpecialHook=L"H코드 후킹, AGTH 코드는 지원하지 않습니다."; 112 +const wchar_t* SpecialHook = L"H코드 후킹, AGTH 코드는 지원하지 않습니다.";
113 //Process window 113 //Process window
114 -const wchar_t* TabTitlePID=L"PID"; 114 +const wchar_t* TabTitlePID = L"PID";
115 -const wchar_t* TabTitleMemory=L"메모리"; 115 +const wchar_t* TabTitleMemory = L"메모리";
116 -const wchar_t* TabTitleName=L"이름"; 116 +const wchar_t* TabTitleName = L"이름";
117 -const wchar_t* TabTitleTID=L"TID"; 117 +const wchar_t* TabTitleTID = L"TID";
118 -const wchar_t* TabTitleStart=L"시작"; 118 +const wchar_t* TabTitleStart = L"시작";
119 -const wchar_t* TabTitleModule=L"모듈"; 119 +const wchar_t* TabTitleModule = L"모듈";
120 -const wchar_t* TabTitleState=L"상태"; 120 +const wchar_t* TabTitleState = L"상태";
121 -const wchar_t* SuccessAttach=L"프로세스에 ITH 부착성공."; 121 +const wchar_t* SuccessAttach = L"프로세스에 ITH 부착성공.";
122 -const wchar_t* FailAttach=L"프로세스에 ITH 부착실패."; 122 +const wchar_t* FailAttach = L"프로세스에 ITH 부착실패.";
123 -const wchar_t* SuccessDetach=L"프로세스에서 ITH 탈착성공."; 123 +const wchar_t* SuccessDetach = L"프로세스에서 ITH 탈착성공.";
124 -const wchar_t* FailDetach=L"ITH 탈착실패."; 124 +const wchar_t* FailDetach = L"ITH 탈착실패.";
125 //Profile window 125 //Profile window
126 -const wchar_t* ProfileExist=L"프로필이 이미 존재함."; 126 +const wchar_t* ProfileExist = L"프로필이 이미 존재함.";
127 -const wchar_t* SuccessAddProfile=L"프로필 추가됨."; 127 +const wchar_t* SuccessAddProfile = L"프로필 추가됨.";
128 -const wchar_t* FailAddProfile=L"프로필 추가실패"; 128 +const wchar_t* FailAddProfile = L"프로필 추가실패";
129 -const wchar_t* TabTitleNumber=L"No."; 129 +const wchar_t* TabTitleNumber = L"No.";
130 -const wchar_t* NoFile=L"파일을 찾을 수 없음."; 130 +const wchar_t* NoFile = L"파일을 찾을 수 없음.";
131 -const wchar_t* PathDismatch=L"프로세스 이름이 일치하지 않습니다, 계속하시겠습니까?"; 131 +const wchar_t* PathDismatch = L"프로세스 이름이 일치하지 않습니다, 계속하시겠습니까?";
132 -const wchar_t* SuccessImportProfile=L"프로필 가져오기 성공"; 132 +const wchar_t* SuccessImportProfile = L"프로필 가져오기 성공";
133 -//const wchar_t* SuccessAddProfile=L"Profile added."; 133 +//const wchar_t* SuccessAddProfile=L"Profile added.";
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -16,11 +16,11 @@ ...@@ -16,11 +16,11 @@
16 */ 16 */
17 17
18 #include "ITH.h" 18 #include "ITH.h"
19 -#include "ith/host/srv.h" 19 +#include "host/host.h"
20 -#include "ith/host/hookman.h" 20 +#include "host/hookman.h"
21 -#include "ith/host/SettingManager.h" 21 +#include "host/settings.h"
22 #include "CustomFilter.h" 22 #include "CustomFilter.h"
23 -#include "profile.h" 23 +#include "profile/Profile.h"
24 #include "ProfileManager.h" 24 #include "ProfileManager.h"
25 25
26 HINSTANCE hIns; 26 HINSTANCE hIns;
...@@ -39,10 +39,10 @@ extern "C" { ...@@ -39,10 +39,10 @@ extern "C" {
39 CustomFilter* uni_filter; 39 CustomFilter* uni_filter;
40 CustomFilter* mb_filter; 40 CustomFilter* mb_filter;
41 HookManager* man; 41 HookManager* man;
42 -SettingManager* setman; 42 +Settings* setman;
43 LONG split_time, cyclic_remove, global_filter; 43 LONG split_time, cyclic_remove, global_filter;
44 LONG process_time, inject_delay, insert_delay, 44 LONG process_time, inject_delay, insert_delay,
45 - auto_inject, auto_insert, clipboard_flag; 45 +auto_inject, auto_insert, clipboard_flag;
46 46
47 std::map<std::wstring, long> setting; 47 std::map<std::wstring, long> setting;
48 48
...@@ -69,11 +69,13 @@ void RecordUniChar(WORD uni, PVOID f) ...@@ -69,11 +69,13 @@ void RecordUniChar(WORD uni, PVOID f)
69 69
70 void SaveSettings() 70 void SaveSettings()
71 { 71 {
72 - GetWindowRect(hMainWnd, &window); 72 + WINDOWPLACEMENT wndpl;
73 - setting[L"window_left"] = window.left; 73 + wndpl.length = sizeof(WINDOWPLACEMENT);
74 - setting[L"window_right"] = window.right; 74 + GetWindowPlacement(hMainWnd, &wndpl);
75 - setting[L"window_top"] = window.top; 75 + setting[L"window_left"] = wndpl.rcNormalPosition.left;
76 - setting[L"window_bottom"] = window.bottom; 76 + setting[L"window_right"] = wndpl.rcNormalPosition.right;
77 + setting[L"window_top"] = wndpl.rcNormalPosition.top;
78 + setting[L"window_bottom"] = wndpl.rcNormalPosition.bottom;
77 setting[L"split_time"] = split_time; 79 setting[L"split_time"] = split_time;
78 setting[L"process_time"] = process_time; 80 setting[L"process_time"] = process_time;
79 setting[L"inject_delay"] = inject_delay; 81 setting[L"inject_delay"] = inject_delay;
...@@ -238,17 +240,18 @@ LONG WINAPI UnhandledExcept(_EXCEPTION_POINTERS *ExceptionInfo) ...@@ -238,17 +240,18 @@ LONG WINAPI UnhandledExcept(_EXCEPTION_POINTERS *ExceptionInfo)
238 return 0; 240 return 0;
239 } 241 }
240 242
241 -int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) 243 +int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
242 { 244 {
245 + InitCommonControls();
243 if (!IthInitSystemService()) 246 if (!IthInitSystemService())
244 TerminateProcess(GetCurrentProcess(), 0); 247 TerminateProcess(GetCurrentProcess(), 0);
245 CreateMutex(NULL, TRUE, L"ITH_MAIN_RUNNING"); 248 CreateMutex(NULL, TRUE, L"ITH_MAIN_RUNNING");
246 - if (IHF_Init()) 249 + if (Host_Open())
247 { 250 {
248 SetUnhandledExceptionFilter(UnhandledExcept); 251 SetUnhandledExceptionFilter(UnhandledExcept);
249 - IHF_GetHookManager(&man); 252 + Host_GetHookManager(&man);
250 - IHF_GetSettingManager(&setman); 253 + Host_GetSettings(&setman);
251 - setman->SetValue(SETTING_SPLIT_TIME, 200); 254 + setman->splittingInterval = 200;
252 MonitorFlag = true; 255 MonitorFlag = true;
253 pfman = new ProfileManager(); 256 pfman = new ProfileManager();
254 mb_filter = new CustomFilter(); 257 mb_filter = new CustomFilter();
...@@ -256,11 +259,11 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin ...@@ -256,11 +259,11 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
256 DefaultSettings(); 259 DefaultSettings();
257 LoadSettings(); 260 LoadSettings();
258 InitializeSettings(); 261 InitializeSettings();
259 - setman->SetValue(SETTING_SPLIT_TIME, split_time); 262 + setman->splittingInterval = split_time;
260 - setman->SetValue(SETTING_CLIPFLAG, clipboard_flag); 263 + setman->clipboardFlag = clipboard_flag > 0;
261 hIns = hInstance; 264 hIns = hInstance;
262 MyRegisterClass(hIns); 265 MyRegisterClass(hIns);
263 - InitInstance(hIns, IHF_IsAdmin(), &window); 266 + InitInstance(hIns, FALSE, &window);
264 MSG msg; 267 MSG msg;
265 while (GetMessage(&msg, NULL, 0, 0)) 268 while (GetMessage(&msg, NULL, 0, 0))
266 { 269 {
...@@ -277,7 +280,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin ...@@ -277,7 +280,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
277 { 280 {
278 FindITH(); 281 FindITH();
279 } 282 }
280 - IHF_Cleanup(); 283 + Host_Close();
281 IthCloseSystemService(); 284 IthCloseSystemService();
282 TerminateProcess(GetCurrentProcess(), 0); 285 TerminateProcess(GetCurrentProcess(), 0);
283 } 286 }
......
...@@ -16,10 +16,11 @@ ...@@ -16,10 +16,11 @@
16 */ 16 */
17 17
18 #include "utility.h" 18 #include "utility.h"
19 -#include "ith/host/srv.h" 19 +#include "host/host.h"
20 -#include "ith/host/hookman.h" 20 +#include "host/hookman.h"
21 -#include "ith/common/types.h" 21 +#include "vnrhook/include/types.h"
22 -#include "ith/common/const.h" 22 +#include "vnrhook/include/const.h"
23 +#include "profile/misc.h"
23 24
24 extern HookManager* man; // main.cpp 25 extern HookManager* man; // main.cpp
25 26
...@@ -65,11 +66,11 @@ std::wstring GetWindowsPath(const std::wstring& path) ...@@ -65,11 +66,11 @@ std::wstring GetWindowsPath(const std::wstring& path)
65 // path is in device form 66 // path is in device form
66 // \Device\HarddiskVolume2\Windows\System32\taskhost.exe 67 // \Device\HarddiskVolume2\Windows\System32\taskhost.exe
67 auto pathOffset = path.find(L'\\', 1) + 1; 68 auto pathOffset = path.find(L'\\', 1) + 1;
68 - pathOffset = path.find(L'\\', pathOffset); 69 + pathOffset = path.find(L'\\', pathOffset);
69 std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2 70 std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2
70 std::wstring dosDrive = GetDriveLetter(devicePath); // C: 71 std::wstring dosDrive = GetDriveLetter(devicePath); // C:
71 if (dosDrive.empty()) 72 if (dosDrive.empty())
72 - return L""; 73 + return path;
73 std::wstring dosPath = dosDrive; // C: 74 std::wstring dosPath = dosDrive; // C:
74 dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe 75 dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe
75 return dosPath; 76 return dosPath;
...@@ -117,16 +118,16 @@ std::wstring GetCode(const HookParam& hp, DWORD pid) ...@@ -117,16 +118,16 @@ std::wstring GetCode(const HookParam& hp, DWORD pid)
117 code += c; 118 code += c;
118 if (hp.type & NO_CONTEXT) 119 if (hp.type & NO_CONTEXT)
119 code += L'N'; 120 code += L'N';
120 - if (hp.off >> 31) 121 + if (hp.offset >> 31)
121 - code += L"-" + ToHexString(-(hp.off + 4)); 122 + code += L"-" + ToHexString(-(hp.offset + 4));
122 else 123 else
123 - code += ToHexString(hp.off); 124 + code += ToHexString(hp.offset);
124 if (hp.type & DATA_INDIRECT) 125 if (hp.type & DATA_INDIRECT)
125 { 126 {
126 - if (hp.ind >> 31) 127 + if (hp.index >> 31)
127 - code += L"*-" + ToHexString(-hp.ind); 128 + code += L"*-" + ToHexString(-hp.index);
128 else 129 else
129 - code += L"*" + ToHexString(hp.ind); 130 + code += L"*" + ToHexString(hp.index);
130 } 131 }
131 if (hp.type & USING_SPLIT) 132 if (hp.type & USING_SPLIT)
132 { 133 {
...@@ -137,21 +138,21 @@ std::wstring GetCode(const HookParam& hp, DWORD pid) ...@@ -137,21 +138,21 @@ std::wstring GetCode(const HookParam& hp, DWORD pid)
137 } 138 }
138 if (hp.type & SPLIT_INDIRECT) 139 if (hp.type & SPLIT_INDIRECT)
139 { 140 {
140 - if (hp.split_ind >> 31) 141 + if (hp.split_index >> 31)
141 - code += L"*-" + ToHexString(-hp.split_ind); 142 + code += L"*-" + ToHexString(-hp.split_index);
142 else 143 else
143 - code += L"*" + ToHexString(hp.split_ind); 144 + code += L"*" + ToHexString(hp.split_index);
144 } 145 }
145 if (pid) 146 if (pid)
146 { 147 {
147 - PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.addr); 148 + PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.address);
148 if (allocationBase) 149 if (allocationBase)
149 { 150 {
150 std::wstring path = GetModuleFileNameAsString(pid, allocationBase); 151 std::wstring path = GetModuleFileNameAsString(pid, allocationBase);
151 if (!path.empty()) 152 if (!path.empty())
152 { 153 {
153 auto fileName = path.substr(path.rfind(L'\\') + 1); 154 auto fileName = path.substr(path.rfind(L'\\') + 1);
154 - DWORD relativeHookAddress = hp.addr - (DWORD)allocationBase; 155 + DWORD relativeHookAddress = hp.address - (DWORD)allocationBase;
155 code += L"@" + ToHexString(relativeHookAddress) + L":" + fileName; 156 code += L"@" + ToHexString(relativeHookAddress) + L":" + fileName;
156 return code; 157 return code;
157 } 158 }
...@@ -159,20 +160,20 @@ std::wstring GetCode(const HookParam& hp, DWORD pid) ...@@ -159,20 +160,20 @@ std::wstring GetCode(const HookParam& hp, DWORD pid)
159 } 160 }
160 if (hp.module) 161 if (hp.module)
161 { 162 {
162 - code += L"@" + ToHexString(hp.addr) + L"!" + ToHexString(hp.module); 163 + code += L"@" + ToHexString(hp.address) + L"!" + ToHexString(hp.module);
163 if (hp.function) 164 if (hp.function)
164 code += L"!" + ToHexString(hp.function); 165 code += L"!" + ToHexString(hp.function);
165 } 166 }
166 else 167 else
167 { 168 {
168 - // hack, the original address is stored in the function field 169 + // Hack. The original address is stored in the function field
169 - // if (module == NULL && function != NULL) 170 + // if (module == NULL && function != NULL).
170 - // in TextHook::UnsafeInsertHookCode() MODULE_OFFSET and FUNCTION_OFFSET are removed from 171 + // MODULE_OFFSET and FUNCTION_OFFSET are removed from HookParam.type in
171 - // HookParam.type 172 + // TextHook::UnsafeInsertHookCode() and can not be used here.
172 if (hp.function) 173 if (hp.function)
173 code += L"@" + ToHexString(hp.function); 174 code += L"@" + ToHexString(hp.function);
174 else 175 else
175 - code += L"@" + ToHexString(hp.addr) + L":"; 176 + code += L"@" + ToHexString(hp.address) + L":";
176 } 177 }
177 return code; 178 return code;
178 } 179 }
...@@ -282,13 +283,13 @@ HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition) ...@@ -282,13 +283,13 @@ HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition)
282 return CreateFile(path.c_str(), option, share, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL); 283 return CreateFile(path.c_str(), option, share, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL);
283 } 284 }
284 285
285 -//SJIS->Unicode. 'mb' must be null-terminated. 'wc_length' is the length of 'wc' in characters. 286 +//SJIS->Unicode. mb must be null-terminated. wc_length is the length of wc in characters.
286 int MB_WC(const char* mb, wchar_t* wc, int wc_length) 287 int MB_WC(const char* mb, wchar_t* wc, int wc_length)
287 { 288 {
288 return MultiByteToWideChar(932, 0, mb, -1, wc, wc_length); 289 return MultiByteToWideChar(932, 0, mb, -1, wc, wc_length);
289 } 290 }
290 291
291 -// Count characters in wide string. 'mb_length' is the number of bytes from 'mb' to convert or 292 +// Count characters in wide string. mb_length is the number of bytes from mb to convert or
292 // -1 if the string is null terminated. 293 // -1 if the string is null terminated.
293 int MB_WC_count(const char* mb, int mb_length) 294 int MB_WC_count(const char* mb, int mb_length)
294 { 295 {
...@@ -300,12 +301,3 @@ int WC_MB(const wchar_t *wc, char* mb, int mb_length) ...@@ -300,12 +301,3 @@ int WC_MB(const wchar_t *wc, char* mb, int mb_length)
300 { 301 {
301 return WideCharToMultiByte(932, 0, wc, -1, mb, mb_length, NULL, NULL); 302 return WideCharToMultiByte(932, 0, wc, -1, mb, mb_length, NULL, NULL);
302 } 303 }
303 -
304 -DWORD Hash(const std::wstring& module, int length)
305 -{
306 - DWORD hash = 0;
307 - auto end = length < 0 || static_cast<std::size_t>(length) > module.length() ? module.end() : module.begin() + length;
308 - for (auto it = module.begin(); it != end; ++it)
309 - hash = _rotr(hash, 7) + *it;
310 - return hash;
311 -}
......
1 #pragma once 1 #pragma once
2 +
2 #include "ITH.h" 3 #include "ITH.h"
3 4
4 struct HookParam; 5 struct HookParam;
5 struct ProcessRecord; 6 struct ProcessRecord;
6 7
7 -DWORD Hash(const std::wstring& module, int length = -1);
8 DWORD ProcessCommand(const std::wstring& cmd, DWORD pid); 8 DWORD ProcessCommand(const std::wstring& cmd, DWORD pid);
9 std::wstring GetProcessPath(DWORD pid); 9 std::wstring GetProcessPath(DWORD pid);
10 void ConsoleOutput(LPCWSTR); 10 void ConsoleOutput(LPCWSTR);
...@@ -58,13 +58,6 @@ int MB_WC_count(const char* mb, int mb_length); ...@@ -58,13 +58,6 @@ int MB_WC_count(const char* mb, int mb_length);
58 int WC_MB(const wchar_t *wc, char* mb, int mb_length); 58 int WC_MB(const wchar_t *wc, char* mb, int mb_length);
59 bool Parse(const std::wstring& cmd, HookParam& hp); 59 bool Parse(const std::wstring& cmd, HookParam& hp);
60 60
61 -template <typename T>
62 -std::wstring ToHexString(T i) {
63 - std::wstringstream ss;
64 - ss << std::uppercase << std::hex << i;
65 - return ss.str();
66 -}
67 -
68 // http://jrdodds.blogs.com/blog/2004/08/raii_in_c.html 61 // http://jrdodds.blogs.com/blog/2004/08/raii_in_c.html
69 class CriticalSection 62 class CriticalSection
70 { 63 {
......
...@@ -18,15 +18,16 @@ ...@@ -18,15 +18,16 @@
18 #include "ProcessWindow.h" 18 #include "ProcessWindow.h"
19 #include "resource.h" 19 #include "resource.h"
20 #include "language.h" 20 #include "language.h"
21 -#include "ith/host/srv.h" 21 +#include "host/host.h"
22 -#include "ith/host/hookman.h" 22 +#include "host/hookman.h"
23 -#include "ith/common/const.h" 23 +#include "vnrhook/include/const.h"
24 #include "version.h" 24 #include "version.h"
25 #include "ProfileManager.h" 25 #include "ProfileManager.h"
26 -#include "ith/host/SettingManager.h" 26 +#include "host/settings.h"
27 #include "CustomFilter.h" 27 #include "CustomFilter.h"
28 -#include "Profile.h" 28 +#include "profile/Profile.h"
29 #include "TextBuffer.h" 29 #include "TextBuffer.h"
30 +#include "profile/misc.h"
30 31
31 #define CMD_SIZE 512 32 #define CMD_SIZE 512
32 33
...@@ -46,39 +47,38 @@ extern ProfileManager* pfman; // ProfileManager.cpp ...@@ -46,39 +47,38 @@ extern ProfileManager* pfman; // ProfileManager.cpp
46 extern HookManager* man; // main.cpp 47 extern HookManager* man; // main.cpp
47 extern CustomFilter* mb_filter; // main.cpp 48 extern CustomFilter* mb_filter; // main.cpp
48 extern CustomFilter* uni_filter; // main.cpp 49 extern CustomFilter* uni_filter; // main.cpp
49 -extern SettingManager* setman; // main.cpp 50 +extern Settings* setman; // main.cpp
50 #define COMMENT_BUFFER_LENGTH 512 51 #define COMMENT_BUFFER_LENGTH 512
51 static WCHAR comment_buffer[COMMENT_BUFFER_LENGTH]; 52 static WCHAR comment_buffer[COMMENT_BUFFER_LENGTH];
52 53
53 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); 54 LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
54 void SaveSettings(); // main.cpp 55 void SaveSettings(); // main.cpp
55 extern LONG split_time, process_time, inject_delay, insert_delay, 56 extern LONG split_time, process_time, inject_delay, insert_delay,
56 - auto_inject, auto_insert, clipboard_flag, cyclic_remove, global_filter; //main.cpp 57 +auto_inject, auto_insert, clipboard_flag, cyclic_remove, global_filter; //main.cpp
57 static int last_select, last_edit; 58 static int last_select, last_edit;
58 -void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const TextThread& thread);
59 59
60 ATOM MyRegisterClass(HINSTANCE hInstance) 60 ATOM MyRegisterClass(HINSTANCE hInstance)
61 { 61 {
62 WNDCLASSEX wcex; 62 WNDCLASSEX wcex;
63 wcex.cbSize = sizeof(WNDCLASSEX); 63 wcex.cbSize = sizeof(WNDCLASSEX);
64 - wcex.style = CS_HREDRAW | CS_VREDRAW; 64 + wcex.style = CS_HREDRAW | CS_VREDRAW;
65 - wcex.lpfnWndProc = WndProc; 65 + wcex.lpfnWndProc = WndProc;
66 - wcex.cbClsExtra = 0; 66 + wcex.cbClsExtra = 0;
67 - wcex.cbWndExtra = 0; 67 + wcex.cbWndExtra = 0;
68 - wcex.hInstance = hInstance; 68 + wcex.hInstance = hInstance;
69 - wcex.hIcon = NULL; 69 + wcex.hIcon = NULL;
70 - wcex.hCursor = NULL; 70 + wcex.hCursor = NULL;
71 - wcex.hbrBackground = GetStockBrush(WHITE_BRUSH); 71 + wcex.hbrBackground = GetStockBrush(WHITE_BRUSH);
72 - wcex.lpszMenuName = NULL; 72 + wcex.lpszMenuName = NULL;
73 - wcex.lpszClassName = ClassName; 73 + wcex.lpszClassName = ClassName;
74 - wcex.hIconSm = LoadIcon(hInstance, (LPWSTR)IDI_ICON1); 74 + wcex.hIconSm = LoadIcon(hInstance, (LPWSTR)IDI_ICON1);
75 return RegisterClassEx(&wcex); 75 return RegisterClassEx(&wcex);
76 } 76 }
77 77
78 BOOL InitInstance(HINSTANCE hInstance, DWORD nAdmin, RECT* rc) 78 BOOL InitInstance(HINSTANCE hInstance, DWORD nAdmin, RECT* rc)
79 { 79 {
80 hIns = hInstance; 80 hIns = hInstance;
81 - LPCWSTR name = (nAdmin) ? ClassNameAdmin : ClassName; 81 + LPCWSTR name = (nAdmin) ? ClassNameAdmin : ClassName;
82 hMainWnd = CreateWindow(ClassName, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN, 82 hMainWnd = CreateWindow(ClassName, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
83 rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, NULL, NULL, hInstance, 0); 83 rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, NULL, NULL, hInstance, 0);
84 if (!hMainWnd) 84 if (!hMainWnd)
...@@ -137,8 +137,8 @@ BOOL CALLBACK OptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) ...@@ -137,8 +137,8 @@ BOOL CALLBACK OptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
137 clipboard_flag = IsDlgButtonChecked(hDlg, IDC_CHECK3); 137 clipboard_flag = IsDlgButtonChecked(hDlg, IDC_CHECK3);
138 cyclic_remove = IsDlgButtonChecked(hDlg, IDC_CHECK4); 138 cyclic_remove = IsDlgButtonChecked(hDlg, IDC_CHECK4);
139 global_filter = IsDlgButtonChecked(hDlg, IDC_CHECK5); 139 global_filter = IsDlgButtonChecked(hDlg, IDC_CHECK5);
140 - setman->SetValue(SETTING_CLIPFLAG, clipboard_flag); 140 + setman->clipboardFlag = clipboard_flag;
141 - setman->SetValue(SETTING_SPLIT_TIME, split_time); 141 + setman->splittingInterval = split_time;
142 if (auto_inject == 0) auto_insert = 0; 142 if (auto_inject == 0) auto_insert = 0;
143 } 143 }
144 case IDCANCEL: 144 case IDCANCEL:
...@@ -187,15 +187,15 @@ BOOL CALLBACK ProcessDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) ...@@ -187,15 +187,15 @@ BOOL CALLBACK ProcessDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
187 pswnd->DetachProcess(); 187 pswnd->DetachProcess();
188 break; 188 break;
189 case IDC_BUTTON5: 189 case IDC_BUTTON5:
190 - pswnd->AddCurrentToProfile(); 190 + pswnd->CreateProfileForSelectedProcess();
191 break; 191 break;
192 case IDC_BUTTON6: 192 case IDC_BUTTON6:
193 - pswnd->RemoveCurrentFromProfile(); 193 + pswnd->DeleteProfileForSelectedProcess();
194 break; 194 break;
195 } 195 }
196 } 196 }
197 return TRUE; 197 return TRUE;
198 - 198 +
199 case WM_NOTIFY: 199 case WM_NOTIFY:
200 { 200 {
201 LPNMHDR dr = (LPNMHDR)lParam; 201 LPNMHDR dr = (LPNMHDR)lParam;
...@@ -223,22 +223,22 @@ LRESULT CALLBACK EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) ...@@ -223,22 +223,22 @@ LRESULT CALLBACK EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
223 switch (message) 223 switch (message)
224 { 224 {
225 case WM_CHAR: //Filter user input. 225 case WM_CHAR: //Filter user input.
226 - if (GetKeyState(VK_CONTROL) & 0x8000) 226 + if (GetKeyState(VK_CONTROL) & 0x8000)
227 + {
228 + if (wParam == 1)
227 { 229 {
228 - if (wParam == 1) 230 + Edit_SetSel(hwndEdit, 0, -1);
229 - { 231 + SendMessage(hwndEdit, WM_COPY, 0, 0);
230 - Edit_SetSel(hwndEdit, 0, -1);
231 - SendMessage(hwndEdit, WM_COPY, 0, 0);
232 - }
233 } 232 }
234 - return 0; 233 + }
234 + return 0;
235 case WM_LBUTTONUP: 235 case WM_LBUTTONUP:
236 - if (hwndEdit) 236 + if (hwndEdit)
237 - SendMessage(hwndEdit, WM_COPY, 0, 0); 237 + SendMessage(hwndEdit, WM_COPY, 0, 0);
238 default: 238 default:
239 - { 239 + {
240 - return proc(hWnd, message, wParam, lParam); 240 + return proc(hWnd, message, wParam, lParam);
241 - } 241 + }
242 242
243 } 243 }
244 } 244 }
...@@ -280,29 +280,29 @@ LRESULT CALLBACK EditCmdProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPar ...@@ -280,29 +280,29 @@ LRESULT CALLBACK EditCmdProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPar
280 280
281 void CreateButtons(HWND hWnd) 281 void CreateButtons(HWND hWnd)
282 { 282 {
283 - hwndProcess = CreateWindow(L"Button", L"프로세스", WS_CHILD | WS_VISIBLE, 283 + hwndProcess = CreateWindow(L"Button", L"프로세스", WS_CHILD | WS_VISIBLE,
284 0, 0, 0, 0, hWnd, 0, hIns, NULL); 284 0, 0, 0, 0, hWnd, 0, hIns, NULL);
285 - hwndOption = CreateWindow(L"Button", L"옵션", WS_CHILD | WS_VISIBLE, 285 + hwndOption = CreateWindow(L"Button", L"옵션", WS_CHILD | WS_VISIBLE,
286 0, 0, 0, 0, hWnd, 0, hIns, NULL); 286 0, 0, 0, 0, hWnd, 0, hIns, NULL);
287 - hwndClear = CreateWindow(L"Button", L"지우기", WS_CHILD | WS_VISIBLE, 287 + hwndClear = CreateWindow(L"Button", L"지우기", WS_CHILD | WS_VISIBLE,
288 0, 0, 0, 0, hWnd, 0, hIns, NULL); 288 0, 0, 0, 0, hWnd, 0, hIns, NULL);
289 - hwndSave = CreateWindow(L"Button", L"저장", WS_CHILD | WS_VISIBLE, 289 + hwndSave = CreateWindow(L"Button", L"저장", WS_CHILD | WS_VISIBLE,
290 0, 0, 0, 0, hWnd, 0, hIns, NULL); 290 0, 0, 0, 0, hWnd, 0, hIns, NULL);
291 - hwndRemoveLink = CreateWindow(L"Button", L"링크해제", WS_CHILD | WS_VISIBLE, 291 + hwndRemoveLink = CreateWindow(L"Button", L"링크해제", WS_CHILD | WS_VISIBLE,
292 0, 0, 0, 0, hWnd, 0, hIns, NULL); 292 0, 0, 0, 0, hWnd, 0, hIns, NULL);
293 - hwndRemoveHook = CreateWindow(L"Button", L"후킹해제", WS_CHILD | WS_VISIBLE, 293 + hwndRemoveHook = CreateWindow(L"Button", L"후킹해제", WS_CHILD | WS_VISIBLE,
294 0, 0, 0, 0, hWnd, 0, hIns, NULL); 294 0, 0, 0, 0, hWnd, 0, hIns, NULL);
295 - hwndTop = CreateWindow(L"Button", L"항상위", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE | BS_CHECKBOX, 295 + hwndTop = CreateWindow(L"Button", L"항상위", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE | BS_CHECKBOX,
296 0, 0, 0, 0, hWnd, 0, hIns, NULL); 296 0, 0, 0, 0, hWnd, 0, hIns, NULL);
297 hwndProcessComboBox = CreateWindow(L"ComboBox", NULL, 297 hwndProcessComboBox = CreateWindow(L"ComboBox", NULL,
298 WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | 298 WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
299 CBS_SORT | WS_VSCROLL | WS_TABSTOP, 299 CBS_SORT | WS_VSCROLL | WS_TABSTOP,
300 0, 0, 0, 0, hWnd, 0, hIns, NULL); 300 0, 0, 0, 0, hWnd, 0, hIns, NULL);
301 hwndCmd = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL, 301 hwndCmd = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL,
302 - WS_CHILD | WS_VISIBLE | ES_NOHIDESEL| ES_LEFT | ES_AUTOHSCROLL, 302 + WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_AUTOHSCROLL,
303 0, 0, 0, 0, hWnd, 0, hIns, NULL); 303 0, 0, 0, 0, hWnd, 0, hIns, NULL);
304 hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL, 304 hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL,
305 - WS_CHILD | WS_VISIBLE | ES_NOHIDESEL| WS_VSCROLL | 305 + WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | WS_VSCROLL |
306 ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL, 306 ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
307 0, 0, 0, 0, hWnd, 0, hIns, NULL); 307 0, 0, 0, 0, hWnd, 0, hIns, NULL);
308 } 308 }
...@@ -336,7 +336,7 @@ void ClickButton(HWND hWnd, HWND h) ...@@ -336,7 +336,7 @@ void ClickButton(HWND hWnd, HWND h)
336 } 336 }
337 else if (h == hwndTop) 337 else if (h == hwndTop)
338 { 338 {
339 - if (Button_GetCheck(h)==BST_CHECKED) 339 + if (Button_GetCheck(h) == BST_CHECKED)
340 { 340 {
341 Button_SetCheck(h, BST_UNCHECKED); 341 Button_SetCheck(h, BST_UNCHECKED);
342 SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE); 342 SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
...@@ -363,7 +363,7 @@ void ClickButton(HWND hWnd, HWND h) ...@@ -363,7 +363,7 @@ void ClickButton(HWND hWnd, HWND h)
363 DWORD pid = std::stoul(str); 363 DWORD pid = std::stoul(str);
364 SaveProcessProfile(pid); 364 SaveProcessProfile(pid);
365 } 365 }
366 - pfman->SaveProfile(); 366 + pfman->SaveProfiles();
367 } 367 }
368 else if (h == hwndRemoveLink) 368 else if (h == hwndRemoveLink)
369 { 369 {
...@@ -372,7 +372,7 @@ void ClickButton(HWND hWnd, HWND h) ...@@ -372,7 +372,7 @@ void ClickButton(HWND hWnd, HWND h)
372 { 372 {
373 DWORD from = std::stoul(str, NULL, 16); 373 DWORD from = std::stoul(str, NULL, 16);
374 if (from != 0) 374 if (from != 0)
375 - IHF_UnLink(from); 375 + Host_UnLink(from);
376 } 376 }
377 } 377 }
378 else if (h == hwndRemoveHook) 378 else if (h == hwndRemoveHook)
...@@ -388,12 +388,12 @@ void ClickButton(HWND hWnd, HWND h) ...@@ -388,12 +388,12 @@ void ClickButton(HWND hWnd, HWND h)
388 entry = entry.substr(i + 1); 388 entry = entry.substr(i + 1);
389 DWORD addr = std::stoul(entry, NULL, 16); 389 DWORD addr = std::stoul(entry, NULL, 16);
390 if (threadNumber != 0) 390 if (threadNumber != 0)
391 - IHF_RemoveHook(pid, addr); 391 + Host_RemoveHook(pid, addr);
392 } 392 }
393 } 393 }
394 } 394 }
395 395
396 -DWORD ThreadFilter(TextThread* thread, BYTE* out,DWORD len, DWORD new_line, PVOID data, bool space) 396 +DWORD ThreadFilter(TextThread* thread, BYTE* out, DWORD len, DWORD new_line, PVOID data, bool space)
397 { 397 {
398 DWORD status = thread->Status(); 398 DWORD status = thread->Status();
399 if (global_filter && !new_line && thread->Number() != 0) 399 if (global_filter && !new_line && thread->Number() != 0)
...@@ -444,7 +444,7 @@ DWORD ThreadFilter(TextThread* thread, BYTE* out,DWORD len, DWORD new_line, PVOI ...@@ -444,7 +444,7 @@ DWORD ThreadFilter(TextThread* thread, BYTE* out,DWORD len, DWORD new_line, PVOI
444 return len; 444 return len;
445 } 445 }
446 446
447 -DWORD ThreadOutput(TextThread* thread, BYTE* out,DWORD len, DWORD new_line, PVOID data, bool space) 447 +DWORD ThreadOutput(TextThread* thread, BYTE* out, DWORD len, DWORD new_line, PVOID data, bool space)
448 { 448 {
449 if (len == 0) 449 if (len == 0)
450 return len; 450 return len;
...@@ -498,11 +498,16 @@ bool GetHookParam(DWORD pid, DWORD hook_addr, HookParam& hp) ...@@ -498,11 +498,16 @@ bool GetHookParam(DWORD pid, DWORD hook_addr, HookParam& hp)
498 return result; 498 return result;
499 } 499 }
500 500
501 -void AddToCombo(TextThread& thread, bool replace) 501 +std::wstring GetEntryString(TextThread& thread)
502 { 502 {
503 - WCHAR entry[512]; 503 + CHAR entry[512];
504 thread.GetEntryString(entry, 512); 504 thread.GetEntryString(entry, 512);
505 - std::wstring entryWithLink(entry); 505 + return toUnicodeString(entry);
506 +}
507 +
508 +std::wstring CreateEntryWithLink(TextThread& thread, std::wstring& entry)
509 +{
510 + std::wstring entryWithLink = entry;
506 if (thread.Link()) 511 if (thread.Link())
507 entryWithLink += L"->" + ToHexString(thread.LinkNumber()); 512 entryWithLink += L"->" + ToHexString(thread.LinkNumber());
508 if (thread.PID() == 0) 513 if (thread.PID() == 0)
...@@ -510,7 +515,14 @@ void AddToCombo(TextThread& thread, bool replace) ...@@ -510,7 +515,14 @@ void AddToCombo(TextThread& thread, bool replace)
510 HookParam hp = {}; 515 HookParam hp = {};
511 if (GetHookParam(thread.PID(), thread.Addr(), hp)) 516 if (GetHookParam(thread.PID(), thread.Addr(), hp))
512 entryWithLink += L" (" + GetCode(hp, thread.PID()) + L")"; 517 entryWithLink += L" (" + GetCode(hp, thread.PID()) + L")";
513 - int i = ComboBox_FindString(hwndCombo, 0, entry); 518 + return entryWithLink;
519 +}
520 +
521 +void AddToCombo(TextThread& thread, bool replace)
522 +{
523 + std::wstring entry = GetEntryString(thread);
524 + std::wstring entryWithLink = CreateEntryWithLink(thread, entry);
525 + int i = ComboBox_FindString(hwndCombo, -1, entry.c_str());
514 if (replace) 526 if (replace)
515 { 527 {
516 int sel = ComboBox_GetCurSel(hwndCombo); 528 int sel = ComboBox_GetCurSel(hwndCombo);
...@@ -531,11 +543,12 @@ void AddToCombo(TextThread& thread, bool replace) ...@@ -531,11 +543,12 @@ void AddToCombo(TextThread& thread, bool replace)
531 543
532 void RemoveFromCombo(TextThread* thread) 544 void RemoveFromCombo(TextThread* thread)
533 { 545 {
534 - WCHAR entry[512]; 546 + CHAR entry[512];
535 thread->GetEntryString(entry, 512); 547 thread->GetEntryString(entry, 512);
548 + std::wstring unicodeEntry = toUnicodeString(entry);
536 if (thread->PID() == 0) 549 if (thread->PID() == 0)
537 - std::wcscat(entry, L"ConsoleOutput"); 550 + unicodeEntry += L"ConsoleOutput";
538 - int i = ComboBox_FindString(hwndCombo, 0, entry); 551 + int i = ComboBox_FindString(hwndCombo, 0, unicodeEntry.c_str());
539 if (i != CB_ERR) 552 if (i != CB_ERR)
540 { 553 {
541 if (ComboBox_DeleteString(hwndCombo, i) == CB_ERR) 554 if (ComboBox_DeleteString(hwndCombo, i) == CB_ERR)
...@@ -595,6 +608,36 @@ DWORD AddRemoveLink(TextThread* thread) ...@@ -595,6 +608,36 @@ DWORD AddRemoveLink(TextThread* thread)
595 return 0; 608 return 0;
596 } 609 }
597 610
611 +bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook);
612 +void AddLinksToHookManager(const Profile* pf, size_t thread_index, const TextThread* thread);
613 +
614 +DWORD ThreadCreate(TextThread* thread)
615 +{
616 + thread->RegisterOutputCallBack(ThreadOutput, 0);
617 + thread->RegisterFilterCallBack(ThreadFilter, 0);
618 + AddToCombo(*thread, false);
619 + const auto& tp = thread->GetThreadParameter();
620 + auto pr = man->GetProcessRecord(tp->pid);
621 + if (pr == NULL)
622 + return 0;
623 + if (IsUnicodeHook(*pr, tp->hook))
624 + thread->Status() |= USING_UNICODE;
625 + auto pf = pfman->GetProfile(tp->pid);
626 + if (!pf)
627 + return 0;
628 + const std::wstring& hook_name = GetHookNameByAddress(*pr, thread->GetThreadParameter()->hook);
629 + auto thread_profile = pf->FindThread(thread->GetThreadParameter(), hook_name);
630 + if (thread_profile != pf->Threads().end())
631 + {
632 + (*thread_profile)->HookManagerIndex() = thread->Number();
633 + auto thread_index = thread_profile - pf->Threads().begin();
634 + AddLinksToHookManager(pf, thread_index, thread);
635 + if (pf->IsThreadSelected(thread_profile))
636 + ThreadReset(thread);
637 + }
638 + return 0;
639 +}
640 +
598 bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook) 641 bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook)
599 { 642 {
600 bool res = false; 643 bool res = false;
...@@ -612,50 +655,21 @@ bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook) ...@@ -612,50 +655,21 @@ bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook)
612 return res; 655 return res;
613 } 656 }
614 657
615 -DWORD ThreadCreate(TextThread* thread) 658 +void AddLinksToHookManager(const Profile* pf, size_t thread_index, const TextThread* thread)
616 { 659 {
617 - thread->RegisterOutputCallBack(ThreadOutput, 0); 660 + for (auto lp = pf->Links().begin(); lp != pf->Links().end(); ++lp)
618 - thread->RegisterFilterCallBack(ThreadFilter, 0);
619 - AddToCombo(*thread, false);
620 - const auto tp = thread->GetThreadParameter();
621 - auto pr = man->GetProcessRecord(tp->pid);
622 - if (pr != NULL)
623 { 661 {
624 - if (IsUnicodeHook(*pr, tp->hook)) 662 + if ((*lp)->FromIndex() == thread_index)
625 - thread->Status() |= USING_UNICODE;
626 - }
627 -
628 - auto pf = pfman->GetProfile(tp->pid);
629 - if (pf)
630 - {
631 - auto thread_profile = pf->FindThreadProfile(*tp);
632 - if (thread_profile != pf->Threads().end())
633 { 663 {
634 - (*thread_profile)->HookManagerIndex() = thread->Number(); 664 + WORD to_index = pf->Threads()[(*lp)->ToIndex()]->HookManagerIndex();
635 - auto thread_profile_index = thread_profile - pf->Threads().begin();
636 - AddLinksToHookManager(*pf, thread_profile_index, *thread);
637 - if (pf->SelectedIndex() == thread_profile_index)
638 - ThreadReset(thread);
639 - }
640 - }
641 - return 0;
642 -}
643 -
644 -void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const TextThread& thread)
645 -{
646 - for (auto lp = pf.Links().begin(); lp != pf.Links().end(); ++lp)
647 - {
648 - if ((*lp)->FromIndex() == thread_profile_index)
649 - {
650 - WORD to_index = pf.Threads()[(*lp)->ToIndex()]->HookManagerIndex();
651 if (to_index != 0) 665 if (to_index != 0)
652 - man->AddLink(thread.Number(), to_index); 666 + man->AddLink(thread->Number(), to_index);
653 } 667 }
654 - if ((*lp)->ToIndex() == thread_profile_index) 668 + if ((*lp)->ToIndex() == thread_index)
655 { 669 {
656 - WORD from_index = pf.Threads()[(*lp)->FromIndex()]->HookManagerIndex(); 670 + WORD from_index = pf->Threads()[(*lp)->FromIndex()]->HookManagerIndex();
657 if (from_index != 0) 671 if (from_index != 0)
658 - man->AddLink(from_index, thread.Number()); 672 + man->AddLink(from_index, thread->Number());
659 } 673 }
660 } 674 }
661 } 675 }
...@@ -663,14 +677,6 @@ void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const ...@@ -663,14 +677,6 @@ void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const
663 DWORD ThreadRemove(TextThread* thread) 677 DWORD ThreadRemove(TextThread* thread)
664 { 678 {
665 RemoveFromCombo(thread); 679 RemoveFromCombo(thread);
666 - const auto tp = thread->GetThreadParameter();
667 - auto pf = pfman->GetProfile(tp->pid);
668 - if (pf)
669 - {
670 - auto thread_profile = pf->FindThreadProfile(*tp);
671 - if (thread_profile != pf->Threads().end())
672 - (*thread_profile)->HookManagerIndex() = 0; // reset hookman index number
673 - }
674 return 0; 680 return 0;
675 } 681 }
676 682
...@@ -684,7 +690,6 @@ DWORD RegisterProcessList(DWORD pid) ...@@ -684,7 +690,6 @@ DWORD RegisterProcessList(DWORD pid)
684 ComboBox_AddString(hwndProcessComboBox, str); 690 ComboBox_AddString(hwndProcessComboBox, str);
685 if (ComboBox_GetCount(hwndProcessComboBox) == 1) 691 if (ComboBox_GetCount(hwndProcessComboBox) == 1)
686 ComboBox_SetCurSel(hwndProcessComboBox, 0); 692 ComboBox_SetCurSel(hwndProcessComboBox, 0);
687 - pfman->FindProfileAndUpdateHookAddresses(pid, path);
688 } 693 }
689 return 0; 694 return 0;
690 } 695 }
...@@ -706,9 +711,6 @@ DWORD RemoveProcessList(DWORD pid) ...@@ -706,9 +711,6 @@ DWORD RemoveProcessList(DWORD pid)
706 711
707 DWORD RefreshProfileOnNewHook(DWORD pid) 712 DWORD RefreshProfileOnNewHook(DWORD pid)
708 { 713 {
709 - auto path = GetProcessPath(pid);
710 - if (!path.empty())
711 - pfman->FindProfileAndUpdateHookAddresses(pid, path);
712 return 0; 714 return 0;
713 } 715 }
714 716
...@@ -716,136 +718,138 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) ...@@ -716,136 +718,138 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
716 { 718 {
717 switch (message) 719 switch (message)
718 { 720 {
719 - case WM_CREATE: 721 + case WM_CREATE:
720 - CreateButtons(hWnd); 722 + CreateButtons(hWnd);
721 - // Add text to the window. 723 + // Add text to the window.
722 - Edit_LimitText(hwndEdit, -1); 724 + Edit_LimitText(hwndEdit, -1);
723 - SendMessage(hwndEdit, WM_INPUTLANGCHANGEREQUEST, 0, 0x411); 725 + SendMessage(hwndEdit, WM_INPUTLANGCHANGEREQUEST, 0, 0x411);
724 - proc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)EditProc); 726 + proc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)EditProc);
725 - proccmd = (WNDPROC)SetWindowLong(hwndCmd, GWL_WNDPROC, (LONG)EditCmdProc); 727 + proccmd = (WNDPROC)SetWindowLong(hwndCmd, GWL_WNDPROC, (LONG)EditCmdProc);
726 - hwndCombo = CreateWindow(L"ComboBox", NULL, 728 + hwndCombo = CreateWindow(L"ComboBox", NULL,
727 - WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST | 729 + WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
728 - CBS_SORT | WS_VSCROLL | WS_TABSTOP, 730 + CBS_SORT | WS_VSCROLL | WS_TABSTOP,
729 - 0, 0, 0, 0, hWnd, 0, hIns, NULL); 731 + 0, 0, 0, 0, hWnd, 0, hIns, NULL);
732 + {
733 + HDC hDC = GetDC(hWnd);
734 + int nHeight = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72);
735 + ReleaseDC(hWnd, hDC);
736 + HFONT hf = CreateFont(nHeight, 0, 0, 0, FW_LIGHT, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET,
737 + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
738 + L"MS Gothic");
739 + hWhiteBrush = GetStockBrush(WHITE_BRUSH);
740 + SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0);
741 + SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hf, 0);
742 + SendMessage(hwndCombo, WM_SETFONT, (WPARAM)hf, 0);
743 + SendMessage(hwndProcessComboBox, WM_SETFONT, (WPARAM)hf, 0);
744 + texts = new TextBuffer(hwndEdit);
745 + man->RegisterThreadCreateCallback(ThreadCreate);
746 + man->RegisterThreadRemoveCallback(ThreadRemove);
747 + man->RegisterThreadResetCallback(ThreadReset);
748 + TextThread* console = man->FindSingle(0);
749 + console->RegisterOutputCallBack(ThreadOutput, NULL);
750 + AddToCombo(*console, false);
751 + man->RegisterProcessAttachCallback(RegisterProcessList);
752 + man->RegisterProcessDetachCallback(RemoveProcessList);
753 + man->RegisterProcessNewHookCallback(RefreshProfileOnNewHook);
754 + man->RegisterAddRemoveLinkCallback(AddRemoveLink);
755 + man->RegisterConsoleCallback(ConsoleOutput);
756 + Host_Start();
730 { 757 {
731 - HFONT hf = CreateFont(18, 0, 0, 0, FW_LIGHT, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, ANTIALIASED_QUALITY, 0, 758 + static const WCHAR program_name[] = L"Interactive Text Hooker";
732 - L"MS Gothic"); 759 + //static const WCHAR program_version[] = L"3.0";
733 - hWhiteBrush = GetStockBrush(WHITE_BRUSH); 760 + static WCHAR version_info[256];
734 - SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0); 761 + std::swprintf(version_info, L"%s %s (%s)", program_name, program_version, build_date);
735 - SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hf, 0); 762 + man->AddConsoleOutput(version_info);
736 - SendMessage(hwndCombo, WM_SETFONT, (WPARAM)hf, 0); 763 + man->AddConsoleOutput(InitMessage);
737 - SendMessage(hwndProcessComboBox, WM_SETFONT, (WPARAM)hf, 0);
738 - texts = new TextBuffer(hwndEdit);
739 - man->RegisterThreadCreateCallback(ThreadCreate);
740 - man->RegisterThreadRemoveCallback(ThreadRemove);
741 - man->RegisterThreadResetCallback(ThreadReset);
742 - TextThread* console = man->FindSingle(0);
743 - console->RegisterOutputCallBack(ThreadOutput, NULL);
744 - AddToCombo(*console, false);
745 - man->RegisterProcessAttachCallback(RegisterProcessList);
746 - man->RegisterProcessDetachCallback(RemoveProcessList);
747 - man->RegisterProcessNewHookCallback(RefreshProfileOnNewHook);
748 - man->RegisterAddRemoveLinkCallback(AddRemoveLink);
749 - man->RegisterConsoleCallback(ConsoleOutput);
750 - IHF_Start();
751 - {
752 - static const WCHAR program_name[] = L"Interactive Text Hooker";
753 - //static const WCHAR program_version[] = L"3.0";
754 - static WCHAR version_info[256];
755 - std::swprintf(version_info, L"%s %s (%s)", program_name, program_version, build_date);
756 - man->AddConsoleOutput(version_info);
757 - man->AddConsoleOutput(InitMessage);
758 - }
759 -
760 - if (background == 0)
761 - man->AddConsoleOutput(BackgroundMsg);
762 - if (!IHF_IsAdmin())
763 - man->AddConsoleOutput(NotAdmin);
764 } 764 }
765 765
766 - return 0; 766 + if (background == 0)
767 - case WM_COMMAND: 767 + man->AddConsoleOutput(BackgroundMsg);
768 + }
769 +
770 + return 0;
771 + case WM_COMMAND:
772 + {
773 + DWORD wmId, wmEvent, dwId;
774 + wmId = LOWORD(wParam);
775 + wmEvent = HIWORD(wParam);
776 + switch (wmEvent)
777 + {
778 + case EN_VSCROLL:
779 + {
780 + SCROLLBARINFO info = { sizeof(info) };
781 + GetScrollBarInfo(hwndEdit, OBJID_VSCROLL, &info);
782 + InvalidateRect(hwndEdit, 0, 1);
783 + ValidateRect(hwndEdit, &info.rcScrollBar);
784 + RedrawWindow(hwndEdit, 0, 0, RDW_ERASE);
785 + }
786 + break;
787 + case CBN_SELENDOK:
788 + {
789 + if ((HWND)lParam == hwndProcessComboBox)
790 + return 0;
791 + dwId = ComboBox_GetCurSel(hwndCombo);
792 + int len = ComboBox_GetLBTextLen(hwndCombo, dwId);
793 + if (len > 0)
768 { 794 {
769 - DWORD wmId, wmEvent, dwId; 795 + LPWSTR pwcEntry = new WCHAR[len + 1];
770 - wmId = LOWORD(wParam); 796 + len = ComboBox_GetLBText(hwndCombo, dwId, pwcEntry);
771 - wmEvent = HIWORD(wParam); 797 + DWORD num = std::stoul(pwcEntry, NULL, 16);
772 - switch (wmEvent) 798 + man->SelectCurrent(num);
773 - { 799 + delete[] pwcEntry;
774 - case EN_VSCROLL:
775 - {
776 - SCROLLBARINFO info={sizeof(info)};
777 - GetScrollBarInfo(hwndEdit, OBJID_VSCROLL, &info);
778 - InvalidateRect(hwndEdit, 0, 1);
779 - ValidateRect(hwndEdit, &info.rcScrollBar);
780 - RedrawWindow(hwndEdit, 0, 0, RDW_ERASE);
781 - }
782 - break;
783 - case CBN_SELENDOK:
784 - {
785 - if ((HWND)lParam == hwndProcessComboBox)
786 - return 0;
787 - dwId = ComboBox_GetCurSel(hwndCombo);
788 - int len = ComboBox_GetLBTextLen(hwndCombo, dwId);
789 - if (len > 0)
790 - {
791 - LPWSTR pwcEntry = new WCHAR[len + 1];
792 - len = ComboBox_GetLBText(hwndCombo, dwId, pwcEntry);
793 - DWORD num = std::stoul(pwcEntry, NULL, 16);
794 - man->SelectCurrent(num);
795 - delete[] pwcEntry;
796 - }
797 - }
798 - return 0;
799 - case BN_CLICKED:
800 - ClickButton(hWnd, (HWND)lParam);
801 - break;
802 - default:
803 - break;
804 - }
805 } 800 }
801 + }
802 + return 0;
803 + case BN_CLICKED:
804 + ClickButton(hWnd, (HWND)lParam);
806 break; 805 break;
807 - case WM_SETFOCUS:
808 - SetFocus(hwndEdit);
809 - return 0;
810 - case WM_SIZE:
811 - {
812 - WORD width = LOWORD(lParam);
813 - WORD height = HIWORD(lParam);
814 - DWORD l = width / 7;
815 - WORD h = HIWORD(GetDialogBaseUnits()); // height of the system font
816 - h = h + (h / 2);
817 - HDC hDC = GetDC(hWnd);
818 - RECT rc;
819 - GetClientRect(hWnd, &rc);
820 - FillRect(hDC, &rc, hWhiteBrush);
821 - ReleaseDC(hWnd, hDC);
822 - MoveWindow(hwndProcess, 0, 0, l, h, TRUE);
823 - MoveWindow(hwndOption, l * 1, 0, l, h, TRUE);
824 - MoveWindow(hwndTop, l * 2, 0, l, h, TRUE);
825 - MoveWindow(hwndClear, l * 3, 0, l, h, TRUE);
826 - MoveWindow(hwndRemoveLink, l * 4, 0, l, h, TRUE);
827 - MoveWindow(hwndRemoveHook, l * 5, 0, l, h, TRUE);
828 - MoveWindow(hwndSave, l * 6, 0, width - 6 * l, h, TRUE);
829 - l *= 2;
830 - MoveWindow(hwndProcessComboBox, 0, h, l, 200, TRUE);
831 - MoveWindow(hwndCmd, l, h, width - l, h, TRUE);
832 - MoveWindow(hwndCombo, 0, h * 2, width, 200, TRUE);
833 - h *= 3;
834 - MoveWindow(hwndEdit, 0, h, width, height - h, TRUE);
835 - }
836 - return 0;
837 - case WM_DESTROY:
838 - man->RegisterThreadCreateCallback(0);
839 - man->RegisterThreadRemoveCallback(0);
840 - man->RegisterThreadResetCallback(0);
841 - man->RegisterProcessAttachCallback(0);
842 - man->RegisterProcessDetachCallback(0);
843 - //delete texts;
844 - SaveSettings();
845 - PostQuitMessage(0);
846 - return 0;
847 default: 806 default:
848 - return DefWindowProc(hWnd, message, wParam, lParam); 807 + break;
808 + }
809 + }
810 + break;
811 + case WM_SETFOCUS:
812 + SetFocus(hwndEdit);
813 + return 0;
814 + case WM_SIZE:
815 + {
816 + WORD width = LOWORD(lParam);
817 + WORD height = HIWORD(lParam);
818 + DWORD l = width / 7;
819 + WORD h = HIWORD(GetDialogBaseUnits()); // height of the system font
820 + h = h + (h / 2);
821 + HDC hDC = GetDC(hWnd);
822 + RECT rc;
823 + GetClientRect(hWnd, &rc);
824 + FillRect(hDC, &rc, hWhiteBrush);
825 + ReleaseDC(hWnd, hDC);
826 + MoveWindow(hwndProcess, 0, 0, l, h, TRUE);
827 + MoveWindow(hwndOption, l * 1, 0, l, h, TRUE);
828 + MoveWindow(hwndTop, l * 2, 0, l, h, TRUE);
829 + MoveWindow(hwndClear, l * 3, 0, l, h, TRUE);
830 + MoveWindow(hwndRemoveLink, l * 4, 0, l, h, TRUE);
831 + MoveWindow(hwndRemoveHook, l * 5, 0, l, h, TRUE);
832 + MoveWindow(hwndSave, l * 6, 0, width - 6 * l, h, TRUE);
833 + l *= 2;
834 + MoveWindow(hwndProcessComboBox, 0, h, l, 200, TRUE);
835 + MoveWindow(hwndCmd, l, h, width - l, h, TRUE);
836 + MoveWindow(hwndCombo, 0, h * 2, width, 200, TRUE);
837 + h *= 3;
838 + MoveWindow(hwndEdit, 0, h, width, height - h, TRUE);
839 + }
840 + return 0;
841 + case WM_DESTROY:
842 + man->RegisterThreadCreateCallback(0);
843 + man->RegisterThreadRemoveCallback(0);
844 + man->RegisterThreadResetCallback(0);
845 + man->RegisterProcessAttachCallback(0);
846 + man->RegisterProcessDetachCallback(0);
847 + //delete texts;
848 + SaveSettings();
849 + PostQuitMessage(0);
850 + return 0;
851 + default:
852 + return DefWindowProc(hWnd, message, wParam, lParam);
849 } 853 }
850 return 0; 854 return 0;
851 } 855 }
......
...@@ -16,4 +16,5 @@ ...@@ -16,4 +16,5 @@
16 */ 16 */
17 17
18 #pragma once 18 #pragma once
19 +
19 #include "ITH.h" 20 #include "ITH.h"
......
1 +# config.pri
2 +# DEFINES += _SECURE_SCL=0 _SCL_SECURE_NO_WARNINGS
3 +# DEFINES += _CRT_SECURE_NO_WARNINGS
4 +
1 cmake_minimum_required(VERSION 2.8) 5 cmake_minimum_required(VERSION 2.8)
2 6
3 set(CMAKE_CONFIGURATION_TYPES Debug Release) 7 set(CMAKE_CONFIGURATION_TYPES Debug Release)
4 8
5 project(vnr) 9 project(vnr)
6 10
7 -set(WDK_HOME "C:\\WinDDK\\7600.16385.1" CACHE FILEPATH "path to the Windows DDK directory") 11 +set(WDK_HOME "C:\\WinDDK\\7600.16385.1" CACHE FILEPATH "Windows Driver Kit path")
8 12
9 add_definitions( 13 add_definitions(
10 - -DUNICODE 14 + /DUNICODE
11 - -D_UNICODE 15 + /D_UNICODE
16 + /D_SECURE_SCL=0
17 + /D_SCL_SECURE_NO_WARNINGS
18 + /D_CRT_SECURE_NO_WARNINGS
12 ) 19 )
13 20
14 -include_directories(${PROJECT_SOURCE_DIR}) 21 +include_directories(
15 - 22 + ${PROJECT_SOURCE_DIR}
16 -set(common_src 23 + ${PROJECT_SOURCE_DIR}/texthook
17 - ${PROJECT_SOURCE_DIR}/ith/common/const.h
18 - ${PROJECT_SOURCE_DIR}/ith/common/defs.h
19 - ${PROJECT_SOURCE_DIR}/ith/common/except.h
20 - ${PROJECT_SOURCE_DIR}/ith/common/growl.h
21 - ${PROJECT_SOURCE_DIR}/ith/common/memory.h
22 - ${PROJECT_SOURCE_DIR}/ith/common/types.h
23 -)
24 -
25 -set(import_src
26 - ${PROJECT_SOURCE_DIR}/ith/import/mono/funcinfo.h
27 - ${PROJECT_SOURCE_DIR}/ith/import/mono/types.h
28 - ${PROJECT_SOURCE_DIR}/ith/import/ppsspp/funcinfo.h
29 ) 24 )
30 25
31 -add_subdirectory(ith/hook) 26 +add_subdirectory(vnrhook)
32 -add_subdirectory(ith/host) 27 +add_subdirectory(texthook/host)
33 -add_subdirectory(ith/sys) 28 +add_subdirectory(ithsys)
29 +add_subdirectory(profile)
......
...@@ -72,8 +72,8 @@ win32 { ...@@ -72,8 +72,8 @@ win32 {
72 ## External Libraries 72 ## External Libraries
73 73
74 win32 { 74 win32 {
75 - D3D_HOME = "$$PROGRAMFILES/Microsoft DirectX SDK" 75 + D3D_HOME = "C:/Program Files/Microsoft DirectX SDK"
76 - DETOURS_HOME = "$$PROGRAMFILES/Microsoft Research/Detours Express 3.0" 76 + DETOURS_HOME = "C:/Program Files/Microsoft Research/Detours Express 3.0"
77 #DEV_HOME = c:/dev 77 #DEV_HOME = c:/dev
78 DEV_HOME = z:/local/windows/developer 78 DEV_HOME = z:/local/windows/developer
79 BOOST_HOME = $$DEV_HOME/boost/build 79 BOOST_HOME = $$DEV_HOME/boost/build
...@@ -81,8 +81,8 @@ win32 { ...@@ -81,8 +81,8 @@ win32 {
81 MSIME_HOME = $$DEV_HOME/msime 81 MSIME_HOME = $$DEV_HOME/msime
82 #PYTHON_HOME = $$ROOTDIR/../Python 82 #PYTHON_HOME = $$ROOTDIR/../Python
83 #PYTHON_HOME = C:/Python 83 #PYTHON_HOME = C:/Python
84 - PYTHON_HOME = Z:/Local/Windows/Developer/Python 84 + PYTHON_HOME = $$DEV_HOME/python
85 - PYSIDE_HOME = $$PYTHON_HOME/Lib/site-packages/PySide 85 + PYSIDE_HOME = $$PYTHON_HOME/lib/site-packages/PySide
86 QT_HOME = c:/qt/4 86 QT_HOME = c:/qt/4
87 QT_SRC = c:/qt 87 QT_SRC = c:/qt
88 SAPI_HOME = "$$PROGRAMFILES/Microsoft Speech SDK 5.1" 88 SAPI_HOME = "$$PROGRAMFILES/Microsoft Speech SDK 5.1"
...@@ -161,7 +161,7 @@ win32 { ...@@ -161,7 +161,7 @@ win32 {
161 QMAKE_CXXFLAGS_EXCEPTIONS_ON += -EHa 161 QMAKE_CXXFLAGS_EXCEPTIONS_ON += -EHa
162 } 162 }
163 163
164 - CONFIG(noeh) { # No Exception handler 164 + CONFIG(noeh) { # No exception handler
165 message(CONFIG noeh) 165 message(CONFIG noeh)
166 #CONFIG -= rtti #-exceptions -stl 166 #CONFIG -= rtti #-exceptions -stl
167 QMAKE_CXXFLAGS += /GR- 167 QMAKE_CXXFLAGS += /GR-
...@@ -175,7 +175,7 @@ win32 { ...@@ -175,7 +175,7 @@ win32 {
175 } 175 }
176 } 176 }
177 177
178 - CONFIG(nosafeseh) { # No Exception handler 178 + CONFIG(nosafeseh) { # No safe exception handler
179 message(CONFIG nosafeseh) 179 message(CONFIG nosafeseh)
180 180
181 # Disable SafeSEH table 181 # Disable SafeSEH table
......
1 @echo off 1 @echo off
2 setlocal 2 setlocal
3 if [%1] == [] ( 3 if [%1] == [] (
4 - echo usage: copy_vnr <path-to-Sakura-directory> 4 + echo usage: copy_vnr path_to_Sakura
5 goto :EOF 5 goto :EOF
6 ) 6 )
7 xcopy %1\config.pri . /S /Y /I 7 xcopy %1\config.pri . /S /Y /I
8 xcopy %1\cpp\libs\ccutil ccutil /S /Y /I 8 xcopy %1\cpp\libs\ccutil ccutil /S /Y /I
9 xcopy %1\cpp\libs\cpputil cpputil /S /Y /I 9 xcopy %1\cpp\libs\cpputil cpputil /S /Y /I
10 xcopy %1\cpp\libs\disasm disasm /S /Y /I /EXCLUDE:exclude.txt 10 xcopy %1\cpp\libs\disasm disasm /S /Y /I /EXCLUDE:exclude.txt
11 -xcopy %1\cpp\plugins\ith ith /S /Y /I 11 +xcopy %1\cpp\libs\hashutil hashutil /S /Y /I
12 +xcopy %1\cpp\plugins\ithsys ithsys /S /Y /I
13 +xcopy %1\cpp\plugins\vnrhook vnrhook /S /Y /I
14 +xcopy %1\cpp\plugins\texthook texthook /S /Y /I /EXCLUDE:exclude.txt
12 xcopy %1\cpp\libs\memdbg memdbg /S /Y /I 15 xcopy %1\cpp\libs\memdbg memdbg /S /Y /I
13 xcopy %1\cpp\libs\ntdll ntdll /S /Y /I 16 xcopy %1\cpp\libs\ntdll ntdll /S /Y /I
14 xcopy %1\cpp\libs\ntinspect ntinspect /S /Y /I 17 xcopy %1\cpp\libs\ntinspect ntinspect /S /Y /I
18 +xcopy %1\cpp\libs\winkey winkey /S /Y /I
15 xcopy %1\cpp\libs\winmaker winmaker /S /Y /I 19 xcopy %1\cpp\libs\winmaker winmaker /S /Y /I
16 xcopy %1\cpp\libs\winmutex winmutex /S /Y /I 20 xcopy %1\cpp\libs\winmutex winmutex /S /Y /I
17 xcopy %1\cpp\libs\winversion winversion /S /Y /I 21 xcopy %1\cpp\libs\winversion winversion /S /Y /I
18 xcopy %1\cpp\libs\winseh winseh /S /Y /I 22 xcopy %1\cpp\libs\winseh winseh /S /Y /I
23 +xcopy %1\cpp\libs\wintimer wintimer /S /Y /I
24 +xcopy %1\cpp\libs\windbg windbg /S /Y /I
25 +xcopy %1\cpp\libs\sakurakit sakurakit /S /Y /I
26 +xcopy %1\cpp\libs\mono mono /S /Y /I
19 27
20 endlocal 28 endlocal
......
...@@ -19,8 +19,8 @@ inline size_t cpp_basic_strlen(const charT *s) ...@@ -19,8 +19,8 @@ inline size_t cpp_basic_strlen(const charT *s)
19 return p - s; 19 return p - s;
20 } 20 }
21 21
22 -inline size_t cpp_strlen(const char *s) { return cpp_basic_strlen(s); } 22 +inline size_t cpp_strlen(const char *s) { return cpp_basic_strlen<char>(s); }
23 -inline size_t cpp_wstrlen(const wchar_t *s) { return cpp_basic_strlen(s); } 23 +inline size_t cpp_wstrlen(const wchar_t *s) { return cpp_basic_strlen<wchar_t>(s); }
24 24
25 template <typename charT> 25 template <typename charT>
26 inline size_t cpp_basic_strnlen(const charT *s, size_t n) 26 inline size_t cpp_basic_strnlen(const charT *s, size_t n)
...@@ -30,8 +30,8 @@ inline size_t cpp_basic_strnlen(const charT *s, size_t n) ...@@ -30,8 +30,8 @@ inline size_t cpp_basic_strnlen(const charT *s, size_t n)
30 return p - s; 30 return p - s;
31 } 31 }
32 32
33 -inline size_t cpp_strnlen(const char *s, size_t n) { return cpp_basic_strnlen(s, n); } 33 +inline size_t cpp_strnlen(const char *s, size_t n) { return cpp_basic_strnlen<char>(s, n); }
34 -inline size_t cpp_wstrnlen(const wchar_t *s, size_t n) { return cpp_basic_strnlen(s, n); } 34 +inline size_t cpp_wstrnlen(const wchar_t *s, size_t n) { return cpp_basic_strnlen<wchar_t>(s, n); }
35 35
36 // strnchr 36 // strnchr
37 37
...@@ -45,19 +45,15 @@ inline size_t cpp_wstrnlen(const wchar_t *s, size_t n) { return cpp_basic_strnle ...@@ -45,19 +45,15 @@ inline size_t cpp_wstrnlen(const wchar_t *s, size_t n) { return cpp_basic_strnle
45 return nullptr; \ 45 return nullptr; \
46 } 46 }
47 template <typename charT> 47 template <typename charT>
48 -inline charT *cpp_basic_strnchr(charT *s, int c, size_t n) 48 +inline charT *cpp_basic_strnchr(charT *s, charT c, size_t n) cpp_basic_strnchr_(s, c, n)
49 -cpp_basic_strnchr_(s, c, n)
50 -
51 template <typename charT> 49 template <typename charT>
52 -inline const charT *cpp_basic_strnchr(const charT *s, int c, size_t n) 50 +inline const charT *cpp_basic_strnchr(const charT *s, charT c, size_t n) cpp_basic_strnchr_(s, c, n)
53 -cpp_basic_strnchr_(s, c, n)
54 51
55 // The same as memchr 52 // The same as memchr
56 -inline char *cpp_strnchr(char *s, int c, size_t n) { return cpp_basic_strnchr(s, c, n); } 53 +inline char *cpp_strnchr(char *s, char c, size_t n) { return cpp_basic_strnchr<char>(s, c, n); }
57 -inline const char *cpp_strnchr(const char *s, int c, size_t n) { return cpp_basic_strnchr(s, c, n); } 54 +inline const char *cpp_strnchr(const char *s, char c, size_t n) { return cpp_basic_strnchr<char>(s, c, n); }
58 - 55 +inline wchar_t *cpp_wcsnchr(wchar_t *s, wchar_t c, size_t n) { return cpp_basic_strnchr<wchar_t>(s, c, n); }
59 -inline wchar_t *cpp_wcsnchr(wchar_t *s, int c, size_t n) { return cpp_basic_strnchr(s, c, n); } 56 +inline const wchar_t *cpp_wcsnchr(const wchar_t *s, wchar_t c, size_t n) { return cpp_basic_strnchr<wchar_t>(s, c, n); }
60 -inline const wchar_t *cpp_wcsnchr(const wchar_t *s, int c, size_t n) { return cpp_basic_strnchr(s, c, n); }
61 57
62 // strnstr 58 // strnstr
63 59
...@@ -72,25 +68,19 @@ inline const wchar_t *cpp_wcsnchr(const wchar_t *s, int c, size_t n) { return cp ...@@ -72,25 +68,19 @@ inline const wchar_t *cpp_wcsnchr(const wchar_t *s, int c, size_t n) { return cp
72 } 68 }
73 69
74 template <typename charT> 70 template <typename charT>
75 -inline charT *cpp_basic_strnstr(charT *s, const charT *r, size_t n) 71 +inline charT *cpp_basic_strnstr(charT *s, const charT *r, size_t n) cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
76 -cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
77 -
78 template <typename charT> 72 template <typename charT>
79 -inline const charT *cpp_basic_strnstr(const charT *s, const charT *r, size_t n) 73 +inline const charT *cpp_basic_strnstr(const charT *s, const charT *r, size_t n) cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
80 -cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
81 74
82 template <> 75 template <>
83 -inline wchar_t *cpp_basic_strnstr<wchar_t>(wchar_t *s, const wchar_t *r, size_t n) 76 +inline wchar_t *cpp_basic_strnstr<wchar_t>(wchar_t *s, const wchar_t *r, size_t n) cpp_basic_strnstr_(s, n, r, ::wcslen(r), ::wcsncmp)
84 -cpp_basic_strnstr_(s, n, r, ::wcslen(r), ::wcsncmp)
85 -
86 template <> 77 template <>
87 -inline const wchar_t *cpp_basic_strnstr<wchar_t>(const wchar_t *s, const wchar_t *r, size_t n) 78 +inline const wchar_t *cpp_basic_strnstr<wchar_t>(const wchar_t *s, const wchar_t *r, size_t n) cpp_basic_strnstr_(s, n, r, ::wcslen(r), ::wcsncmp)
88 -cpp_basic_strnstr_(s, n, r, ::wcslen(r), ::wcsncmp)
89 79
90 -inline char *cpp_strnstr(char *s, const char *r, size_t n) { return cpp_basic_strnstr(s, r, n); } 80 +inline char *cpp_strnstr(char *s, const char *r, size_t n) { return cpp_basic_strnstr<char>(s, r, n); }
91 -inline const char *cpp_strnstr(const char *s, const char *r, size_t n) { return cpp_basic_strnstr(s, r, n); } 81 +inline const char *cpp_strnstr(const char *s, const char *r, size_t n) { return cpp_basic_strnstr<char>(s, r, n); }
92 -inline wchar_t *cpp_wcsnstr(wchar_t *s, const wchar_t *r, size_t n) { return cpp_basic_strnstr(s, r, n); } 82 +inline wchar_t *cpp_wcsnstr(wchar_t *s, const wchar_t *r, size_t n) { return cpp_basic_strnstr<wchar_t>(s, r, n); }
93 -inline const wchar_t *cpp_wcsnstr(const wchar_t *s, const wchar_t *r, size_t n) { return cpp_basic_strnstr(s, r, n); } 83 +inline const wchar_t *cpp_wcsnstr(const wchar_t *s, const wchar_t *r, size_t n) { return cpp_basic_strnstr<wchar_t>(s, r, n); }
94 84
95 // strnpbrk 85 // strnpbrk
96 86
......
...@@ -4,9 +4,23 @@ ...@@ -4,9 +4,23 @@
4 // cpplocale.h 4 // cpplocale.h
5 // 9/26/2014 jichi 5 // 9/26/2014 jichi
6 6
7 -#include <codecvt>
8 #include <locale> 7 #include <locale>
9 8
9 +#ifdef WITHOUT_CXX_CODECVT
10 +// http://www.boost.org/doc/libs/1_48_0/libs/serialization/doc/codecvt.html
11 +# define BOOST_UTF8_BEGIN_NAMESPACE
12 +# define BOOST_UTF8_END_NAMESPACE
13 +# define BOOST_UTF8_DECL
14 +# include <boost/detail/utf8_codecvt_facet.hpp>
15 +# include <boost/detail/utf8_codecvt_facet.ipp> // WARNING: This implementation should only be included ONCE
16 +# define CPPLOCALE_NEW_FACET_UTF8(charT) (new utf8_codecvt_facet) // charT is ignored and assumed to be wchar_t
17 +//# include <boost/detail/serialization/utf8_codecvt_facet.hpp>
18 +//# define CPPLOCALE_NEW_FACET_UTF8(charT) (new utf8_codecvt_facet<charT>)
19 +#else
20 +# include <codecvt>
21 +# define CPPLOCALE_NEW_FACET_UTF8(charT) (new std::codecvt_utf8<charT, 0x10ffff, std::consume_header>)
22 +#endif // WITHOUT_CXX_CODECVT
23 +
10 //#include <boost/locale.hpp> 24 //#include <boost/locale.hpp>
11 25
12 // See: http://stackoverflow.com/questions/20195262/how-to-read-an-utf-8-encoded-file-containing-chinese-characters-and-output-them 26 // See: http://stackoverflow.com/questions/20195262/how-to-read-an-utf-8-encoded-file-containing-chinese-characters-and-output-them
...@@ -15,7 +29,7 @@ ...@@ -15,7 +29,7 @@
15 // - 0x10ffff is the default maximum value. 29 // - 0x10ffff is the default maximum value.
16 // - std::consume_header will skip the leading encoding byte from the input. 30 // - std::consume_header will skip the leading encoding byte from the input.
17 template <class charT> 31 template <class charT>
18 -inline std::locale cpp_utf8_locale(std::locale init = std::locale()) 32 +inline std::locale cpp_utf8_locale(std::locale init = std::locale()) //::empty())
19 -{ return std::locale(init, new std::codecvt_utf8<charT, 0x10ffff, std::consume_header>()); } 33 +{ return std::locale(init, CPPLOCALE_NEW_FACET_UTF8(charT)); }
20 34
21 #endif // CPPLOCALE_H 35 #endif // CPPLOCALE_H
......
...@@ -4,33 +4,15 @@ ...@@ -4,33 +4,15 @@
4 // cppstring.h 4 // cppstring.h
5 // 10/12/2014 jichi 5 // 10/12/2014 jichi
6 6
7 -#include <cstring> 7 +/#include <string>
8 -#include <string>
9 8
10 // Initializers 9 // Initializers
11 10
12 -template <typename charT, typename stringT> 11 +template <typename charT>
13 -inline std::basic_string<charT> cpp_basic_string_of(const stringT &s) 12 +inline std::basic_string<charT> cpp_basic_string_of(const std::string &s)
14 -{ return std::basic_string<charT>(s.cbegin(), s.cend()); } 13 +{ return std::basic_string<charT>(s.begin(), s.end()); }
15 14
16 -template <typename stringT> 15 +inline std::wstring cpp_wstring_of(const std::string &s)
17 -inline std::string cpp_string_of(const stringT &s) 16 +{ return std::wstring(s.begin(), s.end()); }
18 -{ return std::string(s.cbegin(), s.cend()); }
19 -
20 -inline std::string cpp_string_of(const char *s)
21 -{ return s; }
22 -
23 -inline std::string cpp_string_of(const wchar_t *s)
24 -{ return std::string(s, s + ::wcslen(s)); }
25 -
26 -template <typename stringT>
27 -inline std::wstring cpp_wstring_of(const stringT &s)
28 -{ return std::wstring(s.cbegin(), s.cend()); }
29 -
30 -inline std::wstring cpp_wstring_of(const wchar_t *s)
31 -{ return s; }
32 -
33 -inline std::wstring cpp_wstring_of(const char *s)
34 -{ return std::wstring(s, s + ::strlen(s)); }
35 17
36 #endif // CPPSTRING_H 18 #endif // CPPSTRING_H
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
9 // 3024b815 0f1302 movlps qword ptr ds:[edx],xmm0 9 // 3024b815 0f1302 movlps qword ptr ds:[edx],xmm0
10 10
11 #include "disasm.h" 11 #include "disasm.h"
12 +#include <windows.h>
12 13
13 // disasm_flag values: 14 // disasm_flag values:
14 enum : unsigned { 15 enum : unsigned {
...@@ -29,21 +30,22 @@ DISASM_BEGIN_NAMESPACE ...@@ -29,21 +30,22 @@ DISASM_BEGIN_NAMESPACE
29 // But the are currently unused and could make disasm thread-unsafe 30 // But the are currently unused and could make disasm thread-unsafe
30 namespace { // unnamed 31 namespace { // unnamed
31 32
32 -BYTE disasm_seg, // CS DS ES SS FS GS 33 +BYTE disasm_seg // CS DS ES SS FS GS
33 - disasm_rep, // REPZ/REPNZ 34 + , disasm_rep // REPZ/REPNZ
34 - disasm_opcode, // opcode 35 + , disasm_opcode // opcode
35 - disasm_opcode2, // used when opcode==0f 36 + , disasm_opcode2 // used when opcode==0f
36 - disasm_modrm, // modxxxrm 37 + , disasm_modrm // modxxxrm
37 - disasm_sib, // scale-index-base 38 + , disasm_sib // scale-index-base
38 - disasm_mem[8], // mem addr value 39 + , disasm_mem[8] // mem addr value
39 - disasm_data[8]; // data value 40 + , disasm_data[8] // data value
41 + ;
40 42
41 } // unnamed namespace 43 } // unnamed namespace
42 44
43 // return: length if success, 0 if error 45 // return: length if success, 0 if error
44 -int disasm(const BYTE *opcode0) 46 +size_t disasm(const void *opcode0)
45 { 47 {
46 - const BYTE *opcode = opcode0; 48 + const BYTE *opcode = (const BYTE *)opcode0;
47 49
48 DWORD disasm_len = 0, // 0 if error 50 DWORD disasm_len = 0, // 0 if error
49 disasm_flag = 0, // C_xxx 51 disasm_flag = 0, // C_xxx
...@@ -253,7 +255,7 @@ retry: ...@@ -253,7 +255,7 @@ retry:
253 for (DWORD i = 0; i < disasm_datasize; i++) 255 for (DWORD i = 0; i < disasm_datasize; i++)
254 disasm_data[i] = *opcode++; 256 disasm_data[i] = *opcode++;
255 257
256 - disasm_len = opcode - opcode0; 258 + disasm_len = opcode - (const BYTE *)opcode0;
257 259
258 return disasm_len; 260 return disasm_len;
259 } // disasm 261 } // disasm
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
4 4
5 // Include typedef of BYTE 5 // Include typedef of BYTE
6 //#include <windef.h> 6 //#include <windef.h>
7 -#include <windows.h> 7 +//#include <windows.h>
8 8
9 //#ifdef QT_CORE_LIB 9 //#ifdef QT_CORE_LIB
10 //# include <qt_windows.h> 10 //# include <qt_windows.h>
...@@ -20,7 +20,13 @@ ...@@ -20,7 +20,13 @@
20 #endif 20 #endif
21 21
22 DISASM_BEGIN_NAMESPACE 22 DISASM_BEGIN_NAMESPACE
23 -int disasm(const BYTE *opcode0); // return: op length if success, 0 if error 23 +/**
24 + * This function can do more, but currently only used to estimate the length of an instruction.
25 + * Warning: The current implementation is stateful and hence not thread-safe.
26 + * @param address of the instruction to look at
27 + * @return length of the instruction at the address or 0 if failed
28 + */
29 +size_t disasm(const void *address);
24 DISASM_END_NAMESPACE 30 DISASM_END_NAMESPACE
25 31
26 // EOF 32 // EOF
......
1 +#ifndef HASHSTR_H
2 +#define HASHSTR_H
3 +
4 +// hashstr.h
5 +// 8/1/2011
6 +// See: http://www.cse.yorku.ca/~oz/hash.html
7 +
8 +#include "hashutil/hashutil.h"
9 +#include <cstdint>
10 +
11 +HASHUTIL_BEGIN_NAMESPACE
12 +
13 +enum : uint64_t { djb2_hash0 = 5381 };
14 +
15 +/// djb2: h = h*33 + c
16 +template <typename charT>
17 +inline uint64_t djb2(const charT *str, uint64_t hash = djb2_hash0)
18 +{
19 + charT c;
20 + while ((c = *str++))
21 + hash = ((hash << 5) + hash) + c; // hash * 33 + c
22 + return hash;
23 +}
24 +
25 +/// n: length
26 +template <typename charT>
27 +inline uint64_t djb2_n(const charT *str, size_t len, uint64_t hash = djb2_hash0)
28 +{
29 + while (len--)
30 + hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
31 + return hash;
32 +}
33 +
34 +/// sdbm: hash(i) = hash(i - 1) * 65599 + str[i];
35 +template <typename charT>
36 +inline uint64_t sdbm(const charT *str, uint64_t hash = 0)
37 +{
38 + charT c;
39 + while ((c = *str++))
40 + hash = c + (hash << 6) + (hash << 16) - hash;
41 + return hash;
42 +}
43 +
44 +template <typename charT>
45 +inline uint64_t loselose(const charT *str, uint64_t hash = 0)
46 +{
47 + charT c;
48 + while ((c = *str++))
49 + hash += c;
50 + return hash;
51 +}
52 +
53 +HASHUTIL_END_NAMESPACE
54 +
55 +#endif // HASHSTR_H
1 +#ifndef HASHUTIL_H
2 +#define HASHUTIL_H
3 +
4 +// hashutil.h
5 +// 6/16/2015 jichi
6 +
7 +// Redefine HASHUTIL_BEGIN_NAMESPACE/HASHUTIL_END_NAMESPACE if need custom namespace
8 +#ifndef HASHUTIL_BEGIN_NAMESPACE
9 +# define HASHUTIL_BEGIN_NAMESPACE namespace hashutil {
10 +#endif
11 +#ifndef HASHUTIL_END_NAMESPACE
12 +# define HASHUTIL_END_NAMESPACE } // namespace hashutil
13 +#endif
14 +
15 +#endif // HASHUTIL_H
1 +# hashutil.pri
2 +# 6/28/2011 jichi
3 +
4 +DEFINES += WITH_LIB_HASHUTIL
5 +DEPENDPATH += $$PWD
6 +
7 +HEADERS += \
8 + $$PWD/hashstr.h \
9 + $$PWD/hashutil.h
10 +
11 +# EOF
1 +# # ithsys.pro
2 +# CONFIG += noqt staticlib
3 +# include(../../../config.pri)
4 +
5 +# # jichi 7/12/2015: Always enable SEH
6 +# DEFINES += ITH_HAS_SEH
7 +
8 +# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
9 +
10 +set(ithsys_src
11 + ithsys.h
12 + ithsys.cc
13 +)
14 +
15 +add_library(ithsys STATIC ${ithsys_src})
16 +
17 +target_compile_options(ithsys PRIVATE
18 + $<$<CONFIG:Release>:>
19 + $<$<CONFIG:Debug>:>
20 +)
21 +
22 +target_link_libraries(ithsys comctl32.lib)
23 +
24 +target_compile_definitions(ithsys
25 + PRIVATE
26 + ITH_HAS_SEH
27 + _CRT_NON_CONFORMING_SWPRINTFS
28 +)
1 +// ithsys.cc
2 +// 8/21/2013 jichi
3 +// Branch: ITH_SYS/SYS.cpp, rev 126
4 +//
5 +// 8/24/2013 TODO:
6 +// - Clean up the code
7 +// - Move my old create remote thread for ITH2 here
8 +
9 +#include "ithsys/ithsys.h"
10 +//#include "vnrhook/src/util/growl.h"
11 +
12 +//#define ITH_SYS_SECTION L"ITH_SysSection"
13 +#define ITH_THREADMAN_SECTION L"VNR_SYS_THREAD"
14 +
15 +// jichi 9/28/2013: Weither use NtThread or RemoteThread
16 +// RemoteThread works on both Windows 7 or Wine, while NtThread does not work on wine
17 +#define ITH_ENABLE_THREADMAN (!IthIsWindows8OrGreater() && !IthIsWine())
18 +//#define ITH_ENABLE_THREADMAN true
19 +
20 +//#define ITH_ENABLE_WINAPI // jichi: prefer Win32 API to NTDLL API
21 +
22 +// Helpers
23 +
24 +// jichi 2/3/2015: About GetVersion
25 +// Windows XP SP3: 5.1
26 +// Windows 7: 6.1, 0x1db10106
27 +// Windows 8: 6.2, 0x23f00206
28 +// Windows 10: 6.2, 0x23f00206 (build 9926):
29 +
30 +BOOL IthIsWindowsXp()
31 +{
32 + static BOOL ret = -1; // cached
33 + if (ret < 0) {
34 + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439%28v=vs.85%29.aspx
35 + DWORD v = ::GetVersion();
36 + BYTE major = LOBYTE(LOWORD(v));
37 + //DWORD minor = (DWORD)(HIBYTE(LOWORD(v)));
38 +
39 + // Windows XP = 5.1
40 + //ret = major < 6 ? 1 : 0;
41 + ret = major < 6;
42 + }
43 + return ret;
44 +}
45 +
46 +// https://msdn.microsoft.com/en-us/library/windows/desktop/dn424972%28v=vs.85%29.aspx
47 +// The same as IsWindows8OrGreater, which I don't know if the function is available to lower Windows.
48 +static BOOL IthIsWindows8OrGreater() // this function is not exported
49 +{
50 + static BOOL ret = -1; // cached
51 + if (ret < 0) {
52 + // http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439%28v=vs.85%29.aspx
53 + DWORD v = ::GetVersion();
54 + BYTE major = LOBYTE(LOWORD(v)),
55 + minor = HIBYTE(LOWORD(v));
56 + //DWORD minor = (DWORD)(HIBYTE(LOWORD(v)));
57 +
58 + // Windows 8/10 = 6.2
59 + ret = major > 6 || (major == 6 && minor >= 2);
60 + }
61 + return ret;
62 +}
63 +
64 +BOOL IthIsWine()
65 +{
66 + static BOOL ret = -1; // cached
67 + if (ret < 0) {
68 + const wchar_t *path;
69 + wchar_t buffer[MAX_PATH];
70 + if (UINT sz = ::GetSystemDirectoryW(buffer, MAX_PATH)) {
71 + path = buffer;
72 + ::wcscpy(buffer + sz, L"\\winecfg.exe");
73 + } else
74 + path = L"C:\\Windows\\System32\\winecfg.exe";
75 + //ITH_MSG(path);
76 + ret = ::GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES ? TRUE : FALSE;
77 + }
78 + return ret;
79 +}
80 +
81 +// jichi 9/28/2013: prevent parallelization in wine
82 +void IthCoolDown()
83 +{
84 + // http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Thread/NtDelayExecution.html
85 + //const LONGLONG timeout = -10000; // in 100ns, i.e. 1ms
86 + //NtDelayExecution(FALSE, (PLARGE_INTEGER)&timeout);
87 + //NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
88 + // Flush the instruction cache line, and prevent wine from rending things in parallel
89 + if (IthIsWine())
90 + IthSleep(1); // sleep for 1 ms
91 + //__asm
92 + //{
93 + // //mov eax,0x2710 // = 10000
94 + // mov ecx,time
95 + // mul ecx
96 + // neg eax
97 + // adc edx,0
98 + // neg edx
99 + // push edx
100 + // push eax
101 + // push esp
102 + // push 0
103 + // call dword ptr [NtDelayExecution]
104 + // add esp,8
105 + //}
106 +}
107 +
108 +// jichi 9/23/2013: wine deficenciy on mapping sections
109 +// Whe set to false, do not map sections.
110 +//static bool ith_has_section = true;
111 +
112 +//#ifdef ITH_WINE
113 +//# include "winddk/winddk.h"
114 +//#endif // ITH_WINE
115 +
116 +//#define SEC_BASED 0x200000 // jichi 8/24/2013: emoved
117 +
118 +// jichi 10/6/2013
119 +// See: http://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
120 +// See: http://www.codeproject.com/Articles/16598/Get-Your-DLL-s-Path-Name
121 +EXTERN_C IMAGE_DOS_HEADER __ImageBase;
122 +#define CURRENT_MODULE_HANDLE ((HINSTANCE)&__ImageBase)
123 +size_t IthGetCurrentModulePath(wchar_t *buf, size_t len)
124 +{ return ::GetModuleFileNameW(CURRENT_MODULE_HANDLE, buf, len); }
125 +
126 +// - Global variables -
127 +
128 +#ifdef ITH_HAS_HEAP
129 +HANDLE hHeap; // used in ith/common/memory.h
130 +#endif // ITH_HAS_HEAP
131 +
132 +DWORD current_process_id;
133 +DWORD debug;
134 +BYTE launch_time[0x10];
135 +LPVOID page;
136 +
137 +// jichi 6/12/2015: https://en.wikipedia.org/wiki/Shift_JIS
138 +// Leading table for SHIFT-JIS encoding
139 +BYTE LeadByteTable[0x100] = {
140 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
141 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
142 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
143 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
144 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
145 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
146 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
147 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
148 + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
149 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
150 + 2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
151 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
152 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
153 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
154 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
155 + 2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1
156 +};
157 +
158 +namespace { // unnamed
159 +
160 +WCHAR file_path[MAX_PATH] = L"\\??\\";
161 +LPWSTR current_dir;
162 +DWORD page_locale;
163 +HANDLE root_obj,
164 + dir_obj,
165 + codepage_section,
166 + thread_man_section;
167 +
168 +BYTE file_info[0x1000];
169 +
170 +
171 +// - Helper functions -
172 +
173 +inline DWORD GetShareMemory()
174 +{
175 + __asm
176 + {
177 + mov eax,fs:[0x30]
178 + mov eax,[eax+0x4C]
179 + }
180 +}
181 +
182 +inline LARGE_INTEGER *GetTimeBias()
183 +{ __asm mov eax,0x7ffe0020 }
184 +
185 +
186 +//Get full path of current process.
187 +//inline LPWSTR GetModulePath()
188 +//{
189 +// __asm
190 +// {
191 +// mov eax,fs:[0x30]
192 +// mov eax,[eax+0xC]
193 +// mov eax,[eax+0xC]
194 +// mov eax,[eax+0x28]
195 +// }
196 +//}
197 +
198 +// - Singleton classes -
199 +
200 +BYTE normal_routine[0x14] = {
201 + 0x51,0x52,0x64,0x89,0x23,0x55,0xff,0xd0,0x50,0x6a,0xfe,0xff,0x15,0x14,0x00,0x00,0x00
202 +};
203 +
204 +BYTE except_routine[0xe0] = {
205 + 0xba,0x08,0x00,0x00,0x00,0x8b,0xc1,0x83,0xe0,0x0f,0x83,0xf8,0x0a,0x72,0x02,0x04,
206 + 0x07,0x04,0x30,0x66,0xab,0xc1,0xc9,0x04,0x4a,0x75,0xea,0xc3,0x00,0x00,0x00,0x00,
207 + 0x8b,0x44,0xe4,0x04,0x31,0xf6,0x8b,0x28,0x8b,0x4c,0xe4,0x0c,0x8b,0x99,0xb8,0x00,
208 + 0x00,0x00,0x81,0xec,0x40,0x02,0x00,0x00,0x8d,0x7c,0xe4,0x40,0x89,0xe0,0x56,0x6a,
209 + 0x1c,0x50,0x56,0x53,0x6a,0xff,0xff,0x15,0x18,0x00,0x00,0x00,0x85,0xc0,0x75,0x98,
210 + 0x89,0xe0,0x50,0x68,0x00,0x02,0x00,0x00,0x57,0x6a,0x02,0x53,0x6a,0xff,0xff,0x15,
211 + 0x18,0x00,0x00,0x00,0x85,0xc0,0x75,0xe6,0x5e,0x0f,0xc1,0xf7,0xfd,0xb0,0x5c,0x66,
212 + 0xf2,0xaf,0x66,0xc7,0x47,0x02,0x3a,0x00,0x89,0xd9,0x2b,0x0c,0xe4,0xe8,0x7e,0xff,
213 + 0xff,0xff,0x47,0x47,0x87,0xfe,0x89,0xe9,0xe8,0x73,0xff,0xff,0xff,0x47,0x47,0x31,
214 + 0xc0,0x89,0x47,0x10,0x6a,0x00,0x57,0x56,0x6a,0x00,0xfc,0xff,0x15,0x1c,0x00,0x00,
215 + 0x00,0x83,0xc8,0xff,0xeb,0xbe
216 +};
217 +
218 +// jichi 8/24/2013: Could be initialized using NtMapViewOfSection/ZwMapViewOfSection
219 +// This class cannot have constructor / destructor
220 +struct _ThreadView {
221 + UINT_PTR mutex,
222 + count;
223 + DWORD proc_record[1];
224 +};
225 +
226 +class : private _ThreadView { // ThreadStartManager
227 +
228 + enum {
229 + ADDR0 = 0xD
230 + , ADDR1 = 0x48
231 + , ADDR2 = 0x60
232 + , ADDR3 = 0x9D
233 + };
234 +
235 +public:
236 + LPVOID GetProcAddr(HANDLE hProc)
237 + {
238 + AcquireLock();
239 + DWORD pid,addr,len;
240 + if (hProc == NtCurrentProcess())
241 + pid = ::current_process_id;
242 + else {
243 + PROCESS_BASIC_INFORMATION info;
244 + NtQueryInformationProcess(hProc, ProcessBasicInformation, &info, sizeof(info), &len);
245 + pid=info.uUniqueProcessId;
246 + }
247 + pid >>= 2;
248 + for (UINT_PTR i = 0; i < count; i++)
249 + if (pid == (proc_record[i] & 0xfff)) {
250 + addr = proc_record[i] & ~0xfff;
251 + ReleaseLock();
252 + return (LPVOID)addr;
253 + }
254 + len = 0x1000;
255 + NtAllocateVirtualMemory(hProc, (PVOID *)(proc_record + count), 0, &len,
256 + MEM_COMMIT,PAGE_EXECUTE_READWRITE);
257 + DWORD base = proc_record[count];
258 + proc_record[count] |= pid;
259 + union {
260 + LPVOID buffer;
261 + DWORD b;
262 + };
263 + b = base;
264 + LPVOID fun_table[3];
265 + *(DWORD *)(normal_routine + ADDR0) += base;
266 + NtWriteVirtualMemory(hProc, buffer, normal_routine, 0x14, 0);
267 + *(DWORD *)(normal_routine + ADDR0) -= base;
268 + b += 0x14;
269 + fun_table[0] = NtTerminateThread;
270 + fun_table[1] = NtQueryVirtualMemory;
271 + fun_table[2] = MessageBoxW;
272 + NtWriteVirtualMemory(hProc, buffer, fun_table, 0xC, 0);
273 + b += 0xc;
274 + *(DWORD *)(except_routine + ADDR1) += base;
275 + *(DWORD *)(except_routine + ADDR2) += base;
276 + *(DWORD *)(except_routine + ADDR3) += base;
277 + NtWriteVirtualMemory(hProc, buffer, except_routine, 0xE0, 0);
278 + *(DWORD *)(except_routine + ADDR1) -= base;
279 + *(DWORD *)(except_routine + ADDR2) -= base;
280 + *(DWORD *)(except_routine + ADDR3) -= base;
281 + count++;
282 + ReleaseLock();
283 + return (LPVOID)base;
284 + }
285 + void ReleaseProcessMemory(HANDLE hProc)
286 + {
287 + DWORD pid,addr,len;
288 + AcquireLock();
289 + if (hProc==NtCurrentProcess())
290 + pid = ::current_process_id;
291 + else {
292 + PROCESS_BASIC_INFORMATION info;
293 + NtQueryInformationProcess(hProc,ProcessBasicInformation,&info,sizeof(info),&len);
294 + pid = info.uUniqueProcessId;
295 + }
296 + pid >>= 2;
297 + //NtWaitForSingleObject(thread_man_mutex,0,0);
298 + for (UINT_PTR i = 0; i < count; i++) {
299 + if ((proc_record[i]&0xfff) == pid) {
300 + addr = proc_record[i] & ~0xfff;
301 + DWORD size=0x1000;
302 + NtFreeVirtualMemory(hProc, (PVOID *)&addr, &size, MEM_RELEASE);
303 + count--;
304 + for (UINT_PTR j = i; j < count; j++)
305 + proc_record[j] = proc_record[j + 1];
306 + proc_record[count] = 0;
307 + ReleaseLock();
308 + //NtReleaseMutant(thread_man_mutex,0);
309 + return;
310 + }
311 + }
312 + ReleaseLock();
313 + //NtReleaseMutant(thread_man_mutex,0);
314 + }
315 + void CheckProcessMemory()
316 + {
317 + UINT_PTR i, j, flag, addr;
318 + DWORD len;
319 + CLIENT_ID id;
320 + OBJECT_ATTRIBUTES oa = {};
321 + HANDLE hProc;
322 + BYTE buffer[8];
323 + AcquireLock();
324 + id.UniqueThread = 0;
325 + oa.uLength = sizeof(oa);
326 + for (i = 0; i < count ; i++) {
327 + id.UniqueProcess = (proc_record[i]&0xfff)<<2;
328 + addr = proc_record[i] & ~0xfff;
329 + flag = 0;
330 + if (NT_SUCCESS(NtOpenProcess(&hProc, PROCESS_VM_OPERATION|PROCESS_VM_READ, &oa, &id))) {
331 + if (NT_SUCCESS(NtReadVirtualMemory(hProc, (PVOID)addr, buffer, 8, &len)))
332 + if (::memcmp(buffer, normal_routine, 4) == 0)
333 + flag = 1;
334 + NtClose(hProc);
335 + }
336 + if (flag == 0) {
337 + for (j = i; j < count; j++)
338 + proc_record[j] = proc_record[j + 1];
339 + count--;
340 + i--;
341 + }
342 + }
343 + ReleaseLock();
344 + }
345 + void AcquireLock()
346 + {
347 + LONG *p = (LONG *)&mutex;
348 + while (_interlockedbittestandset(p,0))
349 + YieldProcessor();
350 + }
351 + void ReleaseLock()
352 + {
353 + LONG *p = (LONG*)&mutex;
354 + _interlockedbittestandreset(p, 0);
355 + }
356 +} *thread_man_ = nullptr; // global singleton
357 +
358 +} // unnamed namespace
359 +
360 +// - API functions -
361 +
362 +extern "C" {
363 +
364 +void FreeThreadStart(HANDLE hProc)
365 +{
366 + if (thread_man_)
367 + ::thread_man_->ReleaseProcessMemory(hProc);
368 +}
369 +
370 +void CheckThreadStart()
371 +{
372 + if (thread_man_)
373 + ::thread_man_->CheckProcessMemory();
374 +
375 + // jichi 2/2/2015: This function is only used to wait for injected threads vnrhost.
376 + // Sleep for 100 ms to wait for remote thread to start
377 + //IthSleep(100);
378 + //IthCoolDown();
379 +}
380 +
381 +void IthSleep(int time)
382 +{
383 + __asm
384 + {
385 + mov eax,0x2710 // jichi = 10000
386 + mov ecx,time
387 + mul ecx
388 + neg eax
389 + adc edx,0
390 + neg edx
391 + push edx
392 + push eax
393 + push esp
394 + push 0
395 + call dword ptr [NtDelayExecution]
396 + add esp,8
397 + }
398 +}
399 +
400 +void IthSystemTimeToLocalTime(LARGE_INTEGER *time)
401 +{ time->QuadPart -= GetTimeBias()->QuadPart; }
402 +
403 +int FillRange(LPCWSTR name, DWORD *lower, DWORD *upper)
404 +{
405 + PLDR_DATA_TABLE_ENTRY it;
406 + LIST_ENTRY *begin;
407 + __asm
408 + {
409 + mov eax,fs:[0x30]
410 + mov eax,[eax+0xc]
411 + mov eax,[eax+0xc]
412 + mov it,eax
413 + mov begin,eax
414 + }
415 +
416 + while (it->SizeOfImage) {
417 + if (::_wcsicmp(it->BaseDllName.Buffer, name) == 0) {
418 + *lower = *upper = (DWORD)it->DllBase;
419 + MEMORY_BASIC_INFORMATION info = {};
420 + DWORD l,size;
421 + size = 0;
422 + do {
423 + NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)(*upper), MemoryBasicInformation, &info, sizeof(info), &l);
424 + if (info.Protect&PAGE_NOACCESS) {
425 + it->SizeOfImage=size;
426 + break;
427 + }
428 + size += info.RegionSize;
429 + *upper += info.RegionSize;
430 + } while (size < it->SizeOfImage);
431 + return 1;
432 + }
433 + it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink;
434 + if (it->InLoadOrderModuleList.Flink == begin)
435 + break;
436 + }
437 + return 0;
438 +}
439 +
440 +DWORD SearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length) // KMP
441 +{
442 + __asm
443 + {
444 + mov eax,search_length
445 +alloc:
446 + push 0
447 + sub eax,1
448 + jnz alloc
449 +
450 + mov edi,search
451 + mov edx,search_length
452 + mov ecx,1
453 + xor esi,esi
454 +build_table:
455 + mov al,byte ptr [edi+esi]
456 + cmp al,byte ptr [edi+ecx]
457 + sete al
458 + test esi,esi
459 + jz pre
460 + test al,al
461 + jnz pre
462 + mov esi,[esp+esi*4-4]
463 + jmp build_table
464 +pre:
465 + test al,al
466 + jz write_table
467 + inc esi
468 +write_table:
469 + mov [esp+ecx*4],esi
470 +
471 + inc ecx
472 + cmp ecx,edx
473 + jb build_table
474 +
475 + mov esi,base
476 + xor edx,edx
477 + mov ecx,edx
478 +matcher:
479 + mov al,byte ptr [edi+ecx]
480 + cmp al,byte ptr [esi+edx]
481 + sete al
482 + test ecx,ecx
483 + jz match
484 + test al,al
485 + jnz match
486 + mov ecx, [esp+ecx*4-4]
487 + jmp matcher
488 +match:
489 + test al,al
490 + jz pre2
491 + inc ecx
492 + cmp ecx,search_length
493 + je finish
494 +pre2:
495 + inc edx
496 + cmp edx,base_length // search_length
497 + jb matcher
498 + mov edx,search_length
499 + dec edx
500 +finish:
501 + mov ecx,search_length
502 + sub edx,ecx
503 + lea eax,[edx+1]
504 + lea ecx,[ecx*4]
505 + add esp,ecx
506 + }
507 +}
508 +
509 +// jichi 2/5/2014: '?' = 0xff
510 +// See: http://sakuradite.com/topic/124
511 +DWORD SearchPatternEx(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length, BYTE wildcard) // KMP
512 +{
513 + __asm
514 + {
515 + // jichi 2/5/2014 BEGIN
516 + mov bl,wildcard
517 + // jichi 2/5/2014 END
518 + mov eax,search_length
519 +alloc:
520 + push 0
521 + sub eax,1
522 + jnz alloc // jichi 2/5/2014: this will also set %eax to zero
523 +
524 + mov edi,search
525 + mov edx,search_length
526 + mov ecx,1
527 + xor esi,esi
528 +build_table:
529 + mov al,byte ptr [edi+esi]
530 + cmp al,byte ptr [edi+ecx]
531 + sete al
532 + test esi,esi
533 + jz pre
534 + test al,al
535 + jnz pre
536 + mov esi,[esp+esi*4-4]
537 + jmp build_table
538 +pre:
539 + test al,al
540 + jz write_table
541 + inc esi
542 +write_table:
543 + mov [esp+ecx*4],esi
544 +
545 + inc ecx
546 + cmp ecx,edx
547 + jb build_table
548 +
549 + mov esi,base
550 + xor edx,edx
551 + mov ecx,edx
552 +matcher:
553 + mov al,byte ptr [edi+ecx] // search
554 + // jichi 2/5/2014 BEGIN
555 + mov bh,al // save loaded byte to reduce cache access. %ah is not used and always zero
556 + cmp al,bl // %bl is the wildcard byte
557 + sete al
558 + test al,al
559 + jnz wildcard_matched
560 + mov al,bh // restore the loaded byte
561 + // jichi 2/5/2014 END
562 + cmp al,byte ptr [esi+edx] // base
563 + sete al
564 + // jichi 2/5/2014 BEGIN
565 +wildcard_matched:
566 + // jichi 2/5/2014 END
567 + test ecx,ecx
568 + jz match
569 + test al,al
570 + jnz match
571 + mov ecx, [esp+ecx*4-4]
572 + jmp matcher
573 +match:
574 + test al,al
575 + jz pre2
576 + inc ecx
577 + cmp ecx,search_length
578 + je finish
579 +pre2:
580 + inc edx
581 + cmp edx,base_length // search_length
582 + jb matcher
583 + mov edx,search_length
584 + dec edx
585 +finish:
586 + mov ecx,search_length
587 + sub edx,ecx
588 + lea eax,[edx+1]
589 + lea ecx,[ecx*4]
590 + add esp,ecx
591 + }
592 +}
593 +
594 +DWORD IthGetMemoryRange(LPCVOID mem, DWORD *base, DWORD *size)
595 +{
596 + DWORD r;
597 + MEMORY_BASIC_INFORMATION info;
598 + NtQueryVirtualMemory(NtCurrentProcess(), const_cast<LPVOID>(mem), MemoryBasicInformation, &info, sizeof(info), &r);
599 + if (base)
600 + *base = (DWORD)info.BaseAddress;
601 + if (size)
602 + *size = info.RegionSize;
603 + return (info.Type&PAGE_NOACCESS) == 0;
604 +}
605 +
606 +// jichi 9/25/2013
607 +// See: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.nls/doc/nlsgdrf/multi-byte_widechar_subr.htm
608 +// SJIS->Unicode. 'mb' must be null-terminated. 'wc' should have enough space ( 2*strlen(mb) is safe).
609 +//#ifdef ITH_WINE
610 +//int MB_WC(char *mb, wchar_t *wc)
611 +//{ return mbstowcs(wc, mb, 0x100); }
612 +//
613 +//#else
614 +int MB_WC(char *mb, wchar_t *wc)
615 +{
616 + __asm
617 + {
618 + mov esi,mb
619 + mov edi,wc
620 + mov edx,page
621 + lea ebx,LeadByteTable
622 + add edx,0x220
623 + push 0
624 +_mb_translate:
625 + movzx eax,word ptr [esi]
626 + test al,al
627 + jz _mb_fin
628 + movzx ecx,al
629 + xlat
630 + test al,1
631 + cmovnz cx, word ptr [ecx*2+edx-0x204]
632 + jnz _mb_next
633 + mov cx,word ptr [ecx*2+edx]
634 + mov cl,ah
635 + mov cx, word ptr [ecx*2+edx]
636 +_mb_next:
637 + mov [edi],cx
638 + add edi,2
639 + movzx eax,al
640 + add esi,eax
641 + inc dword ptr [esp]
642 + jmp _mb_translate
643 +_mb_fin:
644 + pop eax
645 + }
646 +}
647 +
648 +// Count characters of 'mb' string. 'mb_length' is max length.
649 +// jichi 9/25/2013: This function is not used
650 +//int MB_WC_count(char *mb, int mb_length)
651 +//{
652 +// __asm
653 +// {
654 +// xor eax,eax
655 +// xor edx,edx
656 +// mov esi,mb
657 +// mov edi,mb_length
658 +// lea ebx,LeadByteTable
659 +//_mbc_count:
660 +// mov dl,byte ptr [esi]
661 +// test dl,dl
662 +// jz _mbc_finish
663 +// movzx ecx, byte ptr [ebx+edx]
664 +// add esi,ecx
665 +// inc eax
666 +// sub edi,ecx
667 +// ja _mbc_count
668 +//_mbc_finish:
669 +// }
670 +//}
671 +
672 +// jichi 9/25/2013
673 +// See: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.nls/doc/nlsgdrf/multi-byte_widechar_subr.htm
674 +// Unicode->SJIS. Analogous to MB_WC.
675 +//#ifdef ITH_WINE
676 +//int WC_MB(wchar_t *wc, char *mb)
677 +//{ return wcstombs(mb, wc, 0x100); }
678 +//
679 +//#else
680 +int WC_MB(wchar_t *wc, char *mb)
681 +{
682 + __asm
683 + {
684 + mov esi,wc
685 + mov edi,mb
686 + mov edx,page
687 + add edx,0x7c22
688 + xor ebx,ebx
689 +_wc_translate:
690 + movzx eax,word ptr [esi]
691 + test eax,eax
692 + jz _wc_fin
693 + mov cx,word ptr [eax*2+edx]
694 + test ch,ch
695 + jz _wc_single
696 + mov [edi+ebx],ch
697 + inc ebx
698 +_wc_single:
699 + mov [edi+ebx],cl
700 + inc ebx
701 + add esi,2
702 + jmp _wc_translate
703 +_wc_fin:
704 + mov eax,ebx
705 + }
706 +}
707 +
708 +//Initialize environment for NT native calls. Not thread safe so only call it once in one module.
709 +//1. Create new heap. Future memory requests are handled by this heap.
710 +//Destroying this heap will completely release all dynamically allocated memory, thus prevent memory leaks on unload.
711 +//2. Create handle to root directory of process objects (section/event/mutex/semaphore).
712 +//NtCreate* calls will use this handle as base directory.
713 +//3. Load SJIS code page. First check for Japanese locale. If not then load from 'C_932.nls' in system folder.
714 +//MB_WC & WC_MB use this code page for translation.
715 +//4. Locate current NT path (start with \??\).
716 +//NtCreateFile requires full path or a root handle. But this handle is different from object.
717 +//5. Map shared memory for ThreadStartManager into virtual address space.
718 +//This will allow IthCreateThread function properly.
719 +BOOL IthInitSystemService()
720 +{
721 + PPEB peb;
722 + //NTSTATUS status;
723 + DWORD size;
724 + ULONG LowFragmentHeap;
725 + UNICODE_STRING us;
726 + OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
727 + IO_STATUS_BLOCK ios;
728 + HANDLE codepage_file;
729 + LARGE_INTEGER sec_size = {0x1000, 0};
730 + __asm
731 + {
732 + mov eax,fs:[0x18]
733 + mov ecx,[eax+0x20]
734 + mov eax,[eax+0x30]
735 + mov peb,eax
736 + mov current_process_id,ecx
737 + }
738 + debug = peb->BeingDebugged;
739 + LowFragmentHeap = 2;
740 +
741 +#ifdef ITH_HAS_HEAP
742 + ::hHeap = RtlCreateHeap(0x1002, 0, 0, 0, 0, 0);
743 + RtlSetHeapInformation(::hHeap, HeapCompatibilityInformation, &LowFragmentHeap, sizeof(LowFragmentHeap));
744 +#endif // ITH_HAS_HEAP
745 +
746 + LPWSTR t = nullptr, // jichi: path to system32, such as "c:\windows\system32"
747 + obj = nullptr; // jichi: path to current kernel session, such as "Sessions\\1\\BaseNamedObjects"
748 + // jichi 9/22/2013: This would crash wine with access violation exception.
749 + if (!IthIsWine()) {
750 + // jichi 9/22/2013: For ChuSingura46+1 on Windows 7
751 + // t = L"C:\\Windows\\system32";
752 + // obj = L"\\Sessions\\1\\BaseNamedObjects";
753 + // On Windows XP
754 + // t = L"C:\\WINDOWS\\system32";
755 + // obj = L"\\BaseNamedObjects";
756 + MEMORY_BASIC_INFORMATION info;
757 + if (!NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(), peb->ReadOnlySharedMemoryBase, MemoryBasicInformation, &info, sizeof(info), &size)))
758 + return FALSE;
759 + DWORD base = (DWORD)peb->ReadOnlySharedMemoryBase;
760 + DWORD end = base + info.RegionSize - 0x40;
761 + static WCHAR system32[] = L"system32";
762 + for (;base < end; base += 2)
763 + if (::memcmp((PVOID)base, system32, 0x10) == 0) {
764 + t = (LPWSTR)base;
765 + while (*t-- != L':');
766 + obj = (LPWSTR)base;
767 + while (*obj != L'\\') obj++;
768 + break;
769 + }
770 + if (base == end)
771 + return FALSE;
772 + }
773 + //ITH_MSG(t);
774 + //ITH_MSG(obj);
775 +
776 + LDR_DATA_TABLE_ENTRY *ldr_entry = (LDR_DATA_TABLE_ENTRY*)peb->Ldr->InLoadOrderModuleList.Flink;
777 +
778 + // jichi 7/12/2015: This will fail when the file path is a remote path such as:
779 + // Original remote file path: \\??\\\\\\psf\\Host\\Local\\Windows\\Games\\ShinaRio\\Ayakashibito_trial\\");
780 + // Correct UNC path: \\??\\\\UNC\\psf\\Host\\Local\\Windows\\Games\\ShinaRio\\Ayakashibito_trial\\");
781 + //RtlInitUnicodeString(&us, L"\\??\\UNC\\psf\\Host\\Local\\Windows\\Games\\ShinaRio\\Ayakashibito_trial\\");
782 + //WCHAR file_path[MAX_PATH] = L"\\??\\";
783 + LPCWSTR modulePath = ldr_entry->FullDllName.Buffer;
784 + if (modulePath[0] == '\\' && modulePath[1] == '\\') { // This is a remote path
785 + ::file_path[4] = 'U';
786 + ::file_path[5] = 'N';
787 + ::file_path[6] = 'C';
788 + ::wcscpy(::file_path + 7, modulePath + 1);
789 + } else
790 + ::wcscpy(::file_path + 4, modulePath);
791 +
792 + current_dir = ::wcsrchr(::file_path, L'\\') + 1;
793 + *current_dir = 0;
794 +
795 + //GROWL(::file_path);
796 + RtlInitUnicodeString(&us, ::file_path);
797 +
798 + if (!NT_SUCCESS(NtOpenFile(&dir_obj,FILE_LIST_DIRECTORY|FILE_TRAVERSE|SYNCHRONIZE,
799 + &oa,&ios,FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT)))
800 + return FALSE;
801 +
802 + // jichi 9/22/2013: Get kernel object session ID
803 + // See: http://www.brianbondy.com/blog/id/100/
804 + // It seems that on sessionId is 0 on Windows XP, and 1 on Windows Vista and later
805 + // I assume that sessionId is in [0,9]
806 + // For ChuSingura46+1 on Windows 7
807 + // obj = L"\\Sessions\\1\\BaseNamedObjects";
808 + // On Windows XP
809 + // obj = L"\\BaseNamedObjects";
810 + //ITH_MSG(obj);
811 + {
812 + if (obj)
813 + RtlInitUnicodeString(&us, obj);
814 + else { // jichi ITH is on Wine
815 + // Get session ID in PEB
816 + // See: http://msdn.microsoft.com/en-us/library/bb432286%28v=vs.85%29.aspx
817 + DWORD sessionId = peb->SessionId;
818 + if (!sessionId) // Windows XP
819 + RtlInitUnicodeString(&us, L"\\BaseNamedObjects");
820 + else { // Windows Vista +
821 + wchar_t path[] = L"\\Sessions\\0\\BaseNamedObjects";
822 + path[10] += (wchar_t)sessionId; // replace 0 with the session ID
823 + RtlInitUnicodeString(&us, path);
824 + }
825 + }
826 + }
827 +
828 + if (!NT_SUCCESS(NtOpenDirectoryObject(&::root_obj, READ_CONTROL|0xf, &oa)))
829 + return FALSE;
830 +
831 + ::page = peb->InitAnsiCodePageData;
832 +
833 + enum { CP932 = 932 };
834 +
835 + // jichi 9/23/2013: Access violation on Wine
836 + if (IthIsWine())
837 + // One wine, there is no C_932.nls
838 + //page_locale = 0x4e4; // 1252, English
839 + //page_locale = GetACP(); // This will return 932 when LC_ALL=ja_JP.UTF-8 on wine
840 + // Always set locale to CP932 on Wine, since C_932.nls could be missing.
841 + ::page_locale = CP932;
842 + else
843 + ::page_locale = *(DWORD *)page >> 16;
844 +
845 + if (::page_locale == CP932) {
846 + oa.hRootDirectory = ::root_obj;
847 + oa.uAttributes |= OBJ_OPENIF;
848 + } else { // Unreachable or wine
849 +//#ifdef ITH_WINE
850 +// // jichi 9/22/2013: For ChuSingura46+1 on Windows 7
851 +// //t = L"C:\\Windows\\system32";
852 +// wchar_t buffer[MAX_PATH];
853 +// if (!t) { // jichi 9/22/2013: ITH is one wine
854 +// if (UINT sz = ::GetSystemDirectoryW(buffer, MAX_PATH)) {
855 +// buffer[sz] = 0;
856 +// t = buffer;
857 +// } else
858 +// t = L"C:\\Windows\\System32"; // jichi 9/29/2013: sth is wrong here
859 +// }
860 +//#endif // ITH_WINE
861 +
862 + ::wcscpy(::file_path + 4, t);
863 + t = ::file_path;
864 + while(*++t);
865 + if (*(t-1)!=L'\\')
866 + *t++=L'\\';
867 + ::wcscpy(t,L"C_932.nls");
868 + RtlInitUnicodeString(&us, ::file_path);
869 + if (!NT_SUCCESS(NtOpenFile(&codepage_file, FILE_READ_DATA, &oa, &ios,FILE_SHARE_READ,0)))
870 + return FALSE;
871 + oa.hRootDirectory = ::root_obj;
872 + oa.uAttributes |= OBJ_OPENIF;
873 + RtlInitUnicodeString(&us, L"JPN_CodePage");
874 + if (!NT_SUCCESS(NtCreateSection(&codepage_section, SECTION_MAP_READ,
875 + &oa,0, PAGE_READONLY, SEC_COMMIT, codepage_file)))
876 + return FALSE;
877 + NtClose(codepage_file);
878 + size = 0;
879 + ::page = nullptr;
880 + if (!NT_SUCCESS(NtMapViewOfSection(::codepage_section, NtCurrentProcess(),
881 + &::page,
882 + 0, 0, 0, &size, ViewUnmap, 0,
883 + PAGE_READONLY)))
884 + return FALSE;
885 + }
886 + if (ITH_ENABLE_THREADMAN) {
887 + RtlInitUnicodeString(&us, ITH_THREADMAN_SECTION);
888 + if (!NT_SUCCESS(NtCreateSection(&thread_man_section, SECTION_ALL_ACCESS, &oa, &sec_size,
889 + PAGE_EXECUTE_READWRITE, SEC_COMMIT, 0)))
890 + return FALSE;
891 + size = 0;
892 + // http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Section/NtMapViewOfSection.html
893 + thread_man_ = nullptr;
894 + if (!NT_SUCCESS(NtMapViewOfSection(thread_man_section, NtCurrentProcess(),
895 + (LPVOID *)&thread_man_,
896 + 0,0,0, &size, ViewUnmap, 0,
897 + PAGE_EXECUTE_READWRITE)))
898 + return FALSE;
899 + }
900 + return TRUE;
901 +}
902 +
903 +//Release resources allocated by IthInitSystemService.
904 +//After destroying the heap, all memory allocated by ITH module is returned to system.
905 +void IthCloseSystemService()
906 +{
907 + if (::page_locale != 0x3a4) {
908 + NtUnmapViewOfSection(NtCurrentProcess(), ::page);
909 + NtClose(::codepage_section);
910 + }
911 + if (ITH_ENABLE_THREADMAN) {
912 + NtUnmapViewOfSection(NtCurrentProcess(), ::thread_man_);
913 + NtClose(::thread_man_section);
914 + }
915 + NtClose(::root_obj);
916 +#ifdef ITH_HAS_HEAP
917 + RtlDestroyHeap(::hHeap);
918 +#endif // ITH_HAS_HEAP
919 +}
920 +
921 +//Check for existence of a file in current folder. Thread safe after init.
922 +//For ITH main module, it's ITH folder. For target process it's the target process's current folder.
923 +BOOL IthCheckFile(LPCWSTR file)
924 +{
925 + //return PathFileExistsW(file); // jichi: need Shlwapi.lib
926 +
927 + //return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
928 + //return GetFileAttributesW(file) != INVALID_FILE_ATTRIBUTES; // jichi: does not consider the current app's path
929 +
930 + // jichi 9/22/2013: Following code does not work in Wine
931 + // See: http://stackoverflow.com/questions/3828835/how-can-we-check-if-a-file-exists-or-not-using-win32-program
932 + //WIN32_FIND_DATA FindFileData;
933 + //HANDLE handle = FindFirstFileW(file, &FindFileData);
934 + //if (handle != INVALID_HANDLE_VALUE) {
935 + // FindClose(handle);
936 + // return TRUE;
937 + //}
938 + //return FALSE;
939 + if (IthIsWine()) {
940 + HANDLE hFile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, 0);
941 + if (hFile != INVALID_HANDLE_VALUE) {
942 + CloseHandle(hFile);
943 + return TRUE;
944 + } else if (!wcschr(file, L':')) { // jichi: this is relative path
945 + // jichi 9/22/2013: Change current directory to the same as main module path
946 + // Otherwise NtFile* would not work for files with relative paths.
947 + if (const wchar_t *path = GetMainModulePath()) // path to VNR's python exe
948 + if (const wchar_t *base = wcsrchr(path, L'\\')) {
949 + size_t dirlen = base - path + 1;
950 + if (dirlen + wcslen(file) < MAX_PATH) {
951 + wchar_t buf[MAX_PATH];
952 + wcsncpy(buf, path, dirlen);
953 + wcscpy(buf + dirlen, file);
954 + return IthCheckFile(buf);
955 + }
956 + }
957 + }
958 + } else { // not wine
959 + HANDLE hFile;
960 + IO_STATUS_BLOCK isb;
961 + UNICODE_STRING us;
962 + RtlInitUnicodeString(&us, file);
963 + OBJECT_ATTRIBUTES oa = { sizeof(oa), dir_obj, &us, 0, 0, 0};
964 + // jichi 9/22/2013: Following code does not work in Wine
965 + if (NT_SUCCESS(NtCreateFile(&hFile, FILE_READ_DATA, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0))) {
966 + NtClose(hFile);
967 + return TRUE;
968 + }
969 + }
970 + return FALSE;
971 + //return IthGetFileInfo(file,file_info);
972 + //wcscpy(current_dir,file);
973 +}
974 +
975 +//Check for existence of files in current folder.
976 +//Unlike IthCheckFile, this function allows wildcard character.
977 +BOOL IthFindFile(LPCWSTR file)
978 +{
979 + NTSTATUS status;
980 + HANDLE h;
981 + UNICODE_STRING us;
982 + OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
983 + us.Buffer = const_cast<LPWSTR>(file);
984 + LPCWSTR path = wcsrchr(file, L'\\');
985 + if (path) {
986 + us.Length = (path - file) << 1;
987 + us.MaximumLength = us.Length;
988 + } else {
989 + us.Length = 0;
990 + us.MaximumLength = 0;
991 + }
992 + IO_STATUS_BLOCK ios;
993 + if (NT_SUCCESS(NtOpenFile(&h,FILE_LIST_DIRECTORY|SYNCHRONIZE,
994 + &oa,&ios,FILE_SHARE_READ,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT))) {
995 + BYTE info[0x400];
996 + if (path)
997 + RtlInitUnicodeString(&us, path + 1);
998 + else
999 + RtlInitUnicodeString(&us, file);
1000 + status = NtQueryDirectoryFile(h,0,0,0,&ios,info,0x400,FileBothDirectoryInformation,TRUE,&us,TRUE);
1001 + NtClose(h);
1002 + return NT_SUCCESS(status);
1003 + }
1004 + return FALSE;
1005 +}
1006 +//Analogous to IthFindFile, but return detail information in 'info'.
1007 +BOOL IthGetFileInfo(LPCWSTR file, LPVOID info, DWORD size)
1008 +{
1009 + NTSTATUS status;
1010 + HANDLE h;
1011 + UNICODE_STRING us;
1012 + LPCWSTR path = wcsrchr(file, L'\\');
1013 + us.Buffer = const_cast<LPWSTR>(file);
1014 + if (path) {
1015 + us.Length = (path - file) << 1;
1016 + us.MaximumLength = us.Length;
1017 + } else {
1018 + us.Length = 0;
1019 + us.MaximumLength = 0;
1020 + }
1021 + //RtlInitUnicodeString(&us,file);
1022 + OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
1023 + IO_STATUS_BLOCK ios;
1024 + if (NT_SUCCESS(NtOpenFile(&h,FILE_LIST_DIRECTORY|SYNCHRONIZE,
1025 + &oa,&ios,FILE_SHARE_READ,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT))) {
1026 + RtlInitUnicodeString(&us,file);
1027 + status = NtQueryDirectoryFile(h,0,0,0,&ios,info,size,FileBothDirectoryInformation,0,&us,0);
1028 + status = NT_SUCCESS(status);
1029 + NtClose(h);
1030 + } else
1031 + status = FALSE;
1032 + return status;
1033 +}
1034 +
1035 +//Check for existence of a file with full NT path(start with \??\).
1036 +BOOL IthCheckFileFullPath(LPCWSTR file)
1037 +{
1038 + UNICODE_STRING us;
1039 + RtlInitUnicodeString(&us, file);
1040 + OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
1041 + HANDLE hFile;
1042 + IO_STATUS_BLOCK isb;
1043 + if (NT_SUCCESS(NtCreateFile(&hFile,FILE_READ_DATA,&oa,&isb,0,0,FILE_SHARE_READ,FILE_OPEN,0,0,0))) {
1044 + NtClose(hFile);
1045 + return TRUE;
1046 + } else
1047 + return FALSE;
1048 +}
1049 +//Create or open file in current folder. Analogous to Win32 CreateFile.
1050 +//option: GENERIC_READ / GENERIC_WRITE.
1051 +//share: FILE_SHARE_READ / FILE_SHARE_WRITE / FILE_SHARE_DELETE. 0 for exclusive access.
1052 +//disposition: FILE_OPEN / FILE_OPEN_IF.
1053 +//Use FILE_OPEN instead of OPEN_EXISTING and FILE_OPEN_IF for CREATE_ALWAYS.
1054 +HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition)
1055 +{
1056 + UNICODE_STRING us;
1057 + RtlInitUnicodeString(&us, name);
1058 + OBJECT_ATTRIBUTES oa = { sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0 };
1059 + HANDLE hFile;
1060 + IO_STATUS_BLOCK isb;
1061 + return NT_SUCCESS(NtCreateFile(&hFile,
1062 + option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
1063 + &oa,&isb,0,0,share,disposition,
1064 + FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
1065 + hFile : INVALID_HANDLE_VALUE;
1066 +}
1067 +//Create a directory file in current folder.
1068 +HANDLE IthCreateDirectory(LPCWSTR name)
1069 +{
1070 + UNICODE_STRING us;
1071 + RtlInitUnicodeString(&us,name);
1072 + OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
1073 + HANDLE hFile;
1074 + IO_STATUS_BLOCK isb;
1075 + return NT_SUCCESS(NtCreateFile(&hFile,FILE_LIST_DIRECTORY|FILE_TRAVERSE|SYNCHRONIZE,&oa,&isb,0,0,
1076 + FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN_IF,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT,0,0)) ?
1077 + hFile : INVALID_HANDLE_VALUE;
1078 +}
1079 +
1080 +HANDLE IthCreateFileInDirectory(LPCWSTR name, HANDLE dir, DWORD option, DWORD share, DWORD disposition)
1081 +{
1082 + UNICODE_STRING us;
1083 + RtlInitUnicodeString(&us,name);
1084 + if (dir == 0) dir = dir_obj;
1085 + OBJECT_ATTRIBUTES oa = {sizeof(oa), dir, &us, OBJ_CASE_INSENSITIVE, 0, 0};
1086 + HANDLE hFile;
1087 + IO_STATUS_BLOCK isb;
1088 + return NT_SUCCESS(NtCreateFile(&hFile,
1089 + option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
1090 + &oa,&isb,0,0,share,disposition,
1091 + FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
1092 + hFile : INVALID_HANDLE_VALUE;
1093 +}
1094 +
1095 +//Analogous to IthCreateFile, but with full NT path.
1096 +HANDLE IthCreateFileFullPath(LPCWSTR path, DWORD option, DWORD share, DWORD disposition)
1097 +{
1098 + UNICODE_STRING us;
1099 + RtlInitUnicodeString(&us,path);
1100 + OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
1101 + HANDLE hFile;
1102 + IO_STATUS_BLOCK isb;
1103 + return NT_SUCCESS(NtCreateFile(&hFile,
1104 + option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
1105 + &oa,&isb,0,0,share,disposition,
1106 + FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
1107 + hFile : INVALID_HANDLE_VALUE;
1108 +}
1109 +
1110 +//Create section object for sharing memory between processes.
1111 +//Similar to CreateFileMapping.
1112 +HANDLE IthCreateSection(LPCWSTR name, DWORD size, DWORD right)
1113 +{
1114 +// jichi 9/25/2013: GENERIC_ALL does NOT work one wine
1115 +// See ZwCreateSection: http://msdn.microsoft.com/en-us/library/windows/hardware/ff566428%28v=vs.85%29.aspx
1116 +//#ifdef ITH_WINE
1117 + enum { DesiredAccess = SECTION_ALL_ACCESS };
1118 +//#else
1119 +// enum { DesiredAccess = GENERIC_ALL }; // jichi 9/25/2013: not sure whhy ITH is usin GENERIC_ALL
1120 +//#endif // ITH_WINE
1121 +#define eval (NT_SUCCESS(NtCreateSection(&hSection, DesiredAccess, poa, &s, \
1122 + right, SEC_COMMIT, 0)) ? hSection : INVALID_HANDLE_VALUE)
1123 + HANDLE hSection;
1124 + LARGE_INTEGER s = {size, 0};
1125 + OBJECT_ATTRIBUTES *poa = nullptr;
1126 + // jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
1127 + // is pointed to freed object on the stack?! This will crash wine!
1128 + if (name) {
1129 + UNICODE_STRING us;
1130 + RtlInitUnicodeString(&us, name);
1131 + OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us,OBJ_OPENIF,0,0};
1132 + poa = &oa;
1133 + return eval;
1134 + } else
1135 + return eval;
1136 +#undef retval
1137 +}
1138 +
1139 +//Create event object. Similar to CreateEvent.
1140 +HANDLE IthCreateEvent(LPCWSTR name, DWORD auto_reset, DWORD init_state)
1141 +{
1142 +#define eval (NT_SUCCESS(NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, poa, auto_reset, init_state)) ? \
1143 + hEvent : INVALID_HANDLE_VALUE)
1144 + HANDLE hEvent;
1145 + OBJECT_ATTRIBUTES *poa = nullptr;
1146 + // jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
1147 + // is pointed to freed object on the stack?! This will crash wine!
1148 + if (name) {
1149 + UNICODE_STRING us;
1150 + RtlInitUnicodeString(&us,name);
1151 + OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, OBJ_OPENIF, 0, 0};
1152 + poa = &oa;
1153 + return eval;
1154 + } else
1155 + return eval;
1156 +#undef eval
1157 +}
1158 +
1159 +HANDLE IthOpenEvent(LPCWSTR name)
1160 +{
1161 + UNICODE_STRING us;
1162 + RtlInitUnicodeString(&us, name);
1163 + OBJECT_ATTRIBUTES oa = { sizeof(oa), root_obj, &us, 0, 0, 0 };
1164 + HANDLE hEvent;
1165 + return NT_SUCCESS(NtOpenEvent(&hEvent, EVENT_ALL_ACCESS, &oa)) ?
1166 + hEvent : INVALID_HANDLE_VALUE;
1167 +}
1168 +
1169 +void IthSetEvent(HANDLE hEvent) { NtSetEvent(hEvent, 0); }
1170 +
1171 +void IthResetEvent(HANDLE hEvent) { NtClearEvent(hEvent); }
1172 +
1173 +//Create mutex object. Similar to CreateMutex.
1174 +//If 'exist' is not null, it will be written 1 if mutex exist.
1175 +HANDLE IthCreateMutex(LPCWSTR name, BOOL InitialOwner, DWORD *exist)
1176 +{
1177 +#ifdef ITH_ENABLE_WINAPI
1178 + HANDLE ret = ::CreateMutexW(nullptr, InitialOwner, name);
1179 + if (exist)
1180 + *exist = ret == INVALID_HANDLE_VALUE || ::GetLastError() == ERROR_ALREADY_EXISTS;
1181 + return ret;
1182 +#else
1183 +#define eval NtCreateMutant(&hMutex, MUTEX_ALL_ACCESS, poa, InitialOwner)
1184 + UNICODE_STRING us;
1185 + HANDLE hMutex;
1186 + NTSTATUS status;
1187 + OBJECT_ATTRIBUTES *poa = nullptr;
1188 + // jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
1189 + // is pointed to freed object on the stack?! This will crash wine!
1190 + if (name) {
1191 + //GROWL(name);
1192 + RtlInitUnicodeString(&us, name);
1193 + OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, OBJ_OPENIF, 0, 0};
1194 + poa = &oa;
1195 + status = eval;
1196 + //GROWL_DWORD(status);
1197 + } else
1198 + status = eval;
1199 + if (NT_SUCCESS(status)) {
1200 + if (exist)
1201 + *exist = status == STATUS_OBJECT_NAME_EXISTS;
1202 + return hMutex;
1203 + } else
1204 + return INVALID_HANDLE_VALUE;
1205 +#undef eval
1206 +#endif // ITH_ENABLE_WINAPI
1207 +}
1208 +
1209 +HANDLE IthOpenMutex(LPCWSTR name)
1210 +{
1211 +#ifdef ITH_ENABLE_WINAPI
1212 + return ::OpenMutexW(MUTEX_ALL_ACCESS, FALSE, name);
1213 +#else
1214 + UNICODE_STRING us;
1215 + RtlInitUnicodeString(&us, name);
1216 + OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, 0, 0, 0};
1217 + HANDLE hMutex;
1218 + if (NT_SUCCESS(NtOpenMutant(&hMutex, MUTEX_ALL_ACCESS, &oa)))
1219 + return hMutex;
1220 + else
1221 + return INVALID_HANDLE_VALUE;
1222 +#endif // ITH_ENABLE_WINAPI
1223 +}
1224 +
1225 +BOOL IthReleaseMutex(HANDLE hMutex)
1226 +{ return NT_SUCCESS(NtReleaseMutant(hMutex, 0)); }
1227 +
1228 +//Create new thread. 'hProc' must have following right.
1229 +//PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE.
1230 +HANDLE IthCreateThread(LPCVOID start_addr, DWORD param, HANDLE hProc)
1231 +{
1232 + HANDLE hThread;
1233 + // jichi 9/27/2013: NtCreateThread is not implemented in Wine 1.7
1234 + if (thread_man_) { // Windows XP
1235 + // jichi 9/29/2013: Reserved && commit stack size
1236 + // See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366803%28v=vs.85%29.aspx
1237 + // See: http://msdn.microsoft.com/en-us/library/ms810627.aspx
1238 + enum { DEFAULT_STACK_LIMIT = 0x400000 };
1239 + enum { DEFAULT_STACK_COMMIT = 0x10000 };
1240 + enum { PAGE_SIZE = 0x1000 };
1241 + CLIENT_ID id;
1242 + LPVOID protect;
1243 + USER_STACK stack = {};
1244 + CONTEXT ctx = {CONTEXT_FULL};
1245 + DWORD size = DEFAULT_STACK_LIMIT,
1246 + commit = DEFAULT_STACK_COMMIT;
1247 + if (!NT_SUCCESS(NtAllocateVirtualMemory(hProc, &stack.ExpandableStackBottom, 0, &size, MEM_RESERVE, PAGE_READWRITE)))
1248 + return INVALID_HANDLE_VALUE;
1249 +
1250 + stack.ExpandableStackBase = (char *)stack.ExpandableStackBottom + size;
1251 + stack.ExpandableStackLimit = (char *)stack.ExpandableStackBase - commit;
1252 + size = PAGE_SIZE;
1253 + commit += size;
1254 + protect = (char *)stack.ExpandableStackBase - commit;
1255 + NtAllocateVirtualMemory(hProc, &protect, 0, &commit, MEM_COMMIT, PAGE_READWRITE);
1256 + DWORD oldAccess; // jichi 9/29/2013: unused
1257 + NtProtectVirtualMemory(hProc, &protect, &size, PAGE_READWRITE|PAGE_GUARD, &oldAccess);
1258 + ctx.SegGs = 0;
1259 + ctx.SegFs = 0x38;
1260 + ctx.SegEs = 0x20;
1261 + ctx.SegDs = 0x20;
1262 + ctx.SegSs = 0x20;
1263 + ctx.SegCs = 0x18;
1264 + ctx.EFlags = 0x3000;
1265 + ctx.Eip = (DWORD)thread_man_->GetProcAddr(hProc);
1266 + ctx.Eax = (DWORD)start_addr;
1267 + ctx.Ecx = ctx.Eip + 0x40;
1268 + ctx.Edx = 0xffffffff;
1269 + ctx.Esp = (DWORD)stack.ExpandableStackBase - 0x10;
1270 + ctx.Ebp = param;
1271 +
1272 + // NTSYSAPI
1273 + // NTSTATUS
1274 + // NTAPI
1275 + // NtCreateThread(
1276 + // _Out_ PHANDLE ThreadHandle,
1277 + // _In_ ACCESS_MASK DesiredAccess,
1278 + // _In_ POBJECT_ATTRIBUTES ObjectAttributes,
1279 + // _In_ HANDLE ProcessHandle,
1280 + // _Out_ PCLIENT_ID ClientId,
1281 + // _In_ PCONTEXT ThreadContext,
1282 + // _In_ PUSER_STACK UserStack,
1283 + // _In_ BOOLEAN CreateSuspended
1284 + // );
1285 + if (NT_SUCCESS(NtCreateThread(
1286 + &hThread, // _Out_ PHANDLE ThreadHandle,
1287 + THREAD_ALL_ACCESS, // _In_ ACCESS_MASK DesiredAccess,
1288 + nullptr, // _In_ POBJECT_ATTRIBUTES ObjectAttributes,
1289 + hProc, // _In_ HANDLE ProcessHandle,
1290 + &id, // _Out_ PCLIENT_ID ClientId,
1291 + &ctx, // _In_ PCONTEXT ThreadContext,
1292 + &stack, // _In_ PUSER_STACK UserStack,
1293 + TRUE // _In_ BOOLEAN CreateSuspended
1294 + ))) {
1295 + // On x64 Windows, NtCreateThread in ntdll calls NtCreateThread in ntoskrnl via WOW64,
1296 + // which maps 32-bit system call to the correspond 64-bit version.
1297 + // This layer doesn't correctly copy whole CONTEXT structure, so we must set it manually
1298 + // after the thread is created.
1299 + // On x86 Windows, this step is not necessary.
1300 + NtSetContextThread(hThread, &ctx);
1301 + NtResumeThread(hThread, 0);
1302 + } else
1303 + hThread = INVALID_HANDLE_VALUE;
1304 +
1305 + } else {
1306 + // jichi 9/27/2013: CreateRemoteThread works on both Wine and Windows 7
1307 + // Use CreateRemoteThread instead
1308 + // FIXME 10/5/2031: Though sometimes works, CreateRemoteThread randomly crashes on wine.
1309 + // See:
1310 + // - http://www.unknowncheats.me/forum/c-and-c/64775-createremotethread-dll-injection.html
1311 + // - http://source.winehq.org/WineAPI/CreateRemoteThread.html
1312 + // - http://msdn.microsoft.com/en-us/library/windows/desktop/ms682437%28v=vs.85%29.aspx
1313 + // HANDLE WINAPI CreateRemoteThread(
1314 + // _In_ HANDLE hProcess,
1315 + // _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
1316 + // _In_ SIZE_T dwStackSize,
1317 + // _In_ LPTHREAD_START_ROUTINE lpStartAddress,
1318 + // _In_ LPVOID lpParameter,
1319 + // _In_ DWORD dwCreationFlags,
1320 + // _Out_ LPDWORD lpThreadId
1321 + // );
1322 + //ITH_TRY {
1323 + if (hProc == INVALID_HANDLE_VALUE)
1324 + hProc = GetCurrentProcess();
1325 + //DWORD dwThreadId;
1326 + hThread = CreateRemoteThread(
1327 + hProc, // _In_ HANDLE hProcess,
1328 + nullptr, // _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
1329 + 0, // _In_ SIZE_T dwStackSize,
1330 + (LPTHREAD_START_ROUTINE)start_addr, // _In_ LPTHREAD_START_ROUTINE lpStartAddress,
1331 + (LPVOID)param, // _In_ LPVOID lpParameter,
1332 + 0, //STACK_SIZE_PARAM_IS_A_RESERVATION // _In_ DWORD dwCreationFlags,
1333 + nullptr // _Out_ LPDWORD lpThreadId
1334 + );
1335 + if (!hThread) // jichi: this function returns nullptr instead of -1
1336 + hThread = INVALID_HANDLE_VALUE;
1337 + //} ITH_EXCEPT {
1338 + // ITH_WARN(L"exception");
1339 + // hThread = INVALID_HANDLE_VALUE;
1340 + //}
1341 + }
1342 + /*
1343 + else {
1344 + // jichi 9/29/2013: Also work on Wine and Windows 7
1345 + // See: http://waleedassar.blogspot.com/2012/06/createremotethread-vs.html
1346 + CLIENT_ID id;
1347 + //DWORD size = DEFAULT_STACK_LIMIT,
1348 + // commit = DEFAULT_STACK_COMMIT;
1349 + DWORD reserve = 0,
1350 + commit = 0;
1351 + // http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Executable%20Images/RtlCreateUserThread.html
1352 + // NTSYSAPI
1353 + // NTSTATUS
1354 + // NTAPI
1355 + // RtlCreateUserThread(
1356 + // IN HANDLE ProcessHandle,
1357 + // IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
1358 + // IN BOOLEAN CreateSuspended,
1359 + // IN ULONG StackZeroBits,
1360 + // IN OUT PULONG StackReserved,
1361 + // IN OUT PULONG StackCommit,
1362 + // IN PVOID StartAddress,
1363 + // IN PVOID StartParameter OPTIONAL,
1364 + // OUT PHANDLE ThreadHandle,
1365 + // OUT PCLIENT_ID ClientID);
1366 + if (!NT_SUCCESS(RtlCreateUserThread(
1367 + hProc, // HANDLE hProcess,
1368 + nullptr, // IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
1369 + FALSE, // IN BOOLEAN CreateSuspended,
1370 + 0, // IN ULONG StackZeroBits,
1371 + &reserve, // IN OUT PULONG StackReserved,
1372 + &commit, // IN OUT PULONG StackCommit,
1373 + (LPVOID)start_addr, // IN PVOID StartAddress,
1374 + (LPVOID)param,// IN PVOID StartParameter OPTIONAL,
1375 + &hThread, // OUT PHANDLE ThreadHandle,
1376 + &id // OUT PCLIENT_ID ClientID
1377 + )))
1378 + hThread = INVALID_HANDLE_VALUE;
1379 + }
1380 + */
1381 + return hThread;
1382 +}
1383 +
1384 +//Query module export table. Return function address if found.
1385 +//Similar to GetProcAddress
1386 +DWORD GetExportAddress(DWORD hModule,DWORD hash)
1387 +{
1388 + IMAGE_DOS_HEADER *DosHdr;
1389 + IMAGE_NT_HEADERS *NtHdr;
1390 + IMAGE_EXPORT_DIRECTORY *ExtDir;
1391 + UINT uj;
1392 + char* pcExportAddr,*pcFuncPtr,*pcBuffer;
1393 + DWORD dwReadAddr,dwFuncAddr,dwFuncName;
1394 + WORD wOrd;
1395 + DosHdr = (IMAGE_DOS_HEADER*)hModule;
1396 + if (IMAGE_DOS_SIGNATURE==DosHdr->e_magic) {
1397 + dwReadAddr=hModule+DosHdr->e_lfanew;
1398 + NtHdr=(IMAGE_NT_HEADERS*)dwReadAddr;
1399 + if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
1400 + pcExportAddr = (char*)((DWORD)hModule+
1401 + (DWORD)NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
1402 + if (!pcExportAddr)
1403 + return 0;
1404 + ExtDir = (IMAGE_EXPORT_DIRECTORY*)pcExportAddr;
1405 + pcExportAddr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfNames);
1406 +
1407 + for (uj = 0; uj < ExtDir->NumberOfNames; uj++) {
1408 + dwFuncName = *(DWORD *)pcExportAddr;
1409 + pcBuffer = (char*)((DWORD)hModule+dwFuncName);
1410 + if (GetHash(pcBuffer) == hash) {
1411 + pcFuncPtr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfNameOrdinals+(uj*sizeof(WORD)));
1412 + wOrd = *(WORD*)pcFuncPtr;
1413 + pcFuncPtr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfFunctions+(wOrd*sizeof(DWORD)));
1414 + dwFuncAddr = *(DWORD *)pcFuncPtr;
1415 + return hModule+dwFuncAddr;
1416 + }
1417 + pcExportAddr += sizeof(DWORD);
1418 + }
1419 + }
1420 + }
1421 + return 0;
1422 +}
1423 +
1424 +} // extern "C"
1425 +
1426 +// EOF
1427 +
1428 +/*__declspec(naked) void normal_asm()
1429 +{
1430 + __asm
1431 + {
1432 + push ecx
1433 + push edx
1434 + mov fs:[0],esp
1435 + push ebp
1436 + call eax
1437 +_terminate:
1438 + push eax
1439 + push -2
1440 + call dword ptr [NtTerminateThread]
1441 + }
1442 +}*/
1443 +
1444 +/*
1445 +__declspec(naked) void RegToStrAsm()
1446 +{
1447 + __asm
1448 + {
1449 + mov edx, 8
1450 +_cvt_loop:
1451 + mov eax, ecx
1452 + and eax, 0xF
1453 + cmp eax, 0xA
1454 + jb _below_ten
1455 + add al,7
1456 +_below_ten:
1457 + add al,0x30
1458 + stosw
1459 + ror ecx,4
1460 + dec edx
1461 + jne _cvt_loop
1462 + retn
1463 + }
1464 +}
1465 +__declspec(naked) void except_asm()
1466 +{
1467 + __asm
1468 + {
1469 + mov eax,[esp + 4]
1470 + xor esi,esi
1471 + mov ebp,[eax]
1472 + mov ecx,[esp + 0xC]
1473 + mov ebx,[ecx + 0xB8]
1474 + sub esp,0x240
1475 + lea edi,[esp + 0x40]
1476 + mov eax,esp
1477 + push esi
1478 + push 0x1C
1479 + push eax
1480 + push esi
1481 + push ebx
1482 + push -1
1483 + call dword ptr [NtQueryVirtualMemory]
1484 + test eax,eax
1485 + jne _terminate
1486 + mov eax,esp
1487 + push eax
1488 + push 0x200
1489 + push edi
1490 + push 2
1491 + push ebx
1492 + push -1
1493 + call dword ptr [NtQueryVirtualMemory]
1494 + test eax,eax
1495 + jne _terminate
1496 + pop esi
1497 + xadd edi,esi
1498 + std
1499 + mov al,0x5C
1500 + repen scasw
1501 + mov word ptr [edi + 2], 0x3A
1502 + mov ecx,ebx
1503 + sub ecx,[esp]
1504 + call RegToStrAsm
1505 + inc edi
1506 + inc edi
1507 + xchg esi,edi
1508 + mov ecx,ebp
1509 + call RegToStrAsm
1510 + inc edi
1511 + inc edi
1512 + xor eax,eax
1513 + mov [edi + 0x10], eax
1514 + push 0
1515 + push edi
1516 + push esi
1517 + push 0
1518 + call dword ptr [MessageBoxW]
1519 + or eax, -1
1520 + jmp _terminate
1521 + }
1522 +}
1523 +
1524 +//Prompt for file name.
1525 +HANDLE IthPromptCreateFile(DWORD option, DWORD share, DWORD disposition)
1526 +{
1527 + OPENFILENAME ofn = {sizeof(ofn)}; // common dialog box structure
1528 + WCHAR szFile[MAX_PATH]; // buffer for file name
1529 + wcscpy(current_dir,L"ITH_export.txt");
1530 + wcscpy(szFile,file_path);
1531 +
1532 + //szFile[0]=0;
1533 + ofn.lpstrFile = szFile + 4;
1534 + ofn.nMaxFile = MAX_PATH;
1535 + ofn.lpstrFilter = L"Text\0*.txt";
1536 + BOOL result;
1537 + if (disposition==FILE_OPEN)
1538 + result=GetOpenFileName(&ofn);
1539 + else
1540 + result=GetSaveFileName(&ofn);
1541 + if (result)
1542 + {
1543 + LPWSTR s=szFile+wcslen(szFile) - 4;
1544 + if (_wcsicmp(s,L".txt")!=0) wcscpy(s + 4,L".txt");
1545 + return IthCreateFileFullPath(szFile,option,share,disposition);
1546 + }
1547 + else return INVALID_HANDLE_VALUE;
1548 +}
1549 +*/
1 +#pragma once
2 +
3 +// ithsys.h
4 +// 8/23/2013 jichi
5 +// Branch: ITH/IHF_SYS.h, rev 111
6 +
7 +#ifdef _MSC_VER
8 +# pragma warning(disable:4800) // C4800: forcing value to bool
9 +#endif // _MSC_VER
10 +
11 +#include "ntdll/ntdll.h"
12 +
13 +// jichi 8/24/2013: Why extern "C"? Any specific reason to use C instead of C++ naming?
14 +extern "C" {
15 +//int disasm(BYTE *opcode0); // jichi 8/15/2013: move disasm to separate file
16 +extern WORD *NlsAnsiCodePage;
17 +int FillRange(LPCWSTR name,DWORD *lower, DWORD *upper);
18 +int MB_WC(char *mb, wchar_t *wc);
19 +//int MB_WC_count(char *mb, int mb_length);
20 +int WC_MB(wchar_t *wc, char *mb);
21 +
22 +// jichi 10/1/2013: Return 0 if failed. So, it is ambiguous if the search pattern starts at 0
23 +DWORD SearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length); // KMP
24 +
25 +// jichi 2/5/2014: The same as SearchPattern except it uses 0xff to match everything
26 +// According to @Andys, 0xff seldom appear in the source code: http://sakuradite.com/topic/124
27 +enum : BYTE { SP_ANY = 0xff };
28 +#define SP_ANY_2 SP_ANY,SP_ANY
29 +#define SP_ANY_3 SP_ANY,SP_ANY,SP_ANY
30 +#define SP_ANY_4 SP_ANY,SP_ANY,SP_ANY,SP_ANY
31 +DWORD SearchPatternEx(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length, BYTE wildcard=SP_ANY);
32 +
33 +BOOL IthInitSystemService();
34 +void IthCloseSystemService();
35 +DWORD IthGetMemoryRange(LPCVOID mem, DWORD *base, DWORD *size);
36 +BOOL IthCheckFile(LPCWSTR file);
37 +BOOL IthFindFile(LPCWSTR file);
38 +BOOL IthGetFileInfo(LPCWSTR file, LPVOID info, DWORD size = 0x1000);
39 +BOOL IthCheckFileFullPath(LPCWSTR file);
40 +HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition);
41 +HANDLE IthCreateFileInDirectory(LPCWSTR name, HANDLE dir, DWORD option, DWORD share, DWORD disposition);
42 +HANDLE IthCreateDirectory(LPCWSTR name);
43 +HANDLE IthCreateFileFullPath(LPCWSTR fullpath, DWORD option, DWORD share, DWORD disposition);
44 +HANDLE IthPromptCreateFile(DWORD option, DWORD share, DWORD disposition);
45 +HANDLE IthCreateSection(LPCWSTR name, DWORD size, DWORD right);
46 +HANDLE IthCreateEvent(LPCWSTR name, DWORD auto_reset=0, DWORD init_state=0);
47 +HANDLE IthOpenEvent(LPCWSTR name);
48 +void IthSetEvent(HANDLE hEvent);
49 +void IthResetEvent(HANDLE hEvent);
50 +HANDLE IthCreateMutex(LPCWSTR name, BOOL InitialOwner, DWORD *exist=0);
51 +HANDLE IthOpenMutex(LPCWSTR name);
52 +BOOL IthReleaseMutex(HANDLE hMutex);
53 +//DWORD IthWaitForSingleObject(HANDLE hObject, DWORD dwTime);
54 +HANDLE IthCreateThread(LPCVOID start_addr, DWORD param, HANDLE hProc=(HANDLE)-1);
55 +DWORD GetExportAddress(DWORD hModule,DWORD hash);
56 +void IthSleep(int time); // jichi 9/28/2013: in ms
57 +void IthSystemTimeToLocalTime(LARGE_INTEGER *ptime);
58 +void FreeThreadStart(HANDLE hProc);
59 +void CheckThreadStart();
60 +} // extern "C"
61 +
62 +#ifdef ITH_HAS_HEAP
63 +extern HANDLE hHeap; // used in ith/common/memory.h
64 +#endif // ITH_HAS_HEAP
65 +
66 +extern DWORD current_process_id;
67 +extern DWORD debug;
68 +extern BYTE LeadByteTable[];
69 +extern LPVOID page;
70 +extern BYTE launch_time[];
71 +
72 +inline DWORD GetHash(LPSTR str)
73 +{
74 + DWORD hash = 0;
75 + //for (; *str; str++)
76 + while (*str)
77 + hash = ((hash>>7) | (hash<<25)) + *str++;
78 + return hash;
79 +}
80 +
81 +inline DWORD GetHash(LPCWSTR str)
82 +{
83 + DWORD hash = 0;
84 + //for (; *str; str++)
85 + while (*str)
86 + hash = ((hash>>7) | (hash<<25)) + *str++;
87 + return hash;
88 +}
89 +
90 +inline void IthBreak()
91 +{ if (debug) __debugbreak(); }
92 +
93 +inline LPCWSTR GetMainModulePath()
94 +{
95 + __asm
96 + {
97 + mov eax, fs:[0x30]
98 + mov eax, [eax + 0xC]
99 + mov eax, [eax + 0xC]
100 + mov eax, [eax + 0x28]
101 + }
102 +}
103 +
104 +// jichi 9/28/2013: Add this to lock NtWriteFile in wine
105 +class IthMutexLocker
106 +{
107 + HANDLE m;
108 +public:
109 + explicit IthMutexLocker(HANDLE mutex) : m(mutex)
110 + { NtWaitForSingleObject(m, 0, 0); }
111 +
112 + ~IthMutexLocker() { if (m != INVALID_HANDLE_VALUE) IthReleaseMutex(m); }
113 +
114 + bool locked() const { return m != INVALID_HANDLE_VALUE; }
115 +
116 + void unlock() { if (m != INVALID_HANDLE_VALUE) { IthReleaseMutex(m); m = INVALID_HANDLE_VALUE; } }
117 +};
118 +
119 +void IthCoolDown();
120 +
121 +BOOL IthIsWine();
122 +BOOL IthIsWindowsXp();
123 +//BOOL IthIsWindows8OrGreater(); // not public
124 +
125 +/** Get current dll path.
126 + * @param buf
127 + * @param len
128 + * @return length of the path excluding \0
129 + */
130 +size_t IthGetCurrentModulePath(wchar_t *buf, size_t len);
131 +
132 +// EOF
1 +# ithsys.pri
2 +# 8/21/2013 jichi
3 +
4 +DEFINES += WITH_LIB_ITHSYS
5 +LIBS += -lithsys
6 +DEPENDPATH += $$PWD
7 +HEADERS += $$PWD/ithsys.h
8 +#SOURCES += $$PWD/ithsys.cc
9 +
10 +#include($$LIBDIR/winddk/winddk.pri)
11 +#LIBS += -L$$WDK/lib/wxp/i386
12 +
13 +# EOF
1 +# ithsys.pro
2 +# 8/21/2013 jichi
3 +# Build ithsys.lib
4 +
5 +#CONFIG += noqt noeh staticlib
6 +CONFIG += noqt staticlib
7 +
8 +include(../../../config.pri)
9 +include($$LIBDIR/ntdll/ntdll.pri)
10 +
11 +#LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
12 +
13 +#include($$LIBDIR/winddk/winddk.pri)
14 +#LIBS += -L$$WDK/lib/wxp/i386
15 +
16 +# jichi 9/22/2013: When ITH is on wine, certain NT functions are replaced
17 +#DEFINES += ITH_WINE
18 +
19 +# jichi 7/12/2015: Always enable SEH
20 +DEFINES += ITH_HAS_SEH
21 +
22 +# jichi 11/24/2013: Disable manual heap
23 +DEFINES -= ITH_HAS_HEAP
24 +
25 +## Libraries
26 +
27 +#INCLUDEPATH += $$ITH_HOME/include
28 +#INCLUDEPATH += $$WDK7_HOME/inc/ddk
29 +
30 +#LIBS += -lgdi32 -luser32 -lkernel32
31 +#LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
32 +#LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
33 +
34 +#DEFINES += ITH_HAS_CXX
35 +
36 +#LIBS += -lith_sys -lntdll
37 +#LIBS += -lith_tls -lntdll
38 +#LIBS += -lntoskrnl
39 +
40 +DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
41 +
42 +## Sources
43 +
44 +TEMPLATE = lib
45 +TARGET = ithsys
46 +
47 +HEADERS += ithsys.h
48 +SOURCES += ithsys.cc
49 +
50 +OTHER_FILES += ithsys.pri
51 +
52 +# EOF
...@@ -198,76 +198,190 @@ finish: ...@@ -198,76 +198,190 @@ finish:
198 } 198 }
199 } 199 }
200 200
201 +#if 0
202 +/**
203 + * Search from stopAddress back to startAddress - range
204 + * This function is not well debugged
205 + */
206 +DWORD reverseSearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length) // KMP
207 +{
208 + __asm
209 + {
210 + mov eax,search_length
211 +alloc:
212 + push 0
213 + sub eax,1
214 + jnz alloc
215 +
216 + mov edi,search
217 + mov edx,search_length
218 + mov ecx,1
219 + xor esi,esi
220 +build_table:
221 + mov al,byte ptr [edi+esi]
222 + cmp al,byte ptr [edi+ecx]
223 + sete al
224 + test esi,esi
225 + jz pre
226 + test al,al
227 + jnz pre
228 + mov esi,[esp+esi*4-4]
229 + jmp build_table
230 +pre:
231 + test al,al
232 + jz write_table
233 + inc esi
234 +write_table:
235 + mov [esp+ecx*4],esi
236 +
237 + inc ecx
238 + cmp ecx,edx
239 + jb build_table
240 +
241 + mov esi,base
242 + xor edx,edx
243 + mov ecx,edx
244 +matcher:
245 + mov al,byte ptr [edi+ecx]
246 + cmp al,byte ptr [esi-edx] // jichi 6/1/2014: The only place that is modified
247 + sete al
248 + test ecx,ecx
249 + jz match
250 + test al,al
251 + jnz match
252 + mov ecx, [esp+ecx*4-4]
253 + jmp matcher
254 +match:
255 + test al,al
256 + jz pre2
257 + inc ecx
258 + cmp ecx,search_length
259 + je finish
260 +pre2:
261 + inc edx
262 + cmp edx,base_length // search_length
263 + jb matcher
264 + mov edx,search_length
265 + dec edx
266 +finish:
267 + mov ecx,search_length
268 + sub edx,ecx
269 + lea eax,[edx+1]
270 + lea ecx,[ecx*4]
271 + add esp,ecx
272 + }
273 +}
274 +#endif // 0
275 +
201 // Modified from ITH findCallOrJmpAbs 276 // Modified from ITH findCallOrJmpAbs
202 // Example call: 277 // Example call:
203 // 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA 278 // 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
204 enum : WORD { 279 enum : WORD {
205 - word_jmp = 0x25ff 280 + word_jmp = 0x25ff // long jump
206 , word_call = 0x15ff // far call 281 , word_call = 0x15ff // far call
207 }; 282 };
283 +
284 +// Modified from ITH findCallOrJmpAbs
285 +enum : BYTE {
286 + byte_jmp = 0xe9 // long call
287 + , byte_call = 0xe8 // near call
288 + , byte_push_small = 0x6a // push byte operand
289 + , byte_push_large = 0x68 // push operand > 0xff
290 +};
291 +
208 /*** 292 /***
209 * Return the absolute address of op. Op takes 1 parameter. 293 * Return the absolute address of op. Op takes 1 parameter.
294 + * DWORD call with absolute address.
210 * 295 *
211 * @param op first half of the operator 296 * @param op first half of the operator
212 * @param arg1 the function address 297 * @param arg1 the function address
213 * @param start address 298 * @param start address
214 - * @param search range 299 + * @param stop address
300 + * @param offset search after start address
301 + * @param range search size
215 * @return absolute address or 0 302 * @return absolute address or 0
216 */ 303 */
217 -DWORD findWordCall(WORD op, DWORD arg1, DWORD start, DWORD size) 304 +DWORD findWordCall(WORD op, DWORD arg1, DWORD start, DWORD stop, DWORD offset, DWORD range)
218 { 305 {
219 typedef WORD optype; 306 typedef WORD optype;
220 typedef DWORD argtype; 307 typedef DWORD argtype;
221 308
222 - enum { START = 0x1000 }; // leading size to skip 309 + for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
223 - for (DWORD i = START; i < size - sizeof(argtype); i++)
224 if (op == *(optype *)(start + i)) { 310 if (op == *(optype *)(start + i)) {
225 DWORD t = *(DWORD *)(start + i + sizeof(optype)); 311 DWORD t = *(DWORD *)(start + i + sizeof(optype));
226 - if (t > start && t < start + size) { 312 + if (t > start && t < stop) {
227 - if (arg1 == *(argtype *)t) 313 + if (arg1 == *(argtype *)t) // absolute address
228 return start + i; 314 return start + i;
229 - else 315 + //i += sizeof(optype) + sizeof(argtype) - 1; // == 5
230 - i += sizeof(optype) + sizeof(argtype) - 1; // == 5
231 } 316 }
232 } 317 }
233 return 0; 318 return 0;
234 } 319 }
235 320
236 -// Modified from ITH findCallOrJmpAbs 321 +DWORD findLastWordCall(WORD op, DWORD arg1, DWORD start, DWORD stop, DWORD offset, DWORD range)
237 -enum : BYTE { 322 +{
238 - byte_call = 0xe8 // near call 323 + typedef WORD optype;
239 - , byte_push_small = 0x6a // push byte operand 324 + typedef DWORD argtype;
240 - , byte_push_large = 0x68 // push operand > 0xff 325 + DWORD ret = 0;
241 -}; 326 +
327 + for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
328 + if (op == *(optype *)(start + i)) {
329 + DWORD t = *(DWORD *)(start + i + sizeof(optype));
330 + if (t > start && t < stop) {
331 + if (arg1 == *(argtype *)t) // absolute address
332 + ret = start + i;
333 + //i += sizeof(optype) + sizeof(argtype) - 1; // == 5
334 + }
335 + }
336 + return ret;
337 +}
338 +
242 339
243 /*** 340 /***
244 * Return the absolute address of op. Op takes 1 address parameter. 341 * Return the absolute address of op. Op takes 1 address parameter.
342 + * BYTE call with relative address.
245 * 343 *
246 * @param op first half of the operator 344 * @param op first half of the operator
247 * @param arg1 the function address 345 * @param arg1 the function address
248 * @param start address 346 * @param start address
249 - * @param search range 347 + * @param offset search after start address
348 + * @param range search size
250 * @return absolute address or 0 349 * @return absolute address or 0
251 */ 350 */
252 -DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size) 351 +DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD offset, DWORD range)
253 { 352 {
254 typedef BYTE optype; 353 typedef BYTE optype;
255 typedef DWORD argtype; 354 typedef DWORD argtype;
256 355
257 - enum { START = 0x1000 }; // leading size to skip 356 + for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
258 - for (DWORD i = START; i < size - sizeof(argtype); i++)
259 if (op == *(optype *)(start + i)) { 357 if (op == *(optype *)(start + i)) {
260 - DWORD t = *(DWORD *)(start + i + sizeof(optype)); 358 + DWORD t = *(argtype *)(start + i + sizeof(optype));
261 - if (t > start && t < start + size) { 359 + //if (t > start && t < stop) {
262 - if (arg1 == *(argtype *)t) 360 + if (arg1 == t + start + i + sizeof(optype) + sizeof(argtype)) // relative address
263 - return start + i; 361 + return start + i;
264 - else 362 + //i += sizeof(optype) + sizeof(argtype) - 1; // == 4
265 - i += sizeof(optype) + sizeof(argtype) - 1; // == 4 363 + //}
266 - }
267 } 364 }
268 return 0; 365 return 0;
269 } 366 }
270 367
368 +DWORD findLastByteCall(BYTE op, DWORD arg1, DWORD start, DWORD offset, DWORD range)
369 +{
370 + typedef BYTE optype;
371 + typedef DWORD argtype;
372 + DWORD ret = 0;
373 + for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
374 + if (op == *(optype *)(start + i)) {
375 + DWORD t = *(argtype *)(start + i + sizeof(optype));
376 + //if (t > start && t < stop) {
377 + if (arg1 == t + start + i + sizeof(optype) + sizeof(argtype)) // relative address
378 + ret = start + i;
379 + //i += sizeof(optype) + sizeof(argtype) - 1; // == 4
380 + //}
381 + }
382 + return ret;
383 +}
384 +
271 /*** 385 /***
272 * Return the absolute address of op. Op takes 1 parameter. 386 * Return the absolute address of op. Op takes 1 parameter.
273 * 387 *
...@@ -277,13 +391,12 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size) ...@@ -277,13 +391,12 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
277 * @param search range 391 * @param search range
278 * @return absolute address or 0 392 * @return absolute address or 0
279 */ 393 */
280 -//DWORD findByteOp1(BYTE op, DWORD arg1, DWORD start, DWORD size) 394 +//DWORD findByteOp1(BYTE op, DWORD arg1, DWORD start, DWORD size, DWORD offset)
281 //{ 395 //{
282 // typedef BYTE optype; 396 // typedef BYTE optype;
283 // typedef DWORD argtype; 397 // typedef DWORD argtype;
284 // 398 //
285 -// enum { START = 0x1000 }; // leading size to skip 399 +// for (DWORD i = offset; i < size - sizeof(argtype); i++)
286 -// for (DWORD i = START; i < size - sizeof(argtype); i++)
287 // if (op == *(optype *)(start + i)) { 400 // if (op == *(optype *)(start + i)) {
288 // DWORD t = *(DWORD *)(start + i + sizeof(optype)); 401 // DWORD t = *(DWORD *)(start + i + sizeof(optype));
289 // if (t == arg1) { 402 // if (t == arg1) {
...@@ -299,14 +412,29 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size) ...@@ -299,14 +412,29 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
299 412
300 MEMDBG_BEGIN_NAMESPACE 413 MEMDBG_BEGIN_NAMESPACE
301 414
302 -DWORD findJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound) 415 +DWORD findLongJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
303 -{ return findWordCall(word_jmp, funcAddr, lowerBound, upperBound - lowerBound); } 416 +{ return findWordCall(word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
417 +
418 +DWORD findShortJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
419 +{ return findByteCall(byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
420 +
421 +DWORD findFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
422 +{ return findWordCall(word_call, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
304 423
305 -DWORD findFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound) 424 +DWORD findNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
306 -{ return findWordCall(word_call, funcAddr, lowerBound, upperBound - lowerBound); } 425 +{ return findByteCall(byte_call, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
307 426
308 -DWORD findNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound) 427 +DWORD findLastLongJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
309 -{ return findByteCall(byte_call, funcAddr, lowerBound, upperBound - lowerBound); } 428 +{ return findLastWordCall(word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
429 +
430 +DWORD findLastShortJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
431 +{ return findLastByteCall(byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
432 +
433 +DWORD findLastFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
434 +{ return findLastWordCall(word_call, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
435 +
436 +DWORD findLastNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
437 +{ return findLastByteCall(byte_call, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
310 438
311 DWORD findPushDwordAddress(DWORD value, DWORD lowerBound, DWORD upperBound) 439 DWORD findPushDwordAddress(DWORD value, DWORD lowerBound, DWORD upperBound)
312 { 440 {
...@@ -322,9 +450,8 @@ DWORD findPushByteAddress(BYTE value, DWORD lowerBound, DWORD upperBound) ...@@ -322,9 +450,8 @@ DWORD findPushByteAddress(BYTE value, DWORD lowerBound, DWORD upperBound)
322 return findBytes(bytes, sizeof(bytes), lowerBound, upperBound); 450 return findBytes(bytes, sizeof(bytes), lowerBound, upperBound);
323 } 451 }
324 452
325 -DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength) 453 +DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
326 { 454 {
327 - enum { Start = 0x1000 };
328 enum { PatternSize = 4 }; 455 enum { PatternSize = 4 };
329 const DWORD size = upperBound - lowerBound - PatternSize; 456 const DWORD size = upperBound - lowerBound - PatternSize;
330 const DWORD fun = (DWORD)funcAddr; 457 const DWORD fun = (DWORD)funcAddr;
...@@ -332,9 +459,9 @@ DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upper ...@@ -332,9 +459,9 @@ DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upper
332 // 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA 459 // 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
333 //WCHAR str[0x40]; 460 //WCHAR str[0x40];
334 const DWORD mask = sigMask(sig); 461 const DWORD mask = sigMask(sig);
335 - for (DWORD i = Start; i < size; i++) 462 + for (DWORD i = offset; i < size; i++)
336 if (*(WORD *)(lowerBound + i) == word_call) { 463 if (*(WORD *)(lowerBound + i) == word_call) {
337 - DWORD t = *(DWORD *)(lowerBound + i + 2); 464 + DWORD t = *(DWORD *)(lowerBound + i + 2); // 2 = sizeof(word)
338 if (t >= lowerBound && t <= upperBound - PatternSize) { 465 if (t >= lowerBound && t <= upperBound - PatternSize) {
339 if (*(DWORD *)t == fun) 466 if (*(DWORD *)t == fun)
340 //swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i); 467 //swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
...@@ -352,9 +479,154 @@ DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upper ...@@ -352,9 +479,154 @@ DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upper
352 return 0; 479 return 0;
353 } 480 }
354 481
355 -DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount, DWORD lowerBound, DWORD upperBound, DWORD reverseLength) 482 +#ifndef MEMDBG_NO_STL
483 +
484 +bool iterFindBytes(const address_fun_t &fun, const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
485 +{
486 + for (DWORD addr = lowerBound; addr < upperBound - patternSize; addr += patternSize) {
487 + addr = findBytes(pattern, patternSize, addr, upperBound);
488 + if (!addr || !fun(addr))
489 + return false;
490 + }
491 + return true;
492 +}
493 +
494 +bool iterMatchBytes(const address_fun_t &fun, const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
495 +{
496 + for (DWORD addr = lowerBound; addr < upperBound - patternSize; addr += patternSize) { ;
497 + addr = matchBytes(pattern, patternSize, addr, upperBound);
498 + if (!addr || !fun(addr))
499 + return false;
500 + }
501 + return true;
502 +}
503 +
504 +bool iterWordCall(const address_fun_t &callback, WORD op, DWORD arg1, DWORD start, DWORD stop, DWORD offset, DWORD range)
505 +{
506 + typedef WORD optype;
507 + typedef DWORD argtype;
508 +
509 + for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
510 + if (op == *(optype *)(start + i)) {
511 + DWORD t = *(DWORD *)(start + i + sizeof(optype));
512 + if (t > start && t < stop) {
513 + if (arg1 == *(argtype *)t // absolute address
514 + && !callback(start + i))
515 + return false;
516 + //i += sizeof(optype) + sizeof(argtype) - 1; // == 5
517 + }
518 + }
519 + return true;
520 +}
521 +
522 +bool iterByteCall(const address_fun_t &callback, BYTE op, DWORD arg1, DWORD start, DWORD offset, DWORD range)
523 +{
524 + typedef BYTE optype;
525 + typedef DWORD argtype;
526 +
527 + for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
528 + if (op == *(optype *)(start + i)) {
529 + DWORD t = *(argtype *)(start + i + sizeof(optype));
530 + //if (t > start && t < stop) {
531 + if (arg1 == t + start + i + sizeof(optype) + sizeof(argtype) // relative address
532 + && !callback(start + i))
533 + return false;
534 + //i += sizeof(optype) + sizeof(argtype) - 1; // == 4
535 + //}
536 + }
537 + return true;
538 +}
539 +
540 +bool iterCallerAddress(const address2_fun_t &callback, DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
541 +{
542 + enum { PatternSize = 4 };
543 + const DWORD size = upperBound - lowerBound - PatternSize;
544 + const DWORD fun = (DWORD)funcAddr;
545 + // Example function call:
546 + // 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
547 + //WCHAR str[0x40];
548 + const DWORD mask = sigMask(sig);
549 + for (DWORD i = offset; i < size; i++)
550 + if (*(WORD *)(lowerBound + i) == word_call) {
551 + DWORD t = *(DWORD *)(lowerBound + i + 2); // 2 = sizeof(word)
552 + if (t >= lowerBound && t <= upperBound - PatternSize) {
553 + if (*(DWORD *)t == fun)
554 + //swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
555 + //OutputConsole(str);
556 + for (DWORD j = i ; j > i - reverseLength; j--)
557 + if ((*(DWORD *)(lowerBound + j) & mask) == sig) {
558 + if (!callback(lowerBound + j, lowerBound + i))
559 + return false;
560 + break;
561 + }
562 +
563 + } else
564 + i += 6;
565 + }
566 + //OutputConsole(L"Find call and entry failed.");
567 + return true;
568 +}
569 +
570 +bool iterCallerAddressAfterInt3(const address2_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
571 +{
572 + auto callback = [&fun](dword_t addr, dword_t call) -> bool {
573 + while (byte_int3 == *(BYTE *)++addr); // skip leading int3
574 + return fun(addr, call);
575 + };
576 + return iterCallerAddress(callback, funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
577 +}
578 +
579 +bool iterUniqueCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
580 +{
581 + dword_t prevAddr = 0;
582 + auto callback = [&fun, &prevAddr](dword_t addr, dword_t) -> bool {
583 + if (prevAddr == addr)
584 + return true;
585 + prevAddr = addr;
586 + return fun(addr);
587 + };
588 + return iterCallerAddress(callback, funcAddr, funcInst, lowerBound, upperBound, callerSearchSize, offset);
589 +}
590 +
591 +bool iterUniqueCallerAddressAfterInt3(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
592 +{
593 + auto callback = [&fun](dword_t addr) -> bool {
594 + while (byte_int3 == *(BYTE *)++addr); // skip leading int3
595 + return fun(addr);
596 + };
597 + return iterUniqueCallerAddress(callback, funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
598 +}
599 +
600 +bool iterLongJumpAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
601 +{ return iterWordCall(fun, word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
602 +
603 +bool iterShortJumpAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
604 +{ return iterByteCall(fun, byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
605 +
606 +bool iterFarCallAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
607 +{ return iterWordCall(fun, word_call, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
608 +
609 +bool iterNearCallAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
610 +{ return iterByteCall(fun, byte_call, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
611 +
612 +bool iterAlignedNearCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
613 +{
614 + dword_t prevAddr = 0;
615 + auto callback = [&fun, &prevAddr, callerSearchSize](dword_t addr) -> bool {
616 + if ((addr = findEnclosingAlignedFunction(addr, callerSearchSize))
617 + && prevAddr != addr) {
618 + prevAddr = addr;
619 + return fun(addr);
620 + }
621 + return true;
622 + };
623 + return iterNearCallAddress(callback, funcAddr, lowerBound, upperBound, offset);
624 +}
625 +
626 +#endif // MEMDBG_NO_STL
627 +
628 +DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
356 { 629 {
357 - enum { Start = 0x1000 };
358 enum { PatternSize = 4 }; 630 enum { PatternSize = 4 };
359 const DWORD size = upperBound - lowerBound - PatternSize; 631 const DWORD size = upperBound - lowerBound - PatternSize;
360 const DWORD fun = (DWORD)funcAddr; 632 const DWORD fun = (DWORD)funcAddr;
...@@ -367,9 +639,9 @@ DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount, ...@@ -367,9 +639,9 @@ DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount,
367 for (DWORD k = 0; k < sigCount; k++) 639 for (DWORD k = 0; k < sigCount; k++)
368 masks[k] = sigMask(sigs[k]); 640 masks[k] = sigMask(sigs[k]);
369 641
370 - for (DWORD i = Start; i < size; i++) 642 + for (DWORD i = offset; i < size; i++)
371 if (*(WORD *)(lowerBound + i) == word_call) { 643 if (*(WORD *)(lowerBound + i) == word_call) {
372 - DWORD t = *(DWORD *)(lowerBound + i + 2); 644 + DWORD t = *(DWORD *)(lowerBound + i + 2); // 2 = sizeof(word)
373 if (t >= lowerBound && t <= upperBound - PatternSize) { 645 if (t >= lowerBound && t <= upperBound - PatternSize) {
374 if (*(DWORD *)t == fun) 646 if (*(DWORD *)t == fun)
375 //swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i); 647 //swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
...@@ -391,16 +663,15 @@ DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount, ...@@ -391,16 +663,15 @@ DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount,
391 return 0; 663 return 0;
392 } 664 }
393 665
394 -DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength) 666 +DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
395 { 667 {
396 - enum { Start = 0x1000 };
397 enum { PatternSize = 4 }; 668 enum { PatternSize = 4 };
398 const DWORD size = upperBound - lowerBound - PatternSize; 669 const DWORD size = upperBound - lowerBound - PatternSize;
399 const DWORD fun = (DWORD)funcAddr; 670 const DWORD fun = (DWORD)funcAddr;
400 //WCHAR str[0x40]; 671 //WCHAR str[0x40];
401 DWORD ret = 0; 672 DWORD ret = 0;
402 const DWORD mask = sigMask(sig); 673 const DWORD mask = sigMask(sig);
403 - for (DWORD i = Start; i < size; i++) 674 + for (DWORD i = offset; i < size; i++)
404 if (*(WORD *)(lowerBound + i) == word_call) { 675 if (*(WORD *)(lowerBound + i) == word_call) {
405 DWORD t = *(DWORD *)(lowerBound + i + 2); 676 DWORD t = *(DWORD *)(lowerBound + i + 2);
406 if (t >= lowerBound && t <= upperBound - PatternSize) { 677 if (t >= lowerBound && t <= upperBound - PatternSize) {
...@@ -408,10 +679,12 @@ DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD u ...@@ -408,10 +679,12 @@ DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD u
408 //swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i); 679 //swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
409 //OutputConsole(str); 680 //OutputConsole(str);
410 for (DWORD j = i ; j > i - reverseLength; j--) 681 for (DWORD j = i ; j > i - reverseLength; j--)
411 - if ((*(DWORD *)(lowerBound + j) & mask) == sig) // Fun entry 1. 682 + if ((*(DWORD *)(lowerBound + j) & mask) == sig) { // Fun entry 1.
412 //swprintf(str,L"Entry: 0x%.8X",lowerBound + j); 683 //swprintf(str,L"Entry: 0x%.8X",lowerBound + j);
413 //OutputConsole(str); 684 //OutputConsole(str);
414 ret = lowerBound + j; 685 ret = lowerBound + j;
686 + break;
687 + }
415 688
416 } else 689 } else
417 i += 6; 690 i += 6;
...@@ -420,22 +693,36 @@ DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD u ...@@ -420,22 +693,36 @@ DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD u
420 return ret; 693 return ret;
421 } 694 }
422 695
423 -DWORD findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize) 696 +DWORD findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
424 { 697 {
425 - DWORD addr = findCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize); 698 + DWORD addr = findCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
426 if (addr) 699 if (addr)
427 while (byte_int3 == *(BYTE *)++addr); 700 while (byte_int3 == *(BYTE *)++addr);
428 return addr; 701 return addr;
429 } 702 }
430 703
431 -DWORD findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize) 704 +DWORD findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
432 { 705 {
433 - DWORD addr = findLastCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize); 706 + DWORD addr = findLastCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
434 if (addr) 707 if (addr)
435 while (byte_int3 == *(BYTE *)++addr); 708 while (byte_int3 == *(BYTE *)++addr);
436 return addr; 709 return addr;
437 } 710 }
438 711
712 +DWORD findAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
713 +{
714 + if (DWORD addr = findNearCallAddress(funcAddr, lowerBound, upperBound, offset))
715 + return findEnclosingAlignedFunction(addr, callerSearchSize);
716 + return 0;
717 +}
718 +
719 +DWORD findLastAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
720 +{
721 + if (DWORD addr = findLastCallerAddressAfterInt3(funcAddr, lowerBound, upperBound, callerSearchSize, offset))
722 + return findEnclosingAlignedFunction(addr, callerSearchSize);
723 + return 0;
724 +}
725 +
439 DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range) 726 DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range)
440 { 727 {
441 start &= ~0xf; 728 start &= ~0xf;
...@@ -466,6 +753,31 @@ DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range) ...@@ -466,6 +753,31 @@ DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range)
466 return 0; 753 return 0;
467 } 754 }
468 755
756 +DWORD findEnclosingFunctionAfterDword(DWORD sig, DWORD start, DWORD back_range, DWORD step)
757 +{
758 + start &= ~0xf;
759 + for (DWORD i = start, j = start - back_range; i > j; i-=step) { // 0x10 is aligned
760 + DWORD k = *(DWORD *)(i-4); // 4 = sizeof(DWORD)
761 + if (k == sig)
762 + return i;
763 + }
764 + return 0;
765 +}
766 +
767 +DWORD findEnclosingFunctionBeforeDword(DWORD sig, DWORD start, DWORD back_range,DWORD step)
768 +{
769 + DWORD addr = findEnclosingFunctionAfterDword(sig, start, back_range, step);
770 + if (addr)
771 + addr -= sizeof(DWORD);
772 + return addr;
773 +}
774 +
775 +DWORD findEnclosingFunctionAfterInt3(DWORD start, DWORD back_range, DWORD step)
776 +{ return findEnclosingFunctionAfterDword(0xcccccccc, start, back_range, step); }
777 +
778 +DWORD findEnclosingFunctionAfterNop(DWORD start, DWORD back_range, DWORD step)
779 +{ return findEnclosingFunctionAfterDword(0x90909090,start, back_range, step); }
780 +
469 DWORD findBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound) 781 DWORD findBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
470 { 782 {
471 DWORD reladdr = searchPattern(lowerBound, upperBound - lowerBound, pattern, patternSize); 783 DWORD reladdr = searchPattern(lowerBound, upperBound - lowerBound, pattern, patternSize);
...@@ -478,6 +790,12 @@ DWORD matchBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD ...@@ -478,6 +790,12 @@ DWORD matchBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD
478 return reladdr ? lowerBound + reladdr : 0; 790 return reladdr ? lowerBound + reladdr : 0;
479 } 791 }
480 792
793 +//DWORD reverseFindBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
794 +//{
795 +// DWORD reladdr = reverseSearchPattern(lowerBound, upperBound - lowerBound, pattern, patternSize);
796 +// return reladdr ? lowerBound + reladdr : 0;
797 +//}
798 +
481 #if 0 // not used 799 #if 0 // not used
482 DWORD findBytesInPages(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound, SearchType search) 800 DWORD findBytesInPages(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound, SearchType search)
483 { 801 {
...@@ -552,81 +870,3 @@ DWORD matchBytesInPages(const void *pattern, DWORD patternSize, DWORD lowerBound ...@@ -552,81 +870,3 @@ DWORD matchBytesInPages(const void *pattern, DWORD patternSize, DWORD lowerBound
552 MEMDBG_END_NAMESPACE 870 MEMDBG_END_NAMESPACE
553 871
554 // EOF 872 // EOF
555 -
556 -#if 0 // disabled
557 -
558 -/**
559 - * Search from stopAddres back to startAddress - range
560 - * This function is not well debugged
561 - */
562 -DWORD reverseSearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length) // KMP
563 -{
564 - __asm
565 - {
566 - mov eax,search_length
567 -alloc:
568 - push 0
569 - sub eax,1
570 - jnz alloc
571 -
572 - mov edi,search
573 - mov edx,search_length
574 - mov ecx,1
575 - xor esi,esi
576 -build_table:
577 - mov al,byte ptr [edi+esi]
578 - cmp al,byte ptr [edi+ecx]
579 - sete al
580 - test esi,esi
581 - jz pre
582 - test al,al
583 - jnz pre
584 - mov esi,[esp+esi*4-4]
585 - jmp build_table
586 -pre:
587 - test al,al
588 - jz write_table
589 - inc esi
590 -write_table:
591 - mov [esp+ecx*4],esi
592 -
593 - inc ecx
594 - cmp ecx,edx
595 - jb build_table
596 -
597 - mov esi,base
598 - xor edx,edx
599 - mov ecx,edx
600 -matcher:
601 - mov al,byte ptr [edi+ecx]
602 - cmp al,byte ptr [esi-edx] // jichi 6/1/2014: The only place that is modified
603 - sete al
604 - test ecx,ecx
605 - jz match
606 - test al,al
607 - jnz match
608 - mov ecx, [esp+ecx*4-4]
609 - jmp matcher
610 -match:
611 - test al,al
612 - jz pre2
613 - inc ecx
614 - cmp ecx,search_length
615 - je finish
616 -pre2:
617 - inc edx
618 - cmp edx,base_length // search_length
619 - jb matcher
620 - mov edx,search_length
621 - dec edx
622 -finish:
623 - mov ecx,search_length
624 - sub edx,ecx
625 - lea eax,[edx+1]
626 - lea ecx,[ecx*4]
627 - add esp,ecx
628 - }
629 -}
630 -
631 -#endif // 0, disabled
632 -
......
...@@ -5,14 +5,53 @@ ...@@ -5,14 +5,53 @@
5 // 4/20/2014 jichi 5 // 4/20/2014 jichi
6 6
7 #include "memdbg/memdbg.h" 7 #include "memdbg/memdbg.h"
8 +#ifndef MEMDBG_NO_STL
9 +# include <functional>
10 +#endif // MEMDBG_NO_STL
8 11
9 MEMDBG_BEGIN_NAMESPACE 12 MEMDBG_BEGIN_NAMESPACE
10 13
11 /// Estimated maximum size of the caller function, the same as ITH FindCallAndEntryAbs 14 /// Estimated maximum size of the caller function, the same as ITH FindCallAndEntryAbs
12 enum { MaximumFunctionSize = 0x800 }; 15 enum { MaximumFunctionSize = 0x800 };
13 16
17 +/// Offset added to the beginning of the searched address
18 +enum { MemoryPaddingOffset = 0x1000 };
19 +
20 +enum { MemoryAlignedStep = 0x10 };
21 +
22 +#ifndef MEMDBG_NO_STL
23 +/// Iterate address and return false if abort iteration.
24 +typedef std::function<bool (dword_t)> address_fun_t;
25 +typedef std::function<bool (dword_t, dword_t)> address2_fun_t;
26 +
14 /** 27 /**
15 - * Return the absolute address of the caller function 28 + * Iterate all call and caller addresses
29 + * @param fun the first parameter is the address of the caller, and the second parameter is the address of the call itself
30 + * @return false if return early, and true if iterate all elements
31 + */
32 +bool iterCallerAddress(const address2_fun_t &fun, dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
33 +bool iterCallerAddressAfterInt3(const address2_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
34 +bool iterUniqueCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
35 +bool iterUniqueCallerAddressAfterInt3(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
36 +
37 +/**
38 + * Iterate all call and caller addresses
39 + * @param fun the parameter is the address of the call
40 + * @return false if return early, and true if iterate all elements
41 + */
42 +bool iterFarCallAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
43 +bool iterNearCallAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
44 +bool iterLongJumpAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
45 +bool iterShortJumpAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
46 +
47 +bool iterAlignedNearCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
48 +
49 +bool iterFindBytes(const address_fun_t &fun, const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
50 +bool iterMatchBytes(const address_fun_t &fun, const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
51 +#endif // MEMDBG_NO_STL
52 +
53 +/**
54 + * Return the absolute address of the far caller function
16 * The same as ITH FindCallAndEntryAbs(). 55 * The same as ITH FindCallAndEntryAbs().
17 * 56 *
18 * @param funcAddr callee function address 57 * @param funcAddr callee function address
...@@ -27,12 +66,15 @@ enum { MaximumFunctionSize = 0x800 }; ...@@ -27,12 +66,15 @@ enum { MaximumFunctionSize = 0x800 };
27 * 0x81,0xec: sub esp XXOO (0xec81) 66 * 0x81,0xec: sub esp XXOO (0xec81)
28 * 0x83,0xec: sub esp XXOO (0xec83) 67 * 0x83,0xec: sub esp XXOO (0xec83)
29 */ 68 */
30 -dword_t findCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize); 69 +dword_t findCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
31 -dword_t findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize); 70 +dword_t findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
32 -dword_t findLastCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize); 71 +dword_t findLastCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
33 -dword_t findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize); 72 +dword_t findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
73 +
74 +dword_t findMultiCallerAddress(dword_t funcAddr, const dword_t funcInsts[], dword_t funcInstCount, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
34 75
35 -dword_t findMultiCallerAddress(dword_t funcAddr, const dword_t funcInsts[], dword_t funcInstCount, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize); 76 +dword_t findAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
77 +dword_t findLastAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
36 78
37 /** 79 /**
38 * Return the absolute address of the long jump (not short jump) instruction address. 80 * Return the absolute address of the long jump (not short jump) instruction address.
...@@ -41,9 +83,14 @@ dword_t findMultiCallerAddress(dword_t funcAddr, const dword_t funcInsts[], dwor ...@@ -41,9 +83,14 @@ dword_t findMultiCallerAddress(dword_t funcAddr, const dword_t funcInsts[], dwor
41 * @param funcAddr callee function address 83 * @param funcAddr callee function address
42 * @param lowerBound the lower memory address to search 84 * @param lowerBound the lower memory address to search
43 * @param upperBound the upper memory address to search 85 * @param upperBound the upper memory address to search
86 + * @param* offset the relative address to search from the lowerBound
87 + * @param* range the relative size to search, use lowerBound - upperBound when zero
44 * @return the call instruction address if succeed or 0 if fail 88 * @return the call instruction address if succeed or 0 if fail
45 */ 89 */
46 -dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound); 90 +dword_t findLongJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
91 +dword_t findShortJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
92 +dword_t findLastLongJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
93 +dword_t findLastShortJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
47 94
48 /** 95 /**
49 * Return the absolute address of the far call (inter-module) instruction address. 96 * Return the absolute address of the far call (inter-module) instruction address.
...@@ -52,16 +99,28 @@ dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound ...@@ -52,16 +99,28 @@ dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound
52 * @param funcAddr callee function address 99 * @param funcAddr callee function address
53 * @param lowerBound the lower memory address to search 100 * @param lowerBound the lower memory address to search
54 * @param upperBound the upper memory address to search 101 * @param upperBound the upper memory address to search
102 + * @param* offset the relative address to search from the lowerBound
103 + * @param* range the relative size to search, use lowerBound - upperBound when zero
55 * @return the call instruction address if succeed or 0 if fail 104 * @return the call instruction address if succeed or 0 if fail
56 */ 105 */
57 -dword_t findFarCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound); 106 +dword_t findFarCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
107 +dword_t findLastFarCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
58 108
59 /// Near call (intra-module) 109 /// Near call (intra-module)
60 -dword_t findNearCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound); 110 +dword_t findNearCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
111 +dword_t findLastNearCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
112 +
113 +/// Default to far call, for backward compatibility
114 +inline dword_t findCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
115 +{ return findFarCallAddress(funcAddr, lowerBound, upperBound, offset, range); }
116 +inline dword_t findLastCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
117 +{ return findLastFarCallAddress(funcAddr, lowerBound, upperBound, offset, range); }
61 118
62 -/// Default to far call 119 +/// Default to long jump, for backward compatibility
63 -inline dword_t findCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound) 120 +inline dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
64 -{ return findFarCallAddress(funcAddr, lowerBound, upperBound); } 121 +{ return findLongJumpAddress(funcAddr, lowerBound, upperBound, offset, range); }
122 +inline dword_t findLastJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
123 +{ return findLastLongJumpAddress(funcAddr, lowerBound, upperBound, offset, range); }
65 124
66 /// Push value >= 0xff 125 /// Push value >= 0xff
67 dword_t findPushDwordAddress(dword_t value, dword_t lowerBound, dword_t upperBound); 126 dword_t findPushDwordAddress(dword_t value, dword_t lowerBound, dword_t upperBound);
...@@ -87,6 +146,10 @@ inline dword_t findPushAddress(dword_t value, dword_t lowerBound, dword_t upperB ...@@ -87,6 +146,10 @@ inline dword_t findPushAddress(dword_t value, dword_t lowerBound, dword_t upperB
87 * @exception illegal memory access 146 * @exception illegal memory access
88 */ 147 */
89 dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumFunctionSize); 148 dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumFunctionSize);
149 +dword_t findEnclosingFunctionBeforeDword(dword_t sig, dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
150 +dword_t findEnclosingFunctionAfterDword(dword_t sig, dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
151 +dword_t findEnclosingFunctionAfterInt3(dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
152 +dword_t findEnclosingFunctionAfterNop(dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
90 153
91 /** 154 /**
92 * Return the address of the first matched pattern. 155 * Return the address of the first matched pattern.
...@@ -102,6 +165,7 @@ dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumF ...@@ -102,6 +165,7 @@ dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumF
102 * @exception illegal memory access 165 * @exception illegal memory access
103 */ 166 */
104 dword_t findBytes(const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound); 167 dword_t findBytes(const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
168 +//dword_t reverseFindBytes(const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
105 169
106 /** 170 /**
107 * jichi 2/5/2014: The same as findBytes except it uses widecard to match everything. 171 * jichi 2/5/2014: The same as findBytes except it uses widecard to match everything.
......
1 +# mono.pri
2 +# 9/26/2012 jichi
3 +
4 +DEFINES += WITH_LIB_MONO
5 +
6 +DEPENDPATH += $$PWD
7 +
8 +HEADERS += \
9 + $$PWD/monoobject.h \
10 + $$PWD/monotype.h
11 +
12 +# EOF
1 +#ifndef MONOOBJECT_H
2 +#define MONOOBJECT_H
3 +
4 +// monoobject.h
5 +// 12/26/2014 jichi
6 +// https://github.com/mono/mono/blob/master/mono/metadata/object.h
7 +// https://github.com/mono/mono/blob/master/mono/metadata/object-internals.h
8 +// https://github.com/mono/mono/blob/master/mono/util/mono-publib.h
9 +
10 +#include <cstdint>
11 +
12 +#define MONO_ZERO_LEN_ARRAY 1
13 +
14 +// mono/io-layer/uglify.h
15 +//typedef int8_t gint8;
16 +//typedef int32_t gint32;
17 +//typedef wchar_t gunichar2; // either char or wchar_t, depending on how mono is compiled
18 +
19 +typedef int32_t mono_bool;
20 +typedef uint8_t mono_byte;
21 +typedef uint16_t mono_unichar2;
22 +typedef uint32_t mono_unichar4;
23 +
24 +// mono/metadata/object.h
25 +
26 +typedef mono_bool MonoBoolean;
27 +
28 +struct MonoArray;
29 +struct MonoDelegate;
30 +struct MonoDomain;
31 +struct MonoException;
32 +struct MonoString;
33 +struct MonoThreadsSync;
34 +struct MonoThread;
35 +struct MonoVTable;
36 +
37 +struct MonoObject {
38 + MonoVTable *vtable;
39 + MonoThreadsSync *synchronisation;
40 +};
41 +
42 +struct MonoString {
43 + MonoObject object;
44 + int32_t length;
45 + mono_unichar2 chars[MONO_ZERO_LEN_ARRAY];
46 +};
47 +
48 +#endif // MONOOBJECT_H
1 +#ifndef MONOTYPE_H
2 +#define MONOTYPE_H
3 +
4 +// monotype.h
5 +// 12/26/2014 jichi
6 +// https://github.com/mono/mono/blob/master/mono/metadata/object.h
7 +
8 +#include "mono/monoobject.h"
9 +
10 +// Function typedefs
11 +typedef MonoDomain *(* mono_object_get_domain_fun_t)(MonoObject *obj);
12 +
13 +typedef MonoString *(* mono_string_new_utf16_fun_t)(MonoDomain *domain, const mono_unichar2 *text, int32_t len);
14 +
15 +typedef char * (* mono_string_to_utf8_fun_t)(MonoString *string_obj);
16 +
17 +#endif // MONOTYPE_H
...@@ -3,11 +3,15 @@ ...@@ -3,11 +3,15 @@
3 #include "ntdll/ntdll.h" 3 #include "ntdll/ntdll.h"
4 #include "ntinspect/ntinspect.h" 4 #include "ntinspect/ntinspect.h"
5 5
6 +// https://social.msdn.microsoft.com/Forums/vstudio/en-US/4cb11cd3-8ce0-49d7-9dda-d62e9ae0180b/how-to-get-current-module-handle?forum=vcgeneral
7 +EXTERN_C IMAGE_DOS_HEADER __ImageBase;
8 +
6 //#ifdef _MSC_VER 9 //#ifdef _MSC_VER
7 //# pragma warning(disable:4018) // C4018: signed/unsigned mismatch 10 //# pragma warning(disable:4018) // C4018: signed/unsigned mismatch
8 //#endif // _MSC_VER 11 //#endif // _MSC_VER
9 12
10 namespace { // unnamed 13 namespace { // unnamed
14 +
11 // Replacement of wcscpy_s which is not available on Windows XP's msvcrt 15 // Replacement of wcscpy_s which is not available on Windows XP's msvcrt
12 // http://sakuradite.com/topic/247 16 // http://sakuradite.com/topic/247
13 errno_t wcscpy_safe(wchar_t *buffer, size_t bufferSize, const wchar_t *source) 17 errno_t wcscpy_safe(wchar_t *buffer, size_t bufferSize, const wchar_t *source)
...@@ -22,7 +26,12 @@ errno_t wcscpy_safe(wchar_t *buffer, size_t bufferSize, const wchar_t *source) ...@@ -22,7 +26,12 @@ errno_t wcscpy_safe(wchar_t *buffer, size_t bufferSize, const wchar_t *source)
22 26
23 NTINSPECT_BEGIN_NAMESPACE 27 NTINSPECT_BEGIN_NAMESPACE
24 28
25 -BOOL getCurrentProcessName(LPWSTR buffer, int bufferSize) 29 +// https://social.msdn.microsoft.com/Forums/vstudio/en-US/4cb11cd3-8ce0-49d7-9dda-d62e9ae0180b/how-to-get-current-module-handle?forum=vcgeneral
30 +HMODULE getCurrentModuleHandle() { return (HMODULE)&__ImageBase; }
31 +
32 +/** Memory range */
33 +
34 +BOOL getProcessName(LPWSTR buffer, int bufferSize)
26 { 35 {
27 //assert(name); 36 //assert(name);
28 PLDR_DATA_TABLE_ENTRY it; 37 PLDR_DATA_TABLE_ENTRY it;
...@@ -38,6 +47,7 @@ BOOL getCurrentProcessName(LPWSTR buffer, int bufferSize) ...@@ -38,6 +47,7 @@ BOOL getCurrentProcessName(LPWSTR buffer, int bufferSize)
38 return 0 == wcscpy_safe(buffer, bufferSize, it->BaseDllName.Buffer); 47 return 0 == wcscpy_safe(buffer, bufferSize, it->BaseDllName.Buffer);
39 } 48 }
40 49
50 +// See: ITH FillRange
41 BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBound) 51 BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBound)
42 { 52 {
43 //assert(lower); 53 //assert(lower);
...@@ -86,15 +96,114 @@ BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBou ...@@ -86,15 +96,114 @@ BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBou
86 return FALSE; 96 return FALSE;
87 } 97 }
88 98
89 -BOOL getCurrentMemoryRange(DWORD *lowerBound, DWORD *upperBound) 99 +BOOL getProcessMemoryRange(DWORD *lowerBound, DWORD *upperBound)
90 { 100 {
91 WCHAR procName[MAX_PATH]; // cached 101 WCHAR procName[MAX_PATH]; // cached
92 *lowerBound = 0; 102 *lowerBound = 0;
93 *upperBound = 0; 103 *upperBound = 0;
94 - return getCurrentProcessName(procName, MAX_PATH) 104 + return getProcessName(procName, MAX_PATH)
95 && getModuleMemoryRange(procName, lowerBound, upperBound); 105 && getModuleMemoryRange(procName, lowerBound, upperBound);
96 } 106 }
97 107
108 +/** Module header */
109 +
110 +// See: ITH AddAllModules
111 +bool iterModule(const iter_module_fun_t &fun)
112 +{
113 + // Iterate loaded modules
114 + PPEB ppeb;
115 + __asm {
116 + mov eax, fs:[0x30]
117 + mov ppeb, eax
118 + }
119 + const DWORD start = *(DWORD *)&ppeb->Ldr->InLoadOrderModuleList;
120 + for (auto it = (PLDR_DATA_TABLE_ENTRY)start;
121 + it->SizeOfImage && *(DWORD *)it != start;
122 + it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink)
123 + if (!fun((HMODULE)it->DllBase, it->BaseDllName.Buffer))
124 + return false;
125 + return true;
126 +}
127 +
128 +
129 +// See: ITH AddAllModules
130 +DWORD getExportFunction(LPCSTR funcName)
131 +{
132 + // Iterate loaded modules
133 + PPEB ppeb;
134 + __asm {
135 + mov eax, fs:[0x30]
136 + mov ppeb, eax
137 + }
138 + const DWORD start = *(DWORD *)&ppeb->Ldr->InLoadOrderModuleList;
139 + for (auto it = (PLDR_DATA_TABLE_ENTRY)start;
140 + it->SizeOfImage && *(DWORD *)it != start;
141 + it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink) {
142 + //if (moduleName && ::wcscmp(moduleName, it->BaseDllName.Buffer)) // BaseDllName.Buffer == moduleName
143 + // continue;
144 + if (DWORD addr = getModuleExportFunction((HMODULE)it->DllBase, funcName))
145 + return addr;
146 + }
147 + return 0;
148 +}
149 +
150 +// See: ITH AddModule
151 +DWORD getModuleExportFunction(HMODULE hModule, LPCSTR funcName)
152 +{
153 + if (!hModule)
154 + return 0;
155 + DWORD startAddress = (DWORD)hModule;
156 + IMAGE_DOS_HEADER *DosHdr = (IMAGE_DOS_HEADER *)hModule;
157 + if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
158 + DWORD dwReadAddr = startAddress + DosHdr->e_lfanew;
159 + IMAGE_NT_HEADERS *NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr;
160 + if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
161 + DWORD dwExportAddr = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
162 + if (dwExportAddr == 0)
163 + return 0;
164 + dwExportAddr += startAddress;
165 + IMAGE_EXPORT_DIRECTORY *ExtDir = (IMAGE_EXPORT_DIRECTORY *)dwExportAddr;
166 + dwExportAddr = startAddress + ExtDir->AddressOfNames;
167 + for (UINT uj = 0; uj < ExtDir->NumberOfNames; uj++) {
168 + DWORD dwFuncName = *(DWORD *)dwExportAddr;
169 + LPCSTR pcFuncName = (LPCSTR)(startAddress + dwFuncName);
170 + if (::strcmp(funcName, pcFuncName) == 0) {
171 + char *pcFuncPtr = (char *)(startAddress + (DWORD)ExtDir->AddressOfNameOrdinals+(uj * sizeof(WORD)));
172 + WORD word = *(WORD *)pcFuncPtr;
173 + pcFuncPtr = (char *)(startAddress + (DWORD)ExtDir->AddressOfFunctions+(word * sizeof(DWORD)));
174 + return startAddress + *(DWORD *)pcFuncPtr; // absolute address
175 + }
176 + dwExportAddr += sizeof(DWORD);
177 + }
178 + }
179 + }
180 + return 0;
181 +}
182 +
183 +// See: ITH FindImportEntry
184 +DWORD getModuleImportAddress(HMODULE hModule, DWORD exportAddress)
185 +{
186 + if (!hModule)
187 + return 0;
188 + DWORD startAddress = (DWORD)hModule;
189 + IMAGE_DOS_HEADER *DosHdr = (IMAGE_DOS_HEADER *)hModule;
190 + if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
191 + IMAGE_NT_HEADERS *NtHdr = (IMAGE_NT_HEADERS *)(startAddress + DosHdr->e_lfanew);
192 + if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
193 + DWORD IAT = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
194 + DWORD end = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
195 + IAT += startAddress;
196 + end += IAT;
197 + for (DWORD pt = IAT; pt < end; pt += 4) {
198 + DWORD addr = *(DWORD *)pt;
199 + if (addr == (DWORD)exportAddress)
200 + return pt;
201 + }
202 + }
203 + }
204 + return 0;
205 +}
206 +
98 NTINSPECT_END_NAMESPACE 207 NTINSPECT_END_NAMESPACE
99 208
100 // EOF 209 // EOF
......
...@@ -4,6 +4,9 @@ ...@@ -4,6 +4,9 @@
4 // 4/20/2014 jichi 4 // 4/20/2014 jichi
5 5
6 #include <windows.h> 6 #include <windows.h>
7 +#ifndef MEMDBG_NO_STL
8 +# include <functional>
9 +#endif // MEMDBG_NO_STL
7 10
8 #ifndef NTINSPECT_BEGIN_NAMESPACE 11 #ifndef NTINSPECT_BEGIN_NAMESPACE
9 # define NTINSPECT_BEGIN_NAMESPACE namespace NtInspect { 12 # define NTINSPECT_BEGIN_NAMESPACE namespace NtInspect {
...@@ -14,17 +17,73 @@ ...@@ -14,17 +17,73 @@
14 17
15 NTINSPECT_BEGIN_NAMESPACE 18 NTINSPECT_BEGIN_NAMESPACE
16 19
20 +// Get the module handle of the current module (not the current process that is GetModuleHandleA(0))
21 +HMODULE getCurrentModuleHandle();
22 +
17 /// Get current module name in fs:0x30 23 /// Get current module name in fs:0x30
18 -BOOL getCurrentProcessName(_Out_ LPWSTR buffer, _In_ int bufferSize); 24 +BOOL getProcessName(_Out_ LPWSTR buffer, _In_ int bufferSize);
19 25
20 /** 26 /**
21 * Get the memory range of the module if succeed 27 * Get the memory range of the module if succeed
22 - * See: ITH FillRange 28 + * @param moduleName
29 + * @param[out[ lowerBound
30 + * @param[out] upperBound
31 + * @return if succeed
23 */ 32 */
24 BOOL getModuleMemoryRange(_In_ LPCWSTR moduleName, _Out_ DWORD *lowerBound, _Out_ DWORD *upperBound); 33 BOOL getModuleMemoryRange(_In_ LPCWSTR moduleName, _Out_ DWORD *lowerBound, _Out_ DWORD *upperBound);
25 34
26 -/// Get memory of the current process 35 +/// Get memory of the current process module
27 -BOOL getCurrentMemoryRange(_Out_ DWORD *lowerBound, _Out_ DWORD *upperBound); 36 +BOOL getProcessMemoryRange(_Out_ DWORD *lowerBound, _Out_ DWORD *upperBound);
37 +
38 +#ifndef NTINSPECT_NO_STL
39 +/// Iterate module information and return false if abort iteration.
40 +typedef std::function<bool (HMODULE hModule, LPCWSTR moduleName)> iter_module_fun_t;
41 +#else
42 +typedef bool (* iter_module_fun_t)(HMODULE hModule, LPCWSTR moduleName);
43 +#endif // NTINSPECT_NO_STL
44 +
45 +/**
46 + * Iterate all modules
47 + * @param fun the first parameter is the address of the caller, and the second parameter is the address of the call itself
48 + * @return false if return early, and true if iterate all elements
49 + */
50 +bool iterModule(const iter_module_fun_t &fun);
51 +
52 +/**
53 + * Return the absolute address of the function imported from the given module
54 + * @param functionName
55 + * @param* hModule find from any module when null
56 + * @return function address or 0
57 + */
58 +DWORD getModuleExportFunction(HMODULE hModule, LPCSTR functionName);
59 +
60 +inline DWORD getModuleExportFunctionA(LPCSTR moduleName, LPCSTR functionName)
61 +{ return getModuleExportFunction(::GetModuleHandleA(moduleName), functionName); }
62 +
63 +inline DWORD getModuleExportFunctionW(LPCWSTR moduleName, LPCSTR functionName)
64 +{ return getModuleExportFunction(::GetModuleHandleW(moduleName), functionName); }
65 +
66 +/// Get the function address exported from any module
67 +DWORD getExportFunction(LPCSTR functionName);
68 +
69 +/**
70 + * Get the import address in the specified module
71 + * @param hModule
72 + * @param exportAddress absolute address of the function exported from other modules
73 + * @return function address or 0
74 + */
75 +DWORD getModuleImportAddress(HMODULE hModule, DWORD exportAddress);
76 +
77 +inline DWORD getModuleImportAddressA(LPCSTR moduleName, DWORD exportAddress)
78 +{ return getModuleImportAddress(::GetModuleHandleA(moduleName), exportAddress); }
79 +
80 +inline DWORD getModuleImportAddressW(LPCWSTR moduleName, DWORD exportAddress)
81 +{ return getModuleImportAddress(::GetModuleHandleW(moduleName), exportAddress); }
82 +
83 +/// Get the import address in the current executable
84 +inline DWORD getProcessImportAddress(DWORD exportAddress)
85 +{ return getModuleImportAddress(::GetModuleHandleA(nullptr), exportAddress); }
86 +
28 87
29 NTINSPECT_END_NAMESPACE 88 NTINSPECT_END_NAMESPACE
30 89
......
1 +set(profile_src
2 + Profile.h
3 + Profile.cpp
4 + pugiconfig.hpp
5 + pugixml.cpp
6 + pugixml.hpp
7 + misc.h
8 + misc.cpp
9 +)
10 +
11 +add_library(profile STATIC ${profile_src})
12 +
13 +target_compile_options(profile PRIVATE
14 + $<$<CONFIG:Release>:>
15 + $<$<CONFIG:Debug>:>
16 +)
17 +
18 +#target_link_libraries(profile comctl32.lib)
19 +
20 +#target_compile_definitions(profile
21 +# PRIVATE
22 +# _CRT_NON_CONFORMING_SWPRINTFS
23 +#)
1 +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com)
2 + * This file is part of the Interactive Text Hooker.
3 +
4 + * Interactive Text Hooker is free software: you can redistribute it and/or
5 + * modify it under the terms of the GNU General Public License as published
6 + * by the Free Software Foundation, either version 3 of the License, or
7 + * (at your option) any later version.
8 +
9 + * This program is distributed in the hope that it will be useful,
10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 + * GNU General Public License for more details.
13 +
14 + * You should have received a copy of the GNU General Public License
15 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 + */
17 +
18 +
19 +#include "host/host.h"
20 +#include "host/hookman.h"
21 +#include "vnrhook/include/types.h"
22 +#include "vnrhook/include/const.h"
23 +#include "Profile.h"
24 +#include "misc.h"
25 +#include <algorithm>
26 +#include "pugixml.hpp"
27 +
28 +extern HookManager* man;
29 +
30 +Profile::Profile(const std::wstring& title) :
31 +select_index(-1),
32 +title(title)
33 +{}
34 +
35 +const std::unordered_set<hook_ptr>& Profile::Hooks() const
36 +{
37 + return hooks;
38 +}
39 +
40 +const std::vector<thread_ptr>& Profile::Threads() const
41 +{
42 + return threads;
43 +}
44 +
45 +const std::unordered_set<link_ptr>& Profile::Links() const
46 +{
47 + return links;
48 +}
49 +
50 +bool Profile::XmlReadProfile(pugi::xml_node profile)
51 +{
52 + auto hooks_node = profile.child(L"Hooks");
53 + auto threads_node = profile.child(L"Threads");
54 + auto links_node = profile.child(L"Links");
55 + if (hooks_node && !XmlReadProfileHook(hooks_node))
56 + return false;
57 + if (threads_node && !XmlReadProfileThread(threads_node))
58 + return false;
59 + if (links_node && !XmlReadProfileLink(links_node))
60 + return false;
61 + auto select_node = profile.child(L"Select");
62 + if (select_node)
63 + {
64 + auto thread_index = select_node.attribute(L"ThreadIndex");
65 + if (!thread_index)
66 + return false;
67 + DWORD tmp_select = std::stoul(thread_index.value(), NULL, 16);
68 + select_index = tmp_select & 0xFFFF;
69 + }
70 + return true;
71 +}
72 +
73 +bool Profile::XmlReadProfileHook(pugi::xml_node hooks_node)
74 +{
75 + for (auto hook = hooks_node.begin(); hook != hooks_node.end(); ++hook)
76 + {
77 + std::wstring name = hook->name();
78 + if (name.empty() || name.compare(L"Hook") != 0)
79 + return false;
80 + auto type = hook->attribute(L"Type");
81 + if (!type || type.empty())
82 + return false;
83 + auto code = hook->attribute(L"Code");
84 + if (!code)
85 + return false;
86 + std::wstring code_value = code.value();
87 + HookParam hp = {};
88 + switch (type.value()[0])
89 + {
90 + case L'H':
91 + if (code_value[0] != L'/')
92 + return false;
93 + if (code_value[1] != L'H' && code_value[1] != L'h')
94 + return false;
95 + if (Parse(code_value.substr(2), hp))
96 + {
97 + auto name = hook->attribute(L"Name");
98 + if (!name || name.empty())
99 + AddHook(hook_ptr(new HookProfile(hp, L"")));
100 + else
101 + AddHook(hook_ptr(new HookProfile(hp, name.value())));
102 + }
103 + break;
104 + default:
105 + return false;
106 + }
107 + }
108 + return true;
109 +}
110 +
111 +bool Profile::XmlReadProfileThread(pugi::xml_node threads_node)
112 +{
113 + std::wstring hook_name_buffer;
114 + for (auto thread = threads_node.begin(); thread != threads_node.end(); ++thread)
115 + {
116 + std::wstring name = thread->name();
117 + if (name.empty() || name.compare(L"Thread") != 0)
118 + return false;
119 + auto hook_name = thread->attribute(L"HookName");
120 + if (!hook_name)
121 + return false;
122 + auto context = thread->attribute(L"Context");
123 + if (!context)
124 + return false;
125 + auto sub_context = thread->attribute(L"SubContext");
126 + if (!sub_context)
127 + return false;
128 + auto mask = thread->attribute(L"Mask");
129 + if (!mask)
130 + return false;
131 + DWORD mask_tmp = std::stoul(mask.value(), NULL, 16);
132 + auto comment = thread->attribute(L"Comment");
133 + auto retn = std::stoul(context.value(), NULL, 16);
134 + auto split = std::stoul(sub_context.value(), NULL, 16);
135 + WORD flags = mask_tmp & 0xFFFF;
136 + auto tp = new ThreadProfile(hook_name.value(), retn, split, 0, 0, flags,
137 + comment.value());
138 + AddThread(thread_ptr(tp));
139 + }
140 + return true;
141 +}
142 +
143 +bool Profile::XmlReadProfileLink(pugi::xml_node links_node)
144 +{
145 + for (auto link = links_node.begin(); link != links_node.end(); ++link)
146 + {
147 + std::wstring name = link->name();
148 + if (name.empty() || name.compare(L"Link") != 0)
149 + return false;
150 + auto from = link->attribute(L"From");
151 + if (!from)
152 + return false;
153 + DWORD link_from = std::stoul(from.value(), NULL, 16);
154 + auto to = link->attribute(L"To");
155 + if (!to)
156 + return false;
157 + DWORD link_to = std::stoul(to.value(), NULL, 16);
158 + auto lp = new LinkProfile(link_from & 0xFFFF, link_to & 0xFFFF);
159 + AddLink(link_ptr(lp));
160 + }
161 + return true;
162 +}
163 +
164 +bool Profile::XmlWriteProfile(pugi::xml_node profile_node)
165 +{
166 + if (!hooks.empty())
167 + {
168 + auto node = profile_node.append_child(L"Hooks");
169 + XmlWriteProfileHook(node);
170 + }
171 + if (!threads.empty())
172 + {
173 + auto node = profile_node.append_child(L"Threads");
174 + XmlWriteProfileThread(node);
175 + }
176 + if (!links.empty())
177 + {
178 + auto node = profile_node.append_child(L"Links");
179 + XmlWriteProfileLink(node);
180 + }
181 + if (select_index != 0xFFFF)
182 + {
183 + auto node = profile_node.append_child(L"Select");
184 + node.append_attribute(L"ThreadIndex") = select_index;
185 + }
186 + return true;
187 +}
188 +
189 +bool Profile::XmlWriteProfileHook(pugi::xml_node hooks_node)
190 +{
191 + for (auto hook = hooks.begin(); hook != hooks.end(); ++hook)
192 + {
193 + auto hook_node = hooks_node.append_child(L"Hook");
194 + hook_node.append_attribute(L"Type") = L"H";
195 + hook_node.append_attribute(L"Code") = ParseCode((*hook)->HP()).c_str();
196 + if (!(*hook)->Name().empty())
197 + hook_node.append_attribute(L"Name") = (*hook)->Name().c_str();
198 + }
199 + return true;
200 +}
201 +
202 +bool Profile::XmlWriteProfileThread(pugi::xml_node threads_node)
203 +{
204 + for (auto thread = threads.begin(); thread != threads.end(); ++thread)
205 + {
206 + const std::wstring& name = (*thread)->HookName();
207 + if (name.empty())
208 + return false;
209 + auto node = threads_node.append_child(L"Thread");
210 + node.append_attribute(L"HookName") = name.c_str();
211 + std::wstring hex = ToHexString((*thread)->Flags() & (THREAD_MASK_RETN | THREAD_MASK_SPLIT));
212 + node.append_attribute(L"Mask") = hex.c_str();
213 + hex = ToHexString((*thread)->Split());
214 + node.append_attribute(L"SubContext") = hex.c_str();
215 + hex = ToHexString((*thread)->Return());
216 + node.append_attribute(L"Context") = hex.c_str();
217 + if (!(*thread)->Comment().empty())
218 + node.append_attribute(L"Comment") = (*thread)->Comment().c_str();
219 + }
220 + return true;
221 +}
222 +
223 +bool Profile::XmlWriteProfileLink(pugi::xml_node links_node)
224 +{
225 + for (auto link = links.begin(); link != links.end(); ++link)
226 + {
227 + auto node = links_node.append_child(L"Link");
228 + node.append_attribute(L"From") = ToHexString((*link)->FromIndex()).c_str();
229 + node.append_attribute(L"To") = ToHexString((*link)->ToIndex()).c_str();
230 + }
231 + return true;
232 +}
233 +
234 +void Profile::Clear()
235 +{
236 + title = L"";
237 + select_index = -1;
238 + hooks.clear();
239 + threads.clear();
240 + links.clear();
241 +}
242 +
243 +void Profile::AddHook(hook_ptr hook)
244 +{
245 + hooks.insert(std::move(hook));
246 +}
247 +
248 +// add the thread profile and return its index
249 +int Profile::AddThread(thread_ptr tp)
250 +{
251 + auto it = std::find_if(threads.begin(), threads.end(), [&tp](thread_ptr& thread)
252 + {
253 + return thread->HookName().compare(tp->HookName()) == 0 &&
254 + thread->Return() == tp->Return() &&
255 + thread->Split() == tp->Split();
256 + });
257 + if (it != threads.end())
258 + return it - threads.begin();
259 + threads.push_back(std::move(tp));
260 + return threads.size() - 1;
261 +}
262 +
263 +void Profile::AddLink(link_ptr link)
264 +{
265 + links.insert(std::move(link));
266 +}
267 +
268 +const std::wstring& Profile::Title() const
269 +{
270 + return title;
271 +}
272 +
273 +bool Profile::IsThreadSelected(thread_ptr_iter thread_profile)
274 +{
275 + if (thread_profile != threads.end())
276 + {
277 + auto thread_index = thread_profile - threads.begin();
278 + return select_index == thread_index;
279 + }
280 + return false;
281 +}
282 +
283 +thread_ptr_iter Profile::FindThread(const ThreadParameter* tp, const std::wstring& hook_name) const
284 +{
285 + auto thread_profile = std::find_if(threads.begin(), threads.end(),
286 + [&tp, &hook_name](const thread_ptr& thread_profile) -> bool
287 + {
288 + return thread_profile->HookName().compare(hook_name) == 0
289 + && thread_profile->Return() == tp->retn
290 + && thread_profile->Split() == tp->spl;
291 + });
292 + return thread_profile;
293 +}
1 +/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com)
2 + * This file is part of the Interactive Text Hooker.
3 +
4 + * Interactive Text Hooker is free software: you can redistribute it and/or
5 + * modify it under the terms of the GNU General Public License as published
6 + * by the Free Software Foundation, either version 3 of the License, or
7 + * (at your option) any later version.
8 +
9 + * This program is distributed in the hope that it will be useful,
10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 + * GNU General Public License for more details.
13 +
14 + * You should have received a copy of the GNU General Public License
15 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 + */
17 +
18 +#pragma once
19 +
20 +#include "vnrhook/include/types.h" // HookParam
21 +#include <string>
22 +#include <memory>
23 +#include <vector>
24 +#include <unordered_set>
25 +
26 +struct ThreadParameter;
27 +class TextThread;
28 +class HookProfile;
29 +class ThreadProfile;
30 +class LinkProfile;
31 +typedef std::unique_ptr<HookProfile> hook_ptr;
32 +typedef std::unique_ptr<ThreadProfile> thread_ptr;
33 +typedef std::unique_ptr<LinkProfile> link_ptr;
34 +typedef std::vector<thread_ptr>::const_iterator thread_ptr_iter;
35 +namespace pugi {
36 + class xml_node;
37 +}
38 +
39 +#define THREAD_MASK_RETN 1
40 +#define THREAD_MASK_SPLIT 2
41 +
42 +class HookProfile
43 +{
44 + HookParam hp;
45 + std::wstring name;
46 +public:
47 + HookProfile(const HookParam& hp, const std::wstring& name) :
48 + hp(hp),
49 + name(name)
50 + {}
51 + const HookParam& HP() const { return hp; };
52 + const std::wstring& Name() const { return name; };
53 +};
54 +
55 +class ThreadProfile
56 +{
57 + std::wstring hook_name;
58 + DWORD retn;
59 + DWORD split;
60 + DWORD hook_addr;
61 + WORD hm_index, flags;
62 + std::wstring comment;
63 +public:
64 + ThreadProfile(const std::wstring& hook_name,
65 + DWORD retn,
66 + DWORD split,
67 + DWORD hook_addr,
68 + WORD hm_index,
69 + WORD flags,
70 + const std::wstring& comment) :
71 + hook_name(hook_name),
72 + retn(retn),
73 + split(split),
74 + hook_addr(hook_addr),
75 + hm_index(hm_index),
76 + flags(flags),
77 + comment(comment)
78 + {
79 + }
80 + const std::wstring& HookName() const { return hook_name; }
81 + const std::wstring& Comment() const { return comment; }
82 + DWORD Return() const { return retn; }
83 + DWORD Split() const { return split; }
84 + DWORD& HookAddress() { return hook_addr; }
85 + WORD& HookManagerIndex() { return hm_index; }
86 + WORD Flags() const { return flags; }
87 +};
88 +
89 +class LinkProfile
90 +{
91 + WORD from_index, to_index;
92 +public:
93 + LinkProfile(WORD from_index, WORD to_index) :
94 + from_index(from_index),
95 + to_index(to_index)
96 + {}
97 + WORD FromIndex() const { return from_index; }
98 + WORD ToIndex() const { return to_index; }
99 +};
100 +
101 +namespace std {
102 + template<>
103 + struct hash<hook_ptr> {
104 + size_t operator()(const hook_ptr &r) const
105 + {
106 + return hash<DWORD>{}(r->HP().address)
107 + ^ hash<DWORD>{}(r->HP().module)
108 + ^ hash<DWORD>{}(r->HP().function);
109 + }
110 + };
111 + template<>
112 + struct equal_to<hook_ptr> {
113 + bool operator()(const hook_ptr& r, const hook_ptr& r2) const
114 + {
115 + return r->HP().address == r2->HP().address
116 + && r->HP().module == r2->HP().module
117 + && r->HP().function == r2->HP().function;
118 + }
119 + };
120 +
121 + template<>
122 + struct hash<link_ptr> {
123 + size_t operator()(const link_ptr &r) const
124 + {
125 + return hash<WORD>{}(r->FromIndex())
126 + ^ hash<WORD>{}(r->ToIndex());
127 + }
128 + };
129 + template<>
130 + struct equal_to<link_ptr> {
131 + bool operator()(const link_ptr& r, const link_ptr& r2) const
132 + {
133 + return r->FromIndex() == r2->FromIndex()
134 + && r->ToIndex() == r2->ToIndex();
135 + }
136 + };
137 +}
138 +
139 +class Profile
140 +{
141 +public:
142 + Profile(const std::wstring& title);
143 + bool XmlReadProfile(pugi::xml_node profile_node);
144 + bool XmlWriteProfile(pugi::xml_node profile_node);
145 + void AddHook(hook_ptr hook);
146 + int AddThread(thread_ptr tp);
147 + void AddLink(link_ptr lp);
148 + void Clear();
149 + const std::unordered_set<hook_ptr>& Hooks() const;
150 + const std::vector<thread_ptr>& Threads() const;
151 + const std::unordered_set<link_ptr>& Links() const;
152 + const std::wstring& Title() const;
153 + thread_ptr_iter FindThread(const ThreadParameter* tp, const std::wstring& hook_name) const;
154 + WORD& SelectedIndex() { return select_index; }
155 + bool IsThreadSelected(thread_ptr_iter thread_profile);
156 +
157 +private:
158 + bool XmlReadProfileHook(pugi::xml_node hooks_node);
159 + bool XmlReadProfileThread(pugi::xml_node threads_node);
160 + bool XmlReadProfileLink(pugi::xml_node links_node);
161 + bool XmlWriteProfileHook(pugi::xml_node hooks_node);
162 + bool XmlWriteProfileThread(pugi::xml_node threads_node);
163 + bool XmlWriteProfileLink(pugi::xml_node links_node);
164 +
165 + std::wstring title;
166 + std::unordered_set<hook_ptr> hooks;
167 + std::vector<thread_ptr> threads;
168 + std::unordered_set<link_ptr> links;
169 +
170 + WORD select_index;
171 +};
1 +#include "misc.h"
2 +#include <regex>
3 +#include <memory>
4 +#include "host/host.h"
5 +#include "vnrhook/include/const.h"
6 +#include "vnrhook/include/types.h"
7 +
8 +DWORD Hash(const std::wstring& module, int length)
9 +{
10 + DWORD hash = 0;
11 + auto end = (length < 0 || static_cast<size_t>(length) > module.length()) ?
12 + module.end() :
13 + module.begin() + length;
14 + for (auto it = module.begin(); it != end; ++it)
15 + hash = _rotr(hash, 7) + *it;
16 + return hash;
17 +}
18 +
19 +bool Parse(const std::wstring& cmd, HookParam& hp)
20 +{
21 + using std::wregex;
22 + using std::regex_search;
23 + // /H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]
24 + wregex rx(L"^X?([ABWSQ])(N)?", wregex::icase);
25 + std::match_results<std::wstring::const_iterator> m;
26 + auto start = cmd.begin();
27 + auto end = cmd.end();
28 + bool result = regex_search(start, end, m, rx);
29 + if (!result)
30 + return result;
31 + start = m[0].second;
32 + if (m[2].matched)
33 + hp.type |= NO_CONTEXT;
34 +
35 + switch (m[1].first[0])
36 + {
37 + case L's':
38 + case L'S':
39 + hp.type |= USING_STRING;
40 + break;
41 + case L'e':
42 + case L'E':
43 + hp.type |= STRING_LAST_CHAR;
44 + case L'a':
45 + case L'A':
46 + hp.type |= BIG_ENDIAN;
47 + hp.length_offset = 1;
48 + break;
49 + case L'b':
50 + case L'B':
51 + hp.length_offset = 1;
52 + break;
53 + case L'h':
54 + case L'H':
55 + hp.type |= PRINT_DWORD;
56 + case L'q':
57 + case L'Q':
58 + hp.type |= USING_STRING | USING_UNICODE;
59 + break;
60 + case L'l':
61 + case L'L':
62 + hp.type |= STRING_LAST_CHAR;
63 + case L'w':
64 + case L'W':
65 + hp.type |= USING_UNICODE;
66 + hp.length_offset = 1;
67 + break;
68 + default:
69 + break;
70 + }
71 +
72 + // [data_offset[*drdo]]
73 + std::wstring data_offset(L"(-?[[:xdigit:]]+)"), drdo(L"(\\*-?[[:xdigit:]]+)?");
74 + rx = wregex(L"^" + data_offset + drdo, wregex::icase);
75 + result = regex_search(start, end, m, rx);
76 + if (result)
77 + {
78 + start = m[0].second;
79 + hp.offset = std::stoul(m[1].str(), NULL, 16);
80 + if (m[2].matched)
81 + {
82 + hp.type |= DATA_INDIRECT;
83 + hp.index = std::stoul(m[2].str().substr(1), NULL, 16);
84 + }
85 + }
86 +
87 + // [:sub_offset[*drso]]
88 + std::wstring sub_offset(L"(-?[[:xdigit:]]+)"), drso(L"(\\*-?[[:xdigit:]]+)?");
89 + rx = wregex(L"^:" + sub_offset + drso, wregex::icase);
90 + result = regex_search(start, end, m, rx);
91 + if (result)
92 + {
93 + start = m[0].second;
94 + hp.type |= USING_SPLIT;
95 + hp.split = std::stoul(m[1].str(), NULL, 16);
96 + if (m[2].matched)
97 + {
98 + hp.type |= SPLIT_INDIRECT;
99 + hp.split_index = std::stoul(m[2].str().substr(1), NULL, 16);
100 + }
101 + }
102 + // @addr
103 + rx = wregex(L"^@[[:xdigit:]]+", wregex::icase);
104 + result = regex_search(start, end, m, rx);
105 + if (!result)
106 + return false;
107 + start = m[0].second;
108 + hp.address = std::stoul(m[0].str().substr(1), NULL, 16);
109 + if (hp.offset & 0x80000000)
110 + hp.offset -= 4;
111 + if (hp.split & 0x80000000)
112 + hp.split -= 4;
113 +
114 + // [:[module[:{name|#ordinal}]]]
115 + // ":" -> module == NULL &% function == NULL
116 + // "" -> MODULE_OFFSET && module == NULL && function == addr
117 + // ":GDI.dll" -> MODULE_OFFSET && module != NULL
118 + // ":GDI.dll:strlen" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
119 + // ":GDI.dll:#123" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
120 + std::wstring module(L"([[:graph:]]+)"), name(L"[[:graph:]]+"), ordinal(L"\\d+");
121 + rx = wregex(L"^:(" + module + L"(:" + name + L"|#" + ordinal + L")?)?$", wregex::icase);
122 + result = regex_search(start, end, m, rx);
123 + if (result) // :[module[:{name|#ordinal}]]
124 + {
125 + if (m[1].matched) // module
126 + {
127 + hp.type |= MODULE_OFFSET;
128 + std::wstring module = m[2];
129 + std::transform(module.begin(), module.end(), module.begin(), ::towlower);
130 + hp.module = Hash(module);
131 + if (m[3].matched) // :name|#ordinal
132 + {
133 + hp.type |= FUNCTION_OFFSET;
134 + hp.function = Hash(m[3].str().substr(1));
135 + }
136 + }
137 + }
138 + else
139 + {
140 + rx = wregex(L"^!([[:xdigit:]]+)(!([[:xdigit:]]+))?$", wregex::icase);
141 + result = regex_search(start, end, m, rx);
142 + if (result)
143 + {
144 + hp.type |= MODULE_OFFSET;
145 + hp.module = std::stoul(m[1].str(), NULL, 16);
146 + if (m[2].matched)
147 + {
148 + hp.type |= FUNCTION_OFFSET;
149 + hp.function = std::stoul(m[2].str().substr(1), NULL, 16);
150 + }
151 + }
152 + else
153 + {
154 + // Hack. Hook is relative to the executable. Store the original address in function.
155 + // hp.module == NULL && hp.function != NULL
156 + hp.type |= MODULE_OFFSET;
157 + hp.function = hp.address;
158 + }
159 + }
160 + return true;
161 +}
162 +
163 +std::wstring ParseCode(const HookParam& hp)
164 +{
165 + std::wstring code(L"/H");
166 + WCHAR c;
167 + if (hp.type & PRINT_DWORD)
168 + c = L'H';
169 + else if (hp.type & USING_UNICODE)
170 + {
171 + if (hp.type & USING_STRING)
172 + c = L'Q';
173 + else if (hp.type & STRING_LAST_CHAR)
174 + c = L'L';
175 + else
176 + c = L'W';
177 + }
178 + else
179 + {
180 + if (hp.type & USING_STRING)
181 + c = L'S';
182 + else if (hp.type & BIG_ENDIAN)
183 + c = L'A';
184 + else if (hp.type & STRING_LAST_CHAR)
185 + c = L'E';
186 + else
187 + c = L'B';
188 + }
189 + code += c;
190 + if (hp.type & NO_CONTEXT)
191 + code += L'N';
192 + if (hp.offset >> 31)
193 + code += L'-' + ToHexString(-(hp.offset + 4));
194 + else
195 + code += ToHexString(hp.offset);
196 + if (hp.type & DATA_INDIRECT)
197 + {
198 + if (hp.index >> 31)
199 + code += L'*-' + ToHexString(-hp.index);
200 + else
201 + code += L'*' + ToHexString(hp.index);
202 + }
203 + if (hp.type & USING_SPLIT)
204 + {
205 + if (hp.split >> 31)
206 + code += L":-" + ToHexString(-(4 + hp.split));
207 + else
208 + code += L":" + ToHexString(hp.split);
209 + }
210 + if (hp.type & SPLIT_INDIRECT)
211 + {
212 + if (hp.split_index >> 31)
213 + code += L"*-" + ToHexString(-hp.split_index);
214 + else
215 + code += L"*" + ToHexString(hp.split_index);
216 + }
217 + if (hp.module)
218 + {
219 + code += L"@" + ToHexString(hp.address) + L"!" + ToHexString(hp.module);
220 + if (hp.function)
221 + code += L"!" + ToHexString(hp.function);
222 + }
223 + else
224 + {
225 + // Hack. The original address is stored in the function field
226 + // if (module == NULL && function != NULL).
227 + // MODULE_OFFSET and FUNCTION_OFFSET are removed from HookParam.type in
228 + // TextHook::UnsafeInsertHookCode() and can not be used here.
229 + if (hp.function)
230 + code += L"@" + ToHexString(hp.function);
231 + else
232 + code += L"@" + ToHexString(hp.address) + L":";
233 + }
234 + return code;
235 +}
236 +
237 +
238 +std::string toMultiByteString(const std::wstring& unicodeString)
239 +{
240 + int cbMultiByte = WideCharToMultiByte(932, 0, unicodeString.c_str(), unicodeString.length(),
241 + NULL, 0, NULL, NULL);
242 + auto lpMultiByteStr = std::make_unique<CHAR[]>(cbMultiByte);
243 + WideCharToMultiByte(932, 0, unicodeString.c_str(), unicodeString.length(),
244 + lpMultiByteStr.get(), cbMultiByte, NULL, NULL);
245 + return std::string(lpMultiByteStr.get(), cbMultiByte);
246 +}
247 +
248 +std::wstring toUnicodeString(const std::string& mbString)
249 +{
250 + int cchWideChar = MultiByteToWideChar(932, 0, mbString.c_str(), mbString.length(), NULL, 0);
251 + auto lpWideCharStr = std::make_unique<WCHAR[]>(cchWideChar);
252 + MultiByteToWideChar(932, 0, mbString.c_str(), mbString.length(), lpWideCharStr.get(), cchWideChar);
253 + return std::wstring(lpWideCharStr.get(), cchWideChar);
254 +}
255 +
256 +std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address)
257 +{
258 + std::wstring hook_name;
259 + WaitForSingleObject(pr.hookman_mutex, 0);
260 + auto hooks = (const Hook*)pr.hookman_map;
261 + for (int i = 0; i < MAX_HOOK; ++i)
262 + {
263 + auto& hook = hooks[i];
264 + if (hook.Address() == hook_address)
265 + {
266 + std::unique_ptr<CHAR[]> name(new CHAR[hook.NameLength()]);
267 + // name is zero terminated
268 + if (ReadProcessMemory(pr.process_handle, hooks[i].Name(), name.get(), hook.NameLength(), NULL))
269 + hook_name = toUnicodeString(name.get());
270 + break;
271 + }
272 + }
273 + ReleaseMutex(pr.hookman_mutex);
274 + return hook_name;
275 +}
1 +#pragma once
2 +
3 +#include <Windows.h>
4 +#include <string>
5 +#include <sstream>
6 +
7 +struct HookParam;
8 +struct ProcessRecord;
9 +
10 +bool Parse(const std::wstring& cmd, HookParam& hp);
11 +DWORD Hash(const std::wstring& module, int length = -1);
12 +std::wstring ParseCode(const HookParam& hp);
13 +std::string toMultiByteString(const std::wstring& unicodeString);
14 +std::wstring toUnicodeString(const std::string& mbString);
15 +std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address);
16 +
17 +template <typename T>
18 +std::wstring ToHexString(T i) {
19 + std::wstringstream ss;
20 + ss << std::uppercase << std::hex << i;
21 + return ss.str();
22 +}
1 +/**
2 + * pugixml parser - version 1.6
3 + * --------------------------------------------------------
4 + * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
5 + * Report bugs and download new versions at http://pugixml.org/
6 + *
7 + * This library is distributed under the MIT License. See notice at the end
8 + * of this file.
9 + *
10 + * This work is based on the pugxml parser, which is:
11 + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
12 + */
13 +
14 +#ifndef HEADER_PUGICONFIG_HPP
15 +#define HEADER_PUGICONFIG_HPP
16 +
17 +// Uncomment this to enable wchar_t mode
18 +#define PUGIXML_WCHAR_MODE
19 +
20 +// Uncomment this to disable XPath
21 +// #define PUGIXML_NO_XPATH
22 +
23 +// Uncomment this to disable STL
24 +// #define PUGIXML_NO_STL
25 +
26 +// Uncomment this to disable exceptions
27 +// #define PUGIXML_NO_EXCEPTIONS
28 +
29 +// Set this to control attributes for public classes/functions, i.e.:
30 +// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
31 +// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
32 +// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
33 +// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
34 +
35 +// Tune these constants to adjust memory-related behavior
36 +// #define PUGIXML_MEMORY_PAGE_SIZE 32768
37 +// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
38 +// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
39 +
40 +// Uncomment this to switch to header-only version
41 +// #define PUGIXML_HEADER_ONLY
42 +
43 +// Uncomment this to enable long long support
44 +// #define PUGIXML_HAS_LONG_LONG
45 +
46 +#endif
47 +
48 +/**
49 + * Copyright (c) 2006-2015 Arseny Kapoulkine
50 + *
51 + * Permission is hereby granted, free of charge, to any person
52 + * obtaining a copy of this software and associated documentation
53 + * files (the "Software"), to deal in the Software without
54 + * restriction, including without limitation the rights to use,
55 + * copy, modify, merge, publish, distribute, sublicense, and/or sell
56 + * copies of the Software, and to permit persons to whom the
57 + * Software is furnished to do so, subject to the following
58 + * conditions:
59 + *
60 + * The above copyright notice and this permission notice shall be
61 + * included in all copies or substantial portions of the Software.
62 + *
63 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
64 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
65 + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
66 + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
67 + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
68 + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
69 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
70 + * OTHER DEALINGS IN THE SOFTWARE.
71 + */
This diff could not be displayed because it is too large.
1 +/**
2 + * pugixml parser - version 1.6
3 + * --------------------------------------------------------
4 + * Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
5 + * Report bugs and download new versions at http://pugixml.org/
6 + *
7 + * This library is distributed under the MIT License. See notice at the end
8 + * of this file.
9 + *
10 + * This work is based on the pugxml parser, which is:
11 + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
12 + */
13 +
14 +#ifndef PUGIXML_VERSION
15 +// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons
16 +# define PUGIXML_VERSION 160
17 +#endif
18 +
19 +// Include user configuration file (this can define various configuration macros)
20 +#include "pugiconfig.hpp"
21 +
22 +#ifndef HEADER_PUGIXML_HPP
23 +#define HEADER_PUGIXML_HPP
24 +
25 +// Include stddef.h for size_t and ptrdiff_t
26 +#include <stddef.h>
27 +
28 +// Include exception header for XPath
29 +#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS)
30 +# include <exception>
31 +#endif
32 +
33 +// Include STL headers
34 +#ifndef PUGIXML_NO_STL
35 +# include <iterator>
36 +# include <iosfwd>
37 +# include <string>
38 +#endif
39 +
40 +// Macro for deprecated features
41 +#ifndef PUGIXML_DEPRECATED
42 +# if defined(__GNUC__)
43 +# define PUGIXML_DEPRECATED __attribute__((deprecated))
44 +# elif defined(_MSC_VER) && _MSC_VER >= 1300
45 +# define PUGIXML_DEPRECATED __declspec(deprecated)
46 +# else
47 +# define PUGIXML_DEPRECATED
48 +# endif
49 +#endif
50 +
51 +// If no API is defined, assume default
52 +#ifndef PUGIXML_API
53 +# define PUGIXML_API
54 +#endif
55 +
56 +// If no API for classes is defined, assume default
57 +#ifndef PUGIXML_CLASS
58 +# define PUGIXML_CLASS PUGIXML_API
59 +#endif
60 +
61 +// If no API for functions is defined, assume default
62 +#ifndef PUGIXML_FUNCTION
63 +# define PUGIXML_FUNCTION PUGIXML_API
64 +#endif
65 +
66 +// If the platform is known to have long long support, enable long long functions
67 +#ifndef PUGIXML_HAS_LONG_LONG
68 +# if defined(__cplusplus) && __cplusplus >= 201103
69 +# define PUGIXML_HAS_LONG_LONG
70 +# elif defined(_MSC_VER) && _MSC_VER >= 1400
71 +# define PUGIXML_HAS_LONG_LONG
72 +# endif
73 +#endif
74 +
75 +// Character interface macros
76 +#ifdef PUGIXML_WCHAR_MODE
77 +# define PUGIXML_TEXT(t) L ## t
78 +# define PUGIXML_CHAR wchar_t
79 +#else
80 +# define PUGIXML_TEXT(t) t
81 +# define PUGIXML_CHAR char
82 +#endif
83 +
84 +namespace pugi
85 +{
86 + // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE
87 + typedef PUGIXML_CHAR char_t;
88 +
89 +#ifndef PUGIXML_NO_STL
90 + // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE
91 + typedef std::basic_string<PUGIXML_CHAR, std::char_traits<PUGIXML_CHAR>, std::allocator<PUGIXML_CHAR> > string_t;
92 +#endif
93 +}
94 +
95 +// The PugiXML namespace
96 +namespace pugi
97 +{
98 + // Tree node types
99 + enum xml_node_type
100 + {
101 + node_null, // Empty (null) node handle
102 + node_document, // A document tree's absolute root
103 + node_element, // Element tag, i.e. '<node/>'
104 + node_pcdata, // Plain character data, i.e. 'text'
105 + node_cdata, // Character data, i.e. '<![CDATA[text]]>'
106 + node_comment, // Comment tag, i.e. '<!-- text -->'
107 + node_pi, // Processing instruction, i.e. '<?name?>'
108 + node_declaration, // Document declaration, i.e. '<?xml version="1.0"?>'
109 + node_doctype // Document type declaration, i.e. '<!DOCTYPE doc>'
110 + };
111 +
112 + // Parsing options
113 +
114 + // Minimal parsing mode (equivalent to turning all other flags off).
115 + // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed.
116 + const unsigned int parse_minimal = 0x0000;
117 +
118 + // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default.
119 + const unsigned int parse_pi = 0x0001;
120 +
121 + // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default.
122 + const unsigned int parse_comments = 0x0002;
123 +
124 + // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default.
125 + const unsigned int parse_cdata = 0x0004;
126 +
127 + // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree.
128 + // This flag is off by default; turning it on usually results in slower parsing and more memory consumption.
129 + const unsigned int parse_ws_pcdata = 0x0008;
130 +
131 + // This flag determines if character and entity references are expanded during parsing. This flag is on by default.
132 + const unsigned int parse_escapes = 0x0010;
133 +
134 + // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default.
135 + const unsigned int parse_eol = 0x0020;
136 +
137 + // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default.
138 + const unsigned int parse_wconv_attribute = 0x0040;
139 +
140 + // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default.
141 + const unsigned int parse_wnorm_attribute = 0x0080;
142 +
143 + // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default.
144 + const unsigned int parse_declaration = 0x0100;
145 +
146 + // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default.
147 + const unsigned int parse_doctype = 0x0200;
148 +
149 + // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only
150 + // of whitespace is added to the DOM tree.
151 + // This flag is off by default; turning it on may result in slower parsing and more memory consumption.
152 + const unsigned int parse_ws_pcdata_single = 0x0400;
153 +
154 + // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default.
155 + const unsigned int parse_trim_pcdata = 0x0800;
156 +
157 + // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document
158 + // is a valid document. This flag is off by default.
159 + const unsigned int parse_fragment = 0x1000;
160 +
161 + // The default parsing mode.
162 + // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded,
163 + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
164 + const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol;
165 +
166 + // The full parsing mode.
167 + // Nodes of all types are added to the DOM tree, character/reference entities are expanded,
168 + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
169 + const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype;
170 +
171 + // These flags determine the encoding of input data for XML document
172 + enum xml_encoding
173 + {
174 + encoding_auto, // Auto-detect input encoding using BOM or < / <? detection; use UTF8 if BOM is not found
175 + encoding_utf8, // UTF8 encoding
176 + encoding_utf16_le, // Little-endian UTF16
177 + encoding_utf16_be, // Big-endian UTF16
178 + encoding_utf16, // UTF16 with native endianness
179 + encoding_utf32_le, // Little-endian UTF32
180 + encoding_utf32_be, // Big-endian UTF32
181 + encoding_utf32, // UTF32 with native endianness
182 + encoding_wchar, // The same encoding wchar_t has (either UTF16 or UTF32)
183 + encoding_latin1
184 + };
185 +
186 + // Formatting flags
187 +
188 + // Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default.
189 + const unsigned int format_indent = 0x01;
190 +
191 + // Write encoding-specific BOM to the output stream. This flag is off by default.
192 + const unsigned int format_write_bom = 0x02;
193 +
194 + // Use raw output mode (no indentation and no line breaks are written). This flag is off by default.
195 + const unsigned int format_raw = 0x04;
196 +
197 + // Omit default XML declaration even if there is no declaration in the document. This flag is off by default.
198 + const unsigned int format_no_declaration = 0x08;
199 +
200 + // Don't escape attribute values and PCDATA contents. This flag is off by default.
201 + const unsigned int format_no_escapes = 0x10;
202 +
203 + // Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.
204 + const unsigned int format_save_file_text = 0x20;
205 +
206 + // The default set of formatting flags.
207 + // Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
208 + const unsigned int format_default = format_indent;
209 +
210 + // Forward declarations
211 + struct xml_attribute_struct;
212 + struct xml_node_struct;
213 +
214 + class xml_node_iterator;
215 + class xml_attribute_iterator;
216 + class xml_named_node_iterator;
217 +
218 + class xml_tree_walker;
219 +
220 + struct xml_parse_result;
221 +
222 + class xml_node;
223 +
224 + class xml_text;
225 +
226 + #ifndef PUGIXML_NO_XPATH
227 + class xpath_node;
228 + class xpath_node_set;
229 + class xpath_query;
230 + class xpath_variable_set;
231 + #endif
232 +
233 + // Range-based for loop support
234 + template <typename It> class xml_object_range
235 + {
236 + public:
237 + typedef It const_iterator;
238 + typedef It iterator;
239 +
240 + xml_object_range(It b, It e): _begin(b), _end(e)
241 + {
242 + }
243 +
244 + It begin() const { return _begin; }
245 + It end() const { return _end; }
246 +
247 + private:
248 + It _begin, _end;
249 + };
250 +
251 + // Writer interface for node printing (see xml_node::print)
252 + class PUGIXML_CLASS xml_writer
253 + {
254 + public:
255 + virtual ~xml_writer() {}
256 +
257 + // Write memory chunk into stream/file/whatever
258 + virtual void write(const void* data, size_t size) = 0;
259 + };
260 +
261 + // xml_writer implementation for FILE*
262 + class PUGIXML_CLASS xml_writer_file: public xml_writer
263 + {
264 + public:
265 + // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio
266 + xml_writer_file(void* file);
267 +
268 + virtual void write(const void* data, size_t size);
269 +
270 + private:
271 + void* file;
272 + };
273 +
274 + #ifndef PUGIXML_NO_STL
275 + // xml_writer implementation for streams
276 + class PUGIXML_CLASS xml_writer_stream: public xml_writer
277 + {
278 + public:
279 + // Construct writer from an output stream object
280 + xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream);
281 + xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream);
282 +
283 + virtual void write(const void* data, size_t size);
284 +
285 + private:
286 + std::basic_ostream<char, std::char_traits<char> >* narrow_stream;
287 + std::basic_ostream<wchar_t, std::char_traits<wchar_t> >* wide_stream;
288 + };
289 + #endif
290 +
291 + // A light-weight handle for manipulating attributes in DOM tree
292 + class PUGIXML_CLASS xml_attribute
293 + {
294 + friend class xml_attribute_iterator;
295 + friend class xml_node;
296 +
297 + private:
298 + xml_attribute_struct* _attr;
299 +
300 + typedef void (*unspecified_bool_type)(xml_attribute***);
301 +
302 + public:
303 + // Default constructor. Constructs an empty attribute.
304 + xml_attribute();
305 +
306 + // Constructs attribute from internal pointer
307 + explicit xml_attribute(xml_attribute_struct* attr);
308 +
309 + // Safe bool conversion operator
310 + operator unspecified_bool_type() const;
311 +
312 + // Borland C++ workaround
313 + bool operator!() const;
314 +
315 + // Comparison operators (compares wrapped attribute pointers)
316 + bool operator==(const xml_attribute& r) const;
317 + bool operator!=(const xml_attribute& r) const;
318 + bool operator<(const xml_attribute& r) const;
319 + bool operator>(const xml_attribute& r) const;
320 + bool operator<=(const xml_attribute& r) const;
321 + bool operator>=(const xml_attribute& r) const;
322 +
323 + // Check if attribute is empty
324 + bool empty() const;
325 +
326 + // Get attribute name/value, or "" if attribute is empty
327 + const char_t* name() const;
328 + const char_t* value() const;
329 +
330 + // Get attribute value, or the default value if attribute is empty
331 + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
332 +
333 + // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty
334 + int as_int(int def = 0) const;
335 + unsigned int as_uint(unsigned int def = 0) const;
336 + double as_double(double def = 0) const;
337 + float as_float(float def = 0) const;
338 +
339 + #ifdef PUGIXML_HAS_LONG_LONG
340 + long long as_llong(long long def = 0) const;
341 + unsigned long long as_ullong(unsigned long long def = 0) const;
342 + #endif
343 +
344 + // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty
345 + bool as_bool(bool def = false) const;
346 +
347 + // Set attribute name/value (returns false if attribute is empty or there is not enough memory)
348 + bool set_name(const char_t* rhs);
349 + bool set_value(const char_t* rhs);
350 +
351 + // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
352 + bool set_value(int rhs);
353 + bool set_value(unsigned int rhs);
354 + bool set_value(double rhs);
355 + bool set_value(float rhs);
356 + bool set_value(bool rhs);
357 +
358 + #ifdef PUGIXML_HAS_LONG_LONG
359 + bool set_value(long long rhs);
360 + bool set_value(unsigned long long rhs);
361 + #endif
362 +
363 + // Set attribute value (equivalent to set_value without error checking)
364 + xml_attribute& operator=(const char_t* rhs);
365 + xml_attribute& operator=(int rhs);
366 + xml_attribute& operator=(unsigned int rhs);
367 + xml_attribute& operator=(double rhs);
368 + xml_attribute& operator=(float rhs);
369 + xml_attribute& operator=(bool rhs);
370 +
371 + #ifdef PUGIXML_HAS_LONG_LONG
372 + xml_attribute& operator=(long long rhs);
373 + xml_attribute& operator=(unsigned long long rhs);
374 + #endif
375 +
376 + // Get next/previous attribute in the attribute list of the parent node
377 + xml_attribute next_attribute() const;
378 + xml_attribute previous_attribute() const;
379 +
380 + // Get hash value (unique for handles to the same object)
381 + size_t hash_value() const;
382 +
383 + // Get internal pointer
384 + xml_attribute_struct* internal_object() const;
385 + };
386 +
387 +#ifdef __BORLANDC__
388 + // Borland C++ workaround
389 + bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs);
390 + bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs);
391 +#endif
392 +
393 + // A light-weight handle for manipulating nodes in DOM tree
394 + class PUGIXML_CLASS xml_node
395 + {
396 + friend class xml_attribute_iterator;
397 + friend class xml_node_iterator;
398 + friend class xml_named_node_iterator;
399 +
400 + protected:
401 + xml_node_struct* _root;
402 +
403 + typedef void (*unspecified_bool_type)(xml_node***);
404 +
405 + public:
406 + // Default constructor. Constructs an empty node.
407 + xml_node();
408 +
409 + // Constructs node from internal pointer
410 + explicit xml_node(xml_node_struct* p);
411 +
412 + // Safe bool conversion operator
413 + operator unspecified_bool_type() const;
414 +
415 + // Borland C++ workaround
416 + bool operator!() const;
417 +
418 + // Comparison operators (compares wrapped node pointers)
419 + bool operator==(const xml_node& r) const;
420 + bool operator!=(const xml_node& r) const;
421 + bool operator<(const xml_node& r) const;
422 + bool operator>(const xml_node& r) const;
423 + bool operator<=(const xml_node& r) const;
424 + bool operator>=(const xml_node& r) const;
425 +
426 + // Check if node is empty.
427 + bool empty() const;
428 +
429 + // Get node type
430 + xml_node_type type() const;
431 +
432 + // Get node name, or "" if node is empty or it has no name
433 + const char_t* name() const;
434 +
435 + // Get node value, or "" if node is empty or it has no value
436 + // Note: For <node>text</node> node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes.
437 + const char_t* value() const;
438 +
439 + // Get attribute list
440 + xml_attribute first_attribute() const;
441 + xml_attribute last_attribute() const;
442 +
443 + // Get children list
444 + xml_node first_child() const;
445 + xml_node last_child() const;
446 +
447 + // Get next/previous sibling in the children list of the parent node
448 + xml_node next_sibling() const;
449 + xml_node previous_sibling() const;
450 +
451 + // Get parent node
452 + xml_node parent() const;
453 +
454 + // Get root of DOM tree this node belongs to
455 + xml_node root() const;
456 +
457 + // Get text object for the current node
458 + xml_text text() const;
459 +
460 + // Get child, attribute or next/previous sibling with the specified name
461 + xml_node child(const char_t* name) const;
462 + xml_attribute attribute(const char_t* name) const;
463 + xml_node next_sibling(const char_t* name) const;
464 + xml_node previous_sibling(const char_t* name) const;
465 +
466 + // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
467 + const char_t* child_value() const;
468 +
469 + // Get child value of child with specified name. Equivalent to child(name).child_value().
470 + const char_t* child_value(const char_t* name) const;
471 +
472 + // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)
473 + bool set_name(const char_t* rhs);
474 + bool set_value(const char_t* rhs);
475 +
476 + // Add attribute with specified name. Returns added attribute, or empty attribute on errors.
477 + xml_attribute append_attribute(const char_t* name);
478 + xml_attribute prepend_attribute(const char_t* name);
479 + xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr);
480 + xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr);
481 +
482 + // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors.
483 + xml_attribute append_copy(const xml_attribute& proto);
484 + xml_attribute prepend_copy(const xml_attribute& proto);
485 + xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);
486 + xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);
487 +
488 + // Add child node with specified type. Returns added node, or empty node on errors.
489 + xml_node append_child(xml_node_type type = node_element);
490 + xml_node prepend_child(xml_node_type type = node_element);
491 + xml_node insert_child_after(xml_node_type type, const xml_node& node);
492 + xml_node insert_child_before(xml_node_type type, const xml_node& node);
493 +
494 + // Add child element with specified name. Returns added node, or empty node on errors.
495 + xml_node append_child(const char_t* name);
496 + xml_node prepend_child(const char_t* name);
497 + xml_node insert_child_after(const char_t* name, const xml_node& node);
498 + xml_node insert_child_before(const char_t* name, const xml_node& node);
499 +
500 + // Add a copy of the specified node as a child. Returns added node, or empty node on errors.
501 + xml_node append_copy(const xml_node& proto);
502 + xml_node prepend_copy(const xml_node& proto);
503 + xml_node insert_copy_after(const xml_node& proto, const xml_node& node);
504 + xml_node insert_copy_before(const xml_node& proto, const xml_node& node);
505 +
506 + // Move the specified node to become a child of this node. Returns moved node, or empty node on errors.
507 + xml_node append_move(const xml_node& moved);
508 + xml_node prepend_move(const xml_node& moved);
509 + xml_node insert_move_after(const xml_node& moved, const xml_node& node);
510 + xml_node insert_move_before(const xml_node& moved, const xml_node& node);
511 +
512 + // Remove specified attribute
513 + bool remove_attribute(const xml_attribute& a);
514 + bool remove_attribute(const char_t* name);
515 +
516 + // Remove specified child
517 + bool remove_child(const xml_node& n);
518 + bool remove_child(const char_t* name);
519 +
520 + // Parses buffer as an XML document fragment and appends all nodes as children of the current node.
521 + // Copies/converts the buffer, so it may be deleted or changed after the function returns.
522 + // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory.
523 + xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
524 +
525 + // Find attribute using predicate. Returns first attribute for which predicate returned true.
526 + template <typename Predicate> xml_attribute find_attribute(Predicate pred) const
527 + {
528 + if (!_root) return xml_attribute();
529 +
530 + for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute())
531 + if (pred(attrib))
532 + return attrib;
533 +
534 + return xml_attribute();
535 + }
536 +
537 + // Find child node using predicate. Returns first child for which predicate returned true.
538 + template <typename Predicate> xml_node find_child(Predicate pred) const
539 + {
540 + if (!_root) return xml_node();
541 +
542 + for (xml_node node = first_child(); node; node = node.next_sibling())
543 + if (pred(node))
544 + return node;
545 +
546 + return xml_node();
547 + }
548 +
549 + // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true.
550 + template <typename Predicate> xml_node find_node(Predicate pred) const
551 + {
552 + if (!_root) return xml_node();
553 +
554 + xml_node cur = first_child();
555 +
556 + while (cur._root && cur._root != _root)
557 + {
558 + if (pred(cur)) return cur;
559 +
560 + if (cur.first_child()) cur = cur.first_child();
561 + else if (cur.next_sibling()) cur = cur.next_sibling();
562 + else
563 + {
564 + while (!cur.next_sibling() && cur._root != _root) cur = cur.parent();
565 +
566 + if (cur._root != _root) cur = cur.next_sibling();
567 + }
568 + }
569 +
570 + return xml_node();
571 + }
572 +
573 + // Find child node by attribute name/value
574 + xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const;
575 + xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const;
576 +
577 + #ifndef PUGIXML_NO_STL
578 + // Get the absolute node path from root as a text string.
579 + string_t path(char_t delimiter = '/') const;
580 + #endif
581 +
582 + // Search for a node by path consisting of node names and . or .. elements.
583 + xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const;
584 +
585 + // Recursively traverse subtree with xml_tree_walker
586 + bool traverse(xml_tree_walker& walker);
587 +
588 + #ifndef PUGIXML_NO_XPATH
589 + // Select single node by evaluating XPath query. Returns first node from the resulting node set.
590 + xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const;
591 + xpath_node select_node(const xpath_query& query) const;
592 +
593 + // Select node set by evaluating XPath query
594 + xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
595 + xpath_node_set select_nodes(const xpath_query& query) const;
596 +
597 + // (deprecated: use select_node instead) Select single node by evaluating XPath query.
598 + xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
599 + xpath_node select_single_node(const xpath_query& query) const;
600 +
601 + #endif
602 +
603 + // Print subtree using a writer object
604 + void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
605 +
606 + #ifndef PUGIXML_NO_STL
607 + // Print subtree to stream
608 + void print(std::basic_ostream<char, std::char_traits<char> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
609 + void print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const;
610 + #endif
611 +
612 + // Child nodes iterators
613 + typedef xml_node_iterator iterator;
614 +
615 + iterator begin() const;
616 + iterator end() const;
617 +
618 + // Attribute iterators
619 + typedef xml_attribute_iterator attribute_iterator;
620 +
621 + attribute_iterator attributes_begin() const;
622 + attribute_iterator attributes_end() const;
623 +
624 + // Range-based for support
625 + xml_object_range<xml_node_iterator> children() const;
626 + xml_object_range<xml_named_node_iterator> children(const char_t* name) const;
627 + xml_object_range<xml_attribute_iterator> attributes() const;
628 +
629 + // Get node offset in parsed file/string (in char_t units) for debugging purposes
630 + ptrdiff_t offset_debug() const;
631 +
632 + // Get hash value (unique for handles to the same object)
633 + size_t hash_value() const;
634 +
635 + // Get internal pointer
636 + xml_node_struct* internal_object() const;
637 + };
638 +
639 +#ifdef __BORLANDC__
640 + // Borland C++ workaround
641 + bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs);
642 + bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs);
643 +#endif
644 +
645 + // A helper for working with text inside PCDATA nodes
646 + class PUGIXML_CLASS xml_text
647 + {
648 + friend class xml_node;
649 +
650 + xml_node_struct* _root;
651 +
652 + typedef void (*unspecified_bool_type)(xml_text***);
653 +
654 + explicit xml_text(xml_node_struct* root);
655 +
656 + xml_node_struct* _data_new();
657 + xml_node_struct* _data() const;
658 +
659 + public:
660 + // Default constructor. Constructs an empty object.
661 + xml_text();
662 +
663 + // Safe bool conversion operator
664 + operator unspecified_bool_type() const;
665 +
666 + // Borland C++ workaround
667 + bool operator!() const;
668 +
669 + // Check if text object is empty
670 + bool empty() const;
671 +
672 + // Get text, or "" if object is empty
673 + const char_t* get() const;
674 +
675 + // Get text, or the default value if object is empty
676 + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
677 +
678 + // Get text as a number, or the default value if conversion did not succeed or object is empty
679 + int as_int(int def = 0) const;
680 + unsigned int as_uint(unsigned int def = 0) const;
681 + double as_double(double def = 0) const;
682 + float as_float(float def = 0) const;
683 +
684 + #ifdef PUGIXML_HAS_LONG_LONG
685 + long long as_llong(long long def = 0) const;
686 + unsigned long long as_ullong(unsigned long long def = 0) const;
687 + #endif
688 +
689 + // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty
690 + bool as_bool(bool def = false) const;
691 +
692 + // Set text (returns false if object is empty or there is not enough memory)
693 + bool set(const char_t* rhs);
694 +
695 + // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
696 + bool set(int rhs);
697 + bool set(unsigned int rhs);
698 + bool set(double rhs);
699 + bool set(float rhs);
700 + bool set(bool rhs);
701 +
702 + #ifdef PUGIXML_HAS_LONG_LONG
703 + bool set(long long rhs);
704 + bool set(unsigned long long rhs);
705 + #endif
706 +
707 + // Set text (equivalent to set without error checking)
708 + xml_text& operator=(const char_t* rhs);
709 + xml_text& operator=(int rhs);
710 + xml_text& operator=(unsigned int rhs);
711 + xml_text& operator=(double rhs);
712 + xml_text& operator=(float rhs);
713 + xml_text& operator=(bool rhs);
714 +
715 + #ifdef PUGIXML_HAS_LONG_LONG
716 + xml_text& operator=(long long rhs);
717 + xml_text& operator=(unsigned long long rhs);
718 + #endif
719 +
720 + // Get the data node (node_pcdata or node_cdata) for this object
721 + xml_node data() const;
722 + };
723 +
724 +#ifdef __BORLANDC__
725 + // Borland C++ workaround
726 + bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs);
727 + bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs);
728 +#endif
729 +
730 + // Child node iterator (a bidirectional iterator over a collection of xml_node)
731 + class PUGIXML_CLASS xml_node_iterator
732 + {
733 + friend class xml_node;
734 +
735 + private:
736 + mutable xml_node _wrap;
737 + xml_node _parent;
738 +
739 + xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent);
740 +
741 + public:
742 + // Iterator traits
743 + typedef ptrdiff_t difference_type;
744 + typedef xml_node value_type;
745 + typedef xml_node* pointer;
746 + typedef xml_node& reference;
747 +
748 + #ifndef PUGIXML_NO_STL
749 + typedef std::bidirectional_iterator_tag iterator_category;
750 + #endif
751 +
752 + // Default constructor
753 + xml_node_iterator();
754 +
755 + // Construct an iterator which points to the specified node
756 + xml_node_iterator(const xml_node& node);
757 +
758 + // Iterator operators
759 + bool operator==(const xml_node_iterator& rhs) const;
760 + bool operator!=(const xml_node_iterator& rhs) const;
761 +
762 + xml_node& operator*() const;
763 + xml_node* operator->() const;
764 +
765 + const xml_node_iterator& operator++();
766 + xml_node_iterator operator++(int);
767 +
768 + const xml_node_iterator& operator--();
769 + xml_node_iterator operator--(int);
770 + };
771 +
772 + // Attribute iterator (a bidirectional iterator over a collection of xml_attribute)
773 + class PUGIXML_CLASS xml_attribute_iterator
774 + {
775 + friend class xml_node;
776 +
777 + private:
778 + mutable xml_attribute _wrap;
779 + xml_node _parent;
780 +
781 + xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent);
782 +
783 + public:
784 + // Iterator traits
785 + typedef ptrdiff_t difference_type;
786 + typedef xml_attribute value_type;
787 + typedef xml_attribute* pointer;
788 + typedef xml_attribute& reference;
789 +
790 + #ifndef PUGIXML_NO_STL
791 + typedef std::bidirectional_iterator_tag iterator_category;
792 + #endif
793 +
794 + // Default constructor
795 + xml_attribute_iterator();
796 +
797 + // Construct an iterator which points to the specified attribute
798 + xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent);
799 +
800 + // Iterator operators
801 + bool operator==(const xml_attribute_iterator& rhs) const;
802 + bool operator!=(const xml_attribute_iterator& rhs) const;
803 +
804 + xml_attribute& operator*() const;
805 + xml_attribute* operator->() const;
806 +
807 + const xml_attribute_iterator& operator++();
808 + xml_attribute_iterator operator++(int);
809 +
810 + const xml_attribute_iterator& operator--();
811 + xml_attribute_iterator operator--(int);
812 + };
813 +
814 + // Named node range helper
815 + class PUGIXML_CLASS xml_named_node_iterator
816 + {
817 + friend class xml_node;
818 +
819 + public:
820 + // Iterator traits
821 + typedef ptrdiff_t difference_type;
822 + typedef xml_node value_type;
823 + typedef xml_node* pointer;
824 + typedef xml_node& reference;
825 +
826 + #ifndef PUGIXML_NO_STL
827 + typedef std::bidirectional_iterator_tag iterator_category;
828 + #endif
829 +
830 + // Default constructor
831 + xml_named_node_iterator();
832 +
833 + // Construct an iterator which points to the specified node
834 + xml_named_node_iterator(const xml_node& node, const char_t* name);
835 +
836 + // Iterator operators
837 + bool operator==(const xml_named_node_iterator& rhs) const;
838 + bool operator!=(const xml_named_node_iterator& rhs) const;
839 +
840 + xml_node& operator*() const;
841 + xml_node* operator->() const;
842 +
843 + const xml_named_node_iterator& operator++();
844 + xml_named_node_iterator operator++(int);
845 +
846 + const xml_named_node_iterator& operator--();
847 + xml_named_node_iterator operator--(int);
848 +
849 + private:
850 + mutable xml_node _wrap;
851 + xml_node _parent;
852 + const char_t* _name;
853 +
854 + xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name);
855 + };
856 +
857 + // Abstract tree walker class (see xml_node::traverse)
858 + class PUGIXML_CLASS xml_tree_walker
859 + {
860 + friend class xml_node;
861 +
862 + private:
863 + int _depth;
864 +
865 + protected:
866 + // Get current traversal depth
867 + int depth() const;
868 +
869 + public:
870 + xml_tree_walker();
871 + virtual ~xml_tree_walker();
872 +
873 + // Callback that is called when traversal begins
874 + virtual bool begin(xml_node& node);
875 +
876 + // Callback that is called for each node traversed
877 + virtual bool for_each(xml_node& node) = 0;
878 +
879 + // Callback that is called when traversal ends
880 + virtual bool end(xml_node& node);
881 + };
882 +
883 + // Parsing status, returned as part of xml_parse_result object
884 + enum xml_parse_status
885 + {
886 + status_ok = 0, // No error
887 +
888 + status_file_not_found, // File was not found during load_file()
889 + status_io_error, // Error reading from file/stream
890 + status_out_of_memory, // Could not allocate memory
891 + status_internal_error, // Internal error occurred
892 +
893 + status_unrecognized_tag, // Parser could not determine tag type
894 +
895 + status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction
896 + status_bad_comment, // Parsing error occurred while parsing comment
897 + status_bad_cdata, // Parsing error occurred while parsing CDATA section
898 + status_bad_doctype, // Parsing error occurred while parsing document type declaration
899 + status_bad_pcdata, // Parsing error occurred while parsing PCDATA section
900 + status_bad_start_element, // Parsing error occurred while parsing start element tag
901 + status_bad_attribute, // Parsing error occurred while parsing element attribute
902 + status_bad_end_element, // Parsing error occurred while parsing end element tag
903 + status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)
904 +
905 + status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer)
906 +
907 + status_no_document_element // Parsing resulted in a document without element nodes
908 + };
909 +
910 + // Parsing result
911 + struct PUGIXML_CLASS xml_parse_result
912 + {
913 + // Parsing status (see xml_parse_status)
914 + xml_parse_status status;
915 +
916 + // Last parsed offset (in char_t units from start of input data)
917 + ptrdiff_t offset;
918 +
919 + // Source document encoding
920 + xml_encoding encoding;
921 +
922 + // Default constructor, initializes object to failed state
923 + xml_parse_result();
924 +
925 + // Cast to bool operator
926 + operator bool() const;
927 +
928 + // Get error description
929 + const char* description() const;
930 + };
931 +
932 + // Document class (DOM tree root)
933 + class PUGIXML_CLASS xml_document: public xml_node
934 + {
935 + private:
936 + char_t* _buffer;
937 +
938 + char _memory[192];
939 +
940 + // Non-copyable semantics
941 + xml_document(const xml_document&);
942 + const xml_document& operator=(const xml_document&);
943 +
944 + void create();
945 + void destroy();
946 +
947 + public:
948 + // Default constructor, makes empty document
949 + xml_document();
950 +
951 + // Destructor, invalidates all node/attribute handles to this document
952 + ~xml_document();
953 +
954 + // Removes all nodes, leaving the empty document
955 + void reset();
956 +
957 + // Removes all nodes, then copies the entire contents of the specified document
958 + void reset(const xml_document& proto);
959 +
960 + #ifndef PUGIXML_NO_STL
961 + // Load document from stream.
962 + xml_parse_result load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
963 + xml_parse_result load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options = parse_default);
964 + #endif
965 +
966 + // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied.
967 + xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
968 +
969 + // Load document from zero-terminated string. No encoding conversions are applied.
970 + xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default);
971 +
972 + // Load document from file
973 + xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
974 + xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
975 +
976 + // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns.
977 + xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
978 +
979 + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
980 + // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed.
981 + xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
982 +
983 + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
984 + // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore).
985 + xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
986 +
987 + // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details).
988 + void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
989 +
990 + #ifndef PUGIXML_NO_STL
991 + // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details).
992 + void save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
993 + void save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const;
994 + #endif
995 +
996 + // Save XML to file
997 + bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
998 + bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
999 +
1000 + // Get document element
1001 + xml_node document_element() const;
1002 + };
1003 +
1004 +#ifndef PUGIXML_NO_XPATH
1005 + // XPath query return type
1006 + enum xpath_value_type
1007 + {
1008 + xpath_type_none, // Unknown type (query failed to compile)
1009 + xpath_type_node_set, // Node set (xpath_node_set)
1010 + xpath_type_number, // Number
1011 + xpath_type_string, // String
1012 + xpath_type_boolean // Boolean
1013 + };
1014 +
1015 + // XPath parsing result
1016 + struct PUGIXML_CLASS xpath_parse_result
1017 + {
1018 + // Error message (0 if no error)
1019 + const char* error;
1020 +
1021 + // Last parsed offset (in char_t units from string start)
1022 + ptrdiff_t offset;
1023 +
1024 + // Default constructor, initializes object to failed state
1025 + xpath_parse_result();
1026 +
1027 + // Cast to bool operator
1028 + operator bool() const;
1029 +
1030 + // Get error description
1031 + const char* description() const;
1032 + };
1033 +
1034 + // A single XPath variable
1035 + class PUGIXML_CLASS xpath_variable
1036 + {
1037 + friend class xpath_variable_set;
1038 +
1039 + protected:
1040 + xpath_value_type _type;
1041 + xpath_variable* _next;
1042 +
1043 + xpath_variable();
1044 +
1045 + // Non-copyable semantics
1046 + xpath_variable(const xpath_variable&);
1047 + xpath_variable& operator=(const xpath_variable&);
1048 +
1049 + public:
1050 + // Get variable name
1051 + const char_t* name() const;
1052 +
1053 + // Get variable type
1054 + xpath_value_type type() const;
1055 +
1056 + // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error
1057 + bool get_boolean() const;
1058 + double get_number() const;
1059 + const char_t* get_string() const;
1060 + const xpath_node_set& get_node_set() const;
1061 +
1062 + // Set variable value; no type conversion is performed, false is returned on type mismatch error
1063 + bool set(bool value);
1064 + bool set(double value);
1065 + bool set(const char_t* value);
1066 + bool set(const xpath_node_set& value);
1067 + };
1068 +
1069 + // A set of XPath variables
1070 + class PUGIXML_CLASS xpath_variable_set
1071 + {
1072 + private:
1073 + xpath_variable* _data[64];
1074 +
1075 + // Non-copyable semantics
1076 + xpath_variable_set(const xpath_variable_set&);
1077 + xpath_variable_set& operator=(const xpath_variable_set&);
1078 +
1079 + xpath_variable* find(const char_t* name) const;
1080 +
1081 + public:
1082 + // Default constructor/destructor
1083 + xpath_variable_set();
1084 + ~xpath_variable_set();
1085 +
1086 + // Add a new variable or get the existing one, if the types match
1087 + xpath_variable* add(const char_t* name, xpath_value_type type);
1088 +
1089 + // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch
1090 + bool set(const char_t* name, bool value);
1091 + bool set(const char_t* name, double value);
1092 + bool set(const char_t* name, const char_t* value);
1093 + bool set(const char_t* name, const xpath_node_set& value);
1094 +
1095 + // Get existing variable by name
1096 + xpath_variable* get(const char_t* name);
1097 + const xpath_variable* get(const char_t* name) const;
1098 + };
1099 +
1100 + // A compiled XPath query object
1101 + class PUGIXML_CLASS xpath_query
1102 + {
1103 + private:
1104 + void* _impl;
1105 + xpath_parse_result _result;
1106 +
1107 + typedef void (*unspecified_bool_type)(xpath_query***);
1108 +
1109 + // Non-copyable semantics
1110 + xpath_query(const xpath_query&);
1111 + xpath_query& operator=(const xpath_query&);
1112 +
1113 + public:
1114 + // Construct a compiled object from XPath expression.
1115 + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors.
1116 + explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);
1117 +
1118 + // Destructor
1119 + ~xpath_query();
1120 +
1121 + // Get query expression return type
1122 + xpath_value_type return_type() const;
1123 +
1124 + // Evaluate expression as boolean value in the specified context; performs type conversion if necessary.
1125 + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
1126 + bool evaluate_boolean(const xpath_node& n) const;
1127 +
1128 + // Evaluate expression as double value in the specified context; performs type conversion if necessary.
1129 + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
1130 + double evaluate_number(const xpath_node& n) const;
1131 +
1132 + #ifndef PUGIXML_NO_STL
1133 + // Evaluate expression as string value in the specified context; performs type conversion if necessary.
1134 + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
1135 + string_t evaluate_string(const xpath_node& n) const;
1136 + #endif
1137 +
1138 + // Evaluate expression as string value in the specified context; performs type conversion if necessary.
1139 + // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero).
1140 + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
1141 + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead.
1142 + size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const;
1143 +
1144 + // Evaluate expression as node set in the specified context.
1145 + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
1146 + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.
1147 + xpath_node_set evaluate_node_set(const xpath_node& n) const;
1148 +
1149 + // Evaluate expression as node set in the specified context.
1150 + // Return first node in document order, or empty node if node set is empty.
1151 + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
1152 + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead.
1153 + xpath_node evaluate_node(const xpath_node& n) const;
1154 +
1155 + // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)
1156 + const xpath_parse_result& result() const;
1157 +
1158 + // Safe bool conversion operator
1159 + operator unspecified_bool_type() const;
1160 +
1161 + // Borland C++ workaround
1162 + bool operator!() const;
1163 + };
1164 +
1165 + #ifndef PUGIXML_NO_EXCEPTIONS
1166 + // XPath exception class
1167 + class PUGIXML_CLASS xpath_exception: public std::exception
1168 + {
1169 + private:
1170 + xpath_parse_result _result;
1171 +
1172 + public:
1173 + // Construct exception from parse result
1174 + explicit xpath_exception(const xpath_parse_result& result);
1175 +
1176 + // Get error message
1177 + virtual const char* what() const throw();
1178 +
1179 + // Get parse result
1180 + const xpath_parse_result& result() const;
1181 + };
1182 + #endif
1183 +
1184 + // XPath node class (either xml_node or xml_attribute)
1185 + class PUGIXML_CLASS xpath_node
1186 + {
1187 + private:
1188 + xml_node _node;
1189 + xml_attribute _attribute;
1190 +
1191 + typedef void (*unspecified_bool_type)(xpath_node***);
1192 +
1193 + public:
1194 + // Default constructor; constructs empty XPath node
1195 + xpath_node();
1196 +
1197 + // Construct XPath node from XML node/attribute
1198 + xpath_node(const xml_node& node);
1199 + xpath_node(const xml_attribute& attribute, const xml_node& parent);
1200 +
1201 + // Get node/attribute, if any
1202 + xml_node node() const;
1203 + xml_attribute attribute() const;
1204 +
1205 + // Get parent of contained node/attribute
1206 + xml_node parent() const;
1207 +
1208 + // Safe bool conversion operator
1209 + operator unspecified_bool_type() const;
1210 +
1211 + // Borland C++ workaround
1212 + bool operator!() const;
1213 +
1214 + // Comparison operators
1215 + bool operator==(const xpath_node& n) const;
1216 + bool operator!=(const xpath_node& n) const;
1217 + };
1218 +
1219 +#ifdef __BORLANDC__
1220 + // Borland C++ workaround
1221 + bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs);
1222 + bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs);
1223 +#endif
1224 +
1225 + // A fixed-size collection of XPath nodes
1226 + class PUGIXML_CLASS xpath_node_set
1227 + {
1228 + public:
1229 + // Collection type
1230 + enum type_t
1231 + {
1232 + type_unsorted, // Not ordered
1233 + type_sorted, // Sorted by document order (ascending)
1234 + type_sorted_reverse // Sorted by document order (descending)
1235 + };
1236 +
1237 + // Constant iterator type
1238 + typedef const xpath_node* const_iterator;
1239 +
1240 + // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work
1241 + typedef const xpath_node* iterator;
1242 +
1243 + // Default constructor. Constructs empty set.
1244 + xpath_node_set();
1245 +
1246 + // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful
1247 + xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted);
1248 +
1249 + // Destructor
1250 + ~xpath_node_set();
1251 +
1252 + // Copy constructor/assignment operator
1253 + xpath_node_set(const xpath_node_set& ns);
1254 + xpath_node_set& operator=(const xpath_node_set& ns);
1255 +
1256 + // Get collection type
1257 + type_t type() const;
1258 +
1259 + // Get collection size
1260 + size_t size() const;
1261 +
1262 + // Indexing operator
1263 + const xpath_node& operator[](size_t index) const;
1264 +
1265 + // Collection iterators
1266 + const_iterator begin() const;
1267 + const_iterator end() const;
1268 +
1269 + // Sort the collection in ascending/descending order by document order
1270 + void sort(bool reverse = false);
1271 +
1272 + // Get first node in the collection by document order
1273 + xpath_node first() const;
1274 +
1275 + // Check if collection is empty
1276 + bool empty() const;
1277 +
1278 + private:
1279 + type_t _type;
1280 +
1281 + xpath_node _storage;
1282 +
1283 + xpath_node* _begin;
1284 + xpath_node* _end;
1285 +
1286 + void _assign(const_iterator begin, const_iterator end);
1287 + };
1288 +#endif
1289 +
1290 +#ifndef PUGIXML_NO_STL
1291 + // Convert wide string to UTF8
1292 + std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const wchar_t* str);
1293 + std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >& str);
1294 +
1295 + // Convert UTF8 to wide string
1296 + std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const char* str);
1297 + std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >& str);
1298 +#endif
1299 +
1300 + // Memory allocation function interface; returns pointer to allocated memory or NULL on failure
1301 + typedef void* (*allocation_function)(size_t size);
1302 +
1303 + // Memory deallocation function interface
1304 + typedef void (*deallocation_function)(void* ptr);
1305 +
1306 + // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions.
1307 + void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate);
1308 +
1309 + // Get current memory management functions
1310 + allocation_function PUGIXML_FUNCTION get_memory_allocation_function();
1311 + deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function();
1312 +}
1313 +
1314 +#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))
1315 +namespace std
1316 +{
1317 + // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)
1318 + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&);
1319 + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&);
1320 + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);
1321 +}
1322 +#endif
1323 +
1324 +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)
1325 +namespace std
1326 +{
1327 + // Workarounds for (non-standard) iterator category detection
1328 + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&);
1329 + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&);
1330 + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);
1331 +}
1332 +#endif
1333 +
1334 +#endif
1335 +
1336 +// Make sure implementation is included in header-only mode
1337 +// Use macro expansion in #include to work around QMake (QTBUG-11923)
1338 +#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE)
1339 +# define PUGIXML_SOURCE "pugixml.cpp"
1340 +# include PUGIXML_SOURCE
1341 +#endif
1342 +
1343 +/**
1344 + * Copyright (c) 2006-2015 Arseny Kapoulkine
1345 + *
1346 + * Permission is hereby granted, free of charge, to any person
1347 + * obtaining a copy of this software and associated documentation
1348 + * files (the "Software"), to deal in the Software without
1349 + * restriction, including without limitation the rights to use,
1350 + * copy, modify, merge, publish, distribute, sublicense, and/or sell
1351 + * copies of the Software, and to permit persons to whom the
1352 + * Software is furnished to do so, subject to the following
1353 + * conditions:
1354 + *
1355 + * The above copyright notice and this permission notice shall be
1356 + * included in all copies or substantial portions of the Software.
1357 + *
1358 + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
1359 + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
1360 + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
1361 + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
1362 + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
1363 + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
1364 + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
1365 + * OTHER DEALINGS IN THE SOFTWARE.
1366 + */
1 +# sakurakit.pri
2 +# 6/28/2011 jichi
3 +#
4 +# config:
5 +# - sakurakit_qml
6 +# - sakurakit_gui
7 +# - sakurakit_widgets
8 +
9 +DEFINES += WITH_LIB_SAKURAKIT
10 +DEPENDPATH += $$PWD
11 +
12 +#QT += core
13 +HEADERS += \
14 + $$PWD/skautorun.h \
15 + $$PWD/skdebug.h \
16 + $$PWD/skglobal.h \
17 + $$PWD/skhash.h
18 +
19 +#CONFIG(sakurakit_gui) {
20 +# DEFINES += SK_ENABLE_GUI
21 +# QT += gui
22 +# HEADERS += \
23 +# $$PWD/skuiutil.h
24 +#
25 +# CONFIG(sakurakit_qml) {
26 +# DEFINES += SK_ENABLE_QML
27 +# QT += qml quick
28 +# HEADERS += \
29 +# $$PWD/skdraggablequickview.h
30 +# SOURCES += \
31 +# $$PWD/skdraggablequickview.cc
32 +# }
33 +#
34 +# CONFIG(sakurakit_widgets) {
35 +# DEFINES += SK_ENABLE_WIDGETS
36 +# QT += widgets
37 +# HEADERS += \
38 +# $$PWD/skdraggabledialog.h \
39 +# $$PWD/skdraggablemainwindow.h \
40 +# $$PWD/skdraggablewidget.h \
41 +# $$PWD/skpushbutton.h \
42 +# $$PWD/sksystemtrayicon.h \
43 +# $$PWD/sktoolbutton.h \
44 +# $$PWD/skwindowcontainer.h
45 +# SOURCES += \
46 +# $$PWD/skdraggablewidget.cc \
47 +# $$PWD/skpushbutton.cc \
48 +# $$PWD/sktoolbutton.cc \
49 +# $$PWD/skwindowcontainer.cc \
50 +# $$PWD/skdraggabledialog.cc \
51 +# $$PWD/skdraggablemainwindow.cc
52 +#
53 +# CONFIG(sakurakit_qml) {
54 +# HEADERS += \
55 +# $$PWD/skquickwidget.h
56 +# SOURCES += \
57 +# $$PWD/skquickwidget.cc
58 +# }
59 +# }
60 +#}
61 +
62 +# EOF
1 +#ifndef SKAUTORUN_H
2 +#define SKAUTORUN_H
3 +
4 +// skautorun.h
5 +// 9/30/2012 jichi
6 +
7 +#include "sakurakit/skglobal.h"
8 +#include <functional>
9 +
10 +SK_BEGIN_NAMESPACE
11 +
12 +class SkAutoRun
13 +{
14 +public:
15 + typedef std::function<void ()> function_type;
16 + SkAutoRun(const function_type &start, const function_type &exit)
17 + : exit_(exit) { start(); }
18 + ~SkAutoRun() { exit_(); }
19 +private:
20 + function_type exit_;
21 +};
22 +
23 +class SkAutoRunAtStartup
24 +{
25 +public:
26 + typedef SkAutoRun::function_type function_type;
27 + explicit SkAutoRunAtStartup(const function_type &start) { start(); }
28 +};
29 +
30 +class SkAutoRunAtExit
31 +{
32 +public:
33 + typedef SkAutoRun::function_type function_type;
34 + explicit SkAutoRunAtExit(const function_type &exit) : exit_(exit) {}
35 + ~SkAutoRunAtExit() { exit_(); }
36 +private:
37 + function_type exit_;
38 +};
39 +
40 +SK_END_NAMESPACE
41 +
42 +#endif // SkAUTORUN_H
1 +#ifndef SKDEBUG_H
2 +#define SKDEBUG_H
3 +
4 +// skdebug.h
5 +// 10/16/2011 jichi
6 +// Macros for debug.
7 +
8 +// Debug I/O
9 +// - DPRINT: similar to fprintf, or KDPrint. Print to qDebug
10 +// - DOUT: similar to std::cout. Print to qDebug
11 +// - DERR: similar to std::cerr. Print to qWarning
12 +#if defined(DEBUG) && !defined(SK_NO_DEBUG)
13 +
14 +# if defined(QT_CORE_LIB) && !defined(SK_NO_QT)
15 +# include <QtCore/QDebug>
16 +# define DPRINT(...) qDebug(QString("%1:%2:").arg((DEBUG), (__FUNCTION__)).toLocal8Bit().constData(), __VA_ARGS__)
17 +# define DOUT(_msg) qDebug() << QString("%1:%2:").arg((DEBUG), (__FUNCTION__)).toLocal8Bit().constData() << _msg
18 +# define DERR(_msg) qWarning() << QString("%1:%2:").arg((DEBUG), (__FUNCTION__)).toLocal8Bit().constData() << _msg
19 +# else
20 +# include <iostream>
21 +# include <cstdio>
22 +# define DPRINT(...) fprintf(stderr, DEBUG ":" __FUNCTION__ ": " __VA_ARGS__)
23 +# define DWPRINT(...) fwprintf(stderr, DEBUG ":" __FUNCTION__ ": " __VA_ARGS__)
24 +# define DOUT(_msg) std::cout << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
25 +# define DWOUT(_msg) std::wcout << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
26 +# define DERR(_msg) std::cerr << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
27 +# define DWERR(_msg) std::wcerr << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
28 +# endif // QT_CORE_LIB
29 +
30 +#else // DEBUG
31 +
32 +# define DPRINT(_dummy) (void)0
33 +# define DOUT(_dummy) (void)0
34 +# define DERR(_dummy) (void)0
35 +
36 +//#ifdef _MSC_VER
37 +//# pragma warning (disable:4390) // C4390: empty controlled statement found: is this the intent?
38 +//#endif // _MSC_VER
39 +
40 +//#ifdef __GNUC__
41 +//# pragma GCC diagnostic ignored "-Wempty-body" // empty body in an if or else statement
42 +//#endif // __GNUC__
43 +
44 +#endif // DEBUG
45 +
46 +#endif // SKDEBUG_H
1 +#ifndef SKGLOBAL_H
2 +#define SKGLOBAL_H
3 +
4 +// skglobal.h
5 +// 9/15/2012 jichi
6 +// Similar to QtGlobal from Qt.
7 +//
8 +// Conventions:
9 +// - All classes in sakurakit will be wrapped with SK_BEGIN_NAMESPACE and SK_END_NAMESPACE
10 +// - All classes from sakurakit begin with Sk, such as SkClassA.
11 +// All functions from sakurakit begin with sk, such as skFuncA.
12 +
13 +// Redefine SK_BEGIN_NAMESPACE/SK_END_NAMESPACE if need custom namespace
14 +#ifndef SK_BEGIN_NAMESPACE
15 +# define SK_BEGIN_NAMESPACE namespace Sk {
16 +#endif
17 +#ifndef SK_END_NAMESPACE
18 +# define SK_END_NAMESPACE } // namespace Sk
19 +#endif
20 +
21 +#define SK_FORWARD_DECLARE_CLASS(_name) SK_BEGIN_NAMESPACE class _name; SK_END_NAMESPACE
22 +#define SK_FORWARD_DECLARE_STRUCT(_name) SK_BEGIN_NAMESPACE struct _name; SK_END_NAMESPACE
23 +
24 +SK_BEGIN_NAMESPACE
25 +namespace Sk {}
26 +SK_END_NAMESPACE
27 +
28 +// In case Qt is not avaliable
29 +
30 +//inline void sk_noop(void) {}
31 +//
32 +//template <typename T>
33 +//inline void skUnused(T &x) { (void)x; }
34 +
35 +#define SK_UNUSED(_var) (void)(_var)
36 +#define SK_NOP SK_UNUSED(0)
37 +
38 +// same as Q_DISABLE_COPY and boost::noncopyable
39 +// Disable when BOOST_PYTHON is enabled
40 +#ifdef BOOST_PYTHON
41 +# define SK_DISABLE_COPY(_class)
42 +#else
43 +# define SK_DISABLE_COPY(_class) \
44 + _class(const _class &); \
45 + _class &operator=(const _class &);
46 +#endif // BOOST_PYTHON
47 +
48 +// - Qt-like Pimp -
49 +
50 +// Similar to QT_DECLARE_PRIVATE
51 +#define SK_DECLARE_PRIVATE(_class) \
52 + friend class _class; \
53 + typedef _class D; \
54 + D *const d_;
55 +
56 +// Similar to QT_DECLARE_PUBLIC
57 +#define SK_DECLARE_PUBLIC(_class) \
58 + friend class _class; \
59 + typedef _class Q; \
60 + Q *const q_;
61 +
62 +// - Self and Base -
63 +
64 +#define SK_CLASS(_self) \
65 + typedef _self Self; \
66 + Self *self() const { return const_cast<Self *>(this); }
67 +
68 +#define SK_EXTEND_CLASS(_self, _base) \
69 + SK_CLASS(_self) \
70 + typedef _base Base;
71 +
72 +#define SK_UNDEF_POS QPoint(-1, -1)
73 +#define SK_UNDEF_POSF QPointF(-1, -1)
74 +
75 +// - QWidget Style Class for QSS -
76 +
77 +// Read-only property
78 +#define SK_STYLE_CLASS(_class) \
79 + Q_PROPERTY(QString class READ styleClass) \
80 + public: \
81 + QString styleClass() const { return #_class; } \
82 + private:
83 +
84 +// Read-write property
85 +#define SK_SYNTHESIZE_STYLE_CLASS \
86 + Q_PROPERTY(QString class READ styleClass WRITE setStyleClass) \
87 + QString styleClass_; \
88 + public: \
89 + QString styleClass() const { return styleClass_; } \
90 + public slots: \
91 + void seStyleClass(const QString &value) { styleClass_ = value; } \
92 + private:
93 +
94 +#endif // SKGLOBAL_H
1 +#ifndef SKHASH_H
2 +#define SKHASH_H
3 +
4 +// skhash.h
5 +// 8/1/2011
6 +
7 +#include "sakurakit/skglobal.h"
8 +#include <QtGlobal>
9 +
10 +SK_BEGIN_NAMESPACE
11 +
12 +enum : quint64 { djb2_hash0 = 5381 };
13 +
14 +/// djb2: h = h*33 + c
15 +inline quint64 djb2(const quint8 *str, quint64 hash = djb2_hash0)
16 +{
17 + quint8 c;
18 + while ((c = *str++))
19 + hash = ((hash << 5) + hash) + c; // hash * 33 + c
20 + return hash;
21 +}
22 +
23 +/// s: signed char
24 +inline quint64 djb2_s(const char *str, quint64 hash = djb2_hash0)
25 +{
26 + char c;
27 + while ((c = *str++))
28 + hash = ((hash << 5) + hash) + c; // hash * 33 + c
29 + return hash;
30 +}
31 +
32 +/// n: length
33 +inline quint64 djb2_n(const quint8 *str, size_t len, quint64 hash = djb2_hash0)
34 +{
35 + while (len--)
36 + hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
37 + return hash;
38 +}
39 +
40 +/// sdbm: hash(i) = hash(i - 1) * 65599 + str[i];
41 +inline quint64 sdbm(const quint8 *str, quint64 hash = 0)
42 +{
43 + quint8 c;
44 + while ((c = *str++))
45 + hash = c + (hash << 6) + (hash << 16) - hash;
46 + return hash;
47 +}
48 +
49 +inline quint64 loselose(const quint8 *str, quint64 hash = 0)
50 +{
51 + quint8 c;
52 + while ((c = *str++))
53 + hash += c;
54 + return hash;
55 +}
56 +
57 +SK_END_NAMESPACE
58 +
59 +#endif // SKHASH_H
1 +#pragma once
2 +
3 +// growl.h
4 +// 9/17/2013 jichi
5 +
6 +//#ifdef GROWL_HAS_GROWL
7 +
8 +#include <windows.h>
9 +#include <cstdio>
10 +
11 +#define GROWL_MSG_A(_msg) MessageBoxA(nullptr, _msg, "VNR Message", MB_OK)
12 +#define GROWL_MSG(_msg) MessageBoxW(nullptr, _msg, L"VNR Message", MB_OK)
13 +#define GROWL_WARN(_msg) MessageBoxW(nullptr, _msg, L"VNR Warning", MB_OK)
14 +#define GROWL_ERROR(_msg) MessageBoxW(nullptr, _msg, L"VNR Error", MB_OK)
15 +
16 +inline void GROWL_DWORD(DWORD value)
17 +{
18 + WCHAR buf[100];
19 + swprintf(buf, L"DWORD: %x", value);
20 + GROWL_MSG(buf);
21 +}
22 +
23 +inline void GROWL_DWORD2(DWORD v, DWORD v2)
24 +{
25 + WCHAR buf[100];
26 + swprintf(buf, L"DWORD2: %x,%x", v, v2);
27 + GROWL_MSG(buf);
28 +}
29 +
30 +inline void 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 + GROWL_MSG(buf);
35 +}
36 +
37 +inline void 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 + GROWL_MSG(buf);
42 +}
43 +
44 +inline void 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 + GROWL_MSG(buf);
49 +}
50 +
51 +inline void 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 + GROWL_MSG(buf);
56 +}
57 +
58 +inline void 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 + GROWL_MSG(buf);
63 +}
64 +
65 +inline void 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 + GROWL_MSG(buf);
70 +}
71 +
72 +inline void 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 + GROWL_MSG(buf);
77 +}
78 +
79 +inline void GROWL(DWORD v) { GROWL_DWORD(v); }
80 +inline void GROWL(LPCWSTR v) { GROWL_MSG(v); }
81 +inline void GROWL(LPCSTR v) { GROWL_MSG_A(v); }
82 +
83 +//#endif // GROWL_HAS_GROWL
84 +
85 +// EOF
1 +# texthook.pro
2 +# CONFIG += noqtgui dll #eha # eha will catch all exceptions, but does not work on Windows XP
3 +
4 +# DEFINES += ITH_HAS_CRT # Use native CRT
5 +
6 +# # TODO: Get rid of dependence on msvc's swprintf
7 +# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
8 +
9 +set(vnrhost_src
10 + avl_p.h
11 + config.h
12 + hookman.h
13 + host.h
14 + host_p.h
15 + settings.h
16 + textthread.h
17 + textthread_p.h
18 + hookman.cc
19 + host.cc
20 + pipe.cc
21 + textthread.cc
22 + ${PROJECT_SOURCE_DIR}/winmaker/winmaker.h
23 + ${PROJECT_SOURCE_DIR}/winmaker/winmaker.cc
24 + ${PROJECT_SOURCE_DIR}/winmutex/winmutex.h
25 +# ${PROJECT_SOURCE_DIR}/wintimer/wintimer.h
26 +# ${PROJECT_SOURCE_DIR}/wintimer/wintimer.cc
27 +# ${PROJECT_SOURCE_DIR}/wintimer/wintimerbase.cc
28 +# ${PROJECT_SOURCE_DIR}/wintimer/wintimerbase.h
29 + ${PROJECT_SOURCE_DIR}/windbg/windbg.h
30 + ${PROJECT_SOURCE_DIR}/windbg/windbg_p.h
31 + ${PROJECT_SOURCE_DIR}/windbg/inject.h
32 + ${PROJECT_SOURCE_DIR}/windbg/inject.cc
33 + ${PROJECT_SOURCE_DIR}/windbg/hijack.h
34 + ${PROJECT_SOURCE_DIR}/windbg/hijack.cc
35 + ${PROJECT_SOURCE_DIR}/windbg/util.h
36 +# ${PROJECT_SOURCE_DIR}/windbg/util.cc
37 + ${PROJECT_SOURCE_DIR}/windbg/unload.h
38 + ${PROJECT_SOURCE_DIR}/windbg/unload.cc
39 + ${PROJECT_SOURCE_DIR}/sakurakit/skdebug.h
40 +)
41 +
42 +add_library(vnrhost SHARED ${vnrhost_src})
43 +
44 +set_target_properties(vnrhost PROPERTIES LINK_FLAGS /SUBSYSTEM:WINDOWS)
45 +
46 +target_compile_options(vnrhost PRIVATE
47 +# /GR-
48 + $<$<CONFIG:Release>:>
49 + $<$<CONFIG:Debug>:>
50 +)
51 +
52 +#STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
53 +
54 +target_link_libraries(vnrhost
55 + ithsys
56 + profile
57 + ${WDK_HOME}/lib/wxp/i386/ntdll.lib
58 +)
59 +
60 +target_compile_definitions(vnrhost
61 + PRIVATE
62 + ITH_HAS_CRT
63 + _CRT_NON_CONFORMING_SWPRINTFS
64 +)
65 +
66 +install(TARGETS vnrhost RUNTIME
67 + DESTINATION .
68 + CONFIGURATIONS Release
69 +)
1 +#pragma once
2 +// avl_p.h
3 +// 8/23/2013 jichi
4 +// Branch: ITH/AVL.h, rev 133
5 +
6 +#include "config.h"
7 +#include <cstring>
8 +
9 +enum { STACK_SIZE = 32 };
10 +
11 +//#ifndef ITH_STACK
12 +//#define ITH_STACK
13 +
14 +template<class T, int stack_size>
15 +class MyStack
16 +{
17 + int index;
18 + T s[stack_size];
19 +
20 +public:
21 + MyStack(): index(0)
22 + { ::memset(s, 0, sizeof(s)); } // jichi 9/21/2013: assume T is atomic type
23 +
24 + T &back() { return s[index-1]; }
25 + int size() { return index; }
26 +
27 + void push_back(const T &e)
28 + {
29 + if (index < stack_size)
30 + s[index++]=e;
31 + }
32 +
33 + void pop_back() { index--; }
34 +
35 + T &operator[](int i) { return s[i]; }
36 +};
37 +//#endif // ITH_STACK
38 +
39 +// jichi 9/22/2013: T must be a pointer type which can be deleted
40 +template <class T, class D>
41 +struct IHFSERVICE TreeNode
42 +{
43 + //typedef TreeNode<T, D> Self;
44 + TreeNode() :
45 + Left(nullptr), Right(nullptr), Parent(nullptr)
46 + , rank(1)
47 + , factor('\0'), reserve('\0')
48 + //, key()
49 + //, data()
50 + {
51 + ::memset(&key, 0, sizeof(key)); // jcihi 9/26/2013: zero memory
52 + ::memset(&data, 0, sizeof(data)); // jcihi 9/26/2013: zero memory
53 + }
54 +
55 + TreeNode(const T &k, const D &d) :
56 + Left(nullptr), Right(nullptr), Parent(nullptr)
57 + , rank(1)
58 + , factor('\0'), reserve('\0') // jichi 9/21/2013: zero reserve
59 + , key(k)
60 + , data(d)
61 + {}
62 +
63 + TreeNode *Successor()
64 + {
65 + TreeNode *Node,
66 + *ParentNode;
67 + Node = Right;
68 + if (!Node) {
69 + Node = this;
70 + for (;;) {
71 + ParentNode = Node->Parent;
72 + if (!ParentNode)
73 + return nullptr;
74 + if (ParentNode->Left == Node)
75 + break;
76 + Node = ParentNode;
77 + }
78 + return ParentNode;
79 + }
80 + else
81 + while (Node->Left)
82 + Node = Node->Left;
83 + return Node;
84 + }
85 + TreeNode *Predecessor()
86 + {
87 + TreeNode *Node,
88 + *ParentNode;
89 + Node = Left;
90 + if (!Node) {
91 + Node = this;
92 + for(;;) {
93 + ParentNode = Node->Parent;
94 + if (!ParentNode)
95 + return nullptr;
96 + if (ParentNode->Right == Node)
97 + break;
98 + Node = ParentNode;
99 + }
100 + return ParentNode;
101 + }
102 + else
103 + while (Node->Right)
104 + Node = Node->Right;
105 + return Node;
106 + }
107 + int height()
108 + {
109 + if (!this) // jichi 9/26/2013: what?!
110 + return 0;
111 + int l = Left->height(),
112 + r = Right->height(),
113 + f = factor;
114 + if (l - r + f != 0)
115 + __debugbreak();
116 + f = l > r ? l : r;
117 + return f + 1;
118 + }
119 + TreeNode *Left,
120 + *Right,
121 + *Parent;
122 + unsigned short rank;
123 + char factor,
124 + reserve;
125 + T key;
126 + D data;
127 +};
128 +
129 +template<class T,class D>
130 +struct NodePath
131 +{
132 + NodePath() { ::memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH
133 + NodePath(TreeNode<T,D> *n, int f): Node(n), fact(f) {}
134 + TreeNode<T,D> *Node;
135 + union { char factor; int fact; };
136 +};
137 +
138 +template <class T, class D, class fComp, class fCopy, class fLength>
139 +class AVLTree
140 +{
141 +protected:
142 + TreeNode<T*, D> head;
143 + fComp fCmp;
144 + fCopy fCpy;
145 + fLength fLen;
146 +
147 +public:
148 + // - Construction -
149 + AVLTree() {}
150 +
151 + virtual ~AVLTree() { DeleteAll(); }
152 +
153 + // - Properties -
154 +
155 + TreeNode<T*, D> *TreeRoot() const { return head.Left; }
156 +
157 + // - Actions -
158 +
159 + void DeleteAll()
160 + {
161 + while (head.Left)
162 + DeleteRoot();
163 + }
164 +
165 + TreeNode<T*, D> *Insert(const T *key, const D &data)
166 + {
167 + if (head.Left) {
168 + MyStack<TreeNode<T*, D> *,STACK_SIZE> path;
169 + TreeNode<T*,D> *DownNode, *ParentNode, *BalanceNode, *TryNode, *NewNode; //P,T,S,Q
170 + ParentNode = &head;
171 + path.push_back(ParentNode);
172 + char factor,f;
173 + BalanceNode = DownNode = head.Left;
174 + for (;;) { //The first part of AVL tree insert. Just do as binary tree insert routine and record some nodes.
175 + factor = fCmp(key,DownNode->key);
176 + if (factor == 0)
177 + return DownNode; //Duplicate key. Return and do nothing.
178 + TryNode = _FactorLink(DownNode, factor);
179 + if (factor == -1)
180 + path.push_back(DownNode);
181 + if (TryNode) { //DownNode has a child.
182 + if (TryNode->factor != 0) { //Keep track of unbalance node and its parent.
183 + ParentNode = DownNode;
184 + BalanceNode = TryNode;
185 + }
186 + DownNode = TryNode;
187 + }
188 + else
189 + break; //Finished binary tree search;
190 + }
191 + while (path.size()) {
192 + path.back()->rank++;
193 + path.pop_back();
194 + }
195 + size_t sz = fLen(key) + 1;
196 + T *new_key = new T[sz];
197 + ::memset(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
198 + fCpy(new_key, key);
199 + TryNode = new TreeNode<T*, D>(new_key, data);
200 + _FactorLink(DownNode, factor) = TryNode;
201 + TryNode->Parent = DownNode;
202 + NewNode = TryNode;
203 + //Finished binary tree insert. Next to do is to modify balance factors between
204 + //BalanceNode and the new node.
205 + TreeNode<T*, D> *ModifyNode;
206 + factor = fCmp(key, BalanceNode->key);
207 + //factor=key<BalanceNode->key ? factor=-1:1; //Determine the balance factor at BalanceNode.
208 + ModifyNode = DownNode = _FactorLink(BalanceNode,factor);
209 + //ModifyNode will be the 1st child.
210 + //DownNode will travel from here to the recent inserted node (TryNode).
211 + while (DownNode != TryNode) { //Check if we reach the bottom.
212 + f = fCmp(key,DownNode->key);
213 + //f=_FactorCompare(key,DownNode->key);
214 + DownNode->factor = f;
215 + DownNode = _FactorLink(DownNode, f);//Modify balance factor and travels down.
216 + }
217 + //Finshed modifying balance factor.
218 + //Next to do is check the tree if it's unbalance and recover balance.
219 + if (BalanceNode->factor == 0) { //Tree has grown higher.
220 + BalanceNode->factor = factor;
221 + _IncreaseHeight(); //Modify balance factor and increase the height.
222 + return NewNode;
223 + }
224 + if (BalanceNode->factor + factor == 0) { //Tree has gotten more balanced.
225 + BalanceNode->factor = 0; //Set balance factor to 0.
226 + return NewNode;
227 + }
228 + //Tree has gotten out of balance.
229 + if (ModifyNode->factor == factor) //A node and its child has same factor. Single rotation.
230 + DownNode = _SingleRotation(BalanceNode, ModifyNode, factor);
231 + else //A node and its child has converse factor. Double rotation.
232 + DownNode = _DoubleRotation(BalanceNode, ModifyNode, factor);
233 + //Finished the balancing work. Set child field to the root of the new child tree.
234 + if (BalanceNode == ParentNode->Left)
235 + ParentNode->Left = DownNode;
236 + else
237 + ParentNode->Right = DownNode;
238 + return NewNode;
239 + }
240 + else { //root null?
241 + size_t sz = fLen(key) + 1;
242 + T *new_key = new T[sz];
243 + ::memset(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
244 + fCpy(new_key, key);
245 + head.Left = new TreeNode<T *, D>(new_key, data);
246 + head.rank++;
247 + _IncreaseHeight();
248 + return head.Left;
249 + }
250 + }
251 + bool Delete(T *key)
252 + {
253 + NodePath<T*,D> PathNode;
254 + MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
255 + path.push_back(NodePath<T*,D>(&head,-1));
256 + TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
257 + TryNode=head.Left;
258 + char factor;
259 + for (;;) { //Search for the
260 + if (TryNode == 0)
261 + return false; //Not found.
262 + factor = fCmp(key, TryNode->key);
263 + if (factor == 0)
264 + break; //Key found, continue to delete.
265 + //factor = _FactorCompare( key, TryNode->key );
266 + path.push_back(NodePath<T*,D>(TryNode,factor));
267 + TryNode = _FactorLink(TryNode,factor); //Move to left.
268 + }
269 + SuccNode = TryNode->Right; //Find a successor.
270 + factor = 1;
271 + if (SuccNode == 0) {
272 + SuccNode = TryNode->Left;
273 + factor = -1;
274 + }
275 + path.push_back(NodePath<T*,D>(TryNode,factor));
276 + while (SuccNode) {
277 + path.push_back(NodePath<T*,D>(SuccNode, -factor));
278 + SuccNode = _FactorLink(SuccNode,-factor);
279 + }
280 + PathNode = path.back();
281 + delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
282 + TryNode->key = PathNode.Node->key; //Replace key and data field with the successor or predecessor.
283 + PathNode.Node->key = nullptr;
284 + TryNode->data = PathNode.Node->data;
285 + path.pop_back();
286 + _FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
287 + delete PathNode.Node; //Remove the successor from the tree and release memory.
288 + PathNode = path.back();
289 + for (int i=0; i<path.size(); i++)
290 + if (path[i].factor==-1)
291 + path[i].Node->rank--;
292 + for (;;) { //Rebalance the tree along the path back to the root.
293 + if (path.size()==1) {
294 + _DecreaseHeight();
295 + break;
296 + }
297 + BalanceNode = PathNode.Node;
298 + if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurve since subtree height stays.
299 + BalanceNode->factor=-PathNode.factor;
300 + break;
301 + }
302 + if (BalanceNode->factor == PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurve.
303 + BalanceNode->factor = 0;
304 + path.pop_back();
305 + PathNode = path.back();
306 + continue;
307 + }
308 + //Node get out of balance. Here raises 3 cases.
309 + ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
310 + if (ChildNode->factor == 0) { // New case different to insert operation.
311 + TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
312 + path.pop_back();
313 + PathNode = path.back();
314 + _FactorLink(PathNode.Node, PathNode.factor) = TryNode;
315 + break;
316 + }
317 + else {
318 + if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
319 + TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
320 + else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
321 + TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
322 + }
323 + path.pop_back(); //Recurse back along the path.
324 + PathNode = path.back();
325 + _FactorLink(PathNode.Node, PathNode.factor) = TryNode;
326 + }
327 + return true;
328 + }
329 +
330 + D &operator [](T *key)
331 + { return (Insert(key,D())->data); }
332 +
333 + TreeNode<T*,D> *Search(const T *key)
334 + {
335 + TreeNode<T*,D> *Find=head.Left;
336 + char k;
337 + while (Find != 0) {//&&Find->key!=key)
338 + k = fCmp(key, Find->key);
339 + if (k == 0) break;
340 + Find = _FactorLink(Find, k);
341 + }
342 + return Find;
343 + }
344 +
345 + TreeNode<T*,D> *SearchIndex(unsigned int rank)
346 + {
347 + unsigned int r = head.rank;
348 + if (rank == -1)
349 + return 0;
350 + if (++rank>=r)
351 + return 0;
352 + TreeNode<T*,D> *n=&head;
353 + while (r!=rank) {
354 + if (rank>r) {
355 + n=n->Right;
356 + rank-=r;
357 + r=n->rank;
358 + } else {
359 + n=n->Left;
360 + r=n->rank;
361 + }
362 + }
363 + return n;
364 + }
365 +
366 + TreeNode<T*,D> *Begin()
367 + {
368 + TreeNode<T*,D> *Node = head.Left;
369 + if (Node)
370 + while (Node->Left) Node = Node->Left;
371 + return Node;
372 + }
373 +
374 + TreeNode<T*,D> *End()
375 + {
376 + TreeNode<T*,D> *Node=head.Left;
377 + if (Node)
378 + while (Node->Right) Node = Node->Right;
379 + return Node;
380 + }
381 + unsigned int Count() const { return head.rank - 1; }
382 +
383 + template <class Fn>
384 + Fn TraverseTree(Fn &f)
385 + { return TraverseTreeNode(head.Left,f); }
386 +
387 +protected:
388 + bool DeleteRoot()
389 + {
390 + NodePath<T*,D> PathNode;
391 + MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
392 + path.push_back(NodePath<T*,D>(&head,-1));
393 + TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
394 + TryNode=head.Left;
395 + char factor;
396 + SuccNode=TryNode->Right; //Find a successor.
397 + factor=1;
398 + if (SuccNode==0)
399 + {
400 + SuccNode=TryNode->Left;
401 + factor=-1;
402 + }
403 + path.push_back(NodePath<T*,D>(TryNode,factor));
404 + while (SuccNode) {
405 + path.push_back(NodePath<T*,D>(SuccNode,-factor));
406 + SuccNode=_FactorLink(SuccNode,-factor);
407 + }
408 + PathNode=path.back();
409 + delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
410 + TryNode->key=PathNode.Node->key; //Replace key and data field with the successor.
411 + PathNode.Node->key = nullptr;
412 + TryNode->data=PathNode.Node->data;
413 + path.pop_back();
414 + _FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
415 + delete PathNode.Node; //Remove the successor from the tree and release memory.
416 + PathNode=path.back();
417 + for (int i=0;i<path.size();i++)
418 + if (path[i].factor==-1)
419 + path[i].Node->rank--;
420 + for (;;) { //Rebalance the tree along the path back to the root.
421 + if (path.size() == 1) {
422 + _DecreaseHeight();
423 + break;
424 + }
425 +
426 + BalanceNode = PathNode.Node;
427 + if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurse since subtree height not changed.
428 + BalanceNode->factor=-PathNode.factor;
429 + break;
430 + }
431 + if (BalanceNode->factor==PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurse.
432 + BalanceNode->factor=0;
433 + path.pop_back();
434 + PathNode=path.back();
435 + continue;
436 + }
437 + //Node get out of balance. Here raises 3 cases.
438 + ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
439 + if (ChildNode->factor == 0) { // New case different to insert operation.
440 + TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
441 + path.pop_back();
442 + PathNode=path.back();
443 + _FactorLink(PathNode.Node, PathNode.factor) = TryNode;
444 + break;
445 + } else {
446 + if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
447 + TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
448 + else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
449 + TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
450 + }
451 + path.pop_back(); // Recurve back along the path.
452 + PathNode=path.back();
453 + _FactorLink(PathNode.Node, PathNode.factor) = TryNode;
454 + }
455 + return true;
456 + }
457 + template <class Fn>
458 + Fn TraverseTreeNode(TreeNode<T*,D> *Node, Fn &f)
459 + {
460 + if (Node) {
461 + if (Node->Left)
462 + TraverseTreeNode(Node->Left,f);
463 + f(Node);
464 + if (Node->Right)
465 + TraverseTreeNode(Node->Right,f);
466 + }
467 + return f;
468 + }
469 + TreeNode<T*,D> *_SingleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
470 + {
471 + TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
472 + _FactorLink(BalanceNode, factor) = Node;
473 + _FactorLink(ModifyNode, -factor) = BalanceNode;
474 + if (Node)
475 + Node->Parent = BalanceNode;
476 + ModifyNode->Parent = BalanceNode->Parent;
477 + BalanceNode->Parent = ModifyNode;
478 + BalanceNode->factor = ModifyNode->factor = 0; //After single rotation, set all factor of 3 node to 0.
479 + if (factor == 1)
480 + ModifyNode->rank += BalanceNode->rank;
481 + else
482 + BalanceNode->rank -= ModifyNode->rank;
483 + return ModifyNode;
484 + }
485 + TreeNode<T*,D> *_SingleRotation2(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
486 + {
487 + TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
488 + _FactorLink(BalanceNode, factor) = Node;
489 + _FactorLink(ModifyNode, -factor) = BalanceNode;
490 + if (Node) Node->Parent = BalanceNode;
491 + ModifyNode->Parent = BalanceNode->Parent;
492 + BalanceNode->Parent = ModifyNode;
493 + ModifyNode->factor = -factor;
494 + if (factor == 1)
495 + ModifyNode->rank+=BalanceNode->rank;
496 + else
497 + BalanceNode->rank-=ModifyNode->rank;
498 + return ModifyNode;
499 + }
500 + TreeNode<T*,D> *_DoubleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
501 + {
502 + TreeNode<T*,D> *DownNode = _FactorLink(ModifyNode, -factor);
503 + TreeNode<T*,D> *Node1, *Node2;
504 + Node1 = _FactorLink(DownNode, factor);
505 + Node2 = _FactorLink(DownNode, -factor);
506 + _FactorLink(ModifyNode, -factor) = Node1;
507 + _FactorLink(DownNode, factor) = ModifyNode;
508 + _FactorLink(BalanceNode, factor) = Node2;
509 + _FactorLink(DownNode, -factor) = BalanceNode;
510 + if (Node1)
511 + Node1->Parent = ModifyNode;
512 + if (Node2)
513 + Node2->Parent = BalanceNode;
514 + DownNode->Parent = BalanceNode->Parent;
515 + BalanceNode->Parent = DownNode;
516 + ModifyNode->Parent = DownNode;
517 + //Set factor according to the result.
518 + if (DownNode->factor == factor) {
519 + BalanceNode->factor = -factor;
520 + ModifyNode->factor = 0;
521 + } else if (DownNode->factor == 0)
522 + BalanceNode->factor = ModifyNode->factor = 0;
523 + else {
524 + BalanceNode->factor = 0;
525 + ModifyNode->factor = factor;
526 + }
527 + DownNode->factor = 0;
528 + if (factor==1) {
529 + ModifyNode->rank -= DownNode->rank;
530 + DownNode->rank += BalanceNode->rank;
531 + } else {
532 + DownNode->rank += ModifyNode->rank;
533 + BalanceNode->rank -= DownNode->rank;
534 + }
535 + return DownNode;
536 + }
537 +
538 + TreeNode<T*,D>* &__fastcall _FactorLink(TreeNode<T*,D> *Node, char factor)
539 + //Private helper method to retrieve child according to factor.
540 + //Return right child if factor>0 and left child otherwise.
541 + { return factor>0? Node->Right : Node->Left; }
542 +
543 + void Check()
544 + {
545 + unsigned int k = (unsigned int)head.Right;
546 + unsigned int t = head.Left->height();
547 + if (k != t)
548 + __debugbreak();
549 + }
550 +
551 + void _IncreaseHeight()
552 + {
553 + unsigned int k = (unsigned int)head.Right;
554 + head.Right = (TreeNode<T*,D>*)++k;
555 + }
556 +
557 + void _DecreaseHeight()
558 + {
559 + unsigned int k = (unsigned int)head.Right;
560 + head.Right = (TreeNode<T*,D>*)--k;
561 + }
562 +};
563 +
564 +struct SCMP
565 +{
566 + char operator()(const char *s1,const char *s2)
567 + {
568 + int t = _stricmp(s1, s2);
569 + return t == 0 ? 0 : t > 0 ? 1 :-1;
570 + }
571 +};
572 +
573 +struct SCPY { char *operator()(char *dest, const char *src) { return strcpy(dest, src); } };
574 +struct SLEN { int operator()(const char *str) { return strlen(str); } };
575 +
576 +struct WCMP
577 +{
578 + char operator()(const wchar_t *s1,const wchar_t *s2)
579 + {
580 + int t =_wcsicmp(s1, s2);
581 + return t == 0 ? 0 : t > 0 ? 1 : -1;
582 + }
583 +};
584 +
585 +struct WCPY { wchar_t *operator()(wchar_t *dest, const wchar_t *src) { return wcscpy(dest,src); } };
586 +struct WLEN { int operator()(const wchar_t *str) { return wcslen(str); } };
587 +
588 +// 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 +#define IHFAPI __stdcall
10 +#ifdef IHF
11 +# define IHFSERVICE __declspec(dllexport)
12 +#else
13 +# define IHFSERVICE __declspec(dllimport)
14 +#endif
15 +
16 +// EOF
1 +// hookman.cc
2 +// 8/24/2013 jichi
3 +// Branch IHF/HookManager.cpp, rev 133
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:4146) // C4146: unary minus operator applied to unsigned type
9 +#endif // _MSC_VER
10 +
11 +#include "hookman.h"
12 +#include "vnrhook/include/const.h"
13 +#include "vnrhook/include/defs.h"
14 +#include "vnrhook/include/types.h"
15 +#include "ithsys/ithsys.h"
16 +#include <stdio.h>
17 +//#include <emmintrin.h>
18 +#include "profile/Profile.h"
19 +#include "profile/pugixml.hpp"
20 +#include "profile/misc.h"
21 +
22 +#define DEBUG "vnrhost/hookman.cc"
23 +#include "sakurakit/skdebug.h"
24 +
25 +namespace { // unnamed
26 +//enum { MAX_ENTRY = 0x40 };
27 +
28 +#define HM_LOCK win_mutex_lock<HookManager::mutex_type> d_locker(hmcs) // Synchronized scope for accessing private data
29 +// jichi 9/23/2013: wine deficenciy on mapping sections
30 +// Whe set to false, do not map sections.
31 +//bool ith_has_section = true;
32 +
33 +// jichi 9/28/2013: Remove ConsoleOutput from available hooks
34 +//LPWSTR HookNameInitTable[]={ L"ConsoleOutput" , HOOK_FUN_NAME_LIST };
35 +//LPCWSTR HookNameInitTable[] = {HOOK_FUN_NAME_LIST};
36 +//LPVOID DefaultHookAddr[HOOK_FUN_COUNT];
37 +
38 +//BYTE null_buffer[4]={0,0,0,0};
39 +//BYTE static_small_buffer[0x100];
40 +//DWORD zeros[4]={0,0,0,0};
41 +//WCHAR user_entry[0x40];
42 +
43 +bool GetProcessPath(HANDLE hProc, __out LPWSTR path)
44 +{
45 + PROCESS_BASIC_INFORMATION info;
46 + LDR_DATA_TABLE_ENTRY entry;
47 + PEB_LDR_DATA ldr;
48 + PEB peb;
49 + if (NT_SUCCESS(NtQueryInformationProcess(hProc, ProcessBasicInformation, &info, sizeof(info), 0)))
50 + if (info.PebBaseAddress)
51 + if (NT_SUCCESS(NtReadVirtualMemory(hProc, info.PebBaseAddress, &peb,sizeof(peb), 0)))
52 + if (NT_SUCCESS(NtReadVirtualMemory(hProc, peb.Ldr, &ldr, sizeof(ldr), 0)))
53 + if (NT_SUCCESS(NtReadVirtualMemory(hProc, (LPVOID)ldr.InLoadOrderModuleList.Flink,
54 + &entry, sizeof(LDR_DATA_TABLE_ENTRY), 0)))
55 + if (NT_SUCCESS(NtReadVirtualMemory(hProc, entry.FullDllName.Buffer,
56 + path, MAX_PATH * 2, 0)))
57 + return true;
58 + path = L"";
59 + return false;
60 +}
61 +
62 +bool GetProcessPath(DWORD pid, __out LPWSTR path)
63 +{
64 + CLIENT_ID id;
65 + OBJECT_ATTRIBUTES oa = {};
66 + HANDLE hProc;
67 + id.UniqueProcess = pid;
68 + id.UniqueThread = 0;
69 + oa.uLength = sizeof(oa);
70 + if (NT_SUCCESS(NtOpenProcess(&hProc , PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, &oa, &id))) {
71 + bool flag = GetProcessPath(hProc, path);
72 + NtClose(hProc);
73 + return flag;
74 + }
75 + path = L"";
76 + return false;
77 +}
78 +
79 +} // unnamed namespace
80 +
81 +HookManager *man; // jichi 9/22/2013: initialized in main
82 +//BitMap* pid_map;
83 +DWORD clipboard_flag,
84 + split_time,
85 + repeat_count,
86 + global_filter,
87 + cyclic_remove;
88 +
89 +DWORD GetHookName(LPSTR str, DWORD pid, DWORD hook_addr, DWORD max)
90 +{
91 + if (!pid)
92 + return 0;
93 +
94 + DWORD len = 0;
95 + max--; //for '\0' magic marker.
96 +
97 + //if (pid == 0) {
98 + // len = wcslen(HookNameInitTable[0]);
99 + // if (len >= max)
100 + // len = max;
101 + // memcpy(str, HookNameInitTable[0], len << 1);
102 + // str[len] = 0;
103 + // return len;
104 + //}
105 +
106 + //::man->LockProcessHookman(pid);
107 + ProcessRecord *pr = ::man->GetProcessRecord(pid);
108 + if (!pr)
109 + return 0;
110 + NtWaitForSingleObject(pr->hookman_mutex, 0, 0);
111 + Hook *hks = (Hook *)pr->hookman_map;
112 + for (int i = 0; i < MAX_HOOK; i++)
113 + if (hks[i].Address() == hook_addr) {
114 + len = hks[i].NameLength();
115 + if (len >= max)
116 + len = max;
117 + NtReadVirtualMemory(pr->process_handle, hks[i].Name(), str, len, &len);
118 + if (str[len - 1] == 0)
119 + len--;
120 + else
121 + str[len] = 0;
122 + break;
123 + }
124 +
125 + // jichi 9/27/2013: The hook man should be consistent with the one defined in vnrcli
126 + //Hook *h = (Hook *)hks;
127 + //for (int i = 0; i < MAX_HOOK; i++)
128 + // if (!h[i].hook_name)
129 + // break;
130 + // else {
131 + // const Hook &hi = h[i];
132 + // wchar_t buffer[1000];
133 + // DWORD len = hi.NameLength();
134 + // NtReadVirtualMemory(pr->process_handle, hi.hook_name, buffer, len << 1, &len);
135 + // buffer[len] = 0;
136 + // ITH_MSG(buffer);
137 + // }
138 +
139 + NtReleaseMutant(pr->hookman_mutex, 0);
140 + //::man->UnlockProcessHookman(pid);
141 + return len;
142 +}
143 +
144 +// 7/2/2015 jichi: This function is not used and removed
145 +//int GetHookNameByIndex(LPSTR str, DWORD pid, DWORD index)
146 +//{
147 +// if (!pid)
148 +// return 0;
149 +//
150 +// //if (pid == 0) {
151 +// // wcscpy(str, HookNameInitTable[0]);
152 +// // return wcslen(HookNameInitTable[0]);
153 +// //}
154 +// DWORD len = 0;
155 +// //::man->LockProcessHookman(pid);
156 +// ProcessRecord *pr = ::man->GetProcessRecord(pid);
157 +// if (!pr)
158 +// return 0;
159 +// //NtWaitForSingleObject(pr->hookman_mutex,0,0); //already locked
160 +// Hook *hks = (Hook *)pr->hookman_map;
161 +// if (hks[index].Address()) {
162 +// NtReadVirtualMemory(pr->process_handle, hks[index].Name(), str, hks[index].NameLength() << 1, &len);
163 +// len = hks[index].NameLength();
164 +// }
165 +// //NtReleaseMutant(pr->hookman_mutex,0);
166 +// return len;
167 +//}
168 +
169 +//int GetHookString(LPWSTR str, DWORD pid, DWORD hook_addr, DWORD status)
170 +//{
171 +// LPWSTR begin=str;
172 +// str+=swprintf(str,L"%4d:0x%08X:",pid,hook_addr);
173 +// str+=GetHookName(str,pid,hook_addr);
174 +// return str-begin;
175 +//}
176 +
177 +void ThreadTable::SetThread(DWORD num, TextThread *ptr)
178 +{
179 + int number = num;
180 + if (number >= size) {
181 + while (number >= size)
182 + size <<= 1;
183 + TextThread **temp;
184 + //if (size < 0x10000) {
185 + temp = new TextThread*[size];
186 + if (size > used)
187 + ::memset(temp, 0, (size - used) * sizeof(TextThread *)); // jichi 9/21/2013: zero memory
188 + memcpy(temp, storage, used * sizeof(TextThread *));
189 + //}
190 + delete[] storage;
191 + storage = temp;
192 + }
193 + storage[number] = ptr;
194 + if (ptr == nullptr) {
195 + if (number == used - 1)
196 + while (storage[used - 1] == 0)
197 + used--;
198 + } else if (number >= used)
199 + used = number + 1;
200 +}
201 +
202 +TextThread *ThreadTable::FindThread(DWORD number)
203 +{ return number <= (DWORD)used ? storage[number] : nullptr; }
204 +
205 +static const char sse_table_eq[0x100]={
206 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
207 + -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
208 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
209 + -1,-1,-1,-1, 1,1,1,1, -1,-1,-1,-1, 1,1,1,1, //3, compare 3
210 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
211 + -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
212 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
213 + -1,-1,-1,-1, -1,-1,-1,-1, 1,1,1,1, 1,1,1,1, //7, compare 4
214 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
215 + -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
216 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
217 + -1,-1,-1,-1, 1,1,1,1, -1,-1,-1,-1, 1,1,1,1, //3, compare 3
218 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
219 + -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
220 + -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
221 + 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 //f, equal
222 +};
223 +char original_cmp(const ThreadParameter *t1, const ThreadParameter *t2)
224 +{
225 + //Q_ASSERT(t1 && t2);
226 + int t = t1->pid - t2->pid;
227 + if (t == 0) {
228 + t = t1->hook - t2->hook;
229 + if (t == 0) {
230 + t = t1->retn - t2->retn;
231 + if (t == 0) {
232 + t = t1->spl-t2->spl;
233 + if (t == 0) return 0;
234 + return t1->spl > t2->spl ? 1 : -1;
235 + }
236 + else return t1->retn > t2->retn ? 1 : -1;
237 + }
238 + else return t1->hook > t2->hook ? 1: -1;
239 + }
240 + else return t1->pid > t2->pid ? 1 : -1;
241 + //return t>0?1:-1;
242 +}
243 +char TCmp::operator()(const ThreadParameter* t1, const ThreadParameter* t2)
244 + //SSE speed up. Compare four integers in const time without branching.
245 + //The AVL tree branching operation needs 2 bit of information.
246 + //One bit for equality and one bit for "less than" or "greater than".
247 +
248 +{
249 + union{__m128 m0;__m128i i0;};
250 + union{__m128 m1;__m128i i1;};
251 + union{__m128 m2;__m128i i2;};
252 + int k0,k1;
253 + i1 = _mm_loadu_si128((const __m128i*)t1);
254 + i2 = _mm_loadu_si128((const __m128i*)t2);
255 + i0 = _mm_cmpgt_epi32(i1,i2);
256 + k0 = _mm_movemask_ps(m0);
257 + i1 = _mm_cmpeq_epi32(i1,i2);
258 + k1 = _mm_movemask_ps(m1);
259 + return sse_table_eq[k1*16+k0];
260 +}
261 +void TCpy::operator()(ThreadParameter* t1, const ThreadParameter* t2)
262 +{ memcpy(t1,t2,sizeof(ThreadParameter)); }
263 +
264 +int TLen::operator()(const ThreadParameter* t) { return 0; }
265 +
266 +#define NAMED_PIPE_DISCONNECT 1
267 +//Class member of HookManger
268 +HookManager::HookManager() :
269 + // jichi 9/21/2013: Zero memory
270 + //CRITICAL_SECTION hmcs;
271 + current(nullptr)
272 + , create(nullptr)
273 + , remove(nullptr)
274 + , reset(nullptr)
275 + , attach(nullptr)
276 + , detach(nullptr)
277 + , hook(nullptr)
278 + , current_pid(0)
279 + , thread_table(nullptr)
280 + , destroy_event(nullptr)
281 + , register_count(0)
282 + , new_thread_number(0)
283 +{
284 + // jichi 9/21/2013: zero memory
285 + ::memset(record, 0, sizeof(record));
286 + ::memset(text_pipes, 0, sizeof(text_pipes));
287 + ::memset(cmd_pipes, 0, sizeof(cmd_pipes));
288 + ::memset(recv_threads, 0, sizeof(recv_threads));
289 +
290 + head.key = new ThreadParameter;
291 + head.key->pid = 0;
292 + head.key->hook = -1;
293 + head.key->retn = -1;
294 + head.key->spl = -1;
295 + head.data = 0;
296 + thread_table = new ThreadTable; // jichi 9/26/2013: zero memory in ThreadTable
297 +
298 + TextThread *entry = new TextThread(0, -1,-1,-1, new_thread_number++); // jichi 9/26/2013: zero memory in TextThread
299 + thread_table->SetThread(0, entry);
300 + SetCurrent(entry);
301 + entry->Status() |= USING_UNICODE;
302 + //texts->SetUnicode(true);
303 + //entry->AddToCombo();
304 + //entry->ComboSelectCurrent();
305 +
306 + //if (background==0) entry->AddToStore((BYTE*)BackgroundMsg,wcslen(BackgroundMsg)<<1,0,1);
307 +
308 + //InitializeCriticalSection(&hmcs);
309 + destroy_event = IthCreateEvent(0, 0, 0);
310 +}
311 +
312 +HookManager::~HookManager()
313 +{
314 + //LARGE_INTEGER timeout={-1000*1000,-1};
315 + //IthBreak();
316 + NtWaitForSingleObject(destroy_event, 0, 0);
317 + NtClose(destroy_event);
318 + NtClose(cmd_pipes[0]);
319 + NtClose(recv_threads[0]);
320 + delete thread_table;
321 + delete head.key;
322 + //DeleteCriticalSection(&hmcs);
323 +}
324 +
325 +TextThread *HookManager::FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split)
326 +{
327 + if (pid == 0)
328 + return thread_table->FindThread(0);
329 + ThreadParameter tp = {pid, hook, retn, split};
330 + TreeNode<ThreadParameter *,DWORD> *node = Search(&tp);
331 + return node ? thread_table->FindThread(node->data) : nullptr;
332 +}
333 +
334 +TextThread *HookManager::FindSingle(DWORD number)
335 +{ return (number & 0x80008000) ? nullptr : thread_table->FindThread(number); }
336 +
337 +void HookManager::DetachProcess(DWORD pid) {}
338 +
339 +void HookManager::SetCurrent(TextThread *it)
340 +{
341 + if (current)
342 + current->Status() &= ~CURRENT_SELECT;
343 + current = it;
344 + if (it)
345 + it->Status() |= CURRENT_SELECT;
346 +}
347 +void HookManager::SelectCurrent(DWORD num)
348 +{
349 + if (TextThread *st = FindSingle(num)) {
350 + SetCurrent(st);
351 + if (reset)
352 + reset(st);
353 + //st->ResetEditText();
354 + }
355 +}
356 +void HookManager::RemoveSingleHook(DWORD pid, DWORD addr)
357 +{
358 + HM_LOCK;
359 + //ConsoleOutput("vnrhost:RemoveSingleHook: lock");
360 + //EnterCriticalSection(&hmcs);
361 + DWORD max = thread_table->Used();
362 + bool flag = false;
363 + for (DWORD i = 1; i <= max; i++)
364 + if (TextThread *it = thread_table->FindThread(i))
365 + if (it->PID() == pid && it->Addr() == addr) {
366 + flag |= (it == current);
367 + //flag|=it->RemoveFromCombo();
368 + thread_table->SetThread(i, 0);
369 + if (it->Number() < new_thread_number)
370 + new_thread_number = it->Number();
371 + Delete(it->GetThreadParameter());
372 + if (remove)
373 + remove(it);
374 + delete it;
375 + }
376 +
377 + for (DWORD i = 0; i <= max; i++)
378 + if (TextThread *it = thread_table->FindThread(i))
379 + if (it->Link() && thread_table->FindThread(it->LinkNumber()) == nullptr) {
380 + it->LinkNumber() = -1;
381 + it->Link() = nullptr;
382 + }
383 +
384 + if (flag) {
385 + current = nullptr;
386 + DWORD number = head.Left ? head.Left->data : 0;
387 + SetCurrent(thread_table->FindThread(number));
388 + if (reset && current)
389 + reset(current);
390 + //it->ResetEditText();
391 + }
392 + //LeaveCriticalSection(&hmcs);
393 + //ConsoleOutput("vnrhost:RemoveSingleHook: unlock");
394 +}
395 +void HookManager::RemoveSingleThread(DWORD number)
396 +{
397 + if (number == 0)
398 + return;
399 + HM_LOCK;
400 + //ConsoleOutput("vnrhost:RemoveSingleThread: lock");
401 + //EnterCriticalSection(&hmcs);
402 + if (TextThread *it = thread_table->FindThread(number)) {
403 + thread_table->SetThread(number, 0);
404 + Delete(it->GetThreadParameter());
405 + if (remove)
406 + remove(it);
407 + bool flag = (it == current);
408 + if (it->Number() < new_thread_number)
409 + new_thread_number = it->Number();
410 + delete it;
411 + for (int i = 0; i <= thread_table->Used(); i++)
412 + if (TextThread *t = thread_table->FindThread(i))
413 + if (t->LinkNumber() == number) {
414 + t->Link() = 0;
415 + t->LinkNumber() = -1;
416 + }
417 +
418 + if (flag) {
419 + current = nullptr;
420 + number = head.Left ? head.Left->data : 0;
421 + SetCurrent(thread_table->FindThread(number));
422 + if (reset && current)
423 + reset(current);
424 + //it->ResetEditText();
425 + }
426 + }
427 + //LeaveCriticalSection(&hmcs);
428 + //ConsoleOutput("vnrhost:RemoveSingleThread: unlock");
429 +}
430 +
431 +void HookManager::RemoveProcessContext(DWORD pid)
432 +{
433 + HM_LOCK;
434 + bool flag = false;
435 + //ConsoleOutput("vnrhost:RemoveProcessContext: lock");
436 + //EnterCriticalSection(&hmcs);
437 + for (int i = 1; i < thread_table->Used(); i++)
438 + if (TextThread *it = thread_table->FindThread(i))
439 + if (it->PID() == pid) {
440 + Delete(it->GetThreadParameter());
441 + //if (false == Delete(it->GetThreadParameter())) {
442 + // // jichi 11/26/2013: Remove debugging instructions
443 + // //if (debug)
444 + // // __asm int 3
445 + //}
446 + flag |= (it == current);
447 + //flag|=it->RemoveFromCombo();
448 + if (it->Number() <new_thread_number)
449 + new_thread_number = it->Number();
450 + thread_table->SetThread(i,0);
451 + if (remove)
452 + remove(it);
453 + delete it;
454 + }
455 +
456 + for (int i = 0; i < thread_table->Used(); i++)
457 + if (TextThread *it=thread_table->FindThread(i))
458 + if (it->Link() && thread_table->FindThread(it->LinkNumber()) == nullptr) {
459 + it->LinkNumber()=-1;
460 + it->Link() = nullptr;
461 + }
462 +
463 + if (flag) {
464 + current = nullptr;
465 + DWORD number = head.Left ? head.Left->data : 0;
466 + SetCurrent(thread_table->FindThread(number));
467 + if (reset && current)
468 + reset(current);
469 + //if (it) it->ResetEditText();
470 + }
471 + //LeaveCriticalSection(&hmcs);
472 + //ConsoleOutput("vnrhost:RemoveProcessContext: unlock");
473 +}
474 +void HookManager::RegisterThread(TextThread* it, DWORD num)
475 +{ thread_table->SetThread(num, it); }
476 +
477 +void HookManager::RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread)
478 +{
479 + text_pipes[register_count] = text;
480 + cmd_pipes[register_count] = cmd;
481 + recv_threads[register_count] = thread;
482 + register_count++;
483 + if (register_count == 1)
484 + NtSetEvent(destroy_event, 0);
485 + else
486 + NtClearEvent(destroy_event);
487 +}
488 +void HookManager::RegisterProcess(DWORD pid, DWORD hookman, DWORD module)
489 +{
490 + HM_LOCK;
491 + wchar_t str[0x40],
492 + path[MAX_PATH];
493 + //pid_map->Set(pid>>2);
494 + //ConsoleOutput("vnrhost:RegisterProcess: lock");
495 + //EnterCriticalSection(&hmcs);
496 + record[register_count - 1].pid_register = pid;
497 + record[register_count - 1].hookman_register = hookman;
498 + record[register_count - 1].module_register = module;
499 + //record[register_count - 1].engine_register = engine;
500 + swprintf(str, ITH_SECTION_ L"%d", pid);
501 + HANDLE hSection = IthCreateSection(str, HOOK_SECTION_SIZE, PAGE_READONLY);
502 + LPVOID map = nullptr;
503 + //DWORD map_size = 0x1000;
504 + DWORD map_size = HOOK_SECTION_SIZE / 2; // jichi 1/16/2015: Changed to half to hook section size
505 + //if (::ith_has_section)
506 + NtMapViewOfSection(hSection, NtCurrentProcess(),
507 + &map, 0, map_size, 0, &map_size, ViewUnmap, 0,
508 + PAGE_READONLY);
509 +
510 + record[register_count - 1].hookman_section = hSection;
511 + record[register_count - 1].hookman_map = map;
512 +
513 + HANDLE hProc;
514 + CLIENT_ID id;
515 + id.UniqueProcess = pid;
516 + id.UniqueThread = 0;
517 + OBJECT_ATTRIBUTES oa = {};
518 + oa.uLength = sizeof(oa);
519 + if (NT_SUCCESS(NtOpenProcess(&hProc,
520 + PROCESS_QUERY_INFORMATION|
521 + PROCESS_CREATE_THREAD|
522 + PROCESS_VM_READ|
523 + PROCESS_VM_WRITE|
524 + PROCESS_VM_OPERATION,
525 + &oa,&id)))
526 + record[register_count - 1].process_handle = hProc;
527 + else {
528 + DOUT("failed to open process");
529 + //::man->AddConsoleOutput(ErrorOpenProcess);
530 + //LeaveCriticalSection(&hmcs);
531 + //ConsoleOutput("vnrhost:RegisterProcess: unlock");
532 + return;
533 + }
534 +
535 + // jichi 9/27/2013: The hook man should be consistent with the one defined in vnrcli
536 + //Hook *h = (Hook *)map;
537 + //for (int i = 0; i < MAX_HOOK; i++)
538 + // if (!h[i].hook_name)
539 + // break;
540 + // else {
541 + // const Hook &hi = h[i];
542 + // wchar_t buffer[1000];
543 + // DWORD len = hi.NameLength();
544 + // NtReadVirtualMemory(hProc, hi.hook_name, buffer, len << 1, &len);
545 + // buffer[len] = 0;
546 + // ITH_MSG(buffer);
547 + // }
548 +
549 + swprintf(str, ITH_HOOKMAN_MUTEX_ L"%d", pid);
550 + record[register_count - 1].hookman_mutex = IthOpenMutex(str);
551 + if (!GetProcessPath(pid, path))
552 + path[0] = 0;
553 + //swprintf(str,L"%.4d:%s", pid, wcsrchr(path, L'\\') + 1); // jichi 9/25/2013: this is useless?
554 + current_pid = pid;
555 + if (attach)
556 + attach(pid);
557 + //LeaveCriticalSection(&hmcs);
558 + //ConsoleOutput("vnrhost:RegisterProcess: unlock");
559 +}
560 +
561 +void HookManager::UnRegisterProcess(DWORD pid)
562 +{
563 + HM_LOCK;
564 + //ConsoleOutput("vnrhost:UnRegisterProcess: lock");
565 + //EnterCriticalSection(&hmcs);
566 +
567 + int i;
568 + for (i = 0; i < MAX_REGISTER; i++)
569 + if(record[i].pid_register == pid)
570 + break;
571 +
572 + if (i < MAX_REGISTER) {
573 + NtClose(text_pipes[i]);
574 + NtClose(cmd_pipes[i]);
575 + NtClose(recv_threads[i]);
576 + NtClose(record[i].hookman_mutex);
577 +
578 + //if (::ith_has_section)
579 + NtUnmapViewOfSection(NtCurrentProcess(), record[i].hookman_map);
580 + //else
581 + // delete[] record[i].hookman_map;
582 +
583 + NtClose(record[i].process_handle);
584 + NtClose(record[i].hookman_section);
585 +
586 + for (; i < MAX_REGISTER; i++) {
587 + record[i] = record[i+1];
588 + text_pipes[i] = text_pipes[i+1];
589 + cmd_pipes[i] = cmd_pipes[i+1];
590 + recv_threads[i] = recv_threads[i+1];
591 + if (text_pipes[i] == 0)
592 + break;
593 + }
594 + register_count--;
595 + if (current_pid == pid)
596 + current_pid = register_count ? record[0].pid_register : 0;
597 + RemoveProcessContext(pid);
598 + }
599 + //pid_map->Clear(pid>>2);
600 +
601 + if (register_count == 1)
602 + NtSetEvent(destroy_event, 0);
603 + //LeaveCriticalSection(&hmcs);
604 + //ConsoleOutput("vnrhost:UnRegisterProcess: unlock");
605 + if (detach)
606 + detach(pid);
607 +}
608 +
609 +// jichi 9/28/2013: I do not need this
610 +//void HookManager::SetName(DWORD type)
611 +//{
612 +// WCHAR c;
613 +// if (type & PRINT_DWORD)
614 +// c = L'H';
615 +// else if (type & USING_UNICODE) {
616 +// if (type & STRING_LAST_CHAR)
617 +// c = L'L';
618 +// else if (type & USING_STRING)
619 +// c = L'Q';
620 +// else
621 +// c = L'W';
622 +// } else {
623 +// if (type & USING_STRING)
624 +// c = L'S';
625 +// else if (type & BIG_ENDIAN)
626 +// c = L'A';
627 +// else
628 +// c = L'B';
629 +// }
630 +// //swprintf(user_entry,L"UserHook%c",c);
631 +//}
632 +
633 +void HookManager::AddLink(WORD from, WORD to)
634 +{
635 + HM_LOCK;
636 + //bool flag=false;
637 + //ConsoleOutput("vnrhost:AddLink: lock");
638 + //EnterCriticalSection(&hmcs);
639 + TextThread *from_thread = thread_table->FindThread(from),
640 + *to_thread = thread_table->FindThread(to);
641 + if (to_thread && from_thread) {
642 + if (from_thread->GetThreadParameter()->pid != to_thread->GetThreadParameter()->pid)
643 + DOUT("link to different process");
644 + else if (from_thread->Link()==to_thread)
645 + DOUT("link already exists");
646 + else if (to_thread->CheckCycle(from_thread))
647 + DOUT("cyclic link");
648 + else {
649 + from_thread->Link()=to_thread;
650 + from_thread->LinkNumber()=to;
651 + DOUT("thread linked");
652 + if (addRemoveLink)
653 + addRemoveLink(from_thread);
654 + //WCHAR str[0x40];
655 + //swprintf(str,FormatLink,from,to);
656 + //AddConsoleOutput(str);
657 + }
658 + } else
659 + DOUT("error link");
660 + //else
661 + // AddConsoleOutput(ErrorLink);
662 + //LeaveCriticalSection(&hmcs);
663 + //ConsoleOutput("vnrhost:AddLink: unlock");
664 +}
665 +void HookManager::UnLink(WORD from)
666 +{
667 + HM_LOCK;
668 + //bool flag=false;
669 + //ConsoleOutput("vnrhost:UnLink: lock");
670 + //EnterCriticalSection(&hmcs);
671 + if (TextThread *from_thread = thread_table->FindThread(from)) {
672 + from_thread->Link() = nullptr;
673 + from_thread->LinkNumber() = 0xffff;
674 + DOUT("link deleted");
675 + if (addRemoveLink)
676 + addRemoveLink(from_thread);
677 + }
678 + //else // jichi 12/25/2013: This could happen when the game exist
679 + // ConsoleOutput("vnrhost:UnLink: thread does not exist");
680 + //LeaveCriticalSection(&hmcs);
681 + //ConsoleOutput("vnrhost:UnLink: unlock");
682 +}
683 +void HookManager::UnLinkAll(WORD from)
684 +{
685 + HM_LOCK;
686 + //bool flag=false;
687 + //ConsoleOutput("vnrhost:UnLinkAll: lock");
688 + //EnterCriticalSection(&hmcs);
689 + if (TextThread *from_thread = thread_table->FindThread(from)) {
690 + from_thread->UnLinkAll();
691 + DOUT("link deleted");
692 + }
693 + //else // jichi 12/25/2013: This could happen after the process exists
694 + // ConsoleOutput("vnrhost:UnLinkAll: thread not exist");
695 + //AddConsoleOutput(L"Link deleted.");
696 + //} else
697 + // AddConsoleOutput(L"Thread not exist.");
698 + //LeaveCriticalSection(&hmcs);
699 + //ConsoleOutput("vnrhost:UnLinkAll: unlock");
700 +}
701 +
702 +void HookManager::DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD spl, int len, bool space)
703 +{
704 + // jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need
705 + if (!text || !pid || (len <= 0 && !space))
706 + return;
707 + HM_LOCK;
708 + //bool flag=false;
709 + ThreadParameter tp = {pid, hook, retn, spl};
710 + //ConsoleOutput("vnrhost:DispatchText: lock");
711 + //EnterCriticalSection(&hmcs);
712 + TextThread *it;
713 + //`try {
714 + if (TreeNode<ThreadParameter *,DWORD> *in = Search(&tp)) {
715 + DWORD number = in->data;
716 + it = thread_table->FindThread(number);
717 + } else if (IsFull()) { // jichi 1/16/2015: Skip adding threads when full
718 + static bool once = true; // output only once
719 + if (once) {
720 + once = false;
721 + DOUT("so many new threads, skip");
722 + }
723 + return;
724 + } else { // New thread
725 + Insert(&tp, new_thread_number);
726 + it = new TextThread(pid, hook, retn, spl, new_thread_number);
727 + RegisterThread(it, new_thread_number);
728 + DOUT("found new thread");
729 + char entstr[0x200];
730 + it->GetEntryString(entstr);
731 + DOUT(entstr);
732 + while (thread_table->FindThread(++new_thread_number));
733 + if (create)
734 + create(it);
735 + }
736 + if (it)
737 + it->AddText(text, len, false, space); // jichi 10/27/2013: new line is false
738 + //LeaveCriticalSection(&hmcs);
739 + //ConsoleOutput("vnrhost:DispatchText: unlock");
740 + //} catch (...) {
741 + // // ignored
742 + //}
743 +}
744 +
745 +void HookManager::AddConsoleOutput(LPCWSTR text)
746 +{
747 + if (text) {
748 + int len = wcslen(text) << 1;
749 + TextThread *console = thread_table->FindThread(0);
750 + //EnterCriticalSection(&hmcs);
751 + console->AddText((BYTE*)text,len,false,true);
752 + console->AddText((BYTE*)L"\r\n",4,false,true);
753 + //LeaveCriticalSection(&hmcs);
754 + }
755 +}
756 +
757 +void HookManager::ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD spl)
758 +{
759 + HM_LOCK;
760 + //bool flag=false;
761 + //ConsoleOutput("vnrhost:ClearText: lock");
762 + //EnterCriticalSection(&hmcs);
763 + ThreadParameter tp = {pid, hook, retn, spl};
764 + if (TreeNode<ThreadParameter *, DWORD> *in = Search(&tp))
765 + if (TextThread *it = thread_table->FindThread(in->data)) {
766 + it->Reset();
767 + //SetCurrent(it);
768 + if (reset)
769 + reset(it);
770 + //it->ResetEditText();
771 + }
772 +
773 + //LeaveCriticalSection(&hmcs);
774 + //ConsoleOutput("vnrhost:ClearText: unlock");
775 +}
776 +void HookManager::ClearCurrent()
777 +{
778 + HM_LOCK;
779 + //ConsoleOutput("vnrhost:ClearCurrent: lock");
780 + //EnterCriticalSection(&hmcs);
781 + if (current) {
782 + current->Reset();
783 + if (reset)
784 + reset(current);
785 + }
786 + //current->ResetEditText();
787 + //LeaveCriticalSection(&hmcs);
788 + //ConsoleOutput("vnrhost:ClearCurrent: unlock");
789 +}
790 +void HookManager::ResetRepeatStatus()
791 +{
792 + HM_LOCK;
793 + //ConsoleOutput("vnrhost:ResetRepeatStatus: lock");
794 + //EnterCriticalSection(&hmcs);
795 + for (int i = 1; i < thread_table->Used(); i++)
796 + if (TextThread *it = thread_table->FindThread(i))
797 + it->ResetRepeatStatus();
798 +
799 + //LeaveCriticalSection(&hmcs);
800 + //ConsoleOutput("vnrhost:ResetRepeatStatus: unlock");
801 +}
802 +//void HookManager::LockHookman(){ EnterCriticalSection(&hmcs); }
803 +//void HookManager::UnlockHookman(){ LeaveCriticalSection(&hmcs); }
804 +
805 +/*void HookManager::SetProcessEngineType(DWORD pid, DWORD type)
806 +{
807 + int i;
808 + for (i=0;i<MAX_REGISTER;i++)
809 + if (record[i].pid_register==pid) break;
810 + if (i<MAX_REGISTER)
811 + {
812 + record[i].engine_register|=type;
813 + }
814 +}*/
815 +
816 +ProcessRecord *HookManager::GetProcessRecord(DWORD pid)
817 +{
818 + HM_LOCK;
819 + //EnterCriticalSection(&hmcs);
820 + for (int i = 0; i < MAX_REGISTER; i++)
821 + if (record[i].pid_register == pid)
822 + return record + i;
823 + return nullptr;
824 + //ProcessRecord *pr = i < MAX_REGISTER ? record + i : nullptr;
825 + //LeaveCriticalSection(&hmcs);
826 + //return pr;
827 +}
828 +
829 +DWORD HookManager::GetProcessIDByPath(LPCWSTR str)
830 +{
831 + WCHAR path[MAX_PATH];
832 + for (int i = 0; i < 8 && record[i].process_handle; i++) {
833 + ::GetProcessPath(record[i].process_handle, path);
834 + if (_wcsicmp(path,str) == 0)
835 + return record[i].pid_register;
836 + }
837 + return 0;
838 +}
839 +
840 +DWORD HookManager::GetCurrentPID() { return current_pid; }
841 +
842 +HANDLE HookManager::GetCmdHandleByPID(DWORD pid)
843 +{
844 + HM_LOCK;
845 + //EnterCriticalSection(&hmcs);
846 + for (int i = 0; i < MAX_REGISTER; i++)
847 + if (record[i].pid_register == pid)
848 + return cmd_pipes[i];
849 + return nullptr;
850 + //HANDLE h = i < MAX_REGISTER ? cmd_pipes[i] : 0;
851 + //LeaveCriticalSection(&hmcs);
852 + //return h;
853 +}
854 +
855 +MK_BASIC_TYPE(DWORD)
856 +MK_BASIC_TYPE(LPVOID)
857 +
858 +//DWORD Hash(LPCWSTR module, int length)
859 +//{
860 +// bool flag = (length==-1);
861 +// DWORD hash = 0;
862 +// for (;*module && (flag || length--); module++)
863 +// hash = ((hash>>7)|(hash<<25)) + *module;
864 +// return hash;
865 +//}
866 +
867 +DWORD GetCurrentPID() { return ::man->GetCurrentPID(); }
868 +
869 +HANDLE GetCmdHandleByPID(DWORD pid) { return ::man->GetCmdHandleByPID(pid); }
870 +
871 +//void AddLink(WORD from, WORD to) { ::man->AddLink(from, to); }
872 +
873 +// jichi 9/27/2013: Unparse to hook parameters /H code
874 +void GetCode(const HookParam &hp, LPWSTR buffer, DWORD pid)
875 +{
876 + WCHAR c;
877 + LPWSTR ptr = buffer;
878 + // jichi 12/7/2014: disabled
879 + //if (hp.type&PRINT_DWORD)
880 + // c = L'H';
881 + if (hp.type&USING_UNICODE) {
882 + if (hp.type&USING_STRING)
883 + c = L'Q';
884 + else if (hp.type&STRING_LAST_CHAR)
885 + c = L'L';
886 + else
887 + c = L'W';
888 + } else {
889 + if (hp.type&USING_STRING)
890 + c = L'S';
891 + else if (hp.type&BIG_ENDIAN)
892 + c = L'A';
893 + else if (hp.type&STRING_LAST_CHAR)
894 + c = L'E';
895 + else
896 + c = L'B';
897 + }
898 + ptr += swprintf(ptr, L"/H%c",c);
899 + if (hp.type & NO_CONTEXT)
900 + *ptr++ = L'N';
901 + if (hp.offset>>31)
902 + ptr += swprintf(ptr, L"-%X",-(hp.offset+4));
903 + else
904 + ptr += swprintf(ptr, L"%X",hp.offset);
905 + if (hp.type & DATA_INDIRECT) {
906 + if (hp.index>>31)
907 + ptr += swprintf(ptr, L"*-%X",-hp.index);
908 + else
909 + ptr += swprintf(ptr,L"*%X",hp.index);
910 + }
911 + if (hp.type & USING_SPLIT) {
912 + if (hp.split >> 31)
913 + ptr += swprintf(ptr, L":-%X", -(4 + hp.split));
914 + else
915 + ptr += swprintf(ptr, L":%X", hp.split);
916 + }
917 + if (hp.type & SPLIT_INDIRECT) {
918 + if (hp.split_index >> 31)
919 + ptr += swprintf(ptr, L"*-%X", -hp.split_index);
920 + else
921 + ptr += swprintf(ptr, L"*%X", hp.split_index);
922 + }
923 + if (hp.module) {
924 + if (pid) {
925 + WCHAR path[MAX_PATH];
926 + MEMORY_BASIC_INFORMATION info;
927 + ProcessRecord* pr = ::man->GetProcessRecord(pid);
928 + if (pr) {
929 + HANDLE hProc = pr->process_handle;
930 + if (NT_SUCCESS(NtQueryVirtualMemory(hProc,(PVOID)hp.address, MemorySectionName, path, MAX_PATH*2, 0)) &&
931 + NT_SUCCESS(NtQueryVirtualMemory(hProc,(PVOID)hp.address, MemoryBasicInformation, &info, sizeof(info), 0)))
932 + ptr += swprintf(ptr, L"@%X:%s", hp.address - (DWORD)info. AllocationBase, wcsrchr(path,L'\\') + 1);
933 + }
934 + } else {
935 + ptr += swprintf(ptr, L"@%X!%X", hp.address, hp.module);
936 + if (hp.function)
937 + ptr += swprintf(ptr, L"!%X", hp.function);
938 + }
939 + }
940 + else
941 + ptr += swprintf(ptr, L"@%X", hp.address);
942 +}
943 +
944 +// jichi 1/16/2015
945 +bool HookManager::IsFull() const { return new_thread_number >= MAX_HOOK; }
946 +
947 +void AddHooksToProfile(Profile& pf, const ProcessRecord& pr);
948 +DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread* thread);
949 +void MakeHookRelative(const ProcessRecord& pr, HookParam& hp);
950 +
951 +void HookManager::GetProfile(DWORD pid, pugi::xml_node profile_node)
952 +{
953 + const ProcessRecord* pr = GetProcessRecord(pid);
954 + if (pr == NULL)
955 + return;
956 + Profile pf(L"serialize");
957 + AddHooksToProfile(pf, *pr);
958 + AddThreadsToProfile(pf, *pr, pid);
959 + pf.XmlWriteProfile(profile_node);
960 +}
961 +
962 +void AddHooksToProfile(Profile& pf, const ProcessRecord& pr)
963 +{
964 + WaitForSingleObject(pr.hookman_mutex, 0);
965 + auto hooks = (const Hook*)pr.hookman_map;
966 + for (DWORD i = 0; i < MAX_HOOK; ++i)
967 + {
968 + if (hooks[i].Address() == 0)
969 + continue;
970 + auto& hook = hooks[i];
971 + DWORD type = hook.Type();
972 + if ((type & HOOK_ADDITIONAL) && (type & HOOK_ENGINE) == 0)
973 + {
974 + std::unique_ptr<CHAR[]> name(new CHAR[hook.NameLength()]);
975 + if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength(), NULL))
976 + {
977 + if (hook.hp.module)
978 + {
979 + HookParam hp = hook.hp;
980 + MakeHookRelative(pr, hp);
981 + pf.AddHook(hook_ptr(new HookProfile(hp, toUnicodeString(name.get()))));
982 + }
983 + else
984 + pf.AddHook(hook_ptr(new HookProfile(hook.hp, toUnicodeString(name.get()))));
985 + }
986 + }
987 + }
988 + ReleaseMutex(pr.hookman_mutex);
989 +}
990 +
991 +void MakeHookRelative(const ProcessRecord& pr, HookParam& hp)
992 +{
993 + MEMORY_BASIC_INFORMATION info;
994 + VirtualQueryEx(pr.process_handle, (LPCVOID)hp.address, &info, sizeof(info));
995 + hp.address -= (DWORD)info.AllocationBase;
996 + hp.function = 0;
997 +}
998 +
999 +void HookManager::AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid)
1000 +{
1001 + HM_LOCK;
1002 + ThreadTable* table = Table();
1003 + for (int i = 0; i < table->Used(); ++i)
1004 + {
1005 + TextThread* tt = table->FindThread(i);
1006 + if (tt == NULL || tt->GetThreadParameter()->pid != pid)
1007 + continue;
1008 + //if (tt->Status() & CURRENT_SELECT || tt->Link() || tt->GetComment())
1009 + if (tt->Status() & CURRENT_SELECT || tt->Link())
1010 + AddThreadToProfile(pf, pr, tt);
1011 + }
1012 +}
1013 +
1014 +DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread* thread)
1015 +{
1016 + const ThreadParameter* tp = thread->GetThreadParameter();
1017 + std::wstring hook_name = GetHookNameByAddress(pr, tp->hook);
1018 + if (hook_name.empty())
1019 + return -1;
1020 + auto thread_profile = new ThreadProfile(hook_name, tp->retn, tp->spl, 0, 0,
1021 + THREAD_MASK_RETN | THREAD_MASK_SPLIT, L"");
1022 + DWORD threads_size = pf.Threads().size();
1023 + int thread_profile_index = pf.AddThread(thread_ptr(thread_profile));
1024 + if (thread_profile_index == threads_size) // new thread
1025 + {
1026 + WORD iw = thread_profile_index & 0xFFFF;
1027 + if (thread->Status() & CURRENT_SELECT)
1028 + pf.SelectedIndex() = iw;
1029 + if (thread->Link())
1030 + {
1031 + WORD to_index = AddThreadToProfile(pf, pr, thread->Link()) & 0xFFFF;
1032 + if (iw >= 0)
1033 + pf.AddLink(link_ptr(new LinkProfile(iw, to_index)));
1034 + }
1035 + }
1036 + return thread_profile_index; // in case more than one thread links to the same thread
1037 +}
1038 +
1039 +// EOF
1 +#pragma once
2 +
3 +// hookman.h
4 +// 8/23/2013 jichi
5 +// Branch: ITH/HookManager.h, rev 133
6 +
7 +#include "host/avl_p.h"
8 +#include "host/textthread.h"
9 +#include "winmutex/winmutex.h"
10 +
11 +namespace pugi {
12 + class xml_node;
13 +}
14 +class Profile;
15 +
16 +enum { MAX_REGISTER = 0xf };
17 +enum { MAX_PREV_REPEAT_LENGTH = 0x20 };
18 +
19 +struct ProcessRecord {
20 + DWORD pid_register;
21 + DWORD hookman_register;
22 + DWORD module_register;
23 + //DWORD engine_register; // jichi 10/19/2014: removed
24 + HANDLE process_handle;
25 + HANDLE hookman_mutex;
26 + HANDLE hookman_section;
27 + LPVOID hookman_map;
28 +};
29 +
30 +class ThreadTable : public MyVector<TextThread *, 0x40>
31 +{
32 +public:
33 + virtual void SetThread(DWORD number, TextThread *ptr);
34 + virtual TextThread *FindThread(DWORD number);
35 +};
36 +
37 +struct IHFSERVICE TCmp { char operator()(const ThreadParameter *t1, const ThreadParameter *t2); };
38 +struct IHFSERVICE TCpy { void operator()(ThreadParameter *t1, const ThreadParameter *t2); };
39 +struct IHFSERVICE TLen { int operator()(const ThreadParameter *t); };
40 +
41 +typedef DWORD (*ProcessEventCallback)(DWORD pid);
42 +
43 +class IHFSERVICE HookManager : public AVLTree<ThreadParameter, DWORD, TCmp, TCpy, TLen>
44 +{
45 +public:
46 + HookManager();
47 + ~HookManager();
48 + // jichi 12/26/2013: remove virtual modifiers
49 + TextThread *FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split);
50 + TextThread *FindSingle(DWORD number);
51 + ProcessRecord *GetProcessRecord(DWORD pid);
52 + DWORD GetProcessIDByPath(LPCWSTR str); // private
53 + void RemoveSingleThread(DWORD number);
54 + //void LockHookman();
55 + //void UnlockHookman();
56 + void ResetRepeatStatus();
57 + void ClearCurrent();
58 + void AddLink(WORD from, WORD to);
59 + void UnLink(WORD from);
60 + void UnLinkAll(WORD from);
61 + void SelectCurrent(DWORD num);
62 + void DetachProcess(DWORD pid);
63 + void SetCurrent(TextThread *it);
64 + void AddConsoleOutput(LPCWSTR text);
65 +
66 + // jichi 10/27/2013: Add const; add space.
67 + void DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD split, int len, bool space);
68 +
69 + void ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD split); // private
70 + void RemoveProcessContext(DWORD pid); // private
71 + void RemoveSingleHook(DWORD pid, DWORD addr);
72 + void RegisterThread(TextThread*, DWORD); // private
73 + void RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread);
74 + void RegisterProcess(DWORD pid, DWORD hookman, DWORD module);
75 + void UnRegisterProcess(DWORD pid);
76 + //void SetName(DWORD);
77 +
78 + DWORD GetCurrentPID(); // private
79 + HANDLE GetCmdHandleByPID(DWORD pid);
80 +
81 + ConsoleCallback RegisterConsoleCallback(ConsoleCallback cf)
82 + { return (ConsoleCallback)_InterlockedExchange((long*)&console,(long)cf); }
83 +
84 + ConsoleWCallback RegisterConsoleWCallback(ConsoleWCallback cf)
85 + { return (ConsoleWCallback)_InterlockedExchange((long*)&wconsole,(long)cf); }
86 +
87 + ThreadEventCallback RegisterThreadCreateCallback(ThreadEventCallback cf)
88 + { return (ThreadEventCallback)_InterlockedExchange((long*)&create,(long)cf); }
89 +
90 + ThreadEventCallback RegisterThreadRemoveCallback(ThreadEventCallback cf)
91 + { return (ThreadEventCallback)_InterlockedExchange((long*)&remove,(long)cf); }
92 +
93 + ThreadEventCallback RegisterThreadResetCallback(ThreadEventCallback cf)
94 + { return (ThreadEventCallback)_InterlockedExchange((long*)&reset,(long)cf); }
95 +
96 + ThreadEventCallback RegisterAddRemoveLinkCallback(ThreadEventCallback cf)
97 + { return (ThreadEventCallback)_InterlockedExchange((long*)&addRemoveLink, (long)cf); }
98 +
99 + ProcessEventCallback RegisterProcessAttachCallback(ProcessEventCallback cf)
100 + { return (ProcessEventCallback)_InterlockedExchange((long*)&attach,(long)cf); }
101 +
102 + ProcessEventCallback RegisterProcessDetachCallback(ProcessEventCallback cf)
103 + { return (ProcessEventCallback)_InterlockedExchange((long*)&detach,(long)cf); }
104 +
105 + ProcessEventCallback RegisterProcessNewHookCallback(ProcessEventCallback cf)
106 + { return (ProcessEventCallback)_InterlockedExchange((long*)&hook,(long)cf); }
107 +
108 + ProcessEventCallback ProcessNewHook() { return hook; }
109 + TextThread *GetCurrentThread() { return current; } // private
110 + ProcessRecord *Records() { return record; } // private
111 + ThreadTable *Table() { return thread_table; } // private
112 +
113 + //DWORD& SplitTime() { return split_time; }
114 + //DWORD& RepeatCount() { return repeat_count; }
115 + //DWORD& CyclicRemove() { return cyclic_remove; }
116 + //DWORD& GlobalFilter() { return global_filter; }
117 + void ConsoleOutput(LPCSTR text) { if (console) console(text); } // not thread safe
118 + void ConsoleOutputW(LPCWSTR text) { if (wconsole) wconsole(text); } // not thread safe
119 +
120 + void OnThreadCreate(pugi::xml_node profile_node, TextThread* thread);
121 + void GetProfile(DWORD pid, pugi::xml_node profile_node);
122 +
123 +private:
124 + typedef win_mutex<CRITICAL_SECTION> mutex_type;
125 + mutex_type hmcs;
126 +
127 + TextThread *current;
128 + ConsoleCallback console; // jichi 12/25/2013: add console output callback
129 + ConsoleWCallback wconsole;
130 + ThreadEventCallback create,
131 + remove,
132 + reset,
133 + addRemoveLink;
134 + ProcessEventCallback attach,
135 + detach,
136 + hook;
137 + DWORD current_pid;
138 + ThreadTable *thread_table;
139 + HANDLE destroy_event;
140 + ProcessRecord record[MAX_REGISTER + 1];
141 + HANDLE text_pipes[MAX_REGISTER + 1],
142 + cmd_pipes[MAX_REGISTER + 1],
143 + recv_threads[MAX_REGISTER + 1];
144 + WORD register_count,
145 + new_thread_number;
146 +
147 + // jichi 1/16/2014: Stop adding new threads when full
148 + bool IsFull() const; // { return new_thread_number >= MAX_HOOK; }
149 + bool IsEmpty() const { return !new_thread_number; }
150 + void HookManager::AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid);
151 +};
152 +
153 +// EOF
1 +// host.cc
2 +// 8/24/2013 jichi
3 +// Branch IHF/main.cpp, rev 111
4 +// 8/24/2013 TODO: Clean up this file
5 +
6 +//#ifdef _MSC_VER
7 +//# pragma warning(disable:4800) // C4800: forcing value to bool (performance warning)
8 +//#endif // _MSC_VER
9 +
10 +//#include "customfilter.h"
11 +#include "growl.h"
12 +#include "host.h"
13 +#include "host_p.h"
14 +#include "settings.h"
15 +#include "vnrhook/include/const.h"
16 +#include "vnrhook/include/defs.h"
17 +#include "vnrhook/include/types.h"
18 +#include "ithsys/ithsys.h"
19 +#include "windbg/inject.h"
20 +#include "winmaker/winmaker.h"
21 +#include "ccutil/ccmacro.h"
22 +#include <commctrl.h>
23 +
24 +//#define ITH_WINE
25 +//#define ITH_USE_UX_DLLS IthIsWine()
26 +//#define ITH_USE_XP_DLLS (IthIsWindowsXp() && !IthIsWine())
27 +
28 +#define DEBUG "vnrhost/host.cc"
29 +#include "sakurakit/skdebug.h"
30 +
31 +namespace { // unnamed
32 +
33 +//enum { HOOK_TIMEOUT = -50000000 }; // in nanoseconds = 5 seconds
34 +
35 +CRITICAL_SECTION cs;
36 +//WCHAR exist[] = ITH_PIPEEXISTS_EVENT;
37 +//WCHAR mutex[] = L"ITH_RUNNING";
38 +//WCHAR EngineName[] = ITH_ENGINE_DLL;
39 +//WCHAR EngineNameXp[] = ITH_ENGINE_XP_DLL;
40 +//WCHAR DllName[] = ITH_CLIENT_DLL;
41 +//WCHAR DllNameXp[] = ITH_CLIENT_XP_DLL;
42 +HANDLE hServerMutex; // jichi 9/28/2013: used to guard pipe
43 +HANDLE hHookMutex; // jichi 9/28/2013: used to guard hook modification
44 +} // unnamed namespace
45 +
46 +//extern LPWSTR current_dir;
47 +extern CRITICAL_SECTION detach_cs;
48 +
49 +Settings *settings;
50 +HWND hMainWnd;
51 +HANDLE hPipeExist;
52 +BOOL running;
53 +
54 +#define ITH_SYNC_HOOK IthMutexLocker locker(::hHookMutex)
55 +
56 +namespace { // unnamed
57 +
58 +void GetDebugPriv()
59 +{
60 + HANDLE hToken;
61 + DWORD dwRet;
62 + NTSTATUS status;
63 +
64 + TOKEN_PRIVILEGES Privileges = {1,{0x14,0,SE_PRIVILEGE_ENABLED}};
65 +
66 + NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
67 +
68 + status = NtAdjustPrivilegesToken(hToken, 0, &Privileges, sizeof(Privileges), 0, &dwRet);
69 + //if (STATUS_SUCCESS == status)
70 + //{
71 + // admin = 1;
72 + //}
73 + //else
74 + //{
75 + // WCHAR buffer[0x10];
76 + // swprintf(buffer, L"%.8X",status);
77 + // MessageBox(0, NotAdmin, buffer, 0);
78 + //}
79 + NtClose(hToken);
80 +}
81 +
82 +bool sendCommand(HANDLE hCmd, HostCommandType cmd)
83 +{
84 + IO_STATUS_BLOCK ios;
85 + //SendParam sp = {};
86 + //sp.type = cmd;
87 + DWORD data = cmd;
88 + return hCmd && NT_SUCCESS(NtWriteFile(hCmd, 0,0,0, &ios, &data, sizeof(data), 0,0));
89 +}
90 +
91 +// jichi 9/22/2013: Change current directory to the same as main module path
92 +// Otherwise NtFile* would not work for files with relative paths.
93 +//BOOL ChangeCurrentDirectory()
94 +//{
95 +// if (const wchar_t *path = GetMainModulePath()) // path to VNR's python exe
96 +// if (const wchar_t *base = wcsrchr(path, L'\\')) {
97 +// size_t len = base - path;
98 +// if (len < MAX_PATH) {
99 +// wchar_t buf[MAX_PATH];
100 +// wcsncpy(buf, path, len);
101 +// buf[len] = 0;
102 +// return SetCurrentDirectoryW(buf);
103 +// }
104 +// }
105 +// return FALSE;
106 +//}
107 +
108 +#if 0
109 +bool injectUsingWin32Api(LPCWSTR path, HANDLE hProc)
110 +{ return WinDbg::injectDllW(path, 0, hProc); }
111 +
112 +bool ejectUsingWin32Api(HANDLE hModule, HANDLE hProc)
113 +{ return WinDbg::ejectDll(hModule, hProc); }
114 +
115 +// The original inject logic in ITH
116 +bool injectUsingNTApi(LPCWSTR path, HANDLE hProc)
117 +{
118 + LPVOID lpvAllocAddr = 0;
119 + DWORD dwWrite = 0x1000; //, len = 0;
120 + //if (IthIsWine())
121 + // lpvAllocAddr = VirtualAllocEx(hProc, nullptr, dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
122 + //else
123 + NtAllocateVirtualMemory(hProc, &lpvAllocAddr, 0, &dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
124 + if (!lpvAllocAddr)
125 + return false;
126 +
127 + CheckThreadStart();
128 +
129 + //Copy module path into address space of target process.
130 + //if (IthIsWine())
131 + // WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
132 + //else
133 + NtWriteVirtualMemory(hProc, lpvAllocAddr, (LPVOID)path, MAX_PATH << 1, &dwWrite);
134 + HANDLE hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc);
135 + if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) {
136 + DOUT("ERROR: failed to create remote cli thread");
137 + //ConsoleOutput(ErrorRemoteThread);
138 + return false;
139 + }
140 + // jichi 9/28/2013: no wait as it will not blocked
141 + NtWaitForSingleObject(hTH, 0, nullptr);
142 + THREAD_BASIC_INFORMATION info;
143 + NtQueryInformationThread(hTH, ThreadBasicInformation, &info, sizeof(info), &dwWrite);
144 + NtClose(hTH);
145 +
146 + // jichi 10/19/2014: Disable inject the second dll
147 + //if (info.ExitStatus) {
148 + // //IthCoolDown();
149 + // wcscpy(p, engine);
150 + // //if (IthIsWine())
151 + // // WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
152 + // //else
153 + // NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
154 + // hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc);
155 + // if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) {
156 + // //ConsoleOutput(ErrorRemoteThread);
157 + // ConsoleOutput("vnrhost:inject: ERROR: failed to create remote eng thread");
158 + // return error;
159 + // }
160 + //
161 + // // jichi 9/28/2013: no wait as it will not blocked
162 + // NtWaitForSingleObject(hTH, 0, nullptr);
163 + // NtClose(hTH);
164 + //}
165 +
166 + dwWrite = 0;
167 + //if (IthIsWine())
168 + // VirtualFreeEx(hProc, lpvAllocAddr, dwWrite, MEM_RELEASE);
169 + //else
170 + NtFreeVirtualMemory(hProc, &lpvAllocAddr, &dwWrite, MEM_RELEASE);
171 + return info.ExitStatus != -1;
172 +}
173 +
174 +bool ejectUsingNTApi(HANDLE hModule, HANDLE hProc)
175 +{
176 + //IthCoolDown();
177 +//#ifdef ITH_WINE // Nt series crash on wine
178 +// hThread = IthCreateThread(FreeLibrary, engine, hProc);
179 +//#else
180 + HANDLE hThread = IthCreateThread(LdrUnloadDll, module, hProc);
181 +//#endif // ITH_WINE
182 + if (hThread == 0 || hThread == INVALID_HANDLE_VALUE)
183 + return false;
184 + // jichi 10/22/2013: Timeout might crash vnrsrv
185 + //NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout);
186 + NtWaitForSingleObject(hThread, 0, nullptr);
187 + //man->UnlockHookman();
188 + THREAD_BASIC_INFORMATION info;
189 + NtQueryInformationThread(hThread, ThreadBasicInformation, &info, sizeof(info), 0);
190 + NtClose(hThread);
191 + NtSetEvent(hPipeExist, 0);
192 + FreeThreadStart(hProc);
193 + return info.ExitStatus;
194 +}
195 +#endif // 0
196 +
197 +bool Inject(HANDLE hProc)
198 +{
199 + //LPWSTR dllname = (IthIsWindowsXp() && !IthIsWine()) ? DllNameXp : DllName;
200 + //LPCWSTR dllname = ITH_USE_XP_DLLS ? ITH_DLL_XP : ITH_DLL;
201 + //LPCWSTR dllname = ITH_DLL;
202 + //if (!IthCheckFile(dllname))
203 + // return error;
204 + wchar_t path[MAX_PATH];
205 + size_t len = IthGetCurrentModulePath(path, MAX_PATH);
206 + if (!len)
207 + return false;
208 +
209 + wchar_t *p;
210 + for (p = path + len; *p != L'\\'; p--);
211 + p++; // ending with L"\\"
212 +
213 + //LPCWSTR mp = GetMainModulePath();
214 + //len = wcslen(mp);
215 + //memcpy(path, mp, len << 1);
216 + //memset(path + len, 0, (MAX_PATH - len) << 1);
217 + //LPWSTR p;
218 + //for (p = path + len; *p != L'\\'; p--); // Always a \ after drive letter.
219 + //p++;
220 + ::wcscpy(p, ITH_DLL);
221 +
222 + return WinDbg::injectDllW(path, 0, hProc);
223 + //if (IthIsWindowsXp()) // && !IthIsWine())
224 + // return injectUsingWin32Api(path, hProc);
225 + //else
226 + // return injectUsingNTApi(path, hProc);
227 +}
228 +
229 +} // unnamed namespace
230 +
231 +void CreateNewPipe();
232 +
233 +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
234 +{
235 + CC_UNUSED(lpvReserved);
236 + switch (fdwReason)
237 + {
238 + case DLL_PROCESS_ATTACH:
239 + LdrDisableThreadCalloutsForDll(hinstDLL);
240 + InitializeCriticalSection(&::cs);
241 + IthInitSystemService();
242 + GetDebugPriv();
243 + // jichi 12/20/2013: Since I already have a GUI, I don't have to InitCommonControls()
244 + //Used by timers.
245 + InitCommonControls();
246 + // jichi 8/24/2013: Create hidden window so that ITH can access timer and events
247 + hMainWnd = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0);
248 + //wm_register_hidden_class("vnrsrv.class");
249 + //hMainWnd = (HWND)wm_create_hidden_window("vnrsrv", "Button", hinstDLL);
250 + //ChangeCurrentDirectory();
251 + break;
252 + case DLL_PROCESS_DETACH:
253 + if (::running)
254 + Host_Close();
255 + DeleteCriticalSection(&::cs);
256 + IthCloseSystemService();
257 + //wm_destroy_window(hMainWnd);
258 + DestroyWindow(hMainWnd);
259 + break;
260 + default:
261 + break;
262 + }
263 + return true;
264 +}
265 +
266 +HANDLE IthOpenPipe(LPWSTR name, ACCESS_MASK direction)
267 +{
268 + UNICODE_STRING us;
269 + RtlInitUnicodeString(&us, name);
270 + SECURITY_DESCRIPTOR sd = {1};
271 + OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
272 + HANDLE hFile;
273 + IO_STATUS_BLOCK isb;
274 + if (NT_SUCCESS(NtCreateFile(&hFile, direction, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0)))
275 + return hFile;
276 + else
277 + return INVALID_HANDLE_VALUE;
278 +}
279 +
280 +enum { IHS_SIZE = 0x80 };
281 +enum { IHS_BUFF_SIZE = IHS_SIZE - sizeof(HookParam) };
282 +
283 +struct InsertHookStruct
284 +{
285 + SendParam sp;
286 + BYTE name_buffer[IHS_SIZE];
287 +};
288 +
289 +IHFSERVICE void IHFAPI Host_Init()
290 +{
291 + InitializeCriticalSection(&::cs);
292 + GetDebugPriv();
293 +}
294 +
295 +IHFSERVICE void IHFAPI Host_Destroy()
296 +{
297 + InitializeCriticalSection(&::cs);
298 +}
299 +
300 +IHFSERVICE BOOL IHFAPI Host_Open()
301 +{
302 + BOOL result = false;
303 + EnterCriticalSection(&::cs);
304 + DWORD present;
305 + hServerMutex = IthCreateMutex(ITH_SERVER_MUTEX, 1, &present);
306 + if (present)
307 + //MessageBox(0,L"Already running.",0,0);
308 + // jichi 8/24/2013
309 + GROWL_WARN(L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!");
310 + else if (!::running) {
311 + ::running = true;
312 + ::settings = new Settings;
313 + ::man = new HookManager;
314 + //cmdq = new CommandQueue;
315 + InitializeCriticalSection(&detach_cs);
316 +
317 + ::hHookMutex = IthCreateMutex(ITH_SERVER_HOOK_MUTEX, FALSE);
318 + result = true;
319 + }
320 + LeaveCriticalSection(&::cs);
321 + return result;
322 +}
323 +
324 +IHFSERVICE DWORD IHFAPI Host_Start()
325 +{
326 + //IthBreak();
327 + CreateNewPipe();
328 + ::hPipeExist = IthCreateEvent(ITH_PIPEEXISTS_EVENT);
329 + NtSetEvent(::hPipeExist, nullptr);
330 + return 0;
331 +}
332 +
333 +IHFSERVICE DWORD IHFAPI Host_Close()
334 +{
335 + BOOL result = FALSE;
336 + EnterCriticalSection(&::cs);
337 + if (::running) {
338 + ::running = FALSE;
339 + HANDLE hRecvPipe = IthOpenPipe(recv_pipe, GENERIC_WRITE);
340 + NtClose(hRecvPipe);
341 + NtClearEvent(::hPipeExist);
342 + //delete cmdq;
343 + delete man;
344 + delete settings;
345 + NtClose(::hHookMutex);
346 + NtClose(hServerMutex);
347 + NtClose(::hPipeExist);
348 + DeleteCriticalSection(&detach_cs);
349 + result = TRUE;
350 + }
351 + LeaveCriticalSection(&::cs);
352 + return result;
353 +}
354 +
355 +IHFSERVICE DWORD IHFAPI Host_GetPIDByName(LPCWSTR pwcTarget)
356 +{
357 + DWORD dwSize = 0x20000,
358 + dwExpectSize = 0;
359 + LPVOID pBuffer = 0;
360 + SYSTEM_PROCESS_INFORMATION *spiProcessInfo;
361 + DWORD dwPid = 0;
362 + DWORD dwStatus;
363 +
364 + NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
365 + dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize);
366 + if (!NT_SUCCESS(dwStatus)) {
367 + NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE);
368 + if (dwStatus != STATUS_INFO_LENGTH_MISMATCH || dwExpectSize < dwSize)
369 + return 0;
370 + dwSize = (dwExpectSize | 0xFFF) + 0x4001; //
371 + pBuffer = 0;
372 + NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
373 + dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize);
374 + if (!NT_SUCCESS(dwStatus)) goto _end;
375 + }
376 +
377 + for (spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *)pBuffer; spiProcessInfo->dNext;) {
378 + spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *)
379 + ((DWORD)spiProcessInfo + spiProcessInfo -> dNext);
380 + if (_wcsicmp(pwcTarget, spiProcessInfo -> usName.Buffer) == 0) {
381 + dwPid = spiProcessInfo->dUniqueProcessId;
382 + break;
383 + }
384 + }
385 + if (!dwPid)
386 + DOUT("pid not found");
387 + //if (dwPid == 0) ConsoleOutput(ErrorNoProcess);
388 +_end:
389 + NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE);
390 + return dwPid;
391 +}
392 +
393 +IHFSERVICE bool IHFAPI Host_InjectByPID(DWORD pid)
394 +{
395 + WCHAR str[0x80];
396 + if (!::running)
397 + return 0;
398 + if (pid == current_process_id) {
399 + //ConsoleOutput(SelfAttach);
400 + DOUT("refuse to inject myself");
401 + return false;
402 + }
403 + if (man->GetProcessRecord(pid)) {
404 + //ConsoleOutput(AlreadyAttach);
405 + DOUT("already attached");
406 + return false;
407 + }
408 + swprintf(str, ITH_HOOKMAN_MUTEX_ L"%d", pid);
409 + DWORD s;
410 + NtClose(IthCreateMutex(str, 0, &s));
411 + if (s) {
412 + DOUT("already locked");
413 + return false;
414 + }
415 + CLIENT_ID id;
416 + OBJECT_ATTRIBUTES oa = {};
417 + HANDLE hProc;
418 + id.UniqueProcess = pid;
419 + id.UniqueThread = 0;
420 + oa.uLength = sizeof(oa);
421 + if (!NT_SUCCESS(NtOpenProcess(&hProc,
422 + PROCESS_QUERY_INFORMATION|
423 + PROCESS_CREATE_THREAD|
424 + PROCESS_VM_OPERATION|
425 + PROCESS_VM_READ|
426 + PROCESS_VM_WRITE,
427 + &oa, &id))) {
428 + //ConsoleOutput(ErrorOpenProcess);
429 + DOUT("failed to open process");
430 + return false;
431 + }
432 +
433 + //if (!engine)
434 + // engine = ITH_USE_XP_DLLS ? ITH_ENGINE_XP_DLL : ITH_ENGINE_DLL;
435 + bool ok = Inject(hProc);
436 + //NtClose(hProc); //already closed
437 + if (!ok) {
438 + DOUT("inject failed");
439 + return false;
440 + }
441 + //swprintf(str, FormatInject, pid, module);
442 + //ConsoleOutput(str);
443 + DOUT("inject succeed");
444 + return true;
445 +}
446 +
447 +// jichi 7/16/2014: Test if process is valid before creating remote threads
448 +// See: http://msdn.microsoft.com/en-us/library/ms687032.aspx
449 +static bool isProcessTerminated(HANDLE hProc)
450 +{ return WAIT_OBJECT_0 == ::WaitForSingleObject(hProc, 0); }
451 +//static bool isProcessRunning(HANDLE hProc)
452 +//{ return WAIT_TIMEOUT == ::WaitForSingleObject(hProc, 0); }
453 +
454 +// jichi 7/16/2014: Test if process is valid before creating remote threads
455 +//static bool isProcessRunning(DWORD pid)
456 +//{
457 +// bool ret = false;
458 +// HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
459 +// if (hProc) {
460 +// DWORD status;
461 +// if (::GetExitCodeProcess(hProc, &status)) {
462 +// ret = status == STILL_ACTIVE;
463 +// ::CloseHandle(hProc);
464 +// } else
465 +// ret = true;
466 +// }
467 +// return ret;
468 +//}
469 +
470 +IHFSERVICE bool IHFAPI Host_ActiveDetachProcess(DWORD pid)
471 +{
472 + ITH_SYNC_HOOK;
473 +
474 + //man->LockHookman();
475 + ProcessRecord *pr = man->GetProcessRecord(pid);
476 + HANDLE hCmd = man->GetCmdHandleByPID(pid);
477 + if (pr == 0 || hCmd == 0)
478 + return false;
479 + HANDLE hProc;
480 + //hProc = pr->process_handle; //This handle may be closed(thus invalid) during the detach process.
481 + NtDuplicateObject(NtCurrentProcess(), pr->process_handle,
482 + NtCurrentProcess(), &hProc, 0, 0, DUPLICATE_SAME_ACCESS); // Make a copy of the process handle.
483 + HANDLE hModule = (HANDLE)pr->module_register;
484 + if (!hModule) {
485 + DOUT("process module not found");
486 + return false;
487 + }
488 +
489 + // jichi 7/15/2014: Process already closed
490 + if (isProcessTerminated(hProc)) {
491 + DOUT("process has terminated");
492 + return false;
493 + }
494 +
495 + // jichi 10/19/2014: Disable the second dll
496 + //engine = pr->engine_register;
497 + //engine &= ~0xff;
498 +
499 + DOUT("send detach command");
500 + bool ret = sendCommand(hCmd, HOST_COMMAND_DETACH);
501 +
502 + // jichi 7/15/2014: Process already closed
503 + //if (isProcessTerminated(hProc)) {
504 + // DOUT("process has terminated");
505 + // return false;
506 + //}
507 + //WinDbg::ejectDll(hModule, 0, hProc); // eject in case module has not loaded yet
508 +
509 + //cmdq->AddRequest(sp, pid);
510 +////#ifdef ITH_WINE // Nt series crash on wine
511 +//// hThread = IthCreateThread(FreeLibrary, engine, hProc);
512 +////#else
513 +// hThread = IthCreateThread(LdrUnloadDll, engine, hProc);
514 +////#endif // ITH_WINE
515 +// if (hThread == 0 || hThread == INVALID_HANDLE_VALUE)
516 +// return FALSE;
517 +// // jichi 10/22/2013: Timeout might crash vnrsrv
518 +// //const LONGLONG timeout = HOOK_TIMEOUT;
519 +// //NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout);
520 +// NtWaitForSingleObject(hThread, 0, nullptr);
521 +// NtClose(hThread);
522 + NtClose(hProc);
523 + return ret;
524 +}
525 +
526 +IHFSERVICE DWORD IHFAPI Host_GetHookManager(HookManager** hookman)
527 +{
528 + if (::running) {
529 + *hookman = man;
530 + return 0;
531 + }
532 + else
533 + return 1;
534 +}
535 +
536 +IHFSERVICE bool IHFAPI Host_GetSettings(Settings **p)
537 +{
538 + if (::running) {
539 + *p = settings;
540 + return true;
541 + }
542 + else
543 + return false;
544 +}
545 +
546 +IHFSERVICE bool IHFAPI Host_HijackProcess(DWORD pid)
547 +{
548 + //ITH_SYNC_HOOK;
549 + HANDLE hCmd = man->GetCmdHandleByPID(pid);
550 + return hCmd && sendCommand(hCmd, HOST_COMMAND_HIJACK_PROCESS);
551 +}
552 +
553 +IHFSERVICE DWORD IHFAPI Host_InsertHook(DWORD pid, HookParam *hp, LPCSTR name)
554 +{
555 + ITH_SYNC_HOOK;
556 +
557 + HANDLE hCmd = man->GetCmdHandleByPID(pid);
558 + if (hCmd == 0)
559 + return -1;
560 +
561 + InsertHookStruct s;
562 + s.sp.type = HOST_COMMAND_NEW_HOOK;
563 + s.sp.hp = *hp;
564 + size_t len;
565 + if (name)
566 + len = ::strlen(name);
567 + else
568 + len = 0;
569 + if (len) {
570 + if (len >= IHS_BUFF_SIZE) len = IHS_BUFF_SIZE - 1;
571 + memcpy(s.name_buffer, name, len);
572 + }
573 + s.name_buffer[len] = 0;
574 + IO_STATUS_BLOCK ios;
575 + NtWriteFile(hCmd, 0,0,0, &ios, &s, IHS_SIZE, 0, 0);
576 +
577 + //memcpy(&sp.hp,hp,sizeof(HookParam));
578 + //cmdq->AddRequest(sp, pid);
579 + return 0;
580 +}
581 +
582 +IHFSERVICE DWORD IHFAPI Host_ModifyHook(DWORD pid, HookParam *hp)
583 +{
584 + ITH_SYNC_HOOK;
585 +
586 + HANDLE hCmd = GetCmdHandleByPID(pid);
587 + if (hCmd == 0)
588 + return -1;
589 + HANDLE hModify = IthCreateEvent(ITH_MODIFYHOOK_EVENT);
590 + SendParam sp;
591 + sp.type = HOST_COMMAND_MODIFY_HOOK;
592 + sp.hp = *hp;
593 + IO_STATUS_BLOCK ios;
594 + if (NT_SUCCESS(NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam), 0, 0)))
595 + // jichi 9/28/2013: no wait timeout
596 + //const LONGLONG timeout = HOOK_TIMEOUT;
597 + NtWaitForSingleObject(hModify, 0, nullptr);
598 + NtClose(hModify);
599 + man->RemoveSingleHook(pid, sp.hp.address);
600 + return 0;
601 +}
602 +
603 +IHFSERVICE DWORD IHFAPI Host_RemoveHook(DWORD pid, DWORD addr)
604 +{
605 + ITH_SYNC_HOOK;
606 +
607 + HANDLE hRemoved,hCmd;
608 + hCmd = GetCmdHandleByPID(pid);
609 + if (hCmd == 0)
610 + return -1;
611 + hRemoved = IthCreateEvent(ITH_REMOVEHOOK_EVENT);
612 + SendParam sp = {};
613 + IO_STATUS_BLOCK ios;
614 + sp.type = HOST_COMMAND_REMOVE_HOOK;
615 + sp.hp.address = addr;
616 + //cmdq -> AddRequest(sp, pid);
617 + NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam),0,0);
618 + // jichi 10/22/2013: Timeout might crash vnrsrv
619 + //const LONGLONG timeout = HOOK_TIMEOUT;
620 + //NtWaitForSingleObject(hRemoved, 0, (PLARGE_INTEGER)&timeout);
621 + NtWaitForSingleObject(hRemoved, 0, nullptr);
622 + NtClose(hRemoved);
623 + man -> RemoveSingleHook(pid, sp.hp.address);
624 + return 0;
625 +}
626 +
627 +// 4/30/2015: Removed as not needed. Going to change to json
628 +IHFSERVICE DWORD IHFAPI Host_AddLink(DWORD from, DWORD to)
629 +{
630 + man->AddLink(from & 0xffff, to & 0xffff);
631 + return 0;
632 +}
633 +
634 +IHFSERVICE DWORD IHFAPI Host_UnLink(DWORD from)
635 +{
636 + man->UnLink(from & 0xffff);
637 + return 0;
638 +}
639 +
640 +IHFSERVICE DWORD IHFAPI Host_UnLinkAll(DWORD from)
641 +{
642 + man->UnLinkAll(from & 0xffff);
643 + return 0;
644 +}
645 +
646 +// EOF
1 +#pragma once
2 +
3 +// host.h
4 +// 8/23/2013 jichi
5 +// Branch: ITH/IHF.h, rev 105
6 +
7 +//#include "host/settings.h"
8 +#include "config.h"
9 +#include "host/hookman.h"
10 +
11 +struct Settings;
12 +struct HookParam;
13 +
14 +IHFSERVICE void IHFAPI Host_Init();
15 +IHFSERVICE void IHFAPI Host_Destroy();
16 +
17 +IHFSERVICE DWORD IHFAPI Host_Start();
18 +IHFSERVICE BOOL IHFAPI Host_Open();
19 +IHFSERVICE DWORD IHFAPI Host_Close();
20 +IHFSERVICE DWORD IHFAPI Host_GetHookManager(HookManager **hookman);
21 +IHFSERVICE bool IHFAPI Host_GetSettings(Settings **settings);
22 +IHFSERVICE DWORD IHFAPI Host_GetPIDByName(LPCWSTR pwcTarget);
23 +IHFSERVICE bool IHFAPI Host_InjectByPID(DWORD pid);
24 +IHFSERVICE bool IHFAPI Host_ActiveDetachProcess(DWORD pid);
25 +IHFSERVICE bool IHFAPI Host_HijackProcess(DWORD pid);
26 +IHFSERVICE DWORD IHFAPI Host_InsertHook(DWORD pid, HookParam *hp, LPCSTR name = nullptr);
27 +IHFSERVICE DWORD IHFAPI Host_ModifyHook(DWORD pid, HookParam *hp);
28 +IHFSERVICE DWORD IHFAPI Host_RemoveHook(DWORD pid, DWORD addr);
29 +IHFSERVICE DWORD IHFAPI Host_AddLink(DWORD from, DWORD to);
30 +IHFSERVICE DWORD IHFAPI Host_UnLink(DWORD from);
31 +IHFSERVICE DWORD IHFAPI Host_UnLinkAll(DWORD from);
32 +
33 +// EOF
1 +# host.pri
2 +# 8/9/2011 jichi
3 +
4 +DEFINES += WITH_LIB_VNRHOST
5 +
6 +DEPENDPATH += $$PWD
7 +
8 +HEADERS += \
9 + $$PWD/avl_p.h \
10 + $$PWD/hookman.h \
11 + $$PWD/settings.h \
12 + $$PWD/host.h \
13 + $$PWD/host_p.h \
14 + $$PWD/textthread.h \
15 + $$PWD/textthread_p.h
16 +
17 +SOURCES += \
18 + $$PWD/hookman.cc \
19 + $$PWD/host.cc \
20 + $$PWD/pipe.cc \
21 + $$PWD/textthread.cc
22 +
23 +# EOF
1 +#pragma once
2 +// host_p.h
3 +// 8/24/2013 jichi
4 +// Branch IHF/main.h, rev 111
5 +#include <windows.h>
6 +
7 +#define GLOBAL extern
8 +#define SHIFT_JIS 0x3A4
9 +class HookManager;
10 +//class CommandQueue;
11 +class SettingManager;
12 +class TextHook;
13 +//class BitMap;
14 +//class CustomFilterMultiByte;
15 +//class CustomFilterUnicode;
16 +//#define TextHook Hook
17 +GLOBAL BOOL running;
18 +//GLOBAL BitMap *pid_map;
19 +//GLOBAL CustomFilterMultiByte *mb_filter;
20 +//GLOBAL CustomFilterUnicode *uni_filter;
21 +GLOBAL HookManager *man;
22 +//GLOBAL CommandQueue *cmdq;
23 +GLOBAL SettingManager *setman;
24 +GLOBAL WCHAR recv_pipe[];
25 +GLOBAL WCHAR command[];
26 +GLOBAL HANDLE hPipeExist;
27 +GLOBAL DWORD split_time,
28 + cyclic_remove,
29 + clipboard_flag,
30 + global_filter;
31 +GLOBAL CRITICAL_SECTION detach_cs;
32 +
33 +DWORD WINAPI RecvThread(LPVOID lpThreadParameter);
34 +DWORD WINAPI CmdThread(LPVOID lpThreadParameter);
35 +
36 +DWORD GetCurrentPID();
37 +//DWORD GetProcessIDByPath(LPWSTR str);
38 +HANDLE GetCmdHandleByPID(DWORD pid);
39 +//DWORD Inject(HANDLE hProc);
40 +//DWORD InjectByPID(DWORD pid);
41 +//DWORD PIDByName(LPWSTR target);
42 +//DWORD Hash(LPCWSTR module, int length=-1);
43 +
44 +// EOF
1 +// pipe.cc
2 +// 8/24/2013 jichi
3 +// Branch IHF/pipe.cpp, rev 93
4 +// 8/24/2013 TODO: Clean up this file
5 +
6 +#include "host_p.h"
7 +#include "hookman.h"
8 +#include "vnrhook/include/defs.h"
9 +#include "vnrhook/include/const.h"
10 +#include "ithsys/ithsys.h"
11 +#include <stdio.h>
12 +//#include "CommandQueue.h"
13 +//#include <QtCore/QDebug>
14 +
15 +#define DEBUG "vnrhost/pipe.cc"
16 +#include "sakurakit/skdebug.h"
17 +
18 +//DWORD WINAPI UpdateWindows(LPVOID lpThreadParameter);
19 +
20 +namespace { // unnamed
21 +enum NamedPipeCommand {
22 + NAMED_PIPE_DISCONNECT = 1
23 + , NAMED_PIPE_CONNECT = 2
24 +};
25 +
26 +bool newline = false;
27 +bool detach = false;
28 +
29 +// jichi 10/27/2013
30 +// Check if text has leading space
31 +enum { _filter_limit = 0x20 }; // The same as the orignal ITH filter. So, I don't have to check \u3000
32 +//enum { _filter_limit = 0x19 };
33 +inline bool has_leading_space(const BYTE *text, int len)
34 +{
35 + return len == 1 ? *text <= _filter_limit : // 1 byte
36 + *reinterpret_cast<const WORD *>(text) <= _filter_limit; // 2 bytes
37 +}
38 +
39 +// jichi 9/28/2013: Skip leading garbage
40 +// Note:
41 +// - Modifying limit will break manual translation. The orignal one is 0x20
42 +// - Eliminating 0x20 will break English-translated games
43 +const BYTE *Filter(const BYTE *str, int len)
44 +{
45 +#ifdef ITH_DISABLE_FILTER // jichi 9/28/2013: only for debugging purpose
46 + return str;
47 +#endif // ITH_DISABLE_FILTER
48 +// if (len && *str == 0x10) // jichi 9/28/2013: garbage on wine, data link escape, or ^P
49 +// return nullptr;
50 + //enum { limit = 0x19 };
51 + while (true)
52 + if (len >= 2) {
53 + if (*(const WORD *)str <= _filter_limit) { // jichi 10/27/2013: two bytes
54 + str += 2;
55 + len -= 2;
56 + } else
57 + break;
58 + } else if (*str <= _filter_limit) { // jichi 10/27/2013: 1 byte
59 + str++;
60 + len--;
61 + } else
62 + break;
63 + return str;
64 +}
65 +} // unnamed namespace
66 +
67 +//WCHAR recv_pipe[] = L"\\??\\pipe\\ITH_PIPE";
68 +//WCHAR command_pipe[] = L"\\??\\pipe\\ITH_COMMAND";
69 +wchar_t recv_pipe[] = ITH_TEXT_PIPE;
70 +wchar_t command_pipe[] = ITH_COMMAND_PIPE;
71 +
72 +CRITICAL_SECTION detach_cs; // jichi 9/27/2013: also used in main
73 +//HANDLE hDetachEvent;
74 +extern HANDLE hPipeExist;
75 +
76 +void CreateNewPipe()
77 +{
78 + static DWORD acl[7] = {
79 + 0x1C0002,
80 + 1,
81 + 0x140000,
82 + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
83 + 0x101,
84 + 0x1000000,
85 + 0};
86 + static SECURITY_DESCRIPTOR sd = {1, 0, 4, 0, 0, 0, (PACL)acl};
87 +
88 + HANDLE hTextPipe, hCmdPipe, hThread;
89 + IO_STATUS_BLOCK ios;
90 + UNICODE_STRING us;
91 +
92 + OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
93 + LARGE_INTEGER time = {-500000, -1};
94 +
95 + RtlInitUnicodeString(&us, recv_pipe);
96 + if (!NT_SUCCESS(NtCreateNamedPipeFile(
97 + &hTextPipe,
98 + GENERIC_READ | SYNCHRONIZE,
99 + &oa,
100 + &ios,
101 + FILE_SHARE_WRITE,
102 + FILE_OPEN_IF,
103 + FILE_SYNCHRONOUS_IO_NONALERT,
104 + 1, 1, 0, -1,
105 + 0x1000,
106 + 0x1000,
107 + &time))) {
108 + //ConsoleOutput(ErrorCreatePipe);
109 + DOUT("failed to create recv pipe");
110 + return;
111 + }
112 +
113 + RtlInitUnicodeString(&us, command_pipe);
114 + if (!NT_SUCCESS(NtCreateNamedPipeFile(
115 + &hCmdPipe,
116 + GENERIC_WRITE | SYNCHRONIZE,
117 + &oa,
118 + &ios,
119 + FILE_SHARE_READ,
120 + FILE_OPEN_IF,
121 + FILE_SYNCHRONOUS_IO_NONALERT,
122 + 1, 1, 0, -1,
123 + 0x1000,
124 + 0x1000,
125 + &time))) {
126 + //ConsoleOutput(ErrorCreatePipe);
127 + DOUT("failed to create cmd pipe");
128 + return;
129 + }
130 +
131 + hThread = IthCreateThread(RecvThread, (DWORD)hTextPipe);
132 + man->RegisterPipe(hTextPipe, hCmdPipe, hThread);
133 +}
134 +
135 +void DetachFromProcess(DWORD pid)
136 +{
137 + HANDLE hMutex = INVALID_HANDLE_VALUE,
138 + hEvent = INVALID_HANDLE_VALUE;
139 + //try {
140 + IO_STATUS_BLOCK ios;
141 + ProcessRecord *pr = man->GetProcessRecord(pid);
142 + if (!pr)
143 + return;
144 + //IthBreak();
145 + hEvent = IthCreateEvent(nullptr);
146 + if (STATUS_PENDING == NtFsControlFile(
147 + man->GetCmdHandleByPID(pid),
148 + hEvent,
149 + 0,0,
150 + &ios,
151 + CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0),
152 + 0,0,0,0))
153 + NtWaitForSingleObject(hEvent, 0, 0);
154 + NtClose(hEvent);
155 + //hEvent = INVALID_HANDLE_VALUE;
156 +
157 + WCHAR mutex[0x20];
158 + swprintf(mutex, ITH_DETACH_MUTEX_ L"%d", pid);
159 + hMutex = IthOpenMutex(mutex);
160 + if (hMutex != INVALID_HANDLE_VALUE) {
161 + NtWaitForSingleObject(hMutex, 0, 0);
162 + NtReleaseMutant(hMutex, 0);
163 + NtClose(hMutex);
164 + //hMutex = INVALID_HANDLE_VALUE;
165 + }
166 +
167 + //} catch (...) {
168 + // if (hEvent != INVALID_HANDLE_VALUE)
169 + // NtClose(hEvent);
170 + // else if (hMutex != INVALID_HANDLE_VALUE) {
171 + // NtWaitForSingleObject(hMutex, 0, 0);
172 + // NtReleaseMutant(hMutex, 0);
173 + // NtClose(hMutex);
174 + // }
175 + //}
176 +
177 + //NtSetEvent(hDetachEvent, 0);
178 + if (::running)
179 + NtSetEvent(hPipeExist, 0);
180 +}
181 +
182 +// jichi 9/27/2013: I don't need this
183 +//void OutputDWORD(DWORD d)
184 +//{
185 +// WCHAR str[0x20];
186 +// swprintf(str, L"%.8X", d);
187 +// ConsoleOutput(str);
188 +//}
189 +
190 +DWORD WINAPI RecvThread(LPVOID lpThreadParameter)
191 +{
192 + HANDLE hTextPipe = (HANDLE)lpThreadParameter;
193 +
194 + IO_STATUS_BLOCK ios;
195 + NtFsControlFile(hTextPipe,
196 + 0, 0, 0,
197 + &ios,
198 + CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_CONNECT, 0, 0),
199 + 0, 0, 0, 0);
200 + if (!::running) {
201 + NtClose(hTextPipe);
202 + return 0;
203 + }
204 +
205 + BYTE *buff;
206 +
207 + enum { PipeBufferSize = 0x1000 };
208 + buff = new BYTE[PipeBufferSize];
209 + ::memset(buff, 0, PipeBufferSize); // jichi 8/27/2013: zero memory, or it will crash wine on start up
210 +
211 + // 10/19/2014 jichi: there are totally three words received
212 + // See: hook/rpc/pipe.cc
213 + // struct {
214 + // DWORD pid;
215 + // TextHook *man;
216 + // DWORD module;
217 + // //DWORD engine;
218 + // } u;
219 + enum { module_struct_size = 12 };
220 + NtReadFile(hTextPipe, 0, 0, 0, &ios, buff, module_struct_size, 0, 0);
221 +
222 + // jichi 7/2/2015: This must be consistent with the struct declared in vnrhook/pipe.cc
223 + DWORD pid = *(DWORD *)buff,
224 + module = *(DWORD *)(buff + 0x8),
225 + hookman = *(DWORD *)(buff + 0x4);
226 + //engine = *(DWORD *)(buff + 0xc);
227 + man->RegisterProcess(pid, hookman, module);
228 +
229 + // jichi 9/27/2013: why recursion?
230 + CreateNewPipe();
231 +
232 + //NtClose(IthCreateThread(UpdateWindows,0));
233 + while (::running) {
234 + if (!NT_SUCCESS(NtReadFile(hTextPipe,
235 + 0, 0, 0,
236 + &ios,
237 + buff,
238 + 0xf80,
239 + 0, 0)))
240 + break;
241 +
242 + enum { data_offset = 0xc }; // jichi 10/27/2013: Seem to be the data offset in the pipe
243 +
244 + DWORD RecvLen = ios.uInformation;
245 + if (RecvLen < data_offset)
246 + break;
247 + DWORD hook = *(DWORD *)buff;
248 +
249 + union { DWORD retn; DWORD cmd_type; };
250 + union { DWORD split; DWORD new_engine_type; };
251 +
252 + retn = *(DWORD *)(buff + 4);
253 + split = *(DWORD *)(buff + 8);
254 +
255 + buff[RecvLen] = 0;
256 + buff[RecvLen + 1] = 0;
257 +
258 + if (hook == HOST_NOTIFICATION) {
259 + switch (cmd_type) {
260 + case HOST_NOTIFICATION_NEWHOOK:
261 + {
262 + static long lock;
263 + while (InterlockedExchange(&lock, 1) == 1);
264 + ProcessEventCallback new_hook = man->ProcessNewHook();
265 + if (new_hook)
266 + new_hook(pid);
267 + lock = 0;
268 + } break;
269 + case HOST_NOTIFICATION_TEXT:
270 + //qDebug() << ((LPCSTR)(buff + 8));
271 + break;
272 + }
273 + } else {
274 + // jichi 9/28/2013: Debug raw data
275 + //ITH_DEBUG_DWORD9(RecvLen - 0xc,
276 + // buff[0xc], buff[0xd], buff[0xe], buff[0xf],
277 + // buff[0x10], buff[0x11], buff[0x12], buff[0x13]);
278 +
279 + const BYTE *data = buff + data_offset; // th
280 + int len = RecvLen - data_offset;
281 + bool space = ::has_leading_space(data, len);
282 + if (space) {
283 + const BYTE *it = ::Filter(data, len);
284 + len -= it - data;
285 + data = it;
286 + }
287 + if (len >> 31) // jichi 10/27/2013: len is too large, which seldom happens
288 + len = 0;
289 + //man->DispatchText(pid, len ? data : nullptr, hook, retn, split, len, space);
290 + man->DispatchText(pid, data, hook, retn, split, len, space);
291 + }
292 + }
293 +
294 + EnterCriticalSection(&detach_cs);
295 +
296 + HANDLE hDisconnect = IthCreateEvent(nullptr);
297 +
298 + if (STATUS_PENDING == NtFsControlFile(
299 + hTextPipe,
300 + hDisconnect,
301 + 0, 0,
302 + &ios,
303 + CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0),
304 + 0, 0, 0, 0))
305 + NtWaitForSingleObject(hDisconnect, 0, 0);
306 +
307 + NtClose(hDisconnect);
308 + DetachFromProcess(pid);
309 + man->UnRegisterProcess(pid);
310 +
311 + //NtClearEvent(hDetachEvent);
312 +
313 + LeaveCriticalSection(&detach_cs);
314 + delete[] buff;
315 +
316 + if (::running)
317 + DOUT("detached");
318 +
319 + //if (::running) {
320 + // swprintf((LPWSTR)buff, FormatDetach, pid);
321 + // ConsoleOutput((LPWSTR)buff);
322 + // NtClose(IthCreateThread(UpdateWindows, 0));
323 + //}
324 + return 0;
325 +}
326 +
327 +// EOF
1 +#pragma once
2 +
3 +// settings.h
4 +// 8/24/2013 jichi
5 +
6 +struct Settings {
7 + //bool debug; // whether output debug messages using pipes
8 + int splittingInterval;// time to split text into sentences
9 + bool clipboardFlag;
10 +
11 + Settings() : splittingInterval(200),
12 + clipboardFlag(false)
13 + {}
14 +
15 +};
16 +
17 +// EOF
1 +// textthread.cc
2 +// 8/24/2013 jichi
3 +// Branch IHF/TextThread.cpp, rev 133
4 +// 8/24/2013 TODO: Clean up this file
5 +
6 +#ifdef _MSC_VER
7 +# pragma warning (disable:4100) // C4100: unreference formal parameter
8 +#endif // _MSC_VER
9 +
10 +#include "settings.h"
11 +#include "textthread.h"
12 +//#include "wintimer/wintimer.h"
13 +#include "vnrhook/include/const.h"
14 +#include "ithsys/ithsys.h"
15 +#include <stdio.h>
16 +
17 +MK_BASIC_TYPE(BYTE)
18 +MK_BASIC_TYPE(ThreadParameter)
19 +
20 +static DWORD MIN_DETECT = 0x20;
21 +static DWORD MIN_REDETECT = 0x80;
22 +//#define MIN_DETECT 0x20
23 +//#define MIN_REDETECT 0x80
24 +#ifndef CURRENT_SELECT
25 +# define CURRENT_SELECT 0x1000
26 +#endif
27 +#ifndef REPEAT_NUMBER_DECIDED
28 +# define REPEAT_NUMBER_DECIDED 0x2000
29 +#endif
30 +
31 +DWORD GetHookName(LPSTR str, DWORD pid, DWORD hook_addr,DWORD max);
32 +
33 +extern Settings *settings;
34 +extern HWND hMainWnd;
35 +void CALLBACK NewLineBuff(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
36 +{
37 + KillTimer(hwnd,idEvent);
38 + TextThread *id=(TextThread*)idEvent;
39 +
40 + if (id->Status()&CURRENT_SELECT)
41 + //texts->SetLine();
42 + id->CopyLastToClipboard();
43 + id->SetNewLineFlag();
44 +}
45 +
46 +// jichi 10/27/2013: removed
47 +//void CALLBACK NewLineConsole(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
48 +//{
49 +// KillTimer(hwnd,idEvent);
50 +// TextThread *id=(TextThread*)idEvent;
51 +// if (id->Status()&USING_UNICODE)
52 +// id->AddText((BYTE*)L"\r\n",4,true,true);
53 +// if (id->Status()&CURRENT_SELECT)
54 +// {
55 +// //texts->SetLine();
56 +// }
57 +//}
58 +
59 +// jichi 10/27/2013: removed
60 +//void ReplaceSentence(BYTE* text, int len)
61 +//{
62 +// __asm int 3
63 +//}
64 +
65 +TextThread::TextThread(DWORD id, DWORD hook, DWORD retn, DWORD spl, WORD num) :
66 + //,tp
67 + thread_number(num)
68 + // jichi 9/21/2013: zero all fields
69 + , link_number(-1)
70 + , last (0)
71 + , align_space(0)
72 + , repeat_single(0)
73 + , repeat_single_current(0)
74 + , repeat_single_count(0)
75 + , repeat_detect_count(0)
76 + , head(new RepeatCountNode())
77 + , link(nullptr)
78 + //, filter(nullptr)
79 + , output(nullptr)
80 + , app_data(nullptr)
81 + //, comment(nullptr)
82 + , thread_string(nullptr)
83 + , timer(0)
84 + , status (0)
85 + , repeat_detect_limit(0x80)
86 + , last_sentence(0)
87 + , prev_sentence(0)
88 + , sentence_length(0)
89 + , repeat_index(0)
90 + , last_time(0)
91 +// , tp({id, hook, retn, spl})
92 +{
93 + tp.pid = id;
94 + tp.hook = hook;
95 + tp.retn = retn;
96 + tp.spl = spl;
97 + //head = new RepeatCountNode;
98 + //::memset(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory
99 + //link_number = -1;
100 + //repeat_detect_limit = 0x80;
101 + //filter = nullptr;
102 + //output = nullptr;
103 +}
104 +TextThread::~TextThread()
105 +{
106 + //KillTimer(hMainWnd,timer);
107 + RepeatCountNode *t = head,
108 + *tt;
109 + while (t) {
110 + tt = t;
111 + t = tt->next;
112 + delete tt;
113 + }
114 + head = nullptr;
115 + //if (comment) {
116 + // delete[] comment;
117 + // comment = nullptr;
118 + //}
119 + if (thread_string)
120 + delete[] thread_string;
121 +}
122 +void TextThread::Reset()
123 +{
124 + //timer=0;
125 + last_sentence = 0;
126 + //if (comment) {
127 + // delete[] comment;
128 + // comment = nullptr;
129 + //}
130 + MyVector::Reset();
131 +}
132 +void TextThread::RemoveSingleRepeatAuto(const BYTE *con, int &len)
133 +{
134 +#ifdef ITH_DISABLE_REPEAT // jichi 9/28/2013: only for debugging purpose
135 + return;
136 +#endif // ITH_DISABLE_REPEAT
137 + WORD *text = (WORD *)con;
138 + if (len <= 2) {
139 + if (repeat_single) {
140 + if (repeat_single_count<repeat_single&&
141 + last == *text) {
142 + len = 0;
143 + repeat_single_count++;
144 + } else {
145 + last = *text;
146 + repeat_single_count=0;
147 + }
148 + }
149 + if (status & REPEAT_NUMBER_DECIDED) {
150 + if (++repeat_detect_count>MIN_REDETECT) {
151 + repeat_detect_count = 0;
152 + status ^= REPEAT_NUMBER_DECIDED;
153 + last = 0;
154 + RepeatCountNode *t = head,
155 + *tt;
156 + while (t) {
157 + tt = t;
158 + t = tt->next;
159 + delete tt;
160 + }
161 + head = new RepeatCountNode;
162 + ::memset(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory
163 + }
164 + } else {
165 + repeat_detect_count++;
166 + if (last == *text)
167 + repeat_single_current++;
168 + else {
169 + if (last == 0) {
170 + last = *text;
171 + return;
172 + }
173 + if (repeat_single_current == 0) {
174 + status |= REPEAT_NUMBER_DECIDED;
175 + repeat_single = 0;
176 + return;
177 + }
178 + last = *text;
179 + RepeatCountNode *it = head;
180 + if (repeat_detect_count > MIN_DETECT) {
181 + while (it = it->next)
182 + if (it->count>head->count) {
183 + head->count=it->count;
184 + head->repeat=it->repeat;
185 + }
186 + repeat_single = head->repeat;
187 + repeat_single_current = 0;
188 + repeat_detect_count = 0;
189 + status |= REPEAT_NUMBER_DECIDED;
190 + DWORD repeat_sc = repeat_single*4;
191 + if (repeat_sc > MIN_DETECT) {
192 + MIN_DETECT <<= 1;
193 + MIN_REDETECT <<= 1;
194 + }
195 + } else {
196 + bool flag=true;
197 + while (it) {
198 + if (it->repeat == repeat_single_current) {
199 + it->count++;
200 + flag = false;
201 + break;
202 + }
203 + it=it->next;
204 + }
205 + if (flag) {
206 + RepeatCountNode *n = new RepeatCountNode;
207 + n->count = 1;
208 + n->repeat = repeat_single_current;
209 + n->next = head->next;
210 + head->next = n;
211 + }
212 + repeat_single_current = 0;
213 + } //Decide repeat_single
214 + } //Check Repeat
215 + } //repeat_single decided?
216 + } //len
217 + else {
218 + status |= REPEAT_NUMBER_DECIDED;
219 + repeat_single = 0;
220 + }
221 +}
222 +
223 +void TextThread::RemoveSingleRepeatForce(BYTE *con,int &len)
224 +{
225 + // jichi 9/1/2013: manual repetition count removed
226 + WORD *text = (WORD *)con;
227 + //if (repeat_single_count<setman->GetValue(SETTING_REPEAT_COUNT)&&last==*text) {
228 + // len=0;
229 + // repeat_single_count++;
230 + //}
231 + //else
232 + {
233 + last = *text;
234 + repeat_single_count=0;
235 + }
236 +}
237 +void TextThread::RemoveCyclicRepeat(BYTE* &con, int &len)
238 +{
239 + DWORD current_time = GetTickCount();
240 + if (status & REPEAT_SUPPRESS) {
241 + if (current_time - last_time < (unsigned)settings->splittingInterval &&
242 + ::memcmp(storage + last_sentence + repeat_index, con, len) == 0) {
243 + repeat_index += len;
244 + if (repeat_index>=sentence_length)
245 + repeat_index -= sentence_length;
246 + len = 0;
247 + } else {
248 + repeat_index = 0;
249 + status &= ~REPEAT_SUPPRESS;
250 + }
251 + } else if (status & REPEAT_DETECT) {
252 + if (::memcmp(storage + last_sentence + repeat_index, con, len) == 0) {
253 + int half_length=repeat_index+len;
254 + if (::memcmp(storage + last_sentence, storage + last_sentence + half_length, repeat_index) == 0) {
255 + len=0;
256 + sentence_length=half_length;
257 + status&=~REPEAT_DETECT;
258 + status|=REPEAT_SUPPRESS;
259 +
260 + // jichi 10/27/2013: Not used
261 + //if (status&CURRENT_SELECT)
262 + // ReplaceSentence(storage+last_sentence+half_length,repeat_index);
263 + ClearMemory(last_sentence+half_length,repeat_index);
264 + used-=repeat_index;
265 + repeat_index=0;
266 + }
267 + else
268 + repeat_index += len;
269 + }
270 + else {
271 + repeat_index=0;
272 + status &= ~REPEAT_DETECT;
273 + }
274 + } else {
275 + if (sentence_length == 0)
276 + return;
277 + else if (len <= (int)sentence_length) {
278 + if (memcmp(storage + last_sentence, con, len) == 0) {
279 + status |= REPEAT_DETECT;
280 + repeat_index = len;
281 + if (repeat_index == sentence_length) {
282 + repeat_index = 0;
283 + len = 0;
284 + }
285 + } else if (sentence_length > repeat_detect_limit) {
286 + if (len > 2) {
287 + DWORD u = used;
288 + while (memcmp(storage + u - len, con, len) == 0)
289 + u -= len;
290 + ClearMemory(u, used - u);
291 + used = u;
292 + repeat_index = 0;
293 + // jichi 10/27/2013: Not used
294 + //if (status & CURRENT_SELECT)
295 + // ReplaceSentence(storage + last_sentence, used - u);
296 + status |= REPEAT_SUPPRESS;
297 + len = 0;
298 + } else if (len <= 2)
299 + {
300 + WORD tmp = *(WORD *)(storage + last_sentence);
301 + DWORD index, last_index, tmp_len;
302 + index = used-len;
303 + if (index < last_sentence)
304 + index = last_sentence;
305 + //Locate position of current input.
306 +_again:
307 + *(WORD *)(storage+last_sentence) = *(WORD *)con;
308 + while (*(WORD *)(storage + index) != *(WORD *)con)
309 + index--;
310 + *(WORD *)(storage + last_sentence) = tmp;
311 + if (index > last_sentence) {
312 + tmp_len = used - index;
313 + if (tmp_len <= 2) {
314 + repeat_detect_limit += 0x40;
315 + last_time = current_time;
316 + return;
317 + }
318 + if (index - last_sentence >= tmp_len &&
319 + memcmp(storage + index - tmp_len, storage + index, tmp_len) == 0) {
320 + repeat_detect_limit = 0x80;
321 + sentence_length =tmp_len;
322 + index -= tmp_len;
323 + while (memcmp(storage + index - sentence_length, storage + index, sentence_length) == 0)
324 + index -= sentence_length;
325 + repeat_index = 2;
326 + len = 0;
327 + last_index = index;
328 + if (status&USING_UNICODE) {
329 + while (storage[index] == storage[index + sentence_length])
330 + index -= 2;
331 + index += 2;
332 + while (true) {
333 + tmp = *(WORD *)(storage + index);
334 + if (tmp >= 0x3000 && tmp < 0x3020)
335 + index += 2;
336 + else
337 + break;
338 + }
339 + } else {
340 + DWORD last_char_len;
341 + while (storage[index] == storage[index + sentence_length]) {
342 + last_char_len = LeadByteTable[storage[index]];
343 + index -= last_char_len;
344 + }
345 + index += last_char_len;
346 + while (storage[index] == 0x81) {
347 + if ((storage[index+1]>>4) == 4)
348 + index += 2;
349 + else
350 + break;
351 + }
352 + }
353 + repeat_index += last_index - index;
354 + status |= REPEAT_SUPPRESS;
355 + last_sentence = index;
356 +
357 + index += sentence_length;
358 + // jichi 10/27/2013: Not used
359 + //if (status&CURRENT_SELECT)
360 + // ReplaceSentence(storage + index, used - index);
361 +
362 + ClearMemory(index, used - index);
363 + //memset(storage + index, 0, used - index);
364 + used = index;
365 + } else {
366 + index--;
367 + goto _again;
368 + }
369 + }
370 + else
371 + repeat_detect_limit += 0x40;
372 + }
373 + }
374 + }
375 + }
376 + last_time = current_time;
377 +}
378 +
379 +void TextThread::ResetRepeatStatus()
380 +{
381 + last=0;
382 + repeat_single=0;
383 + repeat_single_current=0;
384 + repeat_single_count=0;
385 + repeat_detect_count=0;
386 + RepeatCountNode *t = head->next,
387 + *tt;
388 + while (t) {
389 + tt = t;
390 + t = tt->next;
391 + delete tt;
392 + }
393 + //head=new RepeatCountNode;
394 + head->count = head->repeat = 0;
395 + status &= ~REPEAT_NUMBER_DECIDED;
396 +}
397 +void TextThread::AddLineBreak()
398 +{
399 + if (sentence_length == 0) return;
400 + if (status&BUFF_NEWLINE)
401 + {
402 + prev_sentence=last_sentence;
403 + sentence_length=0;
404 + if (status & USING_UNICODE)
405 + AddToStore((BYTE *)L"\r\n\r\n", 8);
406 + else
407 + AddToStore((BYTE *)"\r\n\r\n", 4);
408 + if (output)
409 + output(this, 0, 8, TRUE, app_data, false); // jichi 10/27/2013: space is false
410 + last_sentence = used;
411 + status &= ~BUFF_NEWLINE;
412 + }
413 +}
414 +void TextThread::AddText(const BYTE *con, int len, bool new_line, bool space)
415 +{
416 + if (!con || (len <= 0 && !space))
417 + return;
418 + if (len && !new_line) {
419 + // jichi 9/1/2013: manual repetition count removed
420 + //if (setman->GetValue(SETTING_REPEAT_COUNT)) {
421 + // status|=REPEAT_NUMBER_DECIDED;
422 + // RemoveSingleRepeatForce(con,len);
423 + //}
424 + //else
425 + RemoveSingleRepeatAuto(con, len);
426 + if (len <= 0 && !space)
427 + return;
428 + }
429 +
430 + // jichi 9/1/2013: manual repetition count removed
431 + //if(setman->GetValue(SETTING_CYCLIC_REMOVE)) {
432 + // //if (status & REPEAT_NUMBER_DECIDED)
433 + // RemoveCyclicRepeat(con,len);
434 + //}
435 + //if (len <= 0)
436 + // return;
437 +
438 + // jichi 10/27/2013: User-defined filter callback is disabled
439 + //if (filter)
440 + // len = filter(this, con,len, new_line, app_data);
441 + //if (len <= 0)
442 + // return;
443 +
444 + if (len && sentence_length == 0) {
445 + if (status & USING_UNICODE) {
446 + if (*(WORD *)con == 0x3000) { // jichi 10/27/2013: why skip unicode space?!
447 + con += 2;
448 + len -= 2;
449 + }
450 + } else if (*(WORD *)con == 0x4081) {
451 + con += 2;
452 + len -= 2;
453 + }
454 +
455 + if (len <= 0 && !space)
456 + return;
457 + }
458 +
459 + if (status & BUFF_NEWLINE)
460 + AddLineBreak();
461 +
462 + if (len)
463 + if (new_line) {
464 + prev_sentence = last_sentence;
465 + last_sentence = used + 4;
466 + if (status & USING_UNICODE)
467 + last_sentence += 4;
468 + sentence_length = 0;
469 + } else {
470 + SetNewLineTimer();
471 + if (link) {
472 + const BYTE *send = con;
473 + int l = len;
474 + if (status & USING_UNICODE) { // Although unlikely, a thread and its link may have different encoding.
475 + if ((link->Status() & USING_UNICODE) == 0) {
476 + send = new BYTE[l];
477 + //::memset(send, 0, l); // jichi 9/26/2013: zero memory
478 + l = WC_MB((LPWSTR)con, (char *)send);
479 + }
480 + link->AddTextDirect(send, l, space);
481 + } else {
482 + if (link->Status() & USING_UNICODE) {
483 + size_t sz = len * 2 + 2;
484 + send = new BYTE[sz];
485 + //::memset(send, 0, sz); // jichi 9/26/2013: zero memory
486 + l = MB_WC((char *)con, (LPWSTR)send) << 1;
487 + }
488 + link->AddTextDirect(send, l, space);
489 + }
490 + link->SetNewLineTimer();
491 + if (send != con)
492 + delete[] send;
493 + }
494 + sentence_length += len;
495 + }
496 +
497 + BYTE *data = const_cast<BYTE *>(con); // jichi 10/27/2013: TODO: Figure out where con is modified
498 + if (output)
499 + len = output(this, data, len, new_line, app_data, space);
500 + if (AddToStore(data, len)) {
501 + //sentence_length += len;
502 + /*ResetRepeatStatus();
503 + last_sentence=0;
504 + prev_sentence=0;
505 + sentence_length=len;
506 + repeat_index=0;
507 + status&=~REPEAT_DETECT|REPEAT_SUPPRESS; */
508 + }
509 +}
510 +
511 +void TextThread::AddTextDirect(const BYTE* con, int len, bool space) // Add to store directly, penetrating repetition filters.
512 +{
513 + // jichi 10/27/2013: Accordig to the logic, both len and con must be > 0
514 + if (status & BUFF_NEWLINE)
515 + AddLineBreak();
516 + //SetNewLineTimer();
517 + if (link) {
518 + const BYTE *send = con;
519 + int l = len;
520 + if (status & USING_UNICODE) {
521 + if ((link->Status()&USING_UNICODE) == 0) {
522 + send = new BYTE[l];
523 + //::memset(send, 0, l); // jichi 9/26/2013: zero memory
524 + l = WC_MB((LPWSTR)con,(char*)send);
525 + }
526 + link->AddText(send, l, false, space); // new_line is false
527 + } else {
528 + if (link->Status()&USING_UNICODE) {
529 + size_t sz = len * 2 + 2;
530 + send = new BYTE[sz];
531 + //::memset(send, 0, sz); // jichi 9/26/2013: zero memory
532 + l = MB_WC((char *)con, (LPWSTR)send) << 1;
533 + }
534 + link->AddText(send, l, false, space); // new_line is false
535 + }
536 + link->SetNewLineTimer();
537 + if (send != con)
538 + delete[] send;
539 + }
540 + sentence_length += len;
541 +
542 + BYTE *data = const_cast<BYTE *>(con); // jichi 10/27/2013: TODO: Figure out where con is modified
543 + if (output)
544 + len = output(this, data, len, false, app_data, space);
545 + AddToStore(data, len);
546 +}
547 +
548 +DWORD TextThread::GetEntryString(LPSTR str, DWORD max)
549 +{
550 + DWORD len = 0;
551 + if (str && max > 0x40) {
552 + max--;
553 + if (thread_string) {
554 + len = ::strlen(thread_string);
555 + len = len < max ? len : max;
556 + memcpy(str, thread_string, len);
557 + str[len] = 0;
558 +
559 + } else {
560 + len = ::sprintf(str, "%.4X:%.4d:0x%08X:0x%08X:0x%08X:",
561 + thread_number, tp. pid, tp.hook, tp.retn, tp.spl);
562 +
563 + len += GetHookName(str + len, tp.pid, tp.hook, max - len);
564 + thread_string = new char[len + 1];
565 + //::memset(thread_string, 0, (len+1) * sizeof(wchar_t)); // jichi 9/26/2013: zero memory
566 + thread_string[len] = 0;
567 + ::memcpy(thread_string, str, len);
568 + }
569 + //if (comment) {
570 + // str += len;
571 + // max--;
572 + // DWORD cl = wcslen(comment);
573 + // if (len + cl >= max)
574 + // cl = max - len;
575 + // *str++ = L'-';
576 + // memcpy(str, comment, cl << 1);
577 + // str[cl] = 0;
578 + // len += cl;
579 + //}
580 + }
581 + return len;
582 +}
583 +// jichi 9/28/2013: removed
584 +//void TextThread::CopyLastSentence(LPWSTR str)
585 +//{
586 +// int i,j,l;
587 +// if (status&USING_UNICODE)
588 +// {
589 +// if (used>8)
590 +// {
591 +// j=used>0xF0?(used-0xF0):0;
592 +// for (i=used-0xA;i>=j;i-=2)
593 +// {
594 +// if (*(DWORD*)(storage+i)==0xA000D) break;
595 +// }
596 +// if (i>=j)
597 +// {
598 +// l=used-i;
599 +// if (i>j) l-=4;
600 +// j=4;
601 +// }
602 +// else
603 +// {
604 +// i+=2;
605 +// l=used-i;
606 +// j=0;
607 +// }
608 +// memcpy(str,storage+i+j,l);
609 +// str[l>>1]=0;
610 +// }
611 +// else
612 +// {
613 +// memcpy(str,storage,used);
614 +// str[used>>1]=0;
615 +// }
616 +// }
617 +// else
618 +// {
619 +// if (used>4)
620 +// {
621 +// j=used>0x80?(used-0x80):0;
622 +// for (i=used-5;i>=j;i--)
623 +// {
624 +// if (*(DWORD*)(storage+i)==0xA0D0A0D) break;
625 +// }
626 +// if (i>=j)
627 +// {
628 +// l=used-i;
629 +// if (i>j) l-=4;
630 +// j=4;
631 +// }
632 +// else
633 +// {
634 +// i++;
635 +// l=used-i;
636 +// j=0;
637 +// }
638 +// size_t sz = (l|0xF) + 1;
639 +// char *buff = new char[sz];
640 +// //memset(buff, 0, sz); // jichi 9/26/2013: zero memory
641 +// memcpy(buff, storage + i + j, l);
642 +// buff[l] = 0;
643 +// str[MB_WC(buff, str)] = 0;
644 +// delete[] buff;
645 +// } else {
646 +// storage[used] = 0;
647 +// str[MB_WC((char *)storage, str)] = 0;
648 +// }
649 +// }
650 +//}
651 +
652 +static char clipboard_buffer[0x400];
653 +// jichi 8/25/2013: clipboard removed
654 +void CopyToClipboard(void* str,bool unicode, int len)
655 +{
656 + if (settings->clipboardFlag && str && len > 0)
657 + {
658 + int size=(len*2|0xF)+1;
659 + if (len>=1022) return;
660 + memcpy(clipboard_buffer,str,len);
661 + *(WORD*)(clipboard_buffer+len)=0;
662 + HGLOBAL hCopy;
663 + LPWSTR copy;
664 + if (OpenClipboard(0))
665 + {
666 + if (hCopy=GlobalAlloc(GMEM_MOVEABLE,size))
667 + {
668 + if (copy=(LPWSTR)GlobalLock(hCopy))
669 + {
670 + if (unicode)
671 + {
672 + memcpy(copy,clipboard_buffer,len+2);
673 + }
674 + else
675 + copy[MB_WC(clipboard_buffer,copy)]=0;
676 + GlobalUnlock(hCopy);
677 + EmptyClipboard();
678 + SetClipboardData(CF_UNICODETEXT,hCopy);
679 + }
680 + }
681 + CloseClipboard();
682 + }
683 + }
684 +}
685 +void TextThread::CopyLastToClipboard()
686 +{
687 + // jichi 8/25/2013: clipboard removed
688 + CopyToClipboard(storage+last_sentence,(status&USING_UNICODE)>0,used-last_sentence);
689 +}
690 +
691 +//void TextThread::ResetEditText()
692 +//{
693 +// //__asm int 3;
694 +// WCHAR str[0x20];
695 +// swprintf(str,L"%.8X",_ReturnAddress());
696 +//}
697 +
698 +// jichi 9/25/2013: Removed
699 +//void TextThread::ExportTextToFile(LPWSTR) //filename)
700 +//{
701 +// HANDLE hFile=IthCreateFile(filename,FILE_WRITE_DATA,0,FILE_OPEN_IF);
702 +// if (hFile==INVALID_HANDLE_VALUE) return;
703 +// EnterCriticalSection(&cs_store);
704 +// IO_STATUS_BLOCK ios;
705 +// LPVOID buffer=storage;
706 +// DWORD len=used;
707 +// BYTE bom[4]={0xFF,0xFE,0,0};
708 +// LARGE_INTEGER offset={2,0};
709 +// if ((status&USING_UNICODE)==0)
710 +// {
711 +// len=MB_WC_count((char*)storage,used);
712 +// buffer = new wchar_t[len+1];
713 +// MB_WC((char*)storage,(wchar_t*)buffer);
714 +// len<<=1;
715 +// }
716 +// NtWriteFile(hFile,0,0,0,&ios,bom,2,0,0);
717 +// NtWriteFile(hFile,0,0,0,&ios,buffer,len,&offset,0);
718 +// NtFlushBuffersFile(hFile,&ios);
719 +// if (buffer !=storage)
720 +// delete[] buffer;
721 +// NtClose(hFile);
722 +// LeaveCriticalSection(&cs_store);
723 +//}
724 +
725 +//void TextThread::SetComment(LPWSTR str)
726 +//{
727 +// if (comment)
728 +// delete[] comment;
729 +// size_t sz = wcslen(str);
730 +// comment = new wchar_t[sz + 1];
731 +// comment[sz] = 0;
732 +// wcscpy(comment, str);
733 +//}
734 +
735 +void TextThread::SetNewLineFlag() { status |= BUFF_NEWLINE; }
736 +
737 +bool TextThread::CheckCycle(TextThread* start)
738 +{
739 + if (link==start||this==start) return true;
740 + if (link==0) return false;
741 + return link->CheckCycle(start);
742 +}
743 +void TextThread::SetNewLineTimer()
744 +{
745 + if (thread_number == 0)
746 + // jichi 10/27/2013: Not used
747 + timer = 0; //SetTimer(hMainWnd,(UINT_PTR)this, settings->splittingInterval, NewLineConsole);
748 + else
749 + timer = SetTimer(hMainWnd, (UINT_PTR)this, settings->splittingInterval, NewLineBuff);
750 +}
751 +
752 +DWORD TextThread::GetThreadString(LPSTR str, DWORD max)
753 +{
754 + DWORD len = 0;
755 + if (max) {
756 + char buffer[0x200];
757 + char c;
758 + if (thread_string == nullptr)
759 + GetEntryString(buffer, 0x200); //This will allocate thread_string.
760 + LPSTR end = thread_string;
761 + for (; *end; end++);
762 + c = thread_string[0];
763 + thread_string[0] = ':';
764 + LPSTR p1 = end;
765 + for (; *p1 != ':'; p1--);
766 + thread_string[0] = c;
767 + if (p1 == thread_string)
768 + return 0;
769 + p1++;
770 + len = end - p1;
771 + if (len >= max)
772 + len = max - 1;
773 + ::memcpy(str, p1, len);
774 + str[len] = 0;
775 + }
776 +
777 + return len;
778 +}
779 +void TextThread::UnLinkAll()
780 +{
781 + if (link) link->UnLinkAll();
782 + link = 0;
783 + link_number = -1;
784 +}
785 +
786 +// EOF
1 +#pragma once
2 +
3 +// textthread.h
4 +// 8/23/2013 jichi
5 +// Branch: ITH/TextThread.h, rev 120
6 +
7 +#include "host/textthread_p.h"
8 +#include <intrin.h> // require _InterlockedExchange
9 +
10 +struct RepeatCountNode {
11 + short repeat;
12 + short count;
13 + RepeatCountNode *next;
14 +
15 + //RepeatCountNode() : repeat(0), count(0), next(nullptr) {}
16 +};
17 +
18 +struct ThreadParameter {
19 + DWORD pid; // jichi: 5/11/2014: The process ID
20 + DWORD hook;
21 + DWORD retn; // jichi 5/11/2014: The return address of the hook
22 + DWORD spl; // jichi 5/11/2014: the processed split value of the hook parameter
23 +};
24 +
25 +#define CURRENT_SELECT 0x1000
26 +#define REPEAT_NUMBER_DECIDED 0x2000
27 +#define BUFF_NEWLINE 0x4000
28 +#define CYCLIC_REPEAT 0x8000
29 +#define COUNT_PER_FOWARD 0x200
30 +#define REPEAT_DETECT 0x10000
31 +#define REPEAT_SUPPRESS 0x20000
32 +#define REPEAT_NEWLINE 0x40000
33 +
34 +class TextThread;
35 +typedef void (* ConsoleCallback)(LPCSTR text);
36 +typedef void (* ConsoleWCallback)(LPCWSTR text);
37 +typedef DWORD (* ThreadOutputFilterCallback)(TextThread *, BYTE *, DWORD, DWORD, PVOID, bool space); // jichi 10/27/2013: Add space
38 +typedef DWORD (* ThreadEventCallback)(TextThread *);
39 +
40 +//extern DWORD split_time,repeat_count,global_filter,cyclic_remove;
41 +
42 +class TextThread : public MyVector<BYTE, 0x200>
43 +{
44 +public:
45 + TextThread(DWORD pid, DWORD hook, DWORD retn, DWORD spl, WORD num);
46 + ~TextThread();
47 + //virtual void CopyLastSentence(LPWSTR str);
48 + //virtual void SetComment(LPWSTR);
49 + //virtual void ExportTextToFile(LPWSTR filename);
50 +
51 + virtual bool CheckCycle(TextThread *start);
52 + virtual DWORD GetThreadString(LPSTR str, DWORD max);
53 + virtual DWORD GetEntryString(LPSTR str, DWORD max = 0x200);
54 +
55 + void Reset();
56 + void AddText(const BYTE *con,int len, bool new_line, bool space); // jichi 10/27/2013: add const; remove console; add space
57 + void RemoveSingleRepeatAuto(const BYTE *con, int &len); // jichi 10/27/2013: add const
58 + void RemoveSingleRepeatForce(BYTE *con, int &len);
59 + void RemoveCyclicRepeat(BYTE *&con, int &len);
60 + void ResetRepeatStatus();
61 + void AddLineBreak();
62 + //void ResetEditText();
63 + void ComboSelectCurrent();
64 + void UnLinkAll();
65 + void CopyLastToClipboard();
66 +
67 + //void AdjustPrevRepeat(DWORD len);
68 + //void PrevRepeatLength(DWORD &len);
69 +
70 + //bool AddToCombo();
71 + bool RemoveFromCombo();
72 +
73 + void SetNewLineFlag();
74 + void SetNewLineTimer();
75 +
76 + BYTE *GetStore(DWORD *len) { if (len) *len = used; return storage; }
77 + DWORD LastSentenceLen() { return used - last_sentence; }
78 + DWORD PID() const { return tp.pid; }
79 + DWORD Addr() const {return tp.hook; }
80 + DWORD &Status() { return status; }
81 + WORD Number() const { return thread_number; }
82 + WORD &Last() { return last; }
83 + WORD &LinkNumber() { return link_number; }
84 + UINT_PTR &Timer() { return timer; }
85 + ThreadParameter *GetThreadParameter() { return &tp; }
86 + TextThread *&Link() { return link; }
87 + //LPCWSTR GetComment() { return comment; }
88 +
89 + ThreadOutputFilterCallback RegisterOutputCallBack(ThreadOutputFilterCallback cb, PVOID data)
90 + {
91 + app_data = data;
92 + return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&output,(long)cb);
93 + }
94 +
95 + ThreadOutputFilterCallback RegisterFilterCallBack(ThreadOutputFilterCallback cb, PVOID data)
96 + {
97 + app_data = data;
98 + return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&filter,(long)cb);
99 + }
100 +
101 + void SetRepeatFlag() { status |= CYCLIC_REPEAT; }
102 + void ClearNewLineFlag() { status &= ~BUFF_NEWLINE; }
103 + void ClearRepeatFlag() { status &= ~CYCLIC_REPEAT; }
104 +
105 +protected:
106 + void AddTextDirect(const BYTE *con, int len, bool space); // jichi 10/27/2013: add const; add space; change to protected
107 +
108 +private:
109 + ThreadParameter tp;
110 +
111 + WORD thread_number,
112 + link_number;
113 + WORD last,
114 + align_space;
115 + WORD repeat_single;
116 + WORD repeat_single_current;
117 + WORD repeat_single_count;
118 + WORD repeat_detect_count;
119 + RepeatCountNode *head;
120 +
121 + TextThread *link;
122 + ThreadOutputFilterCallback filter; // jichi 10/27/2013: Remove filter
123 + ThreadOutputFilterCallback output;
124 + PVOID app_data;
125 + LPSTR thread_string;
126 + UINT_PTR timer;
127 + DWORD status,repeat_detect_limit;
128 + DWORD last_sentence,
129 + prev_sentence,
130 + sentence_length,
131 + repeat_index,
132 + last_time;
133 +};
134 +
135 +// EOF
1 +#pragma once
2 +// textthread_p.h
3 +// 8/14/2013 jichi
4 +// Branch: ITH/main_template.h, rev 66
5 +
6 +#include <windows.h>
7 +
8 +template <typename T>
9 +void Release(const T &p) { delete p; }
10 +
11 +// Prevent memory release.
12 +// Used when T is basic types and will be automatically released (on stack).
13 +#define MK_BASIC_TYPE(T) \
14 + template<> \
15 + void Release<T>(const T &p) {}
16 +
17 +template<class T>
18 +struct BinaryEqual {
19 + bool operator ()(const T &a, const T &b, DWORD) { return a == b; }
20 +};
21 +
22 +template<class T, int default_size, class fComp=BinaryEqual<T> >
23 +class MyVector
24 +{
25 +public:
26 + MyVector() : size(default_size), used(0)
27 + {
28 + InitializeCriticalSection(&cs_store);
29 + storage = new T[size];
30 + // jichi 9/21/2013: zero memory
31 + // This would cause trouble if T is not an atomic type
32 + ::memset(storage, 0, sizeof(T) * size);
33 + }
34 +
35 + virtual ~MyVector()
36 + {
37 + if (storage)
38 + delete[] storage;
39 + DeleteCriticalSection(&cs_store);
40 + storage = 0;
41 + }
42 +
43 + void Reset()
44 + {
45 + EnterCriticalSection(&cs_store);
46 + for (int i = 0; i < used; i++) {
47 + Release<T>(storage[i]);
48 + storage[i] = T();
49 + }
50 + used = 0;
51 + LeaveCriticalSection(&cs_store);
52 + }
53 + void Remove(int index)
54 + {
55 + if (index>=used)
56 + return;
57 + Release<T>(storage[index]);
58 + for (int i = index; i < used; i++)
59 + storage[i] = storage[i+1];
60 + used--;
61 + }
62 + void ClearMemory(int offset, int clear_size)
63 + {
64 + if (clear_size < 0)
65 + return;
66 + EnterCriticalSection(&cs_store);
67 + if (offset+clear_size <= size)
68 + ::memset(storage+offset, 0, clear_size * sizeof(T)); // jichi 11/30/2013: This is the original code of ITH
69 + LeaveCriticalSection(&cs_store);
70 + //else __asm int 3
71 + }
72 + int AddToStore(T *con,int amount)
73 + {
74 + if (amount <= 0 || con == 0)
75 + return 0;
76 + int status = 0;
77 + EnterCriticalSection(&cs_store);
78 + if (amount + used + 2 >= size) {
79 + while (amount + used + 2 >= size)
80 + size<<=1;
81 + T *temp;
82 + if (size * sizeof(T) < 0x1000000) {
83 + temp = new T[size];
84 + if (size > used)
85 + ::memset(temp, 0, (size - used) * sizeof(T)); // jichi 9/25/2013: zero memory
86 + memcpy(temp, storage, used * sizeof(T));
87 + } else {
88 + size = default_size;
89 + temp = new T[size];
90 + ::memset(temp, 0, sizeof(T) * size); // jichi 9/25/2013: zero memory
91 + used = 0;
92 + status = 1;
93 + }
94 + delete[] storage;
95 + storage = temp;
96 + }
97 + memcpy(storage+used, con, amount * sizeof(T));
98 + used += amount;
99 + LeaveCriticalSection(&cs_store);
100 + return status;
101 + }
102 + int Find(const T &item, int start = 0, DWORD control = 0)
103 + {
104 + int c = -1;
105 + for (int i=start; i < used; i++)
106 + if (fCmp(storage[i],item,control)) {
107 + c=i;
108 + break;
109 + }
110 + //if (storage[i]==item) {c=i;break;}
111 + return c;
112 + }
113 + int Used() const { return used; }
114 + T *Storage() const { return storage; }
115 + void LockVector() { EnterCriticalSection(&cs_store); }
116 + void UnlockVector() { LeaveCriticalSection(&cs_store); }
117 +protected:
118 + CRITICAL_SECTION cs_store;
119 + int size,
120 + used;
121 + T *storage;
122 + fComp fCmp;
123 +};
124 +
125 +// EOF
126 +
127 +/*
128 +#ifndef ITH_STACK
129 +#define ITH_STACK
130 +template<class T, int default_size>
131 +class MyStack
132 +{
133 +public:
134 + MyStack(): index(0) {}
135 + void push_back(const T& e)
136 + {
137 + if (index<default_size)
138 + s[index++]=e;
139 + }
140 + void pop_back()
141 + {
142 + index--;
143 + }
144 + T& back()
145 + {
146 + return s[index-1];
147 + }
148 + T& operator[](int i) {return s[i];}
149 + int size() {return index;}
150 +private:
151 + int index;
152 + T s[default_size];
153 +};
154 +#endif
155 +*/
1 +# 6/6/2012
2 +# IHF.dll
3 +# Skip swprintf and MessageBox statements in GetDebugPriv
4 +#
5 +# SVN checkout: 2012/6/6
6 +# - IHF/main.cpp: 2012/4/8
7 +# - IHF.{dll,lib}: 2012/6/5
8 +
9 +6F753055 CALL DWORD PTR DS:ntdll.ZwAdjustPrivilegesTOken
10 +6F75305B TEST EAX,EAX
11 +6F75305B JNE SHORT 6F753077 => JNE SHORT 6F75309F
12 +...
13 +6F753077 PUSH EAX
14 +...
15 +6F75309F MOVE EAX,DWORD PTR SS:[ESP]
16 +6F7530A2 PUSH EAX
17 +6F7530A3 CALL DWORD PTR DS:ntdll.NtClose
18 +...
19 +
20 +# EOF
1 +// Host_p.cc
2 +// 10/15/2011 jichi
3 +
4 +#include "texthook/ihf_p.h"
5 +#include "texthook/ith_p.h"
6 +#include "texthook/textthread_p.h"
7 +#include "host/host.h"
8 +#include "vnrhook/include/types.h"
9 +#include "ithsys/ithsys.h"
10 +#include "wintimer/wintimer.h"
11 +#include <QtCore/QDebug>
12 +
13 +#ifdef WITH_LIB_WINMAKER
14 +# include "winmaker/winmaker.h"
15 +#endif // WITH_LIB_WINMAKER
16 +
17 +//#define ITH_RUNNING_EVENT L"ITH_PIPE_EXIST"
18 +//#define ITH_RUNNING_MUTEX L"ITH_RUNNING"
19 +//#define ITH_MUTEX_NAME L"ITH_MAIN_RUNNING"
20 +
21 +//#define DEBUG "ihf_p.cc"
22 +#include "sakurakit/skdebug.h"
23 +
24 +//#define ITH_WITH_LINK
25 +
26 +// - Construction -
27 +
28 +//bool Ihf::debug_ = true;
29 +bool Ihf::enabled_ = true;
30 +
31 +//Settings *Ihf::settings_;
32 +HookManager *Ihf::hookManager_;
33 +qint64 Ihf::messageInterval_ = 250; // 0.25 secs by default, larger than the split_time (0.2sec) in ITH::setman
34 +WId Ihf::parentWindow_;
35 +
36 +QHash<TextThread *, TextThreadDelegate *> Ihf::threadDelegates_;
37 +//QHash<TextThreadDelegate *, TextThreadDelegate *> Ihf::linkedDelegates_;
38 +QHash<QString, ulong> Ihf::hookAddresses_;
39 +
40 +char Ihf::keptThreadName_[ITH_THREAD_NAME_CAPACITY];
41 +
42 +bool Ihf::whitelistEnabled_;
43 +qint32 Ihf::whitelist_[Ihf::WhitelistSize];
44 +
45 +// Debugging output
46 +//void Ihf::consoleOutput(const char *text)
47 +//{ if (debug_) qDebug() << "texthook:console:" << text; }
48 +
49 +//void Ihf::consoleOutputW(const wchar_t *text)
50 +//{ if (debug_) qDebug() << "texthook:console:" << QString::fromWCharArray(text); }
51 +
52 +void Ihf::init()
53 +{
54 + IthInitSystemService();
55 + Host_Init();
56 +}
57 +void Ihf::destroy()
58 +{
59 + Host_Destroy();
60 + IthCloseSystemService();
61 +}
62 +
63 +// See also: HelloITH/main.cpp
64 +bool Ihf::load()
65 +{
66 + // 12/20/2013: This would crash the error of failure to create QTimer
67 + //if (!parentWindow_)
68 +
69 + //::wm_register_hidden_class("vnrtexthook.class");
70 + //parentWindow_ = (WId)::wm_create_hidden_window("vnrtexthook.class", "vnrtexthook");
71 +
72 + DOUT("enter");
73 + if (hookManager_) {
74 + DOUT("leave: already loaded");
75 + return true;
76 + }
77 +
78 + // Single instance protection
79 + //HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, ITH_MUTEX_NAME); // in kernel32.dll
80 + //if (hMutex != 0 || ::GetLastError() != ERROR_FILE_NOT_FOUND) {
81 + // ::CloseHandle(hMutex);
82 + // return false;
83 + //}
84 +
85 + // See: ITH/main.cpp
86 + //if (!IthInitSystemService()) {
87 + // DOUT("leave: error: failed to init system service");
88 + // return false;
89 + //}
90 +
91 + if (::Host_Open()) {
92 +#ifdef WITH_LIB_WINMAKER
93 + if (!parentWindow_)
94 + parentWindow_ = (WId)::wm_create_hidden_window("vnrtexthook");
95 +#endif // WITH_LIB_WINMAKER
96 + WinTimer::setGlobalWindow(parentWindow_);
97 + ::Host_GetHookManager(&hookManager_);
98 + if (hookManager_) {
99 + //::Host_GetSettings(&settings_);
100 + //settings_->debug = debug_;
101 +
102 + //hookManager_->RegisterConsoleCallback(consoleOutput);
103 + //hookManager_->RegisterConsoleWCallback(consoleOutputW);
104 + //hookManager_->RegisterProcessAttachCallback(processAttach);
105 + //hookManager_->RegisterProcessDetachCallback(processDetach);
106 + //hookManager_->RegisterProcessNewHookCallback(processNewHook);
107 + //hookManager_->RegisterThreadResetCallback(threadReset);
108 + hookManager_->RegisterThreadCreateCallback(threadCreate);
109 + hookManager_->RegisterThreadRemoveCallback(threadRemove);
110 +
111 + ::Host_Start();
112 + }
113 + } else
114 + ::Host_Close();
115 + DOUT("leave: hook manager =" << hookManager_);
116 + return hookManager_;
117 +}
118 +
119 +void Ihf::unload()
120 +{
121 + DOUT("enter: hook manager =" << hookManager_);
122 + if (hookManager_) {
123 + //hookManager_->RegisterProcessAttachCallback(nullptr);
124 + //hookManager_->RegisterProcessDetachCallback(nullptr);
125 + //hookManager_->RegisterProcessNewHookCallback(nullptr);
126 + //hookManager_->RegisterThreadResetCallback(nullptr);
127 + hookManager_->RegisterThreadCreateCallback(nullptr);
128 + hookManager_->RegisterThreadRemoveCallback(nullptr);
129 + // Console output is not unregisterd to avoid segmentation fault
130 + //hookManager_->RegisterConsoleCallback(nullptr);
131 +
132 + ::Host_Close();
133 + hookManager_ = nullptr;
134 + //settings_ = nullptr;
135 +
136 +#ifdef WITH_LIB_WINMAKER
137 + if (parentWindow_) {
138 + wm_destroy_window(parentWindow_);
139 + parentWindow_ = nullptr;
140 + }
141 +#endif // WITH_LIB_WINMAKER
142 + }
143 + //if (parentWindow_) {
144 + // wm_destroy_window(parentWindow_);
145 + // parentWindow_ = nullptr;
146 + //}
147 + DOUT("leave");
148 +}
149 +
150 +// - Callbacks -
151 +
152 +//DWORD Ihf::processAttach(DWORD pid)
153 +//{
154 +// DOUT("enter");
155 +// Q_UNUSED(pid);
156 +// DOUT("leave");
157 +// return 0;
158 +//}
159 +
160 +//DWORD Ihf::processDetach(DWORD pid)
161 +//{
162 +// DOUT("enter");
163 +// Q_UNUSED(pid);
164 +// DOUT("leave");
165 +// return 0;
166 +//}
167 +
168 +//DWORD Ihf::processNewHook(DWORD pid)
169 +//{
170 +// DOUT("enter");
171 +// Q_UNUSED(pid);
172 +// DOUT("leave");
173 +// return 0;
174 +//}
175 +
176 +// See: HelloITH/main.cpp
177 +// See: ThreadCreate in ITH/window.cpp
178 +DWORD Ihf::threadCreate(TextThread *t)
179 +{
180 + Q_ASSERT(t);
181 + DOUT("enter: pid =" << t->PID());
182 + Q_ASSERT(hookManager_);
183 +
184 + // Propagate UNICODE
185 + // See: ThreadCreate in ITH/window.cpp
186 + //if (ProcessRecord *pr = hookManager_->GetProcessRecord(t->PID())) {
187 + // NtWaitForSingleObject(pr->hookman_mutex, 0, 0);
188 + // Hook *hk = static_cast<Hook *>(pr->hookman_map);
189 + // Q_ASSERT(!hk&&!MAX_HOOK || hk&&MAX_HOOK);
190 + // for (int i = 0; i < MAX_HOOK; i++) {
191 + // if (hk[i].Address() == t->Addr()) {
192 + // if (hk[i].Type() & USING_UNICODE)
193 + // t->Status() |= USING_UNICODE;
194 + // break;
195 + // }
196 + // }
197 + // NtReleaseMutant(pr->hookman_mutex, 0);
198 + //}
199 + auto d = new TextThreadDelegate(t);
200 + bool init = true;
201 + foreach (TextThreadDelegate *it, threadDelegates_)
202 + if (d->signature() == it->signature()) {
203 + TextThreadDelegate::release(d);
204 + d = it;
205 + d->retain();
206 + init = false;
207 + break;
208 + }
209 + if (init) {
210 + d->setInterval(messageInterval_);
211 + d->setParentWindow(parentWindow_);
212 + updateLinkedDelegate(d);
213 + }
214 + threadDelegates_[t] = d;
215 + t->RegisterOutputCallBack(threadOutput, d);
216 + //t->RegisterFilterCallBack(threadFilter, d);
217 + DOUT("leave");
218 + return 0;
219 +}
220 +
221 +// See also: HelloITH/main.cpp
222 +DWORD Ihf::threadRemove(TextThread *t)
223 +{
224 + DOUT("enter");
225 + Q_ASSERT(t);
226 +
227 + auto p = threadDelegates_.find(t);
228 + if (p != threadDelegates_.end()) {
229 + auto d = p.value();
230 + //if (!linkedDelegates_.isEmpty()) {
231 + // linkedDelegates_.remove(d);
232 + // while (auto k = linkedDelegates_.key(d))
233 + // linkedDelegates_.remove(k);
234 + //}
235 + threadDelegates_.erase(p);
236 + TextThreadDelegate::release(d);
237 + }
238 +
239 +#ifdef ITH_WITH_LINK
240 + ::Host_UnLinkAll(t->Number());
241 +#endif // ITH_WITH_LINK
242 +
243 + DOUT("leave");
244 + return 0;
245 +}
246 +
247 +// See: HelloITH/main.cpp
248 +DWORD Ihf::threadOutput(TextThread *t, BYTE *data, DWORD dataLength, DWORD newLine, PVOID pUserData, bool space)
249 +{
250 + DOUT("newLine =" << newLine << ", dataLength =" << dataLength << ", space =" << space);
251 + Q_UNUSED(t)
252 + Q_ASSERT(data);
253 + Q_ASSERT(pUserData);
254 +
255 + auto d = static_cast<TextThreadDelegate *>(pUserData);
256 + //if (TextThreadDelegate *link = findLinkedDelegate(d))
257 + // d = link;
258 + Q_ASSERT(d);
259 + if (!enabled_ ||
260 + whitelistEnabled_ &&
261 + !whitelistContains(d->signature()) &&
262 + !(keptThreadName_[0] && d->nameEquals(keptThreadName_))) {
263 + DOUT("leave: ignored");
264 + return dataLength;
265 + }
266 + if (newLine)
267 + d->touch();
268 + //d->flush(); // new line data are ignored
269 + else if (dataLength || space)
270 + d->append(reinterpret_cast<char *>(data), dataLength, space);
271 + //QString text = QString::fromLocal8Bit(reinterpret_cast<LPCSTR>(data), len);
272 + DOUT("leave");
273 + return dataLength;
274 +}
275 +
276 +//TextThreadDelegate *Ihf::findLinkedDelegate(TextThreadDelegate *d)
277 +//{
278 +// Q_ASSERT(d);
279 +// if (!linkedDelegates_.isEmpty()) {
280 +// auto p = linkedDelegates_.find(d);
281 +// if (p != linkedDelegates_.end())
282 +// return p.value();
283 +// }
284 +// return nullptr;
285 +//}
286 +
287 +void Ihf::updateLinkedDelegate(TextThreadDelegate *d)
288 +{
289 +#ifdef ITH_WITH_LINK
290 + Q_ASSERT(t);
291 + foreach (TextThreadDelegate *it, threadDelegates_)
292 + if (it->delegateOf(d))
293 + ::Host_AddLink(d->threadNumber(), it->threadNumber());
294 + else if (d->delegateOf(it))
295 + ::Host_AddLink(it->threadNumber(), d->threadNumber());
296 +#else
297 + Q_UNUSED(d);
298 +#endif // ITH_WITH_LINK
299 +}
300 +
301 +// - Injection -
302 +
303 +// See: Host_InjectByPID in IHF/main.cpp
304 +// See: InjectThread in ITH/profile.cpp
305 +bool Ihf::attachProcess(DWORD pid)
306 +{
307 + DOUT("enter: pid =" << pid);
308 + bool ok = ::Host_InjectByPID(pid);
309 +
310 + //enum { AttachDelay = 500 }; // in msec
311 + //::Sleep(AttachDelay);
312 +
313 + DOUT("leave: ret =" << ok);
314 + return ok;
315 +}
316 +
317 +// See: Host_ActiveDetachProcess in IHF/main.cpp
318 +bool Ihf::detachProcess(DWORD pid) { return ::Host_ActiveDetachProcess(pid); }
319 +bool Ihf::hijackProcess(DWORD pid) { return ::Host_HijackProcess(pid); }
320 +
321 +// - Hook -
322 +
323 +// See: Host_ModifyHook in IHF/main.cpp
324 +bool Ihf::updateHook(ulong pid, const QString &code)
325 +{
326 + DOUT("enter: pid =" << pid << ", code =" << code);
327 + Q_ASSERT(pid);
328 + HookParam hp = {};
329 + if (!Ith::parseHookCode(code, &hp)) {
330 + DOUT("leave: failed to parse hook code");
331 + return false;
332 + }
333 +
334 + DWORD hh = ::Host_ModifyHook(pid, &hp);
335 + bool ok = ~hh;
336 + DOUT("leave: ret =" << ok);
337 + return ok;
338 +}
339 +
340 +// See: Host_InsertHook in IHF/main.cpp
341 +bool Ihf::addHook(ulong pid, const QString &code, const QString &name, bool verbose)
342 +{
343 + DOUT("enter: pid =" << pid << ", name =" << name << ", code =" << code);
344 + Q_ASSERT(pid);
345 + if (hookAddresses_.contains(code)) {
346 + DOUT("leave: already added");
347 + return false;
348 + }
349 +
350 + HookParam hp = {};
351 + if (!Ith::parseHookCode(code, &hp, verbose)) {
352 + DOUT("leave: failed to parse hook code");
353 + return false;
354 + }
355 +
356 + DWORD hh = ::Host_InsertHook(pid, &hp, name.toAscii());
357 + //DWORD hh = ::NewHook(hp, nameBuf);
358 + bool ok = ~hh;
359 + if (ok && hp.address) {
360 + DOUT("hook address =" << hp.address);
361 + hookAddresses_[code] = hp.address;
362 + }
363 + DOUT("leave: ok =" << ok);
364 + return ok;
365 +}
366 +
367 +// See: Host_RemoveHook in IHF/main.cpp
368 +bool Ihf::removeHook(ulong pid, const QString &code)
369 +{
370 + DOUT("enter: pid =" << pid << ", code =" << code);
371 + Q_ASSERT(pid);
372 + auto p = hookAddresses_.find(code);
373 + if (p == hookAddresses_.end()) {
374 + DOUT("leave: hook not added");
375 + return false;
376 + }
377 + DWORD addr = p.value();
378 + Q_ASSERT(addr);
379 + hookAddresses_.erase(p);
380 +
381 + DWORD hh = ::Host_RemoveHook(pid, addr);
382 + bool ok = ~hh;
383 + DOUT("leave: ret =" << ok);
384 + return ok;
385 +}
386 +
387 +bool Ihf::verifyHookCode(const QString &code)
388 +{ return Ith::verifyHookCode(code); }
389 +
390 +// - Whitelist -
391 +
392 +QList<qint32> Ihf::whitelist()
393 +{
394 + QList<qint32> ret;
395 + const qint32 *p = whitelist_;
396 + while (*p)
397 + ret.append(*p++);
398 + return ret;
399 +}
400 +
401 +void Ihf::clearWhitelist() { *whitelist_= 0; }
402 +
403 +void Ihf::setWhitelist(const QList<qint32> &l)
404 +{
405 + qint32 *p = whitelist_;
406 + if (!l.isEmpty())
407 + foreach (qint32 it, l) {
408 + *p++ = it;
409 + if (p >= whitelist_ + WhitelistSize)
410 + break;
411 + }
412 + whitelist_[qMin(l.size(), WhitelistSize -1)] = 0;
413 +}
414 +
415 +bool Ihf::whitelistContains(qint32 signature)
416 +{
417 + const qint32 *p = whitelist_;
418 + while (*p)
419 + if (signature == *p++)
420 + return true;
421 + return false;
422 +}
423 +
424 +// EOF
425 +
426 +/*
427 +BYTE LeadByteTable[0x100] = {
428 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
429 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
430 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
431 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
432 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
433 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
434 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
435 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
436 + 1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
437 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
438 + 2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
439 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
440 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
441 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
442 + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
443 + 2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1
444 +};
445 +
446 +DWORD Ihf::threadFilter(TextThread *thread, BYTE *out, DWORD len, DWORD new_line, PVOID data)
447 +{
448 + DWORD status = thread->Status();
449 +
450 + if (!new_line && thread->Number() != 0)
451 + {
452 + if (status & USING_UNICODE)
453 + {
454 + DWORD i, j;
455 + len >>= 1;
456 + WCHAR c, *str = (LPWSTR)out;
457 + for (i = 0, j = 0; i < len; i++)
458 + {
459 + c = str[i];
460 + //if (!uni_filter->Check(c))
461 + str[j++] = c;
462 +
463 + }
464 + memset(str + j, 0, (len - j) << 1);
465 + len = j << 1;
466 + }
467 + else
468 + {
469 + WORD c;
470 + DWORD i, j;
471 + for (i = 0, j = 0; i < len; i++)
472 + {
473 + c = out[i];
474 + if (LeadByteTable[c] == 1)
475 + {
476 + //if (!mb_filter->Check(c))
477 + out[j++] = c & 0xFF;
478 + }
479 + else if (i + 1 < len)
480 + {
481 +
482 + c = out[i + 1];
483 + c <<= 8;
484 + c |= out[i];
485 + //if (!mb_filter->Check(c))
486 + {
487 + out[j++] = c & 0xFF;
488 + out[j++] = c >> 8;
489 + }
490 + i++;
491 + }
492 + }
493 + memset(out + j, 0, len - j);
494 + len = j;
495 + }
496 + }
497 + return len;
498 +}
499 +*/
500 +
501 +
502 +/*
503 +// jichi: 10/15/2011: FIXME: This overload will infect the entire program,
504 +// even source files that exclude this header, which is unexpected.
505 +// No idea what is the trade off of this behavior on performance and liability.
506 +// Lots of Qt stuff doesn't work such as QString::toStdString.
507 +// I have to use dynamic linkage to avoid being polluted by this module.
508 +//
509 +// original author: HEAP_ZERO_MEMORY flag is critical. All new object are assumed with zero initialized.
510 +// jichi: 10/20/2011: I think the only reason to use Rtl heap here is to ensure HEAP_ZERO_MEMORY,
511 +// which is really a bad programming style and incur unstability on heap memory allocation.
512 +// ::RtlFreeHeap crash on DLL debug mode. Replace it with standard malloc/free.
513 +// ::hHeap handle is also removed from ith/sys.c.cc
514 +
515 +inline void * __cdecl operator new(size_t lSize)
516 +{ return ::RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize); }
517 +
518 +inline void * __cdecl operator new[](size_t lSize)
519 +{ return ::RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize); }
520 +
521 +inline void __cdecl operator delete(void *pBlock)
522 +{ ::RtlFreeHeap(::hHeap, 0, pBlock); }
523 +
524 +inline void __cdecl operator delete[](void* pBlock)
525 +{ ::RtlFreeHeap(::hHeap, 0, pBlock); }
526 +
527 +
528 +#include <cstdlib>
529 +#include <cstring>
530 +inline void * __cdecl operator new(size_t size) throw()
531 +{
532 + if (!size) // When the value of the expression in a direct-new-declarator is zero,
533 + size = 4; // the allocation function is called to allocatean array with no elements.(ISO)
534 +
535 + void *p = malloc(size);
536 + if (p)
537 + memset(p, 0, size);
538 + return p;
539 +}
540 +
541 +inline void * __cdecl operator new[](size_t size) throw()
542 +{
543 + if (!size) // When the value of the expression in a direct-new-declarator is zero,
544 + size = 4; // the allocation function is called to allocatean array with no elements.(ISO)
545 +
546 + void *p = malloc(size);
547 + if (p)
548 + memset(p, 0, size);
549 + return p;
550 +}
551 +
552 +inline void __cdecl operator delete(void *p) throw() { free(p); }
553 +inline void __cdecl operator delete[](void *p) throw() { free(p); }
554 +*/
555 +
556 +
557 +//QString
558 +//Ihf::getHookNameById(ulong hookId)
559 +//{
560 +// QString ret;
561 +// if (hookId) {
562 +// auto p = reinterpret_cast<TextThread *>(hookId);
563 +// if (p->good())
564 +// ret = p->name();
565 +// }
566 +// return ret;
567 +//}
568 +
569 +//DWORD ProcessAttach(DWORD pid)
570 +//{
571 +// DOUT("process attached, pid =" << pid);
572 +// return 0;
573 +//}
574 +//DWORD ProcessDetach(DWORD pid)
575 +//{
576 +// DOUT("process detached, pid =" << pid);
577 +// return 0;
578 +//}
579 +//DWORD ProcessNewHook(DWORD pid)
580 +//{
581 +// DOUT("process has new hook inserted, pid =" << pid);
582 +// return 0;
583 +//}
1 +#pragma once
2 +
3 +// ihf_p.h
4 +// 10/15/2011 jichi
5 +// Internal header.
6 +// Wrapper of IHF functions.
7 +
8 +#include <QtCore/QHash>
9 +#include <QtCore/QList>
10 +#include <QtCore/QString>
11 +#include <QtGui/qwindowdefs.h> // for WId
12 +
13 +//struct Settings; // opaque in ith/host/settings.h
14 +class HookManager; // opaque in ith/host/hookman.h
15 +class TextThread; // opaque in ith/host/textthread.h
16 +class TextThreadDelegate;
17 +
18 +enum { ITH_THREAD_NAME_CAPACITY = 0x200 }; // used internally by ITH
19 +
20 +class Ihf
21 +{
22 + Ihf() {} // Singleton
23 +
24 + static bool enabled_;
25 +
26 + //static Settings *settings_;
27 + static HookManager *hookManager_;
28 + static qint64 messageInterval_;
29 + static WId parentWindow_;
30 +
31 + static QHash<TextThread *, TextThreadDelegate *> threadDelegates_;
32 + //static QHash<TextThreadDelegate *, TextThreadDelegate *> linkedDelegates_;
33 + static QHash<QString, ulong> hookAddresses_;
34 +
35 + enum { WhitelistSize = 0x20 + 1 }; // ITH capacity is 0x20
36 + static qint32 whitelist_[WhitelistSize]; // List of signatures. The last element is zero. I.e., at most BlackSize-1 threads.
37 + static bool whitelistEnabled_;
38 + static char keptThreadName_[ITH_THREAD_NAME_CAPACITY];
39 + //static QString userDefinedThreadName_;
40 +
41 +public:
42 +
43 + // - Initialization -
44 + static void init();
45 + static void destroy();
46 +
47 + static bool load();
48 + static bool isLoaded() { return hookManager_; }
49 + static void unload();
50 +
51 + // - Properties -
52 +
53 + static bool isEnabled() { return enabled_; }
54 + static void setEnabled(bool t) { enabled_ = t; }
55 +
56 + /// A valid window handle is required to make ITH work
57 + static WId parentWindow() { return parentWindow_; }
58 + static void setParentWindow(WId hwnd) { parentWindow_ = hwnd; }
59 +
60 + /// Timeout (msecs) for a text message
61 + static qint64 messageInterval() { return messageInterval_; }
62 + static void setMessageInterval(qint64 msecs) { messageInterval_ = msecs; }
63 +
64 + // - Injection -
65 + static bool attachProcess(ulong pid);
66 + static bool detachProcess(ulong pid);
67 + static bool hijackProcess(ulong pid);
68 +
69 + /// Add hook code
70 + static bool addHook(ulong pid, const QString &code, const QString &name = QString(), bool verbose = true);
71 + static bool updateHook(ulong pid, const QString &code); // not used
72 + static bool removeHook(ulong pid, const QString &code);
73 + static bool verifyHookCode(const QString &code);
74 +
75 + // - Whitelist -
76 + static bool isWhitelistEnabled() { return whitelistEnabled_; }
77 + static void setWhitelistEnabled(bool t) { whitelistEnabled_ = t; }
78 +
79 + static QList<qint32> whitelist();
80 + static void setWhitelist(const QList<qint32> &l);
81 + static void clearWhitelist();
82 +
83 + //static QString userDefinedThreadName() { return userDefinedThreadName_; }
84 + //static void setUserDefinedThreadName(const QString &val) { userDefinedThreadName_ = val; }
85 + static const char *keptThreadName() { return keptThreadName_; }
86 +
87 + static void setKeptThreadName(const QString &v)
88 + {
89 + if (v.size() < ITH_THREAD_NAME_CAPACITY)
90 + ::strcpy(keptThreadName_, v.toAscii());
91 + else
92 + setKeptThreadName(v.left(ITH_THREAD_NAME_CAPACITY - 1));
93 + }
94 +
95 +private:
96 + static bool whitelistContains(qint32 signature);
97 +
98 + // - Callbacks -
99 + //static ulong processAttach(ulong pid);
100 + //static ulong processDetach(ulong pid);
101 + //static ulong processNewHook(ulong pid);
102 +
103 + static ulong threadCreate(_In_ TextThread *t);
104 + static ulong threadRemove(_In_ TextThread *t);
105 + static ulong threadOutput(_In_ TextThread *t, _In_ uchar *data, _In_ ulong dataLength, _In_ ulong bNewLine, _In_ void *pUserData, _In_ bool space);
106 + //static ulong threadFilter(_In_ TextThread *t, _Out_ uchar *data, _In_ ulong dataLength, _In_ ulong bNewLine, _In_ void *pUserData);
107 + //static ulong threadReset(TextThread *t);
108 + //static void consoleOutput(const char *text);
109 + //static void consoleOutputW(const wchar_t *text);
110 +
111 + // - Linked threasds -
112 +private:
113 + //static TextThreadDelegate *findLinkedDelegate(TextThreadDelegate *d);
114 + static void updateLinkedDelegate(TextThreadDelegate *d);
115 +};
116 +
117 +// EOF
1 +// ith_p.cc
2 +// 10/15/2011 jichi
3 +
4 +#include "texthook/ith_p.h"
5 +#include "vnrhook/include/const.h"
6 +#include "vnrhook/include/types.h"
7 +#include <string>
8 +
9 +#define DEBUG "ith_p.cc"
10 +#include "sakurakit/skdebug.h"
11 +
12 +// HookParam copied from ITH/common.h:
13 +// struct HookParam // size = 40 (0x24)
14 +// {
15 +// typedef void (*DataFun)(DWORD, HookParam*, DWORD*, DWORD*, DWORD*);
16 +//
17 +// DWORD addr; // 4
18 +// DWORD off, // 8
19 +// ind, // 12
20 +// split, // 16
21 +// split_ind; // 20
22 +// DWORD module, // 24
23 +// function; // 28
24 +// DataFun text_fun; // 32, jichi: is this the same in x86 and x86_64?
25 +// DWORD type; // 36
26 +// WORD length_offset; // 38
27 +// BYTE hook_len, // 39
28 +// recover_len; // 40
29 +// };
30 +
31 +
32 +// - Implementation Details -
33 +
34 +namespace { namespace detail { // unnamed
35 +
36 +// ITH ORIGINAL CODE BEGIN
37 +
38 +// See: ITH/ITH.h
39 +// Revision: 133
40 +inline DWORD Hash(_In_ LPWSTR module, int length = -1)
41 +{
42 + bool flag = length == -1;
43 + DWORD hash = 0;
44 + for (; *module && (flag || length--); module++)
45 + hash = _rotr(hash,7) + *module; //hash=((hash>>7)|(hash<<25))+(*module);
46 + return hash;
47 +}
48 +
49 +// See: ITH/command.cpp
50 +// Revision: 133
51 +//
52 +// jichi note: str[0xF] will be modified and restored.
53 +// So, the buffer of str must be larger than 0xF.
54 +int Convert(_In_ LPWSTR str, _Out_ DWORD *num, _In_ LPWSTR delim)
55 +{
56 + if (!num)
57 + return -1;
58 + WCHAR t = *str,
59 + tc = *(str + 0xF);
60 + WCHAR temp[0x10] = {};
61 + LPWSTR it = temp,
62 + istr = str,
63 + id = temp;
64 + if (delim) {
65 + id = wcschr(delim, t);
66 + str[0xF] = delim[0]; // reset str[0xF] in case of out-of-bound iteration
67 + }
68 + else
69 + str[0xF] = 0; // reset str[0xF] in case of out-of-bound iteration
70 + while (!id && t) {
71 + *it = t;
72 + it++; istr++;
73 + t = *istr;
74 + if (delim)
75 + id = wcschr(delim, t);
76 + }
77 + swscanf(temp, L"%x", num);
78 + str[0xF] = tc; // restore the str[0xF]
79 + if (!id || istr - str == 0xF)
80 + return -1;
81 +
82 + if (!t)
83 + return istr - str; // >= 0
84 + else
85 + return id - delim; // >= 0
86 +}
87 +
88 +// See: ITH/command.cpp
89 +// Revision: 133
90 +//
91 +// jichi note: str[0xF] will be modified and restored.
92 +// So, the buffer of cmd must be larger than 0xF*2 = 0x1F.
93 +bool Parse(_In_ LPWSTR cmd, _Out_ HookParam &hp)
94 +{
95 + ::memset(&hp, 0, sizeof(hp));
96 +
97 + int t;
98 + bool accept = false;
99 + DWORD *data = &hp.offset; //
100 + LPWSTR offset = cmd + 1;
101 + LPWSTR delim_str = L":*@!";
102 + LPWSTR delim = delim_str;
103 + if (*offset == L'n' || *offset == 'N') {
104 + offset++;
105 + hp.type |= NO_CONTEXT;
106 + }
107 + // jichi 4/25/2015: Add support for fixing hook
108 + if (*offset == L'f' || *offset == 'F') {
109 + offset++;
110 + hp.type |= FIXING_SPLIT;
111 + }
112 + if (*offset == L'j' || *offset == 'J') { // 11/22/2015: J stands for Japanese only
113 + offset++;
114 + hp.type |= NO_ASCII;
115 + }
116 + while (!accept) {
117 + t = Convert(offset, data, delim);
118 + if (t < 0)
119 + return false; //ConsoleOutput(L"Syntax error.");
120 + offset = ::wcschr(offset , delim[t]);
121 + if (offset)
122 + offset++; // skip the current delim
123 + else //goto _error;
124 + return false; //ConsoleOutput(L"Syntax error.");
125 + switch (delim[t]) {
126 + case L':':
127 + data = &hp.split;
128 + delim = delim_str + 1;
129 + hp.type |= USING_SPLIT;
130 + break;
131 + case L'*':
132 + if (hp.split) {
133 + data = &hp.split_index;
134 + delim = delim_str + 2;
135 + hp.type |= SPLIT_INDIRECT;
136 + }
137 + else {
138 + hp.type |= DATA_INDIRECT;
139 + data = &hp.index;
140 + }
141 + break;
142 + case L'@':
143 + accept = true;
144 + break;
145 + }
146 + }
147 + t = Convert(offset, &hp.address, delim_str);
148 + if (t < 0)
149 + return false;
150 + if (hp.offset & 0x80000000)
151 + hp.offset -= 4;
152 + if (hp.split & 0x80000000)
153 + hp.split -= 4;
154 + LPWSTR temp = offset;
155 + offset = ::wcschr(offset, L':');
156 + if (offset) {
157 + hp.type |= MODULE_OFFSET;
158 + offset++;
159 + delim = ::wcschr(offset, L':');
160 +
161 + if (delim) {
162 + *delim = 0;
163 + delim++;
164 + _wcslwr(offset);
165 + hp.function = Hash(delim);
166 + hp.module = Hash(offset, delim - offset - 1);
167 + hp.type |= FUNCTION_OFFSET;
168 + }
169 + else
170 + hp.module = Hash(_wcslwr(offset));
171 +
172 + } else {
173 + offset = ::wcschr(temp, L'!');
174 + if (offset) {
175 + hp.type |= MODULE_OFFSET;
176 + swscanf(offset + 1, L"%x", &hp.module);
177 + offset = ::wcschr(offset + 1, L'!');
178 + if (offset) {
179 + hp.type |= FUNCTION_OFFSET;
180 + swscanf(offset + 1, L"%x", &hp.function);
181 + }
182 + }
183 + }
184 + switch (*cmd) {
185 + case L's':
186 + case L'S':
187 + hp.type |= USING_STRING;
188 + break;
189 + case L'e':
190 + case L'E':
191 + hp.type |= STRING_LAST_CHAR;
192 + case L'a':
193 + case L'A':
194 + hp.type |= BIG_ENDIAN;
195 + hp.length_offset = 1;
196 + break;
197 + case L'b':
198 + case L'B':
199 + hp.length_offset = 1;
200 + break;
201 + // jichi 12/7/2014: Disabled
202 + //case L'h':
203 + //case L'H':
204 + // hp.type |= PRINT_DWORD;
205 + case L'q':
206 + case L'Q':
207 + hp.type |= USING_STRING | USING_UNICODE;
208 + break;
209 + case L'l':
210 + case L'L':
211 + hp.type |= STRING_LAST_CHAR;
212 + case L'w':
213 + case L'W':
214 + hp.type |= USING_UNICODE;
215 + hp.length_offset = 1;
216 + break;
217 + default: ;
218 + }
219 + //ConsoleOutput(L"Try to insert additional hook.");
220 + return true;
221 +}
222 +
223 +// ITH ORIGINAL CODE END
224 +
225 +}} // unnamed detail
226 +
227 +// - ITH API -
228 +
229 +// Sample code: L"/HS-4:-14@4383C0" (WHITE ALBUM 2)
230 +bool Ith::parseHookCode(const QString &code, HookParam *hp, bool verbose)
231 +{
232 +#define HCODE_PREFIX "/H"
233 + enum { HCODE_PREFIX_LEN = sizeof(HCODE_PREFIX) -1 }; // 2
234 + if (!hp || !code.startsWith(HCODE_PREFIX))
235 + return false;
236 + if (verbose)
237 + DOUT("enter: code =" << code);
238 + else
239 + DOUT("enter");
240 +
241 + size_t bufsize = qMax(0xFF, code.size() + 1); // in case detail::Convert modify the buffer
242 + auto buf = new wchar_t[bufsize];
243 + code.toWCharArray(buf);
244 + buf[code.size()] = 0;
245 +
246 + bool ret = detail::Parse(buf + HCODE_PREFIX_LEN, *hp);
247 + delete[] buf;
248 +#ifdef DEBUG
249 + if (ret && verbose)
250 + qDebug()
251 + << "addr:" << hp->address
252 + << ", text_fun:" << hp->text_fun
253 + << ", function:"<< hp->function
254 + << ", hook_len:" << hp->hook_len
255 + << ", ind:" << hp->index
256 + << ", length_offset:" << hp->length_offset
257 + << ", module:" << hp->module
258 + << ", off:" <<hp->offset
259 + << ", recover_len:" << hp->recover_len
260 + << ", split:" << hp->split
261 + << ", split_ind:" << hp->split_index
262 + << ", type:" << hp->type;
263 +#endif // DEBUG
264 + DOUT("leave: ret =" << ret);
265 + return ret;
266 +#undef HOOK_CODE_PREFIX
267 +}
268 +
269 +bool Ith::verifyHookCode(const QString &code)
270 +{
271 + HookParam hp = {};
272 + return parseHookCode(code, &hp);
273 +}
274 +
275 +// EOF
1 +#pragma once
2 +
3 +// ith_p.h
4 +// 10/15/2011 jichi
5 +// Internal header.
6 +// Wrapper of functions from ITH.
7 +
8 +#include <QtCore/QString>
9 +
10 +struct HookParam; // opaque, declared in ITH/common.h
11 +
12 +namespace Ith {
13 +
14 +/// Parse hook code, and save the result to hook param if succeeded.
15 +bool parseHookCode(_In_ const QString &code, _Out_ HookParam *hp, bool verbose = true);
16 +bool verifyHookCode(_In_ const QString &code);
17 +
18 +} // namespace Ith
19 +
20 +// EOF
1 +// texthook.cc
2 +// 10/14/2011 jichi
3 +
4 +#include "texthook/texthook.h"
5 +#include "texthook/texthook_p.h"
6 +#include "texthook/ihf_p.h"
7 +#include "texthook/textthread_p.h"
8 +#include "texthook/winapi_p.h"
9 +#include <QtCore>
10 +
11 +//#define DEBUG "texthook.cc"
12 +#include "sakurakit/skdebug.h"
13 +
14 +//#include <ITH/IHF_SYS.h>
15 +//namespace { int _ = IthInitSystemService(); }
16 +
17 +/** Private class */
18 +
19 +TextHookPrivate *TextHookPrivate::instance_;
20 +
21 +/** Public class */
22 +
23 +// - Construction -
24 +
25 +//TextHook *TextHook::g_;
26 +//TextHook *TextHook::globalInstance() { static Self g; return &g; }
27 +
28 +//TextHook::TextHook(QObject *parent)
29 +// : Base(parent), d_(new D)
30 +//{}
31 +
32 +TextHook::TextHook(QObject *parent)
33 + : Base(parent), d_(new D(this))
34 +{
35 + Ihf::init();
36 + //Ihf::setUserDefinedThreadName(d_->source);
37 + DOUT("pass");
38 +}
39 +
40 +TextHook::~TextHook()
41 +{
42 + DOUT("enter");
43 + if (isActive())
44 + stop();
45 + delete d_;
46 +
47 + Ihf::destroy();
48 + DOUT("leave");
49 +}
50 +
51 +// - Properties -
52 +
53 +int TextHook::dataCapacity() const
54 +{ return TextThreadDelegate::capacity(); }
55 +
56 +void TextHook::setDataCapacity(int value)
57 +{ TextThreadDelegate::setCapacity(value); }
58 +
59 +bool TextHook::removesRepeat() const
60 +{ return TextThreadDelegate::removesRepeat(); }
61 +
62 +void TextHook::setRemovesRepeat(bool value)
63 +{ TextThreadDelegate::setRemovesRepeat(value); }
64 +
65 +bool TextHook::keepsSpace() const
66 +{ return TextThreadDelegate::keepsSpace(); }
67 +
68 +void TextHook::setKeepsSpace(bool value)
69 +{ TextThreadDelegate::setKeepsSpace(value); }
70 +
71 +bool TextHook::wideCharacter() const
72 +{ return TextThreadDelegate::wideCharacter(); }
73 +
74 +void TextHook::setWideCharacter(bool value)
75 +{ TextThreadDelegate::setWideCharacter(value); }
76 +
77 +// see: ITH/common.h
78 +int TextHook::capacity() const
79 +{ return 0x20; }
80 +
81 +QString TextHook::defaultHookName() const
82 +{ return d_->source; }
83 +
84 +void TextHook::setDefaultHookName(const QString &name)
85 +{
86 + d_->source = name;
87 + //Ihf::setUserDefinedThreadName(name);
88 +}
89 +
90 +bool TextHook::isEnabled() const
91 +{ return d_->enabled; }
92 +
93 +void TextHook::setEnabled(bool t)
94 +{
95 + d_->enabled = t;
96 + Ihf::setEnabled(t);
97 +}
98 +
99 +bool TextHook::isActive() const
100 +{ return Ihf::isLoaded(); }
101 +
102 +void TextHook::start()
103 +{ Ihf::load(); }
104 +
105 +void TextHook::stop()
106 +{
107 + if (!isEmpty())
108 + clear();
109 + Ihf::unload();
110 +}
111 +
112 +WId TextHook::parentWinId() const { return Ihf::parentWindow(); }
113 +void TextHook::setParentWinId(WId hwnd) { Ihf::setParentWindow(hwnd); }
114 +
115 +int TextHook::interval() const
116 +{ return Ihf::messageInterval(); }
117 +
118 +void TextHook::setInterval(int msecs)
119 +{ Ihf::setMessageInterval(msecs); }
120 +
121 +// - Injection -
122 +
123 +void TextHook::clear()
124 +{
125 + DOUT("enter");
126 + foreach (ulong pid, d_->pids)
127 + detachProcess(pid);
128 + if (!d_->hooks.isEmpty())
129 + d_->hooks.clear();
130 + clearThreadWhitelist();
131 + DOUT("leave");
132 +}
133 +
134 +bool TextHook::containsProcess(ulong pid) const { return d_->pids.contains(pid); }
135 +bool TextHook::isEmpty() const { return d_->pids.isEmpty(); }
136 +
137 +//QList<ulong> TextHook::attachedProcesses(bool checkActive) const
138 +//{
139 +// if (isEmpty() || !checkActive)
140 +// return d_->pids;
141 +//
142 +// QList<ulong> ret;
143 +// foreach (ulong pid, d_->pids)
144 +// if (winapi::IsProcessActiveWithId(pid))
145 +// ret.append(pid);
146 +// return ret;
147 +//}
148 +
149 +//ulong TextHook::currentProccess() const
150 +//{ return anyAttachedProcess(true); } // check active = true
151 +
152 +//ulong TextHook::anyAttachedProcess(bool checkActive) const
153 +//{
154 +// if (isEmpty())
155 +// return 0;
156 +// if (!checkActive)
157 +// return d_->pids.first();
158 +//
159 +// foreach (ulong pid, d_->pids)
160 +// if (winapi::IsProcessActiveWithId(pid))
161 +// return pid;
162 +// return 0;
163 +//}
164 +
165 +//bool TextHook::attachOneProcess(ulong pid, bool checkActive)
166 +//{
167 +// DOUT("enter: pid =" << pid);
168 +// DOUT("isAttached =" << containsProcess(pid));
169 +// if (!isActive())
170 +// start();
171 +//
172 +// if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
173 +// DOUT("leave: ret = false, isActive = false");
174 +// return false;
175 +// }
176 +//
177 +// if (containsProcess(pid)) {
178 +// DOUT("leave: pid already attached");
179 +// return true;
180 +// }
181 +//
182 +// detachAllProcesses();
183 +//
184 +// bool ret = Ihf::attachProcess(pid);
185 +// if (ret && !containsProcess(pid)) {
186 +// d_->pids.removeAll(pid);
187 +// d_->pids.append(pid);
188 +// emit processAttached(pid);
189 +// }
190 +//
191 +// DOUT("leave: ret =" << ret);
192 +// return ret;
193 +//}
194 +
195 +bool TextHook::attachProcess(ulong pid, bool checkActive)
196 +{
197 + DOUT("enter: pid =" << pid << ", isAttached =" << containsProcess(pid));
198 + if (!isActive())
199 + start();
200 + Q_ASSERT(isActive());
201 + DOUT("isActive =" << isActive());
202 +
203 + if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
204 + DOUT("leave: ret = false, isActive = false");
205 + return false;
206 + }
207 +
208 + bool ret = Ihf::attachProcess(pid);
209 + if (ret) {
210 + d_->pids.insert(pid);
211 + emit processAttached(pid);
212 + }
213 +
214 + DOUT("leave: ret =" << ret);
215 + return ret;
216 +}
217 +
218 +bool TextHook::detachProcess(ulong pid, bool checkActive)
219 +{
220 + DOUT("enter: pid =" << pid << ", isAttached =" << containsProcess(pid));
221 + Q_ASSERT(isActive());
222 +
223 + auto it = d_->pids.find(pid);
224 + if (it == d_->pids.end()) {
225 + DOUT("leave: ret = false, not attached");
226 + return false;
227 + }
228 + d_->pids.erase(it);
229 + d_->hooks.remove(pid);
230 +
231 + if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
232 + emit processDetached(pid);
233 + DOUT("leave: ret = false, isActive = false");
234 + return false;
235 + }
236 +
237 + bool ret = Ihf::detachProcess(pid);
238 + //try {
239 + // ret = Ihf::detachProcess(pid);
240 + //} catch (...) {
241 + // DOUT("warning: detach exception");
242 + //}
243 +
244 + emit processDetached(pid);
245 + DOUT("leave: ret =" << ret);
246 + return ret;
247 +}
248 +
249 +bool TextHook::hijackProcess(ulong pid)
250 +{
251 + DOUT("enter: pid =" << pid);
252 + Q_ASSERT(isActive());
253 +
254 + if (!containsProcess(pid)) {
255 + DOUT("leave: aborted, process not attached");
256 + return false;
257 + }
258 +
259 + // 7/12/2015: Function disabled
260 + return true;
261 +
262 + //bool ret = Ihf::hijackProcess(pid);
263 + //DOUT("leave: ret =" << ret);
264 + //return ret;
265 +}
266 +
267 +//void TextHook::detachAllProcesses()
268 +//{
269 +// DOUT("enter");
270 +// foreach (ulong pid, d_->pids)
271 +// detachProcess(pid);
272 +// if (!d_->hooks.isEmpty())
273 +// d_->hooks.clear();
274 +// DOUT("leave");
275 +//}
276 +
277 +// - Hook -
278 +
279 +//bool TextHook::containsHook(ulong pid) const
280 +//{ return d_->hooks.contains(pid); }
281 +//
282 +//bool TextHook::containsHook(ulong pid, const QString &code) const
283 +//{ return processHook(pid) == code; }
284 +
285 +bool TextHook::addHookCode(ulong pid, const QString &code, const QString &name, bool verbose)
286 +{
287 + DOUT("enter: pid =" << pid << ", code =" << code);
288 + if (isEmpty() || !containsProcess(pid)) {
289 + DOUT("leave: failed, process not attached");
290 + return false;
291 + }
292 + if (d_->hooks.contains(pid)) {
293 + DOUT("leave: failed, hook already exists");
294 + return false;
295 + }
296 + bool ok = Ihf::addHook(pid, code,
297 + name.isEmpty() ? defaultHookName() : name,
298 + verbose);
299 + if (ok)
300 + d_->hooks[pid] = code;
301 + DOUT("leave: ret =" << ok);
302 + return ok;
303 +}
304 +
305 +bool TextHook::verifyHookCode(const QString &code) { return Ihf::verifyHookCode(code); }
306 +
307 +bool TextHook::removeHookCode(ulong pid)
308 +{
309 + DOUT("enter");
310 + auto p = d_->hooks.find(pid);
311 + if (p == d_->hooks.end()) {
312 + DOUT("leave: not hooked");
313 + return false;
314 + }
315 +
316 + //DOUT("remove existing hook, THIS SHOULD NOT HAPPEN");
317 + bool ok = Ihf::removeHook(pid, p.value());
318 + d_->hooks.erase(p);
319 + DOUT("leave: ret =" << ok);
320 + return ok;
321 +}
322 +
323 +//QString TextHook::processHook(ulong pid) const
324 +//{
325 +// auto p = d_->hooks.find(pid);
326 +// return p == d_->hooks.end() ? QString() : p.value();
327 +//}
328 +
329 +bool TextHook::isThreadWhitelistEnabled() const { return Ihf::isWhitelistEnabled(); }
330 +
331 +void TextHook::setThreadWhitelistEnabled(bool t) { Ihf::setWhitelistEnabled(t); }
332 +
333 +QList<qint32> TextHook::threadWhitelist() const { return Ihf::whitelist(); }
334 +
335 +void TextHook::setThreadWhitelist(const QList<qint32> &sigs) { Ihf::setWhitelist(sigs); }
336 +
337 +void TextHook::clearThreadWhitelist() { Ihf::clearWhitelist(); }
338 +
339 +QString TextHook::keptThreadName() const { return Ihf::keptThreadName(); }
340 +
341 +void TextHook::setKeptThreadName(const QString &v) { Ihf::setKeptThreadName(v); }
342 +
343 +// EOF
344 +
345 +/*
346 +QString
347 +TextHook::guessEncodingForFile(const QString &fileName)
348 +{
349 + static QHash<QString, QString> db;
350 + if (db.isEmpty()) {
351 + db["malie.exe"] = "UTF-16";
352 + }
353 + auto p = db.find(fileName);
354 + return p == db.end() ? QString() : p.value();
355 +}
356 +
357 +// - Helpers -
358 +
359 +bool
360 +TextHook::isStandardHookName(const QString &name) const
361 +{
362 + static QSet<uint> hashes;
363 + if (hashes.isEmpty()) {
364 +#define ADD(_text) hashes.insert(qHash(QString(_text)))
365 + ADD("ConsoleOutput");
366 + ADD("GetTextExtentPoint32A");
367 + ADD("GetGlyphOutlineA");
368 + ADD("ExtTextOutA");
369 + ADD("TextOutA");
370 + ADD("GetCharABCWidthsA");
371 + ADD("DrawTextA");
372 + ADD("DrawTextExA");
373 + ADD("GetTextExtentPoint32W");
374 + ADD("GetGlyphOutlineW");
375 + ADD("ExtTextOutW");
376 + ADD("TextOutW");
377 + ADD("GetCharABCWidthsW");
378 + ADD("DrawTextW");
379 + ADD("DrawTextExW");
380 +#undef ADD
381 + }
382 + uint h = qHash(name);
383 + return hashes.contains(h);
384 +}
385 +
386 +bool
387 +TextHook::isKnownHookForProcess(const QString &hook, const QString &proc) const
388 +{
389 + // TODO: update database on line periodically
390 + qDebug() << "qth::isKnownHookForProcess: hook =" << hook << ", proc =" << proc;
391 +
392 + static QSet<uint> hashes;
393 + if (hashes.isEmpty()) {
394 +#define ADD(_hook, _proc) hashes.insert(qHash(QString(_hook) + "\n" + _proc))
395 + //ADD("Malie", "malie"); // light
396 + ADD("GetGlyphOutlineA", "STEINSGATE");
397 + ADD("StuffScriptEngine", "EVOLIMIT");
398 +#undef ADD
399 + }
400 + uint h = qHash(hook + "\n" + proc);
401 + return hashes.contains(h);
402 +}
403 +
404 +QString
405 +TextHook::hookNameById(ulong hookId) const
406 +{
407 + //return Ihf::getHookNameById(hookId);
408 + // FIXME: supposed to be the engine name, unimplemented
409 + Q_UNUSED(hookId)
410 + return QString();
411 +}
412 +
413 +
414 +*/
1 +#pragma once
2 +
3 +// texthook.h
4 +// 10/14/2011 jichi
5 +
6 +#include "texthook_config.h"
7 +#include "sakurakit/skglobal.h"
8 +#include <QtCore/QByteArray>
9 +#include <QtCore/QObject>
10 +#include <QtCore/QList>
11 +#include <QtCore/QString>
12 +#include <QtGui/qwindowdefs.h> // for WId
13 +
14 +class TextHookPrivate;
15 +/// Singleton class. Only one instance is allowed.
16 +class TEXTHOOK_EXPORT TextHook : public QObject
17 +{
18 + Q_OBJECT
19 + Q_DISABLE_COPY(TextHook)
20 + SK_EXTEND_CLASS(TextHook, QObject)
21 + SK_DECLARE_PRIVATE(TextHookPrivate)
22 +
23 + // - Construction -
24 +public:
25 + explicit TextHook(QObject *parent = nullptr);
26 + ~TextHook();
27 +
28 +signals:
29 + void dataReceived(QByteArray raw, QByteArray rendered, qint32 signature, QString source);
30 + void processAttached(qint64 pid);
31 + void processDetached(qint64 pid);
32 +
33 + // - Properties -
34 +public:
35 + /// Limited by ITH
36 + int capacity() const;
37 +
38 + bool isEnabled() const;
39 + void setEnabled(bool t);
40 +
41 + WId parentWinId() const; ///< Must be set to a valid window so that ::SetTimer works
42 + void setParentWinId(WId hwnd);
43 +
44 + int interval() const; ///< Time to differentiate sentences
45 + void setInterval(int msecs);
46 +
47 + int dataCapacity() const; ///< Maximum text length
48 + void setDataCapacity(int value);
49 +
50 + bool removesRepeat() const;
51 + void setRemovesRepeat(bool value);
52 +
53 + bool keepsSpace() const;
54 + void setKeepsSpace(bool value);
55 +
56 + bool wideCharacter() const;
57 + void setWideCharacter(bool value);
58 +
59 + QString defaultHookName() const; ///< The default one is "H-code"
60 + void setDefaultHookName(const QString &name);
61 +
62 + bool isActive() const;
63 + void start();
64 + void stop();
65 + void clear();
66 +
67 + // - Injection -
68 +public:
69 + //bool attachOneProcess(ulong pid, bool checkActive = false);
70 + bool attachProcess(ulong pid, bool checkActive = false);
71 + bool detachProcess(ulong pid, bool checkActive = false);
72 + bool hijackProcess(ulong pid);
73 + //void detachAllProcesses();
74 + //QList<ulong> attachedProcesses(bool checkActive = false) const;
75 + //ulong anyAttachedProcess(bool checkActive = false) const;
76 + //ulong currentProccess() const;
77 +
78 + bool containsProcess(ulong pid) const;
79 + bool isEmpty() const; ///< Return true if at least one process is attached
80 +
81 + bool addHookCode(ulong pid, const QString &code, const QString &name = QString(), bool verbose = true);
82 + static bool verifyHookCode(const QString &code); ///< Return if hcode is valid
83 + //bool containsHook(ulong pid) const;
84 + //bool containsHook(ulong pid, const QString &code) const;
85 + //QString processHook(ulong pid) const;
86 + //QString currentHook() const { return processHook(currentProccess()); }
87 + bool removeHookCode(ulong pid); ///< Assume atmost one hcode per process
88 +
89 + // - Whitelist -
90 +public:
91 + bool isThreadWhitelistEnabled() const;
92 + void setThreadWhitelistEnabled(bool t);
93 + QList<qint32> threadWhitelist() const;
94 + void setThreadWhitelist(const QList<qint32> &signatures);
95 + void clearThreadWhitelist();
96 + // Note: len(v) must be smaller than 0x200
97 + void setKeptThreadName(const QString &v);
98 + QString keptThreadName() const;
99 +};
100 +
101 +// EOF
1 +# texthook.pri
2 +# 10/13/2011 jichi
3 +
4 +DEFINES += WITH_LIB_TEXTHOOK
5 +
6 +INCLUDEPATH += $$PWD
7 +DEPENDPATH += $$PWD
8 +
9 +QT += core
10 +LIBS += -ltexthook
11 +
12 +HEADERS += \
13 + $$PWD/texthook_config.h
14 +
15 +# texthook.h should not be in HEADERS, or it will be processed by moc
16 +OTHER_FILES += \
17 + $$PWD/texthook.h \
18 + $$PWD/texthook.pro
19 +
20 +# EOF
1 +# texthook.pro
2 +# 10/13/2011 jichi
3 +# Build ith texthook dll.
4 +
5 +CONFIG += noqtgui dll #eha # eha will catch all exceptions, but does not work on Windows XP
6 +include(../../../config.pri)
7 +include(host/host.pri)
8 +include($$LIBDIR/winmutex/winmutex.pri)
9 +include($$LIBDIR/wintimer/wintimer.pri)
10 +include($$LIBDIR/windbg/windbg.pri)
11 +#include($$LIBDIR/winmaker/winmaker.pri)
12 +
13 +# TODO: Get rid of dependence on ITHSYS and NT APIs
14 +include($$PLUGINDIR/vnrhook/vnrhook.pri)
15 +include($$PLUGINDIR/ithsys/ithsys.pri)
16 +LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
17 +
18 +DEFINES += ITH_HAS_CRT # Use native CRT
19 +
20 +# TODO: Get rid of dependence on msvc's swprintf
21 +DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
22 +
23 +## Libraries
24 +
25 +QT += core
26 +QT -= gui
27 +
28 +## Sources
29 +
30 +TEMPLATE = lib
31 +TARGET = texthook
32 +
33 +#CONFIG += staticlib
34 +DEFINES += TEXTHOOK_BUILD_LIB
35 +
36 +HEADERS += \
37 + growl.h \
38 + ihf_p.h \
39 + ith_p.h \
40 + texthook_config.h \
41 + texthook.h \
42 + texthook_p.h \
43 + textthread_p.h \
44 + winapi_p.h
45 +
46 +SOURCES += \
47 + ihf_p.cc \
48 + ith_p.cc \
49 + texthook.cc \
50 + textthread_p.cc \
51 + winapi_p.cc
52 +
53 +#!wince*: LIBS += -lshell32
54 +#RC_FILE += texthook.rc
55 +
56 +OTHER_FILES += \
57 + texthook.pri \
58 + texthook_static.pri \
59 + texthook.rc
60 +
61 +# EOF
1 +/*
2 + * texthook.rc
3 + * 10/20/2011 jichi
4 + */
5 +#if defined(UNDER_CE)
6 +# include <winbase.h>
7 +#else
8 +# include <winver.h>
9 +#endif
10 +
11 +VS_VERSION_INFO VERSIONINFO
12 + FILEVERSION 1,0,0,0
13 + PRODUCTVERSION 1,0,0,0
14 + FILEFLAGSMASK 0x3fL
15 +#ifdef _DEBUG
16 + FILEFLAGS VS_FF_DEBUG
17 +#else
18 + FILEFLAGS 0x0L
19 +#endif
20 + FILEOS VOS__WINDOWS32
21 + FILETYPE VFT_DLL
22 + FILESUBTYPE 0x0L
23 + BEGIN
24 + BLOCK "StringFileInfo"
25 + BEGIN
26 + BLOCK "040904B0"
27 + BEGIN
28 + VALUE "CompanyName", "Sakuradite\0"
29 + VALUE "FileDescription", "Text Hook\0"
30 + VALUE "FileVersion", "1.0.0.0\0"
31 + VALUE "LegalCopyright", "Copyright (C) 2012.\0"
32 + VALUE "OriginalFilename", "texthook.dll\0"
33 + VALUE "ProductName", "texthook\0"
34 + END
35 + END
36 + END
37 +
38 +/* End of Version info */
1 +#pragma once
2 +
3 +// texthook_config.h
4 +// 10/20/2011 jichi
5 +
6 +//#define TEXTHOOK_EXPORT
7 +
8 +#ifndef TEXTHOOK_EXPORT
9 +# ifdef TEXTHOOK_STATIC_LIB
10 +# define TEXTHOOK_EXPORT
11 +# elif defined(TEXTHOOK_BUILD_LIB)
12 +# define TEXTHOOK_EXPORT Q_DECL_EXPORT
13 +# else
14 +# define TEXTHOOK_EXPORT Q_DECL_IMPORT
15 +# endif
16 +#endif // TEXTHOOK_EXPORT
17 +
18 +#define TEXTHOOK_DEFAULT_NAME "H-code"
19 +
20 +// EOF
1 +#pragma once
2 +
3 +// texthook_p.h
4 +// 10/14/2011 jichi
5 +// Internal header.
6 +// Defines TextHook private data.
7 +
8 +#include "texthook/texthook.h"
9 +#include <QtCore/QHash>
10 +#include <QtCore/QSet>
11 +
12 +// - Private -
13 +
14 +class TextHookPrivate
15 +{
16 + SK_CLASS(TextHookPrivate)
17 + SK_DECLARE_PUBLIC(TextHook)
18 +
19 + static Self *instance_; // global instance
20 +
21 + bool enabled;
22 + QString source;
23 + QSet<ulong> pids;
24 + QHash<ulong, QString> hooks; // ITH hook code, indexed by pid
25 +
26 + explicit TextHookPrivate(Q *q)
27 + : q_(q), enabled(true), source(TEXTHOOK_DEFAULT_NAME) { instance_ = this; }
28 +
29 + ~TextHookPrivate() { instance_ = nullptr; }
30 +
31 +public:
32 + static void sendData(const QByteArray &rawData, const QByteArray &renderedData, qint32 signature, const QString &name)
33 + {
34 + if (instance_ && instance_->q_->isEnabled())
35 + emit instance_->q_->dataReceived(rawData, renderedData, signature, name);
36 + }
37 +};
38 +
39 +// EOF
1 +# texthook_static.pri
2 +# 10/13/2011 jichi
3 +
4 +include(../../../config.pri)
5 +include($$LIBDIR/wintimer/wintimer.pri)
6 +
7 +## Libraries
8 +
9 +#DEFINES += _ITH_DEBUG_MEMORY
10 +
11 +QT += core
12 +
13 +INCLUDEPATH += $$PWD
14 +DEPENDPATH += $$PWD
15 +
16 +#WDK_HOME = c:/winddk
17 +#LIBS += -L$$WDK_HOME/lib
18 +# override folder must come before winddk/inc/api
19 +#INCLUDEPATH += $$PWD/override/wdk/vc10
20 +#INCLUDEPATH += $$WDK_HOME/include/api
21 +#INCLUDEPATH += $$WDK_HOME/include/crt
22 +#INCLUDEPATH += $$WDK_HOME/include/ddk
23 +
24 +#ITH_HOME = c:/dev/ith
25 +#INCLUDEPATH += $$ITH_HOME/include
26 +#LIBS += -L$$ITH_HOME/lib
27 +#LIBS += -lITH_DLL #-lITH_SYS
28 +
29 +#INCLUDEPATH += $$ITH_HOME/include
30 +#LIBS += -L$$ITH_HOME/lib -lihf
31 +
32 +# Tell IHF not to override new operators, see: ith/mem.h
33 +DEFINES += DEFAULT_MM
34 +
35 +#LIBS += -lmsvcrtd
36 +#LIBS += -lmsvcrt
37 +#QMAKE_LFLAGS += /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:msvcrtd.lib
38 +DEFINES += _CRT_SECURE_NO_WARNINGS _CRT_NON_CONFORMING_SWPRINTFS
39 +
40 +## Sources
41 +
42 +#TEMPLATE = lib
43 +#TARGET = texthook
44 +#CONFIG += dll
45 +#CONFIG += staticlib
46 +#DEFINES += TEXTHOOK_BUILD_LIB
47 +DEFINES += TEXTHOOK_STATIC_LIB
48 +
49 +HEADERS += \
50 + $$PWD/ihf_p.h \
51 + $$PWD/ith_p.h \
52 + $$PWD/texthook_config.h \
53 + $$PWD/texthook.h \
54 + $$PWD/texthook_p.h \
55 + $$PWD/textthread_p.h \
56 + $$PWD/winapi_p.h
57 +
58 +SOURCES += \
59 + $$PWD/ihf_p.cc \
60 + $$PWD/ith_p.cc \
61 + $$PWD/texthook.cc \
62 + $$PWD/textthread_p.cc \
63 + $$PWD/winapi_p.cc
64 +
65 +#!wince*: LIBS += -lshell32
66 +#RC_FILE += texthook.rc
67 +
68 +OTHER_FILES += \
69 + $$PWD/texthook.pri \
70 + $$PWD/texthook.pro \
71 + $$PWD/texthook.rc
72 +
73 +# EOF
1 +// textthread_p.cc
2 +// 6/6/2012 jichi
3 +
4 +#include "texthook/textthread_p.h"
5 +#include "texthook/texthook_p.h"
6 +#include "winmutex/winmutex.h"
7 +#include "wintimer/wintimer.h"
8 +#include "host/textthread.h"
9 +#include <QtCore/QRegExp>
10 +
11 +//#define DEBUG "textthread_p.cc"
12 +#include "sakurakit/skdebug.h"
13 +
14 +enum { ITH_THREAD_NAME_CAPACITY = 0x200 }; // used internally by ITH
15 +
16 +#define REPEAT_RX_1 "(.{2,})\\1+$" // The pattern has at least 2 bytes, and repeats at least once
17 +
18 +/** Private class */
19 +
20 +#define D_LOCK win_mutex_lock<D::mutex_type> d_lock(D::globalMutex) // Synchronized scope for accessing private data
21 +
22 +class TextThreadDelegatePrivate
23 +{
24 + SK_CLASS(TextThreadDelegatePrivate)
25 + SK_DISABLE_COPY(TextThreadDelegatePrivate)
26 +
27 +public:
28 + typedef win_mutex<CRITICAL_SECTION> mutex_type;
29 +
30 + static mutex_type globalMutex; // Used only in public class. Because ITH is running in another single thread
31 + static int globalCapacity; // maximum text size
32 + static bool globalRemovesRepeat;
33 + static bool globalKeepsSpace;
34 + static bool globalWideCharacter;
35 +
36 + TextThread *t;
37 + WinTimer flushTimer; // as QTimer does not work with windows remote thread, use native WM_TIMER instead
38 +
39 + ulong signature; // buffered
40 + char sourceBuffer[ITH_THREAD_NAME_CAPACITY]; // buffered
41 + QString source; // buffered
42 +
43 + int bufferSize;
44 + int bufferCapacity;
45 + char *buffer;
46 + bool removesRepeat;
47 +
48 + QByteArray spaceBuffer;
49 + int spaceCount;
50 +
51 + struct Repeat
52 + {
53 + QRegExp rx; // cached
54 + char *buffer; // repeated string
55 + int size;
56 + int pos; // >= 0, current pos of repeating string
57 + int offset; // offset of repeated string
58 +
59 + Repeat() : rx(REPEAT_RX_1), buffer(nullptr), size(0), pos(0), offset(-1) {}
60 + ~Repeat() { if (buffer) delete[] buffer; }
61 +
62 + void clear()
63 + {
64 + size = pos = 0;
65 + offset = -1;
66 + }
67 +
68 + bool isRepeating(const char *data, int len) const
69 + {
70 + if (!size || !buffer)
71 + return false;
72 + switch (len) {
73 + case 1: return pos < size && buffer[pos] == *data;
74 + case 2: return pos < size + 1 && buffer[pos] == data[0] && buffer[pos +1] == data[1];
75 + default:
76 + if (pos + len >= size)
77 + return false;
78 + for (int i = 0; i < len; i++)
79 + if (buffer[pos + i] != data[i])
80 + return false;
81 + return true;
82 + }
83 + }
84 + } repeat;
85 +
86 + // - Construction -
87 +public:
88 + explicit TextThreadDelegatePrivate(TextThread *thread) : t(thread),
89 + bufferSize(0), bufferCapacity(globalCapacity), buffer(new char[globalCapacity]),
90 + spaceCount(0),
91 + removesRepeat(false)
92 + {
93 + signature = signatureOf(t);
94 +
95 + //size_t size =
96 + t->GetThreadString(sourceBuffer, ITH_THREAD_NAME_CAPACITY);
97 + source = sourceBuffer;
98 + }
99 +
100 + ~TextThreadDelegatePrivate() { delete[] buffer; }
101 +
102 + // - Properties -
103 +public:
104 + //QString text() const { return QString::fromLocal8Bit(buffer); }
105 + //ulong context() const { return t->GetThreadParameter()->retn; }
106 + //ulong subcontext() const { return t->GetThreadParameter()->spl; }
107 +
108 + //ulong processId() const { return t->PID(); }
109 +
110 + // - Actions -
111 +public:
112 + void flush()
113 + {
114 + if (flushTimer.isActive())
115 + flushTimer.stop();
116 + if (bufferSize) {
117 + send();
118 + bufferSize = 0;
119 + }
120 + if (!spaceBuffer.isEmpty())
121 + spaceBuffer.clear();
122 + spaceCount = 0;
123 + }
124 +
125 + void syncGlobal()
126 + {
127 + if (bufferCapacity < globalCapacity) {
128 + delete[] buffer;
129 + bufferCapacity = globalCapacity;
130 + buffer = new char[bufferCapacity];
131 + if (repeat.buffer) {
132 + delete[] repeat.buffer;
133 + if (!removesRepeat)
134 + repeat.buffer = nullptr;
135 + else {
136 + char *largerBuffer = new char[bufferCapacity];
137 + if (repeat.size)
138 + qMemCopy(largerBuffer, repeat.buffer, repeat.size);
139 + repeat.buffer = largerBuffer;
140 + }
141 + }
142 + //bufferSize = repeatOffset = 0; // already reset in flush
143 + //if (removesRepeat)
144 + // repeat.reset();
145 + }
146 + if (removesRepeat != globalRemovesRepeat) {
147 + removesRepeat = globalRemovesRepeat;
148 + if (removesRepeat)
149 + repeat.clear();
150 + }
151 + }
152 +
153 + void appendSpace()
154 + {
155 + flushTimer.start();
156 + spaceCount++;
157 + if (spaceBuffer.isEmpty())
158 + spaceBuffer.append(buffer, bufferSize);
159 + spaceBuffer.append(' ');
160 + if (globalWideCharacter)
161 + spaceBuffer.append('\0'); // L' ' = {'\x20', '\0'};
162 + }
163 +
164 + void append(const char *data, int len)
165 + {
166 + flushTimer.start();
167 + if (bufferSize < qMin(bufferCapacity, globalCapacity))
168 + switch (len) {
169 + case 1: buffer[bufferSize++] = *data; break;
170 + case 2: buffer[bufferSize++] = *data;
171 + if (bufferSize < bufferCapacity)
172 + buffer[bufferSize++] = data[1];
173 + break;
174 + default:
175 + {
176 + int diff = qMin(len, bufferCapacity - bufferSize);
177 + qMemCopy(buffer + bufferSize, data, diff);
178 + bufferSize += diff;
179 + }
180 + }
181 + if (!spaceBuffer.isEmpty())
182 + spaceBuffer.append(data, len);
183 + }
184 +
185 + void appendRepeat(const char *data, int len)
186 + {
187 + if (bufferSize + len >= qMin(bufferCapacity, globalCapacity)) // overflow
188 + return;
189 + if (repeat.isRepeating(data, len)) {
190 + repeat.pos += len;
191 + if (repeat.pos >= repeat.size)
192 + repeat.pos = 0;
193 + return;
194 + }
195 + repeat.clear();
196 +
197 + append(data, len);
198 +
199 + if (bufferSize >= 6) { // at least 2 characters
200 + // Use fromLatin1 to prevent the data from being decoded
201 + QString t = QString::fromLatin1(buffer, bufferSize);
202 + repeat.offset = repeat.rx.indexIn(t);
203 + if (repeat.offset >= 0) {
204 + repeat.size = repeat.rx.cap(1).size();
205 + if (!repeat.buffer)
206 + repeat.buffer = new char[bufferCapacity];
207 + qMemCopy(repeat.buffer, buffer + repeat.offset, repeat.size);
208 + //bufferSize = repeat.offset repeat.size;
209 + }
210 + }
211 + }
212 +
213 +private:
214 + void send()
215 + {
216 + int size;
217 + if (removesRepeat && repeat.offset >= 0 && repeat.size)
218 + size = repeat.offset + repeat.size;
219 + else
220 + size = bufferSize;
221 + if (!spaceBuffer.isEmpty() && spaceBuffer.size() != size)
222 + spaceBuffer.truncate(size + spaceCount);
223 + TextHookPrivate::sendData(
224 + QByteArray(buffer, size), spaceBuffer,
225 + signature, source);
226 + }
227 +
228 + static qint32 signatureOf(TextThread *t)
229 + {
230 + qint32 ret =
231 + (t->GetThreadParameter()->retn & 0xffff) | // context
232 + (t->GetThreadParameter()->spl & 0xffff) << 16; // subcontext
233 + return ret ? ret : t->Addr();
234 + }
235 +
236 + //static QString sourceOf(TextThread *t);
237 +
238 +public:
239 + static ulong contextOf(TextThread *t)
240 + { return t->GetThreadParameter()->retn; }
241 +
242 + static ulong subcontextOf(TextThread *t)
243 + { return t->GetThreadParameter()->spl; }
244 +};
245 +
246 +TextThreadDelegatePrivate::mutex_type TextThreadDelegatePrivate::globalMutex;
247 +int TextThreadDelegatePrivate::globalCapacity = 512;
248 +bool TextThreadDelegatePrivate::globalRemovesRepeat = false;
249 +bool TextThreadDelegatePrivate::globalKeepsSpace = false;
250 +bool TextThreadDelegatePrivate::globalWideCharacter = false;
251 +
252 +//QString TextThreadDelegatePrivate::sourceOf(TextThread *t)
253 +//{
254 +// Q_ASSERT(t);
255 +// QString ret;
256 +// enum { buf_size = 0x200 }; // 0x200 is used by ITH internally
257 +// wchar_t buf[buf_size];
258 +// ulong len = t->GetThreadString(buf, buf_size);
259 +// if (len)
260 +// ret = QString::fromWCharArray(buf, len);
261 +// return ret;
262 +//}
263 +
264 +/** Public class */
265 +
266 +// - Constructions -
267 +
268 +TextThreadDelegate::TextThreadDelegate(TextThread *t)
269 + : d_(new D(t))
270 +{
271 + d_->flushTimer.setMethod(this, &Self::flush);
272 + d_->flushTimer.setSingleShot(true);
273 +}
274 +
275 +TextThreadDelegate::~TextThreadDelegate()
276 +{
277 + if (d_->flushTimer.isActive())
278 + d_->flushTimer.stop();
279 + delete d_;
280 +}
281 +
282 +bool TextThreadDelegate::delegateOf(const Self *that) const
283 +{
284 + Q_ASSERT(t);
285 + // Both have no context, and my subcontext is smaller
286 + return that
287 + && !D::contextOf(that->d_->t) && !D::contextOf(d_->t)
288 + && D::subcontextOf(that->d_->t) >= D::subcontextOf(d_->t)
289 + && ::strcmp(d_->sourceBuffer, that->d_->sourceBuffer) == 0
290 + && nameEquals("Malie");
291 +}
292 +
293 +// - Properties -
294 +
295 +//TextThread *TextThreadDelegate::t() const { return d_->t; }
296 +int TextThreadDelegate::threadNumber() const
297 +{ return d_->t->Number(); }
298 +
299 +qint32 TextThreadDelegate::signature() const
300 +{ return d_->signature; }
301 +
302 +QString TextThreadDelegate::name() const
303 +{ return d_->source; }
304 +
305 +bool TextThreadDelegate::nameEquals(const char *that) const
306 +{ return !::strcmp(d_->sourceBuffer, that); }
307 +
308 +int TextThreadDelegate::capacity() { return D::globalCapacity; }
309 +void TextThreadDelegate::setCapacity(int value) { D::globalCapacity = value; }
310 +
311 +bool TextThreadDelegate::removesRepeat() { return D::globalRemovesRepeat; }
312 +void TextThreadDelegate::setRemovesRepeat(bool value) { D::globalRemovesRepeat = value; }
313 +
314 +bool TextThreadDelegate::wideCharacter() { return D::globalWideCharacter; }
315 +void TextThreadDelegate::setWideCharacter(bool value) { D::globalWideCharacter = value; }
316 +
317 +bool TextThreadDelegate::keepsSpace() { return D::globalKeepsSpace; }
318 +void TextThreadDelegate::setKeepsSpace(bool value) { D::globalKeepsSpace = value; }
319 +
320 +void TextThreadDelegate::setInterval(int msecs)
321 +{ d_->flushTimer.setInterval(msecs); }
322 +
323 +void TextThreadDelegate::setParentWindow(WId winId)
324 +{ d_->flushTimer.setParentWindow(winId); }
325 +
326 +// - Actions -
327 +
328 +void TextThreadDelegate::flush()
329 +{
330 + D_LOCK;
331 + d_->flush();
332 + d_->syncGlobal();
333 +}
334 +
335 +void TextThreadDelegate::touch()
336 +{
337 + D_LOCK;
338 + d_->flushTimer.start();
339 +}
340 +
341 +void TextThreadDelegate::append(const char *data, int len, bool space)
342 +{
343 + D_LOCK;
344 + if (space && D::globalKeepsSpace)
345 + d_->appendSpace();
346 + if (data && len) {
347 + if (d_->removesRepeat)
348 + d_->appendRepeat(data, len);
349 + else
350 + d_->append(data, len);
351 + }
352 +}
353 +
354 +// EOF
355 +/*
356 +void TextThreadDelegate::append(const QByteArray &data)
357 +{
358 + D::mutex_lock_type locker(D::mutex);
359 +
360 + d_->flushTimer.start();
361 + if (d_->buffer.size() <= D::capacity)
362 + d_->buffer.append(data);
363 +}
364 +void TextThreadDelegatePrivate::send()
365 +{
366 +#ifdef DEBUG
367 + qDebug()<< source()
368 + << t->Number()
369 + << t->PID()
370 + << QString::number(t->Addr(), 16)
371 + << QString::number(t->GetThreadParameter()->retn, 16)
372 + << QString::number(t->GetThreadParameter()->spl, 16)
373 + << QTextCodec::codecForName("SHIFT-JIS")->makeDecoder()->toUnicode(buffer);
374 +#endif // DEBUG
375 +}
376 +*/
1 +#pragma once
2 +
3 +// textthread_p.h
4 +// 6/6/2012 jichi
5 +// Internal header.
6 +// Defines TextHook delegate class.
7 +
8 +#include "sakurakit/skglobal.h"
9 +#include <QtGui/qwindowdefs.h>
10 +
11 +//QT_FORWARD_DECLARE_CLASS(QByteArray)
12 +
13 +class SharedRef
14 +{
15 + SK_CLASS(SharedRef)
16 + int count_;
17 +public:
18 + SharedRef(): count_(1) {}
19 + int retainCount() const { return count_; }
20 + void retain() { count_++; }
21 + //void release() { count_--; }
22 + static void release(Self *x) { if (--x->count_ <= 0) delete x; }
23 +};
24 +
25 +// FIXME: This class is not thread-safe!
26 +class TextThread;
27 +class TextThreadDelegatePrivate;
28 +class TextThreadDelegate : public SharedRef
29 +{
30 + SK_EXTEND_CLASS(TextThreadDelegate, SharedRef)
31 + SK_DISABLE_COPY(TextThreadDelegate)
32 + SK_DECLARE_PRIVATE(TextThreadDelegatePrivate)
33 +public:
34 + explicit TextThreadDelegate(TextThread *t);
35 + ~TextThreadDelegate();
36 +
37 + bool delegateOf(const Self *t) const;
38 +
39 + // - Properties -
40 +
41 + //TextThread *t() const;
42 + int threadNumber() const;
43 + qint32 signature() const;
44 + QString name() const;
45 + bool nameEquals(const char *that) const; // optimized
46 +
47 + // Maximum text size
48 + static int capacity();
49 + static void setCapacity(int value);
50 +
51 + static bool wideCharacter();
52 + static void setWideCharacter(bool value);
53 +
54 + static bool removesRepeat();
55 + static void setRemovesRepeat(bool value);
56 +
57 + static bool keepsSpace();
58 + static void setKeepsSpace(bool value);
59 +
60 + //TextThread *t() const;
61 +
62 + //int interval() const;
63 + void setInterval(int msecs);
64 +
65 + //WId parentWindow() const;
66 + void setParentWindow(WId winId);
67 +
68 + // - Actions -
69 +
70 + //void append(const QByteArray &data);
71 + /** Add data to the text thread
72 + * @param data raw data
73 + * @param len length of the data
74 + * @param space Whether have LEADING space
75 + */
76 + void append(const char *data, int len, bool space=false);
77 + void flush();
78 + void touch(); // keep timer running
79 +};
80 +
81 +// EOF
1 +// apiwin_p.cc
2 +// 10/6/2012 jichi
3 +#include "texthook/winapi_p.h"
4 +#include <windows.h>
5 +
6 +WINAPI_BEGIN_NAMESPACE
7 +
8 +bool IsProcessActiveWithId(DWORD dwProcessId)
9 +{
10 + bool ret = false;
11 + if (HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId)) {
12 + DWORD dwExitCode;
13 + ret = ::GetExitCodeProcess(hProc, &dwExitCode) && (dwExitCode == STILL_ACTIVE);
14 + ::CloseHandle(hProc);
15 + }
16 + return ret;
17 +}
18 +
19 +WINAPI_END_NAMESPACE
20 +
21 +// EOF
1 +#pragma once
2 +// winapi_p.h
3 +// 10/5/2012 jichi
4 +// Internal header.
5 +// Wrapper of <windows.h>
6 +
7 +#ifndef WINAPI_BEGIN_NAMESPACE
8 +# define WINAPI_BEGIN_NAMESPACE namespace winapi {
9 +#endif
10 +#ifndef WINAPI_END_NAMESPACE
11 +# define WINAPI_END_NAMESPACE } // namespace winapi
12 +#endif
13 +
14 +WINAPI_BEGIN_NAMESPACE
15 +bool IsProcessActiveWithId(unsigned long dwProcessId);
16 +WINAPI_END_NAMESPACE
17 +
18 +// EOF
1 +# # hook.pro
2 +# # Exception handler to catch all exceptions
3 +# CONFIG += dll noqt eh eha # noeh nosafeseh
4 +
5 +# DEFINES += ITH_HAS_CRT ITH_HAS_SEH
6 +# DEFINES += MEMDBG_NO_STL NTINSPECT_NO_STL # disabled as not used
7 +
8 +# # jichi 11/13/2011: disable swprinf warning
9 +# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
10 +
11 +# config.pri
12 +# CONFIG(eha) {
13 +# message(CONFIG eha)
14 +# QMAKE_CXXFLAGS_STL_ON -= /EHsc
15 +# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
16 +# QMAKE_CXXFLAGS_STL_ON += /EHa
17 +# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
18 +# }
19 +
20 +include_directories(${CMAKE_CURRENT_SOURCE_DIR})
21 +
22 +set(vnrhook_src
23 + include/const.h
24 + include/defs.h
25 + include/types.h
26 + src/except.h
27 + src/main.cc
28 + src/main.h
29 + src/pipe.cc
30 + src/engine/engine.cc
31 + src/engine/engine.h
32 + src/engine/hookdefs.h
33 + src/engine/match.cc
34 + src/engine/match.h
35 + src/engine/pchooks.cc
36 + src/engine/pchooks.h
37 + src/engine/mono/funcinfo.h
38 + src/engine/mono/types.h
39 + src/engine/ppsspp/funcinfo.h
40 + src/hijack/texthook.cc
41 + src/hijack/texthook.h
42 + src/tree/avl.h
43 + src/util/growl.h
44 + src/util/util.cc
45 + src/util/util.h
46 + ${PROJECT_SOURCE_DIR}/ccutil/ccmacro.h
47 + ${PROJECT_SOURCE_DIR}/cpputil/cpplocale.h
48 + ${PROJECT_SOURCE_DIR}/cpputil/cppmarshal.h
49 + ${PROJECT_SOURCE_DIR}/cpputil/cppmath.h
50 + ${PROJECT_SOURCE_DIR}/cpputil/cpppath.h
51 + ${PROJECT_SOURCE_DIR}/cpputil/cppstring.h
52 + ${PROJECT_SOURCE_DIR}/cpputil/cpptype.h
53 + ${PROJECT_SOURCE_DIR}/cpputil/cppunicode.h
54 + ${PROJECT_SOURCE_DIR}/disasm/disasm.cc
55 + ${PROJECT_SOURCE_DIR}/hashutil/hashstr.h
56 + ${PROJECT_SOURCE_DIR}/hashutil/hashutil.h
57 + ${PROJECT_SOURCE_DIR}/memdbg/memdbg.h
58 + ${PROJECT_SOURCE_DIR}/memdbg/memsearch.cc
59 + ${PROJECT_SOURCE_DIR}/memdbg/memsearch.h
60 + ${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.cc
61 + ${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.h
62 + ${PROJECT_SOURCE_DIR}/winkey/winkey.h
63 + ${PROJECT_SOURCE_DIR}/winversion/winversion.cc
64 + ${PROJECT_SOURCE_DIR}/winversion/winversion.h
65 + ${PROJECT_SOURCE_DIR}/winseh/winseh.h
66 + ${PROJECT_SOURCE_DIR}/winseh/winseh.cc
67 + ${PROJECT_SOURCE_DIR}/winseh/winseh_safe.cc
68 + ${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
69 + ${PROJECT_SOURCE_DIR}/mono/monoobject.h
70 + ${PROJECT_SOURCE_DIR}/mono/monotype.h
71 +)
72 +
73 +add_library(vnrhook SHARED ${vnrhook_src})
74 +
75 +enable_language(ASM_MASM)
76 +
77 +set_source_files_properties(
78 + ${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
79 + PROPERTIES
80 + # CMAKE_ASM_MASM_FLAGS /safeseh # CMake bug 14711: http://www.cmake.org/Bug/view.php?id=14711
81 + COMPILE_FLAGS /safeseh
82 +)
83 +
84 +set_target_properties(vnrhook PROPERTIES
85 + LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO"
86 +)
87 +
88 +target_compile_options(vnrhook PRIVATE
89 + /EHa
90 + $<$<CONFIG:Release>:>
91 + $<$<CONFIG:Debug>:>
92 +)
93 +
94 +set(vnrhook_libs
95 + ithsys
96 + ${WDK_HOME}/lib/wxp/i386/ntdll.lib
97 + Version.lib
98 +)
99 +
100 +target_link_libraries(vnrhook ${vnrhook_libs})
101 +
102 +target_compile_definitions(vnrhook
103 + PRIVATE
104 + ITH_HAS_CRT
105 + ITH_HAS_SEH
106 + _CRT_NON_CONFORMING_SWPRINTFS
107 +)
108 +
109 +install(TARGETS vnrhook RUNTIME
110 + DESTINATION .
111 + CONFIGURATIONS Release
112 +)
1 +# dllconfig.pri
2 +# 8/9/2013 jichi
3 +# For linking ITH injectable dlls.
4 +# The dll is self-contained 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 +# hookxp.pro
2 +# 8/9/2013 jichi
3 +# Build vnrhookxp.dll for Windows XP
4 +
5 +CONFIG += noeh # msvcrt on Windows XP does not has exception handler
6 +include(../dllconfig.pri)
7 +include(../sys/sys.pri)
8 +include($$LIBDIR/disasm/disasm.pri)
9 +include($$LIBDIR/memdbg/memdbg.pri)
10 +include($$LIBDIR/ntinspect/ntinspect.pri)
11 +include($$LIBDIR/winkey/winkey.pri)
12 +include($$LIBDIR/winseh/winseh_safe.pri)
13 +include($$LIBDIR/winversion/winversion.pri)
14 +
15 +VPATH += ../hook
16 +INCLUDEPATH += ../hook
17 +
18 +# 9/27/2013: disable ITH this game engine, only for debugging purpose
19 +#DEFINES += ITH_DISABLE_ENGINE
20 +
21 +# jichi 9/22/2013: When ITH is on wine, mutex is needed to protect NtWriteFile
22 +#DEFINES += ITH_WINE
23 +#DEFINES += ITH_SYNC_PIPE
24 +
25 +DEFINES += MEMDBG_NO_STL NTINSPECT_NO_STL
26 +
27 +## Libraries
28 +
29 +LIBS += -lkernel32 -luser32 -lgdi32 #-lgdiplus
30 +
31 +## Sources
32 +
33 +TEMPLATE = lib
34 +TARGET = vnrhookxp
35 +
36 +#CONFIG += staticlib
37 +
38 +HEADERS += \
39 + config.h \
40 + cli.h \
41 + hook.h \
42 + engine/engine.h \
43 + engine/hookdefs.h \
44 + engine/match.h \
45 + engine/pchooks.h \
46 + engine/util.h \
47 + tree/avl.h
48 +
49 +SOURCES += \
50 + main.cc \
51 + rpc/pipe.cc \
52 + hijack/texthook.cc \
53 + engine/engine.cc \
54 + engine/match.cc \
55 + engine/pchooks.cc \
56 + engine/util.cc
57 +
58 +#RC_FILE += vnrhook.rc
59 +#OTHER_FILES += vnrhook.rc
60 +
61 +# 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 +12/16/2013
2 +
3 +Differences between xp.dll and non-xp.dll for vnrhook.
4 +
5 +non-xp:
6 + CONFIG += eh
7 +
8 +xp:
9 + CONFIG += noeh
10 + CONFIG -= embed_manifest_dll # Pure dynamic determined. The manifest would break Windows XP support
11 + include($$LIBDIR/winseh/winseh_safe.pri)
1 +#pragma once
2 +
3 +// vnrhook/const.h
4 +// 8/23/2013 jichi
5 +// Branch: ITH/common.h, rev 128
6 +
7 +// jichi 9/9/2013: Another importnat function is lstrcatA, which is already handled by
8 +// Debonosu hooks. Wait until it is really needed by certain games.
9 +// The order of the functions is used in several place.
10 +// I need to recompile all of the dlls to modify the order.
11 +
12 +// jichi 10/14/2014
13 +#define HOOK_GDI_FUNCTION_LIST \
14 + GetTextExtentPoint32A \
15 + , GetTextExtentExPointA \
16 + , GetTabbedTextExtentA \
17 + , GetCharacterPlacementA \
18 + , GetGlyphIndicesA \
19 + , GetGlyphOutlineA \
20 + , ExtTextOutA \
21 + , TextOutA \
22 + , TabbedTextOutA \
23 + , GetCharABCWidthsA \
24 + , GetCharABCWidthsFloatA \
25 + , GetCharWidth32A \
26 + , GetCharWidthFloatA \
27 + , GetTextExtentPoint32W \
28 + , GetTextExtentExPointW \
29 + , GetTabbedTextExtentW \
30 + , GetCharacterPlacementW \
31 + , GetGlyphIndicesW \
32 + , GetGlyphOutlineW \
33 + , ExtTextOutW \
34 + , TextOutW \
35 + , TabbedTextOutW \
36 + , GetCharABCWidthsW \
37 + , GetCharABCWidthsFloatW \
38 + , GetCharWidth32W \
39 + , GetCharWidthFloatW \
40 + , DrawTextA \
41 + , DrawTextExA \
42 + , DrawTextW \
43 + , DrawTextExW
44 + //, CharNextA
45 + //, CharPrevA
46 +//enum { HOOK_FUN_COUNT = 30 }; // total number of GDI hooks
47 +// jichi 1/16/2015: Though called max hook, it means max number of text threads
48 +enum { MAX_HOOK = 64 }; // must be larger than HOOK_FUN_COUNT
49 +
50 +//enum { HOOK_SECTION_SIZE = 0x2000 }; // default ITH value
51 +// jichi 1/16/2015: Change to a very large number to prevent crash
52 +//enum { MAX_HOOK = 0x100 }; // must be larger than HookFunCount
53 +enum { HOOK_SECTION_SIZE = MAX_HOOK * 0x100 }; // default ITH value is 0x2000 for 32 hook (0x100 per hook)
54 +
55 +// jichi 375/2014: Add offset of pusha/pushad
56 +// http://faydoc.tripod.com/cpu/pushad.htm
57 +// http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
58 +//
59 +// Warning: The offset in ITH has -4 offset comparing to pusha and AGTH
60 +enum pusha_off {
61 + pusha_eax_off = -0x4
62 + , pusha_ecx_off = -0x8
63 + , pusha_edx_off = -0xc
64 + , pusha_ebx_off = -0x10
65 + , pusha_esp_off = -0x14
66 + , pusha_ebp_off = -0x18
67 + , pusha_esi_off = -0x1c
68 + , pusha_edi_off = -0x20
69 + , pusha_off = -0x24 // pushad offset
70 +};
71 +
72 +enum HostCommandType {
73 + HOST_COMMAND = -1 // null type
74 + , HOST_COMMAND_NEW_HOOK = 0
75 + , HOST_COMMAND_REMOVE_HOOK = 1
76 + , HOST_COMMAND_MODIFY_HOOK = 2
77 + , HOST_COMMAND_HIJACK_PROCESS = 3
78 + , HOST_COMMAND_DETACH = 4
79 +};
80 +
81 +enum HostNotificationType {
82 + HOST_NOTIFICATION = -1 // null type
83 + , HOST_NOTIFICATION_TEXT = 0
84 + , HOST_NOTIFICATION_NEWHOOK = 1
85 +};
86 +
87 +// jichi 9/8/2013: The meaning are guessed
88 +// Values must be within DWORD
89 +// Unused values are as follows:
90 +// - 0x100
91 +enum HookParamType : unsigned long {
92 + USING_STRING = 0x1 // type(data) is char* or wchar_t* and has length
93 + , USING_UTF8 = USING_STRING // jichi 10/21/2014: temporarily handled the same way as USING_STRING
94 + , USING_UNICODE = 0x2 // type(data) is wchar_t or wchar_t*
95 + , BIG_ENDIAN = 0x4 // type(data) is char
96 + , DATA_INDIRECT = 0x8
97 + , USING_SPLIT = 0x10 // aware of split time?
98 + , SPLIT_INDIRECT = 0x20
99 + , MODULE_OFFSET = 0x40 // do hash module, and the address is relative to module
100 + , FUNCTION_OFFSET = 0x80 // do hash function, and the address is relative to funccion
101 + , PRINT_DWORD = 0x100 // jichi 12/7/2014: Removed
102 + , NO_ASCII = 0x100 // jichi 1l/22/2015: Skip ascii characters
103 + , STRING_LAST_CHAR = 0x200
104 + , NO_CONTEXT = 0x400
105 + , HOOK_EMPTY = 0x800
106 + , FIXING_SPLIT = 0x1000
107 + //, HOOK_AUXILIARY = 0x2000 // jichi 12/13/2013: None of known hooks are auxiliary
108 + , RELATIVE_SPLIT = 0x2000 // jichi 10/24/2014: relative split return address
109 + , HOOK_ENGINE = 0x4000
110 + , HOOK_ADDITIONAL = 0x8000
111 +};
112 +
113 +// 6/1/2014: Fixed split value for hok parameter
114 +// Fuse all threads, and prevent floating
115 +enum { FIXED_SPLIT_VALUE = 0x10001 };
116 +
117 +// jichi 12/18/2013:
118 +// These dlls are used to guess the range for non-NO_CONTEXT hooks.
119 +//
120 +// Disabling uxtheme.dll would crash certain system: http://tieba.baidu.com/p/2764436254
121 +#define IHF_FILTER_DLL_LIST \
122 + /* ITH original filters */ \
123 + L"gdiplus.dll" /* Graphics functions like TextOutA */ \
124 + , L"lpk.dll" /* Language package scripts and fonts */ \
125 + , L"msctf.dll" /* Text service */ \
126 + , L"psapi.dll" /* Processes */ \
127 + , L"usp10.dll" /* UNICODE rendering */ \
128 + , L"user32.dll" /* Non-graphics functions like lstrlenA */ \
129 + , L"uxtheme.dll" /* Theme */ \
130 + \
131 + /* Windows DLLs */ \
132 + , L"advapi32.dll" /* Advanced services */ \
133 + , L"apphelp.dll" /* Appliation help */ \
134 + , L"audioses.dll" /* Audios */ \
135 + , L"avrt.dll" /* Audio video runtime */ \
136 + , L"cfgmgr32.dll" /* Configuration manager */ \
137 + , L"clbcatq.dll" /* COM query service */ \
138 + , L"comctl32.dll" /* Common control library */ \
139 + , L"comdlg32.dll" /* Common dialogs */ \
140 + , L"crypt32.dll" /* Security cryption */ \
141 + , L"cryptbase.dll"/* Security cryption */ \
142 + , L"cryptsp.dll" /* Security cryption */ \
143 + , L"d3d8thk.dll" /* Direct3D 8 */ \
144 + , L"d3d9.dll" /* Direct3D 9 */ \
145 + , L"dbghelp.dll" /* Debug help */ \
146 + , L"dciman32.dll" /* Display cotrol */ \
147 + , L"devobj.dll" /* Device object */ \
148 + , L"ddraw.dll" /* Direct draw */ \
149 + , L"dinput.dll" /* Diret input */ \
150 + , L"dsound.dll" /* Direct sound */ \
151 + , L"DShowRdpFilter.dll" /* Direct show */ \
152 + , L"dwmapi.dll" /* Windows manager */ \
153 + , L"gdi32.dll" /* GDI32 */ \
154 + , L"hid.dll" /* HID user library */ \
155 + , L"iertutil.dll" /* IE runtime */ \
156 + , L"imagehlp.dll" /* Image help */ \
157 + , L"imm32.dll" /* Input method */ \
158 + , L"ksuser.dll" /* Kernel service */ \
159 + , L"ole32.dll" /* COM OLE */ \
160 + , L"oleacc.dll" /* OLE access */ \
161 + , L"oleaut32.dll" /* COM OLE */ \
162 + , L"kernel.dll" /* Kernel functions */ \
163 + , L"kernelbase.dll" /* Kernel functions */ \
164 + , L"midimap.dll" /* MIDI */ \
165 + , L"mmdevapi.dll" /* Audio device */ \
166 + , L"mpr.dll" /* Winnet */ \
167 + , L"msacm32.dll" /* MS ACM */ \
168 + , L"msacm32.drv" /* MS ACM */ \
169 + , L"msasn1.dll" /* Encoding/decoding */ \
170 + , L"msimg32.dll" /* Image */ \
171 + , L"msvfw32.dll" /* Media play */ \
172 + , L"netapi32.dll" /* Network service */ \
173 + , L"normaliz.dll" /* Normalize */ \
174 + , L"nsi.dll" /* NSI */ \
175 + , L"ntdll.dll" /* NT functions */ \
176 + , L"ntmarta.dll" /* NT MARTA */ \
177 + , L"nvd3dum.dll" /* Direct 3D */ \
178 + , L"powerprof.dll"/* Power profile */ \
179 + , L"profapi.dll" /* Profile API */ \
180 + , L"propsys.dll" /* System properties */ \
181 + , L"quartz.dll" /* OpenGL */ \
182 + , L"rpcrt4.dll" /* RPC runtime */ \
183 + , L"rpcrtremote.dll" /* RPC runtime */ \
184 + , L"rsabase.dll" /* RSA cryption */ \
185 + , L"rsaenh.dll" /* RSA cryption */ \
186 + , L"schannel.dll" /* Security channel */ \
187 + , L"sechost.dll" /* Service host */ \
188 + , L"setupapi.dll" /* Setup service */ \
189 + , L"shell32.dll" /* Windows shell */ \
190 + , L"shlwapi.dll" /* Light-weighted shell */ \
191 + , L"slc.dll" /* SLC */ \
192 + , L"srvcli.dll" /* Service client */ \
193 + , L"version.dll" /* Windows version */ \
194 + , L"wdmaud.drv" /* Wave output */ \
195 + , L"wldap32.dll" /* Wireless */ \
196 + , L"wininet.dll" /* Internet access */ \
197 + , L"winmm.dll" /* Windows sound */ \
198 + , L"winsta.dll" /* Connection system */ \
199 + , L"wtsapi32.dll" /* Windows terminal server */ \
200 + , L"wintrust.dll" /* Windows trust */ \
201 + , L"wsock32.dll" /* Windows sock */ \
202 + , L"ws2_32.dll" /* Terminal server */ \
203 + , L"wkscli.dll" /* ACIS */ \
204 + \
205 + /* MSVCRT */ \
206 + , L"msvcrt.dll" /* VC rutime */ \
207 + , L"msvcr80.dll" /* VC rutime 8 */ \
208 + , L"msvcp80.dll" /* VC rutime 8 */ \
209 + , L"msvcr90.dll" /* VC rutime 9 */ \
210 + , L"msvcp90.dll" /* VC rutime 9 */ \
211 + , L"msvcr100.dll" /* VC rutime 10 */ \
212 + , L"msvcp100.dll" /* VC rutime 10 */ \
213 + , L"msvcr110.dll" /* VC rutime 11 */ \
214 + , L"msvcp110.dll" /* VC rutime 11 */ \
215 + \
216 + /* VNR */ \
217 + , L"vnrhook.dll" \
218 + , L"vnrhookxp.dll" \
219 + \
220 + /* Sogou IME */ \
221 + , L"sogoupy.ime" \
222 + , L"PicFace.dll" \
223 + , L"AddressSearch.dll" \
224 + \
225 + /* QQ IME */ \
226 + , L"QQPINYIN.IME" \
227 + \
228 + /* AlphaROM */ \
229 + , L"kDays.dll" \
230 + \
231 + /* 360Safe */ \
232 + , L"safemon.dll" \
233 + \
234 + /* Locale changers */ \
235 + , L"AlLayer.dll" /* AppLocale */ \
236 + , L"LocaleEmulator.dll" /* Locale Emulator */ \
237 + , L"LSH.dll" /* LocaleSwitch */ \
238 + , L"ntleah.dll" /* NTLEA */
239 +
240 + // Google Japanese IME
241 + //, L"GoogleIMEJaTIP32.dll"
242 +
243 +enum {
244 + //IHF_FILTER_COUNT = 7
245 + IHF_FILTER_COUNT = 7 + 72 + 9 + 4 + 3 + 1 + 1 + 1 + 4 // count of total dlls to filter
246 + , IHF_FILTER_CAPACITY = IHF_FILTER_COUNT + 1 // one more than the dll count
247 +};
248 +
249 +// EOF
1 +#pragma once
2 +
3 +// vnrhook/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 +// jichi 7/12/2015:
31 +// ITH IO name prefix, needed by Windows 10 for NT event and mutex APIs
32 +// Otherwise, NT functions will return status = STATUS_OBJECT_PATH_SYNTAX_BAD
33 +//#define ITH_PATH_ L"\\BaseNamedObjects\\"
34 +#define ITH_PATH_ L""
35 +
36 +#define ITH_PROCESS_MUTEX_ ITH_PATH_ L"VNR_PROCESS_" // ITH_%d
37 +#define ITH_HOOKMAN_MUTEX_ ITH_PATH_ L"VNR_HOOKMAN_" // ITH_HOOKMAN_%d
38 +#define ITH_DETACH_MUTEX_ ITH_PATH_ L"VNR_DETACH_" // ITH_DETACH_%d
39 +
40 +#define ITH_GRANTPIPE_MUTEX ITH_PATH_ L"VNR_GRANT_PIPE" // ITH_GRANT_PIPE
41 +
42 +#define ITH_CLIENT_MUTEX ITH_PATH_ L"VNR_CLIENT" // ITH_DLL_RUNNING
43 +#define ITH_SERVER_MUTEX ITH_PATH_ L"VNR_SERVER" // ITH_RUNNING
44 +#define ITH_SERVER_HOOK_MUTEX ITH_PATH_ L"VNR_SERVER_HOOK" // original
45 +
46 +// Events
47 +
48 +#define ITH_REMOVEHOOK_EVENT ITH_PATH_ L"VNR_REMOVE_HOOK" // ITH_REMOVE_HOOK
49 +#define ITH_MODIFYHOOK_EVENT ITH_PATH_ L"VNR_MODIFY_HOOK" // ITH_MODIFY_HOOK
50 +#define ITH_PIPEEXISTS_EVENT ITH_PATH_ L"VNR_PIPE_EXISTS" // ITH_PIPE_EXIST
51 +
52 +// EOF
1 +#pragma once
2 +
3 +// vnrhook/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.
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 address; // absolute or relative address
31 + DWORD offset, // offset of the data in the memory
32 + index, // ?
33 + split, // esp offset of the split character = pusha offset - 4
34 + split_index; // ?
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 + LPSTR hook_name;
80 + int name_length;
81 + BYTE recover[0x68 - sizeof(HookParam)];
82 + BYTE original[0x10];
83 +
84 + DWORD Address() const { return hp.address; }
85 + DWORD Type() const { return hp.type; }
86 + WORD Length() const { return hp.hook_len; }
87 + LPSTR Name() const { return hook_name; }
88 + int NameLength() const { return name_length; }
89 +};
90 +
91 +// 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 <windows.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 +// Game-speicific engines
74 +bool InsertShinyDaysGameHook(); // ShinyDays
75 +bool InsertLovaGameHook(); // lova.jp
76 +
77 +// PC engines
78 +
79 +bool Insert2RMHook(); // 2RM - Adventure Engine
80 +bool Insert5pbHook(); // 5pb.jp, PSP/PS3 games ported to PC
81 +bool InsertAB2TryHook(); // Yane@AkabeiSoft2Try: YaneSDK.dll.
82 +bool InsertAbelHook(); // Abel
83 +bool InsertAdobeAirHook(); // Adobe AIR
84 +bool InsertAdobeFlash10Hook(); // Adobe Flash Player 10
85 +bool InsertAliceHook(); // System40@AliceSoft; do not work for latest alice games
86 +//bool InsertAmuseCraftHook(); // AMUSE CRAFT: *.pac
87 +bool InsertAnex86Hook(); // Anex86: anex86.exe
88 +bool InsertAOSHook(); // AOS: *.aos
89 +bool InsertApricoTHook(); // Apricot: arc.a*
90 +bool InsertArtemisHook(); // Artemis Engine: *.pfs
91 +bool InsertAtelierHook(); // Atelier Kaguya: message.dat
92 +bool InsertBGIHook(); // BGI: BGI.*
93 +bool InsertBootupHook(); // Bootup: Bootup.dat
94 +bool InsertC4Hook(); // C4: C4.EXE or XEX.EXE
95 +bool InsertCaramelBoxHook(); // Caramel: *.bin
96 +bool InsertCandyHook(); // SystemC@CandySoft: *.fpk
97 +bool InsertCatSystemHook(); // CatSystem2: *.int
98 +bool InsertCMVSHook(); // CMVS: data/pack/*.cpz; do not support the latest cmvs32.exe and cmvs64.exe
99 +bool InsertCotophaHook(); // Cotopha: *.noa
100 +bool InsertDebonosuHook(); // Debonosu: bmp.bak and dsetup.dll
101 +bool InsertEaglsHook(); // E.A.G.L.S: EAGLES.dll
102 +bool InsertEMEHook(); // EmonEngine: emecfg.ecf
103 +bool InsertEscudeHook(); // Escude
104 +bool InsertEushullyHook(); // Eushully: AGERC.DLL
105 +bool InsertExpHook(); // EXP: http://www.exp-inc.jp
106 +bool InsertFocasLensHook(); // FocasLens: Dat/*.arc, http://www.fo-lens.net
107 +bool InsertGXPHook(); // GXP: *.gxp
108 +bool InsertHorkEyeHook(); // HorkEye: resource string
109 +bool InsertKAGParserHook(); // plugin/KAGParser.dll
110 +bool InsertKAGParserExHook(); // plugin/KAGParserEx.dll
111 +bool InsertKiriKiriHook(); // KiriKiri: *.xp3, resource string
112 +bool InsertKiriKiriZHook(); // KiriKiri: *.xp3, resource string
113 +bool InsertLeafHook(); // Leaf: *.pak
114 +bool InsertLiveHook(); // Live: live.dll
115 +bool InsertLunaSoftHook(); // LunaSoft: Pac/*.pac
116 +bool InsertMalieHook(); // Malie@light: malie.ini
117 +bool InsertMajiroHook(); // Majiro: *.arc
118 +bool InsertMarineHeartHook(); // Marine Heart: SAISYS.exe
119 +bool InsertMBLHook(); // MBL: *.mbl
120 +bool InsertMEDHook(); // MED: *.med
121 +bool InsertMinkHook(); // Mink: *.at2
122 +//bool InsertMonoHook(); // Mono (Unity3D): */Mono/mono.dll
123 +bool InsertNeXASHook(); // NeXAS: Thumbnail.pac
124 +bool InsertNextonHook(); // NEXTON: aInfo.db
125 +bool InsertNexton1Hook();
126 +bool InsertNitroplusHook(); // Nitroplus: *.npa
127 +bool InsertPalHook(); // AMUSE CRAFT: *.pac
128 +bool InsertPensilHook(); // Pensil: PSetup.exe
129 +bool InsertQLIEHook(); // QLiE: GameData/*.pack
130 +//bool InsertRai7Hook(); // Rai7puk: rai7.exe
131 +bool InsertRejetHook(); // Rejet: Module/{gd.dat,pf.dat,sd.dat}
132 +bool InsertRUGPHook(); // rUGP: rUGP.exe
133 +bool InsertRetouchHook(); // Retouch: resident.dll
134 +bool InsertRREHook(); // RunrunEngine: rrecfg.rcf
135 +bool InsertShinaHook(); // ShinaRio: Rio.ini
136 +bool InsertElfHook(); // elf: Silky.exe
137 +bool InsertScenarioPlayerHook();// sol-fa-soft: *.iar && *.sec5
138 +bool InsertSiglusHook(); // SiglusEngine: SiglusEngine.exe
139 +bool InsertSideBHook(); // SideB: Copyright side-B
140 +bool InsertSilkysHook(); // SilkysPlus
141 +bool InsertSyuntadaHook(); // Syuntada: dSoh.dat
142 +bool InsertSystem43Hook(); // System43@AliceSoft: AliceStart.ini
143 +bool InsertSystemAoiHook(); // SystemAoi: *.vfs
144 +bool InsertTamamoHook(); // Tamamo
145 +bool InsertTanukiHook(); // Tanuki: *.tak
146 +bool InsertTaskforce2Hook(); // Taskforce2.exe
147 +bool InsertTencoHook(); // Tenco: Check.mdx
148 +bool InsertTriangleHook(); // Triangle: Execle.exe
149 +bool InsertUnicornHook(); // Gsen18: *.szs|Data/*.szs
150 +bool InsertWillPlusHook(); // WillPlus: Rio.arc
151 +bool InsertWolfHook(); // Wolf: Data.wolf
152 +bool InsertYukaSystem2Hook(); // YukaSystem2: *.ykc
153 +bool InsertYurisHook(); // YU-RIS: *.ypf
154 +
155 +void InsertBrunsHook(); // Bruns: bruns.exe
156 +void InsertIronGameSystemHook();// IroneGameSystem: igs_sample.exe
157 +void InsertLucifenHook(); // Lucifen@Navel: *.lpk
158 +void InsertRyokuchaHook(); // Ryokucha: _checksum.exe
159 +void InsertRealliveHook(); // RealLive: RealLive*.exe
160 +void InsertStuffScriptHook(); // Stuff: *.mpk
161 +void InsertTinkerBellHook(); // TinkerBell: arc00.dat
162 +void InsertWaffleHook(); // WAFFLE: cg.pak
163 +
164 +// CIRCUS: avdata/
165 +bool InsertCircusHook1();
166 +bool InsertCircusHook2();
167 +
168 +} // namespace Engine
169 +
170 +// EOF
1 +#pragma once
2 +
3 +// engine/hookdefs.h
4 +// 7/20/2014 jichi
5 +
6 +// For HookParam user flags
7 +enum HookParamFlag : unsigned long {
8 + HPF_Null = 0 // never used
9 + , HPF_IgnoreSameAddress = 1 // ignore the last same text address
10 +};
11 +
12 +// EOF
1 +// 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 "src/engine/match.h"
11 +#include "src/engine/engine.h"
12 +#include "src/engine/pchooks.h"
13 +#include "src/util/growl.h"
14 +#include "src/util/util.h"
15 +#include "src/main.h"
16 +#include "src/except.h"
17 +#include "ithsys/ithsys.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 +bool DetermineGameHooks() // 7/19/2015
44 +{
45 +#if 0 // jichi 7/19/2015: Disabled as it will crash the game
46 + if (IthFindFile(L"UE3ShaderCompileWorker.exe") && IthFindFile(L"awesomium_process.exe")) {
47 + InsertLovaGameHook();
48 + return true;
49 + }
50 +#endif // 0
51 + return false;
52 +}
53 +
54 +// jichi 7/17/2014: Disable GDI hooks for PPSSPP
55 +bool DeterminePCEngine()
56 +{
57 + if (DetermineGameHooks()) {
58 + ConsoleOutput("vnreng: found game-specific hook");
59 + return true;
60 + }
61 +
62 + if (IthFindFile(L"PPSSPP*.exe")) { // jichi 7/12/2014 PPSSPPWindows.exe, PPSSPPEX.exe PPSSPPSP.exe
63 + InsertPPSSPPHooks();
64 + return true;
65 + }
66 +
67 + if (IthFindFile(L"pcsx2*.exe")) { // jichi 7/19/2014 PCSX2.exe or PCSX2WX.exe
68 + InsertPCSX2Hooks();
69 + return true;
70 + }
71 +
72 + if (IthFindFile(L"Dolphin.exe")) { // jichi 7/20/2014
73 + InsertGCHooks();
74 + return true;
75 + }
76 +
77 + // jichi 5/14/2015: Skip hijacking BALDRSKY ZEROs
78 + if (IthCheckFile(L"bsz_Data\\Mono\\mono.dll") || IthCheckFile(L"bsz2_Data\\Mono\\mono.dll")) {
79 + ConsoleOutput("vnreng: IGNORE BALDRSKY ZEROs");
80 + return true;
81 + }
82 + if (::GetModuleHandleA("mono.dll")) {
83 + InsertMonoHooks();
84 +
85 + // 3/20/2015 jichi
86 + // Always insert GDI hooks even for Mono games
87 + // For example: ?? need GetGlyphOutlineA
88 + PcHooks::hookGDIFunctions();
89 + return true;
90 + }
91 +
92 + // PC games
93 + PcHooks::hookGDIFunctions();
94 + EnableGDIPlusHooks();
95 + return false;
96 +}
97 +
98 +bool DetermineEngineByFile1()
99 +{
100 + if (IthFindFile(L"*.xp3") || Util::SearchResourceString(L"TVP(KIRIKIRI)")) {
101 + if (Util::SearchResourceString(L"TVP(KIRIKIRI) Z ")) { // TVP(KIRIKIRI) Z CORE
102 + // jichi 11/24/2014: Disabled that might crash VBH
103 + //if (IthCheckFile(L"plugin\\KAGParser.dll"))
104 + // InsertKAGParserHook();
105 + //else if (IthCheckFile(L"plugin\\KAGParserEx.dll"))
106 + // InsertKAGParserExHook();
107 + if (InsertKiriKiriZHook())
108 + return true;
109 + }
110 + InsertKiriKiriHook();
111 + return true;
112 + }
113 + // 8/2/2014 jichi: Game name shown as 2RM - Adventure Engine, text also in GetGlyphOutlineA
114 + if (Util::SearchResourceString(L"2RM") && Util::SearchResourceString(L"Adventure Engine")) {
115 + Insert2RMHook();
116 + return true;
117 + }
118 + // 8/2/2014 jichi: Copyright is side-B, a conf.dat will be generated after the game is launched
119 + // It also contains lua5.1.dll and lua5.dll
120 + if (Util::SearchResourceString(L"side-B")) {
121 + InsertSideBHook();
122 + return true;
123 + }
124 + if (IthFindFile(L"bgi.*") || IthFindFile(L"BHVC.exe") || IthFindFile(L"sysgrp.arc")) {
125 + InsertBGIHook();
126 + return true;
127 + }
128 + if (IthCheckFile(L"Bootup.dat") && InsertBootupHook()) // 5/22/2015 Bootup
129 + // lstrlenW can also find text with repetition though
130 + return true;
131 + if (IthCheckFile(L"AGERC.DLL")) { // 6/1/2014 jichi: Eushully, AGE.EXE
132 + InsertEushullyHook();
133 + return true;
134 + }
135 + if (IthFindFile(L"data*.arc") && IthFindFile(L"stream*.arc")) {
136 + InsertMajiroHook();
137 + return true;
138 + }
139 + // jichi 5/31/2014
140 + if (//IthCheckFile(L"Silkys.exe") || // It might or might not have Silkys.exe
141 + // data, effect, layer, mes, music
142 + IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"mes.arc")) {
143 + InsertElfHook();
144 + return true;
145 + }
146 + // jichi 6/9/2015: Skip Silkys Sakura
147 + if ( // Almost the same as Silkys except mes.arc is replaced by Script.arc
148 + IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"Script.arc")) {
149 + InsertSilkysHook();
150 + return true;
151 + }
152 + if (IthFindFile(L"data\\pack\\*.cpz")) {
153 + InsertCMVSHook();
154 + return true;
155 + }
156 + // jichi 10/12/2013: Restore wolf engine
157 + // jichi 10/18/2013: Check for data/*.wolf
158 + if (IthFindFile(L"data.wolf") || IthFindFile(L"data\\*.wolf")) {
159 + InsertWolfHook();
160 + return true;
161 + }
162 + if (IthCheckFile(L"AdvData\\DAT\\NAMES.DAT")) {
163 + InsertCircusHook1();
164 + return true;
165 + }
166 + if (IthCheckFile(L"AdvData\\GRP\\NAMES.DAT")) {
167 + InsertCircusHook2();
168 + return true;
169 + }
170 + if (IthFindFile(L"*.noa") || IthFindFile(L"data\\*.noa")) {
171 + InsertCotophaHook();
172 + return true;
173 + }
174 + if (IthFindFile(L"*.pfs")) { // jichi 10/1/2013
175 + InsertArtemisHook();
176 + return true;
177 + }
178 + if (IthFindFile(L"*.int")) {
179 + InsertCatSystemHook();
180 + return true;
181 + }
182 + if (IthCheckFile(L"message.dat")) {
183 + InsertAtelierHook();
184 + return true;
185 + }
186 + if (IthCheckFile(L"Check.mdx")) { // jichi 4/1/2014: AUGame
187 + InsertTencoHook();
188 + return true;
189 + }
190 + // jichi 12/25/2013: It may or may not be QLIE.
191 + // AlterEgo also has GameData/sound.pack but is not QLIE
192 + if (IthFindFile(L"GameData\\*.pack") && InsertQLIEHook())
193 + return true;
194 +
195 + if (IthCheckFile(L"dll\\Pal.dll")) {
196 + InsertPalHook();
197 + return true;
198 + }
199 +
200 + if (IthFindFile(L"*.pac")) {
201 + // jichi 6/3/2014: AMUSE CRAFT and SOFTPAL
202 + // Selectively insert, so that lstrlenA can still get correct text if failed
203 + //if (IthCheckFile(L"dll\\resource.dll") && IthCheckFile(L"dll\\pal.dll") && InsertAmuseCraftHook())
204 + // return true;
205 +
206 + if (IthCheckFile(L"Thumbnail.pac")) {
207 + //ConsoleOutput("vnreng: IGNORE NeXAS");
208 + InsertNeXASHook(); // jichi 7/6/2014: GIGA
209 + return true;
210 + }
211 +
212 + if (Util::SearchResourceString(L"SOFTPAL")) {
213 + ConsoleOutput("vnreng: IGNORE SoftPal UNiSONSHIFT");
214 + return true;
215 + }
216 + }
217 + // jichi 12/27/2014: LunaSoft
218 + if (IthFindFile(L"Pac\\*.pac")) {
219 + InsertLunaSoftHook();
220 + return true;
221 + }
222 + // jichi 9/16/2013: Add Gesen18
223 + if (IthFindFile(L"*.szs") || IthFindFile(L"Data\\*.szs")) {
224 + InsertUnicornHook();
225 + return true;
226 + }
227 + // jichi 12/22/2013: Add rejet
228 + if (IthCheckFile(L"gd.dat") && IthCheckFile(L"pf.dat") && IthCheckFile(L"sd.dat")) {
229 + InsertRejetHook();
230 + return true;
231 + }
232 + // Only examined with version 1.0
233 + //if (IthFindFile(L"Adobe AIR\\Versions\\*\\Adobe AIR.dll")) { // jichi 4/15/2014: FIXME: Wildcard not working
234 + if (IthCheckFile(L"Adobe AIR\\Versions\\1.0\\Adobe AIR.dll")) { // jichi 4/15/2014: Adobe AIR
235 + InsertAdobeAirHook();
236 + return true;
237 + }
238 + return false;
239 +}
240 +
241 +bool DetermineEngineByFile2()
242 +{
243 + if (IthCheckFile(L"resident.dll")) {
244 + InsertRetouchHook();
245 + return true;
246 + }
247 + if (IthCheckFile(L"Malie.ini") || IthCheckFile(L"Malie.exe")) { // jichi: 9/9/2014: Add malie.exe in case malie.ini is missing
248 + InsertMalieHook();
249 + return true;
250 + }
251 + if (IthCheckFile(L"live.dll")) {
252 + InsertLiveHook();
253 + return true;
254 + }
255 + // 9/5/2013 jichi
256 + if (IthCheckFile(L"aInfo.db")) {
257 + InsertNextonHook();
258 + return true;
259 + }
260 + if (IthFindFile(L"*.lpk")) {
261 + InsertLucifenHook();
262 + return true;
263 + }
264 + if (IthCheckFile(L"cfg.pak")) {
265 + InsertWaffleHook();
266 + return true;
267 + }
268 + if (IthCheckFile(L"Arc00.dat")) {
269 + InsertTinkerBellHook();
270 + return true;
271 + }
272 + if (IthFindFile(L"*.vfs")) { // jichi 7/6/2014: Better to test AoiLib.dll? ja.wikipedia.org/wiki/իȫϫ
273 + InsertSystemAoiHook();
274 + return true;
275 + }
276 + if (IthFindFile(L"*.mbl")) {
277 + InsertMBLHook();
278 + return true;
279 + }
280 + // jichi 8/1/2014: YU-RIS engine, lots of clockup game also has this pattern
281 + if (IthFindFile(L"pac\\*.ypf") || IthFindFile(L"*.ypf")) {
282 + // jichi 8/14/2013: CLOCLUP: "?֫쫹֫?" would crash the game.
283 + if (!IthCheckFile(L"noblesse.exe"))
284 + InsertYurisHook();
285 + return true;
286 + }
287 + if (IthFindFile(L"*.npa")) {
288 + InsertNitroplusHook();
289 + return true;
290 + }
291 + return false;
292 +}
293 +
294 +bool DetermineEngineByFile3()
295 +{
296 + //if (IthCheckFile(L"libscr.dll")) { // already checked
297 + // InsertBrunsHook();
298 + // return true;
299 + //}
300 +
301 + // jichi 10/12/2013: Sample args.txt:
302 + // See: http://tieba.baidu.com/p/2631413816
303 + // -workdir
304 + // .
305 + // -loadpath
306 + // .
307 + // am.cfg
308 + if (IthCheckFile(L"args.txt")) {
309 + InsertBrunsHook();
310 + return true;
311 + }
312 + if (IthCheckFile(L"emecfg.ecf")) {
313 + InsertEMEHook();
314 + return true;
315 + }
316 + if (IthCheckFile(L"rrecfg.rcf")) {
317 + InsertRREHook();
318 + return true;
319 + }
320 + if (IthFindFile(L"*.fpk") || IthFindFile(L"data\\*.fpk")) {
321 + InsertCandyHook();
322 + return true;
323 + }
324 + if (IthFindFile(L"arc.a*")) {
325 + InsertApricoTHook();
326 + return true;
327 + }
328 + if (IthFindFile(L"*.mpk")) {
329 + InsertStuffScriptHook();
330 + return true;
331 + }
332 + if (IthCheckFile(L"Execle.exe")) {
333 + InsertTriangleHook();
334 + return true;
335 + }
336 + // jichi 2/28/2015: No longer work for "᡿??꫹ episode I" from Primula
337 + //if (IthCheckFile(L"PSetup.exe")) {
338 + // InsertPensilHook();
339 + // return true;
340 + //}
341 + if (IthCheckFile(L"Yanesdk.dll")) {
342 + InsertAB2TryHook();
343 + return true;
344 + }
345 + if (IthFindFile(L"*.med")) {
346 + InsertMEDHook();
347 + return true;
348 + }
349 + return false;
350 +}
351 +
352 +bool DetermineEngineByFile4()
353 +{
354 + if (IthCheckFile(L"EAGLS.dll")) { // jichi 3/24/2014: E.A.G.L.S
355 + //ConsoleOutput("vnreng: IGNORE EAGLS");
356 + InsertEaglsHook();
357 + return true;
358 + }
359 + if (IthCheckFile(L"bmp.pak") && IthCheckFile(L"dsetup.dll")) {
360 + // 1/1/2016 jich: skip izumo4 from studio ego that is not supported by debonosu
361 + if (IthFindFile(L"*izumo4*.exe")) {
362 + PcHooks::hookLstrFunctions();
363 + return true;
364 + }
365 + InsertDebonosuHook();
366 + return true;
367 + }
368 + if (IthCheckFile(L"C4.EXE") || IthCheckFile(L"XEX.EXE")) {
369 + InsertC4Hook();
370 + return true;
371 + }
372 + if (IthCheckFile(L"Rio.arc") && IthFindFile(L"Chip*.arc")) {
373 + InsertWillPlusHook();
374 + return true;
375 + }
376 + if (IthFindFile(L"*.tac")) {
377 + InsertTanukiHook();
378 + return true;
379 + }
380 + if (IthFindFile(L"*.gxp")) {
381 + InsertGXPHook();
382 + return true;
383 + }
384 + if (IthFindFile(L"*.aos")) { // jichi 4/2/2014: AOS hook
385 + InsertAOSHook();
386 + return true;
387 + }
388 + if (IthFindFile(L"*.at2")) { // jichi 12/23/2014: Mink, sample files: voice.at2, voice.det, voice.nme
389 + InsertMinkHook();
390 + return true;
391 + }
392 + if (IthFindFile(L"*.ykc")) { // jichi 7/15/2014: YukaSystem1 is not supported, though
393 + //ConsoleOutput("vnreng: IGNORE YKC:Feng/HookSoft(SMEE)");
394 + InsertYukaSystem2Hook();
395 + return true;
396 + }
397 + if (IthFindFile(L"model\\*.hed")) { // jichi 9/8/2014: EXP
398 + InsertExpHook();
399 + return true;
400 + }
401 + // jichi 2/6/2015
402 + // dPi.dat, dPih.dat, dSc.dat, dSch.dat, dSo.dat, dSoh.dat, dSy.dat
403 + //if (IthCheckFile(L"dSoh.dat")) { // no idea why this file does not work
404 + if (IthCheckFile(L"dSch.dat")) {
405 + InsertSyuntadaHook();
406 + return true;
407 + }
408 +
409 + // jichi 2/28/2015: Delay checking Pensil in case something went wrong
410 + // File pattern observed in [Primula] ᡿??꫹ episode I
411 + // - PSetup.exe no longer exists
412 + // - MovieTexture.dll information shows MovieTex dynamic library, copyright Pensil 2013
413 + // - ta_trial.exe information shows 2XT - Primula Adventure Engine
414 + if (IthCheckFile(L"PSetup.exe") || IthFindFile(L"PENCIL.*") || Util::SearchResourceString(L"2XT -")) {
415 + InsertPensilHook();
416 + return true;
417 + }
418 + return false;
419 +}
420 +
421 +bool DetermineEngineByProcessName()
422 +{
423 + WCHAR str[MAX_PATH];
424 + wcscpy(str, process_name_);
425 + _wcslwr(str); // lower case
426 +
427 + if (wcsstr(str,L"reallive") || IthCheckFile(L"Reallive.exe") || IthCheckFile(L"REALLIVEDATA\\Start.ini")) {
428 + InsertRealliveHook();
429 + return true;
430 + }
431 +
432 + // jichi 8/19/2013: DO NOT WORK for games likeϫԫ᫢
433 + //if (wcsstr(str,L"cmvs32") || wcsstr(str,L"cmvs64")) {
434 + // InsertCMVSHook();
435 + // return true;
436 + //}
437 +
438 + // jichi 8/17/2013: Handle "~"
439 + if (wcsstr(str, L"siglusengine") || !wcsncmp(str, L"siglus~", 7) || IthCheckFile(L"SiglusEngine.exe")) {
440 + InsertSiglusHook();
441 + return true;
442 + }
443 +
444 + if (wcsstr(str, L"taskforce2") || !wcsncmp(str, L"taskfo~", 7) || IthCheckFile(L"Taskforce2.exe")) {
445 + InsertTaskforce2Hook();
446 + return true;
447 + }
448 +
449 + if (wcsstr(str,L"rugp") || IthCheckFile(L"rugp.exe")) {
450 + InsertRUGPHook();
451 + return true;
452 + }
453 +
454 + // jichi 8/17/2013: Handle "~"
455 + if (wcsstr(str, L"igs_sample") || !wcsncmp(str, L"igs_sa~", 7) || IthCheckFile(L"igs_sample.exe")) {
456 + InsertIronGameSystemHook();
457 + return true;
458 + }
459 +
460 + if (wcsstr(str, L"bruns") || IthCheckFile(L"bruns.exe")) {
461 + InsertBrunsHook();
462 + return true;
463 + }
464 +
465 + if (wcsstr(str, L"anex86") || IthCheckFile(L"anex86.exe")) {
466 + InsertAnex86Hook();
467 + return true;
468 + }
469 +
470 + // jichi 8/17/2013: Handle "~"
471 + if (wcsstr(str, L"shinydays") || !wcsncmp(str, L"shinyd~", 7) || IthCheckFile(L"ShinyDays.exe")) {
472 + InsertShinyDaysGameHook();
473 + return true;
474 + }
475 +
476 + // jichi 10/3/2013: FIXME: Does not work
477 + // Raise C0000005 even with admin priv
478 + //if (wcsstr(str, L"bsz")) { // BALDRSKY ZERO
479 + // InsertBaldrHook();
480 + // return true;
481 + //}
482 +
483 + if (wcsstr(process_name_, L"SAISYS") || IthCheckFile(L"SaiSys.exe")) { // jichi 4/19/2014: Marine Heart
484 + InsertMarineHeartHook();
485 + return true;
486 + }
487 +
488 + DWORD len = wcslen(str);
489 +
490 + // jichi 8/24/2013: Checking for Rio.ini or $procname.ini
491 + //wcscpy(str+len-4, L"_?.war");
492 + //if (IthFindFile(str)) {
493 + // InsertShinaHook();
494 + // return true;
495 + //}
496 + if (InsertShinaHook())
497 + return true;
498 +
499 + // jichi 8/10/2013: Since *.bin is common, move CaramelBox to the end
500 + str[len - 3] = L'b';
501 + str[len - 2] = L'i';
502 + str[len - 1] = L'n';
503 + str[len] = 0;
504 + if ((IthCheckFile(str) || IthCheckFile(L"trial.bin")) // jichi 7/8/2014: add trial.bin
505 + && InsertCaramelBoxHook())
506 + return true;
507 +
508 + // jichi 7/23/2015 It also has gameexe.bin existed
509 + if (IthCheckFile(L"configure.exe") && IthCheckFile(L"configure.cfg") && IthCheckFile(L"gfx.bin")) {
510 + InsertEscudeHook();
511 + return true;
512 + }
513 +
514 + // This must appear at last since str is modified
515 + wcscpy(str + len - 4, L"_checksum.exe");
516 + if (IthCheckFile(str)) {
517 + InsertRyokuchaHook();
518 +
519 + if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games
520 + InsertScenarioPlayerHook();
521 + return true;
522 + }
523 +
524 + return false;
525 +}
526 +
527 +bool DetermineEngineOther()
528 +{
529 + if (InsertAliceHook())
530 + return true;
531 + // jichi 1/19/2015: Disable inserting Lstr for System40
532 + // See: http://sakuradite.com/topic/618
533 + if (IthCheckFile(L"System40.ini")) {
534 + ConsoleOutput("vnreng: IGNORE old System40.ini");
535 + return true;
536 + }
537 + // jichi 12/26/2013: Add this after alicehook
538 + if (IthCheckFile(L"AliceStart.ini")) {
539 + InsertSystem43Hook();
540 + return true;
541 + }
542 +
543 + // jichi 8/24/2013: Move into functions
544 + static BYTE static_file_info[0x1000];
545 + if (IthGetFileInfo(L"*01", static_file_info))
546 + if (*(DWORD*)static_file_info == 0) {
547 + static WCHAR static_search_name[MAX_PATH];
548 + LPWSTR name=(LPWSTR)(static_file_info+0x5E);
549 + int len = wcslen(name);
550 + name[len - 2] = L'*';
551 + name[len - 1] = 0;
552 + wcscpy(static_search_name, name);
553 + IthGetFileInfo(static_search_name, static_file_info);
554 + union {
555 + FILE_BOTH_DIR_INFORMATION *both_info;
556 + DWORD addr;
557 + };
558 + both_info = (FILE_BOTH_DIR_INFORMATION *)static_file_info;
559 + //BYTE* ptr=static_file_info;
560 + len = 0;
561 + while (both_info->NextEntryOffset) {
562 + addr += both_info->NextEntryOffset;
563 + len++;
564 + }
565 + if (len > 3) {
566 + InsertAbelHook();
567 + return true;
568 + }
569 + }
570 +
571 + return false;
572 +}
573 +
574 +// jichi 8/17/2014
575 +// Put the patterns that might break other games at last
576 +bool DetermineEngineAtLast()
577 +{
578 + if (IthCheckFile(L"MovieTexture.dll") && (InsertPensilHook() || Insert2RMHook())) // MovieTexture.dll also exists in 2RM games such as ٽ2??, which is checked first
579 + return true;
580 + if (IthFindFile(L"system") && IthFindFile(L"system.dat")) { // jichi 7/31/2015
581 + InsertAbelHook();
582 + return true;
583 + }
584 + if (IthFindFile(L"data\\*.cpk")) { // jichi 12/2/2014
585 + Insert5pbHook();
586 + return true;
587 + }
588 + // jichi 7/6/2014: named as ScenarioPlayer since resource string could be: scenario player program for xxx
589 + // Do this at last as it is common
590 + if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) { // jichi 4/18/2014: Other game engine could also have *.iar such as Ryokucha
591 + InsertScenarioPlayerHook();
592 + return true;
593 + }
594 + //if (IthCheckFile(L"arc0.dat") && IthCheckFile(L"script.dat") // jichi 11/14/2014: too common
595 + if (Util::SearchResourceString(L"HorkEye")) { // appear in copyright: Copyright (C) HorkEye, http://horkeye.com
596 + InsertHorkEyeHook();
597 + return true;
598 + }
599 + if (IthCheckFile(L"comnArc.arc") // jichi 8/17/2014: this file might exist in multiple files
600 + && InsertNexton1Hook()) // old nexton game
601 + return true;
602 + if (IthCheckFile(L"arc.dat") // jichi 9/27/2014: too common
603 + && InsertApricoTHook())
604 + return true;
605 + if (IthFindFile(L"*.pak") // jichi 12/25/2014: too common
606 + && InsertLeafHook())
607 + return true;
608 + // jichi 10/31/2014
609 + // File description: Adobe Flash Player 10.2r153
610 + // Product name: Shockwave Flash
611 + // Original filename: SAFlashPlayer.exe
612 + // Legal trademarks: Adobe Flash Player
613 + // No idea why, this must appear at last or it will crash
614 + if (Util::SearchResourceString(L"Adobe Flash Player 10")) {
615 + InsertAdobeFlash10Hook(); // only v10 might be supported. Otherwise, fallback to Lstr hooks
616 + return true;
617 + }
618 + if (IthFindFile(L"dat\\*.arc")) { // jichi 2/6/2015
619 + InsertFocasLensHook(); // Touhou
620 + return true;
621 + }
622 +
623 + // jichi 8/23/2015: Tamamo
624 + if (IthCheckFile(L"data.pck") && IthCheckFile(L"image.pck") && IthCheckFile(L"script.pck")) {
625 + //if (IthCheckFile(L"QtGui.dll"))
626 + InsertTamamoHook();
627 + return true;
628 + }
629 +
630 + return false;
631 +}
632 +
633 +// jichi 6/1/2014
634 +bool DetermineEngineGeneric()
635 +{
636 + bool ret = false;
637 +
638 + if (IthCheckFile(L"AlterEgo.exe")) {
639 + ConsoleOutput("vnreng: AlterEgo, INSERT WideChar hooks");
640 + ret = true;
641 + } else if (IthFindFile(L"data\\Sky\\*")) {
642 + ConsoleOutput("vnreng: TEATIME, INSERT WideChar hooks");
643 + ret = true;
644 + }
645 + //} else if (IthFindFile(L"image\\*.po2") || IthFindFile(L"image\\*.jo2")) {
646 + // ConsoleOutput("vnreng: HarukaKanata, INSERT WideChar hooks"); // Ϫ몫ʪ
647 + // ret = true;
648 + //}
649 + if (ret)
650 + PcHooks::hookWcharFunctions();
651 + return ret;
652 +}
653 +
654 +bool DetermineNoEngine()
655 +{
656 + //if (IthFindFile(L"*\\Managed\\UnityEngine.dll")) { // jichi 12/3/2013: Unity (BALDRSKY ZERO)
657 + // ConsoleOutput("vnreng: IGNORE Unity");
658 + // return true;
659 + //}
660 + //if (IthCheckFile(L"bsz_Data\\Managed\\UnityEngine.dll") || IthCheckFile(L"bsz2_Data\\Managed\\UnityEngine.dll")) {
661 + // ConsoleOutput("vnreng: IGNORE Unity");
662 + // return true;
663 + //}
664 +
665 + // jichi 6/7/2015: RPGMaker v3
666 + if (IthFindFile(L"*.rgss3a")) {
667 + ConsoleOutput("vnreng: IGNORE RPGMaker RGSS3");
668 + return true;
669 + }
670 +
671 + // jichi 11/22/2015: NECRO ??
672 + if (IthFindFile(L"*.npk")) {
673 + ConsoleOutput("vnreng: IGNORE new Nitroplus");
674 + return true;
675 + }
676 +
677 + // 8/29/2015 jichi: minori, text in GetGlyphOutlineA
678 + if (IthFindFile(L"*.paz")) {
679 + ConsoleOutput("vnreng: IGNORE minori");
680 + return true;
681 + }
682 +
683 + // 7/28/2015 jichi: Favorite games
684 + if (IthFindFile(L"*.hcb")) {
685 + ConsoleOutput("vnreng: IGNORE FVP");
686 + return true;
687 + }
688 +
689 + // jichi 2/14/2015: Guilty+ ңɣΡӣţ (PK)
690 + if (IthCheckFile(L"rio.ini") || IthFindFile(L"*.war")) {
691 + ConsoleOutput("vnreng: IGNORE unknown ShinaRio");
692 + return true;
693 + }
694 +
695 + if (IthCheckFile(L"AdvHD.exe") || IthCheckFile(L"AdvHD.dll")) {
696 + ConsoleOutput("vnreng: IGNORE Adv Player HD"); // supposed to be WillPlus
697 + return true;
698 + }
699 +
700 + if (IthCheckFile(L"ScrPlayer.exe")) {
701 + ConsoleOutput("vnreng: IGNORE ScrPlayer");
702 + return true;
703 + }
704 +
705 + if (IthCheckFile(L"nnnConfig2.exe")) {
706 + ConsoleOutput("vnreng: IGNORE Nya NNNConfig");
707 + return true;
708 + }
709 +
710 + // jichi 4/30/2015: Skip games made from 骹, such as ȪΫͫȫ
711 + // It has garbage from lstrlenW. Correct text is supposed to be in TabbedTextOutA.
712 + if (IthCheckFile(L"data_cg.dpm")) {
713 + ConsoleOutput("vnreng: IGNORE DPM data_cg.dpm");
714 + return true;
715 + }
716 +
717 + //if (IthCheckFile(L"AGERC.DLL")) { // jichi 3/17/2014: Eushully, AGE.EXE
718 + // ConsoleOutput("vnreng: IGNORE Eushully");
719 + // return true;
720 + //}
721 +
722 + if (IthCheckFile(L"game_sys.exe")) {
723 + ConsoleOutput("vnreng: IGNORE Atelier Kaguya BY/TH");
724 + return true;
725 + }
726 +
727 + if (IthFindFile(L"*.bsa")) {
728 + ConsoleOutput("vnreng: IGNORE Bishop");
729 + return true;
730 + }
731 +
732 + // jichi 3/19/2014: Escude game
733 + // Example: bgm.bin gfx.bin maou.bin script.bin snd.bin voc.bin
734 + if (IthCheckFile(L"gfx.bin") && IthCheckFile(L"snd.bin") && IthCheckFile(L"voc.bin")) {
735 + ConsoleOutput("vnreng: IGNORE Escude");
736 + return true;
737 + }
738 +
739 + // jichi 2/18/2015: Ignore if there is Nitro+ copyright
740 + if (Util::SearchResourceString(L"Nitro+")) {
741 + ConsoleOutput("vnreng: IGNORE unknown Nitro+");
742 + return true;
743 + }
744 +
745 + // jichi 12/28/2014: "Chartreux Inc." in Copyright.
746 + // Sublimary brands include Rosebleu, MORE, etc.
747 + // GetGlyphOutlineA already works.
748 + if (Util::SearchResourceString(L"Chartreux")) {
749 + ConsoleOutput("vnreng: IGNORE Chartreux");
750 + return true;
751 + }
752 +
753 + if (IthCheckFile(L"MovieTexture.dll")) {
754 + ConsoleOutput("vnreng: IGNORE MovieTexture");
755 + return true;
756 + }
757 +
758 + if (wcsstr(process_name_, L"lcsebody") || !wcsncmp(process_name_, L"lcsebo~", 7) || IthFindFile(L"lcsebody*")) { // jichi 3/19/2014: LC-ScriptEngine, GetGlyphOutlineA
759 + ConsoleOutput("vnreng: IGNORE lcsebody");
760 + return true;
761 + }
762 +
763 + wchar_t str[MAX_PATH];
764 + DWORD i;
765 + for (i = 0; process_name_[i]; i++) {
766 + str[i] = process_name_[i];
767 + if (process_name_[i] == L'.')
768 + break;
769 + }
770 + *(DWORD *)(str + i + 1) = 0x630068; //.hcb
771 + *(DWORD *)(str + i + 3) = 0x62;
772 + if (IthCheckFile(str)) {
773 + ConsoleOutput("vnreng: IGNORE FVP"); // jichi 10/3/2013: such like ȫꫨ
774 + return true;
775 + }
776 + return false;
777 +}
778 +
779 +// 12/13/2013: Declare it in a way compatible to EXCEPTION_PROCEDURE
780 +EXCEPTION_DISPOSITION ExceptHandler(PEXCEPTION_RECORD ExceptionRecord, LPVOID, PCONTEXT, LPVOID)
781 +{
782 + if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) {
783 + module_limit_ = ExceptionRecord->ExceptionInformation[1];
784 + //OutputDWORD(module_limit_);
785 + __asm
786 + {
787 + mov eax,fs:[0x30] // jichi 12/13/2013: get PEB
788 + mov eax,[eax+0xc]
789 + mov eax,[eax+0xc]
790 + mov ecx,module_limit_
791 + sub ecx,module_base_
792 + mov [eax+0x20],ecx
793 + }
794 + }
795 + //ContextRecord->Esp = recv_esp;
796 + //ContextRecord->Eip = recv_eip;
797 + //return ExceptionContinueExecution; // jichi 3/11/2014: this will still crash. Not sure why ITH use this. Change to ExceptionContinueSearch
798 + return ExceptionContinueSearch; // an unwind is in progress,
799 +}
800 +
801 +// jichi 9/14/2013: Certain ITH functions like FindEntryAligned might raise exception without admin priv
802 +// Return if succeeded.
803 +bool UnsafeDetermineEngineType()
804 +{
805 + return DeterminePCEngine()
806 + || DetermineEngineByFile1()
807 + || DetermineEngineByFile2()
808 + || DetermineEngineByFile3()
809 + || DetermineEngineByFile4()
810 + || DetermineEngineByProcessName()
811 + || DetermineEngineOther()
812 + || DetermineEngineAtLast()
813 + || DetermineEngineGeneric()
814 + || DetermineNoEngine()
815 + ;
816 +}
817 +
818 +// jichi 10/21/2014: Return whether found the game engine
819 +bool DetermineEngineType()
820 +{
821 + // jichi 9/27/2013: disable game engine for debugging use
822 +#ifdef ITH_DISABLE_ENGINE
823 + PcHooks::hookLstrFunctions();
824 + PcHooks::hookCharNextFunctions();
825 + return false;
826 +#else
827 + bool found = false;
828 +#ifdef ITH_HAS_SEH
829 + __try { found = UnsafeDetermineEngineType(); }
830 + __except(ExceptHandler((GetExceptionInformation())->ExceptionRecord, 0, 0, 0)) {}
831 +#else // use my own SEH
832 + seh_with_eh(ExceptHandler,
833 + found = UnsafeDetermineEngineType());
834 +#endif // ITH_HAS_SEH
835 + if (::GDIPlusHooksEnabled())
836 + PcHooks::hookGDIPlusFunctions();
837 + if (!found) { // jichi 10/2/2013: Only enable it if no game engine is detected
838 + PcHooks::hookLstrFunctions();
839 + PcHooks::hookCharNextFunctions();
840 + } else
841 + ConsoleOutput("vnreng: found game engine, IGNORE non gui hooks");
842 + return found;
843 +#endif // ITH_DISABLE_ENGINE
844 +}
845 +
846 +// __asm
847 +// {
848 +// mov eax,seh_recover
849 +// mov recv_eip,eax
850 +// push ExceptHandler
851 +// push fs:[0]
852 +// mov fs:[0],esp
853 +// pushad
854 +// mov recv_esp,esp
855 +// }
856 +// DetermineEngineType();
857 +// status++;
858 +// __asm
859 +// {
860 +//seh_recover:
861 +// popad
862 +// mov eax,[esp]
863 +// mov fs:[0],eax
864 +// add esp,8
865 +// }
866 +// if (status == 0)
867 +// ConsoleOutput("Fail to identify engine type.");
868 +// else
869 +// ConsoleOutput("Initialized successfully.");
870 +//}
871 +//
872 +
873 +HANDLE hijackThread;
874 +void hijackThreadProc(LPVOID lpThreadParameter)
875 +{
876 + CC_UNUSED(lpThreadParameter);
877 +
878 + //static bool done = false;
879 + //if (done)
880 + // return;
881 + //done = true;
882 +
883 + // jichi 12/18/2013: Though FillRange could raise, it should never raise for he current process
884 + // So, SEH is not used here.
885 + Util::GetProcessName(process_name_); // Initialize shared process name
886 + Util::GetProcessPath(process_path_); // Initialize shared process path
887 +
888 + FillRange(process_name_, &module_base_, &module_limit_);
889 + DetermineEngineType();
890 +}
891 +
892 +}} // namespace Engine unnamed
893 +
894 +// - API -
895 +
896 +DWORD Engine::InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
897 +{ return trigger_fun_ ? !trigger_fun_(addr, frame, stack) : 0; }
898 +
899 +void Engine::hijack()
900 +{
901 + if (!hijackThread) {
902 + ConsoleOutput("vnreng: hijack process");
903 + hijackThread = IthCreateThread(hijackThreadProc, 0);
904 + }
905 +}
906 +
907 +void Engine::terminate()
908 +{
909 + if (hijackThread) {
910 + const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
911 + NtWaitForSingleObject(hijackThread, 0, (PLARGE_INTEGER)&timeout);
912 + NtClose(hijackThread);
913 + hijackThread = 0;
914 + }
915 +}
916 +
917 +// EOF
918 +
919 +/*
920 +extern "C" {
921 + // http://gmogre3d.googlecode.com/svn-history/r815/trunk/OgreMain/src/WIN32/OgreMinGWSupport.cpp
922 + // http://forum.osdev.org/viewtopic.php?f=8&t=22352
923 + //#pragma data_seg()
924 + //#pragma comment(linker, "/merge:.CRT=.data") // works fine in visual c++ 6
925 + //#pragma data_seg()
926 + //#pragma comment(linker, "/merge:.CRT=.rdata")
927 + // MSVC libs use _chkstk for stack-probing. MinGW equivalent is _alloca.
928 + //void _alloca();
929 + //void _chkstk() { _alloca(); }
930 +
931 + // MSVC uses security cookies to prevent some buffer overflow attacks.
932 + // provide dummy implementations.
933 + //void _fastcall __security_check_cookie(intptr_t i) {}
934 + void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie) {}
935 +}
936 +*/
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 <windows.h>
11 +
12 +namespace Engine {
13 +
14 +// jichi 10/21/2014: Return whether found the engine
15 +void hijack();
16 +void terminate();
17 +
18 +// jichi 10/21/2014: Return 0 if failed
19 +DWORD InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack);
20 +
21 +} // namespace Engine
22 +
23 +// EOF
1 +#pragma once
2 +
3 +// mono/funcinfo.h
4 +// 12/26/2014
5 +// https://github.com/mono/mono/blob/master/mono/metadata/object.h
6 +// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
7 +
8 +//#include "ith/import/mono/types.h"
9 +
10 +// MonoString* mono_string_new (MonoDomain *domain,
11 +// const char *text);
12 +// MonoString* mono_string_new_len (MonoDomain *domain,
13 +// const char *text,
14 +// guint length);
15 +// MonoString* mono_string_new_size (MonoDomain *domain,
16 +// gint32 len);
17 +// MonoString* mono_string_new_utf16 (MonoDomain *domain,
18 +// const guint16 *text,
19 +// gint32 len);
20 +// MonoString* mono_string_from_utf16 (gunichar2 *data);
21 +// mono_unichar2* mono_string_to_utf16 (MonoString *s);
22 +// char* mono_string_to_utf8 (MonoString *s);
23 +// gboolean mono_string_equal (MonoString *s1,
24 +// MonoString *s2);
25 +// guint mono_string_hash (MonoString *s);
26 +// MonoString* mono_string_intern (MonoString *str);
27 +// MonoString* mono_string_is_interned (MonoString *o);
28 +// MonoString* mono_string_new_wrapper (const char *text);
29 +// gunichar2* mono_string_chars (MonoString *s);
30 +// int mono_string_length (MonoString *s);
31 +// gunichar2* mono_unicode_from_external (const gchar *in, gsize *bytes);
32 +// gchar* mono_unicode_to_external (const gunichar2 *uni);
33 +// gchar* mono_utf8_from_external (const gchar *in);
34 +
35 +struct MonoFunction {
36 + const char *functionName;
37 + size_t textIndex; // argument index, starting from 0
38 + size_t lengthIndex; // argument index, start from 0
39 + unsigned long hookType; // HookParam type
40 + void *text_fun; // HookParam::text_fun_t
41 +};
42 +
43 +#define MONO_FUNCTIONS_INITIALIZER \
44 + { "mono_string_to_utf8", 0, 0, USING_UNICODE|NO_CONTEXT, SpecialHookMonoString } \
45 + , { "mono_string_to_utf8_checked", 0, 0, USING_UNICODE|NO_CONTEXT, SpecialHookMonoString } \
46 + , { "mono_string_to_utf16", 0, 0, USING_UNICODE|NO_CONTEXT, SpecialHookMonoString } \
47 + , { "mono_utf8_from_external", 1, 0, USING_STRING|USING_UTF8, nullptr } \
48 + , { "mono_string_from_utf16", 1, 0, USING_UNICODE, nullptr } \
49 + , { "mono_string_new_utf16", 2, 3, USING_UNICODE, nullptr } \
50 + , { "mono_unicode_from_external", 1, 0, USING_UNICODE, nullptr } \
51 + , { "mono_unicode_to_external", 1, 0, USING_UNICODE, nullptr }
52 + //, { "mono_string_new", 2, 0, USING_STRING|USING_UTF8, nullptr }
53 + //, { "mono_string_new_wrapper", 1, 0, USING_STRING|USING_UTF8, nullptr }
54 +
55 +// EOF
1 +#pragma once
2 +
3 +// mono/types.h
4 +// 12/26/2014
5 +// https://github.com/mono/mono/blob/master/mono/metadata/object.h
6 +// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
7 +
8 +#include <cstdint>
9 +
10 +// mono/io-layer/uglify.h
11 +typedef int8_t gint8;
12 +typedef int32_t gint32;
13 +typedef wchar_t gunichar2; // either char or wchar_t, depending on how mono is compiled
14 +
15 +typedef gint8 mono_byte;
16 +typedef gunichar2 mono_unichar2;
17 +
18 +// mono/metadata/object.h
19 +
20 +typedef mono_byte MonoBoolean;
21 +
22 +struct MonoArray;
23 +struct MonoDelegate;
24 +struct MonoException;
25 +struct MonoString;
26 +struct MonoThreadsSync;
27 +struct MonoThread;
28 +struct MonoVTable;
29 +
30 +struct MonoObject {
31 + MonoVTable *vtable;
32 + MonoThreadsSync *synchronisation;
33 +};
34 +
35 +struct MonoString {
36 + MonoObject object;
37 + gint32 length;
38 + gunichar2 chars[0];
39 +};
40 +
41 +// EOF
1 +// pchooks.cc
2 +// 8/1/2014 jichi
3 +
4 +#include "src/engine/pchooks.h"
5 +#include "src/main.h"
6 +//#include <gdiplus.h>
7 +
8 +#define DEBUG "vnrcli"
9 +#define DPRINT(cstr) ConsoleOutput(DEBUG ":" __FUNCTION__ ":" cstr) // defined in vnrcli
10 +
11 +// 8/1/2014 jichi: Split is not used.
12 +// Although split is specified, USING_SPLIT is not assigned.
13 +
14 +// Use LPASTE to convert to wchar_t
15 +// http://bytes.com/topic/c/answers/135834-defining-wide-character-strings-macros
16 +//#define LPASTE(s) L##s
17 +//#define L(s) LPASTE(s)
18 +#define NEW_HOOK_AT(_addr, _fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
19 + { \
20 + HookParam hp = {}; \
21 + hp.address = _addr; \
22 + hp.offset = _data; \
23 + hp.index = _data_ind; \
24 + hp.split = _split_off; \
25 + hp.split_index = _split_ind; \
26 + hp.type = _type; \
27 + hp.length_offset = _len_off; \
28 + NewHook(hp, #_fun); \
29 + }
30 +
31 +// Static hook
32 +#define NEW_HOOK(_fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
33 + NEW_HOOK_AT((DWORD)_fun, _fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
34 +
35 +#define NEW_MODULE_HOOK(_module, _fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
36 + { \
37 + if (DWORD addr = (DWORD)::GetProcAddress(_module, #_fun)) \
38 + NEW_HOOK_AT(addr, _fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
39 + }
40 +
41 +// jichi 7/17/2014: Renamed from InitDefaultHook
42 +void PcHooks::hookGDIFunctions()
43 +{
44 + DPRINT("enter");
45 + // int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
46 + //
47 + // jichi 9/8/2013: Guessed meaning
48 + // - data(off): 4 * the n-th (base 1) parameter representing the data of the string
49 + // - len_off:
50 + // - the n-th (base 1) parameter representing the length of the string
51 + // - or 1 if is char
52 + // - or 0 if detect on run time
53 + // - type: USING_STRING if len_off != 1 else BIG_ENDIAN or USING_UNICODE
54 + //
55 + // Examples:
56 + // int WINAPI lstrlenA(LPCSTR lpString)
57 + // - data: 4 * 1 = 4, as lpString is the first
58 + // - len_off: 0, as no parameter representing string length
59 + // - type: BIG_ENDIAN, since len_off == 1
60 + // BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
61 + // - data: 4 * 2 = 0x8, as lpString is the second
62 + // - len_off: 3, as nCount is the 3rd parameter
63 + // - type: USING_STRING, since len_off != 1
64 + //
65 + // Note: All functions does not have NO_CONTEXT attribute and will be filtered.
66 +
67 + enum stack {
68 + s_retaddr = 0
69 + , s_arg1 = 4 * 1 // 0x4
70 + , s_arg2 = 4 * 2 // 0x8
71 + , s_arg3 = 4 * 3 // 0xc
72 + , s_arg4 = 4 * 4 // 0x10
73 + , s_arg5 = 4 * 5 // 0x14
74 + , s_arg6 = 4 * 6 // 0x18
75 + };
76 +
77 +//#define _(Name, ...) \
78 +// hookman[HF_##Name].InitHook(Name, __VA_ARGS__); \
79 +// hookman[HF_##Name].SetHookName(names[HF_##Name]);
80 +
81 + // Always use s_arg1 = hDC as split_off
82 + // 7/26/2014 jichi: Why there is no USING_SPLIT type?
83 +
84 + // gdi32.dll
85 + NEW_HOOK(GetTextExtentPoint32A, s_arg2, 0,s_arg1,0, USING_STRING, 3) // BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
86 + NEW_HOOK(GetTextExtentExPointA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // BOOL GetTextExtentExPoint(HDC hdc, LPCTSTR lpszStr, int cchString, int nMaxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize);
87 + NEW_HOOK(GetTabbedTextExtentA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // DWORD GetTabbedTextExtent(HDC hDC, LPCTSTR lpString, int nCount, int nTabPositions, const LPINT lpnTabStopPositions);
88 + NEW_HOOK(GetCharacterPlacementA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // DWORD GetCharacterPlacement(HDC hdc, LPCTSTR lpString, int nCount, int nMaxExtent, LPGCP_RESULTS lpResults, DWORD dwFlags);
89 + NEW_HOOK(GetGlyphIndicesA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // DWORD GetGlyphIndices( HDC hdc, LPCTSTR lpstr, int c, LPWORD pgi, DWORD fl);
90 + 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);
91 + 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);
92 + NEW_HOOK(TextOutA, s_arg4, 0,s_arg1,0, USING_STRING, 5) // BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cchString);
93 + NEW_HOOK(TabbedTextOutA, s_arg4, 0,s_arg1,0, USING_STRING, 5) // LONG TabbedTextOut(HDC hDC, int X, int Y, LPCTSTR lpString, int nCount, int nTabPositions, const LPINT lpnTabStopPositions, int nTabOrigin);
94 + NEW_HOOK(GetCharABCWidthsA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharABCWidths(HDC hdc, UINT uFirstChar, UINT uLastChar, LPABC lpabc);
95 + NEW_HOOK(GetCharABCWidthsFloatA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharABCWidthsFloat(HDC hdc, UINT iFirstChar, UINT iLastChar, LPABCFLOAT lpABCF);
96 + NEW_HOOK(GetCharWidth32A, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharWidth32(HDC hdc, UINT iFirstChar, UINT iLastChar, LPINT lpBuffer);
97 + NEW_HOOK(GetCharWidthFloatA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharWidthFloat(HDC hdc, UINT iFirstChar, UINT iLastChar, PFLOAT pxBuffer);
98 +
99 + NEW_HOOK(GetTextExtentPoint32W, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
100 + NEW_HOOK(GetTextExtentExPointW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
101 + NEW_HOOK(GetTabbedTextExtentW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
102 + NEW_HOOK(GetCharacterPlacementW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
103 + NEW_HOOK(GetGlyphIndicesW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
104 + NEW_HOOK(GetGlyphOutlineW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
105 + NEW_HOOK(ExtTextOutW, s_arg6, 0,s_arg1,0, USING_UNICODE|USING_STRING, 7)
106 + NEW_HOOK(TextOutW, s_arg4, 0,s_arg1,0, USING_UNICODE|USING_STRING, 5)
107 + NEW_HOOK(TabbedTextOutW, s_arg4, 0,s_arg1,0, USING_UNICODE|USING_STRING, 5)
108 + NEW_HOOK(GetCharABCWidthsW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
109 + NEW_HOOK(GetCharABCWidthsFloatW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
110 + NEW_HOOK(GetCharWidth32A, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
111 + NEW_HOOK(GetCharWidthFloatA, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
112 +
113 + // user32.dll
114 + NEW_HOOK(DrawTextA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawText(HDC hDC, LPCTSTR lpchText, int nCount, LPRECT lpRect, UINT uFormat);
115 + 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);
116 + NEW_HOOK(DrawTextW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
117 + NEW_HOOK(DrawTextExW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
118 +
119 + DPRINT("leave");
120 +}
121 +
122 +// jichi 6/18/2015: GDI+ functions
123 +void PcHooks::hookGDIPlusFunctions()
124 +{
125 + HMODULE hModule = ::GetModuleHandleA("gdiplus.dll");
126 + if (!hModule) {
127 + DPRINT("not loaded");
128 + return;
129 + }
130 +
131 + DPRINT("enter");
132 + enum stack {
133 + s_retaddr = 0
134 + , s_arg1 = 4 * 1 // 0x4
135 + , s_arg2 = 4 * 2 // 0x8
136 + , s_arg3 = 4 * 3 // 0xc
137 + , s_arg4 = 4 * 4 // 0x10
138 + , s_arg5 = 4 * 5 // 0x14
139 + , s_arg6 = 4 * 6 // 0x18
140 + };
141 +
142 + // gdiplus.dll
143 + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms534053%28v=vs.85%29.aspx
144 + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms534052%28v=vs.85%29.aspx
145 + // https://msdn.microsoft.com/en-us/library/windows/desktop/ms534039%28v=vs.85%29.aspx
146 + // Use arg1 pionter to GpGraphics as split
147 + //using namespace Gdiplus::DllExports;
148 + // Use arg5 style as split
149 + NEW_MODULE_HOOK(hModule, GdipAddPathString, s_arg2, 0,s_arg5,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipAddPathString(GpPath *path, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFontFamily *family, INT style, REAL emSize, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *format)
150 + NEW_MODULE_HOOK(hModule, GdipAddPathStringl, s_arg2, 0,s_arg5,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipAddPathStringI(GpPath *path, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFontFamily *family, INT style, REAL emSize, GDIPCONST Rect *layoutRect, GDIPCONST GpStringFormat *format)
151 + //NEW_MODULE_HOOK(hModule, GdipMeasureCharacterRanges, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF &layoutRect, GDIPCONST GpStringFormat *stringFormat, INT regionCount, GpRegion **regions)
152 + NEW_MODULE_HOOK(hModule, GdipDrawString, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *stringFormat, GDIPCONST GpBrush *brush);
153 + NEW_MODULE_HOOK(hModule, GdipMeasureString, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *stringFormat, RectF *boundingBox, INT *codepointsFitted, INT *linesFilled )
154 +
155 + DPRINT("leave");
156 +}
157 +
158 +// jichi 10/2/2013
159 +// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
160 +void PcHooks::hookLstrFunctions()
161 +{
162 + DPRINT("enter");
163 + // int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
164 +
165 + enum stack {
166 + s_retaddr = 0
167 + , s_arg1 = 4 * 1 // 0x4
168 + //, s_arg2 = 4 * 2 // 0x8
169 + //, s_arg3 = 4 * 3 // 0xc
170 + //, s_arg4 = 4 * 4 // 0x10
171 + //, s_arg5 = 4 * 5 // 0x14
172 + //, s_arg6 = 4 * 6 // 0x18
173 + };
174 +
175 + // http://msdn.microsoft.com/en-us/library/78zh94ax.aspx
176 + // int WINAPI lstrlen(LPCTSTR lpString);
177 + // Lstr functions usually extracts rubbish, and might crash certain games like 「Magical Marriage Lunatics!!」
178 + // Needed by Gift
179 + // Use arg1 address for both split and data
180 + NEW_HOOK(lstrlenA, s_arg1, 0,s_arg1,0, USING_STRING, 0) // 9/8/2013 jichi: int WINAPI lstrlen(LPCTSTR lpString);
181 + NEW_HOOK(lstrlenW, s_arg1, 0,s_arg1,0, USING_UNICODE|USING_STRING, 0) // 9/8/2013 jichi: add lstrlen
182 +
183 + // size_t strlen(const char *str);
184 + // size_t strlen_l(const char *str, _locale_t locale);
185 + // size_t wcslen(const wchar_t *str);
186 + // size_t wcslen_l(const wchar_t *str, _locale_t locale);
187 + // size_t _mbslen(const unsigned char *str);
188 + // size_t _mbslen_l(const unsigned char *str, _locale_t locale);
189 + // size_t _mbstrlen(const char *str);
190 + // size_t _mbstrlen_l(const char *str, _locale_t locale);
191 +
192 + // http://msdn.microsoft.com/en-us/library/ex0hs2ad.aspx
193 + // Needed by 娘姉妹
194 + //
195 + // <tchar.h>
196 + // char *_strinc(const char *current, _locale_t locale);
197 + // wchar_t *_wcsinc(const wchar_t *current, _locale_t locale);
198 + // <mbstring.h>
199 + // unsigned char *_mbsinc(const unsigned char *current);
200 + // unsigned char *_mbsinc_l(const unsigned char *current, _locale_t locale);
201 + //_(L"_strinc", _strinc, 4, 0,4,0, USING_STRING, 0) // 12/13/2013 jichi
202 + //_(L"_wcsinc", _wcsinc, 4, 0,4,0, USING_UNICODE|USING_STRING, 0)
203 + DPRINT("leave");
204 +}
205 +
206 +void PcHooks::hookWcharFunctions()
207 +{
208 + DPRINT("enter");
209 + // 12/1/2013 jichi:
210 + // AlterEgo
211 + // http://tieba.baidu.com/p/2736475133
212 + // http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page355
213 + //
214 + // MultiByteToWideChar
215 + // http://blgames.proboards.com/thread/265
216 + //
217 + // WideCharToMultiByte
218 + // http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page156
219 + //
220 + // int MultiByteToWideChar(
221 + // _In_ UINT CodePage,
222 + // _In_ DWORD dwFlags,
223 + // _In_ LPCSTR lpMultiByteStr, // hook here
224 + // _In_ int cbMultiByte,
225 + // _Out_opt_ LPWSTR lpWideCharStr,
226 + // _In_ int cchWideChar
227 + // );
228 + // int WideCharToMultiByte(
229 + // _In_ UINT CodePage,
230 + // _In_ DWORD dwFlags,
231 + // _In_ LPCWSTR lpWideCharStr,
232 + // _In_ int cchWideChar,
233 + // _Out_opt_ LPSTR lpMultiByteStr,
234 + // _In_ int cbMultiByte,
235 + // _In_opt_ LPCSTR lpDefaultChar,
236 + // _Out_opt_ LPBOOL lpUsedDefaultChar
237 + // );
238 +
239 + enum stack {
240 + s_retaddr = 0
241 + , s_arg1 = 4 * 1 // 0x4
242 + //, s_arg2 = 4 * 2 // 0x8
243 + , s_arg3 = 4 * 3 // 0xc
244 + //, s_arg4 = 4 * 4 // 0x10
245 + //, s_arg5 = 4 * 5 // 0x14
246 + //, s_arg6 = 4 * 6 // 0x18
247 + };
248 +
249 + // 3/17/2014 jichi: Temporarily disabled
250 + // http://sakuradite.com/topic/159
251 + NEW_HOOK(MultiByteToWideChar, s_arg3, 0,4,0, USING_STRING, 4)
252 + NEW_HOOK(WideCharToMultiByte, s_arg3, 0,4,0, USING_UNICODE|USING_STRING, 4)
253 + DPRINT("leave");
254 +}
255 +
256 +void PcHooks::hookCharNextFunctions()
257 +{
258 + enum stack {
259 + s_retaddr = 0
260 + , s_arg1 = 4 * 1 // 0x4
261 + , s_arg2 = 4 * 2 // 0x8
262 + //, s_arg3 = 4 * 3 // 0xc
263 + //, s_arg4 = 4 * 4 // 0x10
264 + //, s_arg5 = 4 * 5 // 0x14
265 + //, s_arg6 = 4 * 6 // 0x18
266 + };
267 +
268 + DPRINT("enter");
269 + NEW_HOOK(CharNextA, s_arg1, 0,0,0, USING_STRING|DATA_INDIRECT, 1) // LPTSTR WINAPI CharNext(_In_ LPCTSTR lpsz);
270 + NEW_HOOK(CharNextW, s_arg1, 0,0,0, USING_UNICODE|DATA_INDIRECT, 1)
271 + NEW_HOOK(CharPrevA, s_arg1, 0,0,0, USING_STRING|DATA_INDIRECT, 1) // LPTSTR WINAPI CharPrev(_In_ LPCTSTR lpszStart, _In_ LPCTSTR lpszCurrent);
272 + NEW_HOOK(CharPrevW, s_arg1, 0,0,0, USING_UNICODE|DATA_INDIRECT, 1)
273 + //NEW_HOOK(CharNextExA, s_arg2, 0,0,0, USING_STRING|DATA_INDIRECT, 1) // LPSTR WINAPI CharNextExA(_In_ WORD CodePage, _In_ LPCSTR lpCurrentChar, _In_ DWORD dwFlags);
274 + //NEW_HOOK(CharNextExW, s_arg2, 0,0,0, USING_UNICODE|DATA_INDIRECT, 1)
275 + DPRINT("leave");
276 +}
277 +
278 +// EOF
1 +#pragma once
2 +
3 +// pchooks.h
4 +// 8/1/2014 jichi
5 +
6 +namespace PcHooks {
7 +
8 +void hookGDIFunctions();
9 +void hookGDIPlusFunctions();
10 +void hookLstrFunctions();
11 +void hookWcharFunctions();
12 +void hookCharNextFunctions();
13 +
14 +} // namespace PcHooks
15 +
16 +// EOF
1 +#pragma once
2 +
3 +// ppsspp/funcinfo.h
4 +// 12/26/2014
5 +// See: https://github.com/hrydgard/ppsspp
6 +
7 +// Core/HLE (High Level Emulator)
8 +// - sceCcc
9 +// #void sceCccSetTable(u32 jis2ucs, u32 ucs2jis)
10 +// int sceCccUTF8toUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr)
11 +// int sceCccUTF8toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
12 +// int sceCccUTF16toUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr)
13 +// int sceCccUTF16toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
14 +// int sceCccSJIStoUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr)
15 +// int sceCccSJIStoUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr)
16 +// int sceCccStrlenUTF8(u32 strAddr)
17 +// int sceCccStrlenUTF16(u32 strAddr)
18 +// int sceCccStrlenSJIS(u32 strAddr)
19 +// u32 sceCccEncodeUTF8(u32 dstAddrAddr, u32 ucs)
20 +// void sceCccEncodeUTF16(u32 dstAddrAddr, u32 ucs)
21 +// u32 sceCccEncodeSJIS(u32 dstAddrAddr, u32 jis)
22 +// u32 sceCccDecodeUTF8(u32 dstAddrAddr)
23 +// u32 sceCccDecodeUTF16(u32 dstAddrAddr)
24 +// u32 sceCccDecodeSJIS(u32 dstAddrAddr)
25 +// int sceCccIsValidUTF8(u32 c)
26 +// int sceCccIsValidUTF16(u32 c)
27 +// int sceCccIsValidSJIS(u32 c)
28 +// int sceCccIsValidUCS2(u32 c)
29 +// int sceCccIsValidUCS4(u32 c)
30 +// int sceCccIsValidJIS(u32 c)
31 +// int sceCccIsValidUnicode(u32 c)
32 +// #u32 sceCccSetErrorCharUTF8(u32 c)
33 +// #u32 sceCccSetErrorCharUTF16(u32 c)
34 +// #u32 sceCccSetErrorCharSJIS(u32 c)
35 +// u32 sceCccUCStoJIS(u32 c, u32 alt)
36 +// u32 sceCccJIStoUCS(u32 c, u32 alt)
37 +// - sceFont: search charCode
38 +// int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr)
39 +// int sceFontGetShadowInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr)
40 +// int sceFontGetCharImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr)
41 +// int sceFontGetShadowImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr)
42 +// int sceFontGetCharGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr)
43 +// int sceFontGetCharGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight)
44 +// #int sceFontSetAltCharacterCode(u32 fontLibHandle, u32 charCode)
45 +// int sceFontGetShadowGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr)
46 +// int sceFontGetShadowGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight)
47 +// - sceKernelInterrupt
48 +// u32 sysclib_strcat(u32 dst, u32 src)
49 +// int sysclib_strcmp(u32 dst, u32 src)
50 +// u32 sysclib_strcpy(u32 dst, u32 src)
51 +// u32 sysclib_strlen(u32 src)
52 +//
53 +// Sample debug string:
54 +// 006EFD8E PUSH PPSSPPWi.00832188 ASCII "sceCccEncodeSJIS(%08x, U+%04x)"
55 +// Corresponding source code in sceCcc:
56 +// ERROR_LOG(HLE, "sceCccEncodeSJIS(%08x, U+%04x): invalid pointer", dstAddrAddr, jis);
57 +
58 +struct PPSSPPFunction
59 +{
60 + const char *hookName; // hook name
61 + size_t argIndex; // argument index
62 + unsigned long hookType; // hook parameter type
63 + unsigned long hookSplit; // hook parameter split, positive: stack, negative: registers
64 + const char *pattern; // debug string used within the function
65 +};
66 +
67 +// jichi 7/14/2014: UTF-8 is treated as STRING
68 +// http://867258173.diandian.com/post/2014-06-26/40062099618
69 +// sceFontGetCharGlyphImage_Clip
70 +// Sample game: [KID] Monochrome: sceFontGetCharInfo, sceFontGetCharGlyphImage_Clip
71 +//
72 +// Example: { L"sceFontGetCharInfo", 2, USING_UNICODE, 4, "sceFontGetCharInfo(" }
73 +// Text is at arg2, using arg1 as split
74 +#define PPSSPP_FUNCTIONS_INITIALIZER \
75 + { "sceCccStrlenSJIS", 1, USING_STRING, 0, "sceCccStrlenSJIS(" } \
76 + , { "sceCccStrlenUTF8", 1, USING_UTF8, 0, "sceCccStrlenUTF8(" } \
77 + , { "sceCccStrlenUTF16", 1, USING_UNICODE, 0, "sceCccStrlenUTF16(" } \
78 +\
79 + , { "sceCccSJIStoUTF8", 3, USING_UTF8, 0, "sceCccSJIStoUTF8(" } \
80 + , { "sceCccSJIStoUTF16", 3, USING_STRING, 0, "sceCccSJIStoUTF16(" } \
81 + , { "sceCccUTF8toSJIS", 3, USING_UTF8, 0, "sceCccUTF8toSJIS(" } \
82 + , { "sceCccUTF8toUTF16", 3, USING_UTF8, 0, "sceCccUTF8toUTF16(" } \
83 + , { "sceCccUTF16toSJIS", 3, USING_UNICODE, 0, "sceCccUTF16toSJIS(" } \
84 + , { "sceCccUTF16toUTF8", 3, USING_UNICODE, 0, "sceCccUTF16toUTF8(" } \
85 +\
86 + , { "sceFontGetCharInfo", 2, USING_UNICODE, 4, "sceFontGetCharInfo(" } \
87 + , { "sceFontGetShadowInfo", 2, USING_UNICODE, 4, "sceFontGetShadowInfo("} \
88 + , { "sceFontGetCharImageRect", 2, USING_UNICODE, 4, "sceFontGetCharImageRect(" } \
89 + , { "sceFontGetShadowImageRect", 2, USING_UNICODE, 4, "sceFontGetShadowImageRect(" } \
90 + , { "sceFontGetCharGlyphImage", 2, USING_UNICODE, 4, "sceFontGetCharGlyphImage(" } \
91 + , { "sceFontGetCharGlyphImage_Clip", 2, USING_UNICODE, 4, "sceFontGetCharGlyphImage_Clip(" } \
92 + , { "sceFontGetShadowGlyphImage", 2, USING_UNICODE, 4, "sceFontGetShadowGlyphImage(" } \
93 + , { "sceFontGetShadowGlyphImage_Clip", 2, USING_UNICODE, 4, "sceFontGetShadowGlyphImage_Clip(" } \
94 +\
95 + , { "sysclib_strcat", 2, USING_STRING, 0, "Untested sysclib_strcat(" } \
96 + , { "sysclib_strcpy", 2, USING_STRING, 0, "Untested sysclib_strcpy(" } \
97 + , { "sysclib_strlen", 1, USING_STRING, 0, "Untested sysclib_strlen(" }
98 +
99 + // Disabled as I am not sure how to deal with the source string
100 + //, { "sceCccEncodeSJIS", 2, USING_STRING, 0, "sceCccEncodeSJIS(" }
101 + //, { "sceCccEncodeUTF8", 2, USING_UTF8, 0, "sceCccEncodeUTF8(" }
102 + //, { "sceCccEncodeUTF16", 2, USING_UNICODE, 0, "sceCccEncodeUTF16(" }
103 + //, { "sysclib_strcmp", 2, USING_STRING, 0, "Untested sysclib_strcmp(" }
104 +
105 +// EOF
1 +#pragma once
2 +
3 +// 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 +// 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 "src/hijack/texthook.h"
13 +#include "src/engine/match.h"
14 +#include "src/except.h"
15 +#include "src/main.h"
16 +#include "include/const.h"
17 +#include "ithsys/ithsys.h"
18 +#include "winkey/winkey.h"
19 +#include "disasm/disasm.h"
20 +//#include "winseh/winseh.h"
21 +
22 +//#define ConsoleOutput(...) (void)0 // jichi 9/17/2013: I don't need this ><
23 +
24 +// - Global variables -
25 +
26 +// 10/14/2014 jichi: disable GDI hooks
27 +static bool gdi_hook_enabled_ = true; // enable GDI by default
28 +static bool gdiplus_hook_enabled_ = false; // disable GDIPlus by default
29 +bool GDIHooksEnabled() { return ::gdi_hook_enabled_; }
30 +bool GDIPlusHooksEnabled() { return ::gdiplus_hook_enabled_; }
31 +void EnableGDIHooks() { ::gdi_hook_enabled_ = true; }
32 +void EnableGDIPlusHooks() { ::gdiplus_hook_enabled_ = true; }
33 +void DisableGDIHooks() { ::gdi_hook_enabled_ = false; }
34 +void DisableGDIPlusHooks() { ::gdiplus_hook_enabled_ = false; }
35 +
36 +static bool IsGDIFunction(LPCVOID addr)
37 +{
38 + static LPVOID funcs[] = { HOOK_GDI_FUNCTION_LIST };
39 + for (size_t i = 0; i < sizeof(funcs)/sizeof(*funcs); i++)
40 + if (addr == funcs[i])
41 + return true;
42 + return false;
43 +}
44 +
45 +//FilterRange filter[8];
46 +
47 +DWORD flag,
48 + enter_count;
49 +
50 +TextHook *hookman,
51 + *current_available;
52 +
53 +// - Unnamed helpers -
54 +
55 +namespace { // unnamed
56 +//provide const time hook entry.
57 +int userhook_count;
58 +
59 +#if 0 // 3/6/2015 jichi: this hook is not used and hence disabled
60 +const byte common_hook2[] = {
61 + 0x89, 0x3c,0xe4, // mov [esp],edi
62 + 0x60, // pushad
63 + 0x9c, // pushfd
64 + 0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
65 + 0x8b,0x32, // mov esi,[edx] ; return address
66 + 0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook
67 + 0xe8, 0,0,0,0, // call @hook
68 + 0x9d, // popfd
69 + 0x61, // popad
70 + 0x5f, // pop edi ; skip return address on stack
71 +}; //...
72 +#endif // 0
73 +
74 +const BYTE common_hook[] = {
75 + 0x9c, // pushfd
76 + 0x60, // pushad
77 + 0x9c, // pushfd
78 + 0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
79 + 0x8b,0x32, // mov esi,[edx] ; return address
80 + 0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook
81 + 0xe8, 0,0,0,0, // call @hook
82 + 0x9d, // popfd
83 + 0x61, // popad
84 + 0x9d // popfd
85 +};
86 +
87 +/**
88 + * jichi 7/19/2014
89 + *
90 + * @param original_addr
91 + * @param new_addr
92 + * @param hook_len
93 + * @param original_len
94 + * @return -1 if failed, else 0 if ?, else ?
95 + */
96 +int MapInstruction(DWORD original_addr, DWORD new_addr, BYTE &hook_len, BYTE &original_len)
97 +{
98 + int flag = 0;
99 + DWORD l = 0;
100 + const BYTE *r = (const BYTE *)original_addr; // 7/19/2014 jichi: original address is not modified
101 + BYTE *c = (BYTE *)new_addr; // 7/19/2014 jichi: but new address might be modified
102 + while((r - (BYTE *) original_addr) < 5) {
103 + l = ::disasm(r);
104 + if (l == 0) {
105 + ConsoleOutput("vnrcli:MapInstruction: FAILED: failed to disasm");
106 + return -1;
107 + }
108 +
109 + ::memcpy(c, r, l);
110 + if (*r >= 0x70 && *r < 0x80) {
111 + c[0] = 0xf;
112 + c[1] = *r + 0x10;
113 + c += 6;
114 + __asm
115 + {
116 + mov eax,r
117 + add eax,2
118 + movsx edx,byte ptr [eax-1]
119 + add edx,eax
120 + mov eax,c
121 + sub edx,eax
122 + mov [eax-4],edx
123 + }
124 + } else if (*r == 0xeb) {
125 + c[0] = 0xe9;
126 + c += 5;
127 + __asm
128 + {
129 + mov eax,r
130 + add eax,2
131 + movsx edx,[eax-1]
132 + add edx,eax
133 + mov eax,c
134 + sub edx,eax
135 + mov [eax-4],edx
136 + }
137 + if (r - (BYTE *)original_addr < 5 - l) {
138 + ConsoleOutput("vnrcli:MapInstruction: not safe to move instruction right after short jmp");
139 + return -1; // Not safe to move instruction right after short jmp.
140 + } else
141 + flag = 1;
142 + } else if (*r == 0xe8 || *r == 0xe9) {
143 + c[0]=*r;
144 + c += 5;
145 + flag = (*r == 0xe9);
146 + __asm
147 + {
148 + mov eax,r
149 + add eax,5
150 + mov edx,[eax-4]
151 + add edx,eax
152 + mov eax,c
153 + sub edx,eax
154 + mov [eax-4],edx
155 + }
156 + } else if (*r == 0xf && (*(r + 1) >> 4) == 0x8) {
157 + c += 6;
158 + __asm
159 + {
160 + mov eax,r
161 + mov edx,dword ptr [eax+2]
162 + add eax,6
163 + add eax,edx
164 + mov edx,c
165 + sub eax,edx
166 + mov [edx-4],eax
167 + }
168 + }
169 + else
170 + c += l;
171 + r += l;
172 + }
173 + original_len = r - (BYTE *)original_addr;
174 + hook_len = c - (BYTE *)new_addr;
175 + return flag;
176 +}
177 +
178 +//copy original instruction
179 +//jmp back
180 +DWORD GetModuleBase(DWORD hash)
181 +{
182 + __asm
183 + {
184 + mov eax,fs:[0x30]
185 + mov eax,[eax+0xc]
186 + mov esi,[eax+0x14]
187 + mov edi,_wcslwr
188 +listfind:
189 + mov edx,[esi+0x28]
190 + test edx,edx
191 + jz notfound
192 + push edx
193 + call edi
194 + pop edx
195 + xor eax,eax
196 +calc:
197 + movzx ecx, word ptr [edx]
198 + test cl,cl
199 + jz fin
200 + ror eax,7
201 + add eax,ecx
202 + add edx,2
203 + jmp calc
204 +fin:
205 + cmp eax,[hash]
206 + je found
207 + mov esi,[esi]
208 + jmp listfind
209 +notfound:
210 + xor eax,eax
211 + jmp termin
212 +found:
213 + mov eax,[esi+0x10]
214 +termin:
215 + }
216 +}
217 +
218 +DWORD GetModuleBase()
219 +{
220 + __asm
221 + {
222 + mov eax, fs:[0x18]
223 + mov eax, [eax + 0x30]
224 + mov eax, [eax + 0xc]
225 + mov eax, [eax + 0xc]
226 + mov eax, [eax + 0x18]
227 + }
228 +}
229 +
230 +//void NotifyHookInsert()
231 +//{
232 +// if (live)
233 +// {
234 +// BYTE buffer[0x10];
235 +// *(DWORD*)buffer=-1;
236 +// *(DWORD*)(buffer+4)=1;
237 +// IO_STATUS_BLOCK ios;
238 +// NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0);
239 +// }
240 +//}
241 +
242 +__declspec(naked) void SafeExit() // Return to eax
243 +{
244 + __asm
245 + {
246 + mov [esp+0x24], eax
247 + popfd
248 + popad
249 + retn
250 + }
251 +}
252 +
253 +#if 0
254 +// jichi 12/2/2013: This function mostly return 0.
255 +// But sometimes return the hook address from TextHook::Send
256 +__declspec(naked) // jichi 10/2/2013: No prolog and epilog
257 +int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
258 +{
259 + //with_seh(hook->Send(dwDataBase, dwRetn));
260 + seh_push_(seh_exit, 0, eax, ebx) // jichi 12/13/2013: only eax and ebx are available. ecx and edx are used.
261 + __asm
262 + {
263 + push esi
264 + push edx
265 + call TextHook::UnsafeSend
266 + test eax, eax
267 + jz seh_exit // label in seh_pop
268 + mov ecx, SafeExit
269 + mov [esp + 8], ecx // jichi 12/13/2013: change exit point if Send returns non-zero, not + 8 beause two elements has been pused
270 + }
271 + seh_pop_(seh_exit)
272 + __asm retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
273 +}
274 +#endif // 0
275 +
276 +#if 1
277 +__declspec(naked) // jichi 10/2/2013: No prolog and epilog
278 +int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
279 +{
280 + // jichi 12/17/2013: The function parameters here are meaning leass. The parameters are in esi and edi
281 + __asm
282 + {
283 + push esi
284 + push edx
285 + call TextHook::Send
286 + test eax, eax
287 + jz ok // label in seh_pop
288 + mov ecx, SafeExit
289 + mov [esp], ecx // jichi 12/13/2013: change exit point if Send returns non-zero
290 + ok:
291 + retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
292 + }
293 +}
294 +#endif // 1
295 +
296 + // jichi 12/13/2013: return if the retn address is within the filter dlls
297 +inline bool HookFilter(DWORD retn)
298 +{
299 + for (DWORD i = 0; ::filter[i].lower; i++)
300 + if (retn > ::filter[i].lower && retn < ::filter[i].upper)
301 + return true;
302 + return false;
303 +}
304 +
305 +// Return false if all text are ascii
306 +bool NoAsciiFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
307 +{
308 + auto text = reinterpret_cast<LPBYTE>(data);
309 + if (text)
310 + for (size_t i = 0; i < *size; i++)
311 + if (text[i] > 127)
312 + return true;
313 + return false;
314 +}
315 +
316 +} // unnamed namespace
317 +
318 +// - TextHook methods -
319 +
320 +// jichi 12/2/2013: This function mostly return 0.
321 +// It return the hook address only for auxiliary case.
322 +// However, because no known hooks are auxiliary, this function always return 0.
323 +//
324 +// jichi 5/11/2014:
325 +// - dwDataBase: the stack address
326 +// - dwRetn: the return address of the hook
327 +DWORD TextHook::Send(DWORD dwDataBase, DWORD dwRetn)
328 +{
329 + // jich: 6/17/2015: do not send when ctrl/shift are controlled
330 + //if (WinKey::isKeyControlPressed() || WinKey::isKeyShiftPressed() && !WinKey::isKeyReturnPressed())
331 + // return 0;
332 +
333 + DWORD ret = 0;
334 + //char b[0x100];
335 + //::wcstombs(b, hook_name, 0x100);
336 + //ConsoleOutput(b);
337 + ITH_WITH_SEH(ret = UnsafeSend(dwDataBase, dwRetn));
338 + return ret;
339 +}
340 +
341 +DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn)
342 +{
343 + enum { SMALL_BUFF_SIZE = 0x80 };
344 + enum { MAX_DATA_SIZE = 0x10000 }; // jichi 12/25/2013: The same as the original ITH
345 + DWORD dwCount,
346 + dwAddr,
347 + dwDataIn,
348 + dwSplit;
349 + BYTE *pbData,
350 + pbSmallBuff[SMALL_BUFF_SIZE];
351 + DWORD dwType = hp.type;
352 + if (!::live) // the pipe thread is busy
353 + return 0;
354 + if ((dwType & NO_CONTEXT) == 0 && HookFilter(dwRetn))
355 + return 0;
356 +
357 + if ((dwType & NO_ASCII) && !hp.filter_fun)
358 + hp.filter_fun = NoAsciiFilter;
359 +
360 + // jichi 10/24/2014: Skip GDI functions
361 + if (!::gdi_hook_enabled_ && ::IsGDIFunction((LPCVOID)hp.address))
362 + return 0;
363 +
364 + dwAddr = hp.address;
365 +
366 + /** jichi 12/24/2014
367 + * @param addr function address
368 + * @param frame real address of the function, supposed to be the same as addr
369 + * @param stack address of current stack - 4
370 + * @return If success, which is reverted
371 + */
372 + if (::trigger)
373 + ::trigger = Engine::InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
374 + // jichi 10/21/2014: Directly invoke engine functions.
375 + //if (trigger) {
376 + // if (InsertDynamicHook)
377 + // trigger = InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
378 + // else
379 + // trigger = 0;
380 + //}
381 +#if 0 // diasble HOOK_AUXILIARY
382 + // jichi 12/13/2013: None of known hooks are auxiliary
383 + if (dwType & HOOK_AUXILIARY) {
384 + //Clean hook when dynamic hook finished.
385 + //AUX hook is only used for a foothold of dynamic hook.
386 + if (!trigger) {
387 + ClearHook();
388 + // jichi 12/13/2013: This is the only place where this function could return non-zero value
389 + // However, I non of the known hooks are auxiliary
390 + return dwAddr;
391 + }
392 + return 0;
393 + }
394 +#endif // 0
395 + // jichi 10/24/2014: generic hook function
396 + if (hp.hook_fun && !hp.hook_fun(dwDataBase, &hp))
397 + hp.hook_fun = nullptr;
398 +
399 + if (dwType & HOOK_EMPTY) // jichi 10/24/2014: dummy hook only for dynamic hook
400 + return 0;
401 +
402 + // jichi 2/2/2015: Send multiple texts
403 + for (BYTE textIndex = 0; textIndex <= hp.extra_text_count; textIndex++) {
404 + dwCount = 0;
405 + dwSplit = 0;
406 + dwDataIn = *(DWORD *)(dwDataBase + hp.offset); // default value
407 +
408 + //if (dwType & EXTERN_HOOK) {
409 + if (hp.text_fun) { // jichi 10/24/2014: remove EXTERN_HOOK
410 + //DataFun fun=(DataFun)hp.text_fun;
411 + //auto fun = hp.text_fun;
412 + hp.text_fun(dwDataBase, &hp, textIndex, &dwDataIn, &dwSplit, &dwCount);
413 + //if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
414 + // return 0;
415 + if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
416 + dwSplit -= ::processStartAddress;
417 + } else {
418 + if (dwDataIn == 0)
419 + return 0;
420 + if (dwType & FIXING_SPLIT)
421 + dwSplit = FIXED_SPLIT_VALUE; // fuse all threads, and prevent floating
422 + else if (dwType & USING_SPLIT) {
423 + dwSplit = *(DWORD *)(dwDataBase + hp.split);
424 + if (dwType & SPLIT_INDIRECT) {
425 + if (IthGetMemoryRange((LPVOID)(dwSplit + hp.split_index), 0, 0))
426 + dwSplit = *(DWORD *)(dwSplit + hp.split_index);
427 + else
428 + return 0;
429 + }
430 + if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
431 + dwSplit -= ::processStartAddress;
432 + }
433 + if (dwType & DATA_INDIRECT) {
434 + if (IthGetMemoryRange((LPVOID)(dwDataIn + hp.index), 0, 0))
435 + dwDataIn = *(DWORD *)(dwDataIn + hp.index);
436 + else
437 + return 0;
438 + }
439 + //if (dwType & PRINT_DWORD) {
440 + // swprintf((WCHAR *)(pbSmallBuff + HEADER_SIZE), L"%.8X ", dwDataIn);
441 + // dwDataIn = (DWORD)pbSmallBuff + HEADER_SIZE;
442 + //}
443 + dwCount = GetLength(dwDataBase, dwDataIn);
444 + }
445 +
446 + // jichi 12/25/2013: validate data size
447 + if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
448 + return 0;
449 +
450 + size_t sz = dwCount + HEADER_SIZE;
451 + if (sz >= SMALL_BUFF_SIZE)
452 + pbData = new BYTE[sz];
453 + //ITH_MEMSET_HEAP(pbData, 0, sz * sizeof(BYTE)); // jichi 9/26/2013: zero memory
454 + else
455 + pbData = pbSmallBuff;
456 +
457 + if (hp.length_offset == 1) {
458 + if (dwType & STRING_LAST_CHAR) {
459 + LPWSTR ts = (LPWSTR)dwDataIn;
460 + dwDataIn = ts[::wcslen(ts) -1];
461 + }
462 + dwDataIn &= 0xffff;
463 + if ((dwType & BIG_ENDIAN) && (dwDataIn >> 8))
464 + dwDataIn = _byteswap_ushort(dwDataIn & 0xffff);
465 + if (dwCount == 1)
466 + dwDataIn &= 0xff;
467 + *(WORD *)(pbData + HEADER_SIZE) = dwDataIn & 0xffff;
468 + }
469 + else
470 + ::memcpy(pbData + HEADER_SIZE, (void *)dwDataIn, dwCount);
471 +
472 + // jichi 10/14/2014: Add filter function
473 + if (hp.filter_fun && !hp.filter_fun(pbData + HEADER_SIZE, &dwCount, &hp, textIndex) || dwCount <= 0) {
474 + if (pbData != pbSmallBuff)
475 + delete[] pbData;
476 + return 0;
477 + }
478 +
479 + *(DWORD *)pbData = dwAddr;
480 + if (dwType & (NO_CONTEXT|FIXING_SPLIT))
481 + dwRetn = 0;
482 + else if (dwRetn && (dwType & RELATIVE_SPLIT))
483 + dwRetn -= ::processStartAddress;
484 +
485 + *((DWORD *)pbData + 1) = dwRetn;
486 + *((DWORD *)pbData + 2) = dwSplit;
487 + if (dwCount) {
488 + IO_STATUS_BLOCK ios = {};
489 +
490 + IthCoolDown(); // jichi 9/28/2013: cool down to prevent parallelization in wine
491 + //CliLockPipe();
492 + if (STATUS_PENDING == NtWriteFile(::hPipe, 0, 0, 0, &ios, pbData, dwCount + HEADER_SIZE, 0, 0)) {
493 + NtWaitForSingleObject(::hPipe, 0, 0);
494 + NtFlushBuffersFile(::hPipe, &ios);
495 + }
496 + //CliUnlockPipe();
497 + }
498 + if (pbData != pbSmallBuff)
499 + delete[] pbData;
500 + }
501 + return 0;
502 +
503 +}
504 +
505 +int TextHook::InsertHook()
506 +{
507 + //ConsoleOutput("vnrcli:InsertHook: enter");
508 + NtWaitForSingleObject(hmMutex, 0, 0);
509 + int ok = InsertHookCode();
510 + IthReleaseMutex(hmMutex);
511 + if (hp.type & HOOK_ADDITIONAL) {
512 + NotifyHookInsert(hp.address);
513 + //ConsoleOutput(hook_name);
514 + //RegisterHookName(hook_name,hp.address);
515 + }
516 + //ConsoleOutput("vnrcli:InsertHook: leave");
517 + return ok;
518 +}
519 +
520 +int TextHook::InsertHookCode()
521 +{
522 + enum : int { yes = 0, no = 1 };
523 + DWORD ret = no;
524 + // jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7
525 + ITH_WITH_SEH(ret = UnsafeInsertHookCode());
526 + //if (ret == no)
527 + // ITH_WARN(L"Failed to insert hook");
528 + return ret;
529 +}
530 +
531 +int TextHook::UnsafeInsertHookCode()
532 +{
533 + //ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter");
534 + enum : int { yes = 0, no = 1 };
535 + // MODULE_OFFSET is set, but there's no module address
536 + // this means that this is an absolute address found on Windows 2000/XP
537 + // we make the address relative to the process base
538 + // we also store the original address in the function field because normally there can not
539 + // exist a function address without a module address
540 + if (hp.type & MODULE_OFFSET && !hp.module) {
541 + DWORD base = GetModuleBase();
542 + hp.function = hp.address;
543 + hp.address -= 0x400000;
544 + hp.address += base;
545 + hp.type &= ~MODULE_OFFSET;
546 + }
547 + else if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address.
548 + if (DWORD base = GetModuleBase(hp.module)) {
549 + if (hp.function && (hp.type & FUNCTION_OFFSET)) {
550 + base = GetExportAddress(base, hp.function);
551 + if (base)
552 + hp.address += base;
553 + else {
554 + current_hook--;
555 + ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: function not found in the export table");
556 + return no;
557 + }
558 + }
559 + else {
560 + hp.address += base;
561 + }
562 + hp.type &= ~(MODULE_OFFSET | FUNCTION_OFFSET);
563 + }
564 + else {
565 + current_hook--;
566 + ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: module not present");
567 + return no;
568 + }
569 + }
570 +
571 + {
572 + TextHook *it = hookman;
573 + for (int i = 0; (i < current_hook) && it; it++) { // Check if there is a collision.
574 + if (it->Address())
575 + i++;
576 + //it = hookman + i;
577 + if (it == this)
578 + continue;
579 + if (it->Address() <= hp.address &&
580 + it->Address() + it->Length() > hp.address) {
581 + it->ClearHook();
582 + break;
583 + }
584 + }
585 + }
586 +
587 + // Verify hp.address.
588 + MEMORY_BASIC_INFORMATION info = {};
589 + NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)hp.address, MemoryBasicInformation, &info, sizeof(info), nullptr);
590 + if (info.Type & PAGE_NOACCESS) {
591 + ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: page no access");
592 + return no;
593 + }
594 +
595 + // Initialize common routine.
596 + memcpy(recover, common_hook, sizeof(common_hook));
597 + BYTE *c = (BYTE *)hp.address,
598 + *r = recover;
599 + BYTE inst[8]; // jichi 9/27/2013: Why 8? Only 5 bytes will be written using NtWriteVirtualMemory
600 + inst[0] = 0xe9; // jichi 9/27/2013: 0xe9 is jump, see: http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Hackers_Manual
601 + __asm
602 + {
603 + mov edx,r // r = recover
604 + mov eax,this
605 + mov [edx+0xa],eax // push TextHook*, resolve to correspond hook.
606 + lea eax,[edx+0x13]
607 + mov edx,ProcessHook
608 + sub edx,eax
609 + mov [eax-4],edx // call ProcessHook
610 + mov eax,c
611 + add eax,5
612 + mov edx,r
613 + sub edx,eax
614 + lea eax,inst+1
615 + mov [eax],edx // jichi 12/17/2013: the parameter of jmp is in edx. So, ProcessHook must be naked.
616 + }
617 + r += sizeof(common_hook);
618 + hp.hook_len = 5;
619 + //bool jmpflag=false; // jichi 9/28/2013: nto used
620 + // Copy original code.
621 + switch (MapInstruction(hp.address, (DWORD)r, hp.hook_len, hp.recover_len)) {
622 + case -1:
623 + ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: failed to map instruction");
624 + return no;
625 + case 0:
626 + __asm
627 + {
628 + mov ecx,this
629 + movzx eax,[ecx]hp.hook_len
630 + movzx edx,[ecx]hp.recover_len
631 + add edx,[ecx]hp.address
632 + add eax,r
633 + add eax,5
634 + sub edx,eax
635 + mov [eax-5],0xe9 // jichi 9/27/2013: 0xe9 is jump
636 + mov [eax-4],edx
637 + }
638 + }
639 + // jichi 9/27/2013: Save the original instructions in the memory
640 + memcpy(original, (LPVOID)hp.address, hp.recover_len);
641 + //Check if the new hook range conflict with existing ones. Clear older if conflict.
642 + {
643 + TextHook *it = hookman;
644 + for (int i = 0; i < current_hook; it++) {
645 + if (it->Address())
646 + i++;
647 + if (it == this)
648 + continue;
649 + if (it->Address() >= hp.address &&
650 + it->Address() < hp.hook_len + hp.address) {
651 + it->ClearHook();
652 + break;
653 + }
654 + }
655 + }
656 + // Insert hook and flush instruction cache.
657 + enum {c8 = 0xcccccccc};
658 + DWORD int3[] = {c8, c8};
659 + DWORD t = 0x100,
660 + old,
661 + len;
662 + // jichi 9/27/2013: Overwrite the memory with inst
663 + // See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Memory%20Management/Virtual%20Memory/NtProtectVirtualMemory.html
664 + // See: http://doxygen.reactos.org/d8/d6b/ndk_2mmfuncs_8h_af942709e0c57981d84586e74621912cd.html
665 + DWORD addr = hp.address;
666 + NtProtectVirtualMemory(NtCurrentProcess(), (PVOID *)&addr, &t, PAGE_EXECUTE_READWRITE, &old);
667 + NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.address, inst, 5, &t);
668 + len = hp.recover_len - 5;
669 + if (len)
670 + NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.address + 5, int3, len, &t);
671 + NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.address, hp.recover_len);
672 + NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)::hookman, 0x1000);
673 + //ConsoleOutput("vnrcli:UnsafeInsertHookCode: leave: succeed");
674 + return 0;
675 +}
676 +
677 +int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind,
678 + DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
679 +{
680 + NtWaitForSingleObject(hmMutex, 0, 0);
681 + hp.address = (DWORD)addr;
682 + hp.offset = data;
683 + hp.index = data_ind;
684 + hp.split = split_off;
685 + hp.split_index = split_ind;
686 + hp.type = type;
687 + hp.hook_len = 0;
688 + hp.module = 0;
689 + hp.length_offset = len_off & 0xffff;
690 + current_hook++;
691 + if (current_available >= this)
692 + for (current_available = this + 1; current_available->Address(); current_available++);
693 + IthReleaseMutex(hmMutex);
694 + return this - hookman;
695 +}
696 +
697 +int TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag)
698 +{
699 + NtWaitForSingleObject(hmMutex, 0, 0);
700 + hp = h;
701 + hp.type |= set_flag;
702 + if (name && name != hook_name) {
703 + SetHookName(name);
704 + }
705 + current_hook++;
706 + current_available = this+1;
707 + while (current_available->Address())
708 + current_available++;
709 + IthReleaseMutex(hmMutex);
710 + return 1;
711 +}
712 +
713 +int TextHook::RemoveHook()
714 +{
715 + enum : int { yes = 1, no = 0 };
716 + if (!hp.address)
717 + return no;
718 + ConsoleOutput("vnrcli:RemoveHook: enter");
719 + const LONGLONG timeout = -50000000; // jichi 9/28/2012: in 100ns, wait at most for 5 seconds
720 + NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout);
721 + DWORD l = hp.hook_len;
722 + //with_seh({ // jichi 9/17/2013: might crash ><
723 + // jichi 12/25/2013: Actually, __try cannot catch such kind of exception
724 + ITH_TRY {
725 + NtWriteVirtualMemory(NtCurrentProcess(), (LPVOID)hp.address, original, hp.recover_len, &l);
726 + NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.address, hp.recover_len);
727 + } ITH_EXCEPT {}
728 + //});
729 + hp.hook_len = 0;
730 + IthReleaseMutex(hmMutex);
731 + ConsoleOutput("vnrcli:RemoveHook: leave");
732 + return yes;
733 +}
734 +
735 +int TextHook::ClearHook()
736 +{
737 + NtWaitForSingleObject(hmMutex, 0, 0);
738 + int err = RemoveHook();
739 + if (hook_name) {
740 + delete[] hook_name;
741 + hook_name = nullptr;
742 + }
743 + memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH
744 + //if (current_available>this)
745 + // current_available = this;
746 + current_hook--;
747 + IthReleaseMutex(hmMutex);
748 + return err;
749 +}
750 +
751 +int TextHook::ModifyHook(const HookParam &hp)
752 +{
753 + //WCHAR name[0x40];
754 + DWORD len = 0;
755 + if (hook_name)
756 + len = ::strlen(hook_name);
757 + LPSTR name = 0;
758 + if (len) {
759 + name = new char[len + 1];
760 + //ITH_MEMSET_HEAP(name, 0, sizeof(wchar_t) * (len + 1)); // jichi 9/26/2013: zero memory
761 + strcpy(name, hook_name);
762 + }
763 + ClearHook();
764 + InitHook(hp, name);
765 + InsertHook();
766 + if (name)
767 + delete[] name;
768 + return 0;
769 +}
770 +
771 +int TextHook::RecoverHook()
772 +{
773 + if (hp.address) {
774 + // jichi 9/28/2013: Only enable TextOutA to debug Cross Channel
775 + //if (hp.address == (DWORD)TextOutA)
776 + InsertHook();
777 + return 1;
778 + }
779 + return 0;
780 +}
781 +
782 +int TextHook::SetHookName(LPCSTR name)
783 +{
784 + name_length = strlen(name) + 1;
785 + if (hook_name)
786 + delete[] hook_name;
787 + hook_name = new char[name_length];
788 + //ITH_MEMSET_HEAP(hook_name, 0, sizeof(wchar_t) * name_length); // jichi 9/26/2013: zero memory
789 + strcpy(hook_name, name);
790 + return 0;
791 +}
792 +
793 +int TextHook::GetLength(DWORD base, DWORD in)
794 +{
795 + if (base == 0)
796 + return 0;
797 + int len;
798 + switch (hp.length_offset) {
799 + default: // jichi 12/26/2013: I should not put this default branch to the end
800 + len = *((int *)base + hp.length_offset);
801 + if (len >= 0) {
802 + if (hp.type & USING_UNICODE)
803 + len <<= 1;
804 + break;
805 + }
806 + else if (len != -1)
807 + break;
808 + //len == -1 then continue to case 0.
809 + case 0:
810 + if (hp.type & USING_UNICODE)
811 + len = wcslen((const wchar_t *)in) << 1;
812 + else
813 + len = strlen((const char *)in);
814 + break;
815 + case 1:
816 + if (hp.type & USING_UNICODE)
817 + len = 2;
818 + else {
819 + if (hp.type & BIG_ENDIAN)
820 + in >>= 8;
821 + len = LeadByteTable[in & 0xff]; //Slightly faster than IsDBCSLeadByte
822 + }
823 + break;
824 + }
825 + // jichi 12/25/2013: This function originally return -1 if failed
826 + //return len;
827 + return max(0, len);
828 +}
829 +
830 +// EOF
831 +
832 +//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
833 +
834 +/*
835 +DWORD recv_esp, recv_addr;
836 +EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
837 + void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
838 +{
839 + //WCHAR str[0x40],
840 + // name[0x100];
841 + //ConsoleOutput(L"Exception raised during hook processing.");
842 + //swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
843 + //ConsoleOutput(str);
844 + //MEMORY_BASIC_INFORMATION info;
845 + //if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
846 + // MemoryBasicInformation,&info,sizeof(info),0)) &&
847 + // NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
848 + // MemorySectionName,name,0x200,0))) {
849 + // swprintf(str, L"Exception offset: 0x%.8X:%s",
850 + // ContextRecord->Eip-(DWORD)info.AllocationBase,
851 + // wcsrchr(name,L'\\')+1);
852 + // ConsoleOutput(str);
853 + //}
854 + ContextRecord->Esp = recv_esp;
855 + ContextRecord->Eip = recv_addr;
856 + return ExceptionContinueExecution;
857 +}
858 +
859 +
860 +//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
861 +
862 +DWORD recv_esp, recv_addr;
863 +EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
864 + void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
865 +{
866 + //WCHAR str[0x40],
867 + // name[0x100];
868 + //ConsoleOutput(L"Exception raised during hook processing.");
869 + //swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
870 + //ConsoleOutput(str);
871 + //MEMORY_BASIC_INFORMATION info;
872 + //if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
873 + // MemoryBasicInformation,&info,sizeof(info),0)) &&
874 + // NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
875 + // MemorySectionName,name,0x200,0))) {
876 + // swprintf(str, L"Exception offset: 0x%.8X:%s",
877 + // ContextRecord->Eip-(DWORD)info.AllocationBase,
878 + // wcsrchr(name,L'\\')+1);
879 + // ConsoleOutput(str);
880 + //}
881 + ContextRecord->Esp = recv_esp;
882 + ContextRecord->Eip = recv_addr;
883 + return ExceptionContinueExecution;
884 +}
885 +
886 +__declspec(naked) // jichi 10/2/2013: No prolog and epilog
887 +int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
888 +{
889 + __asm
890 + {
891 + mov eax,seh_recover
892 + mov recv_addr,eax
893 + push ExceptHandler
894 + push fs:[0]
895 + mov recv_esp,esp
896 + mov fs:[0],esp
897 + push esi
898 + push edx
899 + call TextHook::Send
900 + test eax,eax
901 + jz seh_recover
902 + mov ecx,SafeExit
903 + mov [esp + 0x8], ecx // change exit point
904 +seh_recover:
905 + pop dword ptr fs:[0]
906 + pop ecx
907 + retn
908 + }
909 +}
910 +*/
1 +#pragma once
2 +
3 +// texthook.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 "src/tree/avl.h"
12 +#include "include/types.h"
13 +#include <windows.h>
14 +
15 +// jichi 12/25/2013: Header in each message sent to vnrsrv
16 +// There are totally three elements
17 +// - 0x0 dwAddr hook address
18 +// - 0x4 dwRetn return address
19 +// - 0x8 dwSplit split value
20 +#define HEADER_SIZE 0xc
21 +
22 +extern int current_hook;
23 +extern WCHAR dll_mutex[];
24 +//extern WCHAR dll_name[];
25 +extern DWORD trigger;
26 +//extern DWORD current_process_id;
27 +
28 +// jichi 6/3/2014: Get memory range of the current module
29 +extern DWORD processStartAddress,
30 + processStopAddress;
31 +
32 +template <class T, class D, class fComp, class fCopy, class fLength>
33 +class AVLTree;
34 +struct FunctionInfo {
35 + DWORD addr;
36 + DWORD module;
37 + DWORD size;
38 + LPWSTR name;
39 +};
40 +struct SCMP;
41 +struct SCPY;
42 +struct SLEN;
43 +extern AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN> *tree;
44 +
45 +void InitFilterTable();
46 +
47 +// jichi 9/25/2013: This class will be used by NtMapViewOfSectionfor
48 +// interprocedure communication, where constructor/destructor will NOT work.
49 +class TextHook : public Hook
50 +{
51 + int UnsafeInsertHookCode();
52 + DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn);
53 +public:
54 + int InsertHook();
55 + int InsertHookCode();
56 + int InitHook(const HookParam &hp, LPCSTR name = 0, WORD set_flag = 0);
57 + int InitHook(LPVOID addr, DWORD data, DWORD data_ind,
58 + DWORD split_off, DWORD split_ind, WORD type, DWORD len_off = 0);
59 + DWORD Send(DWORD dwDataBase, DWORD dwRetn);
60 + int RecoverHook();
61 + int RemoveHook();
62 + int ClearHook();
63 + int ModifyHook(const HookParam&);
64 + int SetHookName(LPCSTR name);
65 + int GetLength(DWORD base, DWORD in); // jichi 12/25/2013: Return 0 if failed
66 + void CoolDown(); // jichi 9/28/2013: flush instruction cache on wine
67 +};
68 +
69 +extern TextHook *hookman,
70 + *current_available;
71 +
72 +//void InitDefaultHook();
73 +
74 +struct FilterRange { DWORD lower, upper; };
75 +extern FilterRange *filter;
76 +
77 +extern bool running,
78 + live;
79 +
80 +extern HANDLE hPipe,
81 + hmMutex;
82 +
83 +DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter);
84 +DWORD WINAPI CommandPipe(LPVOID lpThreadParameter);
85 +
86 +//void RequestRefreshProfile();
87 +
88 +//typedef DWORD (*InsertHookFun)(DWORD);
89 +//typedef DWORD (*IdentifyEngineFun)();
90 +//typedef DWORD (*InsertDynamicHookFun)(LPVOID addr, DWORD frame, DWORD stack);
91 +//extern IdentifyEngineFun IdentifyEngine;
92 +//extern InsertDynamicHookFun InsertDynamicHook;
93 +
94 +// jichi 9/28/2013: Protect pipeline in wine
95 +void CliLockPipe();
96 +void CliUnlockPipe();
97 +
98 +// EOF
1 +// main.cc
2 +// 8/24/2013 jichi
3 +// Branch: ITH_DLL/main.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:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
9 +#endif // _MSC_VER
10 +
11 +#include "src/main.h"
12 +#include "src/tree/avl.h"
13 +#include "src/engine/match.h"
14 +#include "src/hijack/texthook.h"
15 +#include "src/util/growl.h"
16 +#include "src/except.h"
17 +#include "include/const.h"
18 +#include "include/defs.h"
19 +#include "ithsys/ithsys.h"
20 +#include "ccutil/ccmacro.h"
21 +#include <cstdio> // for swprintf
22 +//#include "ntinspect/ntinspect.h"
23 +//#include "winseh/winseh.h"
24 +//#include <boost/foreach.hpp>
25 +//#include "md5.h"
26 +//#include <ITH\AVL.h>
27 +//#include <ITH\ntdll.h>
28 +
29 +// Global variables
30 +
31 +// jichi 6/3/2014: memory range of the current module
32 +DWORD processStartAddress,
33 + processStopAddress;
34 +
35 +namespace { // unnamed
36 +wchar_t processName[MAX_PATH];
37 +
38 +inline void GetProcessName(wchar_t *name)
39 +{
40 + //assert(name);
41 + PLDR_DATA_TABLE_ENTRY it;
42 + __asm
43 + {
44 + mov eax,fs:[0x30]
45 + mov eax,[eax+0xc]
46 + mov eax,[eax+0xc]
47 + mov it,eax
48 + }
49 + wcscpy(name, it->BaseDllName.Buffer);
50 +}
51 +} // unmaed namespace
52 +
53 +enum { HOOK_BUFFER_SIZE = MAX_HOOK * sizeof(TextHook) };
54 +//#define MAX_HOOK (HOOK_BUFFER_SIZE/sizeof(TextHook))
55 +DWORD hook_buff_len = HOOK_BUFFER_SIZE;
56 +
57 +namespace { FilterRange _filter[IHF_FILTER_CAPACITY]; }
58 +FilterRange *filter = _filter;
59 +
60 +WCHAR hm_section[0x100];
61 +HINSTANCE hDLL;
62 +HANDLE hSection;
63 +bool running,
64 + live = false;
65 +int current_hook = 0,
66 + user_hook_count = 0;
67 +DWORD trigger = 0;
68 +HANDLE
69 + hFile,
70 + hMutex,
71 + hmMutex;
72 +//DWORD current_process_id;
73 +extern DWORD enter_count;
74 +//extern LPWSTR current_dir;
75 +extern DWORD engine_type;
76 +extern DWORD module_base;
77 +AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN> *tree;
78 +
79 +namespace { // unnamed
80 +
81 +void AddModule(DWORD hModule, DWORD size, LPWSTR name)
82 +{
83 + FunctionInfo info = {0, hModule, size, name};
84 + IMAGE_DOS_HEADER *DosHdr = (IMAGE_DOS_HEADER *)hModule;
85 + if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
86 + DWORD dwReadAddr = hModule + DosHdr->e_lfanew;
87 + IMAGE_NT_HEADERS *NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr;
88 + if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
89 + DWORD dwExportAddr = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
90 + if (dwExportAddr == 0)
91 + return;
92 + dwExportAddr += hModule;
93 + IMAGE_EXPORT_DIRECTORY *ExtDir = (IMAGE_EXPORT_DIRECTORY*)dwExportAddr;
94 + dwExportAddr = hModule+ExtDir->AddressOfNames;
95 + for (UINT uj = 0; uj < ExtDir->NumberOfNames; uj++) {
96 + DWORD dwFuncName = *(DWORD *)dwExportAddr;
97 + char *pcBuffer = (char *)(hModule + dwFuncName);
98 + char *pcFuncPtr = (char *)(hModule + (DWORD)ExtDir->AddressOfNameOrdinals+(uj * sizeof(WORD)));
99 + WORD word = *(WORD *)pcFuncPtr;
100 + pcFuncPtr = (char *)(hModule + (DWORD)ExtDir->AddressOfFunctions+(word * sizeof(DWORD)));
101 + info.addr = hModule + *(DWORD *)pcFuncPtr;
102 + ::tree->Insert(pcBuffer, info);
103 + dwExportAddr += sizeof(DWORD);
104 + }
105 + }
106 + }
107 +}
108 +
109 +void AddAllModules()
110 +{
111 + // jichi 9/26/2013: AVLTree is already zero
112 + PPEB ppeb;
113 + __asm {
114 + mov eax, fs:[0x30]
115 + mov ppeb, eax
116 + }
117 + DWORD temp = *(DWORD *)(&ppeb->Ldr->InLoadOrderModuleList);
118 + PLDR_DATA_TABLE_ENTRY it = (PLDR_DATA_TABLE_ENTRY)temp;
119 + while (it->SizeOfImage) {
120 + AddModule((DWORD)it->DllBase, it->SizeOfImage, it->BaseDllName.Buffer);
121 + it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink;
122 + if (*(DWORD *)it == temp)
123 + break;
124 + }
125 +}
126 +
127 +void RequestRefreshProfile()
128 +{
129 + if (::live) {
130 + BYTE buffer[0x80] = {}; // 11/14/2013: reset to zero. Shouldn't it be 0x8 instead of 0x80?
131 + *(DWORD *)buffer = -1;
132 + *(DWORD *)(buffer + 4) = 1;
133 + *(DWORD *)(buffer + 8) = 0;
134 + IO_STATUS_BLOCK ios;
135 + CliLockPipe();
136 + NtWriteFile(hPipe, 0, 0, 0, &ios, buffer, HEADER_SIZE, 0, 0);
137 + CliUnlockPipe();
138 + }
139 +}
140 +
141 +} // unnamed namespace
142 +
143 +DWORD GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name)
144 +{
145 + TreeNode<char *,FunctionInfo> *node = ::tree->Search(name);
146 + if (node) {
147 + if (addr) *addr = node->data.addr;
148 + if (base) *base = node->data.module;
149 + if (size) *size = node->data.size;
150 + if (base_name) *base_name = node->data.name;
151 + return TRUE;
152 + }
153 + else
154 + return FALSE;
155 +}
156 +
157 +BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID lpReserved)
158 +{
159 + static HANDLE hSendThread,
160 + hCmdThread;
161 +
162 + CC_UNUSED(lpReserved);
163 +
164 + //static WCHAR dll_exist[] = L"ITH_DLL_RUNNING";
165 + static WCHAR dll_exist[] = ITH_CLIENT_MUTEX;
166 + static HANDLE hDllExist;
167 +
168 + // jichi 9/23/2013: wine deficenciy on mapping sections
169 + // Whe set to false, do not map sections.
170 + //static bool ith_has_section = true;
171 +
172 + switch (fdwReason) {
173 + case DLL_PROCESS_ATTACH:
174 + {
175 + static bool attached_ = false;
176 + if (attached_) // already attached
177 + return TRUE;
178 + attached_ = true;
179 +
180 + LdrDisableThreadCalloutsForDll(hModule);
181 +
182 + //IthBreak();
183 + ::module_base = (DWORD)hModule;
184 +
185 + //if (!IthInitSystemService()) {
186 + // GROWL_WARN(L"Initialization failed.\nAre you running game on a network drive?");
187 + // return FALSE;
188 + //}
189 + // No longer checking if SystemService fails, which could happen on non-Japanese OS
190 + IthInitSystemService();
191 +
192 + swprintf(hm_section, ITH_SECTION_ L"%d", current_process_id);
193 +
194 + // jichi 9/25/2013: Interprocedural communication with vnrsrv.
195 + hSection = IthCreateSection(hm_section, HOOK_SECTION_SIZE, PAGE_EXECUTE_READWRITE);
196 + ::hookman = nullptr;
197 + NtMapViewOfSection(hSection, NtCurrentProcess(),
198 + (LPVOID *)&::hookman, 0, hook_buff_len, 0, &hook_buff_len, ViewUnmap, 0,
199 + PAGE_EXECUTE_READWRITE);
200 + //PAGE_EXECUTE_READWRITE);
201 +
202 + GetProcessName(::processName);
203 + FillRange(::processName, &::processStartAddress, &::processStopAddress);
204 + //NtInspect::getProcessMemoryRange(&::processStartAddress, &::processStopAddress);
205 +
206 + //if (!::hookman) {
207 + // ith_has_section = false;
208 + // ::hookman = new TextHook[MAX_HOOK];
209 + // memset(::hookman, 0, MAX_HOOK * sizeof(TextHook));
210 + //}
211 +
212 + {
213 + wchar_t hm_mutex[0x100];
214 + swprintf(hm_mutex, ITH_HOOKMAN_MUTEX_ L"%d", current_process_id);
215 + ::hmMutex = IthCreateMutex(hm_mutex, FALSE);
216 + }
217 + {
218 + wchar_t dll_mutex[0x100];
219 + swprintf(dll_mutex, ITH_PROCESS_MUTEX_ L"%d", current_process_id);
220 + DWORD exists;
221 + ::hMutex = IthCreateMutex(dll_mutex, TRUE, &exists); // jichi 9/18/2013: own is true, make sure the injected dll is singleton
222 + if (exists)
223 + return FALSE;
224 + }
225 +
226 + hDllExist = IthCreateMutex(dll_exist, 0);
227 + hDLL = hModule;
228 + ::running = true;
229 + ::current_available = ::hookman;
230 + ::tree = new AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN>;
231 + AddAllModules();
232 + InitFilterTable();
233 +
234 + hSendThread = IthCreateThread(WaitForPipe, 0);
235 + hCmdThread = IthCreateThread(CommandPipe, 0);
236 + } break;
237 + case DLL_PROCESS_DETACH:
238 + {
239 + static bool detached_ = false;
240 + if (detached_) // already detached
241 + return TRUE;
242 + detached_ = true;
243 +
244 + // jichi 10/2/2103: Cannot use __try in functions that require object unwinding
245 + //ITH_TRY {
246 + ::running = false;
247 + ::live = false;
248 +
249 + const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
250 +
251 + Engine::terminate();
252 +
253 + if (hSendThread) {
254 + NtWaitForSingleObject(hSendThread, 0, (PLARGE_INTEGER)&timeout);
255 + NtClose(hSendThread);
256 + }
257 +
258 + if (hCmdThread) {
259 + NtWaitForSingleObject(hCmdThread, 0, (PLARGE_INTEGER)&timeout);
260 + NtClose(hCmdThread);
261 + }
262 +
263 + for (TextHook *man = ::hookman; man->RemoveHook(); man++);
264 + //LARGE_INTEGER lint = {-10000, -1};
265 + while (::enter_count)
266 + IthSleep(1); // jichi 9/28/2013: sleep for 1 ms
267 + //NtDelayExecution(0, &lint);
268 + for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++)
269 + man->ClearHook();
270 + //if (ith_has_section)
271 + NtUnmapViewOfSection(NtCurrentProcess(), ::hookman);
272 + //else
273 + // delete[] ::hookman;
274 + NtClose(hSection);
275 + NtClose(hMutex);
276 +
277 + delete ::tree;
278 + IthCloseSystemService();
279 + NtClose(hmMutex);
280 + NtClose(hDllExist);
281 + //} ITH_EXCEPT {}
282 + } break;
283 + }
284 + return TRUE;
285 +}
286 +
287 +//extern "C" {
288 +DWORD NewHook(const HookParam &hp, LPCSTR name, DWORD flag)
289 +{
290 + CHAR str[128];
291 + int current = ::current_available - ::hookman;
292 + if (current < MAX_HOOK) {
293 + //flag &= 0xffff;
294 + //if ((flag & HOOK_AUXILIARY) == 0)
295 + flag |= HOOK_ADDITIONAL;
296 + if (name == NULL || name[0] == '\0')
297 + {
298 + sprintf(str, "UserHook%d", user_hook_count++);
299 + }
300 + else
301 + {
302 + strcpy(str, name);
303 + }
304 +
305 + ConsoleOutput("vnrcli:NewHook: try inserting hook");
306 +
307 + // jichi 7/13/2014: This function would raise when too many hooks added
308 + ::hookman[current].InitHook(hp, str, flag & 0xffff);
309 +
310 + if (::hookman[current].InsertHook() == 0) {
311 + ConsoleOutput("vnrcli:NewHook: hook inserted");
312 + //ConsoleOutputW(name);
313 + //swprintf(str,L"Insert address 0x%.8X.", hookman[current].Address());
314 + RequestRefreshProfile();
315 + } else
316 + ConsoleOutput("vnrcli:NewHook:WARNING: failed to insert hook");
317 + }
318 + return 0;
319 +}
320 +DWORD RemoveHook(DWORD addr)
321 +{
322 + for (int i = 0; i < MAX_HOOK; i++)
323 + if (::hookman[i].Address ()== addr) {
324 + ::hookman[i].ClearHook();
325 + return 0;
326 + }
327 + return 0;
328 +}
329 +
330 +DWORD SwitchTrigger(DWORD t)
331 +{
332 + trigger = t;
333 + return 0;
334 +}
335 +
336 +//} // extern "C"
337 +
338 +
339 +namespace { // unnamed
340 +
341 +BOOL SafeFillRange(LPCWSTR dll, DWORD *lower, DWORD *upper)
342 +{
343 + BOOL ret = FALSE;
344 + ITH_WITH_SEH(ret = FillRange(dll, lower, upper));
345 + return ret;
346 +}
347 +
348 +} // unnamed namespace
349 +
350 +// jichi 12/13/2013
351 +// Use listdlls from SystemInternals
352 +void InitFilterTable()
353 +{
354 + LPCWSTR l[] = { IHF_FILTER_DLL_LIST };
355 + enum { capacity = sizeof(l)/sizeof(*l) };
356 +
357 + size_t count = 0;
358 + //for (auto p : l)
359 + for (size_t i = 0; i < capacity; i++)
360 + if (SafeFillRange(l[i], &::filter[count].lower, &::filter[count].upper))
361 + count++;
362 +}
363 +
364 +// EOF
365 +/*
366 +
367 +static DWORD recv_esp, recv_addr;
368 +static CONTEXT recover_context;
369 +static __declspec(naked) void MySEH()
370 +{
371 + __asm{
372 + mov eax, [esp+0xC]
373 + mov edi,eax
374 + mov ecx,0xB3
375 + mov esi, offset recover_context
376 + rep movs
377 + mov ecx, [recv_esp]
378 + mov [eax+0xC4],ecx
379 + mov edx, [recv_addr]
380 + mov [eax+0xB8],edx
381 + xor eax,eax
382 + retn
383 + }
384 +}
385 +
386 +EXCEPTION_DISPOSITION ExceptHandler(
387 + EXCEPTION_RECORD *ExceptionRecord,
388 + void * EstablisherFrame,
389 + CONTEXT *ContextRecord,
390 + void * DispatcherContext )
391 +{
392 + ContextRecord->Esp=recv_esp;
393 + ContextRecord->Eip=recv_addr;
394 + return ExceptionContinueExecution;
395 +}
396 +int GuardRange(LPWSTR module, DWORD *a, DWORD *b)
397 +{
398 + int flag=0;
399 + __asm
400 + {
401 + mov eax,seh_recover
402 + mov recv_addr,eax
403 + push ExceptHandler
404 + push fs:[0]
405 + mov recv_esp,esp
406 + mov fs:[0],esp
407 + }
408 + flag = FillRange(module, a, b);
409 + __asm
410 + {
411 +seh_recover:
412 + mov eax,[esp]
413 + mov fs:[0],eax
414 + add esp,8
415 + }
416 + return flag;
417 +}
418 +*/
1 +#pragma once
2 +
3 +// main.h
4 +// 8/23/2013 jichi
5 +// Branch: ITH/IHF_DLL.h, rev 66
6 +
7 +#include "include/const.h"
8 +#include "include/types.h"
9 +
10 +void ConsoleOutput(LPCSTR text); // jichi 12/25/2013: Used to return length of sent text
11 +DWORD NotifyHookInsert(DWORD addr);
12 +DWORD NewHook(const HookParam &hp, LPCSTR name, DWORD flag = HOOK_ENGINE);
13 +DWORD RemoveHook(DWORD addr);
14 +DWORD SwitchTrigger(DWORD on);
15 +DWORD GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name);
16 +
17 +// 10/14/2014 jichi: disable GDI hooks
18 +void EnableGDIHooks();
19 +void EnableGDIPlusHooks();
20 +void DisableGDIHooks();
21 +void DisableGDIPlusHooks();
22 +bool GDIHooksEnabled();
23 +bool GDIPlusHooksEnabled();
24 +
25 +// EOF
1 +// pipe.cc
2 +// 8/24/2013 jichi
3 +// Branch: ITH_DLL/pipe.cpp, rev 66
4 +// 8/24/2013 TODO: Clean up this file
5 +
6 +#ifdef _MSC_VER
7 +# pragma warning (disable:4100) // C4100: unreference formal parameter
8 +#endif // _MSC_VER
9 +
10 +#include "src/hijack/texthook.h"
11 +#include "src/engine/match.h"
12 +#include "src/util/util.h"
13 +#include "src/main.h"
14 +#include "include/defs.h"
15 +//#include "src/util/growl.h"
16 +#include "ithsys/ithsys.h"
17 +#include "ccutil/ccmacro.h"
18 +#include <cstdio> // for swprintf
19 +
20 +//#include <ITH\AVL.h>
21 +//#include <ITH\ntdll.h>
22 +WCHAR detach_mutex[0x20];
23 +//WCHAR write_event[0x20];
24 +//WCHAR engine_event[0x20];
25 +
26 +//WCHAR recv_pipe[] = L"\\??\\pipe\\ITH_PIPE";
27 +//WCHAR command[] = L"\\??\\pipe\\ITH_COMMAND";
28 +wchar_t recv_pipe[] = ITH_TEXT_PIPE;
29 +wchar_t command[] = ITH_COMMAND_PIPE;
30 +
31 +LARGE_INTEGER wait_time = {-100*10000, -1};
32 +LARGE_INTEGER sleep_time = {-20*10000, -1};
33 +
34 +DWORD engine_type;
35 +DWORD module_base;
36 +
37 +HANDLE hPipe,
38 + hCommand,
39 + hDetach; //,hLose;
40 +//InsertHookFun InsertHook;
41 +//IdentifyEngineFun IdentifyEngine;
42 +//InsertDynamicHookFun InsertDynamicHook;
43 +
44 +// jichi 9/28/2013: protect pipe on wine
45 +// Put the definition in this file so that it might be inlined
46 +void CliUnlockPipe()
47 +{
48 + if (IthIsWine())
49 + IthReleaseMutex(::hmMutex);
50 +}
51 +
52 +void CliLockPipe()
53 +{
54 + if (IthIsWine()) {
55 + const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
56 + NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout);
57 + }
58 +}
59 +
60 +HANDLE IthOpenPipe(LPWSTR name, ACCESS_MASK direction)
61 +{
62 + UNICODE_STRING us;
63 + RtlInitUnicodeString(&us,name);
64 + SECURITY_DESCRIPTOR sd = {1};
65 + OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
66 + HANDLE hFile;
67 + IO_STATUS_BLOCK isb;
68 + if (NT_SUCCESS(NtCreateFile(&hFile, direction, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0)))
69 + return hFile;
70 + else
71 + return INVALID_HANDLE_VALUE;
72 +}
73 +
74 +DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter) // Dynamically detect ITH main module status.
75 +{
76 + CC_UNUSED(lpThreadParameter);
77 + // jichi 7/2/2015:This must be consistent with the struct declared in vnrhost/pipe.cc
78 + struct {
79 + DWORD pid;
80 + DWORD module;
81 + TextHook *man;
82 + //DWORD engine;
83 + } u;
84 +
85 + //swprintf(engine_event,L"ITH_ENGINE_%d",current_process_id);
86 + swprintf(::detach_mutex, ITH_DETACH_MUTEX_ L"%d", current_process_id);
87 + //swprintf(lose_event,L"ITH_LOSEPIPE_%d",current_process_id);
88 + //hEngine=IthCreateEvent(engine_event);
89 + //NtWaitForSingleObject(hEngine,0,0);
90 + //NtClose(hEngine);
91 +
92 + //while (!engine_registered)
93 + // NtDelayExecution(0, &wait_time);
94 +
95 + //LoadEngine(L"ITH_Engine.dll");
96 + u.module = module_base;
97 + u.pid = current_process_id;
98 + u.man = hookman;
99 + //u.engine = engine_base; // jichi 10/19/2014: disable the second dll
100 + HANDLE hPipeExist = IthOpenEvent(ITH_PIPEEXISTS_EVENT);
101 + IO_STATUS_BLOCK ios;
102 + //hLose=IthCreateEvent(lose_event,0,0);
103 + if (hPipeExist != INVALID_HANDLE_VALUE)
104 + while (::running) {
105 + ::hPipe = INVALID_HANDLE_VALUE;
106 + hCommand = INVALID_HANDLE_VALUE;
107 + while (NtWaitForSingleObject(hPipeExist, 0, &wait_time) == WAIT_TIMEOUT)
108 + if (!::running)
109 + goto _release;
110 + HANDLE hMutex = IthCreateMutex(ITH_GRANTPIPE_MUTEX, 0);
111 + NtWaitForSingleObject(hMutex, 0, 0);
112 + while (::hPipe == INVALID_HANDLE_VALUE||
113 + hCommand == INVALID_HANDLE_VALUE) {
114 + NtDelayExecution(0, &sleep_time);
115 + if (::hPipe == INVALID_HANDLE_VALUE)
116 + ::hPipe = IthOpenPipe(recv_pipe, GENERIC_WRITE);
117 + if (hCommand == INVALID_HANDLE_VALUE)
118 + hCommand = IthOpenPipe(command, GENERIC_READ);
119 + }
120 + //NtClearEvent(hLose);
121 + CliLockPipe();
122 + NtWriteFile(::hPipe, 0, 0, 0, &ios, &u, sizeof(u), 0, 0);
123 + CliUnlockPipe();
124 + for (int i = 0, count = 0; count < ::current_hook; i++)
125 + if (hookman[i].RecoverHook()) // jichi 9/27/2013: This is the place where built-in hooks like TextOutA are inserted
126 + count++;
127 + //ConsoleOutput(dll_name);
128 + //OutputDWORD(tree->Count());
129 + NtReleaseMutant(hMutex,0);
130 + NtClose(hMutex);
131 +
132 +
133 + ::live = true;
134 +
135 + // jichi 7/17/2014: Always hijack by default or I have to wait for it is ready
136 + Engine::hijack();
137 + ConsoleOutput("vnrcli:WaitForPipe: pipe connected");
138 +
139 + ::hDetach = IthCreateMutex(::detach_mutex,1);
140 + while (::running && NtWaitForSingleObject(hPipeExist, 0, &sleep_time) == WAIT_OBJECT_0)
141 + NtDelayExecution(0, &sleep_time);
142 + ::live = false;
143 +
144 + for (int i = 0, count = 0; count < ::current_hook; i++)
145 + if (hookman[i].RemoveHook())
146 + count++;
147 + if (!::running) {
148 + IthCoolDown(); // jichi 9/28/2013: Use cooldown instead of lock pipe to prevent from hanging on exit
149 + //CliLockPipe();
150 + //NtWriteFile(::hPipe, 0, 0, 0, &ios, man, 4, 0, 0);
151 + NtWriteFile(::hPipe, 0, 0, 0, &ios, hookman, 4, 0, 0);
152 + //CliUnlockPipe();
153 + IthReleaseMutex(::hDetach);
154 + }
155 + NtClose(::hDetach);
156 + NtClose(::hPipe);
157 + }
158 +_release:
159 + //NtClose(hLose);
160 + NtClose(hPipeExist);
161 + return 0;
162 +}
163 +
164 +DWORD WINAPI CommandPipe(LPVOID lpThreadParameter)
165 +{
166 + CC_UNUSED(lpThreadParameter);
167 + DWORD command;
168 + BYTE buff[0x400] = {};
169 + HANDLE hPipeExist;
170 + hPipeExist = IthOpenEvent(ITH_PIPEEXISTS_EVENT);
171 + IO_STATUS_BLOCK ios={};
172 +
173 + if (hPipeExist != INVALID_HANDLE_VALUE)
174 + while (::running) {
175 + while (!::live) {
176 + if (!::running)
177 + goto _detach;
178 + NtDelayExecution(0, &sleep_time);
179 + }
180 + // jichi 9/27/2013: Why 0x200 not 0x400? wchar_t?
181 + switch (NtReadFile(hCommand, 0, 0, 0, &ios, buff, 0x200, 0, 0)) {
182 + case STATUS_PIPE_BROKEN:
183 + case STATUS_PIPE_DISCONNECTED:
184 + NtClearEvent(hPipeExist);
185 + continue;
186 + case STATUS_PENDING:
187 + NtWaitForSingleObject(hCommand, 0, 0);
188 + switch (ios.Status) {
189 + case STATUS_PIPE_BROKEN:
190 + case STATUS_PIPE_DISCONNECTED:
191 + NtClearEvent(hPipeExist);
192 + continue;
193 + case 0: break;
194 + default:
195 + if (NtWaitForSingleObject(::hDetach, 0, &wait_time) == WAIT_OBJECT_0)
196 + goto _detach;
197 + }
198 + }
199 + if (ios.uInformation && ::live) {
200 + command = *(DWORD *)buff;
201 + switch(command) {
202 + case HOST_COMMAND_NEW_HOOK:
203 + //IthBreak();
204 + buff[ios.uInformation] = 0;
205 + //buff[ios.uInformation + 1] = 0;
206 + NewHook(*(HookParam *)(buff + 4), (LPSTR)(buff + 4 + sizeof(HookParam)), 0);
207 + break;
208 + case HOST_COMMAND_REMOVE_HOOK:
209 + {
210 + DWORD rm_addr = *(DWORD *)(buff+4);
211 + HANDLE hRemoved = IthOpenEvent(ITH_REMOVEHOOK_EVENT);
212 +
213 + TextHook *in = hookman;
214 + for (int i = 0; i < current_hook; in++) {
215 + if (in->Address()) i++;
216 + if (in->Address() == rm_addr) break;
217 + }
218 + if (in->Address())
219 + in->ClearHook();
220 + IthSetEvent(hRemoved);
221 + NtClose(hRemoved);
222 + } break;
223 +#if 0 // Temporarily disabled as these operations are not used by VNR
224 + case HOST_COMMAND_MODIFY_HOOK:
225 + {
226 + DWORD rm_addr = *(DWORD *)(buff + 4);
227 + HANDLE hModify = IthOpenEvent(ITH_MODIFYHOOK_EVENT);
228 + TextHook *in = hookman;
229 + for (int i = 0; i < current_hook; in++) {
230 + if (in->Address())
231 + i++;
232 + if (in->Address() == rm_addr)
233 + break;
234 + }
235 + if (in->Address())
236 + in->ModifyHook(*(HookParam *)(buff + 4));
237 + IthSetEvent(hModify);
238 + NtClose(hModify);
239 + } break;
240 + case HOST_COMMAND_HIJACK_PROCESS:
241 + Engine::hijack();
242 + break;
243 +#endif // 0
244 + case HOST_COMMAND_DETACH:
245 + ::running = false;
246 + ::live = false;
247 + goto _detach;
248 + }
249 + }
250 + }
251 +_detach:
252 + NtClose(hPipeExist);
253 + NtClose(hCommand);
254 + Util::unloadCurrentModule(); // jichi: this is not always needed
255 + return 0;
256 +}
257 +//extern "C" {
258 +void ConsoleOutput(LPCSTR text)
259 +{ // jichi 12/25/2013: Rewrite the implementation
260 + if (!live || !text)
261 + return;
262 + enum { buf_size = 0x50 };
263 + BYTE buf[buf_size]; // buffer is needed to append the message header
264 + size_t text_size = strlen(text) + 1;
265 + size_t data_size = text_size + 8;
266 +
267 + BYTE *data = (data_size <= buf_size) ? buf : new BYTE[data_size];
268 + *(DWORD *)data = HOST_NOTIFICATION; //cmd
269 + *(DWORD *)(data + 4) = HOST_NOTIFICATION_TEXT; //console
270 + memcpy(data + 8, text, text_size);
271 +
272 + IO_STATUS_BLOCK ios;
273 + NtWriteFile(hPipe, 0, 0, 0, &ios, data, data_size, 0, 0);
274 + if (data != buf)
275 + delete[] data;
276 +}
277 + //if (str) {
278 + // int t, len, sum;
279 + // BYTE buffer[0x80];
280 + // BYTE *buff;
281 + // len = wcslen(str) << 1;
282 + // t = swprintf((LPWSTR)(buffer + 8),L"%d: ",current_process_id) << 1;
283 + // sum = len + t + 8;
284 + // if (sum > 0x80) {
285 + // buff = new BYTE[sum];
286 + // memset(buff, 0, sum); // jichi 9/25/2013: zero memory
287 + // memcpy(buff + 8, buffer + 8, t);
288 + // }
289 + // else
290 + // buff = buffer;
291 + // *(DWORD *)buff = HOST_NOTIFICATION; //cmd
292 + // *(DWORD *)(buff + 4) = HOST_NOTIFICATION_TEXT; //console
293 + // memcpy(buff + t + 8, str, len);
294 + // IO_STATUS_BLOCK ios;
295 + // NtWriteFile(hPipe,0,0,0,&ios,buff,sum,0,0);
296 + // if (buff != buffer)
297 + // delete[] buff;
298 + // return len;
299 + //}
300 +
301 +//DWORD IOutputDWORD(DWORD d)
302 +//{
303 +// WCHAR str[0x10];
304 +// swprintf(str,L"%.8X",d);
305 +// ConsoleOutput(str);
306 +// return 0;
307 +//}
308 +//DWORD IOutputRegister(DWORD *base)
309 +//{
310 +// WCHAR str[0x40];
311 +// swprintf(str,L"EAX:%.8X",base[0]);
312 +// ConsoleOutput(str);
313 +// swprintf(str,L"ECX:%.8X",base[-1]);
314 +// ConsoleOutput(str);
315 +// swprintf(str,L"EDX:%.8X",base[-2]);
316 +// ConsoleOutput(str);
317 +// swprintf(str,L"EBX:%.8X",base[-3]);
318 +// ConsoleOutput(str);
319 +// swprintf(str,L"ESP:%.8X",base[-4]);
320 +// ConsoleOutput(str);
321 +// swprintf(str,L"EBP:%.8X",base[-5]);
322 +// ConsoleOutput(str);
323 +// swprintf(str,L"ESI:%.8X",base[-6]);
324 +// ConsoleOutput(str);
325 +// swprintf(str,L"EDI:%.8X",base[-7]);
326 +// ConsoleOutput(str);
327 +// return 0;
328 +//}
329 +//DWORD IRegisterEngineModule(DWORD idEngine, DWORD dnHook)
330 +//{
331 +// ::IdentifyEngine = (IdentifyEngineFun)idEngine;
332 +// ::InsertDynamicHook = (InsertDynamicHookFun)dnHook;
333 +// ::engine_registered = true;
334 +// return 0;
335 +//}
336 +DWORD NotifyHookInsert(DWORD addr)
337 +{
338 + if (live) {
339 + BYTE buffer[0x10];
340 + *(DWORD *)buffer = HOST_NOTIFICATION;
341 + *(DWORD *)(buffer + 4) = HOST_NOTIFICATION_NEWHOOK;
342 + *(DWORD *)(buffer + 8) = addr;
343 + *(DWORD *)(buffer + 0xc) = 0;
344 + IO_STATUS_BLOCK ios;
345 + CliLockPipe();
346 + NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0);
347 + CliUnlockPipe();
348 + }
349 + return 0;
350 +}
351 +//} // extern "C"
352 +
353 +// EOF
1 +#pragma once
2 +
3 +// avl.h
4 +// 8/23/2013 jichi
5 +// Branch: ITH/AVL.h, rev 133
6 +// 8/24/2013 TODO: Clean up this file
7 +#include <cstring>
8 +
9 +enum { STACK_SIZE = 32 };
10 +
11 +//#ifndef ITH_STACK
12 +//#define ITH_STACK
13 +
14 +template<class T, int stack_size>
15 +class MyStack
16 +{
17 + int index;
18 + T s[stack_size];
19 +
20 +public:
21 + MyStack(): index(0)
22 + { ::memset(s, 0, sizeof(s)); } // jichi 9/21/2013: assume T is atomic type
23 +
24 + T &back() { return s[index-1]; }
25 + int size() { return index; }
26 +
27 + void push_back(const T &e)
28 + {
29 + if (index < stack_size)
30 + s[index++]=e;
31 + }
32 +
33 + void pop_back() { index--; }
34 +
35 + T &operator[](int i) { return s[i]; }
36 +};
37 +//#endif // ITH_STACK
38 +
39 +// jichi 9/22/2013: T must be a pointer type which can be deleted
40 +template <class T, class D>
41 +struct TreeNode
42 +{
43 + //typedef TreeNode<T, D> Self;
44 + TreeNode() :
45 + Left(nullptr), Right(nullptr), Parent(nullptr)
46 + , rank(1)
47 + , factor('\0'), reserve('\0')
48 + //, key()
49 + //, data()
50 + {
51 + ::memset(&key, 0, sizeof(key)); // jichi 9/26/2013: zero memory
52 + ::memset(&data, 0, sizeof(data)); // jichi 9/26/2013: zero memory
53 + }
54 +
55 + TreeNode(const T &k, const D &d) :
56 + Left(nullptr), Right(nullptr), Parent(nullptr)
57 + , rank(1)
58 + , factor('\0'), reserve('\0') // jichi 9/21/2013: zero reserve
59 + , key(k)
60 + , data(d)
61 + {}
62 +
63 + TreeNode *Successor()
64 + {
65 + TreeNode *Node,
66 + *ParentNode;
67 + Node = Right;
68 + if (!Node) {
69 + Node = this;
70 + for (;;) {
71 + ParentNode = Node->Parent;
72 + if (!ParentNode)
73 + return nullptr;
74 + if (ParentNode->Left == Node)
75 + break;
76 + Node = ParentNode;
77 + }
78 + return ParentNode;
79 + }
80 + else
81 + while (Node->Left)
82 + Node = Node->Left;
83 + return Node;
84 + }
85 + TreeNode *Predecessor()
86 + {
87 + TreeNode *Node,
88 + *ParentNode;
89 + Node = Left;
90 + if (!Node) {
91 + Node = this;
92 + for(;;) {
93 + ParentNode = Node->Parent;
94 + if (!ParentNode)
95 + return nullptr;
96 + if (ParentNode->Right == Node)
97 + break;
98 + Node = ParentNode;
99 + }
100 + return ParentNode;
101 + }
102 + else
103 + while (Node->Right)
104 + Node = Node->Right;
105 + return Node;
106 + }
107 + int height()
108 + {
109 + if (!this) // jichi 9/26/2013: what?!
110 + return 0;
111 + int l = Left->height(),
112 + r = Right->height(),
113 + f = factor;
114 + if (l - r + f != 0)
115 + __debugbreak();
116 + f = l > r ? l : r;
117 + return f + 1;
118 + }
119 + TreeNode *Left,
120 + *Right,
121 + *Parent;
122 + unsigned short rank;
123 + char factor,
124 + reserve;
125 + T key;
126 + D data;
127 +};
128 +
129 +template<class T,class D>
130 +struct NodePath
131 +{
132 + NodePath() { ::memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH
133 + NodePath(TreeNode<T,D> *n, int f): Node(n), fact(f) {}
134 + TreeNode<T,D> *Node;
135 + union { char factor; int fact; };
136 +};
137 +
138 +template <class T, class D, class fComp, class fCopy, class fLength>
139 +class AVLTree
140 +{
141 + fComp fCmp;
142 + fCopy fCpy;
143 + fLength fLen;
144 +
145 +protected:
146 + TreeNode<T*, D> head;
147 +
148 +public:
149 + // - Construction -
150 + AVLTree() {}
151 +
152 + virtual ~AVLTree() { DeleteAll(); }
153 +
154 + // - Properties -
155 +
156 + TreeNode<T*, D> *TreeRoot() const { return head.Left; }
157 +
158 + // - Actions -
159 +
160 + void DeleteAll()
161 + {
162 + while (head.Left)
163 + DeleteRoot();
164 + }
165 +
166 + TreeNode<T*, D> *Insert(const T *key, const D &data)
167 + {
168 + if (head.Left) {
169 + MyStack<TreeNode<T*, D> *,STACK_SIZE> path;
170 + TreeNode<T*,D> *DownNode, *ParentNode, *BalanceNode, *TryNode, *NewNode; //P,T,S,Q
171 + ParentNode = &head;
172 + path.push_back(ParentNode);
173 + char factor,f;
174 + BalanceNode = DownNode = head.Left;
175 + for (;;) { //The first part of AVL tree insert. Just do as binary tree insert routine and record some nodes.
176 + factor = fCmp(key,DownNode->key);
177 + if (factor == 0)
178 + return DownNode; //Duplicate key. Return and do nothing.
179 + TryNode = _FactorLink(DownNode, factor);
180 + if (factor == -1)
181 + path.push_back(DownNode);
182 + if (TryNode) { //DownNode has a child.
183 + if (TryNode->factor != 0) { //Keep track of unbalance node and its parent.
184 + ParentNode = DownNode;
185 + BalanceNode = TryNode;
186 + }
187 + DownNode = TryNode;
188 + }
189 + else
190 + break; //Finished binary tree search;
191 + }
192 + while (path.size()) {
193 + path.back()->rank++;
194 + path.pop_back();
195 + }
196 + size_t sz = fLen(key) + 1;
197 + T *new_key = new T[sz];
198 + ::memset(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
199 + fCpy(new_key, key);
200 + TryNode = new TreeNode<T*, D>(new_key, data);
201 + _FactorLink(DownNode, factor) = TryNode;
202 + TryNode->Parent = DownNode;
203 + NewNode = TryNode;
204 + //Finished binary tree insert. Next to do is to modify balance factors between
205 + //BalanceNode and the new node.
206 + TreeNode<T*, D> *ModifyNode;
207 + factor = fCmp(key, BalanceNode->key);
208 + //factor=key<BalanceNode->key ? factor=-1:1; //Determine the balance factor at BalanceNode.
209 + ModifyNode = DownNode = _FactorLink(BalanceNode,factor);
210 + //ModifyNode will be the 1st child.
211 + //DownNode will travel from here to the recent inserted node (TryNode).
212 + while (DownNode != TryNode) { //Check if we reach the bottom.
213 + f = fCmp(key,DownNode->key);
214 + //f=_FactorCompare(key,DownNode->key);
215 + DownNode->factor = f;
216 + DownNode = _FactorLink(DownNode, f);//Modify balance factor and travels down.
217 + }
218 + //Finshed modifying balance factor.
219 + //Next to do is check the tree if it's unbalance and recover balance.
220 + if (BalanceNode->factor == 0) { //Tree has grown higher.
221 + BalanceNode->factor = factor;
222 + _IncreaseHeight(); //Modify balance factor and increase the height.
223 + return NewNode;
224 + }
225 + if (BalanceNode->factor + factor == 0) { //Tree has gotten more balanced.
226 + BalanceNode->factor = 0; //Set balance factor to 0.
227 + return NewNode;
228 + }
229 + //Tree has gotten out of balance.
230 + if (ModifyNode->factor == factor) //A node and its child has same factor. Single rotation.
231 + DownNode = _SingleRotation(BalanceNode, ModifyNode, factor);
232 + else //A node and its child has converse factor. Double rotation.
233 + DownNode = _DoubleRotation(BalanceNode, ModifyNode, factor);
234 + //Finished the balancing work. Set child field to the root of the new child tree.
235 + if (BalanceNode == ParentNode->Left)
236 + ParentNode->Left = DownNode;
237 + else
238 + ParentNode->Right = DownNode;
239 + return NewNode;
240 + }
241 + else { //root null?
242 + size_t sz = fLen(key) + 1;
243 + T *new_key = new T[sz];
244 + ::memset(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
245 + fCpy(new_key, key);
246 + head.Left = new TreeNode<T *, D>(new_key, data);
247 + head.rank++;
248 + _IncreaseHeight();
249 + return head.Left;
250 + }
251 + }
252 + bool Delete(T *key)
253 + {
254 + NodePath<T*,D> PathNode;
255 + MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
256 + path.push_back(NodePath<T*,D>(&head,-1));
257 + TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
258 + TryNode=head.Left;
259 + char factor;
260 + for (;;) { //Search for the
261 + if (TryNode == 0)
262 + return false; //Not found.
263 + factor = fCmp(key, TryNode->key);
264 + if (factor == 0)
265 + break; //Key found, continue to delete.
266 + //factor = _FactorCompare( key, TryNode->key );
267 + path.push_back(NodePath<T*,D>(TryNode,factor));
268 + TryNode = _FactorLink(TryNode,factor); //Move to left.
269 + }
270 + SuccNode = TryNode->Right; //Find a successor.
271 + factor = 1;
272 + if (SuccNode == 0) {
273 + SuccNode = TryNode->Left;
274 + factor = -1;
275 + }
276 + path.push_back(NodePath<T*,D>(TryNode,factor));
277 + while (SuccNode) {
278 + path.push_back(NodePath<T*,D>(SuccNode, -factor));
279 + SuccNode = _FactorLink(SuccNode,-factor);
280 + }
281 + PathNode = path.back();
282 + delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
283 + TryNode->key = PathNode.Node->key; //Replace key and data field with the successor or predecessor.
284 + PathNode.Node->key = nullptr;
285 + TryNode->data = PathNode.Node->data;
286 + path.pop_back();
287 + _FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
288 + delete PathNode.Node; //Remove the successor from the tree and release memory.
289 + PathNode = path.back();
290 + for (int i=0; i<path.size(); i++)
291 + if (path[i].factor==-1)
292 + path[i].Node->rank--;
293 + for (;;) { //Rebalance the tree along the path back to the root.
294 + if (path.size()==1) {
295 + _DecreaseHeight();
296 + break;
297 + }
298 + BalanceNode = PathNode.Node;
299 + if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurve since subtree height stays.
300 + BalanceNode->factor=-PathNode.factor;
301 + break;
302 + }
303 + if (BalanceNode->factor == PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurve.
304 + BalanceNode->factor = 0;
305 + path.pop_back();
306 + PathNode = path.back();
307 + continue;
308 + }
309 + //Node get out of balance. Here raises 3 cases.
310 + ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
311 + if (ChildNode->factor == 0) { // New case different to insert operation.
312 + TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
313 + path.pop_back();
314 + PathNode = path.back();
315 + _FactorLink(PathNode.Node, PathNode.factor) = TryNode;
316 + break;
317 + }
318 + else {
319 + if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
320 + TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
321 + else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
322 + TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
323 + }
324 + path.pop_back(); //Recurse back along the path.
325 + PathNode = path.back();
326 + _FactorLink(PathNode.Node, PathNode.factor) = TryNode;
327 + }
328 + return true;
329 + }
330 +
331 + D &operator [](T *key)
332 + { return (Insert(key,D())->data); }
333 +
334 + TreeNode<T*,D> *Search(const T *key)
335 + {
336 + TreeNode<T*,D> *Find=head.Left;
337 + char k;
338 + while (Find != 0) {//&&Find->key!=key)
339 + k=fCmp(key, Find->key);
340 + if (k==0) break;
341 + Find = _FactorLink(Find, k);
342 + }
343 + return Find;
344 + }
345 +
346 + TreeNode<T*,D> *SearchIndex(unsigned int rank)
347 + {
348 + unsigned int r = head.rank;
349 + if (rank == -1)
350 + return 0;
351 + if (++rank>=r)
352 + return 0;
353 + TreeNode<T*,D> *n=&head;
354 + while (r!=rank) {
355 + if (rank>r) {
356 + n=n->Right;
357 + rank-=r;
358 + r=n->rank;
359 + } else {
360 + n=n->Left;
361 + r=n->rank;
362 + }
363 + }
364 + return n;
365 + }
366 +
367 + TreeNode<T*,D> *Begin()
368 + {
369 + TreeNode<T*,D> *Node = head.Left;
370 + if (Node)
371 + while (Node->Left) Node = Node->Left;
372 + return Node;
373 + }
374 +
375 + TreeNode<T*,D> *End()
376 + {
377 + TreeNode<T*,D> *Node=head.Left;
378 + if (Node)
379 + while (Node->Right) Node = Node->Right;
380 + return Node;
381 + }
382 + unsigned int Count() const { return head.rank - 1; }
383 +
384 + template <class Fn>
385 + Fn TraverseTree(Fn &f)
386 + { return TraverseTreeNode(head.Left,f); }
387 +
388 +protected:
389 + bool DeleteRoot()
390 + {
391 + NodePath<T*,D> PathNode;
392 + MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
393 + path.push_back(NodePath<T*,D>(&head,-1));
394 + TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
395 + TryNode=head.Left;
396 + char factor;
397 + SuccNode=TryNode->Right; //Find a successor.
398 + factor=1;
399 + if (SuccNode==0)
400 + {
401 + SuccNode=TryNode->Left;
402 + factor=-1;
403 + }
404 + path.push_back(NodePath<T*,D>(TryNode,factor));
405 + while (SuccNode) {
406 + path.push_back(NodePath<T*,D>(SuccNode,-factor));
407 + SuccNode=_FactorLink(SuccNode,-factor);
408 + }
409 + PathNode=path.back();
410 + delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
411 + TryNode->key=PathNode.Node->key; //Replace key and data field with the successor.
412 + PathNode.Node->key = nullptr;
413 + TryNode->data=PathNode.Node->data;
414 + path.pop_back();
415 + _FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
416 + delete PathNode.Node; //Remove the successor from the tree and release memory.
417 + PathNode=path.back();
418 + for (int i=0;i<path.size();i++)
419 + if (path[i].factor==-1)
420 + path[i].Node->rank--;
421 + for (;;) { //Rebalance the tree along the path back to the root.
422 + if (path.size() == 1) {
423 + _DecreaseHeight();
424 + break;
425 + }
426 +
427 + BalanceNode = PathNode.Node;
428 + if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurse since subtree height not changed.
429 + BalanceNode->factor=-PathNode.factor;
430 + break;
431 + }
432 + if (BalanceNode->factor==PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurse.
433 + BalanceNode->factor=0;
434 + path.pop_back();
435 + PathNode=path.back();
436 + continue;
437 + }
438 + //Node get out of balance. Here raises 3 cases.
439 + ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
440 + if (ChildNode->factor == 0) { // New case different to insert operation.
441 + TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
442 + path.pop_back();
443 + PathNode=path.back();
444 + _FactorLink(PathNode.Node, PathNode.factor) = TryNode;
445 + break;
446 + } else {
447 + if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
448 + TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
449 + else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
450 + TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
451 + }
452 + path.pop_back(); // Recurve back along the path.
453 + PathNode=path.back();
454 + _FactorLink(PathNode.Node, PathNode.factor) = TryNode;
455 + }
456 + return true;
457 + }
458 + template <class Fn>
459 + Fn TraverseTreeNode(TreeNode<T*,D> *Node, Fn &f)
460 + {
461 + if (Node) {
462 + if (Node->Left)
463 + TraverseTreeNode(Node->Left,f);
464 + f(Node);
465 + if (Node->Right)
466 + TraverseTreeNode(Node->Right,f);
467 + }
468 + return f;
469 + }
470 + TreeNode<T*,D> *_SingleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
471 + {
472 + TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
473 + _FactorLink(BalanceNode, factor) = Node;
474 + _FactorLink(ModifyNode, -factor) = BalanceNode;
475 + if (Node)
476 + Node->Parent = BalanceNode;
477 + ModifyNode->Parent = BalanceNode->Parent;
478 + BalanceNode->Parent = ModifyNode;
479 + BalanceNode->factor = ModifyNode->factor = 0; //After single rotation, set all factor of 3 node to 0.
480 + if (factor == 1)
481 + ModifyNode->rank += BalanceNode->rank;
482 + else
483 + BalanceNode->rank -= ModifyNode->rank;
484 + return ModifyNode;
485 + }
486 + TreeNode<T*,D> *_SingleRotation2(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
487 + {
488 + TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
489 + _FactorLink(BalanceNode, factor) = Node;
490 + _FactorLink(ModifyNode, -factor) = BalanceNode;
491 + if (Node) Node->Parent = BalanceNode;
492 + ModifyNode->Parent = BalanceNode->Parent;
493 + BalanceNode->Parent = ModifyNode;
494 + ModifyNode->factor = -factor;
495 + if (factor == 1)
496 + ModifyNode->rank+=BalanceNode->rank;
497 + else
498 + BalanceNode->rank-=ModifyNode->rank;
499 + return ModifyNode;
500 + }
501 + TreeNode<T*,D> *_DoubleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
502 + {
503 + TreeNode<T*,D> *DownNode = _FactorLink(ModifyNode, -factor);
504 + TreeNode<T*,D> *Node1, *Node2;
505 + Node1 = _FactorLink(DownNode, factor);
506 + Node2 = _FactorLink(DownNode, -factor);
507 + _FactorLink(ModifyNode, -factor) = Node1;
508 + _FactorLink(DownNode, factor) = ModifyNode;
509 + _FactorLink(BalanceNode, factor) = Node2;
510 + _FactorLink(DownNode, -factor) = BalanceNode;
511 + if (Node1)
512 + Node1->Parent = ModifyNode;
513 + if (Node2)
514 + Node2->Parent = BalanceNode;
515 + DownNode->Parent = BalanceNode->Parent;
516 + BalanceNode->Parent = DownNode;
517 + ModifyNode->Parent = DownNode;
518 + //Set factor according to the result.
519 + if (DownNode->factor == factor) {
520 + BalanceNode->factor = -factor;
521 + ModifyNode->factor = 0;
522 + } else if (DownNode->factor == 0)
523 + BalanceNode->factor = ModifyNode->factor = 0;
524 + else {
525 + BalanceNode->factor = 0;
526 + ModifyNode->factor = factor;
527 + }
528 + DownNode->factor = 0;
529 + if (factor==1) {
530 + ModifyNode->rank -= DownNode->rank;
531 + DownNode->rank += BalanceNode->rank;
532 + } else {
533 + DownNode->rank += ModifyNode->rank;
534 + BalanceNode->rank -= DownNode->rank;
535 + }
536 + return DownNode;
537 + }
538 +
539 + TreeNode<T*,D>* &__fastcall _FactorLink(TreeNode<T*,D> *Node, char factor)
540 + //Private helper method to retrieve child according to factor.
541 + //Return right child if factor>0 and left child otherwise.
542 + { return factor>0? Node->Right : Node->Left; }
543 +
544 + void Check()
545 + {
546 + unsigned int k = (unsigned int)head.Right;
547 + unsigned int t = head.Left->height();
548 + if (k != t)
549 + __debugbreak();
550 + }
551 +
552 + void _IncreaseHeight()
553 + {
554 + unsigned int k = (unsigned int)head.Right;
555 + head.Right = (TreeNode<T*,D>*)++k;
556 + }
557 +
558 + void _DecreaseHeight()
559 + {
560 + unsigned int k = (unsigned int)head.Right;
561 + head.Right = (TreeNode<T*,D>*)--k;
562 + }
563 +};
564 +
565 +struct SCMP
566 +{
567 + char operator()(const char *s1,const char *s2)
568 + {
569 + int t = _stricmp(s1, s2);
570 + return t == 0 ? 0 : t > 0 ? 1 :-1;
571 + }
572 +};
573 +
574 +struct SCPY { char *operator()(char *dest, const char *src) { return strcpy(dest, src); } };
575 +struct SLEN { int operator()(const char *str) { return strlen(str); } };
576 +
577 +struct WCMP
578 +{
579 + char operator()(const wchar_t *s1,const wchar_t *s2)
580 + {
581 + int t =_wcsicmp(s1, s2);
582 + return t == 0 ? 0 : t > 0 ? 1 : -1;
583 + }
584 +};
585 +
586 +struct WCPY { wchar_t *operator()(wchar_t *dest, const wchar_t *src) { return wcscpy(dest,src); } };
587 +struct WLEN { int operator()(const wchar_t *str) { return wcslen(str); } };
588 +
589 +// EOF
1 +#pragma once
2 +
3 +// growl.h
4 +// 9/17/2013 jichi
5 +
6 +//#ifdef GROWL_HAS_GROWL
7 +
8 +#include <windows.h>
9 +#include <cstdio>
10 +
11 +#define GROWL_MSG_A(_msg) MessageBoxA(nullptr, _msg, "VNR Message", MB_OK)
12 +#define GROWL_MSG(_msg) MessageBoxW(nullptr, _msg, L"VNR Message", MB_OK)
13 +#define GROWL_WARN(_msg) MessageBoxW(nullptr, _msg, L"VNR Warning", MB_OK)
14 +#define GROWL_ERROR(_msg) MessageBoxW(nullptr, _msg, L"VNR Error", MB_OK)
15 +
16 +inline void GROWL_DWORD(DWORD value)
17 +{
18 + WCHAR buf[100];
19 + swprintf(buf, L"DWORD: %x", value);
20 + GROWL_MSG(buf);
21 +}
22 +
23 +inline void GROWL_DWORD2(DWORD v, DWORD v2)
24 +{
25 + WCHAR buf[100];
26 + swprintf(buf, L"DWORD2: %x,%x", v, v2);
27 + GROWL_MSG(buf);
28 +}
29 +
30 +inline void 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 + GROWL_MSG(buf);
35 +}
36 +
37 +inline void 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 + GROWL_MSG(buf);
42 +}
43 +
44 +inline void 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 + GROWL_MSG(buf);
49 +}
50 +
51 +inline void 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 + GROWL_MSG(buf);
56 +}
57 +
58 +inline void 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 + GROWL_MSG(buf);
63 +}
64 +
65 +inline void 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 + GROWL_MSG(buf);
70 +}
71 +
72 +inline void 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 + GROWL_MSG(buf);
77 +}
78 +
79 +inline void GROWL(DWORD v) { GROWL_DWORD(v); }
80 +inline void GROWL(LPCWSTR v) { GROWL_MSG(v); }
81 +inline void GROWL(LPCSTR v) { GROWL_MSG_A(v); }
82 +
83 +//#endif // GROWL_HAS_GROWL
84 +
85 +// 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 "src/util/util.h"
7 +#include "ithsys/ithsys.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 +EXTERN_C IMAGE_DOS_HEADER __ImageBase;
306 +// See: http://stackoverflow.com/questions/3410130/dll-unloading-itself
307 +bool Util::unloadCurrentModule()
308 +{
309 + auto fun = ::FreeLibrary;
310 + //auto fun = ::LdrUnloadDll;
311 + if (HANDLE h = ::IthCreateThread(fun, (DWORD)&__ImageBase)) {
312 + //const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
313 + //NtWaitForSingleObject(h, 0, (PLARGE_INTEGER)&timeout);
314 + NtClose(h);
315 + return true;
316 + }
317 +
318 + // CreateThread does not always work on Windows XP. Use IthCreateThread (i.e. CreateRemoteThread under the water) instead.
319 + //if (HANDLE h = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)fun, &__ImageBase, 0, NULL)) {
320 + // ::CloseHandle(h);
321 + // return true;
322 + //}
323 + return false;
324 +}
325 +
326 +// EOF
1 +#pragma once
2 +
3 +// util.h
4 +// 8/23/2013 jichi
5 +
6 +#include "ntdll/ntdll.h"
7 +
8 +namespace Util {
9 +
10 +bool unloadCurrentModule();
11 +
12 +DWORD GetCodeRange(DWORD hModule,DWORD *low, DWORD *high);
13 +DWORD FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig);
14 +DWORD FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp);
15 +DWORD FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp);
16 +DWORD FindCallBoth(DWORD fun, DWORD size, DWORD pt);
17 +DWORD FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig);
18 +DWORD FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig);
19 +DWORD FindEntryAligned(DWORD start, DWORD back_range);
20 +DWORD FindImportEntry(DWORD hModule, DWORD fun);
21 +
22 +// jichi 4/15/2014: Copied from ITH CLI, for debugging purpose
23 +DWORD FindModuleBase(DWORD hash);
24 +
25 +bool SearchResourceString(LPCWSTR str);
26 +
27 +/**
28 + * @param name process name without path deliminator
29 + */
30 +inline void GetProcessName(wchar_t *name)
31 +{
32 + //assert(name);
33 + PLDR_DATA_TABLE_ENTRY it;
34 + __asm
35 + {
36 + mov eax,fs:[0x30]
37 + mov eax,[eax+0xc]
38 + mov eax,[eax+0xc]
39 + mov it,eax
40 + }
41 + ::wcscpy(name, it->BaseDllName.Buffer);
42 +}
43 +
44 +/**
45 + * @param path with process name and directy name
46 + */
47 +inline void GetProcessPath(wchar_t *path)
48 +{
49 + //assert(path);
50 + PLDR_DATA_TABLE_ENTRY it;
51 + __asm
52 + {
53 + mov eax,fs:[0x30]
54 + mov eax,[eax+0xc]
55 + mov eax,[eax+0xc]
56 + mov it,eax
57 + }
58 + ::wcscpy(path, it->FullDllName.Buffer);
59 +}
60 +
61 +/**
62 + * @return HANDLE module handle
63 + */
64 +inline DWORD GetModuleBase()
65 +{
66 + __asm
67 + {
68 + mov eax,fs:[0x18]
69 + mov eax,[eax+0x30]
70 + mov eax,[eax+0xc]
71 + mov eax,[eax+0xc]
72 + mov eax,[eax+0x18]
73 + }
74 +}
75 +
76 +} // namespace Util
77 +
78 +// EOF
1 +# vnrhook.pri
2 +# 8/21/2013 jichi
3 +
4 +DEFINES += WITH_LIB_VNRHOOK
5 +DEPENDPATH += $$PWD/include
6 +
7 +HEADERS += \
8 + $$PWD/include/const.h \
9 + $$PWD/include/defs.h \
10 + $$PWD/include/types.h
11 +
12 +# EOF
1 +# hook.pro
2 +# 8/9/2013 jichi
3 +# Build vnrhook.dll for Windows 7+
4 +
5 +# Exception handler to catch all exceptions
6 +CONFIG += dll noqt eh eha # noeh nosafeseh
7 +
8 +#CONFIG += noeh # msvcrt on Windows XP does not has exception handler
9 +include(../../../config.pri)
10 +include($$PLUGINDIR/ithsys/ithsys.pri)
11 +include($$LIBDIR/disasm/disasm.pri)
12 +include($$LIBDIR/memdbg/memdbg.pri)
13 +include($$LIBDIR/ntdll/ntdll.pri)
14 +include($$LIBDIR/ntinspect/ntinspect.pri)
15 +include($$LIBDIR/winkey/winkey.pri)
16 +#include($$LIBDIR/winseh/winseh_safe.pri)
17 +include($$LIBDIR/winversion/winversion.pri)
18 +
19 +# 9/27/2013: disable ITH this game engine, only for debugging purpose
20 +#DEFINES += ITH_DISABLE_ENGINE
21 +
22 +# jichi 9/22/2013: When ITH is on wine, mutex is needed to protect NtWriteFile
23 +#DEFINES += ITH_WINE
24 +#DEFINES += ITH_SYNC_PIPE
25 +
26 +DEFINES += ITH_HAS_CRT ITH_HAS_SEH
27 +DEFINES += MEMDBG_NO_STL NTINSPECT_NO_STL # disabled as not used
28 +
29 +# jichi 11/24/2013: Disable manual heap
30 +DEFINES -= ITH_HAS_HEAP
31 +
32 +# jichi 11/13/2011: disable swprinf warning
33 +DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
34 +
35 +## Libraries
36 +
37 +#LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
38 +#LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
39 +
40 +LIBS += -lkernel32 -luser32 -lgdi32 #-lgdiplus
41 +
42 +## Sources
43 +
44 +TEMPLATE = lib
45 +TARGET = vnrhook
46 +
47 +#CONFIG += staticlib
48 +
49 +HEADERS += \
50 + include/const.h \
51 + include/defs.h \
52 + include/types.h \
53 + src/except.h \
54 + src/main.h \
55 + src/util/growl.h \
56 + src/util/util.h \
57 + src/tree/avl.h \
58 + src/hijack/texthook.h \
59 + src/engine/engine.h \
60 + src/engine/hookdefs.h \
61 + src/engine/match.h \
62 + src/engine/pchooks.h \
63 + src/engine/mono/funcinfo.h \
64 + src/engine/ppsspp/funcinfo.h
65 +
66 +SOURCES += \
67 + src/main.cc \
68 + src/pipe.cc \
69 + src/util/util.cc \
70 + src/hijack/texthook.cc \
71 + src/engine/engine.cc \
72 + src/engine/match.cc \
73 + src/engine/pchooks.cc
74 +
75 +#RC_FILE += vnrhook.rc
76 +#OTHER_FILES += vnrhook.rc
77 +
78 +OTHER_FILES += vnrhook.pri
79 +
80 +# EOF
1 +// hijack.cc
2 +// 1/27/2013 jichi
3 +#include "windbg/hijack.h"
4 +#include "windbg/windbg_p.h"
5 +
6 +#ifdef _MSC_VER
7 +# pragma warning (disable:4996) // C4996: use POSIX function (stricmp)
8 +#endif // _MSC_VER
9 +
10 +//#define DEBUG "winsec"
11 +#include "sakurakit/skdebug.h"
12 +
13 +WINDBG_BEGIN_NAMESPACE
14 +
15 +// - Inline Hook -
16 +// See: http://asdf.wkeya.com/code/apihook6.html
17 +PVOID overrideFunctionA(HMODULE stealFrom, LPCSTR oldFunctionModule, LPCSTR functionName, LPCVOID newFunction)
18 +{
19 + if (!stealFrom)
20 + return nullptr;
21 + //HMODULE oldModule = GetModuleHandleA(oldFunctionModule);
22 + //if (!oldModule)
23 + // return nullptr;
24 + //void *originalAddress = GetProcAddress(oldModule, functionName);
25 + LPVOID originalAddress = details::getModuleFunctionAddressA(functionName, oldFunctionModule);
26 + if (!originalAddress)
27 + return nullptr;
28 + IMAGE_DOS_HEADER *dosHeader = reinterpret_cast<IMAGE_DOS_HEADER *>(stealFrom);
29 + char *base = reinterpret_cast<char *>(stealFrom);
30 + if (::IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER)) || dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
31 + return nullptr;
32 + IMAGE_NT_HEADERS *ntHeader =
33 + reinterpret_cast<IMAGE_NT_HEADERS* >(base + dosHeader->e_lfanew);
34 + if (::IsBadReadPtr(ntHeader, sizeof(IMAGE_NT_HEADERS)) || ntHeader->Signature != IMAGE_NT_SIGNATURE)
35 + return nullptr;
36 + if (!ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
37 + return nullptr;
38 + // See: http://msdn.microsoft.com/en-us/magazine/cc301808.aspx
39 + IMAGE_IMPORT_DESCRIPTOR *import =
40 + reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR *>(base + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
41 +
42 + // scan memory
43 + // TODO: add a maximum loop counter here!
44 + while (import->Name) {
45 + char *name = base + import->Name;
46 + if (!::stricmp(name, oldFunctionModule))
47 + break;
48 + import++;
49 + }
50 + if (!import->Name)
51 + return nullptr;
52 + IMAGE_THUNK_DATA *thunk = reinterpret_cast<IMAGE_THUNK_DATA *>(base + import->FirstThunk);
53 + while (thunk->u1.Function) {
54 + if ((ULONG_PTR)thunk->u1.Function == (ULONG_PTR)originalAddress) {
55 + ULONG_PTR *addr = reinterpret_cast<ULONG_PTR *>(&thunk->u1.Function);
56 +
57 + // See: http://asdf.wkeya.com/code/apihook6.html
58 + // Inline hook mechanism:
59 + //
60 + // LPVOID InlineHook3( PUINT8 mem, DWORD dwLen, PUINT8 pfOld, PUINT8 pfNew )
61 + // {
62 + // DWORD dwOldProtect;
63 + // VirtualProtect( ( PUINT8 )( pfOld ), dwLen, PAGE_READWRITE, &dwOldProtect );
64 + // // 関数のエントリーから指定したbyte数をメモリの前方にコピー
65 + // // メモリの数byte後方からオリジナルへのジャンプを作成
66 + // // 指定の関数アドレスから5byteをフックへのjmp命令に書き換え
67 + // VirtualProtect( ( PUINT8 )( pfOld ), dwLen, dwOldProtect, &dwOldProtect );
68 + // return ( PVOID )mem;
69 + // }
70 +
71 + MEMORY_BASIC_INFORMATION mbi;
72 + if (::VirtualQuery((LPVOID)addr, &mbi, sizeof(mbi)) == sizeof(mbi)) {
73 + DWORD dwOldProtect;
74 + if (::VirtualProtect(mbi.BaseAddress, ((ULONG_PTR)addr + 8)-(ULONG_PTR)mbi.BaseAddress, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
75 + *addr = (ULONG_PTR)newFunction;
76 + ::VirtualProtect(mbi.BaseAddress, ((ULONG_PTR)addr + 8)-(ULONG_PTR)mbi.BaseAddress, dwOldProtect, &dwOldProtect);
77 + return originalAddress;
78 + }
79 + }
80 +
81 + }
82 + thunk++;
83 + }
84 + return nullptr;
85 +}
86 +
87 +WINDBG_END_NAMESPACE
88 +
89 +// EOF
1 +#pragma once
2 +
3 +// hijack.h
4 +// 1/27/2013 jichi
5 +
6 +#include "windbg/windbg.h"
7 +#include <windows.h>
8 +
9 +WINDBG_BEGIN_NAMESPACE
10 +
11 +/**
12 + * Replace the named function entry with the new one.
13 + * @param stealFrom instance of target module
14 + * @param oldFunctionModule TODO
15 + * @param functionName name of the target function
16 + * @return the orignal address if succeed, else nullptr
17 + *
18 + * See: http://www.codeproject.com/KB/DLL/DLL_Injection_tutorial.aspx
19 + */
20 +PVOID overrideFunctionA(_In_ HMODULE stealFrom, _In_ LPCSTR oldFunctionModule,
21 + _In_ LPCSTR functionName, _In_ LPCVOID newFunction);
22 +
23 +WINDBG_END_NAMESPACE
24 +
25 +// EOF
1 +// inject.cc
2 +// 1/27/2013 jichi
3 +#include "windbg/inject.h"
4 +#include "windbg/windbg_p.h"
5 +#include <cwchar> // for wcslen
6 +
7 +//#define DEBUG "windbg::inject"
8 +#include "sakurakit/skdebug.h"
9 +
10 +WINDBG_BEGIN_NAMESPACE
11 +
12 +// - Remote Injection -
13 +
14 +BOOL InjectFunction1(LPCVOID addr, LPCVOID data, SIZE_T dataSize, DWORD pid, HANDLE hProcess, INT timeout)
15 +{
16 + DOUT("enter: pid =" << pid);
17 + if (hProcess == INVALID_HANDLE_VALUE && pid) {
18 + hProcess = ::OpenProcess(PROCESS_INJECT_ACCESS, FALSE, pid);
19 + // TODO: Privilege elevation is not implemented. See: skwinsec.py.
20 + //if (!hProcess) {
21 + // priv = SkProcessElevator('SeDebugPrivilege')
22 + // if not priv.isEmpty():
23 + // handle = win32api.OpenProcess(PROCESS_INJECT_ACCESS, 0, pid)
24 + //}
25 + }
26 + if (hProcess == INVALID_HANDLE_VALUE) {
27 + DOUT("exit: error: failed to get process handle");
28 + return FALSE;
29 + }
30 +
31 + BOOL ret = FALSE;
32 + if (LPVOID remoteData = ::VirtualAllocEx(hProcess, nullptr, dataSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE)) {
33 + if (::WriteProcessMemory(hProcess, remoteData, data, dataSize, nullptr))
34 + if (HANDLE hThread = ::CreateRemoteThread(
35 + hProcess,
36 + nullptr, 0,
37 + reinterpret_cast<LPTHREAD_START_ROUTINE>(addr),
38 + remoteData,
39 + 0, nullptr)) {
40 + ::WaitForSingleObject(hThread, timeout);
41 + ::CloseHandle(hThread);
42 + ret = TRUE;
43 + }
44 + ::VirtualFreeEx(hProcess, remoteData, dataSize, MEM_RELEASE);
45 + }
46 + ::CloseHandle(hProcess);
47 + DOUT("exit: ret =" << ret);
48 + return ret;
49 +}
50 +
51 +BOOL injectDllW(LPCWSTR dllPath, DWORD pid, HANDLE hProcess, INT timeout)
52 +{
53 + DOUT("enter: pid =" << pid);
54 + LPCVOID fun = details::getModuleFunctionAddressA("LoadLibraryW", "kernel32.dll");
55 + if (!fun) {
56 + DOUT("exit error: cannot find function");
57 + return FALSE;
58 + }
59 + LPCVOID data = dllPath;
60 + SIZE_T dataSize = ::wcslen(dllPath) * 2 + 2; // L'\0'
61 + BOOL ok = InjectFunction1(fun, data, dataSize, pid, hProcess, timeout);
62 + DOUT("exit: ret =" << ok);
63 + return ok;
64 +}
65 +
66 +BOOL ejectDll(HANDLE hDll, DWORD pid, HANDLE hProcess, INT timeout)
67 +{
68 + DOUT("enter: pid =" << pid);
69 + LPCVOID fun = details::getModuleFunctionAddressA("FreeLibrary", "kernel32.dll");
70 + if (!fun) {
71 + DOUT("exit error: cannot find function");
72 + return FALSE;
73 + }
74 + LPCVOID data = &hDll;
75 + SIZE_T dataSize = sizeof(hDll);
76 + BOOL ok = InjectFunction1(fun, data, dataSize, pid, hProcess, timeout);
77 + DOUT("exit: ret =" << ok);
78 + return ok;
79 +}
80 +
81 +WINDBG_END_NAMESPACE
82 +
83 +// EOF
84 +
85 +/*
86 +enum { CREATE_THREAD_ACCESS = (PROCESS_CREATE_THREAD |
87 + PROCESS_QUERY_INFORMATION |
88 + PROCESS_VM_OPERATION |
89 + PROCESS_VM_WRITE |
90 + PROCESS_VM_READ ) };
91 +
92 +
93 +int InjectDll(HANDLE hProcess, HINSTANCE hInst) {
94 + HANDLE hThread;
95 +
96 + wchar_t dllFile[2*MAX_PATH];
97 + if (GetModuleFileNameW(hInst, dllFile, sizeof(dllFile)/2) > sizeof(dllFile)/2) return 0;
98 +
99 + void *loadLibraryW = GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW");
100 + if (!loadLibraryW) return 0;
101 +
102 + wchar_t *name;
103 + if (!(name = wcsrchr(dllFile, '\\'))) return 0;
104 + name ++;
105 + wcscpy(name, DLL_NAME);
106 + if (GetFileAttributes(dllFile) == INVALID_FILE_ATTRIBUTES) return 0;
107 +
108 + size_t len = sizeof(wchar_t)*(1+wcslen(dllFile));
109 + void *remoteString = (LPVOID)VirtualAllocEx(hProcess,
110 + NULL,
111 + len,
112 + MEM_RESERVE|MEM_COMMIT,
113 + PAGE_READWRITE
114 + );
115 + if (remoteString) {
116 + if (WriteProcessMemory(hProcess, (LPVOID)remoteString, dllFile, len, NULL)) {
117 + if (hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)loadLibraryW, (LPVOID)remoteString, 0,0)) {
118 + WaitForSingleObject(hThread, 3000);
119 + CloseHandle(hThread);
120 + VirtualFreeEx(hProcess, remoteString, len, MEM_FREE);
121 + // Make sure it's injected before resuming.
122 + return 1;
123 + }
124 + }
125 + VirtualFreeEx(hProcess, remoteString, len, MEM_FREE);
126 + }
127 + return 0;
128 +}
129 +
130 +int getPriv(const char * name) {
131 + HANDLE hToken;
132 + LUID seValue;
133 + TOKEN_PRIVILEGES tkp;
134 +
135 + if (!LookupPrivilegeValueA(NULL, name, &seValue) ||
136 + !OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
137 + return 0;
138 + }
139 +
140 + tkp.PrivilegeCount = 1;
141 + tkp.Privileges[0].Luid = seValue;
142 + tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
143 +
144 + int res = AdjustTokenPrivileges(hToken, 0, &tkp, sizeof(tkp), NULL, NULL);
145 +
146 + CloseHandle(hToken);
147 + return res;
148 +}
149 +
150 +inline int getDebugPriv() {
151 + return getPriv("SeDebugPrivilege");
152 +}
153 +
154 +int InjectIntoProcess(int pid) {
155 + HANDLE hProcess = OpenProcess(CREATE_THREAD_ACCESS, 0, pid);
156 + if (hProcess == 0) {
157 + getDebugPriv();
158 + hProcess = OpenProcess(CREATE_THREAD_ACCESS, 0, pid);
159 + if (!hProcess) return 0;
160 + }
161 +
162 + int out = InjectDll(hProcess);
163 +
164 + CloseHandle(hProcess);
165 + return out;
166 +}
167 +*/
1 +#pragma once
2 +
3 +// inject.h
4 +// 1/27/2013 jichi
5 +
6 +#include "windbg/windbg.h"
7 +
8 +#include <windows.h>
9 +
10 +WINDBG_BEGIN_NAMESPACE
11 +
12 +enum { PROCESS_INJECT_ACCESS = PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ };
13 +enum { INJECT_TIMEOUT = 3000 }; // wait at most 3 seconds for creating remote threads
14 +
15 +/**
16 + * Inject function with 1 argument
17 + * Either pid or the process handle should be specified
18 + * @param addr LONG function memory address
19 + * @param arg LPVOID arg1 data
20 + * @param argSize int arg1 data size
21 + * @param pid process id
22 + * @param hProcess process handle
23 + * @param timeout msec
24 + * @return BOOL
25 + */
26 +BOOL injectFunction1(_In_ LPCVOID addr, _In_ LPCVOID arg, _In_ SIZE_T argSize,
27 + _In_ DWORD pid = 0, _In_ HANDLE hProcess = INVALID_HANDLE_VALUE,
28 + _In_ INT timeout = INJECT_TIMEOUT);
29 +
30 +/**
31 + * Either pid or the process handle should be specified
32 + * @param dllpath ABSOLUTE path to dll
33 + * @param pid process id
34 + * @param hProcess process handle
35 + * @param timeout msec
36 + * @return BOOL
37 + */
38 +BOOL injectDllW(_In_ LPCWSTR dllPath,
39 + _In_ DWORD pid = 0, _In_ HANDLE hProcess = INVALID_HANDLE_VALUE,
40 + _In_ INT timeout = INJECT_TIMEOUT);
41 +
42 +/**
43 + * Either pid or the process handle should be specified
44 + * @param hDll dll module handle
45 + * @param pid process id
46 + * @param hProcess process handle
47 + * @param timeout msec
48 + * @return BOOL
49 + */
50 +BOOL ejectDll(_In_ HANDLE hDll,
51 + _In_ DWORD pid = 0, _In_ HANDLE hProcess = INVALID_HANDLE_VALUE,
52 + _In_ INT timeout = INJECT_TIMEOUT);
53 +
54 +WINDBG_END_NAMESPACE
55 +
56 +// EOF
1 +// unload.cc
2 +// 5/2/2014 jichi
3 +#include "windbg/unload.h"
4 +
5 +WINDBG_BEGIN_NAMESPACE
6 +
7 +EXTERN_C IMAGE_DOS_HEADER __ImageBase;
8 +// See: http://stackoverflow.com/questions/3410130/dll-unloading-itself
9 +BOOL unloadCurrentModule()
10 +{
11 + auto fun = ::FreeLibrary;
12 + //auto fun = ::LdrUnloadDll;
13 + if (HANDLE h = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)fun, &__ImageBase, 0, NULL)) {
14 + ::CloseHandle(h);
15 + return TRUE;
16 + }
17 + return FALSE;
18 +}
19 +
20 +WINDBG_END_NAMESPACE
21 +
22 +// EOF
1 +#pragma once
2 +
3 +// unload.h
4 +// 5/2/2014 jichi
5 +
6 +#include "windbg/windbg.h"
7 +#include <windows.h>
8 +
9 +WINDBG_BEGIN_NAMESPACE
10 +
11 +/**
12 + * Unload current injected DLL.
13 + * @return BOOL
14 + */
15 +BOOL unloadCurrentModule();
16 +
17 +WINDBG_END_NAMESPACE
18 +
19 +// EOF
1 +// windbg/util.cc
2 +// 1/27/2013 jichi
3 +#include "windbg/util.h"
4 +#include <tlhelp32.h>
5 +#include <boost/foreach.hpp>
6 +#include <list>
7 +
8 +WINDBG_BEGIN_NAMESPACE
9 +
10 +class ThreadsSuspenderPrivate
11 +{
12 +public:
13 + std::list<HANDLE> threads;
14 +};
15 +
16 +ThreadsSuspender::ThreadsSuspender(bool autoSuspend)
17 + : d_(new D)
18 +{ if (autoSuspend) suspend(); }
19 +
20 +ThreadsSuspender::~ThreadsSuspender()
21 +{
22 + resume();
23 + delete d_;
24 +}
25 +
26 +void ThreadsSuspender::suspend()
27 +{
28 + HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
29 + if (hSnap == INVALID_HANDLE_VALUE)
30 + return;
31 + THREADENTRY32 entry;
32 + entry.dwSize = sizeof(entry);
33 + DWORD pid = ::GetCurrentProcessId();
34 + DWORD tid = ::GetCurrentThreadId();
35 + if (::Thread32First(hSnap, &entry))
36 + do if (entry.dwSize >= 4 * sizeof(DWORD) && entry.th32OwnerProcessID == pid && entry.th32ThreadID != tid) {
37 + if (HANDLE hThread = ::OpenThread(THREAD_SUSPEND_RESUME, 0, entry.th32ThreadID)) {
38 + if (::SuspendThread(hThread) != DWORD(-1))
39 + d_->threads.push_back(hThread);
40 + else
41 + ::CloseHandle(hThread);
42 + }
43 + entry.dwSize = sizeof(entry);
44 + } while (::Thread32Next(hSnap, &entry));
45 + ::CloseHandle(hSnap);
46 +}
47 +
48 +void ThreadsSuspender::resume()
49 +{
50 + if (!d_->threads.empty()) {
51 + BOOST_FOREACH (HANDLE hThread, d_->threads) {
52 + ::ResumeThread(hThread);
53 + ::CloseHandle(hThread);
54 + }
55 + d_->threads.clear();
56 + }
57 +}
58 +
59 +WINDBG_END_NAMESPACE
60 +
61 +// EOF
1 +#pragma once
2 +
3 +// windbg/util.h
4 +// 1/27/2013 jichi
5 +
6 +#include "windbg/windbg.h"
7 +#include "sakurakit/skglobal.h"
8 +
9 +#include <windows.h>
10 +
11 +WINDBG_BEGIN_NAMESPACE
12 +
13 +class ThreadsSuspenderPrivate;
14 +/**
15 + * When created, automatically suspends all threads in the current process.
16 + * When destroyed, resume suspended threads.
17 + */
18 +class ThreadsSuspender
19 +{
20 + SK_CLASS(ThreadsSuspender)
21 + SK_DISABLE_COPY(ThreadsSuspender)
22 + SK_DECLARE_PRIVATE(ThreadsSuspenderPrivate)
23 +
24 +public:
25 + explicit ThreadsSuspender(bool autoSuspend = true);
26 + ~ThreadsSuspender();
27 +
28 + void resume(); ///< Manually resume all threads
29 + void suspend(); ///< Manually suspend all threads
30 +};
31 +
32 +WINDBG_END_NAMESPACE
33 +
34 +// EOF
1 +#pragma once
2 +
3 +// windbg.h
4 +// 1/27/2013 jichi
5 +
6 +#ifndef WINDBG_BEGIN_NAMESPACE
7 +# define WINDBG_BEGIN_NAMESPACE namespace WinDbg {
8 +#endif
9 +#ifndef WINDBG_END_NAMESPACE
10 +# define WINDBG_END_NAMESPACE } // namespace WinDbg
11 +#endif
12 +
13 +WINDBG_BEGIN_NAMESPACE
14 +WINDBG_END_NAMESPACE
15 +
16 +// EOF
1 +# windbg.pri
2 +# 4/21/2014 jichi
3 +win32 {
4 +
5 +DEFINES += WITH_LIB_WINDBG
6 +
7 +LIBS += -lkernel32 -luser32
8 +
9 +DEPENDPATH += $$PWD
10 +
11 +HEADERS += \
12 + $$PWD/hijack.h \
13 + $$PWD/inject.h \
14 + $$PWD/util.h \
15 + $$PWD/unload.h \
16 + $$PWD/windbg_p.h \
17 + $$PWD/windbg.h
18 +
19 +SOURCES += \
20 + $$PWD/hijack.cc \
21 + $$PWD/inject.cc \
22 + $$PWD/util.cc \
23 + $$PWD/unload.cc
24 +}
25 +
26 +# EOF
1 +#pragma once
2 +
3 +// windbg_p.h
4 +// 1/27/2013 jichi
5 +
6 +#include "windbg/windbg.h"
7 +#include <windows.h>
8 +
9 +WINDBG_BEGIN_NAMESPACE
10 +
11 +namespace details { // unnamed
12 +
13 +/// Return the address of func in module.
14 +inline FARPROC getModuleFunctionAddressA(LPCSTR func, LPCSTR module = nullptr)
15 +{ return ::GetProcAddress(::GetModuleHandleA(module), func); }
16 +
17 +inline FARPROC getModuleFunctionAddressW(LPCSTR func, LPCWSTR module = nullptr)
18 +{ return ::GetProcAddress(::GetModuleHandleW(module), func); }
19 +
20 +} // unamed namespace details
21 +
22 +WINDBG_END_NAMESPACE
23 +
24 +// EOF
1 +#pragma once
2 +
3 +// winkey.h
4 +// 7/21/2011
5 +
6 +#include <windows.h>
7 +
8 +#ifndef WINKEY_BEGIN_NAMESPACE
9 +# define WINKEY_BEGIN_NAMESPACE namespace WinKey {
10 +#endif
11 +#ifndef WINKEY_END_NAMESPACE
12 +# define WINKEY_END_NAMESPACE } // namespace WinKey
13 +#endif
14 +
15 +
16 +WINKEY_BEGIN_NAMESPACE
17 +
18 +inline bool isKeyPressed(int vk) { return ::GetKeyState(vk) & 0xf0; }
19 +inline bool isKeyToggled(int vk) { return ::GetKeyState(vk) & 0x0f; }
20 +
21 +inline bool isKeyReturnPressed() { return isKeyPressed(VK_RETURN); }
22 +inline bool isKeyControlPressed() { return isKeyPressed(VK_CONTROL); }
23 +inline bool isKeyShiftPressed() { return isKeyPressed(VK_SHIFT); }
24 +inline bool isKeyAltPressed() { return isKeyPressed(VK_MENU); }
25 +//inline bool sKeyCapslockToggled() { return isKeyToggled(VK_CAPITAL); }
26 +inline bool isKeyWinPressed() { return isKeyPressed(VK_LWIN) || isKeyPressed(VK_RWIN); }
27 +
28 +inline bool isMouseLeftButtonPressed() { return isKeyPressed(VK_LBUTTON); }
29 +inline bool isMouseMiddleButtonPressed() { return isKeyPressed(VK_MBUTTON); }
30 +inline bool isMouseRightButtonPressed() { return isKeyPressed(VK_RBUTTON); }
31 +
32 +WINKEY_END_NAMESPACE
1 +# winkey.pri
2 +# 7/20/2011 jichi
3 +win32 {
4 +
5 +DEFINES += WITH_LIB_WINKEY
6 +
7 +LIBS += -luser32
8 +
9 +DEPENDPATH += $$PWD
10 +
11 +HEADERS += $$PWD/winkey.h
12 +#SOURCES += $$PWD/winkey.cc
13 +}
14 +
15 +# EOF
...@@ -31,7 +31,7 @@ template <typename _Mutex> ...@@ -31,7 +31,7 @@ template <typename _Mutex>
31 native_handle_type native_handle() { return _M_mutex.native_handle(); } 31 native_handle_type native_handle() { return _M_mutex.native_handle(); }
32 void unlock() { _M_mutex.unlock(); _M_locked = false; } 32 void unlock() { _M_mutex.unlock(); _M_locked = false; }
33 void lock() { _M_mutex.lock(); _M_locked = true; } 33 void lock() { _M_mutex.lock(); _M_locked = true; }
34 - bool tryLock() { return _M_locked = _M_mutex.tryLock(); } 34 + bool try_lock() { return _M_locked = _M_mutex.try_lock(); }
35 }; 35 };
36 36
37 // Mutex 37 // Mutex
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
3 # This file is for Windows only. 3 # This file is for Windows only.
4 # Compile SAFESEH table from the ASM file. 4 # Compile SAFESEH table from the ASM file.
5 # See: http://stackoverflow.com/questions/19722308/exception-handler-not-called-in-c 5 # See: http://stackoverflow.com/questions/19722308/exception-handler-not-called-in-c
6 -# See: ::http://stackoverflow.com/questions/12019689/custom-seh-handler-with-safeseh 6 +# See: http://stackoverflow.com/questions/12019689/custom-seh-handler-with-safeseh
7 # See: http://msdn.microsoft.com/en-us/library/16aexws6.aspx 7 # See: http://msdn.microsoft.com/en-us/library/16aexws6.aspx
8 8
9 BUILDDIR = ../../../build 9 BUILDDIR = ../../../build
......
1 ; safeseh.asm 1 ; safeseh.asm
2 -; 12/13/2013 jichi 2 +; 12/13/2013 jichi
3 ; see: http://stackoverflow.com/questions/12019689/custom-seh-handler-with-safeseh 3 ; see: http://stackoverflow.com/questions/12019689/custom-seh-handler-with-safeseh
4 ; see: http://code.metager.de/source/xref/WebKit/Source/WebCore/platform/win/makesafeseh.asm 4 ; see: http://code.metager.de/source/xref/WebKit/Source/WebCore/platform/win/makesafeseh.asm
5 ; see: http://jpassing.com/2008/05/20/fun-with-low-level-seh/ 5 ; see: http://jpassing.com/2008/05/20/fun-with-low-level-seh/
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
5 #include "ntdll/ntdll.h" 5 #include "ntdll/ntdll.h"
6 //#include <cstdio> 6 //#include <cstdio>
7 7
8 -// - Global variables - 8 +// - Define global variables -
9 9
10 seh_dword_t seh_esp[seh_capacity], 10 seh_dword_t seh_esp[seh_capacity],
11 seh_eip[seh_capacity], 11 seh_eip[seh_capacity],
...@@ -29,16 +29,16 @@ seh_dword_t seh_count; ...@@ -29,16 +29,16 @@ seh_dword_t seh_count;
29 // EXCEPTION_DISPOSITION 29 // EXCEPTION_DISPOSITION
30 // NTAPI 30 // NTAPI
31 // EXCEPTION_ROUTINE ( 31 // EXCEPTION_ROUTINE (
32 -// _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord, 32 +// _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
33 -// _In_ PVOID EstablisherFrame, 33 +// _In_ PVOID EstablisherFrame,
34 -// _In_ struct _CONTEXT *ContextRecord, 34 +// _In_ struct _CONTEXT *ContextRecord,
35 -// _In_ PVOID DispatcherContext 35 +// _In_ PVOID DispatcherContext
36 -// ); 36 +// );
37 extern "C" EXCEPTION_DISPOSITION _seh_handler( // extern C is needed to avoid name hashing in C++ 37 extern "C" EXCEPTION_DISPOSITION _seh_handler( // extern C is needed to avoid name hashing in C++
38 _In_ PEXCEPTION_RECORD ExceptionRecord, 38 _In_ PEXCEPTION_RECORD ExceptionRecord,
39 - _In_ PVOID EstablisherFrame, // does not work if I use ULONG64 39 + _In_ PVOID EstablisherFrame, // do not work if I use ULONG64
40 _Inout_ PCONTEXT ContextRecord, 40 _Inout_ PCONTEXT ContextRecord,
41 - _In_ PVOID DispatcherContext) // PDISPATCHER_CONTEXT is not declared in windows.h 41 + _In_ PVOID DispatcherContext) // PDISPATCHER_CONTEXT is not declared in windows.h, use PVOID instead
42 { 42 {
43 //assert(::seh_count > 0); 43 //assert(::seh_count > 0);
44 ContextRecord->Esp = ::seh_esp[::seh_count - 1]; 44 ContextRecord->Esp = ::seh_esp[::seh_count - 1];
......
...@@ -23,11 +23,13 @@ typedef unsigned long seh_dword_t; // DWORD in <windows.h> ...@@ -23,11 +23,13 @@ typedef unsigned long seh_dword_t; // DWORD in <windows.h>
23 23
24 // 12/13/2013 jichi 24 // 12/13/2013 jichi
25 // The list implementation is not thread-safe 25 // The list implementation is not thread-safe
26 -extern seh_dword_t seh_esp[seh_capacity], // LPVOID, current stack 26 +extern seh_dword_t
27 - seh_eip[seh_capacity], // LPVOID, current IP address 27 + seh_count // current number of exception handlers
28 - seh_eh[seh_capacity]; // EXCEPTION_ROUTINE, current exception handler function address 28 + , seh_handler // extern PEXCEPTION_ROUTINE seh_handler;
29 -extern seh_dword_t seh_count; // current number of exception handlers 29 + , seh_esp[seh_capacity] // LPVOID, current stack
30 -extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler; 30 + , seh_eip[seh_capacity] // LPVOID, current IP address
31 + , seh_eh[seh_capacity] // EXCEPTION_ROUTINE, current exception handler function address
32 +;
31 33
32 /** 34 /**
33 * Push SEH handler 35 * Push SEH handler
...@@ -49,21 +51,22 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler; ...@@ -49,21 +51,22 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler;
49 * 51 *
50 * EPB and ESP 52 * EPB and ESP
51 * http://stackoverflow.com/questions/1395591/what-is-exactly-the-base-pointer-and-stack-pointer-to-what-do-they-point 53 * http://stackoverflow.com/questions/1395591/what-is-exactly-the-base-pointer-and-stack-pointer-to-what-do-they-point
54 + *
55 + * TODO: get sizeof dword instead of hardcode 4
52 */ 56 */
53 #define seh_push_(_label, _eh, _r1, _r2) \ 57 #define seh_push_(_label, _eh, _r1, _r2) \
54 { \ 58 { \
55 - __asm mov _r1, _eh /* move new handler address */ \ 59 + __asm mov _r1, _eh /* move new handler address */ \
56 - __asm mov _r2, seh_count /* get current seh counter */ \ 60 + __asm mov _r2, seh_count /* get current seh counter */ \
57 - __asm mov dword ptr seh_eh[_r2*4], _r1 /* set recover exception hander */ \ 61 + __asm mov dword ptr seh_eh[_r2*4], _r1 /* set recover exception hander */ \
58 - __asm mov _r1, _label /* move jump label address */ \ 62 + __asm mov _r1, _label /* move jump label address */ \
59 __asm mov dword ptr seh_eip[_r2*4], _r1 /* set recover eip as the jump label */ \ 63 __asm mov dword ptr seh_eip[_r2*4], _r1 /* set recover eip as the jump label */ \
60 - __asm push seh_handler /* push new safe seh handler */ \ 64 + __asm push seh_handler /* push new safe seh handler */ \
61 - __asm push fs:[0] /* push old fs:0 */ \ 65 + __asm push fs:[0] /* push old fs:0 */ \
62 __asm mov dword ptr seh_esp[_r2*4], esp /* safe current stack address */ \ 66 __asm mov dword ptr seh_esp[_r2*4], esp /* safe current stack address */ \
63 - __asm mov fs:[0], esp /* change fs:0 to the current stack */ \ 67 + __asm mov fs:[0], esp /* change fs:0 to the current stack */ \
64 - __asm inc seh_count /* increase number of seh */ \ 68 + __asm inc seh_count /* increase number of seh */ \
65 } 69 }
66 - //TODO: get sizeof dword instead of hardcode 4
67 70
68 /** 71 /**
69 * Restore old SEH handler 72 * Restore old SEH handler
...@@ -71,12 +74,13 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler; ...@@ -71,12 +74,13 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler;
71 */ 74 */
72 #define seh_pop_(_label) \ 75 #define seh_pop_(_label) \
73 { \ 76 { \
74 - __asm _label: /* the exception recover label */ \ 77 + __asm _label: /* the exception recover label */ \
75 - __asm pop dword ptr fs:[0] /* restore old fs:0 */ \ 78 + __asm pop dword ptr fs:[0] /* restore old fs:0 */ \
76 - __asm add esp, 4 /* pop seh_handler */ \ 79 + __asm add esp, 4 /* pop seh_handler */ \
77 - __asm dec seh_count /* decrease number of seh */ \ 80 + __asm dec seh_count /* decrease number of seh */ \
78 } 81 }
79 82
83 +// Define seh_exit as the shared exit label
80 #define seh_pop() seh_pop_(seh_exit) 84 #define seh_pop() seh_pop_(seh_exit)
81 #define seh_push() seh_push_(seh_exit, 0, eax, ecx) // use ecx as counter better than ebx 85 #define seh_push() seh_push_(seh_exit, 0, eax, ecx) // use ecx as counter better than ebx
82 86
...@@ -93,7 +97,7 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler; ...@@ -93,7 +97,7 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler;
93 { \ 97 { \
94 seh_push() \ 98 seh_push() \
95 __VA_ARGS__ \ 99 __VA_ARGS__ \
96 - ; \ 100 + ; /* allow __VA_ARGS__ to be an expression */ \
97 seh_pop() \ 101 seh_pop() \
98 } 102 }
99 103
...@@ -106,7 +110,7 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler; ...@@ -106,7 +110,7 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler;
106 { \ 110 { \
107 seh_push_eh(_eh) \ 111 seh_push_eh(_eh) \
108 __VA_ARGS__ \ 112 __VA_ARGS__ \
109 - ; \ 113 + ; /* allow __VA_ARGS__ to be an expression */ \
110 seh_pop() \ 114 seh_pop() \
111 } 115 }
112 116
......
1 +// wintimer.cc
2 +// 6/6/2012 jichi
3 +
4 +#include "wintimer/wintimer.h"
5 +
6 +//#define DEBUG "wintimer.cc"
7 +#include "sakurakit/skdebug.h"
8 +#include <windows.h>
9 +WINTIMER_BEGIN_NAMESPACE
10 +
11 +void WinTimer::singleShot(int msecs, const function_type &f, WId parent)
12 +{
13 + Self *t = new Self(parent);
14 + t->setInterval(msecs);
15 + t->setSingleShot(true);
16 + t->setFunction([=] { // Copy function f instead of pass by reference.
17 + f();
18 + delete t;
19 + });
20 + t->start();
21 +}
22 +
23 +WINTIMER_END_NAMESPACE
24 +
25 +// EOF
26 +
27 +/*
28 +// - Single shot -
29 +
30 +namespace { // unnamed
31 +
32 +class apply_delete
33 +{
34 + typedef WinTimer::function_type function_type;
35 + function_type f_;
36 + WinTimer *t_;
37 +public:
38 + apply_delete(const function_type &f, WinTimer *t)
39 + : f_(f), t_(t) { Q_ASSERT(t); }
40 +
41 + void operator()()
42 + {
43 + f_();
44 + delete t_;
45 + }
46 +};
47 +
48 +} // unnamed namespace
49 +*/
1 +#pragma once
2 +
3 +// wintimer.h
4 +// 6/6/2012 jichi
5 +//
6 +// A light-weighted native windows timer as a replacement of QTimer from Qt.
7 +// Implementation is based on Windows Messaging. A visible parent hwnd is required.
8 +//
9 +// This timer is critical where QTimer or event loop are not available, or need to
10 +// warp to different event loop. Some usage cases follow:
11 +// - Used by texthook as a replacement of QTimer in non-QThread
12 +// - Used by qapplicationloader to implement pseudo event loop
13 +// - Used by winhook to synchronize with window event loop across threads
14 +
15 +#include "wintimer/wintimerbase.h"
16 +#include <boost/bind.hpp>
17 +
18 +/**
19 + * @brief A light-weighted native windows timer as a replacement of QTimer.
20 + *
21 + * Needed when in a thread where event loop is not accessible.
22 + * Implemented using extensive inlining over pimp, so that the entire class
23 + * could be put on the stack without heap.
24 + *
25 + * Each timer requires an valid visible window's handle to synchronize with.
26 + * Either specify the window handle with the parent window or a global window.
27 + */
28 +class WinTimer : protected WinTimerBase
29 +{
30 + SK_EXTEND_CLASS(WinTimer, WinTimerBase)
31 + SK_DISABLE_COPY(WinTimer)
32 +
33 + // - Construction -
34 +public:
35 + //typedef std::function<void ()> function_type;
36 + using Base::function_type; ///< std::function<void ()>
37 +
38 + /// Default parent window of all timers.
39 + static WId globalWindow() { return Base::globalWindow; }
40 + static void setGlobalWindow(WId winId) { Base::globalWindow = winId; }
41 +
42 + //static WId createHiddenWindow();
43 +
44 +public:
45 + /// Construct a timer with the parent window handle.
46 + explicit WinTimer(WId parentWindow = 0) { setParentWindow(parentWindow); }
47 +
48 + static void singleShot(int msecs, const function_type &f, WId parent = 0);
49 +
50 + // - Properties -
51 +public:
52 + using Base::isActive;
53 + using Base::isSingleShot;
54 +
55 + void setSingleShot(bool t) { Base::singleShot = t; }
56 +
57 + //bool isEmpty() const { return Base::function.empty(); }
58 +
59 + WId parentWindow() const { return Base::parentWindow; }
60 + void setParentWindow(WId winId) { Base::parentWindow = winId ? winId : Base::globalWindow; }
61 +
62 + int interval() const { return Base::interval; }
63 + void setInterval(int msecs) { Base::interval = msecs; }
64 +
65 + /// Timeout callback when trigger.
66 + void setFunction(const function_type &f) { Base::function = f; }
67 +
68 + /// @overload Set callback to a class method
69 + template <typename Class, typename Member>
70 + void setMethod(Class *obj, Member mfunc)
71 + { setFunction(boost::bind(mfunc, obj)); }
72 +
73 + /// @overload Set callback to a const class method
74 + template <typename Class, typename Member>
75 + void setMethod(const Class *obj, Member mfunc)
76 + { setFunction(boost::bind(mfunc, obj)); }
77 +
78 + // - Actions -
79 +public:
80 + /// Start TimerProc
81 + using Base::start;
82 +
83 + /// Stop TimerProc
84 + using Base::stop;
85 +
86 + /// Reset interval and start TimerProc
87 + void start(int interval) { setInterval(interval); start(); }
88 +
89 + /// Invoke the callback. This function is the callback of the underlying TimerProc
90 + using Base::trigger;
91 +};
92 +
93 +WINTIMER_END_NAMESPACE
1 +# wintimer.pri
2 +# 7/20/2011 jichi
3 +win32 {
4 +
5 +DEFINES += WITH_LIB_WINTIMER
6 +
7 +LIBS += -lkernel32 -luser32 -lwintimer
8 +
9 +DEPENDPATH += $$PWD
10 +
11 +HEADERS += \
12 + $$PWD/wintimer.h \
13 + $$PWD/wintimerbase.h
14 +
15 +#SOURCES += \
16 +# $$PWD/wintimer.cc \
17 +# $$PWD/wintimerbase.cc
18 +}
19 +
20 +# EOF
1 +# sys.pro
2 +# 8/21/2013 jichi
3 +# Build ITH_engine.dll
4 +
5 +#CONFIG += noqt noeh staticlib
6 +CONFIG += staticlib
7 +include(../../../config.pri)
8 +
9 +## Sources
10 +
11 +TEMPLATE = lib
12 +TARGET = wintimer
13 +
14 +HEADERS += \
15 + wintimer.h \
16 + wintimerbase.h
17 +
18 +SOURCES += \
19 + wintimer.cc \
20 + wintimerbase.cc
21 +
22 +# EOF
1 +// wintimerbase.cc
2 +// 6/6/2012 jichi
3 +
4 +#include "wintimer/wintimerbase.h"
5 +#ifdef QT_CORE_LIB
6 +# include <qt_windows.h>
7 +#else
8 +# include <windows.h>
9 +#endif // QT_CORE_LIB
10 +#include "ccutil/ccmacro.h"
11 +
12 +//#define DEBUG "wintimerbase.cc"
13 +#include "sakurakit/skdebug.h"
14 +
15 +static VOID CALLBACK WinTimerProc(
16 + HWND hwnd, // ウィンドウのハンドル
17 + UINT uMsg, // WM_TIMER メッセージ
18 + UINT_PTR idEvent, // Timer ID
19 + DWORD dwTime // 現在のシステム時刻
20 +)
21 +{
22 + Q_UNUSED(hwnd)
23 + Q_UNUSED(dwTime)
24 + Q_UNUSED(uMsg)
25 + Q_ASSERT(idEvent);
26 + if (CC_UNLIKELY(!idEvent))
27 + return;
28 + DOUT("enter");
29 + WinTimerBase *t = reinterpret_cast<WinTimerBase *>(idEvent);
30 +
31 + if (t->isSingleShot() && t->isActive())
32 + t->stop();
33 + t->trigger();
34 + DOUT("leave");
35 +}
36 +
37 +WINTIMER_BEGIN_NAMESPACE
38 +
39 +// - Construction -
40 +
41 +WId WinTimerBase::globalWindow;
42 +
43 +//WId WinTimer::createHiddenWindow()
44 +//{
45 +// DOUT("enter: warning: hidden window used");
46 +// QWidget *w = new QWidget;
47 +// w->resize(QSize());
48 +// w->show();
49 +// DOUT("leave");
50 +// return w->winId();
51 +//}
52 +
53 +// - Timer -
54 +
55 +void WinTimerBase::start()
56 +{
57 + DOUT("enter: active =" << active << ", interval =" << interval);
58 + active = true;
59 + ::SetTimer(parentWindow, reinterpret_cast<UINT_PTR>(this), interval, WinTimerProc);
60 + DOUT("leave");
61 +}
62 +
63 +void WinTimerBase::stop()
64 +{
65 + DOUT("enter: active =" << active);
66 + active = false;
67 + ::KillTimer(parentWindow, reinterpret_cast<UINT_PTR>(this));
68 + DOUT("leave");
69 +}
70 +
71 +WINTIMER_END_NAMESPACE
72 +
73 +// EOF
1 +#pragma once
2 +
3 +// wintimerbase.h
4 +// 6/6/2012 jichi
5 +//
6 +// Internal header for wintimer base class.
7 +
8 +#include "sakurakit/skglobal.h"
9 +#include <functional>
10 +
11 +#ifdef QT_CORE_LIB
12 +# include <QtGui/qwindowdefs.h>
13 +#else
14 +# include <windows.h>
15 +#endif // QT_CORE_LIB
16 +
17 +#ifndef WINTIMER_BEGIN_NAMESPACE
18 +# define WINTIMER_BEGIN_NAMESPACE
19 +#endif
20 +#ifndef WINTIMER_END_NAMESPACE
21 +# define WINTIMER_END_NAMESPACE
22 +#endif
23 +
24 +WINTIMER_BEGIN_NAMESPACE
25 +
26 +/// Internal base class for WinTimer
27 +class WinTimerBase
28 +{
29 + SK_CLASS(WinTimerBase)
30 + SK_DISABLE_COPY(WinTimerBase)
31 +
32 + // - Types -
33 +public:
34 + typedef std::function<void ()> function_type;
35 +#ifndef QT_CORE_LIB
36 + typedef HWND WId;
37 +#endif // QT_CORE_LIB
38 +
39 + // - Methods -
40 +public:
41 + /// Construct a timer with the parent window handle.
42 + WinTimerBase()
43 + : parentWindow(0), // use 0 instead of nullptr to be consistent with Qt5
44 + interval(0), singleShot(false), active(false) {}
45 +
46 + bool isSingleShot() const { return singleShot; }
47 + bool isActive() const { return active; }
48 +
49 + /// Start TimerProc
50 + void start();
51 + /// Stop TimerProc
52 + void stop();
53 + /// Invoke the callback. This function is the callback of the underlying TimerProc
54 + void trigger() { function(); }
55 +
56 + // - Fields -
57 +protected:
58 + static WId globalWindow;
59 +
60 + WId parentWindow;
61 + int interval;
62 + bool singleShot;
63 + bool active;
64 + function_type function;
65 +
66 +};
67 +
68 +WINTIMER_END_NAMESPACE