Hooking NtWriteFile with MS Detours

499 Views Asked by At

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. :)

#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;
}
1

There are 1 best solutions below

13
RbMm On

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(&__imp_NtWriteFile, sizeof(PVOID), PAGE_EXECUTE_READWRITE, &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(&__ImageBase, TRUE, IMAGE_DIRECTORY_ENTRY_IAT, &size); )

and use next call

DetourDetach(&__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(&__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"ntdll.dll"), "NtWriteFile");

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, "/ALTERNATENAME:___imp_NtWriteFile=__imp__NtWriteFile@36")
#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, "/ALTERNATENAME:__imp__NtWriteFile@36=___imp_NtWriteFile")
#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