Вы должны войти, чтобы создавать сообщения и темы.

Поиск и изменение с увеличением или уменьшением в бинарном файле [Решено]

Поиск и изменение с увеличением или уменьшением в бинарном файле.
Здравствуйте. Хочется автоматизировать 1 задачу, которую довольно часто приходится выполнять в шестнадцатеричном редакторе для файла. (Скажем так. Написать свой чит к 1 игре, а именно автоматизированный редактор сохранений).
Но вот не могу найти инфы по тому, как вообще можно делать поиск и уж тем более заменять найденное значение на большее или меньшее. Из того что логично предположить это работать с массивом байтов. Может кто показать желательно на примере.
Например, в бинарном файле есть такой набор байтов в такой последовательности:
08 00 00 00 67 6F 6C 64 3D 35 32 31
И заменить на такой:
0B 00 00 00 67 6F 6C 64 3D 39 39 39 39 39 39


Комментарий модератора:
Для перехода на новую строку используйте просто Enter, а не Shift+Enter

Добрый день.
Для чтения и записи используйте BinaryWriter и BinaryReader. Как ими пользоваться загуглите сами.
Для быстрого поиска бинарных сигнатур есть класс, ссылку на который дам позже, когда найду его исходник.

Например, в бинарном файле есть такой набор байтов в такой последовательности:
08 00 00 00 67 6F 6C 64 3D 35 32 31
И заменить на такой:
0B 00 00 00 67 6F 6C 64 3D 39 39 39 39 39 39

А вы тут точно ничего не путаете? Обычно в бинарных данных не допускается изменение длины. Можно ссылку на данный мануал, по которому вы это делаете?

P.S. Например вот. Или можете поискать другие реализации алгоритма Boyer-Moore.
Так же где-то был исходник поиска по маске, но пока не вспомню, где я его использовал, что бы найти в исходниках.

В одном проекте использовал еще вот такой класс:

Поиск в потоке первого вхождения

public static class Search
    {
        /// <summary>
        /// Finds the first occurrence of <paramref name="pattern"/> in a stream
        /// </summary>
        /// <param name="s">The input stream</param>
        /// <param name="pattern">The pattern</param>
        /// <returns>The index of the first occurrence, or -1 if the pattern has not been found</returns>
        public static long IndexOf(Stream s, byte[] pattern)
        {
            // Prepare the bad character array is done once in a separate step
            var badCharacters = MakeBadCharArray(pattern);

            // We now repeatedly read the stream into a buffer and apply the Boyer-Moore-Horspool algorithm on the buffer until we get a match
            var buffer = new byte[Math.Max(2 * pattern.Length, 4096)];
            long offset = 0; // keep track of the offset in the input stream
            while (true)
            {
                int dataLength;
                if (offset == 0)
                {
                    // the first time we fill the whole buffer
                    dataLength = s.Read(buffer, 0, buffer.Length);
                }
                else
                {
                    // Later, copy the last pattern.Length bytes from the previous buffer to the start and fill up from the stream
                    // This is important so we can also find matches which are partly in the old buffer
                    Array.Copy(buffer, buffer.Length - pattern.Length, buffer, 0, pattern.Length);
                    dataLength = s.Read(buffer, pattern.Length, buffer.Length - pattern.Length) + pattern.Length;
                }

                var index = IndexOf(buffer, dataLength, pattern, badCharacters);
                if (index >= 0)
                    return offset + index; // found!
                if (dataLength < buffer.Length)
                    break;
                offset += dataLength - pattern.Length;
            }

            return -1;
        }

        // --- Boyer-Moore-Horspool algorithm ---
        // (Slightly modified code from
        // https://stackoverflow.com/questions/16252518/boyer-moore-horspool-algorithm-for-all-matches-find-byte-array-inside-byte-arra)
        // Prepare the bad character array is done once in a separate step:
        private static int[] MakeBadCharArray(byte[] pattern)
        {
            var badCharacters = new int[256];

            for (long i = 0; i < 256; ++i)
                badCharacters[i] = pattern.Length;

            for (var i = 0; i < pattern.Length - 1; ++i)
                badCharacters[pattern[i]] = pattern.Length - 1 - i;

            return badCharacters;
        }

        // Core of the BMH algorithm
        private static int IndexOf(byte[] value, int valueLength, byte[] pattern, int[] badCharacters)
        {
            int index = 0;

            while (index <= valueLength - pattern.Length)
            {
                for (var i = pattern.Length - 1; value[index + i] == pattern[i]; --i)
                {
                    if (i == 0)
                        return index;
                }

                index += badCharacters[value[index + pattern.Length - 1]];
            }

            return -1;
        }
    }


А вот и для поиска по маске нашел:
Поиск по маске

public static class Searcher
    {
        private const string _q = "?";
        private static int largest(ref string[][] list)
        {
            int size = 0;
            for (int i = 0; i < list.Length; i++)
                size = (list[i].Length > size) ? list[i].Length : size;
            return size;
        }
        public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, string wildcard, bool onlyOne)
        {
            int sl = searchBytes.Length;
            if (sl < 1 || bytes.Length < sl)
                return new int[] { };
            byte[] sbytes = new byte[sl];
            for (int i = 0; i < sl; i++)
                if (searchBytes[i] != wildcard)
                    if (!Byte.TryParse(searchBytes[i], System.Globalization.NumberStyles.HexNumber, null, out sbytes[i]))
                        return new int[] { };

            List<int> locs = new List<int>();
            int n = bytes.Length - sl;
            int found = 0;
            for (var i = 0; i <= n; i++)
            {
                for (int j = 0; j < sl; j++)
                    if (searchBytes[j] != wildcard)
                        if (bytes[i + j] != sbytes[j])
                            goto nope;
                locs.Add(i);
                found++;
                if (onlyOne && found > 1)
                    break;
                nope:;
            }
            return locs.ToArray();
        }

        // (without wildcard)
        public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, bool onlyOne)
        {
            return BinaryPatternSearch(ref bytes, searchBytes, _q, onlyOne);
        }

        // (return only 1)
        public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, string wildcard)
        {
            return BinaryPatternSearch(ref bytes, searchBytes, wildcard, true);
        }

        // (return only 1 and no wildcard)
        public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes)
        {
            return BinaryPatternSearch(ref bytes, searchBytes, _q, true);
        }
    }


Маска задается в строке, примерно так "BA ? 8D 0D ? ? E8 BA"

Цитата: Adler от 14.05.2022, 11:36

Добрый день.
Для чтения и записи используйте BinaryWriter и BinaryReader. Как ими пользоваться загуглите сами.
Для быстрого поиска бинарных сигнатур есть класс, ссылку на который дам позже, когда найду его исходник.

Например, в бинарном файле есть такой набор байтов в такой последовательности:
08 00 00 00 67 6F 6C 64 3D 35 32 31
И заменить на такой:
0B 00 00 00 67 6F 6C 64 3D 39 39 39 39 39 39

А вы тут точно ничего не путаете? Обычно в бинарных данных не допускается изменение длины. Можно ссылку на данный мануал, по которому вы это делаете?

P.S. Например вот. Или можете поискать другие реализации алгоритма Boyer-Moore.
Так же где-то был исходник поиска по маске, но пока не вспомню, где я его использовал, что бы найти в исходниках.

Вот подробная инструкция
Число 08 это длинна. Дальше 3 нулевых байта и идёт значение gold=521
Я меняю на 0B 00 00 00 67 6F 6C 64 3D 39 39 39 39 39 39
Тоесть длинна 11. Дальше 3 нулевых байта не изменяется и gold=999999
Длинна gold=521 равняется 8 а gold=999999 уже 11 поэтому 1 байт с 8 нужно менять на 11 в шестнадцатеричном виде 0B

Цитата: Adler от 14.05.2022, 12:04

В одном проекте использовал еще вот такой класс:

Поиск в потоке первого вхождения

 
 
 
 
public static class Search
{
/// <summary>
/// Finds the first occurrence of <paramref name="pattern"/> in a stream
/// </summary>
/// <param name="s">The input stream</param>
/// <param name="pattern">The pattern</param>
/// <returns>The index of the first occurrence, or -1 if the pattern has not been found</returns>
public static long IndexOf(Stream s, byte[] pattern)
{
// Prepare the bad character array is done once in a separate step
var badCharacters = MakeBadCharArray(pattern);
 
// We now repeatedly read the stream into a buffer and apply the Boyer-Moore-Horspool algorithm on the buffer until we get a match
var buffer = new byte[Math.Max(2 * pattern.Length, 4096)];
long offset = 0; // keep track of the offset in the input stream
while (true)
{
int dataLength;
if (offset == 0)
{
// the first time we fill the whole buffer
dataLength = s.Read(buffer, 0, buffer.Length);
}
else
{
// Later, copy the last pattern.Length bytes from the previous buffer to the start and fill up from the stream
// This is important so we can also find matches which are partly in the old buffer
Array.Copy(buffer, buffer.Length - pattern.Length, buffer, 0, pattern.Length);
dataLength = s.Read(buffer, pattern.Length, buffer.Length - pattern.Length) + pattern.Length;
}
 
var index = IndexOf(buffer, dataLength, pattern, badCharacters);
if (index >= 0)
return offset + index; // found!
if (dataLength < buffer.Length)
break;
offset += dataLength - pattern.Length;
}
 
return -1;
}
 
// --- Boyer-Moore-Horspool algorithm ---
// (Slightly modified code from
// Prepare the bad character array is done once in a separate step:
private static int[] MakeBadCharArray(byte[] pattern)
{
var badCharacters = new int[256];
 
for (long i = 0; i < 256; ++i)
badCharacters[i] = pattern.Length;
 
for (var i = 0; i < pattern.Length - 1; ++i)
badCharacters[pattern[i]] = pattern.Length - 1 - i;
 
return badCharacters;
}
 
// Core of the BMH algorithm
private static int IndexOf(byte[] value, int valueLength, byte[] pattern, int[] badCharacters)
{
int index = 0;
 
while (index <= valueLength - pattern.Length)
{
for (var i = pattern.Length - 1; value[index + i] == pattern[i]; --i)
{
if (i == 0)
return index;
}
 
index += badCharacters[value[index + pattern.Length - 1]];
}
 
return -1;
}
}
public static class Search { /// <summary> /// Finds the first occurrence of <paramref name="pattern"/> in a stream /// </summary> /// <param name="s">The input stream</param> /// <param name="pattern">The pattern</param> /// <returns>The index of the first occurrence, or -1 if the pattern has not been found</returns> public static long IndexOf(Stream s, byte[] pattern) { // Prepare the bad character array is done once in a separate step var badCharacters = MakeBadCharArray(pattern); // We now repeatedly read the stream into a buffer and apply the Boyer-Moore-Horspool algorithm on the buffer until we get a match var buffer = new byte[Math.Max(2 * pattern.Length, 4096)]; long offset = 0; // keep track of the offset in the input stream while (true) { int dataLength; if (offset == 0) { // the first time we fill the whole buffer dataLength = s.Read(buffer, 0, buffer.Length); } else { // Later, copy the last pattern.Length bytes from the previous buffer to the start and fill up from the stream // This is important so we can also find matches which are partly in the old buffer Array.Copy(buffer, buffer.Length - pattern.Length, buffer, 0, pattern.Length); dataLength = s.Read(buffer, pattern.Length, buffer.Length - pattern.Length) + pattern.Length; } var index = IndexOf(buffer, dataLength, pattern, badCharacters); if (index >= 0) return offset + index; // found! if (dataLength < buffer.Length) break; offset += dataLength - pattern.Length; } return -1; } // --- Boyer-Moore-Horspool algorithm --- // (Slightly modified code from // https://stackoverflow.com/questions/16252518/boyer-moore-horspool-algorithm-for-all-matches-find-byte-array-inside-byte-arra) // Prepare the bad character array is done once in a separate step: private static int[] MakeBadCharArray(byte[] pattern) { var badCharacters = new int[256]; for (long i = 0; i < 256; ++i) badCharacters[i] = pattern.Length; for (var i = 0; i < pattern.Length - 1; ++i) badCharacters[pattern[i]] = pattern.Length - 1 - i; return badCharacters; } // Core of the BMH algorithm private static int IndexOf(byte[] value, int valueLength, byte[] pattern, int[] badCharacters) { int index = 0; while (index <= valueLength - pattern.Length) { for (var i = pattern.Length - 1; value[index + i] == pattern[i]; --i) { if (i == 0) return index; } index += badCharacters[value[index + pattern.Length - 1]]; } return -1; } }
 
public static class Search
    {
        /// <summary>
        /// Finds the first occurrence of <paramref name="pattern"/> in a stream
        /// </summary>
        /// <param name="s">The input stream</param>
        /// <param name="pattern">The pattern</param>
        /// <returns>The index of the first occurrence, or -1 if the pattern has not been found</returns>
        public static long IndexOf(Stream s, byte[] pattern)
        {
            // Prepare the bad character array is done once in a separate step
            var badCharacters = MakeBadCharArray(pattern);

            // We now repeatedly read the stream into a buffer and apply the Boyer-Moore-Horspool algorithm on the buffer until we get a match
            var buffer = new byte[Math.Max(2 * pattern.Length, 4096)];
            long offset = 0; // keep track of the offset in the input stream
            while (true)
            {
                int dataLength;
                if (offset == 0)
                {
                    // the first time we fill the whole buffer
                    dataLength = s.Read(buffer, 0, buffer.Length);
                }
                else
                {
                    // Later, copy the last pattern.Length bytes from the previous buffer to the start and fill up from the stream
                    // This is important so we can also find matches which are partly in the old buffer
                    Array.Copy(buffer, buffer.Length - pattern.Length, buffer, 0, pattern.Length);
                    dataLength = s.Read(buffer, pattern.Length, buffer.Length - pattern.Length) + pattern.Length;
                }

                var index = IndexOf(buffer, dataLength, pattern, badCharacters);
                if (index >= 0)
                    return offset + index; // found!
                if (dataLength < buffer.Length)
                    break;
                offset += dataLength - pattern.Length;
            }

            return -1;
        }

        // --- Boyer-Moore-Horspool algorithm ---
        // (Slightly modified code from
        // https://stackoverflow.com/questions/16252518/boyer-moore-horspool-algorithm-for-all-matches-find-byte-array-inside-byte-arra)
        // Prepare the bad character array is done once in a separate step:
        private static int[] MakeBadCharArray(byte[] pattern)
        {
            var badCharacters = new int[256];

            for (long i = 0; i < 256; ++i)
                badCharacters[i] = pattern.Length;

            for (var i = 0; i < pattern.Length - 1; ++i)
                badCharacters[pattern[i]] = pattern.Length - 1 - i;

            return badCharacters;
        }

        // Core of the BMH algorithm
        private static int IndexOf(byte[] value, int valueLength, byte[] pattern, int[] badCharacters)
        {
            int index = 0;

            while (index <= valueLength - pattern.Length)
            {
                for (var i = pattern.Length - 1; value[index + i] == pattern[i]; --i)
                {
                    if (i == 0)
                        return index;
                }

                index += badCharacters[value[index + pattern.Length - 1]];
            }

            return -1;
        }
    }


А вот и для поиска по маске нашел:
Поиск по маске

 
 
 
 
public static class Searcher
{
private const string _q = "?";
private static int largest(ref string[][] list)
{
int size = 0;
for (int i = 0; i < list.Length; i++)
size = (list[i].Length > size) ? list[i].Length : size;
return size;
}
public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, string wildcard, bool onlyOne)
{
int sl = searchBytes.Length;
if (sl < 1 || bytes.Length < sl)
return new int[] { };
byte[] sbytes = new byte[sl];
for (int i = 0; i < sl; i++)
if (searchBytes[i] != wildcard)
if (!Byte.TryParse(searchBytes[i], System.Globalization.NumberStyles.HexNumber, null, out sbytes[i]))
return new int[] { };
 
List<int> locs = new List<int>();
int n = bytes.Length - sl;
int found = 0;
for (var i = 0; i <= n; i++)
{
for (int j = 0; j < sl; j++)
if (searchBytes[j] != wildcard)
if (bytes[i + j] != sbytes[j])
goto nope;
locs.Add(i);
found++;
if (onlyOne && found > 1)
break;
nope:;
}
return locs.ToArray();
}
 
// (without wildcard)
public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, bool onlyOne)
{
return BinaryPatternSearch(ref bytes, searchBytes, _q, onlyOne);
}
 
// (return only 1)
public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, string wildcard)
{
return BinaryPatternSearch(ref bytes, searchBytes, wildcard, true);
}
 
// (return only 1 and no wildcard)
public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes)
{
return BinaryPatternSearch(ref bytes, searchBytes, _q, true);
}
}
public static class Searcher { private const string _q = "?"; private static int largest(ref string[][] list) { int size = 0; for (int i = 0; i < list.Length; i++) size = (list[i].Length > size) ? list[i].Length : size; return size; } public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, string wildcard, bool onlyOne) { int sl = searchBytes.Length; if (sl < 1 || bytes.Length < sl) return new int[] { }; byte[] sbytes = new byte[sl]; for (int i = 0; i < sl; i++) if (searchBytes[i] != wildcard) if (!Byte.TryParse(searchBytes[i], System.Globalization.NumberStyles.HexNumber, null, out sbytes[i])) return new int[] { }; List<int> locs = new List<int>(); int n = bytes.Length - sl; int found = 0; for (var i = 0; i <= n; i++) { for (int j = 0; j < sl; j++) if (searchBytes[j] != wildcard) if (bytes[i + j] != sbytes[j]) goto nope; locs.Add(i); found++; if (onlyOne && found > 1) break; nope:; } return locs.ToArray(); } // (without wildcard) public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, bool onlyOne) { return BinaryPatternSearch(ref bytes, searchBytes, _q, onlyOne); } // (return only 1) public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, string wildcard) { return BinaryPatternSearch(ref bytes, searchBytes, wildcard, true); } // (return only 1 and no wildcard) public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes) { return BinaryPatternSearch(ref bytes, searchBytes, _q, true); } }
 
public static class Searcher
    {
        private const string _q = "?";
        private static int largest(ref string[][] list)
        {
            int size = 0;
            for (int i = 0; i < list.Length; i++)
                size = (list[i].Length > size) ? list[i].Length : size;
            return size;
        }
        public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, string wildcard, bool onlyOne)
        {
            int sl = searchBytes.Length;
            if (sl < 1 || bytes.Length < sl)
                return new int[] { };
            byte[] sbytes = new byte[sl];
            for (int i = 0; i < sl; i++)
                if (searchBytes[i] != wildcard)
                    if (!Byte.TryParse(searchBytes[i], System.Globalization.NumberStyles.HexNumber, null, out sbytes[i]))
                        return new int[] { };

            List<int> locs = new List<int>();
            int n = bytes.Length - sl;
            int found = 0;
            for (var i = 0; i <= n; i++)
            {
                for (int j = 0; j < sl; j++)
                    if (searchBytes[j] != wildcard)
                        if (bytes[i + j] != sbytes[j])
                            goto nope;
                locs.Add(i);
                found++;
                if (onlyOne && found > 1)
                    break;
                nope:;
            }
            return locs.ToArray();
        }

        // (without wildcard)
        public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, bool onlyOne)
        {
            return BinaryPatternSearch(ref bytes, searchBytes, _q, onlyOne);
        }

        // (return only 1)
        public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes, string wildcard)
        {
            return BinaryPatternSearch(ref bytes, searchBytes, wildcard, true);
        }

        // (return only 1 and no wildcard)
        public static int[] BinaryPatternSearch(ref byte[] bytes, string[] searchBytes)
        {
            return BinaryPatternSearch(ref bytes, searchBytes, _q, true);
        }
    }


Маска задается в строке, примерно так "BA ? 8D 0D ? ? E8 BA"

Не понял как замену делать. К слову вот что мне необходимо автоматизировать:

Много перерыл сайтов. Не нашел как менять набор байтов на больший но подумал а что если преоброзовать набор байтов строку и желательно так чтоб не испортил данные. Короче нашел и написал:

Мой код

private static byte[] getBytes(TypeData typeData, int num)
        {
            var bytesString = "";
            switch (typeData)
            {
                case TypeData.tdGold: bytesString = "gold=" + num.ToString();
                    break;
                case TypeData.tdGem: bytesString = "gem=" + num.ToString();
                    break;
                case TypeData.tdCIB:
                    bytesString = "cib=" + num.ToString();
                    break;
                    default: return new byte[0];
            }

            var byteslength = Convert.ToByte(bytesString.Length);

            var retBytes = new byte[4 + bytesString.Length];

            retBytes[0] = byteslength;
            retBytes[3] = retBytes[2] = retBytes[1] = 0;
            for (var i = 4; i < retBytes.Length; i++)
            {
                retBytes[i] = Convert.ToByte(bytesString[i - 4]);
            }

            return retBytes;
        }

        private static byte[] FromHex(string hex)
        {
            hex = hex.Replace("-", "");
            byte[] raw = new byte[hex.Length / 2];
            for (int i = 0; i < raw.Length; i++)
            {
                raw[i] = Convert.ToByte(hex.Substring(i * 2, 2), 16);
            }
            return raw;
        }

        public static void EditGold(string fileName, int searchGold, int newGold)
        {
            var searchBytesGold = getBytes(TypeData.tdGold, searchGold);
            var newBytesGold = getBytes(TypeData.tdGold, newGold);
            
            var dataFile = BitConverter.ToString(File.ReadAllBytes(fileName));
            dataFile = dataFile.Replace(BitConverter.ToString(searchBytesGold), BitConverter.ToString(newBytesGold));
            var dataFileSave = FromHex(dataFile);
            File.Copy(fileName, fileName+ ".back");
            File.WriteAllBytes(fileName, dataFileSave);
        }


И как итог всё работает. Правда сам Replace не очень хорошо подходит так как меняет все вхождения а менять нужно только 1 раз а не везде. Такую ситуацию тоже нужно предусмотреть но именно сохранение не ломается и загружается.