btparser/cparser/RARTemplate.bt

457 lines
9.9 KiB
Plaintext
Raw Normal View History

//-----------------------------------
//--- 010 Editor v2.0 Binary Template
//
// File: RARTemplate2.bt
// Author: Alexander Sherman
// Purpose: Defines a template for
// parsing RAR files.
// Revision: 7.03
//-----------------------------------
// RAR archive structures
// Based on TECHNOTE.TXT from the RAR archiver distribution
LittleEndian();
local const uint16 SignatureLen = 7;
const string RarSignature = "Rar!" + '\x1A' + '\x07';
///////////////
struct FileHeadBlock;
struct OldCommentBlock;
struct OldSubBlock;
struct SubBlock;
///////////////
enum <byte> RarBlockType
{
MARKER=0x72, ARCHIVE, FILE_OR_DIR, COMMENT_OLD, AV_OLD_1, SUBBLOCK_OLD, RR_OLD, AV_OLD_2, SUBBLOCK, _END_
};
////////////////
local uint16 _flags = 0;
/// info:
local uquad iBlocksCounter = 0;
local uquad iFiles = 0;
local uquad iDirs = 0;
local uquad iComments = 0;
local uquad iSubBlocks = 0;
local uquad iUniNames = 0;
local uquad iBadCRCCounter = 0;
local ubyte iMinVerToUnpack = 0xff;
local ubyte iMaxVerToUnpack = 0;
local uquad iTotalUnpackedSize = 0;
local uint iAV1 = 0;
local uint iAV2 = 0;
////////////////
enum <ubyte> OsType
{
_MS_DOS, _OS_2, _Win32, _Unix, _Mac_OS, _BeOS
};
enum <char> PackMethod
{
Store='0', Fastest, Fast, Normal, Good, Best
};
enum <uint16> SubType_OldStyle
{
OS2_EA = 0x0100
};
struct UnixStyleAttrs
{
uint32 owner_may_eXecute : 1;
uint32 owner_may_Write : 1;
uint32 owner_may_Read : 1;
uint32 group_may_eXecute : 1;
uint32 gorup_may_Write : 1;
uint32 group_may_Read : 1;
uint32 everybody_may_eXecute : 1;
uint32 everybody_may_Write : 1;
uint32 everybody_may_Read : 1;
uint32 _s : 1; // ??
uint32 _s : 1; // ??
uint32 _unused : 21;
};
struct DosFileAttrs
{
uint32 READONLY : 1; // 0x00000001
uint32 HIDDEN : 1; // 0x00000002
uint32 SYSTEM : 1; // 0x00000004
uint32 VOLUME : 1;
uint32 DIRECTORY : 1; // 0x00000010
uint32 ARCHIVE : 1; // 0x00000020
uint32 _res : 26;
};
struct WinFileAttrs
{
uint32 READONLY : 1;
uint32 HIDDEN : 1;
uint32 SYSTEM : 1;
uint32 VOLUME : 1;
uint32 DIRECTORY : 1;
uint32 ARCHIVE : 1;
uint32 DEVICE : 1;
uint32 NORMAL : 1;
uint32 TEMPORARY : 1;
uint32 SPARSE_FILE : 1;
uint32 REPARSE_POINT : 1;
uint32 COMPRESSED : 1;
uint32 OFFLINE : 1;
uint32 NOT_CONTENT_INDEXED : 1;
uint32 ENCRYPTED : 1;
uint32 _res2 : 17;
};
struct CommonBlockFlags
{
ushort _reserved : 14;
ushort OLD_VERSION_IGNORE : 1;
ushort ADD_SIZE_PRESENT : 1;
};
struct MainHeadFlags
{
ubyte ARCHIVE_VOLUME : 1;
ubyte ARCHIVE_COMMENT_PRESENT : 1;
ubyte ARCHIVE_LOCKED : 1;
ubyte ARCHIVE_SOLID : 1;
ubyte NEW_VOLUME_NAMING : 1;
ubyte AV_PRESENT : 1;
ubyte RECOVERY_PRESENT : 1;
ubyte BLOCK_HEADERS_ENCRYPTED : 1;
ubyte IS_FIRST_VOLUME : 1;
ubyte _reserved : 5;
ubyte OLD_VERSION_IGNORE : 1;
ubyte ADD_SIZE_PRESENT : 1;
};
enum <byte> FileDictType
{
_64K, _128K, _256K, _512K, _1024K, _2048K, _4096K, _Directory
};
struct FileHeadFlags
{
ubyte from_PREV_VOLUME : 1;
ubyte to_NEXT_VOLUME : 1;
ubyte PASSWORD_ENCRYPTED : 1;
ubyte FILE_COMMENT_PRESENT : 1;
ubyte SOLID : 1;
FileDictType DICTIONARY : 3;
ubyte HIGH_SIZE : 1;
ubyte has_UNICODE_FILENAME : 1;
ubyte ENCRYPTION_SALT : 1;
ubyte IS_OLD_FILE_VERSION : 1;
ubyte EXTENDED_TIME_INFO : 1;
ubyte _reserved : 1;
ubyte OLD_VERSION_IGNORE : 1;
ubyte ADD_SIZE_PRESENT : 1;
};
struct RarBlock
{
local quad iOfs = FTell();
uint16 HEAD_CRC <format=hex, fgcolor=cRed>;
RarBlockType HeadType <fgcolor=cGreen>;
_flags = ReadUShort(FTell());
if (HeadType == ARCHIVE)
MainHeadFlags HEAD_FLAGS;
else if (HeadType == FILE_OR_DIR)
FileHeadFlags HEAD_FLAGS;
else
CommonBlockFlags HEAD_FLAGS;
++iBlocksCounter;
if (HeadType < MARKER || HeadType > _END_)
{
Warning("Unknown Header Type (0x%02x) in Block #%Lu", HeadType, iBlocksCounter);
Printf("Unknown Header Type (0x%02x) in Block #%Lu\n", HeadType, iBlocksCounter);
}
uint16 HeaderSize;
if (HeaderSize < 7)
{
Warning("Invalid block size (%u) in Block #%Lu", HeaderSize, iBlocksCounter);
Printf("Invalid block size (%u) in Block #%Lu\n", HeaderSize, iBlocksCounter);
return -1;
}
if (HeadType != MARKER)
{
local uint16 crcCheck = Checksum(CHECKSUM_CRC32, startof(HeadType), HeaderSize-sizeof(HEAD_CRC)) & 0xFFFF;
if (crcCheck != HEAD_CRC)
{
Warning("Header CRC mismatch in Block #%Lu", iBlocksCounter);
Printf("Header CRC mismatch in Block #%Lu: expected CRC is 0x%X, got 0x%X\n", iBlocksCounter, crcCheck, HEAD_CRC);
++iBadCRCCounter;
}
}
if (HEAD_FLAGS.ADD_SIZE_PRESENT)
uint32 RawDataSize;
else
local uint32 RawDataSize = 0;
switch (HeadType) {
case ARCHIVE:
uint16 _reserved1;
uint32 _reserved2;
if (HEAD_FLAGS.ARCHIVE_COMMENT_PRESENT) struct RarBlock MainComment;
break;
case FILE_OR_DIR:
if (HEAD_FLAGS.DICTIONARY == 7)
{
++iDirs;
FileHeadBlock dir;
}
else
{
++iFiles;
FileHeadBlock file;
}
break;
case COMMENT_OLD:
OldCommentBlock cmm;
break;
case SUBBLOCK_OLD:
OldSubBlocksub;
break;
case SUBBLOCK:
SubBlock sub;
break;
case AV_OLD_1:
++iAV1;
Printf("*** AV was found (RAR v. < 2.60) @ block #%Lu.\n", iBlocksCounter);
break;
case AV_OLD_2:
++iAV2;
Printf("*** AV was found (RAR v. 2.60 - 2.9x) @ block #%Lu.\n", iBlocksCounter);
break;
}
iOfs = HeaderSize - (FTell() - iOfs);
if (iOfs > 0)
ubyte _reserved[iOfs];
if (RawDataSize > 0)
ubyte _raw[RawDataSize] <format=hex, fgcolor=cBlue>;
};
struct FileHeadBlock
{
uint32 UnpackedSize;
iTotalUnpackedSize += UnpackedSize;
OsType Host_OS;
uint32 FileCRC32 <format=hex>;
DOSTIME FileTime;
DOSDATE FileDate;
ubyte VersionToUnpack;
if (VersionToUnpack > iMaxVerToUnpack)
iMaxVerToUnpack = VersionToUnpack;
if (VersionToUnpack < iMinVerToUnpack)
iMinVerToUnpack = VersionToUnpack;
PackMethod Method;
uint16 NameSize;
switch (Host_OS) {
case _Win32:
WinFileAttrs Attributes;
break;
case _MS_DOS:
case _Mac_OS:
case _OS_2:
DosFileAttrs Attributes;
break;
case _Unix:
case _BeOS:
UnixStyleAttrs Attributes;
break;
default:
uint32 Attributes <format=binary>;
}
if (_flags & 0x0100)
{
uint32 HIGH_PACK_SIZE;
uint32 HIGH_UNP_SIZE;
iTotalUnpackSize += (HIGH_UNP_SIZE << 32);
}
if (_flags & 0x0200)
{
++iUniNames;
string FileName;
uint16 WideFileNameData[(NameSize-sizeof(FileName))/2];
}
else
char FileName[NameSize];
if (_flags & 0x0008)
{
RarBlock FileComment; // used in RAR v. <= 3.11
}
if (_flags & 0x0400)
uquad SALT <format=hex>;
};
/////////////////
struct OldCommentBlock {
++iComments;
uint16 UnpackedSize;
ubyte VersionToUnpack;
PackMethod Method;
uint16 CommentCRC <format=hex>;
Printf("*** Old style CommentBlock: (Block #%Lu)\n", iBlocksCounter);
};
struct OldSubBlock {
++iSubBlocks;
SubType_OldStyle SubType;
ubyte _reserved;
Printf("*** Old style SubBlock: %u (Block #%Lu)\n", SubType, iBlocksCounter);
};
struct SubBlock {
++iSubBlocks;
ubyte _unknown_to_me_1[15];
ubyte SubTypeLen;
ubyte _unknown_to_me_2[5];
char SubType[SubTypeLen];
Printf("*** SubBlock: %s (Block #%Lu)\n", SubType+'\0', iBlocksCounter);
switch (SubType) {
case "CMT":
++iComments;
break;
case "AV":
Printf("*** Authenticity Verification info (RAR v. 3.x) @ block #%Lu.\n", iBlocksCounter);
break;
case "RR":
Printf("*** Recovery Record was found (RAR v. 3.x) @ block #%Lu.\n", iBlocksCounter);
break;
}
};
/////////////////
local string fn = GetFileName();
local quad SignaturePos = 0;
local char Signature[SignatureLen];
if (Strstr(fn, ".rar") != Strlen(fn)-4)
{
Warning("Seeking for RAR signature...");
local quad _p = FindFirst(RarSignature);
if (_p >= 0)
FSeek(_p);
else
{
Warning("Not a RAR archive!");
return -1;
}
Warning("RAR signature found at 0x%08x.", _p);
Printf("RAR signature found at 0x%08x.\n", _p);
}
else
{
ReadBytes(Signature, SignaturePos, SignatureLen);
if (Strcmp(Signature, RarSignature))
{
Warning("Invalid RAR Archive Signature!");
return SignaturePos;
}
}
RarBlock Marker;
RarBlock ArcHeader;
if (ArcHeader.HeadType != ARCHIVE)
{
Warning("Main archive header is either bad or missing!");
return -2;
}
else
{
Printf("It is a %s%s %s %s RAR archive.\n",
SignaturePos > 0 ? "SelF-eXtractable " : "",
ArcHeader.HEAD_FLAGS.ARCHIVE_LOCKED ? "LOCKED" : "non-locked",
ArcHeader.HEAD_FLAGS.ARCHIVE_SOLID ? "SOLID" : "regular",
ArcHeader.HEAD_FLAGS.ARCHIVE_VOLUME ? "VOLUME'd" : "one-part");
if (ArcHeader.HEAD_FLAGS.ARCHIVE_COMMENT_PRESENT)
Printf("Main comment is present.\n");
if (ArcHeader.HEAD_FLAGS.AV_PRESENT)
Printf("Old style Authenticity Verification is present.\n");
if (ArcHeader.HEAD_FLAGS.RECOVERY_PRESENT)
Printf("Recovery Record is present.\n");
if (ArcHeader.HEAD_FLAGS.BLOCK_HEADERS_ENCRYPTED)
{
Printf("It's an encrypted archive. Cannot proceed, exiting...\n");
return -3;
}
}
while (!FEof())
{
RarBlock block;
}
if (block.HeadType != _END_ && iMaxVerToUnpack > 20)
{
Warning("END Marker block was expected here.");
}
if (iFiles || iDirs)
{
Printf("Version to unpack: %u.%u\n", iMaxVerToUnpack / 10, iMaxVerToUnpack % 10);
if (iMinVerToUnpack != iMaxVerToUnpack)
Printf("Some data can also be retrieved by an earlier version of %u.%u\n",
iMinVerToUnpack /10, iMinVerToUnpack %10);
}
Printf("Files: %Lu, Dirs: %Lu, Comments: %Lu, SubBlocks: %Lu, Unpacked Size: %Lu\n", iFiles, iDirs, iComments, iSubBlocks, iTotalUnpackedSize);
Printf("UNICODE Names: %Lu\n", iUniNames);
if (iBadCRCCounter)
Printf("%Lu blocks corrupted.\n", iBadCRCCounter);
Printf("Done. %Lu blocks processed.\n", iBlocksCounter);