Skip to main content

Module modes

Module modes 

Source
Expand description

§⚙️ Network-mode vs. Buffer-mode

SaM implements two network simulation modes, selected based on the target binary’s characteristics:

  1. Buffer-mode: For malware sample using standard OS networking primitives, SaM intercepts the network buffer directly at the relevant network API call, injecting synthetic response data without completing the actual network operation. This approach integrates naturally with coverage-guided fuzzers (e.g., WinAFL) and offers high throughput.
  2. Network-mode: For samples that implement custom networking stacks (e.g., Go binaries), SaM delegates to a network emulation layer (e.g., FakeNet-NG) that intercepts traffic at the network interface. While slower, this approach handles cases where buffer-level interposition is impractical.

The following schematic compares the call stacks for the two modes. The additional traversal through the kernel network stack reduces throughput, which is a disadvantage when fuzzing.

A schematic comparing network-level and buffer-level interposition

Both modes apply the same instrumentation elsewhere (e.g., for collecting coverage information and conducting fuzzing rollbacks) - the choice is driven by compatibility rather than analytical capability.

§When and Why is Network-mode needed

We conducted research experiments with FrostyGoop (see our paper for more details) which was written in the Go programming language. We found out that the net package in the Go standard library uses an async network stack when building for Windows (as shown here in the golang source code).

Thankfully, the instrumentation to hook the functions in the Windows async network stack is included in SaM, so observing outbound requests from network clients written in Go (such as the FrostyGoop malware) was no problem.

However, injecting responses into an async networking stack is much more complicated. After writing to the correct buffer, the client runtime must be notified that the buffer is populated, and several mechanisms exist for this async signalling. Writing implementations to cover all mechanisms is a lot of development work, and at this stage we have opted to allow the OS kernel to do this work for us.

As such, Network-mode is required for binaries that use Windows async network APIs, or any other complex socket management workflows that are not currently handled in SaM’s socket simulation (e.g. getting and setting socket options with getsockopt and setsockopt).

§Why Network-mode is only a partial solution, and a suggested refactor for a full solution

It is currently a requirement that SaM observes the outbound requests from the target binary, even when using Network-mode to supply responses at the network-level. This precludes working with other complex networking stacks (such as a binary that uses low-level ioctl commands in favour of a high-level network API).

This is not a necessary constraint, it is an implementation artefact that can be resolved with the following code refactor:

  • Currently, when an outbound request is observed on a network API hook, the configured Trajectory for the run is used to determine the planned response. This logic all resides in libinject, and executes in the runtime of the target binary during the hook.
  • If running in Network-mode, the decided response (along with the request) is piped to a server as a HalfDuplexExchange. This server is responsible for interposing on the network event at the network-level.
  • In this setup, the server behaves as follows: i) receive a request, ii) block on the pipe waiting for a HalfDuplexExchange, iii) verify that the request in the HalfDuplexExchange matches the received request, iv) respond to the request with the response included in the HalfDuplexExchange.
  • Naturally, given that the server receives the request independently of the network API hook in libinject, the hook, pipe and HalfDuplexExchange can all be removed - as long as the server can run the logic (in it’s own runtime) to decide how to respond to each request.
  • As such, the refactor should achieve the following:
    1. in Buffer-mode, use the configured Trajectory to decide responses to requests in the runtime of the target binary (i.e. in libinject) - this is currently implemented
    2. in Network-mode, pass the configured Trajectory to the server when it is initialised, and add logic to the server to use the Trajectory to decide responses to requests in the runtime of the server (i.e the logic probably goes in navigator/src/server.rs).

§Network-mode does not support fuzzing in parallel

Running fuzzing sessions in parallel greatly increases fuzzing throughput, as discussed in the Configuring Fuzzing Sessions section of the Getting Started guide.

Setting the number of fuzzers to set up in parallel (with the --parallel flag) is not permitted when running in Network-mode (which is set using the --server-managed flag).

This is because parallel fuzzing currently only supports running multiple fuzzers on one machine, and Network-mode requires an OS instance per fuzzer (because it delgates the response injection to the OS).

§A possible solution requiring further development

WinAFL does support multi-system parallelization, discussed in the docs here. However this has not yet been implemented for SaM.

It is possible that multi-system parallelization might provide the option to fuzz in parallel with Network-mode, by running one fuzzer per machine across mutliple machines.