Repository: CCob/SylantStrike
Branch: master
Commit: 11f613c4fe00
Files: 32
Total size: 198.7 KB
Directory structure:
gitextract_uwm5figu/
├── .gitattributes
├── .gitignore
├── README.md
├── SylantStrike/
│ ├── SylantStrike.cpp
│ ├── SylantStrike.h
│ ├── SylantStrike.vcxproj
│ ├── SylantStrike.vcxproj.filters
│ ├── cpp.hint
│ ├── dllmain.cpp
│ ├── framework.h
│ ├── minhook/
│ │ ├── include/
│ │ │ └── MinHook.h
│ │ └── src/
│ │ ├── buffer.c
│ │ ├── buffer.h
│ │ ├── hde/
│ │ │ ├── hde32.c
│ │ │ ├── hde32.h
│ │ │ ├── hde64.c
│ │ │ ├── hde64.h
│ │ │ ├── pstdint.h
│ │ │ ├── table32.h
│ │ │ └── table64.h
│ │ ├── hook.c
│ │ ├── trampoline.c
│ │ └── trampoline.h
│ ├── pch.cpp
│ └── pch.h
├── SylantStrike.sln
└── SylantStrikeInject/
├── App.config
├── BasicInject.cs
├── Options.cs
├── Program.cs
├── Properties/
│ └── AssemblyInfo.cs
└── SylantStrikeInject.csproj
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# Visual Studio 2017 auto generated files
Generated\ Files/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
# StyleCop
StyleCopReport.xml
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/[Pp]ackages/*
# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- Backup*.rdl
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush personal settings
.cr/personal
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/
# Azure Stream Analytics local run output
ASALocalRun/
# MSBuild Binary and Structured Log
*.binlog
# NVidia Nsight GPU debugger configuration file
*.nvuser
# MFractors (Xamarin productivity tool) working folder
.mfractor/
# Local History for Visual Studio
.localhistory/
# BeatPulse healthcheck temp database
healthchecksdb
================================================
FILE: README.md
================================================
### SylantStrike - a simple EDR written as part of a two part blog series on creating and bypassing an EDR
Original blog post with description at https://ethicalchaos.dev/2020/05/27/lets-create-an-edr-and-bypass-it-part-1/
================================================
FILE: SylantStrike/SylantStrike.cpp
================================================
// SylantStrike.cpp : Hooked API implementations
//
#include "pch.h"
#include "framework.h"
#include "SylantStrike.h"
//Pointer to the trampoline function used to call the original API
pNtProtectVirtualMemory pOriginalNtProtectVirtualMemory = nullptr;
DWORD NTAPI NtProtectVirtualMemory(IN HANDLE ProcessHandle, IN OUT PVOID* BaseAddress, IN OUT PULONG NumberOfBytesToProtect, IN ULONG NewAccessProtection, OUT PULONG OldAccessProtection) {
//Check to see if the calling application is requesting RWX
if ((NewAccessProtection & PAGE_EXECUTE_READWRITE) == PAGE_EXECUTE_READWRITE) {
//It was, so notify the user of naughtly behaviour and terminate the running program
MessageBox(nullptr, TEXT("You've been a naughty little hax0r, terminating program"), TEXT("Hax0r Detected"), MB_OK);
TerminateProcess(GetCurrentProcess(), 0xdead1337);
//Unreachable code
return 0;
}
//No it wasn't, so just call the original function as normal
return pOriginalNtProtectVirtualMemory(ProcessHandle, BaseAddress, NumberOfBytesToProtect, NewAccessProtection, OldAccessProtection);
}
================================================
FILE: SylantStrike/SylantStrike.h
================================================
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the CYLANTSTRIKE_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// CYLANTSTRIKE_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef CYLANTSTRIKE_EXPORTS
#define CYLANTSTRIKE_API __declspec(dllexport)
#else
#define CYLANTSTRIKE_API __declspec(dllimport)
#endif
typedef DWORD (NTAPI *pNtProtectVirtualMemory)(IN HANDLE ProcessHandle, IN OUT PVOID* BaseAddress, IN OUT PULONG NumberOfBytesToProtect, IN ULONG NewAccessProtection, OUT PULONG OldAccessProtection);
extern pNtProtectVirtualMemory pOriginalNtProtectVirtualMemory;
DWORD NTAPI NtProtectVirtualMemory(IN HANDLE ProcessHandle, IN OUT PVOID* BaseAddress, IN OUT PULONG NumberOfBytesToProtect, IN ULONG NewAccessProtection, OUT PULONG OldAccessProtection);
================================================
FILE: SylantStrike/SylantStrike.vcxproj
================================================
Debug
Win32
Release
Win32
Debug
x64
Release
x64
16.0
Win32Proj
{1ddd15aa-d837-4143-a272-0e08f9ed6c40}
CylantStrike
10.0
SylantStrike
DynamicLibrary
true
v142
Unicode
false
DynamicLibrary
false
v142
true
Unicode
false
DynamicLibrary
true
v142
Unicode
false
DynamicLibrary
false
v142
true
Unicode
false
true
SylantStrike
false
SylantStrike
true
SylantStrike
true
false
SylantStrike
Level3
true
WIN32;_DEBUG;CYLANTSTRIKE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
true
Use
pch.h
Windows
true
false
Level3
true
true
true
WIN32;NDEBUG;CYLANTSTRIKE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
true
Use
pch.h
Windows
true
true
true
false
Level3
true
_DEBUG;CYLANTSTRIKE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
true
Use
pch.h
Windows
true
false
Level3
true
true
true
NDEBUG;CYLANTSTRIKE_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)
true
Use
pch.h
Windows
true
true
true
false
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
NotUsing
Create
Create
Create
Create
================================================
FILE: SylantStrike/SylantStrike.vcxproj.filters
================================================
{4FC737F1-C7A5-4376-A066-2A32D752A2FF}
cpp;c;cc;cxx;c++;def;odl;idl;hpj;bat;asm;asmx
{93995380-89BD-4b04-88EB-625FBE52EBFB}
h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd
{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Header Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
Source Files
================================================
FILE: SylantStrike/cpp.hint
================================================
#define CYLANTSTRIKE_API __declspec(dllexport)
#define CYLANTSTRIKE_API __declspec(dllimport)
================================================
FILE: SylantStrike/dllmain.cpp
================================================
// dllmain.cpp : Defines the entry point for the DLL application.
#include "pch.h"
#include "minhook/include/MinHook.h"
#include "SylantStrike.h"
DWORD WINAPI InitHooksThread(LPVOID param) {
//MinHook itself requires initialisation, lets do this
//before we hook specific API calls.
if (MH_Initialize() != MH_OK) {
OutputDebugString(TEXT("Failed to initalize MinHook library\n"));
return -1;
}
//Now that we have initialised MinHook, lets prepare to hook NtProtectVirtualMemory from ntdll.dll
MH_STATUS status = MH_CreateHookApi(TEXT("ntdll"), "NtProtectVirtualMemory", NtProtectVirtualMemory,
reinterpret_cast(&pOriginalNtProtectVirtualMemory));
//Enable our hooks so they become active
status = MH_EnableHook(MH_ALL_HOOKS);
return status;
}
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH: {
//We are not interested in callbacks when a thread is created
DisableThreadLibraryCalls(hModule);
//We need to create a thread when initialising our hooks since
//DllMain is prone to lockups if executing code inline.
HANDLE hThread = CreateThread(nullptr, 0, InitHooksThread, nullptr, 0, nullptr);
if (hThread != nullptr) {
CloseHandle(hThread);
}
break;
}
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
================================================
FILE: SylantStrike/framework.h
================================================
#pragma once
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files
#include
================================================
FILE: SylantStrike/minhook/include/MinHook.h
================================================
/*
* MinHook - The Minimalistic API Hooking Library for x64/x86
* Copyright (C) 2009-2017 Tsuda Kageyu.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__)
#error MinHook supports only x86 and x64 systems.
#endif
#include
// MinHook Error Codes.
typedef enum MH_STATUS
{
// Unknown error. Should not be returned.
MH_UNKNOWN = -1,
// Successful.
MH_OK = 0,
// MinHook is already initialized.
MH_ERROR_ALREADY_INITIALIZED,
// MinHook is not initialized yet, or already uninitialized.
MH_ERROR_NOT_INITIALIZED,
// The hook for the specified target function is already created.
MH_ERROR_ALREADY_CREATED,
// The hook for the specified target function is not created yet.
MH_ERROR_NOT_CREATED,
// The hook for the specified target function is already enabled.
MH_ERROR_ENABLED,
// The hook for the specified target function is not enabled yet, or already
// disabled.
MH_ERROR_DISABLED,
// The specified pointer is invalid. It points the address of non-allocated
// and/or non-executable region.
MH_ERROR_NOT_EXECUTABLE,
// The specified target function cannot be hooked.
MH_ERROR_UNSUPPORTED_FUNCTION,
// Failed to allocate memory.
MH_ERROR_MEMORY_ALLOC,
// Failed to change the memory protection.
MH_ERROR_MEMORY_PROTECT,
// The specified module is not loaded.
MH_ERROR_MODULE_NOT_FOUND,
// The specified function is not found.
MH_ERROR_FUNCTION_NOT_FOUND
}
MH_STATUS;
// Can be passed as a parameter to MH_EnableHook, MH_DisableHook,
// MH_QueueEnableHook or MH_QueueDisableHook.
#define MH_ALL_HOOKS NULL
#ifdef __cplusplus
extern "C" {
#endif
// Initialize the MinHook library. You must call this function EXACTLY ONCE
// at the beginning of your program.
MH_STATUS WINAPI MH_Initialize(VOID);
// Uninitialize the MinHook library. You must call this function EXACTLY
// ONCE at the end of your program.
MH_STATUS WINAPI MH_Uninitialize(VOID);
// Creates a Hook for the specified target function, in disabled state.
// Parameters:
// pTarget [in] A pointer to the target function, which will be
// overridden by the detour function.
// pDetour [in] A pointer to the detour function, which will override
// the target function.
// ppOriginal [out] A pointer to the trampoline function, which will be
// used to call the original target function.
// This parameter can be NULL.
MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);
// Creates a Hook for the specified API function, in disabled state.
// Parameters:
// pszModule [in] A pointer to the loaded module name which contains the
// target function.
// pszTarget [in] A pointer to the target function name, which will be
// overridden by the detour function.
// pDetour [in] A pointer to the detour function, which will override
// the target function.
// ppOriginal [out] A pointer to the trampoline function, which will be
// used to call the original target function.
// This parameter can be NULL.
MH_STATUS WINAPI MH_CreateHookApi(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal);
// Creates a Hook for the specified API function, in disabled state.
// Parameters:
// pszModule [in] A pointer to the loaded module name which contains the
// target function.
// pszTarget [in] A pointer to the target function name, which will be
// overridden by the detour function.
// pDetour [in] A pointer to the detour function, which will override
// the target function.
// ppOriginal [out] A pointer to the trampoline function, which will be
// used to call the original target function.
// This parameter can be NULL.
// ppTarget [out] A pointer to the target function, which will be used
// with other functions.
// This parameter can be NULL.
MH_STATUS WINAPI MH_CreateHookApiEx(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget);
// Removes an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget);
// Enables an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// enabled in one go.
MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget);
// Disables an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// disabled in one go.
MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget);
// Queues to enable an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// queued to be enabled.
MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget);
// Queues to disable an already created hook.
// Parameters:
// pTarget [in] A pointer to the target function.
// If this parameter is MH_ALL_HOOKS, all created hooks are
// queued to be disabled.
MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget);
// Applies all queued changes in one go.
MH_STATUS WINAPI MH_ApplyQueued(VOID);
// Translates the MH_STATUS to its name as a string.
const char * WINAPI MH_StatusToString(MH_STATUS status);
#ifdef __cplusplus
}
#endif
================================================
FILE: SylantStrike/minhook/src/buffer.c
================================================
/*
* MinHook - The Minimalistic API Hooking Library for x64/x86
* Copyright (C) 2009-2017 Tsuda Kageyu.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
#include "buffer.h"
// Size of each memory block. (= page size of VirtualAlloc)
#define MEMORY_BLOCK_SIZE 0x1000
// Max range for seeking a memory block. (= 1024MB)
#define MAX_MEMORY_RANGE 0x40000000
// Memory protection flags to check the executable address.
#define PAGE_EXECUTE_FLAGS \
(PAGE_EXECUTE | PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_EXECUTE_WRITECOPY)
// Memory slot.
typedef struct _MEMORY_SLOT
{
union
{
struct _MEMORY_SLOT *pNext;
UINT8 buffer[MEMORY_SLOT_SIZE];
};
} MEMORY_SLOT, *PMEMORY_SLOT;
// Memory block info. Placed at the head of each block.
typedef struct _MEMORY_BLOCK
{
struct _MEMORY_BLOCK *pNext;
PMEMORY_SLOT pFree; // First element of the free slot list.
UINT usedCount;
} MEMORY_BLOCK, *PMEMORY_BLOCK;
//-------------------------------------------------------------------------
// Global Variables:
//-------------------------------------------------------------------------
// First element of the memory block list.
PMEMORY_BLOCK g_pMemoryBlocks;
//-------------------------------------------------------------------------
VOID InitializeBuffer(VOID)
{
// Nothing to do for now.
}
//-------------------------------------------------------------------------
VOID UninitializeBuffer(VOID)
{
PMEMORY_BLOCK pBlock = g_pMemoryBlocks;
g_pMemoryBlocks = NULL;
while (pBlock)
{
PMEMORY_BLOCK pNext = pBlock->pNext;
VirtualFree(pBlock, 0, MEM_RELEASE);
pBlock = pNext;
}
}
//-------------------------------------------------------------------------
#if defined(_M_X64) || defined(__x86_64__)
static LPVOID FindPrevFreeRegion(LPVOID pAddress, LPVOID pMinAddr, DWORD dwAllocationGranularity)
{
ULONG_PTR tryAddr = (ULONG_PTR)pAddress;
// Round down to the allocation granularity.
tryAddr -= tryAddr % dwAllocationGranularity;
// Start from the previous allocation granularity multiply.
tryAddr -= dwAllocationGranularity;
while (tryAddr >= (ULONG_PTR)pMinAddr)
{
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0)
break;
if (mbi.State == MEM_FREE)
return (LPVOID)tryAddr;
if ((ULONG_PTR)mbi.AllocationBase < dwAllocationGranularity)
break;
tryAddr = (ULONG_PTR)mbi.AllocationBase - dwAllocationGranularity;
}
return NULL;
}
#endif
//-------------------------------------------------------------------------
#if defined(_M_X64) || defined(__x86_64__)
static LPVOID FindNextFreeRegion(LPVOID pAddress, LPVOID pMaxAddr, DWORD dwAllocationGranularity)
{
ULONG_PTR tryAddr = (ULONG_PTR)pAddress;
// Round down to the allocation granularity.
tryAddr -= tryAddr % dwAllocationGranularity;
// Start from the next allocation granularity multiply.
tryAddr += dwAllocationGranularity;
while (tryAddr <= (ULONG_PTR)pMaxAddr)
{
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery((LPVOID)tryAddr, &mbi, sizeof(mbi)) == 0)
break;
if (mbi.State == MEM_FREE)
return (LPVOID)tryAddr;
tryAddr = (ULONG_PTR)mbi.BaseAddress + mbi.RegionSize;
// Round up to the next allocation granularity.
tryAddr += dwAllocationGranularity - 1;
tryAddr -= tryAddr % dwAllocationGranularity;
}
return NULL;
}
#endif
//-------------------------------------------------------------------------
static PMEMORY_BLOCK GetMemoryBlock(LPVOID pOrigin)
{
PMEMORY_BLOCK pBlock;
#if defined(_M_X64) || defined(__x86_64__)
ULONG_PTR minAddr;
ULONG_PTR maxAddr;
SYSTEM_INFO si;
GetSystemInfo(&si);
minAddr = (ULONG_PTR)si.lpMinimumApplicationAddress;
maxAddr = (ULONG_PTR)si.lpMaximumApplicationAddress;
// pOrigin ± 512MB
if ((ULONG_PTR)pOrigin > MAX_MEMORY_RANGE && minAddr < (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE)
minAddr = (ULONG_PTR)pOrigin - MAX_MEMORY_RANGE;
if (maxAddr > (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE)
maxAddr = (ULONG_PTR)pOrigin + MAX_MEMORY_RANGE;
// Make room for MEMORY_BLOCK_SIZE bytes.
maxAddr -= MEMORY_BLOCK_SIZE - 1;
#endif
// Look the registered blocks for a reachable one.
for (pBlock = g_pMemoryBlocks; pBlock != NULL; pBlock = pBlock->pNext)
{
#if defined(_M_X64) || defined(__x86_64__)
// Ignore the blocks too far.
if ((ULONG_PTR)pBlock < minAddr || (ULONG_PTR)pBlock >= maxAddr)
continue;
#endif
// The block has at least one unused slot.
if (pBlock->pFree != NULL)
return pBlock;
}
#if defined(_M_X64) || defined(__x86_64__)
// Alloc a new block above if not found.
{
LPVOID pAlloc = pOrigin;
while ((ULONG_PTR)pAlloc >= minAddr)
{
pAlloc = FindPrevFreeRegion(pAlloc, (LPVOID)minAddr, si.dwAllocationGranularity);
if (pAlloc == NULL)
break;
pBlock = (PMEMORY_BLOCK)VirtualAlloc(
pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pBlock != NULL)
break;
}
}
// Alloc a new block below if not found.
if (pBlock == NULL)
{
LPVOID pAlloc = pOrigin;
while ((ULONG_PTR)pAlloc <= maxAddr)
{
pAlloc = FindNextFreeRegion(pAlloc, (LPVOID)maxAddr, si.dwAllocationGranularity);
if (pAlloc == NULL)
break;
pBlock = (PMEMORY_BLOCK)VirtualAlloc(
pAlloc, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
if (pBlock != NULL)
break;
}
}
#else
// In x86 mode, a memory block can be placed anywhere.
pBlock = (PMEMORY_BLOCK)VirtualAlloc(
NULL, MEMORY_BLOCK_SIZE, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
#endif
if (pBlock != NULL)
{
// Build a linked list of all the slots.
PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBlock + 1;
pBlock->pFree = NULL;
pBlock->usedCount = 0;
do
{
pSlot->pNext = pBlock->pFree;
pBlock->pFree = pSlot;
pSlot++;
} while ((ULONG_PTR)pSlot - (ULONG_PTR)pBlock <= MEMORY_BLOCK_SIZE - MEMORY_SLOT_SIZE);
pBlock->pNext = g_pMemoryBlocks;
g_pMemoryBlocks = pBlock;
}
return pBlock;
}
//-------------------------------------------------------------------------
LPVOID AllocateBuffer(LPVOID pOrigin)
{
PMEMORY_SLOT pSlot;
PMEMORY_BLOCK pBlock = GetMemoryBlock(pOrigin);
if (pBlock == NULL)
return NULL;
// Remove an unused slot from the list.
pSlot = pBlock->pFree;
pBlock->pFree = pSlot->pNext;
pBlock->usedCount++;
#ifdef _DEBUG
// Fill the slot with INT3 for debugging.
memset(pSlot, 0xCC, sizeof(MEMORY_SLOT));
#endif
return pSlot;
}
//-------------------------------------------------------------------------
VOID FreeBuffer(LPVOID pBuffer)
{
PMEMORY_BLOCK pBlock = g_pMemoryBlocks;
PMEMORY_BLOCK pPrev = NULL;
ULONG_PTR pTargetBlock = ((ULONG_PTR)pBuffer / MEMORY_BLOCK_SIZE) * MEMORY_BLOCK_SIZE;
while (pBlock != NULL)
{
if ((ULONG_PTR)pBlock == pTargetBlock)
{
PMEMORY_SLOT pSlot = (PMEMORY_SLOT)pBuffer;
#ifdef _DEBUG
// Clear the released slot for debugging.
memset(pSlot, 0x00, sizeof(*pSlot));
#endif
// Restore the released slot to the list.
pSlot->pNext = pBlock->pFree;
pBlock->pFree = pSlot;
pBlock->usedCount--;
// Free if unused.
if (pBlock->usedCount == 0)
{
if (pPrev)
pPrev->pNext = pBlock->pNext;
else
g_pMemoryBlocks = pBlock->pNext;
VirtualFree(pBlock, 0, MEM_RELEASE);
}
break;
}
pPrev = pBlock;
pBlock = pBlock->pNext;
}
}
//-------------------------------------------------------------------------
BOOL IsExecutableAddress(LPVOID pAddress)
{
MEMORY_BASIC_INFORMATION mi;
VirtualQuery(pAddress, &mi, sizeof(mi));
return (mi.State == MEM_COMMIT && (mi.Protect & PAGE_EXECUTE_FLAGS));
}
================================================
FILE: SylantStrike/minhook/src/buffer.h
================================================
/*
* MinHook - The Minimalistic API Hooking Library for x64/x86
* Copyright (C) 2009-2017 Tsuda Kageyu.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
// Size of each memory slot.
#if defined(_M_X64) || defined(__x86_64__)
#define MEMORY_SLOT_SIZE 64
#else
#define MEMORY_SLOT_SIZE 32
#endif
VOID InitializeBuffer(VOID);
VOID UninitializeBuffer(VOID);
LPVOID AllocateBuffer(LPVOID pOrigin);
VOID FreeBuffer(LPVOID pBuffer);
BOOL IsExecutableAddress(LPVOID pAddress);
================================================
FILE: SylantStrike/minhook/src/hde/hde32.c
================================================
/*
* Hacker Disassembler Engine 32 C
* Copyright (c) 2008-2009, Vyacheslav Patkov.
* All rights reserved.
*
*/
#if defined(_M_IX86) || defined(__i386__)
#include "hde32.h"
#include "table32.h"
unsigned int hde32_disasm(const void *code, hde32s *hs)
{
uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0;
uint8_t *ht = hde32_table, m_mod, m_reg, m_rm, disp_size = 0;
// Avoid using memset to reduce the footprint.
#ifndef _MSC_VER
memset((LPBYTE)hs, 0, sizeof(hde32s));
#else
__stosb((LPBYTE)hs, 0, sizeof(hde32s));
#endif
for (x = 16; x; x--)
switch (c = *p++) {
case 0xf3:
hs->p_rep = c;
pref |= PRE_F3;
break;
case 0xf2:
hs->p_rep = c;
pref |= PRE_F2;
break;
case 0xf0:
hs->p_lock = c;
pref |= PRE_LOCK;
break;
case 0x26: case 0x2e: case 0x36:
case 0x3e: case 0x64: case 0x65:
hs->p_seg = c;
pref |= PRE_SEG;
break;
case 0x66:
hs->p_66 = c;
pref |= PRE_66;
break;
case 0x67:
hs->p_67 = c;
pref |= PRE_67;
break;
default:
goto pref_done;
}
pref_done:
hs->flags = (uint32_t)pref << 23;
if (!pref)
pref |= PRE_NONE;
if ((hs->opcode = c) == 0x0f) {
hs->opcode2 = c = *p++;
ht += DELTA_OPCODES;
} else if (c >= 0xa0 && c <= 0xa3) {
if (pref & PRE_67)
pref |= PRE_66;
else
pref &= ~PRE_66;
}
opcode = c;
cflags = ht[ht[opcode / 4] + (opcode % 4)];
if (cflags == C_ERROR) {
hs->flags |= F_ERROR | F_ERROR_OPCODE;
cflags = 0;
if ((opcode & -3) == 0x24)
cflags++;
}
x = 0;
if (cflags & C_GROUP) {
uint16_t t;
t = *(uint16_t *)(ht + (cflags & 0x7f));
cflags = (uint8_t)t;
x = (uint8_t)(t >> 8);
}
if (hs->opcode2) {
ht = hde32_table + DELTA_PREFIXES;
if (ht[ht[opcode / 4] + (opcode % 4)] & pref)
hs->flags |= F_ERROR | F_ERROR_OPCODE;
}
if (cflags & C_MODRM) {
hs->flags |= F_MODRM;
hs->modrm = c = *p++;
hs->modrm_mod = m_mod = c >> 6;
hs->modrm_rm = m_rm = c & 7;
hs->modrm_reg = m_reg = (c & 0x3f) >> 3;
if (x && ((x << m_reg) & 0x80))
hs->flags |= F_ERROR | F_ERROR_OPCODE;
if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {
uint8_t t = opcode - 0xd9;
if (m_mod == 3) {
ht = hde32_table + DELTA_FPU_MODRM + t*8;
t = ht[m_reg] << m_rm;
} else {
ht = hde32_table + DELTA_FPU_REG;
t = ht[t] << m_reg;
}
if (t & 0x80)
hs->flags |= F_ERROR | F_ERROR_OPCODE;
}
if (pref & PRE_LOCK) {
if (m_mod == 3) {
hs->flags |= F_ERROR | F_ERROR_LOCK;
} else {
uint8_t *table_end, op = opcode;
if (hs->opcode2) {
ht = hde32_table + DELTA_OP2_LOCK_OK;
table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;
} else {
ht = hde32_table + DELTA_OP_LOCK_OK;
table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;
op &= -2;
}
for (; ht != table_end; ht++)
if (*ht++ == op) {
if (!((*ht << m_reg) & 0x80))
goto no_lock_error;
else
break;
}
hs->flags |= F_ERROR | F_ERROR_LOCK;
no_lock_error:
;
}
}
if (hs->opcode2) {
switch (opcode) {
case 0x20: case 0x22:
m_mod = 3;
if (m_reg > 4 || m_reg == 1)
goto error_operand;
else
goto no_error_operand;
case 0x21: case 0x23:
m_mod = 3;
if (m_reg == 4 || m_reg == 5)
goto error_operand;
else
goto no_error_operand;
}
} else {
switch (opcode) {
case 0x8c:
if (m_reg > 5)
goto error_operand;
else
goto no_error_operand;
case 0x8e:
if (m_reg == 1 || m_reg > 5)
goto error_operand;
else
goto no_error_operand;
}
}
if (m_mod == 3) {
uint8_t *table_end;
if (hs->opcode2) {
ht = hde32_table + DELTA_OP2_ONLY_MEM;
table_end = ht + sizeof(hde32_table) - DELTA_OP2_ONLY_MEM;
} else {
ht = hde32_table + DELTA_OP_ONLY_MEM;
table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;
}
for (; ht != table_end; ht += 2)
if (*ht++ == opcode) {
if (*ht++ & pref && !((*ht << m_reg) & 0x80))
goto error_operand;
else
break;
}
goto no_error_operand;
} else if (hs->opcode2) {
switch (opcode) {
case 0x50: case 0xd7: case 0xf7:
if (pref & (PRE_NONE | PRE_66))
goto error_operand;
break;
case 0xd6:
if (pref & (PRE_F2 | PRE_F3))
goto error_operand;
break;
case 0xc5:
goto error_operand;
}
goto no_error_operand;
} else
goto no_error_operand;
error_operand:
hs->flags |= F_ERROR | F_ERROR_OPERAND;
no_error_operand:
c = *p++;
if (m_reg <= 1) {
if (opcode == 0xf6)
cflags |= C_IMM8;
else if (opcode == 0xf7)
cflags |= C_IMM_P66;
}
switch (m_mod) {
case 0:
if (pref & PRE_67) {
if (m_rm == 6)
disp_size = 2;
} else
if (m_rm == 5)
disp_size = 4;
break;
case 1:
disp_size = 1;
break;
case 2:
disp_size = 2;
if (!(pref & PRE_67))
disp_size <<= 1;
}
if (m_mod != 3 && m_rm == 4 && !(pref & PRE_67)) {
hs->flags |= F_SIB;
p++;
hs->sib = c;
hs->sib_scale = c >> 6;
hs->sib_index = (c & 0x3f) >> 3;
if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))
disp_size = 4;
}
p--;
switch (disp_size) {
case 1:
hs->flags |= F_DISP8;
hs->disp.disp8 = *p;
break;
case 2:
hs->flags |= F_DISP16;
hs->disp.disp16 = *(uint16_t *)p;
break;
case 4:
hs->flags |= F_DISP32;
hs->disp.disp32 = *(uint32_t *)p;
}
p += disp_size;
} else if (pref & PRE_LOCK)
hs->flags |= F_ERROR | F_ERROR_LOCK;
if (cflags & C_IMM_P66) {
if (cflags & C_REL32) {
if (pref & PRE_66) {
hs->flags |= F_IMM16 | F_RELATIVE;
hs->imm.imm16 = *(uint16_t *)p;
p += 2;
goto disasm_done;
}
goto rel32_ok;
}
if (pref & PRE_66) {
hs->flags |= F_IMM16;
hs->imm.imm16 = *(uint16_t *)p;
p += 2;
} else {
hs->flags |= F_IMM32;
hs->imm.imm32 = *(uint32_t *)p;
p += 4;
}
}
if (cflags & C_IMM16) {
if (hs->flags & F_IMM32) {
hs->flags |= F_IMM16;
hs->disp.disp16 = *(uint16_t *)p;
} else if (hs->flags & F_IMM16) {
hs->flags |= F_2IMM16;
hs->disp.disp16 = *(uint16_t *)p;
} else {
hs->flags |= F_IMM16;
hs->imm.imm16 = *(uint16_t *)p;
}
p += 2;
}
if (cflags & C_IMM8) {
hs->flags |= F_IMM8;
hs->imm.imm8 = *p++;
}
if (cflags & C_REL32) {
rel32_ok:
hs->flags |= F_IMM32 | F_RELATIVE;
hs->imm.imm32 = *(uint32_t *)p;
p += 4;
} else if (cflags & C_REL8) {
hs->flags |= F_IMM8 | F_RELATIVE;
hs->imm.imm8 = *p++;
}
disasm_done:
if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {
hs->flags |= F_ERROR | F_ERROR_LENGTH;
hs->len = 15;
}
return (unsigned int)hs->len;
}
#endif // defined(_M_IX86) || defined(__i386__)
================================================
FILE: SylantStrike/minhook/src/hde/hde32.h
================================================
/*
* Hacker Disassembler Engine 32
* Copyright (c) 2006-2009, Vyacheslav Patkov.
* All rights reserved.
*
* hde32.h: C/C++ header file
*
*/
#ifndef _HDE32_H_
#define _HDE32_H_
/* stdint.h - C99 standard header
* http://en.wikipedia.org/wiki/stdint.h
*
* if your compiler doesn't contain "stdint.h" header (for
* example, Microsoft Visual C++), you can download file:
* http://www.azillionmonkeys.com/qed/pstdint.h
* and change next line to:
* #include "pstdint.h"
*/
#include "pstdint.h"
#define F_MODRM 0x00000001
#define F_SIB 0x00000002
#define F_IMM8 0x00000004
#define F_IMM16 0x00000008
#define F_IMM32 0x00000010
#define F_DISP8 0x00000020
#define F_DISP16 0x00000040
#define F_DISP32 0x00000080
#define F_RELATIVE 0x00000100
#define F_2IMM16 0x00000800
#define F_ERROR 0x00001000
#define F_ERROR_OPCODE 0x00002000
#define F_ERROR_LENGTH 0x00004000
#define F_ERROR_LOCK 0x00008000
#define F_ERROR_OPERAND 0x00010000
#define F_PREFIX_REPNZ 0x01000000
#define F_PREFIX_REPX 0x02000000
#define F_PREFIX_REP 0x03000000
#define F_PREFIX_66 0x04000000
#define F_PREFIX_67 0x08000000
#define F_PREFIX_LOCK 0x10000000
#define F_PREFIX_SEG 0x20000000
#define F_PREFIX_ANY 0x3f000000
#define PREFIX_SEGMENT_CS 0x2e
#define PREFIX_SEGMENT_SS 0x36
#define PREFIX_SEGMENT_DS 0x3e
#define PREFIX_SEGMENT_ES 0x26
#define PREFIX_SEGMENT_FS 0x64
#define PREFIX_SEGMENT_GS 0x65
#define PREFIX_LOCK 0xf0
#define PREFIX_REPNZ 0xf2
#define PREFIX_REPX 0xf3
#define PREFIX_OPERAND_SIZE 0x66
#define PREFIX_ADDRESS_SIZE 0x67
#pragma pack(push,1)
typedef struct {
uint8_t len;
uint8_t p_rep;
uint8_t p_lock;
uint8_t p_seg;
uint8_t p_66;
uint8_t p_67;
uint8_t opcode;
uint8_t opcode2;
uint8_t modrm;
uint8_t modrm_mod;
uint8_t modrm_reg;
uint8_t modrm_rm;
uint8_t sib;
uint8_t sib_scale;
uint8_t sib_index;
uint8_t sib_base;
union {
uint8_t imm8;
uint16_t imm16;
uint32_t imm32;
} imm;
union {
uint8_t disp8;
uint16_t disp16;
uint32_t disp32;
} disp;
uint32_t flags;
} hde32s;
#pragma pack(pop)
#ifdef __cplusplus
extern "C" {
#endif
/* __cdecl */
unsigned int hde32_disasm(const void *code, hde32s *hs);
#ifdef __cplusplus
}
#endif
#endif /* _HDE32_H_ */
================================================
FILE: SylantStrike/minhook/src/hde/hde64.c
================================================
/*
* Hacker Disassembler Engine 64 C
* Copyright (c) 2008-2009, Vyacheslav Patkov.
* All rights reserved.
*
*/
#if defined(_M_X64) || defined(__x86_64__)
#include "hde64.h"
#include "table64.h"
unsigned int hde64_disasm(const void *code, hde64s *hs)
{
uint8_t x, c, *p = (uint8_t *)code, cflags, opcode, pref = 0;
uint8_t *ht = hde64_table, m_mod, m_reg, m_rm, disp_size = 0;
uint8_t op64 = 0;
// Avoid using memset to reduce the footprint.
#ifndef _MSC_VER
memset((LPBYTE)hs, 0, sizeof(hde64s));
#else
__stosb((LPBYTE)hs, 0, sizeof(hde64s));
#endif
for (x = 16; x; x--)
switch (c = *p++) {
case 0xf3:
hs->p_rep = c;
pref |= PRE_F3;
break;
case 0xf2:
hs->p_rep = c;
pref |= PRE_F2;
break;
case 0xf0:
hs->p_lock = c;
pref |= PRE_LOCK;
break;
case 0x26: case 0x2e: case 0x36:
case 0x3e: case 0x64: case 0x65:
hs->p_seg = c;
pref |= PRE_SEG;
break;
case 0x66:
hs->p_66 = c;
pref |= PRE_66;
break;
case 0x67:
hs->p_67 = c;
pref |= PRE_67;
break;
default:
goto pref_done;
}
pref_done:
hs->flags = (uint32_t)pref << 23;
if (!pref)
pref |= PRE_NONE;
if ((c & 0xf0) == 0x40) {
hs->flags |= F_PREFIX_REX;
if ((hs->rex_w = (c & 0xf) >> 3) && (*p & 0xf8) == 0xb8)
op64++;
hs->rex_r = (c & 7) >> 2;
hs->rex_x = (c & 3) >> 1;
hs->rex_b = c & 1;
if (((c = *p++) & 0xf0) == 0x40) {
opcode = c;
goto error_opcode;
}
}
if ((hs->opcode = c) == 0x0f) {
hs->opcode2 = c = *p++;
ht += DELTA_OPCODES;
} else if (c >= 0xa0 && c <= 0xa3) {
op64++;
if (pref & PRE_67)
pref |= PRE_66;
else
pref &= ~PRE_66;
}
opcode = c;
cflags = ht[ht[opcode / 4] + (opcode % 4)];
if (cflags == C_ERROR) {
error_opcode:
hs->flags |= F_ERROR | F_ERROR_OPCODE;
cflags = 0;
if ((opcode & -3) == 0x24)
cflags++;
}
x = 0;
if (cflags & C_GROUP) {
uint16_t t;
t = *(uint16_t *)(ht + (cflags & 0x7f));
cflags = (uint8_t)t;
x = (uint8_t)(t >> 8);
}
if (hs->opcode2) {
ht = hde64_table + DELTA_PREFIXES;
if (ht[ht[opcode / 4] + (opcode % 4)] & pref)
hs->flags |= F_ERROR | F_ERROR_OPCODE;
}
if (cflags & C_MODRM) {
hs->flags |= F_MODRM;
hs->modrm = c = *p++;
hs->modrm_mod = m_mod = c >> 6;
hs->modrm_rm = m_rm = c & 7;
hs->modrm_reg = m_reg = (c & 0x3f) >> 3;
if (x && ((x << m_reg) & 0x80))
hs->flags |= F_ERROR | F_ERROR_OPCODE;
if (!hs->opcode2 && opcode >= 0xd9 && opcode <= 0xdf) {
uint8_t t = opcode - 0xd9;
if (m_mod == 3) {
ht = hde64_table + DELTA_FPU_MODRM + t*8;
t = ht[m_reg] << m_rm;
} else {
ht = hde64_table + DELTA_FPU_REG;
t = ht[t] << m_reg;
}
if (t & 0x80)
hs->flags |= F_ERROR | F_ERROR_OPCODE;
}
if (pref & PRE_LOCK) {
if (m_mod == 3) {
hs->flags |= F_ERROR | F_ERROR_LOCK;
} else {
uint8_t *table_end, op = opcode;
if (hs->opcode2) {
ht = hde64_table + DELTA_OP2_LOCK_OK;
table_end = ht + DELTA_OP_ONLY_MEM - DELTA_OP2_LOCK_OK;
} else {
ht = hde64_table + DELTA_OP_LOCK_OK;
table_end = ht + DELTA_OP2_LOCK_OK - DELTA_OP_LOCK_OK;
op &= -2;
}
for (; ht != table_end; ht++)
if (*ht++ == op) {
if (!((*ht << m_reg) & 0x80))
goto no_lock_error;
else
break;
}
hs->flags |= F_ERROR | F_ERROR_LOCK;
no_lock_error:
;
}
}
if (hs->opcode2) {
switch (opcode) {
case 0x20: case 0x22:
m_mod = 3;
if (m_reg > 4 || m_reg == 1)
goto error_operand;
else
goto no_error_operand;
case 0x21: case 0x23:
m_mod = 3;
if (m_reg == 4 || m_reg == 5)
goto error_operand;
else
goto no_error_operand;
}
} else {
switch (opcode) {
case 0x8c:
if (m_reg > 5)
goto error_operand;
else
goto no_error_operand;
case 0x8e:
if (m_reg == 1 || m_reg > 5)
goto error_operand;
else
goto no_error_operand;
}
}
if (m_mod == 3) {
uint8_t *table_end;
if (hs->opcode2) {
ht = hde64_table + DELTA_OP2_ONLY_MEM;
table_end = ht + sizeof(hde64_table) - DELTA_OP2_ONLY_MEM;
} else {
ht = hde64_table + DELTA_OP_ONLY_MEM;
table_end = ht + DELTA_OP2_ONLY_MEM - DELTA_OP_ONLY_MEM;
}
for (; ht != table_end; ht += 2)
if (*ht++ == opcode) {
if (*ht++ & pref && !((*ht << m_reg) & 0x80))
goto error_operand;
else
break;
}
goto no_error_operand;
} else if (hs->opcode2) {
switch (opcode) {
case 0x50: case 0xd7: case 0xf7:
if (pref & (PRE_NONE | PRE_66))
goto error_operand;
break;
case 0xd6:
if (pref & (PRE_F2 | PRE_F3))
goto error_operand;
break;
case 0xc5:
goto error_operand;
}
goto no_error_operand;
} else
goto no_error_operand;
error_operand:
hs->flags |= F_ERROR | F_ERROR_OPERAND;
no_error_operand:
c = *p++;
if (m_reg <= 1) {
if (opcode == 0xf6)
cflags |= C_IMM8;
else if (opcode == 0xf7)
cflags |= C_IMM_P66;
}
switch (m_mod) {
case 0:
if (pref & PRE_67) {
if (m_rm == 6)
disp_size = 2;
} else
if (m_rm == 5)
disp_size = 4;
break;
case 1:
disp_size = 1;
break;
case 2:
disp_size = 2;
if (!(pref & PRE_67))
disp_size <<= 1;
}
if (m_mod != 3 && m_rm == 4) {
hs->flags |= F_SIB;
p++;
hs->sib = c;
hs->sib_scale = c >> 6;
hs->sib_index = (c & 0x3f) >> 3;
if ((hs->sib_base = c & 7) == 5 && !(m_mod & 1))
disp_size = 4;
}
p--;
switch (disp_size) {
case 1:
hs->flags |= F_DISP8;
hs->disp.disp8 = *p;
break;
case 2:
hs->flags |= F_DISP16;
hs->disp.disp16 = *(uint16_t *)p;
break;
case 4:
hs->flags |= F_DISP32;
hs->disp.disp32 = *(uint32_t *)p;
}
p += disp_size;
} else if (pref & PRE_LOCK)
hs->flags |= F_ERROR | F_ERROR_LOCK;
if (cflags & C_IMM_P66) {
if (cflags & C_REL32) {
if (pref & PRE_66) {
hs->flags |= F_IMM16 | F_RELATIVE;
hs->imm.imm16 = *(uint16_t *)p;
p += 2;
goto disasm_done;
}
goto rel32_ok;
}
if (op64) {
hs->flags |= F_IMM64;
hs->imm.imm64 = *(uint64_t *)p;
p += 8;
} else if (!(pref & PRE_66)) {
hs->flags |= F_IMM32;
hs->imm.imm32 = *(uint32_t *)p;
p += 4;
} else
goto imm16_ok;
}
if (cflags & C_IMM16) {
imm16_ok:
hs->flags |= F_IMM16;
hs->imm.imm16 = *(uint16_t *)p;
p += 2;
}
if (cflags & C_IMM8) {
hs->flags |= F_IMM8;
hs->imm.imm8 = *p++;
}
if (cflags & C_REL32) {
rel32_ok:
hs->flags |= F_IMM32 | F_RELATIVE;
hs->imm.imm32 = *(uint32_t *)p;
p += 4;
} else if (cflags & C_REL8) {
hs->flags |= F_IMM8 | F_RELATIVE;
hs->imm.imm8 = *p++;
}
disasm_done:
if ((hs->len = (uint8_t)(p-(uint8_t *)code)) > 15) {
hs->flags |= F_ERROR | F_ERROR_LENGTH;
hs->len = 15;
}
return (unsigned int)hs->len;
}
#endif // defined(_M_X64) || defined(__x86_64__)
================================================
FILE: SylantStrike/minhook/src/hde/hde64.h
================================================
/*
* Hacker Disassembler Engine 64
* Copyright (c) 2008-2009, Vyacheslav Patkov.
* All rights reserved.
*
* hde64.h: C/C++ header file
*
*/
#ifndef _HDE64_H_
#define _HDE64_H_
/* stdint.h - C99 standard header
* http://en.wikipedia.org/wiki/stdint.h
*
* if your compiler doesn't contain "stdint.h" header (for
* example, Microsoft Visual C++), you can download file:
* http://www.azillionmonkeys.com/qed/pstdint.h
* and change next line to:
* #include "pstdint.h"
*/
#include "pstdint.h"
#define F_MODRM 0x00000001
#define F_SIB 0x00000002
#define F_IMM8 0x00000004
#define F_IMM16 0x00000008
#define F_IMM32 0x00000010
#define F_IMM64 0x00000020
#define F_DISP8 0x00000040
#define F_DISP16 0x00000080
#define F_DISP32 0x00000100
#define F_RELATIVE 0x00000200
#define F_ERROR 0x00001000
#define F_ERROR_OPCODE 0x00002000
#define F_ERROR_LENGTH 0x00004000
#define F_ERROR_LOCK 0x00008000
#define F_ERROR_OPERAND 0x00010000
#define F_PREFIX_REPNZ 0x01000000
#define F_PREFIX_REPX 0x02000000
#define F_PREFIX_REP 0x03000000
#define F_PREFIX_66 0x04000000
#define F_PREFIX_67 0x08000000
#define F_PREFIX_LOCK 0x10000000
#define F_PREFIX_SEG 0x20000000
#define F_PREFIX_REX 0x40000000
#define F_PREFIX_ANY 0x7f000000
#define PREFIX_SEGMENT_CS 0x2e
#define PREFIX_SEGMENT_SS 0x36
#define PREFIX_SEGMENT_DS 0x3e
#define PREFIX_SEGMENT_ES 0x26
#define PREFIX_SEGMENT_FS 0x64
#define PREFIX_SEGMENT_GS 0x65
#define PREFIX_LOCK 0xf0
#define PREFIX_REPNZ 0xf2
#define PREFIX_REPX 0xf3
#define PREFIX_OPERAND_SIZE 0x66
#define PREFIX_ADDRESS_SIZE 0x67
#pragma pack(push,1)
typedef struct {
uint8_t len;
uint8_t p_rep;
uint8_t p_lock;
uint8_t p_seg;
uint8_t p_66;
uint8_t p_67;
uint8_t rex;
uint8_t rex_w;
uint8_t rex_r;
uint8_t rex_x;
uint8_t rex_b;
uint8_t opcode;
uint8_t opcode2;
uint8_t modrm;
uint8_t modrm_mod;
uint8_t modrm_reg;
uint8_t modrm_rm;
uint8_t sib;
uint8_t sib_scale;
uint8_t sib_index;
uint8_t sib_base;
union {
uint8_t imm8;
uint16_t imm16;
uint32_t imm32;
uint64_t imm64;
} imm;
union {
uint8_t disp8;
uint16_t disp16;
uint32_t disp32;
} disp;
uint32_t flags;
} hde64s;
#pragma pack(pop)
#ifdef __cplusplus
extern "C" {
#endif
/* __cdecl */
unsigned int hde64_disasm(const void *code, hde64s *hs);
#ifdef __cplusplus
}
#endif
#endif /* _HDE64_H_ */
================================================
FILE: SylantStrike/minhook/src/hde/pstdint.h
================================================
/*
* MinHook - The Minimalistic API Hooking Library for x64/x86
* Copyright (C) 2009-2017 Tsuda Kageyu. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include
// Integer types for HDE.
typedef INT8 int8_t;
typedef INT16 int16_t;
typedef INT32 int32_t;
typedef INT64 int64_t;
typedef UINT8 uint8_t;
typedef UINT16 uint16_t;
typedef UINT32 uint32_t;
typedef UINT64 uint64_t;
================================================
FILE: SylantStrike/minhook/src/hde/table32.h
================================================
/*
* Hacker Disassembler Engine 32 C
* Copyright (c) 2008-2009, Vyacheslav Patkov.
* All rights reserved.
*
*/
#define C_NONE 0x00
#define C_MODRM 0x01
#define C_IMM8 0x02
#define C_IMM16 0x04
#define C_IMM_P66 0x10
#define C_REL8 0x20
#define C_REL32 0x40
#define C_GROUP 0x80
#define C_ERROR 0xff
#define PRE_ANY 0x00
#define PRE_NONE 0x01
#define PRE_F2 0x02
#define PRE_F3 0x04
#define PRE_66 0x08
#define PRE_67 0x10
#define PRE_LOCK 0x20
#define PRE_SEG 0x40
#define PRE_ALL 0xff
#define DELTA_OPCODES 0x4a
#define DELTA_FPU_REG 0xf1
#define DELTA_FPU_MODRM 0xf8
#define DELTA_PREFIXES 0x130
#define DELTA_OP_LOCK_OK 0x1a1
#define DELTA_OP2_LOCK_OK 0x1b9
#define DELTA_OP_ONLY_MEM 0x1cb
#define DELTA_OP2_ONLY_MEM 0x1da
unsigned char hde32_table[] = {
0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,0xa8,0xa3,
0xa8,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xaa,0xac,0xaa,0xb2,0xaa,0x9f,0x9f,
0x9f,0x9f,0xb5,0xa3,0xa3,0xa4,0xaa,0xaa,0xba,0xaa,0x96,0xaa,0xa8,0xaa,0xc3,
0xc3,0x96,0x96,0xb7,0xae,0xd6,0xbd,0xa3,0xc5,0xa3,0xa3,0x9f,0xc3,0x9c,0xaa,
0xaa,0xac,0xaa,0xbf,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0x90,
0x82,0x7d,0x97,0x59,0x59,0x59,0x59,0x59,0x7f,0x59,0x59,0x60,0x7d,0x7f,0x7f,
0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x9a,0x88,0x7d,
0x59,0x50,0x50,0x50,0x50,0x59,0x59,0x59,0x59,0x61,0x94,0x61,0x9e,0x59,0x59,
0x85,0x59,0x92,0xa3,0x60,0x60,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,0x59,
0x59,0x59,0x9f,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xcc,0x01,0xbc,0x03,0xf0,
0x10,0x10,0x10,0x10,0x50,0x50,0x50,0x50,0x14,0x20,0x20,0x20,0x20,0x01,0x01,
0x01,0x01,0xc4,0x02,0x10,0x00,0x00,0x00,0x00,0x01,0x01,0xc0,0xc2,0x10,0x11,
0x02,0x03,0x11,0x03,0x03,0x04,0x00,0x00,0x14,0x00,0x02,0x00,0x00,0xc6,0xc8,
0x02,0x02,0x02,0x02,0x00,0x00,0xff,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0xca,
0x01,0x01,0x01,0x00,0x06,0x00,0x04,0x00,0xc0,0xc2,0x01,0x01,0x03,0x01,0xff,
0xff,0x01,0x00,0x03,0xc4,0xc4,0xc6,0x03,0x01,0x01,0x01,0xff,0x03,0x03,0x03,
0xc8,0x40,0x00,0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,
0x00,0x00,0x00,0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,
0x00,0xff,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0xff,0xff,0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x7f,0x00,0x00,0xff,0x4a,0x4a,0x4a,0x4a,0x4b,0x52,0x4a,0x4a,0x4a,0x4a,0x4f,
0x4c,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x55,0x45,0x40,0x4a,0x4a,0x4a,
0x45,0x59,0x4d,0x46,0x4a,0x5d,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,0x4a,
0x4a,0x4a,0x4a,0x4a,0x4a,0x61,0x63,0x67,0x4e,0x4a,0x4a,0x6b,0x6d,0x4a,0x4a,
0x45,0x6d,0x4a,0x4a,0x44,0x45,0x4a,0x4a,0x00,0x00,0x00,0x02,0x0d,0x06,0x06,
0x06,0x06,0x0e,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x00,0x06,0x06,0x02,0x06,
0x00,0x0a,0x0a,0x07,0x07,0x06,0x02,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,
0x04,0x04,0x00,0x00,0x00,0x0e,0x05,0x06,0x06,0x06,0x01,0x06,0x00,0x00,0x08,
0x00,0x10,0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,
0x86,0x00,0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,
0xf8,0xbb,0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,
0xc4,0xff,0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,
0x13,0x09,0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,
0xb2,0xff,0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,
0xe7,0x08,0x00,0xf0,0x02,0x00
};
================================================
FILE: SylantStrike/minhook/src/hde/table64.h
================================================
/*
* Hacker Disassembler Engine 64 C
* Copyright (c) 2008-2009, Vyacheslav Patkov.
* All rights reserved.
*
*/
#define C_NONE 0x00
#define C_MODRM 0x01
#define C_IMM8 0x02
#define C_IMM16 0x04
#define C_IMM_P66 0x10
#define C_REL8 0x20
#define C_REL32 0x40
#define C_GROUP 0x80
#define C_ERROR 0xff
#define PRE_ANY 0x00
#define PRE_NONE 0x01
#define PRE_F2 0x02
#define PRE_F3 0x04
#define PRE_66 0x08
#define PRE_67 0x10
#define PRE_LOCK 0x20
#define PRE_SEG 0x40
#define PRE_ALL 0xff
#define DELTA_OPCODES 0x4a
#define DELTA_FPU_REG 0xfd
#define DELTA_FPU_MODRM 0x104
#define DELTA_PREFIXES 0x13c
#define DELTA_OP_LOCK_OK 0x1ae
#define DELTA_OP2_LOCK_OK 0x1c6
#define DELTA_OP_ONLY_MEM 0x1d8
#define DELTA_OP2_ONLY_MEM 0x1e7
unsigned char hde64_table[] = {
0xa5,0xaa,0xa5,0xb8,0xa5,0xaa,0xa5,0xaa,0xa5,0xb8,0xa5,0xb8,0xa5,0xb8,0xa5,
0xb8,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xc0,0xac,0xc0,0xcc,0xc0,0xa1,0xa1,
0xa1,0xa1,0xb1,0xa5,0xa5,0xa6,0xc0,0xc0,0xd7,0xda,0xe0,0xc0,0xe4,0xc0,0xea,
0xea,0xe0,0xe0,0x98,0xc8,0xee,0xf1,0xa5,0xd3,0xa5,0xa5,0xa1,0xea,0x9e,0xc0,
0xc0,0xc2,0xc0,0xe6,0x03,0x7f,0x11,0x7f,0x01,0x7f,0x01,0x3f,0x01,0x01,0xab,
0x8b,0x90,0x64,0x5b,0x5b,0x5b,0x5b,0x5b,0x92,0x5b,0x5b,0x76,0x90,0x92,0x92,
0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x6a,0x73,0x90,
0x5b,0x52,0x52,0x52,0x52,0x5b,0x5b,0x5b,0x5b,0x77,0x7c,0x77,0x85,0x5b,0x5b,
0x70,0x5b,0x7a,0xaf,0x76,0x76,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,0x5b,
0x5b,0x5b,0x86,0x01,0x03,0x01,0x04,0x03,0xd5,0x03,0xd5,0x03,0xcc,0x01,0xbc,
0x03,0xf0,0x03,0x03,0x04,0x00,0x50,0x50,0x50,0x50,0xff,0x20,0x20,0x20,0x20,
0x01,0x01,0x01,0x01,0xc4,0x02,0x10,0xff,0xff,0xff,0x01,0x00,0x03,0x11,0xff,
0x03,0xc4,0xc6,0xc8,0x02,0x10,0x00,0xff,0xcc,0x01,0x01,0x01,0x00,0x00,0x00,
0x00,0x01,0x01,0x03,0x01,0xff,0xff,0xc0,0xc2,0x10,0x11,0x02,0x03,0x01,0x01,
0x01,0xff,0xff,0xff,0x00,0x00,0x00,0xff,0x00,0x00,0xff,0xff,0xff,0xff,0x10,
0x10,0x10,0x10,0x02,0x10,0x00,0x00,0xc6,0xc8,0x02,0x02,0x02,0x02,0x06,0x00,
0x04,0x00,0x02,0xff,0x00,0xc0,0xc2,0x01,0x01,0x03,0x03,0x03,0xca,0x40,0x00,
0x0a,0x00,0x04,0x00,0x00,0x00,0x00,0x7f,0x00,0x33,0x01,0x00,0x00,0x00,0x00,
0x00,0x00,0xff,0xbf,0xff,0xff,0x00,0x00,0x00,0x00,0x07,0x00,0x00,0xff,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xff,0xff,
0x00,0x00,0x00,0xbf,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x7f,0x00,0x00,
0xff,0x40,0x40,0x40,0x40,0x41,0x49,0x40,0x40,0x40,0x40,0x4c,0x42,0x40,0x40,
0x40,0x40,0x40,0x40,0x40,0x40,0x4f,0x44,0x53,0x40,0x40,0x40,0x44,0x57,0x43,
0x5c,0x40,0x60,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,0x40,
0x40,0x40,0x64,0x66,0x6e,0x6b,0x40,0x40,0x6a,0x46,0x40,0x40,0x44,0x46,0x40,
0x40,0x5b,0x44,0x40,0x40,0x00,0x00,0x00,0x00,0x06,0x06,0x06,0x06,0x01,0x06,
0x06,0x02,0x06,0x06,0x00,0x06,0x00,0x0a,0x0a,0x00,0x00,0x00,0x02,0x07,0x07,
0x06,0x02,0x0d,0x06,0x06,0x06,0x0e,0x05,0x05,0x02,0x02,0x00,0x00,0x04,0x04,
0x04,0x04,0x05,0x06,0x06,0x06,0x00,0x00,0x00,0x0e,0x00,0x00,0x08,0x00,0x10,
0x00,0x18,0x00,0x20,0x00,0x28,0x00,0x30,0x00,0x80,0x01,0x82,0x01,0x86,0x00,
0xf6,0xcf,0xfe,0x3f,0xab,0x00,0xb0,0x00,0xb1,0x00,0xb3,0x00,0xba,0xf8,0xbb,
0x00,0xc0,0x00,0xc1,0x00,0xc7,0xbf,0x62,0xff,0x00,0x8d,0xff,0x00,0xc4,0xff,
0x00,0xc5,0xff,0x00,0xff,0xff,0xeb,0x01,0xff,0x0e,0x12,0x08,0x00,0x13,0x09,
0x00,0x16,0x08,0x00,0x17,0x09,0x00,0x2b,0x09,0x00,0xae,0xff,0x07,0xb2,0xff,
0x00,0xb4,0xff,0x00,0xb5,0xff,0x00,0xc3,0x01,0x00,0xc7,0xff,0xbf,0xe7,0x08,
0x00,0xf0,0x02,0x00
};
================================================
FILE: SylantStrike/minhook/src/hook.c
================================================
/*
* MinHook - The Minimalistic API Hooking Library for x64/x86
* Copyright (C) 2009-2017 Tsuda Kageyu.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
#include
#include
#include "../include/MinHook.h"
#include "buffer.h"
#include "trampoline.h"
#ifndef ARRAYSIZE
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
#endif
// Initial capacity of the HOOK_ENTRY buffer.
#define INITIAL_HOOK_CAPACITY 32
// Initial capacity of the thread IDs buffer.
#define INITIAL_THREAD_CAPACITY 128
// Special hook position values.
#define INVALID_HOOK_POS UINT_MAX
#define ALL_HOOKS_POS UINT_MAX
// Freeze() action argument defines.
#define ACTION_DISABLE 0
#define ACTION_ENABLE 1
#define ACTION_APPLY_QUEUED 2
// Thread access rights for suspending/resuming threads.
#define THREAD_ACCESS \
(THREAD_SUSPEND_RESUME | THREAD_GET_CONTEXT | THREAD_QUERY_INFORMATION | THREAD_SET_CONTEXT)
// Hook information.
typedef struct _HOOK_ENTRY
{
LPVOID pTarget; // Address of the target function.
LPVOID pDetour; // Address of the detour or relay function.
LPVOID pTrampoline; // Address of the trampoline function.
UINT8 backup[8]; // Original prologue of the target function.
UINT8 patchAbove : 1; // Uses the hot patch area.
UINT8 isEnabled : 1; // Enabled.
UINT8 queueEnable : 1; // Queued for enabling/disabling when != isEnabled.
UINT nIP : 4; // Count of the instruction boundaries.
UINT8 oldIPs[8]; // Instruction boundaries of the target function.
UINT8 newIPs[8]; // Instruction boundaries of the trampoline function.
} HOOK_ENTRY, *PHOOK_ENTRY;
// Suspended threads for Freeze()/Unfreeze().
typedef struct _FROZEN_THREADS
{
LPDWORD pItems; // Data heap
UINT capacity; // Size of allocated data heap, items
UINT size; // Actual number of data items
} FROZEN_THREADS, *PFROZEN_THREADS;
//-------------------------------------------------------------------------
// Global Variables:
//-------------------------------------------------------------------------
// Spin lock flag for EnterSpinLock()/LeaveSpinLock().
volatile LONG g_isLocked = FALSE;
// Private heap handle. If not NULL, this library is initialized.
HANDLE g_hHeap = NULL;
// Hook entries.
struct
{
PHOOK_ENTRY pItems; // Data heap
UINT capacity; // Size of allocated data heap, items
UINT size; // Actual number of data items
} g_hooks;
//-------------------------------------------------------------------------
// Returns INVALID_HOOK_POS if not found.
static UINT FindHookEntry(LPVOID pTarget)
{
UINT i;
for (i = 0; i < g_hooks.size; ++i)
{
if ((ULONG_PTR)pTarget == (ULONG_PTR)g_hooks.pItems[i].pTarget)
return i;
}
return INVALID_HOOK_POS;
}
//-------------------------------------------------------------------------
static PHOOK_ENTRY AddHookEntry()
{
if (g_hooks.pItems == NULL)
{
g_hooks.capacity = INITIAL_HOOK_CAPACITY;
g_hooks.pItems = (PHOOK_ENTRY)HeapAlloc(
g_hHeap, 0, g_hooks.capacity * sizeof(HOOK_ENTRY));
if (g_hooks.pItems == NULL)
return NULL;
}
else if (g_hooks.size >= g_hooks.capacity)
{
PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc(
g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity * 2) * sizeof(HOOK_ENTRY));
if (p == NULL)
return NULL;
g_hooks.capacity *= 2;
g_hooks.pItems = p;
}
return &g_hooks.pItems[g_hooks.size++];
}
//-------------------------------------------------------------------------
static void DeleteHookEntry(UINT pos)
{
if (pos < g_hooks.size - 1)
g_hooks.pItems[pos] = g_hooks.pItems[g_hooks.size - 1];
g_hooks.size--;
if (g_hooks.capacity / 2 >= INITIAL_HOOK_CAPACITY && g_hooks.capacity / 2 >= g_hooks.size)
{
PHOOK_ENTRY p = (PHOOK_ENTRY)HeapReAlloc(
g_hHeap, 0, g_hooks.pItems, (g_hooks.capacity / 2) * sizeof(HOOK_ENTRY));
if (p == NULL)
return;
g_hooks.capacity /= 2;
g_hooks.pItems = p;
}
}
//-------------------------------------------------------------------------
static DWORD_PTR FindOldIP(PHOOK_ENTRY pHook, DWORD_PTR ip)
{
UINT i;
if (pHook->patchAbove && ip == ((DWORD_PTR)pHook->pTarget - sizeof(JMP_REL)))
return (DWORD_PTR)pHook->pTarget;
for (i = 0; i < pHook->nIP; ++i)
{
if (ip == ((DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i]))
return (DWORD_PTR)pHook->pTarget + pHook->oldIPs[i];
}
#if defined(_M_X64) || defined(__x86_64__)
// Check relay function.
if (ip == (DWORD_PTR)pHook->pDetour)
return (DWORD_PTR)pHook->pTarget;
#endif
return 0;
}
//-------------------------------------------------------------------------
static DWORD_PTR FindNewIP(PHOOK_ENTRY pHook, DWORD_PTR ip)
{
UINT i;
for (i = 0; i < pHook->nIP; ++i)
{
if (ip == ((DWORD_PTR)pHook->pTarget + pHook->oldIPs[i]))
return (DWORD_PTR)pHook->pTrampoline + pHook->newIPs[i];
}
return 0;
}
//-------------------------------------------------------------------------
static void ProcessThreadIPs(HANDLE hThread, UINT pos, UINT action)
{
// If the thread suspended in the overwritten area,
// move IP to the proper address.
CONTEXT c;
#if defined(_M_X64) || defined(__x86_64__)
DWORD64 *pIP = &c.Rip;
#else
DWORD *pIP = &c.Eip;
#endif
UINT count;
c.ContextFlags = CONTEXT_CONTROL;
if (!GetThreadContext(hThread, &c))
return;
if (pos == ALL_HOOKS_POS)
{
pos = 0;
count = g_hooks.size;
}
else
{
count = pos + 1;
}
for (; pos < count; ++pos)
{
PHOOK_ENTRY pHook = &g_hooks.pItems[pos];
BOOL enable;
DWORD_PTR ip;
switch (action)
{
case ACTION_DISABLE:
enable = FALSE;
break;
case ACTION_ENABLE:
enable = TRUE;
break;
default: // ACTION_APPLY_QUEUED
enable = pHook->queueEnable;
break;
}
if (pHook->isEnabled == enable)
continue;
if (enable)
ip = FindNewIP(pHook, *pIP);
else
ip = FindOldIP(pHook, *pIP);
if (ip != 0)
{
*pIP = ip;
SetThreadContext(hThread, &c);
}
}
}
//-------------------------------------------------------------------------
static VOID EnumerateThreads(PFROZEN_THREADS pThreads)
{
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hSnapshot != INVALID_HANDLE_VALUE)
{
THREADENTRY32 te;
te.dwSize = sizeof(THREADENTRY32);
if (Thread32First(hSnapshot, &te))
{
do
{
if (te.dwSize >= (FIELD_OFFSET(THREADENTRY32, th32OwnerProcessID) + sizeof(DWORD))
&& te.th32OwnerProcessID == GetCurrentProcessId()
&& te.th32ThreadID != GetCurrentThreadId())
{
if (pThreads->pItems == NULL)
{
pThreads->capacity = INITIAL_THREAD_CAPACITY;
pThreads->pItems
= (LPDWORD)HeapAlloc(g_hHeap, 0, pThreads->capacity * sizeof(DWORD));
if (pThreads->pItems == NULL)
break;
}
else if (pThreads->size >= pThreads->capacity)
{
LPDWORD p = (LPDWORD)HeapReAlloc(
g_hHeap, 0, pThreads->pItems, (pThreads->capacity * 2) * sizeof(DWORD));
if (p == NULL)
break;
pThreads->capacity *= 2;
pThreads->pItems = p;
}
pThreads->pItems[pThreads->size++] = te.th32ThreadID;
}
te.dwSize = sizeof(THREADENTRY32);
} while (Thread32Next(hSnapshot, &te));
}
CloseHandle(hSnapshot);
}
}
//-------------------------------------------------------------------------
static VOID Freeze(PFROZEN_THREADS pThreads, UINT pos, UINT action)
{
pThreads->pItems = NULL;
pThreads->capacity = 0;
pThreads->size = 0;
EnumerateThreads(pThreads);
if (pThreads->pItems != NULL)
{
UINT i;
for (i = 0; i < pThreads->size; ++i)
{
HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]);
if (hThread != NULL)
{
SuspendThread(hThread);
ProcessThreadIPs(hThread, pos, action);
CloseHandle(hThread);
}
}
}
}
//-------------------------------------------------------------------------
static VOID Unfreeze(PFROZEN_THREADS pThreads)
{
if (pThreads->pItems != NULL)
{
UINT i;
for (i = 0; i < pThreads->size; ++i)
{
HANDLE hThread = OpenThread(THREAD_ACCESS, FALSE, pThreads->pItems[i]);
if (hThread != NULL)
{
ResumeThread(hThread);
CloseHandle(hThread);
}
}
HeapFree(g_hHeap, 0, pThreads->pItems);
}
}
//-------------------------------------------------------------------------
static MH_STATUS EnableHookLL(UINT pos, BOOL enable)
{
PHOOK_ENTRY pHook = &g_hooks.pItems[pos];
DWORD oldProtect;
SIZE_T patchSize = sizeof(JMP_REL);
LPBYTE pPatchTarget = (LPBYTE)pHook->pTarget;
if (pHook->patchAbove)
{
pPatchTarget -= sizeof(JMP_REL);
patchSize += sizeof(JMP_REL_SHORT);
}
if (!VirtualProtect(pPatchTarget, patchSize, PAGE_EXECUTE_READWRITE, &oldProtect))
return MH_ERROR_MEMORY_PROTECT;
if (enable)
{
PJMP_REL pJmp = (PJMP_REL)pPatchTarget;
pJmp->opcode = 0xE9;
pJmp->operand = (UINT32)((LPBYTE)pHook->pDetour - (pPatchTarget + sizeof(JMP_REL)));
if (pHook->patchAbove)
{
PJMP_REL_SHORT pShortJmp = (PJMP_REL_SHORT)pHook->pTarget;
pShortJmp->opcode = 0xEB;
pShortJmp->operand = (UINT8)(0 - (sizeof(JMP_REL_SHORT) + sizeof(JMP_REL)));
}
}
else
{
if (pHook->patchAbove)
memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
else
memcpy(pPatchTarget, pHook->backup, sizeof(JMP_REL));
}
VirtualProtect(pPatchTarget, patchSize, oldProtect, &oldProtect);
// Just-in-case measure.
FlushInstructionCache(GetCurrentProcess(), pPatchTarget, patchSize);
pHook->isEnabled = enable;
pHook->queueEnable = enable;
return MH_OK;
}
//-------------------------------------------------------------------------
static MH_STATUS EnableAllHooksLL(BOOL enable)
{
MH_STATUS status = MH_OK;
UINT i, first = INVALID_HOOK_POS;
for (i = 0; i < g_hooks.size; ++i)
{
if (g_hooks.pItems[i].isEnabled != enable)
{
first = i;
break;
}
}
if (first != INVALID_HOOK_POS)
{
FROZEN_THREADS threads;
Freeze(&threads, ALL_HOOKS_POS, enable ? ACTION_ENABLE : ACTION_DISABLE);
for (i = first; i < g_hooks.size; ++i)
{
if (g_hooks.pItems[i].isEnabled != enable)
{
status = EnableHookLL(i, enable);
if (status != MH_OK)
break;
}
}
Unfreeze(&threads);
}
return status;
}
//-------------------------------------------------------------------------
static VOID EnterSpinLock(VOID)
{
SIZE_T spinCount = 0;
// Wait until the flag is FALSE.
while (InterlockedCompareExchange(&g_isLocked, TRUE, FALSE) != FALSE)
{
// No need to generate a memory barrier here, since InterlockedCompareExchange()
// generates a full memory barrier itself.
// Prevent the loop from being too busy.
if (spinCount < 32)
Sleep(0);
else
Sleep(1);
spinCount++;
}
}
//-------------------------------------------------------------------------
static VOID LeaveSpinLock(VOID)
{
// No need to generate a memory barrier here, since InterlockedExchange()
// generates a full memory barrier itself.
InterlockedExchange(&g_isLocked, FALSE);
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_Initialize(VOID)
{
MH_STATUS status = MH_OK;
EnterSpinLock();
if (g_hHeap == NULL)
{
g_hHeap = HeapCreate(0, 0, 0);
if (g_hHeap != NULL)
{
// Initialize the internal function buffer.
InitializeBuffer();
}
else
{
status = MH_ERROR_MEMORY_ALLOC;
}
}
else
{
status = MH_ERROR_ALREADY_INITIALIZED;
}
LeaveSpinLock();
return status;
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_Uninitialize(VOID)
{
MH_STATUS status = MH_OK;
EnterSpinLock();
if (g_hHeap != NULL)
{
status = EnableAllHooksLL(FALSE);
if (status == MH_OK)
{
// Free the internal function buffer.
// HeapFree is actually not required, but some tools detect a false
// memory leak without HeapFree.
UninitializeBuffer();
HeapFree(g_hHeap, 0, g_hooks.pItems);
HeapDestroy(g_hHeap);
g_hHeap = NULL;
g_hooks.pItems = NULL;
g_hooks.capacity = 0;
g_hooks.size = 0;
}
}
else
{
status = MH_ERROR_NOT_INITIALIZED;
}
LeaveSpinLock();
return status;
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal)
{
MH_STATUS status = MH_OK;
EnterSpinLock();
if (g_hHeap != NULL)
{
if (IsExecutableAddress(pTarget) && IsExecutableAddress(pDetour))
{
UINT pos = FindHookEntry(pTarget);
if (pos == INVALID_HOOK_POS)
{
LPVOID pBuffer = AllocateBuffer(pTarget);
if (pBuffer != NULL)
{
TRAMPOLINE ct;
ct.pTarget = pTarget;
ct.pDetour = pDetour;
ct.pTrampoline = pBuffer;
if (CreateTrampolineFunction(&ct))
{
PHOOK_ENTRY pHook = AddHookEntry();
if (pHook != NULL)
{
pHook->pTarget = ct.pTarget;
#if defined(_M_X64) || defined(__x86_64__)
pHook->pDetour = ct.pRelay;
#else
pHook->pDetour = ct.pDetour;
#endif
pHook->pTrampoline = ct.pTrampoline;
pHook->patchAbove = ct.patchAbove;
pHook->isEnabled = FALSE;
pHook->queueEnable = FALSE;
pHook->nIP = ct.nIP;
memcpy(pHook->oldIPs, ct.oldIPs, ARRAYSIZE(ct.oldIPs));
memcpy(pHook->newIPs, ct.newIPs, ARRAYSIZE(ct.newIPs));
// Back up the target function.
if (ct.patchAbove)
{
memcpy(
pHook->backup,
(LPBYTE)pTarget - sizeof(JMP_REL),
sizeof(JMP_REL) + sizeof(JMP_REL_SHORT));
}
else
{
memcpy(pHook->backup, pTarget, sizeof(JMP_REL));
}
if (ppOriginal != NULL)
*ppOriginal = pHook->pTrampoline;
}
else
{
status = MH_ERROR_MEMORY_ALLOC;
}
}
else
{
status = MH_ERROR_UNSUPPORTED_FUNCTION;
}
if (status != MH_OK)
{
FreeBuffer(pBuffer);
}
}
else
{
status = MH_ERROR_MEMORY_ALLOC;
}
}
else
{
status = MH_ERROR_ALREADY_CREATED;
}
}
else
{
status = MH_ERROR_NOT_EXECUTABLE;
}
}
else
{
status = MH_ERROR_NOT_INITIALIZED;
}
LeaveSpinLock();
return status;
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget)
{
MH_STATUS status = MH_OK;
EnterSpinLock();
if (g_hHeap != NULL)
{
UINT pos = FindHookEntry(pTarget);
if (pos != INVALID_HOOK_POS)
{
if (g_hooks.pItems[pos].isEnabled)
{
FROZEN_THREADS threads;
Freeze(&threads, pos, ACTION_DISABLE);
status = EnableHookLL(pos, FALSE);
Unfreeze(&threads);
}
if (status == MH_OK)
{
FreeBuffer(g_hooks.pItems[pos].pTrampoline);
DeleteHookEntry(pos);
}
}
else
{
status = MH_ERROR_NOT_CREATED;
}
}
else
{
status = MH_ERROR_NOT_INITIALIZED;
}
LeaveSpinLock();
return status;
}
//-------------------------------------------------------------------------
static MH_STATUS EnableHook(LPVOID pTarget, BOOL enable)
{
MH_STATUS status = MH_OK;
EnterSpinLock();
if (g_hHeap != NULL)
{
if (pTarget == MH_ALL_HOOKS)
{
status = EnableAllHooksLL(enable);
}
else
{
FROZEN_THREADS threads;
UINT pos = FindHookEntry(pTarget);
if (pos != INVALID_HOOK_POS)
{
if (g_hooks.pItems[pos].isEnabled != enable)
{
Freeze(&threads, pos, ACTION_ENABLE);
status = EnableHookLL(pos, enable);
Unfreeze(&threads);
}
else
{
status = enable ? MH_ERROR_ENABLED : MH_ERROR_DISABLED;
}
}
else
{
status = MH_ERROR_NOT_CREATED;
}
}
}
else
{
status = MH_ERROR_NOT_INITIALIZED;
}
LeaveSpinLock();
return status;
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget)
{
return EnableHook(pTarget, TRUE);
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget)
{
return EnableHook(pTarget, FALSE);
}
//-------------------------------------------------------------------------
static MH_STATUS QueueHook(LPVOID pTarget, BOOL queueEnable)
{
MH_STATUS status = MH_OK;
EnterSpinLock();
if (g_hHeap != NULL)
{
if (pTarget == MH_ALL_HOOKS)
{
UINT i;
for (i = 0; i < g_hooks.size; ++i)
g_hooks.pItems[i].queueEnable = queueEnable;
}
else
{
UINT pos = FindHookEntry(pTarget);
if (pos != INVALID_HOOK_POS)
{
g_hooks.pItems[pos].queueEnable = queueEnable;
}
else
{
status = MH_ERROR_NOT_CREATED;
}
}
}
else
{
status = MH_ERROR_NOT_INITIALIZED;
}
LeaveSpinLock();
return status;
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget)
{
return QueueHook(pTarget, TRUE);
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget)
{
return QueueHook(pTarget, FALSE);
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_ApplyQueued(VOID)
{
MH_STATUS status = MH_OK;
UINT i, first = INVALID_HOOK_POS;
EnterSpinLock();
if (g_hHeap != NULL)
{
for (i = 0; i < g_hooks.size; ++i)
{
if (g_hooks.pItems[i].isEnabled != g_hooks.pItems[i].queueEnable)
{
first = i;
break;
}
}
if (first != INVALID_HOOK_POS)
{
FROZEN_THREADS threads;
Freeze(&threads, ALL_HOOKS_POS, ACTION_APPLY_QUEUED);
for (i = first; i < g_hooks.size; ++i)
{
PHOOK_ENTRY pHook = &g_hooks.pItems[i];
if (pHook->isEnabled != pHook->queueEnable)
{
status = EnableHookLL(i, pHook->queueEnable);
if (status != MH_OK)
break;
}
}
Unfreeze(&threads);
}
}
else
{
status = MH_ERROR_NOT_INITIALIZED;
}
LeaveSpinLock();
return status;
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_CreateHookApiEx(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour,
LPVOID *ppOriginal, LPVOID *ppTarget)
{
HMODULE hModule;
LPVOID pTarget;
hModule = GetModuleHandleW(pszModule);
if (hModule == NULL)
return MH_ERROR_MODULE_NOT_FOUND;
pTarget = (LPVOID)GetProcAddress(hModule, pszProcName);
if (pTarget == NULL)
return MH_ERROR_FUNCTION_NOT_FOUND;
if(ppTarget != NULL)
*ppTarget = pTarget;
return MH_CreateHook(pTarget, pDetour, ppOriginal);
}
//-------------------------------------------------------------------------
MH_STATUS WINAPI MH_CreateHookApi(
LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal)
{
return MH_CreateHookApiEx(pszModule, pszProcName, pDetour, ppOriginal, NULL);
}
//-------------------------------------------------------------------------
const char * WINAPI MH_StatusToString(MH_STATUS status)
{
#define MH_ST2STR(x) \
case x: \
return #x;
switch (status) {
MH_ST2STR(MH_UNKNOWN)
MH_ST2STR(MH_OK)
MH_ST2STR(MH_ERROR_ALREADY_INITIALIZED)
MH_ST2STR(MH_ERROR_NOT_INITIALIZED)
MH_ST2STR(MH_ERROR_ALREADY_CREATED)
MH_ST2STR(MH_ERROR_NOT_CREATED)
MH_ST2STR(MH_ERROR_ENABLED)
MH_ST2STR(MH_ERROR_DISABLED)
MH_ST2STR(MH_ERROR_NOT_EXECUTABLE)
MH_ST2STR(MH_ERROR_UNSUPPORTED_FUNCTION)
MH_ST2STR(MH_ERROR_MEMORY_ALLOC)
MH_ST2STR(MH_ERROR_MEMORY_PROTECT)
MH_ST2STR(MH_ERROR_MODULE_NOT_FOUND)
MH_ST2STR(MH_ERROR_FUNCTION_NOT_FOUND)
}
#undef MH_ST2STR
return "(unknown)";
}
================================================
FILE: SylantStrike/minhook/src/trampoline.c
================================================
/*
* MinHook - The Minimalistic API Hooking Library for x64/x86
* Copyright (C) 2009-2017 Tsuda Kageyu.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include
#ifndef ARRAYSIZE
#define ARRAYSIZE(A) (sizeof(A)/sizeof((A)[0]))
#endif
#if defined(_M_X64) || defined(__x86_64__)
#include "./hde/hde64.h"
typedef hde64s HDE;
#define HDE_DISASM(code, hs) hde64_disasm(code, hs)
#else
#include "./hde/hde32.h"
typedef hde32s HDE;
#define HDE_DISASM(code, hs) hde32_disasm(code, hs)
#endif
#include "trampoline.h"
#include "buffer.h"
// Maximum size of a trampoline function.
#if defined(_M_X64) || defined(__x86_64__)
#define TRAMPOLINE_MAX_SIZE (MEMORY_SLOT_SIZE - sizeof(JMP_ABS))
#else
#define TRAMPOLINE_MAX_SIZE MEMORY_SLOT_SIZE
#endif
//-------------------------------------------------------------------------
static BOOL IsCodePadding(LPBYTE pInst, UINT size)
{
UINT i;
if (pInst[0] != 0x00 && pInst[0] != 0x90 && pInst[0] != 0xCC)
return FALSE;
for (i = 1; i < size; ++i)
{
if (pInst[i] != pInst[0])
return FALSE;
}
return TRUE;
}
//-------------------------------------------------------------------------
BOOL CreateTrampolineFunction(PTRAMPOLINE ct)
{
#if defined(_M_X64) || defined(__x86_64__)
CALL_ABS call = {
0xFF, 0x15, 0x00000002, // FF15 00000002: CALL [RIP+8]
0xEB, 0x08, // EB 08: JMP +10
0x0000000000000000ULL // Absolute destination address
};
JMP_ABS jmp = {
0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
0x0000000000000000ULL // Absolute destination address
};
JCC_ABS jcc = {
0x70, 0x0E, // 7* 0E: J** +16
0xFF, 0x25, 0x00000000, // FF25 00000000: JMP [RIP+6]
0x0000000000000000ULL // Absolute destination address
};
#else
CALL_REL call = {
0xE8, // E8 xxxxxxxx: CALL +5+xxxxxxxx
0x00000000 // Relative destination address
};
JMP_REL jmp = {
0xE9, // E9 xxxxxxxx: JMP +5+xxxxxxxx
0x00000000 // Relative destination address
};
JCC_REL jcc = {
0x0F, 0x80, // 0F8* xxxxxxxx: J** +6+xxxxxxxx
0x00000000 // Relative destination address
};
#endif
UINT8 oldPos = 0;
UINT8 newPos = 0;
ULONG_PTR jmpDest = 0; // Destination address of an internal jump.
BOOL finished = FALSE; // Is the function completed?
#if defined(_M_X64) || defined(__x86_64__)
UINT8 instBuf[16];
#endif
ct->patchAbove = FALSE;
ct->nIP = 0;
do
{
HDE hs;
UINT copySize;
LPVOID pCopySrc;
ULONG_PTR pOldInst = (ULONG_PTR)ct->pTarget + oldPos;
ULONG_PTR pNewInst = (ULONG_PTR)ct->pTrampoline + newPos;
copySize = HDE_DISASM((LPVOID)pOldInst, &hs);
if (hs.flags & F_ERROR)
return FALSE;
pCopySrc = (LPVOID)pOldInst;
if (oldPos >= sizeof(JMP_REL))
{
// The trampoline function is long enough.
// Complete the function with the jump to the target function.
#if defined(_M_X64) || defined(__x86_64__)
jmp.address = pOldInst;
#else
jmp.operand = (UINT32)(pOldInst - (pNewInst + sizeof(jmp)));
#endif
pCopySrc = &jmp;
copySize = sizeof(jmp);
finished = TRUE;
}
#if defined(_M_X64) || defined(__x86_64__)
else if ((hs.modrm & 0xC7) == 0x05)
{
// Instructions using RIP relative addressing. (ModR/M = 00???101B)
// Modify the RIP relative address.
PUINT32 pRelAddr;
// Avoid using memcpy to reduce the footprint.
#ifndef _MSC_VER
memcpy(instBuf, (LPBYTE)pOldInst, copySize);
#else
__movsb(instBuf, (LPBYTE)pOldInst, copySize);
#endif
pCopySrc = instBuf;
// Relative address is stored at (instruction length - immediate value length - 4).
pRelAddr = (PUINT32)(instBuf + hs.len - ((hs.flags & 0x3C) >> 2) - 4);
*pRelAddr
= (UINT32)((pOldInst + hs.len + (INT32)hs.disp.disp32) - (pNewInst + hs.len));
// Complete the function if JMP (FF /4).
if (hs.opcode == 0xFF && hs.modrm_reg == 4)
finished = TRUE;
}
#endif
else if (hs.opcode == 0xE8)
{
// Direct relative CALL
ULONG_PTR dest = pOldInst + hs.len + (INT32)hs.imm.imm32;
#if defined(_M_X64) || defined(__x86_64__)
call.address = dest;
#else
call.operand = (UINT32)(dest - (pNewInst + sizeof(call)));
#endif
pCopySrc = &call;
copySize = sizeof(call);
}
else if ((hs.opcode & 0xFD) == 0xE9)
{
// Direct relative JMP (EB or E9)
ULONG_PTR dest = pOldInst + hs.len;
if (hs.opcode == 0xEB) // isShort jmp
dest += (INT8)hs.imm.imm8;
else
dest += (INT32)hs.imm.imm32;
// Simply copy an internal jump.
if ((ULONG_PTR)ct->pTarget <= dest
&& dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL)))
{
if (jmpDest < dest)
jmpDest = dest;
}
else
{
#if defined(_M_X64) || defined(__x86_64__)
jmp.address = dest;
#else
jmp.operand = (UINT32)(dest - (pNewInst + sizeof(jmp)));
#endif
pCopySrc = &jmp;
copySize = sizeof(jmp);
// Exit the function If it is not in the branch
finished = (pOldInst >= jmpDest);
}
}
else if ((hs.opcode & 0xF0) == 0x70
|| (hs.opcode & 0xFC) == 0xE0
|| (hs.opcode2 & 0xF0) == 0x80)
{
// Direct relative Jcc
ULONG_PTR dest = pOldInst + hs.len;
if ((hs.opcode & 0xF0) == 0x70 // Jcc
|| (hs.opcode & 0xFC) == 0xE0) // LOOPNZ/LOOPZ/LOOP/JECXZ
dest += (INT8)hs.imm.imm8;
else
dest += (INT32)hs.imm.imm32;
// Simply copy an internal jump.
if ((ULONG_PTR)ct->pTarget <= dest
&& dest < ((ULONG_PTR)ct->pTarget + sizeof(JMP_REL)))
{
if (jmpDest < dest)
jmpDest = dest;
}
else if ((hs.opcode & 0xFC) == 0xE0)
{
// LOOPNZ/LOOPZ/LOOP/JCXZ/JECXZ to the outside are not supported.
return FALSE;
}
else
{
UINT8 cond = ((hs.opcode != 0x0F ? hs.opcode : hs.opcode2) & 0x0F);
#if defined(_M_X64) || defined(__x86_64__)
// Invert the condition in x64 mode to simplify the conditional jump logic.
jcc.opcode = 0x71 ^ cond;
jcc.address = dest;
#else
jcc.opcode1 = 0x80 | cond;
jcc.operand = (UINT32)(dest - (pNewInst + sizeof(jcc)));
#endif
pCopySrc = &jcc;
copySize = sizeof(jcc);
}
}
else if ((hs.opcode & 0xFE) == 0xC2)
{
// RET (C2 or C3)
// Complete the function if not in a branch.
finished = (pOldInst >= jmpDest);
}
// Can't alter the instruction length in a branch.
if (pOldInst < jmpDest && copySize != hs.len)
return FALSE;
// Trampoline function is too large.
if ((newPos + copySize) > TRAMPOLINE_MAX_SIZE)
return FALSE;
// Trampoline function has too many instructions.
if (ct->nIP >= ARRAYSIZE(ct->oldIPs))
return FALSE;
ct->oldIPs[ct->nIP] = oldPos;
ct->newIPs[ct->nIP] = newPos;
ct->nIP++;
// Avoid using memcpy to reduce the footprint.
#ifndef _MSC_VER
memcpy((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize);
#else
__movsb((LPBYTE)ct->pTrampoline + newPos, pCopySrc, copySize);
#endif
newPos += copySize;
oldPos += hs.len;
}
while (!finished);
// Is there enough place for a long jump?
if (oldPos < sizeof(JMP_REL)
&& !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL) - oldPos))
{
// Is there enough place for a short jump?
if (oldPos < sizeof(JMP_REL_SHORT)
&& !IsCodePadding((LPBYTE)ct->pTarget + oldPos, sizeof(JMP_REL_SHORT) - oldPos))
{
return FALSE;
}
// Can we place the long jump above the function?
if (!IsExecutableAddress((LPBYTE)ct->pTarget - sizeof(JMP_REL)))
return FALSE;
if (!IsCodePadding((LPBYTE)ct->pTarget - sizeof(JMP_REL), sizeof(JMP_REL)))
return FALSE;
ct->patchAbove = TRUE;
}
#if defined(_M_X64) || defined(__x86_64__)
// Create a relay function.
jmp.address = (ULONG_PTR)ct->pDetour;
ct->pRelay = (LPBYTE)ct->pTrampoline + newPos;
memcpy(ct->pRelay, &jmp, sizeof(jmp));
#endif
return TRUE;
}
================================================
FILE: SylantStrike/minhook/src/trampoline.h
================================================
/*
* MinHook - The Minimalistic API Hooking Library for x64/x86
* Copyright (C) 2009-2017 Tsuda Kageyu.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#pragma pack(push, 1)
// Structs for writing x86/x64 instructions.
// 8-bit relative jump.
typedef struct _JMP_REL_SHORT
{
UINT8 opcode; // EB xx: JMP +2+xx
UINT8 operand;
} JMP_REL_SHORT, *PJMP_REL_SHORT;
// 32-bit direct relative jump/call.
typedef struct _JMP_REL
{
UINT8 opcode; // E9/E8 xxxxxxxx: JMP/CALL +5+xxxxxxxx
UINT32 operand; // Relative destination address
} JMP_REL, *PJMP_REL, CALL_REL;
// 64-bit indirect absolute jump.
typedef struct _JMP_ABS
{
UINT8 opcode0; // FF25 00000000: JMP [+6]
UINT8 opcode1;
UINT32 dummy;
UINT64 address; // Absolute destination address
} JMP_ABS, *PJMP_ABS;
// 64-bit indirect absolute call.
typedef struct _CALL_ABS
{
UINT8 opcode0; // FF15 00000002: CALL [+6]
UINT8 opcode1;
UINT32 dummy0;
UINT8 dummy1; // EB 08: JMP +10
UINT8 dummy2;
UINT64 address; // Absolute destination address
} CALL_ABS;
// 32-bit direct relative conditional jumps.
typedef struct _JCC_REL
{
UINT8 opcode0; // 0F8* xxxxxxxx: J** +6+xxxxxxxx
UINT8 opcode1;
UINT32 operand; // Relative destination address
} JCC_REL;
// 64bit indirect absolute conditional jumps that x64 lacks.
typedef struct _JCC_ABS
{
UINT8 opcode; // 7* 0E: J** +16
UINT8 dummy0;
UINT8 dummy1; // FF25 00000000: JMP [+6]
UINT8 dummy2;
UINT32 dummy3;
UINT64 address; // Absolute destination address
} JCC_ABS;
#pragma pack(pop)
typedef struct _TRAMPOLINE
{
LPVOID pTarget; // [In] Address of the target function.
LPVOID pDetour; // [In] Address of the detour function.
LPVOID pTrampoline; // [In] Buffer address for the trampoline and relay function.
#if defined(_M_X64) || defined(__x86_64__)
LPVOID pRelay; // [Out] Address of the relay function.
#endif
BOOL patchAbove; // [Out] Should use the hot patch area?
UINT nIP; // [Out] Number of the instruction boundaries.
UINT8 oldIPs[8]; // [Out] Instruction boundaries of the target function.
UINT8 newIPs[8]; // [Out] Instruction boundaries of the trampoline function.
} TRAMPOLINE, *PTRAMPOLINE;
BOOL CreateTrampolineFunction(PTRAMPOLINE ct);
================================================
FILE: SylantStrike/pch.cpp
================================================
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"
// When you are using pre-compiled headers, this source file is necessary for compilation to succeed.
================================================
FILE: SylantStrike/pch.h
================================================
// pch.h: This is a precompiled header file.
// Files listed below are compiled only once, improving build performance for future builds.
// This also affects IntelliSense performance, including code completion and many code browsing features.
// However, files listed here are ALL re-compiled if any one of them is updated between builds.
// Do not add files here that you will be updating frequently as this negates the performance advantage.
#ifndef PCH_H
#define PCH_H
// add headers that you want to pre-compile here
#include "framework.h"
#endif //PCH_H
================================================
FILE: SylantStrike.sln
================================================
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SylantStrike", "SylantStrike\SylantStrike.vcxproj", "{1DDD15AA-D837-4143-A272-0E08F9ED6C40}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SylantStrikeInject", "SylantStrikeInject\SylantStrikeInject.csproj", "{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Debug|Any CPU.ActiveCfg = Debug|Win32
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Debug|x64.ActiveCfg = Debug|x64
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Debug|x64.Build.0 = Debug|x64
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Debug|x86.ActiveCfg = Debug|Win32
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Debug|x86.Build.0 = Debug|Win32
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Release|Any CPU.ActiveCfg = Release|Win32
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Release|x64.ActiveCfg = Release|x64
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Release|x64.Build.0 = Release|x64
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Release|x86.ActiveCfg = Release|Win32
{1DDD15AA-D837-4143-A272-0E08F9ED6C40}.Release|x86.Build.0 = Release|Win32
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Debug|x64.ActiveCfg = Debug|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Debug|x64.Build.0 = Debug|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Debug|x86.ActiveCfg = Debug|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Debug|x86.Build.0 = Debug|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Release|Any CPU.Build.0 = Release|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Release|x64.ActiveCfg = Release|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Release|x64.Build.0 = Release|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Release|x86.ActiveCfg = Release|Any CPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C38993BA-BB45-4062-BD3E-AC155D4A3C84}
EndGlobalSection
EndGlobal
================================================
FILE: SylantStrikeInject/App.config
================================================
================================================
FILE: SylantStrikeInject/BasicInject.cs
================================================
//Based on code from https://codingvision.net/security/c-inject-a-dll-into-a-process-w-createremotethread
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
public class BasicInject {
[DllImport("kernel32.dll")]
public static extern IntPtr OpenProcess(int dwDesiredAccess, bool bInheritHandle, int dwProcessId);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr GetModuleHandle(string lpModuleName);
[DllImport("kernel32", CharSet = CharSet.Ansi, ExactSpelling = true, SetLastError = true)]
static extern IntPtr GetProcAddress(IntPtr hModule, string procName);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern IntPtr VirtualAllocEx(IntPtr hProcess, IntPtr lpAddress,
uint dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, byte[] lpBuffer, uint nSize, out UIntPtr lpNumberOfBytesWritten);
[DllImport("kernel32.dll")]
static extern IntPtr CreateRemoteThread(IntPtr hProcess,
IntPtr lpThreadAttributes, uint dwStackSize, IntPtr lpStartAddress, IntPtr lpParameter, uint dwCreationFlags, IntPtr lpThreadId);
// privileges
const int PROCESS_CREATE_THREAD = 0x0002;
const int PROCESS_QUERY_INFORMATION = 0x0400;
const int PROCESS_VM_OPERATION = 0x0008;
const int PROCESS_VM_WRITE = 0x0020;
const int PROCESS_VM_READ = 0x0010;
// used for memory allocation
const uint MEM_COMMIT = 0x00001000;
const uint MEM_RESERVE = 0x00002000;
const uint PAGE_READWRITE = 4;
public static int Inject(int pid, string dllName) {
// geting the handle of the process - with required privileges
IntPtr procHandle = OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, false, pid);
// searching for the address of LoadLibraryA and storing it in a pointer
IntPtr loadLibraryAddr = GetProcAddress(GetModuleHandle("kernel32.dll"), "LoadLibraryA");
// alocating some memory on the target process - enough to store the name of the dll
// and storing its address in a pointer
IntPtr allocMemAddress = VirtualAllocEx(procHandle, IntPtr.Zero, (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
// writing the name of the dll there
UIntPtr bytesWritten;
WriteProcessMemory(procHandle, allocMemAddress, Encoding.ASCII.GetBytes(dllName), (uint)((dllName.Length + 1) * Marshal.SizeOf(typeof(char))), out bytesWritten);
// creating a thread that will call LoadLibraryA with allocMemAddress as argument
CreateRemoteThread(procHandle, IntPtr.Zero, 0, loadLibraryAddr, allocMemAddress, 0, IntPtr.Zero);
return 0;
}
}
================================================
FILE: SylantStrikeInject/Options.cs
================================================
//
// Options.cs
//
// Authors:
// Jonathan Pryor ,
// Federico Di Gregorio
// Rolf Bjarne Kvinge
//
// Copyright (C) 2008 Novell (http://www.novell.com)
// Copyright (C) 2009 Federico Di Gregorio.
// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.com)
//
// 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.
//
// Compile With:
// mcs -debug+ -r:System.Core Options.cs -o:Mono.Options.dll -t:library
// mcs -debug+ -d:LINQ -r:System.Core Options.cs -o:Mono.Options.dll -t:library
//
// The LINQ version just changes the implementation of
// OptionSet.Parse(IEnumerable), and confers no semantic changes.
//
// A Getopt::Long-inspired option parsing library for C#.
//
// Mono.Options.OptionSet is built upon a key/value table, where the
// key is a option format string and the value is a delegate that is
// invoked when the format string is matched.
//
// Option format strings:
// Regex-like BNF Grammar:
// name: .+
// type: [=:]
// sep: ( [^{}]+ | '{' .+ '}' )?
// aliases: ( name type sep ) ( '|' name type sep )*
//
// Each '|'-delimited name is an alias for the associated action. If the
// format string ends in a '=', it has a required value. If the format
// string ends in a ':', it has an optional value. If neither '=' or ':'
// is present, no value is supported. `=' or `:' need only be defined on one
// alias, but if they are provided on more than one they must be consistent.
//
// Each alias portion may also end with a "key/value separator", which is used
// to split option values if the option accepts > 1 value. If not specified,
// it defaults to '=' and ':'. If specified, it can be any character except
// '{' and '}' OR the *string* between '{' and '}'. If no separator should be
// used (i.e. the separate values should be distinct arguments), then "{}"
// should be used as the separator.
//
// Options are extracted either from the current option by looking for
// the option name followed by an '=' or ':', or is taken from the
// following option IFF:
// - The current option does not contain a '=' or a ':'
// - The current option requires a value (i.e. not a Option type of ':')
//
// The `name' used in the option format string does NOT include any leading
// option indicator, such as '-', '--', or '/'. All three of these are
// permitted/required on any named option.
//
// Option bundling is permitted so long as:
// - '-' is used to start the option group
// - all of the bundled options are a single character
// - at most one of the bundled options accepts a value, and the value
// provided starts from the next character to the end of the string.
//
// This allows specifying '-a -b -c' as '-abc', and specifying '-D name=value'
// as '-Dname=value'.
//
// Option processing is disabled by specifying "--". All options after "--"
// are returned by OptionSet.Parse() unchanged and unprocessed.
//
// Unprocessed options are returned from OptionSet.Parse().
//
// Examples:
// int verbose = 0;
// OptionSet p = new OptionSet ()
// .Add ("v", v => ++verbose)
// .Add ("name=|value=", v => Console.WriteLine (v));
// p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"});
//
// The above would parse the argument string array, and would invoke the
// lambda expression three times, setting `verbose' to 3 when complete.
// It would also print out "A" and "B" to standard output.
// The returned array would contain the string "extra".
//
// C# 3.0 collection initializers are supported and encouraged:
// var p = new OptionSet () {
// { "h|?|help", v => ShowHelp () },
// };
//
// System.ComponentModel.TypeConverter is also supported, allowing the use of
// custom data types in the callback type; TypeConverter.ConvertFromString()
// is used to convert the value option to an instance of the specified
// type:
//
// var p = new OptionSet () {
// { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) },
// };
//
// Random other tidbits:
// - Boolean options (those w/o '=' or ':' in the option format string)
// are explicitly enabled if they are followed with '+', and explicitly
// disabled if they are followed with '-':
// string a = null;
// var p = new OptionSet () {
// { "a", s => a = s },
// };
// p.Parse (new string[]{"-a"}); // sets v != null
// p.Parse (new string[]{"-a+"}); // sets v != null
// p.Parse (new string[]{"-a-"}); // sets v == null
//
//
// Mono.Options.CommandSet allows easily having separate commands and
// associated command options, allowing creation of a *suite* along the
// lines of **git**(1), **svn**(1), etc.
//
// CommandSet allows intermixing plain text strings for `--help` output,
// Option values -- as supported by OptionSet -- and Command instances,
// which have a name, optional help text, and an optional OptionSet.
//
// var suite = new CommandSet ("suite-name") {
// // Use strings and option values, as with OptionSet
// "usage: suite-name COMMAND [OPTIONS]+",
// { "v:", "verbosity", (int? v) => Verbosity = v.HasValue ? v.Value : Verbosity+1 },
// // Commands may also be specified
// new Command ("command-name", "command help") {
// Options = new OptionSet {/*...*/},
// Run = args => { /*...*/},
// },
// new MyCommandSubclass (),
// };
// return suite.Run (new string[]{...});
//
// CommandSet provides a `help` command, and forwards `help COMMAND`
// to the registered Command instance by invoking Command.Invoke()
// with `--help` as an option.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.IO;
#if PCL
using System.Reflection;
#else
using System.Runtime.Serialization;
using System.Security.Permissions;
#endif
using System.Text;
using System.Text.RegularExpressions;
#if LINQ
using System.Linq;
#endif
#if TEST
using NDesk.Options;
#endif
#if PCL
using MessageLocalizerConverter = System.Func;
#else
using MessageLocalizerConverter = System.Converter;
#endif
#if NDESK_OPTIONS
namespace NDesk.Options
#else
namespace Mono.Options
#endif
{
static class StringCoda {
public static IEnumerable WrappedLines(string self, params int[] widths) {
IEnumerable w = widths;
return WrappedLines(self, w);
}
public static IEnumerable WrappedLines(string self, IEnumerable widths) {
if (widths == null)
throw new ArgumentNullException("widths");
return CreateWrappedLinesIterator(self, widths);
}
private static IEnumerable CreateWrappedLinesIterator(string self, IEnumerable widths) {
if (string.IsNullOrEmpty(self)) {
yield return string.Empty;
yield break;
}
using (IEnumerator ewidths = widths.GetEnumerator()) {
bool? hw = null;
int width = GetNextWidth(ewidths, int.MaxValue, ref hw);
int start = 0, end;
do {
end = GetLineEnd(start, width, self);
// endCorrection is 1 if the line end is '\n', and might be 2 if the line end is '\r\n'.
int endCorrection = 1;
if (end >= 2 && self.Substring(end - 2, 2).Equals("\r\n"))
endCorrection = 2;
char c = self[end - endCorrection];
if (char.IsWhiteSpace(c))
end -= endCorrection;
bool needContinuation = end != self.Length && !IsEolChar(c);
string continuation = "";
if (needContinuation) {
--end;
continuation = "-";
}
string line = self.Substring(start, end - start) + continuation;
yield return line;
start = end;
if (char.IsWhiteSpace(c))
start += endCorrection;
width = GetNextWidth(ewidths, width, ref hw);
} while (start < self.Length);
}
}
private static int GetNextWidth(IEnumerator ewidths, int curWidth, ref bool? eValid) {
if (!eValid.HasValue || (eValid.HasValue && eValid.Value)) {
curWidth = (eValid = ewidths.MoveNext()).Value ? ewidths.Current : curWidth;
// '.' is any character, - is for a continuation
const string minWidth = ".-";
if (curWidth < minWidth.Length)
throw new ArgumentOutOfRangeException("widths",
string.Format("Element must be >= {0}, was {1}.", minWidth.Length, curWidth));
return curWidth;
}
// no more elements, use the last element.
return curWidth;
}
private static bool IsEolChar(char c) {
return !char.IsLetterOrDigit(c);
}
private static int GetLineEnd(int start, int length, string description) {
int end = System.Math.Min(start + length, description.Length);
int sep = -1;
for (int i = start; i < end; ++i) {
if (i + 2 <= description.Length && description.Substring(i, 2).Equals("\r\n"))
return i + 2;
if (description[i] == '\n')
return i + 1;
if (IsEolChar(description[i]))
sep = i + 1;
}
if (sep == -1 || end == description.Length)
return end;
return sep;
}
}
public class OptionValueCollection : IList, IList {
List values = new List();
OptionContext c;
internal OptionValueCollection(OptionContext c) {
this.c = c;
}
#region ICollection
void ICollection.CopyTo(Array array, int index) { (values as ICollection).CopyTo(array, index); }
bool ICollection.IsSynchronized { get { return (values as ICollection).IsSynchronized; } }
object ICollection.SyncRoot { get { return (values as ICollection).SyncRoot; } }
#endregion
#region ICollection
public void Add(string item) { values.Add(item); }
public void Clear() { values.Clear(); }
public bool Contains(string item) { return values.Contains(item); }
public void CopyTo(string[] array, int arrayIndex) { values.CopyTo(array, arrayIndex); }
public bool Remove(string item) { return values.Remove(item); }
public int Count { get { return values.Count; } }
public bool IsReadOnly { get { return false; } }
#endregion
#region IEnumerable
IEnumerator IEnumerable.GetEnumerator() { return values.GetEnumerator(); }
#endregion
#region IEnumerable
public IEnumerator GetEnumerator() { return values.GetEnumerator(); }
#endregion
#region IList
int IList.Add(object value) { return (values as IList).Add(value); }
bool IList.Contains(object value) { return (values as IList).Contains(value); }
int IList.IndexOf(object value) { return (values as IList).IndexOf(value); }
void IList.Insert(int index, object value) { (values as IList).Insert(index, value); }
void IList.Remove(object value) { (values as IList).Remove(value); }
void IList.RemoveAt(int index) { (values as IList).RemoveAt(index); }
bool IList.IsFixedSize { get { return false; } }
object IList.this[int index] { get { return this[index]; } set { (values as IList)[index] = value; } }
#endregion
#region IList
public int IndexOf(string item) { return values.IndexOf(item); }
public void Insert(int index, string item) { values.Insert(index, item); }
public void RemoveAt(int index) { values.RemoveAt(index); }
private void AssertValid(int index) {
if (c.Option == null)
throw new InvalidOperationException("OptionContext.Option is null.");
if (index >= c.Option.MaxValueCount)
throw new ArgumentOutOfRangeException("index");
if (c.Option.OptionValueType == OptionValueType.Required &&
index >= values.Count)
throw new OptionException(string.Format(
c.OptionSet.MessageLocalizer("Missing required value for option '{0}'."), c.OptionName),
c.OptionName);
}
public string this[int index] {
get {
AssertValid(index);
return index >= values.Count ? null : values[index];
}
set {
values[index] = value;
}
}
#endregion
public List ToList() {
return new List(values);
}
public string[] ToArray() {
return values.ToArray();
}
public override string ToString() {
return string.Join(", ", values.ToArray());
}
}
public class OptionContext {
private Option option;
private string name;
private int index;
private OptionSet set;
private OptionValueCollection c;
public OptionContext(OptionSet set) {
this.set = set;
this.c = new OptionValueCollection(this);
}
public Option Option {
get { return option; }
set { option = value; }
}
public string OptionName {
get { return name; }
set { name = value; }
}
public int OptionIndex {
get { return index; }
set { index = value; }
}
public OptionSet OptionSet {
get { return set; }
}
public OptionValueCollection OptionValues {
get { return c; }
}
}
public enum OptionValueType {
None,
Optional,
Required,
}
public abstract class Option {
string prototype, description;
string[] names;
OptionValueType type;
int count;
string[] separators;
bool hidden;
protected Option(string prototype, string description)
: this(prototype, description, 1, false) {
}
protected Option(string prototype, string description, int maxValueCount)
: this(prototype, description, maxValueCount, false) {
}
protected Option(string prototype, string description, int maxValueCount, bool hidden) {
if (prototype == null)
throw new ArgumentNullException("prototype");
if (prototype.Length == 0)
throw new ArgumentException("Cannot be the empty string.", "prototype");
if (maxValueCount < 0)
throw new ArgumentOutOfRangeException("maxValueCount");
this.prototype = prototype;
this.description = description;
this.count = maxValueCount;
this.names = (this is OptionSet.Category)
// append GetHashCode() so that "duplicate" categories have distinct
// names, e.g. adding multiple "" categories should be valid.
? new[] { prototype + this.GetHashCode() }
: prototype.Split('|');
if (this is OptionSet.Category || this is CommandOption)
return;
this.type = ParsePrototype();
this.hidden = hidden;
if (this.count == 0 && type != OptionValueType.None)
throw new ArgumentException(
"Cannot provide maxValueCount of 0 for OptionValueType.Required or " +
"OptionValueType.Optional.",
"maxValueCount");
if (this.type == OptionValueType.None && maxValueCount > 1)
throw new ArgumentException(
string.Format("Cannot provide maxValueCount of {0} for OptionValueType.None.", maxValueCount),
"maxValueCount");
if (Array.IndexOf(names, "<>") >= 0 &&
((names.Length == 1 && this.type != OptionValueType.None) ||
(names.Length > 1 && this.MaxValueCount > 1)))
throw new ArgumentException(
"The default option handler '<>' cannot require values.",
"prototype");
}
public string Prototype { get { return prototype; } }
public string Description { get { return description; } }
public OptionValueType OptionValueType { get { return type; } }
public int MaxValueCount { get { return count; } }
public bool Hidden { get { return hidden; } }
public string[] GetNames() {
return (string[])names.Clone();
}
public string[] GetValueSeparators() {
if (separators == null)
return new string[0];
return (string[])separators.Clone();
}
protected static T Parse(string value, OptionContext c) {
Type tt = typeof(T);
#if PCL
TypeInfo ti = tt.GetTypeInfo ();
#else
Type ti = tt;
#endif
bool nullable =
ti.IsValueType &&
ti.IsGenericType &&
!ti.IsGenericTypeDefinition &&
ti.GetGenericTypeDefinition() == typeof(Nullable<>);
#if PCL
Type targetType = nullable ? tt.GenericTypeArguments [0] : tt;
#else
Type targetType = nullable ? tt.GetGenericArguments()[0] : tt;
#endif
T t = default(T);
try {
if (value != null) {
#if PCL
if (targetType.GetTypeInfo ().IsEnum)
t = (T) Enum.Parse (targetType, value, true);
else
t = (T) Convert.ChangeType (value, targetType);
#else
TypeConverter conv = TypeDescriptor.GetConverter(targetType);
t = (T)conv.ConvertFromString(value);
#endif
}
} catch (Exception e) {
throw new OptionException(
string.Format(
c.OptionSet.MessageLocalizer("Could not convert string `{0}' to type {1} for option `{2}'."),
value, targetType.Name, c.OptionName),
c.OptionName, e);
}
return t;
}
internal string[] Names { get { return names; } }
internal string[] ValueSeparators { get { return separators; } }
static readonly char[] NameTerminator = new char[] { '=', ':' };
private OptionValueType ParsePrototype() {
char type = '\0';
List seps = new List();
for (int i = 0; i < names.Length; ++i) {
string name = names[i];
if (name.Length == 0)
throw new ArgumentException("Empty option names are not supported.", "prototype");
int end = name.IndexOfAny(NameTerminator);
if (end == -1)
continue;
names[i] = name.Substring(0, end);
if (type == '\0' || type == name[end])
type = name[end];
else
throw new ArgumentException(
string.Format("Conflicting option types: '{0}' vs. '{1}'.", type, name[end]),
"prototype");
AddSeparators(name, end, seps);
}
if (type == '\0')
return OptionValueType.None;
if (count <= 1 && seps.Count != 0)
throw new ArgumentException(
string.Format("Cannot provide key/value separators for Options taking {0} value(s).", count),
"prototype");
if (count > 1) {
if (seps.Count == 0)
this.separators = new string[] { ":", "=" };
else if (seps.Count == 1 && seps[0].Length == 0)
this.separators = null;
else
this.separators = seps.ToArray();
}
return type == '=' ? OptionValueType.Required : OptionValueType.Optional;
}
private static void AddSeparators(string name, int end, ICollection seps) {
int start = -1;
for (int i = end + 1; i < name.Length; ++i) {
switch (name[i]) {
case '{':
if (start != -1)
throw new ArgumentException(
string.Format("Ill-formed name/value separator found in \"{0}\".", name),
"prototype");
start = i + 1;
break;
case '}':
if (start == -1)
throw new ArgumentException(
string.Format("Ill-formed name/value separator found in \"{0}\".", name),
"prototype");
seps.Add(name.Substring(start, i - start));
start = -1;
break;
default:
if (start == -1)
seps.Add(name[i].ToString());
break;
}
}
if (start != -1)
throw new ArgumentException(
string.Format("Ill-formed name/value separator found in \"{0}\".", name),
"prototype");
}
public void Invoke(OptionContext c) {
OnParseComplete(c);
c.OptionName = null;
c.Option = null;
c.OptionValues.Clear();
}
protected abstract void OnParseComplete(OptionContext c);
internal void InvokeOnParseComplete(OptionContext c) {
OnParseComplete(c);
}
public override string ToString() {
return Prototype;
}
}
public abstract class ArgumentSource {
protected ArgumentSource() {
}
public abstract string[] GetNames();
public abstract string Description { get; }
public abstract bool GetArguments(string value, out IEnumerable replacement);
#if !PCL || NETSTANDARD1_3
public static IEnumerable GetArgumentsFromFile(string file) {
return GetArguments(File.OpenText(file), true);
}
#endif
public static IEnumerable GetArguments(TextReader reader) {
return GetArguments(reader, false);
}
// Cribbed from mcs/driver.cs:LoadArgs(string)
static IEnumerable GetArguments(TextReader reader, bool close) {
try {
StringBuilder arg = new StringBuilder();
string line;
while ((line = reader.ReadLine()) != null) {
int t = line.Length;
for (int i = 0; i < t; i++) {
char c = line[i];
if (c == '"' || c == '\'') {
char end = c;
for (i++; i < t; i++) {
c = line[i];
if (c == end)
break;
arg.Append(c);
}
} else if (c == ' ') {
if (arg.Length > 0) {
yield return arg.ToString();
arg.Length = 0;
}
} else
arg.Append(c);
}
if (arg.Length > 0) {
yield return arg.ToString();
arg.Length = 0;
}
}
} finally {
if (close)
reader.Dispose();
}
}
}
#if !PCL || NETSTANDARD1_3
internal class ResponseFileSource : ArgumentSource {
public override string[] GetNames() {
return new string[] { "@file" };
}
public override string Description {
get { return "Read response file for more options."; }
}
public override bool GetArguments(string value, out IEnumerable replacement) {
if (string.IsNullOrEmpty(value) || !value.StartsWith("@")) {
replacement = null;
return false;
}
replacement = ArgumentSource.GetArgumentsFromFile(value.Substring(1));
return true;
}
}
#endif
#if !PCL
[Serializable]
#endif
internal class OptionException : Exception {
private string option;
public OptionException() {
}
public OptionException(string message, string optionName)
: base(message) {
this.option = optionName;
}
public OptionException(string message, string optionName, Exception innerException)
: base(message, innerException) {
this.option = optionName;
}
#if !PCL
protected OptionException(SerializationInfo info, StreamingContext context)
: base(info, context) {
this.option = info.GetString("OptionName");
}
#endif
public string OptionName {
get { return this.option; }
}
#if !PCL
#pragma warning disable 618 // SecurityPermissionAttribute is obsolete
[SecurityPermission(SecurityAction.LinkDemand, SerializationFormatter = true)]
#pragma warning restore 618
public override void GetObjectData(SerializationInfo info, StreamingContext context) {
base.GetObjectData(info, context);
info.AddValue("OptionName", option);
}
#endif
}
public delegate void OptionAction(TKey key, TValue value);
public class OptionSet : KeyedCollection {
public OptionSet()
: this(null) {
}
public OptionSet(MessageLocalizerConverter localizer) {
this.roSources = new ReadOnlyCollection(sources);
this.localizer = localizer;
if (this.localizer == null) {
this.localizer = delegate (string f) {
return f;
};
}
}
MessageLocalizerConverter localizer;
public MessageLocalizerConverter MessageLocalizer {
get { return localizer; }
internal set { localizer = value; }
}
List sources = new List();
ReadOnlyCollection roSources;
public ReadOnlyCollection ArgumentSources {
get { return roSources; }
}
protected override string GetKeyForItem(Option item) {
if (item == null)
throw new ArgumentNullException("option");
if (item.Names != null && item.Names.Length > 0)
return item.Names[0];
// This should never happen, as it's invalid for Option to be
// constructed w/o any names.
throw new InvalidOperationException("Option has no names!");
}
[Obsolete("Use KeyedCollection.this[string]")]
protected Option GetOptionForName(string option) {
if (option == null)
throw new ArgumentNullException("option");
try {
return base[option];
} catch (KeyNotFoundException) {
return null;
}
}
protected override void InsertItem(int index, Option item) {
base.InsertItem(index, item);
AddImpl(item);
}
protected override void RemoveItem(int index) {
Option p = Items[index];
base.RemoveItem(index);
// KeyedCollection.RemoveItem() handles the 0th item
for (int i = 1; i < p.Names.Length; ++i) {
Dictionary.Remove(p.Names[i]);
}
}
protected override void SetItem(int index, Option item) {
base.SetItem(index, item);
AddImpl(item);
}
private void AddImpl(Option option) {
if (option == null)
throw new ArgumentNullException("option");
List added = new List(option.Names.Length);
try {
// KeyedCollection.InsertItem/SetItem handle the 0th name.
for (int i = 1; i < option.Names.Length; ++i) {
Dictionary.Add(option.Names[i], option);
added.Add(option.Names[i]);
}
} catch (Exception) {
foreach (string name in added)
Dictionary.Remove(name);
throw;
}
}
public OptionSet Add(string header) {
if (header == null)
throw new ArgumentNullException("header");
Add(new Category(header));
return this;
}
internal sealed class Category : Option {
// Prototype starts with '=' because this is an invalid prototype
// (see Option.ParsePrototype(), and thus it'll prevent Category
// instances from being accidentally used as normal options.
public Category(string description)
: base("=:Category:= " + description, description) {
}
protected override void OnParseComplete(OptionContext c) {
throw new NotSupportedException("Category.OnParseComplete should not be invoked.");
}
}
public new OptionSet Add(Option option) {
base.Add(option);
return this;
}
sealed class ActionOption : Option {
Action action;
public ActionOption(string prototype, string description, int count, Action action)
: this(prototype, description, count, action, false) {
}
public ActionOption(string prototype, string description, int count, Action action, bool hidden)
: base(prototype, description, count, hidden) {
if (action == null)
throw new ArgumentNullException("action");
this.action = action;
}
protected override void OnParseComplete(OptionContext c) {
action(c.OptionValues);
}
}
public OptionSet Add(string prototype, Action action) {
return Add(prototype, null, action);
}
public OptionSet Add(string prototype, string description, Action action) {
return Add(prototype, description, action, false);
}
public OptionSet Add(string prototype, string description, Action action, bool hidden) {
if (action == null)
throw new ArgumentNullException("action");
Option p = new ActionOption(prototype, description, 1,
delegate (OptionValueCollection v) { action(v[0]); }, hidden);
base.Add(p);
return this;
}
public OptionSet Add(string prototype, OptionAction action) {
return Add(prototype, null, action);
}
public OptionSet Add(string prototype, string description, OptionAction action) {
return Add(prototype, description, action, false);
}
public OptionSet Add(string prototype, string description, OptionAction action, bool hidden) {
if (action == null)
throw new ArgumentNullException("action");
Option p = new ActionOption(prototype, description, 2,
delegate (OptionValueCollection v) { action(v[0], v[1]); }, hidden);
base.Add(p);
return this;
}
sealed class ActionOption : Option {
Action action;
public ActionOption(string prototype, string description, Action action)
: base(prototype, description, 1) {
if (action == null)
throw new ArgumentNullException("action");
this.action = action;
}
protected override void OnParseComplete(OptionContext c) {
action(Parse(c.OptionValues[0], c));
}
}
sealed class ActionOption : Option {
OptionAction action;
public ActionOption(string prototype, string description, OptionAction action)
: base(prototype, description, 2) {
if (action == null)
throw new ArgumentNullException("action");
this.action = action;
}
protected override void OnParseComplete(OptionContext c) {
action(
Parse(c.OptionValues[0], c),
Parse(c.OptionValues[1], c));
}
}
public OptionSet Add(string prototype, Action action) {
return Add(prototype, null, action);
}
public OptionSet Add(string prototype, string description, Action action) {
return Add(new ActionOption(prototype, description, action));
}
public OptionSet Add(string prototype, OptionAction action) {
return Add(prototype, null, action);
}
public OptionSet Add(string prototype, string description, OptionAction action) {
return Add(new ActionOption(prototype, description, action));
}
public OptionSet Add(ArgumentSource source) {
if (source == null)
throw new ArgumentNullException("source");
sources.Add(source);
return this;
}
protected virtual OptionContext CreateOptionContext() {
return new OptionContext(this);
}
public List Parse(IEnumerable arguments) {
if (arguments == null)
throw new ArgumentNullException("arguments");
OptionContext c = CreateOptionContext();
c.OptionIndex = -1;
bool process = true;
List unprocessed = new List();
Option def = Contains("<>") ? this["<>"] : null;
ArgumentEnumerator ae = new ArgumentEnumerator(arguments);
foreach (string argument in ae) {
++c.OptionIndex;
if (argument == "--") {
process = false;
continue;
}
if (!process) {
Unprocessed(unprocessed, def, c, argument);
continue;
}
if (AddSource(ae, argument))
continue;
if (!Parse(argument, c))
Unprocessed(unprocessed, def, c, argument);
}
if (c.Option != null)
c.Option.Invoke(c);
return unprocessed;
}
class ArgumentEnumerator : IEnumerable {
List> sources = new List>();
public ArgumentEnumerator(IEnumerable arguments) {
sources.Add(arguments.GetEnumerator());
}
public void Add(IEnumerable arguments) {
sources.Add(arguments.GetEnumerator());
}
public IEnumerator GetEnumerator() {
do {
IEnumerator c = sources[sources.Count - 1];
if (c.MoveNext())
yield return c.Current;
else {
c.Dispose();
sources.RemoveAt(sources.Count - 1);
}
} while (sources.Count > 0);
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
bool AddSource(ArgumentEnumerator ae, string argument) {
foreach (ArgumentSource source in sources) {
IEnumerable replacement;
if (!source.GetArguments(argument, out replacement))
continue;
ae.Add(replacement);
return true;
}
return false;
}
private static bool Unprocessed(ICollection extra, Option def, OptionContext c, string argument) {
if (def == null) {
extra.Add(argument);
return false;
}
c.OptionValues.Add(argument);
c.Option = def;
c.Option.Invoke(c);
return false;
}
private readonly Regex ValueOption = new Regex(
@"^(?--|-|/)(?[^:=]+)((?[:=])(?.*))?$");
protected bool GetOptionParts(string argument, out string flag, out string name, out string sep, out string value) {
if (argument == null)
throw new ArgumentNullException("argument");
flag = name = sep = value = null;
Match m = ValueOption.Match(argument);
if (!m.Success) {
return false;
}
flag = m.Groups["flag"].Value;
name = m.Groups["name"].Value;
if (m.Groups["sep"].Success && m.Groups["value"].Success) {
sep = m.Groups["sep"].Value;
value = m.Groups["value"].Value;
}
return true;
}
protected virtual bool Parse(string argument, OptionContext c) {
if (c.Option != null) {
ParseValue(argument, c);
return true;
}
string f, n, s, v;
if (!GetOptionParts(argument, out f, out n, out s, out v))
return false;
Option p;
if (Contains(n)) {
p = this[n];
c.OptionName = f + n;
c.Option = p;
switch (p.OptionValueType) {
case OptionValueType.None:
c.OptionValues.Add(n);
c.Option.Invoke(c);
break;
case OptionValueType.Optional:
case OptionValueType.Required:
ParseValue(v, c);
break;
}
return true;
}
// no match; is it a bool option?
if (ParseBool(argument, n, c))
return true;
// is it a bundled option?
if (ParseBundledValue(f, string.Concat(n + s + v), c))
return true;
return false;
}
private void ParseValue(string option, OptionContext c) {
if (option != null)
foreach (string o in c.Option.ValueSeparators != null
? option.Split(c.Option.ValueSeparators, c.Option.MaxValueCount - c.OptionValues.Count, StringSplitOptions.None)
: new string[] { option }) {
c.OptionValues.Add(o);
}
if (c.OptionValues.Count == c.Option.MaxValueCount ||
c.Option.OptionValueType == OptionValueType.Optional)
c.Option.Invoke(c);
else if (c.OptionValues.Count > c.Option.MaxValueCount) {
throw new OptionException(localizer(string.Format(
"Error: Found {0} option values when expecting {1}.",
c.OptionValues.Count, c.Option.MaxValueCount)),
c.OptionName);
}
}
private bool ParseBool(string option, string n, OptionContext c) {
Option p;
string rn;
if (n.Length >= 1 && (n[n.Length - 1] == '+' || n[n.Length - 1] == '-') &&
Contains((rn = n.Substring(0, n.Length - 1)))) {
p = this[rn];
string v = n[n.Length - 1] == '+' ? option : null;
c.OptionName = option;
c.Option = p;
c.OptionValues.Add(v);
p.Invoke(c);
return true;
}
return false;
}
private bool ParseBundledValue(string f, string n, OptionContext c) {
if (f != "-")
return false;
for (int i = 0; i < n.Length; ++i) {
Option p;
string opt = f + n[i].ToString();
string rn = n[i].ToString();
if (!Contains(rn)) {
if (i == 0)
return false;
throw new OptionException(string.Format(localizer(
"Cannot use unregistered option '{0}' in bundle '{1}'."), rn, f + n), null);
}
p = this[rn];
switch (p.OptionValueType) {
case OptionValueType.None:
Invoke(c, opt, n, p);
break;
case OptionValueType.Optional:
case OptionValueType.Required: {
string v = n.Substring(i + 1);
c.Option = p;
c.OptionName = opt;
ParseValue(v.Length != 0 ? v : null, c);
return true;
}
default:
throw new InvalidOperationException("Unknown OptionValueType: " + p.OptionValueType);
}
}
return true;
}
private static void Invoke(OptionContext c, string name, string value, Option option) {
c.OptionName = name;
c.Option = option;
c.OptionValues.Add(value);
option.Invoke(c);
}
private const int OptionWidth = 29;
private const int Description_FirstWidth = 80 - OptionWidth;
private const int Description_RemWidth = 80 - OptionWidth - 2;
static readonly string CommandHelpIndentStart = new string(' ', OptionWidth);
static readonly string CommandHelpIndentRemaining = new string(' ', OptionWidth + 2);
public void WriteOptionDescriptions(TextWriter o) {
foreach (Option p in this) {
int written = 0;
if (p.Hidden)
continue;
Category c = p as Category;
if (c != null) {
WriteDescription(o, p.Description, "", 80, 80);
continue;
}
CommandOption co = p as CommandOption;
if (co != null) {
WriteCommandDescription(o, co.Command, co.CommandName);
continue;
}
if (!WriteOptionPrototype(o, p, ref written))
continue;
if (written < OptionWidth)
o.Write(new string(' ', OptionWidth - written));
else {
o.WriteLine();
o.Write(new string(' ', OptionWidth));
}
WriteDescription(o, p.Description, new string(' ', OptionWidth + 2),
Description_FirstWidth, Description_RemWidth);
}
foreach (ArgumentSource s in sources) {
string[] names = s.GetNames();
if (names == null || names.Length == 0)
continue;
int written = 0;
Write(o, ref written, " ");
Write(o, ref written, names[0]);
for (int i = 1; i < names.Length; ++i) {
Write(o, ref written, ", ");
Write(o, ref written, names[i]);
}
if (written < OptionWidth)
o.Write(new string(' ', OptionWidth - written));
else {
o.WriteLine();
o.Write(new string(' ', OptionWidth));
}
WriteDescription(o, s.Description, new string(' ', OptionWidth + 2),
Description_FirstWidth, Description_RemWidth);
}
}
internal void WriteCommandDescription(TextWriter o, Command c, string commandName) {
var name = new string(' ', 8) + (commandName ?? c.Name);
if (name.Length < OptionWidth - 1) {
WriteDescription(o, name + new string(' ', OptionWidth - name.Length) + c.Help, CommandHelpIndentRemaining, 80, Description_RemWidth);
} else {
WriteDescription(o, name, "", 80, 80);
WriteDescription(o, CommandHelpIndentStart + c.Help, CommandHelpIndentRemaining, 80, Description_RemWidth);
}
}
void WriteDescription(TextWriter o, string value, string prefix, int firstWidth, int remWidth) {
bool indent = false;
foreach (string line in GetLines(localizer(GetDescription(value)), firstWidth, remWidth)) {
if (indent)
o.Write(prefix);
o.WriteLine(line);
indent = true;
}
}
bool WriteOptionPrototype(TextWriter o, Option p, ref int written) {
string[] names = p.Names;
int i = GetNextOptionIndex(names, 0);
if (i == names.Length)
return false;
if (names[i].Length == 1) {
Write(o, ref written, " -");
Write(o, ref written, names[0]);
} else {
Write(o, ref written, " --");
Write(o, ref written, names[0]);
}
for (i = GetNextOptionIndex(names, i + 1);
i < names.Length; i = GetNextOptionIndex(names, i + 1)) {
Write(o, ref written, ", ");
Write(o, ref written, names[i].Length == 1 ? "-" : "--");
Write(o, ref written, names[i]);
}
if (p.OptionValueType == OptionValueType.Optional ||
p.OptionValueType == OptionValueType.Required) {
if (p.OptionValueType == OptionValueType.Optional) {
Write(o, ref written, localizer("["));
}
Write(o, ref written, localizer("=" + GetArgumentName(0, p.MaxValueCount, p.Description)));
string sep = p.ValueSeparators != null && p.ValueSeparators.Length > 0
? p.ValueSeparators[0]
: " ";
for (int c = 1; c < p.MaxValueCount; ++c) {
Write(o, ref written, localizer(sep + GetArgumentName(c, p.MaxValueCount, p.Description)));
}
if (p.OptionValueType == OptionValueType.Optional) {
Write(o, ref written, localizer("]"));
}
}
return true;
}
static int GetNextOptionIndex(string[] names, int i) {
while (i < names.Length && names[i] == "<>") {
++i;
}
return i;
}
static void Write(TextWriter o, ref int n, string s) {
n += s.Length;
o.Write(s);
}
static string GetArgumentName(int index, int maxIndex, string description) {
var matches = Regex.Matches(description ?? "", @"(?<=(? 1
if (maxIndex > 1 && parts.Length == 2 &&
parts[0] == index.ToString(CultureInfo.InvariantCulture)) {
argName = parts[1];
}
}
if (string.IsNullOrEmpty(argName)) {
argName = maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1);
}
return argName;
}
private static string GetDescription(string description) {
if (description == null)
return string.Empty;
StringBuilder sb = new StringBuilder(description.Length);
int start = -1;
for (int i = 0; i < description.Length; ++i) {
switch (description[i]) {
case '{':
if (i == start) {
sb.Append('{');
start = -1;
} else if (start < 0)
start = i + 1;
break;
case '}':
if (start < 0) {
if ((i + 1) == description.Length || description[i + 1] != '}')
throw new InvalidOperationException("Invalid option description: " + description);
++i;
sb.Append("}");
} else {
sb.Append(description.Substring(start, i - start));
start = -1;
}
break;
case ':':
if (start < 0)
goto default;
start = i + 1;
break;
default:
if (start < 0)
sb.Append(description[i]);
break;
}
}
return sb.ToString();
}
private static IEnumerable GetLines(string description, int firstWidth, int remWidth) {
return StringCoda.WrappedLines(description, firstWidth, remWidth);
}
}
internal class Command {
public string Name { get; }
public string Help { get; }
public OptionSet Options { get; set; }
public Action> Run { get; set; }
public CommandSet CommandSet { get; internal set; }
public Command(string name, string help = null) {
if (string.IsNullOrEmpty(name))
throw new ArgumentNullException(nameof(name));
Name = NormalizeCommandName(name);
Help = help;
}
static string NormalizeCommandName(string name) {
var value = new StringBuilder(name.Length);
var space = false;
for (int i = 0; i < name.Length; ++i) {
if (!char.IsWhiteSpace(name, i)) {
space = false;
value.Append(name[i]);
} else if (!space) {
space = true;
value.Append(' ');
}
}
return value.ToString();
}
public virtual int Invoke(IEnumerable arguments) {
var rest = Options?.Parse(arguments) ?? arguments;
Run?.Invoke(rest);
return 0;
}
}
class CommandOption : Option {
public Command Command { get; }
public string CommandName { get; }
// Prototype starts with '=' because this is an invalid prototype
// (see Option.ParsePrototype(), and thus it'll prevent Category
// instances from being accidentally used as normal options.
public CommandOption(Command command, string commandName = null, bool hidden = false)
: base("=:Command:= " + (commandName ?? command?.Name), (commandName ?? command?.Name), maxValueCount: 0, hidden: hidden) {
if (command == null)
throw new ArgumentNullException(nameof(command));
Command = command;
CommandName = commandName ?? command.Name;
}
protected override void OnParseComplete(OptionContext c) {
throw new NotSupportedException("CommandOption.OnParseComplete should not be invoked.");
}
}
class HelpOption : Option {
Option option;
CommandSet commands;
public HelpOption(CommandSet commands, Option d)
: base(d.Prototype, d.Description, d.MaxValueCount, d.Hidden) {
this.commands = commands;
this.option = d;
}
protected override void OnParseComplete(OptionContext c) {
commands.showHelp = true;
option?.InvokeOnParseComplete(c);
}
}
class CommandOptionSet : OptionSet {
CommandSet commands;
public CommandOptionSet(CommandSet commands, MessageLocalizerConverter localizer)
: base(localizer) {
this.commands = commands;
}
protected override void SetItem(int index, Option item) {
if (ShouldWrapOption(item)) {
base.SetItem(index, new HelpOption(commands, item));
return;
}
base.SetItem(index, item);
}
bool ShouldWrapOption(Option item) {
if (item == null)
return false;
var help = item as HelpOption;
if (help != null)
return false;
foreach (var n in item.Names) {
if (n == "help")
return true;
}
return false;
}
protected override void InsertItem(int index, Option item) {
if (ShouldWrapOption(item)) {
base.InsertItem(index, new HelpOption(commands, item));
return;
}
base.InsertItem(index, item);
}
}
internal class CommandSet : KeyedCollection {
readonly string suite;
OptionSet options;
TextWriter outWriter;
TextWriter errorWriter;
internal List NestedCommandSets;
internal HelpCommand help;
internal bool showHelp;
internal OptionSet Options => options;
#if !PCL || NETSTANDARD1_3
public CommandSet(string suite, MessageLocalizerConverter localizer = null)
: this(suite, Console.Out, Console.Error, localizer) {
}
#endif
public CommandSet(string suite, TextWriter output, TextWriter error, MessageLocalizerConverter localizer = null) {
if (suite == null)
throw new ArgumentNullException(nameof(suite));
if (output == null)
throw new ArgumentNullException(nameof(output));
if (error == null)
throw new ArgumentNullException(nameof(error));
this.suite = suite;
options = new CommandOptionSet(this, localizer);
outWriter = output;
errorWriter = error;
}
public string Suite => suite;
public TextWriter Out => outWriter;
public TextWriter Error => errorWriter;
public MessageLocalizerConverter MessageLocalizer => options.MessageLocalizer;
protected override string GetKeyForItem(Command item) {
return item?.Name;
}
public new CommandSet Add(Command value) {
if (value == null)
throw new ArgumentNullException(nameof(value));
AddCommand(value);
options.Add(new CommandOption(value));
return this;
}
void AddCommand(Command value) {
if (value.CommandSet != null && value.CommandSet != this) {
throw new ArgumentException("Command instances can only be added to a single CommandSet.", nameof(value));
}
value.CommandSet = this;
if (value.Options != null) {
value.Options.MessageLocalizer = options.MessageLocalizer;
}
base.Add(value);
help = help ?? value as HelpCommand;
}
public CommandSet Add(string header) {
options.Add(header);
return this;
}
public CommandSet Add(Option option) {
options.Add(option);
return this;
}
public CommandSet Add(string prototype, Action action) {
options.Add(prototype, action);
return this;
}
public CommandSet Add(string prototype, string description, Action action) {
options.Add(prototype, description, action);
return this;
}
public CommandSet Add(string prototype, string description, Action action, bool hidden) {
options.Add(prototype, description, action, hidden);
return this;
}
public CommandSet Add(string prototype, OptionAction action) {
options.Add(prototype, action);
return this;
}
public CommandSet Add(string prototype, string description, OptionAction action) {
options.Add(prototype, description, action);
return this;
}
public CommandSet Add(string prototype, string description, OptionAction action, bool hidden) {
options.Add(prototype, description, action, hidden);
return this;
}
public CommandSet Add(string prototype, Action action) {
options.Add(prototype, null, action);
return this;
}
public CommandSet Add(string prototype, string description, Action action) {
options.Add(prototype, description, action);
return this;
}
public CommandSet Add(string prototype, OptionAction action) {
options.Add(prototype, action);
return this;
}
public CommandSet Add(string prototype, string description, OptionAction action) {
options.Add(prototype, description, action);
return this;
}
public CommandSet Add(ArgumentSource source) {
options.Add(source);
return this;
}
public CommandSet Add(CommandSet nestedCommands) {
if (nestedCommands == null)
throw new ArgumentNullException(nameof(nestedCommands));
if (NestedCommandSets == null) {
NestedCommandSets = new List();
}
if (!AlreadyAdded(nestedCommands)) {
NestedCommandSets.Add(nestedCommands);
foreach (var o in nestedCommands.options) {
if (o is CommandOption c) {
options.Add(new CommandOption(c.Command, $"{nestedCommands.Suite} {c.CommandName}"));
} else {
options.Add(o);
}
}
}
nestedCommands.options = this.options;
nestedCommands.outWriter = this.outWriter;
nestedCommands.errorWriter = this.errorWriter;
return this;
}
bool AlreadyAdded(CommandSet value) {
if (value == this)
return true;
if (NestedCommandSets == null)
return false;
foreach (var nc in NestedCommandSets) {
if (nc.AlreadyAdded(value))
return true;
}
return false;
}
public IEnumerable GetCompletions(string prefix = null) {
string rest;
ExtractToken(ref prefix, out rest);
foreach (var command in this) {
if (command.Name.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) {
yield return command.Name;
}
}
if (NestedCommandSets == null)
yield break;
foreach (var subset in NestedCommandSets) {
if (subset.Suite.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) {
foreach (var c in subset.GetCompletions(rest)) {
yield return $"{subset.Suite} {c}";
}
}
}
}
static void ExtractToken(ref string input, out string rest) {
rest = "";
input = input ?? "";
int top = input.Length;
for (int i = 0; i < top; i++) {
if (char.IsWhiteSpace(input[i]))
continue;
for (int j = i; j < top; j++) {
if (char.IsWhiteSpace(input[j])) {
rest = input.Substring(j).Trim();
input = input.Substring(i, j).Trim();
return;
}
}
rest = "";
if (i != 0)
input = input.Substring(i).Trim();
return;
}
}
public int Run(IEnumerable arguments) {
if (arguments == null)
throw new ArgumentNullException(nameof(arguments));
this.showHelp = false;
if (help == null) {
help = new HelpCommand();
AddCommand(help);
}
Action setHelp = v => showHelp = v != null;
if (!options.Contains("help")) {
options.Add("help", "", setHelp, hidden: true);
}
if (!options.Contains("?")) {
options.Add("?", "", setHelp, hidden: true);
}
var extra = options.Parse(arguments);
if (extra.Count == 0) {
if (showHelp) {
return help.Invoke(extra);
}
Out.WriteLine(options.MessageLocalizer($"Use `{Suite} help` for usage."));
return 1;
}
var command = GetCommand(extra);
if (command == null) {
help.WriteUnknownCommand(extra[0]);
return 1;
}
if (showHelp) {
if (command.Options?.Contains("help") ?? true) {
extra.Add("--help");
return command.Invoke(extra);
}
command.Options.WriteOptionDescriptions(Out);
return 0;
}
return command.Invoke(extra);
}
internal Command GetCommand(List extra) {
return TryGetLocalCommand(extra) ?? TryGetNestedCommand(extra);
}
Command TryGetLocalCommand(List extra) {
var name = extra[0];
if (Contains(name)) {
extra.RemoveAt(0);
return this[name];
}
for (int i = 1; i < extra.Count; ++i) {
name = name + " " + extra[i];
if (!Contains(name))
continue;
extra.RemoveRange(0, i + 1);
return this[name];
}
return null;
}
Command TryGetNestedCommand(List extra) {
if (NestedCommandSets == null)
return null;
var nestedCommands = NestedCommandSets.Find(c => c.Suite == extra[0]);
if (nestedCommands == null)
return null;
var extraCopy = new List(extra);
extraCopy.RemoveAt(0);
if (extraCopy.Count == 0)
return null;
var command = nestedCommands.GetCommand(extraCopy);
if (command != null) {
extra.Clear();
extra.AddRange(extraCopy);
return command;
}
return null;
}
}
internal class HelpCommand : Command {
public HelpCommand()
: base("help", help: "Show this message and exit") {
}
public override int Invoke(IEnumerable arguments) {
var extra = new List(arguments ?? new string[0]);
var _ = CommandSet.Options.MessageLocalizer;
if (extra.Count == 0) {
CommandSet.Options.WriteOptionDescriptions(CommandSet.Out);
return 0;
}
var command = CommandSet.GetCommand(extra);
if (command == this || extra.Contains("--help")) {
CommandSet.Out.WriteLine(_($"Usage: {CommandSet.Suite} COMMAND [OPTIONS]"));
CommandSet.Out.WriteLine(_($"Use `{CommandSet.Suite} help COMMAND` for help on a specific command."));
CommandSet.Out.WriteLine();
CommandSet.Out.WriteLine(_($"Available commands:"));
CommandSet.Out.WriteLine();
var commands = GetCommands();
commands.Sort((x, y) => string.Compare(x.Key, y.Key, StringComparison.OrdinalIgnoreCase));
foreach (var c in commands) {
if (c.Key == "help") {
continue;
}
CommandSet.Options.WriteCommandDescription(CommandSet.Out, c.Value, c.Key);
}
CommandSet.Options.WriteCommandDescription(CommandSet.Out, CommandSet.help, "help");
return 0;
}
if (command == null) {
WriteUnknownCommand(extra[0]);
return 1;
}
if (command.Options != null) {
command.Options.WriteOptionDescriptions(CommandSet.Out);
return 0;
}
return command.Invoke(new[] { "--help" });
}
List> GetCommands() {
var commands = new List>();
foreach (var c in CommandSet) {
commands.Add(new KeyValuePair(c.Name, c));
}
if (CommandSet.NestedCommandSets == null)
return commands;
foreach (var nc in CommandSet.NestedCommandSets) {
AddNestedCommands(commands, "", nc);
}
return commands;
}
void AddNestedCommands(List> commands, string outer, CommandSet value) {
foreach (var v in value) {
commands.Add(new KeyValuePair($"{outer}{value.Suite} {v.Name}", v));
}
if (value.NestedCommandSets == null)
return;
foreach (var nc in value.NestedCommandSets) {
AddNestedCommands(commands, $"{outer}{value.Suite} ", nc);
}
}
internal void WriteUnknownCommand(string unknownCommand) {
CommandSet.Error.WriteLine(CommandSet.Options.MessageLocalizer($"{CommandSet.Suite}: Unknown command: {unknownCommand}"));
CommandSet.Error.WriteLine(CommandSet.Options.MessageLocalizer($"{CommandSet.Suite}: Use `{CommandSet.Suite} help` for usage."));
}
}
}
================================================
FILE: SylantStrikeInject/Program.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.Threading;
using Mono.Options;
namespace SylantStrikeInject {
class Program
{
private static List processList = new List();
private static string dllPath;
static void Main(string[] args)
{
RunThisAsAdmin(string.Join(" ", args));
OptionSet option_set = new OptionSet()
{
{"p=|process=", "Process name to protect with CylantStrike", v => { processList.Add(v.ToLower()); }},
{"d=|dll=", "Path to CylantStrike protection DLL", v => { dllPath = v; }}
};
try
{
option_set.Parse(args);
new Thread(WaitForProcess) {IsBackground = true, Name = "worker"}.Start();
Console.WriteLine("Waiting for process events");
do
{
Thread.Sleep(5000);
} while (true);
}
catch (Exception e)
{
Console.WriteLine($"Failed to setup injector ${e.Message}");
}
}
private static void RunThisAsAdmin(string args)
{
if (!IsAdministrator())
{
var exe = Process.GetCurrentProcess().MainModule.FileName;
var startInfo = new ProcessStartInfo(exe, args)
{
UseShellExecute = true,
Verb = "runas",
WindowStyle = ProcessWindowStyle.Normal,
CreateNoWindow = false,
};
Process.Start(startInfo);
Process.GetCurrentProcess().Kill();
}
}
private static bool IsAdministrator()
{
var identity = WindowsIdentity.GetCurrent();
var principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
static void WaitForProcess()
{
try
{
var startWatch = new ManagementEventWatcher(new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
startWatch.EventArrived += new EventArrivedEventHandler(startWatch_EventArrived);
startWatch.Start();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($"+ Listening for the following processes: {string.Join(" ", processList)}\n");
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(ex);
}
}
static void startWatch_EventArrived(object sender, EventArrivedEventArgs e)
{
try
{
var proc = GetProcessInfo(e);
if (processList.Contains(proc.ProcessName.ToLower()))
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($" Injecting process {proc.ProcessName}({proc.PID}) with DLL {dllPath}");
BasicInject.Inject(proc.PID, dllPath);
}
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine(ex);
}
}
static ProcessInfo GetProcessInfo(EventArrivedEventArgs e)
{
var p = new ProcessInfo();
var pid = 0;
int.TryParse(e.NewEvent.Properties["ProcessID"].Value.ToString(), out pid);
p.PID = pid;
p.ProcessName = e.NewEvent.Properties["ProcessName"].Value.ToString();
return p;
}
internal class ProcessInfo
{
public string ProcessName { get; set; }
public int PID { get; set; }
}
}
}
================================================
FILE: SylantStrikeInject/Properties/AssemblyInfo.cs
================================================
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CylantStrikeInject")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CylantStrikeInject")]
[assembly: AssemblyCopyright("Copyright © 2020")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("1accf54a-57a0-4dd3-bf6d-d06ac450947b")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
================================================
FILE: SylantStrikeInject/SylantStrikeInject.csproj
================================================
Debug
AnyCPU
{1ACCF54A-57A0-4DD3-BF6D-D06AC450947B}
Exe
SylantStrikeInject
SylantStrikeInject
v4.5
512
true
x64
true
full
false
..\x64\Debug\
DEBUG;TRACE
prompt
4
false
x64
pdbonly
true
..\x64\Release\
TRACE
prompt
4