iOS通用加密解密RC4加密的视频文件内存崩溃

时间:2018-11-09 14:33:35

标签: ios objective-c cryptography commoncrypto

我正在解密一个视频文件,该文件非常适合小尺寸文件,但对于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

1 个答案:

答案 0 :(得分:0)

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

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

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

这使我想起另一件事:您检查了几件事,但只记录了它们。对if (dataOutMoved != dataOutLength)(cryptStatus != kCCSuccess)的检查看起来应该停止执行,或者退出循环,或者类似的检查,而不仅仅是记录发生的情况。

我看到的另一件事是dataOutmalloc()一次,没有被清除,并且被重复使用。在某些情况下,这可能是完全有效的,但也可能导致您所看到的错误。您的代码中是否有任何内容假设dataOut的内容?我正在考虑假定为空终止的C字符串操作。如果不小心,则假定例如整个缓冲区已满,则假定存在的空终止符(或者可能实际上是最初存在的空终止符)会被覆盖。同样,我不太了解Objective-C和这些功能,因此,这并不是专门的声明,而是更多地类似于可能发生的事情的种类

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

我敢打赌,通过添加错误检查,您将(1)停止崩溃,并且(2)了解有关为什么的一些特定知识,即文件大于一定数量会导致这些崩溃。