バイナリ解析した際に利用したコードの備忘録

# はじめに
特定のデータファイルなどを改変したい際、その構造を操作する方法が提供されていない場合、最終的にはバイナリ解析して改変することになるかと思います。
チェックサムなどがある場合、その方法も解析が必要になってくるのだと思いますが、ここでは単純なデータ構造のバイナリファイルを解析した際に使用した、C#のコードについての備忘録です。
なお、コードは実際のものではなく簡略化した内容としました。

# 解析について
きわめて原始的な方法で実施していました。
一部を変更し、出力されたファイルと変更前のファイルとをWinMargeで比較し、変わった部分の前後から判断して行くだけです。
今回扱ったデータはそのデータが何であるのかのID、そのデータの長さ、データといった構造になっていました。
単純な構造ですので、データの見方がわかってしまえば簡単です。

# バイト配列の操作
バイト配列を操作する簡単なクラスを作成します。
このままだとあまり`BinaryReader`と変わりませんが、必要に応じて改変していきます。

```C#
public class MyBytes
{
    private List<byte> Bytes { get; set; } = new List<byte>();
    private int Pos { get; set; }
    public MyBytes(string filePath)
    {
        using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
        using (BinaryReader br = new BinaryReader(fs))
        {
            Bytes.AddRange(br.ReadBytes((int)fs.Length));
        }
        Pos = 0;
    }
    public byte[] ReadBytes(int count, int offsetPos = 0, bool readNext = true)
    {
        List<byte> result = Bytes.GetRange(Pos + offsetPos, count);
        if (next)
            readNext += offsetPos + count;

        return result.ToArray();
    }
    public bool EOF { get { return Bytes.Count <= Pos; } }
}
```

# よく使う型のクラス
例えば、データの長さを示す形式がint型なら、int型を操作するクラスを作成します。

```C#
[System.Diagnostics.DebuggerDisplay("{Value} ({System.BitConverter.ToString(ToBytes()).Replace(\"-\", \" \")})")]
public class MyInt : IEnumerable<byte>
{
    public int Value { get; set; }
    public MyInt(MyBytes bytes)
    {
        Value = BitConverter.ToInt32(bytes.ReadBytes(4), 0);
    }
    public MyInt(int value)
    {
        Value = value;
    }
    public byte[] ToBytes()
    {
        return BitConverter.GetBytes(Value);
    }
    public static implicit operator int(MyInt obj)
    {
        return obj.Value;
    }
    public static implicit operator byte[](MyInt obj)
    {
        return obj.ToBytes();
    }
    public static implicit operator MyInt(MyBytes bytes)
    {
        return new MyInt(bytes);
    }
    public static implicit operator MyInt(int value)
    {
        return new MyInt(value);
    }
    public IEnumerator<byte> GetEnumerator()
    {
        return ToBytes().ToList<byte>().GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return ToBytes().GetEnumerator();
    }
}
```

ポイント

- デバッグ時にウォッチで見やすくするように `DebuggerDisplay` を定義
- 要求する型に合わせて動作するように `implicit` を定義
- 繰り返しの要求用に `IEnumerable` を定義
- `MyBytes`からデータを読み進めるため、`MyBytes`での使用前提のクラス

# 解析用のクラス
解析した結果データ構造が判明した場合、各データ項目を操作しやすくするクラスを作成します。

```C#
public class MyData
{
    public MyBytes_Int DataID { get; set; }
    public MyBytes_Int Length { get; set; }
    public byte[] Data { get; set; }
    public EspB_Short DataSize { get; set; }

    public MyData(MyBytes bytes)
    {
        DataID = bytes;
        Length = bytes;
        Data = bytes.ReadBytes(Length);
    }
    public byte[] ToBytes()
    {
        List<byte> result = new List<byte>();
        b.AddRange(DataID);
        b.AddRange(Length);
        b.AddRange(Data);
        return result.ToArray();
    }
}
```

後は上記のクラスを基準にデータ構造に合わせたクラスを順次作成して行きます。

# データの読み込みと書き出し
一通りデータの解析が済みましたら、データを読込み、必要な操作を行い、ファイル出力します。

```C#
MyBytes bytes = new MyBytes(@"C:\Hoge.xxx");
List<MyData> list = new List<MyData>();
while (bytes.EOF)
{
    list.Add(new MyData(bytes));
}

/* （何らかの改変） */

using (FileStream fs = new FileStream(@"C:\Hoge2.xxx", FileMode.Create))
using (BinaryWriter bw = new BinaryWriter(fs))
{
    foreach (var item in list)
    {
        bw.Write(item.ToBytes());
    }
}
```

# 最後に
実際にはもう少し複雑ですが、このような形で進めていました。
やってみれば意外にバイナリ解析は簡単なもので、後は地道な作業です。
クラス設計的に、`MyInt`は`MyBytes`からデータを読み出すこと前提で作っていたりなど、設計思想的には好ましくない部分が多いのかもしれませんが、公式に公開するものではないので、とりあえずやりやすいように作ったものです。


以上