使用 mhook 挂钩 ZwCreateSection()、CreateProcess() 和 CreateProcessEx() 以阻止某些应用程序启动
Hook ZwCreateSection(), CreateProcess() and CreateProcessEx() using mhook to block certain application from launching
我正在尝试使用 mhook 在 CreateProcess()、CreateProcessEx() 或 ZwCreateSection() 上创建一个全局挂钩,以便我可以阻止某些应用程序启动。我按照 https://www.apriorit.com/dev-blog/160-apihooks 中提供的步骤进行操作。但这似乎不起作用。是否有可能,如果可以,请提供任何建议。
我尝试了以下代码来记录使用 CreateProcess() 创建的每个进程。
#include "stdafx.h"
#include<fstream>
#include "mhook/mhook-lib/mhook.h"
typedef BOOL (WINAPI *_CreateProcess)(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
_CreateProcess TrueCreateProcess =
(_CreateProcess)::GetProcAddress(::GetModuleHandle(L"kernel32"),"CreateProcess");
BOOL WINAPI HookCreateProcess(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
)
{
std::wofstream out;
out.open("D:\Log.txt",std::ios_base::app);
if(out!=NULL)
{
out<<"Process Created\n";
}
return TrueCreateProcess(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
}
BOOL WINAPI DllMain(
__in HINSTANCE hInstance,
__in DWORD Reason,
__in LPVOID Reserved
)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH:
Mhook_SetHook((PVOID*)&TrueCreateProcess, HookCreateProcess);
break;
case DLL_PROCESS_DETACH:
Mhook_Unhook((PVOID*)&TrueCreateProcess);
break;
}
return TRUE;
}
从原始 post 评论中的注释进行:
- 您确定您的 DLL 已注入到正在生成您试图监视其创建的程序的进程中吗?
- 您是否已使用调试器进行检查以确保您使用的库已成功挂接例程?
调试另一个进程,如 explorer.exe 并注入您的 DLL,同时在回调上设置断点。然后 运行 任何非提升的程序并查看断点是否命中。由于以下两个原因之一,它不会命中:未调用挂钩例程;或者钩子从未设置。
您应该首先在本地进程中测试这些东西,就像评论中提到的@RbMm 一样。接受他的建议,你的生活会更快乐。
一般说明
首先,请不要在 API 挂钩方面触及 Win32 API,除非您想要任何人都无法理解的最有缺陷的设计实现。
- 您可以修补 NtResumeThread (NTDLL),然后进行检查以确定它是否作为进程创建的一部分被调用。当你生成一个程序时,负责进程创建操作的进程将调用 NtResumeThread 来恢复新创建进程的主线程。
- 或者,只要您不打算支持 Windows Vista 之前的任何 OS 版本,您就可以修补 NtCreateUserProcess (NTDLL)。但是,出于严重的安全原因,不应再使用 Windows 2000 和 Windows XP,并且很长时间以来都没有得到支持,因此我希望您根本不要关注它们。
上述两个想法都比依赖挂钩 Win32 API 来完成您想要做的事情要好,但它们仍然是非常糟糕的想法。我会解释几个原因。
- 修补其他进程内存的虚拟内存以拦截进程创建通常是一个坏主意,应尽可能避免,因为您可能会引入其他漏洞(例如,在回调中留下带有某些标志或错误的内存 routines/helper 可能被滥用的例程)并导致程序不稳定。
- 绕过 API 挂钩的用户模式补丁非常简单,如果执行内存补丁需要花费大量时间来防止这些绕过,你最终可能会造成比以前更大的伤害是(例如性能下降、潜在漏洞等)。绕过针对 Win32 API 的 API 挂钩的示例是您自己重新实现 Win32 API 例程,以及绕过 NT 上的 API 挂钩的示例API 就是自己执行系统调用。
- 您的 API 进程创建挂钩对于以管理员身份启动的程序不起作用,即使您要修补 NTAPI 也是如此。这是因为当您以管理员身份生成程序时,consent.exe 开始发挥作用。我相信 consent.exe 无论如何都是一个受保护的进程(你可以仔细检查说我不正确,因为我不记得了 - 尽管如果它是一个受保护的进程它在逻辑上是有意义的)如果是,那么无论如何你都无法将你的代码注入其中(更不用说像 consent.exe 这样的过程在 99.9% 的情况下是完全不负责任的)。
对于 Windows Vista 和 Windows 7,您可以将代码注入 csrss.exe 然后修补 CsrCreateProcess(由 csrss.exe 依赖的模块导出在)。它比将代码注入多个进程更可靠(并且还将用于监视提升进程上的进程创建),但就稳定性和安全性而言,这仍然是一个坏主意。您需要启用 SeDebugPrivilege 才能触摸 csrss.exe 进行记录。
对于较新版本的 Windows,其中 csrss.exe 是受保护的进程,您可以在 lsass.exe 等进程中修补 NtOpenProcess (NTDLL)。然而,再一次,稳定性和安全性的坏主意 - 更不用说不能保证这将始终 100% 可靠的事实。这只是您可以尝试进行教育实验的估计。您还需要考虑 lsass.exe 是受保护进程的可能性(有一个选项可以通过修改注册表来启用它 - 出于安全原因,每个人都应该这样做)。
以上两种想法各有各的不好。
有 Windows Management Instrumentation (WMI) 可用于接收新进程创建的通知,但它不会像 "interception" 那样工作。它的行为类似于 post-创建通知。但是,如果您需要做的只是创建日志进程,那么从用户模式下可以做的或多或少的最好的事情,而不会弄乱未记录的行为,并且从关注安全性、稳定性和效率的角度来看.
上面的想法是一个很好的例子,应该安全、高效、稳定并记录在案。
监控进程创建(您需要决定 allowing/blocking 操作)的最佳方法是通过内核模式设备驱动程序,使用 PsSetCreateProcessNotifyRoutine 回调。请记住,对于较新版本的 Windows.
,该回调例程还有一个 Ex* 和 Ex2* 版本
内核模式的另一种方法是通过 PsSetLoadImageNotifyRoutine/Ex(然后过滤 NTDLL.DLL)或通过 PsSetCreateThreadNotifyRoutine(并使用线程 ID 保存您自己的进程监控日志以确定线程是否创建是它所针对的过程的第一个)。
现在,关于DLL注入,不要使用AppInit_DLLs。这是完成您在原始 post 中尝试做的事情的最有缺陷的机制之一。即使我不同意你的设计,我也可以解释为什么它是一个有缺陷的设计。
AppInit_DLLs 只会影响加载了 User32.dll 的程序,这意味着您的监控会很重 limited/unreliable。您将无法监视跨非 GUI 程序的进程创建(很少导入 User32.dll - 像 Windows 服务,甚至是不依赖任何与 User32 相关的消息框的控制台进程等)。
更好的 RCE 方法是拥有一个特权进程(因此您在获取进程句柄方面的限制较少),它依赖于远程线程 Creation/Asynchronous 过程调用来触发简单且格式良好的程序的执行shell-代码(将在该阶段之前写入进程的虚拟内存)以调用 LdrLoadDll (NTDLL)。这也将允许注入本机进程(不依赖任何 Win32 API 模块的进程),只要注入的 DLL 也是本机的。
总的来说,作为一般性评论,您真的不想继续尝试做您寻求帮助的事情(挂接 Win32 API)和我提到的其他挂接方法仅出于理论目的提及完成进程创建监视,而不是为了实际建议您采用这些路径。我怎么强调都不能过分强调专注于文档和稳定性很重要,因为虽然您现在可能不相信,但尝试像您目前正在尝试使用您提出的设计那样做的事情,很容易让您感到困惑结束并破坏破坏。我一点也不夸张。
重要的是要记住,执行远程代码执行通常不是一个好主意,请仅在别无他法时才这样做。一个真正需要它的例子是 AV 供应商试图开发行为 Blocker/HIPS,但没有内核模式回调来过滤他们试图过滤的内容(并且他们可能没有虚拟化支持通过 Intel VT-x 或 AMD SVM 控制 x64 上的内核,而不会导致错误检查)。一个糟糕的理由是它的目的是简单地 "log" 进程创建,特别是当有像 WMI 这样的选项可用时,这些选项仅具有此功能。
请不要误会,但我从经验中了解到,API 挂钩并不是所有时候的答案,通常可以考虑更好的设计。有时候你可能真的要依赖它,但据我所见,大多数人问它甚至不需要依赖它。
如果这不仅仅是一个实验,而且是针对生产级别的,请采用 WMI 或内核模式设备驱动程序方法。如果您采用内核模式设备驱动程序方法,请尽可能少地从内核模式执行操作并在用户模式服务进程中处理过滤。
祝你好运。
我正在尝试使用 mhook 在 CreateProcess()、CreateProcessEx() 或 ZwCreateSection() 上创建一个全局挂钩,以便我可以阻止某些应用程序启动。我按照 https://www.apriorit.com/dev-blog/160-apihooks 中提供的步骤进行操作。但这似乎不起作用。是否有可能,如果可以,请提供任何建议。 我尝试了以下代码来记录使用 CreateProcess() 创建的每个进程。
#include "stdafx.h"
#include<fstream>
#include "mhook/mhook-lib/mhook.h"
typedef BOOL (WINAPI *_CreateProcess)(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
);
_CreateProcess TrueCreateProcess =
(_CreateProcess)::GetProcAddress(::GetModuleHandle(L"kernel32"),"CreateProcess");
BOOL WINAPI HookCreateProcess(
_In_opt_ LPCTSTR lpApplicationName,
_Inout_opt_ LPTSTR lpCommandLine,
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
_In_ BOOL bInheritHandles,
_In_ DWORD dwCreationFlags,
_In_opt_ LPVOID lpEnvironment,
_In_opt_ LPCTSTR lpCurrentDirectory,
_In_ LPSTARTUPINFO lpStartupInfo,
_Out_ LPPROCESS_INFORMATION lpProcessInformation
)
{
std::wofstream out;
out.open("D:\Log.txt",std::ios_base::app);
if(out!=NULL)
{
out<<"Process Created\n";
}
return TrueCreateProcess(lpApplicationName,
lpCommandLine,
lpProcessAttributes,
lpThreadAttributes,
bInheritHandles,
dwCreationFlags,
lpEnvironment,
lpCurrentDirectory,
lpStartupInfo,
lpProcessInformation);
}
BOOL WINAPI DllMain(
__in HINSTANCE hInstance,
__in DWORD Reason,
__in LPVOID Reserved
)
{
switch (Reason)
{
case DLL_PROCESS_ATTACH:
Mhook_SetHook((PVOID*)&TrueCreateProcess, HookCreateProcess);
break;
case DLL_PROCESS_DETACH:
Mhook_Unhook((PVOID*)&TrueCreateProcess);
break;
}
return TRUE;
}
从原始 post 评论中的注释进行:
- 您确定您的 DLL 已注入到正在生成您试图监视其创建的程序的进程中吗?
- 您是否已使用调试器进行检查以确保您使用的库已成功挂接例程?
调试另一个进程,如 explorer.exe 并注入您的 DLL,同时在回调上设置断点。然后 运行 任何非提升的程序并查看断点是否命中。由于以下两个原因之一,它不会命中:未调用挂钩例程;或者钩子从未设置。
您应该首先在本地进程中测试这些东西,就像评论中提到的@RbMm 一样。接受他的建议,你的生活会更快乐。
一般说明
首先,请不要在 API 挂钩方面触及 Win32 API,除非您想要任何人都无法理解的最有缺陷的设计实现。
- 您可以修补 NtResumeThread (NTDLL),然后进行检查以确定它是否作为进程创建的一部分被调用。当你生成一个程序时,负责进程创建操作的进程将调用 NtResumeThread 来恢复新创建进程的主线程。
- 或者,只要您不打算支持 Windows Vista 之前的任何 OS 版本,您就可以修补 NtCreateUserProcess (NTDLL)。但是,出于严重的安全原因,不应再使用 Windows 2000 和 Windows XP,并且很长时间以来都没有得到支持,因此我希望您根本不要关注它们。
上述两个想法都比依赖挂钩 Win32 API 来完成您想要做的事情要好,但它们仍然是非常糟糕的想法。我会解释几个原因。
- 修补其他进程内存的虚拟内存以拦截进程创建通常是一个坏主意,应尽可能避免,因为您可能会引入其他漏洞(例如,在回调中留下带有某些标志或错误的内存 routines/helper 可能被滥用的例程)并导致程序不稳定。
- 绕过 API 挂钩的用户模式补丁非常简单,如果执行内存补丁需要花费大量时间来防止这些绕过,你最终可能会造成比以前更大的伤害是(例如性能下降、潜在漏洞等)。绕过针对 Win32 API 的 API 挂钩的示例是您自己重新实现 Win32 API 例程,以及绕过 NT 上的 API 挂钩的示例API 就是自己执行系统调用。
- 您的 API 进程创建挂钩对于以管理员身份启动的程序不起作用,即使您要修补 NTAPI 也是如此。这是因为当您以管理员身份生成程序时,consent.exe 开始发挥作用。我相信 consent.exe 无论如何都是一个受保护的进程(你可以仔细检查说我不正确,因为我不记得了 - 尽管如果它是一个受保护的进程它在逻辑上是有意义的)如果是,那么无论如何你都无法将你的代码注入其中(更不用说像 consent.exe 这样的过程在 99.9% 的情况下是完全不负责任的)。
对于 Windows Vista 和 Windows 7,您可以将代码注入 csrss.exe 然后修补 CsrCreateProcess(由 csrss.exe 依赖的模块导出在)。它比将代码注入多个进程更可靠(并且还将用于监视提升进程上的进程创建),但就稳定性和安全性而言,这仍然是一个坏主意。您需要启用 SeDebugPrivilege 才能触摸 csrss.exe 进行记录。
对于较新版本的 Windows,其中 csrss.exe 是受保护的进程,您可以在 lsass.exe 等进程中修补 NtOpenProcess (NTDLL)。然而,再一次,稳定性和安全性的坏主意 - 更不用说不能保证这将始终 100% 可靠的事实。这只是您可以尝试进行教育实验的估计。您还需要考虑 lsass.exe 是受保护进程的可能性(有一个选项可以通过修改注册表来启用它 - 出于安全原因,每个人都应该这样做)。
以上两种想法各有各的不好。
有 Windows Management Instrumentation (WMI) 可用于接收新进程创建的通知,但它不会像 "interception" 那样工作。它的行为类似于 post-创建通知。但是,如果您需要做的只是创建日志进程,那么从用户模式下可以做的或多或少的最好的事情,而不会弄乱未记录的行为,并且从关注安全性、稳定性和效率的角度来看.
上面的想法是一个很好的例子,应该安全、高效、稳定并记录在案。
监控进程创建(您需要决定 allowing/blocking 操作)的最佳方法是通过内核模式设备驱动程序,使用 PsSetCreateProcessNotifyRoutine 回调。请记住,对于较新版本的 Windows.
,该回调例程还有一个 Ex* 和 Ex2* 版本内核模式的另一种方法是通过 PsSetLoadImageNotifyRoutine/Ex(然后过滤 NTDLL.DLL)或通过 PsSetCreateThreadNotifyRoutine(并使用线程 ID 保存您自己的进程监控日志以确定线程是否创建是它所针对的过程的第一个)。
现在,关于DLL注入,不要使用AppInit_DLLs。这是完成您在原始 post 中尝试做的事情的最有缺陷的机制之一。即使我不同意你的设计,我也可以解释为什么它是一个有缺陷的设计。
AppInit_DLLs 只会影响加载了 User32.dll 的程序,这意味着您的监控会很重 limited/unreliable。您将无法监视跨非 GUI 程序的进程创建(很少导入 User32.dll - 像 Windows 服务,甚至是不依赖任何与 User32 相关的消息框的控制台进程等)。
更好的 RCE 方法是拥有一个特权进程(因此您在获取进程句柄方面的限制较少),它依赖于远程线程 Creation/Asynchronous 过程调用来触发简单且格式良好的程序的执行shell-代码(将在该阶段之前写入进程的虚拟内存)以调用 LdrLoadDll (NTDLL)。这也将允许注入本机进程(不依赖任何 Win32 API 模块的进程),只要注入的 DLL 也是本机的。
总的来说,作为一般性评论,您真的不想继续尝试做您寻求帮助的事情(挂接 Win32 API)和我提到的其他挂接方法仅出于理论目的提及完成进程创建监视,而不是为了实际建议您采用这些路径。我怎么强调都不能过分强调专注于文档和稳定性很重要,因为虽然您现在可能不相信,但尝试像您目前正在尝试使用您提出的设计那样做的事情,很容易让您感到困惑结束并破坏破坏。我一点也不夸张。
重要的是要记住,执行远程代码执行通常不是一个好主意,请仅在别无他法时才这样做。一个真正需要它的例子是 AV 供应商试图开发行为 Blocker/HIPS,但没有内核模式回调来过滤他们试图过滤的内容(并且他们可能没有虚拟化支持通过 Intel VT-x 或 AMD SVM 控制 x64 上的内核,而不会导致错误检查)。一个糟糕的理由是它的目的是简单地 "log" 进程创建,特别是当有像 WMI 这样的选项可用时,这些选项仅具有此功能。
请不要误会,但我从经验中了解到,API 挂钩并不是所有时候的答案,通常可以考虑更好的设计。有时候你可能真的要依赖它,但据我所见,大多数人问它甚至不需要依赖它。
如果这不仅仅是一个实验,而且是针对生产级别的,请采用 WMI 或内核模式设备驱动程序方法。如果您采用内核模式设备驱动程序方法,请尽可能少地从内核模式执行操作并在用户模式服务进程中处理过滤。
祝你好运。