关于LD_PRELOAD劫持ioctl修改MAC地址代码的技术疑问
问题1:18字节偏移的作用与推导
你通过strace观察到的SIOCGIFHWADDR请求对应的argp参数是内核定义的struct ifreq结构体,18这个偏移完全是根据该结构体的固定内存布局计算得出:
struct ifreq的第一个字段ifr_name是网卡名称缓冲区,Linux内核固定其长度IFNAMSIZ = 16字节- 后续紧跟的
ifr_hwaddr字段是struct sockaddr类型,该结构体的第一个字段sa_family(地址族标记)占2字节 - 16 + 2 = 18,偏移18字节后的位置就是
sockaddr.sa_data的起始地址,网卡MAC地址就存储在该区域的前6字节,所以代码直接写这个位置就能覆盖返回的MAC值。
这类网络ioctl对应的参数结构体不存在通用的遍历规则,所有字段的内存偏移都是内核和用户态预先约定死的,正常开发时不需要手动算偏移,直接引入<net/if.h>头文件,通过((struct ifreq*)argp)->ifr_hwaddr.sa_data的方式访问即可,硬编码偏移的写法兼容性较差,不同架构如果存在结构体对齐规则差异的话可能失效。
问题2:动态库劫持生效原理
这是Linux动态链接器的LD_PRELOAD机制的特性:
- 动态链接器加载程序依赖的动态库时,
LD_PRELOAD指定的动态库优先级最高,会早于系统默认的libc库加载 - 你的代码里实现了和系统libc中同名的
ioctl函数,链接器解析符号时会优先使用你实现的版本,所有调用ioctl的操作都会先走到你的代码逻辑里 - 代码里先通过
dlopen、dlsym拿到系统原生ioctl的函数指针,调用原生接口拿到真实的MAC地址结果后,判断如果是SIOCGIFHWADDR(获取硬件地址)请求,就直接修改返回结构体里的MAC值,再返回给调用方
ifconfig获取网卡MAC地址的逻辑就是调用ioctl(fd, SIOCGIFHWADDR, &ifr),它拿到的是你修改后的结构体数据,所以输出的就是你指定的假MAC。注意这个欺骗仅对加载了该动态库的进程生效,不会真的修改网卡硬件配置,也不会影响其他未加载该库的进程。
内容的提问来源于stack exchange,提问作者ex1led
相关产品推荐
相关产品推荐

