EncryptedPIDL [SOLVED]

by SalvaG at 2013-03-11 06:22:28

Hi all,
I’m trying to create an script to modify the path of photo screensaver. The EncryptedPIDL key has the Base64 encrypted value of the PIDL, but I cannot decode it.
I’m using CryptBinaryToString and CryptStringToBinary to encrypt/decrypt the path, but it seems I’m doing something worng (I never used c#)

AFAIK, I have to do this (source http://www.ms-windows.info/Help/windows … 19334.aspx):
For encrypting : Read Path → Get PIDL → use CryptBinaryToString
For decrypting : For encrypting : Use CryptStringToBinary → Get PIDL → Read Path

For testing, I’m trying to decode an string to get the original path, but i’m crashing the ISE on every test …

Anyone has do it before?

Here is my code
add-type -Language CSharp @“
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

public const uint CRYPT_STRING_BASE64 = 1;

public class PIDLUtils
//for encoding
public static extern long SHParseDisplayName (string szPath);

//for decoding
public static extern string SHGetPathFromIDList (long pidl);

//BOOL WINAPI CryptStringToBinary
// In LPCTSTR pszString,
// In DWORD cchString,
// In DWORD dwFlags,
// In BYTE pbBinary,
// Inout DWORD pcbBinary,
// Out DWORD pdwSkip,
// Out DWORD pdwFlags

[DllImport(“crypt32.dll”, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CryptStringToBinary([MarshalAs(UnmanagedType.LPWStr)] string pszString, uint cchString, uint dwFlags, out IntPtr pbBinary, ref uint pcbBinary, uint pdwSkip, uint pdwFlags);



$notEncrypted = “C:\temp”

$arrBytes = new-object System.IntPtr

$OK = [PIDLUtils]]]$arrBytes, [ref]$arrBytes.length, 0, 0)
if ($ok)
{ “error”}


by MattG at 2013-03-12 03:52:03
Are you asking how to convert Base64 (which is not encryption, btw :D) to a byte array? If that’s the case then you can convert all your code into a two-liner:
$unencodedBytes = [Convert]::FromBase64String($encoded)
# Optionally, print out the bytes
# ($unencodedBytes | % { [Char]$
}) -join ''
by SalvaG at 2013-03-12 05:26:33
Hi Matt

what I’m trying to do is to be able from Powershell to change the Photo screen saver path. This path is encryted / must be encrypted using CryptBinaryToString or CryptStringToBinary functions.
For the example, $encoded value is what CryptBinaryToString returns from encoding the PIDL assigned to C:\temp, and YES : it is encrypted and encoded using Base64 without headers.

How it works? I think it works like this :

For encoding : Path in clear text (i.e. c:\temp) → PIDL = PathToPIDL (Path) ( using function ??) → EncryptedPath = CryptBinaryToString (PIDL)
For decoding : Encrypted path (i.e. the $encoded value) ->PIDL = CryptStringToBinary (EncryptedPath) → Path in clear text = PIDLToPath (PIDL) (using SHGetPathFromIDList function?

All of this I think have to be made using c# or any compatible language for using with add-type cmdlet, but no idea for doing it.
Any idea?

by SalvaG at 2013-03-14 01:09:27
A friend of mine helped me for getting this. Here i share the code for all, like me, who need it.


#That string is encoded in Base64, without headers. There is a Windows API that encrypt binary arrays (the PIDL) to Base64, CryptBinaryToString. The dwFlags parameter should be set to CRYPT_STRING_BASE64.
#Our problem is the inverse, given a Base64 string (the encoded PIDL), get the decoded PIDL. There’s an API for that, too!, CryptStringToBinary. The dwFlags parameter should also be set to CRYPT_STRING_BASE64.
#I think it is not hard to write a small program that uses those API’s (and some more to get a “readable” path from the PIDL); ask in the MSDN forums to get directions if you get stuck.


#BOOL WINAPI CryptBinaryToString(
# In const BYTE pbBinary,
# In DWORD cbBinary,
# In DWORD dwFlags,
# Out_opt LPTSTR pszString,
# Inout DWORD pcchString
#BOOL WINAPI CryptStringToBinary(
# In LPCTSTR pszString,
# In DWORD cchString,
# In DWORD dwFlags,
# In BYTE pbBinary,
# Inout DWORD pcbBinary,
# Out DWORD pdwSkip,
# Out DWORD pdwFlags

add-type -Language CSharp @"

using System;
using System.Text;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;

public static class PIDLUtils
// funciones
public static extern Int32 SHGetDesktopFolder(out IShellFolder ppshf);

public static extern bool SHGetPathFromIDList(IntPtr pidl, StringBuilder pszPath);

[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CryptBinaryToString(IntPtr pcbBinary, int cbBinary, uint dwFlags, StringBuilder pszString, ref int pcchString);

[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool CryptStringToBinary(string pszString, int cchString, uint dwFlags, IntPtr pbBinary, ref int pcbBinary, ref int pdwSkip, ref int pdwFlags);

// interfaces
public interface IShellFolder
Int32 ParseDisplayName(IntPtr hwnd, IntPtr pbc, String pszDisplayName, UInt32 pchEaten, out IntPtr ppidl, UInt32 pdwAttributes);
Int32 EnumObjects(IntPtr hwnd, ESHCONTF grfFlags, out IntPtr ppenumIDList);
Int32 BindToObject(IntPtr pidl, IntPtr pbc, [In]ref Guid riid, out IntPtr ppv);
Int32 BindToStorage(IntPtr pidl, IntPtr pbc, [In]ref Guid riid, out IntPtr ppv);
Int32 CompareIDs(Int32 lParam, IntPtr pidl1, IntPtr pidl2);
Int32 CreateViewObject(IntPtr hwndOwner, [In] ref Guid riid, out IntPtr ppv);
Int32 GetAttributesOf(UInt32 cidl, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]IntPtr[] apidl, ref ESFGAO rgfInOut);
Int32 GetUIObjectOf(IntPtr hwndOwner, UInt32 cidl, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]IntPtr[] apidl, [In] ref Guid riid, UInt32 rgfReserved, out IntPtr ppv);
Int32 GetDisplayNameOf(IntPtr pidl, ESHGDN uFlags, out ESTRRET pName);
Int32 SetNameOf(IntPtr hwnd, IntPtr pidl, String pszName, ESHCONTF uFlags, out IntPtr ppidlOut);

// enumeraciones
public enum ESHCONTF

public enum ESFGAO : uint
SFGAO_CANCOPY = 0x00000001,
SFGAO_CANMOVE = 0x00000002,
SFGAO_CANLINK = 0x00000004,
SFGAO_LINK = 0x00010000,
SFGAO_SHARE = 0x00020000,
SFGAO_READONLY = 0x00040000,
SFGAO_HIDDEN = 0x00080000,
SFGAO_FOLDER = 0x20000000,
SFGAO_FILESYSTEM = 0x40000000,

public enum ESHGDN
SHGDN_NORMAL = 0x0000,

public enum ESTRRET : int
eeRRET_WSTR = 0x0000,
STRRET_CSTR = 0x0002

// constantes
private const uint CRYPT_STRING_BASE64 = 1;


// mis funciones

public static string Encode(string myClearPath)
IShellFolder folder;
IntPtr pidl;
string Pathcodificado;
Pathcodificado = “ERROR”;
string myPathTextoPlano = myClearPath;

// este es el folder del escritorio (raíz del espacio de nombres del shell)
if (SHGetDesktopFolder(out folder) == 0)
// pidl del archivo
folder.ParseDisplayName(IntPtr.Zero, IntPtr.Zero, myPathTextoPlano, 0, out pidl, 0);

// parseo el pidl para obtener sus tamaño
int k = 0;
short cb = 0;

// while ((k = Marshal.ReadInt16(pidl + cb)) > 0)
// {
// cb += (short)k;
// }

IntPtr tempIntPtr = new IntPtr(pidl.ToInt64() + cb);
while ((k = Marshal.ReadInt16(tempIntPtr)) > 0)
cb += (short)k;
tempIntPtr = new IntPtr(pidl.ToInt64() + cb);

cb += 2;

// encripto el pidl
StringBuilder sb = new StringBuilder();
int largo = 0;

CryptBinaryToString(pidl, cb, CRYPT_STRING_BASE64, null, ref largo);
sb.Capacity = largo;

if (CryptBinaryToString(pidl, cb, CRYPT_STRING_BASE64, sb, ref largo))
Pathcodificado = sb.ToString();

return Pathcodificado;


public static string Decode (string myEncryptedPath)
IShellFolder folder;
// IntPtr pidl;
string PathDescodificado;
PathDescodificado = “ERROR”;
string mypathEncriptado = myEncryptedPath;

// este es el folder del escritorio (raíz del espacio de nombres del shell)
if (SHGetDesktopFolder(out folder) == 0)

// Desencripto
int a = 0;
int b = 0;
int largo = 0;

//CryptStringToBinary(sb.ToString(), sb.Length, CRYPT_STRING_BASE64, IntPtr.Zero, ref largo, ref a, ref b);
CryptStringToBinary(mypathEncriptado, mypathEncriptado.Length, CRYPT_STRING_BASE64, IntPtr.Zero, ref largo, ref a, ref b);
// recreo el objeto
IntPtr pidl2 = Marshal.AllocCoTaskMem(largo);

StringBuilder sb = new StringBuilder();
if (CryptStringToBinary(mypathEncriptado, mypathEncriptado.Length, CRYPT_STRING_BASE64, pidl2, ref largo, ref a, ref b))
// muestro el path proveyendo el pidl reconstruído
//sb.Clear(); Only works with .NET 4
sb.Length = 0;
sb.Capacity = 261;
SHGetPathFromIDList(pidl2, sb);
PathDescodificado = sb.ToString();

return PathDescodificado;



$MyPath = “c:\Flashtool”

$Encrypted = [PIDLUtils]::Encode($MyPath)

$notEncrypted = [PIDLUtils]::Decode($Encrypted)


Edit : Now will run on Windows XP too