关于系统调用 ioctl 和 hostID 操作的问题
Question on syscall ioctl and hostID manipulation
我正在寻找一种方法来使用我的 MAC
地址并对其进行修改,我偶然发现了以下有效的 ANSI-C 代码片段,但我不确定如何进行。在代码片段中,我为我想问的问题添加了要点。
/*
* 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]!='[=10=]') break;
ptr[i] = val;
p+=3;
}
}
return ret;
}
所以,我的第一个问题 [1]
:
语句unsigned char *ptr = argp + 18;
中的'addition'的目的是什么?我们如何得出这个数字?例如,在查看 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
是对应get/set硬件地址的flag,其余是argp
。但是,我们究竟如何迭代这个 struct
数据?
获取 .so
文件并执行以下操作后,可以测试代码的功能,例如:
export LD_PRELOAD="./hostid-spoof.so" # the compiled lib
export MAC_ADDRESS="11:22:33:44:55:66"
ifconfig
那么网口的mac地址确实改变了。但这到底是如何生效的呢?
MAC地址实际上并没有改变;相反,这个 returns 是试图检索地址的 ifconfig
等本地进程的假地址。目前尚不清楚为什么这会很有价值。进出电线的东西不会受到影响。
LD_PRELOAD
用于从标准 C 库中“挂钩”ioctl
函数。请参阅 What is the LD_PRELOAD trick?. The call is first is chained along to the real ioctl
function. If the command was SIOCGIFHWADDR
, then we mess with the returned data. The argp
parameter points to a struct ifreq
; see the netdevice(7) man page(对于 Linux,或您自己的等效内容 OS)。代码作者大概查看了OS上struct ifreq
的二进制布局,确定MAC地址位于struct的第18字节开始,然后戳出想要的fake地址转换为从该位置开始的 6 个字节,以有点笨拙的方式将十六进制转换为二进制。
要查看 18 的来源,这是我在我的系统上所做的:
- 如上面的手册页所述,
struct ifreq
在 <net/if.h>
中定义为(省略无关成员):
struct ifreq {
char ifr_name[IFNAMSIZ]; /* Interface name */
union {
// ...
struct sockaddr ifr_hwaddr;
// ...
};
};
<net/if.h>
将 IFNAMSIZ
定义为 IF_NAMESIZE
,后者定义为 16。所以那里有 16 个字节。接下来,struct sockaddr
在 <sys/socket.h>
中定义,或者更确切地说在 <bits/socket.h>
中定义,后者包括,如:
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
__SOCKADDR_COMMON
宏在<bits/sockaddr.h>
中定义为:
#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family
所以它扩展到 sa_family_t sa_family;
。在 <bits/sockaddr.h>
的上方我们有
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 或适合您的操作系统的网站上询问。
我正在寻找一种方法来使用我的 MAC
地址并对其进行修改,我偶然发现了以下有效的 ANSI-C 代码片段,但我不确定如何进行。在代码片段中,我为我想问的问题添加了要点。
/*
* 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]!='[=10=]') break;
ptr[i] = val;
p+=3;
}
}
return ret;
}
所以,我的第一个问题 [1]
:
语句unsigned char *ptr = argp + 18;
中的'addition'的目的是什么?我们如何得出这个数字?例如,在查看 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
是对应get/set硬件地址的flag,其余是argp
。但是,我们究竟如何迭代这个 struct
数据?
获取 .so
文件并执行以下操作后,可以测试代码的功能,例如:
export LD_PRELOAD="./hostid-spoof.so" # the compiled lib
export MAC_ADDRESS="11:22:33:44:55:66"
ifconfig
那么网口的mac地址确实改变了。但这到底是如何生效的呢?
MAC地址实际上并没有改变;相反,这个 returns 是试图检索地址的 ifconfig
等本地进程的假地址。目前尚不清楚为什么这会很有价值。进出电线的东西不会受到影响。
LD_PRELOAD
用于从标准 C 库中“挂钩”ioctl
函数。请参阅 What is the LD_PRELOAD trick?. The call is first is chained along to the real ioctl
function. If the command was SIOCGIFHWADDR
, then we mess with the returned data. The argp
parameter points to a struct ifreq
; see the netdevice(7) man page(对于 Linux,或您自己的等效内容 OS)。代码作者大概查看了OS上struct ifreq
的二进制布局,确定MAC地址位于struct的第18字节开始,然后戳出想要的fake地址转换为从该位置开始的 6 个字节,以有点笨拙的方式将十六进制转换为二进制。
要查看 18 的来源,这是我在我的系统上所做的:
- 如上面的手册页所述,
struct ifreq
在<net/if.h>
中定义为(省略无关成员):
struct ifreq {
char ifr_name[IFNAMSIZ]; /* Interface name */
union {
// ...
struct sockaddr ifr_hwaddr;
// ...
};
};
<net/if.h>
将 IFNAMSIZ
定义为 IF_NAMESIZE
,后者定义为 16。所以那里有 16 个字节。接下来,struct sockaddr
在 <sys/socket.h>
中定义,或者更确切地说在 <bits/socket.h>
中定义,后者包括,如:
struct sockaddr
{
__SOCKADDR_COMMON (sa_); /* Common data: address family and length. */
char sa_data[14]; /* Address data. */
};
__SOCKADDR_COMMON
宏在<bits/sockaddr.h>
中定义为:
#define __SOCKADDR_COMMON(sa_prefix) \
sa_family_t sa_prefix##family
所以它扩展到 sa_family_t sa_family;
。在 <bits/sockaddr.h>
的上方我们有
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 或适合您的操作系统的网站上询问。