update to 3.5640.1

update to ITHVNR 3.5640.1
and translation
This commit is contained in:
mireado 2016-01-06 00:01:17 +09:00
parent b21c882e1f
commit 90613f5039
140 changed files with 51080 additions and 1125 deletions

View File

@ -1,55 +1,3 @@
# common.pri
# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
# config.pri
# win32 {
# DEFINES += _SECURE_SCL=0 _SCL_SECURE_NO_WARNINGS
# DEFINES += _CRT_SECURE_NO_WARNINGS
# QMAKE_CXXFLAGS += -wd4819
# }
# config.pri
# win32 {
# CONFIG(nocrt) {
# message(CONFIG nocrt)
# QMAKE_CFLAGS -= /MD /MDd
# QMAKE_CFLAGS_DEBUG -= /MD /MDd
# QMAKE_CFLAGS_RELEASE -= /MD /MDd
# QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO -= /MD /MDd
# QMAKE_CXXFLAGS -= /MD /MDd
# QMAKE_CXXFLAGS_DEBUG -= /MD /MDd
# QMAKE_CXXFLAGS_RELEASE -= /MD /MDd
# QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= /MD /MDd
# }
# CONFIG(eha) {
# message(CONFIG eha)
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
# QMAKE_CXXFLAGS_STL_ON += /EHa
# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
# }
# CONFIG(noeh) {
# message(CONFIG noeh)
# QMAKE_CXXFLAGS += /GR-
# QMAKE_CXXFLAGS_RTTI_ON -= /GR
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
# CONFIG(dll) {
# QMAKE_LFLAGS += /ENTRY:"DllMain"
# }
# }
# CONFIG(nosafeseh) {
# message(CONFIG nosafeseh)
# QMAKE_LFLAGS += -safeseh:no
# }
# }
# dllconfig.pri
# win32 {
# CONFIG(eh): DEFINES += ITH_HAS_SEH
# CONFIG(noeh): DEFINES -= ITH_HAS_SEH
# }
cmake_minimum_required(VERSION 2.8)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
@ -66,10 +14,11 @@ set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release")
set(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION ON)
set(CPACK_GENERATOR "ZIP")
set(CPACK_PACKAGE_VERSION_MAJOR 3)
set(CPACK_PACKAGE_VERSION_MINOR 4152)
set(CPACK_PACKAGE_VERSION_PATCH 0)
set(CPACK_PACKAGE_VERSION_MINOR 5640)
set(CPACK_PACKAGE_VERSION_PATCH 1)
set(CPACK_SOURCE_GENERATOR "ZIP")
set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\.svn/;/\\\\.bzr/;/\\\\.hg/;/\\\\.git/;\\\\.swp$;\\\\.#;/#" ".*\\\\.user$" "\\\\.gitignore$" "\\\\.gitmodules$" "\\\\.git$")
include(CPack)
@ -84,24 +33,20 @@ add_compile_options(
)
add_definitions(
-D_SECURE_SCL=0 # config.pri
-D_SCL_SECURE_NO_WARNINGS # config.pri
-D_CRT_SECURE_NO_WARNINGS # config.pri
-DUNICODE # config.pri
-D_UNICODE
-D_CRT_NON_CONFORMING_SWPRINTFS # common.pri
-DITH_HAS_CRT
/D_SECURE_SCL=0 # config.pri
/D_SCL_SECURE_NO_WARNINGS # config.pri
/D_CRT_SECURE_NO_WARNINGS # config.pri
/DUNICODE # config.pri
/D_UNICODE
/D_CRT_NON_CONFORMING_SWPRINTFS # common.pri
/DITH_HAS_CRT
)
include_directories(${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/vnr ${CMAKE_BINARY_DIR}/gui)
set(common_src
vnr/ith/common/const.h
vnr/ith/common/defs.h
vnr/ith/common/except.h
vnr/ith/common/growl.h
vnr/ith/common/memory.h
vnr/ith/common/types.h
include_directories(
.
vnr
vnr/texthook
${CMAKE_BINARY_DIR}/gui
)
set(resource_src
@ -119,13 +64,8 @@ set(ithvnr_src
gui/main.cpp
gui/ProcessWindow.cpp
gui/ProcessWindow.h
gui/Profile.cpp
gui/Profile.h
gui/ProfileManager.cpp
gui/ProfileManager.h
gui/pugiconfig.hpp
gui/pugixml.cpp
gui/pugixml.hpp
gui/resource.h
gui/utility.cpp
gui/utility.h
@ -135,17 +75,15 @@ set(ithvnr_src
gui/window.h
gui/TextBuffer.cpp
gui/TextBuffer.h
${common_src}
${resource_src}
)
source_group("common" FILES ${common_src})
source_group("Resource Files" FILES ${resource_src})
add_executable(${PROJECT_NAME} ${ithvnr_src})
add_subdirectory(vnr)
# add_subdirectory(profile)
set_target_properties(${PROJECT_NAME} PROPERTIES
LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFESTDEPENDENCY:\"type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\""
@ -158,8 +96,9 @@ target_compile_definitions(${PROJECT_NAME}
)
target_link_libraries(${PROJECT_NAME}
profile
vnrhost
vnrsys
ithsys
${WDK_HOME}/lib/wxp/i386/ntdll.lib
comctl32.lib
psapi.lib

View File

@ -16,6 +16,7 @@
*/
#pragma once
#include "ITH.h"
typedef void (*CustomFilterCallBack) (WORD, PVOID);

View File

@ -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)

View File

@ -1,5 +1,5 @@
// Generated by ResEdit 1.6.3
// Copyright (C) 2006-2014
// Generated by ResEdit 1.6.5
// Copyright (C) 2006-2015
// http://www.resedit.net
#include <windows.h>
@ -7,9 +7,6 @@
#include <richedit.h>
#include "resource.h"
#define WC_LISTVIEW L"SysListView32"
@ -22,14 +19,14 @@ STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYS
CAPTION "Process Explorer"
FONT 8, "MS Shell Dlg", 400, 0, 1
{
DEFPUSHBUTTON "확인", IDOK, 281, 189, 53, 14, 0, WS_EX_LEFT
PUSHBUTTON "프로필 제거", IDC_BUTTON6, 226, 189, 53, 14, 0, WS_EX_LEFT
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
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
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
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
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
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
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
{
}

View File

@ -1,9 +1,9 @@
#include "ProcessWindow.h"
#include "resource.h"
#include "ith/host/srv.h"
#include "ith/host/hookman.h"
#include "host/host.h"
#include "host/hookman.h"
#include "ProfileManager.h"
#include "Profile.h"
#include "profile/Profile.h"
extern HookManager* man; // main.cpp
extern ProfileManager* pfman; // ProfileManager.cpp
@ -22,6 +22,8 @@ ProcessWindow::ProcessWindow(HWND hDialog) : hDlg(hDialog)
ListView_SetExtendedListViewStyleEx(hlProcess, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
InitProcessDlg();
RefreshProcess();
EnableWindow(hbDetach, FALSE);
EnableWindow(hbAttach, FALSE);
}
void ProcessWindow::InitProcessDlg()
@ -73,30 +75,33 @@ void ProcessWindow::RefreshProcess()
void ProcessWindow::AttachProcess()
{
DWORD pid = GetSelectedPID();
if (IHF_InjectByPID(pid) != -1)
if (Host_InjectByPID(pid))
{
Host_HijackProcess(pid);
RefreshThreadWithPID(pid, true);
}
}
void ProcessWindow::DetachProcess()
{
DWORD pid = GetSelectedPID();
if (IHF_ActiveDetachProcess(pid) == 0)
if (Host_ActiveDetachProcess(pid) == 0)
RefreshThreadWithPID(pid, false);
}
void ProcessWindow::AddCurrentToProfile()
void ProcessWindow::CreateProfileForSelectedProcess()
{
DWORD pid = GetSelectedPID();
auto path = GetProcessPath(pid);
if (!path.empty())
{
Profile* pf = pfman->AddProfile(path, pid);
pfman->FindProfileAndUpdateHookAddresses(pid, path);
Profile* pf = pfman->CreateProfile(pid);
pfman->SaveProfiles();
RefreshThread(ListView_GetSelectionMark(hlProcess));
}
}
void ProcessWindow::RemoveCurrentFromProfile()
void ProcessWindow::DeleteProfileForSelectedProcess()
{
DWORD pid = GetSelectedPID();
auto path = GetProcessPath(pid);

View File

@ -1,4 +1,5 @@
#pragma once
#include "ITH.h"
class ProcessWindow
@ -9,8 +10,8 @@ public:
void RefreshProcess();
void AttachProcess();
void DetachProcess();
void AddCurrentToProfile();
void RemoveCurrentFromProfile();
void CreateProfileForSelectedProcess();
void DeleteProfileForSelectedProcess();
void RefreshThread(int index);
private:
void RefreshThreadWithPID(DWORD pid, bool isAttached);

View File

@ -1,9 +1,11 @@
#include "ProfileManager.h"
#include "Profile.h"
#include "ith/host/srv.h"
#include "ith/host/hookman.h"
#include "ith/common/types.h"
#include "ith/common/const.h"
#include "profile/Profile.h"
#include "host/host.h"
#include "host/hookman.h"
#include "vnrhook/include/types.h"
#include "vnrhook/include/const.h"
#include "utility.h"
#include "profile/misc.h"
extern HookManager* man; // main.cpp
extern LONG auto_inject, auto_insert, inject_delay; // main.cpp
@ -12,21 +14,16 @@ bool MonitorFlag;
ProfileManager* pfman;
DWORD WINAPI MonitorThread(LPVOID lpThreadParameter);
void AddHooksToProfile(Profile& pf, const ProcessRecord& pr);
void AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid);
DWORD AddThreadToProfile(Profile& pf, const ProcessRecord& pr, TextThread& thread);
void MakeHookRelative(const ProcessRecord& pr, HookParam& hp);
std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address);
void GetHookNameToAddressMap(const ProcessRecord& pr, std::map<std::wstring, DWORD>& hookNameToAddress);
ProfileManager::ProfileManager() :
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));
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;
}
@ -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;
}

View File

@ -1,4 +1,5 @@
#pragma once
#include "ITH.h"
#include "utility.h" // UniqueHandle, CriticalSection
@ -9,14 +10,14 @@ class ProfileManager
public:
ProfileManager();
~ProfileManager();
Profile* AddProfile(const std::wstring& path, DWORD pid);
void DeleteProfile(const std::wstring& path);
void LoadProfile();
void SaveProfile();
void FindProfileAndUpdateHookAddresses(DWORD pid, const std::wstring& path);
bool HasProfile(const std::wstring& path);
Profile* CreateProfile(DWORD pid);
Profile* GetProfile(DWORD pid);
DWORD ProfileCount();
Profile* GetProfile(const std::wstring& path);
void LoadProfiles();
void SaveProfiles();
void DeleteProfile(const std::wstring& path);
void UpdateHookAddresses(DWORD pid);
bool HasProfile(const std::wstring& path);
private:
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;

View File

@ -1,4 +1,5 @@
#pragma once
#include "ITH.h"
#include "utility.h" // UniqueHandle, CriticalSection

View File

@ -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)))
{
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;
}

View File

@ -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\
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* 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* 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* 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* 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* NoFile = L"파일을 찾을 수 없음.";
const wchar_t* PathDismatch = L"프로세스 이름이 일치하지 않습니다, 계속하시겠습니까?";
const wchar_t* SuccessImportProfile = L"프로필 가져오기 성공";
//const wchar_t* SuccessAddProfile=L"Profile added.";

View 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,7 +39,7 @@ 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;
@ -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;
@ -240,15 +242,16 @@ LONG WINAPI UnhandledExcept(_EXCEPTION_POINTERS *ExceptionInfo)
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);
}

View File

@ -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
@ -69,7 +70,7 @@ std::wstring GetWindowsPath(const std::wstring& path)
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;
}

View File

@ -1,10 +1,10 @@
#pragma once
#include "ITH.h"
struct HookParam;
struct ProcessRecord;
DWORD Hash(const std::wstring& module, int length = -1);
DWORD ProcessCommand(const std::wstring& cmd, DWORD pid);
std::wstring GetProcessPath(DWORD pid);
void ConsoleOutput(LPCWSTR);
@ -58,13 +58,6 @@ int MB_WC_count(const char* mb, int mb_length);
int WC_MB(const wchar_t *wc, char* mb, int mb_length);
bool Parse(const std::wstring& cmd, HookParam& hp);
template <typename T>
std::wstring ToHexString(T i) {
std::wstringstream ss;
ss << std::uppercase << std::hex << i;
return ss.str();
}
// http://jrdodds.blogs.com/blog/2004/08/raii_in_c.html
class CriticalSection
{

View File

@ -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,7 +47,7 @@ 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];
@ -55,7 +56,6 @@ 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
static int last_select, last_edit;
void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const TextThread& thread);
ATOM MyRegisterClass(HINSTANCE hInstance)
{
@ -137,8 +137,8 @@ BOOL CALLBACK OptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
clipboard_flag = IsDlgButtonChecked(hDlg, IDC_CHECK3);
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,10 +187,10 @@ 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;
}
}
@ -280,19 +280,19 @@ 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 |
@ -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,7 +388,7 @@ 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);
}
}
}
@ -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)
if ((*lp)->FromIndex() == thread_index)
{
auto thread_profile = pf->FindThreadProfile(*tp);
if (thread_profile != pf->Threads().end())
{
(*thread_profile)->HookManagerIndex() = thread->Number();
auto thread_profile_index = thread_profile - pf->Threads().begin();
AddLinksToHookManager(*pf, thread_profile_index, *thread);
if (pf->SelectedIndex() == thread_profile_index)
ThreadReset(thread);
}
}
return 0;
}
void AddLinksToHookManager(const Profile& pf, size_t thread_profile_index, const TextThread& thread)
{
for (auto lp = pf.Links().begin(); lp != pf.Links().end(); ++lp)
{
if ((*lp)->FromIndex() == thread_profile_index)
{
WORD to_index = pf.Threads()[(*lp)->ToIndex()]->HookManagerIndex();
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;
}
@ -728,7 +730,11 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
CBS_SORT | WS_VSCROLL | WS_TABSTOP,
0, 0, 0, 0, hWnd, 0, hIns, NULL);
{
HFONT hf = CreateFont(18, 0, 0, 0, FW_LIGHT, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, ANTIALIASED_QUALITY, 0,
HDC hDC = GetDC(hWnd);
int nHeight = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72);
ReleaseDC(hWnd, hDC);
HFONT hf = CreateFont(nHeight, 0, 0, 0, FW_LIGHT, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET,
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
L"MS Gothic");
hWhiteBrush = GetStockBrush(WHITE_BRUSH);
SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0);
@ -747,7 +753,7 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
man->RegisterProcessNewHookCallback(RefreshProfileOnNewHook);
man->RegisterAddRemoveLinkCallback(AddRemoveLink);
man->RegisterConsoleCallback(ConsoleOutput);
IHF_Start();
Host_Start();
{
static const WCHAR program_name[] = L"Interactive Text Hooker";
//static const WCHAR program_version[] = L"3.0";
@ -759,8 +765,6 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
if (background == 0)
man->AddConsoleOutput(BackgroundMsg);
if (!IHF_IsAdmin())
man->AddConsoleOutput(NotAdmin);
}
return 0;

View File

@ -16,4 +16,5 @@
*/
#pragma once
#include "ITH.h"

View File

@ -1,33 +1,29 @@
# config.pri
# DEFINES += _SECURE_SCL=0 _SCL_SECURE_NO_WARNINGS
# DEFINES += _CRT_SECURE_NO_WARNINGS
cmake_minimum_required(VERSION 2.8)
set(CMAKE_CONFIGURATION_TYPES Debug Release)
project(vnr)
set(WDK_HOME "C:\\WinDDK\\7600.16385.1" CACHE FILEPATH "path to the Windows DDK directory")
set(WDK_HOME "C:\\WinDDK\\7600.16385.1" CACHE FILEPATH "Windows Driver Kit path")
add_definitions(
-DUNICODE
-D_UNICODE
/DUNICODE
/D_UNICODE
/D_SECURE_SCL=0
/D_SCL_SECURE_NO_WARNINGS
/D_CRT_SECURE_NO_WARNINGS
)
include_directories(${PROJECT_SOURCE_DIR})
set(common_src
${PROJECT_SOURCE_DIR}/ith/common/const.h
${PROJECT_SOURCE_DIR}/ith/common/defs.h
${PROJECT_SOURCE_DIR}/ith/common/except.h
${PROJECT_SOURCE_DIR}/ith/common/growl.h
${PROJECT_SOURCE_DIR}/ith/common/memory.h
${PROJECT_SOURCE_DIR}/ith/common/types.h
include_directories(
${PROJECT_SOURCE_DIR}
${PROJECT_SOURCE_DIR}/texthook
)
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
)
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)

View File

@ -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

View File

@ -1,20 +1,28 @@
@echo off
setlocal
if [%1] == [] (
echo usage: copy_vnr <path-to-Sakura-directory>
echo usage: copy_vnr path_to_Sakura
goto :EOF
)
xcopy %1\config.pri . /S /Y /I
xcopy %1\cpp\libs\ccutil ccutil /S /Y /I
xcopy %1\cpp\libs\cpputil cpputil /S /Y /I
xcopy %1\cpp\libs\disasm disasm /S /Y /I /EXCLUDE:exclude.txt
xcopy %1\cpp\plugins\ith ith /S /Y /I
xcopy %1\cpp\libs\hashutil hashutil /S /Y /I
xcopy %1\cpp\plugins\ithsys ithsys /S /Y /I
xcopy %1\cpp\plugins\vnrhook vnrhook /S /Y /I
xcopy %1\cpp\plugins\texthook texthook /S /Y /I /EXCLUDE:exclude.txt
xcopy %1\cpp\libs\memdbg memdbg /S /Y /I
xcopy %1\cpp\libs\ntdll ntdll /S /Y /I
xcopy %1\cpp\libs\ntinspect ntinspect /S /Y /I
xcopy %1\cpp\libs\winkey winkey /S /Y /I
xcopy %1\cpp\libs\winmaker winmaker /S /Y /I
xcopy %1\cpp\libs\winmutex winmutex /S /Y /I
xcopy %1\cpp\libs\winversion winversion /S /Y /I
xcopy %1\cpp\libs\winseh winseh /S /Y /I
xcopy %1\cpp\libs\wintimer wintimer /S /Y /I
xcopy %1\cpp\libs\windbg windbg /S /Y /I
xcopy %1\cpp\libs\sakurakit sakurakit /S /Y /I
xcopy %1\cpp\libs\mono mono /S /Y /I
endlocal

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -4,7 +4,7 @@
// Include typedef of BYTE
//#include <windef.h>
#include <windows.h>
//#include <windows.h>
//#ifdef QT_CORE_LIB
//# include <qt_windows.h>
@ -20,7 +20,13 @@
#endif
DISASM_BEGIN_NAMESPACE
int disasm(const BYTE *opcode0); // return: op length if success, 0 if error
/**
* This function can do more, but currently only used to estimate the length of an instruction.
* Warning: The current implementation is stateful and hence not thread-safe.
* @param address of the instruction to look at
* @return length of the instruction at the address or 0 if failed
*/
size_t disasm(const void *address);
DISASM_END_NAMESPACE
// EOF

55
vnr/hashutil/hashstr.h Normal file
View File

@ -0,0 +1,55 @@
#ifndef HASHSTR_H
#define HASHSTR_H
// hashstr.h
// 8/1/2011
// See: http://www.cse.yorku.ca/~oz/hash.html
#include "hashutil/hashutil.h"
#include <cstdint>
HASHUTIL_BEGIN_NAMESPACE
enum : uint64_t { djb2_hash0 = 5381 };
/// djb2: h = h*33 + c
template <typename charT>
inline uint64_t djb2(const charT *str, uint64_t hash = djb2_hash0)
{
charT c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; // hash * 33 + c
return hash;
}
/// n: length
template <typename charT>
inline uint64_t djb2_n(const charT *str, size_t len, uint64_t hash = djb2_hash0)
{
while (len--)
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
return hash;
}
/// sdbm: hash(i) = hash(i - 1) * 65599 + str[i];
template <typename charT>
inline uint64_t sdbm(const charT *str, uint64_t hash = 0)
{
charT c;
while ((c = *str++))
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
}
template <typename charT>
inline uint64_t loselose(const charT *str, uint64_t hash = 0)
{
charT c;
while ((c = *str++))
hash += c;
return hash;
}
HASHUTIL_END_NAMESPACE
#endif // HASHSTR_H

15
vnr/hashutil/hashutil.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef HASHUTIL_H
#define HASHUTIL_H
// hashutil.h
// 6/16/2015 jichi
// Redefine HASHUTIL_BEGIN_NAMESPACE/HASHUTIL_END_NAMESPACE if need custom namespace
#ifndef HASHUTIL_BEGIN_NAMESPACE
# define HASHUTIL_BEGIN_NAMESPACE namespace hashutil {
#endif
#ifndef HASHUTIL_END_NAMESPACE
# define HASHUTIL_END_NAMESPACE } // namespace hashutil
#endif
#endif // HASHUTIL_H

11
vnr/hashutil/hashutil.pri Normal file
View File

@ -0,0 +1,11 @@
# hashutil.pri
# 6/28/2011 jichi
DEFINES += WITH_LIB_HASHUTIL
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/hashstr.h \
$$PWD/hashutil.h
# EOF

28
vnr/ithsys/CMakeLists.txt Normal file
View File

@ -0,0 +1,28 @@
# # ithsys.pro
# CONFIG += noqt staticlib
# include(../../../config.pri)
# # jichi 7/12/2015: Always enable SEH
# DEFINES += ITH_HAS_SEH
# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
set(ithsys_src
ithsys.h
ithsys.cc
)
add_library(ithsys STATIC ${ithsys_src})
target_compile_options(ithsys PRIVATE
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
target_link_libraries(ithsys comctl32.lib)
target_compile_definitions(ithsys
PRIVATE
ITH_HAS_SEH
_CRT_NON_CONFORMING_SWPRINTFS
)

1549
vnr/ithsys/ithsys.cc Normal file

File diff suppressed because it is too large Load Diff

132
vnr/ithsys/ithsys.h Normal file
View File

@ -0,0 +1,132 @@
#pragma once
// ithsys.h
// 8/23/2013 jichi
// Branch: ITH/IHF_SYS.h, rev 111
#ifdef _MSC_VER
# pragma warning(disable:4800) // C4800: forcing value to bool
#endif // _MSC_VER
#include "ntdll/ntdll.h"
// jichi 8/24/2013: Why extern "C"? Any specific reason to use C instead of C++ naming?
extern "C" {
//int disasm(BYTE *opcode0); // jichi 8/15/2013: move disasm to separate file
extern WORD *NlsAnsiCodePage;
int FillRange(LPCWSTR name,DWORD *lower, DWORD *upper);
int MB_WC(char *mb, wchar_t *wc);
//int MB_WC_count(char *mb, int mb_length);
int WC_MB(wchar_t *wc, char *mb);
// jichi 10/1/2013: Return 0 if failed. So, it is ambiguous if the search pattern starts at 0
DWORD SearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length); // KMP
// jichi 2/5/2014: The same as SearchPattern except it uses 0xff to match everything
// According to @Andys, 0xff seldom appear in the source code: http://sakuradite.com/topic/124
enum : BYTE { SP_ANY = 0xff };
#define SP_ANY_2 SP_ANY,SP_ANY
#define SP_ANY_3 SP_ANY,SP_ANY,SP_ANY
#define SP_ANY_4 SP_ANY,SP_ANY,SP_ANY,SP_ANY
DWORD SearchPatternEx(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length, BYTE wildcard=SP_ANY);
BOOL IthInitSystemService();
void IthCloseSystemService();
DWORD IthGetMemoryRange(LPCVOID mem, DWORD *base, DWORD *size);
BOOL IthCheckFile(LPCWSTR file);
BOOL IthFindFile(LPCWSTR file);
BOOL IthGetFileInfo(LPCWSTR file, LPVOID info, DWORD size = 0x1000);
BOOL IthCheckFileFullPath(LPCWSTR file);
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition);
HANDLE IthCreateFileInDirectory(LPCWSTR name, HANDLE dir, DWORD option, DWORD share, DWORD disposition);
HANDLE IthCreateDirectory(LPCWSTR name);
HANDLE IthCreateFileFullPath(LPCWSTR fullpath, DWORD option, DWORD share, DWORD disposition);
HANDLE IthPromptCreateFile(DWORD option, DWORD share, DWORD disposition);
HANDLE IthCreateSection(LPCWSTR name, DWORD size, DWORD right);
HANDLE IthCreateEvent(LPCWSTR name, DWORD auto_reset=0, DWORD init_state=0);
HANDLE IthOpenEvent(LPCWSTR name);
void IthSetEvent(HANDLE hEvent);
void IthResetEvent(HANDLE hEvent);
HANDLE IthCreateMutex(LPCWSTR name, BOOL InitialOwner, DWORD *exist=0);
HANDLE IthOpenMutex(LPCWSTR name);
BOOL IthReleaseMutex(HANDLE hMutex);
//DWORD IthWaitForSingleObject(HANDLE hObject, DWORD dwTime);
HANDLE IthCreateThread(LPCVOID start_addr, DWORD param, HANDLE hProc=(HANDLE)-1);
DWORD GetExportAddress(DWORD hModule,DWORD hash);
void IthSleep(int time); // jichi 9/28/2013: in ms
void IthSystemTimeToLocalTime(LARGE_INTEGER *ptime);
void FreeThreadStart(HANDLE hProc);
void CheckThreadStart();
} // extern "C"
#ifdef ITH_HAS_HEAP
extern HANDLE hHeap; // used in ith/common/memory.h
#endif // ITH_HAS_HEAP
extern DWORD current_process_id;
extern DWORD debug;
extern BYTE LeadByteTable[];
extern LPVOID page;
extern BYTE launch_time[];
inline DWORD GetHash(LPSTR str)
{
DWORD hash = 0;
//for (; *str; str++)
while (*str)
hash = ((hash>>7) | (hash<<25)) + *str++;
return hash;
}
inline DWORD GetHash(LPCWSTR str)
{
DWORD hash = 0;
//for (; *str; str++)
while (*str)
hash = ((hash>>7) | (hash<<25)) + *str++;
return hash;
}
inline void IthBreak()
{ if (debug) __debugbreak(); }
inline LPCWSTR GetMainModulePath()
{
__asm
{
mov eax, fs:[0x30]
mov eax, [eax + 0xC]
mov eax, [eax + 0xC]
mov eax, [eax + 0x28]
}
}
// jichi 9/28/2013: Add this to lock NtWriteFile in wine
class IthMutexLocker
{
HANDLE m;
public:
explicit IthMutexLocker(HANDLE mutex) : m(mutex)
{ NtWaitForSingleObject(m, 0, 0); }
~IthMutexLocker() { if (m != INVALID_HANDLE_VALUE) IthReleaseMutex(m); }
bool locked() const { return m != INVALID_HANDLE_VALUE; }
void unlock() { if (m != INVALID_HANDLE_VALUE) { IthReleaseMutex(m); m = INVALID_HANDLE_VALUE; } }
};
void IthCoolDown();
BOOL IthIsWine();
BOOL IthIsWindowsXp();
//BOOL IthIsWindows8OrGreater(); // not public
/** Get current dll path.
* @param buf
* @param len
* @return length of the path excluding \0
*/
size_t IthGetCurrentModulePath(wchar_t *buf, size_t len);
// EOF

13
vnr/ithsys/ithsys.pri Normal file
View File

@ -0,0 +1,13 @@
# ithsys.pri
# 8/21/2013 jichi
DEFINES += WITH_LIB_ITHSYS
LIBS += -lithsys
DEPENDPATH += $$PWD
HEADERS += $$PWD/ithsys.h
#SOURCES += $$PWD/ithsys.cc
#include($$LIBDIR/winddk/winddk.pri)
#LIBS += -L$$WDK/lib/wxp/i386
# EOF

52
vnr/ithsys/ithsys.pro Normal file
View File

@ -0,0 +1,52 @@
# ithsys.pro
# 8/21/2013 jichi
# Build ithsys.lib
#CONFIG += noqt noeh staticlib
CONFIG += noqt staticlib
include(../../../config.pri)
include($$LIBDIR/ntdll/ntdll.pri)
#LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
#include($$LIBDIR/winddk/winddk.pri)
#LIBS += -L$$WDK/lib/wxp/i386
# jichi 9/22/2013: When ITH is on wine, certain NT functions are replaced
#DEFINES += ITH_WINE
# jichi 7/12/2015: Always enable SEH
DEFINES += ITH_HAS_SEH
# jichi 11/24/2013: Disable manual heap
DEFINES -= ITH_HAS_HEAP
## Libraries
#INCLUDEPATH += $$ITH_HOME/include
#INCLUDEPATH += $$WDK7_HOME/inc/ddk
#LIBS += -lgdi32 -luser32 -lkernel32
#LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
#LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
#DEFINES += ITH_HAS_CXX
#LIBS += -lith_sys -lntdll
#LIBS += -lith_tls -lntdll
#LIBS += -lntoskrnl
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
## Sources
TEMPLATE = lib
TARGET = ithsys
HEADERS += ithsys.h
SOURCES += ithsys.cc
OTHER_FILES += ithsys.pri
# EOF

View File

@ -198,76 +198,190 @@ finish:
}
}
#if 0
/**
* Search from stopAddress back to startAddress - range
* This function is not well debugged
*/
DWORD reverseSearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length) // KMP
{
__asm
{
mov eax,search_length
alloc:
push 0
sub eax,1
jnz alloc
mov edi,search
mov edx,search_length
mov ecx,1
xor esi,esi
build_table:
mov al,byte ptr [edi+esi]
cmp al,byte ptr [edi+ecx]
sete al
test esi,esi
jz pre
test al,al
jnz pre
mov esi,[esp+esi*4-4]
jmp build_table
pre:
test al,al
jz write_table
inc esi
write_table:
mov [esp+ecx*4],esi
inc ecx
cmp ecx,edx
jb build_table
mov esi,base
xor edx,edx
mov ecx,edx
matcher:
mov al,byte ptr [edi+ecx]
cmp al,byte ptr [esi-edx] // jichi 6/1/2014: The only place that is modified
sete al
test ecx,ecx
jz match
test al,al
jnz match
mov ecx, [esp+ecx*4-4]
jmp matcher
match:
test al,al
jz pre2
inc ecx
cmp ecx,search_length
je finish
pre2:
inc edx
cmp edx,base_length // search_length
jb matcher
mov edx,search_length
dec edx
finish:
mov ecx,search_length
sub edx,ecx
lea eax,[edx+1]
lea ecx,[ecx*4]
add esp,ecx
}
}
#endif // 0
// Modified from ITH findCallOrJmpAbs
// Example call:
// 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
enum : WORD {
word_jmp = 0x25ff
word_jmp = 0x25ff // long jump
, word_call = 0x15ff // far call
};
/***
* Return the absolute address of op. Op takes 1 parameter.
*
* @param op first half of the operator
* @param arg1 the function address
* @param start address
* @param search range
* @return absolute address or 0
*/
DWORD findWordCall(WORD op, DWORD arg1, DWORD start, DWORD size)
{
typedef WORD optype;
typedef DWORD argtype;
enum { START = 0x1000 }; // leading size to skip
for (DWORD i = START; i < size - sizeof(argtype); i++)
if (op == *(optype *)(start + i)) {
DWORD t = *(DWORD *)(start + i + sizeof(optype));
if (t > start && t < start + size) {
if (arg1 == *(argtype *)t)
return start + i;
else
i += sizeof(optype) + sizeof(argtype) - 1; // == 5
}
}
return 0;
}
// Modified from ITH findCallOrJmpAbs
enum : BYTE {
byte_call = 0xe8 // near call
byte_jmp = 0xe9 // long call
, byte_call = 0xe8 // near call
, byte_push_small = 0x6a // push byte operand
, byte_push_large = 0x68 // push operand > 0xff
};
/***
* Return the absolute address of op. Op takes 1 address parameter.
* Return the absolute address of op. Op takes 1 parameter.
* DWORD call with absolute address.
*
* @param op first half of the operator
* @param arg1 the function address
* @param start address
* @param search range
* @param stop address
* @param offset search after start address
* @param range search size
* @return absolute address or 0
*/
DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
DWORD findWordCall(WORD op, DWORD arg1, DWORD start, DWORD stop, DWORD offset, DWORD range)
{
typedef WORD optype;
typedef DWORD argtype;
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
if (op == *(optype *)(start + i)) {
DWORD t = *(DWORD *)(start + i + sizeof(optype));
if (t > start && t < stop) {
if (arg1 == *(argtype *)t) // absolute address
return start + i;
//i += sizeof(optype) + sizeof(argtype) - 1; // == 5
}
}
return 0;
}
DWORD findLastWordCall(WORD op, DWORD arg1, DWORD start, DWORD stop, DWORD offset, DWORD range)
{
typedef WORD optype;
typedef DWORD argtype;
DWORD ret = 0;
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
if (op == *(optype *)(start + i)) {
DWORD t = *(DWORD *)(start + i + sizeof(optype));
if (t > start && t < stop) {
if (arg1 == *(argtype *)t) // absolute address
ret = start + i;
//i += sizeof(optype) + sizeof(argtype) - 1; // == 5
}
}
return ret;
}
/***
* Return the absolute address of op. Op takes 1 address parameter.
* BYTE call with relative address.
*
* @param op first half of the operator
* @param arg1 the function address
* @param start address
* @param offset search after start address
* @param range search size
* @return absolute address or 0
*/
DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD offset, DWORD range)
{
typedef BYTE optype;
typedef DWORD argtype;
enum { START = 0x1000 }; // leading size to skip
for (DWORD i = START; i < size - sizeof(argtype); i++)
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
if (op == *(optype *)(start + i)) {
DWORD t = *(DWORD *)(start + i + sizeof(optype));
if (t > start && t < start + size) {
if (arg1 == *(argtype *)t)
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;
else
i += sizeof(optype) + sizeof(argtype) - 1; // == 4
}
//i += sizeof(optype) + sizeof(argtype) - 1; // == 4
//}
}
return 0;
}
DWORD findLastByteCall(BYTE op, DWORD arg1, DWORD start, DWORD offset, DWORD range)
{
typedef BYTE optype;
typedef DWORD argtype;
DWORD ret = 0;
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
if (op == *(optype *)(start + i)) {
DWORD t = *(argtype *)(start + i + sizeof(optype));
//if (t > start && t < stop) {
if (arg1 == t + start + i + sizeof(optype) + sizeof(argtype)) // relative address
ret = start + i;
//i += sizeof(optype) + sizeof(argtype) - 1; // == 4
//}
}
return ret;
}
/***
* Return the absolute address of op. Op takes 1 parameter.
*
@ -277,13 +391,12 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
* @param search range
* @return absolute address or 0
*/
//DWORD findByteOp1(BYTE op, DWORD arg1, DWORD start, DWORD size)
//DWORD findByteOp1(BYTE op, DWORD arg1, DWORD start, DWORD size, DWORD offset)
//{
// typedef BYTE optype;
// typedef DWORD argtype;
//
// enum { START = 0x1000 }; // leading size to skip
// for (DWORD i = START; i < size - sizeof(argtype); i++)
// for (DWORD i = offset; i < size - sizeof(argtype); i++)
// if (op == *(optype *)(start + i)) {
// DWORD t = *(DWORD *)(start + i + sizeof(optype));
// if (t == arg1) {
@ -299,14 +412,29 @@ DWORD findByteCall(BYTE op, DWORD arg1, DWORD start, DWORD size)
MEMDBG_BEGIN_NAMESPACE
DWORD findJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
{ return findWordCall(word_jmp, funcAddr, lowerBound, upperBound - lowerBound); }
DWORD findLongJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return findWordCall(word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
DWORD findFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
{ return findWordCall(word_call, funcAddr, lowerBound, upperBound - lowerBound); }
DWORD findShortJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return findByteCall(byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
DWORD findNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound)
{ return findByteCall(byte_call, funcAddr, lowerBound, upperBound - lowerBound); }
DWORD findFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return findWordCall(word_call, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
DWORD findNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return findByteCall(byte_call, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
DWORD findLastLongJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return findLastWordCall(word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
DWORD findLastShortJumpAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return findLastByteCall(byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
DWORD findLastFarCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return findLastWordCall(word_call, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
DWORD findLastNearCallAddress(DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return findLastByteCall(byte_call, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
DWORD findPushDwordAddress(DWORD value, DWORD lowerBound, DWORD upperBound)
{
@ -322,9 +450,8 @@ DWORD findPushByteAddress(BYTE value, DWORD lowerBound, DWORD upperBound)
return findBytes(bytes, sizeof(bytes), lowerBound, upperBound);
}
DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength)
DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
{
enum { Start = 0x1000 };
enum { PatternSize = 4 };
const DWORD size = upperBound - lowerBound - PatternSize;
const DWORD fun = (DWORD)funcAddr;
@ -332,9 +459,9 @@ DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upper
// 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
//WCHAR str[0x40];
const DWORD mask = sigMask(sig);
for (DWORD i = Start; i < size; i++)
for (DWORD i = offset; i < size; i++)
if (*(WORD *)(lowerBound + i) == word_call) {
DWORD t = *(DWORD *)(lowerBound + i + 2);
DWORD t = *(DWORD *)(lowerBound + i + 2); // 2 = sizeof(word)
if (t >= lowerBound && t <= upperBound - PatternSize) {
if (*(DWORD *)t == fun)
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
@ -352,9 +479,154 @@ DWORD findCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upper
return 0;
}
DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount, DWORD lowerBound, DWORD upperBound, DWORD reverseLength)
#ifndef MEMDBG_NO_STL
bool iterFindBytes(const address_fun_t &fun, const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
{
for (DWORD addr = lowerBound; addr < upperBound - patternSize; addr += patternSize) {
addr = findBytes(pattern, patternSize, addr, upperBound);
if (!addr || !fun(addr))
return false;
}
return true;
}
bool iterMatchBytes(const address_fun_t &fun, const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
{
for (DWORD addr = lowerBound; addr < upperBound - patternSize; addr += patternSize) { ;
addr = matchBytes(pattern, patternSize, addr, upperBound);
if (!addr || !fun(addr))
return false;
}
return true;
}
bool iterWordCall(const address_fun_t &callback, WORD op, DWORD arg1, DWORD start, DWORD stop, DWORD offset, DWORD range)
{
typedef WORD optype;
typedef DWORD argtype;
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
if (op == *(optype *)(start + i)) {
DWORD t = *(DWORD *)(start + i + sizeof(optype));
if (t > start && t < stop) {
if (arg1 == *(argtype *)t // absolute address
&& !callback(start + i))
return false;
//i += sizeof(optype) + sizeof(argtype) - 1; // == 5
}
}
return true;
}
bool iterByteCall(const address_fun_t &callback, BYTE op, DWORD arg1, DWORD start, DWORD offset, DWORD range)
{
typedef BYTE optype;
typedef DWORD argtype;
for (DWORD i = offset; i < offset + range - sizeof(argtype); i++)
if (op == *(optype *)(start + i)) {
DWORD t = *(argtype *)(start + i + sizeof(optype));
//if (t > start && t < stop) {
if (arg1 == t + start + i + sizeof(optype) + sizeof(argtype) // relative address
&& !callback(start + i))
return false;
//i += sizeof(optype) + sizeof(argtype) - 1; // == 4
//}
}
return true;
}
bool iterCallerAddress(const address2_fun_t &callback, DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
{
enum { PatternSize = 4 };
const DWORD size = upperBound - lowerBound - PatternSize;
const DWORD fun = (DWORD)funcAddr;
// Example function call:
// 00449063 |. ff15 5cf05300 call dword ptr ds:[<&gdi32.getglyphoutli>; \GetGlyphOutlineA
//WCHAR str[0x40];
const DWORD mask = sigMask(sig);
for (DWORD i = offset; i < size; i++)
if (*(WORD *)(lowerBound + i) == word_call) {
DWORD t = *(DWORD *)(lowerBound + i + 2); // 2 = sizeof(word)
if (t >= lowerBound && t <= upperBound - PatternSize) {
if (*(DWORD *)t == fun)
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
//OutputConsole(str);
for (DWORD j = i ; j > i - reverseLength; j--)
if ((*(DWORD *)(lowerBound + j) & mask) == sig) {
if (!callback(lowerBound + j, lowerBound + i))
return false;
break;
}
} else
i += 6;
}
//OutputConsole(L"Find call and entry failed.");
return true;
}
bool iterCallerAddressAfterInt3(const address2_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
{
auto callback = [&fun](dword_t addr, dword_t call) -> bool {
while (byte_int3 == *(BYTE *)++addr); // skip leading int3
return fun(addr, call);
};
return iterCallerAddress(callback, funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
}
bool iterUniqueCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
{
dword_t prevAddr = 0;
auto callback = [&fun, &prevAddr](dword_t addr, dword_t) -> bool {
if (prevAddr == addr)
return true;
prevAddr = addr;
return fun(addr);
};
return iterCallerAddress(callback, funcAddr, funcInst, lowerBound, upperBound, callerSearchSize, offset);
}
bool iterUniqueCallerAddressAfterInt3(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
{
auto callback = [&fun](dword_t addr) -> bool {
while (byte_int3 == *(BYTE *)++addr); // skip leading int3
return fun(addr);
};
return iterUniqueCallerAddress(callback, funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
}
bool iterLongJumpAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return iterWordCall(fun, word_jmp, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
bool iterShortJumpAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return iterByteCall(fun, byte_jmp, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
bool iterFarCallAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return iterWordCall(fun, word_call, funcAddr, lowerBound, upperBound, offset, range ? range : (upperBound - lowerBound - offset)); }
bool iterNearCallAddress(const address_fun_t &fun, DWORD funcAddr, DWORD lowerBound, DWORD upperBound, DWORD offset, DWORD range)
{ return iterByteCall(fun, byte_call, funcAddr, lowerBound, offset, range ? range : (upperBound - lowerBound - offset)); }
bool iterAlignedNearCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
{
dword_t prevAddr = 0;
auto callback = [&fun, &prevAddr, callerSearchSize](dword_t addr) -> bool {
if ((addr = findEnclosingAlignedFunction(addr, callerSearchSize))
&& prevAddr != addr) {
prevAddr = addr;
return fun(addr);
}
return true;
};
return iterNearCallAddress(callback, funcAddr, lowerBound, upperBound, offset);
}
#endif // MEMDBG_NO_STL
DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
{
enum { Start = 0x1000 };
enum { PatternSize = 4 };
const DWORD size = upperBound - lowerBound - PatternSize;
const DWORD fun = (DWORD)funcAddr;
@ -367,9 +639,9 @@ DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount,
for (DWORD k = 0; k < sigCount; k++)
masks[k] = sigMask(sigs[k]);
for (DWORD i = Start; i < size; i++)
for (DWORD i = offset; i < size; i++)
if (*(WORD *)(lowerBound + i) == word_call) {
DWORD t = *(DWORD *)(lowerBound + i + 2);
DWORD t = *(DWORD *)(lowerBound + i + 2); // 2 = sizeof(word)
if (t >= lowerBound && t <= upperBound - PatternSize) {
if (*(DWORD *)t == fun)
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
@ -391,16 +663,15 @@ DWORD findMultiCallerAddress(DWORD funcAddr, const DWORD sigs[], DWORD sigCount,
return 0;
}
DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength)
DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD upperBound, DWORD reverseLength, DWORD offset)
{
enum { Start = 0x1000 };
enum { PatternSize = 4 };
const DWORD size = upperBound - lowerBound - PatternSize;
const DWORD fun = (DWORD)funcAddr;
//WCHAR str[0x40];
DWORD ret = 0;
const DWORD mask = sigMask(sig);
for (DWORD i = Start; i < size; i++)
for (DWORD i = offset; i < size; i++)
if (*(WORD *)(lowerBound + i) == word_call) {
DWORD t = *(DWORD *)(lowerBound + i + 2);
if (t >= lowerBound && t <= upperBound - PatternSize) {
@ -408,10 +679,12 @@ DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD u
//swprintf(str,L"CALL addr: 0x%.8X",lowerBound + i);
//OutputConsole(str);
for (DWORD j = i ; j > i - reverseLength; j--)
if ((*(DWORD *)(lowerBound + j) & mask) == sig) // Fun entry 1.
if ((*(DWORD *)(lowerBound + j) & mask) == sig) { // Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",lowerBound + j);
//OutputConsole(str);
ret = lowerBound + j;
break;
}
} else
i += 6;
@ -420,22 +693,36 @@ DWORD findLastCallerAddress(DWORD funcAddr, DWORD sig, DWORD lowerBound, DWORD u
return ret;
}
DWORD findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize)
DWORD findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
{
DWORD addr = findCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize);
DWORD addr = findCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
if (addr)
while (byte_int3 == *(BYTE *)++addr);
return addr;
}
DWORD findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize)
DWORD findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
{
DWORD addr = findLastCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize);
DWORD addr = findLastCallerAddress(funcAddr, word_2int3, lowerBound, upperBound, callerSearchSize, offset);
if (addr)
while (byte_int3 == *(BYTE *)++addr);
return addr;
}
DWORD findAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
{
if (DWORD addr = findNearCallAddress(funcAddr, lowerBound, upperBound, offset))
return findEnclosingAlignedFunction(addr, callerSearchSize);
return 0;
}
DWORD findLastAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize, dword_t offset)
{
if (DWORD addr = findLastCallerAddressAfterInt3(funcAddr, lowerBound, upperBound, callerSearchSize, offset))
return findEnclosingAlignedFunction(addr, callerSearchSize);
return 0;
}
DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range)
{
start &= ~0xf;
@ -466,6 +753,31 @@ DWORD findEnclosingAlignedFunction(DWORD start, DWORD back_range)
return 0;
}
DWORD findEnclosingFunctionAfterDword(DWORD sig, DWORD start, DWORD back_range, DWORD step)
{
start &= ~0xf;
for (DWORD i = start, j = start - back_range; i > j; i-=step) { // 0x10 is aligned
DWORD k = *(DWORD *)(i-4); // 4 = sizeof(DWORD)
if (k == sig)
return i;
}
return 0;
}
DWORD findEnclosingFunctionBeforeDword(DWORD sig, DWORD start, DWORD back_range,DWORD step)
{
DWORD addr = findEnclosingFunctionAfterDword(sig, start, back_range, step);
if (addr)
addr -= sizeof(DWORD);
return addr;
}
DWORD findEnclosingFunctionAfterInt3(DWORD start, DWORD back_range, DWORD step)
{ return findEnclosingFunctionAfterDword(0xcccccccc, start, back_range, step); }
DWORD findEnclosingFunctionAfterNop(DWORD start, DWORD back_range, DWORD step)
{ return findEnclosingFunctionAfterDword(0x90909090,start, back_range, step); }
DWORD findBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
{
DWORD reladdr = searchPattern(lowerBound, upperBound - lowerBound, pattern, patternSize);
@ -478,6 +790,12 @@ DWORD matchBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD
return reladdr ? lowerBound + reladdr : 0;
}
//DWORD reverseFindBytes(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound)
//{
// DWORD reladdr = reverseSearchPattern(lowerBound, upperBound - lowerBound, pattern, patternSize);
// return reladdr ? lowerBound + reladdr : 0;
//}
#if 0 // not used
DWORD findBytesInPages(const void *pattern, DWORD patternSize, DWORD lowerBound, DWORD upperBound, SearchType search)
{
@ -552,81 +870,3 @@ DWORD matchBytesInPages(const void *pattern, DWORD patternSize, DWORD lowerBound
MEMDBG_END_NAMESPACE
// EOF
#if 0 // disabled
/**
* Search from stopAddres back to startAddress - range
* This function is not well debugged
*/
DWORD reverseSearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length) // KMP
{
__asm
{
mov eax,search_length
alloc:
push 0
sub eax,1
jnz alloc
mov edi,search
mov edx,search_length
mov ecx,1
xor esi,esi
build_table:
mov al,byte ptr [edi+esi]
cmp al,byte ptr [edi+ecx]
sete al
test esi,esi
jz pre
test al,al
jnz pre
mov esi,[esp+esi*4-4]
jmp build_table
pre:
test al,al
jz write_table
inc esi
write_table:
mov [esp+ecx*4],esi
inc ecx
cmp ecx,edx
jb build_table
mov esi,base
xor edx,edx
mov ecx,edx
matcher:
mov al,byte ptr [edi+ecx]
cmp al,byte ptr [esi-edx] // jichi 6/1/2014: The only place that is modified
sete al
test ecx,ecx
jz match
test al,al
jnz match
mov ecx, [esp+ecx*4-4]
jmp matcher
match:
test al,al
jz pre2
inc ecx
cmp ecx,search_length
je finish
pre2:
inc edx
cmp edx,base_length // search_length
jb matcher
mov edx,search_length
dec edx
finish:
mov ecx,search_length
sub edx,ecx
lea eax,[edx+1]
lea ecx,[ecx*4]
add esp,ecx
}
}
#endif // 0, disabled

View File

@ -5,14 +5,53 @@
// 4/20/2014 jichi
#include "memdbg/memdbg.h"
#ifndef MEMDBG_NO_STL
# include <functional>
#endif // MEMDBG_NO_STL
MEMDBG_BEGIN_NAMESPACE
/// Estimated maximum size of the caller function, the same as ITH FindCallAndEntryAbs
enum { MaximumFunctionSize = 0x800 };
/// Offset added to the beginning of the searched address
enum { MemoryPaddingOffset = 0x1000 };
enum { MemoryAlignedStep = 0x10 };
#ifndef MEMDBG_NO_STL
/// Iterate address and return false if abort iteration.
typedef std::function<bool (dword_t)> address_fun_t;
typedef std::function<bool (dword_t, dword_t)> address2_fun_t;
/**
* Return the absolute address of the caller function
* Iterate all call and caller addresses
* @param fun the first parameter is the address of the caller, and the second parameter is the address of the call itself
* @return false if return early, and true if iterate all elements
*/
bool iterCallerAddress(const address2_fun_t &fun, dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
bool iterCallerAddressAfterInt3(const address2_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
bool iterUniqueCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
bool iterUniqueCallerAddressAfterInt3(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
/**
* Iterate all call and caller addresses
* @param fun the parameter is the address of the call
* @return false if return early, and true if iterate all elements
*/
bool iterFarCallAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
bool iterNearCallAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
bool iterLongJumpAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
bool iterShortJumpAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
bool iterAlignedNearCallerAddress(const address_fun_t &fun, dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
bool iterFindBytes(const address_fun_t &fun, const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
bool iterMatchBytes(const address_fun_t &fun, const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
#endif // MEMDBG_NO_STL
/**
* Return the absolute address of the far caller function
* The same as ITH FindCallAndEntryAbs().
*
* @param funcAddr callee function address
@ -27,12 +66,15 @@ enum { MaximumFunctionSize = 0x800 };
* 0x81,0xec: sub esp XXOO (0xec81)
* 0x83,0xec: sub esp XXOO (0xec83)
*/
dword_t findCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize);
dword_t findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize);
dword_t findLastCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize);
dword_t findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize);
dword_t findCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
dword_t findCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
dword_t findLastCallerAddress(dword_t funcAddr, dword_t funcInst, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
dword_t findLastCallerAddressAfterInt3(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
dword_t findMultiCallerAddress(dword_t funcAddr, const dword_t funcInsts[], dword_t funcInstCount, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize);
dword_t findMultiCallerAddress(dword_t funcAddr, const dword_t funcInsts[], dword_t funcInstCount, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
dword_t findAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
dword_t findLastAlignedNearCallerAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t callerSearchSize = MaximumFunctionSize, dword_t offset = MemoryPaddingOffset);
/**
* Return the absolute address of the long jump (not short jump) instruction address.
@ -41,9 +83,14 @@ dword_t findMultiCallerAddress(dword_t funcAddr, const dword_t funcInsts[], dwor
* @param funcAddr callee function address
* @param lowerBound the lower memory address to search
* @param upperBound the upper memory address to search
* @param* offset the relative address to search from the lowerBound
* @param* range the relative size to search, use lowerBound - upperBound when zero
* @return the call instruction address if succeed or 0 if fail
*/
dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound);
dword_t findLongJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
dword_t findShortJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
dword_t findLastLongJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
dword_t findLastShortJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
/**
* Return the absolute address of the far call (inter-module) instruction address.
@ -52,16 +99,28 @@ dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound
* @param funcAddr callee function address
* @param lowerBound the lower memory address to search
* @param upperBound the upper memory address to search
* @param* offset the relative address to search from the lowerBound
* @param* range the relative size to search, use lowerBound - upperBound when zero
* @return the call instruction address if succeed or 0 if fail
*/
dword_t findFarCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound);
dword_t findFarCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
dword_t findLastFarCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
/// Near call (intra-module)
dword_t findNearCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound);
dword_t findNearCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
dword_t findLastNearCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0);
/// Default to far call
inline dword_t findCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound)
{ return findFarCallAddress(funcAddr, lowerBound, upperBound); }
/// Default to far call, for backward compatibility
inline dword_t findCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
{ return findFarCallAddress(funcAddr, lowerBound, upperBound, offset, range); }
inline dword_t findLastCallAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
{ return findLastFarCallAddress(funcAddr, lowerBound, upperBound, offset, range); }
/// Default to long jump, for backward compatibility
inline dword_t findJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
{ return findLongJumpAddress(funcAddr, lowerBound, upperBound, offset, range); }
inline dword_t findLastJumpAddress(dword_t funcAddr, dword_t lowerBound, dword_t upperBound, dword_t offset = MemoryPaddingOffset, dword_t range = 0)
{ return findLastLongJumpAddress(funcAddr, lowerBound, upperBound, offset, range); }
/// Push value >= 0xff
dword_t findPushDwordAddress(dword_t value, dword_t lowerBound, dword_t upperBound);
@ -87,6 +146,10 @@ inline dword_t findPushAddress(dword_t value, dword_t lowerBound, dword_t upperB
* @exception illegal memory access
*/
dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumFunctionSize);
dword_t findEnclosingFunctionBeforeDword(dword_t sig, dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
dword_t findEnclosingFunctionAfterDword(dword_t sig, dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
dword_t findEnclosingFunctionAfterInt3(dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
dword_t findEnclosingFunctionAfterNop(dword_t addr, dword_t searchSize = MaximumFunctionSize, dword_t step = MemoryAlignedStep);
/**
* Return the address of the first matched pattern.
@ -102,6 +165,7 @@ dword_t findEnclosingAlignedFunction(dword_t addr, dword_t searchSize = MaximumF
* @exception illegal memory access
*/
dword_t findBytes(const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
//dword_t reverseFindBytes(const void *pattern, dword_t patternSize, dword_t lowerBound, dword_t upperBound);
/**
* jichi 2/5/2014: The same as findBytes except it uses widecard to match everything.

12
vnr/mono/mono.pri Normal file
View File

@ -0,0 +1,12 @@
# mono.pri
# 9/26/2012 jichi
DEFINES += WITH_LIB_MONO
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/monoobject.h \
$$PWD/monotype.h
# EOF

48
vnr/mono/monoobject.h Normal file
View File

@ -0,0 +1,48 @@
#ifndef MONOOBJECT_H
#define MONOOBJECT_H
// monoobject.h
// 12/26/2014 jichi
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
// https://github.com/mono/mono/blob/master/mono/metadata/object-internals.h
// https://github.com/mono/mono/blob/master/mono/util/mono-publib.h
#include <cstdint>
#define MONO_ZERO_LEN_ARRAY 1
// mono/io-layer/uglify.h
//typedef int8_t gint8;
//typedef int32_t gint32;
//typedef wchar_t gunichar2; // either char or wchar_t, depending on how mono is compiled
typedef int32_t mono_bool;
typedef uint8_t mono_byte;
typedef uint16_t mono_unichar2;
typedef uint32_t mono_unichar4;
// mono/metadata/object.h
typedef mono_bool MonoBoolean;
struct MonoArray;
struct MonoDelegate;
struct MonoDomain;
struct MonoException;
struct MonoString;
struct MonoThreadsSync;
struct MonoThread;
struct MonoVTable;
struct MonoObject {
MonoVTable *vtable;
MonoThreadsSync *synchronisation;
};
struct MonoString {
MonoObject object;
int32_t length;
mono_unichar2 chars[MONO_ZERO_LEN_ARRAY];
};
#endif // MONOOBJECT_H

17
vnr/mono/monotype.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef MONOTYPE_H
#define MONOTYPE_H
// monotype.h
// 12/26/2014 jichi
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
#include "mono/monoobject.h"
// Function typedefs
typedef MonoDomain *(* mono_object_get_domain_fun_t)(MonoObject *obj);
typedef MonoString *(* mono_string_new_utf16_fun_t)(MonoDomain *domain, const mono_unichar2 *text, int32_t len);
typedef char * (* mono_string_to_utf8_fun_t)(MonoString *string_obj);
#endif // MONOTYPE_H

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,23 @@
set(profile_src
Profile.h
Profile.cpp
pugiconfig.hpp
pugixml.cpp
pugixml.hpp
misc.h
misc.cpp
)
add_library(profile STATIC ${profile_src})
target_compile_options(profile PRIVATE
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
#target_link_libraries(profile comctl32.lib)
#target_compile_definitions(profile
# PRIVATE
# _CRT_NON_CONFORMING_SWPRINTFS
#)

293
vnr/profile/Profile.cpp Normal file
View File

@ -0,0 +1,293 @@
/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com)
* This file is part of the Interactive Text Hooker.
* Interactive Text Hooker is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "host/host.h"
#include "host/hookman.h"
#include "vnrhook/include/types.h"
#include "vnrhook/include/const.h"
#include "Profile.h"
#include "misc.h"
#include <algorithm>
#include "pugixml.hpp"
extern HookManager* man;
Profile::Profile(const std::wstring& title) :
select_index(-1),
title(title)
{}
const std::unordered_set<hook_ptr>& Profile::Hooks() const
{
return hooks;
}
const std::vector<thread_ptr>& Profile::Threads() const
{
return threads;
}
const std::unordered_set<link_ptr>& Profile::Links() const
{
return links;
}
bool Profile::XmlReadProfile(pugi::xml_node profile)
{
auto hooks_node = profile.child(L"Hooks");
auto threads_node = profile.child(L"Threads");
auto links_node = profile.child(L"Links");
if (hooks_node && !XmlReadProfileHook(hooks_node))
return false;
if (threads_node && !XmlReadProfileThread(threads_node))
return false;
if (links_node && !XmlReadProfileLink(links_node))
return false;
auto select_node = profile.child(L"Select");
if (select_node)
{
auto thread_index = select_node.attribute(L"ThreadIndex");
if (!thread_index)
return false;
DWORD tmp_select = std::stoul(thread_index.value(), NULL, 16);
select_index = tmp_select & 0xFFFF;
}
return true;
}
bool Profile::XmlReadProfileHook(pugi::xml_node hooks_node)
{
for (auto hook = hooks_node.begin(); hook != hooks_node.end(); ++hook)
{
std::wstring name = hook->name();
if (name.empty() || name.compare(L"Hook") != 0)
return false;
auto type = hook->attribute(L"Type");
if (!type || type.empty())
return false;
auto code = hook->attribute(L"Code");
if (!code)
return false;
std::wstring code_value = code.value();
HookParam hp = {};
switch (type.value()[0])
{
case L'H':
if (code_value[0] != L'/')
return false;
if (code_value[1] != L'H' && code_value[1] != L'h')
return false;
if (Parse(code_value.substr(2), hp))
{
auto name = hook->attribute(L"Name");
if (!name || name.empty())
AddHook(hook_ptr(new HookProfile(hp, L"")));
else
AddHook(hook_ptr(new HookProfile(hp, name.value())));
}
break;
default:
return false;
}
}
return true;
}
bool Profile::XmlReadProfileThread(pugi::xml_node threads_node)
{
std::wstring hook_name_buffer;
for (auto thread = threads_node.begin(); thread != threads_node.end(); ++thread)
{
std::wstring name = thread->name();
if (name.empty() || name.compare(L"Thread") != 0)
return false;
auto hook_name = thread->attribute(L"HookName");
if (!hook_name)
return false;
auto context = thread->attribute(L"Context");
if (!context)
return false;
auto sub_context = thread->attribute(L"SubContext");
if (!sub_context)
return false;
auto mask = thread->attribute(L"Mask");
if (!mask)
return false;
DWORD mask_tmp = std::stoul(mask.value(), NULL, 16);
auto comment = thread->attribute(L"Comment");
auto retn = std::stoul(context.value(), NULL, 16);
auto split = std::stoul(sub_context.value(), NULL, 16);
WORD flags = mask_tmp & 0xFFFF;
auto tp = new ThreadProfile(hook_name.value(), retn, split, 0, 0, flags,
comment.value());
AddThread(thread_ptr(tp));
}
return true;
}
bool Profile::XmlReadProfileLink(pugi::xml_node links_node)
{
for (auto link = links_node.begin(); link != links_node.end(); ++link)
{
std::wstring name = link->name();
if (name.empty() || name.compare(L"Link") != 0)
return false;
auto from = link->attribute(L"From");
if (!from)
return false;
DWORD link_from = std::stoul(from.value(), NULL, 16);
auto to = link->attribute(L"To");
if (!to)
return false;
DWORD link_to = std::stoul(to.value(), NULL, 16);
auto lp = new LinkProfile(link_from & 0xFFFF, link_to & 0xFFFF);
AddLink(link_ptr(lp));
}
return true;
}
bool Profile::XmlWriteProfile(pugi::xml_node profile_node)
{
if (!hooks.empty())
{
auto node = profile_node.append_child(L"Hooks");
XmlWriteProfileHook(node);
}
if (!threads.empty())
{
auto node = profile_node.append_child(L"Threads");
XmlWriteProfileThread(node);
}
if (!links.empty())
{
auto node = profile_node.append_child(L"Links");
XmlWriteProfileLink(node);
}
if (select_index != 0xFFFF)
{
auto node = profile_node.append_child(L"Select");
node.append_attribute(L"ThreadIndex") = select_index;
}
return true;
}
bool Profile::XmlWriteProfileHook(pugi::xml_node hooks_node)
{
for (auto hook = hooks.begin(); hook != hooks.end(); ++hook)
{
auto hook_node = hooks_node.append_child(L"Hook");
hook_node.append_attribute(L"Type") = L"H";
hook_node.append_attribute(L"Code") = ParseCode((*hook)->HP()).c_str();
if (!(*hook)->Name().empty())
hook_node.append_attribute(L"Name") = (*hook)->Name().c_str();
}
return true;
}
bool Profile::XmlWriteProfileThread(pugi::xml_node threads_node)
{
for (auto thread = threads.begin(); thread != threads.end(); ++thread)
{
const std::wstring& name = (*thread)->HookName();
if (name.empty())
return false;
auto node = threads_node.append_child(L"Thread");
node.append_attribute(L"HookName") = name.c_str();
std::wstring hex = ToHexString((*thread)->Flags() & (THREAD_MASK_RETN | THREAD_MASK_SPLIT));
node.append_attribute(L"Mask") = hex.c_str();
hex = ToHexString((*thread)->Split());
node.append_attribute(L"SubContext") = hex.c_str();
hex = ToHexString((*thread)->Return());
node.append_attribute(L"Context") = hex.c_str();
if (!(*thread)->Comment().empty())
node.append_attribute(L"Comment") = (*thread)->Comment().c_str();
}
return true;
}
bool Profile::XmlWriteProfileLink(pugi::xml_node links_node)
{
for (auto link = links.begin(); link != links.end(); ++link)
{
auto node = links_node.append_child(L"Link");
node.append_attribute(L"From") = ToHexString((*link)->FromIndex()).c_str();
node.append_attribute(L"To") = ToHexString((*link)->ToIndex()).c_str();
}
return true;
}
void Profile::Clear()
{
title = L"";
select_index = -1;
hooks.clear();
threads.clear();
links.clear();
}
void Profile::AddHook(hook_ptr hook)
{
hooks.insert(std::move(hook));
}
// add the thread profile and return its index
int Profile::AddThread(thread_ptr tp)
{
auto it = std::find_if(threads.begin(), threads.end(), [&tp](thread_ptr& thread)
{
return thread->HookName().compare(tp->HookName()) == 0 &&
thread->Return() == tp->Return() &&
thread->Split() == tp->Split();
});
if (it != threads.end())
return it - threads.begin();
threads.push_back(std::move(tp));
return threads.size() - 1;
}
void Profile::AddLink(link_ptr link)
{
links.insert(std::move(link));
}
const std::wstring& Profile::Title() const
{
return title;
}
bool Profile::IsThreadSelected(thread_ptr_iter thread_profile)
{
if (thread_profile != threads.end())
{
auto thread_index = thread_profile - threads.begin();
return select_index == thread_index;
}
return false;
}
thread_ptr_iter Profile::FindThread(const ThreadParameter* tp, const std::wstring& hook_name) const
{
auto thread_profile = std::find_if(threads.begin(), threads.end(),
[&tp, &hook_name](const thread_ptr& thread_profile) -> bool
{
return thread_profile->HookName().compare(hook_name) == 0
&& thread_profile->Return() == tp->retn
&& thread_profile->Split() == tp->spl;
});
return thread_profile;
}

171
vnr/profile/Profile.h Normal file
View File

@ -0,0 +1,171 @@
/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com)
* This file is part of the Interactive Text Hooker.
* Interactive Text Hooker is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "vnrhook/include/types.h" // HookParam
#include <string>
#include <memory>
#include <vector>
#include <unordered_set>
struct ThreadParameter;
class TextThread;
class HookProfile;
class ThreadProfile;
class LinkProfile;
typedef std::unique_ptr<HookProfile> hook_ptr;
typedef std::unique_ptr<ThreadProfile> thread_ptr;
typedef std::unique_ptr<LinkProfile> link_ptr;
typedef std::vector<thread_ptr>::const_iterator thread_ptr_iter;
namespace pugi {
class xml_node;
}
#define THREAD_MASK_RETN 1
#define THREAD_MASK_SPLIT 2
class HookProfile
{
HookParam hp;
std::wstring name;
public:
HookProfile(const HookParam& hp, const std::wstring& name) :
hp(hp),
name(name)
{}
const HookParam& HP() const { return hp; };
const std::wstring& Name() const { return name; };
};
class ThreadProfile
{
std::wstring hook_name;
DWORD retn;
DWORD split;
DWORD hook_addr;
WORD hm_index, flags;
std::wstring comment;
public:
ThreadProfile(const std::wstring& hook_name,
DWORD retn,
DWORD split,
DWORD hook_addr,
WORD hm_index,
WORD flags,
const std::wstring& comment) :
hook_name(hook_name),
retn(retn),
split(split),
hook_addr(hook_addr),
hm_index(hm_index),
flags(flags),
comment(comment)
{
}
const std::wstring& HookName() const { return hook_name; }
const std::wstring& Comment() const { return comment; }
DWORD Return() const { return retn; }
DWORD Split() const { return split; }
DWORD& HookAddress() { return hook_addr; }
WORD& HookManagerIndex() { return hm_index; }
WORD Flags() const { return flags; }
};
class LinkProfile
{
WORD from_index, to_index;
public:
LinkProfile(WORD from_index, WORD to_index) :
from_index(from_index),
to_index(to_index)
{}
WORD FromIndex() const { return from_index; }
WORD ToIndex() const { return to_index; }
};
namespace std {
template<>
struct hash<hook_ptr> {
size_t operator()(const hook_ptr &r) const
{
return hash<DWORD>{}(r->HP().address)
^ hash<DWORD>{}(r->HP().module)
^ hash<DWORD>{}(r->HP().function);
}
};
template<>
struct equal_to<hook_ptr> {
bool operator()(const hook_ptr& r, const hook_ptr& r2) const
{
return r->HP().address == r2->HP().address
&& r->HP().module == r2->HP().module
&& r->HP().function == r2->HP().function;
}
};
template<>
struct hash<link_ptr> {
size_t operator()(const link_ptr &r) const
{
return hash<WORD>{}(r->FromIndex())
^ hash<WORD>{}(r->ToIndex());
}
};
template<>
struct equal_to<link_ptr> {
bool operator()(const link_ptr& r, const link_ptr& r2) const
{
return r->FromIndex() == r2->FromIndex()
&& r->ToIndex() == r2->ToIndex();
}
};
}
class Profile
{
public:
Profile(const std::wstring& title);
bool XmlReadProfile(pugi::xml_node profile_node);
bool XmlWriteProfile(pugi::xml_node profile_node);
void AddHook(hook_ptr hook);
int AddThread(thread_ptr tp);
void AddLink(link_ptr lp);
void Clear();
const std::unordered_set<hook_ptr>& Hooks() const;
const std::vector<thread_ptr>& Threads() const;
const std::unordered_set<link_ptr>& Links() const;
const std::wstring& Title() const;
thread_ptr_iter FindThread(const ThreadParameter* tp, const std::wstring& hook_name) const;
WORD& SelectedIndex() { return select_index; }
bool IsThreadSelected(thread_ptr_iter thread_profile);
private:
bool XmlReadProfileHook(pugi::xml_node hooks_node);
bool XmlReadProfileThread(pugi::xml_node threads_node);
bool XmlReadProfileLink(pugi::xml_node links_node);
bool XmlWriteProfileHook(pugi::xml_node hooks_node);
bool XmlWriteProfileThread(pugi::xml_node threads_node);
bool XmlWriteProfileLink(pugi::xml_node links_node);
std::wstring title;
std::unordered_set<hook_ptr> hooks;
std::vector<thread_ptr> threads;
std::unordered_set<link_ptr> links;
WORD select_index;
};

275
vnr/profile/misc.cpp Normal file
View File

@ -0,0 +1,275 @@
#include "misc.h"
#include <regex>
#include <memory>
#include "host/host.h"
#include "vnrhook/include/const.h"
#include "vnrhook/include/types.h"
DWORD Hash(const std::wstring& module, int length)
{
DWORD hash = 0;
auto end = (length < 0 || static_cast<size_t>(length) > module.length()) ?
module.end() :
module.begin() + length;
for (auto it = module.begin(); it != end; ++it)
hash = _rotr(hash, 7) + *it;
return hash;
}
bool Parse(const std::wstring& cmd, HookParam& hp)
{
using std::wregex;
using std::regex_search;
// /H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:[module[:{name|#ordinal}]]]
wregex rx(L"^X?([ABWSQ])(N)?", wregex::icase);
std::match_results<std::wstring::const_iterator> m;
auto start = cmd.begin();
auto end = cmd.end();
bool result = regex_search(start, end, m, rx);
if (!result)
return result;
start = m[0].second;
if (m[2].matched)
hp.type |= NO_CONTEXT;
switch (m[1].first[0])
{
case L's':
case L'S':
hp.type |= USING_STRING;
break;
case L'e':
case L'E':
hp.type |= STRING_LAST_CHAR;
case L'a':
case L'A':
hp.type |= BIG_ENDIAN;
hp.length_offset = 1;
break;
case L'b':
case L'B':
hp.length_offset = 1;
break;
case L'h':
case L'H':
hp.type |= PRINT_DWORD;
case L'q':
case L'Q':
hp.type |= USING_STRING | USING_UNICODE;
break;
case L'l':
case L'L':
hp.type |= STRING_LAST_CHAR;
case L'w':
case L'W':
hp.type |= USING_UNICODE;
hp.length_offset = 1;
break;
default:
break;
}
// [data_offset[*drdo]]
std::wstring data_offset(L"(-?[[:xdigit:]]+)"), drdo(L"(\\*-?[[:xdigit:]]+)?");
rx = wregex(L"^" + data_offset + drdo, wregex::icase);
result = regex_search(start, end, m, rx);
if (result)
{
start = m[0].second;
hp.offset = std::stoul(m[1].str(), NULL, 16);
if (m[2].matched)
{
hp.type |= DATA_INDIRECT;
hp.index = std::stoul(m[2].str().substr(1), NULL, 16);
}
}
// [:sub_offset[*drso]]
std::wstring sub_offset(L"(-?[[:xdigit:]]+)"), drso(L"(\\*-?[[:xdigit:]]+)?");
rx = wregex(L"^:" + sub_offset + drso, wregex::icase);
result = regex_search(start, end, m, rx);
if (result)
{
start = m[0].second;
hp.type |= USING_SPLIT;
hp.split = std::stoul(m[1].str(), NULL, 16);
if (m[2].matched)
{
hp.type |= SPLIT_INDIRECT;
hp.split_index = std::stoul(m[2].str().substr(1), NULL, 16);
}
}
// @addr
rx = wregex(L"^@[[:xdigit:]]+", wregex::icase);
result = regex_search(start, end, m, rx);
if (!result)
return false;
start = m[0].second;
hp.address = std::stoul(m[0].str().substr(1), NULL, 16);
if (hp.offset & 0x80000000)
hp.offset -= 4;
if (hp.split & 0x80000000)
hp.split -= 4;
// [:[module[:{name|#ordinal}]]]
// ":" -> module == NULL &% function == NULL
// "" -> MODULE_OFFSET && module == NULL && function == addr
// ":GDI.dll" -> MODULE_OFFSET && module != NULL
// ":GDI.dll:strlen" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
// ":GDI.dll:#123" -> MODULE_OFFSET | FUNCTION_OFFSET && module != NULL && function != NULL
std::wstring module(L"([[:graph:]]+)"), name(L"[[:graph:]]+"), ordinal(L"\\d+");
rx = wregex(L"^:(" + module + L"(:" + name + L"|#" + ordinal + L")?)?$", wregex::icase);
result = regex_search(start, end, m, rx);
if (result) // :[module[:{name|#ordinal}]]
{
if (m[1].matched) // module
{
hp.type |= MODULE_OFFSET;
std::wstring module = m[2];
std::transform(module.begin(), module.end(), module.begin(), ::towlower);
hp.module = Hash(module);
if (m[3].matched) // :name|#ordinal
{
hp.type |= FUNCTION_OFFSET;
hp.function = Hash(m[3].str().substr(1));
}
}
}
else
{
rx = wregex(L"^!([[:xdigit:]]+)(!([[:xdigit:]]+))?$", wregex::icase);
result = regex_search(start, end, m, rx);
if (result)
{
hp.type |= MODULE_OFFSET;
hp.module = std::stoul(m[1].str(), NULL, 16);
if (m[2].matched)
{
hp.type |= FUNCTION_OFFSET;
hp.function = std::stoul(m[2].str().substr(1), NULL, 16);
}
}
else
{
// Hack. Hook is relative to the executable. Store the original address in function.
// hp.module == NULL && hp.function != NULL
hp.type |= MODULE_OFFSET;
hp.function = hp.address;
}
}
return true;
}
std::wstring ParseCode(const HookParam& hp)
{
std::wstring code(L"/H");
WCHAR c;
if (hp.type & PRINT_DWORD)
c = L'H';
else if (hp.type & USING_UNICODE)
{
if (hp.type & USING_STRING)
c = L'Q';
else if (hp.type & STRING_LAST_CHAR)
c = L'L';
else
c = L'W';
}
else
{
if (hp.type & USING_STRING)
c = L'S';
else if (hp.type & BIG_ENDIAN)
c = L'A';
else if (hp.type & STRING_LAST_CHAR)
c = L'E';
else
c = L'B';
}
code += c;
if (hp.type & NO_CONTEXT)
code += L'N';
if (hp.offset >> 31)
code += L'-' + ToHexString(-(hp.offset + 4));
else
code += ToHexString(hp.offset);
if (hp.type & DATA_INDIRECT)
{
if (hp.index >> 31)
code += L'*-' + ToHexString(-hp.index);
else
code += L'*' + ToHexString(hp.index);
}
if (hp.type & USING_SPLIT)
{
if (hp.split >> 31)
code += L":-" + ToHexString(-(4 + hp.split));
else
code += L":" + ToHexString(hp.split);
}
if (hp.type & SPLIT_INDIRECT)
{
if (hp.split_index >> 31)
code += L"*-" + ToHexString(-hp.split_index);
else
code += L"*" + ToHexString(hp.split_index);
}
if (hp.module)
{
code += L"@" + ToHexString(hp.address) + L"!" + ToHexString(hp.module);
if (hp.function)
code += L"!" + ToHexString(hp.function);
}
else
{
// Hack. The original address is stored in the function field
// if (module == NULL && function != NULL).
// MODULE_OFFSET and FUNCTION_OFFSET are removed from HookParam.type in
// TextHook::UnsafeInsertHookCode() and can not be used here.
if (hp.function)
code += L"@" + ToHexString(hp.function);
else
code += L"@" + ToHexString(hp.address) + L":";
}
return code;
}
std::string toMultiByteString(const std::wstring& unicodeString)
{
int cbMultiByte = WideCharToMultiByte(932, 0, unicodeString.c_str(), unicodeString.length(),
NULL, 0, NULL, NULL);
auto lpMultiByteStr = std::make_unique<CHAR[]>(cbMultiByte);
WideCharToMultiByte(932, 0, unicodeString.c_str(), unicodeString.length(),
lpMultiByteStr.get(), cbMultiByte, NULL, NULL);
return std::string(lpMultiByteStr.get(), cbMultiByte);
}
std::wstring toUnicodeString(const std::string& mbString)
{
int cchWideChar = MultiByteToWideChar(932, 0, mbString.c_str(), mbString.length(), NULL, 0);
auto lpWideCharStr = std::make_unique<WCHAR[]>(cchWideChar);
MultiByteToWideChar(932, 0, mbString.c_str(), mbString.length(), lpWideCharStr.get(), cchWideChar);
return std::wstring(lpWideCharStr.get(), cchWideChar);
}
std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address)
{
std::wstring hook_name;
WaitForSingleObject(pr.hookman_mutex, 0);
auto hooks = (const Hook*)pr.hookman_map;
for (int i = 0; i < MAX_HOOK; ++i)
{
auto& hook = hooks[i];
if (hook.Address() == hook_address)
{
std::unique_ptr<CHAR[]> name(new CHAR[hook.NameLength()]);
// name is zero terminated
if (ReadProcessMemory(pr.process_handle, hooks[i].Name(), name.get(), hook.NameLength(), NULL))
hook_name = toUnicodeString(name.get());
break;
}
}
ReleaseMutex(pr.hookman_mutex);
return hook_name;
}

22
vnr/profile/misc.h Normal file
View File

@ -0,0 +1,22 @@
#pragma once
#include <Windows.h>
#include <string>
#include <sstream>
struct HookParam;
struct ProcessRecord;
bool Parse(const std::wstring& cmd, HookParam& hp);
DWORD Hash(const std::wstring& module, int length = -1);
std::wstring ParseCode(const HookParam& hp);
std::string toMultiByteString(const std::wstring& unicodeString);
std::wstring toUnicodeString(const std::string& mbString);
std::wstring GetHookNameByAddress(const ProcessRecord& pr, DWORD hook_address);
template <typename T>
std::wstring ToHexString(T i) {
std::wstringstream ss;
ss << std::uppercase << std::hex << i;
return ss.str();
}

View File

@ -0,0 +1,71 @@
/**
* pugixml parser - version 1.6
* --------------------------------------------------------
* Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at http://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
*
* This work is based on the pugxml parser, which is:
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/
#ifndef HEADER_PUGICONFIG_HPP
#define HEADER_PUGICONFIG_HPP
// Uncomment this to enable wchar_t mode
#define PUGIXML_WCHAR_MODE
// Uncomment this to disable XPath
// #define PUGIXML_NO_XPATH
// Uncomment this to disable STL
// #define PUGIXML_NO_STL
// Uncomment this to disable exceptions
// #define PUGIXML_NO_EXCEPTIONS
// Set this to control attributes for public classes/functions, i.e.:
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
// Tune these constants to adjust memory-related behavior
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
// Uncomment this to switch to header-only version
// #define PUGIXML_HEADER_ONLY
// Uncomment this to enable long long support
// #define PUGIXML_HAS_LONG_LONG
#endif
/**
* Copyright (c) 2006-2015 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/

11554
vnr/profile/pugixml.cpp Normal file

File diff suppressed because it is too large Load Diff

1366
vnr/profile/pugixml.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,62 @@
# sakurakit.pri
# 6/28/2011 jichi
#
# config:
# - sakurakit_qml
# - sakurakit_gui
# - sakurakit_widgets
DEFINES += WITH_LIB_SAKURAKIT
DEPENDPATH += $$PWD
#QT += core
HEADERS += \
$$PWD/skautorun.h \
$$PWD/skdebug.h \
$$PWD/skglobal.h \
$$PWD/skhash.h
#CONFIG(sakurakit_gui) {
# DEFINES += SK_ENABLE_GUI
# QT += gui
# HEADERS += \
# $$PWD/skuiutil.h
#
# CONFIG(sakurakit_qml) {
# DEFINES += SK_ENABLE_QML
# QT += qml quick
# HEADERS += \
# $$PWD/skdraggablequickview.h
# SOURCES += \
# $$PWD/skdraggablequickview.cc
# }
#
# CONFIG(sakurakit_widgets) {
# DEFINES += SK_ENABLE_WIDGETS
# QT += widgets
# HEADERS += \
# $$PWD/skdraggabledialog.h \
# $$PWD/skdraggablemainwindow.h \
# $$PWD/skdraggablewidget.h \
# $$PWD/skpushbutton.h \
# $$PWD/sksystemtrayicon.h \
# $$PWD/sktoolbutton.h \
# $$PWD/skwindowcontainer.h
# SOURCES += \
# $$PWD/skdraggablewidget.cc \
# $$PWD/skpushbutton.cc \
# $$PWD/sktoolbutton.cc \
# $$PWD/skwindowcontainer.cc \
# $$PWD/skdraggabledialog.cc \
# $$PWD/skdraggablemainwindow.cc
#
# CONFIG(sakurakit_qml) {
# HEADERS += \
# $$PWD/skquickwidget.h
# SOURCES += \
# $$PWD/skquickwidget.cc
# }
# }
#}
# EOF

42
vnr/sakurakit/skautorun.h Normal file
View File

@ -0,0 +1,42 @@
#ifndef SKAUTORUN_H
#define SKAUTORUN_H
// skautorun.h
// 9/30/2012 jichi
#include "sakurakit/skglobal.h"
#include <functional>
SK_BEGIN_NAMESPACE
class SkAutoRun
{
public:
typedef std::function<void ()> function_type;
SkAutoRun(const function_type &start, const function_type &exit)
: exit_(exit) { start(); }
~SkAutoRun() { exit_(); }
private:
function_type exit_;
};
class SkAutoRunAtStartup
{
public:
typedef SkAutoRun::function_type function_type;
explicit SkAutoRunAtStartup(const function_type &start) { start(); }
};
class SkAutoRunAtExit
{
public:
typedef SkAutoRun::function_type function_type;
explicit SkAutoRunAtExit(const function_type &exit) : exit_(exit) {}
~SkAutoRunAtExit() { exit_(); }
private:
function_type exit_;
};
SK_END_NAMESPACE
#endif // SkAUTORUN_H

46
vnr/sakurakit/skdebug.h Normal file
View File

@ -0,0 +1,46 @@
#ifndef SKDEBUG_H
#define SKDEBUG_H
// skdebug.h
// 10/16/2011 jichi
// Macros for debug.
// Debug I/O
// - DPRINT: similar to fprintf, or KDPrint. Print to qDebug
// - DOUT: similar to std::cout. Print to qDebug
// - DERR: similar to std::cerr. Print to qWarning
#if defined(DEBUG) && !defined(SK_NO_DEBUG)
# if defined(QT_CORE_LIB) && !defined(SK_NO_QT)
# include <QtCore/QDebug>
# define DPRINT(...) qDebug(QString("%1:%2:").arg((DEBUG), (__FUNCTION__)).toLocal8Bit().constData(), __VA_ARGS__)
# define DOUT(_msg) qDebug() << QString("%1:%2:").arg((DEBUG), (__FUNCTION__)).toLocal8Bit().constData() << _msg
# define DERR(_msg) qWarning() << QString("%1:%2:").arg((DEBUG), (__FUNCTION__)).toLocal8Bit().constData() << _msg
# else
# include <iostream>
# include <cstdio>
# define DPRINT(...) fprintf(stderr, DEBUG ":" __FUNCTION__ ": " __VA_ARGS__)
# define DWPRINT(...) fwprintf(stderr, DEBUG ":" __FUNCTION__ ": " __VA_ARGS__)
# define DOUT(_msg) std::cout << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
# define DWOUT(_msg) std::wcout << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
# define DERR(_msg) std::cerr << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
# define DWERR(_msg) std::wcerr << DEBUG << ":" << __FUNCTION__ << ": " << _msg << std::endl
# endif // QT_CORE_LIB
#else // DEBUG
# define DPRINT(_dummy) (void)0
# define DOUT(_dummy) (void)0
# define DERR(_dummy) (void)0
//#ifdef _MSC_VER
//# pragma warning (disable:4390) // C4390: empty controlled statement found: is this the intent?
//#endif // _MSC_VER
//#ifdef __GNUC__
//# pragma GCC diagnostic ignored "-Wempty-body" // empty body in an if or else statement
//#endif // __GNUC__
#endif // DEBUG
#endif // SKDEBUG_H

94
vnr/sakurakit/skglobal.h Normal file
View File

@ -0,0 +1,94 @@
#ifndef SKGLOBAL_H
#define SKGLOBAL_H
// skglobal.h
// 9/15/2012 jichi
// Similar to QtGlobal from Qt.
//
// Conventions:
// - All classes in sakurakit will be wrapped with SK_BEGIN_NAMESPACE and SK_END_NAMESPACE
// - All classes from sakurakit begin with Sk, such as SkClassA.
// All functions from sakurakit begin with sk, such as skFuncA.
// Redefine SK_BEGIN_NAMESPACE/SK_END_NAMESPACE if need custom namespace
#ifndef SK_BEGIN_NAMESPACE
# define SK_BEGIN_NAMESPACE namespace Sk {
#endif
#ifndef SK_END_NAMESPACE
# define SK_END_NAMESPACE } // namespace Sk
#endif
#define SK_FORWARD_DECLARE_CLASS(_name) SK_BEGIN_NAMESPACE class _name; SK_END_NAMESPACE
#define SK_FORWARD_DECLARE_STRUCT(_name) SK_BEGIN_NAMESPACE struct _name; SK_END_NAMESPACE
SK_BEGIN_NAMESPACE
namespace Sk {}
SK_END_NAMESPACE
// In case Qt is not avaliable
//inline void sk_noop(void) {}
//
//template <typename T>
//inline void skUnused(T &x) { (void)x; }
#define SK_UNUSED(_var) (void)(_var)
#define SK_NOP SK_UNUSED(0)
// same as Q_DISABLE_COPY and boost::noncopyable
// Disable when BOOST_PYTHON is enabled
#ifdef BOOST_PYTHON
# define SK_DISABLE_COPY(_class)
#else
# define SK_DISABLE_COPY(_class) \
_class(const _class &); \
_class &operator=(const _class &);
#endif // BOOST_PYTHON
// - Qt-like Pimp -
// Similar to QT_DECLARE_PRIVATE
#define SK_DECLARE_PRIVATE(_class) \
friend class _class; \
typedef _class D; \
D *const d_;
// Similar to QT_DECLARE_PUBLIC
#define SK_DECLARE_PUBLIC(_class) \
friend class _class; \
typedef _class Q; \
Q *const q_;
// - Self and Base -
#define SK_CLASS(_self) \
typedef _self Self; \
Self *self() const { return const_cast<Self *>(this); }
#define SK_EXTEND_CLASS(_self, _base) \
SK_CLASS(_self) \
typedef _base Base;
#define SK_UNDEF_POS QPoint(-1, -1)
#define SK_UNDEF_POSF QPointF(-1, -1)
// - QWidget Style Class for QSS -
// Read-only property
#define SK_STYLE_CLASS(_class) \
Q_PROPERTY(QString class READ styleClass) \
public: \
QString styleClass() const { return #_class; } \
private:
// Read-write property
#define SK_SYNTHESIZE_STYLE_CLASS \
Q_PROPERTY(QString class READ styleClass WRITE setStyleClass) \
QString styleClass_; \
public: \
QString styleClass() const { return styleClass_; } \
public slots: \
void seStyleClass(const QString &value) { styleClass_ = value; } \
private:
#endif // SKGLOBAL_H

59
vnr/sakurakit/skhash.h Normal file
View File

@ -0,0 +1,59 @@
#ifndef SKHASH_H
#define SKHASH_H
// skhash.h
// 8/1/2011
#include "sakurakit/skglobal.h"
#include <QtGlobal>
SK_BEGIN_NAMESPACE
enum : quint64 { djb2_hash0 = 5381 };
/// djb2: h = h*33 + c
inline quint64 djb2(const quint8 *str, quint64 hash = djb2_hash0)
{
quint8 c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; // hash * 33 + c
return hash;
}
/// s: signed char
inline quint64 djb2_s(const char *str, quint64 hash = djb2_hash0)
{
char c;
while ((c = *str++))
hash = ((hash << 5) + hash) + c; // hash * 33 + c
return hash;
}
/// n: length
inline quint64 djb2_n(const quint8 *str, size_t len, quint64 hash = djb2_hash0)
{
while (len--)
hash = ((hash << 5) + hash) + (*str++); // hash * 33 + c
return hash;
}
/// sdbm: hash(i) = hash(i - 1) * 65599 + str[i];
inline quint64 sdbm(const quint8 *str, quint64 hash = 0)
{
quint8 c;
while ((c = *str++))
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
}
inline quint64 loselose(const quint8 *str, quint64 hash = 0)
{
quint8 c;
while ((c = *str++))
hash += c;
return hash;
}
SK_END_NAMESPACE
#endif // SKHASH_H

85
vnr/texthook/growl.h Normal file
View File

@ -0,0 +1,85 @@
#pragma once
// growl.h
// 9/17/2013 jichi
//#ifdef GROWL_HAS_GROWL
#include <windows.h>
#include <cstdio>
#define GROWL_MSG_A(_msg) MessageBoxA(nullptr, _msg, "VNR Message", MB_OK)
#define GROWL_MSG(_msg) MessageBoxW(nullptr, _msg, L"VNR Message", MB_OK)
#define GROWL_WARN(_msg) MessageBoxW(nullptr, _msg, L"VNR Warning", MB_OK)
#define GROWL_ERROR(_msg) MessageBoxW(nullptr, _msg, L"VNR Error", MB_OK)
inline void GROWL_DWORD(DWORD value)
{
WCHAR buf[100];
swprintf(buf, L"DWORD: %x", value);
GROWL_MSG(buf);
}
inline void GROWL_DWORD2(DWORD v, DWORD v2)
{
WCHAR buf[100];
swprintf(buf, L"DWORD2: %x,%x", v, v2);
GROWL_MSG(buf);
}
inline void GROWL_DWORD3(DWORD v, DWORD v2, DWORD v3)
{
WCHAR buf[100];
swprintf(buf, L"DWORD3: %x,%x,%x", v, v2, v3);
GROWL_MSG(buf);
}
inline void GROWL_DWORD4(DWORD v, DWORD v2, DWORD v3, DWORD v4)
{
WCHAR buf[100];
swprintf(buf, L"DWORD4: %x,%x,%x,%x", v, v2, v3, v4);
GROWL_MSG(buf);
}
inline void GROWL_DWORD5(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5)
{
WCHAR buf[100];
swprintf(buf, L"DWORD5: %x,%x,%x,%x,%x", v, v2, v3, v4, v5);
GROWL_MSG(buf);
}
inline void GROWL_DWORD6(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6)
{
WCHAR buf[100];
swprintf(buf, L"DWORD6: %x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6);
GROWL_MSG(buf);
}
inline void GROWL_DWORD7(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7)
{
WCHAR buf[100];
swprintf(buf, L"DWORD7: %x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7);
GROWL_MSG(buf);
}
inline void GROWL_DWORD8(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8)
{
WCHAR buf[100];
swprintf(buf, L"DWORD8: %x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8);
GROWL_MSG(buf);
}
inline void GROWL_DWORD9(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8, DWORD v9)
{
WCHAR buf[100];
swprintf(buf, L"DWORD9: %x,%x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8, v9);
GROWL_MSG(buf);
}
inline void GROWL(DWORD v) { GROWL_DWORD(v); }
inline void GROWL(LPCWSTR v) { GROWL_MSG(v); }
inline void GROWL(LPCSTR v) { GROWL_MSG_A(v); }
//#endif // GROWL_HAS_GROWL
// EOF

View File

@ -0,0 +1,69 @@
# texthook.pro
# CONFIG += noqtgui dll #eha # eha will catch all exceptions, but does not work on Windows XP
# DEFINES += ITH_HAS_CRT # Use native CRT
# # TODO: Get rid of dependence on msvc's swprintf
# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
set(vnrhost_src
avl_p.h
config.h
hookman.h
host.h
host_p.h
settings.h
textthread.h
textthread_p.h
hookman.cc
host.cc
pipe.cc
textthread.cc
${PROJECT_SOURCE_DIR}/winmaker/winmaker.h
${PROJECT_SOURCE_DIR}/winmaker/winmaker.cc
${PROJECT_SOURCE_DIR}/winmutex/winmutex.h
# ${PROJECT_SOURCE_DIR}/wintimer/wintimer.h
# ${PROJECT_SOURCE_DIR}/wintimer/wintimer.cc
# ${PROJECT_SOURCE_DIR}/wintimer/wintimerbase.cc
# ${PROJECT_SOURCE_DIR}/wintimer/wintimerbase.h
${PROJECT_SOURCE_DIR}/windbg/windbg.h
${PROJECT_SOURCE_DIR}/windbg/windbg_p.h
${PROJECT_SOURCE_DIR}/windbg/inject.h
${PROJECT_SOURCE_DIR}/windbg/inject.cc
${PROJECT_SOURCE_DIR}/windbg/hijack.h
${PROJECT_SOURCE_DIR}/windbg/hijack.cc
${PROJECT_SOURCE_DIR}/windbg/util.h
# ${PROJECT_SOURCE_DIR}/windbg/util.cc
${PROJECT_SOURCE_DIR}/windbg/unload.h
${PROJECT_SOURCE_DIR}/windbg/unload.cc
${PROJECT_SOURCE_DIR}/sakurakit/skdebug.h
)
add_library(vnrhost SHARED ${vnrhost_src})
set_target_properties(vnrhost PROPERTIES LINK_FLAGS /SUBSYSTEM:WINDOWS)
target_compile_options(vnrhost PRIVATE
# /GR-
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
#STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
target_link_libraries(vnrhost
ithsys
profile
${WDK_HOME}/lib/wxp/i386/ntdll.lib
)
target_compile_definitions(vnrhost
PRIVATE
ITH_HAS_CRT
_CRT_NON_CONFORMING_SWPRINTFS
)
install(TARGETS vnrhost RUNTIME
DESTINATION .
CONFIGURATIONS Release
)

588
vnr/texthook/host/avl_p.h Normal file
View File

@ -0,0 +1,588 @@
#pragma once
// avl_p.h
// 8/23/2013 jichi
// Branch: ITH/AVL.h, rev 133
#include "config.h"
#include <cstring>
enum { STACK_SIZE = 32 };
//#ifndef ITH_STACK
//#define ITH_STACK
template<class T, int stack_size>
class MyStack
{
int index;
T s[stack_size];
public:
MyStack(): index(0)
{ ::memset(s, 0, sizeof(s)); } // jichi 9/21/2013: assume T is atomic type
T &back() { return s[index-1]; }
int size() { return index; }
void push_back(const T &e)
{
if (index < stack_size)
s[index++]=e;
}
void pop_back() { index--; }
T &operator[](int i) { return s[i]; }
};
//#endif // ITH_STACK
// jichi 9/22/2013: T must be a pointer type which can be deleted
template <class T, class D>
struct IHFSERVICE TreeNode
{
//typedef TreeNode<T, D> Self;
TreeNode() :
Left(nullptr), Right(nullptr), Parent(nullptr)
, rank(1)
, factor('\0'), reserve('\0')
//, key()
//, data()
{
::memset(&key, 0, sizeof(key)); // jcihi 9/26/2013: zero memory
::memset(&data, 0, sizeof(data)); // jcihi 9/26/2013: zero memory
}
TreeNode(const T &k, const D &d) :
Left(nullptr), Right(nullptr), Parent(nullptr)
, rank(1)
, factor('\0'), reserve('\0') // jichi 9/21/2013: zero reserve
, key(k)
, data(d)
{}
TreeNode *Successor()
{
TreeNode *Node,
*ParentNode;
Node = Right;
if (!Node) {
Node = this;
for (;;) {
ParentNode = Node->Parent;
if (!ParentNode)
return nullptr;
if (ParentNode->Left == Node)
break;
Node = ParentNode;
}
return ParentNode;
}
else
while (Node->Left)
Node = Node->Left;
return Node;
}
TreeNode *Predecessor()
{
TreeNode *Node,
*ParentNode;
Node = Left;
if (!Node) {
Node = this;
for(;;) {
ParentNode = Node->Parent;
if (!ParentNode)
return nullptr;
if (ParentNode->Right == Node)
break;
Node = ParentNode;
}
return ParentNode;
}
else
while (Node->Right)
Node = Node->Right;
return Node;
}
int height()
{
if (!this) // jichi 9/26/2013: what?!
return 0;
int l = Left->height(),
r = Right->height(),
f = factor;
if (l - r + f != 0)
__debugbreak();
f = l > r ? l : r;
return f + 1;
}
TreeNode *Left,
*Right,
*Parent;
unsigned short rank;
char factor,
reserve;
T key;
D data;
};
template<class T,class D>
struct NodePath
{
NodePath() { ::memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH
NodePath(TreeNode<T,D> *n, int f): Node(n), fact(f) {}
TreeNode<T,D> *Node;
union { char factor; int fact; };
};
template <class T, class D, class fComp, class fCopy, class fLength>
class AVLTree
{
protected:
TreeNode<T*, D> head;
fComp fCmp;
fCopy fCpy;
fLength fLen;
public:
// - Construction -
AVLTree() {}
virtual ~AVLTree() { DeleteAll(); }
// - Properties -
TreeNode<T*, D> *TreeRoot() const { return head.Left; }
// - Actions -
void DeleteAll()
{
while (head.Left)
DeleteRoot();
}
TreeNode<T*, D> *Insert(const T *key, const D &data)
{
if (head.Left) {
MyStack<TreeNode<T*, D> *,STACK_SIZE> path;
TreeNode<T*,D> *DownNode, *ParentNode, *BalanceNode, *TryNode, *NewNode; //P,T,S,Q
ParentNode = &head;
path.push_back(ParentNode);
char factor,f;
BalanceNode = DownNode = head.Left;
for (;;) { //The first part of AVL tree insert. Just do as binary tree insert routine and record some nodes.
factor = fCmp(key,DownNode->key);
if (factor == 0)
return DownNode; //Duplicate key. Return and do nothing.
TryNode = _FactorLink(DownNode, factor);
if (factor == -1)
path.push_back(DownNode);
if (TryNode) { //DownNode has a child.
if (TryNode->factor != 0) { //Keep track of unbalance node and its parent.
ParentNode = DownNode;
BalanceNode = TryNode;
}
DownNode = TryNode;
}
else
break; //Finished binary tree search;
}
while (path.size()) {
path.back()->rank++;
path.pop_back();
}
size_t sz = fLen(key) + 1;
T *new_key = new T[sz];
::memset(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
fCpy(new_key, key);
TryNode = new TreeNode<T*, D>(new_key, data);
_FactorLink(DownNode, factor) = TryNode;
TryNode->Parent = DownNode;
NewNode = TryNode;
//Finished binary tree insert. Next to do is to modify balance factors between
//BalanceNode and the new node.
TreeNode<T*, D> *ModifyNode;
factor = fCmp(key, BalanceNode->key);
//factor=key<BalanceNode->key ? factor=-1:1; //Determine the balance factor at BalanceNode.
ModifyNode = DownNode = _FactorLink(BalanceNode,factor);
//ModifyNode will be the 1st child.
//DownNode will travel from here to the recent inserted node (TryNode).
while (DownNode != TryNode) { //Check if we reach the bottom.
f = fCmp(key,DownNode->key);
//f=_FactorCompare(key,DownNode->key);
DownNode->factor = f;
DownNode = _FactorLink(DownNode, f);//Modify balance factor and travels down.
}
//Finshed modifying balance factor.
//Next to do is check the tree if it's unbalance and recover balance.
if (BalanceNode->factor == 0) { //Tree has grown higher.
BalanceNode->factor = factor;
_IncreaseHeight(); //Modify balance factor and increase the height.
return NewNode;
}
if (BalanceNode->factor + factor == 0) { //Tree has gotten more balanced.
BalanceNode->factor = 0; //Set balance factor to 0.
return NewNode;
}
//Tree has gotten out of balance.
if (ModifyNode->factor == factor) //A node and its child has same factor. Single rotation.
DownNode = _SingleRotation(BalanceNode, ModifyNode, factor);
else //A node and its child has converse factor. Double rotation.
DownNode = _DoubleRotation(BalanceNode, ModifyNode, factor);
//Finished the balancing work. Set child field to the root of the new child tree.
if (BalanceNode == ParentNode->Left)
ParentNode->Left = DownNode;
else
ParentNode->Right = DownNode;
return NewNode;
}
else { //root null?
size_t sz = fLen(key) + 1;
T *new_key = new T[sz];
::memset(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
fCpy(new_key, key);
head.Left = new TreeNode<T *, D>(new_key, data);
head.rank++;
_IncreaseHeight();
return head.Left;
}
}
bool Delete(T *key)
{
NodePath<T*,D> PathNode;
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
path.push_back(NodePath<T*,D>(&head,-1));
TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
TryNode=head.Left;
char factor;
for (;;) { //Search for the
if (TryNode == 0)
return false; //Not found.
factor = fCmp(key, TryNode->key);
if (factor == 0)
break; //Key found, continue to delete.
//factor = _FactorCompare( key, TryNode->key );
path.push_back(NodePath<T*,D>(TryNode,factor));
TryNode = _FactorLink(TryNode,factor); //Move to left.
}
SuccNode = TryNode->Right; //Find a successor.
factor = 1;
if (SuccNode == 0) {
SuccNode = TryNode->Left;
factor = -1;
}
path.push_back(NodePath<T*,D>(TryNode,factor));
while (SuccNode) {
path.push_back(NodePath<T*,D>(SuccNode, -factor));
SuccNode = _FactorLink(SuccNode,-factor);
}
PathNode = path.back();
delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
TryNode->key = PathNode.Node->key; //Replace key and data field with the successor or predecessor.
PathNode.Node->key = nullptr;
TryNode->data = PathNode.Node->data;
path.pop_back();
_FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
delete PathNode.Node; //Remove the successor from the tree and release memory.
PathNode = path.back();
for (int i=0; i<path.size(); i++)
if (path[i].factor==-1)
path[i].Node->rank--;
for (;;) { //Rebalance the tree along the path back to the root.
if (path.size()==1) {
_DecreaseHeight();
break;
}
BalanceNode = PathNode.Node;
if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurve since subtree height stays.
BalanceNode->factor=-PathNode.factor;
break;
}
if (BalanceNode->factor == PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurve.
BalanceNode->factor = 0;
path.pop_back();
PathNode = path.back();
continue;
}
//Node get out of balance. Here raises 3 cases.
ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
if (ChildNode->factor == 0) { // New case different to insert operation.
TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
path.pop_back();
PathNode = path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
break;
}
else {
if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
}
path.pop_back(); //Recurse back along the path.
PathNode = path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
}
return true;
}
D &operator [](T *key)
{ return (Insert(key,D())->data); }
TreeNode<T*,D> *Search(const T *key)
{
TreeNode<T*,D> *Find=head.Left;
char k;
while (Find != 0) {//&&Find->key!=key)
k = fCmp(key, Find->key);
if (k == 0) break;
Find = _FactorLink(Find, k);
}
return Find;
}
TreeNode<T*,D> *SearchIndex(unsigned int rank)
{
unsigned int r = head.rank;
if (rank == -1)
return 0;
if (++rank>=r)
return 0;
TreeNode<T*,D> *n=&head;
while (r!=rank) {
if (rank>r) {
n=n->Right;
rank-=r;
r=n->rank;
} else {
n=n->Left;
r=n->rank;
}
}
return n;
}
TreeNode<T*,D> *Begin()
{
TreeNode<T*,D> *Node = head.Left;
if (Node)
while (Node->Left) Node = Node->Left;
return Node;
}
TreeNode<T*,D> *End()
{
TreeNode<T*,D> *Node=head.Left;
if (Node)
while (Node->Right) Node = Node->Right;
return Node;
}
unsigned int Count() const { return head.rank - 1; }
template <class Fn>
Fn TraverseTree(Fn &f)
{ return TraverseTreeNode(head.Left,f); }
protected:
bool DeleteRoot()
{
NodePath<T*,D> PathNode;
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
path.push_back(NodePath<T*,D>(&head,-1));
TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
TryNode=head.Left;
char factor;
SuccNode=TryNode->Right; //Find a successor.
factor=1;
if (SuccNode==0)
{
SuccNode=TryNode->Left;
factor=-1;
}
path.push_back(NodePath<T*,D>(TryNode,factor));
while (SuccNode) {
path.push_back(NodePath<T*,D>(SuccNode,-factor));
SuccNode=_FactorLink(SuccNode,-factor);
}
PathNode=path.back();
delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
TryNode->key=PathNode.Node->key; //Replace key and data field with the successor.
PathNode.Node->key = nullptr;
TryNode->data=PathNode.Node->data;
path.pop_back();
_FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
delete PathNode.Node; //Remove the successor from the tree and release memory.
PathNode=path.back();
for (int i=0;i<path.size();i++)
if (path[i].factor==-1)
path[i].Node->rank--;
for (;;) { //Rebalance the tree along the path back to the root.
if (path.size() == 1) {
_DecreaseHeight();
break;
}
BalanceNode = PathNode.Node;
if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurse since subtree height not changed.
BalanceNode->factor=-PathNode.factor;
break;
}
if (BalanceNode->factor==PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurse.
BalanceNode->factor=0;
path.pop_back();
PathNode=path.back();
continue;
}
//Node get out of balance. Here raises 3 cases.
ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
if (ChildNode->factor == 0) { // New case different to insert operation.
TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
path.pop_back();
PathNode=path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
break;
} else {
if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
}
path.pop_back(); // Recurve back along the path.
PathNode=path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
}
return true;
}
template <class Fn>
Fn TraverseTreeNode(TreeNode<T*,D> *Node, Fn &f)
{
if (Node) {
if (Node->Left)
TraverseTreeNode(Node->Left,f);
f(Node);
if (Node->Right)
TraverseTreeNode(Node->Right,f);
}
return f;
}
TreeNode<T*,D> *_SingleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
{
TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
_FactorLink(BalanceNode, factor) = Node;
_FactorLink(ModifyNode, -factor) = BalanceNode;
if (Node)
Node->Parent = BalanceNode;
ModifyNode->Parent = BalanceNode->Parent;
BalanceNode->Parent = ModifyNode;
BalanceNode->factor = ModifyNode->factor = 0; //After single rotation, set all factor of 3 node to 0.
if (factor == 1)
ModifyNode->rank += BalanceNode->rank;
else
BalanceNode->rank -= ModifyNode->rank;
return ModifyNode;
}
TreeNode<T*,D> *_SingleRotation2(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
{
TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
_FactorLink(BalanceNode, factor) = Node;
_FactorLink(ModifyNode, -factor) = BalanceNode;
if (Node) Node->Parent = BalanceNode;
ModifyNode->Parent = BalanceNode->Parent;
BalanceNode->Parent = ModifyNode;
ModifyNode->factor = -factor;
if (factor == 1)
ModifyNode->rank+=BalanceNode->rank;
else
BalanceNode->rank-=ModifyNode->rank;
return ModifyNode;
}
TreeNode<T*,D> *_DoubleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
{
TreeNode<T*,D> *DownNode = _FactorLink(ModifyNode, -factor);
TreeNode<T*,D> *Node1, *Node2;
Node1 = _FactorLink(DownNode, factor);
Node2 = _FactorLink(DownNode, -factor);
_FactorLink(ModifyNode, -factor) = Node1;
_FactorLink(DownNode, factor) = ModifyNode;
_FactorLink(BalanceNode, factor) = Node2;
_FactorLink(DownNode, -factor) = BalanceNode;
if (Node1)
Node1->Parent = ModifyNode;
if (Node2)
Node2->Parent = BalanceNode;
DownNode->Parent = BalanceNode->Parent;
BalanceNode->Parent = DownNode;
ModifyNode->Parent = DownNode;
//Set factor according to the result.
if (DownNode->factor == factor) {
BalanceNode->factor = -factor;
ModifyNode->factor = 0;
} else if (DownNode->factor == 0)
BalanceNode->factor = ModifyNode->factor = 0;
else {
BalanceNode->factor = 0;
ModifyNode->factor = factor;
}
DownNode->factor = 0;
if (factor==1) {
ModifyNode->rank -= DownNode->rank;
DownNode->rank += BalanceNode->rank;
} else {
DownNode->rank += ModifyNode->rank;
BalanceNode->rank -= DownNode->rank;
}
return DownNode;
}
TreeNode<T*,D>* &__fastcall _FactorLink(TreeNode<T*,D> *Node, char factor)
//Private helper method to retrieve child according to factor.
//Return right child if factor>0 and left child otherwise.
{ return factor>0? Node->Right : Node->Left; }
void Check()
{
unsigned int k = (unsigned int)head.Right;
unsigned int t = head.Left->height();
if (k != t)
__debugbreak();
}
void _IncreaseHeight()
{
unsigned int k = (unsigned int)head.Right;
head.Right = (TreeNode<T*,D>*)++k;
}
void _DecreaseHeight()
{
unsigned int k = (unsigned int)head.Right;
head.Right = (TreeNode<T*,D>*)--k;
}
};
struct SCMP
{
char operator()(const char *s1,const char *s2)
{
int t = _stricmp(s1, s2);
return t == 0 ? 0 : t > 0 ? 1 :-1;
}
};
struct SCPY { char *operator()(char *dest, const char *src) { return strcpy(dest, src); } };
struct SLEN { int operator()(const char *str) { return strlen(str); } };
struct WCMP
{
char operator()(const wchar_t *s1,const wchar_t *s2)
{
int t =_wcsicmp(s1, s2);
return t == 0 ? 0 : t > 0 ? 1 : -1;
}
};
struct WCPY { wchar_t *operator()(wchar_t *dest, const wchar_t *src) { return wcscpy(dest,src); } };
struct WLEN { int operator()(const wchar_t *str) { return wcslen(str); } };
// EOF

View File

@ -0,0 +1,16 @@
#pragma once
// config.h
// 8/23/2013 jichi
// The first header file that are included by all source files.
#define IHF // for dll import
//#include "ith/dllconfig.h"
#define IHFAPI __stdcall
#ifdef IHF
# define IHFSERVICE __declspec(dllexport)
#else
# define IHFSERVICE __declspec(dllimport)
#endif
// EOF

1039
vnr/texthook/host/hookman.cc Normal file

File diff suppressed because it is too large Load Diff

153
vnr/texthook/host/hookman.h Normal file
View File

@ -0,0 +1,153 @@
#pragma once
// hookman.h
// 8/23/2013 jichi
// Branch: ITH/HookManager.h, rev 133
#include "host/avl_p.h"
#include "host/textthread.h"
#include "winmutex/winmutex.h"
namespace pugi {
class xml_node;
}
class Profile;
enum { MAX_REGISTER = 0xf };
enum { MAX_PREV_REPEAT_LENGTH = 0x20 };
struct ProcessRecord {
DWORD pid_register;
DWORD hookman_register;
DWORD module_register;
//DWORD engine_register; // jichi 10/19/2014: removed
HANDLE process_handle;
HANDLE hookman_mutex;
HANDLE hookman_section;
LPVOID hookman_map;
};
class ThreadTable : public MyVector<TextThread *, 0x40>
{
public:
virtual void SetThread(DWORD number, TextThread *ptr);
virtual TextThread *FindThread(DWORD number);
};
struct IHFSERVICE TCmp { char operator()(const ThreadParameter *t1, const ThreadParameter *t2); };
struct IHFSERVICE TCpy { void operator()(ThreadParameter *t1, const ThreadParameter *t2); };
struct IHFSERVICE TLen { int operator()(const ThreadParameter *t); };
typedef DWORD (*ProcessEventCallback)(DWORD pid);
class IHFSERVICE HookManager : public AVLTree<ThreadParameter, DWORD, TCmp, TCpy, TLen>
{
public:
HookManager();
~HookManager();
// jichi 12/26/2013: remove virtual modifiers
TextThread *FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split);
TextThread *FindSingle(DWORD number);
ProcessRecord *GetProcessRecord(DWORD pid);
DWORD GetProcessIDByPath(LPCWSTR str); // private
void RemoveSingleThread(DWORD number);
//void LockHookman();
//void UnlockHookman();
void ResetRepeatStatus();
void ClearCurrent();
void AddLink(WORD from, WORD to);
void UnLink(WORD from);
void UnLinkAll(WORD from);
void SelectCurrent(DWORD num);
void DetachProcess(DWORD pid);
void SetCurrent(TextThread *it);
void AddConsoleOutput(LPCWSTR text);
// jichi 10/27/2013: Add const; add space.
void DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD split, int len, bool space);
void ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD split); // private
void RemoveProcessContext(DWORD pid); // private
void RemoveSingleHook(DWORD pid, DWORD addr);
void RegisterThread(TextThread*, DWORD); // private
void RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread);
void RegisterProcess(DWORD pid, DWORD hookman, DWORD module);
void UnRegisterProcess(DWORD pid);
//void SetName(DWORD);
DWORD GetCurrentPID(); // private
HANDLE GetCmdHandleByPID(DWORD pid);
ConsoleCallback RegisterConsoleCallback(ConsoleCallback cf)
{ return (ConsoleCallback)_InterlockedExchange((long*)&console,(long)cf); }
ConsoleWCallback RegisterConsoleWCallback(ConsoleWCallback cf)
{ return (ConsoleWCallback)_InterlockedExchange((long*)&wconsole,(long)cf); }
ThreadEventCallback RegisterThreadCreateCallback(ThreadEventCallback cf)
{ return (ThreadEventCallback)_InterlockedExchange((long*)&create,(long)cf); }
ThreadEventCallback RegisterThreadRemoveCallback(ThreadEventCallback cf)
{ return (ThreadEventCallback)_InterlockedExchange((long*)&remove,(long)cf); }
ThreadEventCallback RegisterThreadResetCallback(ThreadEventCallback cf)
{ return (ThreadEventCallback)_InterlockedExchange((long*)&reset,(long)cf); }
ThreadEventCallback RegisterAddRemoveLinkCallback(ThreadEventCallback cf)
{ return (ThreadEventCallback)_InterlockedExchange((long*)&addRemoveLink, (long)cf); }
ProcessEventCallback RegisterProcessAttachCallback(ProcessEventCallback cf)
{ return (ProcessEventCallback)_InterlockedExchange((long*)&attach,(long)cf); }
ProcessEventCallback RegisterProcessDetachCallback(ProcessEventCallback cf)
{ return (ProcessEventCallback)_InterlockedExchange((long*)&detach,(long)cf); }
ProcessEventCallback RegisterProcessNewHookCallback(ProcessEventCallback cf)
{ return (ProcessEventCallback)_InterlockedExchange((long*)&hook,(long)cf); }
ProcessEventCallback ProcessNewHook() { return hook; }
TextThread *GetCurrentThread() { return current; } // private
ProcessRecord *Records() { return record; } // private
ThreadTable *Table() { return thread_table; } // private
//DWORD& SplitTime() { return split_time; }
//DWORD& RepeatCount() { return repeat_count; }
//DWORD& CyclicRemove() { return cyclic_remove; }
//DWORD& GlobalFilter() { return global_filter; }
void ConsoleOutput(LPCSTR text) { if (console) console(text); } // not thread safe
void ConsoleOutputW(LPCWSTR text) { if (wconsole) wconsole(text); } // not thread safe
void OnThreadCreate(pugi::xml_node profile_node, TextThread* thread);
void GetProfile(DWORD pid, pugi::xml_node profile_node);
private:
typedef win_mutex<CRITICAL_SECTION> mutex_type;
mutex_type hmcs;
TextThread *current;
ConsoleCallback console; // jichi 12/25/2013: add console output callback
ConsoleWCallback wconsole;
ThreadEventCallback create,
remove,
reset,
addRemoveLink;
ProcessEventCallback attach,
detach,
hook;
DWORD current_pid;
ThreadTable *thread_table;
HANDLE destroy_event;
ProcessRecord record[MAX_REGISTER + 1];
HANDLE text_pipes[MAX_REGISTER + 1],
cmd_pipes[MAX_REGISTER + 1],
recv_threads[MAX_REGISTER + 1];
WORD register_count,
new_thread_number;
// jichi 1/16/2014: Stop adding new threads when full
bool IsFull() const; // { return new_thread_number >= MAX_HOOK; }
bool IsEmpty() const { return !new_thread_number; }
void HookManager::AddThreadsToProfile(Profile& pf, const ProcessRecord& pr, DWORD pid);
};
// EOF

646
vnr/texthook/host/host.cc Normal file
View File

@ -0,0 +1,646 @@
// host.cc
// 8/24/2013 jichi
// Branch IHF/main.cpp, rev 111
// 8/24/2013 TODO: Clean up this file
//#ifdef _MSC_VER
//# pragma warning(disable:4800) // C4800: forcing value to bool (performance warning)
//#endif // _MSC_VER
//#include "customfilter.h"
#include "growl.h"
#include "host.h"
#include "host_p.h"
#include "settings.h"
#include "vnrhook/include/const.h"
#include "vnrhook/include/defs.h"
#include "vnrhook/include/types.h"
#include "ithsys/ithsys.h"
#include "windbg/inject.h"
#include "winmaker/winmaker.h"
#include "ccutil/ccmacro.h"
#include <commctrl.h>
//#define ITH_WINE
//#define ITH_USE_UX_DLLS IthIsWine()
//#define ITH_USE_XP_DLLS (IthIsWindowsXp() && !IthIsWine())
#define DEBUG "vnrhost/host.cc"
#include "sakurakit/skdebug.h"
namespace { // unnamed
//enum { HOOK_TIMEOUT = -50000000 }; // in nanoseconds = 5 seconds
CRITICAL_SECTION cs;
//WCHAR exist[] = ITH_PIPEEXISTS_EVENT;
//WCHAR mutex[] = L"ITH_RUNNING";
//WCHAR EngineName[] = ITH_ENGINE_DLL;
//WCHAR EngineNameXp[] = ITH_ENGINE_XP_DLL;
//WCHAR DllName[] = ITH_CLIENT_DLL;
//WCHAR DllNameXp[] = ITH_CLIENT_XP_DLL;
HANDLE hServerMutex; // jichi 9/28/2013: used to guard pipe
HANDLE hHookMutex; // jichi 9/28/2013: used to guard hook modification
} // unnamed namespace
//extern LPWSTR current_dir;
extern CRITICAL_SECTION detach_cs;
Settings *settings;
HWND hMainWnd;
HANDLE hPipeExist;
BOOL running;
#define ITH_SYNC_HOOK IthMutexLocker locker(::hHookMutex)
namespace { // unnamed
void GetDebugPriv()
{
HANDLE hToken;
DWORD dwRet;
NTSTATUS status;
TOKEN_PRIVILEGES Privileges = {1,{0x14,0,SE_PRIVILEGE_ENABLED}};
NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
status = NtAdjustPrivilegesToken(hToken, 0, &Privileges, sizeof(Privileges), 0, &dwRet);
//if (STATUS_SUCCESS == status)
//{
// admin = 1;
//}
//else
//{
// WCHAR buffer[0x10];
// swprintf(buffer, L"%.8X",status);
// MessageBox(0, NotAdmin, buffer, 0);
//}
NtClose(hToken);
}
bool sendCommand(HANDLE hCmd, HostCommandType cmd)
{
IO_STATUS_BLOCK ios;
//SendParam sp = {};
//sp.type = cmd;
DWORD data = cmd;
return hCmd && NT_SUCCESS(NtWriteFile(hCmd, 0,0,0, &ios, &data, sizeof(data), 0,0));
}
// jichi 9/22/2013: Change current directory to the same as main module path
// Otherwise NtFile* would not work for files with relative paths.
//BOOL ChangeCurrentDirectory()
//{
// if (const wchar_t *path = GetMainModulePath()) // path to VNR's python exe
// if (const wchar_t *base = wcsrchr(path, L'\\')) {
// size_t len = base - path;
// if (len < MAX_PATH) {
// wchar_t buf[MAX_PATH];
// wcsncpy(buf, path, len);
// buf[len] = 0;
// return SetCurrentDirectoryW(buf);
// }
// }
// return FALSE;
//}
#if 0
bool injectUsingWin32Api(LPCWSTR path, HANDLE hProc)
{ return WinDbg::injectDllW(path, 0, hProc); }
bool ejectUsingWin32Api(HANDLE hModule, HANDLE hProc)
{ return WinDbg::ejectDll(hModule, hProc); }
// The original inject logic in ITH
bool injectUsingNTApi(LPCWSTR path, HANDLE hProc)
{
LPVOID lpvAllocAddr = 0;
DWORD dwWrite = 0x1000; //, len = 0;
//if (IthIsWine())
// lpvAllocAddr = VirtualAllocEx(hProc, nullptr, dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
//else
NtAllocateVirtualMemory(hProc, &lpvAllocAddr, 0, &dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
if (!lpvAllocAddr)
return false;
CheckThreadStart();
//Copy module path into address space of target process.
//if (IthIsWine())
// WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
//else
NtWriteVirtualMemory(hProc, lpvAllocAddr, (LPVOID)path, MAX_PATH << 1, &dwWrite);
HANDLE hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc);
if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) {
DOUT("ERROR: failed to create remote cli thread");
//ConsoleOutput(ErrorRemoteThread);
return false;
}
// jichi 9/28/2013: no wait as it will not blocked
NtWaitForSingleObject(hTH, 0, nullptr);
THREAD_BASIC_INFORMATION info;
NtQueryInformationThread(hTH, ThreadBasicInformation, &info, sizeof(info), &dwWrite);
NtClose(hTH);
// jichi 10/19/2014: Disable inject the second dll
//if (info.ExitStatus) {
// //IthCoolDown();
// wcscpy(p, engine);
// //if (IthIsWine())
// // WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
// //else
// NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
// hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc);
// if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) {
// //ConsoleOutput(ErrorRemoteThread);
// ConsoleOutput("vnrhost:inject: ERROR: failed to create remote eng thread");
// return error;
// }
//
// // jichi 9/28/2013: no wait as it will not blocked
// NtWaitForSingleObject(hTH, 0, nullptr);
// NtClose(hTH);
//}
dwWrite = 0;
//if (IthIsWine())
// VirtualFreeEx(hProc, lpvAllocAddr, dwWrite, MEM_RELEASE);
//else
NtFreeVirtualMemory(hProc, &lpvAllocAddr, &dwWrite, MEM_RELEASE);
return info.ExitStatus != -1;
}
bool ejectUsingNTApi(HANDLE hModule, HANDLE hProc)
{
//IthCoolDown();
//#ifdef ITH_WINE // Nt series crash on wine
// hThread = IthCreateThread(FreeLibrary, engine, hProc);
//#else
HANDLE hThread = IthCreateThread(LdrUnloadDll, module, hProc);
//#endif // ITH_WINE
if (hThread == 0 || hThread == INVALID_HANDLE_VALUE)
return false;
// jichi 10/22/2013: Timeout might crash vnrsrv
//NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout);
NtWaitForSingleObject(hThread, 0, nullptr);
//man->UnlockHookman();
THREAD_BASIC_INFORMATION info;
NtQueryInformationThread(hThread, ThreadBasicInformation, &info, sizeof(info), 0);
NtClose(hThread);
NtSetEvent(hPipeExist, 0);
FreeThreadStart(hProc);
return info.ExitStatus;
}
#endif // 0
bool Inject(HANDLE hProc)
{
//LPWSTR dllname = (IthIsWindowsXp() && !IthIsWine()) ? DllNameXp : DllName;
//LPCWSTR dllname = ITH_USE_XP_DLLS ? ITH_DLL_XP : ITH_DLL;
//LPCWSTR dllname = ITH_DLL;
//if (!IthCheckFile(dllname))
// return error;
wchar_t path[MAX_PATH];
size_t len = IthGetCurrentModulePath(path, MAX_PATH);
if (!len)
return false;
wchar_t *p;
for (p = path + len; *p != L'\\'; p--);
p++; // ending with L"\\"
//LPCWSTR mp = GetMainModulePath();
//len = wcslen(mp);
//memcpy(path, mp, len << 1);
//memset(path + len, 0, (MAX_PATH - len) << 1);
//LPWSTR p;
//for (p = path + len; *p != L'\\'; p--); // Always a \ after drive letter.
//p++;
::wcscpy(p, ITH_DLL);
return WinDbg::injectDllW(path, 0, hProc);
//if (IthIsWindowsXp()) // && !IthIsWine())
// return injectUsingWin32Api(path, hProc);
//else
// return injectUsingNTApi(path, hProc);
}
} // unnamed namespace
void CreateNewPipe();
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
CC_UNUSED(lpvReserved);
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
LdrDisableThreadCalloutsForDll(hinstDLL);
InitializeCriticalSection(&::cs);
IthInitSystemService();
GetDebugPriv();
// jichi 12/20/2013: Since I already have a GUI, I don't have to InitCommonControls()
//Used by timers.
InitCommonControls();
// jichi 8/24/2013: Create hidden window so that ITH can access timer and events
hMainWnd = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0);
//wm_register_hidden_class("vnrsrv.class");
//hMainWnd = (HWND)wm_create_hidden_window("vnrsrv", "Button", hinstDLL);
//ChangeCurrentDirectory();
break;
case DLL_PROCESS_DETACH:
if (::running)
Host_Close();
DeleteCriticalSection(&::cs);
IthCloseSystemService();
//wm_destroy_window(hMainWnd);
DestroyWindow(hMainWnd);
break;
default:
break;
}
return true;
}
HANDLE IthOpenPipe(LPWSTR name, ACCESS_MASK direction)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
SECURITY_DESCRIPTOR sd = {1};
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
if (NT_SUCCESS(NtCreateFile(&hFile, direction, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0)))
return hFile;
else
return INVALID_HANDLE_VALUE;
}
enum { IHS_SIZE = 0x80 };
enum { IHS_BUFF_SIZE = IHS_SIZE - sizeof(HookParam) };
struct InsertHookStruct
{
SendParam sp;
BYTE name_buffer[IHS_SIZE];
};
IHFSERVICE void IHFAPI Host_Init()
{
InitializeCriticalSection(&::cs);
GetDebugPriv();
}
IHFSERVICE void IHFAPI Host_Destroy()
{
InitializeCriticalSection(&::cs);
}
IHFSERVICE BOOL IHFAPI Host_Open()
{
BOOL result = false;
EnterCriticalSection(&::cs);
DWORD present;
hServerMutex = IthCreateMutex(ITH_SERVER_MUTEX, 1, &present);
if (present)
//MessageBox(0,L"Already running.",0,0);
// jichi 8/24/2013
GROWL_WARN(L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!");
else if (!::running) {
::running = true;
::settings = new Settings;
::man = new HookManager;
//cmdq = new CommandQueue;
InitializeCriticalSection(&detach_cs);
::hHookMutex = IthCreateMutex(ITH_SERVER_HOOK_MUTEX, FALSE);
result = true;
}
LeaveCriticalSection(&::cs);
return result;
}
IHFSERVICE DWORD IHFAPI Host_Start()
{
//IthBreak();
CreateNewPipe();
::hPipeExist = IthCreateEvent(ITH_PIPEEXISTS_EVENT);
NtSetEvent(::hPipeExist, nullptr);
return 0;
}
IHFSERVICE DWORD IHFAPI Host_Close()
{
BOOL result = FALSE;
EnterCriticalSection(&::cs);
if (::running) {
::running = FALSE;
HANDLE hRecvPipe = IthOpenPipe(recv_pipe, GENERIC_WRITE);
NtClose(hRecvPipe);
NtClearEvent(::hPipeExist);
//delete cmdq;
delete man;
delete settings;
NtClose(::hHookMutex);
NtClose(hServerMutex);
NtClose(::hPipeExist);
DeleteCriticalSection(&detach_cs);
result = TRUE;
}
LeaveCriticalSection(&::cs);
return result;
}
IHFSERVICE DWORD IHFAPI Host_GetPIDByName(LPCWSTR pwcTarget)
{
DWORD dwSize = 0x20000,
dwExpectSize = 0;
LPVOID pBuffer = 0;
SYSTEM_PROCESS_INFORMATION *spiProcessInfo;
DWORD dwPid = 0;
DWORD dwStatus;
NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize);
if (!NT_SUCCESS(dwStatus)) {
NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE);
if (dwStatus != STATUS_INFO_LENGTH_MISMATCH || dwExpectSize < dwSize)
return 0;
dwSize = (dwExpectSize | 0xFFF) + 0x4001; //
pBuffer = 0;
NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize);
if (!NT_SUCCESS(dwStatus)) goto _end;
}
for (spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *)pBuffer; spiProcessInfo->dNext;) {
spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *)
((DWORD)spiProcessInfo + spiProcessInfo -> dNext);
if (_wcsicmp(pwcTarget, spiProcessInfo -> usName.Buffer) == 0) {
dwPid = spiProcessInfo->dUniqueProcessId;
break;
}
}
if (!dwPid)
DOUT("pid not found");
//if (dwPid == 0) ConsoleOutput(ErrorNoProcess);
_end:
NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE);
return dwPid;
}
IHFSERVICE bool IHFAPI Host_InjectByPID(DWORD pid)
{
WCHAR str[0x80];
if (!::running)
return 0;
if (pid == current_process_id) {
//ConsoleOutput(SelfAttach);
DOUT("refuse to inject myself");
return false;
}
if (man->GetProcessRecord(pid)) {
//ConsoleOutput(AlreadyAttach);
DOUT("already attached");
return false;
}
swprintf(str, ITH_HOOKMAN_MUTEX_ L"%d", pid);
DWORD s;
NtClose(IthCreateMutex(str, 0, &s));
if (s) {
DOUT("already locked");
return false;
}
CLIENT_ID id;
OBJECT_ATTRIBUTES oa = {};
HANDLE hProc;
id.UniqueProcess = pid;
id.UniqueThread = 0;
oa.uLength = sizeof(oa);
if (!NT_SUCCESS(NtOpenProcess(&hProc,
PROCESS_QUERY_INFORMATION|
PROCESS_CREATE_THREAD|
PROCESS_VM_OPERATION|
PROCESS_VM_READ|
PROCESS_VM_WRITE,
&oa, &id))) {
//ConsoleOutput(ErrorOpenProcess);
DOUT("failed to open process");
return false;
}
//if (!engine)
// engine = ITH_USE_XP_DLLS ? ITH_ENGINE_XP_DLL : ITH_ENGINE_DLL;
bool ok = Inject(hProc);
//NtClose(hProc); //already closed
if (!ok) {
DOUT("inject failed");
return false;
}
//swprintf(str, FormatInject, pid, module);
//ConsoleOutput(str);
DOUT("inject succeed");
return true;
}
// jichi 7/16/2014: Test if process is valid before creating remote threads
// See: http://msdn.microsoft.com/en-us/library/ms687032.aspx
static bool isProcessTerminated(HANDLE hProc)
{ return WAIT_OBJECT_0 == ::WaitForSingleObject(hProc, 0); }
//static bool isProcessRunning(HANDLE hProc)
//{ return WAIT_TIMEOUT == ::WaitForSingleObject(hProc, 0); }
// jichi 7/16/2014: Test if process is valid before creating remote threads
//static bool isProcessRunning(DWORD pid)
//{
// bool ret = false;
// HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
// if (hProc) {
// DWORD status;
// if (::GetExitCodeProcess(hProc, &status)) {
// ret = status == STILL_ACTIVE;
// ::CloseHandle(hProc);
// } else
// ret = true;
// }
// return ret;
//}
IHFSERVICE bool IHFAPI Host_ActiveDetachProcess(DWORD pid)
{
ITH_SYNC_HOOK;
//man->LockHookman();
ProcessRecord *pr = man->GetProcessRecord(pid);
HANDLE hCmd = man->GetCmdHandleByPID(pid);
if (pr == 0 || hCmd == 0)
return false;
HANDLE hProc;
//hProc = pr->process_handle; //This handle may be closed(thus invalid) during the detach process.
NtDuplicateObject(NtCurrentProcess(), pr->process_handle,
NtCurrentProcess(), &hProc, 0, 0, DUPLICATE_SAME_ACCESS); // Make a copy of the process handle.
HANDLE hModule = (HANDLE)pr->module_register;
if (!hModule) {
DOUT("process module not found");
return false;
}
// jichi 7/15/2014: Process already closed
if (isProcessTerminated(hProc)) {
DOUT("process has terminated");
return false;
}
// jichi 10/19/2014: Disable the second dll
//engine = pr->engine_register;
//engine &= ~0xff;
DOUT("send detach command");
bool ret = sendCommand(hCmd, HOST_COMMAND_DETACH);
// jichi 7/15/2014: Process already closed
//if (isProcessTerminated(hProc)) {
// DOUT("process has terminated");
// return false;
//}
//WinDbg::ejectDll(hModule, 0, hProc); // eject in case module has not loaded yet
//cmdq->AddRequest(sp, pid);
////#ifdef ITH_WINE // Nt series crash on wine
//// hThread = IthCreateThread(FreeLibrary, engine, hProc);
////#else
// hThread = IthCreateThread(LdrUnloadDll, engine, hProc);
////#endif // ITH_WINE
// if (hThread == 0 || hThread == INVALID_HANDLE_VALUE)
// return FALSE;
// // jichi 10/22/2013: Timeout might crash vnrsrv
// //const LONGLONG timeout = HOOK_TIMEOUT;
// //NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout);
// NtWaitForSingleObject(hThread, 0, nullptr);
// NtClose(hThread);
NtClose(hProc);
return ret;
}
IHFSERVICE DWORD IHFAPI Host_GetHookManager(HookManager** hookman)
{
if (::running) {
*hookman = man;
return 0;
}
else
return 1;
}
IHFSERVICE bool IHFAPI Host_GetSettings(Settings **p)
{
if (::running) {
*p = settings;
return true;
}
else
return false;
}
IHFSERVICE bool IHFAPI Host_HijackProcess(DWORD pid)
{
//ITH_SYNC_HOOK;
HANDLE hCmd = man->GetCmdHandleByPID(pid);
return hCmd && sendCommand(hCmd, HOST_COMMAND_HIJACK_PROCESS);
}
IHFSERVICE DWORD IHFAPI Host_InsertHook(DWORD pid, HookParam *hp, LPCSTR name)
{
ITH_SYNC_HOOK;
HANDLE hCmd = man->GetCmdHandleByPID(pid);
if (hCmd == 0)
return -1;
InsertHookStruct s;
s.sp.type = HOST_COMMAND_NEW_HOOK;
s.sp.hp = *hp;
size_t len;
if (name)
len = ::strlen(name);
else
len = 0;
if (len) {
if (len >= IHS_BUFF_SIZE) len = IHS_BUFF_SIZE - 1;
memcpy(s.name_buffer, name, len);
}
s.name_buffer[len] = 0;
IO_STATUS_BLOCK ios;
NtWriteFile(hCmd, 0,0,0, &ios, &s, IHS_SIZE, 0, 0);
//memcpy(&sp.hp,hp,sizeof(HookParam));
//cmdq->AddRequest(sp, pid);
return 0;
}
IHFSERVICE DWORD IHFAPI Host_ModifyHook(DWORD pid, HookParam *hp)
{
ITH_SYNC_HOOK;
HANDLE hCmd = GetCmdHandleByPID(pid);
if (hCmd == 0)
return -1;
HANDLE hModify = IthCreateEvent(ITH_MODIFYHOOK_EVENT);
SendParam sp;
sp.type = HOST_COMMAND_MODIFY_HOOK;
sp.hp = *hp;
IO_STATUS_BLOCK ios;
if (NT_SUCCESS(NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam), 0, 0)))
// jichi 9/28/2013: no wait timeout
//const LONGLONG timeout = HOOK_TIMEOUT;
NtWaitForSingleObject(hModify, 0, nullptr);
NtClose(hModify);
man->RemoveSingleHook(pid, sp.hp.address);
return 0;
}
IHFSERVICE DWORD IHFAPI Host_RemoveHook(DWORD pid, DWORD addr)
{
ITH_SYNC_HOOK;
HANDLE hRemoved,hCmd;
hCmd = GetCmdHandleByPID(pid);
if (hCmd == 0)
return -1;
hRemoved = IthCreateEvent(ITH_REMOVEHOOK_EVENT);
SendParam sp = {};
IO_STATUS_BLOCK ios;
sp.type = HOST_COMMAND_REMOVE_HOOK;
sp.hp.address = addr;
//cmdq -> AddRequest(sp, pid);
NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam),0,0);
// jichi 10/22/2013: Timeout might crash vnrsrv
//const LONGLONG timeout = HOOK_TIMEOUT;
//NtWaitForSingleObject(hRemoved, 0, (PLARGE_INTEGER)&timeout);
NtWaitForSingleObject(hRemoved, 0, nullptr);
NtClose(hRemoved);
man -> RemoveSingleHook(pid, sp.hp.address);
return 0;
}
// 4/30/2015: Removed as not needed. Going to change to json
IHFSERVICE DWORD IHFAPI Host_AddLink(DWORD from, DWORD to)
{
man->AddLink(from & 0xffff, to & 0xffff);
return 0;
}
IHFSERVICE DWORD IHFAPI Host_UnLink(DWORD from)
{
man->UnLink(from & 0xffff);
return 0;
}
IHFSERVICE DWORD IHFAPI Host_UnLinkAll(DWORD from)
{
man->UnLinkAll(from & 0xffff);
return 0;
}
// EOF

33
vnr/texthook/host/host.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
// host.h
// 8/23/2013 jichi
// Branch: ITH/IHF.h, rev 105
//#include "host/settings.h"
#include "config.h"
#include "host/hookman.h"
struct Settings;
struct HookParam;
IHFSERVICE void IHFAPI Host_Init();
IHFSERVICE void IHFAPI Host_Destroy();
IHFSERVICE DWORD IHFAPI Host_Start();
IHFSERVICE BOOL IHFAPI Host_Open();
IHFSERVICE DWORD IHFAPI Host_Close();
IHFSERVICE DWORD IHFAPI Host_GetHookManager(HookManager **hookman);
IHFSERVICE bool IHFAPI Host_GetSettings(Settings **settings);
IHFSERVICE DWORD IHFAPI Host_GetPIDByName(LPCWSTR pwcTarget);
IHFSERVICE bool IHFAPI Host_InjectByPID(DWORD pid);
IHFSERVICE bool IHFAPI Host_ActiveDetachProcess(DWORD pid);
IHFSERVICE bool IHFAPI Host_HijackProcess(DWORD pid);
IHFSERVICE DWORD IHFAPI Host_InsertHook(DWORD pid, HookParam *hp, LPCSTR name = nullptr);
IHFSERVICE DWORD IHFAPI Host_ModifyHook(DWORD pid, HookParam *hp);
IHFSERVICE DWORD IHFAPI Host_RemoveHook(DWORD pid, DWORD addr);
IHFSERVICE DWORD IHFAPI Host_AddLink(DWORD from, DWORD to);
IHFSERVICE DWORD IHFAPI Host_UnLink(DWORD from);
IHFSERVICE DWORD IHFAPI Host_UnLinkAll(DWORD from);
// EOF

View File

@ -0,0 +1,23 @@
# host.pri
# 8/9/2011 jichi
DEFINES += WITH_LIB_VNRHOST
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/avl_p.h \
$$PWD/hookman.h \
$$PWD/settings.h \
$$PWD/host.h \
$$PWD/host_p.h \
$$PWD/textthread.h \
$$PWD/textthread_p.h
SOURCES += \
$$PWD/hookman.cc \
$$PWD/host.cc \
$$PWD/pipe.cc \
$$PWD/textthread.cc
# EOF

View File

@ -0,0 +1,44 @@
#pragma once
// host_p.h
// 8/24/2013 jichi
// Branch IHF/main.h, rev 111
#include <windows.h>
#define GLOBAL extern
#define SHIFT_JIS 0x3A4
class HookManager;
//class CommandQueue;
class SettingManager;
class TextHook;
//class BitMap;
//class CustomFilterMultiByte;
//class CustomFilterUnicode;
//#define TextHook Hook
GLOBAL BOOL running;
//GLOBAL BitMap *pid_map;
//GLOBAL CustomFilterMultiByte *mb_filter;
//GLOBAL CustomFilterUnicode *uni_filter;
GLOBAL HookManager *man;
//GLOBAL CommandQueue *cmdq;
GLOBAL SettingManager *setman;
GLOBAL WCHAR recv_pipe[];
GLOBAL WCHAR command[];
GLOBAL HANDLE hPipeExist;
GLOBAL DWORD split_time,
cyclic_remove,
clipboard_flag,
global_filter;
GLOBAL CRITICAL_SECTION detach_cs;
DWORD WINAPI RecvThread(LPVOID lpThreadParameter);
DWORD WINAPI CmdThread(LPVOID lpThreadParameter);
DWORD GetCurrentPID();
//DWORD GetProcessIDByPath(LPWSTR str);
HANDLE GetCmdHandleByPID(DWORD pid);
//DWORD Inject(HANDLE hProc);
//DWORD InjectByPID(DWORD pid);
//DWORD PIDByName(LPWSTR target);
//DWORD Hash(LPCWSTR module, int length=-1);
// EOF

327
vnr/texthook/host/pipe.cc Normal file
View File

@ -0,0 +1,327 @@
// pipe.cc
// 8/24/2013 jichi
// Branch IHF/pipe.cpp, rev 93
// 8/24/2013 TODO: Clean up this file
#include "host_p.h"
#include "hookman.h"
#include "vnrhook/include/defs.h"
#include "vnrhook/include/const.h"
#include "ithsys/ithsys.h"
#include <stdio.h>
//#include "CommandQueue.h"
//#include <QtCore/QDebug>
#define DEBUG "vnrhost/pipe.cc"
#include "sakurakit/skdebug.h"
//DWORD WINAPI UpdateWindows(LPVOID lpThreadParameter);
namespace { // unnamed
enum NamedPipeCommand {
NAMED_PIPE_DISCONNECT = 1
, NAMED_PIPE_CONNECT = 2
};
bool newline = false;
bool detach = false;
// jichi 10/27/2013
// Check if text has leading space
enum { _filter_limit = 0x20 }; // The same as the orignal ITH filter. So, I don't have to check \u3000
//enum { _filter_limit = 0x19 };
inline bool has_leading_space(const BYTE *text, int len)
{
return len == 1 ? *text <= _filter_limit : // 1 byte
*reinterpret_cast<const WORD *>(text) <= _filter_limit; // 2 bytes
}
// jichi 9/28/2013: Skip leading garbage
// Note:
// - Modifying limit will break manual translation. The orignal one is 0x20
// - Eliminating 0x20 will break English-translated games
const BYTE *Filter(const BYTE *str, int len)
{
#ifdef ITH_DISABLE_FILTER // jichi 9/28/2013: only for debugging purpose
return str;
#endif // ITH_DISABLE_FILTER
// if (len && *str == 0x10) // jichi 9/28/2013: garbage on wine, data link escape, or ^P
// return nullptr;
//enum { limit = 0x19 };
while (true)
if (len >= 2) {
if (*(const WORD *)str <= _filter_limit) { // jichi 10/27/2013: two bytes
str += 2;
len -= 2;
} else
break;
} else if (*str <= _filter_limit) { // jichi 10/27/2013: 1 byte
str++;
len--;
} else
break;
return str;
}
} // unnamed namespace
//WCHAR recv_pipe[] = L"\\??\\pipe\\ITH_PIPE";
//WCHAR command_pipe[] = L"\\??\\pipe\\ITH_COMMAND";
wchar_t recv_pipe[] = ITH_TEXT_PIPE;
wchar_t command_pipe[] = ITH_COMMAND_PIPE;
CRITICAL_SECTION detach_cs; // jichi 9/27/2013: also used in main
//HANDLE hDetachEvent;
extern HANDLE hPipeExist;
void CreateNewPipe()
{
static DWORD acl[7] = {
0x1C0002,
1,
0x140000,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
0x101,
0x1000000,
0};
static SECURITY_DESCRIPTOR sd = {1, 0, 4, 0, 0, 0, (PACL)acl};
HANDLE hTextPipe, hCmdPipe, hThread;
IO_STATUS_BLOCK ios;
UNICODE_STRING us;
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
LARGE_INTEGER time = {-500000, -1};
RtlInitUnicodeString(&us, recv_pipe);
if (!NT_SUCCESS(NtCreateNamedPipeFile(
&hTextPipe,
GENERIC_READ | SYNCHRONIZE,
&oa,
&ios,
FILE_SHARE_WRITE,
FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
1, 1, 0, -1,
0x1000,
0x1000,
&time))) {
//ConsoleOutput(ErrorCreatePipe);
DOUT("failed to create recv pipe");
return;
}
RtlInitUnicodeString(&us, command_pipe);
if (!NT_SUCCESS(NtCreateNamedPipeFile(
&hCmdPipe,
GENERIC_WRITE | SYNCHRONIZE,
&oa,
&ios,
FILE_SHARE_READ,
FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
1, 1, 0, -1,
0x1000,
0x1000,
&time))) {
//ConsoleOutput(ErrorCreatePipe);
DOUT("failed to create cmd pipe");
return;
}
hThread = IthCreateThread(RecvThread, (DWORD)hTextPipe);
man->RegisterPipe(hTextPipe, hCmdPipe, hThread);
}
void DetachFromProcess(DWORD pid)
{
HANDLE hMutex = INVALID_HANDLE_VALUE,
hEvent = INVALID_HANDLE_VALUE;
//try {
IO_STATUS_BLOCK ios;
ProcessRecord *pr = man->GetProcessRecord(pid);
if (!pr)
return;
//IthBreak();
hEvent = IthCreateEvent(nullptr);
if (STATUS_PENDING == NtFsControlFile(
man->GetCmdHandleByPID(pid),
hEvent,
0,0,
&ios,
CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0),
0,0,0,0))
NtWaitForSingleObject(hEvent, 0, 0);
NtClose(hEvent);
//hEvent = INVALID_HANDLE_VALUE;
WCHAR mutex[0x20];
swprintf(mutex, ITH_DETACH_MUTEX_ L"%d", pid);
hMutex = IthOpenMutex(mutex);
if (hMutex != INVALID_HANDLE_VALUE) {
NtWaitForSingleObject(hMutex, 0, 0);
NtReleaseMutant(hMutex, 0);
NtClose(hMutex);
//hMutex = INVALID_HANDLE_VALUE;
}
//} catch (...) {
// if (hEvent != INVALID_HANDLE_VALUE)
// NtClose(hEvent);
// else if (hMutex != INVALID_HANDLE_VALUE) {
// NtWaitForSingleObject(hMutex, 0, 0);
// NtReleaseMutant(hMutex, 0);
// NtClose(hMutex);
// }
//}
//NtSetEvent(hDetachEvent, 0);
if (::running)
NtSetEvent(hPipeExist, 0);
}
// jichi 9/27/2013: I don't need this
//void OutputDWORD(DWORD d)
//{
// WCHAR str[0x20];
// swprintf(str, L"%.8X", d);
// ConsoleOutput(str);
//}
DWORD WINAPI RecvThread(LPVOID lpThreadParameter)
{
HANDLE hTextPipe = (HANDLE)lpThreadParameter;
IO_STATUS_BLOCK ios;
NtFsControlFile(hTextPipe,
0, 0, 0,
&ios,
CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_CONNECT, 0, 0),
0, 0, 0, 0);
if (!::running) {
NtClose(hTextPipe);
return 0;
}
BYTE *buff;
enum { PipeBufferSize = 0x1000 };
buff = new BYTE[PipeBufferSize];
::memset(buff, 0, PipeBufferSize); // jichi 8/27/2013: zero memory, or it will crash wine on start up
// 10/19/2014 jichi: there are totally three words received
// See: hook/rpc/pipe.cc
// struct {
// DWORD pid;
// TextHook *man;
// DWORD module;
// //DWORD engine;
// } u;
enum { module_struct_size = 12 };
NtReadFile(hTextPipe, 0, 0, 0, &ios, buff, module_struct_size, 0, 0);
// jichi 7/2/2015: This must be consistent with the struct declared in vnrhook/pipe.cc
DWORD pid = *(DWORD *)buff,
module = *(DWORD *)(buff + 0x8),
hookman = *(DWORD *)(buff + 0x4);
//engine = *(DWORD *)(buff + 0xc);
man->RegisterProcess(pid, hookman, module);
// jichi 9/27/2013: why recursion?
CreateNewPipe();
//NtClose(IthCreateThread(UpdateWindows,0));
while (::running) {
if (!NT_SUCCESS(NtReadFile(hTextPipe,
0, 0, 0,
&ios,
buff,
0xf80,
0, 0)))
break;
enum { data_offset = 0xc }; // jichi 10/27/2013: Seem to be the data offset in the pipe
DWORD RecvLen = ios.uInformation;
if (RecvLen < data_offset)
break;
DWORD hook = *(DWORD *)buff;
union { DWORD retn; DWORD cmd_type; };
union { DWORD split; DWORD new_engine_type; };
retn = *(DWORD *)(buff + 4);
split = *(DWORD *)(buff + 8);
buff[RecvLen] = 0;
buff[RecvLen + 1] = 0;
if (hook == HOST_NOTIFICATION) {
switch (cmd_type) {
case HOST_NOTIFICATION_NEWHOOK:
{
static long lock;
while (InterlockedExchange(&lock, 1) == 1);
ProcessEventCallback new_hook = man->ProcessNewHook();
if (new_hook)
new_hook(pid);
lock = 0;
} break;
case HOST_NOTIFICATION_TEXT:
//qDebug() << ((LPCSTR)(buff + 8));
break;
}
} else {
// jichi 9/28/2013: Debug raw data
//ITH_DEBUG_DWORD9(RecvLen - 0xc,
// buff[0xc], buff[0xd], buff[0xe], buff[0xf],
// buff[0x10], buff[0x11], buff[0x12], buff[0x13]);
const BYTE *data = buff + data_offset; // th
int len = RecvLen - data_offset;
bool space = ::has_leading_space(data, len);
if (space) {
const BYTE *it = ::Filter(data, len);
len -= it - data;
data = it;
}
if (len >> 31) // jichi 10/27/2013: len is too large, which seldom happens
len = 0;
//man->DispatchText(pid, len ? data : nullptr, hook, retn, split, len, space);
man->DispatchText(pid, data, hook, retn, split, len, space);
}
}
EnterCriticalSection(&detach_cs);
HANDLE hDisconnect = IthCreateEvent(nullptr);
if (STATUS_PENDING == NtFsControlFile(
hTextPipe,
hDisconnect,
0, 0,
&ios,
CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0),
0, 0, 0, 0))
NtWaitForSingleObject(hDisconnect, 0, 0);
NtClose(hDisconnect);
DetachFromProcess(pid);
man->UnRegisterProcess(pid);
//NtClearEvent(hDetachEvent);
LeaveCriticalSection(&detach_cs);
delete[] buff;
if (::running)
DOUT("detached");
//if (::running) {
// swprintf((LPWSTR)buff, FormatDetach, pid);
// ConsoleOutput((LPWSTR)buff);
// NtClose(IthCreateThread(UpdateWindows, 0));
//}
return 0;
}
// EOF

View File

@ -0,0 +1,17 @@
#pragma once
// settings.h
// 8/24/2013 jichi
struct Settings {
//bool debug; // whether output debug messages using pipes
int splittingInterval;// time to split text into sentences
bool clipboardFlag;
Settings() : splittingInterval(200),
clipboardFlag(false)
{}
};
// EOF

View File

@ -0,0 +1,786 @@
// textthread.cc
// 8/24/2013 jichi
// Branch IHF/TextThread.cpp, rev 133
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
#endif // _MSC_VER
#include "settings.h"
#include "textthread.h"
//#include "wintimer/wintimer.h"
#include "vnrhook/include/const.h"
#include "ithsys/ithsys.h"
#include <stdio.h>
MK_BASIC_TYPE(BYTE)
MK_BASIC_TYPE(ThreadParameter)
static DWORD MIN_DETECT = 0x20;
static DWORD MIN_REDETECT = 0x80;
//#define MIN_DETECT 0x20
//#define MIN_REDETECT 0x80
#ifndef CURRENT_SELECT
# define CURRENT_SELECT 0x1000
#endif
#ifndef REPEAT_NUMBER_DECIDED
# define REPEAT_NUMBER_DECIDED 0x2000
#endif
DWORD GetHookName(LPSTR str, DWORD pid, DWORD hook_addr,DWORD max);
extern Settings *settings;
extern HWND hMainWnd;
void CALLBACK NewLineBuff(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
KillTimer(hwnd,idEvent);
TextThread *id=(TextThread*)idEvent;
if (id->Status()&CURRENT_SELECT)
//texts->SetLine();
id->CopyLastToClipboard();
id->SetNewLineFlag();
}
// jichi 10/27/2013: removed
//void CALLBACK NewLineConsole(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
//{
// KillTimer(hwnd,idEvent);
// TextThread *id=(TextThread*)idEvent;
// if (id->Status()&USING_UNICODE)
// id->AddText((BYTE*)L"\r\n",4,true,true);
// if (id->Status()&CURRENT_SELECT)
// {
// //texts->SetLine();
// }
//}
// jichi 10/27/2013: removed
//void ReplaceSentence(BYTE* text, int len)
//{
// __asm int 3
//}
TextThread::TextThread(DWORD id, DWORD hook, DWORD retn, DWORD spl, WORD num) :
//,tp
thread_number(num)
// jichi 9/21/2013: zero all fields
, link_number(-1)
, last (0)
, align_space(0)
, repeat_single(0)
, repeat_single_current(0)
, repeat_single_count(0)
, repeat_detect_count(0)
, head(new RepeatCountNode())
, link(nullptr)
//, filter(nullptr)
, output(nullptr)
, app_data(nullptr)
//, comment(nullptr)
, thread_string(nullptr)
, timer(0)
, status (0)
, repeat_detect_limit(0x80)
, last_sentence(0)
, prev_sentence(0)
, sentence_length(0)
, repeat_index(0)
, last_time(0)
// , tp({id, hook, retn, spl})
{
tp.pid = id;
tp.hook = hook;
tp.retn = retn;
tp.spl = spl;
//head = new RepeatCountNode;
//::memset(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory
//link_number = -1;
//repeat_detect_limit = 0x80;
//filter = nullptr;
//output = nullptr;
}
TextThread::~TextThread()
{
//KillTimer(hMainWnd,timer);
RepeatCountNode *t = head,
*tt;
while (t) {
tt = t;
t = tt->next;
delete tt;
}
head = nullptr;
//if (comment) {
// delete[] comment;
// comment = nullptr;
//}
if (thread_string)
delete[] thread_string;
}
void TextThread::Reset()
{
//timer=0;
last_sentence = 0;
//if (comment) {
// delete[] comment;
// comment = nullptr;
//}
MyVector::Reset();
}
void TextThread::RemoveSingleRepeatAuto(const BYTE *con, int &len)
{
#ifdef ITH_DISABLE_REPEAT // jichi 9/28/2013: only for debugging purpose
return;
#endif // ITH_DISABLE_REPEAT
WORD *text = (WORD *)con;
if (len <= 2) {
if (repeat_single) {
if (repeat_single_count<repeat_single&&
last == *text) {
len = 0;
repeat_single_count++;
} else {
last = *text;
repeat_single_count=0;
}
}
if (status & REPEAT_NUMBER_DECIDED) {
if (++repeat_detect_count>MIN_REDETECT) {
repeat_detect_count = 0;
status ^= REPEAT_NUMBER_DECIDED;
last = 0;
RepeatCountNode *t = head,
*tt;
while (t) {
tt = t;
t = tt->next;
delete tt;
}
head = new RepeatCountNode;
::memset(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory
}
} else {
repeat_detect_count++;
if (last == *text)
repeat_single_current++;
else {
if (last == 0) {
last = *text;
return;
}
if (repeat_single_current == 0) {
status |= REPEAT_NUMBER_DECIDED;
repeat_single = 0;
return;
}
last = *text;
RepeatCountNode *it = head;
if (repeat_detect_count > MIN_DETECT) {
while (it = it->next)
if (it->count>head->count) {
head->count=it->count;
head->repeat=it->repeat;
}
repeat_single = head->repeat;
repeat_single_current = 0;
repeat_detect_count = 0;
status |= REPEAT_NUMBER_DECIDED;
DWORD repeat_sc = repeat_single*4;
if (repeat_sc > MIN_DETECT) {
MIN_DETECT <<= 1;
MIN_REDETECT <<= 1;
}
} else {
bool flag=true;
while (it) {
if (it->repeat == repeat_single_current) {
it->count++;
flag = false;
break;
}
it=it->next;
}
if (flag) {
RepeatCountNode *n = new RepeatCountNode;
n->count = 1;
n->repeat = repeat_single_current;
n->next = head->next;
head->next = n;
}
repeat_single_current = 0;
} //Decide repeat_single
} //Check Repeat
} //repeat_single decided?
} //len
else {
status |= REPEAT_NUMBER_DECIDED;
repeat_single = 0;
}
}
void TextThread::RemoveSingleRepeatForce(BYTE *con,int &len)
{
// jichi 9/1/2013: manual repetition count removed
WORD *text = (WORD *)con;
//if (repeat_single_count<setman->GetValue(SETTING_REPEAT_COUNT)&&last==*text) {
// len=0;
// repeat_single_count++;
//}
//else
{
last = *text;
repeat_single_count=0;
}
}
void TextThread::RemoveCyclicRepeat(BYTE* &con, int &len)
{
DWORD current_time = GetTickCount();
if (status & REPEAT_SUPPRESS) {
if (current_time - last_time < (unsigned)settings->splittingInterval &&
::memcmp(storage + last_sentence + repeat_index, con, len) == 0) {
repeat_index += len;
if (repeat_index>=sentence_length)
repeat_index -= sentence_length;
len = 0;
} else {
repeat_index = 0;
status &= ~REPEAT_SUPPRESS;
}
} else if (status & REPEAT_DETECT) {
if (::memcmp(storage + last_sentence + repeat_index, con, len) == 0) {
int half_length=repeat_index+len;
if (::memcmp(storage + last_sentence, storage + last_sentence + half_length, repeat_index) == 0) {
len=0;
sentence_length=half_length;
status&=~REPEAT_DETECT;
status|=REPEAT_SUPPRESS;
// jichi 10/27/2013: Not used
//if (status&CURRENT_SELECT)
// ReplaceSentence(storage+last_sentence+half_length,repeat_index);
ClearMemory(last_sentence+half_length,repeat_index);
used-=repeat_index;
repeat_index=0;
}
else
repeat_index += len;
}
else {
repeat_index=0;
status &= ~REPEAT_DETECT;
}
} else {
if (sentence_length == 0)
return;
else if (len <= (int)sentence_length) {
if (memcmp(storage + last_sentence, con, len) == 0) {
status |= REPEAT_DETECT;
repeat_index = len;
if (repeat_index == sentence_length) {
repeat_index = 0;
len = 0;
}
} else if (sentence_length > repeat_detect_limit) {
if (len > 2) {
DWORD u = used;
while (memcmp(storage + u - len, con, len) == 0)
u -= len;
ClearMemory(u, used - u);
used = u;
repeat_index = 0;
// jichi 10/27/2013: Not used
//if (status & CURRENT_SELECT)
// ReplaceSentence(storage + last_sentence, used - u);
status |= REPEAT_SUPPRESS;
len = 0;
} else if (len <= 2)
{
WORD tmp = *(WORD *)(storage + last_sentence);
DWORD index, last_index, tmp_len;
index = used-len;
if (index < last_sentence)
index = last_sentence;
//Locate position of current input.
_again:
*(WORD *)(storage+last_sentence) = *(WORD *)con;
while (*(WORD *)(storage + index) != *(WORD *)con)
index--;
*(WORD *)(storage + last_sentence) = tmp;
if (index > last_sentence) {
tmp_len = used - index;
if (tmp_len <= 2) {
repeat_detect_limit += 0x40;
last_time = current_time;
return;
}
if (index - last_sentence >= tmp_len &&
memcmp(storage + index - tmp_len, storage + index, tmp_len) == 0) {
repeat_detect_limit = 0x80;
sentence_length =tmp_len;
index -= tmp_len;
while (memcmp(storage + index - sentence_length, storage + index, sentence_length) == 0)
index -= sentence_length;
repeat_index = 2;
len = 0;
last_index = index;
if (status&USING_UNICODE) {
while (storage[index] == storage[index + sentence_length])
index -= 2;
index += 2;
while (true) {
tmp = *(WORD *)(storage + index);
if (tmp >= 0x3000 && tmp < 0x3020)
index += 2;
else
break;
}
} else {
DWORD last_char_len;
while (storage[index] == storage[index + sentence_length]) {
last_char_len = LeadByteTable[storage[index]];
index -= last_char_len;
}
index += last_char_len;
while (storage[index] == 0x81) {
if ((storage[index+1]>>4) == 4)
index += 2;
else
break;
}
}
repeat_index += last_index - index;
status |= REPEAT_SUPPRESS;
last_sentence = index;
index += sentence_length;
// jichi 10/27/2013: Not used
//if (status&CURRENT_SELECT)
// ReplaceSentence(storage + index, used - index);
ClearMemory(index, used - index);
//memset(storage + index, 0, used - index);
used = index;
} else {
index--;
goto _again;
}
}
else
repeat_detect_limit += 0x40;
}
}
}
}
last_time = current_time;
}
void TextThread::ResetRepeatStatus()
{
last=0;
repeat_single=0;
repeat_single_current=0;
repeat_single_count=0;
repeat_detect_count=0;
RepeatCountNode *t = head->next,
*tt;
while (t) {
tt = t;
t = tt->next;
delete tt;
}
//head=new RepeatCountNode;
head->count = head->repeat = 0;
status &= ~REPEAT_NUMBER_DECIDED;
}
void TextThread::AddLineBreak()
{
if (sentence_length == 0) return;
if (status&BUFF_NEWLINE)
{
prev_sentence=last_sentence;
sentence_length=0;
if (status & USING_UNICODE)
AddToStore((BYTE *)L"\r\n\r\n", 8);
else
AddToStore((BYTE *)"\r\n\r\n", 4);
if (output)
output(this, 0, 8, TRUE, app_data, false); // jichi 10/27/2013: space is false
last_sentence = used;
status &= ~BUFF_NEWLINE;
}
}
void TextThread::AddText(const BYTE *con, int len, bool new_line, bool space)
{
if (!con || (len <= 0 && !space))
return;
if (len && !new_line) {
// jichi 9/1/2013: manual repetition count removed
//if (setman->GetValue(SETTING_REPEAT_COUNT)) {
// status|=REPEAT_NUMBER_DECIDED;
// RemoveSingleRepeatForce(con,len);
//}
//else
RemoveSingleRepeatAuto(con, len);
if (len <= 0 && !space)
return;
}
// jichi 9/1/2013: manual repetition count removed
//if(setman->GetValue(SETTING_CYCLIC_REMOVE)) {
// //if (status & REPEAT_NUMBER_DECIDED)
// RemoveCyclicRepeat(con,len);
//}
//if (len <= 0)
// return;
// jichi 10/27/2013: User-defined filter callback is disabled
//if (filter)
// len = filter(this, con,len, new_line, app_data);
//if (len <= 0)
// return;
if (len && sentence_length == 0) {
if (status & USING_UNICODE) {
if (*(WORD *)con == 0x3000) { // jichi 10/27/2013: why skip unicode space?!
con += 2;
len -= 2;
}
} else if (*(WORD *)con == 0x4081) {
con += 2;
len -= 2;
}
if (len <= 0 && !space)
return;
}
if (status & BUFF_NEWLINE)
AddLineBreak();
if (len)
if (new_line) {
prev_sentence = last_sentence;
last_sentence = used + 4;
if (status & USING_UNICODE)
last_sentence += 4;
sentence_length = 0;
} else {
SetNewLineTimer();
if (link) {
const BYTE *send = con;
int l = len;
if (status & USING_UNICODE) { // Although unlikely, a thread and its link may have different encoding.
if ((link->Status() & USING_UNICODE) == 0) {
send = new BYTE[l];
//::memset(send, 0, l); // jichi 9/26/2013: zero memory
l = WC_MB((LPWSTR)con, (char *)send);
}
link->AddTextDirect(send, l, space);
} else {
if (link->Status() & USING_UNICODE) {
size_t sz = len * 2 + 2;
send = new BYTE[sz];
//::memset(send, 0, sz); // jichi 9/26/2013: zero memory
l = MB_WC((char *)con, (LPWSTR)send) << 1;
}
link->AddTextDirect(send, l, space);
}
link->SetNewLineTimer();
if (send != con)
delete[] send;
}
sentence_length += len;
}
BYTE *data = const_cast<BYTE *>(con); // jichi 10/27/2013: TODO: Figure out where con is modified
if (output)
len = output(this, data, len, new_line, app_data, space);
if (AddToStore(data, len)) {
//sentence_length += len;
/*ResetRepeatStatus();
last_sentence=0;
prev_sentence=0;
sentence_length=len;
repeat_index=0;
status&=~REPEAT_DETECT|REPEAT_SUPPRESS; */
}
}
void TextThread::AddTextDirect(const BYTE* con, int len, bool space) // Add to store directly, penetrating repetition filters.
{
// jichi 10/27/2013: Accordig to the logic, both len and con must be > 0
if (status & BUFF_NEWLINE)
AddLineBreak();
//SetNewLineTimer();
if (link) {
const BYTE *send = con;
int l = len;
if (status & USING_UNICODE) {
if ((link->Status()&USING_UNICODE) == 0) {
send = new BYTE[l];
//::memset(send, 0, l); // jichi 9/26/2013: zero memory
l = WC_MB((LPWSTR)con,(char*)send);
}
link->AddText(send, l, false, space); // new_line is false
} else {
if (link->Status()&USING_UNICODE) {
size_t sz = len * 2 + 2;
send = new BYTE[sz];
//::memset(send, 0, sz); // jichi 9/26/2013: zero memory
l = MB_WC((char *)con, (LPWSTR)send) << 1;
}
link->AddText(send, l, false, space); // new_line is false
}
link->SetNewLineTimer();
if (send != con)
delete[] send;
}
sentence_length += len;
BYTE *data = const_cast<BYTE *>(con); // jichi 10/27/2013: TODO: Figure out where con is modified
if (output)
len = output(this, data, len, false, app_data, space);
AddToStore(data, len);
}
DWORD TextThread::GetEntryString(LPSTR str, DWORD max)
{
DWORD len = 0;
if (str && max > 0x40) {
max--;
if (thread_string) {
len = ::strlen(thread_string);
len = len < max ? len : max;
memcpy(str, thread_string, len);
str[len] = 0;
} else {
len = ::sprintf(str, "%.4X:%.4d:0x%08X:0x%08X:0x%08X:",
thread_number, tp. pid, tp.hook, tp.retn, tp.spl);
len += GetHookName(str + len, tp.pid, tp.hook, max - len);
thread_string = new char[len + 1];
//::memset(thread_string, 0, (len+1) * sizeof(wchar_t)); // jichi 9/26/2013: zero memory
thread_string[len] = 0;
::memcpy(thread_string, str, len);
}
//if (comment) {
// str += len;
// max--;
// DWORD cl = wcslen(comment);
// if (len + cl >= max)
// cl = max - len;
// *str++ = L'-';
// memcpy(str, comment, cl << 1);
// str[cl] = 0;
// len += cl;
//}
}
return len;
}
// jichi 9/28/2013: removed
//void TextThread::CopyLastSentence(LPWSTR str)
//{
// int i,j,l;
// if (status&USING_UNICODE)
// {
// if (used>8)
// {
// j=used>0xF0?(used-0xF0):0;
// for (i=used-0xA;i>=j;i-=2)
// {
// if (*(DWORD*)(storage+i)==0xA000D) break;
// }
// if (i>=j)
// {
// l=used-i;
// if (i>j) l-=4;
// j=4;
// }
// else
// {
// i+=2;
// l=used-i;
// j=0;
// }
// memcpy(str,storage+i+j,l);
// str[l>>1]=0;
// }
// else
// {
// memcpy(str,storage,used);
// str[used>>1]=0;
// }
// }
// else
// {
// if (used>4)
// {
// j=used>0x80?(used-0x80):0;
// for (i=used-5;i>=j;i--)
// {
// if (*(DWORD*)(storage+i)==0xA0D0A0D) break;
// }
// if (i>=j)
// {
// l=used-i;
// if (i>j) l-=4;
// j=4;
// }
// else
// {
// i++;
// l=used-i;
// j=0;
// }
// size_t sz = (l|0xF) + 1;
// char *buff = new char[sz];
// //memset(buff, 0, sz); // jichi 9/26/2013: zero memory
// memcpy(buff, storage + i + j, l);
// buff[l] = 0;
// str[MB_WC(buff, str)] = 0;
// delete[] buff;
// } else {
// storage[used] = 0;
// str[MB_WC((char *)storage, str)] = 0;
// }
// }
//}
static char clipboard_buffer[0x400];
// jichi 8/25/2013: clipboard removed
void CopyToClipboard(void* str,bool unicode, int len)
{
if (settings->clipboardFlag && str && len > 0)
{
int size=(len*2|0xF)+1;
if (len>=1022) return;
memcpy(clipboard_buffer,str,len);
*(WORD*)(clipboard_buffer+len)=0;
HGLOBAL hCopy;
LPWSTR copy;
if (OpenClipboard(0))
{
if (hCopy=GlobalAlloc(GMEM_MOVEABLE,size))
{
if (copy=(LPWSTR)GlobalLock(hCopy))
{
if (unicode)
{
memcpy(copy,clipboard_buffer,len+2);
}
else
copy[MB_WC(clipboard_buffer,copy)]=0;
GlobalUnlock(hCopy);
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT,hCopy);
}
}
CloseClipboard();
}
}
}
void TextThread::CopyLastToClipboard()
{
// jichi 8/25/2013: clipboard removed
CopyToClipboard(storage+last_sentence,(status&USING_UNICODE)>0,used-last_sentence);
}
//void TextThread::ResetEditText()
//{
// //__asm int 3;
// WCHAR str[0x20];
// swprintf(str,L"%.8X",_ReturnAddress());
//}
// jichi 9/25/2013: Removed
//void TextThread::ExportTextToFile(LPWSTR) //filename)
//{
// HANDLE hFile=IthCreateFile(filename,FILE_WRITE_DATA,0,FILE_OPEN_IF);
// if (hFile==INVALID_HANDLE_VALUE) return;
// EnterCriticalSection(&cs_store);
// IO_STATUS_BLOCK ios;
// LPVOID buffer=storage;
// DWORD len=used;
// BYTE bom[4]={0xFF,0xFE,0,0};
// LARGE_INTEGER offset={2,0};
// if ((status&USING_UNICODE)==0)
// {
// len=MB_WC_count((char*)storage,used);
// buffer = new wchar_t[len+1];
// MB_WC((char*)storage,(wchar_t*)buffer);
// len<<=1;
// }
// NtWriteFile(hFile,0,0,0,&ios,bom,2,0,0);
// NtWriteFile(hFile,0,0,0,&ios,buffer,len,&offset,0);
// NtFlushBuffersFile(hFile,&ios);
// if (buffer !=storage)
// delete[] buffer;
// NtClose(hFile);
// LeaveCriticalSection(&cs_store);
//}
//void TextThread::SetComment(LPWSTR str)
//{
// if (comment)
// delete[] comment;
// size_t sz = wcslen(str);
// comment = new wchar_t[sz + 1];
// comment[sz] = 0;
// wcscpy(comment, str);
//}
void TextThread::SetNewLineFlag() { status |= BUFF_NEWLINE; }
bool TextThread::CheckCycle(TextThread* start)
{
if (link==start||this==start) return true;
if (link==0) return false;
return link->CheckCycle(start);
}
void TextThread::SetNewLineTimer()
{
if (thread_number == 0)
// jichi 10/27/2013: Not used
timer = 0; //SetTimer(hMainWnd,(UINT_PTR)this, settings->splittingInterval, NewLineConsole);
else
timer = SetTimer(hMainWnd, (UINT_PTR)this, settings->splittingInterval, NewLineBuff);
}
DWORD TextThread::GetThreadString(LPSTR str, DWORD max)
{
DWORD len = 0;
if (max) {
char buffer[0x200];
char c;
if (thread_string == nullptr)
GetEntryString(buffer, 0x200); //This will allocate thread_string.
LPSTR end = thread_string;
for (; *end; end++);
c = thread_string[0];
thread_string[0] = ':';
LPSTR p1 = end;
for (; *p1 != ':'; p1--);
thread_string[0] = c;
if (p1 == thread_string)
return 0;
p1++;
len = end - p1;
if (len >= max)
len = max - 1;
::memcpy(str, p1, len);
str[len] = 0;
}
return len;
}
void TextThread::UnLinkAll()
{
if (link) link->UnLinkAll();
link = 0;
link_number = -1;
}
// EOF

View File

@ -0,0 +1,135 @@
#pragma once
// textthread.h
// 8/23/2013 jichi
// Branch: ITH/TextThread.h, rev 120
#include "host/textthread_p.h"
#include <intrin.h> // require _InterlockedExchange
struct RepeatCountNode {
short repeat;
short count;
RepeatCountNode *next;
//RepeatCountNode() : repeat(0), count(0), next(nullptr) {}
};
struct ThreadParameter {
DWORD pid; // jichi: 5/11/2014: The process ID
DWORD hook;
DWORD retn; // jichi 5/11/2014: The return address of the hook
DWORD spl; // jichi 5/11/2014: the processed split value of the hook parameter
};
#define CURRENT_SELECT 0x1000
#define REPEAT_NUMBER_DECIDED 0x2000
#define BUFF_NEWLINE 0x4000
#define CYCLIC_REPEAT 0x8000
#define COUNT_PER_FOWARD 0x200
#define REPEAT_DETECT 0x10000
#define REPEAT_SUPPRESS 0x20000
#define REPEAT_NEWLINE 0x40000
class TextThread;
typedef void (* ConsoleCallback)(LPCSTR text);
typedef void (* ConsoleWCallback)(LPCWSTR text);
typedef DWORD (* ThreadOutputFilterCallback)(TextThread *, BYTE *, DWORD, DWORD, PVOID, bool space); // jichi 10/27/2013: Add space
typedef DWORD (* ThreadEventCallback)(TextThread *);
//extern DWORD split_time,repeat_count,global_filter,cyclic_remove;
class TextThread : public MyVector<BYTE, 0x200>
{
public:
TextThread(DWORD pid, DWORD hook, DWORD retn, DWORD spl, WORD num);
~TextThread();
//virtual void CopyLastSentence(LPWSTR str);
//virtual void SetComment(LPWSTR);
//virtual void ExportTextToFile(LPWSTR filename);
virtual bool CheckCycle(TextThread *start);
virtual DWORD GetThreadString(LPSTR str, DWORD max);
virtual DWORD GetEntryString(LPSTR str, DWORD max = 0x200);
void Reset();
void AddText(const BYTE *con,int len, bool new_line, bool space); // jichi 10/27/2013: add const; remove console; add space
void RemoveSingleRepeatAuto(const BYTE *con, int &len); // jichi 10/27/2013: add const
void RemoveSingleRepeatForce(BYTE *con, int &len);
void RemoveCyclicRepeat(BYTE *&con, int &len);
void ResetRepeatStatus();
void AddLineBreak();
//void ResetEditText();
void ComboSelectCurrent();
void UnLinkAll();
void CopyLastToClipboard();
//void AdjustPrevRepeat(DWORD len);
//void PrevRepeatLength(DWORD &len);
//bool AddToCombo();
bool RemoveFromCombo();
void SetNewLineFlag();
void SetNewLineTimer();
BYTE *GetStore(DWORD *len) { if (len) *len = used; return storage; }
DWORD LastSentenceLen() { return used - last_sentence; }
DWORD PID() const { return tp.pid; }
DWORD Addr() const {return tp.hook; }
DWORD &Status() { return status; }
WORD Number() const { return thread_number; }
WORD &Last() { return last; }
WORD &LinkNumber() { return link_number; }
UINT_PTR &Timer() { return timer; }
ThreadParameter *GetThreadParameter() { return &tp; }
TextThread *&Link() { return link; }
//LPCWSTR GetComment() { return comment; }
ThreadOutputFilterCallback RegisterOutputCallBack(ThreadOutputFilterCallback cb, PVOID data)
{
app_data = data;
return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&output,(long)cb);
}
ThreadOutputFilterCallback RegisterFilterCallBack(ThreadOutputFilterCallback cb, PVOID data)
{
app_data = data;
return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&filter,(long)cb);
}
void SetRepeatFlag() { status |= CYCLIC_REPEAT; }
void ClearNewLineFlag() { status &= ~BUFF_NEWLINE; }
void ClearRepeatFlag() { status &= ~CYCLIC_REPEAT; }
protected:
void AddTextDirect(const BYTE *con, int len, bool space); // jichi 10/27/2013: add const; add space; change to protected
private:
ThreadParameter tp;
WORD thread_number,
link_number;
WORD last,
align_space;
WORD repeat_single;
WORD repeat_single_current;
WORD repeat_single_count;
WORD repeat_detect_count;
RepeatCountNode *head;
TextThread *link;
ThreadOutputFilterCallback filter; // jichi 10/27/2013: Remove filter
ThreadOutputFilterCallback output;
PVOID app_data;
LPSTR thread_string;
UINT_PTR timer;
DWORD status,repeat_detect_limit;
DWORD last_sentence,
prev_sentence,
sentence_length,
repeat_index,
last_time;
};
// EOF

View File

@ -0,0 +1,155 @@
#pragma once
// textthread_p.h
// 8/14/2013 jichi
// Branch: ITH/main_template.h, rev 66
#include <windows.h>
template <typename T>
void Release(const T &p) { delete p; }
// Prevent memory release.
// Used when T is basic types and will be automatically released (on stack).
#define MK_BASIC_TYPE(T) \
template<> \
void Release<T>(const T &p) {}
template<class T>
struct BinaryEqual {
bool operator ()(const T &a, const T &b, DWORD) { return a == b; }
};
template<class T, int default_size, class fComp=BinaryEqual<T> >
class MyVector
{
public:
MyVector() : size(default_size), used(0)
{
InitializeCriticalSection(&cs_store);
storage = new T[size];
// jichi 9/21/2013: zero memory
// This would cause trouble if T is not an atomic type
::memset(storage, 0, sizeof(T) * size);
}
virtual ~MyVector()
{
if (storage)
delete[] storage;
DeleteCriticalSection(&cs_store);
storage = 0;
}
void Reset()
{
EnterCriticalSection(&cs_store);
for (int i = 0; i < used; i++) {
Release<T>(storage[i]);
storage[i] = T();
}
used = 0;
LeaveCriticalSection(&cs_store);
}
void Remove(int index)
{
if (index>=used)
return;
Release<T>(storage[index]);
for (int i = index; i < used; i++)
storage[i] = storage[i+1];
used--;
}
void ClearMemory(int offset, int clear_size)
{
if (clear_size < 0)
return;
EnterCriticalSection(&cs_store);
if (offset+clear_size <= size)
::memset(storage+offset, 0, clear_size * sizeof(T)); // jichi 11/30/2013: This is the original code of ITH
LeaveCriticalSection(&cs_store);
//else __asm int 3
}
int AddToStore(T *con,int amount)
{
if (amount <= 0 || con == 0)
return 0;
int status = 0;
EnterCriticalSection(&cs_store);
if (amount + used + 2 >= size) {
while (amount + used + 2 >= size)
size<<=1;
T *temp;
if (size * sizeof(T) < 0x1000000) {
temp = new T[size];
if (size > used)
::memset(temp, 0, (size - used) * sizeof(T)); // jichi 9/25/2013: zero memory
memcpy(temp, storage, used * sizeof(T));
} else {
size = default_size;
temp = new T[size];
::memset(temp, 0, sizeof(T) * size); // jichi 9/25/2013: zero memory
used = 0;
status = 1;
}
delete[] storage;
storage = temp;
}
memcpy(storage+used, con, amount * sizeof(T));
used += amount;
LeaveCriticalSection(&cs_store);
return status;
}
int Find(const T &item, int start = 0, DWORD control = 0)
{
int c = -1;
for (int i=start; i < used; i++)
if (fCmp(storage[i],item,control)) {
c=i;
break;
}
//if (storage[i]==item) {c=i;break;}
return c;
}
int Used() const { return used; }
T *Storage() const { return storage; }
void LockVector() { EnterCriticalSection(&cs_store); }
void UnlockVector() { LeaveCriticalSection(&cs_store); }
protected:
CRITICAL_SECTION cs_store;
int size,
used;
T *storage;
fComp fCmp;
};
// EOF
/*
#ifndef ITH_STACK
#define ITH_STACK
template<class T, int default_size>
class MyStack
{
public:
MyStack(): index(0) {}
void push_back(const T& e)
{
if (index<default_size)
s[index++]=e;
}
void pop_back()
{
index--;
}
T& back()
{
return s[index-1];
}
T& operator[](int i) {return s[i];}
int size() {return index;}
private:
int index;
T s[default_size];
};
#endif
*/

20
vnr/texthook/ihf.txt Normal file
View File

@ -0,0 +1,20 @@
# 6/6/2012
# IHF.dll
# Skip swprintf and MessageBox statements in GetDebugPriv
#
# SVN checkout: 2012/6/6
# - IHF/main.cpp: 2012/4/8
# - IHF.{dll,lib}: 2012/6/5
6F753055 CALL DWORD PTR DS:ntdll.ZwAdjustPrivilegesTOken
6F75305B TEST EAX,EAX
6F75305B JNE SHORT 6F753077 => JNE SHORT 6F75309F
...
6F753077 PUSH EAX
...
6F75309F MOVE EAX,DWORD PTR SS:[ESP]
6F7530A2 PUSH EAX
6F7530A3 CALL DWORD PTR DS:ntdll.NtClose
...
# EOF

583
vnr/texthook/ihf_p.cc Normal file
View File

@ -0,0 +1,583 @@
// Host_p.cc
// 10/15/2011 jichi
#include "texthook/ihf_p.h"
#include "texthook/ith_p.h"
#include "texthook/textthread_p.h"
#include "host/host.h"
#include "vnrhook/include/types.h"
#include "ithsys/ithsys.h"
#include "wintimer/wintimer.h"
#include <QtCore/QDebug>
#ifdef WITH_LIB_WINMAKER
# include "winmaker/winmaker.h"
#endif // WITH_LIB_WINMAKER
//#define ITH_RUNNING_EVENT L"ITH_PIPE_EXIST"
//#define ITH_RUNNING_MUTEX L"ITH_RUNNING"
//#define ITH_MUTEX_NAME L"ITH_MAIN_RUNNING"
//#define DEBUG "ihf_p.cc"
#include "sakurakit/skdebug.h"
//#define ITH_WITH_LINK
// - Construction -
//bool Ihf::debug_ = true;
bool Ihf::enabled_ = true;
//Settings *Ihf::settings_;
HookManager *Ihf::hookManager_;
qint64 Ihf::messageInterval_ = 250; // 0.25 secs by default, larger than the split_time (0.2sec) in ITH::setman
WId Ihf::parentWindow_;
QHash<TextThread *, TextThreadDelegate *> Ihf::threadDelegates_;
//QHash<TextThreadDelegate *, TextThreadDelegate *> Ihf::linkedDelegates_;
QHash<QString, ulong> Ihf::hookAddresses_;
char Ihf::keptThreadName_[ITH_THREAD_NAME_CAPACITY];
bool Ihf::whitelistEnabled_;
qint32 Ihf::whitelist_[Ihf::WhitelistSize];
// Debugging output
//void Ihf::consoleOutput(const char *text)
//{ if (debug_) qDebug() << "texthook:console:" << text; }
//void Ihf::consoleOutputW(const wchar_t *text)
//{ if (debug_) qDebug() << "texthook:console:" << QString::fromWCharArray(text); }
void Ihf::init()
{
IthInitSystemService();
Host_Init();
}
void Ihf::destroy()
{
Host_Destroy();
IthCloseSystemService();
}
// See also: HelloITH/main.cpp
bool Ihf::load()
{
// 12/20/2013: This would crash the error of failure to create QTimer
//if (!parentWindow_)
//::wm_register_hidden_class("vnrtexthook.class");
//parentWindow_ = (WId)::wm_create_hidden_window("vnrtexthook.class", "vnrtexthook");
DOUT("enter");
if (hookManager_) {
DOUT("leave: already loaded");
return true;
}
// Single instance protection
//HANDLE hMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, ITH_MUTEX_NAME); // in kernel32.dll
//if (hMutex != 0 || ::GetLastError() != ERROR_FILE_NOT_FOUND) {
// ::CloseHandle(hMutex);
// return false;
//}
// See: ITH/main.cpp
//if (!IthInitSystemService()) {
// DOUT("leave: error: failed to init system service");
// return false;
//}
if (::Host_Open()) {
#ifdef WITH_LIB_WINMAKER
if (!parentWindow_)
parentWindow_ = (WId)::wm_create_hidden_window("vnrtexthook");
#endif // WITH_LIB_WINMAKER
WinTimer::setGlobalWindow(parentWindow_);
::Host_GetHookManager(&hookManager_);
if (hookManager_) {
//::Host_GetSettings(&settings_);
//settings_->debug = debug_;
//hookManager_->RegisterConsoleCallback(consoleOutput);
//hookManager_->RegisterConsoleWCallback(consoleOutputW);
//hookManager_->RegisterProcessAttachCallback(processAttach);
//hookManager_->RegisterProcessDetachCallback(processDetach);
//hookManager_->RegisterProcessNewHookCallback(processNewHook);
//hookManager_->RegisterThreadResetCallback(threadReset);
hookManager_->RegisterThreadCreateCallback(threadCreate);
hookManager_->RegisterThreadRemoveCallback(threadRemove);
::Host_Start();
}
} else
::Host_Close();
DOUT("leave: hook manager =" << hookManager_);
return hookManager_;
}
void Ihf::unload()
{
DOUT("enter: hook manager =" << hookManager_);
if (hookManager_) {
//hookManager_->RegisterProcessAttachCallback(nullptr);
//hookManager_->RegisterProcessDetachCallback(nullptr);
//hookManager_->RegisterProcessNewHookCallback(nullptr);
//hookManager_->RegisterThreadResetCallback(nullptr);
hookManager_->RegisterThreadCreateCallback(nullptr);
hookManager_->RegisterThreadRemoveCallback(nullptr);
// Console output is not unregisterd to avoid segmentation fault
//hookManager_->RegisterConsoleCallback(nullptr);
::Host_Close();
hookManager_ = nullptr;
//settings_ = nullptr;
#ifdef WITH_LIB_WINMAKER
if (parentWindow_) {
wm_destroy_window(parentWindow_);
parentWindow_ = nullptr;
}
#endif // WITH_LIB_WINMAKER
}
//if (parentWindow_) {
// wm_destroy_window(parentWindow_);
// parentWindow_ = nullptr;
//}
DOUT("leave");
}
// - Callbacks -
//DWORD Ihf::processAttach(DWORD pid)
//{
// DOUT("enter");
// Q_UNUSED(pid);
// DOUT("leave");
// return 0;
//}
//DWORD Ihf::processDetach(DWORD pid)
//{
// DOUT("enter");
// Q_UNUSED(pid);
// DOUT("leave");
// return 0;
//}
//DWORD Ihf::processNewHook(DWORD pid)
//{
// DOUT("enter");
// Q_UNUSED(pid);
// DOUT("leave");
// return 0;
//}
// See: HelloITH/main.cpp
// See: ThreadCreate in ITH/window.cpp
DWORD Ihf::threadCreate(TextThread *t)
{
Q_ASSERT(t);
DOUT("enter: pid =" << t->PID());
Q_ASSERT(hookManager_);
// Propagate UNICODE
// See: ThreadCreate in ITH/window.cpp
//if (ProcessRecord *pr = hookManager_->GetProcessRecord(t->PID())) {
// NtWaitForSingleObject(pr->hookman_mutex, 0, 0);
// Hook *hk = static_cast<Hook *>(pr->hookman_map);
// Q_ASSERT(!hk&&!MAX_HOOK || hk&&MAX_HOOK);
// for (int i = 0; i < MAX_HOOK; i++) {
// if (hk[i].Address() == t->Addr()) {
// if (hk[i].Type() & USING_UNICODE)
// t->Status() |= USING_UNICODE;
// break;
// }
// }
// NtReleaseMutant(pr->hookman_mutex, 0);
//}
auto d = new TextThreadDelegate(t);
bool init = true;
foreach (TextThreadDelegate *it, threadDelegates_)
if (d->signature() == it->signature()) {
TextThreadDelegate::release(d);
d = it;
d->retain();
init = false;
break;
}
if (init) {
d->setInterval(messageInterval_);
d->setParentWindow(parentWindow_);
updateLinkedDelegate(d);
}
threadDelegates_[t] = d;
t->RegisterOutputCallBack(threadOutput, d);
//t->RegisterFilterCallBack(threadFilter, d);
DOUT("leave");
return 0;
}
// See also: HelloITH/main.cpp
DWORD Ihf::threadRemove(TextThread *t)
{
DOUT("enter");
Q_ASSERT(t);
auto p = threadDelegates_.find(t);
if (p != threadDelegates_.end()) {
auto d = p.value();
//if (!linkedDelegates_.isEmpty()) {
// linkedDelegates_.remove(d);
// while (auto k = linkedDelegates_.key(d))
// linkedDelegates_.remove(k);
//}
threadDelegates_.erase(p);
TextThreadDelegate::release(d);
}
#ifdef ITH_WITH_LINK
::Host_UnLinkAll(t->Number());
#endif // ITH_WITH_LINK
DOUT("leave");
return 0;
}
// See: HelloITH/main.cpp
DWORD Ihf::threadOutput(TextThread *t, BYTE *data, DWORD dataLength, DWORD newLine, PVOID pUserData, bool space)
{
DOUT("newLine =" << newLine << ", dataLength =" << dataLength << ", space =" << space);
Q_UNUSED(t)
Q_ASSERT(data);
Q_ASSERT(pUserData);
auto d = static_cast<TextThreadDelegate *>(pUserData);
//if (TextThreadDelegate *link = findLinkedDelegate(d))
// d = link;
Q_ASSERT(d);
if (!enabled_ ||
whitelistEnabled_ &&
!whitelistContains(d->signature()) &&
!(keptThreadName_[0] && d->nameEquals(keptThreadName_))) {
DOUT("leave: ignored");
return dataLength;
}
if (newLine)
d->touch();
//d->flush(); // new line data are ignored
else if (dataLength || space)
d->append(reinterpret_cast<char *>(data), dataLength, space);
//QString text = QString::fromLocal8Bit(reinterpret_cast<LPCSTR>(data), len);
DOUT("leave");
return dataLength;
}
//TextThreadDelegate *Ihf::findLinkedDelegate(TextThreadDelegate *d)
//{
// Q_ASSERT(d);
// if (!linkedDelegates_.isEmpty()) {
// auto p = linkedDelegates_.find(d);
// if (p != linkedDelegates_.end())
// return p.value();
// }
// return nullptr;
//}
void Ihf::updateLinkedDelegate(TextThreadDelegate *d)
{
#ifdef ITH_WITH_LINK
Q_ASSERT(t);
foreach (TextThreadDelegate *it, threadDelegates_)
if (it->delegateOf(d))
::Host_AddLink(d->threadNumber(), it->threadNumber());
else if (d->delegateOf(it))
::Host_AddLink(it->threadNumber(), d->threadNumber());
#else
Q_UNUSED(d);
#endif // ITH_WITH_LINK
}
// - Injection -
// See: Host_InjectByPID in IHF/main.cpp
// See: InjectThread in ITH/profile.cpp
bool Ihf::attachProcess(DWORD pid)
{
DOUT("enter: pid =" << pid);
bool ok = ::Host_InjectByPID(pid);
//enum { AttachDelay = 500 }; // in msec
//::Sleep(AttachDelay);
DOUT("leave: ret =" << ok);
return ok;
}
// See: Host_ActiveDetachProcess in IHF/main.cpp
bool Ihf::detachProcess(DWORD pid) { return ::Host_ActiveDetachProcess(pid); }
bool Ihf::hijackProcess(DWORD pid) { return ::Host_HijackProcess(pid); }
// - Hook -
// See: Host_ModifyHook in IHF/main.cpp
bool Ihf::updateHook(ulong pid, const QString &code)
{
DOUT("enter: pid =" << pid << ", code =" << code);
Q_ASSERT(pid);
HookParam hp = {};
if (!Ith::parseHookCode(code, &hp)) {
DOUT("leave: failed to parse hook code");
return false;
}
DWORD hh = ::Host_ModifyHook(pid, &hp);
bool ok = ~hh;
DOUT("leave: ret =" << ok);
return ok;
}
// See: Host_InsertHook in IHF/main.cpp
bool Ihf::addHook(ulong pid, const QString &code, const QString &name, bool verbose)
{
DOUT("enter: pid =" << pid << ", name =" << name << ", code =" << code);
Q_ASSERT(pid);
if (hookAddresses_.contains(code)) {
DOUT("leave: already added");
return false;
}
HookParam hp = {};
if (!Ith::parseHookCode(code, &hp, verbose)) {
DOUT("leave: failed to parse hook code");
return false;
}
DWORD hh = ::Host_InsertHook(pid, &hp, name.toAscii());
//DWORD hh = ::NewHook(hp, nameBuf);
bool ok = ~hh;
if (ok && hp.address) {
DOUT("hook address =" << hp.address);
hookAddresses_[code] = hp.address;
}
DOUT("leave: ok =" << ok);
return ok;
}
// See: Host_RemoveHook in IHF/main.cpp
bool Ihf::removeHook(ulong pid, const QString &code)
{
DOUT("enter: pid =" << pid << ", code =" << code);
Q_ASSERT(pid);
auto p = hookAddresses_.find(code);
if (p == hookAddresses_.end()) {
DOUT("leave: hook not added");
return false;
}
DWORD addr = p.value();
Q_ASSERT(addr);
hookAddresses_.erase(p);
DWORD hh = ::Host_RemoveHook(pid, addr);
bool ok = ~hh;
DOUT("leave: ret =" << ok);
return ok;
}
bool Ihf::verifyHookCode(const QString &code)
{ return Ith::verifyHookCode(code); }
// - Whitelist -
QList<qint32> Ihf::whitelist()
{
QList<qint32> ret;
const qint32 *p = whitelist_;
while (*p)
ret.append(*p++);
return ret;
}
void Ihf::clearWhitelist() { *whitelist_= 0; }
void Ihf::setWhitelist(const QList<qint32> &l)
{
qint32 *p = whitelist_;
if (!l.isEmpty())
foreach (qint32 it, l) {
*p++ = it;
if (p >= whitelist_ + WhitelistSize)
break;
}
whitelist_[qMin(l.size(), WhitelistSize -1)] = 0;
}
bool Ihf::whitelistContains(qint32 signature)
{
const qint32 *p = whitelist_;
while (*p)
if (signature == *p++)
return true;
return false;
}
// EOF
/*
BYTE LeadByteTable[0x100] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1
};
DWORD Ihf::threadFilter(TextThread *thread, BYTE *out, DWORD len, DWORD new_line, PVOID data)
{
DWORD status = thread->Status();
if (!new_line && thread->Number() != 0)
{
if (status & USING_UNICODE)
{
DWORD i, j;
len >>= 1;
WCHAR c, *str = (LPWSTR)out;
for (i = 0, j = 0; i < len; i++)
{
c = str[i];
//if (!uni_filter->Check(c))
str[j++] = c;
}
memset(str + j, 0, (len - j) << 1);
len = j << 1;
}
else
{
WORD c;
DWORD i, j;
for (i = 0, j = 0; i < len; i++)
{
c = out[i];
if (LeadByteTable[c] == 1)
{
//if (!mb_filter->Check(c))
out[j++] = c & 0xFF;
}
else if (i + 1 < len)
{
c = out[i + 1];
c <<= 8;
c |= out[i];
//if (!mb_filter->Check(c))
{
out[j++] = c & 0xFF;
out[j++] = c >> 8;
}
i++;
}
}
memset(out + j, 0, len - j);
len = j;
}
}
return len;
}
*/
/*
// jichi: 10/15/2011: FIXME: This overload will infect the entire program,
// even source files that exclude this header, which is unexpected.
// No idea what is the trade off of this behavior on performance and liability.
// Lots of Qt stuff doesn't work such as QString::toStdString.
// I have to use dynamic linkage to avoid being polluted by this module.
//
// original author: HEAP_ZERO_MEMORY flag is critical. All new object are assumed with zero initialized.
// jichi: 10/20/2011: I think the only reason to use Rtl heap here is to ensure HEAP_ZERO_MEMORY,
// which is really a bad programming style and incur unstability on heap memory allocation.
// ::RtlFreeHeap crash on DLL debug mode. Replace it with standard malloc/free.
// ::hHeap handle is also removed from ith/sys.c.cc
inline void * __cdecl operator new(size_t lSize)
{ return ::RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize); }
inline void * __cdecl operator new[](size_t lSize)
{ return ::RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize); }
inline void __cdecl operator delete(void *pBlock)
{ ::RtlFreeHeap(::hHeap, 0, pBlock); }
inline void __cdecl operator delete[](void* pBlock)
{ ::RtlFreeHeap(::hHeap, 0, pBlock); }
#include <cstdlib>
#include <cstring>
inline void * __cdecl operator new(size_t size) throw()
{
if (!size) // When the value of the expression in a direct-new-declarator is zero,
size = 4; // the allocation function is called to allocatean array with no elements.(ISO)
void *p = malloc(size);
if (p)
memset(p, 0, size);
return p;
}
inline void * __cdecl operator new[](size_t size) throw()
{
if (!size) // When the value of the expression in a direct-new-declarator is zero,
size = 4; // the allocation function is called to allocatean array with no elements.(ISO)
void *p = malloc(size);
if (p)
memset(p, 0, size);
return p;
}
inline void __cdecl operator delete(void *p) throw() { free(p); }
inline void __cdecl operator delete[](void *p) throw() { free(p); }
*/
//QString
//Ihf::getHookNameById(ulong hookId)
//{
// QString ret;
// if (hookId) {
// auto p = reinterpret_cast<TextThread *>(hookId);
// if (p->good())
// ret = p->name();
// }
// return ret;
//}
//DWORD ProcessAttach(DWORD pid)
//{
// DOUT("process attached, pid =" << pid);
// return 0;
//}
//DWORD ProcessDetach(DWORD pid)
//{
// DOUT("process detached, pid =" << pid);
// return 0;
//}
//DWORD ProcessNewHook(DWORD pid)
//{
// DOUT("process has new hook inserted, pid =" << pid);
// return 0;
//}

117
vnr/texthook/ihf_p.h Normal file
View File

@ -0,0 +1,117 @@
#pragma once
// ihf_p.h
// 10/15/2011 jichi
// Internal header.
// Wrapper of IHF functions.
#include <QtCore/QHash>
#include <QtCore/QList>
#include <QtCore/QString>
#include <QtGui/qwindowdefs.h> // for WId
//struct Settings; // opaque in ith/host/settings.h
class HookManager; // opaque in ith/host/hookman.h
class TextThread; // opaque in ith/host/textthread.h
class TextThreadDelegate;
enum { ITH_THREAD_NAME_CAPACITY = 0x200 }; // used internally by ITH
class Ihf
{
Ihf() {} // Singleton
static bool enabled_;
//static Settings *settings_;
static HookManager *hookManager_;
static qint64 messageInterval_;
static WId parentWindow_;
static QHash<TextThread *, TextThreadDelegate *> threadDelegates_;
//static QHash<TextThreadDelegate *, TextThreadDelegate *> linkedDelegates_;
static QHash<QString, ulong> hookAddresses_;
enum { WhitelistSize = 0x20 + 1 }; // ITH capacity is 0x20
static qint32 whitelist_[WhitelistSize]; // List of signatures. The last element is zero. I.e., at most BlackSize-1 threads.
static bool whitelistEnabled_;
static char keptThreadName_[ITH_THREAD_NAME_CAPACITY];
//static QString userDefinedThreadName_;
public:
// - Initialization -
static void init();
static void destroy();
static bool load();
static bool isLoaded() { return hookManager_; }
static void unload();
// - Properties -
static bool isEnabled() { return enabled_; }
static void setEnabled(bool t) { enabled_ = t; }
/// A valid window handle is required to make ITH work
static WId parentWindow() { return parentWindow_; }
static void setParentWindow(WId hwnd) { parentWindow_ = hwnd; }
/// Timeout (msecs) for a text message
static qint64 messageInterval() { return messageInterval_; }
static void setMessageInterval(qint64 msecs) { messageInterval_ = msecs; }
// - Injection -
static bool attachProcess(ulong pid);
static bool detachProcess(ulong pid);
static bool hijackProcess(ulong pid);
/// Add hook code
static bool addHook(ulong pid, const QString &code, const QString &name = QString(), bool verbose = true);
static bool updateHook(ulong pid, const QString &code); // not used
static bool removeHook(ulong pid, const QString &code);
static bool verifyHookCode(const QString &code);
// - Whitelist -
static bool isWhitelistEnabled() { return whitelistEnabled_; }
static void setWhitelistEnabled(bool t) { whitelistEnabled_ = t; }
static QList<qint32> whitelist();
static void setWhitelist(const QList<qint32> &l);
static void clearWhitelist();
//static QString userDefinedThreadName() { return userDefinedThreadName_; }
//static void setUserDefinedThreadName(const QString &val) { userDefinedThreadName_ = val; }
static const char *keptThreadName() { return keptThreadName_; }
static void setKeptThreadName(const QString &v)
{
if (v.size() < ITH_THREAD_NAME_CAPACITY)
::strcpy(keptThreadName_, v.toAscii());
else
setKeptThreadName(v.left(ITH_THREAD_NAME_CAPACITY - 1));
}
private:
static bool whitelistContains(qint32 signature);
// - Callbacks -
//static ulong processAttach(ulong pid);
//static ulong processDetach(ulong pid);
//static ulong processNewHook(ulong pid);
static ulong threadCreate(_In_ TextThread *t);
static ulong threadRemove(_In_ TextThread *t);
static ulong threadOutput(_In_ TextThread *t, _In_ uchar *data, _In_ ulong dataLength, _In_ ulong bNewLine, _In_ void *pUserData, _In_ bool space);
//static ulong threadFilter(_In_ TextThread *t, _Out_ uchar *data, _In_ ulong dataLength, _In_ ulong bNewLine, _In_ void *pUserData);
//static ulong threadReset(TextThread *t);
//static void consoleOutput(const char *text);
//static void consoleOutputW(const wchar_t *text);
// - Linked threasds -
private:
//static TextThreadDelegate *findLinkedDelegate(TextThreadDelegate *d);
static void updateLinkedDelegate(TextThreadDelegate *d);
};
// EOF

275
vnr/texthook/ith_p.cc Normal file
View File

@ -0,0 +1,275 @@
// ith_p.cc
// 10/15/2011 jichi
#include "texthook/ith_p.h"
#include "vnrhook/include/const.h"
#include "vnrhook/include/types.h"
#include <string>
#define DEBUG "ith_p.cc"
#include "sakurakit/skdebug.h"
// HookParam copied from ITH/common.h:
// struct HookParam // size = 40 (0x24)
// {
// typedef void (*DataFun)(DWORD, HookParam*, DWORD*, DWORD*, DWORD*);
//
// DWORD addr; // 4
// DWORD off, // 8
// ind, // 12
// split, // 16
// split_ind; // 20
// DWORD module, // 24
// function; // 28
// DataFun text_fun; // 32, jichi: is this the same in x86 and x86_64?
// DWORD type; // 36
// WORD length_offset; // 38
// BYTE hook_len, // 39
// recover_len; // 40
// };
// - Implementation Details -
namespace { namespace detail { // unnamed
// ITH ORIGINAL CODE BEGIN
// See: ITH/ITH.h
// Revision: 133
inline DWORD Hash(_In_ LPWSTR module, int length = -1)
{
bool flag = length == -1;
DWORD hash = 0;
for (; *module && (flag || length--); module++)
hash = _rotr(hash,7) + *module; //hash=((hash>>7)|(hash<<25))+(*module);
return hash;
}
// See: ITH/command.cpp
// Revision: 133
//
// jichi note: str[0xF] will be modified and restored.
// So, the buffer of str must be larger than 0xF.
int Convert(_In_ LPWSTR str, _Out_ DWORD *num, _In_ LPWSTR delim)
{
if (!num)
return -1;
WCHAR t = *str,
tc = *(str + 0xF);
WCHAR temp[0x10] = {};
LPWSTR it = temp,
istr = str,
id = temp;
if (delim) {
id = wcschr(delim, t);
str[0xF] = delim[0]; // reset str[0xF] in case of out-of-bound iteration
}
else
str[0xF] = 0; // reset str[0xF] in case of out-of-bound iteration
while (!id && t) {
*it = t;
it++; istr++;
t = *istr;
if (delim)
id = wcschr(delim, t);
}
swscanf(temp, L"%x", num);
str[0xF] = tc; // restore the str[0xF]
if (!id || istr - str == 0xF)
return -1;
if (!t)
return istr - str; // >= 0
else
return id - delim; // >= 0
}
// See: ITH/command.cpp
// Revision: 133
//
// jichi note: str[0xF] will be modified and restored.
// So, the buffer of cmd must be larger than 0xF*2 = 0x1F.
bool Parse(_In_ LPWSTR cmd, _Out_ HookParam &hp)
{
::memset(&hp, 0, sizeof(hp));
int t;
bool accept = false;
DWORD *data = &hp.offset; //
LPWSTR offset = cmd + 1;
LPWSTR delim_str = L":*@!";
LPWSTR delim = delim_str;
if (*offset == L'n' || *offset == 'N') {
offset++;
hp.type |= NO_CONTEXT;
}
// jichi 4/25/2015: Add support for fixing hook
if (*offset == L'f' || *offset == 'F') {
offset++;
hp.type |= FIXING_SPLIT;
}
if (*offset == L'j' || *offset == 'J') { // 11/22/2015: J stands for Japanese only
offset++;
hp.type |= NO_ASCII;
}
while (!accept) {
t = Convert(offset, data, delim);
if (t < 0)
return false; //ConsoleOutput(L"Syntax error.");
offset = ::wcschr(offset , delim[t]);
if (offset)
offset++; // skip the current delim
else //goto _error;
return false; //ConsoleOutput(L"Syntax error.");
switch (delim[t]) {
case L':':
data = &hp.split;
delim = delim_str + 1;
hp.type |= USING_SPLIT;
break;
case L'*':
if (hp.split) {
data = &hp.split_index;
delim = delim_str + 2;
hp.type |= SPLIT_INDIRECT;
}
else {
hp.type |= DATA_INDIRECT;
data = &hp.index;
}
break;
case L'@':
accept = true;
break;
}
}
t = Convert(offset, &hp.address, delim_str);
if (t < 0)
return false;
if (hp.offset & 0x80000000)
hp.offset -= 4;
if (hp.split & 0x80000000)
hp.split -= 4;
LPWSTR temp = offset;
offset = ::wcschr(offset, L':');
if (offset) {
hp.type |= MODULE_OFFSET;
offset++;
delim = ::wcschr(offset, L':');
if (delim) {
*delim = 0;
delim++;
_wcslwr(offset);
hp.function = Hash(delim);
hp.module = Hash(offset, delim - offset - 1);
hp.type |= FUNCTION_OFFSET;
}
else
hp.module = Hash(_wcslwr(offset));
} else {
offset = ::wcschr(temp, L'!');
if (offset) {
hp.type |= MODULE_OFFSET;
swscanf(offset + 1, L"%x", &hp.module);
offset = ::wcschr(offset + 1, L'!');
if (offset) {
hp.type |= FUNCTION_OFFSET;
swscanf(offset + 1, L"%x", &hp.function);
}
}
}
switch (*cmd) {
case L's':
case L'S':
hp.type |= USING_STRING;
break;
case L'e':
case L'E':
hp.type |= STRING_LAST_CHAR;
case L'a':
case L'A':
hp.type |= BIG_ENDIAN;
hp.length_offset = 1;
break;
case L'b':
case L'B':
hp.length_offset = 1;
break;
// jichi 12/7/2014: Disabled
//case L'h':
//case L'H':
// hp.type |= PRINT_DWORD;
case L'q':
case L'Q':
hp.type |= USING_STRING | USING_UNICODE;
break;
case L'l':
case L'L':
hp.type |= STRING_LAST_CHAR;
case L'w':
case L'W':
hp.type |= USING_UNICODE;
hp.length_offset = 1;
break;
default: ;
}
//ConsoleOutput(L"Try to insert additional hook.");
return true;
}
// ITH ORIGINAL CODE END
}} // unnamed detail
// - ITH API -
// Sample code: L"/HS-4:-14@4383C0" (WHITE ALBUM 2)
bool Ith::parseHookCode(const QString &code, HookParam *hp, bool verbose)
{
#define HCODE_PREFIX "/H"
enum { HCODE_PREFIX_LEN = sizeof(HCODE_PREFIX) -1 }; // 2
if (!hp || !code.startsWith(HCODE_PREFIX))
return false;
if (verbose)
DOUT("enter: code =" << code);
else
DOUT("enter");
size_t bufsize = qMax(0xFF, code.size() + 1); // in case detail::Convert modify the buffer
auto buf = new wchar_t[bufsize];
code.toWCharArray(buf);
buf[code.size()] = 0;
bool ret = detail::Parse(buf + HCODE_PREFIX_LEN, *hp);
delete[] buf;
#ifdef DEBUG
if (ret && verbose)
qDebug()
<< "addr:" << hp->address
<< ", text_fun:" << hp->text_fun
<< ", function:"<< hp->function
<< ", hook_len:" << hp->hook_len
<< ", ind:" << hp->index
<< ", length_offset:" << hp->length_offset
<< ", module:" << hp->module
<< ", off:" <<hp->offset
<< ", recover_len:" << hp->recover_len
<< ", split:" << hp->split
<< ", split_ind:" << hp->split_index
<< ", type:" << hp->type;
#endif // DEBUG
DOUT("leave: ret =" << ret);
return ret;
#undef HOOK_CODE_PREFIX
}
bool Ith::verifyHookCode(const QString &code)
{
HookParam hp = {};
return parseHookCode(code, &hp);
}
// EOF

20
vnr/texthook/ith_p.h Normal file
View File

@ -0,0 +1,20 @@
#pragma once
// ith_p.h
// 10/15/2011 jichi
// Internal header.
// Wrapper of functions from ITH.
#include <QtCore/QString>
struct HookParam; // opaque, declared in ITH/common.h
namespace Ith {
/// Parse hook code, and save the result to hook param if succeeded.
bool parseHookCode(_In_ const QString &code, _Out_ HookParam *hp, bool verbose = true);
bool verifyHookCode(_In_ const QString &code);
} // namespace Ith
// EOF

414
vnr/texthook/texthook.cc Normal file
View File

@ -0,0 +1,414 @@
// texthook.cc
// 10/14/2011 jichi
#include "texthook/texthook.h"
#include "texthook/texthook_p.h"
#include "texthook/ihf_p.h"
#include "texthook/textthread_p.h"
#include "texthook/winapi_p.h"
#include <QtCore>
//#define DEBUG "texthook.cc"
#include "sakurakit/skdebug.h"
//#include <ITH/IHF_SYS.h>
//namespace { int _ = IthInitSystemService(); }
/** Private class */
TextHookPrivate *TextHookPrivate::instance_;
/** Public class */
// - Construction -
//TextHook *TextHook::g_;
//TextHook *TextHook::globalInstance() { static Self g; return &g; }
//TextHook::TextHook(QObject *parent)
// : Base(parent), d_(new D)
//{}
TextHook::TextHook(QObject *parent)
: Base(parent), d_(new D(this))
{
Ihf::init();
//Ihf::setUserDefinedThreadName(d_->source);
DOUT("pass");
}
TextHook::~TextHook()
{
DOUT("enter");
if (isActive())
stop();
delete d_;
Ihf::destroy();
DOUT("leave");
}
// - Properties -
int TextHook::dataCapacity() const
{ return TextThreadDelegate::capacity(); }
void TextHook::setDataCapacity(int value)
{ TextThreadDelegate::setCapacity(value); }
bool TextHook::removesRepeat() const
{ return TextThreadDelegate::removesRepeat(); }
void TextHook::setRemovesRepeat(bool value)
{ TextThreadDelegate::setRemovesRepeat(value); }
bool TextHook::keepsSpace() const
{ return TextThreadDelegate::keepsSpace(); }
void TextHook::setKeepsSpace(bool value)
{ TextThreadDelegate::setKeepsSpace(value); }
bool TextHook::wideCharacter() const
{ return TextThreadDelegate::wideCharacter(); }
void TextHook::setWideCharacter(bool value)
{ TextThreadDelegate::setWideCharacter(value); }
// see: ITH/common.h
int TextHook::capacity() const
{ return 0x20; }
QString TextHook::defaultHookName() const
{ return d_->source; }
void TextHook::setDefaultHookName(const QString &name)
{
d_->source = name;
//Ihf::setUserDefinedThreadName(name);
}
bool TextHook::isEnabled() const
{ return d_->enabled; }
void TextHook::setEnabled(bool t)
{
d_->enabled = t;
Ihf::setEnabled(t);
}
bool TextHook::isActive() const
{ return Ihf::isLoaded(); }
void TextHook::start()
{ Ihf::load(); }
void TextHook::stop()
{
if (!isEmpty())
clear();
Ihf::unload();
}
WId TextHook::parentWinId() const { return Ihf::parentWindow(); }
void TextHook::setParentWinId(WId hwnd) { Ihf::setParentWindow(hwnd); }
int TextHook::interval() const
{ return Ihf::messageInterval(); }
void TextHook::setInterval(int msecs)
{ Ihf::setMessageInterval(msecs); }
// - Injection -
void TextHook::clear()
{
DOUT("enter");
foreach (ulong pid, d_->pids)
detachProcess(pid);
if (!d_->hooks.isEmpty())
d_->hooks.clear();
clearThreadWhitelist();
DOUT("leave");
}
bool TextHook::containsProcess(ulong pid) const { return d_->pids.contains(pid); }
bool TextHook::isEmpty() const { return d_->pids.isEmpty(); }
//QList<ulong> TextHook::attachedProcesses(bool checkActive) const
//{
// if (isEmpty() || !checkActive)
// return d_->pids;
//
// QList<ulong> ret;
// foreach (ulong pid, d_->pids)
// if (winapi::IsProcessActiveWithId(pid))
// ret.append(pid);
// return ret;
//}
//ulong TextHook::currentProccess() const
//{ return anyAttachedProcess(true); } // check active = true
//ulong TextHook::anyAttachedProcess(bool checkActive) const
//{
// if (isEmpty())
// return 0;
// if (!checkActive)
// return d_->pids.first();
//
// foreach (ulong pid, d_->pids)
// if (winapi::IsProcessActiveWithId(pid))
// return pid;
// return 0;
//}
//bool TextHook::attachOneProcess(ulong pid, bool checkActive)
//{
// DOUT("enter: pid =" << pid);
// DOUT("isAttached =" << containsProcess(pid));
// if (!isActive())
// start();
//
// if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
// DOUT("leave: ret = false, isActive = false");
// return false;
// }
//
// if (containsProcess(pid)) {
// DOUT("leave: pid already attached");
// return true;
// }
//
// detachAllProcesses();
//
// bool ret = Ihf::attachProcess(pid);
// if (ret && !containsProcess(pid)) {
// d_->pids.removeAll(pid);
// d_->pids.append(pid);
// emit processAttached(pid);
// }
//
// DOUT("leave: ret =" << ret);
// return ret;
//}
bool TextHook::attachProcess(ulong pid, bool checkActive)
{
DOUT("enter: pid =" << pid << ", isAttached =" << containsProcess(pid));
if (!isActive())
start();
Q_ASSERT(isActive());
DOUT("isActive =" << isActive());
if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
DOUT("leave: ret = false, isActive = false");
return false;
}
bool ret = Ihf::attachProcess(pid);
if (ret) {
d_->pids.insert(pid);
emit processAttached(pid);
}
DOUT("leave: ret =" << ret);
return ret;
}
bool TextHook::detachProcess(ulong pid, bool checkActive)
{
DOUT("enter: pid =" << pid << ", isAttached =" << containsProcess(pid));
Q_ASSERT(isActive());
auto it = d_->pids.find(pid);
if (it == d_->pids.end()) {
DOUT("leave: ret = false, not attached");
return false;
}
d_->pids.erase(it);
d_->hooks.remove(pid);
if (checkActive && !winapi::IsProcessActiveWithId(pid)) {
emit processDetached(pid);
DOUT("leave: ret = false, isActive = false");
return false;
}
bool ret = Ihf::detachProcess(pid);
//try {
// ret = Ihf::detachProcess(pid);
//} catch (...) {
// DOUT("warning: detach exception");
//}
emit processDetached(pid);
DOUT("leave: ret =" << ret);
return ret;
}
bool TextHook::hijackProcess(ulong pid)
{
DOUT("enter: pid =" << pid);
Q_ASSERT(isActive());
if (!containsProcess(pid)) {
DOUT("leave: aborted, process not attached");
return false;
}
// 7/12/2015: Function disabled
return true;
//bool ret = Ihf::hijackProcess(pid);
//DOUT("leave: ret =" << ret);
//return ret;
}
//void TextHook::detachAllProcesses()
//{
// DOUT("enter");
// foreach (ulong pid, d_->pids)
// detachProcess(pid);
// if (!d_->hooks.isEmpty())
// d_->hooks.clear();
// DOUT("leave");
//}
// - Hook -
//bool TextHook::containsHook(ulong pid) const
//{ return d_->hooks.contains(pid); }
//
//bool TextHook::containsHook(ulong pid, const QString &code) const
//{ return processHook(pid) == code; }
bool TextHook::addHookCode(ulong pid, const QString &code, const QString &name, bool verbose)
{
DOUT("enter: pid =" << pid << ", code =" << code);
if (isEmpty() || !containsProcess(pid)) {
DOUT("leave: failed, process not attached");
return false;
}
if (d_->hooks.contains(pid)) {
DOUT("leave: failed, hook already exists");
return false;
}
bool ok = Ihf::addHook(pid, code,
name.isEmpty() ? defaultHookName() : name,
verbose);
if (ok)
d_->hooks[pid] = code;
DOUT("leave: ret =" << ok);
return ok;
}
bool TextHook::verifyHookCode(const QString &code) { return Ihf::verifyHookCode(code); }
bool TextHook::removeHookCode(ulong pid)
{
DOUT("enter");
auto p = d_->hooks.find(pid);
if (p == d_->hooks.end()) {
DOUT("leave: not hooked");
return false;
}
//DOUT("remove existing hook, THIS SHOULD NOT HAPPEN");
bool ok = Ihf::removeHook(pid, p.value());
d_->hooks.erase(p);
DOUT("leave: ret =" << ok);
return ok;
}
//QString TextHook::processHook(ulong pid) const
//{
// auto p = d_->hooks.find(pid);
// return p == d_->hooks.end() ? QString() : p.value();
//}
bool TextHook::isThreadWhitelistEnabled() const { return Ihf::isWhitelistEnabled(); }
void TextHook::setThreadWhitelistEnabled(bool t) { Ihf::setWhitelistEnabled(t); }
QList<qint32> TextHook::threadWhitelist() const { return Ihf::whitelist(); }
void TextHook::setThreadWhitelist(const QList<qint32> &sigs) { Ihf::setWhitelist(sigs); }
void TextHook::clearThreadWhitelist() { Ihf::clearWhitelist(); }
QString TextHook::keptThreadName() const { return Ihf::keptThreadName(); }
void TextHook::setKeptThreadName(const QString &v) { Ihf::setKeptThreadName(v); }
// EOF
/*
QString
TextHook::guessEncodingForFile(const QString &fileName)
{
static QHash<QString, QString> db;
if (db.isEmpty()) {
db["malie.exe"] = "UTF-16";
}
auto p = db.find(fileName);
return p == db.end() ? QString() : p.value();
}
// - Helpers -
bool
TextHook::isStandardHookName(const QString &name) const
{
static QSet<uint> hashes;
if (hashes.isEmpty()) {
#define ADD(_text) hashes.insert(qHash(QString(_text)))
ADD("ConsoleOutput");
ADD("GetTextExtentPoint32A");
ADD("GetGlyphOutlineA");
ADD("ExtTextOutA");
ADD("TextOutA");
ADD("GetCharABCWidthsA");
ADD("DrawTextA");
ADD("DrawTextExA");
ADD("GetTextExtentPoint32W");
ADD("GetGlyphOutlineW");
ADD("ExtTextOutW");
ADD("TextOutW");
ADD("GetCharABCWidthsW");
ADD("DrawTextW");
ADD("DrawTextExW");
#undef ADD
}
uint h = qHash(name);
return hashes.contains(h);
}
bool
TextHook::isKnownHookForProcess(const QString &hook, const QString &proc) const
{
// TODO: update database on line periodically
qDebug() << "qth::isKnownHookForProcess: hook =" << hook << ", proc =" << proc;
static QSet<uint> hashes;
if (hashes.isEmpty()) {
#define ADD(_hook, _proc) hashes.insert(qHash(QString(_hook) + "\n" + _proc))
//ADD("Malie", "malie"); // light
ADD("GetGlyphOutlineA", "STEINSGATE");
ADD("StuffScriptEngine", "EVOLIMIT");
#undef ADD
}
uint h = qHash(hook + "\n" + proc);
return hashes.contains(h);
}
QString
TextHook::hookNameById(ulong hookId) const
{
//return Ihf::getHookNameById(hookId);
// FIXME: supposed to be the engine name, unimplemented
Q_UNUSED(hookId)
return QString();
}
*/

101
vnr/texthook/texthook.h Normal file
View File

@ -0,0 +1,101 @@
#pragma once
// texthook.h
// 10/14/2011 jichi
#include "texthook_config.h"
#include "sakurakit/skglobal.h"
#include <QtCore/QByteArray>
#include <QtCore/QObject>
#include <QtCore/QList>
#include <QtCore/QString>
#include <QtGui/qwindowdefs.h> // for WId
class TextHookPrivate;
/// Singleton class. Only one instance is allowed.
class TEXTHOOK_EXPORT TextHook : public QObject
{
Q_OBJECT
Q_DISABLE_COPY(TextHook)
SK_EXTEND_CLASS(TextHook, QObject)
SK_DECLARE_PRIVATE(TextHookPrivate)
// - Construction -
public:
explicit TextHook(QObject *parent = nullptr);
~TextHook();
signals:
void dataReceived(QByteArray raw, QByteArray rendered, qint32 signature, QString source);
void processAttached(qint64 pid);
void processDetached(qint64 pid);
// - Properties -
public:
/// Limited by ITH
int capacity() const;
bool isEnabled() const;
void setEnabled(bool t);
WId parentWinId() const; ///< Must be set to a valid window so that ::SetTimer works
void setParentWinId(WId hwnd);
int interval() const; ///< Time to differentiate sentences
void setInterval(int msecs);
int dataCapacity() const; ///< Maximum text length
void setDataCapacity(int value);
bool removesRepeat() const;
void setRemovesRepeat(bool value);
bool keepsSpace() const;
void setKeepsSpace(bool value);
bool wideCharacter() const;
void setWideCharacter(bool value);
QString defaultHookName() const; ///< The default one is "H-code"
void setDefaultHookName(const QString &name);
bool isActive() const;
void start();
void stop();
void clear();
// - Injection -
public:
//bool attachOneProcess(ulong pid, bool checkActive = false);
bool attachProcess(ulong pid, bool checkActive = false);
bool detachProcess(ulong pid, bool checkActive = false);
bool hijackProcess(ulong pid);
//void detachAllProcesses();
//QList<ulong> attachedProcesses(bool checkActive = false) const;
//ulong anyAttachedProcess(bool checkActive = false) const;
//ulong currentProccess() const;
bool containsProcess(ulong pid) const;
bool isEmpty() const; ///< Return true if at least one process is attached
bool addHookCode(ulong pid, const QString &code, const QString &name = QString(), bool verbose = true);
static bool verifyHookCode(const QString &code); ///< Return if hcode is valid
//bool containsHook(ulong pid) const;
//bool containsHook(ulong pid, const QString &code) const;
//QString processHook(ulong pid) const;
//QString currentHook() const { return processHook(currentProccess()); }
bool removeHookCode(ulong pid); ///< Assume atmost one hcode per process
// - Whitelist -
public:
bool isThreadWhitelistEnabled() const;
void setThreadWhitelistEnabled(bool t);
QList<qint32> threadWhitelist() const;
void setThreadWhitelist(const QList<qint32> &signatures);
void clearThreadWhitelist();
// Note: len(v) must be smaller than 0x200
void setKeptThreadName(const QString &v);
QString keptThreadName() const;
};
// EOF

20
vnr/texthook/texthook.pri Normal file
View File

@ -0,0 +1,20 @@
# texthook.pri
# 10/13/2011 jichi
DEFINES += WITH_LIB_TEXTHOOK
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
QT += core
LIBS += -ltexthook
HEADERS += \
$$PWD/texthook_config.h
# texthook.h should not be in HEADERS, or it will be processed by moc
OTHER_FILES += \
$$PWD/texthook.h \
$$PWD/texthook.pro
# EOF

61
vnr/texthook/texthook.pro Normal file
View File

@ -0,0 +1,61 @@
# texthook.pro
# 10/13/2011 jichi
# Build ith texthook dll.
CONFIG += noqtgui dll #eha # eha will catch all exceptions, but does not work on Windows XP
include(../../../config.pri)
include(host/host.pri)
include($$LIBDIR/winmutex/winmutex.pri)
include($$LIBDIR/wintimer/wintimer.pri)
include($$LIBDIR/windbg/windbg.pri)
#include($$LIBDIR/winmaker/winmaker.pri)
# TODO: Get rid of dependence on ITHSYS and NT APIs
include($$PLUGINDIR/vnrhook/vnrhook.pri)
include($$PLUGINDIR/ithsys/ithsys.pri)
LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
DEFINES += ITH_HAS_CRT # Use native CRT
# TODO: Get rid of dependence on msvc's swprintf
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
## Libraries
QT += core
QT -= gui
## Sources
TEMPLATE = lib
TARGET = texthook
#CONFIG += staticlib
DEFINES += TEXTHOOK_BUILD_LIB
HEADERS += \
growl.h \
ihf_p.h \
ith_p.h \
texthook_config.h \
texthook.h \
texthook_p.h \
textthread_p.h \
winapi_p.h
SOURCES += \
ihf_p.cc \
ith_p.cc \
texthook.cc \
textthread_p.cc \
winapi_p.cc
#!wince*: LIBS += -lshell32
#RC_FILE += texthook.rc
OTHER_FILES += \
texthook.pri \
texthook_static.pri \
texthook.rc
# EOF

38
vnr/texthook/texthook.rc Normal file
View File

@ -0,0 +1,38 @@
/*
* texthook.rc
* 10/20/2011 jichi
*/
#if defined(UNDER_CE)
# include <winbase.h>
#else
# include <winver.h>
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,0
PRODUCTVERSION 1,0,0,0
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS__WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904B0"
BEGIN
VALUE "CompanyName", "Sakuradite\0"
VALUE "FileDescription", "Text Hook\0"
VALUE "FileVersion", "1.0.0.0\0"
VALUE "LegalCopyright", "Copyright (C) 2012.\0"
VALUE "OriginalFilename", "texthook.dll\0"
VALUE "ProductName", "texthook\0"
END
END
END
/* End of Version info */

View File

@ -0,0 +1,20 @@
#pragma once
// texthook_config.h
// 10/20/2011 jichi
//#define TEXTHOOK_EXPORT
#ifndef TEXTHOOK_EXPORT
# ifdef TEXTHOOK_STATIC_LIB
# define TEXTHOOK_EXPORT
# elif defined(TEXTHOOK_BUILD_LIB)
# define TEXTHOOK_EXPORT Q_DECL_EXPORT
# else
# define TEXTHOOK_EXPORT Q_DECL_IMPORT
# endif
#endif // TEXTHOOK_EXPORT
#define TEXTHOOK_DEFAULT_NAME "H-code"
// EOF

39
vnr/texthook/texthook_p.h Normal file
View File

@ -0,0 +1,39 @@
#pragma once
// texthook_p.h
// 10/14/2011 jichi
// Internal header.
// Defines TextHook private data.
#include "texthook/texthook.h"
#include <QtCore/QHash>
#include <QtCore/QSet>
// - Private -
class TextHookPrivate
{
SK_CLASS(TextHookPrivate)
SK_DECLARE_PUBLIC(TextHook)
static Self *instance_; // global instance
bool enabled;
QString source;
QSet<ulong> pids;
QHash<ulong, QString> hooks; // ITH hook code, indexed by pid
explicit TextHookPrivate(Q *q)
: q_(q), enabled(true), source(TEXTHOOK_DEFAULT_NAME) { instance_ = this; }
~TextHookPrivate() { instance_ = nullptr; }
public:
static void sendData(const QByteArray &rawData, const QByteArray &renderedData, qint32 signature, const QString &name)
{
if (instance_ && instance_->q_->isEnabled())
emit instance_->q_->dataReceived(rawData, renderedData, signature, name);
}
};
// EOF

View File

@ -0,0 +1,73 @@
# texthook_static.pri
# 10/13/2011 jichi
include(../../../config.pri)
include($$LIBDIR/wintimer/wintimer.pri)
## Libraries
#DEFINES += _ITH_DEBUG_MEMORY
QT += core
INCLUDEPATH += $$PWD
DEPENDPATH += $$PWD
#WDK_HOME = c:/winddk
#LIBS += -L$$WDK_HOME/lib
# override folder must come before winddk/inc/api
#INCLUDEPATH += $$PWD/override/wdk/vc10
#INCLUDEPATH += $$WDK_HOME/include/api
#INCLUDEPATH += $$WDK_HOME/include/crt
#INCLUDEPATH += $$WDK_HOME/include/ddk
#ITH_HOME = c:/dev/ith
#INCLUDEPATH += $$ITH_HOME/include
#LIBS += -L$$ITH_HOME/lib
#LIBS += -lITH_DLL #-lITH_SYS
#INCLUDEPATH += $$ITH_HOME/include
#LIBS += -L$$ITH_HOME/lib -lihf
# Tell IHF not to override new operators, see: ith/mem.h
DEFINES += DEFAULT_MM
#LIBS += -lmsvcrtd
#LIBS += -lmsvcrt
#QMAKE_LFLAGS += /NODEFAULTLIB:msvcrt.lib /NODEFAULTLIB:msvcrtd.lib
DEFINES += _CRT_SECURE_NO_WARNINGS _CRT_NON_CONFORMING_SWPRINTFS
## Sources
#TEMPLATE = lib
#TARGET = texthook
#CONFIG += dll
#CONFIG += staticlib
#DEFINES += TEXTHOOK_BUILD_LIB
DEFINES += TEXTHOOK_STATIC_LIB
HEADERS += \
$$PWD/ihf_p.h \
$$PWD/ith_p.h \
$$PWD/texthook_config.h \
$$PWD/texthook.h \
$$PWD/texthook_p.h \
$$PWD/textthread_p.h \
$$PWD/winapi_p.h
SOURCES += \
$$PWD/ihf_p.cc \
$$PWD/ith_p.cc \
$$PWD/texthook.cc \
$$PWD/textthread_p.cc \
$$PWD/winapi_p.cc
#!wince*: LIBS += -lshell32
#RC_FILE += texthook.rc
OTHER_FILES += \
$$PWD/texthook.pri \
$$PWD/texthook.pro \
$$PWD/texthook.rc
# EOF

View File

@ -0,0 +1,376 @@
// textthread_p.cc
// 6/6/2012 jichi
#include "texthook/textthread_p.h"
#include "texthook/texthook_p.h"
#include "winmutex/winmutex.h"
#include "wintimer/wintimer.h"
#include "host/textthread.h"
#include <QtCore/QRegExp>
//#define DEBUG "textthread_p.cc"
#include "sakurakit/skdebug.h"
enum { ITH_THREAD_NAME_CAPACITY = 0x200 }; // used internally by ITH
#define REPEAT_RX_1 "(.{2,})\\1+$" // The pattern has at least 2 bytes, and repeats at least once
/** Private class */
#define D_LOCK win_mutex_lock<D::mutex_type> d_lock(D::globalMutex) // Synchronized scope for accessing private data
class TextThreadDelegatePrivate
{
SK_CLASS(TextThreadDelegatePrivate)
SK_DISABLE_COPY(TextThreadDelegatePrivate)
public:
typedef win_mutex<CRITICAL_SECTION> mutex_type;
static mutex_type globalMutex; // Used only in public class. Because ITH is running in another single thread
static int globalCapacity; // maximum text size
static bool globalRemovesRepeat;
static bool globalKeepsSpace;
static bool globalWideCharacter;
TextThread *t;
WinTimer flushTimer; // as QTimer does not work with windows remote thread, use native WM_TIMER instead
ulong signature; // buffered
char sourceBuffer[ITH_THREAD_NAME_CAPACITY]; // buffered
QString source; // buffered
int bufferSize;
int bufferCapacity;
char *buffer;
bool removesRepeat;
QByteArray spaceBuffer;
int spaceCount;
struct Repeat
{
QRegExp rx; // cached
char *buffer; // repeated string
int size;
int pos; // >= 0, current pos of repeating string
int offset; // offset of repeated string
Repeat() : rx(REPEAT_RX_1), buffer(nullptr), size(0), pos(0), offset(-1) {}
~Repeat() { if (buffer) delete[] buffer; }
void clear()
{
size = pos = 0;
offset = -1;
}
bool isRepeating(const char *data, int len) const
{
if (!size || !buffer)
return false;
switch (len) {
case 1: return pos < size && buffer[pos] == *data;
case 2: return pos < size + 1 && buffer[pos] == data[0] && buffer[pos +1] == data[1];
default:
if (pos + len >= size)
return false;
for (int i = 0; i < len; i++)
if (buffer[pos + i] != data[i])
return false;
return true;
}
}
} repeat;
// - Construction -
public:
explicit TextThreadDelegatePrivate(TextThread *thread) : t(thread),
bufferSize(0), bufferCapacity(globalCapacity), buffer(new char[globalCapacity]),
spaceCount(0),
removesRepeat(false)
{
signature = signatureOf(t);
//size_t size =
t->GetThreadString(sourceBuffer, ITH_THREAD_NAME_CAPACITY);
source = sourceBuffer;
}
~TextThreadDelegatePrivate() { delete[] buffer; }
// - Properties -
public:
//QString text() const { return QString::fromLocal8Bit(buffer); }
//ulong context() const { return t->GetThreadParameter()->retn; }
//ulong subcontext() const { return t->GetThreadParameter()->spl; }
//ulong processId() const { return t->PID(); }
// - Actions -
public:
void flush()
{
if (flushTimer.isActive())
flushTimer.stop();
if (bufferSize) {
send();
bufferSize = 0;
}
if (!spaceBuffer.isEmpty())
spaceBuffer.clear();
spaceCount = 0;
}
void syncGlobal()
{
if (bufferCapacity < globalCapacity) {
delete[] buffer;
bufferCapacity = globalCapacity;
buffer = new char[bufferCapacity];
if (repeat.buffer) {
delete[] repeat.buffer;
if (!removesRepeat)
repeat.buffer = nullptr;
else {
char *largerBuffer = new char[bufferCapacity];
if (repeat.size)
qMemCopy(largerBuffer, repeat.buffer, repeat.size);
repeat.buffer = largerBuffer;
}
}
//bufferSize = repeatOffset = 0; // already reset in flush
//if (removesRepeat)
// repeat.reset();
}
if (removesRepeat != globalRemovesRepeat) {
removesRepeat = globalRemovesRepeat;
if (removesRepeat)
repeat.clear();
}
}
void appendSpace()
{
flushTimer.start();
spaceCount++;
if (spaceBuffer.isEmpty())
spaceBuffer.append(buffer, bufferSize);
spaceBuffer.append(' ');
if (globalWideCharacter)
spaceBuffer.append('\0'); // L' ' = {'\x20', '\0'};
}
void append(const char *data, int len)
{
flushTimer.start();
if (bufferSize < qMin(bufferCapacity, globalCapacity))
switch (len) {
case 1: buffer[bufferSize++] = *data; break;
case 2: buffer[bufferSize++] = *data;
if (bufferSize < bufferCapacity)
buffer[bufferSize++] = data[1];
break;
default:
{
int diff = qMin(len, bufferCapacity - bufferSize);
qMemCopy(buffer + bufferSize, data, diff);
bufferSize += diff;
}
}
if (!spaceBuffer.isEmpty())
spaceBuffer.append(data, len);
}
void appendRepeat(const char *data, int len)
{
if (bufferSize + len >= qMin(bufferCapacity, globalCapacity)) // overflow
return;
if (repeat.isRepeating(data, len)) {
repeat.pos += len;
if (repeat.pos >= repeat.size)
repeat.pos = 0;
return;
}
repeat.clear();
append(data, len);
if (bufferSize >= 6) { // at least 2 characters
// Use fromLatin1 to prevent the data from being decoded
QString t = QString::fromLatin1(buffer, bufferSize);
repeat.offset = repeat.rx.indexIn(t);
if (repeat.offset >= 0) {
repeat.size = repeat.rx.cap(1).size();
if (!repeat.buffer)
repeat.buffer = new char[bufferCapacity];
qMemCopy(repeat.buffer, buffer + repeat.offset, repeat.size);
//bufferSize = repeat.offset repeat.size;
}
}
}
private:
void send()
{
int size;
if (removesRepeat && repeat.offset >= 0 && repeat.size)
size = repeat.offset + repeat.size;
else
size = bufferSize;
if (!spaceBuffer.isEmpty() && spaceBuffer.size() != size)
spaceBuffer.truncate(size + spaceCount);
TextHookPrivate::sendData(
QByteArray(buffer, size), spaceBuffer,
signature, source);
}
static qint32 signatureOf(TextThread *t)
{
qint32 ret =
(t->GetThreadParameter()->retn & 0xffff) | // context
(t->GetThreadParameter()->spl & 0xffff) << 16; // subcontext
return ret ? ret : t->Addr();
}
//static QString sourceOf(TextThread *t);
public:
static ulong contextOf(TextThread *t)
{ return t->GetThreadParameter()->retn; }
static ulong subcontextOf(TextThread *t)
{ return t->GetThreadParameter()->spl; }
};
TextThreadDelegatePrivate::mutex_type TextThreadDelegatePrivate::globalMutex;
int TextThreadDelegatePrivate::globalCapacity = 512;
bool TextThreadDelegatePrivate::globalRemovesRepeat = false;
bool TextThreadDelegatePrivate::globalKeepsSpace = false;
bool TextThreadDelegatePrivate::globalWideCharacter = false;
//QString TextThreadDelegatePrivate::sourceOf(TextThread *t)
//{
// Q_ASSERT(t);
// QString ret;
// enum { buf_size = 0x200 }; // 0x200 is used by ITH internally
// wchar_t buf[buf_size];
// ulong len = t->GetThreadString(buf, buf_size);
// if (len)
// ret = QString::fromWCharArray(buf, len);
// return ret;
//}
/** Public class */
// - Constructions -
TextThreadDelegate::TextThreadDelegate(TextThread *t)
: d_(new D(t))
{
d_->flushTimer.setMethod(this, &Self::flush);
d_->flushTimer.setSingleShot(true);
}
TextThreadDelegate::~TextThreadDelegate()
{
if (d_->flushTimer.isActive())
d_->flushTimer.stop();
delete d_;
}
bool TextThreadDelegate::delegateOf(const Self *that) const
{
Q_ASSERT(t);
// Both have no context, and my subcontext is smaller
return that
&& !D::contextOf(that->d_->t) && !D::contextOf(d_->t)
&& D::subcontextOf(that->d_->t) >= D::subcontextOf(d_->t)
&& ::strcmp(d_->sourceBuffer, that->d_->sourceBuffer) == 0
&& nameEquals("Malie");
}
// - Properties -
//TextThread *TextThreadDelegate::t() const { return d_->t; }
int TextThreadDelegate::threadNumber() const
{ return d_->t->Number(); }
qint32 TextThreadDelegate::signature() const
{ return d_->signature; }
QString TextThreadDelegate::name() const
{ return d_->source; }
bool TextThreadDelegate::nameEquals(const char *that) const
{ return !::strcmp(d_->sourceBuffer, that); }
int TextThreadDelegate::capacity() { return D::globalCapacity; }
void TextThreadDelegate::setCapacity(int value) { D::globalCapacity = value; }
bool TextThreadDelegate::removesRepeat() { return D::globalRemovesRepeat; }
void TextThreadDelegate::setRemovesRepeat(bool value) { D::globalRemovesRepeat = value; }
bool TextThreadDelegate::wideCharacter() { return D::globalWideCharacter; }
void TextThreadDelegate::setWideCharacter(bool value) { D::globalWideCharacter = value; }
bool TextThreadDelegate::keepsSpace() { return D::globalKeepsSpace; }
void TextThreadDelegate::setKeepsSpace(bool value) { D::globalKeepsSpace = value; }
void TextThreadDelegate::setInterval(int msecs)
{ d_->flushTimer.setInterval(msecs); }
void TextThreadDelegate::setParentWindow(WId winId)
{ d_->flushTimer.setParentWindow(winId); }
// - Actions -
void TextThreadDelegate::flush()
{
D_LOCK;
d_->flush();
d_->syncGlobal();
}
void TextThreadDelegate::touch()
{
D_LOCK;
d_->flushTimer.start();
}
void TextThreadDelegate::append(const char *data, int len, bool space)
{
D_LOCK;
if (space && D::globalKeepsSpace)
d_->appendSpace();
if (data && len) {
if (d_->removesRepeat)
d_->appendRepeat(data, len);
else
d_->append(data, len);
}
}
// EOF
/*
void TextThreadDelegate::append(const QByteArray &data)
{
D::mutex_lock_type locker(D::mutex);
d_->flushTimer.start();
if (d_->buffer.size() <= D::capacity)
d_->buffer.append(data);
}
void TextThreadDelegatePrivate::send()
{
#ifdef DEBUG
qDebug()<< source()
<< t->Number()
<< t->PID()
<< QString::number(t->Addr(), 16)
<< QString::number(t->GetThreadParameter()->retn, 16)
<< QString::number(t->GetThreadParameter()->spl, 16)
<< QTextCodec::codecForName("SHIFT-JIS")->makeDecoder()->toUnicode(buffer);
#endif // DEBUG
}
*/

View File

@ -0,0 +1,81 @@
#pragma once
// textthread_p.h
// 6/6/2012 jichi
// Internal header.
// Defines TextHook delegate class.
#include "sakurakit/skglobal.h"
#include <QtGui/qwindowdefs.h>
//QT_FORWARD_DECLARE_CLASS(QByteArray)
class SharedRef
{
SK_CLASS(SharedRef)
int count_;
public:
SharedRef(): count_(1) {}
int retainCount() const { return count_; }
void retain() { count_++; }
//void release() { count_--; }
static void release(Self *x) { if (--x->count_ <= 0) delete x; }
};
// FIXME: This class is not thread-safe!
class TextThread;
class TextThreadDelegatePrivate;
class TextThreadDelegate : public SharedRef
{
SK_EXTEND_CLASS(TextThreadDelegate, SharedRef)
SK_DISABLE_COPY(TextThreadDelegate)
SK_DECLARE_PRIVATE(TextThreadDelegatePrivate)
public:
explicit TextThreadDelegate(TextThread *t);
~TextThreadDelegate();
bool delegateOf(const Self *t) const;
// - Properties -
//TextThread *t() const;
int threadNumber() const;
qint32 signature() const;
QString name() const;
bool nameEquals(const char *that) const; // optimized
// Maximum text size
static int capacity();
static void setCapacity(int value);
static bool wideCharacter();
static void setWideCharacter(bool value);
static bool removesRepeat();
static void setRemovesRepeat(bool value);
static bool keepsSpace();
static void setKeepsSpace(bool value);
//TextThread *t() const;
//int interval() const;
void setInterval(int msecs);
//WId parentWindow() const;
void setParentWindow(WId winId);
// - Actions -
//void append(const QByteArray &data);
/** Add data to the text thread
* @param data raw data
* @param len length of the data
* @param space Whether have LEADING space
*/
void append(const char *data, int len, bool space=false);
void flush();
void touch(); // keep timer running
};
// EOF

21
vnr/texthook/winapi_p.cc Normal file
View File

@ -0,0 +1,21 @@
// apiwin_p.cc
// 10/6/2012 jichi
#include "texthook/winapi_p.h"
#include <windows.h>
WINAPI_BEGIN_NAMESPACE
bool IsProcessActiveWithId(DWORD dwProcessId)
{
bool ret = false;
if (HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, dwProcessId)) {
DWORD dwExitCode;
ret = ::GetExitCodeProcess(hProc, &dwExitCode) && (dwExitCode == STILL_ACTIVE);
::CloseHandle(hProc);
}
return ret;
}
WINAPI_END_NAMESPACE
// EOF

18
vnr/texthook/winapi_p.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
// winapi_p.h
// 10/5/2012 jichi
// Internal header.
// Wrapper of <windows.h>
#ifndef WINAPI_BEGIN_NAMESPACE
# define WINAPI_BEGIN_NAMESPACE namespace winapi {
#endif
#ifndef WINAPI_END_NAMESPACE
# define WINAPI_END_NAMESPACE } // namespace winapi
#endif
WINAPI_BEGIN_NAMESPACE
bool IsProcessActiveWithId(unsigned long dwProcessId);
WINAPI_END_NAMESPACE
// EOF

112
vnr/vnrhook/CMakeLists.txt Normal file
View File

@ -0,0 +1,112 @@
# # hook.pro
# # Exception handler to catch all exceptions
# CONFIG += dll noqt eh eha # noeh nosafeseh
# DEFINES += ITH_HAS_CRT ITH_HAS_SEH
# DEFINES += MEMDBG_NO_STL NTINSPECT_NO_STL # disabled as not used
# # jichi 11/13/2011: disable swprinf warning
# DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
# config.pri
# CONFIG(eha) {
# message(CONFIG eha)
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
# QMAKE_CXXFLAGS_STL_ON += /EHa
# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
# }
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set(vnrhook_src
include/const.h
include/defs.h
include/types.h
src/except.h
src/main.cc
src/main.h
src/pipe.cc
src/engine/engine.cc
src/engine/engine.h
src/engine/hookdefs.h
src/engine/match.cc
src/engine/match.h
src/engine/pchooks.cc
src/engine/pchooks.h
src/engine/mono/funcinfo.h
src/engine/mono/types.h
src/engine/ppsspp/funcinfo.h
src/hijack/texthook.cc
src/hijack/texthook.h
src/tree/avl.h
src/util/growl.h
src/util/util.cc
src/util/util.h
${PROJECT_SOURCE_DIR}/ccutil/ccmacro.h
${PROJECT_SOURCE_DIR}/cpputil/cpplocale.h
${PROJECT_SOURCE_DIR}/cpputil/cppmarshal.h
${PROJECT_SOURCE_DIR}/cpputil/cppmath.h
${PROJECT_SOURCE_DIR}/cpputil/cpppath.h
${PROJECT_SOURCE_DIR}/cpputil/cppstring.h
${PROJECT_SOURCE_DIR}/cpputil/cpptype.h
${PROJECT_SOURCE_DIR}/cpputil/cppunicode.h
${PROJECT_SOURCE_DIR}/disasm/disasm.cc
${PROJECT_SOURCE_DIR}/hashutil/hashstr.h
${PROJECT_SOURCE_DIR}/hashutil/hashutil.h
${PROJECT_SOURCE_DIR}/memdbg/memdbg.h
${PROJECT_SOURCE_DIR}/memdbg/memsearch.cc
${PROJECT_SOURCE_DIR}/memdbg/memsearch.h
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.cc
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.h
${PROJECT_SOURCE_DIR}/winkey/winkey.h
${PROJECT_SOURCE_DIR}/winversion/winversion.cc
${PROJECT_SOURCE_DIR}/winversion/winversion.h
${PROJECT_SOURCE_DIR}/winseh/winseh.h
${PROJECT_SOURCE_DIR}/winseh/winseh.cc
${PROJECT_SOURCE_DIR}/winseh/winseh_safe.cc
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
${PROJECT_SOURCE_DIR}/mono/monoobject.h
${PROJECT_SOURCE_DIR}/mono/monotype.h
)
add_library(vnrhook SHARED ${vnrhook_src})
enable_language(ASM_MASM)
set_source_files_properties(
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
PROPERTIES
# CMAKE_ASM_MASM_FLAGS /safeseh # CMake bug 14711: http://www.cmake.org/Bug/view.php?id=14711
COMPILE_FLAGS /safeseh
)
set_target_properties(vnrhook PROPERTIES
LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO"
)
target_compile_options(vnrhook PRIVATE
/EHa
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
set(vnrhook_libs
ithsys
${WDK_HOME}/lib/wxp/i386/ntdll.lib
Version.lib
)
target_link_libraries(vnrhook ${vnrhook_libs})
target_compile_definitions(vnrhook
PRIVATE
ITH_HAS_CRT
ITH_HAS_SEH
_CRT_NON_CONFORMING_SWPRINTFS
)
install(TARGETS vnrhook RUNTIME
DESTINATION .
CONFIGURATIONS Release
)

View File

@ -0,0 +1,35 @@
# dllconfig.pri
# 8/9/2013 jichi
# For linking ITH injectable dlls.
# The dll is self-contained and Windows-independent.
CONFIG += dll noqt #noeh nosafeseh
CONFIG -= embed_manifest_dll # dynamically load dlls
win32 {
CONFIG(eh): DEFINES += ITH_HAS_SEH # Do have exception handler in msvcrt.dll on Windows Vista and later
CONFIG(noeh): DEFINES -= ITH_HAS_SEH # Do not have exception handler in msvcrt.dll on Windows XP and before
}
include(../../../config.pri)
#win32 {
# CONFIG(noeh): include($$LIBDIR/winseh/winseh_safe.pri)
#}
# jichi 11/24/2013: Disable manual heap
DEFINES -= ITH_HAS_HEAP
# jichi 11/13/2011: disable swprinf warning
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
## Libraries
#LIBS += -lkernel32 -luser32 -lgdi32
LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
#LIBS += -L$$WDK7_HOME/lib/crt/i386 -lmsvcrt
#QMAKE_LFLAGS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # This will leave runtime flags in the dll
#LIBS += -L$$WDK8_HOME/lib/winv6.3/um/x86 -lntdll
HEADERS += $$PWD/dllconfig.h
# EOF

View File

@ -0,0 +1,61 @@
# hookxp.pro
# 8/9/2013 jichi
# Build vnrhookxp.dll for Windows XP
CONFIG += noeh # msvcrt on Windows XP does not has exception handler
include(../dllconfig.pri)
include(../sys/sys.pri)
include($$LIBDIR/disasm/disasm.pri)
include($$LIBDIR/memdbg/memdbg.pri)
include($$LIBDIR/ntinspect/ntinspect.pri)
include($$LIBDIR/winkey/winkey.pri)
include($$LIBDIR/winseh/winseh_safe.pri)
include($$LIBDIR/winversion/winversion.pri)
VPATH += ../hook
INCLUDEPATH += ../hook
# 9/27/2013: disable ITH this game engine, only for debugging purpose
#DEFINES += ITH_DISABLE_ENGINE
# jichi 9/22/2013: When ITH is on wine, mutex is needed to protect NtWriteFile
#DEFINES += ITH_WINE
#DEFINES += ITH_SYNC_PIPE
DEFINES += MEMDBG_NO_STL NTINSPECT_NO_STL
## Libraries
LIBS += -lkernel32 -luser32 -lgdi32 #-lgdiplus
## Sources
TEMPLATE = lib
TARGET = vnrhookxp
#CONFIG += staticlib
HEADERS += \
config.h \
cli.h \
hook.h \
engine/engine.h \
engine/hookdefs.h \
engine/match.h \
engine/pchooks.h \
engine/util.h \
tree/avl.h
SOURCES += \
main.cc \
rpc/pipe.cc \
hijack/texthook.cc \
engine/engine.cc \
engine/match.cc \
engine/pchooks.cc \
engine/util.cc
#RC_FILE += vnrhook.rc
#OTHER_FILES += vnrhook.rc
# EOF

View File

@ -0,0 +1,46 @@
#pragma once
// ith/common/memory.h
// 8/23/2013 jichi
// Branch: ITH/mem.h, revision 66
#ifndef ITH_HAS_HEAP
# define ITH_MEMSET_HEAP(...) ::memset(__VA_ARGS__)
#else
# define ITH_MEMSET_HEAP(...) (void)0
// Defined in kernel32.lilb
extern "C" {
// PVOID RtlAllocateHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ SIZE_T Size);
__declspec(dllimport) void * __stdcall RtlAllocateHeap(void *HeapHandle, unsigned long Flags, unsigned long Size);
// BOOLEAN RtlFreeHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ PVOID HeapBase);
__declspec(dllimport) int __stdcall RtlFreeHeap(void *HeapHandle, unsigned long Flags, void *HeapBase);
} // extern "C"
//NTSYSAPI
//BOOL
//NTAPI
//RtlFreeHeap(
// _In_ HANDLE hHeap,
// _In_ DWORD dwFlags,
// _In_ LPVOID lpMem
//);
extern void *hHeap; // defined in ith/sys.cc
inline void * __cdecl operator new(size_t lSize)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa366597%28v=vs.85%29.aspx
// HEAP_ZERO_MEMORY flag is critical. All new objects are assumed with zero initialized.
enum { HEAP_ZERO_MEMORY = 0x00000008 };
return RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize);
}
inline void __cdecl operator delete(void *pBlock)
{ RtlFreeHeap(::hHeap, 0, pBlock); }
inline void __cdecl operator delete[](void *pBlock)
{ RtlFreeHeap(::hHeap, 0, pBlock); }
#endif // ITH_HAS_HEAP

View File

@ -0,0 +1,36 @@
#pragma once
// ith/common/string.h
// 8/9/2013 jichi
// Branch: ITH/string.h, rev 66
#ifdef ITH_HAS_CRT // ITH is linked with msvcrt dlls
# include <cstdio>
# include <cstring>
#else
# define _INC_SWPRINTF_INL_
# define CRT_IMPORT __declspec(dllimport)
#include <windows.h> // for wchar_t
extern "C" {
CRT_IMPORT int swprintf(wchar_t *src, const wchar_t *fmt, ...);
CRT_IMPORT int sprintf(char *src, const char *fmt, ...);
CRT_IMPORT int swscanf(const wchar_t *src, const wchar_t *fmt, ...);
CRT_IMPORT int sscanf(const char *src, const char *fmt, ...);
CRT_IMPORT int wprintf(const wchar_t *fmt, ...);
CRT_IMPORT int printf(const char *fmt, ...);
CRT_IMPORT int _wputs(const wchar_t *src);
CRT_IMPORT int puts(const char *src);
CRT_IMPORT int _stricmp(const char *x, const char *y);
CRT_IMPORT int _wcsicmp(const wchar_t *x, const wchar_t *y);
//CRT_IMPORT size_t strlen(const char *);
//CRT_IMPORT size_t wcslen(const wchar_t *);
//CRT_IMPORT char *strcpy(char *,const char *);
//CRT_IMPORT wchar_t *wcscpy(wchar_t *,const wchar_t *);
CRT_IMPORT void *memmove(void *dst, const void *src, size_t sz);
CRT_IMPORT const char *strchr(const char *src, int val);
CRT_IMPORT int strncmp(const char *x, const char *y, size_t sz);
} // extern "C"
#endif // ITH_HAS_CRT

11
vnr/vnrhook/TRASH/xp.txt Normal file
View File

@ -0,0 +1,11 @@
12/16/2013
Differences between xp.dll and non-xp.dll for vnrhook.
non-xp:
CONFIG += eh
xp:
CONFIG += noeh
CONFIG -= embed_manifest_dll # Pure dynamic determined. The manifest would break Windows XP support
include($$LIBDIR/winseh/winseh_safe.pri)

249
vnr/vnrhook/include/const.h Normal file
View File

@ -0,0 +1,249 @@
#pragma once
// vnrhook/const.h
// 8/23/2013 jichi
// Branch: ITH/common.h, rev 128
// jichi 9/9/2013: Another importnat function is lstrcatA, which is already handled by
// Debonosu hooks. Wait until it is really needed by certain games.
// The order of the functions is used in several place.
// I need to recompile all of the dlls to modify the order.
// jichi 10/14/2014
#define HOOK_GDI_FUNCTION_LIST \
GetTextExtentPoint32A \
, GetTextExtentExPointA \
, GetTabbedTextExtentA \
, GetCharacterPlacementA \
, GetGlyphIndicesA \
, GetGlyphOutlineA \
, ExtTextOutA \
, TextOutA \
, TabbedTextOutA \
, GetCharABCWidthsA \
, GetCharABCWidthsFloatA \
, GetCharWidth32A \
, GetCharWidthFloatA \
, GetTextExtentPoint32W \
, GetTextExtentExPointW \
, GetTabbedTextExtentW \
, GetCharacterPlacementW \
, GetGlyphIndicesW \
, GetGlyphOutlineW \
, ExtTextOutW \
, TextOutW \
, TabbedTextOutW \
, GetCharABCWidthsW \
, GetCharABCWidthsFloatW \
, GetCharWidth32W \
, GetCharWidthFloatW \
, DrawTextA \
, DrawTextExA \
, DrawTextW \
, DrawTextExW
//, CharNextA
//, CharPrevA
//enum { HOOK_FUN_COUNT = 30 }; // total number of GDI hooks
// jichi 1/16/2015: Though called max hook, it means max number of text threads
enum { MAX_HOOK = 64 }; // must be larger than HOOK_FUN_COUNT
//enum { HOOK_SECTION_SIZE = 0x2000 }; // default ITH value
// jichi 1/16/2015: Change to a very large number to prevent crash
//enum { MAX_HOOK = 0x100 }; // must be larger than HookFunCount
enum { HOOK_SECTION_SIZE = MAX_HOOK * 0x100 }; // default ITH value is 0x2000 for 32 hook (0x100 per hook)
// jichi 375/2014: Add offset of pusha/pushad
// http://faydoc.tripod.com/cpu/pushad.htm
// http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
//
// Warning: The offset in ITH has -4 offset comparing to pusha and AGTH
enum pusha_off {
pusha_eax_off = -0x4
, pusha_ecx_off = -0x8
, pusha_edx_off = -0xc
, pusha_ebx_off = -0x10
, pusha_esp_off = -0x14
, pusha_ebp_off = -0x18
, pusha_esi_off = -0x1c
, pusha_edi_off = -0x20
, pusha_off = -0x24 // pushad offset
};
enum HostCommandType {
HOST_COMMAND = -1 // null type
, HOST_COMMAND_NEW_HOOK = 0
, HOST_COMMAND_REMOVE_HOOK = 1
, HOST_COMMAND_MODIFY_HOOK = 2
, HOST_COMMAND_HIJACK_PROCESS = 3
, HOST_COMMAND_DETACH = 4
};
enum HostNotificationType {
HOST_NOTIFICATION = -1 // null type
, HOST_NOTIFICATION_TEXT = 0
, HOST_NOTIFICATION_NEWHOOK = 1
};
// jichi 9/8/2013: The meaning are guessed
// Values must be within DWORD
// Unused values are as follows:
// - 0x100
enum HookParamType : unsigned long {
USING_STRING = 0x1 // type(data) is char* or wchar_t* and has length
, USING_UTF8 = USING_STRING // jichi 10/21/2014: temporarily handled the same way as USING_STRING
, USING_UNICODE = 0x2 // type(data) is wchar_t or wchar_t*
, BIG_ENDIAN = 0x4 // type(data) is char
, DATA_INDIRECT = 0x8
, USING_SPLIT = 0x10 // aware of split time?
, SPLIT_INDIRECT = 0x20
, MODULE_OFFSET = 0x40 // do hash module, and the address is relative to module
, FUNCTION_OFFSET = 0x80 // do hash function, and the address is relative to funccion
, PRINT_DWORD = 0x100 // jichi 12/7/2014: Removed
, NO_ASCII = 0x100 // jichi 1l/22/2015: Skip ascii characters
, STRING_LAST_CHAR = 0x200
, NO_CONTEXT = 0x400
, HOOK_EMPTY = 0x800
, FIXING_SPLIT = 0x1000
//, HOOK_AUXILIARY = 0x2000 // jichi 12/13/2013: None of known hooks are auxiliary
, RELATIVE_SPLIT = 0x2000 // jichi 10/24/2014: relative split return address
, HOOK_ENGINE = 0x4000
, HOOK_ADDITIONAL = 0x8000
};
// 6/1/2014: Fixed split value for hok parameter
// Fuse all threads, and prevent floating
enum { FIXED_SPLIT_VALUE = 0x10001 };
// jichi 12/18/2013:
// These dlls are used to guess the range for non-NO_CONTEXT hooks.
//
// Disabling uxtheme.dll would crash certain system: http://tieba.baidu.com/p/2764436254
#define IHF_FILTER_DLL_LIST \
/* ITH original filters */ \
L"gdiplus.dll" /* Graphics functions like TextOutA */ \
, L"lpk.dll" /* Language package scripts and fonts */ \
, L"msctf.dll" /* Text service */ \
, L"psapi.dll" /* Processes */ \
, L"usp10.dll" /* UNICODE rendering */ \
, L"user32.dll" /* Non-graphics functions like lstrlenA */ \
, L"uxtheme.dll" /* Theme */ \
\
/* Windows DLLs */ \
, L"advapi32.dll" /* Advanced services */ \
, L"apphelp.dll" /* Appliation help */ \
, L"audioses.dll" /* Audios */ \
, L"avrt.dll" /* Audio video runtime */ \
, L"cfgmgr32.dll" /* Configuration manager */ \
, L"clbcatq.dll" /* COM query service */ \
, L"comctl32.dll" /* Common control library */ \
, L"comdlg32.dll" /* Common dialogs */ \
, L"crypt32.dll" /* Security cryption */ \
, L"cryptbase.dll"/* Security cryption */ \
, L"cryptsp.dll" /* Security cryption */ \
, L"d3d8thk.dll" /* Direct3D 8 */ \
, L"d3d9.dll" /* Direct3D 9 */ \
, L"dbghelp.dll" /* Debug help */ \
, L"dciman32.dll" /* Display cotrol */ \
, L"devobj.dll" /* Device object */ \
, L"ddraw.dll" /* Direct draw */ \
, L"dinput.dll" /* Diret input */ \
, L"dsound.dll" /* Direct sound */ \
, L"DShowRdpFilter.dll" /* Direct show */ \
, L"dwmapi.dll" /* Windows manager */ \
, L"gdi32.dll" /* GDI32 */ \
, L"hid.dll" /* HID user library */ \
, L"iertutil.dll" /* IE runtime */ \
, L"imagehlp.dll" /* Image help */ \
, L"imm32.dll" /* Input method */ \
, L"ksuser.dll" /* Kernel service */ \
, L"ole32.dll" /* COM OLE */ \
, L"oleacc.dll" /* OLE access */ \
, L"oleaut32.dll" /* COM OLE */ \
, L"kernel.dll" /* Kernel functions */ \
, L"kernelbase.dll" /* Kernel functions */ \
, L"midimap.dll" /* MIDI */ \
, L"mmdevapi.dll" /* Audio device */ \
, L"mpr.dll" /* Winnet */ \
, L"msacm32.dll" /* MS ACM */ \
, L"msacm32.drv" /* MS ACM */ \
, L"msasn1.dll" /* Encoding/decoding */ \
, L"msimg32.dll" /* Image */ \
, L"msvfw32.dll" /* Media play */ \
, L"netapi32.dll" /* Network service */ \
, L"normaliz.dll" /* Normalize */ \
, L"nsi.dll" /* NSI */ \
, L"ntdll.dll" /* NT functions */ \
, L"ntmarta.dll" /* NT MARTA */ \
, L"nvd3dum.dll" /* Direct 3D */ \
, L"powerprof.dll"/* Power profile */ \
, L"profapi.dll" /* Profile API */ \
, L"propsys.dll" /* System properties */ \
, L"quartz.dll" /* OpenGL */ \
, L"rpcrt4.dll" /* RPC runtime */ \
, L"rpcrtremote.dll" /* RPC runtime */ \
, L"rsabase.dll" /* RSA cryption */ \
, L"rsaenh.dll" /* RSA cryption */ \
, L"schannel.dll" /* Security channel */ \
, L"sechost.dll" /* Service host */ \
, L"setupapi.dll" /* Setup service */ \
, L"shell32.dll" /* Windows shell */ \
, L"shlwapi.dll" /* Light-weighted shell */ \
, L"slc.dll" /* SLC */ \
, L"srvcli.dll" /* Service client */ \
, L"version.dll" /* Windows version */ \
, L"wdmaud.drv" /* Wave output */ \
, L"wldap32.dll" /* Wireless */ \
, L"wininet.dll" /* Internet access */ \
, L"winmm.dll" /* Windows sound */ \
, L"winsta.dll" /* Connection system */ \
, L"wtsapi32.dll" /* Windows terminal server */ \
, L"wintrust.dll" /* Windows trust */ \
, L"wsock32.dll" /* Windows sock */ \
, L"ws2_32.dll" /* Terminal server */ \
, L"wkscli.dll" /* ACIS */ \
\
/* MSVCRT */ \
, L"msvcrt.dll" /* VC rutime */ \
, L"msvcr80.dll" /* VC rutime 8 */ \
, L"msvcp80.dll" /* VC rutime 8 */ \
, L"msvcr90.dll" /* VC rutime 9 */ \
, L"msvcp90.dll" /* VC rutime 9 */ \
, L"msvcr100.dll" /* VC rutime 10 */ \
, L"msvcp100.dll" /* VC rutime 10 */ \
, L"msvcr110.dll" /* VC rutime 11 */ \
, L"msvcp110.dll" /* VC rutime 11 */ \
\
/* VNR */ \
, L"vnrhook.dll" \
, L"vnrhookxp.dll" \
\
/* Sogou IME */ \
, L"sogoupy.ime" \
, L"PicFace.dll" \
, L"AddressSearch.dll" \
\
/* QQ IME */ \
, L"QQPINYIN.IME" \
\
/* AlphaROM */ \
, L"kDays.dll" \
\
/* 360Safe */ \
, L"safemon.dll" \
\
/* Locale changers */ \
, L"AlLayer.dll" /* AppLocale */ \
, L"LocaleEmulator.dll" /* Locale Emulator */ \
, L"LSH.dll" /* LocaleSwitch */ \
, L"ntleah.dll" /* NTLEA */
// Google Japanese IME
//, L"GoogleIMEJaTIP32.dll"
enum {
//IHF_FILTER_COUNT = 7
IHF_FILTER_COUNT = 7 + 72 + 9 + 4 + 3 + 1 + 1 + 1 + 4 // count of total dlls to filter
, IHF_FILTER_CAPACITY = IHF_FILTER_COUNT + 1 // one more than the dll count
};
// EOF

View File

@ -0,0 +1,52 @@
#pragma once
// vnrhook/defs.h
// 8/23/2013 jichi
// DLL files
//#define ITH_SERVER_DLL L"vnrsrv.dll"
//#define ITH_CLIENT_DLL L"vnrcli.dll"
//#define ITH_CLIENT_XP_DLL L"vnrclixp.dll"
////#define ITH_CLIENT_UX_DLL L"vnrcliux.dll"
//#define ITH_ENGINE_DLL L"vnreng.dll"
//#define ITH_ENGINE_XP_DLL L"vnrengxp.dll"
//#define ITH_ENGINE_UX_DLL L"vnrengux.dll"
#define ITH_DLL L"vnrhook.dll"
#define ITH_DLL_XP L"vnrhookxp.dll"
// Pipes
#define ITH_TEXT_PIPE L"\\??\\pipe\\VNR_TEXT"
#define ITH_COMMAND_PIPE L"\\??\\pipe\\VNR_COMMAND"
// Sections
#define ITH_SECTION_ L"VNR_SECTION_" // _%d
// Mutex
// jichi 7/12/2015:
// ITH IO name prefix, needed by Windows 10 for NT event and mutex APIs
// Otherwise, NT functions will return status = STATUS_OBJECT_PATH_SYNTAX_BAD
//#define ITH_PATH_ L"\\BaseNamedObjects\\"
#define ITH_PATH_ L""
#define ITH_PROCESS_MUTEX_ ITH_PATH_ L"VNR_PROCESS_" // ITH_%d
#define ITH_HOOKMAN_MUTEX_ ITH_PATH_ L"VNR_HOOKMAN_" // ITH_HOOKMAN_%d
#define ITH_DETACH_MUTEX_ ITH_PATH_ L"VNR_DETACH_" // ITH_DETACH_%d
#define ITH_GRANTPIPE_MUTEX ITH_PATH_ L"VNR_GRANT_PIPE" // ITH_GRANT_PIPE
#define ITH_CLIENT_MUTEX ITH_PATH_ L"VNR_CLIENT" // ITH_DLL_RUNNING
#define ITH_SERVER_MUTEX ITH_PATH_ L"VNR_SERVER" // ITH_RUNNING
#define ITH_SERVER_HOOK_MUTEX ITH_PATH_ L"VNR_SERVER_HOOK" // original
// Events
#define ITH_REMOVEHOOK_EVENT ITH_PATH_ L"VNR_REMOVE_HOOK" // ITH_REMOVE_HOOK
#define ITH_MODIFYHOOK_EVENT ITH_PATH_ L"VNR_MODIFY_HOOK" // ITH_MODIFY_HOOK
#define ITH_PIPEEXISTS_EVENT ITH_PATH_ L"VNR_PIPE_EXISTS" // ITH_PIPE_EXIST
// EOF

View File

@ -0,0 +1,91 @@
#pragma once
// vnrhook/types.h
// 8/23/2013 jichi
// Branch: ITH/common.h, rev 128
#include <windows.h> // needed for windef types
/** jichi 3/7/2014: Add guessed comment
*
* DWORD addr absolute or relative address
* DWORD split esp offset of the split character
*
* http://faydoc.tripod.com/cpu/pushad.htm
* http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
* The order is the same as pushd
* EAX, ECX, EDX, EBX, ESP (original value), EBP, ESI, and EDI (if the current operand-size attribute is 32) and AX, CX, DX, BX, SP
* Negative values of 'data_offset' and 'sub_offset' refer to registers:-4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI
*/
struct HookParam {
// jichi 8/24/2013: For special hooks.
typedef void (*text_fun_t)(DWORD esp, HookParam *hp, BYTE index, DWORD *data, DWORD *split, DWORD *len);
// jichi 10/24/2014: Add filter function. Return the if skip the text
typedef bool (*filter_fun_t)(LPVOID str, DWORD *len, HookParam *hp, BYTE index);
// jichi 10/24/2014: Add generic hook function, return false if stop execution.
typedef bool (*hook_fun_t)(DWORD esp, HookParam *hp);
DWORD address; // absolute or relative address
DWORD offset, // offset of the data in the memory
index, // ?
split, // esp offset of the split character = pusha offset - 4
split_index; // ?
DWORD module, // hash of the module
function;
text_fun_t text_fun;
filter_fun_t filter_fun;
hook_fun_t hook_fun;
DWORD type; // flags
WORD length_offset; // index of the string length
BYTE hook_len, // ?
recover_len; // ?
// 2/2/2015: jichi number of times - 1 to run the hook
BYTE extra_text_count;
BYTE _unused; // jichi 2/2/2015: add a BYTE type to make to total sizeof(HookParam) even.
// 7/20/2014: jichi additional parameters for PSP games
DWORD user_flags,
user_value;
};
// jichi 6/1/2014: Structure of the esp for extern functions
struct HookStack
{
// pushad
DWORD edi, // -0x24
esi, // -0x20
ebp, // -0x1c
esp, // -0x18
ebx, // -0x14
edx, // -0x10
ecx, // -0xc
eax; // -0x8
// pushfd
DWORD eflags; // -0x4
DWORD retaddr; // 0
DWORD args[1]; // 0x4
};
struct SendParam {
DWORD type;
HookParam hp;
};
struct Hook { // size: 0x80
HookParam hp;
LPSTR hook_name;
int name_length;
BYTE recover[0x68 - sizeof(HookParam)];
BYTE original[0x10];
DWORD Address() const { return hp.address; }
DWORD Type() const { return hp.type; }
WORD Length() const { return hp.hook_len; }
LPSTR Name() const { return hook_name; }
int NameLength() const { return name_length; }
};
// EOF

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,170 @@
#pragma once
// engine/engine.h
// 8/23/2013 jichi
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
#include <windows.h>
struct HookParam; // defined in ith types.h
namespace Engine {
// Global variables
extern wchar_t process_name_[MAX_PATH], // cached
process_path_[MAX_PATH]; // cached
extern DWORD module_base_,
module_limit_;
//extern LPVOID trigger_addr;
typedef bool (* trigger_fun_t)(LPVOID addr, DWORD frame, DWORD stack);
extern trigger_fun_t trigger_fun_;
bool InsertMonoHooks(); // Mono
// Wii engines
bool InsertGCHooks(); // Dolphin
bool InsertVanillawareGCHook();
// PS2 engines
bool InsertPCSX2Hooks(); // PCSX2
bool InsertMarvelousPS2Hook(); // http://marvelous.jp
bool InsertMarvelous2PS2Hook(); // http://marvelous.jp
bool InsertTypeMoonPS2Hook(); // http://typemoon.com
//bool InsertNamcoPS2Hook();
// PSP engines
void SpecialPSPHook(DWORD esp_base, HookParam *hp, DWORD *data, DWORD *split, DWORD *len); // General PSP extern hook
bool InsertPPSSPPHooks(); // PPSSPPWindows
bool InsertPPSSPPHLEHooks();
bool InsertOtomatePPSSPPHook(); // PSP otomate.jp, 0.9.9.0 only
bool Insert5pbPSPHook(); // PSP 5pb.jp
bool InsertAlchemistPSPHook(); // PSP Alchemist-net.co.jp, 0.9.8 only
bool InsertAlchemist2PSPHook(); // PSP Alchemist-net.co.jp
bool InsertBandaiNamePSPHook(); // PSP Bandai.co.jp
bool InsertBandaiPSPHook(); // PSP Bandai.co.jp
bool InsertBroccoliPSPHook(); // PSP Broccoli.co.jp
bool InsertFelistellaPSPHook(); // PSP felistella.co.jp
bool InsertCyberfrontPSPHook(); // PSP CYBERFRONT (closed)
bool InsertImageepochPSPHook(); // PSP Imageepoch.co.jp
bool InsertImageepoch2PSPHook();// PSP Imageepoch.co.jp
bool InsertKadokawaNamePSPHook(); // PSP Kadokawa.co.jp
bool InsertKonamiPSPHook(); // PSP Konami.jp
bool InsertTecmoPSPHook(); // PSP Koeitecmo.co.jp
//bool InsertTypeMoonPSPHook(); // PSP Typemoon.com
bool InsertOtomatePSPHook(); // PSP Otomate.jp, 0.9.8 only
//bool InsertOtomate2PSPHook(); // PSP otomate.jp >= 0.9.9.1
bool InsertIntensePSPHook(); // PSP Intense.jp
bool InsertKidPSPHook(); // PSP Kid-game.co.jp
bool InsertNippon1PSPHook(); // PSP Nippon1.jp
bool InsertNippon2PSPHook(); // PSP Nippon1.jp
bool InsertYetiPSPHook(); // PSP Yetigame.jp
bool InsertYeti2PSPHook(); // PSP Yetigame.jp
// Game-speicific engines
bool InsertShinyDaysGameHook(); // ShinyDays
bool InsertLovaGameHook(); // lova.jp
// PC engines
bool Insert2RMHook(); // 2RM - Adventure Engine
bool Insert5pbHook(); // 5pb.jp, PSP/PS3 games ported to PC
bool InsertAB2TryHook(); // Yane@AkabeiSoft2Try: YaneSDK.dll.
bool InsertAbelHook(); // Abel
bool InsertAdobeAirHook(); // Adobe AIR
bool InsertAdobeFlash10Hook(); // Adobe Flash Player 10
bool InsertAliceHook(); // System40@AliceSoft; do not work for latest alice games
//bool InsertAmuseCraftHook(); // AMUSE CRAFT: *.pac
bool InsertAnex86Hook(); // Anex86: anex86.exe
bool InsertAOSHook(); // AOS: *.aos
bool InsertApricoTHook(); // Apricot: arc.a*
bool InsertArtemisHook(); // Artemis Engine: *.pfs
bool InsertAtelierHook(); // Atelier Kaguya: message.dat
bool InsertBGIHook(); // BGI: BGI.*
bool InsertBootupHook(); // Bootup: Bootup.dat
bool InsertC4Hook(); // C4: C4.EXE or XEX.EXE
bool InsertCaramelBoxHook(); // Caramel: *.bin
bool InsertCandyHook(); // SystemC@CandySoft: *.fpk
bool InsertCatSystemHook(); // CatSystem2: *.int
bool InsertCMVSHook(); // CMVS: data/pack/*.cpz; do not support the latest cmvs32.exe and cmvs64.exe
bool InsertCotophaHook(); // Cotopha: *.noa
bool InsertDebonosuHook(); // Debonosu: bmp.bak and dsetup.dll
bool InsertEaglsHook(); // E.A.G.L.S: EAGLES.dll
bool InsertEMEHook(); // EmonEngine: emecfg.ecf
bool InsertEscudeHook(); // Escude
bool InsertEushullyHook(); // Eushully: AGERC.DLL
bool InsertExpHook(); // EXP: http://www.exp-inc.jp
bool InsertFocasLensHook(); // FocasLens: Dat/*.arc, http://www.fo-lens.net
bool InsertGXPHook(); // GXP: *.gxp
bool InsertHorkEyeHook(); // HorkEye: resource string
bool InsertKAGParserHook(); // plugin/KAGParser.dll
bool InsertKAGParserExHook(); // plugin/KAGParserEx.dll
bool InsertKiriKiriHook(); // KiriKiri: *.xp3, resource string
bool InsertKiriKiriZHook(); // KiriKiri: *.xp3, resource string
bool InsertLeafHook(); // Leaf: *.pak
bool InsertLiveHook(); // Live: live.dll
bool InsertLunaSoftHook(); // LunaSoft: Pac/*.pac
bool InsertMalieHook(); // Malie@light: malie.ini
bool InsertMajiroHook(); // Majiro: *.arc
bool InsertMarineHeartHook(); // Marine Heart: SAISYS.exe
bool InsertMBLHook(); // MBL: *.mbl
bool InsertMEDHook(); // MED: *.med
bool InsertMinkHook(); // Mink: *.at2
//bool InsertMonoHook(); // Mono (Unity3D): */Mono/mono.dll
bool InsertNeXASHook(); // NeXAS: Thumbnail.pac
bool InsertNextonHook(); // NEXTON: aInfo.db
bool InsertNexton1Hook();
bool InsertNitroplusHook(); // Nitroplus: *.npa
bool InsertPalHook(); // AMUSE CRAFT: *.pac
bool InsertPensilHook(); // Pensil: PSetup.exe
bool InsertQLIEHook(); // QLiE: GameData/*.pack
//bool InsertRai7Hook(); // Rai7puk: rai7.exe
bool InsertRejetHook(); // Rejet: Module/{gd.dat,pf.dat,sd.dat}
bool InsertRUGPHook(); // rUGP: rUGP.exe
bool InsertRetouchHook(); // Retouch: resident.dll
bool InsertRREHook(); // RunrunEngine: rrecfg.rcf
bool InsertShinaHook(); // ShinaRio: Rio.ini
bool InsertElfHook(); // elf: Silky.exe
bool InsertScenarioPlayerHook();// sol-fa-soft: *.iar && *.sec5
bool InsertSiglusHook(); // SiglusEngine: SiglusEngine.exe
bool InsertSideBHook(); // SideB: Copyright side-B
bool InsertSilkysHook(); // SilkysPlus
bool InsertSyuntadaHook(); // Syuntada: dSoh.dat
bool InsertSystem43Hook(); // System43@AliceSoft: AliceStart.ini
bool InsertSystemAoiHook(); // SystemAoi: *.vfs
bool InsertTamamoHook(); // Tamamo
bool InsertTanukiHook(); // Tanuki: *.tak
bool InsertTaskforce2Hook(); // Taskforce2.exe
bool InsertTencoHook(); // Tenco: Check.mdx
bool InsertTriangleHook(); // Triangle: Execle.exe
bool InsertUnicornHook(); // Gsen18: *.szs|Data/*.szs
bool InsertWillPlusHook(); // WillPlus: Rio.arc
bool InsertWolfHook(); // Wolf: Data.wolf
bool InsertYukaSystem2Hook(); // YukaSystem2: *.ykc
bool InsertYurisHook(); // YU-RIS: *.ypf
void InsertBrunsHook(); // Bruns: bruns.exe
void InsertIronGameSystemHook();// IroneGameSystem: igs_sample.exe
void InsertLucifenHook(); // Lucifen@Navel: *.lpk
void InsertRyokuchaHook(); // Ryokucha: _checksum.exe
void InsertRealliveHook(); // RealLive: RealLive*.exe
void InsertStuffScriptHook(); // Stuff: *.mpk
void InsertTinkerBellHook(); // TinkerBell: arc00.dat
void InsertWaffleHook(); // WAFFLE: cg.pak
// CIRCUS: avdata/
bool InsertCircusHook1();
bool InsertCircusHook2();
} // namespace Engine
// EOF

View File

@ -0,0 +1,12 @@
#pragma once
// engine/hookdefs.h
// 7/20/2014 jichi
// For HookParam user flags
enum HookParamFlag : unsigned long {
HPF_Null = 0 // never used
, HPF_IgnoreSameAddress = 1 // ignore the last same text address
};
// EOF

View File

@ -0,0 +1,936 @@
// match.cc
// 8/9/2013 jichi
// Branch: ITH_Engine/engine.cpp, revision 133
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
#endif // _MSC_VER
#include "src/engine/match.h"
#include "src/engine/engine.h"
#include "src/engine/pchooks.h"
#include "src/util/growl.h"
#include "src/util/util.h"
#include "src/main.h"
#include "src/except.h"
#include "ithsys/ithsys.h"
#include "ccutil/ccmacro.h"
//#define ConsoleOutput(...) (void)0 // jichi 8/18/2013: I don't need ConsoleOutput
enum { MAX_REL_ADDR = 0x200000 }; // jichi 8/18/2013: maximum relative address
// - Global variables -
namespace Engine {
WCHAR process_name_[MAX_PATH], // cached
process_path_[MAX_PATH]; // cached
DWORD module_base_,
module_limit_;
//LPVOID trigger_addr;
trigger_fun_t trigger_fun_;
} // namespace Engine
// - Methods -
namespace Engine { namespace { // unnamed
bool DetermineGameHooks() // 7/19/2015
{
#if 0 // jichi 7/19/2015: Disabled as it will crash the game
if (IthFindFile(L"UE3ShaderCompileWorker.exe") && IthFindFile(L"awesomium_process.exe")) {
InsertLovaGameHook();
return true;
}
#endif // 0
return false;
}
// jichi 7/17/2014: Disable GDI hooks for PPSSPP
bool DeterminePCEngine()
{
if (DetermineGameHooks()) {
ConsoleOutput("vnreng: found game-specific hook");
return true;
}
if (IthFindFile(L"PPSSPP*.exe")) { // jichi 7/12/2014 PPSSPPWindows.exe, PPSSPPEX.exe PPSSPPSP.exe
InsertPPSSPPHooks();
return true;
}
if (IthFindFile(L"pcsx2*.exe")) { // jichi 7/19/2014 PCSX2.exe or PCSX2WX.exe
InsertPCSX2Hooks();
return true;
}
if (IthFindFile(L"Dolphin.exe")) { // jichi 7/20/2014
InsertGCHooks();
return true;
}
// jichi 5/14/2015: Skip hijacking BALDRSKY ZEROs
if (IthCheckFile(L"bsz_Data\\Mono\\mono.dll") || IthCheckFile(L"bsz2_Data\\Mono\\mono.dll")) {
ConsoleOutput("vnreng: IGNORE BALDRSKY ZEROs");
return true;
}
if (::GetModuleHandleA("mono.dll")) {
InsertMonoHooks();
// 3/20/2015 jichi
// Always insert GDI hooks even for Mono games
// For example: ãæá¦?ãÆ? need GetGlyphOutlineA
PcHooks::hookGDIFunctions();
return true;
}
// PC games
PcHooks::hookGDIFunctions();
EnableGDIPlusHooks();
return false;
}
bool DetermineEngineByFile1()
{
if (IthFindFile(L"*.xp3") || Util::SearchResourceString(L"TVP(KIRIKIRI)")) {
if (Util::SearchResourceString(L"TVP(KIRIKIRI) Z ")) { // TVP(KIRIKIRI) Z CORE
// jichi 11/24/2014: Disabled that might crash VBH
//if (IthCheckFile(L"plugin\\KAGParser.dll"))
// InsertKAGParserHook();
//else if (IthCheckFile(L"plugin\\KAGParserEx.dll"))
// InsertKAGParserExHook();
if (InsertKiriKiriZHook())
return true;
}
InsertKiriKiriHook();
return true;
}
// 8/2/2014 jichi: Game name shown as 2RM - Adventure Engine, text also in GetGlyphOutlineA
if (Util::SearchResourceString(L"2RM") && Util::SearchResourceString(L"Adventure Engine")) {
Insert2RMHook();
return true;
}
// 8/2/2014 jichi: Copyright is side-B, a conf.dat will be generated after the game is launched
// It also contains lua5.1.dll and lua5.dll
if (Util::SearchResourceString(L"side-B")) {
InsertSideBHook();
return true;
}
if (IthFindFile(L"bgi.*") || IthFindFile(L"BHVC.exe") || IthFindFile(L"sysgrp.arc")) {
InsertBGIHook();
return true;
}
if (IthCheckFile(L"Bootup.dat") && InsertBootupHook()) // 5/22/2015 Bootup
// lstrlenW can also find text with repetition though
return true;
if (IthCheckFile(L"AGERC.DLL")) { // 6/1/2014 jichi: Eushully, AGE.EXE
InsertEushullyHook();
return true;
}
if (IthFindFile(L"data*.arc") && IthFindFile(L"stream*.arc")) {
InsertMajiroHook();
return true;
}
// jichi 5/31/2014
if (//IthCheckFile(L"Silkys.exe") || // It might or might not have Silkys.exe
// data, effect, layer, mes, music
IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"mes.arc")) {
InsertElfHook();
return true;
}
// jichi 6/9/2015: Skip Silkys Sakura
if ( // Almost the same as Silkys except mes.arc is replaced by Script.arc
IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"Script.arc")) {
InsertSilkysHook();
return true;
}
if (IthFindFile(L"data\\pack\\*.cpz")) {
InsertCMVSHook();
return true;
}
// jichi 10/12/2013: Restore wolf engine
// jichi 10/18/2013: Check for data/*.wolf
if (IthFindFile(L"data.wolf") || IthFindFile(L"data\\*.wolf")) {
InsertWolfHook();
return true;
}
if (IthCheckFile(L"AdvData\\DAT\\NAMES.DAT")) {
InsertCircusHook1();
return true;
}
if (IthCheckFile(L"AdvData\\GRP\\NAMES.DAT")) {
InsertCircusHook2();
return true;
}
if (IthFindFile(L"*.noa") || IthFindFile(L"data\\*.noa")) {
InsertCotophaHook();
return true;
}
if (IthFindFile(L"*.pfs")) { // jichi 10/1/2013
InsertArtemisHook();
return true;
}
if (IthFindFile(L"*.int")) {
InsertCatSystemHook();
return true;
}
if (IthCheckFile(L"message.dat")) {
InsertAtelierHook();
return true;
}
if (IthCheckFile(L"Check.mdx")) { // jichi 4/1/2014: AUGame
InsertTencoHook();
return true;
}
// jichi 12/25/2013: It may or may not be QLIE.
// AlterEgo also has GameData/sound.pack but is not QLIE
if (IthFindFile(L"GameData\\*.pack") && InsertQLIEHook())
return true;
if (IthCheckFile(L"dll\\Pal.dll")) {
InsertPalHook();
return true;
}
if (IthFindFile(L"*.pac")) {
// jichi 6/3/2014: AMUSE CRAFT and SOFTPAL
// Selectively insert, so that lstrlenA can still get correct text if failed
//if (IthCheckFile(L"dll\\resource.dll") && IthCheckFile(L"dll\\pal.dll") && InsertAmuseCraftHook())
// return true;
if (IthCheckFile(L"Thumbnail.pac")) {
//ConsoleOutput("vnreng: IGNORE NeXAS");
InsertNeXASHook(); // jichi 7/6/2014: GIGA
return true;
}
if (Util::SearchResourceString(L"SOFTPAL")) {
ConsoleOutput("vnreng: IGNORE SoftPal UNiSONSHIFT");
return true;
}
}
// jichi 12/27/2014: LunaSoft
if (IthFindFile(L"Pac\\*.pac")) {
InsertLunaSoftHook();
return true;
}
// jichi 9/16/2013: Add Gesen18
if (IthFindFile(L"*.szs") || IthFindFile(L"Data\\*.szs")) {
InsertUnicornHook();
return true;
}
// jichi 12/22/2013: Add rejet
if (IthCheckFile(L"gd.dat") && IthCheckFile(L"pf.dat") && IthCheckFile(L"sd.dat")) {
InsertRejetHook();
return true;
}
// Only examined with version 1.0
//if (IthFindFile(L"Adobe AIR\\Versions\\*\\Adobe AIR.dll")) { // jichi 4/15/2014: FIXME: Wildcard not working
if (IthCheckFile(L"Adobe AIR\\Versions\\1.0\\Adobe AIR.dll")) { // jichi 4/15/2014: Adobe AIR
InsertAdobeAirHook();
return true;
}
return false;
}
bool DetermineEngineByFile2()
{
if (IthCheckFile(L"resident.dll")) {
InsertRetouchHook();
return true;
}
if (IthCheckFile(L"Malie.ini") || IthCheckFile(L"Malie.exe")) { // jichi: 9/9/2014: Add malie.exe in case malie.ini is missing
InsertMalieHook();
return true;
}
if (IthCheckFile(L"live.dll")) {
InsertLiveHook();
return true;
}
// 9/5/2013 jichi
if (IthCheckFile(L"aInfo.db")) {
InsertNextonHook();
return true;
}
if (IthFindFile(L"*.lpk")) {
InsertLucifenHook();
return true;
}
if (IthCheckFile(L"cfg.pak")) {
InsertWaffleHook();
return true;
}
if (IthCheckFile(L"Arc00.dat")) {
InsertTinkerBellHook();
return true;
}
if (IthFindFile(L"*.vfs")) { // jichi 7/6/2014: Better to test AoiLib.dll? ja.wikipedia.org/wiki/«½«Õ«È«Ï«¦«¹«­«ã«é
InsertSystemAoiHook();
return true;
}
if (IthFindFile(L"*.mbl")) {
InsertMBLHook();
return true;
}
// jichi 8/1/2014: YU-RIS engine, lots of clockup game also has this pattern
if (IthFindFile(L"pac\\*.ypf") || IthFindFile(L"*.ypf")) {
// jichi 8/14/2013: CLOCLUP: "«Î?«Ö«ì«¹«ª«Ö«ê?«¸«å" would crash the game.
if (!IthCheckFile(L"noblesse.exe"))
InsertYurisHook();
return true;
}
if (IthFindFile(L"*.npa")) {
InsertNitroplusHook();
return true;
}
return false;
}
bool DetermineEngineByFile3()
{
//if (IthCheckFile(L"libscr.dll")) { // already checked
// InsertBrunsHook();
// return true;
//}
// jichi 10/12/2013: Sample args.txt:
// See: http://tieba.baidu.com/p/2631413816
// -workdir
// .
// -loadpath
// .
// am.cfg
if (IthCheckFile(L"args.txt")) {
InsertBrunsHook();
return true;
}
if (IthCheckFile(L"emecfg.ecf")) {
InsertEMEHook();
return true;
}
if (IthCheckFile(L"rrecfg.rcf")) {
InsertRREHook();
return true;
}
if (IthFindFile(L"*.fpk") || IthFindFile(L"data\\*.fpk")) {
InsertCandyHook();
return true;
}
if (IthFindFile(L"arc.a*")) {
InsertApricoTHook();
return true;
}
if (IthFindFile(L"*.mpk")) {
InsertStuffScriptHook();
return true;
}
if (IthCheckFile(L"Execle.exe")) {
InsertTriangleHook();
return true;
}
// jichi 2/28/2015: No longer work for "ÓÞïá¡¿??«¢«ê«¹ episode I" from Primula
//if (IthCheckFile(L"PSetup.exe")) {
// InsertPensilHook();
// return true;
//}
if (IthCheckFile(L"Yanesdk.dll")) {
InsertAB2TryHook();
return true;
}
if (IthFindFile(L"*.med")) {
InsertMEDHook();
return true;
}
return false;
}
bool DetermineEngineByFile4()
{
if (IthCheckFile(L"EAGLS.dll")) { // jichi 3/24/2014: E.A.G.L.S
//ConsoleOutput("vnreng: IGNORE EAGLS");
InsertEaglsHook();
return true;
}
if (IthCheckFile(L"bmp.pak") && IthCheckFile(L"dsetup.dll")) {
// 1/1/2016 jich: skip izumo4 from studio ego that is not supported by debonosu
if (IthFindFile(L"*izumo4*.exe")) {
PcHooks::hookLstrFunctions();
return true;
}
InsertDebonosuHook();
return true;
}
if (IthCheckFile(L"C4.EXE") || IthCheckFile(L"XEX.EXE")) {
InsertC4Hook();
return true;
}
if (IthCheckFile(L"Rio.arc") && IthFindFile(L"Chip*.arc")) {
InsertWillPlusHook();
return true;
}
if (IthFindFile(L"*.tac")) {
InsertTanukiHook();
return true;
}
if (IthFindFile(L"*.gxp")) {
InsertGXPHook();
return true;
}
if (IthFindFile(L"*.aos")) { // jichi 4/2/2014: AOS hook
InsertAOSHook();
return true;
}
if (IthFindFile(L"*.at2")) { // jichi 12/23/2014: Mink, sample files: voice.at2, voice.det, voice.nme
InsertMinkHook();
return true;
}
if (IthFindFile(L"*.ykc")) { // jichi 7/15/2014: YukaSystem1 is not supported, though
//ConsoleOutput("vnreng: IGNORE YKC:Feng/HookSoft(SMEE)");
InsertYukaSystem2Hook();
return true;
}
if (IthFindFile(L"model\\*.hed")) { // jichi 9/8/2014: EXP
InsertExpHook();
return true;
}
// jichi 2/6/2015 øÁäÌïÍ
// dPi.dat, dPih.dat, dSc.dat, dSch.dat, dSo.dat, dSoh.dat, dSy.dat
//if (IthCheckFile(L"dSoh.dat")) { // no idea why this file does not work
if (IthCheckFile(L"dSch.dat")) {
InsertSyuntadaHook();
return true;
}
// jichi 2/28/2015: Delay checking Pensil in case something went wrong
// File pattern observed in [Primula] ÓÞïá¡¿??«¢«ê«¹ episode I
// - PSetup.exe no longer exists
// - MovieTexture.dll information shows MovieTex dynamic library, copyright Pensil 2013
// - ta_trial.exe information shows 2XT - Primula Adventure Engine
if (IthCheckFile(L"PSetup.exe") || IthFindFile(L"PENCIL.*") || Util::SearchResourceString(L"2XT -")) {
InsertPensilHook();
return true;
}
return false;
}
bool DetermineEngineByProcessName()
{
WCHAR str[MAX_PATH];
wcscpy(str, process_name_);
_wcslwr(str); // lower case
if (wcsstr(str,L"reallive") || IthCheckFile(L"Reallive.exe") || IthCheckFile(L"REALLIVEDATA\\Start.ini")) {
InsertRealliveHook();
return true;
}
// jichi 8/19/2013: DO NOT WORK for games like¡¸«Ï«Ô«á«¢¡¹
//if (wcsstr(str,L"cmvs32") || wcsstr(str,L"cmvs64")) {
// InsertCMVSHook();
// return true;
//}
// jichi 8/17/2013: Handle "~"
if (wcsstr(str, L"siglusengine") || !wcsncmp(str, L"siglus~", 7) || IthCheckFile(L"SiglusEngine.exe")) {
InsertSiglusHook();
return true;
}
if (wcsstr(str, L"taskforce2") || !wcsncmp(str, L"taskfo~", 7) || IthCheckFile(L"Taskforce2.exe")) {
InsertTaskforce2Hook();
return true;
}
if (wcsstr(str,L"rugp") || IthCheckFile(L"rugp.exe")) {
InsertRUGPHook();
return true;
}
// jichi 8/17/2013: Handle "~"
if (wcsstr(str, L"igs_sample") || !wcsncmp(str, L"igs_sa~", 7) || IthCheckFile(L"igs_sample.exe")) {
InsertIronGameSystemHook();
return true;
}
if (wcsstr(str, L"bruns") || IthCheckFile(L"bruns.exe")) {
InsertBrunsHook();
return true;
}
if (wcsstr(str, L"anex86") || IthCheckFile(L"anex86.exe")) {
InsertAnex86Hook();
return true;
}
// jichi 8/17/2013: Handle "~"
if (wcsstr(str, L"shinydays") || !wcsncmp(str, L"shinyd~", 7) || IthCheckFile(L"ShinyDays.exe")) {
InsertShinyDaysGameHook();
return true;
}
// jichi 10/3/2013: FIXME: Does not work
// Raise C0000005 even with admin priv
//if (wcsstr(str, L"bsz")) { // BALDRSKY ZERO
// InsertBaldrHook();
// return true;
//}
if (wcsstr(process_name_, L"SAISYS") || IthCheckFile(L"SaiSys.exe")) { // jichi 4/19/2014: Marine Heart
InsertMarineHeartHook();
return true;
}
DWORD len = wcslen(str);
// jichi 8/24/2013: Checking for Rio.ini or $procname.ini
//wcscpy(str+len-4, L"_?.war");
//if (IthFindFile(str)) {
// InsertShinaHook();
// return true;
//}
if (InsertShinaHook())
return true;
// jichi 8/10/2013: Since *.bin is common, move CaramelBox to the end
str[len - 3] = L'b';
str[len - 2] = L'i';
str[len - 1] = L'n';
str[len] = 0;
if ((IthCheckFile(str) || IthCheckFile(L"trial.bin")) // jichi 7/8/2014: add trial.bin
&& InsertCaramelBoxHook())
return true;
// jichi 7/23/2015 It also has gameexe.bin existed
if (IthCheckFile(L"configure.exe") && IthCheckFile(L"configure.cfg") && IthCheckFile(L"gfx.bin")) {
InsertEscudeHook();
return true;
}
// This must appear at last since str is modified
wcscpy(str + len - 4, L"_checksum.exe");
if (IthCheckFile(str)) {
InsertRyokuchaHook();
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games
InsertScenarioPlayerHook();
return true;
}
return false;
}
bool DetermineEngineOther()
{
if (InsertAliceHook())
return true;
// jichi 1/19/2015: Disable inserting Lstr for System40
// See: http://sakuradite.com/topic/618
if (IthCheckFile(L"System40.ini")) {
ConsoleOutput("vnreng: IGNORE old System40.ini");
return true;
}
// jichi 12/26/2013: Add this after alicehook
if (IthCheckFile(L"AliceStart.ini")) {
InsertSystem43Hook();
return true;
}
// jichi 8/24/2013: Move into functions
static BYTE static_file_info[0x1000];
if (IthGetFileInfo(L"*01", static_file_info))
if (*(DWORD*)static_file_info == 0) {
static WCHAR static_search_name[MAX_PATH];
LPWSTR name=(LPWSTR)(static_file_info+0x5E);
int len = wcslen(name);
name[len - 2] = L'*';
name[len - 1] = 0;
wcscpy(static_search_name, name);
IthGetFileInfo(static_search_name, static_file_info);
union {
FILE_BOTH_DIR_INFORMATION *both_info;
DWORD addr;
};
both_info = (FILE_BOTH_DIR_INFORMATION *)static_file_info;
//BYTE* ptr=static_file_info;
len = 0;
while (both_info->NextEntryOffset) {
addr += both_info->NextEntryOffset;
len++;
}
if (len > 3) {
InsertAbelHook();
return true;
}
}
return false;
}
// jichi 8/17/2014
// Put the patterns that might break other games at last
bool DetermineEngineAtLast()
{
if (IthCheckFile(L"MovieTexture.dll") && (InsertPensilHook() || Insert2RMHook())) // MovieTexture.dll also exists in 2RM games such as Ù½í­äñ2??÷ú, which is checked first
return true;
if (IthFindFile(L"system") && IthFindFile(L"system.dat")) { // jichi 7/31/2015
InsertAbelHook();
return true;
}
if (IthFindFile(L"data\\*.cpk")) { // jichi 12/2/2014
Insert5pbHook();
return true;
}
// jichi 7/6/2014: named as ScenarioPlayer since resource string could be: scenario player program for xxx
// Do this at last as it is common
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) { // jichi 4/18/2014: Other game engine could also have *.iar such as Ryokucha
InsertScenarioPlayerHook();
return true;
}
//if (IthCheckFile(L"arc0.dat") && IthCheckFile(L"script.dat") // jichi 11/14/2014: too common
if (Util::SearchResourceString(L"HorkEye")) { // appear in copyright: Copyright (C) HorkEye, http://horkeye.com
InsertHorkEyeHook();
return true;
}
if (IthCheckFile(L"comnArc.arc") // jichi 8/17/2014: this file might exist in multiple files
&& InsertNexton1Hook()) // old nexton game
return true;
if (IthCheckFile(L"arc.dat") // jichi 9/27/2014: too common
&& InsertApricoTHook())
return true;
if (IthFindFile(L"*.pak") // jichi 12/25/2014: too common
&& InsertLeafHook())
return true;
// jichi 10/31/2014
// File description: Adobe Flash Player 10.2r153
// Product name: Shockwave Flash
// Original filename: SAFlashPlayer.exe
// Legal trademarks: Adobe Flash Player
// No idea why, this must appear at last or it will crash
if (Util::SearchResourceString(L"Adobe Flash Player 10")) {
InsertAdobeFlash10Hook(); // only v10 might be supported. Otherwise, fallback to Lstr hooks
return true;
}
if (IthFindFile(L"dat\\*.arc")) { // jichi 2/6/2015
InsertFocasLensHook(); // Touhou
return true;
}
// jichi 8/23/2015: Tamamo
if (IthCheckFile(L"data.pck") && IthCheckFile(L"image.pck") && IthCheckFile(L"script.pck")) {
//if (IthCheckFile(L"QtGui.dll"))
InsertTamamoHook();
return true;
}
return false;
}
// jichi 6/1/2014
bool DetermineEngineGeneric()
{
bool ret = false;
if (IthCheckFile(L"AlterEgo.exe")) {
ConsoleOutput("vnreng: AlterEgo, INSERT WideChar hooks");
ret = true;
} else if (IthFindFile(L"data\\Sky\\*")) {
ConsoleOutput("vnreng: TEATIME, INSERT WideChar hooks");
ret = true;
}
//} else if (IthFindFile(L"image\\*.po2") || IthFindFile(L"image\\*.jo2")) {
// ConsoleOutput("vnreng: HarukaKanata, INSERT WideChar hooks"); // ªÏªëª«ª«ªÊª¿
// ret = true;
//}
if (ret)
PcHooks::hookWcharFunctions();
return ret;
}
bool DetermineNoEngine()
{
//if (IthFindFile(L"*\\Managed\\UnityEngine.dll")) { // jichi 12/3/2013: Unity (BALDRSKY ZERO)
// ConsoleOutput("vnreng: IGNORE Unity");
// return true;
//}
//if (IthCheckFile(L"bsz_Data\\Managed\\UnityEngine.dll") || IthCheckFile(L"bsz2_Data\\Managed\\UnityEngine.dll")) {
// ConsoleOutput("vnreng: IGNORE Unity");
// return true;
//}
// jichi 6/7/2015: RPGMaker v3
if (IthFindFile(L"*.rgss3a")) {
ConsoleOutput("vnreng: IGNORE RPGMaker RGSS3");
return true;
}
// jichi 11/22/2015: ÔÐÌÈNECRO ??÷ú
if (IthFindFile(L"*.npk")) {
ConsoleOutput("vnreng: IGNORE new Nitroplus");
return true;
}
// 8/29/2015 jichi: minori, text in GetGlyphOutlineA
if (IthFindFile(L"*.paz")) {
ConsoleOutput("vnreng: IGNORE minori");
return true;
}
// 7/28/2015 jichi: Favorite games
if (IthFindFile(L"*.hcb")) {
ConsoleOutput("vnreng: IGNORE FVP");
return true;
}
// jichi 2/14/2015: Guilty+ £Ò£É£Î¡¿£Ó£Å£Î (PK)
if (IthCheckFile(L"rio.ini") || IthFindFile(L"*.war")) {
ConsoleOutput("vnreng: IGNORE unknown ShinaRio");
return true;
}
if (IthCheckFile(L"AdvHD.exe") || IthCheckFile(L"AdvHD.dll")) {
ConsoleOutput("vnreng: IGNORE Adv Player HD"); // supposed to be WillPlus
return true;
}
if (IthCheckFile(L"ScrPlayer.exe")) {
ConsoleOutput("vnreng: IGNORE ScrPlayer");
return true;
}
if (IthCheckFile(L"nnnConfig2.exe")) {
ConsoleOutput("vnreng: IGNORE Nya NNNConfig");
return true;
}
// jichi 4/30/2015: Skip games made from ªéª¹ª³ª¦, such as ªÈª¢ªëìÑô£ªÎ«Í«È«é«ìÞÀï×
// It has garbage from lstrlenW. Correct text is supposed to be in TabbedTextOutA.
if (IthCheckFile(L"data_cg.dpm")) {
ConsoleOutput("vnreng: IGNORE DPM data_cg.dpm");
return true;
}
//if (IthCheckFile(L"AGERC.DLL")) { // jichi 3/17/2014: Eushully, AGE.EXE
// ConsoleOutput("vnreng: IGNORE Eushully");
// return true;
//}
if (IthCheckFile(L"game_sys.exe")) {
ConsoleOutput("vnreng: IGNORE Atelier Kaguya BY/TH");
return true;
}
if (IthFindFile(L"*.bsa")) {
ConsoleOutput("vnreng: IGNORE Bishop");
return true;
}
// jichi 3/19/2014: Escude game
// Example: bgm.bin gfx.bin maou.bin script.bin snd.bin voc.bin
if (IthCheckFile(L"gfx.bin") && IthCheckFile(L"snd.bin") && IthCheckFile(L"voc.bin")) {
ConsoleOutput("vnreng: IGNORE Escude");
return true;
}
// jichi 2/18/2015: Ignore if there is Nitro+ copyright
if (Util::SearchResourceString(L"Nitro+")) {
ConsoleOutput("vnreng: IGNORE unknown Nitro+");
return true;
}
// jichi 12/28/2014: "Chartreux Inc." in Copyright.
// Sublimary brands include Rosebleu, MORE, etc.
// GetGlyphOutlineA already works.
if (Util::SearchResourceString(L"Chartreux")) {
ConsoleOutput("vnreng: IGNORE Chartreux");
return true;
}
if (IthCheckFile(L"MovieTexture.dll")) {
ConsoleOutput("vnreng: IGNORE MovieTexture");
return true;
}
if (wcsstr(process_name_, L"lcsebody") || !wcsncmp(process_name_, L"lcsebo~", 7) || IthFindFile(L"lcsebody*")) { // jichi 3/19/2014: LC-ScriptEngine, GetGlyphOutlineA
ConsoleOutput("vnreng: IGNORE lcsebody");
return true;
}
wchar_t str[MAX_PATH];
DWORD i;
for (i = 0; process_name_[i]; i++) {
str[i] = process_name_[i];
if (process_name_[i] == L'.')
break;
}
*(DWORD *)(str + i + 1) = 0x630068; //.hcb
*(DWORD *)(str + i + 3) = 0x62;
if (IthCheckFile(str)) {
ConsoleOutput("vnreng: IGNORE FVP"); // jichi 10/3/2013: such like «¢«È«ê«¨ª«ª°ªä
return true;
}
return false;
}
// 12/13/2013: Declare it in a way compatible to EXCEPTION_PROCEDURE
EXCEPTION_DISPOSITION ExceptHandler(PEXCEPTION_RECORD ExceptionRecord, LPVOID, PCONTEXT, LPVOID)
{
if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) {
module_limit_ = ExceptionRecord->ExceptionInformation[1];
//OutputDWORD(module_limit_);
__asm
{
mov eax,fs:[0x30] // jichi 12/13/2013: get PEB
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov ecx,module_limit_
sub ecx,module_base_
mov [eax+0x20],ecx
}
}
//ContextRecord->Esp = recv_esp;
//ContextRecord->Eip = recv_eip;
//return ExceptionContinueExecution; // jichi 3/11/2014: this will still crash. Not sure why ITH use this. Change to ExceptionContinueSearch
return ExceptionContinueSearch; // an unwind is in progress,
}
// jichi 9/14/2013: Certain ITH functions like FindEntryAligned might raise exception without admin priv
// Return if succeeded.
bool UnsafeDetermineEngineType()
{
return DeterminePCEngine()
|| DetermineEngineByFile1()
|| DetermineEngineByFile2()
|| DetermineEngineByFile3()
|| DetermineEngineByFile4()
|| DetermineEngineByProcessName()
|| DetermineEngineOther()
|| DetermineEngineAtLast()
|| DetermineEngineGeneric()
|| DetermineNoEngine()
;
}
// jichi 10/21/2014: Return whether found the game engine
bool DetermineEngineType()
{
// jichi 9/27/2013: disable game engine for debugging use
#ifdef ITH_DISABLE_ENGINE
PcHooks::hookLstrFunctions();
PcHooks::hookCharNextFunctions();
return false;
#else
bool found = false;
#ifdef ITH_HAS_SEH
__try { found = UnsafeDetermineEngineType(); }
__except(ExceptHandler((GetExceptionInformation())->ExceptionRecord, 0, 0, 0)) {}
#else // use my own SEH
seh_with_eh(ExceptHandler,
found = UnsafeDetermineEngineType());
#endif // ITH_HAS_SEH
if (::GDIPlusHooksEnabled())
PcHooks::hookGDIPlusFunctions();
if (!found) { // jichi 10/2/2013: Only enable it if no game engine is detected
PcHooks::hookLstrFunctions();
PcHooks::hookCharNextFunctions();
} else
ConsoleOutput("vnreng: found game engine, IGNORE non gui hooks");
return found;
#endif // ITH_DISABLE_ENGINE
}
// __asm
// {
// mov eax,seh_recover
// mov recv_eip,eax
// push ExceptHandler
// push fs:[0]
// mov fs:[0],esp
// pushad
// mov recv_esp,esp
// }
// DetermineEngineType();
// status++;
// __asm
// {
//seh_recover:
// popad
// mov eax,[esp]
// mov fs:[0],eax
// add esp,8
// }
// if (status == 0)
// ConsoleOutput("Fail to identify engine type.");
// else
// ConsoleOutput("Initialized successfully.");
//}
//
HANDLE hijackThread;
void hijackThreadProc(LPVOID lpThreadParameter)
{
CC_UNUSED(lpThreadParameter);
//static bool done = false;
//if (done)
// return;
//done = true;
// jichi 12/18/2013: Though FillRange could raise, it should never raise for he current process
// So, SEH is not used here.
Util::GetProcessName(process_name_); // Initialize shared process name
Util::GetProcessPath(process_path_); // Initialize shared process path
FillRange(process_name_, &module_base_, &module_limit_);
DetermineEngineType();
}
}} // namespace Engine unnamed
// - API -
DWORD Engine::InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{ return trigger_fun_ ? !trigger_fun_(addr, frame, stack) : 0; }
void Engine::hijack()
{
if (!hijackThread) {
ConsoleOutput("vnreng: hijack process");
hijackThread = IthCreateThread(hijackThreadProc, 0);
}
}
void Engine::terminate()
{
if (hijackThread) {
const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
NtWaitForSingleObject(hijackThread, 0, (PLARGE_INTEGER)&timeout);
NtClose(hijackThread);
hijackThread = 0;
}
}
// EOF
/*
extern "C" {
// http://gmogre3d.googlecode.com/svn-history/r815/trunk/OgreMain/src/WIN32/OgreMinGWSupport.cpp
// http://forum.osdev.org/viewtopic.php?f=8&t=22352
//#pragma data_seg()
//#pragma comment(linker, "/merge:.CRT=.data") // works fine in visual c++ 6
//#pragma data_seg()
//#pragma comment(linker, "/merge:.CRT=.rdata")
// MSVC libs use _chkstk for stack-probing. MinGW equivalent is _alloca.
//void _alloca();
//void _chkstk() { _alloca(); }
// MSVC uses security cookies to prevent some buffer overflow attacks.
// provide dummy implementations.
//void _fastcall __security_check_cookie(intptr_t i) {}
void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie) {}
}
*/

View File

@ -0,0 +1,23 @@
#pragma once
// engine/match.h
// 8/23/2013 jichi
// TODO: Clean up the interface to match game engines.
// Split the engine match logic out of hooks.
// Modify the game hook to allow replace functions for arbitary purpose
// instead of just extracting text.
#include <windows.h>
namespace Engine {
// jichi 10/21/2014: Return whether found the engine
void hijack();
void terminate();
// jichi 10/21/2014: Return 0 if failed
DWORD InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack);
} // namespace Engine
// EOF

View File

@ -0,0 +1,55 @@
#pragma once
// mono/funcinfo.h
// 12/26/2014
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
//#include "ith/import/mono/types.h"
// MonoString* mono_string_new (MonoDomain *domain,
// const char *text);
// MonoString* mono_string_new_len (MonoDomain *domain,
// const char *text,
// guint length);
// MonoString* mono_string_new_size (MonoDomain *domain,
// gint32 len);
// MonoString* mono_string_new_utf16 (MonoDomain *domain,
// const guint16 *text,
// gint32 len);
// MonoString* mono_string_from_utf16 (gunichar2 *data);
// mono_unichar2* mono_string_to_utf16 (MonoString *s);
// char* mono_string_to_utf8 (MonoString *s);
// gboolean mono_string_equal (MonoString *s1,
// MonoString *s2);
// guint mono_string_hash (MonoString *s);
// MonoString* mono_string_intern (MonoString *str);
// MonoString* mono_string_is_interned (MonoString *o);
// MonoString* mono_string_new_wrapper (const char *text);
// gunichar2* mono_string_chars (MonoString *s);
// int mono_string_length (MonoString *s);
// gunichar2* mono_unicode_from_external (const gchar *in, gsize *bytes);
// gchar* mono_unicode_to_external (const gunichar2 *uni);
// gchar* mono_utf8_from_external (const gchar *in);
struct MonoFunction {
const char *functionName;
size_t textIndex; // argument index, starting from 0
size_t lengthIndex; // argument index, start from 0
unsigned long hookType; // HookParam type
void *text_fun; // HookParam::text_fun_t
};
#define MONO_FUNCTIONS_INITIALIZER \
{ "mono_string_to_utf8", 0, 0, USING_UNICODE|NO_CONTEXT, SpecialHookMonoString } \
, { "mono_string_to_utf8_checked", 0, 0, USING_UNICODE|NO_CONTEXT, SpecialHookMonoString } \
, { "mono_string_to_utf16", 0, 0, USING_UNICODE|NO_CONTEXT, SpecialHookMonoString } \
, { "mono_utf8_from_external", 1, 0, USING_STRING|USING_UTF8, nullptr } \
, { "mono_string_from_utf16", 1, 0, USING_UNICODE, nullptr } \
, { "mono_string_new_utf16", 2, 3, USING_UNICODE, nullptr } \
, { "mono_unicode_from_external", 1, 0, USING_UNICODE, nullptr } \
, { "mono_unicode_to_external", 1, 0, USING_UNICODE, nullptr }
//, { "mono_string_new", 2, 0, USING_STRING|USING_UTF8, nullptr }
//, { "mono_string_new_wrapper", 1, 0, USING_STRING|USING_UTF8, nullptr }
// EOF

View File

@ -0,0 +1,41 @@
#pragma once
// mono/types.h
// 12/26/2014
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
#include <cstdint>
// mono/io-layer/uglify.h
typedef int8_t gint8;
typedef int32_t gint32;
typedef wchar_t gunichar2; // either char or wchar_t, depending on how mono is compiled
typedef gint8 mono_byte;
typedef gunichar2 mono_unichar2;
// mono/metadata/object.h
typedef mono_byte MonoBoolean;
struct MonoArray;
struct MonoDelegate;
struct MonoException;
struct MonoString;
struct MonoThreadsSync;
struct MonoThread;
struct MonoVTable;
struct MonoObject {
MonoVTable *vtable;
MonoThreadsSync *synchronisation;
};
struct MonoString {
MonoObject object;
gint32 length;
gunichar2 chars[0];
};
// EOF

Some files were not shown because too many files have changed in this diff Show More