-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
169 additions
and
119 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
# Breakpoint Hooks | ||
|
||
You may perform hooking using i386 hardware breakpoints. `BreakpointHook` is thread-specific because it relies on the CPU's debug registers being set in the thread's context. You're also limited to four breakpoints per thread. | ||
|
||
Additionally, you will not be able to debug the hooking function. The attached debugger will see the hardware breakpoint as a Single Step debug event and will either handle it (so `BreakpointHook` cannot handle it), or it will wait indefinitely for user input before continuing (resulting in deadlock). For this reason, `BreakpointHook` will throw an exception if the process is being debugged. | ||
|
||
```C# | ||
static INativeHook? CreateFileWHook; | ||
static delegate* unmanaged[Stdcall]<IntPtr, uint, uint, IntPtr, uint, uint, IntPtr, IntPtr> CreateFileW_original; | ||
|
||
public static int Bootstrap(IntPtr argument, int size) | ||
{ | ||
using var currentProc = Process.GetCurrentProcess(); | ||
var firstThread = currentProc.Threads.Cast<ProcessThread>().MinBy(t => t.StartTime); | ||
|
||
delegate* unmanaged[Stdcall]<IntPtr, uint, uint, IntPtr, uint, uint, IntPtr, IntPtr> hook3 = &CreateFileW_hook; | ||
CreateFileWHook | ||
= currentProc | ||
.GetModulesByName("kernel32") | ||
.FirstOrDefault() | ||
?.GetExportByName("CreateFileW") | ||
?.Hook(hook3, firstThread, installAfterCreate: false); | ||
|
||
if (CreateFileWHook?.OriginalFunction is not null or 0) | ||
{ | ||
CreateFileW_original = | ||
(delegate* unmanaged[Stdcall]<IntPtr, uint, uint, IntPtr, uint, uint, IntPtr, IntPtr>) | ||
CreateFileWHook.OriginalFunction; | ||
|
||
CreateFileWHook.InstallHook(); | ||
} | ||
} | ||
|
||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] | ||
static IntPtr CreateFileW_hook( | ||
IntPtr lpFileName, uint dwDesiredAccess, uint dwShareMode, | ||
IntPtr lpSecurityAttributes, uint dwCreationDisposition, | ||
uint dwFlagsAndAttributes, IntPtr hTemplateFile) | ||
{ | ||
var result | ||
= CreateFileW_original( | ||
lpFileName, | ||
dwDesiredAccess, | ||
dwShareMode, | ||
lpSecurityAttributes, | ||
dwCreationDisposition, | ||
dwFlagsAndAttributes, | ||
hTemplateFile); | ||
|
||
return result; | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
# Hooking Imported Functions | ||
Hooking imports is accomplished by replacing the target function's address in a module's import address table with a pointer to the hook delegate. The hook delegate can be either an `[UnmanagedCallersOnly]` method or a managed delegate with the same signature as the imported function. The original function pointer is stored in `ImportHook.OriginalFunction` and can be called by creating a delegate for it. | ||
|
||
In the sample, all of notepad.exe's calls to `WriteFile` will call `WriteFile_hook`, and `WriteFile_hook` modifies the parameters before calling `Kernel32.WriteFile`. | ||
|
||
```C# | ||
static INativeHook? WriteFileHook; | ||
static WriteFileDelegate? WriteFile_original; | ||
|
||
delegate bool WriteFileDelegate( | ||
IntPtr hFile, | ||
byte* lpBuffer, | ||
int nNumberOfBytesToWrite, | ||
ref int lpNumberOfBytesWritten, | ||
IntPtr lpOverlapped); | ||
|
||
public static int Bootstrap(IntPtr argument, int size) | ||
{ | ||
//Hook kernel32.WriteFile in the main module's import table | ||
WriteFileHook | ||
= Process | ||
.GetCurrentProcess() | ||
.MainModule | ||
?.GetImportByName("kernel32", "WriteFile") | ||
?.Hook(WriteFile_hook); | ||
|
||
if (WriteFileHook is not null) | ||
WriteFile_original = Marshal | ||
.GetDelegateForFunctionPointer<WriteFileDelegate>(WriteFileHook.OriginalFunction); | ||
} | ||
|
||
static bool WriteFile_hook( | ||
IntPtr hFile, | ||
byte* lpBuffer, | ||
int nNumberOfBytesToWrite, | ||
ref int NumberOfBytesWritten, | ||
IntPtr lpOverlapped) | ||
{ | ||
return WriteFile_original!(hFile, lpBuffer, nNumberOfBytesToWrite, ref NumberOfBytesWritten, lpOverlapped); | ||
} | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
# Hooking Exported Functions (Jump Hooks) | ||
|
||
Hooking exports is accomplished by overwriting the original function's entry point instructions with a jump to a block of memory allocated nearby. NativeHelper will attempt to create a trampoline (using a C# port of [minhook](https://github.com/TsudaKageyu/minhook)). If successdul, the original function may be called without removing the hook. If trampoline creation failed, the hook must be removed via `RemoveHook()` before calling the original function. See the `HasTrampoline` property. In both cases, calls to the original function will jump to the hook delegate when the hook is installed. The delegate can be either an `[UnmanagedCallersOnly]` method or a managed delegate with the same signature as the exported function. | ||
|
||
In the sample, all calls to `ReadFile` within notepad.exe's process will call `ReadFile_hook`. `ReadFile_hook` peeks at the parameters, calls `Kernelbase.ReadFile`, and then returns the file handle. | ||
|
||
You may create a jump hook for an arbitrary instruction using `JumpHook.Create()`. | ||
|
||
```C# | ||
using BOOL = System.Int32; | ||
|
||
static INativeHook? ReadFileHook; | ||
static delegate* unmanaged[Stdcall]<IntPtr, byte*, int, int*, IntPtr, BOOL> ReadFile_original; | ||
|
||
public static int Bootstrap(IntPtr argument, int size) | ||
{ | ||
delegate* unmanaged[Stdcall]<IntPtr, byte*, int, int*, IntPtr, BOOL> hook2 = &ReadFile_hook; | ||
ReadFileHook | ||
= currentProc | ||
.GetModulesByName("kernel32") | ||
.FirstOrDefault() | ||
?.GetExportByName("ReadFile") | ||
?.Hook(hook2); | ||
|
||
if (ReadFileHook is not null) | ||
ReadFile_original = | ||
(delegate* unmanaged[Stdcall]<IntPtr, byte*, int, int*, IntPtr, BOOL>) | ||
ReadFileHook.OriginalFunction; | ||
} | ||
[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] | ||
static BOOL ReadFile_hook(IntPtr hFile, byte* lpBuffer, int nNumberOfBytesToWrite, int* lpNumberOfBytesWritten, IntPtr lpOverlapped) | ||
{ | ||
var result = ReadFile_original(hFile, lpBuffer, nNumberOfBytesToWrite, lpNumberOfBytesWritten, lpOverlapped); | ||
return result; | ||
} | ||
``` |