mireado

exclude useless file

Showing 63 changed files with 0 additions and 12899 deletions
/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
template <class T, unsigned int default_size>
class PointerTable
{
public:
PointerTable()
{
assert((default_size & (default_size - 1)) == 0);
size = default_size;
table = new T*[size];
used = 0;
next = 0;
memset(table, 0, size * sizeof(T*));
}
~PointerTable()
{
delete table;
}
T* Set(unsigned int number, T* ptr)
{
if (number >= size - 2)
{
unsigned int new_size = size;
while (number >= new_size - 2) new_size <<= 1;
Resize(new_size);
}
T* original = table[number + 1];
table[number + 1] = ptr;
if (ptr == 0) //Clear pointer.
{
if (number < next) next = number;
if (number == used - 1) //Last used position is cleared.
{
table[0] = (T*)1;
for (used--; table[used] == 0; used--);
}
}
else //Set pointer.
{
__assume(number < size - 2); //Otherwise a resize operation is invoked.
if (number == next)
{
next++; //Next position is occupied.
for (next++; table[next]; next++); //There is always a zero in the end.
next--; //next is zero based but the table start at one(zero is used as sentry).
}
if (number >= used) used = number + 1;
}
return original;
}
T* Get(unsigned int number)
{
number++;
if (number <= used) return table[number];
else return 0;
}
T* operator [](unsigned int number)
{
number++;
if (number <= used) return table[number];
else return 0;
}
void Append(T* ptr)
{
Set(next,ptr);
}
void Resize(unsigned int new_size)
{
assert(new_size > size);
assert((new_size & (new_size - 1)) == 0);
assert(new_size < 0x10000);
T** temp = new T*[new_size];
memcpy(temp, table, size * sizeof(T*));
memset(temp + size, 0, (new_size - size) * sizeof(T*));
delete table;
size = new_size;
table = temp;
}
void DeleteAll() //Release all pointers on demand.
{
T* p;
next = 0;
while (used)
{
p = table[used];
if (p) delete p;
table[used] = 0;
used--;
}
}
void Reset() //Reset without release pointers.
{
memset(table, 0, sizeof(T*) * (used + 1));
next = 0;
used = 0;
}
unsigned int size,next,used;
T** table;
};
/* 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 <http://www.gnu.org/licenses/>.
*/
#include "ITH.h"
#include "ith/host/srv.h"
#include "ith/host/hookman.h"
#include "ith/common/types.h"
#include "ith/common/const.h"
#include "Profile.h"
#include "utility.h"
Profile::Profile(const std::wstring& title) :
select_index(-1),
title(title)
{}
std::vector<thread_ptr>::const_iterator Profile::FindThreadProfile(const ThreadParameter& tp) const
{
auto thread_profile = std::find_if(threads.begin(), threads.end(),
[&tp](const thread_ptr& thread_profile) -> bool
{
if (thread_profile->HookAddress() != tp.hook)
return false;
DWORD t1 = thread_profile->Return();
DWORD t2 = tp.retn;
if (thread_profile->Flags() & THREAD_MASK_RETN)
{
t1 &= 0xFFFF;
t2 &= 0xFFFF;
}
if (t1 != t2)
return false;
t1 = thread_profile->Split();
t2 = tp.spl;
if (thread_profile->Flags() & THREAD_MASK_SPLIT)
{
t1 &= 0xFFFF;
t2 &= 0xFFFF;
}
return t1 == t2;
});
return thread_profile;
}
const std::vector<hook_ptr>& Profile::Hooks() const
{
return hooks;
}
const std::vector<thread_ptr>& Profile::Threads() const
{
return threads;
}
const std::vector<link_ptr>& Profile::Links() const
{
return links;
}
bool Profile::XmlReadProfile(pugi::xml_node profile)
{
auto hooks_node = profile.child(L"Hooks");
auto threads_node = profile.child(L"Threads");
auto links_node = profile.child(L"Links");
if (hooks_node && !XmlReadProfileHook(hooks_node))
return false;
if (threads_node && !XmlReadProfileThread(threads_node))
return false;
if (links_node && !XmlReadProfileLink(links_node))
return false;
auto select_node = profile.child(L"Select");
if (select_node)
{
auto thread_index = select_node.attribute(L"ThreadIndex");
if (!thread_index)
return false;
DWORD tmp_select = std::stoul(thread_index.value(), NULL, 16);
select_index = tmp_select & 0xFFFF;
}
return true;
}
bool Profile::XmlReadProfileHook(pugi::xml_node hooks_node)
{
for (auto hook = hooks_node.begin(); hook != hooks_node.end(); ++hook)
{
std::wstring name = hook->name();
if (name.empty() || name.compare(L"Hook") != 0)
return false;
auto type = hook->attribute(L"Type");
if (!type || type.empty())
return false;
auto code = hook->attribute(L"Code");
if (!code)
return false;
std::wstring code_value = code.value();
HookParam hp = {};
switch (type.value()[0])
{
case L'H':
if (code_value[0] != L'/')
return false;
if (code_value[1] != L'H' && code_value[1] != L'h')
return false;
if (Parse(code_value.substr(2), hp))
{
auto name = hook->attribute(L"Name");
if (!name || name.empty())
AddHook(hp, L"");
else
AddHook(hp, name.value());
}
break;
default:
return false;
}
}
return true;
}
bool Profile::XmlReadProfileThread(pugi::xml_node threads_node)
{
std::wstring hook_name_buffer;
for (auto thread = threads_node.begin(); thread != threads_node.end(); ++thread)
{
std::wstring name = thread->name();
if (name.empty() || name.compare(L"Thread") != 0)
return false;
auto hook_name = thread->attribute(L"HookName");
if (!hook_name)
return false;
auto context = thread->attribute(L"Context");
if (!context)
return false;
auto sub_context = thread->attribute(L"SubContext");
if (!sub_context)
return false;
auto mask = thread->attribute(L"Mask");
if (!mask)
return false;
DWORD mask_tmp = std::stoul(mask.value(), NULL, 16);
auto comment = thread->attribute(L"Comment");
auto retn = std::stoul(context.value(), NULL, 16);
WORD hm_index = 0;
auto hook_addr = 0;
auto split = std::stoul(sub_context.value(), NULL, 16);
WORD flags = mask_tmp & 0xFFFF;
auto tp = new ThreadProfile(hook_name.value(), retn, split, hook_addr, hm_index, flags,
comment.value());
AddThread(thread_ptr(tp));
}
return true;
}
bool Profile::XmlReadProfileLink(pugi::xml_node links_node)
{
for (auto link = links_node.begin(); link != links_node.end(); ++link)
{
std::wstring name = link->name();
if (name.empty() || name.compare(L"Link") != 0)
return false;
auto from = link->attribute(L"From");
if (!from)
return false;
DWORD link_from = std::stoul(from.value(), NULL, 16);
auto to = link->attribute(L"To");
if (!to)
return false;
DWORD link_to = std::stoul(to.value(), NULL, 16);
auto lp = new LinkProfile(link_from & 0xFFFF, link_to & 0xFFFF);
AddLink(link_ptr(lp));
}
return true;
}
bool Profile::XmlWriteProfile(pugi::xml_node profile_node)
{
if (!hooks.empty())
{
auto node = profile_node.append_child(L"Hooks");
XmlWriteProfileHook(node);
}
if (!threads.empty())
{
auto node = profile_node.append_child(L"Threads");
XmlWriteProfileThread(node);
}
if (!links.empty())
{
auto node = profile_node.append_child(L"Links");
XmlWriteProfileLink(node);
}
if (select_index != 0xFFFF)
{
auto node = profile_node.append_child(L"Select");
node.append_attribute(L"ThreadIndex") = select_index;
}
return true;
}
bool Profile::XmlWriteProfileHook(pugi::xml_node hooks_node)
{
for (auto hook = hooks.begin(); hook != hooks.end(); ++hook)
{
auto hook_node = hooks_node.append_child(L"Hook");
hook_node.append_attribute(L"Type") = L"H";
hook_node.append_attribute(L"Code") = GetCode((*hook)->HP()).c_str();
if (!(*hook)->Name().empty())
hook_node.append_attribute(L"Name") = (*hook)->Name().c_str();
}
return true;
}
bool Profile::XmlWriteProfileThread(pugi::xml_node threads_node)
{
for (auto thread = threads.begin(); thread != threads.end(); ++thread)
{
const std::wstring& name = (*thread)->HookName();
if (name.empty())
return false;
auto node = threads_node.append_child(L"Thread");
node.append_attribute(L"HookName") = name.c_str();
node.append_attribute(L"Mask") = ToHexString((*thread)->Flags() & 3).c_str();
node.append_attribute(L"SubContext") = ToHexString((*thread)->Split()).c_str();
node.append_attribute(L"Context") = ToHexString((*thread)->Return()).c_str();
if (!(*thread)->Comment().empty())
node.append_attribute(L"Comment") = (*thread)->Comment().c_str();
}
return true;
}
bool Profile::XmlWriteProfileLink(pugi::xml_node links_node)
{
for (auto link = links.begin(); link != links.end(); ++link)
{
auto node = links_node.append_child(L"Link");
node.append_attribute(L"From") = ToHexString((*link)->FromIndex()).c_str();
node.append_attribute(L"To") = ToHexString((*link)->ToIndex()).c_str();
}
return true;
}
void Profile::Clear()
{
title = L"";
select_index = -1;
hooks.clear();
threads.clear();
links.clear();
}
int Profile::AddHook(const HookParam& hp, const std::wstring& name)
{
//if (hook_count == 4) return;
auto it = std::find_if(hooks.begin(), hooks.end(), [&hp](hook_ptr& hook)
{
return hook->HP().addr == hp.addr &&
hook->HP().module == hp.module &&
hook->HP().function == hp.function;
});
if (it != hooks.end())
return it - hooks.begin();
hooks.emplace_back(new HookProfile(hp, name));
return hooks.size() - 1;
}
// add the thread profile and return its index
int Profile::AddThread(thread_ptr tp)
{
auto it = std::find_if(threads.begin(), threads.end(), [&tp](thread_ptr& thread)
{
return thread->HookName().compare(tp->HookName()) == 0 &&
thread->Return() == tp->Return() &&
thread->Split() == tp->Split();
});
if (it != threads.end())
return it - threads.begin();
threads.push_back(std::move(tp));
return threads.size() - 1;
}
int Profile::AddLink(link_ptr lp)
{
auto it = std::find_if(links.begin(), links.end(), [&lp] (link_ptr& link)
{
return link->FromIndex() == lp->FromIndex() &&
link->ToIndex() == lp->ToIndex();
});
if (it != links.end())
return it - links.begin();
links.push_back(std::move(lp));
return links.size() - 1;
}
void Profile::RemoveHook(DWORD index)
{
if (index >= 0 && index < hooks.size())
hooks.erase(hooks.begin() + index);
}
void Profile::RemoveThread(DWORD index)
{
if (index >= 0 && index < threads.size())
{
links.erase(std::remove_if(links.begin(), links.end(), [index](link_ptr& link)
{
return link->FromIndex() == index + 1 || link->ToIndex() == index + 1;
}), links.end());
if (select_index == index)
select_index = -1;
threads.erase(threads.begin() + index);
if (index < select_index)
select_index--;
}
}
void Profile::RemoveLink(DWORD index)
{
if (index >= 0 && index < links.size())
links.erase(links.begin() + index);
}
const std::wstring& Profile::Title() const
{
return title;
}
/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "ITH.h"
#include "ith/common/types.h" // HookParam
struct ThreadParameter;
#define THREAD_MASK_RETN 1
#define THREAD_MASK_SPLIT 2
class HookProfile
{
HookParam hp;
std::wstring name;
public:
HookProfile(const HookParam& hp, const std::wstring& name):
hp(hp),
name(name)
{}
const HookParam& HP() const { return hp; };
const std::wstring& Name() const { return name; };
};
class ThreadProfile
{
std::wstring hook_name;
DWORD retn;
DWORD split;
DWORD hook_addr;
WORD hm_index, flags;
std::wstring comment;
public:
ThreadProfile(const std::wstring& hook_name,
DWORD retn,
DWORD split,
DWORD hook_addr,
WORD hm_index,
WORD flags,
const std::wstring& comment) :
hook_name(hook_name),
retn(retn),
split(split),
hook_addr(hook_addr),
hm_index(hm_index),
flags(flags),
comment(comment)
{
}
const std::wstring& HookName() const { return hook_name; }
const std::wstring& Comment() const { return comment; }
DWORD Return() const { return retn; }
DWORD Split() const { return split; }
DWORD& HookAddress() { return hook_addr; }
WORD& HookManagerIndex() { return hm_index; }
WORD Flags() const { return flags; }
};
class LinkProfile
{
WORD from_index, to_index;
public:
LinkProfile(WORD from_index, WORD to_index):
from_index(from_index),
to_index(to_index)
{}
WORD FromIndex() const { return from_index; }
WORD ToIndex() const { return to_index; }
};
typedef std::unique_ptr<HookProfile> hook_ptr;
typedef std::unique_ptr<ThreadProfile> thread_ptr;
typedef std::unique_ptr<LinkProfile> link_ptr;
class Profile
{
public:
Profile(const std::wstring& title);
bool XmlReadProfile(pugi::xml_node profile_node);
bool XmlWriteProfile(pugi::xml_node profile_node);
int AddHook(const HookParam& hp, const std::wstring& name);
int AddThread(thread_ptr tp);
int AddLink(link_ptr lp);
void Clear();
const std::vector<hook_ptr>& Hooks() const;
const std::vector<thread_ptr>& Threads() const;
const std::vector<link_ptr>& Links() const;
const std::wstring& Title() const;
std::vector<thread_ptr>::const_iterator FindThreadProfile(const ThreadParameter& tp) const;
WORD& SelectedIndex() { return select_index; }
private:
void RemoveLink(DWORD index);
void RemoveHook(DWORD index);
void RemoveThread(DWORD index);
bool XmlReadProfileHook(pugi::xml_node hooks_node);
bool XmlReadProfileThread(pugi::xml_node threads_node);
bool XmlReadProfileLink(pugi::xml_node links_node);
bool XmlWriteProfileHook(pugi::xml_node hooks_node);
bool XmlWriteProfileThread(pugi::xml_node threads_node);
bool XmlWriteProfileLink(pugi::xml_node links_node);
std::wstring title;
std::vector<hook_ptr> hooks;
std::vector<thread_ptr> threads;
std::vector<link_ptr> links;
WORD select_index;
};
/**
* pugixml parser - version 1.5
* --------------------------------------------------------
* Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at http://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
*
* This work is based on the pugxml parser, which is:
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/
#ifndef HEADER_PUGICONFIG_HPP
#define HEADER_PUGICONFIG_HPP
// Uncomment this to enable wchar_t mode
#define PUGIXML_WCHAR_MODE
// Uncomment this to disable XPath
// #define PUGIXML_NO_XPATH
// Uncomment this to disable STL
// #define PUGIXML_NO_STL
// Uncomment this to disable exceptions
// #define PUGIXML_NO_EXCEPTIONS
// Set this to control attributes for public classes/functions, i.e.:
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
// Tune these constants to adjust memory-related behavior
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
// Uncomment this to switch to header-only version
// #define PUGIXML_HEADER_ONLY
// #include "pugixml.cpp"
// Uncomment this to enable long long support
// #define PUGIXML_HAS_LONG_LONG
#endif
/**
* Copyright (c) 2006-2014 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
This diff could not be displayed because it is too large.
/**
* pugixml parser - version 1.5
* --------------------------------------------------------
* Copyright (C) 2006-2014, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
* Report bugs and download new versions at http://pugixml.org/
*
* This library is distributed under the MIT License. See notice at the end
* of this file.
*
* This work is based on the pugxml parser, which is:
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
*/
#ifndef PUGIXML_VERSION
// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons
# define PUGIXML_VERSION 150
#endif
// Include user configuration file (this can define various configuration macros)
#include "pugiconfig.hpp"
#ifndef HEADER_PUGIXML_HPP
#define HEADER_PUGIXML_HPP
// Include stddef.h for size_t and ptrdiff_t
#include <stddef.h>
// Include exception header for XPath
#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS)
# include <exception>
#endif
// Include STL headers
#ifndef PUGIXML_NO_STL
# include <iterator>
# include <iosfwd>
# include <string>
#endif
// Macro for deprecated features
#ifndef PUGIXML_DEPRECATED
# if defined(__GNUC__)
# define PUGIXML_DEPRECATED __attribute__((deprecated))
# elif defined(_MSC_VER) && _MSC_VER >= 1300
# define PUGIXML_DEPRECATED __declspec(deprecated)
# else
# define PUGIXML_DEPRECATED
# endif
#endif
// If no API is defined, assume default
#ifndef PUGIXML_API
# define PUGIXML_API
#endif
// If no API for classes is defined, assume default
#ifndef PUGIXML_CLASS
# define PUGIXML_CLASS PUGIXML_API
#endif
// If no API for functions is defined, assume default
#ifndef PUGIXML_FUNCTION
# define PUGIXML_FUNCTION PUGIXML_API
#endif
// If the platform is known to have long long support, enable long long functions
#ifndef PUGIXML_HAS_LONG_LONG
# if defined(__cplusplus) && __cplusplus >= 201103
# define PUGIXML_HAS_LONG_LONG
# elif defined(_MSC_VER) && _MSC_VER >= 1400
# define PUGIXML_HAS_LONG_LONG
# endif
#endif
// Character interface macros
#ifdef PUGIXML_WCHAR_MODE
# define PUGIXML_TEXT(t) L ## t
# define PUGIXML_CHAR wchar_t
#else
# define PUGIXML_TEXT(t) t
# define PUGIXML_CHAR char
#endif
namespace pugi
{
// Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE
typedef PUGIXML_CHAR char_t;
#ifndef PUGIXML_NO_STL
// String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE
typedef std::basic_string<PUGIXML_CHAR, std::char_traits<PUGIXML_CHAR>, std::allocator<PUGIXML_CHAR> > string_t;
#endif
}
// The PugiXML namespace
namespace pugi
{
// Tree node types
enum xml_node_type
{
node_null, // Empty (null) node handle
node_document, // A document tree's absolute root
node_element, // Element tag, i.e. '<node/>'
node_pcdata, // Plain character data, i.e. 'text'
node_cdata, // Character data, i.e. '<![CDATA[text]]>'
node_comment, // Comment tag, i.e. '<!-- text -->'
node_pi, // Processing instruction, i.e. '<?name?>'
node_declaration, // Document declaration, i.e. '<?xml version="1.0"?>'
node_doctype // Document type declaration, i.e. '<!DOCTYPE doc>'
};
// Parsing options
// Minimal parsing mode (equivalent to turning all other flags off).
// Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed.
const unsigned int parse_minimal = 0x0000;
// This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default.
const unsigned int parse_pi = 0x0001;
// This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default.
const unsigned int parse_comments = 0x0002;
// This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default.
const unsigned int parse_cdata = 0x0004;
// This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree.
// This flag is off by default; turning it on usually results in slower parsing and more memory consumption.
const unsigned int parse_ws_pcdata = 0x0008;
// This flag determines if character and entity references are expanded during parsing. This flag is on by default.
const unsigned int parse_escapes = 0x0010;
// This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default.
const unsigned int parse_eol = 0x0020;
// This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default.
const unsigned int parse_wconv_attribute = 0x0040;
// This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default.
const unsigned int parse_wnorm_attribute = 0x0080;
// This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default.
const unsigned int parse_declaration = 0x0100;
// This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default.
const unsigned int parse_doctype = 0x0200;
// This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only
// of whitespace is added to the DOM tree.
// This flag is off by default; turning it on may result in slower parsing and more memory consumption.
const unsigned int parse_ws_pcdata_single = 0x0400;
// This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default.
const unsigned int parse_trim_pcdata = 0x0800;
// This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document
// is a valid document. This flag is off by default.
const unsigned int parse_fragment = 0x1000;
// The default parsing mode.
// Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded,
// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol;
// The full parsing mode.
// Nodes of all types are added to the DOM tree, character/reference entities are expanded,
// End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules.
const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype;
// These flags determine the encoding of input data for XML document
enum xml_encoding
{
encoding_auto, // Auto-detect input encoding using BOM or < / <? detection; use UTF8 if BOM is not found
encoding_utf8, // UTF8 encoding
encoding_utf16_le, // Little-endian UTF16
encoding_utf16_be, // Big-endian UTF16
encoding_utf16, // UTF16 with native endianness
encoding_utf32_le, // Little-endian UTF32
encoding_utf32_be, // Big-endian UTF32
encoding_utf32, // UTF32 with native endianness
encoding_wchar, // The same encoding wchar_t has (either UTF16 or UTF32)
encoding_latin1
};
// Formatting flags
// Indent the nodes that are written to output stream with as many indentation strings as deep the node is in DOM tree. This flag is on by default.
const unsigned int format_indent = 0x01;
// Write encoding-specific BOM to the output stream. This flag is off by default.
const unsigned int format_write_bom = 0x02;
// Use raw output mode (no indentation and no line breaks are written). This flag is off by default.
const unsigned int format_raw = 0x04;
// Omit default XML declaration even if there is no declaration in the document. This flag is off by default.
const unsigned int format_no_declaration = 0x08;
// Don't escape attribute values and PCDATA contents. This flag is off by default.
const unsigned int format_no_escapes = 0x10;
// Open file using text mode in xml_document::save_file. This enables special character (i.e. new-line) conversions on some systems. This flag is off by default.
const unsigned int format_save_file_text = 0x20;
// The default set of formatting flags.
// Nodes are indented depending on their depth in DOM tree, a default declaration is output if document has none.
const unsigned int format_default = format_indent;
// Forward declarations
struct xml_attribute_struct;
struct xml_node_struct;
class xml_node_iterator;
class xml_attribute_iterator;
class xml_named_node_iterator;
class xml_tree_walker;
struct xml_parse_result;
class xml_node;
class xml_text;
#ifndef PUGIXML_NO_XPATH
class xpath_node;
class xpath_node_set;
class xpath_query;
class xpath_variable_set;
#endif
// Range-based for loop support
template <typename It> class xml_object_range
{
public:
typedef It const_iterator;
typedef It iterator;
xml_object_range(It b, It e): _begin(b), _end(e)
{
}
It begin() const { return _begin; }
It end() const { return _end; }
private:
It _begin, _end;
};
// Writer interface for node printing (see xml_node::print)
class PUGIXML_CLASS xml_writer
{
public:
virtual ~xml_writer() {}
// Write memory chunk into stream/file/whatever
virtual void write(const void* data, size_t size) = 0;
};
// xml_writer implementation for FILE*
class PUGIXML_CLASS xml_writer_file: public xml_writer
{
public:
// Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio
xml_writer_file(void* file);
virtual void write(const void* data, size_t size);
private:
void* file;
};
#ifndef PUGIXML_NO_STL
// xml_writer implementation for streams
class PUGIXML_CLASS xml_writer_stream: public xml_writer
{
public:
// Construct writer from an output stream object
xml_writer_stream(std::basic_ostream<char, std::char_traits<char> >& stream);
xml_writer_stream(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream);
virtual void write(const void* data, size_t size);
private:
std::basic_ostream<char, std::char_traits<char> >* narrow_stream;
std::basic_ostream<wchar_t, std::char_traits<wchar_t> >* wide_stream;
};
#endif
// A light-weight handle for manipulating attributes in DOM tree
class PUGIXML_CLASS xml_attribute
{
friend class xml_attribute_iterator;
friend class xml_node;
private:
xml_attribute_struct* _attr;
typedef void (*unspecified_bool_type)(xml_attribute***);
public:
// Default constructor. Constructs an empty attribute.
xml_attribute();
// Constructs attribute from internal pointer
explicit xml_attribute(xml_attribute_struct* attr);
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
// Comparison operators (compares wrapped attribute pointers)
bool operator==(const xml_attribute& r) const;
bool operator!=(const xml_attribute& r) const;
bool operator<(const xml_attribute& r) const;
bool operator>(const xml_attribute& r) const;
bool operator<=(const xml_attribute& r) const;
bool operator>=(const xml_attribute& r) const;
// Check if attribute is empty
bool empty() const;
// Get attribute name/value, or "" if attribute is empty
const char_t* name() const;
const char_t* value() const;
// Get attribute value, or the default value if attribute is empty
const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
// Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty
int as_int(int def = 0) const;
unsigned int as_uint(unsigned int def = 0) const;
double as_double(double def = 0) const;
float as_float(float def = 0) const;
#ifdef PUGIXML_HAS_LONG_LONG
long long as_llong(long long def = 0) const;
unsigned long long as_ullong(unsigned long long def = 0) const;
#endif
// Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty
bool as_bool(bool def = false) const;
// Set attribute name/value (returns false if attribute is empty or there is not enough memory)
bool set_name(const char_t* rhs);
bool set_value(const char_t* rhs);
// Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
bool set_value(int rhs);
bool set_value(unsigned int rhs);
bool set_value(double rhs);
bool set_value(bool rhs);
#ifdef PUGIXML_HAS_LONG_LONG
bool set_value(long long rhs);
bool set_value(unsigned long long rhs);
#endif
// Set attribute value (equivalent to set_value without error checking)
xml_attribute& operator=(const char_t* rhs);
xml_attribute& operator=(int rhs);
xml_attribute& operator=(unsigned int rhs);
xml_attribute& operator=(double rhs);
xml_attribute& operator=(bool rhs);
#ifdef PUGIXML_HAS_LONG_LONG
xml_attribute& operator=(long long rhs);
xml_attribute& operator=(unsigned long long rhs);
#endif
// Get next/previous attribute in the attribute list of the parent node
xml_attribute next_attribute() const;
xml_attribute previous_attribute() const;
// Get hash value (unique for handles to the same object)
size_t hash_value() const;
// Get internal pointer
xml_attribute_struct* internal_object() const;
};
#ifdef __BORLANDC__
// Borland C++ workaround
bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs);
bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs);
#endif
// A light-weight handle for manipulating nodes in DOM tree
class PUGIXML_CLASS xml_node
{
friend class xml_attribute_iterator;
friend class xml_node_iterator;
friend class xml_named_node_iterator;
protected:
xml_node_struct* _root;
typedef void (*unspecified_bool_type)(xml_node***);
public:
// Default constructor. Constructs an empty node.
xml_node();
// Constructs node from internal pointer
explicit xml_node(xml_node_struct* p);
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
// Comparison operators (compares wrapped node pointers)
bool operator==(const xml_node& r) const;
bool operator!=(const xml_node& r) const;
bool operator<(const xml_node& r) const;
bool operator>(const xml_node& r) const;
bool operator<=(const xml_node& r) const;
bool operator>=(const xml_node& r) const;
// Check if node is empty.
bool empty() const;
// Get node type
xml_node_type type() const;
// Get node name, or "" if node is empty or it has no name
const char_t* name() const;
// Get node value, or "" if node is empty or it has no value
// Note: For <node>text</node> node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes.
const char_t* value() const;
// Get attribute list
xml_attribute first_attribute() const;
xml_attribute last_attribute() const;
// Get children list
xml_node first_child() const;
xml_node last_child() const;
// Get next/previous sibling in the children list of the parent node
xml_node next_sibling() const;
xml_node previous_sibling() const;
// Get parent node
xml_node parent() const;
// Get root of DOM tree this node belongs to
xml_node root() const;
// Get text object for the current node
xml_text text() const;
// Get child, attribute or next/previous sibling with the specified name
xml_node child(const char_t* name) const;
xml_attribute attribute(const char_t* name) const;
xml_node next_sibling(const char_t* name) const;
xml_node previous_sibling(const char_t* name) const;
// Get child value of current node; that is, value of the first child node of type PCDATA/CDATA
const char_t* child_value() const;
// Get child value of child with specified name. Equivalent to child(name).child_value().
const char_t* child_value(const char_t* name) const;
// Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value)
bool set_name(const char_t* rhs);
bool set_value(const char_t* rhs);
// Add attribute with specified name. Returns added attribute, or empty attribute on errors.
xml_attribute append_attribute(const char_t* name);
xml_attribute prepend_attribute(const char_t* name);
xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr);
xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr);
// Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors.
xml_attribute append_copy(const xml_attribute& proto);
xml_attribute prepend_copy(const xml_attribute& proto);
xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr);
xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr);
// Add child node with specified type. Returns added node, or empty node on errors.
xml_node append_child(xml_node_type type = node_element);
xml_node prepend_child(xml_node_type type = node_element);
xml_node insert_child_after(xml_node_type type, const xml_node& node);
xml_node insert_child_before(xml_node_type type, const xml_node& node);
// Add child element with specified name. Returns added node, or empty node on errors.
xml_node append_child(const char_t* name);
xml_node prepend_child(const char_t* name);
xml_node insert_child_after(const char_t* name, const xml_node& node);
xml_node insert_child_before(const char_t* name, const xml_node& node);
// Add a copy of the specified node as a child. Returns added node, or empty node on errors.
xml_node append_copy(const xml_node& proto);
xml_node prepend_copy(const xml_node& proto);
xml_node insert_copy_after(const xml_node& proto, const xml_node& node);
xml_node insert_copy_before(const xml_node& proto, const xml_node& node);
// Move the specified node to become a child of this node. Returns moved node, or empty node on errors.
xml_node append_move(const xml_node& moved);
xml_node prepend_move(const xml_node& moved);
xml_node insert_move_after(const xml_node& moved, const xml_node& node);
xml_node insert_move_before(const xml_node& moved, const xml_node& node);
// Remove specified attribute
bool remove_attribute(const xml_attribute& a);
bool remove_attribute(const char_t* name);
// Remove specified child
bool remove_child(const xml_node& n);
bool remove_child(const char_t* name);
// Parses buffer as an XML document fragment and appends all nodes as children of the current node.
// Copies/converts the buffer, so it may be deleted or changed after the function returns.
// Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory.
xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Find attribute using predicate. Returns first attribute for which predicate returned true.
template <typename Predicate> xml_attribute find_attribute(Predicate pred) const
{
if (!_root) return xml_attribute();
for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute())
if (pred(attrib))
return attrib;
return xml_attribute();
}
// Find child node using predicate. Returns first child for which predicate returned true.
template <typename Predicate> xml_node find_child(Predicate pred) const
{
if (!_root) return xml_node();
for (xml_node node = first_child(); node; node = node.next_sibling())
if (pred(node))
return node;
return xml_node();
}
// Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true.
template <typename Predicate> xml_node find_node(Predicate pred) const
{
if (!_root) return xml_node();
xml_node cur = first_child();
while (cur._root && cur._root != _root)
{
if (pred(cur)) return cur;
if (cur.first_child()) cur = cur.first_child();
else if (cur.next_sibling()) cur = cur.next_sibling();
else
{
while (!cur.next_sibling() && cur._root != _root) cur = cur.parent();
if (cur._root != _root) cur = cur.next_sibling();
}
}
return xml_node();
}
// Find child node by attribute name/value
xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const;
xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const;
#ifndef PUGIXML_NO_STL
// Get the absolute node path from root as a text string.
string_t path(char_t delimiter = '/') const;
#endif
// Search for a node by path consisting of node names and . or .. elements.
xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const;
// Recursively traverse subtree with xml_tree_walker
bool traverse(xml_tree_walker& walker);
#ifndef PUGIXML_NO_XPATH
// Select single node by evaluating XPath query. Returns first node from the resulting node set.
xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const;
xpath_node select_node(const xpath_query& query) const;
// Select node set by evaluating XPath query
xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const;
xpath_node_set select_nodes(const xpath_query& query) const;
// (deprecated: use select_node instead) Select single node by evaluating XPath query.
xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const;
xpath_node select_single_node(const xpath_query& query) const;
#endif
// Print subtree using a writer object
void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
#ifndef PUGIXML_NO_STL
// Print subtree to stream
void print(std::basic_ostream<char, std::char_traits<char> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const;
void print(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const;
#endif
// Child nodes iterators
typedef xml_node_iterator iterator;
iterator begin() const;
iterator end() const;
// Attribute iterators
typedef xml_attribute_iterator attribute_iterator;
attribute_iterator attributes_begin() const;
attribute_iterator attributes_end() const;
// Range-based for support
xml_object_range<xml_node_iterator> children() const;
xml_object_range<xml_named_node_iterator> children(const char_t* name) const;
xml_object_range<xml_attribute_iterator> attributes() const;
// Get node offset in parsed file/string (in char_t units) for debugging purposes
ptrdiff_t offset_debug() const;
// Get hash value (unique for handles to the same object)
size_t hash_value() const;
// Get internal pointer
xml_node_struct* internal_object() const;
};
#ifdef __BORLANDC__
// Borland C++ workaround
bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs);
bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs);
#endif
// A helper for working with text inside PCDATA nodes
class PUGIXML_CLASS xml_text
{
friend class xml_node;
xml_node_struct* _root;
typedef void (*unspecified_bool_type)(xml_text***);
explicit xml_text(xml_node_struct* root);
xml_node_struct* _data_new();
xml_node_struct* _data() const;
public:
// Default constructor. Constructs an empty object.
xml_text();
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
// Check if text object is empty
bool empty() const;
// Get text, or "" if object is empty
const char_t* get() const;
// Get text, or the default value if object is empty
const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const;
// Get text as a number, or the default value if conversion did not succeed or object is empty
int as_int(int def = 0) const;
unsigned int as_uint(unsigned int def = 0) const;
double as_double(double def = 0) const;
float as_float(float def = 0) const;
#ifdef PUGIXML_HAS_LONG_LONG
long long as_llong(long long def = 0) const;
unsigned long long as_ullong(unsigned long long def = 0) const;
#endif
// Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty
bool as_bool(bool def = false) const;
// Set text (returns false if object is empty or there is not enough memory)
bool set(const char_t* rhs);
// Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false")
bool set(int rhs);
bool set(unsigned int rhs);
bool set(double rhs);
bool set(bool rhs);
#ifdef PUGIXML_HAS_LONG_LONG
bool set(long long rhs);
bool set(unsigned long long rhs);
#endif
// Set text (equivalent to set without error checking)
xml_text& operator=(const char_t* rhs);
xml_text& operator=(int rhs);
xml_text& operator=(unsigned int rhs);
xml_text& operator=(double rhs);
xml_text& operator=(bool rhs);
#ifdef PUGIXML_HAS_LONG_LONG
xml_text& operator=(long long rhs);
xml_text& operator=(unsigned long long rhs);
#endif
// Get the data node (node_pcdata or node_cdata) for this object
xml_node data() const;
};
#ifdef __BORLANDC__
// Borland C++ workaround
bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs);
bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs);
#endif
// Child node iterator (a bidirectional iterator over a collection of xml_node)
class PUGIXML_CLASS xml_node_iterator
{
friend class xml_node;
private:
mutable xml_node _wrap;
xml_node _parent;
xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent);
public:
// Iterator traits
typedef ptrdiff_t difference_type;
typedef xml_node value_type;
typedef xml_node* pointer;
typedef xml_node& reference;
#ifndef PUGIXML_NO_STL
typedef std::bidirectional_iterator_tag iterator_category;
#endif
// Default constructor
xml_node_iterator();
// Construct an iterator which points to the specified node
xml_node_iterator(const xml_node& node);
// Iterator operators
bool operator==(const xml_node_iterator& rhs) const;
bool operator!=(const xml_node_iterator& rhs) const;
xml_node& operator*() const;
xml_node* operator->() const;
const xml_node_iterator& operator++();
xml_node_iterator operator++(int);
const xml_node_iterator& operator--();
xml_node_iterator operator--(int);
};
// Attribute iterator (a bidirectional iterator over a collection of xml_attribute)
class PUGIXML_CLASS xml_attribute_iterator
{
friend class xml_node;
private:
mutable xml_attribute _wrap;
xml_node _parent;
xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent);
public:
// Iterator traits
typedef ptrdiff_t difference_type;
typedef xml_attribute value_type;
typedef xml_attribute* pointer;
typedef xml_attribute& reference;
#ifndef PUGIXML_NO_STL
typedef std::bidirectional_iterator_tag iterator_category;
#endif
// Default constructor
xml_attribute_iterator();
// Construct an iterator which points to the specified attribute
xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent);
// Iterator operators
bool operator==(const xml_attribute_iterator& rhs) const;
bool operator!=(const xml_attribute_iterator& rhs) const;
xml_attribute& operator*() const;
xml_attribute* operator->() const;
const xml_attribute_iterator& operator++();
xml_attribute_iterator operator++(int);
const xml_attribute_iterator& operator--();
xml_attribute_iterator operator--(int);
};
// Named node range helper
class PUGIXML_CLASS xml_named_node_iterator
{
friend class xml_node;
public:
// Iterator traits
typedef ptrdiff_t difference_type;
typedef xml_node value_type;
typedef xml_node* pointer;
typedef xml_node& reference;
#ifndef PUGIXML_NO_STL
typedef std::bidirectional_iterator_tag iterator_category;
#endif
// Default constructor
xml_named_node_iterator();
// Construct an iterator which points to the specified node
xml_named_node_iterator(const xml_node& node, const char_t* name);
// Iterator operators
bool operator==(const xml_named_node_iterator& rhs) const;
bool operator!=(const xml_named_node_iterator& rhs) const;
xml_node& operator*() const;
xml_node* operator->() const;
const xml_named_node_iterator& operator++();
xml_named_node_iterator operator++(int);
const xml_named_node_iterator& operator--();
xml_named_node_iterator operator--(int);
private:
mutable xml_node _wrap;
xml_node _parent;
const char_t* _name;
xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name);
};
// Abstract tree walker class (see xml_node::traverse)
class PUGIXML_CLASS xml_tree_walker
{
friend class xml_node;
private:
int _depth;
protected:
// Get current traversal depth
int depth() const;
public:
xml_tree_walker();
virtual ~xml_tree_walker();
// Callback that is called when traversal begins
virtual bool begin(xml_node& node);
// Callback that is called for each node traversed
virtual bool for_each(xml_node& node) = 0;
// Callback that is called when traversal ends
virtual bool end(xml_node& node);
};
// Parsing status, returned as part of xml_parse_result object
enum xml_parse_status
{
status_ok = 0, // No error
status_file_not_found, // File was not found during load_file()
status_io_error, // Error reading from file/stream
status_out_of_memory, // Could not allocate memory
status_internal_error, // Internal error occurred
status_unrecognized_tag, // Parser could not determine tag type
status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction
status_bad_comment, // Parsing error occurred while parsing comment
status_bad_cdata, // Parsing error occurred while parsing CDATA section
status_bad_doctype, // Parsing error occurred while parsing document type declaration
status_bad_pcdata, // Parsing error occurred while parsing PCDATA section
status_bad_start_element, // Parsing error occurred while parsing start element tag
status_bad_attribute, // Parsing error occurred while parsing element attribute
status_bad_end_element, // Parsing error occurred while parsing end element tag
status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag)
status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer)
status_no_document_element // Parsing resulted in a document without element nodes
};
// Parsing result
struct PUGIXML_CLASS xml_parse_result
{
// Parsing status (see xml_parse_status)
xml_parse_status status;
// Last parsed offset (in char_t units from start of input data)
ptrdiff_t offset;
// Source document encoding
xml_encoding encoding;
// Default constructor, initializes object to failed state
xml_parse_result();
// Cast to bool operator
operator bool() const;
// Get error description
const char* description() const;
};
// Document class (DOM tree root)
class PUGIXML_CLASS xml_document: public xml_node
{
private:
char_t* _buffer;
char _memory[192];
// Non-copyable semantics
xml_document(const xml_document&);
const xml_document& operator=(const xml_document&);
void create();
void destroy();
public:
// Default constructor, makes empty document
xml_document();
// Destructor, invalidates all node/attribute handles to this document
~xml_document();
// Removes all nodes, leaving the empty document
void reset();
// Removes all nodes, then copies the entire contents of the specified document
void reset(const xml_document& proto);
#ifndef PUGIXML_NO_STL
// Load document from stream.
xml_parse_result load(std::basic_istream<char, std::char_traits<char> >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
xml_parse_result load(std::basic_istream<wchar_t, std::char_traits<wchar_t> >& stream, unsigned int options = parse_default);
#endif
// (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied.
xml_parse_result load(const char_t* contents, unsigned int options = parse_default);
// Load document from zero-terminated string. No encoding conversions are applied.
xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default);
// Load document from file
xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns.
xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
// You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed.
xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data).
// You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore).
xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto);
// Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details).
void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
#ifndef PUGIXML_NO_STL
// Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details).
void save(std::basic_ostream<char, std::char_traits<char> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
void save(std::basic_ostream<wchar_t, std::char_traits<wchar_t> >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const;
#endif
// Save XML to file
bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const;
// Get document element
xml_node document_element() const;
};
#ifndef PUGIXML_NO_XPATH
// XPath query return type
enum xpath_value_type
{
xpath_type_none, // Unknown type (query failed to compile)
xpath_type_node_set, // Node set (xpath_node_set)
xpath_type_number, // Number
xpath_type_string, // String
xpath_type_boolean // Boolean
};
// XPath parsing result
struct PUGIXML_CLASS xpath_parse_result
{
// Error message (0 if no error)
const char* error;
// Last parsed offset (in char_t units from string start)
ptrdiff_t offset;
// Default constructor, initializes object to failed state
xpath_parse_result();
// Cast to bool operator
operator bool() const;
// Get error description
const char* description() const;
};
// A single XPath variable
class PUGIXML_CLASS xpath_variable
{
friend class xpath_variable_set;
protected:
xpath_value_type _type;
xpath_variable* _next;
xpath_variable();
// Non-copyable semantics
xpath_variable(const xpath_variable&);
xpath_variable& operator=(const xpath_variable&);
public:
// Get variable name
const char_t* name() const;
// Get variable type
xpath_value_type type() const;
// Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error
bool get_boolean() const;
double get_number() const;
const char_t* get_string() const;
const xpath_node_set& get_node_set() const;
// Set variable value; no type conversion is performed, false is returned on type mismatch error
bool set(bool value);
bool set(double value);
bool set(const char_t* value);
bool set(const xpath_node_set& value);
};
// A set of XPath variables
class PUGIXML_CLASS xpath_variable_set
{
private:
xpath_variable* _data[64];
// Non-copyable semantics
xpath_variable_set(const xpath_variable_set&);
xpath_variable_set& operator=(const xpath_variable_set&);
xpath_variable* find(const char_t* name) const;
public:
// Default constructor/destructor
xpath_variable_set();
~xpath_variable_set();
// Add a new variable or get the existing one, if the types match
xpath_variable* add(const char_t* name, xpath_value_type type);
// Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch
bool set(const char_t* name, bool value);
bool set(const char_t* name, double value);
bool set(const char_t* name, const char_t* value);
bool set(const char_t* name, const xpath_node_set& value);
// Get existing variable by name
xpath_variable* get(const char_t* name);
const xpath_variable* get(const char_t* name) const;
};
// A compiled XPath query object
class PUGIXML_CLASS xpath_query
{
private:
void* _impl;
xpath_parse_result _result;
typedef void (*unspecified_bool_type)(xpath_query***);
// Non-copyable semantics
xpath_query(const xpath_query&);
xpath_query& operator=(const xpath_query&);
public:
// Construct a compiled object from XPath expression.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors.
explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0);
// Destructor
~xpath_query();
// Get query expression return type
xpath_value_type return_type() const;
// Evaluate expression as boolean value in the specified context; performs type conversion if necessary.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
bool evaluate_boolean(const xpath_node& n) const;
// Evaluate expression as double value in the specified context; performs type conversion if necessary.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
double evaluate_number(const xpath_node& n) const;
#ifndef PUGIXML_NO_STL
// Evaluate expression as string value in the specified context; performs type conversion if necessary.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
string_t evaluate_string(const xpath_node& n) const;
#endif
// Evaluate expression as string value in the specified context; performs type conversion if necessary.
// At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero).
// If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors.
// If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead.
size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const;
// Evaluate expression as node set in the specified context.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead.
xpath_node_set evaluate_node_set(const xpath_node& n) const;
// Evaluate expression as node set in the specified context.
// Return first node in document order, or empty node if node set is empty.
// If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors.
// If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead.
xpath_node evaluate_node(const xpath_node& n) const;
// Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode)
const xpath_parse_result& result() const;
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
};
#ifndef PUGIXML_NO_EXCEPTIONS
// XPath exception class
class PUGIXML_CLASS xpath_exception: public std::exception
{
private:
xpath_parse_result _result;
public:
// Construct exception from parse result
explicit xpath_exception(const xpath_parse_result& result);
// Get error message
virtual const char* what() const throw();
// Get parse result
const xpath_parse_result& result() const;
};
#endif
// XPath node class (either xml_node or xml_attribute)
class PUGIXML_CLASS xpath_node
{
private:
xml_node _node;
xml_attribute _attribute;
typedef void (*unspecified_bool_type)(xpath_node***);
public:
// Default constructor; constructs empty XPath node
xpath_node();
// Construct XPath node from XML node/attribute
xpath_node(const xml_node& node);
xpath_node(const xml_attribute& attribute, const xml_node& parent);
// Get node/attribute, if any
xml_node node() const;
xml_attribute attribute() const;
// Get parent of contained node/attribute
xml_node parent() const;
// Safe bool conversion operator
operator unspecified_bool_type() const;
// Borland C++ workaround
bool operator!() const;
// Comparison operators
bool operator==(const xpath_node& n) const;
bool operator!=(const xpath_node& n) const;
};
#ifdef __BORLANDC__
// Borland C++ workaround
bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs);
bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs);
#endif
// A fixed-size collection of XPath nodes
class PUGIXML_CLASS xpath_node_set
{
public:
// Collection type
enum type_t
{
type_unsorted, // Not ordered
type_sorted, // Sorted by document order (ascending)
type_sorted_reverse // Sorted by document order (descending)
};
// Constant iterator type
typedef const xpath_node* const_iterator;
// We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work
typedef const xpath_node* iterator;
// Default constructor. Constructs empty set.
xpath_node_set();
// Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful
xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted);
// Destructor
~xpath_node_set();
// Copy constructor/assignment operator
xpath_node_set(const xpath_node_set& ns);
xpath_node_set& operator=(const xpath_node_set& ns);
// Get collection type
type_t type() const;
// Get collection size
size_t size() const;
// Indexing operator
const xpath_node& operator[](size_t index) const;
// Collection iterators
const_iterator begin() const;
const_iterator end() const;
// Sort the collection in ascending/descending order by document order
void sort(bool reverse = false);
// Get first node in the collection by document order
xpath_node first() const;
// Check if collection is empty
bool empty() const;
private:
type_t _type;
xpath_node _storage;
xpath_node* _begin;
xpath_node* _end;
void _assign(const_iterator begin, const_iterator end);
};
#endif
#ifndef PUGIXML_NO_STL
// Convert wide string to UTF8
std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const wchar_t* str);
std::basic_string<char, std::char_traits<char>, std::allocator<char> > PUGIXML_FUNCTION as_utf8(const std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >& str);
// Convert UTF8 to wide string
std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const char* str);
std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> > PUGIXML_FUNCTION as_wide(const std::basic_string<char, std::char_traits<char>, std::allocator<char> >& str);
#endif
// Memory allocation function interface; returns pointer to allocated memory or NULL on failure
typedef void* (*allocation_function)(size_t size);
// Memory deallocation function interface
typedef void (*deallocation_function)(void* ptr);
// Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions.
void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate);
// Get current memory management functions
allocation_function PUGIXML_FUNCTION get_memory_allocation_function();
deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function();
}
#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC))
namespace std
{
// Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier)
std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&);
std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&);
std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&);
}
#endif
#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC)
namespace std
{
// Workarounds for (non-standard) iterator category detection
std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&);
std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&);
std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&);
}
#endif
#endif
/**
* Copyright (c) 2006-2014 Arseny Kapoulkine
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
# ith/common/common.pri
# 8/9/2011 jichi
# Overwrite ITH headers
#DEFINES += ITH_HAS_CRT # whether ITH is linked with msvcrt
#DEFINES += ITH_HAS_CXX # whether ITH has access to native C++ syntax
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/const.h \
$$PWD/defs.h \
$$PWD/except.h \
$$PWD/growl.h \
$$PWD/memory.h \
$$PWD/string.h \
$$PWD/types.h
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
# jichi 9/14/2013: Whether using SEH exception handle.
# msvcrt on Windows XP is missin EH
#DEFINES += ITH_HAS_SEH
# jichi 9/22/2013: Whether let ITH manage heap
#DEFINES += ITH_HAS_HEAP
# EOF
#pragma once
// ith/common/const.h
// 8/23/2013 jichi
// Branch: ITH/common.h, rev 128
// jichi 9/9/2013: Another importnat function is lstrcatA, which is already handled by
// Debonosu hooks. Wait until it is really needed by certain games.
// The order of the functions is used in several place.
// I need to recompile all of the dlls to modify the order.
enum HookFunType {
HF_Null = -1
, HF_GetTextExtentPoint32A
, HF_GetGlyphOutlineA
, HF_ExtTextOutA
, HF_TextOutA
, HF_GetCharABCWidthsA
, HF_DrawTextA
, HF_DrawTextExA
//, HF_lstrlenA
, HF_GetTextExtentPoint32W
, HF_GetGlyphOutlineW
, HF_ExtTextOutW
, HF_TextOutW
, HF_GetCharABCWidthsW
, HF_DrawTextW
, HF_DrawTextExW
//, HF_lstrlenW
, HookFunCount // 14
};
// jichi 10/14/2014
#define HOOK_GDI_FUNCTION_LIST \
GetTextExtentPoint32A \
, GetGlyphOutlineA \
, ExtTextOutA \
, TextOutA \
, GetCharABCWidthsA \
, GetTextExtentPoint32W \
, GetGlyphOutlineW \
, ExtTextOutW \
, TextOutW \
, GetCharABCWidthsW \
, DrawTextA \
, DrawTextExA \
, DrawTextW \
, DrawTextExW
enum { HOOK_FUN_COUNT = HookFunCount };
// jichi 1/16/2015: Though called max hook, it means max number of text threads
enum { MAX_HOOK = 32 }; // must be larger than HookFunCount
//enum { HOOK_SECTION_SIZE = 0x2000 }; // default ITH value
// jichi 1/16/2015: Change to a very large number to prevent crash
//enum { MAX_HOOK = 0x100 }; // must be larger than HookFunCount
enum { HOOK_SECTION_SIZE = MAX_HOOK * 0x100 }; // default ITH value is 0x2000 for 32 hook (0x100 per hook)
// jichi 375/2014: Add offset of pusha/pushad
// http://faydoc.tripod.com/cpu/pushad.htm
// http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
//
// Warning: The offset in ITH has -4 offset comparing to pusha and AGTH
enum pusha_off {
pusha_eax_off = -0x4
, pusha_ecx_off = -0x8
, pusha_edx_off = -0xc
, pusha_ebx_off = -0x10
, pusha_esp_off = -0x14
, pusha_ebp_off = -0x18
, pusha_esi_off = -0x1c
, pusha_edi_off = -0x20
, pusha_off = -0x24 // pushad offset
};
enum IhfCommandType {
IHF_COMMAND = -1 // null type
, IHF_COMMAND_NEW_HOOK = 0
, IHF_COMMAND_REMOVE_HOOK = 1
, IHF_COMMAND_MODIFY_HOOK = 2
, IHF_COMMAND_DETACH = 3
};
enum IhfNotificationType {
IHF_NOTIFICATION = -1 // null type
, IHF_NOTIFICATION_TEXT = 0
, IHF_NOTIFICATION_NEWHOOK = 1
};
// jichi 9/8/2013: The meaning are guessed
// Values must be within DWORD
// Unused values are as follows:
// - 0x100
enum HookParamType : unsigned long {
USING_STRING = 0x1 // type(data) is char* or wchar_t* and has length
, USING_UTF8 = USING_STRING // jichi 10/21/2014: temporarily handled the same way as USING_STRING
, USING_UNICODE = 0x2 // type(data) is wchar_t or wchar_t*
, BIG_ENDIAN = 0x4 // type(data) is char
, DATA_INDIRECT = 0x8
, USING_SPLIT = 0x10 // aware of split time?
, SPLIT_INDIRECT = 0x20
, MODULE_OFFSET = 0x40 // do hash module, and the address is relative to module
, FUNCTION_OFFSET = 0x80 // do hash function, and the address is relative to funccion
, PRINT_DWORD = 0x100 // jichi 12/7/2014: Removed
, STRING_LAST_CHAR = 0x200
, NO_CONTEXT = 0x400
//, EXTERN_HOOK = 0x800 // jichi 10/24/2014: Removed
//, HOOK_AUXILIARY = 0x2000 // jichi 12/13/2013: None of known hooks are auxiliary
, HOOK_ENGINE = 0x4000
, HOOK_ADDITIONAL = 0x8000
// jichi 10/24/2014: Only trigger the dynamic function, do not return any data
, HOOK_EMPTY = 0x800
// jichi 6/1/2014: fix the split value to 0x10001
, FIXING_SPLIT = 0x1000
, RELATIVE_SPLIT = 0x2000 // relative split return address
};
// 6/1/2014: Fixed split value for hok parameter
// Fuse all threads, and prevent floating
enum { FIXED_SPLIT_VALUE = 0x10001 };
// jichi 12/18/2013:
// These dlls are used to guess the range for non-NO_CONTEXT hooks.
//
// Disabling uxtheme.dll would crash certain system: http://tieba.baidu.com/p/2764436254
#define IHF_FILTER_DLL_LIST \
/* ITH original filters */ \
L"gdiplus.dll" /* Graphics functions like TextOutA */ \
, L"lpk.dll" /* Language package scripts and fonts */ \
, L"msctf.dll" /* Text service */ \
, L"psapi.dll" /* Processes */ \
, L"usp10.dll" /* UNICODE rendering */ \
, L"user32.dll" /* Non-graphics functions like lstrlenA */ \
, L"uxtheme.dll" /* Theme */ \
\
/* Windows DLLs */ \
, L"advapi32.dll" /* Advanced services */ \
, L"apphelp.dll" /* Appliation help */ \
, L"audioses.dll" /* Audios */ \
, L"avrt.dll" /* Audio video runtime */ \
, L"cfgmgr32.dll" /* Configuration manager */ \
, L"clbcatq.dll" /* COM query service */ \
, L"comctl32.dll" /* Common control library */ \
, L"comdlg32.dll" /* Common dialogs */ \
, L"crypt32.dll" /* Security cryption */ \
, L"cryptbase.dll"/* Security cryption */ \
, L"cryptsp.dll" /* Security cryption */ \
, L"d3d8thk.dll" /* Direct3D 8 */ \
, L"d3d9.dll" /* Direct3D 9 */ \
, L"dbghelp.dll" /* Debug help */ \
, L"dciman32.dll" /* Display cotrol */ \
, L"devobj.dll" /* Device object */ \
, L"ddraw.dll" /* Direct draw */ \
, L"dinput.dll" /* Diret input */ \
, L"dsound.dll" /* Direct sound */ \
, L"DShowRdpFilter.dll" /* Direct show */ \
, L"dwmapi.dll" /* Windows manager */ \
, L"gdi32.dll" /* GDI32 */ \
, L"hid.dll" /* HID user library */ \
, L"iertutil.dll" /* IE runtime */ \
, L"imagehlp.dll" /* Image help */ \
, L"imm32.dll" /* Input method */ \
, L"ksuser.dll" /* Kernel service */ \
, L"ole32.dll" /* COM OLE */ \
, L"oleacc.dll" /* OLE access */ \
, L"oleaut32.dll" /* COM OLE */ \
, L"kernel.dll" /* Kernel functions */ \
, L"kernelbase.dll" /* Kernel functions */ \
, L"midimap.dll" /* MIDI */ \
, L"mmdevapi.dll" /* Audio device */ \
, L"mpr.dll" /* Winnet */ \
, L"msacm32.dll" /* MS ACM */ \
, L"msacm32.drv" /* MS ACM */ \
, L"msasn1.dll" /* Encoding/decoding */ \
, L"msimg32.dll" /* Image */ \
, L"msvfw32.dll" /* Media play */ \
, L"netapi32.dll" /* Network service */ \
, L"normaliz.dll" /* Normalize */ \
, L"nsi.dll" /* NSI */ \
, L"ntdll.dll" /* NT functions */ \
, L"ntmarta.dll" /* NT MARTA */ \
, L"nvd3dum.dll" /* Direct 3D */ \
, L"powerprof.dll"/* Power profile */ \
, L"profapi.dll" /* Profile API */ \
, L"propsys.dll" /* System properties */ \
, L"quartz.dll" /* OpenGL */ \
, L"rpcrt4.dll" /* RPC runtime */ \
, L"rpcrtremote.dll" /* RPC runtime */ \
, L"rsabase.dll" /* RSA cryption */ \
, L"rsaenh.dll" /* RSA cryption */ \
, L"schannel.dll" /* Security channel */ \
, L"sechost.dll" /* Service host */ \
, L"setupapi.dll" /* Setup service */ \
, L"shell32.dll" /* Windows shell */ \
, L"shlwapi.dll" /* Light-weighted shell */ \
, L"slc.dll" /* SLC */ \
, L"srvcli.dll" /* Service client */ \
, L"version.dll" /* Windows version */ \
, L"wdmaud.drv" /* Wave output */ \
, L"wldap32.dll" /* Wireless */ \
, L"wininet.dll" /* Internet access */ \
, L"winmm.dll" /* Windows sound */ \
, L"winsta.dll" /* Connection system */ \
, L"wtsapi32.dll" /* Windows terminal server */ \
, L"wintrust.dll" /* Windows trust */ \
, L"wsock32.dll" /* Windows sock */ \
, L"ws2_32.dll" /* Terminal server */ \
, L"wkscli.dll" /* ACIS */ \
\
/* MSVCRT */ \
, L"msvcrt.dll" /* VC rutime */ \
, L"msvcr80.dll" /* VC rutime 8 */ \
, L"msvcp80.dll" /* VC rutime 8 */ \
, L"msvcr90.dll" /* VC rutime 9 */ \
, L"msvcp90.dll" /* VC rutime 9 */ \
, L"msvcr100.dll" /* VC rutime 10 */ \
, L"msvcp100.dll" /* VC rutime 10 */ \
, L"msvcr110.dll" /* VC rutime 11 */ \
, L"msvcp110.dll" /* VC rutime 11 */ \
\
/* VNR */ \
, L"vnrhook.dll" \
, L"vnrhookxp.dll" \
\
/* Sogou IME */ \
, L"sogoupy.ime" \
, L"PicFace.dll" \
, L"AddressSearch.dll" \
\
/* QQ IME */ \
, L"QQPINYIN.IME" \
\
/* AlphaROM */ \
, L"kDays.dll" \
\
/* 360Safe */ \
, L"safemon.dll" \
\
/* Locale changers */ \
, L"AlLayer.dll" /* AppLocale */ \
, L"LocaleEmulator.dll" /* Locale Emulator */ \
, L"LSH.dll" /* LocaleSwitch */ \
, L"ntleah.dll" /* NTLEA */
// Google Japanese IME
//, L"GoogleIMEJaTIP32.dll"
enum {
//IHF_FILTER_COUNT = 7
IHF_FILTER_COUNT = 7 + 72 + 9 + 4 + 3 + 1 + 1 + 1 + 4 // count of total dlls to filter
, IHF_FILTER_CAPACITY = IHF_FILTER_COUNT + 1 // one more than the dll count
};
// EOF
#pragma once
// ith/common/defs.h
// 8/23/2013 jichi
// DLL files
//#define ITH_SERVER_DLL L"vnrsrv.dll"
//#define ITH_CLIENT_DLL L"vnrcli.dll"
//#define ITH_CLIENT_XP_DLL L"vnrclixp.dll"
////#define ITH_CLIENT_UX_DLL L"vnrcliux.dll"
//#define ITH_ENGINE_DLL L"vnreng.dll"
//#define ITH_ENGINE_XP_DLL L"vnrengxp.dll"
//#define ITH_ENGINE_UX_DLL L"vnrengux.dll"
#define ITH_DLL L"vnrhook.dll"
#define ITH_DLL_XP L"vnrhookxp.dll"
// Pipes
#define ITH_TEXT_PIPE L"\\??\\pipe\\VNR_TEXT"
#define ITH_COMMAND_PIPE L"\\??\\pipe\\VNR_COMMAND"
// Sections
#define ITH_SECTION_ L"VNR_SECTION_" // _%d
// Mutex
#define ITH_PROCESS_MUTEX_ L"VNR_PROCESS_" // ITH_%d
#define ITH_HOOKMAN_MUTEX_ L"VNR_HOOKMAN_" // ITH_HOOKMAN_%d
#define ITH_DETACH_MUTEX_ L"VNR_DETACH_" // ITH_DETACH_%d
#define ITH_GRANTPIPE_MUTEX L"VNR_GRANT_PIPE" // ITH_GRANT_PIPE
//#define ITH_ENGINE_MUTEX L"VNR_ENGINE" // ITH_ENGINE
#define ITH_CLIENT_MUTEX L"VNR_CLIENT" // ITH_DLL_RUNNING
#define ITH_SERVER_MUTEX L"VNR_SERVER" // ITH_RUNNING
#define ITH_SERVER_HOOK_MUTEX L"VNR_SERVER_HOOK" // original
// Events
#define ITH_REMOVEHOOK_EVENT L"VNR_REMOVE_HOOK" // ITH_REMOVE_HOOK
#define ITH_MODIFYHOOK_EVENT L"VNR_MODIFY_HOOK" // ITH_MODIFY_HOOK
#define ITH_PIPEEXISTS_EVENT L"VNR_PIPE_EXISTS" // ITH_PIPE_EXIST
// EOF
#pragma once
// ith/common/except.h
// 9/17/2013 jichi
#define ITH_RAISE (*(int*)0 = 0) // raise C000005, for debugging only
#ifdef ITH_HAS_SEH
# define ITH_TRY __try
# define ITH_EXCEPT __except(EXCEPTION_EXECUTE_HANDLER)
# define ITH_WITH_SEH(...) \
ITH_TRY { __VA_ARGS__; } ITH_EXCEPT {}
#else // for old msvcrt.dll on Windows XP that does not have exception handler
// Currently, only with_seh is implemented. Try and catch are not.
# define ITH_TRY if (true)
# define ITH_EXCEPT else
# include "winseh/winseh.h"
# define ITH_WITH_SEH(...) seh_with(__VA_ARGS__)
#endif // ITH_HAS_SEH
// EOF
#pragma once
// ith/common/growl.h
// 9/17/2013 jichi
//#ifdef ITH_HAS_GROWL
#include <windows.h>
#include "ith/common/string.h"
#define ITH_MSG_A(_msg) MessageBoxA(nullptr, _msg, "VNR Message", MB_OK)
#define ITH_MSG(_msg) MessageBoxW(nullptr, _msg, L"VNR Message", MB_OK)
#define ITH_WARN(_msg) MessageBoxW(nullptr, _msg, L"VNR Warning", MB_OK)
#define ITH_ERROR(_msg) MessageBoxW(nullptr, _msg, L"VNR Error", MB_OK)
inline void ITH_GROWL_DWORD(DWORD value)
{
WCHAR buf[100];
swprintf(buf, L"DWORD: %x", value);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD2(DWORD v, DWORD v2)
{
WCHAR buf[100];
swprintf(buf, L"DWORD2: %x,%x", v, v2);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD3(DWORD v, DWORD v2, DWORD v3)
{
WCHAR buf[100];
swprintf(buf, L"DWORD3: %x,%x,%x", v, v2, v3);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD4(DWORD v, DWORD v2, DWORD v3, DWORD v4)
{
WCHAR buf[100];
swprintf(buf, L"DWORD4: %x,%x,%x,%x", v, v2, v3, v4);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD5(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5)
{
WCHAR buf[100];
swprintf(buf, L"DWORD5: %x,%x,%x,%x,%x", v, v2, v3, v4, v5);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD6(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6)
{
WCHAR buf[100];
swprintf(buf, L"DWORD6: %x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD7(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7)
{
WCHAR buf[100];
swprintf(buf, L"DWORD7: %x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD8(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8)
{
WCHAR buf[100];
swprintf(buf, L"DWORD8: %x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8);
ITH_MSG(buf);
}
inline void ITH_GROWL_DWORD9(DWORD v, DWORD v2, DWORD v3, DWORD v4, DWORD v5, DWORD v6, DWORD v7, DWORD v8, DWORD v9)
{
WCHAR buf[100];
swprintf(buf, L"DWORD9: %x,%x,%x,%x,%x,%x,%x,%x,%x", v, v2, v3, v4, v5, v6, v7, v8, v9);
ITH_MSG(buf);
}
inline void ITH_GROWL(DWORD v) { ITH_GROWL_DWORD(v); }
inline void ITH_GROWL(LPCWSTR v) { ITH_MSG(v); }
inline void ITH_GROWL(LPCSTR v) { ITH_MSG_A(v); }
//#endif // ITH_HAS_GROWL
// EOF
#pragma once
// ith/common/memory.h
// 8/23/2013 jichi
// Branch: ITH/mem.h, revision 66
#ifndef ITH_HAS_HEAP
# define ITH_MEMSET_HEAP(...) ::memset(__VA_ARGS__)
#else
# define ITH_MEMSET_HEAP(...) (void)0
// Defined in kernel32.lilb
extern "C" {
// PVOID RtlAllocateHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ SIZE_T Size);
__declspec(dllimport) void * __stdcall RtlAllocateHeap(void *HeapHandle, unsigned long Flags, unsigned long Size);
// BOOLEAN RtlFreeHeap( _In_ PVOID HeapHandle, _In_opt_ ULONG Flags, _In_ PVOID HeapBase);
__declspec(dllimport) int __stdcall RtlFreeHeap(void *HeapHandle, unsigned long Flags, void *HeapBase);
} // extern "C"
//NTSYSAPI
//BOOL
//NTAPI
//RtlFreeHeap(
// _In_ HANDLE hHeap,
// _In_ DWORD dwFlags,
// _In_ LPVOID lpMem
//);
extern void *hHeap; // defined in ith/sys.cc
inline void * __cdecl operator new(size_t lSize)
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa366597%28v=vs.85%29.aspx
// HEAP_ZERO_MEMORY flag is critical. All new objects are assumed with zero initialized.
enum { HEAP_ZERO_MEMORY = 0x00000008 };
return RtlAllocateHeap(::hHeap, HEAP_ZERO_MEMORY, lSize);
}
inline void __cdecl operator delete(void *pBlock)
{ RtlFreeHeap(::hHeap, 0, pBlock); }
inline void __cdecl operator delete[](void *pBlock)
{ RtlFreeHeap(::hHeap, 0, pBlock); }
#endif // ITH_HAS_HEAP
#pragma once
// ith/common/string.h
// 8/9/2013 jichi
// Branch: ITH/string.h, rev 66
#ifdef ITH_HAS_CRT // ITH is linked with msvcrt dlls
# include <cstdio>
# include <cstring>
#else
# define _INC_SWPRINTF_INL_
# define CRT_IMPORT __declspec(dllimport)
#include <windows.h> // for wchar_t
extern "C" {
CRT_IMPORT int swprintf(wchar_t *src, const wchar_t *fmt, ...);
CRT_IMPORT int sprintf(char *src, const char *fmt, ...);
CRT_IMPORT int swscanf(const wchar_t *src, const wchar_t *fmt, ...);
CRT_IMPORT int sscanf(const char *src, const char *fmt, ...);
CRT_IMPORT int wprintf(const wchar_t *fmt, ...);
CRT_IMPORT int printf(const char *fmt, ...);
CRT_IMPORT int _wputs(const wchar_t *src);
CRT_IMPORT int puts(const char *src);
CRT_IMPORT int _stricmp(const char *x, const char *y);
CRT_IMPORT int _wcsicmp(const wchar_t *x, const wchar_t *y);
//CRT_IMPORT size_t strlen(const char *);
//CRT_IMPORT size_t wcslen(const wchar_t *);
//CRT_IMPORT char *strcpy(char *,const char *);
//CRT_IMPORT wchar_t *wcscpy(wchar_t *,const wchar_t *);
CRT_IMPORT void *memmove(void *dst, const void *src, size_t sz);
CRT_IMPORT const char *strchr(const char *src, int val);
CRT_IMPORT int strncmp(const char *x, const char *y, size_t sz);
} // extern "C"
#endif // ITH_HAS_CRT
#pragma once
// ith/common/types.h
// 8/23/2013 jichi
// Branch: ITH/common.h, rev 128
#include <windows.h> // needed for windef types
/** jichi 3/7/2014: Add guessed comment
*
* DWORD addr absolute or relative address
* DWORD split esp offset of the split character
*
* http://faydoc.tripod.com/cpu/pushad.htm
* http://agth.wikia.com/wiki/Cheat_Engine_AGTH_Tutorial
* The order is the same as pushd
* EAX, ECX, EDX, EBX, ESP (original value), EBP, ESI, and EDI (if the current operand-size attribute is 32) and AX, CX, DX, BX, SP
* Negative values of 'data_offset' and 'sub_offset' refer to registers:-4 for EAX, -8 for ECX, -C for EDX, -10 for EBX, -14 for ESP, -18 for EBP, -1C for ESI, -20 for EDI
*/
struct HookParam {
// jichi 8/24/2013: For special hooks. Original name: DataFun
typedef void (*text_fun_t)(DWORD esp, HookParam *hp, BYTE index, DWORD *data, DWORD *split, DWORD *len);
// jichi 10/24/2014: Add filter function. Return the if skip the text
typedef bool (*filter_fun_t)(LPVOID str, DWORD *len, HookParam *hp, BYTE index);
// jichi 10/24/2014: Add generic hook function, return false if stop execution.
typedef bool (*hook_fun_t)(DWORD esp, HookParam *hp);
DWORD addr; // absolute or relative address
DWORD off, // offset of the data in the memory
ind, // ?
split, // esp offset of the split character = pusha offset - 4
split_ind; // ?
DWORD module, // hash of the module
function;
text_fun_t text_fun;
filter_fun_t filter_fun;
hook_fun_t hook_fun;
DWORD type; // flags
WORD length_offset; // index of the string length
BYTE hook_len, // ?
recover_len; // ?
// 2/2/2015: jichi number of times - 1 to run the hook
BYTE extra_text_count;
BYTE _unused; // jichi 2/2/2015: add a BYTE type to make to total sizeof(HookParam) even.
// 7/20/2014: jichi additional parameters for PSP games
DWORD user_flags,
user_value;
};
// jichi 6/1/2014: Structure of the esp for extern functions
struct HookStack
{
// pushad
DWORD edi, // -0x24
esi, // -0x20
ebp, // -0x1c
esp, // -0x18
ebx, // -0x14
edx, // -0x10
ecx, // -0xc
eax; // -0x8
// pushfd
DWORD eflags; // -0x4
DWORD retaddr; // 0
DWORD args[1]; // 0x4
};
struct SendParam {
DWORD type;
HookParam hp;
};
struct Hook { // size: 0x80
HookParam hp;
LPWSTR hook_name;
int name_length;
BYTE recover[0x68 - sizeof(HookParam)];
BYTE original[0x10];
DWORD Address() const { return hp.addr; }
DWORD Type() const { return hp.type; }
WORD Length() const { return hp.hook_len; }
LPWSTR Name() const { return hook_name; }
int NameLength() const { return name_length; }
};
// EOF
#pragma once
// dllconfig.h
// 8/23/2013 jichi
#include "ith/common/memory.h"
#include "ith/common/string.h"
#include "ntdll/ntdll.h"
// EOF
# dllconfig.pri
# 8/9/2013 jichi
# For linking ITH injectable dlls.
# The dll is self-containd and Windows-independent.
CONFIG += dll noqt #noeh nosafeseh
CONFIG -= embed_manifest_dll # dynamically load dlls
win32 {
CONFIG(eh): DEFINES += ITH_HAS_SEH # Do have exception handler in msvcrt.dll on Windows Vista and later
CONFIG(noeh): DEFINES -= ITH_HAS_SEH # Do not have exception handler in msvcrt.dll on Windows XP and before
}
include(../../../config.pri)
#win32 {
# CONFIG(noeh): include($$LIBDIR/winseh/winseh_safe.pri)
#}
# jichi 11/24/2013: Disable manual heap
DEFINES -= ITH_HAS_HEAP
# jichi 11/13/2011: disable swprinf warning
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
## Libraries
#LIBS += -lkernel32 -luser32 -lgdi32
LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
#LIBS += -L$$WDK7_HOME/lib/crt/i386 -lmsvcrt
#QMAKE_LFLAGS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # This will leave runtime flags in the dll
#LIBS += -L$$WDK8_HOME/lib/winv6.3/um/x86 -lntdll
HEADERS += $$PWD/dllconfig.h
# EOF
# hook.pro
# CONFIG += eh eha
# include(../dllconfig.pri)
# hookxp.pro
# CONFIG += noeh
# include(../dllconfig.pri)
# dllconfig.pri
# include(../../../config.pri)
# win32 {
# CONFIG(eh): DEFINES += ITH_HAS_SEH
# CONFIG(noeh): DEFINES -= ITH_HAS_SEH
# }
# config.pri
# CONFIG(eha) {
# message(CONFIG eha)
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
# QMAKE_CXXFLAGS_STL_ON += /EHa
# QMAKE_CXXFLAGS_EXCEPTIONS_ON += /EHa
# }
#
# CONFIG(noeh) { # No Exception handler
# QMAKE_CXXFLAGS += /GR-
# QMAKE_CXXFLAGS_RTTI_ON -= /GR
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
# }
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
set(vnrhook_src
cli.h
config.h
hook.h
main.cc
engine/engine.cc
engine/engine.h
engine/hookdefs.h
engine/match.cc
engine/match.h
engine/pchooks.cc
engine/pchooks.h
engine/util.cc
engine/util.h
hijack/texthook.cc
rpc/pipe.cc
tree/avl.h
${PROJECT_SOURCE_DIR}/ccutil/ccmacro.h
${PROJECT_SOURCE_DIR}/cpputil/cpplocale.h
${PROJECT_SOURCE_DIR}/cpputil/cppmarshal.h
${PROJECT_SOURCE_DIR}/cpputil/cppmath.h
${PROJECT_SOURCE_DIR}/cpputil/cpppath.h
${PROJECT_SOURCE_DIR}/cpputil/cppstring.h
${PROJECT_SOURCE_DIR}/cpputil/cpptype.h
${PROJECT_SOURCE_DIR}/cpputil/cppunicode.h
${PROJECT_SOURCE_DIR}/disasm/disasm.cc
${PROJECT_SOURCE_DIR}/memdbg/memdbg.h
${PROJECT_SOURCE_DIR}/memdbg/memsearch.cc
${PROJECT_SOURCE_DIR}/memdbg/memsearch.h
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.cc
${PROJECT_SOURCE_DIR}/ntinspect/ntinspect.h
${PROJECT_SOURCE_DIR}/winversion/winversion.cc
${PROJECT_SOURCE_DIR}/winversion/winversion.h
${common_src}
${import_src}
)
source_group("common" FILES ${common_src})
source_group("import" FILES ${import_src})
add_library(vnrhook SHARED ${vnrhook_src})
set(vnrhookxp_src ${vnrhook_src}
${PROJECT_SOURCE_DIR}/winseh/winseh.cc
${PROJECT_SOURCE_DIR}/winseh/winseh_safe.cc
${PROJECT_SOURCE_DIR}/winseh/winseh.h
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
)
enable_language(ASM_MASM)
set_source_files_properties(
${PROJECT_SOURCE_DIR}/winseh/safeseh.asm
PROPERTIES
# CMAKE_ASM_MASM_FLAGS /safeseh # CMake bug 14711: http://www.cmake.org/Bug/view.php?id=14711
COMPILE_FLAGS /safeseh
)
add_library(vnrhookxp SHARED ${vnrhookxp_src})
set_target_properties(vnrhook vnrhookxp PROPERTIES
LINK_FLAGS "/SUBSYSTEM:WINDOWS /MANIFEST:NO"
)
target_compile_options(vnrhook PRIVATE
/EHa
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
target_compile_options(vnrhookxp PRIVATE
/GR-
# /EHs-c- # disable exception handling # CMake bug 15243: http://www.cmake.org/Bug/view.php?id=15243
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
if(TARGET vnrhookxp)
STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
endif(TARGET vnrhookxp)
set(vnrhook_libs
vnrsys
${WDK_HOME}/lib/wxp/i386/ntdll.lib
Version.lib
)
target_link_libraries(vnrhook ${vnrhook_libs})
target_link_libraries(vnrhookxp ${vnrhook_libs})
target_compile_definitions(vnrhook
PRIVATE
-DITH_HAS_SEH
)
target_compile_definitions(vnrhookxp
PRIVATE
)
install(TARGETS vnrhook vnrhookxp RUNTIME
DESTINATION .
CONFIGURATIONS Release
)
#pragma once
// cli.h
// 8/24/2013 jichi
// Branch: IHF_DLL/IHF_CLIENT.h, rev 133
//
// 8/24/2013 TODO:
// - Clean up this file
// - Reduce global variables. Use namespaces or singleton classes instead.
//#include <windows.h>
//#define IHF
#include "config.h"
#include "hook.h"
// jichi 12/25/2013: Header in each message sent to vnrsrv
// There are totally three elements
// - 0x0 dwAddr hook address
// - 0x4 dwRetn return address
// - 0x8 dwSplit split value
#define HEADER_SIZE 0xc
extern int current_hook;
extern WCHAR dll_mutex[];
//extern WCHAR dll_name[];
extern DWORD trigger;
//extern DWORD current_process_id;
// jichi 6/3/2014: Get memory range of the current module
extern DWORD processStartAddress,
processStopAddress;
template <class T, class D, class fComp, class fCopy, class fLength>
class AVLTree;
struct FunctionInfo {
DWORD addr;
DWORD module;
DWORD size;
LPWSTR name;
};
struct SCMP;
struct SCPY;
struct SLEN;
extern AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN> *tree;
void InitFilterTable();
// jichi 9/25/2013: This class will be used by NtMapViewOfSectionfor
// interprocedure communication, where constructor/destructor will NOT work.
class TextHook : public Hook
{
int UnsafeInsertHookCode();
DWORD UnsafeSend(DWORD dwDataBase, DWORD dwRetn);
public:
int InsertHook();
int InsertHookCode();
int InitHook(const HookParam &hp, LPCWSTR name = 0, WORD set_flag = 0);
int InitHook(LPVOID addr, DWORD data, DWORD data_ind,
DWORD split_off, DWORD split_ind, WORD type, DWORD len_off = 0);
DWORD Send(DWORD dwDataBase, DWORD dwRetn);
int RecoverHook();
int RemoveHook();
int ClearHook();
int ModifyHook(const HookParam&);
int SetHookName(LPCWSTR name);
int GetLength(DWORD base, DWORD in); // jichi 12/25/2013: Return 0 if failed
void CoolDown(); // jichi 9/28/2013: flush instruction cache on wine
};
extern TextHook *hookman,
*current_available;
//void InitDefaultHook();
struct FilterRange { DWORD lower, upper; };
extern FilterRange *filter;
extern bool running,
live;
extern HANDLE hPipe,
hmMutex;
DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter);
DWORD WINAPI CommandPipe(LPVOID lpThreadParameter);
//void RequestRefreshProfile();
//typedef DWORD (*InsertHookFun)(DWORD);
//typedef DWORD (*IdentifyEngineFun)();
//typedef DWORD (*InsertDynamicHookFun)(LPVOID addr, DWORD frame, DWORD stack);
//extern IdentifyEngineFun IdentifyEngine;
//extern InsertDynamicHookFun InsertDynamicHook;
// jichi 9/28/2013: Protect pipeline in wine
void CliLockPipe();
void CliUnlockPipe();
// EOF
#pragma once
// config.h
// 8/23/2013 jichi
// The first header file that are included by all source files.
#define IHF // for dll import
#include "ith/dllconfig.h"
// EOF
This diff could not be displayed because it is too large.
#pragma once
// engine/engine.h
// 8/23/2013 jichi
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
#include "config.h"
struct HookParam; // defined in ith types.h
namespace Engine {
// Global variables
extern wchar_t process_name_[MAX_PATH], // cached
process_path_[MAX_PATH]; // cached
extern DWORD module_base_,
module_limit_;
//extern LPVOID trigger_addr;
typedef bool (* trigger_fun_t)(LPVOID addr, DWORD frame, DWORD stack);
extern trigger_fun_t trigger_fun_;
bool InsertMonoHooks(); // Mono
// Wii engines
bool InsertGCHooks(); // Dolphin
bool InsertVanillawareGCHook();
// PS2 engines
bool InsertPCSX2Hooks(); // PCSX2
bool InsertMarvelousPS2Hook(); // http://marvelous.jp
bool InsertMarvelous2PS2Hook(); // http://marvelous.jp
bool InsertTypeMoonPS2Hook(); // http://typemoon.com
//bool InsertNamcoPS2Hook();
// PSP engines
void SpecialPSPHook(DWORD esp_base, HookParam *hp, DWORD *data, DWORD *split, DWORD *len); // General PSP extern hook
bool InsertPPSSPPHooks(); // PPSSPPWindows
bool InsertPPSSPPHLEHooks();
bool InsertOtomatePPSSPPHook(); // PSP otomate.jp, 0.9.9.0 only
bool Insert5pbPSPHook(); // PSP 5pb.jp
bool InsertAlchemistPSPHook(); // PSP Alchemist-net.co.jp, 0.9.8 only
bool InsertAlchemist2PSPHook(); // PSP Alchemist-net.co.jp
bool InsertBandaiNamePSPHook(); // PSP Bandai.co.jp
bool InsertBandaiPSPHook(); // PSP Bandai.co.jp
bool InsertBroccoliPSPHook(); // PSP Broccoli.co.jp
bool InsertFelistellaPSPHook(); // PSP felistella.co.jp
bool InsertCyberfrontPSPHook(); // PSP CYBERFRONT (closed)
bool InsertImageepochPSPHook(); // PSP Imageepoch.co.jp
bool InsertImageepoch2PSPHook();// PSP Imageepoch.co.jp
bool InsertKadokawaNamePSPHook(); // PSP Kadokawa.co.jp
bool InsertKonamiPSPHook(); // PSP Konami.jp
bool InsertTecmoPSPHook(); // PSP Koeitecmo.co.jp
//bool InsertTypeMoonPSPHook(); // PSP Typemoon.com
bool InsertOtomatePSPHook(); // PSP Otomate.jp, 0.9.8 only
//bool InsertOtomate2PSPHook(); // PSP otomate.jp >= 0.9.9.1
bool InsertIntensePSPHook(); // PSP Intense.jp
bool InsertKidPSPHook(); // PSP Kid-game.co.jp
bool InsertNippon1PSPHook(); // PSP Nippon1.jp
bool InsertNippon2PSPHook(); // PSP Nippon1.jp
bool InsertYetiPSPHook(); // PSP Yetigame.jp
bool InsertYeti2PSPHook(); // PSP Yetigame.jp
// PC engines
bool Insert2RMHook(); // 2RM - Adventure Engine
bool Insert5pbHook(); // 5pb.jp, PSP/PS3 games ported to PC
bool InsertAB2TryHook(); // Yane@AkabeiSoft2Try: YaneSDK.dll.
bool InsertAbelHook(); // Abel
bool InsertAdobeAirHook(); // Adobe AIR
bool InsertAdobeFlash10Hook(); // Adobe Flash Player 10
bool InsertAliceHook(); // System40@AliceSoft; do not work for latest alice games
bool InsertAmuseCraftHook(); // AMUSE CRAFT: *.pac
bool InsertAnex86Hook(); // Anex86: anex86.exe
bool InsertAOSHook(); // AOS: *.aos
bool InsertApricoTHook(); // Apricot: arc.a*
bool InsertArtemisHook(); // Artemis Engine: *.pfs
bool InsertAtelierHook(); // Atelier Kaguya: message.dat
bool InsertBGIHook(); // BGI: BGI.*
bool InsertC4Hook(); // C4: C4.EXE or XEX.EXE
bool InsertCaramelBoxHook(); // Caramel: *.bin
bool InsertCandyHook(); // SystemC@CandySoft: *.fpk
bool InsertCatSystemHook(); // CatSystem2: *.int
bool InsertCMVSHook(); // CMVS: data/pack/*.cpz; do not support the latest cmvs32.exe and cmvs64.exe
bool InsertCotophaHook(); // Cotopha: *.noa
bool InsertDebonosuHook(); // Debonosu: bmp.bak and dsetup.dll
bool InsertEaglsHook(); // E.A.G.L.S: EAGLES.dll
bool InsertEMEHook(); // EmonEngine: emecfg.ecf
bool InsertEushullyHook(); // Eushully: AGERC.DLL
bool InsertExpHook(); // EXP: http://www.exp-inc.jp
bool InsertFocasLensHook(); // FocasLens: Dat/*.arc, http://www.fo-lens.net
bool InsertGesen18Hook(); // Gsen18: *.szs
bool InsertGXPHook(); // GXP: *.gxp
bool InsertHorkEyeHook(); // HorkEye: resource string
bool InsertKAGParserHook(); // plugin/KAGParser.dll
bool InsertKAGParserExHook(); // plugin/KAGParserEx.dll
bool InsertKiriKiriHook(); // KiriKiri: *.xp3, resource string
bool InsertKiriKiriZHook(); // KiriKiri: *.xp3, resource string
bool InsertLeafHook(); // Leaf: *.pak
bool InsertLiveHook(); // Live: live.dll
bool InsertLunaSoftHook(); // LunaSoft: Pac/*.pac
bool InsertMalieHook(); // Malie@light: malie.ini
bool InsertMajiroHook(); // Majiro: *.arc
bool InsertMarineHeartHook(); // Marine Heart: SAISYS.exe
bool InsertMBLHook(); // MBL: *.mbl
bool InsertMEDHook(); // MED: *.med
bool InsertMinkHook(); // Mink: *.at2
//bool InsertMonoHook(); // Mono (Unity3D): */Mono/mono.dll
bool InsertNeXASHook(); // NeXAS: Thumbnail.pac
bool InsertNextonHook(); // NEXTON: aInfo.db
bool InsertNexton1Hook();
bool InsertNitroPlusHook(); // NitroPlus: *.npa
bool InsertPensilHook(); // Pensil: PSetup.exe
bool InsertQLIEHook(); // QLiE: GameData/*.pack
//bool InsertRai7Hook(); // Rai7puk: rai7.exe
bool InsertRejetHook(); // Rejet: Module/{gd.dat,pf.dat,sd.dat}
bool InsertRUGPHook(); // rUGP: rUGP.exe
bool InsertRetouchHook(); // Retouch: resident.dll
bool InsertRREHook(); // RunrunEngine: rrecfg.rcf
bool InsertShinaHook(); // ShinaRio: Rio.ini
bool InsertShinyDaysHook(); // ShinyDays
bool InsertElfHook(); // elf: Silky.exe
bool InsertScenarioPlayerHook();// sol-fa-soft: *.iar && *.sec5
bool InsertSiglusHook(); // SiglusEngine: SiglusEngine.exe
bool InsertSideBHook(); // SideB: Copyright side-B
bool InsertSyuntadaHook(); // Syuntada: dSoh.dat
bool InsertSystem43Hook(); // System43@AliceSoft: AliceStart.ini
bool InsertSystemAoiHook(); // SystemAoi: *.vfs
bool InsertTanukiHook(); // Tanuki: *.tak
bool InsertTaskforce2Hook(); // Taskforce2.exe
bool InsertTencoHook(); // Tenco: Check.mdx
bool InsertTriangleHook(); // Triangle: Execle.exe
bool InsertYukaSystem2Hook(); // YukaSystem2: *.ykc
bool InsertYurisHook(); // YU-RIS: *.ypf
bool InsertWillPlusHook(); // WillPlus: Rio.arc
bool InsertWolfHook(); // Wolf: Data.wolf
void InsertBrunsHook(); // Bruns: bruns.exe
void InsertIronGameSystemHook();// IroneGameSystem: igs_sample.exe
void InsertLucifenHook(); // Lucifen@Navel: *.lpk
void InsertRyokuchaHook(); // Ryokucha: _checksum.exe
void InsertRealliveHook(); // RealLive: RealLive*.exe
void InsertStuffScriptHook(); // Stuff: *.mpk
void InsertTinkerBellHook(); // TinkerBell: arc00.dat
void InsertWaffleHook(); // WAFFLE: cg.pak
// CIRCUS: avdata/
bool InsertCircusHook1();
bool InsertCircusHook2();
} // namespace Engine
// EOF
#pragma once
// engine/hookdefs.h
// 7/20/2014 jichi
#include "config.h"
// For HookParam user flags
enum HookParamFlag : unsigned long {
HPF_Null = 0 // never used
, HPF_IgnoreSameAddress = 1 // ignore the last same text address
};
// EOF
// eng/match.cc
// 8/9/2013 jichi
// Branch: ITH_Engine/engine.cpp, revision 133
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
#endif // _MSC_VER
#include "engine/match.h"
#include "engine/engine.h"
#include "engine/pchooks.h"
#include "engine/util.h"
#include "hook.h"
#include "ith/sys/sys.h"
#include "ith/common/except.h"
#include "ith/common/growl.h"
#include "ccutil/ccmacro.h"
//#define ConsoleOutput(...) (void)0 // jichi 8/18/2013: I don't need ConsoleOutput
enum { MAX_REL_ADDR = 0x200000 }; // jichi 8/18/2013: maximum relative address
// - Global variables -
namespace Engine {
WCHAR process_name_[MAX_PATH], // cached
process_path_[MAX_PATH]; // cached
DWORD module_base_,
module_limit_;
//LPVOID trigger_addr;
trigger_fun_t trigger_fun_;
} // namespace Engine
// - Methods -
namespace Engine { namespace { // unnamed
// jichi 7/17/2014: Disable GDI hooks for PPSSPP
bool DeterminePCEngine()
{
if (IthFindFile(L"PPSSPP*.exe")) { // jichi 7/12/2014 PPSSPPWindows.exe, PPSSPPEX.exe PPSSPPSP.exe
InsertPPSSPPHooks();
return true;
}
if (IthFindFile(L"pcsx2*.exe")) { // jichi 7/19/2014 PCSX2.exe or PCSX2WX.exe
if (!InsertPCSX2Hooks()) { // don't forget to rebuild vnrcli to inject SSE
// Always insert PC hooks so that user could add PCSX2 to VNR.
// TO BE REMOVED after more PS2 engines are added.
PcHooks::hookGDIFunctions();
PcHooks::hookLstrFunctions();
}
return true;
}
if (IthFindFile(L"Dolphin.exe")) { // jichi 7/20/2014
if (!InsertGCHooks()) {
// Always insert PC hooks so that user could add PCSX2 to VNR.
// TO BE REMOVED after more PS2 engines are added.
PcHooks::hookGDIFunctions();
PcHooks::hookLstrFunctions();
}
return true;
}
//if (IthFindFile(L"*\\Mono\\mono.dll")) { // jichi 4/21/2014: Mono
//if (IthCheckFile(L"bsz2_Data\\Mono\\mono.dll")) { // jichi 4/21/2014: Mono
// InsertMonoHook();
// return true;
//}
if (::GetModuleHandleA("mono.dll")) {
InsertMonoHooks();
// 3/20/2015 jichi
// Always insert GDI hooks even for Mono games
// For example: 新世黙示録 need GetGlyphOutlineA
PcHooks::hookGDIFunctions();
return true;
}
// PC games
PcHooks::hookGDIFunctions();
return false;
}
bool DetermineEngineByFile1()
{
if (IthFindFile(L"*.xp3") || Util::SearchResourceString(L"TVP(KIRIKIRI)")) {
if (Util::SearchResourceString(L"TVP(KIRIKIRI) Z ")) { // TVP(KIRIKIRI) Z CORE
// jichi 11/24/2014: Disabled that might crash VBH
//if (IthCheckFile(L"plugin\\KAGParser.dll"))
// InsertKAGParserHook();
//else if (IthCheckFile(L"plugin\\KAGParserEx.dll"))
// InsertKAGParserExHook();
if (InsertKiriKiriZHook())
return true;
}
InsertKiriKiriHook();
return true;
}
// 8/2/2014 jichi: Game name shown as 2RM - Adventure Engine
if (Util::SearchResourceString(L"2RM") && Util::SearchResourceString(L"Adventure Engine")) {
Insert2RMHook();
return true;
}
// 8/2/2014 jichi: Copyright is side-B, a conf.dat will be generated after the game is launched
// It also contains lua5.1.dll and lua5.dll
if (Util::SearchResourceString(L"side-B")) {
InsertSideBHook();
return true;
}
if (IthFindFile(L"bgi.*")) {
InsertBGIHook();
return true;
}
if (IthCheckFile(L"AGERC.DLL")) { // 6/1/2014 jichi: Eushully, AGE.EXE
InsertEushullyHook();
return true;
}
if (IthFindFile(L"data*.arc") && IthFindFile(L"stream*.arc")) {
InsertMajiroHook();
return true;
}
// jichi 5/31/2014
if (//IthCheckFile(L"Silkys.exe") || // It might or might not have Silkys.exe
// data, effect, layer, mes, music
IthCheckFile(L"data.arc") && IthCheckFile(L"effect.arc") && IthCheckFile(L"mes.arc")) {
InsertElfHook();
return true;
}
if (IthFindFile(L"data\\pack\\*.cpz")) {
InsertCMVSHook();
return true;
}
// jichi 10/12/2013: Restore wolf engine
// jichi 10/18/2013: Check for data/*.wolf
if (IthFindFile(L"data.wolf") || IthFindFile(L"data\\*.wolf")) {
InsertWolfHook();
return true;
}
if (IthCheckFile(L"advdata\\dat\\names.dat")) {
InsertCircusHook1();
return true;
}
if (IthCheckFile(L"advdata\\grp\\names.dat")) {
InsertCircusHook2();
return true;
}
if (IthFindFile(L"*.noa")) {
InsertCotophaHook();
return true;
}
if (IthFindFile(L"*.pfs")) { // jichi 10/1/2013
InsertArtemisHook();
return true;
}
if (IthFindFile(L"*.int")) {
InsertCatSystemHook();
return true;
}
if (IthCheckFile(L"message.dat")) {
InsertAtelierHook();
return true;
}
if (IthCheckFile(L"Check.mdx")) { // jichi 4/1/2014: AUGame
InsertTencoHook();
return true;
}
// jichi 12/25/2013: It may or may not be QLIE.
// AlterEgo also has GameData/sound.pack but is not QLIE
if (IthFindFile(L"GameData\\*.pack") && InsertQLIEHook())
return true;
if (IthFindFile(L"*.pac")) {
// jichi 6/3/2014: AMUSE CRAFT and SOFTPAL
// Selectively insert, so that lstrlenA can still get correct text if failed
if (IthCheckFile(L"dll\\resource.dll") && IthCheckFile(L"dll\\pal.dll") && InsertAmuseCraftHook())
return true;
if (IthCheckFile(L"Thumbnail.pac")) {
//ConsoleOutput("vnreng: IGNORE NeXAS");
InsertNeXASHook(); // jichi 7/6/2014: GIGA
return true;
}
if (Util::SearchResourceString(L"SOFTPAL")) {
ConsoleOutput("vnreng: IGNORE SoftPal UNiSONSHIFT");
return true;
}
}
// jichi 12/27/2014: LunaSoft
if (IthFindFile(L"Pac\\*.pac")) {
InsertLunaSoftHook();
return true;
}
// jichi 9/16/2013: Add Gesen18
if (IthFindFile(L"*.szs") || IthFindFile(L"Data\\*.szs")) {
InsertGesen18Hook();
return true;
}
// jichi 12/22/2013: Add rejet
if (IthCheckFile(L"gd.dat") && IthCheckFile(L"pf.dat") && IthCheckFile(L"sd.dat")) {
InsertRejetHook();
return true;
}
// Only examined with version 1.0
//if (IthFindFile(L"Adobe AIR\\Versions\\*\\Adobe AIR.dll")) { // jichi 4/15/2014: FIXME: Wildcard not working
if (IthCheckFile(L"Adobe AIR\\Versions\\1.0\\Adobe AIR.dll")) { // jichi 4/15/2014: Adobe AIR
InsertAdobeAirHook();
return true;
}
return false;
}
bool DetermineEngineByFile2()
{
if (IthCheckFile(L"resident.dll")) {
InsertRetouchHook();
return true;
}
if (IthCheckFile(L"Malie.ini") || IthCheckFile(L"Malie.exe")) { // jichi: 9/9/2014: Add malie.exe in case malie.ini is missing
InsertMalieHook();
return true;
}
if (IthCheckFile(L"live.dll")) {
InsertLiveHook();
return true;
}
// 9/5/2013 jichi
if (IthCheckFile(L"aInfo.db")) {
InsertNextonHook();
return true;
}
if (IthFindFile(L"*.lpk")) {
InsertLucifenHook();
return true;
}
if (IthCheckFile(L"cfg.pak")) {
InsertWaffleHook();
return true;
}
if (IthCheckFile(L"Arc00.dat")) {
InsertTinkerBellHook();
return true;
}
if (IthFindFile(L"*.vfs")) { // jichi 7/6/2014: Better to test AoiLib.dll? ja.wikipedia.org/wiki/ソフトハウスキャラ
InsertSystemAoiHook();
return true;
}
if (IthFindFile(L"*.mbl")) {
InsertMBLHook();
return true;
}
// jichi 8/1/2014: YU-RIS engine, lots of clockup game also has this pattern
if (IthFindFile(L"pac\\*.ypf") || IthFindFile(L"*.ypf")) {
// jichi 8/14/2013: CLOCLUP: "ノーブレスオブリージュ" would crash the game.
if (!IthCheckFile(L"noblesse.exe"))
InsertYurisHook();
return true;
}
if (IthFindFile(L"*.npa")) {
InsertNitroPlusHook();
return true;
}
return false;
}
bool DetermineEngineByFile3()
{
//if (IthCheckFile(L"libscr.dll")) { // already checked
// InsertBrunsHook();
// return true;
//}
// jichi 10/12/2013: Sample args.txt:
// See: http://tieba.baidu.com/p/2631413816
// -workdir
// .
// -loadpath
// .
// am.cfg
if (IthCheckFile(L"args.txt")) {
InsertBrunsHook();
return true;
}
if (IthCheckFile(L"emecfg.ecf")) {
InsertEMEHook();
return true;
}
if (IthCheckFile(L"rrecfg.rcf")) {
InsertRREHook();
return true;
}
if (IthFindFile(L"*.fpk") || IthFindFile(L"data\\*.fpk")) {
InsertCandyHook();
return true;
}
if (IthFindFile(L"arc.a*")) {
InsertApricoTHook();
return true;
}
if (IthFindFile(L"*.mpk")) {
InsertStuffScriptHook();
return true;
}
if (IthCheckFile(L"Execle.exe")) {
InsertTriangleHook();
return true;
}
// jichi 2/28/2015: No longer work for "大正×対称アリス episode I" from Primula
//if (IthCheckFile(L"PSetup.exe")) {
// InsertPensilHook();
// return true;
//}
if (IthCheckFile(L"Yanesdk.dll")) {
InsertAB2TryHook();
return true;
}
if (IthFindFile(L"*.med")) {
InsertMEDHook();
return true;
}
return false;
}
bool DetermineEngineByFile4()
{
if (IthCheckFile(L"EAGLS.dll")) { // jichi 3/24/2014: E.A.G.L.S
//ConsoleOutput("vnreng: IGNORE EAGLS");
InsertEaglsHook();
return true;
}
if (IthCheckFile(L"bmp.pak") && IthCheckFile(L"dsetup.dll")) {
InsertDebonosuHook();
return true;
}
if (IthCheckFile(L"C4.EXE") || IthCheckFile(L"XEX.EXE")) {
InsertC4Hook();
return true;
}
if (IthCheckFile(L"Rio.arc") && IthFindFile(L"Chip*.arc")) {
InsertWillPlusHook();
return true;
}
if (IthFindFile(L"*.tac")) {
InsertTanukiHook();
return true;
}
if (IthFindFile(L"*.gxp")) {
InsertGXPHook();
return true;
}
if (IthFindFile(L"*.aos")) { // jichi 4/2/2014: AOS hook
InsertAOSHook();
return true;
}
if (IthFindFile(L"*.at2")) { // jichi 12/23/2014: Mink, sample files: voice.at2, voice.det, voice.nme
InsertMinkHook();
return true;
}
if (IthFindFile(L"*.ykc")) { // jichi 7/15/2014: YukaSystem1 is not supported, though
//ConsoleOutput("vnreng: IGNORE YKC:Feng/HookSoft(SMEE)");
InsertYukaSystem2Hook();
return true;
}
if (IthFindFile(L"model\\*.hed")) { // jichi 9/8/2014: EXP
InsertExpHook();
return true;
}
// jichi 2/6/2015 平安亭
// dPi.dat, dPih.dat, dSc.dat, dSch.dat, dSo.dat, dSoh.dat, dSy.dat
//if (IthCheckFile(L"dSoh.dat")) { // no idea why this file does not work
if (IthCheckFile(L"dSch.dat")) {
InsertSyuntadaHook();
return true;
}
// jichi 2/28/2015: Delay checking Pensil in case something went wrong
// File pattern observed in [Primula] 大正×対称アリス episode I
// - PSetup.exe no longer exists
// - MovieTexture.dll information shows MovieTex dynamic library, copyright Pensil 2013
// - ta_trial.exe information shows 2XT - Primula Adventure Engine
if (IthFindFile(L"PSetup.exe") || IthFindFile(L"MovieTexture.dll") || Util::SearchResourceString(L"2XT -")) {
InsertPensilHook();
return true;
}
return false;
}
bool DetermineEngineByProcessName()
{
WCHAR str[MAX_PATH];
wcscpy(str, process_name_);
_wcslwr(str); // lower case
if (wcsstr(str,L"reallive") || IthCheckFile(L"Reallive.exe")) {
InsertRealliveHook();
return true;
}
// jichi 8/19/2013: DO NOT WORK for games like「ハピメア」
//if (wcsstr(str,L"cmvs32") || wcsstr(str,L"cmvs64")) {
// InsertCMVSHook();
// return true;
//}
// jichi 8/17/2013: Handle "~"
if (wcsstr(str, L"siglusengine") || !wcsncmp(str, L"siglus~", 7) || IthCheckFile(L"SiglusEngine.exe")) {
InsertSiglusHook();
return true;
}
if (wcsstr(str, L"taskforce2") || !wcsncmp(str, L"taskfo~", 7) || IthCheckFile(L"Faskforce2.exe")) {
InsertTaskforce2Hook();
return true;
}
if (wcsstr(str,L"rugp") || IthCheckFile(L"rugp.exe")) {
InsertRUGPHook();
return true;
}
// jichi 8/17/2013: Handle "~"
if (wcsstr(str, L"igs_sample") || !wcsncmp(str, L"igs_sa~", 7) || IthCheckFile(L"igs_sample.exe")) {
InsertIronGameSystemHook();
return true;
}
if (wcsstr(str, L"bruns") || IthCheckFile(L"bruns.exe")) {
InsertBrunsHook();
return true;
}
if (wcsstr(str, L"anex86") || IthCheckFile(L"anex86.exe")) {
InsertAnex86Hook();
return true;
}
// jichi 8/17/2013: Handle "~"
if (wcsstr(str, L"shinydays") || !wcsncmp(str, L"shinyd~", 7) || IthCheckFile(L"ShinyDays.exe")) {
InsertShinyDaysHook();
return true;
}
// jichi 10/3/2013: FIXME: Does not work
// Raise C0000005 even with admin priv
//if (wcsstr(str, L"bsz")) { // BALDRSKY ZERO
// InsertBaldrHook();
// return true;
//}
if (wcsstr(process_name_, L"SAISYS") || IthCheckFile(L"SaiSys.exe")) { // jichi 4/19/2014: Marine Heart
InsertMarineHeartHook();
return true;
}
DWORD len = wcslen(str);
// jichi 8/24/2013: Checking for Rio.ini or $procname.ini
//wcscpy(str+len-4, L"_?.war");
//if (IthFindFile(str)) {
// InsertShinaHook();
// return true;
//}
if (InsertShinaHook())
return true;
// jichi 8/10/2013: Since *.bin is common, move CaramelBox to the end
str[len - 3] = L'b';
str[len - 2] = L'i';
str[len - 1] = L'n';
str[len] = 0;
if (IthCheckFile(str) || IthCheckFile(L"trial.bin")) { // jichi 7/8/2014: add trial.bin
InsertCaramelBoxHook();
return true;
}
// This must appear at last since str is modified
wcscpy(str + len - 4, L"_checksum.exe");
if (IthCheckFile(str)) {
InsertRyokuchaHook();
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) // jichi 9/27/2014: For new Ryokucha games
InsertScenarioPlayerHook();
return true;
}
return false;
}
bool DetermineEngineOther()
{
if (InsertAliceHook())
return true;
// jichi 1/19/2015: Disable inserting Lstr for System40
// See: http://sakuradite.com/topic/618
if (IthCheckFile(L"System40.ini")) {
ConsoleOutput("vnreng: IGNORE old System40.ini");
return true;
}
// jichi 12/26/2013: Add this after alicehook
if (IthCheckFile(L"AliceStart.ini")) {
InsertSystem43Hook();
return true;
}
// jichi 8/24/2013: Move into functions
static BYTE static_file_info[0x1000];
if (IthGetFileInfo(L"*01", static_file_info))
if (*(DWORD*)static_file_info == 0) {
static WCHAR static_search_name[MAX_PATH];
LPWSTR name=(LPWSTR)(static_file_info+0x5E);
int len = wcslen(name);
name[len - 2] = L'*';
name[len - 1] = 0;
wcscpy(static_search_name, name);
IthGetFileInfo(static_search_name, static_file_info);
union {
FILE_BOTH_DIR_INFORMATION *both_info;
DWORD addr;
};
both_info = (FILE_BOTH_DIR_INFORMATION *)static_file_info;
//BYTE* ptr=static_file_info;
len = 0;
while (both_info->NextEntryOffset) {
addr += both_info->NextEntryOffset;
len++;
}
if (len > 3) {
InsertAbelHook();
return true;
}
}
return false;
}
// jichi 8/17/2014
// Put the patterns that might break other games at last
bool DetermineEngineAtLast()
{
if (IthFindFile(L"data\\*.cpk")) { // jichi 12/2/2014
Insert5pbHook();
return true;
}
// jichi 7/6/2014: named as ScenarioPlayer since resource string could be: scenario player program for xxx
// Do this at last as it is common
if (IthFindFile(L"*.iar") && IthFindFile(L"*.sec5")) { // jichi 4/18/2014: Other game engine could also have *.iar such as Ryokucha
InsertScenarioPlayerHook();
return true;
}
//if (IthCheckFile(L"arc0.dat") && IthCheckFile(L"script.dat") // jichi 11/14/2014: too common
if (Util::SearchResourceString(L"HorkEye")) { // appear in copyright: Copyright (C) HorkEye, http://horkeye.com
InsertHorkEyeHook();
return true;
}
if (IthCheckFile(L"comnArc.arc") // jichi 8/17/2014: this file might exist in multiple files
&& InsertNexton1Hook()) // old nexton game
return true;
if (IthCheckFile(L"arc.dat") // jichi 9/27/2014: too common
&& InsertApricoTHook())
return true;
if (IthFindFile(L"*.pak") // jichi 12/25/2014: too common
&& InsertLeafHook())
return true;
// jichi 10/31/2014
// File description: Adobe Flash Player 10.2r153
// Product name: Shockwave Flash
// Original filename: SAFlashPlayer.exe
// Legal trademarks: Adobe Flash Player
// No idea why, this must appear at last or it will crash
if (Util::SearchResourceString(L"Adobe Flash Player 10")) {
InsertAdobeFlash10Hook(); // only v10 might be supported. Otherwise, fallback to Lstr hooks
return true;
}
if (IthFindFile(L"dat\\*.arc")) { // jichi 2/6/2015
InsertFocasLensHook(); // Touhou
return true;
}
return false;
}
// jichi 6/1/2014
bool DetermineEngineGeneric()
{
bool ret = false;
if (IthCheckFile(L"AlterEgo.exe")) {
ConsoleOutput("vnreng: AlterEgo, INSERT WideChar hooks");
ret = true;
} else if (IthFindFile(L"data\\Sky\\*")) {
ConsoleOutput("vnreng: TEATIME, INSERT WideChar hooks");
ret = true;
}
//} else if (IthFindFile(L"image\\*.po2") || IthFindFile(L"image\\*.jo2")) {
// ConsoleOutput("vnreng: HarukaKanata, INSERT WideChar hooks"); // はるかかなた
// ret = true;
//}
if (ret)
PcHooks::hookWcharFunctions();
return ret;
}
bool DetermineNoEngine()
{
//if (IthFindFile(L"*\\Managed\\UnityEngine.dll")) { // jichi 12/3/2013: Unity (BALDRSKY ZERO)
// ConsoleOutput("vnreng: IGNORE Unity");
// return true;
//}
//if (IthCheckFile(L"bsz_Data\\Managed\\UnityEngine.dll") || IthCheckFile(L"bsz2_Data\\Managed\\UnityEngine.dll")) {
// ConsoleOutput("vnreng: IGNORE Unity");
// return true;
//}
// jichi 2/14/2015: Guilty+ RIN×SEN (PK)
if (IthCheckFile(L"rio.ini") || IthFindFile(L"*.war")) {
ConsoleOutput("vnreng: IGNORE unknown ShinaRio");
return true;
}
if (IthCheckFile(L"AdvHD.exe") || IthCheckFile(L"AdvHD.dll")) {
ConsoleOutput("vnreng: IGNORE Adv Player HD");
return true;
}
if (IthCheckFile(L"ScrPlayer.exe")) {
ConsoleOutput("vnreng: IGNORE ScrPlayer");
return true;
}
if (IthCheckFile(L"nnnConfig2.exe")) {
ConsoleOutput("vnreng: IGNORE Nya NNNConfig");
return true;
}
//if (IthCheckFile(L"AGERC.DLL")) { // jichi 3/17/2014: Eushully, AGE.EXE
// ConsoleOutput("vnreng: IGNORE Eushully");
// return true;
//}
if (IthCheckFile(L"game_sys.exe")) {
ConsoleOutput("vnreng: IGNORE Atelier Kaguya BY/TH");
return true;
}
if (IthFindFile(L"*.bsa")) {
ConsoleOutput("vnreng: IGNORE Bishop");
return true;
}
// jichi 3/19/2014: Escude game
// Example: bgm.bin gfx.bin maou.bin script.bin snd.bin voc.bin
if (IthCheckFile(L"gfx.bin") && IthCheckFile(L"snd.bin") && IthCheckFile(L"voc.bin")) {
ConsoleOutput("vnreng: IGNORE Escude");
return true;
}
// jichi 2/18/2015: Ignore if there is Nitro+ copyright
if (Util::SearchResourceString(L"Nitro+")) {
ConsoleOutput("vnreng: IGNORE unknown Nitro+");
return true;
}
// jichi 12/28/2014: "Chartreux Inc." in Copyright.
// Sublimary brands include Rosebleu, MORE, etc.
// GetGlyphOutlineA already works.
if (Util::SearchResourceString(L"Chartreux")) {
ConsoleOutput("vnreng: IGNORE Chartreux");
return true;
}
if (wcsstr(process_name_, L"lcsebody") || !wcsncmp(process_name_, L"lcsebo~", 7) || IthCheckFile(L"lcsebody")) { // jichi 3/19/2014: LC-ScriptEngine, GetGlyphOutlineA
ConsoleOutput("vnreng: IGNORE lcsebody");
return true;
}
wchar_t str[MAX_PATH];
DWORD i;
for (i = 0; process_name_[i]; i++) {
str[i] = process_name_[i];
if (process_name_[i] == L'.')
break;
}
*(DWORD *)(str + i + 1) = 0x630068; //.hcb
*(DWORD *)(str + i + 3) = 0x62;
if (IthCheckFile(str)) {
ConsoleOutput("vnreng: IGNORE FVP"); // jichi 10/3/2013: such like アトリエかぐや
return true;
}
return false;
}
// 12/13/2013: Declare it in a way compatible to EXCEPTION_PROCEDURE
EXCEPTION_DISPOSITION ExceptHandler(PEXCEPTION_RECORD ExceptionRecord, LPVOID, PCONTEXT, LPVOID)
{
if (ExceptionRecord->ExceptionCode == STATUS_ACCESS_VIOLATION) {
module_limit_ = ExceptionRecord->ExceptionInformation[1];
//OutputDWORD(module_limit_);
__asm
{
mov eax,fs:[0x30] // jichi 12/13/2013: get PEB
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov ecx,module_limit_
sub ecx,module_base_
mov [eax+0x20],ecx
}
}
//ContextRecord->Esp = recv_esp;
//ContextRecord->Eip = recv_eip;
//return ExceptionContinueExecution; // jichi 3/11/2014: this will still crash. Not sure why ITH use this. Change to ExceptionContinueSearch
return ExceptionContinueSearch; // an unwind is in progress,
}
// jichi 9/14/2013: Certain ITH functions like FindEntryAligned might raise exception without admin priv
// Return if succeeded.
bool UnsafeDetermineEngineType()
{
return DeterminePCEngine()
|| DetermineEngineByFile1()
|| DetermineEngineByFile2()
|| DetermineEngineByFile3()
|| DetermineEngineByFile4()
|| DetermineEngineByProcessName()
|| DetermineEngineOther()
|| DetermineEngineAtLast()
|| DetermineEngineGeneric()
|| DetermineNoEngine()
;
}
// jichi 10/21/2014: Return whether found the game engine
bool DetermineEngineType()
{
// jichi 9/27/2013: disable game engine for debugging use
#ifdef ITH_DISABLE_ENGINE
PcHooks::hookLstrFunctions();
return false;
#else
bool found = false;
#ifdef ITH_HAS_SEH
__try { found = UnsafeDetermineEngineType(); }
__except(ExceptHandler((GetExceptionInformation())->ExceptionRecord, 0, 0, 0)) {}
#else // use my own SEH
seh_with_eh(ExceptHandler,
found = UnsafeDetermineEngineType());
#endif // ITH_HAS_SEH
if (!found) // jichi 10/2/2013: Only enable it if no game engine is detected
PcHooks::hookLstrFunctions();
else
ConsoleOutput("vnreng: found game engine, IGNORE non gui hooks");
return found;
#endif // ITH_DISABLE_ENGINE
}
// __asm
// {
// mov eax,seh_recover
// mov recv_eip,eax
// push ExceptHandler
// push fs:[0]
// mov fs:[0],esp
// pushad
// mov recv_esp,esp
// }
// DetermineEngineType();
// status++;
// __asm
// {
//seh_recover:
// popad
// mov eax,[esp]
// mov fs:[0],eax
// add esp,8
// }
// if (status == 0)
// ConsoleOutput("Fail to identify engine type.");
// else
// ConsoleOutput("Initialized successfully.");
//}
}} // namespace Engine unnamed
// - API -
bool Engine::IdentifyEngine()
{
// jichi 12/18/2013: Though FillRange could raise, it should never raise for he current process
// So, SEH is not used here.
FillRange(process_name_, &module_base_, &module_limit_);
return DetermineEngineType();
}
DWORD Engine::InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack)
{ return trigger_fun_ ? !trigger_fun_(addr, frame, stack) : 0; }
void Engine::match(LPVOID lpThreadParameter)
{
CC_UNUSED(lpThreadParameter);
Util::GetProcessName(process_name_); // Initialize process name
Util::GetProcessPath(process_path_); // Initialize process path
::engine_registered = true;
//::RegisterEngineModule((DWORD)IdentifyEngine, (DWORD)InsertDynamicHook);
}
// EOF
/*
extern "C" {
// http://gmogre3d.googlecode.com/svn-history/r815/trunk/OgreMain/src/WIN32/OgreMinGWSupport.cpp
// http://forum.osdev.org/viewtopic.php?f=8&t=22352
//#pragma data_seg()
//#pragma comment(linker, "/merge:.CRT=.data") // works fine in visual c++ 6
//#pragma data_seg()
//#pragma comment(linker, "/merge:.CRT=.rdata")
// MSVC libs use _chkstk for stack-probing. MinGW equivalent is _alloca.
//void _alloca();
//void _chkstk() { _alloca(); }
// MSVC uses security cookies to prevent some buffer overflow attacks.
// provide dummy implementations.
//void _fastcall __security_check_cookie(intptr_t i) {}
void __declspec(naked) __fastcall __security_check_cookie(UINT_PTR cookie) {}
}
*/
#pragma once
// engine/match.h
// 8/23/2013 jichi
// TODO: Clean up the interface to match game engines.
// Split the engine match logic out of hooks.
// Modify the game hook to allow replace functions for arbitary purpose
// instead of just extracting text.
#include "config.h"
namespace Engine {
void match(LPVOID lpThreadParameter);
// jichi 10/21/2014: Return whether found the engine
bool IdentifyEngine();
// jichi 10/21/2014: Return 0 if failed
DWORD InsertDynamicHook(LPVOID addr, DWORD frame, DWORD stack);
} // namespace Engine
// EOF
// pchooks.cc
// 8/1/2014 jichi
#include "engine/pchooks.h"
#include "hook.h"
#define DEBUG "vnrcli"
#define DPRINT(cstr) ConsoleOutput(DEBUG ":" __FUNCTION__ ":" cstr) // defined in vnrcli
// 8/1/2014 jichi: Split is not used.
// Although split is specified, USING_SPLIT is not assigned.
// Use LPASTE to convert to wchar_t
// http://bytes.com/topic/c/answers/135834-defining-wide-character-strings-macros
#define LPASTE(s) L##s
#define L(s) LPASTE(s)
#define NEW_HOOK(_fun, _data, _data_ind, _split_off, _split_ind, _type, _len_off) \
{ \
HookParam hp = {}; \
hp.addr = (DWORD)_fun; \
hp.off = _data; \
hp.ind = _data_ind; \
hp.split = _split_off; \
hp.split_ind = _split_ind; \
hp.type = _type; \
hp.length_offset = _len_off; \
NewHook(hp, L(#_fun)); \
}
// jichi 7/17/2014: Renamed from InitDefaultHook
void PcHooks::hookGDIFunctions()
{
DPRINT("enter");
// int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
//
// jichi 9/8/2013: Guessed meaning
// - data(off): 4 * the n-th (base 1) parameter representing the data of the string
// - len_off:
// - the n-th (base 1) parameter representing the length of the string
// - or 1 if is char
// - or 0 if detect on run time
// - type: USING_STRING if len_off != 1 else BIG_ENDIAN or USING_UNICODE
//
// Examples:
// int WINAPI lstrlenA(LPCSTR lpString)
// - data: 4 * 1 = 4, as lpString is the first
// - len_off: 0, as no parameter representing string length
// - type: BIG_ENDIAN, since len_off == 1
// BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
// - data: 4 * 2 = 0x8, as lpString is the second
// - len_off: 3, as nCount is the 3rd parameter
// - type: USING_STRING, since len_off != 1
//
// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
enum stack {
s_retaddr = 0
, s_arg1 = 4 * 1 // 0x4
, s_arg2 = 4 * 2 // 0x8
, s_arg3 = 4 * 3 // 0xc
, s_arg4 = 4 * 4 // 0x10
, s_arg5 = 4 * 5 // 0x14
, s_arg6 = 4 * 6 // 0x18
};
//#define _(Name, ...) \
// hookman[HF_##Name].InitHook(Name, __VA_ARGS__); \
// hookman[HF_##Name].SetHookName(names[HF_##Name]);
// Always use s_arg1 = hDC as split_off
// 7/26/2014 jichi: Why there is no USING_SPLIT type?
// gdi32.dll
NEW_HOOK(GetTextExtentPoint32A, s_arg2, 0,s_arg1,0, USING_STRING, 3) // BOOL GetTextExtentPoint32(HDC hdc, LPCTSTR lpString, int c, LPSIZE lpSize);
NEW_HOOK(GetGlyphOutlineA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // DWORD GetGlyphOutline(HDC hdc, UINT uChar, UINT uFormat, LPGLYPHMETRICS lpgm, DWORD cbBuffer, LPVOID lpvBuffer, const MAT2 *lpmat2);
NEW_HOOK(ExtTextOutA, s_arg6, 0,s_arg1,0, USING_STRING, 7) // BOOL ExtTextOut(HDC hdc, int X, int Y, UINT fuOptions, const RECT *lprc, LPCTSTR lpString, UINT cbCount, const INT *lpDx);
NEW_HOOK(TextOutA, s_arg4, 0,s_arg1,0, USING_STRING, 5) // BOOL TextOut(HDC hdc, int nXStart, int nYStart, LPCTSTR lpString, int cchString);
NEW_HOOK(GetCharABCWidthsA, s_arg2, 0,s_arg1,0, BIG_ENDIAN, 1) // BOOL GetCharABCWidths(HDC hdc, UINT uFirstChar, UINT uLastChar, LPABC lpabc);
NEW_HOOK(GetTextExtentPoint32W, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(GetGlyphOutlineW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
NEW_HOOK(ExtTextOutW, s_arg6, 0,s_arg1,0, USING_UNICODE|USING_STRING, 7)
NEW_HOOK(TextOutW, s_arg4, 0,s_arg1,0, USING_UNICODE|USING_STRING, 5)
NEW_HOOK(GetCharABCWidthsW, s_arg2, 0,s_arg1,0, USING_UNICODE, 1)
// user32.dll
NEW_HOOK(DrawTextA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawText(HDC hDC, LPCTSTR lpchText, int nCount, LPRECT lpRect, UINT uFormat);
NEW_HOOK(DrawTextExA, s_arg2, 0,s_arg1,0, USING_STRING, 3) // int DrawTextEx(HDC hdc, LPTSTR lpchText,int cchText, LPRECT lprc, UINT dwDTFormat, LPDRAWTEXTPARAMS lpDTParams);
NEW_HOOK(DrawTextW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
NEW_HOOK(DrawTextExW, s_arg2, 0,s_arg1,0, USING_UNICODE|USING_STRING, 3)
//#undef _
DPRINT("leave");
}
// jichi 10/2/2013
// Note: All functions does not have NO_CONTEXT attribute and will be filtered.
void PcHooks::hookLstrFunctions()
{
DPRINT("enter");
// int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind, DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
enum stack {
s_retaddr = 0
, s_arg1 = 4 * 1 // 0x4
//, s_arg2 = 4 * 2 // 0x8
//, s_arg3 = 4 * 3 // 0xc
//, s_arg4 = 4 * 4 // 0x10
//, s_arg5 = 4 * 5 // 0x14
//, s_arg6 = 4 * 6 // 0x18
};
// http://msdn.microsoft.com/en-us/library/78zh94ax.aspx
// int WINAPI lstrlen(LPCTSTR lpString);
// Lstr functions usually extracts rubbish, and might crash certain games like 「Magical Marriage Lunatics!!」
// Needed by Gift
// Use arg1 address for both split and data
NEW_HOOK(lstrlenA, s_arg1, 0,s_arg1,0, USING_STRING, 0) // 9/8/2013 jichi: int WINAPI lstrlen(LPCTSTR lpString);
NEW_HOOK(lstrlenW, s_arg1, 0,s_arg1,0, USING_UNICODE|USING_STRING, 0) // 9/8/2013 jichi: add lstrlen
// size_t strlen(const char *str);
// size_t strlen_l(const char *str, _locale_t locale);
// size_t wcslen(const wchar_t *str);
// size_t wcslen_l(const wchar_t *str, _locale_t locale);
// size_t _mbslen(const unsigned char *str);
// size_t _mbslen_l(const unsigned char *str, _locale_t locale);
// size_t _mbstrlen(const char *str);
// size_t _mbstrlen_l(const char *str, _locale_t locale);
// http://msdn.microsoft.com/en-us/library/ex0hs2ad.aspx
// Needed by 娘姉妹
//
// <tchar.h>
// char *_strinc(const char *current, _locale_t locale);
// wchar_t *_wcsinc(const wchar_t *current, _locale_t locale);
// <mbstring.h>
// unsigned char *_mbsinc(const unsigned char *current);
// unsigned char *_mbsinc_l(const unsigned char *current, _locale_t locale);
//_(L"_strinc", _strinc, 4, 0,4,0, USING_STRING, 0) // 12/13/2013 jichi
//_(L"_wcsinc", _wcsinc, 4, 0,4,0, USING_UNICODE|USING_STRING, 0)
DPRINT("leave");
}
void PcHooks::hookWcharFunctions()
{
DPRINT("enter");
// 12/1/2013 jichi:
// AlterEgo
// http://tieba.baidu.com/p/2736475133
// http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page355
//
// MultiByteToWideChar
// http://blgames.proboards.com/thread/265
//
// WideCharToMultiByte
// http://www.hongfire.com/forum/showthread.php/36807-AGTH-text-extraction-tool-for-games-translation/page156
//
// int MultiByteToWideChar(
// _In_ UINT CodePage,
// _In_ DWORD dwFlags,
// _In_ LPCSTR lpMultiByteStr, // hook here
// _In_ int cbMultiByte,
// _Out_opt_ LPWSTR lpWideCharStr,
// _In_ int cchWideChar
// );
// int WideCharToMultiByte(
// _In_ UINT CodePage,
// _In_ DWORD dwFlags,
// _In_ LPCWSTR lpWideCharStr,
// _In_ int cchWideChar,
// _Out_opt_ LPSTR lpMultiByteStr,
// _In_ int cbMultiByte,
// _In_opt_ LPCSTR lpDefaultChar,
// _Out_opt_ LPBOOL lpUsedDefaultChar
// );
enum stack {
s_retaddr = 0
//, s_arg1 = 4 * 1 // 0x4
//, s_arg2 = 4 * 2 // 0x8
, s_arg3 = 4 * 3 // 0xc
//, s_arg4 = 4 * 4 // 0x10
//, s_arg5 = 4 * 5 // 0x14
//, s_arg6 = 4 * 6 // 0x18
};
// 3/17/2014 jichi: Temporarily disabled
// http://sakuradite.com/topic/159
NEW_HOOK(MultiByteToWideChar, s_arg3, 0,4,0, USING_STRING, 4)
NEW_HOOK(WideCharToMultiByte, s_arg3, 0,4,0, USING_UNICODE|USING_STRING, 4)
DPRINT("leave");
}
// EOF
#pragma once
// pchooks.h
// 8/1/2014 jichi
#include "config.h"
namespace PcHooks {
void hookGDIFunctions();
void hookLstrFunctions();
void hookWcharFunctions();
} // namespace PcHooks
// EOF
// util/util.cc
// 8/23/2013 jichi
// Branch: ITH_Engine/engine.cpp, revision 133
// See: http://ja.wikipedia.org/wiki/プロジェクト:美少女ゲーム系/ゲームエンジン
#include "engine/util.h"
#include "ith/sys/sys.h"
namespace { // unnamed
// jichi 4/19/2014: Return the integer that can mask the signature
DWORD SigMask(DWORD sig)
{
__asm
{
xor ecx,ecx
mov eax,sig
_mask:
shr eax,8
inc ecx
test eax,eax
jnz _mask
sub ecx,4
neg ecx
or eax,-1
shl ecx,3
shr eax,cl
}
}
} // namespace unnamed
// jichi 8/24/2013: binary search?
DWORD Util::GetCodeRange(DWORD hModule,DWORD *low, DWORD *high)
{
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
DWORD dwReadAddr;
IMAGE_SECTION_HEADER *shdr;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
dwReadAddr = hModule + DosHdr->e_lfanew;
NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr;
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
shdr = (PIMAGE_SECTION_HEADER)((DWORD)(&NtHdr->OptionalHeader) + NtHdr->FileHeader.SizeOfOptionalHeader);
while ((shdr->Characteristics & IMAGE_SCN_CNT_CODE) == 0)
shdr++;
*low = hModule + shdr->VirtualAddress;
*high = *low + (shdr->Misc.VirtualSize & 0xfffff000) + 0x1000;
}
}
return 0;
}
DWORD Util::FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig)
{
//WCHAR str[0x40];
enum { reverse_length = 0x800 };
DWORD t, l;
DWORD mask = SigMask(sig);
bool flag2;
for (DWORD i = 0x1000; i < size-4; i++) {
bool flag1 = false;
if (*(BYTE *)(pt + i) == 0xe8) {
flag1 = flag2 = true;
t = *(DWORD *)(pt + i + 1);
} else if (*(WORD *)(pt + i) == 0x15ff) {
flag1 = true;
flag2 = false;
t = *(DWORD *)(pt + i + 2);
}
if (flag1) {
if (flag2) {
flag1 = (pt + i + 5 + t == fun);
l = 5;
} else if (t >= pt && t <= pt + size - 4) {
flag1 = fun == *(DWORD *)t;
l = 6;
} else
flag1 = false;
if (flag1)
//swprintf(str,L"CALL addr: 0x%.8X",pt + i);
//OutputConsole(str);
for (DWORD j = i; j > i - reverse_length; j--)
if ((*(WORD *)(pt + j)) == (sig & mask)) //Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",pt + j);
//OutputConsole(str);
return pt + j;
else
i += l;
}
}
//OutputConsole(L"Find call and entry failed.");
return 0;
}
DWORD Util::FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp)
{
BYTE sig = (jmp) ? 0xe9 : 0xe8;
for (DWORD i = 0x1000; i < size - 4; i++)
if (sig == *(BYTE *)(pt + i)) {
DWORD t = *(DWORD *)(pt + i + 1);
if(fun == pt + i + 5 + t)
//OutputDWORD(pt + i);
return pt + i;
else
i += 5;
}
return 0;
}
DWORD Util::FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp)
{
WORD sig = jmp ? 0x25ff : 0x15ff;
for (DWORD i = 0x1000; i < size - 4; i++)
if (sig == *(WORD *)(pt + i)) {
DWORD t = *(DWORD *)(pt + i + 2);
if (t > pt && t < pt + size) {
if (fun == *(DWORD *)t)
return pt + i;
else
i += 5;
}
}
return 0;
}
DWORD Util::FindCallBoth(DWORD fun, DWORD size, DWORD pt)
{
for (DWORD i = 0x1000; i < size - 4; i++) {
if (*(BYTE *)(pt + i) == 0xe8) {
DWORD t = *(DWORD *)(pt + i + 1) + pt + i + 5;
if (t == fun)
return i;
}
if (*(WORD *)(pt + i) == 0x15ff) {
DWORD t = *(DWORD *)(pt + i + 2);
if (t >= pt && t <= pt + size - 4) {
if (*(DWORD *)t == fun)
return i;
else
i += 6;
}
}
}
return 0;
}
DWORD Util::FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig)
{
//WCHAR str[0x40];
enum { reverse_length = 0x800 };
DWORD mask = SigMask(sig);
for (DWORD i = 0x1000; i < size - 4; i++)
if (*(WORD *)(pt + i) == 0x15ff) {
DWORD t = *(DWORD *)(pt + i + 2);
if (t >= pt && t <= pt + size - 4) {
if (*(DWORD *)t == fun)
//swprintf(str,L"CALL addr: 0x%.8X",pt + i);
//OutputConsole(str);
for (DWORD j = i ; j > i - reverse_length; j--)
if ((*(DWORD *)(pt + j) & mask) == sig) // Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",pt + j);
//OutputConsole(str);
return pt + j;
} else
i += 6;
}
//OutputConsole(L"Find call and entry failed.");
return 0;
}
DWORD Util::FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig)
{
//WCHAR str[0x40];
enum { reverse_length = 0x800 };
if (DWORD i = FindCallOrJmpRel(fun, size, pt, false)) {
DWORD mask = SigMask(sig);
for (DWORD j = i; j > i - reverse_length; j--)
if (((*(DWORD *)j) & mask) == sig) //Fun entry 1.
//swprintf(str,L"Entry: 0x%.8X",j);
//OutputConsole(str);
return j;
//OutputConsole(L"Find call and entry failed.");
}
return 0;
}
DWORD Util::FindEntryAligned(DWORD start, DWORD back_range)
{
start &= ~0xf;
for (DWORD i = start, j = start - back_range; i > j; i-=0x10) {
DWORD k = *(DWORD *)(i-4);
if (k == 0xcccccccc
|| k == 0x90909090
|| k == 0xccccccc3
|| k == 0x909090c3
)
return i;
DWORD t = k & 0xff0000ff;
if (t == 0xcc0000c2 || t == 0x900000c2)
return i;
k >>= 8;
if (k == 0xccccc3 || k == 0x9090c3)
return i;
t = k & 0xff;
if (t == 0xc2)
return i;
k >>= 8;
if (k == 0xccc3 || k == 0x90c3)
return i;
k >>= 8;
if (k == 0xc3)
return i;
}
return 0;
}
DWORD Util::FindImportEntry(DWORD hModule, DWORD fun)
{
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
DWORD IAT, end, pt, addr;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
IAT = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].VirtualAddress;
end = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IAT].Size;
IAT += hModule;
end += IAT;
for (pt = IAT; pt < end; pt += 4) {
addr = *(DWORD *)pt;
if (addr == fun)
return pt;
}
}
}
return 0;
}
// Search string in rsrc section. This section usually contains version and copyright info.
bool Util::SearchResourceString(LPCWSTR str)
{
DWORD hModule = Util::GetModuleBase();
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
DWORD rsrc, size;
//__asm int 3
if (IMAGE_DOS_SIGNATURE == DosHdr->e_magic) {
NtHdr = (IMAGE_NT_HEADERS *)(hModule + DosHdr->e_lfanew);
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
rsrc = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
if (rsrc) {
rsrc += hModule;
if (IthGetMemoryRange((LPVOID)rsrc, &rsrc ,&size) &&
SearchPattern(rsrc, size - 4, str, wcslen(str) << 1))
return true;
}
}
}
return false;
}
// jichi 4/15/2014: Copied from GetModuleBase in ITH CLI, for debugging purpose
DWORD Util::FindModuleBase(DWORD hash)
{
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov esi,[eax+0x14]
mov edi,_wcslwr
listfind:
mov edx,[esi+0x28]
test edx,edx
jz notfound
push edx
call edi
pop edx
xor eax,eax
calc:
movzx ecx, word ptr [edx]
test cl,cl
jz fin
ror eax,7
add eax,ecx
add edx,2
jmp calc
fin:
cmp eax,[hash]
je found
mov esi,[esi]
jmp listfind
notfound:
xor eax,eax
jmp termin
found:
mov eax,[esi+0x10]
termin:
}
}
// EOF
#pragma once
// util/util.h
// 8/23/2013 jichi
#include "config.h"
namespace Util {
DWORD GetCodeRange(DWORD hModule,DWORD *low, DWORD *high);
DWORD FindCallAndEntryBoth(DWORD fun, DWORD size, DWORD pt, DWORD sig);
DWORD FindCallOrJmpRel(DWORD fun, DWORD size, DWORD pt, bool jmp);
DWORD FindCallOrJmpAbs(DWORD fun, DWORD size, DWORD pt, bool jmp);
DWORD FindCallBoth(DWORD fun, DWORD size, DWORD pt);
DWORD FindCallAndEntryAbs(DWORD fun, DWORD size, DWORD pt, DWORD sig);
DWORD FindCallAndEntryRel(DWORD fun, DWORD size, DWORD pt, DWORD sig);
DWORD FindEntryAligned(DWORD start, DWORD back_range);
DWORD FindImportEntry(DWORD hModule, DWORD fun);
// jichi 4/15/2014: Copied from ITH CLI, for debugging purpose
DWORD FindModuleBase(DWORD hash);
bool SearchResourceString(LPCWSTR str);
/**
* @param name process name without path deliminator
*/
inline void GetProcessName(wchar_t *name)
{
//assert(name);
PLDR_DATA_TABLE_ENTRY it;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
}
::wcscpy(name, it->BaseDllName.Buffer);
}
/**
* @param path with process name and directy name
*/
inline void GetProcessPath(wchar_t *path)
{
//assert(path);
PLDR_DATA_TABLE_ENTRY it;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
}
::wcscpy(path, it->FullDllName.Buffer);
}
/**
* @return HANDLE module handle
*/
inline DWORD GetModuleBase()
{
__asm
{
mov eax,fs:[0x18]
mov eax,[eax+0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov eax,[eax+0x18]
}
}
} // namespace Util
// EOF
// texthook.cc
// 8/24/2013 jichi
// Branch: ITH_DLL/texthook.cpp, rev 128
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
# pragma warning (disable:4018) // C4018: sign/unsigned mismatch
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
#endif // _MSC_VER
#include "cli.h"
#include "engine/match.h"
#include "ith/common/except.h"
//#include "ith/common/growl.h"
#include "ith/sys/sys.h"
#include "disasm/disasm.h"
//#include "winseh/winseh.h"
//#define ConsoleOutput(...) (void)0 // jichi 9/17/2013: I don't need this ><
// - Global variables -
// 10/14/2014 jichi: disable GDI hooks
static bool gdi_hook_disabled_ = false;
void DisableGDIHooks()
{ ::gdi_hook_disabled_ = true; }
static bool IsGDIFunction(LPCVOID addr)
{
static LPVOID funcs[] = { HOOK_GDI_FUNCTION_LIST };
for (size_t i = 0; i < sizeof(funcs)/sizeof(*funcs); i++)
if (addr == funcs[i])
return true;
return false;
}
//FilterRange filter[8];
DWORD flag,
enter_count;
TextHook *hookman,
*current_available;
// - Unnamed helpers -
namespace { // unnamed
//provide const time hook entry.
int userhook_count;
#if 0 // 3/6/2015 jichi: this hook is not used and hence disabled
const byte common_hook2[] = {
0x89, 0x3c,0xe4, // mov [esp],edi
0x60, // pushad
0x9c, // pushfd
0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
0x8b,0x32, // mov esi,[edx] ; return address
0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TextHook
0xe8, 0,0,0,0, // call @hook
0x9d, // popfd
0x61, // popad
0x5f, // pop edi ; skip return address on stack
}; //...
#endif // 0
const BYTE common_hook[] = {
0x9c, // pushfd
0x60, // pushad
0x9c, // pushfd
0x8d,0x54,0x24,0x28, // lea edx,[esp+0x28] ; esp value
0x8b,0x32, // mov esi,[edx] ; return address
0xb9, 0,0,0,0, // mov ecx, $ ; pointer to TexHhook
0xe8, 0,0,0,0, // call @hook
0x9d, // popfd
0x61, // popad
0x9d // popfd
};
/**
* jichi 7/19/2014
*
* @param original_addr
* @param new_addr
* @param hook_len
* @param original_len
* @return -1 if failed, else 0 if ?, else ?
*/
int MapInstruction(DWORD original_addr, DWORD new_addr, BYTE &hook_len, BYTE &original_len)
{
int flag = 0;
DWORD l = 0;
const BYTE *r = (const BYTE *)original_addr; // 7/19/2014 jichi: original address is not modified
BYTE *c = (BYTE *)new_addr; // 7/19/2014 jichi: but new address might be modified
while((r - (BYTE *) original_addr) < 5) {
l = ::disasm(r);
if (l == 0) {
ConsoleOutput("vnrcli:MapInstruction: FAILED: failed to disasm");
return -1;
}
::memcpy(c, r, l);
if (*r >= 0x70 && *r < 0x80) {
c[0] = 0xf;
c[1] = *r + 0x10;
c += 6;
__asm
{
mov eax,r
add eax,2
movsx edx,byte ptr [eax-1]
add edx,eax
mov eax,c
sub edx,eax
mov [eax-4],edx
}
} else if (*r == 0xeb) {
c[0] = 0xe9;
c += 5;
__asm
{
mov eax,r
add eax,2
movsx edx,[eax-1]
add edx,eax
mov eax,c
sub edx,eax
mov [eax-4],edx
}
if (r - (BYTE *)original_addr < 5 - l) {
ConsoleOutput("vnrcli:MapInstruction: not safe to move instruction right after short jmp");
return -1; // Not safe to move instruction right after short jmp.
} else
flag = 1;
} else if (*r == 0xe8 || *r == 0xe9) {
c[0]=*r;
c += 5;
flag = (*r == 0xe9);
__asm
{
mov eax,r
add eax,5
mov edx,[eax-4]
add edx,eax
mov eax,c
sub edx,eax
mov [eax-4],edx
}
} else if (*r == 0xf && (*(r + 1) >> 4) == 0x8) {
c += 6;
__asm
{
mov eax,r
mov edx,dword ptr [eax+2]
add eax,6
add eax,edx
mov edx,c
sub eax,edx
mov [edx-4],eax
}
}
else
c += l;
r += l;
}
original_len = r - (BYTE *)original_addr;
hook_len = c - (BYTE *)new_addr;
return flag;
}
//copy original instruction
//jmp back
DWORD GetModuleBase(DWORD hash)
{
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov esi,[eax+0x14]
mov edi,_wcslwr
listfind:
mov edx,[esi+0x28]
test edx,edx
jz notfound
push edx
call edi
pop edx
xor eax,eax
calc:
movzx ecx, word ptr [edx]
test cl,cl
jz fin
ror eax,7
add eax,ecx
add edx,2
jmp calc
fin:
cmp eax,[hash]
je found
mov esi,[esi]
jmp listfind
notfound:
xor eax,eax
jmp termin
found:
mov eax,[esi+0x10]
termin:
}
}
DWORD GetModuleBase()
{
__asm
{
mov eax, fs:[0x18]
mov eax, [eax + 0x30]
mov eax, [eax + 0xc]
mov eax, [eax + 0xc]
mov eax, [eax + 0x18]
}
}
//void NotifyHookInsert()
//{
// if (live)
// {
// BYTE buffer[0x10];
// *(DWORD*)buffer=-1;
// *(DWORD*)(buffer+4)=1;
// IO_STATUS_BLOCK ios;
// NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0);
// }
//}
__declspec(naked) void SafeExit() // Return to eax
{
__asm
{
mov [esp+0x24], eax
popfd
popad
retn
}
}
#if 0
// jichi 12/2/2013: This function mostly return 0.
// But sometimes return the hook address from TextHook::Send
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
{
//with_seh(hook->Send(dwDataBase, dwRetn));
seh_push_(seh_exit, 0, eax, ebx) // jichi 12/13/2013: only eax and ebx are available. ecx and edx are used.
__asm
{
push esi
push edx
call TextHook::UnsafeSend
test eax, eax
jz seh_exit // label in seh_pop
mov ecx, SafeExit
mov [esp + 8], ecx // jichi 12/13/2013: change exit point if Send returns non-zero, not + 8 beause two elements has been pused
}
seh_pop_(seh_exit)
__asm retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
}
#endif // 0
#if 1
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
{
// jichi 12/17/2013: The function parameters here are meaning leass. The parameters are in esi and edi
__asm
{
push esi
push edx
call TextHook::Send
test eax, eax
jz ok // label in seh_pop
mov ecx, SafeExit
mov [esp], ecx // jichi 12/13/2013: change exit point if Send returns non-zero
ok:
retn // jichi 12/13/2013: return near, see: http://stackoverflow.com/questions/1396909/ret-retn-retf-how-to-use-them
}
}
#endif // 1
// jichi 12/13/2013: return if the retn address is within the filter dlls
inline bool HookFilter(DWORD retn)
{
for (DWORD i = 0; ::filter[i].lower; i++)
if (retn > ::filter[i].lower && retn < ::filter[i].upper)
return true;
return false;
}
} // unnamed namespace
// - TextHook methods -
// jichi 12/2/2013: This function mostly return 0.
// It return the hook address only for auxiliary case.
// However, because no known hooks are auxiliary, this function always return 0.
//
// jichi 5/11/2014:
// - dwDataBase: the stack address
// - dwRetn: the return address of the hook
DWORD TextHook::Send(DWORD dwDataBase, DWORD dwRetn)
{
DWORD ret = 0;
//char b[0x100];
//::wcstombs(b, hook_name, 0x100);
//ConsoleOutput(b);
ITH_WITH_SEH(ret = UnsafeSend(dwDataBase, dwRetn));
return ret;
}
DWORD TextHook::UnsafeSend(DWORD dwDataBase, DWORD dwRetn)
{
enum { SMALL_BUFF_SIZE = 0x80 };
enum { MAX_DATA_SIZE = 0x10000 }; // jichi 12/25/2013: The same as the original ITH
DWORD dwCount,
dwAddr,
dwDataIn,
dwSplit;
BYTE *pbData,
pbSmallBuff[SMALL_BUFF_SIZE];
DWORD dwType = hp.type;
if (!live)
return 0;
if ((dwType & NO_CONTEXT) == 0 && HookFilter(dwRetn))
return 0;
// jichi 10/24/2014: Skip GDI functions
if (::gdi_hook_disabled_ && ::IsGDIFunction((LPCVOID)hp.addr))
return 0;
dwAddr = hp.addr;
/** jichi 12/24/2014
* @param addr function address
* @param frame real address of the function, supposed to be the same as addr
* @param stack address of current stack - 4
* @return If success, which is reverted
*/
if (::trigger)
::trigger = Engine::InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
// jichi 10/21/2014: Directly invoke engine functions.
//if (trigger) {
// if (InsertDynamicHook)
// trigger = InsertDynamicHook((LPVOID)dwAddr, *(DWORD *)(dwDataBase - 0x1c), *(DWORD *)(dwDataBase-0x18));
// else
// trigger = 0;
//}
#if 0 // diasble HOOK_AUXILIARY
// jichi 12/13/2013: None of known hooks are auxiliary
if (dwType & HOOK_AUXILIARY) {
//Clean hook when dynamic hook finished.
//AUX hook is only used for a foothold of dynamic hook.
if (!trigger) {
ClearHook();
// jichi 12/13/2013: This is the only place where this function could return non-zero value
// However, I non of the known hooks are auxiliary
return dwAddr;
}
return 0;
}
#endif // 0
// jichi 10/24/2014: generic hook function
if (hp.hook_fun && !hp.hook_fun(dwDataBase, &hp))
hp.hook_fun = nullptr;
if (dwType & HOOK_EMPTY) // jichi 10/24/2014: dummy hook only for dynamic hook
return 0;
// jichi 2/2/2015: Send multiple texts
for (BYTE textIndex = 0; textIndex <= hp.extra_text_count; textIndex++) {
dwCount = 0;
dwSplit = 0;
dwDataIn = *(DWORD *)(dwDataBase + hp.off); // default value
//if (dwType & EXTERN_HOOK) {
if (hp.text_fun) { // jichi 10/24/2014: remove EXTERN_HOOK
//DataFun fun=(DataFun)hp.text_fun;
//auto fun = hp.text_fun;
hp.text_fun(dwDataBase, &hp, textIndex, &dwDataIn, &dwSplit, &dwCount);
//if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
// return 0;
if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
dwSplit -= ::processStartAddress;
} else {
if (dwDataIn == 0)
return 0;
if (dwType & FIXING_SPLIT)
dwSplit = FIXED_SPLIT_VALUE; // fuse all threads, and prevent floating
else if (dwType & USING_SPLIT) {
dwSplit = *(DWORD *)(dwDataBase + hp.split);
if (dwType & SPLIT_INDIRECT) {
if (IthGetMemoryRange((LPVOID)(dwSplit + hp.split_ind), 0, 0))
dwSplit = *(DWORD *)(dwSplit + hp.split_ind);
else
return 0;
}
if (dwSplit && (dwType & RELATIVE_SPLIT) && dwSplit > ::processStartAddress)
dwSplit -= ::processStartAddress;
}
if (dwType & DATA_INDIRECT) {
if (IthGetMemoryRange((LPVOID)(dwDataIn + hp.ind), 0, 0))
dwDataIn = *(DWORD *)(dwDataIn + hp.ind);
else
return 0;
}
//if (dwType & PRINT_DWORD) {
// swprintf((WCHAR *)(pbSmallBuff + HEADER_SIZE), L"%.8X ", dwDataIn);
// dwDataIn = (DWORD)pbSmallBuff + HEADER_SIZE;
//}
dwCount = GetLength(dwDataBase, dwDataIn);
}
// jichi 12/25/2013: validate data size
if (dwCount == 0 || dwCount > MAX_DATA_SIZE)
return 0;
size_t sz = dwCount + HEADER_SIZE;
if (sz >= SMALL_BUFF_SIZE)
pbData = new BYTE[sz];
//ITH_MEMSET_HEAP(pbData, 0, sz * sizeof(BYTE)); // jichi 9/26/2013: zero memory
else
pbData = pbSmallBuff;
if (hp.length_offset == 1) {
if (dwType & STRING_LAST_CHAR) {
LPWSTR ts = (LPWSTR)dwDataIn;
dwDataIn = ts[::wcslen(ts) -1];
}
dwDataIn &= 0xffff;
if ((dwType & BIG_ENDIAN) && (dwDataIn >> 8))
dwDataIn = _byteswap_ushort(dwDataIn & 0xffff);
if (dwCount == 1)
dwDataIn &= 0xff;
*(WORD *)(pbData + HEADER_SIZE) = dwDataIn & 0xffff;
}
else
::memcpy(pbData + HEADER_SIZE, (void *)dwDataIn, dwCount);
// jichi 10/14/2014: Add filter function
if (hp.filter_fun && !hp.filter_fun(pbData + HEADER_SIZE, &dwCount, &hp, textIndex) || dwCount <= 0) {
if (pbData != pbSmallBuff)
delete[] pbData;
return 0;
}
*(DWORD *)pbData = dwAddr;
if (dwType & (NO_CONTEXT|FIXING_SPLIT))
dwRetn = 0;
else if (dwRetn && (dwType & RELATIVE_SPLIT))
dwRetn -= ::processStartAddress;
*((DWORD *)pbData + 1) = dwRetn;
*((DWORD *)pbData + 2) = dwSplit;
if (dwCount) {
IO_STATUS_BLOCK ios = {};
IthCoolDown(); // jichi 9/28/2013: cool down to prevent parallelization in wine
//CliLockPipe();
if (STATUS_PENDING == NtWriteFile(hPipe, 0, 0, 0, &ios, pbData, dwCount + HEADER_SIZE, 0, 0)) {
NtWaitForSingleObject(hPipe, 0, 0);
NtFlushBuffersFile(hPipe, &ios);
}
//CliUnlockPipe();
}
if (pbData != pbSmallBuff)
delete[] pbData;
}
return 0;
}
int TextHook::InsertHook()
{
//ConsoleOutput("vnrcli:InsertHook: enter");
NtWaitForSingleObject(hmMutex, 0, 0);
int ok = InsertHookCode();
IthReleaseMutex(hmMutex);
if (hp.type & HOOK_ADDITIONAL) {
NotifyHookInsert(hp.addr);
//ConsoleOutput(hook_name);
//RegisterHookName(hook_name,hp.addr);
}
//ConsoleOutput("vnrcli:InsertHook: leave");
return ok;
}
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());
//if (ret == no)
// ITH_WARN(L"Failed to insert hook");
return ret;
}
int TextHook::UnsafeInsertHookCode()
{
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: enter");
enum : int { yes = 0, no = 1 };
// MODULE_OFFSET is set, but there's no module address
// this means that this is an absolute address found on Windows 2000/XP
// we make the address relative to the process base
// we also store the original address in the function field because normally there can not
// exist a function address without a module address
if (hp.type & MODULE_OFFSET && !hp.module) {
DWORD base = GetModuleBase();
hp.function = hp.addr;
hp.addr -= 0x400000;
hp.addr += base;
hp.type &= ~MODULE_OFFSET;
}
else if (hp.module && (hp.type & MODULE_OFFSET)) { // Map hook offset to real address.
if (DWORD base = GetModuleBase(hp.module)) {
if (hp.function && (hp.type & FUNCTION_OFFSET)) {
base = GetExportAddress(base, hp.function);
if (base)
hp.addr += base;
else {
current_hook--;
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: function not found in the export table");
return no;
}
}
else {
hp.addr += base;
}
hp.type &= ~(MODULE_OFFSET | FUNCTION_OFFSET);
}
else {
current_hook--;
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: module not present");
return no;
}
}
{
TextHook *it = hookman;
for (int i = 0; (i < current_hook) && it; it++) { // Check if there is a collision.
if (it->Address())
i++;
//it = hookman + i;
if (it == this)
continue;
if (it->Address() <= hp.addr &&
it->Address() + it->Length() > hp.addr) {
it->ClearHook();
break;
}
}
}
// Verify hp.addr.
MEMORY_BASIC_INFORMATION info = {};
NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)hp.addr, MemoryBasicInformation, &info, sizeof(info), nullptr);
if (info.Type & PAGE_NOACCESS) {
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: page no access");
return no;
}
// Initialize common routine.
memcpy(recover, common_hook, sizeof(common_hook));
BYTE *c = (BYTE *)hp.addr,
*r = recover;
BYTE inst[8]; // jichi 9/27/2013: Why 8? Only 5 bytes will be written using NtWriteVirtualMemory
inst[0] = 0xe9; // jichi 9/27/2013: 0xe9 is jump, see: http://code.google.com/p/sexyhook/wiki/SEXYHOOK_Hackers_Manual
__asm
{
mov edx,r // r = recover
mov eax,this
mov [edx+0xa],eax // push TextHook*, resolve to correspond hook.
lea eax,[edx+0x13]
mov edx,ProcessHook
sub edx,eax
mov [eax-4],edx // call ProcessHook
mov eax,c
add eax,5
mov edx,r
sub edx,eax
lea eax,inst+1
mov [eax],edx // jichi 12/17/2013: the parameter of jmp is in edx. So, ProcessHook must be naked.
}
r += sizeof(common_hook);
hp.hook_len = 5;
//bool jmpflag=false; // jichi 9/28/2013: nto used
// Copy original code.
switch (MapInstruction(hp.addr, (DWORD)r, hp.hook_len, hp.recover_len)) {
case -1:
ConsoleOutput("vnrcli:UnsafeInsertHookCode: FAILED: failed to map instruction");
return no;
case 0:
__asm
{
mov ecx,this
movzx eax,[ecx]hp.hook_len
movzx edx,[ecx]hp.recover_len
add edx,[ecx]hp.addr
add eax,r
add eax,5
sub edx,eax
mov [eax-5],0xe9 // jichi 9/27/2013: 0xe9 is jump
mov [eax-4],edx
}
}
// jichi 9/27/2013: Save the original instructions in the memory
memcpy(original, (LPVOID)hp.addr, hp.recover_len);
//Check if the new hook range conflict with existing ones. Clear older if conflict.
{
TextHook *it = hookman;
for (int i = 0; i < current_hook; it++) {
if (it->Address())
i++;
if (it == this)
continue;
if (it->Address() >= hp.addr &&
it->Address() < hp.hook_len + hp.addr) {
it->ClearHook();
break;
}
}
}
// Insert hook and flush instruction cache.
enum {c8 = 0xcccccccc};
DWORD int3[] = {c8, c8};
DWORD t = 0x100,
old,
len;
// jichi 9/27/2013: Overwrite the memory with inst
// See: http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Memory%20Management/Virtual%20Memory/NtProtectVirtualMemory.html
// See: http://doxygen.reactos.org/d8/d6b/ndk_2mmfuncs_8h_af942709e0c57981d84586e74621912cd.html
DWORD addr = hp.addr;
NtProtectVirtualMemory(NtCurrentProcess(), (PVOID *)&addr, &t, PAGE_EXECUTE_READWRITE, &old);
NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.addr, inst, 5, &t);
len = hp.recover_len - 5;
if (len)
NtWriteVirtualMemory(NtCurrentProcess(), (BYTE *)hp.addr + 5, int3, len, &t);
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)::hookman, 0x1000);
//ConsoleOutput("vnrcli:UnsafeInsertHookCode: leave: succeed");
return 0;
}
int TextHook::InitHook(LPVOID addr, DWORD data, DWORD data_ind,
DWORD split_off, DWORD split_ind, WORD type, DWORD len_off)
{
NtWaitForSingleObject(hmMutex, 0, 0);
hp.addr = (DWORD)addr;
hp.off = data;
hp.ind = data_ind;
hp.split = split_off;
hp.split_ind = split_ind;
hp.type = type;
hp.hook_len = 0;
hp.module = 0;
hp.length_offset = len_off & 0xffff;
current_hook++;
if (current_available >= this)
for (current_available = this + 1; current_available->Address(); current_available++);
IthReleaseMutex(hmMutex);
return this - hookman;
}
int TextHook::InitHook(const HookParam &h, LPCWSTR name, WORD set_flag)
{
NtWaitForSingleObject(hmMutex, 0, 0);
hp = h;
hp.type |= set_flag;
if (name && name != hook_name) {
SetHookName(name);
}
current_hook++;
current_available = this+1;
while (current_available->Address())
current_available++;
IthReleaseMutex(hmMutex);
return 1;
}
int TextHook::RemoveHook()
{
enum : int { yes = 1, no = 0 };
if (!hp.addr)
return no;
ConsoleOutput("vnrcli:RemoveHook: enter");
const LONGLONG timeout = -50000000; // jichi 9/28/2012: in 100ns, wait at most for 5 seconds
NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout);
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(NtCurrentProcess(), (LPVOID)hp.addr, original, hp.recover_len, &l);
NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
} ITH_EXCEPT {}
//});
hp.hook_len = 0;
IthReleaseMutex(hmMutex);
ConsoleOutput("vnrcli:RemoveHook: leave");
return yes;
}
int TextHook::ClearHook()
{
NtWaitForSingleObject(hmMutex, 0, 0);
int err = RemoveHook();
if (hook_name) {
delete[] hook_name;
hook_name = nullptr;
}
memset(this, 0, sizeof(TextHook)); // jichi 11/30/2013: This is the original code of ITH
//if (current_available>this)
// current_available = this;
current_hook--;
IthReleaseMutex(hmMutex);
return err;
}
int TextHook::ModifyHook(const HookParam &hp)
{
//WCHAR name[0x40];
DWORD len = 0;
if (hook_name)
len = wcslen(hook_name);
LPWSTR name = 0;
if (len) {
name = new wchar_t[len + 1];
//ITH_MEMSET_HEAP(name, 0, sizeof(wchar_t) * (len + 1)); // jichi 9/26/2013: zero memory
name[len] = 0;
wcscpy(name, hook_name);
}
ClearHook();
InitHook(hp,name);
InsertHook();
if (name)
delete[] name;
return 0;
}
int TextHook::RecoverHook()
{
if (hp.addr) {
// jichi 9/28/2013: Only enable TextOutA to debug Cross Channel
//if (hp.addr == (DWORD)TextOutA)
InsertHook();
return 1;
}
return 0;
}
int TextHook::SetHookName(LPCWSTR name)
{
name_length = wcslen(name) + 1;
if (hook_name)
delete[] hook_name;
hook_name = new wchar_t[name_length];
//ITH_MEMSET_HEAP(hook_name, 0, sizeof(wchar_t) * name_length); // jichi 9/26/2013: zero memory
hook_name[name_length - 1] = 0;
wcscpy(hook_name, name);
return 0;
}
int TextHook::GetLength(DWORD base, DWORD in)
{
if (base == 0)
return 0;
int len;
switch (hp.length_offset) {
default: // jichi 12/26/2013: I should not put this default branch to the end
len = *((int *)base + hp.length_offset);
if (len >= 0) {
if (hp.type & USING_UNICODE)
len <<= 1;
break;
}
else if (len != -1)
break;
//len == -1 then continue to case 0.
case 0:
if (hp.type & USING_UNICODE)
len = wcslen((const wchar_t *)in) << 1;
else
len = strlen((const char *)in);
break;
case 1:
if (hp.type & USING_UNICODE)
len = 2;
else {
if (hp.type & BIG_ENDIAN)
in >>= 8;
len = LeadByteTable[in & 0xff]; //Slightly faster than IsDBCSLeadByte
}
break;
}
// jichi 12/25/2013: This function originally return -1 if failed
//return len;
return max(0, len);
}
// EOF
//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
/*
DWORD recv_esp, recv_addr;
EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
{
//WCHAR str[0x40],
// name[0x100];
//ConsoleOutput(L"Exception raised during hook processing.");
//swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
//ConsoleOutput(str);
//MEMORY_BASIC_INFORMATION info;
//if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemoryBasicInformation,&info,sizeof(info),0)) &&
// NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemorySectionName,name,0x200,0))) {
// swprintf(str, L"Exception offset: 0x%.8X:%s",
// ContextRecord->Eip-(DWORD)info.AllocationBase,
// wcsrchr(name,L'\\')+1);
// ConsoleOutput(str);
//}
ContextRecord->Esp = recv_esp;
ContextRecord->Eip = recv_addr;
return ExceptionContinueExecution;
}
//typedef void (*DataFun)(DWORD, const HookParam*, DWORD*, DWORD*, DWORD*);
DWORD recv_esp, recv_addr;
EXCEPTION_DISPOSITION ExceptHandler(EXCEPTION_RECORD *ExceptionRecord,
void *EstablisherFrame, CONTEXT *ContextRecord, void *DispatcherContext)
{
//WCHAR str[0x40],
// name[0x100];
//ConsoleOutput(L"Exception raised during hook processing.");
//swprintf(str, L"Exception code: 0x%.8X", ExceptionRecord->ExceptionCode);
//ConsoleOutput(str);
//MEMORY_BASIC_INFORMATION info;
//if (NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemoryBasicInformation,&info,sizeof(info),0)) &&
// NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(),(PVOID)ContextRecord->Eip,
// MemorySectionName,name,0x200,0))) {
// swprintf(str, L"Exception offset: 0x%.8X:%s",
// ContextRecord->Eip-(DWORD)info.AllocationBase,
// wcsrchr(name,L'\\')+1);
// ConsoleOutput(str);
//}
ContextRecord->Esp = recv_esp;
ContextRecord->Eip = recv_addr;
return ExceptionContinueExecution;
}
__declspec(naked) // jichi 10/2/2013: No prolog and epilog
int ProcessHook(DWORD dwDataBase, DWORD dwRetn, TextHook *hook) // Use SEH to ensure normal execution even bad hook inserted.
{
__asm
{
mov eax,seh_recover
mov recv_addr,eax
push ExceptHandler
push fs:[0]
mov recv_esp,esp
mov fs:[0],esp
push esi
push edx
call TextHook::Send
test eax,eax
jz seh_recover
mov ecx,SafeExit
mov [esp + 0x8], ecx // change exit point
seh_recover:
pop dword ptr fs:[0]
pop ecx
retn
}
}
*/
#pragma once
// hook.h
// 8/23/2013 jichi
// Branch: ITH/IHF_DLL.h, rev 66
#include "ith/common/const.h"
#include "ith/common/types.h"
//#ifdef IHF
//# define IHFAPI __declspec(dllexport) __stdcall
//#else
//# define IHFAPI __declspec(dllimport) __stdcall
//#endif // IHF
#define IHFAPI // 9/19/2014 jichi: dummy
//extern "C" {
//DWORD IHFAPI OutputConsole(LPCWSTR text);
void IHFAPI ConsoleOutput(LPCSTR text); // jichi 12/25/2013: Used to return length of sent text
//DWORD IHFAPI OutputDWORD(DWORD d);
//DWORD IHFAPI OutputRegister(DWORD *base);
DWORD IHFAPI NotifyHookInsert(DWORD addr);
DWORD IHFAPI NewHook(const HookParam &hp, LPCWSTR name, DWORD flag = HOOK_ENGINE);
DWORD IHFAPI RemoveHook(DWORD addr);
DWORD IHFAPI SwitchTrigger(DWORD on);
DWORD IHFAPI GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name);
//DWORD IHFAPI RegisterEngineModule(DWORD idEngine, DWORD dnHook);
//} // extern "C"
// 10/21/2014 jichi: TODO: Get rid of this global variable
// Defined in pipe.cc
extern bool engine_registered;
// 10/14/2014 jichi: disable GDI hooks
void DisableGDIHooks();
// EOF
# hook.pro
# 8/9/2013 jichi
# Build vnrhook.dll for Windows 7+
CONFIG += eh eha # exception handler to catch all exceptions
#CONFIG += noeh # msvcrt on Windows XP does not has exception handler
include(../dllconfig.pri)
include(../sys/sys.pri)
include($$LIBDIR/disasm/disasm.pri)
include($$LIBDIR/memdbg/memdbg.pri)
include($$LIBDIR/ntinspect/ntinspect.pri)
#include($$LIBDIR/winseh/winseh_safe.pri)
include($$LIBDIR/winversion/winversion.pri)
# 9/27/2013: disable ITH this game engine, only for debugging purpose
#DEFINES += ITH_DISABLE_ENGINE
# jichi 9/22/2013: When ITH is on wine, mutex is needed to protect NtWriteFile
#DEFINES += ITH_WINE
#DEFINES += ITH_SYNC_PIPE
## Libraries
LIBS += -lkernel32 -luser32 -lgdi32
## Sources
TEMPLATE = lib
TARGET = vnrhook
#CONFIG += staticlib
HEADERS += \
config.h \
cli.h \
hook.h \
engine/engine.h \
engine/hookdefs.h \
engine/match.h \
engine/pchooks.h \
engine/util.h \
tree/avl.h
SOURCES += \
main.cc \
rpc/pipe.cc \
hijack/texthook.cc \
engine/engine.cc \
engine/match.cc \
engine/pchooks.cc \
engine/util.cc
#RC_FILE += vnrhook.rc
#OTHER_FILES += vnrhook.rc
# EOF
// main.cc
// 8/24/2013 jichi
// Branch: ITH_DLL/main.cpp, rev 128
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
//# pragma warning (disable:4733) // C4733: Inline asm assigning to 'FS:0' : handler not registered as safe handler
#endif // _MSC_VER
#include "cli.h"
#include "tree/avl.h"
#include "engine/match.h"
#include "ith/common/const.h"
#include "ith/common/defs.h"
#include "ith/common/except.h"
//#include "ith/common/growl.h"
#include "ith/sys/sys.h"
#include "ccutil/ccmacro.h"
//#include "ntinspect/ntinspect.h"
//#include "winseh/winseh.h"
//#include <boost/foreach.hpp>
//#include "md5.h"
//#include <ITH\AVL.h>
//#include <ITH\ntdll.h>
// Global variables
// jichi 6/3/2014: memory range of the current module
DWORD processStartAddress,
processStopAddress;
namespace { // unnamed
wchar_t processName[MAX_PATH];
inline void GetProcessName(wchar_t *name)
{
//assert(name);
PLDR_DATA_TABLE_ENTRY it;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
}
wcscpy(name, it->BaseDllName.Buffer);
}
} // unmaed namespace
enum { HOOK_BUFFER_SIZE = MAX_HOOK * sizeof(TextHook) };
//#define MAX_HOOK (HOOK_BUFFER_SIZE/sizeof(TextHook))
DWORD hook_buff_len = HOOK_BUFFER_SIZE;
namespace { FilterRange _filter[IHF_FILTER_CAPACITY]; }
FilterRange *filter = _filter;
WCHAR dll_mutex[0x100];
//WCHAR dll_name[0x100];
WCHAR hm_mutex[0x100];
WCHAR hm_section[0x100];
HINSTANCE hDLL;
HANDLE hSection;
bool running,
live = false;
int current_hook = 0,
user_hook_count = 0;
DWORD trigger = 0;
HANDLE
hFile,
hMutex,
hmMutex;
//DWORD current_process_id;
extern DWORD enter_count;
//extern LPWSTR current_dir;
extern DWORD engine_type;
extern DWORD module_base;
AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN> *tree;
namespace { // unnamed
void AddModule(DWORD hModule, DWORD size, LPWSTR name)
{
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
IMAGE_EXPORT_DIRECTORY *ExtDir;
UINT uj;
FunctionInfo info = {0, hModule, size, name};
char *pcFuncPtr, *pcBuffer;
DWORD dwReadAddr, dwFuncName, dwExportAddr;
WORD wOrd;
DosHdr = (IMAGE_DOS_HEADER *)hModule;
if (IMAGE_DOS_SIGNATURE==DosHdr->e_magic) {
dwReadAddr = hModule + DosHdr->e_lfanew;
NtHdr = (IMAGE_NT_HEADERS *)dwReadAddr;
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
dwExportAddr = NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
if (dwExportAddr == 0)
return;
dwExportAddr+=hModule;
ExtDir=(IMAGE_EXPORT_DIRECTORY*)dwExportAddr;
dwExportAddr=hModule+ExtDir->AddressOfNames;
for (uj = 0; uj < ExtDir->NumberOfNames; uj++) {
dwFuncName=*(DWORD*)dwExportAddr;
pcBuffer = (char *)(hModule+dwFuncName);
pcFuncPtr=(char *)(hModule+(DWORD)ExtDir->AddressOfNameOrdinals+(uj*sizeof(WORD)));
wOrd = *(WORD *)pcFuncPtr;
pcFuncPtr = (char *)(hModule+(DWORD)ExtDir->AddressOfFunctions+(wOrd*sizeof(DWORD)));
info.addr=hModule+*(DWORD*)pcFuncPtr;
::tree->Insert(pcBuffer,info);
dwExportAddr+=sizeof(DWORD);
}
}
}
}
void GetFunctionNames()
{
// jichi 9/26/2013: AVLTree is already zero
PPEB ppeb;
__asm {
mov eax, fs:[0x30]
mov ppeb, eax
}
DWORD temp = *(DWORD *)(&ppeb->Ldr->InLoadOrderModuleList);
PLDR_DATA_TABLE_ENTRY it = (PLDR_DATA_TABLE_ENTRY)temp;
while (it->SizeOfImage) {
AddModule((DWORD)it->DllBase, it->SizeOfImage, it->BaseDllName.Buffer);
it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink;
if (*(DWORD *)it == temp)
break;
}
}
void RequestRefreshProfile()
{
if (::live) {
BYTE buffer[0x80] = {}; // 11/14/2013: reset to zero. Shouldn't it be 0x8 instead of 0x80?
*(DWORD *)buffer = -1;
*(DWORD *)(buffer + 4) = 1;
*(DWORD *)(buffer + 8) = 0;
IO_STATUS_BLOCK ios;
CliLockPipe();
NtWriteFile(hPipe, 0, 0, 0, &ios, buffer, HEADER_SIZE, 0, 0);
CliUnlockPipe();
}
}
} // unnamed namespace
DWORD IHFAPI GetFunctionAddr(const char *name, DWORD *addr, DWORD *base, DWORD *size, LPWSTR *base_name)
{
TreeNode<char *,FunctionInfo> *node = ::tree->Search(name);
if (node) {
if (addr) *addr = node->data.addr;
if (base) *base = node->data.module;
if (size) *size = node->data.size;
if (base_name) *base_name = node->data.name;
return TRUE;
}
else
return FALSE;
}
BOOL WINAPI DllMain(HINSTANCE hModule, DWORD fdwReason, LPVOID lpReserved)
{
static HANDLE hSendThread,
hCmdThread,
hEngineThread;
CC_UNUSED(lpReserved);
//static WCHAR dll_exist[] = L"ITH_DLL_RUNNING";
static WCHAR dll_exist[] = ITH_CLIENT_MUTEX;
static HANDLE hDllExist;
// jichi 9/23/2013: wine deficenciy on mapping sections
// Whe set to false, do not map sections.
//static bool ith_has_section = true;
switch (fdwReason) {
case DLL_PROCESS_ATTACH:
{
LdrDisableThreadCalloutsForDll(hModule);
//IthBreak();
::module_base = (DWORD)hModule;
IthInitSystemService();
swprintf(hm_section, ITH_SECTION_ L"%d", current_process_id);
// jichi 9/25/2013: Interprocedural communication with vnrsrv.
hSection = IthCreateSection(hm_section, HOOK_SECTION_SIZE, PAGE_EXECUTE_READWRITE);
::hookman = nullptr;
NtMapViewOfSection(hSection, NtCurrentProcess(),
(LPVOID *)&::hookman, 0, hook_buff_len, 0, &hook_buff_len, ViewUnmap, 0,
PAGE_EXECUTE_READWRITE);
//PAGE_EXECUTE_READWRITE);
GetProcessName(::processName);
FillRange(::processName, &::processStartAddress, &::processStopAddress);
//NtInspect::getCurrentMemoryRange(&::processStartAddress, &::processStopAddress);
//if (!::hookman) {
// ith_has_section = false;
// ::hookman = new TextHook[MAX_HOOK];
// memset(::hookman, 0, MAX_HOOK * sizeof(TextHook));
//}
//LPCWSTR p;
//for (p = GetMainModulePath(); *p; p++);
//for (p = p; *p != L'\\'; p--);
//wcscpy(dll_name, p + 1);
//swprintf(dll_mutex,L"ITH_%.4d_%s",current_process_id,current_dir);
swprintf(dll_mutex, ITH_PROCESS_MUTEX_ L"%d", current_process_id);
swprintf(hm_mutex, ITH_HOOKMAN_MUTEX_ L"%d", current_process_id);
hmMutex = IthCreateMutex(hm_mutex, FALSE);
DWORD s;
hMutex = IthCreateMutex(dll_mutex, TRUE, &s); // jichi 9/18/2013: own is true
if (s)
return FALSE;
hDllExist = IthCreateMutex(dll_exist, 0);
hDLL = hModule;
::running = true;
::current_available = ::hookman;
::tree = new AVLTree<char, FunctionInfo, SCMP, SCPY, SLEN>;
GetFunctionNames();
InitFilterTable();
//InitDefaultHook(); // jichi 7/17/2014: Disabled by default
hSendThread = IthCreateThread(WaitForPipe, 0);
hCmdThread = IthCreateThread(CommandPipe, 0);
hEngineThread = IthCreateThread(Engine::match, 0);
}
break;
case DLL_PROCESS_DETACH:
{
// jichi 10/2/2103: Cannot use __try in functions that require object unwinding
//ITH_TRY {
::running = false;
::live = false;
const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
if (hEngineThread) {
NtWaitForSingleObject(hEngineThread, 0, (PLARGE_INTEGER)&timeout);
NtClose(hEngineThread);
}
if (hSendThread) {
NtWaitForSingleObject(hSendThread, 0, (PLARGE_INTEGER)&timeout);
NtClose(hSendThread);
}
if (hCmdThread) {
NtWaitForSingleObject(hCmdThread, 0, (PLARGE_INTEGER)&timeout);
NtClose(hCmdThread);
}
for (TextHook *man = ::hookman; man->RemoveHook(); man++);
//LARGE_INTEGER lint = {-10000, -1};
while (::enter_count)
IthSleep(1); // jichi 9/28/2013: sleep for 1 ms
//NtDelayExecution(0, &lint);
for (TextHook *man = ::hookman; man < ::hookman + MAX_HOOK; man++)
man->ClearHook();
//if (ith_has_section)
NtUnmapViewOfSection(NtCurrentProcess(), ::hookman);
//else
// delete[] ::hookman;
NtClose(hSection);
NtClose(hMutex);
delete ::tree;
IthCloseSystemService();
NtClose(hmMutex);
NtClose(hDllExist);
//} ITH_EXCEPT {}
} break;
}
return TRUE;
}
//extern "C" {
DWORD IHFAPI NewHook(const HookParam &hp, LPCWSTR name, DWORD flag)
{
WCHAR str[128];
int current = ::current_available - ::hookman;
if (current < MAX_HOOK) {
//flag &= 0xffff;
//if ((flag & HOOK_AUXILIARY) == 0)
flag |= HOOK_ADDITIONAL;
if (name == NULL || name[0] == '\0')
{
swprintf(str, L"UserHook%d", user_hook_count++);
}
else
{
wcscpy(str, name);
}
ConsoleOutput("vnrcli:NewHook: try inserting hook");
// jichi 7/13/2014: This function would raise when too many hooks added
::hookman[current].InitHook(hp, str, flag & 0xffff);
if (::hookman[current].InsertHook() == 0) {
ConsoleOutput("vnrcli:NewHook: hook inserted");
//ConsoleOutputW(name);
//swprintf(str,L"Insert address 0x%.8X.", hookman[current].Address());
RequestRefreshProfile();
} else
ConsoleOutput("vnrcli:NewHook:WARNING: failed to insert hook");
}
return 0;
}
DWORD IHFAPI RemoveHook(DWORD addr)
{
for (int i = 0; i < MAX_HOOK; i++)
if (::hookman[i].Address ()== addr) {
::hookman[i].ClearHook();
return 0;
}
return 0;
}
DWORD IHFAPI SwitchTrigger(DWORD t)
{
trigger = t;
return 0;
}
//} // extern "C"
namespace { // unnamed
BOOL SafeFillRange(LPCWSTR dll, DWORD *lower, DWORD *upper)
{
BOOL ret = FALSE;
ITH_WITH_SEH(ret = FillRange(dll, lower, upper));
return ret;
}
} // unnamed namespace
// jichi 12/13/2013
// Use listdlls from SystemInternals
void InitFilterTable()
{
LPCWSTR l[] = { IHF_FILTER_DLL_LIST };
enum { capacity = sizeof(l)/sizeof(*l) };
size_t count = 0;
//for (auto p : l)
for (size_t i = 0; i < capacity; i++)
if (SafeFillRange(l[i], &::filter[count].lower, &::filter[count].upper))
count++;
}
// EOF
/*
static DWORD recv_esp, recv_addr;
static CONTEXT recover_context;
static __declspec(naked) void MySEH()
{
__asm{
mov eax, [esp+0xC]
mov edi,eax
mov ecx,0xB3
mov esi, offset recover_context
rep movs
mov ecx, [recv_esp]
mov [eax+0xC4],ecx
mov edx, [recv_addr]
mov [eax+0xB8],edx
xor eax,eax
retn
}
}
EXCEPTION_DISPOSITION ExceptHandler(
EXCEPTION_RECORD *ExceptionRecord,
void * EstablisherFrame,
CONTEXT *ContextRecord,
void * DispatcherContext )
{
ContextRecord->Esp=recv_esp;
ContextRecord->Eip=recv_addr;
return ExceptionContinueExecution;
}
int GuardRange(LPWSTR module, DWORD *a, DWORD *b)
{
int flag=0;
__asm
{
mov eax,seh_recover
mov recv_addr,eax
push ExceptHandler
push fs:[0]
mov recv_esp,esp
mov fs:[0],esp
}
flag = FillRange(module, a, b);
__asm
{
seh_recover:
mov eax,[esp]
mov fs:[0],eax
add esp,8
}
return flag;
}
*/
// pipe.cc
// 8/24/2013 jichi
// Branch: ITH_DLL/pipe.cpp, rev 66
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
#endif // _MSC_VER
#include "cli.h"
#include "engine/match.h"
#include "ith/common/defs.h"
//#include "ith/common/growl.h"
#include "ith/sys/sys.h"
#include "ccutil/ccmacro.h"
//#include <ITH\AVL.h>
//#include <ITH\ntdll.h>
WCHAR mutex[] = ITH_GRANTPIPE_MUTEX;
WCHAR exist[] = ITH_PIPEEXISTS_EVENT;
WCHAR detach_mutex[0x20];
//WCHAR write_event[0x20];
//WCHAR engine_event[0x20];
//WCHAR recv_pipe[] = L"\\??\\pipe\\ITH_PIPE";
//WCHAR command[] = L"\\??\\pipe\\ITH_COMMAND";
wchar_t recv_pipe[] = ITH_TEXT_PIPE;
wchar_t command[] = ITH_COMMAND_PIPE;
LARGE_INTEGER wait_time = {-100*10000, -1};
LARGE_INTEGER sleep_time = {-20*10000, -1};
DWORD engine_type;
DWORD module_base;
//DWORD engine_base;
bool engine_registered; // 10/19/2014 jichi: disable engine dll
HANDLE hPipe,
hCommand,
hDetach; //,hLose;
//InsertHookFun InsertHook;
//IdentifyEngineFun IdentifyEngine;
//InsertDynamicHookFun InsertDynamicHook;
bool hook_inserted = false;
// jichi 9/28/2013: protect pipe on wine
// Put the definition in this file so that it might be inlined
void CliUnlockPipe()
{
if (IthIsWine())
IthReleaseMutex(::hmMutex);
}
void CliLockPipe()
{
if (IthIsWine()) {
const LONGLONG timeout = -50000000; // in nanoseconds = 5 seconds
NtWaitForSingleObject(hmMutex, 0, (PLARGE_INTEGER)&timeout);
}
}
HANDLE IthOpenPipe(LPWSTR name, ACCESS_MASK direction)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us,name);
SECURITY_DESCRIPTOR sd = {1};
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
if (NT_SUCCESS(NtCreateFile(&hFile, direction, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0)))
return hFile;
else
return INVALID_HANDLE_VALUE;
}
DWORD WINAPI WaitForPipe(LPVOID lpThreadParameter) // Dynamically detect ITH main module status.
{
CC_UNUSED(lpThreadParameter);
int i;
TextHook *man;
struct {
DWORD pid;
TextHook *man;
DWORD module;
//DWORD engine;
} u;
HANDLE hMutex,
hPipeExist;
//swprintf(engine_event,L"ITH_ENGINE_%d",current_process_id);
swprintf(detach_mutex, ITH_DETACH_MUTEX_ L"%d", current_process_id);
//swprintf(lose_event,L"ITH_LOSEPIPE_%d",current_process_id);
//hEngine=IthCreateEvent(engine_event);
//NtWaitForSingleObject(hEngine,0,0);
//NtClose(hEngine);
while (!engine_registered)
NtDelayExecution(0, &wait_time);
//LoadEngine(L"ITH_Engine.dll");
u.module = module_base;
u.pid = current_process_id;
u.man = hookman;
//u.engine = engine_base; // jichi 10/19/2014: disable the second dll
hPipeExist = IthOpenEvent(exist);
IO_STATUS_BLOCK ios;
//hLose=IthCreateEvent(lose_event,0,0);
if (hPipeExist != INVALID_HANDLE_VALUE)
while (running) {
hPipe = INVALID_HANDLE_VALUE;
hCommand = INVALID_HANDLE_VALUE;
while (NtWaitForSingleObject(hPipeExist,0,&wait_time) == WAIT_TIMEOUT)
if (!running)
goto _release;
hMutex = IthCreateMutex(mutex,0);
NtWaitForSingleObject(hMutex,0,0);
while (hPipe == INVALID_HANDLE_VALUE||
hCommand == INVALID_HANDLE_VALUE) {
NtDelayExecution(0, &sleep_time);
if (hPipe == INVALID_HANDLE_VALUE)
hPipe = IthOpenPipe(recv_pipe, GENERIC_WRITE);
if (hCommand == INVALID_HANDLE_VALUE)
hCommand = IthOpenPipe(command, GENERIC_READ);
}
//NtClearEvent(hLose);
CliLockPipe();
NtWriteFile(hPipe, 0, 0, 0, &ios, &u, sizeof(u), 0, 0);
CliUnlockPipe();
live = true;
for (man = hookman, i = 0; i < current_hook; man++)
if (man->RecoverHook()) // jichi 9/27/2013: This is the place where built-in hooks like TextOutA are inserted
i++;
//ConsoleOutput(dll_name);
ConsoleOutput("vnrcli:WaitForPipe: pipe connected");
//OutputDWORD(tree->Count());
NtReleaseMutant(hMutex,0);
NtClose(hMutex);
if (!hook_inserted && engine_registered) {
hook_inserted = true;
Engine::IdentifyEngine();
}
hDetach = IthCreateMutex(detach_mutex,1);
while (running && NtWaitForSingleObject(hPipeExist, 0, &sleep_time) == WAIT_OBJECT_0)
NtDelayExecution(0, &sleep_time);
live = false;
for (man = hookman, i = 0; i < current_hook; man++)
if (man->RemoveHook())
i++;
if (!running) {
IthCoolDown(); // jichi 9/28/2013: Use cooldown instead of lock pipe to prevent from hanging on exit
//CliLockPipe();
NtWriteFile(hPipe, 0, 0, 0, &ios, man, 4, 0, 0);
//CliUnlockPipe();
IthReleaseMutex(hDetach);
}
NtClose(hDetach);
NtClose(hPipe);
}
_release:
//NtClose(hLose);
NtClose(hPipeExist);
return 0;
}
DWORD WINAPI CommandPipe(LPVOID lpThreadParameter)
{
CC_UNUSED(lpThreadParameter);
DWORD command;
BYTE buff[0x400] = {};
HANDLE hPipeExist;
hPipeExist = IthOpenEvent(exist);
IO_STATUS_BLOCK ios={};
if (hPipeExist!=INVALID_HANDLE_VALUE)
while (running) {
while (!live) {
if (!running)
goto _detach;
NtDelayExecution(0, &sleep_time);
}
// jichi 9/27/2013: Why 0x200 not 0x400? wchar_t?
switch (NtReadFile(hCommand, 0, 0, 0, &ios, buff, 0x200, 0, 0)) {
case STATUS_PIPE_BROKEN:
case STATUS_PIPE_DISCONNECTED:
NtClearEvent(hPipeExist);
continue;
case STATUS_PENDING:
NtWaitForSingleObject(hCommand, 0, 0);
switch (ios.Status) {
case STATUS_PIPE_BROKEN:
case STATUS_PIPE_DISCONNECTED:
NtClearEvent(hPipeExist);
continue;
case 0: break;
default:
if (NtWaitForSingleObject(hDetach, 0, &wait_time) == WAIT_OBJECT_0)
goto _detach;
}
}
if (ios.uInformation && live) {
command = *(DWORD *)buff;
switch(command) {
case IHF_COMMAND_NEW_HOOK:
//IthBreak();
buff[ios.uInformation] = 0;
buff[ios.uInformation + 1] = 0;
NewHook(*(HookParam *)(buff + 4), (LPWSTR)(buff + 4 + sizeof(HookParam)), 0);
break;
case IHF_COMMAND_REMOVE_HOOK:
{
DWORD rm_addr = *(DWORD *)(buff+4);
HANDLE hRemoved = IthOpenEvent(ITH_REMOVEHOOK_EVENT);
TextHook *in = hookman;
for (int i = 0; i < current_hook; in++) {
if (in->Address()) i++;
if (in->Address() == rm_addr) break;
}
if (in->Address())
in->ClearHook();
IthSetEvent(hRemoved);
NtClose(hRemoved);
} break;
case IHF_COMMAND_MODIFY_HOOK:
{
DWORD rm_addr = *(DWORD *)(buff + 4);
HANDLE hModify = IthOpenEvent(ITH_MODIFYHOOK_EVENT);
TextHook *in = hookman;
for (int i = 0; i < current_hook; in++) {
if (in->Address())
i++;
if (in->Address() == rm_addr)
break;
}
if (in->Address())
in->ModifyHook(*(HookParam *)(buff + 4));
IthSetEvent(hModify);
NtClose(hModify);
} break;
case IHF_COMMAND_DETACH:
running = false;
live = false;
goto _detach;
default: ;
}
}
}
_detach:
NtClose(hPipeExist);
NtClose(hCommand);
return 0;
}
//extern "C" {
void IHFAPI ConsoleOutput(LPCSTR text)
{ // jichi 12/25/2013: Rewrite the implementation
if (!live || !text)
return;
enum { buf_size = 0x50 };
BYTE buf[buf_size]; // buffer is needed to append the message header
size_t text_size = strlen(text) + 1;
size_t data_size = text_size + 8;
BYTE *data = (data_size <= buf_size) ? buf : new BYTE[data_size];
*(DWORD *)data = IHF_NOTIFICATION; //cmd
*(DWORD *)(data + 4) = IHF_NOTIFICATION_TEXT; //console
memcpy(data + 8, text, text_size);
IO_STATUS_BLOCK ios;
NtWriteFile(hPipe, 0, 0, 0, &ios, data, data_size, 0, 0);
if (data != buf)
delete[] data;
}
//if (str) {
// int t, len, sum;
// BYTE buffer[0x80];
// BYTE *buff;
// len = wcslen(str) << 1;
// t = swprintf((LPWSTR)(buffer + 8),L"%d: ",current_process_id) << 1;
// sum = len + t + 8;
// if (sum > 0x80) {
// buff = new BYTE[sum];
// memset(buff, 0, sum); // jichi 9/25/2013: zero memory
// memcpy(buff + 8, buffer + 8, t);
// }
// else
// buff = buffer;
// *(DWORD *)buff = IHF_NOTIFICATION; //cmd
// *(DWORD *)(buff + 4) = IHF_NOTIFICATION_TEXT; //console
// memcpy(buff + t + 8, str, len);
// IO_STATUS_BLOCK ios;
// NtWriteFile(hPipe,0,0,0,&ios,buff,sum,0,0);
// if (buff != buffer)
// delete[] buff;
// return len;
//}
//DWORD IHFAPI OutputDWORD(DWORD d)
//{
// WCHAR str[0x10];
// swprintf(str,L"%.8X",d);
// ConsoleOutput(str);
// return 0;
//}
//DWORD IHFAPI OutputRegister(DWORD *base)
//{
// WCHAR str[0x40];
// swprintf(str,L"EAX:%.8X",base[0]);
// ConsoleOutput(str);
// swprintf(str,L"ECX:%.8X",base[-1]);
// ConsoleOutput(str);
// swprintf(str,L"EDX:%.8X",base[-2]);
// ConsoleOutput(str);
// swprintf(str,L"EBX:%.8X",base[-3]);
// ConsoleOutput(str);
// swprintf(str,L"ESP:%.8X",base[-4]);
// ConsoleOutput(str);
// swprintf(str,L"EBP:%.8X",base[-5]);
// ConsoleOutput(str);
// swprintf(str,L"ESI:%.8X",base[-6]);
// ConsoleOutput(str);
// swprintf(str,L"EDI:%.8X",base[-7]);
// ConsoleOutput(str);
// return 0;
//}
//DWORD IHFAPI RegisterEngineModule(DWORD idEngine, DWORD dnHook)
//{
// ::IdentifyEngine = (IdentifyEngineFun)idEngine;
// ::InsertDynamicHook = (InsertDynamicHookFun)dnHook;
// ::engine_registered = true;
// return 0;
//}
DWORD IHFAPI NotifyHookInsert(DWORD addr)
{
if (live) {
BYTE buffer[0x10];
*(DWORD *)buffer = IHF_NOTIFICATION;
*(DWORD *)(buffer + 4) = IHF_NOTIFICATION_NEWHOOK;
*(DWORD *)(buffer + 8) = addr;
*(DWORD *)(buffer + 0xc) = 0;
IO_STATUS_BLOCK ios;
CliLockPipe();
NtWriteFile(hPipe,0,0,0,&ios,buffer,0x10,0,0);
CliUnlockPipe();
}
return 0;
}
//} // extern "C"
// EOF
#pragma once
// avl.h
// 8/23/2013 jichi
// Branch: ITH/AVL.h, rev 133
// 8/24/2013 TODO: Clean up this file
#include "config.h"
enum { STACK_SIZE = 32 };
//#ifndef ITH_STACK
//#define ITH_STACK
template<class T, int stack_size>
class MyStack
{
int index;
T s[stack_size];
public:
MyStack(): index(0)
{ ITH_MEMSET_HEAP(s, 0, sizeof(s)); } // jichi 9/21/2013: assume T is atomic type
T &back() { return s[index-1]; }
int size() { return index; }
void push_back(const T &e)
{
if (index < stack_size)
s[index++]=e;
}
void pop_back() { index--; }
T &operator[](int i) { return s[i]; }
};
//#endif // ITH_STACK
// jichi 9/22/2013: T must be a pointer type which can be deleted
template <class T, class D>
struct TreeNode
{
//typedef TreeNode<T, D> Self;
TreeNode() :
Left(nullptr), Right(nullptr), Parent(nullptr)
, rank(1)
, factor('\0'), reserve('\0')
//, key()
//, data()
{
ITH_MEMSET_HEAP(&key, 0, sizeof(key)); // jichi 9/26/2013: zero memory
ITH_MEMSET_HEAP(&data, 0, sizeof(data)); // jichi 9/26/2013: zero memory
}
TreeNode(const T &k, const D &d) :
Left(nullptr), Right(nullptr), Parent(nullptr)
, rank(1)
, factor('\0'), reserve('\0') // jichi 9/21/2013: zero reserve
, key(k)
, data(d)
{}
TreeNode *Successor()
{
TreeNode *Node,
*ParentNode;
Node = Right;
if (!Node) {
Node = this;
for (;;) {
ParentNode = Node->Parent;
if (!ParentNode)
return nullptr;
if (ParentNode->Left == Node)
break;
Node = ParentNode;
}
return ParentNode;
}
else
while (Node->Left)
Node = Node->Left;
return Node;
}
TreeNode *Predecessor()
{
TreeNode *Node,
*ParentNode;
Node = Left;
if (!Node) {
Node = this;
for(;;) {
ParentNode = Node->Parent;
if (!ParentNode)
return nullptr;
if (ParentNode->Right == Node)
break;
Node = ParentNode;
}
return ParentNode;
}
else
while (Node->Right)
Node = Node->Right;
return Node;
}
int height()
{
if (!this) // jichi 9/26/2013: what?!
return 0;
int l = Left->height(),
r = Right->height(),
f = factor;
if (l - r + f != 0)
__debugbreak();
f = l > r ? l : r;
return f + 1;
}
TreeNode *Left,
*Right,
*Parent;
unsigned short rank;
char factor,
reserve;
T key;
D data;
};
template<class T,class D>
struct NodePath
{
NodePath() { memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH
NodePath(TreeNode<T,D> *n, int f): Node(n), fact(f) {}
TreeNode<T,D> *Node;
union { char factor; int fact; };
};
template <class T, class D, class fComp, class fCopy, class fLength>
class AVLTree
{
fComp fCmp;
fCopy fCpy;
fLength fLen;
protected:
TreeNode<T*, D> head;
public:
// - Construction -
AVLTree() {}
virtual ~AVLTree() { DeleteAll(); }
// - Properties -
TreeNode<T*, D> *TreeRoot() const { return head.Left; }
// - Actions -
void DeleteAll()
{
while (head.Left)
DeleteRoot();
}
TreeNode<T*, D> *Insert(const T *key, const D &data)
{
if (head.Left) {
MyStack<TreeNode<T*, D> *,STACK_SIZE> path;
TreeNode<T*,D> *DownNode, *ParentNode, *BalanceNode, *TryNode, *NewNode; //P,T,S,Q
ParentNode = &head;
path.push_back(ParentNode);
char factor,f;
BalanceNode = DownNode = head.Left;
for (;;) { //The first part of AVL tree insert. Just do as binary tree insert routine and record some nodes.
factor = fCmp(key,DownNode->key);
if (factor == 0)
return DownNode; //Duplicate key. Return and do nothing.
TryNode = _FactorLink(DownNode, factor);
if (factor == -1)
path.push_back(DownNode);
if (TryNode) { //DownNode has a child.
if (TryNode->factor != 0) { //Keep track of unbalance node and its parent.
ParentNode = DownNode;
BalanceNode = TryNode;
}
DownNode = TryNode;
}
else
break; //Finished binary tree search;
}
while (path.size()) {
path.back()->rank++;
path.pop_back();
}
size_t sz = fLen(key) + 1;
T *new_key = new T[sz];
ITH_MEMSET_HEAP(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
fCpy(new_key, key);
TryNode = new TreeNode<T*, D>(new_key, data);
_FactorLink(DownNode, factor) = TryNode;
TryNode->Parent = DownNode;
NewNode = TryNode;
//Finished binary tree insert. Next to do is to modify balance factors between
//BalanceNode and the new node.
TreeNode<T*, D> *ModifyNode;
factor = fCmp(key, BalanceNode->key);
//factor=key<BalanceNode->key ? factor=-1:1; //Determine the balance factor at BalanceNode.
ModifyNode = DownNode = _FactorLink(BalanceNode,factor);
//ModifyNode will be the 1st child.
//DownNode will travel from here to the recent inserted node (TryNode).
while (DownNode != TryNode) { //Check if we reach the bottom.
f = fCmp(key,DownNode->key);
//f=_FactorCompare(key,DownNode->key);
DownNode->factor = f;
DownNode = _FactorLink(DownNode, f);//Modify balance factor and travels down.
}
//Finshed modifying balance factor.
//Next to do is check the tree if it's unbalance and recover balance.
if (BalanceNode->factor == 0) { //Tree has grown higher.
BalanceNode->factor = factor;
_IncreaseHeight(); //Modify balance factor and increase the height.
return NewNode;
}
if (BalanceNode->factor + factor == 0) { //Tree has gotten more balanced.
BalanceNode->factor = 0; //Set balance factor to 0.
return NewNode;
}
//Tree has gotten out of balance.
if (ModifyNode->factor == factor) //A node and its child has same factor. Single rotation.
DownNode = _SingleRotation(BalanceNode, ModifyNode, factor);
else //A node and its child has converse factor. Double rotation.
DownNode = _DoubleRotation(BalanceNode, ModifyNode, factor);
//Finished the balancing work. Set child field to the root of the new child tree.
if (BalanceNode == ParentNode->Left)
ParentNode->Left = DownNode;
else
ParentNode->Right = DownNode;
return NewNode;
}
else { //root null?
size_t sz = fLen(key) + 1;
T *new_key = new T[sz];
ITH_MEMSET_HEAP(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
fCpy(new_key, key);
head.Left = new TreeNode<T *, D>(new_key, data);
head.rank++;
_IncreaseHeight();
return head.Left;
}
}
bool Delete(T *key)
{
NodePath<T*,D> PathNode;
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
path.push_back(NodePath<T*,D>(&head,-1));
TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
TryNode=head.Left;
char factor;
for (;;) { //Search for the
if (TryNode == 0)
return false; //Not found.
factor = fCmp(key, TryNode->key);
if (factor == 0)
break; //Key found, continue to delete.
//factor = _FactorCompare( key, TryNode->key );
path.push_back(NodePath<T*,D>(TryNode,factor));
TryNode = _FactorLink(TryNode,factor); //Move to left.
}
SuccNode = TryNode->Right; //Find a successor.
factor = 1;
if (SuccNode == 0) {
SuccNode = TryNode->Left;
factor = -1;
}
path.push_back(NodePath<T*,D>(TryNode,factor));
while (SuccNode) {
path.push_back(NodePath<T*,D>(SuccNode, -factor));
SuccNode = _FactorLink(SuccNode,-factor);
}
PathNode = path.back();
delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
TryNode->key = PathNode.Node->key; //Replace key and data field with the successor or predecessor.
PathNode.Node->key = nullptr;
TryNode->data = PathNode.Node->data;
path.pop_back();
_FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
delete PathNode.Node; //Remove the successor from the tree and release memory.
PathNode = path.back();
for (int i=0; i<path.size(); i++)
if (path[i].factor==-1)
path[i].Node->rank--;
for (;;) { //Rebalance the tree along the path back to the root.
if (path.size()==1) {
_DecreaseHeight();
break;
}
BalanceNode = PathNode.Node;
if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurve since subtree height stays.
BalanceNode->factor=-PathNode.factor;
break;
}
if (BalanceNode->factor == PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurve.
BalanceNode->factor = 0;
path.pop_back();
PathNode = path.back();
continue;
}
//Node get out of balance. Here raises 3 cases.
ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
if (ChildNode->factor == 0) { // New case different to insert operation.
TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
path.pop_back();
PathNode = path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
break;
}
else {
if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
}
path.pop_back(); //Recurse back along the path.
PathNode = path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
}
return true;
}
D &operator [](T *key)
{ return (Insert(key,D())->data); }
TreeNode<T*,D> *Search(const T *key)
{
TreeNode<T*,D> *Find=head.Left;
char k;
while (Find != 0) {//&&Find->key!=key)
k=fCmp(key, Find->key);
if (k==0) break;
Find = _FactorLink(Find, k);
}
return Find;
}
TreeNode<T*,D> *SearchIndex(unsigned int rank)
{
unsigned int r = head.rank;
if (rank == -1)
return 0;
if (++rank>=r)
return 0;
TreeNode<T*,D> *n=&head;
while (r!=rank) {
if (rank>r) {
n=n->Right;
rank-=r;
r=n->rank;
} else {
n=n->Left;
r=n->rank;
}
}
return n;
}
TreeNode<T*,D> *Begin()
{
TreeNode<T*,D> *Node = head.Left;
if (Node)
while (Node->Left) Node = Node->Left;
return Node;
}
TreeNode<T*,D> *End()
{
TreeNode<T*,D> *Node=head.Left;
if (Node)
while (Node->Right) Node = Node->Right;
return Node;
}
unsigned int Count() const { return head.rank - 1; }
template <class Fn>
Fn TraverseTree(Fn &f)
{ return TraverseTreeNode(head.Left,f); }
protected:
bool DeleteRoot()
{
NodePath<T*,D> PathNode;
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
path.push_back(NodePath<T*,D>(&head,-1));
TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
TryNode=head.Left;
char factor;
SuccNode=TryNode->Right; //Find a successor.
factor=1;
if (SuccNode==0)
{
SuccNode=TryNode->Left;
factor=-1;
}
path.push_back(NodePath<T*,D>(TryNode,factor));
while (SuccNode) {
path.push_back(NodePath<T*,D>(SuccNode,-factor));
SuccNode=_FactorLink(SuccNode,-factor);
}
PathNode=path.back();
delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
TryNode->key=PathNode.Node->key; //Replace key and data field with the successor.
PathNode.Node->key = nullptr;
TryNode->data=PathNode.Node->data;
path.pop_back();
_FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
delete PathNode.Node; //Remove the successor from the tree and release memory.
PathNode=path.back();
for (int i=0;i<path.size();i++)
if (path[i].factor==-1)
path[i].Node->rank--;
for (;;) { //Rebalance the tree along the path back to the root.
if (path.size() == 1) {
_DecreaseHeight();
break;
}
BalanceNode = PathNode.Node;
if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurse since subtree height not changed.
BalanceNode->factor=-PathNode.factor;
break;
}
if (BalanceNode->factor==PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurse.
BalanceNode->factor=0;
path.pop_back();
PathNode=path.back();
continue;
}
//Node get out of balance. Here raises 3 cases.
ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
if (ChildNode->factor == 0) { // New case different to insert operation.
TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
path.pop_back();
PathNode=path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
break;
} else {
if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
}
path.pop_back(); // Recurve back along the path.
PathNode=path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
}
return true;
}
template <class Fn>
Fn TraverseTreeNode(TreeNode<T*,D> *Node, Fn &f)
{
if (Node) {
if (Node->Left)
TraverseTreeNode(Node->Left,f);
f(Node);
if (Node->Right)
TraverseTreeNode(Node->Right,f);
}
return f;
}
TreeNode<T*,D> *_SingleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
{
TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
_FactorLink(BalanceNode, factor) = Node;
_FactorLink(ModifyNode, -factor) = BalanceNode;
if (Node)
Node->Parent = BalanceNode;
ModifyNode->Parent = BalanceNode->Parent;
BalanceNode->Parent = ModifyNode;
BalanceNode->factor = ModifyNode->factor = 0; //After single rotation, set all factor of 3 node to 0.
if (factor == 1)
ModifyNode->rank += BalanceNode->rank;
else
BalanceNode->rank -= ModifyNode->rank;
return ModifyNode;
}
TreeNode<T*,D> *_SingleRotation2(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
{
TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
_FactorLink(BalanceNode, factor) = Node;
_FactorLink(ModifyNode, -factor) = BalanceNode;
if (Node) Node->Parent = BalanceNode;
ModifyNode->Parent = BalanceNode->Parent;
BalanceNode->Parent = ModifyNode;
ModifyNode->factor = -factor;
if (factor == 1)
ModifyNode->rank+=BalanceNode->rank;
else
BalanceNode->rank-=ModifyNode->rank;
return ModifyNode;
}
TreeNode<T*,D> *_DoubleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
{
TreeNode<T*,D> *DownNode = _FactorLink(ModifyNode, -factor);
TreeNode<T*,D> *Node1, *Node2;
Node1 = _FactorLink(DownNode, factor);
Node2 = _FactorLink(DownNode, -factor);
_FactorLink(ModifyNode, -factor) = Node1;
_FactorLink(DownNode, factor) = ModifyNode;
_FactorLink(BalanceNode, factor) = Node2;
_FactorLink(DownNode, -factor) = BalanceNode;
if (Node1)
Node1->Parent = ModifyNode;
if (Node2)
Node2->Parent = BalanceNode;
DownNode->Parent = BalanceNode->Parent;
BalanceNode->Parent = DownNode;
ModifyNode->Parent = DownNode;
//Set factor according to the result.
if (DownNode->factor == factor) {
BalanceNode->factor = -factor;
ModifyNode->factor = 0;
} else if (DownNode->factor == 0)
BalanceNode->factor = ModifyNode->factor = 0;
else {
BalanceNode->factor = 0;
ModifyNode->factor = factor;
}
DownNode->factor = 0;
if (factor==1) {
ModifyNode->rank -= DownNode->rank;
DownNode->rank += BalanceNode->rank;
} else {
DownNode->rank += ModifyNode->rank;
BalanceNode->rank -= DownNode->rank;
}
return DownNode;
}
TreeNode<T*,D>* &__fastcall _FactorLink(TreeNode<T*,D> *Node, char factor)
//Private helper method to retrieve child according to factor.
//Return right child if factor>0 and left child otherwise.
{ return factor>0? Node->Right : Node->Left; }
void Check()
{
unsigned int k = (unsigned int)head.Right;
unsigned int t = head.Left->height();
if (k != t)
__debugbreak();
}
void _IncreaseHeight()
{
unsigned int k = (unsigned int)head.Right;
head.Right = (TreeNode<T*,D>*)++k;
}
void _DecreaseHeight()
{
unsigned int k = (unsigned int)head.Right;
head.Right = (TreeNode<T*,D>*)--k;
}
};
struct SCMP
{
char operator()(const char *s1,const char *s2)
{
int t = _stricmp(s1, s2);
return t == 0 ? 0 : t > 0 ? 1 :-1;
}
};
struct SCPY { char *operator()(char *dest, const char *src) { return strcpy(dest, src); } };
struct SLEN { int operator()(const char *str) { return strlen(str); } };
struct WCMP
{
char operator()(const wchar_t *s1,const wchar_t *s2)
{
int t =_wcsicmp(s1, s2);
return t == 0 ? 0 : t > 0 ? 1 : -1;
}
};
struct WCPY { wchar_t *operator()(wchar_t *dest, const wchar_t *src) { return wcscpy(dest,src); } };
struct WLEN { int operator()(const wchar_t *str) { return wcslen(str); } };
// EOF
# hookxp.pro
# 8/9/2013 jichi
# Build vnrhookxp.dll for Windows XP
CONFIG += noeh # msvcrt on Windows XP does not has exception handler
include(../dllconfig.pri)
include(../sys/sys.pri)
include($$LIBDIR/disasm/disasm.pri)
include($$LIBDIR/memdbg/memdbg.pri)
include($$LIBDIR/ntinspect/ntinspect.pri)
include($$LIBDIR/winseh/winseh_safe.pri)
include($$LIBDIR/winversion/winversion.pri)
VPATH += ../hook
INCLUDEPATH += ../hook
# 9/27/2013: disable ITH this game engine, only for debugging purpose
#DEFINES += ITH_DISABLE_ENGINE
# jichi 9/22/2013: When ITH is on wine, mutex is needed to protect NtWriteFile
#DEFINES += ITH_WINE
#DEFINES += ITH_SYNC_PIPE
## Libraries
LIBS += -lkernel32 -luser32 -lgdi32
## Sources
TEMPLATE = lib
TARGET = vnrhookxp
#CONFIG += staticlib
HEADERS += \
config.h \
cli.h \
hook.h \
engine/engine.h \
engine/hookdefs.h \
engine/match.h \
engine/pchooks.h \
engine/util.h \
tree/avl.h
SOURCES += \
main.cc \
rpc/pipe.cc \
hijack/texthook.cc \
engine/engine.cc \
engine/match.cc \
engine/pchooks.cc \
engine/util.cc
#RC_FILE += vnrhook.rc
#OTHER_FILES += vnrhook.rc
# EOF
# host.pro
# #CONFIG += eha # 3/1/2014: catchlng all exceptions will break pytexthook on Windows XP
# CONFIG += noeh # Needed by pytexthook ONLY on windows xp orz
# include(../dllconfig.pri)
# include(../sys/sys.pri)
# include($$LIBDIR/winmaker/winmaker.pri)
# include($$LIBDIR/winmutex/winmutex.pri)
# config.pri
# CONFIG(noeh) { # No Exception handler
# message(CONFIG noeh)
# QMAKE_CXXFLAGS += /GR-
# QMAKE_CXXFLAGS_RTTI_ON -= /GR
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
# CONFIG(dll) {
# QMAKE_LFLAGS += /ENTRY:"DllMain"
# }
# }
set(vnrhost_src
avl_p.h
config.h
hookman.h
settings.h
srv.h
srv_p.h
textthread.h
textthread_p.h
SettingManager.h
hookman.cc
main.cc
pipe.cc
textthread.cc
${PROJECT_SOURCE_DIR}/winmaker/winmaker.h
${PROJECT_SOURCE_DIR}/winmaker/winmaker.cc
${PROJECT_SOURCE_DIR}/winmutex/winmutex.h
${common_src}
)
source_group("common" FILES ${common_src})
add_library(vnrhost SHARED ${vnrhost_src})
set_target_properties(vnrhost PROPERTIES LINK_FLAGS /SUBSYSTEM:WINDOWS)
target_compile_options(vnrhost PRIVATE
/GR-
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
target_link_libraries(vnrhost
vnrsys
${WDK_HOME}/lib/wxp/i386/ntdll.lib
)
target_compile_definitions(vnrhost PRIVATE
)
install(TARGETS vnrhost RUNTIME
DESTINATION .
CONFIGURATIONS Release
)
/* 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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "config.h"
#include <intrin.h>
#define SETTING_SPLIT_TIME 0
#define SETTING_CYCLIC_REMOVE 1
#define SETTING_REPEAT_COUNT 2
#define SETTING_CLIPFLAG 3
#define SETTING_MAX_INDEX 4
class IHFSERVICE SettingManager
{
public:
SettingManager() {memset(setting_int,0,sizeof(setting_int));}
~SettingManager(){}
unsigned int SetValue(unsigned int index, unsigned int value)
{
if (index < SETTING_MAX_INDEX)
return (unsigned int)_InterlockedExchange((long*)setting_int+index,(long)value);
else return 0;
}
unsigned int GetValue(unsigned int index)
{
if (index < SETTING_MAX_INDEX)
return setting_int[index];
else return 0;
}
private:
unsigned int setting_int[SETTING_MAX_INDEX];
};
\ No newline at end of file
#pragma once
// avl_p.h
// 8/23/2013 jichi
// Branch: ITH/AVL.h, rev 133
#include "config.h"
enum { STACK_SIZE = 32 };
//#ifndef ITH_STACK
//#define ITH_STACK
template<class T, int stack_size>
class MyStack
{
int index;
T s[stack_size];
public:
MyStack(): index(0)
{ ITH_MEMSET_HEAP(s, 0, sizeof(s)); } // jichi 9/21/2013: assume T is atomic type
T &back() { return s[index-1]; }
int size() { return index; }
void push_back(const T &e)
{
if (index < stack_size)
s[index++]=e;
}
void pop_back() { index--; }
T &operator[](int i) { return s[i]; }
};
//#endif // ITH_STACK
// jichi 9/22/2013: T must be a pointer type which can be deleted
template <class T, class D>
struct IHFSERVICE TreeNode
{
//typedef TreeNode<T, D> Self;
TreeNode() :
Left(nullptr), Right(nullptr), Parent(nullptr)
, rank(1)
, factor('\0'), reserve('\0')
//, key()
//, data()
{
ITH_MEMSET_HEAP(&key, 0, sizeof(key)); // jcihi 9/26/2013: zero memory
ITH_MEMSET_HEAP(&data, 0, sizeof(data)); // jcihi 9/26/2013: zero memory
}
TreeNode(const T &k, const D &d) :
Left(nullptr), Right(nullptr), Parent(nullptr)
, rank(1)
, factor('\0'), reserve('\0') // jichi 9/21/2013: zero reserve
, key(k)
, data(d)
{}
TreeNode *Successor()
{
TreeNode *Node,
*ParentNode;
Node = Right;
if (!Node) {
Node = this;
for (;;) {
ParentNode = Node->Parent;
if (!ParentNode)
return nullptr;
if (ParentNode->Left == Node)
break;
Node = ParentNode;
}
return ParentNode;
}
else
while (Node->Left)
Node = Node->Left;
return Node;
}
TreeNode *Predecessor()
{
TreeNode *Node,
*ParentNode;
Node = Left;
if (!Node) {
Node = this;
for(;;) {
ParentNode = Node->Parent;
if (!ParentNode)
return nullptr;
if (ParentNode->Right == Node)
break;
Node = ParentNode;
}
return ParentNode;
}
else
while (Node->Right)
Node = Node->Right;
return Node;
}
int height()
{
if (!this) // jichi 9/26/2013: what?!
return 0;
int l = Left->height(),
r = Right->height(),
f = factor;
if (l - r + f != 0)
__debugbreak();
f = l > r ? l : r;
return f + 1;
}
TreeNode *Left,
*Right,
*Parent;
unsigned short rank;
char factor,
reserve;
T key;
D data;
};
template<class T,class D>
struct NodePath
{
NodePath() { memset(this, 0, sizeof(NodePath)); } // jichi 11/30/2013: This is the original code in ITH
NodePath(TreeNode<T,D> *n, int f): Node(n), fact(f) {}
TreeNode<T,D> *Node;
union { char factor; int fact; };
};
template <class T, class D, class fComp, class fCopy, class fLength>
class IHFSERVICE AVLTree
{
protected:
TreeNode<T*, D> head;
fComp fCmp;
fCopy fCpy;
fLength fLen;
public:
// - Construction -
AVLTree() {}
virtual ~AVLTree() { DeleteAll(); }
// - Properties -
TreeNode<T*, D> *TreeRoot() const { return head.Left; }
// - Actions -
void DeleteAll()
{
while (head.Left)
DeleteRoot();
}
TreeNode<T*, D> *Insert(const T *key, const D &data)
{
if (head.Left) {
MyStack<TreeNode<T*, D> *,STACK_SIZE> path;
TreeNode<T*,D> *DownNode, *ParentNode, *BalanceNode, *TryNode, *NewNode; //P,T,S,Q
ParentNode = &head;
path.push_back(ParentNode);
char factor,f;
BalanceNode = DownNode = head.Left;
for (;;) { //The first part of AVL tree insert. Just do as binary tree insert routine and record some nodes.
factor = fCmp(key,DownNode->key);
if (factor == 0)
return DownNode; //Duplicate key. Return and do nothing.
TryNode = _FactorLink(DownNode, factor);
if (factor == -1)
path.push_back(DownNode);
if (TryNode) { //DownNode has a child.
if (TryNode->factor != 0) { //Keep track of unbalance node and its parent.
ParentNode = DownNode;
BalanceNode = TryNode;
}
DownNode = TryNode;
}
else
break; //Finished binary tree search;
}
while (path.size()) {
path.back()->rank++;
path.pop_back();
}
size_t sz = fLen(key) + 1;
T *new_key = new T[sz];
ITH_MEMSET_HEAP(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
fCpy(new_key, key);
TryNode = new TreeNode<T*, D>(new_key, data);
_FactorLink(DownNode, factor) = TryNode;
TryNode->Parent = DownNode;
NewNode = TryNode;
//Finished binary tree insert. Next to do is to modify balance factors between
//BalanceNode and the new node.
TreeNode<T*, D> *ModifyNode;
factor = fCmp(key, BalanceNode->key);
//factor=key<BalanceNode->key ? factor=-1:1; //Determine the balance factor at BalanceNode.
ModifyNode = DownNode = _FactorLink(BalanceNode,factor);
//ModifyNode will be the 1st child.
//DownNode will travel from here to the recent inserted node (TryNode).
while (DownNode != TryNode) { //Check if we reach the bottom.
f = fCmp(key,DownNode->key);
//f=_FactorCompare(key,DownNode->key);
DownNode->factor = f;
DownNode = _FactorLink(DownNode, f);//Modify balance factor and travels down.
}
//Finshed modifying balance factor.
//Next to do is check the tree if it's unbalance and recover balance.
if (BalanceNode->factor == 0) { //Tree has grown higher.
BalanceNode->factor = factor;
_IncreaseHeight(); //Modify balance factor and increase the height.
return NewNode;
}
if (BalanceNode->factor + factor == 0) { //Tree has gotten more balanced.
BalanceNode->factor = 0; //Set balance factor to 0.
return NewNode;
}
//Tree has gotten out of balance.
if (ModifyNode->factor == factor) //A node and its child has same factor. Single rotation.
DownNode = _SingleRotation(BalanceNode, ModifyNode, factor);
else //A node and its child has converse factor. Double rotation.
DownNode = _DoubleRotation(BalanceNode, ModifyNode, factor);
//Finished the balancing work. Set child field to the root of the new child tree.
if (BalanceNode == ParentNode->Left)
ParentNode->Left = DownNode;
else
ParentNode->Right = DownNode;
return NewNode;
}
else { //root null?
size_t sz = fLen(key) + 1;
T *new_key = new T[sz];
ITH_MEMSET_HEAP(new_key, 0, sz * sizeof(T)); // jichi 9/26/2013: Zero memory
fCpy(new_key, key);
head.Left = new TreeNode<T *, D>(new_key, data);
head.rank++;
_IncreaseHeight();
return head.Left;
}
}
bool Delete(T *key)
{
NodePath<T*,D> PathNode;
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
path.push_back(NodePath<T*,D>(&head,-1));
TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
TryNode=head.Left;
char factor;
for (;;) { //Search for the
if (TryNode == 0)
return false; //Not found.
factor = fCmp(key, TryNode->key);
if (factor == 0)
break; //Key found, continue to delete.
//factor = _FactorCompare( key, TryNode->key );
path.push_back(NodePath<T*,D>(TryNode,factor));
TryNode = _FactorLink(TryNode,factor); //Move to left.
}
SuccNode = TryNode->Right; //Find a successor.
factor = 1;
if (SuccNode == 0) {
SuccNode = TryNode->Left;
factor = -1;
}
path.push_back(NodePath<T*,D>(TryNode,factor));
while (SuccNode) {
path.push_back(NodePath<T*,D>(SuccNode, -factor));
SuccNode = _FactorLink(SuccNode,-factor);
}
PathNode = path.back();
delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
TryNode->key = PathNode.Node->key; //Replace key and data field with the successor or predecessor.
PathNode.Node->key = nullptr;
TryNode->data = PathNode.Node->data;
path.pop_back();
_FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
delete PathNode.Node; //Remove the successor from the tree and release memory.
PathNode = path.back();
for (int i=0; i<path.size(); i++)
if (path[i].factor==-1)
path[i].Node->rank--;
for (;;) { //Rebalance the tree along the path back to the root.
if (path.size()==1) {
_DecreaseHeight();
break;
}
BalanceNode = PathNode.Node;
if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurve since subtree height stays.
BalanceNode->factor=-PathNode.factor;
break;
}
if (BalanceNode->factor == PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurve.
BalanceNode->factor = 0;
path.pop_back();
PathNode = path.back();
continue;
}
//Node get out of balance. Here raises 3 cases.
ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
if (ChildNode->factor == 0) { // New case different to insert operation.
TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
path.pop_back();
PathNode = path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
break;
}
else {
if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
}
path.pop_back(); //Recurse back along the path.
PathNode = path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
}
return true;
}
D &operator [](T *key)
{ return (Insert(key,D())->data); }
TreeNode<T*,D> *Search(const T *key)
{
TreeNode<T*,D> *Find=head.Left;
char k;
while (Find != 0) {//&&Find->key!=key)
k = fCmp(key, Find->key);
if (k == 0) break;
Find = _FactorLink(Find, k);
}
return Find;
}
TreeNode<T*,D> *SearchIndex(unsigned int rank)
{
unsigned int r = head.rank;
if (rank == -1)
return 0;
if (++rank>=r)
return 0;
TreeNode<T*,D> *n=&head;
while (r!=rank) {
if (rank>r) {
n=n->Right;
rank-=r;
r=n->rank;
} else {
n=n->Left;
r=n->rank;
}
}
return n;
}
TreeNode<T*,D> *Begin()
{
TreeNode<T*,D> *Node = head.Left;
if (Node)
while (Node->Left) Node = Node->Left;
return Node;
}
TreeNode<T*,D> *End()
{
TreeNode<T*,D> *Node=head.Left;
if (Node)
while (Node->Right) Node = Node->Right;
return Node;
}
unsigned int Count() const { return head.rank - 1; }
template <class Fn>
Fn TraverseTree(Fn &f)
{ return TraverseTreeNode(head.Left,f); }
protected:
bool DeleteRoot()
{
NodePath<T*,D> PathNode;
MyStack<NodePath<T*,D>,STACK_SIZE> path; //Use to record a path to the destination node.
path.push_back(NodePath<T*,D>(&head,-1));
TreeNode<T*,D> *TryNode,*ChildNode,*BalanceNode,*SuccNode;
TryNode=head.Left;
char factor;
SuccNode=TryNode->Right; //Find a successor.
factor=1;
if (SuccNode==0)
{
SuccNode=TryNode->Left;
factor=-1;
}
path.push_back(NodePath<T*,D>(TryNode,factor));
while (SuccNode) {
path.push_back(NodePath<T*,D>(SuccNode,-factor));
SuccNode=_FactorLink(SuccNode,-factor);
}
PathNode=path.back();
delete[] TryNode->key; // jichi 9/22/2013: key is supposed to be an array
TryNode->key=PathNode.Node->key; //Replace key and data field with the successor.
PathNode.Node->key = nullptr;
TryNode->data=PathNode.Node->data;
path.pop_back();
_FactorLink(path.back().Node,path.back().factor) = _FactorLink(PathNode.Node,-PathNode.factor);
delete PathNode.Node; //Remove the successor from the tree and release memory.
PathNode=path.back();
for (int i=0;i<path.size();i++)
if (path[i].factor==-1)
path[i].Node->rank--;
for (;;) { //Rebalance the tree along the path back to the root.
if (path.size() == 1) {
_DecreaseHeight();
break;
}
BalanceNode = PathNode.Node;
if (BalanceNode->factor == 0) { // A balance node, just need to adjust the factor. Don't have to recurse since subtree height not changed.
BalanceNode->factor=-PathNode.factor;
break;
}
if (BalanceNode->factor==PathNode.factor) { // Node get more balance. Subtree height decrease, need to recurse.
BalanceNode->factor=0;
path.pop_back();
PathNode=path.back();
continue;
}
//Node get out of balance. Here raises 3 cases.
ChildNode = _FactorLink(BalanceNode, -PathNode.factor);
if (ChildNode->factor == 0) { // New case different to insert operation.
TryNode = _SingleRotation2( BalanceNode, ChildNode, BalanceNode->factor );
path.pop_back();
PathNode=path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
break;
} else {
if (ChildNode->factor == BalanceNode->factor) // Analogous to insert operation case 1.
TryNode = _SingleRotation( BalanceNode, ChildNode, BalanceNode->factor );
else if (ChildNode->factor + BalanceNode->factor == 0) // Analogous to insert operation case 2.
TryNode = _DoubleRotation( BalanceNode, ChildNode, BalanceNode->factor );
}
path.pop_back(); // Recurve back along the path.
PathNode=path.back();
_FactorLink(PathNode.Node, PathNode.factor) = TryNode;
}
return true;
}
template <class Fn>
Fn TraverseTreeNode(TreeNode<T*,D> *Node, Fn &f)
{
if (Node) {
if (Node->Left)
TraverseTreeNode(Node->Left,f);
f(Node);
if (Node->Right)
TraverseTreeNode(Node->Right,f);
}
return f;
}
TreeNode<T*,D> *_SingleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
{
TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
_FactorLink(BalanceNode, factor) = Node;
_FactorLink(ModifyNode, -factor) = BalanceNode;
if (Node)
Node->Parent = BalanceNode;
ModifyNode->Parent = BalanceNode->Parent;
BalanceNode->Parent = ModifyNode;
BalanceNode->factor = ModifyNode->factor = 0; //After single rotation, set all factor of 3 node to 0.
if (factor == 1)
ModifyNode->rank += BalanceNode->rank;
else
BalanceNode->rank -= ModifyNode->rank;
return ModifyNode;
}
TreeNode<T*,D> *_SingleRotation2(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
{
TreeNode<T*,D> *Node = _FactorLink(ModifyNode, -factor);
_FactorLink(BalanceNode, factor) = Node;
_FactorLink(ModifyNode, -factor) = BalanceNode;
if (Node) Node->Parent = BalanceNode;
ModifyNode->Parent = BalanceNode->Parent;
BalanceNode->Parent = ModifyNode;
ModifyNode->factor = -factor;
if (factor == 1)
ModifyNode->rank+=BalanceNode->rank;
else
BalanceNode->rank-=ModifyNode->rank;
return ModifyNode;
}
TreeNode<T*,D> *_DoubleRotation(TreeNode<T*,D> *BalanceNode, TreeNode<T*,D> *ModifyNode, char factor)
{
TreeNode<T*,D> *DownNode = _FactorLink(ModifyNode, -factor);
TreeNode<T*,D> *Node1, *Node2;
Node1 = _FactorLink(DownNode, factor);
Node2 = _FactorLink(DownNode, -factor);
_FactorLink(ModifyNode, -factor) = Node1;
_FactorLink(DownNode, factor) = ModifyNode;
_FactorLink(BalanceNode, factor) = Node2;
_FactorLink(DownNode, -factor) = BalanceNode;
if (Node1)
Node1->Parent = ModifyNode;
if (Node2)
Node2->Parent = BalanceNode;
DownNode->Parent = BalanceNode->Parent;
BalanceNode->Parent = DownNode;
ModifyNode->Parent = DownNode;
//Set factor according to the result.
if (DownNode->factor == factor) {
BalanceNode->factor = -factor;
ModifyNode->factor = 0;
} else if (DownNode->factor == 0)
BalanceNode->factor = ModifyNode->factor = 0;
else {
BalanceNode->factor = 0;
ModifyNode->factor = factor;
}
DownNode->factor = 0;
if (factor==1) {
ModifyNode->rank -= DownNode->rank;
DownNode->rank += BalanceNode->rank;
} else {
DownNode->rank += ModifyNode->rank;
BalanceNode->rank -= DownNode->rank;
}
return DownNode;
}
TreeNode<T*,D>* &__fastcall _FactorLink(TreeNode<T*,D> *Node, char factor)
//Private helper method to retrieve child according to factor.
//Return right child if factor>0 and left child otherwise.
{ return factor>0? Node->Right : Node->Left; }
void Check()
{
unsigned int k = (unsigned int)head.Right;
unsigned int t = head.Left->height();
if (k != t)
__debugbreak();
}
void _IncreaseHeight()
{
unsigned int k = (unsigned int)head.Right;
head.Right = (TreeNode<T*,D>*)++k;
}
void _DecreaseHeight()
{
unsigned int k = (unsigned int)head.Right;
head.Right = (TreeNode<T*,D>*)--k;
}
};
struct SCMP
{
char operator()(const char *s1,const char *s2)
{
int t = _stricmp(s1, s2);
return t == 0 ? 0 : t > 0 ? 1 :-1;
}
};
struct SCPY { char *operator()(char *dest, const char *src) { return strcpy(dest, src); } };
struct SLEN { int operator()(const char *str) { return strlen(str); } };
struct WCMP
{
char operator()(const wchar_t *s1,const wchar_t *s2)
{
int t =_wcsicmp(s1, s2);
return t == 0 ? 0 : t > 0 ? 1 : -1;
}
};
struct WCPY { wchar_t *operator()(wchar_t *dest, const wchar_t *src) { return wcscpy(dest,src); } };
struct WLEN { int operator()(const wchar_t *str) { return wcslen(str); } };
// EOF
#pragma once
// config.h
// 8/23/2013 jichi
// The first header file that are included by all source files.
#define IHF // for dll import
#include "ith/dllconfig.h"
#define IHFAPI __stdcall
#ifdef IHF
# define IHFSERVICE __declspec(dllexport)
#else
# define IHFSERVICE __declspec(dllimport)
#endif
// EOF
// hookman.cc
// 8/24/2013 jichi
// Branch IHF/HookManager.cpp, rev 133
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
# pragma warning (disable:4146) // C4146: unary minus operator applied to unsigned type
#endif // _MSC_VER
#include "hookman.h"
#include "ith/common/const.h"
#include "ith/common/defs.h"
#include "ith/common/types.h"
//#include "ith/common/growl.h"
#include "ith/sys/sys.h"
//#include <emmintrin.h>
namespace { // unnamed
//enum { MAX_ENTRY = 0x40 };
#define HM_LOCK win_mutex_lock<HookManager::mutex_type> d_locker(hmcs) // Synchronized scope for accessing private data
// jichi 9/23/2013: wine deficenciy on mapping sections
// Whe set to false, do not map sections.
//bool ith_has_section = true;
// jichi 9/28/2013: Remove ConsoleOutput from available hooks
//LPWSTR HookNameInitTable[]={ L"ConsoleOutput" , HOOK_FUN_NAME_LIST };
//LPCWSTR HookNameInitTable[] = {HOOK_FUN_NAME_LIST};
//LPVOID DefaultHookAddr[HOOK_FUN_COUNT];
//BYTE null_buffer[4]={0,0,0,0};
//BYTE static_small_buffer[0x100];
//DWORD zeros[4]={0,0,0,0};
//WCHAR user_entry[0x40];
bool GetProcessPath(HANDLE hProc, __out LPWSTR path)
{
PROCESS_BASIC_INFORMATION info;
LDR_DATA_TABLE_ENTRY entry;
PEB_LDR_DATA ldr;
PEB peb;
if (NT_SUCCESS(NtQueryInformationProcess(hProc, ProcessBasicInformation, &info, sizeof(info), 0)))
if (info.PebBaseAddress)
if (NT_SUCCESS(NtReadVirtualMemory(hProc, info.PebBaseAddress, &peb,sizeof(peb), 0)))
if (NT_SUCCESS(NtReadVirtualMemory(hProc, peb.Ldr, &ldr, sizeof(ldr), 0)))
if (NT_SUCCESS(NtReadVirtualMemory(hProc, (LPVOID)ldr.InLoadOrderModuleList.Flink,
&entry, sizeof(LDR_DATA_TABLE_ENTRY), 0)))
if (NT_SUCCESS(NtReadVirtualMemory(hProc, entry.FullDllName.Buffer,
path, MAX_PATH * 2, 0)))
return true;
path = L"";
return false;
}
bool GetProcessPath(DWORD pid, __out LPWSTR path)
{
CLIENT_ID id;
OBJECT_ATTRIBUTES oa = {};
HANDLE hProc;
id.UniqueProcess = pid;
id.UniqueThread = 0;
oa.uLength = sizeof(oa);
if (NT_SUCCESS(NtOpenProcess(&hProc , PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, &oa, &id))) {
bool flag = GetProcessPath(hProc, path);
NtClose(hProc);
return flag;
}
path = L"";
return false;
}
} // unnamed namespace
HookManager *man; // jichi 9/22/2013: initialized in main
//BitMap* pid_map;
DWORD clipboard_flag,
split_time,
repeat_count,
global_filter,
cyclic_remove;
DWORD GetHookName(LPWSTR str, DWORD pid, DWORD hook_addr, DWORD max)
{
if (!pid)
return 0;
DWORD len = 0;
max--; //for '\0' magic marker.
//if (pid == 0) {
// len = wcslen(HookNameInitTable[0]);
// if (len >= max)
// len = max;
// memcpy(str, HookNameInitTable[0], len << 1);
// str[len] = 0;
// return len;
//}
//::man->LockProcessHookman(pid);
ProcessRecord *pr = ::man->GetProcessRecord(pid);
if (!pr)
return 0;
NtWaitForSingleObject(pr->hookman_mutex, 0, 0);
Hook *hks = (Hook *)pr->hookman_map;
for (int i = 0; i < MAX_HOOK; i++)
if (hks[i].Address() == hook_addr) {
len = hks[i].NameLength();
if (len >= max)
len = max;
NtReadVirtualMemory(pr->process_handle, hks[i].Name(), str, len << 1, &len);
if (str[len - 1] == 0)
len--;
else
str[len] = 0;
break;
}
// jichi 9/27/2013: The hook man should be consistent with the one defined in vnrcli
//Hook *h = (Hook *)hks;
//for (int i = 0; i < MAX_HOOK; i++)
// if (!h[i].hook_name)
// break;
// else {
// const Hook &hi = h[i];
// wchar_t buffer[1000];
// DWORD len = hi.NameLength();
// NtReadVirtualMemory(pr->process_handle, hi.hook_name, buffer, len << 1, &len);
// buffer[len] = 0;
// ITH_MSG(buffer);
// }
NtReleaseMutant(pr->hookman_mutex, 0);
//::man->UnlockProcessHookman(pid);
return len;
}
int GetHookNameByIndex(LPWSTR str, DWORD pid, DWORD index)
{
if (!pid)
return 0;
//if (pid == 0) {
// wcscpy(str, HookNameInitTable[0]);
// return wcslen(HookNameInitTable[0]);
//}
DWORD len = 0;
//::man->LockProcessHookman(pid);
ProcessRecord *pr = ::man->GetProcessRecord(pid);
if (!pr)
return 0;
//NtWaitForSingleObject(pr->hookman_mutex,0,0); //already locked
Hook *hks = (Hook *)pr->hookman_map;
if (hks[index].Address()) {
NtReadVirtualMemory(pr->process_handle, hks[index].Name(), str, hks[index].NameLength() << 1, &len);
len = hks[index].NameLength();
}
//NtReleaseMutant(pr->hookman_mutex,0);
return len;
}
//int GetHookString(LPWSTR str, DWORD pid, DWORD hook_addr, DWORD status)
//{
// LPWSTR begin=str;
// str+=swprintf(str,L"%4d:0x%08X:",pid,hook_addr);
// str+=GetHookName(str,pid,hook_addr);
// return str-begin;
//}
void ThreadTable::SetThread(DWORD num, TextThread *ptr)
{
int number = num;
if (number >= size) {
while (number >= size)
size <<= 1;
TextThread **temp;
//if (size < 0x10000) {
temp = new TextThread*[size];
if (size > used)
ITH_MEMSET_HEAP(temp, 0, (size - used) * sizeof(TextThread *)); // jichi 9/21/2013: zero memory
memcpy(temp, storage, used * sizeof(TextThread *));
//}
delete[] storage;
storage = temp;
}
storage[number] = ptr;
if (ptr == nullptr) {
if (number == used - 1)
while (storage[used - 1] == 0)
used--;
} else if (number >= used)
used = number + 1;
}
TextThread *ThreadTable::FindThread(DWORD number)
{ return number <= (DWORD)used ? storage[number] : nullptr; }
static const char sse_table_eq[0x100]={
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,-1,-1, 1,1,1,1, -1,-1,-1,-1, 1,1,1,1, //3, compare 3
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,-1,-1, -1,-1,-1,-1, 1,1,1,1, 1,1,1,1, //7, compare 4
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,-1,-1, 1,1,1,1, -1,-1,-1,-1, 1,1,1,1, //3, compare 3
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
-1,-1,1,1, -1,-1,1,1, -1,-1,1,1, -1,-1,1,1, //1, compare 2
-1,1,-1,1, -1,1,-1,1, -1,1,-1,1, -1,1,-1,1, //0, compare 1
0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0 //f, equal
};
char original_cmp(const ThreadParameter *t1, const ThreadParameter *t2)
{
//Q_ASSERT(t1 && t2);
int t = t1->pid - t2->pid;
if (t == 0) {
t = t1->hook - t2->hook;
if (t == 0) {
t = t1->retn - t2->retn;
if (t == 0) {
t = t1->spl-t2->spl;
if (t == 0) return 0;
return t1->spl > t2->spl ? 1 : -1;
}
else return t1->retn > t2->retn ? 1 : -1;
}
else return t1->hook > t2->hook ? 1: -1;
}
else return t1->pid > t2->pid ? 1 : -1;
//return t>0?1:-1;
}
char TCmp::operator()(const ThreadParameter* t1, const ThreadParameter* t2)
//SSE speed up. Compare four integers in const time without branching.
//The AVL tree branching operation needs 2 bit of information.
//One bit for equality and one bit for "less than" or "greater than".
{
union{__m128 m0;__m128i i0;};
union{__m128 m1;__m128i i1;};
union{__m128 m2;__m128i i2;};
int k0,k1;
i1 = _mm_loadu_si128((const __m128i*)t1);
i2 = _mm_loadu_si128((const __m128i*)t2);
i0 = _mm_cmpgt_epi32(i1,i2);
k0 = _mm_movemask_ps(m0);
i1 = _mm_cmpeq_epi32(i1,i2);
k1 = _mm_movemask_ps(m1);
return sse_table_eq[k1*16+k0];
}
void TCpy::operator()(ThreadParameter* t1, const ThreadParameter* t2)
{ memcpy(t1,t2,sizeof(ThreadParameter)); }
int TLen::operator()(const ThreadParameter* t) { return 0; }
#define NAMED_PIPE_DISCONNECT 1
//Class member of HookManger
HookManager::HookManager() :
// jichi 9/21/2013: Zero memory
//CRITICAL_SECTION hmcs;
current(nullptr)
, console(nullptr)
, wconsole(nullptr)
, create(nullptr)
, remove(nullptr)
, reset(nullptr)
, attach(nullptr)
, detach(nullptr)
, hook(nullptr)
, current_pid(0)
, thread_table(nullptr)
, destroy_event(nullptr)
, register_count(0)
, new_thread_number(0)
{
// jichi 9/21/2013: zero memory
ITH_MEMSET_HEAP(record, 0, sizeof(record));
ITH_MEMSET_HEAP(text_pipes, 0, sizeof(text_pipes));
ITH_MEMSET_HEAP(cmd_pipes, 0, sizeof(cmd_pipes));
ITH_MEMSET_HEAP(recv_threads, 0, sizeof(recv_threads));
head.key = new ThreadParameter;
head.key->pid = 0;
head.key->hook = -1;
head.key->retn = -1;
head.key->spl = -1;
head.data = 0;
thread_table = new ThreadTable; // jichi 9/26/2013: zero memory in ThreadTable
TextThread *entry = new TextThread(0, -1,-1,-1, new_thread_number++); // jichi 9/26/2013: zero memory in TextThread
thread_table->SetThread(0, entry);
SetCurrent(entry);
entry->Status() |= USING_UNICODE;
//texts->SetUnicode(true);
//entry->AddToCombo();
//entry->ComboSelectCurrent();
//if (background==0) entry->AddToStore((BYTE*)BackgroundMsg,wcslen(BackgroundMsg)<<1,0,1);
//InitializeCriticalSection(&hmcs);
destroy_event = IthCreateEvent(0, 0, 0);
}
HookManager::~HookManager()
{
//LARGE_INTEGER timeout={-1000*1000,-1};
//IthBreak();
NtWaitForSingleObject(destroy_event, 0, 0);
NtClose(destroy_event);
NtClose(cmd_pipes[0]);
NtClose(recv_threads[0]);
delete thread_table;
delete head.key;
//DeleteCriticalSection(&hmcs);
}
TextThread *HookManager::FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split)
{
if (pid == 0)
return thread_table->FindThread(0);
ThreadParameter tp = {pid, hook, retn, split};
TreeNode<ThreadParameter *,DWORD> *node = Search(&tp);
return node ? thread_table->FindThread(node->data) : nullptr;
}
TextThread *HookManager::FindSingle(DWORD number)
{ return (number & 0x80008000) ? nullptr : thread_table->FindThread(number); }
void HookManager::DetachProcess(DWORD pid) {}
void HookManager::SetCurrent(TextThread *it)
{
if (current)
current->Status() &= ~CURRENT_SELECT;
current = it;
if (it)
it->Status() |= CURRENT_SELECT;
}
void HookManager::SelectCurrent(DWORD num)
{
if (TextThread *st = FindSingle(num)) {
SetCurrent(st);
if (reset)
reset(st);
//st->ResetEditText();
}
}
void HookManager::RemoveSingleHook(DWORD pid, DWORD addr)
{
HM_LOCK;
//ConsoleOutput("vnrhost:RemoveSingleHook: lock");
//EnterCriticalSection(&hmcs);
DWORD max = thread_table->Used();
bool flag = false;
for (DWORD i = 1; i <= max; i++)
if (TextThread *it = thread_table->FindThread(i))
if (it->PID() == pid && it->Addr() == addr) {
flag |= (it == current);
//flag|=it->RemoveFromCombo();
thread_table->SetThread(i, 0);
if (it->Number() < new_thread_number)
new_thread_number = it->Number();
Delete(it->GetThreadParameter());
if (remove)
remove(it);
delete it;
}
for (DWORD i = 0; i <= max; i++)
if (TextThread *it = thread_table->FindThread(i))
if (it->Link() && thread_table->FindThread(it->LinkNumber()) == nullptr) {
it->LinkNumber() = -1;
it->Link() = nullptr;
}
if (flag) {
current = nullptr;
DWORD number = head.Left ? head.Left->data : 0;
SetCurrent(thread_table->FindThread(number));
if (reset && current)
reset(current);
//it->ResetEditText();
}
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RemoveSingleHook: unlock");
}
void HookManager::RemoveSingleThread(DWORD number)
{
if (number == 0)
return;
HM_LOCK;
//ConsoleOutput("vnrhost:RemoveSingleThread: lock");
//EnterCriticalSection(&hmcs);
if (TextThread *it = thread_table->FindThread(number)) {
thread_table->SetThread(number, 0);
Delete(it->GetThreadParameter());
if (remove)
remove(it);
bool flag = (it == current);
if (it->Number() < new_thread_number)
new_thread_number = it->Number();
delete it;
for (int i = 0; i <= thread_table->Used(); i++)
if (TextThread *t = thread_table->FindThread(i))
if (t->LinkNumber() == number) {
t->Link() = 0;
t->LinkNumber() = -1;
}
if (flag) {
current = nullptr;
number = head.Left ? head.Left->data : 0;
SetCurrent(thread_table->FindThread(number));
if (reset && current)
reset(current);
//it->ResetEditText();
}
}
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RemoveSingleThread: unlock");
}
void HookManager::RemoveProcessContext(DWORD pid)
{
HM_LOCK;
bool flag = false;
//ConsoleOutput("vnrhost:RemoveProcessContext: lock");
//EnterCriticalSection(&hmcs);
for (int i = 1; i < thread_table->Used(); i++)
if (TextThread *it = thread_table->FindThread(i))
if (it->PID() == pid) {
Delete(it->GetThreadParameter());
//if (false == Delete(it->GetThreadParameter())) {
// // jichi 11/26/2013: Remove debugging instructions
// //if (debug)
// // __asm int 3
//}
flag |= (it == current);
//flag|=it->RemoveFromCombo();
if (it->Number() <new_thread_number)
new_thread_number = it->Number();
thread_table->SetThread(i,0);
if (remove)
remove(it);
delete it;
}
for (int i = 0; i < thread_table->Used(); i++)
if (TextThread *it=thread_table->FindThread(i))
if (it->Link() && thread_table->FindThread(it->LinkNumber()) == nullptr) {
it->LinkNumber()=-1;
it->Link() = nullptr;
}
if (flag) {
current = nullptr;
DWORD number = head.Left ? head.Left->data : 0;
SetCurrent(thread_table->FindThread(number));
if (reset && current)
reset(current);
//if (it) it->ResetEditText();
}
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RemoveProcessContext: unlock");
}
void HookManager::RegisterThread(TextThread* it, DWORD num)
{ thread_table->SetThread(num, it); }
void HookManager::RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread)
{
text_pipes[register_count] = text;
cmd_pipes[register_count] = cmd;
recv_threads[register_count] = thread;
register_count++;
if (register_count == 1)
NtSetEvent(destroy_event, 0);
else
NtClearEvent(destroy_event);
}
void HookManager::RegisterProcess(DWORD pid, DWORD hookman, DWORD module)
{
HM_LOCK;
wchar_t str[0x40],
path[MAX_PATH];
//pid_map->Set(pid>>2);
//ConsoleOutput("vnrhost:RegisterProcess: lock");
//EnterCriticalSection(&hmcs);
record[register_count - 1].pid_register = pid;
record[register_count - 1].hookman_register = hookman;
record[register_count - 1].module_register = module;
//record[register_count - 1].engine_register = engine;
swprintf(str, ITH_SECTION_ L"%d", pid);
HANDLE hSection = IthCreateSection(str, HOOK_SECTION_SIZE, PAGE_READONLY);
LPVOID map = nullptr;
//DWORD map_size = 0x1000;
DWORD map_size = HOOK_SECTION_SIZE / 2; // jichi 1/16/2015: Changed to half to hook section size
//if (::ith_has_section)
NtMapViewOfSection(hSection, NtCurrentProcess(),
&map, 0, map_size, 0, &map_size, ViewUnmap, 0,
PAGE_READONLY);
record[register_count - 1].hookman_section = hSection;
record[register_count - 1].hookman_map = map;
HANDLE hProc;
CLIENT_ID id;
id.UniqueProcess = pid;
id.UniqueThread = 0;
OBJECT_ATTRIBUTES oa = {};
oa.uLength = sizeof(oa);
if (NT_SUCCESS(NtOpenProcess(&hProc,
PROCESS_QUERY_INFORMATION|
PROCESS_CREATE_THREAD|
PROCESS_VM_READ|
PROCESS_VM_WRITE|
PROCESS_VM_OPERATION,
&oa,&id)))
record[register_count - 1].process_handle = hProc;
else {
ConsoleOutput("failed to open process");
//::man->AddConsoleOutput(ErrorOpenProcess);
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RegisterProcess: unlock");
return;
}
// jichi 9/27/2013: The hook man should be consistent with the one defined in vnrcli
//Hook *h = (Hook *)map;
//for (int i = 0; i < MAX_HOOK; i++)
// if (!h[i].hook_name)
// break;
// else {
// const Hook &hi = h[i];
// wchar_t buffer[1000];
// DWORD len = hi.NameLength();
// NtReadVirtualMemory(hProc, hi.hook_name, buffer, len << 1, &len);
// buffer[len] = 0;
// ITH_MSG(buffer);
// }
swprintf(str, ITH_HOOKMAN_MUTEX_ L"%d", pid);
record[register_count - 1].hookman_mutex = IthOpenMutex(str);
if (!GetProcessPath(pid, path))
path[0] = 0;
//swprintf(str,L"%.4d:%s", pid, wcsrchr(path, L'\\') + 1); // jichi 9/25/2013: this is useless?
current_pid = pid;
if (attach)
attach(pid);
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:RegisterProcess: unlock");
}
void HookManager::UnRegisterProcess(DWORD pid)
{
HM_LOCK;
//ConsoleOutput("vnrhost:UnRegisterProcess: lock");
//EnterCriticalSection(&hmcs);
int i;
for (i = 0; i < MAX_REGISTER; i++)
if(record[i].pid_register == pid)
break;
if (i < MAX_REGISTER) {
NtClose(text_pipes[i]);
NtClose(cmd_pipes[i]);
NtClose(recv_threads[i]);
NtClose(record[i].hookman_mutex);
//if (::ith_has_section)
NtUnmapViewOfSection(NtCurrentProcess(), record[i].hookman_map);
//else
// delete[] record[i].hookman_map;
NtClose(record[i].process_handle);
NtClose(record[i].hookman_section);
for (; i < MAX_REGISTER; i++) {
record[i] = record[i+1];
text_pipes[i] = text_pipes[i+1];
cmd_pipes[i] = cmd_pipes[i+1];
recv_threads[i] = recv_threads[i+1];
if (text_pipes[i] == 0)
break;
}
register_count--;
if (current_pid == pid)
current_pid = register_count ? record[0].pid_register : 0;
RemoveProcessContext(pid);
}
//pid_map->Clear(pid>>2);
if (register_count == 1)
NtSetEvent(destroy_event, 0);
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:UnRegisterProcess: unlock");
if (detach)
detach(pid);
}
// jichi 9/28/2013: I do not need this
//void HookManager::SetName(DWORD type)
//{
// WCHAR c;
// if (type & PRINT_DWORD)
// c = L'H';
// else if (type & USING_UNICODE) {
// if (type & STRING_LAST_CHAR)
// c = L'L';
// else if (type & USING_STRING)
// c = L'Q';
// else
// c = L'W';
// } else {
// if (type & USING_STRING)
// c = L'S';
// else if (type & BIG_ENDIAN)
// c = L'A';
// else
// c = L'B';
// }
// //swprintf(user_entry,L"UserHook%c",c);
//}
void HookManager::AddLink(WORD from, WORD to)
{
HM_LOCK;
//bool flag=false;
//ConsoleOutput("vnrhost:AddLink: lock");
//EnterCriticalSection(&hmcs);
TextThread *from_thread = thread_table->FindThread(from),
*to_thread = thread_table->FindThread(to);
if (to_thread && from_thread) {
if (from_thread->GetThreadParameter()->pid != to_thread->GetThreadParameter()->pid)
ConsoleOutput("vnrhost:AddLink: link to different process");
else if (from_thread->Link()==to_thread)
ConsoleOutput("vnrhost:AddLink: link already exists");
else if (to_thread->CheckCycle(from_thread))
ConsoleOutput("vnrhost:AddLink: cyclic link");
else {
from_thread->Link()=to_thread;
from_thread->LinkNumber()=to;
ConsoleOutput("vnrhost:AddLink: thread linked");
if (addRemoveLink)
addRemoveLink(from_thread);
//WCHAR str[0x40];
//swprintf(str,FormatLink,from,to);
//AddConsoleOutput(str);
}
} else
ConsoleOutput("vnrhost:AddLink: error link");
//else
// AddConsoleOutput(ErrorLink);
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:AddLink: unlock");
}
void HookManager::UnLink(WORD from)
{
HM_LOCK;
//bool flag=false;
//ConsoleOutput("vnrhost:UnLink: lock");
//EnterCriticalSection(&hmcs);
if (TextThread *from_thread = thread_table->FindThread(from)) {
from_thread->Link() = nullptr;
from_thread->LinkNumber() = 0xffff;
ConsoleOutput("vnrhost:UnLink: link deleted");
if (addRemoveLink)
addRemoveLink(from_thread);
}
//else // jichi 12/25/2013: This could happen when the game exist
// ConsoleOutput("vnrhost:UnLink: thread does not exist");
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:UnLink: unlock");
}
void HookManager::UnLinkAll(WORD from)
{
HM_LOCK;
//bool flag=false;
//ConsoleOutput("vnrhost:UnLinkAll: lock");
//EnterCriticalSection(&hmcs);
if (TextThread *from_thread = thread_table->FindThread(from)) {
from_thread->UnLinkAll();
ConsoleOutput("vnrhost:UnLinkAll: link deleted");
}
//else // jichi 12/25/2013: This could happen after the process exists
// ConsoleOutput("vnrhost:UnLinkAll: thread not exist");
//AddConsoleOutput(L"Link deleted.");
//} else
// AddConsoleOutput(L"Thread not exist.");
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:UnLinkAll: unlock");
}
void HookManager::DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD spl, int len, bool space)
{
// jichi 20/27/2013: When PID is zero, the text comes from console, which I don't need
if (!text || !pid || (len <= 0 && !space))
return;
HM_LOCK;
//bool flag=false;
ThreadParameter tp = {pid, hook, retn, spl};
//ConsoleOutput("vnrhost:DispatchText: lock");
//EnterCriticalSection(&hmcs);
TextThread *it;
//`try {
if (TreeNode<ThreadParameter *,DWORD> *in = Search(&tp)) {
DWORD number = in->data;
it = thread_table->FindThread(number);
} else if (IsFull()) { // jichi 1/16/2015: Skip adding threads when full
static bool once = true; // output only once
if (once) {
once = false;
ConsoleOutput("vnrhost:DispatchText: so many new threads, skip");
}
return;
} else { // New thread
Insert(&tp, new_thread_number);
it = new TextThread(pid, hook, retn, spl, new_thread_number);
RegisterThread(it, new_thread_number);
ConsoleOutput("vnrhost:DispatchText: found new thread");
WCHAR entstr[0x200];
it->GetEntryString(entstr);
ConsoleOutputW(entstr);
while (thread_table->FindThread(++new_thread_number));
if (create)
create(it);
}
if (it)
it->AddText(text, len, false, space); // jichi 10/27/2013: new line is false
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:DispatchText: unlock");
//} catch (...) {
// // ignored
//}
}
void HookManager::AddConsoleOutput(LPCWSTR text)
{
if (text) {
int len = wcslen(text) << 1;
TextThread *console = thread_table->FindThread(0);
//EnterCriticalSection(&hmcs);
console->AddText((BYTE*)text,len,false,true);
console->AddText((BYTE*)L"\r\n",4,false,true);
//LeaveCriticalSection(&hmcs);
}
}
void HookManager::ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD spl)
{
HM_LOCK;
//bool flag=false;
//ConsoleOutput("vnrhost:ClearText: lock");
//EnterCriticalSection(&hmcs);
ThreadParameter tp = {pid, hook, retn, spl};
if (TreeNode<ThreadParameter *, DWORD> *in = Search(&tp))
if (TextThread *it = thread_table->FindThread(in->data)) {
it->Reset();
//SetCurrent(it);
if (reset)
reset(it);
//it->ResetEditText();
}
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:ClearText: unlock");
}
void HookManager::ClearCurrent()
{
HM_LOCK;
//ConsoleOutput("vnrhost:ClearCurrent: lock");
//EnterCriticalSection(&hmcs);
if (current) {
current->Reset();
if (reset)
reset(current);
}
//current->ResetEditText();
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:ClearCurrent: unlock");
}
void HookManager::ResetRepeatStatus()
{
HM_LOCK;
//ConsoleOutput("vnrhost:ResetRepeatStatus: lock");
//EnterCriticalSection(&hmcs);
for (int i = 1; i < thread_table->Used(); i++)
if (TextThread *it = thread_table->FindThread(i))
it->ResetRepeatStatus();
//LeaveCriticalSection(&hmcs);
//ConsoleOutput("vnrhost:ResetRepeatStatus: unlock");
}
//void HookManager::LockHookman(){ EnterCriticalSection(&hmcs); }
//void HookManager::UnlockHookman(){ LeaveCriticalSection(&hmcs); }
void HookManager::LockHookman(){ hmcs.lock(); }
void HookManager::UnlockHookman(){ hmcs.unlock(); }
/*void HookManager::SetProcessEngineType(DWORD pid, DWORD type)
{
int i;
for (i=0;i<MAX_REGISTER;i++)
if (record[i].pid_register==pid) break;
if (i<MAX_REGISTER)
{
record[i].engine_register|=type;
}
}*/
ProcessRecord *HookManager::GetProcessRecord(DWORD pid)
{
HM_LOCK;
//EnterCriticalSection(&hmcs);
for (int i = 0; i < MAX_REGISTER; i++)
if (record[i].pid_register == pid)
return record + i;
return nullptr;
//ProcessRecord *pr = i < MAX_REGISTER ? record + i : nullptr;
//LeaveCriticalSection(&hmcs);
//return pr;
}
DWORD HookManager::GetProcessIDByPath(LPCWSTR str)
{
WCHAR path[MAX_PATH];
for (int i = 0; i < 8 && record[i].process_handle; i++) {
::GetProcessPath(record[i].process_handle, path);
if (_wcsicmp(path,str) == 0)
return record[i].pid_register;
}
return 0;
}
DWORD HookManager::GetCurrentPID() { return current_pid; }
HANDLE HookManager::GetCmdHandleByPID(DWORD pid)
{
HM_LOCK;
//EnterCriticalSection(&hmcs);
for (int i = 0; i < MAX_REGISTER; i++)
if (record[i].pid_register == pid)
return cmd_pipes[i];
return nullptr;
//HANDLE h = i < MAX_REGISTER ? cmd_pipes[i] : 0;
//LeaveCriticalSection(&hmcs);
//return h;
}
MK_BASIC_TYPE(DWORD)
MK_BASIC_TYPE(LPVOID)
//DWORD Hash(LPCWSTR module, int length)
//{
// bool flag = (length==-1);
// DWORD hash = 0;
// for (;*module && (flag || length--); module++)
// hash = ((hash>>7)|(hash<<25)) + *module;
// return hash;
//}
DWORD GetCurrentPID() { return ::man->GetCurrentPID(); }
HANDLE GetCmdHandleByPID(DWORD pid) { return ::man->GetCmdHandleByPID(pid); }
void AddLink(WORD from, WORD to) { ::man->AddLink(from, to); }
// jichi 9/27/2013: Unparse to hook parameters /H code
void GetCode(const HookParam &hp, LPWSTR buffer, DWORD pid)
{
WCHAR c;
LPWSTR ptr = buffer;
// jichi 12/7/2014: disabled
//if (hp.type&PRINT_DWORD)
// c = L'H';
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';
}
ptr += swprintf(ptr, L"/H%c",c);
if (hp.type & NO_CONTEXT)
*ptr++ = L'N';
if (hp.off>>31)
ptr += swprintf(ptr,L"-%X",-(hp.off+4));
else
ptr += swprintf(ptr,L"%X",hp.off);
if (hp.type & DATA_INDIRECT) {
if (hp.ind>>31)
ptr += swprintf(ptr,L"*-%X",-hp.ind);
else
ptr += swprintf(ptr,L"*%X",hp.ind);
}
if (hp.type & USING_SPLIT) {
if (hp.split >> 31)
ptr += swprintf(ptr,L":-%X", -(4 + hp.split));
else
ptr += swprintf(ptr,L":%X", hp.split);
}
if (hp.type & SPLIT_INDIRECT) {
if (hp.split_ind >> 31)
ptr += swprintf(ptr, L"*-%X", -hp.split_ind);
else
ptr += swprintf(ptr, L"*%X", hp.split_ind);
}
if (hp.module) {
if (pid) {
WCHAR path[MAX_PATH];
MEMORY_BASIC_INFORMATION info;
ProcessRecord* pr = ::man->GetProcessRecord(pid);
if (pr) {
HANDLE hProc = pr->process_handle;
if (NT_SUCCESS(NtQueryVirtualMemory(hProc,(PVOID)hp.addr, MemorySectionName, path, MAX_PATH*2, 0)) &&
NT_SUCCESS(NtQueryVirtualMemory(hProc,(PVOID)hp.addr, MemoryBasicInformation, &info, sizeof(info), 0)))
ptr += swprintf(ptr, L"@%X:%s", hp.addr - (DWORD)info. AllocationBase, wcsrchr(path,L'\\') + 1);
}
} else {
ptr += swprintf(ptr, L"@%X!%X", hp.addr, hp.module);
if (hp.function)
ptr += swprintf(ptr, L"!%X", hp.function);
}
}
else
ptr += swprintf(ptr, L"@%X", hp.addr);
}
// jichi 1/16/2015
bool HookManager::IsFull() const { return new_thread_number >= MAX_HOOK; }
// EOF
#pragma once
// hookman.h
// 8/23/2013 jichi
// Branch: ITH/HookManager.h, rev 133
#include "ith/host/avl_p.h"
#include "ith/host/textthread.h"
#include "winmutex/winmutex.h"
enum { MAX_REGISTER = 0xf };
enum { MAX_PREV_REPEAT_LENGTH = 0x20 };
struct ProcessRecord {
DWORD pid_register;
DWORD hookman_register;
DWORD module_register;
//DWORD engine_register; // jichi 10/19/2014: removed
HANDLE process_handle;
HANDLE hookman_mutex;
HANDLE hookman_section;
LPVOID hookman_map;
};
class ThreadTable : public MyVector<TextThread *, 0x40>
{
public:
virtual void SetThread(DWORD number, TextThread *ptr);
virtual TextThread *FindThread(DWORD number);
};
struct TCmp { char operator()(const ThreadParameter *t1,const ThreadParameter *t2); };
struct TCpy { void operator()(ThreadParameter *t1,const ThreadParameter *t2); };
struct TLen { int operator()(const ThreadParameter *t); };
typedef DWORD (*ProcessEventCallback)(DWORD pid);
class IHFSERVICE HookManager : public AVLTree<ThreadParameter, DWORD, TCmp, TCpy, TLen>
{
public:
HookManager();
~HookManager();
// jichi 12/26/2013: remove virtual modifiers
TextThread *FindSingle(DWORD pid, DWORD hook, DWORD retn, DWORD split);
TextThread *FindSingle(DWORD number);
ProcessRecord *GetProcessRecord(DWORD pid);
DWORD GetProcessIDByPath(LPCWSTR str);
void RemoveSingleThread(DWORD number);
void LockHookman();
void UnlockHookman();
void ResetRepeatStatus();
void ClearCurrent();
void AddLink(WORD from, WORD to);
void UnLink(WORD from);
void UnLinkAll(WORD from);
void SelectCurrent(DWORD num);
void DetachProcess(DWORD pid);
void SetCurrent(TextThread *it);
void AddConsoleOutput(LPCWSTR text);
// jichi 10/27/2013: Add const; add space.
void DispatchText(DWORD pid, const BYTE *text, DWORD hook, DWORD retn, DWORD split, int len, bool space);
void ClearText(DWORD pid, DWORD hook, DWORD retn, DWORD split);
void RemoveProcessContext(DWORD pid);
void RemoveSingleHook(DWORD pid, DWORD addr);
void RegisterThread(TextThread*, DWORD);
void RegisterPipe(HANDLE text, HANDLE cmd, HANDLE thread);
void RegisterProcess(DWORD pid, DWORD hookman, DWORD module);
void UnRegisterProcess(DWORD pid);
//void SetName(DWORD);
DWORD GetCurrentPID();
HANDLE GetCmdHandleByPID(DWORD pid);
ConsoleCallback RegisterConsoleCallback(ConsoleCallback cf)
{ return (ConsoleCallback)_InterlockedExchange((long*)&console,(long)cf); }
ConsoleWCallback RegisterConsoleWCallback(ConsoleWCallback cf)
{ return (ConsoleWCallback)_InterlockedExchange((long*)&wconsole,(long)cf); }
ThreadEventCallback RegisterThreadCreateCallback(ThreadEventCallback cf)
{ return (ThreadEventCallback)_InterlockedExchange((long*)&create,(long)cf); }
ThreadEventCallback RegisterThreadRemoveCallback(ThreadEventCallback cf)
{ return (ThreadEventCallback)_InterlockedExchange((long*)&remove,(long)cf); }
ThreadEventCallback RegisterThreadResetCallback(ThreadEventCallback cf)
{ return (ThreadEventCallback)_InterlockedExchange((long*)&reset,(long)cf); }
ThreadEventCallback RegisterAddRemoveLinkCallback(ThreadEventCallback cf)
{ return (ThreadEventCallback)_InterlockedExchange((long*)&addRemoveLink, (long)cf); }
ProcessEventCallback RegisterProcessAttachCallback(ProcessEventCallback cf)
{ return (ProcessEventCallback)_InterlockedExchange((long*)&attach,(long)cf); }
ProcessEventCallback RegisterProcessDetachCallback(ProcessEventCallback cf)
{ return (ProcessEventCallback)_InterlockedExchange((long*)&detach,(long)cf); }
ProcessEventCallback RegisterProcessNewHookCallback(ProcessEventCallback cf)
{ return (ProcessEventCallback)_InterlockedExchange((long*)&hook,(long)cf); }
ProcessEventCallback ProcessNewHook() { return hook; }
TextThread *GetCurrentThread() { return current; }
ProcessRecord *Records() { return record; }
ThreadTable *Table() { return thread_table; }
//DWORD& SplitTime() { return split_time; }
//DWORD& RepeatCount() { return repeat_count; }
//DWORD& CyclicRemove() { return cyclic_remove; }
//DWORD& GlobalFilter() { return global_filter; }
void ConsoleOutput(LPCSTR text) { if (console) console(text); } // not thread safe
void ConsoleOutputW(LPCWSTR text) { if (wconsole) wconsole(text); } // not thread safe
private:
typedef win_mutex<CRITICAL_SECTION> mutex_type;
mutex_type hmcs;
TextThread *current;
ConsoleCallback console; // jichi 12/25/2013: add console output callback
ConsoleWCallback wconsole;
ThreadEventCallback create,
remove,
reset,
addRemoveLink;
ProcessEventCallback attach,
detach,
hook;
DWORD current_pid;
ThreadTable *thread_table;
HANDLE destroy_event;
ProcessRecord record[MAX_REGISTER + 1];
HANDLE text_pipes[MAX_REGISTER + 1],
cmd_pipes[MAX_REGISTER + 1],
recv_threads[MAX_REGISTER + 1];
WORD register_count,
new_thread_number;
// jichi 1/16/2014: Stop adding new threads when full
bool IsFull() const; // { return new_thread_number >= MAX_HOOK; }
bool IsEmpty() const { return !new_thread_number; }
};
// EOF
# host.pri
# 8/9/2011 jichi
DEFINES += WITH_LIB_ITH_HOST
DEPENDPATH += $$PWD
LIBS += -lvnrhost
HEADERS += \
$$PWD/avl_p.h \
$$PWD/hookman.h \
$$PWD/settings.h \
$$PWD/srv.h \
$$PWD/textthread.h \
$$PWD/textthread_p.h
# EOF
# host.pro
# 8/9/2013 jichi
# Build vnrhost
#CONFIG += eha # 3/1/2014: catchlng all exceptions will break pytexthook on Windows XP
CONFIG += noeh # Needed by pytexthook ONLY on windows xp orz
include(../dllconfig.pri)
include(../sys/sys.pri)
include($$LIBDIR/winmaker/winmaker.pri)
include($$LIBDIR/winmutex/winmutex.pri)
# 9/22/2013: When ITH is on wine, certain NT functions are replaced
#DEFINES += ITH_WINE
# 9/27/2013: Only for debugging purpose
#DEFINES += ITH_DISABLE_REPEAT # disable repetition elimination
#DEFINES += ITH_DISABLE_FILTER # disable space filter in pipe
## Libraries
LIBS += -lkernel32 -luser32 #-lcomctl32
## Sources
TEMPLATE = lib
#TARGET = IHF # compatible with ITHv3
TARGET = vnrhost
#CONFIG += staticlib
HEADERS += \
avl_p.h \
config.h \
hookman.h \
settings.h \
srv.h \
srv_p.h \
textthread.h \
textthread_p.h
#util.h
SOURCES += \
hookman.cc \
main.cc \
pipe.cc \
textthread.cc
#util.cc
#RC_FILE += engine.rc
#OTHER_FILES += engine.rc
OTHER_FILES += host.pri
# EOF
// main.cc
// 8/24/2013 jichi
// Branch IHF/main.cpp, rev 111
// 8/24/2013 TODO: Clean up this file
//#ifdef _MSC_VER
//# pragma warning(disable:4800) // C4800: forcing value to bool (performance warning)
//#endif // _MSC_VER
#include "config.h"
//#include "customfilter.h"
#include "srv.h"
#include "srv_p.h"
#include "settings.h"
#include "ith/common/const.h"
#include "ith/common/defs.h"
#include "ith/common/growl.h"
#include "ith/common/types.h"
#include "ith/sys/sys.h"
#include "winmaker/winmaker.h"
#include "ccutil/ccmacro.h"
#include <commctrl.h>
//#define ITH_WINE
//#define ITH_USE_UX_DLLS IthIsWine()
#define ITH_USE_XP_DLLS (IthIsWindowsXp() && !IthIsWine())
namespace { // unnamed
//enum { HOOK_TIMEOUT = -50000000 }; // in nanoseconds = 5 seconds
CRITICAL_SECTION cs;
//WCHAR exist[] = ITH_PIPEEXISTS_EVENT;
//WCHAR mutex[] = L"ITH_RUNNING";
//WCHAR EngineName[] = ITH_ENGINE_DLL;
//WCHAR EngineNameXp[] = ITH_ENGINE_XP_DLL;
//WCHAR DllName[] = ITH_CLIENT_DLL;
//WCHAR DllNameXp[] = ITH_CLIENT_XP_DLL;
HANDLE hServerMutex; // jichi 9/28/2013: used to guard pipe
HANDLE hHookMutex; // jichi 9/28/2013: used to guard hook modification
DWORD admin;
} // unnamed namespace
//extern LPWSTR current_dir;
extern CRITICAL_SECTION detach_cs;
SettingManager* setman;
Settings *settings;
HWND hMainWnd;
HANDLE hPipeExist;
BOOL running;
#define ITH_SYNC_HOOK IthMutexLocker locker(::hHookMutex)
namespace { // unnamed
void GetDebugPriv()
{
HANDLE hToken;
DWORD dwRet;
NTSTATUS status;
TOKEN_PRIVILEGES Privileges = {1,{0x14,0,SE_PRIVILEGE_ENABLED}};
NtOpenProcessToken(NtCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
status = NtAdjustPrivilegesToken(hToken, 0, &Privileges, sizeof(Privileges), 0, &dwRet);
admin = 1; // jichi 8/24/2013: ITH do not need admin
//if (STATUS_SUCCESS == status)
//{
// admin = 1;
//}
//else
//{
// WCHAR buffer[0x10];
// swprintf(buffer, L"%.8X",status);
// MessageBox(0, NotAdmin, buffer, 0);
//}
NtClose(hToken);
}
// jichi 9/22/2013: Change current directory to the same as main module path
// Otherwise NtFile* would not work for files with relative paths.
//BOOL ChangeCurrentDirectory()
//{
// if (const wchar_t *path = GetMainModulePath()) // path to VNR's python exe
// if (const wchar_t *base = wcsrchr(path, L'\\')) {
// size_t len = base - path;
// if (len < MAX_PATH) {
// wchar_t buf[MAX_PATH];
// wcsncpy(buf, path, len);
// buf[len] = 0;
// return SetCurrentDirectoryW(buf);
// }
// }
// return FALSE;
//}
DWORD Inject(HANDLE hProc)
{
enum : DWORD { error = (DWORD)-1 };
LPVOID lpvAllocAddr = 0;
DWORD dwWrite = 0x1000; //, len = 0;
HANDLE hTH;
//LPWSTR dllname = (IthIsWindowsXp() && !IthIsWine()) ? DllNameXp : DllName;
LPCWSTR dllname = ITH_USE_XP_DLLS ? ITH_DLL_XP : ITH_DLL;
//if (!IthCheckFile(dllname))
// return error;
wchar_t path[MAX_PATH];
size_t len = IthGetCurrentModulePath(path, MAX_PATH);
if (!len)
return error;
wchar_t *p;
for (p = path + len; *p != L'\\'; p--);
p++; // ending with L"\\"
//LPCWSTR mp = GetMainModulePath();
//len = wcslen(mp);
//memcpy(path, mp, len << 1);
//memset(path + len, 0, (MAX_PATH - len) << 1);
//LPWSTR p;
//for (p = path + len; *p != L'\\'; p--); // Always a \ after drive letter.
//p++;
wcscpy(p, dllname);
//if (IthIsWine())
// lpvAllocAddr = VirtualAllocEx(hProc, nullptr, dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
//else
NtAllocateVirtualMemory(hProc, &lpvAllocAddr, 0, &dwWrite, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
if (!lpvAllocAddr)
return error;
CheckThreadStart();
//Copy module path into address space of target process.
//if (IthIsWine())
// WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
//else
NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc);
if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) {
ConsoleOutput("vnrhost:inject: ERROR: failed to create remote cli thread");
//ConsoleOutput(ErrorRemoteThread);
return -1;
}
// jichi 9/28/2013: no wait as it will not blocked
NtWaitForSingleObject(hTH, 0, nullptr);
THREAD_BASIC_INFORMATION info;
NtQueryInformationThread(hTH, ThreadBasicInformation, &info, sizeof(info), &dwWrite);
NtClose(hTH);
// jichi 10/19/2014: Disable inject the second dll
//if (info.ExitStatus) {
// //IthCoolDown();
// wcscpy(p, engine);
// //if (IthIsWine())
// // WriteProcessMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
// //else
// NtWriteVirtualMemory(hProc, lpvAllocAddr, path, MAX_PATH << 1, &dwWrite);
// hTH = IthCreateThread(LoadLibraryW, (DWORD)lpvAllocAddr, hProc);
// if (hTH == 0 || hTH == INVALID_HANDLE_VALUE) {
// //ConsoleOutput(ErrorRemoteThread);
// ConsoleOutput("vnrhost:inject: ERROR: failed to create remote eng thread");
// return error;
// }
//
// // jichi 9/28/2013: no wait as it will not blocked
// NtWaitForSingleObject(hTH, 0, nullptr);
// NtClose(hTH);
//}
dwWrite = 0;
//if (IthIsWine())
// VirtualFreeEx(hProc, lpvAllocAddr, dwWrite, MEM_RELEASE);
//else
NtFreeVirtualMemory(hProc, &lpvAllocAddr, &dwWrite, MEM_RELEASE);
return info.ExitStatus;
}
} // unnamed namespace
void CreateNewPipe();
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
CC_UNUSED(lpvReserved);
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
LdrDisableThreadCalloutsForDll(hinstDLL);
InitializeCriticalSection(&cs);
IthInitSystemService();
GetDebugPriv();
// jichi 12/20/2013: Since I already have a GUI, I don't have to InitCommonControls()
//Used by timers.
InitCommonControls();
// jichi 8/24/2013: Create hidden window so that ITH can access timer and events
//hMainWnd = CreateWindowW(L"Button", L"InternalWindow", 0, 0, 0, 0, 0, 0, 0, hinstDLL, 0);
//wm_register_hidden_class("vnrsrv.class");
hMainWnd = (HWND)wm_create_hidden_window(L"vnrsrv", L"Button", hinstDLL);
//ChangeCurrentDirectory();
break;
case DLL_PROCESS_DETACH:
if (::running)
IHF_Cleanup();
DeleteCriticalSection(&cs);
IthCloseSystemService();
wm_destroy_window(hMainWnd);
break;
default:
break;
}
return true;
}
HANDLE IthOpenPipe(LPWSTR name, ACCESS_MASK direction)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
SECURITY_DESCRIPTOR sd = {1};
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
if (NT_SUCCESS(NtCreateFile(&hFile, direction, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0)))
return hFile;
else
return INVALID_HANDLE_VALUE;
}
void ConsoleOutput(LPCSTR text) { man->ConsoleOutput(text); }
void ConsoleOutputW(LPCWSTR text) { man->ConsoleOutputW(text); }
enum { IHS_SIZE = 0x80 };
enum { IHS_BUFF_SIZE = IHS_SIZE - sizeof(HookParam) };
struct InsertHookStruct
{
SendParam sp;
BYTE name_buffer[IHS_SIZE];
};
IHFSERVICE DWORD IHFAPI IHF_Init()
{
BOOL result = false;
DWORD present;
EnterCriticalSection(&cs);
hServerMutex = IthCreateMutex(ITH_SERVER_MUTEX, 1, &present);
if (present)
//MessageBox(0,L"Already running.",0,0);
// jichi 8/24/2013
ITH_WARN(L"I am sorry that this game is attached by some other VNR ><\nPlease restart the game and try again!");
else if (!::running) {
::running = true;
::settings = new Settings;
setman = new SettingManager;
setman->SetValue(SETTING_SPLIT_TIME, 200);
::man = new HookManager;
//cmdq = new CommandQueue;
InitializeCriticalSection(&detach_cs);
::hHookMutex = IthCreateMutex(ITH_SERVER_HOOK_MUTEX, FALSE);
result = true;
}
LeaveCriticalSection(&cs);
return result;
}
IHFSERVICE DWORD IHFAPI IHF_Start()
{
//IthBreak();
CreateNewPipe();
hPipeExist = IthCreateEvent(ITH_PIPEEXISTS_EVENT);
NtSetEvent(hPipeExist, nullptr);
return 0;
}
IHFSERVICE DWORD IHFAPI IHF_Cleanup()
{
BOOL result = FALSE;
EnterCriticalSection(&cs);
if (::running) {
::running = FALSE;
HANDLE hRecvPipe = IthOpenPipe(recv_pipe, GENERIC_WRITE);
NtClose(hRecvPipe);
NtClearEvent(hPipeExist);
//delete cmdq;
delete man;
delete settings;
NtClose(::hHookMutex);
NtClose(hServerMutex);
NtClose(hPipeExist);
DeleteCriticalSection(&detach_cs);
result = TRUE;
}
LeaveCriticalSection(&cs);
return result;
}
IHFSERVICE DWORD IHFAPI IHF_GetPIDByName(LPCWSTR pwcTarget)
{
DWORD dwSize = 0x20000,
dwExpectSize = 0;
LPVOID pBuffer = 0;
SYSTEM_PROCESS_INFORMATION *spiProcessInfo;
DWORD dwPid = 0;
DWORD dwStatus;
NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize);
if (!NT_SUCCESS(dwStatus)) {
NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE);
if (dwStatus != STATUS_INFO_LENGTH_MISMATCH || dwExpectSize < dwSize)
return 0;
dwSize = (dwExpectSize | 0xFFF) + 0x4001; //
pBuffer = 0;
NtAllocateVirtualMemory(NtCurrentProcess(), &pBuffer, 0, &dwSize, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE);
dwStatus = NtQuerySystemInformation(SystemProcessInformation, pBuffer, dwSize, &dwExpectSize);
if (!NT_SUCCESS(dwStatus)) goto _end;
}
for (spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *)pBuffer; spiProcessInfo->dNext;) {
spiProcessInfo = (SYSTEM_PROCESS_INFORMATION *)
((DWORD)spiProcessInfo + spiProcessInfo -> dNext);
if (_wcsicmp(pwcTarget, spiProcessInfo -> usName.Buffer) == 0) {
dwPid = spiProcessInfo->dUniqueProcessId;
break;
}
}
if (!dwPid)
ConsoleOutput("vnrhost:IHF_GetPIDByName: pid not found");
//if (dwPid == 0) ConsoleOutput(ErrorNoProcess);
_end:
NtFreeVirtualMemory(NtCurrentProcess(),&pBuffer,&dwSize,MEM_RELEASE);
return dwPid;
}
IHFSERVICE DWORD IHFAPI IHF_InjectByPID(DWORD pid)
{
WCHAR str[0x80];
if (!::running)
return 0;
if (pid == current_process_id) {
//ConsoleOutput(SelfAttach);
ConsoleOutput("vnrhost:IHF_InjectByPID: refuse to inject myself");
return -1;
}
if (man->GetProcessRecord(pid)) {
//ConsoleOutput(AlreadyAttach);
ConsoleOutput("vnrhost:IHF_InjectByPID: already attached");
return -1;
}
swprintf(str, ITH_HOOKMAN_MUTEX_ L"%d", pid);
DWORD s;
NtClose(IthCreateMutex(str, 0, &s));
if (s)
return -1;
CLIENT_ID id;
OBJECT_ATTRIBUTES oa = {};
HANDLE hProc;
id.UniqueProcess = pid;
id.UniqueThread = 0;
oa.uLength = sizeof(oa);
if (!NT_SUCCESS(NtOpenProcess(&hProc,
PROCESS_QUERY_INFORMATION|
PROCESS_CREATE_THREAD|
PROCESS_VM_OPERATION|
PROCESS_VM_READ|
PROCESS_VM_WRITE,
&oa, &id))) {
//ConsoleOutput(ErrorOpenProcess);
ConsoleOutput("vnrhost:IHF_InjectByPID: failed to open process");
return -1;
}
//if (!engine)
// engine = ITH_USE_XP_DLLS ? ITH_ENGINE_XP_DLL : ITH_ENGINE_DLL;
DWORD module = Inject(hProc);
NtClose(hProc);
if (module == -1)
return -1;
//swprintf(str, FormatInject, pid, module);
//ConsoleOutput(str);
ConsoleOutput("vnrhost:IHF_InjectByPID: inject succeed");
return module;
}
// jichi 7/16/2014: Test if process is valid before creating remote threads
// See: http://msdn.microsoft.com/en-us/library/ms687032.aspx
static bool isProcessTerminated(HANDLE hProc)
{ return WAIT_OBJECT_0 == ::WaitForSingleObject(hProc, 0); }
//static bool isProcessRunning(HANDLE hProc)
//{ return WAIT_TIMEOUT == ::WaitForSingleObject(hProc, 0); }
// jichi 7/16/2014: Test if process is valid before creating remote threads
//static bool isProcessRunning(DWORD pid)
//{
// bool ret = false;
// HANDLE hProc = ::OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
// if (hProc) {
// DWORD status;
// if (::GetExitCodeProcess(hProc, &status)) {
// ret = status == STILL_ACTIVE;
// ::CloseHandle(hProc);
// } else
// ret = true;
// }
// return ret;
//}
IHFSERVICE DWORD IHFAPI IHF_ActiveDetachProcess(DWORD pid)
{
ITH_SYNC_HOOK;
DWORD module;
HANDLE hProc, hThread;
IO_STATUS_BLOCK ios;
//man->LockHookman();
ProcessRecord *pr = man->GetProcessRecord(pid);
HANDLE hCmd = man->GetCmdHandleByPID(pid);
if (pr == 0 || hCmd == 0)
return FALSE;
//hProc = pr->process_handle; //This handle may be closed(thus invalid) during the detach process.
NtDuplicateObject(NtCurrentProcess(), pr->process_handle,
NtCurrentProcess(), &hProc, 0, 0, DUPLICATE_SAME_ACCESS); // Make a copy of the process handle.
module = pr->module_register;
if (module == 0)
return FALSE;
// jichi 7/15/2014: Process already closed
if (isProcessTerminated(hProc)) {
ConsoleOutput("vnrhost::activeDetach: process has terminated");
return FALSE;
}
// jichi 10/19/2014: Disable the second dll
//engine = pr->engine_register;
//engine &= ~0xff;
SendParam sp = {};
sp.type = 4;
ConsoleOutput("vnrhost:IHF_ActiveDetachProcess: sending cmd");
NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam), 0,0);
ConsoleOutput("vnrhost:IHF_ActiveDetachProcess: cmd sent");
//cmdq->AddRequest(sp, pid);
////#ifdef ITH_WINE // Nt series crash on wine
//// hThread = IthCreateThread(FreeLibrary, engine, hProc);
////#else
// hThread = IthCreateThread(LdrUnloadDll, engine, hProc);
////#endif // ITH_WINE
// if (hThread == 0 || hThread == INVALID_HANDLE_VALUE)
// return FALSE;
// // jichi 10/22/2013: Timeout might crash vnrsrv
// //const LONGLONG timeout = HOOK_TIMEOUT;
// //NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout);
// NtWaitForSingleObject(hThread, 0, nullptr);
// NtClose(hThread);
// jichi 7/15/2014: Process already closed
if (isProcessTerminated(hProc)) {
ConsoleOutput("vnrhost:activeDetach: process has terminated");
return FALSE;
}
//IthCoolDown();
//#ifdef ITH_WINE // Nt series crash on wine
// hThread = IthCreateThread(FreeLibrary, engine, hProc);
//#else
hThread = IthCreateThread(LdrUnloadDll, module, hProc);
//#endif // ITH_WINE
if (hThread == 0 || hThread == INVALID_HANDLE_VALUE)
return FALSE;
// jichi 10/22/2013: Timeout might crash vnrsrv
//NtWaitForSingleObject(hThread, 0, (PLARGE_INTEGER)&timeout);
NtWaitForSingleObject(hThread, 0, nullptr);
//man->UnlockHookman();
THREAD_BASIC_INFORMATION info;
NtQueryInformationThread(hThread, ThreadBasicInformation, &info, sizeof(info), 0);
NtClose(hThread);
NtSetEvent(hPipeExist, 0);
FreeThreadStart(hProc);
NtClose(hProc);
return info.ExitStatus;
}
IHFSERVICE DWORD IHFAPI IHF_GetHookManager(HookManager** hookman)
{
if (::running) {
*hookman = man;
return 0;
}
else
return 1;
}
IHFSERVICE DWORD IHFAPI IHF_GetSettingManager(SettingManager** set_man)
{
if (running)
{
*set_man = setman;
return 0;
}
else return 1;
}
IHFSERVICE DWORD IHFAPI IHF_GetSettings(Settings **p)
{
if (::running) {
*p = settings;
return 0;
}
else
return 1;
}
IHFSERVICE DWORD IHFAPI IHF_InsertHook(DWORD pid, HookParam *hp, LPCWSTR name)
{
ITH_SYNC_HOOK;
HANDLE hCmd = man->GetCmdHandleByPID(pid);
if (hCmd == 0)
return -1;
InsertHookStruct s;
s.sp.type = IHF_COMMAND_NEW_HOOK;
s.sp.hp = *hp;
DWORD len;
if (name) len = wcslen(name) << 1;
else len = 0;
if (len >= IHS_BUFF_SIZE - 2) len = IHS_BUFF_SIZE - 2;
memcpy(s.name_buffer, name, len);
s.name_buffer[len] = 0;
s.name_buffer[len + 1] = 0;
IO_STATUS_BLOCK ios;
NtWriteFile(hCmd, 0,0,0, &ios, &s, IHS_SIZE, 0, 0);
//memcpy(&sp.hp,hp,sizeof(HookParam));
//cmdq->AddRequest(sp, pid);
return 0;
}
IHFSERVICE DWORD IHFAPI IHF_ModifyHook(DWORD pid, HookParam *hp)
{
ITH_SYNC_HOOK;
SendParam sp;
HANDLE hModify,hCmd;
hCmd = GetCmdHandleByPID(pid);
if (hCmd == 0)
return -1;
hModify = IthCreateEvent(ITH_MODIFYHOOK_EVENT);
sp.type = IHF_COMMAND_MODIFY_HOOK;
sp.hp = *hp;
IO_STATUS_BLOCK ios;
if (NT_SUCCESS(NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam), 0, 0)))
// jichi 9/28/2013: no wait timeout
//const LONGLONG timeout = HOOK_TIMEOUT;
NtWaitForSingleObject(hModify, 0, nullptr);
NtClose(hModify);
man->RemoveSingleHook(pid, sp.hp.addr);
return 0;
}
IHFSERVICE DWORD IHFAPI IHF_RemoveHook(DWORD pid, DWORD addr)
{
ITH_SYNC_HOOK;
HANDLE hRemoved,hCmd;
hCmd = GetCmdHandleByPID(pid);
if (hCmd == 0)
return -1;
hRemoved = IthCreateEvent(ITH_REMOVEHOOK_EVENT);
SendParam sp = {};
IO_STATUS_BLOCK ios;
sp.type = IHF_COMMAND_REMOVE_HOOK;
sp.hp.addr = addr;
//cmdq -> AddRequest(sp, pid);
NtWriteFile(hCmd, 0,0,0, &ios, &sp, sizeof(SendParam),0,0);
// jichi 10/22/2013: Timeout might crash vnrsrv
//const LONGLONG timeout = HOOK_TIMEOUT;
//NtWaitForSingleObject(hRemoved, 0, (PLARGE_INTEGER)&timeout);
NtWaitForSingleObject(hRemoved, 0, nullptr);
NtClose(hRemoved);
man -> RemoveSingleHook(pid, sp.hp.addr);
return 0;
}
IHFSERVICE DWORD IHFAPI IHF_IsAdmin() { return admin; }
IHFSERVICE DWORD IHFAPI IHF_AddLink(DWORD from, DWORD to)
{
man->AddLink(from & 0xffff, to & 0xffff);
return 0;
}
IHFSERVICE DWORD IHFAPI IHF_UnLink(DWORD from)
{
man->UnLink(from & 0xffff);
return 0;
}
IHFSERVICE DWORD IHFAPI IHF_UnLinkAll(DWORD from)
{
man->UnLinkAll(from & 0xffff);
return 0;
}
// EOF
// pipe.cc
// 8/24/2013 jichi
// Branch IHF/pipe.cpp, rev 93
// 8/24/2013 TODO: Clean up this file
#include "srv_p.h"
#include "hookman.h"
#include "ith/common/defs.h"
#include "ith/common/const.h"
//#include "ith/common/growl.h"
#include "ith/sys/sys.h"
//#include "CommandQueue.h"
//DWORD WINAPI UpdateWindows(LPVOID lpThreadParameter);
namespace { // unnamed
enum NamedPipeCommand {
NAMED_PIPE_DISCONNECT = 1
, NAMED_PIPE_CONNECT = 2
};
bool newline = false;
bool detach = false;
// jichi 10/27/2013
// Check if text has leading space
enum { _filter_limit = 0x20 }; // The same as the orignal ITH filter. So, I don't have to check \u3000
//enum { _filter_limit = 0x19 };
inline bool has_leading_space(const BYTE *text, int len)
{
return len == 1 ? *text <= _filter_limit : // 1 byte
*reinterpret_cast<const WORD *>(text) <= _filter_limit; // 2 bytes
}
// jichi 9/28/2013: Skip leading garbage
// Note:
// - Modifying limit will break manual translation. The orignal one is 0x20
// - Eliminating 0x20 will break English-translated games
const BYTE *Filter(const BYTE *str, int len)
{
#ifdef ITH_DISABLE_FILTER // jichi 9/28/2013: only for debugging purpose
return str;
#endif // ITH_DISABLE_FILTER
// if (len && *str == 0x10) // jichi 9/28/2013: garbage on wine, data link escape, or ^P
// return nullptr;
//enum { limit = 0x19 };
while (true)
if (len >= 2) {
if (*(const WORD *)str <= _filter_limit) { // jichi 10/27/2013: two bytes
str += 2;
len -= 2;
} else
break;
} else if (*str <= _filter_limit) { // jichi 10/27/2013: 1 byte
str++;
len--;
} else
break;
return str;
}
} // unnamed namespace
//WCHAR recv_pipe[] = L"\\??\\pipe\\ITH_PIPE";
//WCHAR command_pipe[] = L"\\??\\pipe\\ITH_COMMAND";
wchar_t recv_pipe[] = ITH_TEXT_PIPE;
wchar_t command_pipe[] = ITH_COMMAND_PIPE;
CRITICAL_SECTION detach_cs; // jichi 9/27/2013: also used in main
//HANDLE hDetachEvent;
extern HANDLE hPipeExist;
void CreateNewPipe()
{
static DWORD acl[7] = {
0x1C0002,
1,
0x140000,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
0x101,
0x1000000,
0};
static SECURITY_DESCRIPTOR sd = {1, 0, 4, 0, 0, 0, (PACL)acl};
HANDLE hTextPipe, hCmdPipe, hThread;
IO_STATUS_BLOCK ios;
UNICODE_STRING us;
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, &sd, 0};
LARGE_INTEGER time = {-500000, -1};
RtlInitUnicodeString(&us, recv_pipe);
if (!NT_SUCCESS(NtCreateNamedPipeFile(
&hTextPipe,
GENERIC_READ | SYNCHRONIZE,
&oa,
&ios,
FILE_SHARE_WRITE,
FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
1, 1, 0, -1,
0x1000,
0x1000,
&time))) {
//ConsoleOutput(ErrorCreatePipe);
ConsoleOutput("vnrhost:CreateNewPipe: failed to create recv pipe");
return;
}
RtlInitUnicodeString(&us, command_pipe);
if (!NT_SUCCESS(NtCreateNamedPipeFile(
&hCmdPipe,
GENERIC_WRITE | SYNCHRONIZE,
&oa,
&ios,
FILE_SHARE_READ,
FILE_OPEN_IF,
FILE_SYNCHRONOUS_IO_NONALERT,
1, 1, 0, -1,
0x1000,
0x1000,
&time))) {
//ConsoleOutput(ErrorCreatePipe);
ConsoleOutput("vnrhost:CreateNewPipe: failed to create cmd pipe");
return;
}
hThread = IthCreateThread(RecvThread, (DWORD)hTextPipe);
man->RegisterPipe(hTextPipe, hCmdPipe, hThread);
}
void DetachFromProcess(DWORD pid)
{
HANDLE hMutex = INVALID_HANDLE_VALUE,
hEvent = INVALID_HANDLE_VALUE;
//try {
IO_STATUS_BLOCK ios;
ProcessRecord *pr = man->GetProcessRecord(pid);
if (!pr)
return;
//IthBreak();
hEvent = IthCreateEvent(nullptr);
if (STATUS_PENDING == NtFsControlFile(
man->GetCmdHandleByPID(pid),
hEvent,
0,0,
&ios,
CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0),
0,0,0,0))
NtWaitForSingleObject(hEvent, 0, 0);
NtClose(hEvent);
//hEvent = INVALID_HANDLE_VALUE;
WCHAR mutex[0x20];
swprintf(mutex, ITH_DETACH_MUTEX_ L"%d", pid);
hMutex = IthOpenMutex(mutex);
if (hMutex != INVALID_HANDLE_VALUE) {
NtWaitForSingleObject(hMutex, 0, 0);
NtReleaseMutant(hMutex, 0);
NtClose(hMutex);
//hMutex = INVALID_HANDLE_VALUE;
}
//} catch (...) {
// if (hEvent != INVALID_HANDLE_VALUE)
// NtClose(hEvent);
// else if (hMutex != INVALID_HANDLE_VALUE) {
// NtWaitForSingleObject(hMutex, 0, 0);
// NtReleaseMutant(hMutex, 0);
// NtClose(hMutex);
// }
//}
//NtSetEvent(hDetachEvent, 0);
if (::running)
NtSetEvent(hPipeExist, 0);
}
// jichi 9/27/2013: I don't need this
//void OutputDWORD(DWORD d)
//{
// WCHAR str[0x20];
// swprintf(str, L"%.8X", d);
// ConsoleOutput(str);
//}
DWORD WINAPI RecvThread(LPVOID lpThreadParameter)
{
HANDLE hTextPipe = (HANDLE)lpThreadParameter;
IO_STATUS_BLOCK ios;
NtFsControlFile(hTextPipe,
0, 0, 0,
&ios,
CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_CONNECT, 0, 0),
0, 0, 0, 0);
if (!::running) {
NtClose(hTextPipe);
return 0;
}
BYTE *buff;
enum { PipeBufferSize = 0x1000 };
buff = new BYTE[PipeBufferSize];
ITH_MEMSET_HEAP(buff, 0, PipeBufferSize); // jichi 8/27/2013: zero memory, or it will crash wine on start up
// 10/19/2014 jichi: there are totally three words received
// See: hook/rpc/pipe.cc
// struct {
// DWORD pid;
// TextHook *man;
// DWORD module;
// //DWORD engine;
// } u;
enum { module_struct_size = 12 };
NtReadFile(hTextPipe, 0, 0, 0, &ios, buff, module_struct_size, 0, 0);
DWORD pid = *(DWORD *)buff,
hookman = *(DWORD *)(buff + 0x4),
module = *(DWORD *)(buff + 0x8);
//engine = *(DWORD *)(buff + 0xc);
man->RegisterProcess(pid, hookman, module);
// jichi 9/27/2013: why recursion?
CreateNewPipe();
//NtClose(IthCreateThread(UpdateWindows,0));
while (::running) {
if (!NT_SUCCESS(NtReadFile(hTextPipe,
0, 0, 0,
&ios,
buff,
0xf80,
0, 0)))
break;
enum { data_offset = 0xc }; // jichi 10/27/2013: Seem to be the data offset in the pipe
DWORD RecvLen = ios.uInformation;
if (RecvLen < data_offset)
break;
DWORD hook = *(DWORD *)buff;
union { DWORD retn; DWORD cmd_type; };
union { DWORD split; DWORD new_engine_type; };
retn = *(DWORD *)(buff + 4);
split = *(DWORD *)(buff + 8);
buff[RecvLen] = 0;
buff[RecvLen + 1] = 0;
if (hook == IHF_NOTIFICATION) {
switch (cmd_type) {
case IHF_NOTIFICATION_NEWHOOK:
{
static long lock;
while (InterlockedExchange(&lock, 1) == 1);
ProcessEventCallback new_hook = man->ProcessNewHook();
if (new_hook)
new_hook(pid);
lock = 0;
} break;
case IHF_NOTIFICATION_TEXT:
ConsoleOutput((LPCSTR)(buff + 8));
break;
}
} else {
// jichi 9/28/2013: Debug raw data
//ITH_DEBUG_DWORD9(RecvLen - 0xc,
// buff[0xc], buff[0xd], buff[0xe], buff[0xf],
// buff[0x10], buff[0x11], buff[0x12], buff[0x13]);
const BYTE *data = buff + data_offset; // th
int len = RecvLen - data_offset;
bool space = ::has_leading_space(data, len);
if (space) {
const BYTE *it = ::Filter(data, len);
len -= it - data;
data = it;
}
if (len >> 31) // jichi 10/27/2013: len is too large, which seldom happens
len = 0;
//man->DispatchText(pid, len ? data : nullptr, hook, retn, split, len, space);
man->DispatchText(pid, data, hook, retn, split, len, space);
}
}
EnterCriticalSection(&detach_cs);
HANDLE hDisconnect = IthCreateEvent(nullptr);
if (STATUS_PENDING == NtFsControlFile(
hTextPipe,
hDisconnect,
0, 0,
&ios,
CTL_CODE(FILE_DEVICE_NAMED_PIPE, NAMED_PIPE_DISCONNECT, 0, 0),
0, 0, 0, 0))
NtWaitForSingleObject(hDisconnect, 0, 0);
NtClose(hDisconnect);
DetachFromProcess(pid);
man->UnRegisterProcess(pid);
//NtClearEvent(hDetachEvent);
LeaveCriticalSection(&detach_cs);
delete[] buff;
if (::running)
ConsoleOutput("vnrhost:DetachFromProcess: detached");
//if (::running) {
// swprintf((LPWSTR)buff, FormatDetach, pid);
// ConsoleOutput((LPWSTR)buff);
// NtClose(IthCreateThread(UpdateWindows, 0));
//}
return 0;
}
// EOF
#pragma once
// settings.h
// 8/24/2013 jichi
struct Settings {
//bool debug; // whether output debug messages using pipes
int splittingInterval;// time to split text into sentences
Settings() : splittingInterval(200) {}
};
// EOF
#pragma once
// srv.h
// 8/23/2013 jichi
// Branch: ITH/IHF.h, rev 105
#include "config.h"
//#include "ith/host/settings.h"
#include "ith/host/hookman.h"
#include "ith/host/SettingManager.h"
struct Settings;
struct HookParam;
// jichi 8/24/2013: Why extern "C"? Any specific reason to use C instead of C++ naming?
extern "C" {
IHFSERVICE DWORD IHFAPI IHF_Init();
IHFSERVICE DWORD IHFAPI IHF_Start();
IHFSERVICE DWORD IHFAPI IHF_Cleanup();
IHFSERVICE DWORD IHFAPI IHF_GetPIDByName(LPCWSTR pwcTarget);
IHFSERVICE DWORD IHFAPI IHF_InjectByPID(DWORD pid);
IHFSERVICE DWORD IHFAPI IHF_ActiveDetachProcess(DWORD pid);
IHFSERVICE DWORD IHFAPI IHF_GetHookManager(HookManager **hookman);
IHFSERVICE DWORD IHFAPI IHF_GetSettingManager(SettingManager** set_man);
IHFSERVICE DWORD IHFAPI IHF_GetSettings(Settings **settings);
IHFSERVICE DWORD IHFAPI IHF_InsertHook(DWORD pid, HookParam *hp, LPCWSTR name = 0);
IHFSERVICE DWORD IHFAPI IHF_ModifyHook(DWORD pid, HookParam *hp);
IHFSERVICE DWORD IHFAPI IHF_RemoveHook(DWORD pid, DWORD addr);
IHFSERVICE DWORD IHFAPI IHF_IsAdmin();
//IHFSERVICE DWORD IHFAPI IHF_GetFilters(PVOID *mb_filter, PVOID *uni_filter);
IHFSERVICE DWORD IHFAPI IHF_AddLink(DWORD from, DWORD to);
IHFSERVICE DWORD IHFAPI IHF_UnLink(DWORD from);
IHFSERVICE DWORD IHFAPI IHF_UnLinkAll(DWORD from);
} // extern "C"
// EOF
#pragma once
// srv_p.h
// 8/24/2013 jichi
// Branch IHF/main.h, rev 111
#include "config.h"
#define GLOBAL extern
#define SHIFT_JIS 0x3A4
class HookManager;
//class CommandQueue;
class SettingManager;
class TextHook;
//class BitMap;
//class CustomFilterMultiByte;
//class CustomFilterUnicode;
//#define TextHook Hook
GLOBAL BOOL running;
//GLOBAL BitMap *pid_map;
//GLOBAL CustomFilterMultiByte *mb_filter;
//GLOBAL CustomFilterUnicode *uni_filter;
GLOBAL HookManager *man;
//GLOBAL CommandQueue *cmdq;
GLOBAL SettingManager *setman;
GLOBAL WCHAR recv_pipe[];
GLOBAL WCHAR command[];
GLOBAL HANDLE hPipeExist;
GLOBAL DWORD split_time,
cyclic_remove,
clipboard_flag,
global_filter;
GLOBAL CRITICAL_SECTION detach_cs;
DWORD WINAPI RecvThread(LPVOID lpThreadParameter);
DWORD WINAPI CmdThread(LPVOID lpThreadParameter);
void ConsoleOutput(LPCSTR text);
void ConsoleOutputW(LPCWSTR text);
DWORD GetCurrentPID();
//DWORD GetProcessIDByPath(LPWSTR str);
HANDLE GetCmdHandleByPID(DWORD pid);
//DWORD Inject(HANDLE hProc);
//DWORD InjectByPID(DWORD pid);
//DWORD PIDByName(LPWSTR target);
//DWORD Hash(LPCWSTR module, int length=-1);
// EOF
// textthread.cc
// 8/24/2013 jichi
// Branch IHF/TextThread.cpp, rev 133
// 8/24/2013 TODO: Clean up this file
#ifdef _MSC_VER
# pragma warning (disable:4100) // C4100: unreference formal parameter
#endif // _MSC_VER
#include "config.h"
#include "settings.h"
#include "textthread.h"
#include "ith/common/const.h"
#include "ith/sys/sys.h"
#include "SettingManager.h"
MK_BASIC_TYPE(BYTE)
MK_BASIC_TYPE(ThreadParameter)
static DWORD MIN_DETECT = 0x20;
static DWORD MIN_REDETECT = 0x80;
//#define MIN_DETECT 0x20
//#define MIN_REDETECT 0x80
#ifndef CURRENT_SELECT
# define CURRENT_SELECT 0x1000
#endif
#ifndef REPEAT_NUMBER_DECIDED
# define REPEAT_NUMBER_DECIDED 0x2000
#endif
DWORD GetHookName(LPWSTR str, DWORD pid, DWORD hook_addr,DWORD max);
extern SettingManager* setman;
extern Settings *settings;
extern HWND hMainWnd;
void CALLBACK NewLineBuff(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
{
KillTimer(hwnd,idEvent);
TextThread *id=(TextThread*)idEvent;
if (id->Status()&CURRENT_SELECT)
//texts->SetLine();
id->CopyLastToClipboard();
id->SetNewLineFlag();
}
// jichi 10/27/2013: removed
//void CALLBACK NewLineConsole(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime)
//{
// KillTimer(hwnd,idEvent);
// TextThread *id=(TextThread*)idEvent;
// if (id->Status()&USING_UNICODE)
// id->AddText((BYTE*)L"\r\n",4,true,true);
// if (id->Status()&CURRENT_SELECT)
// {
// //texts->SetLine();
// }
//}
// jichi 10/27/2013: removed
//void ReplaceSentence(BYTE* text, int len)
//{
// __asm int 3
//}
TextThread::TextThread(DWORD id, DWORD hook, DWORD retn, DWORD spl, WORD num) :
//,tp
thread_number(num)
// jichi 9/21/2013: zero all fields
, link_number(-1)
, last (0)
, align_space(0)
, repeat_single(0)
, repeat_single_current(0)
, repeat_single_count(0)
, repeat_detect_count(0)
, head(new RepeatCountNode())
, link(nullptr)
//, filter(nullptr)
, output(nullptr)
, app_data(nullptr)
//, comment(nullptr)
, thread_string(nullptr)
, timer(0)
, status (0)
, repeat_detect_limit(0x80)
, last_sentence(0)
, prev_sentence(0)
, sentence_length(0)
, repeat_index(0)
, last_time(0)
// , tp({id, hook, retn, spl})
{
tp.pid = id;
tp.hook = hook;
tp.retn = retn;
tp.spl = spl;
//head = new RepeatCountNode;
//ITH_MEMSET_HEAP(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory
//link_number = -1;
//repeat_detect_limit = 0x80;
//filter = nullptr;
//output = nullptr;
}
TextThread::~TextThread()
{
//KillTimer(hMainWnd,timer);
RepeatCountNode *t = head,
*tt;
while (t) {
tt = t;
t = tt->next;
delete tt;
}
head = nullptr;
//if (comment) {
// delete[] comment;
// comment = nullptr;
//}
if (thread_string)
delete[] thread_string;
}
void TextThread::Reset()
{
//timer=0;
last_sentence = 0;
//if (comment) {
// delete[] comment;
// comment = nullptr;
//}
MyVector::Reset();
}
void TextThread::RemoveSingleRepeatAuto(const BYTE *con, int &len)
{
#ifdef ITH_DISABLE_REPEAT // jichi 9/28/2013: only for debugging purpose
return;
#endif // ITH_DISABLE_REPEAT
WORD *text = (WORD *)con;
if (len <= 2) {
if (repeat_single) {
if (repeat_single_count<repeat_single&&
last == *text) {
len = 0;
repeat_single_count++;
} else {
last = *text;
repeat_single_count=0;
}
}
if (status & REPEAT_NUMBER_DECIDED) {
if (++repeat_detect_count>MIN_REDETECT) {
repeat_detect_count = 0;
status ^= REPEAT_NUMBER_DECIDED;
last = 0;
RepeatCountNode *t = head,
*tt;
while (t) {
tt = t;
t = tt->next;
delete tt;
}
head = new RepeatCountNode;
ITH_MEMSET_HEAP(head, 0, sizeof(RepeatCountNode)); // jichi 9/21/2013: zero memory
}
} else {
repeat_detect_count++;
if (last == *text)
repeat_single_current++;
else {
if (last == 0) {
last = *text;
return;
}
if (repeat_single_current == 0) {
status |= REPEAT_NUMBER_DECIDED;
repeat_single = 0;
return;
}
last = *text;
RepeatCountNode *it = head;
if (repeat_detect_count > MIN_DETECT) {
while (it = it->next)
if (it->count>head->count) {
head->count=it->count;
head->repeat=it->repeat;
}
repeat_single = head->repeat;
repeat_single_current = 0;
repeat_detect_count = 0;
status |= REPEAT_NUMBER_DECIDED;
DWORD repeat_sc = repeat_single*4;
if (repeat_sc > MIN_DETECT) {
MIN_DETECT <<= 1;
MIN_REDETECT <<= 1;
}
} else {
bool flag=true;
while (it) {
if (it->repeat == repeat_single_current) {
it->count++;
flag = false;
break;
}
it=it->next;
}
if (flag) {
RepeatCountNode *n = new RepeatCountNode;
n->count = 1;
n->repeat = repeat_single_current;
n->next = head->next;
head->next = n;
}
repeat_single_current = 0;
} //Decide repeat_single
} //Check Repeat
} //repeat_single decided?
} //len
else {
status |= REPEAT_NUMBER_DECIDED;
repeat_single = 0;
}
}
void TextThread::RemoveSingleRepeatForce(BYTE *con,int &len)
{
// jichi 9/1/2013: manual repetition count removed
WORD *text = (WORD *)con;
//if (repeat_single_count<setman->GetValue(SETTING_REPEAT_COUNT)&&last==*text) {
// len=0;
// repeat_single_count++;
//}
//else
{
last = *text;
repeat_single_count=0;
}
}
void TextThread::RemoveCyclicRepeat(BYTE* &con, int &len)
{
DWORD currnet_time = GetTickCount();
if (status & REPEAT_SUPPRESS) {
if (currnet_time - last_time < (unsigned)settings->splittingInterval &&
::memcmp(storage + last_sentence + repeat_index, con, len) == 0) {
repeat_index += len;
if (repeat_index>=sentence_length)
repeat_index -= sentence_length;
len = 0;
} else {
repeat_index = 0;
status &= ~REPEAT_SUPPRESS;
}
} else if (status & REPEAT_DETECT) {
if (::memcmp(storage + last_sentence + repeat_index, con, len) == 0) {
int half_length=repeat_index+len;
if (::memcmp(storage + last_sentence, storage + last_sentence + half_length, repeat_index) == 0) {
len=0;
sentence_length=half_length;
status&=~REPEAT_DETECT;
status|=REPEAT_SUPPRESS;
// jichi 10/27/2013: Not used
//if (status&CURRENT_SELECT)
// ReplaceSentence(storage+last_sentence+half_length,repeat_index);
ClearMemory(last_sentence+half_length,repeat_index);
used-=repeat_index;
repeat_index=0;
}
else
repeat_index += len;
}
else {
repeat_index=0;
status &= ~REPEAT_DETECT;
}
} else {
if (sentence_length == 0)
return;
else if (len <= (int)sentence_length) {
if (memcmp(storage + last_sentence, con, len) == 0) {
status |= REPEAT_DETECT;
repeat_index = len;
if (repeat_index == sentence_length) {
repeat_index = 0;
len = 0;
}
} else if (sentence_length > repeat_detect_limit) {
if (len > 2) {
DWORD u = used;
while (memcmp(storage + u - len, con, len) == 0)
u -= len;
ClearMemory(u, used - u);
used = u;
repeat_index = 0;
// jichi 10/27/2013: Not used
//if (status & CURRENT_SELECT)
// ReplaceSentence(storage + last_sentence, used - u);
status |= REPEAT_SUPPRESS;
len = 0;
} else if (len <= 2)
{
WORD tmp = *(WORD *)(storage + last_sentence);
DWORD index, last_index, tmp_len;
index = used-len;
if (index < last_sentence)
index = last_sentence;
//Locate position of current input.
_again:
*(WORD *)(storage+last_sentence) = *(WORD *)con;
while (*(WORD *)(storage + index) != *(WORD *)con)
index--;
*(WORD *)(storage + last_sentence) = tmp;
if (index > last_sentence) {
tmp_len = used - index;
if (tmp_len <= 2) {
repeat_detect_limit += 0x40;
last_time = currnet_time;
return;
}
if (index - last_sentence >= tmp_len &&
memcmp(storage + index - tmp_len, storage + index, tmp_len) == 0) {
repeat_detect_limit = 0x80;
sentence_length =tmp_len;
index -= tmp_len;
while (memcmp(storage + index - sentence_length, storage + index, sentence_length) == 0)
index -= sentence_length;
repeat_index = 2;
len = 0;
last_index = index;
if (status&USING_UNICODE) {
while (storage[index] == storage[index + sentence_length])
index -= 2;
index += 2;
while (true) {
tmp = *(WORD *)(storage + index);
if (tmp >= 0x3000 && tmp < 0x3020)
index += 2;
else
break;
}
} else {
DWORD last_char_len;
while (storage[index] == storage[index + sentence_length]) {
last_char_len = LeadByteTable[storage[index]];
index -= last_char_len;
}
index += last_char_len;
while (storage[index] == 0x81) {
if ((storage[index+1]>>4) == 4)
index += 2;
else
break;
}
}
repeat_index += last_index - index;
status |= REPEAT_SUPPRESS;
last_sentence = index;
index += sentence_length;
// jichi 10/27/2013: Not used
//if (status&CURRENT_SELECT)
// ReplaceSentence(storage + index, used - index);
ClearMemory(index, used - index);
//memset(storage + index, 0, used - index);
used = index;
} else {
index--;
goto _again;
}
}
else
repeat_detect_limit += 0x40;
}
}
}
}
last_time = currnet_time;
}
void TextThread::ResetRepeatStatus()
{
last=0;
repeat_single=0;
repeat_single_current=0;
repeat_single_count=0;
repeat_detect_count=0;
RepeatCountNode *t = head->next,
*tt;
while (t) {
tt = t;
t = tt->next;
delete tt;
}
//head=new RepeatCountNode;
head->count = head->repeat = 0;
status &= ~REPEAT_NUMBER_DECIDED;
}
void TextThread::AddLineBreak()
{
if (sentence_length == 0) return;
if (status&BUFF_NEWLINE)
{
prev_sentence=last_sentence;
sentence_length=0;
if (status & USING_UNICODE)
AddToStore((BYTE *)L"\r\n\r\n", 8);
else
AddToStore((BYTE *)"\r\n\r\n", 4);
if (output)
output(this, 0, 8, TRUE, app_data, false); // jichi 10/27/2013: space is false
last_sentence = used;
status &= ~BUFF_NEWLINE;
}
}
void TextThread::AddText(const BYTE *con, int len, bool new_line, bool space)
{
if (!con || (len <= 0 && !space))
return;
if (len && !new_line) {
// jichi 9/1/2013: manual repetition count removed
//if (setman->GetValue(SETTING_REPEAT_COUNT)) {
// status|=REPEAT_NUMBER_DECIDED;
// RemoveSingleRepeatForce(con,len);
//}
//else
RemoveSingleRepeatAuto(con, len);
if (len <= 0 && !space)
return;
}
// jichi 9/1/2013: manual repetition count removed
//if(setman->GetValue(SETTING_CYCLIC_REMOVE)) {
// //if (status & REPEAT_NUMBER_DECIDED)
// RemoveCyclicRepeat(con,len);
//}
//if (len <= 0)
// return;
// jichi 10/27/2013: User-defined filter callback is disabled
//if (filter)
// len = filter(this, con,len, new_line, app_data);
//if (len <= 0)
// return;
if (len && sentence_length == 0) {
if (status & USING_UNICODE) {
if (*(WORD *)con == 0x3000) { // jichi 10/27/2013: why skip unicode space?!
con += 2;
len -= 2;
}
} else if (*(WORD *)con == 0x4081) {
con += 2;
len -= 2;
}
if (len <= 0 && !space)
return;
}
if (status & BUFF_NEWLINE)
AddLineBreak();
if (len)
if (new_line) {
prev_sentence = last_sentence;
last_sentence = used + 4;
if (status & USING_UNICODE)
last_sentence += 4;
sentence_length = 0;
} else {
SetNewLineTimer();
if (link) {
const BYTE *send = con;
int l = len;
if (status & USING_UNICODE) { // Although unlikely, a thread and its link may have different encoding.
if ((link->Status() & USING_UNICODE) == 0) {
send = new BYTE[l];
//ITH_MEMSET_HEAP(send, 0, l); // jichi 9/26/2013: zero memory
l = WC_MB((LPWSTR)con, (char *)send);
}
link->AddTextDirect(send, l, space);
} else {
if (link->Status() & USING_UNICODE) {
size_t sz = len * 2 + 2;
send = new BYTE[sz];
//ITH_MEMSET_HEAP(send, 0, sz); // jichi 9/26/2013: zero memory
l = MB_WC((char *)con, (LPWSTR)send) << 1;
}
link->AddTextDirect(send, l, space);
}
link->SetNewLineTimer();
if (send != con)
delete[] send;
}
sentence_length += len;
}
BYTE *data = const_cast<BYTE *>(con); // jichi 10/27/2013: TODO: Figure out where con is modified
if (output)
len = output(this, data, len, new_line, app_data, space);
if (AddToStore(data, len)) {
//sentence_length += len;
/*ResetRepeatStatus();
last_sentence=0;
prev_sentence=0;
sentence_length=len;
repeat_index=0;
status&=~REPEAT_DETECT|REPEAT_SUPPRESS; */
}
}
void TextThread::AddTextDirect(const BYTE* con, int len, bool space) // Add to store directly, penetrating repetition filters.
{
// jichi 10/27/2013: Accordig to the logic, both len and con must be > 0
if (status & BUFF_NEWLINE)
AddLineBreak();
SetNewLineTimer();
if (link) {
const BYTE *send = con;
int l = len;
if (status & USING_UNICODE) {
if ((link->Status()&USING_UNICODE) == 0) {
send = new BYTE[l];
//ITH_MEMSET_HEAP(send, 0, l); // jichi 9/26/2013: zero memory
l = WC_MB((LPWSTR)con,(char*)send);
}
link->AddText(send, l, false, space); // new_line is false
} else {
if (link->Status()&USING_UNICODE) {
size_t sz = len * 2 + 2;
send = new BYTE[sz];
//ITH_MEMSET_HEAP(send, 0, sz); // jichi 9/26/2013: zero memory
l = MB_WC((char *)con, (LPWSTR)send) << 1;
}
link->AddText(send, l, false, space); // new_line is false
}
link->SetNewLineTimer();
if (send != con)
delete[] send;
}
sentence_length += len;
BYTE *data = const_cast<BYTE *>(con); // jichi 10/27/2013: TODO: Figure out where con is modified
if (output)
len = output(this, data, len, false, app_data, space);
AddToStore(data, len);
}
DWORD TextThread::GetEntryString(LPWSTR str, DWORD max)
{
DWORD len = 0;
if (str && max > 0x40) {
max--;
if (thread_string) {
len = wcslen(thread_string);
len = len < max ? len : max;
memcpy(str, thread_string, len << 1);
str[len] = 0;
} else {
len = swprintf(str, L"%.4X:%.4d:0x%08X:0x%08X:0x%08X:",
thread_number, tp. pid, tp.hook, tp.retn, tp.spl);
len += GetHookName(str + len, tp.pid, tp.hook, max - len);
thread_string = new wchar_t[len + 1];
//ITH_MEMSET_HEAP(thread_string, 0, (len+1) * sizeof(wchar_t)); // jichi 9/26/2013: zero memory
thread_string[len] = 0;
memcpy(thread_string, str, len * sizeof(wchar_t));
}
//if (comment) {
// str += len;
// max--;
// DWORD cl = wcslen(comment);
// if (len + cl >= max)
// cl = max - len;
// *str++ = L'-';
// memcpy(str, comment, cl << 1);
// str[cl] = 0;
// len += cl;
//}
}
return len;
}
// jichi 9/28/2013: removed
//void TextThread::CopyLastSentence(LPWSTR str)
//{
// int i,j,l;
// if (status&USING_UNICODE)
// {
// if (used>8)
// {
// j=used>0xF0?(used-0xF0):0;
// for (i=used-0xA;i>=j;i-=2)
// {
// if (*(DWORD*)(storage+i)==0xA000D) break;
// }
// if (i>=j)
// {
// l=used-i;
// if (i>j) l-=4;
// j=4;
// }
// else
// {
// i+=2;
// l=used-i;
// j=0;
// }
// memcpy(str,storage+i+j,l);
// str[l>>1]=0;
// }
// else
// {
// memcpy(str,storage,used);
// str[used>>1]=0;
// }
// }
// else
// {
// if (used>4)
// {
// j=used>0x80?(used-0x80):0;
// for (i=used-5;i>=j;i--)
// {
// if (*(DWORD*)(storage+i)==0xA0D0A0D) break;
// }
// if (i>=j)
// {
// l=used-i;
// if (i>j) l-=4;
// j=4;
// }
// else
// {
// i++;
// l=used-i;
// j=0;
// }
// size_t sz = (l|0xF) + 1;
// char *buff = new char[sz];
// //memset(buff, 0, sz); // jichi 9/26/2013: zero memory
// memcpy(buff, storage + i + j, l);
// buff[l] = 0;
// str[MB_WC(buff, str)] = 0;
// delete[] buff;
// } else {
// storage[used] = 0;
// str[MB_WC((char *)storage, str)] = 0;
// }
// }
//}
static char clipboard_buffer[0x400];
// jichi 8/25/2013: clipboard removed
void CopyToClipboard(void* str,bool unicode, int len)
{
if (setman->GetValue(SETTING_CLIPFLAG) && str && len > 0)
{
int size=(len*2|0xF)+1;
if (len>=1022) return;
memcpy(clipboard_buffer,str,len);
*(WORD*)(clipboard_buffer+len)=0;
HGLOBAL hCopy;
LPWSTR copy;
if (OpenClipboard(0))
{
if (hCopy=GlobalAlloc(GMEM_MOVEABLE,size))
{
if (copy=(LPWSTR)GlobalLock(hCopy))
{
if (unicode)
{
memcpy(copy,clipboard_buffer,len+2);
}
else
copy[MB_WC(clipboard_buffer,copy)]=0;
GlobalUnlock(hCopy);
EmptyClipboard();
SetClipboardData(CF_UNICODETEXT,hCopy);
}
}
CloseClipboard();
}
}
}
void TextThread::CopyLastToClipboard()
{
// jichi 8/25/2013: clipboard removed
CopyToClipboard(storage+last_sentence,(status&USING_UNICODE)>0,used-last_sentence);
}
//void TextThread::ResetEditText()
//{
// //__asm int 3;
// WCHAR str[0x20];
// swprintf(str,L"%.8X",_ReturnAddress());
//}
// jichi 9/25/2013: Removed
//void TextThread::ExportTextToFile(LPWSTR) //filename)
//{
// HANDLE hFile=IthCreateFile(filename,FILE_WRITE_DATA,0,FILE_OPEN_IF);
// if (hFile==INVALID_HANDLE_VALUE) return;
// EnterCriticalSection(&cs_store);
// IO_STATUS_BLOCK ios;
// LPVOID buffer=storage;
// DWORD len=used;
// BYTE bom[4]={0xFF,0xFE,0,0};
// LARGE_INTEGER offset={2,0};
// if ((status&USING_UNICODE)==0)
// {
// len=MB_WC_count((char*)storage,used);
// buffer = new wchar_t[len+1];
// MB_WC((char*)storage,(wchar_t*)buffer);
// len<<=1;
// }
// NtWriteFile(hFile,0,0,0,&ios,bom,2,0,0);
// NtWriteFile(hFile,0,0,0,&ios,buffer,len,&offset,0);
// NtFlushBuffersFile(hFile,&ios);
// if (buffer !=storage)
// delete[] buffer;
// NtClose(hFile);
// LeaveCriticalSection(&cs_store);
//}
//void TextThread::SetComment(LPWSTR str)
//{
// if (comment)
// delete[] comment;
// size_t sz = wcslen(str);
// comment = new wchar_t[sz + 1];
// comment[sz] = 0;
// wcscpy(comment, str);
//}
void TextThread::SetNewLineFlag() { status |= BUFF_NEWLINE; }
bool TextThread::CheckCycle(TextThread* start)
{
if (link==start||this==start) return true;
if (link==0) return false;
return link->CheckCycle(start);
}
void TextThread::SetNewLineTimer()
{
if (thread_number == 0)
// jichi 10/27/2013: Not used
timer = 0; //SetTimer(hMainWnd,(UINT_PTR)this, settings->splittingInterval, NewLineConsole);
else
timer = SetTimer(hMainWnd,(UINT_PTR)this, settings->splittingInterval, NewLineBuff);
}
DWORD TextThread::GetThreadString(LPWSTR str, DWORD max)
{
DWORD len = 0;
if (max) {
wchar_t buffer[0x200];
wchar_t c;
if (thread_string == nullptr)
GetEntryString(buffer, 0x200); //This will allocate thread_string.
LPWSTR p1,
end;
for (end = thread_string; *end; end++);
c = thread_string[0];
thread_string[0] = L':';
for (p1 = end; *p1 != L':'; p1--);
thread_string[0] = c;
if (p1 == thread_string)
return 0;
p1++;
len = end - p1;
if (len >= max)
len = max;
memcpy(str, p1, len << 1);
str[len] = 0;
}
return len;
}
void TextThread::UnLinkAll()
{
if (link) link->UnLinkAll();
link = 0;
link_number = -1;
}
// EOF
#pragma once
// textthread.h
// 8/23/2013 jichi
// Branch: ITH/TextThread.h, rev 120
#include "ith/host/textthread_p.h"
#include <intrin.h> // require _InterlockedExchange
struct RepeatCountNode {
short repeat;
short count;
RepeatCountNode *next;
//RepeatCountNode() : repeat(0), count(0), next(nullptr) {}
};
struct ThreadParameter {
DWORD pid; // jichi: 5/11/2014: The process ID
DWORD hook;
DWORD retn; // jichi 5/11/2014: The return address of the hook
DWORD spl; // jichi 5/11/2014: the processed split value of the hook parameter
};
#define CURRENT_SELECT 0x1000
#define REPEAT_NUMBER_DECIDED 0x2000
#define BUFF_NEWLINE 0x4000
#define CYCLIC_REPEAT 0x8000
#define COUNT_PER_FOWARD 0x200
#define REPEAT_DETECT 0x10000
#define REPEAT_SUPPRESS 0x20000
#define REPEAT_NEWLINE 0x40000
class TextThread;
typedef void (* ConsoleCallback)(LPCSTR text);
typedef void (* ConsoleWCallback)(LPCWSTR text);
typedef DWORD (* ThreadOutputFilterCallback)(TextThread *, BYTE *, DWORD, DWORD, PVOID, bool space); // jichi 10/27/2013: Add space
typedef DWORD (* ThreadEventCallback)(TextThread *);
//extern DWORD split_time,repeat_count,global_filter,cyclic_remove;
class TextThread : public MyVector<BYTE, 0x200>
{
public:
TextThread(DWORD pid, DWORD hook, DWORD retn, DWORD spl, WORD num);
~TextThread();
//virtual void CopyLastSentence(LPWSTR str);
//virtual void SetComment(LPWSTR);
//virtual void ExportTextToFile(LPWSTR filename);
virtual bool CheckCycle(TextThread *start);
virtual DWORD GetThreadString(LPWSTR str, DWORD max);
virtual DWORD GetEntryString(LPWSTR str, DWORD max = 0x200);
void Reset();
void AddText(const BYTE *con,int len, bool new_line, bool space); // jichi 10/27/2013: add const; remove console; add space
void RemoveSingleRepeatAuto(const BYTE *con, int &len); // jichi 10/27/2013: add const
void RemoveSingleRepeatForce(BYTE *con, int &len);
void RemoveCyclicRepeat(BYTE *&con, int &len);
void ResetRepeatStatus();
void AddLineBreak();
//void ResetEditText();
void ComboSelectCurrent();
void UnLinkAll();
void CopyLastToClipboard();
//void AdjustPrevRepeat(DWORD len);
//void PrevRepeatLength(DWORD &len);
//bool AddToCombo();
bool RemoveFromCombo();
void SetNewLineFlag();
void SetNewLineTimer();
BYTE *GetStore(DWORD *len) { if (len) *len = used; return storage; }
DWORD LastSentenceLen() { return used - last_sentence; }
DWORD PID() const { return tp.pid; }
DWORD Addr() const {return tp.hook; }
DWORD &Status() { return status; }
WORD Number() const { return thread_number; }
WORD &Last() { return last; }
WORD &LinkNumber() { return link_number; }
UINT_PTR &Timer() { return timer; }
ThreadParameter *GetThreadParameter() { return &tp; }
TextThread *&Link() { return link; }
//LPCWSTR GetComment() { return comment; }
ThreadOutputFilterCallback RegisterOutputCallBack(ThreadOutputFilterCallback cb, PVOID data)
{
app_data = data;
return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&output,(long)cb);
}
ThreadOutputFilterCallback RegisterFilterCallBack(ThreadOutputFilterCallback cb, PVOID data)
{
app_data = data;
return (ThreadOutputFilterCallback)_InterlockedExchange((long*)&filter,(long)cb);
}
void SetRepeatFlag() { status |= CYCLIC_REPEAT; }
void ClearNewLineFlag() { status &= ~BUFF_NEWLINE; }
void ClearRepeatFlag() { status &= ~CYCLIC_REPEAT; }
protected:
void AddTextDirect(const BYTE *con, int len, bool space); // jichi 10/27/2013: add const; add space; change to protected
private:
ThreadParameter tp;
WORD thread_number,
link_number;
WORD last,
align_space;
WORD repeat_single;
WORD repeat_single_current;
WORD repeat_single_count;
WORD repeat_detect_count;
RepeatCountNode *head;
TextThread *link;
ThreadOutputFilterCallback filter; // jichi 10/27/2013: Remove filter
ThreadOutputFilterCallback output;
PVOID app_data;
//LPWSTR comment,
LPWSTR thread_string;
UINT_PTR timer;
DWORD status,repeat_detect_limit;
DWORD last_sentence,
prev_sentence,
sentence_length,
repeat_index,
last_time;
};
// EOF
#pragma once
// textthread_p.h
// 8/14/2013 jichi
// Branch: ITH/main_template.h, rev 66
#include "config.h"
template <typename T>
void Release(const T &p) { delete p; }
// Prevent memory release.
// Used when T is basic types and will be automatically released (on stack).
#define MK_BASIC_TYPE(T) \
template<> \
void Release<T>(const T &p) {}
template<class T>
struct BinaryEqual {
bool operator ()(const T &a, const T &b, DWORD) { return a == b; }
};
template<class T, int default_size, class fComp=BinaryEqual<T> >
class MyVector
{
public:
MyVector() : size(default_size), used(0)
{
InitializeCriticalSection(&cs_store);
storage = new T[size];
// jichi 9/21/2013: zero memory
// This would cause trouble if T is not an atomic type
ITH_MEMSET_HEAP(storage, 0, sizeof(T) * size);
}
virtual ~MyVector()
{
if (storage)
delete[] storage;
DeleteCriticalSection(&cs_store);
storage = 0;
}
void Reset()
{
EnterCriticalSection(&cs_store);
for (int i = 0; i < used; i++) {
Release<T>(storage[i]);
storage[i] = T();
}
used = 0;
LeaveCriticalSection(&cs_store);
}
void Remove(int index)
{
if (index>=used)
return;
Release<T>(storage[index]);
for (int i = index; i < used; i++)
storage[i] = storage[i+1];
used--;
}
void ClearMemory(int offset, int clear_size)
{
if (clear_size < 0)
return;
EnterCriticalSection(&cs_store);
if (offset+clear_size <= size)
memset(storage+offset, 0, clear_size * sizeof(T)); // jichi 11/30/2013: This is the original code of ITH
LeaveCriticalSection(&cs_store);
//else __asm int 3
}
int AddToStore(T *con,int amount)
{
if (amount <= 0 || con == 0)
return 0;
int status = 0;
EnterCriticalSection(&cs_store);
if (amount + used + 2 >= size) {
while (amount + used + 2 >= size)
size<<=1;
T *temp;
if (size * sizeof(T) < 0x1000000) {
temp = new T[size];
if (size > used)
ITH_MEMSET_HEAP(temp, 0, (size - used) * sizeof(T)); // jichi 9/25/2013: zero memory
memcpy(temp, storage, used * sizeof(T));
} else {
size = default_size;
temp = new T[size];
ITH_MEMSET_HEAP(temp, 0, sizeof(T) * size); // jichi 9/25/2013: zero memory
used = 0;
status = 1;
}
delete[] storage;
storage = temp;
}
memcpy(storage+used, con, amount * sizeof(T));
used += amount;
LeaveCriticalSection(&cs_store);
return status;
}
int Find(const T &item, int start = 0, DWORD control = 0)
{
int c = -1;
for (int i=start; i < used; i++)
if (fCmp(storage[i],item,control)) {
c=i;
break;
}
//if (storage[i]==item) {c=i;break;}
return c;
}
int Used() const { return used; }
T *Storage() const { return storage; }
void LockVector() { EnterCriticalSection(&cs_store); }
void UnlockVector() { LeaveCriticalSection(&cs_store); }
protected:
CRITICAL_SECTION cs_store;
int size,
used;
T *storage;
fComp fCmp;
};
// EOF
/*
#ifndef ITH_STACK
#define ITH_STACK
template<class T, int default_size>
class MyStack
{
public:
MyStack(): index(0) {}
void push_back(const T& e)
{
if (index<default_size)
s[index++]=e;
}
void pop_back()
{
index--;
}
T& back()
{
return s[index-1];
}
T& operator[](int i) {return s[i];}
int size() {return index;}
private:
int index;
T s[default_size];
};
#endif
*/
#pragma once
// mono/funcinfo.h
// 12/26/2014
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
//#include "ith/import/mono/types.h"
// MonoString* mono_string_new (MonoDomain *domain,
// const char *text);
// MonoString* mono_string_new_len (MonoDomain *domain,
// const char *text,
// guint length);
// MonoString* mono_string_new_size (MonoDomain *domain,
// gint32 len);
// MonoString* mono_string_new_utf16 (MonoDomain *domain,
// const guint16 *text,
// gint32 len);
// MonoString* mono_string_from_utf16 (gunichar2 *data);
// mono_unichar2* mono_string_to_utf16 (MonoString *s);
// char* mono_string_to_utf8 (MonoString *s);
// gboolean mono_string_equal (MonoString *s1,
// MonoString *s2);
// guint mono_string_hash (MonoString *s);
// MonoString* mono_string_intern (MonoString *str);
// MonoString* mono_string_is_interned (MonoString *o);
// MonoString* mono_string_new_wrapper (const char *text);
// gunichar2* mono_string_chars (MonoString *s);
// int mono_string_length (MonoString *s);
// gunichar2* mono_unicode_from_external (const gchar *in, gsize *bytes);
// gchar* mono_unicode_to_external (const gunichar2 *uni);
// gchar* mono_utf8_from_external (const gchar *in);
struct MonoFunction {
const wchar_t *hookName;
const char *functionName;
size_t textIndex; // argument index, starting from 0
size_t lengthIndex; // argument index, start from 0
unsigned long hookType; // HookParam type
void *text_fun; // HookParam::text_fun_t
};
#define MONO_FUNCTIONS_INITIALIZER \
{ L"mono_string_to_utf8", "mono_string_to_utf8", 0, 0, USING_UNICODE, SpecialHookMonoString } \
, { L"mono_string_to_utf16", "mono_string_to_utf16", 0, 0, USING_UNICODE, SpecialHookMonoString } \
, { L"mono_utf8_from_external", "mono_utf8_from_external", 1, 0, USING_STRING|USING_UTF8, nullptr } \
, { L"mono_string_from_utf16", "mono_string_from_utf16", 1, 0, USING_UNICODE, nullptr } \
, { L"mono_unicode_from_external", "mono_unicode_from_external", 1, 2, USING_UNICODE, nullptr } \
, { L"mono_unicode_to_external", "mono_unicode_to_external", 1, 0, USING_UNICODE, nullptr }
// EOF
# mono.pri
# 12/26/2014 jichi
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/funcinfo.h \
$$PWD/types.h
# EOF
#pragma once
// mono/types.h
// 12/26/2014
// https://github.com/mono/mono/blob/master/mono/metadata/object.h
// http://api.xamarin.com/index.aspx?link=xhtml%3Adeploy%2Fmono-api-string.html
#include <cstdint>
// mono/io-layer/uglify.h
typedef int8_t gint8;
typedef int32_t gint32;
typedef wchar_t gunichar2; // either char or wchar_t, depending on how mono is compiled
typedef gint8 mono_byte;
typedef gunichar2 mono_unichar2;
// mono/metadata/object.h
typedef mono_byte MonoBoolean;
struct MonoArray;
struct MonoDelegate;
struct MonoException;
struct MonoString;
struct MonoThreadsSync;
struct MonoThread;
struct MonoVTable;
struct MonoObject {
MonoVTable *vtable;
MonoThreadsSync *synchronisation;
};
struct MonoString {
MonoObject object;
gint32 length;
gunichar2 chars[0];
};
// EOF
#pragma once
//#include "ith/common/const.h"
// ppsspp/funcinfo.h
// 12/26/2014
// See: https://github.com/hrydgard/ppsspp
// Core/HLE (High Level Emulator)
// - sceCcc
// #void sceCccSetTable(u32 jis2ucs, u32 ucs2jis)
// int sceCccUTF8toUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccUTF8toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccUTF16toUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccUTF16toSJIS(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccSJIStoUTF8(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccSJIStoUTF16(u32 dstAddr, u32 dstSize, u32 srcAddr)
// int sceCccStrlenUTF8(u32 strAddr)
// int sceCccStrlenUTF16(u32 strAddr)
// int sceCccStrlenSJIS(u32 strAddr)
// u32 sceCccEncodeUTF8(u32 dstAddrAddr, u32 ucs)
// void sceCccEncodeUTF16(u32 dstAddrAddr, u32 ucs)
// u32 sceCccEncodeSJIS(u32 dstAddrAddr, u32 jis)
// u32 sceCccDecodeUTF8(u32 dstAddrAddr)
// u32 sceCccDecodeUTF16(u32 dstAddrAddr)
// u32 sceCccDecodeSJIS(u32 dstAddrAddr)
// int sceCccIsValidUTF8(u32 c)
// int sceCccIsValidUTF16(u32 c)
// int sceCccIsValidSJIS(u32 c)
// int sceCccIsValidUCS2(u32 c)
// int sceCccIsValidUCS4(u32 c)
// int sceCccIsValidJIS(u32 c)
// int sceCccIsValidUnicode(u32 c)
// #u32 sceCccSetErrorCharUTF8(u32 c)
// #u32 sceCccSetErrorCharUTF16(u32 c)
// #u32 sceCccSetErrorCharSJIS(u32 c)
// u32 sceCccUCStoJIS(u32 c, u32 alt)
// u32 sceCccJIStoUCS(u32 c, u32 alt)
// - sceFont: search charCode
// int sceFontGetCharInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr)
// int sceFontGetShadowInfo(u32 fontHandle, u32 charCode, u32 charInfoPtr)
// int sceFontGetCharImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr)
// int sceFontGetShadowImageRect(u32 fontHandle, u32 charCode, u32 charRectPtr)
// int sceFontGetCharGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr)
// int sceFontGetCharGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight)
// #int sceFontSetAltCharacterCode(u32 fontLibHandle, u32 charCode)
// int sceFontGetShadowGlyphImage(u32 fontHandle, u32 charCode, u32 glyphImagePtr)
// int sceFontGetShadowGlyphImage_Clip(u32 fontHandle, u32 charCode, u32 glyphImagePtr, int clipXPos, int clipYPos, int clipWidth, int clipHeight)
// - sceKernelInterrupt
// u32 sysclib_strcat(u32 dst, u32 src)
// int sysclib_strcmp(u32 dst, u32 src)
// u32 sysclib_strcpy(u32 dst, u32 src)
// u32 sysclib_strlen(u32 src)
//
// Sample debug string:
// 006EFD8E PUSH PPSSPPWi.00832188 ASCII "sceCccEncodeSJIS(%08x, U+%04x)"
// Corresponding source code in sceCcc:
// ERROR_LOG(HLE, "sceCccEncodeSJIS(%08x, U+%04x): invalid pointer", dstAddrAddr, jis);
struct PPSSPPFunction
{
const wchar_t *hookName; // hook name
size_t argIndex; // argument index
unsigned long hookType; // hook parameter type
unsigned long hookSplit; // hook parameter split, positive: stack, negative: registers
const char *pattern; // debug string used within the function
};
// jichi 7/14/2014: UTF-8 is treated as STRING
// http://867258173.diandian.com/post/2014-06-26/40062099618
// sceFontGetCharGlyphImage_Clip
// Sample game: [KID] Monochrome: sceFontGetCharInfo, sceFontGetCharGlyphImage_Clip
//
// Example: { L"sceFontGetCharInfo", 2, USING_UNICODE, 4, "sceFontGetCharInfo(" }
// Text is at arg2, using arg1 as split
#define PPSSPP_FUNCTIONS_INITIALIZER \
{ L"sceCccStrlenSJIS", 1, USING_STRING, 0, "sceCccStrlenSJIS(" } \
, { L"sceCccStrlenUTF8", 1, USING_UTF8, 0, "sceCccStrlenUTF8(" } \
, { L"sceCccStrlenUTF16", 1, USING_UNICODE, 0, "sceCccStrlenUTF16(" } \
\
, { L"sceCccSJIStoUTF8", 3, USING_UTF8, 0, "sceCccSJIStoUTF8(" } \
, { L"sceCccSJIStoUTF16", 3, USING_STRING, 0, "sceCccSJIStoUTF16(" } \
, { L"sceCccUTF8toSJIS", 3, USING_UTF8, 0, "sceCccUTF8toSJIS(" } \
, { L"sceCccUTF8toUTF16", 3, USING_UTF8, 0, "sceCccUTF8toUTF16(" } \
, { L"sceCccUTF16toSJIS", 3, USING_UNICODE, 0, "sceCccUTF16toSJIS(" } \
, { L"sceCccUTF16toUTF8", 3, USING_UNICODE, 0, "sceCccUTF16toUTF8(" } \
\
, { L"sceFontGetCharInfo", 2, USING_UNICODE, 4, "sceFontGetCharInfo(" } \
, { L"sceFontGetShadowInfo", 2, USING_UNICODE, 4, "sceFontGetShadowInfo("} \
, { L"sceFontGetCharImageRect", 2, USING_UNICODE, 4, "sceFontGetCharImageRect(" } \
, { L"sceFontGetShadowImageRect", 2, USING_UNICODE, 4, "sceFontGetShadowImageRect(" } \
, { L"sceFontGetCharGlyphImage", 2, USING_UNICODE, 4, "sceFontGetCharGlyphImage(" } \
, { L"sceFontGetCharGlyphImage_Clip", 2, USING_UNICODE, 4, "sceFontGetCharGlyphImage_Clip(" } \
, { L"sceFontGetShadowGlyphImage", 2, USING_UNICODE, 4, "sceFontGetShadowGlyphImage(" } \
, { L"sceFontGetShadowGlyphImage_Clip", 2, USING_UNICODE, 4, "sceFontGetShadowGlyphImage_Clip(" } \
\
, { L"sysclib_strcat", 2, USING_STRING, 0, "Untested sysclib_strcat(" } \
, { L"sysclib_strcpy", 2, USING_STRING, 0, "Untested sysclib_strcpy(" } \
, { L"sysclib_strlen", 1, USING_STRING, 0, "Untested sysclib_strlen(" }
// Disabled as I am not sure how to deal with the source string
//, { L"sceCccEncodeSJIS", 2, USING_STRING, 0, "sceCccEncodeSJIS(" }
//, { L"sceCccEncodeUTF8", 2, USING_UTF8, 0, "sceCccEncodeUTF8(" }
//, { L"sceCccEncodeUTF16", 2, USING_UNICODE, 0, "sceCccEncodeUTF16(" }
//, { L"sysclib_strcmp", 2, USING_STRING, 0, "Untested sysclib_strcmp(" }
// EOF
# ppsspp.pri
# 12/26/2014 jichi
DEPENDPATH += $$PWD
HEADERS += \
$$PWD/funcinfo.h
# EOF
# ith.pro
# 10/13/2011 jichi
TEMPLATE = subdirs
# The order is important!
SUBDIRS += \
sys \
hook hookxp \
host
OTHER_FILES += dllconfig.pri
include(common/common.pri) # not used
include(import/mono/mono.pri) # not used
include(import/ppsspp/ppsspp.pri) # not used
# EOF
# sys.pro
# CONFIG += noqt noeh staticlib
# CONFIG(noeh) {
# message(CONFIG noeh)
# QMAKE_CXXFLAGS += /GR-
# QMAKE_CXXFLAGS_RTTI_ON -= /GR
# QMAKE_CXXFLAGS_STL_ON -= /EHsc
# QMAKE_CXXFLAGS_EXCEPTIONS_ON -= /EHsc
# CONFIG(dll) {
# QMAKE_LFLAGS += /ENTRY:"DllMain"
# }
# }
set(vnrsys_src
sys.h
sys.cc
)
add_library(vnrsys STATIC ${vnrsys_src})
target_compile_options(vnrsys PRIVATE
# http://msdn.microsoft.com/library/we6hfdy0.aspx
/GR- # disable RTTI
# http://msdn.microsoft.com/library/1deeycx5.aspx
# /EHs-c- # disable exception handling # CMake bug 15243: http://www.cmake.org/Bug/view.php?id=15243
$<$<CONFIG:Release>:>
$<$<CONFIG:Debug>:>
)
STRING(REPLACE "/EHsc" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
target_link_libraries(vnrsys comctl32.lib)
target_compile_definitions(vnrsys
PRIVATE
)
// sys.cc
// 8/21/2013 jichi
// Branch: ITH_SYS/SYS.cpp, rev 126
//
// 8/24/2013 TODO:
// - Clean up the code
// - Move my old create remote thread for ITH2 here
#include "ith/sys/sys.h"
//#include "ith/common/growl.h"
//#include "ith/common/except.h"
//#define ITH_SYS_SECTION L"ITH_SysSection"
#define ITH_THREADMAN_SECTION L"VNR_SYS_THREAD"
// jichi 9/28/2013: Weither use NtThread or RemoteThread
// RemoteThread works on both Windows 7 or Wine, while NtThread does not work on wine
#define ITH_ENABLE_THREADMAN (!IthIsWindows8OrGreater() && !IthIsWine())
//#define ITH_ENABLE_THREADMAN true
// Helpers
// jichi 2/3/2015: About GetVersion
// Windows XP SP3: 5.1
// Windows 7: 6.1, 0x1db10106
// Windows 8: 6.2, 0x23f00206
// Windows 10: 6.2, 0x23f00206 (build 9926):
BOOL IthIsWindowsXp()
{
static BOOL ret = -1; // cached
if (ret < 0) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439%28v=vs.85%29.aspx
DWORD v = ::GetVersion();
BYTE major = LOBYTE(LOWORD(v));
//DWORD minor = (DWORD)(HIBYTE(LOWORD(v)));
// Windows XP = 5.1
//ret = major < 6 ? 1 : 0;
ret = major < 6;
}
return ret;
}
// https://msdn.microsoft.com/en-us/library/windows/desktop/dn424972%28v=vs.85%29.aspx
// The same as IsWindows8OrGreater, which I don't know if the function is available to lower Windows.
static BOOL IthIsWindows8OrGreater() // this function is not exported
{
static BOOL ret = -1; // cached
if (ret < 0) {
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724439%28v=vs.85%29.aspx
DWORD v = ::GetVersion();
BYTE major = LOBYTE(LOWORD(v)),
minor = HIBYTE(LOWORD(v));
//DWORD minor = (DWORD)(HIBYTE(LOWORD(v)));
// Windows 8/10 = 6.2
ret = major > 6 || (major == 6 && minor >= 2);
}
return ret;
}
BOOL IthIsWine()
{
static BOOL ret = -1; // cached
if (ret < 0) {
const wchar_t *path;
wchar_t buffer[MAX_PATH];
if (UINT sz = ::GetSystemDirectoryW(buffer, MAX_PATH)) {
path = buffer;
::wcscpy(buffer + sz, L"\\winecfg.exe");
} else
path = L"C:\\Windows\\System32\\winecfg.exe";
//ITH_MSG(path);
ret = ::GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES ? TRUE : FALSE;
}
return ret;
}
// jichi 9/28/2013: prevent parallelization in wine
void IthCoolDown()
{
// http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Thread/NtDelayExecution.html
//const LONGLONG timeout = -10000; // in 100ns, i.e. 1ms
//NtDelayExecution(FALSE, (PLARGE_INTEGER)&timeout);
//NtFlushInstructionCache(NtCurrentProcess(), (LPVOID)hp.addr, hp.recover_len);
// Flush the instruction cache line, and prevent wine from rending things in parallel
if (IthIsWine())
IthSleep(1); // sleep for 1 ms
//__asm
//{
// //mov eax,0x2710 // = 10000
// mov ecx,time
// mul ecx
// neg eax
// adc edx,0
// neg edx
// push edx
// push eax
// push esp
// push 0
// call dword ptr [NtDelayExecution]
// add esp,8
//}
}
// jichi 9/23/2013: wine deficenciy on mapping sections
// Whe set to false, do not map sections.
//static bool ith_has_section = true;
//#ifdef ITH_WINE
//# include "winddk/winddk.h"
//#endif // ITH_WINE
//#define SEC_BASED 0x200000 // jichi 8/24/2013: emoved
// jichi 10/6/2013
// See: http://stackoverflow.com/questions/557081/how-do-i-get-the-hmodule-for-the-currently-executing-code
// See: http://www.codeproject.com/Articles/16598/Get-Your-DLL-s-Path-Name
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define CURRENT_MODULE_HANDLE ((HINSTANCE)&__ImageBase)
size_t IthGetCurrentModulePath(wchar_t *buf, size_t len)
{ return ::GetModuleFileNameW(CURRENT_MODULE_HANDLE, buf, len); }
// - Global variables -
#ifdef ITH_HAS_HEAP
HANDLE hHeap; // used in ith/common/memory.h
#endif // ITH_HAS_HEAP
DWORD current_process_id;
DWORD debug;
BYTE launch_time[0x10];
LPVOID page;
BYTE LeadByteTable[0x100] = {
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
2,2,2,2,2,2,2,2,2,2,2,2,2,1,1,1
};
namespace { // unnamed
WCHAR file_path[MAX_PATH] = L"\\??\\";
LPWSTR current_dir;
DWORD page_locale;
HANDLE root_obj,
dir_obj,
codepage_section,
thread_man_section;
BYTE file_info[0x1000];
// - Helper functions -
inline DWORD GetShareMemory()
{
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0x4C]
}
}
inline LARGE_INTEGER *GetTimeBias()
{ __asm mov eax,0x7ffe0020 }
//Get full path of current process.
//inline LPWSTR GetModulePath()
//{
// __asm
// {
// mov eax,fs:[0x30]
// mov eax,[eax+0xC]
// mov eax,[eax+0xC]
// mov eax,[eax+0x28]
// }
//}
// - Singleton classes -
BYTE normal_routine[0x14] = {
0x51,0x52,0x64,0x89,0x23,0x55,0xff,0xd0,0x50,0x6a,0xfe,0xff,0x15,0x14,0x00,0x00,0x00
};
BYTE except_routine[0xe0] = {
0xba,0x08,0x00,0x00,0x00,0x8b,0xc1,0x83,0xe0,0x0f,0x83,0xf8,0x0a,0x72,0x02,0x04,
0x07,0x04,0x30,0x66,0xab,0xc1,0xc9,0x04,0x4a,0x75,0xea,0xc3,0x00,0x00,0x00,0x00,
0x8b,0x44,0xe4,0x04,0x31,0xf6,0x8b,0x28,0x8b,0x4c,0xe4,0x0c,0x8b,0x99,0xb8,0x00,
0x00,0x00,0x81,0xec,0x40,0x02,0x00,0x00,0x8d,0x7c,0xe4,0x40,0x89,0xe0,0x56,0x6a,
0x1c,0x50,0x56,0x53,0x6a,0xff,0xff,0x15,0x18,0x00,0x00,0x00,0x85,0xc0,0x75,0x98,
0x89,0xe0,0x50,0x68,0x00,0x02,0x00,0x00,0x57,0x6a,0x02,0x53,0x6a,0xff,0xff,0x15,
0x18,0x00,0x00,0x00,0x85,0xc0,0x75,0xe6,0x5e,0x0f,0xc1,0xf7,0xfd,0xb0,0x5c,0x66,
0xf2,0xaf,0x66,0xc7,0x47,0x02,0x3a,0x00,0x89,0xd9,0x2b,0x0c,0xe4,0xe8,0x7e,0xff,
0xff,0xff,0x47,0x47,0x87,0xfe,0x89,0xe9,0xe8,0x73,0xff,0xff,0xff,0x47,0x47,0x31,
0xc0,0x89,0x47,0x10,0x6a,0x00,0x57,0x56,0x6a,0x00,0xfc,0xff,0x15,0x1c,0x00,0x00,
0x00,0x83,0xc8,0xff,0xeb,0xbe
};
// jichi 8/24/2013: Could be initialized using NtMapViewOfSection/ZwMapViewOfSection
// This class cannot have constructor / destructor
struct _ThreadView {
UINT_PTR mutex,
count;
DWORD proc_record[1];
};
class : private _ThreadView { // ThreadStartManager
enum {
ADDR0 = 0xD
, ADDR1 = 0x48
, ADDR2 = 0x60
, ADDR3 = 0x9D
};
public:
LPVOID GetProcAddr(HANDLE hProc)
{
AcquireLock();
DWORD pid,addr,len;
if (hProc == NtCurrentProcess())
pid = ::current_process_id;
else {
PROCESS_BASIC_INFORMATION info;
NtQueryInformationProcess(hProc, ProcessBasicInformation, &info, sizeof(info), &len);
pid=info.uUniqueProcessId;
}
pid >>= 2;
for (UINT_PTR i = 0; i < count; i++)
if (pid == (proc_record[i] & 0xfff)) {
addr = proc_record[i] & ~0xfff;
ReleaseLock();
return (LPVOID)addr;
}
len = 0x1000;
NtAllocateVirtualMemory(hProc, (PVOID *)(proc_record + count), 0, &len,
MEM_COMMIT,PAGE_EXECUTE_READWRITE);
DWORD base = proc_record[count];
proc_record[count] |= pid;
union {
LPVOID buffer;
DWORD b;
};
b = base;
LPVOID fun_table[3];
*(DWORD *)(normal_routine + ADDR0) += base;
NtWriteVirtualMemory(hProc, buffer, normal_routine, 0x14, 0);
*(DWORD *)(normal_routine + ADDR0) -= base;
b += 0x14;
fun_table[0] = NtTerminateThread;
fun_table[1] = NtQueryVirtualMemory;
fun_table[2] = MessageBoxW;
NtWriteVirtualMemory(hProc, buffer, fun_table, 0xC, 0);
b += 0xc;
*(DWORD *)(except_routine + ADDR1) += base;
*(DWORD *)(except_routine + ADDR2) += base;
*(DWORD *)(except_routine + ADDR3) += base;
NtWriteVirtualMemory(hProc, buffer, except_routine, 0xE0, 0);
*(DWORD *)(except_routine + ADDR1) -= base;
*(DWORD *)(except_routine + ADDR2) -= base;
*(DWORD *)(except_routine + ADDR3) -= base;
count++;
ReleaseLock();
return (LPVOID)base;
}
void ReleaseProcessMemory(HANDLE hProc)
{
DWORD pid,addr,len;
AcquireLock();
if (hProc==NtCurrentProcess())
pid = ::current_process_id;
else {
PROCESS_BASIC_INFORMATION info;
NtQueryInformationProcess(hProc,ProcessBasicInformation,&info,sizeof(info),&len);
pid = info.uUniqueProcessId;
}
pid >>= 2;
//NtWaitForSingleObject(thread_man_mutex,0,0);
for (UINT_PTR i = 0; i < count; i++) {
if ((proc_record[i]&0xfff) == pid) {
addr = proc_record[i] & ~0xfff;
DWORD size=0x1000;
NtFreeVirtualMemory(hProc, (PVOID *)&addr, &size, MEM_RELEASE);
count--;
for (UINT_PTR j = i; j < count; j++)
proc_record[j] = proc_record[j + 1];
proc_record[count] = 0;
ReleaseLock();
//NtReleaseMutant(thread_man_mutex,0);
return;
}
}
ReleaseLock();
//NtReleaseMutant(thread_man_mutex,0);
}
void CheckProcessMemory()
{
UINT_PTR i, j, flag, addr;
DWORD len;
CLIENT_ID id;
OBJECT_ATTRIBUTES oa = {};
HANDLE hProc;
BYTE buffer[8];
AcquireLock();
id.UniqueThread = 0;
oa.uLength = sizeof(oa);
for (i = 0; i < count ; i++) {
id.UniqueProcess = (proc_record[i]&0xfff)<<2;
addr = proc_record[i] & ~0xfff;
flag = 0;
if (NT_SUCCESS(NtOpenProcess(&hProc, PROCESS_VM_OPERATION|PROCESS_VM_READ, &oa, &id))) {
if (NT_SUCCESS(NtReadVirtualMemory(hProc, (PVOID)addr, buffer, 8, &len)))
if (::memcmp(buffer, normal_routine, 4) == 0)
flag = 1;
NtClose(hProc);
}
if (flag == 0) {
for (j = i; j < count; j++)
proc_record[j] = proc_record[j + 1];
count--;
i--;
}
}
ReleaseLock();
}
void AcquireLock()
{
LONG *p = (LONG *)&mutex;
while (_interlockedbittestandset(p,0))
YieldProcessor();
}
void ReleaseLock()
{
LONG *p = (LONG*)&mutex;
_interlockedbittestandreset(p, 0);
}
} *thread_man_ = nullptr; // global singleton
} // unnamed namespace
// - API functions -
extern "C" {
void FreeThreadStart(HANDLE hProc)
{
if (thread_man_)
::thread_man_->ReleaseProcessMemory(hProc);
}
void CheckThreadStart()
{
if (thread_man_)
::thread_man_->CheckProcessMemory();
// jichi 2/2/2015: This function is only used to wait for injected threads vnrhost.
// Sleep for 100 ms to wait for remote thread to start
//IthSleep(100);
//IthCoolDown();
}
void IthSleep(int time)
{
__asm
{
mov eax,0x2710 // jichi = 10000
mov ecx,time
mul ecx
neg eax
adc edx,0
neg edx
push edx
push eax
push esp
push 0
call dword ptr [NtDelayExecution]
add esp,8
}
}
void IthSystemTimeToLocalTime(LARGE_INTEGER *time)
{ time->QuadPart -= GetTimeBias()->QuadPart; }
int FillRange(LPCWSTR name, DWORD *lower, DWORD *upper)
{
PLDR_DATA_TABLE_ENTRY it;
LIST_ENTRY *begin;
__asm
{
mov eax,fs:[0x30]
mov eax,[eax+0xc]
mov eax,[eax+0xc]
mov it,eax
mov begin,eax
}
while (it->SizeOfImage) {
if (::_wcsicmp(it->BaseDllName.Buffer, name) == 0) {
*lower = *upper = (DWORD)it->DllBase;
MEMORY_BASIC_INFORMATION info = {};
DWORD l,size;
size = 0;
do {
NtQueryVirtualMemory(NtCurrentProcess(), (LPVOID)(*upper), MemoryBasicInformation, &info, sizeof(info), &l);
if (info.Protect&PAGE_NOACCESS) {
it->SizeOfImage=size;
break;
}
size += info.RegionSize;
*upper += info.RegionSize;
} while (size < it->SizeOfImage);
return 1;
}
it = (PLDR_DATA_TABLE_ENTRY)it->InLoadOrderModuleList.Flink;
if (it->InLoadOrderModuleList.Flink == begin)
break;
}
return 0;
}
DWORD SearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length) // KMP
{
__asm
{
mov eax,search_length
alloc:
push 0
sub eax,1
jnz alloc
mov edi,search
mov edx,search_length
mov ecx,1
xor esi,esi
build_table:
mov al,byte ptr [edi+esi]
cmp al,byte ptr [edi+ecx]
sete al
test esi,esi
jz pre
test al,al
jnz pre
mov esi,[esp+esi*4-4]
jmp build_table
pre:
test al,al
jz write_table
inc esi
write_table:
mov [esp+ecx*4],esi
inc ecx
cmp ecx,edx
jb build_table
mov esi,base
xor edx,edx
mov ecx,edx
matcher:
mov al,byte ptr [edi+ecx]
cmp al,byte ptr [esi+edx]
sete al
test ecx,ecx
jz match
test al,al
jnz match
mov ecx, [esp+ecx*4-4]
jmp matcher
match:
test al,al
jz pre2
inc ecx
cmp ecx,search_length
je finish
pre2:
inc edx
cmp edx,base_length // search_length
jb matcher
mov edx,search_length
dec edx
finish:
mov ecx,search_length
sub edx,ecx
lea eax,[edx+1]
lea ecx,[ecx*4]
add esp,ecx
}
}
// jichi 2/5/2014: '?' = 0xff
// See: http://sakuradite.com/topic/124
DWORD SearchPatternEx(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length, BYTE wildcard) // KMP
{
__asm
{
// jichi 2/5/2014 BEGIN
mov bl,wildcard
// jichi 2/5/2014 END
mov eax,search_length
alloc:
push 0
sub eax,1
jnz alloc // jichi 2/5/2014: this will also set %eax to zero
mov edi,search
mov edx,search_length
mov ecx,1
xor esi,esi
build_table:
mov al,byte ptr [edi+esi]
cmp al,byte ptr [edi+ecx]
sete al
test esi,esi
jz pre
test al,al
jnz pre
mov esi,[esp+esi*4-4]
jmp build_table
pre:
test al,al
jz write_table
inc esi
write_table:
mov [esp+ecx*4],esi
inc ecx
cmp ecx,edx
jb build_table
mov esi,base
xor edx,edx
mov ecx,edx
matcher:
mov al,byte ptr [edi+ecx] // search
// jichi 2/5/2014 BEGIN
mov bh,al // save loaded byte to reduce cache access. %ah is not used and always zero
cmp al,bl // %bl is the wildcard byte
sete al
test al,al
jnz wildcard_matched
mov al,bh // restore the loaded byte
// jichi 2/5/2014 END
cmp al,byte ptr [esi+edx] // base
sete al
// jichi 2/5/2014 BEGIN
wildcard_matched:
// jichi 2/5/2014 END
test ecx,ecx
jz match
test al,al
jnz match
mov ecx, [esp+ecx*4-4]
jmp matcher
match:
test al,al
jz pre2
inc ecx
cmp ecx,search_length
je finish
pre2:
inc edx
cmp edx,base_length // search_length
jb matcher
mov edx,search_length
dec edx
finish:
mov ecx,search_length
sub edx,ecx
lea eax,[edx+1]
lea ecx,[ecx*4]
add esp,ecx
}
}
DWORD IthGetMemoryRange(LPCVOID mem, DWORD *base, DWORD *size)
{
DWORD r;
MEMORY_BASIC_INFORMATION info;
NtQueryVirtualMemory(NtCurrentProcess(), const_cast<LPVOID>(mem), MemoryBasicInformation, &info, sizeof(info), &r);
if (base)
*base = (DWORD)info.BaseAddress;
if (size)
*size = info.RegionSize;
return (info.Type&PAGE_NOACCESS) == 0;
}
// jichi 9/25/2013
// See: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.nls/doc/nlsgdrf/multi-byte_widechar_subr.htm
// SJIS->Unicode. 'mb' must be null-terminated. 'wc' should have enough space ( 2*strlen(mb) is safe).
//#ifdef ITH_WINE
//int MB_WC(char *mb, wchar_t *wc)
//{ return mbstowcs(wc, mb, 0x100); }
//
//#else
int MB_WC(char *mb, wchar_t *wc)
{
__asm
{
mov esi,mb
mov edi,wc
mov edx,page
lea ebx,LeadByteTable
add edx,0x220
push 0
_mb_translate:
movzx eax,word ptr [esi]
test al,al
jz _mb_fin
movzx ecx,al
xlat
test al,1
cmovnz cx, word ptr [ecx*2+edx-0x204]
jnz _mb_next
mov cx,word ptr [ecx*2+edx]
mov cl,ah
mov cx, word ptr [ecx*2+edx]
_mb_next:
mov [edi],cx
add edi,2
movzx eax,al
add esi,eax
inc dword ptr [esp]
jmp _mb_translate
_mb_fin:
pop eax
}
}
// Count characters of 'mb' string. 'mb_length' is max length.
// jichi 9/25/2013: This function is not used
//int MB_WC_count(char *mb, int mb_length)
//{
// __asm
// {
// xor eax,eax
// xor edx,edx
// mov esi,mb
// mov edi,mb_length
// lea ebx,LeadByteTable
//_mbc_count:
// mov dl,byte ptr [esi]
// test dl,dl
// jz _mbc_finish
// movzx ecx, byte ptr [ebx+edx]
// add esi,ecx
// inc eax
// sub edi,ecx
// ja _mbc_count
//_mbc_finish:
// }
//}
// jichi 9/25/2013
// See: http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.nls/doc/nlsgdrf/multi-byte_widechar_subr.htm
// Unicode->SJIS. Analogous to MB_WC.
//#ifdef ITH_WINE
//int WC_MB(wchar_t *wc, char *mb)
//{ return wcstombs(mb, wc, 0x100); }
//
//#else
int WC_MB(wchar_t *wc, char *mb)
{
__asm
{
mov esi,wc
mov edi,mb
mov edx,page
add edx,0x7C22
xor ebx,ebx
_wc_translate:
movzx eax,word ptr [esi]
test eax,eax
jz _wc_fin
mov cx,word ptr [eax*2+edx]
test ch,ch
jz _wc_single
mov [edi+ebx],ch
inc ebx
_wc_single:
mov [edi+ebx],cl
inc ebx
add esi,2
jmp _wc_translate
_wc_fin:
mov eax,ebx
}
}
//Initialize environment for NT native calls. Not thread safe so only call it once in one module.
//1. Create new heap. Future memory requests are handled by this heap.
//Destroying this heap will completely release all dynamically allocated memory, thus prevent memory leaks on unload.
//2. Create handle to root directory of process objects (section/event/mutex/semaphore).
//NtCreate* calls will use this handle as base directory.
//3. Load SJIS code page. First check for Japanese locale. If not then load from 'C_932.nls' in system folder.
//MB_WC & WC_MB use this code page for translation.
//4. Locate current NT path (start with \??\).
//NtCreateFile requires full path or a root handle. But this handle is different from object.
//5. Map shared memory for ThreadStartManager into virtual address space.
//This will allow IthCreateThread function properly.
BOOL IthInitSystemService()
{
PPEB peb;
//NTSTATUS status;
DWORD size;
ULONG LowFragmentHeap;
UNICODE_STRING us;
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
IO_STATUS_BLOCK ios;
HANDLE codepage_file;
LARGE_INTEGER sec_size = {0x1000, 0};
__asm
{
mov eax,fs:[0x18]
mov ecx,[eax+0x20]
mov eax,[eax+0x30]
mov peb,eax
mov current_process_id,ecx
}
debug = peb->BeingDebugged;
LowFragmentHeap = 2;
#ifdef ITH_HAS_HEAP
::hHeap = RtlCreateHeap(0x1002, 0, 0, 0, 0, 0);
RtlSetHeapInformation(::hHeap, HeapCompatibilityInformation, &LowFragmentHeap, sizeof(LowFragmentHeap));
#endif // ITH_HAS_HEAP
LPWSTR t = nullptr, // jichi: path to system32, such as "c:\windows\system32"
obj = nullptr; // jichi: path to current kernel session, such as "Sessions\\1\\BaseNamedObjects"
// jichi 9/22/2013: This would crash wine with access violation exception.
if (!IthIsWine()) {
// jichi 9/22/2013: For ChuSingura46+1 on Windows 7
// t = L"C:\\Windows\\system32";
// obj = L"\\Sessions\\1\\BaseNamedObjects";
// On Windows XP
// t = L"C:\\WINDOWS\\system32";
// obj = L"\\BaseNamedObjects";
MEMORY_BASIC_INFORMATION info;
if (!NT_SUCCESS(NtQueryVirtualMemory(NtCurrentProcess(), peb->ReadOnlySharedMemoryBase, MemoryBasicInformation, &info, sizeof(info), &size)))
return FALSE;
DWORD base = (DWORD)peb->ReadOnlySharedMemoryBase;
DWORD end = base + info.RegionSize - 0x40;
static WCHAR system32[] = L"system32";
for (;base < end; base += 2)
if (::memcmp((PVOID)base, system32, 0x10) == 0) {
t = (LPWSTR)base;
while (*t-- != L':');
obj = (LPWSTR)base;
while (*obj != L'\\') obj++;
break;
}
if (base == end)
return FALSE;
}
//ITH_MSG(t);
//ITH_MSG(obj);
LDR_DATA_TABLE_ENTRY *ldr_entry = (LDR_DATA_TABLE_ENTRY*)peb->Ldr->InLoadOrderModuleList.Flink;
wcscpy(file_path + 4, ldr_entry->FullDllName.Buffer);
current_dir = wcsrchr(file_path,L'\\') + 1;
*current_dir = 0;
RtlInitUnicodeString(&us, file_path);
if (!NT_SUCCESS(NtOpenFile(&dir_obj,FILE_LIST_DIRECTORY|FILE_TRAVERSE|SYNCHRONIZE,
&oa,&ios,FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT)))
return FALSE;
// jichi 9/22/2013: Get kernel object session ID
// See: http://www.brianbondy.com/blog/id/100/
// It seems that on sessionId is 0 on Windows XP, and 1 on Windows Vista and later
// I assume that sessionId is in [0,9]
// For ChuSingura46+1 on Windows 7
// obj = L"\\Sessions\\1\\BaseNamedObjects";
// On Windows XP
// obj = L"\\BaseNamedObjects";
//ITH_MSG(obj);
{
if (obj)
RtlInitUnicodeString(&us, obj);
else { // jichi ITH is on Wine
// Get session ID in PEB
// See: http://msdn.microsoft.com/en-us/library/bb432286%28v=vs.85%29.aspx
DWORD sessionId = peb->SessionId;
if (!sessionId) // Windows XP
RtlInitUnicodeString(&us, L"\\BaseNamedObjects");
else { // Windows Vista +
wchar_t path[] = L"\\Sessions\\0\\BaseNamedObjects";
path[10] += (wchar_t)sessionId; // replace 0 with the session ID
RtlInitUnicodeString(&us, path);
}
}
}
if (!NT_SUCCESS(NtOpenDirectoryObject(&::root_obj, READ_CONTROL|0xF, &oa)))
return FALSE;
::page = peb->InitAnsiCodePageData;
// jichi 9/23/2013: Access violation on Wine
if (IthIsWine())
// One wine, there is no C_932.nls
//page_locale = 0x4e4; // 1252, English
//page_locale = GetACP(); // This will return 932 when LC_ALL=ja_JP.UTF-8 on wine
// Always set locale to CP932 on Wine, since C_932.nls could be missing.
::page_locale = 0x3a4; // = 932
else
::page_locale = *(DWORD *)page >> 16;
if (::page_locale == 0x3a4) {
oa.hRootDirectory = ::root_obj;
oa.uAttributes |= OBJ_OPENIF;
} else { // Unreachable or wine
//#ifdef ITH_WINE
// // jichi 9/22/2013: For ChuSingura46+1 on Windows 7
// //t = L"C:\\Windows\\system32";
// wchar_t buffer[MAX_PATH];
// if (!t) { // jichi 9/22/2013: ITH is one wine
// if (UINT sz = ::GetSystemDirectoryW(buffer, MAX_PATH)) {
// buffer[sz] = 0;
// t = buffer;
// } else
// t = L"C:\\Windows\\System32"; // jichi 9/29/2013: sth is wrong here
// }
//#endif // ITH_WINE
::wcscpy(file_path + 4, t);
t = file_path;
while(*++t);
if (*(t-1)!=L'\\')
*t++=L'\\';
::wcscpy(t,L"C_932.nls");
RtlInitUnicodeString(&us, file_path);
if (!NT_SUCCESS(NtOpenFile(&codepage_file, FILE_READ_DATA, &oa, &ios,FILE_SHARE_READ,0)))
return FALSE;
oa.hRootDirectory = ::root_obj;
oa.uAttributes |= OBJ_OPENIF;
RtlInitUnicodeString(&us, L"JPN_CodePage");
if (!NT_SUCCESS(NtCreateSection(&codepage_section, SECTION_MAP_READ,
&oa,0, PAGE_READONLY, SEC_COMMIT, codepage_file)))
return FALSE;
NtClose(codepage_file);
size = 0;
::page = nullptr;
if (!NT_SUCCESS(NtMapViewOfSection(::codepage_section, NtCurrentProcess(),
&::page,
0, 0, 0, &size, ViewUnmap, 0,
PAGE_READONLY)))
return FALSE;
}
if (ITH_ENABLE_THREADMAN) {
RtlInitUnicodeString(&us, ITH_THREADMAN_SECTION);
if (!NT_SUCCESS(NtCreateSection(&thread_man_section, SECTION_ALL_ACCESS, &oa, &sec_size,
PAGE_EXECUTE_READWRITE, SEC_COMMIT, 0)))
return FALSE;
size = 0;
// http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/NT%20Objects/Section/NtMapViewOfSection.html
thread_man_ = nullptr;
if (!NT_SUCCESS(NtMapViewOfSection(thread_man_section, NtCurrentProcess(),
(LPVOID *)&thread_man_,
0,0,0, &size, ViewUnmap, 0,
PAGE_EXECUTE_READWRITE)))
return FALSE;
}
return TRUE;
}
//Release resources allocated by IthInitSystemService.
//After destroying the heap, all memory allocated by ITH module is returned to system.
void IthCloseSystemService()
{
if (::page_locale != 0x3a4) {
NtUnmapViewOfSection(NtCurrentProcess(), ::page);
NtClose(::codepage_section);
}
if (ITH_ENABLE_THREADMAN) {
NtUnmapViewOfSection(NtCurrentProcess(), ::thread_man_);
NtClose(::thread_man_section);
}
NtClose(::root_obj);
#ifdef ITH_HAS_HEAP
RtlDestroyHeap(::hHeap);
#endif // ITH_HAS_HEAP
}
//Check for existence of a file in current folder. Thread safe after init.
//For ITH main module, it's ITH folder. For target process it's the target process's current folder.
BOOL IthCheckFile(LPCWSTR file)
{
//return PathFileExistsW(file); // jichi: need Shlwapi.lib
//return (dwAttrib != INVALID_FILE_ATTRIBUTES && !(dwAttrib & FILE_ATTRIBUTE_DIRECTORY));
//return GetFileAttributesW(file) != INVALID_FILE_ATTRIBUTES; // jichi: does not consider the current app's path
// jichi 9/22/2013: Following code does not work in Wine
// See: http://stackoverflow.com/questions/3828835/how-can-we-check-if-a-file-exists-or-not-using-win32-program
//WIN32_FIND_DATA FindFileData;
//HANDLE handle = FindFirstFileW(file, &FindFileData);
//if (handle != INVALID_HANDLE_VALUE) {
// FindClose(handle);
// return TRUE;
//}
//return FALSE;
if (IthIsWine()) {
HANDLE hFile = CreateFileW(file, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, 0);
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
return TRUE;
} else if (!wcschr(file, L':')) { // jichi: this is relative path
// jichi 9/22/2013: Change current directory to the same as main module path
// Otherwise NtFile* would not work for files with relative paths.
if (const wchar_t *path = GetMainModulePath()) // path to VNR's python exe
if (const wchar_t *base = wcsrchr(path, L'\\')) {
size_t dirlen = base - path + 1;
if (dirlen + wcslen(file) < MAX_PATH) {
wchar_t buf[MAX_PATH];
wcsncpy(buf, path, dirlen);
wcscpy(buf + dirlen, file);
return IthCheckFile(buf);
}
}
}
} else { // not wine
HANDLE hFile;
IO_STATUS_BLOCK isb;
UNICODE_STRING us;
RtlInitUnicodeString(&us, file);
OBJECT_ATTRIBUTES oa = { sizeof(oa), dir_obj, &us, 0, 0, 0};
// jichi 9/22/2013: Following code does not work in Wine
if (NT_SUCCESS(NtCreateFile(&hFile, FILE_READ_DATA, &oa, &isb, 0, 0, FILE_SHARE_READ, FILE_OPEN, 0, 0, 0))) {
NtClose(hFile);
return TRUE;
}
}
return FALSE;
//return IthGetFileInfo(file,file_info);
//wcscpy(current_dir,file);
}
//Check for existence of files in current folder.
//Unlike IthCheckFile, this function allows wildcard character.
BOOL IthFindFile(LPCWSTR file)
{
NTSTATUS status;
HANDLE h;
UNICODE_STRING us;
OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
us.Buffer = const_cast<LPWSTR>(file);
LPCWSTR path = wcsrchr(file, L'\\');
if (path) {
us.Length = (path - file) << 1;
us.MaximumLength = us.Length;
} else {
us.Length = 0;
us.MaximumLength = 0;
}
IO_STATUS_BLOCK ios;
if (NT_SUCCESS(NtOpenFile(&h,FILE_LIST_DIRECTORY|SYNCHRONIZE,
&oa,&ios,FILE_SHARE_READ,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT))) {
BYTE info[0x400];
if (path)
RtlInitUnicodeString(&us, path + 1);
else
RtlInitUnicodeString(&us, file);
status = NtQueryDirectoryFile(h,0,0,0,&ios,info,0x400,FileBothDirectoryInformation,TRUE,&us,TRUE);
NtClose(h);
return NT_SUCCESS(status);
}
return FALSE;
}
//Analogous to IthFindFile, but return detail information in 'info'.
BOOL IthGetFileInfo(LPCWSTR file, LPVOID info, DWORD size)
{
NTSTATUS status;
HANDLE h;
UNICODE_STRING us;
LPCWSTR path = wcsrchr(file, L'\\');
us.Buffer = const_cast<LPWSTR>(file);
if (path) {
us.Length = (path - file) << 1;
us.MaximumLength = us.Length;
} else {
us.Length = 0;
us.MaximumLength = 0;
}
//RtlInitUnicodeString(&us,file);
OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
IO_STATUS_BLOCK ios;
if (NT_SUCCESS(NtOpenFile(&h,FILE_LIST_DIRECTORY|SYNCHRONIZE,
&oa,&ios,FILE_SHARE_READ,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT))) {
RtlInitUnicodeString(&us,file);
status = NtQueryDirectoryFile(h,0,0,0,&ios,info,size,FileBothDirectoryInformation,0,&us,0);
status = NT_SUCCESS(status);
NtClose(h);
} else
status = FALSE;
return status;
}
//Check for existence of a file with full NT path(start with \??\).
BOOL IthCheckFileFullPath(LPCWSTR file)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, file);
OBJECT_ATTRIBUTES oa = { sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
if (NT_SUCCESS(NtCreateFile(&hFile,FILE_READ_DATA,&oa,&isb,0,0,FILE_SHARE_READ,FILE_OPEN,0,0,0))) {
NtClose(hFile);
return TRUE;
} else
return FALSE;
}
//Create or open file in current folder. Analogous to Win32 CreateFile.
//option: GENERIC_READ / GENERIC_WRITE.
//share: FILE_SHARE_READ / FILE_SHARE_WRITE / FILE_SHARE_DELETE. 0 for exclusive access.
//disposition: FILE_OPEN / FILE_OPEN_IF.
//Use FILE_OPEN instead of OPEN_EXISTING and FILE_OPEN_IF for CREATE_ALWAYS.
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = { sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0 };
HANDLE hFile;
IO_STATUS_BLOCK isb;
return NT_SUCCESS(NtCreateFile(&hFile,
option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
&oa,&isb,0,0,share,disposition,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
hFile : INVALID_HANDLE_VALUE;
}
//Create a directory file in current folder.
HANDLE IthCreateDirectory(LPCWSTR name)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us,name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), dir_obj, &us, OBJ_CASE_INSENSITIVE, 0, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
return NT_SUCCESS(NtCreateFile(&hFile,FILE_LIST_DIRECTORY|FILE_TRAVERSE|SYNCHRONIZE,&oa,&isb,0,0,
FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN_IF,FILE_DIRECTORY_FILE|FILE_SYNCHRONOUS_IO_NONALERT,0,0)) ?
hFile : INVALID_HANDLE_VALUE;
}
HANDLE IthCreateFileInDirectory(LPCWSTR name, HANDLE dir, DWORD option, DWORD share, DWORD disposition)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us,name);
if (dir == 0) dir = dir_obj;
OBJECT_ATTRIBUTES oa = {sizeof(oa), dir, &us, OBJ_CASE_INSENSITIVE, 0, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
return NT_SUCCESS(NtCreateFile(&hFile,
option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
&oa,&isb,0,0,share,disposition,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
hFile : INVALID_HANDLE_VALUE;
}
//Analogous to IthCreateFile, but with full NT path.
HANDLE IthCreateFileFullPath(LPCWSTR path, DWORD option, DWORD share, DWORD disposition)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us,path);
OBJECT_ATTRIBUTES oa = {sizeof(oa), 0, &us, OBJ_CASE_INSENSITIVE, 0, 0};
HANDLE hFile;
IO_STATUS_BLOCK isb;
return NT_SUCCESS(NtCreateFile(&hFile,
option|FILE_READ_ATTRIBUTES|SYNCHRONIZE,
&oa,&isb,0,0,share,disposition,
FILE_SYNCHRONOUS_IO_NONALERT|FILE_NON_DIRECTORY_FILE,0,0)) ?
hFile : INVALID_HANDLE_VALUE;
}
//Create section object for sharing memory between processes.
//Similar to CreateFileMapping.
HANDLE IthCreateSection(LPCWSTR name, DWORD size, DWORD right)
{
// jichi 9/25/2013: GENERIC_ALL does NOT work one wine
// See ZwCreateSection: http://msdn.microsoft.com/en-us/library/windows/hardware/ff566428%28v=vs.85%29.aspx
//#ifdef ITH_WINE
enum { DesiredAccess = SECTION_ALL_ACCESS };
//#else
// enum { DesiredAccess = GENERIC_ALL }; // jichi 9/25/2013: not sure whhy ITH is usin GENERIC_ALL
//#endif // ITH_WINE
#define eval (NT_SUCCESS(NtCreateSection(&hSection, DesiredAccess, poa, &s, \
right, SEC_COMMIT, 0)) ? hSection : INVALID_HANDLE_VALUE)
HANDLE hSection;
LARGE_INTEGER s = {size, 0};
OBJECT_ATTRIBUTES *poa = nullptr;
// jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
// is pointed to freed object on the stack?! This will crash wine!
if (name) {
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us,OBJ_OPENIF,0,0};
poa = &oa;
return eval;
} else
return eval;
#undef retval
}
//Create event object. Similar to CreateEvent.
HANDLE IthCreateEvent(LPCWSTR name, DWORD auto_reset, DWORD init_state)
{
#define eval (NT_SUCCESS(NtCreateEvent(&hEvent, EVENT_ALL_ACCESS, poa, auto_reset, init_state)) ? \
hEvent : INVALID_HANDLE_VALUE)
HANDLE hEvent;
OBJECT_ATTRIBUTES *poa = nullptr;
// jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
// is pointed to freed object on the stack?! This will crash wine!
if (name) {
UNICODE_STRING us;
RtlInitUnicodeString(&us,name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, OBJ_OPENIF, 0, 0};
poa = &oa;
return eval;
} else
return eval;
#undef eval
}
HANDLE IthOpenEvent(LPCWSTR name)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = { sizeof(oa), root_obj, &us, 0, 0, 0 };
HANDLE hEvent;
return NT_SUCCESS(NtOpenEvent(&hEvent, EVENT_ALL_ACCESS, &oa)) ?
hEvent : INVALID_HANDLE_VALUE;
}
void IthSetEvent(HANDLE hEvent) { NtSetEvent(hEvent, 0); }
void IthResetEvent(HANDLE hEvent) { NtClearEvent(hEvent); }
//Create mutex object. Similar to CreateMutex.
//If 'exist' is not null, it will be written 1 if mutex exist.
HANDLE IthCreateMutex(LPCWSTR name, BOOL InitialOwner, DWORD *exist)
{
#define eval NtCreateMutant(&hMutex, MUTEX_ALL_ACCESS, poa, InitialOwner)
UNICODE_STRING us;
HANDLE hMutex;
NTSTATUS status;
OBJECT_ATTRIBUTES *poa = nullptr;
// jichi 9/25/2013: What the fxxx?! poa in the orignal source code of ITH
// is pointed to freed object on the stack?! This will crash wine!
if (name) {
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, OBJ_OPENIF, 0, 0};
poa = &oa;
status = eval;
} else
status = eval;
if (NT_SUCCESS(status)) {
if (exist)
*exist = status == STATUS_OBJECT_NAME_EXISTS;
return hMutex;
} else
return INVALID_HANDLE_VALUE;
#undef eval
}
HANDLE IthOpenMutex(LPCWSTR name)
{
UNICODE_STRING us;
RtlInitUnicodeString(&us, name);
OBJECT_ATTRIBUTES oa = {sizeof(oa), root_obj, &us, 0, 0, 0};
HANDLE hMutex;
if (NT_SUCCESS(NtOpenMutant(&hMutex, MUTEX_ALL_ACCESS, &oa)))
return hMutex;
else
return INVALID_HANDLE_VALUE;
}
BOOL IthReleaseMutex(HANDLE hMutex)
{ return NT_SUCCESS(NtReleaseMutant(hMutex, 0)); }
//Create new thread. 'hProc' must have following right.
//PROCESS_CREATE_THREAD, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE.
HANDLE IthCreateThread(LPCVOID start_addr, DWORD param, HANDLE hProc)
{
HANDLE hThread;
// jichi 9/27/2013: NtCreateThread is not implemented in Wine 1.7
if (thread_man_) { // Windows XP
// jichi 9/29/2013: Reserved && commit stack size
// See: http://msdn.microsoft.com/en-us/library/windows/desktop/aa366803%28v=vs.85%29.aspx
// See: http://msdn.microsoft.com/en-us/library/ms810627.aspx
enum { DEFAULT_STACK_LIMIT = 0x400000 };
enum { DEFAULT_STACK_COMMIT = 0x10000 };
enum { PAGE_SIZE = 0x1000 };
CLIENT_ID id;
LPVOID protect;
USER_STACK stack = {};
CONTEXT ctx = {CONTEXT_FULL};
DWORD size = DEFAULT_STACK_LIMIT,
commit = DEFAULT_STACK_COMMIT;
if (!NT_SUCCESS(NtAllocateVirtualMemory(hProc, &stack.ExpandableStackBottom, 0, &size, MEM_RESERVE, PAGE_READWRITE)))
return INVALID_HANDLE_VALUE;
stack.ExpandableStackBase = (char *)stack.ExpandableStackBottom + size;
stack.ExpandableStackLimit = (char *)stack.ExpandableStackBase - commit;
size = PAGE_SIZE;
commit += size;
protect = (char *)stack.ExpandableStackBase - commit;
NtAllocateVirtualMemory(hProc, &protect, 0, &commit, MEM_COMMIT, PAGE_READWRITE);
DWORD oldAccess; // jichi 9/29/2013: unused
NtProtectVirtualMemory(hProc, &protect, &size, PAGE_READWRITE|PAGE_GUARD, &oldAccess);
ctx.SegGs = 0;
ctx.SegFs = 0x38;
ctx.SegEs = 0x20;
ctx.SegDs = 0x20;
ctx.SegSs = 0x20;
ctx.SegCs = 0x18;
ctx.EFlags = 0x3000;
ctx.Eip = (DWORD)thread_man_->GetProcAddr(hProc);
ctx.Eax = (DWORD)start_addr;
ctx.Ecx = ctx.Eip + 0x40;
ctx.Edx = 0xffffffff;
ctx.Esp = (DWORD)stack.ExpandableStackBase - 0x10;
ctx.Ebp = param;
// NTSYSAPI
// NTSTATUS
// NTAPI
// NtCreateThread(
// _Out_ PHANDLE ThreadHandle,
// _In_ ACCESS_MASK DesiredAccess,
// _In_ POBJECT_ATTRIBUTES ObjectAttributes,
// _In_ HANDLE ProcessHandle,
// _Out_ PCLIENT_ID ClientId,
// _In_ PCONTEXT ThreadContext,
// _In_ PUSER_STACK UserStack,
// _In_ BOOLEAN CreateSuspended
// );
if (NT_SUCCESS(NtCreateThread(
&hThread, // _Out_ PHANDLE ThreadHandle,
THREAD_ALL_ACCESS, // _In_ ACCESS_MASK DesiredAccess,
nullptr, // _In_ POBJECT_ATTRIBUTES ObjectAttributes,
hProc, // _In_ HANDLE ProcessHandle,
&id, // _Out_ PCLIENT_ID ClientId,
&ctx, // _In_ PCONTEXT ThreadContext,
&stack, // _In_ PUSER_STACK UserStack,
TRUE // _In_ BOOLEAN CreateSuspended
))) {
// On x64 Windows, NtCreateThread in ntdll calls NtCreateThread in ntoskrnl via WOW64,
// which maps 32-bit system call to the correspond 64-bit version.
// This layer doesn't correctly copy whole CONTEXT structure, so we must set it manually
// after the thread is created.
// On x86 Windows, this step is not necessary.
NtSetContextThread(hThread, &ctx);
NtResumeThread(hThread, 0);
} else
hThread = INVALID_HANDLE_VALUE;
} else {
// jichi 9/27/2013: CreateRemoteThread works on both Wine and Windows 7
// Use CreateRemoteThread instead
// FIXME 10/5/2031: Though sometimes works, CreateRemoteThread randomly crashes on wine.
// See:
// - http://www.unknowncheats.me/forum/c-and-c/64775-createremotethread-dll-injection.html
// - http://source.winehq.org/WineAPI/CreateRemoteThread.html
// - http://msdn.microsoft.com/en-us/library/windows/desktop/ms682437%28v=vs.85%29.aspx
// HANDLE WINAPI CreateRemoteThread(
// _In_ HANDLE hProcess,
// _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
// _In_ SIZE_T dwStackSize,
// _In_ LPTHREAD_START_ROUTINE lpStartAddress,
// _In_ LPVOID lpParameter,
// _In_ DWORD dwCreationFlags,
// _Out_ LPDWORD lpThreadId
// );
//ITH_TRY {
if (hProc == INVALID_HANDLE_VALUE)
hProc = GetCurrentProcess();
//DWORD dwThreadId;
hThread = CreateRemoteThread(
hProc, // _In_ HANDLE hProcess,
nullptr, // _In_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
0, // _In_ SIZE_T dwStackSize,
(LPTHREAD_START_ROUTINE)start_addr, // _In_ LPTHREAD_START_ROUTINE lpStartAddress,
(LPVOID)param, // _In_ LPVOID lpParameter,
0, //STACK_SIZE_PARAM_IS_A_RESERVATION // _In_ DWORD dwCreationFlags,
nullptr // _Out_ LPDWORD lpThreadId
);
if (!hThread) // jichi: this function returns nullptr instead of -1
hThread = INVALID_HANDLE_VALUE;
//} ITH_EXCEPT {
// ITH_WARN(L"exception");
// hThread = INVALID_HANDLE_VALUE;
//}
}
/*
else {
// jichi 9/29/2013: Also work on Wine and Windows 7
// See: http://waleedassar.blogspot.com/2012/06/createremotethread-vs.html
CLIENT_ID id;
//DWORD size = DEFAULT_STACK_LIMIT,
// commit = DEFAULT_STACK_COMMIT;
DWORD reserve = 0,
commit = 0;
// http://undocumented.ntinternals.net/UserMode/Undocumented%20Functions/Executable%20Images/RtlCreateUserThread.html
// NTSYSAPI
// NTSTATUS
// NTAPI
// RtlCreateUserThread(
// IN HANDLE ProcessHandle,
// IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
// IN BOOLEAN CreateSuspended,
// IN ULONG StackZeroBits,
// IN OUT PULONG StackReserved,
// IN OUT PULONG StackCommit,
// IN PVOID StartAddress,
// IN PVOID StartParameter OPTIONAL,
// OUT PHANDLE ThreadHandle,
// OUT PCLIENT_ID ClientID);
if (!NT_SUCCESS(RtlCreateUserThread(
hProc, // HANDLE hProcess,
nullptr, // IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
FALSE, // IN BOOLEAN CreateSuspended,
0, // IN ULONG StackZeroBits,
&reserve, // IN OUT PULONG StackReserved,
&commit, // IN OUT PULONG StackCommit,
(LPVOID)start_addr, // IN PVOID StartAddress,
(LPVOID)param,// IN PVOID StartParameter OPTIONAL,
&hThread, // OUT PHANDLE ThreadHandle,
&id // OUT PCLIENT_ID ClientID
)))
hThread = INVALID_HANDLE_VALUE;
}
*/
return hThread;
}
//Query module export table. Return function address if found.
//Similar to GetProcAddress
DWORD GetExportAddress(DWORD hModule,DWORD hash)
{
IMAGE_DOS_HEADER *DosHdr;
IMAGE_NT_HEADERS *NtHdr;
IMAGE_EXPORT_DIRECTORY *ExtDir;
UINT uj;
char* pcExportAddr,*pcFuncPtr,*pcBuffer;
DWORD dwReadAddr,dwFuncAddr,dwFuncName;
WORD wOrd;
DosHdr = (IMAGE_DOS_HEADER*)hModule;
if (IMAGE_DOS_SIGNATURE==DosHdr->e_magic) {
dwReadAddr=hModule+DosHdr->e_lfanew;
NtHdr=(IMAGE_NT_HEADERS*)dwReadAddr;
if (IMAGE_NT_SIGNATURE == NtHdr->Signature) {
pcExportAddr = (char*)((DWORD)hModule+
(DWORD)NtHdr->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);
if (!pcExportAddr)
return 0;
ExtDir = (IMAGE_EXPORT_DIRECTORY*)pcExportAddr;
pcExportAddr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfNames);
for (uj = 0; uj < ExtDir->NumberOfNames; uj++) {
dwFuncName = *(DWORD *)pcExportAddr;
pcBuffer = (char*)((DWORD)hModule+dwFuncName);
if (GetHash(pcBuffer) == hash) {
pcFuncPtr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfNameOrdinals+(uj*sizeof(WORD)));
wOrd = *(WORD*)pcFuncPtr;
pcFuncPtr = (char*)((DWORD)hModule+(DWORD)ExtDir->AddressOfFunctions+(wOrd*sizeof(DWORD)));
dwFuncAddr = *(DWORD *)pcFuncPtr;
return hModule+dwFuncAddr;
}
pcExportAddr += sizeof(DWORD);
}
}
}
return 0;
}
} // extern "C"
// EOF
/*__declspec(naked) void normal_asm()
{
__asm
{
push ecx
push edx
mov fs:[0],esp
push ebp
call eax
_terminate:
push eax
push -2
call dword ptr [NtTerminateThread]
}
}*/
/*
__declspec(naked) void RegToStrAsm()
{
__asm
{
mov edx, 8
_cvt_loop:
mov eax, ecx
and eax, 0xF
cmp eax, 0xA
jb _below_ten
add al,7
_below_ten:
add al,0x30
stosw
ror ecx,4
dec edx
jne _cvt_loop
retn
}
}
__declspec(naked) void except_asm()
{
__asm
{
mov eax,[esp + 4]
xor esi,esi
mov ebp,[eax]
mov ecx,[esp + 0xC]
mov ebx,[ecx + 0xB8]
sub esp,0x240
lea edi,[esp + 0x40]
mov eax,esp
push esi
push 0x1C
push eax
push esi
push ebx
push -1
call dword ptr [NtQueryVirtualMemory]
test eax,eax
jne _terminate
mov eax,esp
push eax
push 0x200
push edi
push 2
push ebx
push -1
call dword ptr [NtQueryVirtualMemory]
test eax,eax
jne _terminate
pop esi
xadd edi,esi
std
mov al,0x5C
repen scasw
mov word ptr [edi + 2], 0x3A
mov ecx,ebx
sub ecx,[esp]
call RegToStrAsm
inc edi
inc edi
xchg esi,edi
mov ecx,ebp
call RegToStrAsm
inc edi
inc edi
xor eax,eax
mov [edi + 0x10], eax
push 0
push edi
push esi
push 0
call dword ptr [MessageBoxW]
or eax, -1
jmp _terminate
}
}
//Prompt for file name.
HANDLE IthPromptCreateFile(DWORD option, DWORD share, DWORD disposition)
{
OPENFILENAME ofn = {sizeof(ofn)}; // common dialog box structure
WCHAR szFile[MAX_PATH]; // buffer for file name
wcscpy(current_dir,L"ITH_export.txt");
wcscpy(szFile,file_path);
//szFile[0]=0;
ofn.lpstrFile = szFile + 4;
ofn.nMaxFile = MAX_PATH;
ofn.lpstrFilter = L"Text\0*.txt";
BOOL result;
if (disposition==FILE_OPEN)
result=GetOpenFileName(&ofn);
else
result=GetSaveFileName(&ofn);
if (result)
{
LPWSTR s=szFile+wcslen(szFile) - 4;
if (_wcsicmp(s,L".txt")!=0) wcscpy(s + 4,L".txt");
return IthCreateFileFullPath(szFile,option,share,disposition);
}
else return INVALID_HANDLE_VALUE;
}
*/
#pragma once
// ith/sys.h
// 8/23/2013 jichi
// Branch: ITH/IHF_SYS.h, rev 111
#ifdef _MSC_VER
# pragma warning(disable:4800) // C4800: forcing value to bool
#endif // _MSC_VER
#include "ntdll/ntdll.h"
// jichi 8/24/2013: Why extern "C"? Any specific reason to use C instead of C++ naming?
extern "C" {
//int disasm(BYTE *opcode0); // jichi 8/15/2013: move disasm to separate file
extern WORD *NlsAnsiCodePage;
int FillRange(LPCWSTR name,DWORD *lower, DWORD *upper);
int MB_WC(char *mb, wchar_t *wc);
//int MB_WC_count(char *mb, int mb_length);
int WC_MB(wchar_t *wc, char *mb);
// jichi 10/1/2013: Return 0 if failed. So, it is ambiguous if the search pattern starts at 0
DWORD SearchPattern(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length); // KMP
// jichi 2/5/2014: The same as SearchPattern except it uses 0xff to match everything
// According to @Andys, 0xff seldom appear in the source code: http://sakuradite.com/topic/124
enum : BYTE { SP_ANY = 0xff };
#define SP_ANY_2 SP_ANY,SP_ANY
#define SP_ANY_3 SP_ANY,SP_ANY,SP_ANY
#define SP_ANY_4 SP_ANY,SP_ANY,SP_ANY,SP_ANY
DWORD SearchPatternEx(DWORD base, DWORD base_length, LPCVOID search, DWORD search_length, BYTE wildcard=SP_ANY);
BOOL IthInitSystemService();
void IthCloseSystemService();
DWORD IthGetMemoryRange(LPCVOID mem, DWORD *base, DWORD *size);
BOOL IthCheckFile(LPCWSTR file);
BOOL IthFindFile(LPCWSTR file);
BOOL IthGetFileInfo(LPCWSTR file, LPVOID info, DWORD size = 0x1000);
BOOL IthCheckFileFullPath(LPCWSTR file);
HANDLE IthCreateFile(LPCWSTR name, DWORD option, DWORD share, DWORD disposition);
HANDLE IthCreateFileInDirectory(LPCWSTR name, HANDLE dir, DWORD option, DWORD share, DWORD disposition);
HANDLE IthCreateDirectory(LPCWSTR name);
HANDLE IthCreateFileFullPath(LPCWSTR fullpath, DWORD option, DWORD share, DWORD disposition);
HANDLE IthPromptCreateFile(DWORD option, DWORD share, DWORD disposition);
HANDLE IthCreateSection(LPCWSTR name, DWORD size, DWORD right);
HANDLE IthCreateEvent(LPCWSTR name, DWORD auto_reset=0, DWORD init_state=0);
HANDLE IthOpenEvent(LPCWSTR name);
void IthSetEvent(HANDLE hEvent);
void IthResetEvent(HANDLE hEvent);
HANDLE IthCreateMutex(LPCWSTR name, BOOL InitialOwner, DWORD *exist=0);
HANDLE IthOpenMutex(LPCWSTR name);
BOOL IthReleaseMutex(HANDLE hMutex);
//DWORD IthWaitForSingleObject(HANDLE hObject, DWORD dwTime);
HANDLE IthCreateThread(LPCVOID start_addr, DWORD param, HANDLE hProc=(HANDLE)-1);
DWORD GetExportAddress(DWORD hModule,DWORD hash);
void IthSleep(int time); // jichi 9/28/2013: in ms
void IthSystemTimeToLocalTime(LARGE_INTEGER *ptime);
void FreeThreadStart(HANDLE hProc);
void CheckThreadStart();
} // extern "C"
#ifdef ITH_HAS_HEAP
extern HANDLE hHeap; // used in ith/common/memory.h
#endif // ITH_HAS_HEAP
extern DWORD current_process_id;
extern DWORD debug;
extern BYTE LeadByteTable[];
extern LPVOID page;
extern BYTE launch_time[];
inline DWORD GetHash(LPSTR str)
{
DWORD hash = 0;
//for (; *str; str++)
while (*str)
hash = ((hash>>7) | (hash<<25)) + *str++;
return hash;
}
inline DWORD GetHash(LPCWSTR str)
{
DWORD hash = 0;
//for (; *str; str++)
while (*str)
hash = ((hash>>7) | (hash<<25)) + *str++;
return hash;
}
inline void IthBreak()
{ if (debug) __debugbreak(); }
inline LPCWSTR GetMainModulePath()
{
__asm
{
mov eax, fs:[0x30]
mov eax, [eax + 0xC]
mov eax, [eax + 0xC]
mov eax, [eax + 0x28]
}
}
// jichi 9/28/2013: Add this to lock NtWriteFile in wine
class IthMutexLocker
{
HANDLE m;
public:
explicit IthMutexLocker(HANDLE mutex) : m(mutex)
{ NtWaitForSingleObject(m, 0, 0); }
~IthMutexLocker() { if (m != INVALID_HANDLE_VALUE) IthReleaseMutex(m); }
bool locked() const { return m != INVALID_HANDLE_VALUE; }
void unlock() { if (m != INVALID_HANDLE_VALUE) { IthReleaseMutex(m); m = INVALID_HANDLE_VALUE; } }
};
void IthCoolDown();
BOOL IthIsWine();
BOOL IthIsWindowsXp();
//BOOL IthIsWindows8OrGreater(); // not public
/** Get current dll path.
* @param buf
* @param len
* @return length of the path excluding \0
*/
size_t IthGetCurrentModulePath(wchar_t *buf, size_t len);
// EOF
# sys.pri
# 8/21/2013 jichi
DEFINES += WITH_LIB_ITH_SYS
LIBS += -lvnrsys
DEPENDPATH += $$PWD
HEADERS += $$PWD/sys.h
#SOURCES += $$PWD/sys.cc
#include($$LIBDIR/winddk/winddk.pri)
#LIBS += -L$$WDK/lib/wxp/i386
# EOF
# sys.pro
# 8/21/2013 jichi
# Build vnrsys.lib
CONFIG += noqt noeh staticlib
include(../../../../config.pri)
include($$LIBDIR/ntdll/ntdll.pri)
#include($$LIBDIR/winddk/winddk.pri)
#LIBS += -L$$WDK/lib/wxp/i386
# jichi 9/22/2013: When ITH is on wine, certain NT functions are replaced
#DEFINES += ITH_WINE
# jichi 9/14/2013: Windows XP's msvnrt does not have except handler
DEFINES -= ITH_HAS_SEH
# jichi 11/24/2013: Disable manual heap
DEFINES -= ITH_HAS_HEAP
## Libraries
#INCLUDEPATH += $$ITH_HOME/include
#INCLUDEPATH += $$WDK7_HOME/inc/ddk
#LIBS += -lgdi32 -luser32 -lkernel32
#LIBS += -L$$WDK7_HOME/lib/wxp/i386 -lntdll
#LIBS += $$WDK7_HOME/lib/crt/i386/msvcrt.lib # Override msvcrt10
#DEFINES += ITH_HAS_CXX
#LIBS += -lith_sys -lntdll
#LIBS += -lith_tls -lntdll
#LIBS += -lntoskrnl
DEFINES += _CRT_NON_CONFORMING_SWPRINTFS
## Sources
TEMPLATE = lib
TARGET = vnrsys
HEADERS += sys.h
SOURCES += sys.cc
OTHER_FILES += sys.pri
# EOF
12/16/2013
Differences between xp.dll and non-xp.dll for vnrhook.
non-xp:
CONFIG += eh
xp:
CONFIG += noeh
CONFIG -= embed_manifest_dll # Pure dynamic determined. The manifest would break Windows XP support
include($$LIBDIR/winseh/winseh_safe.pri)