The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Основы программирования Netfilter на ассемблере. (linux assembler kernel iptables network)


<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>
Ключевые слова: linux, assembler, kernel, iptables, network,  (найти похожие документы)
From: intuit <intuit[at]rootshell.be> Date: Mon, 7 Apr 2004 14:31:37 +0000 (UTC) Subject: Основы программирования Netfilter на ассемблере. Оригинал: http://rootshell.be/~intuit/nf.txt author: intuit mail: intuit[at]rootshell.be Основы программирования Netfilter на ассемблере. Многие слышали фразу: "ОС Linux создана программистами для программистов" На самом деле она звучит иначе: "ОС Linux создана Си-программистами для Си-программистов....." [1. ][Вступление] [2. ][О чем статья] [3. ][Теория] [4. ][Примеры LKM] [5. ][Заключение] [1. ][Вступление] Netfilter - подсистема ядра linux 2.4(экспериментальная поддержка была включена начиная с версий 2.3). Netfilter позволяет осуществлять пакетную фильтрацию, NAT и существенно расширить возможности работы с сетью за счёт установки специальных "hook'ов" в часть ядра ОС, ответственной за network. Их можно устанавливать в ядро одним из двух способов: статически(требуется перекомпиляция ядра) или в виде LKM, регистрируя тем самым разнообразные функции, которые будут вызываться при определенных условиях. Например, при получении определенного сетевого пакета. [2. ][О чем статья] Статья о работе с Netfilter, подсистемой ядра, расширяющей возможности ОС в ра- боте с сетевыми фреймами. Язык программирования - ассемблер. Синтаксис GAS. В статье описано создание простейших модулей(LKM) для демонстрации возможностей данной подсистемы. Желательно иметь под рукой заголовочные файлы с рассматрива- емыми в статье структурами. Ну, и разумеется для загрузки(тестирования) модулей вам необходимы привилегии root ;). [3. ][Теория] Ядро подсистемы Netfilter состоит из 5-ти hook-функций, обьявленных в linux/netfilter_ipv4.h. Видно, что эти функциии для IPv4, хотя больших отличий от их аналогов для IPv6 нет. С помощью них можно контроллировать пакеты на раз- личных уровнях сетевого стека. Ниже приведена схема анализа сетевого пакета системой Netfilter: [INPUT]--->[1]--->[ROUTE]--->[3]--->[4]--->[OUTPUT] | ^ | | | [ROUTE] v | [2] [5] | ^ | | v | [INPUT*] [OUTPUT*] Таблица функций Netfilter с их кодами в правой части (используются в struct nf_hook_ops, см. ниже) [1] - NF_IP_PRE_ROUTING = 0 [2] - NF_IP_LOCAL_IN = 1 [3] - NF_IP_FORWARD = 2 [4] - NF_IP_POST_ROUTING = 3 [5] - NF_IP_LOCAL_OUT = 4 [*] - Network Stack NF_IP_PRE_ROUTING - первый хук, используемый ядром при получении пакета. NF_IP_LOCAL_IN - используется в тех случаях, когда поступивший пакет предназна- чен нашей машине и далее такой пакет "форвардится" не будет. NF_IP_FORWARD - для пакетов, предназначенных для другого интерфейса. NF_IP_POST_ROUTING - для пакетов, которые уже настроены для дальнейшего "путе- шествия" по сети к своему адресату и готовы покинуть наш сетевой стек. NF_IP_LOCAL_OUT - этой функцией обрабатываются пакеты, исходяшие непосредствен- но от нас(из нашего собственного сетевого стека). Несомненно все хук-функции важны, но мы пока сосредоточимся только на первой из них. После обработки(проверки) пакета функция NF_IP_PRE_ROUTING должна возвра- тить одно из предопределенных значений(код), чтобы решить дальнейший "маршрут" пакета: 0 = "NF_DROP" Отбросить пакет. 1 = "NF_ACCEPT" Сохранить пакет.(Отправить пакет след. хук-ф-ии.) 2 = "NF_STOLEN" "Забыть" о пакете. 3 = "NF_QUEUE" Поставить пакет в очередь. 4 = "NF_REPEAT" Вызвать этот хук еще раз. Установить новый хук в системе очень просто. Во-первых, нужно заполнить струк- туру nf_hook_ops, определенную в linux/netfilter.h. На Си структура nf_hook_ops выглядит так: struct nf_hook_ops { struct list_head list; //Заполнение данной структуры не является //обязательным для регистрации хука в системе. /* Поля которые идут далее заполнять обязательно */ nf_hookfn *hook; //Указатель на нашу главную подпрограмму, которая //будет вызываться всякий раз при срабатывании нашего //хука. int pf; //Семейство протоколов с которым будет работать наш хук, //обычно PF_INET(см. таблицу ниже и bits/socket.h). int hooknum; //Одна из 5-ти функций, на которой будет висеть наш хук. //У нас - NF_IP_PRE_ROUTING(код из таблицы). int priority; //Приоритет. Подробнее см. linux/netfilter_ipv4.h // У нас - NF_IP_PRI_FIRST = INT_MIN //INT_MIN - константа, равная (-(2^32)/2) }; Таблица кодов семейств протоколов: PF_UNSPEC = 0 PF_LOCAL = 1 PF_INET = 2 PF_AX25 = 3 PF_IPX = 4 <...> На ассемблере заполнение данной структуры выглядит следующим образом: (более подробно см. source ниже) <...> .comm nf_my_ops,24 <...> movl $our_hook, nf_my_ops+8 movl $2, nf_my_ops+12 movl $0, nf_my_ops+16 movl $int_minimum, nf_my_ops+20 <...> Второе, что нужно сделать - необходимо зарегистрировать свой hook в сис- теме. Это делается с помощью системной функции nf_register_hook(). Данная функция при успешном завершении возвращает 0. В качестве параметра ей пе- редается - указатель на struct nf_hook_ops(см. net/core/netfilter.c): <...> pushl $nf_my_ops call nf_register_hook <...> Теперь, разберем подробнее прототип функции nf_hookfn, указатель на кото- рую передаем в struct nf_hook_ops через nf_hookfn *hook. На Си он выглядит так: typedef unsigned int nf_hookfn(unsigned int hooknum, struct sk_buff **skb, const struct net_device *in, const struct net_device *out, int (*okfn)(struct sk_buff *)); Первый аргумент - название(код) одной из пяти типов хук-функций Netfilter. Например, NF_IP_PRE_ROUTING = 0. Второй - указатель на указатель на структуру sk_buff(управляющая структура для описания сетевого пакета, см. linux/skbuff.h). net_device *in и net_device *out - указатели на структуру net_device(каж- дое сетевое устройство в ОС Linux описывается этой структурой, например, используется для описания интерфейсов lo, eth0). Соответственно, in - для входящего траффика(пакетов), out - для исходящего. Последний аргумент - указатель функции, также принимающей в качестве аргу- мента sk_buff struct и возвращающей целое число(см. net/core/netfilter.c). Работа со структурами, передаваемыми этими аргументами, из нашей функции our_hook на ассемблере происходит следующим образом: Пример 1. Проверка имени интерфейса net_device *in: --------- <...> interface: .string "lo" <...> our_hook: pushl %ebp movl %esp, %ebp <---* subl $16, %esp <---резервируем место в стеке под локальные переменные pushl interface pushl 16(%ebp) <---указатель на struct net_device *in, в частности на поле char name[IFNAMSIZ] call strcmp <...> На момент исполнения инструкции по адресу (*) программный стек будет выгля- деть таким образом: | [...верхние адреса памяти...] | [...........................] _ | [ *okfn ] - 24(%ebp) \ | [ *out ] - 20(%ebp) | рост [ *in ] - 16(%ebp) >---параметры our_hook стека [ **skb ] - 12(%ebp) | на стеке | [ hooknum ] - 8(%ebp)_/ | [ EIP ] - 4(%ebp) | [ EBP ] - 0(%ebp) | [ <next 4 bytes> ] <--- ESP point here | [...........................] V [....нижние адреса памяти...] Приведу небольшую часть структуры net_device: struct net_device { /* * This is the first field of the "visible" part of this structure * (i.e. as seen by users in the "Space.c" file). It is the name * the interface. */ char name[IFNAMSIZ]; - offset 0 /* * I/O specific fields * FIXME: Merge these and struct ifmap into one */ unsigned long rmem_end; /* shmem "recv" end */ - offset 16 unsigned long rmem_start; /* shmem "recv" start */ - offset 20 unsigned long mem_end; /* shared mem end */ - offset 24 unsigned long mem_start; /* shared mem start */ - offset 28 unsigned long base_addr; /* device I/O address */ - offset 32 unsigned int irq; /* device IRQ number */ - offset 36 ... } Как видно начало структуры, как раз содержит имя интерфейса.. Пример 2. Проверка IP адреса отправителя: --------- Для начала приведу кусок struct sk_buff: struct sk_buff { /* These two members must be first. */ /* Next buffer in list */ struct sk_buff * next; - offset 0 /* Previous buffer in list */ struct sk_buff * prev; - offset 4 /* List we are on */ struct sk_buff_head * list; - offset 8 /* Socket we are owned by */ struct sock *sk; - offset 12 /* Time we arrived */ struct timeval stamp; - offset 16 /* Device we arrived on/are leaving by */ struct net_device *dev; - offset 24 /* Transport layer header */ union { struct tcphdr *th; - offset 28 struct udphdr *uh; - offset 28 struct icmphdr *icmph; - offset 28 struct igmphdr *igmph; - offset 28 struct iphdr *ipiph; - offset 28 struct spxhdr *spxh; - offset 28 unsigned char *raw; - offset 28 } h; /* Network layer header */ union { struct iphdr *iph; - offset 32 struct ipv6hdr *ipv6h; - offset 32 struct arphdr *arph; - offset 32 struct ipxhdr *ipxh; - offset 32 unsigned char *raw; - offset 32 } nh; .... } Видно, что для работы с заголовком IP-пакета(IPv4) используется поле по смещению 32 bytes от начала структуры sk_buff, содержащее *iph(указатель на заголовок IP-пакета). Сл-но, для определения IP-адреса отправителя мы должны работать со структурой на которую указывает данное поле.. Теперь посмотрим на саму структуру базового IP-пакета: |<-------- 8 бит -------->|<-------- 8 бит -------->| |-------------------------------------------------------------| 0| Версия | Длина | Тип обслуживания | |-------------------------------------------------------------| 2| Длина пакета | |-------------------------------------------------------------| 4| Идентификатор | |-------------------------------------------------------------| 6| 0 | DF | MF | Смещение фрагмента | |-------------------------------------------------------------| 8| Число переходов | Протокол | |-------------------------------------------------------------| 10| Контрольная сумма заголовка | |-------------------------------------------------------------| 12| IP-адрес отправителя | |-------------------------------------------------------------| 16| IP-адрес получателя | |-------------------------------------------------------------| 20~ Параметры (до 40 байт) ~ |-------------------------------------------------------------| 20-60~ Данные (до 65535 байт минус заголовок) ~ |-------------------------------------------------------------| IP адрес отправителя находится по смещению 12 bytes, от начала IP-пакета. Непосредственное определение структуры IP-пакета на Си: #typedef unsigned int uint; #typedef unsigned char uchar; struct ip_packet { uint version:4; /* 4-bit version */ uint header_len:4; /* header length in words in 32bit words */ uint serve_type:8; /* how to service packet */ uint packet_len:16; /* total size of packet in bytes */ uint ID:16; /* fragment ID */ uint __reserved:1; /* always zero */ uint dont_frag:1; /* flag to permit fragmentation */ uint more_frags:1; /* flag for "more frags to follow" */ uint frag_offset:13; /* to help reassembly */ uint time_to_live:8; /* maximum router hop count */ uint protocol:8; /* ICMP, UDP, TCP */ uint hdr_chksum:16; /* ones-comp. checksum of header */ uchar IPv4_source; /* IP address of originator */ uchar IPv4_dest; /* IP address of destination */ uchar options[]; /* up to 40 bytes */ uchar data[]; /* message data up to 64KB */ }; На ассемблере определение IP-адреса отправителя будет выглядеть так: .comm sock_buff,4 /* blocked ip = 127.0.0.1 */ ip_address: .string "\x7f\x00\x00\x01" <...> our_hook: pushl %ebp movl %esp, %ebp <--- (см. схему строения стека) <...> /* Проверка структуры sk_buff */ movl 12(%ebp), %eax <--- вытащим адрес movl (%eax), %eax <--- структуры sk_buff.. movl %eax, sock_buff <--- в переменную sock_buff cmpl $0, sock_buff jne .ip_head movl $1, %eax jmp .quit /* Проверка валидности IP пакета */ .ip_head: movl sock_buff, %eax cmpl $0, 32(%eax) jne .check_saddr movl $1, %eax jmp .quit /* Проверка IP-адреса отправителя */ .check_saddr: movl sock_buff, %eax movl 32(%eax), %eax <--- вытащим указатель на IP-header из sk_buff.. movl 12(%eax), %eax <--- и source IP из IP-header movl ip_address, %edx cmpl (%edx), %eax jne .accept_packet movl $0, %eax <--- drop packet, if source == blocked ip jmp .quit <...> Также можно фильтровать пакеты по порту получателя.. здесь я этого не рассма- триваю, пусть это будет вашим д/з :) Надеюсь, что работу со струтурами на ассемблере обьяснил более-менее понятно, если будут вопросы стучите в асю or use mail, а лучше смотрите нужные заголо- вочные файлы и ковыряйте сами ;) [4. ][Примеры LKM] Модуль, блокирующий все входящие пакеты: /* ---drop_all_incoming_packets.s--- */ /* резервируем поименованную область nf_my_ops в секции bss */ .comm nf_my_ops,24 .globl init_module .globl cleanup_module .section .modinfo __module_kernel_version: .string "kernel_version=2.4.18-6mdk" .data .align 32 int_minimum: .int -2147483648 .text .align 32 init_module: pushl %ebp movl %esp, %ebp subl $20, %esp /* Заполняем структуру nf_my_ops = nf_hook_ops * nf_my_ops+0: struct list_head list - не заполняем * Заносим адрес нашей главной процедуры our_hook в поле nf_hookfn *hook */ movl $our_hook, nf_my_ops+8 /* PF_INET = 2 */ movl $2, nf_my_ops+12 /* NF_IP_PRE_ROUTING = 0 */ movl $0, nf_my_ops+16 /* NF_IP_PRI_FIRST = INT_MIN */ movl $int_minimum, nf_my_ops+20 /* адрес структуры -> в стек */ pushl $nf_my_ops /* вызываем nf_register_hook */ call nf_register_hook /* в eax -> 0 - код успешной загрузки модуля */ movl $0, %eax movl %ebp, %esp popl %ebp ret cleanup_module: pushl %ebp movl %esp, %ebp subl $20, %esp pushl $nf_my_ops call nf_unregister_hook movl %ebp, %esp popl %ebp ret our_hook: pushl %ebp movl %esp, %ebp /* Возвращаемое функцией значение 0 = "NF_DROP" */ movl $0, %eax popl %ebp ret /* ---EOF--- */ Модуль, блокирующий все входящие пакеты с определенного IP адреса: /* ---drop_all_incoming_packets_from_spec_saddr.s--- */ /* for BSS section */ .comm nf_my_ops,24 .comm sock_buff,4 .globl our_hook .globl init_module .globl cleanup_module .section .modinfo __module_kernel_version: .string "kernel_version=2.4.18-6mdk" .section .rodata .align 32 /* blocked ip = 192.168.16.21 */ .IP: .string "\xC0\xA8\x10\x15" .data .align 32 ip_address: .long .IP int_minimum: .int -2147483648 .text .align 32 our_hook: pushl %ebp movl %esp, %ebp .check_sk_buff: movl 12(%ebp), %eax movl (%eax), %eax movl %eax, sock_buff cmpl $0, sock_buff jne .ip_head movl $1, %eax jmp .quit .ip_head: movl sock_buff, %eax cmpl $0, 32(%eax) jne .check_saddr movl $1, %eax jmp .quit .check_saddr: movl sock_buff, %eax movl 32(%eax), %eax movl 12(%eax), %eax movl ip_address, %edx cmpl (%edx), %eax jne .accept_packet movl $0, %eax jmp .quit .accept_packet: movl $1, %eax .quit: popl %ebp ret init_module: pushl %ebp movl %esp, %ebp subl $20, %esp movl $our_hook, nf_my_ops+8 movl $2, nf_my_ops+12 movl $0, nf_my_ops+16 movl $int_minimum, nf_my_ops+20 pushl $nf_my_ops call nf_register_hook movl $0, %eax movl %ebp, %esp popl %ebp ret cleanup_module: pushl %ebp movl %esp, %ebp subl $20, %esp pushl $nf_my_ops call nf_unregister_hook movl %ebp, %esp popl %ebp ret /* ---EOF--- */ [5. ][Заключение] С помощью широких возможностей предоставляемых подсистемой Netfilter можно без особых усилий написать свой собственный фаерволл под требуемые задачи. to be continued... ---$--- Greetz to all ppl on #m00@EFNet. ---$---

<< Предыдущая ИНДЕКС Поиск в статьях src Установить закладку Перейти на закладку Следующая >>

Обсуждение [ RSS ]
  • 1, kas (??), 01:01, 24/08/2005 [ответить]  
  • +/
    Си и Линукс рулят! Не троньте святое! на сях программеры занимаются математикой и алгоритмами,
    а не мастурбацией с системными вызовами! это не M$DO$ чтоб на асме программить, а проблемы с портируемостью? кароче, ребята, Си изучайте, он рулит.
     
  • 2, гад ползучий (?), 21:58, 28/03/2009 [ответить]  
  • +/
    единственное из-за чего я рекламирую линукс формат на своём сайте-личная антимонопольная программа. а так вообще: все сишники и другие программеры языков высокого уровня- отпрыск социальной пирамиды, которому в общесве уготавливается роль слоя служащего диктатуне: хакеры, программисты ОС, которые даже при своей открытости стали настолько гигантски(внесомненно кастовая кочерга для тех кто ещё не потерял совесть и рассудок) что проще научится прогрить на асме. мастурбацией? ой-йой!!!видели бы себя со стороны. ничем нехуже шакалов, правда умудряющихся ещё и мылостыню выпростить иногда.
     

     Добавить комментарий
    Имя:
    E-Mail:
    Заголовок:
    Текст:




    Партнёры:
    PostgresPro
    Inferno Solutions
    Hosting by Hoster.ru
    Хостинг:

    Закладки на сайте
    Проследить за страницей
    Created 1996-2024 by Maxim Chirkov
    Добавить, Поддержать, Вебмастеру