Disabling Write-Protection

I've been learning win internals and I came across an interesting thing called Write Protection.
I know Windows relies on interrupts but I don't know any other way to do this.
I decided to try it using

__writecr0(__readcr0 & ~0x10000);

Unfortunately that didn't work for me so I decided to do it directly in the memory
I allocated memory, put opcodes there so it represents

cli
mov eax, cr0
and eax, not 0x10000
mov cr0, eax
sti

This didn't work either, I even wrote a code for it.

#include <ntifs.h>
#include <ntddk.h>

NTSTATUS NTAPI
DriverEntry(
	PDRIVER_OBJECT			DriverObject,
	PUNICODE_STRING			RegistryPath
) {
	UNREFERENCED_PARAMETER(DriverObject);
	UNREFERENCED_PARAMETER(RegistryPath);

	unsigned char Opcodes[] =
		{ 0x48, 0x0F, 0x20, 0xC0, 0x48, 0x0D, 0x00, 0x00, 0x01, 0x00, 0x48, 0x0F, 0x22, 0xC0, 0xC3 };

	KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "cr0: %I64X", __readcr0()));

	VOID* Memory = ExAllocatePool2(
		POOL_FLAG_NON_PAGED, 1024, 1337
	);
	if (Memory == NULL)
		return STATUS_INSUFFICIENT_RESOURCES;

	RtlCopyMemory(Memory, Opcodes, sizeof(Opcodes));

	((VOID (*) ())Memory)();

	KdPrintEx((DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "cr0: %I64X", __readcr0()));

	ExFreePoolWithTag(Memory, 1337);

	return STATUS_SUCCESS;
}

Still nothing, don't mind the different opcodes I tried without the REX (48h)
But I noticed that whenever I set the EIP to the address of these instructions I get a BSOD instantly when it comes to "cli"
Why do I get a BSOD when I try to disable interrupts and how else can I disable write protection if not like this?

/ EDIT /
I noticed this
*** Fatal System Error: 0x00000050
(0xFFFFC88F150D88A0,0x0000000000000011,0xFFFFC88F150D88A0,0x0000000000000002)

I guess it's because of how I allocate the memory? it crashes even if I don't try to disable interrupts and even if I do.

Why do you think you're allowed to mess with this? This is used, among other things, for implementing "copy on write" pages. The operating system needs it. A crash is entirely to be expected.

Bugcheck 0x50 is "PAGE_FAULT_IN_NON_PAGED_AREA". The memory you have allocated is not marked as "executable", so you can't jump to it. Read/write/executable pages are a huge intrusion vector, so they are not allowed.

Put another way, "don't do that".

2 Likes

Okay, well I guess I shouldn't mess with that. But I have an issue with executing shellcode in memory. I allocate memory with NonPagedPoolExecute yet I still get a bsod? It says "DRIVER_IRQL_NOT_LESS_OR_EQUAL"" And when I analyse it it says that I accessed some memory on a high IRQL? How am I supposed to set the IRQL to PASSIVE_LEVEL
It's just that I'd really like to play with opcodes and that. write a simple header file that can execute instructions like
__AMOV(EAX, ...)

If you are honestly just playing with opcodes, then go do it in user mode and wrap your bug-ridden attempts in an exception handler. See VirtualAlloc and VirtualProtect.

To execute dynamically generated code, use VirtualAlloc to allocate memory and the VirtualProtect function to grant PAGE_EXECUTE access.

Did you read what I wrote? Each and every page in virtual memory has three attributes that control what can be done with that page: Read (R), Write (W), or Execute (X). Pages in your code sections are marked RX and thus cannot be written. Pages in your data or stack sections are marked RW and thus cannot be jumped to. You should not attempt to alter this because of the extreme danger it represents. Mr. Roddy has the right advice. Do it in user mode.

And as you demonstrate above, any single instructions that have an actual need are available as compiler intrinsics. We used to have inline assembler, but since the proliferation of processor types, that's no longer practical or supported.