Delphi, Как использовать BEncode для получения info_hash?

Я хочу получить info_hash файла *.torrent с помощью Delphi. Пробовал декордер BEncode. Но это дает сумасшедшие символы при декодировании. Любой другой рабочий декодер BEncode в Delphi? Или что-то я делаю не так? Это мой код:

procedure TForm.Button1Click(Sender: TObject);
var
 be: TBEncoded;
 fs: tfilestream;
 op: string;
begin
 fs := tfilestream.Create('xx.torrent', fmOpenReadWrite);
 be := TBEncoded.Create(fs);

 be.Encode(be.ListData.Items[0].Data, op);
 showmessage(op);

 be.Encode(be.ListData.FindElement('info'), op);
 showmessage(op);
end;

person user3431569    schedule 08.12.2015    source источник
comment
Кажется, я наконец понял, что вы пытаетесь сделать с помощью этого кода. Фактически, переменная op в вашем коде действительно должна содержать информационную часть торрента, закодированную с помощью BEncode, это именно то, что вам нужно передать SHA1. В чем была проблема? сумасшедшие символы в «op» — это нормально, потому что они не декодируются, или я должен сказать: они декодируются, а затем кодируются обратно!   -  person Yuriy Afanasenkov    schedule 10.12.2015


Ответы (1)


Я только что попробовал этот декодер, он работает нормально. Вам не нужно было использовать процедуру Encode, ее цель (как видно из названия) — кодировать элементы обратно в BEncode. Это тестовая программа, которая показывает информацию о торрентах в TMemo:

procedure ShowDecoded(be: TBEncoded; indent: string='');
var i: Integer;
begin
  with form1.Memo1.Lines do
    case be.Format of
      befstring: Add(indent+be.StringData);
      befInteger: Add(indent+IntToStr(be.IntegerData));
      befList: begin
        Add(indent+'list');
        for i:=0 to be.ListData.Count-1 do
          ShowDecoded(be.ListData.Items[i].Data as TBEncoded,indent+'   ');
        Add(indent+'end of list');
      end;
      befDictionary: begin
        Add(indent+'dict');
        for i:=0 to be.ListData.Count-1 do begin
          Add(indent+'   '+be.ListData.Items[i].Header+'=');
          ShowDecoded(be.listData.Items[i].Data as TBEncoded,indent+'      ');
        end;
        Add(indent+'end of dict');
      end;
    end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var fs: TFileStream;
    be: TBEncoded;
    i: Integer;
begin
  if OpenDialog1.Execute then begin
    fs:=TFileStream.Create(OpenDialog1.FileName,fmOpenRead);
    try
      be:=TBEncoded.Create(fs);
      ShowDecoded(be);  
      be.Free;
    finally
      fs.Free;
    end;
  end;
end;

Это результат теста:

dict
   created by=
      uTorrent/3.4.3
   creation date=
      1439626950
   encoding=
      UTF-8
   info=
      dict
         length=
            1345178
         name=
            Алябьев А., Лист Ф. - Соловей - 1987.pdf
         piece length=
            16384
         pieces=
            )Lo.Î ’üXí»IÙçsáôt£ˆb›hŒˆ*Ð誺š¤/N7’`0âÓ†nË5&T€:V•Ìפ¯9¤Ý:¦J©Ï|Œ•A¥,¼R¯þ:H:X&…¢<¸º"2îV-vÀÖˆD†¨¬ß‰ƒ,ümjà?éÛoe¬r£{¨¾]•4òØžhô†›­¼AØBeJÕÌ4³·Œ‹¶ËAG— f„\pa
      end of dict
end of dict

Я бы внес некоторые изменения в модуль BEncode, там беспорядок: создание пустых исключений, небезопасное приведение: TBEncoded(object) вместо «object as TBEncoded», проверка нулевого объекта перед object.free, что является тавтологией, но в вообще работает.

Обновление 1. Простой код, который берет одно из полей, "кусочки" и отображает его в шестнадцатеричном формате.

procedure FindAndShowHash(be: TBEncoded);
var i: Integer;
  s: string;
  infoChunk, piecesChunk: TBencoded;
begin
  s:='';
  infoChunk:=be.ListData.FindElement('info') as TBencoded;
  piecesChunk:=infoChunk.ListData.FindElement('pieces') as TBencoded;
  for i:=1 to Length(piecesChunk.StringData) do
    s:=s+IntToHex(Byte(piecesChunk.StringData[i]),2);
  form1.Memo1.Lines.Add('Hash function:');
  form1.Memo1.Lines.Add(s);
end;

Как видите, мы обращаемся к StringData char по char и приводим его к типу Byte. Я просто показал его в шестнадцатеричном формате, конечно, вы можете использовать эти байты для дальнейшей обработки.

Остерегайтесь: вы получите МНОГО шестнадцатеричных значений, это не хэш MD5 или любой другой хэш ВСЕГО торрента, это последовательность хэш-функций для каждого фрагмента данных, обычно блоков по 1 или 2 МБ.

ОБНОВЛЕНИЕ 2

Этот модуль можно использовать в более новых версиях Delphi, все, что вам нужно сделать, это заменить в нем ВСЕ строковые переменные с 'string' на 'ANSIstring', просто с помощью Ctrl+R - ':string' заменить на ':ANSIstring'.

ОБНОВЛЕНИЕ 3

Хорошо, наконец я понял. Вот процедура, которая вычисляет info_hash и показывает его в шестнадцатеричном формате, для этого требуется более новая версия Delphi. Кроме того, добавьте IdGlobal и IdHashSHA в раздел «использует».

procedure makeInfoHash(be: TBEncoded);
var SHA1:  TIdHashSHA1;
    s: string;
    infoChunk: TBencoded;
    infoEncoded: ANSIString;
    bytes: TIdBytes;
begin
  infoChunk:=be.ListData.FindElement('info') as TBencoded;
  TBencoded.Encode(infoChunk,infoEncoded);
  bytes:=RawToBytes(infoEncoded[1],Length(infoEncoded));
  SHA1:=TIdHashSHA1.Create;
  try   
    s:=SHA1.HashBytesAsHex(bytes);
  finally
    SHA1.Free;
  end;
  Form1.Memo1.Lines.Add(s);
end;

Он дает правильный info_hash, такой же, который отображается в uTorrent, например:

7D0487D3D99D9C27A7C09CDCBB2F2A8034D4F9BF

Вы должны заменить все строки на ANSIString в BENcode.pas, как сказано в обновлении 2. Наслаждайтесь!

person Yuriy Afanasenkov    schedule 08.12.2015
comment
Спасибо. Мне удалось заставить ваш код работать только в Delphi 7. Он все еще не работает в Delphi XE7. Это не проблема. Но я играл с ним, чтобы получить info_hash, тогда я получаю неверный info_hash при вычислении SHA1. Я попытался удалить ненужный текст (Добавить (отступ + «список») и т. Д.). Может ли кто-нибудь показать мне, как получить info_hash, используя декодированные данные, пожалуйста? - person user3431569; 09.12.2015
comment
@user3431569 user3431569 Я обновил свой ответ, но до сих пор не понимаю, как именно вы пытались получить с ним info_hash. Это немного сложнее, см. там: stackoverflow.com/questions/28348678/ поэтому вам нужно несколько полей данных, а не только поле "кусков" торрента, а затем сделать из них хэш SHA1. - person Yuriy Afanasenkov; 09.12.2015
comment
@user3431569 user3431569 как именно это не работает в Delphi XE7? Вообще не компилируется, вызывает исключение или просто выдает неверные данные? Я думаю, что могут быть проблемы со строками в этом модуле BEncode. Предполагается, что строка имеет 1 байт на символ, что верно в delphi7, но в современных версиях строка на самом деле является строкой Unicode с 2 байтами на символ. Блок небольшой, его можно починить, но скажите сначала: что вы пытаетесь сделать? Создавать магнитные ссылки из торрентов? - person Yuriy Afanasenkov; 09.12.2015
comment
Большое спасибо. Это решило мою проблему. Я проверю это в XE7 и дам вам знать. Из-за этого я застрял в проекте на несколько недель. - person user3431569; 14.12.2015
comment
P.S.: Я действительно очень благодарен. Теперь все работает в XE7 - person user3431569; 15.12.2015