The method used in this captcha is very is to break. In fact I can
solve
the captchas 6 times as fast as it takes to generate them (locally)
in
only 63 lines of code. I do this by generating a regexp for each
possible
character. As the characters don't get damaged by the noise (as they
get
in most image bases captchas) this works all of the time.
$ ruby benchmark.rb
user system total real
generate: 0.160000 0.020000 0.180000 ( 0.192005)
setup: 0.030000 0.000000 0.030000 ( 0.025381)
break: 0.010000 0.000000 0.010000 ( 0.010908)
generate 200: 12.100000 1.000000 13.100000 ( 13.125787)
break 200: 2.050000 0.100000 2.150000 ( 2.152749)
$ wc -l deflatulent.rb /usr/local/lib/ruby/gems/1.8/gems/
flatulent-0.0.3/lib/flatulent.rb
63 deflatulent.rb
604 /usr/local/lib/ruby/gems/1.8/gems/flatulent-0.0.3/lib/
flatulent.rb
667 total
$ cat benchmark.rb
require 'deflatulent'
require 'flatulent'
require 'benchmark'
defl = html = code = nil
pairs = Array.new(200)
GC.disable
Benchmark.bm(13) do |x|
x.report("generate:") { flat = Flatulent.new; html = flat.form; code
= flat.string }
x.report("setup:") { defl = Deflatulent.new }
x.report("break:") { raise unless defl.deflatulent(html) ==
code }
x.report("generate 200:") { 200.times{|index| flat = Flatulent.new;
pairs[index] = [flat.form,flat.string] } }
x.report("break 200:") { pairs.map{|(html,code)| raise unless
defl.deflatulent(html) == code } }
end
$ cat deflatulent.rb
require 'flatulent'
class Deflatulent
def initialize font="big"
font = Text::Figlet::Font.new(File.join(Flatulent.fontdir,font
+".flf"))
typesetter = Text::Figlet::Typesetter.new font
letters = ('A'..'Z').to_a + ('1'..'9').to_a
@lines_array = letters.map{|letter| [letter,
gen_figlet_lines_array(typesetter[letter])] }
end
def deflatulent string
if string =~ /<pre id='[a-zA-Z0-9_-]+_element' style='.*?'>(.*?)<\/
pre>/m
string = $1
[[/<\/?span>/,''],[" "," "],["<br>","\n"],["<","<"],
[">",">"],[""",'"'],["&","&"]].each do |args|
string.gsub!(*args)
end
end
width = string.index("\n")
string.tr!("\n","")
solution = []
@lines_array.each do |(letter,(length,lines))|
re = "(?="
lines.each{|line| re << line << ".{#{width-length}}" }
re << ")"
string.scan(Regexp.new(re, Regexp::MULTILINE)) do
solution[$~.begin(0) % width] = letter
end
end
solution.join
end
private
def gen_figlet_lines_array string
lines = string.split("\n")
lines.shift while lines.first.strip.empty?
lines.pop while lines.last.strip.empty?
lines.each{|e|e[0,1]=""} while lines.all?{|e|e[0,1]==' '}
lines.each{|e|e[-1,1]=""} while lines.all?{|e|e[-1,1]==' '}
[lines[0].length,lines.map{|e|e.split('').map{|q|(q == ' ' ? '.' :
Regexp.escape(q))}.join}]
end
end
if __FILE__ == $0
defl = Deflatulent.new(ARGV[0] || "big")
loop do
input = ""
while line=gets and not line.chomp.empty?
input << line
end
puts defl.deflatulent(input)
break unless line
end
end
--
Jannis Harder