如何检测应用程序是否被破解,而不需要检查签名身份?

9

曾经有一个方法可以检查应用程序是否是从应用商店购买的,以防止破解:

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

但是这种方法已经不再有效,因为黑客已经找到了绕过修改Info.plist的方法。我知道这个旧问题,但那里提供的答案都依赖于上述技术,而这种技术已经不再有效。

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


4
感谢您希望看到最新的答案!这篇重复的答案和修改后的答案都是在2009年发布的。+1表示同意您的想法。 - Carl Veazey
8
老实说,不必费心了。首先,黑客有很多新技术来绕过这些限制,所以他们总是比你快一步,而且即使你付出努力也可能不值得。我的一些非开发人员的朋友会盗版,他们要么免费获取要么干脆不要。如果他们安装一个应用程序,然后看到“此应用程序已被盗版,请从应用商店购买”,他们就会直接删除它。 - Greg
在早期问题的最高票答案的评论以及该答案的更新中,提到了另一种技术作为Info.plist检查的首选替代方法:http://landonf.bikemonkey.org/code/iphone/iPhone_Preventing_Piracy.20090213.html。这种方法现在也不再有效吗? - Brad Larson
2
我曾经读过Rovio开发人员的一篇文章,说你花时间与黑客作斗争会比不理睬他们更浪费钱。更重要的是,盗版可能会为您的应用程序带来一些新用户。他们免费下载并使用它,如果他们喜欢它,他们可能会向非盗版用户推荐它,后者将稍后购买您的应用程序。Greg所说的也是真的。我有一个越狱的设备,并且购买了Skype。当我启动它时,我收到了一个很棒的UIAlertView,说这个应用程序不能在越狱设备上运行,然后杀死了这个应用程序。我很喜欢它。 - rdurand
Rovio可能会这么说,但是他们的商业模式主要依赖于(几乎无法破解的)附加购买,而不是相对微小的应用程序价格。并非所有开发者都希望或能够依赖准自由模式。另一方面:完全同意,在破解检测代码中即使有很小的误报率也是不可接受的。 - Dave
显示剩余4条评论
4个回答

12
我个人喜欢 Mick 的回答,因为它简短明了。
Greg 的回答无效 - Mick 的代码只检查应用程序是否可以打开该 URL,因此不应该发生崩溃的机会。
我之前在我的一款应用程序中实现的方法更严格地检查应用程序是否加密,如果没有加密,则很可能是破解版应用程序:
从分析数据来看,这种方法已经为我防止了成千上万的盗版用户,并且只需要花费大约 5 分钟来实施,因此成本几乎为零 - 对我来说,我并不关心它是否增加了销量(我确信它不会增加销量),更多的是我不想让人们免费使用我的辛勤劳动。此外,我的应用程序的相当数量的内容在确定应用程序是否为盗版后才提供信息,并返回垃圾数据。
在 main.m 中。
#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;
}

这让我从xCode启动应用程序时返回NO。这正常吗? - Dmitry
@Altaveron 这很正常。只有当您从应用商店启动应用程序时,它才会返回YES。当您将ipa文件提交给苹果时,它会对应用程序进行加密和签名。调试和AdHoc版本始终不会被加密,而Appstore版本则会被加密。 - Tertium
如何在将应用程序发送到AppStore之前测试代码? - Dmitry
1
苹果在审核应用程序时是否使用未加密版本的应用程序?如果是,它可能会向他们显示错误的内容,并导致您的应用程序被拒绝。 - Matej
请问您能分享一下它的 Swift 版本吗? - Asad Ali Choudhry

3

官方的苹果回答:

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

你可能更新了程序,没有使用第一个版本。 - ninjaneer

1
我建议使用与@user1353482提出的相同方式的较小代码片段来完成相同的事情。 我会在注释中编写,但是那样代码将无法阅读。 此外,我可能是错误的,但似乎即使在编译模拟器时也不再需要额外的定义(至少在xcode 4.5.1中有效,目标为5.0)。
请注意,此代码在调试和adhoc二进制文件上返回false,但我们正在谈论应用商店,对吧? 最终加密是由Apple完成的,您不应该在家中尝试这样做 :)
#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;
}

-4

虽然这不是检查应用程序是否从App Store购买的方法,但我使用此代码来检查我的应用程序是否在越狱设备上运行:

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

9
并不是最好的回答,因为并非所有越狱用户都是盗版者。如果有人购买了你的应用程序,并因为他们越狱而崩溃,这会让他们很生气。 - Greg
但是"Icy"和"Installer"呢? - Yauheni Shauchenka
Greg,我的代码不会导致应用程序崩溃,它只是检查URL是否可以打开,即URL方案是否已在设备上注册。 - Mick Walker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接