使用MS Detours挂钩NtWriteFile

huangapple go评论81阅读模式
英文:

Hooking NtWriteFile with MS Detours

问题

我尝试挂钩 NtWriteFile。下面是我为一个dll编写的代码的精简版本。思路是使用MS Detours的withdll.exe加载生成的dll。通过一些调试,我发现确实调用了MyNtWriteFile,但然后卡在了原始函数调用点(RealNtWriteFile调用)上。对于这个问题是否有任何提示将不胜感激。 使用MS Detours挂钩NtWriteFile

#include "pch.h"
#include <windows.h>
#include <detours.h>
#include <stdio.h>
#include <iostream>
#include <winternl.h>

typedef NTSTATUS(*NtWriteFileFunc)(
    HANDLE FileHandle,
    HANDLE Event,
    PIO_APC_ROUTINE ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID Buffer,
    ULONG Length,
    PLARGE_INTEGER ByteOffset,
    PULONG Key
);

NTSTATUS WINAPI MyNtWriteFile(
    HANDLE           FileHandle,
    HANDLE           Event,
    PIO_APC_ROUTINE  ApcRoutine,
    PVOID            ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID            Buffer,
    ULONG            Length,
    PLARGE_INTEGER   ByteOffset,
    PULONG           Key
)
{
    // 调用原始函数。
    NtWriteFileFunc RealNtWriteFile = (NtWriteFileFunc)GetProcAddress(LoadLibrary(L"ntdll.dll"), "NtWriteFile");
    NTSTATUS tmp = RealNtWriteFile(FileHandle, Event, ApcRoutine, ApcContext,
        IoStatusBlock, Buffer, Length, ByteOffset, Key);

    return tmp;
}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{
    HMODULE hNtdll = LoadLibrary(L"ntdll.dll");

    NtWriteFileFunc RealNtWriteFile = (NtWriteFileFunc)GetProcAddress(hNtdll, "NtWriteFile");

    LONG error;

    if (DetourIsHelperProcess()) {
        return TRUE;
    }

    if (dwReason == DLL_PROCESS_ATTACH) {
        DetourRestoreAfterWith();
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&(PVOID&)RealNtWriteFile, MyNtWriteFile);
        error = DetourTransactionCommit();
    }
    else if (dwReason == DLL_PROCESS_DETACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&(PVOID&)RealNtWriteFile, MyNtWriteFile);
        error = DetourTransactionCommit();
    }
    return TRUE;
}
英文:

I try to hook into NtWriteFile. Below you find a stripped version of the code I wrote for a dll. The idea is to load the resulting dll with the withdll.exe of MS Detours. With some debugging I found that MyNtWriteFile gets indeed called but then gets stuck at the point of the original function call (the RealNtWriteFile call). Any hints on why is that are highly appreciated. 使用MS Detours挂钩NtWriteFile

#include &quot;pch.h&quot;

#include&lt;windows.h&gt;
#include &lt;detours.h&gt;
#include &lt;stdio.h&gt;
#include &lt;iostream&gt;
#include &lt;winternl.h&gt;


typedef NTSTATUS(*NtWriteFileFunc)(
    HANDLE FileHandle,
    HANDLE Event,
    PIO_APC_ROUTINE ApcRoutine,
    PVOID ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID Buffer,
    ULONG Length,
    PLARGE_INTEGER ByteOffset,
    PULONG Key
    );


NTSTATUS WINAPI MyNtWriteFile(
    HANDLE           FileHandle,
    HANDLE           Event,
    PIO_APC_ROUTINE  ApcRoutine,
    PVOID            ApcContext,
    PIO_STATUS_BLOCK IoStatusBlock,
    PVOID            Buffer,
    ULONG            Length,
    PLARGE_INTEGER   ByteOffset,
    PULONG           Key
)
{
    // Call the original function.
    NtWriteFileFunc RealNtWriteFile = (NtWriteFileFunc)GetProcAddress(LoadLibrary(L&quot;ntdll.dll&quot;), &quot;NtWriteFile&quot;);
    NTSTATUS tmp = RealNtWriteFile(FileHandle, Event, ApcRoutine, ApcContext,
        IoStatusBlock, Buffer, Length, ByteOffset, Key);
    
    return tmp;
}

BOOL WINAPI DllMain(HINSTANCE hinst, DWORD dwReason, LPVOID reserved)
{   
    HMODULE hNtdll = LoadLibrary(L&quot;ntdll.dll&quot;);
    
    NtWriteFileFunc RealNtWriteFile = (NtWriteFileFunc)GetProcAddress(hNtdll, &quot;NtWriteFile&quot;);
    
    LONG error;

    if (DetourIsHelperProcess()) {
        return TRUE;
    }

    if (dwReason == DLL_PROCESS_ATTACH) {
        DetourRestoreAfterWith();
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourAttach(&amp;(PVOID&amp;)RealNtWriteFile, MyNtWriteFile);
        error = DetourTransactionCommit();

        
    }
    else if (dwReason == DLL_PROCESS_DETACH) {
        DetourTransactionBegin();
        DetourUpdateThread(GetCurrentThread());
        DetourDetach(&amp;(PVOID&amp;)RealNtWriteFile, MyNtWriteFile);
        error = DetourTransactionCommit();
        
    }
    return TRUE;
}

</details>


# 答案1
**得分**: 2

调用 `RealNtWriteFile` 是一个基本错误,因为这会导致无限递归循环。你需要使用指针,在调用 `DetourAttach` 时进行修改,以调用原始函数。

首先使用静态链接 `ntdll.lib` - 不需要 `GetProcAddress`。

然后声明(在 x64 中,在 x86 中需要小的额外技巧)下一个变量:

```c
EXTERN_C extern PVOID __imp_NtWriteFile;

你需要更改此变量的保护:

VirtualProtect(&__imp_NtWriteFile, sizeof(PVOID), PAGE_EXECUTE_READWRITE, &op);

如果你要拦截多个函数 - 最好首先获取自己的 IAT 部分并更改所有 IAT 的保护,以免多次执行此操作(RtlImageDirectoryEntryToData(&__ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_IAT, &size);)。

然后使用下面的调用:

DetourDetach(&__imp_NtWriteFile, MyNtWriteFile);

恢复 __imp_ / IAT 的保护(可选的)。

MyNtWriteFile 内部,如果你想调用原始函数 - 简单地调用 NtWriteFile 即可。

这一切的意义在于,__imp_NtWriteFile 最初将保存 ntdll!NtWriteFile 的地址(这是由加载器完成的)。

DetourAttach(&__imp_NtWriteFile, myhook) - 在指向 __imp_NtWriteFile 的地址上设置钩子并修改这个指针(它是 _Inout_ 参数)。在成功调用后,__imp_NtWriteFile 将指向 trampoline(一块内存块 - 其中保存了几个原始字节或挂钩函数,并在这些字节之后跳转到函数体)。

NtWriteFile 使用存储在变量 __imp_NtWriteFile 中的值来调用 API。重要的是,API 必须声明为 __declspec(dllimport)

这是常见的做法 - 导入的 someapi 使用 PVOID __imp_someapi 变量。

如果由于某种原因(实际上不需要这样做),不希望静态链接到 ntdll - 无论如何,在这种情况下都要声明和定义

EXTERN_C PVOID __imp_NtWriteFile = 0;

请注意,变量不再是 extern(只声明),而是已定义的。

现在你需要直接调用:

__imp_NtWriteFile = GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtWriteFile");

现在当然不需要 VirtualProtect。

对于 x86 - 名称会被修饰为:

__imp__NtWriteFile@36

不幸的是,我们不能在 C/C++ 代码中直接使用带有 @ 符号的名称。因此,有两种可能的解决方案 - 使用汇编语言 - 在汇编语言中我们可以使用这些名称并从汇编语言中调用 DetourAttach

但更简单的解决方案是使用 /ALTERNATENAME 链接器选项。

所以使用:

#ifdef _X86_
#pragma comment(linker, "/ALTERNATENAME:___imp_NtWriteFile=__imp__NtWriteFile@36")
#endif

如果你静态链接到 ntdll - 变量 __imp__NtWriteFile@36 是存在的 - 它由链接器定义。但我们不能在 cpp 中访问它。相反,我们使用 ___imp_NtWriteFile 作为外部定义。它不存在,我们告诉链接器使用 __imp__NtWriteFile@36

如果你不静态链接到 ntdll,但是自己定义了 __imp_NtWriteFile - 需要反向声明:

#ifdef _X86_
#pragma comment(linker, "/ALTERNATENAME:__imp__NtWriteFile@36=___imp_NtWriteFile")
#endif

因为在这种情况下,__imp__NtWriteFile@36 已经不存在,需要在其中使用 ___imp_NtWriteFile


在钩子内部可以做什么:当然,仅设置钩子并调用原始 API 是没有意义的。因此,真正的代码会执行更多操作。在这里存在递归调用的风险 - 在钩子中调用某些 API,而这些 API 再次间接调用你的钩子。为此,你需要检测递归调用,并在这种情况下 - 直接调用原始 API,不进行任何额外的处理。可以使用 RtlGetFrameRtlPushFrameRtlPopFrame 或 tls 来实现这一点。但这已经是一个单独的问题。

英文:

call RealNtWriteFile is fundamental error. because this lead to infinite reqursive loop. you need use pointer , modified in call DetourAttach, for call original function.

at first use static link with ntdll.lib - not need GetProcAddress.

than declare ( in x64, in x86 need small additional trick) next variable:

EXTERN_C extern PVOID __imp_NtWriteFile;

you need change protect of this variable:

VirtualProtect(&amp;__imp_NtWriteFile, sizeof(PVOID), PAGE_EXECUTE_READWRITE, &amp;op);

if you detour several function - better first get self IAT section and change protect of all IAT, for not do this several times ( RtlImageDirectoryEntryToData(&amp;__ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_IAT, &amp;size); )

and use next call

DetourDetach(&amp;__imp_NtWriteFile, MyNtWriteFile);

restore protection of _imp / IAT ( optional )

and inside MyNtWriteFile, if you want call original function - simply call NtWriteFile as is.

sense of all this is next - __imp_NtWriteFile initially will be hold address of ntdll!NtWriteFile ( this do loader )

the DetourAttach(&amp;__imp_NtWriteFile, myhook) - set hook in address to which point __imp_NtWriteFile and modify this pointer (it Inout ) parameter. after (success) call __imp_NtWriteFile will be point to tramopline ( chunk of memory - where several original bytes or hooked function saved + jmp to function body after this bytes)

and NtWriteFile use value stored at variable __imp_NtWriteFile for call api. main that api must be declared with __declspec(dllimport)

this is common - for imported someapi used PVOID __imp_someapi variable.
if you use delayed import - __imp_load_someapi name is used (but not in x86)

if by some reason (really not need do this) want not static link to ntdll - anyway declare and define in this case

EXTERN_C PVOID __imp_NtWriteFile = 0;

note, that already variable not extern (declared only) but defined.

and you need now direct call __imp_NtWriteFile = GetProcAddress(GetModuleHandleW(L&quot;ntdll.dll&quot;), &quot;NtWriteFile&quot;);

and now you not need VirtualProtect of course.

for x86 - name is mangled it will be

__imp__NtWriteFile@36

unfortunatelly we can not direct use names with @ symbol in c/c++ code. so possible 2 solution - use asm - in it we can have such names and call DetourAttach from asm.

but more simply solution, use /ALTERNATENAME linker option.

so use

#ifdef _X86_
#pragma comment(linker, &quot;/ALTERNATENAME:___imp_NtWriteFile=__imp__NtWriteFile@36&quot;)
#endif

in case you static link to ntdll - the variable __imp__NtWriteFile@36 is exist - it defined by linker. but we can not access it in cpp. instead we use ___imp_NtWriteFile defined as extern. it not exist and we tell linker use __imp__NtWriteFile@36

if you not static link to ntdll, but defined __imp_NtWriteFile by self - need inverted declaration

#ifdef _X86_
#pragma comment(linker, &quot;/ALTERNATENAME:__imp__NtWriteFile@36=___imp_NtWriteFile&quot;)
#endif

because in this case already __imp__NtWriteFile@36 not exist and need use ___imp_NtWriteFile in it place


and what you can do inside hook: of course no sense set hook for only and call original api. so real code will be do something more. and here exist risk or reqursive call - in hook you call some api , and this api indirect again call your hook. for this you need detect reqursive call and in such case - direct call original api, without any extra processing. for this can be used RtlGetFrame, RtlPushFrame, RtlPopFrame or tls. but this is already separare question

huangapple
  • 本文由 发表于 2023年1月9日 08:20:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/75052231.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定