检测 iOS 应用程序是不是在调试器中运行

Posted

技术标签:

【中文标题】检测 iOS 应用程序是不是在调试器中运行【英文标题】:Detecting if iOS app is run in debugger检测 iOS 应用程序是否在调试器中运行 【发布时间】:2011-06-12 07:17:00 【问题描述】:

我将我的应用程序设置为将调试输出发送到控制台或日志文件。现在,我想在代码中决定是否

它在调试器(或模拟器)中运行,因此有一个控制台窗口,我想直接在其中读取输出,或者如果 没有控制台窗口,因此应将输出重定向到文件。

有没有办法确定应用程序是否在调试器中运行?

【问题讨论】:

查看以下答案***.com/questions/458304/… @Felz (@angrest):一个程序可以在设备上运行,并且仍然在调试器中。 @KennyTM 是完全正确的。在设备中调试,你会得到一个控制台。 【参考方案1】:

可以指示调试器在启动将要调试的进程时设置环境变量。这可以在 Xcode 中通过转到菜单项 Product->Edit Scheme 来完成。然后在 Debug 方案的 Arguments 选项卡下添加一个新的环境变量。该变量应命名为“debugger”,值为“true”。那么下面的代码 sn -p 可以用来判断调试器是否启动了你的进程:

NSDictionary* env = [NSProcessInfo processInfo].environment;

if ([env[@"debugger"] isEqual:@"true"]) 
    NSLog(@"debugger yes");

else 
    NSLog(@"debugger no");

【讨论】:

好吧,这似乎不是一个解决方案——当应用程序在连接到 XCode/Debugger 的 iPhone 中运行时——我应该如何检查运行 XCode 的机器的环境变量? 调试器设置它启动的进程的环境。模拟器或设备上的可能性。在这两种情况下,调试器都可以添加环境变量。我在设备和模拟器上对其进行了测试,并且可以正常工作。环境变量不是在主机上设置的,而是为在 ios 上运行的应用程序设置的。 @Evan 尽管这在过去有效,但至少在 xcode 4.5 中无效。没有这样的环境变量。 @Tertium 我已经更新了答案,将您指向 Xcode 中的正确位置以设置环境变量。 谢谢,我明天试试,然后回信。【参考方案2】:

我通常会选择更简单的解决方案;二进制文件是否经过优化编译?

调试版本未优化,日志很好。发布版本应该有优化,而不是太多的日志。您可以使用 __OPTIMIZE__ 符号检查这一点。

对于日志记录,我将此设置用于日志记录-函数

#ifdef __OPTIMIZE__ 
  #define CWLog(...)
  #define CWLogDebug(...)
  #define CWLogInfo(...)
#else
  #define CWLog(...) NSLog(__VA_ARGS__)
  #define CWLogDebug( s, ... ) NSLog( @"DEBUG <%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
  #ifndef LOG_INFO
    #define CWLogInfo(...)
  #else
    #define CWLogInfo( s, ... ) NSLog( @"INFO <%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
  #endif
#endif
#define CWLogWarning( s, ... ) NSLog( @"WARNING <%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )
#define CWLogError( s, ... ) NSLog( @"ERROR <%p %@:(%d)> %@", self, [[NSString stringWithUTF8String:__FILE__] lastPathComponent], __LINE__, [NSString stringWithFormat:(s), ##__VA_ARGS__] )

【讨论】:

提示:有时会优化调试版本。您可以更改它的构建设置。只要开发者不改变它的默认值,你的答案就是有效的。 ;-) 恐怕这个答案没有抓住问题的重点。发布者希望将调试消息输出到控制台或文件,这取决于应用程序是否在调试器中运行,而不是是否是调试 build【参考方案3】:

技术问答 1361(entry in Mac library 和 entry in iOS library;它们是相同的)中,Apple 提供了一个功能来检测程序是否正在调试。

技术问答中的代码:

#include <assert.h>
#include <stdbool.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/sysctl.h>

static bool AmIBeingDebugged(void)
    // Returns true if the current process is being debugged (either 
    // running under the debugger or has a debugger attached post facto).

    int                 junk;
    int                 mib[4];
    struct kinfo_proc   info;
    size_t              size;

    // Initialize the flags so that, if sysctl fails for some bizarre 
    // reason, we get a predictable result.

    info.kp_proc.p_flag = 0;

    // Initialize mib, which tells sysctl the info we want, in this case
    // we're looking for information about a specific process ID.

    mib[0] = CTL_KERN;
    mib[1] = KERN_PROC;
    mib[2] = KERN_PROC_PID;
    mib[3] = getpid();

    // Call sysctl.

    size = sizeof(info);
    junk = sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, NULL, 0);
    assert(junk == 0);

    // We're being debugged if the P_TRACED flag is set.

    return ( (info.kp_proc.p_flag & P_TRACED) != 0 );

还要注意问答末尾的这个注释:

重要提示:因为kinfo_proc 结构(在&lt;sys/sysctl.h&gt; 中)的定义是由__APPLE_API_UNSTABLE 限定的,所以您应该将上述代码的使用限制在程序的调试版本中。

【讨论】:

在那篇文档中有一件我不明白的事情......在代码之后它说:“因为 kinfo_proc 结构的定义(在 中)由__APPLE_API_UNSTABLE,您应该将上述代码的使用限制在程序的调试版本中。”。那么......如果我只能将它包含在调试版本中,那么代码的意义何在?如果我只能在调试版本中使用它……这意味着我已经知道我在调试版本中…… 问题不在于 build 是否是 debug build,而在于应用程序是否在调试器中运行。问题已经假设调试构建已构建并运行,然后只想决定如何处理日志消息。您说得对,该文档实际上说您不应该在交付给客户的发布版本中使用此技术。 感谢您的澄清。我希望使用您的建议来解决我遇到的类似问题(***.com/questions/12406831/…)。不幸的是,它看起来不像正确的那个...... 看起来 kinfo_proc 不再受 __APPLE_API_UNSTABLE 条件化,所以我希望它不仅可以在调试中使用 知道如何将它实现为 swift app delegate 吗?【参考方案4】:

有不同的解决方案总是好的,所以这是我的两分钱:

这个想法是检查 stderr 文件句柄(这是 NSLog 打印到的位置)。该解决方案至少从 iOS 4 开始就可以可靠地运行,并且在 iOS 9 中一直如此,无论是在模拟器上还是在设备上。

#import <sys/ioctl.h>
#import <sys/param.h>
#if TARGET_IPHONE_SIMULATOR
    #import <sys/conf.h>
#else
// Not sure why <sys/conf.h> is missing on the iPhoneOS.platform.
// It's there on iPhoneSimulator.platform, though. We need it for D_DISK, only:
    #if ! defined(D_DISK)
        #define D_DISK  2
    #endif
#endif

BOOL isDebuggerAttatchedToConsole(void)

    // We use the type of the stderr file descriptor
    // to guess if a debugger is attached.

    int fd = STDERR_FILENO;

    // is the file handle open?
    if (fcntl(fd, F_GETFD, 0) < 0) 
        return NO;
    

    // get the path of stderr's file handle
    char buf[MAXPATHLEN + 1];
    if (fcntl(fd, F_GETPATH, buf ) >= 0) 
        if (strcmp(buf, "/dev/null") == 0)
            return NO;
        if (strncmp(buf, "/dev/tty", 8) == 0)
            return YES;
    

    // On the device, without attached Xcode, the type is D_DISK (otherwise it's D_TTY)
    int type;
    if (ioctl(fd, FIODTYPE, &type) < 0) 
        return NO;
    

    return type != D_DISK;

【讨论】:

【参考方案5】:

其实最简单的解决方案是

_isDebugging = isatty(STDERR_FILENO);

这与判断应用程序是否在调试器下运行并不完全相同,但足以(甚至更好?)确定是否应将日志写入磁盘。

【讨论】:

【参考方案6】:

对于那些正在寻找更简单解决方案的人 - 这非常有效:

func isDebuggerAttached() -> Bool 
    return getppid() != 1

【讨论】:

这是很多反调试器检测工具使用的,当然。【参考方案7】:

为什么不在 Swift 中使用条件编译块?

     #if DEBUG
        // Do something.
     #endif

有异议吗?

您可以定义是否需要运行时常量

#if DEBUG
public let IS_RUNNING_IN_DEBUGGER: Bool = true
#else
public let IS_RUNNING_IN_DEBUGGER: Bool = false
#endif

同样的方法可以用在 Objc 及其他方面。

【讨论】:

奇怪的是,当我直接在设备上启动我的 DEBUG 构建的可执行文件而不从 Xcode 启动时,它仍然返回 true...【参考方案8】:

基于 an answer in a duplicate thread 也适用于 Objective-C,并展示了 HockeyApp-iOS 是如何做到的,这里是 Swift 5 版本:

let isDebuggerAttached: Bool = 
    var debuggerIsAttached = false

    var name: [Int32] = [CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()]
    var info: kinfo_proc = kinfo_proc()
    var info_size = MemoryLayout<kinfo_proc>.size

    let success = name.withUnsafeMutableBytes  (nameBytePtr: UnsafeMutableRawBufferPointer) -> Bool in
        guard let nameBytesBlindMemory = nameBytePtr.bindMemory(to: Int32.self).baseAddress else  return false 
        return -1 != sysctl(nameBytesBlindMemory, 4, &info/*UnsafeMutableRawPointer!*/, &info_size/*UnsafeMutablePointer<Int>!*/, nil, 0)
    

    // The original HockeyApp code checks for this; you could just as well remove these lines:
    if !success 
        debuggerIsAttached = false
    

    if !debuggerIsAttached && (info.kp_proc.p_flag & P_TRACED) != 0 
        debuggerIsAttached = true
    

    return debuggerIsAttached
()

【讨论】:

此代码对我不起作用。你能解释一下它是如何工作的吗?

以上是关于检测 iOS 应用程序是不是在调试器中运行的主要内容,如果未能解决你的问题,请参考以下文章

Cordova:无论如何,是不是可以在 Javascript 中检测 iOS 应用程序是作为调试版还是作为发布版构建的?

检测 qt 是不是在运行时运行调试构建

如何检测c++中是不是附加了调试器?

有没有办法在 javascript 中检测 Chrome/Chromium/V8 分析器是不是正在运行?

设备检测是不是是android?

iOS 应用程序在使用 XCode 调试器启动时崩溃,在没有调试器的情况下运行良好