资讯详情

CAD病毒查杀工具KillCadVirus的设计与实现:从原理到实战

📅 2026/10/29 8:58:52 | 华诺云谱 👁 阅读
CAD病毒查杀工具KillCadVirus的设计与实现:从原理到实战
CAD 图纸打开就卡死、命令行莫名多出一串乱码、每次保存都提示正在写入块定义却永远写不完——如果你在工程制图这行待过一段时间大概率被这类问题折磨过。这类现象背后往往指向同一类东西寄生在图纸文件与 CAD 运行环境里的恶意脚本与异常代码圈内习惯统称为CAD 病毒。它不像传统意义上的可执行程序那样显眼更多时候藏在一个.fas、.lsp、.dvb文件里或者干脆潜伏在图纸的块定义、图层名、线型名当中跟着图纸在同事、供应商、甲方之间来回流转。我这次要聊的KillCadVirus就是一个专门针对这类问题的查杀工具的设计与实现思路——它要解决的核心问题很明确把藏在图纸和 CAD 环境里的异常代码找出来、清干净同时尽量不破坏正常图纸数据。这篇内容适合两类人看一类是被 CAD 病毒反复骚扰、想搞懂原理并自己动手排查的一线设计人员另一类是想了解文件级查杀工具怎么设计、怎么落地的开发者。下面我按设计思路、核心细节、实操实现、问题排查四个层面把整个东西拆开讲透。1. 整体设计与思路拆解1.1 为什么 CAD 病毒难缠先搞清楚敌人长什么样动手设计工具之前得先把查杀对象的特征摸清楚否则做出来的东西要么误杀一片要么漏杀一堆。CAD 病毒和普通恶意程序最大的区别在于它的寄生性和传播载体。普通程序感染系统靠的是可执行文件或启动项而 CAD 病毒的传播链条几乎完全依附于图纸文件本身——一张.dwg从设计院发到施工单位再转发给分包中间经过十几台机器只要有一台机器上的 CAD 环境被污染这张图纸就可能被接种然后继续往下传。从实际样本归纳这类东西常见的藏身位置有这么几类。第一类是CAD 启动加载链通过修改acad.lsp、acaddoc.lsp这类自动加载的 LISP 文件让恶意代码在每次打开图纸时自动执行。第二类是图纸内部数据把恶意内容写进块定义、图层名、线型定义、甚至文字样式名里因为这些东西会随图纸一起保存和传递。第三类是独立脚本文件.fas编译后的 LISP、.vlx打包的 LISP 应用、.dvbVBA 工程这类文件一旦被 CAD 加载就会执行。第四类是注册表与配置项篡改 CAD 的搜索路径、启动项让恶意文件优先被加载。理解了这个分布工具的设计边界就清楚了查杀范围必须覆盖文件系统 图纸内部数据 运行环境配置三个层面只扫文件系统会漏掉图纸内嵌的恶意块只扫图纸会漏掉启动链上的恶意 LSP。这是整个工具架构的第一块基石。1.2 工具定位是杀毒软件还是图纸体检工具这里有个关键的设计取舍要想明白。如果把它定位成通用杀毒软件那就要做实时监控、进程防护、病毒库更新工程量巨大且容易和现有安全软件冲突。我的选择是把它定位成面向 CAD 场景的专项体检与清理工具理由有三。其一CAD 病毒的传播场景高度集中在图纸流转环节用户的核心诉求是我拿到一张图纸打开前先确认它干不干净这是一个离线、按需触发的场景不需要常驻监控。其二CAD 病毒的行为特征相对固定主要集中在文件写入、自动加载、图纸数据篡改这几类用特征匹配 行为规则的组合就能覆盖大部分样本不需要庞大的病毒库。其三专项工具可以做得更懂行——它能识别哪些块名是正常的、哪些图层名是异常的这种领域知识是通用杀毒软件不具备的。所以 KillCadVirus 的整体架构定为扫描引擎 规则库 清理模块 报告输出四部分。扫描引擎负责遍历目标目录和图纸内部结构规则库提供特征与判定逻辑清理模块执行删除、隔离、修复报告输出给用户一份可读的体检结果。这个架构简单、可控、易扩展也方便后续把新发现的样本特征补进规则库。1.3 技术选型为什么用脚本语言做主力工具实现语言的选择上我没有一上来就选编译型语言。原因很实际CAD 生态里大量的恶意代码是 LISP 和 VBA 写的要解析、比对、清理这些内容用一门能方便处理文本、能调用 CAD 自身接口的语言效率最高。最终方案是以 Python 作为主控与扫描逻辑的实现语言配合 CAD 自身的 LISP/AutoLISP 接口做环境侧清理。Python 的优势在于文件遍历、二进制读取、正则匹配、哈希计算这些基础能力开箱即用写规则库和报告生成也快。而环境侧的清理——比如删除被篡改的自动加载项、重置搜索路径——必须通过 CAD 提供的接口来做这部分用 AutoLISP 写一段清理脚本由 Python 调用 CAD 命令行执行是最稳妥的路径。两者分工明确Python 管看和判LISP 管改。提示不要试图用 Python 直接去改 CAD 的注册表配置来达到清理目的不同版本 CAD 的配置项结构差异很大硬改容易把用户环境搞坏。走 CAD 官方接口是唯一可靠的路子。1.4 扫描策略全量扫描还是增量扫描扫描策略上我做了个折中。全量扫描每次把所有文件重新算哈希、重新解析最可靠但慢一个几万张图纸的项目目录扫一遍可能要几十分钟纯增量扫描只扫新增和修改过的文件快但会漏掉文件没变但环境被污染的情况。最终采用的是分层扫描文件系统层用修改时间 大小 哈希做增量判断图纸内部数据层因为解析成本高只对命中可疑特征的文件做深度解析环境配置层则每次全量检查因为配置项数量少、检查快。这个策略背后的逻辑是把计算资源花在刀刃上。文件系统层的增量判断成本极低图纸深度解析成本高但可以靠前置特征过滤大幅减少解析量环境检查成本低且必须全量。三层各用各的策略整体扫描时间能压到可接受范围。2. 核心细节解析与实操要点2.1 特征库怎么建从样本里提炼可判定的规则特征库是整个工具的灵魂建得好不好直接决定查杀效果。我的做法是把特征分成强特征和弱特征两类分开处理。强特征是出现即判定的规则误报率极低。比如某个已知恶意 LISP 文件里固定出现的函数调用组合、某个恶意块名的固定命名模式、某段 VBA 代码里特有的字符串常量。这类特征用哈希或精确字符串匹配就能判定速度快、准确率高。弱特征是出现后需要结合上下文判断的规则比如图纸里存在自动执行类 LISP 代码这件事本身不一定是恶意的正常的设计辅助工具也会这么写需要结合代码内容、文件来源、是否在启动路径等条件综合判断。建库的实操流程是这样的先收集样本把每个样本的可疑片段提取出来人工确认哪些是恶意行为、哪些是正常功能然后把恶意行为的稳定特征抽象成规则。这里有个经验——不要追求一条规则覆盖所有变种宁可多写几条针对性强的规则也不要写一条又宽又模糊的规则。宽规则误杀率高用户一旦被误杀几次就会弃用工具这比漏杀几个变种更致命。特征类型判定方式误报风险适用场景文件哈希精确匹配极低已知样本文件字符串常量精确/正则匹配低恶意脚本固定片段命名模式正则匹配中恶意块名、图层名行为组合多条件与逻辑中自动加载链分析结构异常解析后比对中高图纸内部数据篡改2.2 图纸内部数据怎么读绕开 CAD 直接解析的取舍图纸内部数据的读取是技术难点。有两条路一是调用 CAD 接口打开图纸再遍历对象二是直接解析.dwg二进制格式。前者准确但慢且依赖 CAD 环境后者快且不依赖环境但.dwg格式封闭、版本众多解析难度大。我的选择是混合方案对.dxf这种文本格式直接解析DXF 是明文结构读起来不难对.dwg则优先用 CAD 接口遍历同时保留一个轻量的二进制特征扫描通道——不追求完整解析只做在二进制流里找可疑字符串和结构标记的粗筛。粗筛命中后再走 CAD 接口做精确确认。这个取舍的理由是完整解析.dwg的投入产出比太低格式版本多、结构复杂维护成本高而粗筛已经能拦住大部分把恶意内容直接写进图纸的情况剩下的靠 CAD 接口兜底。实际测试下来粗筛能覆盖八成以上的图纸内嵌恶意内容剩下的两成交给精确通道整体效率和准确率都够用。注意二进制粗筛一定要做编码兼容处理。图纸里的字符串可能是 ANSI、UTF-8、UTF-16 多种编码只按一种编码匹配会漏。我的做法是把可疑特征同时按几种常见编码生成匹配模式逐一比对。2.3 启动链检查最容易被忽视的高危区很多人查 CAD 病毒只盯着图纸文件忽略了启动链结果图纸清干净了一开 CAD 又被重新感染。启动链检查必须覆盖这几个位置CAD 安装目录下的自动加载 LISP 文件、用户配置目录下的启动脚本、CAD 搜索路径里是否存在可疑目录、以及注册表里与自动加载相关的配置项。检查逻辑是先枚举所有会被 CAD 自动加载的文件路径逐个检查文件内容是否命中恶意特征再检查搜索路径列表看有没有指向临时目录、用户可写目录这类高风险位置的条目最后检查配置项看自动加载开关是否被异常打开。这三步里搜索路径检查最容易被忽略但价值很高——很多恶意文件就是靠把自己所在目录塞进搜索路径然后利用 CAD 的加载顺序优先执行。实操中我遇到过一个典型情况某台机器的 CAD 每次启动都会加载一个名字很像官方组件的 LISP 文件文件本身内容看起来也正常但它所在的目录是一个用户临时目录。这种就是典型的路径劫持——文件名伪装 路径优先。单看文件内容查不出来必须结合路径位置判断。2.4 清理策略删除、隔离还是修复发现威胁之后怎么处理是个需要谨慎设计的问题。直接删除最省事但风险在于可能误删正常文件或者删掉了用户图纸里正常引用的内容。我的清理模块设计了三级处理隔离、修复、删除。隔离是把可疑文件移动到专门的隔离目录并记录原始路径用户确认无误后可一键恢复这是默认动作。修复是针对文件本身正常但被篡改的情况比如某个正常 LISP 文件被追加了恶意代码修复就是把追加部分去掉、恢复原内容。删除只用于确认无疑的恶意文件且需要用户二次确认。这个分级设计的核心考虑是可逆性。查杀工具最怕的就是杀错了还救不回来隔离和修复都保留了回退路径用户有后悔药吃。实际使用中隔离区还成了样本收集的好地方——用户把隔离的文件反馈回来就能不断补充特征库。3. 实操过程与核心环节实现3.1 环境准备与依赖搭建先把运行环境搭起来。工具主控用 Python 3.8 以上版本依赖尽量少核心只用标准库可选依赖装一个regex库用于更灵活的正则匹配标准库re在复杂模式下性能一般。CAD 侧需要一个能执行 AutoLISP 的 CAD 环境版本不限但建议用较新的版本以保证接口稳定。目录结构建议这样组织scanner/放扫描引擎rules/放特征库用 JSON 或 YAML 存规则方便非开发者维护cleaner/放清理模块reports/放输出报告quarantine/放隔离文件。规则库和代码分离是关键设计这样新增特征不用改代码改配置文件就行。# 目录初始化示例 mkdir -p killcadvirus/{scanner,rules,cleaner,reports,quarantine}依赖安装很简单核心功能零第三方依赖需要增强正则时再装pip install regex3.2 文件系统扫描的实现文件系统扫描的核心是遍历 过滤 判定。遍历用os.walk但要加几个过滤条件跳过系统目录、跳过超大文件比如超过 500MB 的文件直接跳过避免卡死、按扩展名过滤只处理 CAD 相关文件类型。判定环节先算文件哈希查强特征库未命中再读内容做字符串匹配。import os import hashlib CAD_EXTS {.dwg, .dxf, .lsp, .fas, .vlx, .dvb, .scr, .arx} MAX_SIZE 500 * 1024 * 1024 # 500MB 上限 def iter_cad_files(root): for dirpath, dirnames, filenames in os.walk(root): # 跳过明显的系统与缓存目录 dirnames[:] [d for d in dirnames if d.lower() not in {windows, system32, $recycle.bin, temp}] for name in filenames: ext os.path.splitext(name)[1].lower() if ext not in CAD_EXTS: continue full os.path.join(dirpath, name) try: if os.path.getsize(full) MAX_SIZE: continue except OSError: continue yield full def file_hash(path): h hashlib.sha256() with open(path, rb) as f: for chunk in iter(lambda: f.read(65536), b): h.update(chunk) return h.hexdigest()这段代码里有两个细节值得说。一是dirnames[:]的原地修改这是os.walk剪枝的标准写法比遍历完再过滤效率高得多。二是分块读文件算哈希避免大文件一次性读进内存。哈希用 SHA-256 而不是 MD5虽然 MD5 更快但 SHA-256 抗碰撞性更好特征库长期维护更放心。3.3 图纸内部数据的粗筛实现图纸粗筛的思路是把文件按二进制读进来用多编码生成的可疑特征模式去匹配。这里的关键是编码兼容和匹配效率。编码兼容前面提过把特征字符串分别按 GBK、UTF-8、UTF-16LE 编码成字节序列再匹配匹配效率上用bytes.find做子串查找比正则快得多先用find粗筛命中后再用正则做精确判定。ENCODINGS (gbk, utf-8, utf-16-le) def build_patterns(keywords): patterns [] for kw in keywords: for enc in ENCODINGS: try: patterns.append(kw.encode(enc)) except UnicodeEncodeError: continue return patterns def scan_binary(path, patterns): hits [] with open(path, rb) as f: data f.read() for pat in patterns: idx data.find(pat) if idx ! -1: hits.append((pat, idx)) return hits实测下来这个粗筛对恶意字符串直接写进图纸的情况命中率很高而且速度够快——一个 20MB 的图纸文件几十个特征模式扫一遍通常在百毫秒级。需要注意的是find只返回第一个命中位置如果要统计命中次数或提取上下文得循环查找。3.4 启动链检查的实现启动链检查分两步枚举自动加载路径检查路径与内容。枚举部分要覆盖 CAD 安装目录、用户配置目录、以及 CAD 搜索路径。这里不能硬编码路径因为不同版本、不同安装方式的路径都不一样正确做法是从 CAD 环境里读取——通过 AutoLISP 的(findfile acad.lsp)这类调用拿到实际加载路径再交给 Python 检查。;; 获取关键自动加载文件的实际路径 (defun get-autoload-paths () (list (findfile acad.lsp) (findfile acaddoc.lsp) (findfile acad.mnl) ) )Python 侧调用这段 LISP 拿到路径列表后逐个检查文件是否存在、内容是否命中特征、所在目录是否在搜索路径的高风险位置。搜索路径的高风险判定规则是路径包含临时目录关键字、路径位于用户可写目录且不在 CAD 安装目录下、路径指向网络共享位置。这三类位置是恶意文件最常藏身的地方。3.5 清理模块的实现与安全边界清理模块的入口函数接收威胁列表每条威胁带类型、路径、处理建议。处理逻辑按三级策略走可修复的走修复不可修复但需保留证据的走隔离确认恶意的走删除。隔离实现就是移动文件到隔离目录并写一条记录到 JSON 日志记录原始路径、哈希、发现时间、威胁类型。import json import shutil import time def quarantine(path, threat_type, qdirquarantine): os.makedirs(qdir, exist_okTrue) base os.path.basename(path) stamp time.strftime(%Y%m%d%H%M%S) dest os.path.join(qdir, f{stamp}_{base}) shutil.move(path, dest) record { original: os.path.abspath(path), quarantined: os.path.abspath(dest), type: threat_type, time: stamp, } with open(os.path.join(qdir, log.jsonl), a, encodingutf-8) as f: f.write(json.dumps(record, ensure_asciiFalse) \n) return dest安全边界上有几条硬规矩必须守住。第一清理前必须备份哪怕是删除操作也要先把文件复制到隔离区再删原文件。第二清理操作要可回滚隔离日志就是回滚依据。第三批量清理要限速并二次确认避免用户误点导致大面积误删。第四不碰系统关键文件如果威胁路径落在系统目录只报告不清理交给用户判断。提示隔离目录不要放在被扫描的目录树内部否则下次扫描会把隔离区里的恶意文件又扫出来形成自己查自己的循环。3.6 报告输出让非技术用户也能看懂报告是工具和用户沟通的窗口设计得好不好直接影响用户信任度。我的报告分三部分概览扫了多少文件、发现多少威胁、各类型分布、明细每条威胁的路径、类型、处理建议、处理结果、建议针对发现的问题给出环境加固建议。概览用表格明细用列表建议用短句。报告格式上同时输出纯文本和 JSON 两种。纯文本给人看JSON 给后续自动化流程用比如集成到图纸接收流程里做自动体检。这个双格式设计在实际使用中很实用——设计人员看文本报告运维人员拿 JSON 做批量统计。4. 常见问题与排查技巧实录4.1 扫描结果误报太多怎么办误报是查杀工具的头号敌人。我踩过的坑里误报主要来自三个源头弱特征规则太宽、编码匹配把正常内容误判、路径规则把正常工具目录当成高风险。排查思路是逐条规则做误报率统计——拿一批确认干净的图纸和文件跑一遍看哪条规则命中了干净文件命中的就是误报源。处理办法分两种。规则太宽的收紧条件比如把存在自动执行代码改成存在自动执行代码且代码中包含文件写入操作。编码误判的检查是不是把某个常见字符串的某种编码形式当成了特征这种情况要么换更长的特征串要么加白名单。路径误判的把正常工具目录加进白名单。误报现象可能原因处理方式正常图纸被标记弱特征过宽收紧规则条件正常 LISP 被标记特征串太短加长特征串或加白名单工具目录被标记路径规则误判补充白名单目录编码乱码误判多编码匹配过泛限定编码范围4.2 扫描大目录时卡死或内存暴涨这个问题在扫描大型项目目录时很常见。根因通常是两个一次性把大文件读进内存或者递归遍历时把整个文件列表攒在内存里。解决办法是流式处理——文件内容分块读、分块匹配遍历结果用生成器逐条产出而不是攒成列表。还有一个隐蔽的坑是符号链接循环。如果目录里有指向父目录的符号链接os.walk默认不跟随符号链接所以问题不大但如果自己写了递归遍历逻辑又没做循环检测就会无限递归。稳妥做法是用os.walk并显式设置followlinksFalse。4.3 清理后病毒又回来了这是最让人抓狂的情况说明清理不彻底。排查顺序是先查启动链是否清干净自动加载文件、搜索路径、配置项再查是否有其他机器在持续投毒比如共享目录里的图纸被反复感染最后查是否有常驻进程在监控并恢复恶意文件。实操中遇到过一个典型案例某台机器清理完重启 CAD 又中招最后发现是 CAD 的某个模板文件.dwt被感染了每次新建图纸都会从模板继承恶意内容。这类模板级感染很容易被忽略因为大家习惯只查.dwg不查.dwt。所以工具的扫描范围一定要把模板文件、样板文件都包含进去。4.4 不同 CAD 版本兼容性怎么处理CAD 版本差异带来的兼容问题主要集中在两处文件格式版本不同老版本.dwg和新版本结构有差异、接口行为不同某些 LISP 函数在新版本里行为变了。处理原则是向下兼容、向上适配——核心扫描逻辑不依赖具体版本只做通用的二进制和文本处理依赖 CAD 接口的部分做版本探测不同版本走不同的调用分支。版本探测可以通过读取 CAD 的版本号接口实现拿到主版本号后选择对应的处理逻辑。对于实在无法兼容的老版本工具降级为只做文件系统扫描 报告不做环境侧清理并在报告里明确提示用户手动处理。4.5 常见问题速查表问题排查方向解决要点扫描无结果但确实有问题特征库是否覆盖该样本补充特征、更新规则库扫描极慢是否全量深度解析启用分层扫描、加前置过滤清理后复发启动链/模板/共享源全面检查启动链与模板文件隔离文件找不回隔离日志是否完整检查 log.jsonl 记录报告乱码编码处理统一用 UTF-8 输出4.6 几条踩坑换来的实操心得第一条心得先做只读扫描再做清理。工具刚上线时我图省事让扫描和清理一步到位结果一次误判把用户一批正常文件隔离了虽然能恢复但用户信任度大打折扣。后来改成默认只读扫描、清理必须用户显式确认体验好很多。第二条心得特征库要版本化。规则改来改去没有版本记录的话出了问题根本不知道是哪次改动引入的。给规则库加个版本号和变更日志排查问题时能省大量时间。第三条心得隔离区定期清理。隔离区会越攒越大尤其是误报多的时候。加一个按时间或大小的自动清理策略避免占满磁盘。第四条心得别忽视用户反馈的样本。用户隔离出来的文件是最好的样本来源定期收集分析特征库的覆盖面会越来越广。这个闭环建起来之后工具的实战效果提升非常明显。这套东西做下来我最深的体会是CAD 病毒查杀的核心不在杀而在判——把正常和异常分清楚比把异常删掉难得多。规则宁可保守一点、多留回退路径也不要为了追求查杀率把用户正常图纸误伤。工具的价值在于让用户敢放心打开每一张流转过来的图纸而不是制造新的麻烦。后续如果继续扩展我倾向于把重点放在图纸内部结构的精确解析和跨版本兼容上这两块做扎实了工具的适用面还能再上一个台阶。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑