Last active
February 24, 2024 15:21
-
-
Save jaredcatkinson/f1cd44a8605e05c2661bdeca558c03e1 to your computer and use it in GitHub Desktop.
Get-ExtendedAttribute is a function to iterate through the C:\ volume looking for files with Extended Attributes. This code is beta and meant only for the purpose of a blog post on detection methodology.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is really beta code used in my Detection Methodology post. I plan to write more efficient code when I get some more time. | |
function Get-ExtendedAttribute | |
{ | |
foreach($file in (Get-ChildItem -Path C:\ -Recurse)) | |
{ | |
$obj = Get-ExtendedAttribute -FilePath $file.FullName | Where-Object {$_ -ne $null} | |
$obj | Add-Member -MemberType NoteProperty -Name FileName -Value $file.FullName | |
Write-Output $obj | |
} | |
} | |
function GetExtendedAttribute | |
{ | |
param | |
( | |
[Parameter()] | |
[string] | |
$FilePath | |
) | |
if(Test-Path -Path $FilePath -PathType Container) | |
{ | |
# FILE_DIRECTORY_FILE | |
$FileMode = 0x00000002 | |
} | |
else | |
{ | |
# FILE_NON_DIRECTORY_FILE | |
$FileMode = 0x00000040 | |
} | |
try | |
{ | |
$FILE_READ_EA = 0x00000008 | |
$FILE_RANDOM_ACCESS = 0x00000800 | |
$READ_CONTROL = 0x00020000 | |
$FILE_OPEN_FOR_BACKUP_INTENT = 0x00004000 | |
$FileHandle = NtOpenFile -FilePath $FilePath -AccessMask ($READ_CONTROL -bor $FILE_READ_EA) -ShareAccess ([System.IO.FileShare]::Delete -bor [System.IO.FileShare]::ReadWrite) -OpenOptions ($FILE_OPEN_FOR_BACKUP_INTENT -bor $FILE_RANDOM_ACCESS -bor $FileMode) | |
$EA = ZwQueryEaFile -FileHandle $FileHandle | |
if($EA -ne $null) | |
{ | |
Write-Output $EA | |
} | |
} | |
catch | |
{ | |
} | |
} | |
$Mod = New-InMemoryModule -ModuleName GetExtendedAttribute | |
$OBJECT_ATTRIBUTES = struct $Mod OBJECT_ATTRIBUTES @{ | |
Length = field 0 Int32 | |
RootDirectory = field 1 IntPtr | |
ObjectName = field 2 IntPtr | |
Attributes = field 3 UInt32 | |
SecurityDescriptor = field 4 IntPtr | |
SecurityQualityOfService = field 5 IntPtr | |
} | |
$IO_STATUS_BLOCK = struct $Mod IO_STATUS_BLOCK @{ | |
Status = field 0 IntPtr | |
Information = field 1 IntPtr | |
} | |
$UNICODE_STRING = struct $Mod UNICODE_STRING @{ | |
Length = field 0 UInt16 | |
MaximumLength = field 1 UInt16 | |
Buffer = field 2 IntPtr | |
} | |
$FILE_FULL_EA_INFORMATION = struct $Mod FILE_FULL_EA_INFORMATION @{ | |
NextEntryOffset = field 0 UInt32 | |
Flags = field 1 byte | |
EaNameLength = field 2 byte | |
EaValueLength = field 3 UInt16 | |
EaName = field 4 char[] -MarshalAs @('ByValArray', 1) | |
} | |
$FunctionDefinitions = @( | |
(func ntdll NtOpenFile ([UInt32]) @( | |
[IntPtr].MakeByRefType(), #_Out_ PHANDLE FileHandle | |
[UInt32], #_In_ ACCESS_MASK DesiredAccess | |
$OBJECT_ATTRIBUTES.MakeByRefType(), #_In_ POBJECT_ATTRIBUTES ObjectAttributes | |
$IO_STATUS_BLOCK.MakeByRefType(), #_Out_ PIO_STATUS_BLOCK IoStatusBlock | |
[System.IO.FileShare], #_In_ ULONG ShareAccess | |
[UInt32] #_In_ ULONG OpenOptions | |
) -EntryPoint NtOpenFile), | |
(func ntdll RtlInitUnicodeString ([void]) @( | |
$UNICODE_STRING.MakeByRefType(), #_Inout_ PUNICODE_STRING DestinationString | |
[string] #_In_opt_ PCWSTR SourceString | |
) -EntryPoint RtlInitUnicodeString), | |
(func ntdll ZwQueryEaFile ([UInt32]) @( | |
[IntPtr], #_In_ HANDLE FileHandle | |
$IO_STATUS_BLOCK.MakeByRefType(), #_Out_ PIO_STATUS_BLOCK IoStatusBlock | |
[IntPtr], #_Out_ PVOID Buffer | |
[UInt32], #_In_ ULONG Length | |
[bool], #_In_ BOOLEAN ReturnSingleEntry | |
[IntPtr], #_In_opt_ PVOID EaList | |
[UInt32], #_In_ ULONG EaListLength | |
[IntPtr], #_In_opt_ PULONG EaIndex | |
[bool] #_In_ BOOLEAN RestartScan | |
) -EntryPoint ZwQueryEaFile) | |
) | |
$Types = $FunctionDefinitions | Add-Win32Type -Module $mod -Namespace ArpCache | |
$Ntdll = $Types['ntdll'] | |
function NtOpenFile | |
{ | |
param | |
( | |
[Parameter(Mandatory = $true)] | |
[string] | |
$FilePath, | |
[Parameter(Mandatory = $true)] | |
[UInt32] | |
$AccessMask, | |
[Parameter(Mandatory = $true)] | |
[System.IO.FileShare] | |
$ShareAccess, | |
[Parameter(Mandatory = $true)] | |
[UInt32] | |
$OpenOptions | |
) | |
$FileHandle = [IntPtr]::Zero | |
$UnicodeString = RtlInitUnicodeString -SourceString "\??\$($FilePath)" | |
$unicodeIntPtr = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($UNICODE_STRING::GetSize()) | |
[System.Runtime.InteropServices.Marshal]::StructureToPtr($UnicodeString, $unicodeIntPtr, $true) | |
$ObjectAttributes = [Activator]::CreateInstance($OBJECT_ATTRIBUTES) | |
$ObjectAttributes.Length = $OBJECT_ATTRIBUTES::GetSize() | |
$ObjectAttributes.ObjectName = $unicodeIntPtr | |
$ObjectAttributes.RootDirectory = [IntPtr]::Zero | |
$ObjectAttributes.Attributes = 0x40 #OBJ_CASE_INSENSITIVE | |
$ObjectAttributes.SecurityDescriptor = [IntPtr]::Zero | |
$ObjectAttributes.SecurityQualityOfService = [IntPtr]::Zero | |
$IoStatusBlock = [Activator]::CreateInstance($IO_STATUS_BLOCK) | |
$SUCCESS = $Ntdll::NtOpenFile([ref]$FileHandle, $AccessMask, [ref]$ObjectAttributes, [ref]$IoStatusBlock, $ShareAccess, $OpenOptions) | |
if($SUCCESS -ne 0) | |
{ | |
throw "Unable to open file handle" | |
} | |
Write-Output $FileHandle | |
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($unicodeIntPtr) | |
} | |
function RtlInitUnicodeString | |
{ | |
param | |
( | |
[Parameter(Mandatory = $true)] | |
[string] | |
$SourceString | |
) | |
$DestinationString = [Activator]::CreateInstance($UNICODE_STRING) | |
$Ntdll::RtlInitUnicodeString([ref]$DestinationString, $SourceString) | |
Write-Output $DestinationString | |
} | |
function ZwQueryEaFile | |
{ | |
param | |
( | |
[Parameter(Mandatory = $true)] | |
[IntPtr] | |
$FileHandle | |
) | |
$IoStatusBlock = [Activator]::CreateInstance($IO_STATUS_BLOCK) | |
$buffer = [System.Runtime.InteropServices.Marshal]::AllocHGlobal(0xFFFF) | |
$SUCCESS = $Ntdll::ZwQueryEaFile($FileHandle, [ref]$IoStatusBlock, $Buffer, 0xFFFF, $true, [IntPtr]::Zero, 0, [IntPtr]::Zero, $false) | |
if($SUCCESS -eq 3221225554) # STATUS_NO_EAS_ON_FILE | |
{ | |
} | |
elseif($SUCCESS -eq 0) # NT_STATUS | |
{ | |
$FileEaInformation = $buffer -as $FILE_FULL_EA_INFORMATION | |
$NameOffset = [IntPtr]::Add($buffer, 8) | |
$Name = [System.Runtime.InteropServices.Marshal]::PtrToStringAnsi($NameOffset, $FileEaInformation.EaNameLength) | |
$ValueOffset = [IntPtr]::Add($NameOffset, $FileEaInformation.EaNameLength + 1) | |
$Value = New-Object -TypeName byte[]($FileEaInformation.EaValueLength) | |
[System.Runtime.InteropServices.Marshal]::Copy($ValueOffset, $Value, 0, $Value.Length) | |
$obj = New-Object -TypeName psobject | |
$obj | Add-Member -MemberType NoteProperty -Name Name -Value $Name | |
$obj | Add-Member -MemberType NoteProperty -Name Value -Value ([System.Text.Encoding]::ASCII.GetString($Value)) | |
$obj | Add-Member -MemberType NoteProperty -Name ValueData -Value $Value | |
$obj | Add-Member -MemberType NoteProperty -Name NextEntryOffset -Value $FileEaInformation.NextEntryOffset | |
$obj | Add-Member -MemberType NoteProperty -Name Flags -Value $FileEaInformation.Flags | |
Write-Output $obj | |
} | |
else | |
{ | |
throw "ZwQueryEaFile Error: $($SUCCESS)" | |
} | |
[System.Runtime.InteropServices.Marshal]::FreeHGlobal($Buffer) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment