windows 将NtWriteFile与MS Detours挂钩

yrdbyhpb  于 2023-01-10  发布在  Windows
关注(0)|答案(1)|浏览(137)

我尝试连接到NtWriteFile。下面是我为dll编写的代码的精简版本。想法是用MS Detours的withdll.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;
}
93ze6v8z

93ze6v8z1#

调用RealNtWriteFile是一个基本错误。因为这会导致无限循环。你需要使用指针,在调用DetourAttach时修改过,用于调用原始函数。
首先使用ntdll.lib的静态链接-不需要GetProcAddress
然后声明(在x64中,在x86中需要小附加技巧)下一个变量:

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的地址(此do加载程序)
DetourAttach(&__imp_NtWriteFile, myhook)-在__imp_NtWriteFile所指向地址中设置挂钩,并修改此指针(itInout)参数。在(成功)调用__imp_NtWriteFile后,将指向tramopline(内存块-其中保存了几个原始字节或挂钩函数+此字节后的函数体jmp)
NtWriteFile使用存储在变量__imp_NtWriteFile处的值用于调用api.main 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了。
对于x86-名称已损坏,将为
__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
以及你在钩子里能做什么当然没有意义只设置钩子和调用原始api.所以真正的代码将做更多的事情.并且这里存在风险或reqursive调用-在钩子中你调用一些api,并且这个api间接的再次调用你的钩子.为此你需要检测reqursive调用并且在这种情况下-直接调用原始api,没有任何额外的处理.为此可以使用RtlGetFrameRtlPushFrameRtlPopFrame或tls。但这已经是单独的问题

相关问题