NationalSecurityAgency / ghidra

Ghidra is a software reverse engineering (SRE) framework
https://www.nsa.gov/ghidra
Apache License 2.0
51.7k stars 5.88k forks source link

Wrong symbol resolved from LC_DYLD_INFO_ONLY on iOS MachO binary #6994

Open TheDauntless opened 1 month ago

TheDauntless commented 1 month ago

Describe the bug

I have a (non-public) MachO iOS file for which Ghidra identifies the wrong symbol/linked library for certain imports. Hopper and LIEF do identify the correct symbol and I've confirmed this via dynamic analysis. I can't share the binary, but I'm trying to debug Ghidra to figure out the error, so some hints as to where to continue my examination could help me in fixing the bug.

The dynamic linking info is available in LC_DYLD_INFO_ONLY and it does get processed, but not applied for some reason (see further down)

There is also a MachoProcessBindScript.java script, which seems related, but this does not fix the problem and it appears that the bind processing is already done by one of the general analyzers? So not sure if this script actually still serves a purpose?

Specifics Ghidra is showing uname as the import at 0x1c85288 while it should be Swift.String.hasSuffix:

image

In Hopper, it's String.hasSuffix, which is correct:

image

And in LIEF it's correct too:

address=0x01c85288, addend=0x0
  symbol=_$sSS9hasSuffixySbSSF
  segment=__DATA
  library=/usr/lib/swift/libswiftCore.dylib

Additional Binary info There are two __LINKEDIT sections:

image

(Actually not entirely sure where the second one comes from)

And inside of it there are the following commands:

image
> otool -l binary
...
Load command 2
      cmd LC_SEGMENT_64
  cmdsize 1672
  segname __DATA
   vmaddr 0x0000000001c84000
   vmsize 0x00000000005d0000
  fileoff 29900800
 filesize 655360
  maxprot 0x00000003
 initprot 0x00000003
   nsects 20
    flags 0x0
...

The second section starts at 1c84000 so add 1288h becomes 1c85288 and so hasSuffix should be set where _uname currently is set.

I've debugged the analyzer a bit and the __LINKEDIT commands are read by dyld/BindingTable.java:

image

I wasn't able to figure out yet where the binding table is further used in the flow.

Environment:

ryanmkurtz commented 1 month ago

Sorry for the delay in getting back to you.

There is also a MachoProcessBindScript.java script, which seems related, but this does not fix the problem and it appears that the bind processing is already done by one of the general analyzers? So not sure if this script actually still serves a purpose?

This is ancient...I'll remove it.

I wasn't able to figure out yet where the binding table is further used in the flow.

https://github.com/NationalSecurityAgency/ghidra/blob/6805c6bc2036bd5c3f1fcca9042431ed92376ae4/Ghidra/Features/Base/src/main/java/ghidra/app/util/opinion/MachoProgramBuilder.java#L889-L984

ryanmkurtz commented 1 month ago

I will try to find the same flaw in my set of samples...hopefully there is something consistently wrong.

ryanmkurtz commented 1 month ago

There are two __LINKEDIT sections: (Actually not entirely sure where the second one comes from)

This is typically due to an older memory block convention we use that creates a 2nd block for the padded out part of the memory block, which we make as uninitialized. The PE loader has dropped this convention, but the Mach-O loader hasn't yet.

ryanmkurtz commented 1 month ago

I think i have reproduced the issue in one of my binaries. I will begin looking into a fix.

ryanmkurtz commented 1 month ago

I take it back, i am not reproducing it. Looking at your initial screen shot, it looks like uname is simply referencing 1c85288. In Ghidra, what is at address 15a1a80?

TheDauntless commented 1 month ago

@ryanmkurtz

image

and

image

I put a breakpoint all over the range you indicated in MachoProgramBuilder but none of them actually get triggered. This is by the way the stack trace of that previous debugger screenshot; maybe I'm in a different code path?

image
ryanmkurtz commented 1 month ago

Any way you can share the binary with me privately? If so, making a private GitHub repo and inviting just me to it has worked in the past. i understand if you can't do that tho.