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

使用PInvoke调用ReadProcessMemory读取PEB时出现“无效句柄”错误

问题:通过PInvoke读取进程PEB的ImageBaseAddress时ReadProcessMemory返回无效句柄

我在C#中通过PInvoke调用Win32 API创建进程后,尝试读取该进程PEB中的ImageBaseAddress值,但ReadProcessMemory调用返回false执行失败。最初未能正确获取Win32错误信息,修正后得知错误为无效句柄。

代码实现

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

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);
            ReadProcessMemory(procInfo.hProcess, pPEB + 16, ImageBaseAddress, 8, out lpNumberOfBytesRead);
            string errorMessage = new Win32Exception(Marshal.GetLastWin32Error()).Message;

            Console.WriteLine("File handle: 0x{0:X16}", procInfo.hProcess);
            Console.WriteLine("PEB base address: 0x{0:X16}", pPEB);
            Console.WriteLine("Last Win32 Error: {0}", errorMessage);
        }
    }
}

相关截图

  • 截图1:程序输出与WinDBG附加到记事本进程后执行!peb命令的输出对比
  • 截图2:Visual Studio调试器截图,断点在ReadProcessMemory调用后的Console.WriteLine处,显示PEB基地址识别正确

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 14:48:16