0

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 think I understood at last what you're trying to do with this code. In fact, 'op' variable in your code should indeed contain 'info' part of torrent, encoded with BEncode, it's exactly what you need to feed to SHA1. What was the problem? crazy characters in 'op' is OK because it's not decoded, or I should say: it's decoded and then encoded back! – Yuriy Afanasenkov Dec 09 '15 at 21:30

1 Answers1

3

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!

Yuriy Afanasenkov
  • 1,440
  • 8
  • 12
  • Thank you. I was managed to get you code works only in Delphi 7. It still not works in Delphi XE7. That's not a problem. But I was playing with it to get info_hash, then I get incorrect info_hash when calculate SHA1. I tried removing unnecessary text (Add(indent+'list'), etc). Can anyone show me how to get info_hash using the decoded data please? – user3431569 Dec 09 '15 at 10:56
  • @user3431569 I updated my answer but still don't understand how exactly you tried to get info_hash with it. It's a bit more difficult, see there: http://stackoverflow.com/questions/28348678/what-exactly-is-the-info-hash-in-a-torrent-file so you need several data fields, not only 'pieces' field of torrent and then make SHA1 hash from them. – Yuriy Afanasenkov Dec 09 '15 at 18:12
  • @user3431569 how exactly it doesn't work in Delphi XE7? Doesn't compile at all, raises exception or just giving incorrect data? I think there could be problems with strings in this BEncode unit. It presumed that string has 1 byte per char which is true in delphi7, but in modern versions string is in fact unicode string with 2 bytes per char. Unit is small, it could be fixed, but tell at first: what are you trying to do? Create magnet links from torrents? – Yuriy Afanasenkov Dec 09 '15 at 18:15
  • Thanks a lot. This solved my problem. I will check it in XE7 and let you know. I was stucked in a project for weeks because of this. – user3431569 Dec 14 '15 at 09:12
  • P.S: I'm really really thankful. Everything works now in XE7 – user3431569 Dec 15 '15 at 04:07