Last active
March 3, 2024 10:09
-
-
Save themactep/a20b7033201b2338b5e46063a2ac8200 to your computer and use it in GitHub Desktop.
Firmware spitter
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/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