Hook–intercept IP address access

Hook – Block IP address access:

1. Demand

  1. No process (real process) is allowed to access the websites in the blacklist;
  2. Use logs (in the form of process name + time + access object + result) to record intercepted access records and other successful access records;
  3. Check the blacklist regularly for changes.

2. Demand analysis

1. Goal:
– Implement a network access filter that allows or denies network connections and accepts requests based on a list of IP addresses in a blacklist.

2. Main functions:

- Blacklist management: The program maintains a blacklist of IP addresses, which is used to store IP addresses that are prohibited from access.
    - Connection filtering: Block connection attempts to IP addresses in the blacklist.
    - Access records: Record successful and intercepted network access events, including timestamp, process ID, operation type, IP address and port information.
    - Dynamic blacklist: Supports dynamically updating the blacklist, reading IP addresses from external files (blacklist.txt), and regularly checking for updates.
    - Based on dynamic linking: Use `dlopen` and `dlsym` to dynamically link the `connect` and `accept` functions in the libc.so.6 system library in order to intercept network connections.

3. Code structure:

  • In the main function, the program initializes the blacklist, obtains the handle to the system library, and dynamically links the connect and accept functions.
  • The connect function is used to intercept network connections, check whether the IP address of the connection request is in the blacklist, record access events, and then decide whether to allow the connection based on the results. (Only ipv4 addresses are supported for now)
  • The accept function is similarly used to intercept accept requests and perform the same operations.

4. Blacklist management:

  • The IP address list of the blacklist is represented by the blacklist array in the program, and new IP addresses can be loaded from the external file blacklist.txt when the program is running.
  • The blacklist file is checked every hour and if there are new IP addresses, the program will load them and update the blacklist.

5. Access records:

- Successful connections and intercepted access events will be recorded to the `ip_access_log.txt` log file, including timestamp, process ID, operation type, IP address and port information.

6. Dynamic link:

- The program uses `dlopen` and `dlsym` to dynamically link the `connect` and `accept` functions in the system library so that it can intercept the calls of these functions and add custom filtering logic.

7. Exception handling:

  • If the blacklist file cannot be read, the program will output an error message.

8. Test address:

  • Some IP addresses for testing are provided, including local machine addresses, Baidu, Google’s public DNS servers, and Cloudflare’s public DNS servers.

3. Code deployment

1. Make sure there is a blacklist file in the current directory: blacklist.txt
2. Make sure there is a log file in the current directory: ip_access_log.txt
3. Query several IP addresses available for testing for command line testing: telnet x.x.x.x
   Provided here: Local address: 127.0.0.1
   Baidu: 220.181.38.149
   Google’s public DNS servers: 8.8.8.8
   Cloudflare's public DNS servers: 1.1.1.1
4. Use the following command to compile the ip.c file into a dynamic link library
   gcc ip.c -fpic -shared -ldl -o ip.so
5. Use the following command to inject the ip.so dynamic library into the current directory
   export LD_PRELOAD=./ip.so
6. Test
   telnet 127.0.0.1
   telnet 220.181.38.149
   telnet 8.8.8.8
   telnet 1.1.1.1

4. Code

#define _GNU_SOURCE // Enable GNU extended features
#include <stdio.h>
#include <sys/types.h>
#include <dlfcn.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>

/*
IP addresses available for testing:
    Local address: 127.0.0.1
    Baidu: 220.181.38.149
    Google’s public DNS servers: 8.8.8.8
    Cloudflare's public DNS servers: 1.1.1.1
*/

// Define a blacklist, including IP addresses that are prohibited from access
const char *blacklist[] = {
    "127.0.0.1"};

//The number of IP addresses in the blacklist
static int numBlacklist = sizeof(blacklist) / sizeof(blacklist[0]);

//Record the time of the last blacklist check
static time_t lastBlacklistCheckTime = 0;

//Define the path to the blacklist file
static const char *blacklistFile = "blacklist.txt";

//Record access records to the log file
void logAccess(const char *action, int sockfd, const char *ip, int port);

// Check if the blacklist has changed
void checkBlacklistChanges();

// Determine whether the accessed network is a filtered IP, and if so, return early (for the IP address where the client initiates the connection)
typedef int (*new_socket)(int, const struct sockaddr *, socklen_t);
typedef int (*new_accept)(int, struct sockaddr *, socklen_t *);

// (for the IP address where the client initiated the connection) intercept the connection request
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
    void *handle = NULL;
    new_socket old_connect = NULL; // Save as old_connect for call

    // Get the handle of libc.so.6
    handle = dlopen("libc.so.6", RTLD_LAZY);

    //Return the loading address of the open function in libc.so.6
    old_connect = (new_socket)dlsym(handle, "connect");

    char ip[128];
    memset(ip, 0, sizeof(ip)); // ip address
    int port = -1; // port number

    // If the incoming address is an IPv4 address
    if (AF_INET == addr->sa_family)
    {
        // Force the incoming address to an IPv4 address structure
        struct sockaddr_in *sa4 = (struct sockaddr_in *)addr;
        // Use the inet_ntop function to convert the IPv4 address to human-readable string form (IP address)
        inet_ntop(AF_INET, (void *)(struct sockaddr *) & sa4->sin_addr, ip, sizeof(ip));
        //Extract the port number and convert it from network byte order to host byte order
        port = ntohs(sa4->sin_port);
        //Print IP address and port number
        printf("\
AF_INET IP===%s:%d\
", ip, port);
    }
    checkBlacklistChanges();
    //Record access information to the log file
    logAccess("connect", sockfd, ip, port);
    for (int i = 0; i < numBlacklist; + + i)
    {
        if (0 == strcmp(ip, blacklist[i]))
        {
            printf("\
===%s netfilter...connect failed!\
", ip);
            //Record intercepted access information to the log file
            logAccess("connect (Blocked)", sockfd, ip, port);
            return -1;
        }
    }

    printf("\
PID:%d, socket:%d, %s Successfully connected!\
", getpid(), sockfd, ip);
    //Record successful access information to the log file
    logAccess("connect (Success)", sockfd, ip, port);
    return old_connect(sockfd, addr, addrlen);
}

// Monitor and control connection requests initiated through sockets (for the IP address of the server initiating the connection), intercept and accept requests
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)
{
    void *handle = NULL;
    new_accept old_accept = NULL; // Save old_accept for call

    // Get the handle of libc.so.6
    handle = dlopen("libc.so.6", RTLD_LAZY);

    // Return the loading address of the accept function in libc.so.6
    old_accept = (new_accept)dlsym(handle, "accept");

    char ip[128];
    memset(ip, 0, sizeof(ip)); // IP address
    int clientfd = -1; // client socketfd

    if (AF_INET == addr->sa_family)
    {
        // Force the incoming address to an IPv4 address structure
        struct sockaddr_in *sa4 = (struct sockaddr_in *)addr;
        // Use the inet_ntoa function to convert the IPv4 address to an IP address string and copy it to the ip variable
        strcpy(ip, inet_ntoa(sa4->sin_addr));
    }
    checkBlacklistChanges();
    //Record access information to the log file
    logAccess("accept", sockfd, ip, -1);

    for (int i = 0; i < numBlacklist; + + i)
    {
        if (0 == strcmp(ip, blacklist[i]))
        {
            printf("\
PID:%d ===%s=== netfilter...accept failed!\
", getpid(), ip);
            //Record intercepted access information to the log file
            logAccess("accept (Blocked)", sockfd, ip, -1);
            return -1;
        }
    }
    // Release and call the original accept function to accept the client's connection request
    clientfd = old_accept(sockfd, addr, addrlen);
    printf("\
PID:%d, clientfd:%d, %s Successfully accepted!\
", getpid(), clientfd, ip);
    //Record successful access information to the log file
    logAccess("accept (Success)", sockfd, ip, -1);
    return clientfd;
}
// Record the access results to the log file
void logAccess(const char *action, int sockfd, const char *ip, int port)
{
    time_t now;
    struct tm *tm_info;
    char timestamp[20];

    time( & amp;now); // Get the current time
    tm_info = localtime( & amp;now); // Convert the current time to the local time structure
    strftime(timestamp, 20, "%Y-%m-%d %H:%M:%S", tm_info); // Format time as a string (year-month-day hour: minute: second)

    char logEntry[256]; // Store log entries
    snprintf(logEntry, sizeof(logEntry), "[%s] [PID:%d] [%s] [IP:%s] [Port:%d]\
", timestamp, getpid(), action, ip, port);

    //Open the log file and append information to the end of the file
    FILE *logFile = fopen("ip_access_log.txt", "a");
    if (logFile != NULL)
    {
        fputs(logEntry, logFile);
        fclose(logFile);
    }
}

// Check if the blacklist has changed
void checkBlacklistChanges()
{
    time_t current_time;
    time(& amp;current_time);
    // The time interval for checking blacklist changes, here is set to 1 hour
    if (current_time - lastBlacklistCheckTime >= 3600)
    {
        FILE *file = fopen(blacklistFile, "r");
        if(file)
        {
            //Read the contents of the blacklist file
            char line[256];
            int numBlacklistCount = 0; // Track the number of IP addresses in the blacklist
            while (fgets(line, sizeof(line), file))
            {
                //Remove the newline character at the end of the line
                line[strcspn(line, "\
")] = '\0';
                // Add the new IP address to the blacklist
                blacklist[numBlacklistCount] = strdup(line);
                numBlacklistCount + + ;
            }
            numBlacklist = numBlacklistCount;
            fclose(file);
            //Update the number of IP addresses in the blacklist
            for (int i = numBlacklistCount; i < sizeof(blacklist) / sizeof(blacklist[0]); + + i)
            {
                blacklist[i] = NULL;
            }
            lastBlacklistCheckTime = current_time; // Update the last check time
        }
    }
    else
    {
        perror("Error reading blacklist file");
    }
}