# a file loader for U-Boot "uImage" flash images # Copyright (c) 2011-2021 Hex-Rays # ALL RIGHTS RESERVED. import idaapi import idc import zlib import ida_idp import ida_typeinf IH_TYPE_INVALID = 0 # /* Invalid Image */ IH_TYPE_STANDALONE = 1 # /* Standalone Program */ IH_TYPE_KERNEL = 2 # /* OS Kernel Image */ IH_TYPE_RAMDISK = 3 # /* RAMDisk Image */ IH_TYPE_MULTI = 4 # /* Multi-File Image */ IH_TYPE_FIRMWARE = 5 # /* Firmware Image */ IH_TYPE_SCRIPT = 6 # /* Script file */ IH_TYPE_FILESYSTEM = 7 # /* Filesystem Image (any type) */ ImageTypeNames = [ "Invalid", "Standalone Program", "OS Kernel", "RAMDisk", "Multi-File", "Firmware", "Script file", "Filesystem" ] IH_ARCH_INVALID = 0 # /* Invalid CPU */ IH_ARCH_ALPHA = 1 # /* Alpha */ IH_ARCH_ARM = 2 # /* ARM */ IH_ARCH_I386 = 3 # /* Intel x86 */ IH_ARCH_IA64 = 4 # /* IA64 */ IH_ARCH_MIPS = 5 # /* MIPS */ IH_ARCH_MIPS64 = 6 # /* MIPS 64 Bit */ IH_ARCH_PPC = 7 # /* PowerPC */ IH_ARCH_S390 = 8 # /* IBM S390 */ IH_ARCH_SH = 9 # /* SuperH */ IH_ARCH_SPARC = 10 # /* Sparc */ IH_ARCH_SPARC64 = 11 # /* Sparc 64 Bit */ IH_ARCH_M68K = 12 # /* M68K */ IH_ARCH_NIOS = 13 # /* Nios-32 */ IH_ARCH_MICROBLAZE = 14 # /* MicroBlaze */ IH_ARCH_NIOS2 = 15 # /* Nios-II */ IH_ARCH_BLACKFIN = 16 # /* */ IH_ARCH_AVR32 = 17 # /* */ IH_ARCH_ST200 = 18 # /* */ IH_ARCH_SANDBOX = 19 # /* */ IH_ARCH_NDS32 = 20 # /* */ IH_ARCH_OPENRISC = 21 # /* */ IH_ARCH_ARM64 = 22 # /* */ IH_ARCH_ARC = 23 # /* */ CPUNames = [ "Invalid", "Alpha", "ARM", "x86", "IA64", "MIPS", "MIPS64", "PowerPC", "IBM S390", "SuperH", "Sparc", "Sparc64", "M68K", "Nios-32", "MicroBlaze", "Nios-II", "Blackfin", "AVR32", "ST200","Sandbox","NDS32", "OpenRISC", "ARM64", "ARC" ] IDACPUNames = { IH_ARCH_ALPHA: "alphab", IH_ARCH_ARM:"ARM", IH_ARCH_I386: "metapc", IH_ARCH_IA64: "ia64b", IH_ARCH_MIPS:"mipsl", IH_ARCH_MIPS64:"mipsl", IH_ARCH_PPC: "ppc", IH_ARCH_SH: "SH4", IH_ARCH_SPARC: "sparcb", IH_ARCH_SPARC64:"sparcb", IH_ARCH_M68K:"68K", IH_ARCH_ARM64:"ARM", IH_ARCH_ARC: "arcmpct" } IDAABINames = { IH_ARCH_MIPS:"n32", IH_ARCH_MIPS64:"n64" } IH_COMP_NONE = 0 # /* No Compression Used */ IH_COMP_GZIP = 1 # /* gzip Compression Used */ IH_COMP_BZIP2 = 2 # /* bzip2 Compression Used */ IH_COMP_LZMA = 3 # /* lzma Compression Used */ IH_COMP_LZO = 4 # /* lzo Compression Used */ CompTypeNames = [ "", "gzip", "bzip2", "lzma", "lzo" ] IH_MAGIC = 0x27051956 # Image Magic Number IH_NMLEN = 32 # Image Name Length import ctypes uint8_t = ctypes.c_byte uint32_t = ctypes.c_uint class image_header(ctypes.BigEndianStructure): _fields_ = [ ("ih_magic", uint32_t), # Image Header Magic Number ("ih_hcrc", uint32_t), # Image Header CRC Checksum ("ih_time", uint32_t), # Image Creation Timestamp ("ih_size", uint32_t), # Image Data Size ("ih_load", uint32_t), # Data Load Address ("ih_ep", uint32_t), # Entry Point Address ("ih_dcrc", uint32_t), # Image Data CRC Checksum ("ih_os", uint8_t), # Operating System ("ih_arch", uint8_t), # CPU architecture ("ih_type", uint8_t), # Image Type ("ih_comp", uint8_t), # Compression Type ("ih_name", uint8_t * IH_NMLEN), # Image Name ] RomFormatName = "U-Boot image" # ----------------------------------------------------------------------- def dwordAt(li, off): li.seek(off) s = li.read(4) if len(s) < 4: return 0 return struct.unpack('= slen: bytes = li.read(slen) fit = min(len(bytes), slen) ctypes.memmove(ctypes.addressof(s), bytes, fit) return s # ----------------------------------------------------------------------- def accept_file(li, filename): """ Check if the file is of supported format @param li: a file-like object which can be used to access the input data @param filename: name of the file, if it is an archive member name then the actual file doesn't exist @return: 0 - no more supported formats string "name" - format name to display in the chooser dialog dictionary { 'format': "name", 'options': integer } options: should be 1, possibly ORed with ACCEPT_FIRST (0x8000) to indicate preferred format """ header = read_struct(li, image_header) # check the signature if header.ih_magic == IH_MAGIC: # accept the file t = header.ih_type c = header.ih_arch if t >= len(ImageTypeNames): t = "unknown type(%d)" % t else: t = ImageTypeNames[t] if c >= len(CPUNames): cname = "unknown CPU(%d)" % c else: cname = CPUNames[c] fmt = "%s (%s for %s)" % (RomFormatName, t, cname) comp = header.ih_comp if comp != IH_COMP_NONE: if comp >= len (CompTypeNames): cmpname = "unknown compression(%d)" else: cmpname = "%s compressed" % CompTypeNames[comp] fmt += " [%s]" % cmpname proc = '' if c in IDACPUNames: proc = IDACPUNames[c] return {'format': fmt, 'processor': proc} # unrecognized format return 0 # ----------------------------------------------------------------------- def load_file(li, neflags, format): """ Load the file into database @param li: a file-like object which can be used to access the input data @param neflags: options selected by the user, see loader.hpp @return: 0-failure, 1-ok """ if format.startswith(RomFormatName): li.seek(0) header = read_struct(li, image_header) c = header.ih_arch cname = IDACPUNames.get(c) if not cname: idc.warning("Unsupported CPU") #return if not header.ih_comp in (IH_COMP_NONE, IH_COMP_GZIP): idc.warning("Can only handle uncompressed or gzip-compressed images") return if cname: idaapi.set_processor_type(cname, ida_idp.SETPROC_LOADER) idc.AddSeg(header.ih_load, header.ih_load + header.ih_size, 0, 1, idaapi.saRelPara, idaapi.scPub) # copy bytes to the database if header.ih_comp == IH_COMP_NONE: li.file2base(ctypes.sizeof(header), header.ih_load, header.ih_load + header.ih_size, 0) else: cdata = li.read(header.ih_size) d = zlib.decompressobj(zlib.MAX_WBITS|32) udata = d.decompress(cdata) udata += d.flush() # expand segment to fit uncompressed data idc.set_segment_bounds(header.ih_load, header.ih_load, header.ih_load+len(udata), idc.SEGMOD_KEEP) idaapi.put_bytes(header.ih_load, udata) if cname == "ARM" and (header.ih_ep & 1) != 0: # Thumb entry point header.ih_ep -= 1 split_sreg_range(header.ih_ep, "T", 1) idaapi.add_entry(header.ih_ep, header.ih_ep, "start", 1) aname = IDAABINames.get(header.ih_arch) if aname: ida_typeinf.set_abi_name(aname) print("Load OK") return 1