Delphi,如何使用BEncode得到info_hash?

Delphi, How to use BEncode to get info_hash?

我想使用 Delphi 获取 info_hash 个 *.torrent 文件。 已尝试 this BEncode decorder。 但是它在解码时会给出疯狂的字符。 Delphi 中还有其他可用的 BEncode 解码器吗?或者我做错了什么? 这是我的代码:

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;

我刚试过这个解码器,它工作正常。您不需要使用 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 之前检查 nil 对象,这是重言式,但通常它有效。

更新 1 获取字段之一的简单代码,'pieces' 并以十六进制显示。

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 并将其转换为字节。我只是用十六进制显示的,当然你可以用这些字节做进一步的处理。

注意:您将获得 LOADS 个十六进制值,这不是 MD5 散列或任何其他整个 torrent 的散列,它是每个数据块的散列函数序列,通常是 1 或 2 MB 的块。

更新 2

这个单元可以在较新版本的Delphi中使用,您需要做的就是将其中的所有字符串变量从'string'替换为'ANSIstring',只需Ctrl+ R - ':string' 替换为 ':ANSIstring'。

更新 3

好的,我终于明白了。这是计算 info_hash 并以十六进制显示它的过程,这需要更新版本的 Delphi。此外,将 IdGlobal 和 IdHashSHA 添加到 'uses' 部分。

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

您必须将 BENcode.pas 中的所有字符串替换为 ANSIstring,如更新 2 中所述。享受!