如何从 iOS 设备上安装的 iOS 应用捕获打印语句?

How to capture print statements from iOS app installed on iOS device?

我正在阅读有关开发 iOS 应用程序的一些良好做法,并正在研究 monitoring logs of an iOS app installed from App Store using Console.app 的可能性。所以,我在这里测试,但我注意到 print 语句没有出现在 Console.app 中,只有 NSLog 出现了。我的问题是:有没有什么方法可以查看设备上安装的 iOS 应用程序中使用 print 命令生成的日志?使用 Frida,Console.app 或任何其他方式?

如果没有其他方法,是否意味着 print 命令比 NSLog 更安全?这对我来说似乎非常违反直觉

当且仅当您想在您的应用程序中使用 print 和 printf 来转到文件或任何文件描述符时:

import SwiftUI
import Darwin
import os.log

extension OSLog {
    private static var subsystem = Bundle.main.bundleIdentifier!
    static let `default` = OSLog(subsystem: subsystem, category: "default")
}

extension TestApp {
    func subscribeFileToStderrAndStdoutIfNotAttachedToDebugger() {
        if isatty(STDERR_FILENO) != 1 {
            let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
            let logfileUrl = documentsUrl.appendingPathComponent("out.log")
            logfileUrl.withUnsafeFileSystemRepresentation { path in
                guard let path = path else {
                    return
                }
                print("redirect stdout and stderr to: \(String(cString: path))")
                let file = fopen(path, "a")
                assert(file != nil, String(cString: strerror(errno)))
                let fd = fileno(file)
                assert(fd >= 0, String(cString: strerror(errno)))
                let result1 = dup2(fd, STDERR_FILENO)
                assert(result1 >= 0, String(cString: strerror(errno)))
                let result2 = dup2(fd, STDOUT_FILENO)
                assert(result2 >= 0, String(cString: strerror(errno)))
            }
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        subscribeFileToStderrAndStdoutIfNotAttachedToDebugger()
        return true
    }
}

print iOS 应用程序中的语句未记录到 iOS 上的 [持久] 日志记录系统,因此您无法通过打印语句访问应用程序的输出,如果它们在过去发生过。

默认情况下,您只能在 XCode 输出面板中显示 print 命令的输出。但是 print 命令本身总是包含在调试和发布版本中,因此会被执行。如果没有 XCode 连接来检索它,则仅丢弃打印语句的输出。

我通过构建以下 SwiftUI 测试应用程序对此进行了测试(请参阅此答案的末尾),确保存档配置文件设置为 RELEASE 并存档项目,以构建 IPA 文件。 然后在 IdaPro 中分析 IPA 文件以查看实际的 ARM 汇编代码。

并且在所有使用不同选项(例如“从 Bitcode 重建”(取消)激活)的测试中,代码始终存在。

因此,如果您将 Frida 附加到应用程序,您可以挂钩打印方法 print(_:separator:terminator:) 以检索所有否则会被丢弃的输出。

struct ContentView: View {
    @State var number : Int = 1
    var body: some View {
        VStack {
            Button("  Print  ") {
                print("print test abcdefgh number %d", number)
            }.padding()
            Button("  os_log  ") {
                os_log("os_log test abcdefgh number %d", number)
            }.padding()
            Button("randomize") {
                self.number = Int.random(in: 1...11111)
            }.padding()
        }
    }
}