winhook

package module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 18, 2022 License: MIT Imports: 6 Imported by: 1

README

winhook

Background

This module came about after looking into performing inline hooking and reading some great material on the subject and also watching several videos on how it is performed in C/C++. What I tended to find was that alot focused on 32bit rather than 64bit and lets be honest when was the last time you worked with 32bit architecture!

Finally I found a fantastic article http://kylehalladay.com/blog/2020/11/13/Hooking-By-Example.html which went through from a simple hook but destructive right through to making a generic hook in 64bit without destroying the original hooked function.

Having this in C/C++ is good however it would be great to be able to have this option and be able to write it in Go to have all the available standard libs available, cross compilation and a single deployed executable not too mention the other numerous reasons using Go for malicous deployments is a great option. I had a good look round to see if anyone else had written any Go module to perform this but was out of luck either they wrapped other hooking libs or used the SetWindowsHookEx which allows certain events to be hooked such as mouse, keyboard but is very limited.

So fast forward and I managed to find a workable solution written mostly in Go but with some interop pieces requiring C. If anyone is able to point me in the right direction on how to make it work 100% Go I'm all ears!

Pre-requisites for hooking

In order to use the package you will need to gather some info on the function you want to hook from the executable or library, I would recommend a decent debugger for this such as x64dbg:

  1. Find the address in memory of where the function resides
  2. Determine if the instructions used by the function are valid to be hooked, cases were hooking is not possible with this library would be if the instructions are less than 5 bytes (relative jmp requires this) or if the instructions include relative positions.
  3. The next step is to work out the signature used by the function, some cases will be easier than other for instance hooking Win32 API functions given the vast amount of MSDN docs out there with how to make calls in C/C++.

Usage

Defining a Go Payload func

This will be the Go func that will be called instead of the hooked func AKA the payload func, it can be named whatever you like and you should define the args of the hooked func as C args:

// typedef unsigned int UINT;
// typedef void* HANDLE;
import "C"

//export goPayloadFunc
func goPayloadFunc(arg1 C.UINT, arg2 C.HANDLE){
    // do actual hooking code here...
}
Defining the C Interop

This is required to capture the hooked call and forward to the Go payload func, then make a call back to the trampoline address:

//
// // required to allow C to make calls into the exported Go func
// extern goPayloadFunc(UINT, HANDLE);
//
// // declare a typedef for the hooked function to be used by the trampoline
// typedef HANDLE HOOKEDFUNC(UINT, HANDLE)
//
// HANDLE Gateway(UINT arg1, HANDLE arg2)
// {
//   // call into the Go func
//   goPayload(arg1, arg2);
//   // then call the trampoline which will in turn return back to the hooked function
//   return trampoline(arg1, arg2);   
// }
//
// // is set against the returned trampoline address
// HOOKEDFUNC *trampoline = NULL;
//
//
import "C"
Making the Hook Call

Finally the hook call can be made with the address of the hooked function, the adress of the C gateway function and finally the number of bytes to steal from the hooked function:

trampolineFunc, err := winhook.InstallHook64(hookedFunc, uintptr(C.Gateway), 5)
if err != nil{
    // handle err
}

// set the C trampoline variable
C.trampoline = (*C.HOOKEDFUNC)(unsafe.Pointer(trampolineFunc))

Examples

I have put together examples that can be used as templates:

deletefile

Hooks into the DeleteFileW function provided by KernelBase. This was chosen over kernel32 as this one simply forwards the call there anyway and there was an appropriate location to install the hook 5 bytes after the function address. The main program is a simple Go program that creates a file and then deletes it, with the hook installed it's possible to monitor when the process performs file deletions.

username

Hooks into the GetUserNameW function provided by advapi32. Instead of just monitoring this example uses the payload function in Go to change the current username returned by this call. This example is rather contrived but demonstrates how manipulation of the calls is possible so could be used for instance to hide information from the user or mislead.

Debugging

As this can require a bit of knowledge of address locations and instructions being written into memory there is a winhook.DebugEnabled flag that can be enabled that will provide diagnostic information in Message Boxes and hopefully help track down why something is not working.

Documentation

Rendered for windows/amd64

Overview

Hooking library for windows, can be used to divert calls made to functions in executables/DLLs at runtime

To use you must know the address to hook and also the signature of the function.

A C function should then be declared with the matching sigature, for example:

extern HANDLE goPayloadFunc(DWORD, HANDLE);

HANDLE HookSetClipboard(DWORD uFormat, HANDLE hMem)

{
		goPayloadFunc(uFormat, hMem);
		return trampoline(uFormat, hMem);
}

The goPayloadFunc should be an exported Go func:

//export goPayloadFunc

func goPayloadFunc(uFormat C.DWORD, hMem C.HANDLE) {
		// do stuff...
	}

The trampoline should be a declared C pointer that also matches the signature of the hooked function:

typedef HANDLE SETCLIPBOARDDATA(DWORD, HANDLE);

SETCLIPBOARDDATA *trampoline = NULL;

When the call to InstallHook is made the returned uintptr should then be casted back to the trampoline variable:

trampolineFunc, err := winhook.InstallHook64(hookAddr, uintptr(C.HookSetClipboard), 5) // handle err

C.trampoline = (*C.SETCLIPBOARDDATA)(unsafe.Pointer(trampolineFunc))

Index

Constants

View Source
const (
	MIN_STEAL_LEN = 5
	MAX_STEAL_LEN = 32
)

Variables

View Source
var DebugEnabled = false

Enable to help with diagnosing issues, writes debug info in messageboxes

Functions

func InstallHook64

func InstallHook64(hookedFunc, payloadFunc uintptr, stealLength int) (uintptr, error)

Installs hook instructions into the hookedFunc address that will redirect calls to the payloadFunc provided instead.

stealLength is the number of instrucitons that need to be taken from the hookedFunc and then moved into the trampoline, this needs to be at least 5 and can be a maximum of 32, to determine this length you need to investigate the instructions in the hookedFunc to know which instructions can be used without leaving part instructions.

payloadFunc should be a C declared function that forwards the call to a Go func and then returns a call to the returned trampoline address

This implementation does not do any disasm to work out if relative positioning needs to be performed, so if your hookedFunc has instructions at the beginning that refer to relative location this will not work!

Types

This section is empty.

Directories

Path Synopsis
examples

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL