2014-01-13 3 views
1

Я нашел this Delphi экзамен. Предполагается встроить CRC и проверить текущий CRC. Оба должны совпадать, но я получаю разные результаты. Как это исправить? И как его ускорить?Почему встроенный CRC и текущий CRC отличаются?

CRC32Calc.pas

unit CRC32Calc; 

interface 

uses Classes, SysUtils, windows, messages; 

type 
    Long = record 
    LoWord: Word; 
    HiWord: Word; 
    end; 

const 
    CRCPOLY = $EDB88320; 

procedure BuildCRCTable; 
function RecountCRC(b: byte; CrcOld: LongWord): LongWord; 
function GetCRC32(FileName: string; Full: boolean): string; 

function SetEmbeddedCRC(FileName: string): string; 
function GetEmbeddedCRC(FileName: string): string; 

function BytesToHexStr(pB: PByte; BufSize: LongWord): String; 
function HexStrToBytes(Str: String): String; 

implementation 

var 
    CRCTable: array [0 .. 512] Of LongWord; 

    // A helper routine that creates and initializes 
    // the lookup table that is used when calculating a CRC polynomial 
procedure BuildCRCTable; 
var 
    i, j: Word; 
    r: LongWord; 
begin 
    FillChar(CRCTable, SizeOf(CRCTable), 0); 
    for i := 0 to 255 do 
    begin 
    r := i shl 1; 
    for j := 8 downto 0 do 
     if (r and 1) <> 0 then 
     r := (r Shr 1) xor CRCPOLY 
     else 
     r := r shr 1; 
    CRCTable[i] := r; 
    end; 
end; 

// A helper routine that recalculates polynomial relative to the specified byte 
function RecountCRC(b: byte; CrcOld: LongWord): LongWord; 
begin 
    RecountCRC := CRCTable[byte(CrcOld xor LongWord(b)) 
    ] xor ((CrcOld shr 8) and $00FFFFFF) 
end; 

// A helper routine that converts Word into String 
function HextW(w: Word): string; 
const 
    h: array [0 .. 15] Of char = 'ABCDEF'; 
begin 
    HextW := ''; 
    HextW := h[Hi(w) shr 4] + h[Hi(w) and $F] + h[Lo(w) shr 4] + h[Lo(w) and $F]; 
end; 

// A helper routine that converts LongWord into String 
function HextL(l: LongWord): string; 
begin 
    with Long(l) do 
    HextL := HextW(HiWord) + HextW(LoWord); 
end; 

// Calculate CRC32 checksum for the specified file 
function GetCRC32(FileName: string; Full: boolean): string; 
var 
    f: TFileStream; 
    i, CRC: LongWord; 
    aBt: byte; 
begin 
    // Build a CRC table 
    BuildCRCTable; 

    CRC := $FFFFFFFF; 
    // Open the file 
    f := TFileStream.Create(FileName, (fmOpenRead or fmShareDenyNone)); 

    // To calculate CRC for the whole file use this loop boundaries 
    if Full then 
    for i := 0 to f.Size - 1 do 
    begin 
     f.Read(aBt, 1); 
     CRC := RecountCRC(aBt, CRC); 
    end 
    else 
    // To calculate CRC for the file excluding the last 4 bytes 
    // use these loop boundaries 
    for i := 0 to f.Size - 5 do 
    begin 
     f.Read(aBt, 1); 
     CRC := RecountCRC(aBt, CRC); 
    end; 

    f.Destroy; 
    CRC := Not CRC; 

    Result := HextL(CRC); 
end; 

// Calculate CRC and writes it to the end of file 
function SetEmbeddedCRC(FileName: string): string; 
var 
    f: TFileStream; 
    CRCOffset: LongWord; 
    CRC: string; 
begin 
    f := TFileStream.Create(FileName, (fmOpenReadWrite or fmShareDenyNone)); 
    CRCOffset := f.Size; 

    // Append a placeholder for actual CRC to the file 
    f.Seek(CRCOffset, TSeekOrigin.soBeginning); 
    f.Write(PByte(HexStrToBytes('FFFFFFFF'))^, 4); 

    // Obtain CRC 
    CRC := GetCRC32(FileName, True); 

    // Write CRC to the end of file 
    f.Seek(CRCOffset, TSeekOrigin.soBeginning); 
    f.Write(PByte(HexStrToBytes(CRC))^, 4); 
    f.Destroy; 
    Result := CRC; 
end; 

// Extract the CRC that was stored at last 4 bytes of a file 
function GetEmbeddedCRC(FileName: string): string; 
var 
    f: TFileStream; 
    CRCOffset: LongWord; 
    pB: PByte; 
begin 
    GetMem(pB, 4); 

    // Open file 
    f := TFileStream.Create(FileName, (fmOpenRead or fmShareDenyNone)); 

    // Proceed upto the end of file 
    CRCOffset := f.Size - 4; 
    f.Seek(CRCOffset, TSeekOrigin.soBeginning); 

    // Read the last four bytes where the CRC is stored 
    f.Read(pB^, 4); 
    f.Destroy; 
    Result := BytesToHexStr(pB, 4); 
end; 

// A helper routine that converts byte value to string with hexadecimal integer 
function BytesToHexStr(pB: PByte; BufSize: LongWord): String; 
var 
    i, j, b: LongWord; 
begin 
    SetLength(Result, 2 * BufSize); 

    for i := 1 to BufSize do 
    begin 
    for j := 0 to 1 do 
    begin 
     if j = 1 then 
     b := pB^ div 16 
     else 
     b := pB^ - (pB^ div 16) * 16; 
     case b of 
     0: 
      Result[2 * i - j] := '0'; 
     1: 
      Result[2 * i - j] := '1'; 
     2: 
      Result[2 * i - j] := '2'; 
     3: 
      Result[2 * i - j] := '3'; 
     4: 
      Result[2 * i - j] := '4'; 
     5: 
      Result[2 * i - j] := '5'; 
     6: 
      Result[2 * i - j] := '6'; 
     7: 
      Result[2 * i - j] := '7'; 
     8: 
      Result[2 * i - j] := '8'; 
     9: 
      Result[2 * i - j] := '9'; 
     10: 
      Result[2 * i - j] := 'A'; 
     11: 
      Result[2 * i - j] := 'B'; 
     12: 
      Result[2 * i - j] := 'C'; 
     13: 
      Result[2 * i - j] := 'D'; 
     14: 
      Result[2 * i - j] := 'E'; 
     15: 
      Result[2 * i - j] := 'F'; 
     end; 
    end; 

    Inc(pB); 
    end; 
end; 

// A helper routine that converts string with hexadecimal integer to byte value 
function HexStrToBytes(Str: String): String; 
var 
    b, b2: byte; 
    lw, lw2, lw3: LongWord; 
begin 
    lw := Length(Str) div 2; 
    SetLength(Result, lw); 

    for lw2 := 1 to lw do 
    begin 
    b := 0; 

    for lw3 := 0 to 1 do 
    begin 
     case Str[2 * lw2 - lw3] of 
     '0': 
      b2 := 0; 
     '1': 
      b2 := 1; 
     '2': 
      b2 := 2; 
     '3': 
      b2 := 3; 
     '4': 
      b2 := 4; 
     '5': 
      b2 := 5; 
     '6': 
      b2 := 6; 
     '7': 
      b2 := 7; 
     '8': 
      b2 := 8; 
     '9': 
      b2 := 9; 
     'a': 
      b2 := 10; 
     'b': 
      b2 := 11; 
     'c': 
      b2 := 12; 
     'd': 
      b2 := 13; 
     'e': 
      b2 := 14; 
     'f': 
      b2 := 15; 
     'A': 
      b2 := 10; 
     'B': 
      b2 := 11; 
     'C': 
      b2 := 12; 
     'D': 
      b2 := 13; 
     'E': 
      b2 := 14; 
     'F': 
      b2 := 15; 
     else 
     b2 := 0; 
     end; 

     if lw3 = 0 then 
     b := b2 
     else 
     b := b + 16 * b2; 
    end; 

    Result[lw2] := char(b); 
    end; 
end; 

end. 

AppendCRC

program AppendCRC; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils, Classes, 
    CRC32Calc in '..\CRC32Checker\CRC32Calc.pas'; 

var 
    FileName: string; 

begin 
    { TODO -oUser -cConsole Main : Insert code here } 
    if ParamCount = 1 then 
    begin 
    FileName := ParamStr(1); 
    // Verify whether a file exists 
    if not FileExists(FileName) then 
    begin 
     WriteLn('The specified file does not exist.'); 
     Exit; 
    end; 
    WriteLn('Full checksum (before): ' + GetCRC32(FileName, True)); 
    SetEmbeddedCRC(FileName); 
    WriteLn('Half checksum: ' + GetCRC32(FileName, False)); 
    WriteLn('Full checksum (after): ' + GetCRC32(FileName, True)); 
    WriteLn('GetEmbeddedCRC: :' + GetEmbeddedCRC(FileName)); 
    WriteLn('The checksum was successfully embedded.') 
    end 
    else 
    begin; 
    WriteLn('Wrong parameters.'); 
    WriteLn('Parameter1 - Full path to file.');; 
    end; 

end. 

Мои результаты:

AppendCRC.exe Hello_Delphi_World.exe 
Full checksum (before): 1912DA64 
Half checksum: 1912DA64 
Full checksum (after): B3F0A43E 
GetEmbeddedCRC: :4400A000 
The checksum was successfully embedded. 

Я использую Delphi xe5.

+0

Предлагаю вам сделать хороший вопрос, разместив здесь соответствующие части кода. StackOverflow предназначен для получения ответов и вопросов на сайте, а не в какой-либо внешней ссылке, которая может исчезнуть в любое время. И попросить людей решить проблему с загрузкой ZIP уменьшит ваши шансы получить достойный ответ. –

ответ

1

Вы должны понимать, как работает этот код. Общая идея состоит в том, чтобы добавить CRC в виде дополнительных 4 байтов из структуры EXE в конец файла. (Лучше всего было бы поставить CRC в специальное поле внутри EXE Header в начале).

Однако это вызывает проблему с курицей и яйцом: после вычисления CRC и ее вставки - файл CRC изменяется (добавляется значение CRC) и изменяется CRC измененных файлов.

Итак, вы в основном должны реализовать два режима/функции вычисления CRC: для всего файла и для файла без последних 4 байтов. Вы должны использовать последний режим для вычисления CRC после добавления (вы называете его вложением), а первый - для вычисления CRC перед ним на только что скомпилированной программе vanilla.

Функция GetCRC32 всегда вырезает последние 4 байта из файла, поэтому перед ее внедрением вычисляет CRC только в некоторой части файла, а не всего файла. Но там должно быть два разных режима.

PS: вы также можете «встроить» CRC в альтернативный поток NTFS, например, с программой MyApp.exe и CRC, хранящейся как MyApp.exe:CRC.

PPS. я думаю, что использование небуферизованного байта чтения байтом в GetCRC32 должно быть очень медленным. Если возможно, лучше используйте TBytesStream для чтения файла в памяти целиком, а затем сканирования в обычном цикле над массивом. Или прочитайте его кусками в 4096 байт, а не байтовыми переменными. Для последнего неполного буфера вы, например, очистите остальную часть буфера нулями.

+0

Я бы изменил SetEmbeddedCRC, чтобы не только вычислить и добавить CRC в конце файла, но также добавить маркер (или подпись), чтобы GetEmbeddedCRC и SetEmbeddedCRC узнали, что в файле есть или нет CRC. Если уже есть CRC, он будет пропущен при вычислении CRC. Подпись CRC может быть еще 4 байта с CRC CRC. Общий байт, добавленный в файл, составляет 8 байтов. – fpiette

+0

В чем смысл? почему бы не сделать в режимах GetCRC32, с или без последних байтов, проигнорированных? –

+0

Спасибо за ваше предложение, но NTFS Alternate Stream - это не то, что я хочу. –