rcs-backdoor 8.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: fcc4702d14c3b715b9804ba55c711ac85b1e4fe4
4
+ data.tar.gz: 80df4d159e0df208e1587b92d1aacfff9c84b3ed
5
+ SHA512:
6
+ metadata.gz: dcac7da44c6ddbd0722ce434818284ea40183338d9d0c782a6eb974b4d10e5d52764052dd3a6b5cb1c9964a52b467ba624fed69228ceaf07a0ff30cd9f1be1be
7
+ data.tar.gz: 879003d4e72fd9a487bd4dfef841842b9a47e74ca7a5b9a4faa350c32a86bef99085a500ba41021b9dcdad218f799a6a8aefa10de01e9bccc0fcbb34bf785f3e
@@ -0,0 +1,56 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
18
+ #
19
+ # * Create a file at ~/.gitignore
20
+ # * Include files you want ignored
21
+ # * Run: git config --global core.excludesfile ~/.gitignore
22
+ #
23
+ # After doing this, these files will be ignored in all your git projects,
24
+ # saving you from having to 'pollute' every project you touch with them
25
+ #
26
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
27
+ #
28
+ # For MacOS:
29
+ #
30
+ .DS_Store
31
+ #
32
+ # For TextMate
33
+ #*.tmproj
34
+ #tmtags
35
+ #
36
+ # For emacs:
37
+ #*~
38
+ #\#*
39
+ #.\#*
40
+ #
41
+ # For vim:
42
+ *.swp
43
+
44
+ # For RubyMine
45
+ .idea
46
+
47
+ Gemfile.lock
48
+
49
+ #evidences
50
+ evidence
51
+
52
+ # RVM gemset
53
+ .ruby-gemset
54
+
55
+ *_config.dec
56
+ *_config.enc
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem "rcs-common", :path => "../rcs-common"
4
+
5
+ # Specify your gem's dependencies in rcs-backdoor.gemspec
6
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 ALoR
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,21 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ def execute(message)
4
+ print message + '...'
5
+ STDOUT.flush
6
+ if block_given? then
7
+ yield
8
+ end
9
+ puts ' ok'
10
+ end
11
+
12
+ desc "Housekeeping for the project"
13
+ task :clean do
14
+ execute "Cleaning the evidence directory" do
15
+ Dir['./evidences/*'].each do |f|
16
+ File.delete(f)
17
+ end
18
+ end
19
+ end
20
+
21
+ Rake::Task["release"].clear
@@ -0,0 +1,16 @@
1
+ ---
2
+ default:
3
+ BACKDOOR_ID: ALoR0000000001
4
+ BACKDOOR_TYPE: OSX
5
+ CONF_KEY: -HcIbnSmrnaXFk6peeZJMx8HFcJPg9Hx
6
+ EVIDENCE_KEY: Ez2vdMwu6VNQxsSj2OmUhbOzoPwsgJTP
7
+ SIGNATURE: 4yeN5zu0+il3Jtcb5a1sBcAdjYFcsD9z
8
+ VERSION: 2013090401
9
+
10
+ minotauro:
11
+ BACKDOOR_ID: RCS_0000041952
12
+ BACKDOOR_TYPE: OSX
13
+ CONF_KEY: vzDkDLNyjGhs8_DHEZgz0i9RWK7dlHrw
14
+ EVIDENCE_KEY: 7bzcX8cccAXnpm27DxN84_NnNDHWX688
15
+ SIGNATURE: 7faa628b44f45e6e1cf1289b87eed01d
16
+ VERSION: 2013103101
@@ -0,0 +1,2 @@
1
+ ---
2
+ SYNC_HOST: 192.168.1.169
@@ -0,0 +1,12 @@
1
+ ---
2
+ default:
3
+ INSTANCE_ID: 20110602007B6A910E7ECC2E987060DB2FF06CD8
4
+ USERID: ALoR
5
+ DEVICEID: 
6
+ SOURCEID:
7
+
8
+ minotauro:
9
+ INSTANCE_ID: 20110602007B6A910E7ECC2E987060DB2FF06CD8
10
+ USERID: ALoR
11
+ DEVICEID: 
12
+ SOURCEID:
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+
4
+ require 'bundler/setup'
5
+
6
+ #begin
7
+ # Bundler.setup(:default, :development)
8
+ #rescue Bundler::BundlerError => e
9
+ # $stderr.puts e.message
10
+ # $stderr.puts "Run `bundle install` to install missing gems"
11
+ # exit e.status_code
12
+ #end
13
+
14
+ require 'rcs-backdoor'
15
+
16
+ exit RCS::Backdoor::Application.run!(*ARGV)
@@ -0,0 +1,115 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ require 'yaml'
5
+ require 'pry'
6
+ require 'json'
7
+ require 'moped'
8
+ require 'digest/sha1'
9
+ require 'optparse'
10
+
11
+ ARGV << "--help" if ARGV.empty?
12
+
13
+ $options = {db_addr: 'localhost'}
14
+
15
+ OptionParser.new do |parser|
16
+ parser.banner = "Add configuration entry for a new backdoor"
17
+
18
+ parser.on("-d", "--db ADDR", "Default is localhost. Specify db addr") do |addr|
19
+ $options[:db_addr] = addr
20
+ end
21
+
22
+ parser.on("-n", "--name FACTORY_NAME", "Specify the factory name") do |name|
23
+ $options[:factory_name] = name
24
+ end
25
+ end.parse!
26
+
27
+ def db_address
28
+ "#{db_host}:27017"
29
+ end
30
+
31
+ def db_host
32
+ $options[:db_addr]
33
+ end
34
+
35
+ def session
36
+ @session ||= begin
37
+ session = Moped::Session.new([db_address])
38
+ session.use('rcs')
39
+ session
40
+ end
41
+ end
42
+
43
+ def can_reach_db?
44
+ session.collections.count rescue nil
45
+ end
46
+
47
+ def factory_name
48
+ $options[:factory_name]
49
+ end
50
+
51
+ def factory
52
+ @factory ||= session['items'].find({_kind: 'factory', name: factory_name}).first
53
+ end
54
+
55
+ def signature
56
+ doc = session['signatures'].find(scope: 'agent').first
57
+ doc['value'].to_s if doc
58
+ end
59
+
60
+ def append_configuration(filename, hash)
61
+ path = File.expand_path("../#{filename}", __FILE__)
62
+ raise("Unable to find file #{path}") unless File.exists?(path)
63
+ raise("#{filename} already has an entry named #{entry_name.inspect}") if File.read(path) =~ /#{entry_name}/i
64
+
65
+ File.open(path, 'a') do |f|
66
+ f.write("\n")
67
+ f.write("#{entry_name}:\n")
68
+ hash.each { |key, value| f.write(" #{key}: #{value}\n") }
69
+ end
70
+ end
71
+
72
+ def entry_name
73
+ "fac_" << factory_name.to_s.downcase.gsub(/[^a-z0-9]/, '').strip
74
+ end
75
+
76
+ def update_binary_yaml
77
+ hash = {
78
+ :BACKDOOR_ID => factory['ident'],
79
+ :BACKDOOR_TYPE => 'OSX',
80
+ :CONF_KEY => factory['confkey'],
81
+ :EVIDENCE_KEY => factory['logkey'],
82
+ :SIGNATURE => signature,
83
+ :VERSION => 2013103101
84
+ }
85
+
86
+ append_configuration('binary.yaml', hash)
87
+ end
88
+
89
+ def instance_id
90
+ @instance_id ||= Digest::SHA1.hexdigest(rand(1E20).to_s)
91
+ end
92
+
93
+ def update_ident_yaml
94
+ hash = {
95
+ :INSTANCE_ID => instance_id,
96
+ :USERID => "topac#{rand(1E5)}",
97
+ :DEVICEID => '',
98
+ :SOURCEID => '',
99
+ }
100
+
101
+ append_configuration("ident.yaml", hash)
102
+ end
103
+
104
+ raise "Unable to connect to database #{db_address.inspect}" unless can_reach_db?
105
+ raise "Unable to find factory name #{factory_name.inspect}" unless factory
106
+ raise "Unable to find agent signature in db #{db_address.inspect}" unless signature
107
+
108
+ update_binary_yaml
109
+ update_ident_yaml
110
+
111
+ puts "Configuration entry added to binary.yaml and ident.yaml"
112
+ cmd = "./bin/rcs-backdoor -s #{db_host} -c #{entry_name}"
113
+ puts "Try to sync with the command #{cmd} (the command has been copied on clipboard)"
114
+
115
+ exec("echo '#{cmd}' | pbcopy")
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+ BINPATH = File.join(File.dirname(__FILE__), 'rcs-backdoor')
3
+
4
+ count = ARGV[0].to_i
5
+
6
+ exit(1) if count <= 0
7
+
8
+ args_for_backdoor = ARGV[1..-1].join(' ')
9
+
10
+ require 'colorize'
11
+
12
+ $threads = []
13
+
14
+ count.times do |i|
15
+ $threads << Thread.new do
16
+ puts "Thread #{i}: sleeping".colorize(:red)
17
+ sleep(rand(0..30))
18
+
19
+ cmd = "#{BINPATH} #{args_for_backdoor} --tag TAG#{i}"
20
+ puts "Thread #{i}: #{cmd}".colorize(:yellow)
21
+ system(cmd)
22
+ end
23
+ end
24
+
25
+ $threads.map &:join
@@ -0,0 +1,32 @@
1
+ #
2
+ # Available name trace:
3
+ # :cookie - the cookie of the current session
4
+ # :host - the server for the sync
5
+
6
+ log4r_config:
7
+ pre_config:
8
+ global:
9
+ level: DEBUG
10
+ root :
11
+ level: DEBUG
12
+
13
+ loggers:
14
+ - name : rcslogger
15
+ level : DEBUG
16
+ additive : 'false'
17
+ trace : 'false'
18
+ outputters:
19
+ - stderr
20
+
21
+
22
+ outputters:
23
+ - type : StderrOutputter
24
+ name : stderr
25
+ level : DEBUG
26
+ formatter:
27
+ # take a look at this: http://log4r.rubyforge.org/rdoc/Log4r/PatternFormatter.html
28
+ date_pattern: '%Y-%m-%d %H:%M:%S'
29
+ pattern : '%d [%l]: %x %m'
30
+ type : PatternFormatter
31
+
32
+
@@ -0,0 +1,2 @@
1
+ require 'rcs-backdoor/backdoor.rb'
2
+ require "rcs-backdoor/version"
@@ -0,0 +1,326 @@
1
+ #
2
+ # The main file of the backdoor
3
+ #
4
+
5
+ # relatives
6
+ require_relative 'sync.rb'
7
+ require_relative 'protocol.rb'
8
+
9
+ # from RCS::Common
10
+ require 'rcs-common/trace'
11
+ require 'rcs-common/crypt'
12
+ require 'rcs-common/evidence'
13
+
14
+ # from System
15
+ require 'digest/md5'
16
+ require 'digest/sha1'
17
+ require 'ostruct'
18
+ require 'yaml'
19
+ require 'optparse'
20
+ require 'fileutils'
21
+
22
+ module RCS
23
+ module Backdoor
24
+ #
25
+ # This is the Backdoor object
26
+ # it needs the backdoor_id, instance and conf_key to be created
27
+ # all those parameters need to be passed as string taken from the db
28
+ #
29
+
30
+ class Backdoor
31
+ include Tracer
32
+
33
+ attr_reader :id
34
+ attr_reader :instance
35
+ attr_reader :type
36
+ attr_reader :conf_key
37
+ attr_reader :evidence_key
38
+ attr_reader :signature
39
+ attr_reader :version
40
+
41
+ attr_reader :userid
42
+ attr_reader :deviceid
43
+ attr_reader :sourceid
44
+
45
+ attr_reader :evidences
46
+
47
+ attr_accessor :scout
48
+ attr_accessor :soldier
49
+
50
+ #setup all the backdoor parameters
51
+ def initialize(binary_file, ident_file, options = {})
52
+ @options = options
53
+
54
+ # parse the parameters from the binary patched constants
55
+ trace :debug, "Parsing binary data..."
56
+ binary = load_yaml(binary_file)
57
+
58
+ # instantiate le empty log queue
59
+ @evidences = []
60
+
61
+ # plain string 'RCS_000000000x'
62
+ @id = binary['BACKDOOR_ID']
63
+
64
+ # the subtype of the backdoor (eg: WIN32, BLACKBERRY...)
65
+ @type = binary['BACKDOOR_TYPE']
66
+
67
+ # the conf key is passed as a string taken from the db
68
+ # we need to calculate the MD5 and use it in binary form
69
+ @conf_key = Digest::MD5.digest binary['CONF_KEY']
70
+
71
+ # the log key is passed as a string taken from the db
72
+ # we need to calculate the MD5 and use it in binary form
73
+ @evidence_key = Digest::MD5.digest binary['EVIDENCE_KEY']
74
+
75
+ # the backdoor signature is passed as a string taken from the db
76
+ # we need to calculate the MD5 and use it in binary form
77
+ @signature = Digest::MD5.digest binary['SIGNATURE']
78
+
79
+ # the backdoor version
80
+ @version = binary['VERSION']
81
+
82
+ ident = load_yaml(ident_file)
83
+ ident['INSTANCE_ID'] = ident['INSTANCE_ID'][0..-((@options[:tag].size)+2)]+"_"+@options[:tag] if @options[:tag]
84
+ # the instance is passed as a string taken from the db
85
+ # we need to convert to binary
86
+ @instance = [ident['INSTANCE_ID']].pack('H*')
87
+
88
+ # directory where evidence files are be stored
89
+ @evidence_dir = File.join(Dir.pwd, 'evidence', ident['INSTANCE_ID'])
90
+
91
+ @userid = ident['USERID'] || ''
92
+ @deviceid = ident['DEVICEID'] || ''
93
+ @sourceid = ident['SOURCEID'] || ''
94
+
95
+ @info = { :device_id => @deviceid, :user_id => @userid, :source_id => @sourceid }
96
+
97
+ trace :debug, "Backdoor instantiated: " << @id << @instance.unpack('H*').to_s
98
+ trace :debug, "Backdoor ident: [#{@userid}] [#{@deviceid}] [#{@sourceid}]"
99
+
100
+ @scout = false
101
+
102
+ begin
103
+ # instantiate the sync object with the protocol to be used
104
+ # and a reference to the backdoor
105
+ @sync = Sync.new(:REST, self)
106
+ rescue Exception => detail
107
+ trace :fatal, "ERROR: " << detail.to_s
108
+ raise
109
+ end
110
+ end
111
+
112
+ def load_yaml(path)
113
+ File.open(path, "r") do |f|
114
+ hash = YAML.load(f.read)
115
+ is_single_config = hash.keys.include?("INSTANCE_ID")
116
+ return hash if is_single_config
117
+ config_name = @options[:config_name] || "default"
118
+ hash[config_name] || raise("Unable to find configuration #{config_name.inspect} in file #{File.basename(path)}")
119
+ return hash[config_name]
120
+ end
121
+ rescue Exception => ex
122
+ trace :fatal, "Cannot load yaml file #{File.basename(path)}: #{ex.message}"
123
+ exit(1)
124
+ end
125
+
126
+ # perform the synchronization with the server
127
+ def sync(host, delete_evidence = true)
128
+ trace :debug, "Loading evidences in memory ..."
129
+ # retrieve the evidence from the local dir
130
+ Dir["#{@evidence_dir}/*"].each do |f|
131
+ @evidences << Evidence.new(@evidence_key).load_from_file(f)
132
+ end
133
+
134
+ trace :debug, "Synchronizing ..."
135
+ # perform the sync
136
+ @sync.perform host
137
+
138
+ if delete_evidence
139
+ trace :debug, "Deleting evidences ..."
140
+ # delete all evidence sent
141
+ Dir["#{@evidence_dir}/*"].each do |f|
142
+ File.delete(f)
143
+ end
144
+ end
145
+
146
+ end
147
+
148
+ # create some evidences
149
+ def create_evidences(num, type = :RANDOM)
150
+ # ensure the directory is created
151
+
152
+ FileUtils.rm_rf(@evidence_dir)
153
+
154
+ FileUtils.mkpath(@evidence_dir) if not File.directory?(@evidence_dir)
155
+
156
+ real_type = type
157
+
158
+ # generate the evidence
159
+ num.times do
160
+ #real_type = RCS::EVIDENCE_TYPES.values.sample if type == :RANDOM
161
+ real_type = [:APPLICATION, :DEVICE, :CHAT, :CLIPBOARD, :CAMERA, :INFO, :KEYLOG, :SCREENSHOT, :MOUSE, :FILEOPEN, :FILECAP].sample if type == :RANDOM
162
+ Evidence.new(@evidence_key).generate(real_type, @info).dump_to_file(@evidence_dir)
163
+ end
164
+ end
165
+ end
166
+
167
+ # this module is used only form bin/rcs-backdoor as a wrapper to
168
+ # execute the backdoor from command line
169
+ class Application
170
+ include RCS::Tracer
171
+
172
+ def run_backdoor(path_to_binary, path_to_ident, options)
173
+ b = RCS::Backdoor::Backdoor.new(path_to_binary, path_to_ident, options)
174
+
175
+ # set the scout flag if specified
176
+ b.scout = true if options[:scout]
177
+ b.soldier = true if options[:soldier]
178
+
179
+ if options[:generate] then
180
+ trace :info, "Creating #{options[:gen_num]} fake evidences..."
181
+ b.create_evidences(options[:gen_num], options[:gen_type])
182
+ end
183
+
184
+ while true
185
+ if options[:sync] then
186
+ b.sync options[:sync_host], !(options[:randomize] or options[:loop]) # delete evidences if not randomizing
187
+ end
188
+
189
+ break unless options[:loop]
190
+
191
+ options[:loop_delay].times do |n|
192
+ sleep 1
193
+ trace :info, "#{options[:loop_delay] - n} seconds to next synchronization." if n % 5 == 0
194
+ end
195
+ end
196
+ rescue Interrupt
197
+ trace :info, "User asked to exit. Bye bye!"
198
+ return 0
199
+ rescue Exception => e
200
+ trace :fatal, "FAILURE: " << e.to_s
201
+ trace :fatal, "EXCEPTION: " + e.backtrace.join("\n")
202
+ return 1
203
+ end
204
+
205
+ def run(options)
206
+
207
+ # if we can't find the trace config file, default to the system one
208
+ if File.exist?('trace.yaml') then
209
+ load_path = Dir.pwd
210
+ trace_yaml = 'trace.yaml'
211
+ else
212
+ load_path = File.dirname(File.dirname(File.dirname(__FILE__))) + "/bin"
213
+ trace_yaml = load_path + "/trace.yaml"
214
+ puts "Cannot find 'trace.yaml' using the default one (#{trace_yaml})"
215
+ end
216
+
217
+ # initialize the tracing facility
218
+ begin
219
+ trace_init load_path, trace_yaml
220
+ rescue Exception => e
221
+ puts e
222
+ exit
223
+ end
224
+
225
+ unless options[:randomize].nil?
226
+ binary = begin
227
+ File.open(load_path + '/ident.yaml', "r") do |f|
228
+ binary = YAML.load(f.read)
229
+ end
230
+ rescue
231
+ trace :fatal, "Cannot open binary patched file"
232
+ exit
233
+ end
234
+
235
+ threads = []
236
+ options[:randomize].times do |n|
237
+ binary["INSTANCE_ID"] = Digest::SHA1.hexdigest(n.to_s).upcase
238
+ path_to_yaml = load_path + "/ident#{n}.yaml"
239
+ File.open(path_to_yaml, "w") {|f| f.write(binary.to_yaml)}
240
+ trace :info, "Spawned backdoor #{path_to_yaml}"
241
+ threads << Thread.new { run_backdoor(load_path + '/binary.yaml', path_to_yaml, options)}
242
+ end
243
+
244
+ begin
245
+ threads.each do |th|
246
+ th.join
247
+ end
248
+ rescue Interrupt
249
+ trace :info, "User asked to exit. Bye bye!"
250
+ return 0
251
+ end
252
+
253
+ else
254
+ run_backdoor(load_path + '/binary.yaml', load_path + '/ident.yaml', options)
255
+ end
256
+
257
+ # concluded successfully
258
+ return 0
259
+ end
260
+
261
+ # since we cannot use trace from a class method
262
+ # we instantiate here an object and run it
263
+ def self.run!(*argv)
264
+ # This hash will hold all of the options parsed from the command-line by OptionParser.
265
+ options = {}
266
+
267
+ srand(Time.now.to_i)
268
+
269
+ types = [:RANDOM] + RCS::EVIDENCE_TYPES.values
270
+
271
+ optparse = OptionParser.new do |opts|
272
+ # Set a banner, displayed at the top of the help screen.
273
+ opts.banner = "Usage: rcs-backdoor [options] arg1 arg2 ..."
274
+
275
+ # Define the options, and what they do
276
+ opts.on( '-r', '--randomize NUM', Integer, 'Randomize NUM instances') do |num|
277
+ options[:randomize] = num
278
+ end
279
+ opts.on( '-g', '--generate NUM', Integer, 'Generate NUM evidences' ) do |num|
280
+ options[:generate] = true
281
+ options[:gen_num] = num
282
+ end
283
+ opts.on( '-t', '--type TYPE', types, 'Generate evidences of type TYPE' ) do |type|
284
+ options[:gen_type] = type
285
+ end
286
+ opts.on( '-s', '--sync HOST', 'Synchronize with remote HOST' ) do |host|
287
+ options[:sync] = true
288
+ options[:sync_host] = host
289
+ end
290
+ opts.on('--tag STRING', 'Append to instance string' ) do |tag|
291
+ options[:tag] = tag
292
+ end
293
+ opts.on( '-l', '--loop DELAY', Integer, 'Loop synchronization every DELAY seconds') do |seconds|
294
+ options[:loop] = true
295
+ options[:loop_delay] = seconds
296
+ end
297
+ opts.on( '-c', '--config CONFIGURATION', String, 'Configuration/environment name') do |value|
298
+ options[:config_name] = value
299
+ end
300
+ opts.on( '--scout', 'Auth like a scout' ) do
301
+ options[:scout] = true
302
+ end
303
+ opts.on( '--soldier', 'Auth like a soldier' ) do
304
+ options[:soldiers] = true
305
+ end
306
+
307
+ # This displays the help screen, all programs are assumed to have this option.
308
+ opts.on( '-h', '--help', 'Display this screen' ) do
309
+ puts opts
310
+ exit
311
+ end
312
+ end
313
+
314
+ optparse.parse(argv)
315
+
316
+ return Application.new.run(options)
317
+ end
318
+
319
+ end # Application::
320
+
321
+ end # Backdoor::
322
+ end # RCS::
323
+
324
+ if __FILE__ == $0
325
+ RCS::Backdoor::Application.run!(*ARGV)
326
+ end