Справочный материал hex-редактора 010 Editor: шаблоны (templates)

в 14:46, , рубрики: hex, hex редакор, reverse engineering, исследование, навыки и умения, отладка, Программирование

Написав пару статей о работе в 010 Editor (часть I, часть II), пришел к выводу, что чем подробно рассказывать о базовых вещах, лучше для начала перевести справку из программы, ибо расписано в ней все неплохо, а русского перевода нет. Итак, в данной статье мы познакомимся с теми особенностями написания шаблонов 010 Editor, о которых говорят сами разработчики.

Справочный материал hex редактора 010 Editor: шаблоны (templates)
Бинарные шаблоны — одна из самых мощных возможностей 010 Editor, которая позволяет представить любой файл в виде упорядоченного набора переменных и структур. Шаблоны помогают исследовать и редактировать файлы гораздо более удобным способом, чем это позволяют обычные hex-редакторы. Каждый шаблон представляет собой текстовый файл с расширением "*.bt", который может быть отредактирован в блокноте и прямо в 010 Editor (см. меню Templates). Шаблон запускается как интерпретируемый код, после его работы результат выполнения отображается на панели Template Results. Шаблоны могут быть сконфигурированы для автоматической загрузки и выполнения, когда в редакторе открыт файл с определенным расширением, например, в стандартный набор 010 Editor входят шаблоны для работы с *.zip, *.bmp и *.wav.

I — Объявление переменных

Объявление переменных идентично С, но с одним важным отличием: всякий раз, когда в шаблоне объявлена переменная, она «переносится» в своем представлении на набор байт в файле. Например, рассмотрим шаблон:

    char     header[4];
    int      numRecords; 

Данный шаблон создаст массив символов заголовка, который «покроет» первые 4 байта исследуемого файла, а целочисленная переменная numRecords покроет следующие 4 байта. Обе переменные будут отображены в панели Template Results и смогут быть использованы для редактирования прямо в файле. Основной способ группировки переменных вместе это объявление структур (structs) и объединений (unions).

Особые атрибуты

Особые атрибуты могут быть определены после объявленной переменной внутри 'угловых' ('<' и '>') скобок. Поддерживаются следующие атрибуты:

   < format=hex|decimal|octal|binary,  // режим отображения: шестнадцатеричный, десятичный, восьмеричный, двоичный
     fgcolor=<color>, // цвет символов
     bgcolor=<color>, // цвет фона
     comment="<string>"|<function_name>, // комментарий
     name="<string>"|<function_name>, // наименование
     open=true|false|suppress, // режим открытия
     hidden=true|false, // режим скрытия
     read=<function_name>, // функция для чтения
     write=<function_name> // функция для записи
     size=<number>|<function_name> >  // размер

Назначение всех атрибутов рассматривается ниже, за исключением функций чтения и записи (они рассмотрены в пункте Custom Varuables), а также размера (рассматривается в разделе On-Demand Structures).

Режим отображения

По умолчанию все переменные, отображенные в Template Results, в десятичном формате. Для переключения между шестнадцатеричным, десятичным, восьмеричным и двоичным, можно использовать функции DisplayFormatDecimal, DisplayFormatHex, DisplayFormatBinary, and DisplayFormatOctal. Альтернативный способ — использование следующего атрибута:

'<format=hex|decimal|octal|binary>'

После объявления переменной или typedef-а. Например:

    int id;
    int crc <format=hex>;
    int flags <format=binary>;

Цвета

При разборе файла с помощью шаблонов можно окрашивать hex-код в разные цвета. Например, заголовок файла может отображаться цветом, отличным от всего остального файла. Есть два способа использования «цветов. Если вы хотите установить цвет одиночной переменной, можете использовать атрибут '<fgcolor=???>' или '<bgcolor=???>' для установки цвета символов и фона соответственно. Аргументом ('???') может являться как встроенная константа цвета (cBlack, cYellow, cWhile, и т.п.), или число в формате '0xBBGGRR' (например, 0xFF0000 это синий цвет). Например:

int id <fgcolor=cBlack, bgcolor=0x0000FF>; 

Второй способ это использование функций SetForeColor, SetBackColor и SetColor. Всем переменным, используемым после одной из данных функций, будет присвоен цвет, определенный ими. Константа 'cNone' выключает использование цветов. Например:

    SetForeColor( cRed ); // установить цвет символов красным
    int first;  // будет окрашена в красный цвет
    int second; // будет окрашена в красный цвет
    SetForeColor( cNone ); // установить цвет символов на стандартный
    int third;  // не будет окрашено в какой-либо цвет
Порядок байт (endian)

Способ чтения и записи зависит от порядка байт в файле: big-endian или little-endian. По умолчанию все объявленные переменные имеют тот же порядок байт, что и исследуемый файл, но режимы можно переключать с использованием функций BigEndian() и LittleEndian(). Используйте данную методику если в файле содержатся данные с различным порядком байт.

Комментарии

Можно добавлять комментарии к переменным с использованием атрибута '<comment=»">'. Например:

    int machineStatus <comment="Это значение должно быть больше, чем 15.">; 

Данный комментарий будет показан в колонке Comment панели Template Results. Также в виде комментария может указываться произвольная функция: '<comment=<function_name>>'. Такая функция берет в качестве аргументов переменную и возвращает строку, которая будет отображаться в колонке Comment. Например:

     int machineStatus <comment=MachineStatusComment>;
     
     string MachineStatusComment( int status )
     {
         if( status <= 15 )
             return "*** - ошибочный статус";
         else
             return "нормальный статус";
     }

Имена

Параметр «name» может быть использован для замены текста, отображаемого в колонке Name панели Template Results. Также, как и рассмотренный выше параметр комментариев, параметр имен может быть установлен с использованием '<name="">' или может содержать произвольную функцию '<name=<function_name>>'. Использование функций в параметре «name» эквивалентно использованию в параметре «comment»: данная функция принимает в качестве аргумента переменную и возвращает строку. Например:

    byte _si8 <name="Signed Byte">;

Данная переменная будет отображаться в панели Template Results не как «byte _si8», а как «Signed Byte». Обратите внимание, что если переменная является частью массива, к имени будут автоматически приписаны индексы массива.

Порядок чтения переменных

После объявления каждой переменной в шаблоне происходит смещение текущего адреса чтения. Текущий адрес может быть считан с помощью функции FTell, а перемещения обработаны с помощью FSeek и FSkip. Данная методика позволяет разбирать файл без строго оговоренного порядка. Обратите внимание, что для чтения файла без объявления переменных, можно использовать функции ReadByte, ReadShort, ReadInt, и т.п.

Локальные переменные

В некоторых случаях приходится использовать переменные, которые надлежит не отображать в панели Template Results, и которые вообще могут не относится ко внутренней структуре файла (т.е. являться обычными переменными C/C++). В таком случае при объявлении переменных следует использовать ключевое слово 'local'. Например:

    local int i, total = 0;

    int    recordCounts[5];
    for( i = 0; i < 5; i++ )
        total += recordCounts[i];
    double records[ total ]; 

В данном примере переменная i не добавляется в панель Template Results, но, тем не менее, можно включить отображение локальных переменных путем команды Show Local Variables (клик правой кнопкой мыши по Template Results).

Статус переменных

После запуска шаблона все созданные переменные отображаются в дереве на панели Template Results. По умолчанию все массивы и структуры являются «свернутыми» и имеют возможность «разворачивания» только путем нажатия на '+' в перечне; тем не менее, иногда полезно иметь перед глазами развернутую структуру или полный перечень элементов массива, при чем чтобы они отображались таким образом в Template Results сразу после выполнения шаблона. Для этого можно использовать атрибут '<open=true>' в параметрах переменной, а атрибут '<open=false>' может быть также использован для того, чтобы некоторая структура или массив отображались свернутыми (как это установлено по умолчанию).

Строки

В 010 Editor есть два варианта синтаксиса для работы с нуль-терминированными строками:

    char str[];

или

   string str; 

Оба подразумевают чтение строки до того момента, как попадется 0. Строки в кодировке Unicode strings (длинные строки) могут быть прочитаны с использованием:

    wchar_t str[]; 

или

    wstring str; 

Перечисления

При объявлении переменной типа enum (перечисление), в панели Template Results отобразится стрелка с выпадающим списком, в котором будет представлено все содержимое перечисления.

Скрытые переменные

Атрибут '<hidden=true>' позволяет скрыть отображение переменных в панели Template Results, '<hidden=false>' — для отмены.

II — Структуры и объединения

Структуры

В языке С ключевое слово 'struct' используется для объявления иерархической структуры данных, в 010 оно имеет точно такое же назначение. Структуры объявляются с использованием синтаксиса C/C++, например:

    struct myStruct {
        int a;
        int b;
        int c;
    }; 

Данный код автоматически создает новый тип myStruct, следующим этапом следует объявить переменную данного типа (экземпляр структуры):

    myStruct s; 

После объявления переменной типа myStruct в Template Results появится запись 'myStruct s' со значком '+'. Кликнув на '+', вы сможете увидеть выпадающий список переменных и их значений: a, b, c.

Экземпляры могут также объявляться альтернативным способом:

    struct myStruct {
        int a;
        int b;
        int c;
    } s1, s2; 

Данный код создаст два экземпляра типа myStruct, при этом s1 займет первые 12 байт файла (3 целочисленных integer-значения по 4 байта каждый), а s2 — следующие 12 байт.

Данные структуры более «мощные», чем структуры стандарта языка C. В 010 поддерживается использование таких выражений, как if, for, while. Например:

    struct myIfStruct {
        int a;
        if( a > 5 )
            int b;
        else
            int c;
    } s; 

В данном примере после объявления экземпляра структуры s, создается только две переменные: a и либо b, либо c. Учтите, что шаблоны выполняются как интерпретируемый код, так что все строки выполняются последовательно. Значение переменной a считывается прямо из файла.

Структуры также могут быть вложенными, например:

   struct {
        int width;

        struct COLOR {
            uchar r, g, b;
        } colors[width];

    } line1; 

Альтернативным способом объявления структур является typedef. Например:

    typedef struct {
        ushort id;
        int    size;
    } 
    myData;  
Объединения

Объединения могут быть объявлены тем же способом, что и структуры, с той лишь разницей, что используется ключевое слово 'union' вместо 'struct'. В объединениях все переменные начинаются с «нулевой позиции», то есть с адреса самого объединения (по сути, «накладываются» друг на друга). Размер объединения равен размеру максимальной по числу байт переменной. Например:

   union myUnion {
        ushort s;
        double d;
        int    i;
    } u; 

Все три переменные будут прочитаны начиная с одного адреса и размер объединения будет 8 байтам, поскольку оно содержит переменную типа double.

Структуры и объединения с аргументами

При объявлении структур или объединений после ключевого слова 'struct' или 'union' можно использовать список аргументов. Это удобный способ работы с комплексными структурами, перечень аргументов определяется тем же способом, что и у функций. Например:

    struct VarSizeStruct (int arraySize)
    {
        int id;
        int array[arraySize];
    }; 

После объявления данной структуры при создании ее экземпляра необходимо заключать в круглые скобки соответствующие аргументы, например:

    VarSizeStruct s1(5);
    VarSizeStruct s2(7); 

Аргументы могут быть также использованы в структурах и объединениях, объявленных как typedef. Например:

   typedef struct (int arraySize)
    {
        int id;
        int array[arraySize];
    } VarSizeStruct; 

III — Массивы, дубликаты, оптимизация

Массивы и дубликаты

В 010 Editor есть специальный синтаксис, который позволяет создавать массивы особым способом. Поддерживается объявление переменных с одинаковым названием (дубликатов), например:

    int x;
    int y;
    int x; 

В 010 Editor объявление переменных с одинаковым названием эквивалентно созданию массива. В данном примере x[0] может быть использовано как ссылка на первое использование x, а x[1] как ссылка на второе. Дубликаты могут объявляться и в циклах, например:

    local int i;
    for( i = 0; i < 5; i++ )
        int x; 

Оптимизация структур

010 Editor по умолчанию поддерживает оптимизацию, дающую возможность работать с массивами структур с большим числом элементов (например, миллионом). Оптимизация работает следующим образом: подсчитывается размер первого элемента массива, после чего подразумевается, что все остальные элементы будут того же самого размера. Соответственно, в случае с элементами переменного размера, результат будет ошибочным. Для выключения оптимизации используйте атрибут '<optimize=false>'. Например:

    typedef struct {
        int   id;
        int   length;
        uchar data[ length ];
    } RECORD;

    RECORD record[5] <optimize=false>; 

Если 010 Editor отображает предупреждение в поле Output, но вы по-прежнему хотите использовать оптимизацию, включите ее принудительное использование с помощью атрибута '<optimize=true>', предупреждение будет скрыто. Атрибут '<optimize=false>' может быть также использован для структур, объявленных не через typedef.

IV — Побитовые структуры

Поля в структурах могут быть разделены на группы бит, данная возможность позволяет хранить несколько переменных в одном блоке памяти. Синтаксис для объявления побитового поля:

имя_типа <имя_переменной> : число_бит

Тип может быть char, short, int, int64 (unsigned или signed) или любой другой эквивалентный тип. Если название некоторой переменной не указано, она будет пропущена. Например:

    int alpha : 5;
    int       : 12;
    int beta  : 15;  

Таким образом, в одном четырех байтовом поле (32 бита) будет две перменных: alpha и beta, первая занимает 5 бит, вторая — 15 бит, при этом 12 бит между переменными пропущено.

Побитовые структуры могут быть скомбинированы с объединениями путем добавления после его объявления символа двоеточия и значения, определяющего число бит. Например, для того, чтобы «упаковать» два объединения в одиночный ushort можно следующим образом:

     enum <ushort> ENUM1 { VAL1_1=25, VAL1_2=29, VAL1_3=7 } var1 : 12;
     enum <ushort> ENUM2 { VAL2_1=5,  VAL2_2=6 }            var2 : 4; 

V — Произвольные переменные (custom variables)

Некоторые бинарные форматы используются переменные специфичных типов, неудобных для наглядного представления. В 010 Editor есть хороший механизм, позволяющий определить переменную любым способом. Для этого можно создать функцию, которая переводит значение переменной в строку (предварительно совершив ряд преобразований), данная строка будет отображена на панели Template Results как значение переменной. Таков механизм чтения, аналогичным образом определяется и механизм записи.

Для присвоения функции чтения и записи для переменной используйте атрибут '<read= <имя_функции>> и <write= <имя_фунции>>' соответственно. Функция чтения принимает переменную как аргумент и возвращает строку, функция записи принимает как аргумент указатель на переменную и ее значение в строковом представлении. Например, для определения «дробного» типа, использующего 16 бит (8 старших бит определяют целую часть, а 8 младших — дробную), можно использовать функцию:

   typedef ushort FIXEDPT <read=FIXEDPTRead, write=FIXEDPTWrite>;

    string FIXEDPTRead( FIXEDPT f )
    {
        string s;   
        SPrintf( s, "%lg", f / 256.0 );
        return s;
    }

    void FIXEDPTWrite( FIXEDPT &f, string s )
    {
        f = (FIXEDPT)( Atof( s ) * 256 );
    } 

В данном примере переменная FIXEDPT может быть определена и без атрибута записи, но в таком случае она будет доступна только для чтения.

При отображении массивов в панели Template Results, обычно никаких значений не показывается до тех пор, пока массив не будет «развернут». Для того, чтобы определить некое значение сразу на весь массив, можно использовать произвольную переменную, функции чтения и записи. Например:

    typedef float VEC3F[3] <read=Vec3FRead, write=Vec3FWrite>;

    string Vec3FRead( VEC3F v )
    {
        string s;   
        SPrintf( s, "(%f %f %f)", v[0], v[1], v[2] );
        return s;
    }

    void Vec3FWrite( VEC3F &v, string s )
    {
        SScanf( s, "(%f %f %f)", v[0], v[1], v[2] );
    } 

Учите, что сейчас функции чтения и записи не могут быть определены для отдельных элементов массива или каждого элемента.

VI — Структуры On-Demand

В шаблоне может быть определено большое число переменных, что очень затратно с точки зрения системной памяти. Для того, чтобы учесть этот нюанс, можно использовать структуры On-Demand. В таком случае переменные внутри структуры или объединения не «переносятся» на исследуемый файл до того момента, пока это не будет необходимым (например, пока структура не будет развернута в панели Template Resilts). Для того, чтобы использовать структуры или объединения On-Demand, необходимо заранее вручную указать их размеры: '<size=|'. Размер таких структур и объединений всегда фиксирован. Например:

     typedef struct
     {
         int header;
         int value1;
         int value2;
     } MyStruct <size=12>;

В данном случае содержимое структуры MyStruct (header, value1 и value2) не определяются до того момента, когда это будет необходимо.

VII — Ограничения

Шаблоны 010 Editor, не смотря на ряд сходств с ANSI C, имеют ряд существенных отличий и не являются полностью совместимыми. Список важных различий между ANSI C и шаблонами 010 Editor:

Указатели

На данный момент использование '*" указателей не поддерживается. Поддерживаются только ссылки '&' — для аргументов функций.

Директивы

Поддерживается большинство директив препроцессора, включая #define, #ifdef, #ifndef, и т.п. Тем не менее, директива #if на данный момент не поддерживается, как и объявление макроса с помощью директивы #define.

Многомерные массивы

На данный момент многомерные массивы не поддерживаются, но есть альтернативный способ их использования. Например, вместо определения 'float matrix[4][4]', тот же самый массив может быть определен как:

    typedef struct 
    {
        float row[4];
    } 
    MATRIX[4];
    MATRIX m; 

Передача управления

Goto не поодерживается.

Если есть вопросы, задавайте, всегда готов ответить.
В переводе возможны мелкие неточности, если нашли что-либо, пишите в ЛС, поправлю.
С уважением, Dageron.

См так же:

Автор: Dageron

Источник

Поделиться

* - обязательные к заполнению поля