最近在测试设备的时候,一直在思考一个问题:数据从外界传输 IoT 设备,到底要经过哪些进程,经过哪些函数,一旦知道数据流经过的函数,就知道程序处理数据的逻辑,就很有可能从中发现漏洞。本次就来讲讲如何追踪嵌入式设备的数据流。
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以太网数据包 */
};
其实主要分为三种
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包*/
常见的接收函数:recv、recvfrom、recvmsg…
链路层原始套接字,第一个参数指定协议族类型为 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 跟踪进程。这就是一个跟踪的流程,通过下面的步骤,你可能会有更加清晰的认识。
/proc 是进程的文件系统挂载点,是反映了内核内部的数据结构的虚拟文件系统,/proc/net 记录了网络统计信息和相关数据。
二层 socket:链路层原始套接字的信息通过 /proc/net/packet 查看
Type:创建时指定的协议类型
Iface:是否绑定网口
Mem:已经使用的接收缓存大小
三层 socket:网络层原始套接字信息通过 /proc/net/raw
查看
包括绑定的地址、创建套接字的类型、发送和接收队列已使用的缓存
四层 socket,即 TCP/UDP socket 通过 /proc/net/UDP or TCP 查看
文件数据都储存在“块”中,我们还必须找到一个地方储存文件的“元信息”,比如文件的创建者、文件的创建日期、文件的大小等等。这种储存文件元信息的区域就叫做 inode,中文译名为“索引节点”。
通过前一节,已经可以获取各个具体协议的 inode,根据 inode,使用 lsof,就可以看到相关的进程了。
例如,上图,就可以看到二层协议关联到进程 dhclient,并输出相关的 PID 和 FD,也就是进程号和句柄。当然,对于 TCP/UDP,可以直接使用 netstat
查看相应的进程信息。
到目前为止,已经获取了进程号,接着需要使用 strace 命令来进行跟踪
strace -o result -f -p [PID] -k
通过 strace,可以看到进程发出的系统调用,以及这些系统调用返回的内容
result 结果如下,可以看到 socket 的调用栈,这就是 ping 经过的一些函数调用
另外一种方法,给目标发送大量 socket 报文,top 观察占用率,找到相关进程,使用 gdb 调试,bt 命令可观察函数调用栈
追踪 IoT 设备的数据流可以大大降低我们逆向的难度,快速定位处理数据的关键函数,更快的找到问题所在,当然应该还有其他方式跟踪,本文在此给出这样一个思路,希望大家都能够融会贯通。
socket(int family, int type, int protocol)各参数解释,https://blog.csdn.net/Oliverlyn/article/details/54285992 ↩︎
Linux原始套接字实现分析,http://blog.chinaunix.net/uid-27074062-id-3388166.html ↩︎