7

我在 C 中有这段代码,需要移植到 C#:

void CryptoBuffer(unsigned char *Buffer, unsigned short length)
{
    unsigned short i;
    for(i=0; i < length; i++)
    {
        *Buffer ^= 0xAA;
        *Buffer++ += 0xC9;
    }
}

我试过这个:

public void CryptoBuffer(byte[] buffer, int length)
{
    for(int i = 0; i < length; i++)
    {
        buffer[i] ^= 0xAA;
        buffer[i] += 0xC9;
    }
}

但结果与预期不符。

根据示例,这是:

A5 03 18 01...

应该变成这样:

A5 6F 93 8B...

它还说第一个字节没有加密,所以这就是 A5 保持不变的原因。

编辑澄清:规范只是说你应该跳过第一个字节,它没有详细说明,所以我猜你只是传递从位置 1 到最后一个位置的序列来跳过第一个字节。

但是我对那个 C# 端口的结果是:

A5 72 7B 74...

这个端口是正确的还是我错过了什么?

编辑2:为了进一步澄清,这是一个封闭的协议,所以我不能详细说明,这就是为什么我提供了足够的信息来帮助我移植代码,C代码是给我的,那就是规范所说的它会做什么。真正的问题是规范中的“0xAA”是错误的,这就是为什么输出不是预期的。此处提供的 C# 代码和接受的答案毕竟是正确的。

4

7 回答 7

9

让我们一次一步地分解它。

void CryptoBuffer(unsigned char *Buffer, unsigned short length)
{
    unsigned short i;
    for(i=0; i < length; i++)
    {
        *Buffer ^= 0xAA;
        *Buffer++ += 0xC9;
    }
}

不管其他一些评论如何,这就是您通常在 C/C++ 中执行这些操作的方式。这段代码没有什么花哨的,也不是太复杂,但我认为最好将其分解以向您展示发生了什么。

注意事项:

  1. unsigned char 与 c# 中的 byte 基本相同
  2. 无符号长度的值介于 0-65536 之间。Int 应该可以解决问题。
  3. 缓冲区有一个后增量
  4. 字节分配 (+= 0xC9) 将溢出。如果它溢出,在这种情况下它会被截断为 8 位。
  5. 缓冲区由 ptr 传递,因此调用方法中的指针将保持不变。
  6. 这只是基本的 C 代码,没有 C++。假设人们在这里不使用运算符重载是非常安全的。

这里唯一“困难”的是 Buffer++。可以在 Sutter 的“Exceptional C++”一书中阅读详细信息,但也有一个小示例对此进行了说明。幸运的是,我们有一个完美的例子。上述代码的直译为:

void CryptoBuffer(unsigned char *Buffer, unsigned short length)
{
    unsigned short i;
    for(i=0; i < length; i++)
    {
        *Buffer ^= 0xAA;
        unsigned char *tmp = Buffer;
        *tmp += 0xC9;
        Buffer = tmp + 1;
    }
}

在这种情况下,临时变量可以很容易地解决,这导致我们:

void CryptoBuffer(unsigned char *Buffer, unsigned short length)
{
    unsigned short i;
    for(i=0; i < length; i++)
    {
        *Buffer ^= 0xAA;
        *Buffer += 0xC9;
        ++Buffer;
    }
}

现在将此代码更改为 C# 非常简单:

private void CryptoBuffer(byte[] Buffer, int length)
{
    for (int i=0; i<length; ++i) 
    {
        Buffer[i] = (byte)((Buffer[i] ^ 0xAA) + 0xC9);
    }
}

这与您移植的代码基本相同。这意味着在路上的某个地方出现了其他问题......所以让我们破解密码缓冲区好吗?:-)

如果我们假设不使用第一个字节(如您所述)并且“0xAA”和/或“0xC9”是错误的,我们可以简单地尝试所有组合:

static void Main(string[] args)
{
    byte[] orig = new byte[] { 0x03, 0x18, 0x01 };
    byte[] target = new byte[] { 0x6F, 0x93, 0x8b };

    for (int i = 0; i < 256; ++i)
    {
        for (int j = 0; j < 256; ++j)
        {
            bool okay = true;
            for (int k = 0; okay && k < 3; ++k)
            {
                byte tmp = (byte)((orig[k] ^ i) + j);
                if (tmp != target[k]) { okay = false; break; }
            }
            if (okay)
            {
                Console.WriteLine("Solution for i={0} and j={1}", i, j);
            }
        }
    }
    Console.ReadLine();
}

我们走了:哎呀,没有解决方案。这意味着加密缓冲区没有做你认为它正在做的事情,或者这里缺少部分 C 代码。前任 他们真的将“缓冲区”传递给 CryptoBuffer 方法还是他们之前更改了指针?

最后,我认为这里唯一好的答案是缺少解决这个问题的关键信息。

于 2013-03-08T21:36:39.810 回答
5

您提供的示例与 C 示例中的代码不一致,C 和 C# 代码产生相同的结果。

于 2013-03-04T22:36:26.880 回答
3

移植看起来正确;你能解释一下为什么03应该变成6F吗?结果似乎偏离了 03 的“预期”值这一事实对我来说有点可疑。

于 2013-03-04T21:53:06.100 回答
1

港口看起来不错。

在这种情况下,我会拿出一张纸和一支笔,用二进制写出字节,进行 XOR,然后加法。现在将其与 C 和 C# 代码进行比较。

于 2013-03-04T21:50:01.293 回答
1

在 C# 中,您正在溢出字节,因此它被截断为 0x72。这是将 0x03 转换为二进制和十六进制的数学运算:

   00000011   0x003
^  10101010   0x0AA
=  10101001   0x0A9
+  11001001   0x0C9
= 101110010   0x172
于 2013-03-04T21:52:40.733 回答
0

使用 C 中的原始方法,我们首先假设序列以对称方式解密/加密,并调用CryptoBuffer

  • 最初调用a5 03 18 01 ...

    a5 03 18 01 ... => d8 72 7b 74 ... 
    

    然后上d8 72 7b 74 ...

    d8 72 7b 74 ... => 3b a1 9a a7 ...
    
  • 最初调用a5 6f 93 8b ...

    a5 6f 93 8b ... => d8 8e 02 ea ...
    

    然后上d8 8e 02 ea ...

    d8 8e 02 ea ... => 3b ed 71 09 ... 
    

我们知道这是不可行的。

当然,您可能有非对称解密方法;但首先,我们需要a5 03 18 01 ... => a5 6f 93 8b ...用任何可能的幻数证明其中一个或相反的方向。使用蛮力方法进行分析的代码放在帖子的后面。

我使幻数成为测试的变量。通过重现性分析,我们发现原始序列可以在连续变化的幻数上每 256 次调用重现。好的,按照我们所经历的,这里仍然是可能的。

然而,可行性分析测试256*256=65536了两个方向的所有案例,从original => expectedexpected => original,并且没有。

现在我们知道没有办法将加密序列解密为预期的结果。

因此,我们只能说两种语言或您的代码的预期行为是相同的,但是由于假设被打破,所以预期结果是不可能的。


分析代码

public void CryptoBuffer(byte[] buffer, ushort magicShort) {
    var magicBytes=BitConverter.GetBytes(magicShort);
    var count=buffer.Length;

    for(var i=0; i<count; i++) {
        buffer[i]^=magicBytes[1];
        buffer[i]+=magicBytes[0];
    }
}

int Analyze(
    Action<byte[], ushort> subject,
    byte[] expected, byte[] original,
    ushort? magicShort=default(ushort?)
    ) {
    Func<byte[], String> LaHeX= // narrowing bytes to hex statement
        arg => arg.Select(x => String.Format("{0:x2}\x20", x)).Aggregate(String.Concat);

    var temporal=(byte[])original.Clone();
    var found=0;

    for(var i=ushort.MaxValue; i>=0; --i) {
        if(found>255) {
            Console.WriteLine(": might found more than the number of square root; ");
            Console.WriteLine(": analyze stopped ");
            Console.WriteLine();
            break;
        }

        subject(temporal, magicShort??i);

        if(expected.SequenceEqual(temporal)) {
            ++found;
            Console.WriteLine("i={0:x2}; temporal={1}", i, LaHeX(temporal));
        }

        if(expected!=original)
            temporal=(byte[])original.Clone();
    }

    return found;
}

void PerformTest() {
    var original=new byte[] { 0xa5, 0x03, 0x18, 0x01 };
    var expected=new byte[] { 0xa5, 0x6f, 0x93, 0x8b };

    Console.WriteLine("--- reproducibility analysis --- ");
    Console.WriteLine("found: {0}", Analyze(CryptoBuffer, original, original, 0xaac9));
    Console.WriteLine();

    Console.WriteLine("--- feasibility analysis --- ");
    Console.WriteLine("found: {0}", Analyze(CryptoBuffer, expected, original));
    Console.WriteLine();

    // swap original and expected
    var temporal=original;
    original=expected;
    expected=temporal;

    Console.WriteLine("--- reproducibility analysis --- ");
    Console.WriteLine("found: {0}", Analyze(CryptoBuffer, original, original, 0xaac9));
    Console.WriteLine();

    Console.WriteLine("--- feasibility analysis --- ");
    Console.WriteLine("found: {0}", Analyze(CryptoBuffer, expected, original));
    Console.WriteLine();
}
于 2013-03-08T11:54:01.837 回答
0

这是一个演示

http://codepad.org/UrX0okgu

显示给定输入的原始代码A5 03 18 01产生D8 72 7B 01;所以

  1. 仅当缓冲区从第 2 个开始发送时,第一个字节未解码的规则才是正确的(向我们展示调用)

  2. 输出不匹配(您是否错过其他电话?)

所以你的翻译是正确的,但你对原始代码的期望却不是。

于 2013-03-14T14:44:21.333 回答