SetProcessValidCallTargets调用返回错误87(无效参数)问题排查
CFG API调用报错ERROR_INVALID_PARAMETER(87)问题原因与修复方案
核心问题点
- 提前释放了依赖模块
你在获取到SetProcessValidCallTargets、NtSetContextThread的函数地址后,直接调用FreeLibrary释放了ntdll.dll和kernelbase.dll,这会导致后续调用SetProcessValidCallTargets时,函数指针指向的内存已经被系统回收,调用行为完全不可预期,必然会出现参数错误或直接崩溃。 - 内存区域大小参数错误
VirtualQuery返回的RegionSize是当前单段内存区域的大小,而SetProcessValidCallTargets要求传入的RegionSize必须是VirtualAddress(也就是你拿到的分配基址)对应的整个内存分配块的总大小,参数不匹配会直接返回参数错误。 - 修改对象错误
你尝试给系统模块ntdll.dll的导出函数NtSetContextThread添加CFG合法标记,首先系统导出函数默认已经被CFG标记为合法调用目标,其次系统模块的内存页受保护,不允许用户态进程修改其CFG位图,调用必然会失败。该API的设计用途是给用户自己分配的可执行内存(比如动态生成shellcode的内存页)添加合法调用标记。 - 前置条件不满足
如果你的进程编译时没有开启CFG(VS编译选项为/guard:cf),系统不会为进程维护CFG调用位图,调用SetProcessValidCallTargets会直接返回参数错误。 - 地址对齐问题
CFG要求合法调用目标必须是16字节对齐,如果你计算出的偏移对应的目标地址没有对齐,也会触发参数错误。
修复步骤
- 把
FreeLibrary(hNtdll)、FreeLibrary(hKernelbase)的调用移到lblCleanup代码块中,确保调用完SetProcessValidCallTargets之后再释放加载的模块。 - 修正内存大小获取逻辑,遍历分配基址下的所有连续内存段,累加得到整个分配块的总大小后再传入API。
- 将修改目标换成你自己分配的可执行内存区域,不要尝试修改系统模块的CFG配置。
- 确认编译选项开启了
/guard:cf,确保进程本身启用了CFG功能。 - 校验目标地址是否为16字节对齐,调整偏移值满足对齐要求。
内容的提问来源于stack exchange,提问作者woldgrep
相关产品推荐
相关产品推荐

