First commit
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
# Формат файлов USR: хранение, распаковка и обратная упаковка
|
||||
|
||||
**Проект:** `ric930-usrinfo`
|
||||
**Версия документа:** 1.1
|
||||
**Дата:** 16 октября 2026 г.
|
||||
**Статус:** техническая спецификация, восстановленная методом реверс-инжиниринга (**неофициальная**)
|
||||
|
||||
## 1. Назначение и краткое описание
|
||||
|
||||
Файл `.USR` данного типа содержит XML-документ, упакованный по схеме:
|
||||
|
||||
```text
|
||||
КОДИРОВАНИЕ
|
||||
+---------------------+ +---------------------+ +----------------------+
|
||||
| XML: исходные байты | ---> | zlib (RFC 1950) | ---> | Побайтовое сложение | ---> .USR
|
||||
| Windows-1251 | | DEFLATE (RFC 1951) | | по модулю 256 |
|
||||
+---------------------+ +---------------------+ +----------------------+
|
||||
|
||||
ДЕКОДИРОВАНИЕ
|
||||
.USR ---> обратное побайтовое вычитание ---> zlib inflate ---> XML (те же байты)
|
||||
```
|
||||
|
||||
Содержимое `.USR` **не** является непосредственно XML, архивом ZIP или GZIP. После снятия обфускации получается **один полный поток zlib** — с заголовком, DEFLATE-данными и контрольной суммой Adler-32.
|
||||
|
||||
Здесь (достоверно не подтверждено) собственных полей `magic`, номера версии, длины, соли, IV, ключа, MAC либо таблицы смещений, сохранённой внутри `.USR`. Весь файл, включая zlib-заголовок и контрольную сумму, обрабатывается одной арифметической операцией. **Размер USR равен размеру обфусцированного zlib-потока.** Все исследованные образцы не содержат данных после конца zlib-потока.
|
||||
|
||||
В распакованном XML находятся данные о конфигурации установки «КонсультантПлюс»: версии компонентов, сведения о системе, составе поставки и информационных банках. Это **содержимое**, а не заголовок бинарного контейнера.
|
||||
|
||||
## 2. Результаты исследования
|
||||
|
||||
### 2.1 Установленные факты
|
||||
|
||||
1. После арифметического преобразования файлов `.USR` получаются корректные потоки zlib, начинающиеся с `78 9C`.
|
||||
2. zlib распаковывает оба потока без ошибки и с корректной Adler-32; после потока нет лишних байтов.
|
||||
3. Полученные XML **побайтово совпадают** с эталонными файлами, расшифрованными оригинальной утилитой.
|
||||
4. `ric930-usrinfo` распаковывает образцы идентично эталонам и осуществляет обратный переход `XML → USR → XML` **без потери XML-байтов**. Сами вновь созданные USR отличаются от эталонных по DEFLATE-представлению.
|
||||
|
||||
### 2.2. Что **не** полуичлось подтвердить
|
||||
|
||||
- Полнота поддержки всех поколений USR.
|
||||
- Побайтовая идентичность USR, созданных произвольным zlib-компрессором, с USR, созданными оригинальной программой.
|
||||
|
||||
## 3. Бинарная организация USR-контейнера
|
||||
|
||||
Пусть `U[0..N-1]` — байты файла `.USR`; `Z[0..N-1]` — байты потока **zlib** после снятия обфускации; `X[0..M-1]` — исходные байты XML.
|
||||
|
||||
```text
|
||||
СМЕЩЕНИЯ В ФАЙЛЕ .USR:
|
||||
0 N-1
|
||||
+-------------------------------------------------------------+
|
||||
| U[0] U[1] U[2] ... U[N-1] |
|
||||
+-------------------------------------------------------------+
|
||||
каждый байт подвергнут обфускации
|
||||
|
|
||||
v
|
||||
СМЕЩЕНИЯ В ВОССТАНОВЛЕННОМ ZLIB:
|
||||
0 1 2 N-4 N-1
|
||||
+------+------+----------------------------------------+--------+
|
||||
| CMF | FLG | DEFLATE: сжатые XML-байты |ADLER32 |
|
||||
+------+------+----------------------------------------+--------+
|
||||
2 байта переменная длина 4 байта
|
||||
|
|
||||
zlib inflate
|
||||
v
|
||||
X[0..M-1] = байты XML
|
||||
```
|
||||
|
||||
- **Нет собственного 4- или 8-байтового заголовка перед zlib:** операция начинается с `U[0]`, то есть с самого первого байта файла.
|
||||
- **Нет отдельного хвоста USR после zlib:** последние четыре байта zlib являются Adler-32, а в файле USR они тоже обфусцированы.
|
||||
- **Нет независимого поля длины XML в контейнере:** размер результата определяется распаковкой.
|
||||
- Порядок байтов самой обфускации не зависит от endian архитектуры; **Adler-32** внутри zlib хранится в big-endian (старший байт первым).
|
||||
- `.USR` следует обрабатывать как бинарный файл: нулевые байты, произвольные байтовые значения и кодировки текстовых файлов здесь не имеют значения.
|
||||
|
||||
Важно: `99 EF` встречается в начале **ВСЕХ** примеров файлов, поскольку обфусцирован заголовок `78 9C`. Это **не универсальная сигнатура всех USR**: другие корректные заголовки zlib могут дать другие первые два байта.
|
||||
|
||||
## 4. Побайтовая обфускация: точная формальная спецификация
|
||||
|
||||
### 4.1. Константы
|
||||
|
||||
| Параметр | Hex | Decimal | Значение |
|
||||
|---|---|---:|---|
|
||||
| Начальное смещение `A` | `0x21` | 33 | Первый аддитивный байт |
|
||||
| Шаг `B` | `0x32` | 50 | Приращение после каждого байта |
|
||||
| Модуль | `0x100` | 256 | Арифметика беззнакового байта |
|
||||
| Начальный индекс | `0x00` | 0 | Отсчёт от начала файла |
|
||||
|
||||
Для индекса `i = 0, 1, ..., N-1`:
|
||||
|
||||
**Декодирование (USR → zlib):**
|
||||
|
||||
```text
|
||||
K(i) = (0x21 + 0x32 * i) mod 256
|
||||
Z[i] = (U[i] - K(i)) mod 256
|
||||
```
|
||||
|
||||
**Кодирование (zlib → USR):**
|
||||
|
||||
```text
|
||||
K(i) = (0x21 + 0x32 * i) mod 256
|
||||
U[i] = (Z[i] + K(i)) mod 256
|
||||
```
|
||||
|
||||
Рекуррентная запись, удобная для потоковой реализации:
|
||||
|
||||
```text
|
||||
offset = 0x21
|
||||
для каждого байта:
|
||||
decode: z = uint8(u - offset)
|
||||
encode: u = uint8(z + offset)
|
||||
offset = uint8(offset + 0x32)
|
||||
```
|
||||
|
||||
**Ключевой момент:** `offset` **нельзя сбрасывать при чтении очередного буфера**; он обновляется непрерывно от начала файла до его конца. Индекс также начинается заново при обработке **следующего файла**.
|
||||
|
||||
### 4.2. Период последовательности
|
||||
|
||||
Поскольку `gcd(50, 256) = 2`, наименьший период равен `256 / 2 = 128`:
|
||||
|
||||
```text
|
||||
K(i + 128) = K(i)
|
||||
```
|
||||
|
||||
Первые смещения:
|
||||
|
||||
```text
|
||||
21 53 85 B7 E9 1B 4D 7F B1 E3 15 47 79 AB DD 0F ... (hex)
|
||||
```
|
||||
|
||||
## 5. Формат потока zlib внутри USR
|
||||
|
||||
После снятия обфускации применяется **zlib** по RFC 1950, использующий **DEFLATE** по RFC 1951.
|
||||
|
||||
### 5.1. Структура zlib
|
||||
|
||||
```text
|
||||
CMF (1 байт) | FLG (1 байт) | [DICTID при FDICT=1] | DEFLATE | ADLER32 (4 байта)
|
||||
```
|
||||
|
||||
### 5.2. Содержимое DEFLATE
|
||||
|
||||
DEFLATE использует повторяющиеся последовательности (LZ77) и коды Хаффмана. Он может содержать несколько блоков разных типов (без сжатия, с фиксированными или динамическими кодами). Подробный битовый формат определяется RFC 1951.
|
||||
|
||||
### 5.3. Adler-32 и контроль целостности
|
||||
|
||||
Последние **4 байта восстановленного zlib-потока** содержат Adler-32 **распакованных XML-байтов** в порядке big-endian:
|
||||
|
||||
```text
|
||||
s1 = 1
|
||||
s2 = 0
|
||||
for each x in XML bytes:
|
||||
s1 = (s1 + x) % 65521
|
||||
s2 = (s2 + s1) % 65521
|
||||
Adler32 = (s2 << 16) | s1
|
||||
```
|
||||
|
||||
Adler-32 помогает обнаруживать **случайное повреждение** при хранении и передаче.
|
||||
|
||||
|
||||
## 6. Использованные материалы и нормативные ссылки
|
||||
|
||||
**Стандарты и справочная документация:**
|
||||
|
||||
- RFC 1950, *ZLIB Compressed Data Format Specification version 3.3*: <https://www.rfc-editor.org/rfc/rfc1950>
|
||||
- RFC 1951, *DEFLATE Compressed Data Format Specification version 1.3*: <https://www.rfc-editor.org/rfc/rfc1951>
|
||||
- Go `compress/zlib`: <https://pkg.go.dev/compress/zlib>
|
||||
- Go `compress/flate`: <https://pkg.go.dev/compress/flate>
|
||||
- Go `encoding/xml`: <https://pkg.go.dev/encoding/xml>
|
||||
Reference in New Issue
Block a user