QVR-08DL型DVR的文件系统硬盘研究



本文致力于研究八通道录像机硬盘的文件结构,以大量提取视频文件。 本文的最后是C中相应程序的实现。

录像机(缩写为DVR)QCM-08DL用于视频监视系统,并允许八路视频和音频记录。 我认为,该模型是最便宜的模型,并且同时运行可靠。 视频压缩格式是流行的H264格式。 对于音频,压缩格式为ADPCM。 视频和音频记录在DVR内部安装的标准计算机SATA硬盘(HDD)上。 使用DVR本身,可以通过按日期和时间搜索录像来查看录像。 另外,可以将数据提取到外部介质上的文件中。 首先,将USB驱动器连接到DVR的USB接口。 其次-通过DVR的WEB界面到计算机。 结果文件的名称很长,其中包括录制日期,开始和结束时间,录制频道和其他附加信息。 文件扩展名为“ .264”。 对此类文件内容的检查使我清楚地知道,包装了音频和视频流的媒体容器远非标准。 可以使用DVR附带的播放器打开此类文件。 播放器非常不舒服。 但是,您也可以将重新打包程序用于AVI容器,该容器也包括在内。 该程序将视频流重新打包,保留为H264格式。 声音流从ADMCM转换为PCM,大小增加了4倍。 结果是一个.avi文件,任何标准播放器都可以播放该文件。 我立即注意到该重新打包程序非常不方便。 它仅允许您对一个文件执行操作。 要重新打包一组文件,您必须依次打开它们。

设置了以下任务。

  1. 通过将硬盘连接到计算机,可以访问DVR硬盘上的所有.264文件。
  2. 研究标准264-avi重新打包程序的工作算法,并单击创建一个程序,该程序将执行相同的操作,但不是对一个文件而是对整个文件。

乍一看,第一个任务可能看起来很简单:您只需将HDD连接到计算机并在Explorer中打开分区。 但是,有陷阱。 本文专门讨论第一个任务。

我已经预先知道DVR微控制器的软件外壳基于类似于Linux的操作系统。 因此,硬盘的分区也很可能类似于Linux。 因此,您需要一台Linux计算机。 以我为例,HDD的容量为1TB,这是一台装有操作系统Xubuntu的计算机。 将硬盘驱动器连接到计算机后,每几千兆字节只能看到一个分区。 显然这不是您所需要的。 在该部分中,有许多文件夹的名称格式为“ YYYY-MM-DD”,与记录的日期相对应。 在每个文件夹中,有许多与条目相对应的文件。 与从DVR提取时获得的文件同名的文件。 但是,它们的大小小很多倍,扩展名不是.264,而是.nvr。 应当假定这些相同的nvr文件是相应264文件(或其媒体流)的密钥,其内容位于主HDD空间中。 我将数据从文件夹复制到单独的介质中以进行进一步研究。

我使用了许多软件工具进行研究:磁盘编辑器(它也是二进制文件编辑器)DiskExplorer(我以后使用WinHex),用于辅助计算和修复结果的MS Excel,用于编写辅助和最终控制台程序的Dev-C ++编程环境等。 在本文中,我将尝试讨论此过程。

首先,看一下硬盘的第一个扇区(一个扇区(1个LBA)占用512字节)。 通常,该扇区包含MBR结构。 它包括一个引导程序和一个基本的目录表部分。 下面给出了该部门的结构以及本节说明的结构(摘自Wikipedia)。





对于调查的硬盘,我们有以下内容。 查看下图并跟随上表,我们看到缺少引导加载程序。 但是我们对分区表更感兴趣。 它以红色框突出显示。 最后两个字节(蓝色填充)-MBR签名。 您可以从分区表中看到磁盘分为两部分。 第一部分(黄色填充)的类型的代码为0x0B。 这是FAT32分区。 第二个类型(橙色填充)的代码为0x83。 这是Linux分区之一(在EXT的意义上)。 分区类型代码字节以蓝色圈出。



下面给出了带有节及其参数表的MBR扇区的完整解密。



注意分区的大小(以GB为单位的扇区数),可以很容易地猜到在装有Xubuntu OS的计算机上,它是第一个分区,只占磁盘空间的一小部分。 顺便说一句,在Windows XP中,仅显示了第一个分区,但未从资源管理器中打开。 那么,为什么第二个Linux分区没有出现在Xubuntu OS上?

先前以EXT2为例研究了Linux文件系统的结构和组织之后,我开始研究第二部分。

从节表中可以看到,第二节从扇区16016805开始。EXT2文件系统手册指出了所谓的超级块的存在,该超级块位于节开头的1024字节处(即,从开头开始的两个扇区)。 但是,扇区16016805 + 2 = 16016807为空。 但是第一扇区16016805在其结构上类似于超级块。 但是其内容与手册中有关超级块内容的描述并不完全一致。 超级块是主块,其中包含一种具有各种常数和参数的表,用于文件系统的功能:其他必要块的位置和大小的地址,尤其是文件记录和目录的标题。 本节中的进一步研究仅得出一个结论:DVR使用其自己的独特文件系统。

将来,我决定查看第一部分的第一部分(部分63)并向下滚动。 在扇区65(以下两个扇区)上发现的内容与FS EXT2超级块的内容完全相似,该内容在手册中进行了描述。 进一步的研究得出的结论是,HDD DVR的第一个分区是EXT2分区,无论目录中的0x08(不是EXT)标记如何,该分区都显示在Xubuntu OS上! 因此,DVR硬盘驱动器的第一个分区是EXT2分区,在该分区上记录了nvr文件,这些文件是所需视频记录的关键。

我将简要介绍.264文件的结构,我之前也对此进行了检查。 将来将需要此信息来研究HDD的第二部分。 像在任何媒体容器中一样,在“ 264”中有一个标头,其中包含服务信息和媒体标签,以及音频和视频流,这些音频流以小块的形式依次出现。 与文件开头的偏移量为0x84字节,注册了关键字“ MDVR96NT_2_R”。 在此字之前的是与录制日期和时间有关的字节。 但是此信息包含在文件名中,因此,在此不应该特别注意。 之后是很多零字节。 音频和视频流的主要信息起源于65,536字节的偏移量。 视频流块以8字节的标头“ 01dcH264”(也称为“ 00dcH264”)开头。 以下4个字节以字节为单位描述视频流当前块的大小。 在4个字节的零(00 00 00 00)之后,视频流块本身开始。 音频流块的标题为“ 03wb”(不过,根据我的观察,在某些情况下,标头的第一个字符不一定是“ 0”)。 之后-我尚未弄清12字节的信息。 从第17个字节开始-固定长度为160个字节的音频流。 文件末尾没有标签。

我们继续研究位于HDD第一分区上的文件和目录的结构。 如上所述,该节的内容已通过Xununtu OS中的常规浏览器复制到单独的介质中。 在每个目录(目录)中,除了nvr文件外,还有一个名为“ file_list”的二进制文件。 从名称来看,它包含有关当前目录中文件列表的信息。 在二进制编辑器中打开此文件(请参见下图)。 我研究了该文件的结构,这里基本上没有什么有趣的。 该文件没有有关所需媒体流位置的任何信息。 不过,我将简要介绍一下这种结构。 前32个字节是带有某些常量的标头。 接下来的16个字节与日期和时间以及当前目录中的文件数有关。 随后是48个字节的常量。 下一个-常量的8个字节,指示文件记录的开始。 接下来,96个字节指示nvr文件的完整路径,包括其名称。 下一步-与时间(从一天的开始,视频的开始和结束起经过的秒数)和视频的其他属性相关的24个字节。 以此类推,对于当前目录中的所有nvr文件,依此类推。 它们的数量等于当天的视频数量,由当前目录的名称表示。 这个文件是做什么用的? 显然,是为了加快DVR界面中视频的搜索速度。



让我们继续研究nvr文件本身的结构。 下图显示了一个这样的文件在二进制(更确切地说是十六进制)编辑器中的外观。 在不详细介绍内容结构的描述(部分内容对我来说还是个谜)的情况下,我重点介绍了最基本的参数,这是可以找到的关键。 这些是32位(4字节)值,每32个字节位于偏移量40的字节处。在图中,它们用红色突出显示。 将来,我变得确信这对于视频的关键已经足够了。 我提醒您,此关键参数值的4个字节位于最低到最高位置,反之亦然! 这种表示法是由于PC处理器的体系结构。 图中的示例显示了第一个目录的第一个nvr文件。 它对应于DVR的第一个视频记录。 显然,在上面的示例中,我称其为key的参数的值形成了一个整数序列,从零开始按升序排列。 检查其他nvr文件,并准确查看其中的这些指定字节,还看到整数,以升序排列。 但是,此序列自然不再从头开始,在某些情况下,在某些地方观察到一到两个数字之间存在间隙。 例如(推土机中的数字):435、436、438、439、442,...(或十六进制:B3010000,B4010000,B6010000,B7010000,BA010000,...)。



这种遗漏的序列发生在与DVR从两个或多个频道同时录制的视频相对应的nvr文件上。 就是说,例如,如果序列“ 435、436、438、439、442,...”是指来自一个频道的视频,那么缺失值(437、440、441)将与来自另一个频道的视频相关,这是在同一频道中执行的时间点。 通过根据相应的名称查看并比较相应的nvr文件,我本人对此深信不疑。 毫无疑问,以上数字构成了与视频相关的某些部分的数字。 剩下的只是解开这些数字和数据所在磁盘空间坐标之间的关系。

另外,还要找出到底什么数据被划分为上述编号的段? 第一个假设-数据是音频和视频流,在容器264中由短块表示,并且如上所述,视频流的块具有不同的大小。 同时,DVR在将视频记录提取到外部媒体的阶段收集这些流并将其打包到容器264中;第二个假设是DVR在开始和视频捕获期间将音频和视频流打包到容器264中。 同时,将已经生成的.264文件数据写入HDD,这是由于将其提取到外部介质而导致的。 探索第二部分中间某个地方的HDD空间,以及音频和视频流的字节以及它们与容器264中相同类型的标头,我还遇到了容器本身的标头:MDVR96NT_2_R。 在此标头之后,还有许多零字节。 总体而言,研究表明,上述两种方法还有第二种选择。 因此,最有可能要获得所需的.264文件,只需要将编号都包含在相应nvr文件中的所有段连接在一起。

让我们开始搜索段号和HDD上的坐标之间的关系。

我在扇区16046629(从该节开始处为29824个扇区)上找到的搜索工具开始,对应于第一个视频记录(段的编号从零开始)的容器264的数据开始。 我们可以假设所谓的参数 初始偏差,它将参与描述所需依赖关系的公式。

让我们获取两个nvr文件,它们对应于DVR同时捕获的来自不同频道的视频。 为此,请查看文件名。 例如,同时录制了由文件“ ch00000000000001-150330-160937-161035-02p101000000.nvr”和“ ch00000000000004-150330-160000-163000-00p004000000.nvr”指向的视频。 第一个记录是从第一个通道从16:09:37到16:10:35时间的记录。 第二条记录是从第4个通道开始的记录,时间为16:00:00到16:30:00。 这两个条目都是在2015年3月30日完成的。显然,在时间轴上,第一条记录的时间间隔是第二条记录的时间间隔的子集。 我还考虑到以下事实:在较短的时间间隔(两个时间间隔的交集)中,DVR不会从其他6个通道中的任何一个进行视频捕获。 浏览这些nvr文件的内容。 我们将确保第二个长文件中缺失的数字(段号)必须完全存在于第一个短文件中。 以通常的方式使用DVR,您需要事先提取由调查的nvr文件引用的.264文件中的至少一个。 假设提取了“ ch00000000000001-150330-160937-161035-02p101000000.264”。 在二进制编辑器中将其打开。 如前所述,在此文件的开头,在关键字“ MDVR96NT_2_R”之前,存在与该文件中包含的视频记录的日期和时间相对应的唯一字节。 我们注销所有这些字节,从非零开始到头为止(此视频记录唯一的字节链越短越好)。 同样,从文件的开头写入此字节字符串的偏移量。 应该注意的是,在提取的.264文件的开头,还有4个字节的零。 通过比较.264文件的前512个字节和一个磁盘空间的扇区(一个.264文件的内容从该扇区开始)开始,这一点就变得很明显(几乎任何文件系统的文件始终从该扇区的开头开始,而且从一个群集开始)。 也就是说,.264文件中的信息会提前向右移动4个字节。 仅在首先从大小中减去数字4后,任何.264文件的大小(以字节为单位)是512的倍数。 让我们开始搜索被调查的.264文件所在的扇区。 在磁盘编辑器中,启动搜索功能。 在所需值的字段中,输入预先注销的唯一字节字符串。 为了加快搜索速度,请在“按偏移量搜索”字段中输入偏移值,然后先减去4。开始搜索。 几个小时后,搜索成功。 我们记下唯一标题所在的部门的编号。 使其为s的值。 我们看一下该视频的nvr文件的内容。 我们注销第一段的编号(偏移量40处的4个字节)。 令其为b的值。 总的来说,虽然我们知道零段号的磁盘扇区号(16046629)(在第一次视频记录中),而段号b的磁盘找到的扇区s的数目刚刚被注销。 您可以计算估计的段大小:(s-16046629)/(b-0)。 计算后,我得到的值为128。因此,段大小等于128个磁盘扇区(LBA),即128 * 512 = 65536字节!

我进行了另一项有趣的实验,终于消除了所有疑问。 如下所述。

从扇区s的开头开始,我们在磁盘上选择一个区域,该区域的大小与以该扇区开头的.264文件的大小可比。 如果我的猜测是正确的,则与硬盘硬盘同时捕获的另一个.264文件的片段将落入所选区域。 将该区域保存到文件(创建图像)。 将生成的图像切成65,536字节(段大小)的文件。 可以使用Total Commander中的“分割文件”功能来完成。 设为M1,M2,M3,...。 同样,我们剪切了研究的.264文件(该文件是从DVR中以用户友好的方式提取的),但首先删除了4个字节的零。 设为K1,K2,K3,...。 使用Total Commander中的“按内容比较”功能,我们依次比较图像片段和.264文件中的片段。 (带有K1的M1,带有K2的M2等),由相应nvr文件中的段号引导。 结果如下。假设(推土机中的数字),nvr中的数字链如下:435、436、438、439、442,...在这种情况下,M1 = K1,M2 = K2,M4 = K3,M5 = K4,M8 = K5,....即,根据序列中的省略,考虑到图像文件的片数的相应提前,将图像文件和.264文件划分成的片数相等。在这里!

总的来说,我们获得了估计的依存关系:S = 16046629 + 128 * d,其中d是nvr文件中的段号,S是HDD上的扇区号,从该段内容开始于磁盘的最开始开始。段大小-128个扇区。上面的公式未考虑第二部分的存在。仅在从HDD到1TB的特定示例中找到依赖项。也许如果您在DVR HDD中放置不同的容量,则常数将具有不同的外观。

为了验证公式的有效性,我们在相应的nvr文件的指导下计算了其他任意.264文件的第一段的位置。注意文件名中的日期和时间,将它们与位于计算扇区上的.264标头中的第一个字节进行比较。分别编码数字,月,年,小时,分钟,秒的字节对应于文件名中的临时数据。因此,“打钉子”!我们在对应于预先提取的.264文件的nvr文件中计算cs段的数量。通常,它们的编号为cs = sf / 32-1,其中sf是nvr文件的大小。如果.264文件由cs段组成,则其大小应等于cs * 65536 + 4(段数乘以段大小(以字节为单位),再加上4个相同的零字节)。确实是!

尽管如此,尝试探索第二部分。如前所述,类似于超级块的内容直接位于该部分的第一个扇区中(16016805)。它的确切副本是由以下七个部门(16016812)发现的。显然,非零基本信息位于超级块的第一扇区中。下图显示了它在磁盘编辑器中的外观。



我设法解密了一部分4字节参数。安装分区的日期和时间以蓝色突出显示。日期和时间以特殊符号“ Unix time”(从1970年1月1日午夜起经过的秒数)表示。在上面的示例中,“ 03 7E 74 54”(十进制值1416920579)对应于“星期二,2014年11月25日13:02:59 GMT”。为了转换这些值,我使用了一个特殊的在线计算器。值为65536的框为紫色框,当读取块大小时,DVR程序中的文件系统解释程序可能会引用超级块的此位置(在先前的上下文中,我称为块段)。值1在绿色框中突出显示,其中之一可能表示所谓的开始位置。位图(从本节开始起算的块数)。的确预先找到了信息的开头,类似于扇区16016933(16016805 + 128 * 1)上的位图。值233在红色框中突出显示,这恰好是这些.264视频记录的开始位置,开始于该部分的开头:16016805 + 128 * 233 = 16046629。

也就是说,第二部分可以称为EXT2的截断和稍加修改的部分。它具有一个超级块,一个副本,一个位图。但是没有所谓的。与文件记录相对应的信息节点。该部分包含.264文件(音频和视频流)的数据,但是此数据的信息节点(比方说)位于第一部分的nvr文件中。也许有一个比较称职的措辞?但这对我来说并不重要。

让我们编写一个简单的程序来批量提取.264文件。我必须马上说,我没有在Windows上进行编程的丰富经验。该程序将扫描所有预先复制到新HDD的1TB部分的nvr文件。通过分析它们,该程序使用对原始HDD扇区的访问权限,在同一目录中创建一个具有相同名称的.264文件。以前,在新HDD的空白部分创建了一个名为“ DVR”的文件夹,该文件夹中按日期放置了文件夹,这些文件夹在Linux中以“通常的方式”被复制。可以在此程序中包含一种算法,该算法可与第一个Linux分区一起使用以访问nvr文件,而不必预先复制它们。您还可以添加其他便捷功能。是的,有可能,但是那一刻我想尽快完成所有工作。

鉴于目录的格式是固定的并且具有两个附件级别,因此我没有使用递归来扫描目录。因此,我应用了两个周期:遍历文件夹直到它们结束,然后遍历每个文件夹中具有相同条件的文件。要读取文件,我使用了fopen函数。为了处理HDD扇区,我使用了类似于处理文件的WinAPI功能。让我们继续执行程序代码。

图书馆需要这样。

#include <windows.h> #include <stdio.h> #include <string.h> 

我从某个论坛完全复制了这些功能。

 HANDLE openDevice(int device) { HANDLE handle = INVALID_HANDLE_VALUE; if (device <0 || device >99) return INVALID_HANDLE_VALUE; char _devicename[20]; sprintf(_devicename, "\\\\.\\PhysicalDrive%d", device); // Creating a handle to disk drive using CreateFile () function .. handle = CreateFile(_devicename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); return handle; } HANDLE openOutputFile(const char * filename) { return CreateFile ( filename, // Open Two.txt. GENERIC_WRITE, // Open for writing 0, // Do not share NULL, // No security OPEN_ALWAYS, // Open or create FILE_ATTRIBUTE_NORMAL, // Normal file NULL); // No template file } 

复制函数包含一个线性相关性公式,该公式出现在上面的理论中。

 void copy(HANDLE device, HANDLE file, unsigned long int s){ LONG HPos; LONG LPos; __int64 sector; sector = 16046629+128*s; HPos = (sector*512)>>32; LPos = (sector*512); SetFilePointer (device, LPos, &HPos, FILE_BEGIN); DWORD dwBytesRead; DWORD dwBytesWritten; unsigned char buf[65536]; ReadFile(device, buf, 65536, &dwBytesRead, NULL); WriteFile(file, buf, dwBytesRead, &dwBytesWritten, NULL); } 

主要功能也很简单。

 int main(){ HANDLE hdd = openDevice(1); //    HDD  DVR,    ; SetFilePointer (hdd, 0, NULL, FILE_BEGIN); DWORD dwBytesRead; char name[100]; unsigned int bl; //  ; unsigned int N; // ; unsigned long int pt; //  ; WIN32_FIND_DATA fld,fld1; //   nvr   ; HANDLE hf,hf1; hf=FindFirstFile("E:\\DVR\\*",&fld); FindNextFile(hf,&fld);// "."; FindNextFile(hf,&fld);// ".."; do{ char *str = new char; sprintf(str,"%s%s%s","E:\\DVR\\",fld.cFileName,"\\*.nvr"); printf("\n\nFOLDER: %s\n\n",str); hf1=FindFirstFile(str,&fld1); do{ FILE *nvr; sprintf(name,"%s%s%s%s","E:\\DVR\\",fld.cFileName,"\\",fld1.cFileName); nvr=fopen(name,"rb"); name[strlen(name)-3]='2'; //   ,  name[strlen(name)-2]='6'; // ; name[strlen(name)-1]='4'; HANDLE out = openOutputFile(name); SetFilePointer(out, 4, NULL, FILE_BEGIN); //  "",  4      (  ); bl=0; N=fld1.nFileSizeLow/32-1; //   (); printf("\t%s\n\t%i Blocks\n\n",fld1.cFileName,N); for(bl=0;bl<N;bl++){ //  ; fseek(nvr,40+32*bl,SEEK_SET); //; fread(&pt,1,4,nvr); // ; copy(hdd,out,pt); //  ; } CloseHandle(out); fclose(nvr); }while(FindNextFile(hf1,&fld1)); FindClose(hf1); delete str; }while(FindNextFile(hf,&fld)); FindClose(hf); CloseHandle(hdd); system("PAUSE"); return 0; } 

在装有奔腾4处理器和PCI SATA控制器的旧计算机上,该程序平均在7个小时内成功地将包含数千个.264文件的完整HDD传输到了最后。 在新计算机上-快三倍。 正如我已经指出的,该程序不是通用的,所有常量和变量都根据我的特定情况从HDD调整到1TB。 但是,您可以做更多的工作,使其通用,并为其绘制图形界面。

在本文的第二部分中,我将写出如何“自己动手”以将其从“ 264”容器重新包装为标准“ avi”容器的方法。

Source: https://habr.com/ru/post/zh-CN421933/


All Articles