Skip to content

Instantly share code, notes, and snippets.

@themactep
Last active March 3, 2024 10:09
Show Gist options
  • Select an option

  • Save themactep/a20b7033201b2338b5e46063a2ac8200 to your computer and use it in GitHub Desktop.

Select an option

Save themactep/a20b7033201b2338b5e46063a2ac8200 to your computer and use it in GitHub Desktop.
Firmware spitter
#!/bin/ruby
# frozen_string_literal: true
#
# Firmware spitter
# Paul Philippov <[email protected]>
#
# 2022-11-16: Initial version
# 2023-04-16: Support for dd older than v9.0
# Use the last found mtdparts
# 2024-03-02: Extract an arbitrary range of addresses
#
require 'fileutils'
def show_usage_and_die(message = nil)
puts message if message
puts "Usage: #{$PROGRAM_NAME} <binary file> [<from address> <to address>]"
puts ARGV.map.with_index {|x, idx| "#{idx} => #{x}"}.join("\n")
exit 1
end
def input_file_name
@input_file_name ||= ARGV[0]
end
def address_from
show_usage_and_die "Start address is not set" unless ARGV[1]
@address_from ||= ARGV[1]
@address_from.to_i(16) if @address_from.start_with?('0x')
end
def address_till
@address_till ||= if ARGV[2]
@address_till = ARGV[2]
@address_till.to_i(16) if @address_till.start_with?('0x')
else
full_length
end
end
def address_range_length
@address_range_lenght ||= address_till - address_from
end
def full_length
@full_length ||= File.size(input_file_name)
#`stat -c%s #{input_file_name}`
end
def extract_a_chunk
output_file_name = format('0x%08X-0x%08X.bin', address_from, address_till)
`dd if=#{input_file_name} of=#{output_file_name} skip=#{address_from}B bs=#{address_range_length} count=1`
end
def extract_all
puts "Processing #{input_file_name} file."
@text = `strings "#{input_file_name}" | grep mtdparts=.*: | tail -1`
if @text.eql?('')
puts 'ERROR: mtdparts not found.'
exit 2
end
puts "Found mtdparts: #{@text}"
outdir = "#{input_file_name}_split"
# if File.exist?(outdir)
# puts "ERROR! Output directory #{outdir} exists!"
# exit 3
# end
FileUtils.mkdir(outdir) unless File.directory?(outdir)
offset = 0
/mtdparts=[\w:(),-@]+/.match(@text)[0].split(':')[1].split(',').each_with_index do |mtdpart, idx|
puts "\nParsing #{mtdpart}"
/(?<mtdpart_size>([\w@]+|-))\((?<mtdpart_name>\w+)\)/ =~ mtdpart
puts "Name: #{mtdpart_name}"
puts "Size: #{mtdpart_size}"
if /@/.match?(mtdpart_size)
puts "Size #{mtdpart_size} consists of size and offset. Extracting size per se."
x = mtdpart_size.split('@', 2)
mtdpart_size = x[0]
offset = x[1] unless x[1].eql?('-')
puts "Size: #{mtdpart_size}"
puts "Offset: #{offset}"
end
if /^0x/.match?(mtdpart_size)
puts "Size #{mtdpart_size} is in hexadecimal format. Converting to decimal."
mtdpart_size = mtdpart_size.hex
puts "Size: #{mtdpart_size}"
end
if /^0x/.match?(offset.to_s)
puts "Offset #{offset} is in hexadecimal format. Converting to decimal."
offset = offset.hex
puts "Offset: #{offset}"
end
case mtdpart_size[-1]
when 'K', 'k'
puts "Size #{mtdpart_size} is in kilobytes. Converting to bytes."
mtdpart_size = mtdpart_size.chop.to_i * 1024
when 'M', 'm'
puts "Size #{mtdpart_size} is in megabytes. Converting to bytes."
mtdpart_size = mtdpart_size.chop.to_i * 1024 * 1024
when '-'
puts 'Size is not set. Calculating from filesize and pointer.'
puts "Binary file size: #{full_length}"
mtdpart_size = full_length - offset
end
case offset[-1]
when 'K', 'k'
puts "Size #{offset} is in kilobytes. Converting to bytes."
offset = offset.chop.to_i * 1024
when 'M', 'm'
puts "Size #{offset} is in megabytes. Converting to bytes."
offset = offset.chop.to_i * 1024 * 1024
end
puts "Size: #{mtdpart_size}"
puts "Offset: #{offset}"
fname = "#{input_file_name}_split/#{idx + 1}-#{mtdpart_name}.bin"
puts "Extracting partition #{mtdpart_name} (#{mtdpart_size}) to #{fname}."
if `dd --version|head -1|awk '{print $3}'`.to_f > 9.0
`dd if="#{input_file_name}" of="#{fname}" bs=#{mtdpart_size} count=1 skip=#{offset}B status=progress`
else
`dd if="#{input_file_name}" of="#{fname}" bs=1 count=#{mtdpart_size} skip=#{offset} status=progress`
end
offset = offset.to_i + mtdpart_size
end
end
show_usage_and_die "Please provide a file name" if ARGV.empty?
if ARGV[1]
extract_a_chunk
else
extract_all
end
puts "\nDone."
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment