forked from Public-Mirror/Textractor
add function based offset. rewrite hookparam processing while we're at it
This commit is contained in:
parent
3b9ca65e39
commit
f7e3bbeb02
@ -234,7 +234,7 @@ namespace Host
|
|||||||
WaitForSingleObject(pr.sectionMutex, 0);
|
WaitForSingleObject(pr.sectionMutex, 0);
|
||||||
const TextHook* hooks = (const TextHook*)pr.sectionMap;
|
const TextHook* hooks = (const TextHook*)pr.sectionMap;
|
||||||
for (int i = 0; i < MAX_HOOK; ++i)
|
for (int i = 0; i < MAX_HOOK; ++i)
|
||||||
if (hooks[i].hp.address == addr)
|
if (hooks[i].hp.insertion_address == addr)
|
||||||
ret = hooks[i].hp;
|
ret = hooks[i].hp;
|
||||||
ReleaseMutex(pr.sectionMutex);
|
ReleaseMutex(pr.sectionMutex);
|
||||||
return ret;
|
return ret;
|
||||||
@ -252,7 +252,7 @@ namespace Host
|
|||||||
WaitForSingleObject(pr.sectionMutex, 0);
|
WaitForSingleObject(pr.sectionMutex, 0);
|
||||||
const TextHook* hooks = (const TextHook*)pr.sectionMap;
|
const TextHook* hooks = (const TextHook*)pr.sectionMap;
|
||||||
for (int i = 0; i < MAX_HOOK; ++i)
|
for (int i = 0; i < MAX_HOOK; ++i)
|
||||||
if (hooks[i].hp.address == addr)
|
if (hooks[i].hp.insertion_address == addr)
|
||||||
{
|
{
|
||||||
buffer.resize(hooks[i].name_length);
|
buffer.resize(hooks[i].name_length);
|
||||||
ReadProcessMemory(pr.processHandle, hooks[i].hook_name, buffer.data(), hooks[i].name_length, nullptr);
|
ReadProcessMemory(pr.processHandle, hooks[i].hook_name, buffer.data(), hooks[i].name_length, nullptr);
|
||||||
|
@ -157,7 +157,7 @@ std::unordered_map<std::string, int64_t> MainWindow::GetInfoForExtensions(TextTh
|
|||||||
|
|
||||||
QVector<HookParam> MainWindow::GetAllHooks(DWORD processId)
|
QVector<HookParam> MainWindow::GetAllHooks(DWORD processId)
|
||||||
{
|
{
|
||||||
QSet<DWORD> addresses;
|
QSet<uint64_t> addresses;
|
||||||
QVector<HookParam> hooks;
|
QVector<HookParam> hooks;
|
||||||
for (int i = 0; i < ttCombo->count(); ++i)
|
for (int i = 0; i < ttCombo->count(); ++i)
|
||||||
{
|
{
|
||||||
@ -208,13 +208,13 @@ void MainWindow::on_unhookButton_clicked()
|
|||||||
QStringList hookList;
|
QStringList hookList;
|
||||||
for (auto hook : hooks)
|
for (auto hook : hooks)
|
||||||
hookList.push_back(
|
hookList.push_back(
|
||||||
QString::fromStdWString(Host::GetHookName(GetSelectedProcessId(), hook.address)) +
|
QString::fromStdWString(Host::GetHookName(GetSelectedProcessId(), hook.insertion_address)) +
|
||||||
": " +
|
": " +
|
||||||
GenerateCode(hook, GetSelectedProcessId())
|
GenerateCode(hook, GetSelectedProcessId())
|
||||||
);
|
);
|
||||||
bool ok;
|
bool ok;
|
||||||
QString hook = QInputDialog::getItem(this, "Unhook", "Which hook to remove?", hookList, 0, false, &ok);
|
QString hook = QInputDialog::getItem(this, "Unhook", "Which hook to remove?", hookList, 0, false, &ok);
|
||||||
if (ok) Host::RemoveHook(GetSelectedProcessId(), hooks.at(hookList.indexOf(hook)).address);
|
if (ok) Host::RemoveHook(GetSelectedProcessId(), hooks.at(hookList.indexOf(hook)).insertion_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::on_saveButton_clicked()
|
void MainWindow::on_saveButton_clicked()
|
||||||
|
221
GUI/misc.cpp
221
GUI/misc.cpp
@ -1,7 +1,7 @@
|
|||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
#include "const.h"
|
#include "const.h"
|
||||||
#include <QRegExp>
|
|
||||||
#include <Psapi.h>
|
#include <Psapi.h>
|
||||||
|
#include <QTextStream>
|
||||||
|
|
||||||
QString GetFullModuleName(DWORD processId, HMODULE module)
|
QString GetFullModuleName(DWORD processId, HMODULE module)
|
||||||
{
|
{
|
||||||
@ -32,18 +32,12 @@ QMultiHash<QString, DWORD> GetAllProcesses()
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
DWORD Hash(QString module)
|
|
||||||
{
|
|
||||||
module = module.toLower();
|
|
||||||
DWORD hash = 0;
|
|
||||||
for (auto i : module) hash = _rotr(hash, 7) + i.unicode();
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<HookParam> ParseRCode(QString RCode)
|
std::optional<HookParam> ParseRCode(QString RCode)
|
||||||
{
|
{
|
||||||
HookParam hp = {};
|
HookParam hp = {};
|
||||||
hp.type |= DIRECT_READ;
|
hp.type |= DIRECT_READ;
|
||||||
|
|
||||||
|
// {S|Q|V}
|
||||||
switch (RCode.at(0).unicode())
|
switch (RCode.at(0).unicode())
|
||||||
{
|
{
|
||||||
case L'S':
|
case L'S':
|
||||||
@ -58,25 +52,32 @@ namespace
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
RCode.remove(0, 1);
|
RCode.remove(0, 1);
|
||||||
if (RCode.at(0).unicode() == L'0') RCode.remove(0, 1);
|
|
||||||
QRegExp stringGap("^\\*(\\-?[\\dA-F]+)");
|
// [*deref_offset|0]
|
||||||
if (stringGap.indexIn(RCode) != -1)
|
if (RCode.at(0).unicode() == L'0') RCode.remove(0, 1); // Legacy
|
||||||
|
QRegularExpressionMatch deref = QRegularExpression("^\\*(\\-?[[:xdigit:]]+)").match(RCode);
|
||||||
|
if (deref.hasMatch())
|
||||||
{
|
{
|
||||||
hp.index = stringGap.cap(1).toInt(nullptr, 16);
|
|
||||||
RCode.remove(0, stringGap.cap(0).length());
|
|
||||||
hp.type |= DATA_INDIRECT;
|
hp.type |= DATA_INDIRECT;
|
||||||
|
hp.index = deref.captured(1).toInt(nullptr, 16);
|
||||||
|
RCode.remove(0, deref.captured(0).length());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RCode.at(0).unicode() != L'@') return {};
|
if (RCode.at(0).unicode() != L'@') return {};
|
||||||
RCode.remove(0, 1);
|
RCode.remove(0, 1);
|
||||||
QRegExp address("[\\dA-F]+$");
|
|
||||||
if (address.indexIn(RCode) == -1) return {};
|
// @addr
|
||||||
hp.address = address.cap(0).toULongLong(nullptr, 16);
|
QRegularExpressionMatch address = QRegularExpression("^@([[:xdigit:]]+)$").match(RCode);
|
||||||
|
if (!address.hasMatch()) return {};
|
||||||
|
hp.address = address.captured(1).toULongLong(nullptr, 16);
|
||||||
return hp;
|
return hp;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<HookParam> ParseHCode(QString HCode)
|
std::optional<HookParam> ParseHCode(QString HCode)
|
||||||
{
|
{
|
||||||
HookParam hp = {};
|
HookParam hp = {};
|
||||||
|
|
||||||
|
// {A|B|W|S|Q|V}
|
||||||
switch (HCode.at(0).unicode())
|
switch (HCode.at(0).unicode())
|
||||||
{
|
{
|
||||||
case L'S':
|
case L'S':
|
||||||
@ -103,139 +104,141 @@ namespace
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
HCode.remove(0, 1);
|
HCode.remove(0, 1);
|
||||||
|
|
||||||
|
// [N]
|
||||||
if (HCode.at(0).unicode() == L'N')
|
if (HCode.at(0).unicode() == L'N')
|
||||||
{
|
{
|
||||||
hp.type |= NO_CONTEXT;
|
hp.type |= NO_CONTEXT;
|
||||||
HCode.remove(0, 1);
|
HCode.remove(0, 1);
|
||||||
}
|
}
|
||||||
QRegExp dataOffset("^\\-?[\\dA-F]+");
|
|
||||||
if (dataOffset.indexIn(HCode) == -1) return {};
|
// data_offset
|
||||||
hp.offset = dataOffset.cap(0).toInt(nullptr, 16);
|
QRegularExpressionMatch dataOffset = QRegularExpression("^\\-?[[:xdigit:]]+").match(HCode);
|
||||||
HCode.remove(0, dataOffset.cap(0).length());
|
if (!dataOffset.hasMatch()) return {};
|
||||||
QRegExp dataIndirect("^\\*(\\-?[\\dA-F]+)");
|
hp.offset = dataOffset.captured(0).toInt(nullptr, 16);
|
||||||
if (dataIndirect.indexIn(HCode) != -1)
|
HCode.remove(0, dataOffset.captured(0).length());
|
||||||
|
|
||||||
|
// [*deref_offset1]
|
||||||
|
QRegularExpressionMatch deref1 = QRegularExpression("^\\*(\\-?[[:xdigit:]]+)").match(HCode);
|
||||||
|
if (deref1.hasMatch())
|
||||||
{
|
{
|
||||||
hp.type |= DATA_INDIRECT;
|
hp.type |= DATA_INDIRECT;
|
||||||
hp.index = dataIndirect.cap(1).toInt(nullptr, 16);
|
hp.index = deref1.captured(1).toInt(nullptr, 16);
|
||||||
HCode.remove(0, dataIndirect.cap(0).length());
|
HCode.remove(0, deref1.captured(0).length());
|
||||||
}
|
}
|
||||||
QRegExp split("^\\:(\\-?[\\dA-F]+)");
|
|
||||||
if (split.indexIn(HCode) != -1)
|
// [:split_offset[*deref_offset2]]
|
||||||
|
QRegularExpressionMatch splitOffset = QRegularExpression("^\\:(\\-?[[:xdigit:]]+)").match(HCode);
|
||||||
|
if (splitOffset.hasMatch())
|
||||||
{
|
{
|
||||||
hp.type |= USING_SPLIT;
|
hp.type |= USING_SPLIT;
|
||||||
hp.split = split.cap(1).toInt(nullptr, 16);
|
hp.split = splitOffset.captured(1).toInt(nullptr, 16);
|
||||||
HCode.remove(0, split.cap(0).length());
|
HCode.remove(0, splitOffset.captured(0).length());
|
||||||
QRegExp splitIndirect("^\\*(\\-?[\\dA-F]+)");
|
|
||||||
if (splitIndirect.indexIn(HCode) != -1)
|
QRegularExpressionMatch deref2 = QRegularExpression("^\\*(\\-?[[:xdigit:]]+)").match(HCode);
|
||||||
|
if (deref2.hasMatch())
|
||||||
{
|
{
|
||||||
hp.type |= SPLIT_INDIRECT;
|
hp.type |= SPLIT_INDIRECT;
|
||||||
hp.split_index = splitIndirect.cap(1).toInt(nullptr, 16);
|
hp.split_index = deref2.captured(1).toInt(nullptr, 16);
|
||||||
HCode.remove(0, splitIndirect.cap(0).length());
|
HCode.remove(0, deref2.captured(0).length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (HCode.at(0).unicode() != L'@') return {};
|
|
||||||
HCode.remove(0, 1);
|
// @addr[:module[:func]]
|
||||||
QRegExp address("^([\\dA-F]+):?");
|
QStringList addressPieces = HCode.split(":");
|
||||||
if (address.indexIn(HCode) == -1) return {};
|
QRegularExpressionMatch address = QRegularExpression("^@([[:xdigit:]]+)$").match(addressPieces.at(0));
|
||||||
hp.address = address.cap(1).toULongLong(nullptr, 16);
|
if (!address.hasMatch()) return {};
|
||||||
HCode.remove(address.cap(0));
|
hp.address = address.captured(1).toULongLong(nullptr, 16);
|
||||||
if (HCode.length())
|
if (addressPieces.size() > 1)
|
||||||
{
|
{
|
||||||
hp.type |= MODULE_OFFSET;
|
hp.type |= MODULE_OFFSET;
|
||||||
hp.module = Hash(HCode);
|
wcscpy_s<MAX_MODULE_SIZE>(hp.module, addressPieces.at(1).toStdWString().c_str());
|
||||||
}
|
}
|
||||||
if (hp.offset < 0)
|
if (addressPieces.size() > 2)
|
||||||
hp.offset -= 4;
|
{
|
||||||
if (hp.split < 0)
|
hp.type |= FUNCTION_OFFSET;
|
||||||
hp.split -= 4;
|
strcpy_s<MAX_MODULE_SIZE>(hp.function, addressPieces.at(2).toStdString().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITH has registers offset by 4 vs AGTH: need this to correct
|
||||||
|
if (hp.offset < 0) hp.offset -= 4;
|
||||||
|
if (hp.split < 0) hp.split -= 4;
|
||||||
|
|
||||||
return hp;
|
return hp;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GenerateHCode(HookParam hp, DWORD processId)
|
QString GenerateHCode(HookParam hp, DWORD processId)
|
||||||
{
|
{
|
||||||
QString code = "/H";
|
QString HCode = "/H";
|
||||||
|
QTextStream codeBuilder(&HCode);
|
||||||
|
codeBuilder.setIntegerBase(16);
|
||||||
|
codeBuilder.setNumberFlags(QTextStream::UppercaseDigits);
|
||||||
|
|
||||||
if (hp.type & USING_UNICODE)
|
if (hp.type & USING_UNICODE)
|
||||||
{
|
{
|
||||||
if (hp.type & USING_STRING)
|
if (hp.type & USING_STRING) codeBuilder << "Q";
|
||||||
code += "Q";
|
else codeBuilder << "W";
|
||||||
else
|
|
||||||
code += "W";
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (hp.type & USING_UTF8)
|
if (hp.type & USING_UTF8) codeBuilder << "V";
|
||||||
code += "V";
|
else if (hp.type & USING_STRING) codeBuilder << "S";
|
||||||
else if (hp.type & USING_STRING)
|
else if (hp.type & BIG_ENDIAN) codeBuilder << "A";
|
||||||
code += "S";
|
else codeBuilder << "B";
|
||||||
else if (hp.type & BIG_ENDIAN)
|
|
||||||
code += "A";
|
|
||||||
else
|
|
||||||
code += "B";
|
|
||||||
}
|
}
|
||||||
if (hp.type & NO_CONTEXT)
|
if (hp.type & NO_CONTEXT) codeBuilder << "N";
|
||||||
code += "N";
|
|
||||||
if (hp.offset < 0) hp.offset += 4;
|
if (hp.offset < 0) hp.offset += 4;
|
||||||
if (hp.split < 0) hp.split += 4;
|
if (hp.split < 0) hp.split += 4;
|
||||||
if (hp.offset < 0)
|
|
||||||
code += "-" + QString::number(-hp.offset, 16);
|
codeBuilder << hp.offset;
|
||||||
else
|
if (hp.type & DATA_INDIRECT) codeBuilder << "*" << hp.index;
|
||||||
code += QString::number(hp.offset, 16);
|
if (hp.type & USING_SPLIT) codeBuilder << ":" << hp.split;
|
||||||
if (hp.type & DATA_INDIRECT)
|
if (hp.type & SPLIT_INDIRECT) codeBuilder << "*" << hp.split_index;
|
||||||
|
|
||||||
|
// Attempt to make the address relative
|
||||||
|
if (!(hp.type & MODULE_OFFSET))
|
||||||
{
|
{
|
||||||
if (hp.index < 0)
|
HANDLE processHandle;
|
||||||
code += "*-" + QString::number(-hp.index, 16);
|
if (!(processHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))) goto fin;
|
||||||
else
|
MEMORY_BASIC_INFORMATION info;
|
||||||
code += "*" + QString::number(hp.index, 16);
|
if (!VirtualQueryEx(processHandle, (LPCVOID)hp.address, &info, sizeof(info))) goto fin;
|
||||||
|
QString moduleName = GetModuleName(processId, (HMODULE)info.AllocationBase);
|
||||||
|
if (moduleName.size() == 0) goto fin;
|
||||||
|
hp.type |= MODULE_OFFSET;
|
||||||
|
hp.address -= (uint64_t)info.AllocationBase;
|
||||||
|
wcscpy_s<MAX_MODULE_SIZE>(hp.module, moduleName.toStdWString().c_str());
|
||||||
}
|
}
|
||||||
if (hp.type & USING_SPLIT)
|
|
||||||
{
|
fin:
|
||||||
if (hp.split < 0)
|
codeBuilder << "@" << hp.address;
|
||||||
code += ":-" + QString::number(-hp.split, 16);
|
if (hp.type & MODULE_OFFSET) codeBuilder << ":" << QString::fromWCharArray(hp.module);
|
||||||
else
|
if (hp.type & FUNCTION_OFFSET) codeBuilder << ":" << hp.function;
|
||||||
code += ":" + QString::number(hp.split, 16);
|
|
||||||
}
|
return HCode;
|
||||||
if (hp.type & SPLIT_INDIRECT)
|
|
||||||
{
|
|
||||||
if (hp.split_index < 0)
|
|
||||||
code += "*-" + QString::number(-hp.split_index, 16);
|
|
||||||
else
|
|
||||||
code += "*" + QString::number(hp.split_index, 16);
|
|
||||||
}
|
|
||||||
code += "@";
|
|
||||||
QString badCode = (code + QString::number(hp.address, 16)).toUpper();
|
|
||||||
HANDLE processHandle;
|
|
||||||
if (!(processHandle = OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, processId))) return badCode;
|
|
||||||
MEMORY_BASIC_INFORMATION info;
|
|
||||||
if (!VirtualQueryEx(processHandle, (LPCVOID)hp.address, &info, sizeof(info))) return badCode;
|
|
||||||
QString moduleName = GetModuleName(processId, (HMODULE)info.AllocationBase);
|
|
||||||
if (moduleName.size() == 0) return badCode;
|
|
||||||
code += QString::number(hp.address - (DWORD)info.AllocationBase, 16) + ":";
|
|
||||||
code = code.toUpper();
|
|
||||||
code += moduleName;
|
|
||||||
return code;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QString GenerateRCode(HookParam hp)
|
QString GenerateRCode(HookParam hp)
|
||||||
{
|
{
|
||||||
QString code = "/R";
|
QString RCode = "/R";
|
||||||
if (hp.type & USING_UNICODE)
|
QTextStream codeBuilder(&RCode);
|
||||||
code += "Q";
|
codeBuilder.setIntegerBase(16);
|
||||||
else if (hp.type & USING_UTF8)
|
codeBuilder.setNumberFlags(QTextStream::UppercaseDigits);
|
||||||
code += "V";
|
|
||||||
else
|
if (hp.type & USING_UNICODE) codeBuilder << "Q";
|
||||||
code += "S";
|
else if (hp.type & USING_UTF8) codeBuilder << "V";
|
||||||
if (hp.type & DATA_INDIRECT)
|
else codeBuilder << "S";
|
||||||
code += "*" + QString::number(hp.index, 16);
|
|
||||||
//code += QString::number(hp.offset, 16);
|
if (hp.type & DATA_INDIRECT) codeBuilder << "*" << hp.index;
|
||||||
code += "@";
|
|
||||||
code += QString::number(hp.address, 16);
|
codeBuilder << "@" << hp.address;
|
||||||
return code.toUpper();
|
|
||||||
|
return RCode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<HookParam> ParseCode(QString code)
|
std::optional<HookParam> ParseCode(QString code)
|
||||||
{
|
{
|
||||||
code = code.toUpper();
|
|
||||||
if (code.startsWith("/H")) return ParseHCode(code.remove(0, 2));
|
if (code.startsWith("/H")) return ParseHCode(code.remove(0, 2));
|
||||||
else if (code.startsWith("/R")) return ParseRCode(code.remove(0, 2));
|
else if (code.startsWith("/R")) return ParseRCode(code.remove(0, 2));
|
||||||
else return {};
|
else return {};
|
||||||
|
@ -13,7 +13,7 @@ QString GenerateCode(HookParam hp, DWORD processId);
|
|||||||
|
|
||||||
static QString CodeInfoDump =
|
static QString CodeInfoDump =
|
||||||
"Enter hook code\r\n\
|
"Enter hook code\r\n\
|
||||||
/H{A|B|W|S|Q|V}[N]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module]\r\n\
|
/H{A|B|W|S|Q|V}[N]data_offset[*deref_offset1][:split_offset[*deref_offset2]]@addr[:module[:func]]\r\n\
|
||||||
OR\r\n\
|
OR\r\n\
|
||||||
Enter read code\r\n\
|
Enter read code\r\n\
|
||||||
/R{S|Q|V}[*deref_offset|0]@addr\r\n\
|
/R{S|Q|V}[*deref_offset|0]@addr\r\n\
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
// 8/23/2013 jichi
|
// 8/23/2013 jichi
|
||||||
// Branch: ITH/common.h, rev 128
|
// Branch: ITH/common.h, rev 128
|
||||||
|
|
||||||
enum { MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 0x1000, SHIFT_JIS = 932, MAX_THREAD_COUNT };
|
enum { MESSAGE_SIZE = 500, PIPE_BUFFER_SIZE = 0x1000, SHIFT_JIS = 932, MAX_THREAD_COUNT = 250, MAX_MODULE_SIZE = 120 };
|
||||||
|
|
||||||
// jichi 375/2014: Add offset of pusha/pushad
|
// jichi 375/2014: Add offset of pusha/pushad
|
||||||
// http://faydoc.tripod.com/cpu/pushad.htm
|
// http://faydoc.tripod.com/cpu/pushad.htm
|
||||||
@ -50,8 +50,8 @@ enum HookParamType : unsigned long
|
|||||||
DATA_INDIRECT = 0x8,
|
DATA_INDIRECT = 0x8,
|
||||||
USING_SPLIT = 0x10, // aware of split time?
|
USING_SPLIT = 0x10, // aware of split time?
|
||||||
SPLIT_INDIRECT = 0x20,
|
SPLIT_INDIRECT = 0x20,
|
||||||
MODULE_OFFSET = 0x40, // do hash module, and the address is relative to module
|
MODULE_OFFSET = 0x40, // address is relative to module
|
||||||
//FUNCTION_OFFSET = 0x80, // do hash function, and the address is relative to funccion
|
FUNCTION_OFFSET = 0x80, // address is relative to function
|
||||||
USING_UTF8 = 0x100,
|
USING_UTF8 = 0x100,
|
||||||
NO_CONTEXT = 0x400,
|
NO_CONTEXT = 0x400,
|
||||||
HOOK_EMPTY = 0x800,
|
HOOK_EMPTY = 0x800,
|
||||||
|
@ -11,12 +11,14 @@ struct HookParam
|
|||||||
typedef bool(*filter_fun_t)(LPVOID str, DWORD *len, HookParam *hp, BYTE index); // jichi 10/24/2014: Add filter function. Return true if skip the text
|
typedef bool(*filter_fun_t)(LPVOID str, DWORD *len, HookParam *hp, BYTE index); // jichi 10/24/2014: Add filter function. Return true if skip the text
|
||||||
typedef bool(*hook_fun_t)(DWORD esp, HookParam *hp); // jichi 10/24/2014: Add generic hook function, return false if stop execution.
|
typedef bool(*hook_fun_t)(DWORD esp, HookParam *hp); // jichi 10/24/2014: Add generic hook function, return false if stop execution.
|
||||||
|
|
||||||
uint64_t address; // absolute or relative address
|
uint64_t insertion_address; // absolute address
|
||||||
|
uint64_t address; // absolute or relative address (not changed by TextHook)
|
||||||
int offset, // offset of the data in the memory
|
int offset, // offset of the data in the memory
|
||||||
index, // deref_offset1
|
index, // deref_offset1
|
||||||
split, // offset of the split character
|
split, // offset of the split character
|
||||||
split_index; // deref_offset2
|
split_index; // deref_offset2
|
||||||
DWORD module; // hash of the module
|
wchar_t module[MAX_MODULE_SIZE];
|
||||||
|
char function[MAX_MODULE_SIZE];
|
||||||
DWORD type; // flags
|
DWORD type; // flags
|
||||||
WORD length_offset; // index of the string length
|
WORD length_offset; // index of the string length
|
||||||
DWORD user_value; // 7/20/2014: jichi additional parameters for PSP games
|
DWORD user_value; // 7/20/2014: jichi additional parameters for PSP games
|
||||||
@ -24,8 +26,6 @@ struct HookParam
|
|||||||
text_fun_t text_fun;
|
text_fun_t text_fun;
|
||||||
filter_fun_t filter_fun;
|
filter_fun_t filter_fun;
|
||||||
hook_fun_t hook_fun;
|
hook_fun_t hook_fun;
|
||||||
|
|
||||||
HANDLE readerHandle; // Artikash 8/4/2018: handle for reader thread
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ThreadParam // From hook, used internally by host as well
|
struct ThreadParam // From hook, used internally by host as well
|
||||||
|
@ -40,29 +40,6 @@ namespace { // unnamed
|
|||||||
0xe9 // jmp @original
|
0xe9 // jmp @original
|
||||||
};
|
};
|
||||||
|
|
||||||
DWORD Hash(std::wstring module)
|
|
||||||
{
|
|
||||||
DWORD hash = 0;
|
|
||||||
for (auto i : module) hash = _rotr(hash, 7) + i;
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
//copy original instruction
|
|
||||||
//jmp back
|
|
||||||
DWORD GetModuleBase(DWORD hash)
|
|
||||||
{
|
|
||||||
HMODULE allModules[1000];
|
|
||||||
DWORD size;
|
|
||||||
EnumProcessModules(GetCurrentProcess(), allModules, sizeof(allModules), &size);
|
|
||||||
wchar_t name[MAX_PATH];
|
|
||||||
for (int i = 0; i < size / sizeof(HMODULE); ++i)
|
|
||||||
{
|
|
||||||
GetModuleFileNameW(allModules[i], name, MAX_PATH);
|
|
||||||
_wcslwr(name);
|
|
||||||
if (Hash(wcsrchr(name, L'\\') + 1) == hash) return (DWORD)allModules[i];
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
|
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
|
||||||
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
|
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
|
||||||
@ -76,6 +53,7 @@ namespace { // unnamed
|
|||||||
retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
|
retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
const BYTE common_hook[] = {
|
const BYTE common_hook[] = {
|
||||||
0x9c, // push rflags
|
0x9c, // push rflags
|
||||||
@ -160,7 +138,7 @@ DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn)
|
|||||||
BYTE pbData[PIPE_BUFFER_SIZE];
|
BYTE pbData[PIPE_BUFFER_SIZE];
|
||||||
DWORD dwType = hp.type;
|
DWORD dwType = hp.type;
|
||||||
|
|
||||||
dwAddr = hp.address;
|
dwAddr = hp.insertion_address;
|
||||||
|
|
||||||
/** jichi 12/24/2014
|
/** jichi 12/24/2014
|
||||||
* @param addr function address
|
* @param addr function address
|
||||||
@ -254,19 +232,19 @@ bool TextHook::InsertHookCode()
|
|||||||
|
|
||||||
bool TextHook::UnsafeInsertHookCode()
|
bool TextHook::UnsafeInsertHookCode()
|
||||||
{
|
{
|
||||||
if (hp.module && (hp.type & MODULE_OFFSET)) // Map hook offset to real address.
|
if (hp.type & MODULE_OFFSET) // Map hook offset to real address.
|
||||||
{
|
if (hp.type & FUNCTION_OFFSET)
|
||||||
if (DWORD base = GetModuleBase(hp.module)) hp.address += base;
|
if (FARPROC function = GetProcAddress(GetModuleHandleW(hp.module), hp.function)) hp.insertion_address += (uint64_t)function;
|
||||||
|
else return ConsoleOutput("Textractor: UnsafeInsertHookCode: FAILED: function not present"), false;
|
||||||
|
else if (HMODULE moduleBase = GetModuleHandleW(hp.module)) hp.insertion_address += (uint64_t)moduleBase;
|
||||||
else return ConsoleOutput("Textractor: UnsafeInsertHookCode: FAILED: module not present"), false;
|
else return ConsoleOutput("Textractor: UnsafeInsertHookCode: FAILED: module not present"), false;
|
||||||
hp.type &= ~MODULE_OFFSET;
|
|
||||||
}
|
|
||||||
|
|
||||||
BYTE* original;
|
BYTE* original;
|
||||||
insert:
|
insert:
|
||||||
if (MH_STATUS err = MH_CreateHook((void*)hp.address, (void*)trampoline, (void**)&original))
|
if (MH_STATUS err = MH_CreateHook((void*)hp.insertion_address, (void*)trampoline, (void**)&original))
|
||||||
if (err == MH_ERROR_ALREADY_CREATED)
|
if (err == MH_ERROR_ALREADY_CREATED)
|
||||||
{
|
{
|
||||||
RemoveHook(hp.address);
|
RemoveHook(hp.insertion_address);
|
||||||
goto insert; // FIXME: i'm too lazy to do this properly right now...
|
goto insert; // FIXME: i'm too lazy to do this properly right now...
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -293,7 +271,7 @@ bool TextHook::UnsafeInsertHookCode()
|
|||||||
//memcpy(trampoline + 46, &sendPtr, sizeof(sendPtr));
|
//memcpy(trampoline + 46, &sendPtr, sizeof(sendPtr));
|
||||||
//memcpy(trampoline + sizeof(common_hook) - 8, &original, sizeof(void*));
|
//memcpy(trampoline + sizeof(common_hook) - 8, &original, sizeof(void*));
|
||||||
|
|
||||||
if (MH_EnableHook((void*)hp.address) != MH_OK) return false;
|
if (MH_EnableHook((void*)hp.insertion_address) != MH_OK) return false;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@ -305,15 +283,15 @@ DWORD WINAPI ReaderThread(LPVOID hookPtr)
|
|||||||
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
||||||
unsigned int changeCount = 0;
|
unsigned int changeCount = 0;
|
||||||
int dataLen = 0;
|
int dataLen = 0;
|
||||||
const void* currentAddress = (void*)hook->hp.address;
|
const void* currentAddress = (void*)hook->hp.insertion_address;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (!IthGetMemoryRange((void*)hook->hp.address, nullptr, nullptr))
|
if (!IthGetMemoryRange((void*)hook->hp.insertion_address, nullptr, nullptr))
|
||||||
{
|
{
|
||||||
ConsoleOutput("Textractor: can't read desired address");
|
ConsoleOutput("Textractor: can't read desired address");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (hook->hp.type & DATA_INDIRECT) currentAddress = *((char**)hook->hp.address + hook->hp.index);
|
if (hook->hp.type & DATA_INDIRECT) currentAddress = *((char**)hook->hp.insertion_address + hook->hp.index);
|
||||||
if (!IthGetMemoryRange(currentAddress, nullptr, nullptr))
|
if (!IthGetMemoryRange(currentAddress, nullptr, nullptr))
|
||||||
{
|
{
|
||||||
ConsoleOutput("Textractor: can't read desired address");
|
ConsoleOutput("Textractor: can't read desired address");
|
||||||
@ -336,7 +314,7 @@ DWORD WINAPI ReaderThread(LPVOID hookPtr)
|
|||||||
else
|
else
|
||||||
dataLen = strlen((const char*)currentAddress);
|
dataLen = strlen((const char*)currentAddress);
|
||||||
|
|
||||||
*(ThreadParam*)buffer = { GetCurrentProcessId(), hook->hp.address, 0, 0 };
|
*(ThreadParam*)buffer = { GetCurrentProcessId(), hook->hp.insertion_address, 0, 0 };
|
||||||
memcpy(buffer + sizeof(ThreadParam), currentAddress, dataLen + 1);
|
memcpy(buffer + sizeof(ThreadParam), currentAddress, dataLen + 1);
|
||||||
DWORD unused;
|
DWORD unused;
|
||||||
WriteFile(::hookPipe, buffer, dataLen + sizeof(ThreadParam), &unused, nullptr);
|
WriteFile(::hookPipe, buffer, dataLen + sizeof(ThreadParam), &unused, nullptr);
|
||||||
@ -349,7 +327,7 @@ DWORD WINAPI ReaderThread(LPVOID hookPtr)
|
|||||||
bool TextHook::InsertReadCode()
|
bool TextHook::InsertReadCode()
|
||||||
{
|
{
|
||||||
//RemoveHook(hp.address); // Artikash 8/25/2018: clear existing
|
//RemoveHook(hp.address); // Artikash 8/25/2018: clear existing
|
||||||
hp.readerHandle = CreateThread(nullptr, 0, ReaderThread, this, 0, nullptr);
|
readerHandle = CreateThread(nullptr, 0, ReaderThread, this, 0, nullptr);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -357,6 +335,7 @@ void TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag)
|
|||||||
{
|
{
|
||||||
WaitForSingleObject(hmMutex, 0);
|
WaitForSingleObject(hmMutex, 0);
|
||||||
hp = h;
|
hp = h;
|
||||||
|
hp.insertion_address = hp.address;
|
||||||
hp.type |= set_flag;
|
hp.type |= set_flag;
|
||||||
if (name && name != hook_name) SetHookName(name);
|
if (name && name != hook_name) SetHookName(name);
|
||||||
ReleaseMutex(hmMutex);
|
ReleaseMutex(hmMutex);
|
||||||
@ -364,14 +343,14 @@ void TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag)
|
|||||||
|
|
||||||
void TextHook::RemoveHookCode()
|
void TextHook::RemoveHookCode()
|
||||||
{
|
{
|
||||||
MH_DisableHook((void*)hp.address);
|
MH_DisableHook((void*)hp.insertion_address);
|
||||||
MH_RemoveHook((void*)hp.address);
|
MH_RemoveHook((void*)hp.insertion_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextHook::RemoveReadCode()
|
void TextHook::RemoveReadCode()
|
||||||
{
|
{
|
||||||
TerminateThread(hp.readerHandle, 0);
|
TerminateThread(readerHandle, 0);
|
||||||
CloseHandle(hp.readerHandle);
|
CloseHandle(readerHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TextHook::ClearHook()
|
void TextHook::ClearHook()
|
||||||
@ -380,7 +359,7 @@ void TextHook::ClearHook()
|
|||||||
if (hook_name) ConsoleOutput(("Textractor: removing hook: " + std::string(hook_name)).c_str());
|
if (hook_name) ConsoleOutput(("Textractor: removing hook: " + std::string(hook_name)).c_str());
|
||||||
if (hp.type & DIRECT_READ) RemoveReadCode();
|
if (hp.type & DIRECT_READ) RemoveReadCode();
|
||||||
else RemoveHookCode();
|
else RemoveHookCode();
|
||||||
NotifyHookRemove(hp.address);
|
NotifyHookRemove(hp.insertion_address);
|
||||||
if (hook_name) delete[] hook_name;
|
if (hook_name) delete[] hook_name;
|
||||||
memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH
|
memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH
|
||||||
ReleaseMutex(hmMutex);
|
ReleaseMutex(hmMutex);
|
||||||
|
@ -30,6 +30,7 @@ public:
|
|||||||
LPSTR hook_name;
|
LPSTR hook_name;
|
||||||
int name_length;
|
int name_length;
|
||||||
BYTE trampoline[120];
|
BYTE trampoline[120];
|
||||||
|
HANDLE readerHandle;
|
||||||
|
|
||||||
bool InsertHook();
|
bool InsertHook();
|
||||||
void InitHook(const HookParam &hp, LPCSTR name = 0, WORD set_flag = 0);
|
void InitHook(const HookParam &hp, LPCSTR name = 0, WORD set_flag = 0);
|
||||||
|
@ -49,7 +49,7 @@ BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID)
|
|||||||
{
|
{
|
||||||
::running = false;
|
::running = false;
|
||||||
MH_Uninitialize();
|
MH_Uninitialize();
|
||||||
for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++) if (man->hp.address) man->ClearHook();
|
for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++) if (man->hp.insertion_address) man->ClearHook();
|
||||||
//if (ith_has_section)
|
//if (ith_has_section)
|
||||||
UnmapViewOfFile(::hookman);
|
UnmapViewOfFile(::hookman);
|
||||||
|
|
||||||
@ -83,7 +83,7 @@ void NewHook(const HookParam &hp, LPCSTR lpname, DWORD flag)
|
|||||||
void RemoveHook(uint64_t addr)
|
void RemoveHook(uint64_t addr)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < MAX_HOOK; i++)
|
for (int i = 0; i < MAX_HOOK; i++)
|
||||||
if (abs((long long)(::hookman[i].hp.address - addr)) < 9)
|
if (abs((long long)(::hookman[i].hp.insertion_address - addr)) < 9)
|
||||||
{
|
{
|
||||||
::hookman[i].ClearHook();
|
::hookman[i].ClearHook();
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user