将C中包含无符号char指针的代码移植到c#

本文关键字:代码 指针 包含 无符号 char | 更新日期: 2023-09-27 18:02:05

我在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...

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

EDIT 2:为了进一步澄清,这是一个封闭的协议,所以我不能详细说明,这就是为什么我只提供了足够的信息来帮助我移植代码,C代码是给我的,这就是规范说它会做的。真正的问题是规范中的"0xAA"是错误的,这就是输出不是预期输出的原因。这里提供的c#代码和公认的答案毕竟是正确的。

将C中包含无符号char指针的代码移植到c#

让我们慢慢来,一步一步来。
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#
  2. 中的byte基本相同
  3. unsigned length的取值范围是0-65536。Int应该可以。
  4. Buffer有一个后增量
  5. 字节赋值(+= 0xC9)将溢出。如果它溢出,在这种情况下它被截断为8位。
  6. buffer由ptr传递,所以调用方法中的指针将保持不变。
  7. 这只是基本的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;
    }
}

在这种情况下,可以简单地求解temp变量,这导致我们:

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代码。F.ex。他们真的把'Buffer'传递给CryptoBuffer方法,还是他们之前改变了指针?

总之,我认为唯一好的答案是解决这个问题的关键信息缺失了。

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

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

在这种情况下,我要做的是拿出一张纸和一支笔,写出二进制的字节,做异或运算,然后做加法。现在将其与C和c#代码进行比较。

在c#中,您溢出了字节,因此它被截断为0x72。下面是将0x03转换为二进制和十六进制的数学方法:

   00000011   0x003
^  10101010   0x0AA
=  10101001   0x0A9
+  11001001   0x0C9
= 101110010   0x172

对于C中的原始方法,我们首先假设序列以对称的方式通过调用CryptoBuffer

进行解密/加密。
  • 初始调用a5 03 18 01 ...

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

    then on d8 72 7b 74 ...

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

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

    then on d8 8e 02 ea ...

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

我们知道这是不可行的。

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

我将幻数作为测试的变量。通过可再现性分析,我们发现在连续变化的幻数上,每256次调用可以再现原始序列。好吧,我们经历了这么多,还是有可能的。

然而,可行性分析original => expectedexpected => original两个方向测试所有256*256=65536案例,没有一个成功。

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

因此,我们只能告诉两种语言或代码的预期行为是相同的,但对于预期结果是不可能的,因为假设被打破了。

分析代码

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();
}

这里有一个演示

http://codepad.org/UrX0okgu

表示原代码在输入A5 03 18 01时生成D8 72 7B 01;所以

  1. 第一个字节不被解码的规则只有当缓冲区从第2(显示我们的调用)开始发送时才能正确

  2. 输出不匹配(您错过了其他呼叫吗?)

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