mirror of
https://github.com/wietze/Invoke-ArgFuscator
synced 2026-06-08 18:17:29 +00:00
Created an option to specify the command instead of the JSON file and added code to publish it to PowerShell Gallery (#1)
* add an option to include command parameter * add option to push to PowerShell Gallery * Update Invoke-ArgFuscator.psm1 * fix inoptionchar for windows * Minor changes to parameters (documentation) * Updating Invoke-ArgFuscator.ps1, README --------- Co-authored-by: Wietze <wietze@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
name: Publish PowerShell Module
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
publish-powershell-gallery:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: publishing
|
||||
run: |
|
||||
curl -L https://github.com/wietze/ArgFuscator.net/archive/refs/heads/main.zip -o ArgFuscator.zip && unzip ArgFuscator.zip && rm ArgFuscator.zip
|
||||
mv ./ArgFuscator.net-main/models .
|
||||
rm -rf ./ArgFuscator.net-main
|
||||
rm ./Invoke-ArgFuscator.ps1
|
||||
Publish-Module -Path '.' -NuGetApiKey ${{ secrets.PGALLERY }}
|
||||
shell: pwsh
|
||||
@@ -14,12 +14,7 @@ if ($args.Length -eq 0) {
|
||||
|
||||
$InputFile = Read-Host "Path to configuration file"
|
||||
if (!($n = Read-Host "Number of commands to generate [default=$n_default]")) { $n = $n_default }
|
||||
} else {
|
||||
$InputFile = $Args[0]
|
||||
$n = if ($args.Length -eq 2) { $Args[1] } else { $n_default }
|
||||
if ($args.Length -gt 2) {
|
||||
throw "Unexpected argument count"
|
||||
}
|
||||
$Args = @{ InputFile = $InputFile; n = $n };
|
||||
}
|
||||
|
||||
Invoke-ArgFuscator -InputFile $InputFile -n $n
|
||||
Invoke-ArgFuscator @Args
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
@{
|
||||
|
||||
# Script module or binary module file associated with this manifest.
|
||||
RootModule = 'Invoke-ArgFuscator.psm1'
|
||||
|
||||
# Version number of this module.
|
||||
ModuleVersion = '1.0.0'
|
||||
|
||||
# ID used to uniquely identify this module
|
||||
GUID = '844d9edc-57ad-4fcc-9fd5-77a69d4bf569'
|
||||
|
||||
# Author of this module
|
||||
Author = 'wietze'
|
||||
|
||||
# Description of the functionality provided by this module
|
||||
Description = 'A PowerShell module that generate obfuscated command-lines for common system-native executables'
|
||||
|
||||
# Minimum version of the Windows PowerShell engine required by this module
|
||||
PowerShellVersion = '5.0'
|
||||
|
||||
# Modules that must be imported into the global environment prior to importing this module
|
||||
RequiredModules = @()
|
||||
|
||||
# Script files (.ps1) that are run in the caller's environment prior to importing this module.
|
||||
ScriptsToProcess = @()
|
||||
|
||||
# Functions to export from this module
|
||||
FunctionsToExport = @('Invoke-ArgFuscator')
|
||||
|
||||
# Cmdlets to export from this module
|
||||
CmdletsToExport = @()
|
||||
|
||||
# Variables to export from this module
|
||||
VariablesToExport = @()
|
||||
|
||||
# Aliases to export from this module
|
||||
AliasesToExport = @()
|
||||
|
||||
# Private data to pass to the module specified in RootModule/ModuleToProcess
|
||||
PrivateData = @{
|
||||
PSData = @{
|
||||
# A URL to the license for this module.
|
||||
LicenseUri = 'https://github.com/wietze/Invoke-ArgFuscator/blob/main/LICENSE'
|
||||
|
||||
# A URL to the main website for this project.
|
||||
ProjectUri = 'https://github.com/wietze/Invoke-ArgFuscator'
|
||||
}
|
||||
}
|
||||
|
||||
NestedModules = @()
|
||||
|
||||
}
|
||||
+142
-4
@@ -10,10 +10,107 @@ using module "Modifiers\Shorthands.psm1"
|
||||
using module "Modifiers\UrlTransformer.psm1"
|
||||
|
||||
$OutputEncoding = [ System.Text.Encoding]::UTF8
|
||||
function Invoke-TokeniseCommand {
|
||||
param(
|
||||
[string]$InputCommand
|
||||
)
|
||||
|
||||
if ($null -eq $InputCommand) { return $null }
|
||||
$SeparationChar = ' '
|
||||
$QuoteChars = @('"', "'")
|
||||
$ValueChars = @('=', ':')
|
||||
$CommonOptionChars = @('/', '-')
|
||||
|
||||
$InQuote = $null
|
||||
[Token[]]$Tokens = @() # Explicitly type the array as Token[]
|
||||
$TokenContent = @()
|
||||
$SeenValueChar = $false
|
||||
|
||||
for ($i = 0; $i -lt $InputCommand.Length; $i++) {
|
||||
if ($TokenContent.Count -eq 0) { $SeenValueChar = $false }
|
||||
$Char = $InputCommand[$i].ToString()
|
||||
|
||||
if (($null -eq $InQuote) -and (
|
||||
($Char -eq $SeparationChar) -or (
|
||||
(-not $SeenValueChar) -and
|
||||
((($i -eq $InputCommand.Length) -or (-not @('\\', '/') -contains $InputCommand[$i + 1])) -and
|
||||
$ValueChars.contains($Char))
|
||||
)
|
||||
)) {
|
||||
if ($Char -ne $SeparationChar) {
|
||||
$TokenContent += $Char
|
||||
}
|
||||
|
||||
if ($TokenContent.Count -gt 0) {
|
||||
$Tokens += [Token]::new($TokenContent)
|
||||
}
|
||||
$TokenContent = @()
|
||||
}
|
||||
else {
|
||||
if (($null -ne $InQuote) -and ($Char -eq $InQuote)) {
|
||||
$InQuote = $null
|
||||
}
|
||||
elseif (($null -eq $InQuote) -and ($QuoteChars | Where-Object { $_ -eq $Char })) {
|
||||
$InQuote = $Char
|
||||
}
|
||||
|
||||
$TokenContent += $Char
|
||||
}
|
||||
$SeenValueChar = $SeenValueChar -or ($ValueChars | Where-Object { $_ -eq $Char })
|
||||
}
|
||||
|
||||
if ($TokenContent.Count -gt 0) {
|
||||
$Tokens += [Token]::new($TokenContent)
|
||||
}
|
||||
|
||||
# Find matching template, if available
|
||||
$Tokens[0].Type = "command"
|
||||
|
||||
$Tokens | Select-Object -Skip 1 | ForEach-Object -Begin { $i = 0 } -Process {
|
||||
$TokenText = $_.ToString()
|
||||
$_TokenText = $TokenText -replace "(['`"])(.*?)\1", '$2' #Remove any surrounding quotes
|
||||
|
||||
# If previous token ends with a ValueChar, assume this token denotes a 'value' type;
|
||||
# or, if no option char present, designate it as 'value', unless overwritten further down
|
||||
if (($ValueChars | Where-Object { $Tokens[$i].TokenContent[-1] -eq $_ }) -or -not ($CommonOptionChars | Where-Object { $_TokenText.StartsWith($_) })) {
|
||||
$_.Type = 'value'
|
||||
|
||||
# Special case: WMIC
|
||||
if ($Tokens[0].ToString() -match 'wmic(\.exe)?'`
|
||||
-and -not ($Tokens[1..($i + 1)] | Where-Object { $_.GetType() -eq 'disabled' })`
|
||||
-and -not ($Tokens[$i].GetType() -eq 'argument' -and ($ValueChars | Where-Object { $Tokens[$i].TokenContent[-1] -eq $_ }))) {
|
||||
Write-Host $Tokens[$i]
|
||||
$_.Type = 'disabled'
|
||||
}
|
||||
}
|
||||
|
||||
if ($_TokenText -match '^(?:\\\\[^\\]+|[a-zA-Z]:|\.[\\/])((?:\\[^\\]+)+\\)?([^<>:]*)$' -or $_TokenText -match '^[^<>:]+\.[a-zA-Z0-9]{2,4}$') {
|
||||
$_.Type = 'path' # Windows file path format
|
||||
}
|
||||
if ($_TokenText -match '^(HKLM|HKCC|HKCR|HKCU|HKU|HKEY_(LOCAL_MACHINE|CURRENT_CONFIG|CLASSES_ROOT|CURRENT_USER|USERS))\\?') {
|
||||
$_.Type = 'disabled' # Windows Registry
|
||||
}
|
||||
if ($_TokenText.StartsWith('http:') -or $_TokenText.StartsWith('https:') -or $_TokenText -match '[12]?\d?\d\.[12]?\d?\d\.[12]?\d?\d\.[12]?\d?\d') {
|
||||
$_.Type = 'url' #URLs (including IP addresses)
|
||||
}
|
||||
|
||||
$i++
|
||||
}
|
||||
|
||||
$result = @()
|
||||
$Tokens | ForEach-Object {
|
||||
$hashtable = @{}
|
||||
$hashtable[$_.Type] = ($_.TokenContent -join '')
|
||||
$result += $hashtable
|
||||
}
|
||||
|
||||
return $result
|
||||
}
|
||||
|
||||
function Invoke-ArgFuscator {
|
||||
[CmdletBinding()]
|
||||
Param (
|
||||
[Parameter(Mandatory = $true)]
|
||||
[Parameter(Mandatory = $true, ParameterSetName = "FromFile")]
|
||||
[ValidateScript({ if ((Test-Path $_ -PathType 'Leaf') -and ((Get-Item $_ | Select-Object -Expand Extension) -eq ".json" )) {
|
||||
return $true
|
||||
}
|
||||
@@ -21,18 +118,40 @@ function Invoke-ArgFuscator {
|
||||
throw "Make sure the file exists, and has a '.json' extension."
|
||||
} })]
|
||||
[string]$InputFile,
|
||||
|
||||
[Parameter(Mandatory = $true, ParameterSetName = "FromCommand")]
|
||||
[ValidateNotNullOrEmpty()]
|
||||
[string]$Command,
|
||||
|
||||
[Parameter(ParameterSetName = "FromCommand")]
|
||||
[ValidateScript({
|
||||
$platformPath = Join-Path $PSScriptRoot "models" $_
|
||||
if (Test-Path $platformPath -PathType Container) {
|
||||
return $true
|
||||
}
|
||||
else {
|
||||
throw "Platform '$_' not found. Make sure the platform directory exists in the models folder ($platformPath)."
|
||||
}
|
||||
})]
|
||||
[string]$Platform = "windows",
|
||||
[int]$n = 1
|
||||
)
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Obfuscates a command provided in a JSON-formatted configuration file.
|
||||
Obfuscates a command provided in a JSON-formatted configuration file or command string.
|
||||
|
||||
.DESCRIPTION
|
||||
Obfuscates a command provided in a JSON-formatted configuration file by applying specified obfuscation options to the provided command.
|
||||
Obfuscates a command provided in a JSON-formatted configuration file/command string by applying specified obfuscation options to the provided command.
|
||||
|
||||
.PARAMETER InputFile
|
||||
Specifies the path to the JSON-formatted config file.
|
||||
|
||||
.PARAMETER Command
|
||||
Specifies the command as string.
|
||||
|
||||
.PARAMETER Platform
|
||||
Specifies the platform (windows, linux, macos). Default value is windows.
|
||||
|
||||
.PARAMETER n
|
||||
Specifies the number of obfuscated commands to generate. Default value is 1.
|
||||
|
||||
@@ -46,7 +165,26 @@ function Invoke-ArgFuscator {
|
||||
https://www.github.com/wietze/Invoke-Argfuscator
|
||||
#>
|
||||
|
||||
$JSONData = Get-Content -Encoding UTF8 -Path $InputFile | ConvertFrom-Json;
|
||||
if ($PSCmdlet.ParameterSetName -eq "FromFile") {
|
||||
$JSONData = Get-Content -Encoding UTF8 -Path $InputFile | ConvertFrom-Json
|
||||
}
|
||||
else {
|
||||
$CommandData = Invoke-TokeniseCommand $Command
|
||||
$cmd = $CommandData[0]["command"]
|
||||
$filePath = Join-Path -Path $PSScriptRoot -ChildPath "\models\$Platform\$cmd.json"
|
||||
if (Test-Path $filePath) {
|
||||
$ModelData = Get-Content -Encoding UTF8 -Path $filePath | ConvertFrom-Json
|
||||
# Create a PSCustomObject that matches the expected format
|
||||
$JSONData = [PSCustomObject]@{
|
||||
"command" = ($CommandData | ConvertTo-JSON | ConvertFrom-Json)
|
||||
"modifiers" = $ModelData.modifiers
|
||||
}
|
||||
}
|
||||
else {
|
||||
Write-Error("Command '{0}' could not be found in models folder ({1} does not exist)" -f $cmd,$filePath)
|
||||
return $null
|
||||
}
|
||||
}
|
||||
$ErrorModifiers = @();
|
||||
for ($i = 0; $i -lt $n; $i++) {
|
||||
$Tokens = [System.Collections.ArrayList]@();
|
||||
|
||||
@@ -25,34 +25,72 @@ This module works on any operating system supporting PowerShell/pwsh; this inclu
|
||||
* **macOS**: If you have `brew` preinstalled, run `brew install powershell/tap/powershell` to install the latest version of PowerShell. For alternative installation options, refer to Microsoft's [documentation](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-macos).
|
||||
* **Linux**: Refer to Microsoft's [documentation](https://learn.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-linux) to see how you can install PowerShell on your distribution.
|
||||
|
||||
### Installation
|
||||
### Installation & usage
|
||||
|
||||
Simply clone or download the contents of this repository to a folder of choice.
|
||||
1. The simplest way to install this module is via the following PowerShell command:
|
||||
|
||||
## Usage
|
||||
|
||||
1. Make sure you have a model file, in JSON format. These can be generated via [ArgFuscator.net](https://argfuscator.net/) via the 'Download' option; alternatively, you can obtain raw base files via [GitHub](https://github.com/wietze/Argfuscator.net/tree/main/models).
|
||||
2. Call `Invoke-ArgFuscator.ps1` via PowerShell, e.g.:
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
powershell .\Invoke-ArgFuscator.ps1
|
||||
|
||||
# macOS and Linux
|
||||
pwsh ./Invoke-ArgFuscator.ps1
|
||||
```pwsh
|
||||
Install-Module -Name Invoke-ArgFuscator
|
||||
```
|
||||
|
||||
This will allow you to pass the path to the model file interactively.
|
||||
2. To use the module, call the function `Invoke-ArgFuscator` from within PowerShell, for example:
|
||||
|
||||
***Alternatively***, pass the path to the model file as command-line argument, e.g.:
|
||||
a. To pass a command line you want to obfuscate as a command-line argument (assuming it is supported by [ArgFuscator.net](https://github.com/wietze/Argfuscator.net)):
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
powershell .\Invoke-ArgFuscator.ps1 "path\to\file.json"
|
||||
```bash
|
||||
# Windows
|
||||
powershell /c "Invoke-ArgFuscator -Command 'certutil /f /urlcache https://www.example.org/ homepage.txt'"
|
||||
|
||||
# macOS and Linux
|
||||
pwsh ./Invoke-ArgFuscator.ps1 "path/to/file.json"
|
||||
```
|
||||
# macOS and Linux
|
||||
pwsh -c "Invoke-ArgFuscator -Command 'certutil /f /urlcache https://www.example.org/ homepage.txt'"
|
||||
```
|
||||
|
||||
b. To use your own model files[^1]:
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
powershell /c "Invoke-ArgFuscator -InputFile path\to\file.json"
|
||||
|
||||
# macOS and Linux
|
||||
pwsh -c "Invoke-ArgFuscator -InputFile path/to/file.json"
|
||||
```
|
||||
|
||||
## Local Development
|
||||
|
||||
1. Clone this repository to your device.
|
||||
2. Call `Invoke-ArgFuscator.ps1` via PowerShell, for example:
|
||||
|
||||
a. To run interactively pass the path of a model file[^1] via the standard input (stdin):
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
powershell .\Invoke-ArgFuscator.ps1
|
||||
|
||||
# macOS and Linux
|
||||
pwsh ./Invoke-ArgFuscator.ps1
|
||||
```
|
||||
|
||||
b. To pass the path to the model file[^1] as a command-line argument:
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
powershell .\Invoke-ArgFuscator.ps1 -InputFile "path\to\file.json"
|
||||
|
||||
# macOS and Linux
|
||||
pwsh ./Invoke-ArgFuscator.ps1 -InputFile "path/to/file.json"
|
||||
```
|
||||
|
||||
c. To pass a command line you want to obfuscate as a command-line argument:
|
||||
|
||||
*Note that this requires the [models/](https://github.com/wietze/ArgFuscator.net/tree/main/models) folder to be present in the same folder as `Invoke-ArgFuscator.ps1`.*
|
||||
|
||||
```bash
|
||||
# Windows
|
||||
powershell .\Invoke-ArgFuscator.ps1 -Command "certutil /f /urlcache https://www.example.org/ homepage.txt"
|
||||
|
||||
# macOS and Linux
|
||||
pwsh ./Invoke-ArgFuscator.ps1 -Command "certutil /f /urlcache https://www.example.org/ homepage.txt"
|
||||
```
|
||||
|
||||
## Integration
|
||||
|
||||
@@ -61,13 +99,19 @@ Because Invoke-ArgFuscator is a PowerShell module, you can add this project's fu
|
||||
To leverage Invoke-ArgFuscator, add
|
||||
|
||||
```pwsh
|
||||
Import-Module ./Invoke-ArgFuscator.psm1
|
||||
Import-Module Invoke-ArgFuscator
|
||||
```
|
||||
|
||||
to your PowerShell file, and call it as follows:
|
||||
to your PowerShell file, and call it as either of the following:
|
||||
|
||||
```pwsh
|
||||
Invoke-ArgFuscator -InputFile $InputFile -n $n
|
||||
Invoke-ArgFuscator -Command $Command -Platform $Platform -n $n
|
||||
```
|
||||
|
||||
with `$InputFile` a `string` containing a (relative/absolute) file path to the model file, and `$n` an `integer` greater than 0 for the number of obfuscated command-line equivalents that should be produced.
|
||||
with
|
||||
|
||||
* `$InputFile` a `string` containing a (relative/absolute) file path to the model file, and `$n` an `integer` greater than 0 for the number of obfuscated command-line equivalents that should be produced (optional); or,
|
||||
* `$Command` a `string` containing the command line you wish to obfuscate, `$Platform` a `string` with the relevant platform (e.g. `windows`, optional), and `$n` an `integer` greater than 0 for the number of obfuscated command-line equivalents that should be produced (optional).
|
||||
|
||||
[^1]: These can be generated via [ArgFuscator.net](https://argfuscator.net/) via the 'Download' option, or downloaded from [GitHub](https://github.com/wietze/Argfuscator.net/tree/main/models).
|
||||
|
||||
Reference in New Issue
Block a user