使用 PHP 提取通过 Java RandomAccessFile 创建的存档

问题描述 投票:0回答:1

我正在尝试重新创建一个丢失已久的 PHP 网站。该网站的其中一个页面允许员工上传由他们执行的本地脚本创建的存档文件。然后,网络服务器会将内容提取到单独的文件中,以便存储在不同的文件夹中以用于其他目的。

谢天谢地,我有创建档案的脚本,但它是用 Java 编写的。我想这可以逆转吗?他们使用的脚本基本上只是在多个文件路径上运行以下 addFile。

public class Archive {
    static void create(File f) throws IOException {
        BufferedOutputStream w = new BufferedOutputStream(new FileOutputStream(f));
        w.write(new byte[]{1, 3, 3, 7});
        w.write(new byte[4]);
        w.close();
    }

    static int addFile(File archive, File add, String name) throws IOException {
        if (!add.exists()) {
            throw new IOException("File to be added does not exist!");
        }
        if (add.isDirectory()) {
            throw new IOException("Cannot add directories!");
        }
        if (!archive.exists()) {
            Archive.create(archive);
        }
        if (archive.isDirectory()) {
            throw new IOException("Archive is no valid archive!");
        }
        RandomAccessFile r = new RandomAccessFile(archive, "rw");
        int code = r.readInt();
        if (code != 16974599) {
            throw new IOException("Archive is no valid archive!");
        }
        int fileCount = r.readInt();
        r.seek(4);
        r.writeInt(fileCount + 1);
        r.seek(r.length());
        RandomAccessFile bi = new RandomAccessFile(add, "r");
        r.writeInt((int)bi.length());
        r.writeBytes(name);
        r.write(0);
        byte[] swap = new byte[(int)bi.length()];
        bi.readFully(swap);
        r.write(swap);
        bi.close();
        r.close();
        return fileCount + 1;
    }

    public static void main(String[] args) throws IOException {
    }
}

更新:

我使用 fread() 创建了一个函数,但在第一个文件后它耗尽了内存。即内存限制暂时设置为 512mb。有替代方案吗?

java php reverse-engineering randomaccessfile
1个回答
0
投票

根据Java代码,文件格式如下:

  • 0x01030307(即十进制表示的 16974599)
  • 32 字节文件计数,小端
  • 文件 1 32 字节长度,小端
  • 文件 1 名称后跟 0x00
  • 文件1字节
  • ...
  • 文件 N 32 字节长度,小端
  • 文件 N 名称后跟 0x00
  • 文件N字节

它不是一种存档格式,而是文件与一些元数据的简单串联。

要从这样的“存档”中提取文件,我们可以使用如下 PHP 代码:

<?php
class MyArchiveHeader {
    public function __construct(
        private int $typeCode,
        private int $fileCount
    ) {}

    public function getTypeCode(): int
    {
        return $this->typeCode;
    }

    public function getFileCount(): int
    {
        return $this->fileCount;
    }
}

class MyArchiveFile {
    public function __construct(
        private string $filename,
        private string $contents
    ) {}

    public function getFilename(): string
    {
        return $this->filename;
    }

    public function getContents(): string
    {
        return $this->contents;
    }
}

class MyArchive {

    public function __construct(private string $filename) {}

    public function extractFiles(string $outputDirectory): void
    {
        if (!is_dir($outputDirectory)) {
            throw new \InvalidArgumentException('Output directory does not exist');
        }

        $file = new \SplFileObject($this->filename, 'rb');

        $header = $this->parseHeader($file);

        $fileCount = $header->getFileCount();
        for ($i = 0; $i < $fileCount; $i++) {
            $parsedFile = $this->parseFile($file);

            $outputFilename = $outputDirectory . DIRECTORY_SEPARATOR . $parsedFile->getFilename();
            file_put_contents($outputFilename, $parsedFile->getContents());
        }
    }

    private function parseHeader(\SplFileObject $file): MyArchiveHeader
    {
        $typeCodeBytes = $file->fread(4);
        if ($typeCodeBytes === false) {
            throw new \RuntimeException('Could not read file type code');
        }

        $typeCode = unpack('V', $typeCodeBytes)[1]; // Unpack 4 bytes as unsigned integer
        if ($typeCode !== 0x01030307) {
            throw new \RuntimeException('Invalid file type code');
        }

        $fileCountBytes = $file->fread(4);
        if ($fileCountBytes === false) {
            throw new \RuntimeException('Could not read file count');
        }

        $fileCount = unpack('V', $fileCountBytes)[1]; // Unpack 4 bytes as unsigned integer

        return new MyArchiveHeader($typeCode, $fileCount);
    }

    private function parseFile(\SplFileObject $file): MyArchiveFile
    {
        $fileLengthBytes = $file->fread(4);
        if ($fileLengthBytes === false) {
            throw new \RuntimeException('Could not read file length');
        }

        $fileLength = unpack('V', $fileLengthBytes)[1]; // Unpack 4 bytes as unsigned integer

        $filename = "";
        while (!$file->eof()) {
            $char = $file->fread(1);
            if ($char === "\0") {
                break;
            }
            $filename .= $char;
        }

        // TODO Might need to convert $filename to UTF-8, for instance.

        $contents = $file->fread($fileLength);
        if ($contents === false) {
            throw new \RuntimeException('Could not read file contents');
        }

        return new MyArchiveFile($filename, $contents);
    }
}

我还没有测试代码,但它应该给你一个很好的起点。

© www.soinside.com 2019 - 2024. All rights reserved.