Hooking NtWriteFile with MS Detours



我尝试钩入NtWriteFile. 下面是我为一个dll编写的代码的精简版本。这个想法是用MS Detours的withll .exe加载结果dll。经过一些调试,我发现MyNtWriteFile确实被调用了,但随后在原始函数调用(RealNtWriteFile调用)的点上卡住了。任何关于原因的提示都是非常感谢的。:)

#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
)
{
// Call the original function.
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;
}

callRealNtWriteFile是基本错误。因为这会导致无限递归循环。你需要使用指针,在调用DetourAttach中修改,调用原始函数

首先使用与ntdll的静态链接。lib - not needGetProcAddress.

than declare(在x64中,在x86中需要额外的小技巧)next变量:

EXTERN_C extern PVOID __imp_NtWriteFile;

你需要改变这个变量的保护:

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

如果您绕过几个函数-最好先获得self 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)- set钩子地址,__imp_NtWriteFile修改该指针(itInout)参数。(成功)调用后,__imp_NtWriteFile将指向tramopline(内存块-其中保存了几个原始字节或钩子函数+ jmp到这些字节之后的函数体)

NtWriteFile使用存储在变量__imp_NtWriteFile中的值来调用api。确保api必须__declspec(dllimport)

声明这很常见——对于导入的someapi使用PVOID __imp_someapi变量。如果您使用延迟导入-__imp_load_someapi名称被使用(但不是在x86)

如果出于某种原因(真的不需要这样做)不想静态链接到ntdll -无论如何声明和定义在本例中

EXTERN_C PVOID __imp_NtWriteFile = 0;

注意,这个变量不是extern(只声明),而是定义的。

你现在需要直接调用__imp_NtWriteFile = GetProcAddress(GetModuleHandleW(L"ntdll.dll"), "NtWriteFile");

现在你当然不需要VirtualProtect了

for x86 - name是混乱的,它将是

__imp__NtWriteFile@36

不幸的是,在c/c++代码中,我们不能直接使用带有@符号的名称。所以可能的解决方案-使用asm -在其中我们可以有这样的名称,并从asm调用DetourAttach

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

使用

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

如果你的静态链接到ntdll -变量__imp__NtWriteFile@36是存在的-它是由链接器定义的。但是我们不能在cpp中访问它。相反,我们使用定义为extern的___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来代替


以及在hook中可以做的事情:当然没有意义,只设置hook for并调用原始api。真正的代码会做更多的事情。这里存在的风险是你调用一些API,而这个API又间接调用你的钩子。为此,您需要检测递归调用,在这种情况下-直接调用原始api,而不需要任何额外的处理。为此,可以使用RtlGetFrame,RtlPushFrame,RtlPopFrame或tls。但这已经是单独的问题了

最新更新