I have an old, no longer manufactured electronic device with a serial port. I am trying to reverse engineer the data packet CRC/checksum/hash used in this device.
Anyone with keen eyes, sharp math skills out there who might be able to crack this thing ?
Here is what I know so far ...
Every packet is always 21 bytes. 19 bytes of data plus 2 bytes at end for CRC/checksum/hash
Hence, there are here are no length or header bytes. All 19 bytes are involved in the hash calculation.
I have ability to generate certain amounts of data packets with the device
My first thoughts is that the data packets have some sort of CRC-16 calculation
So I followed reversing hints in www.cosc.canterbury.ac.nz/greg.ewing/essays/CRC-Reverse-Engineering.html
Verified that my data packet samples observed the "superposition principle" outlined in above web link. This indicates that they have a mathematical XOR relationship.
Started to feel good ... but then stumped after that. Have not been able to determine a CRC-16 polynomial. There is a strong possibility that these data packets hashes are not CRC-related, but rather some home brew scheme.
Read thru “A PAINLESS GUIDE TO CRC ERROR DETECTION ALGORITHMS” by Ross N. Williams
See http://www.ross.net/crc/download/crc_v3.txt
Also used application: CRC Reveng – a reverse engineering application
See reveng.sourceforge.net
Still no luck ...
Unfortunately, I do not have access to any of the devices source/binary code
Also ran tests to see if used other hashes such as Fletcher's checksum
Here are various samples of my data packets.
0x47366B2EE00000000000751CEB5F3469543B585E2D
0x47366B2ED00000000000751CEB5F3469543B582A2C
0x47366B2EC80000000000751CEB5F3469543B580B2B
0x47366B2EC40000000000751CEB5F3469543B58BB2A
0x47366B2EC20040000000751CEB5F3469543B58DFE7
0x47366B2EC10000000000751CEB5F3469543B58A328
0x47366B2EC08000000000751CEB5F3469543B584127
0x47366B2EC04000000000751CEB5F3469543B588126
0x47366B2EC02000000000751CEB5F3469543B580525
0x47366B2EC01000000000751CEB5F3469543B580124
Please note the following about these data packets ...
CRC is found on the last 2 bytes of the data packet.
If I look at the bits on a logic analyzer, I have expressed the bytes as MSB-first
Hence, packet 0x47366B2EE00000000000751CEB5F3469543B585E2D is seen in binary as:
01000111 .............................................................00101101
(0x47).....................................................................(0x2D)
I do not know if my system is big or little endian, but quite certain bytes are LSB-first
Note that for the above 10 data packet samples, each packet differs by 1 bit shifting thru 10 bit positions. Except for 5th packet, where I had to change 2 bits
See data bytes that follows 0x47366B2E portion of the data packet.
Only pattern I see that emerged is the last byte decrements by one (2D, 2C, ...) on each data packet. (Except the 5th packet, where I had to change 2 bits)
This last byte is NOT some sort of sequence number, since I can generate them at any time with same value.
But it possibly gives a hint on the mathematical hash used.
Any help is appreciated !
IF it follows the simple XOR relationship that (checksum(A ^ B) == checksum(A) ^ checksum(B)) then there is a simple brute force solution!
Illustration. Suppose you have a 1-byte value with a K-bit checksum - where K doesn't actually matter, so we just represent checksums as c(i).
Step 1. Experiment: observe the checksum c(-1) of the all zeros packet.
0b0000000 => c(-1)
Step 2. Experiment: observe the checksums c(i) of all binary sequence with a single 1 in them at position i
0b00000001 => c(0)
0b00000010 => c(1)
0b00000100 => c(2)
0b00001000 => c(3)
0b00010000 => c(4)
0b00100000 => c(5)
0b01000000 => c(6)
0b10000000 => c(7)
The values you observed the checksums for form a linear basis for GF(2), and the XOR relationship now allows you to compute any checksum.
Now you can compute checksums by adding the checksums for each bit position with a 1, e.g. suppose you want the checksum of 0XF3 which in binary is 0b11110011. Since
0b11110011 = (0) + 0x80 + 0x40 + 0x20 + 0x10 + 0x02 + 0x01
then by XOR relationship,
checksum(0b11110011) = c(7) + c(6) + c(5) + c(4) + c(1) + c(0) + c(-1)
i.e. for every bit you are going to output, just XOR-accumulate the known checksum for that bit.
If you do this exercise and experimentally write out all 152 checksums of the basis vectors, it's possible you will also in the process spot a simple pattern that explains how the checksums come from the basis vectors. :) If so it would be nice to post that back here! (And maybe tell us what we're reversing?)
I have run some packets through an application called "SRP16", which searches for and displays CRC16 Rocksoft parameters. The output follows:
===== Result parameter sets =====
CRC=$2a2c Poly=$2817 init=$3141 xorout=$ffff refin=true refout=true
*** Second data set verified
CRC=$2a2c Poly=$2817 init=$70f4 xorout=$0000 refin=true refout=true
*** Second data set verified
CRC=$2a2c Poly=$2817 init=$9bf3 xorout=$0000 refin=false refout=true
*** Second data set verified
CRC=$2a2c Poly=$2817 init=$da46 xorout=$ffff refin=false refout=true
*** Second data set verified
CRC=$2a2c Poly=$4777 init=$1263 xorout=$0000 refin=false refout=true
*** Second data set verified
CRC=$2a2c Poly=$4777 init=$6f2d xorout=$0000 refin=true refout=true
*** Second data set verified
CRC=$2a2c Poly=$4777 init=$a127 xorout=$ffff refin=true refout=true
*** Second data set verified
CRC=$2a2c Poly=$4777 init=$dc69 xorout=$ffff refin=false refout=true
*** Second data set verified
CRC=$2c2a Poly=$7354 init=$1dab xorout=$0000 refin=false refout=true
*** Third data set verified
CRC=$2c2a Poly=$7354 init=$417e xorout=$0000 refin=false refout=true
*** Third data set verified
CRC=$2c2a Poly=$7354 init=$a401 xorout=$0000 refin=false refout=true
*** Third data set verified
CRC=$2c2a Poly=$7354 init=$f8d4 xorout=$0000 refin=false refout=true
*** Third data set verified
CRC=$2c2a Poly=$8a23 init=$0fa0 xorout=$0000 refin=false refout=true
*** Second data set verified
CRC=$2c2a Poly=$8a23 init=$3f6a xorout=$ffff refin=false refout=true
*** Second data set verified
CRC=$2c2a Poly=$8a23 init=$cc70 xorout=$0000 refin=true refout=true
*** Second data set verified
CRC=$2c2a Poly=$8a23 init=$fcba xorout=$ffff refin=true refout=true
*** Second data set verified
CRC=$2c2a Poly=$9656 init=$3460 xorout=$0000 refin=false refout=true
*** Third data set verified
CRC=$2c2a Poly=$9656 init=$ff4b xorout=$0000 refin=false refout=true
*** Third data set verified
CRC=$2c2a Poly=$a644 init=$195b xorout=$0000 refin=false refout=true
*** Third data set verified
CRC=$2c2a Poly=$a644 init=$70ca xorout=$0000 refin=false refout=true
*** Third data set verified
CRC=$2c2a Poly=$a644 init=$a3e8 xorout=$0000 refin=false refout=true
*** Third data set verified
CRC=$2c2a Poly=$a644 init=$ca79 xorout=$0000 refin=false refout=true
*** Third data set verified
===== done =====
Maybe give these a try and see if they work for you?
Good luck!
Related
I found a strange mismatch in the reference manual for the OTP memory size:
Accordingly to the manual (RM0385, page 78) the start-address of the OTP memory begins at 0x1FF0 F000 and ends at address: 0x1FF0 F41F (in AXIM mode) and is declared as a memory area with the size of 1024 bytes:
But if I calculate 0x1FF0F41F - 0x1FF0F000 + 1 I get a total of 1056 bytes OTP memory?!
Same addresses are set in the latest CMSIS header V1.17.0 (https://raw.githubusercontent.com/STMicroelectronics/STM32CubeF7/master/Drivers/CMSIS/Device/ST/STM32F7xx/Include/stm32f746xx.h):
#define FLASH_OTP_BASE 0x1FF0F000UL
#define FLASH_OTP_END 0x1FF0F41FUL
Is this a typo in the manual or is my calculation wrong?
I don't think it's a typo, and your calculation is correct.
My explanation is as follows:
Chapter 3.6 of the same Reference Manual shows the organization of the one-time programmable (OTP) part of the OTP area, and explains:
The OTP area is divided into 16 OTP data blocks of 64 bytes and one lock OTP block of 16 bytes. The OTP data and lock blocks cannot be erased. The lock block contains 16 bytes LOCKBi (0 ≤ i ≤ 15) to lock the corresponding OTP data block (blocks 0 to 15). (...)
So it consists of 1024 data bytes and 16 lock bytes. I guess the lock bytes are implemented as special "registers" and are not considered part of the sector. It's more a matter of definition and does not really matter that much.
There probably is a 1024-byte sector to write the (one-time programmable) data to, but the address range is slightly larger because the lock bits have to be addressed as well.
Looking for help with understanding how to change value in address DS1 (400001). First the click appears to use a 6 digit Modbus so not sure how to deal with the in 2 bytes. I think I read 40001 is the same but do not see how. I am able to receive data and understand the data when the Click PLC is the master. I would like my PC to be the master and change the address.
Here is the data I am sending to the PLC. I am expecting this data to be sent to PLC slave 02 and change the data in DS1 (400001) to the value of zero.
frame(0) = 2 'Slave Address =2
frame(1) = 6 'Mode =6
frame(2) = CByte(40001 / 256) '
frame(3) = CByte(40001 Mod 256) '
frame(4) = 0 '
frame(5) = 0 '
Dim crc As Byte() = CRC(frame) ' Call CRC Calculate.
frame(6) = crc(0) '=59 Error Check Lo
frame(7) = crc(1) '=189 Error Check Hi
SerialPort1.Write(frame, 0, frame.Length)
Realize that Application Layer addressing in Modbus is different than the bytes on the wire. The leading digit in an application layer address (e.g. 4xxxx for Holding Register) is implied in the function code (e.g. Read Holding Register)
So on the wire, you drop the leading 4, and left with an offset of 1-65536 (yes, Application Layer offsets are 1-based). But on the WIRE, they are 0-based, so you then subtact 1 from the offset to get the value 0-65535.
So, sometimes you see Application Modbus HRs like 4001, 40001, or 400001, all referencing the first HR in the device. 5 digit is most common. I do see 4 digit for old RTU devices. I do see a 6 digit every once in a while where the remote device has a ton of memory (or not, like Click).
Realize that a lot of devices are implemented by people who only understand the low level protocol, so when they say something is at address 40001, it may actually be at offset 0x0001, or 0x0000 (the correct offset on the wire). I even saw one implementation that implemented the address 40001 as literally 0x9C41 on the wire (maybe 0x9C40). Yes, 6 digit Application Layer Holding Register 440001.
Computer System Architecture - Morris Mano In chapter 5 section 7 figure 5-13
When IEN it checks whether "FGI" or "FGO" are set to 1 then an interrupt cycle happens, but as I know is when FGI = 1 it means that information in INPR cannot be changed, and FGO is the reverse to that which means that when FGO is set to 1 then information in AC will be transferred to OUTR 'OUTR can be changed' so the question here shouldn't the condition of applying interrupt cycle happen when "FGI" = 0 or "FGO" = 1 since INPR or OUTR can be changed under these conditions which now make since to execute an interrupt?
Either flag being 1 logically means a device is "ready", but what "ready" means differs for input and for output devices. In either case, flag being 1 means that the processor can or should now take action.
FGI=1 means the input device is ready, but that really means a new input is available (e.g. the user typed a key on a keyboard) and the processor should accept it. FGO=1 prevents the input device(s) from overwriting a prior input held in INPR that the processor hasn't accepted yet. When the processor accepts the input, FGI goes to 0 unlocking the INPR register, and that allows the input device to write again, which it will eventually do when the user presses another key (sending FGI back to 1 to signal the processor).
FGO=1 means ready for output, which really means the last output has been fully accepted by the device, so the OUTR register is unlocked for the processor to write a new data (character for the console). FGO=0 prevents the processor from writing OUTR as the output device hasn't accepted the last one yet.
The interrupt service routine should check each flag, and if FGI=1 then accept an input (INPR->AC) and move it into a buffer for the user program to read when it is ready. Whereas if FGO=1, then move an output character from memory buffer into the AC, and then do AC->OUTR, also lowering FGO to 0, which will preclude the processor writing until that data has been accepted by the device.
So, FGI=0 means that the processor has accepted the prior INPR value provided by the input device, and there is no new character as yet but the register is unlocked so the device can write at will.
FGO=0 means that the processor has written a value to the OUTR register, but the output device hasn't accepted that yet, so the register should be considered locked.
Am trying to initialize the NRF24L01+ registers using SPI but they always return 0x00.
According to the datasheet, Table 20 on page 51, all write commands will have a pattern of b001x xxxx, which i understood as having a 0x2x pattern.
In my snapshot below, i send the register value, for example register 0x00 will be sent as 0x20 indicating a write command to that register and then i send the value to be written on that register.
As you see on the MISO line, the value is 0x00 even when am trying to write a 0x08 which should be the default value according to page 57 of the datasheet.
I still dont know why its returning 0x00 even when i independently try to read the contents of that register later on without writing to it. I still get 0x00. The same applies to all other registers that am trying to re-init.
Anyone who has experienced this behaviour elsewhere or is it me that is having something wrong?
The NRF24 am trying to program here is this type from sparkfun
You are close. The datasheet shows write register as 001A AAAA and read as 000A AAAA, where the 5 A's represent the register you want to write to. The spec states that while the command is being sent (read, write, read payload, write payload, flush, activate, and so on), the device will return the status register. In your data, the device is responding with 0x0E, which is correct; decoded is is saying no errors and no data received or pending to transmit. If you want to see if the command you send was accepted, you need to first write the data and then read the data. For example, let's say we want to write the config register to enable the device as a receiver, 2 byte CRC with Rx interrupts enabled.
First, you would send 0b00100000 (0x20), 0b00111111 (0x3F). The device will respond with 0b00001110 (0x0e), 0b00000000 (0x00). This is what you are seeing. If you want to verify the configuration register, you need to then send 0b00000000 (0x00),which is the command to read the config register, then 0b00000000 (0x00), which is a dummy byte to clock out the data. The device will respond with 0x0e, which is the status, and then 0x3F assuming you configured as I did above.
Note that there are more commands than just reading and writing the registers, there are specific commands to fill and read the pipeline data.
Hope this helps.
I'm using AT24C512 EEPROM which is 512KB along with my STM32
I'm able to write 128bytes of data at once using
HAL_I2C_Mem_Write(&_EEPROM24XX_I2C,0xa0,Address,I2C_MEMADD_SIZE_16BIT,(uint8_t*)data,size_of_data,100)
but the issue is that i want to write more data after the data that was just wrote, but the EEPROM will replace the data as the Address is the same
so how can i skip the written address ?
This answer is not about using HAL with I2C, but hope it will point you
Just check datasheet (I looking into STM32F0) and you can see that the limit is 255 bytes (register CR2:NBYTES), I'm not sure if there is another limitation in HAL, but using direct access to registers you can sent 255 bytes at once or fragment it and sent how much you want.
For fragmenting there is bit CR2:RELOAD, if you set this, then at the end will be not transfer stopped, and you can update next NBYTES, .. when you will set last block of bytes (which will fit into NBYTES) then clear bit CR2:RELOAD.
This has one disadvantage, that every 255 bytes, you will be interrupted.
i think you should check the AT24C512 datasheet page 7.
If more
than 128 data words are transmitted to the EEPROM, the
data word address will
“
roll over
”
and previous data will be
overwritten. The address
“
roll over
”
during write is from the
last byte of the current page to the first byte of the same
page.