Dev See merge request vmp2/vmassembler!1merge-requests/2/head^2 v1.1
commit
64ca8e60b1
@ -0,0 +1,18 @@
|
||||
---
|
||||
BasedOnStyle: Microsoft
|
||||
AlignAfterOpenBracket: Align
|
||||
AllowAllArgumentsOnNextLine: 'true'
|
||||
AllowAllParametersOfDeclarationOnNextLine: 'true'
|
||||
AllowShortIfStatementsOnASingleLine: Never
|
||||
BreakBeforeBraces: Allman
|
||||
IndentWidth: '4'
|
||||
Language: Cpp
|
||||
NamespaceIndentation: All
|
||||
SpacesInAngles: 'true'
|
||||
SpacesInCStyleCastParentheses: 'true'
|
||||
SpacesInContainerLiterals: 'true'
|
||||
SpacesInParentheses: 'true'
|
||||
SpacesInSquareBrackets: 'true'
|
||||
UseTab: Never
|
||||
|
||||
...
|
@ -1,3 +1,9 @@
|
||||
[submodule "dependencies/vmprofiler"]
|
||||
path = dependencies/vmprofiler
|
||||
url = https://githacks.org/vmp2/vmprofiler.git
|
||||
[submodule "dependencies/xtils"]
|
||||
path = dependencies/xtils
|
||||
url = https://githacks.org/_xeroxz/xtils.git
|
||||
[submodule "dependencies/cli-parser"]
|
||||
path = dependencies/cli-parser
|
||||
url = https://githacks.org/_xeroxz/cli-parser.git
|
||||
|
@ -0,0 +1 @@
|
||||
Subproject commit 1aedaf8bb7f383f54b7cd498767611535526da85
|
@ -1 +1 @@
|
||||
Subproject commit 05ba2cc84ba94e1d526168ad686fc2e167ad2eab
|
||||
Subproject commit e58c23c40e13528f5d9b84feb7e23b62a886ed5a
|
@ -0,0 +1 @@
|
||||
Subproject commit 7c32517322c29a866cbb1e67fb9051efa2e05553
|
@ -1,580 +0,0 @@
|
||||
/**
|
||||
* License: Apache 2.0 with LLVM Exception or GPL v3
|
||||
*
|
||||
* Author: Jesse Laning
|
||||
*/
|
||||
|
||||
#ifndef ARGPARSE_H
|
||||
#define ARGPARSE_H
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <locale>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace argparse {
|
||||
namespace detail {
|
||||
static inline bool _not_space(int ch) { return !std::isspace(ch); }
|
||||
static inline void _ltrim(std::string& s, bool (*f)(int) = _not_space) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), f));
|
||||
}
|
||||
static inline void _rtrim(std::string& s, bool (*f)(int) = _not_space) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), f).base(), s.end());
|
||||
}
|
||||
static inline void _trim(std::string& s, bool (*f)(int) = _not_space) {
|
||||
_ltrim(s, f);
|
||||
_rtrim(s, f);
|
||||
}
|
||||
static inline std::string _ltrim_copy(std::string s,
|
||||
bool (*f)(int) = _not_space) {
|
||||
_ltrim(s, f);
|
||||
return s;
|
||||
}
|
||||
static inline std::string _rtrim_copy(std::string s,
|
||||
bool (*f)(int) = _not_space) {
|
||||
_rtrim(s, f);
|
||||
return s;
|
||||
}
|
||||
static inline std::string _trim_copy(std::string s,
|
||||
bool (*f)(int) = _not_space) {
|
||||
_trim(s, f);
|
||||
return s;
|
||||
}
|
||||
template <typename InputIt>
|
||||
static inline std::string _join(InputIt begin, InputIt end,
|
||||
const std::string& separator = " ") {
|
||||
std::ostringstream ss;
|
||||
if (begin != end) {
|
||||
ss << *begin++;
|
||||
}
|
||||
while (begin != end) {
|
||||
ss << separator;
|
||||
ss << *begin++;
|
||||
}
|
||||
return ss.str();
|
||||
}
|
||||
static inline bool _is_number(const std::string& arg) {
|
||||
std::istringstream iss(arg);
|
||||
float f;
|
||||
iss >> std::noskipws >> f;
|
||||
return iss.eof() && !iss.fail();
|
||||
}
|
||||
|
||||
static inline int _find_equal(const std::string& s) {
|
||||
for (size_t i = 0; i < s.length(); ++i) {
|
||||
// if find graph symbol before equal, end search
|
||||
// i.e. don't accept --asd)f=0 arguments
|
||||
// but allow --asd_f and --asd-f arguments
|
||||
if (std::ispunct(static_cast<int>(s[i]))) {
|
||||
if (s[i] == '=') {
|
||||
return static_cast<int>(i);
|
||||
}
|
||||
else if (s[i] == '_' || s[i] == '-') {
|
||||
continue;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static inline size_t _find_name_end(const std::string& s) {
|
||||
size_t i;
|
||||
for (i = 0; i < s.length(); ++i) {
|
||||
if (std::ispunct(static_cast<int>(s[i]))) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
namespace is_vector_impl {
|
||||
template <typename T>
|
||||
struct is_vector : std::false_type {};
|
||||
template <typename... Args>
|
||||
struct is_vector<std::vector<Args...>> : std::true_type {};
|
||||
} // namespace is_vector_impl
|
||||
|
||||
// type trait to utilize the implementation type traits as well as decay the
|
||||
// type
|
||||
template <typename T>
|
||||
struct is_vector {
|
||||
static constexpr bool const value =
|
||||
is_vector_impl::is_vector<typename std::decay<T>::type>::value;
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
class argument_parser_t {
|
||||
private:
|
||||
public:
|
||||
class Argument;
|
||||
|
||||
class Result {
|
||||
public:
|
||||
Result() {}
|
||||
Result(std::string err) noexcept : _error(true), _what(err) {}
|
||||
|
||||
operator bool() const { return _error; }
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Result& dt);
|
||||
|
||||
const std::string& what() const { return _what; }
|
||||
|
||||
private:
|
||||
bool _error{ false };
|
||||
std::string _what{};
|
||||
};
|
||||
|
||||
class Argument {
|
||||
public:
|
||||
enum Position : int { LAST = -1, DONT_CARE = -2 };
|
||||
enum Count : int { ANY = -1 };
|
||||
|
||||
Argument& name(const std::string& name) {
|
||||
_names.push_back(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument& names(std::vector<std::string> names) {
|
||||
_names.insert(_names.end(), names.begin(), names.end());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument& description(const std::string& description) {
|
||||
_desc = description;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument& required(bool req) {
|
||||
_required = req;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument& position(int position) {
|
||||
if (position != Position::LAST) {
|
||||
// position + 1 because technically argument zero is the name of the
|
||||
// executable
|
||||
_position = position + 1;
|
||||
}
|
||||
else {
|
||||
_position = position;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Argument& count(int count) {
|
||||
_count = count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool found() const { return _found; }
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<detail::is_vector<T>::value, T>::type get() {
|
||||
T t = T();
|
||||
typename T::value_type vt;
|
||||
for (auto& s : _values) {
|
||||
std::istringstream in(s);
|
||||
in >> vt;
|
||||
t.push_back(vt);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!detail::is_vector<T>::value, T>::type get() {
|
||||
std::istringstream in(get<std::string>());
|
||||
T t = T();
|
||||
in >> t >> std::ws;
|
||||
return t;
|
||||
}
|
||||
|
||||
private:
|
||||
Argument(const std::string& name, const std::string& desc,
|
||||
bool required = false)
|
||||
: _desc(desc), _required(required) {
|
||||
_names.push_back(name);
|
||||
}
|
||||
|
||||
Argument() {}
|
||||
|
||||
friend class argument_parser_t;
|
||||
int _position{ Position::DONT_CARE };
|
||||
int _count{ Count::ANY };
|
||||
std::vector<std::string> _names{};
|
||||
std::string _desc{};
|
||||
bool _found{ false };
|
||||
bool _required{ false };
|
||||
int _index{ -1 };
|
||||
|
||||
std::vector<std::string> _values{};
|
||||
};
|
||||
|
||||
argument_parser_t(std::string bin, std::string desc)
|
||||
: _bin(bin), _desc(desc) {}
|
||||
|
||||
Argument& add_argument() {
|
||||
_arguments.push_back({});
|
||||
_arguments.back()._index = static_cast<int>(_arguments.size()) - 1;
|
||||
return _arguments.back();
|
||||
}
|
||||
|
||||
Argument& add_argument(const std::string& name, const std::string& long_name,
|
||||
const std::string& desc, const bool required = false) {
|
||||
_arguments.push_back(Argument(name, desc, required));
|
||||
_arguments.back()._names.push_back(long_name);
|
||||
_arguments.back()._index = static_cast<int>(_arguments.size()) - 1;
|
||||
return _arguments.back();
|
||||
}
|
||||
|
||||
Argument& add_argument(const std::string& name, const std::string& desc,
|
||||
const bool required = false) {
|
||||
_arguments.push_back(Argument(name, desc, required));
|
||||
_arguments.back()._index = static_cast<int>(_arguments.size()) - 1;
|
||||
return _arguments.back();
|
||||
}
|
||||
|
||||
void print_help(size_t count = 0, size_t page = 0) {
|
||||
if (page * count > _arguments.size()) {
|
||||
return;
|
||||
}
|
||||
if (page == 0) {
|
||||
std::cout << "Usage: " << _bin;
|
||||
if (_positional_arguments.empty()) {
|
||||
std::cout << " [options...]" << std::endl;
|
||||
}
|
||||
else {
|
||||
int current = 1;
|
||||
for (auto& v : _positional_arguments) {
|
||||
if (v.first != Argument::Position::LAST) {
|
||||
for (; current < v.first; current++) {
|
||||
std::cout << " [" << current << "]";
|
||||
}
|
||||
std::cout
|
||||
<< " ["
|
||||
<< detail::_ltrim_copy(
|
||||
_arguments[static_cast<size_t>(v.second)]._names[0],
|
||||
[](int c) -> bool { return c != static_cast<int>('-'); })
|
||||
<< "]";
|
||||
}
|
||||
}
|
||||
auto it = _positional_arguments.find(Argument::Position::LAST);
|
||||
if (it == _positional_arguments.end()) {
|
||||
std::cout << " [options...]";
|
||||
}
|
||||
else {
|
||||
std::cout
|
||||
<< " [options...] ["
|
||||
<< detail::_ltrim_copy(
|
||||
_arguments[static_cast<size_t>(it->second)]._names[0],
|
||||
[](int c) -> bool { return c != static_cast<int>('-'); })
|
||||
<< "]";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
std::cout << "Options:" << std::endl;
|
||||
}
|
||||
if (count == 0) {
|
||||
page = 0;
|
||||
count = _arguments.size();
|
||||
}
|
||||
for (size_t i = page * count;
|
||||
i < std::min<size_t>(page * count + count, _arguments.size()); i++) {
|
||||
Argument& a = _arguments[i];
|
||||
std::string name = a._names[0];
|
||||
for (size_t n = 1; n < a._names.size(); ++n) {
|
||||
name.append(", " + a._names[n]);
|
||||
}
|
||||
std::cout << " " << std::setw(23) << std::left << name << std::setw(23)
|
||||
<< a._desc;
|
||||
if (a._required) {
|
||||
std::cout << " (Required)";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
Result parse(int argc, const char* argv[]) {
|
||||
Result err;
|
||||
if (argc > 1) {
|
||||
// build name map
|
||||
for (auto& a : _arguments) {
|
||||
for (auto& n : a._names) {
|
||||
std::string name = detail::_ltrim_copy(
|
||||
n, [](int c) -> bool { return c != static_cast<int>('-'); });
|
||||
if (_name_map.find(name) != _name_map.end()) {
|
||||
return Result("Duplicate of argument name: " + n);
|
||||
}
|
||||
_name_map[name] = a._index;
|
||||
}
|
||||
if (a._position >= 0 || a._position == Argument::Position::LAST) {
|
||||
_positional_arguments[a._position] = a._index;
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
// parse
|
||||
std::string current_arg;
|
||||
size_t arg_len;
|
||||
for (int argv_index = 1; argv_index < argc; ++argv_index) {
|
||||
current_arg = std::string(argv[argv_index]);
|
||||
arg_len = current_arg.length();
|
||||
if (arg_len == 0) {
|
||||
continue;
|
||||
}
|
||||
if (_help_enabled && (current_arg == "-h" || current_arg == "--help")) {
|
||||
_arguments[static_cast<size_t>(_name_map["help"])]._found = true;
|
||||
}
|
||||
else if (argv_index == argc - 1 &&
|
||||
_positional_arguments.find(Argument::Position::LAST) !=
|
||||
_positional_arguments.end()) {
|
||||
err = _end_argument();
|
||||
Result b = err;
|
||||
err = _add_value(current_arg, Argument::Position::LAST);
|
||||
if (b) {
|
||||
return b;
|
||||
}
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
else if (arg_len >= 2 &&
|
||||
!detail::_is_number(current_arg)) { // ignores the case if
|
||||
// the arg is just a -
|
||||
// look for -a (short) or --arg (long) args
|
||||
if (current_arg[0] == '-') {
|
||||
err = _end_argument();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
// look for --arg (long) args
|
||||
if (current_arg[1] == '-') {
|
||||
err = _begin_argument(current_arg.substr(2), true, argv_index);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
else { // short args
|
||||
err = _begin_argument(current_arg.substr(1), false, argv_index);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // argument value
|
||||
err = _add_value(current_arg, argv_index);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // argument value
|
||||
err = _add_value(current_arg, argv_index);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_help_enabled && exists("help")) {
|
||||
return Result();
|
||||
}
|
||||
err = _end_argument();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
for (auto& p : _positional_arguments) {
|
||||
Argument& a = _arguments[static_cast<size_t>(p.second)];
|
||||
if (a._values.size() > 0 && a._values[0][0] == '-') {
|
||||
std::string name = detail::_ltrim_copy(a._values[0], [](int c) -> bool {
|
||||
return c != static_cast<int>('-');
|
||||
});
|
||||
if (_name_map.find(name) != _name_map.end()) {
|
||||
if (a._position == Argument::Position::LAST) {
|
||||
return Result(
|
||||
"Poisitional argument expected at the end, but argument " +
|
||||
a._values[0] + " found instead");
|
||||
}
|
||||
else {
|
||||
return Result("Poisitional argument expected in position " +
|
||||
std::to_string(a._position) + ", but argument " +
|
||||
a._values[0] + " found instead");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& a : _arguments) {
|
||||
if (a._required && !a._found) {
|
||||
return Result("Required argument not found: " + a._names[0]);
|
||||
}
|
||||
if (a._position >= 0 && argc >= a._position && !a._found) {
|
||||
return Result("Argument " + a._names[0] + " expected in position " +
|
||||
std::to_string(a._position));
|
||||
}
|
||||
}
|
||||
return Result();
|
||||
}
|
||||
|
||||
void enable_help() {
|
||||
add_argument("-h", "--help", "Shows this page", false);
|
||||
_help_enabled = true;
|
||||
}
|
||||
|
||||
bool exists(const std::string& name) const {
|
||||
std::string n = detail::_ltrim_copy(
|
||||
name, [](int c) -> bool { return c != static_cast<int>('-'); });
|
||||
auto it = _name_map.find(n);
|
||||
if (it != _name_map.end()) {
|
||||
return _arguments[static_cast<size_t>(it->second)]._found;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get(const std::string& name) {
|
||||
auto t = _name_map.find(name);
|
||||
if (t != _name_map.end()) {
|
||||
return _arguments[static_cast<size_t>(t->second)].get<T>();
|
||||
}
|
||||
return T();
|
||||
}
|
||||
|
||||
private:
|
||||
Result _begin_argument(const std::string& arg, bool longarg, int position) {
|
||||
auto it = _positional_arguments.find(position);
|
||||
if (it != _positional_arguments.end()) {
|
||||
Result err = _end_argument();
|
||||
Argument& a = _arguments[static_cast<size_t>(it->second)];
|
||||
a._values.push_back((longarg ? "--" : "-") + arg);
|
||||
a._found = true;
|
||||
return err;
|
||||
}
|
||||
if (_current != -1) {
|
||||
return Result("Current argument left open");
|
||||
}
|
||||
size_t name_end = detail::_find_name_end(arg);
|
||||
std::string arg_name = arg.substr(0, name_end);
|
||||
if (longarg) {
|
||||
int equal_pos = detail::_find_equal(arg);
|
||||
auto nmf = _name_map.find(arg_name);
|
||||
if (nmf == _name_map.end()) {
|
||||
return Result("Unrecognized command line option '" + arg_name + "'");
|
||||
}
|
||||
_current = nmf->second;
|
||||
_arguments[static_cast<size_t>(nmf->second)]._found = true;
|
||||
if (equal_pos == 0 ||
|
||||
(equal_pos < 0 &&
|
||||
arg_name.length() < arg.length())) { // malformed argument
|
||||
return Result("Malformed argument: " + arg);
|
||||
}
|
||||
else if (equal_pos > 0) {
|
||||
std::string arg_value = arg.substr(name_end + 1);
|
||||
_add_value(arg_value, position);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Result r;
|
||||
if (arg_name.length() == 1) {
|
||||
return _begin_argument(arg, true, position);
|
||||
}
|
||||
else {
|
||||
for (char& c : arg_name) {
|
||||
r = _begin_argument(std::string(1, c), true, position);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
r = _end_argument();
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result();
|
||||
}
|
||||
|
||||
Result _add_value(const std::string& value, int location) {
|
||||
if (_current >= 0) {
|
||||
Result err;
|
||||
Argument& a = _arguments[static_cast<size_t>(_current)];
|
||||
if (a._count >= 0 && static_cast<int>(a._values.size()) >= a._count) {
|
||||
err = _end_argument();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
goto unnamed;
|
||||
}
|
||||
a._values.push_back(value);
|
||||
if (a._count >= 0 && static_cast<int>(a._values.size()) >= a._count) {
|
||||
err = _end_argument();
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
return Result();
|
||||
}
|
||||
else {
|
||||
unnamed:
|
||||
auto it = _positional_arguments.find(location);
|
||||
if (it != _positional_arguments.end()) {
|
||||
Argument& a = _arguments[static_cast<size_t>(it->second)];
|
||||
a._values.push_back(value);
|
||||
a._found = true;
|
||||
}
|
||||
// TODO
|
||||
return Result();
|
||||
}
|
||||
}
|
||||
|
||||
Result _end_argument() {
|
||||
if (_current >= 0) {
|
||||
Argument& a = _arguments[static_cast<size_t>(_current)];
|
||||
_current = -1;
|
||||
if (static_cast<int>(a._values.size()) < a._count) {
|
||||
return Result("Too few arguments given for " + a._names[0]);
|
||||
}
|
||||
if (a._count >= 0) {
|
||||
if (static_cast<int>(a._values.size()) > a._count) {
|
||||
return Result("Too many arguments given for " + a._names[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result();
|
||||
}
|
||||
|
||||
bool _help_enabled{ false };
|
||||
int _current{ -1 };
|
||||
std::string _bin{};
|
||||
std::string _desc{};
|
||||
std::vector<Argument> _arguments{};
|
||||
std::map<int, int> _positional_arguments{};
|
||||
std::map<std::string, int> _name_map{};
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const argument_parser_t::Result& r) {
|
||||
os << r.what();
|
||||
return os;
|
||||
}
|
||||
template <>
|
||||
inline std::string argument_parser_t::Argument::get<std::string>() {
|
||||
return detail::_join(_values.begin(), _values.end());
|
||||
}
|
||||
template <>
|
||||
inline std::vector<std::string>
|
||||
argument_parser_t::Argument::get<std::vector<std::string>>() {
|
||||
return _values;
|
||||
}
|
||||
|
||||
} // namespace argparse
|
||||
#endif
|
@ -1,49 +1,47 @@
|
||||
#pragma once
|
||||
#include <Windows.h>
|
||||
#include <vmprofiler.hpp>
|
||||
#include <vmctx.h>
|
||||
#include <vm.h>
|
||||
#include <algorithm>
|
||||
#include <vmprofiler.hpp>
|
||||
|
||||
#include "parser.h"
|
||||
|
||||
#define NT_HEADER(x) \
|
||||
reinterpret_cast<PIMAGE_NT_HEADERS64>( \
|
||||
reinterpret_cast<PIMAGE_DOS_HEADER>(x)->e_lfanew + x)
|
||||
#define NT_HEADER( x ) \
|
||||
reinterpret_cast< PIMAGE_NT_HEADERS64 >( reinterpret_cast< PIMAGE_DOS_HEADER >( x )->e_lfanew + x )
|
||||
|
||||
namespace vm
|
||||
{
|
||||
struct base_data_t
|
||||
{
|
||||
std::uintptr_t module_base;
|
||||
std::uintptr_t image_base;
|
||||
};
|
||||
struct base_data_t
|
||||
{
|
||||
std::uintptr_t module_base;
|
||||
std::uintptr_t image_base;
|
||||
};
|
||||
|
||||
struct vinstr_data
|
||||
{
|
||||
std::uint8_t vm_handler;
|
||||
std::uint64_t operand;
|
||||
std::uint8_t imm_size; // size in bits...
|
||||
};
|
||||
|
||||
struct vinstr_data
|
||||
{
|
||||
std::uint8_t vm_handler;
|
||||
std::uint64_t operand;
|
||||
std::uint8_t imm_size; // size in bits...
|
||||
};
|
||||
class compiler_t
|
||||
{
|
||||
public:
|
||||
explicit compiler_t( base_data_t base_data, vmp2::exec_type_t exec_type,
|
||||
std::vector< vm::handler::handler_t > *vm_handlers, zydis_routine_t *calc_jmp,
|
||||
zydis_routine_t *vm_entry );
|
||||
|
||||
class compiler_t
|
||||
{
|
||||
public:
|
||||
explicit compiler_t(
|
||||
base_data_t base_data,
|
||||
vmp2::exec_type_t exec_type,
|
||||
std::vector<vm::handler_t>* vm_handlers,
|
||||
zydis_routine_t* calc_jmp
|
||||
);
|
||||
std::pair< bool, std::vector< vinstr_data > * > encode();
|
||||
std::pair< std::uint64_t, std::vector< std::uint8_t > * > encrypt();
|
||||
std::uint64_t encrypt_rva( std::uint64_t rva );
|
||||
|
||||
std::pair<bool, std::vector<vinstr_data>*> encode();
|
||||
std::pair<std::uint64_t, std::vector<std::uint8_t>* > encrypt();
|
||||
private:
|
||||
transform::map_t calc_jmp_transforms;
|
||||
zydis_routine_t* calc_jmp;
|
||||
std::vector<vm::handler_t>* vm_handlers;
|
||||
vmp2::exec_type_t exec_type;
|
||||
std::vector<vinstr_data> vinstrs;
|
||||
std::vector<std::uint8_t> result_buffer;
|
||||
std::uintptr_t image_base, module_base;
|
||||
};
|
||||
}
|
||||
private:
|
||||
transform::map_t calc_jmp_transforms;
|
||||
zydis_routine_t *calc_jmp, *vm_entry;
|
||||
std::vector< vm::handler::handler_t > *vm_handlers;
|
||||
vmp2::exec_type_t exec_type;
|
||||
std::vector< vinstr_data > vinstrs;
|
||||
std::vector< std::uint8_t > result_buffer;
|
||||
std::vector< zydis_decoded_instr_t > encrypt_vinstrs_rva;
|
||||
std::uintptr_t image_base, module_base;
|
||||
};
|
||||
} // namespace vm
|
@ -1,223 +1,193 @@
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <Windows.h>
|
||||
#include <cli-parser.hpp>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <transform.hpp>
|
||||
#include <xtils.hpp>
|
||||
|
||||
#include "compiler.h"
|
||||
#include "parser.tab.h"
|
||||
#include "parser.h"
|
||||
#include "cli-parser.hpp"
|
||||
#include "parser.tab.h"
|
||||
#include "vmasm.hpp"
|
||||
|
||||
extern FILE* yyin;
|
||||
extern FILE *yyin;
|
||||
extern "C" int yywrap()
|
||||
{ return 1; }
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void yyerror(char* msg)
|
||||
{ std::printf("[!] parsing failure: %s\n", msg); }
|
||||
void yyerror( char *msg )
|
||||
{
|
||||
std::printf( "[!] parsing failure: %s\n", msg );
|
||||
}
|
||||
|
||||
int __cdecl main(int argc, const char* argv[])
|
||||
int __cdecl main( int argc, const char *argv[] )
|
||||
{
|
||||
//
|
||||
// handle arguments from the commandline...
|
||||
//
|
||||
|
||||
argparse::argument_parser_t argp(
|
||||
"vmassembler", "virtual instruction assembler for VMProtect 2");
|
||||
|
||||
argp.add_argument()
|
||||
.names({ "--input", "--in" })
|
||||
.description("path to a vasm file to be assembled...")
|
||||
.required(true);
|
||||
|
||||
argp.add_argument()
|
||||
.names({ "--vmpbin", "--bin" })
|
||||
.description("path to protected binary...")
|
||||
.required(true);
|
||||
|
||||
argp.add_argument()
|
||||
.names({ "--vmentry", "--entry" })
|
||||
.description("rva to vm entry...")
|
||||
.required(true);
|
||||
|
||||
argp.add_argument()
|
||||
.names({ "--imagebase", "--base" })
|
||||
.description("optional header image base field value...")
|
||||
.required(true);
|
||||
|
||||
argp.add_argument()
|
||||
.name({ "--advancement" })
|
||||
.description("which direction vip advances... the options are 'forward' and 'backward'")
|
||||
.required(true);
|
||||
|
||||
argp.add_argument()
|
||||
.name({ "--output" })
|
||||
.description("output file name and path...")
|
||||
.required(true);
|
||||
|
||||
argp.enable_help();
|
||||
auto err = argp.parse(argc, argv);
|
||||
|
||||
if (err)
|
||||
{
|
||||
std::cout << err << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (argp.exists("help"))
|
||||
{
|
||||
argp.print_help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// set yyin to the vasm file...
|
||||
//
|
||||
|
||||
if ((yyin = fopen(argp.get<std::string>("input").c_str(), "r")) == nullptr)
|
||||
{
|
||||
std::printf("[!] failed to open vasm file...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// parse vasm file for all of the instructions...
|
||||
//
|
||||
|
||||
yyparse();
|
||||
std::printf("[+] finished parsing vasm file...\n");
|
||||
|
||||
//
|
||||
// init vm variables...
|
||||
//
|
||||
|
||||
const auto module_base =
|
||||
reinterpret_cast<std::uintptr_t>(
|
||||
LoadLibraryExA(argp.get<std::string>("vmpbin").c_str(),
|
||||
NULL, DONT_RESOLVE_DLL_REFERENCES));
|
||||
|
||||
const auto vm_entry_rva = std::strtoull(
|
||||
argp.get<std::string>("vmentry").c_str(), nullptr, 16);
|
||||
|
||||
const auto image_base = std::strtoull(
|
||||
argp.get<std::string>("imagebase").c_str(), nullptr, 16);
|
||||
|
||||
vmp2::exec_type_t advancement =
|
||||
argp.get<std::string>("advancement") == "backward" ?
|
||||
vmp2::exec_type_t::backward : vmp2::exec_type_t::forward;
|
||||
|
||||
zydis_routine_t vm_entry, calc_jmp;
|
||||
std::vector<vm::handler_t> vm_handlers;
|
||||
std::uintptr_t* vm_handler_table;
|
||||
ZydisDecodedInstruction encrypt_rva;
|
||||
|
||||
if (!vm::util::flatten(vm_entry, module_base + vm_entry_rva))
|
||||
{
|
||||
std::printf("[!] failed to flatten vm entry...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::printf("[+] flattened vm_entry...\n");
|
||||
vm::util::deobfuscate(vm_entry);
|
||||
std::printf("[+] deobfuscated vm_entry...\n");
|
||||
vm::util::print(vm_entry);
|
||||
|
||||
if (!(vm_handler_table = vm::handler::table::get(vm_entry)))
|
||||
{
|
||||
std::printf("[!] failed to obtain vm handler table...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!vm::handler::get_all(module_base, image_base, vm_entry, vm_handler_table, vm_handlers))
|
||||
{
|
||||
std::printf("[!] failed to get all vm handlers...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!vm::get_calc_jmp(vm_entry, calc_jmp))
|
||||
{
|
||||
std::printf("[!] failed to get calc_jmp...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!vm::get_vinstr_rva_transform(vm_entry, &encrypt_rva))
|
||||
{
|
||||
std::printf("[!] failed to get virtual instruction rva encryption transform...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vm::compiler_t compiler({ module_base, image_base }, advancement, &vm_handlers, &calc_jmp);
|
||||
|
||||
//
|
||||
// encode virtual instructions...
|
||||
//
|
||||
|
||||
auto [encoded_success, vinstrs] = compiler.encode();
|
||||
std::printf("[+] finished encoding... encoded instructions below...\n");
|
||||
|
||||
if (!encoded_success)
|
||||
{
|
||||
std::printf("[!] failed to encode virtual instructions...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (auto& vinstr : *vinstrs)
|
||||
{
|
||||
if (vinstr.imm_size)
|
||||
std::printf("> 0x%x - 0x%x\n", vinstr.vm_handler, vinstr.operand);
|
||||
else
|
||||
std::printf("> 0x%x\n", vinstr.vm_handler);
|
||||
}
|
||||
|
||||
//
|
||||
// encrypt virtual instructions...
|
||||
//
|
||||
|
||||
auto [entry_rva, result_buffer] = compiler.encrypt();
|
||||
std::printf("[+] finished encrypting... encrypted instructions below...\n");
|
||||
|
||||
if (!entry_rva)
|
||||
{
|
||||
std::printf("[!] failed to encrypt virtual instructions...\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::printf("> virtual instructions must be allocated at = 0x%p\n", entry_rva);
|
||||
std::printf("> ");
|
||||
{
|
||||
auto idx = 0u;
|
||||
for (auto byte : *result_buffer)
|
||||
{
|
||||
std::printf("0x%x ", byte);
|
||||
if (++idx == 10)
|
||||
{
|
||||
std::printf("\n");
|
||||
idx = 0u;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::printf("\n");
|
||||
|
||||
//
|
||||
// write the result to disk...
|
||||
//
|
||||
|
||||
vmasm::file_header_t file_header;
|
||||
file_header.magic = VASM_MAGIC;
|
||||
file_header.epoch_time = std::time(nullptr);
|
||||
file_header.vasm_size = result_buffer->size();
|
||||
file_header.alloc_rva = (entry_rva - image_base);
|
||||
file_header.vasm_offset = sizeof vmasm::file_header_t;
|
||||
file_header.encrypted_rva = vm::transform::apply(
|
||||
encrypt_rva.operands[0].size,
|
||||
encrypt_rva.mnemonic, entry_rva,
|
||||
// check to see if this instruction has an IMM...
|
||||
vm::transform::has_imm(&encrypt_rva) ?
|
||||
encrypt_rva.operands[1].imm.value.u : 0);
|
||||
|
||||
std::ofstream output(argp.get<std::string>("output"), std::ios::binary);
|
||||
output.write(reinterpret_cast<char*>(&file_header), sizeof file_header);
|
||||
output.write(reinterpret_cast<char*>(result_buffer->data()), result_buffer->size());
|
||||
output.close();
|
||||
argparse::argument_parser_t argp( "vmassembler", "virtual instruction assembler" );
|
||||
|
||||
argp.add_argument()
|
||||
.names( { "--input", "--in" } )
|
||||
.description( "path to a vasm file to be assembled..." )
|
||||
.required( true );
|
||||
|
||||
argp.add_argument().names( { "--vmpbin", "--bin" } ).description( "path to protected binary..." ).required( true );
|
||||
argp.add_argument().names( { "--vmentry", "--entry" } ).description( "rva to vm entry..." ).required( true );
|
||||
argp.add_argument().name( { "--output" } ).description( "output file name and path..." ).required( true );
|
||||
|
||||
argp.enable_help();
|
||||
auto err = argp.parse( argc, argv );
|
||||
|
||||
if ( err )
|
||||
{
|
||||
std::cout << err << std::endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( argp.exists( "help" ) )
|
||||
{
|
||||
argp.print_help();
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// set yyin to the vasm file...
|
||||
//
|
||||
|
||||
if ( ( yyin = fopen( argp.get< std::string >( "input" ).c_str(), "r" ) ) == nullptr )
|
||||
{
|
||||
std::printf( "[!] failed to open vasm file...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
//
|
||||
// parse vasm file for all of the instructions...
|
||||
//
|
||||
|
||||
yyparse();
|
||||
std::printf( "[+] finished parsing vasm file...\n" );
|
||||
|
||||
//
|
||||
// init vm variables...
|
||||
//
|
||||
|
||||
const auto module_base = reinterpret_cast< std::uintptr_t >(
|
||||
LoadLibraryExA( argp.get< std::string >( "vmpbin" ).c_str(), NULL, DONT_RESOLVE_DLL_REFERENCES ) );
|
||||
|
||||
const auto vm_entry_rva = std::strtoull( argp.get< std::string >( "vmentry" ).c_str(), nullptr, 16 );
|
||||
const auto image_base = xtils::um_t::get_instance()->image_base( argp.get< std::string >( "vmpbin" ).c_str() );
|
||||
|
||||
zydis_routine_t vm_entry, calc_jmp;
|
||||
std::vector< vm::handler::handler_t > vm_handlers;
|
||||
std::uintptr_t *vm_handler_table;
|
||||
|
||||
if ( !vm::util::flatten( vm_entry, module_base + vm_entry_rva ) )
|
||||
{
|
||||
std::printf( "[!] failed to flatten vm entry...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::printf( "[+] flattened vm_entry...\n" );
|
||||
std::printf( "[+] deobfuscated vm_entry...\n" );
|
||||
|
||||
vm::util::deobfuscate( vm_entry );
|
||||
vm::util::print( vm_entry );
|
||||
|
||||
if ( !( vm_handler_table = vm::handler::table::get( vm_entry ) ) )
|
||||
{
|
||||
std::printf( "[!] failed to obtain vm handler table...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( !vm::handler::get_all( module_base, image_base, vm_entry, vm_handler_table, vm_handlers ) )
|
||||
{
|
||||
std::printf( "[!] failed to get all vm handlers...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ( !vm::calc_jmp::get( vm_entry, calc_jmp ) )
|
||||
{
|
||||
std::printf( "[!] failed to get calc_jmp...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto advancement = vm::calc_jmp::get_advancement( calc_jmp );
|
||||
|
||||
if ( !advancement.has_value() )
|
||||
{
|
||||
std::printf( "[!] failed to determine advancement direction...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::printf( "> virtual instruction pointer advances %s...\n",
|
||||
advancement.value() == vmp2::exec_type_t::forward ? "forward" : "backward" );
|
||||
|
||||
vm::compiler_t compiler( { module_base, image_base }, advancement.value(), &vm_handlers, &calc_jmp, &vm_entry );
|
||||
|
||||
//
|
||||
// encode virtual instructions...
|
||||
//
|
||||
|
||||
auto [ encoded_success, vinstrs ] = compiler.encode();
|
||||
std::printf( "[+] finished encoding... encoded instructions below...\n" );
|
||||
|
||||
if ( !encoded_success )
|
||||
{
|
||||
std::printf( "[!] failed to encode virtual instructions...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
for ( auto &vinstr : *vinstrs )
|
||||
{
|
||||
if ( vinstr.imm_size )
|
||||
std::printf( "> 0x%x - 0x%x\n", vinstr.vm_handler, vinstr.operand );
|
||||
else
|
||||
std::printf( "> 0x%x\n", vinstr.vm_handler );
|
||||
}
|
||||
|
||||
//
|
||||
// encrypt virtual instructions...
|
||||
//
|
||||
|
||||
auto [ entry_rva, result_buffer ] = compiler.encrypt();
|
||||
std::printf( "[+] finished encrypting... encrypted instructions below...\n" );
|
||||
|
||||
if ( !entry_rva )
|
||||
{
|
||||
std::printf( "[!] failed to encrypt virtual instructions...\n" );
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::printf( "> virtual instructions must be allocated at = 0x%p\n", entry_rva );
|
||||
std::printf( "> " );
|
||||
{
|
||||
auto idx = 0u;
|
||||
for ( auto byte : *result_buffer )
|
||||
{
|
||||
std::printf( "0x%x ", byte );
|
||||
if ( ++idx == 10 )
|
||||
{
|
||||
std::printf( "\n" );
|
||||
idx = 0u;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::printf( "\n" );
|
||||
|
||||
//
|
||||
// write the result to disk...
|
||||
//
|
||||
|
||||
vmasm::file_header_t file_header;
|
||||
file_header.magic = VASM_MAGIC;
|
||||
file_header.epoch_time = std::time( nullptr );
|
||||
file_header.vasm_size = result_buffer->size();
|
||||
file_header.alloc_rva = ( entry_rva - image_base );
|
||||
file_header.vasm_offset = sizeof vmasm::file_header_t;
|
||||
file_header.encrypted_rva = compiler.encrypt_rva( entry_rva );
|
||||
|
||||
std::ofstream output( argp.get< std::string >( "output" ), std::ios::binary );
|
||||
output.write( reinterpret_cast< char * >( &file_header ), sizeof file_header );
|
||||
output.write( reinterpret_cast< char * >( result_buffer->data() ), result_buffer->size() );
|
||||
output.close();
|
||||
}
|
@ -1,31 +1,32 @@
|
||||
#include "parser.h"
|
||||
|
||||
parse_t::parse_t()
|
||||
{}
|
||||
parse_t::parse_t()
|
||||
{
|
||||
}
|
||||
|
||||
auto parse_t::get_instance() -> parse_t*
|
||||
auto parse_t::get_instance() -> parse_t *
|
||||
{
|
||||
static parse_t obj;
|
||||
return &obj;
|
||||
static parse_t obj;
|
||||
return &obj;
|
||||
}
|
||||
|
||||
void parse_t::add_vinstr(std::string vinstr_name)
|
||||
void parse_t::add_vinstr( std::string vinstr_name )
|
||||
{
|
||||
_vinstr_meta data{ vinstr_name, false, 0u };
|
||||
vinstrs.push_back(data);
|
||||
_vinstr_meta data{ vinstr_name, false, 0u };
|
||||
vinstrs.push_back( data );
|
||||
}
|
||||
|
||||
void parse_t::add_vinstr(std::string vinstr_name, std::uintptr_t imm_val)
|
||||
void parse_t::add_vinstr( std::string vinstr_name, std::uintptr_t imm_val )
|
||||
{
|
||||
_vinstr_meta data{ vinstr_name, true, imm_val };
|
||||
vinstrs.push_back(data);
|
||||
_vinstr_meta data{ vinstr_name, true, imm_val };
|
||||
vinstrs.push_back( data );
|
||||
}
|
||||
|
||||
bool parse_t::for_each(callback_t callback)
|
||||
bool parse_t::for_each( callback_t callback )
|
||||
{
|
||||
for (auto& entry : vinstrs)
|
||||
if (!callback(&entry))
|
||||
return false;
|
||||
for ( auto &entry : vinstrs )
|
||||
if ( !callback( &entry ) )
|
||||
return false;
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}
|
@ -1,29 +1,30 @@
|
||||
#pragma once
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
struct _vinstr_meta
|
||||
{
|
||||
std::string name;
|
||||
std::string name;
|
||||
|
||||
bool has_imm;
|
||||
std::uintptr_t imm;
|
||||
bool has_imm;
|
||||
std::uintptr_t imm;
|
||||
};
|
||||
|
||||
using callback_t = std::function<bool(_vinstr_meta*)>;
|
||||
using callback_t = std::function< bool( _vinstr_meta * ) >;
|
||||
|
||||
// this singleton class contains all the
|
||||
// this singleton class contains all the
|
||||
// information for parsed virtual instructions...
|
||||
class parse_t
|
||||
{
|
||||
public:
|
||||
static auto get_instance() -> parse_t*;
|
||||
void add_vinstr(std::string vinstr_name);
|
||||
void add_vinstr(std::string vinstr_name, std::uintptr_t imm_val);
|
||||
bool for_each(callback_t callback);
|
||||
private:
|
||||
parse_t();
|
||||
std::vector<_vinstr_meta> vinstrs;
|
||||
public:
|
||||
static auto get_instance() -> parse_t *;
|
||||
void add_vinstr( std::string vinstr_name );
|
||||
void add_vinstr( std::string vinstr_name, std::uintptr_t imm_val );
|
||||
bool for_each( callback_t callback );
|
||||
|
||||
private:
|
||||
parse_t();
|
||||
std::vector< _vinstr_meta > vinstrs;
|
||||
};
|
Loading…
Reference in new issue