Skip to content
Powrót do bloga
Poradniki

Warianty CRC-16: dlaczego MODBUS, CCITT i XMODEM się różnią

Te same bajty, cztery różne wyniki CRC-16. Sprawdź online, jak poly, init, refin, refout i xorout oddzielają MODBUS od CCITT-FALSE oraz XMODEM.

13 min czytania

Dlaczego te same bajty dają cztery różne wyniki CRC-16

CRC-16 nazywa całą rodzinę, a nie pojedynczy algorytm, i członkowie tej rodziny nie zgadzają się ze sobą. Wystarczy podać te same bajty do MODBUS, do CCITT-FALSE i do XMODEM, a wrócą trzy zupełnie różne 16-bitowe liczby.

O tym, który z nich faktycznie liczy, decyduje pięć stałych: poly, init, refin, refout i xorout. Zmiana jednej z nich całkowicie zmienia wynik, a żadne częściowe podobieństwo nie ostrzeże, że coś poszło nie tak. Większość pytań w stylu „dlaczego moje CRC nie zgadza się z tym z urządzenia” kończy się dokładnie w tym miejscu: obie strony liczą inny wariant, a nikt nie zapisał który.

Każda wartość w tym przewodniku wyszła z parametryzowanego modelu CRC uruchomionego w Pythonie 3.14.7 pod macOS i została sprawdzona na cztery sposoby: przez zlib.crc32 oraz binascii.crc_hqx z biblioteki standardowej, przez opublikowane wartości kontrolne z katalogu CRC RevEng oraz przez dokumentację producentów dla każdego zestawu parametrów.

1. Ta sama ramka, cztery wyniki CRC-16

Oto prawdziwe zapytanie Modbus RTU. Urządzenie podrzędne 01, kod funkcji 03 (odczyt rejestrów przechowujących), adres początkowy 0x0000, liczba rejestrów 0x000A:

01 03 00 00 00 0A

Te same sześć bajtów przepuszczone przez cztery warianty:

WariantWynikNa łączu (młodszy bajt pierwszy)
CRC-16/MODBUS0xCDC5C5 CD
CRC-16/IBM-3740 (CCITT-FALSE)0x042828 04
CRC-16/XMODEM0x0A3838 0A
CRC-16/ARC0xD6C5C5 D6

Tych czterech wartości nie łączy nic: żadnego wspólnego układu nibbli, żadnej stałej różnicy, a przestawianie kolejności nie zamieni jednej w drugą. Dwie z nich zaczynają się na łączu tym samym bajtem, ale to czysty przypadek.

Napis „CRC-16” w karcie katalogowej niesie więc niemal zero informacji. Mówi tyle, że wynik ma szerokość 16 bitów, i na tym koniec. Kiedy trzeba obejrzeć wynik w innym systemie liczbowym, bo urządzenie po drugiej stronie drukuje binarnie, przelicznik systemów liczbowych przerzuca 16-bitową wartość między hex a postacią binarną i sam pilnuje dopełnienia zerami.

2. Co zmienia każdy z pięciu parametrów

Pod każdym wariantem siedzi ta sama maszyna: rejestr przesuwny, który połyka po jednym bicie i wprowadza stałą operacją XOR za każdym razem, gdy z góry wypada jedynka. Parametry decydują o tym, co wchodzi do rejestru i w którą stronę wędrują bity, plus jeden ostatni XOR na wyjściu.

Oto cały silnik w czternastu linijkach Pythona. To implementacja bit po bicie — wolna, ale czytelna — i odtwarza każdą wartość z tego artykułu:

def crc(data, width, poly, init, refin, refout, xorout):
    top = 1 << (width - 1)
    mask = (1 << width) - 1
    reg = init
    for byte in data:
        if refin:
            byte = int(f"{byte:08b}"[::-1], 2)
        reg ^= byte << (width - 8)
        for _ in range(8):
            reg = ((reg << 1) ^ poly) if reg & top else (reg << 1)
            reg &= mask
    if refout:
        reg = int(f"{reg:0{width}b}"[::-1], 2)
    return reg ^ xorout

Wynik tego silnika można skonfrontować z biblioteką standardową Pythona. zlib.crc32 i binascii.crc_hqx dają po jednej niezależnej odpowiedzi, a wszystkie trzy się zgadzają:

zlib.crc32(b"123456789")               = 0xCBF43926   engine above = 0xCBF43926   MATCH
binascii.crc_hqx(b"123456789", 0x0000) = 0x31C3       CRC-16/XMODEM = 0x31C3      MATCH
binascii.crc_hqx(b"123456789", 0xFFFF) = 0x29B1       CCITT-FALSE   = 0x29B1      MATCH

poly: dwa obozy, 0x8005 i 0x1021

Wielomian to stała wprowadzana operacją XOR z powrotem do rejestru. Zapisuje się go w postaci „normalnej”, z domyślnym najstarszym bitem, więc 0x8005 oznacza x^16 + x^15 + x^2 + 1, a 0x1021 oznacza x^16 + x^12 + x^5 + 1. Pętla przesunięć i XOR-ów to dzielenie wielomianów nad GF(2): wielomian jest dzielnikiem, rejestr trzyma bieżącą resztę, a każdy bit wiadomości posuwa dzielenie o jeden krok.

Prawie każde spotykane CRC-16 używa jednego z tych dwóch. 0x8005 obsługuje ARC, MODBUS i USB. 0x1021 obejmuje cały gąszcz CCITT plus XMODEM, KERMIT i warianty RFID. Sama znajomość wielomianu nigdy nie identyfikuje wariantu.

init: wartość początkowa rejestru

Dominują dwie wartości, 0x0000 i 0xFFFF. Przy starcie od zera bajt zerowy zostawia rejestr na zerze, więc 00 00 01 i 01 dają identyczne CRC. Wiadomość, która po drodze zyskuje lub gubi wiodące zera, przechodzi kontrolę. Start od 0xFFFF usuwa ten martwy punkt i właśnie dlatego korzystają z niego Modbus, CCITT-FALSE i USB.

refin i refout: kolejność bitów, nie bajtów

refin odwraca osiem bitów każdego bajtu wejściowego, zanim trafi on do rejestru. refout odwraca końcową zawartość rejestru przed ostatnim XOR-em. Układy sprzętowe wysuwające dane najmłodszym bitem naprzód dostają te odbicia za darmo, więc warianty odbite to zwykle te, które wyrosły z protokołów szeregowych.

To właśnie ta para parametrów najczęściej myli się z kolejnością bajtów, a działa na zupełnie innym poziomie. Rozdziela je sekcja 6.

xorout: końcowy XOR

To ostatni krok, wykonywany na rejestrze po refout. Dla CRC-16 zwykle jest to 0x0000 albo 0xFFFF; rodzina CRC-32 używa 0xFFFFFFFF. Najłatwiej pomylić się właśnie tutaj, bo niezgodność w tym miejscu wygląda dokładnie tak samo jak niezgodność gdziekolwiek indziej.

width: 8, 16 albo 32 bity

Szerokość wyznacza sufit wykrywalności. CRC o szerokości n łapie każdy błąd seryjny o długości do n bitów, a losowe uszkodzenie przepuszcza z prawdopodobieństwem około 2^-n. To 1 na 65 536 dla CRC-16 i 1 na 4,3 miliarda dla CRC-32.

Poniższe przebiegi startują od CRC-16/XMODEM i zmieniają dokładnie jeden parametr naraz, na standardowym wejściu testowym 123456789:

wariant bazowy CRC-16/XMODEM               = 0x31C3
init 0x0000 -> 0xFFFF   (daje CCITT-FALSE) = 0x29B1
refin/refout -> true    (daje KERMIT)      = 0x2189
xorout 0x0000 -> 0xFFFF                    = 0xCE3C
poly 0x1021 -> 0x8005                      = 0xFEE8

Jedna przestawiona flaga daje zupełnie inny wynik. CRC nie zna pojęcia „blisko”. Dwa wyniki albo się zgadzają, albo nie mówią nic o tym, jak daleko od siebie były wejścia, więc wpatrywanie się w niezgodność nigdy nie lokalizuje przyczyny. Wewnętrzna pętla to wyłącznie XOR-y i przesunięcia; kompletny przewodnik po operacjach bitowych omawia same te operatory, a ten artykuł zakłada ich znajomość.

3. „CCITT” to zła nazwa

Katalog CRC RevEng wymienia we wrześniu 2026 roku 31 odrębnych definicji 16-bitowego CRC, a trzy z nich noszą etykietę CCITT. Żadna z tych trzech nie zgadza się z pozostałymi. Cztery wiersze poniżej dzielą wielomian i wejście, a lądują na czterech niepowiązanych wynikach:

Nazwa potocznaNazwa katalogowacheckinitrefin/refoutxorout
CCITT-FALSECRC-16/IBM-37400x29B10xFFFFfalse0x0000
XMODEMCRC-16/XMODEM0x31C30x0000false0x0000
„prawdziwe” CCITTCRC-16/KERMIT0x21890x0000true0x0000
CRC-16/MCRF4XX0x6F910xFFFFtrue0x0000

Historia jest krótka i mało pomocna. Katalog wymienia CRC-16/KERMIT z aliasami CRC-CCITT i CRC-16/CCITT-TRUE, i to właśnie ten wariant jest odbity. Pod nazwą CCITT szeroko krążyła też implementacja bez odbicia, z init 0xFFFF, i dlatego katalog zapisuje „CCITT-FALSE” jako alias CRC-16/IBM-3740. XMODEM siedzi pomiędzy nimi: ten sam wielomian, brak odbicia, init zerowy.

Karta katalogowa, która pisze o CCITT, zdradza tylko tyle, że wielomian to 0x1021. Kandydatów nadal jest czterech, a zły wybór daje wartość wyglądającą równie wiarygodnie jak ta właściwa.

W dokumencie protokołu lepiej wpisać parametry niż nazwę. Linijka poly=0x1021, init=0xFFFF, refin=false, refout=false, xorout=0x0000 rozstrzyga sprawę raz na zawsze; „CRC-16/CCITT” nie rozstrzyga niczego.

4. Jak ustalić, z którym wariantem CRC-16 się pracuje

Katalog rozwiązuje to odciskiem palca. Każdy wpis publikuje wartość kontrolną: CRC z dziewięciu bajtów ASCII 123456789. Ten ciąg przepuszcza się przez implementację, którą się ma przed sobą, i odszukuje wynik w tabeli.

Wariantcheckpolyinitrefinrefoutxorout
CRC-16/ARC (IBM/LHA)0xBB3D0x80050x0000truetrue0x0000
CRC-16/MODBUS0x4B370x80050xFFFFtruetrue0x0000
CRC-16/USB0xB4C80x80050xFFFFtruetrue0xFFFF
CRC-16/IBM-3740 (CCITT-FALSE)0x29B10x10210xFFFFfalsefalse0x0000
CRC-16/XMODEM0x31C30x10210x0000falsefalse0x0000
CRC-16/KERMIT (prawdziwe CCITT)0x21890x10210x0000truetrue0x0000
CRC-16/GENIBUS0xD64E0x10210xFFFFfalsefalse0xFFFF
CRC-16/MCRF4XX0x6F910x10210xFFFFtruetrue0x0000
CRC-32/ISO-HDLC (zip, PNG, zlib)0xCBF439260x04C11DB70xFFFFFFFFtruetrue0xFFFFFFFF
CRC-32/BZIP20xFC8919180x04C11DB70xFFFFFFFFfalsefalse0xFFFFFFFF
CRC-32C (Castagnoli, iSCSI)0xE30692830x1EDC6F410xFFFFFFFFtruetrue0xFFFFFFFF
CRC-8/SMBUS0xF40x070x00falsefalse0x00
CRC-8/MAXIM-DOW (1-Wire)0xA10x310x00truetrue0x00

Jeden szczegół psuje zaskakująco wiele takich porównań: 123456789 oznacza dziewięć bajtów 31 32 33 34 35 36 37 38 39, a nie liczbę 123456789 ani ciąg zakończony bajtem zerowym. Jeśli język przekazuje funkcji szeroki ciąg znaków albo dokleja terminator, na wejściu jest co innego i każdy wiersz chybi. Payload spoza ASCII dokłada drugą pułapkę: ten sam tekst zakodowany w UTF-8 i w Windows-1252 to dwie różne sekwencje bajtów, a więc dwa różne CRC. Tablica ASCII i konwerter pokazuje bajt stojący za każdym znakiem, co rozstrzyga tę kwestię w kilka sekund.

Jeśli wartość kontrolna nie pasuje do żadnego wiersza, przed podejrzeniem własnego wielomianu warto wykluczyć przyczyny nudne:

  1. Wynik bywa odczytywany z łącza w odwrotnej kolejności bajtów.
  2. Nadawca dołącza albo pomija bajt adresu, bajt długości lub samo pole CRC, więc obie strony liczą z innego zakresu.
  3. Firmowy init, który nie jest ani 0x0000, ani 0xFFFF — zdarza się to w zamkniętych protokołach liczników i zwykle jest zagrzebane w przypisie.

Brute-force na parametrach

Kiedy producent nie chce powiedzieć, a firmware’u nie da się przeczytać, wystarczy poprosić o jedno: przechwyconą ramkę plus CRC, które wyliczyło dla niej jego urządzenie. Potem pozostaje wyliczyć wszystkie możliwości. Dwa wielomiany, dwie wartości init, niezależnie refin i refout oraz dwie wartości xorout dają 32 kombinacje, czyli tyle co nic:

target = 0x4B37                    # wartość zwrócona przez ich implementację
for poly in (0x8005, 0x1021):
    for init in (0x0000, 0xFFFF):
        for refin in (False, True):
            for refout in (False, True):
                for xorout in (0x0000, 0xFFFF):
                    if crc(b"123456789", 16, poly, init, refin, refout, xorout) == target:
                        print(f"poly=0x{poly:04X} init=0x{init:04X} "
                              f"refin={refin} refout={refout} xorout=0x{xorout:04X}")
poly=0x8005 init=0xFFFF refin=True refout=True xorout=0x0000

Trafienie jest jedno: CRC-16/MODBUS. Po podstawieniu prawdziwej ramki zamiast 123456789 to samo 32-elementowe przemiatanie zadziała na dowolnej wiadomości, dla której zna się poprawne CRC. Jeśli po jednej próbce zostaje kilka kombinacji, trzeba puścić drugą ramkę i przeciąć wyniki.

5. Modbus RTU w praktyce

Modbus RTU używa CRC-16/MODBUS: wielomian 0x8005, init 0xFFFF, odbicie na wejściu i na wyjściu, brak końcowego XOR-a. Dla naszego sześciobajtowego zapytania CRC wynosi 0xCDC5, a kompletna ramka wygląda tak:

01 03 00 00 00 0A C5 CD

Kolejność na łączu jest odwrotna do zapisu wartości

Wartość to 0xCDC5. Bajty dopisane do ramki to C5 CD. Modbus nakazuje umieścić młodszy bajt CRC jako pierwszy, czyli odwrotnie, niż czyta się liczbę szesnastkową, i bez przerwy ktoś się na tym potyka. Każde inne wielobajtowe pole tej samej ramki, łącznie z tą liczbą rejestrów 00 0A, jest big-endian. CRC jest wyjątkiem.

Ma to użyteczny efekt uboczny. Policzenie CRC-16/MODBUS z całej ramki, razem z bajtami CRC, daje wynik:

0x0000

To cała procedura walidacji po stronie odbiornika. Nie trzeba odcinać dwóch końcowych bajtów, zamieniać ich miejscami i porównywać z wartością policzoną lokalnie. Wystarczy policzyć CRC ze wszystkiego, co przyszło, i sprawdzić, czy wyszło zero. Im mniej kroków, tym mniej okazji do odwrócenia bajtów nie tak, jak trzeba.

Dlaczego dokumentacja Modbusa pokazuje 0xA001

Po otwarciu niemal dowolnej implementacji Modbusa stałą w kodzie okazuje się 0xA001, a nie 0x8005. Obie są poprawne. 0xA001 to 0x8005 z odwróconymi 16 bitami i należy do odbitej postaci algorytmu, w której rejestr przesuwa się w prawo zamiast w lewo, a bajty wejściowe nie wymagają odwracania. Obie implementacje dają identyczny wynik; różni je tylko środek. 0xA001 w kodzie źródłowym to pewny znak, że implementacja CRC-16 jest odbita.

W przechwyconym ruchu czyha jeszcze jedna pułapka. Modbus ASCII to osobne ramkowanie, które niesie każdy bajt jako dwa znaki szesnastkowe między początkowym 3A (:) a końcowym 0D 0A, i używa LRC zamiast CRC. Jeśli zrzut pokazuje 3A 30 31 30 33 tam, gdzie spodziewano się 01 03, to jest tryb ASCII i żaden wariant CRC nigdy się nie zgodzi. Tablica ASCII przekłada te bajty prosto z powrotem na znaki.

Ten sam algorytm w C

Firmware Modbusa prawie nigdy nie korzysta z powyższego modelu w Pythonie. Sięga wprost po postać odbitą, przesuwając w prawo i XOR-ując z 0xA001:

uint16_t crc16_modbus(const uint8_t *buf, size_t len) {
    uint16_t crc = 0xFFFF;
    for (size_t i = 0; i < len; i++) {
        crc ^= buf[i];
        for (int b = 0; b < 8; b++)
            crc = (crc & 1) ? (crc >> 1) ^ 0xA001 : crc >> 1;
    }
    return crc;
}

Na ramce 01 03 00 00 00 0A funkcja zwraca 0xCDC5, tę samą wartość, którą wyprodukował silnik w Pythonie. Ciąg kontrolny 123456789 daje 0x4B37. Oba przebiegi wykonano pod Apple clang 21 i zgadzają się z tabelą z sekcji 4 co do bitu.

6. refin/refout to nie kolejność bajtów

Te dwie rzeczy żyją na różnych poziomach, a pomylenie ich kosztuje, bo Modbus zawiera obie naraz.

Kolejność bajtów dotyczy bajtów wewnątrz wartości wielobajtowej: tego, czy 0xCDC5 zapisuje się i przesyła jako CD C5, czy jako C5 CD. Wewnątrz bajtu nic się nie rusza. Ten poziom w całości omawia big-endian kontra little-endian i ten artykuł tego nie powtarza.

Odbicie dotyczy bitów wewnątrz pojedynczego bajtu. refin odwraca osiem bitów każdego bajtu, zanim zobaczy go rejestr: bit 0 staje się bitem 7. Bajt zachowuje swoją pozycję w wiadomości. Żadne z tych ustawień nie wpływa na drugie.

W ramce Modbusa dzieje się jedno i drugie, niezależnie od siebie:

  • Wewnątrz algorytmu refin i refout mają wartość true, więc bity są odwracane wewnątrz bajtów.
  • Na łączu gotowe CRC dopisuje się młodszym bajtem naprzód, co jest decyzją o kolejności bajtów podjętą przez protokół.

Wyłączenie odbicia, żeby „naprawić” problem z kolejnością bajtów, daje zupełnie inny wariant, a zamiana końcowych bajtów ramki jako rekompensata za niezgodne odbicie daje wartość błędną na nowy sposób. Diagnozować trzeba po jednym: najpierw uzgodnić wartość kontrolną samym algorytmem, a dopiero potem zająć się układem ramki.

7. CRC-32 kontra CRC-16: którego użyć?

CRC-32 ma ten sam problem co CRC-16, tylko mniej widoczny, bo jeden wariant dominuje tak całkowicie, że większość programistów nigdy się nie dowiaduje o istnieniu pozostałych.

Wariantcheckpolyrefin/refout
CRC-32/ISO-HDLC0xCBF439260x04C11DB7true
CRC-32/BZIP20xFC8919180x04C11DB7false
CRC-32C (Castagnoli)0xE30692830x1EDC6F41true

ISO-HDLC to dokładnie to, co liczy zlib.crc32, i po webowej stronie stosu jest wszędzie. Zip zapisuje je przy każdym wpisie w katalogu centralnym, a każdy chunk PNG kończy się taką sumą obejmującą typ chunka i dane. Ethernetowa sekwencja kontrolna ramki używa tego samego zestawu parametrów na końcu każdej ramki wysyłanej przez serwer. BZIP2 to ten sam wielomian z wyłączonymi odbiciami, co daje wartość niemającą nic wspólnego z ISO-HDLC.

CRC-16 pokazuje się w tej samej okolicy: Redis Cluster kieruje klucz do jednego ze swoich 16 384 slotów wyrażeniem CRC16(key) mod 16384, i dlatego klucze z tym samym hash tagiem lądują na tym samym węźle.

Dlaczego CRC-32C wygrał sprzętową loterię

CRC-32C używa innego wielomianu, o lepszej wykrywalności błędów na krótkich blokach, jakie wysyłają protokoły pamięci masowej i sieciowe. To zapewniło mu iSCSI, sumy kontrolne metadanych ext4, SCTP i Btrfs. Potem Intel wsadził go w krzem: instrukcja crc32 z SSE4.2 liczy CRC-32C bezpośrednio, co na współczesnym x86 zamienia go w praktycznie darmową kontrolę integralności. Do nowego kodu, o ile nic nie wymusza zgodności ze starszym formatem, to właśnie ten wariant warto wziąć.

Żaden z nich nie jest hashem do weryfikacji pobranego pliku. Kiedy potrzebna jest suma kontrolna potwierdzająca, że plik dotarł w całości, znanym narzędziem jest generator hashy MD5, a MD5 kontra SHA-256 omawia, któremu skrótowi ufać i do czego. CRC odpowiada na pytanie „czy to się uszkodziło”; hash kryptograficzny odpowiada na pytanie „czy to dokładnie ta treść, której oczekuję”.

8. CRC nie powstrzymuje manipulacji

Oto dwie wiadomości JSON o przeciwnych znaczeniach i tym samym CRC-32:

message A = b'{"to":"alice","amount":100}  H\xf6\xc0E'
message B = b'{"to":"mallory","amount":999}\xb2\xb2\xc2\xc2'
CRC-32(A) = 0x12345678
CRC-32(B) = 0x12345678

SHA-256 z tych samych dwóch:

A -> a83b90f5e3f21dd32039e0e693a8b15864eda83e258c115deeee50a60c09ae23
B -> f9319ee0507d719c7260535dff33df721ddd9c0e1857690f3d8e02f92601b742

Nic tutaj nie zostało znalezione metodą brute-force. Końcowe bajty wyliczono algebraicznie. CRC jest funkcją liniową, a dopisanie czterech bajtów do wiadomości odwzorowuje się na wyjście CRC-32 bijektywnie, więc dla dowolnej wiadomości i dowolnej wartości docelowej istnieje dokładnie jeden czterobajtowy sufiks, który do niej prowadzi, a jego znalezienie to arytmetyka.

Dopisanie czterech wyliczonych bajtów 46 C9 6E 0B do hello world:

CRC-32(b'hello world' + 46 C9 6E 0B) = 0xDEADBEEF   target 0xDEADBEEF   MATCH

Napastnik, który potrafi zmodyfikować payload, potrafi zatem także poprawić CRC — w czasie stałym i bez szukania czegokolwiek. Poprzedzenie wiadomości sekretem nic nie ratuje: przy edycji zachowującej długość poprawka nanoszona na CRC zależy wyłącznie od zmienionych bitów, więc da się ją policzyć, nie znając sekretu. Taki właśnie kształt miał atak, który złamał WEP.

CRC broni przed szumem transmisyjnym, który jest losowy i nie ma celu. Przeciwnik nie jest ani jednym, ani drugim, a wobec niego CRC nie daje nic. Kiedy wymogiem jest autentyczność, a nie integralność, potrzebna jest konstrukcja z kluczem: generator HMAC tworzy ją dla dowolnego payloadu i klucza, a dlaczego weryfikacja podpisu webhooka zawodzi przechodzi przez te elementy, które sprawiają kłopot przy podłączaniu jej do prawdziwego endpointu.

9. FAQ

Czy CRC-16/CCITT to to samo co CRC-16/CCITT-FALSE?

Nie, CRC-16/CCITT-FALSE i CRC-16/CCITT to różne warianty. CCITT-FALSE to CRC-16/IBM-3740: init 0xFFFF, bez odbicia, wartość kontrolna 0x29B1. Wariant, o który zwykle chodzi przy samym CCITT, to CRC-16/KERMIT: init 0x0000, odbity, check 0x2189. Dzielą wielomian 0x1021 i nie zgadzają się w niczym więcej.

Dlaczego kalkulator online nie zgadza się z moim urządzeniem?

Prawie zawsze przez różne parametry. Narzędzie ma domyślnie jeden wariant, a urządzenie implementuje inny. Wystarczy przepuścić bajty ASCII 123456789 przez oba, porównać obie wartości kontrolne z tabelą z sekcji 4, a niezgodny parametr zwykle ujawnia się w niecałą minutę.

Dlaczego Modbus umieszcza młodszy bajt CRC jako pierwszy?

Bo tak mówi specyfikacja, i jest to jedyne pole ramki, które zachowuje się w ten sposób. Adresy rejestrów i liczniki są big-endian; końcowe CRC nie. Zamiast samodzielnie przestawiać te dwa bajty, lepiej policzyć CRC-16/MODBUS z całej ramki razem z nimi i sprawdzić, czy wychodzi 0x0000.

Co dokładnie odwracają refin i refout?

Bity wewnątrz bajtu, nigdy bajty wewnątrz wiadomości. refin odwraca osiem bitów każdego bajtu wejściowego przed przetworzeniem; refout odwraca końcową zawartość rejestru przed ostatnim XOR-em. Oba są niezależne od kolejności bajtów, która rządzi tym, jak wartość wielobajtowa jest ułożona na łączu.

Użyć CRC-16 czy CRC-32?

CRC-16 przepuszcza losowe uszkodzenie mniej więcej raz na 65 536 przypadków, CRC-32 raz na 4,3 miliarda. Przy krótkich ramkach szeregowych o długości kilkudziesięciu bajtów CRC-16 w zupełności wystarcza i tak czy inaczej zwykle narzuca je protokół. Przy plikach, ramkach sieciowych i wszystkim powyżej kilku kilobajtów lepiej sięgnąć po CRC-32.

Czy można użyć CRC jako podpisu API?

Nie, CRC nie może służyć jako podpis API. CRC jest liniowe, więc każdy, kto zmieni payload, może przeliczyć pasującą wartość, a dopisanie czterech wybranych bajtów pozwala trafić w dowolną wskazaną wartość docelową CRC-32. Podpisy wymagają tajnego klucza i konstrukcji nieliniowej. Trzeba użyć HMAC z SHA-256.

Czy z wartości CRC da się odzyskać oryginalne dane?

Nie, oryginalnych danych nie da się odzyskać z wartości CRC. CRC-16 ściska dowolne wejście do 16 bitów, więc każdą wartość dzieli nieskończenie wiele wiadomości. Kierunek odwrotny jest jednak dla napastnika użyteczny: mając wartość docelową, można skonstruować wiadomość, która ją produkuje, i właśnie dlatego CRC niczego nie uwierzytelnia.

Dokument producenta mówi tylko „CRC-16”. Jak ustalić parametry?

Najprościej poprosić o jedną przechwyconą ramkę plus CRC, które wyliczyło dla niej ich urządzenie, a potem puścić na tę parę 32-kombinacyjne przemiatanie z sekcji 4. Jeśli przetrwa więcej niż jeden zestaw parametrów, trzeba powtórzyć z drugą ramką i zostawić część wspólną. Dwie próbki prawie zawsze rozstrzygają.

Tagi: crc checksum modbus embedded data-integrity

Powiązane artykuły

Zobacz wszystkie artykuły