Мне просто нужна демонстрационная версия hello world, чтобы увидеть, как работает машинный код.
Хотя окна EXE и linux ELF находятся рядом с машинным кодом, но не PURE
Как я могу написать/выполнить PURE машинный код?
Мне просто нужна демонстрационная версия hello world, чтобы увидеть, как работает машинный код.
Хотя окна EXE и linux ELF находятся рядом с машинным кодом, но не PURE
Как я могу написать/выполнить PURE машинный код?
Все знают, что приложение, которое мы обычно пишем, выполняется в операционной системе. И управляется этим.
Это означает, что операционная система запущена на машине. Поэтому я думаю, что это ЧИСТЫЙ машинный код, который вы сказали.
Итак, вам нужно изучить, как работает операционная система.
Вот код сборки NASM для загрузочного сектора, который может печатать "Hello world" в PURE.
org
xor ax, ax
mov ds, ax
mov si, msg
boot_loop:lodsb
or al, al
jz go_flag
mov ah, 0x0E
int 0x10
jmp boot_loop
go_flag:
jmp go_flag
msg db 'hello world', 13, 10, 0
times 510-($-$$) db 0
db 0x55
db 0xAA
И вы можете найти больше ресурсов здесь: http://wiki.osdev.org/Main_Page.
END.
Если у вас установлен нос и установлена дискета, вы можете
nasm boot.asm -f bin -o boot.bin
dd if=boot.bin of=/dev/fd0
Затем вы можете загрузиться с этой дискеты, и вы увидите сообщение. (ПРИМЕЧАНИЕ: вы должны сделать первую загрузку вашего компьютера дискетой.)
На самом деле, я предлагаю вам запустить этот код на полной виртуальной машине, такой как: bochs, virtualbox и т.д. Потому что трудно найти машины с дискетой.
Итак, шаги Во-первых, вам необходимо установить полноценную виртуальную машину. Во-вторых, создайте визуальную дискету с помощью commend: bximage В-третьих, запишите файл bin на эту визуальную дискету. Наконец, запустите ваш визуальный компьютер с этой визуальной дискеты.
ПРИМЕЧАНИЕ. В https://wiki.osdev.org есть некоторая базовая информация по этой теме.
Вы можете писать в PURE машинный код вручную БЕЗ СБОРКИ
Linux/ELF: https://github.com/XlogicX/m2elf. Это все еще в стадии разработки, я только начал работать над этим вчера.
Исходный файл для "Hello World" будет выглядеть следующим образом:
b8 21 0a 00 00 #moving "!\n" into eax
a3 0c 10 00 06 #moving eax into first memory location
b8 6f 72 6c 64 #moving "orld" into eax
a3 08 10 00 06 #moving eax into next memory location
b8 6f 2c 20 57 #moving "o, W" into eax
a3 04 10 00 06 #moving eax into next memory location
b8 48 65 6c 6c #moving "Hell" into eax
a3 00 10 00 06 #moving eax into next memory location
b9 00 10 00 06 #moving pointer to start of memory location into ecx
ba 10 00 00 00 #moving string size into edx
bb 01 00 00 00 #moving "stdout" number to ebx
b8 04 00 00 00 #moving "print out" syscall number to eax
cd 80 #calling the linux kernel to execute our print to stdout
b8 01 00 00 00 #moving "sys_exit" call number to eax
cd 80 #executing it via linux sys_call
WIN/MZ/PE:
Расположение сценария shellcode2exe.py (принимает код оболочки asciihex и создает допустимый исполняемый файл MZ PE):
http://zeltser.com/reverse-malware/shellcode2exe.py.txt
Зависимость:
экстракт
python setup.py build
sudo python setup.py install
Похоже, вы ищете старый 16-разрядный формат DOS .COM
. Байты файла .COM
загружаются со смещением 100h в сегменте программы (ограничивая их максимальным размером 64k - 256 байт), а процессор просто запускается с шагом 100h. Нет никаких заголовков или любой требуемой информации любого рода, только исходные инструкции CPU.
В ОС не выполняются инструкции, процессор делает (за исключением того, что мы говорим о ОС виртуальной машины, которые существуют, я думаю о Форте или таких вещах). Однако ОС требует некоторой метаинформации, чтобы знать, что файл действительно содержит исполняемый код и как он ожидает, что его среда будет выглядеть. ELF находится не только рядом с машинным кодом. Он - это машинный код, а также некоторая информация для ОС, чтобы знать, что он должен был заставить CPU фактически выполнить эту вещь.
Если вам нужно что-то более простое, чем ELF, но * nix, посмотрите на формат a.out, который намного проще. Традиционно компиляторы * nix C (все еще) записывают свой исполняемый файл в файл с именем a.out, если не указано имя вывода.
В Windows - не менее 32-битной Windows - вы можете выполнить RAW INSTRUCTIONS с использованием .com файла.
Например, если вы берете эту строку и сохраняете ее в блокноте с расширением .com:
X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*
Он напечатает строку и запустит антивирусное программное обеспечение.
При настройке встроенной системы вы можете создать двоичный образ rom или ram, который строго соответствует инструкциям и связанным с ними данным из программы. И часто можно записать этот двоичный файл в flash/rom и запустить его.
Операционные системы хотят знать больше, и разработчики часто хотят оставить больше, чем в своем файле, чтобы они могли отлаживать или делать с ним другие вещи позже (разобрать с некоторыми узнаваемыми именами символов). Кроме того, встроенный или в операционной системе вам может потребоваться отделить .text от .data из .bss из .rodata и т.д. И форматов файлов, таких как .elf, предоставить механизм для этого, и предпочтительным вариантом использования является загрузка этого эльфа с некоторыми вроде загрузчика, будь то операционная система или что-то программирующее ром и плунжер микроконтроллера.
.exe также имеет информацию о заголовке. Как упоминалось .com не загружался по адресу 0x100h и разветвлен там.
чтобы создать исходный двоичный файл из исполняемого файла, с созданным gcc файлом эльфа, например, вы можете сделать что-то вроде
objcopy file.elf -O бинарный файл .bin
Если программа сегментирована (.text,.data и т.д.), и эти сегменты не возвращаются назад, бинарный файл может стать довольно большим. Опять же, используя встроенный в качестве примера, если rom находится в 0x00000000, а данные или bss - на 0x20000000, даже если ваша программа имеет всего 4 байта objcopy данных, создаст файл размером 0x20000004, заполняющий промежуток между .text и .data(как и должен потому что это то, что вы просили у него).
Что вы пытаетесь сделать? Чтение эльфа или файла hex hex или srec довольно тривиально, и из него вы можете увидеть все биты и байты двоичного файла. Или разобрать эльфа, или что-то еще покажет вам это в удобочитаемой форме. (objdump -D file.elf > file.list)
С чистым машинным кодом вы можете использовать любой язык, на котором есть возможность записывать файлы. даже visual basic.net может писать 8,16,32,64 бит при перестановке между типами int во время записи.
Вы даже можете настроить, чтобы vb выписывал машинный код в цикле по мере необходимости для чего-то вроде setpixel, где x, y изменяется, и у вас есть ваши цвета argb.
или, регулярно создавайте свою программу vb.net в Windows и используйте NGEN.exe
, чтобы создать собственный файл кода вашей программы. Он создает чистый машинный код, специфичный для ia-32, одним выстрелом, отбрасывающим отладчик JIT.
Следующая программа - это программа Hello World, которую я написал в машинном коде 16 бит (intel 8086). Если вы хотите узнать машинный код, я предлагаю вам сначала изучить ассемблер, потому что каждая строка кода в ассемблере преобразуется в строку кода в Машинный код. Хорошо знаю, что я из немногих людей в мире, которые все еще программируют на машинном коде, а не на ассемблере.
Кстати, чтобы запустить его, сохраните файл с расширением ".com" и запустите на DOSBOX!
Это приятные ответы, но почему кто-то захочет это сделать, может лучше ответить на этот вопрос. Я думаю, что самая важная причина - получить полный контроль над своей машиной, особенно за ее кеш-запись, за максимальную производительность и не позволять любой ОС делиться процессором или виртуализировать ваш код (тем самым замедляя его) или особенно в эти дни, отслеживая ваш код также. Насколько я могу судить, ассемблер не справляется с этими проблемами, а M $/Intel и другие компании рассматривают это как нарушение или "для хакеров". Однако это очень неправильно. Если ваш код ассемблера передается ОС или проприетарному оборудованию, истинная оптимизация (возможно, на частотах ГГц) будет недосягаема. Это очень важная проблема в отношении науки и техники, поскольку наши компьютеры не могут использоваться в полной мере без аппаратной оптимизации и часто вычисляют на несколько порядков ниже нее. Вероятно, есть обходное решение или какое-то оборудование с открытым исходным кодом, которое позволяет это, но я еще не нашел его. Пенни для любых мыслей.
Я не программист, и все, что я знаю о программировании, - это то, что я узнал, когда я закончил раздел JavaScript в Kahn Academy, но я даже не могу вспомнить, что такое цикл for. Я искал что-то о машинных кодах и нашел руководство по программированию машинного кода для 16-битной Windows, и первая программа должна была сгенерировать Hello, World!
.
Код:
ba 0c 01 b4 09
cd 21
b8 00 4c
cd 21
48 65 6c 6c 6f 2c 20 57 6f 72 6c 64 21 0d 0a 24
В двоичном выражении это означает:
10111010 00001100 00000001
10110100 00001001
11001101 00100001
10111000 00000000 01001100
11001101 00100001
01001000 01100101 01101100 01101100 01101111 00101100 00100000 01010111 01101111 01110010 01101100 01100100 00100001 00100001 00001010 00100100
У меня есть куча всех-вы-вызовов, которые разделяют строки кода, но по какой-то причине это не появляется для меня. Возможно, это будет для вас.
Что нужно для запуска теста: Linux x86 или x64 (в моем случае я использую Ubuntu x64)
Позвольте начать
Эта сборка (x86) перемещает значение 666 в регистр eax:
movl $666, %eax
ret
Позвольте сделать двоичное представление этого:
Код операции movl (movl - это mov с размером операнда 32) в двоичном виде = 1011
Инструкция ширина в двоичном виде = 1
Зарегистрируйте eax в двоичном виде = 000
Число 666 в двоичном формате со знаком 32 бита = 00000000 00000000 00000010 10011010
666 преобразовано в младший порядковый номер is = 10011010 00000010 00000000 00000000
Инструкция ret (возврат) в двоичном виде = 11000011
Итак, наконец наши чистые двоичные инструкции будут выглядеть так:
1011(movl)1(width)000(eax)10011010000000100000000000000000(666)
11000011(ret)
Собираем все вместе:
1011100010011010000000100000000000000000
11000011
Для его выполнения двоичный код должен быть помещен на страницу памяти с правами на выполнение, мы можем сделать это, используя следующий код C:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
/* Allocate size bytes of executable memory. */
unsigned char *alloc_exec_mem(size_t size)
{
void *ptr;
ptr = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANON, -1, 0);
if (ptr == MAP_FAILED) {
perror("mmap");
exit(1);
}
return ptr;
}
/* Read up to buffer_size bytes, encoded as 1 and 0's, into buffer. */
void read_ones_and_zeros(unsigned char *buffer, size_t buffer_size)
{
unsigned char byte = 0;
int bit_index = 0;
int c;
while ((c = getchar()) != EOF) {
if (isspace(c)) {
continue;
} else if (c != '0' && c != '1') {
fprintf(stderr, "error: expected 1 or 0!\n");
exit(1);
}
byte = (byte << 1) | (c == '1');
bit_index++;
if (bit_index == 8) {
if (buffer_size == 0) {
fprintf(stderr, "error: buffer full!\n");
exit(1);
}
*buffer++ = byte;
--buffer_size;
byte = 0;
bit_index = 0;
}
}
if (bit_index != 0) {
fprintf(stderr, "error: left-over bits!\n");
exit(1);
}
}
int main()
{
typedef int (*func_ptr_t)(void);
func_ptr_t func;
unsigned char *mem;
int x;
mem = alloc_exec_mem(1024);
func = (func_ptr_t) mem;
read_ones_and_zeros(mem, 1024);
x = (*func)();
printf("function returned %d\n", x);
return 0;
}
Источник: https://www.hanshq.net/files/ones-and-zeros_42.c
Мы можем скомпилировать его с помощью:
gcc source.c -o binaryexec
Чтобы выполнить это:
./binaryexec
Затем мы передаем первые наборы инструкций:
1011100010011010000000100000000000000000
нажмите ввод
и передайте инструкцию возврата:
11000011
нажмите ввод
наконец, Ctrl + D, чтобы завершить программу и получить вывод:
функция вернула 666