Read other process current directory in C#
Asked Answered
L

2

11

I am trying to get current working directory of selected process. I am sure that it is possible, because tools like Process Explorer can show this information. I found out that this information is stored in PEB.

Moreover I found Oleksiy blog post: http://eazfuscator.blogspot.com/2011/06/reading-environment-variables-from.html, but all my modifications are totally unless.

So my question is how can I read this information in C#? If Oleksiy code is a good way, how should I modify his code to get this information?

Lizzettelizzie answered 19/4, 2013 at 18:7 Comment(5)
This is not something apps should be doing to each other. The only valid use would be some sort diagnostic tool.Forgery
You are right, I need this for Security DepartmentLizzettelizzie
You mean Insecurity department?Chrissie
The information is not documented. Any technique you find will not be supported.Forgery
You probably have to read the other processes memory; msdn.microsoft.com/en-us/library/windows/desktop/… or do some kind of WinAPI call that gives the CWD given a process id.Justinejustinian
H
19

As other pointed out, this is completely undocumented, but here is a piece of code that does it (as of today, it seems to work). Of course, it needs to run as admin. Note it can work for any process bitness, 32-bit or 64-bit (some processess may report an access denied, even running as admin), with any compilation target (x86 or x64). It also is capable of reading the command line. Use at your own risk!

// All offset values below have been tested on Windows 7 & 8 only
// but you can use WinDbg "dt ntdll!_PEB" command and search for ProcessParameters offset to find the truth, depending on the OS version
public static class ProcessUtilities
{
    public static string GetCurrentDirectory(int processId)
    {
        return GetProcessParametersString(processId, Environment.Is64BitOperatingSystem ? 0x38 : 0x24);
    }

    public static string GetCurrentDirectory(this Process process)
    {
        if (process == null)
            throw new ArgumentNullException("process");

        return GetCurrentDirectory(process.Id);
    }

    public static string GetCommandLine(int processId)
    {
        return GetProcessParametersString(processId, Environment.Is64BitOperatingSystem ? 0x70 : 0x40);
    }

    public static string GetCommandLine(this Process process)
    {
        if (process == null)
            throw new ArgumentNullException("process");

        return GetCommandLine(process.Id);
    }

    private static string GetProcessParametersString(int processId, int offset)
    {
        IntPtr handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId);
        if (handle == IntPtr.Zero)
            throw new Win32Exception(Marshal.GetLastWin32Error());

        int processParametersOffset = Environment.Is64BitOperatingSystem ? 0x20 : 0x10;
        try
        {
            if (Environment.Is64BitOperatingSystem && !Environment.Is64BitProcess) // are we running in WOW?
            {
                PROCESS_BASIC_INFORMATION_WOW64 pbi = new PROCESS_BASIC_INFORMATION_WOW64();
                int hr = NtWow64QueryInformationProcess64(handle, 0, ref pbi, Marshal.SizeOf(pbi), IntPtr.Zero);
                if (hr != 0)
                    throw new Win32Exception(hr);

                long pp = 0;
                hr = NtWow64ReadVirtualMemory64(handle, pbi.PebBaseAddress + processParametersOffset, ref pp, Marshal.SizeOf(pp), IntPtr.Zero);
                if (hr != 0)
                    throw new Win32Exception(hr);

                UNICODE_STRING_WOW64 us = new UNICODE_STRING_WOW64();
                hr = NtWow64ReadVirtualMemory64(handle, pp + offset, ref us, Marshal.SizeOf(us), IntPtr.Zero);
                if (hr != 0)
                    throw new Win32Exception(hr);

                if ((us.Buffer == 0) || (us.Length == 0))
                    return null;

                string s = new string('\0', us.Length / 2);
                hr = NtWow64ReadVirtualMemory64(handle, us.Buffer, s, us.Length, IntPtr.Zero);
                if (hr != 0)
                    throw new Win32Exception(hr);

                return s;
            }
            else // we are running with the same bitness as the OS, 32 or 64
            {
                PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
                int hr = NtQueryInformationProcess(handle, 0, ref pbi, Marshal.SizeOf(pbi), IntPtr.Zero);
                if (hr != 0)
                    throw new Win32Exception(hr);

                IntPtr pp = new IntPtr();
                if (!ReadProcessMemory(handle, pbi.PebBaseAddress + processParametersOffset, ref pp, new IntPtr(Marshal.SizeOf(pp)), IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                UNICODE_STRING us = new UNICODE_STRING();
                if (!ReadProcessMemory(handle, pp + offset, ref us, new IntPtr(Marshal.SizeOf(us)), IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                if ((us.Buffer == IntPtr.Zero) || (us.Length == 0))
                    return null;

                string s = new string('\0', us.Length / 2);
                if (!ReadProcessMemory(handle, us.Buffer, s, new IntPtr(us.Length), IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                return s;
            }
        }
        finally
        {
            CloseHandle(handle);
        }
    }

    private const int PROCESS_QUERY_INFORMATION = 0x400;
    private const int PROCESS_VM_READ = 0x10;

    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESS_BASIC_INFORMATION
    {
        public IntPtr Reserved1;
        public IntPtr PebBaseAddress;
        public IntPtr Reserved2_0;
        public IntPtr Reserved2_1;
        public IntPtr UniqueProcessId;
        public IntPtr Reserved3;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct UNICODE_STRING
    {
        public short Length;
        public short MaximumLength;
        public IntPtr Buffer;
    }

    // for 32-bit process in a 64-bit OS only
    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESS_BASIC_INFORMATION_WOW64
    {
        public long Reserved1;
        public long PebBaseAddress;
        public long Reserved2_0;
        public long Reserved2_1;
        public long UniqueProcessId;
        public long Reserved3;
    }

    // for 32-bit process in a 64-bit OS only
    [StructLayout(LayoutKind.Sequential)]
    private struct UNICODE_STRING_WOW64
    {
        public short Length;
        public short MaximumLength;
        public long Buffer;
    }

    [DllImport("ntdll.dll")]
    private static extern int NtQueryInformationProcess(IntPtr ProcessHandle, int ProcessInformationClass, ref PROCESS_BASIC_INFORMATION ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref IntPtr lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [MarshalAs(UnmanagedType.LPWStr)] string lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);

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

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

    // for 32-bit process in a 64-bit OS only
    [DllImport("ntdll.dll")]
    private static extern int NtWow64QueryInformationProcess64(IntPtr ProcessHandle, int ProcessInformationClass, ref PROCESS_BASIC_INFORMATION_WOW64 ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);

    [DllImport("ntdll.dll")]
    private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, ref long lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);

    [DllImport("ntdll.dll")]
    private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, ref UNICODE_STRING_WOW64 lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);

    [DllImport("ntdll.dll")]
    private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, [MarshalAs(UnmanagedType.LPWStr)] string lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);
}
Herculie answered 22/4, 2013 at 8:41 Comment(3)
I think there are 4 possible combinations of 32 vs 64-bit processes. You also need IsWow64Process() to check if the target process is 32-bit. Oh joy ;)Haha
@HansPassant - I don't think so. The code is made to handle the 3 combinations - not 64 on 32 I admit :-), have you tested it or looked at it carefully?Herculie
NtWow64ReadVirtualMemory64 returns neither a HRESULT nor a Win32 error code. Use RtlNtStatusToDosError to convert the result signal to a throwable error code.Gauss
A
9

"Simon Mourier" source are work fine in OS,Executor,Target is 32bit. But others are not. So I Add source to handle all combination of 32, 64bit. There are 5 possible combination of 32, 64bit process. First, os,executor,target are 32bit. Second, os is 64bit, executor, target are combination of 32,64bit process. This code is work fine in My notebook Win7 64Bit OS, 32,64bit Process & target 32,64bit Process, WinXp 32bit , exeutor,target are 32bit. But, Use at your own risk!

// ref: http://www.microsoft.com/whdc/system/Sysinternals/MoreThan64proc.mspx
public enum PROCESSINFOCLASS : int
{
    ProcessBasicInformation = 0, // 0, q: PROCESS_BASIC_INFORMATION, PROCESS_EXTENDED_BASIC_INFORMATION
    ProcessWow64Information = 26, // q: ULONG_PTR
}
[Flags]
public enum PEB_OFFSET
{
    CurrentDirectory,
    //DllPath,
    //ImagePathName,
    CommandLine,
    //WindowTitle,
    //DesktopInfo,
    //ShellInfo,
    //RuntimeData,
    //TypeMask = 0xffff,
    //Wow64 = 0x10000,
};

public class Is64BitChecker
{
    [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool IsWow64Process(
        [In] IntPtr hProcess,
        [Out] out bool wow64Process
    );

    public static bool GetProcessIsWow64(IntPtr hProcess)
    {
        if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) ||
            Environment.OSVersion.Version.Major >= 6)
        {
            bool retVal;
            if (!IsWow64Process(hProcess, out retVal))
            {
                return false;
            }
            return retVal;
        }
        else
        {
            return false;
        }
    }

    public static bool InternalCheckIsWow64()
    {
        if ((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1) ||
            Environment.OSVersion.Version.Major >= 6)
        {
            using (Process p = Process.GetCurrentProcess())
            {
                bool retVal;
                if (!IsWow64Process(p.Handle, out retVal))
                {
                    return false;
                }
                return retVal;
            }
        }
        else
        {
            return false;
        }
    }
}

// All offset values below have been tested on Windows 7 & 8 only
// but you can use WinDbg "dt ntdll!_PEB" command and search for ProcessParameters offset to find the truth, depending on the OS version
public static class ProcessUtilities
{
    public static readonly bool Is64BitProcess = IntPtr.Size > 4;
    public static readonly bool Is64BitOperatingSystem = Is64BitProcess || Is64BitChecker.InternalCheckIsWow64();

    public static string GetCurrentDirectory(int processId)
    {
        return GetProcessParametersString(processId, PEB_OFFSET.CurrentDirectory);
    }

    public static string GetCurrentDirectory(this Process process)
    {
        if (process == null)
            throw new ArgumentNullException("process");

        return GetCurrentDirectory(process.Id);
    }

    #region GetCommandLine
    //public static string GetCommandLine(int processId)
    //{
    //    return null;// GetProcessParametersString(processId, Is64BitOperatingSystem ? 0x70 : 0x40);
    //}

    //public static string GetCommandLine(this Process process)
    //{
    //    if (process == null)
    //        throw new ArgumentNullException("process");

    //    return GetCommandLine(process.Id);
    //} 
    #endregion

    private static string GetProcessParametersString(int processId, PEB_OFFSET Offset)
    {
        IntPtr handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, processId);
        if (handle == IntPtr.Zero)
            throw new Win32Exception(Marshal.GetLastWin32Error());

        bool IsWow64Process = Is64BitChecker.InternalCheckIsWow64();
        bool IsTargetWow64Process = Is64BitChecker.GetProcessIsWow64(handle);
        bool IsTarget64BitProcess = Is64BitOperatingSystem && !IsTargetWow64Process;

        long offset = 0;
        long processParametersOffset = IsTarget64BitProcess ? 0x20 : 0x10;
        switch (Offset)
        {
            case PEB_OFFSET.CurrentDirectory:
                offset = IsTarget64BitProcess ? 0x38 : 0x24;
                break;
            case PEB_OFFSET.CommandLine:
            default:
                return null;
        }

        try
        {
            long pebAddress = 0;
            if (IsTargetWow64Process) // OS : 64Bit, Cur : 32 or 64, Tar: 32bit
            {
                IntPtr peb32 = new IntPtr();

                int hr = NtQueryInformationProcess(handle, (int)PROCESSINFOCLASS.ProcessWow64Information, ref peb32, IntPtr.Size, IntPtr.Zero);
                if (hr != 0) throw new Win32Exception(hr);
                pebAddress = peb32.ToInt64();

                IntPtr pp = new IntPtr();
                if (!ReadProcessMemory(handle, new IntPtr(pebAddress + processParametersOffset), ref pp, new IntPtr(Marshal.SizeOf(pp)), IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                UNICODE_STRING_32 us = new UNICODE_STRING_32();
                if (!ReadProcessMemory(handle, new IntPtr(pp.ToInt64() + offset), ref us, new IntPtr(Marshal.SizeOf(us)), IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                if ((us.Buffer == 0) || (us.Length == 0))
                    return null;

                string s = new string('\0', us.Length / 2);
                if (!ReadProcessMemory(handle, new IntPtr(us.Buffer), s, new IntPtr(us.Length), IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                return s;
            }
            else if (IsWow64Process)//Os : 64Bit, Cur 32, Tar 64
            {
                PROCESS_BASIC_INFORMATION_WOW64 pbi = new PROCESS_BASIC_INFORMATION_WOW64();
                int hr = NtWow64QueryInformationProcess64(handle, (int)PROCESSINFOCLASS.ProcessBasicInformation, ref pbi, Marshal.SizeOf(pbi), IntPtr.Zero);
                if (hr != 0) throw new Win32Exception(hr);
                pebAddress = pbi.PebBaseAddress;

                long pp = 0;
                hr = NtWow64ReadVirtualMemory64(handle, pebAddress + processParametersOffset, ref pp, Marshal.SizeOf(pp), IntPtr.Zero);
                if (hr != 0)
                    throw new Win32Exception(hr);

                UNICODE_STRING_WOW64 us = new UNICODE_STRING_WOW64();
                hr = NtWow64ReadVirtualMemory64(handle, pp + offset, ref us, Marshal.SizeOf(us), IntPtr.Zero);
                if (hr != 0)
                    throw new Win32Exception(hr);

                if ((us.Buffer == 0) || (us.Length == 0))
                    return null;

                string s = new string('\0', us.Length / 2);
                hr = NtWow64ReadVirtualMemory64(handle, us.Buffer, s, us.Length, IntPtr.Zero);
                if (hr != 0)
                    throw new Win32Exception(hr);

                return s;
            }
            else// Os,Cur,Tar : 64 or 32
            {
                PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
                int hr = NtQueryInformationProcess(handle, (int)PROCESSINFOCLASS.ProcessBasicInformation, ref pbi, Marshal.SizeOf(pbi), IntPtr.Zero);
                if (hr != 0) throw new Win32Exception(hr);
                pebAddress = pbi.PebBaseAddress.ToInt64();

                IntPtr pp = new IntPtr();
                if (!ReadProcessMemory(handle, new IntPtr(pebAddress + processParametersOffset), ref pp, new IntPtr(Marshal.SizeOf(pp)), IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                UNICODE_STRING us = new UNICODE_STRING();
                if (!ReadProcessMemory(handle, new IntPtr((long)pp + offset), ref us, new IntPtr(Marshal.SizeOf(us)), IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                if ((us.Buffer == IntPtr.Zero) || (us.Length == 0))
                    return null;

                string s = new string('\0', us.Length / 2);
                if (!ReadProcessMemory(handle, us.Buffer, s, new IntPtr(us.Length), IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());

                return s;
            }
        }
        finally
        {
            CloseHandle(handle);
        }
    }

    private const int PROCESS_QUERY_INFORMATION = 0x400;
    private const int PROCESS_VM_READ = 0x10;

    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESS_BASIC_INFORMATION
    {
        public IntPtr Reserved1;
        public IntPtr PebBaseAddress;
        public IntPtr Reserved2_0;
        public IntPtr Reserved2_1;
        public IntPtr UniqueProcessId;
        public IntPtr Reserved3;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct UNICODE_STRING
    {
        public short Length;
        public short MaximumLength;
        public IntPtr Buffer;
    }

    // for 32-bit process in a 64-bit OS only
    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESS_BASIC_INFORMATION_WOW64
    {
        public long Reserved1;
        public long PebBaseAddress;
        public long Reserved2_0;
        public long Reserved2_1;
        public long UniqueProcessId;
        public long Reserved3;
    }

    // for 32-bit process
    [StructLayout(LayoutKind.Sequential)]
    private struct UNICODE_STRING_WOW64
    {
        public short Length;
        public short MaximumLength;
        public long Buffer;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct UNICODE_STRING_32
    {
        public short Length;
        public short MaximumLength;
        public int Buffer;
    }

    [DllImport("ntdll.dll")]
    private static extern int NtQueryInformationProcess(IntPtr ProcessHandle, int ProcessInformationClass, ref PROCESS_BASIC_INFORMATION ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);

    //ProcessWow64Information, // q: ULONG_PTR
    [DllImport("ntdll.dll")]
    private static extern int NtQueryInformationProcess(IntPtr ProcessHandle, int ProcessInformationClass, ref IntPtr ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref IntPtr lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING_32 lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);

    //[DllImport("kernel32.dll", SetLastError = true)]
    //private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, ref UNICODE_STRING_WOW64 lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [MarshalAs(UnmanagedType.LPWStr)] string lpBuffer, IntPtr dwSize, IntPtr lpNumberOfBytesRead);

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

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

    // for 32-bit process in a 64-bit OS only
    [DllImport("ntdll.dll")]
    private static extern int NtWow64QueryInformationProcess64(IntPtr ProcessHandle, int ProcessInformationClass, ref PROCESS_BASIC_INFORMATION_WOW64 ProcessInformation, int ProcessInformationLength, IntPtr ReturnLength);

    [DllImport("ntdll.dll")]
    private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, ref long lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);

    [DllImport("ntdll.dll")]
    private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, ref UNICODE_STRING_WOW64 lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);

    [DllImport("ntdll.dll")]
    private static extern int NtWow64ReadVirtualMemory64(IntPtr hProcess, long lpBaseAddress, [MarshalAs(UnmanagedType.LPWStr)] string lpBuffer, long dwSize, IntPtr lpNumberOfBytesRead);
}
Anything answered 24/5, 2014 at 7:53 Comment(1)
Thank you so much for writing this up!Belligerent

© 2022 - 2024 — McMap. All rights reserved.