我正在阅读有关开发 iOS 应用程序的一些良好实践,并研究使用 Console.app 监控从 App Store 安装的 iOS 应用程序的日志的可能性。所以,我在这里进行测试,但我注意到
print
语句没有出现在 Console.app 中,只有 NSLog
出现。我的问题是: 有什么方法可以查看设备上安装的 iOS 应用程序中使用 print
命令生成的日志吗?使用 Frida、Console.app 或任何其他方式?
如果没有其他方法,是否意味着
print
命令比NSLog
更安全?这对我来说似乎非常违反直觉🤔
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()
}
}
}
当且仅当您想在应用程序中使用 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
}
}