You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

313 lines
7.4 KiB

3 years ago
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
}
}
}