You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PowerShell中获取不同权限进程的实际当前工作目录?

获取跨权限进程的当前工作目录(CWD)

Windows的进程权限隔离机制导致跨权限读取CWD存在限制,以下是针对不同场景的可行解决方案:

权限基础规则

  • 非管理员进程:仅能读取同权限(非管理员)进程的CWD,无法读取管理员进程的CWD(Windows安全限制,无合法绕过方式)。
  • 管理员进程:默认可读取非管理员进程的CWD;读取其他管理员/系统进程的CWD需额外启用SeDebugPrivilege权限。

解决方案

1. 用CIM/WMI实现(最简方案)

适用于大多数常规场景,无需手动处理API调用:

function Get-ProcessCwd {
    param(
        [Parameter(Mandatory=$true)]
        [int]$ProcessId
    )
    $process = Get-CimInstance Win32_Process -Filter "ProcessId = $ProcessId"
    if ($process) {
        return $process.CurrentDirectory
    }
    Write-Error "进程ID $ProcessId 不存在或无权限访问"
}

# 示例调用
Get-ProcessCwd -ProcessId 13896
  • 权限限制:
    • 非管理员:仅能获取非管理员进程的CWD。
    • 管理员:可获取所有非管理员进程的CWD,部分管理员进程可能因权限问题返回空,需启用SeDebugPrivilege。

2. 管理员专属:启用SeDebugPrivilege后调用Native API

若管理员身份下仍无法读取目标进程CWD(如遇到错误998),需临时启用SeDebugPrivilege权限,再通过NtQueryInformationProcess获取:

# 定义Native API调用类
Add-Type @"
using System;
using System.Runtime.InteropServices;
using System.ComponentModel;

public class ProcessCwdUtils {
    [DllImport("ntdll.dll")]
    private static extern int NtQueryInformationProcess(
        IntPtr ProcessHandle,
        int ProcessInformationClass,
        IntPtr ProcessInformation,
        int ProcessInformationLength,
        out int ReturnLength);

    [DllImport("kernel32.dll")]
    private static extern IntPtr OpenProcess(
        uint dwDesiredAccess,
        bool bInheritHandle,
        int dwProcessId);

    [DllImport("kernel32.dll")]
    private static extern bool CloseHandle(IntPtr hObject);

    [DllImport("kernel32.dll")]
    private static extern bool IsWow64Process(IntPtr hProcess, out bool isWow64);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
    private static extern int GetFinalPathNameByHandle(IntPtr hFile, char[] lpszFilePath, int cchFilePath, uint dwFlags);

    private const int ProcessCurrentDirectory = 28;
    private const uint PROCESS_QUERY_INFORMATION = 0x0400;
    private const uint PROCESS_VM_READ = 0x0010;

    public static string GetProcessCurrentDirectory(int pid) {
        IntPtr processHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid);
        if (processHandle == IntPtr.Zero) {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        try {
            bool isWow64 = false;
            if (IntPtr.Size == 4) {
                IsWow64Process(processHandle, out isWow64);
            }

            int bufferSize = isWow64 ? 8 : IntPtr.Size;
            IntPtr buffer = Marshal.AllocHGlobal(bufferSize);
            try {
                int returnLength;
                int status = NtQueryInformationProcess(processHandle, ProcessCurrentDirectory, buffer, bufferSize, out returnLength);
                if (status != 0) {
                    throw new Exception($"NtQueryInformationProcess失败,状态码: {status}");
                }

                IntPtr directoryHandle = isWow64 ? Marshal.ReadIntPtr(buffer, 4) : Marshal.ReadIntPtr(buffer);
                if (directoryHandle == IntPtr.Zero) {
                    return null;
                }

                char[] pathBuffer = new char[260];
                int pathLength = GetFinalPathNameByHandle(directoryHandle, pathBuffer, pathBuffer.Length, 0);
                if (pathLength == 0) {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }

                return new string(pathBuffer, 0, pathLength).TrimEnd('\0');
            } finally {
                Marshal.FreeHGlobal(buffer);
            }
        } finally {
            CloseHandle(processHandle);
        }
    }
}
"@

# 启用SeDebugPrivilege的函数
Add-Type @"
using System;
using System.Runtime.InteropServices;
using System.Security.Principal;

public class PrivilegeManager {
    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, ref IntPtr TokenHandle);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref LUID lpLuid);

    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool DisableAllPrivileges, ref TOKEN_PRIVILEGES NewState, uint BufferLength, IntPtr PreviousState, IntPtr ReturnLength);

    [DllImport("kernel32.dll")]
    private static extern IntPtr GetCurrentProcess();

    [DllImport("kernel32.dll")]
    private static extern bool CloseHandle(IntPtr hObject);

    [StructLayout(LayoutKind.Sequential)]
    private struct LUID {
        public uint LowPart;
        public int HighPart;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct LUID_AND_ATTRIBUTES {
        public LUID Luid;
        public uint Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct TOKEN_PRIVILEGES {
        public uint PrivilegeCount;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
        public LUID_AND_ATTRIBUTES[] Privileges;
    }

    private const uint TOKEN_ADJUST_PRIVILEGES = 0x0020;
    private const uint TOKEN_QUERY = 0x0008;
    private const uint SE_PRIVILEGE_ENABLED = 0x00000002;

    public static bool EnableSeDebugPrivilege() {
        if (!new WindowsPrincipal(WindowsIdentity.GetCurrent()).IsInRole(WindowsBuiltInRole.Administrator)) {
            return false;
        }

        IntPtr tokenHandle = IntPtr.Zero;
        try {
            if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref tokenHandle)) {
                return false;
            }

            LUID luid = new LUID();
            if (!LookupPrivilegeValue(null, "SeDebugPrivilege", ref luid)) {
                return false;
            }

            TOKEN_PRIVILEGES tp = new TOKEN_PRIVILEGES();
            tp.PrivilegeCount = 1;
            tp.Privileges = new LUID_AND_ATTRIBUTES[1];
            tp.Privileges[0].Luid = luid;
            tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;

            if (!AdjustTokenPrivileges(tokenHandle, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero)) {
                return false;
            }

            return true;
        } finally {
            if (tokenHandle != IntPtr.Zero) {
                CloseHandle(tokenHandle);
            }
        }
    }
}
"@

# 使用示例
if ([PrivilegeManager]::EnableSeDebugPrivilege()) {
    try {
        [ProcessCwdUtils]::GetProcessCurrentDirectory(13896)
    } catch {
        Write-Error "获取CWD失败: $_"
    }
} else {
    Write-Error "需要管理员权限才能启用SeDebugPrivilege"
}
  • 说明:
    • 必须以管理员身份运行PowerShell。
    • 启用SeDebugPrivilege后,可读取所有进程(包括系统进程、其他管理员进程)的CWD。
    • 错误998(ERROR_NOACCESS)的核心原因是未获取足够的进程访问权限或未启用SeDebugPrivilege。

3. 非管理员的限制说明

非管理员身份下,Windows严格禁止读取管理员进程的CWD,这是系统权限隔离的核心机制,不存在合法绕过方法。任何声称无需提权即可读取管理员进程CWD的方案均不可信。

常见问题排查

  • 错误998:
    • 非管理员:目标进程为管理员进程,无权限读取。
    • 管理员:未启用SeDebugPrivilege,或目标进程为受保护的系统进程。
  • Get-CimInstance返回空:目标进程已终止,或当前身份无权限访问该进程。

内容的提问来源于stack exchange,提问作者Vic Zhang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 20:44:53