tcpdump源码分析

进入tcpdump.c(函数入口)之前,先看一些头文件netdissect.h里定义了一个数据结构struct netdissect_options来描述tcdpump支持的所有参数动作,每一个参数有对应的flag, 在tcpdump 的main 里面, 会根据用户的传入的参数来增加相应flag数值, 最后根据这些flag数值来实现特定动作。各个参数含义请参考源代码注释。

1.netdissect.h 头文件

struct netdissect_options {int ndo_bflag;              /* 以 ASDOT 表示法打印 4 字节 AS 号 */int ndo_eflag;              /* 打印以太网头 */int ndo_fflag;              /* 不翻译“外部”IP 地址 */int ndo_Kflag;              /* 不检查 IP、TCP 或 UDP 校验和 */int ndo_nflag;              /* 将地址显示为数字形式 */int ndo_Nflag;              /* 打印主机名时去掉域名 */int ndo_qflag;              /* 简短输出 */int ndo_Sflag;              /* 打印原始 TCP 序列号 */int ndo_tflag;              /* 打印数据包到达时间 */int ndo_uflag;              /* 打印未解码的 NFS 句柄 */int ndo_vflag;              /* 冗余级别 */int ndo_xflag;              /* 以十六进制打印数据包 */int ndo_Xflag;              /* 以十六进制/ASCII 打印数据包 */int ndo_Aflag;              /* 仅以 ASCII 打印数据包,并将 TAB、LF、CR 和 SPACE 视为图形字符 */int ndo_Hflag;              /* 解析 802.11s 草案网状标准 */const char *ndo_protocol;   /* 协议 */jmp_buf ndo_early_end;      /* 用于 setjmp()/longjmp() 的 jmp_buf */void *ndo_last_mem_p;       /* 指向最后分配的内存块的指针 */int ndo_packet_number;      /* 在行首打印数据包编号 */int ndo_print_sampling;     /* 打印每 N 个数据包 */int ndo_suppress_default_print; /* 对未知的数据包类型不使用 default_print() */int ndo_tstamp_precision;   /* 请求的时间戳精度 */const char *program_name;   /* 使用该库的程序名称 */char *ndo_espsecret;               /* ESP 密钥 */struct sa_list *ndo_sa_list_head;  /* 由 print-esp.c 使用 */struct sa_list *ndo_sa_default;char *ndo_sigsecret;        /* 签名验证密钥 */int ndo_packettype;         /* 由 -T 指定的数据包类型 */int ndo_snaplen;            /* 抓取长度 */int ndo_ll_hdr_len;         /* 链路层头长度 *//* 全局指针,指向当前数据包的开始和结束(在打印过程中) */const u_char *ndo_packetp;const u_char *ndo_snapend;/* 保存的数据包边界和缓冲区信息的堆栈 */struct netdissect_saved_packet_info *ndo_packet_info_stack;/* 指向 if_printer 函数的指针 */if_printer ndo_if_printer;/* 指向输出内容的 void 函数的指针 */void (*ndo_default_print)(netdissect_options *,const u_char *bp, u_int length);/* 指向执行常规输出的函数的指针 */int  (*ndo_printf)(netdissect_options *,const char *fmt, ...)PRINTFLIKE_FUNCPTR(2, 3);/* 指向输出错误的函数的指针 */void NORETURN_FUNCPTR (*ndo_error)(netdissect_options *,status_exit_codes_t status,const char *fmt, ...)PRINTFLIKE_FUNCPTR(3, 4);/* 指向输出警告的函数的指针 */void (*ndo_warning)(netdissect_options *,const char *fmt, ...)PRINTFLIKE_FUNCPTR(2, 3);
};

字段解释:

  • 标志位

    • ndo_bflag: 决定是否以 ASDOT 表示法打印 4 字节 AS 号。
    • ndo_eflag: 决定是否打印以太网头。
    • ndo_fflag: 决定是否不翻译“外部”IP 地址。
    • ndo_Kflag: 决定是否不检查 IP、TCP 或 UDP 校验和。
    • ndo_nflag: 决定是否将地址保留为数字形式。
    • ndo_Nflag: 决定是否在打印主机名时去掉域名。
    • ndo_qflag: 决定是否进行简短输出。
    • ndo_Sflag: 决定是否打印原始 TCP 序列号。
    • ndo_tflag: 决定是否打印数据包到达时间。
    • ndo_uflag: 决定是否打印未解码的 NFS 句柄。
    • ndo_vflag: 设置冗余级别。
    • ndo_xflag: 决定是否以十六进制打印数据包。
    • ndo_Xflag: 决定是否以十六进制和 ASCII 打印数据包。
    • ndo_Aflag: 决定是否仅以 ASCII 打印数据包,并将 TAB、LF、CR 和 SPACE 视为图形字符。
    • ndo_Hflag: 决定是否解析 802.11s 草案网状标准。
  • 其他选项

    • ndo_protocol: 指定的协议。
    • ndo_early_end: 用于 setjmp()/longjmp() 的跳转缓冲区。
    • ndo_last_mem_p: 指向最后分配的内存块的指针。
    • ndo_packet_number: 决定是否在行首打印数据包编号。
    • ndo_print_sampling: 打印每 N 个数据包。
    • ndo_suppress_default_print: 决定是否对未知的数据包类型不使用 default_print()。
    • ndo_tstamp_precision: 请求的时间戳精度。
    • program_name: 使用该库的程序名称。
  • ESP 和签名相关

    • ndo_espsecret: ESP 密钥。
    • ndo_sa_list_head, ndo_sa_default: 由 print-esp.c 使用的 SA 列表。
    • ndo_sigsecret: 签名验证密钥。
  • 数据包和链接层相关

    • ndo_packettype: 由 -T 指定的数据包类型。
    • ndo_snaplen: 抓取长度。
    • ndo_ll_hdr_len: 链路层头长度。
    • ndo_packetp, ndo_snapend: 当前数据包的开始和结束指针。
    • ndo_packet_info_stack: 保存的数据包边界和缓冲区信息的堆栈。
  • 函数指针

    • ndo_if_printer: 指向 if_printer 函数的指针。
    • ndo_default_print: 指向输出内容的 void 函数的指针。
    • ndo_printf: 指向执行常规输出的函数的指针。
    • ndo_error: 指向输出错误的函数的指针。
    • ndo_warning: 指向输出警告的函数的指针。

此次需要关注的是 “ndo_snaplen: 抓取长度”变量。

2.从指针安全读取x字节的相关宏定义

在 print-eigrp.c 中有如下代码:

GET_BE_U_2(eigrp_com_header->checksum),

 继续追踪到 extract.h 文件:

#define GET_BE_U_2(p) get_be_u_2(ndo, (const u_char *)(p))

  继续追踪,仍在 extract.h 文件中:get_be_u_2 可见是一个内联函数

static inline uint16_t
get_be_u_2(netdissect_options *ndo, const u_char *p)
{if (!ND_TTEST_2(p))nd_trunc_longjmp(ndo);return EXTRACT_BE_U_2(p);
}

这个内联函数 get_be_u_2 做了以下几件事:

  1. 调用 ND_TTEST_2(p) 来检查是否可以从指针 p 安全地读取 2 个字节。
  2. 如果检查失败,调用 nd_trunc_longjmp(ndo) 进行错误处理,通常会跳转到某个提前定义的错误处理位置。
  3. 如果检查成功,调用 EXTRACT_BE_U_2(p) 从指针 p 提取 2 个字节的数据,并将其从网络字节顺序转换为主机字节顺序。

继续追踪 ND_TTEST_2 宏函数,仍在 extract.h 文件中:

#define ND_TTEST_2(p) ND_TTEST_LEN((p), 2)
//这个宏调用 ND_TTEST_LEN,传递参数 p 和长度 2,表示要检查从指针 p 开始的 2 个字节。 

继续追踪 ND_TTEST_LEN 宏函数,跳转到 netdissect.h 文件中:


/** True if "l" bytes from "p" were captured.** The "ndo->ndo_snapend - (l) <= ndo->ndo_snapend" checks to make sure* "l" isn't so large that "ndo->ndo_snapend - (l)" underflows.** The check is for <= rather than < because "l" might be 0.** We cast the pointers to uintptr_t to make sure that the compiler* doesn't optimize away any of these tests (which it is allowed to* do, as adding an integer to, or subtracting an integer from, a* pointer assumes that the pointer is a pointer to an element of an* array and that the result of the addition or subtraction yields a* pointer to another member of the array, so that, for example, if* you subtract a positive integer from a pointer, the result is* guaranteed to be less than the original pointer value). See**	https://www.kb.cert.org/vuls/id/162289*//** Test in two parts to avoid these warnings:* comparison of unsigned expression >= 0 is always true [-Wtype-limits],* comparison is always true due to limited range of data type [-Wtype-limits].*//** 如果从 "p" 开始的 "l" 字节被捕获,则返回真。** 通过 "ndo->ndo_snapend - (l) <= ndo->ndo_snapend" 的检查来确保* "l" 不会大到使 "ndo->ndo_snapend - (l)" 发生下溢。** 检查使用 <= 而不是 < 是因为 "l" 可能为 0。** 我们将指针转换为 uintptr_t 是为了确保编译器不会优化掉这些测试* (编译器被允许这样做,因为将整数加到指针上,或从指针上减去整数,* 假定指针是一个数组元素的指针,并且加法或减法的结果会生成另一个数组成员的指针,* 因此,例如,如果从指针上减去一个正整数,结果保证小于原始指针值)。参见**	https://www.kb.cert.org/vuls/id/162289*//** 分两部分测试以避免这些警告:* unsigned 表达式的比较总是大于等于 0 是始终为真的 [-Wtype-limits],* 由于数据类型的有限范围,比较总是为真 [-Wtype-limits]。*/#define IS_NOT_NEGATIVE(x) (((x) > 0) || ((x) == 0))#define ND_TTEST_LEN(p, l) \(IS_NOT_NEGATIVE(l) && \((uintptr_t)ndo->ndo_snapend - (l) <= (uintptr_t)ndo->ndo_snapend && \(uintptr_t)(p) <= (uintptr_t)ndo->ndo_snapend - (l)))
  1. 这个宏检查以下几个条件:

    • IS_NOT_NEGATIVE(l):长度 l 必须是非负数。
    • ((uintptr_t)ndo->ndo_snapend - (l) <= (uintptr_t)ndo->ndo_snapend):确保 ndo->ndo_snapend 减去 l 不会发生下溢。
    • ((uintptr_t)(p) <= (uintptr_t)ndo->ndo_snapend - (l)):确保指针 p 不会越过数据包的边界。

ND_TTEST_2 是一个宏,用于检查是否可以安全地从指定的指针 p 读取 2 个字节(即 16 位)。它通过验证内存边界和指针合法性来确保不会发生越界访问。具体来说,它结合了多个宏和函数,形成一个完整的边界检查机制。

总结

ND_TTEST_2 的主要目的是确保在从数据包读取数据时不会发生越界访问,从而保证程序的安全性和稳定性。它通过一系列的检查来验证指针和长度的合法性,防止因非法内存访问导致的崩溃或数据损坏。

3.setjmp/longjmp 非局部跳转函数的应用

现在存在的问题是没有搞清tcpdump 的执行流程:

调用顺序:

(gdb) bt
#0  eigrp_print (ndo=0x7fffffffcf00, pptr=0x8ea512 "\002\005\356h", len=40) at ./print-eigrp.c:233
#1  0x0000000000458255 in ip_demux_print (ndo=0x7fffffffcf00, bp=0x8ea512 "\002\005\356h", length=40, ver=4, fragmented=0, ttl_hl=2, nh=88 'X', iph=0x8ea4fe "E\300") at ./print-ip-demux.c:143
#2  0x00000000004227cf in ip_print (ndo=0x7fffffffcf00, bp=0x8ea4fe "E\300", length=60) at ./print-ip.c:487
#3  0x000000000041dbbc in ethertype_print (ndo=0x7fffffffcf00, ether_type=2048, p=0x8ea4fe "E\300", length=60, caplen=60, src=0x7fffffffccb0, dst=0x7fffffffcca0) at ./print-ether.c:536
#4  0x000000000041d680 in ether_common_print (ndo=0x7fffffffcf00, p=0x8ea4fe "E\300", length=60, caplen=60, print_switch_tag=0x0, switch_tag_len=0, print_encap_header=0x0, encap_header_arg=0x0) at ./print-ether.c:391
#5  0x000000000041d7c1 in ether_print (ndo=0x7fffffffcf00, p=0x8ea4f0 "\001", length=74, caplen=74, print_encap_header=0x0, encap_header_arg=0x0)at ./print-ether.c:448
#6  0x000000000041d81a in ether_if_print (ndo=0x7fffffffcf00, h=0x7fffffffce40, p=0x8ea4f0 "\001") at ./print-ether.c:464
#7  0x0000000000407ead in pretty_print_packet (ndo=0x7fffffffcf00, h=0x7fffffffce40, sp=0x8ea4f0 "\001", packets_captured=1) at ./print.c:414
#8  0x0000000000407343 in print_packet (user=0x7fffffffcf00 "", h=0x7fffffffce40, sp=0x8ea4f0 "\001") at ./tcpdump.c:3127
#9  0x00000000004f4f07 in pcap_offline_read (p=p@entry=0x8ea250, cnt=2147483647, cnt@entry=-1, callback=callback@entry=0x4072ed <print_packet>, user=user@entry=0x7fffffffcf00 "") at ./savefile.c:691
#10 0x00000000004e2baf in pcap_loop (p=0x8ea250, cnt=-1, callback=0x4072ed <print_packet>, user=0x7fffffffcf00 "") at ./pcap.c:2916
#11 0x00000000004066c2 in main (argc=3, argv=0x7fffffffe3c8) at ./tcpdump.c:2569

setjmp() 函数是在 #7 的 pretty_print_packet() 函数中调用的。

参考:

1、非局部跳转:8.6 非本地跳转 | 深入理解计算机系统(CSAPP) (gitbook.io)

2、 【C指针(五)】6种转移表实现整合longjmp()/setjmp()函数和qsort函数详解分析&&模拟实现-支付宝开发者社区 (alipay.com)

3、tcpdump 源码解析:https://www.cnblogs.com/pangblog/p/3364690.html

 分析2:tcpdump源码分析-CSDN博客

4、tcpdump 越界访问追踪: tcpdump 4.5.1 crash 深入分析-安全客 - 安全资讯平台

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/332253.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#基础语言

​​​​ 目录 一个c# 程序主要包括以下部分&#xff1a;​​​​​​​ 标识符 C# 关键字 C# 数据类型 值类型&#xff08;Value types&#xff09; 引用类型&#xff08;Reference types&#xff09; 对象&#xff08;Object&#xff09;类型 动态&#xff08;Dynam…

总是等不是办法,向媒体投稿你得学会用新方法

初入信息宣传领域,我怀揣着对文字的热爱与传播价值的热情,肩负起了单位活动的宣传报道重任。那时的我,满脑子都是传统的投稿思维:精心撰写每一篇稿件,然后逐一搜寻各大媒体的投稿邮箱,一封封邮件满怀期待地发出,像播撒希望的种子,渴望在广袤的媒体土壤中生根发芽。然而,理想很丰…

YOLOv10尝鲜测试五分钟极简配置

最近清华大学团队又推出YOLOv10&#xff0c;真是好家伙了。 安装&#xff1a; pip install supervision githttps://github.com/THU-MIG/yolov10.git下载权重&#xff1a;https://github.com/THU-MIG/yolov10/releases/download/v1.0/yolov10n.pt 预测&#xff1a; from ult…

C++笔记:三种适配器(分别修饰函数、迭代器、容器)

Algorithms看不见Containers&#xff0c;对其一无所知。所以&#xff0c;它所需要的一切信息都必须从iterators取得&#xff0c;而iterators&#xff08;由Containers提供&#xff09;必须能够回答Algorithm的所有提问&#xff0c;才能搭配该Algorithm的所有操作。 1. C 标准库…

国内首个智能体生态大会!2024百度万象大会定档5月30日

最近&#xff0c;百度悄悄「上新」了几个AI神器。 百度搜索上线「互动」功能&#xff0c;可以实时问答&#xff0c;查询信息就像聊天一样简单&#xff0c;还可以艾特相关智能体&#xff0c;更细致精确地满足个性化需求&#xff0c;比如去新加坡旅游&#xff0c;可以让新加坡旅…

智慧校园学工管理系统的部署

学工体系思政服务该怎么规划建造&#xff1f;思政作为高校育人的中心使命&#xff0c;在做到让学生健康高兴生长的一起&#xff0c;也应满意学生生长成才的各类需求。使用技术为学生供给优质的信息化服务&#xff0c;是其间的有效途径。大数据让个性化教育成为可能&#xff0c;…

OTA在线旅行社系统架构:连接世界的科技纽带

随着互联网的快速发展和人们对旅行需求的不断增长&#xff0c;OTA&#xff08;Online Travel Agency&#xff09;在线旅行社成为了现代旅行业中的重要一环。OTA系统架构的设计和实现将对旅行行业产生深远影响。本文将探讨OTA在线旅行社系统架构的重要性和关键组成部分&#xff…

人工智能为犯罪地下世界带来了巨大的生产力提升

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

centos7.9用docker运行一个nginx容器

首先你的linux 系统里面已经安装好了docker&#xff0c;docker的安装教程看这个 1&#xff0c;下载nginx镜像 有很多文章会把镜像下载说成是拉取镜像&#xff0c; 我觉得就是下载的意思啊&#xff0c;搞不懂为什么要说拉取&#xff1f; docker pull nginx 下载最新版 Nginx …

开源的在线JSON数据可视化编辑器jsoncrack本地部署与远程访问

文章目录 1. 在Linux上使用Docker安装JSONCrack2. 安装Cpolar内网穿透工具3. 配置JSON Crack界面公网地址4. 远程访问 JSONCrack 界面5. 固定 JSONCrack公网地址 JSON Crack 是一款免费的开源数据可视化应用程序&#xff0c;能够将 JSON、YAML、XML、CSV 等数据格式可视化为交互…

【移动云】云端赋能——数字化时代游戏与工作的新境界

前言 在当今这个信息化、数字化的时代&#xff0c;云计算、大数据和人工智能等前沿技术已经深入到我们生活的方方面面。作为我国通信行业的领军企业&#xff0c;中国移动凭借其在5G技术领域的领先优势&#xff0c;推出了基于移动云计算技术的云业务品牌——移动云。移动云以云操…

Linux 的性能调优的思路

Linux操作系统是一个开源产品&#xff0c;也是一个开源软件的实践和应用平台&#xff0c;在这个平台下有无数的开源软件支撑&#xff0c;我们常见的apache、tomcat、mysql等。 开源软件的最大理念是自由、开放&#xff0c;那么Linux作为一个开源平台&#xff0c;最终要实现的是…

Overleaf中出现文字越界、越下届、没有正确分页、换页的原因和解决方法

在使用overleaf中&#xff0c;我偶尔会遇到如标题所说的情况&#xff0c;也如图所示&#xff1a; 后来发现&#xff0c;是因为这一页前面是一个表格&#xff0c;所以怀疑是表格的格式导致的。所以让chatgpt帮我更换了表格的格式&#xff0c;成功解决问题。 对于问题可能的成因…

<Python实际应用>用yolov9实现垃圾检测

公司一个项目需要在无人机巡检的画面中识别垃圾和汽车&#xff0c;正好听闻yolov9最新出炉&#xff0c;于是试了一下采用yolov9来搭建该项目 1.下载和部署 下载yolov9:GitHub地址&#xff1a;GitHub代码下载地址 配置环境可以参考之前关于yolov5的文章 Yolov5自学笔记之一-…

【二叉树】LeetCode.144:二叉树的前序遍历(小细节把握)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;初阶初阶结构刷题 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 1.题目描述&#xff1a;​编辑 2.问题分析&#xff1a; &#x1f354;函数解读&#xff1a; …

Linux系统启动原理

Linux系统启动原理及故障排除 Centos6系统启动过程 修改系统启动级别 vim /etc/inittabCentos7启动流程 加载BIOS信息&#xff0c;进行硬件检测 根据BIOS设定读取设备中的MBR&#xff0c;加载Boot loader 加载内核&#xff0c;内核初始化以后以模块的形式动态加载硬件 并且加…

【Spring】深入理解 Spring 状态机:简化复杂业务逻辑的利器

前言 在软件开发中&#xff0c;有许多场景需要处理状态转换和状态驱动的逻辑&#xff0c;比如订单处理、工作流程管理、游戏引擎等。Spring 状态机&#xff08;Spring State Machine&#xff09;是 Spring Framework 提供的一个强大的模块&#xff0c;用于帮助开发人员轻松构建…

IOT技术怎么落地?以宝马,施耐德为例

物联网技术 物联网&#xff08;IoT&#xff09;技术正逐渐成为数字化工厂转型的核心驱动力。本文将通过实际案例&#xff0c;探讨IoT技术如何促进制造业的数字化转型&#xff0c;提高生产效率&#xff0c;降低成本&#xff0c;并提升产品质量。 1. 物联网技术简介 物联网技术通…

【Python】 XGBoost模型的使用案例及原理解析

原谅把你带走的雨天 在渐渐模糊的窗前 每个人最后都要说再见 原谅被你带走的永远 微笑着容易过一天 也许是我已经 老了一点 那些日子你会不会舍不得 思念就像关不紧的门 空气里有幸福的灰尘 否则为何闭上眼睛的时候 又全都想起了 谁都别说 让我一个人躲一躲 你的承诺 我竟然没怀…

部署LAMP平台

目录 一、LAMP简介与概述 1.1 各组件作用 1.2 LAMP平台搭建时各组件安装顺序 1.3 httpd服务的目录结构 1.4 httpd.conf配置文件 二、编译安装Apache httpd服务 2.1 关闭防火墙&#xff0c;将安装Apache所需软件包传到/opt目录下 2.2 安装环境依赖包 ​2.3 配置软件模块…