Для тех, кто только подключился -- я рассказываю про платформу для VR игр, как с ней интегрироваться и как добраться до ее сенсоров напрямую.

Я уже заметил некоторые глюки и проблемы с сенсорами -- значит, неплохо было бы их поправить. Но чтобы их поправить, надо понять что они за зверь, и можно ли их как-то поменять. Из-за огромного размера статьи, пришлось разделить и без того маленький рефератик на еще более маленькие кусочки -- поэтому сегодня мы просто заглянем внутрь сенсора и научимся менять ему прошивку в простом и удобном виде.

Вскрытие покажет

Изучение чего либо обычно представляет собой хаотичный процесс с заходами с разных сторон, так называемый "метод научного тыка". Никогда заранее не скажешь, где идеальный путь, пока не посмотришь на итог процесса на ретроспективе. Поэтому не следует воспринимать данный опус как руководство к действию, скорее как небольшой обзор на что можно смотреть, да где искать крупицы полезностей. Как правило, всё множество путей помогают друг другу и в конце концов приводят к пониманию.

Вскроем...
Вскроем...

Но что точно хорошо почти всегда (когда есть возможность) -- это заглядывание внутрь. Если мы заглянем внутрь сенсора для ног, мы сходу заметим большой модуль, подписанный HY-40R201C. Это BLE5.0 модуль, основанный на TI CC2640R2. То есть это процессор (точнее, два) плюс радиомодуль. Может быть использован как сам по себе, загружая свою прошивку в него, так и в тандеме с внешним процессором.

перевернём на животик...
перевернём на животик...

Других процессоров на плате не видно, на обратной стороне ничего, кроме модуля оптической мыши A9800 не обнаружено.

Вывод: модуль используется напрямую как логический процессор. На плате присутствуют два порта для батарей, но подключена только одна. Есть кнопки Reset и Boot. Теперь мы всегда можем открыть даташит, чтобы понять где какие порты и адреса железа лежат, да знаем что это ARM Cortex M3 с ПЗУ на 128кб, рам на 8кб. Не очень много, но явно хватает.

В отличие от Nordic модуля, которым я воспользовался, поддержка USB напрямую отсутствует. Зато на плате виден CH9326, который совпадает по названию с лежащей в папке гейтвея DLLке.

Таким образом, мы либо узнали, либо подтвердили, что сенсоры общаются через USB-HID конвертер-чип, работают на ARM, и основаны на Texas Instruments SIMPLELINK-CC2640R2-SDK. К SDK еще идёт "Academy", в котором в сжатой форме можно почтитать про BLE, как его готовить внутри СДК, да немного рассмотрено примеров. В любом случае, так как девкита нет, на самом сенсоре поиграться сходу не получится, в остальном придётся пользоваться полнотекстовым поиском внутри установленного SDK.

Что в ROMушке тебе моём

Внешний осмотр это хорошо, но понять, что внутри, просто осмотром не получится. На плате есть пятачки подписанные "V R C M G", где G явно Ground, всё остальное -- вопрос. Можно прозвонить, конечно, но... JTAG у меня всё равно нет.

Тут, к счастью, можно вспомнить, что гейтвей умеет прошивать сенсоры. Я когда первый раз их подключал, он мне говорил, что прошивка старая, и надо залить новую. И залил. Вывод -- прошивка гейтвею доступна. Обзор глазами файлов в папке гейтвея:

C:\>dir /b /d "C:\Program Files (x86)\KAT Gateway\*bin"
loco_ankle_by_embeded.bin
loco_receiver_by_embeded.bin
loco_sensor_group_application_foot_release_by_embeded_engineer.bin
loco_sensor_group_application_waist_release_by_embeded_engineer.bin
loco_s_ankle_by_embeded.bin
loco_s_foot_by_embeded.bin
loco_s_receiver_by_embeded.bin
loco_s_waist_by_embeded.bin
loco_waist_by_embeded.bin
walk_c_foot_by_embeded.bin
walk_c_hall_by_embeded.bin
walk_c_receiver_by_embeded.bin
walk_c_v2_foot_by_embeded.bin
walk_c_v2_hall_by_embeded.bin
walk_c_v2_receiver_by_embeded.bin

Прикольно, а где C2?!

C:\>dir /b /d "C:\Program Files (x86)\KAT Gateway\*hex"
katvr_direction.hex
katvr_foot.hex
katvr_receiver.hex

Хм. Грузим dotPeek опять, Ctrl+Alt+T, ".hex"... Ха!

поиск по точкагексу
поиск по точкагексу
    byte index = 0;

    int num1 = (int) KatvrFirmwareHelper.ch9326_find();
    assert(num1 != 0)

    int num2 = (int) KatvrFirmwareHelper.ch9326_open(Update_Firmware_Upgrading_Form.vid, Update_Firmware_Upgrading_Form.pid);
    assert(num2 != 0)

    int num21 = KatvrFirmwareHelper.ch9326_set_gpio(index, (byte) 15, (byte) 15)
    assert(num21 != 0)

    int num3 = (int) KatvrFirmwareHelper.ch9326_connected(index);
    assert(num3 != 0)

    int num4 = (int) KatvrFirmwareHelper.flash(_hex_path, device_type, device_state);
    assert(num4 == 1)

    KatvrFirmwareHelper.ch9326_ClearThreadData();
    KatvrFirmwareHelper.close_ch9326();

    /* Write MACs of sensors into receiver if we updated receiver */
    if (Update_Firmware_Upgrading_Form.deviceType == C2FirmwareUpdaeManager.C2DeviceType.Receiver) {
        KATSDKInterfaceHelper.WriteSensorPair(...)
    }

Прекрасно, то есть для прошивки нам потребуется пара вызовов из KatvrFirmwareHelper и гекс прошивки. Но в дизасм как правило надо грузить BIN, а не HEX. Впрочем, это решается множеством способов. Я просто запустил WSL:

$ cd /mnt/c/Program\ Files\ \(x86\)/KAT\ Gateway/
$ for k in foot direction receiver; do objcopy --input-target=ihex --output-target=binary katvr_$k.hex katvr_$k.bin; done
$ ls -la kat*bin
-rwxrwxrwx 1 datacompboy datacompboy 131072 Mar 29 21:46 katvr_direction.bin
-rwxrwxrwx 1 datacompboy datacompboy 131072 Mar 29 21:46 katvr_foot.bin
-rwxrwxrwx 1 datacompboy datacompboy 131072 Mar 29 21:46 katvr_receiver.bin

Обращаем внимание, что все файлы ровно 128кБ, при этом hex разных размеров, значит, прошивка из нескольких секций, и, вероятно, части в ней отсутствуют или использованы для настроек, или еще что. Просто учтём на будущее.

Кстати, если мы еще не вскрывали сенсор, и не знаем что за процессор, можно попробовать потыкать в бинарничек через binwalk:

$ binwalk --disasm ./katvr_direction.bin 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
0             0x0             ARM executable code, 16-bit (Thumb), little endian, at least 1624 valid instructions

или cpu_rec:

$ python cpu_rec.py ./katvr_direction.bin 
./katvr_direction.bin       full(0x20000)  None        chunk(0x10000;32)   ARMhf

Где мы убеждаемся, что да, это ARM и в основном в Thumb режиме.

Потыкав дополнительно через strings:

$ strings -n 10 katvr_direction.bin
inputNormal
FinputGyroRv
executable
N]_]>CNUW]>@FUm
`(i0a(}0uh}pu
[USQOMKIFCA?<:8
p>"`hBp	!`h
k +# p(F#p
!i"hQ\)pch!i
pGpGpGpGpG
{unknown-instance-name}
{empty-instance-name}
F{static-instance-name}
Заметки на полях

Поиск полезных данных никогда не бывает лишним. Например, вот этот вывод strings, в котором есть странные строки -- "inputGyroRv" и "inputNormal". Поиск по ним на github дал сходу интересную вещь, что позволило разметить часть функций и структур, которыми пользуется сенсор направления. Для ног однако подобной фкусности не обнаружилось.

Заливка прошивки на ходу

Разглядывание документации на Serial Boot Loader показывает, что бутлоадер просто готов принимать прошивку если в него перезагрузиться, 0x55 синхронизует скорость, в общем, всё как всегда. Мы, впрочем, не можем пользоваться родным прошивальщиком, так как у нас нет последовательного интерфейса -- у нас есть HID2Serial, так что прошивка должна идти через него. Но, как мы уже выше выяснили, функции прошивки экспортированы наружу, так что можно просто попробовать вызвать их.

Создадим простой C# проект, куда импортируем KatvrFirmwareHelper и katvr_firmware.dll на которую он ссылается:

    static void Main(string[] args)
    {
        uint vid = 0xC4F4u;
        byte device_state = 0;
        byte index = 0;
        uint pid = 28471u;
        byte device_type = 3;
        string hex_path = "C:\\Program Files (x86)\\KAT Gateway\\katvr_foot.hex";

        if (KatvrFirmwareHelper.ch9326_find() == 0)
        {
            Console.WriteLine("ch9326_find failed");
            return;
        }

        if (KatvrFirmwareHelper.ch9326_open(vid, pid) == 0)
        {
            Console.WriteLine("ch9326_open failed");
            return;
        }

        if (KatvrFirmwareHelper.ch9326_set_gpio(index, (byte)15, (byte)15) == 0)
        {
            Console.WriteLine("ch9326_set_gpio failed");
            return;
        }

        if (KatvrFirmwareHelper.ch9326_connected(index) == 0)
        {
            Console.WriteLine("ch9326_connected failed");
            return;
        }

        if (KatvrFirmwareHelper.flash(hex_path, device_type, device_state) != 1)
        {
            Console.WriteLine("KatvrFirmwareHelper.flash failed");
            Console.ReadKey();
            return;
        }

        KatvrFirmwareHelper.ch9326_ClearThreadData();
        KatvrFirmwareHelper.close_ch9326();
    }

Запустим -- в консоли какие-то отладочные распечатки, ошибка. Эм... А, ну да, зажимаем Flash кнопку, тыкаем Reset, запускаем опять -- побежали точки по экрану. Через минуты полторы -- готово. Сенсор всё еще жив. Правда, мигает левой лампочкой вместо правой, как мигал до этого. Упс. Настройки стерлись.

Патчим прошивку

Но хотелось бы комфортного патчинга. У гидры есть возможность экспортировать текущий файл как Hex или Raw, но экспортируется всё, включая рам и области, которых не было в исходном hex'е. Вывод -- надо патчить прямо HEX файл, а как?

Для начала -- на чем-то надо тренироваться. Проще всего сделать простой бинарный патч -- поменяем "KATVR" строку на "KAT-F", то есть устройство будет себя анонсировать как "KAT-F" (типа Foot/нога). Открываем любым hex редактором, например, WinHex, и правим. Затем берём дифф:

> fc.exe /b .\katvr_foot_orig.bin .\katvr_foot.bin
Comparing files .\katvr_foot_orig.bin and .\KATVR_FOOT.BIN
000129E9: 56 2D
000129EA: 52 46

Отлично, у нас есть патч, который легко прочитать как есть или сконвертировать с C#:

    static readonly PatchEntry[] PatchFoot = {
        ( 0x000129e9, 0x56, 0x2D ), // V => -
        ( 0x000129ea, 0x52, 0x46 ), // R => F
    };

Из вкусных трюков, которые удалось найти для C# (я очень редко его трогаю) -- приведение тупла к структуре, позволяющая сократить многабукав в статических массивах. Просто структуру автоматически он не разбирает, но мы можем добавить конструктор и implicit operator который вызовет приведение:

    struct PatchEntry {
        readonly public int addr;
        readonly public byte orig;
        readonly public byte patch;
        public PatchEntry(int addr, byte orig, byte patch) {
            this.addr = addr;
            this.orig = orig;
            this.patch = patch; 
        }
        public static implicit operator PatchEntry((int addr, uint orig, uint patch) tuple) {
            return new PatchEntry(tuple.addr, (byte)tuple.orig, (byte)tuple.patch); 
        }
    };

Теперь, когда у нас есть патч в удобном машино-читаемом виде, его надо наложить на HEX.

Возьмём HexIO библиотеку и накидаем фиксилку. Знать надо не много: следим за текущим адресом, если прочитанная строка включает в себя адрес патча -- исправляем. К сожалению, HexIO не следит за изменениями в структуре, и не обновляет контрольную сумму -- пришлось выкрутиться через пересоздание записи. Не очень красиво, но быстро и работает:

    static string PatchHex(string input, PatchEntry[] patch)
    {
        string output = System.IO.Path.GetTempFileName() + ".hex";

        IIntelHexStreamReader hexInput = new IntelHexStreamReader(input);
        using (StreamWriter hexOutput = new StreamWriter(output))
        {
            uint offset = 0;
            var patch_i = 0;
            do
            {
                IntelHexRecord rec = hexInput.ReadHexRecord();
                if (rec.RecordType == IntelHexRecordType.Data)
                {
                    while (patch_i < patch.Length) {
                        var pe = patch[patch_i];
                        if (pe.addr >= offset + rec.Offset)
                        {
                            long idx = pe.addr - offset - rec.Offset;
                            if (idx >= rec.RecordLength)
                            {
                                break;
                            }
                            if (rec.Data[(int)idx] != pe.orig)
                            {
                                Console.WriteLine("File data doesn't match expected.");
                                throw new InvalidDataException();
                            }
                            rec.Data[(int)idx] = pe.patch;
                            rec = new IntelHexRecord(rec.Offset, rec.RecordType, rec.Data);
                            patch_i++;
                        }
                        else
                        {
                            Console.WriteLine("Can't apply patch to a gap.");
                            throw new InvalidDataException();
                        }
                    };
                }
                else if (rec.RecordType == IntelHexRecordType.ExtendedLinearAddress && rec.RecordLength == 2)
                {
                    offset = (uint)((rec.Data[0] << 8 | rec.Data[1]) << 16);
                }
                else if (rec.RecordType == IntelHexRecordType.EndOfFile)
                {
                    if (patch_i < patch.Length)
                    {
                        Console.WriteLine("Not all patch was applied!");
                        throw new InvalidDataException();
                    }
                }
                else
                {
                    Console.WriteLine(rec.ToString());
                    throw new InvalidDataException();
                }
                hexOutput.WriteLine(rec.ToHexRecordString());
            } while (!hexInput.State.Eof);
        };

        return output;
    }

Теперь применим патч и используем патченный hex вместо оригинала:

    static void Main(string[] args)
    {
        string orig_hex = "C:\\Program Files (x86)\\KAT Gateway\\katvr_foot.hex";
        string hex_path = PatchHex(orig_hex, PatchFoot);
        ...
    }

Прошиваем, смотрим, что видно в Bluetooth окружении: ха! есть "KAT-F" устройство.

Заметки на полях:

Когда позднее потребовалось добавить немного кода в прошивку, пришлось повозиться со вставкой новых строк. Для этого вместо ошибки в Else вместо ошибки про Gap добавил кусок, напрямую конструирующий новые записи и выводящий их сразу:

    } else {
        int start = pe.addr;
        List<byte> data = new List<byte>();
        do
        {
            data.Add(patch[patch_i++].patch);
        } while (patch_i < patch.Length &&
                    patch[patch_i].addr - 1 == patch[patch_i-1].addr &&
                    patch[patch_i].addr - start < 0x20);
        if (start - offset >= 0x10000)
        {
            Console.WriteLine("Can't inject a record: cross boundary");
            throw new InvalidDataException();
        }
        var newrec = new IntelHexRecord((ushort)(start - offset), rec.RecordType, data);
        hexOutput.WriteLine(newrec.ToHexRecordString());
    }

User-friendly патчинг

Так как я хочу сделать патчи, которыми могут пользоваться люди, нужно оформить прошивку в виде скрипта.

В принципе, как уже было сказано, C# переносится в PowerShell тривиально:

param (
    [string]$firmware = "",
    [int]$dvid = 0xC4F4,
    [int]$dpid = 28471,
    [int]$type = 3,
    [int]$index = 0
)

Add-Type -Path "C:\Program Files (x86)\KAT Gateway\KAT_WalkC2_Dx.dll"

if ($firmware -eq "") {
  $firmware = $katPath + "C:\Program Files (x86)\KAT Gateway\katvr_foot.hex"
}
Write-Host "Want to flash $firmware"

if ([KAT_WalkC2_Dx.KatvrFirmwareHelper]::ch9326_find() -eq 0) {
    throw "ch9326_find failed"
}
if ([KAT_WalkC2_Dx.KatvrFirmwareHelper]::ch9326_open($dvid, $dpid) -eq 0) {
    throw "ch9326_open failed"
}
if ([KAT_WalkC2_Dx.KatvrFirmwareHelper]::ch9326_set_gpio($index, 15, 15) -eq 0) {
    throw "ch9326_set_gpio failed"
}
if ([KAT_WalkC2_Dx.KatvrFirmwareHelper]::ch9326_connected($index) -eq 0) {
    throw "ch9326_connected failed"
}
if ([KAT_WalkC2_Dx.KatvrFirmwareHelper]::flash($firmware, $type, 0) -ne 1) {
    throw "KatvrFirmwareHelper.flash failed"
}
[KAT_WalkC2_Dx.KatvrFirmwareHelper]::ch9326_ClearThreadData()
[KAT_WalkC2_Dx.KatvrFirmwareHelper]::close_ch9326()

Уже неплохо, позволяет прошить любой патч или откатить прошивку. Но вот после прошивки сенсор теряет его настройку (левый-правый), значит, надо сделать восстановление режима спаривания.

Позаимствовав код определения спаривания из прошлых скриптов, по сути надо только добавить поиск сенсора и отправку команды

Причем и ReadDeviceId и WriteDeviceId уже есть готовые:

...
$id = -1
[IBizLibrary.KATSDKInterfaceHelper]::ReadDeviceId($dev.serialNumber, [ref]$id)
$sensor = New-Object IBizLibrary.KATSDKInterfaceHelper+sensorInformation
[IBizLibrary.KATSDKInterfaceHelper]::GetSensorInformation([ref]$sensor, $dev.serialNumber)
$leftmac = [IBizLibrary.KATSDKInterfaceHelper]::receiverPairingInfoSave.ReceiverPairingByte[7..12]
$rightmac = [IBizLibrary.KATSDKInterfaceHelper]::receiverPairingInfoSave.ReceiverPairingByte[13..19]
if(-not(Compare-Object $leftmac $sensor.mac)) {
    [IBizLibrary.KATSDKInterfaceHelper]::WriteDeviceId($dev.serialNumber, 2)
    Write-Host "Made the sensor to be Left Foot"
}
elseif(-not(Compare-Object $rightmac $sensor.mac)) {
    [IBizLibrary.KATSDKInterfaceHelper]::WriteDeviceId($dev.serialNumber, 3)
    Write-Host "Made the sensor to be Right Foot"
}
else {
    throw "The sensor's mac is not paired to the treadmill"
}

Если добавить еще cmd батнички:

:: restore-foot.cmd
powershell.exe -ExecutionPolicy Bypass -File flush-feet-sensor.ps1

:: update-foot.cmd
powershell.exe -ExecutionPolicy Bypass -File flush-feet-sensor.ps1 --firmware .\my-foot.hex

То пользователю надо будет только запустить скрипт.

Патчим патчи, чтобы патчить патчи

Строго говоря мы не владеем исходной прошивкой, а потому распространять её как-то не оч. С другой стороны, у всех пользователей уже стоит гейтвей, то есть мы можем просто использовать исходную прошивку -- надо только наложить таки патч поверх неё. Хотелось бы избежать бинарников или слишком сложного скрипта (можно было бы перенести всю логику HEX патча с C# на PowerShell). Вывод -- надо сделать простой патчинг.

Иначе говоря, чтобы пропатчить патч, надо наложить патч. Ну вы поняли, да?

We need to go deeper
We need to go deeper

У нас теперь есть два HEXа: оригинальный, и полученный после наложения на него бинарного патча. Можно взять текстовый diff между ними:

> fc.exe /l /n 'C:\Program Files (x86)\KAT Gateway\katvr_foot.hex' patch.hex
Comparing files C:\PROGRAM FILES (X86)\KAT GATEWAY\katvr_foot.hex and PATCH.HEX
***** C:\PROGRAM FILES (X86)\KAT GATEWAY\katvr_foot.hex
 2463:  :2029C80000010203AEC2E6ED0001C3AA9D00F802CF01060302040F02013F050609FF4B41D2
 2464:  :2029E8005456520512089F000900020A039F1902AF9607B039043D4FF703636505771064CC
 2465:  :202A0800115ED1F9E86567676215F40000E979FA175FBD027BA63075FFFF79BF19070080C2
***** PATCH.HEX
 2463:  :2029C80000010203AEC2E6ED0001C3AA9D00F802CF01060302040F02013F050609FF4B41D2
 2464:  :2029E800542D460512089F000900020A039F1902AF9607B039043D4FF70363650577106401
 2465:  :202A0800115ED1F9E86567676215F40000E979FA175FBD027BA63075FFFF79BF19070080C2
*****

Вот только как его наложить? Ну то есть diff есть (fc.exe) а вот patch нету! Что ж, так как формат патча прост и для нашего случая не требуется интеллектуального наложения (нужно чтобы он применился 1-в-1, это еще подтвердит что исходная прошивка правильная), то можно сделать просто: читаем файл, для каждой строки в исходном проверяем равенство, для каждой строки выходной просто печатаем её.

А можно вообще, превратить этот дифф в скрипт, который будет прямо патчить. Не знаю что проще, если честно, но я пошел вторым путём (если честно -- чтобы не возиться с чтением двух файлов, а просто работать как обработчик пайпа). Конвертер делается простой стейт машиной:

  • "Comparing files" строка => выводим заголовок, переходим в ожидание патча

  • "*****" в ожидании патча => переходим в ожидание строк исходного

  • "*****" в ожидании строк исходного => переходим в ожидание патченных строк

  • другая строка в ожидании строк исходного => печатаем if на номер строки и её содержимое

  • "*****" в ожидании патченных строк => переходим в ожидание патча

  • срока в ожидании патченных строк => печатаем её

Вроде просто.

Из важных вещей с которыми пришлось познакомиться: по умолчанию редирект работает не в той кодировке, что было на входе и не в utf8, а в utf-16. Так что приходится редирект отсылать в Out-File -Encoding Ascii. Но это не сильно мешает.

Итак, теперь можно скормить ему патч, и посмотреть, что получилось:

> fc.exe /l /n 'C:\Program Files (x86)\KAT Gateway\katvr_foot.hex' patch.hex | .\fc-text-to-patcher.ps1 | Out-File -Filepath foot-patch.ps1 -Encoding Ascii
> cat .\foot-patch.ps1
$in_line = 0
$Input | ForEach-Object {
  $in_line++
  $skip = 0
  if ($in_line -eq  2463) {
    if ($_ -ne ':2029C80000010203AEC2E6ED0001C3AA9D00F802CF01060302040F02013F050609FF4B41D2') { throw 'File content mismatch'; }
    $skip = 1
  }
  if ($in_line -eq  2464) {
    if ($_ -ne ':2029E8005456520512089F000900020A039F1902AF9607B039043D4FF703636505771064CC') { throw 'File content mismatch'; }
    $skip = 1
  }
  if ($in_line -eq  2465) {
    if ($_ -ne ':202A0800115ED1F9E86567676215F40000E979FA175FBD027BA63075FFFF79BF19070080C2') { throw 'File content mismatch'; }
    $skip = 1
  }
  if ($in_line -eq  2465) {
    Write-Output ':2029C80000010203AEC2E6ED0001C3AA9D00F802CF01060302040F02013F050609FF4B41D2'
  }
  if ($in_line -eq  2465) {
    Write-Output ':2029E800542D460512089F000900020A039F1902AF9607B039043D4FF70363650577106401'
  }
  if ($in_line -eq  2465) {
    Write-Output ':202A0800115ED1F9E86567676215F40000E979FA175FBD027BA63075FFFF79BF19070080C2'
  }
  if ($skip -eq 0) {
    Write-Output $_
  }
}

Отлично! Проверим:

> cat 'C:\Program Files (x86)\KAT Gateway\katvr_foot.hex' | .\foot-patch.ps1 | Out-File -Filepath out.hex -Encoding ascii
> fc.exe .\out.hex .\patch.hex
Comparing files .\out.hex and .\PATCH.HEX
FC: no differences encountered

Последние штрихи

Поправим скрипт, добавим вызов восстановления и код для наложения патча. Поймём, что в папке scripts образовалась каша, так что... Время рефакторинга! оставляем нормальные имена файлов для .cmd -- для пользователей, чтоб в начале были.

Оба рабочих скрипта (прошивки и восстановления настроек) перенесём в конец, обозвав их y-$script.ps1. А патчи положим в z_patch_$sensor.ps1.

Еще заметка на полях:

Вызвать скрипт косвенно через переменную -- амперсанд. Ну то есть наложение патча идёт через:

$newfw = $ENV:TEMP + "\katvr_" + $orig + "_patch.hex"
$patchscript = ".\z_patch_" + $patch + ".ps1"
Get-Content $firmware | & $patchscript | Out-File -FilePath $newfw -Encoding ascii

Полученную кашицу уже можно считать финалом.

В следующей серии

Теперь, когда мы подготовили материальную базу, можно заняться самой интересной частью -- разбором паршивок и правкой их по-настоящему, а не просто прямой правкой констант. Не переключайтесь!

Ссылки

Комментарии (0)