diff --git a/unsign b/unsign new file mode 100755 index 000000000..0a8a5c36b --- /dev/null +++ b/unsign @@ -0,0 +1,99 @@ +#!/usr/bin/env ruby +# Deactivates any embedded code signatures in a Mach-O binary. + +module MachO + class Unsign + def self.unsign(filename) + File.open(filename, "r+") do |f| + Unsign.new(f).headers + end + end + + attr_accessor :headers + + protected + + FatHeader = Struct.new(:cpu_type, :cpu_subtype, :offset, :size, :align, :mach) + MachHeader = Struct.new(:cpu_type, :cpu_subtype, :filetype, :ncmds, :sizeofcmds, :flags, :reserved, :cmds) + LoadCommand = Struct.new(:cmd, :cmdsize) + + def initialize(f) + @f = f + @headers = process + end + + def debug(message) + puts message if ENV["DEBUG"] + end + + def word_type + @big_endian ? 'N' : 'V' + end + + def patch_code_signature(lc) + # just change LC_CODE_SIGNATURE to a high value that will be ignored by the loader + debug "PATCHING LC_CODE_SIGNATURE" + @f.seek(-8, IO::SEEK_CUR) + @f.write([0xff, lc.cmdsize].pack("#{word_type}2")) + lc + end + + def process_mach + len = @x86_64 ? 7 : 6 + header = MachHeader.new(*@f.read(len*4).unpack("#{word_type}#{len}")) + debug "MACH HEADER: #{header.inspect}" + header.cmds = (1..(header.ncmds)).collect do + lc = LoadCommand.new(*@f.read(8).unpack("#{word_type}2")) + debug "LOAD COMMAND: #{lc.inspect}" + + lc = case lc.cmd + when 0x1d then patch_code_signature(lc) + else lc + end + + @f.seek(lc.cmdsize - 8, IO::SEEK_CUR) + + lc + end + header + end + + def process_fat + num_arches, = @f.read(4).unpack("N") + arches = (1..num_arches).collect do + FatHeader.new(*@f.read(20).unpack("N5")) + end + debug "FAT HEADER: #{arches.inspect}" + arches.each do |arch| + @f.seek(arch.offset) + arch.mach = process + end + arches + end + + def process + magic, = @f.read(4).unpack("N") + debug "MAGIC: 0x%08x" % magic + case magic + when 0xcafebabe then @big_endian, @x86_64 = false, false; process_fat + when 0xfeedface then @big_endian, @x86_64 = true, false; process_mach + when 0xcffaedfe then @big_endian, @x86_64 = false, true; process_mach + when 0xcefaedfe then @big_endian, @x86_64 = false, false; process_mach + else raise "unknown magic: 0x%08x" % magic + end + end + end +end + +# command line driver +if __FILE__ == $0 + if ARGV.empty? + $stderr.puts "usage: #{$0} filename ..." + exit 1 + end + + ARGV.each do |filename| + puts "removing signatures from: #{filename}" + MachO::Unsign::unsign(filename) + end +end