[Урок] Структура smx

Kailo

Участник
Сообщения
168
Реакции
755
Данный урок требует продвинутых знаний в программирование и труден к пониманию новичкам.

Введение
*.smx файл содержит программу и дополнительную информацию о ней (не обязательную для работы программы). Хранящаяся информация представляет собой значения переменных в строго определенном порядке, записанных так, как они хранятся в памяти компьютера, а не в удобном для чтения виде. Такие файлы еще называют бинарными.

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


Структура, Заголовок
Теперь разберём начало smx файла.
Файл начинается с заголовка (англ. header).
Из исходников компилятора: include/smx/smx-headers.h (в исходниках есть дополнительная информация в виде комментариев на английском)
Код:
typedef struct sp_file_hdr_s
{
  uint32_t  magic;
  uint16_t  version;
  uint8_t   compression;
  uint32_t  disksize;
  uint32_t  imagesize;
  uint8_t   sections;
  uint32_t  stringtab;
  uint32_t  dataoffs;
} sp_file_hdr_t;
Теперь раскроем, за что отвечают эти данные:
magic - индификатор типа файла
version - версия формата smx
compression - тип сжатия (далее об этом будет подробнее)
disksize - размер в байтах занимаемый файлом
imagesize - размер в байтах занимаемый файлом после разжатия
sections - кол-во секций в файле
stringtab - отступ (смещение, англ. offset) от начала файла до места, где хранятся имена секций (в байтах, и все размеры и отступы в байтах, если не сказано другого)
dataoffs - отступ от начала файла до места, где начинаются секции

Magic
Сведения о magic из исходников компилятора: include/smx/smx-headers.h
Код:
// SourcePawn File Format (SPFF) magic number.
 static const uint32_t FILE_MAGIC = 0x53504646;
Version
0x0101 - SourcePawn 1.0; изначальная версия, используемая SourceMod 1.0.
0x0102 - SourcePawn 1.1; используется SourceMod 1.1+.
0x0200 - Используется spcomp2.
P.S. Я работал только с 0x0102 и отличий от других версий не знаю.

Compression
Основная часть данных может быть сжата.
0 - сжатия нет
1 - Gzip
Если сжатие есть, оно начинается с dataoffs и до конца файла. Поэтому обычные программы для разжатия не помогут.

Рассмотрим header на примере моего smartjaildoors.smx (Я использую Notepad++ с плагином HEX-editor)
Первые 24 байта файла:
Код:
46 46 50 53 02 01 01 b3 8c 00 00 5a df 01 00 0d b4 00 00 00 2f 01 00 00
46 46 50 53 | 02 01 | 01 | b3 8c 00 00 | 5a df 01 00 | 0d | b4 00 00 00 | 2f 01 00 00
Переставляя байты и объединяя получаем
magic: 0x53504646
version: 0x0102
compression: 0x01
disksize: 0x8cb3, 36019 (в DEC)
imagesize: 0x1df5a, 122714
sections: 0xd, 13
stringtab: 0xb4, 180
dataoffs: 0x12f, 303

Заголовоки секций
Сразу после заголовка файла, следуют заголовки секций.
Их кол-во равно sections из заголовка файла.
Из исходников: include/smx/smx-headers.h
Код:
typedef struct sp_file_section_s
{
  uint32_t  nameoffs;  /**< Offset into the string table. */
  uint32_t  dataoffs;  /**< Offset into the file for the section contents. */
  uint32_t  size;      /**< Size of this section's contents. */
} sp_file_section_t;
nameoffs - Отступ от начала таблицы строк, до места, где хранится имя секции.
dataoffs - Отступ от начала файла, до места, где начинается секция.
size - Размер секции.

Таблица строк (англ. Stringtab, String table)
Содержит строки заканчивающиеся нулевым символом.
Соответственно, когда читаем заголовок секции, обращаемся по адресу stringtab + nameoffs и получаем имя секции, читая строку пока не встретится '\0'.

P.S. Так же заголовком можно назвать сумму заголовка файла, заголовков секций и таблицу строк, т.е. все до начала сжатых данных. А сжатые данные, т.е. содержание секций назвать данными (англ. data).

Структура, Секции
У каждой секции своё предназначение и своя структура. Секции, содержащие в имени .dbg префикс, содержат информацию не обязательную для работы программы, но нужную для её отладки. В настоящее время в файлах используются следующие секции:
.code - набор байт-кодов
.data - начальные значения переменных
.publics - таблица, позволяющая вызывать функции сторонним плагинам
.pubvars - информация о публичных переменных
.natives - краткая информация о нативных функциях
.names - набор строк, содержащий специфицированные имена публичных функций и перменных, нативов, тегов (типов)
.dbg.files - информация о файлах, используемых при компиляции
.dbg.symbols - информация о переменных
.dbg.lines - информация о строках в исходных файлах
.dbg.natives - полная информация о нативных функциях
.dbg.strings - набор строк, содержащий имена файлов, переменных, функций, нативов.
.dbg.info - информация о содержании отладочной информации
.tags - информация о тегах (типах)

.code
В начале .code секции идет заголовок секции.
Из исходников: include/smx/smx-headers.h
Код:
typedef struct sp_file_code_s
{
    uint32_t codesize; /**< Size of the code section. */
    uint8_t cellsize; /**< Cellsize in bytes. */
    uint8_t codeversion; /**< Version of opcodes supported. */
    uint16_t flags; /**< Flags. */
    uint32_t main; /**< Address to "main". */
     uint32_t code; /**< Offset to bytecode, relative to the start of this section. */
} sp_file_code_t;
codesize - размер данных в секции (не путайте с размером секции)
cellsize - кол-во байт в ячейки (англ. cell) (по сути всегда равна 4)
codeversion - версия данных
flags - битовые флаги
main - адрес "main" функции (по сути не используется и всегда 0)
code - отступ от начала секции до начала данных секции

Пример заголовка code секции:
codesize: 7488
cellsize: 4
codeversion: 10
flags: 1
main: 0
code: 16

Оставшаяся часть это байт-код.
Данные представляют собой набор чисел типа int (т.к. cellsize 4).
Пример первых 32-х чисел из данных code секции:
Код:
120 0 46 137 137 39 100 135 0 1 137 39 120 135 0 1 137 39 136 135 0 1 137 39 156 135 0 1 137 39 180 135 0 ...
О том, что обозначают данные числа, читайте в отдельном уроке. Урок:Байт-код SourcePawn (урок будет написан позже)
(Для подробностей читайте: Википедия:Байт-код)

.data
Содержит данные для инициализации памяти плагина.
Адрес в данных совпадает с адресом в памяти.
Данные включают в себя начальные значения переменных для их инициализации, как для глобальных, так и для локальных, константные массивы используемые в плагине (к примеру строки в вызовах).
В начале секции расположен заголовок:
PHP:
typedef struct sp_file_data_s
{
  uint32_t  datasize; /**< Размер секции (занимаемого места в памяти) */
  uint32_t  memsize;  /**< Общее кол-во места в памяти необходимой плагину (включая данные, heap, stack) */
  uint32_t  data;     /**< Смещение от начала секции до самих данных (равно размеру заголовка, т.е. 12) */
} sp_file_data_t;
А далее соответственно идут сами данные.

.publics

Содержит указания, какие функции являются публичными.
Представляет собой массив структур. Размер секции всегда кратен размеру структуры sp_file_publics_t.
PHP:
// ".publics" секция.
typedef struct sp_file_publics_s
{
    uint32_t  address;  /**< Адрес начала функции в code секции */
    uint32_t  name;      /**< Адрес имени функции в names секции */
} sp_file_publics_t;

.pubvars

Содержит указания, какие глобальные переменные являются публичными.
Представляет собой массив структур.
Размер секции всегда кратен размеру структуры sp_file_pubvars_t.
PHP:
// ".pubvars" секция.
typedef struct sp_file_pubvars_s
{
    uint32_t address;  /**< Адрес переменной в DAT секции */
    uint32_t name;      /**< Адрес имени переменной в names секции */
} sp_file_pubvars_t;

.natives

Последовательность (массив) int чисел, указывающих на начало имени функции в .names секции.
Нативные функции вызываются по индексу, который соответствует индексу внутри этой последовательности, которая указывает на имя нужной функции.

Пример данных:
index name
#0 563
#1 584
#2 602
#3 608
#4 617
#5 624
#6 636
#7 644
#8 659
#9 680
...
Где .name соответственно
offset name
...
563 MarkNativeAsOptional
584 VerifyCoreVersion
602 float
608 FloatMul
617 strcmp
624 SplitString
636 strcopy
644 IsClientInGame
659 SetGlobalTransTarget
680 VFormat
...

.names (таблица имен, nametable)

Эта секция представляет собой набор строк оканчивающихся нулевым символом.
На неё ссылаются элементы .publics, .pubvars, .natives, .tags, в виде отступа от начала .names секции до места, где начинается строка.

Пример содержания:
offset name
0 MaxClients
11 NULL_STRING
23 NULL_VECTOR
35 __ext_core
46 __ext_cstrike
60 __ext_sdktools
75 __version
85 myinfo
92 .10204.ToggleDoorsOnMap
... и так далее

.dbg.files

Содержит информацию о файлах, использованных при компиляции. Служит для указания пути вызова при возникновении ошибок в плагине.
Представляет собой последовательность (массив) из элементов являющихся структурой типа:
PHP:
// ".dbg.files" секция.
typedef struct sp_fdbg_file_s
{
    uint32_t  addr;   /**< адрес в code */
    uint32_t  name;   /**< отступ имени в .dbg.strings */
} sp_fdbg_file_t;
Кол-во элементов в последовательности указано в .dbg.info секции.

.dbg.symbols

Содержит информацию о всех "символах" плагина.
Под символами понимаются глобальные переменные, локальные переменные, параметры функций, функции.
Кол-во символов в секции указано в ".dbg.info" секции - num_syms.
Кол-во массивов среди них так же указано в в ".dbg.info" - num_arrays.
PHP:
// The ".dbg.symbols" table.
typedef struct sp_fdbg_symbol_s
{
    int32_t addr;            /**< Адрес (относительно DAT секции или code секции или stack frame) */
    int16_t tagid;            /**< Номер тега */
    uint32_t codestart; /**< Начало области видимости в code */
    uint32_t codeend;   /**< Конец области видимости в code */
    uint8_t ident;           /**< Тип */
    uint8_t vclass;          /**< Тип области */
    uint16_t dimcount; /**< Количество размерностей (for arrays) */
    uint32_t name;        /**< Адрес имени в ".dbg.strings" секции */
} sp_fdbg_symbol_t;
Если dimcount не равно 0, то на каждую размерность сразу после этой структуры, следуют следующие структуры:
PHP:
typedef struct sp_fdbg_arraydim_s
{
    int16_t tagid; /**< Номер тега */
    uint32_t size; /**< Размер */
} sp_fdbg_arraydim_t;
Адрес:
Для глобальных и static локальных переменных указывает на начало переменной в DAT/памяти.
Для параметров функций является положительным числом. Первый параметр соответствует 12, и каждый последующий на 4 больше.
Для функций соответствует адресу функции в code секции.

Codestart:
Для глобальных переменных соответствует началу записи файла, в котором объявлена.
Для локальных static переменных соответствует адресу объявления переменной в code.
Для параметров соответствует адресу начала функции.
Для локальных переменных соответствует адресу объявления переменной в code.

Codeend:
Для глобальных переменных соответствует концу code, или концу файла, в котором объявлена в случае static.
Для локальных static переменных соответствует адресу после котором её уже "не видно" (включая сам адрес).
Для параметров соответствует адресу конца функции.
Для локальных переменных соответствует адресу, после которого переменная перестает существовать, т.к. память выделенная под неё "освобождается" (включая сам адрес).

Ident:
PHP:
static const uint8_t IDENT_VARIABLE = 1;      // Обычное значение.
static const uint8_t IDENT_REFERENCE = 2; // Ссылка на значение (для аргументов).
static const uint8_t IDENT_ARRAY = 3;            // Массив с известным размером.
static const uint8_t IDENT_REFARRAY = 4;    // Массив с неизвестными размерами (по сути ссылка на массив).
static const uint8_t IDENT_FUNCTION = 9;    // Функция.
static const uint8_t IDENT_VARARGS = 11;     // Variadic argument (...).
Vclass:
PHP:
#define sGLOBAL 0 /* Глобальная переменная/константа/функция */
#define sLOCAL 1 /* Локальная переменная/константа */
#define sSTATIC 2 /* static переменная в функции ("Живет" в глобальной области, но доступна только в локальной) */

.dbg.lines

Используется для указания строки при возникновении ошибки.
Содержит последовательность (массив) из структур вида
PHP:
// The ".dbg.lines" section.
typedef struct sp_fdbg_line_s
{
    uint32_t  addr;   /**< Адрес в code */
    uint32_t  line;   /**< номер строки */
} sp_fdbg_line_t;
Пример данных между файлами. До 96 код первого файла, с 96 начинается код второго файла.
Код:
адрес строка
12 8
16 9
40 10
64 12
88 13
96 3
100 4
...

.dbg.natives

Содержит прототипы "нативных" функций используемых в плагине.
В начале секции расположен её заголовок:
PHP:
typedef struct sp_fdbg_ntvtab_s
{
  uint32_t num_entries;  /**< Кол-во записей в секции. */
} sp_fdbg_ntvtab_t;
Далее по структуре представляет собой последовательно размещенные описания функций с опциональным добавлением повторяющихся блоков данных между записями. К примеру
Код:
Native 1
    [аргумент 1]
    [аргумент 2]
        [размерность 1]
    [аргумент 3]
Native 2
Если функция не имеет аргументов, то сразу после описании функции идет описание следующий.
Если у неё есть аргументы, они описываются сразу после описания функция в порядке от первого до последнего.
Если аргумент является массивом, то после описания аргумента идет описание каждой размерности (т.е. есть аргумент функции имеет тип array[5][10], то будет описание двух размерностей).
Сами описания представляют собой структуры данных:
Для функции:
PHP:
typedef struct sp_fdbg_native_s
{
  uint32_t index;      /**< Индекс натива в плагине (в секции ".natives"). */
  uint32_t name;      /**< Смещение в таблице имен (".dbg.strings") */
  int16_t tagid;      /**< Тип возвращаемого значения */
  uint16_t nargs;      /**< Кол-во аргументов */
} sp_fdbg_native_t;
Для аргумента:
PHP:
typedef struct sp_fdbg_ntvarg_s
{
  uint8_t   ident;    /**< Вид переменной (Ident), аналогично ".dbg.symbols", см. выше */
  int16_t   tagid;    /**< Тип */
  uint16_t  dimcount; /**< Размерность (для массивов) */
  uint32_t  name;     /**< Смещение в таблице имен (".dbg.strings") */
} sp_fdbg_ntvarg_t;
Для размерности (совпадает с таким же описанием из ".dbg.symbols"):
PHP:
typedef struct sp_fdbg_arraydim_s
{
    int16_t tagid; /**< Номер тега */
    uint32_t size; /**< Размер */
} sp_fdbg_arraydim_t;

.dbg.strings

Содержит нуль-терминированные строки расположенные друг за другом. Используются другими .dbg.* секциями. Строки используются для обозначения исходных имён переменных и функций, исходных файлов, нативов, аргументов.

.dbg.info

Содержит информацию о количественном содержании элементов в других .dbg.* секциях.
Из исходников компилятора: include/smx/smx-v1.h#L83
PHP:
// ".dbg.info" секция.
typedef struct sp_fdbg_info_s
{
    uint32_t  num_files;  /**< количество файлов */
    uint32_t  num_lines;  /**< количество строк */
    uint32_t  num_syms;   /**< количество символов (переменные, функции и др.) */
    uint32_t  num_arrays; /**< количество массивов среди символов */
} sp_fdbg_info_t;

.tags

Содержит информацию о типах (тегах) использованных при компиляции. Эта информация ни как не используется в процессе работы и может быть безопасно вырезана из плагина.
Но на неё ссылаются другие секции, такие как ".dbg.natives", ".dbg.symbols".
Структурно секция представляет собой массив структур вида:
PHP:
// ".tags" секция.
typedef struct sp_file_tag_s
{
    uint32_t tag_id; /**< Tag ID из компилятора */
    uint32_t name; /**< индекс в names секции */
} sp_file_tag_t;
Tag ID является составным. Нижние 16 бит которого "номер" тега, а верхние битовые флаги:
PHP:
// Флаги
#define FIXEDTAG     0x40000000
#define FUNCTAG      0x20000000 // Объявленные через typedef или typeset
#define OBJECTTAG    0x10000000 // Handle и его дочерние теги
#define ENUMTAG      0x08000000 // Объявленные через enum
#define METHODMAPTAG 0x04000000 // Объявленные через methodmap
#define STRUCTTAG    0x02000000 // Объявленные через struct
// Макросы из исходников, TAGID позволяет получить "номер" тега из Tag ID.
#define TAGTYPEMASK   (FUNCTAG | OBJECTTAG | ENUMTAG | METHODMAPTAG | STRUCTTAG)
#define TAGFLAGMASK   (FIXEDTAG | TAGTYPEMASK)
#define TAGID(tag)    ((tag) & ~(TAGFLAGMASK))
Обычно "номер" тега совпадает с индексом ячейки массива набора тегов.
Пример информации из секции:
Код:
> Tags
 tag_id name
 #0 0 653 // 0 int flags:
 #1 1 655 // 1 bool flags:
 #2 2 660 // 2 any flags:
 #3 1610612739 664 // 3 Function flags: FIXEDTAG FUNCTAG
 #4 1073741828 673 // 4 char flags: FIXEDTAG
 #5 1073741829 680 // 5 Action flags: FIXEDTAG
 #6 1073741830 687 // 6 Identity flags: FIXEDTAG
Программы для просмотра smx
SmxViewer by BAILOPAN
SPEdit by Aeon
 
Последнее редактирование:

Drumanid

Неактивный пользователь
Сообщения
1,773
Реакции
1,458
Щас хацкеров наберется... @1mpulse , придумывай новую защиту, а то твой лк хакнут.
 

Kailo

Участник
Сообщения
168
Реакции
755
Щас хацкеров наберется... @1mpulse , придумывай новую защиту, а то твой лк хакнут.
До взлома нам еще далеко, сначала надо объяснить структуру, потом научить байткоду, потом научить его редактированию и только потом учить взлому =)
 

Kruzya

Социопат
Команда форума
Сообщения
9,153
Реакции
7,447
Спасибо, раскрыл небольшие неясные для меня моменты. Смог для теста даже скрипт на PHP набросать, который парсит заголовок плагина.


Пойду вплотную копать исходники компилятора...
Upd: Смог распаковать плагин. Изучаю структуру.
 
Последнее редактирование:
Сверху