2026/10/14 2:08:48

SavvyCAN UDS 扫描窗口实战指南:在 CAN 总线上定位与探测 UDS 节点

SavvyCAN UDS 扫描窗口实战指南:在 CAN 总线上定位与探测 UDS 节点 开发工具逆向工程嵌入式【免费下载链接】SavvyCANQT based cross platform canbus tool项目地址https://gitcode.com/gh_mirrors/sa/SavvyCAN点击查看免费下载UDSUnified Diagnostic Services统一诊断服务是 ISO 14229 定义的车载诊断协议现代车辆的 ECU 大多通过 ISO-TPISO 15765在 CAN 总线上承载 UDS 请求与响应。SavvyCAN 的 UDS 扫描窗口UDS Scan Window是一个专门面向 UDS 的“特殊模糊测试工具”它既可以在一片 CAN 地址区间内搜索符合或近似符合UDS 规范的节点也可以针对已知 UDS 节点批量探测其支持的服务、子功能和数据项。阅读本文后你将掌握如何配置多组扫描任务、逐项理解 Tester Present、Session Control、Security Access、Read By ID 等扫描类型的含义与底层服务码并能结合 SavvyCAN 的源码与脚本接口把扫描能力进一步延伸到自动化诊断分析中。窗口定位一个“特殊”的 CAN 模糊测试器SavvyCAN 中 UDS 扫描窗口本质上属于 CAN 模糊测试fuzzing家族的一员但它的目标高度专一在总线上搜索 UDS 合规或近合规的节点或在已知节点上做服务、子功能与数据项的全量探测。它由主窗口菜单中的 UDS Scanner 动作触发见 mainwindow.cpp 中的showUDSScanWindow()。与通用模糊测试窗口对应文档 fuzzingwindow.md不同UDS 扫描窗口发出的每条报文都被组织成规范的 UDS 服务请求——第一个字节是服务 IDService ID其后是定长的子功能字节最终由底层 ISO-TP 处理器封装成 CAN 帧发送。从源码结构看udsscanwindow.cpp 的setupScan()每条扫描请求都是先构造UDS_MESSAGE再交由UDS_HANDLER::sendUDSFrame()发出与手工逐条发送相比它把“按区间、按步进、按类型”批量生成测试流的过程完全自动化了。UDS 扫描窗口主界面左侧为待执行扫描任务列表右侧为参数配置区与树形结果展示区界面布局与任务列表窗口采用左右分栏布局对应 udsscanwindow.ui左侧 “Scans To Run”以列表形式保存全部扫描任务支持 Add新增、Delete删除、Load / Save载入 / 保存任务组。右侧上半部当前选中任务的各项参数ID 范围、总线、回复偏移、扫描类型、子功能/服务范围等。右侧下半部树形结果视图Results、进度条以及 Run All Scans / Run Selected Scan / Save Scan Results 三个执行按钮。每个扫描任务都由一个ScanEntry结构描述见 udsscanwindow.h其关键字段如下字段含义默认值由addNewScan()设定startID/endID扫描的起始 / 结束 CAN ID0x7E0/0x7E7idOffset期望的回复地址偏移8bAdaptiveOffset是否允许自适应回复偏移falseUI 中复选框默认勾选bShowNoReplies是否在结果中显示无回复项falseUI 中默认勾选busToScan扫描的总线编号0maxWaitTime每条请求的最大等待时间毫秒100scanType扫描类型SCAN_TYPE枚举ST_TESTER_PRESENTsessType测试前切换的会话类型0不切换subfunctLen子功能Subfunction字节数1subfunctLower/subfunctUpper子功能下界 / 上界0/0subfunctIncrement子功能步进1serviceLower/serviceUpper服务字节下界 / 上界Custom 类型使用1/1任务按列表顺序依次执行每个任务可以拥有完全独立的参数。例如任务 A 扫描0x7E0到0x7E7任务 B 只扫0x600一个地址两者互不影响。任务描述会以(0x起始ID - 0x结束ID) 类型缩写的形式实时刷新在列表中缩写对应关系见generateListDesc()如 TP Tester Present、SESS Session Control、RID Read By ID 等。任务组可以保存为*.uds文件版本号为 1Load 时若文件版本不符会弹出警告并拒绝加载见 udsscanwindow.cpp 的loadScans()/saveScans()。总线与回复匹配配置扫描开始前需要设置四个与“怎么发、怎么收”相关的参数Starting ID 与 Ending IDUDS 请求按Starting ID到Ending ID逐地址发送。通常 UDS 合规的 ECU 会响应0x7E0~0x7E7物理请求地址区间因此这也是默认值。但部分车辆在其它 ID 上使用“类 UDS”协议此时可将区间设为目标地址。两个输入框均为 16 进制显示displayIntegerBase16最大值可达536870911即 0x1FFFFFFF。若起始 ID 大于结束 ID对应输入框背景会变红提示无效checkIDRange()中的setControlState()逻辑。Bus to Scan 与 Maximum reply delayBus to scan选择在哪个 CAN 总线上执行扫描。可用总线来自CANConManager运行时枚举udsscanwindow.cpp。Maximum reply delay (ms)每条请求发出后等待回复的最长时间范围 50~10000 ms步进 10 ms默认 100 ms。等待超时后窗口会按“无回复”处理并发送下一条timeOut()槽函数。Reply Offset 与 Allow adaptive reply offsetUDS 节点通常以比请求 ID 大 8 的地址回复因此程序默认将回复偏移Reply Offset设为 8。但存在两类例外部分节点使用偏移 16 的回复地址少数节点的回复地址与请求地址毫无关联。针对这些情况勾选“Allow adaptive reply offset”后无论回复来自哪个地址都会被接受取消勾选则只接受“恰好等于 请求 ID 偏移”的回复。当自适应模式开启时Reply Offset 输入框自动禁用见adaptiveToggled()udsscanwindow.cpp。Reply Offset 的可调范围为 -128~128这为处理“回复地址反而小于请求地址”的少见情况也留出了余地。Show “No Reply”勾选该选项后结果树中会为每个“超时无回复”的探测项生成一条灰色NO REPLY记录不勾选则直接跳过无回复项减少列表干扰。有些 ECU 对不认识的报文会直接忽略是否在结果中保留这些“被忽略”痕迹属于个人偏好。在源码中无回复项通过哨兵 ID0xDEAD5EA1标记再以NO REPLY文本呈现udsscanwindow.cpp。扫描类型详解每个任务只能选择一种扫描类型但可以自由创建任意多个任务组合成一套完整的探测方案。窗口共支持 13 种扫描类型SCAN_TYPE枚举见 udsscanwindow.h按 UI 顺序依次为Tester Present、Session Control、Communication Control、ECU Reset、Clear DTCs、Read DTCs、Security Access、Read By ID、Read By Address、Read Scaling Data By ID、IO Control、Routine Control、Custom UDS。切换类型时窗口会按类型自动启用或禁用对应的参数控件changedScanType()中的分支逻辑。Tester Present0x3E发送 Tester Present 请求子功能 0x00探测目标地址是否“有人”。这是最基础的存活探测正常情况下诊断仪连接期间会周期性地发送 Tester Present 告知 ECU “测试仪仍然在线”。在扫描场景下它最适合用来扫宽泛的地址区间以快速缩小需要深入扫描的地址列表——正如原文档所述该服务几乎必然被支持因此命中率极高。其服务码0x3E与说明 “Tester is present” 可在 uds_handler.h 的UDS_SERVICES枚举及 uds_handler.cpp 的服务描述表中确认。Session Control0x10 / DIAG_CONTROLUDS 定义了若干会话类型Session Type常见的包括normal默认、extended diagnostics扩展诊断、programming编程。Session Control 扫描会逐一尝试这些会话类型并反馈设备实际支持哪些。需要注意即便收到“条件不满足”或“访问被拒绝”的负响应也很可能意味着该模式本身受支持只是需要先完成前置操作——这依然是有价值的信息。窗口实现中会话扫描遍历类型 1~3for (int typ 1; typ 4; typ)对应UDS_DIAG_CTRL_SUB表中的 DFLT_SESS(1)、PROG_SESS(2)、EXT_SESS(3)见 uds_handler.cpp。扫描窗口本身太简单不适合进一步“破解”这类受保护功能——原文档明确建议此时应改用 SavvyCAN 的脚本接口详见下文“脚本扩展”一节。Communication Control0x28发送 Communication Control 请求子功能 2 字节值为0x100总线上的含义为“00 01 启用 Rx/Tx”探测 ECU 对通信控制服务的支持情况。服务描述表中的子功能还包括 COMM_DIS_TX(1)、COMM_DIS_ALL(3) 等但扫描窗口只发送启用类型属于较安全的探测。ECU Reset0x11ECU Reset 扫描尝试各种复位类型并查看哪些受支持。窗口遍历类型 1~3对应UDS_ECU_RESET_SUB表中的 HARD_RESET(1)、KEYOFFON_RESET(2)、SOFT_RESET(3)uds_handler.cpp。注意复位类操作会影响目标 ECU 的运行状态应在理解后果的前提下使用。Clear DTCs0x14与 Read DTCs0x19这两个类型用于故障码DTC管理Clear DTCs发送CLEAR_DIAG服务子功能 3 字节取0xFFFFFF——按源码注释为“clear everything!”即清除全部 DTC 组。Read DTCs发送READ_DTC服务子功能 2 字节取0x02FF——即“按掩码读取 DTC掩码为 FF”。两者均以最激进的参数探测 ECU 是否支持故障码清除 / 读取能力属于“能力确认型”扫描。Security Access0x27设备刚启动并正常运行期间通常不允许执行下载固件、修改参数等危险或敏感操作必须先进入更高的安全级别这一机制即 Security Access。本扫描类型会尝试找出 ECU 支持的安全级别并判断其是否受保护。源码中遍历奇数子功能 1~0x41typ typ 2步进 2for (int typ 1; typ 0x42; ...)因为按 UDS 约定奇数子功能用于请求种子seed偶数子功能用于发送密钥key。原文档对此做了非常务实的提醒大多数情况下高安全级别由 challenge/response挑战/应答机制保护——ECU 下发一串随机字节作为挑战你必须返回正确的应答猜错次数过多会被锁定负响应码0x36 EXCEED_ATTEMPTS、0x37 TIMEDELAY等正是描述此类锁定的错误码见 uds_handler.cpp 的负响应码表。偶尔 C/R 是固定的抓一次合法流量就能永久复用偶尔 C/R 简单到只有几种可能答案而如果 ECU 开发者认真实现了安全机制这个扫描类型只能确认级别存在基本无法真正解锁。因此本类型用于探测哪些安全级别存在以及是否受保护而不是用来破解。值得注意的是UDS_HANDLER::getDetailedMessageAnalysis()对 Security Access 做了精细解码区分 seed 请求/seed 响应/key 发送/解锁成功uds_handler.cpp配合 ISO-TP 解释窗口可深入观察这类交互。Read By ID0x22UDS 允许通过一个 ID 编号读取 ECU 数据。大量 ID 并未在标准中定义属于各 ECU 私有定义因此可以用本类型扫描一段 ID 区间看哪些 ID 有响应。ID 的字节数没有统一标准——可能是 1、2、3 或 4 字节通常为 2 字节。操作方式用Subfunct Len子功能字节数设定 ID 大小可选范围 1~4设置Lower Bound / Upper Bound上下界会随字节数自动扩展到对应上限可选设置Increment步进。步进上限为 10240x400对宽区间扫描很有用。该类型同时启用 “Run test in session type” 选项因为某些数据 ID 仅在特定会话下可读。Read By Address0x23与 Read By ID 类似但改为按内存地址读取。地址长度同样随硬件不同而不同配置方式与 Read By ID 完全一致。源码实现中二者共用一套子功能范围遍历逻辑ST_READ_ID与ST_READ_ADDR分支结构相同仅服务码不同udsscanwindow.cpp。Read Scaling Data By ID0x24读取 ID 对应的缩放/换算数据如传感器量程、分辨率等。源码强制subFuncLen 2并按子功能区间 步进遍历。它比普通 Read By ID 更“温和”常用于确认 ECU 对数据定义类服务的支持。IO Control0x2F发送 IO Control 请求子功能 3 字节高字节为 0即 “Return control to ECU”——将控制权交还给 ECU。窗口遍历各 IO 控制参数区间探测 ECU 支持哪些 IO 控制项。源码注释强调这是相对安全的选择因为并没有真正强制改变 IO 状态。Routine Control0x31发送 Routine Control 请求子功能 3 字节按(subf 8) 3构造——只请求例程结果GET_ROUTINE_RESULTS这是最安全的做法。源码注释明确警告“启动或停止任意例程极度危险除非你非常清楚自己在做什么否则可能发生非常疯狂的事情”udsscanwindow.cpp。因此该类型仅探测“有哪些例程 ID 存在并返回结果”。Custom UDS通配符扫描文档中称 Wildcard这是最具模糊测试色彩的类型对应ST_CUSTOM。你可以同时设定Lower Service / Upper Service服务字节的下界与上界Subfunct Len、Lower/Upper Bound、Increment子功能的字节数与遍历区间。它会以“双重循环”方式把服务字节与子功能逐一组合发送for service ... for subFunc ...全方位扫出 ECU 支持和不支持的内容。如果区间设置不当这一测试可能耗时极长请务必控制范围。Run test in session type部分扫描类型支持“在指定会话类型中执行测试”选项包括No Change不切换、Default、Programming、Extended Diag、Safety Sys Diag见 udsscanwindow.cpp。勾选后程序会在正式测试前先向目标发送一条DIAG_CONTROL0x10请求切换会话setupScan()中sessType 0的分支再执行当前任务。这对“仅在诊断模式或编程模式下才可用”的功能探测很有价值。不过请注意进入编程模式通常需要先提升安全级别而这是本程序不负责的专有过程所以切换可能失败——此时负响应结果同样能提供线索。执行扫描与解读结果点击Run All Scans运行全部任务按列表顺序展开所有请求或Run Selected Scan仅运行当前选中任务即可开始。执行流程如下对应startScan()/sendNextMsg()udsscanwindow.cpp清空结果树启动 UDS 接收setReception(true)、全 ID 处理setProcessAllIDs(true)与流控setFlowCtrl(true)按任务生成UDS_MESSAGE队列sendingFrames进度条以队列长度为上限逐条推进每条请求发出后启动等待定时器收到匹配回复或超时后立即发送下一条。结果以树形结构呈现层次为请求 ID → 回复 ID或无回复→ 服务名称 → 子功能值 → 具体回复内容setupNodes()构建udsscanwindow.cpp。关键判定逻辑在gotUDSReply()中若回复服务为0x40 请求服务判为正响应POSITIVE以深绿色显示并附上全部返回数据字节若msg.isErrorReply为真即收到0x7F负响应帧判为负响应NEGATIVE以深红色显示并直接翻译负响应码的短名称如SERVICE_NOTSUPP、SECURITY_DENIED、RESP_PENDING等对应 uds_handler.cpp 的UDS_NEG_RESPONSE表超时且勾选了 Show “No Reply” 时生成灰色NO REPLY节点。响应匹配的前提是回复地址满足偏移规则id sentFrame.frameId() offset或已勾选自适应偏移。底层判定依赖UDS_HANDLER::tryISOtoUDS()uds_handler.cpp把 ISO-TP 消息解析为UDS_MESSAGE首字节为服务码0x7F开头则标记为错误回复并把真正的服务码取回。扫描完成后点击Save Scan Results可将结果树以缩进文本形式导出为.txt文件dumpNode()递归写出udsscanwindow.cpp。底层原理UDS 请求如何在 CAN 上落地了解底层链路有助于正确解释扫描结果。SavvyCAN 的 UDS 栈分三层UDS_HANDLERbus_protocols/uds_handler.h负责服务码 / 子功能语义。sendUDSFrame()将service与按subFuncLen大端展开的子功能字节拼入数据区再交给 ISO-TP 层uds_handler.cpp。ISOTP_HANDLERbus_protocols/isotp_handler.cpp当单帧数据不足 8 字节时直接以单帧发送数据较长时按 ISO 15765 发送首帧0x10 长度并等待流控帧随后以连续帧0x20 序号完成传输。接收侧同理将分散的 CAN 帧重组为完整 ISO-TP 消息。CANConManager把最终 CAN 帧送上所选总线。这也解释了为什么扫描窗口同时开启了流控Flow Control与全 ID 接收UDS 响应经常是多帧 ISO-TP 消息必须正确处理流控与重组而“全 ID 处理”保证了无论 ECU 用什么地址回复配合自适应偏移都能被捕获。脚本扩展把扫描能力带向自动化原文档明确指出UDS 扫描窗口适合“确认能力”而更深入的交互如完成安全解锁流程应交给脚本接口。SavvyCAN 的脚本容器为 JS 提供了UDSScriptHelperscriptcontainer.cpp可直接调用uds.setFilter(id, mask, bus)设置 UDS 接收过滤器uds.sendUDS(bus, id, service, sublen, subFunc, length, dataBytes)按参数构造并发送 UDS 消息对应UDS_HANDLER::sendUDSFrame的同名流程在 JS 中定义gotUDSMessage(bus, id, service, subFunc, len, data)回调函数即可接收解析后的 UDS 回复。仓库自带的示例脚本 CaptureAllMsgTypes.js 展示了这种用法——它先发送一条 Tester Presentuds.sendUDS(0, 0x604, 0x3E, 1, 0, 0, 0)再通过gotUDSMessage回调打印捕获到的 UDS 总线、ID 与服务码。这正是把“扫描窗口发现的能力”转化为“自定义自动化诊断流程”的推荐路径。小结与安全提醒SavvyCAN 的 UDS 扫描窗口是一个介于“CAN 模糊测试”与“诊断协议分析器”之间的实用工具用 Tester Present 快速圈定节点地址用 Session Control / Security Access / Read By ID 等类型逐项确认 ECU 的能力边界用 Custom UDS 做全量探测最后把结果保存为文本或交给脚本接口做进一步交互。最后重申原文档与源码反复强调的安全边界ECU Reset 会重置目标设备Routine Control 可能触发不可预知的后果Security Access 的暴力试探会触发 ECU 的锁定机制。所有扫描都应在合法授权、可承受风险的测试环境中进行——本工具的价值在于把“探测”做得清晰、可控、可复现而不是替代对协议与目标的审慎判断。赞分享开发工具逆向工程嵌入式【免费下载链接】SavvyCANQT based cross platform canbus tool项目地址https://gitcode.com/gh_mirrors/sa/SavvyCAN点击查看免费下载相关推荐SavvyCAN 脚本接口完全指南用 JavaScript 的 host/can/isotp/uds 对象操控 CAN 总线SavvyCAN 脚本接口完全指南用 JavaScript 的 host/can/isotp/uds 对象操控 CAN 总线 SavvyCAN 内置一套基于开发工具逆向工程嵌入式SavvyCAN 时间窗口Temporal Window实战指南用密度热图定位 CAN 总线高流量时段与活跃 IDSavvyCAN 时间窗口Temporal Window实战指南用密度热图定位 CAN 总线高流量时段与活跃 ID Temporal Window 是 S开发工具逆向工程嵌入式SavvyCAN Fuzzing Window 实战指南用 CAN 总线模糊测试探测报文 ID 与数据位SavvyCAN Fuzzing Window 实战指南用 CAN 总线模糊测试探测报文 ID 与数据位 本篇技术指南以 SavvyCAN 内置的 Fuzzi开发工具逆向工程嵌入式上一篇告别音频混乱Remotion三轨混音术让你的视频声效秒变专业级下一篇Sails.js数据库事务管理确保数据一致性创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考