【问题标题】:Question on syscall ioctl and hostID manipulation关于 syscall ioctl 和 hostID 操作的问题
【发布时间】:2021-11-23 07:43:03
【问题描述】:

我一直在寻找一种方法来处理我的MAC 地址并对其进行修改,我偶然发现了以下可用的 ANSI-C 代码 sn-p,但我不确定如何操作。在sn-p里面,我为我想问的问题加了分。

/*
 * Spoof a MAC address with LD_PRELOAD
 *
 * If environment variable $MAC_ADDRESS is set in the form "01:02:03:04:05:06"
 * then use that value, otherwise use the hardcoded 'mac_address' in this file.
 *
 * Bugs: This currently spoofs MAC addresses for *all* interfaces.
 * It would be better to watch calls to socket() for the interfaces
 * you want and then only spoof ioctl calls to that file descriptor.
 */
#include <dlfcn.h>
#include <stdlib.h>
#include <stdio.h>

#define SIOCGIFHWADDR 0x8927

static unsigned char mac_address[6] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06};

int
ioctl(int d, int request, unsigned char *argp)
{
    static void *handle = NULL;
    static int (*orig_ioctl)(int, int, char*);
    int ret;
    char *p;

    // If this var isn't set, use the hardcoded address above
    p=getenv("MAC_ADDRESS");

    if (handle == NULL) {
        char *error;
        handle = dlopen("/lib/libc.so.6", RTLD_LAZY);
        if (!handle) {
            fputs(dlerror(), stderr);
            exit(EXIT_FAILURE);
        }
        orig_ioctl = dlsym(handle, "ioctl");
        if ((error = dlerror()) != NULL) {
            fprintf(stderr, "%s\n", error);
            exit(EXIT_FAILURE);
        }
    }
    
    ret = orig_ioctl(d, request, argp);
    
    if (request == SIOCGIFHWADDR) {
        unsigned char *ptr = argp + 18; // [1] what is the +18 purpose?

        int i;
        for (i=0; i < 6; i++) {
            if (!p) {
                ptr[i] = mac_address[i];
                continue;
            }
            int val = 0;
            if (p[0]>='0' && p[0]<='9') val |= (p[0]-'0')<<4;
            else if (p[0]>='a' && p[0]<='f') val |= (p[0]-'a'+10)<<4;
            else if (p[0]>='A' && p[0]<='F') val |= (p[0]-'A'+10)<<4;
            else break;
            if (p[1]>='0' && p[1]<='9') val |= p[1]-'0';
            else if (p[1]>='a' && p[1]<='f') val |= p[1]-'a'+10;
            else if (p[1]>='A' && p[1]<='F') val |= p[1]-'A'+10;
            else break;
            if (p[2]!=':' && p[2]!='\0') break;
            ptr[i] = val;
            p+=3;
        }
    }
    return ret; 
}

那么,我的第一个问题[1]: 在声明unsigned char *ptr = argp + 18; 中,“添加”的目的是什么?我们如何得出这个数字?例如,在查看strace ifconfig 之后,我可以看到使用以下参数调用 ioctl:

ioctl(5, SIOCGIFHWADDR, {ifr_name="eth0", ifr_hwaddr={sa_family=ARPHRD_ETHER, sa_data=00:15:5d:2f:7f:86}}) = 0

其中5 是文件描述符,SIOCGIFHWADDR 是对应于获取/设置硬件地址的标志,其余的是argp。但是,我们如何准确地迭代这个struct 的数据?

在获取.so 文件并执行以下操作后,可以测试代码的功能,例如:

export LD_PRELOAD="./hostid-spoof.so" # the compiled lib
export MAC_ADDRESS="11:22:33:44:55:66"
ifconfig

那么网络接口的mac地址确实改变了。但这具体是如何生效的呢?

【问题讨论】:

  • 什么是hostID?
  • @ZsigmondLőrinczy 其硬件地址 (MAC)。

标签: c ioctl


【解决方案1】:

MAC 地址实际上并没有改变;相反,这会将虚假地址返回给尝试检索地址的本地进程,例如 ifconfig。目前尚不清楚为什么这会很有价值。电线上进出的东西不会受到影响。

LD_PRELOAD 用于从标准 C 库中“挂钩”ioctl 函数。见What is the LD_PRELOAD trick?。调用首先链接到真正的ioctl 函数。如果命令是SIOCGIFHWADDR,那么我们就会弄乱返回的数据。 argp 参数指向一个struct ifreq;请参阅netdevice(7) man page(适用于 Linux,或您自己的操作系统上的等价物)。代码的作者大概查看了此操作系统上struct ifreq 的二进制布局,以确定 MAC 地址位于结构的第 18 字节开始,然后将所需的假地址插入从该位置开始的 6 个字节中,以一种有点笨拙的方式将十六进制转换为二进制。


要查看 18 的来源,这是我在系统上所做的:

  • 如上面手册页所述,struct ifreq&lt;net/if.h&gt; 中定义为(省略不相关成员):
struct ifreq {
    char ifr_name[IFNAMSIZ]; /* Interface name */
    union {
        // ...
        struct sockaddr ifr_hwaddr;
        // ...
    };
};

&lt;net/if.h&gt;IFNAMSIZ 定义为IF_NAMESIZE,定义为16。所以那里有16 个字节。接下来,struct sockaddr&lt;sys/socket.h&gt; 中定义,或者更确切地说在&lt;bits/socket.h&gt; 中定义,后者包括:

struct sockaddr
  {
    __SOCKADDR_COMMON (sa_);    /* Common data: address family and length.  */
    char sa_data[14];           /* Address data.  */
  };

__SOCKADDR_COMMON 宏在&lt;bits/sockaddr.h&gt; 中定义为:

#define __SOCKADDR_COMMON(sa_prefix) \
  sa_family_t sa_prefix##family

所以它扩展到sa_family_t sa_family;。就在上面&lt;bits/sockaddr.h&gt; 我们有

typedef unsigned short int sa_family_t;

现在unsigned short int 在我的系统上是 16 位类型,所以是 2 个字节。 sa_data 成员将在此之后立即启动,因为 char 数组不需要任何填充。因此 16 + 2 = 18。

另一种方法是编写一个简单的程序,打印出offsetof(struct ifreq, ifr_hwaddr) + offsetof(struct sockaddr, sa_data) 的值。这也会在我的系统上打印 18。


如果您想实际更改接口的硬件 MAC 地址,则更多的是系统管理问题而不是编程问题;在http://superuser.com 或适合您操作系统的网站上询问。

【讨论】:

  • 我很清楚,实际的硬件地址并没有改变,而是被“欺骗”了,这是真的。话虽如此,我假设 ioctl 调用然后将 argp 参数视为引用并修改它,因此数据。但是,我对这个+18 很感兴趣。例如,如何在ioctl 调用之后迭代argp 的内容并确定ifr_hwaddr 的确切偏移量是多少?
  • @ex1led:查看编辑。
  • 谢谢!但是,在查看 &lt;net/if.h&gt; 头文件中的结构 ifreq 后,我想知道:结构中包含的其余字段不应该发挥作用并出现在 argp 中吗?即使他们没有被初始化,他们不应该说占据他们各自在 struct 中的空间吗? (因此,影响偏移)?
  • @ex1led:偏移量计算正确。 struct ifreq 实际上只有两个成员:ifr_name 在偏移量 0 处,以及一个匿名联合体在偏移量 16 处。联合体的所有成员都分配在同一个内存中,因此它们都具有相同的偏移量,即 16。当然,我们一次只能使用其中一个,文档解释了每次调用使用哪一个。 (我想,您熟悉工会的一般运作方式吗?)
  • @ex1led:让我说这段代码在几个方面是不必要的复杂。首先,我看不出他们必须手动使用偏移量的任何理由。他们本可以自己包含&lt;net/if.h&gt;,将argp 参数声明为struct ifreq *argp,然后直接写入argp-&gt;ifr_hwaddr.sa_data[i]。当strtoulsscanf 工作得很好时,手写的十六进制到二进制的转换是愚蠢的。
猜你喜欢
  • 1970-01-01
  • 2011-06-09
  • 1970-01-01
  • 1970-01-01
  • 2013-02-27
  • 2011-11-19
  • 1970-01-01
  • 2017-01-08
  • 2021-07-17
相关资源
最近更新 更多