车载网络测试实操源码_使用CAPL解析VBF文件

系列文章目录

车载网络测试实操源码_使用CAPL脚本解析hex、S19、vbf文件
车载网络测试实操源码_使用CAPL脚本对CAN报文的Counter和CRC进行实时监控
车载网络测试实操源码_使用CAPL脚本模拟发送符合协议要求的Counter和CRC的CAN报文
车载网络测试实操源码_使用CAPL脚本实现安全访问解锁
车载网络测试实操源码_使用CAPL脚本进行DTC自动化测试
车载网络测试实操源码_使用CAPL脚本进行UDS刷写及其自动化测试
车载网络测试实操源码_使用CAPL脚本进行UDS协议测试
粉丝问题解答系列文章… …
其他持续更新中… …



前言

VBF(Versatile Binary Format)文件是一种二进制文件格式,被沃尔沃、福特、马自达、吉利等汽车制造商广泛采用。这种格式无法使用我们常规的烧录器进行刷写,通常需要进行解析转化。如果各位需要自己编写软件或脚本来刷写VBF文件,或者需要对刷写功能进行测试时,那么本篇文章就会对你非常有用。今天我们就来讲一下如何使用CAPL脚本对VBF文件进行数据解析。


一、VBF文件介绍

VBF文件包含三部分:VBF版本段、文件头段、数据段。前面的版本段和文件头段是ASCII码,包含版本、地址、校验码等信息;后面的数据段是二进制码,是原始的烧录二进制文件。由于ECU固件通常很大,因此VBF文件通常分为多个数据块,以便在刷写期间逐步传输和写入ECU。具体格式如下:
在这里插入图片描述

二、VBF文件解析

以下是一个实际的VBF文件示例,分别是SBL(Secondary Bootloader)和APP的VBF文件,大家可以对应上面的格式进行理解:
在这里插入图片描述
在这里插入图片描述
根据前面的讲解我们已经知道,上面示例中header{}部分就是文件头段,后面一堆看起来乱码的数据其实就是程序的二进制数据内容,我们实际主要做的就是把header中的相关信息(比如签名值)和数据段中的二进制数据解析出来,用于程序烧写和验签。

话不多说,上代码(以解析上图格式的VBF文件为示例)

/*@!Encoding:936*/
includes
{
}

variables
{
  /*sbl和app的vbf文件定义*/
  struct VbfData
  {
    byte Block_1_Address[4];
    byte Block_1_Length[4];
    byte Block_1_data[0x100000];
    byte Block_1_check[2];
    
    byte Block_2_Address[4];
    byte Block_2_Length[4];
    byte Block_2_data[0x100000];
    byte Block_2_check[2];
    
    byte verification_block_start[4];
    byte verification_block_length[4];
    byte verification_block_data[0x100];
    byte verification_block_check[2];
    
    byte sw_signature_dev[0x500];
  };
  struct VbfData SblVbfData;
  struct VbfData AppVbfData;

  /* 指定待解析的文件 */
  char SblVbf_PathFile[200] = "E:\\TEST\\SBL.vbf";
  char AppVbf_PathFile[200] = "E:\\TEST\\APP.vbf";
}

/*将char字符转换为byte*/
byte char2byte(char ch)
{
   byte  val = 0;
   if ( ch >= '0' && ch <= '9')
   {
      val = ch - '0';      
   }
   if ( ch >= 'a' && ch <= 'f')
   {
      val = (ch - 'a') + 10;      
   }
   if ( ch >= 'A' && ch <= 'F')
   {
      val = (ch - 'A') + 10;       
   }
   return val;
}

/*解析VBF文件*/
void vbfFile_Parse(byte File_Select,struct VbfData File_obj)
{
  char UpdateFileAdress[200];
  char VbfFileHeaderBuffer[0x1000];
  char VbfFileHeaderBuffer_tmp[10];
  byte VbfFileBinaryBuffer[0x100000];
  byte VbfFileBinaryBuffer_tmp[10];
  dword UpdateFileHandle;
  dword buf_idx;
  byte brace_count;
  long i;
  char strcmp[20] = "sw_signature_dev = ";
  byte str_cut;
  dword str_cut_start,str_cut_end;
  struct
  {
    dword data;
  }Block_1_Length,Block_2_Length,Block_3_Length;
  dword Block_1_offset,Block_2_offset;
  
  str_cut = 0;
  brace_count = 0;
  buf_idx = 0;
 
  /*获取文件路径*/
  if(File_Select == 1)//SBL
  {
    memcpy(UpdateFileAdress,SblVbf_PathFile,elcount(SblVbf_PathFile));
  }
  else if(File_Select == 2)//APP
  {
    memcpy(UpdateFileAdress,AppVbf_PathFile,elcount(AppVbf_PathFile));
  }

  /*以二进制模式打开文件*/
  UpdateFileHandle = OpenFileRead(UpdateFileAdress,1);

  if(UpdateFileHandle != 0)
  {
    /*读取Header字符数据*/
    while( fileGetString(VbfFileHeaderBuffer_tmp,2,UpdateFileHandle)!=0 ) 
    {
      VbfFileHeaderBuffer[buf_idx++] = VbfFileHeaderBuffer_tmp[0];
      
      if(VbfFileHeaderBuffer_tmp[0] == '}'){brace_count++;}//以'}'划分出文件头段和二进制数据段
      if((File_Select == 1 && brace_count == 1) || ( File_Select == 2 && brace_count == 5 ))//SBL的Header有1个'}',APP的Header有5个'}'
      {
          break;
      }
    }
    write ("Header数据总字节数:%x",buf_idx);
    for(i=0;i<buf_idx;i++)
    {
      if(0 == strncmp_off(VbfFileHeaderBuffer,i,strcmp,0,strlen(strcmp)))
      {
        str_cut = 1;
        str_cut_start = i+strlen(strcmp)+2;//当前位置+"sw_signature_dev = 0x"的偏移
      }
      if(str_cut == 1 && VbfFileHeaderBuffer[i] == ';')
      {
        str_cut_end = i;
        break;
      }
    }
    buf_idx = 0;
    for(i = str_cut_start; i < str_cut_end; i += 2)
    {
      File_obj.sw_signature_dev[buf_idx++]=char2byte(VbfFileHeaderBuffer[i])*0x10 + char2byte(VbfFileHeaderBuffer[i+1]);
    }
    
    /*读取下载的二进制数据*/
    buf_idx = 0;
    while(fileGetBinaryBlock(VbfFileBinaryBuffer_tmp,1,UpdateFileHandle)!=0)
    {
      VbfFileBinaryBuffer[buf_idx++] = VbfFileBinaryBuffer_tmp[0];
    }
    write ("二进制数据总字节数:%x",buf_idx);
    
    fileClose (UpdateFileHandle);
  }
  else
  {
    write ("Failed to read file.");
  }
  
  /*解析Block1*/
  memcpy_off(File_obj.Block_1_Address,0,VbfFileBinaryBuffer,0,4);
  memcpy_off(File_obj.Block_1_Length,0,VbfFileBinaryBuffer,0+4,4);
  memcpy(Block_1_Length,File_obj.Block_1_Length);
  Block_1_Length.data = swapDWord(Block_1_Length.data);
  memcpy_off(File_obj.Block_1_data,0,VbfFileBinaryBuffer,4+4,Block_1_Length.data);
  memcpy_off(File_obj.Block_1_check,0,VbfFileBinaryBuffer,4+4+Block_1_Length.data,2);
  Block_1_offset = 4+4+Block_1_Length.data+2;
  /*解析Block2*/
  if(File_Select == 2)  //APP才有Block2
  {
    memcpy_off(File_obj.Block_2_Address,0,VbfFileBinaryBuffer,Block_1_offset,4);
    memcpy_off(File_obj.Block_2_Length,0,VbfFileBinaryBuffer,Block_1_offset+4,4);
    memcpy(Block_2_Length,File_obj.Block_2_Length);
    Block_2_Length.data = swapDWord(Block_2_Length.data);
    memcpy_off(File_obj.Block_2_data,0,VbfFileBinaryBuffer,Block_1_offset+4+4,Block_2_Length.data);
    memcpy_off(File_obj.Block_2_check,0,VbfFileBinaryBuffer,Block_1_offset+4+4+Block_2_Length.data,2);
    Block_2_offset = Block_1_offset+4+4+Block_2_Length.data+2;
  }
  else if(File_Select == 1)
  {
    Block_2_offset = Block_1_offset;
  }
  /*解析verification_block*/
  memcpy_off(File_obj.verification_block_start,0,VbfFileBinaryBuffer,Block_2_offset,4);
  memcpy_off(File_obj.verification_block_length,0,VbfFileBinaryBuffer,Block_2_offset+4,4);
  memcpy(Block_3_Length,File_obj.verification_block_length);
  Block_3_Length.data = swapDWord(Block_3_Length.data);
  memcpy_off(File_obj.verification_block_data,0,VbfFileBinaryBuffer,Block_2_offset+4+4,Block_3_Length.data);
  memcpy_off(File_obj.verification_block_check,0,VbfFileBinaryBuffer,Block_2_offset+4+4+Block_3_Length.data,2);
}

/*解析函数调用示例*/
void VbfParseMain()
{
  vbfFile_Parse(1,SblVbfData);
  vbfFile_Parse(2,AppVbfData);
}

解析完的SBL和App程序数据就存在SblVbfData和AppVbfData中,包括文件头段中的签名值和数据段中的多块二进制数据。
以上代码是根据上面的示例VBF文件进行编写的,如果你的VBF文件中数据块个数不一样,需要调整一下脚本中解析的数据块个数。

总结

本文简单介绍了VBF文件的格式,以及如何使用CAPL脚本对VBF文件中的数据进行解析,希望对大家有所帮助。本文只讲解了如何解析出VBF文件中的数据,后续文章会讲解如何将解析出来的数据通过CAN进行刷写下载。

  • 19
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
CAPL是一种编程语言,通常用于编写测试和仿真脚本,不直接支持解析S19文件。但是,CAPL提供了一个二进制文件I/O库,可以用于读写二进制文件,因此可以使用CAPL来读取S19文件并提取其中的数据。 S19文件是一种十六进制文件格式,用于存储嵌入式系统的程序和数据。它包含了一些元数据和十六进制数据记录。下面是一个S19文件的示例: ``` S1130000C0F2BEEF00000000000000000000000000B6 S11300100000000000000000000000000000000000A6 S9030000FC ``` 每行记录由以下几部分组成: - 起始符号"S1"或"S9",表示数据记录类型。S1表示数据记录,S9表示文件结束记录。 - 记录长度,以十六进制表示。例如,S113表示记录长度为19字节(0x13)。 - 起始地址,以十六进制表示。例如,0000表示起始地址为0。 - 记录类型,以十六进制表示。例如,C0表示数据记录。 - 数据,以十六进制表示。例如,F2BEEF表示数据为0xF2, 0xBE, 0xEF。 要解析S19文件,可以使用CAPL的二进制文件I/O库和字符串处理函数。基本的步骤如下: 1. 打开S19文件使用fopen函数打开文件并返回一个文件指针。 2. 读取每行记录,使用fgets函数读取文件中的每一行。 3. 解析每行记录,使用字符串处理函数和类型转换函数将每行记录转换为数据。 4. 处理解析后的数据,根据记录类型将数据存储到相应的位置。 以下是一个简单的CAPL代码示例,演示了如何读取S19文件并提取其中的数据。 ``` variables { FILE* fp; char line[80]; unsigned char data[1024]; unsigned long address = 0; } on start { fp = fopen("program.s19", "r"); if (fp == NULL) { write("Failed to open file"); return; } while (fgets(line, sizeof(line), fp)) { if (line[0] != 'S') { continue; // skip comments and other lines } int len = (int)strtol(line + 2, NULL, 16); // parse length field int type = (int)strtol(line + 8, NULL, 16); // parse type field if (type == 0) { int i; for (i = 0; i < len - 5; i += 2) { int byte = (int)strtol(line + 10 + i, NULL, 16); // parse data byte data[address++] = (unsigned char)byte; } } else if (type == 9) { break; // end of file } } fclose(fp); // process data here } ``` 该代码打开名为“program.s19”的文件,并逐行读取文件内容。对于每个数据记录,它解析长度、类型和数据,将数据存储到data数组中,并将地址address增加相应的字节数。最后,它关闭文件并在此处处理data数组中的数据。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MrxMyx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值