3

Long story short, I'm trying to query the recycle bin as another user. This is a WPF app with a service, and I need to query the recycle bin within the service... I know... We need to use NetNamedPipeBinding, and can't use the OperationBehavior attribute.

Thus far, I've had success in my Windows 10 VM with the following method:

I've then tried the same code in a Windows XP VM, and I can't get it work. As soon as SHQueryRecycleBin is called, I get an message box display stating that:

The Recycle Bin on C:\ is corrupted. Do you want to empty the Recycle Bin for this drive?

I know this is not true, as I can query the recycle bin if I'm running as tester, and also view it via windows explorer.

This looks like a common issue, and I found a Microsoft KB article. This is listed under causes:

This problem can occur when the logical drive that is referenced is an NTFS drive and there was an error in a permissions-compare operation.

I believe all my problems are related to permissions. But this is where I'm stuck. I don't know why I don't have the necessary permissions, and don't have any idea how to troubleshoot this problem. Does anyone have any ideas?

I've tried using ProcessAccessFlags.All, TOKEN_ALL_ACCESS, and AdjustTokenPrivileges as a long shot, which didn't help.

I've been testing this with LINQPad, and using this SO answer to run LINQPad under the system account. For completeness here's a snippet which should copy / paste easily, and the output running under tester and system.

Running as tester:

OpenProcessToken returned 1
AdjustTokenPrivileges = True
Got user's token as 2264
Process running as XPTESTER\tester
Impersonating XPTESTER\tester
hresult=0, cbSize=20, i64Size=160420, i64NumItems=8

Running as system:

OpenProcessToken returned 1
AdjustTokenPrivileges = True
Got user's token as 2128
Process running as NT AUTHORITY\SYSTEM
Impersonating XPTESTER\tester
hresult=0, cbSize=20, i64Size=0, i64NumItems=0

Crude testing code:

void Main()
{
    string drive = "C:";
    uint pid = 1676;
    IntPtr userToken = DuplicateProcessToken(pid);

    LUID tLuid = new LUID();
    TOKEN_PRIVILEGES NewState = new TOKEN_PRIVILEGES();
    NewState.PrivilegeCount = 1;
    NewState.Privileges.pLuid = tLuid;
    NewState.Privileges.Attributes = SE_PRIVILEGE_ENABLED;
    var adjustResult = AdjustTokenPrivileges(userToken, false, ref NewState, (uint)Marshal.SizeOf(NewState), IntPtr.Zero, IntPtr.Zero);
    Console.WriteLine("AdjustTokenPrivileges = {0}", adjustResult);

    Console.WriteLine("Got user's token as {0}", userToken);
    Console.WriteLine("Process running as {0}", WindowsIdentity.GetCurrent().Name);
    using(var impersonatedUser = WindowsIdentity.Impersonate(userToken))
    {
        var winIdentity = WindowsIdentity.GetCurrent();
        Console.WriteLine("Impersonating {0}", winIdentity.Name);   

        var result = new SHQUERYRBINFO();
        result.cbSize = Marshal.SizeOf(typeof(SHQUERYRBINFO));
        int hresult = SHQueryRecycleBin(drive, ref result);

        Console.WriteLine(
            "hresult={0}, cbSize={1}, i64Size={2}, i64NumItems={3}",
            hresult,
            result.cbSize,
            result.i64Size,
            result.i64NumItems);
    }
}

public const Int32 SE_PRIVILEGE_ENABLED = 0x00000002;

public static IntPtr DuplicateProcessToken(uint pid)
{
    IntPtr hProcess = OpenProcess(ProcessAccessFlags.All, 0, pid);  
    uint uPriv = TOKEN_ALL_ACCESS;  
    IntPtr token = IntPtr.Zero;
    int result = OpenProcessToken(hProcess, uPriv, ref token);
    Console.WriteLine("OpenProcessToken returned {0}", result);
    return token;
}

public const uint STANDARD_RIGHTS_REQUIRED = 0x00F0000;
public const uint STANDARD_RIGHTS_READ = 0x002000;
public const uint TOKEN_ASSIGN_PRIMARY = 0x0001;
public const uint TOKEN_DUPLICATE = 0x0002;
public const uint TOKEN_IMPERSONATE = 0x004;
public const uint TOKEN_QUERY = 0x0008;
public const uint TOKEN_QUERY_SOURCE = 0x0010;
public const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
public const uint TOKEN_ADJUST_GROUPS = 0x0040;
public const uint TOKEN_ADJUST_DEFAULT = 0x0080;
public const uint TOKEN_ADJUST_SESSIONID = 0x0100;
public const uint TOKEN_READ = (STANDARD_RIGHTS_REQUIRED | TOKEN_QUERY);
public const uint TOKEN_ALL_ACCESS = (STANDARD_RIGHTS_REQUIRED
    | TOKEN_ASSIGN_PRIMARY
    | TOKEN_DUPLICATE
    | TOKEN_IMPERSONATE
    | TOKEN_QUERY
    | TOKEN_QUERY_SOURCE
    | TOKEN_ADJUST_PRIVILEGES
    | TOKEN_ADJUST_GROUPS
    | TOKEN_ADJUST_DEFAULT
    | TOKEN_ADJUST_SESSIONID);

// Pack = 8 for x64
[StructLayout(LayoutKind.Sequential, Pack = 4)]
public struct SHQUERYRBINFO
{
    public int cbSize;
    public long i64Size;
    public long i64NumItems;        
}

[DllImport("shell32.dll", SetLastError = true)]
private static extern int SHQueryRecycleBin(string pszRootPath, ref SHQUERYRBINFO pSHQueryRBInfo);

[DllImport("kernel32.dll", SetLastError = true)]
public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, int blnheritHanfle, uint dwAppProcessId);

[DllImport("advapi32.dll", SetLastError = true)]
public static extern int OpenProcessToken(IntPtr processHandle, uint desiredAccess, ref IntPtr tokenHandle);

[Flags]
public enum ProcessAccessFlags : uint
{
    All = 0x001F0FFF,
    Terminate = 0x00000001,
    CreateThread = 0x00000002,
    VMOperation = 0x00000008,
    VMRead = 0x00000010,
    VMWrite = 0x00000020,
    DupHandle = 0x00000040,
    SetInformation = 0x00000200,
    QueryInformation = 0x00000400,
    Synchronize = 0x00100000
}

[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool AdjustTokenPrivileges(
    IntPtr TokenHandle,
    [MarshalAs(UnmanagedType.Bool)]bool DisableAllPrivileges,
    ref TOKEN_PRIVILEGES NewState,
    UInt32 BufferLength,
    IntPtr PreviousState,
    IntPtr ReturnLength);

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct LUID
{
  public Int32 LowPart;
  public Int32 HighPart;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct LUID_AND_ATTRIBUTES
{
  public LUID pLuid;
  public Int32 Attributes;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TOKEN_PRIVILEGES
{
  public Int32 PrivilegeCount;
  public LUID_AND_ATTRIBUTES Privileges;
}
Community
  • 1
  • 1
Darren Hale
  • 1,801
  • 4
  • 28
  • 43
  • You *know*, but you don't say *why*. Surely the real question you should be asking is how to fix the design problems that led you to this point. – Cody Gray May 31 '16 at 14:13
  • I said that because I do feel it is something that should be running under the user, not from a service running under the local system account. However, if we can move this to the service and impersonate the user, it will make our service easier to consume in our multiple clients. Trying to share as much code as possible, and reduce boilerplate code in the clients. – Darren Hale May 31 '16 at 15:11
  • 10
    As a general rule, functions do not work while impersonating. (For one thing, the HKEY_CURRENT_USER registry key does not work while impersonating, and a lot of things depend on that.) You can have the WPF program get the recycle bin information and pass it to the service. – Raymond Chen May 31 '16 at 15:17
  • 2
    Also, time to stop writing new code targetting XP. – Jonathan Potter May 31 '16 at 21:09
  • If you really can't modify the WPF application, the service could launch a new process in the context of the user to do the check on its behalf. – Harry Johnston Jun 02 '16 at 02:48

0 Answers0