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

Pset4 (cs50) recover does not work properly. It compiles, but does not recover more than 2 jpegs. Is something wrong with checking for JPEG signature?

我正在学习如何编码,但我完全没有这方面的经验。我已经成功进入 PSET4 并坚持恢复。我已经在网上阅读了关于这个问题的所有内容,我发现很多人都有与我相似的代码并且它有效。对我不起作用。请看一下并提示我做错了什么以及如何纠正。 这是关于 pset4 的所有内容 recover i downloaded their card.raw from here 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;
}

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

只需更改:

char filename[8] = { 0 };

char filename[17] = { 0 };

因为,你使用了一个int,这个值是实现定义的,但是在很多系统中有一个32位的int。所以可能的值在 -2^312^31 - 1 之间,最大为 11 chars (-2147483648)。我们在“.JPEG”中添加 chars 的数量,5。我们有 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 , uint8_t 在 C 中总是一个八位字节。所以你不需要 typedef.

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

#include <stdint.h>
#include <stdio.h>

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

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

  FILE *outptr = NULL;
  uint8_t buffer[512];
  size_t const buffer_size = sizeof buffer / sizeof *buffer;
  size_t jpeg = 0;
  while (fread(buffer, sizeof *buffer, buffer_size, inptr) == buffer_size) {
    if (buffer[0] == 0xff && buffer[1] == 0xd8 && buffer[2] == 0xff &&
        buffer[3] == 0xe0) {
      if (outptr != NULL) {
        fclose(outptr);
      }

      char filename[26];
      sprintf(filename, "%03zu.JPEG", jpeg++);
      outptr = fopen(filename, "w");
    }
    if (outptr != NULL) {
      fwrite(buffer, sizeof *buffer, buffer_size, outptr);
    }
  }

  if (outptr != NULL) {
    fwrite(buffer, sizeof *buffer, buffer_size, outptr);
  }

  if (outptr != NULL) {
    fclose(outptr);
  }

  fclose(inptr);
}

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

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

你不知道。

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

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

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

一般来说,这会导致最后创建的文件包含多个图像。

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

我用过的每一台数码相机,SD卡上都有一个目录,里面有所有的文件。

建议使用该目录和 linked 网页中的信息来查找每个 JPEG 图像并确定何时遇到该图像的末尾。 (即 0xFF 0xD9)