forked from Public-Mirror/Textractor
276 lines
6.4 KiB
C++
276 lines
6.4 KiB
C++
|
// ith_p.cc
|
||
|
// 10/15/2011 jichi
|
||
|
|
||
|
#include "texthook/ith_p.h"
|
||
|
#include "vnrhook/include/const.h"
|
||
|
#include "vnrhook/include/types.h"
|
||
|
#include <string>
|
||
|
|
||
|
#define DEBUG "ith_p.cc"
|
||
|
#include "sakurakit/skdebug.h"
|
||
|
|
||
|
// HookParam copied from ITH/common.h:
|
||
|
// struct HookParam // size = 40 (0x24)
|
||
|
// {
|
||
|
// typedef void (*DataFun)(DWORD, HookParam*, DWORD*, DWORD*, DWORD*);
|
||
|
//
|
||
|
// DWORD addr; // 4
|
||
|
// DWORD off, // 8
|
||
|
// ind, // 12
|
||
|
// split, // 16
|
||
|
// split_ind; // 20
|
||
|
// DWORD module, // 24
|
||
|
// function; // 28
|
||
|
// DataFun text_fun; // 32, jichi: is this the same in x86 and x86_64?
|
||
|
// DWORD type; // 36
|
||
|
// WORD length_offset; // 38
|
||
|
// BYTE hook_len, // 39
|
||
|
// recover_len; // 40
|
||
|
// };
|
||
|
|
||
|
|
||
|
// - Implementation Details -
|
||
|
|
||
|
namespace { namespace detail { // unnamed
|
||
|
|
||
|
// ITH ORIGINAL CODE BEGIN
|
||
|
|
||
|
// See: ITH/ITH.h
|
||
|
// Revision: 133
|
||
|
inline DWORD Hash(_In_ LPWSTR module, int length = -1)
|
||
|
{
|
||
|
bool flag = length == -1;
|
||
|
DWORD hash = 0;
|
||
|
for (; *module && (flag || length--); module++)
|
||
|
hash = _rotr(hash,7) + *module; //hash=((hash>>7)|(hash<<25))+(*module);
|
||
|
return hash;
|
||
|
}
|
||
|
|
||
|
// See: ITH/command.cpp
|
||
|
// Revision: 133
|
||
|
//
|
||
|
// jichi note: str[0xF] will be modified and restored.
|
||
|
// So, the buffer of str must be larger than 0xF.
|
||
|
int Convert(_In_ LPWSTR str, _Out_ DWORD *num, _In_ LPWSTR delim)
|
||
|
{
|
||
|
if (!num)
|
||
|
return -1;
|
||
|
WCHAR t = *str,
|
||
|
tc = *(str + 0xF);
|
||
|
WCHAR temp[0x10] = {};
|
||
|
LPWSTR it = temp,
|
||
|
istr = str,
|
||
|
id = temp;
|
||
|
if (delim) {
|
||
|
id = wcschr(delim, t);
|
||
|
str[0xF] = delim[0]; // reset str[0xF] in case of out-of-bound iteration
|
||
|
}
|
||
|
else
|
||
|
str[0xF] = 0; // reset str[0xF] in case of out-of-bound iteration
|
||
|
while (!id && t) {
|
||
|
*it = t;
|
||
|
it++; istr++;
|
||
|
t = *istr;
|
||
|
if (delim)
|
||
|
id = wcschr(delim, t);
|
||
|
}
|
||
|
swscanf(temp, L"%x", num);
|
||
|
str[0xF] = tc; // restore the str[0xF]
|
||
|
if (!id || istr - str == 0xF)
|
||
|
return -1;
|
||
|
|
||
|
if (!t)
|
||
|
return istr - str; // >= 0
|
||
|
else
|
||
|
return id - delim; // >= 0
|
||
|
}
|
||
|
|
||
|
// See: ITH/command.cpp
|
||
|
// Revision: 133
|
||
|
//
|
||
|
// jichi note: str[0xF] will be modified and restored.
|
||
|
// So, the buffer of cmd must be larger than 0xF*2 = 0x1F.
|
||
|
bool Parse(_In_ LPWSTR cmd, _Out_ HookParam &hp)
|
||
|
{
|
||
|
::memset(&hp, 0, sizeof(hp));
|
||
|
|
||
|
int t;
|
||
|
bool accept = false;
|
||
|
DWORD *data = &hp.offset; //
|
||
|
LPWSTR offset = cmd + 1;
|
||
|
LPWSTR delim_str = L":*@!";
|
||
|
LPWSTR delim = delim_str;
|
||
|
if (*offset == L'n' || *offset == 'N') {
|
||
|
offset++;
|
||
|
hp.type |= NO_CONTEXT;
|
||
|
}
|
||
|
// jichi 4/25/2015: Add support for fixing hook
|
||
|
if (*offset == L'f' || *offset == 'F') {
|
||
|
offset++;
|
||
|
hp.type |= FIXING_SPLIT;
|
||
|
}
|
||
|
if (*offset == L'j' || *offset == 'J') { // 11/22/2015: J stands for Japanese only
|
||
|
offset++;
|
||
|
hp.type |= NO_ASCII;
|
||
|
}
|
||
|
while (!accept) {
|
||
|
t = Convert(offset, data, delim);
|
||
|
if (t < 0)
|
||
|
return false; //ConsoleOutput(L"Syntax error.");
|
||
|
offset = ::wcschr(offset , delim[t]);
|
||
|
if (offset)
|
||
|
offset++; // skip the current delim
|
||
|
else //goto _error;
|
||
|
return false; //ConsoleOutput(L"Syntax error.");
|
||
|
switch (delim[t]) {
|
||
|
case L':':
|
||
|
data = &hp.split;
|
||
|
delim = delim_str + 1;
|
||
|
hp.type |= USING_SPLIT;
|
||
|
break;
|
||
|
case L'*':
|
||
|
if (hp.split) {
|
||
|
data = &hp.split_index;
|
||
|
delim = delim_str + 2;
|
||
|
hp.type |= SPLIT_INDIRECT;
|
||
|
}
|
||
|
else {
|
||
|
hp.type |= DATA_INDIRECT;
|
||
|
data = &hp.index;
|
||
|
}
|
||
|
break;
|
||
|
case L'@':
|
||
|
accept = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
t = Convert(offset, &hp.address, delim_str);
|
||
|
if (t < 0)
|
||
|
return false;
|
||
|
if (hp.offset & 0x80000000)
|
||
|
hp.offset -= 4;
|
||
|
if (hp.split & 0x80000000)
|
||
|
hp.split -= 4;
|
||
|
LPWSTR temp = offset;
|
||
|
offset = ::wcschr(offset, L':');
|
||
|
if (offset) {
|
||
|
hp.type |= MODULE_OFFSET;
|
||
|
offset++;
|
||
|
delim = ::wcschr(offset, L':');
|
||
|
|
||
|
if (delim) {
|
||
|
*delim = 0;
|
||
|
delim++;
|
||
|
_wcslwr(offset);
|
||
|
hp.function = Hash(delim);
|
||
|
hp.module = Hash(offset, delim - offset - 1);
|
||
|
hp.type |= FUNCTION_OFFSET;
|
||
|
}
|
||
|
else
|
||
|
hp.module = Hash(_wcslwr(offset));
|
||
|
|
||
|
} else {
|
||
|
offset = ::wcschr(temp, L'!');
|
||
|
if (offset) {
|
||
|
hp.type |= MODULE_OFFSET;
|
||
|
swscanf(offset + 1, L"%x", &hp.module);
|
||
|
offset = ::wcschr(offset + 1, L'!');
|
||
|
if (offset) {
|
||
|
hp.type |= FUNCTION_OFFSET;
|
||
|
swscanf(offset + 1, L"%x", &hp.function);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
switch (*cmd) {
|
||
|
case L's':
|
||
|
case L'S':
|
||
|
hp.type |= USING_STRING;
|
||
|
break;
|
||
|
case L'e':
|
||
|
case L'E':
|
||
|
hp.type |= STRING_LAST_CHAR;
|
||
|
case L'a':
|
||
|
case L'A':
|
||
|
hp.type |= BIG_ENDIAN;
|
||
|
hp.length_offset = 1;
|
||
|
break;
|
||
|
case L'b':
|
||
|
case L'B':
|
||
|
hp.length_offset = 1;
|
||
|
break;
|
||
|
// jichi 12/7/2014: Disabled
|
||
|
//case L'h':
|
||
|
//case L'H':
|
||
|
// hp.type |= PRINT_DWORD;
|
||
|
case L'q':
|
||
|
case L'Q':
|
||
|
hp.type |= USING_STRING | USING_UNICODE;
|
||
|
break;
|
||
|
case L'l':
|
||
|
case L'L':
|
||
|
hp.type |= STRING_LAST_CHAR;
|
||
|
case L'w':
|
||
|
case L'W':
|
||
|
hp.type |= USING_UNICODE;
|
||
|
hp.length_offset = 1;
|
||
|
break;
|
||
|
default: ;
|
||
|
}
|
||
|
//ConsoleOutput(L"Try to insert additional hook.");
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// ITH ORIGINAL CODE END
|
||
|
|
||
|
}} // unnamed detail
|
||
|
|
||
|
// - ITH API -
|
||
|
|
||
|
// Sample code: L"/HS-4:-14@4383C0" (WHITE ALBUM 2)
|
||
|
bool Ith::parseHookCode(const QString &code, HookParam *hp, bool verbose)
|
||
|
{
|
||
|
#define HCODE_PREFIX "/H"
|
||
|
enum { HCODE_PREFIX_LEN = sizeof(HCODE_PREFIX) -1 }; // 2
|
||
|
if (!hp || !code.startsWith(HCODE_PREFIX))
|
||
|
return false;
|
||
|
if (verbose)
|
||
|
DOUT("enter: code =" << code);
|
||
|
else
|
||
|
DOUT("enter");
|
||
|
|
||
|
size_t bufsize = qMax(0xFF, code.size() + 1); // in case detail::Convert modify the buffer
|
||
|
auto buf = new wchar_t[bufsize];
|
||
|
code.toWCharArray(buf);
|
||
|
buf[code.size()] = 0;
|
||
|
|
||
|
bool ret = detail::Parse(buf + HCODE_PREFIX_LEN, *hp);
|
||
|
delete[] buf;
|
||
|
#ifdef DEBUG
|
||
|
if (ret && verbose)
|
||
|
qDebug()
|
||
|
<< "addr:" << hp->address
|
||
|
<< ", text_fun:" << hp->text_fun
|
||
|
<< ", function:"<< hp->function
|
||
|
<< ", hook_len:" << hp->hook_len
|
||
|
<< ", ind:" << hp->index
|
||
|
<< ", length_offset:" << hp->length_offset
|
||
|
<< ", module:" << hp->module
|
||
|
<< ", off:" <<hp->offset
|
||
|
<< ", recover_len:" << hp->recover_len
|
||
|
<< ", split:" << hp->split
|
||
|
<< ", split_ind:" << hp->split_index
|
||
|
<< ", type:" << hp->type;
|
||
|
#endif // DEBUG
|
||
|
DOUT("leave: ret =" << ret);
|
||
|
return ret;
|
||
|
#undef HOOK_CODE_PREFIX
|
||
|
}
|
||
|
|
||
|
bool Ith::verifyHookCode(const QString &code)
|
||
|
{
|
||
|
HookParam hp = {};
|
||
|
return parseHookCode(code, &hp);
|
||
|
}
|
||
|
|
||
|
// EOF
|