Reading from Protected Process Memory

0

I'm trying to read the memory of a process.

The actual code loops through the process' memory and searches for values but this is the general idea.

I'm compiling for x64 and attempting to read x64 processes.

This code fails after the call to VirtualProtectEx with either error code 5 (ERROR_ACCESS_DENIED) or error code 487 (ERROR_INVALID_ADDRESS) depending on the process selected.

  • Am I reading the process' memory in the correct way?
  • How can VirtualProtectEx fail with access denied?
  • Are there other protection methods I haven't considered?

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Security.Principal;

namespace ReadProcessMemoryTest {
    public class Program {
        public static void Main(string[] args) {
            string processName = "ProcessName";
            IntPtr startAddress = new IntPtr(0x00000000);
            IntPtr endAddress = new IntPtr(0x7FFFFFFF);
            uint bytesToRead = 8;
            int errorCode = 0;

            // Ensure running as admin
            WindowsIdentity identity = WindowsIdentity.GetCurrent();
            WindowsPrincipal principal = new WindowsPrincipal(identity);
            if(!principal.IsInRole(WindowsBuiltInRole.Administrator)){
                throw new Exception("Not running as administrator");
            }

            // Turn on SeDebugPrivilege
            Process.EnterDebugMode();

            // Select the process
            Process process = Process.GetProcessesByName(processName)[0];

            // Get a handle to the process with all access rights
            IntPtr processHandle = OpenProcess(0x001F0FFF, 1, (uint)process.Id);

            // Check for errors
            errorCode = Marshal.GetLastWin32Error();
            if(errorCode != 0) {
                throw new Exception("OpenProcess error: " + errorCode);
            }

            // Set the protection level of these 8 bytes to execute, read and write
            uint prevProtection = 0;
            VirtualProtectEx(processHandle, startAddress, new UIntPtr(bytesToRead), 0x40, out prevProtection);

            // Check for errors
            errorCode = Marshal.GetLastWin32Error();
            if(errorCode != 0) {
                throw new Exception("VirtualProtectEx error: " + errorCode);
            }

            // Read some bytes into an array
            byte[] buffer = new byte[bytesToRead];
            IntPtr bytesRead;
            ReadProcessMemory(processHandle, startAddress, buffer, bytesToRead, out bytesRead);

            // Check for errors
            errorCode = Marshal.GetLastWin32Error();
            if(errorCode != 0) {
                throw new Exception("ReadProcessMemory error: " + errorCode);
            }

            // Close the process handle
            CloseHandle(processHandle);
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Int32 bInheritHandle, UInt32 dwProcessId);

        [DllImport("kernel32.dll")]
        public static extern Int32 CloseHandle(IntPtr hObject);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern Int32 VirtualProtectEx(IntPtr hProcess, IntPtr lpAddress, UIntPtr dwSize, uint flNewProtect, out uint lpflOldProtect);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern Int32 ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [In, Out] byte[] buffer, UInt32 size, out IntPtr lpNumberOfBytesRead);
    }
}
c#
winapi
asked on Stack Overflow Apr 26, 2016 by flau • edited Apr 26, 2016 by flau

1 Answer

1

EnterDebugMode() is not enough, you need to explicitly AdjustTokenPrivileges

using System;
using System.Runtime.InteropServices;

public class TokenManipulator
{
 [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
 internal static extern bool AdjustTokenPrivileges(IntPtr htok, bool disall,
 ref TokPriv1Luid newst, int len, IntPtr prev, IntPtr relen);
 [DllImport("kernel32.dll", ExactSpelling = true)]
 internal static extern IntPtr GetCurrentProcess();
 [DllImport("advapi32.dll", ExactSpelling = true, SetLastError = true)]
 internal static extern bool OpenProcessToken(IntPtr h, int acc, ref IntPtr phtok);
 [DllImport("advapi32.dll", SetLastError = true)]
 internal static extern bool LookupPrivilegeValue(string host, string name, ref long pluid);
 [StructLayout(LayoutKind.Sequential, Pack = 1)]
 internal struct TokPriv1Luid
 {
  public int Count;
  public long Luid;
  public int Attr;
 }
 internal const int SE_PRIVILEGE_DISABLED = 0x00000000;
 internal const int SE_PRIVILEGE_ENABLED = 0x00000002;
 internal const int TOKEN_QUERY = 0x00000008;
 internal const int TOKEN_ADJUST_PRIVILEGES = 0x00000020;
 public static bool AddPrivilege(string privilege)
 {
  try
  {
   bool retVal;
   TokPriv1Luid tp;
   IntPtr hproc = GetCurrentProcess();
   IntPtr htok = IntPtr.Zero;
   retVal = OpenProcessToken(hproc, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, ref htok);
   tp.Count = 1;
   tp.Luid = 0;
   tp.Attr = SE_PRIVILEGE_ENABLED;
   retVal = LookupPrivilegeValue(null, privilege, ref tp.Luid);
   retVal = AdjustTokenPrivileges(htok, false, ref tp, 0, IntPtr.Zero, IntPtr.Zero);
   return retVal;
  }
  catch (Exception ex)
  {
   throw ex;
  }
 }
answered on Stack Overflow Apr 18, 2020 by GuidedHacking

User contributions licensed under CC BY-SA 3.0