首页 网络安全 安全学院 查看内容

针对路由器设备上的netfilter hook方法的入门实践

2017-4-6 11:27 |来自: 互联网 5031 0

摘要: 前言你有没有想过你所访问的流量被重定向到某处正在被别人分(hen)析(lu),你有没有奇怪为何你未点任何可疑连接而自己的账号密码却遭到泄露,你有没有怀疑不断弹出的小广告到底从何而来?所有的这一切,可能正是 ...

前言

你有没有想过你所访问的流量被重定向到某处正在被别人分(hen)析(lu),你有没有奇怪为何你未点任何可疑连接而自己的账号密码却遭到泄露,你有没有怀疑不断弹出的小广告到底从何而来?所有的这一切,可能正是因为你连接的路由器被别人做了手脚,而他们也许用到的技术就是netfilter hook。在本文中,我首先会简单的介绍netfilter hook技术,随后,将重点放在如何编译出一款在路由器上可用的netfilter模块并给出测试结果。

netfilter hook技术简介

netfilter技术在网上已经介绍的很多,不太了解的朋友,可以参考这篇 文章 ,出自著名黑客杂志phrack,表示不想看英文的同学,可以参考这篇翻译过来的 文章 。当然,已经有高手利用这项技术做过很多事了,可参考 基于netfilter的高效广告植入 和 基于netfilter编程的DNS数据伪造

在这些文章中,虽然给出了netfilter hook的实现思路,甚至是利用代码,但却没有讲解如何编译出一款路由器可用的netfilter hook模块。本人在收集了很多资料,踩了很多坑的基础上,终于编译出一款在路由器上可用的netfilter hook模块(仅作实验用),在此供大家参考。后文主要分为两大部分,第一部分是内核编译环境的搭建;第二部分是测试netfilter hook模块。编译环境搭建部分由于嵌套了很多我踩坑的经验,因而会比较长,熟悉的高手,可直接跳过。

内核编译环境搭建

在IoT设备程序开发及编译环境搭建初体验这篇文章中,我们是直接下载的已经配置好的编译工具,但在内核模块编译中,对模块版本信息的审查十分严格,需要对照着IoT设备的系统信息配置相应的交叉编译环境。因此,首先第一步要做的就是获取IoT设备的各项系统信息。下面,我还是以小米路由器3为实验设备,介绍如何编译一个可成功运行的内核模块。配置步骤主要参照 这里,本文在配置过程中有些许的不一样,索性重写一遍,并将我遇到的问题也穿插进去。

第一步 IoT设备各项系统信息获取

ssh登录到路由器,执行cat /proc/cpuinfo

root@XiaoQiang:~# cat /proc/cpuinfo 
system type             : Ralink SoC
processor               : 0
cpu model               : MIPS 24Kc V5.0
BogoMIPS                : 385.84
wait instruction        : yes
microsecond timers      : yes
tlb_entries             : 32
extra interrupt vector  : yes
hardware watchpoint     : yes, count: 4, address/irw mask: [0x0000, 0x0ff8, 0x0ff8, 0x0ff8]
ASEs implemented        : mips16 dsp
shadow register sets    : 1
core                    : 0
VCED exceptions         : not available
VCEI exceptions         : not available

CPU 型号是MIPS 24Kc V5.0,在 MIPS集合 和 MIPS型号 中,可查看到该CPU架构为MIPS Ⅱ型。    

同时,在小米路由器上远程拷贝一个可执行程序到本地,并查看它的文件信息,如下所示:

[ker@localhost kernel]$ scp root@192.168.9.250:/usr/bin/curl ./
root@192.168.9.250's password: 
curl                                                         100%   97KB  96.9KB/s   00:01    
[ker@localhost kernel]$ file curl 
curl: ELF 32-bit LSB executable, MIPS, MIPS32 version 1, dynamically linked (uses shared libs), corrupted section header size

可以确定,它是MIPS32 version 1(基于MIPS Ⅱ的一个版本),32位系统。

查看路由器内核、gcc版本和uClibc版本,运行cat/proc/version,可以看到内核版本和gcc版本;运行ls-l  /lib/ | grep uCl,为查看uClibc版本。

root@XiaoQiang:~# cat /proc/version 
Linux version 2.6.36 (jenkins@xiaoqiang-OptiPlex-9010) (gcc version 4.6.3 20120201 (prerelease) (Linaro GCC 4.6-2012.02) ) #1 Thu Aug 18 10:53:53 CST 2016
root@XiaoQiang:~# ls -l  /lib/ | grep uCl
-rwxrwxr-x    1 root     root         28966 Aug 18 10:25 ld-uClibc-0.9.33.2.so
lrwxrwxrwx    1 root     root            21 Aug 18 10:52 ld-uClibc.so.0 -> ld-uClibc-0.9.33.2.so
lrwxrwxrwx    1 root     root            21 Aug 18 10:52 libc.so.0 -> libuClibc-0.9.33.2.so
-rwxrwxr-x    1 root     root        589093 Aug 18 10:25 libuClibc-0.9.33.2.so

可以看到,内核版本为2.6.36,gcc版本为4.6.3,uClibc版本为0.9.33.2。

同样,还需要获取的是CPU是大端存储,还是小端存储,可以参考这篇文章。而我是通过最笨的方法,大小端都各编了一个,拿去试出来的,最后发现它是小端存储(little endian)。

到此,路由器系统的信息基本收集完毕,并在下面的交叉编译器的配置过程中会用到,总结如下:

CPU架构:MIPS Ⅱ型, 32位,little endian;

内核版本:2.6.36

gcc版本:4.6.3

uClibc版本:0.9.33.2

第二步 交叉编译环境配置与搭建

此过程,我最终是在centos6.2 32位系统下完成,在Ubuntu12.04 32位下,折腾了很久,有一个gcc-4.6.3的编译bug始终绕不过,换到centos下竟然可以,具体原因实在想不通,有遇到过这个问题的牛牛们可以解答下。下面,我们就以centos为例进行描述。

以普通用户登陆centos,切换到root权限下,安装各个依赖包。

[ker@localhost ~]$ su -
[ker@localhost ~]$ yum install gcc
[ker@localhost ~]$ yum -y install gcc gcc-c++ bison flex gperf texinfo patch libtool ncurses-devel expat expat-devel
[ker@localhost ~]$ su ker   //回到普通用户

需要说明的是,除了安装依赖包在root用户下,下面的其他操作,建议都在普通用户下进行,以防在后面引起不必要的错误。

接下来,按照下面的步骤,安装交叉编译包crosstool-ng:

[ker@localhost Downloads]$ wget http://www.crosstool-ng.org/download/crosstool-ng/crosstool-ng-1.18.0.tar.bz2
[ker@localhost Downloads]$ tar xjf crosstool-ng-1.18.0.tar.bz2
[ker@localhost Downloads]$ cd crosstool-ng-1.18.0/
[ker@localhost crosstool-ng-1.18.0]$ mkdir $HOME/bin/
[ker@localhost crosstool-ng-1.18.0]$ mkdir $HOME/bin/crosstool-ng
[ker@localhost crosstool-ng-1.18.0]$ ./configure --prefix=$HOME/bin/crosstool-ng
[ker@localhost crosstool-ng-1.18.0]$ make
[ker@localhost crosstool-ng-1.18.0]$ make install
[ker@localhost crosstool-ng-1.18.0]$ export PATH="${PATH}:$HOME/bin/crosstool-ng/bin"

下载linux-2.6.36内核包,并解压:

[ker@localhost Downloads]$ wget https://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.36.tar.bz2
[ker@localhost Downloads]$ tar xjf linux-2.6.36.tar.bz2

建立适用于MIPS架构的交叉编译配置文件夹并开始配置:

[ker@localhost crosstool-ng-1.18.0]$ mkdir $HOME/toolchain
[ker@localhost crosstool-ng-1.18.0]$ cd $HOME/toolchain
[ker@localhost toolchain]$ ct-ng menuconfig

进入Target options

Target Architecture (mips)

Endianness: (Little endian)

Bitness: (32-bit)

ABI (o32)


进入Operating System

选择Linux kernel version为(custom tarball or directory),将custom tarballor or directory设置成下载的Linux-2.6.36的内核路径。


进入C compiler选择:

gcc version(4.6.3)

进入C-library选择:

C library (uClibc)

uClibc version (0.9.33.2)


进入Debug facilities选择:

开启strace


按两下ESC,保存并退出。


到此,crosstool配置完成,配置用到的信息就是在第一步中查询的信息。运行: 

[ker@localhost toolchain]$ ct-ng build

这条命令回去下载对应的gcc,uClibc的安装包,并存放在当前目录的.build/tarballs下:

[ker@localhost toolchain]$ cd .build/tarballs
[ker@localhost tarballs]$ ll
total 107592
-rwxrw-r--. 1 ker ker 19973532 Mar 25 15:48 binutils-2.22.tar.bz2
-rwxrw-r--. 1 ker ker   772737 Mar 25 15:48 cloog-ppl-0.15.11.tar.gz
-rwxrw-r--. 1 ker ker 71999439 Mar 25 15:48 gcc-4.6.3.tar.bz2
-rwxrw-r--. 1 ker ker  2024576 Mar 25 15:48 gmp-5.0.2.tar.bz2
-rwxrw-r--. 1 ker ker   148529 Mar 25 15:48 libelf-0.8.13.tar.gz
-rwxrw-r--. 1 ker ker   565953 Mar 25 15:48 mpc-0.9.tar.gz
-rwxrw-r--. 1 ker ker  1083128 Mar 25 15:48 mpfr-3.1.0.tar.xz
-rwxrw-r--. 1 ker ker 10346588 Mar 25 15:48 ppl-0.11.2.tar.lzma
-rwxrw-r--. 1 ker ker   403788 Mar 25 15:48 strace-4.6.tar.xz
-rwxrw-r--. 1 ker ker  2834029 Mar 25 15:48 uClibc-0.9.33.2.tar.bz2

可以手动下载这些安装包,并拷贝到该目录下,否则自动下载,需要等待很久的时间(也会出问题),这里给出一些包的下载连接:

http://mirrors.ibiblio.org/gnu/ftp/gnu/gcc/gcc-4.6.3/gcc-4.6.3.tar.bz2

http://ftp.gnu.org/gnu/binutils/binutils-2.22.tar.bz2

http://repository.timesys.com/buildsources/u/uClibc/uClibc-0.9.33.2/uClibc-0.9.33.2.tar.bz2

同时,在进一步build的过程中,会出现这个错误:

[ERROR]    You did not provide a uClibc config file!

这是需要对uClibc进行配置并生成.config文件,进入uClibc的解压目录:

[ker@localhost toolchain]$ cd .build/src/uClibc-0.9.33.2/
[ker@localhost uClibc-0.9.32]$ make menuconfig

选择Target Architecture:

Target Architecture (mips)


进入Target Architecture Features and Options:

Target Processor Architecture (MIPS II)

Target Processor Endianness (LittleEndian)


保存并退出。此时,记下当前目录的路径并回到toolchain目录下,运行:

[ker@localhost toolchain]$ cd ../../../
[ker@localhost toolchain]$ ct-ng  menuconfig

在C-library中,配置configurationfile的路径,为上一步生成的uClibc的.config文件的路径:


配置好后,保存并退出。

重新build,同样运行:

[ker@localhost toolchain]$ ct-ng build

此时,可以安心等待build完成,一般要花25分钟左右。build完成后,交叉编译的工具会被安装到$HOME/x-tools/,将其加入环境变量中:

[ker@localhost ~]$ export PATH="${PATH}:$HOME/x-tools/mipsel-unknown-linux-uclibc/bin"

到此,交叉编译工具即编译完成,下一步,开始编译MIPS架构的内核。

第三步,MIPS架构的内核编译环境搭建

进入 linux-2.6.36

[ker@localhost Downloads]$ cd linux-2.6.36/
[ker@localhost linux-2.6.36]$ export ARCH=mips
[ker@localhost linux-2.6.36]$ export CROSS_COMPILE=$HOME/x-tools/mipsel-unknown-linux-uclibc/bin/mipsel-unknown-linux-uclibc-
[ker@localhost linux-2.6.36]$ make menuconfig

在Machine selection 中,选择

Select System type (MIPS Malta board)


在Endianess selection中,选择:

Select Endianess selection (Little endian)


在CPU selection中,选择:

Select CPU type (MIPS32 Release 2)


保存并退出。用make开始编译内核:

[ker@localhost linux-2.6.36]$ make –j4    //开4个线程

make完成后,运行make modules:

[ker@localhost linux-2.6.36]$ make modules

这条命令的目的是产生Module.symvers文件,下文编译内核模块要用到,自此,内核模块的编译也讲完了。下面,就可以对netfilter模块进行编译和测试了。

Netfilter hook模块的测试

编译过程中,可能会产生错误,为内核的bug,参考这个连接:

https://github.com/AshishPrasad/BTP/blob/master/2.6.36%20Kernel%20Compilation%20Problems

一开始,找到一篇 现成的帖子 (包括代码和编译调试命令),欣喜之,遂赶紧调试, 代码微改如下。

#define __KERNEL__
#define MODULE
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/skbuff.h>
#include <linux/ip.h> /* For IP header */
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
/* This is the structure we shall use to register our function */
static struct nf_hook_ops nfho;

/* This is the hook function itself */
unsigned int hook_func(unsigned int hooknum,
                       struct sk_buff **skb,
		       const struct net_device *in,
		       const struct net_device *out,
                       int (*okfn)(struct sk_buff *))
{
	struct sk_buff *sb = *skb;
	iph = ip_hdr(skb); 
        printk("iph->saddr is %d \n", sb->nh.iph->saddr);       
        printk("iph->daddr is %d \n", sb->nh.iph->daddr);       
	return NF_ACCEPT;
}
/* Initialisation routine */
int init_module()
{
	/* Fill in our hook structure */
	nfho.hook = hook_func;
	/* Handler function */
	nfho.hooknum = NF_IP_PRE_ROUTING; /* First for IPv4 */
	nfho.pf = PF_INET;
	nfho.priority = NF_IP_PRI_FIRST; /* Make our func first */
	nf_register_hook(&nfho);
	return 0;
}
/* Cleanup routine */
void cleanup_module()
{
    nf_unregister_hook(&nfho);
}

该模块的作用是查看能否hook并解析到网络数据包的ip。用insmod将其注入到内核中,先是在centos上做测试,系统直接挂掉,排查后发现,这个例子只适用于linux-2.4到linux2.6.20内核的版本,高版本的内核sk_buff结构体发生了变化,要用如下的方法才能准确获取ip头,可以参考: 

http://blog.chinaunix.net/uid-24977843-id-3011050.html

http://blog.csdn.net/guowenyan001/article/details/41284723

建议:保险起见,调试内核过程中,特别是用到的一些结构体和函数,还是得多翻一翻内核源码。 

后将程序改为如下,才能正常运行:

#ifndef __KERNEL__
#define __KERNEL__
#endif

#ifndef MODULE
#define MODULE
#endif

#include <linux/module.h>
#include 
                      声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 
                      [邮箱地址] 删除
                    				    
                                        

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部