如何追踪 IoT 设备进程空间的数据流及其相关调用栈

最近在测试设备的时候,一直在思考一个问题:数据从外界传输 IoT 设备,到底要经过哪些进程,经过哪些函数,一旦知道数据流经过的函数,就知道程序处理数据的逻辑,就很有可能从中发现漏洞。本次就来讲讲如何追踪嵌入式设备的数据流。

0x10 Linux Socket

0x11 Socket 定义

Linux 系统中,所有的数据都是通过 Socket 接口进入进程,常见的封装函数有 VOS_Socket/SS_Socket 等。先看一下 Socket 的定义

Socket(int family, int type, int protocol);

family 表示协议簇,常见参数如下1

#define AF_INET 2 		/* IPv4 协议 */
#define AF_INET6 10		/* IPv6 协议 */
#define AF_PACKET 17 	/* Packet family */
#define AF_NETLINK 16	/* 特殊 socket,用户空间和内核空间的交互,netlink是一种异步通信机制。 系统调用和 ioctl 则是同步通信机制。 */

#define PF_PACKET AF_PACKET

type 指明套接字类型,常见参数如下

enum sock_type {
    
    
	SOCK_STREAM = 1,	/* TCP 协议 */
	SOCK_DGRAM = 2,		/* UPD 协议 */
	SOCK_RAW = 3,		/* 原始 socket,链路层可使用 */
	SOCK_RDM = 4,		/* 提供可靠的数据包连接 */
	SOCK_SEQPACKET= 5,
	SOCK_DCCP = 6,
	SOCK_PACKET = 10,	/* 与网络驱动直接通信 */
	/* PF_PACKET 和 SOCK_RAW 可发送自定义type以太网数据包 */
};

其实主要分为三种

  • 流式套接字(SOCK_STREAM):提供面向连接、可靠的数据传输服务,数据无差错、无重复的发送,且按发送顺序接收(TCP协议)
  • 数据报式套接字(SOCK_DGRAM):提供无连接服务,数据包以独立包形式发送,不提供无措保证,数据可能丢失,并且接收顺序混乱(UDP协议)
  • 原始套接字(SOCK_RAM)

protocol 指明协议类型,0 的时候,family 与 type 共同决定,其值为 0 的时候,famliy 和 type 都能够用来确定使用的协议类型。

#define IPPROTO_IPIP IPPROTO_IPIP
IPPROTO_TCP = 6, /* TCP*/
#define IPPROTO_PUP IPPROTO_PUP
IPPROTO_UDP = 17, /* UDP*/
#define IPPROTO_UDPLITE IPPROTO_UDPLITE
IPPROTO_RAW = 255, /* 原生IP包*/

0x11 Socket 在四层参考模型的不同表现形式

常见的接收函数:recv、recvfrom、recvmsg…

  • 链路层报文接收:socket(0x11, 3, 0)
  • 网络层报文接收:socket(2, 3, …)
  • TCP 接收:socket(2, 2, 0)
  • UDP 接收:socket(2, 1, 0)

链路层原始套接字,第一个参数指定协议族类型为 PF_PACKET,第二个参数可以为 SOCK_RAW 或者 SOCK_DGRAM,第三个参数只对报文接收有意义2。当你逆向二进制的时候,看到的代码表现形式往往是

socket(0x11, 0x3, 0)

网络层原始套接字,第一个参数指定协议族类型为 PF_INET,第二个参数为 SOCK_RAW,同样的道理,反编译的代码往往是数字,而非原始编程时使用的宏定义

socket(2, 3,  ..)

TCP 和 UDP 的套接字是我们最常见的 socket,常常以第二个参数,STREAM 代表 TCP,DGRAM 代表 UDP,这在其他编程语言如 Java Python 里也可以看到。

下面,以 ping 命令为例,ping 发出的网络包到底经过哪些进程或者函数呢?这就需要我们对进程空间的 Socket 有一个清晰的了解,首先识别系统存在的各层协议(ping 在第三层);然后获取 Socket 协议的相关进程的句柄;最后用 strace 跟踪进程。这就是一个跟踪的流程,通过下面的步骤,你可能会有更加清晰的认识。

0x2 进程空间内的 Socket

0x21 识别系统存在的各层协议,获取索引节点

/proc 是进程的文件系统挂载点,是反映了内核内部的数据结构的虚拟文件系统,/proc/net 记录了网络统计信息和相关数据。

二层 socket:链路层原始套接字的信息通过 /proc/net/packet 查看
在这里插入图片描述
Type:创建时指定的协议类型
Iface:是否绑定网口
Mem:已经使用的接收缓存大小

三层 socket:网络层原始套接字信息通过 /proc/net/raw 查看
在这里插入图片描述
包括绑定的地址、创建套接字的类型、发送和接收队列已使用的缓存

四层 socket,即 TCP/UDP socket 通过 /proc/net/UDP or TCP 查看
在这里插入图片描述

0x22 获取 Socket 协议相关进程及句柄信息

文件数据都储存在“块”中,我们还必须找到一个地方储存文件的“元信息”,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做 inode,中文译名为“索引节点”。

通过前一节,已经可以获取各个具体协议的 inode,根据 inode,使用 lsof,就可以看到相关的进程了。
在这里插入图片描述
例如,上图,就可以看到二层协议关联到进程 dhclient,并输出相关的 PID 和 FD,也就是进程号和句柄。当然,对于 TCP/UDP,可以直接使用 netstat 查看相应的进程信息。

0x23 ptrace/strace 跟踪进程

到目前为止,已经获取了进程号,接着需要使用 strace 命令来进行跟踪

strace -o result -f -p [PID] -k

通过 strace,可以看到进程发出的系统调用,以及这些系统调用返回的内容
在这里插入图片描述
result 结果如下,可以看到 socket 的调用栈,这就是 ping 经过的一些函数调用
在这里插入图片描述
另外一种方法,给目标发送大量 socket 报文,top 观察占用率,找到相关进程,使用 gdb 调试,bt 命令可观察函数调用栈
在这里插入图片描述

0x30 总结

追踪 IoT 设备的数据流可以大大降低我们逆向的难度,快速定位处理数据的关键函数,更快的找到问题所在,当然应该还有其他方式跟踪,本文在此给出这样一个思路,希望大家都能够融会贯通。


  1. socket(int family, int type, int protocol)各参数解释,https://blog.csdn.net/Oliverlyn/article/details/54285992 ↩︎

  2. Linux原始套接字实现分析,http://blog.chinaunix.net/uid-27074062-id-3388166.html ↩︎

猜你喜欢

转载自blog.csdn.net/song_lee/article/details/104513710
IOT