tcpdump源码分析

news2024/11/16 19:52:55
进入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.coloradmin.cn/o/1693143.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SK6812-RGBW是一个集控制电路与发光电路于一体的智能外控LED光源

产品概述: SK6812-RGBW是一个集控制电路与发光电路于一体的智能外控LED光源。其外型与一个5050LED灯珠相同&#xff0c;每个元件即为一个像素点。像素点内部包含了智能数字接口数据锁存信号整形放大驱动电路&#xff0c;电源稳压电路&#xff0c;内置恒流电路&#xff0…

最新ChatGpt Desktop for Mac 安装使用教程

1. 下载地址 请点击链接下载 ChatGPT Desktop for MacOS 2. 使用要求 MacOS 版本 14需要时M1芯片的&#xff0c;如果你是因特尔的暂时还还不行 就算下载了也会出现下面的异常 3. 获取权限资格 目前 ChatGPT MacOS Desktop还不是全量开放的, 如果你没有收到通知说明你还没…

Redis篇 redis基本命令和定时器原理

基本命令和定时器原理 一. exists命令二. del命令三. Expire命令四. ttl命令五. redis的过期策略六. 定时器的两种设计方式七. type命令 一. exists命令 用来判断key的值是否存在 返回值是key的个数 这样写的话&#xff0c;有没有什么区别呢&#xff1f; 效率变低&#xff0c;消…

【开源】多语言大型语言模型的革新:百亿参数模型超越千亿参数性能

大型人工智能模型&#xff0c;尤其是那些拥有千亿参数的模型&#xff0c;因其出色的商业应用表现而受到市场的青睐。但是&#xff0c;直接通过API使用这些模型可能会带来数据泄露的风险&#xff0c;尤其是当模型提供商如OpenAI等可能涉及数据隐私问题时。私有部署虽然是一个解决…

数据库主流技术

文章目录 1.分布式数据库1.1 基础知识1.2 体系结构 2.Web数据库3.XML与数据库4.面向对象数据库5.大数据和数据仓库 1.分布式数据库 1.1 基础知识 分布式数据库系统是数据库系统和计算机网络相结合的产物。 由于计算机功能增强&#xff0c;成本下降&#xff0c;几乎每个办公室…

通过扩展指令增强基于覆盖引导的模糊测试

本文由Bruno Oliveira于2024年4月25日发表于IncludeSec的官方网站上。作为IncludeSec的安全研究人员&#xff0c;在他们日常的安全审计和渗透测试工作中&#xff0c;有时需要为客户开发一些模糊测试工具。在安全评估方法中使用模糊测试技术&#xff0c;可以有效地在复杂的现代化…

小程序-购物车-基于SKU电商规格组件实现

SKU 概念&#xff1a; 存货单位&#xff08; Stock Keeping Unit &#xff09;&#xff0c; 库存 管理的最小可用单元&#xff0c;通常称为“单品”。 SKU 常见于电商领域&#xff0c;对于前端工程师而言&#xff0c;更多关注 SKU 算法 &#xff0c;基于后端的 SKU 数据…

AI大模型探索之路-实战篇6: Function Calling技术调研之详细流程剖析

系列篇章&#x1f4a5; AI大模型探索之路-实战篇4&#xff1a;DB-GPT数据应用开发框架调研实践 AI大模型探索之路-实战篇5&#xff1a; Open Interpreter开放代码解释器调研实践 目录 系列篇章&#x1f4a5;一、前言二、Function Calling详细流程剖析1、创建OpenAI客户端2、定…

infoq读书笔记-Davide Taibi博士-花8年转型微服务却得不到回报,问题出在哪 儿?

InfoQ&#xff1a;您的论文“On the Definition of Microservice Bad Smells”涉及非常多的微服务不良做法&#xff0c;但如果要用几个大类别来列举危害性比较大的微服务反模式&#xff0c;您认为会是哪几类&#xff1f;另外&#xff0c;您能再大概分析说明下造成这个几个反模式…

《计算机网络微课堂》1-2:因特网概述

1-2&#xff1a;因特网概述 网络、互连网&#xff08;互联网&#xff09;和因特网因特网发展的三个阶段因特网的标准化工作因特网的组成 ‍ 网络、互连网&#xff08;互联网&#xff09;和因特网 我们首先介绍网络、互联网&#xff08;互连网&#xff09;因特网的基本概念&a…

c语言——宏offsetof

1.介绍 &#xff01;&#xff01;&#xff01; offsetof 是一个宏 2.使用举例 结构体章节的计算结构体占多少字节需要先掌握&#xff08;本人博客结构体篇章中已经讲解过&#xff09; 计算结构体中某变量相对于首地址的偏移&#xff0c;并给出说明 首先&#xff0c;结构体首个…

MyBatis常见报错:org.apache.ibatis.binding.BindingException

哈喽&#xff0c;大家好&#xff0c;我是木头左&#xff01; 异常现象描述 当开发者在使用MyBatis进行数据库操作时&#xff0c;可能会遇到org.apache.ibatis.binding.BindingException: Parameter appId not found这样的错误提示。这个错误通常会让程序无法正常运行&#xff…

如何在华为手机上恢复已删除的视频[4种解决方案]

概括 在数字媒体时代&#xff0c;智能手机已成为我们的个人金库&#xff0c;存储以视频形式捕捉的珍贵记忆。然而&#xff0c;意外删除这些珍贵的文件可能会是一次令人心痛的经历。对于华为手机用户来说&#xff0c;由于删除或其他意外导致视频丢失尤其令人痛苦。但不用担心&a…

cadence23---PCB Editer 学习笔记

1.交互式布局 在Orcad中点击图标N生成第一网表&#xff1a; 在PCB Editer中导入第一网表&#xff0c; 之后点击移动命令并在右侧属性框中勾选Symbol选项卡&#xff1a; 设置--应用模式--点击常规编辑&#xff1a; 之后就可以进行交互式选择了。 绿色图标为打开全部飞线&#…

【Spring Security + OAuth2】OAuth2

Spring Security OAuth2 第一章 Spring Security 快速入门 第二章 Spring Security 自定义配置 第三章 Spring Security 前后端分离配置 第四章 Spring Security 身份认证 第五章 Spring Security 授权 第六章 OAuth2 文章目录 Spring Security OAuth21、OAuth2简介1.1、OAu…

力扣:92. 反转链表 II(Java)

目录 题目描述&#xff1a;示例 1&#xff1a;示例 2&#xff1a;代码实现&#xff1a; 题目描述&#xff1a; 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的…

刷代码随想录有感(78):回溯算法——关于树枝/树层去重的思考(涉及break/continue的使用)

在复原IP地址中&#xff0c;剪枝操作我们使用的是break: if(isvalid(s, start, i)){s.insert(s.begin() i 1, .);pointNum;backtracking(s, i 2, pointNum);s.erase(s.begin() i 1);pointNum--; }else break;在其他情况&#xff0c;举个例子&#xff0c;在含有重复元素求…

WiFi蓝牙模块开发配置过程中需要注意的细节

在很多产品的应用场景中&#xff0c;WIFI网络会给我们提供很多便捷&#xff0c;MCU开发中大多使用串口WIFI蓝牙模块来实现产品接入WIFI网络中。   具体的使用模型如下图所示&#xff1a;整个系统涉及到WIFI网络、手机、服务器平台以及我们设计的产品&#xff0c;一个完整的生…

第十一课,end关键字、简单while循环嵌套、初识for循环

一&#xff0c;end关键字 end关键字用于在print输出的内容后面声明结束的字符&#xff0c;我们之前学过并且十分了解print是默认输出内容之后跟着换行的&#xff0c;如果我们不希望换行而希望使用其它字符来代替换行&#xff0c;就可以用end关键字来实现 特殊的&#xff0c;en…