Skip to content

macOS 11 boot kext collection fails to import because ntools is set to 0 in LC_BUILD_VERSION #2192

Description

@zhuowei

Describe the bug
Ghidra 9.1.2 fails to import the boot kext cache from macOS 11 beta 3, because its Mach-O file contains a LC_BUILD_VERSION header with ntools set to 0.

To Reproduce
Steps to reproduce the behavior:

  1. Try to import /System/Library/KernelCollections/BootKernelExtensions.kc from macOS 11 beta 3

Expected behavior
The kernel cache is imported.

Actual behaviour

This error is shown:

Error importing file:
java.io.IOException
	at ghidra.app.util.opinion.MachoLoader.load(MachoLoader.java:98)
	at ghidra.app.util.opinion.AbstractLibrarySupportLoader.doLoad(AbstractLibrarySupportLoader.java:346)
	at ghidra.app.util.opinion.AbstractLibrarySupportLoader.loadProgram(AbstractLibrarySupportLoader.java:83)
	at ghidra.app.util.opinion.AbstractProgramLoader.load(AbstractProgramLoader.java:112)
	at ghidra.plugin.importer.ImporterUtilities.importSingleFile(ImporterUtilities.java:401)
	at ghidra.plugin.importer.ImporterDialog.lambda$okCallback$7(ImporterDialog.java:351)
	at ghidra.util.task.TaskLauncher$1.run(TaskLauncher.java:90)
	at ghidra.util.task.Task.monitoredRun(Task.java:126)
	at ghidra.util.task.TaskRunner.lambda$startTaskThread$1(TaskRunner.java:94)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)

---------------------------------------------------
Build Date: 2020-Feb-12 1149 EST
Ghidra Version: 9.1.2
Java Home: /Library/Java/JavaVirtualMachines/amazon-corretto-11.jdk/Contents/Home
JVM Version: Amazon.com Inc. 11.0.4
OS: Mac OS X 10.15.6 x86_64

After looking for the root cause of this, it appears that this is caused by BuildVersionCommand.toDataType() passing the ntools field from the load command directly to ArrayDataType, which throws if the number of items is 0.

Unlike other MachO executables, the kext collection sets ntool to 0:

Load command 1
      cmd LC_BUILD_VERSION
  cmdsize 24
 platform 0
    minos 0.0
      sdk n/a
   ntools 0

This appears to still be a valid MachO, and indeed Ghidra does handle .o files with ntools=0 just fine; it only fails to handle this case for executables such as the kext collection.

Environment (please complete the following information):

  • OS: macOS 10.15.6
  • Java Version: 11.0.4
  • Ghidra Version: 9.1.2

Appendix: tracking down the root cause

The top level IOException wraps a NullPointerException:

java.lang.NullPointerException
	at ghidra.program.model.data.DataUtilities.createData(DataUtilities.java:138)
	at ghidra.app.util.opinion.MachoProgramBuilder.markupHeaders(MachoProgramBuilder.java:751)
	at ghidra.app.util.opinion.MachoProgramBuilder.build(MachoProgramBuilder.java:133)
	at ghidra.app.util.opinion.MachoProgramBuilder.buildProgram(MachoProgramBuilder.java:102)
	at ghidra.app.util.opinion.MachoLoader.load(MachoLoader.java:94)
	at ghidra.app.util.opinion.AbstractLibrarySupportLoader.doLoad(AbstractLibrarySupportLoader.java:346)
	at ghidra.app.util.opinion.AbstractLibrarySupportLoader.loadProgram(AbstractLibrarySupportLoader.java:83)
	at ghidra.app.util.opinion.AbstractProgramLoader.load(AbstractProgramLoader.java:112)
	at ghidra.plugin.importer.ImporterUtilities.importSingleFile(ImporterUtilities.java:401)
	at ghidra.plugin.importer.ImporterDialog.lambda$okCallback$7(ImporterDialog.java:351)
	at ghidra.util.task.TaskLauncher$1.run(TaskLauncher.java:90)
	at ghidra.util.task.Task.monitoredRun(Task.java:126)
	at ghidra.util.task.TaskRunner.lambda$startTaskThread$1(TaskRunner.java:94)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)

This shows that the loadCommandDataType is set to null:

newDataType = newDataType.clone(program.getDataTypeManager());

DataUtilities.createData(program, loadCommandAddr, loadCommandDataType, -1, false,

> 
Breakpoint hit: "thread=Task - Import File", ghidra.app.util.opinion.MachoProgramBuilder.markupHeaders(), line=751 bci=104

Task - Import File[1] locals
Method arguments:
header = instance of ghidra.app.util.bin.format.macho.MachHeader$$EnhancerByCGLIB$$f0aa4076(id=9950)
headerAddr = instance of ghidra.program.model.address.GenericAddress(id=9951)
Local variables:
loadCommand = instance of ghidra.app.util.bin.format.macho.commands.BuildVersionCommand$$EnhancerByCGLIB$$b005e22d(id=9955)
loadCommandAddr = instance of ghidra.program.model.address.GenericAddress(id=9956)
loadCommandDataType = null
Task - Import File[1] print loadCommand.toDataType()
 loadCommand.toDataType() = null

loadCommand.toDataType() is returning null because the ContinuesInterceptor caught an IllegalArgumentException:

Step completed: "thread=Task - Import File", generic.continues.ContinuesInterceptor.intercept(), line=42 bci=38

Task - Import File[1] locals
Method arguments:
obj = instance of ghidra.app.util.bin.format.macho.commands.BuildVersionCommand$$EnhancerByCGLIB$$b005e22d(id=9955)
method = instance of java.lang.reflect.Method(id=9976)
args = instance of java.lang.Object[0] (id=9974)
proxy = instance of net.sf.cglib.proxy.MethodProxy(id=9977)
Local variables:
retValFromSuper = null
e = instance of java.lang.IllegalArgumentException(id=9978)
Task - Import File[1] print e
Internal exception during operation:
    Unexpected JDWP Error: 502

The original IllegalArgumentException is:

Exception occurred: java.lang.IllegalArgumentException (to be caught at: ghidra.app.util.bin.format.macho.commands.BuildVersionCommand$$EnhancerByCGLIB$$b005e22d$$FastClassByCGLIB$$2ec3d351.invoke(), line=-1 bci=774)"thread=Task - Import File", ghidra.program.model.data.ArrayDataType.<init>(), line=64 bci=85

shows that ArrayDataType throws the IllegalArgumentException if the number of items is <= 0

struct.add(new ArrayDataType(buildToolVersionDataType, ntools,
shows that ArrayDataType would be called based on ntools from the header

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions