Создание исполняемого файла ELF вручную

Автор оригинала: Robin Hoksbergen

Привет, класс, и добро пожаловать в x86 Masochism 101. Здесь вы узнаете, как использовать опкоды непосредственно для создания исполняемого файла, даже не прикасаясь к компилятору, ассемблеру или компоновщику. Мы будем использовать только редактор, способный изменять двоичные файлы (т.е. шестнадцатеричный редактор), и «chmod», чтобы сделать файл исполняемым.

Если это вас не заводит, то я даже не знаю...

Если серьезно, то это одна из тех вещей, которые я лично считаю очень интересными. Очевидно, вы не собираетесь использовать это для создания серьезных программ с миллионами строк. Тем не менее, вы можете получить огромное удовольствие, узнав, что вы действительно понимаете, как такие вещи действительно работают на низком уровне. Также здорово иметь возможность сказать, что вы написали исполняемый файл, даже не касаясь компилятора или интерпретатора. Помимо этого, существуют приложения для программирования ядра, реверс-инжиниринга и (что неудивительно) создания компиляторов.

Прежде всего, давайте очень быстро посмотрим, как на самом деле работает выполнение файла ELF. Многие детали будут опущены. Что важно, так это получить хорошее представление о том, что делает ваш компьютер, когда вы говорите ему выполнить двоичный файл ELF.

Когда вы говорите компьютеру выполнить двоичный файл ELF, первое, что он будет искать, - это соответствующие заголовки ELF. Эти заголовки содержат всевозможную важную информацию об архитектуре процессора, сегментах и секциях файла и многое другое - мы поговорим об этом позже. Заголовок также содержит информацию, которая помогает компьютеру идентифицировать файл как ELF. Что наиболее важно, заголовок ELF содержит информацию о таблице заголовков программы (program header table) в случае исполняемого файла и виртуальном адресе, на который компьютер передает управление при выполнении.

Таблица заголовков программы, в свою очередь, определяет несколько сегментов. Если вы когда-либо программировали на ассемблере, вы можете думать о некоторых сегментах, таких как «text» и «data», как о сегментах в исполняемом файле. Заголовки программы также определяют, где в фактическом файле находятся данные этих сегментов, и какой адрес виртуальной памяти им назначить.

Если все было сделано правильно, компьютер загружает все сегменты в виртуальную память на основе данных в заголовках программ, затем передает управление на адрес виртуальной памяти, назначенный в заголовке ELF, и начинает выполнение инструкций.

Прежде чем мы начнем практиковаться, убедитесь, что у вас есть настоящий шестнадцатеричный редактор на вашем компьютере, и что вы можете запускать двоичные файлы ELF и вы используете компьютер архитектуры x86. Большинство шестнадцатеричных редакторов должны работать и позволять редактировать и сохранять вашу работу - мне лично нравится Bless. Если вы работаете в Linux, с двоичными файлами ELF все будет в порядке. Некоторые другие Unix-подобные операционные системы тоже могут работать, но разные ОС реализуют вещи немного по-разному, поэтому я не могу быть уверен. Я также широко использую системные вызовы, что еще больше ограничивает совместимость. Если вы используете Windows, вам не повезло. Точно так же, если архитектура вашего процессора отличается от x86 (хотя x86_64 должна работать), поскольку я просто не могу предоставить коды операций для каждой архитектуры.

Создание исполняемого файла ELF состоит из трех этапов. Сначала мы создадим фактическую полезную нагрузку (payload), используя опкоды. Во-вторых, мы создадим заголовки ELF и program header table, чтобы превратить эту полезную нагрузку в рабочую программу. Наконец, мы убедимся, что все смещения и виртуальные адреса верны, и заполним последние пробелы.

Предупреждение: создание исполняемого файла ELF вручную может быть очень неприятным. Я сам предоставил пример двоичного файла, который вы можете использовать для сравнения своей работы, но имейте в виду, что нет компилятора или компоновщика, который бы сказал вам, что вы сделали не так. Если (читайте: когда) вы облажались, ваш компьютер сообщит вам только «Ошибка ввода-вывода» или «Ошибка сегментации», что затрудняет отладку этих программ. И никаких отладочных символов не будет!

Создание полезной нагрузки

Давайте постараемся сделать полезную нагрузку простой, но достаточно сложной, чтобы быть интересной. Наша полезная нагрузка должна вывести "Hello World!" на экран, затем выйти с кодом 93. Это сложнее, чем кажется. Нам понадобится как текстовый сегмент (содержащий исполняемые инструкции), так и сегмент данных (содержащий строку «Hello World!» и некоторые другие второстепенные данные). Давайте посмотрим на ассемблерный код, который нам нужен для этого:

(text segment)
mov ebx, 1
mov eax, 4
mov ecx, HWADDR
mov edx, HWLEN
int 0x80

mov eax, 1
mov ebx, 0x5D
int 0x80

Приведенный выше код не слишком сложен, даже если вы никогда не программировали на ассемблере. Прерывание 0x80 используется для выполнения системных вызовов, причем значения в регистрах EAX и EBX сообщают ядру, что это за вызов. Вы можете получить более подробную информацию о системных вызовах и их значениях в соответствующем мануале.

Для нашей полезной нагрузки нам нужно преобразовать эти инструкции в шестнадцатеричные коды операций. К счастью, есть хорошие онлайн-мануалы, которые помогают нам в этом. Попробуйте найти такой для семейства x86 и посмотрите, сможете ли вы понять, как перейти от приведенного выше кода к приведенным ниже шестнадцатеричным кодам:

0xBB 0x01 0x00 0x00 0x00
0xB8 0x04 0x00 0x00 0x00
0xB9 0x** 0x** 0x** 0x**
0xBA 0x0D 0x00 0x00 0x00
0xCD 0x80

0xB8 0x01 0x00 0x00 0x00
0xBB 0x5D 0x00 0x00 0x00 
0xCD 0x80

(Здесь звёздочки обозначают виртуальные адреса. Мы их еще не знаем, поэтому пока оставим их пустыми)

Вторая часть полезной нагрузки состоит из сегмента данных, который на самом деле представляет собой просто строку «Hello World!\n». Используйте таблицу преобразования ASCII ('man ascii'), чтобы преобразовать эти значения в шестнадцатеричный формат, и вы увидите, как мы получим следующие данные:

(data segment)
0x48 0x65 0x6C 0x6C 0x6F 0x20 0x57 0x6F 0x72 0x6C 0x64 0x21 0x0A

И вот наша полезная нагрузка готова!

Создание заголовков

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

e_ident(16), e_type(2), e_machine(2), e_version(4), e_entry(4), e_phoff(4),
e_shoff(4), e_flags(4), e_ehsize(2), e_phentsize(2), e_phnum(2), e_shentsize(2)
e_shnum(2), e_shstrndx(2)

Теперь мы заполним структуру, и я объясню немного больше об этих параметрах, где это необходимо.

e_ident (16) - этот параметр содержит первые 16 байтов информации, которая идентифицирует файл как файл ELF. Первые четыре байта всегда содержат 0x7F, 'E', L ', F'. Байты с пятого по седьмой содержат 0x01 для 32-битных двоичных файлов на машинах с little-endian. Байты с восьмого по пятнадцатый являются заполнителями, поэтому они могут быть 0x00, а шестнадцатый байт содержит длину этого блока, поэтому он должен быть 16 (= 0x10).

e_type (2) - установите в 0x02 0x00. По сути, это говорит компьютеру, что это исполняемый файл ELF.

e_machine (2) - установите значение 0x03 0x00, что сообщает компьютеру, что файл ELF был создан для работы на процессорах типа i386.

e_version (4) - установите 0x01 0x00 0x00 0x00.

e_entry (4) - передать управление на этот виртуальный адрес при исполнении. Мы еще не определили его, поэтому пока это 0x** 0x** 0x** 0x**.

e_phoff (4) - смещение от файла к program header table. Мы помещаем его сразу после заголовка ELF, так что размер заголовка ELF в байтах: 0x34 0x00 0x00 0x00.

e_shoff (4) - смещение от начала файла к таблице заголовков раздела. Нам это не нужно. 0x00 0x00 0x00 0x00.

e_flags (4) - флаги нам тоже не нужны. 0x00 0x00 0x00 0x00 снова.

e_ehsize (2) - размер заголовка ELF, поэтому содержит 0x34 0x00.

e_phentsize (2) - размер заголовка программы. Технически мы этого еще не знаем, но я уже могу сказать вам, что он должен содержать 0x20 0x00. Прокрутите вниз, чтобы проверить, если хотите.

e_phnum (2) - количество заголовков программы, что напрямую соответствует количеству сегментов в файле. Нам нужен текст и сегмент данных, поэтому это должно быть 0x02 0x00.

e_shentsize (2), e_shnum (2), e_shstrndx (2) - все это на самом деле не актуально, если мы не реализуем заголовки секций (а мы не реализуем), поэтому вы можете просто установить это значение 0x00 0x00 0x00 0x00 0x00 0x00.

И это заголовок ELF! Это первое, что находится в файле, и если вы все сделали правильно, окончательный заголовок в шестнадцатеричном формате должен выглядеть так:

0x7F 0x45 0x4C 0x46 0x01 0x01 0x01 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x10 0x02 0x00 0x03 0x00 0x01 0x00 0x00 0x00
0x** 0x** 0x** 0x** 0x34 0x00 0x00 0x00 0x00 0x00 0x00 0x00
0x00 0x00 0x00 0x00 0x34 0x00 0x20 0x00 0x02 0x00 0x00 0x00
0x00 0x00 0x00 0x00

Однако мы еще не закончили с заголовками. Теперь нам нужно также создать program header table. Он имеет следующие записи:

p_type(4), p_offset(4), p_vaddr(4), p_paddr(4), p_filesz(4), p_memsz(4),
p_flags(4), p_align(4)

Опять же, я заполню структуру (на этот раз дважды: один для сегмента текста, второй для сегмента данных) и объясню ряд вещей по пути:

p_type (4) - сообщает программе тип сегмента. И текст, и данные используют здесь PT_LOAD (= 0x01 0x00 0x00 0x00).

p_offset (4) - смещение от начала файла. Эти значения зависят от размера заголовков и сегментов, поскольку мы не хотим, чтобы они перекрывались. Пока пусть будет 0x** 0x** 0x** 0x**.

p_vaddr (4) - какой виртуальный адрес назначить сегменту. Пусть будет 0x** 0x** 0x** 0x** 0x**, мы поговорим об этом позже.

p_paddr (4) - физическая адресация не имеет значения, поэтому вы можете указать здесь 0x00 0x00 0x00 0x00.

p_filesz (4) - количество байтов в образе файла сегмента, должно быть больше или равно размеру полезной нагрузки в сегменте. Опять же, установите значение 0x** 0x** 0x** 0x**. Мы изменим это позже.

p_memsz (4) - количество байтов в памяти образа сегмента. Обратите внимание, что это не обязательно равно p_filesz, но может быть и так. Пока оставьте его на 0x** 0x** 0x** 0x**, но помните, что позже мы можем установить его на то же значение, которое мы присваиваем p_filesz.

p_flags (4) - эти флаги могут быть непростыми, если вы не привыкли с ними работать. Что вам нужно запомнить, так это то, что флаг READ - 0x04, флаг WRITE - 0x02, а флаг EXEC - 0x01. Для текстового сегмента мы хотим READ + EXEC, поэтому 0x05 0x00 0x00 0x00, а для сегмента данных мы предпочитаем READ + WRITE + EXEC, поэтому 0x07 0x00 0x00 0x00.

p_align (4) - указывает на выравнивание страниц памяти. Размер страницы обычно составляет 4 КиБ, поэтому значение должно быть 0x1000. Помните, что x86 является little-endian, поэтому окончательное значение равно 0x00 0x10 0x00 0x00.

Уф. Мы, безусловно, уже многое сделали. Мы еще не заполнили многие поля в заголовках программ, и нам также не хватает нескольких байтов в заголовке ELF, но мы приближаемся. Если все пойдет по плану, таблица заголовков вашей программы (которую, кстати, можно вставить непосредственно за заголовком ELF - помните наше смещение в этом заголовке?) Должна выглядеть примерно так:

0x01 0x00 0x00 0x00 0x** 0x** 0x** 0x** 0x** 0x** 0x** 0x**
0x00 0x00 0x00 0x00 0x** 0x** 0x** 0x** 0x** 0x** 0x** 0x**
0x05 0x00 0x00 0x00 0x00 0x10 0x00 0x00 
0x01 0x00 0x00 0x00 0x** 0x** 0x** 0x** 0x** 0x** 0x** 0x**
0x00 0x00 0x00 0x00 0x** 0x** 0x** 0x** 0x** 0x** 0x** 0x**
0x07 0x00 0x00 0x00 0x00 0x10 0x00 0x00

Заполнение пробелов

Хотя мы уже выполнили большую часть тяжелой работы, нам все еще нужно сделать несколько сложных вещей. У нас есть заголовок ELF и таблица программ, которые мы можем разместить в начале нашего файла, и у нас есть полезная нагрузка для нашей реальной программы, но нам все еще нужно поместить что-то в таблицу, которая сообщает компьютеру, где это найти. И нам нужно разместить нашу полезную нагрузку в файле, чтобы ее можно было найти.

Во-первых, мы хотим вычислить размер наших заголовков и полезной нагрузки, прежде чем мы сможем определить какие-либо смещения. Просто сложите вместе размеры всех полей в заголовках и получите минимальное смещение для любого из сегментов. В заголовке ELF 116 байт + 2 заголовка программы, и 116 = 0x74, поэтому минимальное смещение равно 0x74. Чтобы сделать это безопасно, давайте установим начальное смещение на 0x80. Заполните от 0x74 до 0x7F 0x00, затем поместите текстовый сегмент в 0x80 в файл.

Размер самого текстового сегмента составляет 34 = 0x22 байта, что означает, что минимальное смещение для сегмента данных составляет 0x80 + 0x22 = 0xA2. Поместим сегмент данных в 0xA4 и заполним 0xA2 и 0xA3 значениями 0x00.

Если вы делали все вышеперечисленное в своем шестнадцатеричном редакторе, теперь у вас будет двоичный файл, содержащий ELF, и заголовки программ от 0x00 до 0x73, от 0x74 до 0x7F будут заполнены нулями, текстовый сегмент размещен от 0x80 до 0xA1, 0xA2 и 0xA3 снова являются нулями, и сегмент данных идет от 0xA4 до 0xB0. Если вы следуете этим инструкциям, и не получаете правильного результата, сейчас самое время посмотреть, что пошло не так.

Предполагая, что теперь все находится в нужном месте в файле, пора изменить некоторые из наших предыдущих звездочек на фактические значения. Я просто сначала дам вам значения для каждого параметра, а затем объясню, почему мы используем именно эти значения.

e_entry (4) - 0x80 0x80 0x04 0x08; Мы выберем 0x8048080 в качестве точки входа в виртуальной памяти. Существуют некоторые правила относительно того, что вы можете, а что не можете выбрать в качестве точки входа, но самое важное, что нужно помнить, - это то, что начальный адрес виртуальной памяти по модулю размера страницы должен быть равен смещению в файле по модулю размера страницы. Вы можете проверить это в справочнике по ELF и некоторым другим хорошие книгам для получения дополнительной информации, но если это кажется слишком сложным, просто забудьте об этом и используйте эти значения.

p_offset (4) - 0x80 0x00 0x00 0x00 для текста, 0xA4 0x00 0x00 0x00 для данных. Это из-за очевидной причины, по которой эти сегменты находятся в файле.

p_vaddr (4) - 0x80 0x80 0x04 0x08 для текста, 0xA4 0x80 0x04 0x08 для данных. Мы хотим, чтобы сегмент текста был точкой входа для программы, и мы помещаем сегмент данных в память таким образом, чтобы он прямо соответствовал физическим смещениям.

p_filesz (4) - 0x24 0x00 0x00 0x00 для текста, 0x20 0x00 0x00 0x00 для данных. Это просто байтовые размеры различных сегментов файла и памяти. В этом случае p_memsz = p_filesz, поэтому используйте те же значения там.

Окончательный результат

Если вы выполнили все до буквы, вот что вы получите, если выгрузите все в шестнадцатеричном формате:

7F 45 4C 46 01 01 01 00 00 00 00 00 00 00 00 10 02 00 03 00
01 00 00 00 80 80 04 08 34 00 00 00 00 00 00 00 00 00 00 00
34 00 20 00 02 00 00 00 00 00 00 00 01 00 00 00 80 00 00 00
80 80 04 08 00 00 00 00 24 00 00 00 24 00 00 00 05 00 00 00
00 10 00 00 01 00 00 00 A4 00 00 00 A4 80 04 08 00 00 00 00
20 00 00 00 20 00 00 00 07 00 00 00 00 10 00 00 00 00 00 00
00 00 00 00 00 00 00 00 BB 01 00 00 00 B8 04 00 00 00 B9 A4
80 04 08 BA 0D 00 00 00 CD 80 B8 01 00 00 00 BB 2A 00 00 00
CD 80 00 00 48 65 6C 6C 6F 20 57 6F 72 6C 64 21 0A

Вот и все. Запустите chmod +x для этого двоичного файла, а затем выполните его. Hello World в 178 байтах. Надеюсь, вам понравилось это писать. :-) Если вы считаете этот HOWTO полезным или интересным, дайте мне знать! Я всегда это ценю. Также всегда приветствуются советы, комментарии и / или конструктивная критика.

Комментарии 6

    0
    Круг замкнулся archive.radio.ru/web/1986/08/025 :-) А вообще Ваш вариант гораздо полезнее вот таких microsin.net/programming/avr-working-with-usb/avr-usb-mega16-and-csharp-class.html
      0
      Не очень понятна применимость второй ссылки в контексте статьи
        0
        Это к тому, что Ваша статья в сто раз полезнее и нужнее, чем статья по моей второй ссылке. :-)
      0
      imitron, с сисколами все чудесно. А вот когда нам надо импорты использовать?
      format ELF executable 3
      entry start

      include 'linux/x86.inc'

      interpreter '/lib/ld-linux.so.2'
      needed 'libX11.so.6'

      include 'os_specs/linux/api/x86/libx11.inc'

      segment readable executable
      start:
      cinvoke libx11.XOpenDisplay, 0
      or eax, eax
      jz .failXserver
      mov [hdisplay], eax
      cinvoke libx11.XDefaultScreen, eax
      cinvoke libx11.XDefaultRootWindow, [hdisplay]
      cinvoke libx11.XCreateSimpleWindow, [hdisplay], eax, 10, 20, 400, 200, 0, $000000, $D4D0C8
      or eax, eax
      jz .failCreateWindow
      mov [hwnd], eax
      cinvoke libx11.XSelectInput, [hdisplay], [hwnd], 0
      cinvoke libx11.XStoreName, [hdisplay], [hwnd], _title
      cinvoke libx11.XMapRaised, [hdisplay], [hwnd]
      or eax, eax
      jz .failShow
      cinvoke libx11.XCreateGC, [hdisplay], [hwnd], 0, 0
      mov [hGC], eax
      cinvoke libx11.XSetForeground, [hdisplay], [hGC], $000000
      cinvoke libx11.XSetBackground, [hdisplay], [hGC], $ffffff
      .msg_pump:
      cinvoke libx11.XNextEvent, [hdisplay], event
      jmp .msg_pump
      stdcall Xshutdown
      .failShow:
      .failCreateWindow:
      .failXserver:
      mov [con_handle],2
      mov esi,_error
      call display_string
      mov al,-1
      exit_program:
      movzx ebx,al
      lcall sys_exit,ebx
      Xshutdown:
      cinvoke libx11.XFreeGC, [hdisplay], [hGC]
      cinvoke libx11.XDestroyWindow, [hwnd], [hdisplay]
      cinvoke libx11.XSetErrorHandler, [oError]
      xor eax, eax
      retn

      display_string:
      push ebx
      mov edi,esi
      mov edx,esi
      or ecx,-1
      xor al,al
      repne scasb
      neg ecx
      sub ecx,2
      xchg ecx,edx
      lcall sys_write,[con_handle],ecx,edx
      pop ebx
      retn

      segment readable writeable
      _title TCHAR 'Linux32 Xlib GUI program template',0
      _error TCHAR 'Startup failed.',0
      con_handle dd 0
      oError dd 0
      hGC dd 0
      hwnd dd 0
      hdisplay dd 0
      event rd 132


      макросы для 32битного импорта:
      macro Elf32_Sym name,value,size,bind,type,other,shndx {
      dd name+0
      dd value+0
      dd size+0
      db bind+0 + type+0
      db other+0
      dw shndx+0 }

      virtual at 0
      Elf32_Sym
      sizeof.Elf32_Sym = $
      end virtual

      macro Elf32_Rel offset,symbol,type
      {
      dd offset+0
      dd (symbol+0) shl 8 + (type+0)
      }

      virtual at 0
      Elf32_Rel
      sizeof.Elf32_Rel = $
      end virtual

      macro Elf32_Rela offset,symbol,type,addend
      {
      dd offset+0
      dd (symbol+0) shl 8 + (type+0)
      dd addend+0
      }

      virtual at 0
      Elf32_Rela
      sizeof.Elf32_Rela = $
      end virtual

      macro interpreter [library] {
      define interpreter@ library }

      macro needed [library] {
      local str
      match needed,needed@dynamic \{ define needed@dynamic needed,str:library \}
      match ,needed@dynamic \{ define needed@dynamic str:library \} }

      define needed@dynamic

      macro import [name,string] {
      local str
      match import,import@strtab \{ define import@strtab import,str:name:string \}
      match ,import@strtab \{ define import@strtab str:name:string \} }

      define import@strtab

      postpone {
      match library,interpreter@ \{
      segment interpreter readable
      db library,0 \}
      match needed,needed@dynamic \{ match import,import@strtab \\{
      segment dynamic readable
      local strtab,strsz,symtab,rel,relsz,hash,counter
      irp item,needed \\\{
      match str:library,item \\\\{
      dd DT_NEEDED,str-strtab \\\\} \\\}
      dd DT_STRTAB,strtab
      dd DT_STRSZ,strsz
      dd DT_SYMTAB,symtab
      dd DT_SYMENT,sizeof.Elf32_Sym
      dd DT_REL,rel
      dd DT_RELSZ,relsz
      dd DT_RELENT,sizeof.Elf32_Rel
      dd DT_HASH,hash
      dd DT_NULL,0

      segment readable writeable
      symtab: Elf32_Sym
      counter = 1
      irp item,import \\\{
      match fstr:name:string,item \\\\{
      if used name
      Elf32_Sym fstr-strtab,0,0,STB_GLOBAL,STT_FUNC,0,0
      end if \\\\} \\\}
      rel:
      irp item,import \\\{
      match fstr:name:string,item \\\\{
      if used name
      Elf32_Rel name,counter,R_386_32
      counter = counter+1
      end if \\\\} \\\}
      relsz = $-rel
      hash:
      dd 1,counter
      ;repeat bundlecounter
      ;if %=bundlecounter
      dd 0
      ;else
      ;dd %
      ;end if
      ;end repeat
      repeat counter
      if %=counter
      dd 0
      else
      dd %
      end if
      end repeat
      strtab:
      db '',0
      irp item,import \\\{
      match fstr:name:string,item \\\\{
      if used name
      fstr db string,0
      end if \\\\} \\\}
      irp item,needed \\\{
      match str:library,item \\\\{
      str db library,0 \\\\} \\\}
      strsz = $-strtab
      irp item,import \\\{
      match fstr:name:string,item \\\\{
      if used name
      name dd 0
      end if \\\\} \\\} \\} \} }



      имеем на выходе:
      db 7Fh, 45h, 4Ch, 46h, 01h, 01h, 01h, 03h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 02h, 00h, 03h, 00h, 01h, 00h, 00h, 00h, 0D4h, 80h, 04h, 08h, 34h, 00h, 00h, 00h
      db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 34h, 00h, 20h, 00h, 05h, 00h, 28h, 00h
      db 00h, 00h, 00h, 00h, 01h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 80h, 04h, 08h
      db 00h, 80h, 04h, 08h, 0FBh, 02h, 00h, 00h, 0FBh, 02h, 00h, 00h, 05h, 00h, 00h, 00h
      db 00h, 10h, 00h, 00h, 01h, 00h, 00h, 00h, 0FBh, 02h, 00h, 00h, 0FBh, 92h, 04h, 08h
      db 0FBh, 92h, 04h, 08h, 46h, 00h, 00h, 00h, 56h, 02h, 00h, 00h, 06h, 00h, 00h, 00h
      db 00h, 10h, 00h, 00h, 03h, 00h, 00h, 00h, 41h, 03h, 00h, 00h, 41h, 0A3h, 04h, 08h
      db 41h, 0A3h, 04h, 08h, 13h, 00h, 00h, 00h, 13h, 00h, 00h, 00h, 04h, 00h, 00h, 00h
      db 01h, 00h, 00h, 00h, 02h, 00h, 00h, 00h, 54h, 03h, 00h, 00h, 54h, 0A3h, 04h, 08h
      db 54h, 0A3h, 04h, 08h, 50h, 00h, 00h, 00h, 50h, 00h, 00h, 00h, 04h, 00h, 00h, 00h
      db 01h, 00h, 00h, 00h, 01h, 00h, 00h, 00h, 41h, 03h, 00h, 00h, 41h, 0A3h, 04h, 08h
      db 41h, 0A3h, 04h, 08h, 11h, 03h, 00h, 00h, 11h, 03h, 00h, 00h, 06h, 00h, 00h, 00h
      db 00h, 10h, 00h, 00h, 89h, 0E5h, 83h, 0ECh, 04h, 83h, 0E4h, 0F0h, 83h, 0C4h, 04h, 6Ah
      db 00h, 0FFh, 15h, 3Ah, 0A6h, 04h, 08h, 89h, 0ECh, 09h, 0C0h, 0Fh, 84h, 6Eh, 01h, 00h
      db 00h, 0A3h, 3Dh, 93h, 04h, 08h, 89h, 0E5h, 83h, 0ECh, 04h, 83h, 0E4h, 0F0h, 83h, 0C4h
      db 04h, 50h, 0FFh, 15h, 26h, 0A6h, 04h, 08h, 89h, 0ECh, 89h, 0E5h, 83h, 0ECh, 04h, 83h
      db 0E4h, 0F0h, 83h, 0C4h, 04h, 0FFh, 35h, 3Dh, 93h, 04h, 08h, 0FFh, 15h, 22h, 0A6h, 04h
      db 08h, 89h, 0ECh, 89h, 0E5h, 83h, 0ECh, 24h, 83h, 0E4h, 0F0h, 83h, 0C4h, 24h, 68h, 0C8h
      db 0D0h, 0D4h, 00h, 6Ah, 00h, 6Ah, 00h, 68h, 0C8h, 00h, 00h, 00h, 68h, 90h, 01h, 00h
      db 00h, 6Ah, 14h, 6Ah, 0Ah, 50h, 0FFh, 35h, 3Dh, 93h, 04h, 08h, 0FFh, 15h, 1Eh, 0A6h
      db 04h, 08h, 89h, 0ECh, 09h, 0C0h, 0Fh, 84h, 03h, 01h, 00h, 00h, 0A3h, 39h, 93h, 04h
      db 08h, 89h, 0E5h, 83h, 0ECh, 0Ch, 83h, 0E4h, 0F0h, 83h, 0C4h, 0Ch, 6Ah, 00h, 0FFh, 35h
      db 39h, 93h, 04h, 08h, 0FFh, 35h, 3Dh, 93h, 04h, 08h, 0FFh, 15h, 3Eh, 0A6h, 04h, 08h
      db 89h, 0ECh, 89h, 0E5h, 83h, 0ECh, 0Ch, 83h, 0E4h, 0F0h, 83h, 0C4h, 0Ch, 68h, 0FBh, 92h
      db 04h, 08h, 0FFh, 35h, 39h, 93h, 04h, 08h, 0FFh, 35h, 3Dh, 93h, 04h, 08h, 0FFh, 15h
      db 4Eh, 0A6h, 04h, 08h, 89h, 0ECh, 89h, 0E5h, 83h, 0ECh, 08h, 83h, 0E4h, 0F0h, 83h, 0C4h
      db 08h, 0FFh, 35h, 39h, 93h, 04h, 08h, 0FFh, 35h, 3Dh, 93h, 04h, 08h, 0FFh, 15h, 32h
      db 0A6h, 04h, 08h, 89h, 0ECh, 09h, 0C0h, 0Fh, 84h, 92h, 00h, 00h, 00h, 89h, 0E5h, 83h
      db 0ECh, 10h, 83h, 0E4h, 0F0h, 83h, 0C4h, 10h, 6Ah, 00h, 6Ah, 00h, 0FFh, 35h, 39h, 93h
      db 04h, 08h, 0FFh, 35h, 3Dh, 93h, 04h, 08h, 0FFh, 15h, 1Ah, 0A6h, 04h, 08h, 89h, 0ECh
      db 0A3h, 35h, 93h, 04h, 08h, 89h, 0E5h, 83h, 0ECh, 0Ch, 83h, 0E4h, 0F0h, 83h, 0C4h, 0Ch
      db 6Ah, 00h, 0FFh, 35h, 35h, 93h, 04h, 08h, 0FFh, 35h, 3Dh, 93h, 04h, 08h, 0FFh, 15h
      db 4Ah, 0A6h, 04h, 08h, 89h, 0ECh, 89h, 0E5h, 83h, 0ECh, 0Ch, 83h, 0E4h, 0F0h, 83h, 0C4h
      db 0Ch, 68h, 0FFh, 0FFh, 0FFh, 00h, 0FFh, 35h, 35h, 93h, 04h, 08h, 0FFh, 35h, 3Dh, 93h
      db 04h, 08h, 0FFh, 15h, 42h, 0A6h, 04h, 08h, 89h, 0ECh, 89h, 0E5h, 83h, 0ECh, 08h, 83h
      db 0E4h, 0F0h, 83h, 0C4h, 08h, 68h, 41h, 93h, 04h, 08h, 0FFh, 35h, 3Dh, 93h, 04h, 08h
      db 0FFh, 15h, 36h, 0A6h, 04h, 08h, 89h, 0ECh, 0EBh, 0E0h, 0E8h, 20h, 00h, 00h, 00h, 0C7h
      db 05h, 2Dh, 93h, 04h, 08h, 02h, 00h, 00h, 00h, 0BEh, 1Dh, 93h, 04h, 08h, 0E8h, 66h
      db 00h, 00h, 00h, 0B0h, 0FFh, 0Fh, 0B6h, 0D8h, 0B8h, 01h, 00h, 00h, 00h, 0CDh, 80h, 89h
      db 0E5h, 83h, 0ECh, 08h, 83h, 0E4h, 0F0h, 83h, 0C4h, 08h, 0FFh, 35h, 35h, 93h, 04h, 08h
      db 0FFh, 35h, 3Dh, 93h, 04h, 08h, 0FFh, 15h, 2Eh, 0A6h, 04h, 08h, 89h, 0ECh, 89h, 0E5h
      db 83h, 0ECh, 08h, 83h, 0E4h, 0F0h, 83h, 0C4h, 08h, 0FFh, 35h, 3Dh, 93h, 04h, 08h, 0FFh
      db 35h, 39h, 93h, 04h, 08h, 0FFh, 15h, 2Ah, 0A6h, 04h, 08h, 89h, 0ECh, 89h, 0E5h, 83h
      db 0ECh, 04h, 83h, 0E4h, 0F0h, 83h, 0C4h, 04h, 0FFh, 35h, 31h, 93h, 04h, 08h, 0FFh, 15h
      db 46h, 0A6h, 04h, 08h, 89h, 0ECh, 31h, 0C0h, 0C3h, 53h, 89h, 0F7h, 89h, 0F2h, 83h, 0C9h
      db 0FFh, 30h, 0C0h, 0F2h, 0AEh, 0F7h, 0D9h, 83h, 0E9h, 02h, 87h, 0CAh, 8Bh, 1Dh, 2Dh, 93h
      db 04h, 08h, 0B8h, 04h, 00h, 00h, 00h, 0CDh, 80h, 5Bh, 0C3h, 4Ch, 69h, 6Eh, 75h, 78h
      db 33h, 32h, 20h, 58h, 6Ch, 69h, 62h, 20h, 47h, 55h, 49h, 20h, 70h, 72h, 6Fh, 67h
      db 72h, 61h, 6Dh, 20h, 74h, 65h, 6Dh, 70h, 6Ch, 61h, 74h, 65h, 00h, 53h, 74h, 61h
      db 72h, 74h, 75h, 70h, 20h, 66h, 61h, 69h, 6Ch, 65h, 64h, 2Eh, 00h, 00h, 00h, 00h
      db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 00h, 2Fh, 6Ch, 69h, 62h, 2Fh, 6Ch, 64h, 2Dh, 6Ch, 69h, 6Eh, 75h, 78h, 2Eh, 73h
      db 6Fh, 2Eh, 32h, 00h, 01h, 00h, 00h, 00h, 0C2h, 00h, 00h, 00h, 05h, 00h, 00h, 00h
      db 4Ch, 0A5h, 04h, 08h, 0Ah, 00h, 00h, 00h, 0CEh, 00h, 00h, 00h, 06h, 00h, 00h, 00h
      db 0A4h, 0A3h, 04h, 08h, 0Bh, 00h, 00h, 00h, 10h, 00h, 00h, 00h, 11h, 00h, 00h, 00h
      db 94h, 0A4h, 04h, 08h, 12h, 00h, 00h, 00h, 70h, 00h, 00h, 00h, 13h, 00h, 00h, 00h
      db 08h, 00h, 00h, 00h, 04h, 00h, 00h, 00h, 04h, 0A5h, 04h, 08h, 00h, 00h, 00h, 00h
      db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 00h, 00h, 00h, 00h, 01h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 0Bh, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 1Fh, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 32h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 41h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 50h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 58h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 63h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 6Eh, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 7Bh, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 88h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 97h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 0A8h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 0B7h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 12h, 00h, 00h, 00h, 1Ah, 0A6h, 04h, 08h, 01h, 01h, 00h, 00h, 1Eh, 0A6h, 04h, 08h
      db 01h, 02h, 00h, 00h, 22h, 0A6h, 04h, 08h, 01h, 03h, 00h, 00h, 26h, 0A6h, 04h, 08h
      db 01h, 04h, 00h, 00h, 2Ah, 0A6h, 04h, 08h, 01h, 05h, 00h, 00h, 2Eh, 0A6h, 04h, 08h
      db 01h, 06h, 00h, 00h, 32h, 0A6h, 04h, 08h, 01h, 07h, 00h, 00h, 36h, 0A6h, 04h, 08h
      db 01h, 08h, 00h, 00h, 3Ah, 0A6h, 04h, 08h, 01h, 09h, 00h, 00h, 3Eh, 0A6h, 04h, 08h
      db 01h, 0Ah, 00h, 00h, 42h, 0A6h, 04h, 08h, 01h, 0Bh, 00h, 00h, 46h, 0A6h, 04h, 08h
      db 01h, 0Ch, 00h, 00h, 4Ah, 0A6h, 04h, 08h, 01h, 0Dh, 00h, 00h, 4Eh, 0A6h, 04h, 08h
      db 01h, 0Eh, 00h, 00h, 01h, 00h, 00h, 00h, 0Fh, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 01h, 00h, 00h, 00h, 02h, 00h, 00h, 00h, 03h, 00h, 00h, 00h, 04h, 00h, 00h, 00h
      db 05h, 00h, 00h, 00h, 06h, 00h, 00h, 00h, 07h, 00h, 00h, 00h, 08h, 00h, 00h, 00h
      db 09h, 00h, 00h, 00h, 0Ah, 00h, 00h, 00h, 0Bh, 00h, 00h, 00h, 0Ch, 00h, 00h, 00h
      db 0Dh, 00h, 00h, 00h, 0Eh, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 58h, 43h, 72h
      db 65h, 61h, 74h, 65h, 47h, 43h, 00h, 58h, 43h, 72h, 65h, 61h, 74h, 65h, 53h, 69h
      db 6Dh, 70h, 6Ch, 65h, 57h, 69h, 6Eh, 64h, 6Fh, 77h, 00h, 58h, 44h, 65h, 66h, 61h
      db 75h, 6Ch, 74h, 52h, 6Fh, 6Fh, 74h, 57h, 69h, 6Eh, 64h, 6Fh, 77h, 00h, 58h, 44h
      db 65h, 66h, 61h, 75h, 6Ch, 74h, 53h, 63h, 72h, 65h, 65h, 6Eh, 00h, 58h, 44h, 65h
      db 73h, 74h, 72h, 6Fh, 79h, 57h, 69h, 6Eh, 64h, 6Fh, 77h, 00h, 58h, 46h, 72h, 65h
      db 65h, 47h, 43h, 00h, 58h, 4Dh, 61h, 70h, 52h, 61h, 69h, 73h, 65h, 64h, 00h, 58h
      db 4Eh, 65h, 78h, 74h, 45h, 76h, 65h, 6Eh, 74h, 00h, 58h, 4Fh, 70h, 65h, 6Eh, 44h
      db 69h, 73h, 70h, 6Ch, 61h, 79h, 00h, 58h, 53h, 65h, 6Ch, 65h, 63h, 74h, 49h, 6Eh
      db 70h, 75h, 74h, 00h, 58h, 53h, 65h, 74h, 42h, 61h, 63h, 6Bh, 67h, 72h, 6Fh, 75h
      db 6Eh, 64h, 00h, 58h, 53h, 65h, 74h, 45h, 72h, 72h, 6Fh, 72h, 48h, 61h, 6Eh, 64h
      db 6Ch, 65h, 72h, 00h, 58h, 53h, 65h, 74h, 46h, 6Fh, 72h, 65h, 67h, 72h, 6Fh, 75h
      db 6Eh, 64h, 00h, 58h, 53h, 74h, 6Fh, 72h, 65h, 4Eh, 61h, 6Dh, 65h, 00h, 6Ch, 69h
      db 62h, 58h, 31h, 31h, 2Eh, 73h, 6Fh, 2Eh, 36h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h, 00h
      db 00h, 00h


      где то глюк в макросах импорта, есть версия где?
      Спасибо.
        0
        Я пока не настолько богоподобен чтобы отладить этот код в уме. Думаю придется посидеть в отладчике, чтобы разобраться где проблема
        0
        Немного не в тему: можно ли в эльфе создать (не вручную, а допустим в gcc или еще как нибудь) произвольную секцию? Скажем, .mydata c каким-то размером. В РЕ файле такое просто, а здесь?

        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

        Самое читаемое