[英]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 附加到記事本進程。
這是 Visual Studio 調試器的屏幕截圖,斷點位於Console.WriteLine
調用上,在調用ReadProcessMemory
之后。
您的 PInvoke 代碼中有許多明顯的錯誤。
GetLastWin32Error
。 它不能等待,因為它會很快被其他調用覆蓋。SafeFileHandle
,並使用using
而不是CloseHandle
來處理它們。ReadProcessMemory
的調用應使用IntPtr.Size
來匹配緩沖區數組。CreateProcessW
和CharSet.Unicode
string
和ref
參數。IntPtr
和uint
類型也是錯誤的。[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.