Я остался под впечатлением от прочитанной первой или второй главы про хакерство в книге Криса Касперски "Техника отладки программ без исходных текстов".
Хоть я и знал о том, что раньше писали в машинных кодах, но когда Крис так подал интересно материал, что я замотивировался сделать небольшой компьютер эмулятор, который бы позволял обрабатывать мой машинный код. Тут дело проще чем кажется, так как я решил взять всего 16 инструкций для этого дела. Плюс ко всему я как-то давно мечтал сделать hex редактор на ncurses, но каждый раз не было смысла его делать просто так, а теперь сделал.
Инструкция хранится в старшем полубайте, вот их битовые обозначения.
* 0000 ADD - Команда сложения
* 0001 SUB - вычитание
* 0010 AND - битовое И
* 0011 OR - битовое ИЛИ
* 0100 XOR - ксор регистров или числа
* 0101 SHL - сдвиг влево
* 0110 SHR - сдвиг вправо
* 0111 LD - запись в регистр аккумулятор
* 1000 IN - входящие данные по порту
* 1001 NOP - нет команды
* 1010 OUT - исходящие данные по порту
* 1011 PUSH - пуш байта
* 1100 POP - поп байта
* 1101 TEST - вычитание без записи в регистр A
* 1110 JC - условный и безусловный переход
* 1111 HALT - останова программы
Эти команды формируют старший полубайт опкода. Младший полубайт опкода указывает на регистры или регистр число.
флаги
Z - флаг нуля, то-есть если делается тест, вычитается значение, если результат равно нулю, то устанавливается этот флаг
S - флаг отрицательного числа, если в результате стало равно меньше нуля, или то-есть установлен 0x80 бит.
C - флаг переполнения
Условия у переходов
0000 JC
0001 JS
0010 JZ
0011 JMP
младший полубайт
* A - 0b00
* X - 0b01
* Y - 0b10
* число/адрес - 0b11
адреса
0x00 in, out - таймер, можно задать время обновления в миллисекундах
0x01 in, out - экран, 22x10. Значение заноситься из регистров. В регистре A находится символ, в регистре X координата по x, в регистре Y координата по y. Например [IN 0x01]; [1000 0000 0000 0001]; 0x80 0x01
0x02 out - чтение в регистр A значения стрелок. Четыре стороны, 0b1000 - это только влево. 0b1100 - это влево и вверх. Как в vim [jkl;]
0x03 out - чтение в регистр A нажатой клавиши, всего 4 клавиши.
Я написал этот эмулятор за одну ночь и один вечер. Когда решил начать писать код, я немного испугался, ведь я не помню ни одной инструкции. Тогда я открыл текстовый файл с обозначениями всей архитектуры и начал писать код. Мне удалось вывести одну букву в видео память. Далее хотел вывести строку, но это пока что оказалось для меня более сложной задачей.
Как можно работать с памятью. Например рассмотрим инструкцию LD, которая загружает в регистр какое-то число. Регистр может быть регистром данных или регистром адресом.
LD инструкция это 0111 - то-есть 0x7x. Например хотим загрузить в регистр A число 0x14, будет так. LD 0111; A 00; число 11; 0x14 0001 0100; Получается 0x73 0x14.
Команд мало и их можно запомнить, плюс потренировать свою память.
В этом эмуляторе есть стек, который начинается с адреса 0xffff, мы можем контролировать стек только с помощью PUSH и POP. С помощью LD мы можем загружать только байт, и адрес может быть только байт (хотя можно сделать и два байта). Пока адрес для LD есть как один байт, поэтому все переменные нужно хранить в первых 256 байтах.
Цель проекта почувствовать что такое программировать в машинных кодах на упрощенной архитектуре. Я бы хотел даже электронное устройство такое иметь, где можно было бы ходить с ним и в память вбивать опкоды, чтобы на экране потом что-нибудь выводилось.
Такое я смог написать только под вдохновением, когда как разработка моего эмулятора i386 простаивает.
В любом случае, можно теперь почувствовать ту боль или то восхищение, когда код писался с помощью чисел.
Вот ссылка на проект, если кому-то тоже интересно почувствовать как это было, хотя бы примерно, в те годы.
Если интерес не пропадет, то сделаю сборку для android или аврора.
Еще в планах сделать пошаговый отладчик.
Заканчивая статью, хочу сказать, когда речи прекрасны, они мотивируют на подвиги, и даже, когда я занялся реверс инжинирингом, то всё-же уделил пару дней на написания эмулятора, потому что в душе я не только исследователь, но и творец!
Комментарии (12)
longtolik
04.01.2025 21:52Это здорово, когда мы чувствуем железо.
Последний раз использовал машинные коды, когда для Raspberry Pi 3 делал звук/видео буфер на голом железе. FASM интерпретировал мнемонику в машинный код, а GCC - нет. Пришлось в текст программы вставить кусок данных. Заработало.
А в общем, до сих пор помню многие коды из PDP-11.
Можно купить БК-0010 и пробовать..
Ещё делал дизассемблер на лету для IBM совместимых компьютеров . Надо было убрать кусок кода, который защиту на Com port проверял. Через T бит после каждой команды выполнялос прерывание, и дизассемблировалась команда. На 386SX работало с приемлемой скоростью.
kpen
04.01.2025 21:52Машинные коды для PDP-11... пультовый режим...
1000:012737
1002:000123
1004:177566а теперь набираем 1000 G
(это печатало символ S на алфавитно-цифровой экран)
Трава тогда была зеленее... как алфавитно-цифровой монитор ДВК-2)
Уважаемый Анатолий, не могли бы Вы вкратце поделиться Вашим методом по запихиванию и запуску машинных кодов в RaspberryPi? (если это не секрет).
longtolik
04.01.2025 21:52Еще там была проверка готовности через регистр состояния.
Коллега делал проверку бита готовности через TSTB и потом ветвление.
Позже я понял, что надо делать через BPL, так как бит готовности - он же старший в байте, поэтому соответствует знаку.
Потом, когда RT-11 (ФОДОС) появился, фокус ему показывал
MOV #123, R0
EMT O341 // .TTYOUT R0
EMT O350 // EXITВстраивание данных в код
Кусок программы от Peter Lemon на ассемблере
Инициализация Frame Buffer
...
FB_Init:
mov w0,FB_STRUCT + MAIL_TAGS
mov x1,MAIL_BASE
orr x1,x1,PERIPHERAL_BASE
str w0,[x1,MAIL_WRITE + MAIL_TAGS] ; Mail Box Writeldr w0,[FB_POINTER] ; W0 = Frame Buffer Pointer
cbz w0,FB_Init ; IF (Frame Buffer Pointer == Zero) Re-Initialize Frame Buffer
...Он транслируется в FASM, но выдает ошибки в GCC aarch64-linux-gnu
Поэтому сначала я вставлял машинные коды нетранслируемых команд как директивы .word ...
а после преобразовал текст программы.
...
FB_Init:
mov w0, FB_STRUCT+MAIL_TAGS
.word 0x5282a900 // альтеративно - такая командаmov x1, MAIL_BASE orr x1,x1, PERIPHERAL_BASE str w0, [x1,MAIL_WRITE + MAIL_TAGS] // Mail Box Write adr x1, FB_POINTER //.word 0x18000700 //ldr w0,[FB_POINTER] // W0 = Frame Buffer Pointer [] ldr w0, [x1] // Store Frame Buffer Pointer Physical Address cbz w0, FB_Init // IF (Frame Buffer Pointer == Zero) Re-Initialize Frame Buffer and w0, w0,0x3FFFFFFF // Convert Mail Box Frame Buffer Pointer From BUS Address To Physical Address ($CXXXXXXX -> $3XXXXXXX) adr x1, FB_POINTER // already there str w0, [x1] // Store Frame Buffer Pointer Physical Address
...
Дизассемблер:
0000000000000094 :
94: 5282a900 mov w0, #0x1548 // #5448
98: 5282a900 .word 0x5282a900
9c: d2971001 mov x1, #0xb880 // #47232
a0: b2681421 orr x1, x1, #0x3f000000
a4: b9002820 str w0, [x1, #40]
a8: 1000a8a1 adr x1, 15bc
ac: b9400020 ldr w0, [x1]
b0: 34ffff20 cbz w0, 94
b4: 12007400 and w0, w0, #0x3fffffff
b8: 1000a821 adr x1, 15bc
bc: b9000020 str w0, [x1]
c0: b26a03ff mov sp, #0x400000 // #4194304
c4: 14001682 b 5accДве первые строки mov w0, FB_STRUCT+MAIL_TAGS .word 0x5282a900 выдают один и тот же машинный код 94: 5282a900 ... 98: 5282a900 ...
Таким образом, можно создать базу данных (на самом деле - кодов). Из этих "кирпичиков" построить программу, передать на нее управление, и она будет работать.
Можно применить генетические алгоритмы, то есть, "выживать" будут лучшие экземпляры.
Также не исключено добавление случайных кодов, в случае ошибки возникнет прерывание по неправильному коду операции, этот код может быть подправлен и т. д.Тут нужно смотреть, чтобы изменения кода происходили, не мешая кешу команд и данных.
Самомодифицирующиеся программы существуют уже давно.
Про Raspberry Pi
Пробовал на 3+A и B, 2W - все с процессором A53. Четыре ядра работают - это позволяет на каждом выполнять свою задачу без переключений. С Raspberry Pi 5 на голом железе пока не пробовал - там чип ввода-вывода поставили.
jobless
04.01.2025 21:52Это самый удобный отладчик в моей жизни. Система команд и адреса устройств в восьмеричном виде на ОДНОМ листе А4(фото бумага). Первая моя программа которую кто то использовал кроме меня тестировала перфокарты. Перфораторы старые и не все дырки качественно пробивались (помимо просто ошибок). Предыдущая не моя программа печатала номера плохих карт и их в ручную нужно было подсчитывать в колоде. А моя позволяла остановить чтение на ошибке проверить карту и вернуть на чтение в карман устройства.
nixtonixto
04.01.2025 21:52Начало нулевых, программировал AT89C2051 без компьютера (не было), самодельным программатором на базе двоичных счётчиков, генераторов-одновибраторов, 28С еепром и параллельной ОЗУ. Через ОЗУ переставлял куски программы в еепром, корректировал и вводил код через сдвиговой регистр и 2 кнопки (CLK и DATA). Вначале рисовал алгоритм, потом переводил в машинные коды и вводил в память программатора.
alabamaa
04.01.2025 21:52Читал отзывы на приведенную в статье книгу о том, что на сегодня она уже не актуальна. По вашему мнению, используют ли разработчики современного софта приемы защиты, описанные в данной книге ? Стоит ли ее читать сегодня, чтобы использовать полученные знания по назначению, а не для развлечения с машинными кодами ?
xverizex Автор
04.01.2025 21:52Я её читаю, чтобы усвоить базу, узнать какая защита была раньше. Это же знания.
4m3l
04.01.2025 21:52Я бы хотел даже электронное устройство такое иметь, где можно было бы ходить с ним и в память вбивать опкоды, чтобы на экране потом что-нибудь выводилось.
Вы не думали на FPGA реализовать свою архитектуру и играть с ней?
xverizex Автор
04.01.2025 21:52Вы не думали на FPGA реализовать свою архитектуру и играть с ней?
Я думал об этом, я хочу электронику изучить, но мне сложно себя организовать, так как я не знаю как правильно учиться электронике. А так, да, можно было бы сделать такое. Может в будущем сделаю, если обрету эти священные знания по электронике.
Спасибо, что своим вопросом вдохновляете.
Да и все присутствующие в комментариях очень порадовали меня, мотивация очень сильная, я рад, что ностальгия есть у людей по тем временам.
andy_p
Он вроде как Крис Касперски.
svs85
Более того, интересна история придуманного псевдонима :)))
themen2
Ага, он приходил к настоящему Касперскому, но тот чё то психанул, если не ошибаюсь и куда то убежал на пердячей тяге