Is there a Swift-only way to detect whether an app bundle is translocated to a random path by GateKeeper?

I am thinking of whether I can only use Swift to achieve what this script does:
McBopomofo/BundleTranslocate.m at master · openvanilla/McBopomofo (github.com)

I wonder whether this approach works:
(Theoretically, if the path is a randomized path then it cannot be writable to FileManager.)

  func readonlyAccessCheckGPR(_ bundle: String) -> Bool {
    !FileManager.default.isWritableFile(atPath: (bundle as NSString).expandingTildeInPath)
  }

I remember pretty much none of the file system is writeable from sandboxed mac apps. Exceptions are Documents & Temporary folders and explicit exceptions set in entitlements.

I guess you can port that above obj-c sample to swift pretty much one to one (or just use it in its obj-c form as is if you can).

Thanks for your response.

My goal is to make my project sans-(obj)c(pp).
I tried porting the above obj-c example to Swift line-by-line, but Swift has much tougher type casting limitations.
At least, the following one in Swift looks impossible to me:

struct statfs *bufs = (struct statfs *)calloc(entryCount, entrySize);

The rhs part is an unsafe pointer and it appears that Swift does not allow it to be casted to statfs.

In this case, you can just create an array of entryCount instances of default-initialized statfs, then pass that array to getfsstat.

Try this:

public func isAppBundleTranslocated(bundlePath: String) -> Bool {
    var entryCount = getfsstat(nil, 0, 0)
    var entries: [statfs] = .init(repeating: .init(), count: Int(entryCount))
    let absPath = (bundlePath as NSString).expandingTildeInPath.cString(using: .utf8)
    entryCount = getfsstat(&entries, entryCount * Int32(MemoryLayout<statfs>.stride), MNT_NOWAIT)
    for entry in entries.prefix(Int(entryCount)) {
        let isMatch = withUnsafeBytes(of: entry.f_mntfromname) { f_mntfromname in
            strcmp(absPath, f_mntfromname.baseAddress) == 0
        }
        if isMatch {
            var stat = statfs()
            let rc = statfs(absPath, &stat)
            return rc == 0
        }
    }
    return false
}
1 Like

Many thanks for your demonstration.
Could you please tell me which license are you going to apply to it?

I disclaim all rights to my Swift translation. Since it is a close derivative of the Objective-C version, you need to follow the license of the original code.

1 Like

Does this mean that my approach (of using FileManager) works for non-sandbox-entitled apps?

Thanks for your confirmation. I'll write credits in this way:

  // Determines if an app is translocated by Gatekeeper to a randomized path.
  // See https://weblog.rogueamoeba.com/2016/06/29/sierra-and-gatekeeper-path-randomization/
  // Originally written by Zonble Yang in Objective-C (MIT License).
  // Swiftified by: Rob Mayoff. Ref: https://forums.swift.org/t/58719/5
  func isAppBundleTranslocated(atPath bundlePath: String) -> Bool { ... }