简体   繁体   中英

Delphi, How to use BEncode to get info_hash?

I want to get info_hash of *.torrent file using Delphi. Tried this BEncode decorder . But it gives crazy characters when decode. Any other working BEncode decoder in Delphi? Or anything I'm doing wrong? This is my code:

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;

I've just tried this decoder, it's working normally. You didn't need to use Encode procedure, its purpose (as seen from name) is to encode elements back to BEncode. That's test program that shows torrent information in 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;

That's test result:

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

I'd make some changes to BEncode unit, there is mess in there: raising empty exceptions, unsafe cast: TBEncoded(object) instead of "object as TBEncoded", checking for nil object before object.free, which is tautology, but in general it works.

Update 1 Simple code to take one of the fields, 'pieces' and show in hex.

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;

As you see, we access StringData char by char and cast it as Byte. I just showed it in hex, of course you can use these bytes for further processing.

Beware: you'll get LOADS of hex values, this is not MD5 hash or any other hash of WHOLE torrent, it's sequence of hash functions for each piece of data, usually blocks of 1 or 2 MB.

UPDATE 2

This unit can be used in newer versions of Delphi, all you need to do is to replace ALL string variables in it from 'string' to 'ANSIstring', just with Ctrl+R - ':string' replace to ':ANSIstring'.

UPDATE 3

OK, finally I get it. Here is procedure which computes info_hash and shows it in hex, this requires newer version of Delphi. Also, add IdGlobal and IdHashSHA to 'uses' section.

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;

It gives correct info_hash, the same which is displayed in uTorrent, like this:

7D0487D3D99D9C27A7C09CDCBB2F2A8034D4F9BF

You must replace all string to ANSIstring in BENcode.pas, as said in update 2. Enjoy!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM