Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extract strings #54

Merged
merged 3 commits into from
Nov 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions GUI/extenwindow.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "extenwindow.h"
#include "ui_extenwindow.h"
#include "defs.h"
#include "text.h"
#include "types.h"
#include <QFileDialog>
#include <QMimeData>
Expand Down Expand Up @@ -142,7 +142,7 @@ void ExtenWindow::dropEvent(QDropEvent* event)

void ExtenWindow::on_addButton_clicked()
{
Add(QFileDialog::getOpenFileName(this, "Select Extension", "C:\\", "Extensions (*.dll)"));
Add(QFileDialog::getOpenFileName(this, SELECT_EXTENSION, "C:\\", EXTENSIONS));
}

void ExtenWindow::on_rmvButton_clicked()
Expand Down
3 changes: 2 additions & 1 deletion GUI/extenwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define EXTENSIONS_H

#include "qtcommon.h"
#include "defs.h"
#include <shared_mutex>
#include <QListWidget>
#include <QDragEnterEvent>
Expand Down Expand Up @@ -34,7 +35,7 @@ private slots:
void dropEvent(QDropEvent* event);

Ui::ExtenWindow* ui;
QFile extenSaveFile = QFile("Extensions.txt");
QFile extenSaveFile = QFile(EXTEN_SAVE_FILE);
QListWidget* extenList;
};

Expand Down
26 changes: 11 additions & 15 deletions GUI/host/host.cc
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "host.h"
#include "const.h"
#include "defs.h"
#include "text.h"
#include "../vnrhook/hijack/texthook.h"

namespace
Expand Down Expand Up @@ -59,7 +60,7 @@ namespace
LOCK(hostMutex);
if (textThreadsByParams[tp] == nullptr)
{
if (textThreadsByParams.size() > MAX_THREAD_COUNT) return Host::AddConsoleOutput(L"too many text threads: can't create more");
if (textThreadsByParams.size() > MAX_THREAD_COUNT) return Host::AddConsoleOutput(TOO_MANY_THREADS);
OnCreate(textThreadsByParams[tp] = std::make_shared<TextThread>(tp, Host::GetHookParam(tp), Host::GetHookName(tp)));
}
textThreadsByParams[tp]->Push(text, len);
Expand Down Expand Up @@ -91,7 +92,7 @@ namespace
RemoveThreads([&](ThreadParam tp) { return tp.pid == processId; });
}

void StartPipe()
void CreatePipe()
{
std::thread([]
{
Expand All @@ -108,15 +109,11 @@ namespace
ReadFile(hookPipe, &processId, sizeof(processId), &bytesRead, nullptr);
RegisterProcess(processId, hostPipe);

// jichi 9/27/2013: why recursion?
// Artikash 5/20/2018: Easy way to create a new pipe for another process
StartPipe();
CreatePipe();

while (ReadFile(hookPipe, buffer, PIPE_BUFFER_SIZE, &bytesRead, nullptr))
switch (*(int*)buffer)
{
//case HOST_NOTIFICATION_NEWHOOK: // Artikash 7/18/2018: Useless for now, but could be used to implement smth later
//break;
case HOST_NOTIFICATION_RMVHOOK:
{
auto info = *(HookRemovedNotif*)buffer;
Expand All @@ -139,9 +136,9 @@ namespace
break;
}

UnregisterProcess(processId);
DisconnectNamedPipe(hookPipe);
DisconnectNamedPipe(hostPipe);
UnregisterProcess(processId);
CloseHandle(hookPipe);
CloseHandle(hostPipe);
}).detach();
Expand All @@ -151,7 +148,6 @@ namespace
{
std::thread([]
{
std::wstring last;
while (true)
{
Sleep(50);
Expand All @@ -161,8 +157,8 @@ namespace
{
if (wchar_t* clipboardData = (wchar_t*)GlobalLock(clipboardHandle))
{
if (last != clipboardData)
Host::GetThread(CLIPBOARD)->AddSentence(last = clipboardData);
static std::wstring last;
if (last != clipboardData) Host::GetThread(CLIPBOARD)->AddSentence(last = clipboardData);
GlobalUnlock(clipboardHandle);
}
}
Expand All @@ -182,7 +178,7 @@ namespace Host
OnCreate(textThreadsByParams[CONSOLE] = std::make_shared<TextThread>(CONSOLE, HookParam{}, L"Console"));
OnCreate(textThreadsByParams[CLIPBOARD] = std::make_shared<TextThread>(CLIPBOARD, HookParam{}, L"Clipboard"));
StartCapturingClipboard();
StartPipe();
CreatePipe();
}

void Close()
Expand All @@ -202,7 +198,7 @@ namespace Host
CloseHandle(CreateMutexW(nullptr, FALSE, (ITH_HOOKMAN_MUTEX_ + std::to_wstring(processId)).c_str()));
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
AddConsoleOutput(L"already injected");
AddConsoleOutput(ALREADY_INJECTED);
return false;
}

Expand All @@ -218,7 +214,7 @@ namespace Host
IsWow64Process(processHandle, &invalidProcess);
if (invalidProcess)
{
AddConsoleOutput(L"architecture mismatch: try 32 bit Textractor instead");
AddConsoleOutput(ARCHITECTURE_MISMATCH);
CloseHandle(processHandle);
return false;
}
Expand All @@ -239,7 +235,7 @@ namespace Host
}
}

AddConsoleOutput(L"couldn't inject dll");
AddConsoleOutput(INJECT_FAILED);
return false;
}

Expand Down
4 changes: 2 additions & 2 deletions GUI/host/host.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ namespace Host
void AddConsoleOutput(std::wstring text);
}

inline UINT DEFAULT_CODEPAGE = SHIFT_JIS;
inline std::wstring StringToWideString(const std::string& text, UINT encoding = DEFAULT_CODEPAGE)
inline UINT CURRENT_CODEPAGE = SHIFT_JIS;
inline std::wstring StringToWideString(const std::string& text, UINT encoding = CURRENT_CODEPAGE)
{
std::wstring ret(text.size() + 1, 0);
ret.resize(MultiByteToWideChar(encoding, 0, text.c_str(), -1, ret.data(), ret.capacity()) - 1);
Expand Down
2 changes: 1 addition & 1 deletion GUI/host/textthread.cc
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void TextThread::Push(const BYTE* data, int len)
LOCK(threadMutex);
buffer += hp.type & USING_UNICODE
? std::wstring((wchar_t*)data, len / 2)
: StringToWideString(std::string((char*)data, len), hp.codepage != 0 ? hp.codepage : DEFAULT_CODEPAGE);
: StringToWideString(std::string((char*)data, len), hp.codepage != 0 ? hp.codepage : CURRENT_CODEPAGE);
if (std::all_of(buffer.begin(), buffer.end(), [&](wchar_t c) { return repeatingChars.count(c) > 0; })) buffer.clear();
lastPushTime = GetTickCount();
}
Expand Down
48 changes: 22 additions & 26 deletions GUI/mainwindow.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "defs.h"
#include "text.h"
#include "extenwindow.h"
#include "misc.h"
#include <QInputDialog>
Expand All @@ -16,11 +16,11 @@ MainWindow::MainWindow(QWidget *parent) :
ttCombo = findChild<QComboBox*>("ttCombo");
textOutput = findChild<QPlainTextEdit*>("textOutput");

if (settings.contains("Window")) this->setGeometry(settings.value("Window").toRect());
if (settings.contains(WINDOW)) this->setGeometry(settings.value(WINDOW).toRect());
// TODO: add GUI for changing these
if (settings.contains("Default_Codepage")) DEFAULT_CODEPAGE = settings.value("Default_Codepage").toInt();
if (settings.contains("Flush_Delay")) TextThread::flushDelay = settings.value("Flush_Delay").toInt();
if (settings.contains("Max_Buffer_Size")) TextThread::maxBufferSize = settings.value("Max_Buffer_Size").toInt();
if (settings.contains(DEFAULT_CODEPAGE)) CURRENT_CODEPAGE = settings.value(DEFAULT_CODEPAGE).toInt();
if (settings.contains(FLUSH_DELAY)) TextThread::flushDelay = settings.value(FLUSH_DELAY).toInt();
if (settings.contains(MAX_BUFFER_SIZE)) TextThread::maxBufferSize = settings.value(MAX_BUFFER_SIZE).toInt();

qRegisterMetaType<std::shared_ptr<TextThread>>();

Expand All @@ -37,15 +37,15 @@ MainWindow::MainWindow(QWidget *parent) :
[&](std::shared_ptr<TextThread> thread) { emit SigRemoveThread(thread); },
[&](TextThread* thread, std::wstring& output) { return ProcessThreadOutput(thread, output); }
);
Host::AddConsoleOutput(L"Textractor beta v3.4.0 by Artikash\r\nSource code and more information available under GPLv3 at https://github.com/Artikash/Textractor");
Host::AddConsoleOutput(ABOUT);
}

MainWindow::~MainWindow()
{
settings.setValue("Window", this->geometry());
settings.setValue("Default_Codepage", DEFAULT_CODEPAGE);
settings.setValue("Flush_Delay", TextThread::flushDelay);
settings.setValue("Max_Buffer_Size", TextThread::maxBufferSize);
settings.setValue(WINDOW, this->geometry());
settings.setValue(DEFAULT_CODEPAGE, CURRENT_CODEPAGE);
settings.setValue(FLUSH_DELAY, TextThread::flushDelay);
settings.setValue(MAX_BUFFER_SIZE, TextThread::maxBufferSize);
settings.sync();
delete ui;

Expand All @@ -60,7 +60,7 @@ void MainWindow::closeEvent(QCloseEvent*)
void MainWindow::AddProcess(unsigned processId)
{
processCombo->addItem(QString::number(processId, 16).toUpper() + ": " + GetModuleName(processId));
QFile file("SavedHooks.txt");
QFile file(HOOK_SAVE_FILE);
file.open(QIODevice::ReadOnly);
QString processName = GetFullModuleName(processId);
QStringList allProcesses = QString(file.readAll()).split("\r", QString::SkipEmptyParts);
Expand Down Expand Up @@ -175,18 +175,14 @@ QVector<HookParam> MainWindow::GetAllHooks(DWORD processId)

void MainWindow::on_attachButton_clicked()
{
QMultiHash<QString, DWORD> allProcesses = GetAllProcesses();
auto allProcesses = GetAllProcesses();
QStringList processList(allProcesses.uniqueKeys());
processList.sort(Qt::CaseInsensitive);
bool ok;
QString process = QInputDialog::getItem(this, "Select Process",
"If you don't see the process you want to inject, try running with admin rights\r\nYou can also type in the process id",
processList, 0, true, &ok);
bool injected = false;
QString process = QInputDialog::getItem(this, SELECT_PROCESS, INJECT_INFO, processList, 0, true, &ok);
if (!ok) return;
if (process.toInt(nullptr, 0)) injected |= Host::InjectProcess(process.toInt(nullptr, 0));
else for (auto processId : allProcesses.values(process)) injected |= Host::InjectProcess(processId);
if (!injected) Host::AddConsoleOutput(L"failed to inject");
if (process.toInt(nullptr, 0)) Host::InjectProcess(process.toInt(nullptr, 0));
else for (auto processId : allProcesses.values(process)) Host::InjectProcess(processId);
}

void MainWindow::on_detachButton_clicked()
Expand All @@ -197,16 +193,16 @@ void MainWindow::on_detachButton_clicked()
void MainWindow::on_hookButton_clicked()
{
bool ok;
QString hookCode = QInputDialog::getText(this, "Add Hook", CodeInfoDump, QLineEdit::Normal, "", &ok);
QString hookCode = QInputDialog::getText(this, ADD_HOOK, CODE_INFODUMP, QLineEdit::Normal, "", &ok);
if (!ok) return;
if (auto hp = ParseCode(hookCode)) Host::InsertHook(GetSelectedProcessId(), hp.value());
else Host::AddConsoleOutput(L"invalid code");
else Host::AddConsoleOutput(INVALID_CODE);
}

void MainWindow::on_unhookButton_clicked()
{
QVector<HookParam> hooks = GetAllHooks(GetSelectedProcessId());
if (hooks.empty()) return Host::AddConsoleOutput(L"no hooks detected");
auto hooks = GetAllHooks(GetSelectedProcessId());
if (hooks.empty()) return Host::AddConsoleOutput(NO_HOOKS);
QStringList hookList;
for (auto hook : hooks)
hookList.push_back(
Expand All @@ -215,18 +211,18 @@ void MainWindow::on_unhookButton_clicked()
GenerateCode(hook, GetSelectedProcessId())
);
bool ok;
QString hook = QInputDialog::getItem(this, "Unhook", "Which hook to remove?", hookList, 0, false, &ok);
QString hook = QInputDialog::getItem(this, UNHOOK, REMOVE_HOOK, hookList, 0, false, &ok);
if (ok) Host::RemoveHook(GetSelectedProcessId(), hooks.at(hookList.indexOf(hook)).insertion_address);
}

void MainWindow::on_saveButton_clicked()
{
QVector<HookParam> hooks = GetAllHooks(GetSelectedProcessId());
auto hooks = GetAllHooks(GetSelectedProcessId());
QString hookList = GetFullModuleName(GetSelectedProcessId());
for (auto hook : hooks)
if (!(hook.type & HOOK_ENGINE))
hookList += " , " + GenerateCode(hook, GetSelectedProcessId());
QFile file("SavedHooks.txt");
QFile file(HOOK_SAVE_FILE);
file.open(QIODevice::Append);
file.write((hookList + "\r\n").toUtf8());
}
Expand Down
3 changes: 2 additions & 1 deletion GUI/mainwindow.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

#include "qtcommon.h"
#include "host/host.h"
#include "defs.h"
#include <QPlainTextEdit>
#include <QComboBox>
#include <QSettings>
Expand Down Expand Up @@ -53,7 +54,7 @@ private slots:
void closeEvent(QCloseEvent*);

Ui::MainWindow* ui;
QSettings settings = QSettings("Textractor.ini", QSettings::IniFormat);
QSettings settings = QSettings(CONFIG_FILE, QSettings::IniFormat);
QComboBox* processCombo;
QComboBox* ttCombo;
QPlainTextEdit* textOutput;
Expand Down
13 changes: 0 additions & 13 deletions GUI/misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,4 @@ QMultiHash<QString, DWORD> GetAllProcesses();
std::optional<HookParam> ParseCode(QString HCode);
QString GenerateCode(HookParam hp, DWORD processId);

static QString CodeInfoDump =
"Enter hook code\r\n\
/H{A|B|W|S|Q|V}[N][codepage#]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]]\r\n\
OR\r\n\
Enter read code\r\n\
/R{S|Q|V}[codepage#][*deref_offset|0]@addr\r\n\
All numbers except codepage in hexadecimal\r\n\
A/B: Shift-JIS char little/big endian\r\n\
W: UTF-16 char\r\n\
S/Q/V: Shift-JIS/UTF-16/UTF-8 string\r\n\
Negatives for data_offset/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\
* means dereference pointer+deref_offset";
#endif // MISC_H
23 changes: 18 additions & 5 deletions include/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,32 @@
// vnrhook/defs.h
// 8/23/2013 jichi

#define ITH_DLL L"vnrhook"
constexpr auto ITH_DLL = L"vnrhook";

// Pipes

#define HOOK_PIPE L"\\\\.\\pipe\\TEXTRACTOR_HOOK"
#define HOST_PIPE L"\\\\.\\pipe\\TEXTRACTOR_HOST"
constexpr auto HOOK_PIPE = L"\\\\.\\pipe\\TEXTRACTOR_HOOK";
constexpr auto HOST_PIPE = L"\\\\.\\pipe\\TEXTRACTOR_HOST";

// Sections

#define ITH_SECTION_ L"VNR_SECTION_" // _%d
constexpr auto ITH_SECTION_ = L"VNR_SECTION_"; // _%d

// Mutex

#define ITH_HOOKMAN_MUTEX_ L"VNR_HOOKMAN_" // ITH_HOOKMAN_%d
constexpr auto ITH_HOOKMAN_MUTEX_ = L"VNR_HOOKMAN_"; // ITH_HOOKMAN_%d

// Files

constexpr auto CONFIG_FILE = u8"Textractor.ini";
constexpr auto HOOK_SAVE_FILE = u8"SavedHooks.txt";
constexpr auto EXTEN_SAVE_FILE = u8"Extensions.txt";

// Settings

constexpr auto WINDOW = u8"Window";
constexpr auto DEFAULT_CODEPAGE = u8"Default_Codepage";
constexpr auto FLUSH_DELAY = u8"Flush_Delay";
constexpr auto MAX_BUFFER_SIZE = u8"Max_Buffer_Size";

// EOF
30 changes: 30 additions & 0 deletions include/text.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#pragma once

constexpr auto SELECT_PROCESS = u8"Select Process";
constexpr auto INJECT_INFO = u8"If you don't see the process you want to inject, try running with admin rights\r\n"
"You can also type in the process id";
constexpr auto ADD_HOOK = u8"Add hook";
constexpr auto CODE_INFODUMP = u8"Enter hook code\r\n"
"/H{A|B|W|S|Q|V}[N][codepage#]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]]\r\n"
"OR\r\n"
"Enter read code\r\n"
"/R{S|Q|V}[codepage#][*deref_offset|0]@addr\r\n"
"All numbers except codepage in hexadecimal\r\n"
"A/B: Shift-JIS char little/big endian\r\n"
"W: UTF-16 char\r\n"
"S/Q/V: Shift-JIS/UTF-16/UTF-8 string\r\n"
"Negatives for data_offset/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"
"* means dereference pointer+deref_offset";
constexpr auto UNHOOK = u8"Unhook";
constexpr auto REMOVE_HOOK = u8"Which hook to remove?";
constexpr auto SELECT_EXTENSION = u8"Select Extension";
constexpr auto EXTENSIONS = u8"Extensions (*.dll)";
constexpr auto ABOUT = L"Textractor beta v3.4.0 by Artikash\r\n"
"Source code and more information available under GPLv3 at https://github.com/Artikash/Textractor";
constexpr auto TOO_MANY_THREADS = L"Textractor: ERROR: too many text threads: can't create more";
constexpr auto ALREADY_INJECTED = L"Textractor: ERROR: already injected";
constexpr auto ARCHITECTURE_MISMATCH = L"Textractor: ERROR: architecture mismatch: try 32 bit Textractor instead";
constexpr auto INJECT_FAILED = L"Textractor: ERROR: couldn't inject";
constexpr auto INVALID_CODE = L"Textractor: invalid code";
constexpr auto NO_HOOKS = L"Textractor: no hooks detected";