0%

网络相关知识:网络抓包原理

手机 App 抓包

要实现对 App 的网络数据抓包,需要监控 App 与服务器交互之间的网络节点,监控其中任意一个网络节点(网卡),获取所有经过网卡中的数据,对这些数据按照网络协议进行解析,这就是抓包的基本原理

但是中间网络节点,不受我们控制,所以基本无法实现抓包的,只能在客户端和服务端进行抓包

通常我们监控本地网卡数据,如下图:

本地网络 指的是WIFI的路由,如果直接抓路由器的包还是比较麻烦的,因此我们会在 手机本地路由 之间加一层 代理服务,这样只要抓代理服务的网络数据即可:

Linux 抓包

Linux 抓包是通过 注册一种虚拟的底层网络协议 来完成对网络报文(准确的说是网络设备)消息的处理权

  • 当网卡接收到一个网络报文之后,它会遍历系统中所有已经注册的网络协议(例如,以太网协议,x25协议处理模块)来尝试进行报文的解析处理(这一点和一些文件系统的挂载相似,就是让系统中所有的已经注册的文件系统来进行尝试挂载,如果哪一个认为自己可以处理,那么就完成挂载)
  • 当抓包模块把自己伪装成一个网络协议的时候,系统在收到报文的时候就会给这个伪协议一次机会,让它来对网卡收到的报文进行一次处理,此时该模块就会趁机对报文进行窥探,也就是把这个报文完完整整的复制一份,假装是自己接收到的报文,汇报给抓包模块

具体是使用 libpcap 获取被监听网络接口的数据

在 Linux 内核中,使用网络过滤器的数据包捕获是通过附加钩子来完成的:

  • 可以根据需要在路径中的不同位置指定钩子,后跟内核网络数据包
  • 可以在此处找到组织结构图,其中包含路线后跟包裹以及钩子的可能区域

钩子 hook 是通过以下结构定义的:

1
2
3
4
5
6
7
8
9
10
11
12
typedef unsigned int nf_hookfn(void *priv,
struct sk_buff *skb,
const struct nf_hook_state *state);

struct nf_hook_ops {
nf_hookfn *hook; /* 捕获网络数据包(作为结构发送的数据包)时,调用的处理程序(该字段是传递给处理程序的私有信息) */
struct net_device *dev; /* 要捕获的设备(网络接口) */
void *priv;
u_int8_t pf; /* 包装类型(PF_INET等) */
unsigned int hooknum; /* hook编号 */
int priority; /* 优先级 */
};
  • 钩子函数 hook 的签名中有一个 nf_hook_state 结构体,用于描述 hook 的状态信息,关键条目如下:
1
2
3
4
5
6
7
8
9
struct nf_hook_state {
unsigned int hook; /* hook编号 */
u_int8_t pf; /* 包装类型 */
struct net_device *in; /* 输入接口 */
struct net_device *out; /* 输出接口 */
struct sock *sk; /* 对应的sock(INET套接字) */
struct net *net; /* 对应的net(内核网络命名空间) */
int (*okfn)(struct net *, struct sock *, struct sk_buff *);
};

相关 API 如下:

1
2
int nf_register_net_hook(struct net *net, const struct nf_hook_ops *ops); /* 用于注册挂钩点 */
void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *ops); /* 用于注销挂钩点 */
1
2
3
4
int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg,
unsigned int n); /* 调用n次nf_register_net_hook */
void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg,
unsigned int n); /* 调用n次nf_unregister_net_hook */
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int nf_register_net_hooks(struct net *net, const struct nf_hook_ops *reg,
unsigned int n)
{
unsigned int i;
int err = 0;

for (i = 0; i < n; i++) {
err = nf_register_net_hook(net, &reg[i]);
if (err)
goto err;
}
return err;

err:
if (i > 0)
nf_unregister_net_hooks(net, reg, i);
return err;
}
EXPORT_SYMBOL(nf_register_net_hooks);

void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg,
unsigned int hookcount)
{
unsigned int i;

for (i = 0; i < hookcount; i++)
nf_unregister_net_hook(net, &reg[i]);
}
EXPORT_SYMBOL(nf_unregister_net_hooks);

Linux 抓包的具体实现就是依靠该 hook 机制完成的,当网络过滤器捕获数据时,就可以依靠抓包程序的 hook 把数据包传输到对应的软件中