МІНІСТЕРСТВО ОСВІТИ ТА НАУКИ УКРАЇНИ НАЦІОНАЛЬНИЙ ТЕХНІЧНИЙ УНІВЕРСИТЕТ «ХАРКІВСЬКИЙ ПОЛІТЕХНІЧНИЙ ІНСТИТУТ» О.М. Рисований РЕВЕРСНЕ ПРОГРАМУВАННЯ ЗАХИСТ КОДУ Середовище програмування masm64 Навчально-методичний посібник для студентів спеціальності 123 - "Комп'ютерна інженерія" всіх форм навчання Затверджено редакційно-видавничим порадою університету, протокол №2 від 27.06.2024 Харків 2024 ББК 32.973-018.1 УДК 004.42 Р54 Рецензенти: Н.Ю. Любченко, канд. техн. наук, доцент кафедри системного аналізу та інформаційно-аналітичних технологій НТУ ХПІ Р.А. Гамзаєв, канд. техн. наук, віце-президент „Української асоціації спеціалістів інформаційних технологій“ Рисований О.М. Р54 Реверсне програмування. Захист коду. Середовище програмування masm64: навчально-методичний посібник для студентів спеціальності 123 "Комп'ютерна інженерія" всіх форм навчання [електронне видання] / О.М. Рисований. – Харків : НТУ «ХПІ», 2024. – 214 с. Основну увагу приділено захисту програм у ОС Windows 11 під час програмування мовою Асемблер серед masm64. У роботі розглянуто питання: розміщення даних у секції коду; процедури із параметрами; перетворення логічних операцій; виклики функцій; самомодифікація коду; перевірка дати та часу; приховування пароля; зворотне шифрування; кодування файлів; хешування за алгоритмом SHA-1; обчислення CRC; шифрування повідомлень шляхом заміни бітових уявлень. Наведено лабораторні роботи із завданнями та прикладами виконання у середовищі masm64. Наведено літературу, яка використовується при вивченні матеріалу, що розглядається. Призначений для студентів спеціальності 123 – «Комп'ютерна інженерія». Іл. 54. Бібліогр. 11 назв. УДК 004.42 ББК 32.973-018.1 © О.М. Рисований, 2024 3 ЗМІСТ ВСТУП ..................................................................................................... 6 1. РОЗМІЩЕННЯ ДАНИХ У СЕКЦІЇ КОДУ ...................................... 8 1.1. Розміщення даних у секції коду .................................................. 8 1.2. Лабораторна робота «Розміщення даних у секції коду» .......... 12 Контрольні питання для перевірки знань .......................................... 23 2. ЗАСТОСУВАННЯ ПРОЦЕДУР З ПАРАМЕТРАМИ .................... 24 2.1. Передача параметрів за замовчуванням ...................................... 24 2.2. Передача параметрів процедури через стек ............................... 27 2.3. Лабораторна робота «Процедури з параметрами» .................... 30 Контрольні питання для перевірки знань .......................................... 35 3. ПЕРЕТВОРЕННЯ ЛОГІЧНИХ ОПЕРАЦІЙ ..................................... 36 3.1. Заміна команди xor ....................................................................... 36 3.2. Заміна арифметичних операцій логічними ................................. 39 3.3. Лабораторна робота «Перетворення логічних операцій» ............................................................................................. 45 Контрольні питання для перевірки знань .......................................... 46 4. ПЕРЕКРИТТЯ КОДУ ......................................................................... 47 4.1. Загальні відомості про перекриття коду .................................... 47 4.2. Перекриття коду з переходом назад за кодом програми ........... 49 4.3. Перекриття коду з переходом уперед за кодом програми ......... 53 4.4. Лабораторна робота «Перекриття коду» ................................... 60 Контрольні питання для перевірки знань .......................................... 62 5. ВИКЛИКИ ФУНКЦІЙ ....................................................................... 63 5.1. Виклик функцій без використання директиви invoke ............... 63 5.2. Організація переходу з використанням команди повернення .. 64 5.3. Організація переходу з використанням команди call та перекриттям коду .................................................................................... 67 5.4. Лабораторна робота «Організація переходів» ........................... 71 Контрольні питання для перевірки знань .......................................... 72 6. САМОМОДИФИКАЦИЯ КОДА ПРОГРАММЫ ........................... 73 6.1. Загальні відомості про самодифікацію коду .............................. 73 4 6.2. Організація переходу з використанням команди повернення .. 74 6.3. Алгоритм самодифікації коду ...................................................... 77 6.4. Самодифікуюча діалогова програма з кількома діалоговими вікнами ......................................................... 81 6.5. Лабораторна робота «Самомодифікація коду» ......................... 87 Контрольні питання для перевірки знань .......................................... 90 7. ПЕРЕВІРКА ДАТИ І ЧАСУ .............................................................. 91 7.1. Загальні відомості про фіксовану кількість запусків ................ 91 7.2. Перевірка дати закінчення запуску програми ........................... 91 7.3. Запис до файлу кількості запусків програми ............................. 95 7.4. Обмеження спроб введення пароля ............................................ 97 7.5. Запис до реєстру кількості запусків програми .......................... 99 7.6. Лабораторна робота «Перевірка дати та часу» ......................... 111 Контрольні питання для перевірки знань .......................................... 112 8. ШИФРУВАННЯ ПАРОЛЮ ............................................................... 113 8.1. Загальні відомості про шифрування пароля .............................. 113 8.2. Шифрування пароля окремою програмою ................................ 113 8.3. Вилучення частини пароля з великої послідовності ................. 115 8.4. Лабораторна робота «Приховування пароля» ........................... 120 Контрольні питання для перевірки знань .......................................... 121 9. ШИФРУВАННЯ ДАНИХ .................................................................. 122 9.1. Поворотне шифрування ............................................................... 122 9.2. Операція підсумовування за модулем 3 ..................................... 124 9.3. Шифрування даних з інкрементованим ключем ....................... 125 9.4. Лабораторна робота «Зворотне шифрування» .......................... 127 Контрольні питання для перевірки знань .......................................... 129 10. Кодування .......................................................................................... 130 10.1. Загальні відомості про кодування ............................................ 130 10.2. Отримання випадкової послідовності ...................................... 130 10.3. Алгоритм отримання псевдовипадкової послідовності .......... 140 10.4. Поліноміальне кодування .......................................................... 144 10.5. Лабораторна робота «Кодування файлів» ............................... 163 Контрольні питання для перевірки знань .......................................... 163 11. ХЕШУВАННЯ .................................................................................. 165 11.1. Загальні відомості про хешування ............................................ 165 11.2. Алгоритм хешування SHA-1 ...................................................... 166 5 11.3. Лабораторна робота «Хешування за алгоритмом SHA-1» ..... 168 Контрольні питання для перевірки знань .......................................... 169 12. КОНТРОЛЬ ЦИКЛІЧНИМ НАДЛИШНИМ КОДОМ ................. 170 12.1. Загальні відомості про поліноми .............................................. 170 12.2. Арифметика CRC ....................................................................... 171 12.3. Прямий алгоритм обчислення CRC .......................................... 173 12.4. Стандартний табличний алгоритм ............................................ 175 12.5. Лабораторна робота «Обчислення CRC» ................................. 176 Контрольні питання для перевірки знань ......................................... 176 13. ШИФРУВАННЯ ПОВІДОМЛЕНЬ МЕТОДОМ ЗАМІНИ БИТОВИХ ПРЕДСТАВ ........................................................................ 177 13.1. Метод заміни бітових уявлень .................................................. 177 13.2. Лабораторна робота «Шифрування повідомлень методом заміни бітових уявлень» ..................................................................... 187 Контрольні питання для перевірки знань ......................................... 188 14. ІНШІ МЕТОДИ ЗАХИСТУ КОДУ ................................................ 189 14.1. Змішування коду ........................................................................ 189 14.2. Застосування команд jmp .......................................................... 189 14.3. Розбиття рядків на частини ....................................................... 189 14.4. Приховування змісту програми ................................................ 189 14.5. Позбавлення адресних констант ................................................ 189 Додаток А. Поліноми degP(x)=4 у GF(2) та їх періоди ...................... 190 Додаток Б. Поліноми degP(x)=5 у GF(2) та їх періоди ....................... 191 Додаток В. Поліноми degP(x)=4 у GF(3) та їх періоди ....................... 193 Додаток Г. Поліноми degP(x)=5 у GF(3) та їх періоди ....................... 205 Додаток Д. Поліноми degP(x)=6 у GF(3) та їх періоди ...................... 207 6 ВСТУП Досконалого захисту не існує. Тому вибору методу захисту вихідного коду програми треба приділяти найсерйознішу увагу. Якісний захист повинен забезпечити такий рівень, щоб на розтин захисту потрібно було витратити зусилля, які можна порівняти з написанням самої програми. У такому разі залишається лише «спортивний» інтерес злому. Захист повинен бути багаторівневим і перекривається (тобто рівні повинні працювати незалежно). Відомо, що програма в основному складається із сегментів: – коду; – даних; - Стека. За промовчанням права доступу на читання, запис та виконання програми визначені для кожного сегмента. Так, у сегменті коду дозволено лише виконання, у сегменті даних та стеку – зчитування та запис. Однак завжди існує можливість зміни цих властивостей. При захисті програми паролем завжди, яким би складним пароль не був, можна його обійти, оскільки є код порівняння з стандартом. Для злому необхідно змінити лише одну команду розгалуження з аналізу ознаки виконання операції та програма реагуватиме на будь-який пароль [3, 6-11]. Крім того, програма зберігається в оперативній пам'яті, звідки може вважатися іншим додатком. Теоретично захисту вихідного коду виділяють методи: – приховування алгоритму роботи програми; - приховування виконуваних інструкцій; - Приховування даних шляхом шифрування. Для утруднення пошуку пароля, заплутування коду використовують поняття обфускації (від лат. Obfuscare - затіняти, затемняти; і англ. obfuscate - робити неочевидним, заплутаним, збивати з пантелику). Обфускація - це приведення вихідного тексту або виконуваного коду програми до виду, що зберігає її функціональність, але ускладнює аналіз, розуміння алгоритмів роботи та модифікацію при декомпіляції. 7 При обфускації широко застосовують такі прийоми: - Запис програми в один рядок (за наявності відповідних макросів); – Заміна імен змінних та функцій; - Вставка команд, що нічого не значать; - Розміщення даних у сторінках коду; - Перекриття коду; - Неявний виклик функцій; - Перетворення логічних операцій; - Змішування коду; - Застосування великої кількості команд jmp; - Розбиття рядків на частини; - Звільнення від адресних констант; - Шифрування та ін. Існують і інші, дуже ефективні, але трохи складні прийоми захисту, які потребують інших знань та часу на їхнє вивчення [1 - 7]. Розробка будь-якої програми починається із створення алгоритму. Якщо планується захист програми паролем, то заплутаний алгоритм виклику процедур не дозволить хакеру визначити логіку роботи. Тому важливо розробляти алгоритм з урахуванням вимог безпеки. Логічну структуру алгоритму можна ускладнити за допомогою: - Розміщення даних у сторінках коду; – великої кількості викликів підпрограм (процедур); - Застосуванням рідко використовуваних інструкцій процесора; - Застосуванням великої кількості констант; - Застосуванням функцій, які при їх виконанні змінюють код та інші прийоми. Реалізація кожного з цих пунктів призводить до заплутування коду, а, отже, і до підвищення загальної безпеки програми. 8 1. РОЗМІЩЕННЯ ДАНИХ У СЕКЦІЇ КОДУ 1.1. Розміщення даних у секції коду Хакер майже ніколи не доступний вихідний текст програми. Йому доступний лише ехе-файл. Застосування даних у секції коду при аналізі ехе-файла в налагоджувачі дозволяє дуже заплутати дослідження програми, оскільки дані в коді подаються як команди, які необхідно ігнорувати. Такий прийом заплутування коду також дозволяє збільшити час його аналіз. При класичному написанні програми на асемблері майже завжди в секцію даних записують дані, а в секцію коду - програмний код. Секція - це безперервний діапазон адрес, всі дані в яких вважаються з певної точки зору однорідними. Це з тим, що у секцію даних можна писати дані у процесі виконання програми, а секцію коду – не можна. Розглянемо для архітектури х64 програму, в якій дані розміщені у секції коду, але при цьому в процесі виконання коду її логіка роботи не порушується. Це відбувається через те, що програма командою jmp перестрибує дані, що розміщуються в ній. Програма 1.1. Розміщення даних у секції коду: include \masm64\include64\masm64rt.inc .code entry_point proc jmp @f titl: db "Просто MessageBoxTimeout",0 inf1: db "Обошлись без секции .data",10,"Ждем 9 секунд",10,10, "НТУ ХПИ, каф. КИТ",0 @@: lea rsi,inf1 invoke MessageBoxTimeout,0,rsi,addr titl,MB_ICONINFORMATION,0, 9000 invoke ExitProcess,0 entry_point endp end 9 У програмі представлено два варіанти застосування параметрів функції MessageBoxTimeout: один параметр передається через регістр rsi – адресу розміщення змінної, яка як приклад, вказана міткою inf1, інша – безпосередньо міткою titl. Чим більше даних розташовано в секції коду, тим складніше у налагоджувачі проаналізувати код. Це відбувається тому, що компілятор перетворює дані в коди операцій, послідовність яких немає логічного сенсу. Результат виконання програми наведенона рис. 1.1. Вікно відладчика x64Dbg з програмою, що розглядається, представлене на рис. 1.2. Код, який не має особливої логіки, у налагоджувачі підсвічений червоним кольором. Це і є дані, які розміщені у секції коду, але Рис. 1.1. Результат выполнения программы Рис. 1.2. Вікно налагоджувача x64Dbg з розглянутою програмою 10 інтерпретовані як коди операцій. Те, що це дані, теоретично можна обчислити розташованою перед цими даними командою jmp, за якою відбувається стрибок на блок коду, який має логічний сенс. Для закріплення матеріалу розміщення даних у секції коду наведемо ще один приклад (програма 1.2). Ті змінні, які обчислюються в програмі або в них буде здійснено запис у процесі виконання програми, не можна записувати в секцію коду. Програма 1.2. Обчислення рівняння ab+c/d - sqrt(d) для 5-ти значень змінної ai {2.,3.,4.,5.,6.}: include \masm64\include64\masm64rt.inc .data len2 equ ($-mas2)/type mas2 res dq 5 DUP(0),0 ; buf1 dq 0 ; буфер вывода сообщения .code entry_point proc jmp m1 mas1 real8 3.,8.,4. ; b, c, d m1: vmovsd xmm2,mas1[0] ; xmm2 - b vmovsd xmm3,mas1[8] ; xmm3 - c vmovsd xmm4,mas1[16] ; xmm3 - d jmp m2 mas2 dq 2.,3.,4.,5.,6. ; массив чисел А m2: mov rcx,len2 ; счетчик чисел mas2 jmp m3 tit1 db "masm64. AVX. Результат вычисления уравнения ab+c/d - sqrt(d)", 0 m3: lea rdx,res ; адрес массива результатов lea rbx,mas2 ; адрес mas2 jmp m4 ifmt db "masm64. Массив ai = 2.,3.,4.,5.,6.",10, 9,"Числа: b, c, d := 3., 8., 4.",10, "Результаты вычисления: %d , %d , %d , %d , %d ",10,10, "Автор: Рысованый А.Н., каф. КИП, НТУ ХПИ",10, 9,"Сайт: http://blogs.kpi.kharkov.ua/v2/asm/",0 m4: @@: vmovsd xmm1,qword ptr[rbx] ; xmm0 - a vmulsd xmm5,xmm2,xmm1 ; ab 11 vdivsd xmm6,xmm3,xmm4 ; c/d vaddsd xmm7,xmm5,xmm6 ; ab+c/d vsqrtsd xmm8,xmm4,xmm4 ; sqrt(d) vsubsd xmm9,xmm7,xmm8 ; ab+c/d - sqrt(d) vcvttsd2si eax,xmm9 ; movsxd r15,eax mov [rdx],eax ; сохранение результата add rbx,8 add rdx,8 loop @b ; ссылка на предыдущую метку @@ (наверх) ; подготовка данных для вывода mov r10,res mov r11,res[8] mov r12,res[16] mov r13,res[24] mov r14,res[32] ; преобразование и вывод invoke wsprintf,addr buf1,addr ifmt,r10,r11,r12,r13,r14; invoke MessageBox,0,addr buf1,addr tit1,MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end Результат виконання програми наведено на рис. 1.3. Рівняння розраховується для 5-ти значень змінної а. Тому в програмі виводяться 5 результатів обчислення цього рівняння. Рис. 1.3. Результат виконання программи 12 Дані, що переносяться з секції data, розміщуються всередині циклу, який обмежує командою jmp та міткою. Чим більше таких циклів, тим більше труднощів для дослідника блоків коду в ехе-файлі. Усі секції програми можна розглянути у налагоджувачі x64Dbg при виборі кнопки меню Карта памяти (рис. 1.4). У програмі є секції: секція коду – секції text та секції, на наявність яких програміст може впливати лише у певних випадках. Але у зв'язку з тим, що в програмі не всі дані знаходяться в сегменті коду, а деякі – в сегменті даних, то й у переліку секцій вони також присутні. 1.2. Лабораторна робота «Розміщення даних у секції коду» Мета заняття: – отримати практичні навички розміщення даних у секції коду програми. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; - текст програми з виведенням назви лабораторної роботи, варіантом, прізвища та ініціалів, e-mail, номери групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання 1 Використовувати чистий USB-flash носій, т.к. дані з нього будуть втрачені. Написати програму з виконанням дій: Рис. 1.4. Секції програми 13 - Вибір USB-flash носія; - записати блок даних зі значеннями 0FFh у кожну комірку пам'яті; - порахувати дані та порівняти результати; - підрахувати кількість справних та несправних осередків. Завдання 2 Написати програму з використанням функцій: 1. Функція FindFirstFileExA - виконує пошук у каталозі файлу або підкаталогу з ім'ям та атрибутами, що відповідають зазначеним. Завдання – організувати виведення всіх файлів у файл з однаковим розширенням. 2. Функція FindFirstFileNameW – створює перерахування всіх жорстких посилань на вказаний файл. 3. Функція FindNextStreamW – продовжує пошук потоку, запущений під час попереднього виклику. 4. Функція FindNextVolumeW – продовжує пошук тома, запущений викликом. 5. Функція GetCompressedFileSize. Витягує фактичну кількість байтів дискового сховища. 6. Функція GetDiskFreeSpace - отримує відомості про вказаний диск, включаючи обсяг вільного місця на диску. 7. Функція GetDiskFreeSpaceEx - отримує відомості про обсяг вільного місця на томі диска, тобто про загальний обсяг вільного місця, а також про загальний обсяг вільного місця, доступного користувачеві, пов'язаному з викликаючим потоком. 8. Функція GetDiskSpaceInformation - отримує відомості про дисковий простір для тома по заданому кореневому шляху. 9. Функція GetDriveType – визначає, чи є диск знімним, фіксованим, компакт-диском, диском ОЗУ чи мережним диском. 10. Функція GetFileAttributes – витягує атрибути файлової системи для зазначеного файлу чи каталогу. 11. Функція GetFileAttributesEx – витягує атрибути для зазначеного файлу чи каталогу. 12. Функція GetFileInformationByHandle - отримує відомості про файл для зазначеного файлу. 13. Функція GetFileSize – витягує розмір зазначеного файлу в байтах. 14. Функція GetFinalPathNameByHandle – витягує розмір зазначеного файлу в байтах. 15. Функція GetFinalPathNameByHandle – заповнює буфер рядками, що вказують допустимі диски в системі. 14 16. Функція GetLongPathName - перетворює зазначений шлях на довгу форму. 17. Функція GetTempFileName – створює ім'я для тимчасового файлу. Якщо створюється унікальне ім'я файлу, створюється порожній файл та дескриптор для нього звільняється; Інакше створюється лише ім'я файлу. 18. Функція GetVolumeInformation - витягує відомості про файлову систему та том, пов'язаний із зазначеним кореневим каталогом. 19. Функція GetVolumeInformationByHandle - витягує відомості про файлову систему та том, пов'язаний із зазначеним файлом. 20. Функція GetVolumeInformation - витягує відомості про файлову систему та том, пов'язаний із зазначеним кореневим каталогом. 21. Функція GetVolumeNameForVolumeMountPoint - витягує шлях GUID тома для тома, пов'язаного із зазначеною точкою підключення тому (літера диска, шлях GUID тома або підключена папка). 22. Функція GetVolumePathNamesForVolumeName - витягує список літер дисків та підключених шляхів до папок для зазначеного тома. 23. Функція GetVolumePathName - витягує точку підключення тому, до якого підключений зазначений шлях. 24. Функція SetFileAttributes - задає атрибути для файлу чи каталогу. 25. Функція SetFileInformationByHandle - задає інформацію про файл для зазначеного файла. Приклади використання функцій Програма 1.3. Вилучення повного шляху файлу: title Извлечение полного пути файла; masm64 include \masm64\include64\masm64rt.inc .data buf1 db MAX_PATH dup(?);макс. длина пути MAX_PATH - 260(?) символов tit1 db "GetFullPathName - извлечение полного пути файла",0 .code entry_point proc invoke GetFullPathName,chr$("1.asm"),sizeof buf1,addr buf1,0;rsp invoke MessageBox,0,addr buf1,addr tit1,MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end 15 Програма 1.4. Виведення першого файлу у папці, який відповідає формату *.bat: title вывод первого файла в папке, который соответствует формату *.bat include \masm64\include64\masm64rt.inc .data diry db "c:\masm64\bin64\*.bat",0 wfd WIN32_FIND_DATA <> ;t1 FILETIME <> buf1 dq 5 dup(0) fmt1 db "Информация о файле:",10,10,"FileAttributes = %d, ",10, "ст.ч. размер файла = %d, ",10, "мл.ч. размер файла = %d байтов, ",10,"короткое имя файла = %d",0 titl1 db "Имя 1-го файла в папке с расширением bat",0 titl2 db "WIN32_FIND_DATA <>",0 .code entry_point proc invoke FindFirstFile,addr diry,addr wfd; открывает дескриптор поиска ; и возвращает информацию о первом файле, имя которого ; соответствует указанному образцу invoke MessageBox,0,addr wfd.cFileName,addr titl1,MB_OK movsxd r10,wfd.dwFileAttributes ; атрибуты файла movsxd r11,wfd.nFileSizeHigh ; старшая половина размера файла movsxd r12,wfd.nFileSizeLow ; младшая половина размера файла movzx r13,wfd.cAlternate ; короткое имя файла invoke wsprintf, ADDR buf1, ADDR fmt1,r10,r11,r12,r13; invoke MessageBox,0,ADDR buf1,ADDR titl2,MB_OK invoke ExitProcess,0 ; возврат управл. ОС и освобождение ресурсов entry_point endp end Програма 1.5. Визначення часу створення файлу: include \masm64\include64\masm64rt.inc SYSTEMTIME struc wYear WORD ? ; Год wMonth WORD ? ; Месяц wDayOfWeek dw ?; День недели wDay WORD ? ; День месяца wHour WORD ? ; Час wMinute WORD ? ; Минута wSecond WORD ? ; Секунда 16 wMilliseconds dw ? ; Миллисекунда SYSTEMTIME ends .data fTime FILETIME <> sTime SYSTEMTIME <> buf db 32 dup(?),0 File1 db "25.asm",0;опр. время создания, а не изменения! hFile HANDLE ? fmt db "Дата и время создания файла: %d.%d.%d : %d.%d",0 .code entry_point proc invoke CreateFile,addr File1,GENERIC_WRITE,0,0,OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,0 mov hFile,rax invoke GetFileTime,hFile,addr fTime,0,0; invoke FileTimeToSystemTime,addr fTime,addr sTime invoke SystemTimeToTzSpecificLocalTime,0,addr fTime,addr sTime ;отформатирование времени для отображения и показа invoke GetTimeFormat, LOCALE_USER_DEFAULT, TIME_FORCE24HOURFORMAT or TIME_NOTIMEMARKER, addr sTime,0, addr buf, sizeof buf invoke MessageBox,0,ADDR buf,"Время создания файла",MB_OK ; Получить из числа строку movzx r15,sTime.wYear ; год movzx r14,sTime.wMonth ; месяц movzx r13,sTime.wDay ; день месяца movzx r12,sTime.wHour ; час movzx r11,sTime.wMinute ; минуты invoke wsprintf,addr buf,ADDR fmt,r15,r14,r13,r12,r11 invoke MessageBox,0,ADDR buf,"Время создания файла",MB_OK invoke ExitProcess,0 ; возврат упр. и освобождение ресурсов entry_point endp end Програма 1.6. Встановлення та читання покажчиків файлу: include \masm64\include64\masm64rt.inc .data _file1 db "File-2-1.txt",0 ; имя 1-го файла 17 _file2 db "File-2-2.txt",0 ; имя 2-го файла hFile01 HANDLE ? hFile02 HANDLE ? from_file db 4096 dup(?) read_by_file dq ? write_by_file dq ? file01_size dq ? file02_size dq ? title1 db "Добавление одного файла в конец другого",0 msg1 db "Ошибка! Файл 'File-2-1.txt' не доступен!",0 msg2 db "Размер файла 'File-2-2.txt': %d,",10,"Размер файла 'File-2-2.txt': %d.",10, "Считано байт: %d, записано байт: %d.",10,10, "Рысованый А.Н., НТУ ХПИ, каф. КИП",0 buf dq 0 .code entry_point proc invoke CreateFile,addr _file1,GENERIC_READ,0,0,OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,0 mov hFile01,rax ; сохранение дескриптора файла .if hFile01 == INVALID_HANDLE_VALUE ; если CreateFile возвр. ошибку invoke MessageBox,0,addr msg1,addr title1,0 ; сообщ. об ошибке .endif ; Открытие второго файла для записи invoke CreateFile,addr _file2,GENERIC_WRITE,FILE_SHARE_READ,0, OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0 mov hFile02,rax ; сохранение дескриптора файла invoke GetFileSize,hFile01,0 ; получение размера файла 01 mov file01_size,rax invoke GetFileSize,hFile02,0 ; получение размера файла 02 mov file02_size,rax ; Установка указателя на конец файла invoke SetFilePointer,hFile02,0,0,FILE_END ; Чтение из файла и запись в from_file invoke ReadFile,hFile01,addr from_file,file01_size,addr read_by_file,0 ; Запись в файл (конец) из from_file invoke WriteFile,hFile02,addr from_file,read_by_file,addr write_by_file,0 ; Преобразование данных и вывод (информация о программе; автор) 18 invoke wsprintf,addr buf,addr msg2,file01_size,file02_size, read_by_file, write_by_file invoke MessageBox,0,addr buf,addr title1,MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end Програма 1.7. Перезапис даних із масиву у файл: title перезапись данных из массива в файл; masm64 include \masm64\include64\masm64rt.inc .data mas1 dq 1,4,6,3ah,3bh mas2 dq 5 dup(?) len1 equ ($-mas2)/type mas2 BSIZE1 equ 40 ; количество байтов, которые записываются в файл fName BYTE "File2-64-2.txt",0 ; имя файла fHandle dq ? ; дескриптор хранения файлов cWritten dq ? ; ячейки для адреса символов вывода fmt db "%d",0 ; задание преобразования символа szFileName db "c:\masm32\bin64\File2-64-3-sms.exe",0 .code entry_point proc mov r11,len1 ; len1=5 lea rsi,mas1 ; занесение адреса начала элементов массива mas1 lea rdi,mas2 ; занесение адреса массива результата mas2 @1: mov r15,[rsi] push r11 ; сохранение в стеке перед выполнением wsprintf invoke wsprintf,ADDR [rdi],ADDR fmt,r15 pop r11 ; возврат из стека add rsi,type mas1 add rdi,type mas2 ;8 dec r11 jnz @1 invoke CreateFile,ADDR fName,GENERIC_WRITE,0,0, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE,0 mov fHandle,rax invoke WriteFile,fHandle,ADDR mas2,BSIZE1,ADDR cWritten,0; 19 ;invoke CloseHandle, fHandle ; закрыть дескриптор файла invoke WinExec,addr szFileName,SW_SHOW ; запуск внешнего файла invoke ExitProcess, 0 entry_point endp end Програма 1.8. Створення файлу, запис у файл даних та перегляд у блокноті: title создание файла, запись в файл и просмотр в блокноте; masm64 include \masm64\include64\masm64rt.inc .data fName BYTE "File-4.txt",0 ; имя файла fName2 BYTE "notepad C:\masm64\bin64\File-4.txt",0 ; имя файла inf1 db "Задача успешно решена",0 ; titl1 db "Сообщение о завершении",0 ; BSIZE equ 16 ; размер буфера для вывода чисел mas1 dq 10,12 ; массив чисел для записи в файл len1 equ ($-mas1)/type mas1 fmt db "%d",0 ; для преобразования числа перед выводом cWritten dq ? ; buf1 dq 2 dup(0) ; буфер для массива при выводе hFile dq ?,0 ; дескриптор файла .code entry_point proc lea r13,buf1 ; загрузка адреса массива вывода чисел lea r12,mas1 ; загрузка адреса массива исходных чисел mov r15,len1 ; загрузка счетчика чисел массива mas1 @1: ; начало цикла преобразования чисел массива mas1 mov r10,[r12] ; загрузка числа из массива mas1 invoke wsprintf,ADDR [r13],ADDR fmt,r10 ; преобразование add r12,8 ; увеличение адреса mas1 add r13,type buf1 ; увеличение адреса buf1 dec r15 ; уменьшение счетчика на 1 jnz @1 ; перейти на метку, если не ноль invoke CreateFile,ADDR fName, GENERIC_WRITE, 0,0, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE,0 mov hFile,rax ; invoke WriteFile,hFile,ADDR buf1,BSIZE,ADDR cWritten,0; чтение invoke CloseHandle,hFile ; закрыть дескриптор файла (обязательно) 20 invoke MessageBoxTimeout,0,addr inf1,addr titl1, MB_OK, 0,1500 invoke WinExec,addr fName2,SW_SHOW ; запуск внешнего файла invoke ExitProcess, 0 entry_point endp end Программа 1.9. Визначення розміру файлу: include \masm64\include64\masm64rt.inc .data BS equ 128 ; задание кол. байтов Fname BYTE "makeit.bat",0 ; файл для определения размера ms1 db "Size of file (in bits) " ; сообщение totalsize dq ? ; ячейка для сохранения размера ;stdout dq ? ; для хранения дескриптора вывода hFile dq ? ; дескриптор файла buf db BS dup (?) ; буфер fmt db "%d",0 ; форматирование числа ms2 db "Error!!File not found" ; sms .code entry_point proc invoke CreateFile,ADDR Fname,GENERIC_READ,0,NULL,\ OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL ;открытие файла mov hFile,rax ;сохранение дескриптора .if hFile==0FFFFFFFFh ;если ошибка jmp m1 ;перейти на метку m1 .endif invoke GetFileSizeEx,hFile,addr totalsize ; получение размера файла ;invoke GetStdHandle,STD_OUTPUT_HANDLE ;mov stdout,rax ;invoke WriteConsoleA,stdout,ADDR ms1,24,0,0 invoke wsprintf,addr buf,addr fmt,totalsize,0 invoke MessageBox,0,ADDR buf,ADDR ms1,MB_YESNO jmp m2 ;invoke WriteConsoleA,stdout,ADDR buf,15,0,0 invoke CloseHandle,hFile jmp m2 m1: invoke MessageBox,0,ADDR ms2,ADDR ms1,MB_ABORTRETRYIGNORE 21 m2: invoke ExitProcess,0 entry_point endp end Програма 1.10. Перейменування існуючого файлу: title переименование существующего файла; masm64 include \masm64\include64\masm64rt.inc .data File1 db "C:\masm64\bin64\0.asm",0 NewFile2 db "C:\masm64\bin64\0new.asm.asm",0 .code entry_point proc invoke MoveFile,addr File1, addr NewFile2 ;файл File1 переименовать invoke ExitProcess,0 entry_point endp end Програма 1.11. Визначення атрибутів файлу: include \masm64\include64\masm64rt.inc .data BS equ 512 ; Number of bytes Fname BYTE "c:\masm64\bin64\Px4m2.txt",0 ms1 db "Stream attributes:",0 mRead db ' READONLY', 0 mHide db ' HIDDEN', 0 mSys db ' SYSTEM', 0 mArch db ' ARCHIVE', 0 mNorm db ' NORMAL', 0 mDir db ' DIRECTORY', 0 errorBuf db BS dup (?) INVALID_HANDLE_VALUE equ -1 .code entry_point proc invoke CreateFile,ADDR Fname,GENERIC_READ,FILE_SHARE_READ,0, OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS, NULL mov rsi, rax cmp rsi, INVALID_HANDLE_VALUE 22 je file_not_found ; Определяем атрибуты файла invoke GetFileAttributes, ADDR Fname mov rcx, rax ; формирование атрибутов файла test ecx, FILE_ATTRIBUTE_READONLY jnz display_readonly_file test ecx, FILE_ATTRIBUTE_HIDDEN ; скрытый файл jnz display_hidden_file test ecx, FILE_ATTRIBUTE_SYSTEM ; только для ОС jnz display_system_file test ecx, FILE_ATTRIBUTE_ARCHIVE ; большинство файлов jnz display_archive_file test ecx, FILE_ATTRIBUTE_NORMAL ; файл без атрибутов jnz display_normal_file test ecx, FILE_ATTRIBUTE_DIRECTORY ; каталог вместо файла jnz display_directory_file display_readonly_file: invoke MessageBoxA,0,addr mRead,addr ms1,MB_OK jmp end_program display_hidden_file: invoke MessageBoxA,0,addr mHide,addr ms1,MB_OK jmp end_program display_system_file: invoke MessageBoxA,0,addr mSys,addr ms1,MB_OK jmp end_program display_archive_file: invoke MessageBoxA,0,addr mArch,addr ms1,MB_OK jmp end_program display_normal_file: invoke MessageBoxA,0,addr mNorm,addr ms1,MB_OK jmp end_program 23 display_directory_file: invoke MessageBoxA,0,addr mDir,addr ms1,MB_OK jmp end_program file_not_found: invoke GetLastError invoke FormatMessageA,FORMAT_MESSAGE_FROM_SYSTEM, 0,rax,0, ADDR errorBuf,BS,NULL invoke MessageBoxA,0,ADDR errorBuf,addr ms1,MB_OK end_program: ; Закрываем дескриптор файла перед завершением программы invoke CloseHandle,rsi entry_point endp end Контрольні питання для перевірки знань 1. Яка інформація зберігається у кожній із секцій? 2. Чи можна написати програму без секції data? 3. За допомогою якої команди дані можна розташувати у секції коду? 4. Скільки байтів займає команда jmp та у яких випадках? 5. Як переглянути наявність, назву та характеристики секцій програми? 6. Які ознаки знаходження даних у секції коду під час перегляду у відладчику? 24 2. ЗАСТОСУВАННЯ ПРОЦЕДУР З ПАРАМЕТРАМИ Застосування великої кількості процедур при аналізі коду в налагоджувачі досить сильно ускладнює його аналіз, особливо якщо параметри процедур передаються через стек. Цей прийом заплутування коду сприяє збільшенню часу з його аналіз. Угода про виклики для 64-розрядних систем передбачає передачу 4 перших параметрів через регістри RCX, RDX, R8, R9, а решти – через стек. 2.1. Передача параметрів процедури за замовчуванням Передача параметрів через регістри здійснюється, якщо цих параметрів трохи більше 4-х. Але трохи нижче буде розглянуто, що і перші 4 параметри те ж можна передавати через комірки стека. Застосування великої кількості дзвінків підпрограм (процедур) дозволяє ускладнити код. Причому перші чотири параметри передаються через регістри RCX, RDX, R8, R9, а інші – через стек, що також заплутує аналіз коду. Розглянемо програму 2.1, де виконується умова ab + c/d. Для простоти будемо використовувати лише дві процедури з передачею параметрів через перші регістри. Програма 2.1. Обчислення рівняння ab+c/d з використанням 2-х процедур: include \masm64\include64\masm64rt.inc .data a1 dq 1 b1 dq 2 c1 dq 19 d1 dq 4 titl1 db "Процедуры. masm64",0 sbuf dq ?,0 inf1 db "Уравнение ab + c/d",10,"a = 1",10,"b = 2",10, "c = 19",10,"d = 4",10,"Результат: %d",10,10, "Автор: Рысованый А.Н., каф. КИП, НТУ ХПИ",10, 9,"Сайт: http://blogs.kpi.kharkov.ua/v2/asm/",0 .code ; ab + c/d 25 count1 proc arga:QWORD, argb:QWORD ; ab mov rax,rcx ; arg a mul rdx ; arg b ; rdx:rax mov rsi,rax ret count1 endp count2 proc argc:QWORD,argd:QWORD ; c/d mov rax,rcx ; arg c mov r10,rdx xor rdx,rdx div r10 ; arg d mov rdi,rax ret count2 endp entry_point proc invoke count1,a1,b1 ; ab invoke count2,c1,d1 ; c/d add rsi,rdi ; сложение результатов из 2-х процедур ; ab + c/d invoke wsprintf,ADDR sbuf,ADDR inf1,rsi ; преобразование rsi invoke MessageBox,0,addr sbuf,addr titl1, MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end Результат виконання програми наведено на рис. 2.1. Рис. 2.1. Результат виконання програми 26 Код програми у налагоджувачі x64Dbg представлений на рис. 2.2. Для того, щоб у відладчику потрапити до процедури та побачити код процедури необхідно на рядку call натиснути в покроковому режимі кнопку F7 (рис. 2.3). Рис. 2.2. Код програми у відладчику x64Dbg Рис. 2.3. Код процедур у відладчику x64Dbg 27 Аналіз передачі параметрів процедури через регістри перестав бути складним прийомом роботи з кодом як для програміста при налагодженні, а й хакеру у його зловмисних діях. 2.2. Передача параметрів процедури через стек Майже завжди перші 4 параметри процедури послідовно передаються через регістри: rcx, rdx, r8, r9 (але є можливість і ці параметри передавати через стек). При використанні параметрів процедури, починаючи з п'ятого параметра, дані передаються тільки через стек, що ще більше заплутує код, оскільки при аналізі коду необхідно переглядати значення цих осередків. Але можна написати програму з невикористовуваними першими чотирма параметрами, а застосовувати лише ті, що передаються через стек (програма 2.2). Програма 2.2. Обчислення рівняння ab+c/d з використанням 2-х процедур та з передачею параметрів через стек: include \masm64\include64\masm64rt.inc .data f1 dq 10 a1 dq 1 b1 dq 2 c1 dq 19 d1 dq 4 titl1 db "Процедуры. masm64",0 sbuf dq ?,0 inf1 db "Уравнение ab + c/d",10,"a = 1",10,"b = 2",10, "c = 19",10,"d = 4",10,"Результат: %d",10,10, "Автор: Рысованый А.Н., каф. КИП, НТУ ХПИ",0 .code ; ab + c/d count1 proc arga: DQ,argb: DQ,argc: DQ,argd: DQ, arge: DQ,argf: DQ mov rax,[rbp+30h] ; arg a mov r10,[rbp+38h] mul r10 ; arg b ; на ячейку стека умножать нельзя mov rsi,rax ret count1 endp count2 proc arga:DQ,argb: DQ,argc: DQ,argd: DQ, arge: DQ,argf: DQ mov rax,[rbp+30h] ; arg c 28 mov r10,rdx xor rdx,rdx mov r10,[rbp+38h] div r10 ; arg d ; на ячейку стека делить нельзя mov rdi,rax ret count2 endp entry_point proc invoke count1,f1,f1,f1,f1,a1,b1 invoke count2,f1,f1,f1,f1,c1,d1 add rsi,rdi invoke wsprintf,ADDR sbuf,ADDR inf1,rsi invoke MessageBox,0,addr sbuf,addr titl1, MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end При виклику процедури, наприклад invoke count1,f1,f1,f1,f1,a1,b1 перші 4-ті може мати однакові імена, т.к. вони все одно в програмі не плануються використовувати. У зв'язку з тим, що кількість параметрів збільшилася, то і код у налагоджувачі став складнішим. (рис. 2.4). Рис. 2.4. Код у відладчику x64Dbg 29 При покроковому виконанні кожного рядка коду (кнопка F7) та натисканні на рядок коду з ім'ям call потрапляємо усередину цієї процедури. Але так як у тексті програми ці процедури знаходяться біля один одного, то й у налагоджувачі вони теж розташовуються разом (рис. 2.5). Рис. 2.5. Код процедур у відладчику x64Dbg 30 При розкритті коду процедури видно значення зсувів кожного параметра, що важливо як із написання програми, і під час аналізу коду. Першим передається параметр процедури через регістр rcx сегмент стека зі значенням [rbp+10] в 16-річній системі числення. Другим передається параметр процедури через регістр rdx сегмент стека зі значенням [rbp+18] в 16-річній системі числення. Третій передається параметр процедури через регістр r8 сегмент стека зі значенням [rbp+18h+8h] = [rbp+20h] в 16-річній системі числення. Четвертий передається параметр процедури через регістр r9 сегмент стека зі значенням [rbp+28] в 16-річній системі числення. П'ятий параметр процедури передається через стек із значенням [rbp+30]. Шостий параметр процедури передається через стек із значенням [rbp+38]. Через передачу параметрів у процедурах код процедур збільшився. Але необхідність перегляду вмісту кожної комірки пам'яті збільшує час аналізу коду. Для цього рекомендується і перші 4 параметри (rcx, rdx, r8, r9) передавати те ж через зміщення стека. 2.3. Лабораторна робота «Процедури з параметрами» Мета заняття: - Отримати практичні навички застосування параметрів процедури. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; - текст програми з виведенням назви лабораторної роботи, варіантом, прізвища та ініціалів, e-mail, номери групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання Згідно з номером студента у групі вибрати варіант завдання та написати на асемблері програму обчислення рівняння із застосуванням кількох процедур. В останній процедурі параметри передати лише через стек. Отримані результати записати у файл. 31 Варіанти 1. ac + b/d + f/e +1; 15. g/f + e/d — cb/a; 2. f/e — b/d – a/c +2; 16. gf/e + dc/b — a; 3. b/d + f/e — ca +3; 17. gf + e/d/c + ba; 4. db – hc +a/e+f + 4; 18. g + fe/d/cb — a; 5. a/c + bd — efg + 5; 19. gf/e+dc/b + a; 6. a/b/c — d/e + f + 6; 20. g/f + (ed)/c+b/a; 7. a/d + c/b + efg + 7; 21. 2d/a – 21bc+f; 8. ef + d/c — ab + 8; 22. 4b – a/22c + de; 9. a/b — cd/e+f/g + 9; 23. ab/4e – 16cde; 10. a + b/c/d + efg + 10; 24. d/ba – cd/e/f; 11. abc — de/fg; 25. e/8b + acdef; 12. a/b + cd + e/fg; 26. 4d/a – сd+ bef; 13. ab + cd/ef — g; 27. 8a/b + cd – (e/f)g; 14. abcd — ef/g; 28. 4abc – e/f — d, где a, b, c, d, e, f, g – числа. Приклад виконання Програма 2.3. Виконання рівняння ac + b/e + f/d + 5 з передачею параметрів в останній процедурі лише через стек: ; Уравнение ac + b/e + f/d + 5 include \masm64\include64\masm64rt.inc .data a1 dq 1 b1 dq 9 c1 dq 2 e1 dq 3 d1 dq 4 f1 dq 12 const dq 5 titl1 db "Процедуры. masm64",0 sbuf dq ?,0 inf1 db "Уравнение ac + b/e + f/d + 5",10,"a = 1, b = 9",10, "c = 2, d = 4",10,"e = 3, f = 12",10,"const = 5",10,"Результат: %d",10,10, "Автор: Рысованый А.Н., каф. КИП, НТУ ХПИ",10, 9,"Сайт: http://blogs.kpi.kharkov.ua/v2/asm/",0 .code ; ac + b/e + f/d + 5 count1 proc arg1:QWORD,arg2:QWORD ; ac mov rax,rcx ; arg a 32 mul rdx ; arg c ; rdx:rax mov rsi,rax ret count1 endp count2 proc arg1:QWORD,arg2:QWORD ; b/e mov rax,rcx ; arg b mov r10,rdx xor rdx,rdx div r10 ; arg e mov rdi,rax ret count2 endp count3 proc,arg5:DQ,arg6:DQ, arg7:DQ; f/d+5 mov rax,[rbp+30h] ; arg f mov r10,[rbp+38h] ; arg d ;xor rdx,rdx div r10 ; add rax,[rbp+40h] ; arg const mov rdi,rax ret count3 endp entry_point proc invoke count1,a1,c1 ; ab invoke count2,b1,e1 ; b/e add rsi,rdi ; сложение результатов из 2-х процедур ; ac + b/e invoke count3,a1,a1,a1,a1,f1,d1,const ; add rsi,rdi ; ac + b/e + f/d + 5 invoke wsprintf,ADDR sbuf,ADDR inf1,rsi ; преобразование rsi invoke MessageBox,0,addr sbuf,addr titl1, MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end Результат виконання програми наведено на рис. 2.6. 33 У програмі використовуються 3 процедури. Перші дві процедури мають лише по 2 параметри. Ці параметри передаються через регістри. У першій процедурі виконується множення значень 2-х змінних та збереження результату в регістрі rsi. У другій процедурі виконується операція поділу двох змінних. Перед виконанням операції розподілу рекомендується ініціалізувати регістр rdx. Для цього використано команду xor rdx,rdx Результат поділу зберігається у регістрі rdi. Особливістю 3-ї процедури є те, що необхідні параметри передаються лише через стек із необхідними зміщеннями. А перші чотири параметри, які передаються через регістри, у процедурі не використовуються. Внутрішні рядки коду процедур зручно аналізувати у відладчику (рис. 2.7). Рис. 2.6. Результат виконання рівняння 34 У відладчику зручно переглядати послідовність передачі параметрів та адрес, за якими передаються вміст регістрів та стека. Причому в налагоджувачі видно, що значення параметрів можна передавати не тільки через регістри, але і з використанням зсувів. Наприклад, перший параметр процедури передається через регістр rcx. Однак видно, що цей параметр можна передати через використання стека, як [rbp + 10h]. Рис. 2.7. Внутрішні рядки коду процедур 35 Отже, можна всі параметри передавати через стек, наприклад: Приклад частини попередньої програми, в якій параметри передаються тільки через стек, може бути таким: count1 proc arg1: QWORD, arg2: QWORD; ac mov rax,[rbp+10h];rcx; arg a mul rdx; arg c; rdx:rax mov rsi,rax ret count1 endp count2 proc arg1: QWORD, arg2: QWORD; b/e mov rax, [rbp + 10h]; rcx arg b mov r10, [rbp+18h]; rdx xor rdx,rdx div r10; arg e mov rdi,rax ret count2 endp Передача всіх параметрів тільки через комірки стека вносить труднощі в аналіз коду і автора програми, і хакера. Контрольні питання для перевірки знань 1. Перелічити послідовність передачі параметрів у процедурі. 2. Чи можна передавати параметри процедури лише через стек? 3. Яка адреса має перший параметр, який передається через стек? 4. Як розраховуються адреси комірок стека? 5. Якщо в першій процедурі першою операцією є поділ, то в чому полягає особливість цієї операції? 36 3. ПЕРЕТВОРЕННЯ ЛОГІЧНИХ ОПЕРАЦІЙ Перетворення логічних операцій у менш відомі формули, а також застосування логічних операцій (де це можливо) замість арифметичних дозволяє заплутувати (ускладнювати) логіку роботи програми, а отже, і витрачати додатковий час на її аналіз. 3.1. Заміна команди xor Команда xor – це операція додавання по mod2. Фактично це операція розподілу результату підсумовування на модуль числа (у разі - число два) і взяття залишку від розподілу як результату операції. Програма 3.1 отримує залишок від розподілу числа на число два і є еквівалентною операцією додавання mod2. Програма 3.1. Отримання результату за модулем два:include \masm64\include64\masm64rt.inc .data a1 dq 1 b1 dq 4 const dq 2 mySg1 segment READ WRITE EXECUTE alias("mySeg") ;.code entry_point proc mov rax,a1 ; занесение a1 add rax,b1 ; mov rdx,0 ; подготовка к делению div const ; mov r10,rdx ; a1 mod2 b1 invoke ExitProcess,0 entry_point endp mySg1 ends end У програмі наведено приклад отримання модуля 2 і перейменування секції коду в нове ім'я mySeg. 37 При отриманні модуля 2 береться не результат цілого розподілу з регістра rax, а залишок від поділу, який зберігається в регістрі rdx. У наступному рядку коду значення цього регістру rdx переноситься в регістр r10: mov r10,rdx Нова назва секції коду та характеристики секцій видно при виборі у налагоджувачі x64Dbg кнопки меню Карта памяти (рис. 3.1). Ця програма показує класичний приклад отримання результату за модулем два при застосуванні арифметичної операції поділу. У програмі не відбуваються логічні перетворення. Основою перетворення логічних функцій є заміна логічного висловлювання на формули перетворень із законів булевої алгебри. Їх застосування дещо збільшує код, але зовсім не порівнянно з часом пошуку пароля. Наприклад, функція додавання по mod2 - функція, яка визначається наступним чином: х1  х2 = 2121 хххх + = (х1 + х2)( 21 хх + ). Функція додавання по mod2 має такі характеристики: - Комутативність (закон переміщення): х1  х2 = х2  х1; – асоціативності (сполучний закон): х1  (х2  х3) = (х1 х2)  х3; – дистрибутивності (розділовий закон): х1 (х2  х3) = (х1х2)  (х1х3). Для цієї функції справедливі аксіоми: х  х = 0; х  1= х ; Рис. 3.1. Характеристики секцій програми 38 х  х = 1; х  0 = х. На підставі аксіом і властивостей можна вивести правила перетворень функцій І, АБО, НЕ через функцію додавання по mod2: х 1 = х1  1; x1 + x2 = x1  x2  x1x2; x1x2 = (x1  x2)  (x1 + x2) Розглянемо програму додавання по mod2, яка замінює команду xor (програма 3.2). Програма 3.2. Заміна команди xor на еквівалентну операцію 𝑎1𝑏1 + 𝑎1𝑏1: include \masm64\include64\masm64rt.inc .data a1 dq 2 b1 dq 3 .code entry_point proc mov rax,a1 ; a1 mov rbx,b1 ; b1 mov r14,rax ; a1 mov r15,rbx ; b1 not r14 ; ⌐a1 not r15 ; ⌐b1 and rax,r15 ; and r14,rbx ; or rax,r14 ; a1 mod1 b1 invoke ExitProcess,0 entry_point endp end Операція інверсії не завжди викликає певну настороженість. Її краще замінити на еквівалентну операцію отримання інверсії х  1 = х . І тут алгоритм отримання операції mod2 ще більше зміниться. Розглянемо приклад отримання операції mod2 за формулою (х1 + х2)( 21 хх + ) (программа 3.3). 39 Програма 3.3. Заміна команди xor на еквівалентну операцію (х1 + х2)( 21 хх + ): include \masm64\include64\masm64rt.inc .data a1 dq 1 b1 dq 1 .code entry_point proc mov rax,a1 ; a1 mov r10,rax ; mov rbx,b1 ; b1 mov r11,rbx ; or rax,rbx ; a1 v b1 mov r15,rax ; xor r10,1 ; ⌐a1 xor r11,1 ; ⌐b1 or r10,r11 ; ⌐a1 v ⌐b1 and r15,r10 ; a1 mod2 b1 invoke ExitProcess,0 entry_point endp end Наведена програма не однозначно свідчить про операції mod2. А при додаванні до цієї формули логічних виразів, які не впливають на правильність результату, а лише ускладнюють їх аналіз, це ще більше заплутує аналіз коду. 3.2. Заміна арифметичних операцій логічними Розглянемо програму з використанням макросу, що виконує множення на число. Причому якщо це число є ступенем двійки і не перевищує 16, то множення виконується за швидкою схемою з використанням зрушень. Програма 3.4. Застосування зсувів при множенні, якщо число є ступенем двійки: include \masm64\include64\masm64rt.inc; библиотеки mMulD macro digit ; макрос с именем mMulD 40 .if digit == 2 ;; если digit = 2, то shl eax,1 ;; умножение на 2 - сдвиг влево на 1 разряд .elseif digit == 4 ;; если digit = 4, то shl eax,2 ;; умножение на 4 - сдвиг влево на 2 разряда .elseif digit == 8 ;; если digit = 8, то shl eax,3 ;; умножение на 8 - сдвиг влево на 3 разряда .elseif digit == 16 ;; если digit = 16, то shl eax,4 ;; умножение на 16 - сдвиг влево на 4 разряда .else ;; иначе mov ebx, digit mul ebx ;; eax, edx = eax*ebx – обычное умножение .endif endm ;; окончание макроса .data digit dd 6 ; запись в 32-разрядную ячейку с именем digit числа 6 titl db " Ускоренное умножение ",0 ; название окна st2 dw ?,0 ; буфер вывода сообщения ifmt db "Исходное число = %d", 10, "%d x %d", 10, "Результат умножения = %d", 10,10, ; преобразование символа "Автор программы: Рысованый А.Н., НТУ ХПИ",0; .code entry_point proc mov eax,digit ; занесение в еах из ячейки памяти с именем digit mMulD [digit] ; вызов макроса invoke wsprintf,ADDR st2,ADDR ifmt,digit,digit,digit,eax ; invoke MessageBox,0,addr st2,addr titl,MB_ICONWARNING invoke ExitProcess,0 entry_point endp end У програмі для прискореного множення застосовується макрос. Хоча можна оформити програму без нього. Результат виконання програми наведено на рис. 3.2. 41 Код програми у налагоджувачі x64Dbg представлений на рис. 3.3, на якому видно коди команд та переходи між ними залежно від умов. Рис. 3.2. Результат выполнения программы Рис. 3.3. Код програми у відладчику x64Dbg 42 У програмі 3.5 розглядається операція поділу альтернативним способом - командами зсуву. Але такі операції можливі, якщо числа є ступенем двійки. Програма 3.5. Рішення рівняння a/c + b/e для конкретних даних: include \masm64\include64\masm64rt.inc .data a1 dq 16 b1 dq 8 c1 dq 4 d1 dq 2 titl1 db "Решение уравнения. Процедуры. masm64",0 sbuf dq ?,0 inf1 db "Уравнение a/c + b/e",10,"a = 16, b = 8",10, "c = 4, d = 2",10,10,"Результат: %d",10,10, "Автор: Рысованый А.Н., НТУ ХПИ",10, 9,"Сайт: http://blogs.kpi.kharkov.ua/v2/asm/",0 .code ; a/c + b/e count1 proc arg1:DQ,arg2:DQ,arg3:DQ,arg4:DQ ; mov r10,[rbp+10h] ; arg b shl r10,3 ; mov r11,[rbp+20h] ; arg c shl r11,1 ; add r10,r11 ret count1 endp entry_point proc ; a1 = 16, b1 = 8, c1 = 4, d1 = 2 invoke count1,a1,b1,c1,d1 ; a/c + b/e invoke wsprintf,ADDR sbuf,ADDR inf1,r11 ; преобразование r11 invoke MessageBox,0,addr sbuf,addr titl1, MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end У програмі 3.6 розглядається операція поділу як командами зсуву (якщо число є ступенем двійки та звичайним поділом в іншому випадку). 43 Програма 3.6. Застосування зрушень при розподілі, якщо число є ступенем двійки: include \masm64\include64\masm64rt.inc; библиотеки .data a1 dq 16 b1 dq 8 c1 dq 4 d1 dq 2 titl1 db "Решение уравнения. Процедуры. masm64",0 sbuf dq ?,0 inf1 db "Уравнение a/b + c/d",10,"a = 16, b = 8",10, "c = 4, d = 2",10,10,"Результат: %d",10,10, "Автор: Рысованый А.Н., НТУ ХПИ",10, 9,"Сайт: http://blogs.kpi.kharkov.ua/v2/asm/",0 .code count1 proc arg1:DQ,arg2:DQ ; a/b mov rax,rcx mov r10,rdx .if r10 == 2 ; если digit = 2, то shr rax,1 ; деление на 2 - сдвиг влево на 1 разряд .elseif r10 == 4 ; если = 4, то shr rax,2 ; деление на 4 - сдвиг влево на 2 разряда .elseif r10 == 8 ; если = 8, то shr rax,3 ; деление на 8 - сдвиг влево на 3 разряда .elseif r10 == 16 ; если 16, то shr rax,4 ; деление на 16 - сдвиг влево на 4 разряда .else ; иначе div r10 ; rax,rdx – обычное деление mov r14,rax .endif mov r14,rax ret count1 endp count2 proc arg1:DQ,arg2:DQ ; c/d mov rax,rcx mov r10,rdx .if r10 == 2 ; если digit = 2, то shr rax,1 ; деление на 2 - сдвиг влево на 1 разряд .elseif r10 == 4 ; если = 4, то shr rax,2 ; деление на 4 - сдвиг влево на 2 разряда 44 .elseif r10 == 8 ; если = 8, то shr rax,3 ; деление на 8 - сдвиг влево на 3 разряда .elseif r10 == 16 ; если = 16, то shr rax,4 ; деление на 16 - сдвиг влево на 4 разряда .else ; иначе div r10 ; rax,rdx – обычное деление mov r11,rax .endif mov r11,rax ret count2 endp entry_point proc invoke count1,a1,b1 ; a/b invoke count2,c1,d1 ; c/d add r14,r11 invoke wsprintf,ADDR sbuf,ADDR inf1,r14 ; преобразование invoke MessageBox,0,addr sbuf,addr titl1,MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end Результат виконання програми представлений на рис. 3.4. Рис. 3.4. Результат виконання програми 45 3.3. Лабораторна робота «Перетворення логічних операцій» Мета заняття: – отримати практичні навички застосування логічних операцій та операції додавання за модулем. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – текст програми з виведенням назви лабораторної роботи, варіантом, прізвища та ініціалів, e-mail, номери групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання Згідно з номером студента у групі вибрати варіант завдання та написати на асемблері програму обчислення рівняння. Числа підібрати таким чином, щоб можна було застосувати операції зсуву. Одну з операцій замінити на операцію додавання по модулю 2, причому застосувати альтернативне додавання по модулю. Отримані результати записати у файл. Варіанти 1. a/c – e/f – ad; 16. a/b-cd/e+fg; 2. a/b + c/d – eg; 17. fe - b/d - a/c; 3. a/b + cd - f/(ge); 18. g/f + e/dc + ba; 4. d/a – сd+ bef; 19. g/e+d/b+a; 5. g/f+(ed)/c+b/a; 20. g + f/d + cb – a; 6. e/b + a/c – def; 21. g/f/e + dc/b – a; 7. ab + cd/e + f – g; 22. g/f + e/d - c/a; 8. a/b + cd + e/f + g; 23. a/d + f/e – ca; 9. d/b/a – cd+/e/f; 24. ab – gc +a/e + f; 10. abc - d + e / f; 25. a/c + b/d – efg; 11. a+b/e – cde; 26. a/b/c-de + f; 12. b - a/c + de; 27. a/d + c/b +e+ fg; 13. d/a - bc + f; 28. af + d/c – ab; 14. a + b/cd + efg; 29. a/b + c/d + ef; 15. ac + b/d + f/e; 30 abc + d+ef, де a, b, c, d, e, f, g – числа, показники ступеня 2. 46 Контрольні питання для перевірки знань 1. Якому результату дорівнює одне зрушення вліво чи вправо? 2. Яку дію виконує операція за модулем 2? 3. Яку дію виконує операція за модулем 3? 4. Яка з операцій – додавання, множення, розподіл та зсув виконується довше? 5. Яка з операцій – додавання, множення, розподіл та зсув виконується за менший час? 47 4. ПЕРЕКРИТТЯ КОДУ Програміст, який використовує Асемблер і вміє використовувати прийом "перекриття коду", вже близький до професіонала. Залишилося до вже вивченого освоїти прийоми приховування виклику функцій, самодифікації коду, виконання коду в стеку (шкода, що під х64 можливості вже немає), шифрування коду або його частини, впровадження свого коду в чужий процес і ще трохи - і ви професіонал або програміст-фокусник! 4.1. Загальні відомості про перекриття коду Суть методу перекриття коду у тому, що частина однієї машинної команди може бути іншою командою. Наприклад, команда mov може завантажити в регістр число, яке є кодом операції іншої команди. Потім, іншою командою необхідно в команді mov необхідно початок виконання коду поставити на потрібний байт даних, які вважати кодом операції. При цьому необхідно врахувати, що числа в пам'яті розташовуються в такому порядку: спочатку молодші частини з меншими адресами, а потім старші частини зі старшими (великими) адресами (у тексті програми навпаки). В асемблерній програмі можна задавати не абсолютні адреси, а зміщення щодо поточної адреси: jmp $ + 25; перехід на 25h байт "вперед"; jmp $-15; перехід на 15h байт "назад". Ідентифікатор $ – це значення поточного лічильника адреси рядка коду, в якому міститься цей символ. А вираз «$-» позначає арифметичну різницю між адресою рядка коду, в якій знаходиться цей символ і того значення, яке вказано за ним (або іншої операції, яке застосовано до ідентифікатора $. У пам'яті як дані, так і рядки коду розташовуються, починаючи з менших адрес. Наприклад mas1 DD 20 dup(1,2,4,6); резервування осередків пам'яті для mas1 dd 16 dup(1); 80 +16 = 96 len1 equ ($-mas1); число байтів у mas1 позначає, що змінної len1 буде присвоєно число, яке вираховується як різниця між поточною адресою, за якою 48 розташовується змінна $ (велике значення адреси) та значення адреси змінної з ім'ям mas1 (менше значення адреси). А т.к. кожен байт адресується своєю адресою, то практично змінна len1 дорівнює числу байтів, які відводяться для зберігання масиву mas1. Якщо після рядків mas1 DD 20 dup(1,2,4,6); резервування осередків пам'яті для mas1 dd 16 dup(1) і перед рядком len1 equ ($-mas1) розташувати один або кілька рядків іншого коду, то ідентифікатор вважатиме результуюче число байтів весь блок коду. Розглянемо блок коду: mov ax,05EBh jmp $-2 У пам'яті команда mov ax, 05EBh запишеться машинним кодом: 66: B8 EB05 (видно у відладчику). Причому значення 05EBh помінялися місцями. На згадку про програми числа записуються за правилом «молодший байт – за молодшою адресою». Також необхідно пам'ятати, що кожен байт у машинному коді має адресу і до будь-якого байта на їхню адресу можна звернутися. Отже, команда jmp$-2 позначає, що з адреси рядка, в якому вказано ідентифікатор $, буде відраховано 2 байти. Це означає, що буде пропущено 2 байти попереднього рядка коду 66:B8 EB05 і виконається команда з кодом операції EB та операндом 05. Перекриття коду застосовують до вже готової програми при відкритті її ехе-файлу у відладчику. Алгоритм перекриття коду полягає в наступному: 1. Відкривається ехе-файл програми у налагоджувачі (у разі відладчик x64Dbg); 2. Аналізується код у відладчику щодо пошуку місць застосування перекриття коду. Закривається відладчик; 3. Відкривається текст програми у редакторі середовища masm64. Вставляється команда jmp$ з негативним або позитивним збільшенням відповідно до кількості байтів сусідніх команд. У цьому випадку стає доречним застосування «сміттєвого» коду; 4. Компілюється програма та перевіряється правильність застосування перекриття коду; 49 5. Повернення до пункту 2, доки не виконаються вимоги до програми. 4.2. Перекриття коду з переходом назад за кодом програми При розгляді програми, в якому застосована технологія перекриття коду, необхідно з початку відкрити налагоджувач, т.к. тільки в ньому наведено всі коди операцій та дані в машинному поданні. У програмі 4.1 застосовано технологію перекриття коду з переходом назад за кодом програми. Програма 4.1. Перекриття коду з переходом назад: include \masm64\include64\masm64rt.inc .code entry_point proc ; mov ax,05EBh jmp $ - 2 ; пропуск 2 байтов EB 05 и остановка на EB ; EB 05 – это jmp на 5 байтов вперед с учетом своих 2-х байтов dec r9 ; пропускаем, 3 байта: 49:FFC9 inc r10 ; выполняем с этой команды mov r8,1 mov eax,06EB0000h jmp $ - 2 ; пропуск 2 байтов 06 и EB и остановка на EB ; EB 06 – это jmp на 6 байтов вперед с учетом своих 2-х байтов dec cl ; пропускаем, 2 байта: FE C9 dec al ; пропускаем, 2 байта: FE C8 dec dl ; выполняем с этой команды nop mov rax,09EB000000000000h jmp $ - 2 ; пропуск 2 байтов EB 09 и остановка на EB ; EB 09 – это jmp на 9 байтов вперед с учетом своих 2-х байтов mov r15,1 ; пропускаем, 7 байтов: 49:C7C7 01000000 nop ; выполняем с этой команды invoke ExitProcess,0 entry_point endp end У програмі наведено 3 команди jmp з різною кількістю байтів, що пропускаються. 50 Скомпілюємо програму та відкриємо її ехе-файл у відладчику x64Dbg. На початку програми наведено блок коду: mov ax,05EBh jmp $-2 У цьому випадку застосований 16-розрядний регістр ах, т.к. у налагоджувачі код операції має префікс 66. Команда mov знаходиться в пам'яті як 66:B8 EB05 mov ax,05EB Перший байт із вмістом 66 – це префікс заміни розміру операнда (16-бітовий регістр ах); B8 – код команди mov; EB 05 – константа 05EBh, записана за правилом «молодший байт – на молодшу адресу» (рис. 4.1). Графічними ознаками переходів у відладчику є червоні стрілки в лівому його полі. Запам'ятовуємо, що код операції команди jmp – ЄВ. Команда jmp$-2 змушує мікропроцесор пропустити 2 байти з попереднього рядка (знак мінус) коду 66:B8 EB05, зупинитися на коді операції EB (команда jmp) і виконати байти EB 05, що вже означає команду jmp$+5, тобто . перехід на 5h байт вперед за програмним кодом. І це з'ясовується лише у відладчику. Якщо виконати команду jmp$-1, то буде здійснено перехід на байт із вмістом 05, а він інтерпретується командою add eax,FF49FDEB, що в нашому випадку не має (поки що) жодного сенсу. Рис. 4.1. Результат виконання програми 51 Рядок jmp 00.7FF7F028100A передає керування на адресу 00.7FF7F028100A. На початку адреси вказано ім'я файлу – 00.ехе. У відладчику в полі адрес можна порахувати байт, на який буде передано управління. Після виконання аналізованої команди jmp 00.7FF7F028100A вміст відладчика матиме вигляд (рис. 4.2). нтерес представляє лише верхній рядок jmp 00.7FF7F0281011 З цього рядка червона лінія вказує перехід на рядок із командою inc r10. Підходимо до аналізу наступного блоку коду: inc r10; виконуємо з цієї команди mov r8,1 mov eax,06EB0000h jmp $-2; Для покрокового виконання програми у налагоджувачі x64Dbg послідовно натискаємо F7 або F8. І після натискання F7 на рядку 00007FF7F0281020 EB FC jmp 00.7FF7F028101E цей рядок виконається та перетвориться (рис. 4.3). Рис. 4.2. Вміст відладчика після виконання команди jmp 00.7FF7F028100A 52 Розглянемо аналогічну ситуацію. Команда EB – це код операції jmp, а 06 – число байт, якими команда здійснить перехід. Знову слід звернути увагу тому факту, що на цю адресу в налагоджувачі вказують стрілочки переходів зліва, а також у самій команді jmp записана ця адреса. У програмі не можна записувати безпосередньо адресу переходу, але при налагодженні заздалегідь можна командою jmp $ - підібрати цю адресу. У цьому блоці коду застосовано 32-розрядний регістр. Тому і запис у нього відбувається 32-розрядним числом і спочатку записані молодші числа, а потім старші. Але префікс розмірності перед кодом операції немає. Розглянемо наступні рядки, у яких здійснюється перекриття коду mov rax,09EB000000000000h jmp $-2 У цьому блоці коду застосовано 64-розрядний регістр. Тому й запис до нього відбувається 64-розрядним числом і спочатку записані молодші числа, а потім – старші (48:B8 000000000000EB09). Префікс розмірності дорівнює 48. Кожен байт у машинному коді має адресу і до будь-якого байта на їхню адресу можна звернутися. Після виконання команди jmp$-2 відбудеться перехід усередину команди mov (48:B8 000000000000EB09) на байти ЕВ06 (рис. 4.4). Рис. 4.3. Результат виконання команди jmp 00.7FF7F028101E 53 Після розгляду перекриття коду з переходом назад за кодом програми можна зробити висновок: слід застосовувати цей спосіб досить обережно і остерігатися зациклювання коду. Цей спосіб добре підходить для неявного, але перестрибування непотрібного коду або даних в секції коду. 4.3. Перекриття коду з переходом уперед за кодом програми При розгляді технології перекриття коду з переходом уперед за кодом програми виконується за допомогою команди jmp$+. Розглянемо програму 4.2, у якій застосовано технологію перекриття коду, але з переходом уперед за кодом програми. Програма 4.2. Перекриття коду з переходом уперед за кодом програми: include \masm64\include64\masm64rt.inc .code entry_point proc ; mov ax,05EBh jmp $ + 5 ; перепрыгивает свои 2 байта и 3 байта dec r9 dec r9 ; 49:FFC0 inc r10 ; выполняем с этой команды mov r8,1 mov eax,06EB0000h jmp $ + 6 ; перепрыгивает свои 2 байта и следующие 4 dec cl ; FEC9 dec al ; FEC8 dec dl ; FECA; выполняем с этой команды nop mov rax,09EB000000000000h jmp $ + 9 ; перепрыгивает свои 2 байта и следующие 7 mov r15,1 ; 49:C7C7 0100 0000 Рис. 4.4. Переход назад во внутрь команды mov rax,09EB000000000000h 54 nop ; выполняем с этой команды invoke ExitProcess,0 entry_point endp end У наведеній програмі застосовується рядковий ідентифікатор $ з різними позитивними збільшеннями. Так зроблено у тому, щоб розглянути особливості переходів через регістри з різною розрядною сіткою. У першому блоці коду, що нас цікавить mov ax,05EBh jmp $ + 5; перестрибує свої 2 байти та 3 байти dec r9 dec r9; 49:FFC0 inc r10; виконуємо з цієї команди команда mov ax, 05EBh ні на що не впливає. Значення має команда jmp$+5, яка займає в пам'яті 2 байти: EB03. За результатами виконання цієї команди відбувається перехід через свої 2 байти та 3 байти, розташованих за нею рядки коду. Усього вийшло зміщення на 5 байтів. Для простоти розгляду застосовано команду dec r9 з кодом операції 49:FFC0, яка має розмірність 3 байти. Але якщо є логічний зміст і задум програміста, то можна вказати інше зміщення адреси і увійти всередину цієї команди і виконати зовсім інший код операції. Наступні блоки коду зі зміщеннями +6 та +9 мають аналогічний зміст, і ці блоки добре прокоментовані. Розглянемо застосування позитивних зсувів у рядковому ідентифікаторі $ (програма 4.3). Програма 4.3. include \masm64\include64\masm64rt.inc .data a1 db 1 b1 db 2 c1 db 4 d1 db 3 .code entry_point proc ; ; блок1 : ab + c/d 55 mov al,a1 mul b1 mov cl,al mov al,c1 div d1 add al,cl nop ; блок2 : ab + c/d mov al,a1 ; 6 байтов: 8A05 D61F0000 jmp $+4 ; 2 байта: EB 02 nop ; nop ; mul b1 ; 6 байтов: F625 CD1F0000 jmp $+5 ; 2 байта: EB 03 dec dx ; 3 байта: 66:FFCA mov cl,al ; jmp $+9 ; 2 байта: EB 07 mov r15,1 ; 7 байтов: 49:C7C7 01000000 mov al,c1 ; jmp $+7 ; 2 байта: EB 05 mov esi,1234 ; 5 байтов: BE D2040000 div d1 ; add al,cl ; invoke ExitProcess,0 entry_point endp end Програма складається з двох частин, які виконують обчислення одного і того ж цілісного виразу: ab + c/d. У блоці коду 1 відсутні будь-які заплутування коду. А в блоці 2 обчислюється теж вираз, але із застосуванням рядкового ідентифікатора для невеликого утруднення аналізу. Для простоти аналізу в блоці 2 відсутні входження всередину машинного коду команд, що є суттєвим недоліком цієї програми. Але навіть таке застосування ускладнює аналіз цього коду. Перед тим, як застосувати перекриття коду, розглянемо звичайну програму пересилання цілісного масиву з одного місця пам'яті до іншого за допомогою команд SSE (програма 4.4). Програма 4.4. include \masm64\include64\masm64rt.inc 56 .data mas1 DD 20 dup(1,2,4,6) ; резервирование ячеек памяти для mas1 dd 16 dup(1) ; 80+16=96 len1 equ ($-mas1)/type mas1 mas2 DD len1 dup(0) ; резервирование ячеек памяти для mas2 ifmt db "… masm64",10, "…",0 titl1 db "Пересылка целых чисел, SSE",0 .code entry_point proc ; mov rcx,len1/4 ; количество блоков массива mas1 lea rsi,mas1 ; адрес начала массива mas1 lea rdi,mas2 ; адрес начала массива mas2 @@: movups xmm0,dword ptr [rsi] ; пересылка 4-x 32-разр. целых чисел movups dword ptr [rdi],xmm0 add rsi,16; add rdi,16; loop @b invoke MessageBox,0,addr ifmt,addr titl1,MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end Для утруднення аналізу цієї програми внесемо до неї незначні зміни шляхом внесення даних до секції коду. Дані можна переносити лише ті, які під час виконання програми не вимагають запису (змін) до секції коду (надалі буде розглянуто таку можливість за допомогою змін атрибутів секції) (програма 4.5). Програма 4.5. Паралельна передача 32-розр. цілих чисел: include \masm64\include64\masm64rt.inc .data mas1 DD 20 dup(1,2,4,6) ; резервирование ячеек памяти для mas1 dd 16 dup(1) ; 80+16=96 len1 equ ($-mas1)/type mas1 mas2 DD len1 dup(0) ; резервирование ячеек памяти для mas2 .code entry_point proc ; mov rcx,len1/4 ; количество блоков массива mas1 lea rsi,mas1 ; адрес начала массива mas1 lea rdi,mas2 ; адрес начала массива mas2 jmp m2 ifmt db "Проверка перекрытия кода на masm64",10, 57 "Рысованый А.Н., каф. КИП, НТУ ХПИ",0 m2: @@: movups xmm0,dword ptr [rsi] ; пересылка 4-x 32-разр. целых чисел movups dword ptr [rdi],xmm0 add rsi,16; jmp m1 titl1 db "Пересылка целых чисел, SSE",0 m1: add rdi,16; loop @b invoke MessageBox,0,addr ifmt,addr titl1,MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp end Відладчик намагається байти даних подати як коди операцій з наступними операндами. Але йому це не виходить. І ці рядки відзначаються червоним кольором (не розпізнані байти). Наступним кроком зміни першої програми є застосування перекриття коду. У навчальних цілях простіше це робити на командах jmp тому, що в інших командах застосування перекриття призводить до зациклювання цієї частини програми (програма 4.6). Програма 4.6. Паралельна передача 32-розр. цілих чисел із перекриттям коду: include \masm64\include64\masm64rt.inc .data mas1 DD 20 dup(1,2,4,6) ; резервирование ячеек памяти для mas1 dd 16 dup(1) ; 80+16=96 len1 equ ($-mas1)/type mas1 mas2 DD len1 dup(0) ; резервирование ячеек памяти для mas2 mySg1 segment READ WRITE EXECUTE alias("mySeg") ;.code entry_point proc ; mov rcx,len1/4 ; количество блоков массива mas1 lea rsi,mas1 ; адрес начала массива mas1 lea rdi,mas2 ; адрес начала массива mas2 jmp m2 ifmt db "Проверка перекрытия кода на masm64",10,10, "Рысованый А.Н., каф. КИП, НТУ ХПИ",10, 9,"Сайт: http://blogs.kpi.kharkov.ua/v2/asm/",0 58 m2: @@: movups xmm0,dword ptr [rsi] ; пересылка 4-x 32-разр. целых чисел movups dword ptr [rdi],xmm0 add rsi,16; mov ax,1debh jmp $-2 ; jmp m1 titl1 db "Пересылка целых чисел, SSE",0 ; m1: add rdi,16; loop @b invoke MessageBox,0,addr ifmt,addr titl1,MB_ICONINFORMATION invoke ExitProcess,0 entry_point endp mySg1 ends end У програмі для утруднення аналізу дані, які в процесі виконання не змінюються, розміщені в секції коду і ця секція перейменована керуючим словом segment в нове ім'я mySeg. Дані обрамлені командою jmp та відповідною міткою. Результат виконання програми наведено на рис. 4.5. Команда jmp замінена на рядки коду mov ax,1debh jmp $-2 У регістр ax заноситься код операції jmp – значення ebh та усунення – 1dh. Вміст вікна налагоджувача перед виконанням команди jmp 00.7FF7F8DD30A0 наведено на рис. 4.6. Рис. 4.5. Результат виконання програми 59 Вміст вікна відладчика вже після виконання команди jmp 00.7FF7F8DD30A0 наведено на рис. 4.7. В останньому вікні можна однозначно визначити лише параметри функції MessageBox. Після виконання рядка 00007FF7F8DD30A0 EB 1D jmp 00.7FF7F8DD30BF Рис. 4.6. Вміст вікна відладчика перед виконанням команди jmp Рис. 4.7. Вміст вікна відладчика після виконання команди jmp 60 Відкривається нове вікно відладчика на завершення трасування коду (рис. 4.8). Безпосередній результат пересилання цілих даних можна подивитися в дампі пам'яті відладчика x64Dbg. 4.4. Лабораторна робота "Перекриття коду" Мета заняття: придбати практичні навички написання та застосування програм перевірки пароля з розміщенням даних у сторінках коду та з перекриттям коду. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – текст програми з виведенням назви лабораторної роботи, варіантом, прізвища та ініціалів, e-mail, номери групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання 1. Написати програму в середовищі masm64 відповідно до варіанта завдання з використанням максимально можливої кількості прийомів перекриття коду. 2. Використовувати процедуру перевірки пароля. 3. Використовувати перевірку на підтримку команд AVX, яку оформити як виклик окремого ехе-файла. Якщо команди AVX мікропроцесором не підтримуються, то використовувати команди SSE. За допомогою команд AVX виконати рівняння, де змінна приймає п'ять значень і задана масивом. Числа, що залишилися, також задані в Рис. 4.8. Вміст вікна налагоджувача на завершення трасування коду 61 масиві {b, c, d, e}. Числа задані у масивах. Застосувати максимально можливу кількість прийомів із перекриттям коду. Отримані результати записати у файл. Варіанти 1. (√a) b/c – de; 11. √(ab) + cd + √e; 2. (√/b) c + ad√c; 12. √a – cd – √(eb); 3. (√(bc) a – e√d; 13. √(ce) + a + b/d; 4. (√a/b) c + d/e; 14. a/b/c + √d – e); 5. ab + √(bc) + de; 15. a√(b/c) + cd + e; 6. √(ab) + c/d/e; 16. a√(ce) – b/d; 7. √(ab) + cd√e; 17. bd/c + √(ae); 8. √(edc) – a/b; 18. a√b + c/d + e; 9. √(ac) + b/c + d/e; 19. ab√c + de; 10. √(de) + cd + a; 20. a√e + bc/d, где a, b, c, d ,e – числа, числа а є{a1, a2, a3, a4, a5} заданы массивом. Програма 4.7. Перевірка пароля include \masm64\include64\masm64rt.inc .data Password db "12345" ; проверка первых пяти символов len1 equ $-Password Buf dq 5 ; Err1 dq 0 Msg1 db "Пароль совпал",0 Msg2 db "Пароль не корректен",0 Title1 db "Проверка пароля",0 stdout1 dq 0 ; stdin1 dq 0 ; cRead dq 0 ; cWritten dq 0 ; Msg db "Please enter your password, 5 characters",10,10,0; .code Pas1 proc lea rsi,Password ;адрес первого элемента строки lea rdi,Buf ;адрес второго элемента строки mov rcx,len1 repe cmpsb ;побайтно проверяется len раз jz m2 ; inc Err1 ; счетчик несовпадений m2: ret 62 Pas1 endp entry_point proc invoke GetStdHandle,STD_OUTPUT_HANDLE mov stdout1,rax invoke GetStdHandle,STD_INPUT_HANDLE mov stdin1,rax invoke WriteConsole,stdout1,ADDR Msg,sizeof Msg,ADDR cWritten,0 invoke ReadConsole,stdin1,ADDR Buf,5,ADDR cRead,0 invoke Pas1 .if (Err1==0); invoke MessageBox,0,addr Msg1,"Проверка пароля", MB_ICONINFORMATION .else invoke MessageBox,0,addr Msg2,"Проверка пароля",MB_OK .endif invoke ExitProcess,0 entry_point endp end Контрольні питання для перевірки знань 1. У чому полягає суть методу перекриття коду із переходом назад за кодом програми? 2. У чому полягає суть методу перекриття коду з переходом уперед за кодом програми? 3. Чому дорівнює код операції команди jmp? 4. Чи можна зробити перехід на будь-який байт команди? 5. У якому разі можливе зациклювання коду програми? 63 5. ВИКЛИКИ ФУНКЦІЙ У директиві invoke параметри між собою не пов'язані. Тому якщо між ними вставити код або сміття, то програма стає досить заплутаною. 5.1. Виклик функцій без використання директиви invoke При написанні програм часто користуються директивою invoke. При використанні директиви invoke текст програми стає компактним. Але директива invoke складається з команди call і параметрів. Наприклад, у програмі, яка виводить повідомлення Hello World!, параметри функції MessageBox передаються окремими командами і не використовується директива invoke (програма 5.1). Програма 5.1. Виведення повідомлення 'Hello World!' із передачею параметрів функції MessageBox окремими командами: include \masm64\include64\masm64rt.inc .data msg db 'Hello World!', 0 ttl db "Название окна",0 .code entry_point proc ; invoke MessageBox,0,addr msg,addr ttl,MB_ICONINFORMATION xor rcx,rcx lea rdx,msg lea r8,ttl mov r9d,64 call MessageBox invoke ExitProcess,0 entry_point endp end Такий запис змінюється транслятором masm64 на послідовність команд: xor ecx,ecx lea rdx,qword ptr ds:[7FF7CF043006] lea r8,qword ptr ds:[7FF7CF04304D] mov r9d,40 call qword ptr ds:[<&MessageBoxA>] 64 З цього запису випливає, що рядки коду один від одного не залежать. Тому цілком логічно не скрізь користуватися директивою invoke, а тільки в тих блоках коду, які планується внести заплутування коду. І обов'язково між ними вставляти будь-які інші команди, які на даний момент не заважають логіці програми (програма 5.2). Програма 5.2. Виведення повідомлення 'Hello World!' зі вставками між параметрами функції інших команд: include \masm64\include64\masm64rt.inc .data msg db 'Hello World!', 0 ttl db "Название окна",0 .code entry_point proc ; invoke MessageBox,0,addr msg,addr ttl,MB_ICONINFORMATION xor rcx,rcx mov r15,1 ; “мусор” lea rdx,inf1 nop ; “мусор” lea r8,titl add r15,-1 ; “мусор” mov r9d,40h xor r14,14 ; “мусор” call MessageBox invoke ExitProcess,0 entry_point endp end Кількість рядків коду, які вставляються між параметрами, може бути більшою. Доцільно вставляти великі шматки коду, кожен з яких має пов'язаний зміст, але ні на що не впливають і не псують основну програму (сміттєвий код). 5.2. Організація переходу з використанням команди повернення Виклик процедури виконується командою call, а повернення із неї – командою RET. Команда call передає керування ближньою чи далекою процедурою із запам'ятовуванням у стеку адреси точки повернення. Команда ret повертає управління з процедури викликаючої програмі, адреса повернення отримує з стека. 65 Команда CALL аналогічна команді JMP і підтримує самі способи адресації: CALL зміщення; CALL регістр; CALL qword ptr [змінна]; CALL qword ptr [64-бітний регістр]. Відмінність CALL від JMP у цьому, що CALL зберігає у стеку адресу повернення, т. е. адресу наступної інструкції. Цю адресу використовує команда RET, яку прийнято називати командою повернення з підпрограми. Насправді це назва умовна, т.к. команду RET можна використовувати і для виклику підпрограм, і для переходів. Дія команди RET формально можна записати так: rip ← ss:[rsp] add rsp, 8 Тобто. верхні 8 байта стека завантажуються в регістр-покажчик команд, після чого покажчик стека коригується. Але перед виконанням команди ret компілятор вставляє команду leave. Для повернення з процедури це правильно, а при переході на адресу – ні. Тому потрібна корекція. Назад для leave є команда enter 0,0. Команда ENTER створює кадр стека. Перший операнд задає кількість байтів пам'яті, що виділяється в стеку при вході в процедуру. Другий операнд задає рівень вкладеності процедури у вихідному коді. Він визначає кількість покажчиків кадру стека, що копіюються в новий кадр стека з попереднього. Команда LEAVE має дію, протилежну команді ENTER. Фактично, команда LEAVE тільки копіює вміст RBP в RSP, тим самим викидаючи зі стека весь кадр, створений командою ENTER, і зчитує зі стека значення регістра RBP для попередньої процедури. Значить, команду call rax можна замінити командами push rax enter 0,0; 0h - кількість байтів у пам'яті, 0 - рівень вкладення ret 66 У rах попередньо має бути завантажена необхідна адреса переходу. У програмі 5.3 повинна виконуватися операція ab + a/c, але через розгляд переходу перша частина рівняння не виконується, а виконається тільки операція a/c. Програма 5.3. Використання команди ret під час виконання обчислень: title Использование команды ret include \masm64\include64\masm64rt.inc .data a1 dq 6 ; b1 dq 2 ; c1 dq 3 titl db "Вывод результата",0; buf1 dq 1 dup(0),0 ; буфер вывода сообщения fmt1 db "Результат: "," %d",0 .code ; entry_point proc lea rax,[m1] ; адрес метки push rax enter 0,0; 0h - число байтов в памяти, 0 - уровень вложения ret ;m1: mov rax,a1 ; imul rax,b1 ; a1 x b1 mov r11,rax ; m1: xor rdx,rdx mov rax,a1 div c1 ; a1/c1 add rax,r11 invoke wsprintf, ADDR buf1, ADDR fmt1,rax; invoke MessageBox,0,addr buf1,addr titl, MB_ICONINFORMATION; invoke ExitProcess, 0 entry_point endp end На рис. 5.1 наведено вікно відладчика. 67 У цьому вікні видно, що компілятор при звичайному використанні команди ret вставляє команду leave. Саме тому в програму і треба вставляти для команди leave команду enter. 5.3. Організація переходу з використанням команди call та перекриттям коду Якщо при вставці в програму команд з використанням перекриття застосовується код команди ret з метою переходу на іншу гілку програми, компілятор не ідентифікує цю команду і не вставляє додаткову команду leave. У такому разі і в програму не вставляється команда корекції – enter. Розглянемо консольну програму перевірки пароля із заміною команди call командами push та ret із застосуванням технології перекриття коду (програма 5.4). Програма 5.4. Перевірка пароля з перекриттям коду: include \masm64\include64\masm64rt.inc .data Password db "12345" ; проверка только первых пяти символов len1 equ ($-Password)/type Password Buf dq 5 ; Err1 dq 0 stdout1 dq 0 ; stdin1 dq 0 ; cRead dq 0 ; cWritten dq 0 ; Msg db "Please enter your password, 5 characters",10,10,0 ; Msg1 db "Поздравляем! Пароль совпал",0 Msg2 db "Сожалеем! Пароль не корректен",0 Рис. 5.1. Використання команд ret і enter 68 Title1 db "Проверка пароля",0 .code entry_point proc mov rcx,STD_OUTPUT_HANDLE call GetStdHandle mov stdout1,rax mov rcx,STD_INPUT_HANDLE call GetStdHandle mov stdin1,rax lea rax,[m1] mov ebx,0C350D8FAh ; 50 - push eax, C3 - ret jmp $ - 2 ; на предыдущий код m1: ; адрес возврата – на продолжение invoke WriteConsole,stdout1,ADDR Msg,sizeof Msg,ADDR cWritten,0 invoke ReadConsole,stdin1,ADDR Buf,5,ADDR cRead,0 lea rsi,Password ; адрес первого элемента строки lea rdi,Buf ; адрес второго элемента строки mov rcx,len1 repe cmpsb ; побайтно проверяется len раз jz m2 ; inc Err1 ; счетчик несовпадений m2: .if (Err1==0); invoke MessageBox,0,addr Msg1,addr Title1,MB_ICONINFORMATION .else invoke MessageBox,0,addr Msg2,addr Title1,MB_ICONERROR .endif invoke ExitProcess,0 entry_point endp end На рис. 5.2 представлено консольне вікно з виведенням запрошення та введенням пароля. Повідомлення при успішному збігу пароля показано на рис. 5.3. 69 3 У програмі представлений блок коду, який показує технологію перекриття коду та використання команд push та ret для здійснення переходу на необхідну ділянку коду. Застосування команди call явно свідчить про виклик процедури. А застосування зв'язки команд push та ret дозволяє ускладнити аналіз коду та зробити виклик процедури (або переходу) не явним. У цій програмі не показано процес застосування виклику процедури (переходу) із застосуванням зміщення на велику кількість байтів. Блок програми, в якому відбувається перекриття коду та перехід на іншу адресу представляє наступну послідовність команд: lea rax,[m1] mov ebx,0C350D8FAh; 50 - push eax, C3 - ret jmp $-2; на попередній код m1:; адреса повернення – на продовження У команді mov ebx,0C350D8FAh значення D8FAh - будь-які заповнення формату регістру. Для порівняння змін послідовності виконання рядків коду у налагоджувачі наведено приклад до виконання команди jmp $ - 2 (рис. 5.4). Рис. 5.2. Консольне вікно введення пароля Рис. 5.3. Повідомлення при успішному збігу пароля 70 Блок коду після виконання команди jmp$-2 представлений на рис. 5.5. При виконанні команди ret за адресою регістра rax відбувається перехід на новий рядок коду. Тобто для здійснення переходу можна використовувати зв'язок команд push і ret, яку можна спробувати сховати ще й застосуванням технології перекриття коду. Програма має великий недолік: значення пароля прописано в самій програмі секції даних. Наприклад, при запиті програми введення пароля вводимо значення 99999. А значення пароля у цьому прикладі 12345. Рис. 5.4. Блок коду до виконання команди jmp $ - 2 Рис. 5.5. Блок коду після виконання команди jmp $ - 2 71 Відкриваємо відладчик x64Dbg і в покроковому режимі при багаторазовому натисканні на клавішу F8 аналізуємо повідомлення програми. Шукаємо введені значення 99999. І через деякий час з'являються рядки коду, які наведені на рис. 5.6. У верхньому рядку в полі коментарів у першій частині видно правильний пароль і в другій частині – ті значення, які були введені (99999). Тому найпростіший варіант це якось виправити – сховати пароль у рядках коду. Для цього, як було розглянуто раніше, потрібно використати команду jmp. 5.4. Лабораторна робота «Організація переходів» Мета заняття: набути практичних навичок написання та застосування програм з використанням команди повернення RET. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – у тексті програми організувати виведення назви лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Рис. 5.6. Вікно з паролями 72 Завдання За допомогою команд AVX виконати рівняння, де змінна приймає п'ять значень і задана масивом. Числа, що залишилися, також задані в масиві {b, c, d, e}. Застосувати організацію 2-х типів переходу: - зв'язок команд push та ret; - Перекриття коду. Отримані результати записати у файл. Варіанти 1. a√(bc) – df/e; 11. ab/cd + f√e; 21. ab/c + df√e; 2. a/b + df√c; 12. a/cd – √e + fb; 22. a/bc + f√(de); 3. bc/d – ef√a; 13. ce + √a + bdf; 23. abc√d – e√f; 4. a/b/c + f√(de); 14. a/b/c + f√(de); 24. ab√cd) – f/e; 5. a + bc – df√e; 15. √(ab) + cf/d – e; 25. de√c + bcf/a, 6. ab + c/d – f√e; 16. af√(ce) – bd; 7. (a/b) + cdf√e; 17. bd/c + √a – fe; 8. ed/c – af√b; 18. c√e + a/b + df; 9. √a + √b + cdf/e; 19. de√b + cf/a; 10. c√(de) + df/a; 20. a/e + bf√cd, де a, b, c, d ,e, f – числа, числа а є{a1,a2,a3,a4} задані масивом. Контрольні питання для перевірки знань 1. Навіщо під час використання команди ret необхідна корекція у програмі? 2. Які дії формально виконує команда CALL? 3. Чому при виконанні команд CALL та RET не потрібна корекція у програмі? 4. Що таке кадр стека і коли він формується? 5. Чому під час перекриття коду з використанням зв'язки команд push і ret не потрібна корекція в програмі? 73 6. САМОМОДИФІКАЦІЯ КОДУ ПРОГРАМИ При налагодженні програми в налагоджувачі хакер не завжди і не відразу перевіряє кожен рядок коду на логіку її виконання. А застосування самодифікації коду дозволяє вже при виконанні змінювати логіку програми. Такий прийом ускладнює процес аналізу програми. 6.1. Загальні відомості про самодифікацію коду Для передачі керування з однієї ділянки коду на інший зазвичай використовуються такі команди: • безперечний перехід JMP; • умовні переходи Jcc; • виклик підпрограми CALL; • "повернення з підпрограми" RET; • команда циклу LOOP. При зламі програми завжди шукають команди test, cmp, jz, jnz, які у відладчику одразу впадають у вічі. Достатньо змінити jz на jnz або взагалі "вбити" виклик процедури порівняння паролів - і програма почне приймати будь-які дані як правильний пароль. При цьому не потрібно навіть пізнавати правильний пароль. Завдання – приховати логіку захисту. Необхідно записати в коді довільне сміття, а потім уже в процесі виконання програми змінювати його на потрібні команди. Тут виникає проблема – запис даних у сторінки коду заборонено. Але й така проблема так само вирішувана. Перш ніж змінювати jz або jnz, спочатку розглянемо коди операцій. Мета - аналіз кодів операцій. Для цього розглянемо програму 6.1. Програма 6.1. Застосування команд розгалуження: .code entry_point proc jmp L1 jmp L2 jz L1 jnz L1 jb L1 74 jnb L1 jg L1 jng L1 L1: mov al,bl L2: invoke ExitProcess,0 На рис. 6.1 наведено вікно відладника x64Dbg з отриманим ехе- файлом. Усі команди ближнього переходу – двобайтові. Перший байт – код команди, другий – усунення щодо поточної команди до потрібної адреси. Команди ближнього переходу дозволяють перейти на -128/127 байт щодо поточної інструкції. Відомо, що команди далекого переходу п'ятибайтові. Слід звернути увагу, що код операції jmp дорівнює ЕВh. 6.2. Самомодифікація команди переходу При перевірці пароля завжди відбувається порівняння чисел (символів) та подальший перехід, як правило, за командою jnz. Тому в тексті програми необхідно замість команди jnz застосувати іншу команду, наприклад, команду jc. А вже при виконанні ехе-файлу потрібно команду jnz поміняти на команду jc. Такий прийом однозначно збиває хакера під час аналізу коду. Рис. 6.1. Вікно налагоджувача x64Dbg з отриманим ехе-файлом 75 Зміна коду операції здійснюється за допомогою функції WriteProcessMemory. Параметри функції WriteProcessMemory: - дескриптор процесу (до якого здійснюватиметься запис): - адреса, за якою здійснюватиметься запис; - адреса буфера, з якого здійснюватиметься запис; - кількість байтів, що необхідно записати; - Покажчик на змінну, в яку буде записано кількість скопійованих байт. Якщо він дорівнює нулю, запис ігнорується. Програма 6.2. Консольне введення пароля: include \masm64\include64\masm64rt.inc .data Password db "12345" ; пароль len1 equ $-Password Buf dq 5 ; Err1 dq 0 Msg1 db "Пароль совпал",0 Msg2 db "Пароль не корректен",0 Title1 db "Проверка пароля",0 stdout1 dq 0 ; stdin1 dq 0 ; cRead dq 0 ; cWritten dq 0 ; Msg db "Введите пароль, 5 символов",10,0 ; Msg10 db "*****",10,0 ; opc db 075h ; КОП JNZ .code entry_point proc invoke GetStdHandle,STD_OUTPUT_HANDLE mov stdout1,rax invoke GetStdHandle,STD_INPUT_HANDLE mov stdin1,rax invoke SetConsoleCP,1251;уст. кодовой страницы win 1251 в поток ввода invoke SetConsoleOutputCP,1251;уст. кодовой страницы win1251 вывода invoke WriteConsole,stdout1,ADDR Msg,sizeof Msg,ADDR cWritten,0 invoke WriteConsole,stdout1,ADDR Msg10,sizeof Msg10,ADDR cWritten,0 invoke ReadConsole,stdin1,ADDR Buf,5,ADDR cRead,0 invoke GetCurrentProcessId 76 invoke OpenProcess,PROCESS_VM_OPERATION or PROCESS_VM_WRITE, 1, rax lea rdi,_toWrite ; записать адрес метки, т.е. команды jc m1 invoke WriteProcessMemory,rax,rdi,addr opc,1,0 ; в rdi opc=75h lea rsi,Password ; загрузка адреса нахождения Password lea rdi,Buf ; загрузка адреса буфера введенных символов mov rcx,len1 ; счетчик символов m1: mov al,[rsi] ; загрузка числа по адресу из rsi mov bl,[rdi] ; cmp al,bl ; сравнение символов jz m2 ; если совпали символы inc Err1 ; счетчик несовпадений m2: add rsi,1 ; inc адреса add rdi,1 ; dec rcx _toWrite: jc m1 .if (Err1==0); invoke MessageBox,0,addr Msg1,addr Title1,MB_ICONINFORMATION .else invoke MessageBox,0,addr Msg2,addr Title1,MB_OK .endif invoke ExitProcess,0 entry_point endp end Під час виконання функції WriteProcessMemory команда jс змінюється на команду jnz. У секції даних програми записано лише один байт – код команди jnz, але в разі потреби таким чином можна копіювати цілі функції. 6.3. Алгоритм самодифікації коду Алгоритм написання програми, що самодифікується, може бути таким: 77 1. Пишеться програма без самодифікації, компілюється та перевіряється правильність її роботи (наприклад, за повідомленнями програми). 2. Вибираються рядки програми, яких можна застосувати самомодифікацію. На першому етапі для простоти це можуть бути команди, а чи не функції. Причому самодифікацію простіше проводити по одному рядку коду (по одній команді). 3. У відладчику відкривається ехе-файл і на аркуш паперу (або в блокноті) записуються побайтно коди операції та операнди, але у зворотній послідовності – молодші значення байтів записуються правіше, а старші – лівіше. 4. Резервується місце для команди, яка вставлятиметься. Для цього на місці того рядка, який модифікується, ставиться мітка і пишеться будь-яка команда або команди загальною розмірністю, що дорівнює команді, яка буде вставлятися. Після внесених змін необхідно перевіряти працездатність програми. Потім застосовувати самодифікацію до наступного рядка та постійно перевіряти працездатність програми. Програма 6.3. Самомодифікуюча програма перевірки пароля із заміною кількох команд: include \masm64\include64\masm64rt.inc .data Password db "12345" ; пароль len1 equ ($-Password) Buf dq 5 ; Err1 dq 0 Msg1 db "Пароль совпал",0 Msg2 db "Пароль не корректен",0 Title1 db "Проверка пароля",0 stdout1 dq 0 ; stdin1 dq 0 ; cRead dq 0 ; cWritten dq 0 ; Msg db "Введите пароль, 5 символов",10,0 ; Msg10 db "*****",10,0 ; opc db 075h ; код операции (КОП) JNZ opc1 dq 0068Ah ; mov al,[rsi] 78 h1 dq ? ; идентификатор процесса opc2 dq 01F8Ah ; mov bl,[rdi] opc3 dq 0C33Ah ; cmp al,bl opc4 dq 01C68348h ; add rsi,1 .code entry_point proc invoke GetStdHandle,STD_OUTPUT_HANDLE mov stdout1,rax invoke GetStdHandle,STD_INPUT_HANDLE mov stdin1,rax invoke SetConsoleCP,1251;уст. кодовой стран. win 1251 в поток ввода invoke SetConsoleOutputCP,1251;уст. код. стр. win1251 в поток вывода invoke WriteConsole,stdout1,ADDR Msg,sizeof Msg,ADDR cWritten,0 invoke WriteConsole,stdout1,ADDR Msg10,sizeof Msg10,ADDR cWritten,0 invoke ReadConsole,stdin1,ADDR Buf,5,ADDR cRead,0 invoke GetCurrentProcessId invoke OpenProcess,PROCESS_VM_OPERATION or PROCESS_VM_WRITE,1,rax mov h1,rax ; идентификатор процесса lea rdi,_toWrite invoke WriteProcessMemory,rax,rdi,addr opc,1,0 lea r15,m1 invoke WriteProcessMemory,h1,r15,addr opc1,2,0 lea r15,mm10 invoke WriteProcessMemory,h1,r15,addr opc2,2,0 lea r15,mm11 invoke WriteProcessMemory,h1,r15,addr opc3,2,0 lea r15,m2 invoke WriteProcessMemory,h1,r15,addr opc4,4,0 lea rsi,Password ; загрузка адреса нахождения Password lea rdi,Buf ; загрузка адреса буфера введенных символов mov rcx,len1 ; счетчик символов, 10 байтов не вмещается в dq ; в эти места будут записываться новые команды из секции данных m1: add al,10 ; любая команда на 2 байта (mov al,[rsi]) mm10: add al,11 ; любая команда на 2 байта (mov bl,[rdi]) mm11: add al,12 ; любая команда на 2 байта (cmp al,bl) 79 ;mov al,[rsi] ; загрузка числа по адресу из rsi ;mov bl,[rdi] ; ;cmp al,bl ; сравнение символов jz m2 ; если совпали символы inc Err1 ; счетчик несовпадений m2: sub rsp,60 ; 4 байта (add rsi,1) ;add rsi,1 ; inc адреса, 4 байта add rdi,1 ; dec rcx _toWrite: jc m1 .if (Err1==0); invoke MessageBox,0,addr Msg1,addr Title1,MB_ICONINFORMATION .else invoke MessageBox,0,addr Msg2,addr Title1,MB_OK .endif invoke ExitProcess,0 entry_point endp end На рис. 6.2 наведено вікно налагоджувача x64Dbg до виконання самодифікації коду. Звернути увагу необхідно на рядки коду внизу вікна: add al,A add al,B add al,C Результат виконання самодифікації із заповненими рядками до рядка, позначеного сірим кольором, показаний на рис. 6.3. 80 Рис. 6.3. Результат виконання самодифікації Рис. 6.2. Вікно налагоджувача x64Dbg до виконання самодифікації коду 81 Недоліки цього підходу – відкритість (виклик функції WriteProcessMemory привертає увагу) і трудомісткість (якщо йдеться про складні функції). 6.4. Самодифікуюча діалогова програма з кількома діалоговими вікнами Процес створення діалогового вікна складається з етапів: 1. Створення файлу ресурсів; 2. Створення діалогової програми; 3. Переналаштування (створення) командного (пакетного) файлу. Для створення ресурсів (наприклад, вставка іконки, JPG-файлу та інше) будемо використовувати програму ResEd. Послідовність створення файлу ресурсів може бути такою: 1. У програмі ResEd створюємо новий файл ресурсів (File - New Project і вводимо ім'я). У правій верхній панелі з'являється піктограма папки та ім'я файлу. 2. Натискаємо правою кнопкою миші на піктограму папки та вибираємо «Add Dialog». 3. Виставляємо характеристики вікна: - Виставляємо мишкою необхідний розмір вікна; - у полі властивостей вікна Caption змінюємо назву діалогового вікна. 4. Для розміщення картинки (у нашому випадку іконки) вибираємо кнопку та перетягуємо її мишкою на діалогове вікно. 5. Для розміщення кнопки вибираємо в полі інструментів кнопку із зображенням ОК та перетягуємо її мишкою на вибране місце. У полі Caption вводимо назву кнопки. 6. Для написання тексту на вікні вибираємо в полі інструментів кнопку із зображенням літери «А» та перетягуємо її мишкою на вибране місце. У полі Caption вводимо текст. 7. Для створення рамки (якщо необхідно) вибираємо поле із зображенням рамки. У полі Caption вводимо назву рамки. 8. Додаємо файл ресурсів. Додавання файлу констант. Для цього натискаємо правою кнопкою миші по значку папки, вибираємо "Include file", потім "Add". Вибираємо у вікні програми три точки і вводимо шлях, наприклад C: masm64include64 RESOURCE.H. 9. Зберігаємо програму. 82 10. Відкриваємо створений rc-файл і додаємо рядок із ресурсом курсору: 10 ICON DISCARDABLE " sent.ico" У цьому рядку число 10 означає цифровий ідентифікатор іконки (ресурс). Він може мати будь-яке значення, але і в rc-і asm-файлах він повинен бути одним і тим же. 11. Для створення чергового вікна натискаємо правою кнопкою миші на піктограму папки та вибираємо «Add Dialog». Інші дії над створенням елементів вікна повторюються. 12. Створити у текстовому редакторі маніфест додатка із вмістом, як наведено у програмі 6.4. У зв'язку з тим, що програма створення ресурсів ResEd вже давно не оновлюється, вона неправильно вносить рядки директив, які відносяться до вмісту файлу маніфесту в ОС Win64. Тому рядки коду файлу ресурсів, які відносяться до маніфесту, повинні бути такими: #define MANIFEST 1 // 1 24 "MsgD.manifest.xml" Програма 6.4. Маніфест програми: Диал. прог. 83 13. До файлу ресурсів дописати структури визначення версії: VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 FILEOS 0x00000000 FILETYPE 0x00000000 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "041904B0" BEGIN VALUE "CompanyName", "каф. КІП\000\0" VALUE "FileDescription", "Рысованый А.Н. \000\0" VALUE "FileVersion", "1.0\000\0" VALUE "InternalName", " MsgD \000\0" VALUE "OriginalFilename", " MsgD.exe\000\0" VALUE "LegalCopyright", "\251 2024, Рысованый А.Н. \000\0" VALUE "ProductName", " MsgD \000\0" VALUE "ProductVersion", "1.0\000\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0419, 0x04B0 END END В результаті будуть створені вікна, зображення яких наведено на рис. 6.4. Рис. 6.4. Діалогові вікна програми 84 Програма 6.5. Діалогова програма проекту: include \masm64\include64\masm64rt.inc .data? hInstance dq ? hIcon dq ? hIcon3 dq ? hImg1 dq ? hImg3 dq ?; tEdit dq ? buffer db 8 dup(?); BYTE 64 dup(?);;; .data szTit1e104 db «Общ. сведения",0 szInf104 db "masm64... ",0 pbuf dq buffer,0 ;;; szPas db "12345",0 ; пароль szStr1 db "Вы ввели корректный пароль. Поздравляем!",0 szStr2 db "Вы ввели неправильный пароль.",0 titl db "программа проверки пароля",0 szRun db "RP-Xor-64-sam.exe",0 .code entry_point proc mov hInstance, rvcall(GetModuleHandle,0) mov hIcon, rv(LoadImage,hInstance,10,IMAGE_ICON,256,256,LR_DEFAULTCOLOR) invoke DialogBoxParam,hInstance,100,0,ADDR main,hIcon .exit entry_point endp main proc hWin:QWORD,uMsg:QWORD,wParam:QWORD, lParam:QWORD .switch uMsg .case WM_INITDIALOG rcall SendMessage,hWin,WM_SETICON,1,lParam ; установить иконку в строке заголовка mov hImg1, rvcall(GetDlgItem,hWin,102) rcall SendMessage,hImg1,STM_SETIMAGE,IMAGE_ICON,lParam ; иконка в клиентской области .case WM_COMMAND .switch wParam .case 103 ; кнопка <Ввод текста> mov hIcon,rv(LoadImage,hInstance,11,IMAGE_ICON,256,256, LR_DEFAULTCOLOR) 85 invoke DialogBoxParam,hInstance, 200,0, ADDR GetUserText,hIcon test rax,rax ; нажата ли кнопка отмены jz notext rcall MessageBox,hWin,pbuf,"Пользовательский текст", MB_ICONQUESTION lea r14,pbuf; для того, чтобы убрать лишний служебный символ mov r15,[r14] invoke lstrcmpi,addr szPas,r15 ;addr pbuf ; сравнение строк символов .if rax==0 invoke MessageBox,0,addr szStr1,addr titl,MB_ICONINFORMATION .else invoke MessageBox,0,addr szStr2,addr titl,MB_ICONERROR jmp exit_dialog .endif mov hIcon3,rv(LoadImage,hInstance,12,IMAGE_ICON,256,256,LR_DEFAULTCO LOR) invoke DialogBoxParam,hInstance,300,0,ADDR Dial3,hIcon3 notext: .case 105 ; кнопка jmp exit_dialog .case 104 ; кнопка <Общ. сведения> invoke MsgboxI,0,ADDR szInf104,ADDR szTit1e104,MB_OK,10 .endsw .case WM_CLOSE exit_dialog: rcall EndDialog,hWin,0 .endsw xor rax, rax ret main endp GetUserText proc hWin:QWORD,uMsg:QWORD,wParam:QWORD, lParam:QWORD .switch uMsg .case WM_INITDIALOG rcall SendMessage,hWin,WM_SETICON,1,lParam ; иконка в строке заголовка mov hImg1, rvcall(GetDlgItem,hWin,205); извлекает дескриптор 86 rcall SendMessage,hImg1,STM_SETIMAGE,IMAGE_ICON,lParam ; иконка в клиентской области mov tEdit,rvcall(GetDlgItem,hWin,201) invoke SetFocus,tEdit ; установка курсора в поле ввода .case WM_COMMAND .switch wParam .case 202 ; кнопка ОК rcall GetWindowText,tEdit,pbuf,sizeof buffer; записать текст по адресу буфера .if rax == 0 rcall MessageBox,hWin,"Введите текст или нажмите Cancel","Текст отсутствует", MB_ICONWARNING rcall SetFocus,tEdit ; установка фокуса на окно Edit .else rcall EndDialog,hWin,1 .endif .case 203 ; jmp exit_dialog .endsw .case WM_CLOSE exit_dialog: rcall EndDialog,hWin,0 .endsw xor rax, rax ret GetUserText endp Dial3 proc hWin:QWORD,uMsg:QWORD,wParam:QWORD, lParam:QWORD .switch uMsg .case WM_INITDIALOG rcall SendMessage,hWin,WM_SETICON,1,lParam ; установить иконку в строке заголовка mov hImg3,rvcall(GetDlgItem,hWin,302) rcall SendMessage,hImg3,STM_SETIMAGE,IMAGE_ICON,lParam ; иконка в клиентской области .case WM_COMMAND .switch wParam .case 304 ; кн. Автор invoke MsgboxI,0,"Рысованый А.Н., каф. КИП","Автор",MB_OK,13 .case 306 ; кн. Функционал invoke WinExec,ADDR szRun,SW_HIDE .case 305 ; кн. Exit 87 jmp exit_dialog3 .endsw .case WM_CLOSE exit_dialog3: rcall EndDialog,hWin,0 .endsw xor rax, rax ret Dial3 endp end Діалогова програма викликає ехе-файл перевірки пароля (програма 5.4, яка розглянута раніше). 6.5. Лабораторна робота «Самомодифікація коду» Мета заняття: набути практичних навичок застосування самодифікації коду. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – представити дві програми: без діалогового вікна та проект з діалоговими вікнами; – у тексті програми організувати виведення назви лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання Відповідно до номера студента у групі вибрати варіант завдання та написати в середовищі masm64 на асемблері програму обчислення одного з виразів. Отримані результати записати у файл. Варіанти 1. Помножити матрицю [2 х 2] на матрицю [2 х 2]. 2. Помножити матрицю [2 х 2] на матрицю [2 х 2] mod2. 3. Помножити матрицю [2 х 2] на матрицю [2 х 2] за mod3. 88 4. Помножити матрицю [3 х 3] на матрицю [3 х 1]. 5. Помножити матрицю [3 х 3] на матрицю [3 х 1] mod2. 6. Помножити матрицю [3 х 3] на матрицю [3 х 1] mod3. 7. Помножити матрицю [3 х 3] на матрицю [3 х 3]. 8. Помножити матрицю [3 х 3] на матрицю [3 х 3] mod2. 9. Помножити матрицю [3 х 3] на матрицю [3 х 3] mod3. 10. Помножити матрицю [4 х 4] на матрицю [4 х 1]. 11. Помножити матрицю [4 х 4] на матрицю [4 х 1] mod2. 12. Помножити матрицю [4 х 4] на матрицю [4 х 1] mod3. 13. Помножити матрицю [4 х 4] на матрицю [4 х 4]. 14. Помножити матрицю [4 х 4] на матрицю [4 х 4] mod2. 15. Помножити матрицю [4 х 4] на матрицю [4 х 4] mod3. 16. Помножити матрицю [5 х 5] на матрицю [5 х 1]. 17. Помножити матрицю [5 х 5] на матрицю [5 х 1] mod2. 18. Помножити матрицю [5 х 5] на матрицю [5 х 1] mod3. 19. Помножити матрицю [5 х 5] на матрицю [5 х 5]. 20. Помножити матрицю [5 х 5] на матрицю [5 х 5] mod3. 21. Помножити матрицю [6 х 6] на матрицю [6 х 6]. 22. Помножити матрицю [6 х 6] на матрицю [6 х 6] по mod3. 23. Помножити матрицю [7 х 7] на матрицю [7 х 7]. 24. Помножити матрицю [7 х 7] на матрицю [7 х 7] mod2. 25. Помножити матрицю [7 х 7] на матрицю [7 х 7] mod3. 26. Помножити матрицю [8 х 8] на матрицю [8 х 8]. Програма 6.6. Розмноження матриці [3 х 3] на матрицю [3 х 1]: include \masm64\include64\masm64rt.inc ; .data mas1 dd 1,2,3 ; a1, b1, c1 mas2 dd 4,5,6 ; a2, b2, c2 mas3 dd 3,2,1 ; a3, b3, c3 mas4 dd 10,; d1 11,; d2 12 ; d3 buf1 dq 3 dup(0); буфер fmt1 db "masm64.",10,10, "|1 2 3| |10|",10,"|4 5 6| x |11|", 10,"|3 2 1| |12|",10,10, "Результат = |%d %d %d|T",10,10, "Автор: Рысованый А.Н., каф. КИП, НТУ ХПИ",0 titl1 db "Перемножение матриц 3х3 на 3х1.",0 .code ; entry_point proc 89 ;;;; Умножить матрицу [3 х 3] на матрицу [3 х 1] vmovd xmm1,mas1[0] ; a1 [3x3] vmovd xmm2,mas1[4] ; b1 vmovd xmm3,mas1[8] ; c1 vmovd xmm4,mas4[0] ; d1 vmovd xmm5,mas4[4] ; d2 vmovd xmm6,mas4[8] ; d3 vpmulld xmm10,xmm1,xmm4 ;[a1xd1] vpmulld xmm11,xmm2,xmm5 ;[b1xd1] vpmulld xmm12,xmm3,xmm6 ;[c1xd3] vaddsd xmm10,xmm10,xmm11 vaddsd xmm10,xmm10,xmm12 vmovd r10d,xmm10 movsxd r10,r10d ; [3x1] result vmovd xmm1,mas2[0] ; a2 [3x3] vmovd xmm2,mas2[4] ; b2 vmovd xmm3,mas2[8] ; c2 vpmulld xmm7,xmm1,xmm4 ;[a2xd1] vpmulld xmm8,xmm2,xmm5 ;[b2xd2] vpmulld xmm9,xmm3,xmm6 ;[c3xd3] vaddsd xmm11,xmm7,xmm8 ; vaddsd xmm11,xmm11,xmm9 ; vmovd r11d,xmm11 movsxd r11,r11d ;a2 [3x1] result vmovd xmm1,mas3[0] ; a3 [3x3] vmovd xmm2,mas3[4] ; b3 vmovd xmm3,mas3[8] ; c3 vpmulld xmm1,xmm1,xmm4 ;[a3xd1] vpmulld xmm2,xmm2,xmm5 ;[b3xd2] vpmulld xmm3,xmm3,xmm6 ;[c3xd3] vaddsd xmm12,xmm1,xmm2 vaddsd xmm12,xmm12,xmm3 vmovd r12d,xmm12 movsxd r12,r12d ;a3 [3x1] result invoke wsprintf,addr buf1,ADDR fmt1,r10,r11,r12 invoke MessageBox,0,addr buf1,addr titl1,MB_OK invoke ExitProcess,0 entry_point endp end 90 Контрольні питання для перевірки знань 1. Наведіть особливості формування коду операцій команд переходу. 2. Яка розмірність команд ближнього та далекого переходу? 3. Опишіть послідовність зміни коду операції. 4. Опишіть послідовність перевірки пароля. 5. Опишіть алгоритм написання програми, що самодифікується. 91 7. ПЕРЕВІРКА ДАТИ І ЧАСУ 7.1. Загальні відомості про фіксовану кількість запусків Обхід захисту програми за датою та часом (як і будь-який інший простий захист) для звичайного користувача є непереборною дією. Це можна стверджувати і для звичайного програміста. Для хакера це дуже легко (якщо він хакер). Для будь-якого злому необхідні специфічні знання, робота з особливим програмним забезпеченням та досвід, який напрацьовується на простих методах захисту. Фіксовану кількість запусків програми можна реалізувати кількома способами в залежності від конкретних вимог та мови програмування. Можна організувати обмеження запусків програми за такими подіями: - по даті; = запис дати першого запуску та перевірка з поточною датою; = Порівняння поточної дати з датою створення. - за часом використання; - за кількістю запусків програми; - за кількістю записів у файл та ін. Лічильники подій можна розмістити: - у реєстрі; - у ключовому файлі; - в оперативній пам'яті та ін. 7.2. Перевірка дати закінчення запуску програми У програмі 7.1 розглядається перевірка дати закінчення її запуску за рахунок порівняння з елементами структури. У цій структурі наведено дату, з якою порівнюється з поточною датою. І за перевищення поточної дати виконується перехід закінчення роботи програми. Програма 7.1. Перевірка дати закінчення запуску: include \masm64\include64\masm64rt.inc ; ; ограничение запуска по установленной дате ; консольный ввод SYSTEMTIME1 STRUCT wYear dw ? ; год 92 wMonth dw ? ; месяц wDayOfWeek dw ? ; день недели wDay dw ? ; день месяца 0-31 wHour dw ? ; час 0-23 wMinute dw ? ; минута 0-59 wSecond dw ? ; секунда 0-59 wMilliseconds dw ? ; миллисекунда 0-999 SYSTEMTIME1 ENDS .data ; today SYSTEMTIME1 date_final SYSTEMTIME1 <2024,6,?,15,?,?,?,?> ; установленная дата Password db "12345" ; пароль len1 equ $-Password ; число байтов Buf dq 3 dup(0),0 ; Err1 dq 3 dup(0),0 Msg1 db "Пароль совпал",0 Msg2 db "Пароль не корректен",0 Title1 db "Проверка пароля",0 out1 HANDLE 0 ; in1 HANDLE 0 ; cRead dq 3 dup(0),0 ; cWritten dq 3 dup(0),0 ; Msg db "Введите пароль, 5 значений",10,0 ; Msg3 db "*****",10,0 ; .code ; entry_point proc ; invoke GetSystemTime,addr today ; извл. текущую системную дату и время movzx r13,today.wYear ; значение текущего года movzx r14,date_final.wYear ; значение конечного года cmp r14,r13 ; сравнение годов JNBE next1 ; если не > и = movzx r13, today.wMonth ; извлечение значения текущего месяца movzx r14, date_final.wMonth ; извлеч. конечного значения месяца cmp r14,r13 ; сравнение значений jnbe next1 ; если не > и = movzx r13, today.wDay ; значение текущего дня movzx r14, date_final.wDay ; значение конечного дня cmp r14, r13 ; сравнение значений дней 93 jnbe next1 ; если >= fn MessageBox,0,"Дата запуска программы истекла","Проверка интервала времени",MB_OK jmp exit next1: fn MessageBox,0,"Дата запуска программы не истекла","Проверка интервала времени",MB_ICONINFORMATION invoke GetStdHandle,STD_OUTPUT_HANDLE; mov out1,rax invoke GetStdHandle,STD_INPUT_HANDLE mov in1,rax invoke SetConsoleCP,1251;устан. кодовой стр. win 1251 в поток ввода invoke SetConsoleOutputCP,1251;устан. кодовой стр. win 1251 в поток вывода invoke WriteConsole,out1,ADDR Msg,sizeof Msg,ADDR cWritten,0 invoke WriteConsole,out1,ADDR Msg3,sizeof Msg3,ADDR cWritten,0 invoke ReadConsole,in1,ADDR Buf,5,ADDR cRead,0 lea rsi,Password ;адрес первого элемента строки lea rdi,Buf ;адрес второго элемента строки mov rcx,len1 repe cmpsb ; побайтно проверяется len1 раз jz m2 ; inc Err1 ; счетчик несовпадений jmp exit m2: invoke MessageBox,0,addr Msg1,addr Title1,MB_OK exit: invoke ExitProcess,0 entry_point endp end Розглянута програма консольна. Отже, вона повинна компілюватися bat-файлом з параметром лінкування /SUBSYSTEM:CONSOLE. Під час запуску програми спочатку перевіряються три дати: рік, місяць та день, до якої дозволено використовувати програму (рис. 7.1). 94 Після натискання на кнопку якого виводиться в консольне вікно пропозиція про введення пароля (рис. 7.2). У консольному вікні виведено 2 рядки. Перший рядок з текстом виводиться функцією з параметрами: invoke WriteConsole,out1,ADDR Msg,sizeof Msg,ADDR cWritten,0 Другий рядок із символами "*****" виводиться функцією з параметрами: invoke WriteConsole,out1,ADDR Msg3,sizeof Msg3,ADDR cWritten,0 7.3. Запис у файл кількості запусків програми Розглянемо програму 7.2, у якій окремий файл записується кількість запусків цієї програми. Для зменшення рядків програми застосовані макроси середовища masm64. Рис. 7.1. Перше повідомлення програми Рис. 7.2. Друге повідомлення програми 95 Програма 7.2. Запис у файл кількості запусків програми: include \masm64\include64\masm64rt.inc ; Запись количества запусков программы в файл. ; Алгоритм: ; - делается попытка открыть файл, ; - если получилось - записываем 0. ; - при каждой записи инкрементируем значение. ; - иначе – создается новый файл и записывается в него число 0 .code entry_point proc LOCAL hFile: QWORD LOCAL run_count: QWORD LOCAL buf_ptr: QWORD mov buf_ptr,ptr$(run_count) mov hFile,flopen("test3.txt") ; открыть файл cmp hFile,INVALID_HANDLE_VALUE je FILE_NOT_EXISTS ; если файл не существует jmp INC_RUN_COUNT FILE_NOT_EXISTS: mov hFile,flcreate("test3.txt"); создать файл mov run_count,0 jmp WRITE_FILE INC_RUN_COUNT: mov rax,flread(hFile,buf_ptr,sizeof run_count); чтение файла cmp rax,0 je FILE_READ_ERROR mov rax, flseek(hFile,0,0); установить указатель файла. См. API SetFilePointer inc run_count ; увеличение счетчика запуска файла jmp WRITE_FILE FILE_READ_ERROR: flclose hFile ; закрыть открытый файл по дескриптору файла fn MessageBox,0,LastError$(),"ReadFile: Last Error",MB_OK .exit WRITE_FILE: ; запись mov rax,flwrite(hFile,buf_ptr,sizeof run_count) cmp rax,0 je FILE_WRITE_ERROR 96 flclose hFile fn MessageBox,0,str$(run_count),"Количество запусков программы",MB_OK .exit FILE_WRITE_ERROR: flclose hFile ; закрыть открытый файл по дескриптору файла fn MessageBox,0,LastError$(),"WriteFile: Last Error", MB_OK .exit entry_point endp end Макроси середовища наведені у довіднику та деякі з них представлені на рис. 7.3. На початку програми створюється файл, який записується початкове значення 0. При наступних запусках значення кількості запусків програми інкрементується. У програмі не аналізується кількість решти запусків файлу і заборона запуску надалі. Рис. 7.3. Макроси у довіднику masm64 97 7.4. Обмеження спроб введення пароля Дуже часто в різних програмах вводиться обмеження кількості спроб введення пароля. Ціль такого обмеження – зменшення ймовірності підбору пароля. У програмі 7.3 розглядається приклад обмеження спроб введення пароля трохи більше 2 разів. Для простоти довжина неправильного пароля не повинна перевищувати 8 символів. Приклад 7.3. Обмеження спроб введення неправильного пароля не більше 2-х разів: include \masm64\include64\masm64rt.inc ; ; Ограничение попыток ввода пароля (2 раза) ; в программе ограничение на длину не правильного пароля - не более 8 символов .data Password db "12345" ; пароль len1 equ $-Password ; число байтов Buf dq 0,0 ; Err1 dq 0 out1 HANDLE 0 ; in1 HANDLE 0 ; cRead dq 0,0 ; cWritten dq 0,0 ; Msg db "Введите пароль, 5 значений",10,0 ; Msg3 db "*****",10,0 ; .code ; entry_point proc ; invoke GetStdHandle,STD_OUTPUT_HANDLE; mov out1,rax invoke GetStdHandle,STD_INPUT_HANDLE mov in1,rax invoke SetConsoleCP,1251;уст. кодовой страницы win 1251 в поток ввода invoke SetConsoleOutputCP,1251;уст. кодовой стр. win 1251 для вывода m10: invoke WriteConsole,out1,ADDR Msg,sizeof Msg,ADDR cWritten,0 invoke WriteConsole,out1,ADDR Msg3,sizeof Msg3,ADDR cWritten,0 invoke ReadConsole,in1,ADDR Buf,5,ADDR cRead,0 98 lea rsi,Password ;адрес первого элемента строки lea rdi,Buf ;адрес второго элемента строки mov rcx,len1 repe cmpsb ; побайтно проверяется len1 раз; [RDI] – [RSI], пока RСХ / ? 0 jz m2 ; inc Err1 ; счетчик кол. проходов jmp exit m2: .if (Err1==0); fn MessageBox,0,"Пароль совпал","Проверка пароля", MB_ICONINFORMATION jmp exit1 .else exit: fn MessageBox,0,"Пароль не корректен","Проверка пароля", MB_ICONERROR invoke ReadConsole,in1,ADDR Buf,5,ADDR cRead,0 .endif inc Err1 ; увеличение счетчика повторения программы .if Err1==2 ; mov Err1,0; при вводе правильного пароля 2-й раз jmp m10 ; на новый запуск программы .endif exit1: invoke ExitProcess,0 entry_point endp end Лічильник кількості неправильно введених паролів організований змінної Err1, нульове значення якого аналізується тоді, коли значення збіглися. На рис. 7.4 наведено приклад 2-х спроб введення пароля. 99 7.5. Запис до реєстру кількості запусків програми Windows фіксує кожне звернення користувача до тієї чи іншої програми і формує на основі зібраної статистики список програм, що часто використовуються. До реєстру також можна записувати різну інформацію, у тому числі кількість запусків вибраної програми. У Windows 11 процес запису в реєстр розробники ОС зробили лише з правами адміністратора, що певною мірою ускладнює використання такого прийому. При написанні програми роботи з реєстром необхідно враховувати, що функції API, що відповідають за використання реєстру, знаходяться в бібліотеці advapi32. Тому цю бібліотеку треба приєднувати примусово на початку програми або доповнити файл masm64rt.inc цими рядками бібліотеки. Програма 7.4. Запис до реєстру кількості запусків програми: include \masm64\include64\masm64rt.inc include \masm64\include64\advapi32.inc includelib \masm64\lib64\advapi32.lib .data szTestKey db 'Software\Microsoft\Windows\CurrentVersion\Policies',0 szREGSZ db 'REG_DWORD',0 hKey dd ? lpdwDisp dd ? szValueName1 db "Timer",0; новая запись с именем setValue dq "8",0 buf1 LPDWORD 128,0 getKol LPBYTE "8",0 ; максимальное количество запусков buf3 LPDWORD 128,0 error dd ?,0 ertit db "Описание ошибки",0 ook db "Ключ успешно создан",0 Рис. 7.4. Приклад 2-х спроб введення пароля 100 bed db "Ключ не создан",0 kk db "Реестр",0 kol dd 0 ; для максимального числа первой строки titl db "Результат программы",0 buf dd 0,0 ifmt db "Условие: Ограничение количества запусков программы ",10,10,\ "Осталось количество запусков: %c",0 .code entry_point proc ; СОЗДАЕМ РАЗДЕЛ invoke RegCreateKeyEx,HKEY_CURRENT_USER,ADDR szTestKey, 0, ADDR szREGSZ, REG_OPTION_VOLATILE,KEY_ALL_ACCESS,0,ADDR hKey,ADDR lpdwDisp; cmp rax,0 ;Раздел создан успешно ? jne m1 ; ПРОВЕРЯЕМ РАЗДЕЛ cmp lpdwDisp,REG_OPENED_EXISTING_KEY je m2 ; СОЗДАЕМ КЛЮЧ И ЗАПИСЫВАЕМ ДАННЫЕ invoke RegSetValueEx,hKey,addr szValueName1,0,REG_SZ, addr setValue,1 ; размер ключа в байтах cmp rax,0 ;ПРОВЕРКА КЛЮЧА НА СОЗДАНИЕ jne m1 invoke MessageBox,0,addr ook,addr kk,MB_ICONINFORMATION jmp m3 m2: ; ОТКРЫВАЕМ КЛЮЧ СЧИТЫВАЕМ ДАННЫЕ И ЗАПИСЫВАЕМ НОВЫЕ invoke RegQueryValueEx,hKey,addr szValueName1,0,addr buf1,addr getKol, addr buf3 ; cmp getKol,"0" je ex dec getKol invoke RegSetValueEx,hKey,addr szValueName1,0,REG_SZ,addr getKol,1 ; ;размер ключа в байтах m3: ; ЗАКРЫВАЕМ РАЗДЕЛ 101 invoke RegCloseKey,hKey ; идентификация открытого ключа для закрытия jmp next m1: ; ВЫВОДИМ СООБЩЕНИЕ ОБ ОШИБКЕ invoke FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM,0,rax,0, addr error,100,0 invoke MessageBox,0,addr error,addr bed,MB_ICONINFORMATION jmp ex next: invoke wsprintf, ADDR buf, ADDR ifmt,getKol invoke MessageBox,0,addr buf,addr titl,MB_ICONEXCLAMATION ex: invoke ExitProcess,0 entry_point endp end Результат запису в реєстр з кількістю запусків, що залишилися, наведено на рис. 7.5. Повідомлення про можливу помилку вводиться функцією FormatMessage, яка, окрім закладених у ній можливостей щодо форматування рядків, може використовуватися для отримання текстового опису помилки за її кодом. Рис. 7.5. Результат запису в реєстр з кількістю запусків, що залишилися 102 Розглянемо діалогову програму встановлення та видалення ключів у реєстр. Програма встановлює та видаляє ключі у гілки реєстру, а також створює у реєстрі розділ та підрозділ. При створенні проекту з діалоговим вікном спочатку визначаються з інтерфейсом вікна: наявністю інформації, що виводиться, кількістю кнопок, якістю та інформаційністю іконки. Для створення інтерфейсу використано програму ResEd. Зовнішній вигляд створеного діалогового вікна наведено на рис. 7.6. Програма 7.5. Файл ресурсів: #define IDI_ICON 1001 #define IDD_DLG1 100 #define IDC_CREATEKEY 1001 #define IDC_DELETEKEY 1003 #define IDC_STC1 1013 #define IDC_STC2 1004 #define IDC_GRP1 1005 #define IDC_BTN1 103 #define IDC_BTN2 101 #define IDC_IMG1 102 #include "C:/masm64/include64/Resource.h" 10 ICON DISCARDABLE "user.ico" IDD_DLG1 DIALOGEX 12,6,294,171 CAPTION "Запись и удаление ключей в реестре" FONT 8,"MS Sans Serif",0,0,0 STYLE WS_VISIBLE|WS_OVERLAPPEDWINDOW BEGIN Рис. 7.6. Зовнішній вигляд діалогового вікна 103 CONTROL "Создать ключи",IDC_CREATEKEY,"Button", WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,15,27,102,21 CONTROL "Удалить ключи",IDC_DELETEKEY,"Button", WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,174,27,96,21 CONTROL "https://blogs.kpi.kharkov.ua/v2/asm/",IDC_STC1, "Static",WS_CHILDWINDOW|WS_VISIBLE,156,153,132,12 CONTROL "НТУ ХПИ",IDC_STC2,"Static", WS_CHILDWINDOW|WS_VISIBLE|SS_CENTER,186,132,69,9 CONTROL "Управление реестром",IDC_GRP1,"Button", WS_CHILDWINDOW|WS_VISIBLE|BS_GROUPBOX,6,9,279,45 CONTROL "Выход",IDC_BTN1,"Button", WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,174,96,96,21 CONTROL "Инф. о ветвях реестра",IDC_BTN2,"Button", WS_CHILDWINDOW|WS_VISIBLE|WS_TABSTOP,174,66,96,21 CONTROL "",IDC_IMG1,"Static", WS_CHILDWINDOW|WS_VISIBLE|SS_CENTERIMAGE|SS_ICON,30,69,87,75 END VS_VERSION_INFO VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 FILEOS 0x00000000 FILETYPE 0x00000000 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "041904B0" BEGIN VALUE "CompanyName", "HTU KhPI\000\0" VALUE "FileDescription", "Рысованый А.Н.\000\0" VALUE "FileVersion", "1.0\000\0" VALUE "InternalName", "Reg64KeyDial-01\000\0" VALUE "OriginalFilename", "Reg64KeyDial-01.exe\000\0" VALUE "LegalCopyright", "\251 2024 Рысованый А.Н.\000\0" VALUE "ProductName", "Reg64KeyDial-01\000\0" VALUE "ProductVersion", "1.0\000\0" END END BLOCK "VarFileInfo" BEGIN VALUE "Translation", 0x0419, 0x04B0 END END // 0x0419 язык русский, 0x0409 англ. // 0x04B0 страница кодировки 104 /* английский язык (0x409) в кодовой странице Windows ANSI (1252). */ /*VALUE "Translation", 0x409, 1252*/ Наприкінці файлу ресурсів вставлені рядки коду, які відповідають формування властивостей файла. Після отримання ехе-файлу ці рядки дозволяють вивести властивості файлу (рис. 7.7). Програма 7.6. Файл ресурсів: include \masm64\include64\masm64rt.inc include \masm64\include64\advapi32.inc includelib \masm64\lib64\advapi32.lib ; уст. ключей и раздела в реестр .data? hInstance dq ? hIcon dq ? hBmp dq ? hStatic dq ? hKey dd ? lpdwDisp dd ? .data szREGSZ db 'REG_SZ',0 Рис. 7.7. Властивості файлу 105 setValue db "10",0 ValSize1 db 4,0 buf1 LPDWORD 128,0 ; getKol LPBYTE "9",0 ; buf3 LPDWORD 128,0 ; error dd ?,0 ; res1 dq 0 ; key1 db "SYSTEM\ControlSet001\ Services\W32Time\TimeProviders\NtpClient8",0 valueName1 db "SpecialPollInterval ",0 key2 db "SOFTWARE\Microsoft\Windows\CurrentVersion\ DateTime\Servers8",0 valueName2 db "sTimeFormat ",0 key3 db 'Control Panel\International2',0 ; уст. Раздела valueName3 db "sTimeFormat ",0 buf dq 12 dup(0),0 .code entry_point proc mov hInstance,rvcall(GetModuleHandle,0) mov hIcon,rv(LoadImage,hInstance,10,IMAGE_ICON,128,128, LR_DEFAULTCOLOR) invoke DialogBoxParam,hInstance,100,0,ADDR main,hIcon invoke ExitProcess,0 ret entry_point endp main proc hWin:QWORD,uMsg:QWORD,wParam:QWORD,lParam:QWORD .switch uMsg .case WM_INITDIALOG ; сообщение о создании диал. окна invoke SendMessage,hWin,WM_SETICON,1,lParam ; отправляет смс окну invoke SendMessage,rv(GetDlgItem,hWin,102),\ ; сообщение окну по дескриптору органа управления STM_SETIMAGE,IMAGE_ICON,lParam .return TRUE .case WM_COMMAND ; сообщение от меню или кнопки .switch wParam 106 .case 101 ; если выбран вывод информации о задании .data txt2 db "Создание и удаление ключей реестра по адресам:", 10,10,"Ветки:"," HKLM\SYSTEM\ControlSet001\Services\ W32Time\TimeProviders\NtpClient", 10,"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\ DateTime\Servers", 10,"HKCU\Control Panel\International", 10,10,"Ключи: SpecialPollInterval, sTimeFormat.",0 titl2 db "Информация о задании",0 .code invoke MsgboxI,hWin,ADDR txt2,ADDR titl2,MB_OK,10 .case 103 ; .data msg db "Выход из программы.",0 ; вывод уведомления о выходе .code invoke MsgboxI,hWin,ptr$(msg),"Выход",MB_OK,10 rcall SendMessage,hWin,WM_SYSCOMMAND,SC_CLOSE,0 ; уничтожение окна .case 1001 ; Создание ключа в реестре .code ; создание раздела invoke RegCreateKeyEx,HKEY_LOCAL_MACHINE,ADDR key1,0,ADDR szREGSZ,REG_OPTION_VOLATILE,KEY_ALL_ACCESS,0,ADDR hKey,ADDR lpdwDisp; cmp rax,0 ; проверка создан ли раздел jne m11 cmp lpdwDisp,REG_OPENED_EXISTING_KEY ; проверяем раздел je m21 ; создание ключа и записывание данных invoke RegSetValueEx,hKey,addr valueName1,0,REG_SZ,addr setValue,1 ; размер ключа в байтах cmp rax,0 ; проверка создался ли ключ jne m11 invoke MessageBox,0,chr$("Ключ 1 создан"),chr$("Создание ключа"),MB_ICONINFORMATION jmp m31 m21: 107 invoke RegQueryValueEx,\ ; откр. ключа, считывание и записывание новых hKey,addr valueName1,0,addr buf1,addr getKol,addr buf3 ; cmp getKol,"0" je part2 dec getKol invoke RegSetValueEx,hKey, addr valueName1,0, REG_SZ,addr getKol,1 ; ;размер ключа в байтах m31: ; закрытие раздела invoke RegCloseKey,hKey ; идентификация открытого ключа для закрытия jmp part2 m11: ; вывод сообщения об ошибке invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,\ NULL,rax,NULL,addr error,100,NULL invoke MessageBox,0,addr error,chr$("Ошибка"),\ MB_ICONINFORMATION jmp _end ;=========================== part2: invoke RegCreateKeyEx,\ ; создание раздела HKEY_LOCAL_MACHINE,ADDR key2,0,ADDR szREGSZ,REG_OPTION_VOLATILE,KEY_ALL_ACCESS,0,ADDR hKey,ADDR lpdwDisp; cmp rax,0 ; проверка создан ли раздел jne m12 cmp lpdwDisp,REG_OPENED_EXISTING_KEY ; проверяем раздел je m22 invoke RegSetValueEx,\ ; создание ключа и записывание данных hKey,addr valueName2,0,REG_SZ,addr setValue,1 ;размер ключа в байтах cmp rax,0 ; проверка создался ли ключ jne m12 invoke MessageBox,0,chr$("Ключ 2 создан"),chr$("Создание ключа"), MB_ICONINFORMATION jmp m32 108 m22: ; откр. ключа, считывание и записывание новых invoke RegQueryValueEx,hKey,addr valueName2,0,addr buf1,addr getKol, addr buf3 ; cmp getKol,"0" je _end dec getKol ; размер ключа в байтах invoke RegSetValueEx,hKey,addr valueName2,0,REG_SZ,addr getKol,1 m32: ; закрытие раздела invoke RegCloseKey,hKey ; идентификация открытого ключа для закрытия jmp part3 m12: ; вывод сообщения об ошибке invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,\ NULL,rax,NULL,addr error,100,NULL invoke MessageBox,0,addr error,chr$("Ошибка"),\ MB_ICONINFORMATION jmp _end ;======================= part3: ; создание раздела invoke RegCreateKeyEx, HKEY_CURRENT_USER,ADDR key3, 0,ADDR szREGSZ, REG_OPTION_VOLATILE,KEY_ALL_ACCESS,0,ADDR hKey,ADDR lpdwDisp; cmp rax,0 ; проверка создан ли раздел jne m13 cmp lpdwDisp,REG_OPENED_EXISTING_KEY ; проверяем раздел je m23 invoke RegSetValueEx,\ ; создание ключа и записывание данных hKey,addr valueName2,0,REG_SZ,addr setValue,1;размер ключа в байтах cmp rax,0 ; проверка создался ли ключ 109 jne m13 invoke MessageBox,0,chr$("Ключ 3 создан"),chr$("Создание ключа"), MB_ICONINFORMATION jmp m33 m23: ; открытие ключа, считывание и записывание новых invoke RegQueryValueEx,hKey,addr valueName2,0,addr buf1,addr getKol,addr buf3 ; cmp getKol,"0" je _end dec getKol invoke RegSetValueEx,\ hKey,addr valueName2,0,REG_SZ,addr getKol,1 ; размер ключа в байтах m33: ; закрытие раздела invoke RegCloseKey,hKey ; идентификация открытого ключа для закрытия jmp part3 m13: ; вывод сообщения об ошибке invoke FormatMessage,FORMAT_MESSAGE_FROM_SYSTEM,\ NULL,rax,NULL,addr error,100,NULL invoke MessageBox,0,addr error,chr$("Ошибка"),\ MB_ICONINFORMATION _end: .case 1003 ; Удаление ключа реестра invoke RegDeleteKey, HKEY_LOCAL_MACHINE, chr$("SYSTEM\ControlSet001\ Services\W32Time\TimeProviders\NtpClient8") .if eax == ERROR_SUCCESS ; ключ создан успешно ? invoke RegCloseKey,addr hKey ;закр. дескриптора ключа invoke MessageBox,0,chr$("Ключ 1 успешно удалён"),chr$("Реестр"),MB_ICONINFORMATION .else invoke MessageBox,0,chr$("Ключ 1 не удалён"),chr$("Ошибка"), MB_ICONINFORMATION 110 .endif invoke RegDeleteKey,HKEY_LOCAL_MACHINE, chr$("SOFTWARE\Microsoft\Windows\CurrentVersion\DateTime\Servers8") .if eax == ERROR_SUCCESS ; ключ создан успешно ? invoke RegCloseKey,addr hKey ;закрытие дескриптора ключа в системном реестре invoke MessageBox,0,chr$("Ключ 2 успешно удалён"),chr$("Реестр"), MB_ICONINFORMATION .else invoke MessageBox,0,chr$("Ключ 2 не удалён"),chr$("Ошибка"), MB_ICONINFORMATION .endif invoke RegDeleteKey,HKEY_CURRENT_USER,chr$("Control Panel\International2") .if eax == ERROR_SUCCESS ; ключ создан успешно ? invoke RegCloseKey,addr hKey ;закрытие дескриптора ключа invoke MessageBox,0,chr$("Ключ 3 успешно удалён"), chr$("Реестр"),MB_ICONINFORMATION .else invoke MessageBox,0,chr$("Ключ 3 не удалён"),chr$("Ошибка"), MB_ICONINFORMATION .endif .endsw .case WM_CLOSE ; если есть сообщение о закрытии окна invoke RegDeleteKey,HKEY_LOCAL_MACHINE, chr$("SYSTEM\ ControlSet001\ Services\W32Time\TimeProviders\NtpClient8") invoke RegDeleteKey,HKEY_LOCAL_MACHINE, chr$("SOFTWARE\Microsoft\Windows\CurrentVersion\DateTime\Servers8") invoke RegDeleteKey,HKEY_CURRENT_USER,chr$("Control Panel\ International8") invoke EndDialog,hWin,0 ; .endsw xor rax, rax ret main endp 111 end Результат додавання каталогу до реєстру показано на рис. 7.7. 7.6. Лабораторна робота «Перевірка дати та часу» Мета заняття: придбати практичні навички написання та застосування програм фіксованої кількості запусків та виправлення у ехе-файлах у середовищі masm64. У звіті подати: – номер, назва лабораторної роботи та прізвище виконавця; - Завдання; – текст програми перевірки пароля з розміщенням даних у сторінках коду із двома блоками: без перекриття коду та з перекриттям. Привести коментарі до кожного рядка цих програм; – у тексті програми організувати виведення назви лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – результати виконання програм (скриншоти спрощеного вікна (MessageBox) та відладчика); – загальний алгоритм програми із частиною коду перевірки пароля; – скріншоти пошуку пароля. Рис. 7.8. Результат додавання каталогу до Реєструр 112 Постановка задачі 1. Написати програму в середовищі masm64 відповідно до варіанта завдання. 2. Вивести результати виконання програми. Програму та результати навести у звіті. 3. Навести скріншоти послідовності пошуку пароля. Завдання Вибрати варіант відповідно до завдання. Студенти другого та третього десятків за списком обирають аналогічні завдання, тобто. студент із 21 номером за списком обирає 1-й номер варіанта: 1. Обмежити запуск програми, залежно від дати першого запуску. 2. Обмежити запуск програми, залежно від поточної дати. 3. Обмежити запуск програми в залежності від дати створення. 4. Обмежити запуск програми в залежності від дати та часу останньої операції запису у файл. 5. Обмежити запуск програми за часом використання. 6. Обмежити кількість запусків програми. 7. Запис у файл кількості запусків програми без використання макросів. 8. Запис у файл кількості запусків програми та перевірка цієї кількості. 9-10. Обмеження спроб введення пароля з виведенням попереджувальних повідомлень. * Інший спосіб обмеження. Контрольні питання для перевірки знань 1. Яка основна програма використовується для зламування коду? 2. У яких місцях можна організувати лічильники подій? 3. У яких місцях програми можна розмістити дату закінчення роботи програми? 4. В яких місцях можна заховати пароль? 5. Які особливості програми роботи з реєстром? 113 8. ШИФРУВАННЯ ПАРОЛЮ 8.1. Загальні відомості про шифрування пароля Шифрування пароля дозволяє трохи збільшити безпеку програми, т.к. у ехе-файлі знаходиться не відкритий пароль. Це допоможе не довго збільшити час злому парольної програми. Вся справа в тому, що якщо хакер досліджує програму і знає, що вона має парольний вхід, то він шукає команду порівняння і наступну за нею команду переходу, як правило, команду jnz. Потім змінює її, наприклад, команду jz. І це дозволить реагувати програмі на будь-який пароль. Але в навчальних цілях застосування парольного захисту дає змогу отримати навички дослідження таких простих програм. Можна виділити різні прийоми шифрування пароля, наприклад: - шифрування пароля окремою програмою; - шифрування пароля у самій програмі; - шифрування та розкид елементів пароля в різні частини програми; - шифрування та приховування пароля. Для шифрування пароля переважно застосовують зворотне шифрування. Наприклад, під час передачі пароля його попередньо складають по модулю 2 з ключем. А на приймальні - отриманий оброблений пароль складають з тим же ключем і пароль буде розшифровано. Ключ відомий лише обмеженому колу людей. Це робиться, щоб у разі перехоплення пароля зловмисник не зміг скористатися ним відразу. До зворотної операції можна також віднести операцію зсув вправо, але в приймальні - зрушення вліво та інші аналогічні прості дії. 8.2. Шифрування пароля окремою програмою Написання програми з розміщенням пароля у секції даних є найбільшим недоліком таких програм, т.к. пароль відразу ж видно у відладчику. Пароль у програмі необхідно приховувати. І найпростішим способом приховування пароля – це розміщення пароля у секції коду. Але у навчальних цілях необхідно починати із найпростіших способів. Для шифрування пароля часто використовують дві програми: - Для шифрування пароля; 114 – для використання зашифрованого пароля. Алгоритм шифрування пароля може бути таким: 1. Зашифровуємо пароль. Для цього використовуємо програму шифрування - у програму перевірки пароля з використанням базових команд вставляється рядок коду xor al, key Запам'ятовуємо зашифрований пароль. (Крім операції mod2 можна використовувати будь-які інші – для яких є дії.) 2. Вставляємо в програму 2 в секцію data зашифрований пароль, а також рядок розшифрування xor al, key Програма 8.1 призначена для шифрування пароля та майже нічим не відрізняється від розглянутої раніше. Програма 8.1. Шифрування пароля: ; программа шифрования пароля по модулю 2 ; поразрядное mod2: txt mod2 key include \masm64\include64\masm64rt.inc ; .data rez db 16 DUP (0) buff db 16 DUP (0) txt db "Enter code/Введите код для преобразования по txt mod2 1:", 10,0 TXTSIZE equ $ - txt ; число символов, выведенных на экран stdin1 dq 0 ; для дескриптора ввода stdout1 dq 0 ; для дескриптора вывода wrn dq 0 ; число записанных символов rdn dq 0 ; число прочитанных символов key db 1 ; первоначальное значение ключа .code entry_point proc invoke GetStdHandle,STD_INPUT_HANDLE ;извлекает дескриптор mov stdin1,rax invoke GetStdHandle,STD_OUTPUT_HANDLE ;извлекает дескриптор mov stdout1,rax invoke SetConsoleCP,1251;уст. кодовой страницы win 1251 в поток ввода invoke SetConsoleOutputCP,1251;уст. страницы. win 1251 в поток вывода 115 invoke WriteConsoleA,stdout1,addr txt,TXTSIZE,addr wrn,0;вывод смс invoke ReadConsole,stdin1,addr buff,16,addr rdn,0;чтение символов ;16 - число символов для чтения ; ReadConsole записала по адр. rdn +2 символа lea rsi, buff ; загрузка адреса буфера lea rdi, rez ; загрузка адреса ячейки результата mov rcx, rdn ; запись количества введенных символов (rdn-2) sub rcx, 2 ; минус служебные символы _cycle: mov al, byte ptr [rsi]; xor al, key ; mov byte ptr [rdi],al ; inc key ; изменение ключа +1 ключа inc rsi ; inc rdi ; loop _cycle invoke WriteConsoleA,stdout1,addr rez,14,addr wrn,0; вывод только 14 символов invoke ReadConsole,stdin1,addr buff,16,addr rdn,0;вывод на 2 символа меньше (16-2=14) invoke ExitProcess,0 entry_point endp end Программа шифрует только 14 символов. Значение ключа изменяется на 1 относительно предыдущего значения. 8.3. Вилучення частини пароля з великої послідовності У програмі пароль складається з 2-х частин. У першій частині відбувається звичайний аналіз пароля, що вводиться. У другій частині відбувається порівняння лише з частиною введеного пароля. Перший пароль дорівнює 1234. Другий пароль дорівнює 567, але у відладчику видно пароль 012345678567. Звичайно, хакеру важко здогадатися, що перевіряється тільки якась частина. 116 Для невеликого заплутування процесу злому програма складається з 2-х вікон: діалогового та консольного. Перший пароль вводиться у діалогове вікно, а другий (пароль 567) у консольне вікно. Програма 8.2. Перевірка пароля з 2-х частин: include \masm64\include64\masm64rt.inc .data? hInstance dq ? hIcon dq ? hImg1 dq ? tEdit dq ? tStat dq ? pbuf dq ? .data Password db "1234" ; пароль, часть 1 len1 equ $-Password Password2 db "012345678567" ; пароль 567, часть 2 len2 equ $-Password2 Buf2 dq 0 ; hOut dq 0 ; hIn dq 0 ; cRead dq 0 ; cWritten dq 0 ; Msg4 db "Enter password, part 2",10,10,0 ;Введите пароль, часть 2 .code entry_point proc mov hInstance, rvcall(GetModuleHandle,0) mov hIcon, rv(LoadImage,hInstance,10,IMAGE_ICON, 128,128, LR_DEFAULTCOLOR) invoke DialogBoxParam,hInstance,200,0,ADDR GetUserText,hIcon .exit entry_point endp GetUserText proc hWin:QWORD,uMsg:QWORD,wParam:QWORD, lParam:QWORD LOCAL buffer[260]:BYTE .switch uMsg .case WM_INITDIALOG invoke SendMessage,hWin,WM_SETICON,1,lParam ; установить значок invoke SendMessage,rv(GetDlgItem,hWin,205),\ ; значок в клиентской обл. STM_SETIMAGE,IMAGE_ICON,lParam 117 mov tEdit, rvcall(GetDlgItem,hWin,201) invoke SetFocus, tEdit ; установка курсора в поле ввода mov pbuf, ptr$(buffer) ; символьный тип данных .case WM_COMMAND .switch wParam .case 202 invoke GetWindowText,tEdit,pbuf,sizeof buffer; записать текст в буфер .if rax == 0 rcall MessageBox,hWin,"Введите пароль или нажмите Cancel","Пароль не введен", MB_ICONINFORMATION rcall SetFocus,tEdit ; установка курсора в поле ввода .else invoke szCmp,pbuf,ADDR Password cmp rax, len1 jne WRONG_PASSWORD invoke MessageBox,0,"1-я часть пароля правильная","Проверка пароля", MB_OK jmp m100 WRONG_PASSWORD: invoke MessageBox,0,cfm$("Пароль был введен неправильно! \n Попробуйте, пожалуйста, снова"),\"Вход в программу",MB_OK jmp exit_dialog m100: invoke GetStdHandle,STD_OUTPUT_HANDLE mov hOut,rax invoke GetStdHandle,STD_INPUT_HANDLE mov hIn,rax invoke WriteConsole,hOut,ADDR Msg4,sizeof Msg4,ADDR cWritten,0 invoke ReadConsole,hIn,ADDR Buf2,len2+2,ADDR cRead,0 lea rsi,Password2 ; загрузка адреса нахождения Password2 lea rdi,Buf2 ; загрузка адреса буфера введенных символов add rsi,9 ; сдвиг на 3 байта mov rcx,3 ; счетчик символов xor r14,r14 m4: mov al,[rsi] ; загрузка числа по адресу из rsi mov bl,[rdi] ; cmp al,bl ; сравнение символов jz m3 ; если совпали символы 118 inc r14 ; счетчик несовпадений m3: add rsi,1 ; inc адреса add rdi,1 ; dec rcx jnz m4 .if r14==0; invoke MessageBox,0,"Пароль совпал","Проверка пароля", MB_ICONINFORMATION .else invoke MessageBox,0,"Пароль не корректен","Проверка пароля",MB_OK .endif jmp exit_dialog .endif .case 203 jmp exit_dialog .endsw .case WM_CLOSE exit_dialog: rcall EndDialog,hWin,0 .endsw xor rax, rax ret GetUserText endp end При запуску ехе-файлу програми спочатку виводиться діалогове вікно із запитом пароля (рис. 8.1). Якщо пароль збігся, виводиться вікно повідомлення (рис. 8.2). Рис. 8.1. Діалогове вікно із запитом пароля 119 Друга частина пароля вводиться у консольне вікно (рис. 8.3). Спрощене вікно про підтвердження збігу 2 частини пароля представлено на рис. 8.4. Рис. 8.2. Вікно з підтвердженням про збіг першої частини пароля Рис. 8.3. Консольне вікно введення пароля Рис. 8.4. Вікно з підтвердженням про збіг другої частини пароля 120 В этой программе при просмотре в отладчике части пароля видны в отладчике (рис. 8.5). У вікні відладника видно значення пароля, але не визначено його частини. Тому для того, щоб знайти точні значення пароля, ехе-файл необхідно покроково трасувати і вже детально розбиратися в коді програми. 8.4. Лабораторна робота «Приховування пароля» Мета заняття: набути практичних навичок застосування приховування пароля в програмі. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – представити дві програми: без діалогового вікна та проект з діалоговим вікном; – у тексті програми організувати виведення назви лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання Пароль розбити на частини та до кожної частини застосувати окремий спосіб приховування. Рис. 8.5. Вікно відладчика з видимим паролем 121 Можна застосувати обробку пароля, отримання пароля з відомих значень програми, використовувати стек, запис даних секцію коду та інших. Контрольні питання для перевірки знань 1. Що необхідно зробити у налагоджувачі з ехе-файлом, щоб програма реагувала на будь-який пароль як на правильний? 2. Які шляхи утруднення пошуку пароля? 3. Які операції зворотного шифрування? 4. Яка послідовність розміщення пароля у секції коду? 5. Які операції можна здійснювати із паролем? 122 9. ШИФРОВАНИЕ ДАННЫХ Шифрование пароля и шифрование данных – это один и тот же процесс. Разница только в том, что данные имеют намного большую длину, например, файл. Существует очень большое количество алгоритмов шифрования. В общем случае можно разделить на такие подгруппы: – возвратное шифрование (xor, сдвиг и т. д.); – хеширование (CRC, контрольная сумма, MD5 и т.д.); – кодирование (полиномы и т.д.). Хеширование - подсчет суммы кода, а при проверке используют не сам код, а его сумму. 9.1. Возвратное шифрование Основным свойством алгоритмов возвратного шифрования является их способность как шифровать, так и расшифровывать данные без принципиального изменения шифровального алгоритма. Простейшими алгоритмами считаются сдвиг и xor, т.к. основная операция этих групп является коммутативной, и порядок операндов значения не имеет. Такими командами могут быть: xor eax, ebx или: add r15,7 ; шифрование sub r15,7 ; дешифровка Внедрение этих простых алгоритмов позволяет быстро и надежно решить проблему шифрования данных. А при применении двух и более простых операций шифрования узнать алгоритм расшифрования становится невыполнимой задачей. Рассмотрим консольную программу 9.1, которая применяет внутренний макрос среды masm64 для шифрования исключительно текста. Ключ шифрования имеет имя padd размерностью 60 значений. Текст, который шифруется - "Реверсное программирование". Программа 9.1. Шифрование текста с применением внутренних макросов среды masm64: ; консольная программа 123 ; применение макросов для шифрования текста по мод2 include \masm64\include64\masm64rt.inc .data ; шифрование сообщения 60 символов padd db "1234567890123456789012345678901234567890123456789 01234567890",0 blen dq SIZEOF padd ppad dq padd .code entry_point proc LOCAL st1 :QWORD LOCAL slen :QWORD invoke SetConsoleCP,1251;уст. кодовой страницы win 1251 в поток ввода invoke SetConsoleOutputCP,1251;установка кодовой страницы win 1251 в поток вывода mrm st1,"Реверсное программирование" ; шифруется только текст mov slen,len(st1) sub blen,1 invoke xor_data,st1,slen,ppad,blen ; строка st1 для шифрования conout "Зашифрованный текст = ",st1,lf ; показать зашифр. результат invoke xor_data,st1,slen,ppad,blen ; xor строка для расшифровки conout "Исходный текст = ",st1,lf ; отображение расшифров. данных waitkey ; ожидание invoke ExitProcess,0 entry_point endp end На рис. 9.1 показано консольне вікно з виведенням результату шифрування. Рис. 9.1. Консольне вікно з виведенням результату шифрування 124 9.2. Операція додавання за модулем 3 Операція підсумовування за модулем кінцевого поля 3 (mod3) не зовсім відноситься до зворотного шифрування, тим більше якщо використовувати 2 логічних стан 0 і 1 (поле числа 2). При використанні трьох логічних станів (0, 1, 2) використовується mod3. Різниця при шифруванні між mod2 і mod3 в тому, що при mod2 необхідно один раз скласти дані з ключем, а при mod3 - дві однакові додавання з ключем. Але такий спосіб кодування вносить деяку частку складності під час розкодування інформації. При цьому необхідно або на стороні, що передає, або на приймальній стороні (залежно від прийнятого правила) здійснити необхідне додавання з ключем. Правила складання та множення у кінцевому полі GF(3) = (А, 3, 3), де А={0,1,2} представлені на рис. 9.2. 3 0 1 2 3 0 1 2 0 0 0 0 0 0 1 2 1 0 1 2 1 1 2 0 2 0 2 1 2 2 0 1 Результат операції за модулем визначається як залишок від розподілу отриманого результату на основу модуля. У цьому випадку, це – розподіл на 3 і отримання залишку. Наприклад, 2 3 2 = 4; 4/3 = 1 – залишок. Цілий результат під час операції отримання модуля не враховується. Якщо необхідно обробляти трійкові сигнали, кодування станів елементів з трьома станами здійснюється за правилом.: 0 → 00, 1 → 01, 2 → 10. У обчислювальній техніці три стани мають достатню велику кількість пристроїв: - шинні формувачі; - Контролери шин; Рис. 9.2. Правила складання та множення у кінцевому полі GF(3) 125 - контролери пристроїв (клавіатури, мишки, ОЗУ, ПЗУ, HDD, SSD, відеокарти та ін); - Пристрої, що запам'ятовують. Отримання mod3 числа наведено у програмі 9.2. Програма 9.2. Отримання mod3: include \masm64\include64\masm64rt.inc .data ; a1 dq 5 ; число b1 dq 3 ; основание модуля buf1 dq 0,0 ; буфер .code entry_point proc xor rdx,rdx mov rax,a1 div b1 mov r10,rdx : сохранение rdx перед функцией с параметрами invoke wsprintf,addr buf1, "mod3 = %d",r10 invoke MessageBox,0,addr buf1,chr$(" Операция mod3 "), 0 invoke mov r10,rdx : ExitProcess,0 entry_point endp end Після операції розподілу залишок залишається у регістрі rdx. Але в наступному рядку після операції розподілу виконується функція wsprintf, параметри якої передаються через регістри rcx, rdx, r8, r9. У регістр rdx запише значення "mod3 = %d". Тому rdx зберігається в r10: mov r10,rdx 9.3. Шифрування даних із інкрементованим ключем Розглянемо шифрування даних, які вводяться у консольне вікно та шифруються за допомогою ключа. Причому початкове значення ключа дорівнює 1, а наступні значення виходять шляхом збільшення (інкрементації) на 1 попереднього значення. Програма 9.3. Шифрування mod2 з інкрементуванням ключа: ; поразрядное mod2: txt mod2 key include \masm64\include64\masm64rt.inc ; 126 .data rez db 128 DUP (0) buff db 128 DUP (0) txt db "Введите код для преобразования",10,0 TXTSIZE equ $ - txt ; число символов, выведенных на экран txt2 db "txt mod2 key:",10,0 stdin1 dq 0 ; для дескриптора ввода stdout1 dq 0 ; для дескриптора вывода wrn dq 0 ; число записанных символов rdn dq 0 ; число прочитанных символов key db 1; ключ для сложения по mod2 .code entry_point proc invoke GetStdHandle,STD_INPUT_HANDLE ;извлекает дескриптор mov stdin1,rax invoke GetStdHandle,STD_OUTPUT_HANDLE ;извлекает дескриптор mov stdout1,rax invoke SetConsoleCP,1251;уст. кодовой страницы win 1251 в поток ввода invoke SetConsoleOutputCP,1251;уст. код. стр. win 1251 в поток вывода invoke WriteConsoleA,stdout1,addr txt,TXTSIZE,addr wrn,0 invoke WriteConsoleA,stdout1,addr txt2,14,addr wrn,0 invoke ReadConsole,stdin1,addr buff,128,addr rdn,0 ; ReadConsole записала по адр. rdn +2 символа lea rsi, buff ; загрузка адреса буфера lea rdi, rez ; загрузка адреса ячейки результата mov rcx, rdn ; запись символов sub rcx, 2 ; минус служебные символы _cycle: mov al, byte ptr [rsi]; xor al, key ; mov byte ptr [rdi],al ; inc rsi ; inc rdi ; inc key ; изменение ключа на +1 127 loop _cycle invoke WriteConsoleA,stdout1,addr rez,128,addr wrn,0 invoke ReadConsole,stdin1,addr buff,130,addr rdn,0 invoke ExitProcess,0 entry_point endp end Кількість символів, які можна закодувати в консольному вікні програми, дорівнює 128. Хоча цю кількість можна змінити на досить велике значення. Як ключ для шифрування наступного символу можна використовувати певну задану послідовність дій над попереднім значенням, так і розрахунок наступного значення за формулою. 9.4. Лабораторна робота «Зворотне шифрування» Мета заняття: набути практичних навичок застосування операцій зворотного шифрування в програмі. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – представити програми: без діалогового вікна та проект з діалоговим вікном; – у тексті програми організувати виведення назви лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання Застосувати парольний вхід до програми та над паролем зробити перетворення відповідно до варіанта. Написати програму шифрування файлу (файлів). У програмі еталонний пароль заховати у секції коду. 128 Варіанти У варіантах завдання застосовується поняття інкрементованого ключа – це ключ, у якому кожне наступне значення ключа збільшується на 1 від попереднього. Декрементований ключ – наступне значення ключа зменшується на 1 від попереднього. 1. Циклічний зсув ліворуч на 1 розряд і додавання по mod2 з інкрементованим ключем; 2. Циклічний зсув ліворуч на 1 розряду та додавання по mod3 з інкрементованим ключем; 3. Циклічний зсув праворуч на 1 розряд і додавання по mod2 з інкрементованим ключем; 4. Циклічний зсув праворуч на 2 розряди і додавання по mod3 з інкрементованим ключем; 5. Віднімання інкрементованого ключа та зсув ліворуч на 1 розряд; 6. Віднімання декрементованого ключа та додавання по mod3; 7. Віднімання інкрементованого ключа та зсув праворуч на 1 розряд; 8. Віднімання декрементованого ключа та зсув праворуч на 1 розряд; 9. Додавання інкрементованого ключа та додавання по mod3; 10. Додавання декрементованого ключа і зсув ліворуч на 1 розряд; 11. Додавання інкрементованого ключа та зсув праворуч на 1 розряд; 12. Додавання декрементованого ключа та зсув праворуч на 1 розряд; 13. Додавання по mod2 з ключем, додавання по mod3 з ключем; 14. Додавання по mod2 з ключем, зрушення вліво на 2 розряди, додавання по mod3 з ключем; 15. Додавання по mod2 з ключем, зсув праворуч на 1 розряду, додавання по mod3 з ключем; 16. Додавання по mod2 з ключем, зсув праворуч на 2 розряди, додавання по mod2 з ключем; 17. Циклічний зсув ліворуч на 2 розряди, додавання по mod2 з інкрементованим ключем, зсув праворуч на 1 розряд; 18. Циклічний зсув вправо на 2 розряди, додавання по mod2 з інкрементованим ключем, зрушення вліво на 1 розряд; 19. Циклічний зсув вправо на 3 розряди, додавання по mod2 з інкрементованим ключем, зрушення вліво на 2 розряди; 20*-25*. Власна послідовність обробки пароля, що не зустрічається в інших випадках завдання. 129 Контрольні питання для перевірки знань 1. Які операції є зворотними? 2. Що таке хешування та де застосовується? 3. Для яких цілей застосовується парольний захист програми? 4. У яких випадках пароль видно у ехе-файлі? 5. Навіщо у програмі застосовується кодова сторінка win 1251? 130 10. Кодування 10.1. Загальні відомості про кодування Кодування входить у загальне поняття шифрування і часто-густо ототожнюються друг з одним. Під час кодування повідомлень дані шифруються за допомогою криптографічних ключів. Залежно від природи ключів шифрування може ділитися на 2 категорії: симетричне та асиметричне. Симетричне шифрування: дані шифруються та розшифровуються за допомогою одного криптографічного ключа. Це означає, що ключ, який використовується для шифрування, використовується для розшифровки. Асиметричне шифрування: використовуються два різні ключі - один для шифрування, другий для розшифрування. Один ключ називається публічним, другий таємним. 10.2. Отримання випадкової послідовності Послідовності випадкових чисел є невід'ємним інструментом вирішення багатьох математичних завдань. Більшість природних процесів є випадковими, і, згідно з теорією математичної статистики, вони підкоряються різним законам розподілу випадкових величин. Отримання випадкових чисел без дотримання законів розподілу та критеріїв відбору не є складним завданням для програміста. При передачі закодованого довгого повідомлення на приймальну сторону необхідно передавати файл ключа, що не завжди доцільно. Однак і такий спосіб кодування те саме може застосовуватися. Розглянемо одержання випадкової послідовності (програма 10.1). Основою формування випадкової послідовності є значення, що повертається функцією GetTickCount, над якою згодом проводяться певні програмістом дії. Ці дії можуть бути будь-якими, як і функції, які можуть застосовуватись. Але бажано вибирати такі функції, у яких значення, що повертаються, весь час різні. 131 Програма 10.1. Отримання випадкової послідовності як ключа шифрування на основі використання функції GetTickCount: : include \masm64\include64\masm64rt.inc .data fileName db "keyfile.bin", 0 ; Имя файла для сохранения ключа keyBuffer db 1024 dup(0) ; Буфер для хранения сгенерированного ключа fileHandle dq 0 ; Дескриптор файла bytesWritten dq 0 ; Количество записанных байт .code entry_point proc ; Получить текущее значение системного таймера для использования в качестве начального значения sub rsp, 8 call GetTickCount mov eax, eax ; Извлекаем 32 бита из rax mov dword ptr [keyBuffer], eax ; Записываем это значение в первые 4 байта keyBuffer add rsp, 8 ; Настройка регистров для генерации ключа mov rdi, offset keyBuffer ; Указатель на текущий байт в keyBuffer mov rcx, 1024 ; Счетчик для 1024 байт lea rax, [keyBuffer] ; Загрузка начального значения ключа в rax mov rax, [rax] generate_key: ; Простая генерация случайного ключа xor rax, rcx ; Используем XOR для изменения rax rol rax, cl ; Циклический сдвиг rax на количество бит, указанное в cl mov byte ptr [rdi], al ; Сохраняем младший байт rax в keyBuffer inc rdi ; Переход к следующему байту в keyBuffer dec rcx ; Уменьшение счетчика jnz generate_key ; Продолжить, пока счетчик не станет равным 0 ; Открыть файл для записи сгенерированного ключа invoke CreateFile, addr fileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL mov fileHandle, rax ; Сохраняем дескриптор файла ; Проверка на успешное открытие файла test rax, rax 132 jz exit ; Если файл не открылся, выходим ; Запись ключа в файл invoke WriteFile, fileHandle, addr keyBuffer, 1024, addr bytesWritten, NULL ; Закрыть файл invoke CloseHandle, fileHandle exit: invoke ExitProcess, 0 entry_point endp end В результаті виконання цього ехе-файлу створюється файл з ім'ям keyfile.bin, в якому записана послідовність довжиною 1024 біта. В різні моменти часу вміст файлу буде різним. Випадковий вміст отриманого файлу наведено на рис. 10.1. . Але для розшифровки треба застосовувати той самий файл із ключем з ім'ям keyfile.bin. Шифрування файлу Шифрування файлів у директорії можна виконати за допомогою програми 10.2. Але для шифрування необхідно застосувати ключ, створений програмою 10.1 та має ім'я keyfile.bin. Після шифрування розширення файлу змінюється на ім'я cringe. Рис. 10.1. Випадковий вміст файлу 133 Програма 10.2. Шифрування файлів у директорії ключем із файлу з ім'ям keyfile.bin: include \masm64\include64\masm64rt.inc .data directoryName db "C:\masm64\bin64\test\*", 0 ; Путь к директории для шифрования файлов keyFileName db "keyfile.bin", 0 ; Имя файла с ключом шифрования findData WIN32_FIND_DATA { } ; Структура для хранения информации о файлах в директории ; Буферы и переменные для работы с файлами и ключами keyBuffer db 1024 dup(0) ; Буфер для хранения ключа шифрования buffer db 10240 dup(0) ; Буфер для чтения/записи данных файла fullPathFile db 1024 dup(0) ; Полный путь к файлу для шифрования inputFileNameEnc db 1024 dup(0) ; Имя файла с новым расширением после шифрования newExtension db '.cringe',0 ; Новое расширение для зашифрованных файлов negBytesRead dq 0 ; Переменная для управления смещением в файле .code entry_point proc ; Считать ключ для шифрования invoke OpenKeyFile test rax, rax jz exit ; Шифруем содержимое указанной директории invoke EncryptDirectory, addr directoryName exit: invoke ExitProcess, 0 entry_point endp ; Функция для шифрования содержимого директории EncryptDirectory proc directoryPath:QWORD local hFind:QWORD 134 ; Начинаем поиск файлов в директории lea rdx, [findData] invoke FindFirstFile, directoryPath, rdx mov hFind, rax cmp rax, INVALID_HANDLE_VALUE je end_search ; Завершение, если файлы для поиска отсутствуют search_loop: ; Шифрование каждого найденного файла lea rcx, [findData.cFileName] invoke EncryptFile, rcx, addr keyBuffer lea rdx, [findData] ; Это нужно для обновления адреса в rdx invoke FindNextFile, hFind, rdx cmp rax, 0 jne search_loop ; Повторение поиска, пока есть файлы invoke FindClose, hFind ; Закрытие поиска файлов end_search: ret EncryptDirectory endp ; Функция для конкатенации пути и имени файла ConcatenatePath proc ; rcx - адрес directoryName, rdx - адрес fileName lea rsi, [rcx] ; Указатель на путь к директории lea rdi, [fullPathFile] ; Указатель на полный путь ; Копирование пути к директории в fullPath copy_directory: lodsb ; Загружаем байт из rsi в al cmp al, '*' ; Проверяем, не является ли это звёздочкой je copy_file ; Если да, переходим к копированию имени файла stosb ; Сохраняем байт из al в rdi test al, al ; Проверяем, не достигли ли мы конца строки jnz copy_directory ; Если нет, продолжаем копирование copy_file: lea rsi, [rdx] ; Указатель на имя файла ; Копирование имени файла в fullPath repeat_file: lodsb ; Загружаем байт из rsi в al stosb ; Сохраняем байт из al в rdi 135 test al, al ; Проверяем, не достигли ли мы конца строки jnz repeat_file ; Если нет, продолжаем копирование ret ConcatenatePath endp ; Функция для шифрования отдельного файла EncryptFile proc filePath:QWORD, keyBuffer1:QWORD local hFile:QWORD local bytesRead:QWORD local bytesWritten:DWORD ; Подготовка пути и имени файла для шифрования lea rcx, [directoryName] ; Путь к директории mov rdx, filePath ; Имя файла call ConcatenatePath ; Изменение расширения файла lea rsi, [fullPathFile] ; lea rdi, [inputFileNameEnc] call ChangeExtension ; Открыть файл для чтения и записи invoke CreateFile, addr fullPathFile, GENERIC_READ OR GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL mov hFile, rax cmp rax, INVALID_HANDLE_VALUE je encrypt_exit ; Чтение, шифрование и запись данных файла read_loop: invoke ReadFile, hFile, addr buffer, 1024, addr bytesRead, NULL test rax, rax jz exit lea rsi, [buffer] lea rdx, [keyBuffer] mov rcx, bytesRead xor rdi, rdi ; Цикл шифрования encrypt_loop: mov al, byte ptr [rsi + rdi] 136 xor al, byte ptr [rdx + rdi] mov [rsi + rdi], al inc rdi dec rcx jnz encrypt_loop ; Перемещение указателя файла и запись зашифрованных данных mov rax, bytesRead neg rax mov negBytesRead, rax invoke SetFilePointer, hFile, negBytesRead, NULL, FILE_CURRENT invoke WriteFile, hFile, addr buffer, bytesRead, addr bytesWritten, 0 cmp bytesRead, 1024 je read_loop ; Повторение чтения, если не достигнут конец файла exit: ; Закрытие файла и изменение его имени invoke CloseHandle, hFile invoke MoveFile, addr fullPathFile, addr inputFileNameEnc encrypt_exit: ret EncryptFile endp ; Функция для открытия файла ключа и чтения данных OpenKeyFile proc local hFile:QWORD local bytesRead:QWORD ; Открыть файл ключа для чтения invoke CreateFile, addr keyFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL mov hFile, rax cmp rax, INVALID_HANDLE_VALUE je openkeyfile_exit ; Читаем содержимое файла ключа в keyBuffer invoke ReadFile, hFile, addr keyBuffer, 1024, addr bytesRead, NULL ; Закрыть файл ключа invoke CloseHandle, hFile openkeyfile_exit: 137 ret OpenKeyFile endp ; Функция для замены расширения файла ChangeExtension proc ; Копируем имя файла до точки (если точка есть) copy_name: lodsb cmp al, '.' je add_new_extension ; Если найдена точка, добавляем новое расширение stosb test al, al jnz copy_name ; Продолжение копирования, если не достигнут конец строки ; Добавляем расширение add_new_extension: lea rsi, [newExtension] movsd movsd movsd ; Копирование расширения в имя файла ret ChangeExtension endp end Наведена програма шифрує всі файли у зазначеній директорії directoryName db "C:\masm64\bin64\test\*", 0 Дешифрація файлу Розглянемо програму 10.3, яка дешифрує лише один файл, а чи не всю директорію. Приклад використання директорії показано у програмі шифрування. Програма 10.3 використовує той самий ключ, що й для шифрування і ту саму операцію XOR, що дозволяє відновити оригінальний вміст файлу. Програма 10.3. Дешифрування файлу: include \masm64\include64\masm64rt.inc .data inputFileName db "C:\masm64\bin64\test\01.cringe", 0 ; Имя зашифрованного файла для дешифровки 138 inputFileNameDec db 1024 dup(0) ; Буфер для имени файла после удаления расширения keyFileName db "keyfile.bin", 0 ; Имя файла, содержащего ключ шифрования ; Буферы для ключа и данных keyBuffer db 1024 dup(0) ; Буфер для хранения ключа дешифрования buffer db 10240 dup(0) ; Буфер для хранения данных файла ; Переменные для работы с файлом negBytesRead dq 0 ; Переменная для перемещения указателя файла inputFileHandle dq 0 ; Дескриптор файла, который нужно дешифровать keyFileHandle dq 0 ; Дескриптор файла ключа bytesRead dq 0 ; Количество прочитанных байтов из файла bytesReadKey dq 0 ; Кол. прочитанных байтов из файла ключа bytesWritten dq 0 ; Количество записанных байтов в файл .code entry_point proc ; Подготовка имени файла без расширения xor rsi, rsi xor rdi, rdi lea rsi, [inputFileName] ; Загружаем адрес зашифрованного файла lea rdi, [inputFileNameDec] ; Загружаем адрес буфера для имени файла без расширения call RemoveExtension ; Удаляем расширение из имени файла ; Открытие файла ключа для чтения invoke CreateFile, addr keyFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL mov keyFileHandle, rax test rax, rax jz exit ; Выходим, если файл ключа не может быть открыт ; Чтение ключа из файла ключа invoke ReadFile, keyFileHandle, addr keyBuffer, 1024, addr bytesReadKey,0 139 ; Открытие зашифрованного файла для чтения и записи invoke CreateFile, addr inputFileName, GENERIC_READ OR GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL mov inputFileHandle, rax test rax, rax jz exit ; Выходим, если зашифров. файл не может быть открыт ; Чтение и дешифрование файла read_loop: invoke ReadFile, inputFileHandle, addr buffer, 1024, addr bytesRead,0 test rax, rax jz exit ; Выходим, если достигнут конец файла ; Цикл дешифрования lea rsi, [buffer] lea rdx, [keyBuffer] mov rcx, bytesRead xor rdi, rdi decrypt_loop: mov al, byte ptr [rsi + rdi] xor al, byte ptr [rdx + rdi] mov [rsi + rdi], al inc rdi dec rcx jnz decrypt_loop ; Перемещение указателя файла назад и запись дешифрованных данных mov rax, bytesRead neg rax mov negBytesRead, rax invoke SetFilePointer, inputFileHandle, negBytesRead,0, FILE_CURRENT invoke WriteFile,inputFileHandle,addr buffer, bytesRead,addr bytesWritten, 0 cmp bytesRead, 1024 je read_loop ; Читаем следующий блок, если не достигнут конец файла exit: ; Закрытие дескрипторов файлов и переименование зашифрованного файла invoke CloseHandle, inputFileHandle 140 invoke CloseHandle, keyFileHandle ; Переименовываем файл после дешифрования invoke MoveFile, addr inputFileName, addr inputFileNameDec invoke ExitProcess, 0 entry_point endp ; Процедура для удаления расширения из имени файла RemoveExtension proc copy_name: lodsb ; Загружаем один байт из имени файла cmp al, '.' je finish_copy ; Если найдена точка, завершаем копирование stosb ; Сохраняем байт в новое имя файла test al, al jnz copy_name ; Продолжаем, если не достигнут конец строки finish_copy: mov byte ptr [rdi], 0 ; Добавляем нулевой байт в конец строки ret RemoveExtension endp end 10.3. Алгоритм отримання псевдовипадкової послідовності Найправильнішим з погляду математичного опису способом отримання псевдовипадкової послідовності є спосіб, заснований на застосуванні поліном P(x). І тут застосуємо весь математичний апарат алгебри, дискретної математики та інших галузей математики. Це дозволяє математично описувати такі послідовності, виконувати розрахунки характеристик поліномів та за необхідними критеріями вибирати оптимальні, проводити математичне моделювання поведінки таких послідовностей. Поліном P(x) (він же многочлен), який застосовується при отриманні псевдовипадкової послідовності має вигляд: P(x)=xn+an−1xn−1 + an−2xn−2 + … + a1x + a0x0 де знак + - це операція mod2, n – ступінь полінома, ai – коефіцієнти при відповідних ступенях, a0 = 1. Операція mod2 застосовується у тому випадку, якщо обробляються 2 стани: 0 та 1. 141 На підставі полінома P(x) будується цифрова схема, основою якої є зсувний регістр, а зворотні зв'язки на вхід першого тригера через суматор модуля подаються на основі ступенів обраного полінома. Наприклад, для полінома Р(х) = х16  х12  х9  х7  1 цифрова схема матиме вигляд, як представленоцифрова схема матиме вигляд, як представлено на рис. 10.2. Усі показані 4-і суматори по модулю два об'єднуються в один багатовхідний суматор з mod2. Для того щоб така схема генерувала цифрову послідовність необхідно записати 1 в молодший тригер регістра і за допомогою синхросигналів зрушувати цю послідовність. На вхід першого тригера через суматор mod2 подаються стану тих тригерів, номери яких рівні показниками ступенів полінома. Природно, що перша послідовність h1 матиме вигляд: 1000000000000000. Друга послідовність h2 = 0100 0000 0000 0000 (зі зрушенням на 1 розряд у старший бік). Третя послідовність h3 = 0010 0000 0000 0000 і т.д. Таким чином формується матриця вихідних станів: H = h1, h2, h3, h4…hn  Для простоти викладу розглянемо поліном менших ступенів, наприклад Р(Х) = х4  х3  1, схема з'єднань якого наведена на рис. 10.3. Рис. 10.2. Цифровая схема генератора псевдослучайной последовательности с Р(х) = х16  х12  х9  х7  1 142 Матриця вихідних станів Р(Х) = х4  х3  1 має вигляд: 1 0 0 1 1 0 1 0 1 1 1 1 0 0 0 20 0 1 0 0 1 1 0 1 0 1 1 1 1 0 0 21 0 0 1 0 0 1 1 0 1 0 1 1 1 1 0 22 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 23 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Верхній рядок – це стан першого тригера. Другий рядок – це стан другого тригера. І т.д. Тобто, поліном 4-го ступеня має 4 тригери, а, отже, і 4 рядки вихідних двійкових станів матриці. Кожен стовпець – це псевдовипадкове число. Наприклад, другий стовпець – це h2 = 2. Третій стовпець h3 = 4. Чотвертий стовпець h4 = 9. П'ятий стовпець h5 = 3. Алгоритм отримання псевдовипадкової послідовності може бути таким: 1. Вибирається поліном. Він повинен мати максимальний період генерації, Tmax = 2n -1. Мінус 1 означає, що в послідовності немає стовпця з нульовими станами, інакше регістр почне зрушувати нулі і перестане генерувати псевдовипадкову послідовність. 2. За показниками ступенів обраного полінома будується схема генератора із зворотними зв'язками. H = D ТT C 1 D ТT C 2 C D ТT C 3 D ТT C 4 M2 Рис. 10.3. Схема генератора псевдовипадкової послідовності с Р(Х) = х4  х3  1 143 3. У перший тригер регістру зсуву записується 1 (якщо застосовується mod2). Практично – цей стан дорівнює a0 полінома. Перший стан регістру h1 = 1000. Кількість розрядів hі дорівнює максимальному ступеню полінома. 4. Розраховуємо наступний стовпець. Зробимо це спочатку з прикладу другого стовпець. Для цього в клітинки другого стовпця робиться косий зсув з першого, а в перший тригер другого стовпця записується сума по mod2 станів з першого (попереднього) стовпця, номери яких рівні ступеням обраного полінома. Наприклад, для Р(Х) = х4  х3  1: Тг1 1  Тг2 0 1 Тг3 0 0 Тг4 0 0 h1 h2 На другий стовпець в Тг1 записується стан Тг1  Тг2 першого стовпця. Тобто, необхідно записати 0. Третій стовпець: Тг1 1 0  Тг2 0 1 0 Тг3 0 0 1 Тг4 0 0 0 h1 h2 h3 Тобто, необхідно записати 0. Четвертий стовпець для Р(Х) = х4  х3  1: Тг1 1 0 0 1 Тг2 0 1 0 0 Тг3 0 0 1 0 Тг4 0 0 0 1 h1 h2 h3 h4 Таким чином формуються всі стовпці матриці псевдовипадкової послідовності. 5. Після отримання кожного нового стовпця робиться порівняння з першим стовпцем. Якщо стовпці збігаються, то всі попередні стовпці 144 сформували псевдовипадковий цикл. Розрахунок наступних циклів потрібно припинити, т.к. значення всіх циклів одного полінома однакові. А якщо псевдовипадкова послідовність використовується, наприклад, при тестуванні і не перший цикл генерації, то повністю порушується математичне обгрунтування пошуку розрядів, якими з'явився збій. 10.4. Поліноміальне кодування Для поліномінального кодування необхідні: програма, що генерує псевдовипадкову послідовність; програма, яка використовує цю послідовність як ключ. Символи кодової таблиці кодуються байтами. Отже, і ключ повинен мати розмірність байта. А це 8 розрядів. Тому можна використовувати псевдовипадкові числа як ключ, отримані поліномами до 8 ступеня включно (28 = 256 ключів). Можна застосовувати поліноми і менших ступенів, але в цьому випадку необхідно використовувати цикли, що повторюються. Максимальний рівень полінома записується як degP(x). Наприклад, якщо використовувати degP(x) = 4, то максимальний період генерації Tmax = 24 – 1 = 15. Цей цикл дуже короткий і легко розпізнаний. Практичне використовувати Р(х) з degP(x) = 8 і надалі застосовувати кілька циклів для збільшення послідовності. Наприклад, вірус шифрувальник-вимагач, який додає своє розширення ahtw, кодує 1024 символи. Вірус не змінює (чомусь) перші 5 байтів і дописує до кінця файлу блоки однакового розміру 334 = 14Eh байтів, у яких зберігається так званий маркет (інформація для розшифровки на сервері). Ця шкідлива програма використовує "icacls" - командну утиліту Windows для захисту цього шляху/папки, де розташований сам вірус. Шкідлива програма використовує API- інтерфейси ShellExecute з ім'ям runas, щоб спробувати знову запустити себе з правами адміністратора. Для отримання псевдовипадкової послідовності розглянемо програму 10.4. У ній розраховуються всім Р(х) з degP(x) = 4 двійкові псевдовипадкові числа. Для отримання десяткових чи 16-річних чисел програму необхідно модифікувати. Програма 10.4. Отримання псевдовипадкових двійкових чисел всім Р(х) з degP(x) = 4 полі GF(2): 145 include /masm64/include64/masm64rt.inc BSIZE equ 32 ; кол. байтов, которые записываются в файл: 15+пробел .data Tick1 dq 0 ; ячейка для хранения начального времени Tick2 dq 0 ; ячейка для хранения полученного времени Tick11 DQ 0 bufT dq 0,0 ; буфер ifmtT db "Количество тиков = %d ",10,10, "Количество тиков находятся с помощью",10, "API-функции QueryPerformanceCounter",10,10, "Автор: Рысованый А.Н., https://blogs.kpi.kharkov.ua/v2/asm/ob-avtore/",0 TitlT db "Время выполнения расчетов ",0 szTitle1 db "Время выполнения программы вычисления P(x)=4 c mod2",0 fmt db "%d ",0 ; преобразование символа szBuf1 db BSIZE dup(0),0 ; szBuf2 db BSIZE dup(0),0 ; szBuf3 db BSIZE dup(0),0 ; szBuf4 db BSIZE dup(0),0 ; crtf db 10 ; перенос на следующую строку fName BYTE "Px4m2-64.txt",0 ; имя файла fHandle DQ ? ; дескриптор cWritten DQ ? ; для адреса символов вывода a1 dq 0 ; коэффициент при х1 a2 dq 0 ; коэффициент при х2 a3 dq 0 ; коэффициент при х3 a4 dq 1 ; коэффициент при х4 _h1_1 dq 1 ; начальное значение 1-й яч. 1-го столбца _h1_2 dq 0 ; начальное значение 2-й яч. 1-го столбца _h1_3 dq 0 ; начальное значение 3-й яч. 1-го столбца _h1_4 dq 0 ; начальное значение 4-й яч. 1-го столбца _m1 dq ? ; рабочее значение 1-й ячейки _m2 dq ? ; рабочее значение 2-й ячейки _m3 dq ? ; рабочее значение 3-й ячейки _m4 dq ? ; рабочее значение 4-й ячейки _m1_1 dq ? ; текущее значение 1-й ячейки _m2_1 dq ? ; текущее значение 2-й ячейки 146 _m3_1 dq ? ; текущее значение 3-й ячейки _m4_1 dq ? ; текущее значение 4-й ячейки mmm1 dq 0 ; счетчик столбцов mmm2 dq 15 ; счетчик макс. генерации - от зацикливания при наладке Hello db "Файл с результатами расчета матриц состояний создан",0 szSt1 db "%dx4+%dx3+%dx2+%dx1+1 ",0 ;!!! szBufS1 db "%dx4+%dx3+%dx2+%dx1+1",0 szM db " %d ",0 ;!!! meterLen dq 0 ; счетчик цикла !!! .code procPx4m2 proc; процедура ; создание файла invoke CreateFile,ADDR fName,GENERIC_WRITE,0,0, CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,0 mov fHandle,rax ; дескриптор m1: inc meterLen ;!!! mov rdi,0 ; запись в буфер значения полинома invoke wsprintf,ADDR szBufS1, ADDR szSt1,a4,a3,a2,a1 ; запись в буфер первого столбца матрицы состояний invoke wsprintf,ADDR szBuf1[rdi],ADDR fmt,_h1_1 invoke wsprintf,ADDR szBuf2[rdi],ADDR fmt,_h1_2 invoke wsprintf,ADDR szBuf3[rdi],ADDR fmt,_h1_3 invoke wsprintf,ADDR szBuf4[rdi],ADDR fmt,_h1_4 ; сохранение первого столбца mrmq _m1,_h1_1 ; 1 mrmq _m2,_h1_2 ; 0 mrmq _m3,_h1_3 ; 0 mrmq _m4,_h1_4 ; 0 ; перезапись первого столбца для вычислений следующих столбцов mov r11,_h1_1 ; mov rbx,_h1_2 ; mov rcx,_h1_3 ; mov rdx,_h1_4 ; 147 mBegin: dec mmm2 ; уменьшение счетчика кол. генерации - для останова cmp mmm2,0 ; jz m15 ; and r11,a1 ; выделение состояния 1-й ячейки and rbx,a2 ; выделение состояния 2-й ячейки and rcx,a3 ; выделение состояния 3-й ячейки and rdx,a4 ; выделение состояния 4-й ячейки ; расчет следующего состояния 1-й ячейки xor r11,rbx ; xor rcx,rdx ; xor r11,rcx ; получение следующего состояния 1-й ячейки mov r10,r11 ; сохранение следующего состояния 1-й ячейки mrmq _m4_1,_m3 mrmq _m3_1,_m2 mrmq _m2_1,_m1 mov _m1_1,r10 mov r11,_m1_1 ; mov rbx,_m2_1 ; mov rcx,_m3_1 ; mov rdx,_m4_1 ; mov mmm1,0 ; счетчик совпадения с 1-м столбцом ; сравнение полученного столбца с первым столбцом cmp r11,qword ptr _h1_1 jnz m11 inc mmm1 m11: cmp rbx,qword ptr _h1_2 jnz m12 inc mmm1 m12: cmp rcx,qword ptr _h1_3 jnz m13 inc mmm1 m13: cmp rdx,qword ptr _h1_4 jnz m14 inc mmm1 m14: cmp qword ptr mmm1,4 jz m15 inc rdi ; вставка пробела перед след. столбцом inc rdi ; сдвиг на следующую позицию вывода inc meterLen ;!!! 148 ; сохранение в буфер текущего столбца invoke wsprintf,ADDR szBuf1[rdi],ADDR fmt,_m1_1 invoke wsprintf,ADDR szBuf2[rdi],ADDR fmt,_m2_1 invoke wsprintf,ADDR szBuf3[rdi],ADDR fmt,_m3_1 invoke wsprintf,ADDR szBuf4[rdi],ADDR fmt,_m4_1 invoke wsprintf,ADDR szM,ADDR fmt,meterLen ;!!! mov r11,_m1_1 ; 1-е значение текущего столбца mov rbx,_m2_1 ; 2-е значение текущего столбца mov rcx,_m3_1 ; 3-е значение текущего столбца mov rdx,_m4_1 ; 4-е значение текущего столбца ; подготовка к вычислению следующего столбца mrmq _m1,_m1_1 mrmq _m2,_m2_1 mrmq _m3,_m3_1 mrmq _m4,_m4_1 jmp mBegin m15: invoke WriteFile,fHandle,ADDR szBufS1,BSIZE,ADDR cWritten,0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile,fHandle,ADDR szBuf1,BSIZE,ADDR cWritten,0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile, fHandle, ADDR szBuf2, BSIZE, ADDR cWritten, 0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile, fHandle, ADDR szBuf3, BSIZE, ADDR cWritten, 0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile, fHandle, ADDR szBuf4, BSIZE, ADDR cWritten, 0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 ; очистка буфера ;invoke MemSet, ADDR szBuf1,'0',16 ;invoke ZeroMemory,ADDR szBuf2,32 ;invoke ZeroMemory,ADDR szBuf3,32 ;invoke FlushFileBuffers, szBuf4 mov meterLen,0 ;!!! mov rcx,BSIZE mov rsi,0 149 mov r11b,0 ; ;;; @@: mov szBuf1[rsi],r11b inc rsi loop @b mov rcx,BSIZE mov rsi,0 mov r11b,0; @2: mov szBuf2[rsi],r11b inc rsi loop @2 mov rcx,BSIZE mov rsi,0 mov r11b,0; @3: mov szBuf3[rsi],r11b inc rsi loop @3 mov ecx,BSIZE mov rsi,0 mov r11b,0 ; @4: mov szBuf4[rsi],r11b inc rsi loop @4 ; изменение коэффициентов полинома xor a1,1 cmp a1,1 jz m1 xor a2,1 cmp a2,1 jz m1 xor a3,1 cmp a3,1 jz m1 invoke CloseHandle,fHandle ; закрытие дескриптора файла ret procPx4m2 endp 150 entry_point proc invoke QueryPerformanceCounter,ADDR Tick1; извлечение тиков invoke procPx4m2 invoke QueryPerformanceCounter,ADDR Tick2; извлечение тиков finit fild dword ptr Tick2; загр. последних значений тиков fild dword ptr Tick1; загр. начальных значений тиков fsubp st(1),st ; расчет разницы тиков fistp Tick11 ; сохр. целой части результата тиков invoke wsprintf,ADDR bufT,ADDR ifmtT,Tick11 invoke MessageBox,0,ADDR bufT,ADDR szTitle1, MB_ICONINFORMATION invoke ExitProcess, 0 entry_point endp end Програма створює файл, у якому записує коефіцієнти полінома і праворуч від них – довжину циклу кожного Р(х) (рис. 10.4). Довжина циклу дорівнює кількості стовпців матриці станів Р(х). Значення лічильника циклу кожного Р(х) записується в змінну meterLen. Після кожного формування стовпця значення цього лічильника збільшується на 1: inc meterLen Кожен новий стовпець починаючи з другого порівнюється з першим. І якщо для degp(x)=4 сталося 4 збіги, це свідчить у тому, що почався формуватися новий цикл генерації. Після формування циклу генерування лічильник примусово обнулюється командоюmov meterLen,0 151 1x4+0x3+0x2+0x1+1 4 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1x4+0x3+0x2+1x1+1 15 1 1 1 1 0 1 0 1 1 0 0 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 0 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 0 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 0 1 1x4+0x3+1x2+0x1+1 6 1 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 1 1x4+0x3+1x2+1x1+1 7 1 1 0 1 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 1 1x4+1x3+0x2+0x1+1 15 1 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 1x4+1x3+0x2+1x1+1 6 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 1x4+1x3+1x2+0x1+1 7 1 0 1 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 1 1 1x4+1x3+1x2+1x1+1 5 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 Рис. 10.4. Вміст вихідного файлу 152 Після завершення розрахунків виводиться повідомлення з кількістю тиків, яке витрачено на обчислення (рис. 10.5). Кожен стовпець матриці станів є псевдовипадковим числом. Як приклад невеликої модифікації попередньої програми розглянемо процес отримання всіх Р(х) з degP(x) =5, їх вихідних матриць (матриць станів) та періоду генерування станів кожного з них. Програма 10.5. Програма отримання всіх Р(х) з degP(x) =5, їх вихідних матриць (матриць станів) та періоду генерування станів кожного з них: include /masm64/include64/masm64rt.inc BSIZE equ 80 ; кол. байтов, которые записываются в файл: 32+пробелы .data Tick1 dq 0 ; ячейка для хранения начального времени Tick2 dq 0 ; ячейка для хранения полученного времени Tick11 dq 0 bufT dq 0,0 ; буфер ifmtT db "Kоличество тиков = %d ",10,10, "Kоличество тиков находятся с помощью",10, "API-функции QueryPerformanceCounter",10,10, "Aвтор: Pысованый A.H., https://blogs.kpi.kharkov.ua/v2/asm/ob-avtore/",0 Рис. 10.5. Повідомлення з кількістю тиків на виконання розрахунків 153 TitlT db "Время выполнения расчетов ",0 szTitle1 db "Время выполнения программы вычисления P(x)=4 c mod2",0 fmt db "%d ",0 ; преобразование символа szBuf1 db BSIZE dup(0),0 ; szBuf2 db BSIZE dup(0),0 ; szBuf3 db BSIZE dup(0),0 ; szBuf4 db BSIZE dup(0),0 ; szBuf5 db BSIZE dup(0),0 ; crtf db 10 ; перенос на следующую строку fName BYTE "Px5m2-64.txt",0 ; имя файла fHandle DQ ? ; дескриптор cWritten DQ ? ; для адреса символов вывода a1 dq 0 ; коэффициент при х1 a2 dq 0 ; коэффициент при х2 a3 dq 0 ; коэффициент при х3 a4 dq 0 ; коэффициент при х4 a5 dq 1 ; коэффициент при х5 _h1_1 dq 1 ; начальное значение 1-й яч. 1-го столбца _h1_2 dq 0 ; начальное значение 2-й яч. 1-го столбца _h1_3 dq 0 ; начальное значение 3-й яч. 1-го столбца _h1_4 dq 0 ; начальное значение 4-й яч. 1-го столбца _h1_5 dq 0 ; нач. знач. 5-й яч. 1-го столбца _m1 dq ? ; рабочее значение 1-й ячейки _m2 dq ? ; рабочее значение 2-й ячейки _m3 dq ? ; рабочее значение 3-й ячейки _m4 dq ? ; рабочее значение 4-й ячейки _m5 dq ? ; рабочее знач. 5-й яч. _m1_1 dq ? ; текущее значение 1-й ячейки _m2_1 dq ? ; текущее значение 2-й ячейки _m3_1 dq ? ; текущее значение 3-й ячейки _m4_1 dq ? ; текущее значение 4-й ячейки _m5_1 dq ? ; текущее значение 5-й ячейки mmm1 dq 0 ; счетчик совпадений с 1-м столбцом mmm2 dq 128 ; счетчик макс. генерации - от зацикливания при наладке Hello db "Файл с результатами расчета матриц состояний создан",0 154 szSt1 db "%d%d%d%d%d1",0 ;!!! вывод полинома,x0=1 szBufS1 db " %d ",0; значение периода szM db " %d",0 ;!!! meterLen dq 0 ; счетчик цикла !!! .code procPx5m2 proc ; процедура ; создание файла invoke CreateFile,ADDR fName,GENERIC_WRITE,0,0, CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,0 mov fHandle,rax ; дескриптор m1: inc meterLen ;!!! mov rdi,0 ; запись в буфер значения полинома invoke wsprintf,ADDR szBufS1, ADDR szSt1,a5,a4,a3,a2,a1 ; запись в буфер первого столбца матрицы состояний invoke wsprintf,ADDR szBuf1[rdi],ADDR fmt,_h1_1 invoke wsprintf,ADDR szBuf2[rdi],ADDR fmt,_h1_2 invoke wsprintf,ADDR szBuf3[rdi],ADDR fmt,_h1_3 invoke wsprintf,ADDR szBuf4[rdi],ADDR fmt,_h1_4 invoke wsprintf,ADDR szBuf5[rdi],ADDR fmt,_h1_5 ; сохранение первого столбца mrmq _m1,_h1_1 ; 1 mrmq _m2,_h1_2 ; 0 mrmq _m3,_h1_3 ; 0 mrmq _m4,_h1_4 ; 0 mrmq _m5,_h1_5 ; 0 ; перезапись первого столбца для вычислений следующих столбцов mov r11,_h1_1 ; mov rbx,_h1_2 ; mov rcx,_h1_3 ; mov rdx,_h1_4 ; mov r12,_h1_5 ; mBegin: dec mmm2 ; уменьшение счетчика кол. генерации - для останова cmp mmm2,0 ; jz m15 ; 155 and r11,a1 ; выделение состояния 1-й ячейки and rbx,a2 ; выделение состояния 2-й ячейки and rcx,a3 ; выделение состояния 3-й ячейки and rdx,a4 ; выделение состояния 4-й ячейки and r12,a5 ; выделение состояния 5-й ячейки ; расчет следующего состояния 1-й ячейки xor r11,rbx ; xor rcx,rdx ; xor r11,rcx ; получение следующего состояния 1-й ячейки xor r11,r12 mov r10,r11 ; сохранение следующего состояния 1-й ячейки mrmq _m5_1,_m4 mrmq _m4_1,_m3 mrmq _m3_1,_m2 mrmq _m2_1,_m1 mov _m1_1,r10 mov r11,_m1_1 ; mov rbx,_m2_1 ; mov rcx,_m3_1 ; mov rdx,_m4_1 ; mov r12,_m5_1 ; mov mmm1,0 ; счетчик совпадения с 1-м столбцом ; сравнение полученного столбца с первым столбцом cmp r11,qword ptr _h1_1 jnz m11 inc mmm1 m11: cmp rbx,qword ptr _h1_2 jnz m12 inc mmm1 m12: cmp rcx,qword ptr _h1_3 jnz m13 inc mmm1 m13: cmp rdx,qword ptr _h1_4 jnz m14 inc mmm1 m14: cmp r12,qword ptr _h1_5 jnz m15 inc mmm1 m15: cmp qword ptr mmm1,5 156 jz m16 inc rdi ; вставка пробела перед след. столбцом inc rdi ; сдвиг на следующую позицию вывода inc meterLen ;!!! ; сохранение в буфер текущего столбца invoke wsprintf,ADDR szBuf1[rdi],ADDR fmt,_m1_1 invoke wsprintf,ADDR szBuf2[rdi],ADDR fmt,_m2_1 invoke wsprintf,ADDR szBuf3[rdi],ADDR fmt,_m3_1 invoke wsprintf,ADDR szBuf4[rdi],ADDR fmt,_m4_1 invoke wsprintf,ADDR szBuf5[rdi],ADDR fmt,_m5_1 invoke wsprintf,ADDR szM,ADDR fmt,meterLen ;!!! mov r11,_m1_1 ; 1-е значение текущего столбца mov rbx,_m2_1 ; 2-е значение текущего столбца mov rcx,_m3_1 ; 3-е значение текущего столбца mov rdx,_m4_1 ; 4-е значение текущего столбца mov r12,_m5_1 ; 5-е значение текущего столбца ; подготовка к вычислению следующего столбца mrmq _m1,_m1_1 mrmq _m2,_m2_1 mrmq _m3,_m3_1 mrmq _m4,_m4_1 mrmq _m5,_m5_1 ; jmp mBegin m16: invoke WriteFile,fHandle,ADDR szBufS1,BSIZE,ADDR cWritten,0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile,fHandle,ADDR szBuf1,BSIZE,ADDR cWritten,0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile, fHandle, ADDR szBuf2, BSIZE, ADDR cWritten, 0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile, fHandle, ADDR szBuf3, BSIZE, ADDR cWritten, 0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile, fHandle, ADDR szBuf4, BSIZE, ADDR cWritten, 0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 invoke WriteFile, fHandle, ADDR szBuf5, BSIZE, ADDR cWritten, 0 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 157 invoke WriteFile,fHandle,ADDR crtf,1,ADDR cWritten,0 ; очистка буфера ;invoke MemSet, ADDR szBuf1,'0',16 ;invoke ZeroMemory,ADDR szBuf2,32 ;invoke ZeroMemory,ADDR szBuf3,32 ;invoke FlushFileBuffers, szBuf4 mov meterLen,0 ;!!! mov rcx,BSIZE mov rsi,0 mov r11b,0 ; ;;; @@: mov szBuf1[rsi],r11b inc rsi loop @b mov rcx,BSIZE mov rsi,0 mov r11b,0; @2: mov szBuf2[rsi],r11b inc rsi loop @2 mov rcx,BSIZE mov rsi,0 mov r11b,0; @3: mov szBuf3[rsi],r11b inc rsi loop @3 mov ecx,BSIZE mov rsi,0 mov r11b,0 ; @4: mov szBuf4[rsi],r11b inc rsi loop @4 mov ecx,BSIZE mov rsi,0 mov r11b,0 ; @5: mov szBuf5[rsi],r11b inc rsi loop @5 158 ; изменение коэффициентов полинома xor a1,1 cmp a1,1 jz m1 xor a2,1 cmp a2,1 jz m1 xor a3,1 cmp a3,1 jz m1 xor a4,1 cmp a4,1 jz m1 invoke CloseHandle,fHandle ; закрытие дескриптора файла ret procPx5m2 endp entry_point proc invoke QueryPerformanceCounter,ADDR Tick1 invoke procPx5m2 invoke QueryPerformanceCounter,ADDR Tick2 finit fild dword ptr Tick2 fild dword ptr Tick1 fsubp st(1),st fistp Tick11 invoke wsprintf,ADDR bufT,ADDR ifmtT,Tick11 invoke MessageBox,0,ADDR bufT,ADDR szTitle1, MB_ICONINFORMATION invoke ExitProcess, 0 entry_point endp end 159 Поліноми Р(х) з degP(x) =5 (максимальним ступенем) в полі GF(2) (mod2) генерують послідовність, яку можна для деяких з них представити у вигляді: Р(х) = 100101 = х5 2 х2 2 1, Т = 31 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 Р(х) = 101001 = х5 2 х3 2 1, Т = 31 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 Р(х) = 101111, Т = 31 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 Р(х) = 110111, Т = 31 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 Р(х) = 111011, Т = 31 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 160 Р(х) = 111101, Т = 31 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 Кожен стовпець наведених матриць станів є числом, послідовність яких називається псевдовипадковою. Для деяких з них така псевдовипадкова послідовність наведена нижче: - для P(x)=100101: 1,2,5,10,21,11,23,14,29,27,22,12,24,17,3,7,15,31,30,28,25,19,6,13,26, 20,9,18,4,8,16; - для P(x)= 101001: 1,2,4,9,18,5,11,22,12,25,18,7,15,31,30,28,24,17,3,6,13,27,23,14,29,26, 21,10,20,8,16; - для P(x)= P(x)=101111: 1,3,6,12,25,18,4,9,19,7,15,31,30,29,27,23,14,28,24,17,2,5,10,21,11,22, 13,26,20,8,16; - для P(x)= P(x)= 111011: 1,3,7,14,28,25,19,6,13,27,23,15,31,30,29,26,20,8,17,2,4,9,18,5,10,21,1 1,22,12,24,16. Для отримання псевдовипадкової послідовності з використанням Р(х) інших ступенів необхідно отримати їх стани матриці і кожен стовпець перевести в десяткове число. У програмі 10.6 показано застосування шифрування ключами, отриманими при використанні Р(Х) = х4 х3 х 1. Для збільшення довжини зашифрованої послідовності застосовано 8 циклів генерації чисел по 15 чисел у кожному циклі. Програма 10.6. Шифрування пароля за модулем 2 з використанням команд набору (технології) AVX2: ; программа шифрования пароля по модулю 2 с использ. ком. AVX2 ; поразрядное mod2: txt mod2 key ; key = 120 символов include \masm64\include64\masm64rt.inc ; .data 161 rez db 240 DUP(0) ; для 120 символов buff db 240 DUP(0) ; txt db "Введите код для преобразования",10,0 TXTSIZE equ $ - txt ; число символов, выведенных на экран txt2 db "txt mod2 random:",10,0 ; stdin1 dq 0 ; для дескриптора ввода stdout1 dq 0 ; для дескриптора вывода wrn dq 0 ; число записанных символов rdn dq 0 ; число прочитанных символов ; key db 1 ; ключ нач.знач. для сложения по mod2 (при отладке) key db 1,2,4,9,3,6,0Dh,0Ah,5,0Bh,7,0Fh,0Eh,0Ch,8 ; 15х8=120 db 1,2,4,9,3,6,0Dh,0Ah,5,0Bh,7,0Fh,0Eh,0Ch,8 db 1,2,4,9,3,6,0Dh,0Ah,5,0Bh,7,0Fh,0Eh,0Ch,8 db 1,2,4,9,3,6,0Dh,0Ah,5,0Bh,7,0Fh,0Eh,0Ch,8 db 1,2,4,9,3,6,0Dh,0Ah,5,0Bh,7,0Fh,0Eh,0Ch,8 db 1,2,4,9,3,6,0Dh,0Ah,5,0Bh,7,0Fh,0Eh,0Ch,8 db 1,2,4,9,3,6,0Dh,0Ah,5,0Bh,7,0Fh,0Eh,0Ch,8 db 1,2,4,9,3,6,0Dh,0Ah,5,0Bh,7,0Fh,0Eh,0Ch,8 ; Р(х)=х4 + х3 + 1 .code ; Р(х)=х4 + х3 + 1 entry_point proc invoke GetStdHandle,STD_INPUT_HANDLE ;извлекает дескриптор mov stdin1,rax invoke GetStdHandle,STD_OUTPUT_HANDLE ;извлекает дескриптор mov stdout1,rax invoke SetConsoleCP,1251;уст. кодовой страницы win 1251 в поток ввода invoke SetConsoleOutputCP,1251;уст. код. стр. win 1251 в поток вывода invoke WriteConsoleA,stdout1,addr txt,TXTSIZE,addr wrn,0 ; вывод текста invoke WriteConsoleA,stdout1,addr txt2,17,addr wrn,0 ; вывод текста invoke ReadConsole,stdin1,addr buff,128,addr rdn,0 ; ; ReadConsole записала по адр. rdn +2 символа lea rsi,buff ; загрузка адреса буфера lea rdi,rez ; загрузка адреса ячейки результата lea r15,key ; загрузка адреса нахождения ключа mov rcx,rdn ; запись прочитанных символов sub rcx,2 ; минус служебные символы cmp rcx,8 jl mL 162 jmp mG mL: mov rcx,rdn @@: mov al,byte ptr [rsi] ; buff mov bl,byte ptr [r15] ; key xor al,bl ; шифрование текста по модулю 2 mov byte ptr [rdi],al ; inc rsi ; inc rdi ; inc r15 ; изменение ключа на +1 loop @b ; dec, jnz jmp mExit mG: mov rax,rdn ; количество чисел sub rax,2 ; минус служебные символы mov rbx,8 ; чисел в цикле xor rdx,rdx ; подготовка к делению div rbx ; количество циклов mov rcx,rax ; сохранение количества циклов lea rsi,buff ; загрузка адреса buff lea r15,key ; загрузка адреса key lea rdi,rez ; загрузка адреса rez m1: vmovups ymm0,[rsi] ; buff vmovups ymm1,[r15] ; key vxorpd ymm2,ymm0,ymm1; vmovups [rdi],ymm2 ; перемещение в память по адресу регистра rdi add rsi,32 ; buff, 8 бит x 32 числа = 256 разрядов add r15,32 ; rez, смещение на 256 разряда add rdi,32 ; результат, смещение на 256 разрядов loop m1 mExit: invoke WriteConsoleA,stdout1,addr rez,128,addr wrn,0 invoke ReadConsole,stdin1,addr buff,130,addr rdn,0 invoke ExitProcess,0 entry_point endp end 163 У програмі в секції даних можна застосувати псевдовипадкову послідовність, отриману за допомогою будь-якого полінома з degP(x) 8 ступеня. Програма шифрує 120 символів під час введення символів у консольному вікні. 10.5. Лабораторна робота «Кодування файлів» Мета заняття: набути практичних навичок кодування файлів. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – представити тексти програм та всі файли проекту; – у тексті програми організувати виведення назви лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання Залежно від рівня програмування студента вибрати один чи всі варіанти завдання. Варіанти 1. Написати програму розрахунку всіх вихідних матриць із зазначенням коефіцієнтів для degР(х) = 8, довжину циклів для кожного Р(х), а для Р(х), які генерують максимальні періоди – в окремих файлах навести 10-е чи 16- е уявлення цих послідовностей. 2. Написати програму шифрування каталогу із додаванням нового розширення. 3. Написати програму дешифрування каталогу із відновленням старого розширення. Контрольні питання для перевірки знань 1. Навести приклади API-функцій, які можна використовувати для створення випадкових послідовностей. 164 2. Що являє собою процес кодування при передачі даних та декодування? 3. Які операції застосовуються під час кодування? 4. Навіщо необхідно кодувати дані послідовності з максимальним періодом? 5. Як формується вихідна матриця станів для псевдовипадкової послідовності? 165 11. ХЕШУВАННЯ 11.1. Загальні відомості про хешування Хешування (англ. hashing) – перетворення масиву вхідних даних довільної довжини на (вихідний) бітовий рядок встановленої довжини, що виконується певним алгоритмом. Хеш - це число, яке генерується з тексту за допомогою хеш-алгоритму. Це число менше оригінального тексту. Хеш-функції спроектовані так, щоб навіть незначні зміни у вихідних даних призводили до радикальних змін у хеші, що генерується. Хеш (hash) – це унікальний цифровий відбиток, який можна привласнити будь-якому файлу. Один із популярних варіантів використання хешу – зберігання паролів на сайті. При вході на який-небудь дані не передаються в базу даних у відкритому вигляді - щоб уникнути крадіжки хакерами. Під час авторизації пароль спочатку хешується і потім записується в базу даних. При наступній спробі входу пароль знову перетворюється на хеш і порівнюється з хешом на сервері. У Мережі можна знайти цілі словники, які містять тисячі вкрадених дехешованих паролів. Для запобігання пошуку за словниками кожному паролю під час реєстрації додають сіль - тобто випадковий набір символів. Коли вводиться пароль, алгоритм додає до нього унікальну сіль, потім все хешується і виходить зовсім новий хеш. Хеш-функцію можна отримати на основі різних алгоритмів, наприклад: 1. «Хеш-код» як залишок від розподілу на число всіх можливих «хешів»; 2. "Хеш-код" як набір коефіцієнтів одержуваного полінома; 3. "Хеш-функції", засновані на комбінованих операціях. 166 11.2. Алгоритм хешування SHA-1 Алгоритма SHA-1 (Secure Hashing Algorithm) – вважається застарілим та досить простим, що дозволяє починати вивчення хешування з нього. Хеш-функція представляє 160-бітове (20 байт) значення, зване також дайджестом повідомлення, яке зазвичай відображається як шістнадцяткове число завдовжки 40 цифр. Для отримання хеш-значення алгоритму SHA-1 вихідне повідомлення розбивається на блоки по 512 біт у кожному. Останній блок доповнюється до довжини, кратної 512 біт. Спочатку додається 1 (біт), а потім нулі, щоб довжина блоку стала рівною 512 - 64 = 448 біт. У 64 біти, що залишилися, записується довжина вихідного повідомлення в бітах. Якщо останній блок має довжину більше 447, але менше 512 біт, то доповнення виконується наступним чином: спочатку додається 1 (біт), потім нулі аж до кінця 512-бітного блоку; після цього створюється ще один 512-бітний блок, який заповнюється аж до 448 біт нулями, після чого в 64 біти, що залишилися, записується довжина вихідного повідомлення в бітах. Доповнення останнього блоку здійснюється завжди, навіть якщо повідомлення має потрібну довжину. Наприклад, якщо написати текстовий файл із вмістом «Привіт, світ!», можна вважати, що ця фраза складається з 12 символів включаючи пробіл. Кожен символ кодується байтом, тому 12 х 8 = 96 біт = 0110 0000. Кількість байтів у блоці: 512/8=64. Наприкінці блоку додамо двійковий код, що означає розмір повідомлення в бітах: 1101000b = 104d. Доповнення останнього блоку здійснюється завжди, навіть якщо повідомлення має потрібну довжину. Для отримання хеш-значення використовуються: - п'ять 32-бітових змінних (A, B, C, D, E); - чотири константи Kt (1-4); - чотири нелінійні операції Ft (1-4); Для перемішування блоку 512 біт використовуються константи: A = h0 = 0×67452301 B = h1 = 0xEFCDAB89 C = h2 = 0×98BADCFE D = h3 = 0×10325476 167 E = h4 = 0xC3D2E1F0 Алгоритм дробить вихідне повідомлення на 80 частин і перемішує з кожною констант. Кожна ітерація циклу оновлює значення h0–h4, доки не закінчиться вихідне повідомлення. У результаті п'ять фінальних значень склеюються: h0 h1 h2 h3 h4. Попередня обробка. Приєднуємо біт 1 до повідомлення. Приєднуємо k бітів '0', де k найменше число ≥ 0 таке, що довжина повідомлення, що вийшло (у бітах) порівнянна за модулем 512 з 448 (length mod 512 = 448). Додаємо довжину вихідного повідомлення (до попередньої обробки) як ціле 64-бітове число. В процесі виконання. Повідомлення розбивається послідовно 512 біт. Блок розбивається на 16 частин по 32-біта w[i], 0 <= i <= 15 Ці 16 частин по 32-бітові доповнюються до 80 32-бітових частин: for i from 16 to 79 w[i] = (w[i-3] xor w[i-8] xor w[i-14] xor w[i-16]) цикліч. зрушення вліво 1 Відбувається ініціалізація хеш-значень цієї частини: a = h0 b = h1 c = h2 d = h3 e = h4 Основний цикл має вигляд: for i from 0 to 79 if 0 ≤ i ≤ 19 then f = (b and c) or ((not b) and d) k = 0x5A827999 else if 20 ≤ i ≤ 39 then f = b xor c xor d k = 0x6ED9EBA1 else if 40 ≤ i ≤ 59 then f = (b and c) or (b and d) or (c and d) 168 k = 0x8F1BBCDC else if 60 ≤ i ≤ 79 then f = b xor c xor d k = 0xCA62C1D6 temp = (a leftrotate 5) + f + e + k + w[i] e = d d = c c = b leftrotate 30 b = a a = temp Додаємо хеш-значення цієї частини до результату: h0 = h0 + a h1 = h1 + b h2 = h2 + c h3 = h3 + d h4 = h4 + e Підсумкове хеш-значення (h0, h1, h2, h3, h4 має бути перетворене до виду: digest = hash = h0 append h1 append h2 append h3 append h4 11.3. Лабораторна робота «Хешування за алгоритмом SHA-1» Мета заняття: набути практичних навичок хешування файлів за алгоритмом SHA-1. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – у тексті програми організувати виведення назви лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання Написати програму отримання хеш-функції для своєї оригінальної 169 програми із зазначенням у програмі свого прізвища, номера групи та призначення програми. Контрольні питання для перевірки знань 1. Яку розрядність має хеш-функція? 2. Що являє собою хеш-функція? 3. Скільки 32-бітових змінних застосовується при хешуванні? 4. Скільки констант використовується при хешуванні і чого вони залежать? 5. Скільки нелінійних операцій використовується при хешуванні? 170 12. КОНТРОЛЬ ЦИКЛІЧНИМ НАДЛИШНИМ КОДОМ 12.1. Загальні відомості про поліноми Контроль циклічно надлишковим кодом - CRC (Cyclical Redundancy Check) - це потужний і широко використовуваний метод виявлення помилок передачі інформації. Він забезпечує виявлення помилок із високою ймовірністю. Крім того, цей метод має низку інших корисних моментів, які можуть знайти своє застосування у практичних завданнях. CRC - це залишок від поділу вхідної послідовності на поліном P(x). Поліном (від багаточлен) вибирається за певними критеріями. Приклад запису полінома: Р(х) = х4 2 х3 2 1. Коефіцієнти цього многочлена записуються як 11001 = 318 = 1916 У Р(х) вільний член а0 = 1. Для генераторів псевдовипадкових чисел поліноми Р(х) завжди вибирають ті, що генерують максимальний період, т.к. у цьому періоді присутні всі стани, а пропущений стан лише один – усі нулі. У цьому випадку багаточлен застосовні всі математичні методи його обробки та аналізу. Вихідна послідовність байтів, якою може бути і великий файл, і текст розміром кілька слів і навіть символів, є єдиною послідовністю бітів. Ця послідовність поділяється на деяке фіксоване двійкове число. Інтерес представляє залишок від поділу, який є значенням CRC. На приймальній стороні виконується розподіл той самий поліном і порівняння залишку від розподілу з вихідним значенням CRC. Якщо вони рівні, то вважається, що вихідне повідомлення не пошкоджене. Найбільш повно описані поліноми та їх властивості у книзі «Пітерсон У., Уелдон Е. Коди, що виправляють помилки. 2-ге вид. - М.: Світ, 1976. - 593 с. ». Користуватись поліномами з інших джерел, особливо з інтернету, треба з великою обережністю. Наведені у літературі поліноми немає остаточним вибором і вимагають постійного дослідження залежно від обраних критеріїв. Не всі з наведених Р(х) дозволяють отримати псевдовипадкову послідовність максимальної довжини. Можна припустити, що вибрано 171 зовсім інші критерії і під них отримано певну практичну доцільність їх застосування. Напевно, отримання залишку від поділу на полином (як контрольного результату), властивості Р(х) та його можливості не завжди актуальні. 12.2. Арифметика CRC Розрахунки CRC ведуться у двійковій системі числення. Природно, можна застосовувати й інші системи числення, але це вже в галузі наукових досліджень та областей застосування. Розрахунки CRC здійснюються в поліноміальній арифметиці за модулем 2. У операції mod2 переноси відсутні. Операції додавання та віднімання в арифметиці CRC тотожні і виконуються як додавання за модулем 2 (XOR). В алгоритмі обчислення CRC вводиться ще кілька поліномів та співвідношень між ними: – породжує поліпом G(x) – обраний за певними критеріями поліном, який ділиться вихідний поліном повідомлення; – поліном-приватне Q(x) – поліном, який виходить при розподілі і є приватним від поділу поліномів D(x)/G(x); – поліном-залишок R(x) – поліном, що вийшов як залишок від поділу поліномів D(x)/G(x). Між переліченими поліномами існують відносини: D(x) = Q(x)xG(x) + R(x), Q(x) = (D(x) - R(x))/G(x). Існує кілька різних способів поділу. На рис. 12.1 наведено схему обчислення двійкового поділу (з циклічним переносом). 172 На рис. 12.2 наведено схему обчислення CRC-ділення. Рис. 12.1. Схема обчислення двійкового ділення (З циклічним перенесенням) Рис. 12.2. Схема обчислення CRC- ділення 173 Результати звичайного та CRC-поділів відрізняються. Алгоритм обчислення CRC є одним із можливих алгоритмів хешування. Розрядність полінома G(x) 16 біт, що породжує, забезпечує до 216 = 65 536-1 значень хеш-функції (відсутня нульовий стан). Збільшення розрядності полінома G(x) до 32 бітів призводить до розширення набору значень хеш-функції вже до 4294967296-1. 12.3. Прямий алгоритм обчислення CRC Схема обчислення CRC-розподілу, яка наведена на рис.12.2 реалізує прямий алгоритм CRC-розподілу для вихідної послідовності на стороні, що передає. У CRC арифметиці операції додавання та віднімання в ній тотожні і виконуються як додавання за модулем 2 (XOR). Схема обробки отриманої послідовності на приймальній стороні представлена на рис. 12.3. Рис. 12.3. Схема обчислення CRC-ділення на приймальній стороні 174 На приймальній стороні вхідна послідовність доповнюється трьома нулями, т.к. використовувався Р(х) третього ступеня - 1011. При виконанні CRC-розподілу ці додаткові біти гарантують, що всі біти вихідної послідовності візьмуть участь у процесі формування значення CRC. Довжина значення CRC, що приєднується до вихідної послідовності, повинна дорівнювати ступеню полінома. Але практично немає необхідності доповнювати отриману послідовність нулями. Необхідно виконати CRC-розподіл отриманого вихідного рядка (доповненого в кінці вихідним значенням CRC) на поліном і аналізувати залишок. Якщо залишок від цього поділу нульовий, то вихідна послідовність була порушена під час передачі. Існує досить багато методів отримання контрольної характеристики з отриманням різних результатів. Для зручності та простоти поділу Р(х) необхідно брати кратними значенню 8. Ступінь полінома визначає розмірність регістрів. Ділити багаточлени "стовпчиком" зі зрушенням на 1 біт на кожній ітерації занадто повільно, тому на практиці використовують більш швидкі алгоритми, що ґрунтуються на властивостях CRC-арифметики. На рис. 12.4 показаний один з варіантів поділу вихідної послідовності бітів на многочлен x8 + x2 + x1 + x0, у якому старший розряд зрушений зарубіжних країн регістру. При розгляді цього алгоритму прийнято, що старший аргумент полінома завжди дорівнює 1 і вже зрушений за його межу, але бере участь в операції XOR. Алгоритм обчислення контрольної суми CRC: Рис. 12.4. Приклад поділ вихідної послідовності бітів на багаточлен x8 + x2 + x1 + x0 175 1. Вихідне повідомлення доповнюється нулями в молодших розрядах, у кількості, що дорівнює кількості розрядів полінома. 2. У молодший розряд регістру заноситься один старший біт повідомлення, та якщо з старшого розряду регістру висувається один біт. 3. Якщо висунутий біт дорівнює "1", то виконується операція XOR, у тих розрядах регістру, які відповідають одиницям полінома. 4. Якщо у повідомленні ще є біти, переходимо до кроку 2. 5. Коли всі біти повідомлення надійшли в регістр і були оброблені цим алгоритмом, у регістрі залишається залишок від поділу, який є контрольною сумою CRC. 12.4. Стандартний табличний алгоритм Стандартний табличний алгоритм дозволяє зменшити час отримання залишку від поділу. Для того щоб була можливість обробляти поліном математичними методами, він повинен генерувати максимальний період. Суть його в тому, що кожен поліном (див. рис. 10.4) генерує свою псевдовипадкову послідовність. Для обчислення CRC необхідно кожен розряд вхідної послідовності (0 або 1) помножити (тобто вибрати) на відповідне псевдовипадкове число та вибрані значення скласти mod2. Наприклад, 1-й старший розряд множиться на 1-й стовпець матриці станів (1 число псевдовипадкової послідовності). Другий розряд множиться на 2-й стовпець і т.д. Потім всі значення складаються mod2. Таким чином, програму записується псевдовипадкова послідовність (матриця станів) у вигляді набору псевдовипадкових чисел, які послідовно перемножуються на розряди вхідної послідовності. А потім відбувається складання всіх результатів по mod2. Псевдовипадкову послідовність чисел іноді неправильно називають таблицею ініціалізації, що не відображає суті перетворень, що відбуваються. Такий самий принцип множення матриці станів на вхідну послідовність застосовується і при отриманні сигнатур. 12.5. Лабораторна робота "Обчислення CRC" Мета заняття: набути практичних навичок обчислення CRC. 176 У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – у тексті програми організувати виведення назви лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання Ознайомитись із матеріалами, розташованими за посиланням https://guildalfa.ru/alsha/node/2. Написати програму обчислення CRC для своєї оригінальної програми. Контрольні питання для перевірки знань 1. Які переваги контролю з використанням поліномів? 2. Переваги поліномів, які мають максимальний період генерування? 3. Які операції використовує арифметика CRC? 4. Як за допомогою поліномів описати алгоритм розподілу? 5. Опишіть процес надсилання та отримання повідомлення. 177 13. ШИФРУВАННЯ ПОВІДОМЛЕНЬ МЕТОДОМ ЗАМІНИ БИТОВИХ ПРЕДСТАВ 13.1. Метод заміни бітових уявлень Шифрування повідомлень методом заміни бітових уявлень є ще одним з алгоритмів кодування, який заснований на стисканні даних без втрат. Стиснення даних без втрат (англ. lossless data compression) – клас алгоритмів стиснення даних (відео, аудіо, графіки, документів, які у цифровому вигляді), під час використання яких закодовані дані може бути повністю відновлені. У загальних рисах сенс стиснення без втрат такий: у вихідних даних знаходять якусь закономірність і з урахуванням цієї закономірності генерують другу послідовність, яка повністю визначає вихідну. Наприклад, для кодування двійкових послідовностей, у яких багато нулів та мало одиниць, можна використовувати таку заміну: 00 → 0 01 → 10 10 → 110 11 → 111 У такому разі шістнадцять бітів 00 01 00 00 11 10 00 00 будуть перетворені на тринадцять бітів 0 10 0 0 111 110 0 0 Більшість алгоритмів стиснення без втрат працюють у дві стадії: на першій генерується статистична модель для вхідних даних, на другій проводиться заміна бітових уявлень на нові, використовуючи модель для отримання «імовірнісних» (тобто часто зустрічаються) даних, які використовуються частіше, ніж «неймовірні ». Наведений алгоритм перетворення бітового уявлення (що відноситься до другої стадії стиснення) реалізовано у програмі 13.1. Під час введення довільного тексту у програмі немає стиснення обсягу. Навпаки – обсяг коду після стиску збільшується, т.к. кількість комбінацій 00 набагато менша, ніж інших комбінацій. Однак така програма здійснює шифрування повідомлень шляхом заміни бітових уявлень. 178 Програма 13.1. Шифрування повідомлень методом заміни бітових уявлень: include /masm64/include64/masm64rt.inc .const ; имена фаайлов input_file db "codingIn.dat", 0 ; исходный файл output_file db "codingDe.dat", 0 ; файл с закодированной инф. .data? hInput dq ? hOutput dq ? read_count dq ? ; количество байтов, прочитанных файлом ReadFile .data bRead db 0 ; байт, который был прочитан bToWrite db 0 ; байт для записи bRemainder db 0 ; оставшиеся биты для записи nRemainderSize db 0 ; количество битов в остатке nEncodedSize dq 0 ; количество бит в закодированных данных .code entry_point proc ; Открытие входного файла invoke CreateFileA, addr input_file, GENERIC_READ, FILE_SHARE_READ, NULL,\ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL cmp rax, INVALID_HANDLE_VALUE je exit ; если файл не открываеся file - exit mov hInput, rax ; сохранение дескриптора входного файла ; Создание выходного файла invoke CreateFileA, addr output_file, GENERIC_WRITE, FILE_SHARE_READ, NULL,\ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL cmp rax, INVALID_HANDLE_VALUE je exit ; If can't open file - exit mov hOutput,rax ; Save input file handle ; AX - закодированный результат ; CL - смещение ; BX - loops ; DX - чтение данных во время обработки 179 ; Сегменты данных: ; - количество битов в последнем байте ; - закодированные данные ; Резервный байт для сохранения количества оставшихся битов mov bToWrite, 0 invoke WriteFile, hOutput, addr bToWrite, 1, NULL, NULL ; Основной цикл кодирования - Main encoding loop m0: ; Чтение байта из файла invoke ReadFile, hInput, addr bRead, 1, addr read_count, NULL mov al, bRemainder ; Load reminder into memory mov bl, nRemainderSize ; установить количество начальных битов mov nEncodedSize, rbx ; Обработка последнего байта cmp read_count, 1 je m1 ; если байт прочитан ; иначе - Prepare the remainder, write and exit mov cl, 8 sub cl, nRemainderSize ; 8 - Rem., size = количество бит для сдвига shl al, cl ; сдвиг влево mov bToWrite, al ; запись и выход invoke WriteFile, hOutput, addr bToWrite, 1, NULL, NULL ; количество битов остатка invoke SetFilePointer, hOutput, 0, NULL, FILE_BEGIN ; Размер остатка равен 0 или самому размеру (из последней итерации) invoke WriteFile, hOutput, addr nRemainderSize, 1, NULL, NULL jmp exit m1: ; Обработка данных mov cl, 6 ; shift size (>> 6, >> 4, >> 2, >> 0) mov bl, 4 ; повторение 4 раза (для каждой пары битов) ; Цикл кодирования m2: cmp bl, 0 je m3 ; на окончание кодирования mov dl, bRead ; данные, которые необходимо обработать shr dl, cl ; перемещение битов в начало, чтобы их замаскировать 180 and dl, 3 ; маскированные биты для обработки ; Определение битовой последовательности и запись кодир. битов ; 00 -> 0 cmp dl, 0 jne seq1 shl rax, 1 ; добавление 0 к закодированной последовательности add nEncodedSize,1 ; увеличение размера за счет кол. закодированных битов jmp seq_end ; 01 -> 10 seq1: cmp dl, 1 jne seq2 shl rax, 2 ; добавить 10 к закодированной последовательности or rax, 2 add nEncodedSize, 2 ; увеличение размера за счет кол. закодированных битов jmp seq_end ; 10 -> 110 seq2: cmp dl, 2 jne seq3 shl rax, 3 ; добавить к закодированной последовательности or rax, 6 add nEncodedSize, 3 ; увеличение размера за счет кол. закодированных битов jmp seq_end ; 11 -> 111 seq3: ; Don't need to compare. This is the last possible case shl rax, 3 ; добавить к закодированной последовательности or rax, 7 add nEncodedSize, 3 ; увеличение размера за счет кол. закодированных битов ; конец условия и цикла seq_end: dec bl ; количество повторов sub cl, 2 ; размер сдвига (перейти к следующей паре) jmp m2 181 ; Конец кодирования m3: xor rbx, rbx ; Число циклов записи (0) mov rcx, nEncodedSize ; Нет записи, если размер < 8. Переход к остатку и повторение цикла cmp rcx, 8 jge @f mov bRemainder, al mov nRemainderSize, cl jmp m0 ; Получить количество циклов записи и размер остатка @@: cmp rcx, 8 jl @f ; Size >= 8 inc rbx ; ++ писать циклы sub rcx, 8 ; -1 записываемый байт из размера jmp @b ; Size < 8 @@: mov nRemainderSize, cl ; Сохранение размера остатка для следующего цикла чтения ; Готовим маску для остатка xor dl, dl ; 0000 0000 not dl ; 1111 1111 shl dl, cl ; 1111 1000 not dl ; 0000 0111 ; Сохранение остатка для следующего цикла чтения. and dl, al mov bRemainder, dl shr rax, cl ; удалить остаток из закодированных данных mov r12, rax ; Swap rax, r12. Теперь r12 содержит закодированные данные (поскольку вызов перезапишет rcx-r11) ; Запись байта в цикле ; rax - закодированные данные для обработки ; rbx - кол. циклов ; rcl - shifts count ; r11 - закодированные данные @@: 182 cmp rbx, 0 je m0 ; на след. байт mov rax, r12 dec rbx ; -1 количество циклов/сдвигов (в байтах) mov cl, bl imul rcx, 8 ; количество сдвигов (в битах) shr rax, cl ; Переместить байт влево and rax, 255 ; маска 1 байт mov bToWrite, al ; Заключительный этап invoke WriteFile, hOutput, addr bToWrite, 1, NULL, NULL jmp @b exit: invoke CloseHandle, hInput invoke CloseHandle, hOutput invoke ExitProcess, 0 entry_point endp end В результаті роботи програми файл з ім'ям codingIn.dat кодується та закодована інформація записується у файл та ім'ям codingDe.dat. Для розкодування файлу використовується програма 13.2. Програма 13.2. Розкодування повідомлень методом заміни бітових уявлень: include /masm64/include64/masm64rt.inc .const ; Filenames input_file db "codingDe.dat", 0 output_file db "codingDe2.dat", 0 .data? hInput dq ? hOutput dq ? read_count dq ? ; Number of bytes ReadFile has read .data bRead db 0 ; Byte that were read mInputSize dq 0 ; Size of an input file in bytes nLastBitsCount db 0 ; Bits count in the last byte 183 bRemainder dq 0 ; Remainder from bits to write nRemainderSize db 0 ; Bits count in the remainder bEncodedData dq 0 ; Encoded data bDecodedRemainder db 0 ; Remainder from decoded bits nDecodedRemainderSize db 0 ; Bits count in the decoded remainder bToWrite db 0 ; Byte to be written .code entry_point proc ; Opening an input file invoke CreateFileA, addr input_file, GENERIC_READ, FILE_SHARE_READ, NULL,\ OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL cmp rax, INVALID_HANDLE_VALUE je exit ; If can't open file - exit mov hInput, rax ; Save input file handle ; Creating an output file invoke CreateFileA, addr output_file, GENERIC_WRITE, FILE_SHARE_READ, NULL,\ CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL cmp rax, INVALID_HANDLE_VALUE je exit ; If can't open file - exit mov hOutput, rax ; Save input file handle ; Getting input file size invoke GetFileSizeEx, hInput, addr mInputSize dec mInputSize ; 1 byte is reserved for the remainder size. Skip ; Read bits count in the last byte invoke ReadFile, hInput, addr nLastBitsCount, 1, addr read_count, NULL ; Main decoding loop m0: cmp mInputSize, 0 ; Decode while there are bytes to decode je exit ; End of decoding mov r12, bRemainder ; Load reminder into memory xor rbx, rbx mov bl, nRemainderSize ; Set start bits count ; Read byte from the file invoke ReadFile, hInput, addr bRead, 1, addr read_count, NULL cmp read_count, 0 184 je exit ; Nothing to decode ; Process size for the last byte .if mInputSize == 1 ; If the byte is last add bl, nLastBitsCount ; Add count of read bits shl r12, 8 or r12b, bRead ; Place read bytes mov rcx, 8 sub cl, nLastBitsCount shr r12, cl ; Remove redundant zeros .else add bl, 8 ; Add count of read bits shl r12, 8 ; Place read bytes or r12b, bRead .endif dec mInputSize ; Decrease the remaining file size mov bEncodedData, r12 ; Save full encoded data xor rax, rax mov al, bDecodedRemainder ; Saving the decoded data from the previous iteration xor r9, r9 mov r9b, nDecodedRemainderSize; Load to r9 remainder size ; Don't need this data anymore mov bRemainder, 0 mov nRemainderSize, 0 m2: ; Shifting and preparing metadata ; rax - decoded bits ; rbx - encoded bits count ; rdx - encoded bits ; r8 - count of bits to take ; r9 - count of encoded bits in rax cmp rbx, 0 ; If the size of the remaining encoded data <= 0 jle end_decode ; No more data to decode. Exit cmp rbx, 3 jg @f ; rbx > 3 ; rbx - (0; 3]: r8 = rbx mov r8, rbx ; Take the remaining bits jmp start_decode @@: ; rbx > 3 mov r8, 3 ; Take 3 bits start_decode: 185 ; Moving bits mov rdx, bEncodedData ; Load the encoded data mov rcx, rbx sub rcx, r8 ; Size - count of bits to take = shifts count shr rdx, cl ; Bits are moved to the right border ; Masking bits mov r10, rdx ; Copy encoded data mov rcx, r8 ; rcx - count of bits shr rdx, cl shl rdx, cl ; erase rcx bits from the right side xor rdx, r10 ; mask rcx bits ; Process taken bits depending on their count .if r8 == 3 ; 3 bits .if rdx == 7 ; 111 shl rax, 2 or rax, 3 ; Save 11 sub rbx, 3 ; Decrease ncoded bits count add r9, 2 ; Increase decoded bits count jmp m2 ; Loop .elseif rdx == 6 ; 110 shl rax, 2 or rax, 2 ; Save 10 sub rbx, 3 ; Decrease encoded bits count add r9, 2 ; Increase decoded bits count jmp m2 ; Loop .else dec r8 ; Decrease count of bits to take jmp start_decode ; Take 2 bits instead .endif .elseif r8 == 2 ; 2 bits .if rdx == 2 ; 10 shl rax, 2 or rax, 1 ; Save 01 sub rbx, 2 ; Decrease encoded bits count add r9, 2 ; Increase decoded bits count jmp m2 ; Loop .elseif rdx == 0 ; 00 shl rax, 4 ; Save 0000 sub rbx, 2 ; Decrease encoded bits count add r9, 4 ; Increase decoded bits count jmp m2 ; Loop .elseif rdx == 3 ; 11 - remainder mov nRemainderSize, 2 ; 2 bits are in the remainder mov bRemainder, 3 ; Save the remainder 186 jmp end_decode .else ; 01 or 1 or 0 dec r8 ; Decrease count of bits to take jmp start_decode ; Take 2 bits instead .endif .else ; 1 bit .if rdx == 0 ; 0 shl rax, 2 ; Save 00 sub rbx, 1 ; Decrease encoded bits count add r9, 2 ; Increase decoded bits count jmp m2 ; Loop .else ; 1 mov nRemainderSize, 1 ; 1 bit is in the remainder mov bRemainder, 1 ; Save the remainder jmp end_decode .endif .endif end_decode: xor rbx, rbx ; Writing loops count (0) mov rcx, r9 ; Load size of the decoded data ; Can't write if size < 8. Moving to the remainder and repeating loop cmp rcx, 8 jge @f mov bDecodedRemainder, al mov nDecodedRemainderSize, cl jmp m0 ; Get write loops count and remainder size @@: cmp rcx, 8 jl @f ; Size >= 8 inc rbx ; ++write_loops sub rcx, 8 ; -1 writable byte from size jmp @b ; Size < 8 @@: mov nDecodedRemainderSize, cl ; Save remainder size for the next reading loop ; Masking bits of the remainder mov r10, rax ; Copy encoded data shr r10, cl 187 shl r10, cl ; erase rcx bits from the right side xor r10, rax ; mask rcx bits mov bDecodedRemainder, r10b shr rax, cl ; Remove remainder from the encoded data mov r12, rax ; Swap rax, r12. Now r12 contains encoded data (Because of invoke will rewrite rcx-r11) @@: cmp rbx, 0 je m0 ; End. Next byte, please! mov rax, r12 dec rbx ; -1 loop / shifts count (in bytes) mov cl, bl imul rcx, 8 ; shifts count (in bits) shr rax, cl ; Move the byte to the left and rax, 255 ; Mask exacly 1 byte mov bToWrite, al ; Final step invoke WriteFile, hOutput, addr bToWrite, 1, NULL, NULL jmp @b exit: invoke CloseHandle, hInput invoke CloseHandle, hOutput invoke ExitProcess, 0 entry_point endp end 13.2. Лабораторна робота "Шифрування повідомлень методом заміни бітових уявлень" Мета заняття: набути практичних навичок обчислення шифрування повідомлень методом заміни бітових уявлень. У звіті подати: – назва лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; – у тексті програми організувати виведення назви лабораторної роботи, варіант, прізвище та ініціали, e-mail, номер групи автора програми; 188 – результат виконання завдання (скриншоти вікон програми та відладчика). Завдання Написати програму шифрування повідомлень методом заміни бітових уявлень для обраного кодування двійкових послідовностей. Запропонуйте програму статистичної моделі аналізу вхідних даних. Контрольні питання для перевірки знань 1. Описати алгоритм стиснення даних без втрат? 2. Які переваги та недоліки аналізованого методу стиснення? 3. Які комбінації бінарних послідовностей можна застосувати для стиснення? 4. Які стадії алгоритму стиснення? 5. Запропонуйте варіант статистичної моделі аналізу вхідних даних. 189 14. ІНШІ МЕТОДИ ЗАХИСТУ КОДУ 14.1. Змішування коду Змішування коду застосовується, коли деякі частини програми не залежать один від одного, і їхнє розміщення не впливає на логіку роботи програми. Такі частини програми ускладнюють аналіз коду. 14.2. Застосування команд jmp Часте застосування команди jmp сильно ускладнює аналіз коду, а при досить великій кількості робить такий аналіз практично неможливим. У випадку, якщо програма невелика, для збільшення кількості команд jmp, що застосовуються, необхідно додавати «сміттєвий код». Навіть лінійне виконання програми можна розбити на блоки, розмістити їх у різних місцях програми та керувати ними за допомогою команд jmp. 14.3. Розбиття рядків на частини Розбиття рядків на частини дозволить досліднику збирати всі частини різних місць програми. А ще актуальніше, якщо ці частини розташовані у різних файлах, які потім збираються командним bat- файлом разом. 14.4. Приховування змісту програми Даний метод заснований на використанні системної функції за прямим призначенням. Вона має отримувати деякі параметри, порівнювати їх та повертати певний стан прапорів. А за станом цих прапорів (ознак) можливе подальше продовження алгоритму. 14.5. Позбавлення адресних констант Адресні константи, які зустрічаються у програмі, дозволяють розуміти зміст алгоритму. Тому бажано переписувати в різні регістри або осередки пам'яті, щоб заплутати дослідника. 190 Додаток А Поліноми degP(x)=4 в GF(2) та їх періоди 1x4+0x3+0x2+0x1+1 4 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 1x4+0x3+0x2+1x1+1 15 1 1 1 1 0 1 0 1 1 0 0 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 0 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 0 1 0 0 0 0 1 1 1 1 0 1 0 1 1 0 0 1 1x4+0x3+1x2+0x1+1 6 1 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 1 0 0 0 0 1 0 1 1x4+0x3+1x2+1x1+1 7 1 1 0 1 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 1 0 0 0 0 1 1 0 1 1x4+1x3+0x2+0x1+1 15 1 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 0 0 0 0 1 0 0 1 1 0 1 0 1 1 1 1 1x4+1x3+0x2+1x1+1 6 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 1x4+1x3+1x2+0x1+1 7 1 0 1 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 1 1 0 0 0 0 1 0 1 1 1x4+1x3+1x2+1x1+1 5 1 1 0 0 0 0 1 1 0 0 0 0 1 1 0 0 0 0 1 1 191 Додаток Б Поліноми degP(x)=5 у GF(2) та їх періоди 10000 5 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 10001 21 1 1 1 1 1 0 1 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 1 1 1 1 1 0 1 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 1 1 1 1 1 0 1 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 1 1 1 1 1 0 1 0 1 0 0 1 1 0 0 0 1 0 0 0 0 0 1 1 1 1 1 0 1 0 1 0 0 1 1 0 0 0 1 10010 31 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 0 0 0 0 0 1 0 1 0 1 1 1 0 1 1 0 0 0 1 1 1 1 1 0 0 1 1 0 1 0 0 1 10011 14 1 1 0 1 1 1 1 0 0 1 0 0 0 0 0 1 1 0 1 1 1 1 0 0 1 0 0 0 0 0 1 1 0 1 1 1 1 0 0 1 0 0 0 0 0 1 1 0 1 1 1 1 0 0 1 0 0 0 0 0 1 1 0 1 1 1 1 0 0 1 10100 31 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 0 0 0 0 0 1 0 0 1 0 1 1 0 0 1 1 1 1 1 0 0 0 1 1 0 1 1 1 0 1 0 1 10101 15 1 1 1 0 1 1 0 0 1 0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 0 1 0 0 0 0 0 1 1 1 0 1 1 0 0 1 0 1 10110 13 1 0 1 1 1 1 0 1 0 0 0 0 0 0 1 0 1 1 1 1 0 1 0 0 0 0 0 0 1 0 1 1 1 1 0 1 0 0 0 0 0 0 1 0 1 1 1 1 0 1 1 0 0 0 0 0 1 0 1 1 1 1 0 0 1 10111 31 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 0 0 0 0 0 1 1 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 0 1 0 1 0 1 1 0 1 11000 21 192 1 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 0 0 0 0 0 1 0 0 0 1 1 0 0 1 0 1 0 1 1 1 1 1 11001 8 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 1 0 0 0 0 0 1 1 1 1 11010 15 1 0 1 0 0 1 1 0 1 1 1 0 0 0 0 0 1 0 1 0 0 1 1 0 1 1 1 0 0 0 0 0 1 0 1 0 0 1 1 0 1 1 1 0 0 0 0 0 1 0 1 0 0 1 1 0 1 1 1 0 0 0 0 0 1 0 1 0 0 1 1 0 1 1 1 11011 31 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 0 0 0 0 0 1 1 0 1 0 1 0 0 1 0 0 0 1 0 1 1 1 1 1 0 1 1 0 0 1 1 1 11100 14 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0 1 0 0 1 1 1 1 0 1 1 0 0 0 0 0 1 0 0 1 1 1 1 0 1 1 11101 31 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 0 0 0 0 0 1 1 1 0 0 1 1 0 1 1 1 1 1 0 1 0 0 0 1 0 0 1 0 1 0 1 1 11110 31 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 0 0 0 0 0 1 0 1 1 0 1 0 1 0 0 0 1 1 1 0 1 1 1 1 1 0 0 1 0 0 1 1 11111 6 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 0 0 0 0 0 1 1 193 Додаток Поліноми degP(x)=4 у GF(3) та їх періоди 10001 4 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 10011 80 11112012112120202211020110012220210020002222102122121010112201022002111012001000 01111201211212020221102011001222021002000222210212212101011220102200211101200100 00111120121121202022110201100122202100200022221021221210101122010220021110120010 00011112012112120202211020110012220210020002222102122121010112201022002111012001 10021 80 12122011122220202112010210022120220020002121102221111010122102012001121011001000 01212201112222020211201021002212022002000212110222111101012210201200112101100100 00121220111222202021120102100221202200200021211022211110101221020120011210110010 00012122011122220202112010210022120220020002121102221111010122102012001121011001 10101 16 1 0 1 0 2 0 0 0 2 0 2 0 1 0 0 0 0 1 0 1 0 2 0 0 0 2 0 2 0 1 0 0 0 0 1 0 1 0 2 0 0 0 2 0 2 0 1 0 0 0 0 1 0 1 0 2 0 0 0 2 0 2 0 1 10111 26 1 1 2 0 0 1 0 1 1 0 1 2 1 0 2 1 1 2 2 2 2 0 1 0 0 0 0 1 1 2 0 0 1 0 1 1 0 1 2 1 0 2 1 1 2 2 2 2 0 1 0 0 0 0 1 1 2 0 0 1 0 1 1 0 1 2 1 0 2 1 1 2 2 2 2 0 1 0 0 0 0 1 1 2 0 0 1 0 1 1 0 1 2 1 0 2 1 1 2 2 2 2 0 1 10121 26 1 2 2 0 0 2 0 2 1 0 1 1 1 0 2 2 1 1 2 1 2 0 1 0 0 0 0 1 2 2 0 0 2 0 2 1 0 1 1 1 0 2 2 1 1 2 1 2 0 1 0 0 0 0 1 2 2 0 0 2 0 2 1 0 1 1 1 0 2 2 1 1 2 1 2 0 1 0 0 0 0 1 2 2 0 0 2 0 2 1 0 1 1 1 0 2 2 1 1 2 1 2 0 1 10201 16 1 0 2 0 2 0 0 0 2 0 1 0 1 0 0 0 0 1 0 2 0 2 0 0 0 2 0 1 0 1 0 0 0 0 1 0 2 0 2 0 0 0 2 0 1 0 1 0 0 0 0 1 0 2 0 2 0 0 0 2 0 1 0 1 10211 24 1 1 0 2 0 2 2 2 0 0 2 1 2 1 1 1 2 2 1 0 1 0 0 0 0 1 1 0 2 0 2 2 2 0 0 2 1 2 1 1 1 2 2 1 0 1 0 0 0 0 1 1 0 2 0 2 2 2 0 0 2 1 2 1 1 1 2 2 1 0 1 0 0 0 0 1 1 0 2 0 2 2 2 0 0 2 1 2 1 1 1 2 2 1 0 1 10221 24 1 2 0 1 0 1 2 1 0 0 2 2 2 2 1 2 2 1 1 0 1 0 0 0 0 1 2 0 1 0 1 2 1 0 0 2 2 2 2 1 2 2 1 1 0 1 0 0 0 0 1 2 0 1 0 1 2 1 0 0 2 2 2 2 1 2 2 1 1 0 1 0 0 0 0 1 2 0 1 0 1 2 1 0 0 2 2 2 2 1 2 2 1 1 0 1 194 1100180 10011012110021020122101011112220112120002002202122001201021120202222111022121000 01001101211002102012210101111222011212000200220212200120102112020222211102212100 00100110121100210201221010111122201121200020022021220012010211202022221110221210 00010011012110021020122101011112220112120002002202122001201021120202222111022121 11011 8 1 1 1 2 1 0 0 0 0 1 1 1 2 1 0 0 0 0 1 1 1 2 1 0 0 0 0 1 1 1 2 1 11021 6 1 2 1 0 0 0 0 1 2 1 0 0 0 0 1 2 1 0 0 0 0 1 2 1 11101 24 1 0 1 1 2 2 1 2 2 2 2 0 0 1 2 1 0 1 0 2 1 0 0 0 0 1 0 1 1 2 2 1 2 2 2 2 0 0 1 2 1 0 1 0 2 1 0 0 0 0 1 0 1 1 2 2 1 2 2 2 2 0 0 1 2 1 0 1 0 2 1 0 0 0 0 1 0 1 1 2 2 1 2 2 2 2 0 0 1 2 1 0 1 0 2 1 11111 26 1 1 2 1 2 0 2 2 0 1 2 2 2 1 1 0 1 0 2 0 0 2 1 0 0 0 0 1 1 2 1 2 0 2 2 0 1 2 2 2 1 1 0 1 0 2 0 0 2 1 0 0 0 0 1 1 2 1 2 0 2 2 0 1 2 2 2 1 1 0 1 0 2 0 0 2 1 0 0 0 0 1 1 2 1 2 0 2 2 0 1 2 2 2 1 1 0 1 0 2 0 0 2 1 1112180 12211100220100101022102121101111210120002112220011020020201120121220222212021000 01221110022010010102210212110111121012000211222001102002020112012122022221202100 00122111002201001010221021211011112101200021122200110200202011201212202222120210 00012211100220100101022102121101111210120002112220011020020201120121220222212021 11201 26 1 0 2 1 2 1 1 2 2 0 1 1 1 0 1 2 0 2 0 0 2 2 1 0 0 0 0 1 0 2 1 2 1 1 2 2 0 1 1 1 0 1 2 0 2 0 0 2 2 1 0 0 0 0 1 0 2 1 2 1 1 2 2 0 1 1 1 0 1 2 0 2 0 0 2 2 1 0 0 0 0 1 0 2 1 2 1 1 2 2 0 1 1 1 0 1 2 0 2 0 0 2 2 1 11211 26 1 1 0 0 2 0 1 0 1 2 2 1 2 2 0 1 2 0 2 2 2 2 1 0 0 0 0 1 1 0 0 2 0 1 0 1 2 2 1 2 2 0 1 2 0 2 2 2 2 1 0 0 0 0 1 1 0 0 2 0 1 0 1 2 2 1 2 2 0 1 2 0 2 2 2 2 1 0 0 0 0 1 1 0 0 2 0 1 0 1 2 2 1 2 2 0 1 2 0 2 2 2 2 1 1122180 12021222202212102110202002011002221120002101211110112120122010100102200111221000 01202122220221210211020200201100222112000210121111011212012201010010220011122100 00120212222022121021102020020110022211200021012111101121201220101001022001112210 00012021222202212102110202002011002221120002101211110112120122010100102200111221 1200180 195 10021011120022010221101012122120122220002001202221001102011220202121121021111000 01002101112002201022110101212212012222000200120222100110201122020212112102111100 00100210111200220102211010121221201222200020012022210011020112202021211210211110 00010021011120022010221101012122120122220002001202221001102011220202121121021111 12011 6 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 0 0 0 0 1 1 1 12021 8 1 2 1 1 1 0 0 0 0 1 2 1 1 1 0 0 0 0 1 2 1 1 1 0 0 0 0 1 2 1 1 1 12101 24 1 0 1 2 2 1 1 1 2 1 2 0 0 2 2 2 0 2 0 1 1 0 0 0 0 1 0 1 2 2 1 1 1 2 1 2 0 0 2 2 2 0 2 0 1 1 0 0 0 0 1 0 1 2 2 1 1 1 2 1 2 0 0 2 2 2 0 2 0 1 1 0 0 0 0 1 0 1 2 2 1 1 1 2 1 2 0 0 2 2 2 0 2 0 1 1 12111 80 11221200210200101021102222101212220220002211210012010020201220111120212111011000 01122120021020010102110222210121222022000221121001201002020122011112021211101100 00112212002102001010211022221012122202200022112100120100202012201111202121110110 00011221200210200101021102222101212220220002211210012010020201220111120212111011 12121 26 1 2 2 2 2 0 2 1 0 2 2 1 2 2 1 0 1 0 2 0 0 1 1 0 0 0 0 1 2 2 2 2 0 2 1 0 2 2 1 2 2 1 0 1 0 2 0 0 1 1 0 0 0 0 1 2 2 2 2 0 2 1 0 2 2 1 2 2 1 0 1 0 2 0 0 1 1 0 0 0 0 1 2 2 2 2 0 2 1 0 2 2 1 2 2 1 0 1 0 2 0 0 1 1 12201 26 1 0 2 2 2 2 1 1 2 0 1 2 1 0 1 1 0 1 0 0 2 1 1 0 0 0 0 1 0 2 2 2 2 1 1 2 0 1 2 1 0 1 1 0 1 0 0 2 1 1 0 0 0 0 1 0 2 2 2 2 1 1 2 0 1 2 1 0 1 1 0 1 0 0 2 1 1 0 0 0 0 1 0 2 2 2 2 1 1 2 0 1 2 1 0 1 1 0 1 0 0 2 1 1 12211 80 11011121202111102210202001021001211220002202221210122220112010100201200212211000 01101112120211110221020200102100121122000220222121012222011201010020120021221100 00110111212021111022102020010210012112200022022212101222201120101002012002122110 00011011121202111102210202001021001211220002202221210122220112010100201200212211 12221 26 1 2 0 0 2 0 1 0 1 1 2 2 2 1 0 2 2 0 2 1 2 1 1 0 0 0 0 1 2 0 0 2 0 1 0 1 1 2 2 2 1 0 2 2 0 2 1 2 1 1 0 0 0 0 1 2 0 0 2 0 1 0 1 1 2 2 2 1 0 2 2 0 2 1 2 1 1 0 0 0 0 1 2 0 0 2 0 1 0 1 1 2 2 2 1 0 2 2 0 2 1 2 1 1 20001 8 1 0 0 0 2 0 0 0 0 1 0 0 0 2 0 0 0 0 1 0 0 0 2 0 0 0 0 1 0 0 0 2 196 20011 26 1 1 1 1 0 2 1 0 0 1 0 0 0 2 2 2 2 0 1 2 0 0 2 0 0 0 0 1 1 1 1 0 2 1 0 0 1 0 0 0 2 2 2 2 0 1 2 0 0 2 0 0 0 0 1 1 1 1 0 2 1 0 0 1 0 0 0 2 2 2 2 0 1 2 0 0 2 0 0 0 0 1 1 1 1 0 2 1 0 0 1 0 0 0 2 2 2 2 0 1 2 0 0 2 20021 13 1 2 1 2 0 1 1 0 0 2 0 0 0 0 1 2 1 2 0 1 1 0 0 2 0 0 0 0 1 2 1 2 0 1 1 0 0 2 0 0 0 0 1 2 1 2 0 1 1 0 0 2 20101 12 1 0 1 0 0 0 2 0 2 0 0 0 0 1 0 1 0 0 0 2 0 2 0 0 0 0 1 0 1 0 0 0 2 0 2 0 0 0 0 1 0 1 0 0 0 2 0 2 20111 13 1 1 2 0 1 0 2 2 0 2 0 0 0 0 1 1 2 0 1 0 2 2 0 2 0 0 0 0 1 1 2 0 1 0 2 2 0 2 0 0 0 0 1 1 2 0 1 0 2 2 0 2 20121 26 1 2 2 0 1 0 2 1 0 1 0 0 0 2 1 1 0 2 0 1 2 0 2 0 0 0 0 1 2 2 0 1 0 2 1 0 1 0 0 0 2 1 1 0 2 0 1 2 0 2 0 0 0 0 1 2 2 0 1 0 2 1 0 1 0 0 0 2 1 1 0 2 0 1 2 0 2 0 0 0 0 1 2 2 0 1 0 2 1 0 1 0 0 0 2 1 1 0 2 0 1 2 0 2 20201 6 1 0 2 0 0 0 0 1 0 2 0 0 0 0 1 0 2 0 0 0 0 1 0 2 20211 40 1 1 0 2 1 1 0 0 2 1 2 1 0 1 2 0 1 0 0 0 2 2 0 1 2 2 0 0 1 2 1 2 0 2 1 0 2 0 0 0 0 1 1 0 2 1 1 0 0 2 1 2 1 0 1 2 0 1 0 0 0 2 2 0 1 2 2 0 0 1 2 1 2 0 2 1 0 2 0 0 0 0 1 1 0 2 1 1 0 0 2 1 2 1 0 1 2 0 1 0 0 0 2 2 0 1 2 2 0 0 1 2 1 2 0 2 1 0 2 0 0 0 0 1 1 0 2 1 1 0 0 2 1 2 1 0 1 2 0 1 0 0 0 2 2 0 1 2 2 0 0 1 2 1 2 0 2 1 0 2 20221 40 1 2 0 1 1 2 0 0 2 2 2 2 0 2 2 0 1 0 0 0 2 1 0 2 2 1 0 0 1 1 1 1 0 1 1 0 2 0 0 0 0 1 2 0 1 1 2 0 0 2 2 2 2 0 2 2 0 1 0 0 0 2 1 0 2 2 1 0 0 1 1 1 1 0 1 1 0 2 0 0 0 0 1 2 0 1 1 2 0 0 2 2 2 2 0 2 2 0 1 0 0 0 2 1 0 2 2 1 0 0 1 1 1 1 0 1 1 0 2 0 0 0 0 1 2 0 1 1 2 0 0 2 2 2 2 0 2 2 0 1 0 0 0 2 1 0 2 2 1 0 0 1 1 1 1 0 1 1 0 2 21001 26 1 0 0 1 2 0 1 1 1 1 0 0 0 2 0 0 2 1 0 2 2 2 2 0 0 0 0 1 0 0 1 2 0 1 1 1 1 0 0 0 2 0 0 2 1 0 2 2 2 2 0 0 0 0 1 0 0 1 2 0 1 1 1 1 0 0 0 2 0 0 2 1 0 2 2 2 2 0 0 0 0 1 0 0 1 2 0 1 1 1 1 0 0 0 2 0 0 2 1 0 2 2 2 2 21011 9 1 1 1 2 2 2 0 0 0 0 1 1 1 2 2 2 0 0 0 0 1 1 1 2 2 2 0 0 0 0 1 1 1 2 2 2 197 21021 20 1 2 1 0 1 1 1 0 0 0 2 1 2 0 2 2 2 0 0 0 0 1 2 1 0 1 1 1 0 0 0 2 1 2 0 2 2 2 0 0 0 0 1 2 1 0 1 1 1 0 0 0 2 1 2 0 2 2 2 0 0 0 0 1 2 1 0 1 1 1 0 0 0 2 1 2 0 2 2 2 21101 13 1 0 1 1 0 2 0 1 2 2 0 0 0 0 1 0 1 1 0 2 0 1 2 2 0 0 0 0 1 0 1 1 0 2 0 1 2 2 0 0 0 0 1 0 1 1 0 2 0 1 2 2 21111 12 1 1 2 1 0 2 1 2 2 0 0 0 0 1 1 2 1 0 2 1 2 2 0 0 0 0 1 1 2 1 0 2 1 2 2 0 0 0 0 1 1 2 1 0 2 1 2 2 21121 24 1 2 2 1 2 2 2 1 1 0 0 0 2 1 1 2 1 1 1 2 2 0 0 0 0 1 2 2 1 2 2 2 1 1 0 0 0 2 1 1 2 1 1 1 2 2 0 0 0 0 1 2 2 1 2 2 2 1 1 0 0 0 2 1 1 2 1 1 1 2 2 0 0 0 0 1 2 2 1 2 2 2 1 1 0 0 0 2 1 1 2 1 1 1 2 2 21201 40 1 0 2 1 0 1 2 1 2 0 0 1 1 2 0 1 1 0 0 0 2 0 1 2 0 2 1 2 1 0 0 2 2 1 0 2 2 0 0 0 0 1 0 2 1 0 1 2 1 2 0 0 1 1 2 0 1 1 0 0 0 2 0 1 2 0 2 1 2 1 0 0 2 2 1 0 2 2 0 0 0 0 1 0 2 1 0 1 2 1 2 0 0 1 1 2 0 1 1 0 0 0 2 0 1 2 0 2 1 2 1 0 0 2 2 1 0 2 2 0 0 0 0 1 0 2 1 0 1 2 1 2 0 0 1 1 2 0 1 1 0 0 0 2 0 1 2 0 2 1 2 1 0 0 2 2 1 0 2 2 21211 10 1 1 0 0 0 2 2 0 0 0 0 1 1 0 0 0 2 2 0 0 0 0 1 1 0 0 0 2 2 0 0 0 0 1 1 0 0 0 2 2 21221 8 1 2 0 2 2 0 0 0 0 1 2 0 2 2 0 0 0 0 1 2 0 2 2 0 0 0 0 1 2 0 2 2 22001 13 1 0 0 2 2 0 1 2 1 2 0 0 0 0 1 0 0 2 2 0 1 2 1 2 0 0 0 0 1 0 0 2 2 0 1 2 1 2 0 0 0 0 1 0 0 2 2 0 1 2 1 2 22011 20 1 1 1 0 1 2 1 0 0 0 2 2 2 0 2 1 2 0 0 0 0 1 1 1 0 1 2 1 0 0 0 2 2 2 0 2 1 2 0 0 0 0 1 1 1 0 1 2 1 0 0 0 2 2 2 0 2 1 2 0 0 0 0 1 1 1 0 1 2 1 0 0 0 2 2 2 0 2 1 2 22021 18 1 2 1 1 2 1 0 0 0 2 1 2 2 1 2 0 0 0 0 1 2 1 1 2 1 0 0 0 2 1 2 2 1 2 0 0 0 0 1 2 1 1 2 1 0 0 0 2 1 2 2 1 2 0 0 0 0 1 2 1 1 2 1 0 0 0 2 1 2 2 1 2 198 22101 26 1 0 1 2 0 1 0 2 2 1 0 0 0 2 0 2 1 0 2 0 1 1 2 0 0 0 0 1 0 1 2 0 1 0 2 2 1 0 0 0 2 0 2 1 0 2 0 1 1 2 0 0 0 0 1 0 1 2 0 1 0 2 2 1 0 0 0 2 0 2 1 0 2 0 1 1 2 0 0 0 0 1 0 1 2 0 1 0 2 2 1 0 0 0 2 0 2 1 0 2 0 1 1 2 22111 24 1 1 2 2 2 1 2 2 1 0 0 0 2 2 1 1 1 2 1 1 2 0 0 0 0 1 1 2 2 2 1 2 2 1 0 0 0 2 2 1 1 1 2 1 1 2 0 0 0 0 1 1 2 2 2 1 2 2 1 0 0 0 2 2 1 1 1 2 1 1 2 0 0 0 0 1 1 2 2 2 1 2 2 1 0 0 0 2 2 1 1 1 2 1 1 2 22121 12 1 2 2 2 0 1 1 1 2 0 0 0 0 1 2 2 2 0 1 1 1 2 0 0 0 0 1 2 2 2 0 1 1 1 2 0 0 0 0 1 2 2 2 0 1 1 1 2 22201 40 1 0 2 2 0 2 2 2 2 0 0 2 1 1 0 2 1 0 0 0 2 0 1 1 0 1 1 1 1 0 0 1 2 2 0 1 2 0 0 0 0 1 0 2 2 0 2 2 2 2 0 0 2 1 1 0 2 1 0 0 0 2 0 1 1 0 1 1 1 1 0 0 1 2 2 0 1 2 0 0 0 0 1 0 2 2 0 2 2 2 2 0 0 2 1 1 0 2 1 0 0 0 2 0 1 1 0 1 1 1 1 0 0 1 2 2 0 1 2 0 0 0 0 1 0 2 2 0 2 2 2 2 0 0 2 1 1 0 2 1 0 0 0 2 0 1 1 0 1 1 1 1 0 0 1 2 2 0 1 2 22211 8 1 1 0 1 2 0 0 0 0 1 1 0 1 2 0 0 0 0 1 1 0 1 2 0 0 0 0 1 1 0 1 2 22221 5 1 2 0 0 0 0 1 2 0 0 0 0 1 2 0 0 0 0 1 2 10002 4 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0 2 10012 80 22221021221210101122010220021110120010001111201211212020221102011001222021002000 02222102122121010112201022002111012001000111120121121202022110201100122202100200 00222210212212101011220102200211101200100011112012112120202211020110012220210020 00022221021221210101122010220021110120010001111201211212020221102011001222021002 10022 80 21211022211110101221020120011210110010001212201112222020211201021002212022002000 02121102221111010122102012001121011001000121220111222202021120102100221202200200 00212110222111101012210201200112101100100012122011122220202112010210022120220020 00021211022211110101221020120011210110010001212201112222020211201021002212022002 10102 16 2 0 2 0 1 0 0 0 1 0 1 0 2 0 0 0 0 2 0 2 0 1 0 0 0 1 0 1 0 2 0 0 0 0 2 0 2 0 1 0 0 0 1 0 1 0 2 0 0 0 0 2 0 2 0 1 0 0 0 1 0 1 0 2 199 10112 26 2 2 1 0 0 2 0 2 2 0 2 1 2 0 1 2 2 1 1 1 1 0 2 0 0 0 0 2 2 1 0 0 2 0 2 2 0 2 1 2 0 1 2 2 1 1 1 1 0 2 0 0 0 0 2 2 1 0 0 2 0 2 2 0 2 1 2 0 1 2 2 1 1 1 1 0 2 0 0 0 0 2 2 1 0 0 2 0 2 2 0 2 1 2 0 1 2 2 1 1 1 1 0 2 10122 26 2 1 1 0 0 1 0 1 2 0 2 2 2 0 1 1 2 2 1 2 1 0 2 0 0 0 0 2 1 1 0 0 1 0 1 2 0 2 2 2 0 1 1 2 2 1 2 1 0 2 0 0 0 0 2 1 1 0 0 1 0 1 2 0 2 2 2 0 1 1 2 2 1 2 1 0 2 0 0 0 0 2 1 1 0 0 1 0 1 2 0 2 2 2 0 1 1 2 2 1 2 1 0 2 10202 16 2 0 1 0 1 0 0 0 1 0 2 0 2 0 0 0 0 2 0 1 0 1 0 0 0 1 0 2 0 2 0 0 0 0 2 0 1 0 1 0 0 0 1 0 2 0 2 0 0 0 0 2 0 1 0 1 0 0 0 1 0 2 0 2 10212 24 2 2 0 1 0 1 1 1 0 0 1 2 1 2 2 2 1 1 2 0 2 0 0 0 0 2 2 0 1 0 1 1 1 0 0 1 2 1 2 2 2 1 1 2 0 2 0 0 0 0 2 2 0 1 0 1 1 1 0 0 1 2 1 2 2 2 1 1 2 0 2 0 0 0 0 2 2 0 1 0 1 1 1 0 0 1 2 1 2 2 2 1 1 2 0 2 10222 24 2 1 0 2 0 2 1 2 0 0 1 1 1 1 2 1 1 2 2 0 2 0 0 0 0 2 1 0 2 0 2 1 2 0 0 1 1 1 1 2 1 1 2 2 0 2 0 0 0 0 2 1 0 2 0 2 1 2 0 0 1 1 1 1 2 1 1 2 2 0 2 0 0 0 0 2 1 0 2 0 2 1 2 0 0 1 1 1 1 2 1 1 2 2 0 2 11002 80 20022021220012010211202022221110221210001001101211002102012210101111222011212000 02002202122001201021120202222111022121000100110121100210201221010111122201121200 00200220212200120102112020222211102212100010011012110021020122101011112220112120 00020022021220012010211202022221110221210001001101211002102012210101111222011212 11012 8 2 2 2 1 2 0 0 0 0 2 2 2 1 2 0 0 0 0 2 2 2 1 2 0 0 0 0 2 2 2 1 2 11022 6 2 1 2 0 0 0 0 2 1 2 0 0 0 0 2 1 2 0 0 0 0 2 1 2 11102 24 2 0 2 2 1 1 2 1 1 1 1 0 0 2 1 2 0 2 0 1 2 0 0 0 0 2 0 2 2 1 1 2 1 1 1 1 0 0 2 1 2 0 2 0 1 2 0 0 0 0 2 0 2 2 1 1 2 1 1 1 1 0 0 2 1 2 0 2 0 1 2 0 0 0 0 2 0 2 2 1 1 2 1 1 1 1 0 0 2 1 2 0 2 0 1 2 11112 26 200 2 2 1 2 1 0 1 1 0 2 1 1 1 2 2 0 2 0 1 0 0 1 2 0 0 0 0 2 2 1 2 1 0 1 1 0 2 1 1 1 2 2 0 2 0 1 0 0 1 2 0 0 0 0 2 2 1 2 1 0 1 1 0 2 1 1 1 2 2 0 2 0 1 0 0 1 2 0 0 0 0 2 2 1 2 1 0 1 1 0 2 1 1 1 2 2 0 2 0 1 0 0 1 2 11122 80 21122200110200202011201212202222120210001221110022010010102210212110111121012000 02112220011020020201120121220222212021000122111002201001010221021211011112101200 00211222001102002020112012122022221202100012211100220100101022102121101111210120 00021122200110200202011201212202222120210001221110022010010102210212110111121012 11202 26 2 0 1 2 1 2 2 1 1 0 2 2 2 0 2 1 0 1 0 0 1 1 2 0 0 0 0 2 0 1 2 1 2 2 1 1 0 2 2 2 0 2 1 0 1 0 0 1 1 2 0 0 0 0 2 0 1 2 1 2 2 1 1 0 2 2 2 0 2 1 0 1 0 0 1 1 2 0 0 0 0 2 0 1 2 1 2 2 1 1 0 2 2 2 0 2 1 0 1 0 0 1 1 2 11212 26 2 2 0 0 1 0 2 0 2 1 1 2 1 1 0 2 1 0 1 1 1 1 2 0 0 0 0 2 2 0 0 1 0 2 0 2 1 1 2 1 1 0 2 1 0 1 1 1 1 2 0 0 0 0 2 2 0 0 1 0 2 0 2 1 1 2 1 1 0 2 1 0 1 1 1 1 2 0 0 0 0 2 2 0 0 1 0 2 0 2 1 1 2 1 1 0 2 1 0 1 1 1 1 2 11222 80 21012111101121201220101001022001112210001202122220221210211020200201100222112000 02101211110112120122010100102200111221000120212222022121021102020020110022211200 00210121111011212012201010010220011122100012021222202212102110202002011002221120 00021012111101121201220101001022001112210001202122220221210211020200201100222112 12002 80 20012022210011020112202021211210211110001002101112002201022110101212212012222000 02001202221001102011220202121121021111000100210111200220102211010121221201222200 00200120222100110201122020212112102111100010021011120022010221101012122120122220 00020012022210011020112202021211210211110001002101112002201022110101212212012222 12012 6 2 2 2 0 0 0 0 2 2 2 0 0 0 0 2 2 2 0 0 0 0 2 2 2 12022 8 2 1 2 2 2 0 0 0 0 2 1 2 2 2 0 0 0 0 2 1 2 2 2 0 0 0 0 2 1 2 2 2 12102 24 2 0 2 1 1 2 2 2 1 2 1 0 0 1 1 1 0 1 0 2 2 0 0 0 0 2 0 2 1 1 2 2 2 1 2 1 0 0 1 1 1 0 1 0 2 2 0 0 0 0 2 0 2 1 1 2 2 2 1 2 1 0 0 1 1 1 0 1 0 2 2 0 0 0 0 2 0 2 1 1 2 2 2 1 2 1 0 0 1 1 1 0 1 0 2 2 12112 80 22112100120100202012201111202121110110001122120021020010102110222210121222022000 02211210012010020201220111120212111011000112212002102001010211022221012122202200 00221121001201002020122011112021211101100011221200210200101021102222101212220220 00022112100120100202012201111202121110110001122120021020010102110222210121222022 201 12122 26 2 1 1 1 1 0 1 2 0 1 1 2 1 1 2 0 2 0 1 0 0 2 2 0 0 0 0 2 1 1 1 1 0 1 2 0 1 1 2 1 1 2 0 2 0 1 0 0 2 2 0 0 0 0 2 1 1 1 1 0 1 2 0 1 1 2 1 1 2 0 2 0 1 0 0 2 2 0 0 0 0 2 1 1 1 1 0 1 2 0 1 1 2 1 1 2 0 2 0 1 0 0 2 2 12202 26 2 0 1 1 1 1 2 2 1 0 2 1 2 0 2 2 0 2 0 0 1 2 2 0 0 0 0 2 0 1 1 1 1 2 2 1 0 2 1 2 0 2 2 0 2 0 0 1 2 2 0 0 0 0 2 0 1 1 1 1 2 2 1 0 2 1 2 0 2 2 0 2 0 0 1 2 2 0 0 0 0 2 0 1 1 1 1 2 2 1 0 2 1 2 0 2 2 0 2 0 0 1 2 2 12212 80 22022212101222201120101002012002122110001101112120211110221020200102100121122000 02202221210122220112010100201200212211000110111212021111022102020010210012112200 00220222121012222011201010020120021221100011011121202111102210202001021001211220 00022022212101222201120101002012002122110001101112120211110221020200102100121122 12222 26 2 1 0 0 1 0 2 0 2 2 1 1 1 2 0 1 1 0 1 2 1 2 2 0 0 0 0 2 1 0 0 1 0 2 0 2 2 1 1 1 2 0 1 1 0 1 2 1 2 2 0 0 0 0 2 1 0 0 1 0 2 0 2 2 1 1 1 2 0 1 1 0 1 2 1 2 2 0 0 0 0 2 1 0 0 1 0 2 0 2 2 1 1 1 2 0 1 1 0 1 2 1 2 2 20002 8 2 0 0 0 1 0 0 0 0 2 0 0 0 1 0 0 0 0 2 0 0 0 1 0 0 0 0 2 0 0 0 1 20012 26 2 2 2 2 0 1 2 0 0 2 0 0 0 1 1 1 1 0 2 1 0 0 1 0 0 0 0 2 2 2 2 0 1 2 0 0 2 0 0 0 1 1 1 1 0 2 1 0 0 1 0 0 0 0 2 2 2 2 0 1 2 0 0 2 0 0 0 1 1 1 1 0 2 1 0 0 1 0 0 0 0 2 2 2 2 0 1 2 0 0 2 0 0 0 1 1 1 1 0 2 1 0 0 1 20022 13 2 1 2 1 0 2 2 0 0 1 0 0 0 0 2 1 2 1 0 2 2 0 0 1 0 0 0 0 2 1 2 1 0 2 2 0 0 1 0 0 0 0 2 1 2 1 0 2 2 0 0 1 20102 12 2 0 2 0 0 0 1 0 1 0 0 0 0 2 0 2 0 0 0 1 0 1 0 0 0 0 2 0 2 0 0 0 1 0 1 0 0 0 0 2 0 2 0 0 0 1 0 1 20112 13 2 2 1 0 2 0 1 1 0 1 0 0 0 0 2 2 1 0 2 0 1 1 0 1 0 0 0 0 2 2 1 0 2 0 1 1 0 1 0 0 0 0 2 2 1 0 2 0 1 1 0 1 20122 26 202 2 1 1 0 2 0 1 2 0 2 0 0 0 1 2 2 0 1 0 2 1 0 1 0 0 0 0 2 1 1 0 2 0 1 2 0 2 0 0 0 1 2 2 0 1 0 2 1 0 1 0 0 0 0 2 1 1 0 2 0 1 2 0 2 0 0 0 1 2 2 0 1 0 2 1 0 1 0 0 0 0 2 1 1 0 2 0 1 2 0 2 0 0 0 1 2 2 0 1 0 2 1 0 1 20202 6 2 0 1 0 0 0 0 2 0 1 0 0 0 0 2 0 1 0 0 0 0 2 0 1 20212 40 2 2 0 1 2 2 0 0 1 2 1 2 0 2 1 0 2 0 0 0 1 1 0 2 1 1 0 0 2 1 2 1 0 1 2 0 1 0 0 0 0 2 2 0 1 2 2 0 0 1 2 1 2 0 2 1 0 2 0 0 0 1 1 0 2 1 1 0 0 2 1 2 1 0 1 2 0 1 0 0 0 0 2 2 0 1 2 2 0 0 1 2 1 2 0 2 1 0 2 0 0 0 1 1 0 2 1 1 0 0 2 1 2 1 0 1 2 0 1 0 0 0 0 2 2 0 1 2 2 0 0 1 2 1 2 0 2 1 0 2 0 0 0 1 1 0 2 1 1 0 0 2 1 2 1 0 1 2 0 1 20222 40 2 1 0 2 2 1 0 0 1 1 1 1 0 1 1 0 2 0 0 0 1 2 0 1 1 2 0 0 2 2 2 2 0 2 2 0 1 0 0 0 0 2 1 0 2 2 1 0 0 1 1 1 1 0 1 1 0 2 0 0 0 1 2 0 1 1 2 0 0 2 2 2 2 0 2 2 0 1 0 0 0 0 2 1 0 2 2 1 0 0 1 1 1 1 0 1 1 0 2 0 0 0 1 2 0 1 1 2 0 0 2 2 2 2 0 2 2 0 1 0 0 0 0 2 1 0 2 2 1 0 0 1 1 1 1 0 1 1 0 2 0 0 0 1 2 0 1 1 2 0 0 2 2 2 2 0 2 2 0 1 21002 26 2 0 0 2 1 0 2 2 2 2 0 0 0 1 0 0 1 2 0 1 1 1 1 0 0 0 0 2 0 0 2 1 0 2 2 2 2 0 0 0 1 0 0 1 2 0 1 1 1 1 0 0 0 0 2 0 0 2 1 0 2 2 2 2 0 0 0 1 0 0 1 2 0 1 1 1 1 0 0 0 0 2 0 0 2 1 0 2 2 2 2 0 0 0 1 0 0 1 2 0 1 1 1 1 21012 9 2 2 2 1 1 1 0 0 0 0 2 2 2 1 1 1 0 0 0 0 2 2 2 1 1 1 0 0 0 0 2 2 2 1 1 1 21022 20 2 1 2 0 2 2 2 0 0 0 1 2 1 0 1 1 1 0 0 0 0 2 1 2 0 2 2 2 0 0 0 1 2 1 0 1 1 1 0 0 0 0 2 1 2 0 2 2 2 0 0 0 1 2 1 0 1 1 1 0 0 0 0 2 1 2 0 2 2 2 0 0 0 1 2 1 0 1 1 1 21102 13 2 0 2 2 0 1 0 2 1 1 0 0 0 0 2 0 2 2 0 1 0 2 1 1 0 0 0 0 2 0 2 2 0 1 0 2 1 1 0 0 0 0 2 0 2 2 0 1 0 2 1 1 21112 12 2 2 1 2 0 1 2 1 1 0 0 0 0 2 2 1 2 0 1 2 1 1 0 0 0 0 2 2 1 2 0 1 2 1 1 0 0 0 0 2 2 1 2 0 1 2 1 1 21122 24 2 1 1 2 1 1 1 2 2 0 0 0 1 2 2 1 2 2 2 1 1 0 0 0 0 2 1 1 2 1 1 1 2 2 0 0 0 1 2 2 1 2 2 2 1 1 0 0 0 0 2 1 1 2 1 1 1 2 2 0 0 0 1 2 2 1 2 2 2 1 1 0 0 0 0 2 1 1 2 1 1 1 2 2 0 0 0 1 2 2 1 2 2 2 1 1 203 21202 40 2 0 1 2 0 2 1 2 1 0 0 2 2 1 0 2 2 0 0 0 1 0 2 1 0 1 2 1 2 0 0 1 1 2 0 1 1 0 0 0 0 2 0 1 2 0 2 1 2 1 0 0 2 2 1 0 2 2 0 0 0 1 0 2 1 0 1 2 1 2 0 0 1 1 2 0 1 1 0 0 0 0 2 0 1 2 0 2 1 2 1 0 0 2 2 1 0 2 2 0 0 0 1 0 2 1 0 1 2 1 2 0 0 1 1 2 0 1 1 0 0 0 0 2 0 1 2 0 2 1 2 1 0 0 2 2 1 0 2 2 0 0 0 1 0 2 1 0 1 2 1 2 0 0 1 1 2 0 1 1 21212 10 2 2 0 0 0 1 1 0 0 0 0 2 2 0 0 0 1 1 0 0 0 0 2 2 0 0 0 1 1 0 0 0 0 2 2 0 0 0 1 1 21222 8 2 1 0 1 1 0 0 0 0 2 1 0 1 1 0 0 0 0 2 1 0 1 1 0 0 0 0 2 1 0 1 1 22002 13 2 0 0 1 1 0 2 1 2 1 0 0 0 0 2 0 0 1 1 0 2 1 2 1 0 0 0 0 2 0 0 1 1 0 2 1 2 1 0 0 0 0 2 0 0 1 1 0 2 1 2 1 22012 20 2 2 2 0 2 1 2 0 0 0 1 1 1 0 1 2 1 0 0 0 0 2 2 2 0 2 1 2 0 0 0 1 1 1 0 1 2 1 0 0 0 0 2 2 2 0 2 1 2 0 0 0 1 1 1 0 1 2 1 0 0 0 0 2 2 2 0 2 1 2 0 0 0 1 1 1 0 1 2 1 22022 18 2 1 2 2 1 2 0 0 0 1 2 1 1 2 1 0 0 0 0 2 1 2 2 1 2 0 0 0 1 2 1 1 2 1 0 0 0 0 2 1 2 2 1 2 0 0 0 1 2 1 1 2 1 0 0 0 0 2 1 2 2 1 2 0 0 0 1 2 1 1 2 1 22102 26 2 0 2 1 0 2 0 1 1 2 0 0 0 1 0 1 2 0 1 0 2 2 1 0 0 0 0 2 0 2 1 0 2 0 1 1 2 0 0 0 1 0 1 2 0 1 0 2 2 1 0 0 0 0 2 0 2 1 0 2 0 1 1 2 0 0 0 1 0 1 2 0 1 0 2 2 1 0 0 0 0 2 0 2 1 0 2 0 1 1 2 0 0 0 1 0 1 2 0 1 0 2 2 1 22112 24 2 2 1 1 1 2 1 1 2 0 0 0 1 1 2 2 2 1 2 2 1 0 0 0 0 2 2 1 1 1 2 1 1 2 0 0 0 1 1 2 2 2 1 2 2 1 0 0 0 0 2 2 1 1 1 2 1 1 2 0 0 0 1 1 2 2 2 1 2 2 1 0 0 0 0 2 2 1 1 1 2 1 1 2 0 0 0 1 1 2 2 2 1 2 2 1 22122 12 2 1 1 1 0 2 2 2 1 0 0 0 0 2 1 1 1 0 2 2 2 1 0 0 0 0 2 1 1 1 0 2 2 2 1 0 0 0 0 2 1 1 1 0 2 2 2 1 22202 40 204 2011011110012201200010220222200211021000 0201101111001220120001022022220021102100 0020110111100122012000102202222002110210 0002011011110012201200010220222200211021 22212 8 22021000 02202100 00220210 00022021 22222 5 21000 02100 00210 00021 205 Додаток Г Поліноми degP(x)=5 у GF(3) та їх періоди 100001 5 100011 78 100021 121 100101 104 100111 121 100121 24 100201 80 100211 40 100221 52 101001 80 101011 121 101021 24 101101 121 101111 26 101121 121 101201 12 101211 104 101221 78 102001 104 102011 39 102021 80 102101 18 102111 121 102121 11 102201 121 102211 8 102221 20 110001 121 110011 121 110021 12 110101 80 110111 39 110121 121 110201 24 110211 80 110221 104 111001 52 111011 26 111021 104 111101 20 111111 104 111121 16 111201 78 111211 121 111221 9 112001 24 112011 52 112021 121 112101 11 112111 24 112121 10 112201 121 112211 40 112221 16 120001 78 120011 8 120021 121 120101 39 120111 80 120121 52 120201 121 120211 121 120221 26 121001 40 121011 121 121021 80 121101 8 121111 121 121121 40 121201 104 121211 6 121221 121 122001 121 122011 80 122021 39 122101 121 122111 12 122121 24 122201 26 122211 121 122221 104 200001 10 200011 242 200021 78 200101 104 200111 24 200121 242 200201 80 200211 52 200221 40 201001 104 201011 80 201021 78 201101 18 201111 22 201121 242 201201 242 201211 20 201221 8 202001 80 202011 24 202021 242 202101 242 202111 242 202121 26 202201 12 202211 78 202221 104 210001 242 210011 12 210021 242 210101 80 210111 242 210121 78 210201 24 210211 104 210221 80 211001 24 211011 242 211021 52 211101 22 211111 10 211121 24 211201 242 211211 16 211221 40 212001 52 212011 104 212021 26 212101 20 212111 16 212121 104 212201 78 212211 18 212221 242 220001 78 220011 242 220021 8 220101 78 220111 52 220121 80 220201 242 220211 26 220221 242 221001 242 221011 78 221021 80 221101 242 221111 24 221121 12 221201 26 221211 104 221221 242 222001 40 222011 80 222021 242 222101 8 222111 40 222121 242 222201 104 222211 242 222221 6 100002 5 100012 78 100022 121 100102 104 100112 121 100122 24 100202 80 100212 40 100222 52 101002 80 101012 121 101022 24 101102 121 101112 26 101122 121 101202 12 101212 104 101222 78 102002 104 102012 39 102022 80 102102 18 206 102112 121 102122 11 102202 121 102212 8 102222 20 110002 121 110012 121 110022 12 110102 80 110112 39 110122 121 110202 24 110212 80 110222 104 111002 52 111012 26 111022 104 111102 20 111112 104 111122 16 111202 78 111212 121 111222 9 112002 24 112012 52 112022 121 112102 11 112112 24 112122 10 112202 121 112212 40 112222 16 120002 78 120012 8 120022 121 120102 39 120112 80 120122 52 120202 121 120212 121 120222 26 121002 40 121012 121 121022 80 121102 8 121112 121 121122 40 121202 104 121212 6 121222 121 122002 121 122012 80 122022 39 122102 121 122112 12 122122 24 122202 26 122212 121 122222 104 200002 10 200012 242 200022 78 200102 104 200112 24 200122 242 200202 80 200212 52 200222 40 201002 104 201012 80 201022 78 201102 18 201112 22 201122 242 201202 242 201212 20 201222 8 202002 80 202012 24 202022 242 202102 242 202112 242 202122 26 202202 12 202212 78 202222 104 210002 242 210012 12 210022 242 210102 80 210112 242 210122 78 210202 24 210212 104 210222 80 211002 24 211012 242 211022 52 211102 22 211112 10 211122 24 211202 242 211212 16 211222 40 212002 52 212012 104 212022 26 212102 20 212112 16 212122 104 212202 78 212212 18 212222 242 220002 78 220012 242 220022 8 220102 78 220112 52 220122 80 220202 242 220212 26 220222 242 221002 242 221012 78 221022 80 221102 242 221112 24 221122 12 221202 26 221212 104 221222 242 222002 40 222012 80 222022 242 222102 8 222112 40 222122 242 222202 104 222212 242 222222 6 207 Додаток Д Поліноми degP(x)=6 у GF(3) та їх періоди 1000211 104 1000221 104 1001001 24 1001011 728 1001021 40 1001101 242 1001111 24 1001121 104 1001201 242 1001211 240 1001221 26 1002001 24 1002011 40 1002021 728 1002101 242 1002111 104 1002121 24 1002201 242 1002211 26 1002221 240 1010001 26 1010011 72 1010021 72 1010101 26 1010111 242 1010121 242 1010201 12 1010211 728 1010221 728 1011001 242 1011011 242 1011021 728 1011101 240 1011111 104 1011121 728 1011201 40 1011211 80 1011221 78 1012001 242 1012011 728 1012021 242 1012101 240 1012111 728 1012121 104 1012201 40 1012211 78 1012221 80 1020001 16 1020011 78 1020021 78 1020101 8; 1020111 728 1020121 728 1020201 26 1020211 242 1020221 242 1021001 242 1021011 52 1021021 40 1021101 56 1021111 728 1021121 78 1021201 240 1021211 242 1021221 40 1022001 242 1022011 40 1022021 52 1022101 56 1022111 78 1022121 728 1022201 240 1022211 40 1022221 242 1100001 728 1100011 56 1100021 10 1100101 78 1100111 22 1100121 80 1100201 72 1100211 242 1100221 40 1101001 728 1101011 24 1101021 26 1101101 52 1101111 728 1101121 104 1101201 242 1101211 728 1101221 242 1102001 40 1102011 40 1102021 26 1102101 40 1102111 52 1102121 48 1102201 728 1102211 242 1102221 240 1110001 104 1110011 242 1110021 40 1110101 242 1110111 78 1110121 728 1110201 728 1110211 26 1110221 12 1111001 240 1111011 728 1111021 240 1111101 242 1111111 728 1111121 104 1111201 80 1111211 24 1111221 728 1112001 26 1112011 242 1112021 242 1112101 40 1112111 240 1112121 242 1112201 78 1112211 24 1112221 728 1120001 242 1120011 22 1120021 80 1120101 728 1120111 24 1120121 18 1120201 242 1120211 78 1120221 728 1121001 24 1121011 728 1121021 48 1121101 728 1121111 20 1121121 104 1121201 104 1121211 728 1121221 242 1122001 104 1122011 52 1122021 104 1122101 78 1122111 26 1122121 104 1122201 728 1122211 240 1122221 104 1200001 728 1200011 10 1200021 56 1200101 78 1200111 80 1200121 22 1200201 72 1200211 40 1200221 242 1201001 40 1201011 26 1201021 40 1201101 40 1201111 48 1201121 52 1201201 728 1201211 240 1201221 242 1202001 728 1202011 26 1202021 24 1202101 52 1202111 104 1202121 728 1202201 242 1202211 242 1202221 728 1210001 104 1210011 40 208 1210021 242 1210101 242 1210111 728 1210121 78 1210201 728 1210211 12 1210221 26 1211001 26 1211011 242 1211021 242 1211101 40 1211111 242 1211121 240 1211201 78 1211211 728 1211221 24 1212001 240 1212011 240 1212021 728 1212101 242 1212111 104 1212121 728 1212201 80 1212211 728 1212221 24 1220001 242 1220011 80 1220021 22 1220101 728 1220111 18 1220121 24 1220201 242 1220211 728 1220221 78 1221001 104 1221011 104 1221021 52 1221101 78 1221111 104 1221121 26 1221201 728 1221211 104 1221221 240 1222001 24 1222011 48 1222021 728 1222101 728 1222111 104 1222121 20 1222201 104 1222211 242 1222221 728 2000001 12 2000011 104 2000021 104 2000101 52 2000111 60 2000121 60 2000201 16 2000211 364 2000221 364 2001001 18 2001011 52 2001021 364 2001101 104 2001111 242 2001121 91 2001201 182 2001211 80 2001221 78 2002001 9; 2002011 364 2002021 52 2002101 104 2002111 182 2002121 121 2002201 91 2002211 78 2002221 80 2010001 52 2010011 121 2010021 242 2010101 12 2010111 80 2010121 80 2010201 52 2010211 242 2010221 121 2011001 104 2011011 78 2011021 364 2011101 13 2011111 40 2011121 80 2011201 242 2011211 24 2011221 182 2012001 104 2012011 364 2012021 39 2012101 26 2012111 80 2012121 40 2012201 121 2012211 91 2012221 24 2020001 16 2020011 26 2020021 13 2020101 52 2020111 120 2020121 120 2020201 8; 2020211 121 2020221 242 2021001 182 2021011 364 2021021 24 2021101 242 2021111 11 2021121 80 2021201 30 2021211 52 2021221 78 2022001 91 2022011 24 2022021 364 2022101 121 2022111 80 2022121 22 2022201 30 2022211 39 2022221 52 2100001 104 2100011 15 2100021 24 2100101 121 2100111 242 2100121 39 2100201 26 2100211 364 2100221 78 2101001 52 2101011 28 2101021 242 2101101 78 2101111 80 2101121 120 2101201 364 2101211 80 2101221 16 2102001 364 2102011 12 2102021 121 2102101 364 2102111 104 2102121 242 2102201 24 2102211 182 2102221 182 2110001 60 2110011 242 2110021 78 2110101 80 2110111 26 2110121 8; 2110201 120 2110211 121 2110221 182 2111001 242 2111011 80 2111021 121 2111101 40 2111111 18 2111121 364 2111201 11 2111211 13 2111221 104 2112001 182 2112011 104 2112021 120 2112101 80 2112111 20 2112121 364 2112201 80 2112211 242 2112221 39 2120001 364 2120011 364 2120021 78 2120101 242 2120111 121 2120121 91 2120201 121 2120211 24 2120221 20 2121001 80 2121011 80 2121021 91 2121101 24 2121111 13 2121121 78 2121201 52 2121211 14 209 2121221 121 2122001 78 2122011 182 2122021 16 2122101 91 2122111 242 2122121 104 2122201 39 2122211 36 2122221 242 2200001 104 2200011 24 2200021 30 2200101 242 2200111 78 2200121 121 2200201 13 2200211 78 2200221 364 2201001 364 2201011 242 2201021 12 2201101 364 2201111 121 2201121 104 2201201 24 2201211 91 2201221 91 2202001 52 2202011 121 2202021 28 2202101 39 2202111 120 2202121 80 2202201 364 2202211 16 2202221 80 2210001 60 2210011 39 2210021 121 2210101 80 2210111 8; 2210121 13 2210201 120 2210211 91 2210221 242 2211001 91 2211011 120 2211021 104 2211101 80 2211111 364 2211121 20 2211201 80 2211211 78 2211221 121 2212001 121 2212011 242 2212021 80 2212101 40 2212111 364 2212121 18 2212201 22 2212211 104 2212221 26 2220001 364 2220011 78 2220021 364 2220101 121 2220111 182 2220121 242 2220201 242 2220211 20 2220221 24 2221001 78 2221011 16 2221021 91 2221101 182 2221111 104 2221121 121 2221201 78 2221211 121 2221221 36 2222001 80 2222011 182 2222021 80 2222101 24 2222111 39 2222121 26 2222201 52 2222211 242 2222221 7; 1000002 6; 1000012 728 1000022 728 1000102 16 1000112 242 1000122 242 1000202 26 1000212 104 1000222 104 1001002 24 1001012 728 1001022 40 1001102 242 1001112 24 1001122 104 1001202 242 1001212 240 1001222 26 1002002 24 1002012 40 1002022 728 1002102 242 1002112 104 1002122 24 1002202 242 1002212 26 1002222 240 1010002 26 1010012 72 1010022 72 1010102 26 1010112 242 1010122 242 1010202 12 1010212 728 1010222 728 1011002 242 1011012 242 1011022 728 1011102 240 1011112 104 1011122 728 1011202 40 1011212 80 1011222 78 1012002 242 1012012 728 1012022 242 1012102 240 1012112 728 1012122 104 1012202 40 1012212 78 1012222 80 1020002 16 1020012 78 1020022 78 1020102 8; 1020112 728 1020122 728 1020202 26 1020212 242 1020222 242 1021002 242 1021012 52 1021022 40 1021102 56 1021112 728 1021122 78 1021202 240 1021212 242 1021222 40 1022002 242 1022012 40 1022022 52 1022102 56 1022112 78 1022122 728 1022202 240 1022212 40 1022222 242 1100002 728 1100012 56 1100022 10 1100102 78 1100112 22 1100122 80 1100202 72 1100212 242 1100222 40 1101002 728 1101012 24 1101022 26 1101102 52 1101112 728 1101122 104 1101202 242 1101212 728 1101222 242 1102002 40 1102012 40 1102022 26 1102102 40 1102112 52 1102122 48 1102202 728 1102212 242 1102222 240 1110002 104 1110012 242 1110022 40 1110102 242 1110112 78 210 1110122 728 1110202 728 1110212 26 1110222 12 1111002 240 1111012 728 1111022 240 1111102 242 1111112 728 1111122 104 1111202 80 1111212 24 1111222 728 1112002 26 1112012 242 1112022 242 1112102 40 1112112 240 1112122 242 1112202 78 1112212 24 1112222 728 1120002 242 1120012 22 1120022 80 1120102 728 1120112 24 1120122 18 1120202 242 1120212 78 1120222 728 1121002 24 1121012 728 1121022 48 1121102 728 1121112 20 1121122 104 1121202 104 1121212 728 1121222 242 1122002 104 1122012 52 1122022 104 1122102 78 1122112 26 1122122 104 1122202 728 1122212 240 1122222 104 1200002 728 1200012 10 1200022 56 1200102 78 1200112 80 1200122 22 1200202 72 1200212 40 1200222 242 1201002 40 1201012 26 1201022 40 1201102 40 1201112 48 1201122 52 1201202 728 1201212 240 1201222 242 1202002 728 1202012 26 1202022 24 1202102 52 1202112 104 1202122 728 1202202 242 1202212 242 1202222 728 1210002 104 1210012 40 1210022 242 1210102 242 1210112 728 1210122 78 1210202 728 1210212 12 1210222 26 1211002 26 1211012 242 1211022 242 1211102 40 1211112 242 1211122 240 1211202 78 1211212 728 1211222 24 1212002 240 1212012 240 1212022 728 1212102 242 1212112 104 1212122 728 1212202 80 1212212 728 1212222 24 1220002 242 1220012 80 1220022 22 1220102 728 1220112 18 1220122 24 1220202 242 1220212 728 1220222 78 1221002 104 1221012 104 1221022 52 1221102 78 1221112 104 1221122 26 1221202 728 1221212 104 1221222 240 1222002 24 1222012 48 1222022 728 1222102 728 1222112 104 1222122 20 1222202 104 1222212 242 1222222 728 2000002 12 2000012 104 2000022 104 2000102 52 2000112 60 2000122 60 2000202 16 2000212 364 2000222 364 2001002 18 2001012 52 2001022 364 2001102 104 2001112 242 2001122 91 2001202 182 2001212 80 2001222 78 2002002 9; 2002012 364 2002022 52 2002102 104 2002112 182 2002122 121 2002202 91 2002212 78 2002222 80 2010002 52 2010012 121 2010022 242 2010102 12 2010112 80 2010122 80 2010202 52 2010212 242 2010222 121 2011002 104 2011012 78 2011022 364 2011102 13 2011112 40 2011122 80 2011202 242 2011212 24 2011222 182 2012002 104 2012012 364 2012022 39 2012102 26 2012112 80 2012122 40 2012202 121 2012212 91 2012222 24 2020002 16 2020012 26 2020022 13 2020102 52 2020112 120 2020122 120 2020202 8; 2020212 121 2020222 242 2021002 182 2021012 364 2021022 24 2021102 242 2021112 11 2021122 80 2021202 30 2021212 52 2021222 78 2022002 91 2022012 24 211 2022022 364 2022102 121 2022112 80 2022122 22 2022202 30 2022212 39 2022222 52 2100002 104 2100012 15 2100022 24 2100102 121 2100112 242 2100122 39 2100202 26 2100212 364 2100222 78 2101002 52 2101012 28 2101022 242 2101102 78 2101112 80 2101122 120 2101202 364 2101212 80 2101222 16 2102002 364 2102012 12 2102022 121 2102102 364 2102112 104 2102122 242 2102202 24 2102212 182 2102222 182 2110002 60 2110012 242 2110022 78 2110102 80 2110112 26 2110122 8; 2110202 120 2110212 121 2110222 182 2111002 242 2111012 80 2111022 121 2111102 40 2111112 18 2111122 364 2111202 11 2111212 13 2111222 104 2112002 182 2112012 104 2112022 120 2112102 80 2112112 20 2112122 364 2112202 80 2112212 242 2112222 39 2120002 364 2120012 364 2120022 78 2120102 242 2120112 121 2120122 91 2120202 121 2120212 24 2120222 20 2121002 80 2121012 80 2121022 91 2121102 24 2121112 13 2121122 78 2121202 52 2121212 14 2121222 121 2122002 78 2122012 182 2122022 16 2122102 91 2122112 242 2122122 104 2122202 39 2122212 36 2122222 242 2200002 104 2200012 24 2200022 30 2200102 242 2200112 78 2200122 121 2200202 13 2200212 78 2200222 364 2201002 364 2201012 242 2201022 12 2201102 364 2201112 121 2201122 104 2201202 24 2201212 91 2201222 91 2202002 52 2202012 121 2202022 28 2202102 39 2202112 120 2202122 80 2202202 364 2202212 16 2202222 80 2210002 60 2210012 39 2210022 121 2210102 80 2210112 8; 2210122 13 2210202 120 2210212 91 2210222 242 2211002 91 2211012 120 2211022 104 2211102 80 2211112 364 2211122 20 2211202 80 2211212 78 2211222 121 2212002 121 2212012 242 2212022 80 2212102 40 2212112 364 2212122 18 2212202 22 2212212 104 2212222 26 2220002 364 2220012 78 2220022 364 2220102 121 2220112 182 2220122 242 2220202 242 2220212 20 2220222 24 2221002 78 2221012 16 2221022 91 2221102 182 2221112 104 2221122 121 2221202 78 2221212 121 2221222 36 2222002 80 2222012 182 2222022 80 2222102 24 2222112 39 2222122 26 2222202 52 2222212 242 2222222 7 212 СПИСОК ЛІТЕРАТУРИ 1. Методичні вказівки для виконання лабораторних робіт з курсу "Низькорівневе програмування апаратних засобів", "Розробка та застосування маніфесту додатка Win32. Середовище програмування masm64" для студентів спеціальності: 123 - "Комп'ютерна інженерія" всіх форм навчання/упорядник О.М. Рисований. - Х.: НТУ "ХПІ", 2024. - 64 с. 2. Методичні вказівки для виконання практичних та лабораторних робіт з навчальної дисципліни «Низькорівневе програмування апаратних засобів» «Управління комп'ютером. Командна оболонка» для студентів спеціальності: 123–"Комп'ютерна інженерія" всіх форм навчання [електронне видання] / укладач О.М. Рисований.-Х.: НТУ "ХПІ", 2024. - 74 с. 3. Методичні вказівки для виконання практичних та лабораторних робіт з курсу «Реверсне програмування. Антиналагоджувальні прийоми захисту від реверсу. Середовище програмування masm64 для студентів спеціальності: 123 - "Комп'ютерна інженерія" всіх форм навчання [електронне видання] / укладач О.М. Рисований. - Х.: НТУ "ХПІ", 2024. - 132 с. 4. Методичні вказівки для виконання практичних та лабораторних робіт з курсу «Системне програмування. Графічний інтерфейс користувача (GUI) для студентів спеціальності: 123 – «Комп'ютерна інженерія» всіх форм навчання [електронне видання] / укладач О.М. Рисований. - Х.: НТУ "ХПІ", 2024. - Харків - 90 с. 5. Низькорівневе програмування апаратних засобів. Керування комп'ютером. Командна оболонка. : навчально-методичний посібник для студентів спеціальності 123 "Комп'ютерна інженерія" всіх форм навчання [електронне видання] / О.М. Рисований. – Харків: НТУ «ХПІ», 2024. – 74 с. 6. Методичні вказівки до виконання лабораторних робіт з курсу "Реверсне програмування", Ч.1. «Впровадження коду» для студентів спеціальностей: 123 – «Комп'ютерна інженерія», 125 – «Кібербезпека» / укладач А.М. Рисований. – Х.: «Слово», 2019. – 84 с. 7. Методичні вказівки до виконання лабораторних робіт з курсу «Реверсне програмування», Ч.2 «Крекінг: практика злому простих програм. Середовище програмування masm64» для студентів спеціальностей: 123 – «Комп'ютерна інженерія», 125 – «Кібербезпека» всіх форм навчання/упорядник О.М. Рисований. - Х.: "Слово", 2020. – 213 92 с. 8. Рисований О.М. Системне програмування, Ч. 1. Програмування серед masm64 : учеб.-метод. посібник/О.М. Рисований. - Харків: «Слово», 2017. -108с. 9. Рисований О.М. Системне програмування, Ч. 2. Розширені можливості програмування в середовищі masm64: учеб.-метод. посібник/О.М. Рисований. – Харків: «Слово», 2017. – 140 с. 10. Рисований О.М. Реверсне програмування. Використання коду. Середовище програмування masm64: навчальний посібник для студентів спеціальностей 123 "Комп'ютерна інженерія", 125 "Кібербезпека" / О.М. Рисований-Харків: "Слово", 2021 - 250с. 11. Рисований О.М. Системне програмування : підручник для студентів напряму “Комп’ютерна інженерія” вищих навчальних закладів / О.М. Рисований. – Харків : НТУ “ХПІ”, 2010. – 912с. Навчальне видання Рисований Олександр Миколайович «РЕВЕРСНЕ ПРОГРАМУВАННЯ» Захист коду. Середовище розробки masm64 Навчально-методичний посібник для студентів спеціальності 123 - "Комп'ютерна інженерія" всіх форм навчання Роботу до видання рекомендував Микола Заполовський Відповідальний за випуск Олександр ЗАКОВОРОТНИЙ В авторській редакції План 2024, поз. 95 Підгот. до друку 15.07.2024. Формат 60х84 1/16. Електронне видання. Уч. піч. лист. 11. Видавничий центр НТУ "ХПІ" Свідоцтво суб'єкта вид. Справи ДК №5478 від 21.08.2017р. Вул. Кирпичова, 2, м. Харків, 61002 Електронне видання