This file is indexed.

/usr/bin/splitdns323fw is in dns323-firmware-tools 0.5-1.

This file is owned by root:root, with mode 0o755.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
#!/usr/bin/ruby

# Create and dismantle firmware files suitable for upload to a range of
# D-Link (and compatible) NAS devices.
#
# Copyright (C) 2008,2012 Matt Palmer <mpalmer@hezmatt.org>
#
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
#
# This tool is based on information provided by Leschinsky Oleg.  Many
# thanks go to him for deciphering (and publishing) his analysis of the file
# formats involved.
#
# This file should not be called as dns323fw-tool; create links to this file
# named 'mkdns323fw' and 'splitdns323fw'.

require 'optparse'

# This class handles all the grunt work for decoding and encoding firmware
# files.
class DnsFirmware
	# This class can be initialized two ways:
	#
	# - with a single string argument, which is the filename of an existing
	#   firmware file to be dissected; or
	#
	# - with a hash containing seven (symbol) keys:
	#   * :kernel_file
	#   * :initrd_file
	#   * :defaults_file (optional; can be empty)
	#   * :product_id
	#   * :custom_id
	#   * :model_id
	#   * :compat_id (optional; defaults to 255)
	#   * :signature
	#
	def initialize(opts)
		if opts.is_a? String
			@firmware_file = opts
			parse_header
		elsif opts.is_a? Hash
			@kernel       = opts[:kernel_file]   or raise ArgumentError.new("No :kernel_file provided")
			@initrd       = opts[:initrd_file]   or raise ArgumentError.new("No :initrd_file provided")
			@defaults     = opts[:defaults_file]
			@product_id   = opts[:product_id]    or raise ArgumentError.new("No :product_id provided")
			@custom_id    = opts[:custom_id]     or raise ArgumentError.new("No :custom_id provided")
			@model_id     = opts[:model_id]      or raise ArgumentError.new("No :model_id provided")
			@compat_id    = opts[:compat_id].nil? ? 255 : opts[:compat_id]
			@subcompat_id = opts[:subcompat_id].nil? ? 255 : opts[:subcompat_id]
			@signature    = opts[:signature]     or raise ArgumentError.new("No :signature provided")
		else
			raise ArgumentError.new("Incorrect type passed to DnsFirmware#initialize.  String or Hash expected, got #{opts.class}")
		end
	end
	
	attr_reader :product_id, :custom_id, :model_id, :compat_id, :subcompat_id,
	            :k_size, :i_size, :d_size
	
	# Return the signature of this firmware file.
	def signature
		if @signature =~ /^\x55\xAA(.{7})\0\x55\xAA$/
			return $1
		else
			raise RuntimeError.new("Unparseable signature string: #{@signature.inspect}")
		end
	end

	def write_kernel_file(destfile)
		write_data_file(destfile, @k_size, @k_offset)
	end
	
	def write_initrd_file(destfile)
		write_data_file(destfile, @i_size, @i_offset)
	end
	
	def write_defaults_file(destfile)
		write_data_file(destfile, @d_size, @d_offset)
	end
	
	def verify_kernel_checksum
		verify_checksum(@k_size, @k_offset, @k_sum)
	end
	
	def verify_initrd_checksum
		verify_checksum(@i_size, @i_offset, @i_sum)
	end
	
	def verify_defaults_checksum
		verify_checksum(@d_size, @d_offset, @d_sum)
	end
	
	# This method works from the kernel/initrd/defaults/etc data and writes out
	# a complete firmware file to the destfile of your choosing.
	def write_firmware_file(destfile)
		k_size = File.stat(@kernel).size
		i_size = File.stat(@initrd).size
		d_size = File.stat(@defaults).size rescue 0
		
		header = [
			64,
			k_size,
			64 + k_size,
			i_size,
			64 + k_size + i_size,
			d_size,
			checksum(File.read(@kernel)),
			checksum(File.read(@initrd)),
			d_size == 0 ? 0 : checksum(File.read(@defaults)),
			"\x55\xAA#{@signature}\0\x55\xAA",
			@product_id,
			@custom_id,
			@model_id,
			@compat_id,
			@subcompat_id,
			"\x00" * 7,
			0
		].pack("V9a12c5a7V")
		
		File.write(destfile, header)
		File.write(destfile, File.read(@kernel), 64)
		File.write(destfile, File.read(@initrd), 64+k_size)
		File.write(destfile, File.read(@defaults), 64+k_size+i_size) unless d_size == 0
	end

	private
	def parse_header
		return if @header_parsed
		
		must_have_firmware_file
		
		@k_offset,
		@k_size,
		@i_offset,
		@i_size,
		@d_offset,
		@d_size,
		@k_sum,
		@i_sum,
		@d_sum,
		@signature,
		@product_id,
		@custom_id,
		@model_id,
		@compat_id,
		@subcompat_id,
		unused3,
		unused4 = File.read(@firmware_file, 64).unpack("V9a12c5a7V")

		@header_parsed = true
	end
	
	def must_have_firmware_file
		if @firmware_file.nil?
			raise RuntimeError.new("Attempted to get a firmware parameter without a firmware file.  Not cool, man.")
		end
	end

	def write_data_file(dest, size, offset)
		data = File.read(@firmware_file, size, offset)
		File.write(dest, data)
	end
	
	def verify_checksum(size, offset, sum)
		data = File.read(@firmware_file, size, offset)
		checksum(data) == sum
	end

	def checksum(s)
		s.unpack("V*").inject(0) { |v, i| v ^= i }
	end
end

def mkdns323fw(args)
	opts = OptionParser.new
	optargs = {}

	opts.on('-h', '--help',
	        "Print this help") { puts opts.to_s; exit 0 }
	opts.on('-k KERNEL', '--kernel KERNEL',
	        "Specify the kernel to include in the firmware image",
	        String) { |k| optargs[:kernel_file] = k }
	opts.on('-i INITRD', '--initrd INITRD',
	        "Specify the initrd to include in the firmware image",
	        String) { |i| optargs[:initrd_file] = i }
	opts.on('-d DEFAULTS', '--defaults DEFAULTS',
	        "Specify the defaults.tar.gz to include in the firmware image (optional)",
	        String) { |d| optargs[:defaults_file] = d }
	opts.on('-o OUTPUT', '--output OUTPUT',
	        "Specify where to put the resulting firmware image",
	        String) { |o| optargs[:output] = o }
	opts.on('-p PROD_ID', '--product-id PROD_ID',
	        "The product ID to embed in the firmware image",
	        Integer) { |p| optargs[:prod_id] = p }
	opts.on('-c CUSTOM_ID', '--custom-id CUSTOM_ID',
	        "The custom ID to embed in the firmware image",
	        Integer) { |c| optargs[:custom_id] = c }
	opts.on('-m MODEL_ID', '--model-id MODEL_ID',
	        "The model ID to embed in the firmware image",
	        Integer) { |m| optargs[:model_id] = m }
	opts.on('-s SIGNATURE', '--signature SIGNATURE',
	        "The firmware signature type (either FrodoII, Chopper or Gandolf)",
	        String) { |s| optargs[:signature] = s }

	opts.parse(args)
	
	opts = optargs

	%w{kernel_file initrd_file output prod_id custom_id model_id}.each do |k|
		if opts[k.to_sym].nil?
			$stderr.puts "Missing required argument #{k}"
			exit 1
		end
	end

	%w{kernel_file initrd_file}.each do |k|
		if !File.exist?(opts[k.to_sym])
			$stderr.puts "#{k} file (#{opts[k.to_sym]}) doesn't exist!"
			exit 1
		end
	
		if !is_uboot(opts[k.to_sym])
			$stderr.puts "#{k} file #{opts[k.to_sym]} is not a uboot file"
			exit 1
		end
	end
	
	if opts[:defaults] and !File.exist?(opts[:defaults])
		$stderr.puts "default file (#{opts[:defaults]}) doesn't exist!"
		exit 1
	end
	
	opts[:signature] ||= "FrodoII"
	
	begin
		fw = DnsFirmware.new(opts)
		fw.write_firmware_file(opts[:output])
	rescue StandardError => e
		$stderr.puts "Firmware generation failed: #{e.class}: #{e.message}"
		e.backtrace.each { |l| puts "   #{l}" }
	else
		puts "Firmware generation completed successfully."
	end
end

def splitdns323fw(args)
	opts = OptionParser.new
	optargs = {}

	opts.on('-h', '--help',
	        "Print this help") { $stderr.puts opts.to_s; exit 0 }
	opts.on('-k KERNEL', '--kernel KERNEL',
	        "Write out the kernel to the specified file",
	        String) { |k| optargs[:kernel] = k }
	opts.on('-i INITRD', '--initrd INITRD',
	        "Write out the initrd to the specified file",
	        String) { |i| optargs[:initrd] = i }
	opts.on('-d DEFAULTS', '--defaults DEFAULTS',
	        "Write out the defaults.tar.gz to the specified file",
	        String) { |d| optargs[:defaults] = d }

	image = opts.parse(args)

	if image.nil? or image.empty?
		$stderr.puts "No firmware image provided!"
		exit 1
	end
	
	if image.length > 1
		$stderr.puts "I can only read one firmware image!"
		exit 1
	end
		
	image = image[0]

	fw = DnsFirmware.new(image)
	
	puts "#{fw.signature} firmware signature found"

	fw.verify_kernel_checksum or $stderr.puts "Kernel data failed checksum verification"
	puts "Kernel is #{fw.k_size} bytes"
	fw.verify_initrd_checksum or $stderr.puts "Initrd data failed checksum verification"
	puts "initrd is #{fw.i_size} bytes"
	fw.verify_defaults_checksum or $stderr.puts "Defaults data failed checksum verification"
	puts "defaults.tar.gz is #{fw.d_size} bytes"
	
	puts "Product ID: #{fw.product_id}"
	puts "Custom ID: #{fw.custom_id}"
	puts "Model ID: #{fw.model_id}"
	
	if optargs[:kernel]
		fw.write_kernel_file(optargs[:kernel])
		puts "Kernel data written to #{optargs[:kernel]}"
		unless is_uboot(optargs[:kernel])
			puts "WARNING: kernel data does not appear to be a uBoot file"
		end
	end
	
	if optargs[:initrd]
		fw.write_initrd_file(optargs[:initrd])
		puts "initrd data written to #{optargs[:initrd]}"
		unless is_uboot(optargs[:initrd])
			puts "WARNING: initrd data does not appear to be a uBoot file"
		end
	end
	
	if optargs[:defaults]
		fw.write_defaults_file(optargs[:defaults])
		puts "defaults.tar.gz written to #{optargs[:defaults]}"
	end
end

def is_uboot(file)
	File.read(file, 4) == "\x27\x05\x19\x56"
end

if $0 == __FILE__
	case File.basename($0)
		when 'mkdns323fw'   then mkdns323fw(ARGV)
		when 'splitdns323fw' then splitdns323fw(ARGV)
		else
			$stderr.puts "Please call me as either 'mkdns323fw' or 'splitdns323fw'; symlinks are good"
			exit 1
	end
end