Home > General x86, General x86-64, Programming > Hekate: x86/x64 Winsock Inspection/Modification (Alpha dev release)

Hekate: x86/x64 Winsock Inspection/Modification (Alpha dev release)

September 9th, 2015 Leave a comment Go to comments

Introduction

This post will cover Hekate, a C++ library for interacting with Winsock traffic occurring in a remote process. The purpose of the library is to provide an easy to use interface that allows for inspection, filtering, and modification of any Winsock traffic entering or leaving a target process. Hekate aims to simplify targeted collection of data, aide in reverse engineering protocols, and potentially provide basic security auditing by letting developers fuzz, modify, or replay data being sent to their process.

What it is

Hekate comes provided as a set of components that come together to hook and exfiltrate Winsock data. The final build of the project is a DLL that is injected into the target process. In the project are some of the following:

  1. A generic thread-safe x86/x64 inline hooking engine powered by Capstone Engine, usable for any function hooking (not just Winsock)
  2. IPC based on named pipes to allow sending data to a remote listening process
  3. Winsock specific hooks responsible for matching parameters against filters and taking appropriate action
  4. Several example projects showing the hook, filter, and modify functionality provided by the library
  5. RAII wrappers around Capstone Engine and Windows API objects that automatically clean up upon the resources no longer being needed

These components are combined into the Hekate “app”, which is responsible for handling incoming commands that clients issue and sending captured data out to them.

Architecture

The injected Hekate.dll functions as a server that listens for a client connection to send data out to (once established). The protocols that are communicated between client and server are written to utilize Protocol Buffers and are available in the .proto files contained in the source code. There are eight Winsock functions that are currently being monitored: send/sendto/WSASend/WSASendTo/recv/recvfrom/WSARecv/WSARecvFrom as provided by the Winsock API. For each of these functions, there is a corresponding protobuf message that will copy the parameters, serialize the message, and send it out to a client.  These messages can be found in the HekateServerProto.proto file:

message SendMessage_
{
	required int64 socket = 1;
	required bytes buffer = 2;
	required int32 length = 3;
	required int32 flags = 4;
}
...
message WSARecvFromMessage
{
	required int64 socket = 1;
	repeated int64 buffers = 2;
	repeated int32 buffer_size = 3;
	required int32 count = 4;
	required int64 bytes_received_address = 5;
	required int64 flags_address = 6;
	required int64 from_address = 7;
	required int64 from_length_address = 8;
	required int64 overlapped_address = 9;
	required int64 overlapped_routine_address = 10;
}

A client is responsible for receiving and deserializing these messages. A client is also responsible for issuing commands to the server. At this current dev release, the following commands are supported:

  • Add/Remove a hook on a Winsock function
  • Add/Remove a filter for Winsock data.
  • Pause/Continue execution on filter hit
  • Replay captured data

The client is able to send these commands out immediately after connection to the server is established. The received commands will be processed synchronously to when they are received. The client protocol also provides a debug acknowledge flag that the server will echo back upon a successful receipt of the message (for testing). Additionally, there is copious logging provided throughout the code to notify developers of any potential errors that might have occurred at any stage of usage. The full client-side protocol definition can be found in the HekateClientProto.proto file.

Internals: The Receive & Dispatch Loop

On startup, Hekate initializes two named pipes: \\.\pipe\HekatePipeOutbound and \\.\pipe\HekatePipeInbound. Outgoing messages will be sent on HekatePipeOutbound, and incoming commands will be listened to on HekatePipeInbound. The server will wait for connections on both of these pipes and then spawn a new thread to listen for messages from the client. The incoming/outgoing message format is currently broken into two parts: the first message being a 4-byte size, with the second being the serialized protobuf message of that size. Upon receipt by the server, the message is deserialized and passed to a callback provided by the app. This callback is responsible for parsing and dispatching the message. The flow of incoming messages is IPCNamedPipe::RecvLoop -> HekateMain::RecvCallback -> IMessageHandler::Parse -> HekateMessageHandler::On{Command}Message -> HekateMain::{Command}.

A client talking to the Hekate server mimics this communication behavior closely. A client must open \\.\pipe\HekatePipeOutbound with generic read access and \\.\pipe\HekatePipeInbound with generic write access. Upon the pipe connection being established, the client is free to begin sending commands and listening for responses using the {size} -> {message} scheme described above.

Dynamically Add and Remove Hooks

As mentioned above, Hekate comes with a generic x86/x64 inline hooking engine. On startup, Hekate will locate the target Winsock functions mentioned earlier. Once these are located, the dynamic hooking process can be carried out. When installing a hook, Hekate will disassemble the target function in order to find the appropriate amount of space needed. These instructions will then be relocated to a newly allocated region of memory and have a jump back to the original function. A comprehensive example is shown below:

h0

The original bytes of a send function.

h1

The appropriate amount of space has been calculated for an x86 hook. The bytes have been replaced with a push <target address> -> ret style detour. Extra bytes are padded with int 3 (breakpoint) instructions.

h11The hook function at 0x30BA140 is now being invoked instead when send is being called.

h2

At the end of the hook, the hook will call the relocated bytes, located here at 0x620000. This contains the original bytes that were relocated and a jump back to immediately after the hook in the send function.

This same exact technique is performed for x64 code as well. When a hook is removed, these relocated bytes are written back to the address of the original function and the memory holding the relocated instructions is freed. In an attempt to ensure safe installation and removal, Hekate will suspend all threads (except its own), write the instructions to the process memory, flush the instruction cache, then resume the process threads. An important note is that no hooking takes place on startup or at any point without an explicit command from the client. If the Hekate server DLL is injected into a target, then all it will do is listen for connections; nothing in the original target process will be modified.

Internals: Adding a Hook

The Hekate client protocol describes an easy way to add/remove hooks. Clients simply need to send a message to the server with the name of the desired function to hook/unhook, i.e. “send“, “WSARecv“, and so on. These messages (and more) are in HekateClientProto.proto

message AddHookMessage
{
	required string name = 1;
}

message RemoveHookMessage
{
	required string name = 1;
}

The flow follows the read/dispatch loop until it reaches HekateMain::AddHook, which is responsible for installing the hook and reporting success/failure. The full flow of the code is HekateMain::AddHook -> HookEngine:Add -> HookBase::Install -> InlineHook::InstallImpl -> InlinePlatformHook::Hook -> followed by platform specific calls to InlinePlatformHook_x86::HookImpl/InlinePlatformHook_x64::HookImpl depending on the build. Removing a hook follows a similar path through the files, although obviously calling Unhook/Remove functions instead.

Add and Remove Filters

Hekate allows for filtering of incoming and outgoing Winsock data. Currently there are three supported filter types: byte, length, and substring. Byte filters match against byte(s) found at specific locations in the packet data. Length filters match against packet length for less than, equal to, or greater than a particular size. Lastly, substring filters match against a sequential series of bytes at any location in the packet. Filters are also what allows for manipulation of the data matched against them; you can substitute parts of a message or replace it altogether. Currently filters are matched in a queue: the first filter set will be the first one matched against, the second one will be the second, and so on. There are future plans to add priority to filters, but this dev release does not contain it. Filters also come with a “break-on-hit” flag that allows for the thread calling the target Winsock function to halt when the filter is hit. A client is responsible for sending a continue message to continue execution.

Internals: Adding a Filter and Matching

Adding a filter is initiated entirely on the client-side. The client specifies the match/substitute/replace parameters and forwards this information to the Hekate server, where a new filter of the appropriate type will be created and stored. As an example, below is some sample code that adds a filter and is found in the test filter project provided with the source code:

    auto firstFilter = Hekate::Protobuf::Message::HekateClientMessageBuilder::CreateSubstringFilterMessage(0x123,
        false, "first", 5);
    int replacementIndices1[] = { 12, 13, 14, 15, 16 };
    Hekate::Protobuf::Message::HekateClientMessageBuilder::AddSubstituteMessage(firstFilter, "QWERT", replacementIndices1, 5);
    WriteMessage(hPipeIn, firstFilter);

Here a substring filter with id 0x123 is created that looks for the substring “first”. It will substitute “QWERT” in the packet data at indices [12, 16] if the filter is matched. With this filter, a packet with data = “This is the first buffer” will be matched and replaced to read “This is the QWERT buffer“. The replacement indices do not need to match the indices of where the data was originally. Using replacement indices [0, 4] on the original messages will give “QWERTis the first buffer“.

On the server side, a queue of filters is kept. As mentioned, this queue is processed in the order that filters were created. Filters are initially created and added in HekateMain::AddFilter. For every hook function, i.e. WinsockHooks::SendHook, WinsockHooks::SendToHook, …, WinsockHooks::WSARecvFromHook, the buffer(s) is taken and matched against filters in the queue. This happens in WinsockHooks::CheckFilters, which calls the beginning of the filter chain and reports back whether any filter has been hit. Each filter returns a FilteredInput structure, which contains information about whether the filter was hit, whether there is new/modified data to send out, and the data bytes and length. If a filter was hit and data has changed as a result, then data from this FilteredInput structure is sent out; otherwise the original data will be sent.

Replay Data

Hekate also allows for the complete replaying of data of outgoing data. Parameters are re-sent exactly as they were: to the same socket, with the same buffer and lengths, same flags and any additionally WSA* parameters are provided exactly as received (i.e. same overlapped completion routine address). By design, filters are bypassed when replaying data. Replayed data calls the relocated code instructions and bypasses hitting any hooks/filters.

Dependencies

Hekate relies on Plog for internal logging and Google’s Protocol Buffers for the messaging format between client and server. The protobuf compiler is not provided as part of this release. The compiler source and release binary is available on the Protobuf Github page. The version used for Hekate was build 3.0.0 Alpha 3.

Building the DLL and Examples

Hekate is best built using Visual Studio 2015. Opening up the Hekate.sln file shows six projects

  1. Hekate
  2. HekateMITM
  3. HekateTestFilter
  4. HekateTestListener
  5. HekateTestSender
  6. libprotobuf

Hekate is the main project and contains the DLL that acts as the server. Before building Hekate, libprotobuf needs to be built. Build libprotobuf with a Debug/Release configuration for x86 and x64. These four configurations (Debug x86, Release x86, Debug x64, Release x64) should result in successful builds and there will be four .lib files in the /Hekate/thirdparty/protobuf/lib directory. Make sure that these four .lib files, libprotobufd_x86.lib, libprotobuf_x86.lib, libprotobufd_x64.lib, and libprotobuf_x64.lib are present in the directory as they are needed for the different build configurations. Once this is done, the Hekate project can be built. This project must be built with DebugDll/ReleaseDll configurations instead of Debug/Release. The latter two have been left in for the project in case developers want to mess around with an executable locally instead of building a DLL that needs to be injected. Using these two configurations should result in Hekate.dll being built in the DebugDll/ReleaseDll directories.

There are also four sample projects that serve as sample targets or clients for Hekate. HekateMITM is a sample client/server application that sends and receives data over localhost. One thread is responsible for sending data and the other for receiving. This sample project should be buildable immediately under x86/x64 and has no dependencies. It is intended as a target to test out functionality provided by other projects. HekateTestFilter and HekateTestListener are two sample Hekate clients. HekateTestFilter sets up three different filters, one corresponding to each type. It will substitute bytes in one message, replace bytes entirely in another, and pause execution for five seconds on a third message. A run of HekateMITM and HekateTestFilter is shown below. You can see the filters at work, where the first message type was modified and the second message type replaced entirely (click to enlarge).

c1

HekateTestListener is a passive listener client that will print out the value of the parameters passed into the Winsock functions along with the buffer. HekateTestSender is just a simple target application useful in that calls the eight Winsock functions in a loop useful for debugging/testing.

Code

The Visual Studio 2015 project for this example can be found here. The source code is viewable on Github here.
This code was tested on x64 Windows 7, 8.1, and 10.

Issues

I’ve aimed to have very comprehensive logging contained in the code. The log file is currently written out to C:/Temp/log.txt, and is a good starting point if an error has occurred at runtime. Hekate.dll also relies on Capstone, so capstone_x86.dll/capstone_x64.dll must be present in the same directory as the target.

License

Hekate is provided as-is and is released under the GNU General Public Licence (GPL) v3 for non-commercial use only.

The code base will continue to evolve and features will continue to be added. The content covered in this post might eventually become outdated as a result of this. I am aiming to have each major release/update act as a changelog from this main post. Future plans as far as this project goes is to eventually develop a nice UI wrapper around it that allows for easy interaction and visualization of data, filters, and other related aspects of what is happening to Winsock traffic on a target process. Thanks for reading and be sure to follow on Twitter for more updates.

Categories: General x86, General x86-64, Programming Tags:
  1. September 9th, 2015 at 16:59 | #1

    It would be good to add some more checks in hook engine, so it will not move relative calls/jumps on x86 and RIP-relative addressed opcodes on x64. Apart from that, really nice project 🙂

  2. admin
    September 9th, 2015 at 18:30 | #2

    @ReWolf
    Yes, you’re right — that case slipped my mind. I’ll certainly add it to the list of things for the next release. Thanks.

  1. No trackbacks yet.