Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issues with has_arc() and has_canary() Functions Not Detecting Symbols in Mach-O Files #44

Open
cpuu opened this issue Nov 8, 2023 · 2 comments · May be fixed by #45
Open

Issues with has_arc() and has_canary() Functions Not Detecting Symbols in Mach-O Files #44

cpuu opened this issue Nov 8, 2023 · 2 comments · May be fixed by #45

Comments

@cpuu
Copy link

cpuu commented Nov 8, 2023

Environment

  • Rust version: rustc 1.73.0 (cc66ad468 2023-10-03)
  • Goblin version: goblin = "0.5.2"
  • Operating System and version: Apple M2 Pro, macOS Sonoma 14.1

Description

I've encountered a potential issue with the has_arc() and has_canary() functions in the macho.rs file. These functions are intended to check for the presence of specific keywords in the imports of a Mach-O binary: _objc_release for has_arc(), which should return true if ARC is being used, and either ___stack_chk_fail or ___stack_chk_guard for has_canary(), which should return true if stack protection is enabled.

Expected Behavior

When provided with a Mach-O binary that has ARC and stack canaries properly applied, the has_arc() function is expected to return true if the _objc_release symbol is present

$ nm ~/Desktop/HelloWorld | grep _objc_release
                 U _objc_release

and has_canary() should return true if stack protection symbols are present.

$  nm ~/Desktop/hello_canary
                 U ___stack_chk_fail
                 U ___stack_chk_guard
0000000100000000 T __mh_execute_header
0000000100003f2c T _main
                 U _read

Actual Behavior

Despite supplying a Mach-O file that I've verified to have ARC enabled, the has_arc() function returned false. Upon further inspection, it appears that the list of imports retrieved within the function is empty. This same behavior is observed with the has_canary() function, suggesting that both functions may consistently return false regardless of the actual contents of the Mach-O binary.

$ ./checksec -f ~/Desktop/hello_canary
MachO64: | 
ARC: false 
Canary: false 
...
 | File: /Users/cpuu/Desktop/hello_canary

Investigation

To further investigate the issue, I added additional print statements to the has_arc() function to trace the flow and check where it might be failing. Below is the modified version of the has_arc() function:

fn has_arc(&self) -> bool {
    println!("Starting has_arc check...");

    match self.imports() {
        Ok(imports) => {
            println!("Successfully retrieved imports. Number of imports: {}", imports.len());
            if imports.is_empty() {
                println!("No imports found. Exiting check.");
            }

            for import in &imports {
                println!("Import name: {}", import.name);
                if import.name == "_objc_release" {
                    println!("Found '_objc_release' import. ARC is used. Exiting check.");
                    return true;
                }
            }

            println!("'_objc_release' import not found. ARC is not used.");
            false
        },
        Err(e) => {
            println!("Error while getting imports: {:?}", e);
            false
        }
    }
}

When running the above code with a Mach-O binary that is known to have ARC enabled, the output was as follows:

Starting has_arc check...
Successfully retrieved imports. Number of imports: 0
No imports found. Exiting check.
'_objc_release' import not found. ARC is not used.

This output indicates that the imports() call is successful, but it retrieves an empty list of imports, leading to the function incorrectly reporting that ARC is not used. Given that I have verified the binary has ARC enabled through other means, this result seems to be inaccurate.

Additional Information

I have verified the presence of ARC and stack canaries in my Mach-O binary using both otool and nm command-line tools, which show the expected symbols. This leads me to believe there might be a discrepancy in how the macho.rs file handles the parsing or detection of these symbols.

I am wondering if there could be an underlying issue with how imports are being retrieved or if there's an assumption made by the checker that doesn't hold true for all Mach-O binaries.

Any insights or suggestions on this matter would be greatly appreciated.

@titison
Copy link

titison commented Nov 11, 2023

For me the has_arc and has_canary functions are able to find imports (and return true when the _objc_release symbol is found). But i am running on Linux Mint 21 and not Mac currently.

You said you are using Goblin 0.5.2. Are you using the official 0.0.9 release or did you checkout this git? (Because the current state on git requires at least goblin 0.6.0). If you are using the official release, please try the current state on git.

If the problems persist, could you provide further information on the executables you were checking?

EDIT:
Are you using swift? Maybe its related to this issue #6

@titison
Copy link

titison commented Nov 11, 2023

I looked into this further and the problem seems to be that checksec uses the imports() function which only returns imports known to dyld.
However, this info is not present for all MachO Files.

A potential fix would be to change from the imports function to the symbols() function.
Replace the corresponding functions with:

    fn has_arc(&self) -> bool {
        for i in self.symbols() {
            if let Ok((symbol,_)) = i {
                if symbol == "_objc_release" {
                    return true;
                }
            }
        }
        false
    }
    fn has_canary(&self) -> bool {
        for i in self.symbols() {
            if let Ok((symbol,_)) = i {
                match symbol {
                    "___stack_chk_fail" | "___stack_chk_guard" => return true,
                    _ => continue,
                }
            }
        }
        false
    }

titison added a commit to titison/checksec.rs that referenced this issue Nov 11, 2023
@titison titison linked a pull request Nov 11, 2023 that will close this issue
titison added a commit to titison/checksec.rs that referenced this issue Nov 12, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants