mireado

update to 3.5640.1

update to ITHVNR 3.5640.1
and translation
Showing 140 changed files with 18351 additions and 1108 deletions
# 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)
......
// 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
{
}
......
#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);
......
#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);
......
#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;
}
......
#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)
{
}
......
#pragma once
#include "ITH.h"
#include "utility.h" // UniqueHandle, CriticalSection
......
......@@ -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;
}
......
......@@ -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.";
\ No newline at end of file
......
......@@ -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;
}
......
#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
{
......
......@@ -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 (GetKeyState(VK_CONTROL) & 0x8000)
{
if (wParam == 1)
{
if (wParam == 1)
{
Edit_SetSel(hwndEdit, 0, -1);
SendMessage(hwndEdit, WM_COPY, 0, 0);
}
Edit_SetSel(hwndEdit, 0, -1);
SendMessage(hwndEdit, WM_COPY, 0, 0);
}
return 0;
}
return 0;
case WM_LBUTTONUP:
if (hwndEdit)
SendMessage(hwndEdit, WM_COPY, 0, 0);
if (hwndEdit)
SendMessage(hwndEdit, WM_COPY, 0, 0);
default:
{
return proc(hWnd, message, wParam, lParam);
}
{
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"
......
# 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
......
@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
......
#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
#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
# hashutil.pri
# 6/28/2011 jichi
DEFINES += WITH_LIB_HASHUTIL
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/hashstr.h \
$$PWD/hashutil.h
# EOF
# # 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
)
// ithsys.cc
// 8/21/2013 jichi
// Branch: ITH_SYS/SYS.cpp, rev 126
//
// 8/24/2013 TODO:
// - Clean up the code
// - Move my old create remote thread for ITH2 here
#include "ithsys/ithsys.h"
//#include "vnrhook/src/util/growl.h"
//#define ITH_SYS_SECTION L"ITH_SysSection"
#define ITH_THREADMAN_SECTION L"VNR_SYS_THREAD"
// jichi 9/28/2013: Weither use NtThread or RemoteThread
// RemoteThread works on both Windows 7 or Wine, while NtThread does not work on wine
#define ITH_ENABLE_THREADMAN (!IthIsWindows8OrGreater() && !IthIsWine())
//#define ITH_ENABLE_THREADMAN true
//#define ITH_ENABLE_WINAPI // jichi: prefer Win32 API to NTDLL API
// Helpers
// jichi 2/3/2015: About GetVersion
// Windows XP SP3: 5.1
// Windows 7: 6.1, 0x1db10106
// Windows 8: 6.2, 0x23f00206
// Windows 10: 6.2, 0x23f00206 (build 9926):
BOOL IthIsWindowsXp()
{
static BOOL ret = -1; // cached
if (ret < 0) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439%28v=vs.85%29.aspx
DWORD v = ::GetVersion();
BYTE major = LOBYTE(LOWORD(v));
//DWORD minor = (DWORD)(HIBYTE(LOWORD(v)));
// Windows XP = 5.1
//ret = major < 6 ? 1 : 0;
ret = major < 6;
}
return ret;
}
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn424972%28v=vs.85%29.aspx
// The same as IsWindows8OrGreater, which I don't know if the function is available to lower Windows.
static BOOL IthIsWindows8OrGreater() // this function is not exported
{
static BOOL ret = -1; // cached
if (ret < 0) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439%28v=vs.85%29.aspx
DWORD v = ::GetVersion();
BYTE major = LOBYTE(LOWORD(v)),
minor = HIBYTE(LOWORD(v));
//DWORD minor = (DWORD)(HIBYTE(LOWORD(v)));
// Windows 8/10 = 6.2
ret = major > 6 || (major == 6 && minor >= 2);
}
return ret;
}
BOOL IthIsWine()
{
static BOOL ret = -1; // cached
if (ret < 0) {
const wchar_t *path;
wchar_t buffer[MAX_PATH];
if (UINT sz = ::GetSystemDirectoryW(buffer, MAX_PATH)) {
path = buffer;
::wcscpy(buffer + sz, L"\\winecfg.exe");
} else
path = L"C:\\Windows\\System32\\winecfg.exe";
//ITH_MSG(path);
ret = ::GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES ? TRUE : FALSE;
}
return ret;
}
// jichi 9/28/2013: prevent parallelization in wine
void IthCoolDown()
{
// http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Thread/NtDelayExecution.html
//const LONGLONG timeout = -10000; // in 100ns, i.e. 1ms
//NtDelayExecution(FALSE, (PLARGE_INTEGER)&timeout);
//NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
// Flush the instruction cache line, and prevent wine from rending things in parallel
if (IthIsWine())
IthSleep(1); // sleep for 1 ms
//__asm
//{
// //mov eax,0x2710 // = 10000
// mov ecx,time
// mul ecx
// neg eax
// adc edx,0
// neg edx
// push edx
// push eax
// push esp
// push 0
// call dword ptr [NtDelayExecution]
// add esp,8
//}
}
// jichi 9/23/2013: wine deficenciy on mapping sections
// Whe set to false, do not map sections.
//static bool ith_has_section = true;
//#ifdef ITH_WINE
//# include "winddk/winddk.h"
//#endif // ITH_WINE
//#define SEC_BASED 0x200000 // jichi 8/24/2013: emoved
// jichi 10/6/2013
// See: http://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
// See: http://www.codeproject.com/Articles/16598/Get-Your-DLL-s-Path-Name
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define CURRENT_MODULE_HANDLE ((HINSTANCE)&__ImageBase)
size_t IthGetCurrentModulePath(wchar_t *buf, size_t len)
{ return ::GetModuleFileNameW(CURRENT_MODULE_HANDLE, buf, len); }
// - Global variables -
#ifdef ITH_HAS_HEAP
HANDLE hHeap; // used in ith/common/memory.h
#endif // ITH_HAS_HEAP
DWORD current_process_id;
DWORD debug;
BYTE launch_time[0x10];
LPVOID page;
// jichi 6/12/2015: https://en.wikipedia.org/wiki/Shift_JIS
// Leading table for SHIFT-JIS encoding
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
};
namespace { // unnamed
WCHAR file_path[MAX_PATH] = L"\\??\\";
LPWSTR current_dir;
DWORD page_locale;
HANDLE root_obj,
dir_obj,
codepage_section,
thread_man_section;
BYTE file_info[0x1000];
// - Helper functions -
inline DWORD GetShareMemory()
{
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0x4C]
}
}
inline LARGE_INTEGER *GetTimeBias()
{ __asm mov eax,0x7ffe0020 }
//Get full path of current process.
//inline LPWSTR GetModulePath()
//{
// __asm
// {
// mov eax,fs:[0x30]
// mov eax,[eax+0xC]
// mov eax,[eax+0xC]
// mov eax,[eax+0x28]
// }
//}
// - Singleton classes -
BYTE normal_routine[0x14] = {
0x51,0x52,0x64,0x89,0x23,0x55,0xff,0xd0,0x50,0x6a,0xfe,0xff,0x15,0x14,0x00,0x00,0x00
};
BYTE except_routine[0xe0] = {
0xba,0x08,0x00,0x00,0x00,0x8b,0xc1,0x83,0xe0,0x0f,0x83,0xf8,0x0a,0x72,0x02,0x04,
0x07,0x04,0x30,0x66,0xab,0xc1,0xc9,0x04,0x4a,0x75,0xea,0xc3,0x00,0x00,0x00,0x00,
0x8b,0x44,0xe4,0x04,0x31,0xf6,0x8b,0x28,0x8b,0x4c,0xe4,0x0c,0x8b,0x99,0xb8,0x00,
0x00,0x00,0x81,0xec,0x40,0x02,0x00,0x00,0x8d,0x7c,0xe4,0x40,0x89,0xe0,0x56,0x6a,
0x1c,0x50,0x56,0x53,0x6a,0xff,0xff,0x15,0x18,0x00,0x00,0x00,0x85,0xc0,0x75,0x98,
0x89,0xe0,0x50,0x68,0x00,0x02,0x00,0x00,0x57,0x6a,0x02,0x53,0x6a,0xff,0xff,0x15,
0x18,0x00,0x00,0x00,0x85,0xc0,0x75,0xe6,0x5e,0x0f,0xc1,0xf7,0xfd,0xb0,0x5c,0x66,
0xf2,0xaf,0x66,0xc7,0x47,0x02,0x3a,0x00,0x89,0xd9,0x2b,0x0c,0xe4,0xe8,0x7e,0xff,
0xff,0xff,0x47,0x47,0x87,0xfe,0x89,0xe9,0xe8,0x73,0xff,0xff,0xff,0x47,0x47,0x31,
0xc0,0x89,0x47,0x10,0x6a,0x00,0x57,0x56,0x6a,0x00,0xfc,0xff,0x15,0x1c,0x00,0x00,
0x00,0x83,0xc8,0xff,0xeb,0xbe
};
// jichi 8/24/2013: Could be initialized using NtMapViewOfSection/ZwMapViewOfSection
// This class cannot have constructor / destructor
struct _ThreadView {
UINT_PTR mutex,
count;
DWORD proc_record[1];
};
class : private _ThreadView { // ThreadStartManager
enum {
ADDR0 = 0xD
, ADDR1 = 0x48
, ADDR2 = 0x60
, ADDR3 = 0x9D
};
public:
LPVOID GetProcAddr(HANDLE hProc)
{
AcquireLock();
DWORD pid,addr,len;
if (hProc == NtCurrentProcess())
pid = ::current_process_id;
else {
PROCESS_BASIC_INFORMATION info;
NtQueryInformationProcess(hProc, ProcessBasicInformation, &info, sizeof(info), &len);
pid=info.uUniqueProcessId;
}
pid >>= 2;
for (UINT_PTR i = 0; i < count; i++)
if (pid == (proc_record[i] & 0xfff)) {
addr = proc_record[i] & ~0xfff;
ReleaseLock();
return (LPVOID)addr;
}
len = 0x1000;
NtAllocateVirtualMemory(hProc, (PVOID *)(proc_record + count), 0, &len,
MEM_COMMIT,PAGE_EXECUTE_READWRITE);
DWORD base = proc_record[count];
proc_record[count] |= pid;
union {
LPVOID buffer;
DWORD b;
};
b = base;
LPVOID fun_table[3];
*(DWORD *)(normal_routine + ADDR0) += base;
NtWriteVirtualMemory(hProc, buffer, normal_routine, 0x14, 0);
*(DWORD *)(normal_routine + ADDR0) -= base;
b += 0x14;
fun_table[0] = NtTerminateThread;
fun_table[1] = NtQueryVirtualMemory;
fun_table[2] = MessageBoxW;
NtWriteVirtualMemory(hProc, buffer, fun_table, 0xC, 0);
b += 0xc;
*(DWORD *)(except_routine + ADDR1) += base;
*(DWORD *)(except_routine + ADDR2) += base;
*(DWORD *)(except_routine + ADDR3) += base;
NtWriteVirtualMemory(hProc, buffer, except_routine, 0xE0, 0);
*(DWORD *)(except_routine + ADDR1) -= base;
*(DWORD *)(except_routine + ADDR2) -= base;
*(DWORD *)(except_routine + ADDR3) -= base;
count++;
ReleaseLock();
return (LPVOID)base;
}
void ReleaseProcessMemory(HANDLE hProc)
{
DWORD pid,addr,len;
AcquireLock();
if (hProc==NtCurrentProcess())
pid = ::current_process_id;
else {
PROCESS_BASIC_INFORMATION info;
NtQueryInformationProcess(hProc,ProcessBasicInformation,&info,sizeof(info),&len);
pid = info.uUniqueProcessId;
}
pid >>= 2;
//NtWaitForSingleObject(thread_man_mutex,0,0);
for (UINT_PTR i = 0; i < count; i++) {
if ((proc_record[i]&0xfff) == pid) {
addr = proc_record[i] & ~0xfff;
DWORD size=0x1000;
NtFreeVirtualMemory(hProc, (PVOID *)&addr, &size, MEM_RELEASE);
count--;
for (UINT_PTR j = i; j < count; j++)
proc_record[j] = proc_record[j + 1];
proc_record[count] = 0;
ReleaseLock();
//NtReleaseMutant(thread_man_mutex,0);
return;
}
}
ReleaseLock();
//NtReleaseMutant(thread_man_mutex,0);
}
void CheckProcessMemory()
{
UINT_PTR i, j, flag, addr;
DWORD len;
CLIENT_ID id;
OBJECT_ATTRIBUTES oa = {};
HANDLE hProc;
BYTE buffer[8];
AcquireLock();
id.UniqueThread = 0;
oa.uLength = sizeof(oa);
for (i = 0; i < count ; i++) {
id.UniqueProcess = (proc_record[i]&0xfff)<<2;
addr = proc_record[i] & ~0xfff;
flag = 0;
if (NT_SUCCESS(NtOpenProcess(&hProc, PROCESS_VM_OPERATION|PROCESS_VM_READ, &oa, &id))) {
if (NT_SUCCESS(NtReadVirtualMemory(hProc, (PVOID)addr, buffer, 8, &len)))
if (::memcmp(buffer, normal_routine, 4) == 0)
flag = 1;
NtClose(hProc);
}
if (flag == 0) {
for (j = i; j < count; j++)
proc_record[j] = proc_record[j + 1];
count--;
i--;
}
}
ReleaseLock();
}
void AcquireLock()
{
LONG *p = (LONG *)&mutex;
while (_interlockedbittestandset(p,0))
YieldProcessor();
}
void ReleaseLock()
{
LONG *p = (LONG*)&mutex;
_interlockedbittestandreset(p, 0);
}
} *thread_man_ = nullptr; // global singleton
} // unnamed namespace
// - API functions -
extern "C" {
void FreeThreadStart(HANDLE hProc)
{
if (thread_man_)
::thread_man_->ReleaseProcessMemory(hProc);
}
void CheckThreadStart()
{
if (thread_man_)
::thread_man_->CheckProcessMemory();
// jichi 2/2/2015: This function is only used to wait for injected threads vnrhost.
// Sleep for 100 ms to wait for remote thread to start
//IthSleep(100);
//IthCoolDown();
}
void IthSleep(int time)
{
__asm
{
mov eax,0x2710 // jichi = 10000
mov ecx,time
mul ecx
neg eax
adc edx,0
neg edx
push edx
push eax
push esp
push 0
call dword ptr [NtDelayExecution]
add esp,8
}
}
void IthSystemTimeToLocalTime(LARGE_INTEGER *time)
{ time->QuadPart -= GetTimeBias()->QuadPart; }
int FillRange(LPCWSTR name, DWORD *lower, DWORD *upper)
{
PLDR_DATA_TABLE_ENTRY it;
LIST_ENTRY *begin;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
mov begin,eax
}
while (it->SizeOfImage) {
if (::_wcsicmp(it->BaseDllName.Buffer, name) == 0) {
*lower = *upper = (DWORD)it->DllBase;
MEMORY_BASIC_INFORMATION info = {};
DWORD l,size;
size = 0;
do {
NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)(*upper), MemoryBasicInformation, &info, sizeof(info), &l);
if (info.Protect&PAGE_NOACCESS) {
it->SizeOfImage=size;
break;
}
size += info.RegionSize;
*upper += info.RegionSize;
} while (size < it->SizeOfImage);
return 1;
}
it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink;
if (it->InLoadOrderModuleList.Flink == begin)
break;
}
return 0;
}
DWORD SearchPattern(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]
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
}
}
// jichi 2/5/2014: '?' = 0xff
// See: http://sakuradite.com/topic/124
DWORD SearchPatternEx(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length, BYTE wildcard) // KMP
{
__asm
{
// jichi 2/5/2014 BEGIN
mov bl,wildcard
// jichi 2/5/2014 END
mov eax,search_length
alloc:
push 0
sub eax,1
jnz alloc // jichi 2/5/2014: this will also set %eax to zero
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] // search
// jichi 2/5/2014 BEGIN
mov bh,al // save loaded byte to reduce cache access. %ah is not used and always zero
cmp al,bl // %bl is the wildcard byte
sete al
test al,al
jnz wildcard_matched
mov al,bh // restore the loaded byte
// jichi 2/5/2014 END
cmp al,byte ptr [esi+edx] // base
sete al
// jichi 2/5/2014 BEGIN
wildcard_matched:
// jichi 2/5/2014 END
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
}
}
DWORD IthGetMemoryRange(LPCVOID mem, DWORD *base, DWORD *size)
{
DWORD r;
MEMORY_BASIC_INFORMATION info;
NtQueryVirtualMemory(NtCurrentProcess(), const_cast<LPVOID>(mem), MemoryBasicInformation, &info, sizeof(info), &r);
if (base)
*base = (DWORD)info.BaseAddress;
if (size)
*size = info.RegionSize;
return (info.Type&PAGE_NOACCESS) == 0;
}
// jichi 9/25/2013
// See: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.nls/doc/nlsgdrf/multi-byte_widechar_subr.htm
// SJIS->Unicode. 'mb' must be null-terminated. 'wc' should have enough space ( 2*strlen(mb) is safe).
//#ifdef ITH_WINE
//int MB_WC(char *mb, wchar_t *wc)
//{ return mbstowcs(wc, mb, 0x100); }
//
//#else
int MB_WC(char *mb, wchar_t *wc)
{
__asm
{
mov esi,mb
mov edi,wc
mov edx,page
lea ebx,LeadByteTable
add edx,0x220
push 0
_mb_translate:
movzx eax,word ptr [esi]
test al,al
jz _mb_fin
movzx ecx,al
xlat
test al,1
cmovnz cx, word ptr [ecx*2+edx-0x204]
jnz _mb_next
mov cx,word ptr [ecx*2+edx]
mov cl,ah
mov cx, word ptr [ecx*2+edx]
_mb_next:
mov [edi],cx
add edi,2
movzx eax,al
add esi,eax
inc dword ptr [esp]
jmp _mb_translate
_mb_fin:
pop eax
}
}
// Count characters of 'mb' string. 'mb_length' is max length.
// jichi 9/25/2013: This function is not used
//int MB_WC_count(char *mb, int mb_length)
//{
// __asm
// {
// xor eax,eax
// xor edx,edx
// mov esi,mb
// mov edi,mb_length
// lea ebx,LeadByteTable
//_mbc_count:
// mov dl,byte ptr [esi]
// test dl,dl
// jz _mbc_finish
// movzx ecx, byte ptr [ebx+edx]
// add esi,ecx
// inc eax
// sub edi,ecx
// ja _mbc_count
//_mbc_finish:
// }
//}
// jichi 9/25/2013
// See: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.nls/doc/nlsgdrf/multi-byte_widechar_subr.htm
// Unicode->SJIS. Analogous to MB_WC.
//#ifdef ITH_WINE
//int WC_MB(wchar_t *wc, char *mb)
//{ return wcstombs(mb, wc, 0x100); }
//
//#else
int WC_MB(wchar_t *wc, char *mb)
{
__asm
{
mov esi,wc
mov edi,mb
mov edx,page
add edx,0x7c22
xor ebx,ebx
_wc_translate:
movzx eax,word ptr [esi]
test eax,eax
jz _wc_fin
mov cx,word ptr [eax*2+edx]
test ch,ch
jz _wc_single
mov [edi+ebx],ch
inc ebx
_wc_single:
mov [edi+ebx],cl
inc ebx
add esi,2
jmp _wc_translate
_wc_fin:
mov eax,ebx
}
}
//Initialize environment for NT native calls. Not thread safe so only call it once in one module.
//1. Create new heap. Future memory requests are handled by this heap.
//Destroying this heap will completely release all dynamically allocated memory, thus prevent memory leaks on unload.
//2. Create handle to root directory of process objects (section/event/mutex/semaphore).
//NtCreate* calls will use this handle as base directory.
//3. Load SJIS code page. First check for Japanese locale. If not then load from 'C_932.nls' in system folder.
//MB_WC & WC_MB use this code page for translation.
//4. Locate current NT path (start with \??\).
//NtCreateFile requires full path or a root handle. But this handle is different from object.
//5. Map shared memory for ThreadStartManager into virtual address space.
//This will allow IthCreateThread function properly.
BOOL IthInitSystemService()
{
PPEB peb;
//NTSTATUS status;
DWORD size;
ULONG LowFragmentHeap;
UNICODE_STRING us;
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
IO_STATUS_BLOCK ios;
HANDLE codepage_file;
LARGE_INTEGER sec_size = {0x1000, 0};
__asm
{
mov eax,fs:[0x18]
mov ecx,[eax+0x20]
mov eax,[eax+0x30]
mov peb,eax
mov current_process_id,ecx
}
debug = peb->BeingDebugged;
LowFragmentHeap = 2;
#ifdef ITH_HAS_HEAP
::hHeap = RtlCreateHeap(0x1002, 0, 0, 0, 0, 0);
RtlSetHeapInformation(::hHeap, HeapCompatibilityInformation, &LowFragmentHeap, sizeof(LowFragmentHeap));
#endif // ITH_HAS_HEAP
LPWSTR t = nullptr, // jichi: path to system32, such as "c:\windows\system32"
obj = nullptr; // jichi: path to current kernel session, such as "Sessions\\1\\BaseNamedObjects"
// jichi 9/22/2013: This would crash wine with access violation exception.
if (!IthIsWine()) {
// jichi 9/22/2013: For ChuSingura46+1 on Windows 7
// t = L"C:\\Windows\\system32";
// obj = L"\\Sessions\\1\\BaseNamedObjects";
// On Windows XP
// t = L"C:\\WINDOWS\\system32";
// obj = L"\\BaseNamedObjects";
MEMORY_BASIC_INFORMATION info;
if (!NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(), peb->ReadOnlySharedMemoryBase, MemoryBasicInformation, &info, sizeof(info), &size)))
return FALSE;
DWORD base = (DWORD)peb->ReadOnlySharedMemoryBase;
DWORD end = base + info.RegionSize - 0x40;
static WCHAR system32[] = L"system32";
for (;base < end; base += 2)
if (::memcmp((PVOID)base, system32, 0x10) == 0) {
t = (LPWSTR)base;
while (*t-- != L':');
obj = (LPWSTR)base;
while (*obj != L'\\') obj++;
break;
}
if (base == end)
return FALSE;
}
//ITH_MSG(t);
//ITH_MSG(obj);
LDR_DATA_TABLE_ENTRY *ldr_entry = (LDR_DATA_TABLE_ENTRY*)peb->Ldr->InLoadOrderModuleList.Flink;
// jichi 7/12/2015: This will fail when the file path is a remote path such as:
// Original remote file path: \\??\\\\\\psf\\Host\\Local\\Windows\\Games\\ShinaRio\\Ayakashibito_trial\\");
// Correct UNC path: \\??\\\\UNC\\psf\\Host\\Local\\Windows\\Games\\ShinaRio\\Ayakashibito_trial\\");
//RtlInitUnicodeString(&us, L"\\??\\UNC\\psf\\Host\\Local\\Windows\\Games\\ShinaRio\\Ayakashibito_trial\\");
//WCHAR file_path[MAX_PATH] = L"\\??\\";
LPCWSTR modulePath = ldr_entry->FullDllName.Buffer;
if (modulePath[0] == '\\' && modulePath[1] == '\\') { // This is a remote path
::file_path[4] = 'U';
::file_path[5] = 'N';
::file_path[6] = 'C';
::wcscpy(::file_path + 7, modulePath + 1);
} else
::wcscpy(::file_path + 4, modulePath);
current_dir = ::wcsrchr(::file_path, L'\\') + 1;
*current_dir = 0;
//GROWL(::file_path);
RtlInitUnicodeString(&us, ::file_path);
if (!NT_SUCCESS(NtOpenFile(&dir_obj,FILE_LIST_DIRECTORY|FILE_TRAVERSE|SYNCHRONIZE,
&oa,&ios,FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT)))
return FALSE;
// jichi 9/22/2013: Get kernel object session ID
// See: http://www.brianbondy.com/blog/id/100/
// It seems that on sessionId is 0 on Windows XP, and 1 on Windows Vista and later
// I assume that sessionId is in [0,9]
// For ChuSingura46+1 on Windows 7
// obj = L"\\Sessions\\1\\BaseNamedObjects";
// On Windows XP
// obj = L"\\BaseNamedObjects";
//ITH_MSG(obj);
{
if (obj)
RtlInitUnicodeString(&us, obj);
else { // jichi ITH is on Wine
// Get session ID in PEB
// See: http://msdn.microsoft.com/en-us/library/bb432286%28v=vs.85%29.aspx
DWORD sessionId = peb->SessionId;
if (!sessionId) // Windows XP
RtlInitUnicodeString(&us, L"\\BaseNamedObjects");
else { // Windows Vista +
wchar_t path[] = L"\\Sessions\\0\\BaseNamedObjects";
path[10] += (wchar_t)sessionId; // replace 0 with the session ID
RtlInitUnicodeString(&us, path);
}
}
}
if (!NT_SUCCESS(NtOpenDirectoryObject(&::root_obj, READ_CONTROL|0xf, &oa)))
return FALSE;
::page = peb->InitAnsiCodePageData;
enum { CP932 = 932 };
// jichi 9/23/2013: Access violation on Wine
if (IthIsWine())
// One wine, there is no C_932.nls
//page_locale = 0x4e4; // 1252, English
//page_locale = GetACP(); // This will return 932 when LC_ALL=ja_JP.UTF-8 on wine
// Always set locale to CP932 on Wine, since C_932.nls could be missing.
::page_locale = CP932;
else
::page_locale = *(DWORD *)page >> 16;
if (::page_locale == CP932) {
oa.hRootDirectory = ::root_obj;
oa.uAttributes |= OBJ_OPENIF;
} else { // Unreachable or wine
//#ifdef ITH_WINE
// // jichi 9/22/2013: For ChuSingura46+1 on Windows 7
// //t = L"C:\\Windows\\system32";
// wchar_t buffer[MAX_PATH];
// if (!t) { // jichi 9/22/2013: ITH is one wine
// if (UINT sz = ::GetSystemDirectoryW(buffer, MAX_PATH)) {
// buffer[sz] = 0;
// t = buffer;
// } else
// t = L"C:\\Windows\\System32"; // jichi 9/29/2013: sth is wrong here
// }
//#endif // ITH_WINE
::wcscpy(::file_path + 4, t);
t = ::file_path;
while(*++t);
if (*(t-1)!=L'\\')
*t++=L'\\';
::wcscpy(t,L"C_932.nls");
RtlInitUnicodeString(&us, ::file_path);
if (!NT_SUCCESS(NtOpenFile(&codepage_file, FILE_READ_DATA, &oa, &ios,FILE_SHARE_READ,0)))
return FALSE;
oa.hRootDirectory = ::root_obj;
oa.uAttributes |= OBJ_OPENIF;
RtlInitUnicodeString(&us, L"JPN_CodePage");
if (!NT_SUCCESS(NtCreateSection(&codepage_section, SECTION_MAP_READ,
&oa,0, PAGE_READONLY, SEC_COMMIT, codepage_file)))
return FALSE;
NtClose(codepage_file);
size = 0;
::page = nullptr;
if (!NT_SUCCESS(NtMapViewOfSection(::codepage_section, NtCurrentProcess(),
&::page,
0, 0, 0, &size, ViewUnmap, 0,
PAGE_READONLY)))
return FALSE;
}
if (ITH_ENABLE_THREADMAN) {
RtlInitUnicodeString(&us, ITH_THREADMAN_SECTION);
if (!NT_SUCCESS(NtCreateSection(&thread_man_section, SECTION_ALL_ACCESS, &oa, &sec_size,
PAGE_EXECUTE_READWRITE, SEC_COMMIT, 0)))
return FALSE;
size = 0;
// http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Section/NtMapViewOfSection.html
thread_man_ = nullptr;
if (!NT_SUCCESS(NtMapViewOfSection(thread_man_section, NtCurrentProcess(),
(LPVOID *)&thread_man_,
0,0,0, &size, ViewUnmap, 0,
PAGE_EXECUTE_READWRITE)))
return FALSE;
}
return TRUE;
}
//Release resources allocated by IthInitSystemService.
//After destroying the heap, all memory allocated by ITH module is returned to system.
void IthCloseSystemService()
{
if (::page_locale != 0x3a4) {
NtUnmapViewOfSection(NtCurrentProcess(), ::page);
NtClose(::codepage_section);
}
if (ITH_ENABLE_THREADMAN) {
NtUnmapViewOfSection(NtCurrentProcess(), ::thread_man_);
NtClose(::thread_man_section);
}
NtClose(::root_obj);
#ifdef ITH_HAS_HEAP
RtlDestroyHeap(::hHeap);
#endif // ITH_HAS_HEAP
}
//Check for existence of a file in current folder. Thread safe after init.
//For ITH main module, it's ITH folder. For target process it's the target process's current folder.
BOOL IthCheckFile(LPCWSTR file)
{
//return PathFileExistsW(file); // jichi: need Shlwapi.lib
//return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
//return GetFileAttributesW(file) != INVALID_FILE_ATTRIBUTES; // jichi: does not consider the current app's path
// jichi 9/22/2013: Following code does not work in Wine
// See: http://stackoverflow.com/questions/3828835/how-can-we-check-if-a-file-exists-or-not-using-win32-program
//WIN32_FIND_DATA FindFileData;
//HANDLE handle = FindFirstFileW(file, &FindFileData);
//if (handle != INVALID_HANDLE_VALUE) {
// FindClose(handle);
// return TRUE;
//}
//return FALSE;
if (IthIsWine()) {
HANDLE hFile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
return TRUE;
} else if (!wcschr(file, L':')) { // jichi: this is relative path
// jichi 9/22/2013: Change current directory to the same as main module path
// Otherwise NtFile* would not work for files with relative paths.
if (const wchar_t *path = GetMainModulePath()) // path to VNR's python exe
if (const wchar_t *base = wcsrchr(path, L'\\')) {
size_t dirlen = base - path + 1;
if (dirlen + wcslen(file) < MAX_PATH) {
wchar_t buf[MAX_PATH];
wcsncpy(buf, path, dirlen);
wcscpy(buf + dirlen, file);
return IthCheckFile(buf);
}
}
}
} else { // not wine
HANDLE hFile;
IO_STATUS_BLOCK isb;
UNICODE_STRING us;
RtlInitUnicodeString(&us, file);
OBJECT_ATTRIBUTES oa = { sizeof(oa), dir_obj, &us, 0, 0, 0};
// jichi 9/22/2013: Following code does not work in Wine
if (NT_SUCCESS(NtCreateFile(&hFile, FILE_READ_DATA, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0))) {
NtClose(hFile);
return TRUE;
}
}
return FALSE;
//return IthGetFileInfo(file,file_info);
//wcscpy(current_dir,file);
}
//Check for existence of files in current folder.
//Unlike IthCheckFile, this function allows wildcard character.
BOOL IthFindFile(LPCWSTR file)
{
NTSTATUS status;
HANDLE h;
UNICODE_STRING us;
OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
us.Buffer = const_cast<LPWSTR>(file);
LPCWSTR path = wcsrchr(file, L'\\');
if (path) {
us.Length = (path - file) << 1;
us.MaximumLength = us.Length;
} else {
us.Length = 0;
us.MaximumLength = 0;
}
IO_STATUS_BLOCK ios;
if (NT_SUCCESS(NtOpenFile(&h,FILE_LIST_DIRECTORY|SYNCHRONIZE,
&oa,&ios,FILE_SHARE_READ,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT))) {
BYTE info[0x400];
if (path)
RtlInitUnicodeString(&us, path + 1);
else
RtlInitUnicodeString(&us, file);
status = NtQueryDirectoryFile(h,0,0,0,&ios,info,0x400,FileBothDirectoryInformation,TRUE,&us,TRUE);
NtClose(h);
return NT_SUCCESS(status);
}
return FALSE;
}
//Analogous to IthFindFile, but return detail information in 'info'.
BOOL IthGetFileInfo(LPCWSTR file, LPVOID info, DWORD size)
{
NTSTATUS status;
HANDLE h;
UNICODE_STRING us;
LPCWSTR path = wcsrchr(file, L'\\');
us.Buffer = const_cast<LPWSTR>(file);
if (path) {
us.Length = (path - file) << 1;
us.MaximumLength = us.Length;
} else {
us.Length = 0;
us.MaximumLength = 0;
}
//RtlInitUnicodeString(&us,file);
OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
IO_STATUS_BLOCK ios;
if (NT_SUCCESS(NtOpenFile(&h,FILE_LIST_DIRECTORY|SYNCHRONIZE,
&oa,&ios,FILE_SHARE_READ,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT))) {
RtlInitUnicodeString(&us,file);
status = NtQueryDirectoryFile(h,0,0,0,&ios,info,size,FileBothDirectoryInformation,0,&us,0);
status = NT_SUCCESS(status);
NtClose(h);
} else
status = FALSE;
return status;
}
//Check for existence of a file with full NT path(start with \??\).
BOOL IthCheckFileFullPath(LPCWSTR file)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, file);
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
if (NT_SUCCESS(NtCreateFile(&hFile,FILE_READ_DATA,&oa,&isb,0,0,FILE_SHARE_READ,FILE_OPEN,0,0,0))) {
NtClose(hFile);
return TRUE;
} else
return FALSE;
}
//Create or open file in current folder. Analogous to Win32 CreateFile.
//option: GENERIC_READ / GENERIC_WRITE.
//share: FILE_SHARE_READ / FILE_SHARE_WRITE / FILE_SHARE_DELETE. 0 for exclusive access.
//disposition: FILE_OPEN / FILE_OPEN_IF.
//Use FILE_OPEN instead of OPEN_EXISTING and FILE_OPEN_IF for CREATE_ALWAYS.
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = { sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0 };
HANDLE hFile;
IO_STATUS_BLOCK isb;
return NT_SUCCESS(NtCreateFile(&hFile,
option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
&oa,&isb,0,0,share,disposition,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
hFile : INVALID_HANDLE_VALUE;
}
//Create a directory file in current folder.
HANDLE IthCreateDirectory(LPCWSTR name)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us,name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
return NT_SUCCESS(NtCreateFile(&hFile,FILE_LIST_DIRECTORY|FILE_TRAVERSE|SYNCHRONIZE,&oa,&isb,0,0,
FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN_IF,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT,0,0)) ?
hFile : INVALID_HANDLE_VALUE;
}
HANDLE IthCreateFileInDirectory(LPCWSTR name, HANDLE dir, DWORD option, DWORD share, DWORD disposition)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us,name);
if (dir == 0) dir = dir_obj;
OBJECT_ATTRIBUTES oa = {sizeof(oa), dir, &us, OBJ_CASE_INSENSITIVE, 0, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
return NT_SUCCESS(NtCreateFile(&hFile,
option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
&oa,&isb,0,0,share,disposition,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
hFile : INVALID_HANDLE_VALUE;
}
//Analogous to IthCreateFile, but with full NT path.
HANDLE IthCreateFileFullPath(LPCWSTR path, DWORD option, DWORD share, DWORD disposition)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us,path);
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
return NT_SUCCESS(NtCreateFile(&hFile,
option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
&oa,&isb,0,0,share,disposition,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
hFile : INVALID_HANDLE_VALUE;
}
//Create section object for sharing memory between processes.
//Similar to CreateFileMapping.
HANDLE IthCreateSection(LPCWSTR name, DWORD size, DWORD right)
{
// jichi 9/25/2013: GENERIC_ALL does NOT work one wine
// See ZwCreateSection: http://msdn.microsoft.com/en-us/library/windows/hardware/ff566428%28v=vs.85%29.aspx
//#ifdef ITH_WINE
enum { DesiredAccess = SECTION_ALL_ACCESS };
//#else
// enum { DesiredAccess = GENERIC_ALL }; // jichi 9/25/2013: not sure whhy ITH is usin GENERIC_ALL
//#endif // ITH_WINE
#define eval (NT_SUCCESS(NtCreateSection(&hSection, DesiredAccess, poa, &s, \
right, SEC_COMMIT, 0)) ? hSection : INVALID_HANDLE_VALUE)
HANDLE hSection;
LARGE_INTEGER s = {size, 0};
OBJECT_ATTRIBUTES *poa = nullptr;
// jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
// is pointed to freed object on the stack?! This will crash wine!
if (name) {
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us,OBJ_OPENIF,0,0};
poa = &oa;
return eval;
} else
return eval;
#undef retval
}
//Create event object. Similar to CreateEvent.
HANDLE IthCreateEvent(LPCWSTR name, DWORD auto_reset, DWORD init_state)
{
#define eval (NT_SUCCESS(NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, poa, auto_reset, init_state)) ? \
hEvent : INVALID_HANDLE_VALUE)
HANDLE hEvent;
OBJECT_ATTRIBUTES *poa = nullptr;
// jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
// is pointed to freed object on the stack?! This will crash wine!
if (name) {
UNICODE_STRING us;
RtlInitUnicodeString(&us,name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, OBJ_OPENIF, 0, 0};
poa = &oa;
return eval;
} else
return eval;
#undef eval
}
HANDLE IthOpenEvent(LPCWSTR name)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = { sizeof(oa), root_obj, &us, 0, 0, 0 };
HANDLE hEvent;
return NT_SUCCESS(NtOpenEvent(&hEvent, EVENT_ALL_ACCESS, &oa)) ?
hEvent : INVALID_HANDLE_VALUE;
}
void IthSetEvent(HANDLE hEvent) { NtSetEvent(hEvent, 0); }
void IthResetEvent(HANDLE hEvent) { NtClearEvent(hEvent); }
//Create mutex object. Similar to CreateMutex.
//If 'exist' is not null, it will be written 1 if mutex exist.
HANDLE IthCreateMutex(LPCWSTR name, BOOL InitialOwner, DWORD *exist)
{
#ifdef ITH_ENABLE_WINAPI
HANDLE ret = ::CreateMutexW(nullptr, InitialOwner, name);
if (exist)
*exist = ret == INVALID_HANDLE_VALUE || ::GetLastError() == ERROR_ALREADY_EXISTS;
return ret;
#else
#define eval NtCreateMutant(&hMutex, MUTEX_ALL_ACCESS, poa, InitialOwner)
UNICODE_STRING us;
HANDLE hMutex;
NTSTATUS status;
OBJECT_ATTRIBUTES *poa = nullptr;
// jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
// is pointed to freed object on the stack?! This will crash wine!
if (name) {
//GROWL(name);
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, OBJ_OPENIF, 0, 0};
poa = &oa;
status = eval;
//GROWL_DWORD(status);
} else
status = eval;
if (NT_SUCCESS(status)) {
if (exist)
*exist = status == STATUS_OBJECT_NAME_EXISTS;
return hMutex;
} else
return INVALID_HANDLE_VALUE;
#undef eval
#endif // ITH_ENABLE_WINAPI
}
HANDLE IthOpenMutex(LPCWSTR name)
{
#ifdef ITH_ENABLE_WINAPI
return ::OpenMutexW(MUTEX_ALL_ACCESS, FALSE, name);
#else
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, 0, 0, 0};
HANDLE hMutex;
if (NT_SUCCESS(NtOpenMutant(&hMutex, MUTEX_ALL_ACCESS, &oa)))
return hMutex;
else
return INVALID_HANDLE_VALUE;
#endif // ITH_ENABLE_WINAPI
}
BOOL IthReleaseMutex(HANDLE hMutex)
{ return NT_SUCCESS(NtReleaseMutant(hMutex, 0)); }
//Create new thread. 'hProc' must have following right.
//PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE.
HANDLE IthCreateThread(LPCVOID start_addr, DWORD param, HANDLE hProc)
{
HANDLE hThread;
// jichi 9/27/2013: NtCreateThread is not implemented in Wine 1.7
if (thread_man_) { // Windows XP
// jichi 9/29/2013: Reserved && commit stack size
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366803%28v=vs.85%29.aspx
// See: http://msdn.microsoft.com/en-us/library/ms810627.aspx
enum { DEFAULT_STACK_LIMIT = 0x400000 };
enum { DEFAULT_STACK_COMMIT = 0x10000 };
enum { PAGE_SIZE = 0x1000 };
CLIENT_ID id;
LPVOID protect;
USER_STACK stack = {};
CONTEXT ctx = {CONTEXT_FULL};
DWORD size = DEFAULT_STACK_LIMIT,
commit = DEFAULT_STACK_COMMIT;
if (!NT_SUCCESS(NtAllocateVirtualMemory(hProc, &stack.ExpandableStackBottom, 0, &size, MEM_RESERVE, PAGE_READWRITE)))
return INVALID_HANDLE_VALUE;
stack.ExpandableStackBase = (char *)stack.ExpandableStackBottom + size;
stack.ExpandableStackLimit = (char *)stack.ExpandableStackBase - commit;
size = PAGE_SIZE;
commit += size;
protect = (char *)stack.ExpandableStackBase - commit;
NtAllocateVirtualMemory(hProc, &protect, 0, &commit, MEM_COMMIT, PAGE_READWRITE);
DWORD oldAccess; // jichi 9/29/2013: unused
NtProtectVirtualMemory(hProc, &protect, &size, PAGE_READWRITE|PAGE_GUARD, &oldAccess);
ctx.SegGs = 0;
ctx.SegFs = 0x38;
ctx.SegEs = 0x20;
ctx.SegDs = 0x20;
ctx.SegSs = 0x20;
ctx.SegCs = 0x18;
ctx.EFlags = 0x3000;
ctx.Eip = (DWORD)thread_man_->GetProcAddr(hProc);
ctx.Eax = (DWORD)start_addr;
ctx.Ecx = ctx.Eip + 0x40;
ctx.Edx = 0xffffffff;
ctx.Esp = (DWORD)stack.ExpandableStackBase - 0x10;
ctx.Ebp = param;
// NTSYSAPI
// NTSTATUS
// NTAPI
// NtCreateThread(
// _Out_ PHANDLE ThreadHandle,
// _In_ ACCESS_MASK DesiredAccess,
// _In_ POBJECT_ATTRIBUTES ObjectAttributes,
// _In_ HANDLE ProcessHandle,
// _Out_ PCLIENT_ID ClientId,
// _In_ PCONTEXT ThreadContext,
// _In_ PUSER_STACK UserStack,
// _In_ BOOLEAN CreateSuspended
// );
if (NT_SUCCESS(NtCreateThread(
&hThread, // _Out_ PHANDLE ThreadHandle,
THREAD_ALL_ACCESS, // _In_ ACCESS_MASK DesiredAccess,
nullptr, // _In_ POBJECT_ATTRIBUTES ObjectAttributes,
hProc, // _In_ HANDLE ProcessHandle,
&id, // _Out_ PCLIENT_ID ClientId,
&ctx, // _In_ PCONTEXT ThreadContext,
&stack, // _In_ PUSER_STACK UserStack,
TRUE // _In_ BOOLEAN CreateSuspended
))) {
// On x64 Windows, NtCreateThread in ntdll calls NtCreateThread in ntoskrnl via WOW64,
// which maps 32-bit system call to the correspond 64-bit version.
// This layer doesn't correctly copy whole CONTEXT structure, so we must set it manually
// after the thread is created.
// On x86 Windows, this step is not necessary.
NtSetContextThread(hThread, &ctx);
NtResumeThread(hThread, 0);
} else
hThread = INVALID_HANDLE_VALUE;
} else {
// jichi 9/27/2013: CreateRemoteThread works on both Wine and Windows 7
// Use CreateRemoteThread instead
// FIXME 10/5/2031: Though sometimes works, CreateRemoteThread randomly crashes on wine.
// See:
// - http://www.unknowncheats.me/forum/c-and-c/64775-createremotethread-dll-injection.html
// - http://source.winehq.org/WineAPI/CreateRemoteThread.html
// - http://msdn.microsoft.com/en-us/library/windows/desktop/ms682437%28v=vs.85%29.aspx
// HANDLE WINAPI CreateRemoteThread(
// _In_ HANDLE hProcess,
// _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
// _In_ SIZE_T dwStackSize,
// _In_ LPTHREAD_START_ROUTINE lpStartAddress,
// _In_ LPVOID lpParameter,
// _In_ DWORD dwCreationFlags,
// _Out_ LPDWORD lpThreadId
// );
//ITH_TRY {
if (hProc == INVALID_HANDLE_VALUE)
hProc = GetCurrentProcess();
//DWORD dwThreadId;
hThread = CreateRemoteThread(
hProc, // _In_ HANDLE hProcess,
nullptr, // _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
0, // _In_ SIZE_T dwStackSize,
(LPTHREAD_START_ROUTINE)start_addr, // _In_ LPTHREAD_START_ROUTINE lpStartAddress,
(LPVOID)param, // _In_ LPVOID lpParameter,
0, //STACK_SIZE_PARAM_IS_A_RESERVATION // _In_ DWORD dwCreationFlags,
nullptr // _Out_ LPDWORD lpThreadId
);
if (!hThread) // jichi: this function returns nullptr instead of -1
hThread = INVALID_HANDLE_VALUE;
//} ITH_EXCEPT {
// ITH_WARN(L"exception");
// hThread = INVALID_HANDLE_VALUE;
//}
}
/*
else {
// jichi 9/29/2013: Also work on Wine and Windows 7
// See: http://waleedassar.blogspot.com/2012/06/createremotethread-vs.html
CLIENT_ID id;
//DWORD size = DEFAULT_STACK_LIMIT,
// commit = DEFAULT_STACK_COMMIT;
DWORD reserve = 0,
commit = 0;
// http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Executable%20Images/RtlCreateUserThread.html
// NTSYSAPI
// NTSTATUS
// NTAPI
// RtlCreateUserThread(
// IN HANDLE ProcessHandle,
// IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
// IN BOOLEAN CreateSuspended,
// IN ULONG StackZeroBits,
// IN OUT PULONG StackReserved,
// IN OUT PULONG StackCommit,
// IN PVOID StartAddress,
// IN PVOID StartParameter OPTIONAL,
// OUT PHANDLE ThreadHandle,
// OUT PCLIENT_ID ClientID);
if (!NT_SUCCESS(RtlCreateUserThread(
hProc, // HANDLE hProcess,
nullptr, // IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
FALSE, // IN BOOLEAN CreateSuspended,
0, // IN ULONG StackZeroBits,
&reserve, // IN OUT PULONG StackReserved,
&commit, // IN OUT PULONG StackCommit,
(LPVOID)start_addr, // IN PVOID StartAddress,
(LPVOID)param,// IN PVOID StartParameter OPTIONAL,
&hThread, // OUT PHANDLE ThreadHandle,
&id // OUT PCLIENT_ID ClientID
)))
hThread = INVALID_HANDLE_VALUE;
}
*/
return hThread;
}
//Query module export table. Return function address if found.
//Similar to GetProcAddress
DWORD GetExportAddress(DWORD hModule,DWORD hash)
{
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
IMAGE_EXPORT_DIRECTORY *ExtDir;
UINT uj;
char* pcExportAddr,*pcFuncPtr,*pcBuffer;
DWORD dwReadAddr,dwFuncAddr,dwFuncName;
WORD wOrd;
DosHdr = (IMAGE_DOS_HEADER*)hModule;
if (IMAGE_DOS_SIGNATURE==DosHdr->e_magic) {
dwReadAddr=hModule+DosHdr->e_lfanew;
NtHdr=(IMAGE_NT_HEADERS*)dwReadAddr;
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
pcExportAddr = (char*)((DWORD)hModule+
(DWORD)NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
if (!pcExportAddr)
return 0;
ExtDir = (IMAGE_EXPORT_DIRECTORY*)pcExportAddr;
pcExportAddr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfNames);
for (uj = 0; uj < ExtDir->NumberOfNames; uj++) {
dwFuncName = *(DWORD *)pcExportAddr;
pcBuffer = (char*)((DWORD)hModule+dwFuncName);
if (GetHash(pcBuffer) == hash) {
pcFuncPtr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfNameOrdinals+(uj*sizeof(WORD)));
wOrd = *(WORD*)pcFuncPtr;
pcFuncPtr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfFunctions+(wOrd*sizeof(DWORD)));
dwFuncAddr = *(DWORD *)pcFuncPtr;
return hModule+dwFuncAddr;
}
pcExportAddr += sizeof(DWORD);
}
}
}
return 0;
}
} // extern "C"
// EOF
/*__declspec(naked) void normal_asm()
{
__asm
{
push ecx
push edx
mov fs:[0],esp
push ebp
call eax
_terminate:
push eax
push -2
call dword ptr [NtTerminateThread]
}
}*/
/*
__declspec(naked) void RegToStrAsm()
{
__asm
{
mov edx, 8
_cvt_loop:
mov eax, ecx
and eax, 0xF
cmp eax, 0xA
jb _below_ten
add al,7
_below_ten:
add al,0x30
stosw
ror ecx,4
dec edx
jne _cvt_loop
retn
}
}
__declspec(naked) void except_asm()
{
__asm
{
mov eax,[esp + 4]
xor esi,esi
mov ebp,[eax]
mov ecx,[esp + 0xC]
mov ebx,[ecx + 0xB8]
sub esp,0x240
lea edi,[esp + 0x40]
mov eax,esp
push esi
push 0x1C
push eax
push esi
push ebx
push -1
call dword ptr [NtQueryVirtualMemory]
test eax,eax
jne _terminate
mov eax,esp
push eax
push 0x200
push edi
push 2
push ebx
push -1
call dword ptr [NtQueryVirtualMemory]
test eax,eax
jne _terminate
pop esi
xadd edi,esi
std
mov al,0x5C
repen scasw
mov word ptr [edi + 2], 0x3A
mov ecx,ebx
sub ecx,[esp]
call RegToStrAsm
inc edi
inc edi
xchg esi,edi
mov ecx,ebp
call RegToStrAsm
inc edi
inc edi
xor eax,eax
mov [edi + 0x10], eax
push 0
push edi
push esi
push 0
call dword ptr [MessageBoxW]
or eax, -1
jmp _terminate
}
}
//Prompt for file name.
HANDLE IthPromptCreateFile(DWORD option, DWORD share, DWORD disposition)
{
OPENFILENAME ofn = {sizeof(ofn)}; // common dialog box structure
WCHAR szFile[MAX_PATH]; // buffer for file name
wcscpy(current_dir,L"ITH_export.txt");
wcscpy(szFile,file_path);
//szFile[0]=0;
ofn.lpstrFile = szFile + 4;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = L"Text\0*.txt";
BOOL result;
if (disposition==FILE_OPEN)
result=GetOpenFileName(&ofn);
else
result=GetSaveFileName(&ofn);
if (result)
{
LPWSTR s=szFile+wcslen(szFile) - 4;
if (_wcsicmp(s,L".txt")!=0) wcscpy(s + 4,L".txt");
return IthCreateFileFullPath(szFile,option,share,disposition);
}
else return INVALID_HANDLE_VALUE;
}
*/
#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
# 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
# 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
};
// Modified from ITH findCallOrJmpAbs
enum : BYTE {
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 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 findWordCall(WORD 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;
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)
if (t > start && t < stop) {
if (arg1 == *(argtype *)t) // absolute address
return start + i;
else
i += sizeof(optype) + sizeof(argtype) - 1; // == 5
//i += sizeof(optype) + sizeof(argtype) - 1; // == 5
}
}
return 0;
}
// Modified from ITH findCallOrJmpAbs
enum : BYTE {
byte_call = 0xe8 // near call
, byte_push_small = 0x6a // push byte operand
, byte_push_large = 0x68 // push operand > 0xff
};
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 search range
* @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 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 findShortJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return findByteCall(byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
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 findFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
{ return findWordCall(word_call, funcAddr, lowerBound, upperBound - lowerBound); }
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 findNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
{ return findByteCall(byte_call, funcAddr, lowerBound, upperBound - lowerBound); }
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 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 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, 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 far call
inline dword_t findCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound)
{ return findFarCallAddress(funcAddr, lowerBound, upperBound); }
/// 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.
......
# mono.pri
# 9/26/2012 jichi
DEFINES += WITH_LIB_MONO
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/monoobject.h \
$$PWD/monotype.h
# EOF
#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
#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
......
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
#)
/* 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;
}
/* 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;
};
#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;
}
#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();
}
/**
* 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.
*/
This diff could not be displayed because it is too large.
/**
* 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 PUGIXML_VERSION
// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons
# define PUGIXML_VERSION 160
#endif
// Include user configuration file (this can define various configuration macros)
#include "pugiconfig.hpp"
#ifndef HEADER_PUGIXML_HPP
#define HEADER_PUGIXML_HPP
// Include stddef.h for size_t and ptrdiff_t
#include <stddef.h>
// Include exception header for XPath
#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS)
# include <exception>
#endif
// Include STL headers
#ifndef PUGIXML_NO_STL
# include <iterator>
# include <iosfwd>
# include <string>
#endif
// Macro for deprecated features
#ifndef PUGIXML_DEPRECATED
# if defined(__GNUC__)
# define PUGIXML_DEPRECATED __attribute__((deprecated))
# elif defined(_MSC_VER) && _MSC_VER >= 1300
# define PUGIXML_DEPRECATED __declspec(deprecated)
# else
# define PUGIXML_DEPRECATED
# endif
#endif
// If no API is defined, assume default
#ifndef PUGIXML_API
# define PUGIXML_API
#endif
// If no API for classes is defined, assume default
#ifndef PUGIXML_CLASS
# define PUGIXML_CLASS PUGIXML_API
#endif
// If no API for functions is defined, assume default
#ifndef PUGIXML_FUNCTION
# define PUGIXML_FUNCTION PUGIXML_API
#endif
// If the platform is known to have long long support, enable long long functions
#ifndef PUGIXML_HAS_LONG_LONG
# if defined(__cplusplus) && __cplusplus >= 201103
# define PUGIXML_HAS_LONG_LONG
# elif defined(_MSC_VER) && _MSC_VER >= 1400
# define PUGIXML_HAS_LONG_LONG
# endif
#endif
// Character interface macros
#ifdef PUGIXML_WCHAR_MODE
# define PUGIXML_TEXT(t) L ## t
# define PUGIXML_CHAR wchar_t
#else
# define PUGIXML_TEXT(t) t
# define PUGIXML_CHAR char
#endif
namespace pugi
{
// Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE
typedef PUGIXML_CHAR char_t;
#ifndef PUGIXML_NO_STL
// String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE
typedef std::basic_string<PUGIXML_CHAR, std::char_traits<PUGIXML_CHAR>, std::allocator<PUGIXML_CHAR> > string_t;
#endif
}
// The PugiXML namespace
namespace pugi
{
// Tree node types
enum xml_node_type
{
node_null, // Empty (null) node handle
node_document, // A document tree's absolute root
node_element, // Element tag, i.e. '<node/>'
node_pcdata, // Plain character data, i.e. 'text'
node_cdata, // Character data, i.e. '<![CDATA[text]]>'
node_comment, // Comment tag, i.e. '<!-- text -->'
node_pi, // Processing instruction, i.e. '<?name?>'
node_declaration, // Document declaration, i.e. '<?xml version="1.0"?>'
node_doctype // Document type declaration, i.e. '<!DOCTYPE doc>'
};
// Parsing options
// Minimal parsing mode (equivalent to turning all other flags off).
// Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed.
const unsigned int parse_minimal = 0x0000;
// This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default.
const unsigned int parse_pi = 0x0001;
// This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default.
const unsigned int parse_comments = 0x0002;
// This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default.
const unsigned int parse_cdata = 0x0004;
// This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree.
// This flag is off by default; turning it on usually results in slower parsing and more memory consumption.
const unsigned int parse_ws_pcdata = 0x0008;
// This flag determines if character and entity references are expanded during parsing. This flag is on by default.
const unsigned int parse_escapes = 0x0010;
// This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default.
const unsigned int parse_eol = 0x0020;
// This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default.
const unsigned int parse_wconv_attribute = 0x0040;
// This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default.
const unsigned int parse_wnorm_attribute = 0x0080;
// This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default.
const unsigned int parse_declaration = 0x0100;
// This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default.
const unsigned int parse_doctype = 0x0200;
// This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only
// of whitespace is added to the DOM tree.
// This flag is off by default; turning it on may result in slower parsing and more memory consumption.
const unsigned int parse_ws_pcdata_single = 0x0400;
// This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default.
const unsigned int parse_trim_pcdata = 0x0800;
// This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document
// is a valid document. This flag is off by default.
const unsigned int parse_fragment = 0x1000;
// The default parsing mode.
// Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded,
// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol;
// The full parsing mode.
// Nodes of all types are added to the DOM tree, character/reference entities are expanded,
// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype;
// These flags determine the encoding of input data for XML document
enum xml_encoding
{
encoding_auto, // Auto-detect input encoding using BOM or < / <? detection; use UTF8 if BOM is not found
encoding_utf8, // UTF8 encoding
encoding_utf16_le, // Little-endian UTF16
encoding_utf16_be, // Big-endian UTF16
encoding_utf16, // UTF16 with native endianness
encoding_utf32_le, // Little-endian UTF32
encoding_utf32_be, // Big-endian UTF32
encoding_utf32, // UTF32 with native endianness
encoding_wchar, // The same encoding wchar_t has (either UTF16 or UTF32)
encoding_latin1
};
// Formatting flags
// Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default.
const unsigned int format_indent = 0x01;
// Write encoding-specific BOM to the output stream. This flag is off by default.
const unsigned int format_write_bom = 0x02;
// Use raw output mode (no indentation and no line breaks are written). This flag is off by default.
const unsigned int format_raw = 0x04;
// Omit default XML declaration even if there is no declaration in the document. This flag is off by default.
const unsigned int format_no_declaration = 0x08;
// Don't escape attribute values and PCDATA contents. This flag is off by default.
const unsigned int format_no_escapes = 0x10;
// Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.
const unsigned int format_save_file_text = 0x20;
// The default set of formatting flags.
// Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
const unsigned int format_default = format_indent;
// Forward declarations
struct xml_attribute_struct;
struct xml_node_struct;
class xml_node_iterator;
class xml_attribute_iterator;
class xml_named_node_iterator;
class xml_tree_walker;
struct xml_parse_result;
class xml_node;
class xml_text;
#ifndef PUGIXML_NO_XPATH
class xpath_node;
class xpath_node_set;
class xpath_query;
class xpath_variable_set;
#endif
// Range-based for loop support
template <typename It> class xml_object_range
{
public:
typedef It const_iterator;
typedef It iterator;
xml_object_range(It b, It e): _begin(b), _end(e)
{
}
It begin() const { return _begin; }
It end() const { return _end; }
private:
It _begin, _end;
};
// Writer interface for node printing (see xml_node::print)
class PUGIXML_CLASS xml_writer
{
public:
virtual ~xml_writer() {}
// Write memory chunk into stream/file/whatever
virtual void write(const void* data, size_t size) = 0;
};
// xml_writer implementation for FILE*
class PUGIXML_CLASS xml_writer_file: public xml_writer
{
public:
// Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio
xml_writer_file(void* file);
virtual void write(const void* data, size_t size);
private:
void* file;
};
#ifndef PUGIXML_NO_STL
// xml_writer implementation for streams
class PUGIXML_CLASS xml_writer_stream: public xml_writer
{
public:
// Construct writer from an output stream object
xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream);
xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream);
virtual void write(const void* data, size_t size);
private:
std::basic_ostream<char, std::char_traits<char> >* narrow_stream;
std::basic_ostream<wchar_t, std::char_traits<wchar_t> >* wide_stream;
};
#endif
// A light-weight handle for manipulating attributes in DOM tree
class PUGIXML_CLASS xml_attribute
{
friend class xml_attribute_iterator;
friend class xml_node;
private:
xml_attribute_struct* _attr;
typedef void (*unspecified_bool_type)(xml_attribute***);
public:
// Default constructor. Constructs an empty attribute.
xml_attribute();
// Constructs attribute from internal pointer
explicit xml_attribute(xml_attribute_struct* attr);
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
// Comparison operators (compares wrapped attribute pointers)
bool operator==(const xml_attribute& r) const;
bool operator!=(const xml_attribute& r) const;
bool operator<(const xml_attribute& r) const;
bool operator>(const xml_attribute& r) const;
bool operator<=(const xml_attribute& r) const;
bool operator>=(const xml_attribute& r) const;
// Check if attribute is empty
bool empty() const;
// Get attribute name/value, or "" if attribute is empty
const char_t* name() const;
const char_t* value() const;
// Get attribute value, or the default value if attribute is empty
const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
// Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty
int as_int(int def = 0) const;
unsigned int as_uint(unsigned int def = 0) const;
double as_double(double def = 0) const;
float as_float(float def = 0) const;
#ifdef PUGIXML_HAS_LONG_LONG
long long as_llong(long long def = 0) const;
unsigned long long as_ullong(unsigned long long def = 0) const;
#endif
// Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty
bool as_bool(bool def = false) const;
// Set attribute name/value (returns false if attribute is empty or there is not enough memory)
bool set_name(const char_t* rhs);
bool set_value(const char_t* rhs);
// Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
bool set_value(int rhs);
bool set_value(unsigned int rhs);
bool set_value(double rhs);
bool set_value(float rhs);
bool set_value(bool rhs);
#ifdef PUGIXML_HAS_LONG_LONG
bool set_value(long long rhs);
bool set_value(unsigned long long rhs);
#endif
// Set attribute value (equivalent to set_value without error checking)
xml_attribute& operator=(const char_t* rhs);
xml_attribute& operator=(int rhs);
xml_attribute& operator=(unsigned int rhs);
xml_attribute& operator=(double rhs);
xml_attribute& operator=(float rhs);
xml_attribute& operator=(bool rhs);
#ifdef PUGIXML_HAS_LONG_LONG
xml_attribute& operator=(long long rhs);
xml_attribute& operator=(unsigned long long rhs);
#endif
// Get next/previous attribute in the attribute list of the parent node
xml_attribute next_attribute() const;
xml_attribute previous_attribute() const;
// Get hash value (unique for handles to the same object)
size_t hash_value() const;
// Get internal pointer
xml_attribute_struct* internal_object() const;
};
#ifdef __BORLANDC__
// Borland C++ workaround
bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs);
bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs);
#endif
// A light-weight handle for manipulating nodes in DOM tree
class PUGIXML_CLASS xml_node
{
friend class xml_attribute_iterator;
friend class xml_node_iterator;
friend class xml_named_node_iterator;
protected:
xml_node_struct* _root;
typedef void (*unspecified_bool_type)(xml_node***);
public:
// Default constructor. Constructs an empty node.
xml_node();
// Constructs node from internal pointer
explicit xml_node(xml_node_struct* p);
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
// Comparison operators (compares wrapped node pointers)
bool operator==(const xml_node& r) const;
bool operator!=(const xml_node& r) const;
bool operator<(const xml_node& r) const;
bool operator>(const xml_node& r) const;
bool operator<=(const xml_node& r) const;
bool operator>=(const xml_node& r) const;
// Check if node is empty.
bool empty() const;
// Get node type
xml_node_type type() const;
// Get node name, or "" if node is empty or it has no name
const char_t* name() const;
// Get node value, or "" if node is empty or it has no value
// Note: For <node>text</node> node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes.
const char_t* value() const;
// Get attribute list
xml_attribute first_attribute() const;
xml_attribute last_attribute() const;
// Get children list
xml_node first_child() const;
xml_node last_child() const;
// Get next/previous sibling in the children list of the parent node
xml_node next_sibling() const;
xml_node previous_sibling() const;
// Get parent node
xml_node parent() const;
// Get root of DOM tree this node belongs to
xml_node root() const;
// Get text object for the current node
xml_text text() const;
// Get child, attribute or next/previous sibling with the specified name
xml_node child(const char_t* name) const;
xml_attribute attribute(const char_t* name) const;
xml_node next_sibling(const char_t* name) const;
xml_node previous_sibling(const char_t* name) const;
// Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
const char_t* child_value() const;
// Get child value of child with specified name. Equivalent to child(name).child_value().
const char_t* child_value(const char_t* name) const;
// Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)
bool set_name(const char_t* rhs);
bool set_value(const char_t* rhs);
// Add attribute with specified name. Returns added attribute, or empty attribute on errors.
xml_attribute append_attribute(const char_t* name);
xml_attribute prepend_attribute(const char_t* name);
xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr);
xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr);
// Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors.
xml_attribute append_copy(const xml_attribute& proto);
xml_attribute prepend_copy(const xml_attribute& proto);
xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);
xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);
// Add child node with specified type. Returns added node, or empty node on errors.
xml_node append_child(xml_node_type type = node_element);
xml_node prepend_child(xml_node_type type = node_element);
xml_node insert_child_after(xml_node_type type, const xml_node& node);
xml_node insert_child_before(xml_node_type type, const xml_node& node);
// Add child element with specified name. Returns added node, or empty node on errors.
xml_node append_child(const char_t* name);
xml_node prepend_child(const char_t* name);
xml_node insert_child_after(const char_t* name, const xml_node& node);
xml_node insert_child_before(const char_t* name, const xml_node& node);
// Add a copy of the specified node as a child. Returns added node, or empty node on errors.
xml_node append_copy(const xml_node& proto);
xml_node prepend_copy(const xml_node& proto);
xml_node insert_copy_after(const xml_node& proto, const xml_node& node);
xml_node insert_copy_before(const xml_node& proto, const xml_node& node);
// Move the specified node to become a child of this node. Returns moved node, or empty node on errors.
xml_node append_move(const xml_node& moved);
xml_node prepend_move(const xml_node& moved);
xml_node insert_move_after(const xml_node& moved, const xml_node& node);
xml_node insert_move_before(const xml_node& moved, const xml_node& node);
// Remove specified attribute
bool remove_attribute(const xml_attribute& a);
bool remove_attribute(const char_t* name);
// Remove specified child
bool remove_child(const xml_node& n);
bool remove_child(const char_t* name);
// Parses buffer as an XML document fragment and appends all nodes as children of the current node.
// Copies/converts the buffer, so it may be deleted or changed after the function returns.
// Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory.
xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Find attribute using predicate. Returns first attribute for which predicate returned true.
template <typename Predicate> xml_attribute find_attribute(Predicate pred) const
{
if (!_root) return xml_attribute();
for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute())
if (pred(attrib))
return attrib;
return xml_attribute();
}
// Find child node using predicate. Returns first child for which predicate returned true.
template <typename Predicate> xml_node find_child(Predicate pred) const
{
if (!_root) return xml_node();
for (xml_node node = first_child(); node; node = node.next_sibling())
if (pred(node))
return node;
return xml_node();
}
// Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true.
template <typename Predicate> xml_node find_node(Predicate pred) const
{
if (!_root) return xml_node();
xml_node cur = first_child();
while (cur._root && cur._root != _root)
{
if (pred(cur)) return cur;
if (cur.first_child()) cur = cur.first_child();
else if (cur.next_sibling()) cur = cur.next_sibling();
else
{
while (!cur.next_sibling() && cur._root != _root) cur = cur.parent();
if (cur._root != _root) cur = cur.next_sibling();
}
}
return xml_node();
}
// Find child node by attribute name/value
xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const;
xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const;
#ifndef PUGIXML_NO_STL
// Get the absolute node path from root as a text string.
string_t path(char_t delimiter = '/') const;
#endif
// Search for a node by path consisting of node names and . or .. elements.
xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const;
// Recursively traverse subtree with xml_tree_walker
bool traverse(xml_tree_walker& walker);
#ifndef PUGIXML_NO_XPATH
// Select single node by evaluating XPath query. Returns first node from the resulting node set.
xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const;
xpath_node select_node(const xpath_query& query) const;
// Select node set by evaluating XPath query
xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
xpath_node_set select_nodes(const xpath_query& query) const;
// (deprecated: use select_node instead) Select single node by evaluating XPath query.
xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
xpath_node select_single_node(const xpath_query& query) const;
#endif
// Print subtree using a writer object
void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
#ifndef PUGIXML_NO_STL
// Print subtree to stream
void print(std::basic_ostream<char, std::char_traits<char> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
void print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const;
#endif
// Child nodes iterators
typedef xml_node_iterator iterator;
iterator begin() const;
iterator end() const;
// Attribute iterators
typedef xml_attribute_iterator attribute_iterator;
attribute_iterator attributes_begin() const;
attribute_iterator attributes_end() const;
// Range-based for support
xml_object_range<xml_node_iterator> children() const;
xml_object_range<xml_named_node_iterator> children(const char_t* name) const;
xml_object_range<xml_attribute_iterator> attributes() const;
// Get node offset in parsed file/string (in char_t units) for debugging purposes
ptrdiff_t offset_debug() const;
// Get hash value (unique for handles to the same object)
size_t hash_value() const;
// Get internal pointer
xml_node_struct* internal_object() const;
};
#ifdef __BORLANDC__
// Borland C++ workaround
bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs);
bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs);
#endif
// A helper for working with text inside PCDATA nodes
class PUGIXML_CLASS xml_text
{
friend class xml_node;
xml_node_struct* _root;
typedef void (*unspecified_bool_type)(xml_text***);
explicit xml_text(xml_node_struct* root);
xml_node_struct* _data_new();
xml_node_struct* _data() const;
public:
// Default constructor. Constructs an empty object.
xml_text();
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
// Check if text object is empty
bool empty() const;
// Get text, or "" if object is empty
const char_t* get() const;
// Get text, or the default value if object is empty
const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
// Get text as a number, or the default value if conversion did not succeed or object is empty
int as_int(int def = 0) const;
unsigned int as_uint(unsigned int def = 0) const;
double as_double(double def = 0) const;
float as_float(float def = 0) const;
#ifdef PUGIXML_HAS_LONG_LONG
long long as_llong(long long def = 0) const;
unsigned long long as_ullong(unsigned long long def = 0) const;
#endif
// Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty
bool as_bool(bool def = false) const;
// Set text (returns false if object is empty or there is not enough memory)
bool set(const char_t* rhs);
// Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
bool set(int rhs);
bool set(unsigned int rhs);
bool set(double rhs);
bool set(float rhs);
bool set(bool rhs);
#ifdef PUGIXML_HAS_LONG_LONG
bool set(long long rhs);
bool set(unsigned long long rhs);
#endif
// Set text (equivalent to set without error checking)
xml_text& operator=(const char_t* rhs);
xml_text& operator=(int rhs);
xml_text& operator=(unsigned int rhs);
xml_text& operator=(double rhs);
xml_text& operator=(float rhs);
xml_text& operator=(bool rhs);
#ifdef PUGIXML_HAS_LONG_LONG
xml_text& operator=(long long rhs);
xml_text& operator=(unsigned long long rhs);
#endif
// Get the data node (node_pcdata or node_cdata) for this object
xml_node data() const;
};
#ifdef __BORLANDC__
// Borland C++ workaround
bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs);
bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs);
#endif
// Child node iterator (a bidirectional iterator over a collection of xml_node)
class PUGIXML_CLASS xml_node_iterator
{
friend class xml_node;
private:
mutable xml_node _wrap;
xml_node _parent;
xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent);
public:
// Iterator traits
typedef ptrdiff_t difference_type;
typedef xml_node value_type;
typedef xml_node* pointer;
typedef xml_node& reference;
#ifndef PUGIXML_NO_STL
typedef std::bidirectional_iterator_tag iterator_category;
#endif
// Default constructor
xml_node_iterator();
// Construct an iterator which points to the specified node
xml_node_iterator(const xml_node& node);
// Iterator operators
bool operator==(const xml_node_iterator& rhs) const;
bool operator!=(const xml_node_iterator& rhs) const;
xml_node& operator*() const;
xml_node* operator->() const;
const xml_node_iterator& operator++();
xml_node_iterator operator++(int);
const xml_node_iterator& operator--();
xml_node_iterator operator--(int);
};
// Attribute iterator (a bidirectional iterator over a collection of xml_attribute)
class PUGIXML_CLASS xml_attribute_iterator
{
friend class xml_node;
private:
mutable xml_attribute _wrap;
xml_node _parent;
xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent);
public:
// Iterator traits
typedef ptrdiff_t difference_type;
typedef xml_attribute value_type;
typedef xml_attribute* pointer;
typedef xml_attribute& reference;
#ifndef PUGIXML_NO_STL
typedef std::bidirectional_iterator_tag iterator_category;
#endif
// Default constructor
xml_attribute_iterator();
// Construct an iterator which points to the specified attribute
xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent);
// Iterator operators
bool operator==(const xml_attribute_iterator& rhs) const;
bool operator!=(const xml_attribute_iterator& rhs) const;
xml_attribute& operator*() const;
xml_attribute* operator->() const;
const xml_attribute_iterator& operator++();
xml_attribute_iterator operator++(int);
const xml_attribute_iterator& operator--();
xml_attribute_iterator operator--(int);
};
// Named node range helper
class PUGIXML_CLASS xml_named_node_iterator
{
friend class xml_node;
public:
// Iterator traits
typedef ptrdiff_t difference_type;
typedef xml_node value_type;
typedef xml_node* pointer;
typedef xml_node& reference;
#ifndef PUGIXML_NO_STL
typedef std::bidirectional_iterator_tag iterator_category;
#endif
// Default constructor
xml_named_node_iterator();
// Construct an iterator which points to the specified node
xml_named_node_iterator(const xml_node& node, const char_t* name);
// Iterator operators
bool operator==(const xml_named_node_iterator& rhs) const;
bool operator!=(const xml_named_node_iterator& rhs) const;
xml_node& operator*() const;
xml_node* operator->() const;
const xml_named_node_iterator& operator++();
xml_named_node_iterator operator++(int);
const xml_named_node_iterator& operator--();
xml_named_node_iterator operator--(int);
private:
mutable xml_node _wrap;
xml_node _parent;
const char_t* _name;
xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name);
};
// Abstract tree walker class (see xml_node::traverse)
class PUGIXML_CLASS xml_tree_walker
{
friend class xml_node;
private:
int _depth;
protected:
// Get current traversal depth
int depth() const;
public:
xml_tree_walker();
virtual ~xml_tree_walker();
// Callback that is called when traversal begins
virtual bool begin(xml_node& node);
// Callback that is called for each node traversed
virtual bool for_each(xml_node& node) = 0;
// Callback that is called when traversal ends
virtual bool end(xml_node& node);
};
// Parsing status, returned as part of xml_parse_result object
enum xml_parse_status
{
status_ok = 0, // No error
status_file_not_found, // File was not found during load_file()
status_io_error, // Error reading from file/stream
status_out_of_memory, // Could not allocate memory
status_internal_error, // Internal error occurred
status_unrecognized_tag, // Parser could not determine tag type
status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction
status_bad_comment, // Parsing error occurred while parsing comment
status_bad_cdata, // Parsing error occurred while parsing CDATA section
status_bad_doctype, // Parsing error occurred while parsing document type declaration
status_bad_pcdata, // Parsing error occurred while parsing PCDATA section
status_bad_start_element, // Parsing error occurred while parsing start element tag
status_bad_attribute, // Parsing error occurred while parsing element attribute
status_bad_end_element, // Parsing error occurred while parsing end element tag
status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)
status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer)
status_no_document_element // Parsing resulted in a document without element nodes
};
// Parsing result
struct PUGIXML_CLASS xml_parse_result
{
// Parsing status (see xml_parse_status)
xml_parse_status status;
// Last parsed offset (in char_t units from start of input data)
ptrdiff_t offset;
// Source document encoding
xml_encoding encoding;
// Default constructor, initializes object to failed state
xml_parse_result();
// Cast to bool operator
operator bool() const;
// Get error description
const char* description() const;
};
// Document class (DOM tree root)
class PUGIXML_CLASS xml_document: public xml_node
{
private:
char_t* _buffer;
char _memory[192];
// Non-copyable semantics
xml_document(const xml_document&);
const xml_document& operator=(const xml_document&);
void create();
void destroy();
public:
// Default constructor, makes empty document
xml_document();
// Destructor, invalidates all node/attribute handles to this document
~xml_document();
// Removes all nodes, leaving the empty document
void reset();
// Removes all nodes, then copies the entire contents of the specified document
void reset(const xml_document& proto);
#ifndef PUGIXML_NO_STL
// Load document from stream.
xml_parse_result load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
xml_parse_result load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options = parse_default);
#endif
// (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied.
xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
// Load document from zero-terminated string. No encoding conversions are applied.
xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default);
// Load document from file
xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns.
xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
// You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed.
xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
// You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore).
xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details).
void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
#ifndef PUGIXML_NO_STL
// Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details).
void save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
void save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const;
#endif
// Save XML to file
bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
// Get document element
xml_node document_element() const;
};
#ifndef PUGIXML_NO_XPATH
// XPath query return type
enum xpath_value_type
{
xpath_type_none, // Unknown type (query failed to compile)
xpath_type_node_set, // Node set (xpath_node_set)
xpath_type_number, // Number
xpath_type_string, // String
xpath_type_boolean // Boolean
};
// XPath parsing result
struct PUGIXML_CLASS xpath_parse_result
{
// Error message (0 if no error)
const char* error;
// Last parsed offset (in char_t units from string start)
ptrdiff_t offset;
// Default constructor, initializes object to failed state
xpath_parse_result();
// Cast to bool operator
operator bool() const;
// Get error description
const char* description() const;
};
// A single XPath variable
class PUGIXML_CLASS xpath_variable
{
friend class xpath_variable_set;
protected:
xpath_value_type _type;
xpath_variable* _next;
xpath_variable();
// Non-copyable semantics
xpath_variable(const xpath_variable&);
xpath_variable& operator=(const xpath_variable&);
public:
// Get variable name
const char_t* name() const;
// Get variable type
xpath_value_type type() const;
// Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error
bool get_boolean() const;
double get_number() const;
const char_t* get_string() const;
const xpath_node_set& get_node_set() const;
// Set variable value; no type conversion is performed, false is returned on type mismatch error
bool set(bool value);
bool set(double value);
bool set(const char_t* value);
bool set(const xpath_node_set& value);
};
// A set of XPath variables
class PUGIXML_CLASS xpath_variable_set
{
private:
xpath_variable* _data[64];
// Non-copyable semantics
xpath_variable_set(const xpath_variable_set&);
xpath_variable_set& operator=(const xpath_variable_set&);
xpath_variable* find(const char_t* name) const;
public:
// Default constructor/destructor
xpath_variable_set();
~xpath_variable_set();
// Add a new variable or get the existing one, if the types match
xpath_variable* add(const char_t* name, xpath_value_type type);
// Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch
bool set(const char_t* name, bool value);
bool set(const char_t* name, double value);
bool set(const char_t* name, const char_t* value);
bool set(const char_t* name, const xpath_node_set& value);
// Get existing variable by name
xpath_variable* get(const char_t* name);
const xpath_variable* get(const char_t* name) const;
};
// A compiled XPath query object
class PUGIXML_CLASS xpath_query
{
private:
void* _impl;
xpath_parse_result _result;
typedef void (*unspecified_bool_type)(xpath_query***);
// Non-copyable semantics
xpath_query(const xpath_query&);
xpath_query& operator=(const xpath_query&);
public:
// Construct a compiled object from XPath expression.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors.
explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);
// Destructor
~xpath_query();
// Get query expression return type
xpath_value_type return_type() const;
// Evaluate expression as boolean value in the specified context; performs type conversion if necessary.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
bool evaluate_boolean(const xpath_node& n) const;
// Evaluate expression as double value in the specified context; performs type conversion if necessary.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
double evaluate_number(const xpath_node& n) const;
#ifndef PUGIXML_NO_STL
// Evaluate expression as string value in the specified context; performs type conversion if necessary.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
string_t evaluate_string(const xpath_node& n) const;
#endif
// Evaluate expression as string value in the specified context; performs type conversion if necessary.
// At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero).
// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
// If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead.
size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const;
// Evaluate expression as node set in the specified context.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.
xpath_node_set evaluate_node_set(const xpath_node& n) const;
// Evaluate expression as node set in the specified context.
// Return first node in document order, or empty node if node set is empty.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead.
xpath_node evaluate_node(const xpath_node& n) const;
// Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)
const xpath_parse_result& result() const;
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
};
#ifndef PUGIXML_NO_EXCEPTIONS
// XPath exception class
class PUGIXML_CLASS xpath_exception: public std::exception
{
private:
xpath_parse_result _result;
public:
// Construct exception from parse result
explicit xpath_exception(const xpath_parse_result& result);
// Get error message
virtual const char* what() const throw();
// Get parse result
const xpath_parse_result& result() const;
};
#endif
// XPath node class (either xml_node or xml_attribute)
class PUGIXML_CLASS xpath_node
{
private:
xml_node _node;
xml_attribute _attribute;
typedef void (*unspecified_bool_type)(xpath_node***);
public:
// Default constructor; constructs empty XPath node
xpath_node();
// Construct XPath node from XML node/attribute
xpath_node(const xml_node& node);
xpath_node(const xml_attribute& attribute, const xml_node& parent);
// Get node/attribute, if any
xml_node node() const;
xml_attribute attribute() const;
// Get parent of contained node/attribute
xml_node parent() const;
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
// Comparison operators
bool operator==(const xpath_node& n) const;
bool operator!=(const xpath_node& n) const;
};
#ifdef __BORLANDC__
// Borland C++ workaround
bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs);
bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs);
#endif
// A fixed-size collection of XPath nodes
class PUGIXML_CLASS xpath_node_set
{
public:
// Collection type
enum type_t
{
type_unsorted, // Not ordered
type_sorted, // Sorted by document order (ascending)
type_sorted_reverse // Sorted by document order (descending)
};
// Constant iterator type
typedef const xpath_node* const_iterator;
// We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work
typedef const xpath_node* iterator;
// Default constructor. Constructs empty set.
xpath_node_set();
// Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful
xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted);
// Destructor
~xpath_node_set();
// Copy constructor/assignment operator
xpath_node_set(const xpath_node_set& ns);
xpath_node_set& operator=(const xpath_node_set& ns);
// Get collection type
type_t type() const;
// Get collection size
size_t size() const;
// Indexing operator
const xpath_node& operator[](size_t index) const;
// Collection iterators
const_iterator begin() const;
const_iterator end() const;
// Sort the collection in ascending/descending order by document order
void sort(bool reverse = false);
// Get first node in the collection by document order
xpath_node first() const;
// Check if collection is empty
bool empty() const;
private:
type_t _type;
xpath_node _storage;
xpath_node* _begin;
xpath_node* _end;
void _assign(const_iterator begin, const_iterator end);
};
#endif
#ifndef PUGIXML_NO_STL
// Convert wide string to UTF8
std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const wchar_t* str);
std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >& str);
// Convert UTF8 to wide string
std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const char* str);
std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >& str);
#endif
// Memory allocation function interface; returns pointer to allocated memory or NULL on failure
typedef void* (*allocation_function)(size_t size);
// Memory deallocation function interface
typedef void (*deallocation_function)(void* ptr);
// Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions.
void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate);
// Get current memory management functions
allocation_function PUGIXML_FUNCTION get_memory_allocation_function();
deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function();
}
#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))
namespace std
{
// Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)
std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&);
std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&);
std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);
}
#endif
#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)
namespace std
{
// Workarounds for (non-standard) iterator category detection
std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&);
std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&);
std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);
}
#endif
#endif
// Make sure implementation is included in header-only mode
// Use macro expansion in #include to work around QMake (QTBUG-11923)
#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE)
# define PUGIXML_SOURCE "pugixml.cpp"
# include PUGIXML_SOURCE
#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.
*/
# 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
#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
#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
#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
#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
#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
# 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
)
#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
#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
// hookman.cc
// 8/24/2013 jichi
// Branch IHF/HookManager.cpp, rev 133
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
# pragma warning (disable:4146) // C4146: unary minus operator applied to unsigned type
#endif // _MSC_VER
#include "hookman.h"
#include "vnrhook/include/const.h"
#include "vnrhook/include/defs.h"
#include "vnrhook/include/types.h"
#include "ithsys/ithsys.h"
#include <stdio.h>
//#include <emmintrin.h>
#include "profile/Profile.h"
#include "profile/pugixml.hpp"
#include "profile/misc.h"
#define DEBUG "vnrhost/hookman.cc"
#include "sakurakit/skdebug.h"
namespace { // unnamed
//enum { MAX_ENTRY = 0x40 };
#define HM_LOCK win_mutex_lock<HookManager::mutex_type> d_locker(hmcs) // Synchronized scope for accessing private data
// jichi 9/23/2013: wine deficenciy on mapping sections
// Whe set to false, do not map sections.
//bool ith_has_section = true;
// jichi 9/28/2013: Remove ConsoleOutput from available hooks
//LPWSTR HookNameInitTable[]={ L"ConsoleOutput" , HOOK_FUN_NAME_LIST };
//LPCWSTR HookNameInitTable[] = {HOOK_FUN_NAME_LIST};
//LPVOID DefaultHookAddr[HOOK_FUN_COUNT];
//BYTE null_buffer[4]={0,0,0,0};
//BYTE static_small_buffer[0x100];
//DWORD zeros[4]={0,0,0,0};
//WCHAR user_entry[0x40];
bool GetProcessPath(HANDLE hProc, __out LPWSTR path)
{
PROCESS_BASIC_INFORMATION info;
LDR_DATA_TABLE_ENTRY entry;
PEB_LDR_DATA ldr;
PEB peb;
if (NT_SUCCESS(NtQueryInformationProcess(hProc, ProcessBasicInformation, &info, sizeof(info), 0)))
if (info.PebBaseAddress)
if (NT_SUCCESS(NtReadVirtualMemory(hProc, info.PebBaseAddress, &peb,sizeof(peb), 0)))
if (NT_SUCCESS(NtReadVirtualMemory(hProc, peb.Ldr, &ldr, sizeof(ldr), 0)))
if (NT_SUCCESS(NtReadVirtualMemory(hProc, (LPVOID)ldr.InLoadOrderModuleList.Flink,
&entry, sizeof(LDR_DATA_TABLE_ENTRY), 0)))
if (NT_SUCCESS(NtReadVirtualMemory(hProc, entry.FullDllName.Buffer,
path, MAX_PATH * 2, 0)))
return true;
path = L"";
return false;
}
bool GetProcessPath(DWORD pid, __out LPWSTR path)
{
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_VM_READ, &oa, &id))) {
bool flag = GetProcessPath(hProc, path);
NtClose(hProc);
return flag;
}
path = L"";
return false;
}
} // unnamed namespace
HookManager *man; // jichi 9/22/2013: initialized in main
//BitMap* pid_map;
DWORD clipboard_flag,
split_time,
repeat_count,
global_filter,
cyclic_remove;
DWORD GetHookName(LPSTR str, DWORD pid, DWORD hook_addr, DWORD max)
{
if (!pid)
return 0;
DWORD len = 0;
max--; //for '\0' magic marker.
//if (pid == 0) {
// len = wcslen(HookNameInitTable[0]);
// if (len >= max)
// len = max;
// memcpy(str, HookNameInitTable[0], len << 1);
// str[len] = 0;
// return len;
//}
//::man->LockProcessHookman(pid);
ProcessRecord *pr = ::man->GetProcessRecord(pid);
if (!pr)
return 0;
NtWaitForSingleObject(pr->hookman_mutex, 0, 0);
Hook *hks = (Hook *)pr->hookman_map;
for (int i = 0; i < MAX_HOOK; i++)
if (hks[i].Address() == hook_addr) {
len = hks[i].NameLength();
if (len >= max)
len = max;
NtReadVirtualMemory(pr->process_handle, hks[i].Name(), str, len, &len);
if (str[len - 1] == 0)
len--;
else
str[len] = 0;
break;
}
// jichi 9/27/2013: The hook man should be consistent with the one defined in vnrcli
//Hook *h = (Hook *)hks;
//for (int i = 0; i < MAX_HOOK; i++)
// if (!h[i].hook_name)
// break;
// else {
// const Hook &hi = h[i];
// wchar_t buffer[1000];
// DWORD len = hi.NameLength();
// NtReadVirtualMemory(pr->process_handle, hi.hook_name, buffer, len << 1, &len);
// buffer[len] = 0;
// ITH_MSG(buffer);
// }
NtReleaseMutant(pr->hookman_mutex, 0);
//::man->UnlockProcessHookman(pid);
return len;
}
// 7/2/2015 jichi: This function is not used and removed
//int GetHookNameByIndex(LPSTR str, DWORD pid, DWORD index)
//{
// if (!pid)
// return 0;
//
// //if (pid == 0) {
// // wcscpy(str, HookNameInitTable[0]);
// // return wcslen(HookNameInitTable[0]);
// //}
// DWORD len = 0;
// //::man->LockProcessHookman(pid);
// ProcessRecord *pr = ::man->GetProcessRecord(pid);
// if (!pr)
// return 0;
// //NtWaitForSingleObject(pr->hookman_mutex,0,0); //already locked
// Hook *hks = (Hook *)pr->hookman_map;
// if (hks[index].Address()) {
// NtReadVirtualMemory(pr->process_handle, hks[index].Name(), str, hks[index].NameLength() << 1, &len);
// len = hks[index].NameLength();
// }
// //NtReleaseMutant(pr->hookman_mutex,0);
// return len;
//}
//int GetHookString(LPWSTR str, DWORD pid, DWORD hook_addr, DWORD status)
//{
// LPWSTR begin=str;
// str+=swprintf(str,L"%4d:0x%08X:",pid,hook_addr);
// str+=GetHookName(str,pid,hook_addr);
// return str-begin;
//}
void ThreadTable::SetThread(DWORD num, TextThread *ptr)
{
int number = num;
if (number >= size) {
while (number >= size)
size <<= 1;
TextThread **temp;
//if (size < 0x10000) {
temp = new TextThread*[size];
if (size > used)
::memset(temp, 0, (size - used) * sizeof(TextThread *)); // jichi 9/21/2013: zero memory
memcpy(temp, storage, used * sizeof(TextThread *));
//}
delete[] storage;
storage = temp;
}
storage[number] = ptr;
if (ptr == nullptr) {
if (number == used - 1)
while (storage[used - 1] == 0)
used--;
} else if (number >= used)
used = number + 1;
}
TextThread *ThreadTable::FindThread(DWORD number)
{ return number <= (DWORD)used ? storage[number] : nullptr; }
static const char sse_table_eq[0x100]={
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,-1,-1, 1,1,1,1, -1,-1,-1,-1, 1,1,1,1, //3, compare 3
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,-1,-1, -1,-1,-1,-1, 1,1,1,1, 1,1,1,1, //7, compare 4
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,-1,-1, 1,1,1,1, -1,-1,-1,-1, 1,1,1,1, //3, compare 3
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 //f, equal
};
char original_cmp(const ThreadParameter *t1, const ThreadParameter *t2)
{
//Q_ASSERT(t1 && t2);
int t = t1->pid - t2->pid;
if (t == 0) {
t = t1->hook - t2->hook;
if (t == 0) {
t = t1->retn - t2->retn;
if (t == 0) {
t = t1->spl-t2->spl;
if (t == 0) return 0;
return t1->spl > t2->spl ? 1 : -1;
}
else return t1->retn > t2->retn ? 1 : -1;
}
else return t1->hook > t2->hook ? 1: -1;
}
else return t1->pid > t2->pid ? 1 : -1;
//return t>0?1:-1;
}
char TCmp::operator()(const ThreadParameter* t1, const ThreadParameter* t2)
//SSE speed up. Compare four integers in const time without branching.
//The AVL tree branching operation needs 2 bit of information.
//One bit for equality and one bit for "less than" or "greater than".
{
union{__m128 m0;__m128i i0;};
union{__m128 m1;__m128i i1;};
union{__m128 m2;__m128i i2;};
int k0,k1;
i1 = _mm_loadu_si128((const __m128i*)t1);
i2 = _mm_loadu_si128((const __m128i*)t2);
i0 = _mm_cmpgt_epi32(i1,i2);
k0 = _mm_movemask_ps(m0);
i1 = _mm_cmpeq_epi32(i1,i2);
k1 = _mm_movemask_ps(m1);
return sse_table_eq[k1*16+k0];
}
void TCpy::operator()(ThreadParameter* t1, const ThreadParameter* t2)
{ memcpy(t1,t2,sizeof(ThreadParameter)); }
int TLen::operator()(const ThreadParameter* t) { return 0; }
#define NAMED_PIPE_DISCONNECT 1
//Class member of HookManger
HookManager::HookManager() :
// jichi 9/21/2013: Zero memory
//CRITICAL_SECTION hmcs;
current(nullptr)
, create(nullptr)
, remove(nullptr)
, reset(nullptr)
, attach(nullptr)
, detach(nullptr)
, hook(nullptr)
, current_pid(0)
, thread_table(nullptr)
, destroy_event(nullptr)
, register_count(0)
, new_thread_number(0)
{
// jichi 9/21/2013: zero memory
::memset(record, 0, sizeof(record));
::memset(text_pipes, 0, sizeof(text_pipes));
::memset(cmd_pipes, 0, sizeof(cmd_pipes));
::memset(recv_threads, 0, sizeof(recv_threads));
head.key = new ThreadParameter;
head.key->pid = 0;
head.key->hook = -1;
head.key->retn = -1;
head.key->spl = -1;
head.data = 0;
thread_table = new ThreadTable; // jichi 9/26/2013: zero memory in ThreadTable
TextThread *entry = new TextThread(0, -1,-1,-1, new_thread_number++); // jichi 9/26/2013: zero memory in TextThread
thread_table->SetThread(0, entry);
SetCurrent(entry);
entry->Status() |= USING_UNICODE;
//texts->SetUnicode(true);
//entry->AddToCombo();
//entry->ComboSelectCurrent();
//if (background==0) entry->AddToStore((BYTE*)BackgroundMsg,wcslen(BackgroundMsg)<<1,0,1);
//InitializeCriticalSection(&hmcs);
destroy_event = IthCreateEvent(0, 0, 0);
}
HookManager::~HookManager()
{
//LARGE_INTEGER timeout={-1000*1000,-1};
//IthBreak();
NtWaitForSingleObject(destroy_event, 0, 0);
NtClose(destroy_event);
NtClose(cmd_pipes[0]);
NtClose(recv_threads[0]);
delete thread_table;
delete head.key;
//DeleteCriticalSection(&hmcs);
}
TextThread *HookManager::FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split)
{
if (pid == 0)
return thread_table->FindThread(0);
ThreadParameter tp = {pid, hook, retn, split};
TreeNode<ThreadParameter *,DWORD> *node = Search(&tp);
return node ? thread_table->FindThread(node->data) : nullptr;
}
TextThread *HookManager::FindSingle(DWORD number)
{ return (number & 0x80008000) ? nullptr : thread_table->FindThread(number); }
void HookManager::DetachProcess(DWORD pid) {}
void HookManager::SetCurrent(TextThread *it)
{
if (current)
current->Status() &= ~CURRENT_SELECT;
current = it;
if (it)
it->Status() |= CURRENT_SELECT;
}
void HookManager::SelectCurrent(DWORD num)
{
if (TextThread *st = FindSingle(num)) {
SetCurrent(st);
if (reset)
reset(st);
//st->ResetEditText();
}
}
void HookManager::RemoveSingleHook(DWORD pid, DWORD addr)
{
HM_LOCK;
//ConsoleOutput("vnrhost:RemoveSingleHook: lock");
//EnterCriticalSection(&hmcs);
DWORD max = thread_table->Used();
bool flag = false;
for (DWORD i = 1; i <= max; i++)
if (TextThread *it = thread_table->FindThread(i))
if (it->PID() == pid && it->Addr() == addr) {
flag |= (it == current);
//flag|=it->RemoveFromCombo();
thread_table->SetThread(i, 0);
if (it->Number() < new_thread_number)
new_thread_number = it->Number();
Delete(it->GetThreadParameter());
if (remove)
remove(it);
delete it;
}
for (DWORD i = 0; i <= max; i++)
if (TextThread *it = thread_table->FindThread(i))
if (it->Link() && thread_table->FindThread(it->LinkNumber()) == nullptr) {
it->LinkNumber() = -1;
it->Link() = nullptr;
}
if (flag) {
current = nullptr;
DWORD number = head.Left ? head.Left->data : 0;
SetCurrent(thread_table->FindThread(number));
if (reset && current)
reset(current);
//it->ResetEditText();
}
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RemoveSingleHook: unlock");
}
void HookManager::RemoveSingleThread(DWORD number)
{
if (number == 0)
return;
HM_LOCK;
//ConsoleOutput("vnrhost:RemoveSingleThread: lock");
//EnterCriticalSection(&hmcs);
if (TextThread *it = thread_table->FindThread(number)) {
thread_table->SetThread(number, 0);
Delete(it->GetThreadParameter());
if (remove)
remove(it);
bool flag = (it == current);
if (it->Number() < new_thread_number)
new_thread_number = it->Number();
delete it;
for (int i = 0; i <= thread_table->Used(); i++)
if (TextThread *t = thread_table->FindThread(i))
if (t->LinkNumber() == number) {
t->Link() = 0;
t->LinkNumber() = -1;
}
if (flag) {
current = nullptr;
number = head.Left ? head.Left->data : 0;
SetCurrent(thread_table->FindThread(number));
if (reset && current)
reset(current);
//it->ResetEditText();
}
}
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RemoveSingleThread: unlock");
}
void HookManager::RemoveProcessContext(DWORD pid)
{
HM_LOCK;
bool flag = false;
//ConsoleOutput("vnrhost:RemoveProcessContext: lock");
//EnterCriticalSection(&hmcs);
for (int i = 1; i < thread_table->Used(); i++)
if (TextThread *it = thread_table->FindThread(i))
if (it->PID() == pid) {
Delete(it->GetThreadParameter());
//if (false == Delete(it->GetThreadParameter())) {
// // jichi 11/26/2013: Remove debugging instructions
// //if (debug)
// // __asm int 3
//}
flag |= (it == current);
//flag|=it->RemoveFromCombo();
if (it->Number() <new_thread_number)
new_thread_number = it->Number();
thread_table->SetThread(i,0);
if (remove)
remove(it);
delete it;
}
for (int i = 0; i < thread_table->Used(); i++)
if (TextThread *it=thread_table->FindThread(i))
if (it->Link() && thread_table->FindThread(it->LinkNumber()) == nullptr) {
it->LinkNumber()=-1;
it->Link() = nullptr;
}
if (flag) {
current = nullptr;
DWORD number = head.Left ? head.Left->data : 0;
SetCurrent(thread_table->FindThread(number));
if (reset && current)
reset(current);
//if (it) it->ResetEditText();
}
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RemoveProcessContext: unlock");
}
void HookManager::RegisterThread(TextThread* it, DWORD num)
{ thread_table->SetThread(num, it); }
void HookManager::RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread)
{
text_pipes[register_count] = text;
cmd_pipes[register_count] = cmd;
recv_threads[register_count] = thread;
register_count++;
if (register_count == 1)
NtSetEvent(destroy_event, 0);
else
NtClearEvent(destroy_event);
}
void HookManager::RegisterProcess(DWORD pid, DWORD hookman, DWORD module)
{
HM_LOCK;
wchar_t str[0x40],
path[MAX_PATH];
//pid_map->Set(pid>>2);
//ConsoleOutput("vnrhost:RegisterProcess: lock");
//EnterCriticalSection(&hmcs);
record[register_count - 1].pid_register = pid;
record[register_count - 1].hookman_register = hookman;
record[register_count - 1].module_register = module;
//record[register_count - 1].engine_register = engine;
swprintf(str, ITH_SECTION_ L"%d", pid);
HANDLE hSection = IthCreateSection(str, HOOK_SECTION_SIZE, PAGE_READONLY);
LPVOID map = nullptr;
//DWORD map_size = 0x1000;
DWORD map_size = HOOK_SECTION_SIZE / 2; // jichi 1/16/2015: Changed to half to hook section size
//if (::ith_has_section)
NtMapViewOfSection(hSection, NtCurrentProcess(),
&map, 0, map_size, 0, &map_size, ViewUnmap, 0,
PAGE_READONLY);
record[register_count - 1].hookman_section = hSection;
record[register_count - 1].hookman_map = map;
HANDLE hProc;
CLIENT_ID id;
id.UniqueProcess = pid;
id.UniqueThread = 0;
OBJECT_ATTRIBUTES oa = {};
oa.uLength = sizeof(oa);
if (NT_SUCCESS(NtOpenProcess(&hProc,
PROCESS_QUERY_INFORMATION|
PROCESS_CREATE_THREAD|
PROCESS_VM_READ|
PROCESS_VM_WRITE|
PROCESS_VM_OPERATION,
&oa,&id)))
record[register_count - 1].process_handle = hProc;
else {
DOUT("failed to open process");
//::man->AddConsoleOutput(ErrorOpenProcess);
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RegisterProcess: unlock");
return;
}
// jichi 9/27/2013: The hook man should be consistent with the one defined in vnrcli
//Hook *h = (Hook *)map;
//for (int i = 0; i < MAX_HOOK; i++)
// if (!h[i].hook_name)
// break;
// else {
// const Hook &hi = h[i];
// wchar_t buffer[1000];
// DWORD len = hi.NameLength();
// NtReadVirtualMemory(hProc, hi.hook_name, buffer, len << 1, &len);
// buffer[len] = 0;
// ITH_MSG(buffer);
// }
swprintf(str, ITH_HOOKMAN_MUTEX_ L"%d", pid);
record[register_count - 1].hookman_mutex = IthOpenMutex(str);
if (!GetProcessPath(pid, path))
path[0] = 0;
//swprintf(str,L"%.4d:%s", pid, wcsrchr(path, L'\\') + 1); // jichi 9/25/2013: this is useless?
current_pid = pid;
if (attach)
attach(pid);
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RegisterProcess: unlock");
}
void HookManager::UnRegisterProcess(DWORD pid)
{
HM_LOCK;
//ConsoleOutput("vnrhost:UnRegisterProcess: lock");
//EnterCriticalSection(&hmcs);
int i;
for (i = 0; i < MAX_REGISTER; i++)
if(record[i].pid_register == pid)
break;
if (i < MAX_REGISTER) {
NtClose(text_pipes[i]);
NtClose(cmd_pipes[i]);
NtClose(recv_threads[i]);
NtClose(record[i].hookman_mutex);
//if (::ith_has_section)
NtUnmapViewOfSection(NtCurrentProcess(), record[i].hookman_map);
//else
// delete[] record[i].hookman_map;
NtClose(record[i].process_handle);
NtClose(record[i].hookman_section);
for (; i < MAX_REGISTER; i++) {
record[i] = record[i+1];
text_pipes[i] = text_pipes[i+1];
cmd_pipes[i] = cmd_pipes[i+1];
recv_threads[i] = recv_threads[i+1];
if (text_pipes[i] == 0)
break;
}
register_count--;
if (current_pid == pid)
current_pid = register_count ? record[0].pid_register : 0;
RemoveProcessContext(pid);
}
//pid_map->Clear(pid>>2);
if (register_count == 1)
NtSetEvent(destroy_event, 0);
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:UnRegisterProcess: unlock");
if (detach)
detach(pid);
}
// jichi 9/28/2013: I do not need this
//void HookManager::SetName(DWORD type)
//{
// WCHAR c;
// if (type & PRINT_DWORD)
// c = L'H';
// else if (type & USING_UNICODE) {
// if (type & STRING_LAST_CHAR)
// c = L'L';
// else if (type & USING_STRING)
// c = L'Q';
// else
// c = L'W';
// } else {
// if (type & USING_STRING)
// c = L'S';
// else if (type & BIG_ENDIAN)
// c = L'A';
// else
// c = L'B';
// }
// //swprintf(user_entry,L"UserHook%c",c);
//}
void HookManager::AddLink(WORD from, WORD to)
{
HM_LOCK;
//bool flag=false;
//ConsoleOutput("vnrhost:AddLink: lock");
//EnterCriticalSection(&hmcs);
TextThread *from_thread = thread_table->FindThread(from),
*to_thread = thread_table->FindThread(to);
if (to_thread && from_thread) {
if (from_thread->GetThreadParameter()->pid != to_thread->GetThreadParameter()->pid)
DOUT("link to different process");
else if (from_thread->Link()==to_thread)
DOUT("link already exists");
else if (to_thread->CheckCycle(from_thread))
DOUT("cyclic link");
else {
from_thread->Link()=to_thread;
from_thread->LinkNumber()=to;
DOUT("thread linked");
if (addRemoveLink)
addRemoveLink(from_thread);
//WCHAR str[0x40];
//swprintf(str,FormatLink,from,to);
//AddConsoleOutput(str);
}
} else
DOUT("error link");
//else
// AddConsoleOutput(ErrorLink);
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:AddLink: unlock");
}
void HookManager::UnLink(WORD from)
{
HM_LOCK;
//bool flag=false;
//ConsoleOutput("vnrhost:UnLink: lock");
//EnterCriticalSection(&hmcs);
if (TextThread *from_thread = thread_table->FindThread(from)) {
from_thread->Link() = nullptr;
from_thread->LinkNumber() = 0xffff;
DOUT("link deleted");
if (addRemoveLink)
addRemoveLink(from_thread);
}
//else // jichi 12/25/2013: This could happen when the game exist
// ConsoleOutput("vnrhost:UnLink: thread does not exist");
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:UnLink: unlock");
}
void HookManager::UnLinkAll(WORD from)
{
HM_LOCK;
//bool flag=false;
//ConsoleOutput("vnrhost:UnLinkAll: lock");
//EnterCriticalSection(&hmcs);
if (TextThread *from_thread = thread_table->FindThread(from)) {
from_thread->UnLinkAll();
DOUT("link deleted");
}
//else // jichi 12/25/2013: This could happen after the process exists
// ConsoleOutput("vnrhost:UnLinkAll: thread not exist");
//AddConsoleOutput(L"Link deleted.");
//} else
// AddConsoleOutput(L"Thread not exist.");
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:UnLinkAll: unlock");
}
void HookManager::DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD spl, int len, bool space)
{
// jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need
if (!text || !pid || (len <= 0 && !space))
return;
HM_LOCK;
//bool flag=false;
ThreadParameter tp = {pid, hook, retn, spl};
//ConsoleOutput("vnrhost:DispatchText: lock");
//EnterCriticalSection(&hmcs);
TextThread *it;
//`try {
if (TreeNode<ThreadParameter *,DWORD> *in = Search(&tp)) {
DWORD number = in->data;
it = thread_table->FindThread(number);
} else if (IsFull()) { // jichi 1/16/2015: Skip adding threads when full
static bool once = true; // output only once
if (once) {
once = false;
DOUT("so many new threads, skip");
}
return;
} else { // New thread
Insert(&tp, new_thread_number);
it = new TextThread(pid, hook, retn, spl, new_thread_number);
RegisterThread(it, new_thread_number);
DOUT("found new thread");
char entstr[0x200];
it->GetEntryString(entstr);
DOUT(entstr);
while (thread_table->FindThread(++new_thread_number));
if (create)
create(it);
}
if (it)
it->AddText(text, len, false, space); // jichi 10/27/2013: new line is false
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:DispatchText: unlock");
//} catch (...) {
// // ignored
//}
}
void HookManager::AddConsoleOutput(LPCWSTR text)
{
if (text) {
int len = wcslen(text) << 1;
TextThread *console = thread_table->FindThread(0);
//EnterCriticalSection(&hmcs);
console->AddText((BYTE*)text,len,false,true);
console->AddText((BYTE*)L"\r\n",4,false,true);
//LeaveCriticalSection(&hmcs);
}
}
void HookManager::ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD spl)
{
HM_LOCK;
//bool flag=false;
//ConsoleOutput("vnrhost:ClearText: lock");
//EnterCriticalSection(&hmcs);
ThreadParameter tp = {pid, hook, retn, spl};
if (TreeNode<ThreadParameter *, DWORD> *in = Search(&tp))
if (TextThread *it = thread_table->FindThread(in->data)) {
it->Reset();
//SetCurrent(it);
if (reset)
reset(it);
//it->ResetEditText();
}
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:ClearText: unlock");
}
void HookManager::ClearCurrent()
{
HM_LOCK;
//ConsoleOutput("vnrhost:ClearCurrent: lock");
//EnterCriticalSection(&hmcs);
if (current) {
current->Reset();
if (reset)
reset(current);
}
//current->ResetEditText();
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:ClearCurrent: unlock");
}
void HookManager::ResetRepeatStatus()
{
HM_LOCK;
//ConsoleOutput("vnrhost:ResetRepeatStatus: lock");
//EnterCriticalSection(&hmcs);
for (int i = 1; i < thread_table->Used(); i++)
if (TextThread *it = thread_table->FindThread(i))
it->ResetRepeatStatus();
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:ResetRepeatStatus: unlock");
}
//void HookManager::LockHookman(){ EnterCriticalSection(&hmcs); }
//void HookManager::UnlockHookman(){ LeaveCriticalSection(&hmcs); }
/*void HookManager::SetProcessEngineType(DWORD pid, DWORD type)
{
int i;
for (i=0;i<MAX_REGISTER;i++)
if (record[i].pid_register==pid) break;
if (i<MAX_REGISTER)
{
record[i].engine_register|=type;
}
}*/
ProcessRecord *HookManager::GetProcessRecord(DWORD pid)
{
HM_LOCK;
//EnterCriticalSection(&hmcs);
for (int i = 0; i < MAX_REGISTER; i++)
if (record[i].pid_register == pid)
return record + i;
return nullptr;
//ProcessRecord *pr = i < MAX_REGISTER ? record + i : nullptr;
//LeaveCriticalSection(&hmcs);
//return pr;
}
DWORD HookManager::GetProcessIDByPath(LPCWSTR str)
{
WCHAR path[MAX_PATH];
for (int i = 0; i < 8 && record[i].process_handle; i++) {
::GetProcessPath(record[i].process_handle, path);
if (_wcsicmp(path,str) == 0)
return record[i].pid_register;
}
return 0;
}
DWORD HookManager::GetCurrentPID() { return current_pid; }
HANDLE HookManager::GetCmdHandleByPID(DWORD pid)
{
HM_LOCK;
//EnterCriticalSection(&hmcs);
for (int i = 0; i < MAX_REGISTER; i++)
if (record[i].pid_register == pid)
return cmd_pipes[i];
return nullptr;
//HANDLE h = i < MAX_REGISTER ? cmd_pipes[i] : 0;
//LeaveCriticalSection(&hmcs);
//return h;
}
MK_BASIC_TYPE(DWORD)
MK_BASIC_TYPE(LPVOID)
//DWORD Hash(LPCWSTR module, int length)
//{
// bool flag = (length==-1);
// DWORD hash = 0;
// for (;*module && (flag || length--); module++)
// hash = ((hash>>7)|(hash<<25)) + *module;
// return hash;
//}
DWORD GetCurrentPID() { return ::man->GetCurrentPID(); }
HANDLE GetCmdHandleByPID(DWORD pid) { return ::man->GetCmdHandleByPID(pid); }
//void AddLink(WORD from, WORD to) { ::man->AddLink(from, to); }
// jichi 9/27/2013: Unparse to hook parameters /H code
void GetCode(const HookParam &hp, LPWSTR buffer, DWORD pid)
{
WCHAR c;
LPWSTR ptr = buffer;
// jichi 12/7/2014: disabled
//if (hp.type&PRINT_DWORD)
// c = L'H';
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';
}
ptr += swprintf(ptr, L"/H%c",c);
if (hp.type & NO_CONTEXT)
*ptr++ = L'N';
if (hp.offset>>31)
ptr += swprintf(ptr, L"-%X",-(hp.offset+4));
else
ptr += swprintf(ptr, L"%X",hp.offset);
if (hp.type & DATA_INDIRECT) {
if (hp.index>>31)
ptr += swprintf(ptr, L"*-%X",-hp.index);
else
ptr += swprintf(ptr,L"*%X",hp.index);
}
if (hp.type & USING_SPLIT) {
if (hp.split >> 31)
ptr += swprintf(ptr, L":-%X", -(4 + hp.split));
else
ptr += swprintf(ptr, L":%X", hp.split);
}
if (hp.type & SPLIT_INDIRECT) {
if (hp.split_index >> 31)
ptr += swprintf(ptr, L"*-%X", -hp.split_index);
else
ptr += swprintf(ptr, L"*%X", hp.split_index);
}
if (hp.module) {
if (pid) {
WCHAR path[MAX_PATH];
MEMORY_BASIC_INFORMATION info;
ProcessRecord* pr = ::man->GetProcessRecord(pid);
if (pr) {
HANDLE hProc = pr->process_handle;
if (NT_SUCCESS(NtQueryVirtualMemory(hProc,(PVOID)hp.address, MemorySectionName, path, MAX_PATH*2, 0)) &&
NT_SUCCESS(NtQueryVirtualMemory(hProc,(PVOID)hp.address, MemoryBasicInformation, &info, sizeof(info), 0)))
ptr += swprintf(ptr, L"@%X:%s", hp.address - (DWORD)info. AllocationBase, wcsrchr(path,L'\\') + 1);
}
} else {
ptr += swprintf(ptr, L"@%X!%X", hp.address, hp.module);
if (hp.function)
ptr += swprintf(ptr, L"!%X", hp.function);
}
}
else
ptr += swprintf(ptr, L"@%X", hp.address);
}
// jichi 1/16/2015
bool HookManager::IsFull() const { return new_thread_number >= MAX_HOOK; }
void AddHooksToProfile(Profile& pf, const ProcessRecord& pr);
DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread* thread);
void MakeHookRelative(const ProcessRecord& pr, HookParam& hp);
void HookManager::GetProfile(DWORD pid, pugi::xml_node profile_node)
{
const ProcessRecord* pr = GetProcessRecord(pid);
if (pr == NULL)
return;
Profile pf(L"serialize");
AddHooksToProfile(pf, *pr);
AddThreadsToProfile(pf, *pr, pid);
pf.XmlWriteProfile(profile_node);
}
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<CHAR[]> name(new CHAR[hook.NameLength()]);
if (ReadProcessMemory(pr.process_handle, hook.Name(), name.get(), hook.NameLength(), NULL))
{
if (hook.hp.module)
{
HookParam hp = hook.hp;
MakeHookRelative(pr, hp);
pf.AddHook(hook_ptr(new HookProfile(hp, toUnicodeString(name.get()))));
}
else
pf.AddHook(hook_ptr(new HookProfile(hook.hp, toUnicodeString(name.get()))));
}
}
}
ReleaseMutex(pr.hookman_mutex);
}
void MakeHookRelative(const ProcessRecord& pr, HookParam& hp)
{
MEMORY_BASIC_INFORMATION info;
VirtualQueryEx(pr.process_handle, (LPCVOID)hp.address, &info, sizeof(info));
hp.address -= (DWORD)info.AllocationBase;
hp.function = 0;
}
void HookManager::AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid)
{
HM_LOCK;
ThreadTable* table = 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);
}
}
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
}
// EOF
#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
// 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
#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
# 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
#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
// 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
#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
// 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
#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
#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
*/
# 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
// 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;
//}
#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
// 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
#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
// 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();
}
*/
#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
# 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
# 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
/*
* 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 */
#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
#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
# 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
// 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
}
*/
#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
// 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
#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
# # 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
)
# 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
# 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
#pragma once
// ith/common/memory.h
// 8/23/2013 jichi
// Branch: ITH/mem.h, revision 66
#ifndef ITH_HAS_HEAP
# define ITH_MEMSET_HEAP(...) ::memset(__VA_ARGS__)
#else
# define ITH_MEMSET_HEAP(...) (void)0
// Defined in kernel32.lilb
extern "C" {
// PVOID RtlAllocateHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ SIZE_T Size);
__declspec(dllimport) void * __stdcall RtlAllocateHeap(void *HeapHandle, unsigned long Flags, unsigned long Size);
// BOOLEAN RtlFreeHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ PVOID HeapBase);
__declspec(dllimport) int __stdcall RtlFreeHeap(void *HeapHandle, unsigned long Flags, void *HeapBase);
} // extern "C"
//NTSYSAPI
//BOOL
//NTAPI
//RtlFreeHeap(
// _In_ HANDLE hHeap,
// _In_ DWORD dwFlags,
// _In_ LPVOID lpMem
//);
extern void *hHeap; // defined in ith/sys.cc
inline void * __cdecl operator new(size_t lSize)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa366597%28v=vs.85%29.aspx
// HEAP_ZERO_MEMORY flag is critical. All new objects are assumed with zero initialized.
enum { HEAP_ZERO_MEMORY = 0x00000008 };
return RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize);
}
inline void __cdecl operator delete(void *pBlock)
{ RtlFreeHeap(::hHeap, 0, pBlock); }
inline void __cdecl operator delete[](void *pBlock)
{ RtlFreeHeap(::hHeap, 0, pBlock); }
#endif // ITH_HAS_HEAP
#pragma once
// ith/common/string.h
// 8/9/2013 jichi
// Branch: ITH/string.h, rev 66
#ifdef ITH_HAS_CRT // ITH is linked with msvcrt dlls
# include <cstdio>
# include <cstring>
#else
# define _INC_SWPRINTF_INL_
# define CRT_IMPORT __declspec(dllimport)
#include <windows.h> // for wchar_t
extern "C" {
CRT_IMPORT int swprintf(wchar_t *src, const wchar_t *fmt, ...);
CRT_IMPORT int sprintf(char *src, const char *fmt, ...);
CRT_IMPORT int swscanf(const wchar_t *src, const wchar_t *fmt, ...);
CRT_IMPORT int sscanf(const char *src, const char *fmt, ...);
CRT_IMPORT int wprintf(const wchar_t *fmt, ...);
CRT_IMPORT int printf(const char *fmt, ...);
CRT_IMPORT int _wputs(const wchar_t *src);
CRT_IMPORT int puts(const char *src);
CRT_IMPORT int _stricmp(const char *x, const char *y);
CRT_IMPORT int _wcsicmp(const wchar_t *x, const wchar_t *y);
//CRT_IMPORT size_t strlen(const char *);
//CRT_IMPORT size_t wcslen(const wchar_t *);
//CRT_IMPORT char *strcpy(char *,const char *);
//CRT_IMPORT wchar_t *wcscpy(wchar_t *,const wchar_t *);
CRT_IMPORT void *memmove(void *dst, const void *src, size_t sz);
CRT_IMPORT const char *strchr(const char *src, int val);
CRT_IMPORT int strncmp(const char *x, const char *y, size_t sz);
} // extern "C"
#endif // ITH_HAS_CRT
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)
#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
#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
#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
This diff could not be displayed because it is too large.
#pragma once
// engine/engine.h
// 8/23/2013 jichi
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
#include <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
#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
// 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) {}
}
*/
#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
#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
#pragma once
// mono/types.h
// 12/26/2014
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
#include <cstdint>
// mono/io-layer/uglify.h
typedef int8_t gint8;
typedef int32_t gint32;
typedef wchar_t gunichar2; // either char or wchar_t, depending on how mono is compiled
typedef gint8 mono_byte;
typedef gunichar2 mono_unichar2;
// mono/metadata/object.h
typedef mono_byte MonoBoolean;
struct MonoArray;
struct MonoDelegate;
struct MonoException;
struct MonoString;
struct MonoThreadsSync;
struct MonoThread;
struct MonoVTable;
struct MonoObject {
MonoVTable *vtable;
MonoThreadsSync *synchronisation;
};
struct MonoString {
MonoObject object;
gint32 length;
gunichar2 chars[0];
};
// EOF
// pchooks.cc
// 8/1/2014 jichi
#include "src/engine/pchooks.h"
#include "src/main.h"
//#include <gdiplus.h>
#define DEBUG "vnrcli"
#define DPRINT(cstr) ConsoleOutput(DEBUG ":" __FUNCTION__ ":" cstr) // defined in vnrcli
// 8/1/2014 jichi: Split is not used.
// Although split is specified, USING_SPLIT is not assigned.
// Use LPASTE to convert to wchar_t
// http://bytes.com/topic/c/answers/135834-defining-wide-character-strings-macros
//#define LPASTE(s) L##s
//#define L(s) LPASTE(s)
#define NEW_HOOK_AT(_addr, _fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
{ \
HookParam hp = {}; \
hp.address = _addr; \
hp.offset = _data; \
hp.index = _data_ind; \
hp.split = _split_off; \
hp.split_index = _split_ind; \
hp.type = _type; \
hp.length_offset = _len_off; \
NewHook(hp, #_fun); \
}
// Static hook
#define NEW_HOOK(_fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
NEW_HOOK_AT((DWORD)_fun, _fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
#define NEW_MODULE_HOOK(_module, _fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
{ \
if (DWORD addr = (DWORD)::GetProcAddress(_module, #_fun)) \
NEW_HOOK_AT(addr, _fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
}
// jichi 7/17/2014: Renamed from InitDefaultHook
void PcHooks::hookGDIFunctions()
{
DPRINT("enter");
// int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
//
// jichi 9/8/2013: Guessed meaning
// - data(off): 4 * the n-th (base 1) parameter representing the data of the string
// - len_off:
// - the n-th (base 1) parameter representing the length of the string
// - or 1 if is char
// - or 0 if detect on run time
// - type: USING_STRING if len_off != 1 else BIG_ENDIAN or USING_UNICODE
//
// Examples:
// int WINAPI lstrlenA(LPCSTR lpString)
// - data: 4 * 1 = 4, as lpString is the first
// - len_off: 0, as no parameter representing string length
// - type: BIG_ENDIAN, since len_off == 1
// BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
// - data: 4 * 2 = 0x8, as lpString is the second
// - len_off: 3, as nCount is the 3rd parameter
// - type: USING_STRING, since len_off != 1
//
// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
enum stack {
s_retaddr = 0
, s_arg1 = 4 * 1 // 0x4
, s_arg2 = 4 * 2 // 0x8
, s_arg3 = 4 * 3 // 0xc
, s_arg4 = 4 * 4 // 0x10
, s_arg5 = 4 * 5 // 0x14
, s_arg6 = 4 * 6 // 0x18
};
//#define _(Name, ...) \
// hookman[HF_##Name].InitHook(Name, __VA_ARGS__); \
// hookman[HF_##Name].SetHookName(names[HF_##Name]);
// Always use s_arg1 = hDC as split_off
// 7/26/2014 jichi: Why there is no USING_SPLIT type?
// gdi32.dll
NEW_HOOK(GetTextExtentPoint32A, s_arg2, 0,s_arg1,0, USING_STRING, 3) // BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
NEW_HOOK(GetTextExtentExPointA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // BOOL GetTextExtentExPoint(HDC hdc, LPCTSTR lpszStr, int cchString, int nMaxExtent, LPINT lpnFit, LPINT alpDx, LPSIZE lpSize);
NEW_HOOK(GetTabbedTextExtentA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // DWORD GetTabbedTextExtent(HDC hDC, LPCTSTR lpString, int nCount, int nTabPositions, const LPINT lpnTabStopPositions);
NEW_HOOK(GetCharacterPlacementA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // DWORD GetCharacterPlacement(HDC hdc, LPCTSTR lpString, int nCount, int nMaxExtent, LPGCP_RESULTS lpResults, DWORD dwFlags);
NEW_HOOK(GetGlyphIndicesA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // DWORD GetGlyphIndices( HDC hdc, LPCTSTR lpstr, int c, LPWORD pgi, DWORD fl);
NEW_HOOK(GetGlyphOutlineA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // DWORD GetGlyphOutline(HDC hdc, UINT uChar, UINT uFormat, LPGLYPHMETRICS lpgm, DWORD cbBuffer, LPVOID lpvBuffer, const MAT2 *lpmat2);
NEW_HOOK(ExtTextOutA, s_arg6, 0,s_arg1,0, USING_STRING, 7) // BOOL ExtTextOut(HDC hdc, int X, int Y, UINT fuOptions, const RECT *lprc, LPCTSTR lpString, UINT cbCount, const INT *lpDx);
NEW_HOOK(TextOutA, s_arg4, 0,s_arg1,0, USING_STRING, 5) // BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cchString);
NEW_HOOK(TabbedTextOutA, s_arg4, 0,s_arg1,0, USING_STRING, 5) // LONG TabbedTextOut(HDC hDC, int X, int Y, LPCTSTR lpString, int nCount, int nTabPositions, const LPINT lpnTabStopPositions, int nTabOrigin);
NEW_HOOK(GetCharABCWidthsA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharABCWidths(HDC hdc, UINT uFirstChar, UINT uLastChar, LPABC lpabc);
NEW_HOOK(GetCharABCWidthsFloatA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharABCWidthsFloat(HDC hdc, UINT iFirstChar, UINT iLastChar, LPABCFLOAT lpABCF);
NEW_HOOK(GetCharWidth32A, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharWidth32(HDC hdc, UINT iFirstChar, UINT iLastChar, LPINT lpBuffer);
NEW_HOOK(GetCharWidthFloatA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharWidthFloat(HDC hdc, UINT iFirstChar, UINT iLastChar, PFLOAT pxBuffer);
NEW_HOOK(GetTextExtentPoint32W, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(GetTextExtentExPointW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(GetTabbedTextExtentW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(GetCharacterPlacementW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(GetGlyphIndicesW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(GetGlyphOutlineW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
NEW_HOOK(ExtTextOutW, s_arg6, 0,s_arg1,0, USING_UNICODE|USING_STRING, 7)
NEW_HOOK(TextOutW, s_arg4, 0,s_arg1,0, USING_UNICODE|USING_STRING, 5)
NEW_HOOK(TabbedTextOutW, s_arg4, 0,s_arg1,0, USING_UNICODE|USING_STRING, 5)
NEW_HOOK(GetCharABCWidthsW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
NEW_HOOK(GetCharABCWidthsFloatW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
NEW_HOOK(GetCharWidth32A, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
NEW_HOOK(GetCharWidthFloatA, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
// user32.dll
NEW_HOOK(DrawTextA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawText(HDC hDC, LPCTSTR lpchText, int nCount, LPRECT lpRect, UINT uFormat);
NEW_HOOK(DrawTextExA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawTextEx(HDC hdc, LPTSTR lpchText,int cchText, LPRECT lprc, UINT dwDTFormat, LPDRAWTEXTPARAMS lpDTParams);
NEW_HOOK(DrawTextW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(DrawTextExW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
DPRINT("leave");
}
// jichi 6/18/2015: GDI+ functions
void PcHooks::hookGDIPlusFunctions()
{
HMODULE hModule = ::GetModuleHandleA("gdiplus.dll");
if (!hModule) {
DPRINT("not loaded");
return;
}
DPRINT("enter");
enum stack {
s_retaddr = 0
, s_arg1 = 4 * 1 // 0x4
, s_arg2 = 4 * 2 // 0x8
, s_arg3 = 4 * 3 // 0xc
, s_arg4 = 4 * 4 // 0x10
, s_arg5 = 4 * 5 // 0x14
, s_arg6 = 4 * 6 // 0x18
};
// gdiplus.dll
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms534053%28v=vs.85%29.aspx
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms534052%28v=vs.85%29.aspx
// https://msdn.microsoft.com/en-us/library/windows/desktop/ms534039%28v=vs.85%29.aspx
// Use arg1 pionter to GpGraphics as split
//using namespace Gdiplus::DllExports;
// Use arg5 style as split
NEW_MODULE_HOOK(hModule, GdipAddPathString, s_arg2, 0,s_arg5,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipAddPathString(GpPath *path, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFontFamily *family, INT style, REAL emSize, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *format)
NEW_MODULE_HOOK(hModule, GdipAddPathStringl, s_arg2, 0,s_arg5,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipAddPathStringI(GpPath *path, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFontFamily *family, INT style, REAL emSize, GDIPCONST Rect *layoutRect, GDIPCONST GpStringFormat *format)
//NEW_MODULE_HOOK(hModule, GdipMeasureCharacterRanges, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipMeasureCharacterRanges(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF &layoutRect, GDIPCONST GpStringFormat *stringFormat, INT regionCount, GpRegion **regions)
NEW_MODULE_HOOK(hModule, GdipDrawString, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipDrawString(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *stringFormat, GDIPCONST GpBrush *brush);
NEW_MODULE_HOOK(hModule, GdipMeasureString, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3) // GpStatus WINGDIPAPI GdipMeasureString(GpGraphics *graphics, GDIPCONST WCHAR *string, INT length, GDIPCONST GpFont *font, GDIPCONST RectF *layoutRect, GDIPCONST GpStringFormat *stringFormat, RectF *boundingBox, INT *codepointsFitted, INT *linesFilled )
DPRINT("leave");
}
// jichi 10/2/2013
// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
void PcHooks::hookLstrFunctions()
{
DPRINT("enter");
// int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
enum stack {
s_retaddr = 0
, s_arg1 = 4 * 1 // 0x4
//, s_arg2 = 4 * 2 // 0x8
//, s_arg3 = 4 * 3 // 0xc
//, s_arg4 = 4 * 4 // 0x10
//, s_arg5 = 4 * 5 // 0x14
//, s_arg6 = 4 * 6 // 0x18
};
// http://msdn.microsoft.com/en-us/library/78zh94ax.aspx
// int WINAPI lstrlen(LPCTSTR lpString);
// Lstr functions usually extracts rubbish, and might crash certain games like 「Magical Marriage Lunatics!!」
// Needed by Gift
// Use arg1 address for both split and data
NEW_HOOK(lstrlenA, s_arg1, 0,s_arg1,0, USING_STRING, 0) // 9/8/2013 jichi: int WINAPI lstrlen(LPCTSTR lpString);
NEW_HOOK(lstrlenW, s_arg1, 0,s_arg1,0, USING_UNICODE|USING_STRING, 0) // 9/8/2013 jichi: add lstrlen
// size_t strlen(const char *str);
// size_t strlen_l(const char *str, _locale_t locale);
// size_t wcslen(const wchar_t *str);
// size_t wcslen_l(const wchar_t *str, _locale_t locale);
// size_t _mbslen(const unsigned char *str);
// size_t _mbslen_l(const unsigned char *str, _locale_t locale);
// size_t _mbstrlen(const char *str);
// size_t _mbstrlen_l(const char *str, _locale_t locale);
// http://msdn.microsoft.com/en-us/library/ex0hs2ad.aspx
// Needed by 娘姉妹
//
// <tchar.h>
// char *_strinc(const char *current, _locale_t locale);
// wchar_t *_wcsinc(const wchar_t *current, _locale_t locale);
// <mbstring.h>
// unsigned char *_mbsinc(const unsigned char *current);
// unsigned char *_mbsinc_l(const unsigned char *current, _locale_t locale);
//_(L"_strinc", _strinc, 4, 0,4,0, USING_STRING, 0) // 12/13/2013 jichi
//_(L"_wcsinc", _wcsinc, 4, 0,4,0, USING_UNICODE|USING_STRING, 0)
DPRINT("leave");
}
void PcHooks::hookWcharFunctions()
{
DPRINT("enter");
// 12/1/2013 jichi:
// AlterEgo
// http://tieba.baidu.com/p/2736475133
// http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page355
//
// MultiByteToWideChar
// http://blgames.proboards.com/thread/265
//
// WideCharToMultiByte
// http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page156
//
// int MultiByteToWideChar(
// _In_ UINT CodePage,
// _In_ DWORD dwFlags,
// _In_ LPCSTR lpMultiByteStr, // hook here
// _In_ int cbMultiByte,
// _Out_opt_ LPWSTR lpWideCharStr,
// _In_ int cchWideChar
// );
// int WideCharToMultiByte(
// _In_ UINT CodePage,
// _In_ DWORD dwFlags,
// _In_ LPCWSTR lpWideCharStr,
// _In_ int cchWideChar,
// _Out_opt_ LPSTR lpMultiByteStr,
// _In_ int cbMultiByte,
// _In_opt_ LPCSTR lpDefaultChar,
// _Out_opt_ LPBOOL lpUsedDefaultChar
// );
enum stack {
s_retaddr = 0
, s_arg1 = 4 * 1 // 0x4
//, s_arg2 = 4 * 2 // 0x8
, s_arg3 = 4 * 3 // 0xc
//, s_arg4 = 4 * 4 // 0x10
//, s_arg5 = 4 * 5 // 0x14
//, s_arg6 = 4 * 6 // 0x18
};
// 3/17/2014 jichi: Temporarily disabled
// http://sakuradite.com/topic/159
NEW_HOOK(MultiByteToWideChar, s_arg3, 0,4,0, USING_STRING, 4)
NEW_HOOK(WideCharToMultiByte, s_arg3, 0,4,0, USING_UNICODE|USING_STRING, 4)
DPRINT("leave");
}
void PcHooks::hookCharNextFunctions()
{
enum stack {
s_retaddr = 0
, s_arg1 = 4 * 1 // 0x4
, s_arg2 = 4 * 2 // 0x8
//, s_arg3 = 4 * 3 // 0xc
//, s_arg4 = 4 * 4 // 0x10
//, s_arg5 = 4 * 5 // 0x14
//, s_arg6 = 4 * 6 // 0x18
};
DPRINT("enter");
NEW_HOOK(CharNextA, s_arg1, 0,0,0, USING_STRING|DATA_INDIRECT, 1) // LPTSTR WINAPI CharNext(_In_ LPCTSTR lpsz);
NEW_HOOK(CharNextW, s_arg1, 0,0,0, USING_UNICODE|DATA_INDIRECT, 1)
NEW_HOOK(CharPrevA, s_arg1, 0,0,0, USING_STRING|DATA_INDIRECT, 1) // LPTSTR WINAPI CharPrev(_In_ LPCTSTR lpszStart, _In_ LPCTSTR lpszCurrent);
NEW_HOOK(CharPrevW, s_arg1, 0,0,0, USING_UNICODE|DATA_INDIRECT, 1)
//NEW_HOOK(CharNextExA, s_arg2, 0,0,0, USING_STRING|DATA_INDIRECT, 1) // LPSTR WINAPI CharNextExA(_In_ WORD CodePage, _In_ LPCSTR lpCurrentChar, _In_ DWORD dwFlags);
//NEW_HOOK(CharNextExW, s_arg2, 0,0,0, USING_UNICODE|DATA_INDIRECT, 1)
DPRINT("leave");
}
// EOF
#pragma once
// pchooks.h
// 8/1/2014 jichi
namespace PcHooks {
void hookGDIFunctions();
void hookGDIPlusFunctions();
void hookLstrFunctions();
void hookWcharFunctions();
void hookCharNextFunctions();
} // namespace PcHooks
// EOF
#pragma once
// ppsspp/funcinfo.h
// 12/26/2014
// See: https://github.com/hrydgard/ppsspp
// Core/HLE (High Level Emulator)
// - sceCcc
// #void sceCccSetTable(u32 jis2ucs, u32 ucs2jis)
// int sceCccUTF8toUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccUTF8toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccUTF16toUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccUTF16toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccSJIStoUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccSJIStoUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccStrlenUTF8(u32 strAddr)
// int sceCccStrlenUTF16(u32 strAddr)
// int sceCccStrlenSJIS(u32 strAddr)
// u32 sceCccEncodeUTF8(u32 dstAddrAddr, u32 ucs)
// void sceCccEncodeUTF16(u32 dstAddrAddr, u32 ucs)
// u32 sceCccEncodeSJIS(u32 dstAddrAddr, u32 jis)
// u32 sceCccDecodeUTF8(u32 dstAddrAddr)
// u32 sceCccDecodeUTF16(u32 dstAddrAddr)
// u32 sceCccDecodeSJIS(u32 dstAddrAddr)
// int sceCccIsValidUTF8(u32 c)
// int sceCccIsValidUTF16(u32 c)
// int sceCccIsValidSJIS(u32 c)
// int sceCccIsValidUCS2(u32 c)
// int sceCccIsValidUCS4(u32 c)
// int sceCccIsValidJIS(u32 c)
// int sceCccIsValidUnicode(u32 c)
// #u32 sceCccSetErrorCharUTF8(u32 c)
// #u32 sceCccSetErrorCharUTF16(u32 c)
// #u32 sceCccSetErrorCharSJIS(u32 c)
// u32 sceCccUCStoJIS(u32 c, u32 alt)
// u32 sceCccJIStoUCS(u32 c, u32 alt)
// - sceFont: search charCode
// int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr)
// int sceFontGetShadowInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr)
// int sceFontGetCharImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr)
// int sceFontGetShadowImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr)
// int sceFontGetCharGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr)
// int sceFontGetCharGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight)
// #int sceFontSetAltCharacterCode(u32 fontLibHandle, u32 charCode)
// int sceFontGetShadowGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr)
// int sceFontGetShadowGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight)
// - sceKernelInterrupt
// u32 sysclib_strcat(u32 dst, u32 src)
// int sysclib_strcmp(u32 dst, u32 src)
// u32 sysclib_strcpy(u32 dst, u32 src)
// u32 sysclib_strlen(u32 src)
//
// Sample debug string:
// 006EFD8E PUSH PPSSPPWi.00832188 ASCII "sceCccEncodeSJIS(%08x, U+%04x)"
// Corresponding source code in sceCcc:
// ERROR_LOG(HLE, "sceCccEncodeSJIS(%08x, U+%04x): invalid pointer", dstAddrAddr, jis);
struct PPSSPPFunction
{
const char *hookName; // hook name
size_t argIndex; // argument index
unsigned long hookType; // hook parameter type
unsigned long hookSplit; // hook parameter split, positive: stack, negative: registers
const char *pattern; // debug string used within the function
};
// jichi 7/14/2014: UTF-8 is treated as STRING
// http://867258173.diandian.com/post/2014-06-26/40062099618
// sceFontGetCharGlyphImage_Clip
// Sample game: [KID] Monochrome: sceFontGetCharInfo, sceFontGetCharGlyphImage_Clip
//
// Example: { L"sceFontGetCharInfo", 2, USING_UNICODE, 4, "sceFontGetCharInfo(" }
// Text is at arg2, using arg1 as split
#define PPSSPP_FUNCTIONS_INITIALIZER \
{ "sceCccStrlenSJIS", 1, USING_STRING, 0, "sceCccStrlenSJIS(" } \
, { "sceCccStrlenUTF8", 1, USING_UTF8, 0, "sceCccStrlenUTF8(" } \
, { "sceCccStrlenUTF16", 1, USING_UNICODE, 0, "sceCccStrlenUTF16(" } \
\
, { "sceCccSJIStoUTF8", 3, USING_UTF8, 0, "sceCccSJIStoUTF8(" } \
, { "sceCccSJIStoUTF16", 3, USING_STRING, 0, "sceCccSJIStoUTF16(" } \
, { "sceCccUTF8toSJIS", 3, USING_UTF8, 0, "sceCccUTF8toSJIS(" } \
, { "sceCccUTF8toUTF16", 3, USING_UTF8, 0, "sceCccUTF8toUTF16(" } \
, { "sceCccUTF16toSJIS", 3, USING_UNICODE, 0, "sceCccUTF16toSJIS(" } \
, { "sceCccUTF16toUTF8", 3, USING_UNICODE, 0, "sceCccUTF16toUTF8(" } \
\
, { "sceFontGetCharInfo", 2, USING_UNICODE, 4, "sceFontGetCharInfo(" } \
, { "sceFontGetShadowInfo", 2, USING_UNICODE, 4, "sceFontGetShadowInfo("} \
, { "sceFontGetCharImageRect", 2, USING_UNICODE, 4, "sceFontGetCharImageRect(" } \
, { "sceFontGetShadowImageRect", 2, USING_UNICODE, 4, "sceFontGetShadowImageRect(" } \
, { "sceFontGetCharGlyphImage", 2, USING_UNICODE, 4, "sceFontGetCharGlyphImage(" } \
, { "sceFontGetCharGlyphImage_Clip", 2, USING_UNICODE, 4, "sceFontGetCharGlyphImage_Clip(" } \
, { "sceFontGetShadowGlyphImage", 2, USING_UNICODE, 4, "sceFontGetShadowGlyphImage(" } \
, { "sceFontGetShadowGlyphImage_Clip", 2, USING_UNICODE, 4, "sceFontGetShadowGlyphImage_Clip(" } \
\
, { "sysclib_strcat", 2, USING_STRING, 0, "Untested sysclib_strcat(" } \
, { "sysclib_strcpy", 2, USING_STRING, 0, "Untested sysclib_strcpy(" } \
, { "sysclib_strlen", 1, USING_STRING, 0, "Untested sysclib_strlen(" }
// Disabled as I am not sure how to deal with the source string
//, { "sceCccEncodeSJIS", 2, USING_STRING, 0, "sceCccEncodeSJIS(" }
//, { "sceCccEncodeUTF8", 2, USING_UTF8, 0, "sceCccEncodeUTF8(" }
//, { "sceCccEncodeUTF16", 2, USING_UNICODE, 0, "sceCccEncodeUTF16(" }
//, { "sysclib_strcmp", 2, USING_STRING, 0, "Untested sysclib_strcmp(" }
// EOF
#pragma once
// except.h
// 9/17/2013 jichi
#define ITH_RAISE (*(int*)0 = 0) // raise C000005, for debugging only
#ifdef ITH_HAS_SEH
# define ITH_TRY __try
# define ITH_EXCEPT __except(EXCEPTION_EXECUTE_HANDLER)
# define ITH_WITH_SEH(...) \
ITH_TRY { __VA_ARGS__; } ITH_EXCEPT {}
#else // for old msvcrt.dll on Windows XP that does not have exception handler
// Currently, only with_seh is implemented. Try and catch are not.
# define ITH_TRY if (true)
# define ITH_EXCEPT else
# include "winseh/winseh.h"
# define ITH_WITH_SEH(...) seh_with(__VA_ARGS__)
#endif // ITH_HAS_SEH
// EOF
// texthook.cc
// 8/24/2013 jichi
// Branch: ITH_DLL/texthook.cpp, rev 128
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
# pragma warning (disable:4018) // C4018: sign/unsigned mismatch
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
#endif // _MSC_VER
#include "src/hijack/texthook.h"
#include "src/engine/match.h"
#include "src/except.h"
#include "src/main.h"
#include "include/const.h"
#include "ithsys/ithsys.h"
#include "winkey/winkey.h"
#include "disasm/disasm.h"
//#include "winseh/winseh.h"
//#define ConsoleOutput(...) (void)0 // jichi 9/17/2013: I don't need this ><
// - Global variables -
// 10/14/2014 jichi: disable GDI hooks
static bool gdi_hook_enabled_ = true; // enable GDI by default
static bool gdiplus_hook_enabled_ = false; // disable GDIPlus by default
bool GDIHooksEnabled() { return ::gdi_hook_enabled_; }
bool GDIPlusHooksEnabled() { return ::gdiplus_hook_enabled_; }
void EnableGDIHooks() { ::gdi_hook_enabled_ = true; }
void EnableGDIPlusHooks() { ::gdiplus_hook_enabled_ = true; }
void DisableGDIHooks() { ::gdi_hook_enabled_ = false; }
void DisableGDIPlusHooks() { ::gdiplus_hook_enabled_ = false; }
static bool IsGDIFunction(LPCVOID addr)
{
static LPVOID funcs[] = { HOOK_GDI_FUNCTION_LIST };
for (size_t i = 0; i < sizeof(funcs)/sizeof(*funcs); i++)
if (addr == funcs[i])
return true;
return false;
}
//FilterRange filter[8];
DWORD flag,
enter_count;
TextHook *hookman,
*current_available;
// - Unnamed helpers -
namespace { // unnamed
//provide const time hook entry.
int userhook_count;
#if 0 // 3/6/2015 jichi: this hook is not used and hence disabled
const byte common_hook2[] = {
0x89, 0x3c,0xe4, // mov [esp],edi
0x60, // pushad
0x9c, // pushfd
0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
0x8b,0x32, // mov esi,[edx] ; return address
0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook
0xe8, 0,0,0,0, // call @hook
0x9d, // popfd
0x61, // popad
0x5f, // pop edi ; skip return address on stack
}; //...
#endif // 0
const BYTE common_hook[] = {
0x9c, // pushfd
0x60, // pushad
0x9c, // pushfd
0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
0x8b,0x32, // mov esi,[edx] ; return address
0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook
0xe8, 0,0,0,0, // call @hook
0x9d, // popfd
0x61, // popad
0x9d // popfd
};
/**
* jichi 7/19/2014
*
* @param original_addr
* @param new_addr
* @param hook_len
* @param original_len
* @return -1 if failed, else 0 if ?, else ?
*/
int MapInstruction(DWORD original_addr, DWORD new_addr, BYTE &hook_len, BYTE &original_len)
{
int flag = 0;
DWORD l = 0;
const BYTE *r = (const BYTE *)original_addr; // 7/19/2014 jichi: original address is not modified
BYTE *c = (BYTE *)new_addr; // 7/19/2014 jichi: but new address might be modified
while((r - (BYTE *) original_addr) < 5) {
l = ::disasm(r);
if (l == 0) {
ConsoleOutput("vnrcli:MapInstruction: FAILED: failed to disasm");
return -1;
}
::memcpy(c, r, l);
if (*r >= 0x70 && *r < 0x80) {
c[0] = 0xf;
c[1] = *r + 0x10;
c += 6;
__asm
{
mov eax,r
add eax,2
movsx edx,byte ptr [eax-1]
add edx,eax
mov eax,c
sub edx,eax
mov [eax-4],edx
}
} else if (*r == 0xeb) {
c[0] = 0xe9;
c += 5;
__asm
{
mov eax,r
add eax,2
movsx edx,[eax-1]
add edx,eax
mov eax,c
sub edx,eax
mov [eax-4],edx
}
if (r - (BYTE *)original_addr < 5 - l) {
ConsoleOutput("vnrcli:MapInstruction: not safe to move instruction right after short jmp");
return -1; // Not safe to move instruction right after short jmp.
} else
flag = 1;
} else if (*r == 0xe8 || *r == 0xe9) {
c[0]=*r;
c += 5;
flag = (*r == 0xe9);
__asm
{
mov eax,r
add eax,5
mov edx,[eax-4]
add edx,eax
mov eax,c
sub edx,eax
mov [eax-4],edx
}
} else if (*r == 0xf && (*(r + 1) >> 4) == 0x8) {
c += 6;
__asm
{
mov eax,r
mov edx,dword ptr [eax+2]
add eax,6
add eax,edx
mov edx,c
sub eax,edx
mov [edx-4],eax
}
}
else
c += l;
r += l;
}
original_len = r - (BYTE *)original_addr;
hook_len = c - (BYTE *)new_addr;
return flag;
}
//copy original instruction
//jmp back
DWORD GetModuleBase(DWORD hash)
{
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov esi,[eax+0x14]
mov edi,_wcslwr
listfind:
mov edx,[esi+0x28]
test edx,edx
jz notfound
push edx
call edi
pop edx
xor eax,eax
calc:
movzx ecx, word ptr [edx]
test cl,cl
jz fin
ror eax,7
add eax,ecx
add edx,2
jmp calc
fin:
cmp eax,[hash]
je found
mov esi,[esi]
jmp listfind
notfound:
xor eax,eax
jmp termin
found:
mov eax,[esi+0x10]
termin:
}
}
DWORD GetModuleBase()
{
__asm
{
mov eax, fs:[0x18]
mov eax, [eax + 0x30]
mov eax, [eax + 0xc]
mov eax, [eax + 0xc]
mov eax, [eax + 0x18]
}
}
//void NotifyHookInsert()
//{
// if (live)
// {
// BYTE buffer[0x10];
// *(DWORD*)buffer=-1;
// *(DWORD*)(buffer+4)=1;
// IO_STATUS_BLOCK ios;
// NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0);
// }
//}
__declspec(naked) void SafeExit() // Return to eax
{
__asm
{
mov [esp+0x24], eax
popfd
popad
retn
}
}
#if 0
// jichi 12/2/2013: This function mostly return 0.
// But sometimes return the hook address from TextHook::Send
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
{
//with_seh(hook->Send(dwDataBase, dwRetn));
seh_push_(seh_exit, 0, eax, ebx) // jichi 12/13/2013: only eax and ebx are available. ecx and edx are used.
__asm
{
push esi
push edx
call TextHook::UnsafeSend
test eax, eax
jz seh_exit // label in seh_pop
mov ecx, SafeExit
mov [esp + 8], ecx // jichi 12/13/2013: change exit point if Send returns non-zero, not + 8 beause two elements has been pused
}
seh_pop_(seh_exit)
__asm retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
}
#endif // 0
#if 1
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
{
// jichi 12/17/2013: The function parameters here are meaning leass. The parameters are in esi and edi
__asm
{
push esi
push edx
call TextHook::Send
test eax, eax
jz ok // label in seh_pop
mov ecx, SafeExit
mov [esp], ecx // jichi 12/13/2013: change exit point if Send returns non-zero
ok:
retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
}
}
#endif // 1
// jichi 12/13/2013: return if the retn address is within the filter dlls
inline bool HookFilter(DWORD retn)
{
for (DWORD i = 0; ::filter[i].lower; i++)
if (retn > ::filter[i].lower && retn < ::filter[i].upper)
return true;
return false;
}
// Return false if all text are ascii
bool NoAsciiFilter(LPVOID data, DWORD *size, HookParam *, BYTE)
{
auto text = reinterpret_cast<LPBYTE>(data);
if (text)
for (size_t i = 0; i < *size; i++)
if (text[i] > 127)
return true;
return false;
}
} // unnamed namespace
// - TextHook methods -
// jichi 12/2/2013: This function mostly return 0.
// It return the hook address only for auxiliary case.
// However, because no known hooks are auxiliary, this function always return 0.
//
// jichi 5/11/2014:
// - dwDataBase: the stack address
// - dwRetn: the return address of the hook
DWORD TextHook::Send(DWORD dwDataBase, DWORD dwRetn)
{
// jich: 6/17/2015: do not send when ctrl/shift are controlled
//if (WinKey::isKeyControlPressed() || WinKey::isKeyShiftPressed() && !WinKey::isKeyReturnPressed())
// return 0;
DWORD ret = 0;
//char b[0x100];
//::wcstombs(b, hook_name, 0x100);
//ConsoleOutput(b);
ITH_WITH_SEH(ret = UnsafeSend(dwDataBase, dwRetn));
return ret;
}
DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn)
{
enum { SMALL_BUFF_SIZE = 0x80 };
enum { MAX_DATA_SIZE = 0x10000 }; // jichi 12/25/2013: The same as the original ITH
DWORD dwCount,
dwAddr,
dwDataIn,
dwSplit;
BYTE *pbData,
pbSmallBuff[SMALL_BUFF_SIZE];
DWORD dwType = hp.type;
if (!::live) // the pipe thread is busy
return 0;
if ((dwType & NO_CONTEXT) == 0 && HookFilter(dwRetn))
return 0;
if ((dwType & NO_ASCII) && !hp.filter_fun)
hp.filter_fun = NoAsciiFilter;
// jichi 10/24/2014: Skip GDI functions
if (!::gdi_hook_enabled_ && ::IsGDIFunction((LPCVOID)hp.address))
return 0;
dwAddr = hp.address;
/** jichi 12/24/2014
* @param addr function address
* @param frame real address of the function, supposed to be the same as addr
* @param stack address of current stack - 4
* @return If success, which is reverted
*/
if (::trigger)
::trigger = Engine::InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
// jichi 10/21/2014: Directly invoke engine functions.
//if (trigger) {
// if (InsertDynamicHook)
// trigger = InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
// else
// trigger = 0;
//}
#if 0 // diasble HOOK_AUXILIARY
// jichi 12/13/2013: None of known hooks are auxiliary
if (dwType & HOOK_AUXILIARY) {
//Clean hook when dynamic hook finished.
//AUX hook is only used for a foothold of dynamic hook.
if (!trigger) {
ClearHook();
// jichi 12/13/2013: This is the only place where this function could return non-zero value
// However, I non of the known hooks are auxiliary
return dwAddr;
}
return 0;
}
#endif // 0
// jichi 10/24/2014: generic hook function
if (hp.hook_fun && !hp.hook_fun(dwDataBase, &hp))
hp.hook_fun = nullptr;
if (dwType & HOOK_EMPTY) // jichi 10/24/2014: dummy hook only for dynamic hook
return 0;
// jichi 2/2/2015: Send multiple texts
for (BYTE textIndex = 0; textIndex <= hp.extra_text_count; textIndex++) {
dwCount = 0;
dwSplit = 0;
dwDataIn = *(DWORD *)(dwDataBase + hp.offset); // default value
//if (dwType & EXTERN_HOOK) {
if (hp.text_fun) { // jichi 10/24/2014: remove EXTERN_HOOK
//DataFun fun=(DataFun)hp.text_fun;
//auto fun = hp.text_fun;
hp.text_fun(dwDataBase, &hp, textIndex, &dwDataIn, &dwSplit, &dwCount);
//if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
// return 0;
if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
dwSplit -= ::processStartAddress;
} else {
if (dwDataIn == 0)
return 0;
if (dwType & FIXING_SPLIT)
dwSplit = FIXED_SPLIT_VALUE; // fuse all threads, and prevent floating
else if (dwType & USING_SPLIT) {
dwSplit = *(DWORD *)(dwDataBase + hp.split);
if (dwType & SPLIT_INDIRECT) {
if (IthGetMemoryRange((LPVOID)(dwSplit + hp.split_index), 0, 0))
dwSplit = *(DWORD *)(dwSplit + hp.split_index);
else
return 0;
}
if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
dwSplit -= ::processStartAddress;
}
if (dwType & DATA_INDIRECT) {
if (IthGetMemoryRange((LPVOID)(dwDataIn + hp.index), 0, 0))
dwDataIn = *(DWORD *)(dwDataIn + hp.index);
else
return 0;
}
//if (dwType & PRINT_DWORD) {
// swprintf((WCHAR *)(pbSmallBuff + HEADER_SIZE), L"%.8X ", dwDataIn);
// dwDataIn = (DWORD)pbSmallBuff + HEADER_SIZE;
//}
dwCount = GetLength(dwDataBase, dwDataIn);
}
// jichi 12/25/2013: validate data size
if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
return 0;
size_t sz = dwCount + HEADER_SIZE;
if (sz >= SMALL_BUFF_SIZE)
pbData = new BYTE[sz];
//ITH_MEMSET_HEAP(pbData, 0, sz * sizeof(BYTE)); // jichi 9/26/2013: zero memory
else
pbData = pbSmallBuff;
if (hp.length_offset == 1) {
if (dwType & STRING_LAST_CHAR) {
LPWSTR ts = (LPWSTR)dwDataIn;
dwDataIn = ts[::wcslen(ts) -1];
}
dwDataIn &= 0xffff;
if ((dwType & BIG_ENDIAN) && (dwDataIn >> 8))
dwDataIn = _byteswap_ushort(dwDataIn & 0xffff);
if (dwCount == 1)
dwDataIn &= 0xff;
*(WORD *)(pbData + HEADER_SIZE) = dwDataIn & 0xffff;
}
else
::memcpy(pbData + HEADER_SIZE, (void *)dwDataIn, dwCount);
// jichi 10/14/2014: Add filter function
if (hp.filter_fun && !hp.filter_fun(pbData + HEADER_SIZE, &dwCount, &hp, textIndex) || dwCount <= 0) {
if (pbData != pbSmallBuff)
delete[] pbData;
return 0;
}
*(DWORD *)pbData = dwAddr;
if (dwType & (NO_CONTEXT|FIXING_SPLIT))
dwRetn = 0;
else if (dwRetn && (dwType & RELATIVE_SPLIT))
dwRetn -= ::processStartAddress;
*((DWORD *)pbData + 1) = dwRetn;
*((DWORD *)pbData + 2) = dwSplit;
if (dwCount) {
IO_STATUS_BLOCK ios = {};
IthCoolDown(); // jichi 9/28/2013: cool down to prevent parallelization in wine
//CliLockPipe();
if (STATUS_PENDING == NtWriteFile(::hPipe, 0, 0, 0, &ios, pbData, dwCount + HEADER_SIZE, 0, 0)) {
NtWaitForSingleObject(::hPipe, 0, 0);
NtFlushBuffersFile(::hPipe, &ios);
}
//CliUnlockPipe();
}
if (pbData != pbSmallBuff)
delete[] pbData;
}
return 0;
}
int TextHook::InsertHook()
{
//ConsoleOutput("vnrcli:InsertHook: enter");
NtWaitForSingleObject(hmMutex, 0, 0);
int ok = InsertHookCode();
IthReleaseMutex(hmMutex);
if (hp.type & HOOK_ADDITIONAL) {
NotifyHookInsert(hp.address);
//ConsoleOutput(hook_name);
//RegisterHookName(hook_name,hp.address);
}
//ConsoleOutput("vnrcli:InsertHook: leave");
return ok;
}
int TextHook::InsertHookCode()
{
enum : int { yes = 0, no = 1 };
DWORD ret = no;
// jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7
ITH_WITH_SEH(ret = UnsafeInsertHookCode());
//if (ret == no)
// ITH_WARN(L"Failed to insert hook");
return ret;
}
int TextHook::UnsafeInsertHookCode()
{
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter");
enum : int { yes = 0, no = 1 };
// MODULE_OFFSET is set, but there's no module address
// this means that this is an absolute address found on Windows 2000/XP
// we make the address relative to the process base
// we also store the original address in the function field because normally there can not
// exist a function address without a module address
if (hp.type & MODULE_OFFSET && !hp.module) {
DWORD base = GetModuleBase();
hp.function = hp.address;
hp.address -= 0x400000;
hp.address += base;
hp.type &= ~MODULE_OFFSET;
}
else if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address.
if (DWORD base = GetModuleBase(hp.module)) {
if (hp.function && (hp.type & FUNCTION_OFFSET)) {
base = GetExportAddress(base, hp.function);
if (base)
hp.address += base;
else {
current_hook--;
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: function not found in the export table");
return no;
}
}
else {
hp.address += base;
}
hp.type &= ~(MODULE_OFFSET | FUNCTION_OFFSET);
}
else {
current_hook--;
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: module not present");
return no;
}
}
{
TextHook *it = hookman;
for (int i = 0; (i < current_hook) && it; it++) { // Check if there is a collision.
if (it->Address())
i++;
//it = hookman + i;
if (it == this)
continue;
if (it->Address() <= hp.address &&
it->Address() + it->Length() > hp.address) {
it->ClearHook();
break;
}
}
}
// Verify hp.address.
MEMORY_BASIC_INFORMATION info = {};
NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)hp.address, MemoryBasicInformation, &info, sizeof(info), nullptr);
if (info.Type & PAGE_NOACCESS) {
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: page no access");
return no;
}
// Initialize common routine.
memcpy(recover, common_hook, sizeof(common_hook));
BYTE *c = (BYTE *)hp.address,
*r = recover;
BYTE inst[8]; // jichi 9/27/2013: Why 8? Only 5 bytes will be written using NtWriteVirtualMemory
inst[0] = 0xe9; // jichi 9/27/2013: 0xe9 is jump, see: http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Hackers_Manual
__asm
{
mov edx,r // r = recover
mov eax,this
mov [edx+0xa],eax // push TextHook*, resolve to correspond hook.
lea eax,[edx+0x13]
mov edx,ProcessHook
sub edx,eax
mov [eax-4],edx // call ProcessHook
mov eax,c
add eax,5
mov edx,r
sub edx,eax
lea eax,inst+1
mov [eax],edx // jichi 12/17/2013: the parameter of jmp is in edx. So, ProcessHook must be naked.
}
r += sizeof(common_hook);
hp.hook_len = 5;
//bool jmpflag=false; // jichi 9/28/2013: nto used
// Copy original code.
switch (MapInstruction(hp.address, (DWORD)r, hp.hook_len, hp.recover_len)) {
case -1:
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: failed to map instruction");
return no;
case 0:
__asm
{
mov ecx,this
movzx eax,[ecx]hp.hook_len
movzx edx,[ecx]hp.recover_len
add edx,[ecx]hp.address
add eax,r
add eax,5
sub edx,eax
mov [eax-5],0xe9 // jichi 9/27/2013: 0xe9 is jump
mov [eax-4],edx
}
}
// jichi 9/27/2013: Save the original instructions in the memory
memcpy(original, (LPVOID)hp.address, hp.recover_len);
//Check if the new hook range conflict with existing ones. Clear older if conflict.
{
TextHook *it = hookman;
for (int i = 0; i < current_hook; it++) {
if (it->Address())
i++;
if (it == this)
continue;
if (it->Address() >= hp.address &&
it->Address() < hp.hook_len + hp.address) {
it->ClearHook();
break;
}
}
}
// Insert hook and flush instruction cache.
enum {c8 = 0xcccccccc};
DWORD int3[] = {c8, c8};
DWORD t = 0x100,
old,
len;
// jichi 9/27/2013: Overwrite the memory with inst
// See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Memory%20Management/Virtual%20Memory/NtProtectVirtualMemory.html
// See: http://doxygen.reactos.org/d8/d6b/ndk_2mmfuncs_8h_af942709e0c57981d84586e74621912cd.html
DWORD addr = hp.address;
NtProtectVirtualMemory(NtCurrentProcess(), (PVOID *)&addr, &t, PAGE_EXECUTE_READWRITE, &old);
NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.address, inst, 5, &t);
len = hp.recover_len - 5;
if (len)
NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.address + 5, int3, len, &t);
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.address, hp.recover_len);
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)::hookman, 0x1000);
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: leave: succeed");
return 0;
}
int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind,
DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
{
NtWaitForSingleObject(hmMutex, 0, 0);
hp.address = (DWORD)addr;
hp.offset = data;
hp.index = data_ind;
hp.split = split_off;
hp.split_index = split_ind;
hp.type = type;
hp.hook_len = 0;
hp.module = 0;
hp.length_offset = len_off & 0xffff;
current_hook++;
if (current_available >= this)
for (current_available = this + 1; current_available->Address(); current_available++);
IthReleaseMutex(hmMutex);
return this - hookman;
}
int TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag)
{
NtWaitForSingleObject(hmMutex, 0, 0);
hp = h;
hp.type |= set_flag;
if (name && name != hook_name) {
SetHookName(name);
}
current_hook++;
current_available = this+1;
while (current_available->Address())
current_available++;
IthReleaseMutex(hmMutex);
return 1;
}
int TextHook::RemoveHook()
{
enum : int { yes = 1, no = 0 };
if (!hp.address)
return no;
ConsoleOutput("vnrcli:RemoveHook: enter");
const LONGLONG timeout = -50000000; // jichi 9/28/2012: in 100ns, wait at most for 5 seconds
NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout);
DWORD l = hp.hook_len;
//with_seh({ // jichi 9/17/2013: might crash ><
// jichi 12/25/2013: Actually, __try cannot catch such kind of exception
ITH_TRY {
NtWriteVirtualMemory(NtCurrentProcess(), (LPVOID)hp.address, original, hp.recover_len, &l);
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.address, hp.recover_len);
} ITH_EXCEPT {}
//});
hp.hook_len = 0;
IthReleaseMutex(hmMutex);
ConsoleOutput("vnrcli:RemoveHook: leave");
return yes;
}
int TextHook::ClearHook()
{
NtWaitForSingleObject(hmMutex, 0, 0);
int err = RemoveHook();
if (hook_name) {
delete[] hook_name;
hook_name = nullptr;
}
memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH
//if (current_available>this)
// current_available = this;
current_hook--;
IthReleaseMutex(hmMutex);
return err;
}
int TextHook::ModifyHook(const HookParam &hp)
{
//WCHAR name[0x40];
DWORD len = 0;
if (hook_name)
len = ::strlen(hook_name);
LPSTR name = 0;
if (len) {
name = new char[len + 1];
//ITH_MEMSET_HEAP(name, 0, sizeof(wchar_t) * (len + 1)); // jichi 9/26/2013: zero memory
strcpy(name, hook_name);
}
ClearHook();
InitHook(hp, name);
InsertHook();
if (name)
delete[] name;
return 0;
}
int TextHook::RecoverHook()
{
if (hp.address) {
// jichi 9/28/2013: Only enable TextOutA to debug Cross Channel
//if (hp.address == (DWORD)TextOutA)
InsertHook();
return 1;
}
return 0;
}
int TextHook::SetHookName(LPCSTR name)
{
name_length = strlen(name) + 1;
if (hook_name)
delete[] hook_name;
hook_name = new char[name_length];
//ITH_MEMSET_HEAP(hook_name, 0, sizeof(wchar_t) * name_length); // jichi 9/26/2013: zero memory
strcpy(hook_name, name);
return 0;
}
int TextHook::GetLength(DWORD base, DWORD in)
{
if (base == 0)
return 0;
int len;
switch (hp.length_offset) {
default: // jichi 12/26/2013: I should not put this default branch to the end
len = *((int *)base + hp.length_offset);
if (len >= 0) {
if (hp.type & USING_UNICODE)
len <<= 1;
break;
}
else if (len != -1)
break;
//len == -1 then continue to case 0.
case 0:
if (hp.type & USING_UNICODE)
len = wcslen((const wchar_t *)in) << 1;
else
len = strlen((const char *)in);
break;
case 1:
if (hp.type & USING_UNICODE)
len = 2;
else {
if (hp.type & BIG_ENDIAN)
in >>= 8;
len = LeadByteTable[in & 0xff]; //Slightly faster than IsDBCSLeadByte
}
break;
}
// jichi 12/25/2013: This function originally return -1 if failed
//return len;
return max(0, len);
}
// EOF
//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
/*
DWORD recv_esp, recv_addr;
EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
{
//WCHAR str[0x40],
// name[0x100];
//ConsoleOutput(L"Exception raised during hook processing.");
//swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
//ConsoleOutput(str);
//MEMORY_BASIC_INFORMATION info;
//if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemoryBasicInformation,&info,sizeof(info),0)) &&
// NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemorySectionName,name,0x200,0))) {
// swprintf(str, L"Exception offset: 0x%.8X:%s",
// ContextRecord->Eip-(DWORD)info.AllocationBase,
// wcsrchr(name,L'\\')+1);
// ConsoleOutput(str);
//}
ContextRecord->Esp = recv_esp;
ContextRecord->Eip = recv_addr;
return ExceptionContinueExecution;
}
//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
DWORD recv_esp, recv_addr;
EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
{
//WCHAR str[0x40],
// name[0x100];
//ConsoleOutput(L"Exception raised during hook processing.");
//swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
//ConsoleOutput(str);
//MEMORY_BASIC_INFORMATION info;
//if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemoryBasicInformation,&info,sizeof(info),0)) &&
// NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemorySectionName,name,0x200,0))) {
// swprintf(str, L"Exception offset: 0x%.8X:%s",
// ContextRecord->Eip-(DWORD)info.AllocationBase,
// wcsrchr(name,L'\\')+1);
// ConsoleOutput(str);
//}
ContextRecord->Esp = recv_esp;
ContextRecord->Eip = recv_addr;
return ExceptionContinueExecution;
}
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
{
__asm
{
mov eax,seh_recover
mov recv_addr,eax
push ExceptHandler
push fs:[0]
mov recv_esp,esp
mov fs:[0],esp
push esi
push edx
call TextHook::Send
test eax,eax
jz seh_recover
mov ecx,SafeExit
mov [esp + 0x8], ecx // change exit point
seh_recover:
pop dword ptr fs:[0]
pop ecx
retn
}
}
*/
#pragma once
// texthook.h
// 8/24/2013 jichi
// Branch: IHF_DLL/IHF_CLIENT.h, rev 133
//
// 8/24/2013 TODO:
// - Clean up this file
// - Reduce global variables. Use namespaces or singleton classes instead.
#include "src/tree/avl.h"
#include "include/types.h"
#include <windows.h>
// jichi 12/25/2013: Header in each message sent to vnrsrv
// There are totally three elements
// - 0x0 dwAddr hook address
// - 0x4 dwRetn return address
// - 0x8 dwSplit split value
#define HEADER_SIZE 0xc
extern int current_hook;
extern WCHAR dll_mutex[];
//extern WCHAR dll_name[];
extern DWORD trigger;
//extern DWORD current_process_id;
// jichi 6/3/2014: Get memory range of the current module
extern DWORD processStartAddress,
processStopAddress;
template <class T, class D, class fComp, class fCopy, class fLength>
class AVLTree;
struct FunctionInfo {
DWORD addr;
DWORD module;
DWORD size;
LPWSTR name;
};
struct SCMP;
struct SCPY;
struct SLEN;
extern AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN> *tree;
void InitFilterTable();
// jichi 9/25/2013: This class will be used by NtMapViewOfSectionfor
// interprocedure communication, where constructor/destructor will NOT work.
class TextHook : public Hook
{
int UnsafeInsertHookCode();
DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn);
public:
int InsertHook();
int InsertHookCode();
int InitHook(const HookParam &hp, LPCSTR name = 0, WORD set_flag = 0);
int InitHook(LPVOID addr, DWORD data, DWORD data_ind,
DWORD split_off, DWORD split_ind, WORD type, DWORD len_off = 0);
DWORD Send(DWORD dwDataBase, DWORD dwRetn);
int RecoverHook();
int RemoveHook();
int ClearHook();
int ModifyHook(const HookParam&);
int SetHookName(LPCSTR name);
int GetLength(DWORD base, DWORD in); // jichi 12/25/2013: Return 0 if failed
void CoolDown(); // jichi 9/28/2013: flush instruction cache on wine
};
extern TextHook *hookman,
*current_available;
//void InitDefaultHook();
struct FilterRange { DWORD lower, upper; };
extern FilterRange *filter;
extern bool running,
live;
extern HANDLE hPipe,
hmMutex;
DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter);
DWORD WINAPI CommandPipe(LPVOID lpThreadParameter);
//void RequestRefreshProfile();
//typedef DWORD (*InsertHookFun)(DWORD);
//typedef DWORD (*IdentifyEngineFun)();
//typedef DWORD (*InsertDynamicHookFun)(LPVOID addr, DWORD frame, DWORD stack);
//extern IdentifyEngineFun IdentifyEngine;
//extern InsertDynamicHookFun InsertDynamicHook;
// jichi 9/28/2013: Protect pipeline in wine
void CliLockPipe();
void CliUnlockPipe();
// EOF
// main.cc
// 8/24/2013 jichi
// Branch: ITH_DLL/main.cpp, rev 128
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
#endif // _MSC_VER
#include "src/main.h"
#include "src/tree/avl.h"
#include "src/engine/match.h"
#include "src/hijack/texthook.h"
#include "src/util/growl.h"
#include "src/except.h"
#include "include/const.h"
#include "include/defs.h"
#include "ithsys/ithsys.h"
#include "ccutil/ccmacro.h"
#include <cstdio> // for swprintf
//#include "ntinspect/ntinspect.h"
//#include "winseh/winseh.h"
//#include <boost/foreach.hpp>
//#include "md5.h"
//#include <ITH\AVL.h>
//#include <ITH\ntdll.h>
// Global variables
// jichi 6/3/2014: memory range of the current module
DWORD processStartAddress,
processStopAddress;
namespace { // unnamed
wchar_t processName[MAX_PATH];
inline void GetProcessName(wchar_t *name)
{
//assert(name);
PLDR_DATA_TABLE_ENTRY it;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
}
wcscpy(name, it->BaseDllName.Buffer);
}
} // unmaed namespace
enum { HOOK_BUFFER_SIZE = MAX_HOOK * sizeof(TextHook) };
//#define MAX_HOOK (HOOK_BUFFER_SIZE/sizeof(TextHook))
DWORD hook_buff_len = HOOK_BUFFER_SIZE;
namespace { FilterRange _filter[IHF_FILTER_CAPACITY]; }
FilterRange *filter = _filter;
WCHAR hm_section[0x100];
HINSTANCE hDLL;
HANDLE hSection;
bool running,
live = false;
int current_hook = 0,
user_hook_count = 0;
DWORD trigger = 0;
HANDLE
hFile,
hMutex,
hmMutex;
//DWORD current_process_id;
extern DWORD enter_count;
//extern LPWSTR current_dir;
extern DWORD engine_type;
extern DWORD module_base;
AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN> *tree;
namespace { // unnamed
void AddModule(DWORD hModule, DWORD size, LPWSTR name)
{
FunctionInfo info = {0, hModule, size, name};
IMAGE_DOS_HEADER *DosHdr = (IMAGE_DOS_HEADER *)hModule;
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
DWORD dwReadAddr = hModule + 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;
dwExportAddr += hModule;
IMAGE_EXPORT_DIRECTORY *ExtDir = (IMAGE_EXPORT_DIRECTORY*)dwExportAddr;
dwExportAddr = hModule+ExtDir->AddressOfNames;
for (UINT uj = 0; uj < ExtDir->NumberOfNames; uj++) {
DWORD dwFuncName = *(DWORD *)dwExportAddr;
char *pcBuffer = (char *)(hModule + dwFuncName);
char *pcFuncPtr = (char *)(hModule + (DWORD)ExtDir->AddressOfNameOrdinals+(uj * sizeof(WORD)));
WORD word = *(WORD *)pcFuncPtr;
pcFuncPtr = (char *)(hModule + (DWORD)ExtDir->AddressOfFunctions+(word * sizeof(DWORD)));
info.addr = hModule + *(DWORD *)pcFuncPtr;
::tree->Insert(pcBuffer, info);
dwExportAddr += sizeof(DWORD);
}
}
}
}
void AddAllModules()
{
// jichi 9/26/2013: AVLTree is already zero
PPEB ppeb;
__asm {
mov eax, fs:[0x30]
mov ppeb, eax
}
DWORD temp = *(DWORD *)(&ppeb->Ldr->InLoadOrderModuleList);
PLDR_DATA_TABLE_ENTRY it = (PLDR_DATA_TABLE_ENTRY)temp;
while (it->SizeOfImage) {
AddModule((DWORD)it->DllBase, it->SizeOfImage, it->BaseDllName.Buffer);
it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink;
if (*(DWORD *)it == temp)
break;
}
}
void RequestRefreshProfile()
{
if (::live) {
BYTE buffer[0x80] = {}; // 11/14/2013: reset to zero. Shouldn't it be 0x8 instead of 0x80?
*(DWORD *)buffer = -1;
*(DWORD *)(buffer + 4) = 1;
*(DWORD *)(buffer + 8) = 0;
IO_STATUS_BLOCK ios;
CliLockPipe();
NtWriteFile(hPipe, 0, 0, 0, &ios, buffer, HEADER_SIZE, 0, 0);
CliUnlockPipe();
}
}
} // unnamed namespace
DWORD GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name)
{
TreeNode<char *,FunctionInfo> *node = ::tree->Search(name);
if (node) {
if (addr) *addr = node->data.addr;
if (base) *base = node->data.module;
if (size) *size = node->data.size;
if (base_name) *base_name = node->data.name;
return TRUE;
}
else
return FALSE;
}
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID lpReserved)
{
static HANDLE hSendThread,
hCmdThread;
CC_UNUSED(lpReserved);
//static WCHAR dll_exist[] = L"ITH_DLL_RUNNING";
static WCHAR dll_exist[] = ITH_CLIENT_MUTEX;
static HANDLE hDllExist;
// jichi 9/23/2013: wine deficenciy on mapping sections
// Whe set to false, do not map sections.
//static bool ith_has_section = true;
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
{
static bool attached_ = false;
if (attached_) // already attached
return TRUE;
attached_ = true;
LdrDisableThreadCalloutsForDll(hModule);
//IthBreak();
::module_base = (DWORD)hModule;
//if (!IthInitSystemService()) {
// GROWL_WARN(L"Initialization failed.\nAre you running game on a network drive?");
// return FALSE;
//}
// No longer checking if SystemService fails, which could happen on non-Japanese OS
IthInitSystemService();
swprintf(hm_section, ITH_SECTION_ L"%d", current_process_id);
// jichi 9/25/2013: Interprocedural communication with vnrsrv.
hSection = IthCreateSection(hm_section, HOOK_SECTION_SIZE, PAGE_EXECUTE_READWRITE);
::hookman = nullptr;
NtMapViewOfSection(hSection, NtCurrentProcess(),
(LPVOID *)&::hookman, 0, hook_buff_len, 0, &hook_buff_len, ViewUnmap, 0,
PAGE_EXECUTE_READWRITE);
//PAGE_EXECUTE_READWRITE);
GetProcessName(::processName);
FillRange(::processName, &::processStartAddress, &::processStopAddress);
//NtInspect::getProcessMemoryRange(&::processStartAddress, &::processStopAddress);
//if (!::hookman) {
// ith_has_section = false;
// ::hookman = new TextHook[MAX_HOOK];
// memset(::hookman, 0, MAX_HOOK * sizeof(TextHook));
//}
{
wchar_t hm_mutex[0x100];
swprintf(hm_mutex, ITH_HOOKMAN_MUTEX_ L"%d", current_process_id);
::hmMutex = IthCreateMutex(hm_mutex, FALSE);
}
{
wchar_t dll_mutex[0x100];
swprintf(dll_mutex, ITH_PROCESS_MUTEX_ L"%d", current_process_id);
DWORD exists;
::hMutex = IthCreateMutex(dll_mutex, TRUE, &exists); // jichi 9/18/2013: own is true, make sure the injected dll is singleton
if (exists)
return FALSE;
}
hDllExist = IthCreateMutex(dll_exist, 0);
hDLL = hModule;
::running = true;
::current_available = ::hookman;
::tree = new AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN>;
AddAllModules();
InitFilterTable();
hSendThread = IthCreateThread(WaitForPipe, 0);
hCmdThread = IthCreateThread(CommandPipe, 0);
} break;
case DLL_PROCESS_DETACH:
{
static bool detached_ = false;
if (detached_) // already detached
return TRUE;
detached_ = true;
// jichi 10/2/2103: Cannot use __try in functions that require object unwinding
//ITH_TRY {
::running = false;
::live = false;
const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
Engine::terminate();
if (hSendThread) {
NtWaitForSingleObject(hSendThread, 0, (PLARGE_INTEGER)&timeout);
NtClose(hSendThread);
}
if (hCmdThread) {
NtWaitForSingleObject(hCmdThread, 0, (PLARGE_INTEGER)&timeout);
NtClose(hCmdThread);
}
for (TextHook *man = ::hookman; man->RemoveHook(); man++);
//LARGE_INTEGER lint = {-10000, -1};
while (::enter_count)
IthSleep(1); // jichi 9/28/2013: sleep for 1 ms
//NtDelayExecution(0, &lint);
for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++)
man->ClearHook();
//if (ith_has_section)
NtUnmapViewOfSection(NtCurrentProcess(), ::hookman);
//else
// delete[] ::hookman;
NtClose(hSection);
NtClose(hMutex);
delete ::tree;
IthCloseSystemService();
NtClose(hmMutex);
NtClose(hDllExist);
//} ITH_EXCEPT {}
} break;
}
return TRUE;
}
//extern "C" {
DWORD NewHook(const HookParam &hp, LPCSTR name, DWORD flag)
{
CHAR str[128];
int current = ::current_available - ::hookman;
if (current < MAX_HOOK) {
//flag &= 0xffff;
//if ((flag & HOOK_AUXILIARY) == 0)
flag |= HOOK_ADDITIONAL;
if (name == NULL || name[0] == '\0')
{
sprintf(str, "UserHook%d", user_hook_count++);
}
else
{
strcpy(str, name);
}
ConsoleOutput("vnrcli:NewHook: try inserting hook");
// jichi 7/13/2014: This function would raise when too many hooks added
::hookman[current].InitHook(hp, str, flag & 0xffff);
if (::hookman[current].InsertHook() == 0) {
ConsoleOutput("vnrcli:NewHook: hook inserted");
//ConsoleOutputW(name);
//swprintf(str,L"Insert address 0x%.8X.", hookman[current].Address());
RequestRefreshProfile();
} else
ConsoleOutput("vnrcli:NewHook:WARNING: failed to insert hook");
}
return 0;
}
DWORD RemoveHook(DWORD addr)
{
for (int i = 0; i < MAX_HOOK; i++)
if (::hookman[i].Address ()== addr) {
::hookman[i].ClearHook();
return 0;
}
return 0;
}
DWORD SwitchTrigger(DWORD t)
{
trigger = t;
return 0;
}
//} // extern "C"
namespace { // unnamed
BOOL SafeFillRange(LPCWSTR dll, DWORD *lower, DWORD *upper)
{
BOOL ret = FALSE;
ITH_WITH_SEH(ret = FillRange(dll, lower, upper));
return ret;
}
} // unnamed namespace
// jichi 12/13/2013
// Use listdlls from SystemInternals
void InitFilterTable()
{
LPCWSTR l[] = { IHF_FILTER_DLL_LIST };
enum { capacity = sizeof(l)/sizeof(*l) };
size_t count = 0;
//for (auto p : l)
for (size_t i = 0; i < capacity; i++)
if (SafeFillRange(l[i], &::filter[count].lower, &::filter[count].upper))
count++;
}
// EOF
/*
static DWORD recv_esp, recv_addr;
static CONTEXT recover_context;
static __declspec(naked) void MySEH()
{
__asm{
mov eax, [esp+0xC]
mov edi,eax
mov ecx,0xB3
mov esi, offset recover_context
rep movs
mov ecx, [recv_esp]
mov [eax+0xC4],ecx
mov edx, [recv_addr]
mov [eax+0xB8],edx
xor eax,eax
retn
}
}
EXCEPTION_DISPOSITION ExceptHandler(
EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
CONTEXT *ContextRecord,
void * DispatcherContext )
{
ContextRecord->Esp=recv_esp;
ContextRecord->Eip=recv_addr;
return ExceptionContinueExecution;
}
int GuardRange(LPWSTR module, DWORD *a, DWORD *b)
{
int flag=0;
__asm
{
mov eax,seh_recover
mov recv_addr,eax
push ExceptHandler
push fs:[0]
mov recv_esp,esp
mov fs:[0],esp
}
flag = FillRange(module, a, b);
__asm
{
seh_recover:
mov eax,[esp]
mov fs:[0],eax
add esp,8
}
return flag;
}
*/
#pragma once
// main.h
// 8/23/2013 jichi
// Branch: ITH/IHF_DLL.h, rev 66
#include "include/const.h"
#include "include/types.h"
void ConsoleOutput(LPCSTR text); // jichi 12/25/2013: Used to return length of sent text
DWORD NotifyHookInsert(DWORD addr);
DWORD NewHook(const HookParam &hp, LPCSTR name, DWORD flag = HOOK_ENGINE);
DWORD RemoveHook(DWORD addr);
DWORD SwitchTrigger(DWORD on);
DWORD GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name);
// 10/14/2014 jichi: disable GDI hooks
void EnableGDIHooks();
void EnableGDIPlusHooks();
void DisableGDIHooks();
void DisableGDIPlusHooks();
bool GDIHooksEnabled();
bool GDIPlusHooksEnabled();
// EOF
// pipe.cc
// 8/24/2013 jichi
// Branch: ITH_DLL/pipe.cpp, rev 66
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
#endif // _MSC_VER
#include "src/hijack/texthook.h"
#include "src/engine/match.h"
#include "src/util/util.h"
#include "src/main.h"
#include "include/defs.h"
//#include "src/util/growl.h"
#include "ithsys/ithsys.h"
#include "ccutil/ccmacro.h"
#include <cstdio> // for swprintf
//#include <ITH\AVL.h>
//#include <ITH\ntdll.h>
WCHAR detach_mutex[0x20];
//WCHAR write_event[0x20];
//WCHAR engine_event[0x20];
//WCHAR recv_pipe[] = L"\\??\\pipe\\ITH_PIPE";
//WCHAR command[] = L"\\??\\pipe\\ITH_COMMAND";
wchar_t recv_pipe[] = ITH_TEXT_PIPE;
wchar_t command[] = ITH_COMMAND_PIPE;
LARGE_INTEGER wait_time = {-100*10000, -1};
LARGE_INTEGER sleep_time = {-20*10000, -1};
DWORD engine_type;
DWORD module_base;
HANDLE hPipe,
hCommand,
hDetach; //,hLose;
//InsertHookFun InsertHook;
//IdentifyEngineFun IdentifyEngine;
//InsertDynamicHookFun InsertDynamicHook;
// jichi 9/28/2013: protect pipe on wine
// Put the definition in this file so that it might be inlined
void CliUnlockPipe()
{
if (IthIsWine())
IthReleaseMutex(::hmMutex);
}
void CliLockPipe()
{
if (IthIsWine()) {
const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout);
}
}
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;
}
DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter) // Dynamically detect ITH main module status.
{
CC_UNUSED(lpThreadParameter);
// jichi 7/2/2015:This must be consistent with the struct declared in vnrhost/pipe.cc
struct {
DWORD pid;
DWORD module;
TextHook *man;
//DWORD engine;
} u;
//swprintf(engine_event,L"ITH_ENGINE_%d",current_process_id);
swprintf(::detach_mutex, ITH_DETACH_MUTEX_ L"%d", current_process_id);
//swprintf(lose_event,L"ITH_LOSEPIPE_%d",current_process_id);
//hEngine=IthCreateEvent(engine_event);
//NtWaitForSingleObject(hEngine,0,0);
//NtClose(hEngine);
//while (!engine_registered)
// NtDelayExecution(0, &wait_time);
//LoadEngine(L"ITH_Engine.dll");
u.module = module_base;
u.pid = current_process_id;
u.man = hookman;
//u.engine = engine_base; // jichi 10/19/2014: disable the second dll
HANDLE hPipeExist = IthOpenEvent(ITH_PIPEEXISTS_EVENT);
IO_STATUS_BLOCK ios;
//hLose=IthCreateEvent(lose_event,0,0);
if (hPipeExist != INVALID_HANDLE_VALUE)
while (::running) {
::hPipe = INVALID_HANDLE_VALUE;
hCommand = INVALID_HANDLE_VALUE;
while (NtWaitForSingleObject(hPipeExist, 0, &wait_time) == WAIT_TIMEOUT)
if (!::running)
goto _release;
HANDLE hMutex = IthCreateMutex(ITH_GRANTPIPE_MUTEX, 0);
NtWaitForSingleObject(hMutex, 0, 0);
while (::hPipe == INVALID_HANDLE_VALUE||
hCommand == INVALID_HANDLE_VALUE) {
NtDelayExecution(0, &sleep_time);
if (::hPipe == INVALID_HANDLE_VALUE)
::hPipe = IthOpenPipe(recv_pipe, GENERIC_WRITE);
if (hCommand == INVALID_HANDLE_VALUE)
hCommand = IthOpenPipe(command, GENERIC_READ);
}
//NtClearEvent(hLose);
CliLockPipe();
NtWriteFile(::hPipe, 0, 0, 0, &ios, &u, sizeof(u), 0, 0);
CliUnlockPipe();
for (int i = 0, count = 0; count < ::current_hook; i++)
if (hookman[i].RecoverHook()) // jichi 9/27/2013: This is the place where built-in hooks like TextOutA are inserted
count++;
//ConsoleOutput(dll_name);
//OutputDWORD(tree->Count());
NtReleaseMutant(hMutex,0);
NtClose(hMutex);
::live = true;
// jichi 7/17/2014: Always hijack by default or I have to wait for it is ready
Engine::hijack();
ConsoleOutput("vnrcli:WaitForPipe: pipe connected");
::hDetach = IthCreateMutex(::detach_mutex,1);
while (::running && NtWaitForSingleObject(hPipeExist, 0, &sleep_time) == WAIT_OBJECT_0)
NtDelayExecution(0, &sleep_time);
::live = false;
for (int i = 0, count = 0; count < ::current_hook; i++)
if (hookman[i].RemoveHook())
count++;
if (!::running) {
IthCoolDown(); // jichi 9/28/2013: Use cooldown instead of lock pipe to prevent from hanging on exit
//CliLockPipe();
//NtWriteFile(::hPipe, 0, 0, 0, &ios, man, 4, 0, 0);
NtWriteFile(::hPipe, 0, 0, 0, &ios, hookman, 4, 0, 0);
//CliUnlockPipe();
IthReleaseMutex(::hDetach);
}
NtClose(::hDetach);
NtClose(::hPipe);
}
_release:
//NtClose(hLose);
NtClose(hPipeExist);
return 0;
}
DWORD WINAPI CommandPipe(LPVOID lpThreadParameter)
{
CC_UNUSED(lpThreadParameter);
DWORD command;
BYTE buff[0x400] = {};
HANDLE hPipeExist;
hPipeExist = IthOpenEvent(ITH_PIPEEXISTS_EVENT);
IO_STATUS_BLOCK ios={};
if (hPipeExist != INVALID_HANDLE_VALUE)
while (::running) {
while (!::live) {
if (!::running)
goto _detach;
NtDelayExecution(0, &sleep_time);
}
// jichi 9/27/2013: Why 0x200 not 0x400? wchar_t?
switch (NtReadFile(hCommand, 0, 0, 0, &ios, buff, 0x200, 0, 0)) {
case STATUS_PIPE_BROKEN:
case STATUS_PIPE_DISCONNECTED:
NtClearEvent(hPipeExist);
continue;
case STATUS_PENDING:
NtWaitForSingleObject(hCommand, 0, 0);
switch (ios.Status) {
case STATUS_PIPE_BROKEN:
case STATUS_PIPE_DISCONNECTED:
NtClearEvent(hPipeExist);
continue;
case 0: break;
default:
if (NtWaitForSingleObject(::hDetach, 0, &wait_time) == WAIT_OBJECT_0)
goto _detach;
}
}
if (ios.uInformation && ::live) {
command = *(DWORD *)buff;
switch(command) {
case HOST_COMMAND_NEW_HOOK:
//IthBreak();
buff[ios.uInformation] = 0;
//buff[ios.uInformation + 1] = 0;
NewHook(*(HookParam *)(buff + 4), (LPSTR)(buff + 4 + sizeof(HookParam)), 0);
break;
case HOST_COMMAND_REMOVE_HOOK:
{
DWORD rm_addr = *(DWORD *)(buff+4);
HANDLE hRemoved = IthOpenEvent(ITH_REMOVEHOOK_EVENT);
TextHook *in = hookman;
for (int i = 0; i < current_hook; in++) {
if (in->Address()) i++;
if (in->Address() == rm_addr) break;
}
if (in->Address())
in->ClearHook();
IthSetEvent(hRemoved);
NtClose(hRemoved);
} break;
#if 0 // Temporarily disabled as these operations are not used by VNR
case HOST_COMMAND_MODIFY_HOOK:
{
DWORD rm_addr = *(DWORD *)(buff + 4);
HANDLE hModify = IthOpenEvent(ITH_MODIFYHOOK_EVENT);
TextHook *in = hookman;
for (int i = 0; i < current_hook; in++) {
if (in->Address())
i++;
if (in->Address() == rm_addr)
break;
}
if (in->Address())
in->ModifyHook(*(HookParam *)(buff + 4));
IthSetEvent(hModify);
NtClose(hModify);
} break;
case HOST_COMMAND_HIJACK_PROCESS:
Engine::hijack();
break;
#endif // 0
case HOST_COMMAND_DETACH:
::running = false;
::live = false;
goto _detach;
}
}
}
_detach:
NtClose(hPipeExist);
NtClose(hCommand);
Util::unloadCurrentModule(); // jichi: this is not always needed
return 0;
}
//extern "C" {
void ConsoleOutput(LPCSTR text)
{ // jichi 12/25/2013: Rewrite the implementation
if (!live || !text)
return;
enum { buf_size = 0x50 };
BYTE buf[buf_size]; // buffer is needed to append the message header
size_t text_size = strlen(text) + 1;
size_t data_size = text_size + 8;
BYTE *data = (data_size <= buf_size) ? buf : new BYTE[data_size];
*(DWORD *)data = HOST_NOTIFICATION; //cmd
*(DWORD *)(data + 4) = HOST_NOTIFICATION_TEXT; //console
memcpy(data + 8, text, text_size);
IO_STATUS_BLOCK ios;
NtWriteFile(hPipe, 0, 0, 0, &ios, data, data_size, 0, 0);
if (data != buf)
delete[] data;
}
//if (str) {
// int t, len, sum;
// BYTE buffer[0x80];
// BYTE *buff;
// len = wcslen(str) << 1;
// t = swprintf((LPWSTR)(buffer + 8),L"%d: ",current_process_id) << 1;
// sum = len + t + 8;
// if (sum > 0x80) {
// buff = new BYTE[sum];
// memset(buff, 0, sum); // jichi 9/25/2013: zero memory
// memcpy(buff + 8, buffer + 8, t);
// }
// else
// buff = buffer;
// *(DWORD *)buff = HOST_NOTIFICATION; //cmd
// *(DWORD *)(buff + 4) = HOST_NOTIFICATION_TEXT; //console
// memcpy(buff + t + 8, str, len);
// IO_STATUS_BLOCK ios;
// NtWriteFile(hPipe,0,0,0,&ios,buff,sum,0,0);
// if (buff != buffer)
// delete[] buff;
// return len;
//}
//DWORD IOutputDWORD(DWORD d)
//{
// WCHAR str[0x10];
// swprintf(str,L"%.8X",d);
// ConsoleOutput(str);
// return 0;
//}
//DWORD IOutputRegister(DWORD *base)
//{
// WCHAR str[0x40];
// swprintf(str,L"EAX:%.8X",base[0]);
// ConsoleOutput(str);
// swprintf(str,L"ECX:%.8X",base[-1]);
// ConsoleOutput(str);
// swprintf(str,L"EDX:%.8X",base[-2]);
// ConsoleOutput(str);
// swprintf(str,L"EBX:%.8X",base[-3]);
// ConsoleOutput(str);
// swprintf(str,L"ESP:%.8X",base[-4]);
// ConsoleOutput(str);
// swprintf(str,L"EBP:%.8X",base[-5]);
// ConsoleOutput(str);
// swprintf(str,L"ESI:%.8X",base[-6]);
// ConsoleOutput(str);
// swprintf(str,L"EDI:%.8X",base[-7]);
// ConsoleOutput(str);
// return 0;
//}
//DWORD IRegisterEngineModule(DWORD idEngine, DWORD dnHook)
//{
// ::IdentifyEngine = (IdentifyEngineFun)idEngine;
// ::InsertDynamicHook = (InsertDynamicHookFun)dnHook;
// ::engine_registered = true;
// return 0;
//}
DWORD NotifyHookInsert(DWORD addr)
{
if (live) {
BYTE buffer[0x10];
*(DWORD *)buffer = HOST_NOTIFICATION;
*(DWORD *)(buffer + 4) = HOST_NOTIFICATION_NEWHOOK;
*(DWORD *)(buffer + 8) = addr;
*(DWORD *)(buffer + 0xc) = 0;
IO_STATUS_BLOCK ios;
CliLockPipe();
NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0);
CliUnlockPipe();
}
return 0;
}
//} // extern "C"
// EOF
#pragma once
// avl.h
// 8/23/2013 jichi
// Branch: ITH/AVL.h, rev 133
// 8/24/2013 TODO: Clean up this file
#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 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)); // jichi 9/26/2013: zero memory
::memset(&data, 0, sizeof(data)); // jichi 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
{
fComp fCmp;
fCopy fCpy;
fLength fLen;
protected:
TreeNode<T*, D> head;
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
#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
// util/util.cc
// 8/23/2013 jichi
// Branch: ITH_Engine/engine.cpp, revision 133
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
#include "src/util/util.h"
#include "ithsys/ithsys.h"
namespace { // unnamed
// jichi 4/19/2014: Return the integer that can mask the signature
DWORD SigMask(DWORD sig)
{
__asm
{
xor ecx,ecx
mov eax,sig
_mask:
shr eax,8
inc ecx
test eax,eax
jnz _mask
sub ecx,4
neg ecx
or eax,-1
shl ecx,3
shr eax,cl
}
}
} // namespace unnamed
// jichi 8/24/2013: binary search?
DWORD Util::GetCodeRange(DWORD hModule,DWORD *low, DWORD *high)
{
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
DWORD dwReadAddr;
IMAGE_SECTION_HEADER *shdr;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
dwReadAddr = hModule + DosHdr->e_lfanew;
NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr;
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
shdr = (PIMAGE_SECTION_HEADER)((DWORD)(&NtHdr->OptionalHeader) + NtHdr->FileHeader.SizeOfOptionalHeader);
while ((shdr->Characteristics & IMAGE_SCN_CNT_CODE) == 0)
shdr++;
*low = hModule + shdr->VirtualAddress;
*high = *low + (shdr->Misc.VirtualSize & 0xfffff000) + 0x1000;
}
}
return 0;
}
DWORD Util::FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig)
{
//WCHAR str[0x40];
enum { reverse_length = 0x800 };
DWORD t, l;
DWORD mask = SigMask(sig);
bool flag2;
for (DWORD i = 0x1000; i < size-4; i++) {
bool flag1 = false;
if (*(BYTE *)(pt + i) == 0xe8) {
flag1 = flag2 = true;
t = *(DWORD *)(pt + i + 1);
} else if (*(WORD *)(pt + i) == 0x15ff) {
flag1 = true;
flag2 = false;
t = *(DWORD *)(pt + i + 2);
}
if (flag1) {
if (flag2) {
flag1 = (pt + i + 5 + t == fun);
l = 5;
} else if (t >= pt && t <= pt + size - 4) {
flag1 = fun == *(DWORD *)t;
l = 6;
} else
flag1 = false;
if (flag1)
//swprintf(str,L"CALL addr: 0x%.8X",pt + i);
//OutputConsole(str);
for (DWORD j = i; j > i - reverse_length; j--)
if ((*(WORD *)(pt + j)) == (sig & mask)) //Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",pt + j);
//OutputConsole(str);
return pt + j;
else
i += l;
}
}
//OutputConsole(L"Find call and entry failed.");
return 0;
}
DWORD Util::FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp)
{
BYTE sig = (jmp) ? 0xe9 : 0xe8;
for (DWORD i = 0x1000; i < size - 4; i++)
if (sig == *(BYTE *)(pt + i)) {
DWORD t = *(DWORD *)(pt + i + 1);
if(fun == pt + i + 5 + t)
//OutputDWORD(pt + i);
return pt + i;
else
i += 5;
}
return 0;
}
DWORD Util::FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp)
{
WORD sig = jmp ? 0x25ff : 0x15ff;
for (DWORD i = 0x1000; i < size - 4; i++)
if (sig == *(WORD *)(pt + i)) {
DWORD t = *(DWORD *)(pt + i + 2);
if (t > pt && t < pt + size) {
if (fun == *(DWORD *)t)
return pt + i;
else
i += 5;
}
}
return 0;
}
DWORD Util::FindCallBoth(DWORD fun, DWORD size, DWORD pt)
{
for (DWORD i = 0x1000; i < size - 4; i++) {
if (*(BYTE *)(pt + i) == 0xe8) {
DWORD t = *(DWORD *)(pt + i + 1) + pt + i + 5;
if (t == fun)
return i;
}
if (*(WORD *)(pt + i) == 0x15ff) {
DWORD t = *(DWORD *)(pt + i + 2);
if (t >= pt && t <= pt + size - 4) {
if (*(DWORD *)t == fun)
return i;
else
i += 6;
}
}
}
return 0;
}
DWORD Util::FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig)
{
//WCHAR str[0x40];
enum { reverse_length = 0x800 };
DWORD mask = SigMask(sig);
for (DWORD i = 0x1000; i < size - 4; i++)
if (*(WORD *)(pt + i) == 0x15ff) {
DWORD t = *(DWORD *)(pt + i + 2);
if (t >= pt && t <= pt + size - 4) {
if (*(DWORD *)t == fun)
//swprintf(str,L"CALL addr: 0x%.8X",pt + i);
//OutputConsole(str);
for (DWORD j = i ; j > i - reverse_length; j--)
if ((*(DWORD *)(pt + j) & mask) == sig) // Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",pt + j);
//OutputConsole(str);
return pt + j;
} else
i += 6;
}
//OutputConsole(L"Find call and entry failed.");
return 0;
}
DWORD Util::FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig)
{
//WCHAR str[0x40];
enum { reverse_length = 0x800 };
if (DWORD i = FindCallOrJmpRel(fun, size, pt, false)) {
DWORD mask = SigMask(sig);
for (DWORD j = i; j > i - reverse_length; j--)
if (((*(DWORD *)j) & mask) == sig) //Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",j);
//OutputConsole(str);
return j;
//OutputConsole(L"Find call and entry failed.");
}
return 0;
}
DWORD Util::FindEntryAligned(DWORD start, DWORD back_range)
{
start &= ~0xf;
for (DWORD i = start, j = start - back_range; i > j; i-=0x10) {
DWORD k = *(DWORD *)(i-4);
if (k == 0xcccccccc
|| k == 0x90909090
|| k == 0xccccccc3
|| k == 0x909090c3
)
return i;
DWORD t = k & 0xff0000ff;
if (t == 0xcc0000c2 || t == 0x900000c2)
return i;
k >>= 8;
if (k == 0xccccc3 || k == 0x9090c3)
return i;
t = k & 0xff;
if (t == 0xc2)
return i;
k >>= 8;
if (k == 0xccc3 || k == 0x90c3)
return i;
k >>= 8;
if (k == 0xc3)
return i;
}
return 0;
}
DWORD Util::FindImportEntry(DWORD hModule, DWORD fun)
{
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
DWORD IAT, end, pt, addr;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
IAT = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
end = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
IAT += hModule;
end += IAT;
for (pt = IAT; pt < end; pt += 4) {
addr = *(DWORD *)pt;
if (addr == fun)
return pt;
}
}
}
return 0;
}
// Search string in rsrc section. This section usually contains version and copyright info.
bool Util::SearchResourceString(LPCWSTR str)
{
DWORD hModule = Util::GetModuleBase();
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
DWORD rsrc, size;
//__asm int 3
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
rsrc = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
if (rsrc) {
rsrc += hModule;
if (IthGetMemoryRange((LPVOID)rsrc, &rsrc ,&size) &&
SearchPattern(rsrc, size - 4, str, wcslen(str) << 1))
return true;
}
}
}
return false;
}
// jichi 4/15/2014: Copied from GetModuleBase in ITH CLI, for debugging purpose
DWORD Util::FindModuleBase(DWORD hash)
{
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov esi,[eax+0x14]
mov edi,_wcslwr
listfind:
mov edx,[esi+0x28]
test edx,edx
jz notfound
push edx
call edi
pop edx
xor eax,eax
calc:
movzx ecx, word ptr [edx]
test cl,cl
jz fin
ror eax,7
add eax,ecx
add edx,2
jmp calc
fin:
cmp eax,[hash]
je found
mov esi,[esi]
jmp listfind
notfound:
xor eax,eax
jmp termin
found:
mov eax,[esi+0x10]
termin:
}
}
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
// See: http://stackoverflow.com/questions/3410130/dll-unloading-itself
bool Util::unloadCurrentModule()
{
auto fun = ::FreeLibrary;
//auto fun = ::LdrUnloadDll;
if (HANDLE h = ::IthCreateThread(fun, (DWORD)&__ImageBase)) {
//const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
//NtWaitForSingleObject(h, 0, (PLARGE_INTEGER)&timeout);
NtClose(h);
return true;
}
// CreateThread does not always work on Windows XP. Use IthCreateThread (i.e. CreateRemoteThread under the water) instead.
//if (HANDLE h = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)fun, &__ImageBase, 0, NULL)) {
// ::CloseHandle(h);
// return true;
//}
return false;
}
// EOF
#pragma once
// util.h
// 8/23/2013 jichi
#include "ntdll/ntdll.h"
namespace Util {
bool unloadCurrentModule();
DWORD GetCodeRange(DWORD hModule,DWORD *low, DWORD *high);
DWORD FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig);
DWORD FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp);
DWORD FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp);
DWORD FindCallBoth(DWORD fun, DWORD size, DWORD pt);
DWORD FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig);
DWORD FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig);
DWORD FindEntryAligned(DWORD start, DWORD back_range);
DWORD FindImportEntry(DWORD hModule, DWORD fun);
// jichi 4/15/2014: Copied from ITH CLI, for debugging purpose
DWORD FindModuleBase(DWORD hash);
bool SearchResourceString(LPCWSTR str);
/**
* @param name process name without path deliminator
*/
inline void GetProcessName(wchar_t *name)
{
//assert(name);
PLDR_DATA_TABLE_ENTRY it;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
}
::wcscpy(name, it->BaseDllName.Buffer);
}
/**
* @param path with process name and directy name
*/
inline void GetProcessPath(wchar_t *path)
{
//assert(path);
PLDR_DATA_TABLE_ENTRY it;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
}
::wcscpy(path, it->FullDllName.Buffer);
}
/**
* @return HANDLE module handle
*/
inline DWORD GetModuleBase()
{
__asm
{
mov eax,fs:[0x18]
mov eax,[eax+0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov eax,[eax+0x18]
}
}
} // namespace Util
// EOF
# vnrhook.pri
# 8/21/2013 jichi
DEFINES += WITH_LIB_VNRHOOK
DEPENDPATH += $$PWD/include
HEADERS += \
$$PWD/include/const.h \
$$PWD/include/defs.h \
$$PWD/include/types.h
# EOF
# hook.pro
# 8/9/2013 jichi
# Build vnrhook.dll for Windows 7+
# Exception handler to catch all exceptions
CONFIG += dll noqt eh eha # noeh nosafeseh
#CONFIG += noeh # msvcrt on Windows XP does not has exception handler
include(../../../config.pri)
include($$PLUGINDIR/ithsys/ithsys.pri)
include($$LIBDIR/disasm/disasm.pri)
include($$LIBDIR/memdbg/memdbg.pri)
include($$LIBDIR/ntdll/ntdll.pri)
include($$LIBDIR/ntinspect/ntinspect.pri)
include($$LIBDIR/winkey/winkey.pri)
#include($$LIBDIR/winseh/winseh_safe.pri)
include($$LIBDIR/winversion/winversion.pri)
# 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 += ITH_HAS_CRT ITH_HAS_SEH
DEFINES += MEMDBG_NO_STL NTINSPECT_NO_STL # disabled as not used
# 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 += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
#LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
LIBS += -lkernel32 -luser32 -lgdi32 #-lgdiplus
## Sources
TEMPLATE = lib
TARGET = vnrhook
#CONFIG += staticlib
HEADERS += \
include/const.h \
include/defs.h \
include/types.h \
src/except.h \
src/main.h \
src/util/growl.h \
src/util/util.h \
src/tree/avl.h \
src/hijack/texthook.h \
src/engine/engine.h \
src/engine/hookdefs.h \
src/engine/match.h \
src/engine/pchooks.h \
src/engine/mono/funcinfo.h \
src/engine/ppsspp/funcinfo.h
SOURCES += \
src/main.cc \
src/pipe.cc \
src/util/util.cc \
src/hijack/texthook.cc \
src/engine/engine.cc \
src/engine/match.cc \
src/engine/pchooks.cc
#RC_FILE += vnrhook.rc
#OTHER_FILES += vnrhook.rc
OTHER_FILES += vnrhook.pri
# EOF
// hijack.cc
// 1/27/2013 jichi
#include "windbg/hijack.h"
#include "windbg/windbg_p.h"
#ifdef _MSC_VER
# pragma warning (disable:4996) // C4996: use POSIX function (stricmp)
#endif // _MSC_VER
//#define DEBUG "winsec"
#include "sakurakit/skdebug.h"
WINDBG_BEGIN_NAMESPACE
// - Inline Hook -
// See: http://asdf.wkeya.com/code/apihook6.html
PVOID overrideFunctionA(HMODULE stealFrom, LPCSTR oldFunctionModule, LPCSTR functionName, LPCVOID newFunction)
{
if (!stealFrom)
return nullptr;
//HMODULE oldModule = GetModuleHandleA(oldFunctionModule);
//if (!oldModule)
// return nullptr;
//void *originalAddress = GetProcAddress(oldModule, functionName);
LPVOID originalAddress = details::getModuleFunctionAddressA(functionName, oldFunctionModule);
if (!originalAddress)
return nullptr;
IMAGE_DOS_HEADER *dosHeader = reinterpret_cast<IMAGE_DOS_HEADER *>(stealFrom);
char *base = reinterpret_cast<char *>(stealFrom);
if (::IsBadReadPtr(dosHeader, sizeof(IMAGE_DOS_HEADER)) || dosHeader->e_magic != IMAGE_DOS_SIGNATURE)
return nullptr;
IMAGE_NT_HEADERS *ntHeader =
reinterpret_cast<IMAGE_NT_HEADERS* >(base + dosHeader->e_lfanew);
if (::IsBadReadPtr(ntHeader, sizeof(IMAGE_NT_HEADERS)) || ntHeader->Signature != IMAGE_NT_SIGNATURE)
return nullptr;
if (!ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress)
return nullptr;
// See: http://msdn.microsoft.com/en-us/magazine/cc301808.aspx
IMAGE_IMPORT_DESCRIPTOR *import =
reinterpret_cast<IMAGE_IMPORT_DESCRIPTOR *>(base + ntHeader->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
// scan memory
// TODO: add a maximum loop counter here!
while (import->Name) {
char *name = base + import->Name;
if (!::stricmp(name, oldFunctionModule))
break;
import++;
}
if (!import->Name)
return nullptr;
IMAGE_THUNK_DATA *thunk = reinterpret_cast<IMAGE_THUNK_DATA *>(base + import->FirstThunk);
while (thunk->u1.Function) {
if ((ULONG_PTR)thunk->u1.Function == (ULONG_PTR)originalAddress) {
ULONG_PTR *addr = reinterpret_cast<ULONG_PTR *>(&thunk->u1.Function);
// See: http://asdf.wkeya.com/code/apihook6.html
// Inline hook mechanism:
//
// LPVOID InlineHook3( PUINT8 mem, DWORD dwLen, PUINT8 pfOld, PUINT8 pfNew )
// {
// DWORD dwOldProtect;
// VirtualProtect( ( PUINT8 )( pfOld ), dwLen, PAGE_READWRITE, &dwOldProtect );
// // 関数のエントリーから指定したbyte数をメモリの前方にコピー
// // メモリの数byte後方からオリジナルへのジャンプを作成
// // 指定の関数アドレスから5byteをフックへのjmp命令に書き換え
// VirtualProtect( ( PUINT8 )( pfOld ), dwLen, dwOldProtect, &dwOldProtect );
// return ( PVOID )mem;
// }
MEMORY_BASIC_INFORMATION mbi;
if (::VirtualQuery((LPVOID)addr, &mbi, sizeof(mbi)) == sizeof(mbi)) {
DWORD dwOldProtect;
if (::VirtualProtect(mbi.BaseAddress, ((ULONG_PTR)addr + 8)-(ULONG_PTR)mbi.BaseAddress, PAGE_EXECUTE_READWRITE, &dwOldProtect)) {
*addr = (ULONG_PTR)newFunction;
::VirtualProtect(mbi.BaseAddress, ((ULONG_PTR)addr + 8)-(ULONG_PTR)mbi.BaseAddress, dwOldProtect, &dwOldProtect);
return originalAddress;
}
}
}
thunk++;
}
return nullptr;
}
WINDBG_END_NAMESPACE
// EOF
#pragma once
// hijack.h
// 1/27/2013 jichi
#include "windbg/windbg.h"
#include <windows.h>
WINDBG_BEGIN_NAMESPACE
/**
* Replace the named function entry with the new one.
* @param stealFrom instance of target module
* @param oldFunctionModule TODO
* @param functionName name of the target function
* @return the orignal address if succeed, else nullptr
*
* See: http://www.codeproject.com/KB/DLL/DLL_Injection_tutorial.aspx
*/
PVOID overrideFunctionA(_In_ HMODULE stealFrom, _In_ LPCSTR oldFunctionModule,
_In_ LPCSTR functionName, _In_ LPCVOID newFunction);
WINDBG_END_NAMESPACE
// EOF
// inject.cc
// 1/27/2013 jichi
#include "windbg/inject.h"
#include "windbg/windbg_p.h"
#include <cwchar> // for wcslen
//#define DEBUG "windbg::inject"
#include "sakurakit/skdebug.h"
WINDBG_BEGIN_NAMESPACE
// - Remote Injection -
BOOL InjectFunction1(LPCVOID addr, LPCVOID data, SIZE_T dataSize, DWORD pid, HANDLE hProcess, INT timeout)
{
DOUT("enter: pid =" << pid);
if (hProcess == INVALID_HANDLE_VALUE && pid) {
hProcess = ::OpenProcess(PROCESS_INJECT_ACCESS, FALSE, pid);
// TODO: Privilege elevation is not implemented. See: skwinsec.py.
//if (!hProcess) {
// priv = SkProcessElevator('SeDebugPrivilege')
// if not priv.isEmpty():
// handle = win32api.OpenProcess(PROCESS_INJECT_ACCESS, 0, pid)
//}
}
if (hProcess == INVALID_HANDLE_VALUE) {
DOUT("exit: error: failed to get process handle");
return FALSE;
}
BOOL ret = FALSE;
if (LPVOID remoteData = ::VirtualAllocEx(hProcess, nullptr, dataSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE)) {
if (::WriteProcessMemory(hProcess, remoteData, data, dataSize, nullptr))
if (HANDLE hThread = ::CreateRemoteThread(
hProcess,
nullptr, 0,
reinterpret_cast<LPTHREAD_START_ROUTINE>(addr),
remoteData,
0, nullptr)) {
::WaitForSingleObject(hThread, timeout);
::CloseHandle(hThread);
ret = TRUE;
}
::VirtualFreeEx(hProcess, remoteData, dataSize, MEM_RELEASE);
}
::CloseHandle(hProcess);
DOUT("exit: ret =" << ret);
return ret;
}
BOOL injectDllW(LPCWSTR dllPath, DWORD pid, HANDLE hProcess, INT timeout)
{
DOUT("enter: pid =" << pid);
LPCVOID fun = details::getModuleFunctionAddressA("LoadLibraryW", "kernel32.dll");
if (!fun) {
DOUT("exit error: cannot find function");
return FALSE;
}
LPCVOID data = dllPath;
SIZE_T dataSize = ::wcslen(dllPath) * 2 + 2; // L'\0'
BOOL ok = InjectFunction1(fun, data, dataSize, pid, hProcess, timeout);
DOUT("exit: ret =" << ok);
return ok;
}
BOOL ejectDll(HANDLE hDll, DWORD pid, HANDLE hProcess, INT timeout)
{
DOUT("enter: pid =" << pid);
LPCVOID fun = details::getModuleFunctionAddressA("FreeLibrary", "kernel32.dll");
if (!fun) {
DOUT("exit error: cannot find function");
return FALSE;
}
LPCVOID data = &hDll;
SIZE_T dataSize = sizeof(hDll);
BOOL ok = InjectFunction1(fun, data, dataSize, pid, hProcess, timeout);
DOUT("exit: ret =" << ok);
return ok;
}
WINDBG_END_NAMESPACE
// EOF
/*
enum { CREATE_THREAD_ACCESS = (PROCESS_CREATE_THREAD |
PROCESS_QUERY_INFORMATION |
PROCESS_VM_OPERATION |
PROCESS_VM_WRITE |
PROCESS_VM_READ ) };
int InjectDll(HANDLE hProcess, HINSTANCE hInst) {
HANDLE hThread;
wchar_t dllFile[2*MAX_PATH];
if (GetModuleFileNameW(hInst, dllFile, sizeof(dllFile)/2) > sizeof(dllFile)/2) return 0;
void *loadLibraryW = GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "LoadLibraryW");
if (!loadLibraryW) return 0;
wchar_t *name;
if (!(name = wcsrchr(dllFile, '\\'))) return 0;
name ++;
wcscpy(name, DLL_NAME);
if (GetFileAttributes(dllFile) == INVALID_FILE_ATTRIBUTES) return 0;
size_t len = sizeof(wchar_t)*(1+wcslen(dllFile));
void *remoteString = (LPVOID)VirtualAllocEx(hProcess,
NULL,
len,
MEM_RESERVE|MEM_COMMIT,
PAGE_READWRITE
);
if (remoteString) {
if (WriteProcessMemory(hProcess, (LPVOID)remoteString, dllFile, len, NULL)) {
if (hThread = CreateRemoteThread(hProcess, 0, 0, (LPTHREAD_START_ROUTINE)loadLibraryW, (LPVOID)remoteString, 0,0)) {
WaitForSingleObject(hThread, 3000);
CloseHandle(hThread);
VirtualFreeEx(hProcess, remoteString, len, MEM_FREE);
// Make sure it's injected before resuming.
return 1;
}
}
VirtualFreeEx(hProcess, remoteString, len, MEM_FREE);
}
return 0;
}
int getPriv(const char * name) {
HANDLE hToken;
LUID seValue;
TOKEN_PRIVILEGES tkp;
if (!LookupPrivilegeValueA(NULL, name, &seValue) ||
!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken)) {
return 0;
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Luid = seValue;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
int res = AdjustTokenPrivileges(hToken, 0, &tkp, sizeof(tkp), NULL, NULL);
CloseHandle(hToken);
return res;
}
inline int getDebugPriv() {
return getPriv("SeDebugPrivilege");
}
int InjectIntoProcess(int pid) {
HANDLE hProcess = OpenProcess(CREATE_THREAD_ACCESS, 0, pid);
if (hProcess == 0) {
getDebugPriv();
hProcess = OpenProcess(CREATE_THREAD_ACCESS, 0, pid);
if (!hProcess) return 0;
}
int out = InjectDll(hProcess);
CloseHandle(hProcess);
return out;
}
*/
#pragma once
// inject.h
// 1/27/2013 jichi
#include "windbg/windbg.h"
#include <windows.h>
WINDBG_BEGIN_NAMESPACE
enum { PROCESS_INJECT_ACCESS = PROCESS_CREATE_THREAD|PROCESS_QUERY_INFORMATION|PROCESS_VM_OPERATION|PROCESS_VM_WRITE|PROCESS_VM_READ };
enum { INJECT_TIMEOUT = 3000 }; // wait at most 3 seconds for creating remote threads
/**
* Inject function with 1 argument
* Either pid or the process handle should be specified
* @param addr LONG function memory address
* @param arg LPVOID arg1 data
* @param argSize int arg1 data size
* @param pid process id
* @param hProcess process handle
* @param timeout msec
* @return BOOL
*/
BOOL injectFunction1(_In_ LPCVOID addr, _In_ LPCVOID arg, _In_ SIZE_T argSize,
_In_ DWORD pid = 0, _In_ HANDLE hProcess = INVALID_HANDLE_VALUE,
_In_ INT timeout = INJECT_TIMEOUT);
/**
* Either pid or the process handle should be specified
* @param dllpath ABSOLUTE path to dll
* @param pid process id
* @param hProcess process handle
* @param timeout msec
* @return BOOL
*/
BOOL injectDllW(_In_ LPCWSTR dllPath,
_In_ DWORD pid = 0, _In_ HANDLE hProcess = INVALID_HANDLE_VALUE,
_In_ INT timeout = INJECT_TIMEOUT);
/**
* Either pid or the process handle should be specified
* @param hDll dll module handle
* @param pid process id
* @param hProcess process handle
* @param timeout msec
* @return BOOL
*/
BOOL ejectDll(_In_ HANDLE hDll,
_In_ DWORD pid = 0, _In_ HANDLE hProcess = INVALID_HANDLE_VALUE,
_In_ INT timeout = INJECT_TIMEOUT);
WINDBG_END_NAMESPACE
// EOF
// unload.cc
// 5/2/2014 jichi
#include "windbg/unload.h"
WINDBG_BEGIN_NAMESPACE
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
// See: http://stackoverflow.com/questions/3410130/dll-unloading-itself
BOOL unloadCurrentModule()
{
auto fun = ::FreeLibrary;
//auto fun = ::LdrUnloadDll;
if (HANDLE h = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)fun, &__ImageBase, 0, NULL)) {
::CloseHandle(h);
return TRUE;
}
return FALSE;
}
WINDBG_END_NAMESPACE
// EOF
#pragma once
// unload.h
// 5/2/2014 jichi
#include "windbg/windbg.h"
#include <windows.h>
WINDBG_BEGIN_NAMESPACE
/**
* Unload current injected DLL.
* @return BOOL
*/
BOOL unloadCurrentModule();
WINDBG_END_NAMESPACE
// EOF
// windbg/util.cc
// 1/27/2013 jichi
#include "windbg/util.h"
#include <tlhelp32.h>
#include <boost/foreach.hpp>
#include <list>
WINDBG_BEGIN_NAMESPACE
class ThreadsSuspenderPrivate
{
public:
std::list<HANDLE> threads;
};
ThreadsSuspender::ThreadsSuspender(bool autoSuspend)
: d_(new D)
{ if (autoSuspend) suspend(); }
ThreadsSuspender::~ThreadsSuspender()
{
resume();
delete d_;
}
void ThreadsSuspender::suspend()
{
HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnap == INVALID_HANDLE_VALUE)
return;
THREADENTRY32 entry;
entry.dwSize = sizeof(entry);
DWORD pid = ::GetCurrentProcessId();
DWORD tid = ::GetCurrentThreadId();
if (::Thread32First(hSnap, &entry))
do if (entry.dwSize >= 4 * sizeof(DWORD) && entry.th32OwnerProcessID == pid && entry.th32ThreadID != tid) {
if (HANDLE hThread = ::OpenThread(THREAD_SUSPEND_RESUME, 0, entry.th32ThreadID)) {
if (::SuspendThread(hThread) != DWORD(-1))
d_->threads.push_back(hThread);
else
::CloseHandle(hThread);
}
entry.dwSize = sizeof(entry);
} while (::Thread32Next(hSnap, &entry));
::CloseHandle(hSnap);
}
void ThreadsSuspender::resume()
{
if (!d_->threads.empty()) {
BOOST_FOREACH (HANDLE hThread, d_->threads) {
::ResumeThread(hThread);
::CloseHandle(hThread);
}
d_->threads.clear();
}
}
WINDBG_END_NAMESPACE
// EOF
#pragma once
// windbg/util.h
// 1/27/2013 jichi
#include "windbg/windbg.h"
#include "sakurakit/skglobal.h"
#include <windows.h>
WINDBG_BEGIN_NAMESPACE
class ThreadsSuspenderPrivate;
/**
* When created, automatically suspends all threads in the current process.
* When destroyed, resume suspended threads.
*/
class ThreadsSuspender
{
SK_CLASS(ThreadsSuspender)
SK_DISABLE_COPY(ThreadsSuspender)
SK_DECLARE_PRIVATE(ThreadsSuspenderPrivate)
public:
explicit ThreadsSuspender(bool autoSuspend = true);
~ThreadsSuspender();
void resume(); ///< Manually resume all threads
void suspend(); ///< Manually suspend all threads
};
WINDBG_END_NAMESPACE
// EOF
#pragma once
// windbg.h
// 1/27/2013 jichi
#ifndef WINDBG_BEGIN_NAMESPACE
# define WINDBG_BEGIN_NAMESPACE namespace WinDbg {
#endif
#ifndef WINDBG_END_NAMESPACE
# define WINDBG_END_NAMESPACE } // namespace WinDbg
#endif
WINDBG_BEGIN_NAMESPACE
WINDBG_END_NAMESPACE
// EOF
# windbg.pri
# 4/21/2014 jichi
win32 {
DEFINES += WITH_LIB_WINDBG
LIBS += -lkernel32 -luser32
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/hijack.h \
$$PWD/inject.h \
$$PWD/util.h \
$$PWD/unload.h \
$$PWD/windbg_p.h \
$$PWD/windbg.h
SOURCES += \
$$PWD/hijack.cc \
$$PWD/inject.cc \
$$PWD/util.cc \
$$PWD/unload.cc
}
# EOF
#pragma once
// windbg_p.h
// 1/27/2013 jichi
#include "windbg/windbg.h"
#include <windows.h>
WINDBG_BEGIN_NAMESPACE
namespace details { // unnamed
/// Return the address of func in module.
inline FARPROC getModuleFunctionAddressA(LPCSTR func, LPCSTR module = nullptr)
{ return ::GetProcAddress(::GetModuleHandleA(module), func); }
inline FARPROC getModuleFunctionAddressW(LPCSTR func, LPCWSTR module = nullptr)
{ return ::GetProcAddress(::GetModuleHandleW(module), func); }
} // unamed namespace details
WINDBG_END_NAMESPACE
// EOF
#pragma once
// winkey.h
// 7/21/2011
#include <windows.h>
#ifndef WINKEY_BEGIN_NAMESPACE
# define WINKEY_BEGIN_NAMESPACE namespace WinKey {
#endif
#ifndef WINKEY_END_NAMESPACE
# define WINKEY_END_NAMESPACE } // namespace WinKey
#endif
WINKEY_BEGIN_NAMESPACE
inline bool isKeyPressed(int vk) { return ::GetKeyState(vk) & 0xf0; }
inline bool isKeyToggled(int vk) { return ::GetKeyState(vk) & 0x0f; }
inline bool isKeyReturnPressed() { return isKeyPressed(VK_RETURN); }
inline bool isKeyControlPressed() { return isKeyPressed(VK_CONTROL); }
inline bool isKeyShiftPressed() { return isKeyPressed(VK_SHIFT); }
inline bool isKeyAltPressed() { return isKeyPressed(VK_MENU); }
//inline bool sKeyCapslockToggled() { return isKeyToggled(VK_CAPITAL); }
inline bool isKeyWinPressed() { return isKeyPressed(VK_LWIN) || isKeyPressed(VK_RWIN); }
inline bool isMouseLeftButtonPressed() { return isKeyPressed(VK_LBUTTON); }
inline bool isMouseMiddleButtonPressed() { return isKeyPressed(VK_MBUTTON); }
inline bool isMouseRightButtonPressed() { return isKeyPressed(VK_RBUTTON); }
WINKEY_END_NAMESPACE
# winkey.pri
# 7/20/2011 jichi
win32 {
DEFINES += WITH_LIB_WINKEY
LIBS += -luser32
DEPENDPATH += $$PWD
HEADERS += $$PWD/winkey.h
#SOURCES += $$PWD/winkey.cc
}
# EOF
......@@ -31,7 +31,7 @@ template <typename _Mutex>
native_handle_type native_handle() { return _M_mutex.native_handle(); }
void unlock() { _M_mutex.unlock(); _M_locked = false; }
void lock() { _M_mutex.lock(); _M_locked = true; }
bool tryLock() { return _M_locked = _M_mutex.tryLock(); }
bool try_lock() { return _M_locked = _M_mutex.try_lock(); }
};
// Mutex
......
......@@ -3,7 +3,7 @@
# This file is for Windows only.
# Compile SAFESEH table from the ASM file.
# See: http://stackoverflow.com/questions/19722308/exception-handler-not-called-in-c
# See: ::http://stackoverflow.com/questions/12019689/custom-seh-handler-with-safeseh
# See: http://stackoverflow.com/questions/12019689/custom-seh-handler-with-safeseh
# See: http://msdn.microsoft.com/en-us/library/16aexws6.aspx
BUILDDIR = ../../../build
......
; safeseh.asm
; 12/13/2013 jichi
; 12/13/2013 jichi
; see: http://stackoverflow.com/questions/12019689/custom-seh-handler-with-safeseh
; see: http://code.metager.de/source/xref/WebKit/Source/WebCore/platform/win/makesafeseh.asm
; see: http://jpassing.com/2008/05/20/fun-with-low-level-seh/
......
......@@ -5,7 +5,7 @@
#include "ntdll/ntdll.h"
//#include <cstdio>
// - Global variables -
// - Define global variables -
seh_dword_t seh_esp[seh_capacity],
seh_eip[seh_capacity],
......@@ -29,16 +29,16 @@ seh_dword_t seh_count;
// EXCEPTION_DISPOSITION
// NTAPI
// EXCEPTION_ROUTINE (
// _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
// _In_ PVOID EstablisherFrame,
// _In_ struct _CONTEXT *ContextRecord,
// _In_ PVOID DispatcherContext
// );
// _Inout_ struct _EXCEPTION_RECORD *ExceptionRecord,
// _In_ PVOID EstablisherFrame,
// _In_ struct _CONTEXT *ContextRecord,
// _In_ PVOID DispatcherContext
// );
extern "C" EXCEPTION_DISPOSITION _seh_handler( // extern C is needed to avoid name hashing in C++
_In_ PEXCEPTION_RECORD ExceptionRecord,
_In_ PVOID EstablisherFrame, // does not work if I use ULONG64
_In_ PVOID EstablisherFrame, // do not work if I use ULONG64
_Inout_ PCONTEXT ContextRecord,
_In_ PVOID DispatcherContext) // PDISPATCHER_CONTEXT is not declared in windows.h
_In_ PVOID DispatcherContext) // PDISPATCHER_CONTEXT is not declared in windows.h, use PVOID instead
{
//assert(::seh_count > 0);
ContextRecord->Esp = ::seh_esp[::seh_count - 1];
......
......@@ -23,11 +23,13 @@ typedef unsigned long seh_dword_t; // DWORD in <windows.h>
// 12/13/2013 jichi
// The list implementation is not thread-safe
extern seh_dword_t seh_esp[seh_capacity], // LPVOID, current stack
seh_eip[seh_capacity], // LPVOID, current IP address
seh_eh[seh_capacity]; // EXCEPTION_ROUTINE, current exception handler function address
extern seh_dword_t seh_count; // current number of exception handlers
extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler;
extern seh_dword_t
seh_count // current number of exception handlers
, seh_handler // extern PEXCEPTION_ROUTINE seh_handler;
, seh_esp[seh_capacity] // LPVOID, current stack
, seh_eip[seh_capacity] // LPVOID, current IP address
, seh_eh[seh_capacity] // EXCEPTION_ROUTINE, current exception handler function address
;
/**
* Push SEH handler
......@@ -49,21 +51,22 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler;
*
* EPB and ESP
* http://stackoverflow.com/questions/1395591/what-is-exactly-the-base-pointer-and-stack-pointer-to-what-do-they-point
*
* TODO: get sizeof dword instead of hardcode 4
*/
#define seh_push_(_label, _eh, _r1, _r2) \
{ \
__asm mov _r1, _eh /* move new handler address */ \
__asm mov _r2, seh_count /* get current seh counter */ \
__asm mov dword ptr seh_eh[_r2*4], _r1 /* set recover exception hander */ \
__asm mov _r1, _label /* move jump label address */ \
__asm mov _r1, _eh /* move new handler address */ \
__asm mov _r2, seh_count /* get current seh counter */ \
__asm mov dword ptr seh_eh[_r2*4], _r1 /* set recover exception hander */ \
__asm mov _r1, _label /* move jump label address */ \
__asm mov dword ptr seh_eip[_r2*4], _r1 /* set recover eip as the jump label */ \
__asm push seh_handler /* push new safe seh handler */ \
__asm push fs:[0] /* push old fs:0 */ \
__asm push seh_handler /* push new safe seh handler */ \
__asm push fs:[0] /* push old fs:0 */ \
__asm mov dword ptr seh_esp[_r2*4], esp /* safe current stack address */ \
__asm mov fs:[0], esp /* change fs:0 to the current stack */ \
__asm inc seh_count /* increase number of seh */ \
__asm mov fs:[0], esp /* change fs:0 to the current stack */ \
__asm inc seh_count /* increase number of seh */ \
}
//TODO: get sizeof dword instead of hardcode 4
/**
* Restore old SEH handler
......@@ -71,12 +74,13 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler;
*/
#define seh_pop_(_label) \
{ \
__asm _label: /* the exception recover label */ \
__asm pop dword ptr fs:[0] /* restore old fs:0 */ \
__asm add esp, 4 /* pop seh_handler */ \
__asm dec seh_count /* decrease number of seh */ \
__asm _label: /* the exception recover label */ \
__asm pop dword ptr fs:[0] /* restore old fs:0 */ \
__asm add esp, 4 /* pop seh_handler */ \
__asm dec seh_count /* decrease number of seh */ \
}
// Define seh_exit as the shared exit label
#define seh_pop() seh_pop_(seh_exit)
#define seh_push() seh_push_(seh_exit, 0, eax, ecx) // use ecx as counter better than ebx
......@@ -93,7 +97,7 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler;
{ \
seh_push() \
__VA_ARGS__ \
; \
; /* allow __VA_ARGS__ to be an expression */ \
seh_pop() \
}
......@@ -106,7 +110,7 @@ extern seh_dword_t seh_handler; //extern PEXCEPTION_ROUTINE seh_handler;
{ \
seh_push_eh(_eh) \
__VA_ARGS__ \
; \
; /* allow __VA_ARGS__ to be an expression */ \
seh_pop() \
}
......
// wintimer.cc
// 6/6/2012 jichi
#include "wintimer/wintimer.h"
//#define DEBUG "wintimer.cc"
#include "sakurakit/skdebug.h"
#include <windows.h>
WINTIMER_BEGIN_NAMESPACE
void WinTimer::singleShot(int msecs, const function_type &f, WId parent)
{
Self *t = new Self(parent);
t->setInterval(msecs);
t->setSingleShot(true);
t->setFunction([=] { // Copy function f instead of pass by reference.
f();
delete t;
});
t->start();
}
WINTIMER_END_NAMESPACE
// EOF
/*
// - Single shot -
namespace { // unnamed
class apply_delete
{
typedef WinTimer::function_type function_type;
function_type f_;
WinTimer *t_;
public:
apply_delete(const function_type &f, WinTimer *t)
: f_(f), t_(t) { Q_ASSERT(t); }
void operator()()
{
f_();
delete t_;
}
};
} // unnamed namespace
*/
#pragma once
// wintimer.h
// 6/6/2012 jichi
//
// A light-weighted native windows timer as a replacement of QTimer from Qt.
// Implementation is based on Windows Messaging. A visible parent hwnd is required.
//
// This timer is critical where QTimer or event loop are not available, or need to
// warp to different event loop. Some usage cases follow:
// - Used by texthook as a replacement of QTimer in non-QThread
// - Used by qapplicationloader to implement pseudo event loop
// - Used by winhook to synchronize with window event loop across threads
#include "wintimer/wintimerbase.h"
#include <boost/bind.hpp>
/**
* @brief A light-weighted native windows timer as a replacement of QTimer.
*
* Needed when in a thread where event loop is not accessible.
* Implemented using extensive inlining over pimp, so that the entire class
* could be put on the stack without heap.
*
* Each timer requires an valid visible window's handle to synchronize with.
* Either specify the window handle with the parent window or a global window.
*/
class WinTimer : protected WinTimerBase
{
SK_EXTEND_CLASS(WinTimer, WinTimerBase)
SK_DISABLE_COPY(WinTimer)
// - Construction -
public:
//typedef std::function<void ()> function_type;
using Base::function_type; ///< std::function<void ()>
/// Default parent window of all timers.
static WId globalWindow() { return Base::globalWindow; }
static void setGlobalWindow(WId winId) { Base::globalWindow = winId; }
//static WId createHiddenWindow();
public:
/// Construct a timer with the parent window handle.
explicit WinTimer(WId parentWindow = 0) { setParentWindow(parentWindow); }
static void singleShot(int msecs, const function_type &f, WId parent = 0);
// - Properties -
public:
using Base::isActive;
using Base::isSingleShot;
void setSingleShot(bool t) { Base::singleShot = t; }
//bool isEmpty() const { return Base::function.empty(); }
WId parentWindow() const { return Base::parentWindow; }
void setParentWindow(WId winId) { Base::parentWindow = winId ? winId : Base::globalWindow; }
int interval() const { return Base::interval; }
void setInterval(int msecs) { Base::interval = msecs; }
/// Timeout callback when trigger.
void setFunction(const function_type &f) { Base::function = f; }
/// @overload Set callback to a class method
template <typename Class, typename Member>
void setMethod(Class *obj, Member mfunc)
{ setFunction(boost::bind(mfunc, obj)); }
/// @overload Set callback to a const class method
template <typename Class, typename Member>
void setMethod(const Class *obj, Member mfunc)
{ setFunction(boost::bind(mfunc, obj)); }
// - Actions -
public:
/// Start TimerProc
using Base::start;
/// Stop TimerProc
using Base::stop;
/// Reset interval and start TimerProc
void start(int interval) { setInterval(interval); start(); }
/// Invoke the callback. This function is the callback of the underlying TimerProc
using Base::trigger;
};
WINTIMER_END_NAMESPACE
# wintimer.pri
# 7/20/2011 jichi
win32 {
DEFINES += WITH_LIB_WINTIMER
LIBS += -lkernel32 -luser32 -lwintimer
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/wintimer.h \
$$PWD/wintimerbase.h
#SOURCES += \
# $$PWD/wintimer.cc \
# $$PWD/wintimerbase.cc
}
# EOF
# sys.pro
# 8/21/2013 jichi
# Build ITH_engine.dll
#CONFIG += noqt noeh staticlib
CONFIG += staticlib
include(../../../config.pri)
## Sources
TEMPLATE = lib
TARGET = wintimer
HEADERS += \
wintimer.h \
wintimerbase.h
SOURCES += \
wintimer.cc \
wintimerbase.cc
# EOF
// wintimerbase.cc
// 6/6/2012 jichi
#include "wintimer/wintimerbase.h"
#ifdef QT_CORE_LIB
# include <qt_windows.h>
#else
# include <windows.h>
#endif // QT_CORE_LIB
#include "ccutil/ccmacro.h"
//#define DEBUG "wintimerbase.cc"
#include "sakurakit/skdebug.h"
static VOID CALLBACK WinTimerProc(
HWND hwnd, // ウィンドウのハンドル
UINT uMsg, // WM_TIMER メッセージ
UINT_PTR idEvent, // Timer ID
DWORD dwTime // 現在のシステム時刻
)
{
Q_UNUSED(hwnd)
Q_UNUSED(dwTime)
Q_UNUSED(uMsg)
Q_ASSERT(idEvent);
if (CC_UNLIKELY(!idEvent))
return;
DOUT("enter");
WinTimerBase *t = reinterpret_cast<WinTimerBase *>(idEvent);
if (t->isSingleShot() && t->isActive())
t->stop();
t->trigger();
DOUT("leave");
}
WINTIMER_BEGIN_NAMESPACE
// - Construction -
WId WinTimerBase::globalWindow;
//WId WinTimer::createHiddenWindow()
//{
// DOUT("enter: warning: hidden window used");
// QWidget *w = new QWidget;
// w->resize(QSize());
// w->show();
// DOUT("leave");
// return w->winId();
//}
// - Timer -
void WinTimerBase::start()
{
DOUT("enter: active =" << active << ", interval =" << interval);
active = true;
::SetTimer(parentWindow, reinterpret_cast<UINT_PTR>(this), interval, WinTimerProc);
DOUT("leave");
}
void WinTimerBase::stop()
{
DOUT("enter: active =" << active);
active = false;
::KillTimer(parentWindow, reinterpret_cast<UINT_PTR>(this));
DOUT("leave");
}
WINTIMER_END_NAMESPACE
// EOF
#pragma once
// wintimerbase.h
// 6/6/2012 jichi
//
// Internal header for wintimer base class.
#include "sakurakit/skglobal.h"
#include <functional>
#ifdef QT_CORE_LIB
# include <QtGui/qwindowdefs.h>
#else
# include <windows.h>
#endif // QT_CORE_LIB
#ifndef WINTIMER_BEGIN_NAMESPACE
# define WINTIMER_BEGIN_NAMESPACE
#endif
#ifndef WINTIMER_END_NAMESPACE
# define WINTIMER_END_NAMESPACE
#endif
WINTIMER_BEGIN_NAMESPACE
/// Internal base class for WinTimer
class WinTimerBase
{
SK_CLASS(WinTimerBase)
SK_DISABLE_COPY(WinTimerBase)
// - Types -
public:
typedef std::function<void ()> function_type;
#ifndef QT_CORE_LIB
typedef HWND WId;
#endif // QT_CORE_LIB
// - Methods -
public:
/// Construct a timer with the parent window handle.
WinTimerBase()
: parentWindow(0), // use 0 instead of nullptr to be consistent with Qt5
interval(0), singleShot(false), active(false) {}
bool isSingleShot() const { return singleShot; }
bool isActive() const { return active; }
/// Start TimerProc
void start();
/// Stop TimerProc
void stop();
/// Invoke the callback. This function is the callback of the underlying TimerProc
void trigger() { function(); }
// - Fields -
protected:
static WId globalWindow;
WId parentWindow;
int interval;
bool singleShot;
bool active;
function_type function;
};
WINTIMER_END_NAMESPACE