It uses the AUTOSAR method and VAG Magic bytes. The generator polynomial is 0x2F, the initial value 0xFF and the final CRC is XORd with 0xFF as well.
The reverse engineering was done with CRCbeagle ( https://github.com/colinoflynn/crcbeagle ), if you want to play with it yourself, this is the code I used:
Code: Select all
from crcbeagle import crcbeagle
crcb = crcbeagle.CRCBeagle()
crcb.search([
# first 7 bytes are the message starting from the timer,
# last byte is magic number based on timer
[0x0A, 0x12, 0x0, 0x0, 0x0, 0x30, 0x0, 0xDB], # Timestamp 527760 onwards
[0x0B, 0x12, 0x0, 0x0, 0x0, 0x30, 0x0, 0x15],
[0x0C, 0x12, 0x0, 0x0, 0x0, 0x30, 0x0, 0x4A],
[0x0D, 0x12, 0x0, 0x0, 0x0, 0x30, 0x0, 0x6B],
[0x0E, 0x12, 0x0, 0x0, 0x0, 0x30, 0x0, 0x23]],
# checksums for each message
[[0x27], [0xF3], [0x32], [0x0A], [0x17]]
)
# homework
## F3 0B 12 0 0 0 30 0
## 32 0C 12 0 0 0 30 0
## 0A 0D 12 0 0 0 30 0
## 17 0E 12 0 0 0 30 0
## E2 0F 12 0 0 0 30 0
## 92 00 12 0 0 0 30 0
## E5 01 12 0 0 0 30 0
## 98 02 12 0 0 0 30 0
## C4 03 12 0 0 0 30 0
## 5C 04 12 0 0 0 30 0
## B8 05 12 0 0 0 30 0
## 1E 06 12 0 0 0 30 0
## 7C 07 12 0 0 0 30 0
## 4D 08 12 0 0 0 30 0
## D7 09 12 0 0 0 30 0
## 27 0A 12 0 0 0 30 0
Code: Select all
Input parameters:
8-bit CRC size
5 total messages, with:
5 messages with 8 byte payload
NOTE: Output parameters may be specific to this message size only. Pass different length messages if possible.
Working on messages of 8 length:
Found single likely solution for differences of len=8, yah!
Found single XOR-out value for len = 8: 0x21
********** example usage *************
import struct
from crccheck.crc import Crc8Base
crc = Crc8Base
def my_crc(message):
crc._poly = 0x2F
crc._reflect_input = False
crc._reflect_output = False
crc._initvalue = 0x0
crc._xor_output = 0x21
output_int = crc.calc(message)
output_bytes = struct.pack("B", output_int)
output_list = list(output_bytes)
return (output_int, output_bytes, output_list)
m = [10, 18, 0, 0, 0, 48, 0, 219]
output = my_crc(m)
print(hex(output[0]))
**************************************
If you have multiple message lengths this solution may be valid for this only.
More information about the Magic Numbers can be found here, luckily the 0x187 address was already reverse engineered: https://github.com/commaai/opendbc/blob ... on.cc#L110
More information about the AUTOSAR specifications are here: https://www.autosar.org/fileadmin/user_ ... ibrary.pdf Chapter "7.2.1.2 8-bit 0x2F polynomial CRC Calculation".
For the ZombieVerter code, I wrote this function with a little test (should be executable with every C++ compiler:
Code: Select all
#include <cstdio>
#include <iostream>
/** Calculate the checksum for VAG MEB Chargers
*
* The method used is described in Chapter "7.2.1.2 8-bit 0x2F polynomial CRC Calculation".
* CRC Parameters:
* 0x2F - Polynomial
* 0xFF - Initial Value
* 0xFF - XOR Output
*
* @see https://github.com/colinoflynn/crcbeagle for CRC hacking :)
* @see https://github.com/commaai/opendbc/blob/master/can/common.cc#L110
* @see https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
* @see https://web.archive.org/web/20221105210302/https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
*/
void vw_meb_crc(uint8_t* inputBytes, uint8_t length)
{
const uint8_t poly = 0x2F;
const uint8_t xor_output = 0xFF;
// VAG Magic Bytes for Address 0x187
const uint8_t magicBytes[16] = { 0x7F, 0xED, 0x17, 0xC2, 0x7C, 0xEB, 0x44, 0x21, 0x01, 0xFA, 0xDB, 0x15, 0x4A, 0x6B, 0x23, 0x05 };
uint8_t crc = 0xFF;
for (uint8_t i = 1; i < length + 1; i++)
{
// We skip the empty CRC position and start at the timer
// The last element is the VAG magic byte for address 0x187 depending on the counter value.
if (i < length)
crc ^= inputBytes[i];
else
crc ^= magicBytes[inputBytes[1] & 0x0F];
for (uint8_t j = 0; j < 8; j++)
{
if (crc & 0x80)
crc = (crc << 1) ^ poly;
else
crc = (crc << 1);
}
}
crc ^= xor_output;
inputBytes[0] = crc; // set the CRC checksum directly in the output bytes
}
int main()
{
//uint8_t bytes[8] = { 0x00, 0x0A, 0x12, 0x00, 0x00, 0x00, 0x30, 0x00 };
uint8_t bytes[8] = { 0x9F, 0x88, 0x12, 0x00, 0x00, 0x00, 0x30, 0xFE };
printf("Data bytes before CRC Calculation:\n");
for (int i = 0; i < 8; i++) {
printf("%02X, ", bytes[i]);
}
printf("\n");
vw_meb_crc(bytes, 8);
printf("\nData bytes after CRC Calculation:\n");
for (int i = 0; i < 8; i++) {
printf("%02X, ", bytes[i]);
}
printf("\n");
}
Code: Select all
Data bytes before CRC Calculation:
00, 0A, 12, 00, 00, 00, 30, 00,
Data bytes after CRC Calculation:
27, 0A, 12, 00, 00, 00, 30, 00,
Using the function in the code should be straight forward. Just prepare the CAN message bytes as usual with the counter and the 6 information bytes , leaving the first byte empty (or not, it's overwritten anyways) and call the function. Since it accepts the buffer as pointer, it directly modifies the first byte and adds the CRC here. Then the message can be used for the CAN transmission.