LINUX.ORG.RU

[kernel] Перехват функций ядра.


0

1

Добрый день.
Хочу узнать, из-за чего происходит подобное.
Я ставлю перехватчик на какую-то функцию ядра. (через обыкновенный jmp). При этом, перехватываемая и функция перехватчик абсолютно идентичны. Получается следующее.
«Работа модуля»
Функция ядра
Функция ядра
Функция ядра
Функция перехватчик (заместили собой другую ф-ю)
Функция ядра
Функция ядра

Замечу такой способ перехвата, всегда работал на ура, и никогда никаких проблем не было замечено.
Однако произошло исключение из правила, поставил перехватчик на функцию tcp_sendpage и должно было быть всё, как обычно, - работать без сбоев - идеально.


int tcp_sendpage(struct sock *sk, struct page *page, int offset,
		 size_t size, int flags)
{
	ssize_t res;

	if (!(sk->sk_route_caps & NETIF_F_SG) ||
	    !(sk->sk_route_caps & NETIF_F_ALL_CSUM))
		return sock_no_sendpage(sk->sk_socket, page, offset, size,
					flags);

	lock_sock(sk);
	TCP_CHECK_TIMER(sk);
	res = do_tcp_sendpages(sk, &page, offset, size, flags);
	TCP_CHECK_TIMER(sk);
	release_sock(sk);
	return res;
}




Но при передачи данных (при работе перехватчика) вылетает следующая ошибка.

BUG: unable to handle kernel NULL pointer dereference at 00000044
Dec  4 11:45:59 localhost kernel: IP: [<c0387d1a>] sock_sendmsg+0xca/0x100
Dec  4 11:45:59 localhost kernel: *pde = 00000000 
Dec  4 11:45:59 localhost kernel: Oops: 0000 [#8] SMP 
Dec  4 11:45:59 localhost kernel: last sysfs file: /sys/devices/pci0000:00/0000:00:01.1/host0/target0:0:0/0:0:0:0/block/sda/sda1/uevent

G      D    2.6.33.7-desktop-2mnb #1 /VirtualBox
Dec  4 11:45:59 localhost kernel: EIP: 0060:[<c0387d1a>] EFLAGS: 00010246 CPU: 0
Dec  4 11:45:59 localhost kernel: EIP is at sock_sendmsg+0xca/0x100
Dec  4 11:45:59 localhost kernel: EAX: cc179c70 EBX: 00000000 ECX: cc179d54 EDX: d88bd84c
Dec  4 11:45:59 localhost kernel: ESI: 00001000 EDI: cc179d54 EBP: cc179d2c ESP: cc179c68
Dec  4 11:45:59 localhost kernel: DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068
Dec  4 11:45:59 localhost kernel: Stack:
Dec  4 11:45:59 localhost kernel: 00001000 00000000 00000082 00000082 00000000 00000001 ffffffff 00000000
Dec  4 11:45:59 localhost kernel: <0> 00000000 00000000 00000000 00000000 f3738c80 00000000 00000000 c07141b3
Dec  4 11:45:59 localhost kernel: <0> 00000016 cc179ce0 cc179d2c 00000246 cc179d48 00000082 00000034 fa30063c
Dec  4 11:45:59 localhost kernel: Call Trace:
Dec  4 11:45:59 localhost kernel: [<c01d2a2f>] ? page_address+0xbf/0xe0
Dec  4 11:45:59 localhost kernel: [<c01d2a2f>] ? page_address+0xbf/0xe0
Dec  4 11:45:59 localhost kernel: [<c04292ab>] ? printk+0x1d/0x22
Dec  4 11:45:59 localhost kernel: [<c0387d85>] ? kernel_sendmsg+0x35/0x50
Dec  4 11:45:59 localhost kernel: [<fa2f873f>] ? my_sock_no_sendpage+0xbf/0xf0 [мой модуль]
Dec  4 11:45:59 localhost kernel: [<fa2f87f6>] ? my_tcp_sendpage+0x86/0xb0 [мой модуль]
Dec  4 11:45:59 localhost kernel: [<c03cbac0>] ? tcp_sendpage+0x0/0x470
Dec  4 11:45:59 localhost kernel: [<c0386afd>] ? kernel_sendpage+0x2d/0x50
Dec  4 11:45:59 localhost kernel: [<c0386b62>] ? sock_sendpage+0x42/0x50
Dec  4 11:45:59 localhost kernel: [<c0212487>] ? pipe_to_sendpage+0x87/0x90
Dec  4 11:45:59 localhost kernel: [<c0386b20>] ? sock_sendpage+0x0/0x50
Dec  4 11:45:59 localhost kernel: [<c02124ec>] ? splice_from_pipe_feed+0x5c/0x100
Dec  4 11:45:59 localhost kernel: [<c0212400>] ? pipe_to_sendpage+0x0/0x90
Dec  4 11:45:59 localhost kernel: [<c0389903>] ? sock_aio_write+0x113/0x130
Dec  4 11:45:59 localhost kernel: [<c021289c>] ? __splice_from_pipe+0x5c/0x70
Dec  4 11:45:59 localhost kernel: [<c0212400>] ? pipe_to_sendpage+0x0/0x90
Dec  4 11:45:59 localhost kernel: [<c0212913>] ? splice_from_pipe+0x63/0x80
Dec  4 11:45:59 localhost kernel: [<c0212970>] ? generic_splice_sendpage+0x0/0x30
Dec  4 11:45:59 localhost kernel: [<c0212996>] ? generic_splice_sendpage+0x26/0x30
Dec  4 11:45:59 localhost kernel: [<c0212400>] ? pipe_to_sendpage+0x0/0x90
Dec  4 11:45:59 localhost kernel: [<c0212e1f>] ? vfs_splice_from+0x5f/0x90
Dec  4 11:45:59 localhost kernel: [<c0212e7b>] ? direct_splice_actor+0x2b/0x40
Dec  4 11:45:59 localhost kernel: [<c0212c3f>] ? splice_direct_to_actor+0xcf/0x1d0
Dec  4 11:45:59 localhost kernel: [<c0212e50>] ? direct_splice_actor+0x0/0x40
Dec  4 11:45:59 localhost kernel: [<c0212d9d>] ? do_splice_direct+0x5d/0x80
Dec  4 11:45:59 localhost kernel: [<c01f47f7>] ? do_sendfile+0x1a7/0x230
Dec  4 11:45:59 localhost kernel: [<c01f513b>] ? sys_sendfile64+0x8b/0xb0
Dec  4 11:45:59 localhost kernel: [<c042c2a4>] ? syscall_call+0x7/0xb
Dec  4 11:45:59 localhost kernel: Code: 89 55 d0 89 4d c0 e8 56 2b ee ff 85 c0 75 1f 8b 43 24 89 da 89 f9 89 34 24 89 85 40 ff ff ff 8b 9d 40 ff ff ff 8d 85 44 ff ff ff <ff> 53 44 3d ef fd ff ff 74 14 8b 5d f4 8b 75 f8 8b 7d fc 89 ec 
Dec  4 11:45:59 localhost kernel: EIP: [<c0387d1a>] sock_sendmsg+0xca/0x100 SS:ESP 0068:cc179c68
Dec  4 11:45:59 localhost kernel: CR2: 0000000000000044



Вот список вызываемых функций.
1) my_tcp_sendpage - функция перехватчик.
2) my_sock_no_sendpage - тоже моя функция (копия оригинала из кода ядра) вот ошибка sock->ops = (null) sock->ops должен указывать на функции сокета, а после моего перехвата, по каким-то причинам он равен нулю!!!
3) kernel_sendmsg
4) sock_sendmsg
5) __sock_sendmsg (в этой функции oops происходит, как полагаю на
этом return sock->ops->sendmsg(iocb, sock, msg, size) ) ведь sock->ops = (null) проверено в функции my_sock_no_sendpage

Вот код этих функций из ядра.

int tcp_sendpage(struct sock *sk, struct page *page, int offset,
		 size_t size, int flags)
{
	ssize_t res;

	if (!(sk->sk_route_caps & NETIF_F_SG) ||
	    !(sk->sk_route_caps & NETIF_F_ALL_CSUM))
		return sock_no_sendpage(sk->sk_socket, page, offset, size,
					flags);

	lock_sock(sk);
	TCP_CHECK_TIMER(sk);
	res = do_tcp_sendpages(sk, &page, offset, size, flags);
	TCP_CHECK_TIMER(sk);
	release_sock(sk);
	return res;
}




ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags)
{
	ssize_t res;
	struct msghdr msg = {.msg_flags = flags};
	struct kvec iov;
	char *kaddr = kmap(page);
	iov.iov_base = kaddr + offset;
	iov.iov_len = size;
	res = kernel_sendmsg(sock, &msg, &iov, 1, size);
	kunmap(page);
	return res;
}



int kernel_sendmsg(struct socket *sock, struct msghdr *msg,
		   struct kvec *vec, size_t num, size_t size)
{
	mm_segment_t oldfs = get_fs();
	int result;

	set_fs(KERNEL_DS);
	/*
	 * the following is safe, since for compiler definitions of kvec and
	 * iovec are identical, yielding the same in-core layout and alignment
	 */
	msg->msg_iov = (struct iovec *)vec;
	msg->msg_iovlen = num;
	result = sock_sendmsg(sock, msg, size);
	set_fs(oldfs);
	return result;
}




int sock_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
{
	struct kiocb iocb;
	struct sock_iocb siocb;
	int ret;

	init_sync_kiocb(&iocb, NULL);
	iocb.private = &siocb;
	ret = __sock_sendmsg(&iocb, sock, msg, size);
	if (-EIOCBQUEUED == ret)
		ret = wait_on_sync_kiocb(&iocb);
	return ret;
}





static inline int __sock_sendmsg(struct kiocb *iocb, struct socket *sock,
				 struct msghdr *msg, size_t size)
{
	struct sock_iocb *si = kiocb_to_siocb(iocb);
	int err;

	sock_update_classid(sock->sk);

	si->sock = sock;
	si->scm = NULL;
	si->msg = msg;
	si->size = size;

	err = security_socket_sendmsg(sock, msg, size);
	if (err)
		return err;

	return sock->ops->sendmsg(iocb, sock, msg, size);
}









Ктонибудь может мне объяснит в чём может быть заключена проблема???

Deleted

BUG: unable to handle kernel NULL pointer dereference at 00000044
...
Dec  4 11:45:59 localhost kernel: [<c01d2a2f>] ? page_address+0xbf/0xe0
Dec  4 11:45:59 localhost kernel: [<c01d2a2f>] ? page_address+0xbf/0xe0
Dec  4 11:45:59 localhost kernel: [<c04292ab>] ? printk+0x1d/0x22
Dec  4 11:45:59 localhost kernel: [<c0387d85>] ? kernel_sendmsg+0x35/0x50
Dec  4 11:45:59 localhost kernel: [<fa2f873f>] ? my_sock_no_sendpage+0xbf/0xf0 [мой модуль]
Dec  4 11:45:59 localhost kernel: [<fa2f87f6>] ? my_tcp_sendpage+0x86/0xb0 [мой модуль]
Dec  4 11:45:59 localhost kernel: [<c03cbac0>] ? tcp_sendpage+0x0/0x470
...

может быть обращение по нулевому указателю где-то? в printk из kernel_sendmsg например?

Что за термин такой «перехватчик»?

nanoo_linux ()
Ответ на: комментарий от nanoo_linux

может быть обращение по нулевому указателю где-то? в printk из kernel_sendmsg например?


Нет, это обращение не где-то а именно в __sock_sendmsg вот тут return sock->ops->sendmsg(iocb, sock, msg, size);
Как я и описал, почему-то в функции my_sock_no_sendpage ошибка sock->ops = (null) sock->ops должен указывать на функции сокета, а после вызова моего перехватчика, по каким-то причинам он равен null .
Вот я никак не пойму, почему в мою функцию my_tcp_sendpage (полная копия tcp_sendpage), не передаётся оригинальный struct sock *sk (у которого sock->ops != null.... ) точнее он передаётся (адрес ниже 0xc), но ops не указан.

Что за термин такой «перехватчик»?


Ну а как мне его ещё назвать?

Deleted ()

>Я ставлю перехватчик на какую-то функцию ядра. (через обыкновенный jmp).

Ничего не понял. Динамически патчишь код что ли?

ttnl ★★★★★ ()

> Code: 89 55 d0 89 4d c0 e8 ...

что-то не похоже на sock_sendmsg().

the faulting insn у нас «call *0x44(%ebx)», ebx = 0.
потом проверка на 0xfffffdef == -EIOCBQUEUED. похоже
да, ->ops == NULL.

но:

BUG: unable to handle kernel NULL pointer dereference at 00000044


это соответствует sock->ops->recvmsg, а не sendmsg.


перехватчик на какую-то функцию ядра. (через

обыкновенный jmp)



уверен, ты делаешь это неправильно. впрочем, не факт,
что наблюдаемый oops с этим связан.

если что, есть kprobes. ну, и systemtap.

idle ★★★★★ ()
Ответ на: комментарий от ttnl

ttnl

Динамически патчишь код что ли?


Вобщем да, записываю прыжок на свою функцию, в пролог перехватываемой функции.

idle

уверен, ты делаешь это неправильно


100% делаю это правильно.

впрочем, не факт, что наблюдаемый oops с этим связан.

если что, есть kprobes. ну, и systemtap.


Наблюдаемый oops связан именно с этим, т.к. если убрать мой перехватчик и дать выполниться оригинальному коду по адресу, куда я записывал jmp то этой ошибки не будет.
Если не сложно, поясните как вы определили что это соответствует sock->ops->recvmsg, а не sendmsg.

Deleted ()
Ответ на: комментарий от Deleted

> > уверен, ты делаешь это неправильно



100% делаю это правильно.



не так просто сделать это правильно. ну, как знаешь.

как вы определили что это соответствует sock->ops->recvmsg


смещение посчитал. только, сорри, я ошибся. я смотрел
зачем-то в «struct proto» вместо proto_ops. но там
тоже ->sendmsg никак не получается.

да вообще, asm какой-то странный. откуда там
«call 0xffee2b61» ? это не похоже на правильный
address из ore_kernel_text(). там должен быть
вызов security_socket_sendmsg().

->ops из стека достается почему-то... странно.
но может я ошибся.

idle ★★★★★ ()
Ответ на: комментарий от idle

Извините за тупой вопрос, никогда не пользовался Systemtap
Можете пояснить как им пользоваться? :)
Просмотрел http://sourceware.org/systemtap/tutorial.pdf (англ не знаю, потому просто посмотрел, а не почитал)
Создал файл, с таким содержимым.(совершенно не осознавая, что он значит)

probe kernel.function("*@net/socket.c")
{
  printf(" <- %s\n", probefunc);
}


[root@localhost ]# stap deb.stp
semantic error: missing i386 kernel/module debuginfo under '/lib/modules/2.6.36/build' while resolving probe point kernel.function("*@net/socket.c")
Pass 2: analysis failed.  Try again with another '--vp 01' option.
[root@localhost ]# stap deb.stp --vp 01
semantic error: missing i386 kernel/module debuginfo under '/lib/modules/2.6.36/build' while resolving probe point kernel.function("*@net/socket.c")
Pass 2: analyzed script: 0 probe(s), 0 function(s), 0 embed(s), 0 global(s) using 16332virt/13032res/1868shr kb, in 10usr/90sys/264real ms.
Pass 2: analysis failed.  Try again with another '--vp 01' option.




Есть ли ресурсы на русском, где можно почитать про использование данной программы? (думаю она очень мощно поможет мне)

idle
Где вы там увидели «call 0xffee2b61» ?
И раз уж мы коснулись функции security_socket_sendmsg(), хочу узнать.
grep 'security_socket_sendmsg' -r вернул, два определения

kernel/linux-2.6.36/include/linux/security.h:static inline int security_socket_sendmsg(struct socket *sock,
// определение в одном файле
static inline int security_socket_sendmsg(struct socket *sock,
					  struct msghdr *msg, int size)
{
	return 0;
}


// определение в другом файле
kernel/linux-2.6.36/security/security.c:int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)

int security_socket_sendmsg(struct socket *sock, struct msghdr *msg, int size)
{
	return security_ops->socket_sendmsg(sock, msg, size);
}




И появился такой вопрос, какая из этих функций задействуется? та что в файле security/security.c ?

Deleted ()
Ответ на: комментарий от Deleted

>И появился такой вопрос, какая из этих функций задействуется? та что в файле security/security.c ?

#ifdef CONFIG_SECURITY_NETWORK ;)

ttnl ★★★★★ ()
Ответ на: комментарий от Deleted

я в ядре не силён, сам сейчас сижу разбираюсь. Так что с 100% (с) я ошибаюсь - там даже printf нету :)

nanoo_linux ()
Ответ на: комментарий от Deleted

> 100% делаю это правильно.

Если бы делал всё на 100% правильно то у тебя бы всё работало на 100% без oops'ов ж)

99.9% у тебя либо «уехал» стек, либо есть косяк с доступом к локальным данным для того юнита трансляции, функцию из которого ты патчишь.

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

anonymous ()
Ответ на: комментарий от anonymous

Отмечу ещё один интересный факт.
Как видим в sock_no_sendpage вызывается kernel_sendmsg.
Если поставить перехватчик только на kernel_sendmsg и посмотреть, вызывается ли она вообще, то окажется, что она не вызывается
По ходу происходит тоже самое, что я наблюдал с функцией generic_file_read

Сейчас попробую ещё чтонить сделать с кодом, и чуть позже отпишу, что вышло.

Deleted ()
Ответ на: комментарий от Deleted

> Сейчас попробую ещё чтонить сделать с кодом, и чуть позже отпишу, что вышло.

Кто-нибудь, позвоните куда-нибудь! :D

Хватит заниматься брутфорс-программингом. Добавь логов, возьми дамп памяти, дизасм и просто разберись, что у тебя вышло после очередного «сделаю ещё что-нибудь»

anonymous ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.