DWG 2004 格式解析系列(二)文件结构

目录

0  总体结构

1  文件头

2  数据

2.1 system section

a) system section page header

b) Section Map page data

c) SectionInfo page data

2.2 Data section


0   总体结构

        从整体上看,一个dwg文件可分为2部分:文件头和数据,数据部分在物理上分页(page)存储的,在逻辑上由一个或多个页组成一个段(section),如图所示:

1  文件头

        文件头长度为0x100个字节,布局如下:

地址

长度(byte)

描述

0x00

7

"AC1018\0"

0x07

4

0x00

0x0B

1

主版本号

0x0C

1

0x00或0x01或0x03

0x0D

4

预览图片的地址

0x11

1

Dwg version

0x12

1

Dwg maintance version

0x13

2

Codepage index

0x15

1

0x00

0x16

1

App version

0x17

1

App maintance version

0x18

4

安全标志

0x0001:加密数据(除了AcDb:Preview and AcDb:SummaryInfo)

0x0002:加密属性(用于AcDb:Preview and AcDb:SummaryInfo)

0x0010:sign data

0x0020:add timestamp

0x1C

4

未知

0x20

4

SummaryInfo地址,points to summary info page + page header size(0x20)

0x24

4

VBA工程地址

0x28

4

0x00000080(似乎是2004header的地址)

0x2C

0x54

全是0x00

0x80

0x6C

加密的数据,称为2004header

0xEC

0x14

填充数据(据说是魔术字节序列的前0x14字节,未做考证)

开始的7个字节是DWG版本标志串,以AC开头,后跟4个数字表述版本。对应关系为:

标注串AutoCAD的发行版本
AC1012R13
AC1014R14
AC10152000 - 2003
AC10182004 - 2006
AC10212007 - 2009
AC10242010 - 2012
AC10272013 - 2017
AC10322018 - 2020

从AutoCAD2000开始,dwg版本号每3年做一次升级,所以AC标志串中的数字并不是连续的。

 0x80处的0x6C个字节的加密数据,与下列0x6C个魔术字节逐一做XOR运算,即可解密。

29 23 BE 84 E1 6C D6 AE    52 90 49 F1 F1 BB E9 EB

B3 A6 DB 3C 87 0C 3E 99    24 5E 0D 1C 06 B7 47 DE

B3 12 4D C8 43 BB 8B A6    1F 03 5A 7D 09 38 25 1F

5D D4 CB FC 96 F5 45 3B    13 0D 89 0A 1C DB AE 32

20 9A 50 EE 40 78 36 FD    12 49 32 F6 9E 7D 49 DC

AD 4F 14 F2 44 40 66 D0    6B C4 30 B7 32 3B A1 22

F6 22 91 9D E1 8B 1F DA    B0 CA 99 02

这0x6C个魔术字节可以用下面的代码得到

unsigned char pc[0x6c];

int randseed = 1;

for (int i=0; i<0x6c; i++) {

   randseed *= 0x343fd;

   randseed += 0x269ec3;

   pc[i] = (unsigned char)(randseed>>0x10);
}

经过以上运算解密后的数据布局如下表,称之为ACAD2004HeaderData,这段数据在文件尾部还有一份,即SecondHeaderData。

字地址

长度(byte)

描述

0x00

12

"AcFssFcAJMB\0" 标志串

0x0C

4

0x00 (long) header address

0x10

4

0x6c(long) header size

0x14

4

0x04 (long)

0x18

4

Root tree node gap

0x1C

4

Lowermost left tree node gap

0x20

4

Lowermost right tree node gap

0x24

4

未知(long)

0x28

4

Last section id

0x2C

4

Last section address

0x30

4

0x00

0x34

4

Second header address

0x38

4

0x00

0x3C

4

Gap 总数

0x40

4

Section总数

0x44

4

0x20 (long)

0x48

4

0x80(long)

0x4C

4

0x40(long)

0x50

4

Page Map Id

0x54

4

Page Map Address

该值是以数据部分为起点的偏移,在整个文件中的偏移需要加上文件头的大小(0x100)

0x58

4

0x00

0x5C

4

Section map Id

0x60

4

Section page array size

0x64

4

Gap array size

0x68

4

CRC32 (long)

在这些数据当中,对解析最重要的当属标红的3个数据了。

(5.25,补充一下文件头的解析)

解析2004的文件头比较简单,只需要很少的代码。先定义如下的文件头结构:

#pragma pack(push, 1)
typedef struct _tagDwg2004Header
{	// 务必保持成员的顺序!
	char				version[7];			    // 0x00: "AC1018\0"
	char				x07_unknown_0[4];	    // 0x07: 4zeros
	char				is_maint;			    // 0x0B
	char				x0c_0_1_3;			    // 0x0C: 0 or 1 or 3
	uint32_t			thumbnail_addr;		    // 0x0D
	char				dwg_version;		    // 0x11
	char				dwg_maint_version;	    // 0x12
	short				codepage;			    // 0x13
	char				x15_unknown_0;		    // 0x15
	char				app_version;		    // 0x16
	char				app_maint_version;	    // 0x17
	uint32_t			security_type;		    // 0x18
	uint32_t			x1c_unknown_0;		    // 0x1C
	uint32_t			summary_info_address;	// 0x20
	uint32_t			vba_proj_address;		// 0x24
	uint32_t			r2004_header_address;	// 0x28: 0x80
	char				x2c_0[0x54];			// 0x2C: 0
	union
	{
		char			encrypted_data[0x6c];	// 0x80
		struct _r2004
		{
			char		file_id_string[12];		// "AcFssFcAJMB\0"
			uint32_t	header_address;		    // 0x00
			uint32_t	header_size;			// 0x6c
			uint32_t	x14_unknown_0;			// 0
			uint32_t	root_tree_node_gap;
			uint32_t	lowermost_left_tree_node_gap;
			uint32_t	lowermost_right_tree_node_gap;
			uint32_t	x24_uknown;
			uint32_t	last_section_id;
			uint64_t	last_page_address;
			uint64_t	second_header_address;
			uint32_t	gap_amount;
			uint32_t	page_amount;
			uint32_t	x44_x20;
			uint32_t	x48_x80;
			uint32_t	x4c_x40;
			uint32_t	page_map_id;
			uint64_t	page_map_address;	// offset from DATA, not include this HEADER size
			uint32_t	section_map_id;
			uint32_t	section_array_size;
			uint32_t	gap_array_size;
			uint32_t	crc32;
		} r2004;
	};
	char				padding[0x14];
} Dwg2004Header;		// total 0x100 bytes
#pragma pack(pop)

 解析的代码:

{
    // 说明:m_header的类型是上面定义的Dwg2004Header,DWG_DATA是dwg文件的原始数据
    memcpy(&m_header, DWG_DATA, sizeof(Dwg2004Header));
	// decrypt r2004 header
	int rseed = 1;
	for (int i = 0; i < 0x6c; i++)
	{
		rseed *= 0x343fd;
		rseed += 0x269ec3;
		m_header.encrypted_data[i] ^= (rseed >> 0x10);
	}
}

2   数据

dwg的数据是分段(section)组织的,文件中涉及到的数据段有:

/// \enum Section type of R2004+
typedef enum _tagDwg2004SectionType
{
	SECTION_UNKNOWN = 0,                  ///< The very first 160 byte
	SECTION_HEADER = 1,                   ///< AcDb:Header
	SECTION_AUXHEADER = 2,                ///< AcDb:AuxHeader
	SECTION_CLASSES = 3,                  ///< AcDb:Classes
	SECTION_HANDLES = 4,                  ///< AcDb:Handles
	SECTION_TEMPLATE = 5,                 ///< AcDb:Template
	SECTION_OBJFREESPACE = 6,             ///< AcDb:ObjFreeSpace
	SECTION_OBJECTS = 7,                  ///< AcDb:AcDbObjects
	SECTION_REVHISTORY = 8,               ///< AcDb:RevHistory
	SECTION_SUMMARYINFO = 9,              ///< AcDb:SummaryInfo
	SECTION_PREVIEW = 10,                 ///< AcDb:Preview
	SECTION_APPINFO = 11,                 ///< AcDb:AppInfo
	SECTION_APPINFOHISTORY = 12,          ///< AcDb:AppInfoHistory
	SECTION_FILEDEPLIST = 13,             ///< AcDb:FileDepList
	SECTION_SECURITY,                     ///< AcDb:Security, if stored with a password
	SECTION_VBAPROJECT,                   ///< AcDb:VBAProject
	SECTION_SIGNATURE,                    ///< AcDb:Signature
	SECTION_ACDS,                         ///< AcDb:AcDsPrototype_1b = 12 (ACIS datastorage)
	SECTION_SECTIONMAP,                   ///< system section: section map
	SECTION_PAGEMAP,				      ///< system section: page map
} Dwg2004SectionType;

        所有的section中,page map段和section map段,这2个section至关重要,必须先根据ACAD2004HeaderData中标红的三个参数从文件中读取,再根据读到的结果,一一读取不同的数据段予以解析。

        首先从page map address处读取pagen map,然后从此map中取得section map Id对应的段(即section map section)的地址,并从文件中读出它的数据。这两种section统称为system section,其他的section则称为data section。之后,由这两兄弟配合就可以通过section name确定各种section的数据页在文件中的位置,根据描述信息予以读取。

        system section都只有一页,而data section 至少有一页。

2.1 system section

system section的数据是按页(page)组织的。一个page包含2部分,页头和页数据:

项目

长度

header

0x14bytes

data

压缩数据,长度由header.CompDataSize指定

其中header对于两种system section来说,结构是一样的,差别在于data的结构的不同。

a) system section page header

Header的结构:

地址(偏移)

长度(byte

含义

0x00

4

Page 类型,有2种system page:

page map page: 0x41630e3b

section map page: 0x4163003b

0x04

4

解压后的数据长度 (DecompDataSize)

0x08

4

压缩后的数据长度 (CompDataSize)

0x0C

4

压缩类型(0x02)

0x10

4

CRC

当 page.CompDataSize==0时,这个system section就结束了,后面也不会再跟随page data。如下图所示就是system section的组织形式:

b) Page Map page data

page map段的页数据经解压缩后,为多个(page number, page size)数据对,其中的page number从1开始计数,该数据对记录的是文件中的各数据页的编号和大小。

地址

长度

描述

0x00

4

page number, 1-based

0x04

4

page size

这种数据对一直重复,直到本页的数据耗尽。在读取时,可以顺便计算出各section在文件中的绝对偏移地址:第一个读到的section的地址总是从文件头后(即0x100处)开始,后续读到的section的偏移可从其前一个section的偏移和大小计算得到。

c) Section Mappage data

从上面的pagemap只能得到已知某个编号的page的位置和大小,无法得到该page的具体信息,包括压缩否,加密否等等,这时就该section map上场了。section map的page.data解压缩后的布局如下,这是一个复合结构:

偏移

长度

含义

0x00

4

Number of descriptions(num_descs)

0x04

4

Compressed?(1=no, 2=yes, normally 2)

0x08

4

Max_size(normally 0x7400)

0x0C

4

Excrypted?(0=no, 1=yes, 2=unknown)

0x10

4

Num descriptions 2

共有num_descs

0x00

8

Size of section

0x08

4

Number of pages(num_pages)

0x0C

4

Max decompress size

0x10

4

未知(会不会是long型的Max decompress size的高位?)

0x14

4

Compressed?(1=no,2=yes,normally 2)

0x18

4

Section type

0x1C

4

Encrypted?(0=no,1=yes,2=unknow)

0x20

64

Section name ("AcDbObjects"等等)

共有

num_pages

0x00

4

page number

0x04

4

page data size

0x08

8

Start offset

对system section的归纳图示:

下面是一个解析实例中,关于AcDbObjects的section map的描述。

	Description No.7
	------------------
	size: 0x2b0e9
	num_pages: 0x6
	max_decomp_size: 0x7400
	unknown2: 0x1
	compressed: 0x2
	type: 0x7
	encrypted: 0x0
	name: AcDb:AcDbObjects

		No.1
		.............
		page number: 6
		data size: 0x30ea
		offset: 0x0
		unknown: 0x0

		No.2
		.............
		page number: 7
		data size: 0x7f0
		offset: 0x7400

		No.3
		.............
		number: 8
		data size: 0x628
		offset: 0xe800

		No.4
		.............
		page number: 9
		data size: 0xf1f
		offset: 0x15c00

		No.5
		.............
		page number: 10
		data size: 0xcc0
		offset: 0x1d000

		No.6
		.............
		page number: 11
		data size: 0xe0c
		offset: 0x24400

2.2 Data section page

data section page的布局为:

项目

长度

含义

Header

0x20

加密头

Data

DataSize

数据

Padding

page size

- DataSzie

-0x20

填充

 对加密头解密后的数据结构为:

偏移

长度

含义

0x00

4

Tag of data section (0x4163043B)

0x04

4

Section type

0x08

4

DataSize, Data的长度

0x0C

4

page size 本page的总长度(包括header,data和padding)

0x10

8

offset,Data解压后在解压缓冲中的存放偏移

0x18

4

Check sum 1

0x1C

4

Check sum 2

加密头的解密方法:

假设0x20长度的头数据存放在header[8]中,offset是本page在dwg文件中的绝对偏移,可从page map中得到。

int32_t  header[8];

int32_t  mask = 0x4164536b ^ offset;

for (int i=0; i<8; ++i)

{

   header[i] ^= mask;

}

从中可以看出Autodesk也挺鸡贼的,还整了一个动态加密。

Data section page的图示:

 当要读取某种类型的数据时,先从Setion map中查找此类型的description,依据description中的page列表,然后透过page map定位到各page,依次读取page数据,将之解压且存放到目的缓冲的startoffset处,最后拼接出此类型数据的数据流供后续解析。

大致过程如下:

附一个实例示意图:

关于dwg文件的结构性的解析,至此就算完成了。

  • 12
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
dwg文件的数据结构可以分为文件头和数据两个部分。文件头包含了一些元信息,如dwg文件的版本号、创建时间等。数据部分则包含了实际的绘图数据。 在dwg文件中,数据部分被分为多个页(page),每个页由一个或多个段(section)组成。每个段存储了一部分绘图数据。 具体来说,在解析dwg文件时,可以先读取文件头部分的数据,并按照一定的规则解密其中的一些内容。 然后,根据文件头中的信息,可以找到数据部分的页表(page map),从而确定数据部分的偏移地址。 在读取数据部分时,可以根据页表和偏移地址来定位每个页的数据,并按照一定的规则解密其中的内容。 总结起来,dwg文件的数据结构包括文件头和数据两部分,数据部分由多个页和段组成,每个段存储一部分绘图数据,并且需要根据文件头中的信息和页表来定位和解析数据。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [DWG 2004 格式解析系列文件结构](https://blog.csdn.net/jiangyb999/article/details/124497625)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值