Pset4(cs50)恢复无法正常工作。它编译,但不会恢复超过2 jpegs。检查JPEG签名有问题吗?

时间:2017-06-03 22:30:30

标签: c jpeg cs50

我正在学习如何编码,而我根本没有这方面的经验。我成功上了PSET4并坚持恢复。我已经在网上阅读了关于这个问题的所有内容,我发现许多人的代码与我一样,并且有效。对我来说不起作用。请看看并给我一个提示我做错了什么以及如何纠正它。 以下是关于pset4的一切recover我从这里下载了他们的card.raw card.raw

/** recovering JPEG files from a memory card
* 
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

typedef uint8_t BYTE;

int main(int argc, char* argv[])
{
    // ensure proper usage
    if (argc != 2)
    {
        fprintf(stderr,
            "Usage: ./recover infile (the name of a forensic image from which to recover JPEGs)\n");
        return 1;
    }

    // open input file (forensic image)
    FILE* inptr = fopen(argv[1], "r");
    if (inptr == NULL)
    {
        fprintf(stderr, "Could not open %s.\n", argv[1]);
        return 2;
    }

    FILE* outptr = NULL;

    // create a pointer array of 512 elements to store 512 bytes from the memory card
    BYTE* buffer = malloc(sizeof(BYTE) * 512);
    if (buffer == NULL)
    {
        return 3;
    }

    // count amount of jpeg files found
    int jpeg = 0;

    // string for a file name using sprintf
    char filename[8] = { 0 };

    // read memory card untill the end of file
    while (fread(buffer, sizeof(BYTE) * 512, 1, inptr) != 0)
    {
        // check if jpeg is found
        if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff
            && (buffer[3] >= 0xe0 || buffer[3] <= 0xef))
        {
            if (jpeg > 0)
            {
                fclose(outptr);
            }
            sprintf(filename, "%03d.JPEG", jpeg);
            outptr = fopen(filename, "w");
            jpeg++;
        }

        if (jpeg > 0)
        {
            fwrite(buffer, sizeof(BYTE) * 512, 1, outptr);
        }
    }

    // free memory
    free(buffer);

    // close filename
    fclose(outptr);

    // close input file (forensic image)
    fclose(inptr);

    return 0;
}

2 个答案:

答案 0 :(得分:1)

主要问题是您调用未定义的行为,因为filename不够大。 sprintf()需要9和17个字节的代码,但你只有8个。所以你有一个缓冲区溢出。

只需改变:

char filename[8] = { 0 };

char filename[17] = { 0 };

因为您使用的是int,所以此值已定义,但在许多系统中有{32}的int。因此,可能的值介于-2^312^31 - 1之间,最多可以生成11 char s(-2147483648)。我们在“.JPEG”,5中添加char s的数量。我们有16但是你忘记了c字符串的空终止字节。所以我们最多只有17岁。

现代编译器警告你:gcc版本7.1.1 20170516(GCC):

In function ‘main’:
  warning: ‘sprintf’ writing a terminating nul past the end of the destination [-Wformat-overflow ]
  sprintf(filename, "%03d.JPEG", jpeg++);
                              ^
note: ‘sprintf’ output between 9 and 17 bytes into a destination of size 8
  sprintf(filename, "%03d.JPEG", jpeg++);
  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

另外,你的typedef没用,因为char世界总是C中的一个字节。不仅如此,你不需要一个字节而是一个八位字节,就像char一样,{ {1}}将始终是C中的八位字节。因此您不需要uint8_t

还有一件事,你分配你的缓冲区,但它没用,因为你的缓冲区有一个恒定的大小。所以只需创建一个数组就更简单了。

typedef

注意:这个例子显然不是很完美,这样可以更好地为jpeg文件制作一个真正的解析器,以获得更好的控制流。在这里,我们认为一切都会正确。

答案 1 :(得分:0)

你怎么知道JPEG图像的实例总是以'\ n'结尾?或者更好的是,你怎么知道JPEG图像将是512的精确倍数?

你不知道。

因此,发布的代码需要计算实际值或使用某种方法对任何特定JPEG实例进行最后一次fread()调用,以停止在该图像末尾读取,

然后检查下一个JPEG图像的ID字节将找到下一个图像。

否则,下一个图像的开头已经写入先前的输出文件,并且检查新图像将失败。

通常,这将导致最后创建的文件包含多个图像。

此链接:“https://en.wikipedia.org/wiki/JPEG_File_Interchange_Format”是一个描述JPEG文件格式的网页。

在我使用的每台数码相机上,SD卡都有一个包含所有文件的目录。

建议使用该目录和链接网页中的信息查找每个JPEG图像,并确定何时遇到该图像的结尾。 (I.E. 0xFF 0xD9)