Hey, I contacted the company multiple times and after some weird conversations I got some code that let me read and decode the data. Thank you everyone for your help!
I connected a PCB to my Raspberry PI that should output temperature, humidity, pressure and air quality. I receive the data via serial. I wrote a Python script that read on the serial and outputs the data.
#!/usr/bin/env python
import time
import serial
ser = serial.Serial(
port='/dev/ttyAMA0',
baudrate = 9600,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
bytesize=serial.EIGHTBITS,
timeout=1
)
while 1:
x=ser.readline()
print (x)
And the data looks like this (multiple sample data):
b'ZZ?\x0f\t,\x16a\x01\x86\x8d\x10Y\x00\x02\xa5\x9b\x00p\xdd'
b'ZZ?\x0f\t.\x16]\x01\x86\x8f\x10Z\x00\x02\xa3\x7f\x00p\xc0'
b'ZZ?\x0f\t0\x16[\x01\x86\x91\x10Y\x00\x02\xa2\xcc\x00p\r'
b'ZZ?\x0f\t2\x16S\x01\x86\x91\x10V\x00\x02\xa4\xe7\x00p!'
b'ZZ?\x0f\t3\x16O\x01\x86\x8f\x10X\x00\x02\xa3\x7f\x00p\xb5'
So that should be multiple byte-arrays. Sadly there is no documentation so I can't find anything how to decode this. If I try to decode the data:
x=ser.readline().decode()
I get the following error:
Traceback (most recent call last):
File "ser.py", line 16, in <module>
x=ser.readline().decode()
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x86 in position 9: invalid start byte
So maybe the data is not utf-8? Ignoring the errors does not help. Does someone know how to proper decode the data? That would help me a lot!
Thanks!
This looks like raw binary data (not human readable in any of the common encodings I tried). You'll need to look up the structure of bytes and likely use the struct library to convert to regular python objects.
If there is no documentation you'll have to reverse engineer it. Each bytearray is 20 bytes long, and the first four bytes are all the same, so my gut assumption is that the first four bytes (32 bits) are a header, followed by your four values as 32 bit floats or ints. If that is the case you could decode each array with something like:
>>>struct.unpack('iiiii', b'ZZ?\x0f\t3\x16O\x01\x86\x8f\x10X\x00\x02\xa3\x7f\x00p\xb5')
(255810138, 1326854921, 277841409, -1560149928, -1250951041)
The examples you provided suggested the simple case of all 4 byte numbers probably isn't the case (none of those numbers make sense for weather readings), but it may be a mixture of various length numbers to account for the various sensors having differing levels of precision.
I contacted the company multiple times and after some weird conversations I got some code that let me read and decode the data. Thank you everyone for your help!
Related
So I am currently doing a beginner CTF challengeon pwnable.tw, the "start" challenge specifically. After reversing the challenge binary I found out there was a buffer overflow exploit, and one thing I would have to do to get an ideal starting point would be to leak the stack address by pointing it back to a specific address (0x08048087), so i crafted a payload, that would then overwrite the return address with the address I was aiming for. However, I'm having trouble converting the byte data into a string format to be fed to the vulnerable program.
Below is my python code:
from pwn import *
shellcode = b'A' * 20
shellcode += pack(0x08048087, 32)
print(shellcode)
I use the pwn library to simplify packing the address, and then I print it and then pipe it into the vulnerable binary as stdin. However, what will happen when I print this, is that rather than printing the string equivalent of the associated hex values of that address, it will instead print this:
b'AAAAAAAAAAAAAAAAAAAA\x87\x80\x04\x08'
Just a string literal version of the hex values themselves. However, this will of course not be interpreted by the program in the way i intend it to be. So I try to decode it into utf-8 or an ASCII string, or even use str to convert it no matter which way I choose I get the following error:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x87 in position 20: invalid start byte
It would seem it can't decode the 0x87, which makes sense, in this case there does not seem to be an equivalent for it to decode to. But then my question becomes how can I deliver my shell code, specifically the hexadecimal address part, to the program in a way that the program will interpret that portion of the overflowed buffer as the address that i intend it to, rather than it being incorrectly mapped since my script gave me a stringified version of the hex values themselves?
So I ended up finding the answer, it was to use sys.stdout.buffer.write(), rather than print or sys.stdout.write() since sys.stdout.buffer.write() uses a BufferedWriter which simply operates on raw bytes rather than the other two which operate on text/strings. Thank you to everyone in the comments who helped me!
I have an issue with the pymodbus decoder with strings. For example, when I try to read 'abcdefg' pymodbus gives me 'badcfehg'. The byteorder and the wordorder don't change the result.
Here is my code:
result=client.read_holding_registers(25000,4)
decoder = BinaryPayloadDecoder.fromRegisters(result.registers,byteorder=Endian.Little,wordorder=Endian.Big)
decoder.decode_string(8)
Can someone explain why the order does not change the result? I try with the builder and it's the same problem. However, I don't have this problem with 32 bits floats for example.
I also tried with an older version of pymodbus and it works:
decoder = BinaryPayloadDecoder.fromRegisters(registers,endian=Endian.Little)
Note: I already read the following topic: pymodbus: Issue reading String & multiple type of data from Modbus device but I don't have any access to the modbus server.
The problem is that Modbus specs does not define in what order the two bytes for char strings are sent or even in what order 16-bit words are sent for 32-bit types.
Then some Modbus devices send bytes or words in an order and others do the opposite.
If you are writing a Modbus client then you should add the option in the configuration to be able to invert the order of both bytes and 16-bit words in 32-bit data types.
https://en.wikipedia.org/wiki/Endianness
I've encountered this same issue with byteorder not working (wordorder seems to be fine). The solution I came up with is to use Struct:
import struct
Then:
count = 4 #Read 4 16bit registers
result = client.read_holding_registers(25000,count)
for i in range(count):
result.registers[i] = struct.unpack("<H", struct.pack(">H", result.registers[i]))[0]
decoder = BinaryPayloadDecoder.fromRegisters(result.registers)
print(decoder.decode_string(7)) #Since string is 7 characters long
This uses Struct to unpack and pack as an unsigned short integer. The endianness does not matter since all you're doing is swapping bytes. The result overwrites the registers so you can then use the BinaryPayloadDecoder as you normally would.
I would have preferred to iterate through the responses instead of using range(count), but couldn't find a way to do it and wanted to post this workaround. If I figure it out, I will let you know.
I am communicating with a modbus rtu device with 32bit wide registers, custom apparently.
I tried using a modbus library out of the box but no luck as I get the error as follows modbus_tk.exceptions.ModbusInvalidResponseError: Response length is invalid 0
Two things I do not understand...
why is the byte length not consistent(0x01 :1 byte vs 0x04B:2 bytes vs 0xclff:3 bytes).
if this is unicode, why do I get the error response.decode('utf-8') UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc1 in position 4: invalid start byte
Ultimately the response message is a pressure reading and should look something like 97.6. I cannot figure out how to interpret the byte string to be 97.6.
Any help is greatly appreciated.
I tried decoding to utf-8 and converting the hexadecimal bytes to ascii with no success.
So I attempted to can my request for holding register, I tried ser.write(b'\x01\x03\x07\xd0\x00\x01\xc4\x86')
and I got a response.
response = b'\x01\x03\x04B\xc1ff\x14='
Which I believe is unicode however I am not sure.
Yes, I suppose you get an error, because modbus registers are 16 bits in length. The invalid start byte most likely means that you are starting your reading in the wrong register. Modbus registers are sequential, and have a weird offset from the docs. Like, say your reference states address 40088. By being 40xxx we can assume this is a Function 3 register, but is not mandatory. Now, in fact the address can be base 0 or base 1, meaning to substract or add 1 to it, making the real address 40087 in most cases.
So if you go and read 40088, you'll have a starting byte that doesn't match.
About coding, modbus is Big endian for words and bytes, so you'd have to verify the decoder, and finally the RTU frame comes with a lot more information than the requested value. You need to read the modbus spec to know, but generally there's 1 byte for function, 2 bytes for length, 2 bytes for answer, then 1 byte for closing, or something like that. I don't know the details by heart, I use pymodbus! haha Cheers
I am using the lib presented in https://github.com/cmcqueen/cobs-python to make sure i can send data over a serial line.
However, this lib requires a string as input, which can be about anything (text files, images, etc...). Since I'm using serial line without any special features on, there is no need to worry about special characters triggering some events.
If I could, I would send for example the image in raw mode, since it does not matter how the data is passed to the other end, but I need to encode it with this lib.
I have tried the following:
data = open('img.jpg', 'wb')
cobs_packet = cobs.encode(''.join(data).encode('utf-8'))
This gives me the following error:
>>> UnicodeDecodeError: 'ascii' codec can't decode byte 0xff in position 0: ordinal not in range(128)
The problem is, if I use different encoding types, the data length is changed, and that can't happen for what I'm trying to do.
Isn't there any way to simply convert the input to string as-is?
EDIT: I'm using python version 2.7
I didn't test how far this is valid, but I have found a simple solution which seems to be working. By using bytes() function, it works somehow as strings. I can pass it to the lib in question like this.
Thanks for the help everyone. Cheers.
code:
import socket, binascii, struct
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_UDP)
while True:
print s.recv(2048)
output:
Ek�9##�F5��W�jq��� stackexchangecom� electronics �
h h
stackexchangecomDa�scifi ET##�<���� stackoverflowcom���meta
,��� stackoverflowcom�A���meta
,��� stackexchangecomG��security Ee##�+���� stackexchangecom���scifi
as you can see some of the data has been decoded/interpreted but the rest isn't not sure as to why
Can anyone help?
You're printing raw UDP packets, which contain arbitrary binary data. Some of those bytes are in the printable range, but those that aren't in that range get converted into �.
You can get a better look at that data by printing its representation, which shows the printable bytes as normal and shows the unprintable ones as hexadecimal escape codes. To do that, change your print statement to:
print repr(s.recv(2048))
I suspect you'd like to actually decode those packets. That's quite possible, but it's a bit technical, and you should probably study the topic a bit first. :) This article by Silver Moon, Code a network packet sniffer in python for Linux, looks quite helpful.