diff --git a/bindings/Makefile b/bindings/Makefile index 136e5f6303db6f7e0ac98fee4d00afcfae9eef58..85799b6ae52305359a945baa6154b44ab01a98af 100644 --- a/bindings/Makefile +++ b/bindings/Makefile @@ -19,6 +19,7 @@ all: python const_generator.py rust python const_generator.py go cd ruby && $(MAKE) gen_const + python const_generator.py powershell samples: expected python diff --git a/bindings/README b/bindings/README index 3f48305da0ba91bf4362a5437d3ac61653128e30..2ec0135880ecb4774696e47c8167d17387f4adf3 100644 --- a/bindings/README +++ b/bindings/README @@ -9,6 +9,7 @@ Except Python, all other bindings are contributed by community. - Rust binding: by Remco Verhoef - Haskell binding: by Adrian Herrera - OCaml binding: by Aziem Chawdhary +- PowerShell binding: by Ruben Boonen Other bindings maintained externally by community: diff --git a/bindings/const_generator.py b/bindings/const_generator.py index af0d07ee44ababad91dbe63a00416b6d0ebc589f..4ee721a4ac11e0e8ceaed95b97e594fb73936478 100644 --- a/bindings/const_generator.py +++ b/bindings/const_generator.py @@ -15,6 +15,22 @@ def CamelCase(s): return ''.join(''.join([w[0].upper(), w[1:].lower()]) for w in s.split('_')) template = { + 'powershell': { + 'header': "/// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_h.cs]\n", + 'footer': "", + 'out_file': './powershell/Keystone/Const/%s_h.cs', + # prefixes for constant filenames of all archs - case sensitive + 'keystone.h': 'keystone', + 'comment_open': '///', + 'comment_close': '', + 'rules': [ + { + 'regex': r'.*', + 'line_format': 'KS_{0} = {1},\n', + 'fn': (lambda x: x), + }, + ] + }, 'rust': { 'header': "// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.rs]\nextern crate libc;\n\n", 'footer': "", diff --git a/bindings/powershell/Keystone/Const/keystone_h.cs b/bindings/powershell/Keystone/Const/keystone_h.cs new file mode 100755 index 0000000000000000000000000000000000000000..dcdcb0ea8fd1b37e9ec0d8dfb3c594d34af2f86c --- /dev/null +++ b/bindings/powershell/Keystone/Const/keystone_h.cs @@ -0,0 +1,89 @@ +KS_API_MAJOR = 0, +KS_API_MINOR = 9, +KS_VERSION_MAJOR = 0, +KS_VERSION_MINOR = 9, +KS_VERSION_EXTRA = 1, +KS_ARCH_ARM = 1, +KS_ARCH_ARM64 = 2, +KS_ARCH_MIPS = 3, +KS_ARCH_X86 = 4, +KS_ARCH_PPC = 5, +KS_ARCH_SPARC = 6, +KS_ARCH_SYSTEMZ = 7, +KS_ARCH_HEXAGON = 8, +KS_ARCH_MAX = 9, +KS_MODE_LITTLE_ENDIAN = 0, +KS_MODE_BIG_ENDIAN = 1073741824, +KS_MODE_ARM = 1, +KS_MODE_THUMB = 16, +KS_MODE_V8 = 64, +KS_MODE_MICRO = 16, +KS_MODE_MIPS3 = 32, +KS_MODE_MIPS32R6 = 64, +KS_MODE_MIPS32 = 4, +KS_MODE_MIPS64 = 8, +KS_MODE_16 = 2, +KS_MODE_32 = 4, +KS_MODE_64 = 8, +KS_MODE_PPC32 = 4, +KS_MODE_PPC64 = 8, +KS_MODE_QPX = 16, +KS_MODE_SPARC32 = 4, +KS_MODE_SPARC64 = 8, +KS_MODE_V9 = 16, +KS_ERR_ASM = 128, +KS_ERR_ASM_ARCH = 512, +KS_ERR_OK = 0, +KS_ERR_NOMEM = 1, +KS_ERR_ARCH = 2, +KS_ERR_HANDLE = 3, +KS_ERR_MODE = 4, +KS_ERR_VERSION = 5, +KS_ERR_OPT_INVALID = 6, +KS_ERR_ASM_EXPR_TOKEN = 128, +KS_ERR_ASM_DIRECTIVE_VALUE_RANGE = 129, +KS_ERR_ASM_DIRECTIVE_ID = 130, +KS_ERR_ASM_DIRECTIVE_TOKEN = 131, +KS_ERR_ASM_DIRECTIVE_STR = 132, +KS_ERR_ASM_DIRECTIVE_COMMA = 133, +KS_ERR_ASM_DIRECTIVE_RELOC_NAME = 134, +KS_ERR_ASM_DIRECTIVE_RELOC_TOKEN = 135, +KS_ERR_ASM_DIRECTIVE_FPOINT = 136, +KS_ERR_ASM_DIRECTIVE_UNKNOWN = 137, +KS_ERR_ASM_DIRECTIVE_EQU = 138, +KS_ERR_ASM_DIRECTIVE_INVALID = 139, +KS_ERR_ASM_VARIANT_INVALID = 140, +KS_ERR_ASM_EXPR_BRACKET = 141, +KS_ERR_ASM_SYMBOL_MODIFIER = 142, +KS_ERR_ASM_SYMBOL_REDEFINED = 143, +KS_ERR_ASM_SYMBOL_MISSING = 144, +KS_ERR_ASM_RPAREN = 145, +KS_ERR_ASM_STAT_TOKEN = 146, +KS_ERR_ASM_UNSUPPORTED = 147, +KS_ERR_ASM_MACRO_TOKEN = 148, +KS_ERR_ASM_MACRO_PAREN = 149, +KS_ERR_ASM_MACRO_EQU = 150, +KS_ERR_ASM_MACRO_ARGS = 151, +KS_ERR_ASM_MACRO_LEVELS_EXCEED = 152, +KS_ERR_ASM_MACRO_STR = 153, +KS_ERR_ASM_MACRO_INVALID = 154, +KS_ERR_ASM_ESC_BACKSLASH = 155, +KS_ERR_ASM_ESC_OCTAL = 156, +KS_ERR_ASM_ESC_SEQUENCE = 157, +KS_ERR_ASM_ESC_STR = 158, +KS_ERR_ASM_TOKEN_INVALID = 159, +KS_ERR_ASM_INSN_UNSUPPORTED = 160, +KS_ERR_ASM_FIXUP_INVALID = 161, +KS_ERR_ASM_LABEL_INVALID = 162, +KS_ERR_ASM_FRAGMENT_INVALID = 163, +KS_ERR_ASM_INVALIDOPERAND = 512, +KS_ERR_ASM_MISSINGFEATURE = 513, +KS_ERR_ASM_MNEMONICFAIL = 514, +KS_OPT_SYNTAX = 1, +KS_OPT_SYM_RESOLVER = 2, +KS_OPT_SYNTAX_INTEL = 1, +KS_OPT_SYNTAX_ATT = 2, +KS_OPT_SYNTAX_NASM = 4, +KS_OPT_SYNTAX_MASM = 8, +KS_OPT_SYNTAX_GAS = 16, +KS_OPT_SYNTAX_RADIX16 = 32, \ No newline at end of file diff --git a/bindings/powershell/Keystone/Keystone.psd1 b/bindings/powershell/Keystone/Keystone.psd1 new file mode 100755 index 0000000000000000000000000000000000000000..73936b2b175c7c01029218322a6c1e7d78135437 --- /dev/null +++ b/bindings/powershell/Keystone/Keystone.psd1 @@ -0,0 +1,22 @@ +@{ +# Script module or binary module file associated with this manifest. +ModuleToProcess = 'Keystone.psm1' + +# Version number of this module. +ModuleVersion = '0.0.0.1' + +# ID used to uniquely identify this module +GUID = 'd34db33f-7f65-4681-9cfb-0cf4929a8e68' + +# Author of this module +Author = 'Ruben Boonen' + +# Description of the functionality provided by this module +Description = 'Keystone Engine Binding Module' + +# Minimum version of the Windows PowerShell engine required by this module +PowerShellVersion = '2.0' + +# Functions to export from this module +FunctionsToExport = '*' +} diff --git a/bindings/powershell/Keystone/Keystone.psm1 b/bindings/powershell/Keystone/Keystone.psm1 new file mode 100755 index 0000000000000000000000000000000000000000..c72eb4292395358ddbdf475e63bca994763c14b3 --- /dev/null +++ b/bindings/powershell/Keystone/Keystone.psm1 @@ -0,0 +1,313 @@ +function Get-KeystoneAssembly { +<# +.SYNOPSIS + Powershell wrapper for Keystone (using inline C#). + +.DESCRIPTION + Author: Ruben Boonen (@FuzzySec) + License: BSD 3-Clause + Required Dependencies: None + Optional Dependencies: None + +.PARAMETER Architecture + Architecture type. + +.PARAMETER Mode + Mode type. + +.PARAMETER Code + Assembly string, use ";" or multi-line variables for instruction separation. + +.PARAMETER Syntax + Syntax for input assembly. + +.PARAMETER Version + Print ASCII version banner. + +.EXAMPLE + + # Support for multi-line code blocks + PS C:\> $Code = @" + >> sub esp, 200 + >> pop eax + >> pop ecx + >> ret + >> "@ + PS C:\> Get-KeystoneAssembly -Architecture KS_ARCH_X86 -Mode KS_MODE_32 -Code $Code + + Bytes : 9 + Instructions : 4 + PSArray : {0x81, 0xEC, 0xC8, 0x00...} + CArray : {\x81, \xEC, \xC8, \x00...} + RawArray : {81, EC, C8, 00...} + +.EXAMPLE + + # Get-KeystoneAssembly emits objects + PS C:\> $Code = @" + >> sub esp, 200 + >> pop eax + >> pop ecx + >> ret + >> "@ + PS C:\> $Object = Get-KeystoneAssembly -Architecture KS_ARCH_X86 -Mode KS_MODE_32 -Code $Code + PS C:\> $Object.RawArray -join "" + 81ECC80000005859C3 + PS C:\> $Object.CArray -join "" + \x81\xEC\xC8\x00\x00\x00\x58\x59\xC3 + PS C:\> "`$Shellcode = {" + $($Object.PSArray -join ", ") + "}" + $Shellcode = {0x81, 0xEC, 0xC8, 0x00, 0x00, 0x00, 0x58, 0x59, 0xC3} + +#> + + param( + [Parameter(ParameterSetName='Keystone', Mandatory = $True)] + [ValidateSet( + 'KS_ARCH_ARM', + 'KS_ARCH_ARM64', + 'KS_ARCH_MIPS', + 'KS_ARCH_X86', + 'KS_ARCH_PPC', + 'KS_ARCH_SPARC', + 'KS_ARCH_SYSTEMZ', + 'KS_ARCH_HEXAGON', + 'KS_ARCH_MAX') + ] + [String]$Architecture, + + [Parameter(ParameterSetName='Keystone', Mandatory = $True)] + [ValidateSet( + 'KS_MODE_LITTLE_ENDIAN', + 'KS_MODE_BIG_ENDIAN', + 'KS_MODE_ARM', + 'KS_MODE_THUMB', + 'KS_MODE_V8', + 'KS_MODE_MICRO', + 'KS_MODE_MIPS3', + 'KS_MODE_MIPS32R6', + 'KS_MODE_MIPS32', + 'KS_MODE_MIPS64', + 'KS_MODE_16', + 'KS_MODE_32', + 'KS_MODE_64', + 'KS_MODE_PPC32', + 'KS_MODE_PPC64', + 'KS_MODE_QPX', + 'KS_MODE_SPARC32', + 'KS_MODE_SPARC64', + 'KS_MODE_V9') + ] + [String]$Mode, + + [Parameter(ParameterSetName='Keystone', Mandatory = $True)] + [ValidateNotNullOrEmpty()] + [string]$Code, + + [Parameter(ParameterSetName='Keystone', Mandatory = $False)] + [ValidateSet( + 'KS_OPT_SYNTAX_INTEL', + 'KS_OPT_SYNTAX_ATT', + 'KS_OPT_SYNTAX_NASM', + 'KS_OPT_SYNTAX_MASM', + 'KS_OPT_SYNTAX_GAS') + ] + [String]$Syntax = "KS_OPT_SYNTAX_INTEL", + + [Parameter(ParameterSetName='Version', Mandatory = $False)] + [switch]$Version = $null + ) + + # Compatibility for PS v2 / PS v3+ + if(!$PSScriptRoot) { + $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent + } + + # Set the keystone DLL path + $DllPath = $($PSScriptRoot + '\Lib\Keystone\keystone.dll').Replace('\','\\') + + # Make sure the user didn't forget the DLL + if (![IO.File]::Exists($DllPath)) { + echo "`n[!] Missing Keystone DLL" + echo "[>] Quitting!`n" + Return + } + + # Load C# constants + $ks_err = Select-String "KS_ERR_" $($PSScriptRoot + '\Const\keystone_h.cs') |select -exp line + $ks_arch = Select-String "KS_ARCH_" $($PSScriptRoot + '\Const\keystone_h.cs') |select -exp line + $ks_mode = Select-String "KS_MODE_" $($PSScriptRoot + '\Const\keystone_h.cs') |select -exp line + $ks_opt_value = Select-String "KS_OPT_SYNTAX_" $($PSScriptRoot + '\Const\keystone_h.cs') |select -exp line + + # Inline C# to parse the unmanaged keystone DLL + Add-Type -TypeDefinition @" + using System; + using System.Diagnostics; + using System.Runtime.InteropServices; + using System.Security.Principal; + + public enum ks_err : int + { + $ks_err + } + + public enum ks_arch : int + { + $ks_arch + } + + public enum ks_mode : int + { + $ks_mode + } + + public enum ks_opt_value : uint + { + $ks_opt_value + } + + public static class Keystone + { + [DllImport("$DllPath")] + public static extern ks_err ks_open( + ks_arch arch, + ks_mode mode, + ref IntPtr handle); + + [DllImport("$DllPath")] + public static extern ks_err ks_option( + IntPtr handle, + int mode, + ks_opt_value value); + + [DllImport("$DllPath")] + public static extern int ks_asm( + IntPtr handle, + String assembly, + ulong address, + ref IntPtr encoding, + ref uint encoding_size, + ref uint stat_count); + + [DllImport("$DllPath")] + public static extern ks_err ks_errno( + IntPtr handle); + + [DllImport("$DllPath")] + public static extern ks_err ks_close( + IntPtr handle); + + [DllImport("$DllPath")] + public static extern void ks_free( + IntPtr handle); + + [DllImport("$DllPath")] + public static extern int ks_version( + uint major, + uint minor); + } +"@ + + if ($Version){ + $VerCount = [System.BitConverter]::GetBytes($([Keystone]::ks_version($null,$null))) + $Banner = @" + + ;# + ######### + ######"" ;; + ###";#### ;############## + ##### ### ##"" "## ""###### + #### ### ""### "### + #### ## "### "# + "### \# ; #### + "### " ##"#### + ## \### ## #### + #### "###; ### #### + ######## "#" ;### ###"##### + "#############" ####"/##" + " ;####### + "#######" + # + + -=[Keystone Engine v$($VerCount[1]).$($VerCount[0])]=- + +"@ + # Mmm ASCII version banner! + $Banner + Return + } + + # Asm Handle + $AsmHandle = [IntPtr]::Zero + + # Initialize Keystone with ks_open() + $CallResult = [Keystone]::ks_open($Architecture,$Mode,[ref]$AsmHandle) + if ($CallResult -ne "KS_ERR_OK") { + if ($CallResult -eq "KS_ERR_MODE"){ + echo "`n[!] Invalid Architecture/Mode combination" + echo "[>] Quitting..`n" + } else { + echo "`n[!] cs_open error: $CallResult" + echo "[>] Quitting..`n" + } + Return + } + + # Only one ks_opt_type -> KS_OPT_SYNTAX = 1 + $CallResult = [Keystone]::ks_option($AsmHandle, 1, $Syntax) + if ($CallResult -ne "KS_ERR_OK") { + echo "`n[!] ks_option error: $CallResult" + echo "[>] Quitting..`n" + $CallResult = [Keystone]::ks_close($AsmHandle) + Return + } + + # Result variables + $Encoded = [IntPtr]::Zero + [int]$Encoded_size = 0 + [int]$Stat_count = 0 + + # Assemble instructions + $CallResult = [Keystone]::ks_asm($AsmHandle, $Code, 0, [ref]$Encoded, [ref]$Encoded_size, [ref]$stat_count) + + if ($CallResult -ne 0) { + echo "`n[!] ks_asm error: $([Keystone]::ks_errno($AsmHandle))" + echo "[>] Quitting..`n" + $CallResult = [Keystone]::ks_close($AsmHandle) + Return + } else { + $BufferOffset = $Encoded.ToInt64() + + if ($Encoded_size -gt 0) { + # PS/C# hex array + $PSArray = @() + # C-style hex array + $CArray = @() + # Raw hex array + $RawArray = @() + for ($i=0; $i -lt $Encoded_size; $i++) { + $PSArray += echo "0x$("{0:X2}" -f $([Runtime.InteropServices.Marshal]::ReadByte($BufferOffset)))" + $CArray += echo "\x$("{0:X2}" -f $([Runtime.InteropServices.Marshal]::ReadByte($BufferOffset)))" + $RawArray += echo "$("{0:X2}" -f $([Runtime.InteropServices.Marshal]::ReadByte($BufferOffset)))" + $BufferOffset = $BufferOffset+1 + } + # Result Object + $HashTable = @{ + Bytes = $Encoded_size + Instructions = $stat_count + PSArray = $PSArray + CArray = $CArray + RawArray = $RawArray + } + New-Object PSObject -Property $HashTable |Select-Object Bytes,Instructions,PSArray,CArray,RawArray + + # Clean up! + [Keystone]::ks_free($Encoded) + $CallResult = [Keystone]::ks_close($AsmHandle) + } else { + echo "`n[!] No bytes assembled" + echo "[>] Quitting..`n" + $CallResult = [Keystone]::ks_close($AsmHandle) + Return + } + } +} \ No newline at end of file diff --git a/bindings/powershell/Keystone/Lib/Keystone/.gitignore b/bindings/powershell/Keystone/Lib/Keystone/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/bindings/powershell/README b/bindings/powershell/README new file mode 100644 index 0000000000000000000000000000000000000000..2fd0cc0948cbf3f5350880e16147985e0233d95a --- /dev/null +++ b/bindings/powershell/README @@ -0,0 +1,25 @@ +Usage + +Compile the relevant version (x86/x64) of “keystone.dll†and place it in “./Keystone/Lib/Keystone/â€. Alternatively, pre-compiled DLL’s can be obtained from the Keystone-Engine download page: + + * http://www.keystone-engine.org/download/ + +To use the PowerShell binding, the entire Keystone folder should be added to one of the PowerShell module directories: + + # Global PSModulePath path + %Windir%\System32\WindowsPowerShell\v1.0\Modules + + # User PSModulePath path + %UserProfile%\Documents\WindowsPowerShell\Modules + +Once this is done the module can be initialized by typing “Import-Module Keystone†in a new PowerShell terminal. Further information on the usage of the binding can be obtained with the following command: + + Get-Help Get-KeystoneAssembly -Full + +Notes + +The Keystone engine requires the Visual C++ Redistributable Packages for Visual +Studio 2013. The architecture relevant installer can be downloaded at the following +URL: + + * https://www.microsoft.com/en-gb/download/confirmation.aspx?id=40784 \ No newline at end of file