fbpx
22.8 C
София

Пример за самомодифицираща се програма на x86 асемблер

Оригиналът е на Brian Stadnicki

Най-четени

Даниел Десподов
Даниел Десподовhttps://www.kaldata.com/
Ежедневен автор на новини. Увличам се от съвременни технологии, оръжие, информационна безопасност, спорт, наука и концепцията Internet of Things.

Някои хора считат самомодифициращите се програми за нещо магическо, но всъщност те са доста опростени и за да демонстрираме това, нека да покаже един пример за х86 архитектурата, написан на NASM.

Оригиналният сорс код за х86 асемблер изчисляване на факториел

Нека в началото да разгледаме стандартната програма на асемблер за изчисляване на факториел.

factorial:
    push ebp
    mov ebx, eax
factorial_start:
    sub ebx, 1
    cmp ebx, 0
    je factorial_end
    mul ebx
    jmp factorial_start
factorial_end:
    pop ebp
    ret

Тук няма нищо сложно.

Самомодифициращият се факториел

В алгоритъма за изчисляване на факториела има две места, в които промяната на значенията по време на изпълнението има смисъл. Това са началното значение и множителят.

Техническите особености

Първо, самомодифициращите се програми си има своя характерна специфика. По подразбиране NASM линкерът свързва програмата без възможност за нейната по-нататъшна самостоятелна модификация, вероятно за да не се създават компютърни вируси. Поради съображения за информационната безопасност разделът .text се обозначава само за четене. За да се промени съответния флаг за този раздел с цел да се активира възможността за презапис на неговите клетки е необходимо да се използва objcopy и една малка допълнителна подпрограма.

Началните значения

В сорс кода първоначалното число се подава чрез регистъра EAX. За да може да се използва самомодифициращия се код, най-напред е необходимо в началото на тази функция да бъде включена инструкцията MOV за нулиране на регистъра EAX:

_start:
    mov dword [factorial+2], 0x5
    call factorial

factorial:
    push ebp
    mov eax, 0

Лесно може да се види, че за подаване началното значение програмата променя инструкцията mov eax. Числото нула в тази инструкция е отместено с два байта напред от самото начало на функцията factorial. Именно на мястото на тази 0 се записва значението на факториела, който трябва да бъде изчислен.

Множителят

factorial_start:
    ; multiply
    mov ebx, 0
    mul ebx

Това е кодът, който ще бъде модифициран от самата програма и ще се използва за умножението. След това трябва да съставим необходимия алгоритъм за поставяне на началното значение за изчисляване на факториела в инструкцията mov ebx, 0, неговото декрементиране и окончателно излизане от цикъла.

Инициализацията на множителя

За да се зададе множителя използваме регистъра EBX, в който е записано неговото първоначално значение и го копираме на подходящото място в инструкцията mov eax, 0, която помним, че е в самото начало на функцията factorial_start. Тоест, имаме следния код:

factorial:
    ...
    mov dword [factorial_start+1], ebx ; init decrementer

Декрементирането на множителя

В еталонната програма логиката на събитията е следната:

  • Декремнтиране на множителя
  • Ако той е равен на нула, излизане
  • Извършване на преход назад

В нашата самомодифицираща се програма е променено само едно нещо – декрементирането на множителя.

За да можем да осъществим това е необходимо да получим неговото текущо значение, да го намалим с единица и да го копираме обратно на същото място. Това най-лесно може да стане по следния начин:

factorial_start:
    ...
    ; decrement
    mov ebx, dword [factorial_start+1]
    sub ebx, 1
    mov dword [factorial_start+1], ebx

Крайният резултат

След като съберем всичко изброено до тук на едно място ще получим следния сорс код:

extern printf

section .data
    format:    db "num: %d",10,0

section .text
	global _start

_start:
    mov dword [factorial+2], 0x5 ; start number
    
    call factorial
    ; print result
    push eax
    push format
    call printf
    ; exit
    mov eax, 1
	mov ebx, 0
    int 80h

factorial:
    push ebp
    mov eax, 0

    mov ebx, eax
    sub ebx, 1
    mov dword [factorial_start+1], ebx ; init decrementer
    mov ebx, 0

factorial_start:
    ; multiply
    mov ebx, 0
    mul ebx

    ; decrement
    mov ebx, dword [factorial_start+1]
    sub ebx, 1
    mov dword [factorial_start+1], ebx
    ; exit if at 0
    ; could exit at 1, but then it doesn't handle 0x2
    cmp ebx, 0
    je factorial_end
    ; loop back
    jmp factorial_start

factorial_end:
    pop ebp
    ret

Изводи

Лично на мен самомодифициращите се програми са изключително интересни. Техният код е доста по-различен, изглежда по друг начин, доста е безпорядъчен и включва празни стойности, а схващането на самата логика на алгоритъма е много увлекателно.

Този начин за писане на код се използва в най-различни области и основно за обфускация – умишленото създаване на сорс код или директно на машинен код, който е твърде труден за разбиране от хората. Методът масово се използва за защита на лицензии и за писане на вредоносен софтуер. Мисля да използвам този подход, за да създам своя собствена програма за компресия и криптиране на данните, както и някой много интересен crackme файл.


Към тази статия има голям брой коментари, като има програмисти, които са на мнение, че този пример не е много удачен, понеже изчисляваният факториел трябва да се подава директно в програмата като константа. Предлага се кодът да бъде направен по-универсален като се добави допълнителен параметър, който да подава факториела, който ще се изчислява, чрез стека. Други програмисти контрират, че по този начин бързодействието на алгоритъма силно ще намалее, понеже ще се налага нулиране на стека, а това става чрез кеша.

По принцип, едва ли някоя съвременна операционна система ще разреши подобни директни действия без специални права, но това е друга тема.


Коментирайте статията в нашите Форуми. За да научите първи най-важното, харесайте страницата ни във Facebook, и ни последвайте в Telegram и Viber или изтеглете приложението на Kaldata.com за Android, iOS и Huawei!

Абонирай се
Извести ме за
guest

2 Коментара
стари
нови
Отзиви
Всички коментари

Нови ревюта

Подобни новини