ZigBee 终端设备离线后脱离网络

这里记录之前的一次异常处理,直接现象是多功能传感器(ZigBee End-Device,以下简称ZED)通过小米86盒开关 路由设备(ZigBee Router,以下简称ZR)关联加入网络很容易脱离网络。

首先,需要理清的ZED在IEEE-802.15.4 规定为RFD (精简功能设备),精剪功能主要在不能中继数据,作为树状网络拓扑的终端存在,同时受限于功耗,通常会选择大部分时间休眠以节约功耗。为了保证设备休眠能够正常接收数据,所以接收数据的时候选择间接交易,也就是父节点不直接向终端设备发送数据,是唤醒后的终端设备主动去轮询父节点字节是否有数据需要接收。所以其必须依赖父节点完成正常的通信业务,

通常地,如果ZED多次才能够父亲轮询数据失败,机会考虑其成为孤儿并且考虑重新加入网络,基于此特性在TI 协议栈被 描述为可移动设备(Portable Devices)。

  • Z-Stack Developer’s Guide->7.Portable Devices
  • ZigBee Specification 2012 ->2.Application Layer Specification->2.4.3.3.7 Mgmt_Permit_Joining_req
  • Z-Stack Developer’s Guide->10. Security (Page 34)
  • ZigBee Spe...

固件安全之 TF-A 和 SecureBoot

这里详细介绍基于 ARMv8-A AArch64 固件安全相关,包含 TF-A(Trust Firmware A)。以及基于TF-A OEM厂商对 U-Boot 和 Kernel 实现的安全启动流程 SecureBoot。

对于 TF-A,ARMv8-A AArch64 架构需要满足如下设计需求。

  • 区分不同的异常等级(Exception Level);

对于不同的安全等级区分了可信的世界(Trust World)和非可信世界(Non-Trusted World)。我们的应用程序,内核都工作它认为不可信世界,所以没有办法直接访问一些资源。

  • 不同安全等级下访问不同的硬件资源。

例如一些 OTP/eFUSE区域,在 EL0-EL2 异常等级应用程序是没有办法直接访问的。如果需要访问这些资源,通过 SMC 共享内存通过工作更高等级例如 EL3 的程序实现间接访问。

有了如上认识,接下来我们梳理 TF-A 约定的在不同 EL下的引导程序。

通常地,对于ARM AArch64 如下开机执行如下引导顺序。

BL1 作为 ROM Bootloader 工作在最高安全等级 EL3,它拥有独立的 Trusted...

Linux capabilities

  • 对于权限细节参考man 7 capabilities;
  • 对于-+= eip操作符参考 man 3 cap_from_text;
  • 文档中命令setcap、getcap等都可以通过man命令查看;

我需要在VS Code 调试NuttX应用,它的Sim应用需要cap_net_raw,cap_net_admin权限,否则就无法正常操作HCI设备。

当然网上有一些帖子尝试将VS Code或者GDB运行在root用户,显然这样操作是非常不优雅的。

所以这里尝试给需要调试的应用利用linux capabilities直接给可执行文件设置权限。

此时运行程序,可以通过capsh或者getcaps检查进程确认已经设置成功。

此时通过VS Code启动调试后发现其却没有了需要的文件权限。

看起来是父进程/usr/bin/gdb 启动nuttx进程的时候并没有使用其文件文件属性。

直接测试了gdb ./nuttx确认和上面现象一致,strace确认其通过ptrace创建进程。

怀疑这里是gdb进程没有cap_sys_ptrace权限,添加后再次通过VS Code启用nuttx调试发现其进程已经具有我们需要的权限。

udp发送ip端口发送不可达

众所周知,udp是无连接的,所以忽略udp接收方状态,是否监听对应udp端口以及ip是否存在,直接向udp发送数据,会发生什么?

为了搞清楚这个问题,我们尝试在局域网建立收发设备,然后抓包分析。

从抓包来看,udp数据正常发出。对端设备回复icmp指示端口不可达。

当我们把接收端设备关机,此时udp数据正常发出,直到arp连续三次请求失败。此时抓包显示udp包已经不发送了。

此时,我们跟踪sytem call 确定,发用户空间的sendto 接口仍然显示成功。

此时通过查看arp缓存确定ip状态已经标记为INCOMPLETE。

尝试手动更新arp缓存的mac地址和状态到REACHABLE。

此时udp又重新发了出来。

值得一提的是,在路由器查看arp缓存,发现不管设备断开多久,状态都不会变更为INCOMPLETE而始终是STALE。

一次无效的运维

这里记录一次无效的运维,称之为无效,更多的是我人为原因引起的。

1月2号阿里云提示服务器执行恶意脚本,当时正在休元旦,直接忽略。

1月4号上班的时候查看了完整提示,分析了启动上下文,确定服务器被redis恶意注入矿机脚本。能够被注入原因很简单,把无认证的redis直接暴露了在公网,因为该服务器是从其他公司过户过来,稳定运行了5年,接手后保持原来的端口安全策略也没有注意。

第一时间分析了该脚本启动脚本,发现其破坏了大量系统安全策略,同时预留了大量系统认证的后门,例如:

  • 安装矿机应用;

  • 杀死阿里云安全组件;
  • 关闭selinux,开门狗等系统安全组件;
  • 预留ssh认证公钥,预留系统系统登录账号;

而该脚本注入应该是利用redis持久化,把keys保存在了/etc/crontab,好在我们应用工作在docker,并未关闭掉阿里的安全组件,所以阿里第一时间通知我。同时,该脚本只是注入矿机执行,并没有破坏原来的系统应用,也算是盗亦有道了。

正常来说我只要按照该脚本关闭矿机启动,删除应用,恢复安全策略就行了,但是手贱顺便按照阿里提示升级了网络类型从经典网络到专有网络,噩梦就此开始。

之所以升级网络类型,是因为这前后阿里曾打电话告知,3月份之前不升级会导致网络不可用,这台机器几年不运维一次,所以干脆一劳永逸。

升级完网络类型后服务启动异常,第一时间没有定位到原因,所以直接考虑恢复快照。

第一时间提了阿里工单,但是阿里解决...

OpenID over OAuth2.0 详解

需要在Thingsboard上面加OAuth Server功能。

如上是一开始的需求,但是随着深入理解,这里的OAuth Server并不正确,所以需要先科普知识。

对于OAuth ,参考OAuth2.0,里framework里面并没有定义OAuth Server 角色。它定义了4个角色

  • Resource Ower;

    资源拥有者;

  • Resource Server;

    资源服务器,对外提供访问接口;

  • Client

    客户端,需要访问资源的对象;

  • Authorization Server;

    授权服务器;

尽管我们还不知道明确这角色的区分,但是我们需求更接近Authorization Server。

接下来理解OAuth2.0 的协议流程。

看图理解,比较简单。

  • A 客户端发起受保护源请求;
  • B 资源拥有者同意访问,返回授权码;
  • C 客户端通过该授权码继续请求;
  • D 授权服务器分配访问访问令牌;
  • E 客户端通过令牌完成保护资源的访问;
  • F 完成保护资源的访问;

对于过程B,区分不同授权类型(Grant Type),详细参考rfc6749#section-1.3. Authorization Grant,对于OAuth2.0 用以登录,也就是Thingsboard 的OAuth login功能,其实采用了这里的4.1. Authorization Code Grant

在该类型下,OAuth login完整交互流程如下。

这里引入...

vscode 通过数据断点分析segmentation fault

通常地,程序异常,我们借助vscode在ubuntu主动调试,程序会自动在异常代码位置停下来,但是异常分析通常不会这么简单,而是一连串连锁反应。

这里尝试分析,因为程序异常,非法写入了一段正常内存,从而导致多个链路之后异常发生。而对于此问题,最常使用的调试手段就是添加数据断点。

通常直接在某段代码位置添加的代码为函数断点,程序会自动在该位置停下来。同样地,我们可以添加条件断点:

  • 当某个判断条件(例如,变量值为真)满足的时候;
  • 当某段代码连续执行N次的时候;
  • 当某个变量(内存)被读写的时候;

如上前两个条件断点,vsocde 里面的c/c++ extension 已经支持,可以在ide里面点击断点(红色小点)直接编辑,但是最后一个为数据断点还不支持。

需要借助debug console 键入gdb(gnu debugger)命令,

如上操作,成功初始化device_scan_ 成员变量后,程序不再会对变量进行写操作,但是之后该对象的一个成员变量值被异常写入了非法值,当再次用该对象指针打印日志函数时候发生异常。

此时通过p命令查看变量并获取变量首地址。

通过watch命令监控该地址的写操作。

之后全速运行,发现程序在另外一个非法访问该变量位置停了下来,并且通过 Old Value和New Value提示了值变化。

通过分析上下文就可以定位到引发异常的真正原因。

socat

http://www.dest-unreach.org/socat/

socat 是一个命令行工具,用以建立一个双向字节流交换数据。该字节流又可以被构造成不同地址类型,大量的地址选项也同样可以被作用于该流,因此socat非常强大。

filan 是一个打印文件描述符的工具,已经被重构用以调试socat,当然,也可以另做它用。

procan 是一个打印进程信息的工具,同样被重构用以调试socat。

socat 实例包含如下生命周期。

init 阶段解析命令参数,初始化日志打印;

open阶段打开第一个地址并且转发到第二个地址字节流,此阶段可能会阻塞,因此类似socks的复杂地址选项,需要在连接请求或认证对话框完成后才会开始下一阶段。

transfer阶段通过select 参数观察源地址流的读写操作,当可读或者可写时,socat完成到目的流数据转发。

当任意一方流eof,closing阶段开始,socat 主动发送eof操作到对等需要关闭的另外一方尝试优雅的关闭它的写操作的字节流,在运行的时间范围内,socat也可能会继续传输数据到另外一个方向,直到所有的通道被关闭。

命令的地址参数为用户给socat 建立字节流的必要信息。

一个地址规范通常包含一个地址类型的关键词,零或者多个地址参数通过":"间隔,零或者多个地址选项通过","间隔。

关键词指定地址类型(tcp4、open、exec),而对于一些关键字存在...

学习uboot中 U_BOOT_CMD命令参数编程

学习到一个奇淫技能,这里总结如下。方便以后工程实现。

在uboot中大量的命令交互,这些命令都对应其参数、help、和命令处理函数。

通常做法去解析参数名称后遍历每一个命令名称,选择执行。也就是通常的switch、case做法。

uboot给出了不一样的操作。

先看uboot怎么做。

之后对于对应console 键入命令version就能够查找到该命令处理函数 do_version 执行。

接下来,剖析背后细节。

对于do_version通过U_BOOT_CMD完成了函数申明。

include/command.h#L368

include/command.h#L325

include/linker_lists.h#L70

include/command.h#L353

include/command.h#L237

最终U_BOOT_CMD申明定义了结构体类型为cmd_tbl 的变量_u_boot_list_2_cmd_2_version 并且完成了该命令的初始化赋值。

对于如上的变量属性操作__aligned、__attribute__参考6.35 Specifying Attributes of Types。__attribute__(4)表示改结构体变量4字节对齐,__attribute__声明该变量属性可能不...

在linux 下通过vscode调试elf可执行文件

elf 是linux 下可执行可链接文件(executable and linkable format)。通过gcc/make 我们很容易编译出我们需要的elf文件,借助vscode可以在ubuntu图形化调试该程序,再也不用在gdb命令操作了。

如下是elf文件结构,包含文件头(elf header)和代码、数据区索引表(program header table、section header table)和对应代码区、数据区。

明白了如上文件结构,还需要具体解答如下疑惑。

  • 什么文件可以被调试?

    通过file命令对比可以知道用以调试的文件包含 with debug_info。

    可以被调试的文件。

  • debug 版本额外包含什么信息?

    如上可调式的elf文件中增加包含 debug_aranges、debug_info等7个section存储包含数组、文件信息、行数、字符串、宏等调试信息。

    可以通过 --debug-dump=aranges/info/abbrev/line/str/ranges/macro分别显示对应的 区信息。

  • 如何设置debug/release 版本?

    知道了用以调试的程序信息,那么如何编译对应程序的debug版本,通常来说编gcc的 -gdebug 选项和-O优化选项:

有了如上elf文件认识,接下来就是vscode debug程序了,成功编译可执行文件后,通过vscode 建立工程添...