/* Copyright (C) 2010-2012 kaosu (qiupf2000@gmail.com)
* This file is part of the Interactive Text Hooker.
* Interactive Text Hooker is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
#include "utility.h"
#include "ith/host/srv.h"
#include "ith/host/hookman.h"
#include "ith/common/types.h"
#include "ith/common/const.h"
extern HookManager* man; // main.cpp
std::wstring GetDriveLetter(const std::wstring& devicePath);
std::wstring GetWindowsPath(const std::wstring& fileObjectPath);
PVOID GetAllocationBase(DWORD pid, LPCVOID);
std::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase);
std::wstring GetModuleFileNameAsString();
std::wstring GetProcessPath(HANDLE hProc);
void ConsoleOutput(LPCWSTR text)
{
man->AddConsoleOutput(text);
}
void ConsoleOutput(LPCSTR text)
{
int wc_length = MB_WC_count(text, -1);
LPWSTR wc = new WCHAR[wc_length];
MB_WC(text, wc, wc_length);
man->AddConsoleOutput(wc);
delete wc;
}
std::wstring GetProcessPath(DWORD pid)
{
UniqueHandle hProc(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
if (hProc)
return GetProcessPath(hProc.get());
else
return L"";
}
std::wstring GetProcessPath(HANDLE hProc)
{
wchar_t path[MAX_PATH];
GetProcessImageFileName(hProc, path, MAX_PATH);
return GetWindowsPath(path);
}
std::wstring GetWindowsPath(const std::wstring& path)
{
// path is in device form
// \Device\HarddiskVolume2\Windows\System32\taskhost.exe
auto pathOffset = path.find(L'\\', 1) + 1;
pathOffset = path.find(L'\\', pathOffset);
std::wstring devicePath = path.substr(0, pathOffset); // \Device\HarddiskVolume2
std::wstring dosDrive = GetDriveLetter(devicePath); // C:
if (dosDrive.empty())
return L"";
std::wstring dosPath = dosDrive; // C:
dosPath += path.substr(pathOffset); // C:\Windows\System32\taskhost.exe
return dosPath;
}
std::wstring GetDriveLetter(const std::wstring& devicePath)
{
for (wchar_t drive = L'A'; drive <= L'Z'; drive++)
{
wchar_t szDriveName[3] = { drive, L':', L'\0' };
wchar_t szTarget[512];
if (QueryDosDevice(szDriveName, szTarget, 512))
if (devicePath.compare(szTarget) == 0)
return szDriveName;
}
return L"";
}
std::wstring GetCode(const HookParam& hp, DWORD pid)
{
std::wstring code(L"/H");
WCHAR c;
if (hp.type & PRINT_DWORD)
c = L'H';
else if (hp.type & USING_UNICODE)
{
if (hp.type & USING_STRING)
c = L'Q';
else if (hp.type & STRING_LAST_CHAR)
c = L'L';
else
c = L'W';
}
else
{
if (hp.type & USING_STRING)
c = L'S';
else if (hp.type & BIG_ENDIAN)
c = L'A';
else if (hp.type & STRING_LAST_CHAR)
c = L'E';
else
c = L'B';
}
code += c;
if (hp.type & NO_CONTEXT)
code += L'N';
if (hp.off >> 31)
code += L"-" + ToHexString(-(hp.off + 4));
else
code += ToHexString(hp.off);
if (hp.type & DATA_INDIRECT)
{
if (hp.ind >> 31)
code += L"*-" + ToHexString(-hp.ind);
else
code += L"*" + ToHexString(hp.ind);
}
if (hp.type & USING_SPLIT)
{
if (hp.split >> 31)
code += L":-" + ToHexString(-(4 + hp.split));
else
code += L":" + ToHexString(hp.split);
}
if (hp.type & SPLIT_INDIRECT)
{
if (hp.split_ind >> 31)
code += L"*-" + ToHexString(-hp.split_ind);
else
code += L"*" + ToHexString(hp.split_ind);
}
if (pid)
{
PVOID allocationBase = GetAllocationBase(pid, (LPCVOID)hp.addr);
if (allocationBase)
{
std::wstring path = GetModuleFileNameAsString(pid, allocationBase);
if (!path.empty())
{
auto fileName = path.substr(path.rfind(L'\\') + 1);
DWORD relativeHookAddress = hp.addr - (DWORD)allocationBase;
code += L"@" + ToHexString(relativeHookAddress) + L":" + fileName;
return code;
}
}
}
if (hp.module)
{
code += L"@" + ToHexString(hp.addr) + L"!" + ToHexString(hp.module);
if (hp.function)
code += L"!" + ToHexString(hp.function);
}
else
{
// hack, the original address is stored in the function field
// if (module == NULL && function != NULL)
// in TextHook::UnsafeInsertHookCode() MODULE_OFFSET and FUNCTION_OFFSET are removed from
// HookParam.type
if (hp.function)
code += L"@" + ToHexString(hp.function);
else
code += L"@" + ToHexString(hp.addr) + L":";
}
return code;
}
std::wstring GetModuleFileNameAsString(DWORD pid, PVOID allocationBase)
{
const ProcessRecord* pr = man->GetProcessRecord(pid);
if (pr)
{
HANDLE hProc = pr->process_handle;
WCHAR path[MAX_PATH];
if (GetModuleFileNameEx(hProc, (HMODULE)allocationBase, path, MAX_PATH))
return path;
}
return L"";
}
PVOID GetAllocationBase(DWORD pid, LPCVOID addr)
{
const ProcessRecord *pr = man->GetProcessRecord(pid);
if (pr)
{
MEMORY_BASIC_INFORMATION info;
HANDLE hProc = pr->process_handle;
if (VirtualQueryEx(hProc, addr, &info, sizeof(info)))
{
if (info.Type & MEM_IMAGE)
return info.AllocationBase;
}
}
return NULL;
}
struct TitleParam
{
DWORD pid, buffer_len, retn_len;
std::wstring buffer;
};
BOOL CALLBACK EnumProc(HWND hwnd, LPARAM lParam)
{
TitleParam* p = (TitleParam*)lParam;
DWORD pid;
GetWindowThreadProcessId(hwnd, &pid);
if (pid == p->pid)
{
if (GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE)
{
int len = GetWindowTextLength(hwnd);
std::unique_ptr result(new wchar_t[len + 1]);
GetWindowText(hwnd, result.get(), len + 1);
p->buffer = result.get();
p->retn_len = p->buffer.size();
if (!p->buffer.empty())
return FALSE;
}
}
return TRUE;
}
std::wstring GetProcessTitle(DWORD pid)
{
TitleParam p;
p.pid = pid;
p.buffer_len = 0;
p.retn_len = 0;
EnumWindows(EnumProc, (LPARAM)&p);
return p.buffer;
}
WindowsError::WindowsError(DWORD error_code) : error_code(error_code), msg("")
{
CHAR str[512];
std::sprintf(str, "error code 0x%8x", error_code);
msg = str;
}
const char *WindowsError::what() const
{
return msg.c_str();
}
HANDLE IthCreateThread(LPVOID start_addr, DWORD param)
{
return CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)start_addr, (LPVOID)param, 0, NULL);
}
std::wstring GetModuleFileNameAsString()
{
WCHAR path[MAX_PATH];
GetModuleFileName(NULL, path, MAX_PATH);
return path;
}
bool IthCreateDirectory(LPCWSTR name)
{
std::wstring path = GetModuleFileNameAsString();
path = path.substr(0, path.rfind(L'\\') + 1) + name;
BOOL error_code = CreateDirectory(path.c_str(), NULL);
return error_code != 0 || GetLastError() == ERROR_ALREADY_EXISTS;
}
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition)
{
std::wstring path = GetModuleFileNameAsString();
path = path.substr(0, path.rfind(L'\\') + 1) + name;
return CreateFile(path.c_str(), option, share, NULL, disposition, FILE_ATTRIBUTE_NORMAL, NULL);
}
//SJIS->Unicode. 'mb' must be null-terminated. 'wc_length' is the length of 'wc' in characters.
int MB_WC(const char* mb, wchar_t* wc, int wc_length)
{
return MultiByteToWideChar(932, 0, mb, -1, wc, wc_length);
}
// Count characters in wide string. 'mb_length' is the number of bytes from 'mb' to convert or
// -1 if the string is null terminated.
int MB_WC_count(const char* mb, int mb_length)
{
return MultiByteToWideChar(932, 0, mb, mb_length, NULL, 0);
}
// Unicode->SJIS. Analogous to MB_WC.
int WC_MB(const wchar_t *wc, char* mb, int mb_length)
{
return WideCharToMultiByte(932, 0, wc, -1, mb, mb_length, NULL, NULL);
}
DWORD Hash(const std::wstring& module, int length)
{
DWORD hash = 0;
auto end = length < 0 || static_cast(length) > module.length() ? module.end() : module.begin() + length;
for (auto it = module.begin(); it != end; ++it)
hash = _rotr(hash, 7) + *it;
return hash;
}