implement read code
This commit is contained in:
parent
9a299f357f
commit
ed4879ba5c
@ -71,7 +71,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||
std::map<int, QString> extensions = LoadExtensions();
|
||||
for (auto i : extensions) extenCombo->addItem(QString::number(i.first) + ":" + i.second);
|
||||
Host::Open();
|
||||
Host::AddConsoleOutput(L"NextHooker beta v2.0.2 by Artikash\r\nSource code and more information available under GPLv3 at https://github.com/Artikash/NextHooker");
|
||||
Host::AddConsoleOutput(L"NextHooker beta v2.1.0 by Artikash\r\nSource code and more information available under GPLv3 at https://github.com/Artikash/NextHooker");
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
@ -95,7 +95,7 @@ void MainWindow::AddProcess(unsigned int processId)
|
||||
for (int j = 1; j < hooks.length(); ++j)
|
||||
{
|
||||
Sleep(10);
|
||||
Host::InsertHook(processId, ParseHCode(hooks.at(j)));
|
||||
Host::InsertHook(processId, ParseCode(hooks.at(j)));
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -112,7 +112,7 @@ void MainWindow::AddThread(TextThread* thread)
|
||||
TextThreadString(thread) +
|
||||
QString::fromWCharArray(Host::GetHookName(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook).c_str()) +
|
||||
" (" +
|
||||
GenerateHCode(Host::GetHookParam(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook), thread->GetThreadParameter().pid) +
|
||||
GenerateCode(Host::GetHookParam(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook), thread->GetThreadParameter().pid) +
|
||||
")"
|
||||
);
|
||||
thread->RegisterOutputCallBack([&](TextThread* thread, std::wstring output)
|
||||
@ -188,15 +188,15 @@ void MainWindow::on_detachButton_clicked()
|
||||
void MainWindow::on_hookButton_clicked()
|
||||
{
|
||||
bool ok;
|
||||
QString hookCode = QInputDialog::getText(this, "Add Hook", HCodeInfoDump, QLineEdit::Normal, "/H", &ok);
|
||||
QString hookCode = QInputDialog::getText(this, "Add Hook", CodeInfoDump, QLineEdit::Normal, "", &ok);
|
||||
if (!ok) return;
|
||||
HookParam toInsert = ParseHCode(hookCode);
|
||||
HookParam toInsert = ParseCode(hookCode);
|
||||
if (toInsert.type == 0 && toInsert.length_offset == 0)
|
||||
{
|
||||
Host::AddConsoleOutput(L"invalid /H code");
|
||||
Host::AddConsoleOutput(L"invalid code");
|
||||
return;
|
||||
}
|
||||
Host::InsertHook(processCombo->currentText().split(":")[0].toInt(), ParseHCode(hookCode));
|
||||
Host::InsertHook(processCombo->currentText().split(":")[0].toInt(), ParseCode(hookCode));
|
||||
}
|
||||
|
||||
void MainWindow::on_unhookButton_clicked()
|
||||
@ -206,7 +206,7 @@ void MainWindow::on_unhookButton_clicked()
|
||||
for (auto i : hooks) hookList.push_back(
|
||||
QString::fromWCharArray(Host::GetHookName(processCombo->currentText().split(":")[0].toInt(), i.address).c_str()) +
|
||||
": " +
|
||||
GenerateHCode(i, processCombo->currentText().split(":")[0].toInt())
|
||||
GenerateCode(i, processCombo->currentText().split(":")[0].toInt())
|
||||
);
|
||||
bool ok;
|
||||
QString hook = QInputDialog::getItem(this, "Unhook", "Which hook to remove?", hookList, 0, false, &ok);
|
||||
@ -219,7 +219,7 @@ void MainWindow::on_saveButton_clicked()
|
||||
QString hookList = GetFullModuleName(processCombo->currentText().split(":")[0].toInt());;
|
||||
for (auto i : hooks)
|
||||
if (!(i.type & HOOK_ENGINE))
|
||||
hookList += " , " + GenerateHCode(i, processCombo->currentText().split(":")[0].toInt());
|
||||
hookList += " , " + GenerateCode(i, processCombo->currentText().split(":")[0].toInt());
|
||||
QFile file("SavedHooks.txt");
|
||||
if (!file.open(QIODevice::Append | QIODevice::Text)) return;
|
||||
file.write((hookList + "\r\n").toUtf8());
|
||||
|
61
GUI/misc.cpp
61
GUI/misc.cpp
@ -40,12 +40,38 @@ DWORD Hash(QString module)
|
||||
return hash;
|
||||
}
|
||||
|
||||
HookParam ParseRCode(QString RCode)
|
||||
{
|
||||
HookParam hp = {};
|
||||
switch (RCode.at(0).unicode())
|
||||
{
|
||||
case L'S':
|
||||
break;
|
||||
case L'Q':
|
||||
hp.type |= USING_STRING | USING_UNICODE;
|
||||
break;
|
||||
case L'V':
|
||||
hp.type |= USING_STRING | USING_UTF8;
|
||||
break;
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
RCode.remove(0, 1);
|
||||
QRegExp stringGap("^\\-?[\\dA-F]+");
|
||||
if (stringGap.indexIn(RCode) == -1) return {};
|
||||
hp.offset = stringGap.cap(0).toInt(nullptr, 16);
|
||||
RCode.remove(0, stringGap.cap(0).length());
|
||||
if (RCode.at(0).unicode() != L'@') return {};
|
||||
RCode.remove(0, 1);
|
||||
QRegExp address("[\\dA-F]+$");
|
||||
if (address.indexIn(RCode) == -1) return {};
|
||||
hp.address = address.cap(1).toInt(nullptr, 16);
|
||||
return hp;
|
||||
}
|
||||
|
||||
HookParam ParseHCode(QString HCode)
|
||||
{
|
||||
HookParam hp = {};
|
||||
HCode = HCode.toUpper();
|
||||
if (!HCode.startsWith("/H")) return {};
|
||||
HCode.remove(0, 2);
|
||||
switch (HCode.at(0).unicode())
|
||||
{
|
||||
case L'S':
|
||||
@ -120,6 +146,14 @@ HookParam ParseHCode(QString HCode)
|
||||
return hp;
|
||||
}
|
||||
|
||||
HookParam ParseCode(QString code)
|
||||
{
|
||||
code = code.toUpper();
|
||||
if (code.startsWith("/H")) return ParseHCode(code.remove(0, 2));
|
||||
else if (code.startsWith("/R")) return ParseRCode(code.remove(0, 2));
|
||||
else return {};
|
||||
}
|
||||
|
||||
QString GenerateHCode(HookParam hp, DWORD processId)
|
||||
{
|
||||
QString code = "/H";
|
||||
@ -183,3 +217,24 @@ QString GenerateHCode(HookParam hp, DWORD processId)
|
||||
code += QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1);
|
||||
return code;
|
||||
}
|
||||
|
||||
QString GenerateRCode(HookParam hp)
|
||||
{
|
||||
QString code = "/R";
|
||||
if (hp.type & USING_UNICODE)
|
||||
code += "Q";
|
||||
else if (hp.type & USING_UTF8)
|
||||
code += "V";
|
||||
else
|
||||
code += "S";
|
||||
code += QString::number(hp.offset, 16);
|
||||
code += "@";
|
||||
code += QString::number(hp.address, 16);
|
||||
return code;
|
||||
}
|
||||
|
||||
QString GenerateCode(HookParam hp, DWORD processId)
|
||||
{
|
||||
if (hp.type & DIRECT_READ) return GenerateRCode(hp);
|
||||
else return GenerateHCode(hp, processId);
|
||||
}
|
||||
|
15
GUI/misc.h
15
GUI/misc.h
@ -8,16 +8,21 @@
|
||||
QString GetFullModuleName(DWORD processId, HMODULE module = NULL);
|
||||
QString GetModuleName(DWORD processId, HMODULE module = NULL);
|
||||
QStringList GetAllProcesses();
|
||||
HookParam ParseHCode(QString HCode);
|
||||
QString GenerateHCode(HookParam hp, DWORD processId);
|
||||
HookParam ParseCode(QString HCode);
|
||||
QString GenerateCode(HookParam hp, DWORD processId);
|
||||
|
||||
static QString HCodeInfoDump =
|
||||
"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\
|
||||
static QString CodeInfoDump =
|
||||
"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\
|
||||
OR\r\n\
|
||||
Enter read code\r\n\
|
||||
/R{S|Q|V}string_gap@addr\r\n\
|
||||
All numbers 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";
|
||||
* means dereference pointer+deref_offset\r\n\r\n\
|
||||
Use 0 for string_gap if string is in same location every time";
|
||||
#endif // MISC_H
|
||||
|
@ -43,8 +43,10 @@ struct HookParam {
|
||||
recover_len; // ?
|
||||
|
||||
// 7/20/2014: jichi additional parameters for PSP games
|
||||
DWORD user_flags,
|
||||
user_value;
|
||||
DWORD user_value;
|
||||
|
||||
// Artikash 8/4/2018: handle for reader thread
|
||||
HANDLE readerHandle;
|
||||
};
|
||||
|
||||
// jichi 6/1/2014: Structure of the esp for extern functions
|
||||
|
@ -16694,11 +16694,6 @@ void SpecialPSPHook(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *spl
|
||||
LPCSTR text = LPCSTR(offset + hp->user_value);
|
||||
static LPCSTR lasttext;
|
||||
if (*text) {
|
||||
if (hp->user_flags & HPF_IgnoreSameAddress) {
|
||||
if (text == lasttext)
|
||||
return;
|
||||
lasttext = text;
|
||||
}
|
||||
*data = (DWORD)text;
|
||||
// I only considered SHIFT-JIS/UTF-8 case
|
||||
if (hp->length_offset == 1)
|
||||
@ -17451,7 +17446,6 @@ bool InsertImageepochPSPHook()
|
||||
hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT; // UTF-8, though
|
||||
hp.offset = pusha_eax_off - 4;
|
||||
hp.split = pusha_ecx_off - 4;
|
||||
hp.user_flags = HPF_IgnoreSameAddress;
|
||||
//hp.text_fun = SpecialPSPHook;
|
||||
hp.text_fun = SpecialPSPHookImageepoch; // since this function is common, use its own static lasttext for HPF_IgnoreSameAddress
|
||||
ConsoleOutput("vnreng: Imageepoch PSP: INSERT");
|
||||
|
@ -316,9 +316,11 @@ DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn)
|
||||
|
||||
int TextHook::InsertHook()
|
||||
{
|
||||
int ok = 1;
|
||||
//ConsoleOutput("vnrcli:InsertHook: enter");
|
||||
WaitForSingleObject(hmMutex, 0);
|
||||
int ok = InsertHookCode();
|
||||
if (hp.type & DIRECT_READ) ok = InsertReadCode();
|
||||
else ok = InsertHookCode();
|
||||
ReleaseMutex(hmMutex);
|
||||
//ConsoleOutput("vnrcli:InsertHook: leave");
|
||||
return ok;
|
||||
@ -326,7 +328,6 @@ int TextHook::InsertHook()
|
||||
|
||||
int TextHook::InsertHookCode()
|
||||
{
|
||||
enum : int { yes = 0, no = 1 };
|
||||
DWORD ret = no;
|
||||
// jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7
|
||||
ITH_WITH_SEH(ret = UnsafeInsertHookCode());
|
||||
@ -335,10 +336,76 @@ int TextHook::InsertHookCode()
|
||||
return ret;
|
||||
}
|
||||
|
||||
DWORD WINAPI ReaderThread(LPVOID threadParam)
|
||||
{
|
||||
TextHook* hook = (TextHook*)threadParam;
|
||||
BYTE buffer[PIPE_BUFFER_SIZE] = {};
|
||||
char testChar;
|
||||
unsigned int changeCount = 0;
|
||||
const char* currentAddress = (char*)hook->hp.address;
|
||||
while (true)
|
||||
{
|
||||
Sleep(50);
|
||||
if (testChar == *currentAddress)
|
||||
{
|
||||
changeCount = 0;
|
||||
continue;
|
||||
}
|
||||
testChar = *currentAddress;
|
||||
if (++changeCount > 10)
|
||||
{
|
||||
ConsoleOutput("NextHooker: memory constantly changing, useless to read");
|
||||
ConsoleOutput("NextHooker: remove read code");
|
||||
break;
|
||||
}
|
||||
|
||||
int dataLen;
|
||||
if (hook->hp.type & USING_UNICODE)
|
||||
dataLen = wcslen((const wchar_t*)currentAddress) * 2;
|
||||
else
|
||||
dataLen = strlen(currentAddress);
|
||||
|
||||
*(DWORD*)buffer = hook->hp.address;
|
||||
*(DWORD*)(buffer + 4) = 0;
|
||||
*(DWORD*)(buffer + 8) = 0;
|
||||
memcpy(buffer + HEADER_SIZE, currentAddress, dataLen);
|
||||
DWORD unused;
|
||||
WriteFile(::hookPipe, buffer, dataLen + HEADER_SIZE, &unused, nullptr);
|
||||
|
||||
if (hook->hp.offset == 0) continue;
|
||||
currentAddress += dataLen + hook->hp.offset;
|
||||
testChar = *currentAddress;
|
||||
}
|
||||
hook->ClearHook();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TextHook::InsertReadCode()
|
||||
{
|
||||
hp.hook_len = 0x40;
|
||||
//Check if the new hook range conflict with existing ones. Clear older if conflict.
|
||||
TextHook *it = hookman;
|
||||
for (int i = 0; i < currentHook; it++) {
|
||||
if (it->Address())
|
||||
i++;
|
||||
if (it == this)
|
||||
continue;
|
||||
if ((it->Address() >= hp.address && it->Address() < hp.hook_len + hp.address) || (it->Address() <= hp.address && it->Address() + it->Length() > hp.address))
|
||||
it->ClearHook();
|
||||
}
|
||||
if (!IthGetMemoryRange((LPCVOID)hp.address, 0, 0))
|
||||
{
|
||||
ConsoleOutput("cannot access read address");
|
||||
return no;
|
||||
}
|
||||
hp.readerHandle = CreateThread(nullptr, 0, ReaderThread, this, 0, nullptr);
|
||||
return yes;
|
||||
|
||||
}
|
||||
|
||||
int TextHook::UnsafeInsertHookCode()
|
||||
{
|
||||
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter");
|
||||
enum : int { yes = 0, no = 1 };
|
||||
if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address.
|
||||
if (DWORD base = GetModuleBase(hp.module)) {
|
||||
hp.address += base;
|
||||
@ -467,29 +534,36 @@ int TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag)
|
||||
|
||||
int TextHook::RemoveHookCode()
|
||||
{
|
||||
enum : int { yes = 1, no = 0 };
|
||||
if (!hp.address)
|
||||
return no;
|
||||
ConsoleOutput("vnrcli:RemoveHook: enter");
|
||||
WaitForSingleObject(hmMutex, TIMEOUT); // jichi 9/28/2012: wait at most for 5 seconds
|
||||
|
||||
DWORD l = hp.hook_len;
|
||||
//with_seh({ // jichi 9/17/2013: might crash ><
|
||||
// jichi 12/25/2013: Actually, __try cannot catch such kind of exception
|
||||
ITH_TRY {
|
||||
NtWriteVirtualMemory(GetCurrentProcess(), (LPVOID)hp.address, original, hp.recover_len, &l);
|
||||
NtFlushInstructionCache(GetCurrentProcess(), (LPVOID)hp.address, hp.recover_len);
|
||||
} ITH_EXCEPT {}
|
||||
}
|
||||
ITH_EXCEPT {}
|
||||
//});
|
||||
hp.hook_len = 0;
|
||||
ReleaseMutex(hmMutex);
|
||||
ConsoleOutput("vnrcli:RemoveHook: leave");
|
||||
return yes;
|
||||
}
|
||||
|
||||
int TextHook::RemoveReadCode()
|
||||
{
|
||||
if (!hp.address) return no;
|
||||
TerminateThread(hp.readerHandle, 0);
|
||||
CloseHandle(hp.readerHandle);
|
||||
return yes;
|
||||
}
|
||||
|
||||
int TextHook::ClearHook()
|
||||
{
|
||||
int err;
|
||||
WaitForSingleObject(hmMutex, 0);
|
||||
int err = RemoveHookCode();
|
||||
ConsoleOutput("vnrcli:RemoveHook: enter");
|
||||
if (hp.type & DIRECT_READ) err = RemoveReadCode();
|
||||
else err = RemoveHookCode();
|
||||
NotifyHookRemove(hp.address);
|
||||
if (hook_name) {
|
||||
delete[] hook_name;
|
||||
@ -499,6 +573,7 @@ int TextHook::ClearHook()
|
||||
//if (current_available>this)
|
||||
// current_available = this;
|
||||
currentHook--;
|
||||
ConsoleOutput("vnrcli:RemoveHook: leave");
|
||||
ReleaseMutex(hmMutex);
|
||||
return err;
|
||||
}
|
||||
|
@ -29,9 +29,11 @@ void InitFilterTable();
|
||||
class TextHook : public Hook
|
||||
{
|
||||
int InsertHookCode();
|
||||
int InsertReadCode();
|
||||
int UnsafeInsertHookCode();
|
||||
DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn);
|
||||
int RemoveHookCode();
|
||||
int RemoveReadCode();
|
||||
int SetHookName(LPCSTR name);
|
||||
public:
|
||||
int InsertHook();
|
||||
@ -71,4 +73,6 @@ DWORD WINAPI PipeManager(LPVOID unused);
|
||||
void CliLockPipe();
|
||||
void CliUnlockPipe();
|
||||
|
||||
enum : int { yes = 0, no = 1 };
|
||||
|
||||
// EOF
|
||||
|
Loading…
Reference in New Issue
Block a user