use a default searchparam unless user specifies they want custom settings
This commit is contained in:
parent
ef90382bbb
commit
88b797cd33
@ -105,7 +105,7 @@ namespace
|
|||||||
auto& OnHookFound = processRecordsByIds->at(processId).OnHookFound;
|
auto& OnHookFound = processRecordsByIds->at(processId).OnHookFound;
|
||||||
std::wstring wide = info.text;
|
std::wstring wide = info.text;
|
||||||
if (wide.size() > STRING) OnHookFound(info.hp, info.text);
|
if (wide.size() > STRING) OnHookFound(info.hp, info.text);
|
||||||
info.hp.type = USING_STRING;
|
info.hp.type &= ~USING_UNICODE;
|
||||||
if (auto converted = Util::StringToWideString((char*)info.text, Host::defaultCodepage))
|
if (auto converted = Util::StringToWideString((char*)info.text, Host::defaultCodepage))
|
||||||
if (converted->size() > STRING) OnHookFound(info.hp, converted.value());
|
if (converted->size() > STRING) OnHookFound(info.hp, converted.value());
|
||||||
info.hp.codepage = CP_UTF8;
|
info.hp.codepage = CP_UTF8;
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
#include <QCheckBox>
|
#include <QCheckBox>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QListWidget>
|
#include <QListWidget>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
#include <QInputDialog>
|
#include <QInputDialog>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
@ -21,7 +22,7 @@ extern const char* DETACH;
|
|||||||
extern const char* ADD_HOOK;
|
extern const char* ADD_HOOK;
|
||||||
extern const char* REMOVE_HOOKS;
|
extern const char* REMOVE_HOOKS;
|
||||||
extern const char* SAVE_HOOKS;
|
extern const char* SAVE_HOOKS;
|
||||||
extern const char* FIND_HOOKS;
|
extern const char* SEARCH_FOR_HOOKS;
|
||||||
extern const char* SETTINGS;
|
extern const char* SETTINGS;
|
||||||
extern const char* EXTENSIONS;
|
extern const char* EXTENSIONS;
|
||||||
extern const char* SELECT_PROCESS;
|
extern const char* SELECT_PROCESS;
|
||||||
@ -30,6 +31,7 @@ extern const char* SEARCH_GAME;
|
|||||||
extern const char* PROCESSES;
|
extern const char* PROCESSES;
|
||||||
extern const char* CODE_INFODUMP;
|
extern const char* CODE_INFODUMP;
|
||||||
extern const char* HOOK_SEARCH_UNSTABLE_WARNING;
|
extern const char* HOOK_SEARCH_UNSTABLE_WARNING;
|
||||||
|
extern const char* SEARCH_CJK;
|
||||||
extern const char* SEARCH_PATTERN;
|
extern const char* SEARCH_PATTERN;
|
||||||
extern const char* SEARCH_DURATION;
|
extern const char* SEARCH_DURATION;
|
||||||
extern const char* PATTERN_OFFSET;
|
extern const char* PATTERN_OFFSET;
|
||||||
@ -59,14 +61,14 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||||||
extenWindow(new ExtenWindow(this))
|
extenWindow(new ExtenWindow(this))
|
||||||
{
|
{
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
for (auto[text, slot] : Array<std::tuple<QString, void(MainWindow::*)()>>{
|
for (auto [text, slot] : Array<std::tuple<QString, void(MainWindow::*)()>>{
|
||||||
{ ATTACH, &MainWindow::AttachProcess },
|
{ ATTACH, &MainWindow::AttachProcess },
|
||||||
{ LAUNCH, &MainWindow::LaunchProcess },
|
{ LAUNCH, &MainWindow::LaunchProcess },
|
||||||
{ DETACH, &MainWindow::DetachProcess },
|
{ DETACH, &MainWindow::DetachProcess },
|
||||||
{ ADD_HOOK, &MainWindow::AddHook },
|
{ ADD_HOOK, &MainWindow::AddHook },
|
||||||
{ REMOVE_HOOKS, &MainWindow::RemoveHooks },
|
{ REMOVE_HOOKS, &MainWindow::RemoveHooks },
|
||||||
{ SAVE_HOOKS, &MainWindow::SaveHooks },
|
{ SAVE_HOOKS, &MainWindow::SaveHooks },
|
||||||
{ FIND_HOOKS, &MainWindow::FindHooks },
|
{ SEARCH_FOR_HOOKS, &MainWindow::FindHooks },
|
||||||
{ SETTINGS, &MainWindow::Settings },
|
{ SETTINGS, &MainWindow::Settings },
|
||||||
{ EXTENSIONS, &MainWindow::Extensions }
|
{ EXTENSIONS, &MainWindow::Extensions }
|
||||||
})
|
})
|
||||||
@ -325,9 +327,10 @@ void MainWindow::RemoveHooks()
|
|||||||
}
|
}
|
||||||
auto hookList = new QListWidget(this);
|
auto hookList = new QListWidget(this);
|
||||||
hookList->setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint);
|
hookList->setWindowFlags(Qt::Window | Qt::WindowCloseButtonHint);
|
||||||
|
hookList->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
hookList->setMinimumSize({ 300, 50 });
|
hookList->setMinimumSize({ 300, 50 });
|
||||||
hookList->setWindowTitle(DOUBLE_CLICK_TO_REMOVE_HOOK);
|
hookList->setWindowTitle(DOUBLE_CLICK_TO_REMOVE_HOOK);
|
||||||
for (auto[address, hp] : hooks)
|
for (auto [address, hp] : hooks)
|
||||||
new QListWidgetItem(QString(hp.name) + "@" + QString::number(address, 16), hookList);
|
new QListWidgetItem(QString(hp.name) + "@" + QString::number(address, 16), hookList);
|
||||||
connect(hookList, &QListWidget::itemDoubleClicked, [processId, hookList](QListWidgetItem* item)
|
connect(hookList, &QListWidget::itemDoubleClicked, [processId, hookList](QListWidgetItem* item)
|
||||||
{
|
{
|
||||||
@ -364,120 +367,123 @@ void MainWindow::SaveHooks()
|
|||||||
|
|
||||||
void MainWindow::FindHooks()
|
void MainWindow::FindHooks()
|
||||||
{
|
{
|
||||||
QMessageBox::information(this, FIND_HOOKS, HOOK_SEARCH_UNSTABLE_WARNING);
|
QMessageBox::information(this, SEARCH_FOR_HOOKS, HOOK_SEARCH_UNSTABLE_WARNING);
|
||||||
struct : QDialog
|
|
||||||
|
DWORD processId = GetSelectedProcessId();
|
||||||
|
SearchParam sp = {};
|
||||||
|
bool customSettings = false;
|
||||||
|
std::wregex filter(L".");
|
||||||
|
|
||||||
|
QDialog dialog(this, Qt::WindowCloseButtonHint);
|
||||||
|
QFormLayout layout(&dialog);
|
||||||
|
QCheckBox cjkCheckbox(&dialog);
|
||||||
|
layout.addRow(SEARCH_CJK, &cjkCheckbox);
|
||||||
|
QDialogButtonBox confirm(QDialogButtonBox::Ok | QDialogButtonBox::Help, &dialog);
|
||||||
|
layout.addRow(&confirm);
|
||||||
|
confirm.button(QDialogButtonBox::Ok)->setText(START_HOOK_SEARCH);
|
||||||
|
confirm.button(QDialogButtonBox::Help)->setText(SETTINGS);
|
||||||
|
connect(&confirm, &QDialogButtonBox::helpRequested, [&customSettings] { customSettings = true; });
|
||||||
|
connect(&confirm, &QDialogButtonBox::accepted, &dialog, &QDialog::accept);
|
||||||
|
connect(&confirm, &QDialogButtonBox::helpRequested, &dialog, &QDialog::accept);
|
||||||
|
dialog.setWindowTitle(SEARCH_FOR_HOOKS);
|
||||||
|
if (dialog.exec() == QDialog::Rejected) return;
|
||||||
|
|
||||||
|
if (customSettings)
|
||||||
{
|
{
|
||||||
using QDialog::QDialog;
|
QDialog dialog(this, Qt::WindowCloseButtonHint);
|
||||||
void launch()
|
QFormLayout layout(&dialog);
|
||||||
{
|
QLineEdit patternInput(x64 ? "CC CC 48 89" : "CC CC 55 8B EC", &dialog);
|
||||||
auto layout = new QFormLayout(this);
|
layout.addRow(SEARCH_PATTERN, &patternInput);
|
||||||
auto patternInput = new QLineEdit(x64 ? "CC CC 48 89" : "CC CC 55 8B EC", this);
|
for (auto [value, label] : Array<std::tuple<int&, const char*>>{
|
||||||
layout->addRow(SEARCH_PATTERN, patternInput);
|
|
||||||
for (auto[value, label] : Array<std::tuple<int&, const char*>>{
|
|
||||||
{ sp.searchTime = 20000, SEARCH_DURATION },
|
{ sp.searchTime = 20000, SEARCH_DURATION },
|
||||||
{ sp.offset = 2, PATTERN_OFFSET },
|
{ sp.offset = 2, PATTERN_OFFSET },
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
auto spinBox = new QSpinBox(this);
|
auto spinBox = new QSpinBox(&dialog);
|
||||||
spinBox->setMaximum(INT_MAX);
|
spinBox->setMaximum(INT_MAX);
|
||||||
spinBox->setValue(value);
|
spinBox->setValue(value);
|
||||||
layout->addRow(label, spinBox);
|
layout.addRow(label, spinBox);
|
||||||
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), [=, &value] { value = spinBox->value(); });
|
connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), [&value] (int newValue) { value = newValue; });
|
||||||
}
|
}
|
||||||
for (auto[value, label] : Array<std::tuple<uintptr_t&, const char*>>{
|
for (auto [value, label] : Array<std::tuple<uintptr_t&, const char*>>{
|
||||||
{ sp.minAddress = 0, MIN_ADDRESS },
|
{ sp.minAddress = 0, MIN_ADDRESS },
|
||||||
{ sp.maxAddress = -1ULL, MAX_ADDRESS },
|
{ sp.maxAddress = -1ULL, MAX_ADDRESS },
|
||||||
{ sp.padding = 0, STRING_OFFSET }
|
{ sp.padding = 0, STRING_OFFSET }
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
auto input = new QLineEdit(QString::number(value, 16), this);
|
auto input = new QLineEdit(QString::number(value, 16), &dialog);
|
||||||
layout->addRow(label, input);
|
layout.addRow(label, input);
|
||||||
connect(input, &QLineEdit::textEdited, [&value](QString input)
|
connect(input, &QLineEdit::textEdited, [&value](QString input)
|
||||||
{
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
if (uintptr_t newValue = input.toULongLong(&ok, 16); ok) value = newValue;
|
if (uintptr_t newValue = input.toULongLong(&ok, 16); ok) value = newValue;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
auto filterInput = new QLineEdit(this);
|
QLineEdit filterInput(".", &dialog);
|
||||||
layout->addRow(HOOK_SEARCH_FILTER, filterInput);
|
layout.addRow(HOOK_SEARCH_FILTER, &filterInput);
|
||||||
auto save = new QPushButton(START_HOOK_SEARCH, this);
|
QPushButton startButton(START_HOOK_SEARCH, &dialog);
|
||||||
layout->addWidget(save);
|
layout.addWidget(&startButton);
|
||||||
connect(save, &QPushButton::clicked, this, &QDialog::accept);
|
connect(&startButton, &QPushButton::clicked, &dialog, &QDialog::accept);
|
||||||
connect(save, &QPushButton::clicked, [this, patternInput, filterInput]
|
if (dialog.exec() == QDialog::Rejected) return;
|
||||||
{
|
QByteArray pattern = QByteArray::fromHex(patternInput.text().replace("??", QString::number(XX, 16)).toUtf8());
|
||||||
QByteArray pattern = QByteArray::fromHex(patternInput->text().replace("??", QString::number(XX, 16)).toUtf8());
|
|
||||||
if (pattern.size() < 3) return;
|
|
||||||
std::wregex filter(L".");
|
|
||||||
if (!filterInput->text().isEmpty()) try { filter = std::wregex(S(filterInput->text())); } catch (std::regex_error) {};
|
|
||||||
memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), 25));
|
memcpy(sp.pattern, pattern.data(), sp.length = min(pattern.size(), 25));
|
||||||
|
try { filter = std::wregex(S(filterInput.text())); } catch (std::regex_error) {};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// sp.length is 0 in this branch, so default will be used
|
||||||
|
filter = cjkCheckbox.isChecked() ? std::wregex(L"[\\u3000-\\ua000]{4,}") : std::wregex(L"[\\u0020-\\u1000]{4,}");
|
||||||
|
}
|
||||||
|
|
||||||
auto hooks = std::make_shared<QString>();
|
auto hooks = std::make_shared<QString>();
|
||||||
DWORD processId = this->processId;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Host::FindHooks(processId, sp, [processId, hooks, filter](HookParam hp, const std::wstring& text)
|
Host::FindHooks(processId, sp, [processId, hooks, filter](HookParam hp, const std::wstring& text)
|
||||||
{
|
{
|
||||||
if (std::regex_search(text, filter)) hooks->append(S(Util::GenerateCode(hp, processId)) + ": " + S(text) + "\n");
|
if (std::regex_search(text, filter)) hooks->append(S(Util::GenerateCode(hp, processId)) + ": " + S(text) + "\n");
|
||||||
});
|
});
|
||||||
}
|
} catch (std::out_of_range) { return; }
|
||||||
catch (std::out_of_range) { return; }
|
QString saveFile = QFileDialog::getSaveFileName(this, SAVE_SEARCH_RESULTS, "./Hooks.txt", TEXT_FILES);
|
||||||
QString fileName = QFileDialog::getSaveFileName(this, SAVE_SEARCH_RESULTS, "./Hooks.txt", TEXT_FILES);
|
if (saveFile.isEmpty()) saveFile = "Hooks.txt";
|
||||||
if (fileName.isEmpty()) fileName = "Hooks.txt";
|
std::thread([hooks, saveFile]
|
||||||
std::thread([hooks, fileName]
|
|
||||||
{
|
{
|
||||||
for (int lastSize = 0; hooks->size() == 0 || hooks->size() != lastSize; Sleep(2000)) lastSize = hooks->size();
|
for (int lastSize = 0; hooks->size() == 0 || hooks->size() != lastSize; Sleep(2000)) lastSize = hooks->size();
|
||||||
QTextFile(fileName, QIODevice::WriteOnly | QIODevice::Truncate).write(hooks->toUtf8());
|
QTextFile(saveFile, QIODevice::WriteOnly | QIODevice::Truncate).write(hooks->toUtf8());
|
||||||
hooks->clear();
|
hooks->clear();
|
||||||
}).detach();
|
}).detach();
|
||||||
});
|
|
||||||
setWindowTitle(FIND_HOOKS);
|
|
||||||
exec();
|
|
||||||
}
|
|
||||||
|
|
||||||
SearchParam sp = {};
|
|
||||||
DWORD processId;
|
|
||||||
} searchDialog(this, Qt::WindowCloseButtonHint);
|
|
||||||
searchDialog.processId = GetSelectedProcessId();
|
|
||||||
searchDialog.launch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::Settings()
|
void MainWindow::Settings()
|
||||||
{
|
{
|
||||||
struct : QDialog
|
QDialog dialog(this, Qt::WindowCloseButtonHint);
|
||||||
{
|
QSettings settings(CONFIG_FILE, QSettings::IniFormat, &dialog);
|
||||||
using QDialog::QDialog;
|
QFormLayout layout(&dialog);
|
||||||
void launch()
|
QPushButton saveButton(SAVE_SETTINGS, &dialog);
|
||||||
{
|
layout.addWidget(&saveButton);
|
||||||
auto settings = new QSettings(CONFIG_FILE, QSettings::IniFormat, this);
|
for (auto [value, label] : Array<std::tuple<int&, const char*>>{
|
||||||
auto layout = new QFormLayout(this);
|
|
||||||
auto save = new QPushButton(SAVE_SETTINGS, this);
|
|
||||||
layout->addWidget(save);
|
|
||||||
for (auto[value, label] : Array<std::tuple<int&, const char*>>{
|
|
||||||
{ Host::defaultCodepage, DEFAULT_CODEPAGE },
|
{ Host::defaultCodepage, DEFAULT_CODEPAGE },
|
||||||
{ TextThread::maxBufferSize, MAX_BUFFER_SIZE },
|
{ TextThread::maxBufferSize, MAX_BUFFER_SIZE },
|
||||||
{ TextThread::flushDelay, FLUSH_DELAY },
|
{ TextThread::flushDelay, FLUSH_DELAY },
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
auto spinBox = new QSpinBox(this);
|
auto spinBox = new QSpinBox(&dialog);
|
||||||
spinBox->setMaximum(INT_MAX);
|
spinBox->setMaximum(INT_MAX);
|
||||||
spinBox->setValue(value);
|
spinBox->setValue(value);
|
||||||
layout->insertRow(0, label, spinBox);
|
layout.insertRow(0, label, spinBox);
|
||||||
connect(save, &QPushButton::clicked, [=, &value] { settings->setValue(label, value = spinBox->value()); });
|
connect(&saveButton, &QPushButton::clicked, [spinBox, label, &settings, &value] { settings.setValue(label, value = spinBox->value()); });
|
||||||
}
|
}
|
||||||
for (auto[value, label] : Array<std::tuple<bool&, const char*>>{
|
for (auto [value, label] : Array<std::tuple<bool&, const char*>>{
|
||||||
{ TextThread::filterRepetition, FILTER_REPETITION },
|
{ TextThread::filterRepetition, FILTER_REPETITION },
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
auto checkBox = new QCheckBox(this);
|
auto checkBox = new QCheckBox(&dialog);
|
||||||
checkBox->setChecked(value);
|
checkBox->setChecked(value);
|
||||||
layout->insertRow(0, label, checkBox);
|
layout.insertRow(0, label, checkBox);
|
||||||
connect(save, &QPushButton::clicked, [=, &value] { settings->setValue(label, value = checkBox->isChecked()); });
|
connect(&saveButton, &QPushButton::clicked, [checkBox, label, &settings, &value] { settings.setValue(label, value = checkBox->isChecked()); });
|
||||||
}
|
}
|
||||||
connect(save, &QPushButton::clicked, this, &QDialog::accept);
|
connect(&saveButton, &QPushButton::clicked, &dialog, &QDialog::accept);
|
||||||
setWindowTitle(SETTINGS);
|
dialog.setWindowTitle(SETTINGS);
|
||||||
exec();
|
dialog.exec();
|
||||||
}
|
|
||||||
} settingsDialog(this, Qt::WindowCloseButtonHint);
|
|
||||||
settingsDialog.launch();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::Extensions()
|
void MainWindow::Extensions()
|
||||||
|
@ -61,11 +61,12 @@ struct ThreadParam
|
|||||||
|
|
||||||
struct SearchParam
|
struct SearchParam
|
||||||
{
|
{
|
||||||
BYTE pattern[25] = {}; // pattern in memory to search for
|
BYTE pattern[25]; // pattern in memory to search for
|
||||||
int length, // length of pattern
|
int length, // length of pattern (zero means this SearchParam is invalid and the default should be used)
|
||||||
offset, // offset from start of pattern to add hook
|
offset, // offset from start of pattern to add hook
|
||||||
searchTime; // ms
|
searchTime; // ms
|
||||||
uintptr_t padding, minAddress, maxAddress;
|
uintptr_t padding, minAddress, maxAddress;
|
||||||
|
void(*hookPostProcesser)(HookParam&);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct InsertHookCmd // From host
|
struct InsertHookCmd // From host
|
||||||
|
3
text.cpp
3
text.cpp
@ -12,7 +12,7 @@ const char* DETACH = u8"Detach from game";
|
|||||||
const char* ADD_HOOK = u8"Add hook";
|
const char* ADD_HOOK = u8"Add hook";
|
||||||
const char* REMOVE_HOOKS = u8"Remove hook(s)";
|
const char* REMOVE_HOOKS = u8"Remove hook(s)";
|
||||||
const char* SAVE_HOOKS = u8"Save hook(s)";
|
const char* SAVE_HOOKS = u8"Save hook(s)";
|
||||||
const char* FIND_HOOKS = u8"Find hooks";
|
const char* SEARCH_FOR_HOOKS = u8"Search for hooks";
|
||||||
const char* SETTINGS = u8"Settings";
|
const char* SETTINGS = u8"Settings";
|
||||||
const char* EXTENSIONS = u8"Extensions";
|
const char* EXTENSIONS = u8"Extensions";
|
||||||
const char* SELECT_PROCESS = u8"Select process";
|
const char* SELECT_PROCESS = u8"Select process";
|
||||||
@ -51,6 +51,7 @@ const char* CONFIRM_EXTENSION_OVERWRITE = u8"Another version of this extension a
|
|||||||
const char* EXTENSION_WRITE_ERROR = u8"Failed to save extension";
|
const char* EXTENSION_WRITE_ERROR = u8"Failed to save extension";
|
||||||
const char* USE_JP_LOCALE = u8"Emulate japanese locale?";
|
const char* USE_JP_LOCALE = u8"Emulate japanese locale?";
|
||||||
const char* HOOK_SEARCH_UNSTABLE_WARNING = u8"Searching for hooks is unstable! Be prepared for your game to crash!";
|
const char* HOOK_SEARCH_UNSTABLE_WARNING = u8"Searching for hooks is unstable! Be prepared for your game to crash!";
|
||||||
|
const char* SEARCH_CJK = u8"Search for Chinese/Japanese/Korean";
|
||||||
const char* SEARCH_PATTERN = u8"Search pattern (hex byte array)";
|
const char* SEARCH_PATTERN = u8"Search pattern (hex byte array)";
|
||||||
const char* SEARCH_DURATION = u8"Search duration (ms)";
|
const char* SEARCH_DURATION = u8"Search duration (ms)";
|
||||||
const char* PATTERN_OFFSET = u8"Offset from pattern start";
|
const char* PATTERN_OFFSET = u8"Offset from pattern start";
|
||||||
|
@ -16818,7 +16818,7 @@ bool InsertVanillawareGCHook()
|
|||||||
|
|
||||||
/** Artikash 6/7/2019
|
/** Artikash 6/7/2019
|
||||||
* PPSSPP JIT code has pointers, but they are all added to an offset before being used.
|
* PPSSPP JIT code has pointers, but they are all added to an offset before being used.
|
||||||
Find that offset and report it to user so they can search for hooks properly.
|
Find that offset so that hook searching works properly.
|
||||||
To find the offset, find a page of mapped memory with size 0x1f00000, read and write permissions, take its address and subtract 0x8000000.
|
To find the offset, find a page of mapped memory with size 0x1f00000, read and write permissions, take its address and subtract 0x8000000.
|
||||||
The above is useful for emulating PSP hardware, so unlikely to change between versions.
|
The above is useful for emulating PSP hardware, so unlikely to change between versions.
|
||||||
*/
|
*/
|
||||||
@ -16839,7 +16839,13 @@ bool FindPPSSPP()
|
|||||||
if (info.RegionSize == 0x1f00000 && info.Protect == PAGE_READWRITE && info.Type == MEM_MAPPED)
|
if (info.RegionSize == 0x1f00000 && info.Protect == PAGE_READWRITE && info.Type == MEM_MAPPED)
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
ConsoleOutput("Textractor: PPSSPP memory found: use pattern 79 0F C7 85 and pattern offset 0 and string offset 0x%p to search for hooks", probe - 0x8000000);
|
ConsoleOutput("Textractor: PPSSPP memory found: searching for hooks should yield working hook codes");
|
||||||
|
memcpy(spDefault.pattern, Array<BYTE>{ 0x79, 0x0f, 0xc7, 0x85 }, spDefault.length = 4);
|
||||||
|
spDefault.offset = 0;
|
||||||
|
spDefault.minAddress = 0;
|
||||||
|
spDefault.maxAddress = -1ULL;
|
||||||
|
spDefault.padding = (uintptr_t)probe - 0x8000000;
|
||||||
|
spDefault.hookPostProcesser = [](HookParam& hp) { hp.type |= NO_CONTEXT; };
|
||||||
}
|
}
|
||||||
probe += info.RegionSize;
|
probe += info.RegionSize;
|
||||||
}
|
}
|
||||||
|
@ -37,8 +37,8 @@ namespace Engine
|
|||||||
|
|
||||||
void Hijack()
|
void Hijack()
|
||||||
{
|
{
|
||||||
static bool hijacked = false;
|
static auto _ = []
|
||||||
if (hijacked) return;
|
{
|
||||||
GetModuleFileNameW(nullptr, processPath, MAX_PATH);
|
GetModuleFileNameW(nullptr, processPath, MAX_PATH);
|
||||||
processName = wcsrchr(processPath, L'\\') + 1;
|
processName = wcsrchr(processPath, L'\\') + 1;
|
||||||
|
|
||||||
@ -50,9 +50,12 @@ namespace Engine
|
|||||||
processStopAddress = (uintptr_t)info.BaseAddress + info.RegionSize;
|
processStopAddress = (uintptr_t)info.BaseAddress + info.RegionSize;
|
||||||
} while (info.Protect > PAGE_NOACCESS);
|
} while (info.Protect > PAGE_NOACCESS);
|
||||||
processStopAddress -= info.RegionSize;
|
processStopAddress -= info.RegionSize;
|
||||||
|
spDefault.minAddress = processStartAddress;
|
||||||
|
spDefault.maxAddress = processStopAddress;
|
||||||
|
ConsoleOutput("Textractor: hijacking process located from 0x%p to 0x%p", processStartAddress, processStopAddress);
|
||||||
|
|
||||||
DetermineEngineType();
|
DetermineEngineType();
|
||||||
hijacked = true;
|
return NULL;
|
||||||
ConsoleOutput("Textractor: finished hijacking process located from 0x%p to 0x%p", processStartAddress, processStopAddress);
|
}();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ namespace Engine
|
|||||||
{
|
{
|
||||||
/** Artikash 6/7/2019
|
/** Artikash 6/7/2019
|
||||||
* PPSSPP JIT code has pointers, but they are all added to an offset before being used.
|
* PPSSPP JIT code has pointers, but they are all added to an offset before being used.
|
||||||
Find that offset and report it to user so they can search for hooks properly.
|
Find that offset so that hook searching works properly.
|
||||||
To find the offset, find a page of mapped memory with size 0x1f00000, read and write permissions, take its address and subtract 0x8000000.
|
To find the offset, find a page of mapped memory with size 0x1f00000, read and write permissions, take its address and subtract 0x8000000.
|
||||||
The above is useful for emulating PSP hardware, so unlikely to change between versions.
|
The above is useful for emulating PSP hardware, so unlikely to change between versions.
|
||||||
*/
|
*/
|
||||||
@ -29,7 +29,13 @@ namespace Engine
|
|||||||
if (info.RegionSize == 0x1f00000 && info.Protect == PAGE_READWRITE && info.Type == MEM_MAPPED)
|
if (info.RegionSize == 0x1f00000 && info.Protect == PAGE_READWRITE && info.Type == MEM_MAPPED)
|
||||||
{
|
{
|
||||||
found = true;
|
found = true;
|
||||||
ConsoleOutput("Textractor: PPSSPP memory found: use pattern 79 10 41 C7 and pattern offset 0 and string offset 0x%p to search for hooks", probe - 0x8000000);
|
ConsoleOutput("Textractor: PPSSPP memory found: searching for hooks should yield working hook codes");
|
||||||
|
memcpy(spDefault.pattern, Array<BYTE>{ 0x79, 0x10, 0x41, 0xc7 }, spDefault.length = 4);
|
||||||
|
spDefault.offset = 0;
|
||||||
|
spDefault.minAddress = 0;
|
||||||
|
spDefault.maxAddress = -1ULL;
|
||||||
|
spDefault.padding = (uintptr_t)probe - 0x8000000;
|
||||||
|
spDefault.hookPostProcesser = [](HookParam& hp) { hp.type |= NO_CONTEXT; };
|
||||||
}
|
}
|
||||||
probe += info.RegionSize;
|
probe += info.RegionSize;
|
||||||
}
|
}
|
||||||
|
@ -11,7 +11,7 @@ extern WinMutex viewMutex;
|
|||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
SearchParam current;
|
SearchParam sp;
|
||||||
|
|
||||||
constexpr int CACHE_SIZE = 500'000;
|
constexpr int CACHE_SIZE = 500'000;
|
||||||
struct HookRecord
|
struct HookRecord
|
||||||
@ -23,7 +23,8 @@ namespace
|
|||||||
hp.offset = offset;
|
hp.offset = offset;
|
||||||
hp.type = USING_UNICODE | USING_STRING;
|
hp.type = USING_UNICODE | USING_STRING;
|
||||||
hp.address = address;
|
hp.address = address;
|
||||||
hp.padding = current.padding;
|
hp.padding = sp.padding;
|
||||||
|
if (sp.hookPostProcesser) sp.hookPostProcesser(hp);
|
||||||
NotifyHookFound(hp, (wchar_t*)text);
|
NotifyHookFound(hp, (wchar_t*)text);
|
||||||
}
|
}
|
||||||
uint64_t address = 0;
|
uint64_t address = 0;
|
||||||
@ -118,7 +119,7 @@ void Send(char** stack, uintptr_t address)
|
|||||||
for (int i = -registers; i < 6; ++i)
|
for (int i = -registers; i < 6; ++i)
|
||||||
{
|
{
|
||||||
int length = 0, sum = 0;
|
int length = 0, sum = 0;
|
||||||
char* str = stack[i] + current.padding;
|
char* str = stack[i] + sp.padding;
|
||||||
__try { for (; (str[length] || str[length + 1]) && length < 500; length += 2) sum += str[length] + str[length + 1]; }
|
__try { for (; (str[length] || str[length + 1]) && length < 500; length += 2) sum += str[length] + str[length + 1]; }
|
||||||
__except (EXCEPTION_EXECUTE_HANDLER) {}
|
__except (EXCEPTION_EXECUTE_HANDLER) {}
|
||||||
if (length > STRING && length < 499)
|
if (length > STRING && length < 499)
|
||||||
@ -152,7 +153,7 @@ void Send(char** stack, uintptr_t address)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SearchForHooks(SearchParam sp)
|
void SearchForHooks(SearchParam spUser)
|
||||||
{
|
{
|
||||||
std::thread([=]
|
std::thread([=]
|
||||||
{
|
{
|
||||||
@ -162,7 +163,7 @@ void SearchForHooks(SearchParam sp)
|
|||||||
try { records = std::make_unique<HookRecord[]>(recordsAvailable = CACHE_SIZE); }
|
try { records = std::make_unique<HookRecord[]>(recordsAvailable = CACHE_SIZE); }
|
||||||
catch (std::bad_alloc) { return ConsoleOutput("Textractor: SearchForHooks ERROR (out of memory)"); }
|
catch (std::bad_alloc) { return ConsoleOutput("Textractor: SearchForHooks ERROR (out of memory)"); }
|
||||||
|
|
||||||
current = sp;
|
sp = spUser.length == 0 ? spDefault : spUser;
|
||||||
|
|
||||||
uintptr_t moduleStartAddress = (uintptr_t)GetModuleHandleW(ITH_DLL);
|
uintptr_t moduleStartAddress = (uintptr_t)GetModuleHandleW(ITH_DLL);
|
||||||
uintptr_t moduleStopAddress = moduleStartAddress;
|
uintptr_t moduleStopAddress = moduleStartAddress;
|
||||||
|
@ -165,7 +165,7 @@ void NewHook(HookParam hp, LPCSTR lpname, DWORD flag)
|
|||||||
WideCharToMultiByte(hp.codepage, 0, hp.text, MAX_MODULE_SIZE, codepageText, MAX_MODULE_SIZE * 4, nullptr, nullptr);
|
WideCharToMultiByte(hp.codepage, 0, hp.text, MAX_MODULE_SIZE, codepageText, MAX_MODULE_SIZE * 4, nullptr, nullptr);
|
||||||
if (strlen(utf8Text) < 8 || strlen(codepageText) < 8 || wcslen(hp.text) < 4) return ConsoleOutput(NOT_ENOUGH_TEXT);
|
if (strlen(utf8Text) < 8 || strlen(codepageText) < 8 || wcslen(hp.text) < 4) return ConsoleOutput(NOT_ENOUGH_TEXT);
|
||||||
ConsoleOutput(STARTING_SEARCH);
|
ConsoleOutput(STARTING_SEARCH);
|
||||||
for (auto[addrs, type] : Array<std::tuple<std::vector<uint64_t>, HookParamType>>{
|
for (auto [addrs, type] : Array<std::tuple<std::vector<uint64_t>, HookParamType>>{
|
||||||
{ Util::SearchMemory(utf8Text, strlen(utf8Text), PAGE_READWRITE), USING_UTF8 },
|
{ Util::SearchMemory(utf8Text, strlen(utf8Text), PAGE_READWRITE), USING_UTF8 },
|
||||||
{ Util::SearchMemory(codepageText, strlen(codepageText), PAGE_READWRITE), USING_STRING },
|
{ Util::SearchMemory(codepageText, strlen(codepageText), PAGE_READWRITE), USING_STRING },
|
||||||
{ Util::SearchMemory(hp.text, wcslen(hp.text) * 2, PAGE_READWRITE), USING_UNICODE }
|
{ Util::SearchMemory(hp.text, wcslen(hp.text) * 2, PAGE_READWRITE), USING_UNICODE }
|
||||||
|
@ -14,6 +14,15 @@ void NotifyHookRemove(uint64_t addr, LPCSTR name);
|
|||||||
void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE);
|
void NewHook(HookParam hp, LPCSTR name, DWORD flag = HOOK_ENGINE);
|
||||||
void RemoveHook(uint64_t addr, int maxOffset = 9);
|
void RemoveHook(uint64_t addr, int maxOffset = 9);
|
||||||
|
|
||||||
|
inline SearchParam spDefault = []
|
||||||
|
{
|
||||||
|
SearchParam sp = {};
|
||||||
|
memcpy(sp.pattern, x64 ? Array<BYTE>{ 0xcc, 0xcc, 0x48, 0x89 } : Array<BYTE>{ 0xcc, 0xcc, 0x55, 0x8b, 0xec }, sp.length = x64 ? 4 : 5);
|
||||||
|
sp.offset = 2;
|
||||||
|
sp.searchTime = 20000;
|
||||||
|
return sp;
|
||||||
|
}();
|
||||||
|
|
||||||
extern "C" // minhook library
|
extern "C" // minhook library
|
||||||
{
|
{
|
||||||
enum MH_STATUS
|
enum MH_STATUS
|
||||||
|
Loading…
Reference in New Issue
Block a user