Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
 iakovlev.org 
 Languages
 С
 GNU С Library 
 Qt 
 STL 
 Threads 
 C++ 
 Samples 
 stanford.edu 
 ANSI C
 Libs
 LD
 Socket
 Pusher
 Pipes
 Encryption
 Plugin
 Inter-Process
 Errors
 Deep C Secrets
 C + UNIX
 Linked Lists / Trees
 Asm
 Perl
 Python
 Shell
 Erlang
 Go
 Rust
 Алгоритмы
NEWS
Последние статьи :
  Тренажёр 16.01   
  Эльбрус 05.12   
  Алгоритмы 12.04   
  Rust 07.11   
  Go 25.12   
  EXT4 10.11   
  FS benchmark 15.09   
  Сетунь 23.07   
  Trees 25.06   
  Apache 03.02   
 
TOP 20
 MINIX...3057 
 Solaris...2933 
 LD...2904 
 Linux Kernel 2.6...2470 
 William Gropp...2180 
 Rodriguez 6...2011 
 C++ Templates 3...1945 
 Trees...1937 
 Kamran Husain...1865 
 Secure Programming for Li...1791 
 Максвелл 5...1710 
 DevFS...1693 
 Part 3...1682 
 Stein-MacEachern-> Час...1632 
 Go Web ...1624 
 Ethreal 4...1618 
 Arrays...1606 
 Стивенс 9...1603 
 Максвелл 1...1592 
 FAQ...1538 
 
  01.01.2024 : 3621733 посещений 

iakovlev.org

Making Plain Binary files using a C compiler(i386+)

Cornelis Frank 2000

Создадим исходник test.c

         int main() {}
 
Откомпилируем его :
         gcc -c test.c
         ld -o test -Ttext 0x0 -e main test.o
         objcopy -R .note -R .comment -S -O binary test test.bin
 
Будет создан бинарник test.bin . Запустим команду
         ndisasm -b 32 test.bin
 
Яковлев С : На своей 3-й федоре я получил вывод :
 00000000  55                push ebp
 00000001  89E5              mov ebp,esp
 00000003  83EC08            sub esp,byte +0x8
 00000006  83E4F0            and esp,byte -0x10
 00000009  B800000000        mov eax,0x0
 0000000E  83C00F            add eax,byte +0xf
 00000011  83C00F            add eax,byte +0xf
 00000014  C1E804            shr eax,0x4
 00000017  C1E004            shl eax,0x4
 0000001A  29C4              sub esp,eax
 0000001C  C9                leave
 0000001D  C3                ret
 

Данный код является основой для любой функции . Регистр ebp на всякий сохраняем . GNU GCC может создавать только 32-битный код . Еще один вариант создания бинарника :

         gcc -c test.c
         ld test.o -o test.bin -Ttext 0x0 -e main -oformat binary
 

Теперь создадим локальную переменную

   int main()
   {
         int i;
         i = 0x12345; (16-ричная переменная)
   }
 
Откомпилируем его :
         gcc -c test.c
         ld -o test -Ttext 0x0 -e main test.o
         objcopy -R .note -R .comment -S -O binary test test.bin
 
Будет создан бинарник test.bin . Запустим команду
         ndisasm -b 32 test.bin
 
 00000000  55                push ebp
 00000001  89E5              mov ebp,esp
 00000003  83EC08            sub esp,byte +0x8
 00000006  83E4F0            and esp,byte -0x10
 00000009  B800000000        mov eax,0x0
 0000000E  83C00F            add eax,byte +0xf
 00000011  83C00F            add eax,byte +0xf
 00000014  C1E804            shr eax,0x4
 00000017  C1E004            shl eax,0x4
 0000001A  29C4              sub esp,eax
 0000001C  C745FC45230100    mov dword [ebp-0x4],0x12345
 00000023  C9                leave
 00000024  C3                ret
 
Команда mov dword [ebp-0x4],0x12345 помещает переменную в локальный стек , где 0x4 - зарезервированные 4 байта для типа int . Обратите на порядок байтов во втором столбике напротив этой команды - 452301 . Такой тип хранения называется backwards storage . Если комбинация
         int i;
         i = 0x12345; 
 
будет заменена на
         int i = 0x12345;
 
разницы никакой не будет .

Теперь создадим глобальную переменную :

         int i;
   int main()
   {
         i = 0x12345; 
   }
 
Получим следующий бинарный код :
 00000000  55                push ebp
 00000001  89E5              mov ebp,esp
 00000003  83EC08            sub esp,byte +0x8
 00000006  83E4F0            and esp,byte -0x10
 00000009  B800000000        mov eax,0x0
 0000000E  83C00F            add eax,byte +0xf
 00000011  83C00F            add eax,byte +0xf
 00000014  C1E804            shr eax,0x4
 00000017  C1E004            shl eax,0x4
 0000001A  29C4              sub esp,eax
 0000001C  C705281000004523  mov dword [0x1028],0x12345
          -0100
 00000026  C9                leave
 00000027  C3                ret
 
Наша глобальная переменная теперь пишется в память командой mov dword [0x1028],0x12345 по адресу 0x1028 в отдельный сегмент данных. Мы можем заставить линкер сохранить переменную поближе с помощью опции -N . Или можно для команды ld набрать например -Tdata 0x1234 , и наша глобальная переменная будет размещена по адресу 0x1234 . Хранение переменной в сегменте данных делает ее доступной за пределами функции main .

Теперь попробуем команду objdump :

         objdump --disassemble-all test.o
 
Опять же на моей 3-й федоре это дало результат :
 test.o:     file format elf32-i386
 Disassembly of section .text:
 00000000 
: 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 08 sub $0x8,%esp 6: 83 e4 f0 and $0xfffffff0,%esp 9: b8 00 00 00 00 mov $0x0,%eax e: 83 c0 0f add $0xf,%eax 11: 83 c0 0f add $0xf,%eax 14: c1 e8 04 shr $0x4,%eax 17: c1 e0 04 shl $0x4,%eax 1a: 29 c4 sub %eax,%esp 1c: c7 05 00 00 00 00 45 movl $0x12345,0x0 23: 23 01 00 26: c9 leave 27: c3 ret

Теперь поговорим об указателях

     int main()
     {
         int i;
         int *p;
         p=&i;
         *p=0x12345;
     }
 
Будет сгенерирован код :
 00000000  55                push ebp
 00000001  89E5              mov ebp,esp
 00000003  83EC08            sub esp,byte +0x8
 00000006  83E4F0            and esp,byte -0x10
 00000009  B800000000        mov eax,0x0
 0000000E  83C00F            add eax,byte +0xf
 00000011  83C00F            add eax,byte +0xf
 00000014  C1E804            shr eax,0x4
 00000017  C1E004            shl eax,0x4
 0000001A  29C4              sub esp,eax
 0000001C  8D45FC            lea eax,[ebp-0x4]
 0000001F  8945F8            mov [ebp-0x8],eax
 00000022  8B45F8            mov eax,[ebp-0x8]
 00000025  C70045230100      mov dword [eax],0x12345
 0000002B  C9                leave
 0000002C  C3                ret
 
Команда sub esp,byte +0x8 резервирует в стеке 8 байт для 2-х переменных . Команда lea eax,[ebp-0x4] грузит в регистр eax адрес переменной i . После чего команда mov [ebp-0x8],eax присваивает этот адрес указателю . Затем в 2 приема : mov eax,[ebp-0x8] и mov dword [eax],0x12345 указателю присваивается значение 12345 .

Теперь поговорим о структурах . Рассмотрим пример :

         typedef struct 
         {
                 int a,b,c,d;
                 int i[10];
         } mydef;
         
         mydef myfunc();
         int main () 
         {
                 myfunc();
         }
         
         mydef myfunc()
         {
                 mydef md;
                 return d;
         }
 
Этот пример со структурой сгенерит код :
 00000000  55                push ebp
 00000001  89E5              mov ebp,esp
 00000003  83EC48            sub esp,byte +0x48
 00000006  83E4F0            and esp,byte -0x10
 00000009  B800000000        mov eax,0x0
 0000000E  83C00F            add eax,byte +0xf
 00000011  83C00F            add eax,byte +0xf
 00000014  C1E804            shr eax,0x4
 00000017  C1E004            shl eax,0x4
 0000001A  29C4              sub esp,eax
 0000001C  8D45B8            lea eax,[ebp-0x48]
 0000001F  83EC0C            sub esp,byte +0xc
 00000022  50                push eax
 00000023  E805000000        call 0x2d
 00000028  83C40C            add esp,byte +0xc
 0000002B  C9                leave
 0000002C  C3                ret
 0000002D  55                push ebp
 0000002E  89E5              mov ebp,esp
 00000030  57                push edi
 00000031  56                push esi
 00000032  83EC40            sub esp,byte +0x40
 00000035  8B7D08            mov edi,[ebp+0x8]
 00000038  8D75B8            lea esi,[ebp-0x48]
 0000003B  FC                cld
 0000003C  B80E000000        mov eax,0xe
 00000041  89C1              mov ecx,eax
 00000043  F3A5              rep movsd
 00000045  8B4508            mov eax,[ebp+0x8]
 00000048  83C440            add esp,byte +0x40
 0000004B  5E                pop esi
 0000004C  5F                pop edi
 0000004D  C9                leave
 0000004E  C20400            ret 0x4
 
Как мы видим , команда sub esp,byte +0x48 резервирует в стеке 0x48 байт . Указатель на структуру передается командой call 0x2d по адресу 0x2d .

Особенности GCC-откомпилированного кода

         1.  Он 32-битный 
         2.  Регистры CS,DS,ES,FS,GS,SS  работают в одном сегменте памяти
         3.  Глобальные переменные хранятся в сегменте DATA внутри самого
             бинарного файла , сразу после кодового сегмента
         4.  Переменные типа const хранятся в read-only секции также внутри
             бинарника .
         5.  Стек не работает с глобальными переменными
         
                   
 

 
 
Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье