如何从iOS设备上安装的iOS应用程序捕获打印报表?

问题描述 投票:0回答:2

我正在阅读有关开发 iOS 应用程序的一些良好实践,并研究使用 Console.app 监控从 App Store 安装的 iOS 应用程序的日志的可能性。所以,我在这里进行测试,但我注意到

print
语句没有出现在 Console.app 中,只有
NSLog
出现。我的问题是: 有什么方法可以查看设备上安装的 iOS 应用程序中使用
print
命令生成的日志吗?使用 Frida、Console.app 或任何其他方式?

如果没有其他方法,是否意味着

print
命令比
NSLog
更安全?这对我来说似乎非常违反直觉🤔

ios swift xcode jailbreak frida
2个回答
4
投票
iOS 应用程序中的

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

默认情况下,您只能在 XCode 输出面板中查看

print
命令的输出。然而,
print
命令本身始终包含在调试和发布版本中,因此会被执行。如果没有连接 XCode 来检索打印语句,则仅丢弃打印语句的输出。

我通过构建以下 SwiftUI 测试应用程序对此进行了测试(请参阅本答案的末尾),确保存档配置文件设置为

RELEASE
并存档项目,以构建 IPA 文件。 然后在 IdaPro 中分析 IPA 文件以查看实际的 ARM 汇编代码。

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

因此,如果您将 Frida 连接到应用程序,您可以挂钩 print 方法

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()
        }
    }
}

1
投票

当且仅当您想在应用程序中使用 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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.