📄 568.htm
字号:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<title>CTerm非常精华下载</title>
</head>
<body bgcolor="#FFFFFF">
<table border="0" width="100%" cellspacing="0" cellpadding="0" height="577">
<tr><td width="32%" rowspan="3" height="123"><img src="DDl_back.jpg" width="300" height="129" alt="DDl_back.jpg"></td><td width="30%" background="DDl_back2.jpg" height="35"><p align="center"><a href="http://apue.dhs.org"><font face="黑体"><big><big>apue</big></big></font></a></td></tr>
<tr>
<td width="68%" background="DDl_back2.jpg" height="44"><big><big><font face="黑体"><p align="center"> ● UNIX网络编程 (BM: clown) </font></big></big></td></tr>
<tr>
<td width="68%" height="44" bgcolor="#000000"><font face="黑体"><big><big><p align="center"></big></big><a href="http://cterm.163.net"><img src="banner.gif" width="400" height="60" alt="banner.gif"border="0"></a></font></td>
</tr>
<tr><td width="100%" colspan="2" height="100" align="center" valign="top"><br><p align="center">[<a href="index.htm">回到开始</a>][<a href="15.htm">上一层</a>][<a href="569.htm">下一篇</a>]
<hr><p align="left"><small>发信人: cloudsky (小四), 信区: Security <br>
标 题: RPC/XDR/NFS系列之----RPC超时及破人xh <br>
发信站: 武汉白云黄鹤站 (Thu Feb 24 18:29:26 2000), 站内信件 <br>
标题:RPC/XDR/NFS系列之----RPC超时及破人xh <br>
概述: <br>
这个程序间接调用了portmapper的PMAPPROC_GETPORT远程过程。 <br>
同时程序中纠正了本系列前面所有程序中的一个错误所在,关于 <br>
signal和alarm函数的问题。破人xh说是要写关于siglongjmp <br>
的介绍文章,结果到今天也没有见到,先给他开个头,有这边 <br>
的问题不要问我,找他麻烦去吧。 <br>
参看本系列之 <br>
<< RPC/XDR/NFS系列之----rpcinfo利用 >> <br>
<< RPC/XDR/NFS系列之----RPC编程初战(2) >> <br>
<< RPC/XDR/NFS系列之----RPC编程初战(3) >> <br>
<< RPC/XDR/NFS系列之----RPC编程初战(4) >> <br>
测试: <br>
RedHat6.0 <br>
讨论: <br>
1. 前面给出的rpcscan有这么几种,一种是dump出portmapper的所有的信息, <br>
然后过滤,一种是利用RPC库例程直接向RPC Server发起连接以此判断 <br>
RPC Server是否存在,无论是结合shell script还是直接C语言编程也好, <br>
总之,我觉得效率太低。毕竟portmapper提供了PMAPPROC_GETPORT远程过 <br>
程,本来我想直接调用这个过程,结果在/usr/include/rpc/pmap_clnt.h <br>
中看到pmap_getport函数,于是就不费那个手脚了。 <br>
使用pmap_getport函数的好处在于,我们只和portmapper通信,只和111 <br>
端口通信,避免和复杂的RPC Server本身通信;也不需要dump出所有注册 <br>
信息,减少了网络传输。一般RPC Server启动后都会向portmapper注册自 <br>
己,所以只要我们能取得有效动态端口号,就可以认为相应的RPC Server <br>
已经启动。 <br>
函数原型如下: <br>
extern u_short pmap_getport __P ((struct sockaddr_in *__address, <br>
__const u_long __program, <br>
__const u_long __version, <br>
u_int __protocol)); <br>
用法如下: <br>
port = pmap_getport(address, program, version, protocol); <br>
但是这里存在另外一个问题,下面的信息来自pmap_prot.h: <br>
PMAPPROC_GETPORT(struct pmap) returns (long unsigned). <br>
0 is failure. Otherwise returns the port number where the pair <br>
[prog, vers] is registered. It may lie! <br>
注意最后一句话,It may lie,这是个警告。在网络世界里什么都可以伪装, <br>
一个被设计为陷阱的RPC Server注册自己,一个修改过的portmapper在响应 <br>
你,一个恶意的中间入侵者在spoofing。在不考虑这些的情况下,一个 <br>
RPC Server异常终止,于是没有来得及反注册自己,portmapper依旧保留着 <br>
这些已经失去意义的信息。所以,这个版本的rpcscan加快了扫描速度提高了 <br>
扫描效率是用其他代价换来的。好在扫描的目的不过是获得一个大概的信息, <br>
作为攻击探测,这个扫描足够理想。 <br>
2. 前面所有版本的rpcscan在处理超时时存在问题。因为抛开处理超时本身, <br>
这个版本效率最高,所以只修改了这个版本。 <br>
注意到前面处理超时中使用了alarm信号,假设你对Unix网络编程已经有 <br>
基础,那么是否注意到signal安装alarm信号句柄的时候没有考虑 <br>
SA_RESTART和SA_INTERRUPT两种情况。于是你认为那些为超时所设计的 <br>
代码无效正是因为这个原因,那好,我们把Stevens的函数换上来,如何呢, <br>
处理超时依旧没有成功。在isbase的Unix技术论坛上一个叫小许的朋友 <br>
提供的代码同样存在这个问题,这里一并指出。 <br>
呵,问题在于,系统调用会对SA_RESTART和SA_INTERRUPT的设置作出相应 <br>
动作而不重启本身(被alarm信号打断的),可那些库函数没有这么统一。 <br>
许多库函数实现对于EINTR错误返回值的处理动作就是重启相应的系统调用, <br>
所以,前面版本中的alarm设置是没有预期效果的,相反会进一步消耗时间。 <br>
因为有这种可能,比如connect了15秒,结果被alarm信号打断,然后重新 <br>
connect,直到connect本身的超时时限到了,于是多消耗了15秒。 <br>
作为一般编程爱好者来说,对于系统调用和库函数的区别可能不那么要紧。 <br>
对于进行网络程序开发的Unix程序员,必须仔细区分每一个术语背后的技术 <br>
细节,比如上面提到的信号中断后的重启。 <br>
本程序处理上面提到的几个问题,并且允许从命令行上指定超时时限,针对 <br>
不同的网络负载可以调整这个超时时限,既不要无谓消耗时间减缓扫描速度, <br>
也不要一味追求扫描速度而漏报重要信息。 <br>
3. 为了解决上面alarm信号无法设置超时时限的问题,我们使用sigsetjmp和 <br>
siglongjmp函数。在W.Richard.Stevens的APUE里介绍了这两个函数,在作 <br>
者的另外一部书UNP的26.6节给出了一个实际例子,我们这里的代码取自后者。 <br>
<br>
代码中没有注释,关于这两个函数,xh在solaris版预告自己要写篇介绍文章, <br>
<br>
却不见踪影,看不懂代码就去找他麻烦。此外,使用这两个函数需要 <br>
#include <setjmp.h>。 <br>
4. 修正本系列前面关于rpc.cmsd的一个说明,远程程序号依旧是100068,这个不 <br>
大可能会变,但版本号和底层支持现在增加了不少,注意最后的演示输出。 <br>
程序: <br>
/* <br>
File Name: rpcscan.c <br>
Author : unknown <br>
Test : Linux 2.2.5 <br>
Compile : gcc -pipe -O3 -o rpcscan rpcscan.c <br>
Usage : rpcscan <startIp> <endIp> <rpcServerNumber> <rpcServerVer> [ <tim <br>
eout <br>
> ] <br>
Date : 2000/02/23 <br>
*/ <br>
#include <sys/types.h> <br>
#include <unistd.h> <br>
#include <malloc.h> <br>
#include <stdio.h> <br>
#include <netdb.h> <br>
#include <stdlib.h> <br>
#include <signal.h> <br>
#include <rpc/rpc.h> <br>
#include <arpa/inet.h> <br>
#include <sys/socket.h> <br>
#include <netinet/in.h> <br>
#include <netinet/ip.h> <br>
#include <rpc/pmap_prot.h> <br>
#include <rpc/pmap_clnt.h> <br>
#include <setjmp.h> <br>
#define MAX_IP_LEN 15 /* xxx.xxx.xxx.xxx 长度15个字节 */ <br>
#define DEFAULTTIMEOUT 5 /* 缺省RPC超时时间 */ <br>
typedef void Sigfunc ( int ); /* for signal handlers */ <br>
struct ipoctet <br>
{ <br>
char a[4]; <br>
char b[4]; <br>
char c[4]; <br>
char c[4]; <br>
char d[4]; <br>
}; <br>
struct ipocteti <br>
{ <br>
int a; <br>
int b; <br>
int c; <br>
int d; <br>
}; <br>
static sigjmp_buf jmpbuf; <br>
static int canjump; <br>
unsigned int timeout = DEFAULTTIMEOUT; <br>
void sig_alarm ( int signo ) <br>
{ <br>
if ( canjump == 0 ) <br>
{ <br>
return; <br>
} <br>
/* <br>
siglongjmp使得程序流程远跳转 <br>
*/ <br>
siglongjmp( jmpbuf, 1 ); <br>
return; <br>
} /* end of doNothing */ <br>
Sigfunc * signal ( int signo, Sigfunc * func ) <br>
{ <br>
struct sigaction act, oact; <br>
act.sa_handler = func; <br>
sigemptyset( &act.sa_mask ); <br>
act.sa_flags = 0; <br>
if ( signo == SIGALRM ) <br>
{ <br>
#ifdef SA_INTERRUPT <br>
act.sa_flags |= SA_INTERRUPT; /* SunOS 4.x */ <br>
#endif <br>
} <br>
else <br>
{ <br>
#ifdef SA_RESTART <br>
act.sa_flags |= SA_RESTART; /* SVR4, 44BSD */ <br>
#endif <br>
} <br>
if ( sigaction( signo, &act, &oact ) < 0 ) <br>
{ <br>
{ <br>
return( SIG_ERR ); <br>
} <br>
return( oact.sa_handler ); <br>
} /* end of signal */ <br>
Sigfunc * Signal ( int signo, Sigfunc * func ) /* for our signal() function <br>
*/ <br>
{ <br>
Sigfunc * sigfunc; <br>
if ( ( sigfunc = signal( signo, func ) ) == SIG_ERR ) <br>
{ <br>
fprintf( stderr, "signal error" ); <br>
exit( -1 ); <br>
} <br>
return( sigfunc ); <br>
} /* end of Signal */ <br>
u_long resolveHost ( char * host ) <br>
{ <br>
struct hostent * he; <br>
unsigned long ip; <br>
if( ( he = gethostbyname( host ) ) == NULL ) <br>
{ <br>
ip = inet_addr( host ); /* 网络字节顺序 */ <br>
if ( ip == INADDR_NONE ) <br>
{ <br>
ip = 0; <br>
} <br>
} <br>
else <br>
{ <br>
bcopy( he->h_addr_list[0], &ip, sizeof( unsigned long ) ); <br>
} <br>
return( ip ); <br>
} /* end of resolveHost */ <br>
/* 可以考虑修改这个函数,使得区分TCP和UDP底层支持 */ <br>
unsigned short rpcServerQuery ( char * host, <br>
unsigned long rpcServerNumber, unsigned long rpcServerVer ) <br>
{ <br>
struct sockaddr_in server_addr; <br>
unsigned short rpcServerPort = 0; <br>
server_addr.sin_addr.s_addr = resolveHost( host ); /* 获得远程主机IP地址 <br>
*/ <br>
server_addr.sin_family = AF_INET; /* 只能是这个地址族 <br>
*/ <br>
server_addr.sin_port = htons( PMAPPORT ); /* 111端口 */ <br>
Signal( SIGALRM, sig_alarm ); <br>
if ( sigsetjmp( jmpbuf, 1 ) ) <br>
{ <br>
/* <br>
当sig_alarm中siglongjmp执行后流程会跳转到这里 <br>
*/ <br>
/* fprintf( stderr, "\ntimeout\n" ); */ // 这里注释掉,免得输出太多 <br>
<br>
return( 0 ); <br>
} <br>
canjump = 1; /* siglongjmp初始化完毕,现在可以使用 */ <br>
alarm( 0 ); <br>
if ( alarm( timeout ) != 0 ) <br>
{ <br>
/* fprintf( stderr, "alarm was already set\n" ); */ // 减少输出 <br>
return( 0 ); <br>
} <br>
/* pmap_getport是库函数,不是系统调用 */ <br>
if ( ( rpcServerPort = <br>
pmap_getport( &server_addr, rpcServerNumber, rpcServerVer, IPPROTO_ <br>
TCP <br>
) ) ) <br>
) ) ) <br>
{ <br>
return( rpcServerPort ); <br>
} <br>
if ( ( rpcServerPort = <br>
pmap_getport( &server_addr, rpcServerNumber, rpcServerVer, IPPROTO_ <br>
UDP <br>
) ) ) <br>
{ <br>
return( rpcServerPort ); <br>
} <br>
alarm( 0 ); <br>
Signal( SIGALRM, SIG_DFL ); <br>
return( 0 ); <br>
} /* end of rpcServerQuery */ <br>
int main ( int argc, char * argv[] ) <br>
{ <br>
int i, j, a, b, c, d; <br>
unsigned long progNumber = PMAPPROG; <br>
unsigned long progVer = PMAPVERS; <br>
unsigned short progPort = 0; <br>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -