
Работа с GUID на Ассемблере

Работа с GUID на Ассемблере
GUID (Глобальный уникальный идентификатор) - это 128-битное значение, представленное в виде 32 шестнадцатеричных символов, разделенных дефисами на пять блоков. Формат стандартизирован для обеспечения читаемости, компактности и кроссплатформенной совместимости. Структура классического формата: 8-4-4-4-12 символов. Каждый блок содержит цифры и буквы латинского алфавита от A до F. Такая организация позволяет генерировать практически уникальные идентификаторы, что делает GUID надежным ключом для объектов и данных в различных областях программирования.
Причины выбора формата заключаются в том, что представление GUID с дефисами было принято в рамках технологий OLE/COM от Microsoft, где важна читаемость при отладке и хранении значений в текстовом виде (например, в реестре Windows). Шестнадцатеричная запись с разделителями упрощает визуальный контроль, снижает вероятность ошибок при ручном копировании и облегчает сравнение строк. Фиксированная длина (128 бит) обеспечивает теоретически огромное адресное пространство - порядка 2128 возможных значений, что делает коллизии практически невозможными при корректной генерации. Такой текстовый формат оказался удобен для логирования, конфигурационных файлов и текстовых протоколов, что способствовало его широкому распространению. Хотя изначально формат использовался в реализации Microsoft (GUID), он был стандартизирован как UUID в спецификации RFC 4122, где закреплен канонический вид записи: 8-4-4-4-12 шестнадцатеричных символов, разделенных дефисами (например, 550e8400-e29b-41d4-a716-446655440000). Сегодня этот формат стал де-факто стандартом для представления 128-битных уникальных идентификаторов.
Хотя на первый взгляд GUID может восприниматься как случайная последовательность, его структура содержит служебные поля. В каноническом представлении:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxxсимвол M (14-я позиция в строке, нумерация с 0) кодирует версию алгоритма генерации (4 бита), а символ N (19-я позиция) - вариант формата (старшие биты поля варианта). Например, в UUID 550e8400-e29b-41d4-a716-446655440000 значение "4" в позиции M указывает на версию 4 (генерация на основе случайных чисел), а значение "A" (бинарно 10xx) в позиции N соответствует варианту 1 по спецификации RFC 4122. Можете изучить какой-нибудь GUID или попробовать самостоятельно сделать валидный GUID. Для удобства можете использовать мою программу GUID Helper.
Теперь перейдем к программированию. Как я и обещал, мы будем использовать Ассемблер. Для удобства работы с GUID применяется следующая структура:
Code (Assembler) : Убрать нумерацию
- ; Структура GUID (RFC 4122 / Windows)
- struct GUID
- Data1 dd ? ; 4 байта (32 бита)
- Data2 dw ? ; 2 байта (16 бит)
- Data3 dw ? ; 2 байта (16 бит)
- Data4 db 8 dup(?) ; 8 байт (64 бита)
- ends
Code (Assembler) : Убрать нумерацию
- ; my_guid - данные для работы с GUID
- ; Загружаем Data3 (смещение 6)
- movzx eax,word[my_guid.Data3]
- ; Сдвигаем вправо на 12 бит, остаются биты 15-12
- shr eax,12
- ; EAX определяет версию
- ; 0 -> Nil GUID (00000000-0000-0000-0000-000000000000)
- ; 1 -> Time-based (на основе временной метки и MAC-адреса)
- ; 2 -> DCE Security (устаревший, с UID домена)
- ; 3 -> Name-based с хешем MD5
- ; 4 -> Random (на основе криптографически стойких случайных чисел)
- ; 5 -> Name-based с хешем SHA-1
- ; 6 -> Reordered Time (альтернативный формат временной метки)
- ; 7 -> Unix Time (временная метка в формате Unix)
- ; 8 -> Custom (пользовательский формат)
- ; 9-F -> Зарезервировано
Поле варианта (variant) определяет формат и происхождение идентификатора. Оно хранится в старших битах первого байта поля Data4 (смещение +8 в бинарном представлении), что соответствует 19-й позиции в строковом формате xxxxxxxx-xxxx-xxxx-Nxxx-xxxxxxxxxxxx, где символ N отражает значение варианта. Для извлечения варианта необходимо: загрузить первый байт поля Data4, сдвинуть его вправо на 6 бит - останутся старшие 2 бита (биты 7-6), интерпретировать полученное значение согласно спецификации:
Code (Assembler) : Убрать нумерацию
- ; my_guid - данные для работы с GUID
- ; Загружаем первый байт Data4
- movzx eax,byte[my_guid.Data4]
- ; Оставляем только старшие 2 бита (биты 7-6)
- shr al,6
- ; EAX определяет вариант:
- ; 0 -> NCS (устаревший формат, до стандартизации UUID)
- ; 1 -> RFC 4122 (современный стандарт, наиболее распространенный)
- ; 2 -> Microsoft COM/DCOM (устаревший формат Microsoft)
- ; 3 -> Future (зарезервировано для будущего использования)
Теперь перейдем к генерации GUID в ручном режиме. Для этого сначала необходимо получить 16 байт (128 бит) случайных данных удобным для вас способом, например, с помощью функции BCryptGenRandom. Эта функция из API Windows Cryptography API, предоставляет криптографически стойкие случайные данные.
Code (Assembler) : Убрать нумерацию
- ; my_guid - данные для работы с GUID
- ; Делаем "чистый" случайный GUID
- invoke BCryptGenRandom,NULL,my_guid,GUID.sizeof,\
- BCRYPT_USE_SYSTEM_PREFERRED_RNG
- ; Установка версии Random (v4)
- and [my_guid.Data3],0x0FFF
- or [my_guid.Data3],0x4000
- ; Установка варианта RFC 4122
- and byte [my_guid.Data4],0x3F
- or byte [my_guid.Data4],0x80
Для генерации GUID лучше использовать специальные системные функции: UuidCreate - основная функция из библиотеки rpcrt4.dll, генерирующая криптографически стойкий UUID (обычно версии 4 на основе случайных данных). CoCreateGuid - COM-обертка над UuidCreate из библиотеки ole32.dll. Функционально она не дает никаких преимуществ: просто вызывает UuidCreate и возвращает результат. Используется исключительно для удобства в COM-приложениях, когда ole32.dll уже загружена, а подключать дополнительно rpcrt4.dll не требуется. Обе функции возвращают валидный GUID, соответствующий стандарту RFC 4122. При ручной генерации через случайные числа (например, BCryptGenRandom) необходимо самостоятельно устанавливать биты версии и варианта, системные функции делают это автоматически.
Code (Assembler) : Убрать нумерацию
- ; my_guid - данные для работы с GUID
- ; Генерирует новый случайный UUID
- invoke UuidCreate,my_guid
- ; Он же, COM-обертка над UuidCreate
- invoke CoCreateGuid,my_guid
Code (Assembler) : Убрать нумерацию
- ; my_guid - данные для работы с GUID
- ; GUID заполнен нулями {00000000-0000-0000-0000-000000000000}
- invoke UuidCreateNil,my_guid
Для сравнения GUID используется функция IsEqualGUID, которая выполняет побайтовую проверку двух 128-битных идентификаторов. Функция игнорирует версию и вариант, сравнивает только "сырые" байты, поэтому два валидных GUID с разными версиями будут признаны разными. Такой подход широко применяется при работе с COM-интерфейсами, базами данных и любыми системами, где требуется явная проверка наличия уникального ключа перед его использованием.
Code (Assembler) : Убрать нумерацию
- ; Сравнить my_guid1 и my_guid2
- invoke IsEqualGUID,my_guid1,my_guid2
- add esp,8
- ; Возвращает результат в AL (не BOOL!)
- test al,al
- ; AL = 0, не равны
- jz .not_equal
- ; AL != 0, равны
- jnz .equal
Code (Assembler) : Убрать нумерацию
- ; Сравнение с учетом порядка байт
- invoke UuidCompare,my_guid,my_uuid2
- add esp,12
- ; Возвращает результат в EAX
- ; < 0 - my_guid1 < my_guid2
- ; = 0 - my_guid1 == my_guid2
- ; > 0 - my_guid1 > my_guid2
- test eax,eax
- ; Равно
- jz .equal
- ; Меньше
- js .less
- ; Больше
- jg .more
Code (Assembler) : Убрать нумерацию
- ; Указатель на UUID
- invoke UuidHash,my_guid,status
- add esp,8
- ; EAX содержит 32-битный хеш
Code (Assembler) : Убрать нумерацию
- ; Размер буфера (38 символов + нуль-терминатор)
- invoke StringFromGUID2,my_guid,szBuffer,39
- add esp,12
- ; Возвращает количество записанных символов, включая 0
- ; или 0 при ошибке
Code (Assembler) : Убрать нумерацию
- ; Указатель на UUID
- invoke StringFromCLSID,my_guid,pszResult
- add esp,8
- test eax,eax
- jnz .loc_error
- ; Использование результата, например, ESI - указатель на строку
- mov esi,[pszResult]
- ; Обязательно освобождение памяти
- call CoTaskMemFree,[pszResult]
Code (Assembler) : Убрать нумерацию
- ; Указатель на UUID
- invoke UuidToString,my_guid,pwszResult
- add esp,8
- test eax,eax
- jnz .loc_error
- ; Использование
- mov esi,[pwszResult]
- ; Освобождение памяти через RpcStringFree
- invoke RpcStringFree,pwszResult
Самый распространенный и надежный выбор - функция CLSIDFromString. Она принимает строку в формате Unicode (UTF-16) и заполняет 16-байтную структуру GUID, переданную по указателю. Главное преимущество - гибкость: функция одинаково хорошо понимает строки как с фигурными скобками "{...}", так и без них. Это особенно удобно, когда источник данных непредсказуем, например, один компонент сохраняет с скобками, другой без. Функция сама разбирает формат, проверяет корректность шестнадцатеричных цифр и расставляет байты в правильном порядке. Если используется обычная ANSI-строка из файла в кодировке Windows-1251 или UTF-8, ее сначала нужно преобразовать через MultiByteToWideChar, иначе результат будет неверным или функция вернет ошибку.
Code (Assembler) : Убрать нумерацию
- ; Строка в UTF-16
- invoke CLSIDFromString,szGuid,my_guid
- ; Указатель на структуру GUID (16 байт)
- add esp,8
- test eax,eax
- ; EAX = 0 - my_guid заполнен корректными данными
- jz .loc_ok
- ; EAX != 0 - ошибка преобразования
Code (Assembler) : Убрать нумерацию
- ; Строка в UTF-16
- invoke IIDFromString,szGuid,my_guid
- ; Указатель на структуру GUID (16 байт)
- add esp,8
- test eax,eax
- ; EAX = 0 - my_guid заполнен корректными данными
- jz .loc_ok
- ; EAX != 0 - ошибка преобразования
Code (Assembler) : Убрать нумерацию
- ; 550e8400-e29b-41d4-a716-446655440000
- invoke UuidFromString,szUuid,my_guid
- add esp,8
- test eax,eax
- ; EAX = 0 - my_guid заполнен корректными данными
- jz .loc_ok
- ; EAX != 0 - ошибка преобразования
Code (Assembler) : Убрать нумерацию
- ; {550e8400-e29b-41d4-a716-446655440000}
- invoke GUIDFromString,szGuid
- add esp,4
- test eax,eax
- ; EAX = 0 - ошибка
- jz .loc_error
- ; EAX = HANDLE на внутренний объект
- mov [hResult],eax
- ; Требует освобождения памяти
- invoke GlobalFree,eax
Просмотров: 174 | Комментариев: 1
Метки: Assembler
Комментарии
Отзывы посетителей сайта о статье
Добавить комментарий
Заполните форму для добавления комментария


С тех пор захотелось заиметь такой "поисковик GUID`ов" (во всех подобных представлениях), чтоб искать их как в файловой системе, так и в реестре (+ в "динамически-подгружаемом" кусте COMPONENTS), причём и в "нестроковых" типах значений тоже, т.е. помимо REG_SZ | REG_MULTI_SZ | REG_EXPAND_SZ, искать в других типах как ASCII-текст введённого GUID`а, так и те самые 16 байт… Ну и банально для поиска того номера в Shell\Bags, в котором ColInfo или Sort содержит определённый PropID (без предварительного поиска имени папки в BagsMRU (тоже записанной в двоичном типе значения), и, соответственно, определения его номера)…