网安攻击与防御

本文介绍了如何使用AWK的mktime函数精确筛选服务器日志,特别是在处理包含复杂日期时间格式的情况下,通过strptime函数将日期时间字符串转换为epoch值进行比较。给出了实际的AWK脚本示例和相关的时间函数应用实例。
摘要由CSDN通过智能技术生成

awk筛选给定范围内的日志

为什么要筛选

在服务器被攻击时,我们作为安全人员需要找到对方留下的蛛丝马迹,显而易见查看日志是一个很好的途径。但是日志有着成千上万条,为了方便我们进行查看,所以要进行给定范围内的日志筛选

为什么选择AWK

grep/sed/awk用正则去筛选日志时,如果要精确到小时、分钟、秒,则非常难以实现。主要难点在于日期时间各个部分的进制是不一样的,比如小时是24进制/12进制,分钟是60进制,秒也是60进制(闰秒时为61进制)。

但是awk提供了mktime()函数,它可以将时间转换成epoch时间值。

 

借此,可以取得日志中的时间字符串部分,再将它们的年、月、日、时、分、秒都取出来,然后放入mktime()构建成对应的epoch值。因为epoch值是数值,所以可以比较大小,从而决定时间的大小。

AWK操作

下面strptime1()实现的是将2019-11-10T03:42:40+08:00格式的字符串转换成epoch值,然后和which_time比较大小即可筛选出精确到秒的日志。
建立过滤文件

BEGIN{
  # 要筛选什么时间的日志,将其时间构建成epoch值
  which_time = mktime("2019 11 10 03 42 40")
}

{
  # 取出日志中的日期时间字符串部分
  match($0,"^.*\\[(.*)\\].*",arr)

  # 将日期时间字符串转换为epoch值
  tmp_time = strptime1(arr[1])

  # 通过比较epoch值来比较时间大小
  if(tmp_time > which_time){print}
}

# 构建的时间字符串格式为:"2019-11-10T03:42:40+08:00"
function strptime1(str   ,arr,Y,M,D,H,m,S) {
  patsplit(str,arr,"[0-9]{1,4}")
  Y=arr[1]
  M=arr[2]
  D=arr[3]
  H=arr[4]
  m=arr[5]
  S=arr[6]
  return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
}

下面strptime2()实现的是将10/Nov/2019:23:53:44+08:00格式的字符串转换成epoch值,然后和which_time比较大小即可筛选出精确到秒的日志。

{
  match($0,"^.*\\[(.*)\\].*",arr)

  tmp_time = strptime2(arr[1])

  if(tmp_time > which_time){
    print 
  }
}

# 构建的时间字符串格式为:"10/Nov/2019:23:53:44+08:00"
function strptime2(str   ,dt_str,arr,Y,M,D,H,m,S) {
  dt_str = gensub("[/:+]"," ","g",str)
  # dt_sr = "10 Nov 2019 23 53 44 08 00"
  split(dt_str,arr," ")
  Y=arr[3]
  M=mon_map(arr[2])
  D=arr[1]
  H=arr[4]
  m=arr[5]
  S=arr[6]
  return mktime(sprintf("%s %s %s %s %s %s",Y,M,D,H,m,S))
}

function mon_map(str   ,mons){
  mons["Jan"]=1;  mons["Feb"]=2;  mons["Mar"]=3
  mons["Apr"]=4;  mons["May"]=5;  mons["Jun"]=6
  mons["Jul"]=7;  mons["Aug"]=8;  mons["Sep"]=9
  mons["Oct"]=10; mons["Nov"]=11; mons["Dec"]=12
  return mons[str]
}

# mon_map函数可以简化成如下等价的定义方式:
function mon_map(mon,    str) {
  str = "JanFebMarAprMayJunJulAugSepOctNovDec"
  return ( ( ( index(str, mon) - 1 ) / 3 ) + 1)
}

AWK过滤实例操作

准备日志文件

选择Apache

在windows D: /phpstuy_pro/Extensions/Apache2.4.39/logs access.log文件

 

 编辑awk过滤

 

 AWK查看文件

 过滤

 

 补充

awk 时间函数

在 awk 中,可以使用内置的函数 mktime 将时间转换为秒数,进而可以进行时间的比较。具体使用方法如下:

假设我们有一个文件 test.txt,内容如下:如果我们想找到在 2022 年 2 月 1 日之前的所有如果我们想找到在 2022 年 2 月 1 日之前的所有行,可以使用以下命令:

2022-01-01 12:00:00
2022-02-01 12:00:00
2022-03-01 12:00:00

如果我们想找到在2022年2月1日之前所有的行,可以使用一下命令:

awk -F'[-: ]' '{if (mktime($1" "$2" "$3" "$4" "$5" "$6) < mktime("2022 02 01 00 00 00")) print $0}' test.txt

其中:

-F'[-: ]'
表示使用 -、:、空格 作为分隔符,将每行按照日期和时间分割成多个字段。
$1" "$2" "$3" "$4" "$5" "$6 
表示将每行的前六个字段拼接成一个字符串,作为 mktime 函数的参数。
"2022 02 01 00 00 00"
表示将日期和时间转换为秒数。
if (mktime(...) < mktime("2022 02 01 00 00 00"))
表示比较时间,判断当前行的时间是否在 2022 年 2 月 1 日之前,如果是,则输出当前行。







以上命令的输出结果为:

2022-01-01 12:00:00

awk 时间类内置函数


awk 常用于处理日志,它支持简单的时间类操作。有下面 3 个内置的时间函数:

mktime("YYYY MM DD HH mm SS [DST]")
        构建一个时间,返回这个时间点的秒级 epoch,构建失败则返回 - 1
systime()
        返回当前系统时间点,返回的是秒级 epoch 值
strftime([format [, timestamp [, utc-flag] ] ])
        将时间按指定格式转换为字符串并返回转的结果字符串

注意,awk构建时间都是返回秒级的epoch值,表示从1970-01-01 00:00:00 开始到指定时间已经过的秒数

awk mktime()

mktime 在构建时间时,如果传递的 DD 给定的值超出了月份 MM 允许的天数,则自动延申到下个月。例如,指定”2022 2 29 12 30 59” 中 2 月只有 28 号,所以构建出来的时间是 2022-03-01 12:30:59。

此外,其它部分也不限定必须在范围内。例如,2022 2 23 12 32 65 的秒超出了 59,那么多出来的秒数将进位到分钟。

awk 'BEGIN{
    print mktime("2019 2 23 12 32 65") | "xargs -i date -d@{} +\"%F %T\""
}'
2019-02-23 12:33:05

如果某部位的数值为负数,则表示在此时间点基础上减几。例如:

# 2022-02-23 12:00:59基础上减1分钟
$ awk 'BEGIN{print mktime("2019 2 23 12 -1 59") | "xargs -i date -d@{} +\"%F %T\""}'  
2019-02-23 11:59:59

# 2022-02-23 00:32:59基础上减1小时
$ awk 'BEGIN{print mktime("2019 2 23 -1 32 59") | "xargs -i date -d@{} +\"%F %T\""}'  
2019-02-22 23:32:59


awk strftime()
strftime([format [, timestamp [, utc-flag] ] ])


将指定的时间戳 tiemstamp 按照给定格式 format 转换为字符串并返回这个字符串。

如果省略 timestamp,则对当前系统时间进行格式化。

如果省略 format,则采用 PROCINFO [“strftime”] 的格式,其默认格式为 %a %b %e %H:%M%:S %Z %Y。该格式对应于 Shell 命令 date 的默认输出结果。

$ awk 'BEGIN{print strftime()}'
Wed Oct 30 00:20:01 CST 2014

$ date
Wed Oct 30 00:20:04 CST 2014

$ awk 'BEGIN{print strftime(PROCINFO["strftime"], systime())}'
Wed Oct 30 00:24:00 CST 2014


支持的格式包括:

%a 星期几的缩写,如Mon、Sun Wed Fri
%A 星期几的英文全名,如Monday
%b 月份的英文缩写,如Oct、Sep
%B 月份的英文全名,如February、October
%C 2位数的世纪,例如1970对应的世纪是19
%y 2位数的年份(00–99),通过年份模以100取得,例如2019/100的余数位19
%Y 四位数年份(如2015)
%m 月份(01–12)
%j 年中天(001–366)
%d 月中天(01–31)
%e 空格填充的月中天
%H 24小时制的小时(00–23)
%I 12小时制的小时(01–12)
%p 12小时制时的AM/PM
%M 分钟数(00–59)
%S 秒数(00–60)
%u 数值的星期几(1–7),1表示星期一
%w 数值的星期几(0–6),0表示星期日
%W 年中第几周(00–53)
%z 时区偏移,格式为"+HHMM",如"+0800"
%Z 时区偏移的英文缩写,如CST

%k 24小时制的时间(0-23),1位数的小时使用空格填充
%l 12小时制的时间(1-12),1位数的小时使用空格填充
%s 秒级epoch

##### 特殊符号
%n 换行符
%t 制表符
%% 百分号%

##### 等价写法:
%x 等价于"%A %B %d %Y"
%F 等价于"%Y-%m-%d",用于表示ISO 8601日期格式
%T 等价于"%H:%M:%S"
%X 等价于"%T"
%r 12小时制的时间部分格式,等价于"%I:%M:%S %p"
%R 等价于"%H:%M"
%c 等价于"%A %B %d %T %Y",如Wed 30 Oct 2015 12:34:48 AM CST
%D 等价于"%m/%d/%y"
%h 等价于"%b"

例如

$ awk 'BEGIN{print strftime("%s", mktime("2077 11 12 10 23 32"))}'
3403909412

$ awk 'BEGIN{print strftime("%F %T %Z", mktime("2077 11 12 10 23 32"))}' 
2077-11-12 10:23:32 CST

$ awk 'BEGIN{print strftime("%F %T %z", mktime("2077 11 12 10 23 32"))}' 
2077-11-12 10:23:32 +0800


awk 将日期时间字符串转换为时间:strptime1 ()

例如:

2019-11-11T03:42:42+08:00

1. 将日期时间字符串中的年月日时分秒全都单独保存起来
2. 将年月日时分秒构建成 mktime () 的字符串格式”YYYY MM DD HH mm SS”
3. 使用 mktime () 可以构建出时间点

function strptime(str,    time_str,arr,Y,M,D,H,m,S){
    time_str = gensub(/[-T:+]/," ","g",str)
    split(time_str, arr, " ")
    Y = arr[1]
    M = arr[2]
    D = arr[3]
    H = arr[4]
    m = arr[5]
    S = arr[6]
    # mktime失败返回-1
    return mktime(sprintf("%d %d %d %d %d %d", Y,M,D,H,m,S))
}

BEGIN{
  str = "2019-11-11T03:42:42+08:00"
  print strptime(str)
}

awk 将日期时间字符串转换为时间:strptime2 ()

下面是更难一点的,月份使用的是英文或英文缩写,日期时间分隔符也比较特殊。

Sat 26. Jan 15:36:24 CET 2013
function strptime(str,     time_str,arr,Y,M,D,H,m,S){
    time_str = gensub(/[.:]+/, " ", "g", str)
    split(time_str, arr, " ")
    Y = arr[8]
    M = month_map(arr[3])
    D = arr[2]
    H = arr[4]
    m = arr[5]
    S = arr[6]
    return mktime(sprintf("%d %d %d %d %d %d", Y,M,D,H,m,S))
}

function month_map(str,   mon){
    # mon = substr(str,1,3)
    # return (((index("JanFebMarAprMayJunJelAugSepOctNovDec", mon)-1)/3)+1)
    mon["Jan"] = 1
    mon["Feb"] = 2
    mon["Mar"] = 3
    mon["Apr"] = 4
    mon["May"] = 5
    mon["Jun"] = 6
    mon["Jul"] = 7
    mon["Aug"] = 8
    mon["Sep"] = 9
    mon["Oct"] = 10
    mon["Nov"] = 11
    mon["Dec"] = 12
    return mon[str]
}

BEGIN{
    str = "Sat 26. Jan 15:36:24 CET 2013"
    print strptime(str)
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Rek'Sai

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值