Skip to content

Commit

Permalink
Improve Windows API error handling using a dedicated helper function
Browse files Browse the repository at this point in the history
  • Loading branch information
itm4n committed Dec 31, 2024
1 parent 473daaa commit 9a47cde
Show file tree
Hide file tree
Showing 9 changed files with 102 additions and 74 deletions.
2 changes: 1 addition & 1 deletion build/Build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ function Invoke-Build {
".\src\helper\Msi.ps1",
".\src\helper\SystemConfiguration.ps1",
".\src\helper\SystemInformation.ps1",
".\src\helper\Utility.ps1"
".\src\helper\Utility.ps1",
".\src\check\Globals.ps1",
".\src\check\Main.ps1",
".\src\check\User.ps1",
Expand Down
10 changes: 8 additions & 2 deletions info/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
# Changelog

## 2025-12-28
## 2024-12-31

### Changed

- Improve standard Windows error handling (using a dedicated 'Format-Error' helper function).

## 2024-12-28

### Added

Expand All @@ -11,7 +17,7 @@
- Add multithreading to application file permissions check.
- Refactor application folder enumeration.

## 2025-12-27
## 2024-12-27

### Added

Expand Down
6 changes: 3 additions & 3 deletions src/check/Misc.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@ function Invoke-ExploitableLeakedHandleCheck {
# can be opened with the access right "duplicate handle". So, print a warning,
# just in case.
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "Failed to duplicate handle 0x$('{0:x}' -f $InheritedHandle.HandleValue) - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "Failed to duplicate handle 0x$('{0:x}' -f $InheritedHandle.HandleValue) - $(Format-Error $LastError)"
continue
}

Expand All @@ -859,7 +859,7 @@ function Invoke-ExploitableLeakedHandleCheck {
$TargetProcessId = $script:Kernel32::GetProcessId($InheritedHandleDuplicated)
if ($HandleProcessId -eq 0) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "GetProcessId KO - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "GetProcessId KO - $(Format-Error $LastError)"
continue
}
$CandidateHandle | Add-Member -MemberType "NoteProperty" -Name "TargetProcessId" -Value $TargetProcessId
Expand All @@ -877,7 +877,7 @@ function Invoke-ExploitableLeakedHandleCheck {
$TargetThreadId = $script:Kernel32::GetThreadId($InheritedHandleDuplicated)
if ($HandleThreadId -eq 0) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "GetThreadId KO - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "GetThreadId KO - $(Format-Error $LastError)"
continue
}
$CandidateHandle | Add-Member -MemberType "NoteProperty" -Name "TargetThreadId" -Value $TargetThreadId
Expand Down
92 changes: 66 additions & 26 deletions src/core/WinApi.Wrappers.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,43 @@
function Format-Error {
<#
.SYNOPSIS
Helper - Format a Windows standard error message.
Author: @itm4n
License: BSD 3-Clause
.DESCRIPTION
This cmdlet returns a formatted error message given a standard Windows error code.
.PARAMETER Code
A mandatory standard Windows error code.
.EXAMPLE
PS C:\> Format-Error -Code 5
Access is denied (5) - HRESULT: 0x80004005
.EXAMPLE
PS C:\> Format-Error 2
The system cannot find the file specified (2) - HRESULT: 0x80004005
#>

[OutputType([String])]
[CmdletBinding()]
param (
[Parameter(Position=0, Mandatory=$true)]
[ValidateNotNullOrEmpty()]
[Int32] $Code
)

process {
$ErrorObject = [ComponentModel.Win32Exception] $Code
$ErrorMessage = "$($ErrorObject.Message)"
if ($ErrorObject.NativeErrorCode -ge 0) { $ErrorMessage += " ($($ErrorObject.NativeErrorCode))" }
$ErrorMessage += " - HRESULT: $('0x{0:x8}' -f $ErrorObject.HResult)"
return $ErrorMessage
}
}

function Get-ProcessTokenHandle {
<#
.SYNOPSIS
Expand Down Expand Up @@ -32,7 +72,7 @@ function Get-ProcessTokenHandle {

if ($ProcessHandle -eq [IntPtr]::Zero) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "OpenProcess($($ProcessId), 0x$('{0:x8}' -f $ProcessAccess))) - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "OpenProcess($($ProcessId), 0x$('{0:x8}' -f $ProcessAccess))) - $(Format-Error $LastError)"
return
}
}
Expand All @@ -41,7 +81,7 @@ function Get-ProcessTokenHandle {
$Success = $script:Advapi32::OpenProcessToken($ProcessHandle, $TokenAccess, [ref] $TokenHandle)
if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "OpenProcessToken - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "OpenProcessToken - $(Format-Error $LastError)"
$script:Kernel32::CloseHandle($ProcessHandle) | Out-Null
return
}
Expand Down Expand Up @@ -138,7 +178,7 @@ function Get-TokenInformationData {
$Success = $script:Advapi32::GetTokenInformation($TokenHandle, $InformationClass, 0, $null, [ref] $DataSize)
if ($DataSize -eq 0) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "GetTokenInformation - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "GetTokenInformation - $(Format-Error $LastError)"
return
}

Expand All @@ -147,7 +187,7 @@ function Get-TokenInformationData {
$Success = $script:Advapi32::GetTokenInformation($TokenHandle, $InformationClass, $DataPtr, $DataSize, [ref] $DataSize)
if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "GetTokenInformation - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "GetTokenInformation - $(Format-Error $LastError)"
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($DataPtr)
return
}
Expand Down Expand Up @@ -372,7 +412,7 @@ function Get-TokenInformationPrivilege {

if ($Length -eq 0) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "LookupPrivilegeName - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "LookupPrivilegeName - $(Format-Error $LastError)"
continue
}

Expand All @@ -384,7 +424,7 @@ function Get-TokenInformationPrivilege {

if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "LookupPrivilegeName - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "LookupPrivilegeName - $(Format-Error $LastError)"
continue
}

Expand Down Expand Up @@ -940,7 +980,7 @@ function Convert-PSidToStringSid {

if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "ConvertSidToStringSidW - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "ConvertSidToStringSidW - $(Format-Error $LastError)"
return
}

Expand Down Expand Up @@ -971,7 +1011,7 @@ function Convert-PSidToNameAndType {
$Success = $script:Advapi32::LookupAccountSid($null, $PSid, $Name, [ref] $NameSize, $Domain, [ref] $DomainSize, [ref] $SidType)
if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "LookupAccountSid - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "LookupAccountSid - $(Format-Error $LastError)"
return
}

Expand Down Expand Up @@ -1035,7 +1075,7 @@ function Convert-DosDeviceToDevicePath {
if ($TargetPathLen -eq 0) {
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($TargetPathPtr)
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "QueryDosDevice('$($DosDevice)') - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "QueryDosDevice('$($DosDevice)') - $(Format-Error $LastError)"
return
}

Expand Down Expand Up @@ -1093,7 +1133,7 @@ function Get-FileDacl {

if ($FileHandle -eq [IntPtr]-1) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "CreateFile KO - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "CreateFile KO - $(Format-Error $LastError)"
return
}

Expand All @@ -1108,7 +1148,7 @@ function Get-FileDacl {

if ($Result -ne 0) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "GetSecurityInfo KO ($Result) - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "GetSecurityInfo KO ($Result) - $(Format-Error $LastError)"
$script:Kernel32::CloseHandle($FileHandle) | Out-Null
return
}
Expand All @@ -1124,7 +1164,7 @@ function Get-FileDacl {

if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "ConvertSecurityDescriptorToStringSecurityDescriptor KO ($Result) - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "ConvertSecurityDescriptorToStringSecurityDescriptor KO ($Result) - $(Format-Error $LastError)"
$script:Kernel32::LocalFree($SecurityDescriptorPtr) | Out-Null
$script:Kernel32::CloseHandle($FileHandle) | Out-Null
return
Expand All @@ -1136,7 +1176,7 @@ function Get-FileDacl {

if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Verbose "ConvertStringSecurityDescriptorToSecurityDescriptor KO ($Result) - $([ComponentModel.Win32Exception] $LastError)"
Write-Verbose "ConvertStringSecurityDescriptorToSecurityDescriptor KO ($Result) - $(Format-Error $LastError)"
$script:Kernel32::LocalFree($SecurityDescriptorPtr) | Out-Null
$script:Kernel32::CloseHandle($FileHandle) | Out-Null
return
Expand Down Expand Up @@ -1195,7 +1235,7 @@ function Disable-Wow64FileSystemRedirection {
}
else {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "Wow64DisableWow64FsRedirection KO ($Result) - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "Wow64DisableWow64FsRedirection KO ($Result) - $(Format-Error $LastError)"
}
}
}
Expand Down Expand Up @@ -1235,7 +1275,7 @@ function Restore-Wow64FileSystemRedirection {
}
else {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "Wow64RevertWow64FsRedirection KO ($Result) - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "Wow64RevertWow64FsRedirection KO ($Result) - $(Format-Error $LastError)"
}
}
}
Expand Down Expand Up @@ -1340,7 +1380,7 @@ function Get-DomainInformation {
Write-Verbose "No Azure Active Directory configuration found on this machine."
}
else {
Write-Warning "NetGetAadJoinInformation - $([ComponentModel.Win32Exception] $RetVal)"
Write-Warning "NetGetAadJoinInformation - $(Format-Error $RetVal)"
}
return
}
Expand Down Expand Up @@ -1378,7 +1418,7 @@ function Get-DomainInformation {
$BufferType = 0
$RetVal = $script:Netapi32::NetGetJoinInformation([IntPtr]::Zero, [ref] $NameBufferPtr, [ref] $BufferType)
if ($RetVal -ne 0) {
Write-Warning "NetGetJoinInformation - $([ComponentModel.Win32Exception] $RetVal)"
Write-Warning "NetGetJoinInformation - $(Format-Error $RetVal))"
return
}

Expand All @@ -1389,7 +1429,7 @@ function Get-DomainInformation {

$RetVal = $Netapi32::NetApiBufferFree($NameBufferPtr)
if ($RetVal -ne 0) {
Write-Warning "NetApiBufferFree - $([ComponentModel.Win32Exception] $RetVal)"
Write-Warning "NetApiBufferFree - $(Format-Error $RetVal)"
return
}
}
Expand Down Expand Up @@ -1422,7 +1462,7 @@ function ConvertTo-ArgumentList {
$RetVal = $script:Shell32::CommandLineToArgvW($CommandLine, [ref] $NumArgs)
if ($RetVal -eq [IntPtr]::Zero) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "CommandLineToArgvW - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "CommandLineToArgvW - $(Format-Error $LastError)"
return
}

Expand Down Expand Up @@ -1481,7 +1521,7 @@ function Resolve-ModulePath {
$RetVal = $script:Kernel32::LoadLibrary($Name)
if ($RetVal -eq [IntPtr]::Zero) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "LoadLibrary(`"$($Name)`") - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "LoadLibrary(`"$($Name)`") - $(Format-Error $LastError)"
return
}

Expand All @@ -1501,7 +1541,7 @@ function Resolve-ModulePath {
$RetVal = $script:Kernel32::GetModuleFileName($ModuleHandle, $ModuleFileName, $ModuleFileNameLength)
if ($RetVal -eq 0) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "GetModuleFileName - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "GetModuleFileName - $(Format-Error $LastError)"
return
}

Expand All @@ -1516,7 +1556,7 @@ function Resolve-ModulePath {
continue
}
else {
Write-Warning "GetModuleFileName - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "GetModuleFileName - $(Format-Error $LastError)"
return
}
}
Expand Down Expand Up @@ -1577,7 +1617,7 @@ function Resolve-PathRelativeTo {
$Result = $script:Shlwapi::PathRelativePathTo($PathOut, $From, $FILE_ATTRIBUTE_DIRECTORY, $To, $FILE_ATTRIBUTE_NORMAL)
if (-not $Result) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "PathRelativePathTo(`"$($From)`", `"$($To)`") error - $([ComponentModel.Win32Exception] $LastError)"
Write-Warning "PathRelativePathTo(`"$($From)`", `"$($To)`") error - $(Format-Error $LastError)"
return
}

Expand Down Expand Up @@ -1607,10 +1647,10 @@ function Get-FirmwareType {
process {
[UInt32] $FirmwareType = 0
$Result = $script:Kernel32::GetFirmwareType([ref] $FirmwareType)
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()

if ($Result -eq 0) {
Write-Warning "GetFirmwareType error - $([ComponentModel.Win32Exception] $LastError)"
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
Write-Warning "GetFirmwareType error - $(Format-Error $LastError)"
return
}

Expand Down Expand Up @@ -1666,7 +1706,7 @@ function Get-LocalUserInformation {
$ResumeHandle = [UInt32] 0
$RetVal = $script:Netapi32::NetUserEnum([IntPtr]::Zero, $Level, $FILTER_NORMAL_ACCOUNT, [ref] $BufferPtr, $MAX_PREFERRED_LENGTH, [ref] $EntriesRead, [ref] $TotalEntries, [ref] $ResumeHandle)
if ($RetVal -ne 0) {
Write-Warning "NetUserEnum - $([ComponentModel.Win32Exception] $RetVal)"
Write-Warning "NetUserEnum - $(Format-Error $RetVal)"
return
}

Expand Down
14 changes: 7 additions & 7 deletions src/exploit/PointAndPrint.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ function Get-WinSpoolPrinterDriver {
$null = $script:WinSpool::EnumPrinterDrivers($null, $Environment, $InfoLevel, [IntPtr]::Zero, 0, [ref] $BytesNeeded, [ref] $Count)
if ($BytesNeeded -eq 0) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "EnumPrinterDrivers - $([ComponentModel.Win32Exception] $LastError)"
throw "EnumPrinterDrivers - $(Format-Error $LastError)"
}

Write-Verbose "EnumPrinterDrivers OK - BytesNeeded: $($BytesNeeded)"
Expand All @@ -283,7 +283,7 @@ function Get-WinSpoolPrinterDriver {
$Success = $script:WinSpool::EnumPrinterDrivers($null, $Environment, $InfoLevel, $BufferPtr, $BytesNeeded, [ref] $BytesNeeded, [ref] $Count)
if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "EnumPrinterDrivers - $([ComponentModel.Win32Exception] $LastError)"
throw "EnumPrinterDrivers - $(Format-Error $LastError)"
}

Write-Verbose "EnumPrinterDrivers OK - Count: $($Count)"
Expand Down Expand Up @@ -346,7 +346,7 @@ function Add-WinSpoolPrinterDriver {
$Success = $script:WinSpool::AddPrinterDriverEx($null, $InfoLevel, $DriverInfoPtr, $FileCopyFlags)
if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "AddPrinterDriverEx - $([ComponentModel.Win32Exception] $LastError)"
throw "AddPrinterDriverEx - $(Format-Error $LastError)"
}

[Runtime.InteropServices.Marshal]::FreeHGlobal($DriverInfoPtr)
Expand Down Expand Up @@ -392,7 +392,7 @@ function Remove-WinSpoolPrinterDriver {
$Success = $script:WinSpool::DeletePrinterDriverEx($null, $Environment, $DriverName, $DPD_DELETE_UNUSED_FILES, 0)
if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "DeletePrinterDriverEx - $([ComponentModel.Win32Exception] $LastError)"
throw "DeletePrinterDriverEx - $(Format-Error $LastError)"
}
$Success
}
Expand Down Expand Up @@ -431,7 +431,7 @@ function Add-WinSpoolPrinter {
if (($null -eq $PrinterHandle) -or ($PrinterHandle -eq [IntPtr]::Zero)) {
$PrinterHandle = [IntPtr]::Zero
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "AddPrinter - $([ComponentModel.Win32Exception] $LastError)"
throw "AddPrinter - $(Format-Error $LastError)"
}

[Runtime.InteropServices.Marshal]::FreeHGlobal($PrinterInfoPtr)
Expand Down Expand Up @@ -471,7 +471,7 @@ function Remove-WinSpoolPrinter {
$Success = $script:WinSpool::DeletePrinter($PrinterHandle)
if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "DeletePrinter - $([ComponentModel.Win32Exception] $LastError)"
throw "DeletePrinter - $(Format-Error $LastError)"
}
}

Expand Down Expand Up @@ -509,7 +509,7 @@ function Close-WinSpoolPrinter {
$Success = $script:WinSpool::ClosePrinter($PrinterHandle)
if (-not $Success) {
$LastError = [Runtime.InteropServices.Marshal]::GetLastWin32Error()
throw "ClosePrinter - $([ComponentModel.Win32Exception] $LastError)"
throw "ClosePrinter - $(Format-Error $LastError)"
}
}

Expand Down
Loading

0 comments on commit 9a47cde

Please sign in to comment.