64位平台CreateRemoteThread调用NtAllocateVirtualMemory时错误码5求助
问题分析与解决方案
核心原因
跨进程函数指针无效:你在本地进程获取的
NtAllocateVirtualMemory地址是本地ntdll.dll的函数入口,但64位Windows默认开启ASLR(地址空间布局随机化),目标进程的ntdll.dll加载基址和本地进程不同,导致这个函数指针在目标进程中指向无效内存,调用时触发内存访问错误,线程退出码被系统映射为ERROR_ACCESS_DENIED(0x5)。Shellcode截取逻辑不可靠:用
0xCCCC作为shellcode结束标记仅在VS调试模式下有效,Release模式或其他编译环境下不会有这个填充字节,会导致shellcode截取长度错误,进而引发执行异常。
修复步骤
1. 正确获取目标进程的NtAllocateVirtualMemory地址
不要直接传递本地的函数指针,而是在目标进程中通过GetModuleHandleA("ntdll.dll")和GetProcAddress动态获取正确的函数地址:
ULONG_PTR WINAPI inject_begin(InjectParam* inject_param) { inject_param->age += 1; PVOID memory_address = NULL; SIZE_T size = 10; // 在目标进程内动态获取函数地址 HMODULE h_ntdll = GetModuleHandleA("ntdll.dll"); NTALLOCATEVIRTUALMEMORY pNtAllocate = (NTALLOCATEVIRTUALMEMORY)GetProcAddress(h_ntdll, "NtAllocateVirtualMemory"); if (pNtAllocate) { pNtAllocate((HANDLE)-1, &memory_address, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); } return 0; }
2. 替换可靠的Shellcode截取方式
放弃用0xCCCC判断结束,通过定义结束标记函数,用函数地址差计算shellcode长度:
// shellcode入口函数 ULONG_PTR WINAPI inject_begin(InjectParam* inject_param) { // ... 原有逻辑 } // 结束标记函数,必须紧跟inject_begin定义 void inject_end() {} // 在remote_map_load_dll中计算长度 DWORD shell_code_size = (DWORD)((BYTE*)inject_end - (BYTE*)inject_begin);
这种方式不受编译模式影响,能准确获取shellcode实际长度。
3. 确保权限配置正确(可选)
如果目标进程是UAC提升的进程,当前进程需要以管理员权限运行。同时可以完善权限调整函数的错误检查,确保SE_DEBUG_NAME权限成功启用:
void adjust_token_privileges() { HANDLE h_token = NULL; if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &h_token)) { printf("OpenProcessToken failed: %d\n", GetLastError()); return; } TOKEN_PRIVILEGES priv = { 0 }; priv.PrivilegeCount = 1; priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; if (LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &priv.Privileges[0].Luid)) { DWORD dwErr = 0; if (!AdjustTokenPrivileges(h_token, FALSE, &priv, sizeof(TOKEN_PRIVILEGES), NULL, NULL)) { dwErr = GetLastError(); printf("AdjustTokenPrivileges failed: %d\n", dwErr); } else { dwErr = GetLastError(); if (dwErr == ERROR_NOT_ALL_ASSIGNED) { printf("SE_DEBUG_NAME privilege not assigned\n"); } } } else { printf("LookupPrivilegeValue failed: %d\n", GetLastError()); } CloseHandle(h_token); }
修复后核心代码片段
//shell code ULONG_PTR WINAPI inject_begin(InjectParam* inject_param) { inject_param->age += 1;//age=2 PVOID memory_address = NULL; SIZE_T size = 10; // 目标进程内动态获取函数地址 HMODULE h_ntdll = GetModuleHandleA("ntdll.dll"); NTALLOCATEVIRTUALMEMORY pNtAllocate = (NTALLOCATEVIRTUALMEMORY)GetProcAddress(h_ntdll, "NtAllocateVirtualMemory"); if (pNtAllocate) { pNtAllocate((HANDLE)-1, &memory_address, 0, &size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); } return 0; } // 结束标记函数,用于计算shellcode长度 void inject_end() {} void remote_map_load_dll(HANDLE target_process) { InjectParam inject_param; RtlZeroMemory(&inject_param, sizeof(inject_param)); inject_param.age = 1; // 准确计算shellcode长度 DWORD shell_code_size = (DWORD)((BYTE*)inject_end - (BYTE*)inject_begin); printf("shellcode length:%d\n", shell_code_size); PVOID shell_code_buffer = malloc(shell_code_size); RtlCopyMemory(shell_code_buffer, inject_begin, shell_code_size); // 不再需要传递本地的NtAllocateVirtualMemory指针 PBYTE shellcode_address = (PBYTE)VirtualAllocEx(target_process, 0, shell_code_size + sizeof(inject_param), MEM_COMMIT, PAGE_EXECUTE_READWRITE); // ... 后续写入、创建线程逻辑保持不变 }
内容的提问来源于stack exchange,提问作者Yulin.Li
相关产品推荐
相关产品推荐

