Sign in to follow this  
Followers 0
techiebabel

Encoding a PIDL for EncryptedPIDL

2 posts in this topic

I've been working on a script to configure the Photo Screensaver in Windows for a bunch of computers at work. I've got it working where I just copy the EncryptedPIDL registry key from a workstation where I manually did the configuration, but part of me is just not happy with that... it just doesn't feel right giving up and not being able to generate that key based on a path that I want to set the screensaver to.

I've got a function written to 'decrypt' or decode the value stored in the registry by referencing a whole bunch of work done by a bunch of brilliant folks that were doing similar work, but not quite the same. Here is that (working) function:

Func _EncryptedPIDLDecode($sEncryptedPIDL)
    Local Const $CRYPT_STRING_BASE64 = 0x00000001

    ; Determine length of EncryptedPIDL
    Local $iLength = StringLen($sEncryptedPIDL)

    ; Create structure to hold the attributes returned and used during decoding
    Local $tDecodeAttribs = DllStructCreate("int Size; int Skipped;int Flags")
    If @error Then Return SetError(1,0,'')

    ; Get pointers to the attributes returned and used during decoding
    Local $pDecodeSize    = DllStructGetPtr($tDecodeAttribs, 'Size')
    Local $pDecodeSkipped = DllStructGetPtr($tDecodeAttribs, 'Skipped')
    Local $pDecodeFlags   = DllStructGetPtr($tDecodeAttribs, 'Flags')

    ; Get the values needed for decoding
    DllCall('Crypt32.dll','int','CryptStringToBinary', _
                'str',$sEncryptedPIDL, _
                'int',$iLength, _
                'int',$CRYPT_STRING_BASE64, _
                'ptr',0, _
                'ptr',$pDecodeSize, _
                'ptr',$pDecodeSkipped, _
                'ptr',$pDecodeFlags)
    If @error Then Return SetError(2,0,'')

    ; Create structure to hold the PIDL and path
    Local $tDecodeResults = DllStructCreate("char PIDL[" & DllStructGetData($tDecodeAttribs, 'Size') + 1 & "]; char Path[261]")
    If @error Then Return SetError(3,0,'')

    ; Get pointers to the PIDL and  path
    Local $pDecodePIDL = DllStructGetPtr($tDecodeResults, 'PIDL')
    Local $pDecodePath = DllStructGetPtr($tDecodeResults, 'Path')

    ; Get the PIDL
    DllCall('Crypt32.dll','int','CryptStringToBinary', _
                'str',$sEncryptedPIDL, _
                'int',$iLength, _
                'int',$CRYPT_STRING_BASE64, _
                'ptr',$pDecodePIDL, _
                'ptr',$pDecodeSize, _
                'ptr',$pDecodeSkipped, _
                'ptr',$pDecodeFlags)
    If @error Then Return SetError(4,0,'')

    ; Get the Path
    DllCall('Shell32.dll','int','SHGetPathFromIDList', _
                'ptr',$pDecodePIDL, _
                'ptr',$pDecodePath)
    If @error Then Return SetError(5,0,'')

    ; Return the Path
    Return DllStructGetData($tDecodeResults, 'Path')
EndFunc

But I just can't get the encode or 'encrypt' function working. I'm stuck. I believe I know the basic process... take the string of the path, get the PIDL from that string, and then encode the PIDL using BASE64. I believe I'm stuck at getting PIDL or perhaps the size of the PIDL. I can get a memory pointer, but my brain starts to get fuzzy where I try to determine the size of the data contained to provide to the CryptBinaryToString function of Crypt32.dll.

If you run the code, you'll see something is getting converted, but the string is far too short.

Here is what I have so far (not working):

Func _EncryptedPIDLEncode($sPath)
    Local Const $CRYPT_STRING_BASE64 = 0x00000001
    Local Const $IID_ISHELLFOLDER = "{000214E6-0000-0000-C000-000000000046}"
    Local Const $TAG_ISHELLFOLDER = _
        "ParseDisplayName hresult(hwnd;ptr;wstr;dword*;ptr*;dword*);" & _       ; These methods accepts PIDLs that are relative to the parent folder
        "EnumObjects hresult(hwnd;dword;ptr*);" & _
        "BindToObject hresult(ptr;ptr;struct*;ptr*);" & _
        "BindToStorage hresult(ptr;ptr;ptr;ptr*);" & _
        "CompareIDs hresult(lparam;ptr;ptr);" & _
        "CreateViewObject hresult(hwnd;ptr;ptr*);" & _
        "GetAttributesOf hresult(uint;struct*;ulong*);" & _
        "GetUIObjectOf hresult(hwnd;uint;struct*;struct*;uint*;ptr*);" & _
        "GetDisplayNameOf hresult(ptr;uint;struct*);" & _
        "SetNameOf hresult(hwnd;ptr;wstr;dword;ptr*);"

    ; Get the pointer to the root IShelllFolder interface (the Desktop folder)
    Local $aIShellRoot = DllCall('Shell32.dll', "int", "SHGetDesktopFolder", "ptr*", 0)
    If @error Or Not $aIShellRoot[1] Then
        Return SetError(1,0,'')
    Else
        Local $pIShellRoot = $aIShellRoot[1]
    EndIf

    ; Create an object for the IShellFolder interface
    Local $oIShellFolder = ObjCreateInterface( $pIShellRoot, $IID_ISHELLFOLDER, $TAG_ISHELLFOLDER )

    ; Create a structure to store the pointer to the PIDL
    Local $tPIDLPtr = DllStructCreate("ptr")
    If @error Then Return SetError(2,0,'')

    ; Get the pointer to the PIDL of the specified path
    Local $pPIDL
    $oIShellFolder.ParseDisplayName(0, 0, $sPath, 0, $pPIDL, 0)
    If @error Or Not $pPIDL Then
        Return SetError(3,0,'')
    Else
        DllStructSetData($tPIDLPtr, 1, $pPIDL)
        Local $iSize = DllStructGetSize($tPIDLPtr)
    EndIf

    ; Create a structure to store attributes returned and used during encoding
    Local $tEncodeAttribs = DllStructCreate("int Size")
    If @error Then Return SetError(3,0,'')

    ; Get pointers to the attributes returned and used during encoding
    Local $pEncodeSize = DllStructGetPtr($tEncodeAttribs, 'Size')

    ; Get the size for encoding
    DllCall('Crypt32.dll','int','CryptBinaryToString', _
                'ptr',$pPIDL, _
                'int',$iSize, _
                'int',$CRYPT_STRING_BASE64, _
                'ptr',0, _
                'ptr',$pEncodeSize)
    If @error Then Return SetError(4,0,'')

    ; Create structure to hold the EncryptedPIDL
    Local $tEncodeResults = DllStructCreate("char EncryptedPIDL[" & DllStructGetData($tEncodeAttribs, 'Size') + 1 & "]")
    If @error Then Return SetError(5,0,'')

    ; Get pointers to the PIDL and  path
    Local $pEncryptedPIDL = DllStructGetPtr($tEncodeResults, 'EncryptedPIDL')

    ; Get EncrtypedPIDL
    DllCall('Crypt32.dll','int','CryptBinaryToString', _
                'ptr',$pPIDL, _
                'int',$iSize, _
                'int',$CRYPT_STRING_BASE64, _
                'ptr',$pEncryptedPIDL, _
                'ptr',$pEncodeSize)
    If @error Then Return SetError(6,0,'')

    ; Return the EncrtypedPIDL
    Return DllStructGetData($tEncodeResults, 'EncryptedPIDL')
EndFunc

I have also tried a different variation of the same theme using ILCreateFromPathW, but I get the same results. Here is that variation (also not working).

Func _EncryptedPIDLEncode2($sPath)
    Local Const $CRYPT_STRING_BASE64 = 0x00000001

    ; Get the PIDL
    Local $aPIDL = DllCall('Shell32.dll','ptr','ILCreateFromPathW','wstr',$sPath)
    If @error Then Return SetError(1,0,'')

    ; Create a structure to hold the pointer to PIDL so we can get the size
    Local $tPIDL = DllStructCreate('ptr')
    If @error Then Return SetError(2,0,'')

    ; Get the size of the PIDL
    DllStructSetData($tPIDL, 1, $aPIDL[0])
    Local $iSize = DllStructGetSize($tPIDL)

    ; Create a structure to hold the size of the EncryptedPIDL
    Local $tEncryptedPIDLSize = DllStructCreate('int')
    If @error Then Return SetError(3,0,'')

    ; Get the size of the EncrtypedPIDL
    Local $aResults = DllCall('Crypt32.dll','int','CryptBinaryToString', _
                                'ptr',$aPIDL[0], _
                                'int',$iSize, _
                                'int',$CRYPT_STRING_BASE64, _
                                'ptr',0, _
                                'ptr',DllStructGetPtr($tEncryptedPIDLSize,1))
    If @error Then Return SetError(4,0,'')

    ; Create a structure to hold the EncryptedPIDL
    Local $tEncryptedPIDL = DllStructCreate('char[' & DllStructGetData($tEncryptedPIDLSize,1)+1 & ']')
    If @error Then Return SetError(5,0,'')

    ; Get the EncryptedPIDL
    Local $aResults = DllCall('Crypt32.dll','int','CryptBinaryToString', _
                                'ptr',$aPIDL[0], _
                                'int',$iSize, _
                                'int',$CRYPT_STRING_BASE64, _
                                'ptr',DllStructGetPtr($tEncryptedPIDL,1), _
                                'ptr',DllStructGetPtr($tEncryptedPIDLSize,1))
    If @error Then Return SetError(6,0,'')

    ; Return the EncrtypedPIDL
    Return DllStructGetData($tEncryptedPIDL, 1)
EndFunc

Any insight?

Share this post


Link to post
Share on other sites



_EncryptedPIDLEncode2

Since CryptBinaryToString is already implemented by trancexx in this post, we can concentrate about the PIDLs.

This is the procedure:

Func _EncryptedPIDLEncode2( $sPath )

  ; Get PIDL pointer from path
  Local $pPidl = _WinAPI_ShellILCreateFromPath( $sPath )

  ; Get size of PIDL structure as the pointer is pointing to
  Local $iSize = ILGetSize( $pPidl )

  ; Create a byte structure from memory location defined by PIDL pointer
  Local $tPidl = DllStructCreate( "byte[" & $iSize & "]", $pPidl )

  ; Get binary string from byte structure
  Local $sBinStr = DllStructGetData( $tPidl, 1 )

  ; Create Base64 encoded string
  Local $sEncoded = _Base64Encode( $sBinStr )

  Return $sEncoded

EndFunc

techiebabel, In your PIDL size calculations you are calculating the size of the pointer. Not the size of the PIDL structure as the pointer is pointing to.

Full code including functions and test:

#include <WinAPIShellEx.au3>

$sTestPath = "C:\WINDOWS\Temp\StdPictures" ; Must be an existing folder
ConsoleWrite( @CRLF & "Test path: $sTestPath = " & $sTestPath & @CRLF & @CRLF )

$sEncodedPidl = _EncryptedPIDLEncode2( $sTestPath )
ConsoleWrite( "Base64 encoded PIDL: $sEncodedPidl = " & $sEncodedPidl & @CRLF )

$sDecodedPath = _EncryptedPIDLDecode( $sEncodedPidl )
ConsoleWrite( "Base64 decoded path: $sDecodedPath = " & $sDecodedPath & @CRLF & @CRLF )


Func _EncryptedPIDLEncode2( $sPath )

  ; Get PIDL pointer from path
  Local $pPidl = _WinAPI_ShellILCreateFromPath( $sPath )

  ; Get size of PIDL structure as the pointer is pointing to
  Local $iSize = ILGetSize( $pPidl )

  ; Create a byte structure from memory location defined by PIDL pointer
  Local $tPidl = DllStructCreate( "byte[" & $iSize & "]", $pPidl )

  ; Get binary string from byte structure
  Local $sBinStr = DllStructGetData( $tPidl, 1 )

  ; Create Base64 encoded string
  Local $sEncoded = _Base64Encode( $sBinStr )

  Return $sEncoded

EndFunc


Func ILGetSize( $pidl )
  Local $aRet = DllCall( "shell32.dll", "uint", "ILGetSize", "ptr", $pidl )
  If @error Then Return SetError(1, 0, 0)
  Return $aRet[0]
EndFunc

;By trancexx: http://www.autoitscript.com/forum/index.php?showtopic=81332
Func _Base64Encode($input)
    
    $input = Binary($input)
    
    Local $struct = DllStructCreate("byte[" & BinaryLen($input) & "]")
    
    DllStructSetData($struct, 1, $input)
    
    Local $strc = DllStructCreate("int")
    
    Local $a_Call = DllCall("Crypt32.dll", "int", "CryptBinaryToString", _
            "ptr", DllStructGetPtr($struct), _
            "int", DllStructGetSize($struct), _
            "int", 1, _
            "ptr", 0, _
            "ptr", DllStructGetPtr($strc))
    
    If @error Or Not $a_Call[0] Then
        Return SetError(1, 0, "") ; error calculating the length of the buffer needed
    EndIf
    
    Local $a = DllStructCreate("char[" & DllStructGetData($strc, 1) & "]")
    
    $a_Call = DllCall("Crypt32.dll", "int", "CryptBinaryToString", _
            "ptr", DllStructGetPtr($struct), _
            "int", DllStructGetSize($struct), _
            "int", 1, _
            "ptr", DllStructGetPtr($a), _
            "ptr", DllStructGetPtr($strc))
    
    If @error Or Not $a_Call[0] Then
        Return SetError(2, 0, ""); error encoding
    EndIf
    
    Return DllStructGetData($a, 1)

EndFunc   ;==>_Base64Encode

Func _EncryptedPIDLDecode($sEncryptedPIDL)
    Local Const $CRYPT_STRING_BASE64 = 0x00000001

    ; Determine length of EncryptedPIDL
    Local $iLength = StringLen($sEncryptedPIDL)

    ; Create structure to hold the attributes returned and used during decoding
    Local $tDecodeAttribs = DllStructCreate("int Size; int Skipped;int Flags")
    If @error Then Return SetError(1,0,'')

    ; Get pointers to the attributes returned and used during decoding
    Local $pDecodeSize    = DllStructGetPtr($tDecodeAttribs, 'Size')
    Local $pDecodeSkipped = DllStructGetPtr($tDecodeAttribs, 'Skipped')
    Local $pDecodeFlags   = DllStructGetPtr($tDecodeAttribs, 'Flags')

    ; Get the values needed for decoding
    DllCall('Crypt32.dll','int','CryptStringToBinary', _
                'str',$sEncryptedPIDL, _
                'int',$iLength, _
                'int',$CRYPT_STRING_BASE64, _
                'ptr',0, _
                'ptr',$pDecodeSize, _
                'ptr',$pDecodeSkipped, _
                'ptr',$pDecodeFlags)
    If @error Then Return SetError(2,0,'')

    ; Create structure to hold the PIDL and path
    Local $tDecodeResults = DllStructCreate("char PIDL[" & DllStructGetData($tDecodeAttribs, 'Size') + 1 & "]; char Path[261]")
    If @error Then Return SetError(3,0,'')

    ; Get pointers to the PIDL and  path
    Local $pDecodePIDL = DllStructGetPtr($tDecodeResults, 'PIDL')
    Local $pDecodePath = DllStructGetPtr($tDecodeResults, 'Path')

    ; Get the PIDL
    DllCall('Crypt32.dll','int','CryptStringToBinary', _
                'str',$sEncryptedPIDL, _
                'int',$iLength, _
                'int',$CRYPT_STRING_BASE64, _
                'ptr',$pDecodePIDL, _
                'ptr',$pDecodeSize, _
                'ptr',$pDecodeSkipped, _
                'ptr',$pDecodeFlags)
    If @error Then Return SetError(4,0,'')

    ; Get the Path
    DllCall('Shell32.dll','int','SHGetPathFromIDList', _
                'ptr',$pDecodePIDL, _
                'ptr',$pDecodePath)
    If @error Then Return SetError(5,0,'')

    ; Return the Path
    Return DllStructGetData($tDecodeResults, 'Path')
EndFunc

Output in Scite console:

Test path: $sTestPath = C:\WINDOWS\Temp\StdPictures

Base64 encoded PIDL: $sEncodedPidl = FAAfUOBP0CDqOmkQotgIACswMJ0ZAC9DOlwAAAAAAAAAAAAAAAAAAAAAAAAAPAAx
AAAAAAA6Rbw2EABXSU5ET1dTACYAAwAEAO++JTjxY0RFjCkUAAAAVwBJAE4ARABP
AFcAUwAAABYANAAxAAAAAABERRowEABUZW1wAAAgAAMABADvviU48WNERRowFAAA
AFQAZQBtAHAAAAAUAEYAMQAAAAAAQ0WSqhAAU1REUElDfjEAAC4AAwAEAO++Q0WS
qkRFKCMUAAAAUwB0AGQAUABpAGMAdAB1AHIAZQBzAAAAGAAAAA==

Base64 decoded path: $sDecodedPath = C:\WINDOWS\Temp\StdPictures
1 person likes this

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0