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)
|
||||
|
||||
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(CPACK_GENERATOR "ZIP")
|
||||
set(CPACK_PACKAGE_VERSION_MAJOR 3)
|
||||
set(CPACK_PACKAGE_VERSION_MINOR 4152)
|
||||
set(CPACK_PACKAGE_VERSION_PATCH 0)
|
||||
set(CPACK_PACKAGE_VERSION_MINOR 5640)
|
||||
set(CPACK_PACKAGE_VERSION_PATCH 1)
|
||||
set(CPACK_SOURCE_GENERATOR "ZIP")
|
||||
set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\.svn/;/\\\\.bzr/;/\\\\.hg/;/\\\\.git/;\\\\.swp$;\\\\.#;/#" ".*\\\\.user$" "\\\\.gitignore$" "\\\\.gitmodules$" "\\\\.git$")
|
||||
include(CPack)
|
||||
@ -84,24 +33,20 @@ add_compile_options(
|
||||
)
|
||||
|
||||
add_definitions(
|
||||
-D_SECURE_SCL=0 # config.pri
|
||||
-D_SCL_SECURE_NO_WARNINGS # config.pri
|
||||
-D_CRT_SECURE_NO_WARNINGS # config.pri
|
||||
-DUNICODE # config.pri
|
||||
-D_UNICODE
|
||||
-D_CRT_NON_CONFORMING_SWPRINTFS # common.pri
|
||||
-DITH_HAS_CRT
|
||||
/D_SECURE_SCL=0 # config.pri
|
||||
/D_SCL_SECURE_NO_WARNINGS # config.pri
|
||||
/D_CRT_SECURE_NO_WARNINGS # config.pri
|
||||
/DUNICODE # config.pri
|
||||
/D_UNICODE
|
||||
/D_CRT_NON_CONFORMING_SWPRINTFS # common.pri
|
||||
/DITH_HAS_CRT
|
||||
)
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/vnr ${CMAKE_BINARY_DIR}/gui)
|
||||
|
||||
set(common_src
|
||||
vnr/ith/common/const.h
|
||||
vnr/ith/common/defs.h
|
||||
vnr/ith/common/except.h
|
||||
vnr/ith/common/growl.h
|
||||
vnr/ith/common/memory.h
|
||||
vnr/ith/common/types.h
|
||||
include_directories(
|
||||
.
|
||||
vnr
|
||||
vnr/texthook
|
||||
${CMAKE_BINARY_DIR}/gui
|
||||
)
|
||||
|
||||
set(resource_src
|
||||
@ -119,13 +64,8 @@ set(ithvnr_src
|
||||
gui/main.cpp
|
||||
gui/ProcessWindow.cpp
|
||||
gui/ProcessWindow.h
|
||||
gui/Profile.cpp
|
||||
gui/Profile.h
|
||||
gui/ProfileManager.cpp
|
||||
gui/ProfileManager.h
|
||||
gui/pugiconfig.hpp
|
||||
gui/pugixml.cpp
|
||||
gui/pugixml.hpp
|
||||
gui/resource.h
|
||||
gui/utility.cpp
|
||||
gui/utility.h
|
||||
@ -135,17 +75,15 @@ set(ithvnr_src
|
||||
gui/window.h
|
||||
gui/TextBuffer.cpp
|
||||
gui/TextBuffer.h
|
||||
${common_src}
|
||||
${resource_src}
|
||||
)
|
||||
|
||||
source_group("common" FILES ${common_src})
|
||||
|
||||
source_group("Resource Files" FILES ${resource_src})
|
||||
|
||||
add_executable(${PROJECT_NAME} ${ithvnr_src})
|
||||
|
||||
add_subdirectory(vnr)
|
||||
# add_subdirectory(profile)
|
||||
|
||||
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='*'\""
|
||||
@ -158,8 +96,9 @@ target_compile_definitions(${PROJECT_NAME}
|
||||
)
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}
|
||||
profile
|
||||
vnrhost
|
||||
vnrsys
|
||||
ithsys
|
||||
${WDK_HOME}/lib/wxp/i386/ntdll.lib
|
||||
comctl32.lib
|
||||
psapi.lib
|
||||
|
@ -16,6 +16,7 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "ITH.h"
|
||||
|
||||
typedef void (*CustomFilterCallBack) (WORD, PVOID);
|
||||
|
@ -15,7 +15,8 @@
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <windows.h>
|
||||
|
||||
#include <Windows.h>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
#include <ios>
|
||||
@ -29,9 +30,8 @@
|
||||
#include <map>
|
||||
#include <CommCtrl.h>
|
||||
#include <intrin.h>
|
||||
#include <WindowsX.h>
|
||||
#include <sstream>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include "pugixml.hpp"
|
||||
#include "profile/pugixml.hpp"
|
||||
#pragma warning(disable: 4146)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Generated by ResEdit 1.6.3
|
||||
// Copyright (C) 2006-2014
|
||||
// Generated by ResEdit 1.6.5
|
||||
// Copyright (C) 2006-2015
|
||||
// http://www.resedit.net
|
||||
|
||||
#include <windows.h>
|
||||
@ -7,9 +7,6 @@
|
||||
#include <richedit.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"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 1
|
||||
{
|
||||
DEFPUSHBUTTON "확인", IDOK, 281, 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
|
||||
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_BUTTON3, 116, 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
|
||||
DEFPUSHBUTTON "확인", IDOK, 281, 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
|
||||
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_BUTTON3, 116, 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
|
||||
}
|
||||
|
||||
|
||||
@ -40,22 +37,22 @@ STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYS
|
||||
CAPTION "Option"
|
||||
FONT 8, "MS Shell Dlg", 400, 0, 1
|
||||
{
|
||||
DEFPUSHBUTTON "확인", IDOK, 8, 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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_CHECK2, 62, 87, 50, 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_CHECK6, 7, 149, 81, 8, 0, WS_EX_LEFT
|
||||
AUTOCHECKBOX "글로벌 필터 활성화", IDC_CHECK5, 7, 134, 75, 10, 0, WS_EX_LEFT
|
||||
DEFPUSHBUTTON "확인", IDOK, 8, 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
|
||||
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
|
||||
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
|
||||
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
|
||||
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_CHECK2, 62, 87, 50, 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_CHECK6, 7, 149, 81, 8, 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
|
||||
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 "resource.h"
|
||||
#include "ith/host/srv.h"
|
||||
#include "ith/host/hookman.h"
|
||||
#include "host/host.h"
|
||||
#include "host/hookman.h"
|
||||
#include "ProfileManager.h"
|
||||
#include "Profile.h"
|
||||
#include "profile/Profile.h"
|
||||
|
||||
extern HookManager* man; // main.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);
|
||||
InitProcessDlg();
|
||||
RefreshProcess();
|
||||
EnableWindow(hbDetach, FALSE);
|
||||
EnableWindow(hbAttach, FALSE);
|
||||
}
|
||||
|
||||
void ProcessWindow::InitProcessDlg()
|
||||
@ -73,30 +75,33 @@ void ProcessWindow::RefreshProcess()
|
||||
void ProcessWindow::AttachProcess()
|
||||
{
|
||||
DWORD pid = GetSelectedPID();
|
||||
if (IHF_InjectByPID(pid) != -1)
|
||||
if (Host_InjectByPID(pid))
|
||||
{
|
||||
Host_HijackProcess(pid);
|
||||
RefreshThreadWithPID(pid, true);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessWindow::DetachProcess()
|
||||
{
|
||||
DWORD pid = GetSelectedPID();
|
||||
if (IHF_ActiveDetachProcess(pid) == 0)
|
||||
if (Host_ActiveDetachProcess(pid) == 0)
|
||||
RefreshThreadWithPID(pid, false);
|
||||
}
|
||||
|
||||
void ProcessWindow::AddCurrentToProfile()
|
||||
void ProcessWindow::CreateProfileForSelectedProcess()
|
||||
{
|
||||
DWORD pid = GetSelectedPID();
|
||||
auto path = GetProcessPath(pid);
|
||||
if (!path.empty())
|
||||
{
|
||||
Profile* pf = pfman->AddProfile(path, pid);
|
||||
pfman->FindProfileAndUpdateHookAddresses(pid, path);
|
||||
Profile* pf = pfman->CreateProfile(pid);
|
||||
pfman->SaveProfiles();
|
||||
RefreshThread(ListView_GetSelectionMark(hlProcess));
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessWindow::RemoveCurrentFromProfile()
|
||||
void ProcessWindow::DeleteProfileForSelectedProcess()
|
||||
{
|
||||
DWORD pid = GetSelectedPID();
|
||||
auto path = GetProcessPath(pid);
|
||||
@ -132,7 +137,7 @@ void ProcessWindow::RefreshThreadWithPID(DWORD pid, bool isAttached)
|
||||
|
||||
DWORD ProcessWindow::GetSelectedPID()
|
||||
{
|
||||
LVITEM item={};
|
||||
LVITEM item = {};
|
||||
item.mask = LVIF_PARAM;
|
||||
item.iItem = ListView_GetSelectionMark(hlProcess);
|
||||
ListView_GetItem(hlProcess, &item);
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "ITH.h"
|
||||
|
||||
class ProcessWindow
|
||||
@ -9,8 +10,8 @@ public:
|
||||
void RefreshProcess();
|
||||
void AttachProcess();
|
||||
void DetachProcess();
|
||||
void AddCurrentToProfile();
|
||||
void RemoveCurrentFromProfile();
|
||||
void CreateProfileForSelectedProcess();
|
||||
void DeleteProfileForSelectedProcess();
|
||||
void RefreshThread(int index);
|
||||
private:
|
||||
void RefreshThreadWithPID(DWORD pid, bool isAttached);
|
||||
|
@ -1,9 +1,11 @@
|
||||
#include "ProfileManager.h"
|
||||
#include "Profile.h"
|
||||
#include "ith/host/srv.h"
|
||||
#include "ith/host/hookman.h"
|
||||
#include "ith/common/types.h"
|
||||
#include "ith/common/const.h"
|
||||
#include "profile/Profile.h"
|
||||
#include "host/host.h"
|
||||
#include "host/hookman.h"
|
||||
#include "vnrhook/include/types.h"
|
||||
#include "vnrhook/include/const.h"
|
||||
#include "utility.h"
|
||||
#include "profile/misc.h"
|
||||
|
||||
extern HookManager* man; // main.cpp
|
||||
extern LONG auto_inject, auto_insert, inject_delay; // main.cpp
|
||||
@ -12,21 +14,16 @@ bool MonitorFlag;
|
||||
ProfileManager* pfman;
|
||||
|
||||
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))
|
||||
{
|
||||
LoadProfile();
|
||||
LoadProfiles();
|
||||
}
|
||||
|
||||
ProfileManager::~ProfileManager()
|
||||
{
|
||||
SaveProfile();
|
||||
SaveProfiles();
|
||||
WaitForSingleObject(hMonitorThread.get(), 0);
|
||||
}
|
||||
|
||||
@ -42,7 +39,7 @@ Profile* ProfileManager::GetProfile(DWORD pid)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool ProfileManager::AddProfile(pugi::xml_node game)
|
||||
bool ProfileManager::CreateProfile(pugi::xml_node game)
|
||||
{
|
||||
auto file = game.child(L"File");
|
||||
auto profile = game.child(L"Profile");
|
||||
@ -56,13 +53,17 @@ bool ProfileManager::AddProfile(pugi::xml_node game)
|
||||
auto pf = new Profile(title);
|
||||
if (!pf->XmlReadProfile(profile))
|
||||
return false;
|
||||
AddProfile(path.value(), profile_ptr(pf));
|
||||
return true;
|
||||
CSLock lock(cs);
|
||||
auto& oldProfile = profile_tree[path.value()];
|
||||
if (!oldProfile)
|
||||
oldProfile.swap(profile_ptr(pf));
|
||||
return true;
|
||||
}
|
||||
|
||||
Profile* ProfileManager::AddProfile(const std::wstring& path, DWORD pid)
|
||||
Profile* ProfileManager::CreateProfile(DWORD pid)
|
||||
{
|
||||
CSLock lock(cs);
|
||||
auto path = GetProcessPath(pid);
|
||||
auto& pf = profile_tree[path];
|
||||
if (!pf)
|
||||
{
|
||||
@ -72,15 +73,6 @@ Profile* ProfileManager::AddProfile(const std::wstring& path, DWORD pid)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
UniqueHandle hFile(IthCreateFile(L"ITH_Profile.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
|
||||
@ -112,10 +104,10 @@ void ProfileManager::LoadProfile()
|
||||
if (!root)
|
||||
return;
|
||||
for (auto game = root.begin(); game != root.end(); ++game)
|
||||
AddProfile(*game);
|
||||
CreateProfile(*game);
|
||||
}
|
||||
|
||||
void ProfileManager::SaveProfile()
|
||||
void ProfileManager::SaveProfiles()
|
||||
{
|
||||
pugi::xml_document doc;
|
||||
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));
|
||||
}
|
||||
|
||||
void ProfileManager::FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path)
|
||||
Profile* ProfileManager::GetProfile(const std::wstring& path)
|
||||
{
|
||||
if (path.empty())
|
||||
return;
|
||||
return nullptr;
|
||||
auto it = profile_tree.find(path);
|
||||
if (it == profile_tree.end())
|
||||
return;
|
||||
auto& pf = it->second;
|
||||
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);
|
||||
return nullptr;
|
||||
return it->second.get();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
DWORD ProfileManager::ProfileCount()
|
||||
DWORD ProfileManager::CountProfiles()
|
||||
{
|
||||
return profile_tree.size();
|
||||
}
|
||||
@ -194,7 +156,7 @@ DWORD WINAPI InjectThread(LPVOID lpThreadParameter)
|
||||
Sleep(inject_delay);
|
||||
if (man == NULL)
|
||||
return 0;
|
||||
DWORD status = IHF_InjectByPID(pid);
|
||||
DWORD status = Host_InjectByPID(pid);
|
||||
if (!auto_insert)
|
||||
return status;
|
||||
if (status == -1)
|
||||
@ -206,7 +168,10 @@ DWORD WINAPI InjectThread(LPVOID lpThreadParameter)
|
||||
SendParam sp;
|
||||
sp.type = 0;
|
||||
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;
|
||||
}
|
||||
@ -215,7 +180,7 @@ DWORD WINAPI MonitorThread(LPVOID lpThreadParameter)
|
||||
{
|
||||
while (MonitorFlag)
|
||||
{
|
||||
DWORD aProcesses[1024], cbNeeded, cProcesses;
|
||||
DWORD aProcesses[1024], cbNeeded, cProcesses;
|
||||
if (!EnumProcesses(aProcesses, sizeof(aProcesses), &cbNeeded))
|
||||
break;
|
||||
cProcesses = cbNeeded / sizeof(DWORD);
|
||||
@ -237,116 +202,17 @@ DWORD WINAPI MonitorThread(LPVOID lpThreadParameter)
|
||||
|
||||
DWORD SaveProcessProfile(DWORD pid)
|
||||
{
|
||||
const ProcessRecord* pr = man->GetProcessRecord(pid);
|
||||
if (pr == NULL)
|
||||
return 0;
|
||||
std::wstring path = GetProcessPath(pid);
|
||||
if (path.empty())
|
||||
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);
|
||||
if (pf != NULL)
|
||||
pf->Clear();
|
||||
else
|
||||
pf = pfman->AddProfile(path, pid);
|
||||
AddHooksToProfile(*pf, *pr);
|
||||
AddThreadsToProfile(*pf, *pr, pid);
|
||||
pf = pfman->CreateProfile(pid);
|
||||
pf->XmlReadProfile(profile_node);
|
||||
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
|
||||
|
||||
#include "ITH.h"
|
||||
#include "utility.h" // UniqueHandle, CriticalSection
|
||||
|
||||
@ -9,14 +10,14 @@ class ProfileManager
|
||||
public:
|
||||
ProfileManager();
|
||||
~ProfileManager();
|
||||
Profile* AddProfile(const std::wstring& path, DWORD pid);
|
||||
void DeleteProfile(const std::wstring& path);
|
||||
void LoadProfile();
|
||||
void SaveProfile();
|
||||
void FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path);
|
||||
Profile* CreateProfile(DWORD pid);
|
||||
Profile* GetProfile(DWORD pid);
|
||||
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);
|
||||
Profile* GetProfile(DWORD pid);
|
||||
DWORD ProfileCount();
|
||||
private:
|
||||
typedef std::unique_ptr<Profile> profile_ptr;
|
||||
typedef std::map<std::wstring, profile_ptr> profile_map;
|
||||
@ -24,8 +25,8 @@ private:
|
||||
ProfileManager(const ProfileManager&);
|
||||
ProfileManager operator=(const ProfileManager&);
|
||||
|
||||
bool AddProfile(pugi::xml_node game);
|
||||
Profile* AddProfile(const std::wstring& path, profile_ptr new_profile);
|
||||
DWORD CountProfiles();
|
||||
bool CreateProfile(pugi::xml_node game);
|
||||
void WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node doc);
|
||||
// locate profile with executable path
|
||||
profile_map profile_tree;
|
||||
|
@ -3,8 +3,8 @@
|
||||
DWORD WINAPI FlushThread(LPVOID lParam); // window.cpp
|
||||
|
||||
TextBuffer::TextBuffer(HWND edit) : hThread(IthCreateThread(FlushThread, (DWORD)this)),
|
||||
hEdit(edit),
|
||||
running(true)
|
||||
hEdit(edit),
|
||||
running(true)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "ITH.h"
|
||||
#include "utility.h" // UniqueHandle, CriticalSection
|
||||
|
||||
|
171
gui/command.cpp
171
gui/command.cpp
@ -16,157 +16,16 @@
|
||||
*/
|
||||
|
||||
#include "ITH.h"
|
||||
#include "ith/host/srv.h"
|
||||
#include "ith/common/const.h"
|
||||
#include "ith/common/types.h"
|
||||
#include "host/host.h"
|
||||
#include "vnrhook/include/const.h"
|
||||
#include "vnrhook/include/types.h"
|
||||
#include "language.h"
|
||||
#include "utility.h"
|
||||
#include "profile/misc.h"
|
||||
|
||||
extern HookManager* man;
|
||||
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)
|
||||
{
|
||||
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)))
|
||||
{
|
||||
pid = IHF_GetPIDByName(m[1].str().c_str());
|
||||
pid = Host_GetPIDByName(m[1].str().c_str());
|
||||
if (pid == 0)
|
||||
return 0;
|
||||
IHF_InjectByPID(pid);
|
||||
Host_InjectByPID(pid);
|
||||
}
|
||||
else if (regex_match(cmd, m, wregex(L"/p(\\d+)", wregex::icase)))
|
||||
{
|
||||
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 = {};
|
||||
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 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);
|
||||
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);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleOutput(L"알 수 없는 명령어. 도움말을 보시려면, :h 나 :help를 입력하세요.");
|
||||
ConsoleOutput(L"알 수 없는 명령어. 도움말을 보시려면, :h 나 :help를 입력하세요.");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
154
gui/language.cpp
154
gui/language.cpp
@ -14,44 +14,44 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
const wchar_t* Warning=L"경고!";
|
||||
const wchar_t* Warning = L"경고!";
|
||||
//command.cpp
|
||||
const wchar_t* ErrorSyntax=L"명령어 오류";
|
||||
const wchar_t* Usage = L"명령어:\r\n\
|
||||
const wchar_t* ErrorSyntax = L"명령어 오류";
|
||||
const wchar_t* Usage = L"명령어:\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\
|
||||
/P[{process_id|Nprocess_name}] //프로세스에 부착\r\n\
|
||||
로더 옵션:\r\n\
|
||||
/P[{process_id|Nprocess_name}] //프로세스에 부착\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\
|
||||
\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\
|
||||
\r\n\
|
||||
추가 사용자정의 후킹설정\r\n\
|
||||
추가 사용자정의 후킹설정\r\n\
|
||||
\r\n\
|
||||
후킹 종류 :\r\n\
|
||||
A - DBCS 문자\r\n\
|
||||
B - DBCS 문자(big-endian)\r\n\
|
||||
W - UCS2 문자\r\n\
|
||||
S - MBCS 문자열\r\n\
|
||||
Q - UTF-16 문자열\r\n\
|
||||
후킹 종류 :\r\n\
|
||||
A - DBCS 문자\r\n\
|
||||
B - DBCS 문자(big-endian)\r\n\
|
||||
W - UCS2 문자\r\n\
|
||||
S - MBCS 문자열\r\n\
|
||||
Q - UTF-16 문자열\r\n\
|
||||
\r\n\
|
||||
매개변수:\r\n\
|
||||
X - 하드웨어 구획점 사용\r\n\
|
||||
N - 문법을 사용하지 않음\r\n\
|
||||
매개변수:\r\n\
|
||||
X - 하드웨어 구획점 사용\r\n\
|
||||
N - 문법을 사용하지 않음\r\n\
|
||||
data_offset - stack offset to char / string pointer\r\n\
|
||||
drdo - add a level of indirection to data_offset\r\n\
|
||||
sub_offset - stack offset to subcontext\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\
|
||||
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\
|
||||
@ -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";
|
||||
|
||||
//inject.cpp
|
||||
const wchar_t* ErrorRemoteThread=L"원격 스레드를 생성할 수 없음.";
|
||||
const wchar_t* ErrorOpenProcess=L"프로세스를 열 수 없음.";
|
||||
const wchar_t* ErrorNoProcess=L"프로세스를 찾을 수 없음";
|
||||
const wchar_t* SelfAttach=L"ITH.exe에 부착하지 말아 주세요";
|
||||
const wchar_t* AlreadyAttach=L"프로세스가 이미 부착됨.";
|
||||
const wchar_t* FormatInject=L"프로세스 %d에 인젝션. 모듈 기반 %.8X";
|
||||
const wchar_t* ErrorRemoteThread = L"원격 스레드를 생성할 수 없음.";
|
||||
const wchar_t* ErrorOpenProcess = L"프로세스를 열 수 없음.";
|
||||
const wchar_t* ErrorNoProcess = L"프로세스를 찾을 수 없음";
|
||||
const wchar_t* SelfAttach = L"ITH.exe에 부착하지 말아 주세요";
|
||||
const wchar_t* AlreadyAttach = L"프로세스가 이미 부착됨.";
|
||||
const wchar_t* FormatInject = L"프로세스 %d에 인젝션. 모듈 기반 %.8X";
|
||||
//main.cpp
|
||||
const wchar_t* NotAdmin=L"SeDebugPrevilege을 활성화 할 수 없습니다. ITH가 제대로 작동하지 못합니다.\r\n\
|
||||
관리자 계정으로 실행하시거나 UAC를 끄시고 ITH를 실행해 주세요.";
|
||||
const wchar_t* NotAdmin = L"SeDebugPrevilege을 활성화 할 수 없습니다. ITH가 제대로 작동하지 못합니다.\r\n\
|
||||
관리자 계정으로 실행하시거나 UAC를 끄시고 ITH를 실행해 주세요.";
|
||||
//pipe.cpp
|
||||
const wchar_t* ErrorCreatePipe=L"텍스트 파이프를 생성할 수 없거나, 요청이 너무 많습니다.";
|
||||
const wchar_t* FormatDetach=L"프로세스 %d가 탈착됨.";
|
||||
const wchar_t* ErrorCmdQueueFull=L"명령어 대기열이 가득참.";
|
||||
const wchar_t* ErrorNoAttach=L"프로세스가 부착되지 않음.";
|
||||
const wchar_t* ErrorCreatePipe = L"텍스트 파이프를 생성할 수 없거나, 요청이 너무 많습니다.";
|
||||
const wchar_t* FormatDetach = L"프로세스 %d가 탈착됨.";
|
||||
const wchar_t* ErrorCmdQueueFull = L"명령어 대기열이 가득참.";
|
||||
const wchar_t* ErrorNoAttach = L"프로세스가 부착되지 않음.";
|
||||
|
||||
//profile.cpp
|
||||
const wchar_t* ErrorMonitor=L"프로세스를 감시할 수 없음.";
|
||||
const wchar_t* ErrorMonitor = L"프로세스를 감시할 수 없음.";
|
||||
//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\
|
||||
소스코드 <https://code.google.com/p/interactive-text-hooker/>\r\n\
|
||||
일반토론 <https://groups.google.com/forum/?fromgroups#!forum/interactive-text-hooker>\r\n\
|
||||
한글화 @mireado<https://twitter.com/mireado>";
|
||||
const wchar_t* BackgroundMsg=L"도움말을 보시려면, \"help\", \"도움말\"이나 \"도움\"을 입력하세요.";
|
||||
const wchar_t* ErrorLinkExist=L"연결이 존재함.";
|
||||
const wchar_t* ErrorCylicLink=L"연결실패. 순환연결은 허용되지 않습니다.";
|
||||
const wchar_t* FormatLink=L"출발스레드%.4x에서 도착스레드%.4x로 연결.";
|
||||
const wchar_t* ErrorLink=L"연결실패. 출발/도착 스레드를 찾을 수 없음.";
|
||||
const wchar_t* ErrorDeleteCombo=L"글상자에서 지우기 실패.";
|
||||
소스코드 <https://code.google.com/p/interactive-text-hooker/>\r\n\
|
||||
일반토론 <https://groups.google.com/forum/?fromgroups#!forum/interactive-text-hooker>\r\n\
|
||||
한글화 @mireado<https://twitter.com/mireado>";
|
||||
const wchar_t* BackgroundMsg = L"도움말을 보시려면, \"help\", \"도움말\"이나 \"도움\"을 입력하세요.";
|
||||
const wchar_t* ErrorLinkExist = L"연결이 존재함.";
|
||||
const wchar_t* ErrorCylicLink = L"연결실패. 순환연결은 허용되지 않습니다.";
|
||||
const wchar_t* FormatLink = L"출발스레드%.4x에서 도착스레드%.4x로 연결.";
|
||||
const wchar_t* ErrorLink = L"연결실패. 출발/도착 스레드를 찾을 수 없음.";
|
||||
const wchar_t* ErrorDeleteCombo = L"글상자에서 지우기 실패.";
|
||||
|
||||
//window.cpp
|
||||
const wchar_t* ClassName=L"ITH";
|
||||
const wchar_t* ClassNameAdmin=L"ITH (관리자)";
|
||||
const wchar_t* ErrorNotSplit=L"먼저 문단 나누기를 활성화해주세요!";
|
||||
const wchar_t* ErrorNotModule=L"먼저 모듈을 활성화해주세요!";
|
||||
const wchar_t* ClassName = L"ITH";
|
||||
const wchar_t* ClassNameAdmin = L"ITH (관리자)";
|
||||
const wchar_t* ErrorNotSplit = L"먼저 문단 나누기를 활성화해주세요!";
|
||||
const wchar_t* ErrorNotModule = L"먼저 모듈을 활성화해주세요!";
|
||||
//Main window buttons
|
||||
const wchar_t* ButtonTitleProcess=L"프로세스";
|
||||
const wchar_t* ButtonTitleThread=L"스레드";
|
||||
const wchar_t* ButtonTitleHook=L"후킹";
|
||||
const wchar_t* ButtonTitleProfile=L"프로필";
|
||||
const wchar_t* ButtonTitleOption=L"옵션";
|
||||
const wchar_t* ButtonTitleClear=L"지우기";
|
||||
const wchar_t* ButtonTitleSave=L"저장";
|
||||
const wchar_t* ButtonTitleTop=L"항상위";
|
||||
const wchar_t* ButtonTitleProcess = L"프로세스";
|
||||
const wchar_t* ButtonTitleThread = L"스레드";
|
||||
const wchar_t* ButtonTitleHook = L"후킹";
|
||||
const wchar_t* ButtonTitleProfile = L"프로필";
|
||||
const wchar_t* ButtonTitleOption = L"옵션";
|
||||
const wchar_t* ButtonTitleClear = L"지우기";
|
||||
const wchar_t* ButtonTitleSave = L"저장";
|
||||
const wchar_t* ButtonTitleTop = L"항상위";
|
||||
//Hook window
|
||||
const wchar_t* SpecialHook=L"H코드 후킹, AGTH 코드는 지원하지 않습니다.";
|
||||
const wchar_t* SpecialHook = L"H코드 후킹, AGTH 코드는 지원하지 않습니다.";
|
||||
//Process window
|
||||
const wchar_t* TabTitlePID=L"PID";
|
||||
const wchar_t* TabTitleMemory=L"메모리";
|
||||
const wchar_t* TabTitleName=L"이름";
|
||||
const wchar_t* TabTitleTID=L"TID";
|
||||
const wchar_t* TabTitleStart=L"시작";
|
||||
const wchar_t* TabTitleModule=L"모듈";
|
||||
const wchar_t* TabTitleState=L"상태";
|
||||
const wchar_t* SuccessAttach=L"프로세스에 ITH 부착성공.";
|
||||
const wchar_t* FailAttach=L"프로세스에 ITH 부착실패.";
|
||||
const wchar_t* SuccessDetach=L"프로세스에서 ITH 탈착성공.";
|
||||
const wchar_t* FailDetach=L"ITH 탈착실패.";
|
||||
const wchar_t* TabTitlePID = L"PID";
|
||||
const wchar_t* TabTitleMemory = L"메모리";
|
||||
const wchar_t* TabTitleName = L"이름";
|
||||
const wchar_t* TabTitleTID = L"TID";
|
||||
const wchar_t* TabTitleStart = L"시작";
|
||||
const wchar_t* TabTitleModule = L"모듈";
|
||||
const wchar_t* TabTitleState = L"상태";
|
||||
const wchar_t* SuccessAttach = L"프로세스에 ITH 부착성공.";
|
||||
const wchar_t* FailAttach = L"프로세스에 ITH 부착실패.";
|
||||
const wchar_t* SuccessDetach = L"프로세스에서 ITH 탈착성공.";
|
||||
const wchar_t* FailDetach = L"ITH 탈착실패.";
|
||||
//Profile window
|
||||
const wchar_t* ProfileExist=L"프로필이 이미 존재함.";
|
||||
const wchar_t* SuccessAddProfile=L"프로필 추가됨.";
|
||||
const wchar_t* FailAddProfile=L"프로필 추가실패";
|
||||
const wchar_t* TabTitleNumber=L"No.";
|
||||
const wchar_t* NoFile=L"파일을 찾을 수 없음.";
|
||||
const wchar_t* PathDismatch=L"프로세스 이름이 일치하지 않습니다, 계속하시겠습니까?";
|
||||
const wchar_t* SuccessImportProfile=L"프로필 가져오기 성공";
|
||||
//const wchar_t* SuccessAddProfile=L"Profile added.";
|
||||
const wchar_t* ProfileExist = L"프로필이 이미 존재함.";
|
||||
const wchar_t* SuccessAddProfile = L"프로필 추가됨.";
|
||||
const wchar_t* FailAddProfile = L"프로필 추가실패";
|
||||
const wchar_t* TabTitleNumber = L"No.";
|
||||
const wchar_t* NoFile = L"파일을 찾을 수 없음.";
|
||||
const wchar_t* PathDismatch = L"프로세스 이름이 일치하지 않습니다, 계속하시겠습니까?";
|
||||
const wchar_t* SuccessImportProfile = L"프로필 가져오기 성공";
|
||||
//const wchar_t* SuccessAddProfile=L"Profile added.";
|
43
gui/main.cpp
43
gui/main.cpp
@ -16,11 +16,11 @@
|
||||
*/
|
||||
|
||||
#include "ITH.h"
|
||||
#include "ith/host/srv.h"
|
||||
#include "ith/host/hookman.h"
|
||||
#include "ith/host/SettingManager.h"
|
||||
#include "host/host.h"
|
||||
#include "host/hookman.h"
|
||||
#include "host/settings.h"
|
||||
#include "CustomFilter.h"
|
||||
#include "profile.h"
|
||||
#include "profile/Profile.h"
|
||||
#include "ProfileManager.h"
|
||||
|
||||
HINSTANCE hIns;
|
||||
@ -39,10 +39,10 @@ extern "C" {
|
||||
CustomFilter* uni_filter;
|
||||
CustomFilter* mb_filter;
|
||||
HookManager* man;
|
||||
SettingManager* setman;
|
||||
Settings* setman;
|
||||
LONG split_time, cyclic_remove, global_filter;
|
||||
LONG process_time, inject_delay, insert_delay,
|
||||
auto_inject, auto_insert, clipboard_flag;
|
||||
auto_inject, auto_insert, clipboard_flag;
|
||||
|
||||
std::map<std::wstring, long> setting;
|
||||
|
||||
@ -69,11 +69,13 @@ void RecordUniChar(WORD uni, PVOID f)
|
||||
|
||||
void SaveSettings()
|
||||
{
|
||||
GetWindowRect(hMainWnd, &window);
|
||||
setting[L"window_left"] = window.left;
|
||||
setting[L"window_right"] = window.right;
|
||||
setting[L"window_top"] = window.top;
|
||||
setting[L"window_bottom"] = window.bottom;
|
||||
WINDOWPLACEMENT wndpl;
|
||||
wndpl.length = sizeof(WINDOWPLACEMENT);
|
||||
GetWindowPlacement(hMainWnd, &wndpl);
|
||||
setting[L"window_left"] = wndpl.rcNormalPosition.left;
|
||||
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"process_time"] = process_time;
|
||||
setting[L"inject_delay"] = inject_delay;
|
||||
@ -238,17 +240,18 @@ LONG WINAPI UnhandledExcept(_EXCEPTION_POINTERS *ExceptionInfo)
|
||||
return 0;
|
||||
}
|
||||
|
||||
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())
|
||||
TerminateProcess(GetCurrentProcess(), 0);
|
||||
CreateMutex(NULL, TRUE, L"ITH_MAIN_RUNNING");
|
||||
if (IHF_Init())
|
||||
if (Host_Open())
|
||||
{
|
||||
SetUnhandledExceptionFilter(UnhandledExcept);
|
||||
IHF_GetHookManager(&man);
|
||||
IHF_GetSettingManager(&setman);
|
||||
setman->SetValue(SETTING_SPLIT_TIME, 200);
|
||||
Host_GetHookManager(&man);
|
||||
Host_GetSettings(&setman);
|
||||
setman->splittingInterval = 200;
|
||||
MonitorFlag = true;
|
||||
pfman = new ProfileManager();
|
||||
mb_filter = new CustomFilter();
|
||||
@ -256,11 +259,11 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
|
||||
DefaultSettings();
|
||||
LoadSettings();
|
||||
InitializeSettings();
|
||||
setman->SetValue(SETTING_SPLIT_TIME, split_time);
|
||||
setman->SetValue(SETTING_CLIPFLAG, clipboard_flag);
|
||||
setman->splittingInterval = split_time;
|
||||
setman->clipboardFlag = clipboard_flag > 0;
|
||||
hIns = hInstance;
|
||||
MyRegisterClass(hIns);
|
||||
InitInstance(hIns, IHF_IsAdmin(), &window);
|
||||
InitInstance(hIns, FALSE, &window);
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
@ -277,7 +280,7 @@ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLin
|
||||
{
|
||||
FindITH();
|
||||
}
|
||||
IHF_Cleanup();
|
||||
Host_Close();
|
||||
IthCloseSystemService();
|
||||
TerminateProcess(GetCurrentProcess(), 0);
|
||||
}
|
||||
|
@ -16,10 +16,11 @@
|
||||
*/
|
||||
|
||||
#include "utility.h"
|
||||
#include "ith/host/srv.h"
|
||||
#include "ith/host/hookman.h"
|
||||
#include "ith/common/types.h"
|
||||
#include "ith/common/const.h"
|
||||
#include "host/host.h"
|
||||
#include "host/hookman.h"
|
||||
#include "vnrhook/include/types.h"
|
||||
#include "vnrhook/include/const.h"
|
||||
#include "profile/misc.h"
|
||||
|
||||
extern HookManager* man; // main.cpp
|
||||
|
||||
@ -65,11 +66,11 @@ std::wstring GetWindowsPath(const std::wstring& path)
|
||||
// path is in device form
|
||||
// \Device\HarddiskVolume2\Windows\System32\taskhost.exe
|
||||
auto pathOffset = path.find(L'\\', 1) + 1;
|
||||
pathOffset = path.find(L'\\', pathOffset);
|
||||
pathOffset = path.find(L'\\', pathOffset);
|
||||
std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2
|
||||
std::wstring dosDrive = GetDriveLetter(devicePath); // C:
|
||||
if (dosDrive.empty())
|
||||
return L"";
|
||||
return path;
|
||||
std::wstring dosPath = dosDrive; // C:
|
||||
dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe
|
||||
return dosPath;
|
||||
@ -117,16 +118,16 @@ std::wstring GetCode(const HookParam& hp, DWORD pid)
|
||||
code += c;
|
||||
if (hp.type & NO_CONTEXT)
|
||||
code += L'N';
|
||||
if (hp.off >> 31)
|
||||
code += L"-" + ToHexString(-(hp.off + 4));
|
||||
if (hp.offset >> 31)
|
||||
code += L"-" + ToHexString(-(hp.offset + 4));
|
||||
else
|
||||
code += ToHexString(hp.off);
|
||||
code += ToHexString(hp.offset);
|
||||
if (hp.type & DATA_INDIRECT)
|
||||
{
|
||||
if (hp.ind >> 31)
|
||||
code += L"*-" + ToHexString(-hp.ind);
|
||||
if (hp.index >> 31)
|
||||
code += L"*-" + ToHexString(-hp.index);
|
||||
else
|
||||
code += L"*" + ToHexString(hp.ind);
|
||||
code += L"*" + ToHexString(hp.index);
|
||||
}
|
||||
if (hp.type & USING_SPLIT)
|
||||
{
|
||||
@ -137,21 +138,21 @@ std::wstring GetCode(const HookParam& hp, DWORD pid)
|
||||
}
|
||||
if (hp.type & SPLIT_INDIRECT)
|
||||
{
|
||||
if (hp.split_ind >> 31)
|
||||
code += L"*-" + ToHexString(-hp.split_ind);
|
||||
if (hp.split_index >> 31)
|
||||
code += L"*-" + ToHexString(-hp.split_index);
|
||||
else
|
||||
code += L"*" + ToHexString(hp.split_ind);
|
||||
code += L"*" + ToHexString(hp.split_index);
|
||||
}
|
||||
if (pid)
|
||||
{
|
||||
PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.addr);
|
||||
PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.address);
|
||||
if (allocationBase)
|
||||
{
|
||||
std::wstring path = GetModuleFileNameAsString(pid, allocationBase);
|
||||
if (!path.empty())
|
||||
{
|
||||
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;
|
||||
return code;
|
||||
}
|
||||
@ -159,20 +160,20 @@ std::wstring GetCode(const HookParam& hp, DWORD pid)
|
||||
}
|
||||
if (hp.module)
|
||||
{
|
||||
code += L"@" + ToHexString(hp.addr) + L"!" + ToHexString(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)
|
||||
// in TextHook::UnsafeInsertHookCode() MODULE_OFFSET and FUNCTION_OFFSET are removed from
|
||||
// HookParam.type
|
||||
// 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.addr) + L":";
|
||||
code += L"@" + ToHexString(hp.address) + L":";
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
//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)
|
||||
{
|
||||
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.
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
#include "ITH.h"
|
||||
|
||||
struct HookParam;
|
||||
struct ProcessRecord;
|
||||
|
||||
DWORD Hash(const std::wstring& module, int length = -1);
|
||||
DWORD ProcessCommand(const std::wstring& cmd, DWORD pid);
|
||||
std::wstring GetProcessPath(DWORD pid);
|
||||
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);
|
||||
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
|
||||
class CriticalSection
|
||||
{
|
||||
|
470
gui/window.cpp
470
gui/window.cpp
@ -18,15 +18,16 @@
|
||||
#include "ProcessWindow.h"
|
||||
#include "resource.h"
|
||||
#include "language.h"
|
||||
#include "ith/host/srv.h"
|
||||
#include "ith/host/hookman.h"
|
||||
#include "ith/common/const.h"
|
||||
#include "host/host.h"
|
||||
#include "host/hookman.h"
|
||||
#include "vnrhook/include/const.h"
|
||||
#include "version.h"
|
||||
#include "ProfileManager.h"
|
||||
#include "ith/host/SettingManager.h"
|
||||
#include "host/settings.h"
|
||||
#include "CustomFilter.h"
|
||||
#include "Profile.h"
|
||||
#include "profile/Profile.h"
|
||||
#include "TextBuffer.h"
|
||||
#include "profile/misc.h"
|
||||
|
||||
#define CMD_SIZE 512
|
||||
|
||||
@ -46,39 +47,38 @@ extern ProfileManager* pfman; // ProfileManager.cpp
|
||||
extern HookManager* man; // main.cpp
|
||||
extern CustomFilter* mb_filter; // main.cpp
|
||||
extern CustomFilter* uni_filter; // main.cpp
|
||||
extern SettingManager* setman; // main.cpp
|
||||
extern Settings* setman; // main.cpp
|
||||
#define COMMENT_BUFFER_LENGTH 512
|
||||
static WCHAR comment_buffer[COMMENT_BUFFER_LENGTH];
|
||||
|
||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
||||
void SaveSettings(); // main.cpp
|
||||
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;
|
||||
void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const TextThread& thread);
|
||||
|
||||
ATOM MyRegisterClass(HINSTANCE hInstance)
|
||||
{
|
||||
WNDCLASSEX wcex;
|
||||
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcex.lpfnWndProc = WndProc;
|
||||
wcex.cbClsExtra = 0;
|
||||
wcex.cbWndExtra = 0;
|
||||
wcex.hInstance = hInstance;
|
||||
wcex.hIcon = NULL;
|
||||
wcex.hCursor = NULL;
|
||||
wcex.hbrBackground = GetStockBrush(WHITE_BRUSH);
|
||||
wcex.lpszMenuName = NULL;
|
||||
wcex.lpszClassName = ClassName;
|
||||
wcex.hIconSm = LoadIcon(hInstance, (LPWSTR)IDI_ICON1);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcex.lpfnWndProc = WndProc;
|
||||
wcex.cbClsExtra = 0;
|
||||
wcex.cbWndExtra = 0;
|
||||
wcex.hInstance = hInstance;
|
||||
wcex.hIcon = NULL;
|
||||
wcex.hCursor = NULL;
|
||||
wcex.hbrBackground = GetStockBrush(WHITE_BRUSH);
|
||||
wcex.lpszMenuName = NULL;
|
||||
wcex.lpszClassName = ClassName;
|
||||
wcex.hIconSm = LoadIcon(hInstance, (LPWSTR)IDI_ICON1);
|
||||
return RegisterClassEx(&wcex);
|
||||
}
|
||||
|
||||
BOOL InitInstance(HINSTANCE hInstance, DWORD nAdmin, RECT* rc)
|
||||
{
|
||||
hIns = hInstance;
|
||||
LPCWSTR name = (nAdmin) ? ClassNameAdmin : ClassName;
|
||||
LPCWSTR name = (nAdmin) ? ClassNameAdmin : ClassName;
|
||||
hMainWnd = CreateWindow(ClassName, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
|
||||
rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, NULL, NULL, hInstance, 0);
|
||||
if (!hMainWnd)
|
||||
@ -137,8 +137,8 @@ BOOL CALLBACK OptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
clipboard_flag = IsDlgButtonChecked(hDlg, IDC_CHECK3);
|
||||
cyclic_remove = IsDlgButtonChecked(hDlg, IDC_CHECK4);
|
||||
global_filter = IsDlgButtonChecked(hDlg, IDC_CHECK5);
|
||||
setman->SetValue(SETTING_CLIPFLAG, clipboard_flag);
|
||||
setman->SetValue(SETTING_SPLIT_TIME, split_time);
|
||||
setman->clipboardFlag = clipboard_flag;
|
||||
setman->splittingInterval = split_time;
|
||||
if (auto_inject == 0) auto_insert = 0;
|
||||
}
|
||||
case IDCANCEL:
|
||||
@ -187,15 +187,15 @@ BOOL CALLBACK ProcessDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
pswnd->DetachProcess();
|
||||
break;
|
||||
case IDC_BUTTON5:
|
||||
pswnd->AddCurrentToProfile();
|
||||
pswnd->CreateProfileForSelectedProcess();
|
||||
break;
|
||||
case IDC_BUTTON6:
|
||||
pswnd->RemoveCurrentFromProfile();
|
||||
pswnd->DeleteProfileForSelectedProcess();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
|
||||
|
||||
case WM_NOTIFY:
|
||||
{
|
||||
LPNMHDR dr = (LPNMHDR)lParam;
|
||||
@ -223,22 +223,22 @@ LRESULT CALLBACK EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
switch (message)
|
||||
{
|
||||
case WM_CHAR: //Filter user input.
|
||||
if (GetKeyState(VK_CONTROL) & 0x8000)
|
||||
{
|
||||
if (wParam == 1)
|
||||
{
|
||||
Edit_SetSel(hwndEdit, 0, -1);
|
||||
SendMessage(hwndEdit, WM_COPY, 0, 0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case WM_LBUTTONUP:
|
||||
if (hwndEdit)
|
||||
SendMessage(hwndEdit, WM_COPY, 0, 0);
|
||||
default:
|
||||
if (GetKeyState(VK_CONTROL) & 0x8000)
|
||||
{
|
||||
return proc(hWnd, message, wParam, lParam);
|
||||
if (wParam == 1)
|
||||
{
|
||||
Edit_SetSel(hwndEdit, 0, -1);
|
||||
SendMessage(hwndEdit, WM_COPY, 0, 0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case WM_LBUTTONUP:
|
||||
if (hwndEdit)
|
||||
SendMessage(hwndEdit, WM_COPY, 0, 0);
|
||||
default:
|
||||
{
|
||||
return proc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -280,29 +280,29 @@ LRESULT CALLBACK EditCmdProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lPar
|
||||
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
hwndProcessComboBox = CreateWindow(L"ComboBox", NULL,
|
||||
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
|
||||
CBS_SORT | WS_VSCROLL | WS_TABSTOP,
|
||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||
hwndCmd = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL,
|
||||
WS_CHILD | WS_VISIBLE | ES_NOHIDESEL| ES_LEFT | ES_AUTOHSCROLL,
|
||||
WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_AUTOHSCROLL,
|
||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||
hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL,
|
||||
WS_CHILD | WS_VISIBLE | ES_NOHIDESEL| WS_VSCROLL |
|
||||
WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | WS_VSCROLL |
|
||||
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
|
||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||
}
|
||||
@ -336,7 +336,7 @@ void ClickButton(HWND hWnd, HWND h)
|
||||
}
|
||||
else if (h == hwndTop)
|
||||
{
|
||||
if (Button_GetCheck(h)==BST_CHECKED)
|
||||
if (Button_GetCheck(h) == BST_CHECKED)
|
||||
{
|
||||
Button_SetCheck(h, BST_UNCHECKED);
|
||||
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
||||
@ -363,7 +363,7 @@ void ClickButton(HWND hWnd, HWND h)
|
||||
DWORD pid = std::stoul(str);
|
||||
SaveProcessProfile(pid);
|
||||
}
|
||||
pfman->SaveProfile();
|
||||
pfman->SaveProfiles();
|
||||
}
|
||||
else if (h == hwndRemoveLink)
|
||||
{
|
||||
@ -372,7 +372,7 @@ void ClickButton(HWND hWnd, HWND h)
|
||||
{
|
||||
DWORD from = std::stoul(str, NULL, 16);
|
||||
if (from != 0)
|
||||
IHF_UnLink(from);
|
||||
Host_UnLink(from);
|
||||
}
|
||||
}
|
||||
else if (h == hwndRemoveHook)
|
||||
@ -388,12 +388,12 @@ void ClickButton(HWND hWnd, HWND h)
|
||||
entry = entry.substr(i + 1);
|
||||
DWORD addr = std::stoul(entry, NULL, 16);
|
||||
if (threadNumber != 0)
|
||||
IHF_RemoveHook(pid, addr);
|
||||
Host_RemoveHook(pid, addr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DWORD ThreadFilter(TextThread* thread, BYTE* out,DWORD len, DWORD new_line, PVOID data, bool space)
|
||||
DWORD ThreadFilter(TextThread* thread, BYTE* out, DWORD len, DWORD new_line, PVOID data, bool space)
|
||||
{
|
||||
DWORD status = thread->Status();
|
||||
if (global_filter && !new_line && thread->Number() != 0)
|
||||
@ -444,7 +444,7 @@ DWORD ThreadFilter(TextThread* thread, BYTE* out,DWORD len, DWORD new_line, PVOI
|
||||
return len;
|
||||
}
|
||||
|
||||
DWORD ThreadOutput(TextThread* thread, BYTE* out,DWORD len, DWORD new_line, PVOID data, bool space)
|
||||
DWORD ThreadOutput(TextThread* thread, BYTE* out, DWORD len, DWORD new_line, PVOID data, bool space)
|
||||
{
|
||||
if (len == 0)
|
||||
return len;
|
||||
@ -498,11 +498,16 @@ bool GetHookParam(DWORD pid, DWORD hook_addr, HookParam& hp)
|
||||
return result;
|
||||
}
|
||||
|
||||
void AddToCombo(TextThread& thread, bool replace)
|
||||
std::wstring GetEntryString(TextThread& thread)
|
||||
{
|
||||
WCHAR entry[512];
|
||||
CHAR 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())
|
||||
entryWithLink += L"->" + ToHexString(thread.LinkNumber());
|
||||
if (thread.PID() == 0)
|
||||
@ -510,7 +515,14 @@ void AddToCombo(TextThread& thread, bool replace)
|
||||
HookParam hp = {};
|
||||
if (GetHookParam(thread.PID(), thread.Addr(), hp))
|
||||
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)
|
||||
{
|
||||
int sel = ComboBox_GetCurSel(hwndCombo);
|
||||
@ -531,11 +543,12 @@ void AddToCombo(TextThread& thread, bool replace)
|
||||
|
||||
void RemoveFromCombo(TextThread* thread)
|
||||
{
|
||||
WCHAR entry[512];
|
||||
CHAR entry[512];
|
||||
thread->GetEntryString(entry, 512);
|
||||
std::wstring unicodeEntry = toUnicodeString(entry);
|
||||
if (thread->PID() == 0)
|
||||
std::wcscat(entry, L"ConsoleOutput");
|
||||
int i = ComboBox_FindString(hwndCombo, 0, entry);
|
||||
unicodeEntry += L"ConsoleOutput";
|
||||
int i = ComboBox_FindString(hwndCombo, 0, unicodeEntry.c_str());
|
||||
if (i != CB_ERR)
|
||||
{
|
||||
if (ComboBox_DeleteString(hwndCombo, i) == CB_ERR)
|
||||
@ -595,6 +608,36 @@ DWORD AddRemoveLink(TextThread* thread)
|
||||
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 res = false;
|
||||
@ -612,50 +655,21 @@ bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook)
|
||||
return res;
|
||||
}
|
||||
|
||||
DWORD ThreadCreate(TextThread* thread)
|
||||
void AddLinksToHookManager(const Profile* pf, size_t thread_index, const 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)
|
||||
for (auto lp = pf->Links().begin(); lp != pf->Links().end(); ++lp)
|
||||
{
|
||||
if (IsUnicodeHook(*pr, tp->hook))
|
||||
thread->Status() |= USING_UNICODE;
|
||||
}
|
||||
|
||||
auto pf = pfman->GetProfile(tp->pid);
|
||||
if (pf)
|
||||
{
|
||||
auto thread_profile = pf->FindThreadProfile(*tp);
|
||||
if (thread_profile != pf->Threads().end())
|
||||
if ((*lp)->FromIndex() == thread_index)
|
||||
{
|
||||
(*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();
|
||||
WORD to_index = pf->Threads()[(*lp)->ToIndex()]->HookManagerIndex();
|
||||
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)
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -684,7 +690,6 @@ DWORD RegisterProcessList(DWORD pid)
|
||||
ComboBox_AddString(hwndProcessComboBox, str);
|
||||
if (ComboBox_GetCount(hwndProcessComboBox) == 1)
|
||||
ComboBox_SetCurSel(hwndProcessComboBox, 0);
|
||||
pfman->FindProfileAndUpdateHookAddresses(pid, path);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -706,9 +711,6 @@ DWORD RemoveProcessList(DWORD pid)
|
||||
|
||||
DWORD RefreshProfileOnNewHook(DWORD pid)
|
||||
{
|
||||
auto path = GetProcessPath(pid);
|
||||
if (!path.empty())
|
||||
pfman->FindProfileAndUpdateHookAddresses(pid, path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -716,136 +718,138 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
CreateButtons(hWnd);
|
||||
// Add text to the window.
|
||||
Edit_LimitText(hwndEdit, -1);
|
||||
SendMessage(hwndEdit, WM_INPUTLANGCHANGEREQUEST, 0, 0x411);
|
||||
proc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)EditProc);
|
||||
proccmd = (WNDPROC)SetWindowLong(hwndCmd, GWL_WNDPROC, (LONG)EditCmdProc);
|
||||
hwndCombo = CreateWindow(L"ComboBox", NULL,
|
||||
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
|
||||
CBS_SORT | WS_VSCROLL | WS_TABSTOP,
|
||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||
case WM_CREATE:
|
||||
CreateButtons(hWnd);
|
||||
// Add text to the window.
|
||||
Edit_LimitText(hwndEdit, -1);
|
||||
SendMessage(hwndEdit, WM_INPUTLANGCHANGEREQUEST, 0, 0x411);
|
||||
proc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)EditProc);
|
||||
proccmd = (WNDPROC)SetWindowLong(hwndCmd, GWL_WNDPROC, (LONG)EditCmdProc);
|
||||
hwndCombo = CreateWindow(L"ComboBox", NULL,
|
||||
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
|
||||
CBS_SORT | WS_VSCROLL | WS_TABSTOP,
|
||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
||||
{
|
||||
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");
|
||||
hWhiteBrush = GetStockBrush(WHITE_BRUSH);
|
||||
SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0);
|
||||
SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hf, 0);
|
||||
SendMessage(hwndCombo, WM_SETFONT, (WPARAM)hf, 0);
|
||||
SendMessage(hwndProcessComboBox, WM_SETFONT, (WPARAM)hf, 0);
|
||||
texts = new TextBuffer(hwndEdit);
|
||||
man->RegisterThreadCreateCallback(ThreadCreate);
|
||||
man->RegisterThreadRemoveCallback(ThreadRemove);
|
||||
man->RegisterThreadResetCallback(ThreadReset);
|
||||
TextThread* console = man->FindSingle(0);
|
||||
console->RegisterOutputCallBack(ThreadOutput, NULL);
|
||||
AddToCombo(*console, false);
|
||||
man->RegisterProcessAttachCallback(RegisterProcessList);
|
||||
man->RegisterProcessDetachCallback(RemoveProcessList);
|
||||
man->RegisterProcessNewHookCallback(RefreshProfileOnNewHook);
|
||||
man->RegisterAddRemoveLinkCallback(AddRemoveLink);
|
||||
man->RegisterConsoleCallback(ConsoleOutput);
|
||||
Host_Start();
|
||||
{
|
||||
HFONT hf = CreateFont(18, 0, 0, 0, FW_LIGHT, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, ANTIALIASED_QUALITY, 0,
|
||||
L"MS Gothic");
|
||||
hWhiteBrush = GetStockBrush(WHITE_BRUSH);
|
||||
SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0);
|
||||
SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hf, 0);
|
||||
SendMessage(hwndCombo, WM_SETFONT, (WPARAM)hf, 0);
|
||||
SendMessage(hwndProcessComboBox, WM_SETFONT, (WPARAM)hf, 0);
|
||||
texts = new TextBuffer(hwndEdit);
|
||||
man->RegisterThreadCreateCallback(ThreadCreate);
|
||||
man->RegisterThreadRemoveCallback(ThreadRemove);
|
||||
man->RegisterThreadResetCallback(ThreadReset);
|
||||
TextThread* console = man->FindSingle(0);
|
||||
console->RegisterOutputCallBack(ThreadOutput, NULL);
|
||||
AddToCombo(*console, false);
|
||||
man->RegisterProcessAttachCallback(RegisterProcessList);
|
||||
man->RegisterProcessDetachCallback(RemoveProcessList);
|
||||
man->RegisterProcessNewHookCallback(RefreshProfileOnNewHook);
|
||||
man->RegisterAddRemoveLinkCallback(AddRemoveLink);
|
||||
man->RegisterConsoleCallback(ConsoleOutput);
|
||||
IHF_Start();
|
||||
{
|
||||
static const WCHAR program_name[] = L"Interactive Text Hooker";
|
||||
//static const WCHAR program_version[] = L"3.0";
|
||||
static WCHAR version_info[256];
|
||||
std::swprintf(version_info, L"%s %s (%s)", program_name, program_version, build_date);
|
||||
man->AddConsoleOutput(version_info);
|
||||
man->AddConsoleOutput(InitMessage);
|
||||
}
|
||||
|
||||
if (background == 0)
|
||||
man->AddConsoleOutput(BackgroundMsg);
|
||||
if (!IHF_IsAdmin())
|
||||
man->AddConsoleOutput(NotAdmin);
|
||||
static const WCHAR program_name[] = L"Interactive Text Hooker";
|
||||
//static const WCHAR program_version[] = L"3.0";
|
||||
static WCHAR version_info[256];
|
||||
std::swprintf(version_info, L"%s %s (%s)", program_name, program_version, build_date);
|
||||
man->AddConsoleOutput(version_info);
|
||||
man->AddConsoleOutput(InitMessage);
|
||||
}
|
||||
|
||||
return 0;
|
||||
case WM_COMMAND:
|
||||
if (background == 0)
|
||||
man->AddConsoleOutput(BackgroundMsg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
case WM_COMMAND:
|
||||
{
|
||||
DWORD wmId, wmEvent, dwId;
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
switch (wmEvent)
|
||||
{
|
||||
case EN_VSCROLL:
|
||||
{
|
||||
SCROLLBARINFO info = { sizeof(info) };
|
||||
GetScrollBarInfo(hwndEdit, OBJID_VSCROLL, &info);
|
||||
InvalidateRect(hwndEdit, 0, 1);
|
||||
ValidateRect(hwndEdit, &info.rcScrollBar);
|
||||
RedrawWindow(hwndEdit, 0, 0, RDW_ERASE);
|
||||
}
|
||||
break;
|
||||
case CBN_SELENDOK:
|
||||
{
|
||||
if ((HWND)lParam == hwndProcessComboBox)
|
||||
return 0;
|
||||
dwId = ComboBox_GetCurSel(hwndCombo);
|
||||
int len = ComboBox_GetLBTextLen(hwndCombo, dwId);
|
||||
if (len > 0)
|
||||
{
|
||||
DWORD wmId, wmEvent, dwId;
|
||||
wmId = LOWORD(wParam);
|
||||
wmEvent = HIWORD(wParam);
|
||||
switch (wmEvent)
|
||||
{
|
||||
case EN_VSCROLL:
|
||||
{
|
||||
SCROLLBARINFO info={sizeof(info)};
|
||||
GetScrollBarInfo(hwndEdit, OBJID_VSCROLL, &info);
|
||||
InvalidateRect(hwndEdit, 0, 1);
|
||||
ValidateRect(hwndEdit, &info.rcScrollBar);
|
||||
RedrawWindow(hwndEdit, 0, 0, RDW_ERASE);
|
||||
}
|
||||
break;
|
||||
case CBN_SELENDOK:
|
||||
{
|
||||
if ((HWND)lParam == hwndProcessComboBox)
|
||||
return 0;
|
||||
dwId = ComboBox_GetCurSel(hwndCombo);
|
||||
int len = ComboBox_GetLBTextLen(hwndCombo, dwId);
|
||||
if (len > 0)
|
||||
{
|
||||
LPWSTR pwcEntry = new WCHAR[len + 1];
|
||||
len = ComboBox_GetLBText(hwndCombo, dwId, pwcEntry);
|
||||
DWORD num = std::stoul(pwcEntry, NULL, 16);
|
||||
man->SelectCurrent(num);
|
||||
delete[] pwcEntry;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case BN_CLICKED:
|
||||
ClickButton(hWnd, (HWND)lParam);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
LPWSTR pwcEntry = new WCHAR[len + 1];
|
||||
len = ComboBox_GetLBText(hwndCombo, dwId, pwcEntry);
|
||||
DWORD num = std::stoul(pwcEntry, NULL, 16);
|
||||
man->SelectCurrent(num);
|
||||
delete[] pwcEntry;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
case BN_CLICKED:
|
||||
ClickButton(hWnd, (HWND)lParam);
|
||||
break;
|
||||
case WM_SETFOCUS:
|
||||
SetFocus(hwndEdit);
|
||||
return 0;
|
||||
case WM_SIZE:
|
||||
{
|
||||
WORD width = LOWORD(lParam);
|
||||
WORD height = HIWORD(lParam);
|
||||
DWORD l = width / 7;
|
||||
WORD h = HIWORD(GetDialogBaseUnits()); // height of the system font
|
||||
h = h + (h / 2);
|
||||
HDC hDC = GetDC(hWnd);
|
||||
RECT rc;
|
||||
GetClientRect(hWnd, &rc);
|
||||
FillRect(hDC, &rc, hWhiteBrush);
|
||||
ReleaseDC(hWnd, hDC);
|
||||
MoveWindow(hwndProcess, 0, 0, l, h, TRUE);
|
||||
MoveWindow(hwndOption, l * 1, 0, l, h, TRUE);
|
||||
MoveWindow(hwndTop, l * 2, 0, l, h, TRUE);
|
||||
MoveWindow(hwndClear, l * 3, 0, l, h, TRUE);
|
||||
MoveWindow(hwndRemoveLink, l * 4, 0, l, h, TRUE);
|
||||
MoveWindow(hwndRemoveHook, l * 5, 0, l, h, TRUE);
|
||||
MoveWindow(hwndSave, l * 6, 0, width - 6 * l, h, TRUE);
|
||||
l *= 2;
|
||||
MoveWindow(hwndProcessComboBox, 0, h, l, 200, TRUE);
|
||||
MoveWindow(hwndCmd, l, h, width - l, h, TRUE);
|
||||
MoveWindow(hwndCombo, 0, h * 2, width, 200, TRUE);
|
||||
h *= 3;
|
||||
MoveWindow(hwndEdit, 0, h, width, height - h, TRUE);
|
||||
}
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
man->RegisterThreadCreateCallback(0);
|
||||
man->RegisterThreadRemoveCallback(0);
|
||||
man->RegisterThreadResetCallback(0);
|
||||
man->RegisterProcessAttachCallback(0);
|
||||
man->RegisterProcessDetachCallback(0);
|
||||
//delete texts;
|
||||
SaveSettings();
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
default:
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_SETFOCUS:
|
||||
SetFocus(hwndEdit);
|
||||
return 0;
|
||||
case WM_SIZE:
|
||||
{
|
||||
WORD width = LOWORD(lParam);
|
||||
WORD height = HIWORD(lParam);
|
||||
DWORD l = width / 7;
|
||||
WORD h = HIWORD(GetDialogBaseUnits()); // height of the system font
|
||||
h = h + (h / 2);
|
||||
HDC hDC = GetDC(hWnd);
|
||||
RECT rc;
|
||||
GetClientRect(hWnd, &rc);
|
||||
FillRect(hDC, &rc, hWhiteBrush);
|
||||
ReleaseDC(hWnd, hDC);
|
||||
MoveWindow(hwndProcess, 0, 0, l, h, TRUE);
|
||||
MoveWindow(hwndOption, l * 1, 0, l, h, TRUE);
|
||||
MoveWindow(hwndTop, l * 2, 0, l, h, TRUE);
|
||||
MoveWindow(hwndClear, l * 3, 0, l, h, TRUE);
|
||||
MoveWindow(hwndRemoveLink, l * 4, 0, l, h, TRUE);
|
||||
MoveWindow(hwndRemoveHook, l * 5, 0, l, h, TRUE);
|
||||
MoveWindow(hwndSave, l * 6, 0, width - 6 * l, h, TRUE);
|
||||
l *= 2;
|
||||
MoveWindow(hwndProcessComboBox, 0, h, l, 200, TRUE);
|
||||
MoveWindow(hwndCmd, l, h, width - l, h, TRUE);
|
||||
MoveWindow(hwndCombo, 0, h * 2, width, 200, TRUE);
|
||||
h *= 3;
|
||||
MoveWindow(hwndEdit, 0, h, width, height - h, TRUE);
|
||||
}
|
||||
return 0;
|
||||
case WM_DESTROY:
|
||||
man->RegisterThreadCreateCallback(0);
|
||||
man->RegisterThreadRemoveCallback(0);
|
||||
man->RegisterThreadResetCallback(0);
|
||||
man->RegisterProcessAttachCallback(0);
|
||||
man->RegisterProcessDetachCallback(0);
|
||||
//delete texts;
|
||||
SaveSettings();
|
||||
PostQuitMessage(0);
|
||||
return 0;
|
||||
default:
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -16,4 +16,5 @@
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#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)
|
||||
|
||||
set(CMAKE_CONFIGURATION_TYPES Debug Release)
|
||||
|
||||
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(
|
||||
-DUNICODE
|
||||
-D_UNICODE
|
||||
/DUNICODE
|
||||
/D_UNICODE
|
||||
/D_SECURE_SCL=0
|
||||
/D_SCL_SECURE_NO_WARNINGS
|
||||
/D_CRT_SECURE_NO_WARNINGS
|
||||
)
|
||||
|
||||
include_directories(${PROJECT_SOURCE_DIR})
|
||||
|
||||
set(common_src
|
||||
${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
|
||||
${PROJECT_SOURCE_DIR}/ith/import/mono/funcinfo.h
|
||||
${PROJECT_SOURCE_DIR}/ith/import/mono/types.h
|
||||
${PROJECT_SOURCE_DIR}/ith/import/ppsspp/funcinfo.h
|
||||
include_directories(
|
||||
${PROJECT_SOURCE_DIR}
|
||||
${PROJECT_SOURCE_DIR}/texthook
|
||||
)
|
||||
|
||||
add_subdirectory(ith/hook)
|
||||
add_subdirectory(ith/host)
|
||||
add_subdirectory(ith/sys)
|
||||
add_subdirectory(vnrhook)
|
||||
add_subdirectory(texthook/host)
|
||||
add_subdirectory(ithsys)
|
||||
add_subdirectory(profile)
|
||||
|
@ -72,8 +72,8 @@ win32 {
|
||||
## External Libraries
|
||||
|
||||
win32 {
|
||||
D3D_HOME = "$$PROGRAMFILES/Microsoft DirectX SDK"
|
||||
DETOURS_HOME = "$$PROGRAMFILES/Microsoft Research/Detours Express 3.0"
|
||||
D3D_HOME = "C:/Program Files/Microsoft DirectX SDK"
|
||||
DETOURS_HOME = "C:/Program Files/Microsoft Research/Detours Express 3.0"
|
||||
#DEV_HOME = c:/dev
|
||||
DEV_HOME = z:/local/windows/developer
|
||||
BOOST_HOME = $$DEV_HOME/boost/build
|
||||
@ -81,8 +81,8 @@ win32 {
|
||||
MSIME_HOME = $$DEV_HOME/msime
|
||||
#PYTHON_HOME = $$ROOTDIR/../Python
|
||||
#PYTHON_HOME = C:/Python
|
||||
PYTHON_HOME = Z:/Local/Windows/Developer/Python
|
||||
PYSIDE_HOME = $$PYTHON_HOME/Lib/site-packages/PySide
|
||||
PYTHON_HOME = $$DEV_HOME/python
|
||||
PYSIDE_HOME = $$PYTHON_HOME/lib/site-packages/PySide
|
||||
QT_HOME = c:/qt/4
|
||||
QT_SRC = c:/qt
|
||||
SAPI_HOME = "$$PROGRAMFILES/Microsoft Speech SDK 5.1"
|
||||
@ -161,7 +161,7 @@ win32 {
|
||||
QMAKE_CXXFLAGS_EXCEPTIONS_ON += -EHa
|
||||
}
|
||||
|
||||
CONFIG(noeh) { # No Exception handler
|
||||
CONFIG(noeh) { # No exception handler
|
||||
message(CONFIG noeh)
|
||||
#CONFIG -= rtti #-exceptions -stl
|
||||
QMAKE_CXXFLAGS += /GR-
|
||||
@ -175,7 +175,7 @@ win32 {
|
||||
}
|
||||
}
|
||||
|
||||
CONFIG(nosafeseh) { # No Exception handler
|
||||
CONFIG(nosafeseh) { # No safe exception handler
|
||||
message(CONFIG nosafeseh)
|
||||
|
||||
# Disable SafeSEH table
|
||||
|
@ -1,20 +1,28 @@
|
||||
@echo off
|
||||
setlocal
|
||||
if [%1] == [] (
|
||||
echo usage: copy_vnr <path-to-Sakura-directory>
|
||||
echo usage: copy_vnr path_to_Sakura
|
||||
goto :EOF
|
||||
)
|
||||
xcopy %1\config.pri . /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\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\ntdll ntdll /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\winmutex winmutex /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\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
|
||||
|
@ -19,8 +19,8 @@ inline size_t cpp_basic_strlen(const charT *s)
|
||||
return p - s;
|
||||
}
|
||||
|
||||
inline size_t cpp_strlen(const char *s) { return cpp_basic_strlen(s); }
|
||||
inline size_t cpp_wstrlen(const wchar_t *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<wchar_t>(s); }
|
||||
|
||||
template <typename charT>
|
||||
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;
|
||||
}
|
||||
|
||||
inline size_t cpp_strnlen(const char *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(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<wchar_t>(s, n); }
|
||||
|
||||
// strnchr
|
||||
|
||||
@ -45,19 +45,15 @@ inline size_t cpp_wstrnlen(const wchar_t *s, size_t n) { return cpp_basic_strnle
|
||||
return nullptr; \
|
||||
}
|
||||
template <typename charT>
|
||||
inline charT *cpp_basic_strnchr(charT *s, int c, size_t n)
|
||||
cpp_basic_strnchr_(s, c, n)
|
||||
|
||||
inline charT *cpp_basic_strnchr(charT *s, charT c, size_t n) cpp_basic_strnchr_(s, c, n)
|
||||
template <typename charT>
|
||||
inline const charT *cpp_basic_strnchr(const charT *s, int c, size_t n)
|
||||
cpp_basic_strnchr_(s, c, n)
|
||||
inline const charT *cpp_basic_strnchr(const charT *s, charT c, size_t n) cpp_basic_strnchr_(s, c, n)
|
||||
|
||||
// The same as memchr
|
||||
inline char *cpp_strnchr(char *s, int c, size_t n) { return cpp_basic_strnchr(s, c, n); }
|
||||
inline const char *cpp_strnchr(const char *s, int c, size_t n) { return cpp_basic_strnchr(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, 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, 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 const wchar_t *cpp_wcsnchr(const wchar_t *s, wchar_t c, size_t n) { return cpp_basic_strnchr<wchar_t>(s, c, n); }
|
||||
|
||||
// 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>
|
||||
inline charT *cpp_basic_strnstr(charT *s, const charT *r, size_t n)
|
||||
cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
|
||||
|
||||
inline charT *cpp_basic_strnstr(charT *s, const charT *r, size_t n) cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
|
||||
template <typename charT>
|
||||
inline const charT *cpp_basic_strnstr(const charT *s, const charT *r, size_t n)
|
||||
cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
|
||||
inline const charT *cpp_basic_strnstr(const charT *s, const charT *r, size_t n) cpp_basic_strnstr_(s, n, r, ::strlen(r), ::strncmp)
|
||||
|
||||
template <>
|
||||
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)
|
||||
|
||||
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)
|
||||
template <>
|
||||
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)
|
||||
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)
|
||||
|
||||
inline char *cpp_strnstr(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(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 const wchar_t *cpp_wcsnstr(const wchar_t *s, const wchar_t *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<char>(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<wchar_t>(s, r, n); }
|
||||
|
||||
// strnpbrk
|
||||
|
||||
|
@ -4,9 +4,23 @@
|
||||
// cpplocale.h
|
||||
// 9/26/2014 jichi
|
||||
|
||||
#include <codecvt>
|
||||
#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>
|
||||
|
||||
// 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.
|
||||
// - std::consume_header will skip the leading encoding byte from the input.
|
||||
template <class charT>
|
||||
inline std::locale cpp_utf8_locale(std::locale init = std::locale())
|
||||
{ return std::locale(init, new std::codecvt_utf8<charT, 0x10ffff, std::consume_header>()); }
|
||||
inline std::locale cpp_utf8_locale(std::locale init = std::locale()) //::empty())
|
||||
{ return std::locale(init, CPPLOCALE_NEW_FACET_UTF8(charT)); }
|
||||
|
||||
#endif // CPPLOCALE_H
|
||||
|
@ -4,33 +4,15 @@
|
||||
// cppstring.h
|
||||
// 10/12/2014 jichi
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
/#include <string>
|
||||
|
||||
// Initializers
|
||||
|
||||
template <typename charT, typename stringT>
|
||||
inline std::basic_string<charT> cpp_basic_string_of(const stringT &s)
|
||||
{ return std::basic_string<charT>(s.cbegin(), s.cend()); }
|
||||
template <typename charT>
|
||||
inline std::basic_string<charT> cpp_basic_string_of(const std::string &s)
|
||||
{ return std::basic_string<charT>(s.begin(), s.end()); }
|
||||
|
||||
template <typename stringT>
|
||||
inline std::string cpp_string_of(const stringT &s)
|
||||
{ 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)); }
|
||||
inline std::wstring cpp_wstring_of(const std::string &s)
|
||||
{ return std::wstring(s.begin(), s.end()); }
|
||||
|
||||
#endif // CPPSTRING_H
|
||||
|
@ -9,6 +9,7 @@
|
||||
// 3024b815 0f1302 movlps qword ptr ds:[edx],xmm0
|
||||
|
||||
#include "disasm.h"
|
||||
#include <windows.h>
|
||||
|
||||
// disasm_flag values:
|
||||
enum : unsigned {
|
||||
@ -29,21 +30,22 @@ DISASM_BEGIN_NAMESPACE
|
||||
// But the are currently unused and could make disasm thread-unsafe
|
||||
namespace { // unnamed
|
||||
|
||||
BYTE disasm_seg, // CS DS ES SS FS GS
|
||||
disasm_rep, // REPZ/REPNZ
|
||||
disasm_opcode, // opcode
|
||||
disasm_opcode2, // used when opcode==0f
|
||||
disasm_modrm, // modxxxrm
|
||||
disasm_sib, // scale-index-base
|
||||
disasm_mem[8], // mem addr value
|
||||
disasm_data[8]; // data value
|
||||
BYTE disasm_seg // CS DS ES SS FS GS
|
||||
, disasm_rep // REPZ/REPNZ
|
||||
, disasm_opcode // opcode
|
||||
, disasm_opcode2 // used when opcode==0f
|
||||
, disasm_modrm // modxxxrm
|
||||
, disasm_sib // scale-index-base
|
||||
, disasm_mem[8] // mem addr value
|
||||
, disasm_data[8] // data value
|
||||
;
|
||||
|
||||
} // unnamed namespace
|
||||
|
||||
// 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
|
||||
disasm_flag = 0, // C_xxx
|
||||
@ -253,7 +255,7 @@ retry:
|
||||
for (DWORD i = 0; i < disasm_datasize; i++)
|
||||
disasm_data[i] = *opcode++;
|
||||
|
||||
disasm_len = opcode - opcode0;
|
||||
disasm_len = opcode - (const BYTE *)opcode0;
|
||||
|
||||
return disasm_len;
|
||||
} // disasm
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
// Include typedef of BYTE
|
||||
//#include <windef.h>
|
||||
#include <windows.h>
|
||||
//#include <windows.h>
|
||||
|
||||
//#ifdef QT_CORE_LIB
|
||||
//# include <qt_windows.h>
|
||||
@ -20,7 +20,13 @@
|
||||
#endif
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
// Example call:
|
||||
// 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
|
||||
enum : WORD {
|
||||
word_jmp = 0x25ff
|
||||
word_jmp = 0x25ff // long jump
|
||||
, 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
|
||||
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_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 arg1 the function 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
|
||||
*/
|
||||
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 DWORD argtype;
|
||||
|
||||
enum { START = 0x1000 }; // leading size to skip
|
||||
for (DWORD i = START; i < size - sizeof(argtype); i++)
|
||||
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 < start + size) {
|
||||
if (arg1 == *(argtype *)t)
|
||||
return start + i;
|
||||
else
|
||||
i += sizeof(optype) + sizeof(argtype) - 1; // == 4
|
||||
}
|
||||
DWORD t = *(argtype *)(start + i + sizeof(optype));
|
||||
//if (t > start && t < stop) {
|
||||
if (arg1 == t + start + i + sizeof(optype) + sizeof(argtype)) // relative address
|
||||
return start + i;
|
||||
//i += sizeof(optype) + sizeof(argtype) - 1; // == 4
|
||||
//}
|
||||
}
|
||||
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.
|
||||
*
|
||||
@ -277,13 +391,12 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
|
||||
* @param search range
|
||||
* @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 DWORD argtype;
|
||||
//
|
||||
// enum { START = 0x1000 }; // leading size to skip
|
||||
// for (DWORD i = START; i < size - sizeof(argtype); i++)
|
||||
// for (DWORD i = offset; i < size - sizeof(argtype); i++)
|
||||
// if (op == *(optype *)(start + i)) {
|
||||
// DWORD t = *(DWORD *)(start + i + sizeof(optype));
|
||||
// if (t == arg1) {
|
||||
@ -299,14 +412,29 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
|
||||
|
||||
MEMDBG_BEGIN_NAMESPACE
|
||||
|
||||
DWORD findJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
|
||||
{ return findWordCall(word_jmp, funcAddr, lowerBound, upperBound - lowerBound); }
|
||||
DWORD findLongJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||
{ return findWordCall(word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||
|
||||
DWORD findFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
|
||||
{ return findWordCall(word_call, funcAddr, lowerBound, upperBound - lowerBound); }
|
||||
DWORD findShortJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||
{ return findByteCall(byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
|
||||
|
||||
DWORD findNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
|
||||
{ return findByteCall(byte_call, funcAddr, lowerBound, upperBound - lowerBound); }
|
||||
DWORD findFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
|
||||
{ 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)
|
||||
{
|
||||
@ -322,9 +450,8 @@ DWORD findPushByteAddress(BYTE value, DWORD lowerBound, DWORD 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 };
|
||||
const DWORD size = upperBound - lowerBound - PatternSize;
|
||||
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
|
||||
//WCHAR str[0x40];
|
||||
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) {
|
||||
DWORD t = *(DWORD *)(lowerBound + i + 2);
|
||||
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);
|
||||
@ -352,9 +479,154 @@ DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upper
|
||||
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 };
|
||||
const DWORD size = upperBound - lowerBound - PatternSize;
|
||||
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++)
|
||||
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) {
|
||||
DWORD t = *(DWORD *)(lowerBound + i + 2);
|
||||
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);
|
||||
@ -391,16 +663,15 @@ DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount,
|
||||
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 };
|
||||
const DWORD size = upperBound - lowerBound - PatternSize;
|
||||
const DWORD fun = (DWORD)funcAddr;
|
||||
//WCHAR str[0x40];
|
||||
DWORD ret = 0;
|
||||
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) {
|
||||
DWORD t = *(DWORD *)(lowerBound + i + 2);
|
||||
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);
|
||||
//OutputConsole(str);
|
||||
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);
|
||||
//OutputConsole(str);
|
||||
ret = lowerBound + j;
|
||||
break;
|
||||
}
|
||||
|
||||
} else
|
||||
i += 6;
|
||||
@ -420,22 +693,36 @@ DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD u
|
||||
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)
|
||||
while (byte_int3 == *(BYTE *)++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)
|
||||
while (byte_int3 == *(BYTE *)++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)
|
||||
{
|
||||
start &= ~0xf;
|
||||
@ -466,6 +753,31 @@ DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range)
|
||||
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 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;
|
||||
}
|
||||
|
||||
//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
|
||||
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
|
||||
|
||||
// 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
|
||||
|
||||
#include "memdbg/memdbg.h"
|
||||
#ifndef MEMDBG_NO_STL
|
||||
# include <functional>
|
||||
#endif // MEMDBG_NO_STL
|
||||
|
||||
MEMDBG_BEGIN_NAMESPACE
|
||||
|
||||
/// Estimated maximum size of the caller function, the same as ITH FindCallAndEntryAbs
|
||||
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().
|
||||
*
|
||||
* @param funcAddr callee function address
|
||||
@ -27,12 +66,15 @@ enum { MaximumFunctionSize = 0x800 };
|
||||
* 0x81,0xec: sub esp XXOO (0xec81)
|
||||
* 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 findCallerAddressAfterInt3(dword_t funcAddr, 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 findLastCallerAddressAfterInt3(dword_t funcAddr, 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 offset = MemoryPaddingOffset);
|
||||
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 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.
|
||||
@ -41,9 +83,14 @@ dword_t findMultiCallerAddress(dword_t funcAddr, const dword_t funcInsts[], dwor
|
||||
* @param funcAddr callee function address
|
||||
* @param lowerBound the lower 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
|
||||
*/
|
||||
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.
|
||||
@ -52,16 +99,28 @@ dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound
|
||||
* @param funcAddr callee function address
|
||||
* @param lowerBound the lower 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
|
||||
*/
|
||||
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)
|
||||
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
|
||||
inline dword_t findCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound)
|
||||
{ return findFarCallAddress(funcAddr, lowerBound, upperBound); }
|
||||
/// Default to far call, for backward compatibility
|
||||
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, 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
|
||||
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
|
||||
*/
|
||||
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.
|
||||
@ -102,6 +165,7 @@ dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumF
|
||||
* @exception illegal memory access
|
||||
*/
|
||||
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.
|
||||
|
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 "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
|
||||
//# pragma warning(disable:4018) // C4018: signed/unsigned mismatch
|
||||
//#endif // _MSC_VER
|
||||
|
||||
namespace { // unnamed
|
||||
|
||||
// Replacement of wcscpy_s which is not available on Windows XP's msvcrt
|
||||
// http://sakuradite.com/topic/247
|
||||
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
|
||||
|
||||
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);
|
||||
PLDR_DATA_TABLE_ENTRY it;
|
||||
@ -38,6 +47,7 @@ BOOL getCurrentProcessName(LPWSTR buffer, int bufferSize)
|
||||
return 0 == wcscpy_safe(buffer, bufferSize, it->BaseDllName.Buffer);
|
||||
}
|
||||
|
||||
// See: ITH FillRange
|
||||
BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBound)
|
||||
{
|
||||
//assert(lower);
|
||||
@ -86,15 +96,114 @@ BOOL getModuleMemoryRange(LPCWSTR moduleName, DWORD *lowerBound, DWORD *upperBou
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL getCurrentMemoryRange(DWORD *lowerBound, DWORD *upperBound)
|
||||
BOOL getProcessMemoryRange(DWORD *lowerBound, DWORD *upperBound)
|
||||
{
|
||||
WCHAR procName[MAX_PATH]; // cached
|
||||
*lowerBound = 0;
|
||||
*upperBound = 0;
|
||||
return getCurrentProcessName(procName, MAX_PATH)
|
||||
return getProcessName(procName, MAX_PATH)
|
||||
&& 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
|
||||
|
||||
// EOF
|
||||
|
@ -4,6 +4,9 @@
|
||||
// 4/20/2014 jichi
|
||||
|
||||
#include <windows.h>
|
||||
#ifndef MEMDBG_NO_STL
|
||||
# include <functional>
|
||||
#endif // MEMDBG_NO_STL
|
||||
|
||||
#ifndef NTINSPECT_BEGIN_NAMESPACE
|
||||
# define NTINSPECT_BEGIN_NAMESPACE namespace NtInspect {
|
||||
@ -14,17 +17,73 @@
|
||||
|
||||
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
|
||||
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
|
||||
* 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);
|
||||
|
||||
/// Get memory of the current process
|
||||
BOOL getCurrentMemoryRange(_Out_ DWORD *lowerBound, _Out_ DWORD *upperBound);
|
||||
/// Get memory of the current process module
|
||||
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
|
||||
|
||||
|
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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user