c++ - 如果注入器由特定应用程序启动,PE注入失败?
问题描述
简短免责声明:由于此问题包含有关黑客/渗透测试的主题,我想声明此问题仅作为学校项目的一部分用于教育目的。为了防止可能的滥用,我只会发布理解问题所必需的代码。
为了演示 Windows 10 的危险和漏洞,我目前正在编写一个使用两种常用技术的小型 C++/WinAPI 应用程序:
- 使用“ fodhelper 技术”的 UAC 绕过(通过简单地将特定的注册表值设置为应该提升的可执行文件的路径,然后启动一个名为“
fodhelper.exe
”的自动提升的 Windows 可执行文件,然后它将读取注册表值并将其作为命令执行/启动指定的应用程序)。 - 执行PE注入,即从当前进程的地址空间运行一个PE文件(基于github的这个例子)。在我的程序中注入的 PE 是一个简单的 C++ 控制台应用程序 (x86),它打印一个消息框。shellcode 在注入器二进制文件 (x86) 中硬编码。
我设法在独立文件中成功地执行了这两种技术。但是,一旦我将这两种方法结合起来(即先提升,然后注入),就会出现一个奇怪的错误。
问题描述
当注入器手动启动(通过双击)时,一切正常,但是当注入器System32\fodhelper.exe
由于 UAC 绕过而由 (x64) 启动时,会发生以下情况:注入完成后,注入的控制台窗口应用程序出现,但我没有继续执行,而是收到一堆错误消息,说明“ The code execution cannot proceed because [garbage characters].dll was not found
”。这表明偏移量出了问题,Windows 加载程序试图在错误的位置读取导入。
总结:代码注入工作正常,除非注入器是由fodhelper.exe
. 在这种情况下,注入的 PE 文件无法运行。
到目前为止我已经尝试过的事情来找出问题的根源
GetLastError
使用和打印注入期间使用的各种内存地址来调试注入。如果文件是手动启动的(并且注入成功)或者它是由它启动fodhelper.exe
(并且注入失败),则没有区别。- 当手动启动注入器时,将
WriteProcessMemory
调用替换为比较输出文件,或者替换为. 两个输出文件完全相同且可运行。这表明注入本身不是问题,但 Windows 加载程序的行为似乎有所不同。WriteFile
fodhelper.exe
- 使用 UAC 或使用提升的命令提示符手动提升注射器。在这两种情况下,注入都是成功的。
- 复制
fodhelper.exe
到另一个位置(例如到桌面)并启动此副本。在这种情况下,注入成功。fodhelper.exe
仅当注入器由文件夹中的原始文件启动时,注入才会失败System32
。
看起来注入的行为完全相同,但指标显示,由于传递给注入器的 fodhelper.exe 的一些未知影响,Windows 加载程序的行为似乎有所不同。
我感谢任何解释或假设!如果您需要更多信息,请随时询问。
最小的可重现示例
(调试信息和评论有限): https ://0bin.net/paste/UPRIg12n#6nJvBok72UcDvIa56c-XEss7AibIh1Zrs+c3sUzvQMj
注意:如果您排除该功能或使用 UAC 手动提升 exe,请查看注入是如何工作的elevateProcess
,以及在包含所述功能时它是如何失败的。
编辑
根据用户 RbMm 的回答,此错误是特定漏洞利用保护属性(PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY
带有EnableModuleTamperingProtection
值)的结果,该属性自动应用于fodhelper.exe
所有子进程并似乎被所有子进程继承。据此,在启动目标进程时删除/重置此属性应该可以修复错误。到目前为止,我已经尝试了以下方法,但无法改变结果:
DWORD resetPolicy = 0;
STARTUPINFOEXA siEx = { 0 };
SIZE_T AttributeListSize = 0;
siEx.StartupInfo.cb = sizeof(siEx);
InitializeProcThreadAttributeList(NULL, 1, 0, &AttributeListSize);
siEx.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, AttributeListSize);
InitializeProcThreadAttributeList(siEx.lpAttributeList, 1, 0, &AttributeListSize);
UpdateProcThreadAttribute(siEx.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &resetPolicy, sizeof(resetPolicy), NULL, NULL);
...
CreateProcessA(CurrentFilePath, NULL, NULL, NULL, FALSE, EXTENDED_STARTUPINFO_PRESENT | CREATE_SUSPENDED, NULL, NULL, (LPSTARTUPINFOA)&siEx, &PI);
解决方案
当通过具有提升的RunAs创建的进程 - appinfo.dll调用RAiLaunchAdminProcess函数(这是在某些svchost.exe中)和此函数,传递STARTUPINFOEX
(和EXTENDED_STARTUPINFO_PRESENT
标志)到CreateProcessAsUser
. 在这里 - lpAttributeList,特别是PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY
属性键用于为子进程(在您的情况下为fodhelper.exe )设置几个漏洞利用缓解策略。这里EnableModuleTamperingProtection
是为子进程树设置的。效果 - 当系统解析导入描述符时,它会检查(在LdrpGetImportDescriptorForSnap内部)是否有这个缓解标志,如果它启用 - 调用LdrpCheckPagesForTampering
api,它返回 true,如果SharedOriginal
为 0,这意味着这是 EXE/IAT 的写时复制私有副本——因此被“篡改”。在调用此LdrpMapCleanModuleView之后。此时你的尝试开始打破
来自Alex Ionescu的可能的第一个公开信息-
LdrpCheckPagesForTampering/LdrpMapCleanModuleView (RS3) 是非常酷的防空洞缓解措施 (EPROCESS.EnableModuleTamperingProtection)
如果您自行启动新流程,您当然不需要UpdateProcThreadAttribute
设置PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY
,在这种情况下,您的代码有时会起作用。真的只是随机的,有时 - 这里存在许多其他错误和糟糕的编码
推荐阅读
- javascript - addEventListener 点击一次不工作多次
- c++ - 根据 CMAKE_CONFIGURATION_TYPES 生成预处理器定义
- r - UpdateSelectInput 不更新选项
- c# - 我在编写标签游戏时遇到问题
- javascript - 如何仅从控制台日志数组中打印获取数据,该日志数组具有带字符串的子项
- python - 无法使用 ARIMA 预测下一个值:输入包含 NaN、无穷大或对于 dtype('float64') 而言太大的值
- python - Pandas 累积总和取决于其他列的值
- android - Jetpack Compose:当其子项与列的高度相同时,如何滚动列(并聚焦正确的项目)?
- flutter - 为什么导航栏项中没有索引?
- webstorm - 为什么我的 WebStorm JavaScript 版本中没有 jsx 选项