PERSES Initial commit

master
mike1k 2 years ago
commit 853324ff52

2
.gitignore vendored

@ -0,0 +1,2 @@
*.exe
build/

15
.gitmodules vendored

@ -0,0 +1,15 @@
[submodule "vendor/spdlog"]
path = vendor/spdlog
url = https://github.com/gabime/spdlog
[submodule "vendor/asmjit"]
path = vendor/asmjit
url = https://github.com/asmjit/asmjit
[submodule "vendor/argparse"]
path = vendor/argparse
url = https://github.com/p-ranav/argparse
[submodule "vendor/pepp"]
path = vendor/pepp
url = https://github.com/mike1k/pepp
[submodule "vendor/zydis"]
path = vendor/zydis
url = https://github.com/zyantific/zydis.git

@ -0,0 +1,102 @@
# This file is automatically generated from cmake.toml - DO NOT EDIT
# See https://github.com/build-cpp/cmkr for more information
cmake_minimum_required(VERSION 3.15)
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR)
message(FATAL_ERROR "In-tree builds are not supported. Run CMake from a separate directory: cmake -B build")
endif()
# Regenerate CMakeLists.txt automatically in the root project
set(CMKR_ROOT_PROJECT OFF)
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
set(CMKR_ROOT_PROJECT ON)
# Bootstrap cmkr
include(cmkr.cmake OPTIONAL RESULT_VARIABLE CMKR_INCLUDE_RESULT)
if(CMKR_INCLUDE_RESULT)
cmkr()
endif()
# Enable folder support
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
endif()
# Create a configure-time dependency on cmake.toml to improve IDE support
if(CMKR_ROOT_PROJECT)
configure_file(cmake.toml cmake.toml COPYONLY)
endif()
project(perses)
# vendor
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/vendor")
else()
set(CMAKE_FOLDER vendor)
endif()
add_subdirectory(vendor)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# Target perses
set(CMKR_TARGET perses)
set(perses_SOURCES "")
list(APPEND perses_SOURCES
"src/disasm.cpp"
"src/mapfileparser.cpp"
"src/mutationlight.cpp"
"src/perses.cpp"
"src/x86application.cpp"
"src/details.hpp"
"src/disasm.hpp"
"src/mapfileparser.hpp"
"src/mutationlight.hpp"
"src/perses.hpp"
"src/protectionschema.hpp"
"src/util.hpp"
"src/x86application.hpp"
"src/x86util.hpp"
)
list(APPEND perses_SOURCES
cmake.toml
)
set(CMKR_SOURCES ${perses_SOURCES})
add_executable(perses)
if(perses_SOURCES)
target_sources(perses PRIVATE ${perses_SOURCES})
endif()
get_directory_property(CMKR_VS_STARTUP_PROJECT DIRECTORY ${PROJECT_SOURCE_DIR} DEFINITION VS_STARTUP_PROJECT)
if(NOT CMKR_VS_STARTUP_PROJECT)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT perses)
endif()
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${perses_SOURCES})
target_compile_definitions(perses PRIVATE
NOMINMAX
ZYCORE_STATIC_BUILD
ZYDIS_STATIC_BUILD
ASMJIT_STATIC
)
target_compile_features(perses PRIVATE
cxx_std_20
)
target_link_libraries(perses PRIVATE
spdlog
Zydis
asmjit
argparse
pepp
)
unset(CMKR_TARGET)
unset(CMKR_SOURCES)

@ -0,0 +1,160 @@
<div align="center">
<img src="https://i.imgur.com/Yi2ya5f.png" width="75%" height="75%">
</div>
# Introduction
PERSES is a X86 code obfuscation engine that works with [Portable Executable](https://en.wikipedia.org/wiki/Portable_Executable) files. The obfuscation works by replacing a specific instruction with a larger more sophisticated set that is semantically compatible to the original. PERSES only mutates 4 instructions yet has adverse effects on analyzers/decompilers due to the method of mutation. For more information on PERSES' inner workings, please check out the article written [here](https://back.engineering/13/04/2022/).
PERSES is a work in progress and does not attempt to be a replacement for any established code obfuscation engine, so please be mindful when using it to protect your code. Furthermore, X64 support can be slightly improved and augmented to ensure semantical accuracy.
# Reference Manual
PERSES by default works off a command line. Listed below are the arguments requried to utilize PERSES.
| Argument | Description | May Require |
|-------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |-------------------- |
| `-f` or `--file` | Path to the input PE file. | :heavy_check_mark: |
| `-a` or `-address` | Address or list of addresses required to be mutated. | :heavy_check_mark: |
| `-s` or `--symbol` | Symbol or list of symbols to be mutated. This requires a linked `.map` file. | :x: |
| `--map` | Map file to be linked. IDA Pro `.map` files must have their extension replaced with `.ida`. | :x: |
| `--list` | List of functions to be mutated. Each entry must envelop one line and be formatted as `0x1000:0x2000` where `0x1000` is the start and `0x2000` is the end of the routine. | :x: |
| `--x64` | Used to indicate that the file is of 64bit architecture (AMD64). | :heavy_check_mark: |
| `--rets` | Allow PERSES to build a `RET` gadget pool used to create `JMP`s to random locations. | :x: |
| `--scan` | Force PERSES to scan for code protection markers. | :x: |
Both symbols and addresses can be used, but atleast one of them must be present.
> :information_source: Due to limitations in the `argparse` library, if more than one address or symbol are required, please append the `-a` or `-s` argument and parameters last.
## Working with PERSES Manually
If desired, PERSES can be used manually to generate mutated binaries. To begin, one must declare a `X86BinaryApplication` instance with the provided template parameter.
* `X86BinaryApplication<PERSES_64BIT>(filepath)` to generate a `X86_64` instance.
* `X86BinaryApplication<PERSES_32BIT>(filepath)` to generate a `X86` instance.
### RET Gadgets
After instantiating the object, the `RET` gadget pool mentioned above can be optionally created with `perses::buildKnownRetGadgets(app)`.
### MAP Files
MAP files are optionally produced by compilers to show symbols and sections present in a binary with their corresponding address. PERSES allows MAP files from `IDA Pro` and `MSVC` (tested on VS2022) to be linked via `X86BinaryApplication::linkMapFile`. Afterwards, symbols can be traversed via `X86BinaryApplication::getSymbols` or added directly into the mutation queue by calling `addRoutineBySymbol`. For instance, after linking the map file, adding `main` to the mutation queue is as simple as:
```cpp
app->addRoutineBySymbol("main", PERSES_MARKER_MUTATION);
```
MAP files can aid function size calculation by exposing known symbols, however, MAP linking is completely optional and only added as a convenience.
### Function Lists
If mutating a batch of functions is wanted, function lists can be parsed in order to add the specified routines automatically. This is done by calling `parseFunctionList`. Please be mindful of the required format listed [above](#Reference-Manual) in the argument table. Futhermore, the end address supplied is expected to be the end of a function, providing anything else will likely result in instability of the output program.
### Markers
The [PersesSDK](https://github.com/mike1k/perses/sdk/PersesSDK.h) can be included into a project to emit a scannable pattern into code. PERSES makes use of compiler intrinsics to generate unique patterns. Beginning and end macros named `PERSES_MUTATION_START()` and `PERSES_MUTATION_END()` are provided.
### Applying Transforms
Applying mutation on all routines in the queue is done by calling `X86BinaryApplication::transformRoutines`. Transforms are applied via the corresponding schema. At the moment, there is only one schema supplied; `MutationLight`.
### Compiling.
Compilation of the new binary can be done with `X86BinaryApplication::compile`. PERSES creates a new file and appends `.perses` after the original filename.
# Showcase
Below are some example programs created to show the efficacy in regards to crippling decompiler output.
## Hello, world!
```cpp
int main()
{
PERSES_MUTATION_START()
printf("Hello, world!\n");
Sleep(100);
PERSES_MUTATION_END()
return getchar();
}
```
### Output
![Output1](https://i.imgur.com/k3MvscV.png)
## Jump Table Mutation
```cpp
int main()
{
int input = 0;
std::cin >> input;
switch (input)
{
case 0:
std::cout << "Value is zero" << std::endl;
break;
case 1:
std::cout << "Value is one" << std::endl;
break;
case 2:
std::cout << "Value is two" << std::endl;
break;
case 3:
std::cout << "Value is three" << std::endl;
break;
case 4:
std::cout << "Value is four" << std::endl;
break;
default:
std::cerr << "Unhandled value!" << std::endl;
break;
}
return getchar();
}
```
> Full function mutation using command line
```
perses -f MutationTesting.exe --map MutationTesting.map --rets -s _main
```
### Output
![Output2](https://i.imgur.com/79aWu5c.png)
# Modification
Additional schemas can be created then attached to `X86BinaryApplication::buildSchema`. Alternatively, `MutationLight` can be extended as it only supports a minimal set of instructions. In order to modify the existing schema, please thoroughly read and understand [MutationLight.cpp](https://github.com/mike1k/src/MutationLight.cpp).
# Building (Windows)
PERSES utilizes [cmkr](https://github.com/build-cpp/cmkr). In order to build the PERSES project, please run the following commands:
```
git clone --recursive https://github.com/mike1k/perses.git
cmake -B build
```
# Dependencies
PERSES makes use of multiple great libraries in order to achieve it's objective.
* [Zydis](https://github.com/zyantific/zydis)
* [AsmJit](https://github.com/asmjit/asmjit)
* [spdlog](https://github.com/gabime/spdlog)
* [argparse](https://github.com/p-ranav/argparse)
* [pepp](https://github.com/mike1k/pepp)

@ -0,0 +1,11 @@
[project]
name = "perses"
[subdir.vendor]
[target.perses]
type = "executable"
sources = ["src/**.cpp", "src/**.hpp"]
link-libraries = ["spdlog", "Zydis", "asmjit", "argparse", "pepp"]
compile-features = ["cxx_std_20"]
compile-definitions = ["NOMINMAX", "ZYCORE_STATIC_BUILD", "ZYDIS_STATIC_BUILD", "ASMJIT_STATIC"]

@ -0,0 +1,236 @@
include_guard()
# Change these defaults to point to your infrastructure if desired
set(CMKR_REPO "https://github.com/build-cpp/cmkr" CACHE STRING "cmkr git repository" FORCE)
set(CMKR_TAG "v0.2.12" CACHE STRING "cmkr git tag (this needs to be available forever)" FORCE)
set(CMKR_COMMIT_HASH "" CACHE STRING "cmkr git commit hash (optional)" FORCE)
# To bootstrap/generate a cmkr project: cmake -P cmkr.cmake
if(CMAKE_SCRIPT_MODE_FILE)
set(CMAKE_BINARY_DIR "${CMAKE_BINARY_DIR}/build")
set(CMAKE_CURRENT_BINARY_DIR "${CMAKE_BINARY_DIR}")
file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}")
endif()
# Set these from the command line to customize for development/debugging purposes
set(CMKR_EXECUTABLE "" CACHE FILEPATH "cmkr executable")
set(CMKR_SKIP_GENERATION OFF CACHE BOOL "skip automatic cmkr generation")
set(CMKR_BUILD_TYPE "Debug" CACHE STRING "cmkr build configuration")
mark_as_advanced(CMKR_REPO CMKR_TAG CMKR_COMMIT_HASH CMKR_EXECUTABLE CMKR_SKIP_GENERATION CMKR_BUILD_TYPE)
# Disable cmkr if generation is disabled
if(DEFINED ENV{CI} OR CMKR_SKIP_GENERATION OR CMKR_BUILD_SKIP_GENERATION)
message(STATUS "[cmkr] Skipping automatic cmkr generation")
unset(CMKR_BUILD_SKIP_GENERATION CACHE)
macro(cmkr)
endmacro()
return()
endif()
# Disable cmkr if no cmake.toml file is found
if(NOT CMAKE_SCRIPT_MODE_FILE AND NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
message(AUTHOR_WARNING "[cmkr] Not found: ${CMAKE_CURRENT_SOURCE_DIR}/cmake.toml")
macro(cmkr)
endmacro()
return()
endif()
# Convert a Windows native path to CMake path
if(CMKR_EXECUTABLE MATCHES "\\\\")
string(REPLACE "\\" "/" CMKR_EXECUTABLE_CMAKE "${CMKR_EXECUTABLE}")
set(CMKR_EXECUTABLE "${CMKR_EXECUTABLE_CMAKE}" CACHE FILEPATH "" FORCE)
unset(CMKR_EXECUTABLE_CMAKE)
endif()
# Helper macro to execute a process (COMMAND_ERROR_IS_FATAL ANY is 3.19 and higher)
function(cmkr_exec)
execute_process(COMMAND ${ARGV} RESULT_VARIABLE CMKR_EXEC_RESULT)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "cmkr_exec(${ARGV}) failed (exit code ${CMKR_EXEC_RESULT})")
endif()
endfunction()
# Windows-specific hack (CMAKE_EXECUTABLE_PREFIX is not set at the moment)
if(WIN32)
set(CMKR_EXECUTABLE_NAME "cmkr.exe")
else()
set(CMKR_EXECUTABLE_NAME "cmkr")
endif()
# Use cached cmkr if found
if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}")
set(CMKR_DIRECTORY_PREFIX "$ENV{CMKR_CACHE}")
string(REPLACE "\\" "/" CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}")
if(NOT CMKR_DIRECTORY_PREFIX MATCHES "\\/$")
set(CMKR_DIRECTORY_PREFIX "${CMKR_DIRECTORY_PREFIX}/")
endif()
# Build in release mode for the cache
set(CMKR_BUILD_TYPE "Release")
else()
set(CMKR_DIRECTORY_PREFIX "${CMAKE_CURRENT_BINARY_DIR}/_cmkr_")
endif()
set(CMKR_DIRECTORY "${CMKR_DIRECTORY_PREFIX}${CMKR_TAG}")
set(CMKR_CACHED_EXECUTABLE "${CMKR_DIRECTORY}/bin/${CMKR_EXECUTABLE_NAME}")
# Handle upgrading logic
if(CMKR_EXECUTABLE AND NOT CMKR_CACHED_EXECUTABLE STREQUAL CMKR_EXECUTABLE)
if(CMKR_EXECUTABLE MATCHES "^${CMAKE_CURRENT_BINARY_DIR}/_cmkr")
if(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}")
message(AUTHOR_WARNING "[cmkr] Switching to cached cmkr: '${CMKR_CACHED_EXECUTABLE}'")
if(EXISTS "${CMKR_CACHED_EXECUTABLE}")
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
else()
unset(CMKR_EXECUTABLE CACHE)
endif()
else()
message(AUTHOR_WARNING "[cmkr] Upgrading '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
unset(CMKR_EXECUTABLE CACHE)
endif()
elseif(DEFINED ENV{CMKR_CACHE} AND EXISTS "$ENV{CMKR_CACHE}" AND CMKR_EXECUTABLE MATCHES "^${CMKR_DIRECTORY_PREFIX}")
message(AUTHOR_WARNING "[cmkr] Upgrading cached '${CMKR_EXECUTABLE}' to '${CMKR_CACHED_EXECUTABLE}'")
unset(CMKR_EXECUTABLE CACHE)
endif()
endif()
if(CMKR_EXECUTABLE AND EXISTS "${CMKR_EXECUTABLE}")
message(VERBOSE "[cmkr] Found cmkr: '${CMKR_EXECUTABLE}'")
elseif(CMKR_EXECUTABLE AND NOT CMKR_EXECUTABLE STREQUAL CMKR_CACHED_EXECUTABLE)
message(FATAL_ERROR "[cmkr] '${CMKR_EXECUTABLE}' not found")
elseif(NOT CMKR_EXECUTABLE AND EXISTS "${CMKR_CACHED_EXECUTABLE}")
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
message(STATUS "[cmkr] Found cached cmkr: '${CMKR_EXECUTABLE}'")
else()
set(CMKR_EXECUTABLE "${CMKR_CACHED_EXECUTABLE}" CACHE FILEPATH "Full path to cmkr executable" FORCE)
message(VERBOSE "[cmkr] Bootstrapping '${CMKR_EXECUTABLE}'")
message(STATUS "[cmkr] Fetching cmkr...")
if(EXISTS "${CMKR_DIRECTORY}")
cmkr_exec("${CMAKE_COMMAND}" -E rm -rf "${CMKR_DIRECTORY}")
endif()
find_package(Git QUIET REQUIRED)
cmkr_exec("${GIT_EXECUTABLE}"
clone
--config advice.detachedHead=false
--branch ${CMKR_TAG}
--depth 1
${CMKR_REPO}
"${CMKR_DIRECTORY}"
)
if(CMKR_COMMIT_HASH)
execute_process(
COMMAND "${GIT_EXECUTABLE}" checkout -q "${CMKR_COMMIT_HASH}"
RESULT_VARIABLE CMKR_EXEC_RESULT
WORKING_DIRECTORY "${CMKR_DIRECTORY}"
)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "Tag '${CMKR_TAG}' hash is not '${CMKR_COMMIT_HASH}'")
endif()
endif()
message(STATUS "[cmkr] Building cmkr (using system compiler)...")
cmkr_exec("${CMAKE_COMMAND}"
--no-warn-unused-cli
"${CMKR_DIRECTORY}"
"-B${CMKR_DIRECTORY}/build"
"-DCMAKE_BUILD_TYPE=${CMKR_BUILD_TYPE}"
"-DCMAKE_UNITY_BUILD=ON"
"-DCMAKE_INSTALL_PREFIX=${CMKR_DIRECTORY}"
"-DCMKR_GENERATE_DOCUMENTATION=OFF"
)
cmkr_exec("${CMAKE_COMMAND}"
--build "${CMKR_DIRECTORY}/build"
--config "${CMKR_BUILD_TYPE}"
--parallel
)
cmkr_exec("${CMAKE_COMMAND}"
--install "${CMKR_DIRECTORY}/build"
--config "${CMKR_BUILD_TYPE}"
--prefix "${CMKR_DIRECTORY}"
--component cmkr
)
if(NOT EXISTS ${CMKR_EXECUTABLE})
message(FATAL_ERROR "[cmkr] Failed to bootstrap '${CMKR_EXECUTABLE}'")
endif()
cmkr_exec("${CMKR_EXECUTABLE}" version)
message(STATUS "[cmkr] Bootstrapped ${CMKR_EXECUTABLE}")
endif()
execute_process(COMMAND "${CMKR_EXECUTABLE}" version
RESULT_VARIABLE CMKR_EXEC_RESULT
)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "[cmkr] Failed to get version, try clearing the cache and rebuilding")
endif()
# Use cmkr.cmake as a script
if(CMAKE_SCRIPT_MODE_FILE)
if(NOT EXISTS "${CMAKE_SOURCE_DIR}/cmake.toml")
execute_process(COMMAND "${CMKR_EXECUTABLE}" init
RESULT_VARIABLE CMKR_EXEC_RESULT
)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "[cmkr] Failed to bootstrap cmkr project. Please report an issue: https://github.com/build-cpp/cmkr/issues/new")
else()
message(STATUS "[cmkr] Modify cmake.toml and then configure using: cmake -B build")
endif()
else()
execute_process(COMMAND "${CMKR_EXECUTABLE}" gen
RESULT_VARIABLE CMKR_EXEC_RESULT
)
if(NOT CMKR_EXEC_RESULT EQUAL 0)
message(FATAL_ERROR "[cmkr] Failed to generate project.")
else()
message(STATUS "[cmkr] Configure using: cmake -B build")
endif()
endif()
endif()
# This is the macro that contains black magic
macro(cmkr)
# When this macro is called from the generated file, fake some internal CMake variables
get_source_file_property(CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}" CMKR_CURRENT_LIST_FILE)
if(CMKR_CURRENT_LIST_FILE)
set(CMAKE_CURRENT_LIST_FILE "${CMKR_CURRENT_LIST_FILE}")
get_filename_component(CMAKE_CURRENT_LIST_DIR "${CMAKE_CURRENT_LIST_FILE}" DIRECTORY)
endif()
# File-based include guard (include_guard is not documented to work)
get_source_file_property(CMKR_INCLUDE_GUARD "${CMAKE_CURRENT_LIST_FILE}" CMKR_INCLUDE_GUARD)
if(NOT CMKR_INCLUDE_GUARD)
set_source_files_properties("${CMAKE_CURRENT_LIST_FILE}" PROPERTIES CMKR_INCLUDE_GUARD TRUE)
file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_PRE)
# Generate CMakeLists.txt
cmkr_exec("${CMKR_EXECUTABLE}" gen
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
)
file(SHA256 "${CMAKE_CURRENT_LIST_FILE}" CMKR_LIST_FILE_SHA256_POST)
# Delete the temporary file if it was left for some reason
set(CMKR_TEMP_FILE "${CMAKE_CURRENT_SOURCE_DIR}/CMakerLists.txt")
if(EXISTS "${CMKR_TEMP_FILE}")
file(REMOVE "${CMKR_TEMP_FILE}")
endif()
if(NOT CMKR_LIST_FILE_SHA256_PRE STREQUAL CMKR_LIST_FILE_SHA256_POST)
# Copy the now-generated CMakeLists.txt to CMakerLists.txt
# This is done because you cannot include() a file you are currently in
configure_file(CMakeLists.txt "${CMKR_TEMP_FILE}" COPYONLY)
# Add the macro required for the hack at the start of the cmkr macro
set_source_files_properties("${CMKR_TEMP_FILE}" PROPERTIES
CMKR_CURRENT_LIST_FILE "${CMAKE_CURRENT_LIST_FILE}"
)
# 'Execute' the newly-generated CMakeLists.txt
include("${CMKR_TEMP_FILE}")
# Delete the generated file
file(REMOVE "${CMKR_TEMP_FILE}")
# Do not execute the rest of the original CMakeLists.txt
return()
endif()
# Resume executing the unmodified CMakeLists.txt
endif()
endmacro()

@ -0,0 +1,22 @@
#pragma once
#include <intrin.h>
#include <intrin0.h>
#define PERSES_MUTATION_START() \
{\
__nop();\
__nop();\
__debugbreak();\
__debugbreak();\
_disable();\
}
#define PERSES_MUTATION_END() \
{\
_enable();\
__debugbreak();\
__debugbreak();\
__nop();\
__nop();\
}

@ -0,0 +1,84 @@
#pragma once
// - MACROs for specific toggles
#define PERSES_VERBOSE
#define PERSES_DEBUGGABLE
#ifdef PERSES_DEBUGGABLE
#define PERSES_THROW(msg) throw msg
#define PERSES_THROWIFN(cnd, msg) if (!cnd) PERSES_THROW(msg)
#define PERSES_THROWIF(cnd, msg) if (cnd) PERSES_THROW(msg)
#else
#define PERSES_THROW(msg)
#define PERSES_THROWIFN(cnd, msg)
#define PERSES_THROWIF(cnd, msg)
#endif
#define PERSES_32BIT 32
#define PERSES_64BIT 64
#define PERSES_MARKER_MUTATION 0
#define PERSES_MARKER_VIRTUALIZATION 1
// #define PERSES_BITSTOBYTES(bits) (((bits) + 7) >> 3)
// #define PERSES_BYTESTOBITS(bits) (bits << 3)
constexpr auto PERSES_BITSTOBYTES(size_t bits) noexcept {
return (((bits)+7) >> 3);
}
constexpr auto PERSES_BYTESTOBITS(size_t bytes) noexcept {
return (bytes << 3);
}
namespace perses
{
class ProtectionSchema;
namespace pe = pepp;
namespace assembler = asmjit;
using address = pe::Address< >;
using u8 = uint8_t;
using u16 = uint16_t;
using u32 = uint32_t;
using u64 = uint64_t;
using uptr = uintptr_t;
using SharedProtectionSchema = std::shared_ptr<ProtectionSchema>;
struct RelocationEntry
{
pepp::RelocationType type;
u32 offset;
// Only used on x64 for RIP relative instructions.
u32 base;
u32 length;
u64 absolute;
u64 stream = 0ull;
};
struct JumpTableEntry
{
u32 rva;
u64 address;
u32 newOffset;
assembler::Label label;
};
// - These are what get scanned in .text sections by the engine
// - These two are the begin/end markers respectively.
// - LIMITATIONS:
// * These markers MUST be 5 bytes in length!
inline std::tuple<int, std::string, u64> MarkerTags[] =
{
{PERSES_MARKER_MUTATION, "CC CC 90 90 FA", 0xCCCC9090FBull}
};
}
#define PERSES_MUTATE_FULL 0xf001c0de
#define PERSES_MARKER_SIZE 0x5

@ -0,0 +1,238 @@
#include "perses.hpp"
using namespace perses;
static Disassembler s_disasm;
Disassembler* Disassembler::instance()
{
return &s_disasm;
}
void Disassembler::create(ZydisMachineMode mode)
{
// - Initialize formatter
ZydisFormatterInit(&s_disasm._formatter, ZYDIS_FORMATTER_STYLE_INTEL);
ZydisFormatterSetProperty(&s_disasm._formatter, ZYDIS_FORMATTER_PROP_FORCE_SIZE, ZYAN_TRUE);
s_disasm._mode = mode;
if (mode == ZYDIS_MACHINE_MODE_LONG_64)
{
ZydisDecoderInit(&s_disasm._decoder, s_disasm._mode, ZYDIS_STACK_WIDTH_64);
return;
}
if (mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32)
{
ZydisDecoderInit(&s_disasm._decoder, s_disasm._mode, ZYDIS_STACK_WIDTH_32);
return;
}
PERSES_THROW("Unexpected machine mode passed into Disassembler::create()!");
}
bool Disassembler::decode(void* buf, instruction_t* instr)
{
ZyanStatus status = ZydisDecoderDecodeFull(&_decoder, buf, 0xFFF, &instr->decoded, instr->operands, ZYDIS_MAX_OPERAND_COUNT_VISIBLE, ZYDIS_DFLAG_VISIBLE_OPERANDS_ONLY);
if (ZYAN_SUCCESS(status))
{
instr->raw.resize(instr->decoded.length);
memcpy(&instr->raw[0], buf, instr->decoded.length);
return true;
}
return false;
}
bool Disassembler::decode(void* buf, ZydisDecodedInstruction* instr, ZydisDecodedOperand* op)
{
return ZYAN_SUCCESS(
ZydisDecoderDecodeFull(&_decoder, buf, 0xFFF, instr, op, ZYDIS_MAX_OPERAND_COUNT_VISIBLE, ZYDIS_DFLAG_VISIBLE_OPERANDS_ONLY));
}
u64 Disassembler::calcAbsolute(instruction_t* instr)
{
u64 dst = 0ull;
ZydisCalcAbsoluteAddress(&instr->decoded, &instr->operands[0], instr->address, &dst);
return dst;
}
u64 perses::Disassembler::calcAbsolute(ZydisDecodedInstruction* instr, ZydisDecodedOperand* op, u64 address)
{
u64 dst = 0ull;
ZyanStatus status = ZydisCalcAbsoluteAddress(instr, op, address, &dst);
return dst;
}
bool Disassembler::getSegments(instruction_t* intr, ZydisInstructionSegments* segments)
{
return ZYAN_SUCCESS(
ZydisGetInstructionSegments(&intr->decoded, segments));
}
bool Disassembler::isJmp(instruction_t* i)
{
ZydisDecodedInstruction* instr = &i->decoded;
switch (instr->mnemonic)
{
case ZYDIS_MNEMONIC_JNBE:
case ZYDIS_MNEMONIC_JB:
case ZYDIS_MNEMONIC_JBE:
case ZYDIS_MNEMONIC_JCXZ:
case ZYDIS_MNEMONIC_JECXZ:
case ZYDIS_MNEMONIC_JKNZD:
case ZYDIS_MNEMONIC_JKZD:
case ZYDIS_MNEMONIC_JL:
case ZYDIS_MNEMONIC_JLE:
case ZYDIS_MNEMONIC_JNB:
case ZYDIS_MNEMONIC_JNL:
case ZYDIS_MNEMONIC_JNLE:
case ZYDIS_MNEMONIC_JNO:
case ZYDIS_MNEMONIC_JNP:
case ZYDIS_MNEMONIC_JNS:
case ZYDIS_MNEMONIC_JNZ:
case ZYDIS_MNEMONIC_JO:
case ZYDIS_MNEMONIC_JP:
case ZYDIS_MNEMONIC_JRCXZ:
case ZYDIS_MNEMONIC_JS:
case ZYDIS_MNEMONIC_JZ:
case ZYDIS_MNEMONIC_JMP:
case ZYDIS_MNEMONIC_CALL:
return true;
default:
return false;
}
return false;
}
bool Disassembler::isBbTerminatorInstruction(instruction_t* i)
{
// Check if the instruction ends a basic block
ZydisDecodedInstruction* instr = &i->decoded;
switch (instr->mnemonic)
{
case ZYDIS_MNEMONIC_JNBE:
case ZYDIS_MNEMONIC_JB:
case ZYDIS_MNEMONIC_JBE:
case ZYDIS_MNEMONIC_JCXZ:
case ZYDIS_MNEMONIC_JECXZ:
case ZYDIS_MNEMONIC_JKNZD:
case ZYDIS_MNEMONIC_JKZD:
case ZYDIS_MNEMONIC_JL:
case ZYDIS_MNEMONIC_JLE:
case ZYDIS_MNEMONIC_JNB:
case ZYDIS_MNEMONIC_JNL:
case ZYDIS_MNEMONIC_JNLE:
case ZYDIS_MNEMONIC_JNO:
case ZYDIS_MNEMONIC_JNP:
case ZYDIS_MNEMONIC_JNS:
case ZYDIS_MNEMONIC_JNZ:
case ZYDIS_MNEMONIC_JO:
case ZYDIS_MNEMONIC_JP:
case ZYDIS_MNEMONIC_JRCXZ:
case ZYDIS_MNEMONIC_JS:
case ZYDIS_MNEMONIC_JZ:
case ZYDIS_MNEMONIC_JMP:
case ZYDIS_MNEMONIC_RET:
return true;
default:
return false;
}
return false;
}
std::string Disassembler::format(address addr, ZydisDecodedInstruction* instr, ZydisDecodedOperand* op)
{
char buf[0xFF]{};
ZyanStatus status = 0;
status = ZydisFormatterFormatInstruction(&_formatter, instr, op, instr->operand_count_visible, buf, sizeof(buf), addr.uintptr());
if (!ZYAN_SUCCESS(status))
{
return fmt::format("Unexpected error when formatting address: 0x{:X}", addr.uintptr());
}
return buf;
}
void Routine::buildFromCode(address buf)
{
return;
}
void Routine::printAssembly(uint32_t numInstructions)
{
if (empty())
return;
logger()->info("** Printing routine: 0x{:X}", at(0).address);
int count = 0;
for (auto& instr : *this)
{
if (numInstructions != -1)
{
if (count++ >= numInstructions)
break;
}
std::string fmt = Disassembler::instance()->format(instr.address, &instr.decoded, instr.operands);
logger()->debug("** 0x{:X}\t\t|\t{}", instr.address, fmt);
}
}
size_t perses::Routine::codeSize() const
{
size_t sz {};
for (auto& insn : *this)
sz += insn.decoded.length;
return sz;
}
bool instruction_t::isMnemonic(ZydisMnemonic mnem) const
{
return decoded.mnemonic == mnem;
}
bool instruction_t::isOperandType(size_t index, ZydisOperandType type) const
{
if (const ZydisDecodedOperand* op = getOperand(index))
{
return op->type == type;
}
return false;
}
const ZydisDecodedOperand* instruction_t::getOperand(size_t index) const
{
if (index >= decoded.operand_count_visible)
return nullptr;
return &operands[index];
}
size_t instruction_t::getFirstSegmentOffset(ZydisInstructionSegment type)
{
ZydisInstructionSegments segs { };
Disassembler::instance()->getSegments(this, &segs);
for (auto& seg : segs.segments)
{
if (seg.type == type)
return seg.offset;
}
return 0ull;
}

@ -0,0 +1,61 @@
#pragma once
namespace perses
{
struct instruction_t
{
bool isMnemonic(ZydisMnemonic mnem) const;
bool isOperandType(size_t index, ZydisOperandType type) const;
const ZydisDecodedOperand* getOperand(size_t index) const;
size_t getFirstSegmentOffset(ZydisInstructionSegment type);
uintptr_t address{ };
ZydisDecodedInstruction decoded{ };
ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT_VISIBLE]{ };
std::vector<uint8_t> raw{ };
};
class Disassembler
{
public:
static Disassembler* instance();
static void create(ZydisMachineMode mode);
bool decode(void* buf, instruction_t* instr);
bool decode(void* buf, ZydisDecodedInstruction* instr, ZydisDecodedOperand* op);
u64 calcAbsolute(instruction_t* intr);
u64 calcAbsolute(ZydisDecodedInstruction* instr, ZydisDecodedOperand* op, u64 address);
ZydisRegister enclosingReg(ZydisRegister);
bool getSegments(instruction_t* intr, ZydisInstructionSegments* segments);
bool isJmp(instruction_t* instr);
bool isBbTerminatorInstruction(instruction_t* instr);
std::string format(address addr, ZydisDecodedInstruction* instr, ZydisDecodedOperand* op);
private:
ZydisDecoder _decoder;
ZydisFormatter _formatter;
ZydisMachineMode _mode;
};
class Routine : public std::vector<instruction_t>
{
public:
Routine() = default;
void buildFromCode(address buf);
void printAssembly(uint32_t numInstructions = -1);
void addFlag(int flag) { _flag |= flag; }
void stripFlag(int flag) { _flag &= ~flag; }
int getFlag() const { return _flag; }
size_t codeSize() const;
uptr getAddress() const { return empty() ? 0 : at(0).address; }
private:
int _flag { };
};
}

@ -0,0 +1,75 @@
#include "perses.hpp"
#include <regex>
using namespace perses;
bool MapFileParser::parse(std::filesystem::path filePath)
{
if (!std::filesystem::exists(filePath))
return false;
std::ifstream infile(filePath);
std::string line;
while (std::getline(infile, line))
{
parseLine(line);
}
return !_symbols.empty();
}
bool MSVCMapFileParser::parseLine(std::string_view line)
{
std::string s = line.data();
std::regex rgx (R"#(\s(\d+)\:([a-fA-F0-9]+)\s+(\S+)\s+([a-fA-F0-9]+)\s+(.+))#", std::regex_constants::ECMAScript);
if (std::regex_match(s, rgx))
{
for (std::smatch m; std::regex_search(s, m, rgx); s = m.suffix())
{
MapSymbol symbol { };
symbol.address = std::strtoull(m[4].str().c_str(), nullptr, 16);
if (symbol.address == 0ull)
continue;
symbol.sectionIndex = std::atoi(m[1].str().c_str());
symbol.sectionOffset = std::strtoul(m[2].str().c_str(), nullptr, 16);
symbol.name = m[3].str();
symbol.libobj = m[5].str();
_symbols.emplace_back(std::move(symbol));
}
return true;
}
return false;
}
bool IDAMapFileParser::parseLine(std::string_view line)
{
std::string s = line.data();
std::regex rgx(R"#(.(\d+)\:([a-fA-F0-9]+)\s+(\S+))#", std::regex_constants::ECMAScript);
if (std::regex_match(s, rgx))
{
for (std::smatch m; std::regex_search(s, m, rgx); s = m.suffix())
{
MapSymbol symbol{ };
symbol.address = 0ull;
symbol.sectionIndex = std::atoi(m[1].str().c_str());
symbol.sectionOffset = std::strtoul(m[2].str().c_str(), nullptr, 16);
symbol.name = m[3].str();
_symbols.emplace_back(std::move(symbol));
}
return true;
}
return false;
}

@ -0,0 +1,55 @@
#pragma once
namespace perses
{
struct MapSymbol
{
u32 sectionIndex;
u32 sectionOffset;
u64 address;
std::string name;
std::string libobj;
};
enum class MapFileType
{
kIDAPro = 0,
kMSVC,
kLLVM
};
class MapFileParser : pe::msc::NonCopyable
{
public:
virtual ~MapFileParser() = default;
bool parse(std::filesystem::path filePath);
const std::vector<MapSymbol>& getSymbols() const {
return _symbols;
}
protected:
MapFileParser() = default;
virtual bool parseLine(std::string_view line) = 0;
protected:
std::vector<MapSymbol> _symbols;
};
class MSVCMapFileParser : public MapFileParser
{
public:
MSVCMapFileParser() {}
protected:
bool parseLine(std::string_view line) override;
};
class IDAMapFileParser : public MapFileParser
{
public:
IDAMapFileParser() {}
protected:
bool parseLine(std::string_view line) override;
};
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,48 @@
#pragma once
namespace perses
{
template<int BitSize>
class MutationLightSchema : public ProtectionSchema
{
public:
perses::assembler::CodeBuffer applyTransforms(Routine* rtn) override;
bool handlePush(instruction_t* insn);
bool handleMov(instruction_t* insn);
bool handleXor(instruction_t* insn);
bool handleAdd(instruction_t* insn);
bool handleRelInstruction(instruction_t* insn);
X86BinaryApplication<BitSize>* app();
protected:
void makeRelocation(int offset, bool relative = false, u64 absolute = 0ull);
u32 toRva(uptr address);
void fetchPeb(assembler::x86::Gp dst);
bool recoverJumpTable(instruction_t* insn);
void writeJcc(ZydisDecodedInstruction* instr, assembler::Label& lbl);
void genXor(assembler::x86::Gp dst, assembler::x86::Gp val);
void genXorImm(assembler::x86::Gp dst, u32 val);
void genAdd(assembler::x86::Gp dst, assembler::x86::Gp val);
void genAddImm(assembler::x86::Gp dst, u32 val);
private:
struct RelocGenEntry
{
u16 ioffset;
u16 roffset;
u16 length;
u64 absolute;
};
// Upper half of each entry stores the assosciated instruction's length
// Lower half will store the offet the relocation should happen.
std::vector<RelocGenEntry> _relocEntryList;
instruction_t* _currentInstruction;
uptr _rtnBegin, _rtnEnd;
std::vector<JumpTableEntry> _jumpTables;
u32 _streamOffset;
Routine *_rtn;
};
template<int BitSize>
void buildKnownRetGadgets(X86BinaryApplication<BitSize>* app);
std::vector<u64> getKnownRetGadgets();
}

@ -0,0 +1,177 @@
#include "perses.hpp"
#include <argparse/argparse.hpp>
template<int BitSize>
void createApplication(perses::X86BinaryApplication<BitSize>* app, argparse::ArgumentParser& args)
{
spdlog::stopwatch sw;
int numSeconds = 0;
float numMinutes = 0.f;
if (args.is_used("--rets"))
perses::buildKnownRetGadgets(app);
if (auto param = args.present<std::string>("--map"))
{
std::filesystem::path path = *param;
if (!std::filesystem::exists(path))
{
logger()->critical("Unable to parse map file, non existent!");
goto Delete;
}
if (path.extension().string() == ".ida")
app->linkMapFile(perses::MapFileType::kIDAPro, *param);
else
app->linkMapFile(perses::MapFileType::kMSVC, *param);
}
if (auto param = args.present<std::string>("--list"))
{
std::filesystem::path path = *param;
if (!std::filesystem::exists(path))
{
logger()->critical("Unable to parse list file, non existent!");
goto Delete;
}
app->parseFunctionList(*param);
}
if (auto param = args.present<std::vector<std::string>>("-a"))
{
for (auto& uaddr : *param)
{
perses::u64 addr = strtoull(uaddr.c_str(), nullptr, 16);
if (addr > 0)
app->addRoutineByAddress(addr, PERSES_MARKER_MUTATION);
}
}
if (auto param = args.present<std::vector<std::string>>("-s"))
{
if (app->hasMapFile())
{
for (auto& sym : *param)
{
app->addRoutineBySymbol(sym, PERSES_MARKER_MUTATION);
}
}
else
{
logger()->critical("Unable to use symbols argument without a linked .MAP file!");
goto Delete;
}
}
if (args.is_used("--scan"))
app->scanForMarkers();
if (app->getRoutines().empty())
{
logger()->critical("Unable to mutate: no routines in queue.");
goto Delete;
}
app->transformRoutines();
app->compile();
logger()->info("Mutated {} routines.", app->getRoutines().size());
numSeconds = std::chrono::duration_cast<std::chrono::seconds>(sw.elapsed()).count();
numMinutes = (float)numSeconds / 60.f;
if (numSeconds > 60)
logger()->info("It took {} minutes and {} seconds to complete this operation.", (int)(numSeconds / 60), (int)((float)(numMinutes - (int)numMinutes) * 60.f));
else
logger()->info("It took {} seconds to complete this operation.", numSeconds);
Delete:
delete app;
}
int main(int argc, char* argv[])
{
argparse::ArgumentParser args("PERSES");
void* app = nullptr;
args.add_argument("-f", "--file")
.help("Input file path.")
.required();
args.add_argument("-x64")
.help("Required for X64 PE files.")
.default_value(false)
.implicit_value(true);
args.add_argument("-a", "--address")
.help("Address(es) to mutate")
.remaining();
args.add_argument("-s", "--symbol")
.help("Symbol(s) to mutate (requires .MAP)")
.remaining();
args.add_argument("--list")
.help("Parsable function list (NOTE: all entries in the list will be added).");
args.add_argument("--map")
.help("Parsable map file (NOTE: IDA Pro .MAP files must have their extension named as \".ida\").");
args.add_argument("--rets")
.help("Use RET gadgets.")
.default_value(false)
.implicit_value(true);
args.add_argument("--scan")
.help("Scan for protection markers.")
.default_value(false)
.implicit_value(true);
if (argc <= 1)
{
args.print_help();
return 1;
}
try {
args.parse_args(argc, argv);
}
catch (const std::runtime_error& err) {
args.print_help();
return 1;
}
logger()->debug("PERSES Code Protection Engine");
std::string filepath = args.get<std::string>("-f");
if (!std::filesystem::exists(filepath))
{
logger()->critical("Unable to find file: {}.", filepath);
return 0;
}
if (args.get<bool>("-x64"))
{
createApplication(new perses::X86BinaryApplication<PERSES_64BIT>(filepath), args);
}
else
{
createApplication(new perses::X86BinaryApplication<PERSES_32BIT>(filepath), args);
}
return 0;
}
std::shared_ptr<spdlog::logger> logger()
{
static std::shared_ptr<spdlog::logger> log = nullptr;
if (!log)
{
log = spdlog::stdout_color_mt("console");
log->set_level(spdlog::level::debug);
log->set_pattern("[%^PERSES%$] %v");
spdlog::set_error_handler([](const std::string& msg) { printf("*** LOG ERROR: %s ***\n", msg.c_str()); });
}
return log;
}

@ -0,0 +1,36 @@
#pragma once
#include <Windows.h>
#include <iostream>
#include <string>
#include <string_view>
#include <map>
#include <filesystem>
#include <set>
// - X86 code assembling engine
#include <asmjit/asmjit.h>
// - X86 disassembler
#include <Zydis/Zydis.h>
// - PE (Portable Executable) library for parsing and manipulation.
#include <pepp/PELibrary.hpp>
// - Logging library
#include <spdlog/spdlog.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/stopwatch.h>
#include <spdlog/fmt/bin_to_hex.h>
// - Additional utilities
#include "details.hpp"
#include "disasm.hpp"
#include "util.hpp"
#include "MapFileParser.hpp"
#include "x86util.hpp"
#include "x86application.hpp"
#include "protectionschema.hpp"
std::shared_ptr<spdlog::logger> logger();

@ -0,0 +1,33 @@
#pragma once
namespace perses
{
template<int>
class X86BinaryApplication;
class ProtectionSchema
{
public:
virtual ~ProtectionSchema() = default;
virtual perses::assembler::CodeBuffer applyTransforms(Routine* rtn) = 0;
template<typename SchemaType>
[[nodiscard]] static SharedProtectionSchema create() {
return std::shared_ptr<SchemaType>(new SchemaType());
}
void setApplication(void* app) { _app = app; }
void* getApplication() { return _app; }
assembler::x86::Assembler* getCompiler() { return &_compiler; }
protected:
// Intellisense failing hard here.
void* _app;
assembler::CodeHolder _code;
assembler::x86::Assembler _compiler;
std::vector<RelocationEntry> _relocs;
};
}
#include "MutationLight.hpp"

@ -0,0 +1,26 @@
#pragma once
#include <random>
static std::default_random_engine rng;
namespace perses::util
{
template<typename _Ty>
_Ty genRandInteger()
{
std::random_device r;
std::default_random_engine e1(r());
std::uniform_int_distribution<_Ty> dist;
return dist(e1);
}
template<typename _Ty>
_Ty genRandInteger(_Ty min, _Ty max)
{
std::random_device r;
std::default_random_engine e1(r());
std::uniform_int_distribution<_Ty> dist(min, max);
return dist(e1);
}
}

File diff suppressed because it is too large Load Diff

@ -0,0 +1,51 @@
#pragma once
namespace perses
{
template<int BitSize = 32>
class X86BinaryApplication
{
public:
X86BinaryApplication() = delete;
X86BinaryApplication(std::string_view filePath);
~X86BinaryApplication();
void loadFromFile(std::string_view filePath);
SharedProtectionSchema buildSchema(int flag);
bool scanForMarkers();
bool addRoutineByAddress(uptr address, int marker);
bool addRoutineBySymbol(std::string_view symbolName, int marker);
bool addRoutineBySymbol(const MapSymbol* symbol, int marker);
bool addRoutineByAddress(uptr start, uptr end, int marker);
bool transformRoutines();
bool isRelocationPresent(u32 rva);
void removeRelocation(u32 rva);
void dumpRoutines();
void linkCode(Routine* origRtn, assembler::CodeHolder& code, const std::vector<RelocationEntry>& relocs, const std::vector<JumpTableEntry>& jumpTable);
void compile();
bool linkMapFile(MapFileType type, std::filesystem::path filePath);
bool hasMapFile() const;
bool inquireJumpTable(instruction_t* insn, uptr begin, uptr end, int entryCount, std::vector<JumpTableEntry>& entries);
bool parseFunctionList(std::filesystem::path path);
assembler::Environment getEnvironment();
uptr getBaseAddress() { return _peFile.getImageBase(); }
pe::Image<BitSize>& getImage() { return _peFile; }
std::vector<MapSymbol>& getSymbols() { return _mapSymbols; }
const std::vector<Routine>& getRoutines() const { return _routines; }
const std::set<u32>& getOriginalRelocs() const { return _originalRelocs; }
private:
std::vector<Routine> _routines;
std::map<uptr, std::pair<uptr, pepp::mem::ByteVector>> _proutines;
pe::Image<BitSize> _peFile;
std::set<u32> _originalRelocs;
std::vector<RelocationEntry> _newRelocs;
std::map<u32, std::vector<RelocationEntry>> _relocBlocks;
size_t _currentSectionOffset{};
std::filesystem::path _filePath;
std::vector<MapSymbol> _mapSymbols;
std::vector<MapSymbol> _mapFuncSymbols;
MapFileType _mapType;
uptr _persesAddr{};
};
}

@ -0,0 +1,125 @@
#pragma once
namespace perses::x86util
{
static asmjit::x86::Gp getAsmRegAny(ZydisRegister reg)
{
using namespace asmjit;
switch (reg)
{
case ZYDIS_REGISTER_RAX:
return x86::regs::rax;
case ZYDIS_REGISTER_RBX:
return x86::regs::rbx;
case ZYDIS_REGISTER_RCX:
return x86::regs::rcx;
case ZYDIS_REGISTER_RDX:
return x86::regs::rdx;
case ZYDIS_REGISTER_RSI:
return x86::regs::rsi;
case ZYDIS_REGISTER_RDI:
return x86::regs::rdi;
case ZYDIS_REGISTER_RSP:
return x86::regs::rsp;
case ZYDIS_REGISTER_RBP:
return x86::regs::rbp;
case ZYDIS_REGISTER_R8:
return x86::regs::r8;
case ZYDIS_REGISTER_R8D:
return x86::regs::r8d;
case ZYDIS_REGISTER_R9:
return x86::regs::r9;
case ZYDIS_REGISTER_R9D:
return x86::regs::r9d;
case ZYDIS_REGISTER_R10:
return x86::regs::r10;
case ZYDIS_REGISTER_R10D:
return x86::regs::r10d;
case ZYDIS_REGISTER_R11:
return x86::regs::r11;
case ZYDIS_REGISTER_R11D:
return x86::regs::r11d;
case ZYDIS_REGISTER_R12:
return x86::regs::r12;
case ZYDIS_REGISTER_R12D:
return x86::regs::r12d;
case ZYDIS_REGISTER_R12B:
return x86::regs::r12b;
case ZYDIS_REGISTER_R13:
return x86::regs::r13;
case ZYDIS_REGISTER_R13B:
return x86::regs::r13b;
case ZYDIS_REGISTER_R13D:
return x86::regs::r13d;
case ZYDIS_REGISTER_R14:
return x86::regs::r14;
case ZYDIS_REGISTER_R14D:
return x86::regs::r14d;
case ZYDIS_REGISTER_R14B:
return x86::regs::r14b;
case ZYDIS_REGISTER_R15:
return x86::regs::r15;
case ZYDIS_REGISTER_R15D:
return x86::regs::r15d;
case ZYDIS_REGISTER_EAX:
return x86::regs::eax;
case ZYDIS_REGISTER_EBX:
return x86::regs::ebx;
case ZYDIS_REGISTER_ECX:
return x86::regs::ecx;
case ZYDIS_REGISTER_EDX:
return x86::regs::edx;
case ZYDIS_REGISTER_ESI:
return x86::regs::esi;
case ZYDIS_REGISTER_EDI:
return x86::regs::edi;
case ZYDIS_REGISTER_ESP:
return x86::regs::esp;
case ZYDIS_REGISTER_EBP:
return x86::regs::ebp;
case ZYDIS_REGISTER_AL:
return x86::regs::al;
case ZYDIS_REGISTER_AX:
return x86::regs::ax;
case ZYDIS_REGISTER_AH:
return x86::regs::ah;
case ZYDIS_REGISTER_BX:
return x86::regs::bx;
case ZYDIS_REGISTER_BL:
return x86::regs::bl;
case ZYDIS_REGISTER_BH:
return x86::regs::bh;
case ZYDIS_REGISTER_CX:
return x86::regs::cx;
case ZYDIS_REGISTER_CH:
return x86::regs::ch;
case ZYDIS_REGISTER_CL:
return x86::regs::cl;
case ZYDIS_REGISTER_DX:
return x86::regs::dx;
case ZYDIS_REGISTER_DH:
return x86::regs::dh;
case ZYDIS_REGISTER_SI:
return x86::regs::si;
case ZYDIS_REGISTER_DI:
return x86::regs::di;
case ZYDIS_REGISTER_DIL:
return x86::regs::dil;
case ZYDIS_REGISTER_DL:
return x86::regs::dl;
case ZYDIS_REGISTER_SP:
return x86::regs::sp;
case ZYDIS_REGISTER_BP:
return x86::regs::bp;
case ZYDIS_REGISTER_BPL:
return x86::regs::bpl;
case ZYDIS_REGISTER_SPL:
return x86::regs::spl;
case ZYDIS_REGISTER_SIL:
return x86::regs::sil;
default:
throw "unknown";
}
}
}

@ -0,0 +1,240 @@
# This file is automatically generated from cmake.toml - DO NOT EDIT
# See https://github.com/build-cpp/cmkr for more information
# Create a configure-time dependency on cmake.toml to improve IDE support
if(CMKR_ROOT_PROJECT)
configure_file(cmake.toml cmake.toml COPYONLY)
endif()
# spdlog
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/spdlog")
else()
set(CMAKE_FOLDER spdlog)
endif()
add_subdirectory(spdlog)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# zydis
set(CMKR_CMAKE_FOLDER ${CMAKE_FOLDER})
if(CMAKE_FOLDER)
set(CMAKE_FOLDER "${CMAKE_FOLDER}/zydis")
else()
set(CMAKE_FOLDER zydis)
endif()
add_subdirectory(zydis)
set(CMAKE_FOLDER ${CMKR_CMAKE_FOLDER})
# Target asmjit
set(CMKR_TARGET asmjit)
set(asmjit_SOURCES "")
list(APPEND asmjit_SOURCES
"asmjit/src/asmjit/a64.h"
"asmjit/src/asmjit/arm.h"
"asmjit/src/asmjit/asmjit-scope-begin.h"
"asmjit/src/asmjit/asmjit-scope-end.h"
"asmjit/src/asmjit/asmjit.h"
"asmjit/src/asmjit/core.h"
"asmjit/src/asmjit/x86.h"
"asmjit/src/asmjit/core/api-build_p.h"
"asmjit/src/asmjit/core/api-config.h"
"asmjit/src/asmjit/core/archcommons.h"
"asmjit/src/asmjit/core/archtraits.h"
"asmjit/src/asmjit/core/assembler.h"
"asmjit/src/asmjit/core/builder.h"
"asmjit/src/asmjit/core/codebuffer.h"
"asmjit/src/asmjit/core/codeholder.h"
"asmjit/src/asmjit/core/codewriter_p.h"
"asmjit/src/asmjit/core/compiler.h"
"asmjit/src/asmjit/core/compilerdefs.h"
"asmjit/src/asmjit/core/constpool.h"
"asmjit/src/asmjit/core/cpuinfo.h"
"asmjit/src/asmjit/core/emithelper_p.h"
"asmjit/src/asmjit/core/emitter.h"
"asmjit/src/asmjit/core/emitterutils_p.h"
"asmjit/src/asmjit/core/environment.h"
"asmjit/src/asmjit/core/errorhandler.h"
"asmjit/src/asmjit/core/formatter.h"
"asmjit/src/asmjit/core/formatter_p.h"
"asmjit/src/asmjit/core/func.h"
"asmjit/src/asmjit/core/funcargscontext_p.h"
"asmjit/src/asmjit/core/globals.h"
"asmjit/src/asmjit/core/inst.h"
"asmjit/src/asmjit/core/jitallocator.h"
"asmjit/src/asmjit/core/jitruntime.h"
"asmjit/src/asmjit/core/logger.h"
"asmjit/src/asmjit/core/misc_p.h"
"asmjit/src/asmjit/core/operand.h"
"asmjit/src/asmjit/core/osutils.h"
"asmjit/src/asmjit/core/osutils_p.h"
"asmjit/src/asmjit/core/raassignment_p.h"
"asmjit/src/asmjit/core/rabuilders_p.h"
"asmjit/src/asmjit/core/radefs_p.h"
"asmjit/src/asmjit/core/ralocal_p.h"
"asmjit/src/asmjit/core/rapass_p.h"
"asmjit/src/asmjit/core/rastack_p.h"
"asmjit/src/asmjit/core/string.h"
"asmjit/src/asmjit/core/support.h"
"asmjit/src/asmjit/core/target.h"
"asmjit/src/asmjit/core/type.h"
"asmjit/src/asmjit/core/virtmem.h"
"asmjit/src/asmjit/core/zone.h"
"asmjit/src/asmjit/core/zonehash.h"
"asmjit/src/asmjit/core/zonelist.h"
"asmjit/src/asmjit/core/zonestack.h"
"asmjit/src/asmjit/core/zonestring.h"
"asmjit/src/asmjit/core/zonetree.h"
"asmjit/src/asmjit/core/zonevector.h"
"asmjit/src/asmjit/core/archtraits.cpp"
"asmjit/src/asmjit/core/assembler.cpp"
"asmjit/src/asmjit/core/builder.cpp"
"asmjit/src/asmjit/core/codeholder.cpp"
"asmjit/src/asmjit/core/codewriter.cpp"
"asmjit/src/asmjit/core/compiler.cpp"
"asmjit/src/asmjit/core/constpool.cpp"
"asmjit/src/asmjit/core/cpuinfo.cpp"
"asmjit/src/asmjit/core/emithelper.cpp"
"asmjit/src/asmjit/core/emitter.cpp"
"asmjit/src/asmjit/core/emitterutils.cpp"
"asmjit/src/asmjit/core/environment.cpp"
"asmjit/src/asmjit/core/errorhandler.cpp"
"asmjit/src/asmjit/core/formatter.cpp"
"asmjit/src/asmjit/core/func.cpp"
"asmjit/src/asmjit/core/funcargscontext.cpp"
"asmjit/src/asmjit/core/globals.cpp"
"asmjit/src/asmjit/core/inst.cpp"
"asmjit/src/asmjit/core/jitallocator.cpp"
"asmjit/src/asmjit/core/jitruntime.cpp"
"asmjit/src/asmjit/core/logger.cpp"
"asmjit/src/asmjit/core/operand.cpp"
"asmjit/src/asmjit/core/osutils.cpp"
"asmjit/src/asmjit/core/ralocal.cpp"
"asmjit/src/asmjit/core/rapass.cpp"
"asmjit/src/asmjit/core/rastack.cpp"
"asmjit/src/asmjit/core/string.cpp"
"asmjit/src/asmjit/core/support.cpp"
"asmjit/src/asmjit/core/target.cpp"
"asmjit/src/asmjit/core/type.cpp"
"asmjit/src/asmjit/core/virtmem.cpp"
"asmjit/src/asmjit/core/zone.cpp"
"asmjit/src/asmjit/core/zonehash.cpp"
"asmjit/src/asmjit/core/zonelist.cpp"
"asmjit/src/asmjit/core/zonestack.cpp"
"asmjit/src/asmjit/core/zonetree.cpp"
"asmjit/src/asmjit/core/zonevector.cpp"
"asmjit/src/asmjit/x86/x86archtraits_p.h"
"asmjit/src/asmjit/x86/x86assembler.h"
"asmjit/src/asmjit/x86/x86builder.h"
"asmjit/src/asmjit/x86/x86compiler.h"
"asmjit/src/asmjit/x86/x86emithelper_p.h"
"asmjit/src/asmjit/x86/x86emitter.h"
"asmjit/src/asmjit/x86/x86formatter_p.h"
"asmjit/src/asmjit/x86/x86func_p.h"
"asmjit/src/asmjit/x86/x86globals.h"
"asmjit/src/asmjit/x86/x86instapi_p.h"
"asmjit/src/asmjit/x86/x86instdb.h"
"asmjit/src/asmjit/x86/x86instdb_p.h"
"asmjit/src/asmjit/x86/x86opcode_p.h"
"asmjit/src/asmjit/x86/x86operand.h"
"asmjit/src/asmjit/x86/x86rapass_p.h"
"asmjit/src/asmjit/x86/x86assembler.cpp"
"asmjit/src/asmjit/x86/x86builder.cpp"
"asmjit/src/asmjit/x86/x86compiler.cpp"
"asmjit/src/asmjit/x86/x86emithelper.cpp"
"asmjit/src/asmjit/x86/x86formatter.cpp"
"asmjit/src/asmjit/x86/x86func.cpp"
"asmjit/src/asmjit/x86/x86instapi.cpp"
"asmjit/src/asmjit/x86/x86instdb.cpp"
"asmjit/src/asmjit/x86/x86operand.cpp"
"asmjit/src/asmjit/x86/x86rapass.cpp"
)
set(CMKR_SOURCES ${asmjit_SOURCES})
add_library(asmjit INTERFACE)
if(asmjit_SOURCES)
target_sources(asmjit INTERFACE ${asmjit_SOURCES})
endif()
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${asmjit_SOURCES})
target_compile_definitions(asmjit INTERFACE
ASMJIT_STATIC
ASMJIT_NO_AARCH32
ASMJIT_NO_AARCH64
)
target_include_directories(asmjit INTERFACE
"asmjit/src"
)
unset(CMKR_TARGET)
unset(CMKR_SOURCES)
# Target argparse
set(CMKR_TARGET argparse)
set(argparse_SOURCES "")
set(CMKR_SOURCES ${argparse_SOURCES})
add_library(argparse INTERFACE)
if(argparse_SOURCES)
target_sources(argparse INTERFACE ${argparse_SOURCES})
endif()
target_include_directories(argparse INTERFACE
"argparse/include"
)
unset(CMKR_TARGET)
unset(CMKR_SOURCES)
# Target pepp
set(CMKR_TARGET pepp)
set(pepp_SOURCES "")
list(APPEND pepp_SOURCES
"pepp/pepp/ExportDirectory.hpp"
"pepp/pepp/FileHeader.hpp"
"pepp/pepp/Image.hpp"
"pepp/pepp/ImportDirectory.hpp"
"pepp/pepp/OptionalHeader.hpp"
"pepp/pepp/PEHeader.hpp"
"pepp/pepp/PELibrary.hpp"
"pepp/pepp/PEUtil.hpp"
"pepp/pepp/RelocationDirectory.hpp"
"pepp/pepp/SectionHeader.hpp"
"pepp/pepp/misc/Address.hpp"
"pepp/pepp/misc/ByteVector.hpp"
"pepp/pepp/misc/Concept.hpp"
"pepp/pepp/misc/File.hpp"
"pepp/pepp/misc/NonCopyable.hpp"
"pepp/pepp/ExportDirectory.cpp"
"pepp/pepp/Image.cpp"
"pepp/pepp/ImportDirectory.cpp"
"pepp/pepp/OptionalHeader.cpp"
"pepp/pepp/PEHeader.cpp"
"pepp/pepp/PEUtil.cpp"
"pepp/pepp/RelocationDirectory.cpp"
"pepp/pepp/SectionHeader.cpp"
"pepp/pepp/misc/File.cpp"
)
set(CMKR_SOURCES ${pepp_SOURCES})
add_library(pepp INTERFACE)
if(pepp_SOURCES)
target_sources(pepp INTERFACE ${pepp_SOURCES})
endif()
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${pepp_SOURCES})
target_include_directories(pepp INTERFACE
pepp
)
unset(CMKR_TARGET)
unset(CMKR_SOURCES)

1
vendor/argparse vendored

@ -0,0 +1 @@
Subproject commit 1a552dfd60cb8a478f7d7cb63e4814ac66ca2891

1
vendor/asmjit vendored

@ -0,0 +1 @@
Subproject commit a4cb51b532af0f8137c4182914244c3b05d7745f

17
vendor/cmake.toml vendored

@ -0,0 +1,17 @@
[subdir.spdlog]
[subdir.zydis]
[target.asmjit]
type = "interface"
include-directories = ["asmjit/src"]
sources = ["asmjit/src/asmjit/*.h", "asmjit/src/asmjit/*.cpp", "asmjit/src/asmjit/core/*.h", "asmjit/src/asmjit/core/*.cpp", "asmjit/src/asmjit/x86/*.h", "asmjit/src/asmjit/x86/*.cpp"]
compile-definitions = ["ASMJIT_STATIC", "ASMJIT_NO_AARCH32", "ASMJIT_NO_AARCH64"]
[target.argparse]
type = "interface"
include-directories = ["argparse/include"]
[target.pepp]
type = "interface"
include-directories = ["pepp"]
sources = ["pepp/pepp/**.hpp", "pepp/pepp/**.cpp"]

1
vendor/pepp vendored

@ -0,0 +1 @@
Subproject commit 6eb89d8ecce3df12d91cf326ea050d006b8e6c28

1
vendor/spdlog vendored

@ -0,0 +1 @@
Subproject commit 76fb40d95455f249bd70824ecfcae7a8f0930fa3

1
vendor/zydis vendored

@ -0,0 +1 @@
Subproject commit 3f5a3ad8e16658c62d7033e9373232d19480d3cc
Loading…
Cancel
Save