mirror of
https://github.com/Artikash/Textractor.git
synced 2024-12-24 01:14:12 +08:00
commit
a85c398f47
5
.gitignore
vendored
5
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
.vs/
|
Builds/
|
||||||
CMakeBuilds/
|
*.vs/
|
||||||
|
*.pro.user
|
103
CMakeLists.txt
103
CMakeLists.txt
@ -1,38 +1,12 @@
|
|||||||
cmake_minimum_required(VERSION 2.8)
|
cmake_minimum_required(VERSION 2.8)
|
||||||
|
|
||||||
set(CMAKE_CONFIGURATION_TYPES Debug Release)
|
|
||||||
|
|
||||||
project(NextHooker)
|
project(NextHooker)
|
||||||
|
|
||||||
set(CMAKE_INSTALL_PREFIX "" CACHE FILEPATH "installation path")
|
|
||||||
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/Debug")
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release")
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/Debug")
|
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/Release")
|
|
||||||
|
|
||||||
set(CMAKE_WARN_ON_ABSOLUTE_INSTALL_DESTINATION ON)
|
|
||||||
|
|
||||||
execute_process(COMMAND "cmd" " /C date /T" OUTPUT_VARIABLE FULLDATE)
|
|
||||||
# set(DATE "07/13/2018")
|
|
||||||
string(SUBSTRING ${FULLDATE} 4 10 BUILD_DATE)
|
|
||||||
set(BUILD_DATE ${BUILD_DATE})
|
|
||||||
|
|
||||||
set(CPACK_GENERATOR "ZIP")
|
|
||||||
set(CPACK_PACKAGE_VERSION_MAJOR 1)
|
|
||||||
set(CPACK_PACKAGE_VERSION_MINOR 0)
|
|
||||||
set(CPACK_PACKAGE_VERSION_PATCH 4)
|
|
||||||
set(CPACK_SOURCE_GENERATOR "ZIP")
|
|
||||||
set(CPACK_SOURCE_IGNORE_FILES "/CVS/;/\\\\.svn/;/\\\\.bzr/;/\\\\.hg/;/\\\\.git/;\\\\.swp$;\\\\.#;/#" ".*\\\\.user$" "\\\\.gitignore$" "\\\\.gitmodules$" "\\\\.git$")
|
|
||||||
include(CPack)
|
|
||||||
|
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
#/Zc:auto # config.pri
|
#/Zc:auto # config.pri
|
||||||
/wd4819 # config.pri
|
/wd4819 # config.pri
|
||||||
/MP
|
/MP
|
||||||
/GS-
|
/GS-
|
||||||
$<$<CONFIG:MinSizeRel>:/MT>
|
|
||||||
#$<$<CONFIG:Debug>:/MTd>
|
|
||||||
)
|
)
|
||||||
|
|
||||||
add_definitions(
|
add_definitions(
|
||||||
@ -47,80 +21,17 @@ add_definitions(
|
|||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
.
|
.
|
||||||
|
|
||||||
texthook
|
texthook
|
||||||
${CMAKE_BINARY_DIR}/gui
|
|
||||||
)
|
)
|
||||||
|
|
||||||
set(resource_src
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug)
|
||||||
gui/ITHVNR.rc
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug)
|
||||||
gui/icon1.ico
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/Debug)
|
||||||
)
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release)
|
||||||
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release)
|
||||||
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/Release)
|
||||||
|
|
||||||
set(nexthooker_src
|
set(CMAKE_CONFIGURATION_TYPES Debug Release)
|
||||||
gui/command.cpp
|
|
||||||
gui/ITH.h
|
|
||||||
gui/language.cpp
|
|
||||||
gui/language.h
|
|
||||||
gui/main.cpp
|
|
||||||
gui/ProcessWindow.cpp
|
|
||||||
gui/ProcessWindow.h
|
|
||||||
gui/ProfileManager.cpp
|
|
||||||
gui/ProfileManager.h
|
|
||||||
gui/resource.h
|
|
||||||
gui/utility.cpp
|
|
||||||
gui/utility.h
|
|
||||||
${CMAKE_BINARY_DIR}/gui/version.h
|
|
||||||
gui/version.h.in
|
|
||||||
gui/window.cpp
|
|
||||||
gui/window.h
|
|
||||||
gui/TextBuffer.cpp
|
|
||||||
gui/TextBuffer.h
|
|
||||||
gui/profile/Profile.h
|
|
||||||
gui/profile/Profile.cpp
|
|
||||||
gui/profile/pugiconfig.h
|
|
||||||
gui/profile/pugixml.cpp
|
|
||||||
gui/profile/pugixml.h
|
|
||||||
gui/profile/misc.h
|
|
||||||
gui/profile/misc.cpp
|
|
||||||
${resource_src}
|
|
||||||
)
|
|
||||||
|
|
||||||
source_group("Resource Files" FILES ${resource_src})
|
|
||||||
|
|
||||||
add_executable(${PROJECT_NAME} ${nexthooker_src})
|
|
||||||
|
|
||||||
add_subdirectory(texthook)
|
add_subdirectory(texthook)
|
||||||
add_subdirectory(vnrhook)
|
add_subdirectory(vnrhook)
|
||||||
# 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='*'\""
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_definitions(${PROJECT_NAME}
|
|
||||||
PRIVATE
|
|
||||||
PSAPI_VERSION=1
|
|
||||||
DEFAULT_MM
|
|
||||||
)
|
|
||||||
|
|
||||||
target_link_libraries(${PROJECT_NAME}
|
|
||||||
#profile
|
|
||||||
vnrhost
|
|
||||||
#ithsys
|
|
||||||
#${WDK_HOME}/lib/wxp/i386/ntdll.lib
|
|
||||||
comctl32.lib
|
|
||||||
psapi.lib
|
|
||||||
)
|
|
||||||
|
|
||||||
target_compile_options(${PROJECT_NAME}
|
|
||||||
PRIVATE
|
|
||||||
/EHsc
|
|
||||||
)
|
|
||||||
|
|
||||||
install(TARGETS ${PROJECT_NAME}
|
|
||||||
DESTINATION .
|
|
||||||
CONFIGURATIONS Release
|
|
||||||
)
|
|
||||||
|
|
||||||
configure_file(gui/version.h.in gui/version.h)
|
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
"generator": "Ninja",
|
"generator": "Ninja",
|
||||||
"configurationType": "Debug",
|
"configurationType": "Debug",
|
||||||
"inheritEnvironments": [ "msvc_x86" ],
|
"inheritEnvironments": [ "msvc_x86" ],
|
||||||
"buildRoot": "${workspaceRoot}\\CMakeBuilds\\build\\${name}",
|
"buildRoot": "${workspaceRoot}\\Builds\\${configurationType}",
|
||||||
"installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
"installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
||||||
"cmakeCommandArgs": "",
|
"cmakeCommandArgs": "",
|
||||||
"buildCommandArgs": "-v",
|
"buildCommandArgs": "-v",
|
||||||
@ -15,9 +15,9 @@
|
|||||||
{
|
{
|
||||||
"name": "x86-Release",
|
"name": "x86-Release",
|
||||||
"generator": "Ninja",
|
"generator": "Ninja",
|
||||||
"configurationType": "MinSizeRel",
|
"configurationType": "Release",
|
||||||
"inheritEnvironments": [ "msvc_x86" ],
|
"inheritEnvironments": [ "msvc_x86" ],
|
||||||
"buildRoot": "${workspaceRoot}\\CMakeBuilds\\build\\${name}",
|
"buildRoot": "${workspaceRoot}\\Builds\\${configurationType}",
|
||||||
"installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
"installRoot": "${workspaceRoot}\\CMakeBuilds\\${workspaceHash}\\install\\${name}",
|
||||||
"cmakeCommandArgs": "",
|
"cmakeCommandArgs": "",
|
||||||
"buildCommandArgs": "-v",
|
"buildCommandArgs": "-v",
|
||||||
|
47
GUI/GUI.pro
Normal file
47
GUI/GUI.pro
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
#-------------------------------------------------
|
||||||
|
#
|
||||||
|
# Project created by QtCreator 2018-07-21T15:14:19
|
||||||
|
#
|
||||||
|
#-------------------------------------------------
|
||||||
|
|
||||||
|
QT += core gui widgets
|
||||||
|
|
||||||
|
TARGET = GUI
|
||||||
|
TEMPLATE = app
|
||||||
|
|
||||||
|
# The following define makes your compiler emit warnings if you use
|
||||||
|
# any feature of Qt which has been marked as deprecated (the exact warnings
|
||||||
|
# depend on your compiler). Please consult the documentation of the
|
||||||
|
# deprecated API in order to know how to port your code away from it.
|
||||||
|
DEFINES += QT_DEPRECATED_WARNINGS
|
||||||
|
|
||||||
|
# You can also make your code fail to compile if you use deprecated APIs.
|
||||||
|
# In order to do so, uncomment the following line.
|
||||||
|
# You can also select to disable deprecated APIs only up to a certain version of Qt.
|
||||||
|
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
|
||||||
|
|
||||||
|
CONFIG += c++11
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
main.cpp \
|
||||||
|
mainwindow.cpp \
|
||||||
|
hostsignaller.cpp
|
||||||
|
|
||||||
|
HEADERS += \
|
||||||
|
mainwindow.h \
|
||||||
|
hostsignaller.h
|
||||||
|
|
||||||
|
FORMS += \
|
||||||
|
mainwindow.ui
|
||||||
|
|
||||||
|
win32: LIBS += \
|
||||||
|
-L$$PWD/../Builds/Debug/Debug/ -lvnrhost
|
||||||
|
|
||||||
|
QMAKE_CXXFLAGS_RELEASE += \
|
||||||
|
/MT
|
||||||
|
|
||||||
|
# Default rules for deployment.
|
||||||
|
qnx: target.path = /tmp/$${TARGET}/bin
|
||||||
|
else: unix:!android: target.path = /opt/$${TARGET}/bin
|
||||||
|
!isEmpty(target.path): INSTALLS += target
|
||||||
|
|
19
GUI/hostsignaller.cpp
Normal file
19
GUI/hostsignaller.cpp
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#include "hostsignaller.h"
|
||||||
|
#include "../texthook/host.h"
|
||||||
|
|
||||||
|
void HostSignaller::Initialize()
|
||||||
|
{
|
||||||
|
Host::RegisterProcessAttachCallback([&](DWORD pid){ emit AddProcess(pid); });
|
||||||
|
Host::RegisterProcessDetachCallback([&](DWORD pid){ emit RemoveProcess(pid); });
|
||||||
|
Host::RegisterThreadCreateCallback([&](TextThread* thread)
|
||||||
|
{
|
||||||
|
emit AddThread(thread);
|
||||||
|
thread->RegisterOutputCallBack([&](TextThread* thread, std::wstring output)
|
||||||
|
{
|
||||||
|
//output = DispatchToExtensions(output);
|
||||||
|
emit ThreadOutput(thread, QString::fromWCharArray(output.c_str()));
|
||||||
|
return output;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Host::RegisterThreadRemoveCallback([&](TextThread* thread){ emit RemoveThread(thread); });
|
||||||
|
}
|
24
GUI/hostsignaller.h
Normal file
24
GUI/hostsignaller.h
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#ifndef HOSTSIGNALLER_H
|
||||||
|
#define HOSTSIGNALLER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include "../texthook/host.h"
|
||||||
|
|
||||||
|
// Artikash 7/24/2018: This class is a workaround for the fact that Qt only lets me manipulate the GUI in the main thread.
|
||||||
|
class HostSignaller : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
void Initialize();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void AddProcess(unsigned int processId);
|
||||||
|
void RemoveProcess(unsigned int processId);
|
||||||
|
void AddThread(TextThread* thread);
|
||||||
|
void RemoveThread(TextThread* thread);
|
||||||
|
void ThreadOutput(TextThread* thread, QString output);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // HOSTSIGNALLER_H
|
11
GUI/main.cpp
Normal file
11
GUI/main.cpp
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QApplication a(argc, argv);
|
||||||
|
MainWindow w;
|
||||||
|
w.show();
|
||||||
|
|
||||||
|
return a.exec();
|
||||||
|
}
|
129
GUI/mainwindow.cpp
Normal file
129
GUI/mainwindow.cpp
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
#include "mainwindow.h"
|
||||||
|
#include "ui_mainwindow.h"
|
||||||
|
#include "QCoreApplication"
|
||||||
|
#include "QTextBrowser"
|
||||||
|
#include "QMessageBox"
|
||||||
|
#include "QComboBox"
|
||||||
|
#include "QLineEdit"
|
||||||
|
#include "QInputDialog"
|
||||||
|
#include <QCursor>
|
||||||
|
#include <Qt>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include <qdebug.h>
|
||||||
|
#include <Psapi.h>
|
||||||
|
#include "../texthook/host.h"
|
||||||
|
|
||||||
|
QMainWindow* mainWindow;
|
||||||
|
QComboBox* processCombo;
|
||||||
|
QComboBox* ttCombo;
|
||||||
|
QTextBrowser* textOutput;
|
||||||
|
|
||||||
|
QString GetModuleName(DWORD processId, HMODULE module = NULL)
|
||||||
|
{
|
||||||
|
HANDLE handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, processId);
|
||||||
|
wchar_t buffer[MAX_PATH];
|
||||||
|
GetModuleFileNameExW(handle, module, buffer, MAX_PATH);
|
||||||
|
CloseHandle(handle);
|
||||||
|
return QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ProcessString(DWORD processId)
|
||||||
|
{
|
||||||
|
return QString("%1: %2").arg(QString::number(processId), GetModuleName(processId));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString TextThreadString(TextThread* thread)
|
||||||
|
{
|
||||||
|
ThreadParameter tp = thread->GetThreadParameter();
|
||||||
|
return QString("%1:%2:%3:%4:%5: ").arg(
|
||||||
|
QString::number(thread->Number()),
|
||||||
|
QString::number(tp.pid),
|
||||||
|
QString::number(tp.hook, 16),
|
||||||
|
QString::number(tp.retn, 16),
|
||||||
|
QString::number(tp.spl, 16)
|
||||||
|
).toUpper() + QString::fromWCharArray(Host::GetHookName(tp.pid, tp.hook).c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
QMainWindow(parent),
|
||||||
|
ui(new Ui::MainWindow),
|
||||||
|
hostSignaller(new HostSignaller)
|
||||||
|
{
|
||||||
|
ui->setupUi(this);
|
||||||
|
mainWindow = this;
|
||||||
|
processCombo = mainWindow->findChild<QComboBox*>("processCombo");
|
||||||
|
ttCombo = mainWindow->findChild<QComboBox*>("ttCombo");
|
||||||
|
textOutput = mainWindow->findChild<QTextBrowser*>("textOutput");
|
||||||
|
|
||||||
|
Host::Start();
|
||||||
|
hostSignaller->Initialize();
|
||||||
|
connect(hostSignaller, &HostSignaller::AddProcess, this, &MainWindow::AddProcess);
|
||||||
|
connect(hostSignaller, &HostSignaller::RemoveProcess, this, &MainWindow::RemoveProcess);
|
||||||
|
connect(hostSignaller, &HostSignaller::AddThread, this, &MainWindow::AddThread);
|
||||||
|
connect(hostSignaller, &HostSignaller::RemoveThread, this, &MainWindow::RemoveThread);
|
||||||
|
connect(hostSignaller, &HostSignaller::ThreadOutput, this, &MainWindow::ThreadOutput);
|
||||||
|
Host::Open();
|
||||||
|
}
|
||||||
|
|
||||||
|
MainWindow::~MainWindow()
|
||||||
|
{
|
||||||
|
Host::Close();
|
||||||
|
delete hostSignaller;
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::AddProcess(unsigned int processId)
|
||||||
|
{
|
||||||
|
processCombo->addItem(ProcessString(processId), Qt::AlignHCenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::RemoveProcess(unsigned int processId)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < processCombo->count(); ++i)
|
||||||
|
if (processCombo->itemText(i).split(":")[0] == QString::number(processId))
|
||||||
|
processCombo->removeItem(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::AddThread(TextThread* thread)
|
||||||
|
{
|
||||||
|
ttCombo->addItem(TextThreadString(thread));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::RemoveThread(TextThread* thread)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < ttCombo->count(); ++i)
|
||||||
|
if (ttCombo->itemText(i).split(":")[0] == QString::number(thread->Number()))
|
||||||
|
{
|
||||||
|
ttCombo->removeItem(i);
|
||||||
|
if (i == ttCombo->currentIndex())
|
||||||
|
{
|
||||||
|
ttCombo->setCurrentIndex(0);
|
||||||
|
on_ttCombo_activated(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::ThreadOutput(TextThread* thread, QString output)
|
||||||
|
{
|
||||||
|
if (TextThreadString(thread) == ttCombo->currentText())
|
||||||
|
{
|
||||||
|
textOutput->moveCursor(QTextCursor::End);
|
||||||
|
textOutput->insertPlainText(output);
|
||||||
|
textOutput->moveCursor(QTextCursor::End);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_attachButton_clicked()
|
||||||
|
{
|
||||||
|
Host::InjectProcess(QInputDialog::getInt(this, "Process ID?", "You can find this under Task Manager -> Details"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_detachButton_clicked()
|
||||||
|
{
|
||||||
|
Host::DetachProcess(processCombo->currentText().split(":")[0].toInt());
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::on_ttCombo_activated(int index)
|
||||||
|
{
|
||||||
|
textOutput->setText(QString::fromWCharArray(Host::GetThread(ttCombo->itemText(index).split(":")[0].toInt())->GetStore().c_str()));
|
||||||
|
}
|
38
GUI/mainwindow.h
Normal file
38
GUI/mainwindow.h
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#ifndef MAINWINDOW_H
|
||||||
|
#define MAINWINDOW_H
|
||||||
|
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <Windows.h>
|
||||||
|
#include "../texthook/textthread.h"
|
||||||
|
#include "hostsignaller.h"
|
||||||
|
|
||||||
|
namespace Ui
|
||||||
|
{
|
||||||
|
class MainWindow;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MainWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit MainWindow(QWidget *parent = nullptr);
|
||||||
|
~MainWindow();
|
||||||
|
|
||||||
|
QString ProcessOutput(TextThread *thread, QString output);
|
||||||
|
private slots:
|
||||||
|
void on_attachButton_clicked();
|
||||||
|
void on_detachButton_clicked();
|
||||||
|
void on_ttCombo_activated(int index);
|
||||||
|
void AddProcess(unsigned int processId);
|
||||||
|
void RemoveProcess(unsigned int processId);
|
||||||
|
void AddThread(TextThread* thread);
|
||||||
|
void RemoveThread(TextThread* thread);
|
||||||
|
void ThreadOutput(TextThread* thread, QString output);
|
||||||
|
|
||||||
|
private:
|
||||||
|
Ui::MainWindow *ui;
|
||||||
|
HostSignaller* hostSignaller;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MAINWINDOW_H
|
117
GUI/mainwindow.ui
Normal file
117
GUI/mainwindow.ui
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>MainWindow</class>
|
||||||
|
<widget class="QMainWindow" name="MainWindow">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>600</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>10</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>NextHooker</string>
|
||||||
|
</property>
|
||||||
|
<widget class="QWidget" name="centralWidget">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="processManager">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||||
|
<horstretch>2</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="lineEdit"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="processCombo">
|
||||||
|
<property name="insertPolicy">
|
||||||
|
<enum>QComboBox::InsertAtBottom</enum>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="attachButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Attach to game</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="detachButton">
|
||||||
|
<property name="text">
|
||||||
|
<string>Detach from game</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QFrame" name="output">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
|
||||||
|
<horstretch>6</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="frameShape">
|
||||||
|
<enum>QFrame::StyledPanel</enum>
|
||||||
|
</property>
|
||||||
|
<property name="frameShadow">
|
||||||
|
<enum>QFrame::Raised</enum>
|
||||||
|
</property>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QComboBox" name="ttCombo"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTextBrowser" name="textOutput">
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||||
|
<horstretch>5</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="font">
|
||||||
|
<font>
|
||||||
|
<pointsize>12</pointsize>
|
||||||
|
</font>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<widget class="QMenuBar" name="menuBar">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>800</width>
|
||||||
|
<height>23</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</widget>
|
||||||
|
<layoutdefault spacing="6" margin="11"/>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
@ -47,7 +47,8 @@ GPL v3
|
|||||||
|
|
||||||
## Compiling
|
## Compiling
|
||||||
|
|
||||||
Before compiling *NextHooker*, You should get CMake, [Windows Driver Kit 7.1](http://www.microsoft.com/en-us/download/details.aspx?id=11800), and Visual Studio.
|
Before compiling *NextHooker*, you should get Visual Studio with CMake and ATL support, as well as Qt version 5.11<br>
|
||||||
|
Use Visual Studio to compile all the CMake projects, then use Qt Creator to compile/run the GUI.
|
||||||
|
|
||||||
## Project Architecture
|
## Project Architecture
|
||||||
|
|
||||||
|
37
gui/ITH.h
37
gui/ITH.h
@ -1,37 +0,0 @@
|
|||||||
/* 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 <Windows.h>
|
|
||||||
#include <string>
|
|
||||||
#include <sstream>
|
|
||||||
#include <ios>
|
|
||||||
#include <memory>
|
|
||||||
#include <stdexcept>
|
|
||||||
#include <assert.h>
|
|
||||||
#include <WindowsX.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <Psapi.h>
|
|
||||||
#include <vector>
|
|
||||||
#include <map>
|
|
||||||
#include <CommCtrl.h>
|
|
||||||
#include <intrin.h>
|
|
||||||
#include <sstream>
|
|
||||||
#include <regex>
|
|
||||||
#include <set>
|
|
||||||
#include "profile/pugixml.h"
|
|
||||||
#pragma warning(disable: 4146)
|
|
@ -1,70 +0,0 @@
|
|||||||
// Generated by ResEdit 1.6.6
|
|
||||||
// Copyright (C) 2006-2015
|
|
||||||
// http://www.resedit.net
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
#include <commctrl.h>
|
|
||||||
#include "resource.h"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Dialog resources
|
|
||||||
//
|
|
||||||
LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN
|
|
||||||
IDD_DIALOG2 DIALOGEX 100, 100, 341, 210
|
|
||||||
STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
|
|
||||||
CAPTION "Process Explorer"
|
|
||||||
FONT 8, "MS Shell Dlg", 400, 0, 1
|
|
||||||
{
|
|
||||||
DEFPUSHBUTTON "OK", IDOK, 281, 189, 53, 14, 0, WS_EX_LEFT
|
|
||||||
PUSHBUTTON "Remove Profile", 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 "Process", IDC_STATIC, 7, 7, 65, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
|
||||||
PUSHBUTTON "Attach", IDC_BUTTON2, 61, 189, 53, 14, 0, WS_EX_LEFT
|
|
||||||
PUSHBUTTON "Detach", IDC_BUTTON3, 116, 189, 53, 14, 0, WS_EX_LEFT
|
|
||||||
PUSHBUTTON "Add Profile", IDC_BUTTON5, 171, 189, 53, 14, 0, WS_EX_LEFT
|
|
||||||
PUSHBUTTON "Refresh", IDC_BUTTON1, 7, 189, 53, 14, 0, WS_EX_LEFT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN
|
|
||||||
IDD_DIALOG4 DIALOGEX 150, 100, 123, 71
|
|
||||||
STYLE DS_MODALFRAME | DS_SHELLFONT | WS_CAPTION | WS_VISIBLE | WS_POPUP | WS_SYSMENU
|
|
||||||
CAPTION "Option"
|
|
||||||
FONT 8, "MS Shell Dlg", 400, 0, 1
|
|
||||||
{
|
|
||||||
LTEXT "More options coming soon?", 0, 18, 25, 88, 9, SS_LEFT, WS_EX_LEFT
|
|
||||||
DEFPUSHBUTTON "OK", IDOK, 5, 52, 50, 14, 0, WS_EX_LEFT
|
|
||||||
PUSHBUTTON "Cancel", IDCANCEL, 63, 52, 50, 14, 0, WS_EX_LEFT
|
|
||||||
EDITTEXT IDC_EDIT1, 60, 7, 55, 14, ES_AUTOHSCROLL, WS_EX_LEFT
|
|
||||||
LTEXT "Split time", IDC_STATIC, 7, 7, 47, 13, SS_LEFT | SS_CENTERIMAGE, WS_EX_LEFT
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Icon resources
|
|
||||||
//
|
|
||||||
LANGUAGE LANG_JAPANESE, SUBLANG_JAPANESE_JAPAN
|
|
||||||
IDI_ICON1 ICON "icon1.ico"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Version Information resources
|
|
||||||
//
|
|
||||||
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
|
|
||||||
1 VERSIONINFO
|
|
||||||
FILEVERSION 0,0,0,0
|
|
||||||
PRODUCTVERSION 0,0,0,0
|
|
||||||
FILEOS VOS_UNKNOWN
|
|
||||||
FILETYPE VFT_UNKNOWN
|
|
||||||
FILESUBTYPE VFT2_UNKNOWN
|
|
||||||
FILEFLAGSMASK 0
|
|
||||||
FILEFLAGS 0
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
@ -1,144 +0,0 @@
|
|||||||
#include "ProcessWindow.h"
|
|
||||||
#include "resource.h"
|
|
||||||
#include "host.h"
|
|
||||||
#include "hookman.h"
|
|
||||||
#include "ProfileManager.h"
|
|
||||||
#include "profile/Profile.h"
|
|
||||||
|
|
||||||
extern HookManager* man; // main.cpp
|
|
||||||
extern ProfileManager* pfman; // ProfileManager.cpp
|
|
||||||
|
|
||||||
ProcessWindow::ProcessWindow(HWND hDialog) : hDlg(hDialog)
|
|
||||||
{
|
|
||||||
hbRefresh = GetDlgItem(hDlg, IDC_BUTTON1);
|
|
||||||
hbAttach = GetDlgItem(hDlg, IDC_BUTTON2);
|
|
||||||
hbDetach = GetDlgItem(hDlg, IDC_BUTTON3);
|
|
||||||
hbAddProfile = GetDlgItem(hDlg, IDC_BUTTON5);
|
|
||||||
hbRemoveProfile = GetDlgItem(hDlg, IDC_BUTTON6);
|
|
||||||
EnableWindow(hbAddProfile, FALSE);
|
|
||||||
EnableWindow(hbRemoveProfile, FALSE);
|
|
||||||
hlProcess = GetDlgItem(hDlg, IDC_LIST1);
|
|
||||||
heOutput = GetDlgItem(hDlg, IDC_EDIT1);
|
|
||||||
ListView_SetExtendedListViewStyleEx(hlProcess, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT);
|
|
||||||
InitProcessDlg();
|
|
||||||
RefreshProcess();
|
|
||||||
EnableWindow(hbDetach, FALSE);
|
|
||||||
EnableWindow(hbAttach, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessWindow::InitProcessDlg()
|
|
||||||
{
|
|
||||||
LVCOLUMN lvc = {};
|
|
||||||
lvc.mask = LVCF_FMT | LVCF_TEXT | LVCF_WIDTH;
|
|
||||||
lvc.fmt = LVCFMT_RIGHT; // left-aligned column
|
|
||||||
lvc.cx = 40;
|
|
||||||
lvc.pszText = L"PID";
|
|
||||||
ListView_InsertColumn(hlProcess, 0, &lvc);
|
|
||||||
lvc.cx = 100;
|
|
||||||
lvc.fmt = LVCFMT_LEFT; // left-aligned column
|
|
||||||
lvc.pszText = L"Name";
|
|
||||||
ListView_InsertColumn(hlProcess, 1, &lvc);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessWindow::RefreshProcess()
|
|
||||||
{
|
|
||||||
ListView_DeleteAllItems(hlProcess);
|
|
||||||
LVITEM item = {};
|
|
||||||
item.mask = LVIF_TEXT | LVIF_PARAM | LVIF_STATE;
|
|
||||||
DWORD idProcess[1024], cbNeeded;
|
|
||||||
WCHAR path[MAX_PATH];
|
|
||||||
|
|
||||||
if (EnumProcesses(idProcess, sizeof(idProcess), &cbNeeded))
|
|
||||||
{
|
|
||||||
DWORD len = cbNeeded / sizeof(DWORD);
|
|
||||||
for (DWORD i = 0; i < len; ++i)
|
|
||||||
{
|
|
||||||
DWORD pid = idProcess[i];
|
|
||||||
UniqueHandle hProcess(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
|
|
||||||
if (hProcess)
|
|
||||||
{
|
|
||||||
if (GetProcessImageFileName(hProcess.get(), path, MAX_PATH))
|
|
||||||
{
|
|
||||||
WCHAR buffer[256];
|
|
||||||
std::swprintf(buffer, L"%d", pid);
|
|
||||||
PWCHAR name = wcsrchr(path, L'\\') + 1;
|
|
||||||
item.pszText = buffer;
|
|
||||||
item.lParam = pid;
|
|
||||||
ListView_InsertItem(hlProcess, &item);
|
|
||||||
ListView_SetItemText(hlProcess, item.iItem, 1, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessWindow::AttachProcess()
|
|
||||||
{
|
|
||||||
DWORD pid = GetSelectedPID();
|
|
||||||
if (InjectProcessById(pid))
|
|
||||||
RefreshThreadWithPID(pid, true);
|
|
||||||
else
|
|
||||||
ConsoleOutput(L"NextHooker: could not inject");
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessWindow::DetachProcess()
|
|
||||||
{
|
|
||||||
DWORD pid = GetSelectedPID();
|
|
||||||
DetachProcessById(pid);
|
|
||||||
RefreshThreadWithPID(pid, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessWindow::CreateProfileForSelectedProcess()
|
|
||||||
{
|
|
||||||
DWORD pid = GetSelectedPID();
|
|
||||||
auto path = GetProcessPath(pid);
|
|
||||||
if (!path.empty())
|
|
||||||
{
|
|
||||||
Profile* pf = pfman->CreateProfile(pid);
|
|
||||||
pfman->SaveProfiles();
|
|
||||||
RefreshThread(ListView_GetSelectionMark(hlProcess));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessWindow::DeleteProfileForSelectedProcess()
|
|
||||||
{
|
|
||||||
DWORD pid = GetSelectedPID();
|
|
||||||
auto path = GetProcessPath(pid);
|
|
||||||
if (!path.empty())
|
|
||||||
{
|
|
||||||
pfman->DeleteProfile(path);
|
|
||||||
RefreshThread(ListView_GetSelectionMark(hlProcess));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessWindow::RefreshThread(int index)
|
|
||||||
{
|
|
||||||
LVITEM item = {};
|
|
||||||
item.mask = LVIF_PARAM;
|
|
||||||
item.iItem = index;
|
|
||||||
ListView_GetItem(hlProcess, &item);
|
|
||||||
DWORD pid = item.lParam;
|
|
||||||
bool isAttached = man->GetHostPipe(pid) != NULL;
|
|
||||||
RefreshThreadWithPID(pid, isAttached);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProcessWindow::RefreshThreadWithPID(DWORD pid, bool isAttached)
|
|
||||||
{
|
|
||||||
EnableWindow(hbDetach, isAttached);
|
|
||||||
EnableWindow(hbAttach, !isAttached);
|
|
||||||
auto path = GetProcessPath(pid);
|
|
||||||
bool hasProfile = !path.empty() && pfman->HasProfile(path);
|
|
||||||
EnableWindow(hbAddProfile, isAttached && !hasProfile);
|
|
||||||
EnableWindow(hbRemoveProfile, hasProfile);
|
|
||||||
if (pid == GetCurrentProcessId())
|
|
||||||
EnableWindow(hbAttach, FALSE);
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD ProcessWindow::GetSelectedPID()
|
|
||||||
{
|
|
||||||
LVITEM item = {};
|
|
||||||
item.mask = LVIF_PARAM;
|
|
||||||
item.iItem = ListView_GetSelectionMark(hlProcess);
|
|
||||||
ListView_GetItem(hlProcess, &item);
|
|
||||||
return item.lParam;
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ITH.h"
|
|
||||||
|
|
||||||
class ProcessWindow
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ProcessWindow(HWND hDialog);
|
|
||||||
void InitProcessDlg();
|
|
||||||
void RefreshProcess();
|
|
||||||
void AttachProcess();
|
|
||||||
void DetachProcess();
|
|
||||||
void CreateProfileForSelectedProcess();
|
|
||||||
void DeleteProfileForSelectedProcess();
|
|
||||||
void RefreshThread(int index);
|
|
||||||
private:
|
|
||||||
void RefreshThreadWithPID(DWORD pid, bool isAttached);
|
|
||||||
DWORD GetSelectedPID();
|
|
||||||
HWND hDlg;
|
|
||||||
HWND hlProcess;
|
|
||||||
HWND hbRefresh,hbAttach,hbDetach,hbAddProfile,hbRemoveProfile;
|
|
||||||
HWND heOutput;
|
|
||||||
};
|
|
@ -1,166 +0,0 @@
|
|||||||
#include "ProfileManager.h"
|
|
||||||
#include "profile/Profile.h"
|
|
||||||
#include "host.h"
|
|
||||||
#include "hookman.h"
|
|
||||||
#include "vnrhook/include/types.h"
|
|
||||||
#include "vnrhook/include/const.h"
|
|
||||||
#include "utility.h"
|
|
||||||
#include "profile/misc.h"
|
|
||||||
|
|
||||||
extern HookManager* man; // main.cpp
|
|
||||||
ProfileManager* pfman;
|
|
||||||
|
|
||||||
ProfileManager::ProfileManager()
|
|
||||||
{
|
|
||||||
LoadProfiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
ProfileManager::~ProfileManager()
|
|
||||||
{
|
|
||||||
SaveProfiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
Profile* ProfileManager::GetProfile(DWORD pid)
|
|
||||||
{
|
|
||||||
std::wstring path = GetProcessPath(pid);
|
|
||||||
if (!path.empty())
|
|
||||||
{
|
|
||||||
auto node = profile_tree.find(path);
|
|
||||||
if (node != profile_tree.end())
|
|
||||||
return node->second.get();
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProfileManager::CreateProfile(pugi::xml_node game)
|
|
||||||
{
|
|
||||||
auto file = game.child(L"File");
|
|
||||||
auto profile = game.child(L"Profile");
|
|
||||||
if (!file || !profile)
|
|
||||||
return false;
|
|
||||||
auto path = file.attribute(L"Path");
|
|
||||||
if (!path)
|
|
||||||
return false;
|
|
||||||
auto profile_title = game.attribute(L"Title");
|
|
||||||
auto title = profile_title ? profile_title.value() : L"";
|
|
||||||
auto pf = new Profile(title);
|
|
||||||
if (!pf->XmlReadProfile(profile))
|
|
||||||
return false;
|
|
||||||
CSLock lock(cs);
|
|
||||||
auto& oldProfile = profile_tree[path.value()];
|
|
||||||
if (!oldProfile)
|
|
||||||
oldProfile.swap(profile_ptr(pf));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Profile* ProfileManager::CreateProfile(DWORD pid)
|
|
||||||
{
|
|
||||||
CSLock lock(cs);
|
|
||||||
auto path = GetProcessPath(pid);
|
|
||||||
auto& pf = profile_tree[path];
|
|
||||||
if (!pf)
|
|
||||||
{
|
|
||||||
std::wstring title = GetProcessTitle(pid);
|
|
||||||
pf.reset(new Profile(title));
|
|
||||||
}
|
|
||||||
return pf.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfileManager::WriteProfileXml(const std::wstring& path, Profile& pf, pugi::xml_node root)
|
|
||||||
{
|
|
||||||
auto game = root.append_child(L"Game");
|
|
||||||
auto file_node = game.append_child(L"File");
|
|
||||||
file_node.append_attribute(L"Path") = path.c_str();
|
|
||||||
auto profile_node = game.append_child(L"Profile");
|
|
||||||
pf.XmlWriteProfile(profile_node);
|
|
||||||
if (!pf.Title().empty())
|
|
||||||
{
|
|
||||||
if (!game.attribute(L"Title"))
|
|
||||||
game.append_attribute(L"Title");
|
|
||||||
game.attribute(L"Title") = pf.Title().c_str();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfileManager::LoadProfiles()
|
|
||||||
{
|
|
||||||
pugi::xml_document doc;
|
|
||||||
UniqueHandle hFile(IthCreateFile(L"NextHooker_Profile.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
|
|
||||||
if (hFile.get() == INVALID_HANDLE_VALUE)
|
|
||||||
return;
|
|
||||||
DWORD size = GetFileSize(hFile.get(), NULL);
|
|
||||||
std::unique_ptr<char[]> buffer(new char[size]);
|
|
||||||
ReadFile(hFile.get(), buffer.get(), size, &size, NULL);
|
|
||||||
auto result = doc.load_buffer(buffer.get(), size);
|
|
||||||
if (!result)
|
|
||||||
return;
|
|
||||||
auto root = doc.root().child(L"NextHookerProfile");
|
|
||||||
if (!root)
|
|
||||||
return;
|
|
||||||
for (auto game = root.begin(); game != root.end(); ++game)
|
|
||||||
CreateProfile(*game);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfileManager::SaveProfiles()
|
|
||||||
{
|
|
||||||
pugi::xml_document doc;
|
|
||||||
auto root = doc.append_child(L"NextHookerProfile");
|
|
||||||
for (auto it = profile_tree.begin(); it != profile_tree.end(); ++it) {
|
|
||||||
auto& path = it->first;
|
|
||||||
auto& profile = it->second;
|
|
||||||
WriteProfileXml(path, *profile, root);
|
|
||||||
}
|
|
||||||
UniqueHandle hFile(IthCreateFile(L"NextHooker_Profile.xml", GENERIC_WRITE, 0, CREATE_ALWAYS));
|
|
||||||
if (hFile.get() != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
FileWriter fw(hFile.get());
|
|
||||||
doc.save(fw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfileManager::DeleteProfile(const std::wstring& path)
|
|
||||||
{
|
|
||||||
CSLock lock(cs);
|
|
||||||
profile_tree.erase(profile_tree.find(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
Profile* ProfileManager::GetProfile(const std::wstring& path)
|
|
||||||
{
|
|
||||||
if (path.empty())
|
|
||||||
return nullptr;
|
|
||||||
auto it = profile_tree.find(path);
|
|
||||||
if (it == profile_tree.end())
|
|
||||||
return nullptr;
|
|
||||||
return it->second.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ProfileManager::HasProfile(const std::wstring& path)
|
|
||||||
{
|
|
||||||
return profile_tree.find(path) != profile_tree.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD ProfileManager::CountProfiles()
|
|
||||||
{
|
|
||||||
return profile_tree.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD SaveProcessProfile(TextThread* thread)
|
|
||||||
{
|
|
||||||
ThreadParameter tp = thread->GetThreadParameter();
|
|
||||||
std::wstring path = GetProcessPath(tp.pid);
|
|
||||||
if (path.empty())
|
|
||||||
return 0;
|
|
||||||
pugi::xml_document doc;
|
|
||||||
pugi::xml_node profile_node = doc.append_child(L"Profile");
|
|
||||||
Profile* pf = pfman->GetProfile(tp.pid);
|
|
||||||
if (pf != NULL)
|
|
||||||
pf->Clear();
|
|
||||||
else
|
|
||||||
pf = pfman->CreateProfile(tp.pid);
|
|
||||||
|
|
||||||
pf->AddHook(hook_ptr(new HookProfile(man->GetHookParam(tp.pid, tp.hook), man->GetHookName(tp.pid, tp.hook))));
|
|
||||||
pf->AddThread(thread_ptr(new ThreadProfile(man->GetHookName(tp.pid, tp.hook), tp.retn, tp.spl, tp.hook, 0, THREAD_MASK_RETN | THREAD_MASK_SPLIT, L"")));
|
|
||||||
|
|
||||||
pf->XmlReadProfile(profile_node);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ITH.h"
|
|
||||||
#include "utility.h" // UniqueHandle, CriticalSection
|
|
||||||
|
|
||||||
class Profile;
|
|
||||||
|
|
||||||
class ProfileManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
ProfileManager();
|
|
||||||
~ProfileManager();
|
|
||||||
Profile* CreateProfile(DWORD pid);
|
|
||||||
Profile* GetProfile(DWORD pid);
|
|
||||||
Profile* GetProfile(const std::wstring& path);
|
|
||||||
void LoadProfiles();
|
|
||||||
void SaveProfiles();
|
|
||||||
void DeleteProfile(const std::wstring& path);
|
|
||||||
void UpdateHookAddresses(DWORD pid);
|
|
||||||
bool HasProfile(const std::wstring& path);
|
|
||||||
private:
|
|
||||||
typedef std::unique_ptr<Profile> profile_ptr;
|
|
||||||
typedef std::map<std::wstring, profile_ptr> profile_map;
|
|
||||||
|
|
||||||
ProfileManager(const ProfileManager&);
|
|
||||||
ProfileManager operator=(const ProfileManager&);
|
|
||||||
|
|
||||||
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;
|
|
||||||
CriticalSection cs;
|
|
||||||
};
|
|
@ -1,49 +0,0 @@
|
|||||||
#include "TextBuffer.h"
|
|
||||||
|
|
||||||
DWORD WINAPI FlushThread(LPVOID lParam)
|
|
||||||
{
|
|
||||||
TextBuffer* t = (TextBuffer*)lParam;
|
|
||||||
while (t->Running())
|
|
||||||
{
|
|
||||||
t->Flush();
|
|
||||||
Sleep(10);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
TextBuffer::TextBuffer(HWND edit) : hThread(IthCreateThread(FlushThread, (DWORD)this)),
|
|
||||||
hEdit(edit),
|
|
||||||
running(true)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
TextBuffer::~TextBuffer()
|
|
||||||
{
|
|
||||||
running = false;
|
|
||||||
WaitForSingleObject(hThread.get(), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextBuffer::AddText(std::wstring text, bool line)
|
|
||||||
{
|
|
||||||
CSLock lock(cs);
|
|
||||||
this->str.append(text);
|
|
||||||
line_break = line;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextBuffer::Flush()
|
|
||||||
{
|
|
||||||
CSLock lock(cs);
|
|
||||||
if (line_break || str.empty())
|
|
||||||
return;
|
|
||||||
DWORD t = Edit_GetTextLength(hEdit);
|
|
||||||
Edit_SetSel(hEdit, t, -1);
|
|
||||||
Edit_ReplaceSel(hEdit, str.c_str());
|
|
||||||
str.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TextBuffer::ClearBuffer()
|
|
||||||
{
|
|
||||||
CSLock lock(cs);
|
|
||||||
str.clear();
|
|
||||||
line_break = false;
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ITH.h"
|
|
||||||
#include "utility.h" // UniqueHandle, CriticalSection
|
|
||||||
|
|
||||||
class TextBuffer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
TextBuffer(HWND edit);
|
|
||||||
~TextBuffer();
|
|
||||||
void Flush();
|
|
||||||
void AddText(std::wstring text, bool line);
|
|
||||||
void ClearBuffer();
|
|
||||||
bool Running() { return running; }
|
|
||||||
private:
|
|
||||||
CriticalSection cs;
|
|
||||||
bool line_break, running;
|
|
||||||
UniqueHandle hThread;
|
|
||||||
HWND hEdit;
|
|
||||||
std::wstring str;
|
|
||||||
};
|
|
@ -1,55 +0,0 @@
|
|||||||
/* 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 "ITH.h"
|
|
||||||
#include "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;
|
|
||||||
|
|
||||||
DWORD ProcessCommand(const std::wstring& cmd, DWORD pid)
|
|
||||||
{
|
|
||||||
using std::wregex;
|
|
||||||
using std::regex_match;
|
|
||||||
std::match_results<std::wstring::const_iterator> m;
|
|
||||||
|
|
||||||
if (regex_match(cmd, m, wregex(L"/p(\\d+)", wregex::icase)))
|
|
||||||
{
|
|
||||||
pid = std::stoul(m[1].str());
|
|
||||||
InjectProcessById(pid);
|
|
||||||
}
|
|
||||||
else if (regex_match(cmd, m, wregex(L"/h(.+)", wregex::icase)))
|
|
||||||
{
|
|
||||||
HookParam hp = {};
|
|
||||||
if (Parse(m[1].str(), hp))
|
|
||||||
InsertHook(pid, &hp);
|
|
||||||
}
|
|
||||||
else if (regex_match(cmd, m, wregex(L":(?:h|help)", wregex::icase)))
|
|
||||||
{
|
|
||||||
ConsoleOutput(Usage);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ConsoleOutput(L"Unknown command. Type :h or :help for help.");
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
BIN
gui/icon1.ico
BIN
gui/icon1.ico
Binary file not shown.
Before Width: | Height: | Size: 766 B |
128
gui/language.cpp
128
gui/language.cpp
@ -1,128 +0,0 @@
|
|||||||
/* 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/>.
|
|
||||||
*/
|
|
||||||
const wchar_t* Warning=L"Warning!";
|
|
||||||
//command.cpp
|
|
||||||
const wchar_t* ErrorSyntax=L"Syntax error";
|
|
||||||
const wchar_t* Usage = L"Syntax:\r\n\
|
|
||||||
\r\n\
|
|
||||||
:H[ELP] - print help\r\n\
|
|
||||||
Loader options:\r\n\
|
|
||||||
/P[{process_id|Nprocess_name}] - attach to process\r\n\
|
|
||||||
\r\n\
|
|
||||||
Hook options:\r\n\
|
|
||||||
/H[X]{A|B|W|S|Q}[N][data_offset[*drdo]][:sub_offset[*drso]]@addr[:module[:{name|#ordinal}]]\r\n\
|
|
||||||
\r\n\
|
|
||||||
All numbers in /H (except ordinal) are hexadecimal without any prefixes";
|
|
||||||
|
|
||||||
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\
|
|
||||||
Set additional custom hook\r\n\
|
|
||||||
\r\n\
|
|
||||||
Hook types :\r\n\
|
|
||||||
A - DBCS char\r\n\
|
|
||||||
B - DBCS char(big-endian)\r\n\
|
|
||||||
W - UCS2 char\r\n\
|
|
||||||
S - MBCS string\r\n\
|
|
||||||
Q - UTF-16 string\r\n\
|
|
||||||
\r\n\
|
|
||||||
Parameters:\r\n\
|
|
||||||
X - use hardware breakpoints\r\n\
|
|
||||||
N - don't use contexts\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 - address of the hook\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\
|
|
||||||
\r\n\
|
|
||||||
Negative values of 'data_offset' and 'sub_offset' refer to registers: \r\n\
|
|
||||||
- 4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI\r\n\
|
|
||||||
\r\n\
|
|
||||||
\"Add a level of indirection\" means in C/C++ style: (*(ESP+data_offset)+drdo) instead of (ESP+data_offset)\r\n\
|
|
||||||
\r\n\
|
|
||||||
All numbers except ordinal are hexadecimal without any prefixes";
|
|
||||||
|
|
||||||
//inject.cpp
|
|
||||||
const wchar_t* ErrorRemoteThread=L"Can't create remote thread.";
|
|
||||||
const wchar_t* ErrorOpenProcess=L"Can't open process.";
|
|
||||||
const wchar_t* ErrorNoProcess=L"Process not found";
|
|
||||||
const wchar_t* SelfAttach=L"Please do not attach to ITH.exe";
|
|
||||||
const wchar_t* AlreadyAttach=L"Process already attached.";
|
|
||||||
const wchar_t* FormatInject=L"Inject process %d. Module base %.8X";
|
|
||||||
//main.cpp
|
|
||||||
const wchar_t* NotAdmin=L"Can't enable SeDebugPrevilege. ITH might malfunction.\r\n\
|
|
||||||
Please run ITH as administrator or turn off UAC.";
|
|
||||||
//pipe.cpp
|
|
||||||
const wchar_t* ErrorCreatePipe=L"Can't create text pipe or too many instance.";
|
|
||||||
const wchar_t* FormatDetach=L"Process %d detached.";
|
|
||||||
const wchar_t* ErrorCmdQueueFull=L"Command queue full.";
|
|
||||||
const wchar_t* ErrorNoAttach=L"No process attached.";
|
|
||||||
|
|
||||||
//profile.cpp
|
|
||||||
const wchar_t* ErrorMonitor=L"Can't monitor process.";
|
|
||||||
//utility.cpp
|
|
||||||
const wchar_t* InitMessage=L"Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com)\r\n\
|
|
||||||
Copyright (C) 2015 Stomp (zorkzero@hotmail.com)\r\n\
|
|
||||||
Copyright (C) 2016-2017 mireado (mireado@daum.net)\r\n\
|
|
||||||
Copyright (C) 2018 Artikash (akashmozumdar@gmail.com)\r\n\
|
|
||||||
Source code available at github.com/Artikash/NextHooker (GPLv3 License)";
|
|
||||||
const wchar_t* BackgroundMsg=L"Type \":h\" or \":help\" for help.";
|
|
||||||
const wchar_t* ErrorLinkExist=L"Link exist.";
|
|
||||||
const wchar_t* ErrorCylicLink=L"Link failed. No cyclic link allowed.";
|
|
||||||
const wchar_t* FormatLink=L"Link from thread%.4x to thread%.4x.";
|
|
||||||
const wchar_t* ErrorLink=L"Link failed. Source or/and destination thread not found.";
|
|
||||||
const wchar_t* ErrorDeleteCombo=L"Error delete from combo.";
|
|
||||||
|
|
||||||
//window.cpp
|
|
||||||
const wchar_t* ClassName=L"NextHooker";
|
|
||||||
const wchar_t* ClassNameAdmin=L"NextHooker (Administrator)";
|
|
||||||
const wchar_t* ErrorNotSplit=L"Need to enable split first!";
|
|
||||||
const wchar_t* ErrorNotModule=L"Need to enable module first!";
|
|
||||||
//Main window buttons
|
|
||||||
const wchar_t* ButtonTitleProcess=L"Process";
|
|
||||||
const wchar_t* ButtonTitleThread=L"Thread";
|
|
||||||
const wchar_t* ButtonTitleHook=L"Hook";
|
|
||||||
const wchar_t* ButtonTitleProfile=L"Profile";
|
|
||||||
const wchar_t* ButtonTitleOption=L"Option";
|
|
||||||
const wchar_t* ButtonTitleClear=L"Clear";
|
|
||||||
const wchar_t* ButtonTitleSave=L"Save";
|
|
||||||
const wchar_t* ButtonTitleTop=L"Top";
|
|
||||||
//Hook window
|
|
||||||
const wchar_t* SpecialHook=L"Special hook, no AGTH equivalent.";
|
|
||||||
//Process window
|
|
||||||
const wchar_t* TabTitlePID=L"PID";
|
|
||||||
const wchar_t* TabTitleMemory=L"Memory";
|
|
||||||
const wchar_t* TabTitleName=L"Name";
|
|
||||||
const wchar_t* TabTitleTID=L"TID";
|
|
||||||
const wchar_t* TabTitleStart=L"Start";
|
|
||||||
const wchar_t* TabTitleModule=L"Module";
|
|
||||||
const wchar_t* TabTitleState=L"State";
|
|
||||||
const wchar_t* SuccessAttach=L"Attach ITH to process successfully.";
|
|
||||||
const wchar_t* FailAttach=L"Failed to attach ITH to process.";
|
|
||||||
const wchar_t* SuccessDetach=L"ITH detach from process.";
|
|
||||||
const wchar_t* FailDetach=L"Detach failed.";
|
|
||||||
//Profile window
|
|
||||||
const wchar_t* ProfileExist=L"Profile already exists.";
|
|
||||||
const wchar_t* SuccessAddProfile=L"Profile added.";
|
|
||||||
const wchar_t* FailAddProfile=L"Fail to add profile";
|
|
||||||
const wchar_t* TabTitleNumber=L"No.";
|
|
||||||
const wchar_t* NoFile=L"Can't find file.";
|
|
||||||
const wchar_t* PathDismatch=L"Process name dismatch, continue?";
|
|
||||||
const wchar_t* SuccessImportProfile=L"Import profile success";
|
|
||||||
//const wchar_t* SuccessAddProfile=L"Profile added.";
|
|
@ -1,86 +0,0 @@
|
|||||||
/* 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
|
|
||||||
|
|
||||||
extern const wchar_t* Warning;
|
|
||||||
//command.cpp
|
|
||||||
extern const wchar_t* ErrorSyntax;
|
|
||||||
extern const wchar_t* Usage;
|
|
||||||
extern const wchar_t* ExtendedUsage;
|
|
||||||
//inject.cpp
|
|
||||||
extern const wchar_t* ErrorRemoteThread;
|
|
||||||
extern const wchar_t* ErrorOpenProcess;
|
|
||||||
extern const wchar_t* ErrorNoProcess;
|
|
||||||
extern const wchar_t* SelfAttach;
|
|
||||||
extern const wchar_t* AlreadyAttach;
|
|
||||||
extern const wchar_t* FormatInject;
|
|
||||||
//main.cpp
|
|
||||||
extern const wchar_t* NotAdmin;
|
|
||||||
//pipe.cpp
|
|
||||||
extern const wchar_t* ErrorCreatePipe;
|
|
||||||
extern const wchar_t* FormatDetach;
|
|
||||||
extern const wchar_t* ErrorCmdQueueFull;
|
|
||||||
extern const wchar_t* ErrorNoAttach;
|
|
||||||
|
|
||||||
//profile.cpp
|
|
||||||
extern const wchar_t* ErrorMonitor;
|
|
||||||
|
|
||||||
//utility.cpp
|
|
||||||
extern const wchar_t* InitMessage;
|
|
||||||
extern const wchar_t* BackgroundMsg;
|
|
||||||
extern const wchar_t* ErrorLinkExist;
|
|
||||||
extern const wchar_t* ErrorCylicLink;
|
|
||||||
extern const wchar_t* FormatLink;
|
|
||||||
extern const wchar_t* ErrorLink;
|
|
||||||
extern const wchar_t* ErrorDeleteCombo;
|
|
||||||
|
|
||||||
//window.cpp
|
|
||||||
extern const wchar_t* ClassName;
|
|
||||||
extern const wchar_t* ClassNameAdmin;
|
|
||||||
extern const wchar_t* ErrorNotSplit;
|
|
||||||
extern const wchar_t* ErrorNotModule;
|
|
||||||
//Main window buttons
|
|
||||||
extern const wchar_t* ButtonTitleProcess;
|
|
||||||
extern const wchar_t* ButtonTitleThread;
|
|
||||||
extern const wchar_t* ButtonTitleHook;
|
|
||||||
extern const wchar_t* ButtonTitleProfile;
|
|
||||||
extern const wchar_t* ButtonTitleOption;
|
|
||||||
extern const wchar_t* ButtonTitleClear;
|
|
||||||
extern const wchar_t* ButtonTitleSave;
|
|
||||||
extern const wchar_t* ButtonTitleTop;
|
|
||||||
//Hook window
|
|
||||||
extern const wchar_t* SpecialHook;
|
|
||||||
//Process window
|
|
||||||
extern const wchar_t* TabTitlePID;
|
|
||||||
extern const wchar_t* TabTitleMemory;
|
|
||||||
extern const wchar_t* TabTitleName;
|
|
||||||
extern const wchar_t* TabTitleTID;
|
|
||||||
extern const wchar_t* TabTitleStart;
|
|
||||||
extern const wchar_t* TabTitleModule;
|
|
||||||
extern const wchar_t* TabTitleState;
|
|
||||||
extern const wchar_t* SuccessAttach;
|
|
||||||
extern const wchar_t* FailAttach;
|
|
||||||
extern const wchar_t* SuccessDetach;
|
|
||||||
extern const wchar_t* FailDetach;
|
|
||||||
//Profile window
|
|
||||||
extern const wchar_t* ProfileExist;
|
|
||||||
extern const wchar_t* SuccessAddProfile;
|
|
||||||
extern const wchar_t* FailAddProfile;
|
|
||||||
extern const wchar_t* TabTitleNumber;
|
|
||||||
extern const wchar_t* NoFile;
|
|
||||||
extern const wchar_t* PathDismatch;
|
|
||||||
extern const wchar_t* SuccessImportProfile;
|
|
176
gui/main.cpp
176
gui/main.cpp
@ -1,176 +0,0 @@
|
|||||||
/* 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 "ITH.h"
|
|
||||||
#include "host.h"
|
|
||||||
#include "hookman.h"
|
|
||||||
#include "profile/Profile.h"
|
|
||||||
#include "ProfileManager.h"
|
|
||||||
|
|
||||||
HINSTANCE hIns;
|
|
||||||
ATOM MyRegisterClass(HINSTANCE hInstance);
|
|
||||||
BOOL InitInstance(HINSTANCE hInstance, DWORD nCmdShow, RECT *rc);
|
|
||||||
RECT window;
|
|
||||||
extern HWND hMainWnd; // windows.cpp
|
|
||||||
extern ProfileManager* pfman; // ProfileManager.cpp
|
|
||||||
|
|
||||||
HookManager* man;
|
|
||||||
LONG split_time;
|
|
||||||
|
|
||||||
std::map<std::wstring, long> setting;
|
|
||||||
|
|
||||||
void SaveSettings()
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
|
|
||||||
UniqueHandle hFile(IthCreateFile(L"NextHooker.xml", GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS));
|
|
||||||
if (hFile.get() != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
FileWriter fw(hFile.get());
|
|
||||||
pugi::xml_document doc;
|
|
||||||
auto root = doc.root().append_child(L"NextHookerSetting");
|
|
||||||
for (auto it = setting.begin(); it != setting.end(); ++it)
|
|
||||||
root.append_attribute(it->first.c_str()).set_value(it->second);
|
|
||||||
doc.save(fw);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DefaultSettings()
|
|
||||||
{
|
|
||||||
setting[L"split_time"] = 200;
|
|
||||||
setting[L"window_left"] = 100;
|
|
||||||
setting[L"window_right"] = 800;
|
|
||||||
setting[L"window_top"] = 100;
|
|
||||||
setting[L"window_bottom"] = 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
void InitializeSettings()
|
|
||||||
{
|
|
||||||
split_time = setting[L"split_time"];
|
|
||||||
window.left = setting[L"window_left"];
|
|
||||||
window.right = setting[L"window_right"];
|
|
||||||
window.top = setting[L"window_top"];
|
|
||||||
window.bottom = setting[L"window_bottom"];
|
|
||||||
|
|
||||||
if (window.right < window.left || window.right - window.left < 600)
|
|
||||||
window.right = window.left + 600;
|
|
||||||
if (window.bottom < window.top || window.bottom - window.top < 200)
|
|
||||||
window.bottom = window.top + 200;
|
|
||||||
}
|
|
||||||
|
|
||||||
void LoadSettings()
|
|
||||||
{
|
|
||||||
UniqueHandle hFile(IthCreateFile(L"NextHooker.xml", GENERIC_READ, FILE_SHARE_READ, OPEN_EXISTING));
|
|
||||||
if (hFile.get() != INVALID_HANDLE_VALUE)
|
|
||||||
{
|
|
||||||
DWORD size = GetFileSize(hFile.get(), NULL);
|
|
||||||
std::unique_ptr<char[]> buffer(new char[size]);
|
|
||||||
ReadFile(hFile.get(), buffer.get(), size, &size, NULL);
|
|
||||||
pugi::xml_document doc;
|
|
||||||
auto result = doc.load_buffer_inplace(buffer.get(), size);
|
|
||||||
if (!result)
|
|
||||||
return;
|
|
||||||
auto root = doc.root().child(L"NextHookerSetting");
|
|
||||||
for (auto attr = root.attributes_begin(); attr != root.attributes_end(); ++attr)
|
|
||||||
{
|
|
||||||
auto it = setting.find(attr->name());
|
|
||||||
if (it != setting.end())
|
|
||||||
it->second = std::stoul(attr->value());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern LPCWSTR ClassName, ClassNameAdmin;
|
|
||||||
static WCHAR mutex[] = L"ITH_RUNNING";
|
|
||||||
DWORD FindITH()
|
|
||||||
{
|
|
||||||
HWND hwnd = FindWindow(ClassName, ClassName);
|
|
||||||
if (hwnd == NULL)
|
|
||||||
hwnd = FindWindow(ClassName, ClassNameAdmin);
|
|
||||||
if (hwnd)
|
|
||||||
{
|
|
||||||
ShowWindow(hwnd, SW_SHOWNORMAL);
|
|
||||||
SetForegroundWindow(hwnd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
LONG WINAPI UnhandledExcept(_EXCEPTION_POINTERS *ExceptionInfo)
|
|
||||||
{
|
|
||||||
wchar_t path_name[512]; // fully qualified path name
|
|
||||||
WCHAR code[16];
|
|
||||||
EXCEPTION_RECORD* rec = ExceptionInfo->ExceptionRecord;
|
|
||||||
std::swprintf(code, L"%08X", rec->ExceptionCode);
|
|
||||||
MEMORY_BASIC_INFORMATION info;
|
|
||||||
if (VirtualQuery(rec->ExceptionAddress, &info, sizeof(info)))
|
|
||||||
{
|
|
||||||
if (GetModuleFileName((HMODULE)info.AllocationBase, path_name, 512))
|
|
||||||
{
|
|
||||||
LPWSTR name = wcsrchr(path_name, L'\\');
|
|
||||||
if (name)
|
|
||||||
{
|
|
||||||
DWORD addr = (DWORD)rec->ExceptionAddress;
|
|
||||||
std::swprintf(name, L"%s:%08X", name + 1, addr - (DWORD)info.AllocationBase);
|
|
||||||
MessageBox(NULL, name, code, MB_OK);
|
|
||||||
TerminateProcess(GetCurrentProcess(), 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std::swprintf(path_name, L"%08X", rec->ExceptionAddress);
|
|
||||||
MessageBox(NULL, path_name, code, MB_OK);
|
|
||||||
TerminateProcess(GetCurrentProcess(), 0);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
|
|
||||||
{
|
|
||||||
InitCommonControls();
|
|
||||||
if (StartHost())
|
|
||||||
{
|
|
||||||
SetUnhandledExceptionFilter(UnhandledExcept);
|
|
||||||
GetHostHookManager(&man);
|
|
||||||
pfman = new ProfileManager();
|
|
||||||
DefaultSettings();
|
|
||||||
LoadSettings();
|
|
||||||
InitializeSettings();
|
|
||||||
man->SetSplitInterval(split_time);
|
|
||||||
hIns = hInstance;
|
|
||||||
MyRegisterClass(hIns);
|
|
||||||
InitInstance(hIns, FALSE, &window);
|
|
||||||
MSG msg;
|
|
||||||
while (GetMessage(&msg, NULL, 0, 0))
|
|
||||||
{
|
|
||||||
TranslateMessage(&msg);
|
|
||||||
DispatchMessage(&msg);
|
|
||||||
}
|
|
||||||
delete pfman;
|
|
||||||
man = NULL;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FindITH();
|
|
||||||
}
|
|
||||||
CloseHost();
|
|
||||||
TerminateProcess(GetCurrentProcess(), 0);
|
|
||||||
}
|
|
@ -1,293 +0,0 @@
|
|||||||
/* 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.h"
|
|
||||||
#include "hookman.h"
|
|
||||||
#include "vnrhook/include/types.h"
|
|
||||||
#include "vnrhook/include/const.h"
|
|
||||||
#include "Profile.h"
|
|
||||||
#include "misc.h"
|
|
||||||
#include <algorithm>
|
|
||||||
#include "pugixml.h"
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
@ -1,171 +0,0 @@
|
|||||||
/* 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;
|
|
||||||
};
|
|
@ -1,275 +0,0 @@
|
|||||||
#include "misc.h"
|
|
||||||
#include <regex>
|
|
||||||
#include <memory>
|
|
||||||
#include "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"([^:]+)"), name(L"[^:[:space:]]+"), 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;
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
#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();
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
/**
|
|
||||||
* 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
gui/profile/pugixml.cpp
11554
gui/profile/pugixml.cpp
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,14 +0,0 @@
|
|||||||
#ifndef IDC_STATIC
|
|
||||||
#define IDC_STATIC (-1)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define IDD_DIALOG2 102
|
|
||||||
#define IDD_DIALOG4 104
|
|
||||||
#define IDI_ICON1 110
|
|
||||||
#define IDC_EDIT1 1011
|
|
||||||
#define IDC_BUTTON1 1020
|
|
||||||
#define IDC_BUTTON2 1021
|
|
||||||
#define IDC_BUTTON3 1022
|
|
||||||
#define IDC_BUTTON5 1024
|
|
||||||
#define IDC_LIST1 1028
|
|
||||||
#define IDC_BUTTON6 40000
|
|
302
gui/utility.cpp
302
gui/utility.cpp
@ -1,302 +0,0 @@
|
|||||||
/* 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 "utility.h"
|
|
||||||
#include "host.h"
|
|
||||||
#include "hookman.h"
|
|
||||||
#include "vnrhook/include/types.h"
|
|
||||||
#include "vnrhook/include/const.h"
|
|
||||||
#include "profile/misc.h"
|
|
||||||
|
|
||||||
extern HookManager* man; // main.cpp
|
|
||||||
|
|
||||||
std::wstring GetDriveLetter(const std::wstring& devicePath);
|
|
||||||
std::wstring GetWindowsPath(const std::wstring& fileObjectPath);
|
|
||||||
PVOID GetAllocationBase(DWORD pid, LPCVOID);
|
|
||||||
std::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase);
|
|
||||||
std::wstring GetModuleFileNameAsString();
|
|
||||||
std::wstring GetProcessPath(HANDLE hProc);
|
|
||||||
|
|
||||||
void ConsoleOutput(LPCWSTR text)
|
|
||||||
{
|
|
||||||
man->AddConsoleOutput(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetProcessPath(DWORD pid)
|
|
||||||
{
|
|
||||||
UniqueHandle hProc(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
|
|
||||||
if (hProc)
|
|
||||||
return GetProcessPath(hProc.get());
|
|
||||||
else
|
|
||||||
return L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetProcessPath(HANDLE hProc)
|
|
||||||
{
|
|
||||||
wchar_t path[MAX_PATH];
|
|
||||||
GetProcessImageFileName(hProc, path, MAX_PATH);
|
|
||||||
return GetWindowsPath(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetWindowsPath(const std::wstring& path)
|
|
||||||
{
|
|
||||||
// path is in device form
|
|
||||||
// \Device\HarddiskVolume2\Windows\System32\taskhost.exe
|
|
||||||
auto pathOffset = path.find(L'\\', 1) + 1;
|
|
||||||
pathOffset = path.find(L'\\', pathOffset);
|
|
||||||
std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2
|
|
||||||
std::wstring dosDrive = GetDriveLetter(devicePath); // C:
|
|
||||||
if (dosDrive.empty())
|
|
||||||
return path;
|
|
||||||
std::wstring dosPath = dosDrive; // C:
|
|
||||||
dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe
|
|
||||||
return dosPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetDriveLetter(const std::wstring& devicePath)
|
|
||||||
{
|
|
||||||
for (wchar_t drive = L'A'; drive <= L'Z'; drive++)
|
|
||||||
{
|
|
||||||
wchar_t szDriveName[3] = { drive, L':', L'\0' };
|
|
||||||
wchar_t szTarget[512];
|
|
||||||
if (QueryDosDevice(szDriveName, szTarget, 512))
|
|
||||||
if (devicePath.compare(szTarget) == 0)
|
|
||||||
return szDriveName;
|
|
||||||
}
|
|
||||||
return L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetCode(const HookParam& hp, DWORD pid)
|
|
||||||
{
|
|
||||||
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 (pid)
|
|
||||||
{
|
|
||||||
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.address - (DWORD)allocationBase;
|
|
||||||
code += L"@" + ToHexString(relativeHookAddress) + L":" + fileName;
|
|
||||||
return code;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase)
|
|
||||||
{
|
|
||||||
UniqueHandle hProc(OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid));
|
|
||||||
if (hProc.get())
|
|
||||||
{
|
|
||||||
WCHAR path[MAX_PATH];
|
|
||||||
if (GetModuleFileNameEx(hProc.get(), (HMODULE)allocationBase, path, MAX_PATH))
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
return L"";
|
|
||||||
}
|
|
||||||
|
|
||||||
PVOID GetAllocationBase(DWORD pid, LPCVOID addr)
|
|
||||||
{
|
|
||||||
UniqueHandle hProc(OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid));
|
|
||||||
if (hProc.get())
|
|
||||||
{
|
|
||||||
MEMORY_BASIC_INFORMATION info;
|
|
||||||
if (VirtualQueryEx(hProc.get(), addr, &info, sizeof(info)))
|
|
||||||
{
|
|
||||||
if (info.Type & MEM_IMAGE)
|
|
||||||
return info.AllocationBase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TitleParam
|
|
||||||
{
|
|
||||||
DWORD pid, buffer_len, retn_len;
|
|
||||||
std::wstring buffer;
|
|
||||||
};
|
|
||||||
|
|
||||||
BOOL CALLBACK EnumProc(HWND hwnd, LPARAM lParam)
|
|
||||||
{
|
|
||||||
TitleParam* p = (TitleParam*)lParam;
|
|
||||||
DWORD pid;
|
|
||||||
GetWindowThreadProcessId(hwnd, &pid);
|
|
||||||
if (pid == p->pid)
|
|
||||||
{
|
|
||||||
if (GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE)
|
|
||||||
{
|
|
||||||
int len = GetWindowTextLength(hwnd);
|
|
||||||
std::unique_ptr<wchar_t[]> result(new wchar_t[len + 1]);
|
|
||||||
GetWindowText(hwnd, result.get(), len + 1);
|
|
||||||
p->buffer = result.get();
|
|
||||||
p->retn_len = p->buffer.size();
|
|
||||||
if (!p->buffer.empty())
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetProcessTitle(DWORD pid)
|
|
||||||
{
|
|
||||||
TitleParam p;
|
|
||||||
p.pid = pid;
|
|
||||||
p.buffer_len = 0;
|
|
||||||
p.retn_len = 0;
|
|
||||||
EnumWindows(EnumProc, (LPARAM)&p);
|
|
||||||
return p.buffer;
|
|
||||||
}
|
|
||||||
|
|
||||||
WindowsError::WindowsError(DWORD error_code) : error_code(error_code), msg("")
|
|
||||||
{
|
|
||||||
CHAR str[512];
|
|
||||||
std::sprintf(str, "error code 0x%8x", error_code);
|
|
||||||
msg = str;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char *WindowsError::what() const
|
|
||||||
{
|
|
||||||
return msg.c_str();
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE IthCreateThread(LPVOID start_addr, DWORD param)
|
|
||||||
{
|
|
||||||
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_addr, (LPVOID)param, 0, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetModuleFileNameAsString()
|
|
||||||
{
|
|
||||||
WCHAR path[MAX_PATH];
|
|
||||||
GetModuleFileName(NULL, path, MAX_PATH);
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IthCreateDirectory(LPCWSTR name)
|
|
||||||
{
|
|
||||||
std::wstring path = GetModuleFileNameAsString();
|
|
||||||
path = path.substr(0, path.rfind(L'\\') + 1) + name;
|
|
||||||
BOOL error_code = CreateDirectory(path.c_str(), NULL);
|
|
||||||
return error_code != 0 || GetLastError() == ERROR_ALREADY_EXISTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition)
|
|
||||||
{
|
|
||||||
std::wstring path = GetModuleFileNameAsString();
|
|
||||||
path = path.substr(0, path.rfind(L'\\') + 1) + name;
|
|
||||||
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.
|
|
||||||
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
|
|
||||||
// -1 if the string is null terminated.
|
|
||||||
int MB_WC_count(const char* mb, int mb_length)
|
|
||||||
{
|
|
||||||
return MultiByteToWideChar(932, 0, mb, mb_length, NULL, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unicode->SJIS. Analogous to MB_WC.
|
|
||||||
int WC_MB(const wchar_t *wc, char* mb, int mb_length)
|
|
||||||
{
|
|
||||||
return WideCharToMultiByte(932, 0, wc, -1, mb, mb_length, NULL, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring GetEntryString(TextThread* thread)
|
|
||||||
{
|
|
||||||
ThreadParameter tp = thread->GetThreadParameter();
|
|
||||||
std::wstring buffer;
|
|
||||||
buffer.resize(200);
|
|
||||||
buffer.resize(swprintf(&buffer[0], L"%.4X:%.4d:0x%08X:0x%08X:0x%08X:", thread->Number(), tp.pid, tp.hook, tp.retn, tp.spl));
|
|
||||||
buffer += man->GetHookName(tp.pid, tp.hook);
|
|
||||||
return buffer;
|
|
||||||
}
|
|
107
gui/utility.h
107
gui/utility.h
@ -1,107 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "ITH.h"
|
|
||||||
#include "textthread.h"
|
|
||||||
|
|
||||||
struct HookParam;
|
|
||||||
struct ProcessRecord;
|
|
||||||
|
|
||||||
DWORD ProcessCommand(const std::wstring& cmd, DWORD pid);
|
|
||||||
std::wstring GetProcessPath(DWORD pid);
|
|
||||||
void ConsoleOutput(LPCWSTR);
|
|
||||||
void ConsoleOutput(LPCSTR text);
|
|
||||||
std::wstring GetProcessTitle(DWORD pid);
|
|
||||||
std::wstring GetCode(const HookParam& hp, DWORD pid = 0);
|
|
||||||
|
|
||||||
// http://codesequoia.wordpress.com/2012/08/26/stdunique_ptr-for-windows-handles/
|
|
||||||
struct HandleDeleter
|
|
||||||
{
|
|
||||||
typedef HANDLE pointer;
|
|
||||||
void operator() (HANDLE h)
|
|
||||||
{
|
|
||||||
if (h != INVALID_HANDLE_VALUE) {
|
|
||||||
CloseHandle(h);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef std::unique_ptr<HANDLE, HandleDeleter> UniqueHandle;
|
|
||||||
|
|
||||||
class FileWriter : public pugi::xml_writer
|
|
||||||
{
|
|
||||||
HANDLE hFile;
|
|
||||||
public:
|
|
||||||
FileWriter(HANDLE hFile) : hFile(hFile) {};
|
|
||||||
~FileWriter() {};
|
|
||||||
|
|
||||||
virtual void write(const void* data, size_t size)
|
|
||||||
{
|
|
||||||
DWORD dwNumberOfBytesWritten;
|
|
||||||
WriteFile(hFile, data, size, &dwNumberOfBytesWritten, NULL);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class WindowsError : public std::exception
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
std::string msg;
|
|
||||||
DWORD error_code;
|
|
||||||
public:
|
|
||||||
WindowsError(DWORD error_code);
|
|
||||||
virtual const char *what() const;
|
|
||||||
};
|
|
||||||
|
|
||||||
HANDLE IthCreateThread(LPVOID start_addr, DWORD param);
|
|
||||||
bool IthCreateDirectory(LPCWSTR name);
|
|
||||||
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition);
|
|
||||||
int MB_WC(const char* mb, wchar_t* wc, int wc_length);
|
|
||||||
int MB_WC_count(const char* mb, int mb_length);
|
|
||||||
int WC_MB(const wchar_t *wc, char* mb, int mb_length);
|
|
||||||
std::wstring GetEntryString(TextThread * thread);
|
|
||||||
bool Parse(const std::wstring& cmd, HookParam& hp);
|
|
||||||
|
|
||||||
// http://jrdodds.blogs.com/blog/2004/08/raii_in_c.html
|
|
||||||
class CriticalSection
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CriticalSection()
|
|
||||||
{
|
|
||||||
::InitializeCriticalSection(&m_rep);
|
|
||||||
}
|
|
||||||
~CriticalSection()
|
|
||||||
{
|
|
||||||
::DeleteCriticalSection(&m_rep);
|
|
||||||
}
|
|
||||||
void Enter()
|
|
||||||
{
|
|
||||||
::EnterCriticalSection(&m_rep);
|
|
||||||
}
|
|
||||||
void Leave()
|
|
||||||
{
|
|
||||||
::LeaveCriticalSection(&m_rep);
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
CriticalSection(const CriticalSection&);
|
|
||||||
CriticalSection& operator=(const CriticalSection&);
|
|
||||||
|
|
||||||
CRITICAL_SECTION m_rep;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CSLock
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
CSLock(CriticalSection& a_section)
|
|
||||||
: m_section(a_section)
|
|
||||||
{
|
|
||||||
m_section.Enter();
|
|
||||||
}
|
|
||||||
~CSLock()
|
|
||||||
{
|
|
||||||
m_section.Leave();
|
|
||||||
}
|
|
||||||
private:
|
|
||||||
CSLock(const CSLock&);
|
|
||||||
CSLock& operator=(const CSLock&);
|
|
||||||
|
|
||||||
CriticalSection& m_section;
|
|
||||||
};
|
|
@ -1,2 +0,0 @@
|
|||||||
const wchar_t* build_date=L"@BUILD_DATE@";
|
|
||||||
const WCHAR program_version[] = L"@CPACK_PACKAGE_VERSION_MAJOR@.@CPACK_PACKAGE_VERSION_MINOR@.@CPACK_PACKAGE_VERSION_PATCH@";
|
|
646
gui/window.cpp
646
gui/window.cpp
@ -1,646 +0,0 @@
|
|||||||
/* 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 "window.h"
|
|
||||||
#include "ProcessWindow.h"
|
|
||||||
#include "resource.h"
|
|
||||||
#include "language.h"
|
|
||||||
#include "host.h"
|
|
||||||
#include "hookman.h"
|
|
||||||
#include "vnrhook/include/const.h"
|
|
||||||
#include "version.h"
|
|
||||||
#include "ProfileManager.h"
|
|
||||||
#include "profile/Profile.h"
|
|
||||||
#include "TextBuffer.h"
|
|
||||||
#include "profile/misc.h"
|
|
||||||
|
|
||||||
#define CMD_SIZE 512
|
|
||||||
|
|
||||||
static WNDPROC proc, proccmd, procChar;
|
|
||||||
static WCHAR last_cmd[CMD_SIZE];
|
|
||||||
extern HINSTANCE hIns; // main.cpp
|
|
||||||
|
|
||||||
HWND hMainWnd, hwndCombo, hwndProcessComboBox, hwndEdit, hwndCmd;
|
|
||||||
HWND hwndProcess;
|
|
||||||
HWND hwndOption, hwndTop, hwndClear, hwndSave, hwndExtensions, hwndRemoveHook;
|
|
||||||
HWND hProcDlg, hOptionDlg;
|
|
||||||
HBRUSH hWhiteBrush;
|
|
||||||
DWORD background;
|
|
||||||
ProcessWindow* pswnd;
|
|
||||||
TextBuffer* texts;
|
|
||||||
extern ProfileManager* pfman; // ProfileManager.cpp
|
|
||||||
extern HookManager* man; // main.cpp
|
|
||||||
#define COMMENT_BUFFER_LENGTH 512
|
|
||||||
static WCHAR comment_buffer[COMMENT_BUFFER_LENGTH];
|
|
||||||
|
|
||||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
||||||
void SaveSettings(); // main.cpp
|
|
||||||
extern LONG split_time, process_time, inject_delay, insert_delay,
|
|
||||||
auto_inject, auto_insert, clipboard_flag, cyclic_remove, global_filter; //main.cpp
|
|
||||||
static int last_select, last_edit;
|
|
||||||
|
|
||||||
ATOM MyRegisterClass(HINSTANCE hInstance)
|
|
||||||
{
|
|
||||||
WNDCLASSEX wcex;
|
|
||||||
wcex.cbSize = sizeof(WNDCLASSEX);
|
|
||||||
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
|
||||||
wcex.lpfnWndProc = WndProc;
|
|
||||||
wcex.cbClsExtra = 0;
|
|
||||||
wcex.cbWndExtra = 0;
|
|
||||||
wcex.hInstance = hInstance;
|
|
||||||
wcex.hIcon = NULL;
|
|
||||||
wcex.hCursor = NULL;
|
|
||||||
wcex.hbrBackground = GetStockBrush(WHITE_BRUSH);
|
|
||||||
wcex.lpszMenuName = NULL;
|
|
||||||
wcex.lpszClassName = ClassName;
|
|
||||||
wcex.hIconSm = LoadIcon(hInstance, (LPWSTR)IDI_ICON1);
|
|
||||||
return RegisterClassEx(&wcex);
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL InitInstance(HINSTANCE hInstance, DWORD nAdmin, RECT* rc)
|
|
||||||
{
|
|
||||||
hIns = hInstance;
|
|
||||||
LPCWSTR name = (nAdmin) ? ClassNameAdmin : ClassName;
|
|
||||||
hMainWnd = CreateWindow(ClassName, name, WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
|
|
||||||
rc->left, rc->top, rc->right - rc->left, rc->bottom - rc->top, NULL, NULL, hInstance, 0);
|
|
||||||
if (!hMainWnd)
|
|
||||||
return FALSE;
|
|
||||||
ShowWindow(hMainWnd, SW_SHOWNORMAL);
|
|
||||||
UpdateWindow(hMainWnd);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD SaveProcessProfile(TextThread* thread); // ProfileManager.cpp
|
|
||||||
|
|
||||||
BOOL CALLBACK OptionDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
switch (uMsg)
|
|
||||||
{
|
|
||||||
case WM_INITDIALOG:
|
|
||||||
{
|
|
||||||
SetWindowText(GetDlgItem(hDlg, IDC_EDIT1), std::to_wstring((long long)split_time).c_str());
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
case WM_COMMAND:
|
|
||||||
{
|
|
||||||
DWORD wmId = LOWORD(wParam);
|
|
||||||
DWORD wmEvent = HIWORD(wParam);
|
|
||||||
switch (wmId)
|
|
||||||
{
|
|
||||||
case IDOK:
|
|
||||||
{
|
|
||||||
WCHAR str[128];
|
|
||||||
GetWindowText(GetDlgItem(hDlg, IDC_EDIT1), str, 0x80);
|
|
||||||
DWORD st = std::stoul(str);
|
|
||||||
split_time = st > 100 ? st : 100;
|
|
||||||
man->SetSplitInterval(split_time);
|
|
||||||
}
|
|
||||||
case IDCANCEL:
|
|
||||||
EndDialog(hDlg, 0);
|
|
||||||
hOptionDlg = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL CALLBACK ProcessDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
switch (uMsg)
|
|
||||||
{
|
|
||||||
case WM_INITDIALOG:
|
|
||||||
{
|
|
||||||
pswnd = new ProcessWindow(hDlg);
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
case WM_COMMAND:
|
|
||||||
{
|
|
||||||
DWORD wmId, wmEvent;
|
|
||||||
wmId = LOWORD(wParam);
|
|
||||||
wmEvent = HIWORD(wParam);
|
|
||||||
switch (wmId)
|
|
||||||
{
|
|
||||||
case WM_DESTROY:
|
|
||||||
case IDOK:
|
|
||||||
EndDialog(hDlg, NULL);
|
|
||||||
hProcDlg = NULL;
|
|
||||||
delete pswnd;
|
|
||||||
pswnd = NULL;
|
|
||||||
break;
|
|
||||||
case IDC_BUTTON1:
|
|
||||||
pswnd->RefreshProcess();
|
|
||||||
break;
|
|
||||||
case IDC_BUTTON2:
|
|
||||||
pswnd->AttachProcess();
|
|
||||||
break;
|
|
||||||
case IDC_BUTTON3:
|
|
||||||
pswnd->DetachProcess();
|
|
||||||
break;
|
|
||||||
case IDC_BUTTON5:
|
|
||||||
pswnd->CreateProfileForSelectedProcess();
|
|
||||||
break;
|
|
||||||
case IDC_BUTTON6:
|
|
||||||
pswnd->DeleteProfileForSelectedProcess();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
|
|
||||||
case WM_NOTIFY:
|
|
||||||
{
|
|
||||||
LPNMHDR dr = (LPNMHDR)lParam;
|
|
||||||
switch (dr->code)
|
|
||||||
{
|
|
||||||
case LVN_ITEMCHANGED:
|
|
||||||
if (dr->idFrom == IDC_LIST1)
|
|
||||||
{
|
|
||||||
NMLISTVIEW *nmlv = (LPNMLISTVIEW)lParam;
|
|
||||||
if (nmlv->uNewState & LVIS_SELECTED)
|
|
||||||
pswnd->RefreshThread(nmlv->iItem);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return TRUE;
|
|
||||||
default:
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LRESULT CALLBACK EditProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case WM_CHAR: //Filter user input.
|
|
||||||
if (GetKeyState(VK_CONTROL) & 0x8000)
|
|
||||||
{
|
|
||||||
if (wParam == 1)
|
|
||||||
{
|
|
||||||
Edit_SetSel(hwndEdit, 0, -1);
|
|
||||||
SendMessage(hwndEdit, WM_COPY, 0, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
case WM_LBUTTONUP:
|
|
||||||
if (hwndEdit)
|
|
||||||
SendMessage(hwndEdit, WM_COPY, 0, 0);
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
return proc(hWnd, message, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LRESULT CALLBACK EditCmdProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case WM_KEYDOWN:
|
|
||||||
if (wParam == VK_UP)
|
|
||||||
{
|
|
||||||
SetWindowText(hWnd, last_cmd);
|
|
||||||
SetFocus(hWnd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WM_CHAR:
|
|
||||||
if (wParam == VK_RETURN)
|
|
||||||
{
|
|
||||||
DWORD s = 0, pid = 0;
|
|
||||||
WCHAR str[32];
|
|
||||||
if (GetWindowTextLength(hWnd) == 0)
|
|
||||||
break;
|
|
||||||
GetWindowText(hWnd, last_cmd, CMD_SIZE);
|
|
||||||
//IthBreak();
|
|
||||||
if (GetWindowText(hwndProcessComboBox, str, 32))
|
|
||||||
pid = std::stoul(str);
|
|
||||||
ProcessCommand(last_cmd, pid);
|
|
||||||
Edit_SetSel(hWnd, 0, -1);
|
|
||||||
Edit_ReplaceSel(hWnd, &s);
|
|
||||||
SetFocus(hWnd);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return CallWindowProc(proccmd, hWnd, message, wParam, lParam);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CreateButtons(HWND hWnd)
|
|
||||||
{
|
|
||||||
hwndProcess = CreateWindow(L"Button", L"Process", WS_CHILD | WS_VISIBLE,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
hwndOption = CreateWindow(L"Button", L"Option", WS_CHILD | WS_VISIBLE,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
hwndClear = CreateWindow(L"Button", L"Clear", WS_CHILD | WS_VISIBLE,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
hwndSave = CreateWindow(L"Button", L"Save", WS_CHILD | WS_VISIBLE,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
hwndExtensions = CreateWindow(L"Button", L"Extensions", WS_CHILD | WS_VISIBLE,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
hwndRemoveHook = CreateWindow(L"Button", L"Unhook", WS_CHILD | WS_VISIBLE,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
hwndTop = CreateWindow(L"Button", L"Top", WS_CHILD | WS_VISIBLE | BS_PUSHLIKE | BS_CHECKBOX,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
hwndProcessComboBox = CreateWindow(L"ComboBox", NULL,
|
|
||||||
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
|
|
||||||
CBS_SORT | WS_VSCROLL | WS_TABSTOP,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
hwndCmd = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL,
|
|
||||||
WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | ES_LEFT | ES_AUTOHSCROLL,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
hwndEdit = CreateWindowEx(WS_EX_CLIENTEDGE, L"Edit", NULL,
|
|
||||||
WS_CHILD | WS_VISIBLE | ES_NOHIDESEL | WS_VSCROLL |
|
|
||||||
ES_LEFT | ES_MULTILINE | ES_AUTOVSCROLL,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ClickButton(HWND hWnd, HWND h)
|
|
||||||
{
|
|
||||||
if (h == hwndProcess)
|
|
||||||
{
|
|
||||||
if (hProcDlg)
|
|
||||||
SetForegroundWindow(hProcDlg);
|
|
||||||
else
|
|
||||||
hProcDlg = CreateDialog(hIns, (LPWSTR)IDD_DIALOG2, 0, ProcessDlgProc);
|
|
||||||
}
|
|
||||||
else if (h == hwndOption)
|
|
||||||
{
|
|
||||||
if (hOptionDlg)
|
|
||||||
SetForegroundWindow(hOptionDlg);
|
|
||||||
else
|
|
||||||
hOptionDlg = CreateDialog(hIns, (LPWSTR)IDD_DIALOG4, 0, OptionDlgProc);
|
|
||||||
}
|
|
||||||
else if (h == hwndClear)
|
|
||||||
{
|
|
||||||
man->ClearCurrent();
|
|
||||||
}
|
|
||||||
else if (h == hwndTop)
|
|
||||||
{
|
|
||||||
if (Button_GetCheck(h) == BST_CHECKED)
|
|
||||||
{
|
|
||||||
Button_SetCheck(h, BST_UNCHECKED);
|
|
||||||
SetWindowPos(hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
|
||||||
if (hProcDlg)
|
|
||||||
SetWindowPos(hProcDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
|
||||||
if (hOptionDlg)
|
|
||||||
SetWindowPos(hOptionDlg, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Button_SetCheck(h, BST_CHECKED);
|
|
||||||
SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
|
||||||
if (hProcDlg)
|
|
||||||
SetWindowPos(hProcDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
|
||||||
if (hOptionDlg)
|
|
||||||
SetWindowPos(hOptionDlg, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (h == hwndSave)
|
|
||||||
{
|
|
||||||
WCHAR str[32];
|
|
||||||
if (GetWindowText(hwndCombo, str, 32))
|
|
||||||
{
|
|
||||||
TextThread* current = man->FindSingle(std::stoul(str, nullptr, 16));
|
|
||||||
SaveProcessProfile(current);
|
|
||||||
}
|
|
||||||
pfman->SaveProfiles();
|
|
||||||
}
|
|
||||||
else if (h == hwndExtensions)
|
|
||||||
{
|
|
||||||
man->AddConsoleOutput(L"GUI for managing your extensions coming soon. Please do it manually for now.");
|
|
||||||
}
|
|
||||||
else if (h == hwndRemoveHook)
|
|
||||||
{
|
|
||||||
WCHAR str[32];
|
|
||||||
if (GetWindowText(hwndCombo, str, 32))
|
|
||||||
{
|
|
||||||
std::wstring entry(str);
|
|
||||||
std::size_t i;
|
|
||||||
DWORD threadNumber = std::stoul(entry, &i, 16);
|
|
||||||
entry = entry.substr(i + 1);
|
|
||||||
DWORD pid = std::stoul(entry, &i);
|
|
||||||
entry = entry.substr(i + 1);
|
|
||||||
DWORD addr = std::stoul(entry, NULL, 16);
|
|
||||||
if (threadNumber != 0)
|
|
||||||
RemoveHook(pid, addr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring ThreadOutput(TextThread* thread, std::wstring output)
|
|
||||||
{
|
|
||||||
if (thread->Status() & CURRENT_SELECT) texts->AddText(output, false);
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GetHookParam(DWORD pid, DWORD hook_addr, HookParam& hp)
|
|
||||||
{
|
|
||||||
if (!pid)
|
|
||||||
return false;
|
|
||||||
hp = man->GetHookParam(pid, hook_addr);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring CreateEntryWithLink(ThreadParameter tp, std::wstring& entry)
|
|
||||||
{
|
|
||||||
std::wstring entryWithLink = entry;
|
|
||||||
if (tp.pid == 0)
|
|
||||||
entryWithLink += L"ConsoleOutput";
|
|
||||||
HookParam hp = {};
|
|
||||||
if (GetHookParam(tp.pid, tp.hook, hp))
|
|
||||||
entryWithLink += L" (" + GetCode(hp, tp.pid) + L")";
|
|
||||||
return entryWithLink;
|
|
||||||
}
|
|
||||||
|
|
||||||
void AddToCombo(TextThread& thread, bool replace)
|
|
||||||
{
|
|
||||||
std::wstring entry = GetEntryString(&thread);
|
|
||||||
std::wstring entryWithLink = CreateEntryWithLink(thread.GetThreadParameter(), entry);
|
|
||||||
int i = ComboBox_FindString(hwndCombo, -1, entry.c_str());
|
|
||||||
if (replace)
|
|
||||||
{
|
|
||||||
int sel = ComboBox_GetCurSel(hwndCombo);
|
|
||||||
if (i != CB_ERR)
|
|
||||||
ComboBox_DeleteString(hwndCombo, i);
|
|
||||||
ComboBox_AddString(hwndCombo, entryWithLink.c_str());
|
|
||||||
ComboBox_SetCurSel(hwndCombo, sel);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (i == CB_ERR)
|
|
||||||
ComboBox_AddString(hwndCombo, entryWithLink.c_str());
|
|
||||||
// Why set current selection to 0 when the new thread is selected?
|
|
||||||
if (thread.Status() & CURRENT_SELECT)
|
|
||||||
ComboBox_SetCurSel(hwndCombo, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadRemove(TextThread* thread)
|
|
||||||
{
|
|
||||||
std::wstring entry = GetEntryString(thread);
|
|
||||||
if (thread->GetThreadParameter().pid == 0)
|
|
||||||
entry += L"ConsoleOutput";
|
|
||||||
int i = ComboBox_FindString(hwndCombo, 0, entry.c_str());
|
|
||||||
if (i != CB_ERR)
|
|
||||||
{
|
|
||||||
if (ComboBox_DeleteString(hwndCombo, i) == CB_ERR)
|
|
||||||
ConsoleOutput(ErrorDeleteCombo);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetEditText(LPCWSTR wc)
|
|
||||||
{
|
|
||||||
DWORD line;
|
|
||||||
Edit_SetText(hwndEdit, wc);
|
|
||||||
line = Edit_GetLineCount(hwndEdit);
|
|
||||||
SendMessage(hwndEdit, EM_LINESCROLL, 0, line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadReset(TextThread* thread)
|
|
||||||
{
|
|
||||||
texts->ClearBuffer();
|
|
||||||
man->SetCurrent(thread);;
|
|
||||||
|
|
||||||
std::wstring text = thread->GetStore();
|
|
||||||
SetEditText(text.c_str());
|
|
||||||
|
|
||||||
WCHAR buffer[16];
|
|
||||||
std::swprintf(buffer, L"%04X", thread->Number());
|
|
||||||
DWORD tmp = ComboBox_FindString(hwndCombo, 0, buffer);
|
|
||||||
if (tmp != CB_ERR)
|
|
||||||
ComboBox_SetCurSel(hwndCombo, tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook);
|
|
||||||
|
|
||||||
void ThreadCreate(TextThread* thread)
|
|
||||||
{
|
|
||||||
thread->RegisterOutputCallBack(ThreadOutput);
|
|
||||||
//thread->RegisterFilterCallBack(ThreadFilter, 0);
|
|
||||||
AddToCombo(*thread, false);
|
|
||||||
auto tp = thread->GetThreadParameter();
|
|
||||||
auto hook = man->GetHookParam(tp.pid, tp.hook);
|
|
||||||
if (hook.type & USING_UNICODE) thread->Status() |= USING_UNICODE;
|
|
||||||
auto pf = pfman->GetProfile(tp.pid);
|
|
||||||
if (!pf)
|
|
||||||
return;
|
|
||||||
const std::wstring& hook_name = man->GetHookName(tp.pid, tp.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();
|
|
||||||
if (pf->IsThreadSelected(thread_profile))
|
|
||||||
ThreadReset(thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsUnicodeHook(const ProcessRecord& pr, DWORD hook)
|
|
||||||
{
|
|
||||||
bool res = false;
|
|
||||||
WaitForSingleObject(pr.hookman_mutex, 0);
|
|
||||||
auto hooks = (const Hook*)pr.hookman_map;
|
|
||||||
for (DWORD i = 0; i < MAX_HOOK; i++)
|
|
||||||
{
|
|
||||||
if (hooks[i].Address() == hook)
|
|
||||||
{
|
|
||||||
res = hooks[i].Type() & USING_UNICODE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ReleaseMutex(pr.hookman_mutex);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void RegisterProcess(DWORD pid)
|
|
||||||
{
|
|
||||||
auto path = GetProcessPath(pid);
|
|
||||||
if (!path.empty())
|
|
||||||
{
|
|
||||||
WCHAR str[MAX_PATH];
|
|
||||||
std::swprintf(str, L"%04d:%s", pid, path.substr(path.rfind(L'\\') + 1).c_str());
|
|
||||||
ComboBox_AddString(hwndProcessComboBox, str);
|
|
||||||
if (ComboBox_GetCount(hwndProcessComboBox) == 1)
|
|
||||||
ComboBox_SetCurSel(hwndProcessComboBox, 0);
|
|
||||||
}
|
|
||||||
Profile* pf = pfman->GetProfile(pid);
|
|
||||||
if (pf)
|
|
||||||
{
|
|
||||||
for (auto i = pf->Hooks().begin(); i != pf->Hooks().end(); ++i)
|
|
||||||
{
|
|
||||||
InsertHook(pid, &i->get()->HP(), toMultiByteString(i->get()->Name()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void RemoveProcessList(DWORD pid)
|
|
||||||
{
|
|
||||||
WCHAR str[MAX_PATH];
|
|
||||||
std::swprintf(str, L"%04d", pid);
|
|
||||||
DWORD i = ComboBox_FindString(hwndProcessComboBox, 0, str);
|
|
||||||
DWORD j = ComboBox_GetCurSel(hwndProcessComboBox);
|
|
||||||
if (i != CB_ERR)
|
|
||||||
{
|
|
||||||
DWORD k = ComboBox_DeleteString(hwndProcessComboBox, i);
|
|
||||||
if (i == j)
|
|
||||||
ComboBox_SetCurSel(hwndProcessComboBox, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case WM_CREATE:
|
|
||||||
CreateButtons(hWnd);
|
|
||||||
// Add text to the window.
|
|
||||||
Edit_LimitText(hwndEdit, -1);
|
|
||||||
SendMessage(hwndEdit, WM_INPUTLANGCHANGEREQUEST, 0, 0x411);
|
|
||||||
proc = (WNDPROC)SetWindowLong(hwndEdit, GWL_WNDPROC, (LONG)EditProc);
|
|
||||||
proccmd = (WNDPROC)SetWindowLong(hwndCmd, GWL_WNDPROC, (LONG)EditCmdProc);
|
|
||||||
hwndCombo = CreateWindow(L"ComboBox", NULL,
|
|
||||||
WS_CHILD | WS_VISIBLE | CBS_DROPDOWNLIST |
|
|
||||||
CBS_SORT | WS_VSCROLL | WS_TABSTOP,
|
|
||||||
0, 0, 0, 0, hWnd, 0, hIns, NULL);
|
|
||||||
{
|
|
||||||
HDC hDC = GetDC(hWnd);
|
|
||||||
int nHeight = -MulDiv(12, GetDeviceCaps(hDC, LOGPIXELSY), 72);
|
|
||||||
ReleaseDC(hWnd, hDC);
|
|
||||||
HFONT hf = CreateFont(nHeight, 0, 0, 0, FW_LIGHT, FALSE, FALSE, FALSE, SHIFTJIS_CHARSET,
|
|
||||||
OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
|
|
||||||
L"MS Gothic");
|
|
||||||
hWhiteBrush = GetStockBrush(WHITE_BRUSH);
|
|
||||||
SendMessage(hwndCmd, WM_SETFONT, (WPARAM)hf, 0);
|
|
||||||
SendMessage(hwndEdit, WM_SETFONT, (WPARAM)hf, 0);
|
|
||||||
SendMessage(hwndCombo, WM_SETFONT, (WPARAM)hf, 0);
|
|
||||||
SendMessage(hwndProcessComboBox, WM_SETFONT, (WPARAM)hf, 0);
|
|
||||||
texts = new TextBuffer(hwndEdit);
|
|
||||||
man->RegisterThreadCreateCallback(ThreadCreate);
|
|
||||||
man->RegisterThreadRemoveCallback(ThreadRemove);
|
|
||||||
man->RegisterThreadResetCallback(ThreadReset);
|
|
||||||
TextThread* console = man->FindSingle(0);
|
|
||||||
console->RegisterOutputCallBack(ThreadOutput);
|
|
||||||
AddToCombo(*console, false);
|
|
||||||
man->RegisterProcessAttachCallback(RegisterProcess);
|
|
||||||
man->RegisterProcessDetachCallback(RemoveProcessList);
|
|
||||||
OpenHost();
|
|
||||||
{
|
|
||||||
static const WCHAR program_name[] = L"NextHooker beta v";
|
|
||||||
//static const WCHAR program_version[] = L"3.0";
|
|
||||||
static WCHAR version_info[256];
|
|
||||||
std::swprintf(version_info, L"%s%s (%s)", program_name, program_version, build_date);
|
|
||||||
man->AddConsoleOutput(version_info);
|
|
||||||
man->AddConsoleOutput(InitMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (background == 0)
|
|
||||||
man->AddConsoleOutput(BackgroundMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
case WM_COMMAND:
|
|
||||||
{
|
|
||||||
DWORD wmId, wmEvent, dwId;
|
|
||||||
wmId = LOWORD(wParam);
|
|
||||||
wmEvent = HIWORD(wParam);
|
|
||||||
switch (wmEvent)
|
|
||||||
{
|
|
||||||
case EN_VSCROLL:
|
|
||||||
{
|
|
||||||
SCROLLBARINFO info = { sizeof(info) };
|
|
||||||
GetScrollBarInfo(hwndEdit, OBJID_VSCROLL, &info);
|
|
||||||
InvalidateRect(hwndEdit, 0, 1);
|
|
||||||
ValidateRect(hwndEdit, &info.rcScrollBar);
|
|
||||||
RedrawWindow(hwndEdit, 0, 0, RDW_ERASE);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case CBN_SELENDOK:
|
|
||||||
{
|
|
||||||
if ((HWND)lParam == hwndProcessComboBox)
|
|
||||||
return 0;
|
|
||||||
dwId = ComboBox_GetCurSel(hwndCombo);
|
|
||||||
int len = ComboBox_GetLBTextLen(hwndCombo, dwId);
|
|
||||||
if (len > 0)
|
|
||||||
{
|
|
||||||
LPWSTR pwcEntry = new WCHAR[len + 1];
|
|
||||||
len = ComboBox_GetLBText(hwndCombo, dwId, pwcEntry);
|
|
||||||
DWORD num = std::stoul(pwcEntry, NULL, 16);
|
|
||||||
man->SelectCurrent(num);
|
|
||||||
delete[] pwcEntry;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
case BN_CLICKED:
|
|
||||||
ClickButton(hWnd, (HWND)lParam);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WM_SETFOCUS:
|
|
||||||
SetFocus(hwndEdit);
|
|
||||||
return 0;
|
|
||||||
case WM_SIZE:
|
|
||||||
{
|
|
||||||
WORD width = LOWORD(lParam);
|
|
||||||
WORD height = HIWORD(lParam);
|
|
||||||
DWORD l = width / 7;
|
|
||||||
WORD h = HIWORD(GetDialogBaseUnits()); // height of the system font
|
|
||||||
h = h + (h / 2);
|
|
||||||
HDC hDC = GetDC(hWnd);
|
|
||||||
RECT rc;
|
|
||||||
GetClientRect(hWnd, &rc);
|
|
||||||
FillRect(hDC, &rc, hWhiteBrush);
|
|
||||||
ReleaseDC(hWnd, hDC);
|
|
||||||
MoveWindow(hwndProcess, 0, 0, l, h, TRUE);
|
|
||||||
MoveWindow(hwndOption, l * 1, 0, l, h, TRUE);
|
|
||||||
MoveWindow(hwndTop, l * 2, 0, l, h, TRUE);
|
|
||||||
MoveWindow(hwndClear, l * 3, 0, l, h, TRUE);
|
|
||||||
MoveWindow(hwndExtensions, l * 4, 0, l, h, TRUE);
|
|
||||||
MoveWindow(hwndRemoveHook, l * 5, 0, l, h, TRUE);
|
|
||||||
MoveWindow(hwndSave, l * 6, 0, width - 6 * l, h, TRUE);
|
|
||||||
l *= 2;
|
|
||||||
MoveWindow(hwndProcessComboBox, 0, h, l, 200, TRUE);
|
|
||||||
MoveWindow(hwndCmd, l, h, width - l, h, TRUE);
|
|
||||||
MoveWindow(hwndCombo, 0, h * 2, width, 200, TRUE);
|
|
||||||
h *= 3;
|
|
||||||
MoveWindow(hwndEdit, 0, h, width, height - h, TRUE);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
case WM_DESTROY:
|
|
||||||
man->RegisterThreadCreateCallback(0);
|
|
||||||
man->RegisterThreadRemoveCallback(0);
|
|
||||||
man->RegisterThreadResetCallback(0);
|
|
||||||
man->RegisterProcessAttachCallback(0);
|
|
||||||
man->RegisterProcessDetachCallback(0);
|
|
||||||
//delete texts;
|
|
||||||
SaveSettings();
|
|
||||||
PostQuitMessage(0);
|
|
||||||
return 0;
|
|
||||||
default:
|
|
||||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
20
gui/window.h
20
gui/window.h
@ -1,20 +0,0 @@
|
|||||||
/* 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 "ITH.h"
|
|
@ -1,48 +1,27 @@
|
|||||||
# 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
|
|
||||||
|
|
||||||
project(host)
|
project(host)
|
||||||
|
|
||||||
set(vnrhost_src
|
set(vnrhost_src
|
||||||
hookman.h
|
|
||||||
host.h
|
host.h
|
||||||
pipe.h
|
pipe.h
|
||||||
textthread.h
|
textthread.h
|
||||||
winmutex.h
|
winmutex.h
|
||||||
hookman.cc
|
|
||||||
host.cc
|
host.cc
|
||||||
pipe.cc
|
pipe.cc
|
||||||
textthread.cc
|
textthread.cc
|
||||||
)
|
)
|
||||||
|
|
||||||
include_directories(../)
|
|
||||||
|
|
||||||
add_library(vnrhost SHARED ${vnrhost_src})
|
add_library(vnrhost SHARED ${vnrhost_src})
|
||||||
|
|
||||||
set_target_properties(vnrhost PROPERTIES LINK_FLAGS /SUBSYSTEM:WINDOWS)
|
set_target_properties(vnrhost PROPERTIES LINK_FLAGS /SUBSYSTEM:WINDOWS)
|
||||||
|
|
||||||
target_compile_options(vnrhost PRIVATE
|
target_compile_options(vnrhost PRIVATE
|
||||||
# /GR-
|
# /GR-
|
||||||
$<$<CONFIG:MinSizeRel>:/MT>
|
$<$<CONFIG:Release>:/MT>
|
||||||
$<$<CONFIG:Debug>:>
|
$<$<CONFIG:Debug>:>
|
||||||
)
|
)
|
||||||
|
|
||||||
#STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
|
|
||||||
|
|
||||||
target_link_libraries(vnrhost)
|
|
||||||
|
|
||||||
target_compile_definitions(vnrhost
|
target_compile_definitions(vnrhost
|
||||||
PRIVATE
|
PRIVATE
|
||||||
ITH_HAS_CRT
|
ITH_HAS_CRT
|
||||||
_CRT_NON_CONFORMING_SWPRINTFS
|
_CRT_NON_CONFORMING_SWPRINTFS
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS vnrhost RUNTIME
|
|
||||||
DESTINATION .
|
|
||||||
CONFIGURATIONS Release
|
|
||||||
)
|
|
||||||
|
@ -1,199 +0,0 @@
|
|||||||
// hookman.cc
|
|
||||||
// 8/24/2013 jichi
|
|
||||||
// Branch IHF/HookManager.cpp, rev 133
|
|
||||||
// 8/24/2013 TODO: Clean up this file
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
|
||||||
# pragma warning (disable:4100) // C4100: unreference formal parameter
|
|
||||||
# pragma warning (disable:4146) // C4146: unary minus operator applied to unsigned type
|
|
||||||
#endif // _MSC_VER
|
|
||||||
|
|
||||||
#include "hookman.h"
|
|
||||||
#include "vnrhook/include/const.h"
|
|
||||||
#include "vnrhook/include/defs.h"
|
|
||||||
#include "vnrhook/include/types.h"
|
|
||||||
#include "winmutex.h"
|
|
||||||
#include <atlbase.h>
|
|
||||||
|
|
||||||
#define HM_LOCK CriticalSectionLocker hmLocker(hmCs) // Synchronized scope for accessing private data
|
|
||||||
|
|
||||||
HookManager::HookManager() :
|
|
||||||
create(nullptr),
|
|
||||||
remove(nullptr),
|
|
||||||
reset(nullptr),
|
|
||||||
attach(nullptr),
|
|
||||||
detach(nullptr),
|
|
||||||
nextThreadNumber(0),
|
|
||||||
splitDelay(250),
|
|
||||||
textThreadsByParams(),
|
|
||||||
processRecordsByIds()
|
|
||||||
{
|
|
||||||
InitializeCriticalSection(&hmCs);
|
|
||||||
|
|
||||||
// Console text thread
|
|
||||||
current = textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++, splitDelay);
|
|
||||||
current->Status() |= USING_UNICODE | CURRENT_SELECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
HookManager::~HookManager()
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
DeleteCriticalSection(&hmCs);
|
|
||||||
}
|
|
||||||
|
|
||||||
TextThread *HookManager::FindSingle(DWORD number)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
for (auto i : textThreadsByParams)
|
|
||||||
if (i.second->Number() == number)
|
|
||||||
return i.second;
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HookManager::SetCurrent(TextThread *it)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
if (it == nullptr) return;
|
|
||||||
current->Status() &= ~CURRENT_SELECT;
|
|
||||||
current = it;
|
|
||||||
it->Status() |= CURRENT_SELECT;
|
|
||||||
}
|
|
||||||
|
|
||||||
void HookManager::SelectCurrent(DWORD num)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
if (TextThread *st = FindSingle(num))
|
|
||||||
{
|
|
||||||
SetCurrent(st);
|
|
||||||
if (reset) reset(st);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HookManager::RemoveSingleHook(DWORD pid, DWORD addr)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
std::vector<ThreadParameter> removedThreads;
|
|
||||||
for (auto i : textThreadsByParams)
|
|
||||||
if (i.first.pid == pid && i.first.hook == addr)
|
|
||||||
{
|
|
||||||
if (remove) remove(i.second);
|
|
||||||
delete i.second;
|
|
||||||
removedThreads.push_back(i.first);
|
|
||||||
}
|
|
||||||
for (auto i : removedThreads) textThreadsByParams.erase(i);
|
|
||||||
SelectCurrent(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HookManager::RemoveProcessContext(DWORD pid)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
std::vector<ThreadParameter> removedThreads;
|
|
||||||
for (auto i : textThreadsByParams)
|
|
||||||
if (i.first.pid == pid)
|
|
||||||
{
|
|
||||||
if (remove) remove(i.second);
|
|
||||||
delete i.second;
|
|
||||||
removedThreads.push_back(i.first);
|
|
||||||
}
|
|
||||||
for (auto i : removedThreads) textThreadsByParams.erase(i);
|
|
||||||
SelectCurrent(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HookManager::RegisterProcess(DWORD pid, HANDLE hostPipe)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
ProcessRecord record;
|
|
||||||
record.hostPipe = hostPipe;
|
|
||||||
record.hookman_section = OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(pid)).c_str());
|
|
||||||
record.hookman_map = MapViewOfFile(record.hookman_section, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2); // jichi 1/16/2015: Changed to half to hook section size
|
|
||||||
record.process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
|
||||||
record.hookman_mutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(pid)).c_str());
|
|
||||||
processRecordsByIds[pid] = record;
|
|
||||||
if (attach) attach(pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HookManager::UnRegisterProcess(DWORD pid)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
ProcessRecord pr = processRecordsByIds[pid];
|
|
||||||
CloseHandle(pr.hookman_mutex);
|
|
||||||
UnmapViewOfFile(pr.hookman_map);
|
|
||||||
CloseHandle(pr.process_handle);
|
|
||||||
CloseHandle(pr.hookman_section);
|
|
||||||
processRecordsByIds.erase(pid);
|
|
||||||
RemoveProcessContext(pid);
|
|
||||||
if (detach) detach(pid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HookManager::DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD spl, const BYTE *text, int len)
|
|
||||||
{
|
|
||||||
// jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need
|
|
||||||
if (!text || !pid || len <= 0)
|
|
||||||
return;
|
|
||||||
HM_LOCK;
|
|
||||||
ThreadParameter tp = { pid, hook, retn, spl };
|
|
||||||
TextThread *it;
|
|
||||||
if ((it = textThreadsByParams[tp]) == nullptr)
|
|
||||||
{
|
|
||||||
it = textThreadsByParams[tp] = new TextThread(tp, nextThreadNumber++, splitDelay);
|
|
||||||
if (create) create(it);
|
|
||||||
}
|
|
||||||
it->AddText(text, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HookManager::AddConsoleOutput(LPCWSTR text)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
int len = wcslen(text) * 2;
|
|
||||||
TextThread *console = textThreadsByParams[{ 0, -1UL, -1UL, -1UL }];
|
|
||||||
console->AddSentence(std::wstring(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HookManager::ClearCurrent()
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
current->Reset();
|
|
||||||
if (reset) reset(current);
|
|
||||||
}
|
|
||||||
|
|
||||||
HANDLE HookManager::GetHostPipe(DWORD pid)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
return processRecordsByIds[pid].hostPipe;
|
|
||||||
}
|
|
||||||
|
|
||||||
HookParam HookManager::GetHookParam(DWORD pid, DWORD addr)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
HookParam ret = {};
|
|
||||||
ProcessRecord pr = processRecordsByIds[pid];
|
|
||||||
if (pr.hookman_map == nullptr) return ret;
|
|
||||||
MutexLocker locker(pr.hookman_mutex);
|
|
||||||
const Hook* hooks = (const Hook*)pr.hookman_map;
|
|
||||||
for (int i = 0; i < MAX_HOOK; ++i)
|
|
||||||
if (hooks[i].Address() == addr)
|
|
||||||
ret = hooks[i].hp;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::wstring HookManager::GetHookName(DWORD pid, DWORD addr)
|
|
||||||
{
|
|
||||||
HM_LOCK;
|
|
||||||
std::string buffer;
|
|
||||||
ProcessRecord pr = processRecordsByIds[pid];
|
|
||||||
if (pr.hookman_map == nullptr) return L"";
|
|
||||||
MutexLocker locker(pr.hookman_mutex);
|
|
||||||
USES_CONVERSION;
|
|
||||||
const Hook* hooks = (const Hook*)pr.hookman_map;
|
|
||||||
for (int i = 0; i < MAX_HOOK; ++i)
|
|
||||||
{
|
|
||||||
if (hooks[i].Address() == addr)
|
|
||||||
{
|
|
||||||
buffer.resize(hooks[i].NameLength());
|
|
||||||
ReadProcessMemory(pr.process_handle, hooks[i].Name(), &buffer[0], hooks[i].NameLength(), nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return std::wstring(A2W(buffer.c_str()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// EOF
|
|
@ -1,77 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
// hookman.h
|
|
||||||
// 8/23/2013 jichi
|
|
||||||
// Branch: ITH/HookManager.h, rev 133
|
|
||||||
|
|
||||||
#include <Windows.h>
|
|
||||||
#include "textthread.h"
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <string>
|
|
||||||
#include "vnrhook/include/types.h"
|
|
||||||
|
|
||||||
struct ProcessRecord
|
|
||||||
{
|
|
||||||
HANDLE process_handle;
|
|
||||||
HANDLE hookman_mutex;
|
|
||||||
HANDLE hookman_section;
|
|
||||||
LPVOID hookman_map;
|
|
||||||
HANDLE hostPipe;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void(*ProcessEventCallback)(DWORD pid);
|
|
||||||
typedef void(*ThreadEventCallback)(TextThread*);
|
|
||||||
|
|
||||||
struct ThreadParameterHasher
|
|
||||||
{
|
|
||||||
size_t operator()(const ThreadParameter& tp) const
|
|
||||||
{
|
|
||||||
return std::hash<DWORD>()(tp.pid << 6) + std::hash<DWORD>()(tp.hook) + std::hash<DWORD>()(tp.retn) + std::hash<DWORD>()(tp.spl);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Artikash 7/19/2018: This should probably be broken up into 2-4 classes...
|
|
||||||
class __declspec(dllexport) HookManager
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
HookManager();
|
|
||||||
~HookManager();
|
|
||||||
|
|
||||||
TextThread *FindSingle(DWORD number);
|
|
||||||
HANDLE GetHostPipe(DWORD pid);
|
|
||||||
void ClearCurrent();
|
|
||||||
void SelectCurrent(DWORD num);
|
|
||||||
void SetCurrent(TextThread *it);
|
|
||||||
void AddConsoleOutput(LPCWSTR text);
|
|
||||||
void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len);
|
|
||||||
void RemoveProcessContext(DWORD pid); // private
|
|
||||||
void RemoveSingleHook(DWORD pid, DWORD addr);
|
|
||||||
void RegisterProcess(DWORD pid, HANDLE hostPipe);
|
|
||||||
void UnRegisterProcess(DWORD pid);
|
|
||||||
HookParam GetHookParam(DWORD pid, DWORD addr);
|
|
||||||
std::wstring GetHookName(DWORD pid, DWORD addr);
|
|
||||||
|
|
||||||
void RegisterThreadCreateCallback(ThreadEventCallback cf) { create = cf; }
|
|
||||||
void RegisterThreadRemoveCallback(ThreadEventCallback cf) { remove = cf; }
|
|
||||||
void RegisterThreadResetCallback(ThreadEventCallback cf) { reset = cf; }
|
|
||||||
void RegisterProcessAttachCallback(ProcessEventCallback cf) { attach = cf; }
|
|
||||||
void RegisterProcessDetachCallback(ProcessEventCallback cf) { detach = cf; }
|
|
||||||
|
|
||||||
void SetSplitInterval(unsigned int splitDelay) { this->splitDelay = splitDelay; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::unordered_map<ThreadParameter, TextThread*, ThreadParameterHasher> textThreadsByParams;
|
|
||||||
std::unordered_map<DWORD, ProcessRecord> processRecordsByIds;
|
|
||||||
|
|
||||||
CRITICAL_SECTION hmCs;
|
|
||||||
|
|
||||||
TextThread *current;
|
|
||||||
|
|
||||||
ThreadEventCallback create, remove, reset;
|
|
||||||
ProcessEventCallback attach, detach;
|
|
||||||
|
|
||||||
WORD nextThreadNumber;
|
|
||||||
unsigned int splitDelay;
|
|
||||||
};
|
|
||||||
|
|
||||||
// EOF
|
|
210
texthook/host.cc
210
texthook/host.cc
@ -4,28 +4,37 @@
|
|||||||
|
|
||||||
#include "host.h"
|
#include "host.h"
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
#include "vnrhook/include/const.h"
|
#include "winmutex.h"
|
||||||
#include "vnrhook/include/defs.h"
|
#include <atlbase.h>
|
||||||
#include "vnrhook/include/types.h"
|
#include "../vnrhook/include/const.h"
|
||||||
|
#include "../vnrhook/include/defs.h"
|
||||||
|
#include "../vnrhook/include/types.h"
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
HANDLE preventDuplicationMutex;
|
HANDLE preventDuplicationMutex;
|
||||||
|
|
||||||
HookManager* man;
|
std::unordered_map<ThreadParameter, TextThread*, ThreadParameterHasher> textThreadsByParams;
|
||||||
|
std::unordered_map<DWORD, ProcessRecord> processRecordsByIds;
|
||||||
|
|
||||||
|
CRITICAL_SECTION hostCs;
|
||||||
|
|
||||||
|
ThreadEventCallback onCreate, onRemove;
|
||||||
|
ProcessEventCallback onAttach, onDetach;
|
||||||
|
|
||||||
|
WORD nextThreadNumber;
|
||||||
HWND dummyWindow;
|
HWND dummyWindow;
|
||||||
bool running;
|
bool running;
|
||||||
|
|
||||||
namespace
|
#define HOST_LOCK CriticalSectionLocker hostLocker(hostCs) // Synchronized scope for accessing private data
|
||||||
{ // unnamed
|
|
||||||
void GetDebugPrivileges() // Artikash 5/19/2018: Is it just me or is this function 100% superfluous?
|
void GetDebugPrivileges() // Artikash 5/19/2018: Is it just me or is this function 100% superfluous?
|
||||||
{
|
{
|
||||||
HANDLE processToken;
|
HANDLE processToken;
|
||||||
TOKEN_PRIVILEGES Privileges = { 1, {0x14, 0, SE_PRIVILEGE_ENABLED} };
|
TOKEN_PRIVILEGES Privileges = { 1, {0x14, 0, SE_PRIVILEGE_ENABLED} };
|
||||||
|
|
||||||
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken);
|
OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &processToken);
|
||||||
AdjustTokenPrivileges(processToken, FALSE, &Privileges, 0, nullptr, nullptr);
|
AdjustTokenPrivileges(processToken, FALSE, &Privileges, 0, nullptr, nullptr);
|
||||||
CloseHandle(processToken);
|
CloseHandle(processToken);
|
||||||
}
|
}
|
||||||
} // unnamed namespace
|
|
||||||
|
|
||||||
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused)
|
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused)
|
||||||
{
|
{
|
||||||
@ -33,59 +42,68 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID unused)
|
|||||||
{
|
{
|
||||||
case DLL_PROCESS_ATTACH:
|
case DLL_PROCESS_ATTACH:
|
||||||
DisableThreadLibraryCalls(hinstDLL);
|
DisableThreadLibraryCalls(hinstDLL);
|
||||||
GetDebugPrivileges();
|
|
||||||
// jichi 8/24/2013: Create hidden window so that ITH can access timer and events
|
// jichi 8/24/2013: Create hidden window so that ITH can access timer and events
|
||||||
dummyWindow = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0);
|
dummyWindow = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0);
|
||||||
break;
|
break;
|
||||||
case DLL_PROCESS_DETACH:
|
|
||||||
CloseHost();
|
|
||||||
DestroyWindow(dummyWindow);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT bool StartHost()
|
namespace Host
|
||||||
|
{
|
||||||
|
DLLEXPORT bool Start()
|
||||||
{
|
{
|
||||||
preventDuplicationMutex = CreateMutexW(nullptr, TRUE, ITH_SERVER_MUTEX);
|
preventDuplicationMutex = CreateMutexW(nullptr, TRUE, ITH_SERVER_MUTEX);
|
||||||
if (GetLastError() == ERROR_ALREADY_EXISTS || ::running)
|
if (GetLastError() == ERROR_ALREADY_EXISTS || running)
|
||||||
{
|
{
|
||||||
MessageBoxW(nullptr, L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!", L"Error", MB_ICONERROR);
|
MessageBoxW(nullptr, L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!", L"Error", MB_ICONERROR);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
::running = true;
|
running = true;
|
||||||
::man = new HookManager;
|
GetDebugPrivileges();
|
||||||
|
InitializeCriticalSection(&hostCs);
|
||||||
|
onAttach = onDetach = nullptr;
|
||||||
|
onCreate = onRemove = nullptr;
|
||||||
|
nextThreadNumber = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT void OpenHost()
|
DLLEXPORT void Open()
|
||||||
{
|
{
|
||||||
|
TextThread* console = textThreadsByParams[{ 0, -1UL, -1UL, -1UL }] = new TextThread({ 0, -1UL, -1UL, -1UL }, nextThreadNumber++);
|
||||||
|
console->Status() |= USING_UNICODE;
|
||||||
|
if (onCreate) onCreate(console);
|
||||||
CreateNewPipe();
|
CreateNewPipe();
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT void CloseHost()
|
DLLEXPORT void Close()
|
||||||
{
|
{
|
||||||
if (::running)
|
if (running)
|
||||||
{
|
{
|
||||||
::running = false;
|
EnterCriticalSection(&hostCs);
|
||||||
delete man;
|
running = false;
|
||||||
|
DestroyWindow(dummyWindow);
|
||||||
|
RemoveThreads([](auto one, auto two) { return true; }, {});
|
||||||
|
//for (auto i : processRecordsByIds) UnregisterProcess(i.first); // Artikash 7/24/2018 FIXME: This segfaults since UnregisterProcess invalidates the iterator
|
||||||
|
LeaveCriticalSection(&hostCs);
|
||||||
|
DeleteCriticalSection(&hostCs);
|
||||||
CloseHandle(preventDuplicationMutex);
|
CloseHandle(preventDuplicationMutex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT bool InjectProcessById(DWORD processId, DWORD timeout)
|
DLLEXPORT bool InjectProcess(DWORD processId, DWORD timeout)
|
||||||
{
|
{
|
||||||
if (processId == GetCurrentProcessId()) return false;
|
if (processId == GetCurrentProcessId()) return false;
|
||||||
|
|
||||||
CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str()));
|
CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str()));
|
||||||
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
if (GetLastError() == ERROR_ALREADY_EXISTS)
|
||||||
{
|
{
|
||||||
man->AddConsoleOutput(L"already locked");
|
AddConsoleOutput(L"already locked");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,53 +124,153 @@ DLLEXPORT bool InjectProcessById(DWORD processId, DWORD timeout)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
man->AddConsoleOutput(L"couldn't inject dll");
|
AddConsoleOutput(L"couldn't inject dll");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT bool DetachProcessById(DWORD processId)
|
DLLEXPORT bool DetachProcess(DWORD processId)
|
||||||
{
|
{
|
||||||
DWORD command = HOST_COMMAND_DETACH;
|
DWORD command = HOST_COMMAND_DETACH;
|
||||||
DWORD unused;
|
DWORD unused;
|
||||||
return WriteFile(man->GetHostPipe(processId), &command, sizeof(command), &unused, nullptr);
|
return WriteFile(processRecordsByIds[processId].hostPipe, &command, sizeof(command), &unused, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT void GetHostHookManager(HookManager** hookman)
|
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name)
|
||||||
{
|
{
|
||||||
*hookman = man;
|
|
||||||
}
|
|
||||||
|
|
||||||
DLLEXPORT bool InsertHook(DWORD pid, const HookParam *hp, std::string name)
|
|
||||||
{
|
|
||||||
HANDLE commandPipe = man->GetHostPipe(pid);
|
|
||||||
if (commandPipe == nullptr) return false;
|
|
||||||
|
|
||||||
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
||||||
*(DWORD*)buffer = HOST_COMMAND_NEW_HOOK;
|
*(DWORD*)buffer = HOST_COMMAND_NEW_HOOK;
|
||||||
*(HookParam*)(buffer + sizeof(DWORD)) = *hp;
|
*(HookParam*)(buffer + sizeof(DWORD)) = hp;
|
||||||
if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str());
|
if (name.size()) strcpy((char*)buffer + sizeof(DWORD) + sizeof(HookParam), name.c_str());
|
||||||
DWORD unused;
|
DWORD unused;
|
||||||
return WriteFile(commandPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr);
|
return WriteFile(processRecordsByIds[pid].hostPipe, buffer, sizeof(DWORD) + sizeof(HookParam) + name.size(), &unused, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr)
|
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr)
|
||||||
{
|
{
|
||||||
HANDLE commandPipe = man->GetHostPipe(pid);
|
HANDLE hostPipe = processRecordsByIds[pid].hostPipe;
|
||||||
if (commandPipe == nullptr) return false;
|
if (hostPipe == nullptr) return false;
|
||||||
|
|
||||||
HANDLE hookRemovalEvent = CreateEventW(nullptr, TRUE, FALSE, ITH_REMOVEHOOK_EVENT);
|
HANDLE hookRemovalEvent = CreateEventW(nullptr, TRUE, FALSE, ITH_REMOVEHOOK_EVENT);
|
||||||
|
|
||||||
BYTE buffer[sizeof(DWORD) * 2] = {};
|
BYTE buffer[sizeof(DWORD) * 2] = {};
|
||||||
*(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK;
|
*(DWORD*)buffer = HOST_COMMAND_REMOVE_HOOK;
|
||||||
*(DWORD*)(buffer + sizeof(DWORD)) = addr;
|
*(DWORD*)(buffer + sizeof(DWORD)) = addr;
|
||||||
DWORD unused;
|
DWORD unused;
|
||||||
WriteFile(commandPipe, buffer, sizeof(DWORD) * 2, &unused, nullptr);
|
WriteFile(hostPipe, buffer, sizeof(DWORD) * 2, &unused, nullptr);
|
||||||
|
|
||||||
WaitForSingleObject(hookRemovalEvent, 1000);
|
WaitForSingleObject(hookRemovalEvent, 1000);
|
||||||
CloseHandle(hookRemovalEvent);
|
CloseHandle(hookRemovalEvent);
|
||||||
|
RemoveThreads([](auto one, auto two) { return one.pid == two.pid && one.hook == two.hook; }, { pid, addr, 0, 0 });
|
||||||
man->RemoveSingleHook(pid, addr);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr)
|
||||||
|
{
|
||||||
|
HOST_LOCK;
|
||||||
|
HookParam ret = {};
|
||||||
|
ProcessRecord pr = processRecordsByIds[pid];
|
||||||
|
if (pr.hookman_map == nullptr) return ret;
|
||||||
|
MutexLocker locker(pr.hookman_mutex);
|
||||||
|
const Hook* hooks = (const Hook*)pr.hookman_map;
|
||||||
|
for (int i = 0; i < MAX_HOOK; ++i)
|
||||||
|
if (hooks[i].Address() == addr)
|
||||||
|
ret = hooks[i].hp;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr)
|
||||||
|
{
|
||||||
|
if (pid == 0) return L"Console";
|
||||||
|
HOST_LOCK;
|
||||||
|
std::string buffer = "";
|
||||||
|
ProcessRecord pr = processRecordsByIds[pid];
|
||||||
|
if (pr.hookman_map == nullptr) return L"";
|
||||||
|
MutexLocker locker(pr.hookman_mutex);
|
||||||
|
const Hook* hooks = (const Hook*)pr.hookman_map;
|
||||||
|
for (int i = 0; i < MAX_HOOK; ++i)
|
||||||
|
if (hooks[i].Address() == addr)
|
||||||
|
{
|
||||||
|
buffer.resize(hooks[i].NameLength());
|
||||||
|
ReadProcessMemory(pr.process_handle, hooks[i].Name(), &buffer[0], hooks[i].NameLength(), nullptr);
|
||||||
|
}
|
||||||
|
USES_CONVERSION;
|
||||||
|
return std::wstring(A2W(buffer.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT TextThread* GetThread(DWORD number)
|
||||||
|
{
|
||||||
|
HOST_LOCK;
|
||||||
|
for (auto i : textThreadsByParams)
|
||||||
|
if (i.second->Number() == number)
|
||||||
|
return i.second;
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT void AddConsoleOutput(std::wstring text)
|
||||||
|
{
|
||||||
|
HOST_LOCK;
|
||||||
|
textThreadsByParams[{ 0, -1UL, -1UL, -1UL }]->AddSentence(std::wstring(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf) { onCreate = cf; }
|
||||||
|
DLLEXPORT void RegisterThreadRemoveCallback(ThreadEventCallback cf) { onRemove = cf; }
|
||||||
|
DLLEXPORT void RegisterProcessAttachCallback(ProcessEventCallback cf) { onAttach = cf; }
|
||||||
|
DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf) { onDetach = cf; }
|
||||||
|
}
|
||||||
|
|
||||||
|
void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE * text, int len)
|
||||||
|
{
|
||||||
|
// jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need
|
||||||
|
if (!text || !pid || len <= 0) return;
|
||||||
|
HOST_LOCK;
|
||||||
|
ThreadParameter tp = { pid, hook, retn, split };
|
||||||
|
TextThread *it;
|
||||||
|
if ((it = textThreadsByParams[tp]) == nullptr)
|
||||||
|
{
|
||||||
|
it = textThreadsByParams[tp] = new TextThread(tp, nextThreadNumber++);
|
||||||
|
if (Host::GetHookParam(pid, hook).type & USING_UNICODE) it->Status() |= USING_UNICODE;
|
||||||
|
if (onCreate) onCreate(it);
|
||||||
|
}
|
||||||
|
it->AddText(text, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp)
|
||||||
|
{
|
||||||
|
HOST_LOCK;
|
||||||
|
std::vector<ThreadParameter> removedThreads;
|
||||||
|
for (auto i : textThreadsByParams)
|
||||||
|
if (RemoveIf(i.first, cmp))
|
||||||
|
{
|
||||||
|
if (onRemove) onRemove(i.second);
|
||||||
|
//delete i.second; // Artikash 7/24/2018: FIXME: Qt GUI updates on another thread, so I can't delete this yet.
|
||||||
|
i.second->Reset(); // Temp workaround to free some memory.
|
||||||
|
removedThreads.push_back(i.first);
|
||||||
|
}
|
||||||
|
for (auto i : removedThreads) textThreadsByParams.erase(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RegisterProcess(DWORD pid, HANDLE hostPipe)
|
||||||
|
{
|
||||||
|
HOST_LOCK;
|
||||||
|
ProcessRecord record;
|
||||||
|
record.hostPipe = hostPipe;
|
||||||
|
record.hookman_section = OpenFileMappingW(FILE_MAP_READ, FALSE, (ITH_SECTION_ + std::to_wstring(pid)).c_str());
|
||||||
|
record.hookman_map = MapViewOfFile(record.hookman_section, FILE_MAP_READ, 0, 0, HOOK_SECTION_SIZE / 2); // jichi 1/16/2015: Changed to half to hook section size
|
||||||
|
record.process_handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
|
||||||
|
record.hookman_mutex = OpenMutexW(MUTEX_ALL_ACCESS, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(pid)).c_str());
|
||||||
|
processRecordsByIds[pid] = record;
|
||||||
|
if (onAttach) onAttach(pid);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnregisterProcess(DWORD pid)
|
||||||
|
{
|
||||||
|
HOST_LOCK;
|
||||||
|
ProcessRecord pr = processRecordsByIds[pid];
|
||||||
|
if (!pr.hostPipe) return;
|
||||||
|
CloseHandle(pr.hookman_mutex);
|
||||||
|
UnmapViewOfFile(pr.hookman_map);
|
||||||
|
CloseHandle(pr.process_handle);
|
||||||
|
CloseHandle(pr.hookman_section);
|
||||||
|
processRecordsByIds.erase(pid);
|
||||||
|
RemoveThreads([](auto one, auto two) { return one.pid == two.pid; }, { pid, 0, 0, 0 });
|
||||||
|
if (onDetach) onDetach(pid);
|
||||||
|
}
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
@ -5,17 +5,58 @@
|
|||||||
// Branch: ITH/IHF.h, rev 105
|
// Branch: ITH/IHF.h, rev 105
|
||||||
|
|
||||||
#define DLLEXPORT __declspec(dllexport)
|
#define DLLEXPORT __declspec(dllexport)
|
||||||
#include "hookman.h"
|
|
||||||
#include "vnrhook/include/types.h"
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
DLLEXPORT void OpenHost();
|
#include <Windows.h>
|
||||||
DLLEXPORT bool StartHost();
|
#include "textthread.h"
|
||||||
DLLEXPORT void CloseHost();
|
#include <string>
|
||||||
DLLEXPORT void GetHostHookManager(HookManager **hookman);
|
#include <functional>
|
||||||
DLLEXPORT bool InjectProcessById(DWORD pid, DWORD timeout = 5000);
|
#include "../vnrhook/include/types.h"
|
||||||
DLLEXPORT bool DetachProcessById(DWORD pid);
|
|
||||||
DLLEXPORT bool InsertHook(DWORD pid, const HookParam *hp, std::string name = "");
|
struct ProcessRecord
|
||||||
|
{
|
||||||
|
HANDLE process_handle;
|
||||||
|
HANDLE hookman_mutex;
|
||||||
|
HANDLE hookman_section;
|
||||||
|
LPVOID hookman_map;
|
||||||
|
HANDLE hostPipe;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::function<void(DWORD)> ProcessEventCallback;
|
||||||
|
typedef std::function<void(TextThread*)> ThreadEventCallback;
|
||||||
|
|
||||||
|
struct ThreadParameterHasher
|
||||||
|
{
|
||||||
|
size_t operator()(const ThreadParameter& tp) const
|
||||||
|
{
|
||||||
|
return std::hash<DWORD>()(tp.pid << 6) + std::hash<DWORD>()(tp.hook) + std::hash<DWORD>()(tp.retn) + std::hash<DWORD>()(tp.spl);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace Host
|
||||||
|
{
|
||||||
|
DLLEXPORT void Open();
|
||||||
|
DLLEXPORT bool Start();
|
||||||
|
DLLEXPORT void Close();
|
||||||
|
DLLEXPORT bool InjectProcess(DWORD pid, DWORD timeout = 5000);
|
||||||
|
DLLEXPORT bool DetachProcess(DWORD pid);
|
||||||
|
|
||||||
|
DLLEXPORT bool InsertHook(DWORD pid, HookParam hp, std::string name = "");
|
||||||
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr);
|
DLLEXPORT bool RemoveHook(DWORD pid, DWORD addr);
|
||||||
|
DLLEXPORT HookParam GetHookParam(DWORD pid, DWORD addr);
|
||||||
|
DLLEXPORT std::wstring GetHookName(DWORD pid, DWORD addr);
|
||||||
|
|
||||||
|
DLLEXPORT TextThread* GetThread(DWORD number);
|
||||||
|
DLLEXPORT void AddConsoleOutput(std::wstring text);
|
||||||
|
|
||||||
|
DLLEXPORT void RegisterThreadCreateCallback(ThreadEventCallback cf);
|
||||||
|
DLLEXPORT void RegisterThreadRemoveCallback(ThreadEventCallback cf);
|
||||||
|
DLLEXPORT void RegisterProcessAttachCallback(ProcessEventCallback cf);
|
||||||
|
DLLEXPORT void RegisterProcessDetachCallback(ProcessEventCallback cf);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DispatchText(DWORD pid, DWORD hook, DWORD retn, DWORD split, const BYTE *text, int len);
|
||||||
|
void RemoveThreads(bool(*RemoveIf)(ThreadParameter, ThreadParameter), ThreadParameter cmp);
|
||||||
|
void RegisterProcess(DWORD pid, HANDLE hostPipe);
|
||||||
|
void UnregisterProcess(DWORD pid);
|
||||||
|
|
||||||
// EOF
|
// EOF
|
||||||
|
@ -4,12 +4,10 @@
|
|||||||
|
|
||||||
#include "pipe.h"
|
#include "pipe.h"
|
||||||
#include "host.h"
|
#include "host.h"
|
||||||
#include "vnrhook/include/defs.h"
|
#include "../vnrhook/include/defs.h"
|
||||||
#include "vnrhook/include/const.h"
|
#include "../vnrhook/include/const.h"
|
||||||
#include <atlbase.h>
|
#include <atlbase.h>
|
||||||
|
|
||||||
extern HookManager* man;
|
|
||||||
|
|
||||||
struct Pipes
|
struct Pipes
|
||||||
{
|
{
|
||||||
HANDLE hookPipe;
|
HANDLE hookPipe;
|
||||||
@ -34,7 +32,7 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter)
|
|||||||
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
||||||
DWORD bytesRead, processId;
|
DWORD bytesRead, processId;
|
||||||
ReadFile(pipes->hookPipe, &processId, sizeof(processId), &bytesRead, nullptr);
|
ReadFile(pipes->hookPipe, &processId, sizeof(processId), &bytesRead, nullptr);
|
||||||
man->RegisterProcess(processId, pipes->hostPipe);
|
RegisterProcess(processId, pipes->hostPipe);
|
||||||
|
|
||||||
// jichi 9/27/2013: why recursion?
|
// jichi 9/27/2013: why recursion?
|
||||||
// Artikash 5/20/2018: To create a new pipe for another process
|
// Artikash 5/20/2018: To create a new pipe for another process
|
||||||
@ -55,13 +53,13 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter)
|
|||||||
case HOST_NOTIFICATION_NEWHOOK: // Artikash 7/18/2018: Useless for now, but could be used to implement smth later
|
case HOST_NOTIFICATION_NEWHOOK: // Artikash 7/18/2018: Useless for now, but could be used to implement smth later
|
||||||
break;
|
break;
|
||||||
case HOST_NOTIFICATION_TEXT:
|
case HOST_NOTIFICATION_TEXT:
|
||||||
man->AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text
|
Host::AddConsoleOutput(A2W((LPCSTR)(buffer + sizeof(DWORD) * 2))); // Text
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
man->DispatchText(processId,
|
DispatchText(processId,
|
||||||
*(DWORD*)buffer, // Hook address
|
*(DWORD*)buffer, // Hook address
|
||||||
*(DWORD*)(buffer + sizeof(DWORD)), // Return address
|
*(DWORD*)(buffer + sizeof(DWORD)), // Return address
|
||||||
*(DWORD*)(buffer + sizeof(DWORD) * 2), // Split
|
*(DWORD*)(buffer + sizeof(DWORD) * 2), // Split
|
||||||
@ -73,7 +71,7 @@ DWORD WINAPI TextReceiver(LPVOID lpThreadParameter)
|
|||||||
|
|
||||||
DisconnectNamedPipe(pipes->hookPipe);
|
DisconnectNamedPipe(pipes->hookPipe);
|
||||||
DisconnectNamedPipe(pipes->hostPipe);
|
DisconnectNamedPipe(pipes->hostPipe);
|
||||||
man->UnRegisterProcess(processId);
|
UnregisterProcess(processId);
|
||||||
CloseHandle(pipes->hookPipe);
|
CloseHandle(pipes->hookPipe);
|
||||||
CloseHandle(pipes->hostPipe);
|
CloseHandle(pipes->hostPipe);
|
||||||
delete pipes;
|
delete pipes;
|
||||||
|
@ -5,12 +5,10 @@
|
|||||||
# pragma warning (disable:4100) // C4100: unreference formal parameter
|
# pragma warning (disable:4100) // C4100: unreference formal parameter
|
||||||
#endif // _MSC_VER
|
#endif // _MSC_VER
|
||||||
|
|
||||||
#include "host.h"
|
|
||||||
#include "textthread.h"
|
#include "textthread.h"
|
||||||
#include "vnrhook/include/const.h"
|
#include "../vnrhook/include/const.h"
|
||||||
#include "winmutex.h"
|
#include "winmutex.h"
|
||||||
|
|
||||||
extern HookManager* man;
|
|
||||||
extern HWND dummyWindow;
|
extern HWND dummyWindow;
|
||||||
|
|
||||||
#define TT_LOCK CriticalSectionLocker ttLocker(ttCs) // Synchronized scope for accessing private data
|
#define TT_LOCK CriticalSectionLocker ttLocker(ttCs) // Synchronized scope for accessing private data
|
||||||
@ -29,7 +27,8 @@ TextThread::TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned i
|
|||||||
|
|
||||||
TextThread::~TextThread()
|
TextThread::~TextThread()
|
||||||
{
|
{
|
||||||
TT_LOCK;
|
EnterCriticalSection(&ttCs);
|
||||||
|
LeaveCriticalSection(&ttCs);
|
||||||
DeleteCriticalSection(&ttCs);
|
DeleteCriticalSection(&ttCs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
struct ThreadParameter
|
struct ThreadParameter
|
||||||
{
|
{
|
||||||
@ -22,17 +23,15 @@ struct ThreadParameter
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
#define CURRENT_SELECT 0x1000
|
|
||||||
|
|
||||||
class TextThread;
|
class TextThread;
|
||||||
typedef std::wstring(*ThreadOutputCallback)(TextThread*, std::wstring data);
|
typedef std::function<std::wstring(TextThread*, std::wstring)> ThreadOutputCallback;
|
||||||
|
|
||||||
//extern DWORD split_time,repeat_count,global_filter,cyclic_remove;
|
//extern DWORD split_time,repeat_count,global_filter,cyclic_remove;
|
||||||
|
|
||||||
class TextThread
|
class TextThread
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned int splitDelay);
|
TextThread(ThreadParameter tp, unsigned int threadNumber, unsigned int splitDelay = 250);
|
||||||
~TextThread();
|
~TextThread();
|
||||||
|
|
||||||
void Reset();
|
void Reset();
|
||||||
@ -44,6 +43,7 @@ public:
|
|||||||
DWORD &Status() { return status; }
|
DWORD &Status() { return status; }
|
||||||
WORD Number() const { return threadNumber; }
|
WORD Number() const { return threadNumber; }
|
||||||
ThreadParameter GetThreadParameter() { return tp; }
|
ThreadParameter GetThreadParameter() { return tp; }
|
||||||
|
void SetSplitDelay(unsigned int splitDelay) { this->splitDelay = splitDelay; }
|
||||||
|
|
||||||
void RegisterOutputCallBack(ThreadOutputCallback cb) { output = cb; }
|
void RegisterOutputCallBack(ThreadOutputCallback cb) { output = cb; }
|
||||||
|
|
||||||
|
@ -1,22 +1,3 @@
|
|||||||
# # 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
|
|
||||||
# }
|
|
||||||
|
|
||||||
project(engine)
|
project(engine)
|
||||||
|
|
||||||
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
@ -72,12 +53,12 @@ set_target_properties(vnrhook PROPERTIES
|
|||||||
|
|
||||||
target_compile_options(vnrhook PRIVATE
|
target_compile_options(vnrhook PRIVATE
|
||||||
/EHa
|
/EHa
|
||||||
$<$<CONFIG:MinSizeRel>:/MT>
|
$<$<CONFIG:Release>:/MT>
|
||||||
$<$<CONFIG:Debug>:>
|
$<$<CONFIG:Debug>:>
|
||||||
)
|
)
|
||||||
|
|
||||||
set(vnrhook_libs
|
set(vnrhook_libs
|
||||||
"C:\\Program Files (x86)\\Windows Kits\\10\\Lib\\10.0.17134.0\\um\\x86\\ntdll.lib"
|
ntdll.lib
|
||||||
Version.lib
|
Version.lib
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -89,8 +70,3 @@ target_compile_definitions(vnrhook
|
|||||||
ITH_HAS_SEH
|
ITH_HAS_SEH
|
||||||
_CRT_NON_CONFORMING_SWPRINTFS
|
_CRT_NON_CONFORMING_SWPRINTFS
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS vnrhook RUNTIME
|
|
||||||
DESTINATION .
|
|
||||||
CONFIGURATIONS Release
|
|
||||||
)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user