Search code examples
delphipascalendiannessmifare

Creating a function to reverse order a hex value as string


how to put this...

I have been struggling all day to reverse the UID value of an Mifare NFC card, the value i read as a hex string, which i can convert to a integer, this works very well with a 4 bytes UID.

Things gets messy when it comes to a mifare desfire card which can have a large UID example :

04 44 44 22 E0 62 80

The value is read correctly and i can even convert this to a decimal using my functions StrToInt('$' + TheUIDvalue)

now the weird part is that i need to reverse this using the following function:

function HexToInt(s: string): Longword;
var
  b: byte;
  c: Char;
begin
  Result := 0;
  s := UpperCase(s);
  for b := 1 to Length(s) do
  begin
    Result := Result * 16;
    c := s[b];
    case c of
      '0' .. '9':
        Inc(Result, Ord(c) - Ord('0'));
      'A' .. 'F':
        Inc(Result, Ord(c) - Ord('A') + 10);
    else
      begin
        Result := 0
      end;
    end;
  end;
end;

function LongEndian(L : UInt64) : UInt64;
begin
  result := ((L and $000000FF) shl 24) or
            ((L and $0000FF00) shl 8) or
            ((L and $00FF0000) shr 8) or
            ((L and $FF000000) shr 24);
end;

function TfrmMain.HexResponseReversed ( input: string): string;
begin
  result := IntToHex(EndianLong(hexToInt(input)));
end;



The result i get is : 
         80 62 E0 22
04 44 44 22 E0 62 80

So there is something seriously missing here... please excuse the messy code

I suspect there is something wrong with the IntToHex function


Solution

  • Your HexToInt() function returns a LongWord, so its output is dependant on the platform. For 64bit POSIX platforms like Linux and iOS, it is 8 bytes, for other platforms it is 4 bytes.

    Also, your LongEndian() function only swaps 4 bytes despite operating on an 8-byte UInt64.

    So, you should either:

    • fix those functions to operate on the full 8 bytes on all platforms, eg:

      function HexToInt64(s: string): UInt64;
      var
        Len, I: Integer;
        c: Char;
      begin
        Result := 0;
        Len := Length(s);
        if Len = 0 then Exit;
        if Odd(Len) then
        begin
          s := '0' + s;
          Inc(Len);
        end;
        if Len > 16 then Exit;
        for I := 1 to Len do
        begin
          Result := Result * 16;
          c := s[I];
          case c of
            '0' .. '9':
              Inc(Result, Ord(c) - Ord('0'));
            'A' .. 'F':
              Inc(Result, Ord(c) - Ord('A') + 10);
            'a' .. 'f':
              Inc(Result, Ord(c) - Ord('a') + 10);
          else
            begin
              Result := 0;
              Exit;
            end;
          end;
        end;
      end;
      
      function EndianLong(L : UInt64) : UInt64;
      begin
        Result := ((L and $00000000000000FF) shl 56) or
                  ((L and $000000000000FF00) shl 40) or
                  ((L and $0000000000FF0000) shl 24) or
                  ((L and $00000000FF000000) shl 8) or
                  ((L and $000000FF00000000) shr 8) or
                  ((L and $0000FF0000000000) shr 24) or
                  ((L and $00FF000000000000) shr 40) or
                  ((L and $FF00000000000000) shr 56);
      end;
      
      function TfrmMain.HexResponseReversed(input: string): string;
      begin
        Result := IntToHex(EndianLong(HexToInt64(input)));
      end;
      
    • decode the individual HH pairs of the hex string into a byte array, then swap the order of the array elements, and then create a new hex string from the array elements:

      uses
        ..., Classes;
      
      function HexToBytes(s: string): TBytes;
      var
        Len: Integer;
      begin
        Result := nil;
        Len := Length(s);
        if Len = 0 then Exit;
        if Odd(Len) then
        begin
          s := '0' + s;
          Inc(Len);
        end;
        Len := Len div 2;
        SetLength(Result, Len);
        if HexToBin(PChar(s), PAnsiChar(PByte(Result)), Len) <> Len then
          SetLength(Result, 0);
      end;
      
      function BytesToHex(Bytes: TBytes): string;
      var
        Len: Integer;
      begin
        Result := nil;
        Len := Length(Bytes);
        if Len = 0 then Exit;
        SetLength(Result, Len * 2);
        BinToHex(PAnsiChar(PByte(Bytes)), PChar(Result), Len);
      end;
      
      function EndianLong(Bytes : TBytes) : TBytes;
      var
        Len, I, J: Integer;
        B: Byte;
      begin
        Result := nil;
        Len := Length(Bytes); 
        if Len = 0 then Exit;
        Result := Copy(Bytes, 0, Len);
        if Len = 1 then Exit;
        J := Pred(Len);
        for I := 0 to Pred(Len div 2) do
        begin
          B := Result[I];
          Result[I] := Result[J];
          Result[J] := B;
          Dec(J);
        end;
      end;
      
      function TfrmMain.HexResponseReversed(input: string): string;
      begin
        Result := BytesToHex(EndianLong(HexToBytes(input)));
      end;
      

    That being said, a 3rd option would be to simply swap the HH pairs of the input string directly, without converting the whole input string to an integer or byte array at all:

    uses
      ..., SysUtils;
    
    function TfrmMain.HexResponseReversed(input: string): string;
    const
      HexDigits = ['0'..'9', 'A'..'F', 'a'..'f'];
    var
      Len, I: Integer;
      P, Q: PChar;
      Hex: array[0..1] of Char;
    begin
      Result := '';
      Len := Length(input);
      if Len = 0 then Exit;
      Result := input;
      if Odd(Len) then
      begin
        Result := '0' + Result;
        Inc(Len);
      end;
      Len := Len div 2;
      if Len = 1 then Exit;
      UniqueString(Result);
      P := PChar(Result);
      Q := AnsiLastChar(Result);
      Dec(Q);
      For I := 1 to Len div 2 do
      begin
        Move(P^, Hex, SizeOf(Hex));
        if not ((Hex[0] in HexDigits) and (Hex[1] in HexDigits)) then
        begin
          Result := '';
          Exit;
        end;
        Move(Q^, P^, SizeOf(Hex));
        Move(Hex, Q^, SizeOf(Hex));
        Inc(P, 2);
        Dec(Q, 2);
      end;
    end;