iOS Common Crypto Decrypt RC4 加密视频文件内存崩溃

Posted

技术标签:

【中文标题】iOS Common Crypto Decrypt RC4 加密视频文件内存崩溃【英文标题】:iOS Common Crypto Decrypt RC4 Encrypted video file Memory crash 【发布时间】:2018-11-09 14:33:34 【问题描述】:

我正在解密一个非常适合小尺寸文件的视频文件,但对于 300mb 以上的文件,会出现内存崩溃。代码如下: 我检查了起始字节值,它上升到 315mb,然后崩溃,我的文件大小为 350mb。

它适用于少数 iphone,但很少会崩溃,最好的解决方案是分块执行以避免内存问题,但这样做也会崩溃。

#define kChunkSizeBytes (1024*1024) // 1 MB

@implementation NSMutableData (Crypto)
   -(BOOL) doCrypto:(NSString *)key operation: (CCOperation) operation


    //Keeping it 32 as per our key
    char keyPtr[512 + 1]; // room for terminator (unused)
    bzero(keyPtr, sizeof(keyPtr));     // fill with zeroes (for padding)

    // Fetch key data

    if (![key getCString:keyPtr maxLength:sizeof(keyPtr) encoding:NSUTF8StringEncoding]) return FALSE; // Length of 'key' is bigger than keyPtr

    CCCryptorRef cryptor;




    CCCryptorStatus cryptStatus = CCCryptorCreate(operation, kCCAlgorithmRC4, 0,
                                                  keyPtr, key.length,
                                                  NULL, // IV - needed?
                                                  &cryptor);

    if (cryptStatus != kCCSuccess)  // Handle error here
        return FALSE;
    

    size_t dataOutMoved;
    size_t dataInLength = kChunkSizeBytes; // #define kChunkSizeBytes (16)
    size_t dataOutLength = CCCryptorGetOutputLength(cryptor, dataInLength, FALSE);
    size_t totalLength = 0; // Keeps track of the total length of the output buffer
    size_t filePtr = 0;   // Maintains the file pointer for the output buffer
    NSInteger startByte; // Maintains the file pointer for the input buffer

    char *dataIn = malloc(dataInLength);
    char *dataOut = malloc(dataOutLength);
    NSRange bytesRange = NSMakeRange((NSUInteger) 0, (NSUInteger) 0);

    for (startByte = 0; startByte <= [self length]; startByte += kChunkSizeBytes) 
            if ((startByte + kChunkSizeBytes) > [self length]) 
                dataInLength = [self length] - startByte;
            
            else 
                dataInLength = kChunkSizeBytes;
            

            // Get the chunk to be ciphered from the input buffer
            bytesRange = NSMakeRange((NSUInteger) startByte, (NSUInteger) dataInLength);
            [self getBytes:dataIn range:bytesRange];
            cryptStatus = CCCryptorUpdate(cryptor, dataIn, dataInLength, dataOut, dataOutLength, &dataOutMoved);

            if (startByte >= 203728200) 
                NSLog(@"%ld",(long)startByte);
            
            if (dataOutMoved != dataOutLength) 
                NSLog(@"dataOutMoved (%d) != dataOutLength (%d)", dataOutMoved, dataOutLength);
            

            if ( cryptStatus != kCCSuccess)
            
                NSLog(@"Failed CCCryptorUpdate: %d", cryptStatus);
            

            // Write the ciphered buffer into the output buffer
            bytesRange = NSMakeRange(filePtr, (NSUInteger) dataOutMoved);
            [self replaceBytesInRange:bytesRange withBytes:dataOut];
            totalLength += dataOutMoved;

            filePtr += dataOutMoved;

    

    // Finalize encryption/decryption.
    cryptStatus = CCCryptorFinal(cryptor, dataOut, dataOutLength, &dataOutMoved);
    totalLength += dataOutMoved;

    if ( cryptStatus != kCCSuccess)
    
        NSLog(@"Failed CCCryptorFinal: %d", cryptStatus);
    

    // In the case of encryption, expand the buffer if it required some padding (an encrypted buffer will always be a multiple of 16).
    // In the case of decryption, truncate our buffer in case the encrypted buffer contained some padding
    [self setLength:totalLength];

    // Finalize the buffer with data from the CCCryptorFinal call
    NSRange bytesNewRange = NSMakeRange(filePtr, (NSUInteger) dataOutMoved);
    [self replaceBytesInRange:bytesNewRange withBytes:dataOut];

    CCCryptorRelease(cryptor);

    free(dataIn);
    free(dataOut);

    return 1;

@end

【问题讨论】:

当它崩溃时,上面代码中的什么时候它崩溃了?在那一点上似乎总是崩溃?另外,根据它崩溃的位置,您尝试过什么使其无法解决问题的工作? (这样可以省去别人尝试你已经尝试过的东西的麻烦)thx 看到它在 for 循环中崩溃,它永远不会出现。它每次都在循环中的不同点崩溃,比如有时 startByte 大约 200MB 有时大约 300MB,但它不会超过 315 你已经回答了它在输入文件中崩溃的地方;我在问崩溃时正在执行哪一行代码? 是内存问题吧?因此无法找到它崩溃的行,因为调试器因内存崩溃日志而停止 当然可以找到导致内存相关崩溃的代码行;我已经做过数百次了;您所做的是在代码中添加日志行,作为代码执行的跟踪;当程序崩溃时,您检查日志以查看执行流程,直到崩溃前记录的最后一件事;通常(但并非总是 - 取决于问题)这将是您代码中连续测试运行的同一点;你通常从几行调试日志开始,然后在崩溃的地方添加更多,直到你找到崩溃的确切行 【参考方案1】:

如果replaceBytesInRange:bytesRange 导致崩溃,那么我对如何避免崩溃的第一个建议是为它所依赖的前面的函数调用添加错误检查。

例如,在它崩溃的情况下,bytesRange 可能不是一个有效/可用的值。也许它的dataOut 无效/不可用。您问题中的代码从函数调用中设置这些值,但不检查错误条件/错误指示符/无效值的返回值。

它可能是一个相关的依赖函数调用。例如,cryptStatus 是通过调用 CCCryptorUpdate() 设置的,dataOut 作为输入参数。我不了解Objective-C,也不熟悉函数CCCryptorUpdate(),但看起来它会影响/填充dataOut。如果它实际上返回了一个错误,那么当您在 replaceBytesInRange 行上使用它时,dataOut 可能还没有处于可用状态。检查cryptStatus 的返回值可能会标记您应该在以后的调用中继续使用dataOut 的条件。

这让我想到了我注意到的另一件事:您确实检查了几件事,但只记录了它们。对if (dataOutMoved != dataOutLength)(cryptStatus != kCCSuccess) 的检查看起来应该停止执行,或者跳出循环,或者类似的东西,而不仅仅是记录事件。

我看到的另一件事是dataOutmalloc()'d 一次,没有清除,并重复使用。这在某些情况下可能是完全有效的,但它也可能导致您所看到的那种错误。您的代码中是否有任何关于dataOut 内容的假设?我正在考虑假设空终止的 C 字符串操作。如果一个人不小心,假设存在的空终止符(或者可能一开始实际上存在)可能会被覆盖,例如,如果整个缓冲区被填满。再说一次,我不太了解 Objective-C 和这些函数,所以这并不是一个具体的陈述,而是更多可能发生的种类的类比。 p>

TL; DR:为每个函数调用添加错误检查,并做出相应的响应(中断、退出、重试等),以便以后的函数调用不会尝试使用指示错误或无效的值。

我敢打赌,通过添加错误检查,您将 (1) 停止崩溃,以及 (2) 了解具体的为什么文件大于一定数量会导致这些崩溃。

【讨论】:

以上是关于iOS Common Crypto Decrypt RC4 加密视频文件内存崩溃的主要内容,如果未能解决你的问题,请参考以下文章

GeekGame 2020 Crypto简要出题思路

没有这样的模块 Common Crypto

crypto-js 与 php-mcrypt AES 加密/解密

Erlang crypto:stream_init在32位和64位系统上提供不同的密钥。如何使用?

数字信封例程:坏解密

Base64Utils