簡體   English   中英

PInvoke ReadProcessMemory - 讀取 PEB 返回 false 但不會引發 Win32 異常

[英]PInvoke ReadProcessMemory - Reading PEB returns false but does not raise Win32 exception

我正在嘗試使用 P/Invoke 在 C# 中使用Win32 API 調用創建的進程的PEB中讀取ImageBaseAddress的值。 然而,對ReadProcessMemory的調用返回 false,表明它失敗了。 使用 Visual Studio 調試器進行檢查,我看到傳遞給 function 的字節數組填充為零。 但是沒有引發 Win32Exception,打印Win32Exception(Marshal.GetLastWin32Error()).Message給出了The operation completed successfully.

下面是我的代碼,以及兩個截圖來幫助說明我的觀點。

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.ComponentModel;

namespace ReadProcess
{
    class Program
    {
        [StructLayout(LayoutKind.Sequential)]
        internal struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public int dwProcessId;
            public int dwThreadId;
        }
        [StructLayout(LayoutKind.Sequential)]
        internal struct STARTUPINFO
        {
            uint cb;
            IntPtr lpReserved;
            IntPtr lpDesktop;
            IntPtr lpTitle;
            uint dwX;
            uint dwY;
            uint dwXSize;
            uint dwYSize;
            uint dwXCountChars;
            uint dwYCountChars;
            uint dwFillAttributes;
            uint dwFlags;
            ushort wShowWindow;
            ushort cbReserved;
            IntPtr lpReserved2;
            IntPtr hStdInput;
            IntPtr hStdOutput;
            IntPtr hStdErr;
        }
        [StructLayout(LayoutKind.Sequential)]
        internal struct PROCESS_BASIC_INFORMATION
        {
            public IntPtr ExitStatus;
            public IntPtr PebAddress;
            public IntPtr AffinityMask;
            public IntPtr BasePriority;
            public IntPtr UniquePID;
            public IntPtr InheritedFromUniqueProcessId;
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [Out] byte[] lpBuffer, int dwSize, out IntPtr lpNumberOfBytesRead);
        [DllImport("ntdll.dll", SetLastError = true)]
        static extern int NtQueryInformationProcess(IntPtr processHandle, int processInformationClass, IntPtr processInformation, uint processInformationLength, IntPtr returnLength);
        [DllImport("kernel32.dll")]
        static extern bool CloseHandle(IntPtr hProcess);
        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool CreateProcess(IntPtr lpApplicationName, string lpCommandLine, IntPtr lpProcAttribs, IntPtr lpThreadAttribs, bool bInheritHandles, uint dwCreateFlags, IntPtr lpEnvironment, IntPtr lpCurrentDir, [In] ref STARTUPINFO lpStartinfo, out PROCESS_INFORMATION lpProcInformation);


        public static IntPtr GetPEBAddress(IntPtr hProcess)
        {
            //Allocate memory for a new PROCESS_BASIC_INFORMATION structure
            IntPtr pbi = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)));
            //Allocate memory for a long
            IntPtr outLong = Marshal.AllocHGlobal(sizeof(long));
            IntPtr outPtr = IntPtr.Zero;

            int queryStatus = 0;

            //Store API call success in a boolean
            queryStatus = NtQueryInformationProcess(hProcess, 0, pbi, (uint)Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)), outLong);

            //Close handle and free allocated memory
            CloseHandle(hProcess);
            Marshal.FreeHGlobal(outLong);

            //STATUS_SUCCESS = 0, so if API call was successful querySuccess should contain 0 ergo we reverse the check.
            if (queryStatus == 0)
                outPtr = Marshal.PtrToStructure<PROCESS_BASIC_INFORMATION>(pbi).PebAddress;

            //Free allocated space
            Marshal.FreeHGlobal(pbi);

            //Return pointer to PEB base address
            return outPtr;
        }

        static void Main(string[] args)
        {
            STARTUPINFO startInfo = new STARTUPINFO();
            PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();

            CreateProcess((IntPtr)0, "notepad", (IntPtr)0, (IntPtr)0, false, 0x00000004, (IntPtr)0, (IntPtr)0, ref startInfo, out procInfo);

            byte[] ImageBaseAddress = new byte[IntPtr.Size];
            IntPtr lpNumberOfBytesRead;
            IntPtr pPEB = GetPEBAddress(procInfo.hProcess);
            Console.WriteLine(ReadProcessMemory(procInfo.hProcess, pPEB + 16, ImageBaseAddress, 8, out lpNumberOfBytesRead));

            Console.WriteLine("PEB base address: 0x{0:X16}", pPEB);

            string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;
            Console.WriteLine("Last Win32 Error: {0}", errorMessage);
        }
    }
}

這是程序的 output 與!peb peb 的 output 相比,WinDBG session 附加到記事本進程。 WinDBG 和控制台

這是 Visual Studio 調試器的屏幕截圖,斷點位於Console.WriteLine調用上,在調用ReadProcessMemory之后。 Visual Studio 調試器

您的 PInvoke 代碼中有許多明顯的錯誤。

  • 您的主要問題:您看不到錯誤代碼,因為您沒有在調用后立即檢查GetLastWin32Error 它不能等待,因為它會很快被其他調用覆蓋。
  • 對進程和線程句柄使用SafeFileHandle ,並使用using而不是CloseHandle來處理它們。
  • ReadProcessMemory的調用應使用IntPtr.Size來匹配緩沖區數組。
  • 使用CreateProcessWCharSet.Unicode
  • 除非您知道自己在做什么,否則不要嘗試手動編組。 您有 memory 到處泄漏,並且沒有必要,因為您可以在必要時傳入stringref參數。
  • 一些IntPtruint類型也是錯誤的。
[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
    public SafeFileHandle hProcess;
    public SafeFileHandle hThread;
    public int dwProcessId;
    public int dwThreadId;
}

[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_BASIC_INFORMATION
{
    public int ExitStatus;
    public IntPtr PebAddress;
    public IntPtr AffinityMask;
    public int BasePriority;
    public IntPtr UniquePID;
    public IntPtr InheritedFromUniqueProcessId;
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool ReadProcessMemory (
    SafeFileHandle hProcess,
    IntPtr lpBaseAddress,
    [Out] byte[] lpBuffer,
    int dwSize,
    out IntPtr lpNumberOfBytesRead);

[DllImport("ntdll.dll", SetLastError = true)]
static extern int NtQueryInformationProcess(
    SafeFileHandle processHandle,
    int processInformationClass,
    out PROCESS_BASIC_INFORMATION processInformation,
    int processInformationLength,
    out int returnLength);

[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Unicode)]
static extern bool CreateProcessW (
    string lpApplicationName,
    string lpCommandLine,
    IntPtr lpProcAttribs,
    IntPtr lpThreadAttribs,
    bool bInheritHandles,
    uint dwCreateFlags,
    string lpEnvironment,
    string lpCurrentDir,
    in STARTUPINFO lpStartinfo,
    out PROCESS_INFORMATION lpProcInformation);
public static IntPtr GetPEBAddress(SafeFileHandle hProcess)
{
    if(!NtQueryInformationProcess(hProcess, 0, out var pbi, Marshal.SizeOf<PROCESS_BASIC_INFORMATION>(), out var outLong))
        throw new Win32Exception(Marshal.GetLastWin32Error());

    return pbi.PebAddress;
}

static void Main(string[] args)
{
    STARTUPINFO startInfo = new STARTUPINFO();
    PROCESS_INFORMATION procInfo = new PROCESS_INFORMATION();

    try
    {
        if(!CreateProcessW(null, "notepad", IntPtr.Zero, IntPtr.Zero, false, 0x00000004, null, null, in startInfo, out var procInfo)
            throw new Win32Exception(Marshal.GetLastWin32Error());

        using (procInfo.hProcess)
        using (procInfo.hThread)
        {
            byte[] ImageBaseAddress = new byte[IntPtr.Size];
            IntPtr pPEB = GetPEBAddress(procInfo.hProcess);
            if(!ReadProcessMemory(procInfo.hProcess, pPEB + 16, ImageBaseAddress, IntPtr.Size, out var lpNumberOfBytesRead))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            Console.WriteLine("PEB base address: 0x{0:X16}", pPEB);
            // use ImageBaseAddress here ??
        }
    }
    catch(Exception ex)
    {
        Console.WriteLine("Last Win32 Error: {0}", ex.Message);
    }
}

理想情況下,您將使用正常的Process.Create而不是自己這樣做。 然后你不需要管理那個句柄,你只需要把Process放到using

static void Main(string[] args)
{
    try
    {
        using (var proc = Process.Start("notepad.exe"))
        {
            byte[] ImageBaseAddress = new byte[IntPtr.Size];
            IntPtr pPEB = GetPEBAddress(proc.Handle);
            if(!ReadProcessMemory(proc.Handle, pPEB + 16, ImageBaseAddress, IntPtr.Size, out var lpNumberOfBytesRead))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            Console.WriteLine("PEB base address: 0x{0:X16}", pPEB);
            // use ImageBaseAddress here ??
        }
    }
    catch(Exception ex)
    {
        Console.WriteLine("Last Win32 Error: {0}", ex.Message);
    }
}

我必須說,這整個事情似乎相當可疑,因為您似乎正在嘗試讀取 PEB 塊。 但是 PEB 塊可以並且確實在 Windows 的不同版本之間發生變化。 特別是,偏移量16 似乎與 x64 或 x86 中的任何內容都不匹配。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM