rcs-backdoor 8.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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