Alternate Data Stream Gibberish Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

24 Jun 2018, 11:10

I have updated the code above, try again.
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Alternate Data Stream Gibberish

24 Jun 2018, 11:16

No, same error again. Propvariant seems difficult.
---------------------------
proptest.ahk
---------------------------
Error: Memory limit reached (see #MaxMem in the help file).

Line#
040: NumPut(1, &PROPSPEC, "UInt")
041: NumPut(propid, &PROPSPEC+A_PtrSize, "UInt")
043: VarSetCapacity(PROPVARIANT, A_PtrSize == 4 ? 16 : 24)
046: DllCall(NumGet(NumGet(IPropertyStorage+0)+3*A_PtrSize), "UPtr", IPropertyStorage, "UInt", 1, "UPtr", &PROPSPEC, "UPtr", &PROPVARIANT, "UInt")
047: if (NumGet(&PROPVARIANT+0, "UShort") == 31)
048: List .= StrGet(NumGet(&PROPVARIANT + 8, "UPtr"), "UTF-16") . "
--------------------
"
049: Else
---> 050: List .= "PROPVARIANT.vt=" . NumGet(&PROPVARIANT+0, "UShort") . "
--------------------
"
051: DllCall("Ole32.dll\PropVariantClear", "UPtr", &PROPVARIANT)
052: }
054: MsgBox,Str . "

" . List
055: }
056: Exit
057: Exit
057: Exit

The current thread will exit.
---------------------------
OK
---------------------------
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

24 Jun 2018, 11:20

Replace List .= StrGet(NumGet(&PROPVARIANT + 8, "UPtr"), "UTF-16") . "`n--------------------`n" with List .= StrLen(StrGet(NumGet(&PROPVARIANT + 8, "UPtr"), "UTF-16")) . "`n--------------------`n". test with AHKU32. Tell me what you see in the messages.

Edit: try #MaxMem 500. Although I do not think this is a good idea.
Edit2: Replace List .= StrGet(NumGet(&PROPVARIANT + 8, "UPtr"), "UTF-16") . "`n--------------------`n" with List .= SubStr(StrGet(NumGet(&PROPVARIANT + 8, "UPtr"), "UTF-16"), 1, 100) . "`n--------------------`n" or List .= StrGet(NumGet(&PROPVARIANT + 8, "UPtr"), 100, "UTF-16") . "`n--------------------`n"
zcooler wrote:Propvariant seems difficult
PROPVARIANT is not difficult to understand, in fact, my only problem is to try to understand the correct steps to follow to recover the OLE Streams.
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Alternate Data Stream Gibberish

24 Jun 2018, 11:44

It's a no go. Same Memory limit reached message. Testing with #MaxMem 500 the errormsg doesnt show up but stalls the script and it's infinity waiting for the msgbox. Does this work for u? I do use aHK_H U32 and have kept thos english inclied doublequotes, which I see you removed in the testfile.
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

24 Jun 2018, 11:49

Find and select the .ts file in the dialog.

Code: Select all

fileselectfile File

VarSetCapacity(GUID, 16)
DllCall("Ole32.dll\CLSIDFromString", "Str", "{0000013A-0000-0000-C000-000000000046}", "UPtr", &GUID)

IPropertySetStorage := 0
DllCall("Ole32.dll\StgOpenStorageEx", "UPtr", &File, "UInt", 0x10, "Int", 4, "UInt", 0, "UPtr", 0, "UPtr", 0, "UPtr", &GUID, "UPtrP", IPropertySetStorage, "UInt")

IEnumSTATPROPSETSTG := 0
DllCall(NumGet(NumGet(IPropertySetStorage+0)+6*A_PtrSize), "UPtr", IPropertySetStorage, "UPtrP", IEnumSTATPROPSETSTG, "UInt")

Buffer := 0
VarSetCapacity(STATPROPSETSTG, 64)
Fetched := 0
While !DllCall(NumGet(NumGet(IEnumSTATPROPSETSTG+0)+3*A_PtrSize), "UPtr", IEnumSTATPROPSETSTG, "UInt", 1, "UPtr", &STATPROPSETSTG, "UIntP", Fetched, "UInt") ;enum
{
    ; Predefined Property Set Format Identifiers : https://msdn.microsoft.com/en-us/library/windows/desktop/aa380060(v=vs.85).aspx
    ; FMTID_SummaryInformation = {F29F85E0-4FF9-1068-AB91-08002B27B3D9}
    ; FMTID_DocSummaryInformation = {D5CDD502-2E9C-101B-9397-08002B2CF9AE}    
    ; FMTID_UserDefinedProperties = {D5CDD505-2E9C-101B-9397-08002B2CF9AE}    
    DllCall("Ole32.dll\StringFromCLSID", "UPtr", &STATPROPSETSTG, "UPtrP", Buffer)
    Str := StrGet(Buffer, "UTF-16"), DllCall("Ole32.dll\CoTaskMemFree", "UPtr", Buffer) ; stream name

    IPropertyStorage := 0
    DllCall(NumGet(NumGet(IPropertySetStorage+0)+4*A_PtrSize), "UPtr", IPropertySetStorage, "UPtr", &STATPROPSETSTG, "UInt", 0x10, "UPtrP", IPropertyStorage, "UInt")

    IEnumSTATPROPSTG := 0
    ; IPropertyStorage::Enum method
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379973(v=vs.85).aspx
    DllCall(NumGet(NumGet(IPropertyStorage+0)+11*A_PtrSize), "UPtr", IPropertyStorage, "UPtrP", IEnumSTATPROPSTG, "UInt")

    List := ""
    VarSetCapacity(STATPROPSTG, A_PtrSize + 4 + 4)
    While !DllCall(NumGet(NumGet(IEnumSTATPROPSTG+0)+3*A_PtrSize), "UPtr", IEnumSTATPROPSTG, "UInt", 1, "UPtr", &STATPROPSTG, "UIntP", Fetched, "UInt") ;enum
    {
        propid := NumGet(&STATPROPSTG + A_PtrSize, "UInt")

        VarSetCapacity(PROPSPEC, 2*A_PtrSize)
        NumPut(1, &PROPSPEC, "UInt")
        NumPut(propid, &PROPSPEC+A_PtrSize, "UInt")

        VarSetCapacity(PROPVARIANT, A_PtrSize == 4 ? 16 : 24)
        ; IPropertyStorage::ReadMultiple method
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379975(v=vs.85).aspx
        DllCall(NumGet(NumGet(IPropertyStorage+0)+3*A_PtrSize), "UPtr", IPropertyStorage, "UInt", 1, "UPtr", &PROPSPEC, "UPtr", &PROPVARIANT, "UInt")
        If (NumGet(&PROPVARIANT+0, "UShort") == 31)    ; VT_LPWSTR
            List .= StrGet(NumGet(&PROPVARIANT + 8, "UPtr"), 1000, "UTF-16") . "`n--------------------`n"
        Else
            List .= "PROPVARIANT.vt=" . NumGet(&PROPVARIANT+0, "UShort") . "`n--------------------`n"
        DllCall("Ole32.dll\PropVariantClear", "UPtr", &PROPVARIANT)
    }

    MsgBox % Str . "`n`n" . List
}
;ObjRelease * ...
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Alternate Data Stream Gibberish

24 Jun 2018, 11:58

Oh my, it was my mistake all along :facepalm: Had to use UTF-8-BOM encoding and then it works :D
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Alternate Data Stream Gibberish

24 Jun 2018, 12:01

Flipeador..nicely done :thumbup:
I wonder if it might be possible to convert the GUIDs to display the actual stream names?
I mean these names:
Stream : DocumentSummaryInformation
Stream : SebiesnrMkudrfcoIaamtykdDa
Stream : SummaryInformation
Last edited by zcooler on 24 Jun 2018, 12:08, edited 1 time in total.
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

24 Jun 2018, 12:07

Had to use UTF-8-BOM encoding and then it works
I always recommend using UTF-8 with BOM.
I wonder if it might be possible to convert the GUIDs to display the actual stream names?
Read Predefined Property Set Format Identifiers. That's the least of it, just use If.
Edit: As I said, I'm not sure what this data represents, I think the name of the Stream is the GUIDs.

I will continue investigating, I am not sure what kind of data the PROPVARIANT structure is recovering (if it's about the Stream data or what). Then we'll have to turn all this ugly code into a nice function :) .
zcooler wrote:I mean these names:
Stream : DocumentSummaryInformation
Stream : SebiesnrMkudrfcoIaamtykdDa
Stream : SummaryInformation
Yes, I know, I have no idea where it get those names, maybe ADS Manager do some conversion and are not the real names, you should try another program. is weird, maybe it's a BOM. Several unknowns :lol:
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

24 Jun 2018, 12:34

I just tested my previous function and enumerates the Streams correctly.

Code: Select all

fileselectfile file

List := ""
For Each, Stream in EnumStreams(File)
    List .= "[" . Stream.Size . "] " . Stream.Name . "`n"
MsgBox % List


EnumStreams(File)
{
    Local                Streams := []
        , WIN32_FIND_STREAM_DATA := ""

    VarSetCapacity(WIN32_FIND_STREAM_DATA, 8 + (260 + 36) * 2)
    Local Handle := DllCall("Kernel32.dll\FindFirstStreamW", "UPtr", &File, "UInt", 0, "UPtr", &WIN32_FIND_STREAM_DATA, "UInt", 0, "Ptr")
    If (!Handle)
        Return FALSE
    ObjPush(Streams, {Size: NumGet(&WIN32_FIND_STREAM_DATA, "Int64"), Name: RTrim(StrGet(&WIN32_FIND_STREAM_DATA + 8, "UTF-16"), ":$DATA")})

    While (DllCall("Kernel32.dll\FindNextStreamW", "Ptr", Handle, "UPtr", &WIN32_FIND_STREAM_DATA, "Ptr"))
        ObjPush(Streams, {Size: NumGet(&WIN32_FIND_STREAM_DATA, "Int64"), Name: RTrim(StrGet(&WIN32_FIND_STREAM_DATA + 8, "UTF-16"), ":$DATA")})

    Return Streams
} ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa365741(v=vs.85).aspx
Edit: This is very rare, if I try to modify the Stream data with ADS Manager (load data from a file), PROPVARIANT.vt is set to zero (VT_EMPTY). It seems that ADS Manager uses only FindFirstStream (does not use any interface, so it breaks the OLE Streams, and converts it into normal Streams, or maybe what I'm saying does not make any sense).
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Alternate Data Stream Gibberish

24 Jun 2018, 12:38

Flipeador wrote:I will continue investigating, I am not sure what kind of data the PROPVARIANT structure is recovering (if it's about the Stream data or what). Then we'll have to turn all this ugly code into a nice function :) .
Oh wow, that would be amazing :D
Flipeador wrote:
zcooler wrote:I mean these names:
Stream : DocumentSummaryInformation
Stream : SebiesnrMkudrfcoIaamtykdDa
Stream : SummaryInformation
Yes, I know, I have no idea where it get those names, maybe ADS Manager do some conversion and are not the real names, you should try another program. is weird, maybe it's a BOM. Several unknowns :lol:
Heck yes, this is so far above my head...you are impressing though :D
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Alternate Data Stream Gibberish

24 Jun 2018, 12:55

Flipeador wrote:Edit: This is very rare, if I try to modify the Stream data with ADS Manager (load data from a file), PROPVARIANT.vt is set to zero (VT_EMPTY). It seems that ADS Manager uses only FindFirstStream (does not use any interface, so it breaks the OLE Streams, and converts it into normal Streams, or maybe what I'm saying does not make any sense).
That is correctly observed...it's my experience with aDSmanager as well in text preview. The hex preview works better.
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

24 Jun 2018, 12:58

That is correctly observed...it's my experience with aDSmanager as well in text preview.
Does FindFirstStream work for you too?.
The hex preview works better
That's because it's probably binary data, and not a string. Or who knows what strange things do ADS Manager. :shock:
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Alternate Data Stream Gibberish

24 Jun 2018, 13:22

Flipeador wrote:Does FindFirstStream work for you too?.
Hmm..that is strange. Your code doesnt seem to find the content of DocumentSummaryInformation, while the other two SebiesnrMkudrfcoIaamtykdDa and SummaryInformation seem to be no problem.
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

24 Jun 2018, 13:33

What do you mean? using FileOpen?, What code are you using.
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Alternate Data Stream Gibberish

24 Jun 2018, 13:46

With this code:

Code: Select all

; put the file path here
File := "F:\Ny mapp\20180116_20-59-02_Discovery Channel_Gold Rush  Alaska - S08E03 - “Busted And Bushfixed”.ts"
;File := "F:\Ny mapp\20180102_02-19-01_Sjuan_Clueless - ★★★★★★★☆☆☆.ts"

VarSetCapacity(GUID, 16)
DllCall("Ole32.dll\CLSIDFromString", "Str", "{0000013A-0000-0000-C000-000000000046}", "UPtr", &GUID)

IPropertySetStorage := 0
DllCall("Ole32.dll\StgOpenStorageEx", "UPtr", &File, "UInt", 0x10, "Int", 4, "UInt", 0, "UPtr", 0, "UPtr", 0, "UPtr", &GUID, "UPtrP", IPropertySetStorage, "UInt")

IEnumSTATPROPSETSTG := 0
DllCall(NumGet(NumGet(IPropertySetStorage+0)+6*A_PtrSize), "UPtr", IPropertySetStorage, "UPtrP", IEnumSTATPROPSETSTG, "UInt")

Buffer := 0
VarSetCapacity(STATPROPSETSTG, 64)
Fetched := 0
While !DllCall(NumGet(NumGet(IEnumSTATPROPSETSTG+0)+3*A_PtrSize), "UPtr", IEnumSTATPROPSETSTG, "UInt", 1, "UPtr", &STATPROPSETSTG, "UIntP", Fetched, "UInt") ;enum
{
    ; Predefined Property Set Format Identifiers : https://msdn.microsoft.com/en-us/library/windows/desktop/aa380060(v=vs.85).aspx
    ; FMTID_SummaryInformation = {F29F85E0-4FF9-1068-AB91-08002B27B3D9}
    ; FMTID_DocSummaryInformation = {D5CDD502-2E9C-101B-9397-08002B2CF9AE}    
    ; FMTID_UserDefinedProperties = {D5CDD505-2E9C-101B-9397-08002B2CF9AE}    
    DllCall("Ole32.dll\StringFromCLSID", "UPtr", &STATPROPSETSTG, "UPtrP", Buffer)
    Str := StrGet(Buffer, "UTF-16"), DllCall("Ole32.dll\CoTaskMemFree", "UPtr", Buffer) ; stream name

    IPropertyStorage := 0
    DllCall(NumGet(NumGet(IPropertySetStorage+0)+4*A_PtrSize), "UPtr", IPropertySetStorage, "UPtr", &STATPROPSETSTG, "UInt", 0x10, "UPtrP", IPropertyStorage, "UInt")

    IEnumSTATPROPSTG := 0
    ; IPropertyStorage::Enum method
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379973(v=vs.85).aspx
    DllCall(NumGet(NumGet(IPropertyStorage+0)+11*A_PtrSize), "UPtr", IPropertyStorage, "UPtrP", IEnumSTATPROPSTG, "UInt")

    List := ""
    VarSetCapacity(STATPROPSTG, A_PtrSize + 4 + 4)
    While !DllCall(NumGet(NumGet(IEnumSTATPROPSTG+0)+3*A_PtrSize), "UPtr", IEnumSTATPROPSTG, "UInt", 1, "UPtr", &STATPROPSTG, "UIntP", Fetched, "UInt") ;enum
    {
        propid := NumGet(&STATPROPSTG + A_PtrSize, "UInt")

        VarSetCapacity(PROPSPEC, 2*A_PtrSize)
        NumPut(1, &PROPSPEC, "UInt")
        NumPut(propid, &PROPSPEC+A_PtrSize, "UInt")

        VarSetCapacity(PROPVARIANT, A_PtrSize == 4 ? 16 : 24)
        ; IPropertyStorage::ReadMultiple method
        ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379975(v=vs.85).aspx
        DllCall(NumGet(NumGet(IPropertyStorage+0)+3*A_PtrSize), "UPtr", IPropertyStorage, "UInt", 1, "UPtr", &PROPSPEC, "UPtr", &PROPVARIANT, "UInt")
        If (NumGet(&PROPVARIANT+0, "UShort") == 31)    ; VT_LPWSTR
            List .= StrGet(NumGet(&PROPVARIANT + 8, "UPtr"), 1000, "UTF-16") . "`n--------------------`n"
        Else
            List .= "PROPVARIANT.vt=" . NumGet(&PROPVARIANT+0, "UShort") . "`n--------------------`n"
        DllCall("Ole32.dll\PropVariantClear", "UPtr", &PROPVARIANT)
    }

    MsgBox % Str . "`n`n" . List
}
;ObjRelease * ...
If using your enumeration function these streams are found:
Spoiler
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

24 Jun 2018, 17:48

I have cleaned the code but I do not know why it does not show the content of DocumentSummaryInformation. Also i do not know why it does not retrieve {4c8cc155-6c1e-11d1-8e41-00c04fb9386d}.
If you want to see the code (AHKv2) (do not be intimidated by the amount of code, it's just for greater readability):

Code: Select all

Filename := FileSelect()

; opens an existing root storage object in the file system
IPropertySetStorage := GetPropSetStorage(Filename)
If (!IPropertySetStorage)
{
    MsgBox "IPropertySetStorage ERROR " . Format("0x{:08X}", ErrorLevel)
    ExitApp
}

Info := ""

; retrieves the property sets stored in this property set storage
PropertySets := EnumPropSetStorage(IPropertySetStorage)
For Each, STATPROPSETSTG in PropertySets
{
    Info .= "STATPROPSETSTG.fmtid "    . StringFromCLSID(STATPROPSETSTG.fmtid) . "`n"      ; format identifier (FMTID) of the property set
    Info .= "STATPROPSETSTG.clsid "    . StringFromCLSID(STATPROPSETSTG.clsid) . "`n"
    Info .= "STATPROPSETSTG.grfFlags " . STATPROPSETSTG.grfFlags               . "`n`n"

    ; opens a property set contained in the property set storage object
    IPropertyStorage := PropSetStorage_Open(IPropertySetStorage, STATPROPSETSTG.fmtid)
    If (IPropertyStorage)
    {
        ; get statistical data in the property set
        StatisticalData := EnumPropStorage(IPropertyStorage)
        If (!ObjLength(StatisticalData))
            Info .= "> no statistical data <`n"
        For Each, STATPROPSTG in StatisticalData
        {
            Info .= "STATPROPSTG.lpwstrName " . STATPROPSTG.lpwstrName . "`n"
            Info .= "STATPROPSTG.propid "     . STATPROPSTG.propid     . "`n"
            Info .= "STATPROPSTG.vt "         . STATPROPSTG.vt         . "`n"

            ; retrieves any existing string name for the specified property ID
            Info .= "PropertyName "           . PropStorage_ReadPropertyName(IPropertyStorage, STATPROPSTG.propid) . " (" . ErrorLevel . ")`n"

            ; reads the property value
            PROPVARIANT := PropStorage_Read(IPropertyStorage, STATPROPSTG.propid)
            If (PROPVARIANT)
            {
                Info .= "PROPVARIANT.vt " . PROPVARIANT.vt . "`n"
                If (PROPVARIANT.vt == 30 || PROPVARIANT.vt == 31)    ; VT_LPSTR = 30 | VT_LPWSTR = 31
                    Info .= "`n" . StrGet(PROPVARIANT.ptr, PROPVARIANT.vt == 30 ? "UTF-8" : "UTF-16") . "`n`n"
                Else
                    Info .= "`n< no string >`n`n"
                PropVariantClear(PROPVARIANT)
            }
        }
        ObjRelease(IPropertyStorage)
    }

    Info .= "`n`n`n----------------------`n`n`n`n"
}

; special-case handling for the UserDefined property set
CLSID := ""
IPropertyStorage := PropSetStorage_Open(IPropertySetStorage, CLSIDFromString("{D5CDD505-2E9C-101B-9397-08002B2CF9AE}", CLSID))
If (IPropertyStorage)
{
    Info .= "STATPROPSETSTG.fmtid "    . StringFromCLSID(&CLSID) . "`n`n"      ; format identifier (FMTID) of the property set

    StatisticalData := EnumPropStorage(IPropertyStorage)
    If (!ObjLength(StatisticalData))
        Info .= "> no statistical data <`n"
    For Each, STATPROPSTG in StatisticalData
    {
        Info .= "STATPROPSTG.lpwstrName " . STATPROPSTG.lpwstrName . "`n"
        Info .= "STATPROPSTG.propid "     . STATPROPSTG.propid     . "`n"
        Info .= "STATPROPSTG.vt "         . STATPROPSTG.vt         . "`n"

        Info .= "PropertyName "           . PropStorage_ReadPropertyName(IPropertyStorage, STATPROPSTG.propid) . " (" . ErrorLevel . ")`n"

        PROPVARIANT := PropStorage_Read(IPropertyStorage, STATPROPSTG.propid)
        If (PROPVARIANT)
        {
            Info .= "PROPVARIANT.vt " . PROPVARIANT.vt . "`n"
            If (PROPVARIANT.vt == 30 || PROPVARIANT.vt == 31)    ; VT_LPSTR = 30 | VT_LPWSTR = 31
                Info .= "`n" . StrGet(PROPVARIANT.ptr, PROPVARIANT.vt == 30 ? "UTF-8" : "UTF-16") . "`n`n"
            Else
                Info .= "`n< no string >`n`n"
            PropVariantClear(PROPVARIANT)
        }
    }
    ObjRelease(IPropertyStorage)
}

ObjRelease(IPropertySetStorage)
FileOpen(A_Desktop . "\~tmp.txt", "w", "UTF-16").Write(Info)
Run(A_Desktop . "\~tmp.txt")
ExitApp





GetPropSetStorage(Filename, AccessMode := 0x10)
{
    Local GUID := "", IPropertySetStorage := 0
    VarSetCapacity(GUID, 16)
    DllCall("Ole32.dll\CLSIDFromString", "Str", "{0000013A-0000-0000-C000-000000000046}", "UPtr", &GUID, "UInt")

    ; StgOpenStorageEx function
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa380342(v=vs.85).aspx
    ; STGFMT_ANY = 4
    ErrorLevel := DllCall("Ole32.dll\StgOpenStorageEx", "UPtr", &Filename, "UInt", AccessMode, "Int", 4, "UInt", 0, "UPtr", 0, "UPtr", 0, "UPtr", &GUID, "UPtrP", IPropertySetStorage, "UInt")
    Return IPropertySetStorage
}

EnumPropSetStorage(IPropertySetStorage)
{
    ; IPropertySetStorage::Enum method
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379960(v=vs.85).aspx
    Local IEnumSTATPROPSETSTG := 0
    If (ErrorLevel := DllCall(NumGet(NumGet(IPropertySetStorage)+6*A_PtrSize), "UPtr", IPropertySetStorage, "UPtrP", IEnumSTATPROPSETSTG, "UInt"))
        Return FALSE

    ; STATPROPSETSTG structure
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa380317(v=vs.85).aspx
    Local PropertySets := [ {Buffer: ""} ], i := 1, Address := 0
    ObjSetCapacity(PropertySets[i], "Buffer", 64)    ; sizeof STATPROPSETSTG = 64 | PropertySets.Buffer = struct STATPROPSETSTG

    ; IEnumSTATPROPSETSTG::Next method
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379202(v=vs.85).aspx
    While (!DllCall(NumGet(NumGet(IEnumSTATPROPSETSTG)+3*A_PtrSize), "UPtr", IEnumSTATPROPSETSTG, "UInt", 1, "UPtr", Address:=ObjGetAddress(PropertySets[i], "Buffer"), "UPtr", 0, "UInt"))
    {
        ObjRawSet(PropertySets[i], "fmtid", Address)                          ; FMTID    STATPROPSETSTG.fmtid    (FMTID of the current property set when created)
        ObjRawSet(PropertySets[i], "clsid", Address+16)                       ; CLSID    STATPROPSETSTG.clsid    (CLSID associated with this property set when created or modified)
        ObjRawSet(PropertySets[i], "grfFlags", NumGet(Address+32, "UInt"))    ; DWORD    STATPROPSETSTG.grfFlags (https://msdn.microsoft.com/en-us/library/windows/desktop/aa380069(v=vs.85).aspx)
        ObjRawSet(PropertySets[i], "mtime", Address+40)                       ; FILETIME STATPROPSETSTG.mtime    (time in Universal Coordinated Time (UTC) when the property set was last modified)
        ObjRawSet(PropertySets[i], "ctime", Address+48)                       ; FILETIME STATPROPSETSTG.ctime    (time in UTC when this property set was created)
        ObjRawSet(PropertySets[i], "atime", Address+56)                       ; FILETIME STATPROPSETSTG.atime    (Time in UTC when this property set was last accessed)
        ObjSetCapacity(PropertySets[i:=ObjPush(PropertySets, {Buffer: ""})], "Buffer", 64)    ; new struct STATPROPSETSTG
    }

    ObjPop(PropertySets)    ; remove the last STATPROPSETSTG structure
    ObjRelease(IEnumSTATPROPSETSTG)
    Return PropertySets
}

PropSetStorage_Open(IPropertySetStorage, FMTID, AccessMode := 0x10)
{
    ; IPropertySetStorage::Open method
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379965(v=vs.85).aspx
    Local IPropertyStorage := 0
    ErrorLevel := DllCall(NumGet(NumGet(IPropertySetStorage)+4*A_PtrSize), "UPtr", IPropertySetStorage, "UPtr", FMTID, "UInt", AccessMode, "UPtrP", IPropertyStorage, "UInt")
    Return IPropertyStorage
}

EnumPropStorage(IPropertyStorage)
{
    ; IPropertyStorage::Enum method
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379973(v=vs.85).aspx
    Local IEnumSTATPROPSTG := 0
    If (ErrorLevel := DllCall(NumGet(NumGet(IPropertyStorage)+11*A_PtrSize), "UPtr", IPropertyStorage, "UPtrP", IEnumSTATPROPSTG, "UInt"))
        Return FALSE

    ; STATPROPSTG structure
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa380318(v=vs.85).aspx
    Local StatisticalData := [ {Buffer: ""} ], i := 1, Address := 0
    ObjSetCapacity(StatisticalData[i], "Buffer", A_PtrSize+8)    ; sizeof STATPROPSTG = A_PtrSize+8 | StatisticalData.Buffer = struct STATPROPSTG
    

    ; IEnumSTATPROPSTG::Next method
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379213(v=vs.85).aspx
    While (!DllCall(NumGet(NumGet(IEnumSTATPROPSTG)+3*A_PtrSize), "UPtr", IEnumSTATPROPSTG, "UInt", 1, "UPtr", Address:=ObjGetAddress(StatisticalData[i], "Buffer"), "UPtr", 0, "UInt"))
    {
        If (NumGet(Address))
            ObjRawSet(StatisticalData[i], "lpwstrName", StrGet(NumGet(Address), "UTF-16"))    ; LPWSTR  STATPROPSTG.lpwstrName (optional string name associated with the property)
          , DllCall("Ole32.dll\CoTaskMemFree", "UPtr", NumGet(Address))                       ;   must be freed using CoTaskMemFree
          , NumPut(ObjGetAddress(StatisticalData[i], "lpwstrName"), Address)                  ;   update STATPROPSTG.lpwstrName with the new pointer because we released the previous one
        ObjRawSet(StatisticalData[i], "propid", NumGet(Address+A_PtrSize, "UInt"))            ; PROPID  STATPROPSTG.propid     (32-bit identifier that uniquely identifies the property)
        ObjRawSet(StatisticalData[i], "vt", NumGet(Address+A_PtrSize+4, "UShort"))            ; VARTYPE STATPROPSTG.vt         (property type)
        ObjSetCapacity(StatisticalData[i:=ObjPush(StatisticalData, {Buffer: ""})], "Buffer", A_PtrSize+8)    ; new struct STATPROPSTG
    }

    ObjPop(StatisticalData)    ; remove the last STATPROPSTG structure
    ObjRelease(IEnumSTATPROPSTG)
    Return StatisticalData
}

PropStorage_Read(IPropertyStorage, PropID)
{
    ; PROPSPEC structure
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa380070(v=vs.85).aspx
    Local PROPSPEC := ""
    VarSetCapacity(PROPSPEC, 2*A_PtrSize)
    NumPut(1, &PROPSPEC, "UInt")                     ; ULONG  PROPSPEC.ulKind
    NumPut(PropID, &PROPSPEC + A_PtrSize, "UInt")    ; PROPID PROPSPEC.propid

    ; PROPVARIANT structure
    ; https://docs.microsoft.com/es-es/windows/desktop/api/propidl/ns-propidl-tagpropvariant
    Local PROPVARIANT := {Buffer: ""}
    ObjSetCapacity(PROPVARIANT, "Buffer", A_PtrSize == 4 ? 16 : 24)
    Local Address := ObjGetAddress(PROPVARIANT, "Buffer")

    ; IPropertyStorage::ReadMultiple method
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379975(v=vs.85).aspx
    ; S_FALSE = 1 (valid syntax, but no properties were retrieved)
    If (ErrorLevel := DllCall(NumGet(NumGet(IPropertyStorage)+3*A_PtrSize), "UPtr", IPropertyStorage, "UInt", 1, "UPtr", &PROPSPEC, "UPtr", Address, "UInt"))
        Return FALSE

    ObjRawSet(PROPVARIANT, "vt", NumGet(Address, "UInt"))
    ObjRawSet(PROPVARIANT, "ptr", NumGet(Address+8, "UPtr"))

    Return PROPVARIANT
}

PropStorage_ReadPropertyName(IPropertyStorage, PropID)
{
    ; IPropertyStorage::ReadPropertyNames method
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa379976(v=vs.85).aspx
    Local Buffer := 0
    ErrorLevel := DllCall(NumGet(NumGet(IPropertyStorage)+6*A_PtrSize), "UPtr", IPropertyStorage, "UInt", 1, "UIntP", PropID, "UPtrP", Buffer, "UInt")
    Local R := ""
    If (!ErrorLevel)    ; S_OK
        R := StrGet(Buffer, "UTF-16"), DllCall("Ole32.dll\CoTaskMemFree", "UPtr", Buffer)
    Return R
}

StringFromCLSID(Address)
{
    ; StringFromGUID2 function
    ; https://msdn.microsoft.com/en-us/library/windows/desktop/ms683893(v=vs.85).aspx
    Local Buffer := ""
    VarSetCapacity(Buffer, (38 + 1) * 2)
    DllCall("Ole32.dll\StringFromGUID2", "UPtr", Address, "Str", Buffer, "Int", 38 + 1)
    Return Buffer
}

CLSIDFromString(String, ByRef CLSID)
{
    ; CLSIDFromString function
    ; https://docs.microsoft.com/en-us/windows/desktop/api/combaseapi/nf-combaseapi-clsidfromstring
    VarSetCapacity(CLSID, 16)
    Return DllCall("Ole32.dll\CLSIDFromString", "Str", String, "UPtr", &CLSID, "UInt") ? 0 : &CLSID
}

PropVariantClear(Address)
{
    Return DllCall("Ole32.dll\PropVariantClear", "UPtr", IsObject(Address) ? ObjGetAddress(Address, "Buffer") : Address)
}
I will continue investigating! :headwall:

Edit: It seems that there is a misunderstanding (or at least on my part). Read: Structured Storage | About Structured Storage | Storages and Streams.
It seems that ADS Manager uses the equivalent to FileOpen of AHK, it is exactly the same. When you use FileOpen the data is not displayed correctly because it is binary data, and not a string. This binary data represents a Storage Object, and can only be read through the IPropertySetStorage interface.
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

24 Jun 2018, 20:02

The correct way to do this is to retrieve the binary data from the Stream using FileOpen, and then call StgOpenStorageOnILockBytes to retrieve the IPropertySetStorage interface to read the data correctly.
  • For a file, ::$DATA is always the default Stream type (however, there does not seem to be another type)
  • To list all Streams in a file we must call FindFirstStreamW.
  • To read or write to a Stream we must use FileOpen.
  • To delete a Stream we must use FileDelete.
  • So... what is IPropertySetStorage for?. It is just an interface to organize the streams data in a better way, as if it were a directory/folder. You must use this interface to correctly interpret the binary data in the stream.
  • In a Stream whose content is a Stream Object, its name must start with the U+2663 character. Read: Document Properties Stream Name.
  • If the file contains Streams Objects, you must add a Stream with the name {4c8cc155-6c1e-11d1-8e41-00c04fb9386d}. Read: Control Stream.
zcooler
Posts: 455
Joined: 11 Jan 2014, 04:59

Re: Alternate Data Stream Gibberish

25 Jun 2018, 01:22

Flipeador wrote:Also i do not know why it does not retrieve {4c8cc155-6c1e-11d1-8e41-00c04fb9386d}.
It might not retrieve it cuz it seem to be a control stream and no iproperty stream.
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

25 Jun 2018, 07:06

zcooler wrote:It might not retrieve it cuz it seem to be a control stream and no iproperty stream.
Yes but no.
2.24.3 Control Stream wrote:A file that has one or more property sets associated with it through the alternate stream binding MUST have a control stream, which is an alternate stream with the name "{4c8cc155-6c1e-11d1-8e41-00c04fb9386d}". This stream MUST contain the following packet. [...]
https://msdn.microsoft.com/en-us/library/dd942541.aspx
This Stream complies with the name, but not with the content, since it is zero bytes sized, and this is not correct. We could say that it is an "incomplete" or "erroneous" Control Stream.
Edit: Although maybe if it is valid if all the data is zero.

A Control Stream is just a Stream with the name {4c8cc155-6c1e-11d1-8e41-00c04fb9386d} and whose content is the following: Reserved1 (2 bytes) + Reserved2 (2 bytes) + ApplicationState (4 bytes) + CLSID (16 bytes).
In a hexadecimal viewer, each pair of numbers represents a byte, so we would have something like this:
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Example of how to create a Control Stream in AHK:

Code: Select all

FileOpen(A_Desktop . "\~tmp.txt", "w", "UTF-8-RAW").Write("I have a Control Stream inside of me")
cs := FileOpen(A_Desktop . "\~tmp.txt:{4c8cc155-6c1e-11d1-8e41-00c04fb9386d}", "w")
cs.Length := 2 + 2 + 4 + 16
cs.Seek(0)

; Reserved1 (2 bytes): MUST be set to zero, and nonzero values MUST be rejected.
cs.WriteUShort(0x0000)

; Reserved2 (2 bytes): MUST be set to zero, and MUST be ignored.
cs.WriteUShort(0x0000)

; ApplicationState (4 bytes): An application-provided value that MUST NOT be interpreted by the OLEPS implementation. If the application did not provide a value, it SHOULD be set to zero.
cs.WriteUInt(0x00000000)

; CLSID (16 bytes): An application-provided value that MUST NOT be interpreted by the OLEPS implementation. If the application did not provide a value, it SHOULD be absent.
CLSID := ""
VarSetCapacity(CLSID, 16)
DllCall("Ole32.dll\CLSIDFromString", "Str", "{00000000-0000-0000-0000-000000000000}", "UPtr", &CLSID, "UInt")
cs.RawWrite(&CLSID+0, 16)

cs.Close()
ExitApp
Note: I have used UTF-8-RAW so that it can be displayed in ADS Manager, because if we do not specify "RAW" AutoHotkey writes the BOM, and ADS Manager does not support it, so it does not read the complete text, but rather it stops at the BOM.
Data Type Ranges: https://msdn.microsoft.com/en-us/library/s3f49ktz.aspx.

A CLSID/GUID is defined as:
C:\Program Files (x86)\Windows Kits\10\Include\10.0.16299.0\shared\guiddef.h wrote:typedef struct _GUID {
⠀⠀unsigned long Data1;⠀⠀⠀⠀⠀⠀// 4 bytes | 4⠀| 00000000
⠀⠀unsigned short Data2;⠀⠀⠀⠀ ⠀// 2 bytes | 6⠀| 0000
⠀⠀unsigned short Data3;⠀⠀⠀⠀ ⠀// 2 bytes | 8⠀| 0000
⠀⠀unsigned char Data4[ 8 ];⠀⠀⠀// 8 bytes | 16 | 0000-000000000000
} GUID;
Edit2: 5.6.3 Document Properties Stream Name. That strange character at the beginning of the Streams names is for distinguish "IProperty Streams" from other Streams (note that we always refer to the content of the Stream and not the Stream itself).
User avatar
Flipeador
Posts: 1204
Joined: 15 Nov 2014, 21:31
Location: Argentina
Contact:

Re: Alternate Data Stream Gibberish

25 Jun 2018, 11:24

See this example (AHKv2):

Code: Select all

Global Gui, Tab, Txt, Hex, Btn, LV, File

Gui := GuiCreate("-DPIScale", "Alternate Streams Viewer")
    Gui.SetFont("s10", "Segoe UI")
Tab := Gui.AddTab3("x0 y0 w512 h310", "String view|Hexadecimal view")

Tab.UseTab(1)
Txt := Gui.AddEdit("x5 y30 w500 h272 +ReadOnly -Wrap")

Tab.UseTab(2)
Hex := Gui.AddEdit("x5 y30 w500 h272 +ReadOnly -Wrap")
    Hex.SetFont("s9", "Monospac821 BT")

Tab.UseTab()
LV  := Gui.AddListView("x0 y310 w510 h200 -Multi", "Name|Size|Stream Object")
    LV.OnEvent("ItemSelect", "LVEvent")
Btn := Gui.AddButton("x0 y510 w510 h25", "Select file")
    Btn.OnEvent("Click", "SelectFile")

Gui.Show("w510 h535")
Gui.OnEvent("Close", () => ExitApp())
Return

LVEvent(GuiCtrlObj, Item, Selected)
{
    Txt.Text := "", Hex.Text := ""

    Sleep(50)
    Local Stream := LV.GetText(Item)
        ,      f := FileOpen(File . (Stream == "<unnamed>" ? "" : ":" . Stream), "r")
    If (!f)
    {
        MsgBox "The file couldn't be opened"
        Return
    }

    Txt.Text := f.Read(10000)
    f.Seek(0)
    Local HexTxt := "0001    ", Arr := [], i := 0
    While (!f.AtEOF)
    {
        HexTxt .= Format("{:02X} ", Arr[ObjPush(Arr, f.ReadUChar())])

        If (!Mod(i:=A_Index, 16))
        {
            HexTxt .= "    "
            For k, v in Arr
                HexTxt .= !v || v == 10 || v == 13 ? "." : Chr(v)
            HexTxt .= "`r`n" . Format("{:04X}    ", A_Index//16+1)
            Arr := []
        }
        If (A_Index//16 > 10000)
        {
            HexTxt .= "`r`n    ..."
            Break
        }
    }

    If (i:=Mod(i, 16))
    {
        HexTxt .= "    "
        Loop 16-i
            HexTxt .= "   "
        For k, v in Arr
            HexTxt .= !v || v == 10 || v == 13 ? "." : Chr(v)
    }

    Hex.Text := HexTxt
}

SelectFile()
{
    File := FileSelect(3)
    If (ErrorLevel)
        Return

    Txt.Text := "", Hex.Text := ""
    LV.Delete()

    Local Each := "", Stream := ""
    For Each, Stream in EnumStreams(File)
        LV.Add(, Stream.Name == "" ? "<unnamed>" : Stream.Name, StrFormatByteSize(Stream.Size), Stream.Name ~= "^\x5" ? "TRUE" : "FALSE")
    LV.ModifyCol(1, "AutoHdr")
    LV.ModifyCol(2, "AutoHdr")
}

EnumStreams(File)
{
    Local                Streams := []
        , WIN32_FIND_STREAM_DATA := ""

    VarSetCapacity(WIN32_FIND_STREAM_DATA, 8 + (260 + 36) * 2)
    Local Handle := DllCall("Kernel32.dll\FindFirstStreamW", "UPtr", &File, "UInt", 0, "UPtr", &WIN32_FIND_STREAM_DATA, "UInt", 0, "Ptr")
    If (!Handle)
        Return FALSE
    ObjPush(Streams, {Size: NumGet(&WIN32_FIND_STREAM_DATA, "Int64"), Name: SubStr(StrGet(&WIN32_FIND_STREAM_DATA + 8, "UTF-16"), 2, -6)})

    While (DllCall("Kernel32.dll\FindNextStreamW", "Ptr", Handle, "UPtr", &WIN32_FIND_STREAM_DATA, "Ptr"))
        ObjPush(Streams, {Size: NumGet(&WIN32_FIND_STREAM_DATA, "Int64"), Name: SubStr(StrGet(&WIN32_FIND_STREAM_DATA + 8, "UTF-16"), 2, -6)})

    Return Streams
} ; https://msdn.microsoft.com/en-us/library/windows/desktop/aa365741(v=vs.85).aspx

StrFormatByteSize(Number, Flags := 1)
{
    Local Buffer := ""
    VarSetCapacity(Buffer, 30 * 2, 0)
    Local R := DllCall("Shlwapi.dll\StrFormatByteSizeEx", "Int64", Number, "UInt", Flags, "UPtr", &Buffer, "UInt", 30)
    Return R == 0 ? StrGet(&Buffer, "UTF-16") : ""
} ;https://msdn.microsoft.com/en-us/library/windows/desktop/bb892884(v=vs.85).aspx

Return to “Ask for Help (v1)”

Who is online

Users browsing this forum: DIYCB and 42 guests