车载网络测试实操源码_使用CAPL脚本实现安全访问解锁,并模拟各种测试场景

news2024/11/25 9:48:11

系列文章目录

使用CAPL脚本解析hex、S19、vbf文件
使用CAPL脚本对CAN报文的Counter、CRC、周期、错误帧进行实时监控
使用CAPL脚本模拟发送符合协议要求(Counter和CRC)的CAN报文
使用CAPL脚本控制继电器实现CAN线、电源线的通断
使用CAPL脚本实现安全访问解锁
使用CAPL脚本实现BUS OFF干扰测试
使用CAPL脚本进行DTC自动化测试
使用CAPL脚本进行UDS刷写及其自动化测试
使用CAPL脚本进行UDS协议测试
使用CAPL脚本进行网络管理测试
粉丝问题解答系列文章… …
其他持续更新中… …


文章目录

  • 系列文章目录
  • 前言
  • 一、利用安全算法.dll文件解锁
    • 1、函数介绍
    • 2、示例代码
  • 二、利用自定义安全算法解锁
    • 1、示例代码
  • 三、测试场景示例
    • 1、正常的安全访问解锁
    • 2、安全访问秘钥错误
    • 3、连续多次申请解锁,验证种子随机性
  • 总结


前言

在车载诊断中,我们经常会使用到安全访问解锁,常见的场景包括软件刷写前、通过2E服务写入某个DID前、通过31服务执行某个例程前等,都必须先通过27服务进行安全访问,解锁相应安全等级后才能执行上述操作。
在开发调试或测试时,有时候需要模拟正常的安全解锁、安全访问秘钥错误、连续多次请求安全访问等,手动操作极为不便。本文将介绍如何使用CAPL脚本实现安全访问解锁,并模拟各种测试场景。


一、利用安全算法.dll文件解锁

1、函数介绍

diagGenerateKeyFromSeed函数适用于利用CANoe工程中配置的安全算法动态链接库文件(.dll)生成秘钥的场景。详细说明如下:
在这里插入图片描述

2、示例代码

利用CANoe工程中配置的安全算法动态链接库文件(.dll)进行安全访问解锁。CAPL脚本示例如下:

variables
{
	byte glob_SeedArray[10];
}
//发送报文,并获取响应状态
long diagSendReqAndgetResp(diagRequest * req)
{
  long NRC;
  diagSendRequest(req);
  testWaitForDiagRequestSent(req, 200);
  
  if(1 == TestWaitForDiagResponse(req,5000))
  {
    NRC = diagGetLastResponseCode(req);
    if(NRC == -1)//积极响应
    {
      return 1;
    }
    else//消极响应 
    {
      TestReportWriteDiagResponse(req); 
      return NRC;
    }
  }

  testStep("step","响应超时");
  return -1;//响应超时
}
//安全访问解锁函数:level——解锁的安全等级;errorMode——是否要模拟密钥错误;SeedArray_only——是否只申请种子,不进行解锁
long diagUnlockECU(byte level,byte errorMode,byte SeedArray_only)
{
  long Resp;
  byte  gSeedArray[20];
  int   gSeedArraySize,gSecurityLevel;                    
  char  gVariant[50]     = "Base_Variant";
  char  gOption[50]      = "option";
  byte gKeyArray[20];
  int  gMaxKeyArraySize = 255;
  dword gActualSizeOut;
  long errorcode,size;
  diagRequest  * req;
  diagResponse * resp;
  byte reqBuffer[20];
  
  req.Resize(2);
  diagSetPrimitiveByte(req,0,0x27);
  diagSetPrimitiveByte(req,1,level);
  gSecurityLevel = level;
  
  testStep("","安全访问申请Seed");
  /*请求Seed*/
  Resp = diagSendReqAndgetResp(req);
  if( Resp == 1 )
  {
    /*将响应Seed写入参数数组*/
    diagGetLastResponse(req, resp);
    
    /*获取Seed&Key的长度*/
    size = resp.GetPrimitiveSize();
    gSeedArraySize = size - 2;
    gActualSizeOut = size - 2;
    write("Seed&Key size:%d",size);
    
    resp.GetPrimitiveData(gSeedArray, elcount(gSeedArray));
    memcpy_off( gSeedArray,0,gSeedArray,2, gSeedArraySize);
    if( SeedArray_only == 1 )
    {
      memcpy( glob_SeedArray,gSeedArray, gSeedArraySize);//仅申请安全访问种子
      return 0xFF;
    }
    
    /*计算密钥(使用安全算法动态链接库文件(.dll)生成密钥)*/
    errorcode = diagGenerateKeyFromSeed(gSeedArray, gSeedArraySize, gSecurityLevel, gVariant, gOption, gKeyArray, gMaxKeyArraySize, gActualSizeOut);
    if( 0 == errorcode)
    {
      /*写入请求参数*/
      req.Resize(gActualSizeOut + 2);
      reqBuffer[0] = 0x27;
      reqBuffer[1] = level+1;
      memcpy_off(reqBuffer,2,gKeyArray,0,gActualSizeOut);
      if(errorMode == 1)
      {
        reqBuffer[4] += 1;//模拟制造密钥错误
      }
      req.SetPrimitiveData(reqBuffer,size);
      /*发送密钥*/
      testStep("","发送密钥进行解锁");
      Resp = diagSendReqAndgetResp(req);
      return Resp;
    }
    else
    {
      TestStepFail("","密钥计算出错,errorcode:%x",errorcode);
      return errorcode;
    }
  }
  else
  {
    return Resp;
  }
}

二、利用自定义安全算法解锁

前面说了diagGenerateKeyFromSeed函数是利用CANoe工程中配置的安全算法动态链接库文件(.dll)生成的秘钥,使用起来很方便,不需要再编写生成生成密钥的算法函数。但是有些测试场景可能不是很方便,比如利用自动化脚本分别进入扩展会话和编程会话进行解锁,一般来说扩展会话和编程会话模式下的密钥算法是不一样的,不能通过同一个动态链接库文件(.dll)生成秘钥。这个时候可以自定义一个密钥生成函数来替换diagGenerateKeyFromSeed函数即可。

1、示例代码

利用自定义的安全算法函数进行安全访问解锁。CAPL脚本示例如下:

variables
{
	byte glob_SeedArray[10];
}
//发送报文,并获取响应状态
long diagSendReqAndgetResp(diagRequest * req)
{
  long NRC;
  diagSendRequest(req);
  testWaitForDiagRequestSent(req, 200);
  
  if(1 == TestWaitForDiagResponse(req,5000))
  {
    NRC = diagGetLastResponseCode(req);
    if(NRC == -1)//积极响应
    {
      return 1;
    }
    else//消极响应 
    {
      TestReportWriteDiagResponse(req); 
      return NRC;
    }
  }

  testStep("step","响应超时");
  return -1;//响应超时
}
//根据种子计算密钥(示例)
dword SeedToKey1(byte pData[],byte level)
{
	dword seed = 0;
	dword ret = 0;
	dword PositionA,PositionB,PositionC;
	dword B24 = 0;
	int i;
	qword ChallengeBitArray;
	
	ret = 0;
	PositionA = 0x123456;//示例
	PositionB = 0;
	PositionC = 0;
	B24 = 0;
	if(level == 9)
	{
	  ChallengeBitArray = 0x1122334455LL;//编程会话模式示例
	}
	else if(level == 1)
	{
	  ChallengeBitArray = 0x123456789ALL;//扩展会话模式示例
	}

	seed = (pData[2] << 16) | (pData[1] << 8) | (pData[0]);
	
	ChallengeBitArray = ((ChallengeBitArray << 24) | seed);

	for (i = 0; i < 64; i++)
	{
		B24 = ((ChallengeBitArray >> i) ^ PositionA) & 0x01;
		PositionB = (B24 << 23) | (PositionA >> 1);
		PositionC = 0;
		PositionC = PositionC | (B24 << 3);
		PositionC = PositionC | (B24 << 5);
		PositionC = PositionC | (B24 << 12);
		PositionC = PositionC | (B24 << 15);
		PositionC = PositionC | (B24 << 20);
		PositionC = PositionC ^ PositionB;
		PositionA = PositionC;
	}
	ret = ret | ((PositionC & 0x000FF0) << 12);
	ret = ret | ((PositionC & 0x00F000));
	ret = ret | ((PositionC & 0xF00000) >> 12);
	ret = ret | ((PositionC & 0x00000F) << 4);
	ret = ret | ((PositionC & 0x0F0000) >> 16);
	return ret;
}
//自定义的密钥生成函数(示例)
byte GenerateKeyEx(byte iSeedArray[],dword iSeedArraySize,dword iSecurityLevel,char iVariant[],byte ioKeyArray[],dword iKeyArraySize,dword& oSize)
{
	dword nRet = 0;
	
	if (iSeedArraySize>iKeyArraySize)
	  return 1;

	switch (iSecurityLevel)
	{
	  case 9://编程会话模式
	  	nRet = SeedToKey1(iSeedArray,9);
	  	break;
	
	  case 1://扩展会话模式
	  	nRet = SeedToKey1(iSeedArray,1);
	  	break;
	  default:
	  	break;
	}
	ioKeyArray[0] = nRet >> 16;
	ioKeyArray[1] = (nRet >> 8) & 0xFF;
	ioKeyArray[2] = nRet & 0xFF;
	oSize = 3;
	return 0;
}
//安全访问解锁函数:level——解锁的安全等级;errorMode——是否要模拟密钥错误;SeedArray_only——是否只申请种子,不进行解锁
long diagUnlockECU(byte level,byte errorMode,byte SeedArray_only)
{
  long Resp;
  byte  gSeedArray[20];
  int   gSeedArraySize,gSecurityLevel;                    
  char  gVariant[50]     = "Base_Variant";
  char  gOption[50]      = "option";
  byte gKeyArray[20];
  int  gMaxKeyArraySize = 255;
  dword gActualSizeOut;
  long errorcode,size;
  diagRequest  * req;
  diagResponse * resp;
  byte reqBuffer[20];
  
  req.Resize(2);
  diagSetPrimitiveByte(req,0,0x27);
  diagSetPrimitiveByte(req,1,level);
  gSecurityLevel = level;
  
  testStep("","安全访问申请Seed");
  /*请求Seed*/
  Resp = diagSendReqAndgetResp(req);
  if( Resp == 1 )
  {
    /*将响应Seed写入参数数组*/
    diagGetLastResponse(req, resp);
    
    /*获取Seed&Key的长度*/
    size = resp.GetPrimitiveSize();
    gSeedArraySize = size - 2;
    gActualSizeOut = size - 2;
    write("Seed&Key size:%d",size);
    
    resp.GetPrimitiveData(gSeedArray, elcount(gSeedArray));
    memcpy_off( gSeedArray,0,gSeedArray,2, gSeedArraySize);
    if( SeedArray_only == 1 )
    {
      memcpy( glob_SeedArray,gSeedArray, gSeedArraySize);//仅申请安全访问种子
      return 0xFF;
    }
    
    /*计算密钥(使用自定义的密钥生成函数)*/
    errorcode = GenerateKeyEx(gSeedArray, gSeedArraySize, gSecurityLevel, gVariant, gKeyArray, gMaxKeyArraySize, gActualSizeOut);
    if( 0 == errorcode)
    {
      /*写入请求参数*/
      req.Resize(gActualSizeOut + 2);
      reqBuffer[0] = 0x27;
      reqBuffer[1] = level+1;
      memcpy_off(reqBuffer,2,gKeyArray,0,gActualSizeOut);
      if(errorMode == 1)
      {
        reqBuffer[4] += 1;//模拟制造密钥错误
      }
      req.SetPrimitiveData(reqBuffer,size);
      /*发送密钥*/
      testStep("","发送密钥进行解锁");
      Resp = diagSendReqAndgetResp(req);
      return Resp;
    }
    else
    {
      TestStepFail("","密钥计算出错,errorcode:%x",errorcode);
      return errorcode;
    }
  }
  else
  {
    return Resp;
  }
}

三、测试场景示例

1、正常的安全访问解锁

	long Resp;
	/*正常解锁ECU:Level_1,正常解锁,申请种子+发送正确的密钥*/
	Resp = diagUnlockECU(0x01,0,0);
	if(Resp == 1)
	{
	  testStepPass("正确解锁ECU Level_1,测试成功!");
	}
	else
	{
	  testStepFail("正确解锁ECU Level_1,测试失败!");
	}

2、安全访问秘钥错误

	long Resp;
	/*异常解锁ECU:Level_1,异常解锁,申请种子+发送错误的密钥*/
	Resp = diagUnlockECU(0x01,1,0);
	if(Resp == 0x35)
	{
	  testStepPass("第1次错误解锁ECU Level_1,测试成功!");
	}
	else
	{
	  testStepFail("第1次错误解锁ECU Level_1,测试失败!");
	}

上述示例是密钥错误1次,返回NRC35。可以以此进行扩展,比如错误N-1次,返回NRC36,以及错误达到N次锁定后,返回NRC37。

3、连续多次申请解锁,验证种子随机性

	long Resp;
	byte tmp_SeedArray[6000];
	int i,j,k,cmp_cnt;
	/*Level_1,仅申请种子*/
	for(i = 0;i < 2000;i++)
	{
	  /*27 01申请安全访问种子,1~1000次*/
	  Resp = diagUnlockECU(0x01,0,1);
	  if(Resp == 0xFF)
	  {
	    if((glob_SeedArray[0] == 0) && (glob_SeedArray[1] == 0) && (glob_SeedArray[2] == 0))
	    {
	      testStepFail("","27 01申请安全访问种子,第%d次:获取失败!种子全为0!",i+1);
	    }
	    else
	    {
	      memcpy_off(tmp_SeedArray,3*k,glob_SeedArray,0, 3);
	      testStepPass("","27 01申请安全访问种子,第%d次:获取成功!",i+1);
	    }
	  }
	  else
	  {
	    testStepFail("","27 01申请安全访问种子,第%d次:获取失败!",i+1);
	  }
	}

	for(k = 0;k < 2000;k++)
	{
	  for(i = 0;i < 2000;i++)
	  {
	    cmp_cnt = 0;
	    if(i == k){continue;}
	    for(j = 0;j < 3;j++)
	    {
	      if(tmp_SeedArray[3*k+j] == tmp_SeedArray[3*i+j])
	      {
	        cmp_cnt +=1;
	      }
	    }
	    if( cmp_cnt == 3 )
	    {
	      testStepFail("","27 01申请安全访问种子,第%d次与第%d次种子完全相同:测试失败!",k+1,i+1);
	      break;
	    }
	  }
	  if( cmp_cnt < 3 )
	  {
	    testStepPass("","27 01申请安全访问种子,第%d次与之前的种子不相同:测试成功!",k+1);
	  }
	}

上述示例是连续申请2000次种子(此处示例的种子为3个字节),存放到tmp_SeedArray[]数组中,最后循环遍历对比,看这申请的这2000次是否有相同种子,以此来验证其随机性。


总结

以上就是如何使用CAPL脚本实现安全访问解锁的讲解,并结合了多个运用场景的实例进行介绍,希望对大家有所帮助。各位可根据本文的示例,结合自己的需求,进行完善和二次开发。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2164744.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA忽略node_modules减少内存消耗,提升索引速度

IDEA忽略node_modules减少内存消耗&#xff0c;提升索引速度 简介 在后端开发中&#xff0c;IDEA 在运行前端代码时&#xff0c;频繁扫描 node_modules 文件夹会导致高内存消耗和慢索引速度&#xff0c;甚至可能会导致软件卡死。为了改善这一问题&#xff0c;可以按照以下步骤…

STM32基础学习笔记-USART外设面试基础题3

第三章、USART 常见问题 通讯定义 &#xff1f;通讯分类 &#xff08;有无时钟源、通讯方式、传输方向&#xff09;&#xff1f; 波特率的本质&#xff1a;隐形的时钟 电平转换 &#xff1f;TTL、COMS、RS232、RS485、USB电平标准 &#xff1f; STM32串口与RS-232通讯 &…

《热血江湖》v23巅峰对决游戏程序(真端+最新官方版本)

《热血江湖》v23巅峰对决游戏程序&#xff08;真端最新官方版本&#xff09; 下载地址&#xff1a; 通过网盘分享的文件&#xff1a;【游戏】《热血江湖》v23巅峰对决游戏程序&#xff08;真端最新官方版本&#xff09; 链接: https://pan.baidu.com/s/18svlGuFnPM9ccwEAb7oBMw…

肺结节检测-目标检测数据集(包括VOC格式、YOLO格式)

肺结节检测-目标检测数据集&#xff08;包括VOC格式、YOLO格式&#xff09; 数据集&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1hz3Y6WgWoS3Gubsw_83WnA?pwdaon1 提取码&#xff1a;aon1 数据集信息介绍&#xff1a; 共有 1180 张图像和一一对应的标注文件 标注…

输出不能被3整除的数-C语言

1.问题&#xff1a; 统计0~100之间的不能被3整除的数。 2.解答&#xff1a; 对100-200之间的每一个数进行遍历&#xff0c;如果不能被3整除&#xff0c;就将此数输出&#xff0c;若能被3整除&#xff0c;就不输出此数。 3.代码&#xff1a; #include<stdio.h>//头文件…

图像超补全(Outpainting)技术的前沿模型与数据集资源汇总

“lmage outpainting”这一概念是由斯坦福大学 CS230 课程的 Mark Sabini 等人提出&#xff0c;相较于图像修复技术&#xff0c;lmage outpainting 更进一步&#xff0c;能够从给定的图像片段中“补全”出缺失的外延部分&#xff0c;以精妙的方式补全画面&#xff0c;从而构建出…

基于实验的低光照图像增强方法综述

这篇论文的标题是《An Experiment-Based Review of Low-Light Image Enhancement Methods》&#xff0c;作者包括Wencheng Wang、Xiaojin Wu、Xiaohui Yuan 和 Zairui Gao。论文主要回顾了过去几十年中发展的低光照图像增强技术的主要技术。 以下是论文的详细内容概述&#xf…

Flutter中很有意思的Future

最近在使用Flutter来开发完全免费开放的E6开发平台对应的手机app客户端。 由于Flutter基于Dart语言开发&#xff0c;而Dart具有一个非常有趣的特性&#xff1a;Future&#xff0c;如果一个方法如果被定义为Future&#xff0c;意味着它可以被异步调用&#xff0c;这一特性对页面…

用于多模态MRI重建的具有空间对齐的深度展开网络|文献速递--基于多模态-半监督深度学习的病理学诊断与病灶分割

Title 题目 Deep unfolding network with spatial alignment for multi-modal MRI reconstruction 用于多模态MRI重建的具有空间对齐的深度展开网络 01 文献速递介绍 磁共振成像&#xff08;MRI&#xff09;因其无创性、高分辨率和显著的软组织对比度&#xff0c;已成为广…

算法记录——链表

2.链表 2.1判断是否是回文链表 1.方法一&#xff1a;利用栈反转链表 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode…

IO(输入输出流)

1.IO a.介绍 i.IO是指Input和Output&#xff0c;即输入和输出&#xff0c;以内存为中心&#xff1a; 1.Input是指从外部读入数据到内存。 2.Output是指把数据从内存输出到外部。 ii.IO流是一种顺序读写数据的模式&#xff0c;它的特点是单向流动。数据类似自…

【AIGC】ChatGPT提示词解析:如何生成爆款标题、节日热点文案与完美文字排版

博客主页&#xff1a; [小ᶻZ࿆] 本文专栏: AIGC | ChatGPT 文章目录 &#x1f4af;前言&#x1f4af;情绪化的吸睛爆款标题提示词使用方法 &#x1f4af;紧跟节日热点选题文案提示词使用方法 &#x1f4af;高效文字排版技巧提示词使用方法 &#x1f4af;小结 &#x1f4af…

python-获取浏览器静态/动态素材

f12浏览器中 1&#xff1a;静态爬取 2.动态资源图片获取。斗鱼 3获取视频-抖音 一长串&#xff0c;最后一个http就是视频

Unity-物理系统-碰撞检测-物理材质

物理材质的作用&#xff1a;改变碰撞效果 因为碰撞的过程是相互的&#xff0c;所以在碰撞双方都要加相同的物理材质才能实现效果 物理材质创建 参数

【FPGA】IO电平标准

【FPGA】IO 电平标准 1 LVCMOS&#xff08;低压CMOS&#xff09;1.1 TTL、CMOS、LVTTL、LVCMOS逻辑电平定义1.2 ZYNQ-7000 PS、PL IO Level示例 2 LVTTL&#xff08;低压TTL&#xff09;3 HSTL&#xff08;高速TTL&#xff09;4 SSTL&#xff08;高速&#xff09;5 LVDS&#x…

Pytest测试实战|Conftest.py详解

Pytest测试实战 本文章主要详细地阐述下Pytest测试框架中Conftest.py特性。 Conftest.py实战 Fixture强大的特性在实际的工作中是非常有价值并且是实用的&#xff0c;这样可以根据需求&#xff0c;在对应的测试模块中编写Fixture函数来达到测试需求的目的。但是这样也产生了…

【面经合集】Java基础精选面试题(三)

最近&#xff0c;小编整理了不少Java领域面试题&#xff0c;如有需要&#xff0c;点击关注&#xff0c;回复【面试题】&#xff0c;即可下载。 31 、说说List,Set,Map三者的区别&#xff1f; List、Set、Map是三种不同数据结构的集合&#xff0c;它们的主要区别体现在存储方式…

Linux命令:用来列出当前系统内核加载的所有模块的工具lsmod详解

目录 一、概述 二、 使用方法 三、 输出格式 四、 示例 五、 相关命令 六、 高级用法 1、结合管道符 | 和 grep 命令 2、结合其他命令使用 七、应用和注意 一、概述 lsmod 是一个 Linux 命令行工具&#xff0c;用来列出当前内核加载的所有模块。这个命令对于了解哪些模…

分布式计算技术是什么?在数据集成值得作用?

数据是现代科技技术的基础&#xff0c;面对爆炸性数据的增长&#xff0c;要求计算能力要求更高、数据整合和处理更有效&#xff0c;如何应对数据集成带来的挑战&#xff1f;本文将探讨分布式计算技术在数据集成中的优化作用。 一 分布式计算技术。 定义&#xff1a;分布式计算…

《机器学习by周志华》学习笔记-神经网络-02感知机与多层网络

1、感知机 1.1、概念 感知机(Perceptron)由2层神经元模型组织,如下图所示: 「输入层神经元」接收外界输入信号后,传递给「输出层神经元 」 「输出层神经元」是「M-P神经元」,亦称「阈值逻辑单元(threshold logic unit)」 1.2、作用 「感知机」能够容易的实现逻辑「与…