> For the complete documentation index, see [llms.txt](https://www.impacket.wiki/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.impacket.wiki/how-to-guides/extending-impacket.md).

# How to Extend Impacket and Contributing New DCERPC Protocols

You need to interact with an RPC interface Impacket doesn't cover, or you need ntlmrelayx to attack a protocol that doesn't have an existing module.

## What you need

* A development Impacket installation (cloned from the repository)
* For DCE/RPC: the Microsoft protocol specification for your target interface
* For ntlmrelayx: understanding of the target protocol's authentication and post-auth capabilities

***

## Part 1: Implementing a new DCE/RPC protocol module

### Extract the interface UUID and endpoint

Every RPC specification defines an interface UUID. Find it in the "Protocol Details" section:

```python
from impacket import uuid

# Example: MS-CAPR (Central Access Policy Remote Protocol)
MSRPC_UUID_LRPC = uuid.uuidtup_to_bin(('497b57d6-6995-4057-b850-89aeb0050ff3', '1.0'))
```

Also identify the transport endpoint: a named pipe (e.g., `\pipe\lsarpc`) or dynamic TCP (resolved via `epm.hept_map`).

### Define NDR data structures

Translate the specification's types into Impacket NDR classes:

```python
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray
from impacket.dcerpc.v5.dtypes import ULONG, LPWSTR, NTSTATUS, HANDLE

# A simple structure
class POLICY_ENTRY(NDRSTRUCT):
    structure = (
        ('Name', LPWSTR),
        ('Description', LPWSTR),
        ('Flags', ULONG),
    )

# A pointer to that structure (required for nullable fields)
class PPOLICY_ENTRY(NDRPOINTER):
    referent = (
        ('Data', POLICY_ENTRY),
    )

# An array of pointers
class POLICY_ENTRY_ARRAY(NDRUniConformantArray):
    item = PPOLICY_ENTRY

class PPOLICY_ENTRY_ARRAY(NDRPOINTER):
    referent = (
        ('Data', POLICY_ENTRY_ARRAY),
    )
```

### Define operations (request/response pairs)

Each operation has a request class with an `opnum` and a matching response class:

```python
# Opnum 0: no parameters
class GetAvailablePolicies(NDRCALL):
    opnum = 0
    structure = ()

class GetAvailablePoliciesResponse(NDRCALL):
    structure = (
        ('Count', ULONG),
        ('Policies', PPOLICY_ENTRY_ARRAY),
        ('ErrorCode', NTSTATUS),
    )

# Opnum 1: takes a context handle
class QueryPolicy(NDRCALL):
    opnum = 1
    structure = (
        ('Handle', HANDLE),
        ('PolicyId', ULONG),
    )

class QueryPolicyResponse(NDRCALL):
    structure = (
        ('Policy', PPOLICY_ENTRY),
        ('ErrorCode', NTSTATUS),
    )
```

### Handle the two-call buffer pattern

Many Windows RPC operations require two calls: the first to determine the required buffer size, the second with the correct size:

```python
from impacket.dcerpc.v5.rpcrt import DCERPCSessionError
from impacket import system_errors

def hBufferedQuery(dce, handle):
    request = BufferedQueryRequest()
    request['Handle'] = handle
    request['BufferSize'] = 0

    try:
        return dce.request(request)
    except DCERPCSessionError as e:
        if e.get_error_code() == system_errors.ERROR_INSUFFICIENT_BUFFER:
            resp = e.get_packet()
            request['BufferSize'] = resp['RequiredSize']
            return dce.request(request)
        raise
```

### Register operations and write helpers

```python
OPNUMS = {
    0: (GetAvailablePolicies, GetAvailablePoliciesResponse),
    1: (QueryPolicy, QueryPolicyResponse),
}

def hGetAvailablePolicies(dce):
    return dce.request(GetAvailablePolicies())

def hQueryPolicy(dce, handle, policy_id):
    request = QueryPolicy()
    request['Handle'] = handle
    request['PolicyId'] = policy_id
    return dce.request(request)
```

### Assemble the module file

Place it at `impacket/dcerpc/v5/yourmodule.py`:

```python
"""
MS-YOURPROTO implementation
Reference: https://learn.microsoft.com/en-us/openspecs/...
"""

from impacket import uuid
from impacket.dcerpc.v5.ndr import NDRCALL, NDRSTRUCT, NDRPOINTER, NDRUniConformantArray
from impacket.dcerpc.v5.dtypes import ULONG, LPWSTR, NTSTATUS

MSRPC_UUID_YOURPROTO = uuid.uuidtup_to_bin(('...', '1.0'))

# Structures
# ...

# Operations
# ...

# OPNUMS
OPNUMS = { ... }

# Helpers
# ...
```

### Test it

```python
from impacket.dcerpc.v5 import transport
from impacket.dcerpc.v5.yourmodule import MSRPC_UUID_YOURPROTO, hGetAvailablePolicies

rpctransport = transport.DCERPCTransportFactory(rf'ncacn_np:{target}[\pipe\yourpipe]')
rpctransport.set_credentials(username, password, domain)

dce = rpctransport.get_dce_rpc()
dce.connect()
dce.bind(MSRPC_UUID_YOURPROTO)

resp = hGetAvailablePolicies(dce)
dce.disconnect()
```

If the bind succeeds but calls return NDR errors, your structure definitions likely don't match the specification. Compare field types and ordering against the wire format diagrams.

### Reference existing modules by complexity

* **`wkst.py`**: simple, under 200 lines, few operations
* **`srvs.py`**: medium, many enumerations with array returns
* **`scmr.py`**: context handles (open/query/close chains)
* **`samr.py`**: deep handle hierarchies (server → domain → user)
* **`drsuapi.py`**: complex structures, encrypted data, large responses

***

## Part 2: Creating ntlmrelayx attack modules

### Write an attack module

Attack modules receive an authenticated session after a successful relay and perform a post-relay action.

Create `impacket/examples/ntlmrelayx/attacks/yourattack.py`:

```python
from impacket import LOG
from impacket.examples.ntlmrelayx.attacks import ProtocolAttack

PROTOCOL_ATTACK_CLASS = "YourAttack"

class YourAttack(ProtocolAttack):
    PLUGIN_NAMES = ["SMB"]  # must match the client's PLUGIN_NAME

    def __init__(self, config, client, username, target=None, relay_client=None):
        ProtocolAttack.__init__(self, config, client, username, target, relay_client)

    def run(self):
        # self.client is the authenticated session:
        #   SMB:  SMBConnection
        #   LDAP: ldap3 Connection
        #   MSSQL: tds.MSSQL
        #   HTTP: HTTPClient with auth

        LOG.info(f"Attacking {self.target.hostname} as {self.username}")

        # Perform your post-relay action
        shares = self.client.listShares()
        for share in shares:
            share_name = share['shi1_netname'][:-1]
            LOG.info(f"  Share: {share_name}")

        # Save results to loot directory
        import os
        output_path = os.path.join(self.config.lootdir, f'{self.target.hostname}_shares.txt')
        with open(output_path, 'w') as f:
            for share in shares:
                f.write(share['shi1_netname'][:-1] + '\n')
```

The framework discovers the class via `PROTOCOL_ATTACK_CLASS` and routes relayed sessions to it based on `PLUGIN_NAMES`.

### Target multiple protocols from one file

```python
PROTOCOL_ATTACK_CLASSES = ["SMBYourAttack", "LDAPYourAttack"]

class SMBYourAttack(ProtocolAttack):
    PLUGIN_NAMES = ["SMB"]
    def run(self):
        # self.client is SMBConnection
        pass

class LDAPYourAttack(ProtocolAttack):
    PLUGIN_NAMES = ["LDAP", "LDAPS"]
    def run(self):
        # self.client is ldap3 Connection
        pass
```

### Access configuration and relayed identity

```python
def run(self):
    # Relayed user identity
    domain_part, user_part = self.username.split('/')

    # Configuration from command-line flags
    command = self.config.command       # from -c flag
    loot_dir = self.config.lootdir     # output directory
    interactive = self.config.interactive  # from -i flag
```

### Write a relay client (for new protocols)

If no client exists for your target protocol, create `impacket/examples/ntlmrelayx/clients/yourclient.py`:

```python
from impacket.examples.ntlmrelayx.clients import ProtocolClient

PROTOCOL_CLIENT_CLASS = "YourProtocolClient"

class YourProtocolClient(ProtocolClient):
    PLUGIN_NAME = "YOURPROTO"

    def __init__(self, serverConfig, target, targetPort=YOUR_PORT, extendedSecurity=True):
        ProtocolClient.__init__(self, serverConfig, target, targetPort, extendedSecurity)

    def initConnection(self):
        self.session = YourProtocolSession(self.targetHost, self.targetPort)
        return True

    def sendNegotiate(self, negotiateMessage):
        # Forward NTLM Type 1, return Type 2 (challenge) from target
        return self.session.send_ntlm_negotiate(negotiateMessage)

    def sendAuth(self, authenticateMessageBlob, serverChallenge=None):
        # Forward NTLM Type 3, return success/failure
        if self.session.send_ntlm_auth(authenticateMessageBlob):
            return True, 0  # success
        return False, 0xC000006D  # STATUS_LOGON_FAILURE

    def killConnection(self):
        if self.session:
            self.session.close()

    def keepAlive(self):
        pass
```

After `sendAuth` succeeds, the attack module receives `self.session` as `self.client`.

### Write a relay server (for new capture protocols)

If you need to capture NTLM from a protocol ntlmrelayx doesn't serve, add a server module in `impacket/examples/ntlmrelayx/servers/`. Study `httprelayserver.py` for the simplest example: it extracts NTLM messages from HTTP `Authorization` headers and passes them to the relay framework.

The key contract: your server must extract the NTLM Type 1 from the incoming protocol, pass it to the relay framework, receive the Type 2 challenge, send it back to the victim, receive the Type 3 authenticate message, and pass it through for relay.

***

## Testing both module types

DCE/RPC modules can be tested directly in a Python script against a live target. Relay modules require triggering authentication (coercion, poisoning, or user interaction) and watching ntlmrelayx output. For relay modules, start ntlmrelayx normally; it auto-discovers your new files from the `attacks/` and `clients/` directories.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://www.impacket.wiki/how-to-guides/extending-impacket.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
