Windows System Software -- Consulting, Training, Development -- Unique Expertise, Guaranteed Results

Home NTDEV

More Info on Driver Writing and Debugging


The free OSR Learning Library has more than 50 articles on a wide variety of topics about writing and debugging device drivers and Minifilters. From introductory level to advanced. All the articles have been recently reviewed and updated, and are written using the clear and definitive style you've come to expect from OSR over the years.


Check out The OSR Learning Library at: https://www.osr.com/osr-learning-library/


Before Posting...

Please check out the Community Guidelines in the Announcements and Administration Category.

Very Basic question about PAGE_EXECUTE_READ

AlbertAlbert Member - All Emails Posts: 500

I was reading this article the TL;DR of which is they are patching the instructions of a loaded DLL inside of their process. It reminded me that I do it all the time in the debugger too when I want to quickly set a forced break using int 3h.

But then when I look at the VMMAP of the DLL I see that the .text section has PAGE_EXECUTE_READ , per https://docs.microsoft.com/en-us/windows/win32/memory/memory-protection-constantsMSDN that means the page cannot be modified but executed. How then does this patching work?

BaseAddress: 00007ffba06b0000
RegionSize: 0000000000001000
State: 00001000 MEM_COMMIT
Protect: 00000002 PAGE_READONLY
Type: 01000000 MEM_IMAGE

BaseAddress: 00007ffba06b1000
RegionSize: 000000000000b000
State: 00001000 MEM_COMMIT
Protect: 00000020 PAGE_EXECUTE_READ
Type: 01000000 MEM_IMAGE

BaseAddress: 00007ffba06bc000
RegionSize: 0000000000006000
State: 00001000 MEM_COMMIT
Protect: 00000002 PAGE_READONLY
Type: 01000000 MEM_IMAGE

BaseAddress: 00007ffba06c2000
RegionSize: 0000000000002000
State: 00001000 MEM_COMMIT
Protect: 00000004 PAGE_READWRITE
Type: 01000000 MEM_IMAGE

BaseAddress: 00007ffba06c4000
RegionSize: 0000000000005000
State: 00001000 MEM_COMMIT
Protect: 00000002 PAGE_READONLY
Type: 01000000 MEM_IMAGE

Comments

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,958

    They're probably changing the page protections for that page, then putting them back when they're done.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

  • 0xrepnz0xrepnz Member Posts: 59

    VirtualProtect can be used to change the page protection - This is the code: https://github.com/rasta-mouse/AmsiScanBufferBypass/blob/e04cdf476afffda69aa11971aee83e0247413307/ASBBypass/Program.cs#L22

    About the debugger - If you put a breakpoint, windbg debugger invokes WriteProcessMemory() to put the int3 breakpoint, and WriteProcessMemory() calls NtProtectVirtualMemory to change the page protection to PAGE_READWRITE before trying to write the int3 value - a callstack below:

     #   Call Site
    00  ntdll!NtProtectVirtualMemory
    01  KERNELBASE!WriteProcessMemory+0x431fc
    02  dbgeng!LiveUserDebugServices::WriteVirtual+0x38
    03  dbgeng!BaseX86MachineInfo::InsertBreakpointInstruction+0x14d
    04  dbgeng!LiveUserTargetInfo::InsertCodeBreakpoint+0x88
    05  dbgeng!CodeBreakpoint::Insert+0xa5
    06  dbgeng!InsertBreakpoints+0x836
    07  dbgeng!PrepareForExecution+0x58d
    08  dbgeng!RawWaitForEvent+0x4f
    09  dbgeng!DebugClient::WaitForEvent+0xb1
    0a  windbg!EngineLoop+0x2a8
    0b  KERNEL32!BaseThreadInitThunk+0x14
    0c  ntdll!RtlUserThreadStart+0x21
    
    - Ori Damari
  • AlbertAlbert Member - All Emails Posts: 500

    @0xrepnz said:
    VirtualProtect can be used to change the page protection - This is the code: https://github.com/rasta-mouse/AmsiScanBufferBypass/blob/e04cdf476afffda69aa11971aee83e0247413307/ASBBypass/Program.cs#L22

    About the debugger - If you put a breakpoint, windbg debugger invokes WriteProcessMemory() to put the int3 breakpoint, and WriteProcessMemory() calls NtProtectVirtualMemory to change the page protection to PAGE_READWRITE before trying to write the int3 value - a callstack below:

     #   Call Site
    00  ntdll!NtProtectVirtualMemory
    01  KERNELBASE!WriteProcessMemory+0x431fc
    02  dbgeng!LiveUserDebugServices::WriteVirtual+0x38
    03  dbgeng!BaseX86MachineInfo::InsertBreakpointInstruction+0x14d
    04  dbgeng!LiveUserTargetInfo::InsertCodeBreakpoint+0x88
    05  dbgeng!CodeBreakpoint::Insert+0xa5
    06  dbgeng!InsertBreakpoints+0x836
    07  dbgeng!PrepareForExecution+0x58d
    08  dbgeng!RawWaitForEvent+0x4f
    09  dbgeng!DebugClient::WaitForEvent+0xb1
    0a  windbg!EngineLoop+0x2a8
    0b  KERNEL32!BaseThreadInitThunk+0x14
    0c  ntdll!RtlUserThreadStart+0x21
    

    Thanks for the tip, I woulsn't expect WriteProcessmemory to do that, it is an API to write and not change bits(unlike VirtualProtect). If WriteProcessmemory is able to change whatever protection the memory owner set, then it is a bit counter intuitive isn't it?

    @Tim_Roberts said:
    They're probably changing the page protections for that page, then putting them back when they're done.

    Yes I thought so, but he article seems to not mention that at all, and the scripts used aren't there.

  • 0xrepnz0xrepnz Member Posts: 59

    If WriteProcessmemory is able to change whatever protection the memory owner set, then it is a bit counter intuitive isn't it

    Yes - I wasn't expecting WriteProcessMemory to do this either. Remember that WriteProcessMemory is the win32 API but the native system call (NtWriteVirtualMemory) will not try to change the memory protection

    Yes I thought so, but he article seems to not mention that at all, and the scripts used aren't there.

    The scripts used in the article simply load the .NET code I sent above: https://github.com/rasta-mouse/AmsiScanBufferBypass/blob/e04cdf476afffda69aa11971aee83e0247413307/ASBBypass/Program.cs#L22

    As you can see they use VirtualProtect (0x40 - PAGE_EXECUTE_READWRITE)

        private static void PatchAmsi(byte[] patch)
        {
            try
            {
                var lib = Win32.LoadLibrary("amsi.dll");
                var addr = Win32.GetProcAddress(lib, "AmsiScanBuffer");
    
                uint oldProtect;
                Win32.VirtualProtect(addr, (UIntPtr)patch.Length, 0x40, out oldProtect);
    
                Marshal.Copy(patch, 0, addr, patch.Length);
            }
            catch (Exception e)
            {
                Console.WriteLine(" [x] {0}", e.Message);
                Console.WriteLine(" [x] {0}", e.InnerException);
            }
        }
    
    - Ori Damari
  • MBond2MBond2 Member Posts: 328

    I have not looked at the code myself, but that sounds like a compatibility shim - something that got added to WriteProcessMemory to help keep existing programs working as this verification became more rigorous. I can't quite place it though, because read and execute were conflated by hardware for a long time, but I don't remember anything about write and execute. And VirtualProtect is a fundamental API. Perhaps it had something to do with 16 bit support? But that's a total guess

  • Tim_RobertsTim_Roberts Member - All Emails Posts: 13,958

    I don't understand why you all are surprised by this. If you have permission to open another process, then there's no particular reason for WriteProcessMemory to fail in this case. The writing process would have to that the page permissions anyway, and it already has permission to do so. It seems quite natural to me.

    Tim Roberts, [email protected]
    Providenza & Boekelheide, Inc.

Sign In or Register to comment.

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

Upcoming OSR Seminars
OSR has suspended in-person seminars due to the Covid-19 outbreak. But, don't miss your training! Attend via the internet instead!
Developing Minifilters 24 May 2021 Live, Online
Writing WDF Drivers 14 June 2021 Live, Online
Internals & Software Drivers 27 September 2021 Live, Online
Kernel Debugging 15 November 2021 Live, Online