WhatWeb是一款非常著名的指纹扫描器,在平时渗透测试用的也比较多。从这一篇开始,陆陆续续来分析它的源码,领会其中精髓。写这篇博客的当下,我对ruby并不熟悉,所以开篇就一边看着源码一遍熟悉ruby语法吧。
开始看whatweb这个文件的源码熟悉语法:
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__))) unless
$:.include?(File.dirname(__FILE__)) || $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__)))
$LOAD_PATH << "/usr/share/whatweb/"
$LOAD_PATH是Ruby读取外部文件的一个环境变量,返回值是一个路径的数组。Ruby会在这个环境变量的路径中读取需要的require文件,如果在环境变量中找不到自己想要的文件,就会报LoadError错误。$LOAD_PATH和$:指的都是同一个环境变量。$LOAD_PATH.unshift是把括号里面得到的路径加到现在已经存在所有环境变量之前。<<会把后续给定对象附加到数组的末尾。里面涉及的文件操作就不在这里描述了。
if RUBY_VERSION =~ /^1\.9/
require 'digest/md5'
require 'lib/extend-http_ruby1.9.rb'
看一下这个=~符号是将字符串与正则表达式进行匹配。如果匹配成功,返回匹配子串在字符串中的偏移,如果不成功返回nil。
gems = %w|json mongo rchardet |
gems.each do |thisgem|
begin
require 'rubygems' # rubygems is optional
if gem_available?(thisgem) #
require thisgem
else
end
rescue LoadError
# that failed.. no big deal
raise if $WWDEBUG==true
end
end
上面%w||这个语法是赋值一个数组,具体可以看这里。
PLUGIN_DIRS=[ "plugins", "my-plugins"].map {|x| $LOAD_PATH.map {|y| y+"/"+x if File.exists?(y+"/"+x) } }.flatten.compact
这里主要涉及到map函数,这个函数是针对每个元素进行变换并返回整个修改后的数组。后面的flatten是使数组一维化,并返回新的数组。compact是去掉数组中nil部分,返回新的一个数组。
def randstr
rand(36 ** 8).to_s(36)
end
这个产生随机字符串的方法比较有意思,可以看这里。rand(进制**length).to_s(进制)产生a-z和0-9组成的长度为length的随机字符串。36代表进制(a-z、0-9的数目加起来正好36)。但是有讨论说这个可能产出长度小于length的随机字符串。
while t = next_target
Thread.new(t) do |thistarget|
begin
target = Target.new(thistarget) # we set the target within the thread
rescue => err
error(err)
next
end
puts Thread.current.to_s + " started for " + target.to_s if $verbose>1
sleep $WAIT unless $WAIT.nil? # wait
# follow redirects
no_redirects =false
num_redirects = 0
while no_redirects == false do
no_redirects=true if target.is_file?
# if we redirect 10 times we give up
if num_redirects == $MAX_REDIRECTS
error("ERROR Too many redirects: #{target.to_s}")
no_redirects=true
next
end
begin
target.open
rescue => err
error("ERROR Opening target: #{target.to_s} - #{err}")
no_redirects = true # without this we can get stuck in a loop
raise if $WWDEBUG
next
end
if target.is_url? and target.status.nil?
# assume all HTTP sites return a status
no_redirects=true
next
end
results = run_plugins(target)
# reporting
# multiple output plugins simultaneously, some stdout, some files
output_list.each do |o|
begin
o.out(target, target.status, results)
rescue => err
#srsly, logging failed
error("ERROR Logging failed: #{target.to_s} - #{err}")
raise if $WWDEBUG==true
end
end
# REDIRECTION
unless no_redirects
begin
if newtarget = target.get_redirection_target
num_redirects+=1
target=Target.new(newtarget)
else
no_redirects=true
end
rescue => err
error("ERROR Redirection broken: #{target.to_s} - #{err}")
no_redirects=true
raise if $WWDEBUG==true
end
end
end # while no_redirects
end # Thread.new
while Thread.list.size>($MAX_THREADS+1)
puts "Thread list full, passing control" if $verbose>1
#sleep 0.5
Thread.pass
end
end # targets.each
上面的代码涉及到多线程的知识。下面是由do…end组成的ruby代码块。
Thread.new(t) do |thistarget|
省略的部分代码
end # Thread.new
|thistarget|接收一个参数,新创建的线程执行代码块里面的代码。下面有个示例说明这种语法。
require 'net/http'
pages = %w(www.iteye.com www.csdn.net www.sina.com.cn www.google.cn)
threads = []
for page in pages
threads << Thread.new(page) do |url|
h = Net::HTTP.new(url, 80)
puts "The URL is #{url} "
resp = h.get('/', nil)
puts "The #{url} response : #{resp.message}"
end
end
threads.each { |t|t.join }
运行结果:
好了,一边看代码一边学习语法,做点小笔记。后续会继续更新WhatWeb源码分析系列。