2个小时拿下Perl语言:自动监控服务器内网站访问ip次数并预警(附
发布时间:2020-12-15 23:54:33 所属栏目:大数据 来源:网络整理
导读:[作者] 网名: 猪头三 站点:? http://www.x86asm.com Email: pliceman_110@163.com QQ:? 643439947 编程生涯: 2001~至今[2014年] 职业生涯: 11年 开发语言: C/C++; x86asm; Object Pascal; Object-C; C#; 开发工具: VC++; Delphi; 研发领域: Windows应用软件
[作者]
网名: 猪头三
站点:?http://www.x86asm.com
Email: pliceman_110@163.com
QQ:?643439947
编程生涯: 2001~至今[2014年]
职业生涯: 11年
开发语言: C/C++; x86asm; Object Pascal; Object-C; C#;
开发工具: VC++; Delphi;
研发领域: Windows应用软件安全; Windows系统内核安全; Windows系统磁盘数据安全;
技能种类: 逆向 驱动 磁盘 文件
[序言]
一直都不是Linux服务器专业人员. 但恰恰自己又拥有2台国外服务器,上面有好多关于我国外软件的产品网站. 这2台服务器每天都遭受各种第3世界国家攻击以及主流国家恶意爬虫和wp_login.php攻击. 每次维护一次访问日记都需要1个多小时,很麻烦. 再一个Linux命令苦涩难懂,勉强会用几个,但还是不顶用. 就在前几天心烦了,干脆自己写脚本解析日记算了. 就这个念头一闪,就诞生了这个perl脚本出来.
[学习]
perl语言认真看2个小时,然后再mac os x系统装上eclips+perl插件就开工了(备注: 不会用vi,因为人笨),断断续续用了2天时间终于完成了第一个版本,大约700多行. 在开发过程中,其实很简单: 就是把C语言的逻辑思维转换成perl代码. 为什么要这样做,因为脚本语言通病就是语法糖过多,会让你迷失在语法糖里面而无法写出正常的程序,不是每个开发人员有拥有那种开发语言的天赋(备注:我个人认为只有这样的天赋才能接受各种奇怪的语法糖). 也就是一直坚持这个原则才让这个perl脚本诞生.
[差异]
perl和python都很好上手,只要你有C语言基础,玩这个很简单. 比JS简单多了. 那我为什么用perl呢?其实是我的服务器默认没有安装python,倒反默认安装了ruby,比较神奇. 本来想用ruby的,因为我第一个脚本语言就是ruby,但是后来想想linux服务器一般大头都是perl语言,因此选择了perl而不是ruby.
[源码]
#!usr/bin/perl
#作用:?监视服务器的网站目录下access-log文件夹里的日记记录
#?????并分析日记里面的ip访问次数,?如果达到预警次数则自动发送email
#?????给管理员
#作者:?猪头三?http://www.x86asm.com
#版本:?v0.0.1?测试版
#时间:?2014-07-15
#留言:?本人不是Linux系统专家,?因此写这个脚本是纯粹自己用用,
#?????如果你有更好的建议和方法请联系我.
#使用方式:?按照提示修好源码之后,?就传到linux服务器并通过crontab?-e加入计划任务定时执行即可
#解决其他路径下的模块包含问题
BEGIN
?{
??
push
?@
INC
,
"/Users/PigHeadThree/perl5/lib/perl5"
?;
}
#use?5.010;
use
?
strict
?;
use
?
autodie
;
use
?
Try
::
Tiny
;
use
?
Tie
::
File
;
use
?
Fcntl
?
'O_RDONLY'
?;
use
?
File
::
Spec
::
Functions
?
qw
(
rel2abs
)?;
use
?
File
::
Basename
?
qw
(
dirname
?
basename
)?;
use
?
File
::
Spec
;
use
?
File
::
Copy
?
qw
(
copy
);
use
?
MIME
::
Lite
?;
use
?
MIME
::
Base64
;
use
?
Authen
::
SASL
;?
#?重要:如果服务器没有该模块将会导致email无法发送
#=================测试代码==================
#=================全局变量==================
#目录特征,?防止错误删除(表示你执行脚本要放在此目录下面)(可修改)
my
?
$g_str_App_Flag
?
=
?
'pht_monitor'
?;
#匹配access-logs的每条记录是否包含google和bing机器人(可修改)
#google?bing的ip都是合法ip
my
?
$g_str_grep_Bot
?
'googlebot|bingbot'
?;
#ip访问次数预警(可修改)
my
?
$g_int_Limit_VisitIp
?
=
?
900
?;
#服务器名称(可修改)
my
?
$g_str_ServerName
?
"PHT?Server"
?;
#自动发送email(可修改)
my
?
$g_str_email_From
?
'xxxxxxx@163.com'
?;
my
?
$g_str_email_Pass
?
'xxxxxxx'
?????????;
my
?
$g_str_eamil_Smtp
?
'smtp.163.com'
????;
my
?
$g_str_eamil_To
???
'xxxxxxx@qq.com'
??;
#=================函数入口点==================
try
{
????
&
fun_RunningLog
(
"main()?start..."
)?;
&
main
()?;
"main()?end."
)?;
catch
"===========main()?running?error==========="
)?;?????
};
sub
?
main
()
????
#比如你的服务器存存在一个站点?www.x86asm.com
#那么该服务器内部会有一个专门的文件夹存放该站点的所有文件
#假设为:?x86asm
#那么该目录下会存放一个名为x86asm.com的日记文件,一般路径如下
#/home/x86asm/access-logs/x86asm.com
#我们可以通过分析这个日记文件来得到ip的访问次数
????
my
?
$str_Site_URL
?????????
"www.x86asm.com"
?;
my
?
$str_Site_Name
????????
"x86asm_"
?;
my
?
$str_Site_LogFilePath
?
"/home/x86asm/access-logs/x86asm.com"
?;
????
fun_CountTargetSite
(
$str_Site_URL
????????????????????????
$str_Site_Name
????????????????????????
$str_Site_LogFilePath
)?;
??????????????????????????????
}
#End?main()
????
????
????
???
#=================函数======================
#统计目标网站的ip访问次数
#param_1?:?目标网站域名
#param_2?:?目标网站简称
#param_3?:?目标网站access_log文件路径?
sub
?
fun_CountTargetSite
()
????
#本函数不支持0参数传递
if
?(@
_
?
!=
?
3
)
????{
????
????
return
?
0
?;
????}
????(
my
?
$str_param_Site_URL
?????
my
?
$str_param_Site_Name
my
?
$str_param_Site_LogFilePath
)?
=
?@
_
?;
my
?
%
hash_VisitIp_Count
?;
"[$str_param_Site_URL]?:?start..."
);
&
fun_GetVisitIp
(
$str_param_Site_LogFilePath
????
????
????
????
????
$g_int_Limit_VisitIp
????
????
????
????
????
&
fun_GetLocaltime
(
"/"
)
%
hash_VisitIp_Count
)?;
if
?(
&
fun_IsNeedUpdate
(
$str_param_Site_Name
.&
fun_GetLocaltime
(
"_"
)
.
".txt"
????
????
????
????
????
????
?
%
hash_VisitIp_Count
)?
eq
?
1
)
????????
????????
#保存最新统计
????????
&
fun_VisitIpSaveToCurrentPathFile
(
$str_param_Site_Name
????
????
????
????
????
????
????
????
????
????
%
hash_VisitIp_Count
);
????
????
#发送EMAIL
????
????
my
?
$str_SendEmail_Data
?
=
?
&
fun_GetSendEmailToData
(
$str_param_Site_Name
".txt"
)?;
if
?(
$str_SendEmail_Data
?
ne
?
0
)
????
????{
????
????????
&
fun_AutoSendEmailToAlarm
(
$g_str_ServerName
"[$str_param_Site_URL]"
"Alarm?Ip?Visit"
????
??????????????????????????????????
$str_SendEmail_Data
)?;
????
????}
????
????
else
????}????
"[$str_param_Site_URL]?:?end."
);
#?End?fun_CountTargetSite()?
#获取本地时间
#param_1?:?时间分隔符
sub
?
fun_GetLocaltime
()
#月份对照表
%
hash_montoname
?
=
?(
????
????
"01"
?
=>
?
"Jan"
"02"
?
"Feb"
"03"
?
"Mar"
"04"
?
"Apr"
"05"
?
"May"
"06"
?
"Jun"
"07"
?
"Jul"
"08"
?
"Aug"
"09"
?
"Sep"
"10"
?
"Oct"
"11"
?
"Nov"
"12"
?
"Dec"
)?;
my
?(
$sec
$min
$hour
$mday
$mon
$year_off
$wday
$yday
$isdat
)?
=
?
localtime
;
????
????(
$mday
?
$mon
?
$year_off
)?
sprintf
(
"%02d"
?
$mday
)
?
$mon
+1
)
????
????
$year_off
+1900
);
#月份转换为英文描述
????
$mon
?
=
?
$hash_montoname
{
$mon
}?;
????
return
?
$mday
.
$_
[
0
]
.
$mon
.
$year_off
;
????
????
????
#?End?fun_GetLocaltime()
#用于日记记录的时间格式
sub
?
fun_GetLocaltimeToLog
()
#本函数不支持参数传递
if
?(@
_
)
my
?@
array_montoname
?
=
?
qw
(
Jan
?
Feb
?
Mar
?
Apr
?
May
?
Jun
?
Jul
?
Aug
?
Sep
?
Oct
?
Nov
?
Dec
)?;
????
????(
$sec
?
$min
?
$hour
?
$mday
?
$sec
)
?
$min
)
?
$hour
)
=
?
$array_montoname
[
$mon
]?;
'/'
.
$year_off
'??'
.
$hour
':'
.
$min
.
$sec
?;??
#?End?fun_GetLocaltimeToLog()
#日记记录
#param_1?:?日记内容
sub
?
fun_RunningLog
()
#本函数不支持0个参数传递
my
?
$str_param_Print
)?
#获取时间
my
?
$str_LogTime
?
'['
.&
fun_GetLocaltimeToLog
()
']'
?;
#打开日记文件,?如果没有则创建
my
?
$str_Log_FileName
?
'run_'
'_'
)
'.txt'
?;
????
$str_Log_FileName
????
=
?
File
::
Spec
->
join
(
&
fun_GetCurrentAppPath
()
????????????????????????????????????????????
$str_Log_FileName
)?;
????????????????????????????????????????????
unless
(
-
e
?
$str_Log_FileName
)
unless
(
open
(
HANDLE_OPEN
?
">$str_Log_FileName"
))
????
????
????
">>$str_Log_FileName"
))
#开始写入
print
?
HANDLE_OPEN
?
$str_LogTime
'?:?'
.
$str_param_Print
"n"
?;
close
(
HANDLE_OPEN
)?;
1
?;
#End?fun_RunningLog()
#获取当前执行目录
sub
?
fun_GetCurrentAppPath
()
return
?
dirname
(
rel2abs
(
__FILE__
))?;
#?End?fun_GetCurrentAppPath()
#复制目标文件到当前目录
#param_1?:?需要复制到目标文件全路径
sub
?
fun_CopyFileToCurrentAppPath
()
my
?
$str_CopyFileToPath
?
""
?;
my
?
$str_CopyFileName
???
my
?
$str_CurrentAppPath
?
my
?
$str_param_CopyFilePath
?
=
?
$_
[
0
]?;
#判断文件是否存在?-e?表示?文件?-d?表示目录
-
e
?
$str_param_CopyFilePath
)
????
????
????
#开始复制(先执行旧文件删除)
????
????
????
$str_CurrentAppPath
?
&
fun_GetCurrentAppPath
()?;
????
????
????
$str_CopyFileName
???
=
?
basename
(
$str_param_CopyFilePath
)?;
????
????
????
$str_CopyFileToPath
?
join
(
$str_CurrentAppPath
?
$str_CopyFileName
)?;
????
????
????
-
e
?
$str_CopyFileToPath
)
????
????
????{
????
????
????
????
#删除旧文件
????
????
????
????
if
?(
$str_CopyFileToPath
?
=~
?
/$g_str_App_Flag/i
)
????
????
????
????{
"fun_CopyFileToCurrentAppPath()?deleteing?$str_CopyFileToPath..."
)?;
????
????
????
????
????
unlink
(
$str_CopyFileToPath
);
"fun_CopyFileToCurrentAppPath()?deleted?$str_CopyFileToPath."
)?;????
????
????
????
????}
????
????
????}
????
????
????
copy
(
$str_param_CopyFilePath
?
$str_CopyFileToPath
)?;
return
?
$str_CopyFileToPath
?;
#?End?fun_CopyFileToCurrentAppPath()
#正则表达式提取ip地址
sub
?
fun_GetIpByStr
()
my
?
$str_param_Target
)?
if
?(
$str_param_Target
?
/(d+.d+.d+.d+)/
)
return
?
"$1"
?;?
#IP地址
#?End?fun_GetIpByStr()
#统计目标ip访问次数
#param_1?:?目标访问日记文件路径
#param_2?:?IP访问次数报警
#param_3?:?目标时间时间到访问记录?(02/jul/2014)
#param_4?:?哈希表?(地址传递)
sub
?
fun_GetVisitIp
()
4
)
#获取参数
my
?
$str_param_VisitIp_LogFilePath
my
?
$int_param_VisitIp_Count
my
?
$time_param_Get
my
?
$hash_param_VisitIp
)?
?????
if
?(
$time_param_Get
?
eq
?
"NO"
)
????
????
$time_param_Get
?
"/"
)?;
#开始统计
-
e
?
$str_param_VisitIp_LogFilePath
)
my
?
$str_VisitIp_CopyToPath
?
&
fun_CopyFileToCurrentAppPath
(
$str_param_VisitIp_LogFilePath
)?;
if
?(
$str_VisitIp_CopyToPath
?
eq
?
#获取日记文件失败
#打开日记文件
my
?@
array_VisitIp_Load
?;
my
?
$int_VisitIp_FileLineNums
?;
-
e
?
$str_VisitIp_CopyToPath
)
tie
?@
array_VisitIp_Load
????
????
????????
'Tie::File'
????
????
????????
$str_VisitIp_CopyToPath
????
????
????????
mode
?
=>
?
O_RDONLY
?;
????
????
????????
#获取行数?
????
????
????
$int_VisitIp_FileLineNums
?
=
?@
array_VisitIp_Load
?
-
?
#逆向循环匹配
my
?
$str_Temp_Line
??????????
my
?
$str_Temp_VisitIp
???????
my
?
$str_Temp_VisitIp_Count
?
0
??;
%
hash_Temp_VisitIp
??????
=
?()?;
while
?(
$int_VisitIp_FileLineNums
?
>=
?
????
????
????
????
$str_Temp_Line
?
=
?
$array_VisitIp_Load
[
$int_VisitIp_FileLineNums
]?;
if
?(
$str_Temp_Line
?
/$time_param_Get/i
)
????
????
????
????
????
/$g_str_grep_Bot/i
)
????
????
????
????????{
????
????
????
????????????
????
????
????
????????}
????
????
????
????????
????
????
????
????????????
#捕获ip地址
????
????
????
????????????
$str_Temp_VisitIp
?
&
fun_GetIpByStr
(
$str_Temp_Line
)?;
????
????
????
????????????
if
?(
$str_Temp_VisitIp
?
eq
?
????
????
????
????????????{
????
????
????
????????????}
????
????
????
????????????????
#保存ip地址以及访问次数(临时)
????
????
????
????????????????
if
?(
exists
?
$hash_Temp_VisitIp
{
$str_Temp_VisitIp
})
????
????
????
????????????????{
????
????
????
????????????????????
$hash_Temp_VisitIp
{
$str_Temp_VisitIp
}?
+=
?
????
????
????
????????????????????
????
????
????
????????????????????
#判断是否大于报警次数,如果大于则保存
????
????
????
????????????????????
if
?(
$hash_Temp_VisitIp
{
$str_Temp_VisitIp
}?
>=
?
$int_param_VisitIp_Count
)
????
????
????
????????????????????{
????
????
????
????????????????????????
????
????
????
????????????????????????
exists
?
$$hash_param_VisitIp
{
$str_Temp_VisitIp
})
????
????
????
????
????
????????????????{
????
????
????
????
????
????????????????????
$$hash_param_VisitIp
{
$str_Temp_VisitIp
}?
1
?;??
????
????
????
????
????
????????????????}
????
????
????
????
????
????????????????
=
?
$int_param_VisitIp_Count
?;????????????????????
????
????
????
????????????????????}
????
????
????
????????????????}
????
????
????
????????????????
last
?;
????
????
????
????
????
????
????
????
$int_VisitIp_FileLineNums
?
-=
?
1
?;?
#关闭文件
untie
?@
array_VisitIp_Load
?;?
1
?;???
????
????
"fun_GetVisitIp()?$str_param_VisitIp_LogFilePath?no?found."
)?;
#?End?fun_GetVisitIp()
#把哈希表到内容保存到当前执行目录下
#param_1?:?文件名称
#param_2?:?哈希内容
sub
?
fun_VisitIpSaveToCurrentPathFile
()
2
)
my
?
$str_param_SaveToFileName
my
??
$hash_param_Conent
)?
#校验参数
if
?((
length
(
$str_param_SaveToFileName
)?
==
?
0
)?
or
???????((
keys
?
%
$hash_param_Conent
)?
0
))
#开始保存
my
?
$str_SaveToFilePath
?
?????????????????????????????
$str_param_SaveToFileName
)?;
-
e
?
$str_SaveToFilePath
)
if
?(
$str_SaveToFilePath
?
????
????
????
"fun_VisitIpSaveToCurrentPathFile()?deleteing?$str_SaveToFilePath..."
)?;
unlink
(
$str_SaveToFilePath
);
"fun_VisitIpSaveToCurrentPathFile()?deleted?$str_SaveToFilePath."
)?;
open
(
HANDLE_FILE
">$str_SaveToFilePath"
))
my
?
$hash_Keys
?;
my
?
$hash_Values
?;
foreach
?
$hash_Keys
?(
sort
?
%
$hash_param_Conent
)
????
????
????
$hash_Values
?
=
?
$$hash_param_Conent
{
$hash_Keys
}?;
print
?
HANDLE_FILE
?
"$hash_Keys?=>?$hash_Values?n"
?;
close
(
HANDLE_FILE
)?;
#?End?fun_VisitIpSaveToCurrentPathFile()
#判断是否需要更新日记文件(.txt)
sub
?
fun_IsNeedUpdate
()
my
?
$str_param_FileName
length
(
$str_param_FileName
)?
???????(
%
$hash_param_Conent
?
my
?
$bool_IsNeedUpdate
?
0
?;?
#更新标志位
my
?
$str_Ip
?
%
hash_Load_Ip
?;
my
?
$str_OpenFilePath
?
????????????????????????????????????????????
$str_param_FileName
)?;
#打开文件并匹配ip是否有更新
-
e
?
$str_OpenFilePath
)
open
?(
HANDLE_OPEN
?
$str_OpenFilePath
))
while
(
my
?
$str_Temp_Line
?
<
HANDLE_OPEN
>
)
????
????
????
????
$str_Ip
?
if
?(
$str_Ip
?
eq
?
????
????????????{
????
????????????}
????
????????????
????
????????????????
#?保存之前统计过的ip
????
????????????????
$hash_Load_Ip
{
$str_Ip
}?
1
?;????
close
(
HANDLE_OPEN
);
#开始循环匹配
my
?
$str_Temp_Value
?;
while
((
$str_Ip
?
$str_Temp_Value
)?
each
?
#判断ip是否是存在,?如果不存在则表示需要更新,?返回1
????????????????
exists
?
$hash_Load_Ip
{
$str_Ip
})
????????????????{
????????????????????
????????????????}
????????????????????
$bool_IsNeedUpdate
?
????????????????????
????????????????}???
????
????}???
#没有日记记录文件也表示需要更新
????
????
$bool_IsNeedUpdate
?
return
?
$bool_IsNeedUpdate
?;
#?End?fun_IsNeedUpdate()
#自动发送email进行报警
#param_1?:?标题
#param_2?:?内容
sub
?
fun_AutoSendEmailToAlarm
()
my
?
$str_param_Subject
my
?
$str_param_Data
)?
length
(
$str_param_Subject
)?
????????(
length
(
$str_param_Data
)?
==
??
0
))?
?????{
????????
0
?????}?
my
?
$msg_Send
?
=
?
MIME
::
Lite
->
new
(
From
????
=>
?
$g_str_email_From
????
????
????????????????????????????
To
??????
=>
?
$g_str_eamil_To
????
????
????????????????????????????
Subject
?
=>
?
$str_param_Subject
????
????
????????????????????????????
Data
????
=>
?
$str_param_Data
)?;
#$msg_Send->attr("content-type"?=>?"text/html")?;
????
$msg_Send
send
(
"smtp"
?
$g_str_eamil_Smtp
????????????????????
AuthUser
=>
$g_str_email_From
????????????????????
AuthPass
=>
$g_str_email_Pass
????????????????????
Timeout
?
=>
?
30
)?;?
#?Debug?=>?1?表示打印发送时的各种状态记录
?????
#?End?fun_AutoSendEmailToAlarm()
#从指定文件获取内容当作email内容进行发送
#param_1?:?目标文件名?(默认只在当前目录下获取)
sub
?
fun_GetSendEmailToData
()
my
?
$str_param_FileName
)?
#开始打开文件
my
?
$str_Content
?
????
????
????
????
chomp
(
$str_Temp_Line
)?;
????
????
????
????
$str_Content
?
.=
?
$str_Temp_Line
?
.=
?
"n"
??;
length
(
$str_Content
)?
return
?
$str_Content
?;???
#?End?fun_GetSendEmailToData()
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |