纵向冗余检查失败

发布于 2024-09-02 21:25:09 字数 3935 浏览 7 评论 0原文

我有一个可以解码来自磁条读取器的数据的应用程序。但是,我很难让计算出的 LRC 检查字节与卡片上的字节匹配。如果我要抓 3 张卡,每张卡有 3 个轨道,我猜下面的算法将适用于这些卡中 9 个轨道中的 4 个。

我使用的算法如下所示 (C#):

private static char GetLRC(string s, int start, int end)
{
    int result = 0;
    for (int i = start; i <= end; i++)
    {
        result ^= Convert.ToByte(s[i]);
    }
    return Convert.ToChar(result);
}

这是未通过检查的磁道 3 数据的示例。在这张卡上,轨道 2 匹配,但轨道 1 也失败。

   0 1 2 3 4 5 6 7  8 9 A B C D E F
00 3 4 4 4 4 4 4 4  4 4 4 5 5 5 5 5 
10 5 5 5 5 5 6 6 6  6 6 6 6 6 6 6 7 
20 7 7 7 7 7 7 7 7  7 8 8 8 8 8 8 8 
30 8 8 8 9 9 9 9 9  9 9 9 9 9 0 0 0 
40 0 0 0 0 0 0 0 1  2 3 4 1 1 1 1 1 
50 1 1 1 1 1 2 2 2  2 2 2 2 2 2 2 3 
60 3 3 3 3 3 3 3 3 

扇区分隔符是“;”它以“?”结尾。

该轨道的 LRC 字节是 0x30。不幸的是,上面的算法根据以下计算计算出 0x00 的 LRC(对其长度表示歉意。我想彻底):

00 ^ 3b = 3b ';'
3b ^ 33 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 31 = 39
39 ^ 32 = 0b
0b ^ 33 = 38
38 ^ 34 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 3f = 00 '?'

如果有人能指出如何修复我的算法,我将不胜感激。

谢谢, PaulH


编辑:

这样你就可以看到我是否在 LRC 计算中意外丢失了任何字节或包含了错误的字节(最后的“.”实际上是“\r”)。来自所有三个轨道的完整数据:

   0 1 2 3 4 5 6 7  8 9 A B C D E F
00 % U V W X Y Z 0  1 2 3 4 5 6 7 8 
10 9 9 A B C D E F  G H I J K L M N 
20 O P Q R S T U V  W X Y Z 1 2 3 0 
30 1 2 3 4 5 6 7 8  9 A B C D E F G 
40 H I J K L M N O  P Q R S T ? 3 ; 
50 1 2 3 4 5 6 7 1  2 3 4 5 6 7 8 9 
60 0 1 2 3 4 5 6 7  8 9 0 1 2 3 4 5 
70 6 7 8 9 0 ? 5 ;  3 4 4 4 4 4 4 4 
80 4 4 4 5 5 5 5 5  5 5 5 5 5 6 6 6 
90 6 6 6 6 6 6 6 7  7 7 7 7 7 7 7 7 
A0 7 8 8 8 8 8 8 8  8 8 8 9 9 9 9 9 
B0 9 9 9 9 9 0 0 0  0 0 0 0 0 0 0 1 
C0 2 3 4 1 1 1 1 1  1 1 1 1 1 2 2 2 
D0 2 2 2 2 2 2 2 3  3 3 3 3 3 3 3 3 
E0 ? 0 .

按照建议重新检测 GetLRC() 算法,仅对出现奇数次的异或字节:

private static char GetLRC(string s, int start, int end)
{
    int result = 0;

    byte cur_byte = Convert.ToByte(s[start]);

    int count = 0;
    for (int i = start; i <= end; i++)
    {
        byte b = Convert.ToByte(s[i]);
        if (cur_byte != b)
        {
            if (count % 2 != 0)
            {
                result ^= cur_byte;
            }
            cur_byte = b;
            count = 0;
        }
        ++count;
    }

    if (count % 2 != 0)
    {
        result ^= cur_byte;
    }

    return Convert.ToChar(result);
}

GetLRC() 采取的计算步骤 功能:

00 ^ 3b = 3b ';'
3b ^ 33 = 08
08 ^ 31 = 39
39 ^ 32 = 0b
0b ^ 33 = 38
38 ^ 34 = 0c
0c ^ 33 = 3f
3f ^ 3f = 00 '?'

问:LRC字节是来自卡本身还是由读卡器固件添加? (即也许这是一个固件错误)

I have an application that decodes data from a magnetic stripe reader. But, I'm having difficulty getting my calculated LRC check byte to match the one on the cards. If I were to grab 3 cards each with 3 tracks, I would guess the algorithm below would work on 4 of the 9 tracks in those cards.

The algorithm I'm using looks like this (C#):

private static char GetLRC(string s, int start, int end)
{
    int result = 0;
    for (int i = start; i <= end; i++)
    {
        result ^= Convert.ToByte(s[i]);
    }
    return Convert.ToChar(result);
}

This is an example of track 3 data that fails the check. On this card, track 2 matched, but track 1 also failed.

   0 1 2 3 4 5 6 7  8 9 A B C D E F
00 3 4 4 4 4 4 4 4  4 4 4 5 5 5 5 5 
10 5 5 5 5 5 6 6 6  6 6 6 6 6 6 6 7 
20 7 7 7 7 7 7 7 7  7 8 8 8 8 8 8 8 
30 8 8 8 9 9 9 9 9  9 9 9 9 9 0 0 0 
40 0 0 0 0 0 0 0 1  2 3 4 1 1 1 1 1 
50 1 1 1 1 1 2 2 2  2 2 2 2 2 2 2 3 
60 3 3 3 3 3 3 3 3 

The sector delimiter is ';' and it ends with a '?'.

The LRC byte from this track is 0x30. Unfortunately, the algorithm above computes an LRC of 0x00 per the following calculation (apologies for its length. I want to be thorough):

00 ^ 3b = 3b ';'
3b ^ 33 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 34 = 3c
3c ^ 34 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 35 = 3d
3d ^ 35 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 36 = 3e
3e ^ 36 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 37 = 3f
3f ^ 37 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 38 = 30
30 ^ 38 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 39 = 31
31 ^ 39 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 30 = 38
38 ^ 30 = 08
08 ^ 31 = 39
39 ^ 32 = 0b
0b ^ 33 = 38
38 ^ 34 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 31 = 3d
3d ^ 31 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 32 = 3e
3e ^ 32 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 33 = 0c
0c ^ 33 = 3f
3f ^ 3f = 00 '?'

If anybody can point out how to fix my algorithm, I would appreciate it.

Thanks,
PaulH


Edit:

So that you can see if I'm accidentally missing any bytes in my LRC calculation or including the wrong ones (the final '.' is actually a '\r'). The complete data from all three tracks:

   0 1 2 3 4 5 6 7  8 9 A B C D E F
00 % U V W X Y Z 0  1 2 3 4 5 6 7 8 
10 9 9 A B C D E F  G H I J K L M N 
20 O P Q R S T U V  W X Y Z 1 2 3 0 
30 1 2 3 4 5 6 7 8  9 A B C D E F G 
40 H I J K L M N O  P Q R S T ? 3 ; 
50 1 2 3 4 5 6 7 1  2 3 4 5 6 7 8 9 
60 0 1 2 3 4 5 6 7  8 9 0 1 2 3 4 5 
70 6 7 8 9 0 ? 5 ;  3 4 4 4 4 4 4 4 
80 4 4 4 5 5 5 5 5  5 5 5 5 5 6 6 6 
90 6 6 6 6 6 6 6 7  7 7 7 7 7 7 7 7 
A0 7 8 8 8 8 8 8 8  8 8 8 9 9 9 9 9 
B0 9 9 9 9 9 0 0 0  0 0 0 0 0 0 0 1 
C0 2 3 4 1 1 1 1 1  1 1 1 1 1 2 2 2 
D0 2 2 2 2 2 2 2 3  3 3 3 3 3 3 3 3 
E0 ? 0 .

The GetLRC() algorithm re-instrumented as suggested to only XOR bytes that appear an odd number of times:

private static char GetLRC(string s, int start, int end)
{
    int result = 0;

    byte cur_byte = Convert.ToByte(s[start]);

    int count = 0;
    for (int i = start; i <= end; i++)
    {
        byte b = Convert.ToByte(s[i]);
        if (cur_byte != b)
        {
            if (count % 2 != 0)
            {
                result ^= cur_byte;
            }
            cur_byte = b;
            count = 0;
        }
        ++count;
    }

    if (count % 2 != 0)
    {
        result ^= cur_byte;
    }

    return Convert.ToChar(result);
}

The calculation steps taken by the new GetLRC() function:

00 ^ 3b = 3b ';'
3b ^ 33 = 08
08 ^ 31 = 39
39 ^ 32 = 0b
0b ^ 33 = 38
38 ^ 34 = 0c
0c ^ 33 = 3f
3f ^ 3f = 00 '?'

Question: Does the LRC byte come from the card itself or is it being added by the reader firmware? (i.e. perhaps this is a firmware bug)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

逐鹿 2024-09-09 21:25:12

LRC的算法已修正,但计算LRC的数据格式可能错误。
(这取决于您的 MSR 读卡器)

ANSI/ISO 定义了两种磁道格式(Alpha 和 BCD)。
二进制的编码与 ASCII 不同。

在本例中,起始标记为“;” ,所以格式应该是BCD。
(Alpha 起始标记为 '%')

LRC 使用“真实磁道数据”来计算(不包括奇偶校验位),

Convert rule 
ASCII to BCD ->(ASCII - 0x30) 

    --Data Bits--    Parity 
    b1  b2  b3  b4   b5      Character  Function

    0   0   0   0    1        0 (0H)    Data
    1   0   0   0    0        1 (1H)      " 
    0   1   0   0    0        2 (2H)      " 
    1   1   0   0    1        3 (3H)      " 
    0   0   1   0    0        4 (4H)      " 
    1   0   1   0    1        5 (5H)      " 
    0   1   1   0    1        6 (6H)      " 
    1   1   1   0    0        7 (7H)      " 
    0   0   0   1    0        8 (8H)      " 
    1   0   0   1    1        9 (9H)      " 
    0   1   0   1    1        : (AH)    Control
    1   1   0   1    0        ; (BH)    Start Sentinel
    0   0   1   1    1        < (CH)    Control
    1   0   1   1    0        = (DH)    Field Separator
    0   1   1   1    0        > (EH)    Control
    1   1   1   1    1        ? (FH)    End Sentinel

在您的示例中,

  1. 将 ASCII 磁道数据转换为 BCD 格式。
  2. 使用BCD数据计算LRC,结果为0x00。
  3. 然后转换LRC(BCD转ASCII),最终得到LRC = 0x30。

PS ASCII 转换为 Alpha

    if(bASCII >= 0x20 && bASCII <= 0x5B)
    {
        return(bASCII - 0x20);
    }
    else if(bASCII >= 0x5C && bASCII <= 0x5F)
    {
        return(bASCII - 0x1F);
    }

Algorithm about LRC is corrected, but the format of data to calculate LRC maybe wrong.
(it depends on your MSR reader)

There are two format of track define by ANSI/ISO (Alpha and BCD).
The coding of binary is different to ASCII.

In this case, start sentinel is ';' ,so the format should be BCD.
(Alpha start sentinel is '%')

LRC is use "Real track data" to calculate (not include parity bit),

Convert rule 
ASCII to BCD ->(ASCII - 0x30) 

    --Data Bits--    Parity 
    b1  b2  b3  b4   b5      Character  Function

    0   0   0   0    1        0 (0H)    Data
    1   0   0   0    0        1 (1H)      " 
    0   1   0   0    0        2 (2H)      " 
    1   1   0   0    1        3 (3H)      " 
    0   0   1   0    0        4 (4H)      " 
    1   0   1   0    1        5 (5H)      " 
    0   1   1   0    1        6 (6H)      " 
    1   1   1   0    0        7 (7H)      " 
    0   0   0   1    0        8 (8H)      " 
    1   0   0   1    1        9 (9H)      " 
    0   1   0   1    1        : (AH)    Control
    1   1   0   1    0        ; (BH)    Start Sentinel
    0   0   1   1    1        < (CH)    Control
    1   0   1   1    0        = (DH)    Field Separator
    0   1   1   1    0        > (EH)    Control
    1   1   1   1    1        ? (FH)    End Sentinel

In your sample,

  1. Convert ASCII track data to BCD format.
  2. Use BCD data to calculate LRC, the result is 0x00.
  3. Then convert LRC(BCD to ASCII), finally got LRC = 0x30.

P.S. ASCII convert to Alpha

    if(bASCII >= 0x20 && bASCII <= 0x5B)
    {
        return(bASCII - 0x20);
    }
    else if(bASCII >= 0x5C && bASCII <= 0x5F)
    {
        return(bASCII - 0x1F);
    }
凉风有信 2024-09-09 21:25:12

您的算法与维基百科文章中的 LRC 算法不匹配。您确定您使用的是正确的算法吗?

Your algorithm doesn't match the LRC algorithm in Wikipedia's article. Are you sure you're using the correct algorithm?

探春 2024-09-09 21:25:11

我可以提个建议吗?将数据存储为游程长度,并且仅在游程长度为奇数时才执行异或 - 然后仅执行一次 (runLength & 0x01) 次。这将消除大量无价值的工作,并让人们更清楚地了解正在发生的事情。这样做的结果是:


Run Lengths:
(01,3b)(01,33)(10,34)(10,35)(10,36)(10,37)(10,38)(10,39)(10,30)
(01,31)(01,32)(01,33)(01,34)(10,31)(10,32)(09,33)(1,3f)

做偶数/奇数的事情给出:


3b ^ 33 ^ 31 ^ 32 ^ 33 ^ 34 ^ 33 ^ 3f
        08-->39-->0B-->38-->0C-->3F-->00

看起来更简单、更清晰。我的猜测是,查看您的数据,您的数据流中某处存在额外的 30 个或 1 个短路。添加额外的 30 个即可得到答案:


3b ^ 33 ^ 31 ^ 32 ^ 33 ^ 34 ^ 33 ^ 30 ^ 3F
        08-->39-->0B-->38-->0C-->3F-->0F-->30

除此之外,我将继续挖掘...

您可以向输入参数添加一些断言或其他验证吗?我不想看到越界开始/结束导致兴奋和/或空字符串。另外,有没有可能以一比一结束?包含/排除数据范围?这可能会导致数据末尾有一个额外的 0x030,从存储在磁道 3 末尾的 0 转换为 0x30。另外,是否有可能出现数据损坏或 LRU 损坏的情况?显然,这就是您的检查试图捕获的类型。也许它抓住了什么?

Can I make a suggestion? Store your data as run lengths and only do the xor if the run length is odd - and then only do it once (runLength & 0x01) times. That will get rid of a ton of the worthless bit work and make it clearer on what is occuring. Doing that yields:


Run Lengths:
(01,3b)(01,33)(10,34)(10,35)(10,36)(10,37)(10,38)(10,39)(10,30)
(01,31)(01,32)(01,33)(01,34)(10,31)(10,32)(09,33)(1,3f)

Doing the even/odd thing gives:


3b ^ 33 ^ 31 ^ 32 ^ 33 ^ 34 ^ 33 ^ 3f
        08-->39-->0B-->38-->0C-->3F-->00

Much simpler and cleaner to look at. My guess is that looking at your data, that there is an extra 30 somewhere in your data stream or 1 short. Adding that extra 30 gets you your answer:


3b ^ 33 ^ 31 ^ 32 ^ 33 ^ 34 ^ 33 ^ 30 ^ 3F
        08-->39-->0B-->38-->0C-->3F-->0F-->30

Beyond that, I'll keep digging...

Can you add some asserts or other validation to your input parameters? I'd hate to see out of bounds start/end causing excitement and/or a null string. Also, is there a possibility of an off by one with start end? Inclusive/exclusive data range? That could account for an extra 0x030 at the end of your data from a 0 stored at the end of your track 3 being converted to a 0x30. Also, is there any possibility of having either corrupt data or a corrupt LRU? Obviously, this is the kind of thing your check is trying to catch. Perhaps it caught something?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文