NetHook SDK

A lightweight client library for talking to a NetHook-hooked process over its named pipe — in Python 3 and C#. Connect to an injected process, enable hooks, stream captured traffic, and optionally intercept & rewrite it on the wire.

Overview

When the NetHook payload is injected into a target process it starts a named-pipe server at \\.\pipe\nethook_<pid>. Everything the target sends or receives through the hooked APIs (sockets, TLS, WinINet / WinHTTP, file I/O…) is emitted over that pipe.

This SDK is the other end: a small client that connects to the pipe, turns on the hooks it cares about, and receives every captured packet. It is the same wire protocol the NetHook GUI speaks, distilled to a single drop-in file per language with no third-party dependencies.

What you get

  • Connect by PID (or explicit pipe name)
  • Enable any subset of hooks via a bitmask
  • Stream DATA packets (capture-only)
  • Intercept: forward, drop, or modify
  • Detach (unhook & unload the DLL)

Requirements

  • Windows only (named pipes + the payload).
  • A process already injected with NetHook, so the pipe exists.
  • Python 3.6+ — stdlib only (ctypes).
  • .NET 6+ — BCL only (System.IO.Pipes).
Note: the SDK does not inject the payload — use the NetHook GUI or nethook_inject<arch>.exe for that. The SDK connects to a pipe that already exists.

Package layout

This release contains only what you need to build on top of the SDK. The C# binding ships as a compiled DLL (no C# source); the Python binding ships as a single module.

Structure
SdkRelease/
  documentation.html    All documentation (this single file)
  csharp/
    NetHook.Sdk.dll     The library — reference this DLL (net8.0)
    NetHook.Sdk.xml     IntelliSense / XML docs for the DLL
  python/
    nethook.py          The SDK — single module, standard library only
    pyproject.toml      Optional pip packaging
  example/
    python/             01_capture_traffic.py, 02_intercept_edit.py
    csharp/             01-capture-traffic/, 02-intercept-edit/

Quick start

Both SDKs mirror the same shape. Given a hooked process with PID 1234:

Python capture everything

Python
from nethook import NetHookClient, Func

with NetHookClient(1234) as nh:
    nh.send_config(Func.ALL)
    for ev in nh.events():
        if ev.kind == "data":
            print(ev.packet.summary())

C# capture everything

C#
using NetHook.Sdk;

using var nh = new NetHookClient(1234);
await nh.ConnectAsync();
nh.Data += p => Console.WriteLine(p.Summary());
nh.SendConfig(Func.All);
nh.Run();   // blocks until the pipe closes

The pipe protocol

Every message is a fixed 20-byte header followed by a type-specific payload, all little-endian and packed. The client and server exchange these framed messages over the byte-mode pipe.

Header (20 bytes)

FieldTypeMeaning
magicu320x314B484E ("NHK1"). Resync sentinel — a mismatch means the stream is corrupt.
typeu32Message type (see table).
idu64Correlation id. For an INTERCEPT it is the ref echoed back in a DECISION; otherwise 0.
lengthu32Payload byte count that follows the header (may be 0).

Conversation

1. Client opens \\.\pipe\nethook_<pid>.
2. Server → HELLO  (pid, arch, image path).
3. Client → CONFIG  (which hooks; capture / intercept / block).
4. Server → DATA / LOG streamed as traffic happens.
   or, in intercept mode:
   Server → INTERCEPT (packet held) → Client → DECISION (forward / drop / modify).
5. Client → DETACH (optional) → Server unhooks, unloads, and the pipe closes (BYE/EOF).
Roles: the injected DLL is the pipe server; the SDK is the client. The DLL greets you with HELLO the moment you connect — no capture happens until you answer with a CONFIG that enables at least one hook.

Constants

ConceptC# (NetHookClient)Python (nethook)Value
Frame magicMagicMAGIC0x314B484E ("NHK1")
Pipe prefixPipePrefixPIPE_PREFIX\\.\pipe\nethook_
Header sizeHeaderSizeHEADER_SIZE20

Enums

EnumMembers (value)
DirectionSend (1), Recv (2)
NhAction (C#) / Action (Python)Forward (0), Drop (1), Modify (2)
Func31 hook bits; All = 0x7FFFFFFF (see table)
MsgTypesee the message-type table

nh_data payload layout (68 bytes, then payload)

The body of a DATA / INTERCEPT message; Python struct "<QIIQIHH16s16sI".

OffsetSizeFieldMeaning
08timestamp_msms since epoch
84funcwhich Func bit captured it
124direction1 = Send, 2 = Recv
168socketSOCKET or SSL* handle (informational)
244family2 = AF_INET, 23 = AF_INET6
282local_porthost order
302remote_porthost order
3216local_addrraw address bytes (4 for IPv4, 16 for IPv6)
4816remote_addrraw address bytes
644dlenpayload byte count that follows
68dlendatathe captured bytes

CONFIG is 16 bytes ("<IBBxxII": enabled_funcs u32, intercept u8, block u8, pad, max_capture u32, file_cap u32; a short 12-byte config without file_cap is still accepted); DECISION is 16 bytes ("<QII": ref_id u64, action u32, data_len u32) followed by replacement bytes when the action is Modify.

Examples

The example/ folder ships two runnable samples per language, each using the shipped SDK. Every sample needs a target that is already injected with the NetHook payload.

Intercept mode: you must answer every intercepted packet (forward or drop) — an unanswered packet leaves the target thread blocked.
ExamplePythonC#What it shows
Capture traffic example/python/01_capture_traffic.py example/csharp/01-capture-traffic/ Enable every hook and print a one-line summary of each packet.
Intercept & edit example/python/02_intercept_edit.py example/csharp/02-intercept-edit/ Hold outbound packets and edit the wire, replacing nethook with netbook.
Shell
# Python (module on the import path)
python example/python/01_capture_traffic.py 1234
python example/python/02_intercept_edit.py  1234
Shell
# C# (each project references csharp/NetHook.Sdk.dll)
dotnet run --project example/csharp/01-capture-traffic -- 1234
dotnet run --project example/csharp/02-intercept-edit  -- 1234

Python Install & connect

Copy python/nethook.py next to your script — there is nothing mandatory to pip install. It uses only the standard library (ctypes, socket, struct). A pyproject.toml is included if you prefer pip install ./python.

from nethook import NetHookClient, Func, Action

nh = NetHookClient(pid=1234)        # or NetHookClient(pipe_name=r"\\.\pipe\nethook_1234")
nh.connect(timeout_ms=4000)         # waits for the pipe to appear
# ... use nh ...
nh.close()

# Or as a context manager (auto connect on enter, close on exit):
with NetHookClient(1234) as nh:
    nh.send_config(Func.SSL_WRITE | Func.SSL_READ)
    for ev in nh.events():
        ...

Intercept example

Python
with NetHookClient(1234) as nh:
    nh.send_config(Func.ALL, intercept=True)
    for ev in nh.events():
        if ev.kind == "intercept":
            pkt = ev.packet
            if b"password" in pkt.data.lower():
                nh.drop(pkt)                       # block it
            else:
                nh.forward(pkt)                    # or nh.forward(pkt, new_bytes)

Python API reference

class NetHookClient

MemberDescription
NetHookClient(pid=None, pipe_name=None)Construct for a PID or an explicit pipe name.
connect(timeout_ms=4000) → selfOpen the pipe, waiting for the server to appear. Raises OSError on timeout.
close()Close the pipe handle. Also runs on context-manager exit.
connectedTrue while the handle is open.
read_event() → EventBlock for one message, decoded into an Event.
events()Generator yielding Events until the pipe closes (bye/EOF).
read_message() → (type, id, payload)Low-level: one raw framed message.
send_config(funcs, intercept=False, block=False, max_capture=0, file_cap=0)Enable hooks & capture behaviour. funcs is a Func bitmask; file_cap is the hard per-call ReadFile/WriteFile ceiling.
decision(ref_id, action, data=b"")Answer an intercept by ref id with an Action.
forward(packet, data=None)Let an intercepted packet through, optionally rewriting it.
drop(packet)Drop an intercepted packet.
detach()Ask the DLL to unhook and unload itself.

class Event

ev.kind is one of "hello", "data", "intercept", "log", "bye". Populated fields by kind:

kindFields
hellopid, arch (32/64), image (exe path)
data / interceptpacket — a Packet
logtext
bye— (target detached / pipe closed)

class Packet

See the packet field reference. Helpers: packet.summary(), packet.ascii_preview(limit=64), packet.api, packet.out, packet.source, packet.dest. Raw bytes are packet.data.

Constants

Func (hook bits, see table), Action.FORWARD / DROP / MODIFY, Direction.SEND / RECV, MsgType.*.

C# Install & connect

The C# SDK ships as a compiled library, csharp/NetHook.Sdk.dll (namespace NetHook.Sdk, targets .NET 8, uses only System.IO.Pipes). There is no C# source to build — reference the DLL from your project, keeping NetHook.Sdk.xml beside it for IntelliSense:

XML (.csproj)
<ItemGroup>
  <Reference Include="NetHook.Sdk">
    <HintPath>path\to\csharp\NetHook.Sdk.dll</HintPath>
  </Reference>
</ItemGroup>

Two ready-to-run example projects that already reference the DLL are in example/csharp/. Basic usage:

C#
using NetHook.Sdk;

using var nh = new NetHookClient(1234);   // or new NetHookClient("nethook_1234", 1234)
await nh.ConnectAsync(timeoutMs: 4000);   // or nh.Connect(4000) for the sync path

nh.Hello += (pid, arch, image) => Console.WriteLine($"{image} ({arch}-bit)");
nh.Data  += pkt => Console.WriteLine(pkt.Summary());

nh.SendConfig(Func.SslWrite | Func.SslRead);
nh.Run();      // blocking read loop; use RunAsync() for a Task

Intercept example

C#
nh.Intercepted += pkt =>
{
    var text = System.Text.Encoding.ASCII.GetString(pkt.Data);
    if (text.Contains("password")) nh.Drop(pkt);
    else                           nh.Forward(pkt);   // or nh.Forward(pkt, newBytes)
};
nh.SendConfig(Func.All, intercept: true);
nh.Run();

C# API reference

class NetHookClient : IDisposable

MemberDescription
NetHookClient(int pid)Construct for a PID.
NetHookClient(string pipeName, int pid)Construct for an explicit pipe name.
Task ConnectAsync(int timeoutMs=4000, ct)Open the pipe (async).
void Connect(int timeoutMs=4000)Open the pipe (sync).
bool ConnectedPipe currently connected.
void Run(ct) / Task RunAsync(ct)Read loop that raises the events below until the pipe closes.
bool ReadMessage(out type, out id, out payload)Low-level: one raw message; false at EOF.
void SendConfig(Func funcs, bool intercept=false, bool block=false, uint maxCapture=0, uint fileCap=0)Enable hooks & capture behaviour. fileCap is the hard per-call ReadFile/WriteFile ceiling.
void SendDecision(ulong refId, NhAction action, byte[]? data=null)Answer an intercept.
void Forward(Packet pkt, byte[]? data=null)Forward (optionally rewrite) an intercepted packet.
void Drop(Packet pkt)Drop an intercepted packet.
void Detach()Ask the DLL to unhook and unload.

Events (raised on the read loop)

EventSignature
HelloAction<int pid, int arch, string image>
DataAction<Packet>
InterceptedAction<Packet>
LogAction<string>
ByeAction (pipe closed)

class Packet

Fields per the reference; helpers Summary(), AsciiPreview(limit), properties Api, Out, Source, Dest. Raw bytes are Data.

Constants

Enums Func (hook bits, see table), NhAction, Direction, MsgType; constants NetHookClient.Magic, NetHookClient.HeaderSize.

Message types

NameValueDirPayload
HELLO1server → clientpid u32, arch u32, name_len u32, UTF-8 image path
BYE2server → clientnone (target detaching)
LOG3server → clientUTF-8 text (debug builds)
DATA4server → clientnh_data + captured bytes
INTERCEPT5server → clientnh_data + bytes; held for a DECISION
CONFIG100client → serverfuncs u32, intercept u8, block u8, pad[2], max_capture u32
DECISION101client → serverref_id u64, action u32, data_len u32, replacement bytes
DETACH102client → servernone
REPLACE103client → serversearch-and-replace rule set (advanced; not wrapped in the SDK)
FILE_FILTER104client → serverfile-path capture patterns (advanced; not wrapped in the SDK)
Config flags: intercept holds every packet for a DECISION; block drops all traffic on the enabled hooks; max_capture caps bytes captured per event (0 = unlimited), handy for large file / socket payloads; file_cap is a hard per-call ceiling on ReadFile/WriteFile capture (0 = built-in 256 B default, max 8 MiB).

Hook functions

Pass any OR-combination to send_config / SendConfig. Use Func.ALL (Func.All) for everything.

BitPython / C#Hooked API
0SENDsend
1RECVrecv
2WSASENDWSASend
3WSARECVWSARecv
4SSL_WRITESSL_write (OpenSSL)
5SSL_READSSL_read (OpenSSL)
6ENCRYPTEncryptMessage (Schannel)
7DECRYPTDecryptMessage (Schannel)
8SENDTOsendto
9RECVFROMrecvfrom
10READFILEReadFile
11WRITEFILEWriteFile
12INET_READInternetReadFile (WinINet)
13INET_WRITEInternetWriteFile (WinINet)
14HTTP_SENDHttpSendRequest (WinINet)
15WINHTTP_SENDWinHttpSendRequest / WriteData
16WINHTTP_RECVWinHttpReadData
17SSL_WRITE_EXSSL_write_ex (OpenSSL 1.1.1+/3.x)
18SSL_READ_EXSSL_read_ex (OpenSSL 1.1.1+/3.x)
19PR_WRITEPR_Write (NSS/NSPR — Firefox)
20PR_READPR_Read (NSS/NSPR)
21GNUTLS_SENDgnutls_record_send
22GNUTLS_RECVgnutls_record_recv
23WOLFSSL_WRITEwolfSSL_write
24WOLFSSL_READwolfSSL_read
25DPAPI_PROTECT / DpapiProtectCryptProtectData (crypt32 — cleartext in)
26DPAPI_UNPROTECT / DpapiUnprotectCryptUnprotectData (crypt32 — cleartext out)
27DPAPI_PROTECT_MEM / DpapiProtectMemCryptProtectMemory (crypt32)
28DPAPI_UNPROTECT_MEM / DpapiUnprotectMemCryptUnprotectMemory (crypt32)
29RTL_ENCRYPT_MEM / RtlEncryptMemSystemFunction040 / RtlEncryptMemory (advapi32)
30RTL_DECRYPT_MEM / RtlDecryptMemSystemFunction041 / RtlDecryptMemory (advapi32)
DPAPI / in-memory protection (bits 25–30): these capture the cleartext blob before it is encrypted (Protect) or after it is decrypted (Unprotect) — passwords, tokens, cookies. Observe-only: they never hold or rewrite the call.

Packet fields

A Packet is the decoded nh_data record plus its captured bytes. Not every field is meaningful for every hook — TLS, file and WinHTTP captures carry no socket endpoint, so the address/port fields are empty for them.

PythonC#Meaning
msg_idMsgIdCorrelation id (echo in a decision); 0 for plain DATA.
is_interceptIsInterceptTrue if held for a decision.
timestamp_msTimestampCapture time (ms since epoch / local DateTime).
func / apiFunc / ApiWhich hook captured it; api is the real API name.
direction / outDirection / OutSend (out) vs recv (in).
socketSocketSOCKET / SSL* handle (informational).
familyFamilyAddress family (2 = IPv4, 23 = IPv6).
local_addr, local_portLocalAddr, LocalPortLocal endpoint (socket hooks only).
remote_addr, remote_portRemoteAddr, RemotePortRemote endpoint (socket hooks only).
dataDataThe captured payload bytes (possibly truncated to max_capture).

Error handling

SituationC#Python
Pipe never appears / timeoutConnect / ConnectAsync throwconnect() raises OSError
Bad frame magic (desync)InvalidDataExceptionValueError
Pipe closed mid-streamRun catches IOException and fires Byeevents() stops on ConnectionError/OSError
Clean shutdown from targetBYE ends Run, fires Bye"bye" event ends events()
Sending before connectInvalidOperationExceptionConnectionError
Intercept mode: always answer every intercepted packet (forward or drop) — an unanswered packet leaves the target thread blocked. Dispose/close the client (using / with) to release the pipe handle. Detach tells the DLL to unhook and eject entirely.