update to 3.5640.1
update to ITHVNR 3.5640.1 and translation
This commit is contained in:
parent
b21c882e1f
commit
90613f5039
@ -1,55 +1,3 @@
|
|||||||
# common.pri
|
|
||||||
# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
|
||||||
|
|
||||||
# config.pri
|
|
||||||
# win32 {
|
|
||||||
# DEFINES += _SECURE_SCL=0 _SCL_SECURE_NO_WARNINGS
|
|
||||||
# DEFINES += _CRT_SECURE_NO_WARNINGS
|
|
||||||
# QMAKE_CXXFLAGS += -wd4819
|
|
||||||
# }
|
|
||||||
|
|
||||||
# config.pri
|
|
||||||
# win32 {
|
|
||||||
# CONFIG(nocrt) {
|
|
||||||
# message(CONFIG nocrt)
|
|
||||||
# QMAKE_CFLAGS -= /MD /MDd
|
|
||||||
# QMAKE_CFLAGS_DEBUG -= /MD /MDd
|
|
||||||
# QMAKE_CFLAGS_RELEASE -= /MD /MDd
|
|
||||||
# QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO -= /MD /MDd
|
|
||||||
# QMAKE_CXXFLAGS -= /MD /MDd
|
|
||||||
# QMAKE_CXXFLAGS_DEBUG -= /MD /MDd
|
|
||||||
# QMAKE_CXXFLAGS_RELEASE -= /MD /MDd
|
|
||||||
# QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= /MD /MDd
|
|
||||||
# }
|
|
||||||
# CONFIG(eha) {
|
|
||||||
# message(CONFIG eha)
|
|
||||||
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
|
|
||||||
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
|
|
||||||
# QMAKE_CXXFLAGS_STL_ON += /EHa
|
|
||||||
# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
|
|
||||||
# }
|
|
||||||
# CONFIG(noeh) {
|
|
||||||
# message(CONFIG noeh)
|
|
||||||
# QMAKE_CXXFLAGS += /GR-
|
|
||||||
# QMAKE_CXXFLAGS_RTTI_ON -= /GR
|
|
||||||
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
|
|
||||||
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
|
|
||||||
# CONFIG(dll) {
|
|
||||||
# QMAKE_LFLAGS += /ENTRY:"DllMain"
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
# CONFIG(nosafeseh) {
|
|
||||||
# message(CONFIG nosafeseh)
|
|
||||||
# QMAKE_LFLAGS += -safeseh:no
|
|
||||||
# }
|
|
||||||
# }
|
|
||||||
|
|
||||||
# dllconfig.pri
|
|
||||||
# win32 {
|
|
||||||
# CONFIG(eh): DEFINES += ITH_HAS_SEH
|
|
||||||
# CONFIG(noeh): DEFINES -= ITH_HAS_SEH
|
|
||||||
# }
|
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
set(CMAKE_CONFIGURATION_TYPES Debug Release)
|
set(CMAKE_CONFIGURATION_TYPES Debug Release)
|
||||||
@ -66,10 +14,11 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release")
|
|||||||
|
|
||||||
set(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION ON)
|
set(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION ON)
|
||||||
|
|
||||||
|
|
||||||
set(CPACK_GENERATOR "ZIP")
|
set(CPACK_GENERATOR "ZIP")
|
||||||
set(CPACK_PACKAGE_VERSION_MAJOR 3)
|
set(CPACK_PACKAGE_VERSION_MAJOR 3)
|
||||||
set(CPACK_PACKAGE_VERSION_MINOR 4152)
|
set(CPACK_PACKAGE_VERSION_MINOR 5640)
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH 0)
|
set(CPACK_PACKAGE_VERSION_PATCH 1)
|
||||||
set(CPACK_SOURCE_GENERATOR "ZIP")
|
set(CPACK_SOURCE_GENERATOR "ZIP")
|
||||||
set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\.svn/;/\\\\.bzr/;/\\\\.hg/;/\\\\.git/;\\\\.swp$;\\\\.#;/#" ".*\\\\.user$" "\\\\.gitignore$" "\\\\.gitmodules$" "\\\\.git$")
|
set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\.svn/;/\\\\.bzr/;/\\\\.hg/;/\\\\.git/;\\\\.swp$;\\\\.#;/#" ".*\\\\.user$" "\\\\.gitignore$" "\\\\.gitmodules$" "\\\\.git$")
|
||||||
include(CPack)
|
include(CPack)
|
||||||
@ -84,24 +33,20 @@ add_compile_options(
|
|||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(
|
add_definitions(
|
||||||
-D_SECURE_SCL=0 # config.pri
|
/D_SECURE_SCL=0 # config.pri
|
||||||
-D_SCL_SECURE_NO_WARNINGS # config.pri
|
/D_SCL_SECURE_NO_WARNINGS # config.pri
|
||||||
-D_CRT_SECURE_NO_WARNINGS # config.pri
|
/D_CRT_SECURE_NO_WARNINGS # config.pri
|
||||||
-DUNICODE # config.pri
|
/DUNICODE # config.pri
|
||||||
-D_UNICODE
|
/D_UNICODE
|
||||||
-D_CRT_NON_CONFORMING_SWPRINTFS # common.pri
|
/D_CRT_NON_CONFORMING_SWPRINTFS # common.pri
|
||||||
-DITH_HAS_CRT
|
/DITH_HAS_CRT
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/vnr ${CMAKE_BINARY_DIR}/gui)
|
include_directories(
|
||||||
|
.
|
||||||
set(common_src
|
vnr
|
||||||
vnr/ith/common/const.h
|
vnr/texthook
|
||||||
vnr/ith/common/defs.h
|
${CMAKE_BINARY_DIR}/gui
|
||||||
vnr/ith/common/except.h
|
|
||||||
vnr/ith/common/growl.h
|
|
||||||
vnr/ith/common/memory.h
|
|
||||||
vnr/ith/common/types.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(resource_src
|
set(resource_src
|
||||||
@ -119,13 +64,8 @@ set(ithvnr_src
|
|||||||
gui/main.cpp
|
gui/main.cpp
|
||||||
gui/ProcessWindow.cpp
|
gui/ProcessWindow.cpp
|
||||||
gui/ProcessWindow.h
|
gui/ProcessWindow.h
|
||||||
gui/Profile.cpp
|
|
||||||
gui/Profile.h
|
|
||||||
gui/ProfileManager.cpp
|
gui/ProfileManager.cpp
|
||||||
gui/ProfileManager.h
|
gui/ProfileManager.h
|
||||||
gui/pugiconfig.hpp
|
|
||||||
gui/pugixml.cpp
|
|
||||||
gui/pugixml.hpp
|
|
||||||
gui/resource.h
|
gui/resource.h
|
||||||
gui/utility.cpp
|
gui/utility.cpp
|
||||||
gui/utility.h
|
gui/utility.h
|
||||||
@ -135,17 +75,15 @@ set(ithvnr_src
|
|||||||
gui/window.h
|
gui/window.h
|
||||||
gui/TextBuffer.cpp
|
gui/TextBuffer.cpp
|
||||||
gui/TextBuffer.h
|
gui/TextBuffer.h
|
||||||
${common_src}
|
|
||||||
${resource_src}
|
${resource_src}
|
||||||
)
|
)
|
||||||
|
|
||||||
source_group("common" FILES ${common_src})
|
|
||||||
|
|
||||||
source_group("Resource Files" FILES ${resource_src})
|
source_group("Resource Files" FILES ${resource_src})
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} ${ithvnr_src})
|
add_executable(${PROJECT_NAME} ${ithvnr_src})
|
||||||
|
|
||||||
add_subdirectory(vnr)
|
add_subdirectory(vnr)
|
||||||
|
# add_subdirectory(profile)
|
||||||
|
|
||||||
set_target_properties(${PROJECT_NAME} PROPERTIES
|
set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||||
LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFESTDEPENDENCY:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\""
|
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}
|
|||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME}
|
target_link_libraries(${PROJECT_NAME}
|
||||||
|
profile
|
||||||
vnrhost
|
vnrhost
|
||||||
vnrsys
|
ithsys
|
||||||
${WDK_HOME}/lib/wxp/i386/ntdll.lib
|
${WDK_HOME}/lib/wxp/i386/ntdll.lib
|
||||||
comctl32.lib
|
comctl32.lib
|
||||||
psapi.lib
|
psapi.lib
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ITH.h"
|
#include "ITH.h"
|
||||||
|
|
||||||
typedef void (*CustomFilterCallBack) (WORD, PVOID);
|
typedef void (*CustomFilterCallBack) (WORD, PVOID);
|
||||||
|
@ -15,7 +15,8 @@
|
|||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include <windows.h>
|
|
||||||
|
#include <Windows.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <ios>
|
#include <ios>
|
||||||
@ -29,9 +30,8 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <CommCtrl.h>
|
#include <CommCtrl.h>
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
#include <WindowsX.h>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <regex>
|
#include <regex>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include "pugixml.hpp"
|
#include "profile/pugixml.hpp"
|
||||||
#pragma warning(disable: 4146)
|
#pragma warning(disable: 4146)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Generated by ResEdit 1.6.3
|
// Generated by ResEdit 1.6.5
|
||||||
// Copyright (C) 2006-2014
|
// Copyright (C) 2006-2015
|
||||||
// http://www.resedit.net
|
// http://www.resedit.net
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@ -7,9 +7,6 @@
|
|||||||
#include <richedit.h>
|
#include <richedit.h>
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
|
|
||||||
#define WC_LISTVIEW L"SysListView32"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -22,14 +19,14 @@ STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYS
|
|||||||
CAPTION "Process Explorer"
|
CAPTION "Process Explorer"
|
||||||
FONT 8, "MS Shell Dlg", 400, 0, 1
|
FONT 8, "MS Shell Dlg", 400, 0, 1
|
||||||
{
|
{
|
||||||
DEFPUSHBUTTON "확인", IDOK, 281, 189, 53, 14, 0, WS_EX_LEFT
|
DEFPUSHBUTTON "확인", IDOK, 281, 189, 53, 14, 0, WS_EX_LEFT
|
||||||
PUSHBUTTON "프로필 제거", IDC_BUTTON6, 226, 189, 53, 14, 0, WS_EX_LEFT
|
PUSHBUTTON "프로필 제거", IDC_BUTTON6, 226, 189, 53, 14, 0, WS_EX_LEFT
|
||||||
CONTROL "", IDC_LIST1, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_ALIGNLEFT | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT, 7, 20, 327, 164, WS_EX_LEFT
|
CONTROL "", IDC_LIST1, WC_LISTVIEW, WS_TABSTOP | WS_BORDER | LVS_ALIGNLEFT | LVS_SHOWSELALWAYS | LVS_SINGLESEL | LVS_REPORT, 7, 20, 327, 164, WS_EX_LEFT
|
||||||
LTEXT "프로세스", IDC_STATIC, 7, 7, 65, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
LTEXT "프로세스", IDC_STATIC, 7, 7, 65, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
||||||
PUSHBUTTON "부착", IDC_BUTTON2, 61, 189, 53, 14, 0, WS_EX_LEFT
|
PUSHBUTTON "부착", IDC_BUTTON2, 61, 189, 53, 14, 0, WS_EX_LEFT
|
||||||
PUSHBUTTON "탈착", IDC_BUTTON3, 116, 189, 53, 14, 0, WS_EX_LEFT
|
PUSHBUTTON "탈착", IDC_BUTTON3, 116, 189, 53, 14, 0, WS_EX_LEFT
|
||||||
PUSHBUTTON "프로필 추가", IDC_BUTTON5, 171, 189, 53, 14, 0, WS_EX_LEFT
|
PUSHBUTTON "프로필 추가", IDC_BUTTON5, 171, 189, 53, 14, 0, WS_EX_LEFT
|
||||||
PUSHBUTTON "새로고침", IDC_BUTTON1, 7, 189, 53, 14, 0, WS_EX_LEFT
|
PUSHBUTTON "새로고침", IDC_BUTTON1, 7, 189, 53, 14, 0, WS_EX_LEFT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -40,22 +37,22 @@ STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYS
|
|||||||
CAPTION "Option"
|
CAPTION "Option"
|
||||||
FONT 8, "MS Shell Dlg", 400, 0, 1
|
FONT 8, "MS Shell Dlg", 400, 0, 1
|
||||||
{
|
{
|
||||||
DEFPUSHBUTTON "확인", IDOK, 8, 164, 50, 14, 0, WS_EX_LEFT
|
DEFPUSHBUTTON "확인", IDOK, 8, 164, 50, 14, 0, WS_EX_LEFT
|
||||||
PUSHBUTTON "취소", IDCANCEL, 65, 164, 50, 14, 0, WS_EX_LEFT
|
PUSHBUTTON "취소", IDCANCEL, 65, 164, 50, 14, 0, WS_EX_LEFT
|
||||||
EDITTEXT IDC_EDIT1, 60, 7, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
|
EDITTEXT IDC_EDIT1, 60, 7, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
|
||||||
LTEXT "문단나누기", IDC_STATIC, 7, 7, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
LTEXT "문단나누기", IDC_STATIC, 7, 7, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
||||||
EDITTEXT IDC_EDIT2, 60, 25, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
|
EDITTEXT IDC_EDIT2, 60, 25, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
|
||||||
LTEXT "프로세스 대기", IDC_STATIC, 7, 26, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
LTEXT "프로세스 대기", IDC_STATIC, 7, 26, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
||||||
EDITTEXT IDC_EDIT3, 60, 45, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
|
EDITTEXT IDC_EDIT3, 60, 45, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
|
||||||
LTEXT "인젝션 대기", IDC_STATIC, 7, 45, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
LTEXT "인젝션 대기", IDC_STATIC, 7, 45, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
||||||
EDITTEXT IDC_EDIT4, 60, 65, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
|
EDITTEXT IDC_EDIT4, 60, 65, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
|
||||||
LTEXT "삽입 대기", IDC_STATIC, 7, 65, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
LTEXT "삽입 대기", IDC_STATIC, 7, 65, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
||||||
AUTOCHECKBOX "자동 부착", IDC_CHECK1, 7, 87, 54, 10, 0, WS_EX_LEFT
|
AUTOCHECKBOX "자동 부착", IDC_CHECK1, 7, 87, 54, 10, 0, WS_EX_LEFT
|
||||||
AUTOCHECKBOX "자동 삽입", IDC_CHECK2, 62, 87, 50, 10, 0, WS_EX_LEFT
|
AUTOCHECKBOX "자동 삽입", IDC_CHECK2, 62, 87, 50, 10, 0, WS_EX_LEFT
|
||||||
AUTOCHECKBOX "클립보드로 자동 복사", IDC_CHECK3, 7, 103, 88, 10, 0, WS_EX_LEFT
|
AUTOCHECKBOX "클립보드로 자동 복사", IDC_CHECK3, 7, 103, 88, 10, 0, WS_EX_LEFT
|
||||||
AUTOCHECKBOX "자동 반복문 제거", IDC_CHECK4, 7, 119, 95, 10, 0, WS_EX_LEFT
|
AUTOCHECKBOX "자동 반복문 제거", IDC_CHECK4, 7, 119, 95, 10, 0, WS_EX_LEFT
|
||||||
AUTOCHECKBOX "문자필터 초기화", IDC_CHECK6, 7, 149, 81, 8, 0, WS_EX_LEFT
|
AUTOCHECKBOX "문자필터 초기화", IDC_CHECK6, 7, 149, 81, 8, 0, WS_EX_LEFT
|
||||||
AUTOCHECKBOX "글로벌 필터 활성화", IDC_CHECK5, 7, 134, 75, 10, 0, WS_EX_LEFT
|
AUTOCHECKBOX "글로벌 필터 활성화", IDC_CHECK5, 7, 134, 75, 10, 0, WS_EX_LEFT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -65,3 +62,21 @@ FONT 8, "MS Shell Dlg", 400, 0, 1
|
|||||||
//
|
//
|
||||||
LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN
|
LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN
|
||||||
IDI_ICON1 ICON "icon1.ico"
|
IDI_ICON1 ICON "icon1.ico"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Version Information resources
|
||||||
|
//
|
||||||
|
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
||||||
|
1 VERSIONINFO
|
||||||
|
FILEVERSION 0,0,0,0
|
||||||
|
PRODUCTVERSION 0,0,0,0
|
||||||
|
FILEOS VOS_UNKNOWN
|
||||||
|
FILETYPE VFT_UNKNOWN
|
||||||
|
FILESUBTYPE VFT2_UNKNOWN
|
||||||
|
FILEFLAGSMASK 0
|
||||||
|
FILEFLAGS 0
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
#include "ProcessWindow.h"
|
#include "ProcessWindow.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "ith/host/srv.h"
|
#include "host/host.h"
|
||||||
#include "ith/host/hookman.h"
|
#include "host/hookman.h"
|
||||||
#include "ProfileManager.h"
|
#include "ProfileManager.h"
|
||||||
#include "Profile.h"
|
#include "profile/Profile.h"
|
||||||
|
|
||||||
extern HookManager* man; // main.cpp
|
extern HookManager* man; // main.cpp
|
||||||
extern ProfileManager* pfman; // ProfileManager.cpp
|
extern ProfileManager* pfman; // ProfileManager.cpp
|
||||||
@ -22,6 +22,8 @@ ProcessWindow::ProcessWindow(HWND hDialog) : hDlg(hDialog)
|
|||||||
ListView_SetExtendedListViewStyleEx(hlProcess, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
|
ListView_SetExtendedListViewStyleEx(hlProcess, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
|
||||||
InitProcessDlg();
|
InitProcessDlg();
|
||||||
RefreshProcess();
|
RefreshProcess();
|
||||||
|
EnableWindow(hbDetach, FALSE);
|
||||||
|
EnableWindow(hbAttach, FALSE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessWindow::InitProcessDlg()
|
void ProcessWindow::InitProcessDlg()
|
||||||
@ -73,30 +75,33 @@ void ProcessWindow::RefreshProcess()
|
|||||||
void ProcessWindow::AttachProcess()
|
void ProcessWindow::AttachProcess()
|
||||||
{
|
{
|
||||||
DWORD pid = GetSelectedPID();
|
DWORD pid = GetSelectedPID();
|
||||||
if (IHF_InjectByPID(pid) != -1)
|
if (Host_InjectByPID(pid))
|
||||||
|
{
|
||||||
|
Host_HijackProcess(pid);
|
||||||
RefreshThreadWithPID(pid, true);
|
RefreshThreadWithPID(pid, true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ProcessWindow::DetachProcess()
|
void ProcessWindow::DetachProcess()
|
||||||
{
|
{
|
||||||
DWORD pid = GetSelectedPID();
|
DWORD pid = GetSelectedPID();
|
||||||
if (IHF_ActiveDetachProcess(pid) == 0)
|
if (Host_ActiveDetachProcess(pid) == 0)
|
||||||
RefreshThreadWithPID(pid, false);
|
RefreshThreadWithPID(pid, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessWindow::AddCurrentToProfile()
|
void ProcessWindow::CreateProfileForSelectedProcess()
|
||||||
{
|
{
|
||||||
DWORD pid = GetSelectedPID();
|
DWORD pid = GetSelectedPID();
|
||||||
auto path = GetProcessPath(pid);
|
auto path = GetProcessPath(pid);
|
||||||
if (!path.empty())
|
if (!path.empty())
|
||||||
{
|
{
|
||||||
Profile* pf = pfman->AddProfile(path, pid);
|
Profile* pf = pfman->CreateProfile(pid);
|
||||||
pfman->FindProfileAndUpdateHookAddresses(pid, path);
|
pfman->SaveProfiles();
|
||||||
RefreshThread(ListView_GetSelectionMark(hlProcess));
|
RefreshThread(ListView_GetSelectionMark(hlProcess));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProcessWindow::RemoveCurrentFromProfile()
|
void ProcessWindow::DeleteProfileForSelectedProcess()
|
||||||
{
|
{
|
||||||
DWORD pid = GetSelectedPID();
|
DWORD pid = GetSelectedPID();
|
||||||
auto path = GetProcessPath(pid);
|
auto path = GetProcessPath(pid);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ITH.h"
|
#include "ITH.h"
|
||||||
|
|
||||||
class ProcessWindow
|
class ProcessWindow
|
||||||
@ -9,8 +10,8 @@ public:
|
|||||||
void RefreshProcess();
|
void RefreshProcess();
|
||||||
void AttachProcess();
|
void AttachProcess();
|
||||||
void DetachProcess();
|
void DetachProcess();
|
||||||
void AddCurrentToProfile();
|
void CreateProfileForSelectedProcess();
|
||||||
void RemoveCurrentFromProfile();
|
void DeleteProfileForSelectedProcess();
|
||||||
void RefreshThread(int index);
|
void RefreshThread(int index);
|
||||||
private:
|
private:
|
||||||
void RefreshThreadWithPID(DWORD pid, bool isAttached);
|
void RefreshThreadWithPID(DWORD pid, bool isAttached);
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
#include "ProfileManager.h"
|
#include "ProfileManager.h"
|
||||||
#include "Profile.h"
|
#include "profile/Profile.h"
|
||||||
#include "ith/host/srv.h"
|
#include "host/host.h"
|
||||||
#include "ith/host/hookman.h"
|
#include "host/hookman.h"
|
||||||
#include "ith/common/types.h"
|
#include "vnrhook/include/types.h"
|
||||||
#include "ith/common/const.h"
|
#include "vnrhook/include/const.h"
|
||||||
|
#include "utility.h"
|
||||||
|
#include "profile/misc.h"
|
||||||
|
|
||||||
extern HookManager* man; // main.cpp
|
extern HookManager* man; // main.cpp
|
||||||
extern LONG auto_inject, auto_insert, inject_delay; // main.cpp
|
extern LONG auto_inject, auto_insert, inject_delay; // main.cpp
|
||||||
@ -12,21 +14,16 @@ bool MonitorFlag;
|
|||||||
ProfileManager* pfman;
|
ProfileManager* pfman;
|
||||||
|
|
||||||
DWORD WINAPI MonitorThread(LPVOID lpThreadParameter);
|
DWORD WINAPI MonitorThread(LPVOID lpThreadParameter);
|
||||||
void AddHooksToProfile(Profile& pf, const ProcessRecord& pr);
|
|
||||||
void AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid);
|
|
||||||
DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread& thread);
|
|
||||||
void MakeHookRelative(const ProcessRecord& pr, HookParam& hp);
|
|
||||||
std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address);
|
|
||||||
void GetHookNameToAddressMap(const ProcessRecord& pr, std::map<std::wstring, DWORD>& hookNameToAddress);
|
|
||||||
|
|
||||||
ProfileManager::ProfileManager() :
|
ProfileManager::ProfileManager() :
|
||||||
hMonitorThread(IthCreateThread(MonitorThread, 0))
|
hMonitorThread(IthCreateThread(MonitorThread, 0))
|
||||||
{
|
{
|
||||||
LoadProfile();
|
LoadProfiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
ProfileManager::~ProfileManager()
|
ProfileManager::~ProfileManager()
|
||||||
{
|
{
|
||||||
SaveProfile();
|
SaveProfiles();
|
||||||
WaitForSingleObject(hMonitorThread.get(), 0);
|
WaitForSingleObject(hMonitorThread.get(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +39,7 @@ Profile* ProfileManager::GetProfile(DWORD pid)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProfileManager::AddProfile(pugi::xml_node game)
|
bool ProfileManager::CreateProfile(pugi::xml_node game)
|
||||||
{
|
{
|
||||||
auto file = game.child(L"File");
|
auto file = game.child(L"File");
|
||||||
auto profile = game.child(L"Profile");
|
auto profile = game.child(L"Profile");
|
||||||
@ -56,13 +53,17 @@ bool ProfileManager::AddProfile(pugi::xml_node game)
|
|||||||
auto pf = new Profile(title);
|
auto pf = new Profile(title);
|
||||||
if (!pf->XmlReadProfile(profile))
|
if (!pf->XmlReadProfile(profile))
|
||||||
return false;
|
return false;
|
||||||
AddProfile(path.value(), profile_ptr(pf));
|
CSLock lock(cs);
|
||||||
|
auto& oldProfile = profile_tree[path.value()];
|
||||||
|
if (!oldProfile)
|
||||||
|
oldProfile.swap(profile_ptr(pf));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Profile* ProfileManager::AddProfile(const std::wstring& path, DWORD pid)
|
Profile* ProfileManager::CreateProfile(DWORD pid)
|
||||||
{
|
{
|
||||||
CSLock lock(cs);
|
CSLock lock(cs);
|
||||||
|
auto path = GetProcessPath(pid);
|
||||||
auto& pf = profile_tree[path];
|
auto& pf = profile_tree[path];
|
||||||
if (!pf)
|
if (!pf)
|
||||||
{
|
{
|
||||||
@ -72,15 +73,6 @@ Profile* ProfileManager::AddProfile(const std::wstring& path, DWORD pid)
|
|||||||
return pf.get();
|
return pf.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Profile* ProfileManager::AddProfile(const std::wstring& path, profile_ptr new_profile)
|
|
||||||
{
|
|
||||||
CSLock lock(cs);
|
|
||||||
auto& pf = profile_tree[path];
|
|
||||||
if (!pf)
|
|
||||||
pf.swap(new_profile);
|
|
||||||
return pf.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node root)
|
void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node root)
|
||||||
{
|
{
|
||||||
auto game = root.append_child(L"Game");
|
auto game = root.append_child(L"Game");
|
||||||
@ -96,7 +88,7 @@ void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfileManager::LoadProfile()
|
void ProfileManager::LoadProfiles()
|
||||||
{
|
{
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
UniqueHandle hFile(IthCreateFile(L"ITH_Profile.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
|
UniqueHandle hFile(IthCreateFile(L"ITH_Profile.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
|
||||||
@ -112,10 +104,10 @@ void ProfileManager::LoadProfile()
|
|||||||
if (!root)
|
if (!root)
|
||||||
return;
|
return;
|
||||||
for (auto game = root.begin(); game != root.end(); ++game)
|
for (auto game = root.begin(); game != root.end(); ++game)
|
||||||
AddProfile(*game);
|
CreateProfile(*game);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfileManager::SaveProfile()
|
void ProfileManager::SaveProfiles()
|
||||||
{
|
{
|
||||||
pugi::xml_document doc;
|
pugi::xml_document doc;
|
||||||
auto root = doc.append_child(L"ITH_Profile");
|
auto root = doc.append_child(L"ITH_Profile");
|
||||||
@ -138,44 +130,14 @@ void ProfileManager::DeleteProfile(const std::wstring& path)
|
|||||||
profile_tree.erase(profile_tree.find(path));
|
profile_tree.erase(profile_tree.find(path));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProfileManager::FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path)
|
Profile* ProfileManager::GetProfile(const std::wstring& path)
|
||||||
{
|
{
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
return;
|
return nullptr;
|
||||||
auto it = profile_tree.find(path);
|
auto it = profile_tree.find(path);
|
||||||
if (it == profile_tree.end())
|
if (it == profile_tree.end())
|
||||||
return;
|
return nullptr;
|
||||||
auto& pf = it->second;
|
return it->second.get();
|
||||||
const ProcessRecord* pr = man->GetProcessRecord(pid);
|
|
||||||
if (pr == NULL)
|
|
||||||
return;
|
|
||||||
// hook name -> hook address
|
|
||||||
std::map<std::wstring, DWORD> hookNameToAddress;
|
|
||||||
GetHookNameToAddressMap(*pr, hookNameToAddress);
|
|
||||||
for (auto thread_profile = pf->Threads().begin(); thread_profile != pf->Threads().end();
|
|
||||||
++thread_profile)
|
|
||||||
{
|
|
||||||
auto it = hookNameToAddress.find((*thread_profile)->HookName());
|
|
||||||
if (it != hookNameToAddress.end())
|
|
||||||
(*thread_profile)->HookAddress() = it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GetHookNameToAddressMap(const ProcessRecord& pr,
|
|
||||||
std::map<std::wstring, DWORD>& hookNameToAddress)
|
|
||||||
{
|
|
||||||
WaitForSingleObject(pr.hookman_mutex, 0);
|
|
||||||
auto hooks = (const Hook*)pr.hookman_map;
|
|
||||||
for (DWORD i = 0; i < MAX_HOOK; ++i)
|
|
||||||
{
|
|
||||||
if (hooks[i].Address() == 0)
|
|
||||||
continue;
|
|
||||||
auto& hook = hooks[i];
|
|
||||||
std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]);
|
|
||||||
if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength() * 2, NULL))
|
|
||||||
hookNameToAddress[name.get()] = hook.Address();
|
|
||||||
}
|
|
||||||
ReleaseMutex(pr.hookman_mutex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProfileManager::HasProfile(const std::wstring& path)
|
bool ProfileManager::HasProfile(const std::wstring& path)
|
||||||
@ -183,7 +145,7 @@ bool ProfileManager::HasProfile(const std::wstring& path)
|
|||||||
return profile_tree.find(path) != profile_tree.end();
|
return profile_tree.find(path) != profile_tree.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD ProfileManager::ProfileCount()
|
DWORD ProfileManager::CountProfiles()
|
||||||
{
|
{
|
||||||
return profile_tree.size();
|
return profile_tree.size();
|
||||||
}
|
}
|
||||||
@ -194,7 +156,7 @@ DWORD WINAPI InjectThread(LPVOID lpThreadParameter)
|
|||||||
Sleep(inject_delay);
|
Sleep(inject_delay);
|
||||||
if (man == NULL)
|
if (man == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
DWORD status = IHF_InjectByPID(pid);
|
DWORD status = Host_InjectByPID(pid);
|
||||||
if (!auto_insert)
|
if (!auto_insert)
|
||||||
return status;
|
return status;
|
||||||
if (status == -1)
|
if (status == -1)
|
||||||
@ -206,7 +168,10 @@ DWORD WINAPI InjectThread(LPVOID lpThreadParameter)
|
|||||||
SendParam sp;
|
SendParam sp;
|
||||||
sp.type = 0;
|
sp.type = 0;
|
||||||
for (auto hp = pf->Hooks().begin(); hp != pf->Hooks().end(); ++hp)
|
for (auto hp = pf->Hooks().begin(); hp != pf->Hooks().end(); ++hp)
|
||||||
IHF_InsertHook(pid, const_cast<HookParam*>(&(*hp)->HP()), (*hp)->Name().c_str());
|
{
|
||||||
|
std::string name = toMultiByteString((*hp)->Name());
|
||||||
|
Host_InsertHook(pid, const_cast<HookParam*>(&(*hp)->HP()), name.c_str());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
@ -237,116 +202,17 @@ DWORD WINAPI MonitorThread(LPVOID lpThreadParameter)
|
|||||||
|
|
||||||
DWORD SaveProcessProfile(DWORD pid)
|
DWORD SaveProcessProfile(DWORD pid)
|
||||||
{
|
{
|
||||||
const ProcessRecord* pr = man->GetProcessRecord(pid);
|
|
||||||
if (pr == NULL)
|
|
||||||
return 0;
|
|
||||||
std::wstring path = GetProcessPath(pid);
|
std::wstring path = GetProcessPath(pid);
|
||||||
if (path.empty())
|
if (path.empty())
|
||||||
return 0;
|
return 0;
|
||||||
|
pugi::xml_document doc;
|
||||||
|
pugi::xml_node profile_node = doc.append_child(L"Profile");
|
||||||
|
man->GetProfile(pid, profile_node);
|
||||||
Profile* pf = pfman->GetProfile(pid);
|
Profile* pf = pfman->GetProfile(pid);
|
||||||
if (pf != NULL)
|
if (pf != NULL)
|
||||||
pf->Clear();
|
pf->Clear();
|
||||||
else
|
else
|
||||||
pf = pfman->AddProfile(path, pid);
|
pf = pfman->CreateProfile(pid);
|
||||||
AddHooksToProfile(*pf, *pr);
|
pf->XmlReadProfile(profile_node);
|
||||||
AddThreadsToProfile(*pf, *pr, pid);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddHooksToProfile(Profile& pf, const ProcessRecord& pr)
|
|
||||||
{
|
|
||||||
WaitForSingleObject(pr.hookman_mutex, 0);
|
|
||||||
auto hooks = (const Hook*)pr.hookman_map;
|
|
||||||
for (DWORD i = 0; i < MAX_HOOK; ++i)
|
|
||||||
{
|
|
||||||
if (hooks[i].Address() == 0)
|
|
||||||
continue;
|
|
||||||
auto& hook = hooks[i];
|
|
||||||
DWORD type = hook.Type();
|
|
||||||
if ((type & HOOK_ADDITIONAL) && (type & HOOK_ENGINE) == 0)
|
|
||||||
{
|
|
||||||
std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]);
|
|
||||||
if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength() * 2, NULL))
|
|
||||||
{
|
|
||||||
if (hook.hp.module)
|
|
||||||
{
|
|
||||||
HookParam hp = hook.hp;
|
|
||||||
MakeHookRelative(pr, hp);
|
|
||||||
pf.AddHook(hp, name.get());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
pf.AddHook(hook.hp, name.get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ReleaseMutex(pr.hookman_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void MakeHookRelative(const ProcessRecord& pr, HookParam& hp)
|
|
||||||
{
|
|
||||||
MEMORY_BASIC_INFORMATION info;
|
|
||||||
VirtualQueryEx(pr.process_handle, (LPCVOID)hp.addr, &info, sizeof(info));
|
|
||||||
hp.addr -= (DWORD)info.AllocationBase;
|
|
||||||
hp.function = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid)
|
|
||||||
{
|
|
||||||
man->LockHookman();
|
|
||||||
ThreadTable* table = man->Table();
|
|
||||||
for (int i = 0; i < table->Used(); ++i)
|
|
||||||
{
|
|
||||||
TextThread* tt = table->FindThread(i);
|
|
||||||
if (tt == NULL || tt->GetThreadParameter()->pid != pid)
|
|
||||||
continue;
|
|
||||||
//if (tt->Status() & CURRENT_SELECT || tt->Link() || tt->GetComment())
|
|
||||||
if (tt->Status() & CURRENT_SELECT || tt->Link())
|
|
||||||
AddThreadToProfile(pf, pr, *tt);
|
|
||||||
}
|
|
||||||
man->UnlockHookman();
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread& thread)
|
|
||||||
{
|
|
||||||
const ThreadParameter* tp = thread.GetThreadParameter();
|
|
||||||
std::wstring hook_name = GetHookNameByAddress(pr, tp->hook);
|
|
||||||
if (hook_name.empty())
|
|
||||||
return -1;
|
|
||||||
auto thread_profile = new ThreadProfile(hook_name, tp->retn, tp->spl, 0, 0,
|
|
||||||
THREAD_MASK_RETN | THREAD_MASK_SPLIT, L"");
|
|
||||||
DWORD threads_size = pf.Threads().size();
|
|
||||||
int thread_profile_index = pf.AddThread(thread_ptr(thread_profile));
|
|
||||||
if (thread_profile_index == threads_size) // new thread
|
|
||||||
{
|
|
||||||
WORD iw = thread_profile_index & 0xFFFF;
|
|
||||||
if (thread.Status() & CURRENT_SELECT)
|
|
||||||
pf.SelectedIndex() = iw;
|
|
||||||
if (thread.Link())
|
|
||||||
{
|
|
||||||
WORD to_index = AddThreadToProfile(pf, pr, *(thread.Link())) & 0xFFFF;
|
|
||||||
if (iw >= 0)
|
|
||||||
pf.AddLink(link_ptr(new LinkProfile(iw, to_index)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return thread_profile_index; // in case more than one thread links to the same thread.
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address)
|
|
||||||
{
|
|
||||||
std::wstring hook_name;
|
|
||||||
WaitForSingleObject(pr.hookman_mutex, 0);
|
|
||||||
auto hooks = (const Hook*)pr.hookman_map;
|
|
||||||
for (int i = 0; i < MAX_HOOK; ++i)
|
|
||||||
{
|
|
||||||
auto& hook = hooks[i];
|
|
||||||
if (hook.Address() == hook_address)
|
|
||||||
{
|
|
||||||
std::unique_ptr<WCHAR[]> name(new WCHAR[hook.NameLength() * 2]);
|
|
||||||
if (ReadProcessMemory(pr.process_handle, hooks[i].Name(), name.get(), hook.NameLength() * 2, NULL))
|
|
||||||
hook_name = name.get();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ReleaseMutex(pr.hookman_mutex);
|
|
||||||
return hook_name;
|
|
||||||
}
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ITH.h"
|
#include "ITH.h"
|
||||||
#include "utility.h" // UniqueHandle, CriticalSection
|
#include "utility.h" // UniqueHandle, CriticalSection
|
||||||
|
|
||||||
@ -9,14 +10,14 @@ class ProfileManager
|
|||||||
public:
|
public:
|
||||||
ProfileManager();
|
ProfileManager();
|
||||||
~ProfileManager();
|
~ProfileManager();
|
||||||
Profile* AddProfile(const std::wstring& path, DWORD pid);
|
Profile* CreateProfile(DWORD pid);
|
||||||
void DeleteProfile(const std::wstring& path);
|
|
||||||
void LoadProfile();
|
|
||||||
void SaveProfile();
|
|
||||||
void FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path);
|
|
||||||
bool HasProfile(const std::wstring& path);
|
|
||||||
Profile* GetProfile(DWORD pid);
|
Profile* GetProfile(DWORD pid);
|
||||||
DWORD ProfileCount();
|
Profile* GetProfile(const std::wstring& path);
|
||||||
|
void LoadProfiles();
|
||||||
|
void SaveProfiles();
|
||||||
|
void DeleteProfile(const std::wstring& path);
|
||||||
|
void UpdateHookAddresses(DWORD pid);
|
||||||
|
bool HasProfile(const std::wstring& path);
|
||||||
private:
|
private:
|
||||||
typedef std::unique_ptr<Profile> profile_ptr;
|
typedef std::unique_ptr<Profile> profile_ptr;
|
||||||
typedef std::map<std::wstring, profile_ptr> profile_map;
|
typedef std::map<std::wstring, profile_ptr> profile_map;
|
||||||
@ -24,8 +25,8 @@ private:
|
|||||||
ProfileManager(const ProfileManager&);
|
ProfileManager(const ProfileManager&);
|
||||||
ProfileManager operator=(const ProfileManager&);
|
ProfileManager operator=(const ProfileManager&);
|
||||||
|
|
||||||
bool AddProfile(pugi::xml_node game);
|
DWORD CountProfiles();
|
||||||
Profile* AddProfile(const std::wstring& path, profile_ptr new_profile);
|
bool CreateProfile(pugi::xml_node game);
|
||||||
void WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node doc);
|
void WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node doc);
|
||||||
// locate profile with executable path
|
// locate profile with executable path
|
||||||
profile_map profile_tree;
|
profile_map profile_tree;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ITH.h"
|
#include "ITH.h"
|
||||||
#include "utility.h" // UniqueHandle, CriticalSection
|
#include "utility.h" // UniqueHandle, CriticalSection
|
||||||
|
|
||||||
|
169
gui/command.cpp
169
gui/command.cpp
@ -16,157 +16,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ITH.h"
|
#include "ITH.h"
|
||||||
#include "ith/host/srv.h"
|
#include "host/host.h"
|
||||||
#include "ith/common/const.h"
|
#include "vnrhook/include/const.h"
|
||||||
#include "ith/common/types.h"
|
#include "vnrhook/include/types.h"
|
||||||
#include "language.h"
|
#include "language.h"
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
#include "profile/misc.h"
|
||||||
|
|
||||||
extern HookManager* man;
|
extern HookManager* man;
|
||||||
extern HWND hwndProcessComboBox;
|
extern HWND hwndProcessComboBox;
|
||||||
|
|
||||||
bool Parse(const std::wstring& cmd, HookParam& hp)
|
|
||||||
{
|
|
||||||
using std::wregex;
|
|
||||||
using std::regex_search;
|
|
||||||
// /H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]
|
|
||||||
wregex rx(L"^X?([ABWSQ])(N)?", wregex::icase);
|
|
||||||
std::match_results<std::wstring::const_iterator> m;
|
|
||||||
auto start = cmd.begin();
|
|
||||||
auto end = cmd.end();
|
|
||||||
bool result = regex_search(start, end, m, rx);
|
|
||||||
if (!result)
|
|
||||||
return result;
|
|
||||||
start = m[0].second;
|
|
||||||
if (m[2].matched)
|
|
||||||
hp.type |= NO_CONTEXT;
|
|
||||||
|
|
||||||
switch (m[1].first[0])
|
|
||||||
{
|
|
||||||
case L's':
|
|
||||||
case L'S':
|
|
||||||
hp.type |= USING_STRING;
|
|
||||||
break;
|
|
||||||
case L'e':
|
|
||||||
case L'E':
|
|
||||||
hp.type |= STRING_LAST_CHAR;
|
|
||||||
case L'a':
|
|
||||||
case L'A':
|
|
||||||
hp.type |= BIG_ENDIAN;
|
|
||||||
hp.length_offset = 1;
|
|
||||||
break;
|
|
||||||
case L'b':
|
|
||||||
case L'B':
|
|
||||||
hp.length_offset = 1;
|
|
||||||
break;
|
|
||||||
case L'h':
|
|
||||||
case L'H':
|
|
||||||
hp.type |= PRINT_DWORD;
|
|
||||||
case L'q':
|
|
||||||
case L'Q':
|
|
||||||
hp.type |= USING_STRING | USING_UNICODE;
|
|
||||||
break;
|
|
||||||
case L'l':
|
|
||||||
case L'L':
|
|
||||||
hp.type |= STRING_LAST_CHAR;
|
|
||||||
case L'w':
|
|
||||||
case L'W':
|
|
||||||
hp.type |= USING_UNICODE;
|
|
||||||
hp.length_offset = 1;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// [data_offset[*drdo]]
|
|
||||||
std::wstring data_offset(L"(-?[[:xdigit:]]+)"), drdo(L"(\\*-?[[:xdigit:]]+)?");
|
|
||||||
rx = wregex(L"^"+ data_offset + drdo, wregex::icase);
|
|
||||||
result = regex_search(start, end, m, rx);
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
start = m[0].second;
|
|
||||||
hp.off = std::stoul(m[1].str(), NULL, 16);
|
|
||||||
if (m[2].matched)
|
|
||||||
{
|
|
||||||
hp.type |= DATA_INDIRECT;
|
|
||||||
hp.ind = std::stoul(m[2].str().substr(1), NULL, 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// [:sub_offset[*drso]]
|
|
||||||
std::wstring sub_offset(L"(-?[[:xdigit:]]+)"), drso(L"(\\*-?[[:xdigit:]]+)?");
|
|
||||||
rx = wregex(L"^:" + sub_offset + drso, wregex::icase);
|
|
||||||
result = regex_search(start, end, m, rx);
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
start = m[0].second;
|
|
||||||
hp.type |= USING_SPLIT;
|
|
||||||
hp.split = std::stoul(m[1].str(), NULL, 16);
|
|
||||||
if (m[2].matched)
|
|
||||||
{
|
|
||||||
hp.type |= SPLIT_INDIRECT;
|
|
||||||
hp.split_ind = std::stoul(m[2].str().substr(1), NULL, 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// @addr
|
|
||||||
rx = wregex(L"^@[[:xdigit:]]+", wregex::icase);
|
|
||||||
result = regex_search(start, end, m, rx);
|
|
||||||
if (!result)
|
|
||||||
return false;
|
|
||||||
start = m[0].second;
|
|
||||||
hp.addr = std::stoul(m[0].str().substr(1), NULL, 16);
|
|
||||||
if (hp.off & 0x80000000)
|
|
||||||
hp.off -= 4;
|
|
||||||
if (hp.split & 0x80000000)
|
|
||||||
hp.split -= 4;
|
|
||||||
|
|
||||||
// [:[module[:{name|#ordinal}]]]
|
|
||||||
// ":" ->
|
|
||||||
// "" -> MODULE_OFFSET && module == NULL && function == addr
|
|
||||||
// ":GDI.dll" -> MODULE_OFFSET && module != NULL
|
|
||||||
// ":GDI.dll:strlen" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
|
|
||||||
// ":GDI.dll:#123" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
|
|
||||||
std::wstring module(L"([[:graph:]]+)"), name(L"[[:graph:]]+"), ordinal(L"\\d+");
|
|
||||||
rx = wregex(L"^:(" + module + L"(:" + name + L"|#" + ordinal + L")?)?$", wregex::icase);
|
|
||||||
result = regex_search(start, end, m, rx);
|
|
||||||
if (result) // :[module[:{name|#ordinal}]]
|
|
||||||
{
|
|
||||||
if (m[1].matched) // module
|
|
||||||
{
|
|
||||||
hp.type |= MODULE_OFFSET;
|
|
||||||
std::wstring module = m[2];
|
|
||||||
std::transform(module.begin(), module.end(), module.begin(), ::towlower);
|
|
||||||
hp.module = Hash(module);
|
|
||||||
if (m[3].matched) // :name|#ordinal
|
|
||||||
{
|
|
||||||
hp.type |= FUNCTION_OFFSET;
|
|
||||||
hp.function = Hash(m[3].str().substr(1));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rx = wregex(L"^!([[:xdigit:]]+)(!([[:xdigit:]]+))?$", wregex::icase);
|
|
||||||
result = regex_search(start, end, m, rx);
|
|
||||||
if (result)
|
|
||||||
{
|
|
||||||
hp.type |= MODULE_OFFSET;
|
|
||||||
hp.module = std::stoul(m[1].str(), NULL, 16);
|
|
||||||
if (m[2].matched)
|
|
||||||
{
|
|
||||||
hp.type |= FUNCTION_OFFSET;
|
|
||||||
hp.function = std::stoul(m[2].str().substr(1), NULL, 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
hp.type |= MODULE_OFFSET;
|
|
||||||
hp.function = hp.addr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD ProcessCommand(const std::wstring& cmd, DWORD pid)
|
DWORD ProcessCommand(const std::wstring& cmd, DWORD pid)
|
||||||
{
|
{
|
||||||
using std::wregex;
|
using std::wregex;
|
||||||
@ -175,40 +34,40 @@ DWORD ProcessCommand(const std::wstring& cmd, DWORD pid)
|
|||||||
|
|
||||||
if (regex_match(cmd, m, wregex(L"/pn(.+)", wregex::icase)))
|
if (regex_match(cmd, m, wregex(L"/pn(.+)", wregex::icase)))
|
||||||
{
|
{
|
||||||
pid = IHF_GetPIDByName(m[1].str().c_str());
|
pid = Host_GetPIDByName(m[1].str().c_str());
|
||||||
if (pid == 0)
|
if (pid == 0)
|
||||||
return 0;
|
return 0;
|
||||||
IHF_InjectByPID(pid);
|
Host_InjectByPID(pid);
|
||||||
}
|
}
|
||||||
else if (regex_match(cmd, m, wregex(L"/p(\\d+)", wregex::icase)))
|
else if (regex_match(cmd, m, wregex(L"/p(\\d+)", wregex::icase)))
|
||||||
{
|
{
|
||||||
pid = std::stoul(m[1].str());
|
pid = std::stoul(m[1].str());
|
||||||
IHF_InjectByPID(pid);
|
Host_InjectByPID(pid);
|
||||||
}
|
}
|
||||||
else if (regex_match(cmd, m, wregex(L"/h(.+)", wregex::icase)))
|
else if (regex_match(cmd, m, wregex(L"/h(.+)", wregex::icase)))
|
||||||
{
|
{
|
||||||
HookParam hp = {};
|
HookParam hp = {};
|
||||||
if (Parse(m[1].str(), hp))
|
if (Parse(m[1].str(), hp))
|
||||||
IHF_InsertHook(pid, &hp);
|
Host_InsertHook(pid, &hp);
|
||||||
}
|
}
|
||||||
else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㅇ|연|l|)([[:xdigit:]]+)(?:-| )([[:xdigit:]]+)", wregex::icase)))
|
else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㅇ|연|l|)([[:xdigit:]]+)(?:-| )([[:xdigit:]]+)", wregex::icase)))
|
||||||
{
|
{
|
||||||
DWORD from = std::stoul(m[1].str(), NULL, 16);
|
DWORD from = std::stoul(m[1].str(), NULL, 16);
|
||||||
DWORD to = std::stoul(m[2].str(), NULL, 16);
|
DWORD to = std::stoul(m[2].str(), NULL, 16);
|
||||||
IHF_AddLink(from, to);
|
Host_AddLink(from, to);
|
||||||
}
|
}
|
||||||
else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㅎ|해|해제|u)([[:xdigit:]]+)", wregex::icase)))
|
else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㅎ|해|해제|u)([[:xdigit:]]+)", wregex::icase)))
|
||||||
{
|
{
|
||||||
DWORD from = std::stoul(m[1].str(), NULL, 16);
|
DWORD from = std::stoul(m[1].str(), NULL, 16);
|
||||||
IHF_UnLink(from);
|
Host_UnLink(from);
|
||||||
}
|
}
|
||||||
else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㄷ|도|도움|도움말|h|help)", wregex::icase)))
|
else if (regex_match(cmd, m, wregex(L"(?::|)(?:ㄷ|도|도움|도움말|h|help)", wregex::icase)))
|
||||||
{
|
{
|
||||||
ConsoleOutput(Usage);
|
ConsoleOutput(Usage);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
ConsoleOutput(L"알 수 없는 명령어. 도움말을 보시려면, :h 나 :help를 입력하세요.");
|
ConsoleOutput(L"알 수 없는 명령어. 도움말을 보시려면, :h 나 :help를 입력하세요.");
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
142
gui/language.cpp
142
gui/language.cpp
@ -14,44 +14,44 @@
|
|||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
const wchar_t* Warning=L"경고!";
|
const wchar_t* Warning = L"경고!";
|
||||||
//command.cpp
|
//command.cpp
|
||||||
const wchar_t* ErrorSyntax=L"명령어 오류";
|
const wchar_t* ErrorSyntax = L"명령어 오류";
|
||||||
const wchar_t* Usage = L"명령어:\r\n\
|
const wchar_t* Usage = L"명령어:\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
도움말 //도움말을 출력합니다\r\n\
|
도움말 //도움말을 출력합니다\r\n\
|
||||||
출발 도착 // '출발'스레드에서 '도착'스레드로 연결합니다\r\n\
|
출발 도착 // '출발'스레드에서 '도착'스레드로 연결합니다\r\n\
|
||||||
ㅎ출발 // '출발'스레드에 연결된 링크를 해제합니다\r\n\
|
ㅎ출발 // '출발'스레드에 연결된 링크를 해제합니다\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
'출발'과 '도착'에는 16진법(헥사코드) 스레드번호를 입력합니다. 스레드 번호는 맨 앞에 있는 첫 번째 숫자열입니다.\r\n\
|
'출발'과 '도착'에는 16진법(헥사코드) 스레드번호를 입력합니다. 스레드 번호는 맨 앞에 있는 첫 번째 숫자열입니다.\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
로더 옵션:\r\n\
|
로더 옵션:\r\n\
|
||||||
/P[{process_id|Nprocess_name}] //프로세스에 부착\r\n\
|
/P[{process_id|Nprocess_name}] //프로세스에 부착\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
H코드 후킹 옵션:\r\n\
|
H코드 후킹 옵션:\r\n\
|
||||||
/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:module[:{name|#ordinal}]]\r\n\
|
/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:module[:{name|#ordinal}]]\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
(서수를 제외한) /H코드의 모든 숫자는 아무것도 처리되지 않은 16진법(헥사코드)입니다";
|
(서수를 제외한) /H코드의 모든 숫자는 아무것도 처리되지 않은 16진법(헥사코드)입니다";
|
||||||
|
|
||||||
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\
|
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\
|
||||||
\r\n\
|
\r\n\
|
||||||
추가 사용자정의 후킹설정\r\n\
|
추가 사용자정의 후킹설정\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
후킹 종류 :\r\n\
|
후킹 종류 :\r\n\
|
||||||
A - DBCS 문자\r\n\
|
A - DBCS 문자\r\n\
|
||||||
B - DBCS 문자(big-endian)\r\n\
|
B - DBCS 문자(big-endian)\r\n\
|
||||||
W - UCS2 문자\r\n\
|
W - UCS2 문자\r\n\
|
||||||
S - MBCS 문자열\r\n\
|
S - MBCS 문자열\r\n\
|
||||||
Q - UTF-16 문자열\r\n\
|
Q - UTF-16 문자열\r\n\
|
||||||
\r\n\
|
\r\n\
|
||||||
매개변수:\r\n\
|
매개변수:\r\n\
|
||||||
X - 하드웨어 구획점 사용\r\n\
|
X - 하드웨어 구획점 사용\r\n\
|
||||||
N - 문법을 사용하지 않음\r\n\
|
N - 문법을 사용하지 않음\r\n\
|
||||||
data_offset - stack offset to char / string pointer\r\n\
|
data_offset - stack offset to char / string pointer\r\n\
|
||||||
drdo - add a level of indirection to data_offset\r\n\
|
drdo - add a level of indirection to data_offset\r\n\
|
||||||
sub_offset - stack offset to subcontext\r\n\
|
sub_offset - stack offset to subcontext\r\n\
|
||||||
drso - add a level of indirection to sub_offset\r\n\
|
drso - add a level of indirection to sub_offset\r\n\
|
||||||
addr - 후킹할 주소\r\n\
|
addr - 후킹할 주소\r\n\
|
||||||
module - name of the module to use as base for 'addr'\r\n\
|
module - name of the module to use as base for 'addr'\r\n\
|
||||||
name - name of the 'module' export to use as base for 'addr'\r\n\
|
name - name of the 'module' export to use as base for 'addr'\r\n\
|
||||||
ordinal - number of the 'module' export ordinal to use as base for 'addr'\r\n\
|
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\
|
|||||||
All numbers except ordinal are hexadecimal without any prefixes";
|
All numbers except ordinal are hexadecimal without any prefixes";
|
||||||
|
|
||||||
//inject.cpp
|
//inject.cpp
|
||||||
const wchar_t* ErrorRemoteThread=L"원격 스레드를 생성할 수 없음.";
|
const wchar_t* ErrorRemoteThread = L"원격 스레드를 생성할 수 없음.";
|
||||||
const wchar_t* ErrorOpenProcess=L"프로세스를 열 수 없음.";
|
const wchar_t* ErrorOpenProcess = L"프로세스를 열 수 없음.";
|
||||||
const wchar_t* ErrorNoProcess=L"프로세스를 찾을 수 없음";
|
const wchar_t* ErrorNoProcess = L"프로세스를 찾을 수 없음";
|
||||||
const wchar_t* SelfAttach=L"ITH.exe에 부착하지 말아 주세요";
|
const wchar_t* SelfAttach = L"ITH.exe에 부착하지 말아 주세요";
|
||||||
const wchar_t* AlreadyAttach=L"프로세스가 이미 부착됨.";
|
const wchar_t* AlreadyAttach = L"프로세스가 이미 부착됨.";
|
||||||
const wchar_t* FormatInject=L"프로세스 %d에 인젝션. 모듈 기반 %.8X";
|
const wchar_t* FormatInject = L"프로세스 %d에 인젝션. 모듈 기반 %.8X";
|
||||||
//main.cpp
|
//main.cpp
|
||||||
const wchar_t* NotAdmin=L"SeDebugPrevilege을 활성화 할 수 없습니다. ITH가 제대로 작동하지 못합니다.\r\n\
|
const wchar_t* NotAdmin = L"SeDebugPrevilege을 활성화 할 수 없습니다. ITH가 제대로 작동하지 못합니다.\r\n\
|
||||||
관리자 계정으로 실행하시거나 UAC를 끄시고 ITH를 실행해 주세요.";
|
관리자 계정으로 실행하시거나 UAC를 끄시고 ITH를 실행해 주세요.";
|
||||||
//pipe.cpp
|
//pipe.cpp
|
||||||
const wchar_t* ErrorCreatePipe=L"텍스트 파이프를 생성할 수 없거나, 요청이 너무 많습니다.";
|
const wchar_t* ErrorCreatePipe = L"텍스트 파이프를 생성할 수 없거나, 요청이 너무 많습니다.";
|
||||||
const wchar_t* FormatDetach=L"프로세스 %d가 탈착됨.";
|
const wchar_t* FormatDetach = L"프로세스 %d가 탈착됨.";
|
||||||
const wchar_t* ErrorCmdQueueFull=L"명령어 대기열이 가득참.";
|
const wchar_t* ErrorCmdQueueFull = L"명령어 대기열이 가득참.";
|
||||||
const wchar_t* ErrorNoAttach=L"프로세스가 부착되지 않음.";
|
const wchar_t* ErrorNoAttach = L"프로세스가 부착되지 않음.";
|
||||||
|
|
||||||
//profile.cpp
|
//profile.cpp
|
||||||
const wchar_t* ErrorMonitor=L"프로세스를 감시할 수 없음.";
|
const wchar_t* ErrorMonitor = L"프로세스를 감시할 수 없음.";
|
||||||
//utility.cpp
|
//utility.cpp
|
||||||
const wchar_t* InitMessage = L"Copyright (C) 2010-2012 kaosu <qiupf2000@gmail.com>\r\n\
|
const wchar_t* InitMessage = L"Copyright (C) 2010-2012 kaosu <qiupf2000@gmail.com>\r\n\
|
||||||
Copyright (C) 2015 zorkzero <zorkzero@hotmail.com>\r\n\
|
Copyright (C) 2015 zorkzero <zorkzero@hotmail.com>\r\n\
|
||||||
소스코드 <https://code.google.com/p/interactive-text-hooker/>\r\n\
|
소스코드 <https://code.google.com/p/interactive-text-hooker/>\r\n\
|
||||||
일반토론 <https://groups.google.com/forum/?fromgroups#!forum/interactive-text-hooker>\r\n\
|
일반토론 <https://groups.google.com/forum/?fromgroups#!forum/interactive-text-hooker>\r\n\
|
||||||
한글화 @mireado<https://twitter.com/mireado>";
|
한글화 @mireado<https://twitter.com/mireado>";
|
||||||
const wchar_t* BackgroundMsg=L"도움말을 보시려면, \"help\", \"도움말\"이나 \"도움\"을 입력하세요.";
|
const wchar_t* BackgroundMsg = L"도움말을 보시려면, \"help\", \"도움말\"이나 \"도움\"을 입력하세요.";
|
||||||
const wchar_t* ErrorLinkExist=L"연결이 존재함.";
|
const wchar_t* ErrorLinkExist = L"연결이 존재함.";
|
||||||
const wchar_t* ErrorCylicLink=L"연결실패. 순환연결은 허용되지 않습니다.";
|
const wchar_t* ErrorCylicLink = L"연결실패. 순환연결은 허용되지 않습니다.";
|
||||||
const wchar_t* FormatLink=L"출발스레드%.4x에서 도착스레드%.4x로 연결.";
|
const wchar_t* FormatLink = L"출발스레드%.4x에서 도착스레드%.4x로 연결.";
|
||||||
const wchar_t* ErrorLink=L"연결실패. 출발/도착 스레드를 찾을 수 없음.";
|
const wchar_t* ErrorLink = L"연결실패. 출발/도착 스레드를 찾을 수 없음.";
|
||||||
const wchar_t* ErrorDeleteCombo=L"글상자에서 지우기 실패.";
|
const wchar_t* ErrorDeleteCombo = L"글상자에서 지우기 실패.";
|
||||||
|
|
||||||
//window.cpp
|
//window.cpp
|
||||||
const wchar_t* ClassName = L"ITH";
|
const wchar_t* ClassName = L"ITH";
|
||||||
const wchar_t* ClassNameAdmin=L"ITH (관리자)";
|
const wchar_t* ClassNameAdmin = L"ITH (관리자)";
|
||||||
const wchar_t* ErrorNotSplit=L"먼저 문단 나누기를 활성화해주세요!";
|
const wchar_t* ErrorNotSplit = L"먼저 문단 나누기를 활성화해주세요!";
|
||||||
const wchar_t* ErrorNotModule=L"먼저 모듈을 활성화해주세요!";
|
const wchar_t* ErrorNotModule = L"먼저 모듈을 활성화해주세요!";
|
||||||
//Main window buttons
|
//Main window buttons
|
||||||
const wchar_t* ButtonTitleProcess=L"프로세스";
|
const wchar_t* ButtonTitleProcess = L"프로세스";
|
||||||
const wchar_t* ButtonTitleThread=L"스레드";
|
const wchar_t* ButtonTitleThread = L"스레드";
|
||||||
const wchar_t* ButtonTitleHook=L"후킹";
|
const wchar_t* ButtonTitleHook = L"후킹";
|
||||||
const wchar_t* ButtonTitleProfile=L"프로필";
|
const wchar_t* ButtonTitleProfile = L"프로필";
|
||||||
const wchar_t* ButtonTitleOption=L"옵션";
|
const wchar_t* ButtonTitleOption = L"옵션";
|
||||||
const wchar_t* ButtonTitleClear=L"지우기";
|
const wchar_t* ButtonTitleClear = L"지우기";
|
||||||
const wchar_t* ButtonTitleSave=L"저장";
|
const wchar_t* ButtonTitleSave = L"저장";
|
||||||
const wchar_t* ButtonTitleTop=L"항상위";
|
const wchar_t* ButtonTitleTop = L"항상위";
|
||||||
//Hook window
|
//Hook window
|
||||||
const wchar_t* SpecialHook=L"H코드 후킹, AGTH 코드는 지원하지 않습니다.";
|
const wchar_t* SpecialHook = L"H코드 후킹, AGTH 코드는 지원하지 않습니다.";
|
||||||
//Process window
|
//Process window
|
||||||
const wchar_t* TabTitlePID = L"PID";
|
const wchar_t* TabTitlePID = L"PID";
|
||||||
const wchar_t* TabTitleMemory=L"메모리";
|
const wchar_t* TabTitleMemory = L"메모리";
|
||||||
const wchar_t* TabTitleName=L"이름";
|
const wchar_t* TabTitleName = L"이름";
|
||||||
const wchar_t* TabTitleTID = L"TID";
|
const wchar_t* TabTitleTID = L"TID";
|
||||||
const wchar_t* TabTitleStart=L"시작";
|
const wchar_t* TabTitleStart = L"시작";
|
||||||
const wchar_t* TabTitleModule=L"모듈";
|
const wchar_t* TabTitleModule = L"모듈";
|
||||||
const wchar_t* TabTitleState=L"상태";
|
const wchar_t* TabTitleState = L"상태";
|
||||||
const wchar_t* SuccessAttach=L"프로세스에 ITH 부착성공.";
|
const wchar_t* SuccessAttach = L"프로세스에 ITH 부착성공.";
|
||||||
const wchar_t* FailAttach=L"프로세스에 ITH 부착실패.";
|
const wchar_t* FailAttach = L"프로세스에 ITH 부착실패.";
|
||||||
const wchar_t* SuccessDetach=L"프로세스에서 ITH 탈착성공.";
|
const wchar_t* SuccessDetach = L"프로세스에서 ITH 탈착성공.";
|
||||||
const wchar_t* FailDetach=L"ITH 탈착실패.";
|
const wchar_t* FailDetach = L"ITH 탈착실패.";
|
||||||
//Profile window
|
//Profile window
|
||||||
const wchar_t* ProfileExist=L"프로필이 이미 존재함.";
|
const wchar_t* ProfileExist = L"프로필이 이미 존재함.";
|
||||||
const wchar_t* SuccessAddProfile=L"프로필 추가됨.";
|
const wchar_t* SuccessAddProfile = L"프로필 추가됨.";
|
||||||
const wchar_t* FailAddProfile=L"프로필 추가실패";
|
const wchar_t* FailAddProfile = L"프로필 추가실패";
|
||||||
const wchar_t* TabTitleNumber = L"No.";
|
const wchar_t* TabTitleNumber = L"No.";
|
||||||
const wchar_t* NoFile=L"파일을 찾을 수 없음.";
|
const wchar_t* NoFile = L"파일을 찾을 수 없음.";
|
||||||
const wchar_t* PathDismatch=L"프로세스 이름이 일치하지 않습니다, 계속하시겠습니까?";
|
const wchar_t* PathDismatch = L"프로세스 이름이 일치하지 않습니다, 계속하시겠습니까?";
|
||||||
const wchar_t* SuccessImportProfile=L"프로필 가져오기 성공";
|
const wchar_t* SuccessImportProfile = L"프로필 가져오기 성공";
|
||||||
//const wchar_t* SuccessAddProfile=L"Profile added.";
|
//const wchar_t* SuccessAddProfile=L"Profile added.";
|
39
gui/main.cpp
39
gui/main.cpp
@ -16,11 +16,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ITH.h"
|
#include "ITH.h"
|
||||||
#include "ith/host/srv.h"
|
#include "host/host.h"
|
||||||
#include "ith/host/hookman.h"
|
#include "host/hookman.h"
|
||||||
#include "ith/host/SettingManager.h"
|
#include "host/settings.h"
|
||||||
#include "CustomFilter.h"
|
#include "CustomFilter.h"
|
||||||
#include "profile.h"
|
#include "profile/Profile.h"
|
||||||
#include "ProfileManager.h"
|
#include "ProfileManager.h"
|
||||||
|
|
||||||
HINSTANCE hIns;
|
HINSTANCE hIns;
|
||||||
@ -39,7 +39,7 @@ extern "C" {
|
|||||||
CustomFilter* uni_filter;
|
CustomFilter* uni_filter;
|
||||||
CustomFilter* mb_filter;
|
CustomFilter* mb_filter;
|
||||||
HookManager* man;
|
HookManager* man;
|
||||||
SettingManager* setman;
|
Settings* setman;
|
||||||
LONG split_time, cyclic_remove, global_filter;
|
LONG split_time, cyclic_remove, global_filter;
|
||||||
LONG process_time, inject_delay, insert_delay,
|
LONG process_time, inject_delay, insert_delay,
|
||||||
auto_inject, auto_insert, clipboard_flag;
|
auto_inject, auto_insert, clipboard_flag;
|
||||||
@ -69,11 +69,13 @@ void RecordUniChar(WORD uni, PVOID f)
|
|||||||
|
|
||||||
void SaveSettings()
|
void SaveSettings()
|
||||||
{
|
{
|
||||||
GetWindowRect(hMainWnd, &window);
|
WINDOWPLACEMENT wndpl;
|
||||||
setting[L"window_left"] = window.left;
|
wndpl.length = sizeof(WINDOWPLACEMENT);
|
||||||
setting[L"window_right"] = window.right;
|
GetWindowPlacement(hMainWnd, &wndpl);
|
||||||
setting[L"window_top"] = window.top;
|
setting[L"window_left"] = wndpl.rcNormalPosition.left;
|
||||||
setting[L"window_bottom"] = window.bottom;
|
setting[L"window_right"] = wndpl.rcNormalPosition.right;
|
||||||
|
setting[L"window_top"] = wndpl.rcNormalPosition.top;
|
||||||
|
setting[L"window_bottom"] = wndpl.rcNormalPosition.bottom;
|
||||||
setting[L"split_time"] = split_time;
|
setting[L"split_time"] = split_time;
|
||||||
setting[L"process_time"] = process_time;
|
setting[L"process_time"] = process_time;
|
||||||
setting[L"inject_delay"] = inject_delay;
|
setting[L"inject_delay"] = inject_delay;
|
||||||
@ -240,15 +242,16 @@ LONG WINAPI UnhandledExcept(_EXCEPTION_POINTERS *ExceptionInfo)
|
|||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
||||||
{
|
{
|
||||||
|
InitCommonControls();
|
||||||
if (!IthInitSystemService())
|
if (!IthInitSystemService())
|
||||||
TerminateProcess(GetCurrentProcess(), 0);
|
TerminateProcess(GetCurrentProcess(), 0);
|
||||||
CreateMutex(NULL, TRUE, L"ITH_MAIN_RUNNING");
|
CreateMutex(NULL, TRUE, L"ITH_MAIN_RUNNING");
|
||||||
if (IHF_Init())
|
if (Host_Open())
|
||||||
{
|
{
|
||||||
SetUnhandledExceptionFilter(UnhandledExcept);
|
SetUnhandledExceptionFilter(UnhandledExcept);
|
||||||
IHF_GetHookManager(&man);
|
Host_GetHookManager(&man);
|
||||||
IHF_GetSettingManager(&setman);
|
Host_GetSettings(&setman);
|
||||||
setman->SetValue(SETTING_SPLIT_TIME, 200);
|
setman->splittingInterval = 200;
|
||||||
MonitorFlag = true;
|
MonitorFlag = true;
|
||||||
pfman = new ProfileManager();
|
pfman = new ProfileManager();
|
||||||
mb_filter = new CustomFilter();
|
mb_filter = new CustomFilter();
|
||||||
@ -256,11 +259,11 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
|
|||||||
DefaultSettings();
|
DefaultSettings();
|
||||||
LoadSettings();
|
LoadSettings();
|
||||||
InitializeSettings();
|
InitializeSettings();
|
||||||
setman->SetValue(SETTING_SPLIT_TIME, split_time);
|
setman->splittingInterval = split_time;
|
||||||
setman->SetValue(SETTING_CLIPFLAG, clipboard_flag);
|
setman->clipboardFlag = clipboard_flag > 0;
|
||||||
hIns = hInstance;
|
hIns = hInstance;
|
||||||
MyRegisterClass(hIns);
|
MyRegisterClass(hIns);
|
||||||
InitInstance(hIns, IHF_IsAdmin(), &window);
|
InitInstance(hIns, FALSE, &window);
|
||||||
MSG msg;
|
MSG msg;
|
||||||
while (GetMessage(&msg, NULL, 0, 0))
|
while (GetMessage(&msg, NULL, 0, 0))
|
||||||
{
|
{
|
||||||
@ -277,7 +280,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
|
|||||||
{
|
{
|
||||||
FindITH();
|
FindITH();
|
||||||
}
|
}
|
||||||
IHF_Cleanup();
|
Host_Close();
|
||||||
IthCloseSystemService();
|
IthCloseSystemService();
|
||||||
TerminateProcess(GetCurrentProcess(), 0);
|
TerminateProcess(GetCurrentProcess(), 0);
|
||||||
}
|
}
|
||||||
|
@ -16,10 +16,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
#include "ith/host/srv.h"
|
#include "host/host.h"
|
||||||
#include "ith/host/hookman.h"
|
#include "host/hookman.h"
|
||||||
#include "ith/common/types.h"
|
#include "vnrhook/include/types.h"
|
||||||
#include "ith/common/const.h"
|
#include "vnrhook/include/const.h"
|
||||||
|
#include "profile/misc.h"
|
||||||
|
|
||||||
extern HookManager* man; // main.cpp
|
extern HookManager* man; // main.cpp
|
||||||
|
|
||||||
@ -69,7 +70,7 @@ std::wstring GetWindowsPath(const std::wstring& path)
|
|||||||
std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2
|
std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2
|
||||||
std::wstring dosDrive = GetDriveLetter(devicePath); // C:
|
std::wstring dosDrive = GetDriveLetter(devicePath); // C:
|
||||||
if (dosDrive.empty())
|
if (dosDrive.empty())
|
||||||
return L"";
|
return path;
|
||||||
std::wstring dosPath = dosDrive; // C:
|
std::wstring dosPath = dosDrive; // C:
|
||||||
dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe
|
dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe
|
||||||
return dosPath;
|
return dosPath;
|
||||||
@ -117,16 +118,16 @@ std::wstring GetCode(const HookParam& hp, DWORD pid)
|
|||||||
code += c;
|
code += c;
|
||||||
if (hp.type & NO_CONTEXT)
|
if (hp.type & NO_CONTEXT)
|
||||||
code += L'N';
|
code += L'N';
|
||||||
if (hp.off >> 31)
|
if (hp.offset >> 31)
|
||||||
code += L"-" + ToHexString(-(hp.off + 4));
|
code += L"-" + ToHexString(-(hp.offset + 4));
|
||||||
else
|
else
|
||||||
code += ToHexString(hp.off);
|
code += ToHexString(hp.offset);
|
||||||
if (hp.type & DATA_INDIRECT)
|
if (hp.type & DATA_INDIRECT)
|
||||||
{
|
{
|
||||||
if (hp.ind >> 31)
|
if (hp.index >> 31)
|
||||||
code += L"*-" + ToHexString(-hp.ind);
|
code += L"*-" + ToHexString(-hp.index);
|
||||||
else
|
else
|
||||||
code += L"*" + ToHexString(hp.ind);
|
code += L"*" + ToHexString(hp.index);
|
||||||
}
|
}
|
||||||
if (hp.type & USING_SPLIT)
|
if (hp.type & USING_SPLIT)
|
||||||
{
|
{
|
||||||
@ -137,21 +138,21 @@ std::wstring GetCode(const HookParam& hp, DWORD pid)
|
|||||||
}
|
}
|
||||||
if (hp.type & SPLIT_INDIRECT)
|
if (hp.type & SPLIT_INDIRECT)
|
||||||
{
|
{
|
||||||
if (hp.split_ind >> 31)
|
if (hp.split_index >> 31)
|
||||||
code += L"*-" + ToHexString(-hp.split_ind);
|
code += L"*-" + ToHexString(-hp.split_index);
|
||||||
else
|
else
|
||||||
code += L"*" + ToHexString(hp.split_ind);
|
code += L"*" + ToHexString(hp.split_index);
|
||||||
}
|
}
|
||||||
if (pid)
|
if (pid)
|
||||||
{
|
{
|
||||||
PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.addr);
|
PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.address);
|
||||||
if (allocationBase)
|
if (allocationBase)
|
||||||
{
|
{
|
||||||
std::wstring path = GetModuleFileNameAsString(pid, allocationBase);
|
std::wstring path = GetModuleFileNameAsString(pid, allocationBase);
|
||||||
if (!path.empty())
|
if (!path.empty())
|
||||||
{
|
{
|
||||||
auto fileName = path.substr(path.rfind(L'\\') + 1);
|
auto fileName = path.substr(path.rfind(L'\\') + 1);
|
||||||
DWORD relativeHookAddress = hp.addr - (DWORD)allocationBase;
|
DWORD relativeHookAddress = hp.address - (DWORD)allocationBase;
|
||||||
code += L"@" + ToHexString(relativeHookAddress) + L":" + fileName;
|
code += L"@" + ToHexString(relativeHookAddress) + L":" + fileName;
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
@ -159,20 +160,20 @@ std::wstring GetCode(const HookParam& hp, DWORD pid)
|
|||||||
}
|
}
|
||||||
if (hp.module)
|
if (hp.module)
|
||||||
{
|
{
|
||||||
code += L"@" + ToHexString(hp.addr) + L"!" + ToHexString(hp.module);
|
code += L"@" + ToHexString(hp.address) + L"!" + ToHexString(hp.module);
|
||||||
if (hp.function)
|
if (hp.function)
|
||||||
code += L"!" + ToHexString(hp.function);
|
code += L"!" + ToHexString(hp.function);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// hack, the original address is stored in the function field
|
// Hack. The original address is stored in the function field
|
||||||
// if (module == NULL && function != NULL)
|
// if (module == NULL && function != NULL).
|
||||||
// in TextHook::UnsafeInsertHookCode() MODULE_OFFSET and FUNCTION_OFFSET are removed from
|
// MODULE_OFFSET and FUNCTION_OFFSET are removed from HookParam.type in
|
||||||
// HookParam.type
|
// TextHook::UnsafeInsertHookCode() and can not be used here.
|
||||||
if (hp.function)
|
if (hp.function)
|
||||||
code += L"@" + ToHexString(hp.function);
|
code += L"@" + ToHexString(hp.function);
|
||||||
else
|
else
|
||||||
code += L"@" + ToHexString(hp.addr) + L":";
|
code += L"@" + ToHexString(hp.address) + L":";
|
||||||
}
|
}
|
||||||
return code;
|
return code;
|
||||||
}
|
}
|
||||||
@ -282,13 +283,13 @@ HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition)
|
|||||||
return CreateFile(path.c_str(), option, share, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
return CreateFile(path.c_str(), option, share, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
//SJIS->Unicode. 'mb' must be null-terminated. 'wc_length' is the length of 'wc' in characters.
|
//SJIS->Unicode. mb must be null-terminated. wc_length is the length of wc in characters.
|
||||||
int MB_WC(const char* mb, wchar_t* wc, int wc_length)
|
int MB_WC(const char* mb, wchar_t* wc, int wc_length)
|
||||||
{
|
{
|
||||||
return MultiByteToWideChar(932, 0, mb, -1, wc, wc_length);
|
return MultiByteToWideChar(932, 0, mb, -1, wc, wc_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Count characters in wide string. 'mb_length' is the number of bytes from 'mb' to convert or
|
// Count characters in wide string. mb_length is the number of bytes from mb to convert or
|
||||||
// -1 if the string is null terminated.
|
// -1 if the string is null terminated.
|
||||||
int MB_WC_count(const char* mb, int mb_length)
|
int MB_WC_count(const char* mb, int mb_length)
|
||||||
{
|
{
|
||||||
@ -300,12 +301,3 @@ int WC_MB(const wchar_t *wc, char* mb, int mb_length)
|
|||||||
{
|
{
|
||||||
return WideCharToMultiByte(932, 0, wc, -1, mb, mb_length, NULL, NULL);
|
return WideCharToMultiByte(932, 0, wc, -1, mb, mb_length, NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD Hash(const std::wstring& module, int length)
|
|
||||||
{
|
|
||||||
DWORD hash = 0;
|
|
||||||
auto end = length < 0 || static_cast<std::size_t>(length) > module.length() ? module.end() : module.begin() + length;
|
|
||||||
for (auto it = module.begin(); it != end; ++it)
|
|
||||||
hash = _rotr(hash, 7) + *it;
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ITH.h"
|
#include "ITH.h"
|
||||||
|
|
||||||
struct HookParam;
|
struct HookParam;
|
||||||
struct ProcessRecord;
|
struct ProcessRecord;
|
||||||
|
|
||||||
DWORD Hash(const std::wstring& module, int length = -1);
|
|
||||||
DWORD ProcessCommand(const std::wstring& cmd, DWORD pid);
|
DWORD ProcessCommand(const std::wstring& cmd, DWORD pid);
|
||||||
std::wstring GetProcessPath(DWORD pid);
|
std::wstring GetProcessPath(DWORD pid);
|
||||||
void ConsoleOutput(LPCWSTR);
|
void ConsoleOutput(LPCWSTR);
|
||||||
@ -58,13 +58,6 @@ int MB_WC_count(const char* mb, int mb_length);
|
|||||||
int WC_MB(const wchar_t *wc, char* mb, int mb_length);
|
int WC_MB(const wchar_t *wc, char* mb, int mb_length);
|
||||||
bool Parse(const std::wstring& cmd, HookParam& hp);
|
bool Parse(const std::wstring& cmd, HookParam& hp);
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
std::wstring ToHexString(T i) {
|
|
||||||
std::wstringstream ss;
|
|
||||||
ss << std::uppercase << std::hex << i;
|
|
||||||
return ss.str();
|
|
||||||
}
|
|
||||||
|
|
||||||
// http://jrdodds.blogs.com/blog/2004/08/raii_in_c.html
|
// http://jrdodds.blogs.com/blog/2004/08/raii_in_c.html
|
||||||
class CriticalSection
|
class CriticalSection
|
||||||
{
|
{
|
||||||
|
166
gui/window.cpp
166
gui/window.cpp
@ -18,15 +18,16 @@
|
|||||||
#include "ProcessWindow.h"
|
#include "ProcessWindow.h"
|
||||||
#include "resource.h"
|
#include "resource.h"
|
||||||
#include "language.h"
|
#include "language.h"
|
||||||
#include "ith/host/srv.h"
|
#include "host/host.h"
|
||||||
#include "ith/host/hookman.h"
|
#include "host/hookman.h"
|
||||||
#include "ith/common/const.h"
|
#include "vnrhook/include/const.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "ProfileManager.h"
|
#include "ProfileManager.h"
|
||||||
#include "ith/host/SettingManager.h"
|
#include "host/settings.h"
|
||||||
#include "CustomFilter.h"
|
#include "CustomFilter.h"
|
||||||
#include "Profile.h"
|
#include "profile/Profile.h"
|
||||||
#include "TextBuffer.h"
|
#include "TextBuffer.h"
|
||||||
|
#include "profile/misc.h"
|
||||||
|
|
||||||
#define CMD_SIZE 512
|
#define CMD_SIZE 512
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ extern ProfileManager* pfman; // ProfileManager.cpp
|
|||||||
extern HookManager* man; // main.cpp
|
extern HookManager* man; // main.cpp
|
||||||
extern CustomFilter* mb_filter; // main.cpp
|
extern CustomFilter* mb_filter; // main.cpp
|
||||||
extern CustomFilter* uni_filter; // main.cpp
|
extern CustomFilter* uni_filter; // main.cpp
|
||||||
extern SettingManager* setman; // main.cpp
|
extern Settings* setman; // main.cpp
|
||||||
#define COMMENT_BUFFER_LENGTH 512
|
#define COMMENT_BUFFER_LENGTH 512
|
||||||
static WCHAR comment_buffer[COMMENT_BUFFER_LENGTH];
|
static WCHAR comment_buffer[COMMENT_BUFFER_LENGTH];
|
||||||
|
|
||||||
@ -55,7 +56,6 @@ void SaveSettings(); // main.cpp
|
|||||||
extern LONG split_time, process_time, inject_delay, insert_delay,
|
extern LONG split_time, process_time, inject_delay, insert_delay,
|
||||||
auto_inject, auto_insert, clipboard_flag, cyclic_remove, global_filter; //main.cpp
|
auto_inject, auto_insert, clipboard_flag, cyclic_remove, global_filter; //main.cpp
|
||||||
static int last_select, last_edit;
|
static int last_select, last_edit;
|
||||||
void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const TextThread& thread);
|
|
||||||
|
|
||||||
ATOM MyRegisterClass(HINSTANCE hInstance)
|
ATOM MyRegisterClass(HINSTANCE hInstance)
|
||||||
{
|
{
|
||||||
@ -137,8 +137,8 @@ BOOL CALLBACK OptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||||||
clipboard_flag = IsDlgButtonChecked(hDlg, IDC_CHECK3);
|
clipboard_flag = IsDlgButtonChecked(hDlg, IDC_CHECK3);
|
||||||
cyclic_remove = IsDlgButtonChecked(hDlg, IDC_CHECK4);
|
cyclic_remove = IsDlgButtonChecked(hDlg, IDC_CHECK4);
|
||||||
global_filter = IsDlgButtonChecked(hDlg, IDC_CHECK5);
|
global_filter = IsDlgButtonChecked(hDlg, IDC_CHECK5);
|
||||||
setman->SetValue(SETTING_CLIPFLAG, clipboard_flag);
|
setman->clipboardFlag = clipboard_flag;
|
||||||
setman->SetValue(SETTING_SPLIT_TIME, split_time);
|
setman->splittingInterval = split_time;
|
||||||
if (auto_inject == 0) auto_insert = 0;
|
if (auto_inject == 0) auto_insert = 0;
|
||||||
}
|
}
|
||||||
case IDCANCEL:
|
case IDCANCEL:
|
||||||
@ -187,10 +187,10 @@ BOOL CALLBACK ProcessDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||||||
pswnd->DetachProcess();
|
pswnd->DetachProcess();
|
||||||
break;
|
break;
|
||||||
case IDC_BUTTON5:
|
case IDC_BUTTON5:
|
||||||
pswnd->AddCurrentToProfile();
|
pswnd->CreateProfileForSelectedProcess();
|
||||||
break;
|
break;
|
||||||
case IDC_BUTTON6:
|
case IDC_BUTTON6:
|
||||||
pswnd->RemoveCurrentFromProfile();
|
pswnd->DeleteProfileForSelectedProcess();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -280,19 +280,19 @@ LRESULT CALLBACK EditCmdProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPar
|
|||||||
|
|
||||||
void CreateButtons(HWND hWnd)
|
void CreateButtons(HWND hWnd)
|
||||||
{
|
{
|
||||||
hwndProcess = CreateWindow(L"Button", L"프로세스", WS_CHILD | WS_VISIBLE,
|
hwndProcess = CreateWindow(L"Button", L"프로세스", WS_CHILD | WS_VISIBLE,
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||||
hwndOption = CreateWindow(L"Button", L"옵션", WS_CHILD | WS_VISIBLE,
|
hwndOption = CreateWindow(L"Button", L"옵션", WS_CHILD | WS_VISIBLE,
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||||
hwndClear = CreateWindow(L"Button", L"지우기", WS_CHILD | WS_VISIBLE,
|
hwndClear = CreateWindow(L"Button", L"지우기", WS_CHILD | WS_VISIBLE,
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||||
hwndSave = CreateWindow(L"Button", L"저장", WS_CHILD | WS_VISIBLE,
|
hwndSave = CreateWindow(L"Button", L"저장", WS_CHILD | WS_VISIBLE,
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||||
hwndRemoveLink = CreateWindow(L"Button", L"링크해제", WS_CHILD | WS_VISIBLE,
|
hwndRemoveLink = CreateWindow(L"Button", L"링크해제", WS_CHILD | WS_VISIBLE,
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||||
hwndRemoveHook = CreateWindow(L"Button", L"후킹해제", WS_CHILD | WS_VISIBLE,
|
hwndRemoveHook = CreateWindow(L"Button", L"후킹해제", WS_CHILD | WS_VISIBLE,
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||||
hwndTop = CreateWindow(L"Button", L"항상위", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE | BS_CHECKBOX,
|
hwndTop = CreateWindow(L"Button", L"항상위", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE | BS_CHECKBOX,
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||||
hwndProcessComboBox = CreateWindow(L"ComboBox", NULL,
|
hwndProcessComboBox = CreateWindow(L"ComboBox", NULL,
|
||||||
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
|
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
|
||||||
@ -363,7 +363,7 @@ void ClickButton(HWND hWnd, HWND h)
|
|||||||
DWORD pid = std::stoul(str);
|
DWORD pid = std::stoul(str);
|
||||||
SaveProcessProfile(pid);
|
SaveProcessProfile(pid);
|
||||||
}
|
}
|
||||||
pfman->SaveProfile();
|
pfman->SaveProfiles();
|
||||||
}
|
}
|
||||||
else if (h == hwndRemoveLink)
|
else if (h == hwndRemoveLink)
|
||||||
{
|
{
|
||||||
@ -372,7 +372,7 @@ void ClickButton(HWND hWnd, HWND h)
|
|||||||
{
|
{
|
||||||
DWORD from = std::stoul(str, NULL, 16);
|
DWORD from = std::stoul(str, NULL, 16);
|
||||||
if (from != 0)
|
if (from != 0)
|
||||||
IHF_UnLink(from);
|
Host_UnLink(from);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (h == hwndRemoveHook)
|
else if (h == hwndRemoveHook)
|
||||||
@ -388,7 +388,7 @@ void ClickButton(HWND hWnd, HWND h)
|
|||||||
entry = entry.substr(i + 1);
|
entry = entry.substr(i + 1);
|
||||||
DWORD addr = std::stoul(entry, NULL, 16);
|
DWORD addr = std::stoul(entry, NULL, 16);
|
||||||
if (threadNumber != 0)
|
if (threadNumber != 0)
|
||||||
IHF_RemoveHook(pid, addr);
|
Host_RemoveHook(pid, addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -498,11 +498,16 @@ bool GetHookParam(DWORD pid, DWORD hook_addr, HookParam& hp)
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddToCombo(TextThread& thread, bool replace)
|
std::wstring GetEntryString(TextThread& thread)
|
||||||
{
|
{
|
||||||
WCHAR entry[512];
|
CHAR entry[512];
|
||||||
thread.GetEntryString(entry, 512);
|
thread.GetEntryString(entry, 512);
|
||||||
std::wstring entryWithLink(entry);
|
return toUnicodeString(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring CreateEntryWithLink(TextThread& thread, std::wstring& entry)
|
||||||
|
{
|
||||||
|
std::wstring entryWithLink = entry;
|
||||||
if (thread.Link())
|
if (thread.Link())
|
||||||
entryWithLink += L"->" + ToHexString(thread.LinkNumber());
|
entryWithLink += L"->" + ToHexString(thread.LinkNumber());
|
||||||
if (thread.PID() == 0)
|
if (thread.PID() == 0)
|
||||||
@ -510,7 +515,14 @@ void AddToCombo(TextThread& thread, bool replace)
|
|||||||
HookParam hp = {};
|
HookParam hp = {};
|
||||||
if (GetHookParam(thread.PID(), thread.Addr(), hp))
|
if (GetHookParam(thread.PID(), thread.Addr(), hp))
|
||||||
entryWithLink += L" (" + GetCode(hp, thread.PID()) + L")";
|
entryWithLink += L" (" + GetCode(hp, thread.PID()) + L")";
|
||||||
int i = ComboBox_FindString(hwndCombo, 0, entry);
|
return entryWithLink;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AddToCombo(TextThread& thread, bool replace)
|
||||||
|
{
|
||||||
|
std::wstring entry = GetEntryString(thread);
|
||||||
|
std::wstring entryWithLink = CreateEntryWithLink(thread, entry);
|
||||||
|
int i = ComboBox_FindString(hwndCombo, -1, entry.c_str());
|
||||||
if (replace)
|
if (replace)
|
||||||
{
|
{
|
||||||
int sel = ComboBox_GetCurSel(hwndCombo);
|
int sel = ComboBox_GetCurSel(hwndCombo);
|
||||||
@ -531,11 +543,12 @@ void AddToCombo(TextThread& thread, bool replace)
|
|||||||
|
|
||||||
void RemoveFromCombo(TextThread* thread)
|
void RemoveFromCombo(TextThread* thread)
|
||||||
{
|
{
|
||||||
WCHAR entry[512];
|
CHAR entry[512];
|
||||||
thread->GetEntryString(entry, 512);
|
thread->GetEntryString(entry, 512);
|
||||||
|
std::wstring unicodeEntry = toUnicodeString(entry);
|
||||||
if (thread->PID() == 0)
|
if (thread->PID() == 0)
|
||||||
std::wcscat(entry, L"ConsoleOutput");
|
unicodeEntry += L"ConsoleOutput";
|
||||||
int i = ComboBox_FindString(hwndCombo, 0, entry);
|
int i = ComboBox_FindString(hwndCombo, 0, unicodeEntry.c_str());
|
||||||
if (i != CB_ERR)
|
if (i != CB_ERR)
|
||||||
{
|
{
|
||||||
if (ComboBox_DeleteString(hwndCombo, i) == CB_ERR)
|
if (ComboBox_DeleteString(hwndCombo, i) == CB_ERR)
|
||||||
@ -595,6 +608,36 @@ DWORD AddRemoveLink(TextThread* thread)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook);
|
||||||
|
void AddLinksToHookManager(const Profile* pf, size_t thread_index, const TextThread* thread);
|
||||||
|
|
||||||
|
DWORD ThreadCreate(TextThread* thread)
|
||||||
|
{
|
||||||
|
thread->RegisterOutputCallBack(ThreadOutput, 0);
|
||||||
|
thread->RegisterFilterCallBack(ThreadFilter, 0);
|
||||||
|
AddToCombo(*thread, false);
|
||||||
|
const auto& tp = thread->GetThreadParameter();
|
||||||
|
auto pr = man->GetProcessRecord(tp->pid);
|
||||||
|
if (pr == NULL)
|
||||||
|
return 0;
|
||||||
|
if (IsUnicodeHook(*pr, tp->hook))
|
||||||
|
thread->Status() |= USING_UNICODE;
|
||||||
|
auto pf = pfman->GetProfile(tp->pid);
|
||||||
|
if (!pf)
|
||||||
|
return 0;
|
||||||
|
const std::wstring& hook_name = GetHookNameByAddress(*pr, thread->GetThreadParameter()->hook);
|
||||||
|
auto thread_profile = pf->FindThread(thread->GetThreadParameter(), hook_name);
|
||||||
|
if (thread_profile != pf->Threads().end())
|
||||||
|
{
|
||||||
|
(*thread_profile)->HookManagerIndex() = thread->Number();
|
||||||
|
auto thread_index = thread_profile - pf->Threads().begin();
|
||||||
|
AddLinksToHookManager(pf, thread_index, thread);
|
||||||
|
if (pf->IsThreadSelected(thread_profile))
|
||||||
|
ThreadReset(thread);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook)
|
bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook)
|
||||||
{
|
{
|
||||||
bool res = false;
|
bool res = false;
|
||||||
@ -612,50 +655,21 @@ bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD ThreadCreate(TextThread* thread)
|
void AddLinksToHookManager(const Profile* pf, size_t thread_index, const TextThread* thread)
|
||||||
{
|
{
|
||||||
thread->RegisterOutputCallBack(ThreadOutput, 0);
|
for (auto lp = pf->Links().begin(); lp != pf->Links().end(); ++lp)
|
||||||
thread->RegisterFilterCallBack(ThreadFilter, 0);
|
|
||||||
AddToCombo(*thread, false);
|
|
||||||
const auto tp = thread->GetThreadParameter();
|
|
||||||
auto pr = man->GetProcessRecord(tp->pid);
|
|
||||||
if (pr != NULL)
|
|
||||||
{
|
{
|
||||||
if (IsUnicodeHook(*pr, tp->hook))
|
if ((*lp)->FromIndex() == thread_index)
|
||||||
thread->Status() |= USING_UNICODE;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto pf = pfman->GetProfile(tp->pid);
|
|
||||||
if (pf)
|
|
||||||
{
|
{
|
||||||
auto thread_profile = pf->FindThreadProfile(*tp);
|
WORD to_index = pf->Threads()[(*lp)->ToIndex()]->HookManagerIndex();
|
||||||
if (thread_profile != pf->Threads().end())
|
|
||||||
{
|
|
||||||
(*thread_profile)->HookManagerIndex() = thread->Number();
|
|
||||||
auto thread_profile_index = thread_profile - pf->Threads().begin();
|
|
||||||
AddLinksToHookManager(*pf, thread_profile_index, *thread);
|
|
||||||
if (pf->SelectedIndex() == thread_profile_index)
|
|
||||||
ThreadReset(thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const TextThread& thread)
|
|
||||||
{
|
|
||||||
for (auto lp = pf.Links().begin(); lp != pf.Links().end(); ++lp)
|
|
||||||
{
|
|
||||||
if ((*lp)->FromIndex() == thread_profile_index)
|
|
||||||
{
|
|
||||||
WORD to_index = pf.Threads()[(*lp)->ToIndex()]->HookManagerIndex();
|
|
||||||
if (to_index != 0)
|
if (to_index != 0)
|
||||||
man->AddLink(thread.Number(), to_index);
|
man->AddLink(thread->Number(), to_index);
|
||||||
}
|
}
|
||||||
if ((*lp)->ToIndex() == thread_profile_index)
|
if ((*lp)->ToIndex() == thread_index)
|
||||||
{
|
{
|
||||||
WORD from_index = pf.Threads()[(*lp)->FromIndex()]->HookManagerIndex();
|
WORD from_index = pf->Threads()[(*lp)->FromIndex()]->HookManagerIndex();
|
||||||
if (from_index != 0)
|
if (from_index != 0)
|
||||||
man->AddLink(from_index, thread.Number());
|
man->AddLink(from_index, thread->Number());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -663,14 +677,6 @@ void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const
|
|||||||
DWORD ThreadRemove(TextThread* thread)
|
DWORD ThreadRemove(TextThread* thread)
|
||||||
{
|
{
|
||||||
RemoveFromCombo(thread);
|
RemoveFromCombo(thread);
|
||||||
const auto tp = thread->GetThreadParameter();
|
|
||||||
auto pf = pfman->GetProfile(tp->pid);
|
|
||||||
if (pf)
|
|
||||||
{
|
|
||||||
auto thread_profile = pf->FindThreadProfile(*tp);
|
|
||||||
if (thread_profile != pf->Threads().end())
|
|
||||||
(*thread_profile)->HookManagerIndex() = 0; // reset hookman index number
|
|
||||||
}
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -684,7 +690,6 @@ DWORD RegisterProcessList(DWORD pid)
|
|||||||
ComboBox_AddString(hwndProcessComboBox, str);
|
ComboBox_AddString(hwndProcessComboBox, str);
|
||||||
if (ComboBox_GetCount(hwndProcessComboBox) == 1)
|
if (ComboBox_GetCount(hwndProcessComboBox) == 1)
|
||||||
ComboBox_SetCurSel(hwndProcessComboBox, 0);
|
ComboBox_SetCurSel(hwndProcessComboBox, 0);
|
||||||
pfman->FindProfileAndUpdateHookAddresses(pid, path);
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -706,9 +711,6 @@ DWORD RemoveProcessList(DWORD pid)
|
|||||||
|
|
||||||
DWORD RefreshProfileOnNewHook(DWORD pid)
|
DWORD RefreshProfileOnNewHook(DWORD pid)
|
||||||
{
|
{
|
||||||
auto path = GetProcessPath(pid);
|
|
||||||
if (!path.empty())
|
|
||||||
pfman->FindProfileAndUpdateHookAddresses(pid, path);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -728,7 +730,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
CBS_SORT | WS_VSCROLL | WS_TABSTOP,
|
CBS_SORT | WS_VSCROLL | WS_TABSTOP,
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||||
{
|
{
|
||||||
HFONT hf = CreateFont(18, 0, 0, 0, FW_LIGHT, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, ANTIALIASED_QUALITY, 0,
|
HDC hDC = GetDC(hWnd);
|
||||||
|
int nHeight = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72);
|
||||||
|
ReleaseDC(hWnd, hDC);
|
||||||
|
HFONT hf = CreateFont(nHeight, 0, 0, 0, FW_LIGHT, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET,
|
||||||
|
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
|
||||||
L"MS Gothic");
|
L"MS Gothic");
|
||||||
hWhiteBrush = GetStockBrush(WHITE_BRUSH);
|
hWhiteBrush = GetStockBrush(WHITE_BRUSH);
|
||||||
SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0);
|
SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0);
|
||||||
@ -747,7 +753,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
man->RegisterProcessNewHookCallback(RefreshProfileOnNewHook);
|
man->RegisterProcessNewHookCallback(RefreshProfileOnNewHook);
|
||||||
man->RegisterAddRemoveLinkCallback(AddRemoveLink);
|
man->RegisterAddRemoveLinkCallback(AddRemoveLink);
|
||||||
man->RegisterConsoleCallback(ConsoleOutput);
|
man->RegisterConsoleCallback(ConsoleOutput);
|
||||||
IHF_Start();
|
Host_Start();
|
||||||
{
|
{
|
||||||
static const WCHAR program_name[] = L"Interactive Text Hooker";
|
static const WCHAR program_name[] = L"Interactive Text Hooker";
|
||||||
//static const WCHAR program_version[] = L"3.0";
|
//static const WCHAR program_version[] = L"3.0";
|
||||||
@ -759,8 +765,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|||||||
|
|
||||||
if (background == 0)
|
if (background == 0)
|
||||||
man->AddConsoleOutput(BackgroundMsg);
|
man->AddConsoleOutput(BackgroundMsg);
|
||||||
if (!IHF_IsAdmin())
|
|
||||||
man->AddConsoleOutput(NotAdmin);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -16,4 +16,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "ITH.h"
|
#include "ITH.h"
|
||||||
|
@ -1,33 +1,29 @@
|
|||||||
|
# config.pri
|
||||||
|
# DEFINES += _SECURE_SCL=0 _SCL_SECURE_NO_WARNINGS
|
||||||
|
# DEFINES += _CRT_SECURE_NO_WARNINGS
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
set(CMAKE_CONFIGURATION_TYPES Debug Release)
|
set(CMAKE_CONFIGURATION_TYPES Debug Release)
|
||||||
|
|
||||||
project(vnr)
|
project(vnr)
|
||||||
|
|
||||||
set(WDK_HOME "C:\\WinDDK\\7600.16385.1" CACHE FILEPATH "path to the Windows DDK directory")
|
set(WDK_HOME "C:\\WinDDK\\7600.16385.1" CACHE FILEPATH "Windows Driver Kit path")
|
||||||
|
|
||||||
add_definitions(
|
add_definitions(
|
||||||
-DUNICODE
|
/DUNICODE
|
||||||
-D_UNICODE
|
/D_UNICODE
|
||||||
|
/D_SECURE_SCL=0
|
||||||
|
/D_SCL_SECURE_NO_WARNINGS
|
||||||
|
/D_CRT_SECURE_NO_WARNINGS
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(${PROJECT_SOURCE_DIR})
|
include_directories(
|
||||||
|
${PROJECT_SOURCE_DIR}
|
||||||
set(common_src
|
${PROJECT_SOURCE_DIR}/texthook
|
||||||
${PROJECT_SOURCE_DIR}/ith/common/const.h
|
|
||||||
${PROJECT_SOURCE_DIR}/ith/common/defs.h
|
|
||||||
${PROJECT_SOURCE_DIR}/ith/common/except.h
|
|
||||||
${PROJECT_SOURCE_DIR}/ith/common/growl.h
|
|
||||||
${PROJECT_SOURCE_DIR}/ith/common/memory.h
|
|
||||||
${PROJECT_SOURCE_DIR}/ith/common/types.h
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(import_src
|
add_subdirectory(vnrhook)
|
||||||
${PROJECT_SOURCE_DIR}/ith/import/mono/funcinfo.h
|
add_subdirectory(texthook/host)
|
||||||
${PROJECT_SOURCE_DIR}/ith/import/mono/types.h
|
add_subdirectory(ithsys)
|
||||||
${PROJECT_SOURCE_DIR}/ith/import/ppsspp/funcinfo.h
|
add_subdirectory(profile)
|
||||||
)
|
|
||||||
|
|
||||||
add_subdirectory(ith/hook)
|
|
||||||
add_subdirectory(ith/host)
|
|
||||||
add_subdirectory(ith/sys)
|
|
||||||
|
@ -72,8 +72,8 @@ win32 {
|
|||||||
## External Libraries
|
## External Libraries
|
||||||
|
|
||||||
win32 {
|
win32 {
|
||||||
D3D_HOME = "$$PROGRAMFILES/Microsoft DirectX SDK"
|
D3D_HOME = "C:/Program Files/Microsoft DirectX SDK"
|
||||||
DETOURS_HOME = "$$PROGRAMFILES/Microsoft Research/Detours Express 3.0"
|
DETOURS_HOME = "C:/Program Files/Microsoft Research/Detours Express 3.0"
|
||||||
#DEV_HOME = c:/dev
|
#DEV_HOME = c:/dev
|
||||||
DEV_HOME = z:/local/windows/developer
|
DEV_HOME = z:/local/windows/developer
|
||||||
BOOST_HOME = $$DEV_HOME/boost/build
|
BOOST_HOME = $$DEV_HOME/boost/build
|
||||||
@ -81,8 +81,8 @@ win32 {
|
|||||||
MSIME_HOME = $$DEV_HOME/msime
|
MSIME_HOME = $$DEV_HOME/msime
|
||||||
#PYTHON_HOME = $$ROOTDIR/../Python
|
#PYTHON_HOME = $$ROOTDIR/../Python
|
||||||
#PYTHON_HOME = C:/Python
|
#PYTHON_HOME = C:/Python
|
||||||
PYTHON_HOME = Z:/Local/Windows/Developer/Python
|
PYTHON_HOME = $$DEV_HOME/python
|
||||||
PYSIDE_HOME = $$PYTHON_HOME/Lib/site-packages/PySide
|
PYSIDE_HOME = $$PYTHON_HOME/lib/site-packages/PySide
|
||||||
QT_HOME = c:/qt/4
|
QT_HOME = c:/qt/4
|
||||||
QT_SRC = c:/qt
|
QT_SRC = c:/qt
|
||||||
SAPI_HOME = "$$PROGRAMFILES/Microsoft Speech SDK 5.1"
|
SAPI_HOME = "$$PROGRAMFILES/Microsoft Speech SDK 5.1"
|
||||||
@ -161,7 +161,7 @@ win32 {
|
|||||||
QMAKE_CXXFLAGS_EXCEPTIONS_ON += -EHa
|
QMAKE_CXXFLAGS_EXCEPTIONS_ON += -EHa
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG(noeh) { # No Exception handler
|
CONFIG(noeh) { # No exception handler
|
||||||
message(CONFIG noeh)
|
message(CONFIG noeh)
|
||||||
#CONFIG -= rtti #-exceptions -stl
|
#CONFIG -= rtti #-exceptions -stl
|
||||||
QMAKE_CXXFLAGS += /GR-
|
QMAKE_CXXFLAGS += /GR-
|
||||||
@ -175,7 +175,7 @@ win32 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CONFIG(nosafeseh) { # No Exception handler
|
CONFIG(nosafeseh) { # No safe exception handler
|
||||||
message(CONFIG nosafeseh)
|
message(CONFIG nosafeseh)
|
||||||
|
|
||||||
# Disable SafeSEH table
|
# Disable SafeSEH table
|
||||||
|
@ -1,20 +1,28 @@
|
|||||||
@echo off
|
@echo off
|
||||||
setlocal
|
setlocal
|
||||||
if [%1] == [] (
|
if [%1] == [] (
|
||||||
echo usage: copy_vnr <path-to-Sakura-directory>
|
echo usage: copy_vnr path_to_Sakura
|
||||||
goto :EOF
|
goto :EOF
|
||||||
)
|
)
|
||||||
xcopy %1\config.pri . /S /Y /I
|
xcopy %1\config.pri . /S /Y /I
|
||||||
xcopy %1\cpp\libs\ccutil ccutil /S /Y /I
|
xcopy %1\cpp\libs\ccutil ccutil /S /Y /I
|
||||||
xcopy %1\cpp\libs\cpputil cpputil /S /Y /I
|
xcopy %1\cpp\libs\cpputil cpputil /S /Y /I
|
||||||
xcopy %1\cpp\libs\disasm disasm /S /Y /I /EXCLUDE:exclude.txt
|
xcopy %1\cpp\libs\disasm disasm /S /Y /I /EXCLUDE:exclude.txt
|
||||||
xcopy %1\cpp\plugins\ith ith /S /Y /I
|
xcopy %1\cpp\libs\hashutil hashutil /S /Y /I
|
||||||
|
xcopy %1\cpp\plugins\ithsys ithsys /S /Y /I
|
||||||
|
xcopy %1\cpp\plugins\vnrhook vnrhook /S /Y /I
|
||||||
|
xcopy %1\cpp\plugins\texthook texthook /S /Y /I /EXCLUDE:exclude.txt
|
||||||
xcopy %1\cpp\libs\memdbg memdbg /S /Y /I
|
xcopy %1\cpp\libs\memdbg memdbg /S /Y /I
|
||||||
xcopy %1\cpp\libs\ntdll ntdll /S /Y /I
|
xcopy %1\cpp\libs\ntdll ntdll /S /Y /I
|
||||||
xcopy %1\cpp\libs\ntinspect ntinspect /S /Y /I
|
xcopy %1\cpp\libs\ntinspect ntinspect /S /Y /I
|
||||||
|
xcopy %1\cpp\libs\winkey winkey /S /Y /I
|
||||||
xcopy %1\cpp\libs\winmaker winmaker /S /Y /I
|
xcopy %1\cpp\libs\winmaker winmaker /S /Y /I
|
||||||
xcopy %1\cpp\libs\winmutex winmutex /S /Y /I
|
xcopy %1\cpp\libs\winmutex winmutex /S /Y /I
|
||||||
xcopy %1\cpp\libs\winversion winversion /S /Y /I
|
xcopy %1\cpp\libs\winversion winversion /S /Y /I
|
||||||
xcopy %1\cpp\libs\winseh winseh /S /Y /I
|
xcopy %1\cpp\libs\winseh winseh /S /Y /I
|
||||||
|
xcopy %1\cpp\libs\wintimer wintimer /S /Y /I
|
||||||
|
xcopy %1\cpp\libs\windbg windbg /S /Y /I
|
||||||
|
xcopy %1\cpp\libs\sakurakit sakurakit /S /Y /I
|
||||||
|
xcopy %1\cpp\libs\mono mono /S /Y /I
|
||||||
|
|
||||||
endlocal
|
endlocal
|
||||||
|
@ -19,8 +19,8 @@ inline size_t cpp_basic_strlen(const charT *s)
|
|||||||
return p - s;
|
return p - s;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t cpp_strlen(const char *s) { return cpp_basic_strlen(s); }
|
inline size_t cpp_strlen(const char *s) { return cpp_basic_strlen<char>(s); }
|
||||||
inline size_t cpp_wstrlen(const wchar_t *s) { return cpp_basic_strlen(s); }
|
inline size_t cpp_wstrlen(const wchar_t *s) { return cpp_basic_strlen<wchar_t>(s); }
|
||||||
|
|
||||||
template <typename charT>
|
template <typename charT>
|
||||||
inline size_t cpp_basic_strnlen(const charT *s, size_t n)
|
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)
|
|||||||
return p - s;
|
return p - s;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t cpp_strnlen(const char *s, size_t n) { return cpp_basic_strnlen(s, n); }
|
inline size_t cpp_strnlen(const char *s, size_t n) { return cpp_basic_strnlen<char>(s, n); }
|
||||||
inline size_t cpp_wstrnlen(const wchar_t *s, size_t n) { return cpp_basic_strnlen(s, n); }
|
inline size_t cpp_wstrnlen(const wchar_t *s, size_t n) { return cpp_basic_strnlen<wchar_t>(s, n); }
|
||||||
|
|
||||||
// strnchr
|
// strnchr
|
||||||
|
|
||||||
@ -45,19 +45,15 @@ inline size_t cpp_wstrnlen(const wchar_t *s, size_t n) { return cpp_basic_strnle
|
|||||||
return nullptr; \
|
return nullptr; \
|
||||||
}
|
}
|
||||||
template <typename charT>
|
template <typename charT>
|
||||||
inline charT *cpp_basic_strnchr(charT *s, int c, size_t n)
|
inline charT *cpp_basic_strnchr(charT *s, charT c, size_t n) cpp_basic_strnchr_(s, c, n)
|
||||||
cpp_basic_strnchr_(s, c, n)
|
|
||||||
|
|
||||||
template <typename charT>
|
template <typename charT>
|
||||||
inline const charT *cpp_basic_strnchr(const charT *s, int c, size_t n)
|
inline const charT *cpp_basic_strnchr(const charT *s, charT c, size_t n) cpp_basic_strnchr_(s, c, n)
|
||||||
cpp_basic_strnchr_(s, c, n)
|
|
||||||
|
|
||||||
// The same as memchr
|
// The same as memchr
|
||||||
inline char *cpp_strnchr(char *s, int c, size_t n) { return cpp_basic_strnchr(s, c, n); }
|
inline char *cpp_strnchr(char *s, char c, size_t n) { return cpp_basic_strnchr<char>(s, c, n); }
|
||||||
inline const char *cpp_strnchr(const char *s, int c, size_t n) { return cpp_basic_strnchr(s, c, n); }
|
inline const char *cpp_strnchr(const char *s, char c, size_t n) { return cpp_basic_strnchr<char>(s, c, n); }
|
||||||
|
inline wchar_t *cpp_wcsnchr(wchar_t *s, wchar_t c, size_t n) { return cpp_basic_strnchr<wchar_t>(s, c, n); }
|
||||||
inline wchar_t *cpp_wcsnchr(wchar_t *s, int c, size_t n) { return cpp_basic_strnchr(s, c, n); }
|
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); }
|
||||||
inline const wchar_t *cpp_wcsnchr(const wchar_t *s, int c, size_t n) { return cpp_basic_strnchr(s, c, n); }
|
|
||||||
|
|
||||||
// strnstr
|
// strnstr
|
||||||
|
|
||||||
@ -72,25 +68,19 @@ inline const wchar_t *cpp_wcsnchr(const wchar_t *s, int c, size_t n) { return cp
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename charT>
|
template <typename charT>
|
||||||
inline charT *cpp_basic_strnstr(charT *s, const charT *r, size_t n)
|
inline charT *cpp_basic_strnstr(charT *s, const charT *r, size_t n) cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
|
||||||
cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
|
|
||||||
|
|
||||||
template <typename charT>
|
template <typename charT>
|
||||||
inline const charT *cpp_basic_strnstr(const charT *s, const charT *r, size_t n)
|
inline const charT *cpp_basic_strnstr(const charT *s, const charT *r, size_t n) cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
|
||||||
cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline wchar_t *cpp_basic_strnstr<wchar_t>(wchar_t *s, const wchar_t *r, size_t n)
|
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)
|
||||||
cpp_basic_strnstr_(s, n, r, ::wcslen(r), ::wcsncmp)
|
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline const wchar_t *cpp_basic_strnstr<wchar_t>(const wchar_t *s, const wchar_t *r, size_t n)
|
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)
|
||||||
cpp_basic_strnstr_(s, n, r, ::wcslen(r), ::wcsncmp)
|
|
||||||
|
|
||||||
inline char *cpp_strnstr(char *s, const char *r, size_t n) { return cpp_basic_strnstr(s, r, n); }
|
inline char *cpp_strnstr(char *s, const char *r, size_t n) { return cpp_basic_strnstr<char>(s, r, n); }
|
||||||
inline const char *cpp_strnstr(const char *s, const char *r, size_t n) { return cpp_basic_strnstr(s, r, n); }
|
inline const char *cpp_strnstr(const char *s, const char *r, size_t n) { return cpp_basic_strnstr<char>(s, r, n); }
|
||||||
inline wchar_t *cpp_wcsnstr(wchar_t *s, const wchar_t *r, size_t n) { return cpp_basic_strnstr(s, r, n); }
|
inline wchar_t *cpp_wcsnstr(wchar_t *s, const wchar_t *r, size_t n) { return cpp_basic_strnstr<wchar_t>(s, r, n); }
|
||||||
inline const wchar_t *cpp_wcsnstr(const wchar_t *s, const wchar_t *r, size_t n) { return cpp_basic_strnstr(s, r, n); }
|
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); }
|
||||||
|
|
||||||
// strnpbrk
|
// strnpbrk
|
||||||
|
|
||||||
|
@ -4,9 +4,23 @@
|
|||||||
// cpplocale.h
|
// cpplocale.h
|
||||||
// 9/26/2014 jichi
|
// 9/26/2014 jichi
|
||||||
|
|
||||||
#include <codecvt>
|
|
||||||
#include <locale>
|
#include <locale>
|
||||||
|
|
||||||
|
#ifdef WITHOUT_CXX_CODECVT
|
||||||
|
// http://www.boost.org/doc/libs/1_48_0/libs/serialization/doc/codecvt.html
|
||||||
|
# define BOOST_UTF8_BEGIN_NAMESPACE
|
||||||
|
# define BOOST_UTF8_END_NAMESPACE
|
||||||
|
# define BOOST_UTF8_DECL
|
||||||
|
# include <boost/detail/utf8_codecvt_facet.hpp>
|
||||||
|
# include <boost/detail/utf8_codecvt_facet.ipp> // WARNING: This implementation should only be included ONCE
|
||||||
|
# define CPPLOCALE_NEW_FACET_UTF8(charT) (new utf8_codecvt_facet) // charT is ignored and assumed to be wchar_t
|
||||||
|
//# include <boost/detail/serialization/utf8_codecvt_facet.hpp>
|
||||||
|
//# define CPPLOCALE_NEW_FACET_UTF8(charT) (new utf8_codecvt_facet<charT>)
|
||||||
|
#else
|
||||||
|
# include <codecvt>
|
||||||
|
# define CPPLOCALE_NEW_FACET_UTF8(charT) (new std::codecvt_utf8<charT, 0x10ffff, std::consume_header>)
|
||||||
|
#endif // WITHOUT_CXX_CODECVT
|
||||||
|
|
||||||
//#include <boost/locale.hpp>
|
//#include <boost/locale.hpp>
|
||||||
|
|
||||||
// See: http://stackoverflow.com/questions/20195262/how-to-read-an-utf-8-encoded-file-containing-chinese-characters-and-output-them
|
// See: http://stackoverflow.com/questions/20195262/how-to-read-an-utf-8-encoded-file-containing-chinese-characters-and-output-them
|
||||||
@ -15,7 +29,7 @@
|
|||||||
// - 0x10ffff is the default maximum value.
|
// - 0x10ffff is the default maximum value.
|
||||||
// - std::consume_header will skip the leading encoding byte from the input.
|
// - std::consume_header will skip the leading encoding byte from the input.
|
||||||
template <class charT>
|
template <class charT>
|
||||||
inline std::locale cpp_utf8_locale(std::locale init = std::locale())
|
inline std::locale cpp_utf8_locale(std::locale init = std::locale()) //::empty())
|
||||||
{ return std::locale(init, new std::codecvt_utf8<charT, 0x10ffff, std::consume_header>()); }
|
{ return std::locale(init, CPPLOCALE_NEW_FACET_UTF8(charT)); }
|
||||||
|
|
||||||
#endif // CPPLOCALE_H
|
#endif // CPPLOCALE_H
|
||||||
|
@ -4,33 +4,15 @@
|
|||||||
// cppstring.h
|
// cppstring.h
|
||||||
// 10/12/2014 jichi
|
// 10/12/2014 jichi
|
||||||
|
|
||||||
#include <cstring>
|
/#include <string>
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// Initializers
|
// Initializers
|
||||||
|
|
||||||
template <typename charT, typename stringT>
|
template <typename charT>
|
||||||
inline std::basic_string<charT> cpp_basic_string_of(const stringT &s)
|
inline std::basic_string<charT> cpp_basic_string_of(const std::string &s)
|
||||||
{ return std::basic_string<charT>(s.cbegin(), s.cend()); }
|
{ return std::basic_string<charT>(s.begin(), s.end()); }
|
||||||
|
|
||||||
template <typename stringT>
|
inline std::wstring cpp_wstring_of(const std::string &s)
|
||||||
inline std::string cpp_string_of(const stringT &s)
|
{ return std::wstring(s.begin(), s.end()); }
|
||||||
{ return std::string(s.cbegin(), s.cend()); }
|
|
||||||
|
|
||||||
inline std::string cpp_string_of(const char *s)
|
|
||||||
{ return s; }
|
|
||||||
|
|
||||||
inline std::string cpp_string_of(const wchar_t *s)
|
|
||||||
{ return std::string(s, s + ::wcslen(s)); }
|
|
||||||
|
|
||||||
template <typename stringT>
|
|
||||||
inline std::wstring cpp_wstring_of(const stringT &s)
|
|
||||||
{ return std::wstring(s.cbegin(), s.cend()); }
|
|
||||||
|
|
||||||
inline std::wstring cpp_wstring_of(const wchar_t *s)
|
|
||||||
{ return s; }
|
|
||||||
|
|
||||||
inline std::wstring cpp_wstring_of(const char *s)
|
|
||||||
{ return std::wstring(s, s + ::strlen(s)); }
|
|
||||||
|
|
||||||
#endif // CPPSTRING_H
|
#endif // CPPSTRING_H
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
// 3024b815 0f1302 movlps qword ptr ds:[edx],xmm0
|
// 3024b815 0f1302 movlps qword ptr ds:[edx],xmm0
|
||||||
|
|
||||||
#include "disasm.h"
|
#include "disasm.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
// disasm_flag values:
|
// disasm_flag values:
|
||||||
enum : unsigned {
|
enum : unsigned {
|
||||||
@ -29,21 +30,22 @@ DISASM_BEGIN_NAMESPACE
|
|||||||
// But the are currently unused and could make disasm thread-unsafe
|
// But the are currently unused and could make disasm thread-unsafe
|
||||||
namespace { // unnamed
|
namespace { // unnamed
|
||||||
|
|
||||||
BYTE disasm_seg, // CS DS ES SS FS GS
|
BYTE disasm_seg // CS DS ES SS FS GS
|
||||||
disasm_rep, // REPZ/REPNZ
|
, disasm_rep // REPZ/REPNZ
|
||||||
disasm_opcode, // opcode
|
, disasm_opcode // opcode
|
||||||
disasm_opcode2, // used when opcode==0f
|
, disasm_opcode2 // used when opcode==0f
|
||||||
disasm_modrm, // modxxxrm
|
, disasm_modrm // modxxxrm
|
||||||
disasm_sib, // scale-index-base
|
, disasm_sib // scale-index-base
|
||||||
disasm_mem[8], // mem addr value
|
, disasm_mem[8] // mem addr value
|
||||||
disasm_data[8]; // data value
|
, disasm_data[8] // data value
|
||||||
|
;
|
||||||
|
|
||||||
} // unnamed namespace
|
} // unnamed namespace
|
||||||
|
|
||||||
// return: length if success, 0 if error
|
// return: length if success, 0 if error
|
||||||
int disasm(const BYTE *opcode0)
|
size_t disasm(const void *opcode0)
|
||||||
{
|
{
|
||||||
const BYTE *opcode = opcode0;
|
const BYTE *opcode = (const BYTE *)opcode0;
|
||||||
|
|
||||||
DWORD disasm_len = 0, // 0 if error
|
DWORD disasm_len = 0, // 0 if error
|
||||||
disasm_flag = 0, // C_xxx
|
disasm_flag = 0, // C_xxx
|
||||||
@ -253,7 +255,7 @@ retry:
|
|||||||
for (DWORD i = 0; i < disasm_datasize; i++)
|
for (DWORD i = 0; i < disasm_datasize; i++)
|
||||||
disasm_data[i] = *opcode++;
|
disasm_data[i] = *opcode++;
|
||||||
|
|
||||||
disasm_len = opcode - opcode0;
|
disasm_len = opcode - (const BYTE *)opcode0;
|
||||||
|
|
||||||
return disasm_len;
|
return disasm_len;
|
||||||
} // disasm
|
} // disasm
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
// Include typedef of BYTE
|
// Include typedef of BYTE
|
||||||
//#include <windef.h>
|
//#include <windef.h>
|
||||||
#include <windows.h>
|
//#include <windows.h>
|
||||||
|
|
||||||
//#ifdef QT_CORE_LIB
|
//#ifdef QT_CORE_LIB
|
||||||
//# include <qt_windows.h>
|
//# include <qt_windows.h>
|
||||||
@ -20,7 +20,13 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
DISASM_BEGIN_NAMESPACE
|
DISASM_BEGIN_NAMESPACE
|
||||||
int disasm(const BYTE *opcode0); // return: op length if success, 0 if error
|
/**
|
||||||
|
* This function can do more, but currently only used to estimate the length of an instruction.
|
||||||
|
* Warning: The current implementation is stateful and hence not thread-safe.
|
||||||
|
* @param address of the instruction to look at
|
||||||
|
* @return length of the instruction at the address or 0 if failed
|
||||||
|
*/
|
||||||
|
size_t disasm(const void *address);
|
||||||
DISASM_END_NAMESPACE
|
DISASM_END_NAMESPACE
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
55
vnr/hashutil/hashstr.h
Normal file
55
vnr/hashutil/hashstr.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#ifndef HASHSTR_H
|
||||||
|
#define HASHSTR_H
|
||||||
|
|
||||||
|
// hashstr.h
|
||||||
|
// 8/1/2011
|
||||||
|
// See: http://www.cse.yorku.ca/~oz/hash.html
|
||||||
|
|
||||||
|
#include "hashutil/hashutil.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
HASHUTIL_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
enum : uint64_t { djb2_hash0 = 5381 };
|
||||||
|
|
||||||
|
/// djb2: h = h*33 + c
|
||||||
|
template <typename charT>
|
||||||
|
inline uint64_t djb2(const charT *str, uint64_t hash = djb2_hash0)
|
||||||
|
{
|
||||||
|
charT c;
|
||||||
|
while ((c = *str++))
|
||||||
|
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// n: length
|
||||||
|
template <typename charT>
|
||||||
|
inline uint64_t djb2_n(const charT *str, size_t len, uint64_t hash = djb2_hash0)
|
||||||
|
{
|
||||||
|
while (len--)
|
||||||
|
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sdbm: hash(i) = hash(i - 1) * 65599 + str[i];
|
||||||
|
template <typename charT>
|
||||||
|
inline uint64_t sdbm(const charT *str, uint64_t hash = 0)
|
||||||
|
{
|
||||||
|
charT c;
|
||||||
|
while ((c = *str++))
|
||||||
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename charT>
|
||||||
|
inline uint64_t loselose(const charT *str, uint64_t hash = 0)
|
||||||
|
{
|
||||||
|
charT c;
|
||||||
|
while ((c = *str++))
|
||||||
|
hash += c;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
HASHUTIL_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // HASHSTR_H
|
15
vnr/hashutil/hashutil.h
Normal file
15
vnr/hashutil/hashutil.h
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
#ifndef HASHUTIL_H
|
||||||
|
#define HASHUTIL_H
|
||||||
|
|
||||||
|
// hashutil.h
|
||||||
|
// 6/16/2015 jichi
|
||||||
|
|
||||||
|
// Redefine HASHUTIL_BEGIN_NAMESPACE/HASHUTIL_END_NAMESPACE if need custom namespace
|
||||||
|
#ifndef HASHUTIL_BEGIN_NAMESPACE
|
||||||
|
# define HASHUTIL_BEGIN_NAMESPACE namespace hashutil {
|
||||||
|
#endif
|
||||||
|
#ifndef HASHUTIL_END_NAMESPACE
|
||||||
|
# define HASHUTIL_END_NAMESPACE } // namespace hashutil
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // HASHUTIL_H
|
11
vnr/hashutil/hashutil.pri
Normal file
11
vnr/hashutil/hashutil.pri
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
# hashutil.pri
|
||||||
|
# 6/28/2011 jichi
|
||||||
|
|
||||||
|
DEFINES += WITH_LIB_HASHUTIL
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/hashstr.h \
|
||||||
|
$$PWD/hashutil.h
|
||||||
|
|
||||||
|
# EOF
|
28
vnr/ithsys/CMakeLists.txt
Normal file
28
vnr/ithsys/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# # ithsys.pro
|
||||||
|
# CONFIG += noqt staticlib
|
||||||
|
# include(../../../config.pri)
|
||||||
|
|
||||||
|
# # jichi 7/12/2015: Always enable SEH
|
||||||
|
# DEFINES += ITH_HAS_SEH
|
||||||
|
|
||||||
|
# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
|
||||||
|
set(ithsys_src
|
||||||
|
ithsys.h
|
||||||
|
ithsys.cc
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(ithsys STATIC ${ithsys_src})
|
||||||
|
|
||||||
|
target_compile_options(ithsys PRIVATE
|
||||||
|
$<$<CONFIG:Release>:>
|
||||||
|
$<$<CONFIG:Debug>:>
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(ithsys comctl32.lib)
|
||||||
|
|
||||||
|
target_compile_definitions(ithsys
|
||||||
|
PRIVATE
|
||||||
|
ITH_HAS_SEH
|
||||||
|
_CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
)
|
1549
vnr/ithsys/ithsys.cc
Normal file
1549
vnr/ithsys/ithsys.cc
Normal file
File diff suppressed because it is too large
Load Diff
132
vnr/ithsys/ithsys.h
Normal file
132
vnr/ithsys/ithsys.h
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ithsys.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// Branch: ITH/IHF_SYS.h, rev 111
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning(disable:4800) // C4800: forcing value to bool
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#include "ntdll/ntdll.h"
|
||||||
|
|
||||||
|
// jichi 8/24/2013: Why extern "C"? Any specific reason to use C instead of C++ naming?
|
||||||
|
extern "C" {
|
||||||
|
//int disasm(BYTE *opcode0); // jichi 8/15/2013: move disasm to separate file
|
||||||
|
extern WORD *NlsAnsiCodePage;
|
||||||
|
int FillRange(LPCWSTR name,DWORD *lower, DWORD *upper);
|
||||||
|
int MB_WC(char *mb, wchar_t *wc);
|
||||||
|
//int MB_WC_count(char *mb, int mb_length);
|
||||||
|
int WC_MB(wchar_t *wc, char *mb);
|
||||||
|
|
||||||
|
// jichi 10/1/2013: Return 0 if failed. So, it is ambiguous if the search pattern starts at 0
|
||||||
|
DWORD SearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length); // KMP
|
||||||
|
|
||||||
|
// jichi 2/5/2014: The same as SearchPattern except it uses 0xff to match everything
|
||||||
|
// According to @Andys, 0xff seldom appear in the source code: http://sakuradite.com/topic/124
|
||||||
|
enum : BYTE { SP_ANY = 0xff };
|
||||||
|
#define SP_ANY_2 SP_ANY,SP_ANY
|
||||||
|
#define SP_ANY_3 SP_ANY,SP_ANY,SP_ANY
|
||||||
|
#define SP_ANY_4 SP_ANY,SP_ANY,SP_ANY,SP_ANY
|
||||||
|
DWORD SearchPatternEx(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length, BYTE wildcard=SP_ANY);
|
||||||
|
|
||||||
|
BOOL IthInitSystemService();
|
||||||
|
void IthCloseSystemService();
|
||||||
|
DWORD IthGetMemoryRange(LPCVOID mem, DWORD *base, DWORD *size);
|
||||||
|
BOOL IthCheckFile(LPCWSTR file);
|
||||||
|
BOOL IthFindFile(LPCWSTR file);
|
||||||
|
BOOL IthGetFileInfo(LPCWSTR file, LPVOID info, DWORD size = 0x1000);
|
||||||
|
BOOL IthCheckFileFullPath(LPCWSTR file);
|
||||||
|
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition);
|
||||||
|
HANDLE IthCreateFileInDirectory(LPCWSTR name, HANDLE dir, DWORD option, DWORD share, DWORD disposition);
|
||||||
|
HANDLE IthCreateDirectory(LPCWSTR name);
|
||||||
|
HANDLE IthCreateFileFullPath(LPCWSTR fullpath, DWORD option, DWORD share, DWORD disposition);
|
||||||
|
HANDLE IthPromptCreateFile(DWORD option, DWORD share, DWORD disposition);
|
||||||
|
HANDLE IthCreateSection(LPCWSTR name, DWORD size, DWORD right);
|
||||||
|
HANDLE IthCreateEvent(LPCWSTR name, DWORD auto_reset=0, DWORD init_state=0);
|
||||||
|
HANDLE IthOpenEvent(LPCWSTR name);
|
||||||
|
void IthSetEvent(HANDLE hEvent);
|
||||||
|
void IthResetEvent(HANDLE hEvent);
|
||||||
|
HANDLE IthCreateMutex(LPCWSTR name, BOOL InitialOwner, DWORD *exist=0);
|
||||||
|
HANDLE IthOpenMutex(LPCWSTR name);
|
||||||
|
BOOL IthReleaseMutex(HANDLE hMutex);
|
||||||
|
//DWORD IthWaitForSingleObject(HANDLE hObject, DWORD dwTime);
|
||||||
|
HANDLE IthCreateThread(LPCVOID start_addr, DWORD param, HANDLE hProc=(HANDLE)-1);
|
||||||
|
DWORD GetExportAddress(DWORD hModule,DWORD hash);
|
||||||
|
void IthSleep(int time); // jichi 9/28/2013: in ms
|
||||||
|
void IthSystemTimeToLocalTime(LARGE_INTEGER *ptime);
|
||||||
|
void FreeThreadStart(HANDLE hProc);
|
||||||
|
void CheckThreadStart();
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#ifdef ITH_HAS_HEAP
|
||||||
|
extern HANDLE hHeap; // used in ith/common/memory.h
|
||||||
|
#endif // ITH_HAS_HEAP
|
||||||
|
|
||||||
|
extern DWORD current_process_id;
|
||||||
|
extern DWORD debug;
|
||||||
|
extern BYTE LeadByteTable[];
|
||||||
|
extern LPVOID page;
|
||||||
|
extern BYTE launch_time[];
|
||||||
|
|
||||||
|
inline DWORD GetHash(LPSTR str)
|
||||||
|
{
|
||||||
|
DWORD hash = 0;
|
||||||
|
//for (; *str; str++)
|
||||||
|
while (*str)
|
||||||
|
hash = ((hash>>7) | (hash<<25)) + *str++;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline DWORD GetHash(LPCWSTR str)
|
||||||
|
{
|
||||||
|
DWORD hash = 0;
|
||||||
|
//for (; *str; str++)
|
||||||
|
while (*str)
|
||||||
|
hash = ((hash>>7) | (hash<<25)) + *str++;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void IthBreak()
|
||||||
|
{ if (debug) __debugbreak(); }
|
||||||
|
|
||||||
|
inline LPCWSTR GetMainModulePath()
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
mov eax, fs:[0x30]
|
||||||
|
mov eax, [eax + 0xC]
|
||||||
|
mov eax, [eax + 0xC]
|
||||||
|
mov eax, [eax + 0x28]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 9/28/2013: Add this to lock NtWriteFile in wine
|
||||||
|
class IthMutexLocker
|
||||||
|
{
|
||||||
|
HANDLE m;
|
||||||
|
public:
|
||||||
|
explicit IthMutexLocker(HANDLE mutex) : m(mutex)
|
||||||
|
{ NtWaitForSingleObject(m, 0, 0); }
|
||||||
|
|
||||||
|
~IthMutexLocker() { if (m != INVALID_HANDLE_VALUE) IthReleaseMutex(m); }
|
||||||
|
|
||||||
|
bool locked() const { return m != INVALID_HANDLE_VALUE; }
|
||||||
|
|
||||||
|
void unlock() { if (m != INVALID_HANDLE_VALUE) { IthReleaseMutex(m); m = INVALID_HANDLE_VALUE; } }
|
||||||
|
};
|
||||||
|
|
||||||
|
void IthCoolDown();
|
||||||
|
|
||||||
|
BOOL IthIsWine();
|
||||||
|
BOOL IthIsWindowsXp();
|
||||||
|
//BOOL IthIsWindows8OrGreater(); // not public
|
||||||
|
|
||||||
|
/** Get current dll path.
|
||||||
|
* @param buf
|
||||||
|
* @param len
|
||||||
|
* @return length of the path excluding \0
|
||||||
|
*/
|
||||||
|
size_t IthGetCurrentModulePath(wchar_t *buf, size_t len);
|
||||||
|
|
||||||
|
// EOF
|
13
vnr/ithsys/ithsys.pri
Normal file
13
vnr/ithsys/ithsys.pri
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# ithsys.pri
|
||||||
|
# 8/21/2013 jichi
|
||||||
|
|
||||||
|
DEFINES += WITH_LIB_ITHSYS
|
||||||
|
LIBS += -lithsys
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
HEADERS += $$PWD/ithsys.h
|
||||||
|
#SOURCES += $$PWD/ithsys.cc
|
||||||
|
|
||||||
|
#include($$LIBDIR/winddk/winddk.pri)
|
||||||
|
#LIBS += -L$$WDK/lib/wxp/i386
|
||||||
|
|
||||||
|
# EOF
|
52
vnr/ithsys/ithsys.pro
Normal file
52
vnr/ithsys/ithsys.pro
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
# ithsys.pro
|
||||||
|
# 8/21/2013 jichi
|
||||||
|
# Build ithsys.lib
|
||||||
|
|
||||||
|
#CONFIG += noqt noeh staticlib
|
||||||
|
CONFIG += noqt staticlib
|
||||||
|
|
||||||
|
include(../../../config.pri)
|
||||||
|
include($$LIBDIR/ntdll/ntdll.pri)
|
||||||
|
|
||||||
|
#LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
|
||||||
|
|
||||||
|
#include($$LIBDIR/winddk/winddk.pri)
|
||||||
|
#LIBS += -L$$WDK/lib/wxp/i386
|
||||||
|
|
||||||
|
# jichi 9/22/2013: When ITH is on wine, certain NT functions are replaced
|
||||||
|
#DEFINES += ITH_WINE
|
||||||
|
|
||||||
|
# jichi 7/12/2015: Always enable SEH
|
||||||
|
DEFINES += ITH_HAS_SEH
|
||||||
|
|
||||||
|
# jichi 11/24/2013: Disable manual heap
|
||||||
|
DEFINES -= ITH_HAS_HEAP
|
||||||
|
|
||||||
|
## Libraries
|
||||||
|
|
||||||
|
#INCLUDEPATH += $$ITH_HOME/include
|
||||||
|
#INCLUDEPATH += $$WDK7_HOME/inc/ddk
|
||||||
|
|
||||||
|
#LIBS += -lgdi32 -luser32 -lkernel32
|
||||||
|
#LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
|
||||||
|
#LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
|
||||||
|
|
||||||
|
#DEFINES += ITH_HAS_CXX
|
||||||
|
|
||||||
|
#LIBS += -lith_sys -lntdll
|
||||||
|
#LIBS += -lith_tls -lntdll
|
||||||
|
#LIBS += -lntoskrnl
|
||||||
|
|
||||||
|
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
TEMPLATE = lib
|
||||||
|
TARGET = ithsys
|
||||||
|
|
||||||
|
HEADERS += ithsys.h
|
||||||
|
SOURCES += ithsys.cc
|
||||||
|
|
||||||
|
OTHER_FILES += ithsys.pri
|
||||||
|
|
||||||
|
# EOF
|
@ -198,76 +198,190 @@ finish:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
/**
|
||||||
|
* Search from stopAddress back to startAddress - range
|
||||||
|
* This function is not well debugged
|
||||||
|
*/
|
||||||
|
DWORD reverseSearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length) // KMP
|
||||||
|
{
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
mov eax,search_length
|
||||||
|
alloc:
|
||||||
|
push 0
|
||||||
|
sub eax,1
|
||||||
|
jnz alloc
|
||||||
|
|
||||||
|
mov edi,search
|
||||||
|
mov edx,search_length
|
||||||
|
mov ecx,1
|
||||||
|
xor esi,esi
|
||||||
|
build_table:
|
||||||
|
mov al,byte ptr [edi+esi]
|
||||||
|
cmp al,byte ptr [edi+ecx]
|
||||||
|
sete al
|
||||||
|
test esi,esi
|
||||||
|
jz pre
|
||||||
|
test al,al
|
||||||
|
jnz pre
|
||||||
|
mov esi,[esp+esi*4-4]
|
||||||
|
jmp build_table
|
||||||
|
pre:
|
||||||
|
test al,al
|
||||||
|
jz write_table
|
||||||
|
inc esi
|
||||||
|
write_table:
|
||||||
|
mov [esp+ecx*4],esi
|
||||||
|
|
||||||
|
inc ecx
|
||||||
|
cmp ecx,edx
|
||||||
|
jb build_table
|
||||||
|
|
||||||
|
mov esi,base
|
||||||
|
xor edx,edx
|
||||||
|
mov ecx,edx
|
||||||
|
matcher:
|
||||||
|
mov al,byte ptr [edi+ecx]
|
||||||
|
cmp al,byte ptr [esi-edx] // jichi 6/1/2014: The only place that is modified
|
||||||
|
sete al
|
||||||
|
test ecx,ecx
|
||||||
|
jz match
|
||||||
|
test al,al
|
||||||
|
jnz match
|
||||||
|
mov ecx, [esp+ecx*4-4]
|
||||||
|
jmp matcher
|
||||||
|
match:
|
||||||
|
test al,al
|
||||||
|
jz pre2
|
||||||
|
inc ecx
|
||||||
|
cmp ecx,search_length
|
||||||
|
je finish
|
||||||
|
pre2:
|
||||||
|
inc edx
|
||||||
|
cmp edx,base_length // search_length
|
||||||
|
jb matcher
|
||||||
|
mov edx,search_length
|
||||||
|
dec edx
|
||||||
|
finish:
|
||||||
|
mov ecx,search_length
|
||||||
|
sub edx,ecx
|
||||||
|
lea eax,[edx+1]
|
||||||
|
lea ecx,[ecx*4]
|
||||||
|
add esp,ecx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
// Modified from ITH findCallOrJmpAbs
|
// Modified from ITH findCallOrJmpAbs
|
||||||
// Example call:
|
// Example call:
|
||||||
// 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
|
// 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
|
||||||
enum : WORD {
|
enum : WORD {
|
||||||
word_jmp = 0x25ff
|
word_jmp = 0x25ff // long jump
|
||||||
, word_call = 0x15ff // far call
|
, word_call = 0x15ff // far call
|
||||||
};
|
};
|
||||||
/***
|
|
||||||
* Return the absolute address of op. Op takes 1 parameter.
|
|
||||||
*
|
|
||||||
* @param op first half of the operator
|
|
||||||
* @param arg1 the function address
|
|
||||||
* @param start address
|
|
||||||
* @param search range
|
|
||||||
* @return absolute address or 0
|
|
||||||
*/
|
|
||||||
DWORD findWordCall(WORD op, DWORD arg1, DWORD start, DWORD size)
|
|
||||||
{
|
|
||||||
typedef WORD optype;
|
|
||||||
typedef DWORD argtype;
|
|
||||||
|
|
||||||
enum { START = 0x1000 }; // leading size to skip
|
|
||||||
for (DWORD i = START; i < size - sizeof(argtype); i++)
|
|
||||||
if (op == *(optype *)(start + i)) {
|
|
||||||
DWORD t = *(DWORD *)(start + i + sizeof(optype));
|
|
||||||
if (t > start && t < start + size) {
|
|
||||||
if (arg1 == *(argtype *)t)
|
|
||||||
return start + i;
|
|
||||||
else
|
|
||||||
i += sizeof(optype) + sizeof(argtype) - 1; // == 5
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Modified from ITH findCallOrJmpAbs
|
// Modified from ITH findCallOrJmpAbs
|
||||||
enum : BYTE {
|
enum : BYTE {
|
||||||
byte_call = 0xe8 // near call
|
byte_jmp = 0xe9 // long call
|
||||||
|
, byte_call = 0xe8 // near call
|
||||||
, byte_push_small = 0x6a // push byte operand
|
, byte_push_small = 0x6a // push byte operand
|
||||||
, byte_push_large = 0x68 // push operand > 0xff
|
, byte_push_large = 0x68 // push operand > 0xff
|
||||||
};
|
};
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Return the absolute address of op. Op takes 1 address parameter.
|
* Return the absolute address of op. Op takes 1 parameter.
|
||||||
|
* DWORD call with absolute address.
|
||||||
*
|
*
|
||||||
* @param op first half of the operator
|
* @param op first half of the operator
|
||||||
* @param arg1 the function address
|
* @param arg1 the function address
|
||||||
* @param start address
|
* @param start address
|
||||||
* @param search range
|
* @param stop address
|
||||||
|
* @param offset search after start address
|
||||||
|
* @param range search size
|
||||||
* @return absolute address or 0
|
* @return absolute address or 0
|
||||||
*/
|
*/
|
||||||
DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
|
DWORD findWordCall(WORD op, DWORD arg1, DWORD start, DWORD stop, DWORD offset, DWORD range)
|
||||||
|
{
|
||||||
|
typedef WORD optype;
|
||||||
|
typedef DWORD argtype;
|
||||||
|
|
||||||
|
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
|
||||||
|
if (op == *(optype *)(start + i)) {
|
||||||
|
DWORD t = *(DWORD *)(start + i + sizeof(optype));
|
||||||
|
if (t > start && t < stop) {
|
||||||
|
if (arg1 == *(argtype *)t) // absolute address
|
||||||
|
return start + i;
|
||||||
|
//i += sizeof(optype) + sizeof(argtype) - 1; // == 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD findLastWordCall(WORD op, DWORD arg1, DWORD start, DWORD stop, DWORD offset, DWORD range)
|
||||||
|
{
|
||||||
|
typedef WORD optype;
|
||||||
|
typedef DWORD argtype;
|
||||||
|
DWORD ret = 0;
|
||||||
|
|
||||||
|
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
|
||||||
|
if (op == *(optype *)(start + i)) {
|
||||||
|
DWORD t = *(DWORD *)(start + i + sizeof(optype));
|
||||||
|
if (t > start && t < stop) {
|
||||||
|
if (arg1 == *(argtype *)t) // absolute address
|
||||||
|
ret = start + i;
|
||||||
|
//i += sizeof(optype) + sizeof(argtype) - 1; // == 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Return the absolute address of op. Op takes 1 address parameter.
|
||||||
|
* BYTE call with relative address.
|
||||||
|
*
|
||||||
|
* @param op first half of the operator
|
||||||
|
* @param arg1 the function address
|
||||||
|
* @param start address
|
||||||
|
* @param offset search after start address
|
||||||
|
* @param range search size
|
||||||
|
* @return absolute address or 0
|
||||||
|
*/
|
||||||
|
DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD offset, DWORD range)
|
||||||
{
|
{
|
||||||
typedef BYTE optype;
|
typedef BYTE optype;
|
||||||
typedef DWORD argtype;
|
typedef DWORD argtype;
|
||||||
|
|
||||||
enum { START = 0x1000 }; // leading size to skip
|
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
|
||||||
for (DWORD i = START; i < size - sizeof(argtype); i++)
|
|
||||||
if (op == *(optype *)(start + i)) {
|
if (op == *(optype *)(start + i)) {
|
||||||
DWORD t = *(DWORD *)(start + i + sizeof(optype));
|
DWORD t = *(argtype *)(start + i + sizeof(optype));
|
||||||
if (t > start && t < start + size) {
|
//if (t > start && t < stop) {
|
||||||
if (arg1 == *(argtype *)t)
|
if (arg1 == t + start + i + sizeof(optype) + sizeof(argtype)) // relative address
|
||||||
return start + i;
|
return start + i;
|
||||||
else
|
//i += sizeof(optype) + sizeof(argtype) - 1; // == 4
|
||||||
i += sizeof(optype) + sizeof(argtype) - 1; // == 4
|
//}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD findLastByteCall(BYTE op, DWORD arg1, DWORD start, DWORD offset, DWORD range)
|
||||||
|
{
|
||||||
|
typedef BYTE optype;
|
||||||
|
typedef DWORD argtype;
|
||||||
|
DWORD ret = 0;
|
||||||
|
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
|
||||||
|
if (op == *(optype *)(start + i)) {
|
||||||
|
DWORD t = *(argtype *)(start + i + sizeof(optype));
|
||||||
|
//if (t > start && t < stop) {
|
||||||
|
if (arg1 == t + start + i + sizeof(optype) + sizeof(argtype)) // relative address
|
||||||
|
ret = start + i;
|
||||||
|
//i += sizeof(optype) + sizeof(argtype) - 1; // == 4
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/***
|
/***
|
||||||
* Return the absolute address of op. Op takes 1 parameter.
|
* Return the absolute address of op. Op takes 1 parameter.
|
||||||
*
|
*
|
||||||
@ -277,13 +391,12 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
|
|||||||
* @param search range
|
* @param search range
|
||||||
* @return absolute address or 0
|
* @return absolute address or 0
|
||||||
*/
|
*/
|
||||||
//DWORD findByteOp1(BYTE op, DWORD arg1, DWORD start, DWORD size)
|
//DWORD findByteOp1(BYTE op, DWORD arg1, DWORD start, DWORD size, DWORD offset)
|
||||||
//{
|
//{
|
||||||
// typedef BYTE optype;
|
// typedef BYTE optype;
|
||||||
// typedef DWORD argtype;
|
// typedef DWORD argtype;
|
||||||
//
|
//
|
||||||
// enum { START = 0x1000 }; // leading size to skip
|
// for (DWORD i = offset; i < size - sizeof(argtype); i++)
|
||||||
// for (DWORD i = START; i < size - sizeof(argtype); i++)
|
|
||||||
// if (op == *(optype *)(start + i)) {
|
// if (op == *(optype *)(start + i)) {
|
||||||
// DWORD t = *(DWORD *)(start + i + sizeof(optype));
|
// DWORD t = *(DWORD *)(start + i + sizeof(optype));
|
||||||
// if (t == arg1) {
|
// if (t == arg1) {
|
||||||
@ -299,14 +412,29 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
|
|||||||
|
|
||||||
MEMDBG_BEGIN_NAMESPACE
|
MEMDBG_BEGIN_NAMESPACE
|
||||||
|
|
||||||
DWORD findJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
|
DWORD findLongJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
{ return findWordCall(word_jmp, funcAddr, lowerBound, upperBound - lowerBound); }
|
{ return findWordCall(word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
DWORD findFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
|
DWORD findShortJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
{ return findWordCall(word_call, funcAddr, lowerBound, upperBound - lowerBound); }
|
{ return findByteCall(byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
DWORD findNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
|
DWORD findFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
{ return findByteCall(byte_call, funcAddr, lowerBound, upperBound - lowerBound); }
|
{ return findWordCall(word_call, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
|
DWORD findNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
|
{ return findByteCall(byte_call, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
|
DWORD findLastLongJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
|
{ return findLastWordCall(word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
|
DWORD findLastShortJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
|
{ return findLastByteCall(byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
|
DWORD findLastFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
|
{ return findLastWordCall(word_call, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
|
DWORD findLastNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
|
{ return findLastByteCall(byte_call, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
DWORD findPushDwordAddress(DWORD value, DWORD lowerBound, DWORD upperBound)
|
DWORD findPushDwordAddress(DWORD value, DWORD lowerBound, DWORD upperBound)
|
||||||
{
|
{
|
||||||
@ -322,9 +450,8 @@ DWORD findPushByteAddress(BYTE value, DWORD lowerBound, DWORD upperBound)
|
|||||||
return findBytes(bytes, sizeof(bytes), lowerBound, upperBound);
|
return findBytes(bytes, sizeof(bytes), lowerBound, upperBound);
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength)
|
DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
|
||||||
{
|
{
|
||||||
enum { Start = 0x1000 };
|
|
||||||
enum { PatternSize = 4 };
|
enum { PatternSize = 4 };
|
||||||
const DWORD size = upperBound - lowerBound - PatternSize;
|
const DWORD size = upperBound - lowerBound - PatternSize;
|
||||||
const DWORD fun = (DWORD)funcAddr;
|
const DWORD fun = (DWORD)funcAddr;
|
||||||
@ -332,9 +459,9 @@ DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upper
|
|||||||
// 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
|
// 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
|
||||||
//WCHAR str[0x40];
|
//WCHAR str[0x40];
|
||||||
const DWORD mask = sigMask(sig);
|
const DWORD mask = sigMask(sig);
|
||||||
for (DWORD i = Start; i < size; i++)
|
for (DWORD i = offset; i < size; i++)
|
||||||
if (*(WORD *)(lowerBound + i) == word_call) {
|
if (*(WORD *)(lowerBound + i) == word_call) {
|
||||||
DWORD t = *(DWORD *)(lowerBound + i + 2);
|
DWORD t = *(DWORD *)(lowerBound + i + 2); // 2 = sizeof(word)
|
||||||
if (t >= lowerBound && t <= upperBound - PatternSize) {
|
if (t >= lowerBound && t <= upperBound - PatternSize) {
|
||||||
if (*(DWORD *)t == fun)
|
if (*(DWORD *)t == fun)
|
||||||
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
|
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
|
||||||
@ -352,9 +479,154 @@ DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upper
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount, DWORD lowerBound, DWORD upperBound, DWORD reverseLength)
|
#ifndef MEMDBG_NO_STL
|
||||||
|
|
||||||
|
bool iterFindBytes(const address_fun_t &fun, const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
|
||||||
|
{
|
||||||
|
for (DWORD addr = lowerBound; addr < upperBound - patternSize; addr += patternSize) {
|
||||||
|
addr = findBytes(pattern, patternSize, addr, upperBound);
|
||||||
|
if (!addr || !fun(addr))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iterMatchBytes(const address_fun_t &fun, const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
|
||||||
|
{
|
||||||
|
for (DWORD addr = lowerBound; addr < upperBound - patternSize; addr += patternSize) { ;
|
||||||
|
addr = matchBytes(pattern, patternSize, addr, upperBound);
|
||||||
|
if (!addr || !fun(addr))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iterWordCall(const address_fun_t &callback, WORD op, DWORD arg1, DWORD start, DWORD stop, DWORD offset, DWORD range)
|
||||||
|
{
|
||||||
|
typedef WORD optype;
|
||||||
|
typedef DWORD argtype;
|
||||||
|
|
||||||
|
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
|
||||||
|
if (op == *(optype *)(start + i)) {
|
||||||
|
DWORD t = *(DWORD *)(start + i + sizeof(optype));
|
||||||
|
if (t > start && t < stop) {
|
||||||
|
if (arg1 == *(argtype *)t // absolute address
|
||||||
|
&& !callback(start + i))
|
||||||
|
return false;
|
||||||
|
//i += sizeof(optype) + sizeof(argtype) - 1; // == 5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iterByteCall(const address_fun_t &callback, BYTE op, DWORD arg1, DWORD start, DWORD offset, DWORD range)
|
||||||
|
{
|
||||||
|
typedef BYTE optype;
|
||||||
|
typedef DWORD argtype;
|
||||||
|
|
||||||
|
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
|
||||||
|
if (op == *(optype *)(start + i)) {
|
||||||
|
DWORD t = *(argtype *)(start + i + sizeof(optype));
|
||||||
|
//if (t > start && t < stop) {
|
||||||
|
if (arg1 == t + start + i + sizeof(optype) + sizeof(argtype) // relative address
|
||||||
|
&& !callback(start + i))
|
||||||
|
return false;
|
||||||
|
//i += sizeof(optype) + sizeof(argtype) - 1; // == 4
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iterCallerAddress(const address2_fun_t &callback, DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
|
||||||
|
{
|
||||||
|
enum { PatternSize = 4 };
|
||||||
|
const DWORD size = upperBound - lowerBound - PatternSize;
|
||||||
|
const DWORD fun = (DWORD)funcAddr;
|
||||||
|
// Example function call:
|
||||||
|
// 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
|
||||||
|
//WCHAR str[0x40];
|
||||||
|
const DWORD mask = sigMask(sig);
|
||||||
|
for (DWORD i = offset; i < size; i++)
|
||||||
|
if (*(WORD *)(lowerBound + i) == word_call) {
|
||||||
|
DWORD t = *(DWORD *)(lowerBound + i + 2); // 2 = sizeof(word)
|
||||||
|
if (t >= lowerBound && t <= upperBound - PatternSize) {
|
||||||
|
if (*(DWORD *)t == fun)
|
||||||
|
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
|
||||||
|
//OutputConsole(str);
|
||||||
|
for (DWORD j = i ; j > i - reverseLength; j--)
|
||||||
|
if ((*(DWORD *)(lowerBound + j) & mask) == sig) {
|
||||||
|
if (!callback(lowerBound + j, lowerBound + i))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
} else
|
||||||
|
i += 6;
|
||||||
|
}
|
||||||
|
//OutputConsole(L"Find call and entry failed.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iterCallerAddressAfterInt3(const address2_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
|
||||||
|
{
|
||||||
|
auto callback = [&fun](dword_t addr, dword_t call) -> bool {
|
||||||
|
while (byte_int3 == *(BYTE *)++addr); // skip leading int3
|
||||||
|
return fun(addr, call);
|
||||||
|
};
|
||||||
|
return iterCallerAddress(callback, funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
dword_t prevAddr = 0;
|
||||||
|
auto callback = [&fun, &prevAddr](dword_t addr, dword_t) -> bool {
|
||||||
|
if (prevAddr == addr)
|
||||||
|
return true;
|
||||||
|
prevAddr = addr;
|
||||||
|
return fun(addr);
|
||||||
|
};
|
||||||
|
return iterCallerAddress(callback, funcAddr, funcInst, lowerBound, upperBound, callerSearchSize, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iterUniqueCallerAddressAfterInt3(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
|
||||||
|
{
|
||||||
|
auto callback = [&fun](dword_t addr) -> bool {
|
||||||
|
while (byte_int3 == *(BYTE *)++addr); // skip leading int3
|
||||||
|
return fun(addr);
|
||||||
|
};
|
||||||
|
return iterUniqueCallerAddress(callback, funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iterLongJumpAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
|
{ return iterWordCall(fun, word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
|
bool iterShortJumpAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
|
{ return iterByteCall(fun, byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
|
bool iterFarCallAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
|
{ return iterWordCall(fun, word_call, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
|
bool iterNearCallAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||||
|
{ return iterByteCall(fun, byte_call, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||||
|
|
||||||
|
bool iterAlignedNearCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
|
||||||
|
{
|
||||||
|
dword_t prevAddr = 0;
|
||||||
|
auto callback = [&fun, &prevAddr, callerSearchSize](dword_t addr) -> bool {
|
||||||
|
if ((addr = findEnclosingAlignedFunction(addr, callerSearchSize))
|
||||||
|
&& prevAddr != addr) {
|
||||||
|
prevAddr = addr;
|
||||||
|
return fun(addr);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return iterNearCallAddress(callback, funcAddr, lowerBound, upperBound, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // MEMDBG_NO_STL
|
||||||
|
|
||||||
|
DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
|
||||||
{
|
{
|
||||||
enum { Start = 0x1000 };
|
|
||||||
enum { PatternSize = 4 };
|
enum { PatternSize = 4 };
|
||||||
const DWORD size = upperBound - lowerBound - PatternSize;
|
const DWORD size = upperBound - lowerBound - PatternSize;
|
||||||
const DWORD fun = (DWORD)funcAddr;
|
const DWORD fun = (DWORD)funcAddr;
|
||||||
@ -367,9 +639,9 @@ DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount,
|
|||||||
for (DWORD k = 0; k < sigCount; k++)
|
for (DWORD k = 0; k < sigCount; k++)
|
||||||
masks[k] = sigMask(sigs[k]);
|
masks[k] = sigMask(sigs[k]);
|
||||||
|
|
||||||
for (DWORD i = Start; i < size; i++)
|
for (DWORD i = offset; i < size; i++)
|
||||||
if (*(WORD *)(lowerBound + i) == word_call) {
|
if (*(WORD *)(lowerBound + i) == word_call) {
|
||||||
DWORD t = *(DWORD *)(lowerBound + i + 2);
|
DWORD t = *(DWORD *)(lowerBound + i + 2); // 2 = sizeof(word)
|
||||||
if (t >= lowerBound && t <= upperBound - PatternSize) {
|
if (t >= lowerBound && t <= upperBound - PatternSize) {
|
||||||
if (*(DWORD *)t == fun)
|
if (*(DWORD *)t == fun)
|
||||||
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
|
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
|
||||||
@ -391,16 +663,15 @@ DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength)
|
DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
|
||||||
{
|
{
|
||||||
enum { Start = 0x1000 };
|
|
||||||
enum { PatternSize = 4 };
|
enum { PatternSize = 4 };
|
||||||
const DWORD size = upperBound - lowerBound - PatternSize;
|
const DWORD size = upperBound - lowerBound - PatternSize;
|
||||||
const DWORD fun = (DWORD)funcAddr;
|
const DWORD fun = (DWORD)funcAddr;
|
||||||
//WCHAR str[0x40];
|
//WCHAR str[0x40];
|
||||||
DWORD ret = 0;
|
DWORD ret = 0;
|
||||||
const DWORD mask = sigMask(sig);
|
const DWORD mask = sigMask(sig);
|
||||||
for (DWORD i = Start; i < size; i++)
|
for (DWORD i = offset; i < size; i++)
|
||||||
if (*(WORD *)(lowerBound + i) == word_call) {
|
if (*(WORD *)(lowerBound + i) == word_call) {
|
||||||
DWORD t = *(DWORD *)(lowerBound + i + 2);
|
DWORD t = *(DWORD *)(lowerBound + i + 2);
|
||||||
if (t >= lowerBound && t <= upperBound - PatternSize) {
|
if (t >= lowerBound && t <= upperBound - PatternSize) {
|
||||||
@ -408,10 +679,12 @@ DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD u
|
|||||||
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
|
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
|
||||||
//OutputConsole(str);
|
//OutputConsole(str);
|
||||||
for (DWORD j = i ; j > i - reverseLength; j--)
|
for (DWORD j = i ; j > i - reverseLength; j--)
|
||||||
if ((*(DWORD *)(lowerBound + j) & mask) == sig) // Fun entry 1.
|
if ((*(DWORD *)(lowerBound + j) & mask) == sig) { // Fun entry 1.
|
||||||
//swprintf(str,L"Entry: 0x%.8X",lowerBound + j);
|
//swprintf(str,L"Entry: 0x%.8X",lowerBound + j);
|
||||||
//OutputConsole(str);
|
//OutputConsole(str);
|
||||||
ret = lowerBound + j;
|
ret = lowerBound + j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
} else
|
} else
|
||||||
i += 6;
|
i += 6;
|
||||||
@ -420,22 +693,36 @@ DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD u
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize)
|
DWORD findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
|
||||||
{
|
{
|
||||||
DWORD addr = findCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize);
|
DWORD addr = findCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
|
||||||
if (addr)
|
if (addr)
|
||||||
while (byte_int3 == *(BYTE *)++addr);
|
while (byte_int3 == *(BYTE *)++addr);
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
DWORD findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize)
|
DWORD findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
|
||||||
{
|
{
|
||||||
DWORD addr = findLastCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize);
|
DWORD addr = findLastCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
|
||||||
if (addr)
|
if (addr)
|
||||||
while (byte_int3 == *(BYTE *)++addr);
|
while (byte_int3 == *(BYTE *)++addr);
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD findAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
|
||||||
|
{
|
||||||
|
if (DWORD addr = findNearCallAddress(funcAddr, lowerBound, upperBound, offset))
|
||||||
|
return findEnclosingAlignedFunction(addr, callerSearchSize);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD findLastAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
|
||||||
|
{
|
||||||
|
if (DWORD addr = findLastCallerAddressAfterInt3(funcAddr, lowerBound, upperBound, callerSearchSize, offset))
|
||||||
|
return findEnclosingAlignedFunction(addr, callerSearchSize);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range)
|
DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range)
|
||||||
{
|
{
|
||||||
start &= ~0xf;
|
start &= ~0xf;
|
||||||
@ -466,6 +753,31 @@ DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DWORD findEnclosingFunctionAfterDword(DWORD sig, DWORD start, DWORD back_range, DWORD step)
|
||||||
|
{
|
||||||
|
start &= ~0xf;
|
||||||
|
for (DWORD i = start, j = start - back_range; i > j; i-=step) { // 0x10 is aligned
|
||||||
|
DWORD k = *(DWORD *)(i-4); // 4 = sizeof(DWORD)
|
||||||
|
if (k == sig)
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD findEnclosingFunctionBeforeDword(DWORD sig, DWORD start, DWORD back_range,DWORD step)
|
||||||
|
{
|
||||||
|
DWORD addr = findEnclosingFunctionAfterDword(sig, start, back_range, step);
|
||||||
|
if (addr)
|
||||||
|
addr -= sizeof(DWORD);
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD findEnclosingFunctionAfterInt3(DWORD start, DWORD back_range, DWORD step)
|
||||||
|
{ return findEnclosingFunctionAfterDword(0xcccccccc, start, back_range, step); }
|
||||||
|
|
||||||
|
DWORD findEnclosingFunctionAfterNop(DWORD start, DWORD back_range, DWORD step)
|
||||||
|
{ return findEnclosingFunctionAfterDword(0x90909090,start, back_range, step); }
|
||||||
|
|
||||||
DWORD findBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
|
DWORD findBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
|
||||||
{
|
{
|
||||||
DWORD reladdr = searchPattern(lowerBound, upperBound - lowerBound, pattern, patternSize);
|
DWORD reladdr = searchPattern(lowerBound, upperBound - lowerBound, pattern, patternSize);
|
||||||
@ -478,6 +790,12 @@ DWORD matchBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD
|
|||||||
return reladdr ? lowerBound + reladdr : 0;
|
return reladdr ? lowerBound + reladdr : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//DWORD reverseFindBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
|
||||||
|
//{
|
||||||
|
// DWORD reladdr = reverseSearchPattern(lowerBound, upperBound - lowerBound, pattern, patternSize);
|
||||||
|
// return reladdr ? lowerBound + reladdr : 0;
|
||||||
|
//}
|
||||||
|
|
||||||
#if 0 // not used
|
#if 0 // not used
|
||||||
DWORD findBytesInPages(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound, SearchType search)
|
DWORD findBytesInPages(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound, SearchType search)
|
||||||
{
|
{
|
||||||
@ -552,81 +870,3 @@ DWORD matchBytesInPages(const void *pattern, DWORD patternSize, DWORD lowerBound
|
|||||||
MEMDBG_END_NAMESPACE
|
MEMDBG_END_NAMESPACE
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
|
||||||
#if 0 // disabled
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search from stopAddres back to startAddress - range
|
|
||||||
* This function is not well debugged
|
|
||||||
*/
|
|
||||||
DWORD reverseSearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length) // KMP
|
|
||||||
{
|
|
||||||
__asm
|
|
||||||
{
|
|
||||||
mov eax,search_length
|
|
||||||
alloc:
|
|
||||||
push 0
|
|
||||||
sub eax,1
|
|
||||||
jnz alloc
|
|
||||||
|
|
||||||
mov edi,search
|
|
||||||
mov edx,search_length
|
|
||||||
mov ecx,1
|
|
||||||
xor esi,esi
|
|
||||||
build_table:
|
|
||||||
mov al,byte ptr [edi+esi]
|
|
||||||
cmp al,byte ptr [edi+ecx]
|
|
||||||
sete al
|
|
||||||
test esi,esi
|
|
||||||
jz pre
|
|
||||||
test al,al
|
|
||||||
jnz pre
|
|
||||||
mov esi,[esp+esi*4-4]
|
|
||||||
jmp build_table
|
|
||||||
pre:
|
|
||||||
test al,al
|
|
||||||
jz write_table
|
|
||||||
inc esi
|
|
||||||
write_table:
|
|
||||||
mov [esp+ecx*4],esi
|
|
||||||
|
|
||||||
inc ecx
|
|
||||||
cmp ecx,edx
|
|
||||||
jb build_table
|
|
||||||
|
|
||||||
mov esi,base
|
|
||||||
xor edx,edx
|
|
||||||
mov ecx,edx
|
|
||||||
matcher:
|
|
||||||
mov al,byte ptr [edi+ecx]
|
|
||||||
cmp al,byte ptr [esi-edx] // jichi 6/1/2014: The only place that is modified
|
|
||||||
sete al
|
|
||||||
test ecx,ecx
|
|
||||||
jz match
|
|
||||||
test al,al
|
|
||||||
jnz match
|
|
||||||
mov ecx, [esp+ecx*4-4]
|
|
||||||
jmp matcher
|
|
||||||
match:
|
|
||||||
test al,al
|
|
||||||
jz pre2
|
|
||||||
inc ecx
|
|
||||||
cmp ecx,search_length
|
|
||||||
je finish
|
|
||||||
pre2:
|
|
||||||
inc edx
|
|
||||||
cmp edx,base_length // search_length
|
|
||||||
jb matcher
|
|
||||||
mov edx,search_length
|
|
||||||
dec edx
|
|
||||||
finish:
|
|
||||||
mov ecx,search_length
|
|
||||||
sub edx,ecx
|
|
||||||
lea eax,[edx+1]
|
|
||||||
lea ecx,[ecx*4]
|
|
||||||
add esp,ecx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // 0, disabled
|
|
||||||
|
|
||||||
|
@ -5,14 +5,53 @@
|
|||||||
// 4/20/2014 jichi
|
// 4/20/2014 jichi
|
||||||
|
|
||||||
#include "memdbg/memdbg.h"
|
#include "memdbg/memdbg.h"
|
||||||
|
#ifndef MEMDBG_NO_STL
|
||||||
|
# include <functional>
|
||||||
|
#endif // MEMDBG_NO_STL
|
||||||
|
|
||||||
MEMDBG_BEGIN_NAMESPACE
|
MEMDBG_BEGIN_NAMESPACE
|
||||||
|
|
||||||
/// Estimated maximum size of the caller function, the same as ITH FindCallAndEntryAbs
|
/// Estimated maximum size of the caller function, the same as ITH FindCallAndEntryAbs
|
||||||
enum { MaximumFunctionSize = 0x800 };
|
enum { MaximumFunctionSize = 0x800 };
|
||||||
|
|
||||||
|
/// Offset added to the beginning of the searched address
|
||||||
|
enum { MemoryPaddingOffset = 0x1000 };
|
||||||
|
|
||||||
|
enum { MemoryAlignedStep = 0x10 };
|
||||||
|
|
||||||
|
#ifndef MEMDBG_NO_STL
|
||||||
|
/// Iterate address and return false if abort iteration.
|
||||||
|
typedef std::function<bool (dword_t)> address_fun_t;
|
||||||
|
typedef std::function<bool (dword_t, dword_t)> address2_fun_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the absolute address of the caller function
|
* Iterate all call and caller addresses
|
||||||
|
* @param fun the first parameter is the address of the caller, and the second parameter is the address of the call itself
|
||||||
|
* @return false if return early, and true if iterate all elements
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
bool iterCallerAddressAfterInt3(const address2_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
|
||||||
|
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);
|
||||||
|
bool iterUniqueCallerAddressAfterInt3(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate all call and caller addresses
|
||||||
|
* @param fun the parameter is the address of the call
|
||||||
|
* @return false if return early, and true if iterate all elements
|
||||||
|
*/
|
||||||
|
bool iterFarCallAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
bool iterNearCallAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
bool iterLongJumpAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
bool iterShortJumpAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
|
||||||
|
bool iterAlignedNearCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
|
||||||
|
|
||||||
|
bool iterFindBytes(const address_fun_t &fun, const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
|
||||||
|
bool iterMatchBytes(const address_fun_t &fun, const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
|
||||||
|
#endif // MEMDBG_NO_STL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the absolute address of the far caller function
|
||||||
* The same as ITH FindCallAndEntryAbs().
|
* The same as ITH FindCallAndEntryAbs().
|
||||||
*
|
*
|
||||||
* @param funcAddr callee function address
|
* @param funcAddr callee function address
|
||||||
@ -27,12 +66,15 @@ enum { MaximumFunctionSize = 0x800 };
|
|||||||
* 0x81,0xec: sub esp XXOO (0xec81)
|
* 0x81,0xec: sub esp XXOO (0xec81)
|
||||||
* 0x83,0xec: sub esp XXOO (0xec83)
|
* 0x83,0xec: sub esp XXOO (0xec83)
|
||||||
*/
|
*/
|
||||||
dword_t findCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize);
|
dword_t findCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
|
||||||
dword_t findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize);
|
dword_t findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
|
||||||
dword_t findLastCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize);
|
dword_t findLastCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
|
||||||
dword_t findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize);
|
dword_t findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
|
||||||
|
|
||||||
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 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);
|
||||||
|
|
||||||
|
dword_t findAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
|
||||||
|
dword_t findLastAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the absolute address of the long jump (not short jump) instruction address.
|
* 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
|
|||||||
* @param funcAddr callee function address
|
* @param funcAddr callee function address
|
||||||
* @param lowerBound the lower memory address to search
|
* @param lowerBound the lower memory address to search
|
||||||
* @param upperBound the upper memory address to search
|
* @param upperBound the upper memory address to search
|
||||||
|
* @param* offset the relative address to search from the lowerBound
|
||||||
|
* @param* range the relative size to search, use lowerBound - upperBound when zero
|
||||||
* @return the call instruction address if succeed or 0 if fail
|
* @return the call instruction address if succeed or 0 if fail
|
||||||
*/
|
*/
|
||||||
dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound);
|
dword_t findLongJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
dword_t findShortJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
dword_t findLastLongJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
dword_t findLastShortJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the absolute address of the far call (inter-module) instruction address.
|
* 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
|
|||||||
* @param funcAddr callee function address
|
* @param funcAddr callee function address
|
||||||
* @param lowerBound the lower memory address to search
|
* @param lowerBound the lower memory address to search
|
||||||
* @param upperBound the upper memory address to search
|
* @param upperBound the upper memory address to search
|
||||||
|
* @param* offset the relative address to search from the lowerBound
|
||||||
|
* @param* range the relative size to search, use lowerBound - upperBound when zero
|
||||||
* @return the call instruction address if succeed or 0 if fail
|
* @return the call instruction address if succeed or 0 if fail
|
||||||
*/
|
*/
|
||||||
dword_t findFarCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound);
|
dword_t findFarCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
dword_t findLastFarCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
|
||||||
/// Near call (intra-module)
|
/// Near call (intra-module)
|
||||||
dword_t findNearCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound);
|
dword_t findNearCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
dword_t findLastNearCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
|
||||||
|
|
||||||
/// Default to far call
|
/// Default to far call, for backward compatibility
|
||||||
inline dword_t findCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound)
|
inline dword_t findCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
|
||||||
{ return findFarCallAddress(funcAddr, lowerBound, upperBound); }
|
{ return findFarCallAddress(funcAddr, lowerBound, upperBound, offset, range); }
|
||||||
|
inline dword_t findLastCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
|
||||||
|
{ return findLastFarCallAddress(funcAddr, lowerBound, upperBound, offset, range); }
|
||||||
|
|
||||||
|
/// Default to long jump, for backward compatibility
|
||||||
|
inline dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
|
||||||
|
{ return findLongJumpAddress(funcAddr, lowerBound, upperBound, offset, range); }
|
||||||
|
inline dword_t findLastJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
|
||||||
|
{ return findLastLongJumpAddress(funcAddr, lowerBound, upperBound, offset, range); }
|
||||||
|
|
||||||
/// Push value >= 0xff
|
/// Push value >= 0xff
|
||||||
dword_t findPushDwordAddress(dword_t value, dword_t lowerBound, dword_t upperBound);
|
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
|
|||||||
* @exception illegal memory access
|
* @exception illegal memory access
|
||||||
*/
|
*/
|
||||||
dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumFunctionSize);
|
dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumFunctionSize);
|
||||||
|
dword_t findEnclosingFunctionBeforeDword(dword_t sig, dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
|
||||||
|
dword_t findEnclosingFunctionAfterDword(dword_t sig, dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
|
||||||
|
dword_t findEnclosingFunctionAfterInt3(dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
|
||||||
|
dword_t findEnclosingFunctionAfterNop(dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the address of the first matched pattern.
|
* Return the address of the first matched pattern.
|
||||||
@ -102,6 +165,7 @@ dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumF
|
|||||||
* @exception illegal memory access
|
* @exception illegal memory access
|
||||||
*/
|
*/
|
||||||
dword_t findBytes(const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
|
dword_t findBytes(const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
|
||||||
|
//dword_t reverseFindBytes(const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* jichi 2/5/2014: The same as findBytes except it uses widecard to match everything.
|
* jichi 2/5/2014: The same as findBytes except it uses widecard to match everything.
|
||||||
|
12
vnr/mono/mono.pri
Normal file
12
vnr/mono/mono.pri
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
# mono.pri
|
||||||
|
# 9/26/2012 jichi
|
||||||
|
|
||||||
|
DEFINES += WITH_LIB_MONO
|
||||||
|
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/monoobject.h \
|
||||||
|
$$PWD/monotype.h
|
||||||
|
|
||||||
|
# EOF
|
48
vnr/mono/monoobject.h
Normal file
48
vnr/mono/monoobject.h
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#ifndef MONOOBJECT_H
|
||||||
|
#define MONOOBJECT_H
|
||||||
|
|
||||||
|
// monoobject.h
|
||||||
|
// 12/26/2014 jichi
|
||||||
|
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
|
||||||
|
// https://github.com/mono/mono/blob/master/mono/metadata/object-internals.h
|
||||||
|
// https://github.com/mono/mono/blob/master/mono/util/mono-publib.h
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#define MONO_ZERO_LEN_ARRAY 1
|
||||||
|
|
||||||
|
// mono/io-layer/uglify.h
|
||||||
|
//typedef int8_t gint8;
|
||||||
|
//typedef int32_t gint32;
|
||||||
|
//typedef wchar_t gunichar2; // either char or wchar_t, depending on how mono is compiled
|
||||||
|
|
||||||
|
typedef int32_t mono_bool;
|
||||||
|
typedef uint8_t mono_byte;
|
||||||
|
typedef uint16_t mono_unichar2;
|
||||||
|
typedef uint32_t mono_unichar4;
|
||||||
|
|
||||||
|
// mono/metadata/object.h
|
||||||
|
|
||||||
|
typedef mono_bool MonoBoolean;
|
||||||
|
|
||||||
|
struct MonoArray;
|
||||||
|
struct MonoDelegate;
|
||||||
|
struct MonoDomain;
|
||||||
|
struct MonoException;
|
||||||
|
struct MonoString;
|
||||||
|
struct MonoThreadsSync;
|
||||||
|
struct MonoThread;
|
||||||
|
struct MonoVTable;
|
||||||
|
|
||||||
|
struct MonoObject {
|
||||||
|
MonoVTable *vtable;
|
||||||
|
MonoThreadsSync *synchronisation;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MonoString {
|
||||||
|
MonoObject object;
|
||||||
|
int32_t length;
|
||||||
|
mono_unichar2 chars[MONO_ZERO_LEN_ARRAY];
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MONOOBJECT_H
|
17
vnr/mono/monotype.h
Normal file
17
vnr/mono/monotype.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef MONOTYPE_H
|
||||||
|
#define MONOTYPE_H
|
||||||
|
|
||||||
|
// monotype.h
|
||||||
|
// 12/26/2014 jichi
|
||||||
|
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
|
||||||
|
|
||||||
|
#include "mono/monoobject.h"
|
||||||
|
|
||||||
|
// Function typedefs
|
||||||
|
typedef MonoDomain *(* mono_object_get_domain_fun_t)(MonoObject *obj);
|
||||||
|
|
||||||
|
typedef MonoString *(* mono_string_new_utf16_fun_t)(MonoDomain *domain, const mono_unichar2 *text, int32_t len);
|
||||||
|
|
||||||
|
typedef char * (* mono_string_to_utf8_fun_t)(MonoString *string_obj);
|
||||||
|
|
||||||
|
#endif // MONOTYPE_H
|
@ -3,11 +3,15 @@
|
|||||||
#include "ntdll/ntdll.h"
|
#include "ntdll/ntdll.h"
|
||||||
#include "ntinspect/ntinspect.h"
|
#include "ntinspect/ntinspect.h"
|
||||||
|
|
||||||
|
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/4cb11cd3-8ce0-49d7-9dda-d62e9ae0180b/how-to-get-current-module-handle?forum=vcgeneral
|
||||||
|
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
|
||||||
|
|
||||||
//#ifdef _MSC_VER
|
//#ifdef _MSC_VER
|
||||||
//# pragma warning(disable:4018) // C4018: signed/unsigned mismatch
|
//# pragma warning(disable:4018) // C4018: signed/unsigned mismatch
|
||||||
//#endif // _MSC_VER
|
//#endif // _MSC_VER
|
||||||
|
|
||||||
namespace { // unnamed
|
namespace { // unnamed
|
||||||
|
|
||||||
// Replacement of wcscpy_s which is not available on Windows XP's msvcrt
|
// Replacement of wcscpy_s which is not available on Windows XP's msvcrt
|
||||||
// http://sakuradite.com/topic/247
|
// http://sakuradite.com/topic/247
|
||||||
errno_t wcscpy_safe(wchar_t *buffer, size_t bufferSize, const wchar_t *source)
|
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)
|
|||||||
|
|
||||||
NTINSPECT_BEGIN_NAMESPACE
|
NTINSPECT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
BOOL getCurrentProcessName(LPWSTR buffer, int bufferSize)
|
// https://social.msdn.microsoft.com/Forums/vstudio/en-US/4cb11cd3-8ce0-49d7-9dda-d62e9ae0180b/how-to-get-current-module-handle?forum=vcgeneral
|
||||||
|
HMODULE getCurrentModuleHandle() { return (HMODULE)&__ImageBase; }
|
||||||
|
|
||||||
|
/** Memory range */
|
||||||
|
|
||||||
|
BOOL getProcessName(LPWSTR buffer, int bufferSize)
|
||||||
{
|
{
|
||||||
//assert(name);
|
//assert(name);
|
||||||
PLDR_DATA_TABLE_ENTRY it;
|
PLDR_DATA_TABLE_ENTRY it;
|
||||||
@ -38,6 +47,7 @@ BOOL getCurrentProcessName(LPWSTR buffer, int bufferSize)
|
|||||||
return 0 == wcscpy_safe(buffer, bufferSize, it->BaseDllName.Buffer);
|
return 0 == wcscpy_safe(buffer, bufferSize, it->BaseDllName.Buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// See: ITH FillRange
|
||||||
BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBound)
|
BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBound)
|
||||||
{
|
{
|
||||||
//assert(lower);
|
//assert(lower);
|
||||||
@ -86,15 +96,114 @@ BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBou
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOL getCurrentMemoryRange(DWORD *lowerBound, DWORD *upperBound)
|
BOOL getProcessMemoryRange(DWORD *lowerBound, DWORD *upperBound)
|
||||||
{
|
{
|
||||||
WCHAR procName[MAX_PATH]; // cached
|
WCHAR procName[MAX_PATH]; // cached
|
||||||
*lowerBound = 0;
|
*lowerBound = 0;
|
||||||
*upperBound = 0;
|
*upperBound = 0;
|
||||||
return getCurrentProcessName(procName, MAX_PATH)
|
return getProcessName(procName, MAX_PATH)
|
||||||
&& getModuleMemoryRange(procName, lowerBound, upperBound);
|
&& getModuleMemoryRange(procName, lowerBound, upperBound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Module header */
|
||||||
|
|
||||||
|
// See: ITH AddAllModules
|
||||||
|
bool iterModule(const iter_module_fun_t &fun)
|
||||||
|
{
|
||||||
|
// Iterate loaded modules
|
||||||
|
PPEB ppeb;
|
||||||
|
__asm {
|
||||||
|
mov eax, fs:[0x30]
|
||||||
|
mov ppeb, eax
|
||||||
|
}
|
||||||
|
const DWORD start = *(DWORD *)&ppeb->Ldr->InLoadOrderModuleList;
|
||||||
|
for (auto it = (PLDR_DATA_TABLE_ENTRY)start;
|
||||||
|
it->SizeOfImage && *(DWORD *)it != start;
|
||||||
|
it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink)
|
||||||
|
if (!fun((HMODULE)it->DllBase, it->BaseDllName.Buffer))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// See: ITH AddAllModules
|
||||||
|
DWORD getExportFunction(LPCSTR funcName)
|
||||||
|
{
|
||||||
|
// Iterate loaded modules
|
||||||
|
PPEB ppeb;
|
||||||
|
__asm {
|
||||||
|
mov eax, fs:[0x30]
|
||||||
|
mov ppeb, eax
|
||||||
|
}
|
||||||
|
const DWORD start = *(DWORD *)&ppeb->Ldr->InLoadOrderModuleList;
|
||||||
|
for (auto it = (PLDR_DATA_TABLE_ENTRY)start;
|
||||||
|
it->SizeOfImage && *(DWORD *)it != start;
|
||||||
|
it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink) {
|
||||||
|
//if (moduleName && ::wcscmp(moduleName, it->BaseDllName.Buffer)) // BaseDllName.Buffer == moduleName
|
||||||
|
// continue;
|
||||||
|
if (DWORD addr = getModuleExportFunction((HMODULE)it->DllBase, funcName))
|
||||||
|
return addr;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: ITH AddModule
|
||||||
|
DWORD getModuleExportFunction(HMODULE hModule, LPCSTR funcName)
|
||||||
|
{
|
||||||
|
if (!hModule)
|
||||||
|
return 0;
|
||||||
|
DWORD startAddress = (DWORD)hModule;
|
||||||
|
IMAGE_DOS_HEADER *DosHdr = (IMAGE_DOS_HEADER *)hModule;
|
||||||
|
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
|
||||||
|
DWORD dwReadAddr = startAddress + DosHdr->e_lfanew;
|
||||||
|
IMAGE_NT_HEADERS *NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr;
|
||||||
|
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
|
||||||
|
DWORD dwExportAddr = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||||
|
if (dwExportAddr == 0)
|
||||||
|
return 0;
|
||||||
|
dwExportAddr += startAddress;
|
||||||
|
IMAGE_EXPORT_DIRECTORY *ExtDir = (IMAGE_EXPORT_DIRECTORY *)dwExportAddr;
|
||||||
|
dwExportAddr = startAddress + ExtDir->AddressOfNames;
|
||||||
|
for (UINT uj = 0; uj < ExtDir->NumberOfNames; uj++) {
|
||||||
|
DWORD dwFuncName = *(DWORD *)dwExportAddr;
|
||||||
|
LPCSTR pcFuncName = (LPCSTR)(startAddress + dwFuncName);
|
||||||
|
if (::strcmp(funcName, pcFuncName) == 0) {
|
||||||
|
char *pcFuncPtr = (char *)(startAddress + (DWORD)ExtDir->AddressOfNameOrdinals+(uj * sizeof(WORD)));
|
||||||
|
WORD word = *(WORD *)pcFuncPtr;
|
||||||
|
pcFuncPtr = (char *)(startAddress + (DWORD)ExtDir->AddressOfFunctions+(word * sizeof(DWORD)));
|
||||||
|
return startAddress + *(DWORD *)pcFuncPtr; // absolute address
|
||||||
|
}
|
||||||
|
dwExportAddr += sizeof(DWORD);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: ITH FindImportEntry
|
||||||
|
DWORD getModuleImportAddress(HMODULE hModule, DWORD exportAddress)
|
||||||
|
{
|
||||||
|
if (!hModule)
|
||||||
|
return 0;
|
||||||
|
DWORD startAddress = (DWORD)hModule;
|
||||||
|
IMAGE_DOS_HEADER *DosHdr = (IMAGE_DOS_HEADER *)hModule;
|
||||||
|
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
|
||||||
|
IMAGE_NT_HEADERS *NtHdr = (IMAGE_NT_HEADERS *)(startAddress + DosHdr->e_lfanew);
|
||||||
|
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
|
||||||
|
DWORD IAT = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
|
||||||
|
DWORD end = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
|
||||||
|
IAT += startAddress;
|
||||||
|
end += IAT;
|
||||||
|
for (DWORD pt = IAT; pt < end; pt += 4) {
|
||||||
|
DWORD addr = *(DWORD *)pt;
|
||||||
|
if (addr == (DWORD)exportAddress)
|
||||||
|
return pt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
NTINSPECT_END_NAMESPACE
|
NTINSPECT_END_NAMESPACE
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
@ -4,6 +4,9 @@
|
|||||||
// 4/20/2014 jichi
|
// 4/20/2014 jichi
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#ifndef MEMDBG_NO_STL
|
||||||
|
# include <functional>
|
||||||
|
#endif // MEMDBG_NO_STL
|
||||||
|
|
||||||
#ifndef NTINSPECT_BEGIN_NAMESPACE
|
#ifndef NTINSPECT_BEGIN_NAMESPACE
|
||||||
# define NTINSPECT_BEGIN_NAMESPACE namespace NtInspect {
|
# define NTINSPECT_BEGIN_NAMESPACE namespace NtInspect {
|
||||||
@ -14,17 +17,73 @@
|
|||||||
|
|
||||||
NTINSPECT_BEGIN_NAMESPACE
|
NTINSPECT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
// Get the module handle of the current module (not the current process that is GetModuleHandleA(0))
|
||||||
|
HMODULE getCurrentModuleHandle();
|
||||||
|
|
||||||
/// Get current module name in fs:0x30
|
/// Get current module name in fs:0x30
|
||||||
BOOL getCurrentProcessName(_Out_ LPWSTR buffer, _In_ int bufferSize);
|
BOOL getProcessName(_Out_ LPWSTR buffer, _In_ int bufferSize);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the memory range of the module if succeed
|
* Get the memory range of the module if succeed
|
||||||
* See: ITH FillRange
|
* @param moduleName
|
||||||
|
* @param[out[ lowerBound
|
||||||
|
* @param[out] upperBound
|
||||||
|
* @return if succeed
|
||||||
*/
|
*/
|
||||||
BOOL getModuleMemoryRange(_In_ LPCWSTR moduleName, _Out_ DWORD *lowerBound, _Out_ DWORD *upperBound);
|
BOOL getModuleMemoryRange(_In_ LPCWSTR moduleName, _Out_ DWORD *lowerBound, _Out_ DWORD *upperBound);
|
||||||
|
|
||||||
/// Get memory of the current process
|
/// Get memory of the current process module
|
||||||
BOOL getCurrentMemoryRange(_Out_ DWORD *lowerBound, _Out_ DWORD *upperBound);
|
BOOL getProcessMemoryRange(_Out_ DWORD *lowerBound, _Out_ DWORD *upperBound);
|
||||||
|
|
||||||
|
#ifndef NTINSPECT_NO_STL
|
||||||
|
/// Iterate module information and return false if abort iteration.
|
||||||
|
typedef std::function<bool (HMODULE hModule, LPCWSTR moduleName)> iter_module_fun_t;
|
||||||
|
#else
|
||||||
|
typedef bool (* iter_module_fun_t)(HMODULE hModule, LPCWSTR moduleName);
|
||||||
|
#endif // NTINSPECT_NO_STL
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterate all modules
|
||||||
|
* @param fun the first parameter is the address of the caller, and the second parameter is the address of the call itself
|
||||||
|
* @return false if return early, and true if iterate all elements
|
||||||
|
*/
|
||||||
|
bool iterModule(const iter_module_fun_t &fun);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the absolute address of the function imported from the given module
|
||||||
|
* @param functionName
|
||||||
|
* @param* hModule find from any module when null
|
||||||
|
* @return function address or 0
|
||||||
|
*/
|
||||||
|
DWORD getModuleExportFunction(HMODULE hModule, LPCSTR functionName);
|
||||||
|
|
||||||
|
inline DWORD getModuleExportFunctionA(LPCSTR moduleName, LPCSTR functionName)
|
||||||
|
{ return getModuleExportFunction(::GetModuleHandleA(moduleName), functionName); }
|
||||||
|
|
||||||
|
inline DWORD getModuleExportFunctionW(LPCWSTR moduleName, LPCSTR functionName)
|
||||||
|
{ return getModuleExportFunction(::GetModuleHandleW(moduleName), functionName); }
|
||||||
|
|
||||||
|
/// Get the function address exported from any module
|
||||||
|
DWORD getExportFunction(LPCSTR functionName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the import address in the specified module
|
||||||
|
* @param hModule
|
||||||
|
* @param exportAddress absolute address of the function exported from other modules
|
||||||
|
* @return function address or 0
|
||||||
|
*/
|
||||||
|
DWORD getModuleImportAddress(HMODULE hModule, DWORD exportAddress);
|
||||||
|
|
||||||
|
inline DWORD getModuleImportAddressA(LPCSTR moduleName, DWORD exportAddress)
|
||||||
|
{ return getModuleImportAddress(::GetModuleHandleA(moduleName), exportAddress); }
|
||||||
|
|
||||||
|
inline DWORD getModuleImportAddressW(LPCWSTR moduleName, DWORD exportAddress)
|
||||||
|
{ return getModuleImportAddress(::GetModuleHandleW(moduleName), exportAddress); }
|
||||||
|
|
||||||
|
/// Get the import address in the current executable
|
||||||
|
inline DWORD getProcessImportAddress(DWORD exportAddress)
|
||||||
|
{ return getModuleImportAddress(::GetModuleHandleA(nullptr), exportAddress); }
|
||||||
|
|
||||||
|
|
||||||
NTINSPECT_END_NAMESPACE
|
NTINSPECT_END_NAMESPACE
|
||||||
|
|
||||||
|
23
vnr/profile/CMakeLists.txt
Normal file
23
vnr/profile/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
set(profile_src
|
||||||
|
Profile.h
|
||||||
|
Profile.cpp
|
||||||
|
pugiconfig.hpp
|
||||||
|
pugixml.cpp
|
||||||
|
pugixml.hpp
|
||||||
|
misc.h
|
||||||
|
misc.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(profile STATIC ${profile_src})
|
||||||
|
|
||||||
|
target_compile_options(profile PRIVATE
|
||||||
|
$<$<CONFIG:Release>:>
|
||||||
|
$<$<CONFIG:Debug>:>
|
||||||
|
)
|
||||||
|
|
||||||
|
#target_link_libraries(profile comctl32.lib)
|
||||||
|
|
||||||
|
#target_compile_definitions(profile
|
||||||
|
# PRIVATE
|
||||||
|
# _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
#)
|
293
vnr/profile/Profile.cpp
Normal file
293
vnr/profile/Profile.cpp
Normal file
@ -0,0 +1,293 @@
|
|||||||
|
/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com)
|
||||||
|
* This file is part of the Interactive Text Hooker.
|
||||||
|
|
||||||
|
* Interactive Text Hooker is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "host/host.h"
|
||||||
|
#include "host/hookman.h"
|
||||||
|
#include "vnrhook/include/types.h"
|
||||||
|
#include "vnrhook/include/const.h"
|
||||||
|
#include "Profile.h"
|
||||||
|
#include "misc.h"
|
||||||
|
#include <algorithm>
|
||||||
|
#include "pugixml.hpp"
|
||||||
|
|
||||||
|
extern HookManager* man;
|
||||||
|
|
||||||
|
Profile::Profile(const std::wstring& title) :
|
||||||
|
select_index(-1),
|
||||||
|
title(title)
|
||||||
|
{}
|
||||||
|
|
||||||
|
const std::unordered_set<hook_ptr>& Profile::Hooks() const
|
||||||
|
{
|
||||||
|
return hooks;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<thread_ptr>& Profile::Threads() const
|
||||||
|
{
|
||||||
|
return threads;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::unordered_set<link_ptr>& Profile::Links() const
|
||||||
|
{
|
||||||
|
return links;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Profile::XmlReadProfile(pugi::xml_node profile)
|
||||||
|
{
|
||||||
|
auto hooks_node = profile.child(L"Hooks");
|
||||||
|
auto threads_node = profile.child(L"Threads");
|
||||||
|
auto links_node = profile.child(L"Links");
|
||||||
|
if (hooks_node && !XmlReadProfileHook(hooks_node))
|
||||||
|
return false;
|
||||||
|
if (threads_node && !XmlReadProfileThread(threads_node))
|
||||||
|
return false;
|
||||||
|
if (links_node && !XmlReadProfileLink(links_node))
|
||||||
|
return false;
|
||||||
|
auto select_node = profile.child(L"Select");
|
||||||
|
if (select_node)
|
||||||
|
{
|
||||||
|
auto thread_index = select_node.attribute(L"ThreadIndex");
|
||||||
|
if (!thread_index)
|
||||||
|
return false;
|
||||||
|
DWORD tmp_select = std::stoul(thread_index.value(), NULL, 16);
|
||||||
|
select_index = tmp_select & 0xFFFF;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Profile::XmlReadProfileHook(pugi::xml_node hooks_node)
|
||||||
|
{
|
||||||
|
for (auto hook = hooks_node.begin(); hook != hooks_node.end(); ++hook)
|
||||||
|
{
|
||||||
|
std::wstring name = hook->name();
|
||||||
|
if (name.empty() || name.compare(L"Hook") != 0)
|
||||||
|
return false;
|
||||||
|
auto type = hook->attribute(L"Type");
|
||||||
|
if (!type || type.empty())
|
||||||
|
return false;
|
||||||
|
auto code = hook->attribute(L"Code");
|
||||||
|
if (!code)
|
||||||
|
return false;
|
||||||
|
std::wstring code_value = code.value();
|
||||||
|
HookParam hp = {};
|
||||||
|
switch (type.value()[0])
|
||||||
|
{
|
||||||
|
case L'H':
|
||||||
|
if (code_value[0] != L'/')
|
||||||
|
return false;
|
||||||
|
if (code_value[1] != L'H' && code_value[1] != L'h')
|
||||||
|
return false;
|
||||||
|
if (Parse(code_value.substr(2), hp))
|
||||||
|
{
|
||||||
|
auto name = hook->attribute(L"Name");
|
||||||
|
if (!name || name.empty())
|
||||||
|
AddHook(hook_ptr(new HookProfile(hp, L"")));
|
||||||
|
else
|
||||||
|
AddHook(hook_ptr(new HookProfile(hp, name.value())));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Profile::XmlReadProfileThread(pugi::xml_node threads_node)
|
||||||
|
{
|
||||||
|
std::wstring hook_name_buffer;
|
||||||
|
for (auto thread = threads_node.begin(); thread != threads_node.end(); ++thread)
|
||||||
|
{
|
||||||
|
std::wstring name = thread->name();
|
||||||
|
if (name.empty() || name.compare(L"Thread") != 0)
|
||||||
|
return false;
|
||||||
|
auto hook_name = thread->attribute(L"HookName");
|
||||||
|
if (!hook_name)
|
||||||
|
return false;
|
||||||
|
auto context = thread->attribute(L"Context");
|
||||||
|
if (!context)
|
||||||
|
return false;
|
||||||
|
auto sub_context = thread->attribute(L"SubContext");
|
||||||
|
if (!sub_context)
|
||||||
|
return false;
|
||||||
|
auto mask = thread->attribute(L"Mask");
|
||||||
|
if (!mask)
|
||||||
|
return false;
|
||||||
|
DWORD mask_tmp = std::stoul(mask.value(), NULL, 16);
|
||||||
|
auto comment = thread->attribute(L"Comment");
|
||||||
|
auto retn = std::stoul(context.value(), NULL, 16);
|
||||||
|
auto split = std::stoul(sub_context.value(), NULL, 16);
|
||||||
|
WORD flags = mask_tmp & 0xFFFF;
|
||||||
|
auto tp = new ThreadProfile(hook_name.value(), retn, split, 0, 0, flags,
|
||||||
|
comment.value());
|
||||||
|
AddThread(thread_ptr(tp));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Profile::XmlReadProfileLink(pugi::xml_node links_node)
|
||||||
|
{
|
||||||
|
for (auto link = links_node.begin(); link != links_node.end(); ++link)
|
||||||
|
{
|
||||||
|
std::wstring name = link->name();
|
||||||
|
if (name.empty() || name.compare(L"Link") != 0)
|
||||||
|
return false;
|
||||||
|
auto from = link->attribute(L"From");
|
||||||
|
if (!from)
|
||||||
|
return false;
|
||||||
|
DWORD link_from = std::stoul(from.value(), NULL, 16);
|
||||||
|
auto to = link->attribute(L"To");
|
||||||
|
if (!to)
|
||||||
|
return false;
|
||||||
|
DWORD link_to = std::stoul(to.value(), NULL, 16);
|
||||||
|
auto lp = new LinkProfile(link_from & 0xFFFF, link_to & 0xFFFF);
|
||||||
|
AddLink(link_ptr(lp));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Profile::XmlWriteProfile(pugi::xml_node profile_node)
|
||||||
|
{
|
||||||
|
if (!hooks.empty())
|
||||||
|
{
|
||||||
|
auto node = profile_node.append_child(L"Hooks");
|
||||||
|
XmlWriteProfileHook(node);
|
||||||
|
}
|
||||||
|
if (!threads.empty())
|
||||||
|
{
|
||||||
|
auto node = profile_node.append_child(L"Threads");
|
||||||
|
XmlWriteProfileThread(node);
|
||||||
|
}
|
||||||
|
if (!links.empty())
|
||||||
|
{
|
||||||
|
auto node = profile_node.append_child(L"Links");
|
||||||
|
XmlWriteProfileLink(node);
|
||||||
|
}
|
||||||
|
if (select_index != 0xFFFF)
|
||||||
|
{
|
||||||
|
auto node = profile_node.append_child(L"Select");
|
||||||
|
node.append_attribute(L"ThreadIndex") = select_index;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Profile::XmlWriteProfileHook(pugi::xml_node hooks_node)
|
||||||
|
{
|
||||||
|
for (auto hook = hooks.begin(); hook != hooks.end(); ++hook)
|
||||||
|
{
|
||||||
|
auto hook_node = hooks_node.append_child(L"Hook");
|
||||||
|
hook_node.append_attribute(L"Type") = L"H";
|
||||||
|
hook_node.append_attribute(L"Code") = ParseCode((*hook)->HP()).c_str();
|
||||||
|
if (!(*hook)->Name().empty())
|
||||||
|
hook_node.append_attribute(L"Name") = (*hook)->Name().c_str();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Profile::XmlWriteProfileThread(pugi::xml_node threads_node)
|
||||||
|
{
|
||||||
|
for (auto thread = threads.begin(); thread != threads.end(); ++thread)
|
||||||
|
{
|
||||||
|
const std::wstring& name = (*thread)->HookName();
|
||||||
|
if (name.empty())
|
||||||
|
return false;
|
||||||
|
auto node = threads_node.append_child(L"Thread");
|
||||||
|
node.append_attribute(L"HookName") = name.c_str();
|
||||||
|
std::wstring hex = ToHexString((*thread)->Flags() & (THREAD_MASK_RETN | THREAD_MASK_SPLIT));
|
||||||
|
node.append_attribute(L"Mask") = hex.c_str();
|
||||||
|
hex = ToHexString((*thread)->Split());
|
||||||
|
node.append_attribute(L"SubContext") = hex.c_str();
|
||||||
|
hex = ToHexString((*thread)->Return());
|
||||||
|
node.append_attribute(L"Context") = hex.c_str();
|
||||||
|
if (!(*thread)->Comment().empty())
|
||||||
|
node.append_attribute(L"Comment") = (*thread)->Comment().c_str();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Profile::XmlWriteProfileLink(pugi::xml_node links_node)
|
||||||
|
{
|
||||||
|
for (auto link = links.begin(); link != links.end(); ++link)
|
||||||
|
{
|
||||||
|
auto node = links_node.append_child(L"Link");
|
||||||
|
node.append_attribute(L"From") = ToHexString((*link)->FromIndex()).c_str();
|
||||||
|
node.append_attribute(L"To") = ToHexString((*link)->ToIndex()).c_str();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Profile::Clear()
|
||||||
|
{
|
||||||
|
title = L"";
|
||||||
|
select_index = -1;
|
||||||
|
hooks.clear();
|
||||||
|
threads.clear();
|
||||||
|
links.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Profile::AddHook(hook_ptr hook)
|
||||||
|
{
|
||||||
|
hooks.insert(std::move(hook));
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the thread profile and return its index
|
||||||
|
int Profile::AddThread(thread_ptr tp)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(threads.begin(), threads.end(), [&tp](thread_ptr& thread)
|
||||||
|
{
|
||||||
|
return thread->HookName().compare(tp->HookName()) == 0 &&
|
||||||
|
thread->Return() == tp->Return() &&
|
||||||
|
thread->Split() == tp->Split();
|
||||||
|
});
|
||||||
|
if (it != threads.end())
|
||||||
|
return it - threads.begin();
|
||||||
|
threads.push_back(std::move(tp));
|
||||||
|
return threads.size() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Profile::AddLink(link_ptr link)
|
||||||
|
{
|
||||||
|
links.insert(std::move(link));
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::wstring& Profile::Title() const
|
||||||
|
{
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Profile::IsThreadSelected(thread_ptr_iter thread_profile)
|
||||||
|
{
|
||||||
|
if (thread_profile != threads.end())
|
||||||
|
{
|
||||||
|
auto thread_index = thread_profile - threads.begin();
|
||||||
|
return select_index == thread_index;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_ptr_iter Profile::FindThread(const ThreadParameter* tp, const std::wstring& hook_name) const
|
||||||
|
{
|
||||||
|
auto thread_profile = std::find_if(threads.begin(), threads.end(),
|
||||||
|
[&tp, &hook_name](const thread_ptr& thread_profile) -> bool
|
||||||
|
{
|
||||||
|
return thread_profile->HookName().compare(hook_name) == 0
|
||||||
|
&& thread_profile->Return() == tp->retn
|
||||||
|
&& thread_profile->Split() == tp->spl;
|
||||||
|
});
|
||||||
|
return thread_profile;
|
||||||
|
}
|
171
vnr/profile/Profile.h
Normal file
171
vnr/profile/Profile.h
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com)
|
||||||
|
* This file is part of the Interactive Text Hooker.
|
||||||
|
|
||||||
|
* Interactive Text Hooker is free software: you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License as published
|
||||||
|
* by the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "vnrhook/include/types.h" // HookParam
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_set>
|
||||||
|
|
||||||
|
struct ThreadParameter;
|
||||||
|
class TextThread;
|
||||||
|
class HookProfile;
|
||||||
|
class ThreadProfile;
|
||||||
|
class LinkProfile;
|
||||||
|
typedef std::unique_ptr<HookProfile> hook_ptr;
|
||||||
|
typedef std::unique_ptr<ThreadProfile> thread_ptr;
|
||||||
|
typedef std::unique_ptr<LinkProfile> link_ptr;
|
||||||
|
typedef std::vector<thread_ptr>::const_iterator thread_ptr_iter;
|
||||||
|
namespace pugi {
|
||||||
|
class xml_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define THREAD_MASK_RETN 1
|
||||||
|
#define THREAD_MASK_SPLIT 2
|
||||||
|
|
||||||
|
class HookProfile
|
||||||
|
{
|
||||||
|
HookParam hp;
|
||||||
|
std::wstring name;
|
||||||
|
public:
|
||||||
|
HookProfile(const HookParam& hp, const std::wstring& name) :
|
||||||
|
hp(hp),
|
||||||
|
name(name)
|
||||||
|
{}
|
||||||
|
const HookParam& HP() const { return hp; };
|
||||||
|
const std::wstring& Name() const { return name; };
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThreadProfile
|
||||||
|
{
|
||||||
|
std::wstring hook_name;
|
||||||
|
DWORD retn;
|
||||||
|
DWORD split;
|
||||||
|
DWORD hook_addr;
|
||||||
|
WORD hm_index, flags;
|
||||||
|
std::wstring comment;
|
||||||
|
public:
|
||||||
|
ThreadProfile(const std::wstring& hook_name,
|
||||||
|
DWORD retn,
|
||||||
|
DWORD split,
|
||||||
|
DWORD hook_addr,
|
||||||
|
WORD hm_index,
|
||||||
|
WORD flags,
|
||||||
|
const std::wstring& comment) :
|
||||||
|
hook_name(hook_name),
|
||||||
|
retn(retn),
|
||||||
|
split(split),
|
||||||
|
hook_addr(hook_addr),
|
||||||
|
hm_index(hm_index),
|
||||||
|
flags(flags),
|
||||||
|
comment(comment)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
const std::wstring& HookName() const { return hook_name; }
|
||||||
|
const std::wstring& Comment() const { return comment; }
|
||||||
|
DWORD Return() const { return retn; }
|
||||||
|
DWORD Split() const { return split; }
|
||||||
|
DWORD& HookAddress() { return hook_addr; }
|
||||||
|
WORD& HookManagerIndex() { return hm_index; }
|
||||||
|
WORD Flags() const { return flags; }
|
||||||
|
};
|
||||||
|
|
||||||
|
class LinkProfile
|
||||||
|
{
|
||||||
|
WORD from_index, to_index;
|
||||||
|
public:
|
||||||
|
LinkProfile(WORD from_index, WORD to_index) :
|
||||||
|
from_index(from_index),
|
||||||
|
to_index(to_index)
|
||||||
|
{}
|
||||||
|
WORD FromIndex() const { return from_index; }
|
||||||
|
WORD ToIndex() const { return to_index; }
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace std {
|
||||||
|
template<>
|
||||||
|
struct hash<hook_ptr> {
|
||||||
|
size_t operator()(const hook_ptr &r) const
|
||||||
|
{
|
||||||
|
return hash<DWORD>{}(r->HP().address)
|
||||||
|
^ hash<DWORD>{}(r->HP().module)
|
||||||
|
^ hash<DWORD>{}(r->HP().function);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct equal_to<hook_ptr> {
|
||||||
|
bool operator()(const hook_ptr& r, const hook_ptr& r2) const
|
||||||
|
{
|
||||||
|
return r->HP().address == r2->HP().address
|
||||||
|
&& r->HP().module == r2->HP().module
|
||||||
|
&& r->HP().function == r2->HP().function;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<>
|
||||||
|
struct hash<link_ptr> {
|
||||||
|
size_t operator()(const link_ptr &r) const
|
||||||
|
{
|
||||||
|
return hash<WORD>{}(r->FromIndex())
|
||||||
|
^ hash<WORD>{}(r->ToIndex());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
template<>
|
||||||
|
struct equal_to<link_ptr> {
|
||||||
|
bool operator()(const link_ptr& r, const link_ptr& r2) const
|
||||||
|
{
|
||||||
|
return r->FromIndex() == r2->FromIndex()
|
||||||
|
&& r->ToIndex() == r2->ToIndex();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
class Profile
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Profile(const std::wstring& title);
|
||||||
|
bool XmlReadProfile(pugi::xml_node profile_node);
|
||||||
|
bool XmlWriteProfile(pugi::xml_node profile_node);
|
||||||
|
void AddHook(hook_ptr hook);
|
||||||
|
int AddThread(thread_ptr tp);
|
||||||
|
void AddLink(link_ptr lp);
|
||||||
|
void Clear();
|
||||||
|
const std::unordered_set<hook_ptr>& Hooks() const;
|
||||||
|
const std::vector<thread_ptr>& Threads() const;
|
||||||
|
const std::unordered_set<link_ptr>& Links() const;
|
||||||
|
const std::wstring& Title() const;
|
||||||
|
thread_ptr_iter FindThread(const ThreadParameter* tp, const std::wstring& hook_name) const;
|
||||||
|
WORD& SelectedIndex() { return select_index; }
|
||||||
|
bool IsThreadSelected(thread_ptr_iter thread_profile);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool XmlReadProfileHook(pugi::xml_node hooks_node);
|
||||||
|
bool XmlReadProfileThread(pugi::xml_node threads_node);
|
||||||
|
bool XmlReadProfileLink(pugi::xml_node links_node);
|
||||||
|
bool XmlWriteProfileHook(pugi::xml_node hooks_node);
|
||||||
|
bool XmlWriteProfileThread(pugi::xml_node threads_node);
|
||||||
|
bool XmlWriteProfileLink(pugi::xml_node links_node);
|
||||||
|
|
||||||
|
std::wstring title;
|
||||||
|
std::unordered_set<hook_ptr> hooks;
|
||||||
|
std::vector<thread_ptr> threads;
|
||||||
|
std::unordered_set<link_ptr> links;
|
||||||
|
|
||||||
|
WORD select_index;
|
||||||
|
};
|
275
vnr/profile/misc.cpp
Normal file
275
vnr/profile/misc.cpp
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
#include "misc.h"
|
||||||
|
#include <regex>
|
||||||
|
#include <memory>
|
||||||
|
#include "host/host.h"
|
||||||
|
#include "vnrhook/include/const.h"
|
||||||
|
#include "vnrhook/include/types.h"
|
||||||
|
|
||||||
|
DWORD Hash(const std::wstring& module, int length)
|
||||||
|
{
|
||||||
|
DWORD hash = 0;
|
||||||
|
auto end = (length < 0 || static_cast<size_t>(length) > module.length()) ?
|
||||||
|
module.end() :
|
||||||
|
module.begin() + length;
|
||||||
|
for (auto it = module.begin(); it != end; ++it)
|
||||||
|
hash = _rotr(hash, 7) + *it;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parse(const std::wstring& cmd, HookParam& hp)
|
||||||
|
{
|
||||||
|
using std::wregex;
|
||||||
|
using std::regex_search;
|
||||||
|
// /H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]
|
||||||
|
wregex rx(L"^X?([ABWSQ])(N)?", wregex::icase);
|
||||||
|
std::match_results<std::wstring::const_iterator> m;
|
||||||
|
auto start = cmd.begin();
|
||||||
|
auto end = cmd.end();
|
||||||
|
bool result = regex_search(start, end, m, rx);
|
||||||
|
if (!result)
|
||||||
|
return result;
|
||||||
|
start = m[0].second;
|
||||||
|
if (m[2].matched)
|
||||||
|
hp.type |= NO_CONTEXT;
|
||||||
|
|
||||||
|
switch (m[1].first[0])
|
||||||
|
{
|
||||||
|
case L's':
|
||||||
|
case L'S':
|
||||||
|
hp.type |= USING_STRING;
|
||||||
|
break;
|
||||||
|
case L'e':
|
||||||
|
case L'E':
|
||||||
|
hp.type |= STRING_LAST_CHAR;
|
||||||
|
case L'a':
|
||||||
|
case L'A':
|
||||||
|
hp.type |= BIG_ENDIAN;
|
||||||
|
hp.length_offset = 1;
|
||||||
|
break;
|
||||||
|
case L'b':
|
||||||
|
case L'B':
|
||||||
|
hp.length_offset = 1;
|
||||||
|
break;
|
||||||
|
case L'h':
|
||||||
|
case L'H':
|
||||||
|
hp.type |= PRINT_DWORD;
|
||||||
|
case L'q':
|
||||||
|
case L'Q':
|
||||||
|
hp.type |= USING_STRING | USING_UNICODE;
|
||||||
|
break;
|
||||||
|
case L'l':
|
||||||
|
case L'L':
|
||||||
|
hp.type |= STRING_LAST_CHAR;
|
||||||
|
case L'w':
|
||||||
|
case L'W':
|
||||||
|
hp.type |= USING_UNICODE;
|
||||||
|
hp.length_offset = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// [data_offset[*drdo]]
|
||||||
|
std::wstring data_offset(L"(-?[[:xdigit:]]+)"), drdo(L"(\\*-?[[:xdigit:]]+)?");
|
||||||
|
rx = wregex(L"^" + data_offset + drdo, wregex::icase);
|
||||||
|
result = regex_search(start, end, m, rx);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
start = m[0].second;
|
||||||
|
hp.offset = std::stoul(m[1].str(), NULL, 16);
|
||||||
|
if (m[2].matched)
|
||||||
|
{
|
||||||
|
hp.type |= DATA_INDIRECT;
|
||||||
|
hp.index = std::stoul(m[2].str().substr(1), NULL, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// [:sub_offset[*drso]]
|
||||||
|
std::wstring sub_offset(L"(-?[[:xdigit:]]+)"), drso(L"(\\*-?[[:xdigit:]]+)?");
|
||||||
|
rx = wregex(L"^:" + sub_offset + drso, wregex::icase);
|
||||||
|
result = regex_search(start, end, m, rx);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
start = m[0].second;
|
||||||
|
hp.type |= USING_SPLIT;
|
||||||
|
hp.split = std::stoul(m[1].str(), NULL, 16);
|
||||||
|
if (m[2].matched)
|
||||||
|
{
|
||||||
|
hp.type |= SPLIT_INDIRECT;
|
||||||
|
hp.split_index = std::stoul(m[2].str().substr(1), NULL, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @addr
|
||||||
|
rx = wregex(L"^@[[:xdigit:]]+", wregex::icase);
|
||||||
|
result = regex_search(start, end, m, rx);
|
||||||
|
if (!result)
|
||||||
|
return false;
|
||||||
|
start = m[0].second;
|
||||||
|
hp.address = std::stoul(m[0].str().substr(1), NULL, 16);
|
||||||
|
if (hp.offset & 0x80000000)
|
||||||
|
hp.offset -= 4;
|
||||||
|
if (hp.split & 0x80000000)
|
||||||
|
hp.split -= 4;
|
||||||
|
|
||||||
|
// [:[module[:{name|#ordinal}]]]
|
||||||
|
// ":" -> module == NULL &% function == NULL
|
||||||
|
// "" -> MODULE_OFFSET && module == NULL && function == addr
|
||||||
|
// ":GDI.dll" -> MODULE_OFFSET && module != NULL
|
||||||
|
// ":GDI.dll:strlen" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
|
||||||
|
// ":GDI.dll:#123" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
|
||||||
|
std::wstring module(L"([[:graph:]]+)"), name(L"[[:graph:]]+"), ordinal(L"\\d+");
|
||||||
|
rx = wregex(L"^:(" + module + L"(:" + name + L"|#" + ordinal + L")?)?$", wregex::icase);
|
||||||
|
result = regex_search(start, end, m, rx);
|
||||||
|
if (result) // :[module[:{name|#ordinal}]]
|
||||||
|
{
|
||||||
|
if (m[1].matched) // module
|
||||||
|
{
|
||||||
|
hp.type |= MODULE_OFFSET;
|
||||||
|
std::wstring module = m[2];
|
||||||
|
std::transform(module.begin(), module.end(), module.begin(), ::towlower);
|
||||||
|
hp.module = Hash(module);
|
||||||
|
if (m[3].matched) // :name|#ordinal
|
||||||
|
{
|
||||||
|
hp.type |= FUNCTION_OFFSET;
|
||||||
|
hp.function = Hash(m[3].str().substr(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rx = wregex(L"^!([[:xdigit:]]+)(!([[:xdigit:]]+))?$", wregex::icase);
|
||||||
|
result = regex_search(start, end, m, rx);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
hp.type |= MODULE_OFFSET;
|
||||||
|
hp.module = std::stoul(m[1].str(), NULL, 16);
|
||||||
|
if (m[2].matched)
|
||||||
|
{
|
||||||
|
hp.type |= FUNCTION_OFFSET;
|
||||||
|
hp.function = std::stoul(m[2].str().substr(1), NULL, 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Hack. Hook is relative to the executable. Store the original address in function.
|
||||||
|
// hp.module == NULL && hp.function != NULL
|
||||||
|
hp.type |= MODULE_OFFSET;
|
||||||
|
hp.function = hp.address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring ParseCode(const HookParam& hp)
|
||||||
|
{
|
||||||
|
std::wstring code(L"/H");
|
||||||
|
WCHAR c;
|
||||||
|
if (hp.type & PRINT_DWORD)
|
||||||
|
c = L'H';
|
||||||
|
else if (hp.type & USING_UNICODE)
|
||||||
|
{
|
||||||
|
if (hp.type & USING_STRING)
|
||||||
|
c = L'Q';
|
||||||
|
else if (hp.type & STRING_LAST_CHAR)
|
||||||
|
c = L'L';
|
||||||
|
else
|
||||||
|
c = L'W';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (hp.type & USING_STRING)
|
||||||
|
c = L'S';
|
||||||
|
else if (hp.type & BIG_ENDIAN)
|
||||||
|
c = L'A';
|
||||||
|
else if (hp.type & STRING_LAST_CHAR)
|
||||||
|
c = L'E';
|
||||||
|
else
|
||||||
|
c = L'B';
|
||||||
|
}
|
||||||
|
code += c;
|
||||||
|
if (hp.type & NO_CONTEXT)
|
||||||
|
code += L'N';
|
||||||
|
if (hp.offset >> 31)
|
||||||
|
code += L'-' + ToHexString(-(hp.offset + 4));
|
||||||
|
else
|
||||||
|
code += ToHexString(hp.offset);
|
||||||
|
if (hp.type & DATA_INDIRECT)
|
||||||
|
{
|
||||||
|
if (hp.index >> 31)
|
||||||
|
code += L'*-' + ToHexString(-hp.index);
|
||||||
|
else
|
||||||
|
code += L'*' + ToHexString(hp.index);
|
||||||
|
}
|
||||||
|
if (hp.type & USING_SPLIT)
|
||||||
|
{
|
||||||
|
if (hp.split >> 31)
|
||||||
|
code += L":-" + ToHexString(-(4 + hp.split));
|
||||||
|
else
|
||||||
|
code += L":" + ToHexString(hp.split);
|
||||||
|
}
|
||||||
|
if (hp.type & SPLIT_INDIRECT)
|
||||||
|
{
|
||||||
|
if (hp.split_index >> 31)
|
||||||
|
code += L"*-" + ToHexString(-hp.split_index);
|
||||||
|
else
|
||||||
|
code += L"*" + ToHexString(hp.split_index);
|
||||||
|
}
|
||||||
|
if (hp.module)
|
||||||
|
{
|
||||||
|
code += L"@" + ToHexString(hp.address) + L"!" + ToHexString(hp.module);
|
||||||
|
if (hp.function)
|
||||||
|
code += L"!" + ToHexString(hp.function);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Hack. The original address is stored in the function field
|
||||||
|
// if (module == NULL && function != NULL).
|
||||||
|
// MODULE_OFFSET and FUNCTION_OFFSET are removed from HookParam.type in
|
||||||
|
// TextHook::UnsafeInsertHookCode() and can not be used here.
|
||||||
|
if (hp.function)
|
||||||
|
code += L"@" + ToHexString(hp.function);
|
||||||
|
else
|
||||||
|
code += L"@" + ToHexString(hp.address) + L":";
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::string toMultiByteString(const std::wstring& unicodeString)
|
||||||
|
{
|
||||||
|
int cbMultiByte = WideCharToMultiByte(932, 0, unicodeString.c_str(), unicodeString.length(),
|
||||||
|
NULL, 0, NULL, NULL);
|
||||||
|
auto lpMultiByteStr = std::make_unique<CHAR[]>(cbMultiByte);
|
||||||
|
WideCharToMultiByte(932, 0, unicodeString.c_str(), unicodeString.length(),
|
||||||
|
lpMultiByteStr.get(), cbMultiByte, NULL, NULL);
|
||||||
|
return std::string(lpMultiByteStr.get(), cbMultiByte);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring toUnicodeString(const std::string& mbString)
|
||||||
|
{
|
||||||
|
int cchWideChar = MultiByteToWideChar(932, 0, mbString.c_str(), mbString.length(), NULL, 0);
|
||||||
|
auto lpWideCharStr = std::make_unique<WCHAR[]>(cchWideChar);
|
||||||
|
MultiByteToWideChar(932, 0, mbString.c_str(), mbString.length(), lpWideCharStr.get(), cchWideChar);
|
||||||
|
return std::wstring(lpWideCharStr.get(), cchWideChar);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address)
|
||||||
|
{
|
||||||
|
std::wstring hook_name;
|
||||||
|
WaitForSingleObject(pr.hookman_mutex, 0);
|
||||||
|
auto hooks = (const Hook*)pr.hookman_map;
|
||||||
|
for (int i = 0; i < MAX_HOOK; ++i)
|
||||||
|
{
|
||||||
|
auto& hook = hooks[i];
|
||||||
|
if (hook.Address() == hook_address)
|
||||||
|
{
|
||||||
|
std::unique_ptr<CHAR[]> name(new CHAR[hook.NameLength()]);
|
||||||
|
// name is zero terminated
|
||||||
|
if (ReadProcessMemory(pr.process_handle, hooks[i].Name(), name.get(), hook.NameLength(), NULL))
|
||||||
|
hook_name = toUnicodeString(name.get());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ReleaseMutex(pr.hookman_mutex);
|
||||||
|
return hook_name;
|
||||||
|
}
|
22
vnr/profile/misc.h
Normal file
22
vnr/profile/misc.h
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
struct HookParam;
|
||||||
|
struct ProcessRecord;
|
||||||
|
|
||||||
|
bool Parse(const std::wstring& cmd, HookParam& hp);
|
||||||
|
DWORD Hash(const std::wstring& module, int length = -1);
|
||||||
|
std::wstring ParseCode(const HookParam& hp);
|
||||||
|
std::string toMultiByteString(const std::wstring& unicodeString);
|
||||||
|
std::wstring toUnicodeString(const std::string& mbString);
|
||||||
|
std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address);
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::wstring ToHexString(T i) {
|
||||||
|
std::wstringstream ss;
|
||||||
|
ss << std::uppercase << std::hex << i;
|
||||||
|
return ss.str();
|
||||||
|
}
|
71
vnr/profile/pugiconfig.hpp
Normal file
71
vnr/profile/pugiconfig.hpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
/**
|
||||||
|
* pugixml parser - version 1.6
|
||||||
|
* --------------------------------------------------------
|
||||||
|
* Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
||||||
|
* Report bugs and download new versions at http://pugixml.org/
|
||||||
|
*
|
||||||
|
* This library is distributed under the MIT License. See notice at the end
|
||||||
|
* of this file.
|
||||||
|
*
|
||||||
|
* This work is based on the pugxml parser, which is:
|
||||||
|
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef HEADER_PUGICONFIG_HPP
|
||||||
|
#define HEADER_PUGICONFIG_HPP
|
||||||
|
|
||||||
|
// Uncomment this to enable wchar_t mode
|
||||||
|
#define PUGIXML_WCHAR_MODE
|
||||||
|
|
||||||
|
// Uncomment this to disable XPath
|
||||||
|
// #define PUGIXML_NO_XPATH
|
||||||
|
|
||||||
|
// Uncomment this to disable STL
|
||||||
|
// #define PUGIXML_NO_STL
|
||||||
|
|
||||||
|
// Uncomment this to disable exceptions
|
||||||
|
// #define PUGIXML_NO_EXCEPTIONS
|
||||||
|
|
||||||
|
// Set this to control attributes for public classes/functions, i.e.:
|
||||||
|
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
|
||||||
|
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
|
||||||
|
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
|
||||||
|
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
|
||||||
|
|
||||||
|
// Tune these constants to adjust memory-related behavior
|
||||||
|
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
|
||||||
|
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
|
||||||
|
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
|
||||||
|
|
||||||
|
// Uncomment this to switch to header-only version
|
||||||
|
// #define PUGIXML_HEADER_ONLY
|
||||||
|
|
||||||
|
// Uncomment this to enable long long support
|
||||||
|
// #define PUGIXML_HAS_LONG_LONG
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2006-2015 Arseny Kapoulkine
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person
|
||||||
|
* obtaining a copy of this software and associated documentation
|
||||||
|
* files (the "Software"), to deal in the Software without
|
||||||
|
* restriction, including without limitation the rights to use,
|
||||||
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the
|
||||||
|
* Software is furnished to do so, subject to the following
|
||||||
|
* conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
11554
vnr/profile/pugixml.cpp
Normal file
11554
vnr/profile/pugixml.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1366
vnr/profile/pugixml.hpp
Normal file
1366
vnr/profile/pugixml.hpp
Normal file
File diff suppressed because it is too large
Load Diff
62
vnr/sakurakit/sakurakit.pri
Normal file
62
vnr/sakurakit/sakurakit.pri
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
# sakurakit.pri
|
||||||
|
# 6/28/2011 jichi
|
||||||
|
#
|
||||||
|
# config:
|
||||||
|
# - sakurakit_qml
|
||||||
|
# - sakurakit_gui
|
||||||
|
# - sakurakit_widgets
|
||||||
|
|
||||||
|
DEFINES += WITH_LIB_SAKURAKIT
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
#QT += core
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/skautorun.h \
|
||||||
|
$$PWD/skdebug.h \
|
||||||
|
$$PWD/skglobal.h \
|
||||||
|
$$PWD/skhash.h
|
||||||
|
|
||||||
|
#CONFIG(sakurakit_gui) {
|
||||||
|
# DEFINES += SK_ENABLE_GUI
|
||||||
|
# QT += gui
|
||||||
|
# HEADERS += \
|
||||||
|
# $$PWD/skuiutil.h
|
||||||
|
#
|
||||||
|
# CONFIG(sakurakit_qml) {
|
||||||
|
# DEFINES += SK_ENABLE_QML
|
||||||
|
# QT += qml quick
|
||||||
|
# HEADERS += \
|
||||||
|
# $$PWD/skdraggablequickview.h
|
||||||
|
# SOURCES += \
|
||||||
|
# $$PWD/skdraggablequickview.cc
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# CONFIG(sakurakit_widgets) {
|
||||||
|
# DEFINES += SK_ENABLE_WIDGETS
|
||||||
|
# QT += widgets
|
||||||
|
# HEADERS += \
|
||||||
|
# $$PWD/skdraggabledialog.h \
|
||||||
|
# $$PWD/skdraggablemainwindow.h \
|
||||||
|
# $$PWD/skdraggablewidget.h \
|
||||||
|
# $$PWD/skpushbutton.h \
|
||||||
|
# $$PWD/sksystemtrayicon.h \
|
||||||
|
# $$PWD/sktoolbutton.h \
|
||||||
|
# $$PWD/skwindowcontainer.h
|
||||||
|
# SOURCES += \
|
||||||
|
# $$PWD/skdraggablewidget.cc \
|
||||||
|
# $$PWD/skpushbutton.cc \
|
||||||
|
# $$PWD/sktoolbutton.cc \
|
||||||
|
# $$PWD/skwindowcontainer.cc \
|
||||||
|
# $$PWD/skdraggabledialog.cc \
|
||||||
|
# $$PWD/skdraggablemainwindow.cc
|
||||||
|
#
|
||||||
|
# CONFIG(sakurakit_qml) {
|
||||||
|
# HEADERS += \
|
||||||
|
# $$PWD/skquickwidget.h
|
||||||
|
# SOURCES += \
|
||||||
|
# $$PWD/skquickwidget.cc
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
#}
|
||||||
|
|
||||||
|
# EOF
|
42
vnr/sakurakit/skautorun.h
Normal file
42
vnr/sakurakit/skautorun.h
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
#ifndef SKAUTORUN_H
|
||||||
|
#define SKAUTORUN_H
|
||||||
|
|
||||||
|
// skautorun.h
|
||||||
|
// 9/30/2012 jichi
|
||||||
|
|
||||||
|
#include "sakurakit/skglobal.h"
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
SK_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
class SkAutoRun
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef std::function<void ()> function_type;
|
||||||
|
SkAutoRun(const function_type &start, const function_type &exit)
|
||||||
|
: exit_(exit) { start(); }
|
||||||
|
~SkAutoRun() { exit_(); }
|
||||||
|
private:
|
||||||
|
function_type exit_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkAutoRunAtStartup
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef SkAutoRun::function_type function_type;
|
||||||
|
explicit SkAutoRunAtStartup(const function_type &start) { start(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class SkAutoRunAtExit
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
typedef SkAutoRun::function_type function_type;
|
||||||
|
explicit SkAutoRunAtExit(const function_type &exit) : exit_(exit) {}
|
||||||
|
~SkAutoRunAtExit() { exit_(); }
|
||||||
|
private:
|
||||||
|
function_type exit_;
|
||||||
|
};
|
||||||
|
|
||||||
|
SK_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // SkAUTORUN_H
|
46
vnr/sakurakit/skdebug.h
Normal file
46
vnr/sakurakit/skdebug.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#ifndef SKDEBUG_H
|
||||||
|
#define SKDEBUG_H
|
||||||
|
|
||||||
|
// skdebug.h
|
||||||
|
// 10/16/2011 jichi
|
||||||
|
// Macros for debug.
|
||||||
|
|
||||||
|
// Debug I/O
|
||||||
|
// - DPRINT: similar to fprintf, or KDPrint. Print to qDebug
|
||||||
|
// - DOUT: similar to std::cout. Print to qDebug
|
||||||
|
// - DERR: similar to std::cerr. Print to qWarning
|
||||||
|
#if defined(DEBUG) && !defined(SK_NO_DEBUG)
|
||||||
|
|
||||||
|
# if defined(QT_CORE_LIB) && !defined(SK_NO_QT)
|
||||||
|
# include <QtCore/QDebug>
|
||||||
|
# define DPRINT(...) qDebug(QString("%1:%2:").arg((DEBUG), (__FUNCTION__)).toLocal8Bit().constData(), __VA_ARGS__)
|
||||||
|
# define DOUT(_msg) qDebug() << QString("%1:%2:").arg((DEBUG), (__FUNCTION__)).toLocal8Bit().constData() << _msg
|
||||||
|
# define DERR(_msg) qWarning() << QString("%1:%2:").arg((DEBUG), (__FUNCTION__)).toLocal8Bit().constData() << _msg
|
||||||
|
# else
|
||||||
|
# include <iostream>
|
||||||
|
# include <cstdio>
|
||||||
|
# define DPRINT(...) fprintf(stderr, DEBUG ":" __FUNCTION__ ": " __VA_ARGS__)
|
||||||
|
# define DWPRINT(...) fwprintf(stderr, DEBUG ":" __FUNCTION__ ": " __VA_ARGS__)
|
||||||
|
# define DOUT(_msg) std::cout << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
|
||||||
|
# define DWOUT(_msg) std::wcout << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
|
||||||
|
# define DERR(_msg) std::cerr << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
|
||||||
|
# define DWERR(_msg) std::wcerr << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
|
||||||
|
# endif // QT_CORE_LIB
|
||||||
|
|
||||||
|
#else // DEBUG
|
||||||
|
|
||||||
|
# define DPRINT(_dummy) (void)0
|
||||||
|
# define DOUT(_dummy) (void)0
|
||||||
|
# define DERR(_dummy) (void)0
|
||||||
|
|
||||||
|
//#ifdef _MSC_VER
|
||||||
|
//# pragma warning (disable:4390) // C4390: empty controlled statement found: is this the intent?
|
||||||
|
//#endif // _MSC_VER
|
||||||
|
|
||||||
|
//#ifdef __GNUC__
|
||||||
|
//# pragma GCC diagnostic ignored "-Wempty-body" // empty body in an if or else statement
|
||||||
|
//#endif // __GNUC__
|
||||||
|
|
||||||
|
#endif // DEBUG
|
||||||
|
|
||||||
|
#endif // SKDEBUG_H
|
94
vnr/sakurakit/skglobal.h
Normal file
94
vnr/sakurakit/skglobal.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
#ifndef SKGLOBAL_H
|
||||||
|
#define SKGLOBAL_H
|
||||||
|
|
||||||
|
// skglobal.h
|
||||||
|
// 9/15/2012 jichi
|
||||||
|
// Similar to QtGlobal from Qt.
|
||||||
|
//
|
||||||
|
// Conventions:
|
||||||
|
// - All classes in sakurakit will be wrapped with SK_BEGIN_NAMESPACE and SK_END_NAMESPACE
|
||||||
|
// - All classes from sakurakit begin with Sk, such as SkClassA.
|
||||||
|
// All functions from sakurakit begin with sk, such as skFuncA.
|
||||||
|
|
||||||
|
// Redefine SK_BEGIN_NAMESPACE/SK_END_NAMESPACE if need custom namespace
|
||||||
|
#ifndef SK_BEGIN_NAMESPACE
|
||||||
|
# define SK_BEGIN_NAMESPACE namespace Sk {
|
||||||
|
#endif
|
||||||
|
#ifndef SK_END_NAMESPACE
|
||||||
|
# define SK_END_NAMESPACE } // namespace Sk
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define SK_FORWARD_DECLARE_CLASS(_name) SK_BEGIN_NAMESPACE class _name; SK_END_NAMESPACE
|
||||||
|
#define SK_FORWARD_DECLARE_STRUCT(_name) SK_BEGIN_NAMESPACE struct _name; SK_END_NAMESPACE
|
||||||
|
|
||||||
|
SK_BEGIN_NAMESPACE
|
||||||
|
namespace Sk {}
|
||||||
|
SK_END_NAMESPACE
|
||||||
|
|
||||||
|
// In case Qt is not avaliable
|
||||||
|
|
||||||
|
//inline void sk_noop(void) {}
|
||||||
|
//
|
||||||
|
//template <typename T>
|
||||||
|
//inline void skUnused(T &x) { (void)x; }
|
||||||
|
|
||||||
|
#define SK_UNUSED(_var) (void)(_var)
|
||||||
|
#define SK_NOP SK_UNUSED(0)
|
||||||
|
|
||||||
|
// same as Q_DISABLE_COPY and boost::noncopyable
|
||||||
|
// Disable when BOOST_PYTHON is enabled
|
||||||
|
#ifdef BOOST_PYTHON
|
||||||
|
# define SK_DISABLE_COPY(_class)
|
||||||
|
#else
|
||||||
|
# define SK_DISABLE_COPY(_class) \
|
||||||
|
_class(const _class &); \
|
||||||
|
_class &operator=(const _class &);
|
||||||
|
#endif // BOOST_PYTHON
|
||||||
|
|
||||||
|
// - Qt-like Pimp -
|
||||||
|
|
||||||
|
// Similar to QT_DECLARE_PRIVATE
|
||||||
|
#define SK_DECLARE_PRIVATE(_class) \
|
||||||
|
friend class _class; \
|
||||||
|
typedef _class D; \
|
||||||
|
D *const d_;
|
||||||
|
|
||||||
|
// Similar to QT_DECLARE_PUBLIC
|
||||||
|
#define SK_DECLARE_PUBLIC(_class) \
|
||||||
|
friend class _class; \
|
||||||
|
typedef _class Q; \
|
||||||
|
Q *const q_;
|
||||||
|
|
||||||
|
// - Self and Base -
|
||||||
|
|
||||||
|
#define SK_CLASS(_self) \
|
||||||
|
typedef _self Self; \
|
||||||
|
Self *self() const { return const_cast<Self *>(this); }
|
||||||
|
|
||||||
|
#define SK_EXTEND_CLASS(_self, _base) \
|
||||||
|
SK_CLASS(_self) \
|
||||||
|
typedef _base Base;
|
||||||
|
|
||||||
|
#define SK_UNDEF_POS QPoint(-1, -1)
|
||||||
|
#define SK_UNDEF_POSF QPointF(-1, -1)
|
||||||
|
|
||||||
|
// - QWidget Style Class for QSS -
|
||||||
|
|
||||||
|
// Read-only property
|
||||||
|
#define SK_STYLE_CLASS(_class) \
|
||||||
|
Q_PROPERTY(QString class READ styleClass) \
|
||||||
|
public: \
|
||||||
|
QString styleClass() const { return #_class; } \
|
||||||
|
private:
|
||||||
|
|
||||||
|
// Read-write property
|
||||||
|
#define SK_SYNTHESIZE_STYLE_CLASS \
|
||||||
|
Q_PROPERTY(QString class READ styleClass WRITE setStyleClass) \
|
||||||
|
QString styleClass_; \
|
||||||
|
public: \
|
||||||
|
QString styleClass() const { return styleClass_; } \
|
||||||
|
public slots: \
|
||||||
|
void seStyleClass(const QString &value) { styleClass_ = value; } \
|
||||||
|
private:
|
||||||
|
|
||||||
|
#endif // SKGLOBAL_H
|
59
vnr/sakurakit/skhash.h
Normal file
59
vnr/sakurakit/skhash.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef SKHASH_H
|
||||||
|
#define SKHASH_H
|
||||||
|
|
||||||
|
// skhash.h
|
||||||
|
// 8/1/2011
|
||||||
|
|
||||||
|
#include "sakurakit/skglobal.h"
|
||||||
|
#include <QtGlobal>
|
||||||
|
|
||||||
|
SK_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
enum : quint64 { djb2_hash0 = 5381 };
|
||||||
|
|
||||||
|
/// djb2: h = h*33 + c
|
||||||
|
inline quint64 djb2(const quint8 *str, quint64 hash = djb2_hash0)
|
||||||
|
{
|
||||||
|
quint8 c;
|
||||||
|
while ((c = *str++))
|
||||||
|
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// s: signed char
|
||||||
|
inline quint64 djb2_s(const char *str, quint64 hash = djb2_hash0)
|
||||||
|
{
|
||||||
|
char c;
|
||||||
|
while ((c = *str++))
|
||||||
|
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// n: length
|
||||||
|
inline quint64 djb2_n(const quint8 *str, size_t len, quint64 hash = djb2_hash0)
|
||||||
|
{
|
||||||
|
while (len--)
|
||||||
|
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// sdbm: hash(i) = hash(i - 1) * 65599 + str[i];
|
||||||
|
inline quint64 sdbm(const quint8 *str, quint64 hash = 0)
|
||||||
|
{
|
||||||
|
quint8 c;
|
||||||
|
while ((c = *str++))
|
||||||
|
hash = c + (hash << 6) + (hash << 16) - hash;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline quint64 loselose(const quint8 *str, quint64 hash = 0)
|
||||||
|
{
|
||||||
|
quint8 c;
|
||||||
|
while ((c = *str++))
|
||||||
|
hash += c;
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
SK_END_NAMESPACE
|
||||||
|
|
||||||
|
#endif // SKHASH_H
|
85
vnr/texthook/growl.h
Normal file
85
vnr/texthook/growl.h
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// growl.h
|
||||||
|
// 9/17/2013 jichi
|
||||||
|
|
||||||
|
//#ifdef GROWL_HAS_GROWL
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#define GROWL_MSG_A(_msg) MessageBoxA(nullptr, _msg, "VNR Message", MB_OK)
|
||||||
|
#define GROWL_MSG(_msg) MessageBoxW(nullptr, _msg, L"VNR Message", MB_OK)
|
||||||
|
#define GROWL_WARN(_msg) MessageBoxW(nullptr, _msg, L"VNR Warning", MB_OK)
|
||||||
|
#define GROWL_ERROR(_msg) MessageBoxW(nullptr, _msg, L"VNR Error", MB_OK)
|
||||||
|
|
||||||
|
inline void GROWL_DWORD(DWORD value)
|
||||||
|
{
|
||||||
|
WCHAR buf[100];
|
||||||
|
swprintf(buf, L"DWORD: %x", value);
|
||||||
|
GROWL_MSG(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void GROWL_DWORD2(DWORD v, DWORD v2)
|
||||||
|
{
|
||||||
|
WCHAR buf[100];
|
||||||
|
swprintf(buf, L"DWORD2: %x,%x", v, v2);
|
||||||
|
GROWL_MSG(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void GROWL_DWORD3(DWORD v, DWORD v2, DWORD v3)
|
||||||
|
{
|
||||||
|
WCHAR buf[100];
|
||||||
|
swprintf(buf, L"DWORD3: %x,%x,%x", v, v2, v3);
|
||||||
|
GROWL_MSG(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void GROWL_DWORD4(DWORD v, DWORD v2, DWORD v3, DWORD v4)
|
||||||
|
{
|
||||||
|
WCHAR buf[100];
|
||||||
|
swprintf(buf, L"DWORD4: %x,%x,%x,%x", v, v2, v3, v4);
|
||||||
|
GROWL_MSG(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void GROWL_DWORD5(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5)
|
||||||
|
{
|
||||||
|
WCHAR buf[100];
|
||||||
|
swprintf(buf, L"DWORD5: %x,%x,%x,%x,%x", v, v2, v3, v4, v5);
|
||||||
|
GROWL_MSG(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void GROWL_DWORD6(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6)
|
||||||
|
{
|
||||||
|
WCHAR buf[100];
|
||||||
|
swprintf(buf, L"DWORD6: %x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6);
|
||||||
|
GROWL_MSG(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void GROWL_DWORD7(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7)
|
||||||
|
{
|
||||||
|
WCHAR buf[100];
|
||||||
|
swprintf(buf, L"DWORD7: %x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7);
|
||||||
|
GROWL_MSG(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void GROWL_DWORD8(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8)
|
||||||
|
{
|
||||||
|
WCHAR buf[100];
|
||||||
|
swprintf(buf, L"DWORD8: %x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8);
|
||||||
|
GROWL_MSG(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void GROWL_DWORD9(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8, DWORD v9)
|
||||||
|
{
|
||||||
|
WCHAR buf[100];
|
||||||
|
swprintf(buf, L"DWORD9: %x,%x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8, v9);
|
||||||
|
GROWL_MSG(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void GROWL(DWORD v) { GROWL_DWORD(v); }
|
||||||
|
inline void GROWL(LPCWSTR v) { GROWL_MSG(v); }
|
||||||
|
inline void GROWL(LPCSTR v) { GROWL_MSG_A(v); }
|
||||||
|
|
||||||
|
//#endif // GROWL_HAS_GROWL
|
||||||
|
|
||||||
|
// EOF
|
69
vnr/texthook/host/CMakeLists.txt
Normal file
69
vnr/texthook/host/CMakeLists.txt
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# texthook.pro
|
||||||
|
# CONFIG += noqtgui dll #eha # eha will catch all exceptions, but does not work on Windows XP
|
||||||
|
|
||||||
|
# DEFINES += ITH_HAS_CRT # Use native CRT
|
||||||
|
|
||||||
|
# # TODO: Get rid of dependence on msvc's swprintf
|
||||||
|
# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
|
||||||
|
set(vnrhost_src
|
||||||
|
avl_p.h
|
||||||
|
config.h
|
||||||
|
hookman.h
|
||||||
|
host.h
|
||||||
|
host_p.h
|
||||||
|
settings.h
|
||||||
|
textthread.h
|
||||||
|
textthread_p.h
|
||||||
|
hookman.cc
|
||||||
|
host.cc
|
||||||
|
pipe.cc
|
||||||
|
textthread.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/winmaker/winmaker.h
|
||||||
|
${PROJECT_SOURCE_DIR}/winmaker/winmaker.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/winmutex/winmutex.h
|
||||||
|
# ${PROJECT_SOURCE_DIR}/wintimer/wintimer.h
|
||||||
|
# ${PROJECT_SOURCE_DIR}/wintimer/wintimer.cc
|
||||||
|
# ${PROJECT_SOURCE_DIR}/wintimer/wintimerbase.cc
|
||||||
|
# ${PROJECT_SOURCE_DIR}/wintimer/wintimerbase.h
|
||||||
|
${PROJECT_SOURCE_DIR}/windbg/windbg.h
|
||||||
|
${PROJECT_SOURCE_DIR}/windbg/windbg_p.h
|
||||||
|
${PROJECT_SOURCE_DIR}/windbg/inject.h
|
||||||
|
${PROJECT_SOURCE_DIR}/windbg/inject.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/windbg/hijack.h
|
||||||
|
${PROJECT_SOURCE_DIR}/windbg/hijack.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/windbg/util.h
|
||||||
|
# ${PROJECT_SOURCE_DIR}/windbg/util.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/windbg/unload.h
|
||||||
|
${PROJECT_SOURCE_DIR}/windbg/unload.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/sakurakit/skdebug.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(vnrhost SHARED ${vnrhost_src})
|
||||||
|
|
||||||
|
set_target_properties(vnrhost PROPERTIES LINK_FLAGS /SUBSYSTEM:WINDOWS)
|
||||||
|
|
||||||
|
target_compile_options(vnrhost PRIVATE
|
||||||
|
# /GR-
|
||||||
|
$<$<CONFIG:Release>:>
|
||||||
|
$<$<CONFIG:Debug>:>
|
||||||
|
)
|
||||||
|
|
||||||
|
#STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
||||||
|
|
||||||
|
target_link_libraries(vnrhost
|
||||||
|
ithsys
|
||||||
|
profile
|
||||||
|
${WDK_HOME}/lib/wxp/i386/ntdll.lib
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_definitions(vnrhost
|
||||||
|
PRIVATE
|
||||||
|
ITH_HAS_CRT
|
||||||
|
_CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS vnrhost RUNTIME
|
||||||
|
DESTINATION .
|
||||||
|
CONFIGURATIONS Release
|
||||||
|
)
|
588
vnr/texthook/host/avl_p.h
Normal file
588
vnr/texthook/host/avl_p.h
Normal file
@ -0,0 +1,588 @@
|
|||||||
|
#pragma once
|
||||||
|
// avl_p.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// Branch: ITH/AVL.h, rev 133
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
enum { STACK_SIZE = 32 };
|
||||||
|
|
||||||
|
//#ifndef ITH_STACK
|
||||||
|
//#define ITH_STACK
|
||||||
|
|
||||||
|
template<class T, int stack_size>
|
||||||
|
class MyStack
|
||||||
|
{
|
||||||
|
int index;
|
||||||
|
T s[stack_size];
|
||||||
|
|
||||||
|
public:
|
||||||
|
MyStack(): index(0)
|
||||||
|
{ ::memset(s, 0, sizeof(s)); } // jichi 9/21/2013: assume T is atomic type
|
||||||
|
|
||||||
|
T &back() { return s[index-1]; }
|
||||||
|
int size() { return index; }
|
||||||
|
|
||||||
|
void push_back(const T &e)
|
||||||
|
{
|
||||||
|
if (index < stack_size)
|
||||||
|
s[index++]=e;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pop_back() { index--; }
|
||||||
|
|
||||||
|
T &operator[](int i) { return s[i]; }
|
||||||
|
};
|
||||||
|
//#endif // ITH_STACK
|
||||||
|
|
||||||
|
// jichi 9/22/2013: T must be a pointer type which can be deleted
|
||||||
|
template <class T, class D>
|
||||||
|
struct IHFSERVICE TreeNode
|
||||||
|
{
|
||||||
|
//typedef TreeNode<T, D> Self;
|
||||||
|
TreeNode() :
|
||||||
|
Left(nullptr), Right(nullptr), Parent(nullptr)
|
||||||
|
, rank(1)
|
||||||
|
, factor('\0'), reserve('\0')
|
||||||
|
//, key()
|
||||||
|
//, data()
|
||||||
|
{
|
||||||
|
::memset(&key, 0, sizeof(key)); // jcihi 9/26/2013: zero memory
|
||||||
|
::memset(&data, 0, sizeof(data)); // jcihi 9/26/2013: zero memory
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode(const T &k, const D &d) :
|
||||||
|
Left(nullptr), Right(nullptr), Parent(nullptr)
|
||||||
|
, rank(1)
|
||||||
|
, factor('\0'), reserve('\0') // jichi 9/21/2013: zero reserve
|
||||||
|
, key(k)
|
||||||
|
, data(d)
|
||||||
|
{}
|
||||||
|
|
||||||
|
TreeNode *Successor()
|
||||||
|
{
|
||||||
|
TreeNode *Node,
|
||||||
|
*ParentNode;
|
||||||
|
Node = Right;
|
||||||
|
if (!Node) {
|
||||||
|
Node = this;
|
||||||
|
for (;;) {
|
||||||
|
ParentNode = Node->Parent;
|
||||||
|
if (!ParentNode)
|
||||||
|
return nullptr;
|
||||||
|
if (ParentNode->Left == Node)
|
||||||
|
break;
|
||||||
|
Node = ParentNode;
|
||||||
|
}
|
||||||
|
return ParentNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
while (Node->Left)
|
||||||
|
Node = Node->Left;
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
TreeNode *Predecessor()
|
||||||
|
{
|
||||||
|
TreeNode *Node,
|
||||||
|
*ParentNode;
|
||||||
|
Node = Left;
|
||||||
|
if (!Node) {
|
||||||
|
Node = this;
|
||||||
|
for(;;) {
|
||||||
|
ParentNode = Node->Parent;
|
||||||
|
if (!ParentNode)
|
||||||
|
return nullptr;
|
||||||
|
if (ParentNode->Right == Node)
|
||||||
|
break;
|
||||||
|
Node = ParentNode;
|
||||||
|
}
|
||||||
|
return ParentNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
while (Node->Right)
|
||||||
|
Node = Node->Right;
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
int height()
|
||||||
|
{
|
||||||
|
if (!this) // jichi 9/26/2013: what?!
|
||||||
|
return 0;
|
||||||
|
int l = Left->height(),
|
||||||
|
r = Right->height(),
|
||||||
|
f = factor;
|
||||||
|
if (l - r + f != 0)
|
||||||
|
__debugbreak();
|
||||||
|
f = l > r ? l : r;
|
||||||
|
return f + 1;
|
||||||
|
}
|
||||||
|
TreeNode *Left,
|
||||||
|
*Right,
|
||||||
|
*Parent;
|
||||||
|
unsigned short rank;
|
||||||
|
char factor,
|
||||||
|
reserve;
|
||||||
|
T key;
|
||||||
|
D data;
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T,class D>
|
||||||
|
struct NodePath
|
||||||
|
{
|
||||||
|
NodePath() { ::memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH
|
||||||
|
NodePath(TreeNode<T,D> *n, int f): Node(n), fact(f) {}
|
||||||
|
TreeNode<T,D> *Node;
|
||||||
|
union { char factor; int fact; };
|
||||||
|
};
|
||||||
|
|
||||||
|
template <class T, class D, class fComp, class fCopy, class fLength>
|
||||||
|
class AVLTree
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
TreeNode<T*, D> head;
|
||||||
|
fComp fCmp;
|
||||||
|
fCopy fCpy;
|
||||||
|
fLength fLen;
|
||||||
|
|
||||||
|
public:
|
||||||
|
// - Construction -
|
||||||
|
AVLTree() {}
|
||||||
|
|
||||||
|
virtual ~AVLTree() { DeleteAll(); }
|
||||||
|
|
||||||
|
// - Properties -
|
||||||
|
|
||||||
|
TreeNode<T*, D> *TreeRoot() const { return head.Left; }
|
||||||
|
|
||||||
|
// - Actions -
|
||||||
|
|
||||||
|
void DeleteAll()
|
||||||
|
{
|
||||||
|
while (head.Left)
|
||||||
|
DeleteRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode<T*, D> *Insert(const T *key, const D &data)
|
||||||
|
{
|
||||||
|
if (head.Left) {
|
||||||
|
MyStack<TreeNode<T*, D> *,STACK_SIZE> path;
|
||||||
|
TreeNode<T*,D> *DownNode, *ParentNode, *BalanceNode, *TryNode, *NewNode; //P,T,S,Q
|
||||||
|
ParentNode = &head;
|
||||||
|
path.push_back(ParentNode);
|
||||||
|
char factor,f;
|
||||||
|
BalanceNode = DownNode = head.Left;
|
||||||
|
for (;;) { //The first part of AVL tree insert. Just do as binary tree insert routine and record some nodes.
|
||||||
|
factor = fCmp(key,DownNode->key);
|
||||||
|
if (factor == 0)
|
||||||
|
return DownNode; //Duplicate key. Return and do nothing.
|
||||||
|
TryNode = _FactorLink(DownNode, factor);
|
||||||
|
if (factor == -1)
|
||||||
|
path.push_back(DownNode);
|
||||||
|
if (TryNode) { //DownNode has a child.
|
||||||
|
if (TryNode->factor != 0) { //Keep track of unbalance node and its parent.
|
||||||
|
ParentNode = DownNode;
|
||||||
|
BalanceNode = TryNode;
|
||||||
|
}
|
||||||
|
DownNode = TryNode;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break; //Finished binary tree search;
|
||||||
|
}
|
||||||
|
while (path.size()) {
|
||||||
|
path.back()->rank++;
|
||||||
|
path.pop_back();
|
||||||
|
}
|
||||||
|
size_t sz = fLen(key) + 1;
|
||||||
|
T *new_key = new T[sz];
|
||||||
|
::memset(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
|
||||||
|
fCpy(new_key, key);
|
||||||
|
TryNode = new TreeNode<T*, D>(new_key, data);
|
||||||
|
_FactorLink(DownNode, factor) = TryNode;
|
||||||
|
TryNode->Parent = DownNode;
|
||||||
|
NewNode = TryNode;
|
||||||
|
//Finished binary tree insert. Next to do is to modify balance factors between
|
||||||
|
//BalanceNode and the new node.
|
||||||
|
TreeNode<T*, D> *ModifyNode;
|
||||||
|
factor = fCmp(key, BalanceNode->key);
|
||||||
|
//factor=key<BalanceNode->key ? factor=-1:1; //Determine the balance factor at BalanceNode.
|
||||||
|
ModifyNode = DownNode = _FactorLink(BalanceNode,factor);
|
||||||
|
//ModifyNode will be the 1st child.
|
||||||
|
//DownNode will travel from here to the recent inserted node (TryNode).
|
||||||
|
while (DownNode != TryNode) { //Check if we reach the bottom.
|
||||||
|
f = fCmp(key,DownNode->key);
|
||||||
|
//f=_FactorCompare(key,DownNode->key);
|
||||||
|
DownNode->factor = f;
|
||||||
|
DownNode = _FactorLink(DownNode, f);//Modify balance factor and travels down.
|
||||||
|
}
|
||||||
|
//Finshed modifying balance factor.
|
||||||
|
//Next to do is check the tree if it's unbalance and recover balance.
|
||||||
|
if (BalanceNode->factor == 0) { //Tree has grown higher.
|
||||||
|
BalanceNode->factor = factor;
|
||||||
|
_IncreaseHeight(); //Modify balance factor and increase the height.
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
if (BalanceNode->factor + factor == 0) { //Tree has gotten more balanced.
|
||||||
|
BalanceNode->factor = 0; //Set balance factor to 0.
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
//Tree has gotten out of balance.
|
||||||
|
if (ModifyNode->factor == factor) //A node and its child has same factor. Single rotation.
|
||||||
|
DownNode = _SingleRotation(BalanceNode, ModifyNode, factor);
|
||||||
|
else //A node and its child has converse factor. Double rotation.
|
||||||
|
DownNode = _DoubleRotation(BalanceNode, ModifyNode, factor);
|
||||||
|
//Finished the balancing work. Set child field to the root of the new child tree.
|
||||||
|
if (BalanceNode == ParentNode->Left)
|
||||||
|
ParentNode->Left = DownNode;
|
||||||
|
else
|
||||||
|
ParentNode->Right = DownNode;
|
||||||
|
return NewNode;
|
||||||
|
}
|
||||||
|
else { //root null?
|
||||||
|
size_t sz = fLen(key) + 1;
|
||||||
|
T *new_key = new T[sz];
|
||||||
|
::memset(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
|
||||||
|
fCpy(new_key, key);
|
||||||
|
head.Left = new TreeNode<T *, D>(new_key, data);
|
||||||
|
head.rank++;
|
||||||
|
_IncreaseHeight();
|
||||||
|
return head.Left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bool Delete(T *key)
|
||||||
|
{
|
||||||
|
NodePath<T*,D> PathNode;
|
||||||
|
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
|
||||||
|
path.push_back(NodePath<T*,D>(&head,-1));
|
||||||
|
TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
|
||||||
|
TryNode=head.Left;
|
||||||
|
char factor;
|
||||||
|
for (;;) { //Search for the
|
||||||
|
if (TryNode == 0)
|
||||||
|
return false; //Not found.
|
||||||
|
factor = fCmp(key, TryNode->key);
|
||||||
|
if (factor == 0)
|
||||||
|
break; //Key found, continue to delete.
|
||||||
|
//factor = _FactorCompare( key, TryNode->key );
|
||||||
|
path.push_back(NodePath<T*,D>(TryNode,factor));
|
||||||
|
TryNode = _FactorLink(TryNode,factor); //Move to left.
|
||||||
|
}
|
||||||
|
SuccNode = TryNode->Right; //Find a successor.
|
||||||
|
factor = 1;
|
||||||
|
if (SuccNode == 0) {
|
||||||
|
SuccNode = TryNode->Left;
|
||||||
|
factor = -1;
|
||||||
|
}
|
||||||
|
path.push_back(NodePath<T*,D>(TryNode,factor));
|
||||||
|
while (SuccNode) {
|
||||||
|
path.push_back(NodePath<T*,D>(SuccNode, -factor));
|
||||||
|
SuccNode = _FactorLink(SuccNode,-factor);
|
||||||
|
}
|
||||||
|
PathNode = path.back();
|
||||||
|
delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
|
||||||
|
TryNode->key = PathNode.Node->key; //Replace key and data field with the successor or predecessor.
|
||||||
|
PathNode.Node->key = nullptr;
|
||||||
|
TryNode->data = PathNode.Node->data;
|
||||||
|
path.pop_back();
|
||||||
|
_FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
|
||||||
|
delete PathNode.Node; //Remove the successor from the tree and release memory.
|
||||||
|
PathNode = path.back();
|
||||||
|
for (int i=0; i<path.size(); i++)
|
||||||
|
if (path[i].factor==-1)
|
||||||
|
path[i].Node->rank--;
|
||||||
|
for (;;) { //Rebalance the tree along the path back to the root.
|
||||||
|
if (path.size()==1) {
|
||||||
|
_DecreaseHeight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
BalanceNode = PathNode.Node;
|
||||||
|
if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurve since subtree height stays.
|
||||||
|
BalanceNode->factor=-PathNode.factor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (BalanceNode->factor == PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurve.
|
||||||
|
BalanceNode->factor = 0;
|
||||||
|
path.pop_back();
|
||||||
|
PathNode = path.back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Node get out of balance. Here raises 3 cases.
|
||||||
|
ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
|
||||||
|
if (ChildNode->factor == 0) { // New case different to insert operation.
|
||||||
|
TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
|
||||||
|
path.pop_back();
|
||||||
|
PathNode = path.back();
|
||||||
|
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
|
||||||
|
TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
|
||||||
|
else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
|
||||||
|
TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
|
||||||
|
}
|
||||||
|
path.pop_back(); //Recurse back along the path.
|
||||||
|
PathNode = path.back();
|
||||||
|
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
D &operator [](T *key)
|
||||||
|
{ return (Insert(key,D())->data); }
|
||||||
|
|
||||||
|
TreeNode<T*,D> *Search(const T *key)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *Find=head.Left;
|
||||||
|
char k;
|
||||||
|
while (Find != 0) {//&&Find->key!=key)
|
||||||
|
k = fCmp(key, Find->key);
|
||||||
|
if (k == 0) break;
|
||||||
|
Find = _FactorLink(Find, k);
|
||||||
|
}
|
||||||
|
return Find;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode<T*,D> *SearchIndex(unsigned int rank)
|
||||||
|
{
|
||||||
|
unsigned int r = head.rank;
|
||||||
|
if (rank == -1)
|
||||||
|
return 0;
|
||||||
|
if (++rank>=r)
|
||||||
|
return 0;
|
||||||
|
TreeNode<T*,D> *n=&head;
|
||||||
|
while (r!=rank) {
|
||||||
|
if (rank>r) {
|
||||||
|
n=n->Right;
|
||||||
|
rank-=r;
|
||||||
|
r=n->rank;
|
||||||
|
} else {
|
||||||
|
n=n->Left;
|
||||||
|
r=n->rank;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode<T*,D> *Begin()
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *Node = head.Left;
|
||||||
|
if (Node)
|
||||||
|
while (Node->Left) Node = Node->Left;
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode<T*,D> *End()
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *Node=head.Left;
|
||||||
|
if (Node)
|
||||||
|
while (Node->Right) Node = Node->Right;
|
||||||
|
return Node;
|
||||||
|
}
|
||||||
|
unsigned int Count() const { return head.rank - 1; }
|
||||||
|
|
||||||
|
template <class Fn>
|
||||||
|
Fn TraverseTree(Fn &f)
|
||||||
|
{ return TraverseTreeNode(head.Left,f); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
bool DeleteRoot()
|
||||||
|
{
|
||||||
|
NodePath<T*,D> PathNode;
|
||||||
|
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
|
||||||
|
path.push_back(NodePath<T*,D>(&head,-1));
|
||||||
|
TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
|
||||||
|
TryNode=head.Left;
|
||||||
|
char factor;
|
||||||
|
SuccNode=TryNode->Right; //Find a successor.
|
||||||
|
factor=1;
|
||||||
|
if (SuccNode==0)
|
||||||
|
{
|
||||||
|
SuccNode=TryNode->Left;
|
||||||
|
factor=-1;
|
||||||
|
}
|
||||||
|
path.push_back(NodePath<T*,D>(TryNode,factor));
|
||||||
|
while (SuccNode) {
|
||||||
|
path.push_back(NodePath<T*,D>(SuccNode,-factor));
|
||||||
|
SuccNode=_FactorLink(SuccNode,-factor);
|
||||||
|
}
|
||||||
|
PathNode=path.back();
|
||||||
|
delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
|
||||||
|
TryNode->key=PathNode.Node->key; //Replace key and data field with the successor.
|
||||||
|
PathNode.Node->key = nullptr;
|
||||||
|
TryNode->data=PathNode.Node->data;
|
||||||
|
path.pop_back();
|
||||||
|
_FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
|
||||||
|
delete PathNode.Node; //Remove the successor from the tree and release memory.
|
||||||
|
PathNode=path.back();
|
||||||
|
for (int i=0;i<path.size();i++)
|
||||||
|
if (path[i].factor==-1)
|
||||||
|
path[i].Node->rank--;
|
||||||
|
for (;;) { //Rebalance the tree along the path back to the root.
|
||||||
|
if (path.size() == 1) {
|
||||||
|
_DecreaseHeight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
BalanceNode = PathNode.Node;
|
||||||
|
if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurse since subtree height not changed.
|
||||||
|
BalanceNode->factor=-PathNode.factor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (BalanceNode->factor==PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurse.
|
||||||
|
BalanceNode->factor=0;
|
||||||
|
path.pop_back();
|
||||||
|
PathNode=path.back();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
//Node get out of balance. Here raises 3 cases.
|
||||||
|
ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
|
||||||
|
if (ChildNode->factor == 0) { // New case different to insert operation.
|
||||||
|
TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
|
||||||
|
path.pop_back();
|
||||||
|
PathNode=path.back();
|
||||||
|
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
|
||||||
|
TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
|
||||||
|
else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
|
||||||
|
TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
|
||||||
|
}
|
||||||
|
path.pop_back(); // Recurve back along the path.
|
||||||
|
PathNode=path.back();
|
||||||
|
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
template <class Fn>
|
||||||
|
Fn TraverseTreeNode(TreeNode<T*,D> *Node, Fn &f)
|
||||||
|
{
|
||||||
|
if (Node) {
|
||||||
|
if (Node->Left)
|
||||||
|
TraverseTreeNode(Node->Left,f);
|
||||||
|
f(Node);
|
||||||
|
if (Node->Right)
|
||||||
|
TraverseTreeNode(Node->Right,f);
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
TreeNode<T*,D> *_SingleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
|
||||||
|
_FactorLink(BalanceNode, factor) = Node;
|
||||||
|
_FactorLink(ModifyNode, -factor) = BalanceNode;
|
||||||
|
if (Node)
|
||||||
|
Node->Parent = BalanceNode;
|
||||||
|
ModifyNode->Parent = BalanceNode->Parent;
|
||||||
|
BalanceNode->Parent = ModifyNode;
|
||||||
|
BalanceNode->factor = ModifyNode->factor = 0; //After single rotation, set all factor of 3 node to 0.
|
||||||
|
if (factor == 1)
|
||||||
|
ModifyNode->rank += BalanceNode->rank;
|
||||||
|
else
|
||||||
|
BalanceNode->rank -= ModifyNode->rank;
|
||||||
|
return ModifyNode;
|
||||||
|
}
|
||||||
|
TreeNode<T*,D> *_SingleRotation2(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
|
||||||
|
_FactorLink(BalanceNode, factor) = Node;
|
||||||
|
_FactorLink(ModifyNode, -factor) = BalanceNode;
|
||||||
|
if (Node) Node->Parent = BalanceNode;
|
||||||
|
ModifyNode->Parent = BalanceNode->Parent;
|
||||||
|
BalanceNode->Parent = ModifyNode;
|
||||||
|
ModifyNode->factor = -factor;
|
||||||
|
if (factor == 1)
|
||||||
|
ModifyNode->rank+=BalanceNode->rank;
|
||||||
|
else
|
||||||
|
BalanceNode->rank-=ModifyNode->rank;
|
||||||
|
return ModifyNode;
|
||||||
|
}
|
||||||
|
TreeNode<T*,D> *_DoubleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
|
||||||
|
{
|
||||||
|
TreeNode<T*,D> *DownNode = _FactorLink(ModifyNode, -factor);
|
||||||
|
TreeNode<T*,D> *Node1, *Node2;
|
||||||
|
Node1 = _FactorLink(DownNode, factor);
|
||||||
|
Node2 = _FactorLink(DownNode, -factor);
|
||||||
|
_FactorLink(ModifyNode, -factor) = Node1;
|
||||||
|
_FactorLink(DownNode, factor) = ModifyNode;
|
||||||
|
_FactorLink(BalanceNode, factor) = Node2;
|
||||||
|
_FactorLink(DownNode, -factor) = BalanceNode;
|
||||||
|
if (Node1)
|
||||||
|
Node1->Parent = ModifyNode;
|
||||||
|
if (Node2)
|
||||||
|
Node2->Parent = BalanceNode;
|
||||||
|
DownNode->Parent = BalanceNode->Parent;
|
||||||
|
BalanceNode->Parent = DownNode;
|
||||||
|
ModifyNode->Parent = DownNode;
|
||||||
|
//Set factor according to the result.
|
||||||
|
if (DownNode->factor == factor) {
|
||||||
|
BalanceNode->factor = -factor;
|
||||||
|
ModifyNode->factor = 0;
|
||||||
|
} else if (DownNode->factor == 0)
|
||||||
|
BalanceNode->factor = ModifyNode->factor = 0;
|
||||||
|
else {
|
||||||
|
BalanceNode->factor = 0;
|
||||||
|
ModifyNode->factor = factor;
|
||||||
|
}
|
||||||
|
DownNode->factor = 0;
|
||||||
|
if (factor==1) {
|
||||||
|
ModifyNode->rank -= DownNode->rank;
|
||||||
|
DownNode->rank += BalanceNode->rank;
|
||||||
|
} else {
|
||||||
|
DownNode->rank += ModifyNode->rank;
|
||||||
|
BalanceNode->rank -= DownNode->rank;
|
||||||
|
}
|
||||||
|
return DownNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
TreeNode<T*,D>* &__fastcall _FactorLink(TreeNode<T*,D> *Node, char factor)
|
||||||
|
//Private helper method to retrieve child according to factor.
|
||||||
|
//Return right child if factor>0 and left child otherwise.
|
||||||
|
{ return factor>0? Node->Right : Node->Left; }
|
||||||
|
|
||||||
|
void Check()
|
||||||
|
{
|
||||||
|
unsigned int k = (unsigned int)head.Right;
|
||||||
|
unsigned int t = head.Left->height();
|
||||||
|
if (k != t)
|
||||||
|
__debugbreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _IncreaseHeight()
|
||||||
|
{
|
||||||
|
unsigned int k = (unsigned int)head.Right;
|
||||||
|
head.Right = (TreeNode<T*,D>*)++k;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _DecreaseHeight()
|
||||||
|
{
|
||||||
|
unsigned int k = (unsigned int)head.Right;
|
||||||
|
head.Right = (TreeNode<T*,D>*)--k;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SCMP
|
||||||
|
{
|
||||||
|
char operator()(const char *s1,const char *s2)
|
||||||
|
{
|
||||||
|
int t = _stricmp(s1, s2);
|
||||||
|
return t == 0 ? 0 : t > 0 ? 1 :-1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SCPY { char *operator()(char *dest, const char *src) { return strcpy(dest, src); } };
|
||||||
|
struct SLEN { int operator()(const char *str) { return strlen(str); } };
|
||||||
|
|
||||||
|
struct WCMP
|
||||||
|
{
|
||||||
|
char operator()(const wchar_t *s1,const wchar_t *s2)
|
||||||
|
{
|
||||||
|
int t =_wcsicmp(s1, s2);
|
||||||
|
return t == 0 ? 0 : t > 0 ? 1 : -1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WCPY { wchar_t *operator()(wchar_t *dest, const wchar_t *src) { return wcscpy(dest,src); } };
|
||||||
|
struct WLEN { int operator()(const wchar_t *str) { return wcslen(str); } };
|
||||||
|
|
||||||
|
// EOF
|
16
vnr/texthook/host/config.h
Normal file
16
vnr/texthook/host/config.h
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// config.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// The first header file that are included by all source files.
|
||||||
|
|
||||||
|
#define IHF // for dll import
|
||||||
|
//#include "ith/dllconfig.h"
|
||||||
|
#define IHFAPI __stdcall
|
||||||
|
#ifdef IHF
|
||||||
|
# define IHFSERVICE __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
# define IHFSERVICE __declspec(dllimport)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// EOF
|
1039
vnr/texthook/host/hookman.cc
Normal file
1039
vnr/texthook/host/hookman.cc
Normal file
File diff suppressed because it is too large
Load Diff
153
vnr/texthook/host/hookman.h
Normal file
153
vnr/texthook/host/hookman.h
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// hookman.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// Branch: ITH/HookManager.h, rev 133
|
||||||
|
|
||||||
|
#include "host/avl_p.h"
|
||||||
|
#include "host/textthread.h"
|
||||||
|
#include "winmutex/winmutex.h"
|
||||||
|
|
||||||
|
namespace pugi {
|
||||||
|
class xml_node;
|
||||||
|
}
|
||||||
|
class Profile;
|
||||||
|
|
||||||
|
enum { MAX_REGISTER = 0xf };
|
||||||
|
enum { MAX_PREV_REPEAT_LENGTH = 0x20 };
|
||||||
|
|
||||||
|
struct ProcessRecord {
|
||||||
|
DWORD pid_register;
|
||||||
|
DWORD hookman_register;
|
||||||
|
DWORD module_register;
|
||||||
|
//DWORD engine_register; // jichi 10/19/2014: removed
|
||||||
|
HANDLE process_handle;
|
||||||
|
HANDLE hookman_mutex;
|
||||||
|
HANDLE hookman_section;
|
||||||
|
LPVOID hookman_map;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ThreadTable : public MyVector<TextThread *, 0x40>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual void SetThread(DWORD number, TextThread *ptr);
|
||||||
|
virtual TextThread *FindThread(DWORD number);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct IHFSERVICE TCmp { char operator()(const ThreadParameter *t1, const ThreadParameter *t2); };
|
||||||
|
struct IHFSERVICE TCpy { void operator()(ThreadParameter *t1, const ThreadParameter *t2); };
|
||||||
|
struct IHFSERVICE TLen { int operator()(const ThreadParameter *t); };
|
||||||
|
|
||||||
|
typedef DWORD (*ProcessEventCallback)(DWORD pid);
|
||||||
|
|
||||||
|
class IHFSERVICE HookManager : public AVLTree<ThreadParameter, DWORD, TCmp, TCpy, TLen>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
HookManager();
|
||||||
|
~HookManager();
|
||||||
|
// jichi 12/26/2013: remove virtual modifiers
|
||||||
|
TextThread *FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split);
|
||||||
|
TextThread *FindSingle(DWORD number);
|
||||||
|
ProcessRecord *GetProcessRecord(DWORD pid);
|
||||||
|
DWORD GetProcessIDByPath(LPCWSTR str); // private
|
||||||
|
void RemoveSingleThread(DWORD number);
|
||||||
|
//void LockHookman();
|
||||||
|
//void UnlockHookman();
|
||||||
|
void ResetRepeatStatus();
|
||||||
|
void ClearCurrent();
|
||||||
|
void AddLink(WORD from, WORD to);
|
||||||
|
void UnLink(WORD from);
|
||||||
|
void UnLinkAll(WORD from);
|
||||||
|
void SelectCurrent(DWORD num);
|
||||||
|
void DetachProcess(DWORD pid);
|
||||||
|
void SetCurrent(TextThread *it);
|
||||||
|
void AddConsoleOutput(LPCWSTR text);
|
||||||
|
|
||||||
|
// jichi 10/27/2013: Add const; add space.
|
||||||
|
void DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD split, int len, bool space);
|
||||||
|
|
||||||
|
void ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD split); // private
|
||||||
|
void RemoveProcessContext(DWORD pid); // private
|
||||||
|
void RemoveSingleHook(DWORD pid, DWORD addr);
|
||||||
|
void RegisterThread(TextThread*, DWORD); // private
|
||||||
|
void RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread);
|
||||||
|
void RegisterProcess(DWORD pid, DWORD hookman, DWORD module);
|
||||||
|
void UnRegisterProcess(DWORD pid);
|
||||||
|
//void SetName(DWORD);
|
||||||
|
|
||||||
|
DWORD GetCurrentPID(); // private
|
||||||
|
HANDLE GetCmdHandleByPID(DWORD pid);
|
||||||
|
|
||||||
|
ConsoleCallback RegisterConsoleCallback(ConsoleCallback cf)
|
||||||
|
{ return (ConsoleCallback)_InterlockedExchange((long*)&console,(long)cf); }
|
||||||
|
|
||||||
|
ConsoleWCallback RegisterConsoleWCallback(ConsoleWCallback cf)
|
||||||
|
{ return (ConsoleWCallback)_InterlockedExchange((long*)&wconsole,(long)cf); }
|
||||||
|
|
||||||
|
ThreadEventCallback RegisterThreadCreateCallback(ThreadEventCallback cf)
|
||||||
|
{ return (ThreadEventCallback)_InterlockedExchange((long*)&create,(long)cf); }
|
||||||
|
|
||||||
|
ThreadEventCallback RegisterThreadRemoveCallback(ThreadEventCallback cf)
|
||||||
|
{ return (ThreadEventCallback)_InterlockedExchange((long*)&remove,(long)cf); }
|
||||||
|
|
||||||
|
ThreadEventCallback RegisterThreadResetCallback(ThreadEventCallback cf)
|
||||||
|
{ return (ThreadEventCallback)_InterlockedExchange((long*)&reset,(long)cf); }
|
||||||
|
|
||||||
|
ThreadEventCallback RegisterAddRemoveLinkCallback(ThreadEventCallback cf)
|
||||||
|
{ return (ThreadEventCallback)_InterlockedExchange((long*)&addRemoveLink, (long)cf); }
|
||||||
|
|
||||||
|
ProcessEventCallback RegisterProcessAttachCallback(ProcessEventCallback cf)
|
||||||
|
{ return (ProcessEventCallback)_InterlockedExchange((long*)&attach,(long)cf); }
|
||||||
|
|
||||||
|
ProcessEventCallback RegisterProcessDetachCallback(ProcessEventCallback cf)
|
||||||
|
{ return (ProcessEventCallback)_InterlockedExchange((long*)&detach,(long)cf); }
|
||||||
|
|
||||||
|
ProcessEventCallback RegisterProcessNewHookCallback(ProcessEventCallback cf)
|
||||||
|
{ return (ProcessEventCallback)_InterlockedExchange((long*)&hook,(long)cf); }
|
||||||
|
|
||||||
|
ProcessEventCallback ProcessNewHook() { return hook; }
|
||||||
|
TextThread *GetCurrentThread() { return current; } // private
|
||||||
|
ProcessRecord *Records() { return record; } // private
|
||||||
|
ThreadTable *Table() { return thread_table; } // private
|
||||||
|
|
||||||
|
//DWORD& SplitTime() { return split_time; }
|
||||||
|
//DWORD& RepeatCount() { return repeat_count; }
|
||||||
|
//DWORD& CyclicRemove() { return cyclic_remove; }
|
||||||
|
//DWORD& GlobalFilter() { return global_filter; }
|
||||||
|
void ConsoleOutput(LPCSTR text) { if (console) console(text); } // not thread safe
|
||||||
|
void ConsoleOutputW(LPCWSTR text) { if (wconsole) wconsole(text); } // not thread safe
|
||||||
|
|
||||||
|
void OnThreadCreate(pugi::xml_node profile_node, TextThread* thread);
|
||||||
|
void GetProfile(DWORD pid, pugi::xml_node profile_node);
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef win_mutex<CRITICAL_SECTION> mutex_type;
|
||||||
|
mutex_type hmcs;
|
||||||
|
|
||||||
|
TextThread *current;
|
||||||
|
ConsoleCallback console; // jichi 12/25/2013: add console output callback
|
||||||
|
ConsoleWCallback wconsole;
|
||||||
|
ThreadEventCallback create,
|
||||||
|
remove,
|
||||||
|
reset,
|
||||||
|
addRemoveLink;
|
||||||
|
ProcessEventCallback attach,
|
||||||
|
detach,
|
||||||
|
hook;
|
||||||
|
DWORD current_pid;
|
||||||
|
ThreadTable *thread_table;
|
||||||
|
HANDLE destroy_event;
|
||||||
|
ProcessRecord record[MAX_REGISTER + 1];
|
||||||
|
HANDLE text_pipes[MAX_REGISTER + 1],
|
||||||
|
cmd_pipes[MAX_REGISTER + 1],
|
||||||
|
recv_threads[MAX_REGISTER + 1];
|
||||||
|
WORD register_count,
|
||||||
|
new_thread_number;
|
||||||
|
|
||||||
|
// jichi 1/16/2014: Stop adding new threads when full
|
||||||
|
bool IsFull() const; // { return new_thread_number >= MAX_HOOK; }
|
||||||
|
bool IsEmpty() const { return !new_thread_number; }
|
||||||
|
void HookManager::AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid);
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
646
vnr/texthook/host/host.cc
Normal file
646
vnr/texthook/host/host.cc
Normal file
@ -0,0 +1,646 @@
|
|||||||
|
// host.cc
|
||||||
|
// 8/24/2013 jichi
|
||||||
|
// Branch IHF/main.cpp, rev 111
|
||||||
|
// 8/24/2013 TODO: Clean up this file
|
||||||
|
|
||||||
|
//#ifdef _MSC_VER
|
||||||
|
//# pragma warning(disable:4800) // C4800: forcing value to bool (performance warning)
|
||||||
|
//#endif // _MSC_VER
|
||||||
|
|
||||||
|
//#include "customfilter.h"
|
||||||
|
#include "growl.h"
|
||||||
|
#include "host.h"
|
||||||
|
#include "host_p.h"
|
||||||
|
#include "settings.h"
|
||||||
|
#include "vnrhook/include/const.h"
|
||||||
|
#include "vnrhook/include/defs.h"
|
||||||
|
#include "vnrhook/include/types.h"
|
||||||
|
#include "ithsys/ithsys.h"
|
||||||
|
#include "windbg/inject.h"
|
||||||
|
#include "winmaker/winmaker.h"
|
||||||
|
#include "ccutil/ccmacro.h"
|
||||||
|
#include <commctrl.h>
|
||||||
|
|
||||||
|
//#define ITH_WINE
|
||||||
|
//#define ITH_USE_UX_DLLS IthIsWine()
|
||||||
|
//#define ITH_USE_XP_DLLS (IthIsWindowsXp() && !IthIsWine())
|
||||||
|
|
||||||
|
#define DEBUG "vnrhost/host.cc"
|
||||||
|
#include "sakurakit/skdebug.h"
|
||||||
|
|
||||||
|
namespace { // unnamed
|
||||||
|
|
||||||
|
//enum { HOOK_TIMEOUT = -50000000 }; // in nanoseconds = 5 seconds
|
||||||
|
|
||||||
|
CRITICAL_SECTION cs;
|
||||||
|
//WCHAR exist[] = ITH_PIPEEXISTS_EVENT;
|
||||||
|
//WCHAR mutex[] = L"ITH_RUNNING";
|
||||||
|
//WCHAR EngineName[] = ITH_ENGINE_DLL;
|
||||||
|
//WCHAR EngineNameXp[] = ITH_ENGINE_XP_DLL;
|
||||||
|
//WCHAR DllName[] = ITH_CLIENT_DLL;
|
||||||
|
//WCHAR DllNameXp[] = ITH_CLIENT_XP_DLL;
|
||||||
|
HANDLE hServerMutex; // jichi 9/28/2013: used to guard pipe
|
||||||
|
HANDLE hHookMutex; // jichi 9/28/2013: used to guard hook modification
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
//extern LPWSTR current_dir;
|
||||||
|
extern CRITICAL_SECTION detach_cs;
|
||||||
|
|
||||||
|
Settings *settings;
|
||||||
|
HWND hMainWnd;
|
||||||
|
HANDLE hPipeExist;
|
||||||
|
BOOL running;
|
||||||
|
|
||||||
|
#define ITH_SYNC_HOOK IthMutexLocker locker(::hHookMutex)
|
||||||
|
|
||||||
|
namespace { // unnamed
|
||||||
|
|
||||||
|
void GetDebugPriv()
|
||||||
|
{
|
||||||
|
HANDLE hToken;
|
||||||
|
DWORD dwRet;
|
||||||
|
NTSTATUS status;
|
||||||
|
|
||||||
|
TOKEN_PRIVILEGES Privileges = {1,{0x14,0,SE_PRIVILEGE_ENABLED}};
|
||||||
|
|
||||||
|
NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
|
||||||
|
|
||||||
|
status = NtAdjustPrivilegesToken(hToken, 0, &Privileges, sizeof(Privileges), 0, &dwRet);
|
||||||
|
//if (STATUS_SUCCESS == status)
|
||||||
|
//{
|
||||||
|
// admin = 1;
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
// WCHAR buffer[0x10];
|
||||||
|
// swprintf(buffer, L"%.8X",status);
|
||||||
|
// MessageBox(0, NotAdmin, buffer, 0);
|
||||||
|
//}
|
||||||
|
NtClose(hToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sendCommand(HANDLE hCmd, HostCommandType cmd)
|
||||||
|
{
|
||||||
|
IO_STATUS_BLOCK ios;
|
||||||
|
//SendParam sp = {};
|
||||||
|
//sp.type = cmd;
|
||||||
|
DWORD data = cmd;
|
||||||
|
return hCmd && NT_SUCCESS(NtWriteFile(hCmd, 0,0,0, &ios, &data, sizeof(data), 0,0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 9/22/2013: Change current directory to the same as main module path
|
||||||
|
// Otherwise NtFile* would not work for files with relative paths.
|
||||||
|
//BOOL ChangeCurrentDirectory()
|
||||||
|
//{
|
||||||
|
// if (const wchar_t *path = GetMainModulePath()) // path to VNR's python exe
|
||||||
|
// if (const wchar_t *base = wcsrchr(path, L'\\')) {
|
||||||
|
// size_t len = base - path;
|
||||||
|
// if (len < MAX_PATH) {
|
||||||
|
// wchar_t buf[MAX_PATH];
|
||||||
|
// wcsncpy(buf, path, len);
|
||||||
|
// buf[len] = 0;
|
||||||
|
// return SetCurrentDirectoryW(buf);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return FALSE;
|
||||||
|
//}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
bool injectUsingWin32Api(LPCWSTR path, HANDLE hProc)
|
||||||
|
{ return WinDbg::injectDllW(path, 0, hProc); }
|
||||||
|
|
||||||
|
bool ejectUsingWin32Api(HANDLE hModule, HANDLE hProc)
|
||||||
|
{ return WinDbg::ejectDll(hModule, hProc); }
|
||||||
|
|
||||||
|
// The original inject logic in ITH
|
||||||
|
bool injectUsingNTApi(LPCWSTR path, HANDLE hProc)
|
||||||
|
{
|
||||||
|
LPVOID lpvAllocAddr = 0;
|
||||||
|
DWORD dwWrite = 0x1000; //, len = 0;
|
||||||
|
//if (IthIsWine())
|
||||||
|
// lpvAllocAddr = VirtualAllocEx(hProc, nullptr, dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
//else
|
||||||
|
NtAllocateVirtualMemory(hProc, &lpvAllocAddr, 0, &dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
if (!lpvAllocAddr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
CheckThreadStart();
|
||||||
|
|
||||||
|
//Copy module path into address space of target process.
|
||||||
|
//if (IthIsWine())
|
||||||
|
// WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
|
||||||
|
//else
|
||||||
|
NtWriteVirtualMemory(hProc, lpvAllocAddr, (LPVOID)path, MAX_PATH << 1, &dwWrite);
|
||||||
|
HANDLE hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc);
|
||||||
|
if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) {
|
||||||
|
DOUT("ERROR: failed to create remote cli thread");
|
||||||
|
//ConsoleOutput(ErrorRemoteThread);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// jichi 9/28/2013: no wait as it will not blocked
|
||||||
|
NtWaitForSingleObject(hTH, 0, nullptr);
|
||||||
|
THREAD_BASIC_INFORMATION info;
|
||||||
|
NtQueryInformationThread(hTH, ThreadBasicInformation, &info, sizeof(info), &dwWrite);
|
||||||
|
NtClose(hTH);
|
||||||
|
|
||||||
|
// jichi 10/19/2014: Disable inject the second dll
|
||||||
|
//if (info.ExitStatus) {
|
||||||
|
// //IthCoolDown();
|
||||||
|
// wcscpy(p, engine);
|
||||||
|
// //if (IthIsWine())
|
||||||
|
// // WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
|
||||||
|
// //else
|
||||||
|
// NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
|
||||||
|
// hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc);
|
||||||
|
// if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) {
|
||||||
|
// //ConsoleOutput(ErrorRemoteThread);
|
||||||
|
// ConsoleOutput("vnrhost:inject: ERROR: failed to create remote eng thread");
|
||||||
|
// return error;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // jichi 9/28/2013: no wait as it will not blocked
|
||||||
|
// NtWaitForSingleObject(hTH, 0, nullptr);
|
||||||
|
// NtClose(hTH);
|
||||||
|
//}
|
||||||
|
|
||||||
|
dwWrite = 0;
|
||||||
|
//if (IthIsWine())
|
||||||
|
// VirtualFreeEx(hProc, lpvAllocAddr, dwWrite, MEM_RELEASE);
|
||||||
|
//else
|
||||||
|
NtFreeVirtualMemory(hProc, &lpvAllocAddr, &dwWrite, MEM_RELEASE);
|
||||||
|
return info.ExitStatus != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ejectUsingNTApi(HANDLE hModule, HANDLE hProc)
|
||||||
|
{
|
||||||
|
//IthCoolDown();
|
||||||
|
//#ifdef ITH_WINE // Nt series crash on wine
|
||||||
|
// hThread = IthCreateThread(FreeLibrary, engine, hProc);
|
||||||
|
//#else
|
||||||
|
HANDLE hThread = IthCreateThread(LdrUnloadDll, module, hProc);
|
||||||
|
//#endif // ITH_WINE
|
||||||
|
if (hThread == 0 || hThread == INVALID_HANDLE_VALUE)
|
||||||
|
return false;
|
||||||
|
// jichi 10/22/2013: Timeout might crash vnrsrv
|
||||||
|
//NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout);
|
||||||
|
NtWaitForSingleObject(hThread, 0, nullptr);
|
||||||
|
//man->UnlockHookman();
|
||||||
|
THREAD_BASIC_INFORMATION info;
|
||||||
|
NtQueryInformationThread(hThread, ThreadBasicInformation, &info, sizeof(info), 0);
|
||||||
|
NtClose(hThread);
|
||||||
|
NtSetEvent(hPipeExist, 0);
|
||||||
|
FreeThreadStart(hProc);
|
||||||
|
return info.ExitStatus;
|
||||||
|
}
|
||||||
|
#endif // 0
|
||||||
|
|
||||||
|
bool Inject(HANDLE hProc)
|
||||||
|
{
|
||||||
|
//LPWSTR dllname = (IthIsWindowsXp() && !IthIsWine()) ? DllNameXp : DllName;
|
||||||
|
//LPCWSTR dllname = ITH_USE_XP_DLLS ? ITH_DLL_XP : ITH_DLL;
|
||||||
|
//LPCWSTR dllname = ITH_DLL;
|
||||||
|
//if (!IthCheckFile(dllname))
|
||||||
|
// return error;
|
||||||
|
wchar_t path[MAX_PATH];
|
||||||
|
size_t len = IthGetCurrentModulePath(path, MAX_PATH);
|
||||||
|
if (!len)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
wchar_t *p;
|
||||||
|
for (p = path + len; *p != L'\\'; p--);
|
||||||
|
p++; // ending with L"\\"
|
||||||
|
|
||||||
|
//LPCWSTR mp = GetMainModulePath();
|
||||||
|
//len = wcslen(mp);
|
||||||
|
//memcpy(path, mp, len << 1);
|
||||||
|
//memset(path + len, 0, (MAX_PATH - len) << 1);
|
||||||
|
//LPWSTR p;
|
||||||
|
//for (p = path + len; *p != L'\\'; p--); // Always a \ after drive letter.
|
||||||
|
//p++;
|
||||||
|
::wcscpy(p, ITH_DLL);
|
||||||
|
|
||||||
|
return WinDbg::injectDllW(path, 0, hProc);
|
||||||
|
//if (IthIsWindowsXp()) // && !IthIsWine())
|
||||||
|
// return injectUsingWin32Api(path, hProc);
|
||||||
|
//else
|
||||||
|
// return injectUsingNTApi(path, hProc);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
void CreateNewPipe();
|
||||||
|
|
||||||
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
|
||||||
|
{
|
||||||
|
CC_UNUSED(lpvReserved);
|
||||||
|
switch (fdwReason)
|
||||||
|
{
|
||||||
|
case DLL_PROCESS_ATTACH:
|
||||||
|
LdrDisableThreadCalloutsForDll(hinstDLL);
|
||||||
|
InitializeCriticalSection(&::cs);
|
||||||
|
IthInitSystemService();
|
||||||
|
GetDebugPriv();
|
||||||
|
// jichi 12/20/2013: Since I already have a GUI, I don't have to InitCommonControls()
|
||||||
|
//Used by timers.
|
||||||
|
InitCommonControls();
|
||||||
|
// jichi 8/24/2013: Create hidden window so that ITH can access timer and events
|
||||||
|
hMainWnd = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0);
|
||||||
|
//wm_register_hidden_class("vnrsrv.class");
|
||||||
|
//hMainWnd = (HWND)wm_create_hidden_window("vnrsrv", "Button", hinstDLL);
|
||||||
|
//ChangeCurrentDirectory();
|
||||||
|
break;
|
||||||
|
case DLL_PROCESS_DETACH:
|
||||||
|
if (::running)
|
||||||
|
Host_Close();
|
||||||
|
DeleteCriticalSection(&::cs);
|
||||||
|
IthCloseSystemService();
|
||||||
|
//wm_destroy_window(hMainWnd);
|
||||||
|
DestroyWindow(hMainWnd);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE IthOpenPipe(LPWSTR name, ACCESS_MASK direction)
|
||||||
|
{
|
||||||
|
UNICODE_STRING us;
|
||||||
|
RtlInitUnicodeString(&us, name);
|
||||||
|
SECURITY_DESCRIPTOR sd = {1};
|
||||||
|
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
|
||||||
|
HANDLE hFile;
|
||||||
|
IO_STATUS_BLOCK isb;
|
||||||
|
if (NT_SUCCESS(NtCreateFile(&hFile, direction, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0)))
|
||||||
|
return hFile;
|
||||||
|
else
|
||||||
|
return INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum { IHS_SIZE = 0x80 };
|
||||||
|
enum { IHS_BUFF_SIZE = IHS_SIZE - sizeof(HookParam) };
|
||||||
|
|
||||||
|
struct InsertHookStruct
|
||||||
|
{
|
||||||
|
SendParam sp;
|
||||||
|
BYTE name_buffer[IHS_SIZE];
|
||||||
|
};
|
||||||
|
|
||||||
|
IHFSERVICE void IHFAPI Host_Init()
|
||||||
|
{
|
||||||
|
InitializeCriticalSection(&::cs);
|
||||||
|
GetDebugPriv();
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE void IHFAPI Host_Destroy()
|
||||||
|
{
|
||||||
|
InitializeCriticalSection(&::cs);
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE BOOL IHFAPI Host_Open()
|
||||||
|
{
|
||||||
|
BOOL result = false;
|
||||||
|
EnterCriticalSection(&::cs);
|
||||||
|
DWORD present;
|
||||||
|
hServerMutex = IthCreateMutex(ITH_SERVER_MUTEX, 1, &present);
|
||||||
|
if (present)
|
||||||
|
//MessageBox(0,L"Already running.",0,0);
|
||||||
|
// jichi 8/24/2013
|
||||||
|
GROWL_WARN(L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!");
|
||||||
|
else if (!::running) {
|
||||||
|
::running = true;
|
||||||
|
::settings = new Settings;
|
||||||
|
::man = new HookManager;
|
||||||
|
//cmdq = new CommandQueue;
|
||||||
|
InitializeCriticalSection(&detach_cs);
|
||||||
|
|
||||||
|
::hHookMutex = IthCreateMutex(ITH_SERVER_HOOK_MUTEX, FALSE);
|
||||||
|
result = true;
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&::cs);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_Start()
|
||||||
|
{
|
||||||
|
//IthBreak();
|
||||||
|
CreateNewPipe();
|
||||||
|
::hPipeExist = IthCreateEvent(ITH_PIPEEXISTS_EVENT);
|
||||||
|
NtSetEvent(::hPipeExist, nullptr);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_Close()
|
||||||
|
{
|
||||||
|
BOOL result = FALSE;
|
||||||
|
EnterCriticalSection(&::cs);
|
||||||
|
if (::running) {
|
||||||
|
::running = FALSE;
|
||||||
|
HANDLE hRecvPipe = IthOpenPipe(recv_pipe, GENERIC_WRITE);
|
||||||
|
NtClose(hRecvPipe);
|
||||||
|
NtClearEvent(::hPipeExist);
|
||||||
|
//delete cmdq;
|
||||||
|
delete man;
|
||||||
|
delete settings;
|
||||||
|
NtClose(::hHookMutex);
|
||||||
|
NtClose(hServerMutex);
|
||||||
|
NtClose(::hPipeExist);
|
||||||
|
DeleteCriticalSection(&detach_cs);
|
||||||
|
result = TRUE;
|
||||||
|
}
|
||||||
|
LeaveCriticalSection(&::cs);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_GetPIDByName(LPCWSTR pwcTarget)
|
||||||
|
{
|
||||||
|
DWORD dwSize = 0x20000,
|
||||||
|
dwExpectSize = 0;
|
||||||
|
LPVOID pBuffer = 0;
|
||||||
|
SYSTEM_PROCESS_INFORMATION *spiProcessInfo;
|
||||||
|
DWORD dwPid = 0;
|
||||||
|
DWORD dwStatus;
|
||||||
|
|
||||||
|
NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize);
|
||||||
|
if (!NT_SUCCESS(dwStatus)) {
|
||||||
|
NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE);
|
||||||
|
if (dwStatus != STATUS_INFO_LENGTH_MISMATCH || dwExpectSize < dwSize)
|
||||||
|
return 0;
|
||||||
|
dwSize = (dwExpectSize | 0xFFF) + 0x4001; //
|
||||||
|
pBuffer = 0;
|
||||||
|
NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
|
||||||
|
dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize);
|
||||||
|
if (!NT_SUCCESS(dwStatus)) goto _end;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *)pBuffer; spiProcessInfo->dNext;) {
|
||||||
|
spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *)
|
||||||
|
((DWORD)spiProcessInfo + spiProcessInfo -> dNext);
|
||||||
|
if (_wcsicmp(pwcTarget, spiProcessInfo -> usName.Buffer) == 0) {
|
||||||
|
dwPid = spiProcessInfo->dUniqueProcessId;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dwPid)
|
||||||
|
DOUT("pid not found");
|
||||||
|
//if (dwPid == 0) ConsoleOutput(ErrorNoProcess);
|
||||||
|
_end:
|
||||||
|
NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE);
|
||||||
|
return dwPid;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE bool IHFAPI Host_InjectByPID(DWORD pid)
|
||||||
|
{
|
||||||
|
WCHAR str[0x80];
|
||||||
|
if (!::running)
|
||||||
|
return 0;
|
||||||
|
if (pid == current_process_id) {
|
||||||
|
//ConsoleOutput(SelfAttach);
|
||||||
|
DOUT("refuse to inject myself");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (man->GetProcessRecord(pid)) {
|
||||||
|
//ConsoleOutput(AlreadyAttach);
|
||||||
|
DOUT("already attached");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
swprintf(str, ITH_HOOKMAN_MUTEX_ L"%d", pid);
|
||||||
|
DWORD s;
|
||||||
|
NtClose(IthCreateMutex(str, 0, &s));
|
||||||
|
if (s) {
|
||||||
|
DOUT("already locked");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CLIENT_ID id;
|
||||||
|
OBJECT_ATTRIBUTES oa = {};
|
||||||
|
HANDLE hProc;
|
||||||
|
id.UniqueProcess = pid;
|
||||||
|
id.UniqueThread = 0;
|
||||||
|
oa.uLength = sizeof(oa);
|
||||||
|
if (!NT_SUCCESS(NtOpenProcess(&hProc,
|
||||||
|
PROCESS_QUERY_INFORMATION|
|
||||||
|
PROCESS_CREATE_THREAD|
|
||||||
|
PROCESS_VM_OPERATION|
|
||||||
|
PROCESS_VM_READ|
|
||||||
|
PROCESS_VM_WRITE,
|
||||||
|
&oa, &id))) {
|
||||||
|
//ConsoleOutput(ErrorOpenProcess);
|
||||||
|
DOUT("failed to open process");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (!engine)
|
||||||
|
// engine = ITH_USE_XP_DLLS ? ITH_ENGINE_XP_DLL : ITH_ENGINE_DLL;
|
||||||
|
bool ok = Inject(hProc);
|
||||||
|
//NtClose(hProc); //already closed
|
||||||
|
if (!ok) {
|
||||||
|
DOUT("inject failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//swprintf(str, FormatInject, pid, module);
|
||||||
|
//ConsoleOutput(str);
|
||||||
|
DOUT("inject succeed");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 7/16/2014: Test if process is valid before creating remote threads
|
||||||
|
// See: http://msdn.microsoft.com/en-us/library/ms687032.aspx
|
||||||
|
static bool isProcessTerminated(HANDLE hProc)
|
||||||
|
{ return WAIT_OBJECT_0 == ::WaitForSingleObject(hProc, 0); }
|
||||||
|
//static bool isProcessRunning(HANDLE hProc)
|
||||||
|
//{ return WAIT_TIMEOUT == ::WaitForSingleObject(hProc, 0); }
|
||||||
|
|
||||||
|
// jichi 7/16/2014: Test if process is valid before creating remote threads
|
||||||
|
//static bool isProcessRunning(DWORD pid)
|
||||||
|
//{
|
||||||
|
// bool ret = false;
|
||||||
|
// HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
|
||||||
|
// if (hProc) {
|
||||||
|
// DWORD status;
|
||||||
|
// if (::GetExitCodeProcess(hProc, &status)) {
|
||||||
|
// ret = status == STILL_ACTIVE;
|
||||||
|
// ::CloseHandle(hProc);
|
||||||
|
// } else
|
||||||
|
// ret = true;
|
||||||
|
// }
|
||||||
|
// return ret;
|
||||||
|
//}
|
||||||
|
|
||||||
|
IHFSERVICE bool IHFAPI Host_ActiveDetachProcess(DWORD pid)
|
||||||
|
{
|
||||||
|
ITH_SYNC_HOOK;
|
||||||
|
|
||||||
|
//man->LockHookman();
|
||||||
|
ProcessRecord *pr = man->GetProcessRecord(pid);
|
||||||
|
HANDLE hCmd = man->GetCmdHandleByPID(pid);
|
||||||
|
if (pr == 0 || hCmd == 0)
|
||||||
|
return false;
|
||||||
|
HANDLE hProc;
|
||||||
|
//hProc = pr->process_handle; //This handle may be closed(thus invalid) during the detach process.
|
||||||
|
NtDuplicateObject(NtCurrentProcess(), pr->process_handle,
|
||||||
|
NtCurrentProcess(), &hProc, 0, 0, DUPLICATE_SAME_ACCESS); // Make a copy of the process handle.
|
||||||
|
HANDLE hModule = (HANDLE)pr->module_register;
|
||||||
|
if (!hModule) {
|
||||||
|
DOUT("process module not found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 7/15/2014: Process already closed
|
||||||
|
if (isProcessTerminated(hProc)) {
|
||||||
|
DOUT("process has terminated");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 10/19/2014: Disable the second dll
|
||||||
|
//engine = pr->engine_register;
|
||||||
|
//engine &= ~0xff;
|
||||||
|
|
||||||
|
DOUT("send detach command");
|
||||||
|
bool ret = sendCommand(hCmd, HOST_COMMAND_DETACH);
|
||||||
|
|
||||||
|
// jichi 7/15/2014: Process already closed
|
||||||
|
//if (isProcessTerminated(hProc)) {
|
||||||
|
// DOUT("process has terminated");
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
//WinDbg::ejectDll(hModule, 0, hProc); // eject in case module has not loaded yet
|
||||||
|
|
||||||
|
//cmdq->AddRequest(sp, pid);
|
||||||
|
////#ifdef ITH_WINE // Nt series crash on wine
|
||||||
|
//// hThread = IthCreateThread(FreeLibrary, engine, hProc);
|
||||||
|
////#else
|
||||||
|
// hThread = IthCreateThread(LdrUnloadDll, engine, hProc);
|
||||||
|
////#endif // ITH_WINE
|
||||||
|
// if (hThread == 0 || hThread == INVALID_HANDLE_VALUE)
|
||||||
|
// return FALSE;
|
||||||
|
// // jichi 10/22/2013: Timeout might crash vnrsrv
|
||||||
|
// //const LONGLONG timeout = HOOK_TIMEOUT;
|
||||||
|
// //NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout);
|
||||||
|
// NtWaitForSingleObject(hThread, 0, nullptr);
|
||||||
|
// NtClose(hThread);
|
||||||
|
NtClose(hProc);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_GetHookManager(HookManager** hookman)
|
||||||
|
{
|
||||||
|
if (::running) {
|
||||||
|
*hookman = man;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE bool IHFAPI Host_GetSettings(Settings **p)
|
||||||
|
{
|
||||||
|
if (::running) {
|
||||||
|
*p = settings;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE bool IHFAPI Host_HijackProcess(DWORD pid)
|
||||||
|
{
|
||||||
|
//ITH_SYNC_HOOK;
|
||||||
|
HANDLE hCmd = man->GetCmdHandleByPID(pid);
|
||||||
|
return hCmd && sendCommand(hCmd, HOST_COMMAND_HIJACK_PROCESS);
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_InsertHook(DWORD pid, HookParam *hp, LPCSTR name)
|
||||||
|
{
|
||||||
|
ITH_SYNC_HOOK;
|
||||||
|
|
||||||
|
HANDLE hCmd = man->GetCmdHandleByPID(pid);
|
||||||
|
if (hCmd == 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
InsertHookStruct s;
|
||||||
|
s.sp.type = HOST_COMMAND_NEW_HOOK;
|
||||||
|
s.sp.hp = *hp;
|
||||||
|
size_t len;
|
||||||
|
if (name)
|
||||||
|
len = ::strlen(name);
|
||||||
|
else
|
||||||
|
len = 0;
|
||||||
|
if (len) {
|
||||||
|
if (len >= IHS_BUFF_SIZE) len = IHS_BUFF_SIZE - 1;
|
||||||
|
memcpy(s.name_buffer, name, len);
|
||||||
|
}
|
||||||
|
s.name_buffer[len] = 0;
|
||||||
|
IO_STATUS_BLOCK ios;
|
||||||
|
NtWriteFile(hCmd, 0,0,0, &ios, &s, IHS_SIZE, 0, 0);
|
||||||
|
|
||||||
|
//memcpy(&sp.hp,hp,sizeof(HookParam));
|
||||||
|
//cmdq->AddRequest(sp, pid);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_ModifyHook(DWORD pid, HookParam *hp)
|
||||||
|
{
|
||||||
|
ITH_SYNC_HOOK;
|
||||||
|
|
||||||
|
HANDLE hCmd = GetCmdHandleByPID(pid);
|
||||||
|
if (hCmd == 0)
|
||||||
|
return -1;
|
||||||
|
HANDLE hModify = IthCreateEvent(ITH_MODIFYHOOK_EVENT);
|
||||||
|
SendParam sp;
|
||||||
|
sp.type = HOST_COMMAND_MODIFY_HOOK;
|
||||||
|
sp.hp = *hp;
|
||||||
|
IO_STATUS_BLOCK ios;
|
||||||
|
if (NT_SUCCESS(NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam), 0, 0)))
|
||||||
|
// jichi 9/28/2013: no wait timeout
|
||||||
|
//const LONGLONG timeout = HOOK_TIMEOUT;
|
||||||
|
NtWaitForSingleObject(hModify, 0, nullptr);
|
||||||
|
NtClose(hModify);
|
||||||
|
man->RemoveSingleHook(pid, sp.hp.address);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_RemoveHook(DWORD pid, DWORD addr)
|
||||||
|
{
|
||||||
|
ITH_SYNC_HOOK;
|
||||||
|
|
||||||
|
HANDLE hRemoved,hCmd;
|
||||||
|
hCmd = GetCmdHandleByPID(pid);
|
||||||
|
if (hCmd == 0)
|
||||||
|
return -1;
|
||||||
|
hRemoved = IthCreateEvent(ITH_REMOVEHOOK_EVENT);
|
||||||
|
SendParam sp = {};
|
||||||
|
IO_STATUS_BLOCK ios;
|
||||||
|
sp.type = HOST_COMMAND_REMOVE_HOOK;
|
||||||
|
sp.hp.address = addr;
|
||||||
|
//cmdq -> AddRequest(sp, pid);
|
||||||
|
NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam),0,0);
|
||||||
|
// jichi 10/22/2013: Timeout might crash vnrsrv
|
||||||
|
//const LONGLONG timeout = HOOK_TIMEOUT;
|
||||||
|
//NtWaitForSingleObject(hRemoved, 0, (PLARGE_INTEGER)&timeout);
|
||||||
|
NtWaitForSingleObject(hRemoved, 0, nullptr);
|
||||||
|
NtClose(hRemoved);
|
||||||
|
man -> RemoveSingleHook(pid, sp.hp.address);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4/30/2015: Removed as not needed. Going to change to json
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_AddLink(DWORD from, DWORD to)
|
||||||
|
{
|
||||||
|
man->AddLink(from & 0xffff, to & 0xffff);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_UnLink(DWORD from)
|
||||||
|
{
|
||||||
|
man->UnLink(from & 0xffff);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_UnLinkAll(DWORD from)
|
||||||
|
{
|
||||||
|
man->UnLinkAll(from & 0xffff);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
33
vnr/texthook/host/host.h
Normal file
33
vnr/texthook/host/host.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// host.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// Branch: ITH/IHF.h, rev 105
|
||||||
|
|
||||||
|
//#include "host/settings.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "host/hookman.h"
|
||||||
|
|
||||||
|
struct Settings;
|
||||||
|
struct HookParam;
|
||||||
|
|
||||||
|
IHFSERVICE void IHFAPI Host_Init();
|
||||||
|
IHFSERVICE void IHFAPI Host_Destroy();
|
||||||
|
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_Start();
|
||||||
|
IHFSERVICE BOOL IHFAPI Host_Open();
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_Close();
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_GetHookManager(HookManager **hookman);
|
||||||
|
IHFSERVICE bool IHFAPI Host_GetSettings(Settings **settings);
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_GetPIDByName(LPCWSTR pwcTarget);
|
||||||
|
IHFSERVICE bool IHFAPI Host_InjectByPID(DWORD pid);
|
||||||
|
IHFSERVICE bool IHFAPI Host_ActiveDetachProcess(DWORD pid);
|
||||||
|
IHFSERVICE bool IHFAPI Host_HijackProcess(DWORD pid);
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_InsertHook(DWORD pid, HookParam *hp, LPCSTR name = nullptr);
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_ModifyHook(DWORD pid, HookParam *hp);
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_RemoveHook(DWORD pid, DWORD addr);
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_AddLink(DWORD from, DWORD to);
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_UnLink(DWORD from);
|
||||||
|
IHFSERVICE DWORD IHFAPI Host_UnLinkAll(DWORD from);
|
||||||
|
|
||||||
|
// EOF
|
23
vnr/texthook/host/host.pri
Normal file
23
vnr/texthook/host/host.pri
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# host.pri
|
||||||
|
# 8/9/2011 jichi
|
||||||
|
|
||||||
|
DEFINES += WITH_LIB_VNRHOST
|
||||||
|
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/avl_p.h \
|
||||||
|
$$PWD/hookman.h \
|
||||||
|
$$PWD/settings.h \
|
||||||
|
$$PWD/host.h \
|
||||||
|
$$PWD/host_p.h \
|
||||||
|
$$PWD/textthread.h \
|
||||||
|
$$PWD/textthread_p.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/hookman.cc \
|
||||||
|
$$PWD/host.cc \
|
||||||
|
$$PWD/pipe.cc \
|
||||||
|
$$PWD/textthread.cc
|
||||||
|
|
||||||
|
# EOF
|
44
vnr/texthook/host/host_p.h
Normal file
44
vnr/texthook/host/host_p.h
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
#pragma once
|
||||||
|
// host_p.h
|
||||||
|
// 8/24/2013 jichi
|
||||||
|
// Branch IHF/main.h, rev 111
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
#define GLOBAL extern
|
||||||
|
#define SHIFT_JIS 0x3A4
|
||||||
|
class HookManager;
|
||||||
|
//class CommandQueue;
|
||||||
|
class SettingManager;
|
||||||
|
class TextHook;
|
||||||
|
//class BitMap;
|
||||||
|
//class CustomFilterMultiByte;
|
||||||
|
//class CustomFilterUnicode;
|
||||||
|
//#define TextHook Hook
|
||||||
|
GLOBAL BOOL running;
|
||||||
|
//GLOBAL BitMap *pid_map;
|
||||||
|
//GLOBAL CustomFilterMultiByte *mb_filter;
|
||||||
|
//GLOBAL CustomFilterUnicode *uni_filter;
|
||||||
|
GLOBAL HookManager *man;
|
||||||
|
//GLOBAL CommandQueue *cmdq;
|
||||||
|
GLOBAL SettingManager *setman;
|
||||||
|
GLOBAL WCHAR recv_pipe[];
|
||||||
|
GLOBAL WCHAR command[];
|
||||||
|
GLOBAL HANDLE hPipeExist;
|
||||||
|
GLOBAL DWORD split_time,
|
||||||
|
cyclic_remove,
|
||||||
|
clipboard_flag,
|
||||||
|
global_filter;
|
||||||
|
GLOBAL CRITICAL_SECTION detach_cs;
|
||||||
|
|
||||||
|
DWORD WINAPI RecvThread(LPVOID lpThreadParameter);
|
||||||
|
DWORD WINAPI CmdThread(LPVOID lpThreadParameter);
|
||||||
|
|
||||||
|
DWORD GetCurrentPID();
|
||||||
|
//DWORD GetProcessIDByPath(LPWSTR str);
|
||||||
|
HANDLE GetCmdHandleByPID(DWORD pid);
|
||||||
|
//DWORD Inject(HANDLE hProc);
|
||||||
|
//DWORD InjectByPID(DWORD pid);
|
||||||
|
//DWORD PIDByName(LPWSTR target);
|
||||||
|
//DWORD Hash(LPCWSTR module, int length=-1);
|
||||||
|
|
||||||
|
// EOF
|
327
vnr/texthook/host/pipe.cc
Normal file
327
vnr/texthook/host/pipe.cc
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
// pipe.cc
|
||||||
|
// 8/24/2013 jichi
|
||||||
|
// Branch IHF/pipe.cpp, rev 93
|
||||||
|
// 8/24/2013 TODO: Clean up this file
|
||||||
|
|
||||||
|
#include "host_p.h"
|
||||||
|
#include "hookman.h"
|
||||||
|
#include "vnrhook/include/defs.h"
|
||||||
|
#include "vnrhook/include/const.h"
|
||||||
|
#include "ithsys/ithsys.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
//#include "CommandQueue.h"
|
||||||
|
//#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
#define DEBUG "vnrhost/pipe.cc"
|
||||||
|
#include "sakurakit/skdebug.h"
|
||||||
|
|
||||||
|
//DWORD WINAPI UpdateWindows(LPVOID lpThreadParameter);
|
||||||
|
|
||||||
|
namespace { // unnamed
|
||||||
|
enum NamedPipeCommand {
|
||||||
|
NAMED_PIPE_DISCONNECT = 1
|
||||||
|
, NAMED_PIPE_CONNECT = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
bool newline = false;
|
||||||
|
bool detach = false;
|
||||||
|
|
||||||
|
// jichi 10/27/2013
|
||||||
|
// Check if text has leading space
|
||||||
|
enum { _filter_limit = 0x20 }; // The same as the orignal ITH filter. So, I don't have to check \u3000
|
||||||
|
//enum { _filter_limit = 0x19 };
|
||||||
|
inline bool has_leading_space(const BYTE *text, int len)
|
||||||
|
{
|
||||||
|
return len == 1 ? *text <= _filter_limit : // 1 byte
|
||||||
|
*reinterpret_cast<const WORD *>(text) <= _filter_limit; // 2 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 9/28/2013: Skip leading garbage
|
||||||
|
// Note:
|
||||||
|
// - Modifying limit will break manual translation. The orignal one is 0x20
|
||||||
|
// - Eliminating 0x20 will break English-translated games
|
||||||
|
const BYTE *Filter(const BYTE *str, int len)
|
||||||
|
{
|
||||||
|
#ifdef ITH_DISABLE_FILTER // jichi 9/28/2013: only for debugging purpose
|
||||||
|
return str;
|
||||||
|
#endif // ITH_DISABLE_FILTER
|
||||||
|
// if (len && *str == 0x10) // jichi 9/28/2013: garbage on wine, data link escape, or ^P
|
||||||
|
// return nullptr;
|
||||||
|
//enum { limit = 0x19 };
|
||||||
|
while (true)
|
||||||
|
if (len >= 2) {
|
||||||
|
if (*(const WORD *)str <= _filter_limit) { // jichi 10/27/2013: two bytes
|
||||||
|
str += 2;
|
||||||
|
len -= 2;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
} else if (*str <= _filter_limit) { // jichi 10/27/2013: 1 byte
|
||||||
|
str++;
|
||||||
|
len--;
|
||||||
|
} else
|
||||||
|
break;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
} // unnamed namespace
|
||||||
|
|
||||||
|
//WCHAR recv_pipe[] = L"\\??\\pipe\\ITH_PIPE";
|
||||||
|
//WCHAR command_pipe[] = L"\\??\\pipe\\ITH_COMMAND";
|
||||||
|
wchar_t recv_pipe[] = ITH_TEXT_PIPE;
|
||||||
|
wchar_t command_pipe[] = ITH_COMMAND_PIPE;
|
||||||
|
|
||||||
|
CRITICAL_SECTION detach_cs; // jichi 9/27/2013: also used in main
|
||||||
|
//HANDLE hDetachEvent;
|
||||||
|
extern HANDLE hPipeExist;
|
||||||
|
|
||||||
|
void CreateNewPipe()
|
||||||
|
{
|
||||||
|
static DWORD acl[7] = {
|
||||||
|
0x1C0002,
|
||||||
|
1,
|
||||||
|
0x140000,
|
||||||
|
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
|
||||||
|
0x101,
|
||||||
|
0x1000000,
|
||||||
|
0};
|
||||||
|
static SECURITY_DESCRIPTOR sd = {1, 0, 4, 0, 0, 0, (PACL)acl};
|
||||||
|
|
||||||
|
HANDLE hTextPipe, hCmdPipe, hThread;
|
||||||
|
IO_STATUS_BLOCK ios;
|
||||||
|
UNICODE_STRING us;
|
||||||
|
|
||||||
|
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
|
||||||
|
LARGE_INTEGER time = {-500000, -1};
|
||||||
|
|
||||||
|
RtlInitUnicodeString(&us, recv_pipe);
|
||||||
|
if (!NT_SUCCESS(NtCreateNamedPipeFile(
|
||||||
|
&hTextPipe,
|
||||||
|
GENERIC_READ | SYNCHRONIZE,
|
||||||
|
&oa,
|
||||||
|
&ios,
|
||||||
|
FILE_SHARE_WRITE,
|
||||||
|
FILE_OPEN_IF,
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
||||||
|
1, 1, 0, -1,
|
||||||
|
0x1000,
|
||||||
|
0x1000,
|
||||||
|
&time))) {
|
||||||
|
//ConsoleOutput(ErrorCreatePipe);
|
||||||
|
DOUT("failed to create recv pipe");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RtlInitUnicodeString(&us, command_pipe);
|
||||||
|
if (!NT_SUCCESS(NtCreateNamedPipeFile(
|
||||||
|
&hCmdPipe,
|
||||||
|
GENERIC_WRITE | SYNCHRONIZE,
|
||||||
|
&oa,
|
||||||
|
&ios,
|
||||||
|
FILE_SHARE_READ,
|
||||||
|
FILE_OPEN_IF,
|
||||||
|
FILE_SYNCHRONOUS_IO_NONALERT,
|
||||||
|
1, 1, 0, -1,
|
||||||
|
0x1000,
|
||||||
|
0x1000,
|
||||||
|
&time))) {
|
||||||
|
//ConsoleOutput(ErrorCreatePipe);
|
||||||
|
DOUT("failed to create cmd pipe");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
hThread = IthCreateThread(RecvThread, (DWORD)hTextPipe);
|
||||||
|
man->RegisterPipe(hTextPipe, hCmdPipe, hThread);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DetachFromProcess(DWORD pid)
|
||||||
|
{
|
||||||
|
HANDLE hMutex = INVALID_HANDLE_VALUE,
|
||||||
|
hEvent = INVALID_HANDLE_VALUE;
|
||||||
|
//try {
|
||||||
|
IO_STATUS_BLOCK ios;
|
||||||
|
ProcessRecord *pr = man->GetProcessRecord(pid);
|
||||||
|
if (!pr)
|
||||||
|
return;
|
||||||
|
//IthBreak();
|
||||||
|
hEvent = IthCreateEvent(nullptr);
|
||||||
|
if (STATUS_PENDING == NtFsControlFile(
|
||||||
|
man->GetCmdHandleByPID(pid),
|
||||||
|
hEvent,
|
||||||
|
0,0,
|
||||||
|
&ios,
|
||||||
|
CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0),
|
||||||
|
0,0,0,0))
|
||||||
|
NtWaitForSingleObject(hEvent, 0, 0);
|
||||||
|
NtClose(hEvent);
|
||||||
|
//hEvent = INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
WCHAR mutex[0x20];
|
||||||
|
swprintf(mutex, ITH_DETACH_MUTEX_ L"%d", pid);
|
||||||
|
hMutex = IthOpenMutex(mutex);
|
||||||
|
if (hMutex != INVALID_HANDLE_VALUE) {
|
||||||
|
NtWaitForSingleObject(hMutex, 0, 0);
|
||||||
|
NtReleaseMutant(hMutex, 0);
|
||||||
|
NtClose(hMutex);
|
||||||
|
//hMutex = INVALID_HANDLE_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//} catch (...) {
|
||||||
|
// if (hEvent != INVALID_HANDLE_VALUE)
|
||||||
|
// NtClose(hEvent);
|
||||||
|
// else if (hMutex != INVALID_HANDLE_VALUE) {
|
||||||
|
// NtWaitForSingleObject(hMutex, 0, 0);
|
||||||
|
// NtReleaseMutant(hMutex, 0);
|
||||||
|
// NtClose(hMutex);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
//NtSetEvent(hDetachEvent, 0);
|
||||||
|
if (::running)
|
||||||
|
NtSetEvent(hPipeExist, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 9/27/2013: I don't need this
|
||||||
|
//void OutputDWORD(DWORD d)
|
||||||
|
//{
|
||||||
|
// WCHAR str[0x20];
|
||||||
|
// swprintf(str, L"%.8X", d);
|
||||||
|
// ConsoleOutput(str);
|
||||||
|
//}
|
||||||
|
|
||||||
|
DWORD WINAPI RecvThread(LPVOID lpThreadParameter)
|
||||||
|
{
|
||||||
|
HANDLE hTextPipe = (HANDLE)lpThreadParameter;
|
||||||
|
|
||||||
|
IO_STATUS_BLOCK ios;
|
||||||
|
NtFsControlFile(hTextPipe,
|
||||||
|
0, 0, 0,
|
||||||
|
&ios,
|
||||||
|
CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_CONNECT, 0, 0),
|
||||||
|
0, 0, 0, 0);
|
||||||
|
if (!::running) {
|
||||||
|
NtClose(hTextPipe);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
BYTE *buff;
|
||||||
|
|
||||||
|
enum { PipeBufferSize = 0x1000 };
|
||||||
|
buff = new BYTE[PipeBufferSize];
|
||||||
|
::memset(buff, 0, PipeBufferSize); // jichi 8/27/2013: zero memory, or it will crash wine on start up
|
||||||
|
|
||||||
|
// 10/19/2014 jichi: there are totally three words received
|
||||||
|
// See: hook/rpc/pipe.cc
|
||||||
|
// struct {
|
||||||
|
// DWORD pid;
|
||||||
|
// TextHook *man;
|
||||||
|
// DWORD module;
|
||||||
|
// //DWORD engine;
|
||||||
|
// } u;
|
||||||
|
enum { module_struct_size = 12 };
|
||||||
|
NtReadFile(hTextPipe, 0, 0, 0, &ios, buff, module_struct_size, 0, 0);
|
||||||
|
|
||||||
|
// jichi 7/2/2015: This must be consistent with the struct declared in vnrhook/pipe.cc
|
||||||
|
DWORD pid = *(DWORD *)buff,
|
||||||
|
module = *(DWORD *)(buff + 0x8),
|
||||||
|
hookman = *(DWORD *)(buff + 0x4);
|
||||||
|
//engine = *(DWORD *)(buff + 0xc);
|
||||||
|
man->RegisterProcess(pid, hookman, module);
|
||||||
|
|
||||||
|
// jichi 9/27/2013: why recursion?
|
||||||
|
CreateNewPipe();
|
||||||
|
|
||||||
|
//NtClose(IthCreateThread(UpdateWindows,0));
|
||||||
|
while (::running) {
|
||||||
|
if (!NT_SUCCESS(NtReadFile(hTextPipe,
|
||||||
|
0, 0, 0,
|
||||||
|
&ios,
|
||||||
|
buff,
|
||||||
|
0xf80,
|
||||||
|
0, 0)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
enum { data_offset = 0xc }; // jichi 10/27/2013: Seem to be the data offset in the pipe
|
||||||
|
|
||||||
|
DWORD RecvLen = ios.uInformation;
|
||||||
|
if (RecvLen < data_offset)
|
||||||
|
break;
|
||||||
|
DWORD hook = *(DWORD *)buff;
|
||||||
|
|
||||||
|
union { DWORD retn; DWORD cmd_type; };
|
||||||
|
union { DWORD split; DWORD new_engine_type; };
|
||||||
|
|
||||||
|
retn = *(DWORD *)(buff + 4);
|
||||||
|
split = *(DWORD *)(buff + 8);
|
||||||
|
|
||||||
|
buff[RecvLen] = 0;
|
||||||
|
buff[RecvLen + 1] = 0;
|
||||||
|
|
||||||
|
if (hook == HOST_NOTIFICATION) {
|
||||||
|
switch (cmd_type) {
|
||||||
|
case HOST_NOTIFICATION_NEWHOOK:
|
||||||
|
{
|
||||||
|
static long lock;
|
||||||
|
while (InterlockedExchange(&lock, 1) == 1);
|
||||||
|
ProcessEventCallback new_hook = man->ProcessNewHook();
|
||||||
|
if (new_hook)
|
||||||
|
new_hook(pid);
|
||||||
|
lock = 0;
|
||||||
|
} break;
|
||||||
|
case HOST_NOTIFICATION_TEXT:
|
||||||
|
//qDebug() << ((LPCSTR)(buff + 8));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// jichi 9/28/2013: Debug raw data
|
||||||
|
//ITH_DEBUG_DWORD9(RecvLen - 0xc,
|
||||||
|
// buff[0xc], buff[0xd], buff[0xe], buff[0xf],
|
||||||
|
// buff[0x10], buff[0x11], buff[0x12], buff[0x13]);
|
||||||
|
|
||||||
|
const BYTE *data = buff + data_offset; // th
|
||||||
|
int len = RecvLen - data_offset;
|
||||||
|
bool space = ::has_leading_space(data, len);
|
||||||
|
if (space) {
|
||||||
|
const BYTE *it = ::Filter(data, len);
|
||||||
|
len -= it - data;
|
||||||
|
data = it;
|
||||||
|
}
|
||||||
|
if (len >> 31) // jichi 10/27/2013: len is too large, which seldom happens
|
||||||
|
len = 0;
|
||||||
|
//man->DispatchText(pid, len ? data : nullptr, hook, retn, split, len, space);
|
||||||
|
man->DispatchText(pid, data, hook, retn, split, len, space);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
EnterCriticalSection(&detach_cs);
|
||||||
|
|
||||||
|
HANDLE hDisconnect = IthCreateEvent(nullptr);
|
||||||
|
|
||||||
|
if (STATUS_PENDING == NtFsControlFile(
|
||||||
|
hTextPipe,
|
||||||
|
hDisconnect,
|
||||||
|
0, 0,
|
||||||
|
&ios,
|
||||||
|
CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0),
|
||||||
|
0, 0, 0, 0))
|
||||||
|
NtWaitForSingleObject(hDisconnect, 0, 0);
|
||||||
|
|
||||||
|
NtClose(hDisconnect);
|
||||||
|
DetachFromProcess(pid);
|
||||||
|
man->UnRegisterProcess(pid);
|
||||||
|
|
||||||
|
//NtClearEvent(hDetachEvent);
|
||||||
|
|
||||||
|
LeaveCriticalSection(&detach_cs);
|
||||||
|
delete[] buff;
|
||||||
|
|
||||||
|
if (::running)
|
||||||
|
DOUT("detached");
|
||||||
|
|
||||||
|
//if (::running) {
|
||||||
|
// swprintf((LPWSTR)buff, FormatDetach, pid);
|
||||||
|
// ConsoleOutput((LPWSTR)buff);
|
||||||
|
// NtClose(IthCreateThread(UpdateWindows, 0));
|
||||||
|
//}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
17
vnr/texthook/host/settings.h
Normal file
17
vnr/texthook/host/settings.h
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// settings.h
|
||||||
|
// 8/24/2013 jichi
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
//bool debug; // whether output debug messages using pipes
|
||||||
|
int splittingInterval;// time to split text into sentences
|
||||||
|
bool clipboardFlag;
|
||||||
|
|
||||||
|
Settings() : splittingInterval(200),
|
||||||
|
clipboardFlag(false)
|
||||||
|
{}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
786
vnr/texthook/host/textthread.cc
Normal file
786
vnr/texthook/host/textthread.cc
Normal file
@ -0,0 +1,786 @@
|
|||||||
|
// textthread.cc
|
||||||
|
// 8/24/2013 jichi
|
||||||
|
// Branch IHF/TextThread.cpp, rev 133
|
||||||
|
// 8/24/2013 TODO: Clean up this file
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning (disable:4100) // C4100: unreference formal parameter
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#include "settings.h"
|
||||||
|
#include "textthread.h"
|
||||||
|
//#include "wintimer/wintimer.h"
|
||||||
|
#include "vnrhook/include/const.h"
|
||||||
|
#include "ithsys/ithsys.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
MK_BASIC_TYPE(BYTE)
|
||||||
|
MK_BASIC_TYPE(ThreadParameter)
|
||||||
|
|
||||||
|
static DWORD MIN_DETECT = 0x20;
|
||||||
|
static DWORD MIN_REDETECT = 0x80;
|
||||||
|
//#define MIN_DETECT 0x20
|
||||||
|
//#define MIN_REDETECT 0x80
|
||||||
|
#ifndef CURRENT_SELECT
|
||||||
|
# define CURRENT_SELECT 0x1000
|
||||||
|
#endif
|
||||||
|
#ifndef REPEAT_NUMBER_DECIDED
|
||||||
|
# define REPEAT_NUMBER_DECIDED 0x2000
|
||||||
|
#endif
|
||||||
|
|
||||||
|
DWORD GetHookName(LPSTR str, DWORD pid, DWORD hook_addr,DWORD max);
|
||||||
|
|
||||||
|
extern Settings *settings;
|
||||||
|
extern HWND hMainWnd;
|
||||||
|
void CALLBACK NewLineBuff(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
|
||||||
|
{
|
||||||
|
KillTimer(hwnd,idEvent);
|
||||||
|
TextThread *id=(TextThread*)idEvent;
|
||||||
|
|
||||||
|
if (id->Status()&CURRENT_SELECT)
|
||||||
|
//texts->SetLine();
|
||||||
|
id->CopyLastToClipboard();
|
||||||
|
id->SetNewLineFlag();
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 10/27/2013: removed
|
||||||
|
//void CALLBACK NewLineConsole(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
|
||||||
|
//{
|
||||||
|
// KillTimer(hwnd,idEvent);
|
||||||
|
// TextThread *id=(TextThread*)idEvent;
|
||||||
|
// if (id->Status()&USING_UNICODE)
|
||||||
|
// id->AddText((BYTE*)L"\r\n",4,true,true);
|
||||||
|
// if (id->Status()&CURRENT_SELECT)
|
||||||
|
// {
|
||||||
|
// //texts->SetLine();
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
// jichi 10/27/2013: removed
|
||||||
|
//void ReplaceSentence(BYTE* text, int len)
|
||||||
|
//{
|
||||||
|
// __asm int 3
|
||||||
|
//}
|
||||||
|
|
||||||
|
TextThread::TextThread(DWORD id, DWORD hook, DWORD retn, DWORD spl, WORD num) :
|
||||||
|
//,tp
|
||||||
|
thread_number(num)
|
||||||
|
// jichi 9/21/2013: zero all fields
|
||||||
|
, link_number(-1)
|
||||||
|
, last (0)
|
||||||
|
, align_space(0)
|
||||||
|
, repeat_single(0)
|
||||||
|
, repeat_single_current(0)
|
||||||
|
, repeat_single_count(0)
|
||||||
|
, repeat_detect_count(0)
|
||||||
|
, head(new RepeatCountNode())
|
||||||
|
, link(nullptr)
|
||||||
|
//, filter(nullptr)
|
||||||
|
, output(nullptr)
|
||||||
|
, app_data(nullptr)
|
||||||
|
//, comment(nullptr)
|
||||||
|
, thread_string(nullptr)
|
||||||
|
, timer(0)
|
||||||
|
, status (0)
|
||||||
|
, repeat_detect_limit(0x80)
|
||||||
|
, last_sentence(0)
|
||||||
|
, prev_sentence(0)
|
||||||
|
, sentence_length(0)
|
||||||
|
, repeat_index(0)
|
||||||
|
, last_time(0)
|
||||||
|
// , tp({id, hook, retn, spl})
|
||||||
|
{
|
||||||
|
tp.pid = id;
|
||||||
|
tp.hook = hook;
|
||||||
|
tp.retn = retn;
|
||||||
|
tp.spl = spl;
|
||||||
|
//head = new RepeatCountNode;
|
||||||
|
//::memset(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory
|
||||||
|
//link_number = -1;
|
||||||
|
//repeat_detect_limit = 0x80;
|
||||||
|
//filter = nullptr;
|
||||||
|
//output = nullptr;
|
||||||
|
}
|
||||||
|
TextThread::~TextThread()
|
||||||
|
{
|
||||||
|
//KillTimer(hMainWnd,timer);
|
||||||
|
RepeatCountNode *t = head,
|
||||||
|
*tt;
|
||||||
|
while (t) {
|
||||||
|
tt = t;
|
||||||
|
t = tt->next;
|
||||||
|
delete tt;
|
||||||
|
}
|
||||||
|
head = nullptr;
|
||||||
|
//if (comment) {
|
||||||
|
// delete[] comment;
|
||||||
|
// comment = nullptr;
|
||||||
|
//}
|
||||||
|
if (thread_string)
|
||||||
|
delete[] thread_string;
|
||||||
|
}
|
||||||
|
void TextThread::Reset()
|
||||||
|
{
|
||||||
|
//timer=0;
|
||||||
|
last_sentence = 0;
|
||||||
|
//if (comment) {
|
||||||
|
// delete[] comment;
|
||||||
|
// comment = nullptr;
|
||||||
|
//}
|
||||||
|
MyVector::Reset();
|
||||||
|
}
|
||||||
|
void TextThread::RemoveSingleRepeatAuto(const BYTE *con, int &len)
|
||||||
|
{
|
||||||
|
#ifdef ITH_DISABLE_REPEAT // jichi 9/28/2013: only for debugging purpose
|
||||||
|
return;
|
||||||
|
#endif // ITH_DISABLE_REPEAT
|
||||||
|
WORD *text = (WORD *)con;
|
||||||
|
if (len <= 2) {
|
||||||
|
if (repeat_single) {
|
||||||
|
if (repeat_single_count<repeat_single&&
|
||||||
|
last == *text) {
|
||||||
|
len = 0;
|
||||||
|
repeat_single_count++;
|
||||||
|
} else {
|
||||||
|
last = *text;
|
||||||
|
repeat_single_count=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (status & REPEAT_NUMBER_DECIDED) {
|
||||||
|
if (++repeat_detect_count>MIN_REDETECT) {
|
||||||
|
repeat_detect_count = 0;
|
||||||
|
status ^= REPEAT_NUMBER_DECIDED;
|
||||||
|
last = 0;
|
||||||
|
RepeatCountNode *t = head,
|
||||||
|
*tt;
|
||||||
|
while (t) {
|
||||||
|
tt = t;
|
||||||
|
t = tt->next;
|
||||||
|
delete tt;
|
||||||
|
}
|
||||||
|
head = new RepeatCountNode;
|
||||||
|
::memset(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
repeat_detect_count++;
|
||||||
|
if (last == *text)
|
||||||
|
repeat_single_current++;
|
||||||
|
else {
|
||||||
|
if (last == 0) {
|
||||||
|
last = *text;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (repeat_single_current == 0) {
|
||||||
|
status |= REPEAT_NUMBER_DECIDED;
|
||||||
|
repeat_single = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
last = *text;
|
||||||
|
RepeatCountNode *it = head;
|
||||||
|
if (repeat_detect_count > MIN_DETECT) {
|
||||||
|
while (it = it->next)
|
||||||
|
if (it->count>head->count) {
|
||||||
|
head->count=it->count;
|
||||||
|
head->repeat=it->repeat;
|
||||||
|
}
|
||||||
|
repeat_single = head->repeat;
|
||||||
|
repeat_single_current = 0;
|
||||||
|
repeat_detect_count = 0;
|
||||||
|
status |= REPEAT_NUMBER_DECIDED;
|
||||||
|
DWORD repeat_sc = repeat_single*4;
|
||||||
|
if (repeat_sc > MIN_DETECT) {
|
||||||
|
MIN_DETECT <<= 1;
|
||||||
|
MIN_REDETECT <<= 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool flag=true;
|
||||||
|
while (it) {
|
||||||
|
if (it->repeat == repeat_single_current) {
|
||||||
|
it->count++;
|
||||||
|
flag = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
it=it->next;
|
||||||
|
}
|
||||||
|
if (flag) {
|
||||||
|
RepeatCountNode *n = new RepeatCountNode;
|
||||||
|
n->count = 1;
|
||||||
|
n->repeat = repeat_single_current;
|
||||||
|
n->next = head->next;
|
||||||
|
head->next = n;
|
||||||
|
}
|
||||||
|
repeat_single_current = 0;
|
||||||
|
} //Decide repeat_single
|
||||||
|
} //Check Repeat
|
||||||
|
} //repeat_single decided?
|
||||||
|
} //len
|
||||||
|
else {
|
||||||
|
status |= REPEAT_NUMBER_DECIDED;
|
||||||
|
repeat_single = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextThread::RemoveSingleRepeatForce(BYTE *con,int &len)
|
||||||
|
{
|
||||||
|
// jichi 9/1/2013: manual repetition count removed
|
||||||
|
WORD *text = (WORD *)con;
|
||||||
|
//if (repeat_single_count<setman->GetValue(SETTING_REPEAT_COUNT)&&last==*text) {
|
||||||
|
// len=0;
|
||||||
|
// repeat_single_count++;
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
{
|
||||||
|
last = *text;
|
||||||
|
repeat_single_count=0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TextThread::RemoveCyclicRepeat(BYTE* &con, int &len)
|
||||||
|
{
|
||||||
|
DWORD current_time = GetTickCount();
|
||||||
|
if (status & REPEAT_SUPPRESS) {
|
||||||
|
if (current_time - last_time < (unsigned)settings->splittingInterval &&
|
||||||
|
::memcmp(storage + last_sentence + repeat_index, con, len) == 0) {
|
||||||
|
repeat_index += len;
|
||||||
|
if (repeat_index>=sentence_length)
|
||||||
|
repeat_index -= sentence_length;
|
||||||
|
len = 0;
|
||||||
|
} else {
|
||||||
|
repeat_index = 0;
|
||||||
|
status &= ~REPEAT_SUPPRESS;
|
||||||
|
}
|
||||||
|
} else if (status & REPEAT_DETECT) {
|
||||||
|
if (::memcmp(storage + last_sentence + repeat_index, con, len) == 0) {
|
||||||
|
int half_length=repeat_index+len;
|
||||||
|
if (::memcmp(storage + last_sentence, storage + last_sentence + half_length, repeat_index) == 0) {
|
||||||
|
len=0;
|
||||||
|
sentence_length=half_length;
|
||||||
|
status&=~REPEAT_DETECT;
|
||||||
|
status|=REPEAT_SUPPRESS;
|
||||||
|
|
||||||
|
// jichi 10/27/2013: Not used
|
||||||
|
//if (status&CURRENT_SELECT)
|
||||||
|
// ReplaceSentence(storage+last_sentence+half_length,repeat_index);
|
||||||
|
ClearMemory(last_sentence+half_length,repeat_index);
|
||||||
|
used-=repeat_index;
|
||||||
|
repeat_index=0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
repeat_index += len;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
repeat_index=0;
|
||||||
|
status &= ~REPEAT_DETECT;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (sentence_length == 0)
|
||||||
|
return;
|
||||||
|
else if (len <= (int)sentence_length) {
|
||||||
|
if (memcmp(storage + last_sentence, con, len) == 0) {
|
||||||
|
status |= REPEAT_DETECT;
|
||||||
|
repeat_index = len;
|
||||||
|
if (repeat_index == sentence_length) {
|
||||||
|
repeat_index = 0;
|
||||||
|
len = 0;
|
||||||
|
}
|
||||||
|
} else if (sentence_length > repeat_detect_limit) {
|
||||||
|
if (len > 2) {
|
||||||
|
DWORD u = used;
|
||||||
|
while (memcmp(storage + u - len, con, len) == 0)
|
||||||
|
u -= len;
|
||||||
|
ClearMemory(u, used - u);
|
||||||
|
used = u;
|
||||||
|
repeat_index = 0;
|
||||||
|
// jichi 10/27/2013: Not used
|
||||||
|
//if (status & CURRENT_SELECT)
|
||||||
|
// ReplaceSentence(storage + last_sentence, used - u);
|
||||||
|
status |= REPEAT_SUPPRESS;
|
||||||
|
len = 0;
|
||||||
|
} else if (len <= 2)
|
||||||
|
{
|
||||||
|
WORD tmp = *(WORD *)(storage + last_sentence);
|
||||||
|
DWORD index, last_index, tmp_len;
|
||||||
|
index = used-len;
|
||||||
|
if (index < last_sentence)
|
||||||
|
index = last_sentence;
|
||||||
|
//Locate position of current input.
|
||||||
|
_again:
|
||||||
|
*(WORD *)(storage+last_sentence) = *(WORD *)con;
|
||||||
|
while (*(WORD *)(storage + index) != *(WORD *)con)
|
||||||
|
index--;
|
||||||
|
*(WORD *)(storage + last_sentence) = tmp;
|
||||||
|
if (index > last_sentence) {
|
||||||
|
tmp_len = used - index;
|
||||||
|
if (tmp_len <= 2) {
|
||||||
|
repeat_detect_limit += 0x40;
|
||||||
|
last_time = current_time;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (index - last_sentence >= tmp_len &&
|
||||||
|
memcmp(storage + index - tmp_len, storage + index, tmp_len) == 0) {
|
||||||
|
repeat_detect_limit = 0x80;
|
||||||
|
sentence_length =tmp_len;
|
||||||
|
index -= tmp_len;
|
||||||
|
while (memcmp(storage + index - sentence_length, storage + index, sentence_length) == 0)
|
||||||
|
index -= sentence_length;
|
||||||
|
repeat_index = 2;
|
||||||
|
len = 0;
|
||||||
|
last_index = index;
|
||||||
|
if (status&USING_UNICODE) {
|
||||||
|
while (storage[index] == storage[index + sentence_length])
|
||||||
|
index -= 2;
|
||||||
|
index += 2;
|
||||||
|
while (true) {
|
||||||
|
tmp = *(WORD *)(storage + index);
|
||||||
|
if (tmp >= 0x3000 && tmp < 0x3020)
|
||||||
|
index += 2;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
DWORD last_char_len;
|
||||||
|
while (storage[index] == storage[index + sentence_length]) {
|
||||||
|
last_char_len = LeadByteTable[storage[index]];
|
||||||
|
index -= last_char_len;
|
||||||
|
}
|
||||||
|
index += last_char_len;
|
||||||
|
while (storage[index] == 0x81) {
|
||||||
|
if ((storage[index+1]>>4) == 4)
|
||||||
|
index += 2;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
repeat_index += last_index - index;
|
||||||
|
status |= REPEAT_SUPPRESS;
|
||||||
|
last_sentence = index;
|
||||||
|
|
||||||
|
index += sentence_length;
|
||||||
|
// jichi 10/27/2013: Not used
|
||||||
|
//if (status&CURRENT_SELECT)
|
||||||
|
// ReplaceSentence(storage + index, used - index);
|
||||||
|
|
||||||
|
ClearMemory(index, used - index);
|
||||||
|
//memset(storage + index, 0, used - index);
|
||||||
|
used = index;
|
||||||
|
} else {
|
||||||
|
index--;
|
||||||
|
goto _again;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
repeat_detect_limit += 0x40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
last_time = current_time;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextThread::ResetRepeatStatus()
|
||||||
|
{
|
||||||
|
last=0;
|
||||||
|
repeat_single=0;
|
||||||
|
repeat_single_current=0;
|
||||||
|
repeat_single_count=0;
|
||||||
|
repeat_detect_count=0;
|
||||||
|
RepeatCountNode *t = head->next,
|
||||||
|
*tt;
|
||||||
|
while (t) {
|
||||||
|
tt = t;
|
||||||
|
t = tt->next;
|
||||||
|
delete tt;
|
||||||
|
}
|
||||||
|
//head=new RepeatCountNode;
|
||||||
|
head->count = head->repeat = 0;
|
||||||
|
status &= ~REPEAT_NUMBER_DECIDED;
|
||||||
|
}
|
||||||
|
void TextThread::AddLineBreak()
|
||||||
|
{
|
||||||
|
if (sentence_length == 0) return;
|
||||||
|
if (status&BUFF_NEWLINE)
|
||||||
|
{
|
||||||
|
prev_sentence=last_sentence;
|
||||||
|
sentence_length=0;
|
||||||
|
if (status & USING_UNICODE)
|
||||||
|
AddToStore((BYTE *)L"\r\n\r\n", 8);
|
||||||
|
else
|
||||||
|
AddToStore((BYTE *)"\r\n\r\n", 4);
|
||||||
|
if (output)
|
||||||
|
output(this, 0, 8, TRUE, app_data, false); // jichi 10/27/2013: space is false
|
||||||
|
last_sentence = used;
|
||||||
|
status &= ~BUFF_NEWLINE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TextThread::AddText(const BYTE *con, int len, bool new_line, bool space)
|
||||||
|
{
|
||||||
|
if (!con || (len <= 0 && !space))
|
||||||
|
return;
|
||||||
|
if (len && !new_line) {
|
||||||
|
// jichi 9/1/2013: manual repetition count removed
|
||||||
|
//if (setman->GetValue(SETTING_REPEAT_COUNT)) {
|
||||||
|
// status|=REPEAT_NUMBER_DECIDED;
|
||||||
|
// RemoveSingleRepeatForce(con,len);
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
RemoveSingleRepeatAuto(con, len);
|
||||||
|
if (len <= 0 && !space)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 9/1/2013: manual repetition count removed
|
||||||
|
//if(setman->GetValue(SETTING_CYCLIC_REMOVE)) {
|
||||||
|
// //if (status & REPEAT_NUMBER_DECIDED)
|
||||||
|
// RemoveCyclicRepeat(con,len);
|
||||||
|
//}
|
||||||
|
//if (len <= 0)
|
||||||
|
// return;
|
||||||
|
|
||||||
|
// jichi 10/27/2013: User-defined filter callback is disabled
|
||||||
|
//if (filter)
|
||||||
|
// len = filter(this, con,len, new_line, app_data);
|
||||||
|
//if (len <= 0)
|
||||||
|
// return;
|
||||||
|
|
||||||
|
if (len && sentence_length == 0) {
|
||||||
|
if (status & USING_UNICODE) {
|
||||||
|
if (*(WORD *)con == 0x3000) { // jichi 10/27/2013: why skip unicode space?!
|
||||||
|
con += 2;
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
} else if (*(WORD *)con == 0x4081) {
|
||||||
|
con += 2;
|
||||||
|
len -= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len <= 0 && !space)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status & BUFF_NEWLINE)
|
||||||
|
AddLineBreak();
|
||||||
|
|
||||||
|
if (len)
|
||||||
|
if (new_line) {
|
||||||
|
prev_sentence = last_sentence;
|
||||||
|
last_sentence = used + 4;
|
||||||
|
if (status & USING_UNICODE)
|
||||||
|
last_sentence += 4;
|
||||||
|
sentence_length = 0;
|
||||||
|
} else {
|
||||||
|
SetNewLineTimer();
|
||||||
|
if (link) {
|
||||||
|
const BYTE *send = con;
|
||||||
|
int l = len;
|
||||||
|
if (status & USING_UNICODE) { // Although unlikely, a thread and its link may have different encoding.
|
||||||
|
if ((link->Status() & USING_UNICODE) == 0) {
|
||||||
|
send = new BYTE[l];
|
||||||
|
//::memset(send, 0, l); // jichi 9/26/2013: zero memory
|
||||||
|
l = WC_MB((LPWSTR)con, (char *)send);
|
||||||
|
}
|
||||||
|
link->AddTextDirect(send, l, space);
|
||||||
|
} else {
|
||||||
|
if (link->Status() & USING_UNICODE) {
|
||||||
|
size_t sz = len * 2 + 2;
|
||||||
|
send = new BYTE[sz];
|
||||||
|
//::memset(send, 0, sz); // jichi 9/26/2013: zero memory
|
||||||
|
l = MB_WC((char *)con, (LPWSTR)send) << 1;
|
||||||
|
}
|
||||||
|
link->AddTextDirect(send, l, space);
|
||||||
|
}
|
||||||
|
link->SetNewLineTimer();
|
||||||
|
if (send != con)
|
||||||
|
delete[] send;
|
||||||
|
}
|
||||||
|
sentence_length += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
BYTE *data = const_cast<BYTE *>(con); // jichi 10/27/2013: TODO: Figure out where con is modified
|
||||||
|
if (output)
|
||||||
|
len = output(this, data, len, new_line, app_data, space);
|
||||||
|
if (AddToStore(data, len)) {
|
||||||
|
//sentence_length += len;
|
||||||
|
/*ResetRepeatStatus();
|
||||||
|
last_sentence=0;
|
||||||
|
prev_sentence=0;
|
||||||
|
sentence_length=len;
|
||||||
|
repeat_index=0;
|
||||||
|
status&=~REPEAT_DETECT|REPEAT_SUPPRESS; */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextThread::AddTextDirect(const BYTE* con, int len, bool space) // Add to store directly, penetrating repetition filters.
|
||||||
|
{
|
||||||
|
// jichi 10/27/2013: Accordig to the logic, both len and con must be > 0
|
||||||
|
if (status & BUFF_NEWLINE)
|
||||||
|
AddLineBreak();
|
||||||
|
//SetNewLineTimer();
|
||||||
|
if (link) {
|
||||||
|
const BYTE *send = con;
|
||||||
|
int l = len;
|
||||||
|
if (status & USING_UNICODE) {
|
||||||
|
if ((link->Status()&USING_UNICODE) == 0) {
|
||||||
|
send = new BYTE[l];
|
||||||
|
//::memset(send, 0, l); // jichi 9/26/2013: zero memory
|
||||||
|
l = WC_MB((LPWSTR)con,(char*)send);
|
||||||
|
}
|
||||||
|
link->AddText(send, l, false, space); // new_line is false
|
||||||
|
} else {
|
||||||
|
if (link->Status()&USING_UNICODE) {
|
||||||
|
size_t sz = len * 2 + 2;
|
||||||
|
send = new BYTE[sz];
|
||||||
|
//::memset(send, 0, sz); // jichi 9/26/2013: zero memory
|
||||||
|
l = MB_WC((char *)con, (LPWSTR)send) << 1;
|
||||||
|
}
|
||||||
|
link->AddText(send, l, false, space); // new_line is false
|
||||||
|
}
|
||||||
|
link->SetNewLineTimer();
|
||||||
|
if (send != con)
|
||||||
|
delete[] send;
|
||||||
|
}
|
||||||
|
sentence_length += len;
|
||||||
|
|
||||||
|
BYTE *data = const_cast<BYTE *>(con); // jichi 10/27/2013: TODO: Figure out where con is modified
|
||||||
|
if (output)
|
||||||
|
len = output(this, data, len, false, app_data, space);
|
||||||
|
AddToStore(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD TextThread::GetEntryString(LPSTR str, DWORD max)
|
||||||
|
{
|
||||||
|
DWORD len = 0;
|
||||||
|
if (str && max > 0x40) {
|
||||||
|
max--;
|
||||||
|
if (thread_string) {
|
||||||
|
len = ::strlen(thread_string);
|
||||||
|
len = len < max ? len : max;
|
||||||
|
memcpy(str, thread_string, len);
|
||||||
|
str[len] = 0;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
len = ::sprintf(str, "%.4X:%.4d:0x%08X:0x%08X:0x%08X:",
|
||||||
|
thread_number, tp. pid, tp.hook, tp.retn, tp.spl);
|
||||||
|
|
||||||
|
len += GetHookName(str + len, tp.pid, tp.hook, max - len);
|
||||||
|
thread_string = new char[len + 1];
|
||||||
|
//::memset(thread_string, 0, (len+1) * sizeof(wchar_t)); // jichi 9/26/2013: zero memory
|
||||||
|
thread_string[len] = 0;
|
||||||
|
::memcpy(thread_string, str, len);
|
||||||
|
}
|
||||||
|
//if (comment) {
|
||||||
|
// str += len;
|
||||||
|
// max--;
|
||||||
|
// DWORD cl = wcslen(comment);
|
||||||
|
// if (len + cl >= max)
|
||||||
|
// cl = max - len;
|
||||||
|
// *str++ = L'-';
|
||||||
|
// memcpy(str, comment, cl << 1);
|
||||||
|
// str[cl] = 0;
|
||||||
|
// len += cl;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
// jichi 9/28/2013: removed
|
||||||
|
//void TextThread::CopyLastSentence(LPWSTR str)
|
||||||
|
//{
|
||||||
|
// int i,j,l;
|
||||||
|
// if (status&USING_UNICODE)
|
||||||
|
// {
|
||||||
|
// if (used>8)
|
||||||
|
// {
|
||||||
|
// j=used>0xF0?(used-0xF0):0;
|
||||||
|
// for (i=used-0xA;i>=j;i-=2)
|
||||||
|
// {
|
||||||
|
// if (*(DWORD*)(storage+i)==0xA000D) break;
|
||||||
|
// }
|
||||||
|
// if (i>=j)
|
||||||
|
// {
|
||||||
|
// l=used-i;
|
||||||
|
// if (i>j) l-=4;
|
||||||
|
// j=4;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// i+=2;
|
||||||
|
// l=used-i;
|
||||||
|
// j=0;
|
||||||
|
// }
|
||||||
|
// memcpy(str,storage+i+j,l);
|
||||||
|
// str[l>>1]=0;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// memcpy(str,storage,used);
|
||||||
|
// str[used>>1]=0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// if (used>4)
|
||||||
|
// {
|
||||||
|
// j=used>0x80?(used-0x80):0;
|
||||||
|
// for (i=used-5;i>=j;i--)
|
||||||
|
// {
|
||||||
|
// if (*(DWORD*)(storage+i)==0xA0D0A0D) break;
|
||||||
|
// }
|
||||||
|
// if (i>=j)
|
||||||
|
// {
|
||||||
|
// l=used-i;
|
||||||
|
// if (i>j) l-=4;
|
||||||
|
// j=4;
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// i++;
|
||||||
|
// l=used-i;
|
||||||
|
// j=0;
|
||||||
|
// }
|
||||||
|
// size_t sz = (l|0xF) + 1;
|
||||||
|
// char *buff = new char[sz];
|
||||||
|
// //memset(buff, 0, sz); // jichi 9/26/2013: zero memory
|
||||||
|
// memcpy(buff, storage + i + j, l);
|
||||||
|
// buff[l] = 0;
|
||||||
|
// str[MB_WC(buff, str)] = 0;
|
||||||
|
// delete[] buff;
|
||||||
|
// } else {
|
||||||
|
// storage[used] = 0;
|
||||||
|
// str[MB_WC((char *)storage, str)] = 0;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
static char clipboard_buffer[0x400];
|
||||||
|
// jichi 8/25/2013: clipboard removed
|
||||||
|
void CopyToClipboard(void* str,bool unicode, int len)
|
||||||
|
{
|
||||||
|
if (settings->clipboardFlag && str && len > 0)
|
||||||
|
{
|
||||||
|
int size=(len*2|0xF)+1;
|
||||||
|
if (len>=1022) return;
|
||||||
|
memcpy(clipboard_buffer,str,len);
|
||||||
|
*(WORD*)(clipboard_buffer+len)=0;
|
||||||
|
HGLOBAL hCopy;
|
||||||
|
LPWSTR copy;
|
||||||
|
if (OpenClipboard(0))
|
||||||
|
{
|
||||||
|
if (hCopy=GlobalAlloc(GMEM_MOVEABLE,size))
|
||||||
|
{
|
||||||
|
if (copy=(LPWSTR)GlobalLock(hCopy))
|
||||||
|
{
|
||||||
|
if (unicode)
|
||||||
|
{
|
||||||
|
memcpy(copy,clipboard_buffer,len+2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
copy[MB_WC(clipboard_buffer,copy)]=0;
|
||||||
|
GlobalUnlock(hCopy);
|
||||||
|
EmptyClipboard();
|
||||||
|
SetClipboardData(CF_UNICODETEXT,hCopy);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseClipboard();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void TextThread::CopyLastToClipboard()
|
||||||
|
{
|
||||||
|
// jichi 8/25/2013: clipboard removed
|
||||||
|
CopyToClipboard(storage+last_sentence,(status&USING_UNICODE)>0,used-last_sentence);
|
||||||
|
}
|
||||||
|
|
||||||
|
//void TextThread::ResetEditText()
|
||||||
|
//{
|
||||||
|
// //__asm int 3;
|
||||||
|
// WCHAR str[0x20];
|
||||||
|
// swprintf(str,L"%.8X",_ReturnAddress());
|
||||||
|
//}
|
||||||
|
|
||||||
|
// jichi 9/25/2013: Removed
|
||||||
|
//void TextThread::ExportTextToFile(LPWSTR) //filename)
|
||||||
|
//{
|
||||||
|
// HANDLE hFile=IthCreateFile(filename,FILE_WRITE_DATA,0,FILE_OPEN_IF);
|
||||||
|
// if (hFile==INVALID_HANDLE_VALUE) return;
|
||||||
|
// EnterCriticalSection(&cs_store);
|
||||||
|
// IO_STATUS_BLOCK ios;
|
||||||
|
// LPVOID buffer=storage;
|
||||||
|
// DWORD len=used;
|
||||||
|
// BYTE bom[4]={0xFF,0xFE,0,0};
|
||||||
|
// LARGE_INTEGER offset={2,0};
|
||||||
|
// if ((status&USING_UNICODE)==0)
|
||||||
|
// {
|
||||||
|
// len=MB_WC_count((char*)storage,used);
|
||||||
|
// buffer = new wchar_t[len+1];
|
||||||
|
// MB_WC((char*)storage,(wchar_t*)buffer);
|
||||||
|
// len<<=1;
|
||||||
|
// }
|
||||||
|
// NtWriteFile(hFile,0,0,0,&ios,bom,2,0,0);
|
||||||
|
// NtWriteFile(hFile,0,0,0,&ios,buffer,len,&offset,0);
|
||||||
|
// NtFlushBuffersFile(hFile,&ios);
|
||||||
|
// if (buffer !=storage)
|
||||||
|
// delete[] buffer;
|
||||||
|
// NtClose(hFile);
|
||||||
|
// LeaveCriticalSection(&cs_store);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//void TextThread::SetComment(LPWSTR str)
|
||||||
|
//{
|
||||||
|
// if (comment)
|
||||||
|
// delete[] comment;
|
||||||
|
// size_t sz = wcslen(str);
|
||||||
|
// comment = new wchar_t[sz + 1];
|
||||||
|
// comment[sz] = 0;
|
||||||
|
// wcscpy(comment, str);
|
||||||
|
//}
|
||||||
|
|
||||||
|
void TextThread::SetNewLineFlag() { status |= BUFF_NEWLINE; }
|
||||||
|
|
||||||
|
bool TextThread::CheckCycle(TextThread* start)
|
||||||
|
{
|
||||||
|
if (link==start||this==start) return true;
|
||||||
|
if (link==0) return false;
|
||||||
|
return link->CheckCycle(start);
|
||||||
|
}
|
||||||
|
void TextThread::SetNewLineTimer()
|
||||||
|
{
|
||||||
|
if (thread_number == 0)
|
||||||
|
// jichi 10/27/2013: Not used
|
||||||
|
timer = 0; //SetTimer(hMainWnd,(UINT_PTR)this, settings->splittingInterval, NewLineConsole);
|
||||||
|
else
|
||||||
|
timer = SetTimer(hMainWnd, (UINT_PTR)this, settings->splittingInterval, NewLineBuff);
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD TextThread::GetThreadString(LPSTR str, DWORD max)
|
||||||
|
{
|
||||||
|
DWORD len = 0;
|
||||||
|
if (max) {
|
||||||
|
char buffer[0x200];
|
||||||
|
char c;
|
||||||
|
if (thread_string == nullptr)
|
||||||
|
GetEntryString(buffer, 0x200); //This will allocate thread_string.
|
||||||
|
LPSTR end = thread_string;
|
||||||
|
for (; *end; end++);
|
||||||
|
c = thread_string[0];
|
||||||
|
thread_string[0] = ':';
|
||||||
|
LPSTR p1 = end;
|
||||||
|
for (; *p1 != ':'; p1--);
|
||||||
|
thread_string[0] = c;
|
||||||
|
if (p1 == thread_string)
|
||||||
|
return 0;
|
||||||
|
p1++;
|
||||||
|
len = end - p1;
|
||||||
|
if (len >= max)
|
||||||
|
len = max - 1;
|
||||||
|
::memcpy(str, p1, len);
|
||||||
|
str[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
void TextThread::UnLinkAll()
|
||||||
|
{
|
||||||
|
if (link) link->UnLinkAll();
|
||||||
|
link = 0;
|
||||||
|
link_number = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
135
vnr/texthook/host/textthread.h
Normal file
135
vnr/texthook/host/textthread.h
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// textthread.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// Branch: ITH/TextThread.h, rev 120
|
||||||
|
|
||||||
|
#include "host/textthread_p.h"
|
||||||
|
#include <intrin.h> // require _InterlockedExchange
|
||||||
|
|
||||||
|
struct RepeatCountNode {
|
||||||
|
short repeat;
|
||||||
|
short count;
|
||||||
|
RepeatCountNode *next;
|
||||||
|
|
||||||
|
//RepeatCountNode() : repeat(0), count(0), next(nullptr) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ThreadParameter {
|
||||||
|
DWORD pid; // jichi: 5/11/2014: The process ID
|
||||||
|
DWORD hook;
|
||||||
|
DWORD retn; // jichi 5/11/2014: The return address of the hook
|
||||||
|
DWORD spl; // jichi 5/11/2014: the processed split value of the hook parameter
|
||||||
|
};
|
||||||
|
|
||||||
|
#define CURRENT_SELECT 0x1000
|
||||||
|
#define REPEAT_NUMBER_DECIDED 0x2000
|
||||||
|
#define BUFF_NEWLINE 0x4000
|
||||||
|
#define CYCLIC_REPEAT 0x8000
|
||||||
|
#define COUNT_PER_FOWARD 0x200
|
||||||
|
#define REPEAT_DETECT 0x10000
|
||||||
|
#define REPEAT_SUPPRESS 0x20000
|
||||||
|
#define REPEAT_NEWLINE 0x40000
|
||||||
|
|
||||||
|
class TextThread;
|
||||||
|
typedef void (* ConsoleCallback)(LPCSTR text);
|
||||||
|
typedef void (* ConsoleWCallback)(LPCWSTR text);
|
||||||
|
typedef DWORD (* ThreadOutputFilterCallback)(TextThread *, BYTE *, DWORD, DWORD, PVOID, bool space); // jichi 10/27/2013: Add space
|
||||||
|
typedef DWORD (* ThreadEventCallback)(TextThread *);
|
||||||
|
|
||||||
|
//extern DWORD split_time,repeat_count,global_filter,cyclic_remove;
|
||||||
|
|
||||||
|
class TextThread : public MyVector<BYTE, 0x200>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
TextThread(DWORD pid, DWORD hook, DWORD retn, DWORD spl, WORD num);
|
||||||
|
~TextThread();
|
||||||
|
//virtual void CopyLastSentence(LPWSTR str);
|
||||||
|
//virtual void SetComment(LPWSTR);
|
||||||
|
//virtual void ExportTextToFile(LPWSTR filename);
|
||||||
|
|
||||||
|
virtual bool CheckCycle(TextThread *start);
|
||||||
|
virtual DWORD GetThreadString(LPSTR str, DWORD max);
|
||||||
|
virtual DWORD GetEntryString(LPSTR str, DWORD max = 0x200);
|
||||||
|
|
||||||
|
void Reset();
|
||||||
|
void AddText(const BYTE *con,int len, bool new_line, bool space); // jichi 10/27/2013: add const; remove console; add space
|
||||||
|
void RemoveSingleRepeatAuto(const BYTE *con, int &len); // jichi 10/27/2013: add const
|
||||||
|
void RemoveSingleRepeatForce(BYTE *con, int &len);
|
||||||
|
void RemoveCyclicRepeat(BYTE *&con, int &len);
|
||||||
|
void ResetRepeatStatus();
|
||||||
|
void AddLineBreak();
|
||||||
|
//void ResetEditText();
|
||||||
|
void ComboSelectCurrent();
|
||||||
|
void UnLinkAll();
|
||||||
|
void CopyLastToClipboard();
|
||||||
|
|
||||||
|
//void AdjustPrevRepeat(DWORD len);
|
||||||
|
//void PrevRepeatLength(DWORD &len);
|
||||||
|
|
||||||
|
//bool AddToCombo();
|
||||||
|
bool RemoveFromCombo();
|
||||||
|
|
||||||
|
void SetNewLineFlag();
|
||||||
|
void SetNewLineTimer();
|
||||||
|
|
||||||
|
BYTE *GetStore(DWORD *len) { if (len) *len = used; return storage; }
|
||||||
|
DWORD LastSentenceLen() { return used - last_sentence; }
|
||||||
|
DWORD PID() const { return tp.pid; }
|
||||||
|
DWORD Addr() const {return tp.hook; }
|
||||||
|
DWORD &Status() { return status; }
|
||||||
|
WORD Number() const { return thread_number; }
|
||||||
|
WORD &Last() { return last; }
|
||||||
|
WORD &LinkNumber() { return link_number; }
|
||||||
|
UINT_PTR &Timer() { return timer; }
|
||||||
|
ThreadParameter *GetThreadParameter() { return &tp; }
|
||||||
|
TextThread *&Link() { return link; }
|
||||||
|
//LPCWSTR GetComment() { return comment; }
|
||||||
|
|
||||||
|
ThreadOutputFilterCallback RegisterOutputCallBack(ThreadOutputFilterCallback cb, PVOID data)
|
||||||
|
{
|
||||||
|
app_data = data;
|
||||||
|
return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&output,(long)cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
ThreadOutputFilterCallback RegisterFilterCallBack(ThreadOutputFilterCallback cb, PVOID data)
|
||||||
|
{
|
||||||
|
app_data = data;
|
||||||
|
return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&filter,(long)cb);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetRepeatFlag() { status |= CYCLIC_REPEAT; }
|
||||||
|
void ClearNewLineFlag() { status &= ~BUFF_NEWLINE; }
|
||||||
|
void ClearRepeatFlag() { status &= ~CYCLIC_REPEAT; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void AddTextDirect(const BYTE *con, int len, bool space); // jichi 10/27/2013: add const; add space; change to protected
|
||||||
|
|
||||||
|
private:
|
||||||
|
ThreadParameter tp;
|
||||||
|
|
||||||
|
WORD thread_number,
|
||||||
|
link_number;
|
||||||
|
WORD last,
|
||||||
|
align_space;
|
||||||
|
WORD repeat_single;
|
||||||
|
WORD repeat_single_current;
|
||||||
|
WORD repeat_single_count;
|
||||||
|
WORD repeat_detect_count;
|
||||||
|
RepeatCountNode *head;
|
||||||
|
|
||||||
|
TextThread *link;
|
||||||
|
ThreadOutputFilterCallback filter; // jichi 10/27/2013: Remove filter
|
||||||
|
ThreadOutputFilterCallback output;
|
||||||
|
PVOID app_data;
|
||||||
|
LPSTR thread_string;
|
||||||
|
UINT_PTR timer;
|
||||||
|
DWORD status,repeat_detect_limit;
|
||||||
|
DWORD last_sentence,
|
||||||
|
prev_sentence,
|
||||||
|
sentence_length,
|
||||||
|
repeat_index,
|
||||||
|
last_time;
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
155
vnr/texthook/host/textthread_p.h
Normal file
155
vnr/texthook/host/textthread_p.h
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
#pragma once
|
||||||
|
// textthread_p.h
|
||||||
|
// 8/14/2013 jichi
|
||||||
|
// Branch: ITH/main_template.h, rev 66
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void Release(const T &p) { delete p; }
|
||||||
|
|
||||||
|
// Prevent memory release.
|
||||||
|
// Used when T is basic types and will be automatically released (on stack).
|
||||||
|
#define MK_BASIC_TYPE(T) \
|
||||||
|
template<> \
|
||||||
|
void Release<T>(const T &p) {}
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
struct BinaryEqual {
|
||||||
|
bool operator ()(const T &a, const T &b, DWORD) { return a == b; }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T, int default_size, class fComp=BinaryEqual<T> >
|
||||||
|
class MyVector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyVector() : size(default_size), used(0)
|
||||||
|
{
|
||||||
|
InitializeCriticalSection(&cs_store);
|
||||||
|
storage = new T[size];
|
||||||
|
// jichi 9/21/2013: zero memory
|
||||||
|
// This would cause trouble if T is not an atomic type
|
||||||
|
::memset(storage, 0, sizeof(T) * size);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~MyVector()
|
||||||
|
{
|
||||||
|
if (storage)
|
||||||
|
delete[] storage;
|
||||||
|
DeleteCriticalSection(&cs_store);
|
||||||
|
storage = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Reset()
|
||||||
|
{
|
||||||
|
EnterCriticalSection(&cs_store);
|
||||||
|
for (int i = 0; i < used; i++) {
|
||||||
|
Release<T>(storage[i]);
|
||||||
|
storage[i] = T();
|
||||||
|
}
|
||||||
|
used = 0;
|
||||||
|
LeaveCriticalSection(&cs_store);
|
||||||
|
}
|
||||||
|
void Remove(int index)
|
||||||
|
{
|
||||||
|
if (index>=used)
|
||||||
|
return;
|
||||||
|
Release<T>(storage[index]);
|
||||||
|
for (int i = index; i < used; i++)
|
||||||
|
storage[i] = storage[i+1];
|
||||||
|
used--;
|
||||||
|
}
|
||||||
|
void ClearMemory(int offset, int clear_size)
|
||||||
|
{
|
||||||
|
if (clear_size < 0)
|
||||||
|
return;
|
||||||
|
EnterCriticalSection(&cs_store);
|
||||||
|
if (offset+clear_size <= size)
|
||||||
|
::memset(storage+offset, 0, clear_size * sizeof(T)); // jichi 11/30/2013: This is the original code of ITH
|
||||||
|
LeaveCriticalSection(&cs_store);
|
||||||
|
//else __asm int 3
|
||||||
|
}
|
||||||
|
int AddToStore(T *con,int amount)
|
||||||
|
{
|
||||||
|
if (amount <= 0 || con == 0)
|
||||||
|
return 0;
|
||||||
|
int status = 0;
|
||||||
|
EnterCriticalSection(&cs_store);
|
||||||
|
if (amount + used + 2 >= size) {
|
||||||
|
while (amount + used + 2 >= size)
|
||||||
|
size<<=1;
|
||||||
|
T *temp;
|
||||||
|
if (size * sizeof(T) < 0x1000000) {
|
||||||
|
temp = new T[size];
|
||||||
|
if (size > used)
|
||||||
|
::memset(temp, 0, (size - used) * sizeof(T)); // jichi 9/25/2013: zero memory
|
||||||
|
memcpy(temp, storage, used * sizeof(T));
|
||||||
|
} else {
|
||||||
|
size = default_size;
|
||||||
|
temp = new T[size];
|
||||||
|
::memset(temp, 0, sizeof(T) * size); // jichi 9/25/2013: zero memory
|
||||||
|
used = 0;
|
||||||
|
status = 1;
|
||||||
|
}
|
||||||
|
delete[] storage;
|
||||||
|
storage = temp;
|
||||||
|
}
|
||||||
|
memcpy(storage+used, con, amount * sizeof(T));
|
||||||
|
used += amount;
|
||||||
|
LeaveCriticalSection(&cs_store);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
int Find(const T &item, int start = 0, DWORD control = 0)
|
||||||
|
{
|
||||||
|
int c = -1;
|
||||||
|
for (int i=start; i < used; i++)
|
||||||
|
if (fCmp(storage[i],item,control)) {
|
||||||
|
c=i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
//if (storage[i]==item) {c=i;break;}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
int Used() const { return used; }
|
||||||
|
T *Storage() const { return storage; }
|
||||||
|
void LockVector() { EnterCriticalSection(&cs_store); }
|
||||||
|
void UnlockVector() { LeaveCriticalSection(&cs_store); }
|
||||||
|
protected:
|
||||||
|
CRITICAL_SECTION cs_store;
|
||||||
|
int size,
|
||||||
|
used;
|
||||||
|
T *storage;
|
||||||
|
fComp fCmp;
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
|
||||||
|
/*
|
||||||
|
#ifndef ITH_STACK
|
||||||
|
#define ITH_STACK
|
||||||
|
template<class T, int default_size>
|
||||||
|
class MyStack
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MyStack(): index(0) {}
|
||||||
|
void push_back(const T& e)
|
||||||
|
{
|
||||||
|
if (index<default_size)
|
||||||
|
s[index++]=e;
|
||||||
|
}
|
||||||
|
void pop_back()
|
||||||
|
{
|
||||||
|
index--;
|
||||||
|
}
|
||||||
|
T& back()
|
||||||
|
{
|
||||||
|
return s[index-1];
|
||||||
|
}
|
||||||
|
T& operator[](int i) {return s[i];}
|
||||||
|
int size() {return index;}
|
||||||
|
private:
|
||||||
|
int index;
|
||||||
|
T s[default_size];
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
*/
|
20
vnr/texthook/ihf.txt
Normal file
20
vnr/texthook/ihf.txt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# 6/6/2012
|
||||||
|
# IHF.dll
|
||||||
|
# Skip swprintf and MessageBox statements in GetDebugPriv
|
||||||
|
#
|
||||||
|
# SVN checkout: 2012/6/6
|
||||||
|
# - IHF/main.cpp: 2012/4/8
|
||||||
|
# - IHF.{dll,lib}: 2012/6/5
|
||||||
|
|
||||||
|
6F753055 CALL DWORD PTR DS:ntdll.ZwAdjustPrivilegesTOken
|
||||||
|
6F75305B TEST EAX,EAX
|
||||||
|
6F75305B JNE SHORT 6F753077 => JNE SHORT 6F75309F
|
||||||
|
...
|
||||||
|
6F753077 PUSH EAX
|
||||||
|
...
|
||||||
|
6F75309F MOVE EAX,DWORD PTR SS:[ESP]
|
||||||
|
6F7530A2 PUSH EAX
|
||||||
|
6F7530A3 CALL DWORD PTR DS:ntdll.NtClose
|
||||||
|
...
|
||||||
|
|
||||||
|
# EOF
|
583
vnr/texthook/ihf_p.cc
Normal file
583
vnr/texthook/ihf_p.cc
Normal file
@ -0,0 +1,583 @@
|
|||||||
|
// Host_p.cc
|
||||||
|
// 10/15/2011 jichi
|
||||||
|
|
||||||
|
#include "texthook/ihf_p.h"
|
||||||
|
#include "texthook/ith_p.h"
|
||||||
|
#include "texthook/textthread_p.h"
|
||||||
|
#include "host/host.h"
|
||||||
|
#include "vnrhook/include/types.h"
|
||||||
|
#include "ithsys/ithsys.h"
|
||||||
|
#include "wintimer/wintimer.h"
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
|
||||||
|
#ifdef WITH_LIB_WINMAKER
|
||||||
|
# include "winmaker/winmaker.h"
|
||||||
|
#endif // WITH_LIB_WINMAKER
|
||||||
|
|
||||||
|
//#define ITH_RUNNING_EVENT L"ITH_PIPE_EXIST"
|
||||||
|
//#define ITH_RUNNING_MUTEX L"ITH_RUNNING"
|
||||||
|
//#define ITH_MUTEX_NAME L"ITH_MAIN_RUNNING"
|
||||||
|
|
||||||
|
//#define DEBUG "ihf_p.cc"
|
||||||
|
#include "sakurakit/skdebug.h"
|
||||||
|
|
||||||
|
//#define ITH_WITH_LINK
|
||||||
|
|
||||||
|
// - Construction -
|
||||||
|
|
||||||
|
//bool Ihf::debug_ = true;
|
||||||
|
bool Ihf::enabled_ = true;
|
||||||
|
|
||||||
|
//Settings *Ihf::settings_;
|
||||||
|
HookManager *Ihf::hookManager_;
|
||||||
|
qint64 Ihf::messageInterval_ = 250; // 0.25 secs by default, larger than the split_time (0.2sec) in ITH::setman
|
||||||
|
WId Ihf::parentWindow_;
|
||||||
|
|
||||||
|
QHash<TextThread *, TextThreadDelegate *> Ihf::threadDelegates_;
|
||||||
|
//QHash<TextThreadDelegate *, TextThreadDelegate *> Ihf::linkedDelegates_;
|
||||||
|
QHash<QString, ulong> Ihf::hookAddresses_;
|
||||||
|
|
||||||
|
char Ihf::keptThreadName_[ITH_THREAD_NAME_CAPACITY];
|
||||||
|
|
||||||
|
bool Ihf::whitelistEnabled_;
|
||||||
|
qint32 Ihf::whitelist_[Ihf::WhitelistSize];
|
||||||
|
|
||||||
|
// Debugging output
|
||||||
|
//void Ihf::consoleOutput(const char *text)
|
||||||
|
//{ if (debug_) qDebug() << "texthook:console:" << text; }
|
||||||
|
|
||||||
|
//void Ihf::consoleOutputW(const wchar_t *text)
|
||||||
|
//{ if (debug_) qDebug() << "texthook:console:" << QString::fromWCharArray(text); }
|
||||||
|
|
||||||
|
void Ihf::init()
|
||||||
|
{
|
||||||
|
IthInitSystemService();
|
||||||
|
Host_Init();
|
||||||
|
}
|
||||||
|
void Ihf::destroy()
|
||||||
|
{
|
||||||
|
Host_Destroy();
|
||||||
|
IthCloseSystemService();
|
||||||
|
}
|
||||||
|
|
||||||
|
// See also: HelloITH/main.cpp
|
||||||
|
bool Ihf::load()
|
||||||
|
{
|
||||||
|
// 12/20/2013: This would crash the error of failure to create QTimer
|
||||||
|
//if (!parentWindow_)
|
||||||
|
|
||||||
|
//::wm_register_hidden_class("vnrtexthook.class");
|
||||||
|
//parentWindow_ = (WId)::wm_create_hidden_window("vnrtexthook.class", "vnrtexthook");
|
||||||
|
|
||||||
|
DOUT("enter");
|
||||||
|
if (hookManager_) {
|
||||||
|
DOUT("leave: already loaded");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Single instance protection
|
||||||
|
//HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, ITH_MUTEX_NAME); // in kernel32.dll
|
||||||
|
//if (hMutex != 0 || ::GetLastError() != ERROR_FILE_NOT_FOUND) {
|
||||||
|
// ::CloseHandle(hMutex);
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// See: ITH/main.cpp
|
||||||
|
//if (!IthInitSystemService()) {
|
||||||
|
// DOUT("leave: error: failed to init system service");
|
||||||
|
// return false;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (::Host_Open()) {
|
||||||
|
#ifdef WITH_LIB_WINMAKER
|
||||||
|
if (!parentWindow_)
|
||||||
|
parentWindow_ = (WId)::wm_create_hidden_window("vnrtexthook");
|
||||||
|
#endif // WITH_LIB_WINMAKER
|
||||||
|
WinTimer::setGlobalWindow(parentWindow_);
|
||||||
|
::Host_GetHookManager(&hookManager_);
|
||||||
|
if (hookManager_) {
|
||||||
|
//::Host_GetSettings(&settings_);
|
||||||
|
//settings_->debug = debug_;
|
||||||
|
|
||||||
|
//hookManager_->RegisterConsoleCallback(consoleOutput);
|
||||||
|
//hookManager_->RegisterConsoleWCallback(consoleOutputW);
|
||||||
|
//hookManager_->RegisterProcessAttachCallback(processAttach);
|
||||||
|
//hookManager_->RegisterProcessDetachCallback(processDetach);
|
||||||
|
//hookManager_->RegisterProcessNewHookCallback(processNewHook);
|
||||||
|
//hookManager_->RegisterThreadResetCallback(threadReset);
|
||||||
|
hookManager_->RegisterThreadCreateCallback(threadCreate);
|
||||||
|
hookManager_->RegisterThreadRemoveCallback(threadRemove);
|
||||||
|
|
||||||
|
::Host_Start();
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
::Host_Close();
|
||||||
|
DOUT("leave: hook manager =" << hookManager_);
|
||||||
|
return hookManager_;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ihf::unload()
|
||||||
|
{
|
||||||
|
DOUT("enter: hook manager =" << hookManager_);
|
||||||
|
if (hookManager_) {
|
||||||
|
//hookManager_->RegisterProcessAttachCallback(nullptr);
|
||||||
|
//hookManager_->RegisterProcessDetachCallback(nullptr);
|
||||||
|
//hookManager_->RegisterProcessNewHookCallback(nullptr);
|
||||||
|
//hookManager_->RegisterThreadResetCallback(nullptr);
|
||||||
|
hookManager_->RegisterThreadCreateCallback(nullptr);
|
||||||
|
hookManager_->RegisterThreadRemoveCallback(nullptr);
|
||||||
|
// Console output is not unregisterd to avoid segmentation fault
|
||||||
|
//hookManager_->RegisterConsoleCallback(nullptr);
|
||||||
|
|
||||||
|
::Host_Close();
|
||||||
|
hookManager_ = nullptr;
|
||||||
|
//settings_ = nullptr;
|
||||||
|
|
||||||
|
#ifdef WITH_LIB_WINMAKER
|
||||||
|
if (parentWindow_) {
|
||||||
|
wm_destroy_window(parentWindow_);
|
||||||
|
parentWindow_ = nullptr;
|
||||||
|
}
|
||||||
|
#endif // WITH_LIB_WINMAKER
|
||||||
|
}
|
||||||
|
//if (parentWindow_) {
|
||||||
|
// wm_destroy_window(parentWindow_);
|
||||||
|
// parentWindow_ = nullptr;
|
||||||
|
//}
|
||||||
|
DOUT("leave");
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Callbacks -
|
||||||
|
|
||||||
|
//DWORD Ihf::processAttach(DWORD pid)
|
||||||
|
//{
|
||||||
|
// DOUT("enter");
|
||||||
|
// Q_UNUSED(pid);
|
||||||
|
// DOUT("leave");
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//DWORD Ihf::processDetach(DWORD pid)
|
||||||
|
//{
|
||||||
|
// DOUT("enter");
|
||||||
|
// Q_UNUSED(pid);
|
||||||
|
// DOUT("leave");
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//DWORD Ihf::processNewHook(DWORD pid)
|
||||||
|
//{
|
||||||
|
// DOUT("enter");
|
||||||
|
// Q_UNUSED(pid);
|
||||||
|
// DOUT("leave");
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// See: HelloITH/main.cpp
|
||||||
|
// See: ThreadCreate in ITH/window.cpp
|
||||||
|
DWORD Ihf::threadCreate(TextThread *t)
|
||||||
|
{
|
||||||
|
Q_ASSERT(t);
|
||||||
|
DOUT("enter: pid =" << t->PID());
|
||||||
|
Q_ASSERT(hookManager_);
|
||||||
|
|
||||||
|
// Propagate UNICODE
|
||||||
|
// See: ThreadCreate in ITH/window.cpp
|
||||||
|
//if (ProcessRecord *pr = hookManager_->GetProcessRecord(t->PID())) {
|
||||||
|
// NtWaitForSingleObject(pr->hookman_mutex, 0, 0);
|
||||||
|
// Hook *hk = static_cast<Hook *>(pr->hookman_map);
|
||||||
|
// Q_ASSERT(!hk&&!MAX_HOOK || hk&&MAX_HOOK);
|
||||||
|
// for (int i = 0; i < MAX_HOOK; i++) {
|
||||||
|
// if (hk[i].Address() == t->Addr()) {
|
||||||
|
// if (hk[i].Type() & USING_UNICODE)
|
||||||
|
// t->Status() |= USING_UNICODE;
|
||||||
|
// break;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// NtReleaseMutant(pr->hookman_mutex, 0);
|
||||||
|
//}
|
||||||
|
auto d = new TextThreadDelegate(t);
|
||||||
|
bool init = true;
|
||||||
|
foreach (TextThreadDelegate *it, threadDelegates_)
|
||||||
|
if (d->signature() == it->signature()) {
|
||||||
|
TextThreadDelegate::release(d);
|
||||||
|
d = it;
|
||||||
|
d->retain();
|
||||||
|
init = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (init) {
|
||||||
|
d->setInterval(messageInterval_);
|
||||||
|
d->setParentWindow(parentWindow_);
|
||||||
|
updateLinkedDelegate(d);
|
||||||
|
}
|
||||||
|
threadDelegates_[t] = d;
|
||||||
|
t->RegisterOutputCallBack(threadOutput, d);
|
||||||
|
//t->RegisterFilterCallBack(threadFilter, d);
|
||||||
|
DOUT("leave");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See also: HelloITH/main.cpp
|
||||||
|
DWORD Ihf::threadRemove(TextThread *t)
|
||||||
|
{
|
||||||
|
DOUT("enter");
|
||||||
|
Q_ASSERT(t);
|
||||||
|
|
||||||
|
auto p = threadDelegates_.find(t);
|
||||||
|
if (p != threadDelegates_.end()) {
|
||||||
|
auto d = p.value();
|
||||||
|
//if (!linkedDelegates_.isEmpty()) {
|
||||||
|
// linkedDelegates_.remove(d);
|
||||||
|
// while (auto k = linkedDelegates_.key(d))
|
||||||
|
// linkedDelegates_.remove(k);
|
||||||
|
//}
|
||||||
|
threadDelegates_.erase(p);
|
||||||
|
TextThreadDelegate::release(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ITH_WITH_LINK
|
||||||
|
::Host_UnLinkAll(t->Number());
|
||||||
|
#endif // ITH_WITH_LINK
|
||||||
|
|
||||||
|
DOUT("leave");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: HelloITH/main.cpp
|
||||||
|
DWORD Ihf::threadOutput(TextThread *t, BYTE *data, DWORD dataLength, DWORD newLine, PVOID pUserData, bool space)
|
||||||
|
{
|
||||||
|
DOUT("newLine =" << newLine << ", dataLength =" << dataLength << ", space =" << space);
|
||||||
|
Q_UNUSED(t)
|
||||||
|
Q_ASSERT(data);
|
||||||
|
Q_ASSERT(pUserData);
|
||||||
|
|
||||||
|
auto d = static_cast<TextThreadDelegate *>(pUserData);
|
||||||
|
//if (TextThreadDelegate *link = findLinkedDelegate(d))
|
||||||
|
// d = link;
|
||||||
|
Q_ASSERT(d);
|
||||||
|
if (!enabled_ ||
|
||||||
|
whitelistEnabled_ &&
|
||||||
|
!whitelistContains(d->signature()) &&
|
||||||
|
!(keptThreadName_[0] && d->nameEquals(keptThreadName_))) {
|
||||||
|
DOUT("leave: ignored");
|
||||||
|
return dataLength;
|
||||||
|
}
|
||||||
|
if (newLine)
|
||||||
|
d->touch();
|
||||||
|
//d->flush(); // new line data are ignored
|
||||||
|
else if (dataLength || space)
|
||||||
|
d->append(reinterpret_cast<char *>(data), dataLength, space);
|
||||||
|
//QString text = QString::fromLocal8Bit(reinterpret_cast<LPCSTR>(data), len);
|
||||||
|
DOUT("leave");
|
||||||
|
return dataLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
//TextThreadDelegate *Ihf::findLinkedDelegate(TextThreadDelegate *d)
|
||||||
|
//{
|
||||||
|
// Q_ASSERT(d);
|
||||||
|
// if (!linkedDelegates_.isEmpty()) {
|
||||||
|
// auto p = linkedDelegates_.find(d);
|
||||||
|
// if (p != linkedDelegates_.end())
|
||||||
|
// return p.value();
|
||||||
|
// }
|
||||||
|
// return nullptr;
|
||||||
|
//}
|
||||||
|
|
||||||
|
void Ihf::updateLinkedDelegate(TextThreadDelegate *d)
|
||||||
|
{
|
||||||
|
#ifdef ITH_WITH_LINK
|
||||||
|
Q_ASSERT(t);
|
||||||
|
foreach (TextThreadDelegate *it, threadDelegates_)
|
||||||
|
if (it->delegateOf(d))
|
||||||
|
::Host_AddLink(d->threadNumber(), it->threadNumber());
|
||||||
|
else if (d->delegateOf(it))
|
||||||
|
::Host_AddLink(it->threadNumber(), d->threadNumber());
|
||||||
|
#else
|
||||||
|
Q_UNUSED(d);
|
||||||
|
#endif // ITH_WITH_LINK
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Injection -
|
||||||
|
|
||||||
|
// See: Host_InjectByPID in IHF/main.cpp
|
||||||
|
// See: InjectThread in ITH/profile.cpp
|
||||||
|
bool Ihf::attachProcess(DWORD pid)
|
||||||
|
{
|
||||||
|
DOUT("enter: pid =" << pid);
|
||||||
|
bool ok = ::Host_InjectByPID(pid);
|
||||||
|
|
||||||
|
//enum { AttachDelay = 500 }; // in msec
|
||||||
|
//::Sleep(AttachDelay);
|
||||||
|
|
||||||
|
DOUT("leave: ret =" << ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: Host_ActiveDetachProcess in IHF/main.cpp
|
||||||
|
bool Ihf::detachProcess(DWORD pid) { return ::Host_ActiveDetachProcess(pid); }
|
||||||
|
bool Ihf::hijackProcess(DWORD pid) { return ::Host_HijackProcess(pid); }
|
||||||
|
|
||||||
|
// - Hook -
|
||||||
|
|
||||||
|
// See: Host_ModifyHook in IHF/main.cpp
|
||||||
|
bool Ihf::updateHook(ulong pid, const QString &code)
|
||||||
|
{
|
||||||
|
DOUT("enter: pid =" << pid << ", code =" << code);
|
||||||
|
Q_ASSERT(pid);
|
||||||
|
HookParam hp = {};
|
||||||
|
if (!Ith::parseHookCode(code, &hp)) {
|
||||||
|
DOUT("leave: failed to parse hook code");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD hh = ::Host_ModifyHook(pid, &hp);
|
||||||
|
bool ok = ~hh;
|
||||||
|
DOUT("leave: ret =" << ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: Host_InsertHook in IHF/main.cpp
|
||||||
|
bool Ihf::addHook(ulong pid, const QString &code, const QString &name, bool verbose)
|
||||||
|
{
|
||||||
|
DOUT("enter: pid =" << pid << ", name =" << name << ", code =" << code);
|
||||||
|
Q_ASSERT(pid);
|
||||||
|
if (hookAddresses_.contains(code)) {
|
||||||
|
DOUT("leave: already added");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
HookParam hp = {};
|
||||||
|
if (!Ith::parseHookCode(code, &hp, verbose)) {
|
||||||
|
DOUT("leave: failed to parse hook code");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD hh = ::Host_InsertHook(pid, &hp, name.toAscii());
|
||||||
|
//DWORD hh = ::NewHook(hp, nameBuf);
|
||||||
|
bool ok = ~hh;
|
||||||
|
if (ok && hp.address) {
|
||||||
|
DOUT("hook address =" << hp.address);
|
||||||
|
hookAddresses_[code] = hp.address;
|
||||||
|
}
|
||||||
|
DOUT("leave: ok =" << ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: Host_RemoveHook in IHF/main.cpp
|
||||||
|
bool Ihf::removeHook(ulong pid, const QString &code)
|
||||||
|
{
|
||||||
|
DOUT("enter: pid =" << pid << ", code =" << code);
|
||||||
|
Q_ASSERT(pid);
|
||||||
|
auto p = hookAddresses_.find(code);
|
||||||
|
if (p == hookAddresses_.end()) {
|
||||||
|
DOUT("leave: hook not added");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DWORD addr = p.value();
|
||||||
|
Q_ASSERT(addr);
|
||||||
|
hookAddresses_.erase(p);
|
||||||
|
|
||||||
|
DWORD hh = ::Host_RemoveHook(pid, addr);
|
||||||
|
bool ok = ~hh;
|
||||||
|
DOUT("leave: ret =" << ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ihf::verifyHookCode(const QString &code)
|
||||||
|
{ return Ith::verifyHookCode(code); }
|
||||||
|
|
||||||
|
// - Whitelist -
|
||||||
|
|
||||||
|
QList<qint32> Ihf::whitelist()
|
||||||
|
{
|
||||||
|
QList<qint32> ret;
|
||||||
|
const qint32 *p = whitelist_;
|
||||||
|
while (*p)
|
||||||
|
ret.append(*p++);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ihf::clearWhitelist() { *whitelist_= 0; }
|
||||||
|
|
||||||
|
void Ihf::setWhitelist(const QList<qint32> &l)
|
||||||
|
{
|
||||||
|
qint32 *p = whitelist_;
|
||||||
|
if (!l.isEmpty())
|
||||||
|
foreach (qint32 it, l) {
|
||||||
|
*p++ = it;
|
||||||
|
if (p >= whitelist_ + WhitelistSize)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
whitelist_[qMin(l.size(), WhitelistSize -1)] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ihf::whitelistContains(qint32 signature)
|
||||||
|
{
|
||||||
|
const qint32 *p = whitelist_;
|
||||||
|
while (*p)
|
||||||
|
if (signature == *p++)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
|
||||||
|
/*
|
||||||
|
BYTE LeadByteTable[0x100] = {
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||||
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||||
|
2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
||||||
|
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
|
||||||
|
2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1
|
||||||
|
};
|
||||||
|
|
||||||
|
DWORD Ihf::threadFilter(TextThread *thread, BYTE *out, DWORD len, DWORD new_line, PVOID data)
|
||||||
|
{
|
||||||
|
DWORD status = thread->Status();
|
||||||
|
|
||||||
|
if (!new_line && thread->Number() != 0)
|
||||||
|
{
|
||||||
|
if (status & USING_UNICODE)
|
||||||
|
{
|
||||||
|
DWORD i, j;
|
||||||
|
len >>= 1;
|
||||||
|
WCHAR c, *str = (LPWSTR)out;
|
||||||
|
for (i = 0, j = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
c = str[i];
|
||||||
|
//if (!uni_filter->Check(c))
|
||||||
|
str[j++] = c;
|
||||||
|
|
||||||
|
}
|
||||||
|
memset(str + j, 0, (len - j) << 1);
|
||||||
|
len = j << 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
WORD c;
|
||||||
|
DWORD i, j;
|
||||||
|
for (i = 0, j = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
c = out[i];
|
||||||
|
if (LeadByteTable[c] == 1)
|
||||||
|
{
|
||||||
|
//if (!mb_filter->Check(c))
|
||||||
|
out[j++] = c & 0xFF;
|
||||||
|
}
|
||||||
|
else if (i + 1 < len)
|
||||||
|
{
|
||||||
|
|
||||||
|
c = out[i + 1];
|
||||||
|
c <<= 8;
|
||||||
|
c |= out[i];
|
||||||
|
//if (!mb_filter->Check(c))
|
||||||
|
{
|
||||||
|
out[j++] = c & 0xFF;
|
||||||
|
out[j++] = c >> 8;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
memset(out + j, 0, len - j);
|
||||||
|
len = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
// jichi: 10/15/2011: FIXME: This overload will infect the entire program,
|
||||||
|
// even source files that exclude this header, which is unexpected.
|
||||||
|
// No idea what is the trade off of this behavior on performance and liability.
|
||||||
|
// Lots of Qt stuff doesn't work such as QString::toStdString.
|
||||||
|
// I have to use dynamic linkage to avoid being polluted by this module.
|
||||||
|
//
|
||||||
|
// original author: HEAP_ZERO_MEMORY flag is critical. All new object are assumed with zero initialized.
|
||||||
|
// jichi: 10/20/2011: I think the only reason to use Rtl heap here is to ensure HEAP_ZERO_MEMORY,
|
||||||
|
// which is really a bad programming style and incur unstability on heap memory allocation.
|
||||||
|
// ::RtlFreeHeap crash on DLL debug mode. Replace it with standard malloc/free.
|
||||||
|
// ::hHeap handle is also removed from ith/sys.c.cc
|
||||||
|
|
||||||
|
inline void * __cdecl operator new(size_t lSize)
|
||||||
|
{ return ::RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize); }
|
||||||
|
|
||||||
|
inline void * __cdecl operator new[](size_t lSize)
|
||||||
|
{ return ::RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize); }
|
||||||
|
|
||||||
|
inline void __cdecl operator delete(void *pBlock)
|
||||||
|
{ ::RtlFreeHeap(::hHeap, 0, pBlock); }
|
||||||
|
|
||||||
|
inline void __cdecl operator delete[](void* pBlock)
|
||||||
|
{ ::RtlFreeHeap(::hHeap, 0, pBlock); }
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
|
inline void * __cdecl operator new(size_t size) throw()
|
||||||
|
{
|
||||||
|
if (!size) // When the value of the expression in a direct-new-declarator is zero,
|
||||||
|
size = 4; // the allocation function is called to allocatean array with no elements.(ISO)
|
||||||
|
|
||||||
|
void *p = malloc(size);
|
||||||
|
if (p)
|
||||||
|
memset(p, 0, size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void * __cdecl operator new[](size_t size) throw()
|
||||||
|
{
|
||||||
|
if (!size) // When the value of the expression in a direct-new-declarator is zero,
|
||||||
|
size = 4; // the allocation function is called to allocatean array with no elements.(ISO)
|
||||||
|
|
||||||
|
void *p = malloc(size);
|
||||||
|
if (p)
|
||||||
|
memset(p, 0, size);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void __cdecl operator delete(void *p) throw() { free(p); }
|
||||||
|
inline void __cdecl operator delete[](void *p) throw() { free(p); }
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//QString
|
||||||
|
//Ihf::getHookNameById(ulong hookId)
|
||||||
|
//{
|
||||||
|
// QString ret;
|
||||||
|
// if (hookId) {
|
||||||
|
// auto p = reinterpret_cast<TextThread *>(hookId);
|
||||||
|
// if (p->good())
|
||||||
|
// ret = p->name();
|
||||||
|
// }
|
||||||
|
// return ret;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//DWORD ProcessAttach(DWORD pid)
|
||||||
|
//{
|
||||||
|
// DOUT("process attached, pid =" << pid);
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
//DWORD ProcessDetach(DWORD pid)
|
||||||
|
//{
|
||||||
|
// DOUT("process detached, pid =" << pid);
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
//DWORD ProcessNewHook(DWORD pid)
|
||||||
|
//{
|
||||||
|
// DOUT("process has new hook inserted, pid =" << pid);
|
||||||
|
// return 0;
|
||||||
|
//}
|
117
vnr/texthook/ihf_p.h
Normal file
117
vnr/texthook/ihf_p.h
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ihf_p.h
|
||||||
|
// 10/15/2011 jichi
|
||||||
|
// Internal header.
|
||||||
|
// Wrapper of IHF functions.
|
||||||
|
|
||||||
|
#include <QtCore/QHash>
|
||||||
|
#include <QtCore/QList>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtGui/qwindowdefs.h> // for WId
|
||||||
|
|
||||||
|
//struct Settings; // opaque in ith/host/settings.h
|
||||||
|
class HookManager; // opaque in ith/host/hookman.h
|
||||||
|
class TextThread; // opaque in ith/host/textthread.h
|
||||||
|
class TextThreadDelegate;
|
||||||
|
|
||||||
|
enum { ITH_THREAD_NAME_CAPACITY = 0x200 }; // used internally by ITH
|
||||||
|
|
||||||
|
class Ihf
|
||||||
|
{
|
||||||
|
Ihf() {} // Singleton
|
||||||
|
|
||||||
|
static bool enabled_;
|
||||||
|
|
||||||
|
//static Settings *settings_;
|
||||||
|
static HookManager *hookManager_;
|
||||||
|
static qint64 messageInterval_;
|
||||||
|
static WId parentWindow_;
|
||||||
|
|
||||||
|
static QHash<TextThread *, TextThreadDelegate *> threadDelegates_;
|
||||||
|
//static QHash<TextThreadDelegate *, TextThreadDelegate *> linkedDelegates_;
|
||||||
|
static QHash<QString, ulong> hookAddresses_;
|
||||||
|
|
||||||
|
enum { WhitelistSize = 0x20 + 1 }; // ITH capacity is 0x20
|
||||||
|
static qint32 whitelist_[WhitelistSize]; // List of signatures. The last element is zero. I.e., at most BlackSize-1 threads.
|
||||||
|
static bool whitelistEnabled_;
|
||||||
|
static char keptThreadName_[ITH_THREAD_NAME_CAPACITY];
|
||||||
|
//static QString userDefinedThreadName_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// - Initialization -
|
||||||
|
static void init();
|
||||||
|
static void destroy();
|
||||||
|
|
||||||
|
static bool load();
|
||||||
|
static bool isLoaded() { return hookManager_; }
|
||||||
|
static void unload();
|
||||||
|
|
||||||
|
// - Properties -
|
||||||
|
|
||||||
|
static bool isEnabled() { return enabled_; }
|
||||||
|
static void setEnabled(bool t) { enabled_ = t; }
|
||||||
|
|
||||||
|
/// A valid window handle is required to make ITH work
|
||||||
|
static WId parentWindow() { return parentWindow_; }
|
||||||
|
static void setParentWindow(WId hwnd) { parentWindow_ = hwnd; }
|
||||||
|
|
||||||
|
/// Timeout (msecs) for a text message
|
||||||
|
static qint64 messageInterval() { return messageInterval_; }
|
||||||
|
static void setMessageInterval(qint64 msecs) { messageInterval_ = msecs; }
|
||||||
|
|
||||||
|
// - Injection -
|
||||||
|
static bool attachProcess(ulong pid);
|
||||||
|
static bool detachProcess(ulong pid);
|
||||||
|
static bool hijackProcess(ulong pid);
|
||||||
|
|
||||||
|
/// Add hook code
|
||||||
|
static bool addHook(ulong pid, const QString &code, const QString &name = QString(), bool verbose = true);
|
||||||
|
static bool updateHook(ulong pid, const QString &code); // not used
|
||||||
|
static bool removeHook(ulong pid, const QString &code);
|
||||||
|
static bool verifyHookCode(const QString &code);
|
||||||
|
|
||||||
|
// - Whitelist -
|
||||||
|
static bool isWhitelistEnabled() { return whitelistEnabled_; }
|
||||||
|
static void setWhitelistEnabled(bool t) { whitelistEnabled_ = t; }
|
||||||
|
|
||||||
|
static QList<qint32> whitelist();
|
||||||
|
static void setWhitelist(const QList<qint32> &l);
|
||||||
|
static void clearWhitelist();
|
||||||
|
|
||||||
|
//static QString userDefinedThreadName() { return userDefinedThreadName_; }
|
||||||
|
//static void setUserDefinedThreadName(const QString &val) { userDefinedThreadName_ = val; }
|
||||||
|
static const char *keptThreadName() { return keptThreadName_; }
|
||||||
|
|
||||||
|
static void setKeptThreadName(const QString &v)
|
||||||
|
{
|
||||||
|
if (v.size() < ITH_THREAD_NAME_CAPACITY)
|
||||||
|
::strcpy(keptThreadName_, v.toAscii());
|
||||||
|
else
|
||||||
|
setKeptThreadName(v.left(ITH_THREAD_NAME_CAPACITY - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static bool whitelistContains(qint32 signature);
|
||||||
|
|
||||||
|
// - Callbacks -
|
||||||
|
//static ulong processAttach(ulong pid);
|
||||||
|
//static ulong processDetach(ulong pid);
|
||||||
|
//static ulong processNewHook(ulong pid);
|
||||||
|
|
||||||
|
static ulong threadCreate(_In_ TextThread *t);
|
||||||
|
static ulong threadRemove(_In_ TextThread *t);
|
||||||
|
static ulong threadOutput(_In_ TextThread *t, _In_ uchar *data, _In_ ulong dataLength, _In_ ulong bNewLine, _In_ void *pUserData, _In_ bool space);
|
||||||
|
//static ulong threadFilter(_In_ TextThread *t, _Out_ uchar *data, _In_ ulong dataLength, _In_ ulong bNewLine, _In_ void *pUserData);
|
||||||
|
//static ulong threadReset(TextThread *t);
|
||||||
|
//static void consoleOutput(const char *text);
|
||||||
|
//static void consoleOutputW(const wchar_t *text);
|
||||||
|
|
||||||
|
// - Linked threasds -
|
||||||
|
private:
|
||||||
|
//static TextThreadDelegate *findLinkedDelegate(TextThreadDelegate *d);
|
||||||
|
static void updateLinkedDelegate(TextThreadDelegate *d);
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
275
vnr/texthook/ith_p.cc
Normal file
275
vnr/texthook/ith_p.cc
Normal file
@ -0,0 +1,275 @@
|
|||||||
|
// ith_p.cc
|
||||||
|
// 10/15/2011 jichi
|
||||||
|
|
||||||
|
#include "texthook/ith_p.h"
|
||||||
|
#include "vnrhook/include/const.h"
|
||||||
|
#include "vnrhook/include/types.h"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#define DEBUG "ith_p.cc"
|
||||||
|
#include "sakurakit/skdebug.h"
|
||||||
|
|
||||||
|
// HookParam copied from ITH/common.h:
|
||||||
|
// struct HookParam // size = 40 (0x24)
|
||||||
|
// {
|
||||||
|
// typedef void (*DataFun)(DWORD, HookParam*, DWORD*, DWORD*, DWORD*);
|
||||||
|
//
|
||||||
|
// DWORD addr; // 4
|
||||||
|
// DWORD off, // 8
|
||||||
|
// ind, // 12
|
||||||
|
// split, // 16
|
||||||
|
// split_ind; // 20
|
||||||
|
// DWORD module, // 24
|
||||||
|
// function; // 28
|
||||||
|
// DataFun text_fun; // 32, jichi: is this the same in x86 and x86_64?
|
||||||
|
// DWORD type; // 36
|
||||||
|
// WORD length_offset; // 38
|
||||||
|
// BYTE hook_len, // 39
|
||||||
|
// recover_len; // 40
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
|
// - Implementation Details -
|
||||||
|
|
||||||
|
namespace { namespace detail { // unnamed
|
||||||
|
|
||||||
|
// ITH ORIGINAL CODE BEGIN
|
||||||
|
|
||||||
|
// See: ITH/ITH.h
|
||||||
|
// Revision: 133
|
||||||
|
inline DWORD Hash(_In_ LPWSTR module, int length = -1)
|
||||||
|
{
|
||||||
|
bool flag = length == -1;
|
||||||
|
DWORD hash = 0;
|
||||||
|
for (; *module && (flag || length--); module++)
|
||||||
|
hash = _rotr(hash,7) + *module; //hash=((hash>>7)|(hash<<25))+(*module);
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: ITH/command.cpp
|
||||||
|
// Revision: 133
|
||||||
|
//
|
||||||
|
// jichi note: str[0xF] will be modified and restored.
|
||||||
|
// So, the buffer of str must be larger than 0xF.
|
||||||
|
int Convert(_In_ LPWSTR str, _Out_ DWORD *num, _In_ LPWSTR delim)
|
||||||
|
{
|
||||||
|
if (!num)
|
||||||
|
return -1;
|
||||||
|
WCHAR t = *str,
|
||||||
|
tc = *(str + 0xF);
|
||||||
|
WCHAR temp[0x10] = {};
|
||||||
|
LPWSTR it = temp,
|
||||||
|
istr = str,
|
||||||
|
id = temp;
|
||||||
|
if (delim) {
|
||||||
|
id = wcschr(delim, t);
|
||||||
|
str[0xF] = delim[0]; // reset str[0xF] in case of out-of-bound iteration
|
||||||
|
}
|
||||||
|
else
|
||||||
|
str[0xF] = 0; // reset str[0xF] in case of out-of-bound iteration
|
||||||
|
while (!id && t) {
|
||||||
|
*it = t;
|
||||||
|
it++; istr++;
|
||||||
|
t = *istr;
|
||||||
|
if (delim)
|
||||||
|
id = wcschr(delim, t);
|
||||||
|
}
|
||||||
|
swscanf(temp, L"%x", num);
|
||||||
|
str[0xF] = tc; // restore the str[0xF]
|
||||||
|
if (!id || istr - str == 0xF)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (!t)
|
||||||
|
return istr - str; // >= 0
|
||||||
|
else
|
||||||
|
return id - delim; // >= 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// See: ITH/command.cpp
|
||||||
|
// Revision: 133
|
||||||
|
//
|
||||||
|
// jichi note: str[0xF] will be modified and restored.
|
||||||
|
// So, the buffer of cmd must be larger than 0xF*2 = 0x1F.
|
||||||
|
bool Parse(_In_ LPWSTR cmd, _Out_ HookParam &hp)
|
||||||
|
{
|
||||||
|
::memset(&hp, 0, sizeof(hp));
|
||||||
|
|
||||||
|
int t;
|
||||||
|
bool accept = false;
|
||||||
|
DWORD *data = &hp.offset; //
|
||||||
|
LPWSTR offset = cmd + 1;
|
||||||
|
LPWSTR delim_str = L":*@!";
|
||||||
|
LPWSTR delim = delim_str;
|
||||||
|
if (*offset == L'n' || *offset == 'N') {
|
||||||
|
offset++;
|
||||||
|
hp.type |= NO_CONTEXT;
|
||||||
|
}
|
||||||
|
// jichi 4/25/2015: Add support for fixing hook
|
||||||
|
if (*offset == L'f' || *offset == 'F') {
|
||||||
|
offset++;
|
||||||
|
hp.type |= FIXING_SPLIT;
|
||||||
|
}
|
||||||
|
if (*offset == L'j' || *offset == 'J') { // 11/22/2015: J stands for Japanese only
|
||||||
|
offset++;
|
||||||
|
hp.type |= NO_ASCII;
|
||||||
|
}
|
||||||
|
while (!accept) {
|
||||||
|
t = Convert(offset, data, delim);
|
||||||
|
if (t < 0)
|
||||||
|
return false; //ConsoleOutput(L"Syntax error.");
|
||||||
|
offset = ::wcschr(offset , delim[t]);
|
||||||
|
if (offset)
|
||||||
|
offset++; // skip the current delim
|
||||||
|
else //goto _error;
|
||||||
|
return false; //ConsoleOutput(L"Syntax error.");
|
||||||
|
switch (delim[t]) {
|
||||||
|
case L':':
|
||||||
|
data = &hp.split;
|
||||||
|
delim = delim_str + 1;
|
||||||
|
hp.type |= USING_SPLIT;
|
||||||
|
break;
|
||||||
|
case L'*':
|
||||||
|
if (hp.split) {
|
||||||
|
data = &hp.split_index;
|
||||||
|
delim = delim_str + 2;
|
||||||
|
hp.type |= SPLIT_INDIRECT;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
hp.type |= DATA_INDIRECT;
|
||||||
|
data = &hp.index;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case L'@':
|
||||||
|
accept = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
t = Convert(offset, &hp.address, delim_str);
|
||||||
|
if (t < 0)
|
||||||
|
return false;
|
||||||
|
if (hp.offset & 0x80000000)
|
||||||
|
hp.offset -= 4;
|
||||||
|
if (hp.split & 0x80000000)
|
||||||
|
hp.split -= 4;
|
||||||
|
LPWSTR temp = offset;
|
||||||
|
offset = ::wcschr(offset, L':');
|
||||||
|
if (offset) {
|
||||||
|
hp.type |= MODULE_OFFSET;
|
||||||
|
offset++;
|
||||||
|
delim = ::wcschr(offset, L':');
|
||||||
|
|
||||||
|
if (delim) {
|
||||||
|
*delim = 0;
|
||||||
|
delim++;
|
||||||
|
_wcslwr(offset);
|
||||||
|
hp.function = Hash(delim);
|
||||||
|
hp.module = Hash(offset, delim - offset - 1);
|
||||||
|
hp.type |= FUNCTION_OFFSET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
hp.module = Hash(_wcslwr(offset));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
offset = ::wcschr(temp, L'!');
|
||||||
|
if (offset) {
|
||||||
|
hp.type |= MODULE_OFFSET;
|
||||||
|
swscanf(offset + 1, L"%x", &hp.module);
|
||||||
|
offset = ::wcschr(offset + 1, L'!');
|
||||||
|
if (offset) {
|
||||||
|
hp.type |= FUNCTION_OFFSET;
|
||||||
|
swscanf(offset + 1, L"%x", &hp.function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (*cmd) {
|
||||||
|
case L's':
|
||||||
|
case L'S':
|
||||||
|
hp.type |= USING_STRING;
|
||||||
|
break;
|
||||||
|
case L'e':
|
||||||
|
case L'E':
|
||||||
|
hp.type |= STRING_LAST_CHAR;
|
||||||
|
case L'a':
|
||||||
|
case L'A':
|
||||||
|
hp.type |= BIG_ENDIAN;
|
||||||
|
hp.length_offset = 1;
|
||||||
|
break;
|
||||||
|
case L'b':
|
||||||
|
case L'B':
|
||||||
|
hp.length_offset = 1;
|
||||||
|
break;
|
||||||
|
// jichi 12/7/2014: Disabled
|
||||||
|
//case L'h':
|
||||||
|
//case L'H':
|
||||||
|
// hp.type |= PRINT_DWORD;
|
||||||
|
case L'q':
|
||||||
|
case L'Q':
|
||||||
|
hp.type |= USING_STRING | USING_UNICODE;
|
||||||
|
break;
|
||||||
|
case L'l':
|
||||||
|
case L'L':
|
||||||
|
hp.type |= STRING_LAST_CHAR;
|
||||||
|
case L'w':
|
||||||
|
case L'W':
|
||||||
|
hp.type |= USING_UNICODE;
|
||||||
|
hp.length_offset = 1;
|
||||||
|
break;
|
||||||
|
default: ;
|
||||||
|
}
|
||||||
|
//ConsoleOutput(L"Try to insert additional hook.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITH ORIGINAL CODE END
|
||||||
|
|
||||||
|
}} // unnamed detail
|
||||||
|
|
||||||
|
// - ITH API -
|
||||||
|
|
||||||
|
// Sample code: L"/HS-4:-14@4383C0" (WHITE ALBUM 2)
|
||||||
|
bool Ith::parseHookCode(const QString &code, HookParam *hp, bool verbose)
|
||||||
|
{
|
||||||
|
#define HCODE_PREFIX "/H"
|
||||||
|
enum { HCODE_PREFIX_LEN = sizeof(HCODE_PREFIX) -1 }; // 2
|
||||||
|
if (!hp || !code.startsWith(HCODE_PREFIX))
|
||||||
|
return false;
|
||||||
|
if (verbose)
|
||||||
|
DOUT("enter: code =" << code);
|
||||||
|
else
|
||||||
|
DOUT("enter");
|
||||||
|
|
||||||
|
size_t bufsize = qMax(0xFF, code.size() + 1); // in case detail::Convert modify the buffer
|
||||||
|
auto buf = new wchar_t[bufsize];
|
||||||
|
code.toWCharArray(buf);
|
||||||
|
buf[code.size()] = 0;
|
||||||
|
|
||||||
|
bool ret = detail::Parse(buf + HCODE_PREFIX_LEN, *hp);
|
||||||
|
delete[] buf;
|
||||||
|
#ifdef DEBUG
|
||||||
|
if (ret && verbose)
|
||||||
|
qDebug()
|
||||||
|
<< "addr:" << hp->address
|
||||||
|
<< ", text_fun:" << hp->text_fun
|
||||||
|
<< ", function:"<< hp->function
|
||||||
|
<< ", hook_len:" << hp->hook_len
|
||||||
|
<< ", ind:" << hp->index
|
||||||
|
<< ", length_offset:" << hp->length_offset
|
||||||
|
<< ", module:" << hp->module
|
||||||
|
<< ", off:" <<hp->offset
|
||||||
|
<< ", recover_len:" << hp->recover_len
|
||||||
|
<< ", split:" << hp->split
|
||||||
|
<< ", split_ind:" << hp->split_index
|
||||||
|
<< ", type:" << hp->type;
|
||||||
|
#endif // DEBUG
|
||||||
|
DOUT("leave: ret =" << ret);
|
||||||
|
return ret;
|
||||||
|
#undef HOOK_CODE_PREFIX
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ith::verifyHookCode(const QString &code)
|
||||||
|
{
|
||||||
|
HookParam hp = {};
|
||||||
|
return parseHookCode(code, &hp);
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
20
vnr/texthook/ith_p.h
Normal file
20
vnr/texthook/ith_p.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ith_p.h
|
||||||
|
// 10/15/2011 jichi
|
||||||
|
// Internal header.
|
||||||
|
// Wrapper of functions from ITH.
|
||||||
|
|
||||||
|
#include <QtCore/QString>
|
||||||
|
|
||||||
|
struct HookParam; // opaque, declared in ITH/common.h
|
||||||
|
|
||||||
|
namespace Ith {
|
||||||
|
|
||||||
|
/// Parse hook code, and save the result to hook param if succeeded.
|
||||||
|
bool parseHookCode(_In_ const QString &code, _Out_ HookParam *hp, bool verbose = true);
|
||||||
|
bool verifyHookCode(_In_ const QString &code);
|
||||||
|
|
||||||
|
} // namespace Ith
|
||||||
|
|
||||||
|
// EOF
|
414
vnr/texthook/texthook.cc
Normal file
414
vnr/texthook/texthook.cc
Normal file
@ -0,0 +1,414 @@
|
|||||||
|
// texthook.cc
|
||||||
|
// 10/14/2011 jichi
|
||||||
|
|
||||||
|
#include "texthook/texthook.h"
|
||||||
|
#include "texthook/texthook_p.h"
|
||||||
|
#include "texthook/ihf_p.h"
|
||||||
|
#include "texthook/textthread_p.h"
|
||||||
|
#include "texthook/winapi_p.h"
|
||||||
|
#include <QtCore>
|
||||||
|
|
||||||
|
//#define DEBUG "texthook.cc"
|
||||||
|
#include "sakurakit/skdebug.h"
|
||||||
|
|
||||||
|
//#include <ITH/IHF_SYS.h>
|
||||||
|
//namespace { int _ = IthInitSystemService(); }
|
||||||
|
|
||||||
|
/** Private class */
|
||||||
|
|
||||||
|
TextHookPrivate *TextHookPrivate::instance_;
|
||||||
|
|
||||||
|
/** Public class */
|
||||||
|
|
||||||
|
// - Construction -
|
||||||
|
|
||||||
|
//TextHook *TextHook::g_;
|
||||||
|
//TextHook *TextHook::globalInstance() { static Self g; return &g; }
|
||||||
|
|
||||||
|
//TextHook::TextHook(QObject *parent)
|
||||||
|
// : Base(parent), d_(new D)
|
||||||
|
//{}
|
||||||
|
|
||||||
|
TextHook::TextHook(QObject *parent)
|
||||||
|
: Base(parent), d_(new D(this))
|
||||||
|
{
|
||||||
|
Ihf::init();
|
||||||
|
//Ihf::setUserDefinedThreadName(d_->source);
|
||||||
|
DOUT("pass");
|
||||||
|
}
|
||||||
|
|
||||||
|
TextHook::~TextHook()
|
||||||
|
{
|
||||||
|
DOUT("enter");
|
||||||
|
if (isActive())
|
||||||
|
stop();
|
||||||
|
delete d_;
|
||||||
|
|
||||||
|
Ihf::destroy();
|
||||||
|
DOUT("leave");
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Properties -
|
||||||
|
|
||||||
|
int TextHook::dataCapacity() const
|
||||||
|
{ return TextThreadDelegate::capacity(); }
|
||||||
|
|
||||||
|
void TextHook::setDataCapacity(int value)
|
||||||
|
{ TextThreadDelegate::setCapacity(value); }
|
||||||
|
|
||||||
|
bool TextHook::removesRepeat() const
|
||||||
|
{ return TextThreadDelegate::removesRepeat(); }
|
||||||
|
|
||||||
|
void TextHook::setRemovesRepeat(bool value)
|
||||||
|
{ TextThreadDelegate::setRemovesRepeat(value); }
|
||||||
|
|
||||||
|
bool TextHook::keepsSpace() const
|
||||||
|
{ return TextThreadDelegate::keepsSpace(); }
|
||||||
|
|
||||||
|
void TextHook::setKeepsSpace(bool value)
|
||||||
|
{ TextThreadDelegate::setKeepsSpace(value); }
|
||||||
|
|
||||||
|
bool TextHook::wideCharacter() const
|
||||||
|
{ return TextThreadDelegate::wideCharacter(); }
|
||||||
|
|
||||||
|
void TextHook::setWideCharacter(bool value)
|
||||||
|
{ TextThreadDelegate::setWideCharacter(value); }
|
||||||
|
|
||||||
|
// see: ITH/common.h
|
||||||
|
int TextHook::capacity() const
|
||||||
|
{ return 0x20; }
|
||||||
|
|
||||||
|
QString TextHook::defaultHookName() const
|
||||||
|
{ return d_->source; }
|
||||||
|
|
||||||
|
void TextHook::setDefaultHookName(const QString &name)
|
||||||
|
{
|
||||||
|
d_->source = name;
|
||||||
|
//Ihf::setUserDefinedThreadName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextHook::isEnabled() const
|
||||||
|
{ return d_->enabled; }
|
||||||
|
|
||||||
|
void TextHook::setEnabled(bool t)
|
||||||
|
{
|
||||||
|
d_->enabled = t;
|
||||||
|
Ihf::setEnabled(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextHook::isActive() const
|
||||||
|
{ return Ihf::isLoaded(); }
|
||||||
|
|
||||||
|
void TextHook::start()
|
||||||
|
{ Ihf::load(); }
|
||||||
|
|
||||||
|
void TextHook::stop()
|
||||||
|
{
|
||||||
|
if (!isEmpty())
|
||||||
|
clear();
|
||||||
|
Ihf::unload();
|
||||||
|
}
|
||||||
|
|
||||||
|
WId TextHook::parentWinId() const { return Ihf::parentWindow(); }
|
||||||
|
void TextHook::setParentWinId(WId hwnd) { Ihf::setParentWindow(hwnd); }
|
||||||
|
|
||||||
|
int TextHook::interval() const
|
||||||
|
{ return Ihf::messageInterval(); }
|
||||||
|
|
||||||
|
void TextHook::setInterval(int msecs)
|
||||||
|
{ Ihf::setMessageInterval(msecs); }
|
||||||
|
|
||||||
|
// - Injection -
|
||||||
|
|
||||||
|
void TextHook::clear()
|
||||||
|
{
|
||||||
|
DOUT("enter");
|
||||||
|
foreach (ulong pid, d_->pids)
|
||||||
|
detachProcess(pid);
|
||||||
|
if (!d_->hooks.isEmpty())
|
||||||
|
d_->hooks.clear();
|
||||||
|
clearThreadWhitelist();
|
||||||
|
DOUT("leave");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextHook::containsProcess(ulong pid) const { return d_->pids.contains(pid); }
|
||||||
|
bool TextHook::isEmpty() const { return d_->pids.isEmpty(); }
|
||||||
|
|
||||||
|
//QList<ulong> TextHook::attachedProcesses(bool checkActive) const
|
||||||
|
//{
|
||||||
|
// if (isEmpty() || !checkActive)
|
||||||
|
// return d_->pids;
|
||||||
|
//
|
||||||
|
// QList<ulong> ret;
|
||||||
|
// foreach (ulong pid, d_->pids)
|
||||||
|
// if (winapi::IsProcessActiveWithId(pid))
|
||||||
|
// ret.append(pid);
|
||||||
|
// return ret;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//ulong TextHook::currentProccess() const
|
||||||
|
//{ return anyAttachedProcess(true); } // check active = true
|
||||||
|
|
||||||
|
//ulong TextHook::anyAttachedProcess(bool checkActive) const
|
||||||
|
//{
|
||||||
|
// if (isEmpty())
|
||||||
|
// return 0;
|
||||||
|
// if (!checkActive)
|
||||||
|
// return d_->pids.first();
|
||||||
|
//
|
||||||
|
// foreach (ulong pid, d_->pids)
|
||||||
|
// if (winapi::IsProcessActiveWithId(pid))
|
||||||
|
// return pid;
|
||||||
|
// return 0;
|
||||||
|
//}
|
||||||
|
|
||||||
|
//bool TextHook::attachOneProcess(ulong pid, bool checkActive)
|
||||||
|
//{
|
||||||
|
// DOUT("enter: pid =" << pid);
|
||||||
|
// DOUT("isAttached =" << containsProcess(pid));
|
||||||
|
// if (!isActive())
|
||||||
|
// start();
|
||||||
|
//
|
||||||
|
// if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
|
||||||
|
// DOUT("leave: ret = false, isActive = false");
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// if (containsProcess(pid)) {
|
||||||
|
// DOUT("leave: pid already attached");
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// detachAllProcesses();
|
||||||
|
//
|
||||||
|
// bool ret = Ihf::attachProcess(pid);
|
||||||
|
// if (ret && !containsProcess(pid)) {
|
||||||
|
// d_->pids.removeAll(pid);
|
||||||
|
// d_->pids.append(pid);
|
||||||
|
// emit processAttached(pid);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// DOUT("leave: ret =" << ret);
|
||||||
|
// return ret;
|
||||||
|
//}
|
||||||
|
|
||||||
|
bool TextHook::attachProcess(ulong pid, bool checkActive)
|
||||||
|
{
|
||||||
|
DOUT("enter: pid =" << pid << ", isAttached =" << containsProcess(pid));
|
||||||
|
if (!isActive())
|
||||||
|
start();
|
||||||
|
Q_ASSERT(isActive());
|
||||||
|
DOUT("isActive =" << isActive());
|
||||||
|
|
||||||
|
if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
|
||||||
|
DOUT("leave: ret = false, isActive = false");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = Ihf::attachProcess(pid);
|
||||||
|
if (ret) {
|
||||||
|
d_->pids.insert(pid);
|
||||||
|
emit processAttached(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
DOUT("leave: ret =" << ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextHook::detachProcess(ulong pid, bool checkActive)
|
||||||
|
{
|
||||||
|
DOUT("enter: pid =" << pid << ", isAttached =" << containsProcess(pid));
|
||||||
|
Q_ASSERT(isActive());
|
||||||
|
|
||||||
|
auto it = d_->pids.find(pid);
|
||||||
|
if (it == d_->pids.end()) {
|
||||||
|
DOUT("leave: ret = false, not attached");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
d_->pids.erase(it);
|
||||||
|
d_->hooks.remove(pid);
|
||||||
|
|
||||||
|
if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
|
||||||
|
emit processDetached(pid);
|
||||||
|
DOUT("leave: ret = false, isActive = false");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ret = Ihf::detachProcess(pid);
|
||||||
|
//try {
|
||||||
|
// ret = Ihf::detachProcess(pid);
|
||||||
|
//} catch (...) {
|
||||||
|
// DOUT("warning: detach exception");
|
||||||
|
//}
|
||||||
|
|
||||||
|
emit processDetached(pid);
|
||||||
|
DOUT("leave: ret =" << ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextHook::hijackProcess(ulong pid)
|
||||||
|
{
|
||||||
|
DOUT("enter: pid =" << pid);
|
||||||
|
Q_ASSERT(isActive());
|
||||||
|
|
||||||
|
if (!containsProcess(pid)) {
|
||||||
|
DOUT("leave: aborted, process not attached");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7/12/2015: Function disabled
|
||||||
|
return true;
|
||||||
|
|
||||||
|
//bool ret = Ihf::hijackProcess(pid);
|
||||||
|
//DOUT("leave: ret =" << ret);
|
||||||
|
//return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
//void TextHook::detachAllProcesses()
|
||||||
|
//{
|
||||||
|
// DOUT("enter");
|
||||||
|
// foreach (ulong pid, d_->pids)
|
||||||
|
// detachProcess(pid);
|
||||||
|
// if (!d_->hooks.isEmpty())
|
||||||
|
// d_->hooks.clear();
|
||||||
|
// DOUT("leave");
|
||||||
|
//}
|
||||||
|
|
||||||
|
// - Hook -
|
||||||
|
|
||||||
|
//bool TextHook::containsHook(ulong pid) const
|
||||||
|
//{ return d_->hooks.contains(pid); }
|
||||||
|
//
|
||||||
|
//bool TextHook::containsHook(ulong pid, const QString &code) const
|
||||||
|
//{ return processHook(pid) == code; }
|
||||||
|
|
||||||
|
bool TextHook::addHookCode(ulong pid, const QString &code, const QString &name, bool verbose)
|
||||||
|
{
|
||||||
|
DOUT("enter: pid =" << pid << ", code =" << code);
|
||||||
|
if (isEmpty() || !containsProcess(pid)) {
|
||||||
|
DOUT("leave: failed, process not attached");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (d_->hooks.contains(pid)) {
|
||||||
|
DOUT("leave: failed, hook already exists");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
bool ok = Ihf::addHook(pid, code,
|
||||||
|
name.isEmpty() ? defaultHookName() : name,
|
||||||
|
verbose);
|
||||||
|
if (ok)
|
||||||
|
d_->hooks[pid] = code;
|
||||||
|
DOUT("leave: ret =" << ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextHook::verifyHookCode(const QString &code) { return Ihf::verifyHookCode(code); }
|
||||||
|
|
||||||
|
bool TextHook::removeHookCode(ulong pid)
|
||||||
|
{
|
||||||
|
DOUT("enter");
|
||||||
|
auto p = d_->hooks.find(pid);
|
||||||
|
if (p == d_->hooks.end()) {
|
||||||
|
DOUT("leave: not hooked");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//DOUT("remove existing hook, THIS SHOULD NOT HAPPEN");
|
||||||
|
bool ok = Ihf::removeHook(pid, p.value());
|
||||||
|
d_->hooks.erase(p);
|
||||||
|
DOUT("leave: ret =" << ok);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
|
//QString TextHook::processHook(ulong pid) const
|
||||||
|
//{
|
||||||
|
// auto p = d_->hooks.find(pid);
|
||||||
|
// return p == d_->hooks.end() ? QString() : p.value();
|
||||||
|
//}
|
||||||
|
|
||||||
|
bool TextHook::isThreadWhitelistEnabled() const { return Ihf::isWhitelistEnabled(); }
|
||||||
|
|
||||||
|
void TextHook::setThreadWhitelistEnabled(bool t) { Ihf::setWhitelistEnabled(t); }
|
||||||
|
|
||||||
|
QList<qint32> TextHook::threadWhitelist() const { return Ihf::whitelist(); }
|
||||||
|
|
||||||
|
void TextHook::setThreadWhitelist(const QList<qint32> &sigs) { Ihf::setWhitelist(sigs); }
|
||||||
|
|
||||||
|
void TextHook::clearThreadWhitelist() { Ihf::clearWhitelist(); }
|
||||||
|
|
||||||
|
QString TextHook::keptThreadName() const { return Ihf::keptThreadName(); }
|
||||||
|
|
||||||
|
void TextHook::setKeptThreadName(const QString &v) { Ihf::setKeptThreadName(v); }
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
|
||||||
|
/*
|
||||||
|
QString
|
||||||
|
TextHook::guessEncodingForFile(const QString &fileName)
|
||||||
|
{
|
||||||
|
static QHash<QString, QString> db;
|
||||||
|
if (db.isEmpty()) {
|
||||||
|
db["malie.exe"] = "UTF-16";
|
||||||
|
}
|
||||||
|
auto p = db.find(fileName);
|
||||||
|
return p == db.end() ? QString() : p.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Helpers -
|
||||||
|
|
||||||
|
bool
|
||||||
|
TextHook::isStandardHookName(const QString &name) const
|
||||||
|
{
|
||||||
|
static QSet<uint> hashes;
|
||||||
|
if (hashes.isEmpty()) {
|
||||||
|
#define ADD(_text) hashes.insert(qHash(QString(_text)))
|
||||||
|
ADD("ConsoleOutput");
|
||||||
|
ADD("GetTextExtentPoint32A");
|
||||||
|
ADD("GetGlyphOutlineA");
|
||||||
|
ADD("ExtTextOutA");
|
||||||
|
ADD("TextOutA");
|
||||||
|
ADD("GetCharABCWidthsA");
|
||||||
|
ADD("DrawTextA");
|
||||||
|
ADD("DrawTextExA");
|
||||||
|
ADD("GetTextExtentPoint32W");
|
||||||
|
ADD("GetGlyphOutlineW");
|
||||||
|
ADD("ExtTextOutW");
|
||||||
|
ADD("TextOutW");
|
||||||
|
ADD("GetCharABCWidthsW");
|
||||||
|
ADD("DrawTextW");
|
||||||
|
ADD("DrawTextExW");
|
||||||
|
#undef ADD
|
||||||
|
}
|
||||||
|
uint h = qHash(name);
|
||||||
|
return hashes.contains(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TextHook::isKnownHookForProcess(const QString &hook, const QString &proc) const
|
||||||
|
{
|
||||||
|
// TODO: update database on line periodically
|
||||||
|
qDebug() << "qth::isKnownHookForProcess: hook =" << hook << ", proc =" << proc;
|
||||||
|
|
||||||
|
static QSet<uint> hashes;
|
||||||
|
if (hashes.isEmpty()) {
|
||||||
|
#define ADD(_hook, _proc) hashes.insert(qHash(QString(_hook) + "\n" + _proc))
|
||||||
|
//ADD("Malie", "malie"); // light
|
||||||
|
ADD("GetGlyphOutlineA", "STEINSGATE");
|
||||||
|
ADD("StuffScriptEngine", "EVOLIMIT");
|
||||||
|
#undef ADD
|
||||||
|
}
|
||||||
|
uint h = qHash(hook + "\n" + proc);
|
||||||
|
return hashes.contains(h);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString
|
||||||
|
TextHook::hookNameById(ulong hookId) const
|
||||||
|
{
|
||||||
|
//return Ihf::getHookNameById(hookId);
|
||||||
|
// FIXME: supposed to be the engine name, unimplemented
|
||||||
|
Q_UNUSED(hookId)
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
101
vnr/texthook/texthook.h
Normal file
101
vnr/texthook/texthook.h
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// texthook.h
|
||||||
|
// 10/14/2011 jichi
|
||||||
|
|
||||||
|
#include "texthook_config.h"
|
||||||
|
#include "sakurakit/skglobal.h"
|
||||||
|
#include <QtCore/QByteArray>
|
||||||
|
#include <QtCore/QObject>
|
||||||
|
#include <QtCore/QList>
|
||||||
|
#include <QtCore/QString>
|
||||||
|
#include <QtGui/qwindowdefs.h> // for WId
|
||||||
|
|
||||||
|
class TextHookPrivate;
|
||||||
|
/// Singleton class. Only one instance is allowed.
|
||||||
|
class TEXTHOOK_EXPORT TextHook : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_DISABLE_COPY(TextHook)
|
||||||
|
SK_EXTEND_CLASS(TextHook, QObject)
|
||||||
|
SK_DECLARE_PRIVATE(TextHookPrivate)
|
||||||
|
|
||||||
|
// - Construction -
|
||||||
|
public:
|
||||||
|
explicit TextHook(QObject *parent = nullptr);
|
||||||
|
~TextHook();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void dataReceived(QByteArray raw, QByteArray rendered, qint32 signature, QString source);
|
||||||
|
void processAttached(qint64 pid);
|
||||||
|
void processDetached(qint64 pid);
|
||||||
|
|
||||||
|
// - Properties -
|
||||||
|
public:
|
||||||
|
/// Limited by ITH
|
||||||
|
int capacity() const;
|
||||||
|
|
||||||
|
bool isEnabled() const;
|
||||||
|
void setEnabled(bool t);
|
||||||
|
|
||||||
|
WId parentWinId() const; ///< Must be set to a valid window so that ::SetTimer works
|
||||||
|
void setParentWinId(WId hwnd);
|
||||||
|
|
||||||
|
int interval() const; ///< Time to differentiate sentences
|
||||||
|
void setInterval(int msecs);
|
||||||
|
|
||||||
|
int dataCapacity() const; ///< Maximum text length
|
||||||
|
void setDataCapacity(int value);
|
||||||
|
|
||||||
|
bool removesRepeat() const;
|
||||||
|
void setRemovesRepeat(bool value);
|
||||||
|
|
||||||
|
bool keepsSpace() const;
|
||||||
|
void setKeepsSpace(bool value);
|
||||||
|
|
||||||
|
bool wideCharacter() const;
|
||||||
|
void setWideCharacter(bool value);
|
||||||
|
|
||||||
|
QString defaultHookName() const; ///< The default one is "H-code"
|
||||||
|
void setDefaultHookName(const QString &name);
|
||||||
|
|
||||||
|
bool isActive() const;
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
// - Injection -
|
||||||
|
public:
|
||||||
|
//bool attachOneProcess(ulong pid, bool checkActive = false);
|
||||||
|
bool attachProcess(ulong pid, bool checkActive = false);
|
||||||
|
bool detachProcess(ulong pid, bool checkActive = false);
|
||||||
|
bool hijackProcess(ulong pid);
|
||||||
|
//void detachAllProcesses();
|
||||||
|
//QList<ulong> attachedProcesses(bool checkActive = false) const;
|
||||||
|
//ulong anyAttachedProcess(bool checkActive = false) const;
|
||||||
|
//ulong currentProccess() const;
|
||||||
|
|
||||||
|
bool containsProcess(ulong pid) const;
|
||||||
|
bool isEmpty() const; ///< Return true if at least one process is attached
|
||||||
|
|
||||||
|
bool addHookCode(ulong pid, const QString &code, const QString &name = QString(), bool verbose = true);
|
||||||
|
static bool verifyHookCode(const QString &code); ///< Return if hcode is valid
|
||||||
|
//bool containsHook(ulong pid) const;
|
||||||
|
//bool containsHook(ulong pid, const QString &code) const;
|
||||||
|
//QString processHook(ulong pid) const;
|
||||||
|
//QString currentHook() const { return processHook(currentProccess()); }
|
||||||
|
bool removeHookCode(ulong pid); ///< Assume atmost one hcode per process
|
||||||
|
|
||||||
|
// - Whitelist -
|
||||||
|
public:
|
||||||
|
bool isThreadWhitelistEnabled() const;
|
||||||
|
void setThreadWhitelistEnabled(bool t);
|
||||||
|
QList<qint32> threadWhitelist() const;
|
||||||
|
void setThreadWhitelist(const QList<qint32> &signatures);
|
||||||
|
void clearThreadWhitelist();
|
||||||
|
// Note: len(v) must be smaller than 0x200
|
||||||
|
void setKeptThreadName(const QString &v);
|
||||||
|
QString keptThreadName() const;
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
20
vnr/texthook/texthook.pri
Normal file
20
vnr/texthook/texthook.pri
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# texthook.pri
|
||||||
|
# 10/13/2011 jichi
|
||||||
|
|
||||||
|
DEFINES += WITH_LIB_TEXTHOOK
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
QT += core
|
||||||
|
LIBS += -ltexthook
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/texthook_config.h
|
||||||
|
|
||||||
|
# texthook.h should not be in HEADERS, or it will be processed by moc
|
||||||
|
OTHER_FILES += \
|
||||||
|
$$PWD/texthook.h \
|
||||||
|
$$PWD/texthook.pro
|
||||||
|
|
||||||
|
# EOF
|
61
vnr/texthook/texthook.pro
Normal file
61
vnr/texthook/texthook.pro
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# texthook.pro
|
||||||
|
# 10/13/2011 jichi
|
||||||
|
# Build ith texthook dll.
|
||||||
|
|
||||||
|
CONFIG += noqtgui dll #eha # eha will catch all exceptions, but does not work on Windows XP
|
||||||
|
include(../../../config.pri)
|
||||||
|
include(host/host.pri)
|
||||||
|
include($$LIBDIR/winmutex/winmutex.pri)
|
||||||
|
include($$LIBDIR/wintimer/wintimer.pri)
|
||||||
|
include($$LIBDIR/windbg/windbg.pri)
|
||||||
|
#include($$LIBDIR/winmaker/winmaker.pri)
|
||||||
|
|
||||||
|
# TODO: Get rid of dependence on ITHSYS and NT APIs
|
||||||
|
include($$PLUGINDIR/vnrhook/vnrhook.pri)
|
||||||
|
include($$PLUGINDIR/ithsys/ithsys.pri)
|
||||||
|
LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
|
||||||
|
|
||||||
|
DEFINES += ITH_HAS_CRT # Use native CRT
|
||||||
|
|
||||||
|
# TODO: Get rid of dependence on msvc's swprintf
|
||||||
|
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
|
||||||
|
## Libraries
|
||||||
|
|
||||||
|
QT += core
|
||||||
|
QT -= gui
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
TEMPLATE = lib
|
||||||
|
TARGET = texthook
|
||||||
|
|
||||||
|
#CONFIG += staticlib
|
||||||
|
DEFINES += TEXTHOOK_BUILD_LIB
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
growl.h \
|
||||||
|
ihf_p.h \
|
||||||
|
ith_p.h \
|
||||||
|
texthook_config.h \
|
||||||
|
texthook.h \
|
||||||
|
texthook_p.h \
|
||||||
|
textthread_p.h \
|
||||||
|
winapi_p.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
ihf_p.cc \
|
||||||
|
ith_p.cc \
|
||||||
|
texthook.cc \
|
||||||
|
textthread_p.cc \
|
||||||
|
winapi_p.cc
|
||||||
|
|
||||||
|
#!wince*: LIBS += -lshell32
|
||||||
|
#RC_FILE += texthook.rc
|
||||||
|
|
||||||
|
OTHER_FILES += \
|
||||||
|
texthook.pri \
|
||||||
|
texthook_static.pri \
|
||||||
|
texthook.rc
|
||||||
|
|
||||||
|
# EOF
|
38
vnr/texthook/texthook.rc
Normal file
38
vnr/texthook/texthook.rc
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* texthook.rc
|
||||||
|
* 10/20/2011 jichi
|
||||||
|
*/
|
||||||
|
#if defined(UNDER_CE)
|
||||||
|
# include <winbase.h>
|
||||||
|
#else
|
||||||
|
# include <winver.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
VS_VERSION_INFO VERSIONINFO
|
||||||
|
FILEVERSION 1,0,0,0
|
||||||
|
PRODUCTVERSION 1,0,0,0
|
||||||
|
FILEFLAGSMASK 0x3fL
|
||||||
|
#ifdef _DEBUG
|
||||||
|
FILEFLAGS VS_FF_DEBUG
|
||||||
|
#else
|
||||||
|
FILEFLAGS 0x0L
|
||||||
|
#endif
|
||||||
|
FILEOS VOS__WINDOWS32
|
||||||
|
FILETYPE VFT_DLL
|
||||||
|
FILESUBTYPE 0x0L
|
||||||
|
BEGIN
|
||||||
|
BLOCK "StringFileInfo"
|
||||||
|
BEGIN
|
||||||
|
BLOCK "040904B0"
|
||||||
|
BEGIN
|
||||||
|
VALUE "CompanyName", "Sakuradite\0"
|
||||||
|
VALUE "FileDescription", "Text Hook\0"
|
||||||
|
VALUE "FileVersion", "1.0.0.0\0"
|
||||||
|
VALUE "LegalCopyright", "Copyright (C) 2012.\0"
|
||||||
|
VALUE "OriginalFilename", "texthook.dll\0"
|
||||||
|
VALUE "ProductName", "texthook\0"
|
||||||
|
END
|
||||||
|
END
|
||||||
|
END
|
||||||
|
|
||||||
|
/* End of Version info */
|
20
vnr/texthook/texthook_config.h
Normal file
20
vnr/texthook/texthook_config.h
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// texthook_config.h
|
||||||
|
// 10/20/2011 jichi
|
||||||
|
|
||||||
|
//#define TEXTHOOK_EXPORT
|
||||||
|
|
||||||
|
#ifndef TEXTHOOK_EXPORT
|
||||||
|
# ifdef TEXTHOOK_STATIC_LIB
|
||||||
|
# define TEXTHOOK_EXPORT
|
||||||
|
# elif defined(TEXTHOOK_BUILD_LIB)
|
||||||
|
# define TEXTHOOK_EXPORT Q_DECL_EXPORT
|
||||||
|
# else
|
||||||
|
# define TEXTHOOK_EXPORT Q_DECL_IMPORT
|
||||||
|
# endif
|
||||||
|
#endif // TEXTHOOK_EXPORT
|
||||||
|
|
||||||
|
#define TEXTHOOK_DEFAULT_NAME "H-code"
|
||||||
|
|
||||||
|
// EOF
|
39
vnr/texthook/texthook_p.h
Normal file
39
vnr/texthook/texthook_p.h
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// texthook_p.h
|
||||||
|
// 10/14/2011 jichi
|
||||||
|
// Internal header.
|
||||||
|
// Defines TextHook private data.
|
||||||
|
|
||||||
|
#include "texthook/texthook.h"
|
||||||
|
#include <QtCore/QHash>
|
||||||
|
#include <QtCore/QSet>
|
||||||
|
|
||||||
|
// - Private -
|
||||||
|
|
||||||
|
class TextHookPrivate
|
||||||
|
{
|
||||||
|
SK_CLASS(TextHookPrivate)
|
||||||
|
SK_DECLARE_PUBLIC(TextHook)
|
||||||
|
|
||||||
|
static Self *instance_; // global instance
|
||||||
|
|
||||||
|
bool enabled;
|
||||||
|
QString source;
|
||||||
|
QSet<ulong> pids;
|
||||||
|
QHash<ulong, QString> hooks; // ITH hook code, indexed by pid
|
||||||
|
|
||||||
|
explicit TextHookPrivate(Q *q)
|
||||||
|
: q_(q), enabled(true), source(TEXTHOOK_DEFAULT_NAME) { instance_ = this; }
|
||||||
|
|
||||||
|
~TextHookPrivate() { instance_ = nullptr; }
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void sendData(const QByteArray &rawData, const QByteArray &renderedData, qint32 signature, const QString &name)
|
||||||
|
{
|
||||||
|
if (instance_ && instance_->q_->isEnabled())
|
||||||
|
emit instance_->q_->dataReceived(rawData, renderedData, signature, name);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
73
vnr/texthook/texthook_static.pri
Normal file
73
vnr/texthook/texthook_static.pri
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
# texthook_static.pri
|
||||||
|
# 10/13/2011 jichi
|
||||||
|
|
||||||
|
include(../../../config.pri)
|
||||||
|
include($$LIBDIR/wintimer/wintimer.pri)
|
||||||
|
|
||||||
|
## Libraries
|
||||||
|
|
||||||
|
#DEFINES += _ITH_DEBUG_MEMORY
|
||||||
|
|
||||||
|
QT += core
|
||||||
|
|
||||||
|
INCLUDEPATH += $$PWD
|
||||||
|
DEPENDPATH += $$PWD
|
||||||
|
|
||||||
|
#WDK_HOME = c:/winddk
|
||||||
|
#LIBS += -L$$WDK_HOME/lib
|
||||||
|
# override folder must come before winddk/inc/api
|
||||||
|
#INCLUDEPATH += $$PWD/override/wdk/vc10
|
||||||
|
#INCLUDEPATH += $$WDK_HOME/include/api
|
||||||
|
#INCLUDEPATH += $$WDK_HOME/include/crt
|
||||||
|
#INCLUDEPATH += $$WDK_HOME/include/ddk
|
||||||
|
|
||||||
|
#ITH_HOME = c:/dev/ith
|
||||||
|
#INCLUDEPATH += $$ITH_HOME/include
|
||||||
|
#LIBS += -L$$ITH_HOME/lib
|
||||||
|
#LIBS += -lITH_DLL #-lITH_SYS
|
||||||
|
|
||||||
|
#INCLUDEPATH += $$ITH_HOME/include
|
||||||
|
#LIBS += -L$$ITH_HOME/lib -lihf
|
||||||
|
|
||||||
|
# Tell IHF not to override new operators, see: ith/mem.h
|
||||||
|
DEFINES += DEFAULT_MM
|
||||||
|
|
||||||
|
#LIBS += -lmsvcrtd
|
||||||
|
#LIBS += -lmsvcrt
|
||||||
|
#QMAKE_LFLAGS += /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:msvcrtd.lib
|
||||||
|
DEFINES += _CRT_SECURE_NO_WARNINGS _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
#TEMPLATE = lib
|
||||||
|
#TARGET = texthook
|
||||||
|
#CONFIG += dll
|
||||||
|
#CONFIG += staticlib
|
||||||
|
#DEFINES += TEXTHOOK_BUILD_LIB
|
||||||
|
DEFINES += TEXTHOOK_STATIC_LIB
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
$$PWD/ihf_p.h \
|
||||||
|
$$PWD/ith_p.h \
|
||||||
|
$$PWD/texthook_config.h \
|
||||||
|
$$PWD/texthook.h \
|
||||||
|
$$PWD/texthook_p.h \
|
||||||
|
$$PWD/textthread_p.h \
|
||||||
|
$$PWD/winapi_p.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
$$PWD/ihf_p.cc \
|
||||||
|
$$PWD/ith_p.cc \
|
||||||
|
$$PWD/texthook.cc \
|
||||||
|
$$PWD/textthread_p.cc \
|
||||||
|
$$PWD/winapi_p.cc
|
||||||
|
|
||||||
|
#!wince*: LIBS += -lshell32
|
||||||
|
#RC_FILE += texthook.rc
|
||||||
|
|
||||||
|
OTHER_FILES += \
|
||||||
|
$$PWD/texthook.pri \
|
||||||
|
$$PWD/texthook.pro \
|
||||||
|
$$PWD/texthook.rc
|
||||||
|
|
||||||
|
# EOF
|
376
vnr/texthook/textthread_p.cc
Normal file
376
vnr/texthook/textthread_p.cc
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
// textthread_p.cc
|
||||||
|
// 6/6/2012 jichi
|
||||||
|
|
||||||
|
#include "texthook/textthread_p.h"
|
||||||
|
#include "texthook/texthook_p.h"
|
||||||
|
#include "winmutex/winmutex.h"
|
||||||
|
#include "wintimer/wintimer.h"
|
||||||
|
#include "host/textthread.h"
|
||||||
|
#include <QtCore/QRegExp>
|
||||||
|
|
||||||
|
//#define DEBUG "textthread_p.cc"
|
||||||
|
#include "sakurakit/skdebug.h"
|
||||||
|
|
||||||
|
enum { ITH_THREAD_NAME_CAPACITY = 0x200 }; // used internally by ITH
|
||||||
|
|
||||||
|
#define REPEAT_RX_1 "(.{2,})\\1+$" // The pattern has at least 2 bytes, and repeats at least once
|
||||||
|
|
||||||
|
/** Private class */
|
||||||
|
|
||||||
|
#define D_LOCK win_mutex_lock<D::mutex_type> d_lock(D::globalMutex) // Synchronized scope for accessing private data
|
||||||
|
|
||||||
|
class TextThreadDelegatePrivate
|
||||||
|
{
|
||||||
|
SK_CLASS(TextThreadDelegatePrivate)
|
||||||
|
SK_DISABLE_COPY(TextThreadDelegatePrivate)
|
||||||
|
|
||||||
|
public:
|
||||||
|
typedef win_mutex<CRITICAL_SECTION> mutex_type;
|
||||||
|
|
||||||
|
static mutex_type globalMutex; // Used only in public class. Because ITH is running in another single thread
|
||||||
|
static int globalCapacity; // maximum text size
|
||||||
|
static bool globalRemovesRepeat;
|
||||||
|
static bool globalKeepsSpace;
|
||||||
|
static bool globalWideCharacter;
|
||||||
|
|
||||||
|
TextThread *t;
|
||||||
|
WinTimer flushTimer; // as QTimer does not work with windows remote thread, use native WM_TIMER instead
|
||||||
|
|
||||||
|
ulong signature; // buffered
|
||||||
|
char sourceBuffer[ITH_THREAD_NAME_CAPACITY]; // buffered
|
||||||
|
QString source; // buffered
|
||||||
|
|
||||||
|
int bufferSize;
|
||||||
|
int bufferCapacity;
|
||||||
|
char *buffer;
|
||||||
|
bool removesRepeat;
|
||||||
|
|
||||||
|
QByteArray spaceBuffer;
|
||||||
|
int spaceCount;
|
||||||
|
|
||||||
|
struct Repeat
|
||||||
|
{
|
||||||
|
QRegExp rx; // cached
|
||||||
|
char *buffer; // repeated string
|
||||||
|
int size;
|
||||||
|
int pos; // >= 0, current pos of repeating string
|
||||||
|
int offset; // offset of repeated string
|
||||||
|
|
||||||
|
Repeat() : rx(REPEAT_RX_1), buffer(nullptr), size(0), pos(0), offset(-1) {}
|
||||||
|
~Repeat() { if (buffer) delete[] buffer; }
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
size = pos = 0;
|
||||||
|
offset = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isRepeating(const char *data, int len) const
|
||||||
|
{
|
||||||
|
if (!size || !buffer)
|
||||||
|
return false;
|
||||||
|
switch (len) {
|
||||||
|
case 1: return pos < size && buffer[pos] == *data;
|
||||||
|
case 2: return pos < size + 1 && buffer[pos] == data[0] && buffer[pos +1] == data[1];
|
||||||
|
default:
|
||||||
|
if (pos + len >= size)
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
if (buffer[pos + i] != data[i])
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} repeat;
|
||||||
|
|
||||||
|
// - Construction -
|
||||||
|
public:
|
||||||
|
explicit TextThreadDelegatePrivate(TextThread *thread) : t(thread),
|
||||||
|
bufferSize(0), bufferCapacity(globalCapacity), buffer(new char[globalCapacity]),
|
||||||
|
spaceCount(0),
|
||||||
|
removesRepeat(false)
|
||||||
|
{
|
||||||
|
signature = signatureOf(t);
|
||||||
|
|
||||||
|
//size_t size =
|
||||||
|
t->GetThreadString(sourceBuffer, ITH_THREAD_NAME_CAPACITY);
|
||||||
|
source = sourceBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
~TextThreadDelegatePrivate() { delete[] buffer; }
|
||||||
|
|
||||||
|
// - Properties -
|
||||||
|
public:
|
||||||
|
//QString text() const { return QString::fromLocal8Bit(buffer); }
|
||||||
|
//ulong context() const { return t->GetThreadParameter()->retn; }
|
||||||
|
//ulong subcontext() const { return t->GetThreadParameter()->spl; }
|
||||||
|
|
||||||
|
//ulong processId() const { return t->PID(); }
|
||||||
|
|
||||||
|
// - Actions -
|
||||||
|
public:
|
||||||
|
void flush()
|
||||||
|
{
|
||||||
|
if (flushTimer.isActive())
|
||||||
|
flushTimer.stop();
|
||||||
|
if (bufferSize) {
|
||||||
|
send();
|
||||||
|
bufferSize = 0;
|
||||||
|
}
|
||||||
|
if (!spaceBuffer.isEmpty())
|
||||||
|
spaceBuffer.clear();
|
||||||
|
spaceCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void syncGlobal()
|
||||||
|
{
|
||||||
|
if (bufferCapacity < globalCapacity) {
|
||||||
|
delete[] buffer;
|
||||||
|
bufferCapacity = globalCapacity;
|
||||||
|
buffer = new char[bufferCapacity];
|
||||||
|
if (repeat.buffer) {
|
||||||
|
delete[] repeat.buffer;
|
||||||
|
if (!removesRepeat)
|
||||||
|
repeat.buffer = nullptr;
|
||||||
|
else {
|
||||||
|
char *largerBuffer = new char[bufferCapacity];
|
||||||
|
if (repeat.size)
|
||||||
|
qMemCopy(largerBuffer, repeat.buffer, repeat.size);
|
||||||
|
repeat.buffer = largerBuffer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//bufferSize = repeatOffset = 0; // already reset in flush
|
||||||
|
//if (removesRepeat)
|
||||||
|
// repeat.reset();
|
||||||
|
}
|
||||||
|
if (removesRepeat != globalRemovesRepeat) {
|
||||||
|
removesRepeat = globalRemovesRepeat;
|
||||||
|
if (removesRepeat)
|
||||||
|
repeat.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendSpace()
|
||||||
|
{
|
||||||
|
flushTimer.start();
|
||||||
|
spaceCount++;
|
||||||
|
if (spaceBuffer.isEmpty())
|
||||||
|
spaceBuffer.append(buffer, bufferSize);
|
||||||
|
spaceBuffer.append(' ');
|
||||||
|
if (globalWideCharacter)
|
||||||
|
spaceBuffer.append('\0'); // L' ' = {'\x20', '\0'};
|
||||||
|
}
|
||||||
|
|
||||||
|
void append(const char *data, int len)
|
||||||
|
{
|
||||||
|
flushTimer.start();
|
||||||
|
if (bufferSize < qMin(bufferCapacity, globalCapacity))
|
||||||
|
switch (len) {
|
||||||
|
case 1: buffer[bufferSize++] = *data; break;
|
||||||
|
case 2: buffer[bufferSize++] = *data;
|
||||||
|
if (bufferSize < bufferCapacity)
|
||||||
|
buffer[bufferSize++] = data[1];
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
int diff = qMin(len, bufferCapacity - bufferSize);
|
||||||
|
qMemCopy(buffer + bufferSize, data, diff);
|
||||||
|
bufferSize += diff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!spaceBuffer.isEmpty())
|
||||||
|
spaceBuffer.append(data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void appendRepeat(const char *data, int len)
|
||||||
|
{
|
||||||
|
if (bufferSize + len >= qMin(bufferCapacity, globalCapacity)) // overflow
|
||||||
|
return;
|
||||||
|
if (repeat.isRepeating(data, len)) {
|
||||||
|
repeat.pos += len;
|
||||||
|
if (repeat.pos >= repeat.size)
|
||||||
|
repeat.pos = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
repeat.clear();
|
||||||
|
|
||||||
|
append(data, len);
|
||||||
|
|
||||||
|
if (bufferSize >= 6) { // at least 2 characters
|
||||||
|
// Use fromLatin1 to prevent the data from being decoded
|
||||||
|
QString t = QString::fromLatin1(buffer, bufferSize);
|
||||||
|
repeat.offset = repeat.rx.indexIn(t);
|
||||||
|
if (repeat.offset >= 0) {
|
||||||
|
repeat.size = repeat.rx.cap(1).size();
|
||||||
|
if (!repeat.buffer)
|
||||||
|
repeat.buffer = new char[bufferCapacity];
|
||||||
|
qMemCopy(repeat.buffer, buffer + repeat.offset, repeat.size);
|
||||||
|
//bufferSize = repeat.offset repeat.size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void send()
|
||||||
|
{
|
||||||
|
int size;
|
||||||
|
if (removesRepeat && repeat.offset >= 0 && repeat.size)
|
||||||
|
size = repeat.offset + repeat.size;
|
||||||
|
else
|
||||||
|
size = bufferSize;
|
||||||
|
if (!spaceBuffer.isEmpty() && spaceBuffer.size() != size)
|
||||||
|
spaceBuffer.truncate(size + spaceCount);
|
||||||
|
TextHookPrivate::sendData(
|
||||||
|
QByteArray(buffer, size), spaceBuffer,
|
||||||
|
signature, source);
|
||||||
|
}
|
||||||
|
|
||||||
|
static qint32 signatureOf(TextThread *t)
|
||||||
|
{
|
||||||
|
qint32 ret =
|
||||||
|
(t->GetThreadParameter()->retn & 0xffff) | // context
|
||||||
|
(t->GetThreadParameter()->spl & 0xffff) << 16; // subcontext
|
||||||
|
return ret ? ret : t->Addr();
|
||||||
|
}
|
||||||
|
|
||||||
|
//static QString sourceOf(TextThread *t);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static ulong contextOf(TextThread *t)
|
||||||
|
{ return t->GetThreadParameter()->retn; }
|
||||||
|
|
||||||
|
static ulong subcontextOf(TextThread *t)
|
||||||
|
{ return t->GetThreadParameter()->spl; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TextThreadDelegatePrivate::mutex_type TextThreadDelegatePrivate::globalMutex;
|
||||||
|
int TextThreadDelegatePrivate::globalCapacity = 512;
|
||||||
|
bool TextThreadDelegatePrivate::globalRemovesRepeat = false;
|
||||||
|
bool TextThreadDelegatePrivate::globalKeepsSpace = false;
|
||||||
|
bool TextThreadDelegatePrivate::globalWideCharacter = false;
|
||||||
|
|
||||||
|
//QString TextThreadDelegatePrivate::sourceOf(TextThread *t)
|
||||||
|
//{
|
||||||
|
// Q_ASSERT(t);
|
||||||
|
// QString ret;
|
||||||
|
// enum { buf_size = 0x200 }; // 0x200 is used by ITH internally
|
||||||
|
// wchar_t buf[buf_size];
|
||||||
|
// ulong len = t->GetThreadString(buf, buf_size);
|
||||||
|
// if (len)
|
||||||
|
// ret = QString::fromWCharArray(buf, len);
|
||||||
|
// return ret;
|
||||||
|
//}
|
||||||
|
|
||||||
|
/** Public class */
|
||||||
|
|
||||||
|
// - Constructions -
|
||||||
|
|
||||||
|
TextThreadDelegate::TextThreadDelegate(TextThread *t)
|
||||||
|
: d_(new D(t))
|
||||||
|
{
|
||||||
|
d_->flushTimer.setMethod(this, &Self::flush);
|
||||||
|
d_->flushTimer.setSingleShot(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextThreadDelegate::~TextThreadDelegate()
|
||||||
|
{
|
||||||
|
if (d_->flushTimer.isActive())
|
||||||
|
d_->flushTimer.stop();
|
||||||
|
delete d_;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TextThreadDelegate::delegateOf(const Self *that) const
|
||||||
|
{
|
||||||
|
Q_ASSERT(t);
|
||||||
|
// Both have no context, and my subcontext is smaller
|
||||||
|
return that
|
||||||
|
&& !D::contextOf(that->d_->t) && !D::contextOf(d_->t)
|
||||||
|
&& D::subcontextOf(that->d_->t) >= D::subcontextOf(d_->t)
|
||||||
|
&& ::strcmp(d_->sourceBuffer, that->d_->sourceBuffer) == 0
|
||||||
|
&& nameEquals("Malie");
|
||||||
|
}
|
||||||
|
|
||||||
|
// - Properties -
|
||||||
|
|
||||||
|
//TextThread *TextThreadDelegate::t() const { return d_->t; }
|
||||||
|
int TextThreadDelegate::threadNumber() const
|
||||||
|
{ return d_->t->Number(); }
|
||||||
|
|
||||||
|
qint32 TextThreadDelegate::signature() const
|
||||||
|
{ return d_->signature; }
|
||||||
|
|
||||||
|
QString TextThreadDelegate::name() const
|
||||||
|
{ return d_->source; }
|
||||||
|
|
||||||
|
bool TextThreadDelegate::nameEquals(const char *that) const
|
||||||
|
{ return !::strcmp(d_->sourceBuffer, that); }
|
||||||
|
|
||||||
|
int TextThreadDelegate::capacity() { return D::globalCapacity; }
|
||||||
|
void TextThreadDelegate::setCapacity(int value) { D::globalCapacity = value; }
|
||||||
|
|
||||||
|
bool TextThreadDelegate::removesRepeat() { return D::globalRemovesRepeat; }
|
||||||
|
void TextThreadDelegate::setRemovesRepeat(bool value) { D::globalRemovesRepeat = value; }
|
||||||
|
|
||||||
|
bool TextThreadDelegate::wideCharacter() { return D::globalWideCharacter; }
|
||||||
|
void TextThreadDelegate::setWideCharacter(bool value) { D::globalWideCharacter = value; }
|
||||||
|
|
||||||
|
bool TextThreadDelegate::keepsSpace() { return D::globalKeepsSpace; }
|
||||||
|
void TextThreadDelegate::setKeepsSpace(bool value) { D::globalKeepsSpace = value; }
|
||||||
|
|
||||||
|
void TextThreadDelegate::setInterval(int msecs)
|
||||||
|
{ d_->flushTimer.setInterval(msecs); }
|
||||||
|
|
||||||
|
void TextThreadDelegate::setParentWindow(WId winId)
|
||||||
|
{ d_->flushTimer.setParentWindow(winId); }
|
||||||
|
|
||||||
|
// - Actions -
|
||||||
|
|
||||||
|
void TextThreadDelegate::flush()
|
||||||
|
{
|
||||||
|
D_LOCK;
|
||||||
|
d_->flush();
|
||||||
|
d_->syncGlobal();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextThreadDelegate::touch()
|
||||||
|
{
|
||||||
|
D_LOCK;
|
||||||
|
d_->flushTimer.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void TextThreadDelegate::append(const char *data, int len, bool space)
|
||||||
|
{
|
||||||
|
D_LOCK;
|
||||||
|
if (space && D::globalKeepsSpace)
|
||||||
|
d_->appendSpace();
|
||||||
|
if (data && len) {
|
||||||
|
if (d_->removesRepeat)
|
||||||
|
d_->appendRepeat(data, len);
|
||||||
|
else
|
||||||
|
d_->append(data, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
/*
|
||||||
|
void TextThreadDelegate::append(const QByteArray &data)
|
||||||
|
{
|
||||||
|
D::mutex_lock_type locker(D::mutex);
|
||||||
|
|
||||||
|
d_->flushTimer.start();
|
||||||
|
if (d_->buffer.size() <= D::capacity)
|
||||||
|
d_->buffer.append(data);
|
||||||
|
}
|
||||||
|
void TextThreadDelegatePrivate::send()
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
qDebug()<< source()
|
||||||
|
<< t->Number()
|
||||||
|
<< t->PID()
|
||||||
|
<< QString::number(t->Addr(), 16)
|
||||||
|
<< QString::number(t->GetThreadParameter()->retn, 16)
|
||||||
|
<< QString::number(t->GetThreadParameter()->spl, 16)
|
||||||
|
<< QTextCodec::codecForName("SHIFT-JIS")->makeDecoder()->toUnicode(buffer);
|
||||||
|
#endif // DEBUG
|
||||||
|
}
|
||||||
|
*/
|
81
vnr/texthook/textthread_p.h
Normal file
81
vnr/texthook/textthread_p.h
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// textthread_p.h
|
||||||
|
// 6/6/2012 jichi
|
||||||
|
// Internal header.
|
||||||
|
// Defines TextHook delegate class.
|
||||||
|
|
||||||
|
#include "sakurakit/skglobal.h"
|
||||||
|
#include <QtGui/qwindowdefs.h>
|
||||||
|
|
||||||
|
//QT_FORWARD_DECLARE_CLASS(QByteArray)
|
||||||
|
|
||||||
|
class SharedRef
|
||||||
|
{
|
||||||
|
SK_CLASS(SharedRef)
|
||||||
|
int count_;
|
||||||
|
public:
|
||||||
|
SharedRef(): count_(1) {}
|
||||||
|
int retainCount() const { return count_; }
|
||||||
|
void retain() { count_++; }
|
||||||
|
//void release() { count_--; }
|
||||||
|
static void release(Self *x) { if (--x->count_ <= 0) delete x; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME: This class is not thread-safe!
|
||||||
|
class TextThread;
|
||||||
|
class TextThreadDelegatePrivate;
|
||||||
|
class TextThreadDelegate : public SharedRef
|
||||||
|
{
|
||||||
|
SK_EXTEND_CLASS(TextThreadDelegate, SharedRef)
|
||||||
|
SK_DISABLE_COPY(TextThreadDelegate)
|
||||||
|
SK_DECLARE_PRIVATE(TextThreadDelegatePrivate)
|
||||||
|
public:
|
||||||
|
explicit TextThreadDelegate(TextThread *t);
|
||||||
|
~TextThreadDelegate();
|
||||||
|
|
||||||
|
bool delegateOf(const Self *t) const;
|
||||||
|
|
||||||
|
// - Properties -
|
||||||
|
|
||||||
|
//TextThread *t() const;
|
||||||
|
int threadNumber() const;
|
||||||
|
qint32 signature() const;
|
||||||
|
QString name() const;
|
||||||
|
bool nameEquals(const char *that) const; // optimized
|
||||||
|
|
||||||
|
// Maximum text size
|
||||||
|
static int capacity();
|
||||||
|
static void setCapacity(int value);
|
||||||
|
|
||||||
|
static bool wideCharacter();
|
||||||
|
static void setWideCharacter(bool value);
|
||||||
|
|
||||||
|
static bool removesRepeat();
|
||||||
|
static void setRemovesRepeat(bool value);
|
||||||
|
|
||||||
|
static bool keepsSpace();
|
||||||
|
static void setKeepsSpace(bool value);
|
||||||
|
|
||||||
|
//TextThread *t() const;
|
||||||
|
|
||||||
|
//int interval() const;
|
||||||
|
void setInterval(int msecs);
|
||||||
|
|
||||||
|
//WId parentWindow() const;
|
||||||
|
void setParentWindow(WId winId);
|
||||||
|
|
||||||
|
// - Actions -
|
||||||
|
|
||||||
|
//void append(const QByteArray &data);
|
||||||
|
/** Add data to the text thread
|
||||||
|
* @param data raw data
|
||||||
|
* @param len length of the data
|
||||||
|
* @param space Whether have LEADING space
|
||||||
|
*/
|
||||||
|
void append(const char *data, int len, bool space=false);
|
||||||
|
void flush();
|
||||||
|
void touch(); // keep timer running
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
21
vnr/texthook/winapi_p.cc
Normal file
21
vnr/texthook/winapi_p.cc
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
// apiwin_p.cc
|
||||||
|
// 10/6/2012 jichi
|
||||||
|
#include "texthook/winapi_p.h"
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
WINAPI_BEGIN_NAMESPACE
|
||||||
|
|
||||||
|
bool IsProcessActiveWithId(DWORD dwProcessId)
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
if (HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId)) {
|
||||||
|
DWORD dwExitCode;
|
||||||
|
ret = ::GetExitCodeProcess(hProc, &dwExitCode) && (dwExitCode == STILL_ACTIVE);
|
||||||
|
::CloseHandle(hProc);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
WINAPI_END_NAMESPACE
|
||||||
|
|
||||||
|
// EOF
|
18
vnr/texthook/winapi_p.h
Normal file
18
vnr/texthook/winapi_p.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#pragma once
|
||||||
|
// winapi_p.h
|
||||||
|
// 10/5/2012 jichi
|
||||||
|
// Internal header.
|
||||||
|
// Wrapper of <windows.h>
|
||||||
|
|
||||||
|
#ifndef WINAPI_BEGIN_NAMESPACE
|
||||||
|
# define WINAPI_BEGIN_NAMESPACE namespace winapi {
|
||||||
|
#endif
|
||||||
|
#ifndef WINAPI_END_NAMESPACE
|
||||||
|
# define WINAPI_END_NAMESPACE } // namespace winapi
|
||||||
|
#endif
|
||||||
|
|
||||||
|
WINAPI_BEGIN_NAMESPACE
|
||||||
|
bool IsProcessActiveWithId(unsigned long dwProcessId);
|
||||||
|
WINAPI_END_NAMESPACE
|
||||||
|
|
||||||
|
// EOF
|
112
vnr/vnrhook/CMakeLists.txt
Normal file
112
vnr/vnrhook/CMakeLists.txt
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
# # hook.pro
|
||||||
|
# # Exception handler to catch all exceptions
|
||||||
|
# CONFIG += dll noqt eh eha # noeh nosafeseh
|
||||||
|
|
||||||
|
# DEFINES += ITH_HAS_CRT ITH_HAS_SEH
|
||||||
|
# DEFINES += MEMDBG_NO_STL NTINSPECT_NO_STL # disabled as not used
|
||||||
|
|
||||||
|
# # jichi 11/13/2011: disable swprinf warning
|
||||||
|
# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
|
||||||
|
# config.pri
|
||||||
|
# CONFIG(eha) {
|
||||||
|
# message(CONFIG eha)
|
||||||
|
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
|
||||||
|
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
|
||||||
|
# QMAKE_CXXFLAGS_STL_ON += /EHa
|
||||||
|
# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
|
||||||
|
# }
|
||||||
|
|
||||||
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|
||||||
|
set(vnrhook_src
|
||||||
|
include/const.h
|
||||||
|
include/defs.h
|
||||||
|
include/types.h
|
||||||
|
src/except.h
|
||||||
|
src/main.cc
|
||||||
|
src/main.h
|
||||||
|
src/pipe.cc
|
||||||
|
src/engine/engine.cc
|
||||||
|
src/engine/engine.h
|
||||||
|
src/engine/hookdefs.h
|
||||||
|
src/engine/match.cc
|
||||||
|
src/engine/match.h
|
||||||
|
src/engine/pchooks.cc
|
||||||
|
src/engine/pchooks.h
|
||||||
|
src/engine/mono/funcinfo.h
|
||||||
|
src/engine/mono/types.h
|
||||||
|
src/engine/ppsspp/funcinfo.h
|
||||||
|
src/hijack/texthook.cc
|
||||||
|
src/hijack/texthook.h
|
||||||
|
src/tree/avl.h
|
||||||
|
src/util/growl.h
|
||||||
|
src/util/util.cc
|
||||||
|
src/util/util.h
|
||||||
|
${PROJECT_SOURCE_DIR}/ccutil/ccmacro.h
|
||||||
|
${PROJECT_SOURCE_DIR}/cpputil/cpplocale.h
|
||||||
|
${PROJECT_SOURCE_DIR}/cpputil/cppmarshal.h
|
||||||
|
${PROJECT_SOURCE_DIR}/cpputil/cppmath.h
|
||||||
|
${PROJECT_SOURCE_DIR}/cpputil/cpppath.h
|
||||||
|
${PROJECT_SOURCE_DIR}/cpputil/cppstring.h
|
||||||
|
${PROJECT_SOURCE_DIR}/cpputil/cpptype.h
|
||||||
|
${PROJECT_SOURCE_DIR}/cpputil/cppunicode.h
|
||||||
|
${PROJECT_SOURCE_DIR}/disasm/disasm.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/hashutil/hashstr.h
|
||||||
|
${PROJECT_SOURCE_DIR}/hashutil/hashutil.h
|
||||||
|
${PROJECT_SOURCE_DIR}/memdbg/memdbg.h
|
||||||
|
${PROJECT_SOURCE_DIR}/memdbg/memsearch.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/memdbg/memsearch.h
|
||||||
|
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.h
|
||||||
|
${PROJECT_SOURCE_DIR}/winkey/winkey.h
|
||||||
|
${PROJECT_SOURCE_DIR}/winversion/winversion.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/winversion/winversion.h
|
||||||
|
${PROJECT_SOURCE_DIR}/winseh/winseh.h
|
||||||
|
${PROJECT_SOURCE_DIR}/winseh/winseh.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/winseh/winseh_safe.cc
|
||||||
|
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
|
||||||
|
${PROJECT_SOURCE_DIR}/mono/monoobject.h
|
||||||
|
${PROJECT_SOURCE_DIR}/mono/monotype.h
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(vnrhook SHARED ${vnrhook_src})
|
||||||
|
|
||||||
|
enable_language(ASM_MASM)
|
||||||
|
|
||||||
|
set_source_files_properties(
|
||||||
|
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
|
||||||
|
PROPERTIES
|
||||||
|
# CMAKE_ASM_MASM_FLAGS /safeseh # CMake bug 14711: http://www.cmake.org/Bug/view.php?id=14711
|
||||||
|
COMPILE_FLAGS /safeseh
|
||||||
|
)
|
||||||
|
|
||||||
|
set_target_properties(vnrhook PROPERTIES
|
||||||
|
LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO"
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_options(vnrhook PRIVATE
|
||||||
|
/EHa
|
||||||
|
$<$<CONFIG:Release>:>
|
||||||
|
$<$<CONFIG:Debug>:>
|
||||||
|
)
|
||||||
|
|
||||||
|
set(vnrhook_libs
|
||||||
|
ithsys
|
||||||
|
${WDK_HOME}/lib/wxp/i386/ntdll.lib
|
||||||
|
Version.lib
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(vnrhook ${vnrhook_libs})
|
||||||
|
|
||||||
|
target_compile_definitions(vnrhook
|
||||||
|
PRIVATE
|
||||||
|
ITH_HAS_CRT
|
||||||
|
ITH_HAS_SEH
|
||||||
|
_CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS vnrhook RUNTIME
|
||||||
|
DESTINATION .
|
||||||
|
CONFIGURATIONS Release
|
||||||
|
)
|
35
vnr/vnrhook/TRASH/dllconfig.pri
Normal file
35
vnr/vnrhook/TRASH/dllconfig.pri
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# dllconfig.pri
|
||||||
|
# 8/9/2013 jichi
|
||||||
|
# For linking ITH injectable dlls.
|
||||||
|
# The dll is self-contained and Windows-independent.
|
||||||
|
|
||||||
|
CONFIG += dll noqt #noeh nosafeseh
|
||||||
|
CONFIG -= embed_manifest_dll # dynamically load dlls
|
||||||
|
win32 {
|
||||||
|
CONFIG(eh): DEFINES += ITH_HAS_SEH # Do have exception handler in msvcrt.dll on Windows Vista and later
|
||||||
|
CONFIG(noeh): DEFINES -= ITH_HAS_SEH # Do not have exception handler in msvcrt.dll on Windows XP and before
|
||||||
|
}
|
||||||
|
include(../../../config.pri)
|
||||||
|
#win32 {
|
||||||
|
# CONFIG(noeh): include($$LIBDIR/winseh/winseh_safe.pri)
|
||||||
|
#}
|
||||||
|
|
||||||
|
# jichi 11/24/2013: Disable manual heap
|
||||||
|
DEFINES -= ITH_HAS_HEAP
|
||||||
|
|
||||||
|
# jichi 11/13/2011: disable swprinf warning
|
||||||
|
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
|
||||||
|
|
||||||
|
## Libraries
|
||||||
|
|
||||||
|
#LIBS += -lkernel32 -luser32 -lgdi32
|
||||||
|
LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
|
||||||
|
LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
|
||||||
|
#LIBS += -L$$WDK7_HOME/lib/crt/i386 -lmsvcrt
|
||||||
|
#QMAKE_LFLAGS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # This will leave runtime flags in the dll
|
||||||
|
|
||||||
|
#LIBS += -L$$WDK8_HOME/lib/winv6.3/um/x86 -lntdll
|
||||||
|
|
||||||
|
HEADERS += $$PWD/dllconfig.h
|
||||||
|
|
||||||
|
# EOF
|
61
vnr/vnrhook/TRASH/hookxp.pro
Normal file
61
vnr/vnrhook/TRASH/hookxp.pro
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# hookxp.pro
|
||||||
|
# 8/9/2013 jichi
|
||||||
|
# Build vnrhookxp.dll for Windows XP
|
||||||
|
|
||||||
|
CONFIG += noeh # msvcrt on Windows XP does not has exception handler
|
||||||
|
include(../dllconfig.pri)
|
||||||
|
include(../sys/sys.pri)
|
||||||
|
include($$LIBDIR/disasm/disasm.pri)
|
||||||
|
include($$LIBDIR/memdbg/memdbg.pri)
|
||||||
|
include($$LIBDIR/ntinspect/ntinspect.pri)
|
||||||
|
include($$LIBDIR/winkey/winkey.pri)
|
||||||
|
include($$LIBDIR/winseh/winseh_safe.pri)
|
||||||
|
include($$LIBDIR/winversion/winversion.pri)
|
||||||
|
|
||||||
|
VPATH += ../hook
|
||||||
|
INCLUDEPATH += ../hook
|
||||||
|
|
||||||
|
# 9/27/2013: disable ITH this game engine, only for debugging purpose
|
||||||
|
#DEFINES += ITH_DISABLE_ENGINE
|
||||||
|
|
||||||
|
# jichi 9/22/2013: When ITH is on wine, mutex is needed to protect NtWriteFile
|
||||||
|
#DEFINES += ITH_WINE
|
||||||
|
#DEFINES += ITH_SYNC_PIPE
|
||||||
|
|
||||||
|
DEFINES += MEMDBG_NO_STL NTINSPECT_NO_STL
|
||||||
|
|
||||||
|
## Libraries
|
||||||
|
|
||||||
|
LIBS += -lkernel32 -luser32 -lgdi32 #-lgdiplus
|
||||||
|
|
||||||
|
## Sources
|
||||||
|
|
||||||
|
TEMPLATE = lib
|
||||||
|
TARGET = vnrhookxp
|
||||||
|
|
||||||
|
#CONFIG += staticlib
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
config.h \
|
||||||
|
cli.h \
|
||||||
|
hook.h \
|
||||||
|
engine/engine.h \
|
||||||
|
engine/hookdefs.h \
|
||||||
|
engine/match.h \
|
||||||
|
engine/pchooks.h \
|
||||||
|
engine/util.h \
|
||||||
|
tree/avl.h
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cc \
|
||||||
|
rpc/pipe.cc \
|
||||||
|
hijack/texthook.cc \
|
||||||
|
engine/engine.cc \
|
||||||
|
engine/match.cc \
|
||||||
|
engine/pchooks.cc \
|
||||||
|
engine/util.cc
|
||||||
|
|
||||||
|
#RC_FILE += vnrhook.rc
|
||||||
|
#OTHER_FILES += vnrhook.rc
|
||||||
|
|
||||||
|
# EOF
|
46
vnr/vnrhook/TRASH/memory.h
Normal file
46
vnr/vnrhook/TRASH/memory.h
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ith/common/memory.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// Branch: ITH/mem.h, revision 66
|
||||||
|
|
||||||
|
#ifndef ITH_HAS_HEAP
|
||||||
|
# define ITH_MEMSET_HEAP(...) ::memset(__VA_ARGS__)
|
||||||
|
#else
|
||||||
|
# define ITH_MEMSET_HEAP(...) (void)0
|
||||||
|
|
||||||
|
// Defined in kernel32.lilb
|
||||||
|
extern "C" {
|
||||||
|
// PVOID RtlAllocateHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ SIZE_T Size);
|
||||||
|
__declspec(dllimport) void * __stdcall RtlAllocateHeap(void *HeapHandle, unsigned long Flags, unsigned long Size);
|
||||||
|
|
||||||
|
// BOOLEAN RtlFreeHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ PVOID HeapBase);
|
||||||
|
__declspec(dllimport) int __stdcall RtlFreeHeap(void *HeapHandle, unsigned long Flags, void *HeapBase);
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
//NTSYSAPI
|
||||||
|
//BOOL
|
||||||
|
//NTAPI
|
||||||
|
//RtlFreeHeap(
|
||||||
|
// _In_ HANDLE hHeap,
|
||||||
|
// _In_ DWORD dwFlags,
|
||||||
|
// _In_ LPVOID lpMem
|
||||||
|
//);
|
||||||
|
|
||||||
|
extern void *hHeap; // defined in ith/sys.cc
|
||||||
|
|
||||||
|
inline void * __cdecl operator new(size_t lSize)
|
||||||
|
{
|
||||||
|
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa366597%28v=vs.85%29.aspx
|
||||||
|
// HEAP_ZERO_MEMORY flag is critical. All new objects are assumed with zero initialized.
|
||||||
|
enum { HEAP_ZERO_MEMORY = 0x00000008 };
|
||||||
|
return RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void __cdecl operator delete(void *pBlock)
|
||||||
|
{ RtlFreeHeap(::hHeap, 0, pBlock); }
|
||||||
|
|
||||||
|
inline void __cdecl operator delete[](void *pBlock)
|
||||||
|
{ RtlFreeHeap(::hHeap, 0, pBlock); }
|
||||||
|
|
||||||
|
#endif // ITH_HAS_HEAP
|
36
vnr/vnrhook/TRASH/string.h
Normal file
36
vnr/vnrhook/TRASH/string.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// ith/common/string.h
|
||||||
|
// 8/9/2013 jichi
|
||||||
|
// Branch: ITH/string.h, rev 66
|
||||||
|
|
||||||
|
#ifdef ITH_HAS_CRT // ITH is linked with msvcrt dlls
|
||||||
|
# include <cstdio>
|
||||||
|
# include <cstring>
|
||||||
|
|
||||||
|
#else
|
||||||
|
# define _INC_SWPRINTF_INL_
|
||||||
|
# define CRT_IMPORT __declspec(dllimport)
|
||||||
|
|
||||||
|
#include <windows.h> // for wchar_t
|
||||||
|
extern "C" {
|
||||||
|
CRT_IMPORT int swprintf(wchar_t *src, const wchar_t *fmt, ...);
|
||||||
|
CRT_IMPORT int sprintf(char *src, const char *fmt, ...);
|
||||||
|
CRT_IMPORT int swscanf(const wchar_t *src, const wchar_t *fmt, ...);
|
||||||
|
CRT_IMPORT int sscanf(const char *src, const char *fmt, ...);
|
||||||
|
CRT_IMPORT int wprintf(const wchar_t *fmt, ...);
|
||||||
|
CRT_IMPORT int printf(const char *fmt, ...);
|
||||||
|
CRT_IMPORT int _wputs(const wchar_t *src);
|
||||||
|
CRT_IMPORT int puts(const char *src);
|
||||||
|
CRT_IMPORT int _stricmp(const char *x, const char *y);
|
||||||
|
CRT_IMPORT int _wcsicmp(const wchar_t *x, const wchar_t *y);
|
||||||
|
//CRT_IMPORT size_t strlen(const char *);
|
||||||
|
//CRT_IMPORT size_t wcslen(const wchar_t *);
|
||||||
|
//CRT_IMPORT char *strcpy(char *,const char *);
|
||||||
|
//CRT_IMPORT wchar_t *wcscpy(wchar_t *,const wchar_t *);
|
||||||
|
CRT_IMPORT void *memmove(void *dst, const void *src, size_t sz);
|
||||||
|
CRT_IMPORT const char *strchr(const char *src, int val);
|
||||||
|
CRT_IMPORT int strncmp(const char *x, const char *y, size_t sz);
|
||||||
|
} // extern "C"
|
||||||
|
|
||||||
|
#endif // ITH_HAS_CRT
|
11
vnr/vnrhook/TRASH/xp.txt
Normal file
11
vnr/vnrhook/TRASH/xp.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
12/16/2013
|
||||||
|
|
||||||
|
Differences between xp.dll and non-xp.dll for vnrhook.
|
||||||
|
|
||||||
|
non-xp:
|
||||||
|
CONFIG += eh
|
||||||
|
|
||||||
|
xp:
|
||||||
|
CONFIG += noeh
|
||||||
|
CONFIG -= embed_manifest_dll # Pure dynamic determined. The manifest would break Windows XP support
|
||||||
|
include($$LIBDIR/winseh/winseh_safe.pri)
|
249
vnr/vnrhook/include/const.h
Normal file
249
vnr/vnrhook/include/const.h
Normal file
@ -0,0 +1,249 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// vnrhook/const.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// Branch: ITH/common.h, rev 128
|
||||||
|
|
||||||
|
// jichi 9/9/2013: Another importnat function is lstrcatA, which is already handled by
|
||||||
|
// Debonosu hooks. Wait until it is really needed by certain games.
|
||||||
|
// The order of the functions is used in several place.
|
||||||
|
// I need to recompile all of the dlls to modify the order.
|
||||||
|
|
||||||
|
// jichi 10/14/2014
|
||||||
|
#define HOOK_GDI_FUNCTION_LIST \
|
||||||
|
GetTextExtentPoint32A \
|
||||||
|
, GetTextExtentExPointA \
|
||||||
|
, GetTabbedTextExtentA \
|
||||||
|
, GetCharacterPlacementA \
|
||||||
|
, GetGlyphIndicesA \
|
||||||
|
, GetGlyphOutlineA \
|
||||||
|
, ExtTextOutA \
|
||||||
|
, TextOutA \
|
||||||
|
, TabbedTextOutA \
|
||||||
|
, GetCharABCWidthsA \
|
||||||
|
, GetCharABCWidthsFloatA \
|
||||||
|
, GetCharWidth32A \
|
||||||
|
, GetCharWidthFloatA \
|
||||||
|
, GetTextExtentPoint32W \
|
||||||
|
, GetTextExtentExPointW \
|
||||||
|
, GetTabbedTextExtentW \
|
||||||
|
, GetCharacterPlacementW \
|
||||||
|
, GetGlyphIndicesW \
|
||||||
|
, GetGlyphOutlineW \
|
||||||
|
, ExtTextOutW \
|
||||||
|
, TextOutW \
|
||||||
|
, TabbedTextOutW \
|
||||||
|
, GetCharABCWidthsW \
|
||||||
|
, GetCharABCWidthsFloatW \
|
||||||
|
, GetCharWidth32W \
|
||||||
|
, GetCharWidthFloatW \
|
||||||
|
, DrawTextA \
|
||||||
|
, DrawTextExA \
|
||||||
|
, DrawTextW \
|
||||||
|
, DrawTextExW
|
||||||
|
//, CharNextA
|
||||||
|
//, CharPrevA
|
||||||
|
//enum { HOOK_FUN_COUNT = 30 }; // total number of GDI hooks
|
||||||
|
// jichi 1/16/2015: Though called max hook, it means max number of text threads
|
||||||
|
enum { MAX_HOOK = 64 }; // must be larger than HOOK_FUN_COUNT
|
||||||
|
|
||||||
|
//enum { HOOK_SECTION_SIZE = 0x2000 }; // default ITH value
|
||||||
|
// jichi 1/16/2015: Change to a very large number to prevent crash
|
||||||
|
//enum { MAX_HOOK = 0x100 }; // must be larger than HookFunCount
|
||||||
|
enum { HOOK_SECTION_SIZE = MAX_HOOK * 0x100 }; // default ITH value is 0x2000 for 32 hook (0x100 per hook)
|
||||||
|
|
||||||
|
// jichi 375/2014: Add offset of pusha/pushad
|
||||||
|
// http://faydoc.tripod.com/cpu/pushad.htm
|
||||||
|
// http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
|
||||||
|
//
|
||||||
|
// Warning: The offset in ITH has -4 offset comparing to pusha and AGTH
|
||||||
|
enum pusha_off {
|
||||||
|
pusha_eax_off = -0x4
|
||||||
|
, pusha_ecx_off = -0x8
|
||||||
|
, pusha_edx_off = -0xc
|
||||||
|
, pusha_ebx_off = -0x10
|
||||||
|
, pusha_esp_off = -0x14
|
||||||
|
, pusha_ebp_off = -0x18
|
||||||
|
, pusha_esi_off = -0x1c
|
||||||
|
, pusha_edi_off = -0x20
|
||||||
|
, pusha_off = -0x24 // pushad offset
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HostCommandType {
|
||||||
|
HOST_COMMAND = -1 // null type
|
||||||
|
, HOST_COMMAND_NEW_HOOK = 0
|
||||||
|
, HOST_COMMAND_REMOVE_HOOK = 1
|
||||||
|
, HOST_COMMAND_MODIFY_HOOK = 2
|
||||||
|
, HOST_COMMAND_HIJACK_PROCESS = 3
|
||||||
|
, HOST_COMMAND_DETACH = 4
|
||||||
|
};
|
||||||
|
|
||||||
|
enum HostNotificationType {
|
||||||
|
HOST_NOTIFICATION = -1 // null type
|
||||||
|
, HOST_NOTIFICATION_TEXT = 0
|
||||||
|
, HOST_NOTIFICATION_NEWHOOK = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
// jichi 9/8/2013: The meaning are guessed
|
||||||
|
// Values must be within DWORD
|
||||||
|
// Unused values are as follows:
|
||||||
|
// - 0x100
|
||||||
|
enum HookParamType : unsigned long {
|
||||||
|
USING_STRING = 0x1 // type(data) is char* or wchar_t* and has length
|
||||||
|
, USING_UTF8 = USING_STRING // jichi 10/21/2014: temporarily handled the same way as USING_STRING
|
||||||
|
, USING_UNICODE = 0x2 // type(data) is wchar_t or wchar_t*
|
||||||
|
, BIG_ENDIAN = 0x4 // type(data) is char
|
||||||
|
, DATA_INDIRECT = 0x8
|
||||||
|
, USING_SPLIT = 0x10 // aware of split time?
|
||||||
|
, SPLIT_INDIRECT = 0x20
|
||||||
|
, MODULE_OFFSET = 0x40 // do hash module, and the address is relative to module
|
||||||
|
, FUNCTION_OFFSET = 0x80 // do hash function, and the address is relative to funccion
|
||||||
|
, PRINT_DWORD = 0x100 // jichi 12/7/2014: Removed
|
||||||
|
, NO_ASCII = 0x100 // jichi 1l/22/2015: Skip ascii characters
|
||||||
|
, STRING_LAST_CHAR = 0x200
|
||||||
|
, NO_CONTEXT = 0x400
|
||||||
|
, HOOK_EMPTY = 0x800
|
||||||
|
, FIXING_SPLIT = 0x1000
|
||||||
|
//, HOOK_AUXILIARY = 0x2000 // jichi 12/13/2013: None of known hooks are auxiliary
|
||||||
|
, RELATIVE_SPLIT = 0x2000 // jichi 10/24/2014: relative split return address
|
||||||
|
, HOOK_ENGINE = 0x4000
|
||||||
|
, HOOK_ADDITIONAL = 0x8000
|
||||||
|
};
|
||||||
|
|
||||||
|
// 6/1/2014: Fixed split value for hok parameter
|
||||||
|
// Fuse all threads, and prevent floating
|
||||||
|
enum { FIXED_SPLIT_VALUE = 0x10001 };
|
||||||
|
|
||||||
|
// jichi 12/18/2013:
|
||||||
|
// These dlls are used to guess the range for non-NO_CONTEXT hooks.
|
||||||
|
//
|
||||||
|
// Disabling uxtheme.dll would crash certain system: http://tieba.baidu.com/p/2764436254
|
||||||
|
#define IHF_FILTER_DLL_LIST \
|
||||||
|
/* ITH original filters */ \
|
||||||
|
L"gdiplus.dll" /* Graphics functions like TextOutA */ \
|
||||||
|
, L"lpk.dll" /* Language package scripts and fonts */ \
|
||||||
|
, L"msctf.dll" /* Text service */ \
|
||||||
|
, L"psapi.dll" /* Processes */ \
|
||||||
|
, L"usp10.dll" /* UNICODE rendering */ \
|
||||||
|
, L"user32.dll" /* Non-graphics functions like lstrlenA */ \
|
||||||
|
, L"uxtheme.dll" /* Theme */ \
|
||||||
|
\
|
||||||
|
/* Windows DLLs */ \
|
||||||
|
, L"advapi32.dll" /* Advanced services */ \
|
||||||
|
, L"apphelp.dll" /* Appliation help */ \
|
||||||
|
, L"audioses.dll" /* Audios */ \
|
||||||
|
, L"avrt.dll" /* Audio video runtime */ \
|
||||||
|
, L"cfgmgr32.dll" /* Configuration manager */ \
|
||||||
|
, L"clbcatq.dll" /* COM query service */ \
|
||||||
|
, L"comctl32.dll" /* Common control library */ \
|
||||||
|
, L"comdlg32.dll" /* Common dialogs */ \
|
||||||
|
, L"crypt32.dll" /* Security cryption */ \
|
||||||
|
, L"cryptbase.dll"/* Security cryption */ \
|
||||||
|
, L"cryptsp.dll" /* Security cryption */ \
|
||||||
|
, L"d3d8thk.dll" /* Direct3D 8 */ \
|
||||||
|
, L"d3d9.dll" /* Direct3D 9 */ \
|
||||||
|
, L"dbghelp.dll" /* Debug help */ \
|
||||||
|
, L"dciman32.dll" /* Display cotrol */ \
|
||||||
|
, L"devobj.dll" /* Device object */ \
|
||||||
|
, L"ddraw.dll" /* Direct draw */ \
|
||||||
|
, L"dinput.dll" /* Diret input */ \
|
||||||
|
, L"dsound.dll" /* Direct sound */ \
|
||||||
|
, L"DShowRdpFilter.dll" /* Direct show */ \
|
||||||
|
, L"dwmapi.dll" /* Windows manager */ \
|
||||||
|
, L"gdi32.dll" /* GDI32 */ \
|
||||||
|
, L"hid.dll" /* HID user library */ \
|
||||||
|
, L"iertutil.dll" /* IE runtime */ \
|
||||||
|
, L"imagehlp.dll" /* Image help */ \
|
||||||
|
, L"imm32.dll" /* Input method */ \
|
||||||
|
, L"ksuser.dll" /* Kernel service */ \
|
||||||
|
, L"ole32.dll" /* COM OLE */ \
|
||||||
|
, L"oleacc.dll" /* OLE access */ \
|
||||||
|
, L"oleaut32.dll" /* COM OLE */ \
|
||||||
|
, L"kernel.dll" /* Kernel functions */ \
|
||||||
|
, L"kernelbase.dll" /* Kernel functions */ \
|
||||||
|
, L"midimap.dll" /* MIDI */ \
|
||||||
|
, L"mmdevapi.dll" /* Audio device */ \
|
||||||
|
, L"mpr.dll" /* Winnet */ \
|
||||||
|
, L"msacm32.dll" /* MS ACM */ \
|
||||||
|
, L"msacm32.drv" /* MS ACM */ \
|
||||||
|
, L"msasn1.dll" /* Encoding/decoding */ \
|
||||||
|
, L"msimg32.dll" /* Image */ \
|
||||||
|
, L"msvfw32.dll" /* Media play */ \
|
||||||
|
, L"netapi32.dll" /* Network service */ \
|
||||||
|
, L"normaliz.dll" /* Normalize */ \
|
||||||
|
, L"nsi.dll" /* NSI */ \
|
||||||
|
, L"ntdll.dll" /* NT functions */ \
|
||||||
|
, L"ntmarta.dll" /* NT MARTA */ \
|
||||||
|
, L"nvd3dum.dll" /* Direct 3D */ \
|
||||||
|
, L"powerprof.dll"/* Power profile */ \
|
||||||
|
, L"profapi.dll" /* Profile API */ \
|
||||||
|
, L"propsys.dll" /* System properties */ \
|
||||||
|
, L"quartz.dll" /* OpenGL */ \
|
||||||
|
, L"rpcrt4.dll" /* RPC runtime */ \
|
||||||
|
, L"rpcrtremote.dll" /* RPC runtime */ \
|
||||||
|
, L"rsabase.dll" /* RSA cryption */ \
|
||||||
|
, L"rsaenh.dll" /* RSA cryption */ \
|
||||||
|
, L"schannel.dll" /* Security channel */ \
|
||||||
|
, L"sechost.dll" /* Service host */ \
|
||||||
|
, L"setupapi.dll" /* Setup service */ \
|
||||||
|
, L"shell32.dll" /* Windows shell */ \
|
||||||
|
, L"shlwapi.dll" /* Light-weighted shell */ \
|
||||||
|
, L"slc.dll" /* SLC */ \
|
||||||
|
, L"srvcli.dll" /* Service client */ \
|
||||||
|
, L"version.dll" /* Windows version */ \
|
||||||
|
, L"wdmaud.drv" /* Wave output */ \
|
||||||
|
, L"wldap32.dll" /* Wireless */ \
|
||||||
|
, L"wininet.dll" /* Internet access */ \
|
||||||
|
, L"winmm.dll" /* Windows sound */ \
|
||||||
|
, L"winsta.dll" /* Connection system */ \
|
||||||
|
, L"wtsapi32.dll" /* Windows terminal server */ \
|
||||||
|
, L"wintrust.dll" /* Windows trust */ \
|
||||||
|
, L"wsock32.dll" /* Windows sock */ \
|
||||||
|
, L"ws2_32.dll" /* Terminal server */ \
|
||||||
|
, L"wkscli.dll" /* ACIS */ \
|
||||||
|
\
|
||||||
|
/* MSVCRT */ \
|
||||||
|
, L"msvcrt.dll" /* VC rutime */ \
|
||||||
|
, L"msvcr80.dll" /* VC rutime 8 */ \
|
||||||
|
, L"msvcp80.dll" /* VC rutime 8 */ \
|
||||||
|
, L"msvcr90.dll" /* VC rutime 9 */ \
|
||||||
|
, L"msvcp90.dll" /* VC rutime 9 */ \
|
||||||
|
, L"msvcr100.dll" /* VC rutime 10 */ \
|
||||||
|
, L"msvcp100.dll" /* VC rutime 10 */ \
|
||||||
|
, L"msvcr110.dll" /* VC rutime 11 */ \
|
||||||
|
, L"msvcp110.dll" /* VC rutime 11 */ \
|
||||||
|
\
|
||||||
|
/* VNR */ \
|
||||||
|
, L"vnrhook.dll" \
|
||||||
|
, L"vnrhookxp.dll" \
|
||||||
|
\
|
||||||
|
/* Sogou IME */ \
|
||||||
|
, L"sogoupy.ime" \
|
||||||
|
, L"PicFace.dll" \
|
||||||
|
, L"AddressSearch.dll" \
|
||||||
|
\
|
||||||
|
/* QQ IME */ \
|
||||||
|
, L"QQPINYIN.IME" \
|
||||||
|
\
|
||||||
|
/* AlphaROM */ \
|
||||||
|
, L"kDays.dll" \
|
||||||
|
\
|
||||||
|
/* 360Safe */ \
|
||||||
|
, L"safemon.dll" \
|
||||||
|
\
|
||||||
|
/* Locale changers */ \
|
||||||
|
, L"AlLayer.dll" /* AppLocale */ \
|
||||||
|
, L"LocaleEmulator.dll" /* Locale Emulator */ \
|
||||||
|
, L"LSH.dll" /* LocaleSwitch */ \
|
||||||
|
, L"ntleah.dll" /* NTLEA */
|
||||||
|
|
||||||
|
// Google Japanese IME
|
||||||
|
//, L"GoogleIMEJaTIP32.dll"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
//IHF_FILTER_COUNT = 7
|
||||||
|
IHF_FILTER_COUNT = 7 + 72 + 9 + 4 + 3 + 1 + 1 + 1 + 4 // count of total dlls to filter
|
||||||
|
, IHF_FILTER_CAPACITY = IHF_FILTER_COUNT + 1 // one more than the dll count
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
52
vnr/vnrhook/include/defs.h
Normal file
52
vnr/vnrhook/include/defs.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// vnrhook/defs.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
|
||||||
|
// DLL files
|
||||||
|
|
||||||
|
//#define ITH_SERVER_DLL L"vnrsrv.dll"
|
||||||
|
//#define ITH_CLIENT_DLL L"vnrcli.dll"
|
||||||
|
//#define ITH_CLIENT_XP_DLL L"vnrclixp.dll"
|
||||||
|
////#define ITH_CLIENT_UX_DLL L"vnrcliux.dll"
|
||||||
|
//#define ITH_ENGINE_DLL L"vnreng.dll"
|
||||||
|
//#define ITH_ENGINE_XP_DLL L"vnrengxp.dll"
|
||||||
|
//#define ITH_ENGINE_UX_DLL L"vnrengux.dll"
|
||||||
|
|
||||||
|
#define ITH_DLL L"vnrhook.dll"
|
||||||
|
#define ITH_DLL_XP L"vnrhookxp.dll"
|
||||||
|
|
||||||
|
// Pipes
|
||||||
|
|
||||||
|
#define ITH_TEXT_PIPE L"\\??\\pipe\\VNR_TEXT"
|
||||||
|
#define ITH_COMMAND_PIPE L"\\??\\pipe\\VNR_COMMAND"
|
||||||
|
|
||||||
|
// Sections
|
||||||
|
|
||||||
|
#define ITH_SECTION_ L"VNR_SECTION_" // _%d
|
||||||
|
|
||||||
|
// Mutex
|
||||||
|
|
||||||
|
// jichi 7/12/2015:
|
||||||
|
// ITH IO name prefix, needed by Windows 10 for NT event and mutex APIs
|
||||||
|
// Otherwise, NT functions will return status = STATUS_OBJECT_PATH_SYNTAX_BAD
|
||||||
|
//#define ITH_PATH_ L"\\BaseNamedObjects\\"
|
||||||
|
#define ITH_PATH_ L""
|
||||||
|
|
||||||
|
#define ITH_PROCESS_MUTEX_ ITH_PATH_ L"VNR_PROCESS_" // ITH_%d
|
||||||
|
#define ITH_HOOKMAN_MUTEX_ ITH_PATH_ L"VNR_HOOKMAN_" // ITH_HOOKMAN_%d
|
||||||
|
#define ITH_DETACH_MUTEX_ ITH_PATH_ L"VNR_DETACH_" // ITH_DETACH_%d
|
||||||
|
|
||||||
|
#define ITH_GRANTPIPE_MUTEX ITH_PATH_ L"VNR_GRANT_PIPE" // ITH_GRANT_PIPE
|
||||||
|
|
||||||
|
#define ITH_CLIENT_MUTEX ITH_PATH_ L"VNR_CLIENT" // ITH_DLL_RUNNING
|
||||||
|
#define ITH_SERVER_MUTEX ITH_PATH_ L"VNR_SERVER" // ITH_RUNNING
|
||||||
|
#define ITH_SERVER_HOOK_MUTEX ITH_PATH_ L"VNR_SERVER_HOOK" // original
|
||||||
|
|
||||||
|
// Events
|
||||||
|
|
||||||
|
#define ITH_REMOVEHOOK_EVENT ITH_PATH_ L"VNR_REMOVE_HOOK" // ITH_REMOVE_HOOK
|
||||||
|
#define ITH_MODIFYHOOK_EVENT ITH_PATH_ L"VNR_MODIFY_HOOK" // ITH_MODIFY_HOOK
|
||||||
|
#define ITH_PIPEEXISTS_EVENT ITH_PATH_ L"VNR_PIPE_EXISTS" // ITH_PIPE_EXIST
|
||||||
|
|
||||||
|
// EOF
|
91
vnr/vnrhook/include/types.h
Normal file
91
vnr/vnrhook/include/types.h
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// vnrhook/types.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// Branch: ITH/common.h, rev 128
|
||||||
|
|
||||||
|
#include <windows.h> // needed for windef types
|
||||||
|
|
||||||
|
/** jichi 3/7/2014: Add guessed comment
|
||||||
|
*
|
||||||
|
* DWORD addr absolute or relative address
|
||||||
|
* DWORD split esp offset of the split character
|
||||||
|
*
|
||||||
|
* http://faydoc.tripod.com/cpu/pushad.htm
|
||||||
|
* http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
|
||||||
|
* The order is the same as pushd
|
||||||
|
* EAX, ECX, EDX, EBX, ESP (original value), EBP, ESI, and EDI (if the current operand-size attribute is 32) and AX, CX, DX, BX, SP
|
||||||
|
* Negative values of 'data_offset' and 'sub_offset' refer to registers:-4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI
|
||||||
|
*/
|
||||||
|
struct HookParam {
|
||||||
|
// jichi 8/24/2013: For special hooks.
|
||||||
|
typedef void (*text_fun_t)(DWORD esp, HookParam *hp, BYTE index, DWORD *data, DWORD *split, DWORD *len);
|
||||||
|
|
||||||
|
// jichi 10/24/2014: Add filter function. Return the if skip the text
|
||||||
|
typedef bool (*filter_fun_t)(LPVOID str, DWORD *len, HookParam *hp, BYTE index);
|
||||||
|
|
||||||
|
// jichi 10/24/2014: Add generic hook function, return false if stop execution.
|
||||||
|
typedef bool (*hook_fun_t)(DWORD esp, HookParam *hp);
|
||||||
|
|
||||||
|
DWORD address; // absolute or relative address
|
||||||
|
DWORD offset, // offset of the data in the memory
|
||||||
|
index, // ?
|
||||||
|
split, // esp offset of the split character = pusha offset - 4
|
||||||
|
split_index; // ?
|
||||||
|
DWORD module, // hash of the module
|
||||||
|
function;
|
||||||
|
text_fun_t text_fun;
|
||||||
|
filter_fun_t filter_fun;
|
||||||
|
hook_fun_t hook_fun;
|
||||||
|
DWORD type; // flags
|
||||||
|
WORD length_offset; // index of the string length
|
||||||
|
BYTE hook_len, // ?
|
||||||
|
recover_len; // ?
|
||||||
|
|
||||||
|
// 2/2/2015: jichi number of times - 1 to run the hook
|
||||||
|
BYTE extra_text_count;
|
||||||
|
BYTE _unused; // jichi 2/2/2015: add a BYTE type to make to total sizeof(HookParam) even.
|
||||||
|
|
||||||
|
// 7/20/2014: jichi additional parameters for PSP games
|
||||||
|
DWORD user_flags,
|
||||||
|
user_value;
|
||||||
|
};
|
||||||
|
|
||||||
|
// jichi 6/1/2014: Structure of the esp for extern functions
|
||||||
|
struct HookStack
|
||||||
|
{
|
||||||
|
// pushad
|
||||||
|
DWORD edi, // -0x24
|
||||||
|
esi, // -0x20
|
||||||
|
ebp, // -0x1c
|
||||||
|
esp, // -0x18
|
||||||
|
ebx, // -0x14
|
||||||
|
edx, // -0x10
|
||||||
|
ecx, // -0xc
|
||||||
|
eax; // -0x8
|
||||||
|
// pushfd
|
||||||
|
DWORD eflags; // -0x4
|
||||||
|
DWORD retaddr; // 0
|
||||||
|
DWORD args[1]; // 0x4
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SendParam {
|
||||||
|
DWORD type;
|
||||||
|
HookParam hp;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Hook { // size: 0x80
|
||||||
|
HookParam hp;
|
||||||
|
LPSTR hook_name;
|
||||||
|
int name_length;
|
||||||
|
BYTE recover[0x68 - sizeof(HookParam)];
|
||||||
|
BYTE original[0x10];
|
||||||
|
|
||||||
|
DWORD Address() const { return hp.address; }
|
||||||
|
DWORD Type() const { return hp.type; }
|
||||||
|
WORD Length() const { return hp.hook_len; }
|
||||||
|
LPSTR Name() const { return hook_name; }
|
||||||
|
int NameLength() const { return name_length; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
21158
vnr/vnrhook/src/engine/engine.cc
Normal file
21158
vnr/vnrhook/src/engine/engine.cc
Normal file
File diff suppressed because it is too large
Load Diff
170
vnr/vnrhook/src/engine/engine.h
Normal file
170
vnr/vnrhook/src/engine/engine.h
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// engine/engine.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
struct HookParam; // defined in ith types.h
|
||||||
|
|
||||||
|
namespace Engine {
|
||||||
|
|
||||||
|
// Global variables
|
||||||
|
extern wchar_t process_name_[MAX_PATH], // cached
|
||||||
|
process_path_[MAX_PATH]; // cached
|
||||||
|
extern DWORD module_base_,
|
||||||
|
module_limit_;
|
||||||
|
|
||||||
|
//extern LPVOID trigger_addr;
|
||||||
|
typedef bool (* trigger_fun_t)(LPVOID addr, DWORD frame, DWORD stack);
|
||||||
|
extern trigger_fun_t trigger_fun_;
|
||||||
|
|
||||||
|
bool InsertMonoHooks(); // Mono
|
||||||
|
|
||||||
|
// Wii engines
|
||||||
|
|
||||||
|
bool InsertGCHooks(); // Dolphin
|
||||||
|
bool InsertVanillawareGCHook();
|
||||||
|
|
||||||
|
// PS2 engines
|
||||||
|
|
||||||
|
bool InsertPCSX2Hooks(); // PCSX2
|
||||||
|
bool InsertMarvelousPS2Hook(); // http://marvelous.jp
|
||||||
|
bool InsertMarvelous2PS2Hook(); // http://marvelous.jp
|
||||||
|
bool InsertTypeMoonPS2Hook(); // http://typemoon.com
|
||||||
|
//bool InsertNamcoPS2Hook();
|
||||||
|
|
||||||
|
// PSP engines
|
||||||
|
|
||||||
|
void SpecialPSPHook(DWORD esp_base, HookParam *hp, DWORD *data, DWORD *split, DWORD *len); // General PSP extern hook
|
||||||
|
|
||||||
|
bool InsertPPSSPPHooks(); // PPSSPPWindows
|
||||||
|
|
||||||
|
bool InsertPPSSPPHLEHooks();
|
||||||
|
bool InsertOtomatePPSSPPHook(); // PSP otomate.jp, 0.9.9.0 only
|
||||||
|
|
||||||
|
bool Insert5pbPSPHook(); // PSP 5pb.jp
|
||||||
|
bool InsertAlchemistPSPHook(); // PSP Alchemist-net.co.jp, 0.9.8 only
|
||||||
|
bool InsertAlchemist2PSPHook(); // PSP Alchemist-net.co.jp
|
||||||
|
bool InsertBandaiNamePSPHook(); // PSP Bandai.co.jp
|
||||||
|
bool InsertBandaiPSPHook(); // PSP Bandai.co.jp
|
||||||
|
bool InsertBroccoliPSPHook(); // PSP Broccoli.co.jp
|
||||||
|
bool InsertFelistellaPSPHook(); // PSP felistella.co.jp
|
||||||
|
|
||||||
|
bool InsertCyberfrontPSPHook(); // PSP CYBERFRONT (closed)
|
||||||
|
bool InsertImageepochPSPHook(); // PSP Imageepoch.co.jp
|
||||||
|
bool InsertImageepoch2PSPHook();// PSP Imageepoch.co.jp
|
||||||
|
bool InsertKadokawaNamePSPHook(); // PSP Kadokawa.co.jp
|
||||||
|
bool InsertKonamiPSPHook(); // PSP Konami.jp
|
||||||
|
bool InsertTecmoPSPHook(); // PSP Koeitecmo.co.jp
|
||||||
|
//bool InsertTypeMoonPSPHook(); // PSP Typemoon.com
|
||||||
|
|
||||||
|
bool InsertOtomatePSPHook(); // PSP Otomate.jp, 0.9.8 only
|
||||||
|
//bool InsertOtomate2PSPHook(); // PSP otomate.jp >= 0.9.9.1
|
||||||
|
|
||||||
|
bool InsertIntensePSPHook(); // PSP Intense.jp
|
||||||
|
bool InsertKidPSPHook(); // PSP Kid-game.co.jp
|
||||||
|
bool InsertNippon1PSPHook(); // PSP Nippon1.jp
|
||||||
|
bool InsertNippon2PSPHook(); // PSP Nippon1.jp
|
||||||
|
bool InsertYetiPSPHook(); // PSP Yetigame.jp
|
||||||
|
bool InsertYeti2PSPHook(); // PSP Yetigame.jp
|
||||||
|
|
||||||
|
// Game-speicific engines
|
||||||
|
bool InsertShinyDaysGameHook(); // ShinyDays
|
||||||
|
bool InsertLovaGameHook(); // lova.jp
|
||||||
|
|
||||||
|
// PC engines
|
||||||
|
|
||||||
|
bool Insert2RMHook(); // 2RM - Adventure Engine
|
||||||
|
bool Insert5pbHook(); // 5pb.jp, PSP/PS3 games ported to PC
|
||||||
|
bool InsertAB2TryHook(); // Yane@AkabeiSoft2Try: YaneSDK.dll.
|
||||||
|
bool InsertAbelHook(); // Abel
|
||||||
|
bool InsertAdobeAirHook(); // Adobe AIR
|
||||||
|
bool InsertAdobeFlash10Hook(); // Adobe Flash Player 10
|
||||||
|
bool InsertAliceHook(); // System40@AliceSoft; do not work for latest alice games
|
||||||
|
//bool InsertAmuseCraftHook(); // AMUSE CRAFT: *.pac
|
||||||
|
bool InsertAnex86Hook(); // Anex86: anex86.exe
|
||||||
|
bool InsertAOSHook(); // AOS: *.aos
|
||||||
|
bool InsertApricoTHook(); // Apricot: arc.a*
|
||||||
|
bool InsertArtemisHook(); // Artemis Engine: *.pfs
|
||||||
|
bool InsertAtelierHook(); // Atelier Kaguya: message.dat
|
||||||
|
bool InsertBGIHook(); // BGI: BGI.*
|
||||||
|
bool InsertBootupHook(); // Bootup: Bootup.dat
|
||||||
|
bool InsertC4Hook(); // C4: C4.EXE or XEX.EXE
|
||||||
|
bool InsertCaramelBoxHook(); // Caramel: *.bin
|
||||||
|
bool InsertCandyHook(); // SystemC@CandySoft: *.fpk
|
||||||
|
bool InsertCatSystemHook(); // CatSystem2: *.int
|
||||||
|
bool InsertCMVSHook(); // CMVS: data/pack/*.cpz; do not support the latest cmvs32.exe and cmvs64.exe
|
||||||
|
bool InsertCotophaHook(); // Cotopha: *.noa
|
||||||
|
bool InsertDebonosuHook(); // Debonosu: bmp.bak and dsetup.dll
|
||||||
|
bool InsertEaglsHook(); // E.A.G.L.S: EAGLES.dll
|
||||||
|
bool InsertEMEHook(); // EmonEngine: emecfg.ecf
|
||||||
|
bool InsertEscudeHook(); // Escude
|
||||||
|
bool InsertEushullyHook(); // Eushully: AGERC.DLL
|
||||||
|
bool InsertExpHook(); // EXP: http://www.exp-inc.jp
|
||||||
|
bool InsertFocasLensHook(); // FocasLens: Dat/*.arc, http://www.fo-lens.net
|
||||||
|
bool InsertGXPHook(); // GXP: *.gxp
|
||||||
|
bool InsertHorkEyeHook(); // HorkEye: resource string
|
||||||
|
bool InsertKAGParserHook(); // plugin/KAGParser.dll
|
||||||
|
bool InsertKAGParserExHook(); // plugin/KAGParserEx.dll
|
||||||
|
bool InsertKiriKiriHook(); // KiriKiri: *.xp3, resource string
|
||||||
|
bool InsertKiriKiriZHook(); // KiriKiri: *.xp3, resource string
|
||||||
|
bool InsertLeafHook(); // Leaf: *.pak
|
||||||
|
bool InsertLiveHook(); // Live: live.dll
|
||||||
|
bool InsertLunaSoftHook(); // LunaSoft: Pac/*.pac
|
||||||
|
bool InsertMalieHook(); // Malie@light: malie.ini
|
||||||
|
bool InsertMajiroHook(); // Majiro: *.arc
|
||||||
|
bool InsertMarineHeartHook(); // Marine Heart: SAISYS.exe
|
||||||
|
bool InsertMBLHook(); // MBL: *.mbl
|
||||||
|
bool InsertMEDHook(); // MED: *.med
|
||||||
|
bool InsertMinkHook(); // Mink: *.at2
|
||||||
|
//bool InsertMonoHook(); // Mono (Unity3D): */Mono/mono.dll
|
||||||
|
bool InsertNeXASHook(); // NeXAS: Thumbnail.pac
|
||||||
|
bool InsertNextonHook(); // NEXTON: aInfo.db
|
||||||
|
bool InsertNexton1Hook();
|
||||||
|
bool InsertNitroplusHook(); // Nitroplus: *.npa
|
||||||
|
bool InsertPalHook(); // AMUSE CRAFT: *.pac
|
||||||
|
bool InsertPensilHook(); // Pensil: PSetup.exe
|
||||||
|
bool InsertQLIEHook(); // QLiE: GameData/*.pack
|
||||||
|
//bool InsertRai7Hook(); // Rai7puk: rai7.exe
|
||||||
|
bool InsertRejetHook(); // Rejet: Module/{gd.dat,pf.dat,sd.dat}
|
||||||
|
bool InsertRUGPHook(); // rUGP: rUGP.exe
|
||||||
|
bool InsertRetouchHook(); // Retouch: resident.dll
|
||||||
|
bool InsertRREHook(); // RunrunEngine: rrecfg.rcf
|
||||||
|
bool InsertShinaHook(); // ShinaRio: Rio.ini
|
||||||
|
bool InsertElfHook(); // elf: Silky.exe
|
||||||
|
bool InsertScenarioPlayerHook();// sol-fa-soft: *.iar && *.sec5
|
||||||
|
bool InsertSiglusHook(); // SiglusEngine: SiglusEngine.exe
|
||||||
|
bool InsertSideBHook(); // SideB: Copyright side-B
|
||||||
|
bool InsertSilkysHook(); // SilkysPlus
|
||||||
|
bool InsertSyuntadaHook(); // Syuntada: dSoh.dat
|
||||||
|
bool InsertSystem43Hook(); // System43@AliceSoft: AliceStart.ini
|
||||||
|
bool InsertSystemAoiHook(); // SystemAoi: *.vfs
|
||||||
|
bool InsertTamamoHook(); // Tamamo
|
||||||
|
bool InsertTanukiHook(); // Tanuki: *.tak
|
||||||
|
bool InsertTaskforce2Hook(); // Taskforce2.exe
|
||||||
|
bool InsertTencoHook(); // Tenco: Check.mdx
|
||||||
|
bool InsertTriangleHook(); // Triangle: Execle.exe
|
||||||
|
bool InsertUnicornHook(); // Gsen18: *.szs|Data/*.szs
|
||||||
|
bool InsertWillPlusHook(); // WillPlus: Rio.arc
|
||||||
|
bool InsertWolfHook(); // Wolf: Data.wolf
|
||||||
|
bool InsertYukaSystem2Hook(); // YukaSystem2: *.ykc
|
||||||
|
bool InsertYurisHook(); // YU-RIS: *.ypf
|
||||||
|
|
||||||
|
void InsertBrunsHook(); // Bruns: bruns.exe
|
||||||
|
void InsertIronGameSystemHook();// IroneGameSystem: igs_sample.exe
|
||||||
|
void InsertLucifenHook(); // Lucifen@Navel: *.lpk
|
||||||
|
void InsertRyokuchaHook(); // Ryokucha: _checksum.exe
|
||||||
|
void InsertRealliveHook(); // RealLive: RealLive*.exe
|
||||||
|
void InsertStuffScriptHook(); // Stuff: *.mpk
|
||||||
|
void InsertTinkerBellHook(); // TinkerBell: arc00.dat
|
||||||
|
void InsertWaffleHook(); // WAFFLE: cg.pak
|
||||||
|
|
||||||
|
// CIRCUS: avdata/
|
||||||
|
bool InsertCircusHook1();
|
||||||
|
bool InsertCircusHook2();
|
||||||
|
|
||||||
|
} // namespace Engine
|
||||||
|
|
||||||
|
// EOF
|
12
vnr/vnrhook/src/engine/hookdefs.h
Normal file
12
vnr/vnrhook/src/engine/hookdefs.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// engine/hookdefs.h
|
||||||
|
// 7/20/2014 jichi
|
||||||
|
|
||||||
|
// For HookParam user flags
|
||||||
|
enum HookParamFlag : unsigned long {
|
||||||
|
HPF_Null = 0 // never used
|
||||||
|
, HPF_IgnoreSameAddress = 1 // ignore the last same text address
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
936
vnr/vnrhook/src/engine/match.cc
Normal file
936
vnr/vnrhook/src/engine/match.cc
Normal file
@ -0,0 +1,936 @@
|
|||||||
|
// match.cc
|
||||||
|
// 8/9/2013 jichi
|
||||||
|
// Branch: ITH_Engine/engine.cpp, revision 133
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning (disable:4100) // C4100: unreference formal parameter
|
||||||
|
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
#include "src/engine/match.h"
|
||||||
|
#include "src/engine/engine.h"
|
||||||
|
#include "src/engine/pchooks.h"
|
||||||
|
#include "src/util/growl.h"
|
||||||
|
#include "src/util/util.h"
|
||||||
|
#include "src/main.h"
|
||||||
|
#include "src/except.h"
|
||||||
|
#include "ithsys/ithsys.h"
|
||||||
|
#include "ccutil/ccmacro.h"
|
||||||
|
|
||||||
|
//#define ConsoleOutput(...) (void)0 // jichi 8/18/2013: I don't need ConsoleOutput
|
||||||
|
|
||||||
|
enum { MAX_REL_ADDR = 0x200000 }; // jichi 8/18/2013: maximum relative address
|
||||||
|
|
||||||
|
// - Global variables -
|
||||||
|
|
||||||
|
namespace Engine {
|
||||||
|
|
||||||
|
WCHAR process_name_[MAX_PATH], // cached
|
||||||
|
process_path_[MAX_PATH]; // cached
|
||||||
|
|
||||||
|
DWORD module_base_,
|
||||||
|
module_limit_;
|
||||||
|
|
||||||
|
//LPVOID trigger_addr;
|
||||||
|
trigger_fun_t trigger_fun_;
|
||||||
|
|
||||||
|
} // namespace Engine
|
||||||
|
|
||||||
|
// - Methods -
|
||||||
|
|
||||||
|
namespace Engine { namespace { // unnamed
|
||||||
|
|
||||||
|
bool DetermineGameHooks() // 7/19/2015
|
||||||
|
{
|
||||||
|
#if 0 // jichi 7/19/2015: Disabled as it will crash the game
|
||||||
|
if (IthFindFile(L"UE3ShaderCompileWorker.exe") && IthFindFile(L"awesomium_process.exe")) {
|
||||||
|
InsertLovaGameHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif // 0
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 7/17/2014: Disable GDI hooks for PPSSPP
|
||||||
|
bool DeterminePCEngine()
|
||||||
|
{
|
||||||
|
if (DetermineGameHooks()) {
|
||||||
|
ConsoleOutput("vnreng: found game-specific hook");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IthFindFile(L"PPSSPP*.exe")) { // jichi 7/12/2014 PPSSPPWindows.exe, PPSSPPEX.exe PPSSPPSP.exe
|
||||||
|
InsertPPSSPPHooks();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IthFindFile(L"pcsx2*.exe")) { // jichi 7/19/2014 PCSX2.exe or PCSX2WX.exe
|
||||||
|
InsertPCSX2Hooks();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IthFindFile(L"Dolphin.exe")) { // jichi 7/20/2014
|
||||||
|
InsertGCHooks();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 5/14/2015: Skip hijacking BALDRSKY ZEROs
|
||||||
|
if (IthCheckFile(L"bsz_Data\\Mono\\mono.dll") || IthCheckFile(L"bsz2_Data\\Mono\\mono.dll")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE BALDRSKY ZEROs");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (::GetModuleHandleA("mono.dll")) {
|
||||||
|
InsertMonoHooks();
|
||||||
|
|
||||||
|
// 3/20/2015 jichi
|
||||||
|
// Always insert GDI hooks even for Mono games
|
||||||
|
// For example: ãæá¦?ãÆ? need GetGlyphOutlineA
|
||||||
|
PcHooks::hookGDIFunctions();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// PC games
|
||||||
|
PcHooks::hookGDIFunctions();
|
||||||
|
EnableGDIPlusHooks();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DetermineEngineByFile1()
|
||||||
|
{
|
||||||
|
if (IthFindFile(L"*.xp3") || Util::SearchResourceString(L"TVP(KIRIKIRI)")) {
|
||||||
|
if (Util::SearchResourceString(L"TVP(KIRIKIRI) Z ")) { // TVP(KIRIKIRI) Z CORE
|
||||||
|
// jichi 11/24/2014: Disabled that might crash VBH
|
||||||
|
//if (IthCheckFile(L"plugin\\KAGParser.dll"))
|
||||||
|
// InsertKAGParserHook();
|
||||||
|
//else if (IthCheckFile(L"plugin\\KAGParserEx.dll"))
|
||||||
|
// InsertKAGParserExHook();
|
||||||
|
if (InsertKiriKiriZHook())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
InsertKiriKiriHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 8/2/2014 jichi: Game name shown as 2RM - Adventure Engine, text also in GetGlyphOutlineA
|
||||||
|
if (Util::SearchResourceString(L"2RM") && Util::SearchResourceString(L"Adventure Engine")) {
|
||||||
|
Insert2RMHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 8/2/2014 jichi: Copyright is side-B, a conf.dat will be generated after the game is launched
|
||||||
|
// It also contains lua5.1.dll and lua5.dll
|
||||||
|
if (Util::SearchResourceString(L"side-B")) {
|
||||||
|
InsertSideBHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"bgi.*") || IthFindFile(L"BHVC.exe") || IthFindFile(L"sysgrp.arc")) {
|
||||||
|
InsertBGIHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"Bootup.dat") && InsertBootupHook()) // 5/22/2015 Bootup
|
||||||
|
// lstrlenW can also find text with repetition though
|
||||||
|
return true;
|
||||||
|
if (IthCheckFile(L"AGERC.DLL")) { // 6/1/2014 jichi: Eushully, AGE.EXE
|
||||||
|
InsertEushullyHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"data*.arc") && IthFindFile(L"stream*.arc")) {
|
||||||
|
InsertMajiroHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 5/31/2014
|
||||||
|
if (//IthCheckFile(L"Silkys.exe") || // It might or might not have Silkys.exe
|
||||||
|
// data, effect, layer, mes, music
|
||||||
|
IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"mes.arc")) {
|
||||||
|
InsertElfHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 6/9/2015: Skip Silkys Sakura
|
||||||
|
if ( // Almost the same as Silkys except mes.arc is replaced by Script.arc
|
||||||
|
IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"Script.arc")) {
|
||||||
|
InsertSilkysHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"data\\pack\\*.cpz")) {
|
||||||
|
InsertCMVSHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 10/12/2013: Restore wolf engine
|
||||||
|
// jichi 10/18/2013: Check for data/*.wolf
|
||||||
|
if (IthFindFile(L"data.wolf") || IthFindFile(L"data\\*.wolf")) {
|
||||||
|
InsertWolfHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"AdvData\\DAT\\NAMES.DAT")) {
|
||||||
|
InsertCircusHook1();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"AdvData\\GRP\\NAMES.DAT")) {
|
||||||
|
InsertCircusHook2();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.noa") || IthFindFile(L"data\\*.noa")) {
|
||||||
|
InsertCotophaHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.pfs")) { // jichi 10/1/2013
|
||||||
|
InsertArtemisHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.int")) {
|
||||||
|
InsertCatSystemHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"message.dat")) {
|
||||||
|
InsertAtelierHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"Check.mdx")) { // jichi 4/1/2014: AUGame
|
||||||
|
InsertTencoHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 12/25/2013: It may or may not be QLIE.
|
||||||
|
// AlterEgo also has GameData/sound.pack but is not QLIE
|
||||||
|
if (IthFindFile(L"GameData\\*.pack") && InsertQLIEHook())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (IthCheckFile(L"dll\\Pal.dll")) {
|
||||||
|
InsertPalHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IthFindFile(L"*.pac")) {
|
||||||
|
// jichi 6/3/2014: AMUSE CRAFT and SOFTPAL
|
||||||
|
// Selectively insert, so that lstrlenA can still get correct text if failed
|
||||||
|
//if (IthCheckFile(L"dll\\resource.dll") && IthCheckFile(L"dll\\pal.dll") && InsertAmuseCraftHook())
|
||||||
|
// return true;
|
||||||
|
|
||||||
|
if (IthCheckFile(L"Thumbnail.pac")) {
|
||||||
|
//ConsoleOutput("vnreng: IGNORE NeXAS");
|
||||||
|
InsertNeXASHook(); // jichi 7/6/2014: GIGA
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Util::SearchResourceString(L"SOFTPAL")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE SoftPal UNiSONSHIFT");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// jichi 12/27/2014: LunaSoft
|
||||||
|
if (IthFindFile(L"Pac\\*.pac")) {
|
||||||
|
InsertLunaSoftHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 9/16/2013: Add Gesen18
|
||||||
|
if (IthFindFile(L"*.szs") || IthFindFile(L"Data\\*.szs")) {
|
||||||
|
InsertUnicornHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 12/22/2013: Add rejet
|
||||||
|
if (IthCheckFile(L"gd.dat") && IthCheckFile(L"pf.dat") && IthCheckFile(L"sd.dat")) {
|
||||||
|
InsertRejetHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Only examined with version 1.0
|
||||||
|
//if (IthFindFile(L"Adobe AIR\\Versions\\*\\Adobe AIR.dll")) { // jichi 4/15/2014: FIXME: Wildcard not working
|
||||||
|
if (IthCheckFile(L"Adobe AIR\\Versions\\1.0\\Adobe AIR.dll")) { // jichi 4/15/2014: Adobe AIR
|
||||||
|
InsertAdobeAirHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DetermineEngineByFile2()
|
||||||
|
{
|
||||||
|
if (IthCheckFile(L"resident.dll")) {
|
||||||
|
InsertRetouchHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"Malie.ini") || IthCheckFile(L"Malie.exe")) { // jichi: 9/9/2014: Add malie.exe in case malie.ini is missing
|
||||||
|
InsertMalieHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"live.dll")) {
|
||||||
|
InsertLiveHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 9/5/2013 jichi
|
||||||
|
if (IthCheckFile(L"aInfo.db")) {
|
||||||
|
InsertNextonHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.lpk")) {
|
||||||
|
InsertLucifenHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"cfg.pak")) {
|
||||||
|
InsertWaffleHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"Arc00.dat")) {
|
||||||
|
InsertTinkerBellHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.vfs")) { // jichi 7/6/2014: Better to test AoiLib.dll? ja.wikipedia.org/wiki/«½«Õ«È«Ï«¦«¹««ã«é
|
||||||
|
InsertSystemAoiHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.mbl")) {
|
||||||
|
InsertMBLHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 8/1/2014: YU-RIS engine, lots of clockup game also has this pattern
|
||||||
|
if (IthFindFile(L"pac\\*.ypf") || IthFindFile(L"*.ypf")) {
|
||||||
|
// jichi 8/14/2013: CLOCLUP: "«Î?«Ö«ì«¹«ª«Ö«ê?«¸«å" would crash the game.
|
||||||
|
if (!IthCheckFile(L"noblesse.exe"))
|
||||||
|
InsertYurisHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.npa")) {
|
||||||
|
InsertNitroplusHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DetermineEngineByFile3()
|
||||||
|
{
|
||||||
|
//if (IthCheckFile(L"libscr.dll")) { // already checked
|
||||||
|
// InsertBrunsHook();
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// jichi 10/12/2013: Sample args.txt:
|
||||||
|
// See: http://tieba.baidu.com/p/2631413816
|
||||||
|
// -workdir
|
||||||
|
// .
|
||||||
|
// -loadpath
|
||||||
|
// .
|
||||||
|
// am.cfg
|
||||||
|
if (IthCheckFile(L"args.txt")) {
|
||||||
|
InsertBrunsHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"emecfg.ecf")) {
|
||||||
|
InsertEMEHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"rrecfg.rcf")) {
|
||||||
|
InsertRREHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.fpk") || IthFindFile(L"data\\*.fpk")) {
|
||||||
|
InsertCandyHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"arc.a*")) {
|
||||||
|
InsertApricoTHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.mpk")) {
|
||||||
|
InsertStuffScriptHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"Execle.exe")) {
|
||||||
|
InsertTriangleHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 2/28/2015: No longer work for "ÓÞïá¡¿??«¢«ê«¹ episode I" from Primula
|
||||||
|
//if (IthCheckFile(L"PSetup.exe")) {
|
||||||
|
// InsertPensilHook();
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
if (IthCheckFile(L"Yanesdk.dll")) {
|
||||||
|
InsertAB2TryHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.med")) {
|
||||||
|
InsertMEDHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DetermineEngineByFile4()
|
||||||
|
{
|
||||||
|
if (IthCheckFile(L"EAGLS.dll")) { // jichi 3/24/2014: E.A.G.L.S
|
||||||
|
//ConsoleOutput("vnreng: IGNORE EAGLS");
|
||||||
|
InsertEaglsHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"bmp.pak") && IthCheckFile(L"dsetup.dll")) {
|
||||||
|
// 1/1/2016 jich: skip izumo4 from studio ego that is not supported by debonosu
|
||||||
|
if (IthFindFile(L"*izumo4*.exe")) {
|
||||||
|
PcHooks::hookLstrFunctions();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
InsertDebonosuHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"C4.EXE") || IthCheckFile(L"XEX.EXE")) {
|
||||||
|
InsertC4Hook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"Rio.arc") && IthFindFile(L"Chip*.arc")) {
|
||||||
|
InsertWillPlusHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.tac")) {
|
||||||
|
InsertTanukiHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.gxp")) {
|
||||||
|
InsertGXPHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.aos")) { // jichi 4/2/2014: AOS hook
|
||||||
|
InsertAOSHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.at2")) { // jichi 12/23/2014: Mink, sample files: voice.at2, voice.det, voice.nme
|
||||||
|
InsertMinkHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"*.ykc")) { // jichi 7/15/2014: YukaSystem1 is not supported, though
|
||||||
|
//ConsoleOutput("vnreng: IGNORE YKC:Feng/HookSoft(SMEE)");
|
||||||
|
InsertYukaSystem2Hook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"model\\*.hed")) { // jichi 9/8/2014: EXP
|
||||||
|
InsertExpHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 2/6/2015 øÁäÌïÍ
|
||||||
|
// dPi.dat, dPih.dat, dSc.dat, dSch.dat, dSo.dat, dSoh.dat, dSy.dat
|
||||||
|
//if (IthCheckFile(L"dSoh.dat")) { // no idea why this file does not work
|
||||||
|
if (IthCheckFile(L"dSch.dat")) {
|
||||||
|
InsertSyuntadaHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 2/28/2015: Delay checking Pensil in case something went wrong
|
||||||
|
// File pattern observed in [Primula] ÓÞïá¡¿??«¢«ê«¹ episode I
|
||||||
|
// - PSetup.exe no longer exists
|
||||||
|
// - MovieTexture.dll information shows MovieTex dynamic library, copyright Pensil 2013
|
||||||
|
// - ta_trial.exe information shows 2XT - Primula Adventure Engine
|
||||||
|
if (IthCheckFile(L"PSetup.exe") || IthFindFile(L"PENCIL.*") || Util::SearchResourceString(L"2XT -")) {
|
||||||
|
InsertPensilHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DetermineEngineByProcessName()
|
||||||
|
{
|
||||||
|
WCHAR str[MAX_PATH];
|
||||||
|
wcscpy(str, process_name_);
|
||||||
|
_wcslwr(str); // lower case
|
||||||
|
|
||||||
|
if (wcsstr(str,L"reallive") || IthCheckFile(L"Reallive.exe") || IthCheckFile(L"REALLIVEDATA\\Start.ini")) {
|
||||||
|
InsertRealliveHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 8/19/2013: DO NOT WORK for games like¡¸«Ï«Ô«á«¢¡¹
|
||||||
|
//if (wcsstr(str,L"cmvs32") || wcsstr(str,L"cmvs64")) {
|
||||||
|
// InsertCMVSHook();
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// jichi 8/17/2013: Handle "~"
|
||||||
|
if (wcsstr(str, L"siglusengine") || !wcsncmp(str, L"siglus~", 7) || IthCheckFile(L"SiglusEngine.exe")) {
|
||||||
|
InsertSiglusHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wcsstr(str, L"taskforce2") || !wcsncmp(str, L"taskfo~", 7) || IthCheckFile(L"Taskforce2.exe")) {
|
||||||
|
InsertTaskforce2Hook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wcsstr(str,L"rugp") || IthCheckFile(L"rugp.exe")) {
|
||||||
|
InsertRUGPHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 8/17/2013: Handle "~"
|
||||||
|
if (wcsstr(str, L"igs_sample") || !wcsncmp(str, L"igs_sa~", 7) || IthCheckFile(L"igs_sample.exe")) {
|
||||||
|
InsertIronGameSystemHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wcsstr(str, L"bruns") || IthCheckFile(L"bruns.exe")) {
|
||||||
|
InsertBrunsHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wcsstr(str, L"anex86") || IthCheckFile(L"anex86.exe")) {
|
||||||
|
InsertAnex86Hook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 8/17/2013: Handle "~"
|
||||||
|
if (wcsstr(str, L"shinydays") || !wcsncmp(str, L"shinyd~", 7) || IthCheckFile(L"ShinyDays.exe")) {
|
||||||
|
InsertShinyDaysGameHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 10/3/2013: FIXME: Does not work
|
||||||
|
// Raise C0000005 even with admin priv
|
||||||
|
//if (wcsstr(str, L"bsz")) { // BALDRSKY ZERO
|
||||||
|
// InsertBaldrHook();
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (wcsstr(process_name_, L"SAISYS") || IthCheckFile(L"SaiSys.exe")) { // jichi 4/19/2014: Marine Heart
|
||||||
|
InsertMarineHeartHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DWORD len = wcslen(str);
|
||||||
|
|
||||||
|
// jichi 8/24/2013: Checking for Rio.ini or $procname.ini
|
||||||
|
//wcscpy(str+len-4, L"_?.war");
|
||||||
|
//if (IthFindFile(str)) {
|
||||||
|
// InsertShinaHook();
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
if (InsertShinaHook())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// jichi 8/10/2013: Since *.bin is common, move CaramelBox to the end
|
||||||
|
str[len - 3] = L'b';
|
||||||
|
str[len - 2] = L'i';
|
||||||
|
str[len - 1] = L'n';
|
||||||
|
str[len] = 0;
|
||||||
|
if ((IthCheckFile(str) || IthCheckFile(L"trial.bin")) // jichi 7/8/2014: add trial.bin
|
||||||
|
&& InsertCaramelBoxHook())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// jichi 7/23/2015 It also has gameexe.bin existed
|
||||||
|
if (IthCheckFile(L"configure.exe") && IthCheckFile(L"configure.cfg") && IthCheckFile(L"gfx.bin")) {
|
||||||
|
InsertEscudeHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This must appear at last since str is modified
|
||||||
|
wcscpy(str + len - 4, L"_checksum.exe");
|
||||||
|
if (IthCheckFile(str)) {
|
||||||
|
InsertRyokuchaHook();
|
||||||
|
|
||||||
|
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games
|
||||||
|
InsertScenarioPlayerHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DetermineEngineOther()
|
||||||
|
{
|
||||||
|
if (InsertAliceHook())
|
||||||
|
return true;
|
||||||
|
// jichi 1/19/2015: Disable inserting Lstr for System40
|
||||||
|
// See: http://sakuradite.com/topic/618
|
||||||
|
if (IthCheckFile(L"System40.ini")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE old System40.ini");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 12/26/2013: Add this after alicehook
|
||||||
|
if (IthCheckFile(L"AliceStart.ini")) {
|
||||||
|
InsertSystem43Hook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 8/24/2013: Move into functions
|
||||||
|
static BYTE static_file_info[0x1000];
|
||||||
|
if (IthGetFileInfo(L"*01", static_file_info))
|
||||||
|
if (*(DWORD*)static_file_info == 0) {
|
||||||
|
static WCHAR static_search_name[MAX_PATH];
|
||||||
|
LPWSTR name=(LPWSTR)(static_file_info+0x5E);
|
||||||
|
int len = wcslen(name);
|
||||||
|
name[len - 2] = L'*';
|
||||||
|
name[len - 1] = 0;
|
||||||
|
wcscpy(static_search_name, name);
|
||||||
|
IthGetFileInfo(static_search_name, static_file_info);
|
||||||
|
union {
|
||||||
|
FILE_BOTH_DIR_INFORMATION *both_info;
|
||||||
|
DWORD addr;
|
||||||
|
};
|
||||||
|
both_info = (FILE_BOTH_DIR_INFORMATION *)static_file_info;
|
||||||
|
//BYTE* ptr=static_file_info;
|
||||||
|
len = 0;
|
||||||
|
while (both_info->NextEntryOffset) {
|
||||||
|
addr += both_info->NextEntryOffset;
|
||||||
|
len++;
|
||||||
|
}
|
||||||
|
if (len > 3) {
|
||||||
|
InsertAbelHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 8/17/2014
|
||||||
|
// Put the patterns that might break other games at last
|
||||||
|
bool DetermineEngineAtLast()
|
||||||
|
{
|
||||||
|
if (IthCheckFile(L"MovieTexture.dll") && (InsertPensilHook() || Insert2RMHook())) // MovieTexture.dll also exists in 2RM games such as Ù½íäñ2??÷ú, which is checked first
|
||||||
|
return true;
|
||||||
|
if (IthFindFile(L"system") && IthFindFile(L"system.dat")) { // jichi 7/31/2015
|
||||||
|
InsertAbelHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"data\\*.cpk")) { // jichi 12/2/2014
|
||||||
|
Insert5pbHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// jichi 7/6/2014: named as ScenarioPlayer since resource string could be: scenario player program for xxx
|
||||||
|
// Do this at last as it is common
|
||||||
|
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) { // jichi 4/18/2014: Other game engine could also have *.iar such as Ryokucha
|
||||||
|
InsertScenarioPlayerHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
//if (IthCheckFile(L"arc0.dat") && IthCheckFile(L"script.dat") // jichi 11/14/2014: too common
|
||||||
|
if (Util::SearchResourceString(L"HorkEye")) { // appear in copyright: Copyright (C) HorkEye, http://horkeye.com
|
||||||
|
InsertHorkEyeHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthCheckFile(L"comnArc.arc") // jichi 8/17/2014: this file might exist in multiple files
|
||||||
|
&& InsertNexton1Hook()) // old nexton game
|
||||||
|
return true;
|
||||||
|
if (IthCheckFile(L"arc.dat") // jichi 9/27/2014: too common
|
||||||
|
&& InsertApricoTHook())
|
||||||
|
return true;
|
||||||
|
if (IthFindFile(L"*.pak") // jichi 12/25/2014: too common
|
||||||
|
&& InsertLeafHook())
|
||||||
|
return true;
|
||||||
|
// jichi 10/31/2014
|
||||||
|
// File description: Adobe Flash Player 10.2r153
|
||||||
|
// Product name: Shockwave Flash
|
||||||
|
// Original filename: SAFlashPlayer.exe
|
||||||
|
// Legal trademarks: Adobe Flash Player
|
||||||
|
// No idea why, this must appear at last or it will crash
|
||||||
|
if (Util::SearchResourceString(L"Adobe Flash Player 10")) {
|
||||||
|
InsertAdobeFlash10Hook(); // only v10 might be supported. Otherwise, fallback to Lstr hooks
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (IthFindFile(L"dat\\*.arc")) { // jichi 2/6/2015
|
||||||
|
InsertFocasLensHook(); // Touhou
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 8/23/2015: Tamamo
|
||||||
|
if (IthCheckFile(L"data.pck") && IthCheckFile(L"image.pck") && IthCheckFile(L"script.pck")) {
|
||||||
|
//if (IthCheckFile(L"QtGui.dll"))
|
||||||
|
InsertTamamoHook();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 6/1/2014
|
||||||
|
bool DetermineEngineGeneric()
|
||||||
|
{
|
||||||
|
bool ret = false;
|
||||||
|
|
||||||
|
if (IthCheckFile(L"AlterEgo.exe")) {
|
||||||
|
ConsoleOutput("vnreng: AlterEgo, INSERT WideChar hooks");
|
||||||
|
ret = true;
|
||||||
|
} else if (IthFindFile(L"data\\Sky\\*")) {
|
||||||
|
ConsoleOutput("vnreng: TEATIME, INSERT WideChar hooks");
|
||||||
|
ret = true;
|
||||||
|
}
|
||||||
|
//} else if (IthFindFile(L"image\\*.po2") || IthFindFile(L"image\\*.jo2")) {
|
||||||
|
// ConsoleOutput("vnreng: HarukaKanata, INSERT WideChar hooks"); // ªÏªëª«ª«ªÊª¿
|
||||||
|
// ret = true;
|
||||||
|
//}
|
||||||
|
if (ret)
|
||||||
|
PcHooks::hookWcharFunctions();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DetermineNoEngine()
|
||||||
|
{
|
||||||
|
//if (IthFindFile(L"*\\Managed\\UnityEngine.dll")) { // jichi 12/3/2013: Unity (BALDRSKY ZERO)
|
||||||
|
// ConsoleOutput("vnreng: IGNORE Unity");
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
//if (IthCheckFile(L"bsz_Data\\Managed\\UnityEngine.dll") || IthCheckFile(L"bsz2_Data\\Managed\\UnityEngine.dll")) {
|
||||||
|
// ConsoleOutput("vnreng: IGNORE Unity");
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
// jichi 6/7/2015: RPGMaker v3
|
||||||
|
if (IthFindFile(L"*.rgss3a")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE RPGMaker RGSS3");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 11/22/2015: ÔÐÌÈNECRO ??÷ú
|
||||||
|
if (IthFindFile(L"*.npk")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE new Nitroplus");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8/29/2015 jichi: minori, text in GetGlyphOutlineA
|
||||||
|
if (IthFindFile(L"*.paz")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE minori");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7/28/2015 jichi: Favorite games
|
||||||
|
if (IthFindFile(L"*.hcb")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE FVP");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 2/14/2015: Guilty+ £Ò£É£Î¡¿£Ó£Å£Î (PK)
|
||||||
|
if (IthCheckFile(L"rio.ini") || IthFindFile(L"*.war")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE unknown ShinaRio");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IthCheckFile(L"AdvHD.exe") || IthCheckFile(L"AdvHD.dll")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE Adv Player HD"); // supposed to be WillPlus
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IthCheckFile(L"ScrPlayer.exe")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE ScrPlayer");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IthCheckFile(L"nnnConfig2.exe")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE Nya NNNConfig");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 4/30/2015: Skip games made from ªéª¹ª³ª¦, such as ªÈª¢ªëìÑô£ªÎ«Í«È«é«ìÞÀï×
|
||||||
|
// It has garbage from lstrlenW. Correct text is supposed to be in TabbedTextOutA.
|
||||||
|
if (IthCheckFile(L"data_cg.dpm")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE DPM data_cg.dpm");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (IthCheckFile(L"AGERC.DLL")) { // jichi 3/17/2014: Eushully, AGE.EXE
|
||||||
|
// ConsoleOutput("vnreng: IGNORE Eushully");
|
||||||
|
// return true;
|
||||||
|
//}
|
||||||
|
|
||||||
|
if (IthCheckFile(L"game_sys.exe")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE Atelier Kaguya BY/TH");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IthFindFile(L"*.bsa")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE Bishop");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 3/19/2014: Escude game
|
||||||
|
// Example: bgm.bin gfx.bin maou.bin script.bin snd.bin voc.bin
|
||||||
|
if (IthCheckFile(L"gfx.bin") && IthCheckFile(L"snd.bin") && IthCheckFile(L"voc.bin")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE Escude");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 2/18/2015: Ignore if there is Nitro+ copyright
|
||||||
|
if (Util::SearchResourceString(L"Nitro+")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE unknown Nitro+");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 12/28/2014: "Chartreux Inc." in Copyright.
|
||||||
|
// Sublimary brands include Rosebleu, MORE, etc.
|
||||||
|
// GetGlyphOutlineA already works.
|
||||||
|
if (Util::SearchResourceString(L"Chartreux")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE Chartreux");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IthCheckFile(L"MovieTexture.dll")) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE MovieTexture");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wcsstr(process_name_, L"lcsebody") || !wcsncmp(process_name_, L"lcsebo~", 7) || IthFindFile(L"lcsebody*")) { // jichi 3/19/2014: LC-ScriptEngine, GetGlyphOutlineA
|
||||||
|
ConsoleOutput("vnreng: IGNORE lcsebody");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t str[MAX_PATH];
|
||||||
|
DWORD i;
|
||||||
|
for (i = 0; process_name_[i]; i++) {
|
||||||
|
str[i] = process_name_[i];
|
||||||
|
if (process_name_[i] == L'.')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*(DWORD *)(str + i + 1) = 0x630068; //.hcb
|
||||||
|
*(DWORD *)(str + i + 3) = 0x62;
|
||||||
|
if (IthCheckFile(str)) {
|
||||||
|
ConsoleOutput("vnreng: IGNORE FVP"); // jichi 10/3/2013: such like «¢«È«ê«¨ª«ª°ªä
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12/13/2013: Declare it in a way compatible to EXCEPTION_PROCEDURE
|
||||||
|
EXCEPTION_DISPOSITION ExceptHandler(PEXCEPTION_RECORD ExceptionRecord, LPVOID, PCONTEXT, LPVOID)
|
||||||
|
{
|
||||||
|
if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) {
|
||||||
|
module_limit_ = ExceptionRecord->ExceptionInformation[1];
|
||||||
|
//OutputDWORD(module_limit_);
|
||||||
|
__asm
|
||||||
|
{
|
||||||
|
mov eax,fs:[0x30] // jichi 12/13/2013: get PEB
|
||||||
|
mov eax,[eax+0xc]
|
||||||
|
mov eax,[eax+0xc]
|
||||||
|
mov ecx,module_limit_
|
||||||
|
sub ecx,module_base_
|
||||||
|
mov [eax+0x20],ecx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//ContextRecord->Esp = recv_esp;
|
||||||
|
//ContextRecord->Eip = recv_eip;
|
||||||
|
//return ExceptionContinueExecution; // jichi 3/11/2014: this will still crash. Not sure why ITH use this. Change to ExceptionContinueSearch
|
||||||
|
return ExceptionContinueSearch; // an unwind is in progress,
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 9/14/2013: Certain ITH functions like FindEntryAligned might raise exception without admin priv
|
||||||
|
// Return if succeeded.
|
||||||
|
bool UnsafeDetermineEngineType()
|
||||||
|
{
|
||||||
|
return DeterminePCEngine()
|
||||||
|
|| DetermineEngineByFile1()
|
||||||
|
|| DetermineEngineByFile2()
|
||||||
|
|| DetermineEngineByFile3()
|
||||||
|
|| DetermineEngineByFile4()
|
||||||
|
|| DetermineEngineByProcessName()
|
||||||
|
|| DetermineEngineOther()
|
||||||
|
|| DetermineEngineAtLast()
|
||||||
|
|| DetermineEngineGeneric()
|
||||||
|
|| DetermineNoEngine()
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
// jichi 10/21/2014: Return whether found the game engine
|
||||||
|
bool DetermineEngineType()
|
||||||
|
{
|
||||||
|
// jichi 9/27/2013: disable game engine for debugging use
|
||||||
|
#ifdef ITH_DISABLE_ENGINE
|
||||||
|
PcHooks::hookLstrFunctions();
|
||||||
|
PcHooks::hookCharNextFunctions();
|
||||||
|
return false;
|
||||||
|
#else
|
||||||
|
bool found = false;
|
||||||
|
#ifdef ITH_HAS_SEH
|
||||||
|
__try { found = UnsafeDetermineEngineType(); }
|
||||||
|
__except(ExceptHandler((GetExceptionInformation())->ExceptionRecord, 0, 0, 0)) {}
|
||||||
|
#else // use my own SEH
|
||||||
|
seh_with_eh(ExceptHandler,
|
||||||
|
found = UnsafeDetermineEngineType());
|
||||||
|
#endif // ITH_HAS_SEH
|
||||||
|
if (::GDIPlusHooksEnabled())
|
||||||
|
PcHooks::hookGDIPlusFunctions();
|
||||||
|
if (!found) { // jichi 10/2/2013: Only enable it if no game engine is detected
|
||||||
|
PcHooks::hookLstrFunctions();
|
||||||
|
PcHooks::hookCharNextFunctions();
|
||||||
|
} else
|
||||||
|
ConsoleOutput("vnreng: found game engine, IGNORE non gui hooks");
|
||||||
|
return found;
|
||||||
|
#endif // ITH_DISABLE_ENGINE
|
||||||
|
}
|
||||||
|
|
||||||
|
// __asm
|
||||||
|
// {
|
||||||
|
// mov eax,seh_recover
|
||||||
|
// mov recv_eip,eax
|
||||||
|
// push ExceptHandler
|
||||||
|
// push fs:[0]
|
||||||
|
// mov fs:[0],esp
|
||||||
|
// pushad
|
||||||
|
// mov recv_esp,esp
|
||||||
|
// }
|
||||||
|
// DetermineEngineType();
|
||||||
|
// status++;
|
||||||
|
// __asm
|
||||||
|
// {
|
||||||
|
//seh_recover:
|
||||||
|
// popad
|
||||||
|
// mov eax,[esp]
|
||||||
|
// mov fs:[0],eax
|
||||||
|
// add esp,8
|
||||||
|
// }
|
||||||
|
// if (status == 0)
|
||||||
|
// ConsoleOutput("Fail to identify engine type.");
|
||||||
|
// else
|
||||||
|
// ConsoleOutput("Initialized successfully.");
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
|
||||||
|
HANDLE hijackThread;
|
||||||
|
void hijackThreadProc(LPVOID lpThreadParameter)
|
||||||
|
{
|
||||||
|
CC_UNUSED(lpThreadParameter);
|
||||||
|
|
||||||
|
//static bool done = false;
|
||||||
|
//if (done)
|
||||||
|
// return;
|
||||||
|
//done = true;
|
||||||
|
|
||||||
|
// jichi 12/18/2013: Though FillRange could raise, it should never raise for he current process
|
||||||
|
// So, SEH is not used here.
|
||||||
|
Util::GetProcessName(process_name_); // Initialize shared process name
|
||||||
|
Util::GetProcessPath(process_path_); // Initialize shared process path
|
||||||
|
|
||||||
|
FillRange(process_name_, &module_base_, &module_limit_);
|
||||||
|
DetermineEngineType();
|
||||||
|
}
|
||||||
|
|
||||||
|
}} // namespace Engine unnamed
|
||||||
|
|
||||||
|
// - API -
|
||||||
|
|
||||||
|
DWORD Engine::InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
|
||||||
|
{ return trigger_fun_ ? !trigger_fun_(addr, frame, stack) : 0; }
|
||||||
|
|
||||||
|
void Engine::hijack()
|
||||||
|
{
|
||||||
|
if (!hijackThread) {
|
||||||
|
ConsoleOutput("vnreng: hijack process");
|
||||||
|
hijackThread = IthCreateThread(hijackThreadProc, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::terminate()
|
||||||
|
{
|
||||||
|
if (hijackThread) {
|
||||||
|
const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
|
||||||
|
NtWaitForSingleObject(hijackThread, 0, (PLARGE_INTEGER)&timeout);
|
||||||
|
NtClose(hijackThread);
|
||||||
|
hijackThread = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EOF
|
||||||
|
|
||||||
|
/*
|
||||||
|
extern "C" {
|
||||||
|
// http://gmogre3d.googlecode.com/svn-history/r815/trunk/OgreMain/src/WIN32/OgreMinGWSupport.cpp
|
||||||
|
// http://forum.osdev.org/viewtopic.php?f=8&t=22352
|
||||||
|
//#pragma data_seg()
|
||||||
|
//#pragma comment(linker, "/merge:.CRT=.data") // works fine in visual c++ 6
|
||||||
|
//#pragma data_seg()
|
||||||
|
//#pragma comment(linker, "/merge:.CRT=.rdata")
|
||||||
|
// MSVC libs use _chkstk for stack-probing. MinGW equivalent is _alloca.
|
||||||
|
//void _alloca();
|
||||||
|
//void _chkstk() { _alloca(); }
|
||||||
|
|
||||||
|
// MSVC uses security cookies to prevent some buffer overflow attacks.
|
||||||
|
// provide dummy implementations.
|
||||||
|
//void _fastcall __security_check_cookie(intptr_t i) {}
|
||||||
|
void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie) {}
|
||||||
|
}
|
||||||
|
*/
|
23
vnr/vnrhook/src/engine/match.h
Normal file
23
vnr/vnrhook/src/engine/match.h
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// engine/match.h
|
||||||
|
// 8/23/2013 jichi
|
||||||
|
// TODO: Clean up the interface to match game engines.
|
||||||
|
// Split the engine match logic out of hooks.
|
||||||
|
// Modify the game hook to allow replace functions for arbitary purpose
|
||||||
|
// instead of just extracting text.
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
namespace Engine {
|
||||||
|
|
||||||
|
// jichi 10/21/2014: Return whether found the engine
|
||||||
|
void hijack();
|
||||||
|
void terminate();
|
||||||
|
|
||||||
|
// jichi 10/21/2014: Return 0 if failed
|
||||||
|
DWORD InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack);
|
||||||
|
|
||||||
|
} // namespace Engine
|
||||||
|
|
||||||
|
// EOF
|
55
vnr/vnrhook/src/engine/mono/funcinfo.h
Normal file
55
vnr/vnrhook/src/engine/mono/funcinfo.h
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// mono/funcinfo.h
|
||||||
|
// 12/26/2014
|
||||||
|
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
|
||||||
|
// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
|
||||||
|
|
||||||
|
//#include "ith/import/mono/types.h"
|
||||||
|
|
||||||
|
// MonoString* mono_string_new (MonoDomain *domain,
|
||||||
|
// const char *text);
|
||||||
|
// MonoString* mono_string_new_len (MonoDomain *domain,
|
||||||
|
// const char *text,
|
||||||
|
// guint length);
|
||||||
|
// MonoString* mono_string_new_size (MonoDomain *domain,
|
||||||
|
// gint32 len);
|
||||||
|
// MonoString* mono_string_new_utf16 (MonoDomain *domain,
|
||||||
|
// const guint16 *text,
|
||||||
|
// gint32 len);
|
||||||
|
// MonoString* mono_string_from_utf16 (gunichar2 *data);
|
||||||
|
// mono_unichar2* mono_string_to_utf16 (MonoString *s);
|
||||||
|
// char* mono_string_to_utf8 (MonoString *s);
|
||||||
|
// gboolean mono_string_equal (MonoString *s1,
|
||||||
|
// MonoString *s2);
|
||||||
|
// guint mono_string_hash (MonoString *s);
|
||||||
|
// MonoString* mono_string_intern (MonoString *str);
|
||||||
|
// MonoString* mono_string_is_interned (MonoString *o);
|
||||||
|
// MonoString* mono_string_new_wrapper (const char *text);
|
||||||
|
// gunichar2* mono_string_chars (MonoString *s);
|
||||||
|
// int mono_string_length (MonoString *s);
|
||||||
|
// gunichar2* mono_unicode_from_external (const gchar *in, gsize *bytes);
|
||||||
|
// gchar* mono_unicode_to_external (const gunichar2 *uni);
|
||||||
|
// gchar* mono_utf8_from_external (const gchar *in);
|
||||||
|
|
||||||
|
struct MonoFunction {
|
||||||
|
const char *functionName;
|
||||||
|
size_t textIndex; // argument index, starting from 0
|
||||||
|
size_t lengthIndex; // argument index, start from 0
|
||||||
|
unsigned long hookType; // HookParam type
|
||||||
|
void *text_fun; // HookParam::text_fun_t
|
||||||
|
};
|
||||||
|
|
||||||
|
#define MONO_FUNCTIONS_INITIALIZER \
|
||||||
|
{ "mono_string_to_utf8", 0, 0, USING_UNICODE|NO_CONTEXT, SpecialHookMonoString } \
|
||||||
|
, { "mono_string_to_utf8_checked", 0, 0, USING_UNICODE|NO_CONTEXT, SpecialHookMonoString } \
|
||||||
|
, { "mono_string_to_utf16", 0, 0, USING_UNICODE|NO_CONTEXT, SpecialHookMonoString } \
|
||||||
|
, { "mono_utf8_from_external", 1, 0, USING_STRING|USING_UTF8, nullptr } \
|
||||||
|
, { "mono_string_from_utf16", 1, 0, USING_UNICODE, nullptr } \
|
||||||
|
, { "mono_string_new_utf16", 2, 3, USING_UNICODE, nullptr } \
|
||||||
|
, { "mono_unicode_from_external", 1, 0, USING_UNICODE, nullptr } \
|
||||||
|
, { "mono_unicode_to_external", 1, 0, USING_UNICODE, nullptr }
|
||||||
|
//, { "mono_string_new", 2, 0, USING_STRING|USING_UTF8, nullptr }
|
||||||
|
//, { "mono_string_new_wrapper", 1, 0, USING_STRING|USING_UTF8, nullptr }
|
||||||
|
|
||||||
|
// EOF
|
41
vnr/vnrhook/src/engine/mono/types.h
Normal file
41
vnr/vnrhook/src/engine/mono/types.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// mono/types.h
|
||||||
|
// 12/26/2014
|
||||||
|
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
|
||||||
|
// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// mono/io-layer/uglify.h
|
||||||
|
typedef int8_t gint8;
|
||||||
|
typedef int32_t gint32;
|
||||||
|
typedef wchar_t gunichar2; // either char or wchar_t, depending on how mono is compiled
|
||||||
|
|
||||||
|
typedef gint8 mono_byte;
|
||||||
|
typedef gunichar2 mono_unichar2;
|
||||||
|
|
||||||
|
// mono/metadata/object.h
|
||||||
|
|
||||||
|
typedef mono_byte MonoBoolean;
|
||||||
|
|
||||||
|
struct MonoArray;
|
||||||
|
struct MonoDelegate;
|
||||||
|
struct MonoException;
|
||||||
|
struct MonoString;
|
||||||
|
struct MonoThreadsSync;
|
||||||
|
struct MonoThread;
|
||||||
|
struct MonoVTable;
|
||||||
|
|
||||||
|
struct MonoObject {
|
||||||
|
MonoVTable *vtable;
|
||||||
|
MonoThreadsSync *synchronisation;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MonoString {
|
||||||
|
MonoObject object;
|
||||||
|
gint32 length;
|
||||||
|
gunichar2 chars[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
// EOF
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user