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
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
|
||
|
}
|
||
|
}
|
||
|
}
|