Module memory
Source - HEAP_FLAGS
- HeapMemory
- 堆内存操作
- InProcessMemory
- 进程内内存操作
- PAGE_PROTECTION_FLAGS
- VIRTUAL_ALLOCATION_TYPE
- VIRTUAL_FREE_TYPE
- HEAP_CREATE_ALIGN_16
- HEAP_CREATE_ENABLE_EXECUTE
- HEAP_CREATE_ENABLE_TRACING
- HEAP_CREATE_HARDENED
- HEAP_CREATE_SEGMENT_HEAP
- HEAP_DISABLE_COALESCE_ON_FREE
- HEAP_FREE_CHECKING_ENABLED
- HEAP_GENERATE_EXCEPTIONS
- HEAP_GROWABLE
- HEAP_MAXIMUM_TAG
- HEAP_NONE
- HEAP_NO_SERIALIZE
- HEAP_PSEUDO_TAG_FLAG
- HEAP_REALLOC_IN_PLACE_ONLY
- HEAP_TAG_SHIFT
- HEAP_TAIL_CHECKING_ENABLED
- HEAP_ZERO_MEMORY
- MEM_COMMIT
- MEM_DECOMMIT
- MEM_FREE
- MEM_LARGE_PAGES
- MEM_RELEASE
- MEM_REPLACE_PLACEHOLDER
- MEM_RESERVE
- MEM_RESERVE_PLACEHOLDER
- MEM_RESET
- MEM_RESET_UNDO
- PAGE_ENCLAVE_DECOMMIT
- PAGE_ENCLAVE_MASK
- PAGE_ENCLAVE_SS_FIRST
- PAGE_ENCLAVE_SS_REST
- PAGE_ENCLAVE_THREAD_CONTROL
- PAGE_ENCLAVE_UNVALIDATED
- PAGE_EXECUTE
- PAGE_EXECUTE_READ
- PAGE_EXECUTE_READWRITE
- PAGE_EXECUTE_WRITECOPY
- PAGE_GRAPHICS_COHERENT
- PAGE_GRAPHICS_EXECUTE
- PAGE_GRAPHICS_EXECUTE_READ
- PAGE_GRAPHICS_EXECUTE_READWRITE
- PAGE_GRAPHICS_NOACCESS
- PAGE_GRAPHICS_NOCACHE
- PAGE_GRAPHICS_READONLY
- PAGE_GRAPHICS_READWRITE
- PAGE_GUARD
- PAGE_NOACCESS
- PAGE_NOCACHE
- PAGE_READONLY
- PAGE_READWRITE
- PAGE_REVERT_TO_FILE_MAP
- PAGE_TARGETS_INVALID
- PAGE_TARGETS_NO_UPDATE
- PAGE_WRITECOMBINE
- PAGE_WRITECOPY
- SEC_64K_PAGES
- SEC_COMMIT
- SEC_FILE
- SEC_IMAGE_NO_EXECUTE
- SEC_LARGE_PAGES
- SEC_NOCACHE
- SEC_PARTITION_OWNER_HANDLE
- SEC_PROTECTED_IMAGE
- SEC_RESERVE
- SEC_WRITECOMBINE
- get_process_heap
- 查询调用进程的默认堆的句柄。 然后,可以在对堆函数的后续调用中使用此句柄。
如果函数成功,则返回值是调用进程的堆的句柄。
如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 get_last_error。
get_process_heap 函数获取调用进程的默认堆的句柄。 进程可以使用此句柄从进程堆分配内存,而无需先使用 heap_create 函数创建专用堆。
Windows Server 2003 和 Windows XP: 若要为进程的默认堆启用低碎片堆,请使用 get_process_heap 返回的句柄调用 heap_set_information 函数。
- heap_alloc
- 从堆中分配内存块。 分配的内存不可移动。
如果函数成功,则返回值是指向已分配内存块的指针。
如果函数失败并且您尚未指定 HEAP_GENERATE_EXCEPTIONS,则返回值为 NULL。
如果函数失败并且已指定 HEAP_GENERATE_EXCEPTIONS,则函数可能会生成下表中列出的任一异常。 特定例外取决于堆损坏的性质。 有关详细信息,请参阅 GetExceptionCode。
异常代码 | 说明
STATUS_NO_MEMORY | 由于缺少可用内存或堆损坏,分配尝试失败。
STATUS_ACCESS_VIOLATION | 由于堆损坏或函数参数不正确,分配尝试失败。
如果函数失败,则它不会调用 set_last_error。 应用程序无法调用 get_last_error 以获取扩展错误信息。
如果 heap_alloc 函数成功,它将分配至少请求的内存量。
若要从进程的默认堆分配内存,请将 heap_alloc 与 get_process_heap 函数返回的句柄一起使用。
若要释放 heap_alloc 分配的内存块,请使用 heap_free 函数。
heap_alloc 分配的内存不可移动。 heap_alloc 返回的地址在释放或重新分配内存块之前有效;内存块不需要锁定。 由于系统无法压缩专用堆,因此它可能会碎片化。
heap_alloc 返回的内存对齐方式在 WinNT.h 中MEMORY_ALLOCATION_ALIGNMENT:
- heap_create
- 创建可由调用进程使用的专用堆对象。 函数在进程的虚拟地址空间中保留空间,并为此块的指定初始部分分配物理存储。
如果函数成功,则返回值是新创建的堆的句柄。
如果函数失败,则返回值为 NULL。 要获得更多的错误信息,请调用 get_last_error。
heap_create 函数创建一个专用堆对象,调用进程可以使用 heap_alloc 函数从该对象分配内存块。 初始大小确定最初为堆分配的已提交页数。 最大大小确定保留页的总数。 这些页面在进程的虚拟地址空间中创建一个块,堆可以增长到其中。 如果 heap_alloc 的请求超过已提交页面的当前大小,则如果物理存储可用,则会自动从此预留空间提交其他页面。
Windows Server 2003 和 Windows XP: 默认情况下,新创建的专用堆是标准堆。 若要启用低碎片堆,请使用专用堆的句柄调用 heap_set_information 函数。
专用堆对象的内存只能由创建它的进程访问。 如果动态链接库 (DLL) 创建专用堆,则会在调用 DLL 的进程地址空间中创建堆,并且只有该进程才能访问该堆。
系统使用专用堆中的内存来存储堆支持结构,因此并非所有指定的堆大小都可供进程使用。 例如,如果 heap_alloc 函数从最大大小为 64K 的堆请求 64 KB (K) ,则请求可能会因系统开销而失败。
如果未在简单(默认)指定HEAP_NO_SERIALIZE,则堆会在调用过程中序列化访问。 当两个或多个线程同时尝试从同一堆分配或释放块时,序列化可确保相互排斥。 序列化的性能成本很小,但每当多个线程从同一个堆分配和释放内存时,必须使用它。 heap_lock 和 heap_unlock 函数可用于阻止和允许访问序列化堆。
设置 HEAP_NO_SERIALIZE 会消除堆上的相互排斥。 如果不进行序列化,使用同一堆句柄的两个或多个线程可能会尝试同时分配或释放内存,这可能会导致堆损坏。 因此, 只能在 以下情况下安全地使用HEAP_NO_SERIALIZE:
• 进程只有一个线程。
• 进程有多个线程,但只有一个线程调用特定堆的堆函数。
• 进程具有多个线程,应用程序提供自己的机制,用于对特定堆进行相互排斥。
如果在使用 HEAP_NO_SERIALIZE 标志创建的堆上调用 heap_lock 和 heap_unlock 函数,则结果未定义。
若要获取进程的默认堆的句柄,请使用 get_process_heap 函数。 若要获取对调用进程处于活动状态的默认堆和专用堆的句柄,请使用 get_process_heaps 函数。
options 堆分配选项。 这些选项通过调用堆函数影响对新堆的后续访问。 此参数可以是 0 或以下一个或多个值。
值 | 含义
HEAP_CREATE_ENABLE_EXECUTE 0x00040000 | 如果硬件强制实施 数据执行防护,则从此堆分配的所有内存块都允许代码执行。 在从堆运行代码的应用程序中使用此标志堆。 如果未指定 HEAP_CREATE_ENABLE_EXECUTE ,并且应用程序尝试从受保护的页面运行代码,则应用程序将收到异常,状态代码 STATUS_ACCESS_VIOLATION。
HEAP_GENERATE_EXCEPTIONS 0x00000004 | 系统引发异常以指示失败 (例如,内存不足条件) 调用 heap_alloc 和 heap_re_alloc 而不是返回 NULL。
HEAP_NO_SERIALIZE 0x00000001 | 当堆函数访问此堆时,不使用序列化访问。 此选项适用于所有后续堆函数调用。 或者,可以在单个堆函数调用上指定此选项。无法为使用此选项创建的堆启用低碎片化堆 (LFH) 。不能锁定使用此选项创建的堆。
initial_size 堆的初始大小(以字节为单位)。 此值确定为堆提交的初始内存量。 该值向上舍入为系统页面大小的倍数。 该值必须小于 maximum_size。如果此参数为 0,则函数将提交一页。 若要确定主计算机上的页面大小,请使用 get_system_info 函数。
maximum_size 堆的最大大小(以字节为单位)。 heap_create 函数将 maximum_size 舍入到系统页大小的倍数,然后在堆的进程虚拟地址空间中保留该大小的块。 如果 heap_alloc 或 heap_re_alloc 函数发出的分配请求超过 initial_size 指定的大小,系统会为堆提交额外的内存页,最大大小为堆的最大大小。如果 maximum_size 不为零,则堆大小是固定的,并且不能增长到超过最大大小。 此外,对于 32 位进程,可从堆中分配的最大内存块略小于 512 KB,而对于 64 位进程,则略低于 1,024 KB。 即使堆的最大大小足以包含块,分配较大块的请求也会失败。如果 maximum_size 为 0,堆大小可能会增大。 堆的大小仅受可用内存的限制。 分配大于固定大小堆限制的内存块的请求不会自动失败;相反,系统会调用 virtual_alloc 函数来获取大型块所需的内存。 需要分配大量内存块的应用程序应将 maximum_size 设置为 0。 - heap_destroy
- 销毁指定的堆对象。
heap_destroy 会取消提交并释放私有堆对象的所有页面,并使堆的句柄失效。
进程可以调用 heap_destroy ,而无需先调用 heap_free 函数来释放从堆中分配的内存。
h_heap 要销毁的堆的句柄。 此句柄由 heap_create 函数返回。 请勿使用 get_process_heap 函数返回的进程堆的句柄。 - heap_free
- 释放由 heap_alloc 或 heap_re_alloc 函数从堆分配的内存块。
h_heap 要释放其内存块的堆的句柄。 此句柄由 heap_create 或 get_process_heap 函数返回。
堆免费选项。 使用 HeapCreate 函数创建堆时,指定以下值将替代 flOptions 参数中指定的相应值。
值 | 含义
HEAP_NO_SERIALIZE 0x00000001 | 不会使用序列化访问。若要确保对此函数的所有调用禁用序列化访问,请在对 heap_create 的调用中指定HEAP_NO_SERIALIZE。 在这种情况下,无需在此函数调用中额外指定 HEAP_NO_SERIALIZE 。访问进程堆时不要指定此值。 系统可能会在应用程序的进程中创建其他线程,例如同时访问进程堆的 CTRL+C 处理程序。
mem 指向要释放的内存的指针。 此指针由 heap_alloc 或 heap_re_alloc 函数返回。 此指针可以为 NULL。 - read_process_memory
- read_process_memory 将指定地址范围中的数据从指定进程的地址空间复制到当前进程的指定缓冲区中。 具有PROCESS_VM_READ访问权限的句柄的任何进程都可以调用函数。
要读取的整个区域必须可访问,如果无法访问,则函数将失败。
h_process 包含正在读取的内存的进程句柄。 句柄必须具有对进程的PROCESS_VM_READ访问权限。
base_address 指向从中读取的指定进程中基址的指针。 在进行任何数据传输之前,系统会验证指定大小的基址和内存中的所有数据是否可供读取访问,如果无法访问,则函数将失败。
n_size 要从指定进程读取的字节数。 - virtual_alloc_ex
- 保留、提交或更改指定进程的虚拟地址空间中内存区域的状态。 函数将它分配的内存初始化为零。
若要指定物理内存的 NUMA 节点,请参阅 virtual_alloc_ex_numa。
每个页面都有一个关联的 页面状态。 virtual_alloc_ex 函数可以执行以下操作:
• 提交保留页的区域
• 保留免费页面区域
• 同时保留和提交可用页面区域
virtual_alloc_ex 无法保留保留页。 它可以提交已提交的页面。 这意味着你可以提交一系列页面,无论它们是否已提交,并且函数不会失败。
可以使用 virtual_alloc_ex 保留页块,然后对 virtual_alloc_ex 进行其他调用,以提交保留块中的单个页面。 这使进程能够保留其虚拟地址空间的范围,而无需使用物理存储,直到需要为止。
如果 address 参数不为 NULL,则该函数使用 address 和 size 参数来计算要分配的页面区域。 整个页面范围的当前状态必须与 allocation_type 参数指定的分配类型兼容。 否则,函数将失败,并且不会分配任何页。 此兼容性要求不排除提交已提交的页面;请参阅前面的列表。
若要执行动态生成的代码,请使用 virtual_alloc_ex 分配内存,并使用 virtual_protect_ex 函数授予 PAGE_EXECUTE 访问权限。
virtual_alloc_ex函数可用于在指定进程的虚拟地址空间中保留地址窗口扩展(AWE)内存区域。然后,可以使用此内存区域根据应用程序的要求将物理页映射到虚拟内存中和映射出虚拟内存。必须在allocation_type参数中设置MEM_PHYSICAL和MEM_RESERVE值。不得设置MEM_COMMIT值。
页面保护必须设置为PAGE_READWRITE。virtual_free_ex 函数可以取消提交已提交页面、释放页面的存储,也可以同时取消提交和释放已提交页面。 它还可以释放保留页,使其成为免费页面。
创建可执行的区域时,调用程序负责在代码设置到位后,通过适当调用 flush_instruction_cache 来确保缓存一致性。 否则,尝试在新可执行区域之外执行代码可能会产生不可预知的结果。
h_process 进程的句柄。 函数在此进程的虚拟地址空间中分配内存。句柄必须具有 PROCESS_VM_OPERATION 访问权限。 有关详细信息,请参阅 进程安全和访问权限。
address 为要分配的页面区域指定所需起始地址的指针。如果要保留内存,该函数将此地址向下舍入到分配粒度的最接近倍数。如果要提交已保留的内存,该函数会将此地址向下舍入到最近的页边界。若要确定主计算机上的页面大小和分配粒度,请使用get_system_info函数。如果address为NULL,则该函数确定分配区域的位置。如果此地址位于尚未通过调用initialize_enclave进行初始化的enclave内,virtual_alloc_ex会为该地址上的enclave分配一个零页。该页面必须以前未提交,并且不会使用IntelSoftwareGuardExtensions编程模型的EEXTEND指令进行测量。如果中的地址位于你初始化的enclave中,则分配操作将失败并出现ERROR_INVALID_ADDRESS错误。对于不支持动态内存管理的enclave((即SGX1))也是如此。SGX2enclave将允许分配,并且页面必须在分配后被enclave接受。
size 要分配的内存区域的大小(以字节为单位)。如果address为NULL,则该函数会将size向上舍入到下一页边界。如果address不为NULL,则该函数将分配从address到address+size范围内包含一个或多个字节的所有页。例如,这意味着跨越页边界的2字节范围会导致函数分配这两个页面。
allocation_type 内存分配的类型。 此参数必须包含以下值之一。 - virtual_free
- 释放、取消提交或释放和取消提交指定进程的虚拟地址空间中的内存区域。
h_process 进程的句柄。 函数释放进程的虚拟地址空间中的内存。句柄必须具有 PROCESS_VM_OPERATION 访问权限。 有关详细信息,请参阅 进程安全和访问权限。
address 指向要释放的内存区域的起始地址的指针。如果 free_type 参数 MEM_RELEASE,则 address 必须是保留区域时 virtual_alloc_ex 函数返回的基址。
size 要释放的内存区域的大小(以字节为单位)。如果 free_type 参数 MEM_RELEASE,则 size 必须为 0 (零) 。 函数释放在 对 virtual_alloc_ex 的初始分配调用中保留的整个区域。如果 free_type MEM_DECOMMIT,则函数将取消提交包含从 address 参数到 (address+size)范围内的一个或多个字节的所有内存页。 例如,这意味着跨越页边界的 2 字节内存区域会导致两个页面都解除提交。 如果 address 是 virtual_alloc_ex 返回的基址,而 size 为 0 (零) ,则函数将取消提交 virtual_alloc_ex 分配的整个区域。 之后,整个区域将处于保留状态。
free_type 释放操作的类型。 此参数须为下列值之一。 - write_process_memory
- 将数据写入到指定进程中的内存区域。要写入的整个区域必须可访问,否则操作将失败。
返回接收传输到指定进程的字节数。
write_process_memory 将数据从当前进程中的指定缓冲区复制到指定进程的地址范围。 任何具有 PROCESS_VM_WRITE 句柄且PROCESS_VM_OPERATION访问要写入的进程的进程都可以调用 函数。 通常(但并非总是)正在调试包含正在写入的地址空间的进程。
要写入到的整个区域必须可访问,如果无法访问,则函数将失败。
h_process 要修改的进程内存的句柄。 句柄必须具有对进程的PROCESS_VM_WRITE和PROCESS_VM_OPERATION访问权限。
base_address 指向将数据写入到的指定进程中基址的指针。 在进行数据传输之前,系统会验证指定大小的基址和内存中的所有数据是否可供写入访问,如果无法访问,则函数将失败。
buffer 指向缓冲区的指针,该缓冲区包含要写入指定进程的地址空间中的数据。
n_size 要写入指定进程的字节数。