调用LoadEnclaveData返回错误码87(ERROR_INVALID_PARAMETER)如何解决
问题根因
你遇到的ERROR_INVALID_PARAMETER报错是由多个参数错误共同导致的:
LoadEnclaveData的第三个参数lpBuffer错误传递了&buffer,buffer本身就是存储NOP指令的数组首地址,不需要二次取址,取址后传递的是栈上数组指针的地址,完全不符合参数要求。- 你传入的
nSize参数值为22,不符合必须是系统页大小整数倍的要求,Windows默认页大小为4096字节。 LoadEnclaveData的第8个参数lpNumberOfBytesWritten不能传0,必须传入一个可写的ULONG类型指针用于接收实际写入的字节数。
修正后代码示例
#include <windows.h> #include <stdio.h> #include <enclaveapi.h> int main() { // 先确认VBS Enclave支持 if (!IsEnclaveTypeSupported(ENCLAVE_TYPE_VBS)) { printf("[-] 当前环境不支持VBS Enclave\n"); return 1; } // 获取系统页大小 SYSTEM_INFO sysInfo = {0}; GetSystemInfo(&sysInfo); DWORD pageSize = sysInfo.dwPageSize; HANDLE hProcess = GetCurrentProcess(); ENCLAVE_CREATE_INFO_VBS createInfo = {0}; createInfo.Flags = 0; // 创建Enclave,大小为2MB LPVOID enclaveBase = CreateEnclave( hProcess, NULL, 2 * 1024 * 1024, NULL, ENCLAVE_TYPE_VBS, &createInfo, sizeof(ENCLAVE_CREATE_INFO_VBS), NULL ); if (enclaveBase == NULL) { printf("[-] CreateEnclave失败,错误码:%d\n", GetLastError()); return 1; } printf("[+] Enclave基地址:0x%p\n", enclaveBase); // 分配页对齐的缓冲区,填充NOP指令 LPVOID codeBuffer = VirtualAlloc(NULL, pageSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); if (codeBuffer == NULL) { printf("[-] 分配代码缓冲区失败\n"); return 1; } memset(codeBuffer, 0x90, pageSize); // 末尾加RET指令,执行完后返回宿主 ((PBYTE)codeBuffer)[pageSize - 1] = 0xC3; // 加载数据到Enclave ULONG writtenBytes = 0; BOOL loadRes = LoadEnclaveData( hProcess, enclaveBase, codeBuffer, pageSize, PAGE_READWRITE, NULL, 0, &writtenBytes, NULL ); if (!loadRes) { printf("[-] LoadEnclaveData失败,错误码:%d\n", GetLastError()); return 1; } printf("[+] 加载成功,写入字节数:%d\n", writtenBytes); // 修改内存属性为可执行 BOOL setAttrRes = SetEnclaveProtectedMemoryAttribute( hProcess, enclaveBase, pageSize, ENCLAVE_MEMORY_ATTRIBUTE_EXECUTE, ENCLAVE_MEMORY_ATTRIBUTE_EXECUTE ); if (!setAttrRes) { printf("[-] 修改内存属性失败,错误码:%d\n", GetLastError()); return 1; } // 初始化Enclave ENCLAVE_INIT_INFO_VBS initInfo = {0}; initInfo.Flags = 0; BOOL initRes = InitializeEnclave( hProcess, enclaveBase, &initInfo, sizeof(ENCLAVE_INIT_INFO_VBS) ); if (!initRes) { printf("[-] InitializeEnclave失败,错误码:%d\n", GetLastError()); return 1; } // 调用Enclave代码 printf("[+] 即将调用Enclave代码\n"); BOOL callRes = CallEnclave(enclaveBase, NULL, 0, NULL); if (!callRes) { printf("[-] CallEnclave失败,错误码:%d\n", GetLastError()); return 1; } printf("[+] Enclave代码执行完成\n"); VirtualFree(codeBuffer, 0, MEM_RELEASE); return 0; }
Enclave代码执行注意事项
- 不要直接调用
EENTER汇编指令,直接使用微软封装的CallEnclave函数即可,内部已经处理了权限切换、参数传递等逻辑。 - 加载到Enclave的代码必须是位置无关代码(PIC),不能包含硬编码的绝对地址,因为Enclave的基地址是运行时动态分配的。
- Enclave内存不允许同时设置可写和可执行属性,写入代码时先用
PAGE_READWRITE属性,写完之后再调用SetEnclaveProtectedMemoryAttribute修改为可执行属性。 - 调用
InitializeEnclave之后,Enclave的内存就会被锁定,无法再修改内容,所有代码和数据必须在初始化之前全部加载完成。 - 不要直接向Enclave传递宿主进程的指针,所有需要访问的数据必须提前加载到Enclave内部的内存空间中。
内容的提问来源于stack exchange,提问作者muhammad Ali
相关产品推荐
相关产品推荐

