当前位置: 首页 > news >正文

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 文件头的完整内存布局:

偏移量长度(字节)描述
0x007"AC1018\0" 版本标识
0x074保留字段(全零)
0x0B1主版本号
0x0C1标志位(0/1/3)
0x0D4预览图片地址
0x111DWG 版本号
0x121DWG 维护版本号
0x132代码页索引
0x151保留字段
0x161应用版本号
0x171应用维护版本号
0x184安全标志位
0x1C4未知字段
0x204SummaryInfo 地址
0x244VBA 工程地址
0x2842004 头部地址(固定为 0x80)
0x2C0x54保留字段(全零)
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 02

2.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 字节数据具有以下结构:

偏移量长度(字节)描述
0x0012"AcFssFcAJMB\0" 标志串
0x0C4头部地址(通常为 0x00)
0x104头部大小(0x6C)
0x144保留字段(通常为 0x04)
0x184根树节点间隙
0x1C4最左下树节点间隙
0x204最右下树节点间隙
0x244未知字段
0x284最后段 ID
0x2C4最后段地址
0x304保留字段(全零)
0x344第二头部地址
0x384保留字段(全零)
0x3C4间隙总数
0x404段总数
0x444保留字段(通常为 0x20)
0x484保留字段(通常为 0x80)
0x4C4保留字段(通常为 0x40)
0x504页映射 ID
0x544页映射地址(相对于数据部分的偏移)
0x584保留字段(全零)
0x5C4段映射 ID
0x604段页数组大小
0x644间隙数组大小
0x684CRC32 校验值

3. 关键偏移量定位与解析

解密后的数据中有三个关键偏移量对解析整个 DWG 文件至关重要:

  1. 页映射地址 (Page Map Address):位于 0x54 偏移处
  2. 段映射 ID (Section Map ID):位于 0x5C 偏移处
  3. 段页数组大小 (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: size=0x%X, file_offset=0x%X\n", entry.page_number, entry.page_size, current_offset); current_offset += entry.page_size; } }

3.2 段映射解析

段映射包含了文件中所有段(section)的详细信息。要找到段映射,需要:

  1. 通过页映射找到段映射 ID 对应的页
  2. 解析该页的数据获取段映射信息

段映射页的数据结构如下:

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 文件解析流程如下:

  1. 读取并解析文件头(0x100 字节)
  2. 解密 0x80 处的 0x6C 字节加密数据
  3. 从解密数据中获取页映射地址、段映射 ID 和段页数组大小
  4. 通过页映射地址找到页映射表
  5. 通过段映射 ID 在页映射表中找到段映射页
  6. 解析段映射页获取所有段的信息
  7. 根据需要访问特定段的数据

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 调试技巧

  1. 十六进制查看器:使用 HxD 或 010 Editor 等工具直接查看 DWG 文件二进制内容
  2. CRC 校验:解密后的数据包含 CRC32 校验值,可用于验证解密是否正确
  3. 逐步验证:先验证文件头基本结构,再逐步验证解密数据和关键偏移量

5.2 常见问题

  1. 解密失败:检查魔术字节序列生成算法是否正确,特别是种子值和移位操作
  2. 偏移量计算错误:注意页映射地址是相对于数据部分的偏移,需要加上 0x100
  3. 字节序问题:DWG 文件使用小端字节序,在某些平台上可能需要转换

提示:在实际项目中,建议先对小型 DWG 文件进行测试,验证解析逻辑正确性后再处理大型文件。

http://www.jsqmd.com/news/1158965/

相关文章:

  • 他励直流电动机 MATLAB/Simulink 仿真:3种启动方式对比与串电阻分级启动配置
  • STM32L021K4驱动EPT-14A4005P压电扬声器的低功耗警报方案
  • Node.js 22.4.0 原生 WebSocket 客户端实战:告别 ws 库的 3 步连接
  • Origin 2024b安装避坑指南:系统兼容性与运行时冲突解决方案
  • Cookies 和 Private state tokens
  • 1688一件代发怎么操作?自建一件代发电商店铺实操指南【超详细】
  • 专业的储能电源行业排名靠前的公司
  • 2026年7月最新苏州积家官方售后客户服务电话及线下网点地址 - 积家官方售后服务中心
  • GPT-5.6 使用指南:从 ChatGPT 到 Codex,AI 开发进入 Agent 时代
  • ADS131M02与PIC18LF45K80高精度ADC系统设计指南
  • 微信群消息推送延迟、漏消息?聊聊基于 RPA 接口的队列优化方案
  • 三星2026 AR眼镜深度解析:MicroLED+全息光波导与Android XR空间操作系统
  • Python 3.10+ 类型检查实战:3步根除 ‘list‘ object has no attribute ‘split‘
  • 74LS148/373芯片选型对比:5款经典数字IC在抢答器设计中的功耗与驱动能力实测
  • 2026年AI Agent转行实战指南:从工程化到场景落地的学习路线
  • 富瑶名表珠宝口碑如何 - myqiye
  • 2026年7月最新秦皇岛市抚宁区亨得利官方钟表服务中心电话公示 - 亨得利钟表维修中心
  • 【小白也能轻松玩转龙虾】虾壳云一键部署 OpenClaw v2.7.9,零代码搭建本地自动化智能体(附最新安装包)
  • 2026年7月最新南通宝珀官方售后客户服务电话及线下网点地址 - 宝珀官方售后服务中心
  • 浙江万里学院口碑好吗?2026年万里学院办学实力、培养质量与报考适配全面解析
  • 晶圆厂‘船票’:芯片设计准入的三重硬门槛解析
  • Cadence SPB 24下载地址及安装教程
  • 大数据应用与规划考量
  • 曝光!这5大诚信薄膜拉力试验机排行,你千万不能错过!
  • AI应用开发全链路实战:从代码生成到智能体部署的完整工具链
  • 2026镇江正规漏水检测维修权威推荐-卫生间漏水免砸砖维修/厨房阳台墙面暗管漏水检测/屋顶外墙堵漏维修-防水补漏公司实测口碑榜推荐 - 即刻修防水
  • 2026天津工业冰块测评:哪家口碑与实力双冠? - 热点咨讯
  • 开源vs闭源API:2026下半年企业技术选型
  • 从文字校验到数据逻辑分析,IACheck AI报告审核通审Agent版如何重新定义检测报告智能审核?
  • 2026年7月最新亨得利官方钟表服务中心|官方热线及全部网点地址权威信息通告 - 亨得利官方