GET /api/techniques/136/
HTTP 200 OK
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "name": "NTFS Files Attributes",
    "category": [
        "https://search.unprotect.it/api/categories/2/"
    ],
    "description": "Every New Technology File System (NTFS) formatted partition contains a Master File Table (MFT) that maintains a record for every file/directory on the partition. Within MFT entries are file attributes, such as Extended Attributes and Data known as Alternate Data Streams (ADSs) when more than one Data attribute is present], that can be used to store arbitrary data (and even complete files. \r\n\r\nAdversaries may store malicious data or binaries in file attribute metadata instead of directly in files. This may be done to evade some defenses, such as static indicator scanning tools and anti-virus.",
    "resources": "https://blogs.technet.microsoft.com/askcore/2010/08/25/ntfs-file-attributes/",
    "tags": "ntfs",
    "snippets": [
        {
            "language": "https://search.unprotect.it/api/snippet_languages/1/",
            "author": "https://search.unprotect.it/api/snippet_authors/1/",
            "technique": "https://search.unprotect.it/api/techniques/136/",
            "description": "This code let you handle Alternate Data Streams using two different techniques.\r\n\r\n* `FindFirstStreamW` / `FindNextStreamW` : Available since Windows Vista and easier to use.\r\n* `BackupRead` : Available since Windows XP and more tricky to use.\r\n\r\nYou can:\r\n\r\n* Enumerate ADS Files attached to a target file.\r\n* Backup ADS File(s) attached to a target file.\r\n* Copy any file to target file ADS.\r\n* Delete ADS File(s) attached to a target file.\r\n\r\nIf you want to learn more about how to use this tiny library you can check [this example project on Github](https://github.com/DarkCoderSc/ADS-Revealer).",
            "plain_code": "unit UntDataStreamObject;\r\n\r\ninterface\r\n\r\nuses WinAPI.Windows, System.Classes, System.SysUtils, Generics.Collections,\r\n      RegularExpressions;\r\n\r\ntype\r\n  TEnumDataStream = class;\r\n  TADSBackupStatus = (absTotal, absPartial, absError);\r\n\r\n  TDataStream = class\r\n  private\r\n    FOwner      : TEnumDataStream;\r\n    FStreamName : String;\r\n    FStreamSize : Int64;\r\n\r\n    {@M}\r\n    function GetStreamPath() : String;\r\n  public\r\n    {@C}\r\n    constructor Create(AOwner : TEnumDataStream; AStreamName : String; AStreamSize : Int64);\r\n\r\n    {@M}\r\n    function CopyFileToADS(AFileName : String) : Boolean;\r\n    function BackupFromADS(ADestPath : String) : Boolean;\r\n    function DeleteFromADS() : Boolean;\r\n\r\n    {@G/S}\r\n    property StreamName : String read FStreamName;\r\n    property StreamSize : Int64  read FStreamSize;\r\n    property StreamPath : String read GetStreamPath;\r\n  end;\r\n\r\n  TEnumDataStream = class\r\n  private\r\n    FTargetFile            : String;\r\n    FItems                 : TObjectList<TDataStream>;\r\n    FForceBackUpReadMethod : Boolean;\r\n\r\n    {@M}\r\n    function Enumerate_FindFirstStream() : Int64;\r\n    function Enumerate_BackupRead() : Int64;\r\n    function ExtractADSName(ARawName : String) : String;\r\n    function CopyFromTo(AFrom, ATo : String) : Boolean;\r\n    function GetDataStreamFromName(AStreamName : String) : TDataStream;\r\n  public\r\n    {@C}\r\n    constructor Create(ATargetFile : String; AEnumerateNow : Boolean = True; AForceBackUpReadMethod : Boolean = False);\r\n    destructor Destroy(); override;\r\n\r\n    {@M}\r\n    function Refresh() : Int64;\r\n\r\n    function CopyFileToADS(AFilePath : String) : Boolean;\r\n    function BackupFromADS(ADataStream : TDataStream; ADestPath : String) : Boolean; overload;\r\n    function DeleteFromADS(ADataStream : TDataStream) : Boolean; overload;\r\n    function BackupAllFromADS(ADestPath : String) : TADSBackupStatus;\r\n    function BackupFromADS(AStreamName, ADestPath : String) : Boolean; overload;\r\n    function DeleteFromADS(AStreamName : String) : Boolean; overload;\r\n\r\n    {@G}\r\n    property TargetFile : String                   read FTargetFile;\r\n    property Items      : TObjectList<TDataStream> read FItems;\r\n  end;\r\n\r\nimplementation\r\n\r\n{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n\r\n\r\n   TEnumDataStream\r\n\r\n\r\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}\r\n\r\n{\r\n  FindFirstStream / FindNextStream API Definition\r\n}\r\ntype\r\n  _STREAM_INFO_LEVELS = (FindStreamInfoStandard, FindStreamInfoMaxInfoLevel);\r\n  TStreamInfoLevels = _STREAM_INFO_LEVELS;\r\n\r\n  _WIN32_FIND_STREAM_DATA = record\r\n    StreamSize : LARGE_INTEGER;\r\n    cStreamName : array[0..(MAX_PATH + 36)] of WideChar;\r\n  end;\r\n  TWin32FindStreamData = _WIN32_FIND_STREAM_DATA;\r\n\r\nvar hKernel32         : THandle;\r\n    _FindFirstStreamW : function(lpFileName : LPCWSTR; InfoLevel : TStreamInfoLevels; lpFindStreamData : LPVOID; dwFlags : DWORD) : THandle; stdcall;\r\n    _FindNextStreamW  : function(hFindStream : THandle; lpFindStreamData : LPVOID) : BOOL; stdcall;\r\n\r\n\r\n{-------------------------------------------------------------------------------\r\n  Return the ADS name from it raw name (:<name>:$DATA)\r\n-------------------------------------------------------------------------------}\r\nfunction TEnumDataStream.ExtractADSName(ARawName : String) : String;\r\nvar AMatch : TMatch;\r\n    AName  : String;\r\nbegin\r\n  result := ARawName;\r\n  ///\r\n\r\n  AName := '';\r\n  AMatch := TRegEx.Match(ARawName, ':(.*):');\r\n  if (AMatch.Groups.Count < 2) then\r\n    Exit();\r\n\r\n  result := AMatch.Groups.Item[1].Value;\r\nend;\r\n\r\n{-------------------------------------------------------------------------------\r\n  Scan for ADS using method N�1 (FindFirstStream / FindNextStream). Work since\r\n  Microsoft Windows Vista.\r\n-------------------------------------------------------------------------------}\r\nfunction TEnumDataStream.Enumerate_FindFirstStream() : Int64;\r\nvar hStream     : THandle;\r\n    AData       : TWin32FindStreamData;\r\n\r\n    procedure ProcessDataStream();\r\n    var ADataStream : TDataStream;\r\n    begin\r\n      if (String(AData.cStreamName).CompareTo('::$DATA') = 0) then\r\n        Exit();\r\n      ///\r\n\r\n      ADataStream := TDataStream.Create(self, ExtractADSName(String(AData.cStreamName)), Int64(AData.StreamSize));\r\n\r\n      FItems.Add(ADataStream);\r\n    end;\r\n\r\nbegin\r\n  result := 0;\r\n  ///\r\n\r\n  self.FItems.Clear();\r\n\r\n  if NOT FileExists(FTargetFile) then\r\n    Exit(-1);\r\n\r\n  if (NOT Assigned(@_FindFirstStreamW)) or (NOT Assigned(@_FindNextStreamW)) then\r\n    Exit(-2);\r\n\r\n  FillChar(AData, SizeOf(TWin32FindStreamData), #0);\r\n\r\n  // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findfirststreamw\r\n  hStream := _FindFirstStreamW(PWideChar(FTargetFile), FindStreamInfoStandard, @AData, 0);\r\n  if (hStream = INVALID_HANDLE_VALUE) then begin\r\n    case GetLastError() of\r\n      ERROR_HANDLE_EOF : begin\r\n        Exit(-3); // No ADS Found\r\n      end;\r\n\r\n      ERROR_INVALID_PARAMETER : begin\r\n        Exit(-4); // Not compatible\r\n      end;\r\n\r\n      else begin\r\n        Exit(-5);\r\n      end;\r\n    end;\r\n  end;\r\n\r\n  ProcessDataStream();\r\n\r\n  // https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-findnextstreamw\r\n  while True do begin\r\n    FillChar(AData, SizeOf(TWin32FindStreamData), #0);\r\n\r\n    if NOT _FindNextStreamW(hStream, @AData) then\r\n      break;\r\n\r\n    ProcessDataStream();\r\n  end;\r\n\r\n  ///\r\n  result := self.FItems.Count;\r\nend;\r\n\r\n{-------------------------------------------------------------------------------\r\n  Scan for ADS using method N�2 (BackupRead()). Works since\r\n  Microsoft Windows XP.\r\n-------------------------------------------------------------------------------}\r\nfunction TEnumDataStream.Enumerate_BackupRead() : Int64;\r\nvar hFile           : THandle;\r\n    AStreamId       : TWIN32StreamID;\r\n    ABytesRead      : Cardinal;\r\n    pContext        : Pointer;\r\n    ALowByteSeeked  : Cardinal;\r\n    AHighByteSeeked : Cardinal;\r\n    AName           : String;\r\n    ABytesToRead    : Cardinal;\r\n    ASeekTo         : LARGE_INTEGER;\r\n    AClose          : Boolean;\r\nbegin\r\n  result := 0;\r\n  AClose := False;\r\n  ///\r\n  hFile := CreateFile(\r\n                        PWideChar(self.TargetFile),\r\n                        GENERIC_READ,\r\n                        FILE_SHARE_READ,\r\n                        nil,\r\n                        OPEN_EXISTING,\r\n                        FILE_FLAG_BACKUP_SEMANTICS,\r\n                        0\r\n  );\r\n  if (hFile = INVALID_HANDLE_VALUE) then\r\n    Exit(-1);\r\n  try\r\n    pContext := nil;\r\n    try\r\n      while True do begin\r\n        FillChar(AStreamId, SizeOf(TWIN32StreamID), #0);\r\n        ///\r\n\r\n        {\r\n          Read Stream\r\n        }\r\n        ABytesToRead := SizeOf(TWIN32StreamID) - 4; // We don't count \"cStreamName\"\r\n\r\n        if NOT BackupRead(hFile, @AStreamId, ABytesToRead, ABytesRead, False, False, pContext) then\r\n          break;\r\n\r\n        AClose := True;\r\n\r\n        if (ABytesRead = 0) then\r\n          break;\r\n\r\n        ASeekTo.QuadPart := (AStreamId.Size + AStreamId.dwStreamNameSize);\r\n\r\n        case AStreamId.dwStreamId of\r\n          {\r\n            Deadling with ADS Only\r\n          }\r\n          BACKUP_ALTERNATE_DATA : begin\r\n            if (AStreamId.dwStreamNameSize > 0) then begin\r\n              {\r\n                Read ADS Name\r\n              }\r\n              ABytesToRead := AStreamId.dwStreamNameSize;\r\n              SetLength(AName, (ABytesToRead div SizeOf(WideChar)));\r\n              if BackupRead(hFile, PByte(AName), ABytesToRead, ABytesRead, False, False, pContext) then begin\r\n                Dec(ASeekTo.QuadPart, ABytesRead); // Already done\r\n\r\n                FItems.Add(TDataStream.Create(self, ExtractADSName(AName), AStreamId.Size));\r\n              end;\r\n            end;\r\n          end;\r\n        end;\r\n\r\n        {\r\n          Goto Next Stream.\r\n        }\r\n        if NOT BackupSeek(hFile, ASeekTo.LowPart, ASeekTo.HighPart, ALowByteSeeked, AHighByteSeeked, pContext) then\r\n          break;\r\n\r\n        (*\r\n          //////////////////////////////////////////////////////////////////////\r\n          // BackupSeek() Alternative (Manual method)\r\n          //////////////////////////////////////////////////////////////////////\r\n\r\n          var ABuffer : array[0..2096-1] of byte;\r\n          // ...\r\n          while True do begin\r\n            if (ASeekTo.QuadPart < SizeOf(ABuffer)) then\r\n              ABytesToRead := ASeekTo.QuadPart\r\n            else\r\n              ABytesToRead := SizeOf(ABuffer);\r\n\r\n            if ABytesToRead = 0 then\r\n              break;\r\n\r\n            if NOT BackupRead(hFile, PByte(@ABuffer), ABytesToRead, ABytesRead, False, False, pContext) then\r\n              break;\r\n            ///\r\n\r\n            Dec(ASeekTo.QuadPart, ABytesRead);\r\n\r\n            if (ASeekTo.QuadPart <= 0) then\r\n              break;\r\n          end;\r\n          // ...\r\n\r\n          //////////////////////////////////////////////////////////////////////\r\n        *)\r\n      end;\r\n    finally\r\n      if AClose then\r\n        BackupRead(hFile, nil, 0, ABytesRead, True, False, pContext);\r\n    end;\r\n  finally\r\n    CloseHandle(hFile);\r\n  end;\r\nend;\r\n\r\n{-------------------------------------------------------------------------------\r\n  Refresh embedded data stream objects using Windows API. Returns number of\r\n  data stream objects or an error identifier.\r\n-------------------------------------------------------------------------------}\r\nfunction TEnumDataStream.Refresh() : Int64;\r\nvar AVersion : TOSVersion;\r\nbegin\r\n  result := 0;\r\n  ///\r\n\r\n  if (AVersion.Major >= 6) then begin\r\n    {\r\n      Vista and above\r\n    }\r\n    if self.FForceBackUpReadMethod then\r\n      result := self.Enumerate_BackupRead()\r\n    else\r\n      result := self.Enumerate_FindFirstStream();\r\n  end else if (AVersion.Major = 5) and (AVersion.Minor >= 1) then begin\r\n    {\r\n      Windows XP / Server 2003 & R2\r\n    }\r\n    result := self.Enumerate_BackupRead();\r\n  end else begin\r\n    // Unsupported (???)\r\n  end;\r\nend;\r\n\r\n{-------------------------------------------------------------------------------\r\n  Refresh ADS Files and retrieve one ADS file by it name.\r\n-------------------------------------------------------------------------------}\r\nfunction TEnumDataStream.GetDataStreamFromName(AStreamName : String) : TDataStream;\r\nvar I       : Integer;\r\n    AStream : TDataStream;\r\nbegin\r\n  result := nil;\r\n  ///\r\n\r\n  if (self.Refresh() > 0) then begin\r\n    for I := 0 to self.Items.count -1 do begin\r\n      AStream := self.Items.Items[i];\r\n      if NOT Assigned(AStream) then\r\n        continue;\r\n      ///\r\n\r\n      if (String.Compare(AStream.StreamName, AStreamName, True) = 0) then\r\n        result := AStream;\r\n    end;\r\n  end;\r\nend;\r\n\r\n{-------------------------------------------------------------------------------\r\n  ADS Classic Actions\r\n    - Copy file to current ADS Location.\r\n    - Copy ADS item to destination path.\r\n    - Delete ADS Item.\r\n-------------------------------------------------------------------------------}\r\n\r\nfunction TEnumDataStream.CopyFromTo(AFrom, ATo : String) : Boolean;\r\nvar hFromFile     : THandle;\r\n    hToFile       : THandle;\r\n\r\n    ABuffer       : array[0..4096-1] of byte;\r\n    ABytesRead    : Cardinal;\r\n    ABytesWritten : Cardinal;\r\nbegin\r\n  result := False;\r\n  ///\r\n\r\n  hFromFile := INVALID_HANDLE_VALUE;\r\n  hToFile   := INVALID_HANDLE_VALUE;\r\n\r\n  try\r\n    hFromFile := CreateFile(PWideChar(AFrom), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0);\r\n    if (hFromFile = INVALID_HANDLE_VALUE) then\r\n      Exit();\r\n\r\n    hToFile := CreateFile(\r\n                            PWideChar(ATo),\r\n                            GENERIC_WRITE,\r\n                            FILE_SHARE_WRITE,\r\n                            nil,\r\n                            CREATE_ALWAYS,\r\n                            FILE_ATTRIBUTE_NORMAL,\r\n                            0\r\n    );\r\n\r\n    if (hToFile = INVALID_HANDLE_VALUE) then\r\n      Exit();\r\n    ///\r\n\r\n    while True do begin\r\n      {\r\n        Read\r\n      }\r\n      if NOT ReadFile(hFromFile, ABuffer, SizeOf(ABuffer), ABytesRead, nil) then\r\n        Exit();\r\n\r\n      if ABytesRead = 0 then\r\n        break; // Success\r\n\r\n      {\r\n        Write\r\n      }\r\n      if NOT WriteFile(hToFile, ABuffer, ABytesRead, ABytesWritten, nil) then\r\n        Exit();\r\n\r\n      if (ABytesWritten <> ABytesRead) then\r\n        Exit();\r\n    end;\r\n\r\n    ///\r\n    result := True;\r\n  finally\r\n    if hFromFile <> INVALID_HANDLE_VALUE then\r\n      CloseHandle(hFromFile);\r\n\r\n    if hToFile <> INVALID_HANDLE_VALUE then\r\n      CloseHandle(hToFile);\r\n\r\n    ///\r\n    self.Refresh();\r\n  end;\r\nend;\r\n\r\nfunction TEnumDataStream.CopyFileToADS(AFilePath : String) : Boolean;\r\nbegin\r\n  result := CopyFromTo(AFilePath, Format('%s:%s', [self.FTargetFile, ExtractFileName(AFilePath)]));\r\nend;\r\n\r\nfunction TEnumDataStream.BackupFromADS(ADataStream : TDataStream; ADestPath : String) : Boolean;\r\nbegin\r\n  result := False;\r\n\r\n  if NOT Assigned(ADataStream) then\r\n    Exit();\r\n\r\n  result := CopyFromTo(ADataStream.StreamPath, Format('%s%s', [IncludeTrailingPathDelimiter(ADestPath), ADataStream.StreamName]));\r\nend;\r\n\r\nfunction TEnumDataStream.DeleteFromADS(ADataStream : TDataStream) : Boolean;\r\nbegin\r\n  result := DeleteFile(ADataStream.StreamPath);\r\nend;\r\n\r\nfunction TEnumDataStream.BackupAllFromADS(ADestPath : String) : TADSBackupStatus;\r\nvar I       : integer;\r\n    AStream : TDataStream;\r\nbegin\r\n  result := absError;\r\n  ///\r\n\r\n  if (self.Refresh() > 0) then begin\r\n    for I := 0 to self.Items.count -1 do begin\r\n      AStream := self.Items.Items[i];\r\n      if NOT Assigned(AStream) then\r\n        continue;\r\n      ///\r\n\r\n      if AStream.BackupFromADS(ADestPath) and (result <> absPartial) then\r\n        result := absTotal\r\n      else\r\n        result := absPartial;\r\n    end;\r\n  end;\r\nend;\r\n\r\nfunction TEnumDataStream.BackupFromADS(AStreamName, ADestPath : String) : Boolean;\r\nvar AStream : TDataStream;\r\nbegin\r\n  result := False;\r\n  ///\r\n\r\n  AStream := self.GetDataStreamFromName(AStreamName);\r\n  if Assigned(AStream) then\r\n    result := self.BackupFromADS(AStream, ADestPath);\r\nend;\r\n\r\nfunction TEnumDataStream.DeleteFromADS(AStreamName : String) : Boolean;\r\nvar AStream : TDataStream;\r\nbegin\r\n  result := False;\r\n  ///\r\n\r\n  AStream := self.GetDataStreamFromName(AStreamName);\r\n  if Assigned(AStream) then\r\n    result := self.DeleteFromADS(AStream);\r\nend;\r\n\r\n{-------------------------------------------------------------------------------\r\n  ___constructor\r\n-------------------------------------------------------------------------------}\r\nconstructor TEnumDataStream.Create(ATargetFile : String; AEnumerateNow : Boolean = True; AForceBackUpReadMethod : Boolean = False);\r\nbegin\r\n  self.FTargetFile := ATargetFile;\r\n  self.FForceBackUpReadMethod := AForceBackupReadMethod;\r\n\r\n  FItems := TObjectList<TDataStream>.Create();\r\n  FItems.OwnsObjects := True;\r\n\r\n  if AEnumerateNow then\r\n    self.Refresh();\r\nend;\r\n\r\n{-------------------------------------------------------------------------------\r\n  ___destructor\r\n-------------------------------------------------------------------------------}\r\ndestructor TEnumDataStream.Destroy();\r\nbegin\r\n  if Assigned(FItems) then\r\n    FreeAndNil(FItems);\r\n\r\n  ///\r\n  inherited Destroy();\r\nend;\r\n\r\n{+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n\r\n\r\n   TDataStream\r\n\r\n\r\n+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++}\r\n\r\nconstructor TDataStream.Create(AOwner : TEnumDataStream; AStreamName : String; AStreamSize : Int64);\r\nbegin\r\n  self.FOwner      := AOwner;\r\n  self.FStreamName := AStreamName;\r\n  self.FStreamSize := AStreamSize;\r\nend;\r\n\r\n{-------------------------------------------------------------------------------\r\n  Generate Stream Path Accordingly\r\n-------------------------------------------------------------------------------}\r\nfunction TDataStream.GetStreamPath() : String;\r\nbegin\r\n  result := '';\r\n\r\n  if NOT Assigned(FOwner) then\r\n    Exit();\r\n\r\n  result := Format('%s:%s', [FOwner.TargetFile, self.FStreamName]);\r\nend;\r\n\r\n{-------------------------------------------------------------------------------\r\n  ADS Classic Actions (Redirected to Owner Object)\r\n-------------------------------------------------------------------------------}\r\n\r\nfunction TDataStream.CopyFileToADS(AFileName : String) : Boolean;\r\nbegin\r\n  if Assigned(FOwner) then\r\n    result := FOwner.CopyFileToADS(AFileName);\r\nend;\r\n\r\nfunction TDataStream.BackupFromADS(ADestPath : String) : Boolean;\r\nbegin\r\n  if Assigned(FOwner) then\r\n    result := FOwner.BackupFromADS(self, ADestPath);\r\nend;\r\n\r\nfunction TDataStream.DeleteFromADS() : Boolean;\r\nbegin\r\n  if Assigned(FOwner) then\r\n    result := FOwner.DeleteFromADS(self);\r\nend;\r\n\r\n// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\r\n\r\ninitialization\r\n  _FindFirstStreamW := nil;\r\n  _FindNextStreamW  := nil;\r\n\r\n  hKernel32 := LoadLibrary('KERNEL32.DLL');\r\n  if (hKernel32 > 0) then begin\r\n    @_FindFirstStreamW := GetProcAddress(hKernel32, 'FindFirstStreamW');\r\n    @_FindNextStreamW := GetProcAddress(hKernel32, 'FindNextStreamW');\r\n  end;\r\n\r\nfinalization\r\n  _FindFirstStreamW := nil;\r\n  _FindNextStreamW  := nil;\r\n\r\nend."
        }
    ],
    "detection_rules": []
}