使用NtWow64AllocateVirtualMemory64的句柄无效

时间:2018-03-31 07:39:39

标签: c++ c winapi

测试代码:

typedef NTSTATUS(NTAPI *ntalloc64t)(HANDLE, PULONG64, ULONG64, PULONG64, ULONG, ULONG);
#define NtCurrentProcess() ( (HANDLE)(PULONG64) -1 )  ;
int _tmain(int argc, _TCHAR* argv[])
{
ULONG64 dwSize = 0x1000;

ntalloc64t ntalloc64f = (ntalloc64t)(GetProcAddress(GetModuleHandleA("ntdll"), "NtWow64AllocateVirtualMemory64"));
PVOID pvBaseAddress;
pvBaseAddress = (PVOID)NULL;
long kk = ntalloc64f((HANDLE)GetCurrentProcess(), (PULONG64)&pvBaseAddress, 0, (PULONG64)&dwSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
}

我在WOW64下运行。这将返回0xc0000008,这意味着句柄无效。将-1作为句柄传递时也不起作用,这应该指示WinAPI使用当前进程。

2 个答案:

答案 0 :(得分:3)

NtWow64AllocateVirtualMemory64未记录,但您可以假设其参数与NtAllocateVirtualMemory几乎相同,并且MSDN对基本地址参数说明了这一点:

  

指向将接收已分配页面区域的基址的变量的指针。 如果此参数的初始值为非NULL,则从指定的虚拟地址开始分配区域,向下舍入到下一个主机页面大小地址边界。如果此参数的初始值为NULL,则操作系统将确定分配区域的位置。

你正在隐藏你的演员的错误; (PULONG64)&pvBaseAddress指向来自堆栈某处的pvBaseAddress = (PVOID)NULL 32个未定义位的32位零,如果这些位不是全零,那么您要求的是特定的基址可能没有!

删除尽可能多的演员阵容,它应该开始工作:

typedef NTSTATUS(NTAPI *ntalloc64t)(HANDLE, PULONG64, ULONG64, PULONG64, ULONG, ULONG);
ntalloc64t ntalloc64f = (ntalloc64t) GetProcAddress(GetModuleHandleA("ntdll"), "NtWow64AllocateVirtualMemory64");
// TODO: if (!ntalloc64f) not wow64, handle error...

HANDLE hTargetProcess = OpenProcess(...);
ULONG64 base = 0, size = 0x1000;
long nts = ntalloc64f(hTargetProcess, &base, 0, &size, MEM_COMMIT|MEM_RESERVE, PAGE_READWRITE);
printf("status=%d base=%I64x size=%I64x\n", nts, base, size);

答案 1 :(得分:1)

当我们从32位ntdll.dll调用NtWow64AllocateVirtualMemory64时(它只存在于wow64 ntdll.dll中)whNtWow64AllocateVirtualMemory64(64位函数)在wow64.dll内调用。我从win10汇编代码重建:

struct Wow64AllocateVirtualMemory64_Stack {
    ULONG   ProcessHandle;// !!! unsigned !!
    ULONG   BaseAddress;
    ULONG64 ZeroBits;
    ULONG   RegionSize;
    ULONG   AllocationType;
    ULONG   Protection;
};

NTSTATUS 
NTAPI 
whNtWow64AllocateVirtualMemory64(Wow64AllocateVirtualMemory64_Stack* p)
{
    return NtAllocateVirtualMemory(
        (HANDLE)(ULONG_PTR)p->ProcessHandle,
        (void**)(ULONG_PTR)p->BaseAddress, 
        p->ZeroBits, 
        (PSIZE_T)(ULONG_PTR)p->RegionSize,
        p->AllocationType, 
        p->Protection);
}

这里的关键点是HANDLE在32位代码中是32位大小,在64位代码中是64位大小。因此,32位句柄值必须扩展为64位代码中的64位句柄。但它可以符号扩展。当然,当我们扩展正32bit值(实际处理句柄) - 没有什么不同,结果将是相同的。但是当我们扩展负值-1时 - 零延伸的结果将是0xFFFFFFFF(这是无效的句柄)。符号扩展的结果 - 将是0xFFFFFFFFFFFFFFFF - 纠正当前进程的伪句柄。 windows 10使用零扩展句柄:

enter image description here 结果我们不能在这里使用-1(GetCurrentProcess()

win8使用sign-extend句柄:

enter image description here

但是没有任何意义使用这个api在wow64进程中分配内存。确实 - 如果我们接受任何内存基地址,或者< 4GB - 我们可以使用NtAllocateVirtualMemoryVirtualAlloc[Ex]。所以这个功能只有在我们想要在基地址> = 4Gb分配内存的情况下才有意义。但这在wow64过程中是不可能的。 - 系统保留所有高于> = 4G的内存空间。 wow64bit进程的典型内存映射(使用/LARGEADDRESSAWARE选项)

enter image description here

这里只显示64位ntdll.dll,并保留所有其他内存。 没有/LARGEADDRESSAWARE选项保留范围从7FFF0000开始。此保留的内存也无法释放 - 在通话NtFreeVirtualMemory(从64位进程)我收到STATUS_INVALID_PAGE_PROTECTION错误。

所以没有意义使用这个api来分配内部自我(和任何另一个wow64进程)。只有我们想要在64位进程中分配内存而不是简单地分配,但是在高于4GB的范围内。我甚至不知道这可能需要哪个目标 - 为什么< 4GB内存基数,可以通常NtAllocateVirtualMemoryVirtualAlloc[Ex]分配。并且很有趣,没有相关的NtWow64FreeVirtualMemory64 api - 所以不可能的免费分配记忆。当然可能写入base-Independed(并且因此没有导入)64位代码,嵌入在32位进程中,通过64个调用门调用它,这段代码可以从64位ntdll调用函数(并且只能从它调用)并返回。这是可能的,但已经是另一个故事了