关于ios:如何在不检查SignerIdentity的情况下检测应用是否被破解?

How do I detect if an app has been cracked without examining the SignerIdentity?

过去有一种方法可以检查是否从App Store购买了应用程序,以防止破解:

1
2
3
4
NSBundle *bundle = [NSBundle mainBundle];
NSDictionary *info = [bundle infoDictionary];
if ([info objectForKey: @"SignerIdentity"] != nil)
{ /* do something */  }

,但是此方法不再起作用,因为破解者已经找到了更改Info.plist的方法。我知道这个较旧的问题,但是在那里提出的答案依赖于上面的技术,该技术不再有效。

如何在不从Info.plist读取SignerIdentity的情况下,检测到您的应用是否被破解或从App Store合法购买?


我喜欢Mick的回答,因为它简短而简单。

Greg \\的响应无效-Mick \\的代码仅检查应用程序是否可以打开该URL,因此不会出现崩溃的可能性。

我已经在我的一个应用程序中实现了以下内容,在此之前,我们会更严格地检查该应用程序是否已加密(如果不是,则很可能是破解的应用程序):

从分析的angular来看,这种方法为我阻止了成千上万的盗版用户,并且大概花了5分钟的时间来实施,因此这样做的成本几乎不算什么-对我来说,我不在乎它是否增加了销售额(我可以肯定这是不会的-我不希望人们从我的辛勤工作中解放出来。此外,我的应用程序中的大量内容在弄清该应用程序是否被盗版后会提供信息,如果有,则返回垃圾数据。

在main.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#import <dlfcn.h>
#import <mach-o/dyld.h>
#import <TargetConditionals.h>

#if TARGET_IPHONE_SIMULATOR && !defined(LC_ENCRYPTION_INFO)
#define LC_ENCRYPTION_INFO 0x21
struct encryption_info_command {
    uint32_t cmd;
    uint32_t cmdsize;
    uint32_t cryptoff;
    uint32_t cryptsize;
    uint32_t cryptid;
};
#endif

static BOOL isEncrypted();

static BOOL isEncrypted () {
    const struct mach_header *header;
    Dl_info dlinfo;

    /* Fetch the dlinfo for main() */
    if (dladdr(main, &dlinfo) == 0 || dlinfo.dli_fbase == NULL) {
        //NSLog(@"Could not find main() symbol (very odd)");
        return NO;
    }
    header = dlinfo.dli_fbase;

    /* Compute the image size and search for a UUID */
    struct load_command *cmd = (struct load_command *) (header+1);

    for (uint32_t i = 0; cmd != NULL && i < header->ncmds; i++) {
        /* Encryption info segment */
        if (cmd->cmd == LC_ENCRYPTION_INFO) {
            struct encryption_info_command *crypt_cmd = (struct encryption_info_command *) cmd;
            /* Check if binary encryption is enabled */
            if (crypt_cmd->cryptid < 1) {
                /* Disabled, probably pirated */
                return NO;
            }

            /* Probably not pirated <-- can't say for certain, maybe theres a way around it */
            return YES;
        }

        cmd = (struct load_command *) ((uint8_t *) cmd + cmd->cmdsize);
    }

    /* Encryption info not found */
    return NO;
}


苹果官方回答:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Hello Dmitry,

Thank you for contacting Apple Developer Technical Support (DTS).

DTS does not provide code-level support for DRM issues.  

Please try posting your inquiry to Apple Development Forum:

<https://devforums.apple.com>

While you were initially charged a Technical Support Incident (TSI) for this request, we have assigned a replacement TSI back to your account.

Thank you for understanding our support policies.

Best Regards,

Apple Developer Support
Worldwide Developer Relations


我建议使用一个较小的代码段,该代码段与@ user1353482建议的功能相同(并且方法相同)。我会在注释中写,但是那时的代码将不可读。而且,我可能是错的,但是似乎即使为模拟器编译时也不再需要其他定义(至少在xcode 4.5.1中有效,目标是5.0)。

请注意,此代码在调试和即席二进制文件上返回false,但是我们在谈论appstore,对吗?由Apple进行最终加密的人,应该不要在家中尝试此:)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <execinfo.h>
#import <mach-o/ldsyms.h>

bool executableEncryption()
{
    const uint8_t *command = (const uint8_t *) (&_mh_execute_header + 1);
    for (uint32_t idx = 0; idx < _mh_execute_header.ncmds; ++idx)
    {
        if (((const struct load_command *) command)->cmd == LC_ENCRYPTION_INFO)
        {
            struct encryption_info_command *crypt_cmd = (struct encryption_info_command *) command;    
            if (crypt_cmd->cryptid < 1)
                return false;
            return true;
        }
        else
        {
            command += ((const struct load_command *) command)->cmdsize;
        }
    }
    return false;
}

虽然没有检查是否从App Store购买了应用程序,但我使用以下代码检查我的应用程序是否在越狱设备上运行:

1
2
3
4
+(BOOL)isJailbroken {
    NSURL* url = [NSURL URLWithString:@"cydia://package/com.example.package"];
    return [[UIApplication sharedApplication] canOpenURL:url];
}