DWG 2004 文件头解析实战:0x6C 加密数据 XOR 解密与 3 个关键偏移量定位

发布时间:2026/7/10 5:32:54
DWG 2004 文件头解析实战:0x6C 加密数据 XOR 解密与 3 个关键偏移量定位 DWG 2004 文件头解析实战0x6C 加密数据 XOR 解密与 3 个关键偏移量定位1. DWG 文件头结构概述DWG 2004 文件头是一个固定长度为 0x100 字节的数据块位于文件起始位置。这个头部包含了版本标识、安全标志以及指向文件其他关键部分的指针。对于逆向工程师和 CAD 开发者来说理解这个文件头的结构是解析整个 DWG 文件的第一步。文件头的主要组成部分包括版本标识位于 0x00 偏移处7 字节的字符串 AC1018\0安全标志位于 0x18 偏移处4 字节的位掩码加密数据块位于 0x80 偏移处0x6C 字节的 XOR 加密数据填充数据位于 0xEC 偏移处0x14 字节的魔术字节序列以下是 DWG 2004 文件头的完整内存布局偏移量长度(字节)描述0x007AC1018\0 版本标识0x074保留字段全零0x0B1主版本号0x0C1标志位0/1/30x0D4预览图片地址0x111DWG 版本号0x121DWG 维护版本号0x132代码页索引0x151保留字段0x161应用版本号0x171应用维护版本号0x184安全标志位0x1C4未知字段0x204SummaryInfo 地址0x244VBA 工程地址0x2842004 头部地址固定为 0x800x2C0x54保留字段全零0x800x6C加密的 2004 头部数据0xEC0x14填充数据2. 0x6C 加密数据 XOR 解密算法位于 0x80 偏移处的 0x6C 字节数据是 DWG 文件解析的关键这部分数据使用了一种基于伪随机数生成的 XOR 加密算法。解密过程需要与特定的魔术字节序列进行逐字节异或运算。2.1 魔术字节序列生成魔术字节序列可以通过以下算法生成unsigned char magic_bytes[0x6C]; int seed 1; for (int i 0; i 0x6C; i) { seed seed * 0x343FD 0x269EC3; magic_bytes[i] (unsigned char)(seed 0x10); }生成的魔术字节序列如下十六进制表示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 022.2 XOR 解密实现解密过程就是将加密数据与魔术字节序列逐字节进行异或运算。以下是 C 语言实现示例void decrypt_2004_header(unsigned char* encrypted_data) { int seed 1; for (int i 0; i 0x6C; i) { seed seed * 0x343FD 0x269EC3; encrypted_data[i] ^ (seed 0x10); } }2.3 解密后的数据结构解密后的 0x6C 字节数据具有以下结构偏移量长度(字节)描述0x0012AcFssFcAJMB\0 标志串0x0C4头部地址通常为 0x000x104头部大小0x6C0x144保留字段通常为 0x040x184根树节点间隙0x1C4最左下树节点间隙0x204最右下树节点间隙0x244未知字段0x284最后段 ID0x2C4最后段地址0x304保留字段全零0x344第二头部地址0x384保留字段全零0x3C4间隙总数0x404段总数0x444保留字段通常为 0x200x484保留字段通常为 0x800x4C4保留字段通常为 0x400x504页映射 ID0x544页映射地址相对于数据部分的偏移0x584保留字段全零0x5C4段映射 ID0x604段页数组大小0x644间隙数组大小0x684CRC32 校验值3. 关键偏移量定位与解析解密后的数据中有三个关键偏移量对解析整个 DWG 文件至关重要页映射地址 (Page Map Address)位于 0x54 偏移处段映射 ID (Section Map ID)位于 0x5C 偏移处段页数组大小 (Section Page Array Size)位于 0x60 偏移处3.1 页映射解析页映射地址指向的是文件中的数据页索引表。需要注意的是这个地址是相对于数据部分即文件头之后的偏移量因此实际文件偏移需要加上文件头大小0x100。页映射的结构如下typedef struct { uint32_t page_number; // 页编号从1开始 uint32_t page_size; // 页大小 } PageMapEntry;解析页映射的代码示例void parse_page_map(FILE* file, uint32_t page_map_offset) { fseek(file, 0x100 page_map_offset, SEEK_SET); PageMapEntry entry; uint32_t current_offset 0x100; // 第一个段从文件头后开始 while (fread(entry, sizeof(PageMapEntry), 1, file) 1) { printf(Page %u: size0x%X, file_offset0x%X\n, entry.page_number, entry.page_size, current_offset); current_offset entry.page_size; } }3.2 段映射解析段映射包含了文件中所有段section的详细信息。要找到段映射需要通过页映射找到段映射 ID 对应的页解析该页的数据获取段映射信息段映射页的数据结构如下typedef struct { uint32_t num_descriptions; // 描述数量 uint32_t compressed; // 是否压缩1否2是 uint32_t max_size; // 最大大小通常为0x7400 uint32_t encrypted; // 是否加密0否1是 uint32_t num_descriptions_2; // 描述数量重复 } SectionMapHeader; typedef struct { uint64_t section_size; // 段大小 uint32_t num_pages; // 页数量 uint32_t max_decomp_size; // 最大解压大小 uint32_t unknown; // 未知字段 uint32_t compressed; // 是否压缩1否2是 uint32_t section_type; // 段类型 uint32_t encrypted; // 是否加密0否1是 char name[64]; // 段名称 } SectionDescription; typedef struct { uint32_t page_number; // 页编号 uint32_t page_data_size; // 页数据大小 uint64_t start_offset; // 起始偏移 } SectionPageInfo;3.3 完整解析流程结合上述三个关键偏移量完整的 DWG 文件解析流程如下读取并解析文件头0x100 字节解密 0x80 处的 0x6C 字节加密数据从解密数据中获取页映射地址、段映射 ID 和段页数组大小通过页映射地址找到页映射表通过段映射 ID 在页映射表中找到段映射页解析段映射页获取所有段的信息根据需要访问特定段的数据4. 实战代码示例以下是一个完整的 DWG 2004 文件头解析代码示例包含结构体定义和解密算法#include stdio.h #include stdint.h #include string.h #pragma pack(push, 1) typedef struct { char version[7]; // 0x00: AC1018\0 char x07_unknown[4]; // 0x07: 4 zeros uint8_t is_maint; // 0x0B uint8_t x0c_0_1_3; // 0x0C: 0 or 1 or 3 uint32_t thumbnail_addr;// 0x0D uint8_t dwg_version; // 0x11 uint8_t dwg_maint_version; // 0x12 uint16_t codepage; // 0x13 uint8_t x15_unknown; // 0x15 uint8_t app_version; // 0x16 uint8_t app_maint_version; // 0x17 uint32_t security_type; // 0x18 uint32_t x1c_unknown; // 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: 0x54 zeros union { char encrypted_data[0x6C]; // 0x80 struct { char file_id_string[12]; // AcFssFcAJMB\0 uint32_t header_address; uint32_t header_size; uint32_t x14_unknown; uint32_t root_tree_node_gap; uint32_t lowermost_left_tree_node_gap; uint32_t lowermost_right_tree_node_gap; uint32_t x24_unknown; 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; uint32_t section_map_id; uint32_t section_array_size; uint32_t gap_array_size; uint32_t crc32; } r2004; }; char padding[0x14]; // 0xEC: 0x14 bytes } Dwg2004Header; #pragma pack(pop) void decrypt_2004_header(Dwg2004Header* header) { int seed 1; for (int i 0; i 0x6C; i) { seed seed * 0x343FD 0x269EC3; header-encrypted_data[i] ^ (seed 0x10); } } void print_header_info(const Dwg2004Header* header) { printf(DWG Version: %.6s\n, header-version); printf(Security Flags: 0x%08X\n, header-security_type); printf(Page Map ID: %u\n, header-r2004.page_map_id); printf(Page Map Address: 0x%08X\n, (uint32_t)header-r2004.page_map_address); printf(Section Map ID: %u\n, header-r2004.section_map_id); printf(Section Array Size: %u\n, header-r2004.section_array_size); } int main(int argc, char** argv) { if (argc 2) { printf(Usage: %s dwg_file\n, argv[0]); return 1; } FILE* file fopen(argv[1], rb); if (!file) { perror(Failed to open file); return 1; } Dwg2004Header header; if (fread(header, sizeof(header), 1, file) ! 1) { perror(Failed to read header); fclose(file); return 1; } decrypt_2004_header(header); print_header_info(header); fclose(file); return 0; }5. 调试技巧与常见问题5.1 调试技巧十六进制查看器使用 HxD 或 010 Editor 等工具直接查看 DWG 文件二进制内容CRC 校验解密后的数据包含 CRC32 校验值可用于验证解密是否正确逐步验证先验证文件头基本结构再逐步验证解密数据和关键偏移量5.2 常见问题解密失败检查魔术字节序列生成算法是否正确特别是种子值和移位操作偏移量计算错误注意页映射地址是相对于数据部分的偏移需要加上 0x100字节序问题DWG 文件使用小端字节序在某些平台上可能需要转换提示在实际项目中建议先对小型 DWG 文件进行测试验证解析逻辑正确性后再处理大型文件。