Расшифровка тайлов

Последнее изменение: 02/06/2020 13:36:32

Все тайлы, передаваемые сервером клиенту являются зашифрованными. Векторные и служебные типы данных (т.е. всё, кроме картинок) дополнительно запаковано в zlib архив. В таком же, оригинальном виде, тайлы сохраняются и в кэш клиента (клиенты версии 5.1 и выше при определённых настройках могут сохранять в кэш не оригинальные тайлы. Конкретно, это касается снимков: Формат снимков в кэше GE 5.1 и выше).

Расшифровка/Шифровка тайлов

Алгоритм шифрования GoogleEarth двунаправленный, это значит, что если ему на вход подать зашифрованный тайл, то после обработки мы получим его дешифрованный вариант (т.е. исходный, что был до шифровки), и аналогично, если мы подадим на вход алгоритму не зашифрованный тайл - на выходе получим его зашифрованный вариант.

Крипто-алгоритм в своей работе использует ключ, которым все данные и шифруются. Ключ этот передаётся клиенту в составе файла dbRoot.v5. Длина ключа в алгоритме - 1024 байта. Ключ для алгоритма получается путём добавления 8 байт заполненных нулями к ключу из dbRoot.v5 (в котором он занимает 1016 байт). Про извлечение ключа из dbRoot.v5 смотреть тут: Назначение и устройство файла dbRoot.v5.

За время существования сервиса, ключ ещё ни разу не изменялся. Вот его подготовленный для алгоритма вариант (добитый нулями до 1024 байт): key.bin

Пример алгоритма криптования:

Delphi code
  1. procedure CryptTile (Tile: Pointer; Size: integer);
  2. const
  3.   KeyLen = 1016;
  4. var
  5.   i,j: integer;
  6. begin
  7.   if size = 0 then
  8.     Exit;
  9.   j := 16;
  10.   for i := 0 to Size - 1 do
  11.   begin
  12.     Byte(Pointer(LongWord(Tile) + LongWord(i))^) :=
  13.       Byte(Pointer(LongWord(Tile) + LongWord(i))^) xor key[j+8];
  14.     inc(j);
  15.     if (j mod 8) = 0 then
  16.       j := j + 16;
  17.     if j >= KeyLen then
  18.       j := (j + 8) Mod 24;
  19.   end;
  20. end;

Perl code
  1. sub ge_crypt {
  2.   my $ref = $_[0]; #ссылка на тайл
  3.   my @data = unpack("C*", $$ref);
  4.   for (my ($i) = 0, my ($j) = 0x10; $i < length("$$ref"); $i++) {
  5.     $data[$i] ^= $key[$j+8]; $j++;
  6.     if ( ($j % 8) == 0 ) {
  7.       $j += 0x10
  8.     };
  9.     if ( $j >= 0x03f8 ) {
  10.       $j = ($j+8) % 24
  11.     };
  12.   }
  13.   return pack("C*", @data);
  14. }

Распаковка тайлов

Упакованные тайлы имеют следующую структуру:

Delphi code
  1. type
  2.   TPackZlib = packed record
  3.     Magic : LongWord;       // идентификатор архива 0xADDE6874
  4.     USize : LongWord;       // размер распакованного архива
  5.     Data  : array of byte;  // архив
  6.   end;

Алгоритм распаковки:

Delphi code
  1. uses zlib;
  2. ...
  3. function UnPackGEZlib(InPut: Pointer; InPutSize: integer;
  4.                         out OutPut: Pointer; out OutPutSize: integer): Boolean;
  5. const
  6.   DECRYPTED_ZLIB = $7468DEAD;
  7. begin
  8.   result := False;
  9.   if LongWord(InPut^) = DECRYPTED_ZLIB then
  10.   begin
  11.     OutPutSize := LongWord(Pointer(LongWord(InPut) + 4)^);
  12.     DecompressBuf(Pointer(LongWord(InPut) + 8), InPutSize - 8,
  13.                     OutPutSize, OutPut, OutPutSize);
  14.     result := True;
  15.   end
  16.   else
  17.   begin
  18.     OutPut := nil;
  19.     OutPutSize := 0;
  20.   end;
  21. end;

Perl code
  1. use Compress::Zlib;
  2. ...
  3. sub zlib_unpack {
  4.   my $ref = $_[0];             #ссылка на декриптованный тайл
  5.   my @data = unpack("C*", $$ref);
  6.   my @h = splice (@data,0,8);  #первые 8 байт -> Magic и Uncompress Size
  7.   my $magic = $h[3] + ($h[2] << 8) + ($h[1] << 16) + ($h[0] << 24);
  8.   my $uncompress_size = $h[4] + ($h[5] << 8) + ($h[6] << 16) + ($h[7] << 24);
  9.   if ($magic == 0xADDE6874) {
  10.     my $raw = pack("C*", @data);
  11.     return uncompress(\$raw);
  12.   }
  13. }

В итоге, после всех этих операций, мы получаем "сырые" данные с которыми и оперирует клиент.