PKPM-JWS文件格式剖析(三)

18 May

相关阅读:

PKPM-JWS文件格式剖析(一)

PKPM-JWS文件格式剖析(二)

前面唠叨完了Chunk-A与Chunk-B的内容,今天来收尾,把Chunk-C,也就是构件布置的数据解读完成,也算是了却一个事情。

解读数据其实蛮枯燥无聊,特别是开始的激情过去之后。

Chunk-C与Chunk-B的结构非常相似,Chunk-C中是按照标准层来进行数据组织,每个标准层的offset由Chunk-A中的数据记录。每个标准层中按照顺序记录了节点、轴线、网格、墙、梁、柱、洞口等信息,如下图所示。

image

对于每一类布置信息,简要介绍如下:

节点定义;

image

轴线定义:

image

网格定义:

image

墙布置:

image

梁布置:

image

柱布置:

image

好无聊啊,其余的不想整理了,其实很简单的,哪位有兴趣继续完成吧。

PKPM-JWS文件格式剖析(二)

10 May

距离“PKPM-JWS文件格式剖析(一)”一文发布已经有一个多星期,后续的工作由于比较忙没有继续整理,受多个朋友关注,还是决定把成果分享下去。

这次主要剖析第二部分也就是基本定义部分的数据结构。总体上结构非常简单,部分字段没有时间细细研究,相信经过简单验证就可以确定含义。

每一个chunk都由Chunk-A的索引定位。

每一个子chunk的结构如下,首先是结构体数量,然后是结构体数据。所有的都是。

image

废话说完,直接各类定义。

墙定义:

image

柱子、梁、斜撑定义非常类似,不过不知道为什么还需要拆成几块。(补充:原来这三类是分开定义的,比较土)

image

洞口定义

image

荷载定义

荷载定义的解读会稍微复杂,根据类型来确定不同字段的含义。不细究。

image

自然层定义

自然层的定义稍有区别,在块开始位置定义了3个32位的整数,然后才是自然层属性描述。

image

剩下明天继续。

PKPM-JWS文件格式剖析(一)

5 May

PKPM是中国大陆结构设计软件的龙头,其数据文件主要位于*.JWS文件。现在结构类软件相互转换已经成为趋势,在假期对JWS格式进行了逆向,结构非常简单。记录如下,部分数据需要构造合适模型进行简单验证即可,本人没有太多兴趣详细研究下去。

JWS是典型的带索引的Chunk结构,按照功能分为三类:Chunk-A 为索引表,Chunk-B为基本定义数据,Chunk-C为布置数据。

Chunk-A自文件头开始,并提供其余Chunk的索引。

image

Chunk-B和Chunk-C均为超级Chunk块,包含自描述的子块。

image

累了,后面的待续。。。

BMP文件格式分析

20 Apr

1. 位图文件头

位图文件头包含有关于文件类型、文件大小、存放位置等信息,在Windows 3.0以上版本的位图文件中用BITMAPFILEHEADER结构来定义:

typedef struct tagBITMAPFILEHEADER { /* bmfh */

UINT bfType;
DWORD bfSize;
UINT bfReserved1;
UINT bfReserved2;
DWORD bfOffBits;

} BITMAPFILEHEADER;

其中:

bfType

说明文件的类型.(该值必需是0x4D42,也就是字符’BM’。我们不需要判断OS/2的位图标识,这么做现在来看似乎已经没有什么意义了,而且如果要支持OS/2的位图,程序将变得很繁琐。所以,在此只建议你检察’BM’标识)

bfSize

说明文件的大小,用字节为单位

bfReserved1

保留,必须设置为0

bfReserved2

保留,必须设置为0

bfOffBits

说明从文件头开始到实际的图象数据之间的字节的偏移量。这个参数是非常有用的,因为位图信息头和调色板的长度会根据不同情况而变化,所以你可以用这个偏移值迅速的从文件中读取到位数据。

2. 位图信息头

位图信息用BITMAPINFO结构来定义,它由位图信息头(bitmap-information header)和彩色表(color table)组成,前者用BITMAPINFOHEADER结构定义,后者用RGBQUAD结构定义。BITMAPINFO结构具有如下形式:

typedef struct tagBITMAPINFO { /* bmi */

BITMAPINFOHEADER bmiHeader;
RGBQUAD bmiColors[1];

} BITMAPINFO;

其中:

bmiHeader

说明BITMAPINFOHEADER结构,其中包含了有关位图的尺寸及位格式等信息

bmiColors

说明彩色表RGBQUAD结构的阵列,其中包含索引图像的真实RGB值。

BITMAPINFOHEADER结构包含有位图文件的大小、压缩类型和颜色格式,其结构定义为:

typedef struct tagBITMAPINFOHEADER { /* bmih */

DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;

} BITMAPINFOHEADER;

其中:

biSize

说明BITMAPINFOHEADER结构所需要的字数。注:这个值并不一定是BITMAPINFOHEADER结构的尺寸,它也可能是sizeof(BITMAPV4HEADER)的值,或是sizeof(BITMAPV5HEADER)的值。这要根据该位图文件的格式版本来决定,不过,就现在的情况来看,绝大多数的BMP图像都是BITMAPINFOHEADER结构的(可能是后两者太新的缘故吧:-)。

biWidth

说明图象的宽度,以象素为单位

biHeight

说明图象的高度,以象素为单位。注:这个值除了用于描述图像的高度之外,它还有另一个用处,就是指明该图像是倒向的位图,还是正向的位图。如果该值是一个正数,说明图像是倒向的,如果该值是一个负数,则说明图像是正向的。大多数的BMP文件都是倒向的位图,也就是时,高度值是一个正数。(注:当高度值是一个负数时(正向图像),图像将不能被压缩(也就是说biCompression成员将不能是BI_RLE8或BI_RLE4)。

biPlanes

为目标设备说明位面数,其值将总是被设为1

biBitCount

说明比特数/象素,其值为1、4、8、16、24、或32

biCompression

说明图象数据压缩的类型。其值可以是下述值之一:

BI_RGB:没有压缩;

BI_RLE8:每个象素8比特的RLE压缩编码,压缩格式由2字节组成(重复象素计数和颜色索引);

BI_RLE4:每个象素4比特的RLE压缩编码,压缩格式由2字节组成

BI_BITFIELDS:每个象素的比特由指定的掩码决定。

biSizeImage

说明图象的大小,以字节为单位。当用BI_RGB格式时,可设置为0

biXPelsPerMeter

说明水平分辨率,用象素/米表示

biYPelsPerMeter

说明垂直分辨率,用象素/米表示

biClrUsed

说明位图实际使用的彩色表中的颜色索引数(设为0的话,则说明使用所有调色板项)

biClrImportant

说明对图象显示有重要影响的颜色索引的数目,如果是0,表示都重要。

现就BITMAPINFOHEADER结构作如下说明:

(1) 彩色表的定位

应用程序可使用存储在biSize成员中的信息来查找在BITMAPINFO结构中的彩色表,如下所示:

pColor = ((LPSTR) pBitmapInfo + (WORD) (pBitmapInfo->bmiHeader.biSize))

(2) biBitCount

biBitCount=1 表示位图最多有两种颜色,缺省情况下是黑色和白色,你也可以自己定义这两种颜色。图像信息头装调色板中将有两个调色板项,称为索引0和索引1。图象数据阵列中的每一位表示一个象素。如果一个位是0,显示时就使用索引0的RGB值,如果位是1,则使用索引1的RGB值。

biBitCount=4 表示位图最多有16种颜色。每个象素用4位表示,并用这4位作为彩色表的表项来查找该象素的颜色。例如,如果位图中的第一个字节为0x1F,它表示有两个象素,第一象素的颜色就在彩色表的第2表项中查找,而第二个象素的颜色就在彩色表的第16表项中查找。此时,调色板中缺省情况下会有16个RGB项。对应于索引0到索引15。

biBitCount=8 表示位图最多有256种颜色。每个象素用8位表示,并用这8位作为彩色表的表项来查找该象素的颜色。例如,如果位图中的第一个字节为0x1F,这个象素的颜色就在彩色表的第32表项中查找。此时,缺省情况下,调色板中会有256个RGB项,对应于索引0到索引255。

biBitCount=16 表示位图最多有216种颜色。每个色素用16位(2个字节)表示。这种格式叫作高彩色,或叫增强型16位色,或64K色。它的情况比较复杂,当biCompression成员的值是BI_RGB时,它没有调色板。16位中,最低的5位表示蓝色分量,中间的5位表示绿色分量,高的5位表示红色分量,一共占用了15位,最高的一位保留,设为0。这种格式也被称作555 16位位图。如果biCompression成员的值是BI_BITFIELDS,那么情况就复杂了,首先是原来调色板的位置被三个DWORD变量占据,称为红、绿、蓝掩码。分别用于描述红、绿、蓝分量在16位中所占的位置。在Windows 95(或98)中,系统可接受两种格式的位域:555和565,在555格式下,红、绿、蓝的掩码分别是:0x7C00、0x03E0、0x001F,而在565格式下,它们则分别为:0xF800、0x07E0、0x001F。你在读取一个像素之后,可以分别用掩码“与”上像素值,从而提取出想要的颜色分量(当然还要再经过适当的左右移操作)。在NT系统中,则没有格式限制,只不过要求掩码之间不能有重叠。(注:这种格式的图像使用起来是比较麻烦的,不过因为它的显示效果接近于真彩,而图像数据又比真彩图像小的多,所以,它更多的被用于游戏软件)。

biBitCount=24 表示位图最多有224种颜色。这种位图没有调色板(bmiColors成员尺寸为0),在位数组中,每3个字节代表一个象素,分别对应于颜色R、G、B。

biBitCount=32 表示位图最多有232种颜色。这种位图的结构与16位位图结构非常类似,当biCompression成员的值是BI_RGB时,它也没有调色板,32位中有24位用于存放RGB值,顺序是:最高位—保留,红8位、绿8位、蓝8位。这种格式也被成为888 32位图。如果 biCompression成员的值是BI_BITFIELDS时,原来调色板的位置将被三个DWORD变量占据,成为红、绿、蓝掩码,分别用于描述红、绿、蓝分量在32位中所占的位置。在Windows 95(or 98)中,系统只接受888格式,也就是说三个掩码的值将只能是:0xFF0000、0xFF00、0xFF。而在NT系统中,你只要注意使掩码之间不产生重叠就行。(注:这种图像格式比较规整,因为它是DWORD对齐的,所以在内存中进行图像处理时可进行汇编级的代码优化(简单))。

(3) ClrUsed

BITMAPINFOHEADER结构中的成员ClrUsed指定实际使用的颜色数目。如果ClrUsed设置成0,位图使用的颜色数目就等于biBitCount成员中的数目。请注意,如果ClrUsed的值不是可用颜色的最大值或不是0,则在编程时应该注意调色板尺寸的计算,比如在4位位图中,调色板的缺省尺寸应该是16*sizeof(RGBQUAD),但是,如果ClrUsed的值不是16或者不是0,那么调色板的尺寸就应该是ClrUsed*sizeof(RGBQUAD)。

(4) 图象数据压缩

① BI_RLE8:每个象素为8比特的RLE压缩编码,可使用编码方式和绝对方式中的任何一种进行压缩,这两种方式可在同一幅图中的任何地方使用。

编码方式:由2个字节组成,第一个字节指定使用相同颜色的象素数目,第二个字节指定使用的颜色索引。此外,这个字节对中的第一个字节可设置为0,联合使用第二个字节的值表示:

第二个字节的值为0:行的结束。

第二个字节的值为1:图象结束。

第二个字节的值为2:其后的两个字节表示下一个象素从当前开始的水平和垂直位置的偏移量。

绝对方式:第一个字节设置为0,而第二个字节设置为0x03~0xFF之间的一个值。在这种方式中,第二个字节表示跟在这个字节后面的字节数,每个字节包含单个象素的颜色索引。压缩数据格式需要字边界(word boundary)对齐。下面的例子是用16进制表示的8-位压缩图象数据:

03 04 05 06 00 03 45 56 67 00 02 78 00 02 05 01 02 78 00 00 09 1E 00 01
这些压缩数据可解释为 :

压缩数据

扩展数据

03 04
04 04 04

05 06
06 06 06 06 06

00 03 45 56 67 00
45 56 67

02 78
78 78

00 02 05 01
从当前位置右移5个位置后向下移一行

02 78
78 78

00 00
行结束

09 1E
1E 1E 1E 1E 1E 1E 1E 1E 1E

00 01
RLE编码图象结束

② BI_RLE4:

编码方式:由2个字节组成,第一个字节指定象素数目,第二个字节包含两种颜色索引,一个在高4位,另一个在低4位。第一个象素使用高4位的颜色索引,第二个使用低4位的颜色索引,第3个使用高4位的颜色索引,依此类推。

绝对方式:这个字节对中的第一个字节设置为0,第二个字节包含有颜色索引数,其后续字节包含有颜色索引,颜色索引存放在该字节的高、低4位中,一个颜色索引对应一个象素。此外,BI_RLE4也同样联合使用第二个字节中的值表示:

第二个字节的值为0:行的结束。

第二个字节的值为1:图象结束。

第二个字节的值为2:其后的两个字节表示下一个象素从当前开始的水平和垂直位置的偏移量。

下面的例子是用16进制数表示的4-位压缩图象数据:

03 04 05 06 00 06 45 56 67 00 04 78 00 02 05 01 04 78 00 00 09 1E 00 01

这些压缩数据可解释为 :

压缩数据

扩展数据

03 04
0 4 0

05 06
0 6 0 6 0

00 06 45 56 67 00
4 5 5 6 6 7

04 78
7 8 7 8

00 02 05 01
从当前位置右移5个位置后向下移一行

04 78
7 8 7 8

00 00
行结束

09 1E
1 E 1 E 1 E 1 E 1

00 01
RLE图象结束

3. 彩色表

彩色表包含的元素与位图所具有的颜色数相同,象素的颜色用RGBQUAD结构来定义。对于24-位真彩色图象就不使用彩色表(同样也包括16位、和32位位图),因为位图中的RGB值就代表了每个象素的颜色。彩色表中的颜色按颜色的重要性排序,这可以辅助显示驱动程序为不能显示足够多颜色数的显示设备显示彩色图象。RGBQUAD结构描述由R、G、B相对强度组成的颜色,定义如下:

typedef struct tagRGBQUAD { /* rgbq */

BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;

} RGBQUAD;

其中:

rgbBlue

指定蓝色强度

rgbGreen

指定绿色强度

rgbRed

指定红色强度

rgbReserved

保留,设置为0

4. 位图数据

紧跟在彩色表之后的是图象数据字节阵列。图象的每一扫描行由表示图象象素的连续的字节组成,每一行的字节数取决于图象的颜色数目和用象素表示的图象宽度。扫描行是由底向上存储的,这就是说,阵列中的第一个字节表示位图左下角的象素,而最后一个字节表示位图右上角的象素。(只针对与倒向DIB,如果是正向DIB,则扫描行是由顶向下存储的),倒向DIB的原点在图像的左下角,而正向DIB的原点在图像的左上角。同时,每一扫描行的字节数必需是4的整倍数,也就是DWORD对齐的。如果你想确保图像的扫描行DWORD对齐,可使用下面的代码:
(((width*biBitCount)+31)>>5)<<2

PYC文件格式分析

6 Apr

pyc文件就是 Python 程序编译后得到的字节码文件 (py->pyc).

基本格式

pyc文件一般由3个部分组成:

  • 最开始4个字节是一个Maigc int, 标识此pyc的版本信息, 不同的版本的 Magic 都在 Python/import.c 内定义
  • 接下来四个字节还是个int,是pyc产生的时间(1970.01.01到产生pyc时候的秒 数)
  • 接下来是个序列化了的 PyCodeObject(此结构在 Include/code.h 内定 义),序列化方法在 Python/marshal.c 内定义

前两个字段的读写很简单,接下来咱们主要看一下 PyCodeObject 的序列化过程, 由于 PyCodeObject 内还有其他很多类型的 PyObject, 所以咱们从一般的 PyObject 的序列化开始看起:

PyObject的序列化

PyObject的序列化在 Python/marshal.c 内实现, 一般是先写入一个 byte 来标识此 PyObject 的类型, 每种 PyObject 对应的类型也在 Python/marshal.c 内定义:

image

之后是 PyObject 的具体数据内容, 变长的对象(str, tuple, list 等)往往还 包含了一个 4 bytes 的 len, 比如 PyIntObject 的存储可能是这样的:

image

而 PyStringObject 的存储是这样的:

image

PyTupleObject 和 PyListObject 的存储分别是:

image

各种 PyObject 如何序列化,哪些内容被参与了序列化, 可以参看 Python/marshal.c 内的函数 w_object 函数, 接下来咱们着重看下前 面提到的 PyCodeObject 的序列化:

PyCodeObject 的序列化

结构体 PyCodeObject 在 Include/code.h 中定义如下:

image

我们再看 Python/marshal.c 里面的 w_object 函数,从中找出写 PyCodeObject 的部分如下:

image

可以看出,大于等与90的opcode是有参数的, 有参数的opcode的参数是两个 unsigned byte, 第一个是操作数, 第二个目前固定为0x00但是不能省略,举例来 说,我要把当前code的co_consts里的第二个常量(index是1)载入到栈顶,则对应 的opcode序列为: |LOAD_CONST|0x01|0x00| ,也就是 '0x640x010x00'

其他的opcode大都类似,主要是对函数调用栈以及co_consts, co_varnames, co_freevars, co_cellvars的操作, 还有些BUILD_CLASS, BUILD_MAP, BUILD_LIST, BUILD_TUPLE, BUILD_SLICE, MAKE_FUNCTION, MAKE_CLOSURE 等构 建对象的特殊指令.

有参数的 opcode 的参数的参数大多时候是个 index, 比如 LOAD_CONST 1 的 1 就是个 index, 表示把当前 PyCodeObject.co_consts[1] 这个常量载 入到栈顶, LOAD_FAST 2 则是把PyCodeObject.co_varnames[2] 这个 局部变量载入到栈顶;而 MAKE_FUNCTION 2 则表示栈顶code-obj对应的 function有两个默认参数.

Shapefile文件格式分析

3 Apr

Shapefile文件是美国环境系统研究所(ESRI)所研制的GIS文件系统格式文件,是工业标准的矢量数据文件。 Shapefile将空间特征表中的非拓扑几何对象和属性信息存储在数据集中,特征表中的几何对象存为以坐标点集表示的图形文件—SHP文 件,Shapefile文件并不含拓扑(Topological)数据结构。一个Shape文件包括三个文件:一个主文件(*.shp),一个索引文件 (*.shx),和一个dBASE(*.dbf)表。主文件是一个直接存取,变长度记录的文件,其中每个记录描述构成一个地理特征(Feature)的所 有vertices坐标值。在索引文件中,每条记录包含对应主文件记录距离主文件头开始的偏移量,dBASE表包含SHP文件中每一个Feature的特 征属性,表中几何记录和属性数据之间的一一对应关系是基于记录数目的ID。在dBASE文件中的属性记录必须和主文件中的记录顺序是相同的。图形数据和属 性数据通过索引号建立一一对应的关系。

Shapefile中坐标文件(.shp)由 固定长度的文件头和接着的变长度空间数据记录组成。文件头由100字节的说明信息组成的(附表 1),主要说明文件的长度、Shape类型、整个Shape图层的范围等等,这些信息构成了空间数据的元数据。在导入空间数据时首先要读入文件头获取 Shape文件的基本信息,并以此信息为基础建立相应的元数据表。而变长度空间数据记录是由固定长度的记录头和变长度记录内容组成,其记录结构基本类似, 每条记录都有记录头和记录内容组成(空间坐标对)。记录头的内容包括记录号(Record Number)和坐标记录长度(Content Length)两个记录项,Shapefile文件中的记录号都是从1开始的,坐标记录长度是按16位字来衡量的。记录内容包括目标的几何类型 (ShapeType)和具体的坐标记录(X,Y),记录内容因要素几何类型的不同,其具体的内容和格式都有所不同。

属性文件(.dbf)用于记录属性信息。它是 一个标准的DBF文件,也是由头文件和实体信息两部分构成。其中文件头部分的长度是不定长的,它主要对DBF文件作了一些总体说明(附表 3),其中最主要的是对这个DBF文件的记录项的信息进行了详细的描述(附表 4),比如对每个记录项的名称,数据类型,长度等信息都有具体的说明。属性文件的实体信息部分就是一条条属性记录,每条记录都是由若干个记录项构成,因此 只要依次循环读取每条记录就可以了。

索引文件(.shx)主要包含坐标文件的索引 信息,文件中每个记录包含对应的坐标文件记录距离坐标文件的文件头的偏移量。通过索引文件可以很方便地在坐标文件中定位到指定目标地坐标信息。索引文件也 是由文件头和实体信息两部分构成的,其中文件头部分是一个长度固定(100 bytes)的记录段,其内容与坐标文件的文件头基本一致。它的实体信息以记录为基本单位,每一条记录包括偏移量(Offset)和记录段长度 (Content Length)两个记录项。

 

—–

SHAPE 文件格式
SDE,ARC/INFO,PC ARC/INFO,Data  Automation Kit(DAK)和ArcCAD软件提供了shape 到coverage的数据转换器,ARC/INFO同样提供了coverage到shape的转换器。为了和其他数据格式交换,shape文件的格式在本报告中被出版。其他数据流,比如来自全球定位系统(GPS)接收机的数据能同样被存为shape文件或X,Y事件表。
Shape文件技术描述计算机程序能通过使用本节的技术描述来产生,读,写shape文件。
一个ESRI的shape文件包括一个主文件,一个索引文件,和一个dBASE表。主文件是一个直接存取,变量记录长度文件,其中每个记录描述一个有它自己的vertices列表的shape。在索引文件中,每个记录包含对应主文件记录离主文件头开始的偏移,dBASE表包含一feature一个记录的feature的特征。几何和属性间的一一对应关系是基于记录数目的。在dBASE文件中的属性记录必须和主文件中的记录是相同顺序的。
命名习惯所有文件名都符合8.3命名习惯。主文件,索引文件和dBASE文件有相同的前缀。前缀必须是由字符或数字(a-Z,0-9)开始,后跟0到7个字符(a-Z,0-9,_,)主文件的后缀是.shp,索引文件的后缀是.shx,dBASE表的后缀是.dbf。文件名中的所有字母在对文件名敏感的操作系统中都是小写的。
例子
  主文件:counties.shp  索引文件:counties.shx   dBASE表: ounties.dbf数字类型一个shape文件存储整数和双精度数,本文档的余数指以下类型:
整数:有符号32位整数(4字节)
双精度:有符号64位IEEE双精度浮点数(8字节)浮点数必须是数字的值。负无穷,正无穷和非数字(NaN)值在shape文件不被允许。然而shape文件支持’没有数据’的值这样的概念,但是目前只用于衡量。某些小于-1038被shape文件读取程序用来代表’没有数据’的值。
下面的第一节描述shape文件的总体结构和组织。第二节描述shape文件支持的每种shape类型的记录内容。

主文件的组织
主文件(.shp)由固定长度的文件头和接着的变长度记录组成。每个变长度记录是由固定长度的记录头和接着的变长度记录内容组成。图1图解了主文件的结构。
图 1 主文件的结构
文件头  
记录头  记录内容
记录头  记录内容
记录头  记录内容
记录头  记录内容
……
……
记录头  记录内容
Shape文件中所有的内容可以被分为二类:
  与数据相关的:
      。主文件记录内容
      。主文件头的数据描述域(Shape 类型,边界盒等)
  与文件管理相关的:
      。文件和记录长度
      。记录偏移等
整数和双精度整数在文件头中组成数据描述域,在主文件的记录内容是小ndian(PC或Intel)字节顺序。组成文件的其余部分和文件管理的整数和双精度浮点数是大endian(Sun或Motorola)字节顺序。
主文件头
  主文件头100字节长。表1显示带有字节位置,值,类型和字节顺序的文件头中的域。
在此表中,位置是相对于文件的开始。
表 1 主文件头的描述
位置          域                值           类型           字节顺序
0字节        文件代码          9994          整数              大
4字节        未被使用           0            整数              大
8字节        未被使用           0            整数              大
12字节       未被使用           0            整数              大
16字节       未被使用           0            整数              大
20字节       未被使用           0            整数              大
24字节       文件长度        文件长度        整数              大
28字节         版本           1000           整数              小
32字节       Shape类型      Shape类型       整数              小
36字节        边界盒          Xmin         双精度             小
44字节        边界盒          Ymin         双精度             小
52字节        边界盒          Xmax         双精度             小
60字节        边界盒          Ymax         双精度             小
68字节*        边界盒          Zmin         双精度             小
76字节*        边界盒          Zmax         双精度             小
84字节*        边界盒          Mmin         双精度             小
92字节*        边界盒          Mmax         双精度             小
*未被使用,值为0.0,若没有被衡量或是Z轴。
文件长度的值是在16位字下文件的总长度(包括组成文件头的50个16位字)。在shape文件中的所有非空shape被需要是同种shape类型。Shape类型的值如下:
值              shape类型
0 空shape
1                 点
3                 多线
5 多边形
8 多点
11 点Z
13                多线Z
15                多边形Z
18 多点Z
21 点M
23                多线M
25 多边形M
28 多点M
31                多斑块
没有被定义的Shape 类型值(2,4,6等直到33)为将来可能的使用而保留。目前shape文件被局限于包含以上定义的同种shape类型。在将来shape文件可以被允许包含多于一种shape类型。若混合shape类型被实现,文件头中的shape类型将标识该文件。
主文件头的边界盒存储文件中shape的实际幅度。最小边界X和Y直交的(潜在的M,Z)长方形包含了所有的shape。若shape文件是空的(没有记录),min,Ymin,Xmax,Ymax的值是未被定义的。Mmin和Mmax能包含shape文件用来衡量不包含衡量的shape类型的’没有数据’的值(参见2页的数字类型),记录头
每个记录的头存储了记录的数目和记录内容的长度。记录头有一个固定长度8字节。表 2显示文件记录头中域的字节位置,值,类型和字节顺序。在表中,位置是相对于记录的开始的。
表 2 主文件记录头文件的描述
位置          域                值           类型           字节顺序
0字节      记录数目          记录数目        整数              大
4字节      内容长度          内容长度        整数              大
记录数目从1开始。
一个记录的内容长度是按16位字衡量的记录内容长度。每个记录因此为文件的总长度贡献(4+内容长度)个16位字,正如文件头是24字节一样。

主文件记录内容
Shape文件记录内容包含一个shape类型和接着的该shape的几何数据。记录内容的长度依赖于在一个shape中部分和vertices的数目。对每种shape类型,我们首先描述该shape然后是它在磁盘上的存储镜像。在表3到16,位置是相对于记录内容的开始。
空shape
shape类型为0指代一种不带几何数据的空shape,每种要素类型(点,线,多边形等)都支持空,在同一个shape文件中有点和空的点是有效的。常常空shape是放东西的地方;在shape文件产生时被使用和在产生后更被广泛地使用。
表 3 空shape记录内容
位置          域             值        类型      数目     字节顺序
0字节     shape类型          0         整数        1        小
在X,Y位置的Shape类型
点   一个点包括一对以X,Y顺序排列的双精度的坐标
      Point
      {
           Double    X  //X坐标
           Double    Y  //Y坐标
      }
  表  4 点记录内容
位置          域             值        类型      数目     字节顺序
0字节     shape类型          1         整数        1        小
4字节        X              X        双精度       1        小
12字节       Y              Y        双精度       1        小
多点  一个多点代表一个点的集合:
     MultiPoint
     {
        Double[4]           Box          //边界盒
        Integer              NumPoints    //点的数目
        Point[NumPoints]     Points        //在集合中的点
    }
边界盒以Xmin,Ymin,Xmax,Ymax存储。
表  5  多点记录内容
位置          域             值         类型      数目     字节顺序
0字节     shape类型          8         整数        1        小
4字节       Box             Box       双精度       4        小
36字节    NumPoints      NumPoints     整数        1         小
40字节     Points            Points       点      NumPoints    小
PolyLine  一条PolyLine是指一条包含一个或多个部分的有序的vertices的集合。一个部分是指二个或多个点彼此连接的顺序。部分间彼此相连或不连。部分间彼此可能相交或不相交。
因为该定义没有禁止有确定坐标的连续点,shape文件的读程序必须掌握这样的情况。在另外,退化(degenerate)和可能导致零长度的结果是不被允许的。
PolyLine
{
      Double[4]             Box         //边界盒
      Integer                NumParts    //部分的数目
      Integer                NumPoints   //点的总数目
      Integer[NumParts]       Parts        //在部分中第一个点的索引
      Point[NumPoints]       Points       //所有部分的点
}
PolyLine的域在以下为更详细的描述:
Box         被存储的PolyLine的边界盒,以Xmin,Ymin,Xmax,Ymax的顺序存储。
NumParts    在PolyLine中部分的数目。
NumPoints   所有部分的点的总数目。
Parts        NumParts长度的数列。为每条PolyLine存储它在点数列中的第一个点的索引。数列索引是从0开始的。
Points       NumPoints长度的数列。在PolyLine中的每一部分的点被尾到尾存储。部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。
在部分间点之间没有界限。
表  6  PolyLine记录内容
位置          域             值         类型      数目     字节顺序
0字节     shape类型          3         整数        1        小
4字节       Box             Box       双精度       4        小
36字节    NumParts       NumParts      整数        1         小
40字节    NumPoints      NumPoints     整数        1         小
44字节      Parts            Parts       整数      NumParts    小
X字节     Points            Points       点       NumPoints    小
注意:X=44+4* NumParts多边形  一个多边形包含一个或多个环。一个环是四或多个点彼此相连组成的一个闭合的彼此不相交的环。一个多边形可能包括多个外环,一个环的vertices的顺序和方向指示环的哪一边是多边形的内部。在多边形中的洞的环的vertices是逆时针方向的。一个环组成的多边形总是顺时针方向的。一个多边形的环是被做为它的一部分的。因为该定义没有禁止有确定坐标的连续的点,shape文件读程序必须解决这种情况。在另外,退化(degenerate)和可能导致零长度的结果是不被允许的。多边形的结构被定义为PolyLine结构,正如下文:
Polygon
{
      Double[4]          Box        //边界盒
      Integer                NumParts    //部分的数目
      Integer                NumPoints   //点的总数目
      Integer[NumParts]       Parts        //在部分中第一个点的索引
      Point[NumPoints]       Points       //所有部分的点
}
多边形的域在以下为更详细的描述:
Box         被存储的多边形的边界盒,以Xmin,Ymin,Xmax,Ymax的顺序存储。
NumParts    在多边形中环的数目。
NumPoints   所有环的点的总数目。
Parts        NumParts长度的数列。为每条环存储它在点数列中的第一个点的索引。数列索引是从0开始的。
Points       NumPoints长度的数列。在多边形中的每一个环的点被尾到尾存储。环2的点跟在环1的点之后,如此下去。部分数列对每一环保持开始点的数列索引。在环间点之间没有界限。
图 2中的例子图演示了多边形的表现。该图中的多边形有一个洞,总共是8个vertices。

关于多边形shape有以下重要注意事项:
环是闭合的(第一个和最后一个vetex必须是一样的)在点数列中环的顺序是不重要的。
存储在shape文件中的多边形必须是clean,一个clean的多边形是指这样的一个多边形:
1. 没有自交。这意味着属于一个环的一段可能不与另一个环的一段相交。一个多边形
的环可能在vetices处彼此相交,但不是在沿段处。重合的段被认为是相交的。
2. 在定义多边形的线的正确一边有多边形的内部。一个观察者以vertex顺序沿环走时,右边的邻居是多边形的内部。一个独环的多边形的vertices因此都是顺时针的。在这些多边形中的洞有一个逆时针的方向。当定义在多边形中的洞同样是顺时针时,发生"Dirty"多边形错误。这会导致内部的重叠。
图 2 一个多边形的实例
在此例中,NumParts等于2,NumPoints等于10。注意洞多边形的点的顺序是逆序的。
                  0      5
       部   分 : 0      5
                  0   1   2   3   4   5   6   7   8   9
          点   : v1  v2  v3  v4  v1  v5  v8  v7  v6  v5
表 7 多边形记录内容
位置          域             值         类型      数目     字节顺序
0字节     shape类型          5         整数        1        小
4字节       Box             Box       双精度       4        小
36字节    NumParts       NumParts      整数        1         小
40字节    NumPoints      NumPoints     整数        1         小
44字节      Parts            Parts       整数      NumParts    小
X字节      Points           Points       点        NumPoints    小
注意:X=44+4* NumParts
在X,Y空间中衡量的shape类型该种shape有一个额外的坐标 M,注意"没有数据"的值可以被指定为M的一个值。
PointM   一个PointM包括一对以X,Y顺序排列的双精度的坐标,再加上衡量M。
     PointM
     {
        Double     X    //X 坐标
        Double     Y    //Y 坐标
        Double     M    //M 衡量
     }
表  8 PointM记录内容
位置          域             值        类型      数目     字节顺序
0字节     shape类型         21        整数        1        小
4字节        X              X        双精度       1        小
12字节       Y              Y        双精度       1        小
20字节       M              M       双精度       1        小
多PointM   一个多PointM代表一个PointM的集合:
     MultiPoint
     {
        Double[4]           Box          //边界盒
        Integer              NumPoints    //点的数目
        Point[NumPoints]     Points        //在集合中的点
        Double[2]           M Range      //衡量M的上下界
        Double[NumPoints]   M Array       //衡量
      }
多PointM的域在以下为更详细的描述:
Box         被存储的多PointM的边界盒,以Xmin,Ymin,Xmax,Ymax的顺序存储。
NumPoints   点的总数目。
Points       NumPoints长的点的数列。
M Range     以Mmin,Mmax顺序存储的多PointM的最小,最大值。
M Array     NumPoints长的衡量的数组。
表  9  多PointM记录内容
位置          域             值         类型      数目     字节顺序
0字节     shape类型          28         整数       1         小
4字节       Box             Box       双精度      4         小
36字节    NumPoints      NumPoints     整数        1         小
40字节     Points            Points       点      NumPoints    小
X*字节     Mmin            Mmin       双精度      1         小
X+8*字节   Mmax           Mmax       双精度      1         小
X+16*字节  Marry           Marry        双精度   NumPoints    小
注意:X = 40+(16* NumPoints)  *可选的
多LineM   一条shape文件的多LineM包含一个或多个部分。指一条包含一个或多个部分的有序的vertices的集合。一个部分是指二个或多个点彼此连接的顺序。部分间彼此相连或不连。部分间彼此可能相交或不相交。
PolyLineM
{
      Double[4]             Box         //边界盒
      Integer                NumParts    //部分的数目
      Integer                NumPoints   //点的总数目
      Integer[NumParts]       Parts        //在部分中第一个点的索引
      Point[NumPoints]       Points       //所有部分的点
      Double[2]             M Range      //衡量M的上下界
      Double[NumPoints]     M Array       //所有点的衡量
}
PolyLineM的域在以下为更详细的描述:
Box         被存储的PolyLineM的边界盒,以Xmin,Ymin,Xmax,Ymax的顺序存储。
NumParts    在PolyLineM中部分的数目。
NumPoints   所有部分的点的总数目。
Parts        NumParts长度的数列。为每条PolyLineV存储它在点数列中的第一个点的索引。数列索引是从0开始的。
Points       NumPoints长度的数列。在PolyLineM中的每一部分的点被尾到尾存储。部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。
在部分间点之间没有界限。
M Range     以Mmin,Mmax顺序存储的多PointM的最小,最大值。
M Array     NumPoints长的衡量的数组,在多PointM每部分的衡量被被尾到尾存储部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。在部分间点之间没有界限。
表  10  PolyLineM记录内容
位置          域             值         类型      数目     字节顺序
0字节     shape类型          23         整数        1        小
4字节       Box             Box       双精度       4        小
36字节    NumParts       NumParts      整数        1         小
40字节    NumPoints      NumPoints     整数        1         小
44字节      Parts            Parts       整数      NumParts    小
X字节     Points            Points       点       NumPoints    小
Y*字节     Mmin            Mmin       双精度      1         小
Y+8*字节   Mmax            Mmax      双精度      1         小
Y+16*字节  Marry            Marry       双精度   NumPoints    小
注意:X=44+4* NumParts  Y = 40+(16* NumPoints)  *可选的
多边形M 一个多边形M包含一个或多个环。一个环是四或多个点彼此相连组成的一个闭合的彼此不相交的环。注意相交是在X,Y空间计算,而不是在X,Y,M空间。一个多边形可能包括多个外环,一个多边形的环是被做为它的一部分的。

多边形M的结构被定义为PolyLineM结构,正如下文:
PolygonM
{
      Double[4]          Box        //边界盒
      Integer                NumParts    //部分的数目
      Integer                NumPoints   //点的总数目
      Integer[NumParts]       Parts        //在部分中第一个点的索引
      Point[NumPoints]       Points       //所有部分的点
      Double[2]             M Range      //衡量M的上下界
      Double[NumPoints]     M Array       //所有点的衡量
}
多边形M的域在以下为更详细的描述:
Box         被存储的多边形的边界盒,以Xmin,Ymin,Xmax,Ymax的顺序存储。
NumParts    在多边形M中环的数目。
NumPoints   所有环的点的总数目。
Parts        NumParts长度的数列。为每条环存储它在点数列中的第一个点的索引。数列索引是从0开始的。
Points       NumPoints长度的数列。在多边形中的每一个环的点被尾到尾存储。环2的点跟在环1的点之后,如此下去。部分数列对每一环保持开始点的数列索引。在环间点之间没有界限。
M Range     以Mmin,Mmax顺序存储的多PointM的最小,最大值。
M Array     NumPoints长的衡量的数组,在多PointM每部分的衡量被被尾到尾存储部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。在部分间点之间没有界限。
关于PolygonM shape有以下注意事项:
环是闭合的(第一个和最后一个vetex必须是一样的)在点数列中环的顺序是不重要的。

表 11 多边形M的记录内容
位置          域             值         类型      数目     字节顺序
0字节     shape类型          5         整数        1        小
4字节       Box             Box       双精度       4        小
36字节    NumParts       NumParts      整数        1         小
40字节    NumPoints      NumPoints     整数        1         小
44字节      Parts            Parts       整数      NumParts    小
X字节      Points           Points       点        NumPoints    小
Y*字节     Mmin            Mmin       双精度      1         小
Y+8*字节   Mmax            Mmax      双精度      1         小
Y+16*字节  Marry            Marry       双精度   NumPoints    小
注意:X=44+4* NumParts  Y = 40+(16* NumPoints)  *可选的在X,Y,Z空间中衡量的shape类型该种shape有一个额外的坐标 M,注意"没有数据"的值可以被指定为M的一个值。
PointZ   一个PointZ包括一对以X,Y,Z顺序排列的双精度的坐标,再加上衡量M。
     PointM
     {
        Double     X    //X 坐标
        Double     Y    //Y 坐标
        Double     Z    //Z 坐标
        Double     M    //M 衡量
     }
表  12 PointZ记录内容
位置          域             值        类型      数目     字节顺序
0字节     shape类型         21        整数        1        小
4字节        X              X        双精度       1        小
12字节       Y              Y        双精度       1        小
20字节       Z              Z        双精度       1        小
28字节    Measure           M        双精度       1        小
多PointZ   一个多PointZ代表一个PointZ的集合:
     MultiPoint
     {
        Double[4]           Box          //边界盒
        Integer              NumPoints    //点的数目
        Point[NumPoints]     Points        //在集合中的点
        Double[2]           Z Range      //Z的上下界
        Double[NumPoints]   Z Array       //Z的值
        Double[2]           M Range      //衡量M的上下界
        Double[NumPoints]   M Array       //衡量
      }
边界盒是以Xmin,Ymin,Xmax,Ymax的顺序存储。Z Range是以Mmin,Mmax顺序存储的,M Range是以Mmin,Mmax顺序存储的。
表  9  多PointM记录内容
位置          域             值         类型      数目     字节顺序
0字节     shape类型          28         整数       1         小
4字节       Box             Box       双精度      4         小
36字节    NumPoints      NumPoints     整数        1         小
40字节     Points            Points       点      NumPoints    小
X字节     Zmin            Zmin        双精度      1         小
X+8字节   Zmax            Zmax       双精度      1         小
X+16字节  Zarry            Zarry       双精度   NumPoints    小
Y*字节     Mmin            Mmin       双精度      1         小
Y+8*字节   Mmax           Mmax       双精度      1         小
Y+16*字节  Marry           Marry        双精度   NumPoints    小
注意:X = 40+(16* NumPoints)Y=X+16+(8* NumPoints)  *可选的
多LineZ   一条shape文件的多LineZ包含一个或多个部分。指一条包含一个或多个部分的有序的vertices的集合。一个部分是指二个或多个点彼此连接的顺序。部分间彼此相连或不连。部分间彼此可能相交或不相交。
PolyLineZ
{
      Double[4]             Box         //边界盒
      Integer                NumParts    //部分的数目
      Integer                NumPoints   //点的总数目
      Integer[NumParts]       Parts        //在部分中第一个点的索引
      Point[NumPoints]       Points       //所有部分的点
      Double[2]             Z Range      //Z的上下界
      Double[NumPoints]     Z Array       //Z的值
      Double[2]             M Range      //衡量M的上下界
      Double[NumPoints]     M Array       //所有点的衡量
}
PolyLineZ的域在以下为更详细的描述:
Box         被存储的PolyLineZ的边界盒,以Xmin,Ymin,Xmax,Ymax的顺序存储。
NumParts    在PolyLineZ中部分的数目。
NumPoints   所有部分的点的总数目。
Parts        NumParts长度的数列。为每条PolyLineZ存储它在点数列中的第一个点的索引。数列索引是从0开始的。
Points       NumPoints长度的数列。在PolyLineZ中的每一部分的点被尾到尾存储。部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。在部分间点之间没有界限。
ZRange     以Zmin,Zmax顺序存储的多PointZ的最小,最大值。
Z Array     NumPoints长的衡量的数组,在多PointZ每部分的衡量被被尾到尾存储部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。在部分间点之间没有界限。
M Range     以Mmin,Mmax顺序存储的多PointZ的最小,最大值。
M Array     NumPoints长的衡量的数组,在多PointZ每部分的衡量被被尾到尾存储部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。在部分间点之间没有界限。
表  14  PolyLineZ记录内容
位置          域             值         类型      数目     字节顺序
0字节     shape类型          23         整数        1        小
4字节       Box             Box       双精度       4        小
36字节    NumParts       NumParts      整数        1         小
40字节    NumPoints      NumPoints     整数        1         小
44字节      Parts            Parts       整数      NumParts    小
X字节     Points            Points       点       NumPoints    小
Y字节     Zmin            Zmin        双精度      1         小
Y+8字节   Zmax            Zmax       双精度      1         小
Y+16字节  Zarry            Zarry       双精度   NumPoints    小
Z*字节     Mmin            Mmin       双精度      1         小
Z+8*字节   Mmax            Mmax      双精度      1         小
Z+16*字节  Marry            Marry       双精度   NumPoints    小
注意:X=44+4* NumParts Y = X+(16* NumPoints)Z=Y+16+(8* NumPoints)   *可选的多边形Z 一个多边形Z包含一个或多个环。一个环是四或多个点彼此相连组成的一个闭合的彼此不相交的环。一个多边形可能包括多个外环,一个多边形Z的环是被做为它的一部分的。
多边形Z的结构被定义为PolyLineZ结构,正如下文:
PolygonZ
{
      Double[4]          Box        //边界盒
      Integer                NumParts    //部分的数目
      Integer                NumPoints   //点的总数目
      Integer[NumParts]       Parts        //在部分中第一个点的索引
      Point[NumPoints]       Points       //所有部分的点
      Double[2]             Z Range      //Z的上下界
      Double[NumPoints]     Z Array       //Z的值
      Double[2]             M Range      //衡量M的上下界
      Double[NumPoints]     M Array       //所有点的衡量
}
多边形Z的域在以下为更详细的描述:
Box         被存储的多边形的边界盒,以Xmin,Ymin,Xmax,Ymax的顺序存储。
NumParts    在多边形Z中环的数目。
NumPoints   所有环的点的总数目。
Parts        NumParts长度的数列。为每条环存储它在点数列中的第一个点的索引。数列索引是从0开始的。
Points       NumPoints长度的数列。在多边形中的每一个环的点被尾到尾存储。环2的点跟在环1的点之后,如此下去。部分数列对每一环保持开始点的数列索引。在环间点之间没有界限。
ZRange     以Zmin,Zmax顺序存储的多PointZ的最小,最大值。
Z Array     NumPoints长的衡量的数组,在多PointZ每部分的衡量被被尾到尾存储部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。在部分间点之间没有界限。
M Range     以Mmin,Mmax顺序存储的多PointZ的最小,最大值。
M Array     NumPoints长的衡量的数组,在多PointZ每部分的衡量被被尾到尾存储部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。在部分间点之间没有界限。
关于PolygonZ shape有以下注意事项:
环是闭合的(第一个和最后一个vetex必须是一样的)在点数列中环的顺序是不重要的。

表 15 多边形Z的记录内容
位置          域             值         类型      数目     字节顺序
0字节     shape类型          15         整数        1        小
4字节       Box             Box       双精度       4        小
36字节    NumParts       NumParts      整数        1         小
40字节    NumPoints      NumPoints     整数        1         小
44字节      Parts            Parts       整数      NumParts    小
X字节      Points           Points       点        NumPoints    小
Y字节     Zmin            Zmin        双精度      1         小
Y+8字节   Zmax            Zmax       双精度      1         小
Y+16字节  Zarry            Zarry       双精度   NumPoints    小
Z*字节     Mmin            Mmin       双精度      1         小
Z+8*字节   Mmax            Mmax      双精度      1         小
Z+16*字节  Marry            Marry       双精度   NumPoints    小
注意:X=44+4* NumParts Y = X+(16* NumPoints)Z=Y+16+(8* NumPoints)   *可选的多Patch  一个MultiPatch 包括许多表面斑块。每个表面斑块描述了一个表面。MultiPatch的表面斑块是指比如它的部分,控制一个MultiPatch部分的vertices顺序是如何被解释的部分类型。一个MultiPatch的部分可以是以下类型:
   三角形条带  三角形的连接条带,此处每个顶点(在开始二个后)完成一个新三角形。一个新三角形总是通过把新顶点和二个临近的原有点相连而得到。
三角形扇    三角形的连接扇,此处每个顶点(在开始二个后)完成一个新三角形。一个新三角形总是通过把新顶点和一个个临近的原有点及这部分的第一个顶点相连而得到。
外环        多边形的外环。
内环        多边形的洞。
第一环      未定义类型的多边形的第一个环。
环          未定义类型的多边形的环。
一个简单的三角形条带或三角形扇代表一个简单的表面斑块。参见图 3 看这些部分类型的例子。带环的部分的序列能描述有洞的多边形表面。该序列典型包括一个外环,代表斑块的外边界,用许多内环代表洞。当用代表表现带洞的多边形斑块环的集合的个体环是不知道,整个序列以第一环开始,接着是其他的环。不跟着第一环后的环的序列被认为是没有洞的外环的序列。

     图 3 MultiPatch 部分例子
用来对部分类型进行编码的值有:
值                     部分类型
0 三角形条带
1 三角形扇
2 外环
3 内环
4 第一环
5 环
MultiPatch
{
      Double[4]             Box        //边界盒
      Integer                NumParts    //部分的数目
      Integer                NumPoints   //点的总数目
      Integer[NumParts]       Parts        //在部分中第一个点的索引
      Integer[NumParts]       PartsTypes   //部分类型
      Point[NumPoints]       Points       //所有部分的点
      Double[2]             Z Range      //Z的上下界
      Double[NumPoints]     Z Array       //Z的值
      Double[2]             M Range      //衡量M的上下界
      Double[NumPoints]     M Array       //所有点的衡量
}
多边形Z的域在以下为更详细的描述:
Box         被存储的多边形的边界盒,以Xmin,Ymin,Xmax,Ymax的顺序存储。
NumParts    在多边形Z中环的数目。
NumPoints   所有环的点的总数目。
Parts        NumParts长度的数列。为每条环存储它在点数列中的第一个点的索引。数列索引是从0开始的。
PartsType    NumParts长度的数列,存储每一部分的类型。
Points       NumPoints长度的数列。在多边形中的每一个环的点被尾到尾存储。环2的点跟在环1的点之后,如此下去。部分数列对每一环保持开始点的数列索引。在环间点之间没有界限。
ZRange     以Zmin,Zmax顺序存储的多PointZ的最小,最大值。
Z Array     NumPoints长的衡量的数组,在多PointZ每部分的衡量被被尾到尾存储部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。在部分间点之间没有界限。
M Range     以Mmin,Mmax顺序存储的多PointZ的最小,最大值。
M Array     NumPoints长的衡量的数组,在多PointZ每部分的衡量被被尾到尾存储部分2的点跟在部分1的点之后,如此下去。部分数列对每一部分保持开始点的数列索引。在部分间点之间没有界限

MP4文件格式的分析

14 Mar

一、MP4格式分析

MP4(MPEG-4 Part 14)是一种常见的多媒体容器格式,它是在“ISO/IEC 14496-14”标准文件中定义的,属于MPEG-4的一部分,是“ISO/IEC 14496-12(MPEG-4 Part 12 ISO base media file format)”标准中所定义的媒体格式的一种实现,后者定义了一种通用的媒体文件结构标准。MP4是一种描述较为全面的容器格式,被认为可以在其中嵌入任何形式的数据,各种编码的视频、音频等都不在话下,不过我们常见的大部分的MP4文件存放的AVC(H.264)MPEG-4(Part 2)编码的视频和AAC编码的音频。MP4格式的官方文件后缀名是“.mp4”,还有其他的以mp4为基础进行的扩展或者是缩水版本的格式,包括:M4V3GP, F4V等。

mp4是由一个个“box”组成的,大box中存放小box,一级嵌套一级来存放媒体信息。box的基本结构是:

image

其中,size指明了整个box所占用的大小,包括header部分。如果box很大(例如存放具体视频数据的mdat box),超过了uint32的最大数值,size就被设置为1,并用接下来的8位uint64来存放大小。

一个mp4文件有可能包含非常多的box,在很大程度上增加了解析的复杂性,这个网页上http://mp4ra.org/atoms.html记录了一些当前注册过的box类型。看到这么多box,如果要全部支持,一个个解析,怕是头都要爆了。还好,大部分mp4文件没有那么多的box类型,下图就是一个简化了的,常见的mp4文件结构:

image

一般来说,解析媒体文件,最关心的部分是视频文件的宽高、时长、码率、编码格式、帧列表、关键帧列表,以及所对应的时戳和在文件中的位置,这些信息,在mp4中,是以特定的算法分开存放在stbl box下属的几个box中的,需要解析stbl下面所有的box,来还原媒体信息。下表是对于以上几个重要的box存放信息的说明:

image

要获取到mp4文件的帧列表,还挺不容易的,需要一层层解析,然后综合stts stsc stsz stss stco等这几个box的信息,才能还原出帧列表,每一帧的时戳和偏移量。而且,你要照顾可能出现或者可能不出现的那些box。。。可以看的出来,mp4把帧sample进行了分组,也就是chunk,需要间接的通过chunk来描述帧,这样做的理由是可以压缩存储空间,缩小媒体信息所占用的文件大小。这里面,stsc box的解析相对来说比较复杂,它用了一种巧妙的方式来说明sample和chunk的映射关系,特别介绍一下。

image

这是stsc box的结构,前几项的意义就不解释了,可以看到stsc box里每个entry结构体都存有三项数据,它们的意思是:“从first_chunk这个chunk序号开始,每个chunk都有samples_per_chunk个数的sample,而且每个sample都可以通过sample_description_index这个索引,在stsd box中找到描述信息”。也就是说,每个entry结构体描述的是一组chunk,它们有相同的特点,那就是每个chunk包含samples_per_chunk个sample,好,那你要问,这组相同特点的chunk有多少个?请通过下一个entry结构体来推算,用下一个entry的first_chunk减去本次的first_chunk,就得到了这组chunk的个数。最后一个entry结构体则表明从该first_chunk到最后一个chunk,每个chunk都有sampls_per_chunk个sample。很拗口吧,不过,就是这个意思🙂。由于这种算法无法得知文件所有chunk的个数,所以你必须借助于stco或co64。直接上代码可能会清楚些:

1. 首先直接分析entry

image

2. 然后,通过stco或co64获知chunk总个数之后,开始还原映射表

image

读出stsc之后,就可以综合stbl下的所有box,推算出视频和音频帧列表,时戳和偏移量等数据。

有了关键帧列表之后,就可以继续进一步的工作

使用IDA Pro进行工程文件逆向

5 Mar

为什么要进行协议分析

现在很多厂商都选择不公开其开发的软件所使用的协议,或者只公开其中的一部 分。为了能写出能兼容相关软件的产品,或者分析相关软件中有无后门(或者文 绉绉的讲有无可能导致软件产生安全问题的隐藏功能),作为逆向分析人员应该 能够进行协议分析。在这一章里我们通过对一个程序所使用的协议进行分析,最 终推断出其发送的消息的格式。

大多数协议都是由离散的可以被单独解释的各个消息组成的流。但凡事都有例外,比如:HTTP 协议则是由客户端的一个请求和服务端的一个响应组成的,而 且这些请求和响应都是的格式也比较灵活。而 FTP 协议则是由一个基于文本的控 制通道和一个用于文件传输的 TCP 通道组成的。但是这些协议在我所逆向分析过的协议中只占很小的一部分。

如果逆向分析人员拿不到有关的可执行文件,他仍然可以通过在网络上截包并且 分析相关的数据包来进行协议分析。而要是逆向分析人员能够拿到相关的可执行 文件,但是却不能执行这些可执行文件,他也可以通过静态分析来进行协议分析。

但是在大多数情况下,我们还是即能拿到有关的可执行文件又能运行它的。这样 就能结合上面所说的这 2 种方法对协议进行分析。这样做能在很大程度上加快协 议分析的进度,因为分析嗅探下来的数据包能先给逆向分析人员整个协议数据结 构的一个大致的印象,而通过对可执行文件进行逆向分析我们就能检查协议中某个字段的具体含义。只要有足够的时间我们就把整个协议搞个清清楚楚。

消息的分包传送和重组

任何一个协议首先都必须要能够界定这一个消息的结尾和下一个消息的起始位 置在哪里。有时我们把这些消息称为帧(frame)。计算机要想能与远程的另一台 计算机进行通信首先就必须要与远程计算机在接收合解释消息时保持同步。否则 要是一台计算机 A 发送一个 30 个字节长的消息给另一台计算机 B,而 B 却认为这个消息只有 20 个字节,先不说这个消息中所包含的信息能不能被 B 正确的解析出来,B 就不得不把剩下的 10 个字节当成是另一个消息予以处理,结果这 2台计算机之间的通信就根本不可能有效的建立起来。

数据包的自相似性

协议发送的数据包会有一定的相似性,也就是说在一个会话中有些操作是被反复 执行的,所以程序发送的数据包中会有一些字节会反复的出现。几乎每个协议里 通常都会带有一个协议头。在我们用嗅探器嗅探下来的每一个消息中都会带有这 个协议头,即使是在最小的协议头中也至少会包含消息的长度和消息的类型这 2 项内容。这是因为对方计算机在处理消息时至少需要知道当前处理的消息一共有 多大以及这个消息是哪种消息以便调用不同的模块对消息进行正确的处理。下面 列出的是在一般的协议头中经常会出现的几个域:   Magic Number  幻数(用来识别该消息使用的是不是当前应用程序所能处理的协议) Sequence Number  序列号 Timestamp  时间戳 Data or Section Lengths  数据或者是节的长度 Session ID  会话 ID Number of Submessages  子消息的数量 Error Code  错误代码 Random Nonce 随机内容

在开始协议分析之前,你应该先截几个不同的包下来看看,以便对当前要分析的 协议有一个大致的印象。我一般会把截下来的包以十六进制的格式打印出来,以 便在下面的分析过程中能把得到的结果标注在这上面,并进行验证。下面列出的 就是运行这一章所使用的示例程序时截获的几个数据包。我用粗体字表示客户端 发给服务端的数据包中的内容,用斜体字表示从服务端发送给客户端的数据包中 的内容。

image

乍一看,这么多一堆乱七八糟的数据丢在你面前,你一定有点眼晕吧。一下子你 也分不清哪儿是协议头,哪儿是数据。不过让我们静下心来仔细观察一下这些数 据,我想你还是能大致猜点东西出来的。我们首先注意到每个包的前 4 个字节的 内容是完全一模一样的,它们都是 DEADBEEF,我们基本上可以由此肯定这 4 个字节就是协议头里的幻数。我们又注意到最短的包一共是 12 个字节,而应该可以肯定的是最短的包也一定是包含有协议头的,所以协议头的长度不会小于最短的包的长度(即 12 个字节)。所以我们大致猜协议中的第一个结构体(即协议头)的结构至少应该是这样的:

image

image

这些数据包最终是要进入计算机交由程序进行处理的。一般程序会调用几个 API 从网上读取相关的数据并对其进行处理。当然有些程序会使用系统调用(system call),直接从网卡中读取相关数据,而不是使用封装好了的 API 函数,但是这 种情况一般只见于恶意软件中。所以程序从网上读取数据的地方就是我们开始分 析的一个良好的起点。下面列出的就是一般程序中常用的从网络套接字中读取数 据的 API 函数。   read/write   recv/send   recvfrom,/sendto   WSARecv/WSASend   WSARecvFrom/WSASendTo   ioctl   octlsocket   WSARecvDisconnect/WSASendDisconnect   WSARecvEx/WSASendEx   recvmsg/sendmsg   WSARecvMsg/WSASendMsg

看一眼示例程序的导入表(如图 7.3 所示)我们发现上表中只有 WSARecv 这一个 函数出现在了导入表中,所以示例程序中一定是使用 WSARecv 这个函数从网络上 读取数据的。因此,我们协议分析应该就从分析 WSARecv 函数开始。从 IDA 的分 析结果来看,这个 WSARecv 要把从网上读取的数据写到栈中的一个缓冲区里去, 显然,缓冲区的大小是 0x4000 个字节,WSARecv 一次最多读入 0x4000 个字节。

如果消息开头的幻数确实是 DEADBEEF 的话,程序接下来就要检查接下来的 2 个字节是不是小于 8。要是小于 8,这个包就会直接被程序扔掉,不会再对它进行进一步的处理了。据此,我们现在就可以推测,这 2 个字节应该是一个独立的域,它的值应该是大于 8 的(译注:原文如此,但 JNB 是 jump not below 的意思,所以这里应该是大等于 8)。根据这一推测结果,我们就可以修正一下之前画的那张协议头的结构表了。我猜这 2 个字节(unknown1 和 unknown2)应该是代表数据包的长度的,所以我把这个域称为长度域。

 image

示例程序中处理有关消息的主循环现在已经全部分析完毕了。由于我们在这个主
循环里并没有发现有对我们之前猜测的协议头结构体的其他成员进行处理的代
码,所以我们基本上可以肯定我们之前把协议头猜的稍微大了那么一点点。主循
环里没有处理的其他成员应该不属于协议头,而是协议数据包中携带的具体数据
的部分。现在我们已经把协议头的结构初步确定下来了:

image

使用IDA Pro进行逆向工程(提要)

1 Mar

本文属于看雪学院《Reverse Engineering Code with IDA Pro》
图书翻译项目(
http://bbs.pediy.com/showthread.php?t=66430)版权归原作者所有。

内容提要: 
从一个 read()函数开始对协议进行分析
分析出协议数据包的格式
检查协议中是否隐藏有后门
使用 IDA 找出用于处理某种类型数据包的所有函数