-1

I'm tried to invoke NtProtectVirtualMemory from my dll, that was attached to application using followed code:

typedef NTSTATUS(__stdcall* tNtProtectVirtualMemory) (HANDLE, IN OUT PVOID*, IN OUT PULONG, IN ULONG, OUT PULONG);
...
HMODULE Ntdll = GetModuleHandle("ntdll.dll");
if (!Ntdll) {
    char outtxt[64];
    sprintf(outtxt, "GetModuleHandle error %d", GetLastError());
    MessageBox(NULL, outtxt, "error", MB_OK);
}
tNtProtectVirtualMemory OrigNtProtectVirtualMemory = (tNtProtectVirtualMemory)GetProcAddress(Ntdll, "NtProtectVirtualMemory");
if (!OrigNtProtectVirtualMemory) {
    char outtxt[64];
    sprintf(outtxt, "tNtProtectVirtualMemory is null (%d)", GetLastError());
    MessageBox(NULL, outtxt, "error", MB_OK);
}
NTSTATUS sts = OrigNtProtectVirtualMemory(-1, sectionData->address, sectionData->size, protect, &oldProtect);

GetModuleHandle returns correct handle and GetProcAddress works fine. NtProtectVirtualMemory returns 0xC0000005 (STATUS_ACCESS_VIOLATION).

Now what's most interesting: VirtualProtect works without any errors:

VirtualProtect(sectionData->address, sectionData->size, protect, &oldProtect);

But I have to use exactly NtProtectVirtualMemory. Any ideas?

  • 3
    `VirtualProtect` takes the size-of-region argument by value. `NtProtectVirtualMemory` takes it by pointer - you are supposed to pass a pointer to a `ULONG` variable whose initial value is the size of the region, and which would be updated on return with the size rounded up to the nearest page boundary. Aren't you getting warnings, at least, about type mismatch? – Igor Tandetnik Nov 04 '22 at 19:29
  • 1
    In addition to the comment above, the second argument needs to be a pointer to the pointer that points to the start address to be changed. – Louis Bernard Nov 04 '22 at 22:31

1 Answers1

1

Unlike VirtualProtectEx, the NtProtectVirtualMemory function takes a pointer to the pointer that points to the address of the region whose protection is to be changed, and a pointer to the size:

ULONG oldProtect;
ULONG size = sectionData->size;
PVOID address = sectionData->address;
NTSTATUS sts = OrigNtProtectVirtualMemory((HANDLE)-1, &address, &size, protect, &oldProtect);

To add more information, the (HANDLE)-1 process handle means to take the current process handle - without the need to open the current process, I found this by reversing the ntdll.dll, but couldn't find any documentation about it.

thedemons
  • 1,139
  • 2
  • 9
  • 25
  • Thanks, it helped me. The only thing I have else changed is type of third parameter in prototype of tNtProtectVirtualMemory to `unsigned long long *` as my dll loaded into 64bit process. – CoreEntity Nov 05 '22 at 11:05
  • this is impossible found by reversing the ntdll.dll :) – RbMm Nov 08 '22 at 00:45
  • @RbMm how so? I've done that years ago and couldn't remember exactly where I found the -1 (0x7fffffff), but it's in there somewhere. – thedemons Nov 08 '22 at 02:56
  • In ntdll only stubs. For understanding what api doing need look for implementation, which is inside ntoskrnl. Possible of course try understand buy debugging - set bp on call and look for parameters..but not the best way for complex api. And really this is very well known api. Not need research it by self. Search in Google is enough. – RbMm Nov 08 '22 at 03:06