逆向设计相机协议以获取乐趣和利润

2020-07-16 13:30:11

早在2019年末,我就解剖了一台Reolink B800 IP摄像头,以演示嵌入式Linux系统的各个部分。实际上,它的硬件相当不错-它有一个4K视频传感器,一个麦克风,以太网供电,名义上是防水的。是的,它运行的是Linux。

它是由六个摄像头和一个NVR(也为摄像头供电的专用录像盒)组成的“套件”。不幸的是,NVR相当贫乏:它显然是一个支持4K摄像头的现有机型,它很难同时支持一个以上的观众。

然而,我买了这些摄像头,因为我相信它们支持开放标准,如ONVIF,所以我就把NVR换成在我的服务器上运行的Blue Iris副本。当时,Reolink的支持页面清楚地表明,他们所有的非电池供电的摄像头都支持RTSP。安装系统后,很明显,这些摄像头实际上并不支持RTSP-它们上唯一打开的端口是端口9000。然后,就在我的返回窗口之外,Reolink更新了他们的支持页面,说他们的支持页面上没有RTSP。然后,就在我的返回窗口之外,Reolink更新了他们的支持页面,说他们的支持页面上没有RTSP-它们唯一打开的端口是9000端口。然后,就在我返回的窗口之外,Reolink更新了他们的支持页面,说。

假到我决定按下摄像机,反向工程协议,并编写我自己的软件来获得视频流。最终结果是一款名为Neolink的新开源软件,它允许Blue Iris、Shinobi或其他NVR软件从未经修改的Reolink摄像机接收视频。

作为第一步,我启动了Wireshark并捕获了摄像头与其官方Reolink PC客户端1之间的流量。

我唯一想到的是在每个数据包的开头出现一个同步字0xf0debc0a。(在小端,这是0x0abcdef0。)我随机应变地搜索了一下这个,实际上在GitHub上找到了一个2015年的项目,它试图从Swann相机检索数据!快速浏览一下代码就会发现,尽管它们共享同步字和数据包头,但我的相机和这些老式相机的协议非常不同。有效负载。由于一些有问题的指针操作,代码甚至无法运行。是时候提取我能提取的内容了-标题布局是正确的-然后继续前进。

总是,总是花时间开发调试或分析工具。根据我的经验,这样的工具可以立即为您的时间投资带来4倍或更多的回报。根据我所知道的,我能够使用Mika非常棒的教程为Wireshark编写一个“白川”协议解析器。这既简单又有趣:Wireshark允许您用Lua编写解析器(免责声明:您对乐趣的定义可能会有所不同)。现在Wireshark可以向我显示负载长度和消息ID。虽然不多,但这是一个开始。

为了弄清楚协议使用的是什么加密和/或模糊处理,我计划对固件进行反向工程。我非常确信底层视频使用的是众所周知的协议(特别是因为摄像机似乎有专用的视频编码硬件)。因此,我要做的“所有”工作就是对协议的其余部分进行反向工程。

顺便说一句,这款相机为什么不支持RTSP和/或ONVIF是很自然的。毕竟,很多其他的Reolink相机都支持。因为我想让他们相信,我会提出这样一种可能性,Reolink在这款相机上的存储空间已经用完,不得不削减一些功能。

毕竟,一块16MB的闪存芯片要多花整整两毛钱,这只是一种节约成本的措施,绝对不是厂商锁定的,嗯?

好的。由于协议不能立即访问,是时候打开这个摄像头了。我之前拆解摄像头的时候已经表明,对于这样一个小的Linux系统,它使用的是SPI或FLASH-BOG标准。我想尝试一下我前段时间在淘宝上发现并订购的一个非常整洁的小SOIC插座。我把闪光灯拆开,焊接在一个插座上,而不是像以前那样,我在淘宝上发现并订购了一个非常漂亮的小SOIC插座,于是我把闪光灯拆开,焊接在一个插座上。

相机现在被永久地保护了-我无法做任何事情来把它砌成砖块(我总是可以用闪存刷新它),Reolink也无法阻止我在它上运行我自己的代码(因为如果需要的话,我可以控制执行的第一条指令)。

现在可以方便地将闪存插入插座,我将其倾倒,并使用binwald检查布局。这是闪存布局-工程师倾向于选择的漂亮的圆形字节偏移量。3Bootloader、Linux uImage、squashfs rootfs和JFFS2永久分区。

十进制十六进制DESCRIPTION--67266 0x106C2 ecos RTOS字符串参考1769472 0x1B0000 uImage标头3604480 0x370000 Squashfs文件系统,小字节序7798784 0x770000 JFFS2文件系统,小字节序。

这也与这张由一个名为BinVis的整洁的小工具制作的Flash图像的可视化效果很好地吻合。

我的第一个任务是找到实际的相机二进制文件。它很容易找到;它位于自己的目录/mnt/app/dvr中。DVR二进制文件还有一个附带的dvr.xml,它看起来像一个配置文件。过了一会儿,&;mldr果然有了神奇的单词ONVIF和RTSP!

我怀疑这些仅仅是指示软件启用哪些功能的功能标志。如果我只将这些0更改为1,会怎么样?

重新构建rootfs并不像解压它那么容易,但我通过反复试验拼凑出了一个命令,我希望得到与binwalk报告的格式完全相同的squashfs格式,这样我就可以确定股票内核会挂载它。因为我基本上是在盲目地运行,没有UART控制台,所以我不想有任何麻烦。

$mksquashfs new-squashfs-root/new-squashfs.img-comp xz-b 262144-all-root-noappend$dd if=new-squashfs.img of=pwned.bin bs=1 Seek=$((0x370000))conv=notrunc$flashrom-p ft2232_spi:type=2232h,port=A-w pwned.bin。

唉,事情不是这么容易的。摄像机表现出明显的缺乏行为改革:没有新的端口开放,什么都没有。

为了进行比较,我下载并解压了另一台支持RTSP的Reolink相机的固件更新。那台相机的DVR二进制文件接近8兆字节,而我的受害者的DVR二进制文件只有3个多一点。显然,工程师们编译出了不需要的位。

好的。如果Reolink已经把额外的功能编译出来了,那么相机至少能做的就是给我一个外壳。当我在这里的时候,我决定做一些,嗯,额外的修改。

快速浏览一下Google并没有给出我正在寻找的工具的预构建二进制文件。相反,我签出了一个最新的Buildroot副本,并快速将其设置为使用静态链接的基准MIPS配置,然后要了gdbserver、带有所有补丁的busybox和strace的副本:

30分钟后,我拿到了我的工具。在binwald解压缩的rootfs树中,/etc/init.d中有几种常见的启动脚本。将我精心设计的Busybox复制到/bin,并创建了一个名为telnetd的符号链接,我在其中一个启动脚本中添加了额外的一行:

我打算从固件的静态分析开始,首先对加密方案进行反向工程。如果我被卡住了,我可以在相机二进制文件执行时对其进行询问。

一旦我可以撤销加密,我就可以看到实际的协议是什么样子。

传统的爱好者静态分析工具IDA免费版在这里不是很好,因为我的二进制文件是MIPS的,而免费的IDA拒绝反汇编。取而代之的是Ghidra,这是NSA发布的一个非常好的开源逆向工程套件。现在,通常说“我运行了NSA给我的一个二进制程序”会让你笑出房间。但是Ghidra已经开源了一段时间,所以我觉得安装它是合理的安全的。但是Ghidra已经开源了一段时间,所以我觉得安装它是合理的安全的。现在,通常说“我运行了NSA给我的一个二进制文件”会让你笑出房间。但是Ghidra已经开源了一段时间,所以我觉得安装它是合理的安全的。

Ghidra很棒。说真的,这是一个你必须支付10000美元的软件,它应该是你进行逆向工程工作的首选。除了反汇编和分析器,Ghidra还包括一个反编译器,它可以打印伪C代码,而不是让你在MIPS汇编中挖掘。当你用类型信息和名称注释函数参数时,它还会实时重新分析。这些功能很容易将我花在逆向工程上的时间减少一半。

因此,带着吉陀罗和以前从未做过任何反向工程的虚假自信,我去了白川双星洞穴探险。

大多数反向工程师从检查未知二进制文件中的字符串开始是有原因的-这是一种有效的技术。在我的例子中,检查桌面客户机和固件服务器中的字符串会产生调试打印语句、函数名(Ghidra自动注释)和其他几个奇怪的字符串,稍后我将讨论这些内容。这两个代码库显然都是围绕一个共享的专有“BCSDK”库构建的。

嗯,这里没有RSA,也没有任何类似于“真正”加密的东西,除了AES。(而且我可以很快找到嵌入到应用程序中的任何AES密钥。)。

这不是函数名,我点击了find-reference并读取了使用它的代码。

只需一秒钟就能理解这里是怎么回事:“加密”方案很简单:

太棒了,查理,你的“设计”一直都是在这个协议里永久编码的。孩子们,不要玩你们自己的密码。

不幸的是,Charlie Scrmbler只能从UDP函数调用(参见Ghidra窗口底部的交叉引用),这意味着它不是我的鸽子;我的相机使用的是TCP.此时我不知道其他哪些“加密”函数适合我的相机,所以是时候拿出我的下一个武器了。

在等待shell访问的情况下,我的下一步是附加一个调试器,并使用交叉编译的gdbserver和strace工具从我的工作站远程控制DVR程序。我的Busybox包括一个FTP服务器和一个TCP包装器:

使用此设置,我可以将任何我想要的工具推送到相机文件系统,即使我没有将它们打包到固件中。在变得非常乏味之前,我手动完成了这个过程大约两次。这是一种可以使用Expect自动执行的操作,即Tcl(!)。伪装成控制台用户的程序。我编写了这些交互的脚本,从而将连接、工具推送和GDB设置简化为:

此设置的另一个好处是,我可以将我想要在启动时运行的任何gdb命令粘贴在脚本的末尾,而不是编写专用的gdb脚本。这些动态printf命令只需在相机命中断点时在gdb控制台中打印,有助于在不停止相机的情况下了解正在调用哪些函数:

在连接并停止DVR守护程序后,摄像机立即崩溃并重置。经过快速调查,摄像机在/dev/watchdog处启用了看门狗-这是嵌入式设备非常常见的设置。我在这个软件上做心内直视手术-我不需要一些2位外围设备在旁边徘徊,并用AED击中它!

在命中断点之后,我知道我正在处理的是哪个加密函数:Nets_XmlEncryption。这立即是个好消息,因为在弄清楚加密之后,我可能正在处理很好的普通XML,而不是一些疯狂的半生不熟的C结构。

我查看了Ghidra中有问题的反编译函数,并根据需要进行了注释。Sure看起来确实很熟悉&;mldr。

在重新设计的过程中没有学到任何工程学的经验教训,查理的影响仍然存在!这是查理加密机重新投入使用,但没有混合步骤,而且密钥更短。

很好,不管怎样。在我的Wireshark分析器中实现加扰器并没有花太长时间-尽管我曾短暂地被不同消息类型的不同长度的标题搞糊涂了。一旦实现,我就会看到这样一个壮观的景象:

我不会用血淋淋的细节让你感到厌烦,但我会总结一下我的发现。白川协议多年来已经经历了几次迭代。最古老的协议似乎是基于UDP的,使用的是名为TUTK的专有SDK,在GitHub上可以很方便地找到它的非法副本。这个协议已经不再使用,甚至不存在于相机代码中。

下一个变体实际上是一个纯文本“结构包”,它由一个标题和一个由标题中的消息ID指定的正文组成。这个“传统变体”在B800上短暂使用,以便两个客户端都可以协商升级到“现代变体”,这是您在上面看到的基于XML的加扰方案。

最重要的是,现代报文可以选择性地具有有效载荷。特定的XML报文将整个报文ID切换到“二进制模式”,在后续的报文中提供原始数据流。当客户端发送视频启动命令时,摄像机用包含原始H.265视频的二进制流进行回复。最重要的是,由于某种原因,有效载荷也可以是更加加密的XML,与主XML分开。

一旦我的Wireshark分析器开始工作,就到了编写新客户端的时候了。我希望我的软件是快速、高级和正确的,因为它将是安全系统设置的一部分。

嗯,不完全是。Neolink是一个完全从头开始的新客户端。它和摄像机使用相同的百川协议,它提取视频并通过RTSP将其转发到另一个真正的NVR客户端,如Blue Iris。解析代码有点毛茸茸的4,但除此之外,它很简单。

以下是客户端目前所能做的全部内容。如果您想查看源代码,则非常容易阅读。

发送旧版登录消息,让相机“升级”到现代信息。注意:密码使用纯MD5加密,这是另一个有趣的设计选择。使用不在彩虹表中的密码!

让start_video=BC::new_from_xml(BcMeta{msg_id:MSG_ID_VIDEO,CLIENT_IDX:0,ENCRYPTED:TRUE,class:0x6414,},BcXml{PREVIEW:SOME(Preview{version:xml_ver(),channel_id:0,Handle:0,stream_type:";主流";.to_string(),}),..。default::default()});sub_Video.send(Start_Video)?;

LOOP{let msg=sub_Video.rx.recv_timeout(self.rx_timeout)?;如果让BcBody::Modern nMsg(Modern nMsg{BINARY:SOME(BINARY),..。})=msg.body{data_out.write_all(binary.as_Slice())?;}}。

对于程序的这一部分,我选择了GStreamer,它附带了一个RTSP服务器。GStreamer非常复杂。但是,他们的示例非常棒;他们甚至用Rust提供了一个示例RTSP服务器!

从程序的任意部分馈送GStreamer数据的一般方法是使用一个名为appsr.的块,这使您可以在GStreamer需要数据时获得回调,或者只要有数据就推送数据并让GStreamer处理调度。后一种方法是我在这里使用的方法,因为摄像机不会等待信号来发送视频数据。

关键时刻到来了&;mldr Blue Iris可以连接到我的RTSP服务器并实际显示视频吗?

我已经对Neolink进行了一段时间的浸泡测试,我认为它相当稳定。下一步,我将把它打包成一个真正的Windows服务(而不是命令行程序),以便在我的服务器上与Blue Iris一起运行。去看看吧,自己下载吧!

我也有兴趣让Neolink与其他“仅限NVR”的Reolink摄像头一起工作,这些摄像头相当少。到目前为止,我还没有购买任何其他硬件,所以如果你有这样的摄像头,请联系我们,这样我们就可以测试它了。可能开箱即用。端口扫描你的摄像头!如果他们有可用的9000端口,他们很有可能使用白川协议。

这个项目是对逆向工程的一个“恰到好处”的介绍。像这样的低安全系统可以让你在不积极试图阻挠逆向工程的情况下自学原理。我自学了很多,我希望它能为拥有这些相机的人提供很大的价值。

最后,一些无耻的自我推销:嵌入式Linux系统实际上是相当平易近人的!如果你想学习如何做这类事情,你可能会对我的Mastering Embedded Linux系列感兴趣,该系列旨在帮助你成为黑客攻击低成本嵌入式Linux系统的专家,就像这台相机一样。

如果你喜欢这篇文章,你可以订阅我的博客更新,或者在下面留下评论。谢谢阅读!

Reolink支持论坛总是声称他们的页面是“8小时前更新的”。这显然是无稽之谈。它们确实更新得相当频繁,但不是经常更新。↩︎。

这正是安全引导方案的要点,在这种方案中,不变的引导ROM根据不变的加密密钥验证代码,防止篡改。不用说,这款相机没有实现这样的细微之处。↩︎。

binwald还会出现一大堆误报,很容易被忽略,因为它们没有很好的舍入偏移量。↩︎。

是的,我是一只自豪的密西西比州立大学斗牛犬。你也可以参加,成为一名计算机工程师!↩︎