implement read code

This commit is contained in:
Akash Mozumdar 2018-08-04 18:01:59 -04:00
parent 9a299f357f
commit ed4879ba5c
7 changed files with 171 additions and 36 deletions

View File

@ -71,7 +71,7 @@ MainWindow::MainWindow(QWidget *parent) :
std::map<int, QString> extensions = LoadExtensions(); std::map<int, QString> extensions = LoadExtensions();
for (auto i : extensions) extenCombo->addItem(QString::number(i.first) + ":" + i.second); for (auto i : extensions) extenCombo->addItem(QString::number(i.first) + ":" + i.second);
Host::Open(); 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() MainWindow::~MainWindow()
@ -95,7 +95,7 @@ void MainWindow::AddProcess(unsigned int processId)
for (int j = 1; j < hooks.length(); ++j) for (int j = 1; j < hooks.length(); ++j)
{ {
Sleep(10); Sleep(10);
Host::InsertHook(processId, ParseHCode(hooks.at(j))); Host::InsertHook(processId, ParseCode(hooks.at(j)));
} }
return; return;
} }
@ -112,7 +112,7 @@ void MainWindow::AddThread(TextThread* thread)
TextThreadString(thread) + TextThreadString(thread) +
QString::fromWCharArray(Host::GetHookName(thread->GetThreadParameter().pid, thread->GetThreadParameter().hook).c_str()) + 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) thread->RegisterOutputCallBack([&](TextThread* thread, std::wstring output)
@ -188,15 +188,15 @@ void MainWindow::on_detachButton_clicked()
void MainWindow::on_hookButton_clicked() void MainWindow::on_hookButton_clicked()
{ {
bool ok; 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; if (!ok) return;
HookParam toInsert = ParseHCode(hookCode); HookParam toInsert = ParseCode(hookCode);
if (toInsert.type == 0 && toInsert.length_offset == 0) if (toInsert.type == 0 && toInsert.length_offset == 0)
{ {
Host::AddConsoleOutput(L"invalid /H code"); Host::AddConsoleOutput(L"invalid code");
return; return;
} }
Host::InsertHook(processCombo->currentText().split(":")[0].toInt(), ParseHCode(hookCode)); Host::InsertHook(processCombo->currentText().split(":")[0].toInt(), ParseCode(hookCode));
} }
void MainWindow::on_unhookButton_clicked() void MainWindow::on_unhookButton_clicked()
@ -206,7 +206,7 @@ void MainWindow::on_unhookButton_clicked()
for (auto i : hooks) hookList.push_back( for (auto i : hooks) hookList.push_back(
QString::fromWCharArray(Host::GetHookName(processCombo->currentText().split(":")[0].toInt(), i.address).c_str()) + 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; 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);
@ -219,7 +219,7 @@ void MainWindow::on_saveButton_clicked()
QString hookList = GetFullModuleName(processCombo->currentText().split(":")[0].toInt());; QString hookList = GetFullModuleName(processCombo->currentText().split(":")[0].toInt());;
for (auto i : hooks) for (auto i : hooks)
if (!(i.type & HOOK_ENGINE)) 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"); QFile file("SavedHooks.txt");
if (!file.open(QIODevice::Append | QIODevice::Text)) return; if (!file.open(QIODevice::Append | QIODevice::Text)) return;
file.write((hookList + "\r\n").toUtf8()); file.write((hookList + "\r\n").toUtf8());

View File

@ -40,12 +40,38 @@ DWORD Hash(QString module)
return hash; 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 ParseHCode(QString HCode)
{ {
HookParam hp = {}; HookParam hp = {};
HCode = HCode.toUpper();
if (!HCode.startsWith("/H")) return {};
HCode.remove(0, 2);
switch (HCode.at(0).unicode()) switch (HCode.at(0).unicode())
{ {
case L'S': case L'S':
@ -120,6 +146,14 @@ HookParam ParseHCode(QString HCode)
return hp; 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 GenerateHCode(HookParam hp, DWORD processId)
{ {
QString code = "/H"; QString code = "/H";
@ -183,3 +217,24 @@ QString GenerateHCode(HookParam hp, DWORD processId)
code += QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1); code += QString::fromWCharArray(wcsrchr(buffer, L'\\') + 1);
return code; 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);
}

View File

@ -8,16 +8,21 @@
QString GetFullModuleName(DWORD processId, HMODULE module = NULL); QString GetFullModuleName(DWORD processId, HMODULE module = NULL);
QString GetModuleName(DWORD processId, HMODULE module = NULL); QString GetModuleName(DWORD processId, HMODULE module = NULL);
QStringList GetAllProcesses(); QStringList GetAllProcesses();
HookParam ParseHCode(QString HCode); HookParam ParseCode(QString HCode);
QString GenerateHCode(HookParam hp, DWORD processId); QString GenerateCode(HookParam hp, DWORD processId);
static QString HCodeInfoDump = 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\ "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\ All numbers in hexadecimal\r\n\
A/B: Shift-JIS char little/big endian\r\n\ A/B: Shift-JIS char little/big endian\r\n\
W: UTF-16 char\r\n\ W: UTF-16 char\r\n\
S/Q/V: Shift-JIS/UTF-16/UTF-8 string\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\ 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\ -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 #endif // MISC_H

View File

@ -43,8 +43,10 @@ struct HookParam {
recover_len; // ? recover_len; // ?
// 7/20/2014: jichi additional parameters for PSP games // 7/20/2014: jichi additional parameters for PSP games
DWORD user_flags, DWORD user_value;
user_value;
// Artikash 8/4/2018: handle for reader thread
HANDLE readerHandle;
}; };
// jichi 6/1/2014: Structure of the esp for extern functions // jichi 6/1/2014: Structure of the esp for extern functions

View File

@ -16694,11 +16694,6 @@ void SpecialPSPHook(DWORD esp_base, HookParam *hp, BYTE, DWORD *data, DWORD *spl
LPCSTR text = LPCSTR(offset + hp->user_value); LPCSTR text = LPCSTR(offset + hp->user_value);
static LPCSTR lasttext; static LPCSTR lasttext;
if (*text) { if (*text) {
if (hp->user_flags & HPF_IgnoreSameAddress) {
if (text == lasttext)
return;
lasttext = text;
}
*data = (DWORD)text; *data = (DWORD)text;
// I only considered SHIFT-JIS/UTF-8 case // I only considered SHIFT-JIS/UTF-8 case
if (hp->length_offset == 1) if (hp->length_offset == 1)
@ -17451,7 +17446,6 @@ bool InsertImageepochPSPHook()
hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT; // UTF-8, though hp.type = USING_STRING|USING_SPLIT|NO_CONTEXT; // UTF-8, though
hp.offset = pusha_eax_off - 4; hp.offset = pusha_eax_off - 4;
hp.split = pusha_ecx_off - 4; hp.split = pusha_ecx_off - 4;
hp.user_flags = HPF_IgnoreSameAddress;
//hp.text_fun = SpecialPSPHook; //hp.text_fun = SpecialPSPHook;
hp.text_fun = SpecialPSPHookImageepoch; // since this function is common, use its own static lasttext for HPF_IgnoreSameAddress hp.text_fun = SpecialPSPHookImageepoch; // since this function is common, use its own static lasttext for HPF_IgnoreSameAddress
ConsoleOutput("vnreng: Imageepoch PSP: INSERT"); ConsoleOutput("vnreng: Imageepoch PSP: INSERT");

View File

@ -316,9 +316,11 @@ DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn)
int TextHook::InsertHook() int TextHook::InsertHook()
{ {
int ok = 1;
//ConsoleOutput("vnrcli:InsertHook: enter"); //ConsoleOutput("vnrcli:InsertHook: enter");
WaitForSingleObject(hmMutex, 0); WaitForSingleObject(hmMutex, 0);
int ok = InsertHookCode(); if (hp.type & DIRECT_READ) ok = InsertReadCode();
else ok = InsertHookCode();
ReleaseMutex(hmMutex); ReleaseMutex(hmMutex);
//ConsoleOutput("vnrcli:InsertHook: leave"); //ConsoleOutput("vnrcli:InsertHook: leave");
return ok; return ok;
@ -326,7 +328,6 @@ int TextHook::InsertHook()
int TextHook::InsertHookCode() int TextHook::InsertHookCode()
{ {
enum : int { yes = 0, no = 1 };
DWORD ret = no; DWORD ret = no;
// jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7 // jichi 9/17/2013: might raise 0xC0000005 AccessViolationException on win7
ITH_WITH_SEH(ret = UnsafeInsertHookCode()); ITH_WITH_SEH(ret = UnsafeInsertHookCode());
@ -335,10 +336,76 @@ int TextHook::InsertHookCode()
return ret; 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() int TextHook::UnsafeInsertHookCode()
{ {
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter"); //ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter");
enum : int { yes = 0, no = 1 };
if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address. if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address.
if (DWORD base = GetModuleBase(hp.module)) { if (DWORD base = GetModuleBase(hp.module)) {
hp.address += base; hp.address += base;
@ -467,29 +534,36 @@ int TextHook::InitHook(const HookParam &h, LPCSTR name, WORD set_flag)
int TextHook::RemoveHookCode() int TextHook::RemoveHookCode()
{ {
enum : int { yes = 1, no = 0 };
if (!hp.address) if (!hp.address)
return no; return no;
ConsoleOutput("vnrcli:RemoveHook: enter");
WaitForSingleObject(hmMutex, TIMEOUT); // jichi 9/28/2012: wait at most for 5 seconds
DWORD l = hp.hook_len; DWORD l = hp.hook_len;
//with_seh({ // jichi 9/17/2013: might crash >< //with_seh({ // jichi 9/17/2013: might crash ><
// jichi 12/25/2013: Actually, __try cannot catch such kind of exception // jichi 12/25/2013: Actually, __try cannot catch such kind of exception
ITH_TRY { ITH_TRY {
NtWriteVirtualMemory(GetCurrentProcess(), (LPVOID)hp.address, original, hp.recover_len, &l); NtWriteVirtualMemory(GetCurrentProcess(), (LPVOID)hp.address, original, hp.recover_len, &l);
NtFlushInstructionCache(GetCurrentProcess(), (LPVOID)hp.address, hp.recover_len); NtFlushInstructionCache(GetCurrentProcess(), (LPVOID)hp.address, hp.recover_len);
} ITH_EXCEPT {} }
ITH_EXCEPT {}
//}); //});
hp.hook_len = 0;
ReleaseMutex(hmMutex);
ConsoleOutput("vnrcli:RemoveHook: leave");
return yes; return yes;
} }
int TextHook::RemoveReadCode()
{
if (!hp.address) return no;
TerminateThread(hp.readerHandle, 0);
CloseHandle(hp.readerHandle);
return yes;
}
int TextHook::ClearHook() int TextHook::ClearHook()
{ {
int err;
WaitForSingleObject(hmMutex, 0); WaitForSingleObject(hmMutex, 0);
int err = RemoveHookCode(); ConsoleOutput("vnrcli:RemoveHook: enter");
if (hp.type & DIRECT_READ) err = RemoveReadCode();
else err = RemoveHookCode();
NotifyHookRemove(hp.address); NotifyHookRemove(hp.address);
if (hook_name) { if (hook_name) {
delete[] hook_name; delete[] hook_name;
@ -499,6 +573,7 @@ int TextHook::ClearHook()
//if (current_available>this) //if (current_available>this)
// current_available = this; // current_available = this;
currentHook--; currentHook--;
ConsoleOutput("vnrcli:RemoveHook: leave");
ReleaseMutex(hmMutex); ReleaseMutex(hmMutex);
return err; return err;
} }

View File

@ -29,9 +29,11 @@ void InitFilterTable();
class TextHook : public Hook class TextHook : public Hook
{ {
int InsertHookCode(); int InsertHookCode();
int InsertReadCode();
int UnsafeInsertHookCode(); int UnsafeInsertHookCode();
DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn); DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn);
int RemoveHookCode(); int RemoveHookCode();
int RemoveReadCode();
int SetHookName(LPCSTR name); int SetHookName(LPCSTR name);
public: public:
int InsertHook(); int InsertHook();
@ -71,4 +73,6 @@ DWORD WINAPI PipeManager(LPVOID unused);
void CliLockPipe(); void CliLockPipe();
void CliUnlockPipe(); void CliUnlockPipe();
enum : int { yes = 0, no = 1 };
// EOF // EOF