首页 -> 安全研究

安全研究

绿盟月刊
绿盟安全月刊->第18期->技术专题
期刊号: 类型: 关键词:
kvm编程举例

作者:小四 < mailto: scz@nsfocus.com >
主页:http://www.nsfocus.com
日期:2001-02-05

Q: 为了避免shmget()不必要的失败,想在C代码中获取shmsys:shminfo_shmmax的值。
   但是不能读取/etc/system,那样很不可靠。

   set shmsys:shminfo_shmmax = 0x2000000

A: <hume.spamfilter@bofh.halifax.ns.ca>

首先执行如下shell命令
# echo 'shminfo_shmmax/D' | adb -k
physmem fddb
shminfo_shmmax:
shminfo_shmmax: 134217728

于是我们可以编写如下C代码

--------------------------------------------------------------------------
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <kvm.h>
#include <nlist.h>
#include <fcntl.h>

#define KERN_NAMELIST "/dev/ksyms"
/* should not be /dev/kmem */
#define KERN_CORE     "/dev/mem"

int main ( int argc, char * argv[] )
{
    kvm_t *      krn = NULL;
    struct nlist nms[2];
    unsigned int val;

    if ( ( krn = kvm_open( KERN_NAMELIST, KERN_CORE, NULL, O_RDONLY, argv[0] ) ) == NULL )
    {
        exit( -1 );
    }
    nms[0].n_name  = "shminfo_shmmax";
    nms[0].n_value = 0;
    nms[0].n_type  = 0;
    nms[1].n_name  = NULL;
    nms[1].n_value = 0;
    nms[1].n_type  = 0;
    if ( kvm_nlist( krn, nms ) == 0 )
    {
        if ( nms[0].n_type != 0 )
        {
            if ( kvm_read( krn, nms[0].n_value, ( char * )&val, sizeof( val ) ) != 4 )
            {
                fprintf( stderr, "Unable to fetch shminfo_shmmax.\n" );
            }
            else
            {
                fprintf( stdout, "shminfo_shmmax = %ld\n", val );
            }
        }
        else
        {
            fprintf( stderr, "Unable to fetch shminfo_shmmax.\n" );
        }
    }
    kvm_close( krn );
    exit( 0 );
}  /* end of main */
--------------------------------------------------------------------------

关于kvm_*()系列函数,可以man -s 3k kvm_nlist等等,但是Sun强烈反对利用
kvm_*()函数,几乎没有兼容性、可移植性可言。

A: scz <scz@nsfocus.com>

原代码有误,应该使用/dev/mem作为corefile,而不是/dev/kmem,上例已经做了
修改。编译命令如下:

Solaris 2.6/7 32-bit
gcc -O3 -o getshm getshm.c -lkvm

Solaris 7 64-bit
/opt/SUNWspro/SC5.0/bin/cc -xarch=v9 -O -o getshm getshm.c -lkvm

这个程序只能以root身份运行,应用受限。

顺便我们介绍Mark Henderson提供的sethostid.c,该代码用于修改Solaris 2.6/7系
统上的hostid,如果企图用于Solaris 7,必须用Workshop 5.0编译成64-bit应用程
序,否则无法成功。很多朋友抱怨过change-sun-hostid.tar.gz里的代码无法用于7,
正是这个毛病,我已经增加了64-bit编译注释。

--------------------------------------------------------------------------
/*
* Mark Henderson < mailto: mch@squirrel.com >
*
* 必须以root身份运行,至少拥有/dev/kmem的读写权限
*
* Solaris 2.6 32-bit
* gcc -O3 -o sethostid sethostid.c -lelf
*
* Solaris 2.7 64-bit
* /opt/SUNWspro/SC5.0/bin/cc -xarch=v9 -O -o sethostid sethostid.c -lelf
*
* 不带参数运行直接返回当前hw_serial
* 带参数运行则设置内存映像hw_serial,命令行参数采用16进制指定
*
* June 1996
*/

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <nlist.h>

int main ( int argc, char * argv[] )
{
    struct nlist nl[2];
    int          kmem;
    off_t        where;
    unsigned int new_hostid;  /* scz: 这里必须避免使用u_long,64-bit问题 */
    u_char       hw_serial[12];
    u_char       new_hw_serial[12];

    if ( ( kmem = open( "/dev/kmem", O_RDWR ) ) < 0 )
    {
        fprintf( stderr, "cannot open /dev/kmem\n" );
        exit( 1 );
    }
    nl[0].n_name = "hw_serial";
    nl[1].n_name = NULL;
    if ( nlist( "/dev/ksyms", nl ) < 0 )
    {
        fprintf( stderr, "cannot read namelist out of /dev/ksyms\n" );
        exit( 1 );
    }
    if ( ( where = nl[0].n_value ) == 0 )
    {
        fprintf( stderr, "unknown kernel variable hw_serial\n" );
        exit( 1 );
    }
    if ( lseek( kmem, where, SEEK_SET ) == -1 )
    {
        fprintf( stderr, "lseek on /dev/kmem failed\n" );
        exit( 1 );
    }
    if ( read( kmem, ( char * )&hw_serial[0], 12 ) < 12 )
    {
        fprintf( stderr, "read from /dev/kmem failed\n" );
        exit( 1 );
    }
    if ( lseek( kmem, where, SEEK_SET ) == -1 )
    {
        fprintf( stderr, "lseek on /dev/kmem failed\n" );
        exit( 1 );
    }
    fprintf( stderr, "current hostid is 0x%08x\n", strtoul( hw_serial, NULL, 10 ) );
    if ( argc > 1 )
    {
        if ( lseek( kmem, where, SEEK_SET ) == -1 )
        {
            fprintf( stderr, "lseek on /dev/kmem failed\n" );
            exit( 1 );
        }
        new_hostid = strtoul( argv[1], NULL, 16 );
        fprintf( stderr, "setting hostid to 0x%08x\n", new_hostid );
        sprintf( ( char * )&new_hw_serial, "%u", new_hostid );
        if ( write( kmem, ( char * )&new_hw_serial[0], strlen( new_hw_serial ) + 1 )
            < strlen(new_hw_serial) + 1 )
        {
            fprintf( stderr, "write to /dev/kmem failed\n" );
            exit( 1 );
        }
    }
    close( kmem );
    return( 0 );
}  /* end of main */
--------------------------------------------------------------------------

下面我们用kvm_*()改写一下上述代码,做个对比。

--------------------------------------------------------------------------
/*
* flamez  - Mark Henderson < mailto: mch@squirrel.com >
* greetz  - < mailto: hume.spamfilter@bofh.halifax.ns.ca >
* creditz -
*
* scz < mailto: scz@nsfocus.com >
*
* 必须以root身份运行,至少拥有/dev/kmem的读写权限
*
* Solaris 2.6 32-bit
* gcc -O3 -o sethostid sethostid.c -lkvm
*
* Solaris 2.7 64-bit
* /opt/SUNWspro/SC5.0/bin/cc -xarch=v9 -O -o sethostid sethostid.c -lkvm
*
* 不带参数运行直接返回当前hw_serial
* 带参数运行则设置内存映像hw_serial,命令行参数采用16进制指定
*
* Feb 2000
*/

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <kvm.h>
#include <nlist.h>
#include <fcntl.h>

#define KERN_NAMELIST ""
#define KERN_CORE     ""

int main ( int argc, char * argv[] )
{
    kvm_t *      kmem = NULL;
    struct nlist nl[2];
    u_char       hw_serial[12];
    u_char       new_hw_serial[12];
    unsigned int new_hostid;  /* scz: 这里必须避免使用u_long,64-bit问题 */

    if ( ( kmem = kvm_open( "/dev/ksyms", "/dev/mem", NULL, O_RDWR, argv[0] ) ) == NULL )
    {
        exit( -1 );
    }
    nl[0].n_name  = "hw_serial";
    nl[0].n_value = 0;
    nl[0].n_type  = 0;
    nl[1].n_name  = NULL;
    nl[1].n_value = 0;
    nl[1].n_type  = 0;
    if ( kvm_nlist( kmem, nl ) == 0 )
    {
        if ( nl[0].n_type != 0 )
        {
            if ( kvm_read( kmem, nl[0].n_value, ( char * )&hw_serial, sizeof( hw_serial ) )
                 != sizeof( hw_serial ) )
            {
                fprintf( stderr, "Unable to fetch hw_serial.\n" );
            }
            else
            {
                fprintf( stdout, "current hostid is 0x%08x\n", strtoul( &hw_serial, NULL, 10 ) );
                if ( argc > 1 )
                {
                    new_hostid = strtoul( argv[1], NULL, 16 );
                    fprintf( stderr, "setting hostid to 0x%08x\n", new_hostid );
                    sprintf( ( char * )&new_hw_serial, "%u", new_hostid );
                    if ( kvm_write( kmem, nl[0].n_value, ( char * )&new_hw_serial, strlen( new_hw_serial ) + 1 )
                         != strlen( new_hw_serial ) + 1 )
                    {
                        fprintf( stderr, "write to /dev/kmem failed\n" );
                    }
                }
            }
        }
        else
        {
            fprintf( stderr, "Unable to fetch hw_serial.\n" );
        }
    }
    kvm_close( kmem );
    exit( 0 );
}  /* end of main */
--------------------------------------------------------------------------

个人不推荐在大型应用软件中使用kvm_*()系列函数。有些可配置系统参数可以通过
sysconf(3C)获取。
版权所有,未经许可,不得转载