Pull to refresh

Совершенно секретно или статичный IP на Windows Phone

Reading time7 min
Views5.9K
Понадобилось мне недавно сделать статичным IP на своем Windows Phone.
Пошел я искать решение в гугл. Все, что я там нашел, было связано непосредственно с настройкой роутера или полностью рутованными телефонами (мой рут накрылся после обновления до Tango), поэтому данное решение мне также не подошло. И вот, в голову пришла гениальная идея — а что если ребята из Samsung уже потрудились, однако сообщить об этом забыли? Так неоднократно было с меню Diagnosis. Напомню, через дыру в него создавали полноценный рут, запускали exe файлы без подписей и многое-многое другое. В прошлом. Снова гуглим, но это ничего не дает — никто толком не знает рабочих кодов диагностики. Хорошо. Ищем xap меню диагностики. На удивление, нашел, т.к. обычно они внедрены глубоко в ядро. Ядро было написано на нативном коде, однако интерфейс — на C#. То, что нужно! Нашел, правда, в кэше китайского ресурса на последней странице, но не суть.
Что же, в путь! Открываем любимый dotPeek от JetBrains и видим следующую картину:
image

Те пункты, в которых деревья отказались открываться — это dll, содержащие нативный код. Их сразу отбросим.
Отбросим также и те, которые начинаются со слов Microsoft. Это лишь ссылки. Остался первый проект — DiagnosisApp.
Разворачиваем и смотрим пространства имен, корневую и главную:
image

В корневой видим, что тут постарался DotObfuscator, что сильно усложняет процесс: лишние goto, пустые условия, циклы, однобуквенные названия переменных…
В DiagnosisApp уже интереснее. Видим тут класс, отвечающий за сам код страницы. Вся логика, в лучших традициях быдлокода, лежит не в ViewModel, а прямо в классе. Без всяких там partial. В начале класса видим переменные типа Button. То, что нужно! Это ведь панель набора кода:
internal Button btnDelete;
internal Button btn1;
internal Button btn3;
internal Button btn2;
internal Button btn4;
internal Button btn5;
internal Button btn6;
internal Button btn7;
internal Button btn8;
internal Button btn9;
internal Button btnSharp;
internal Button btn0;
internal Button btnStar;


Ищем событие Click:
this.btn1.Click += new RoutedEventHandler(this.b);
this.btn2.Click += new RoutedEventHandler(this.b);
this.btn3.Click += new RoutedEventHandler(this.b);
this.btn4.Click += new RoutedEventHandler(this.b);
this.btn5.Click += new RoutedEventHandler(this.b);
this.btn6.Click += new RoutedEventHandler(this.b);
this.btn7.Click += new RoutedEventHandler(this.b);
this.btn8.Click += new RoutedEventHandler(this.b);
this.btn9.Click += new RoutedEventHandler(this.b);
this.btn0.Click += new RoutedEventHandler(this.b);
this.btnStar.Click += new RoutedEventHandler(this.b);
this.btnSharp.Click += new RoutedEventHandler(this.b);


Ведет оно в никуда, лишь управление кнопкой delete и вибрацией.

private void b(object A_0, RoutedEventArgs A_1)
{
  if (1 == 0)
    ;
label_2:
  this.timer_btn.Stop();
  this.timer_btn.Start();
  this.vibrate.Start(TimeSpan.FromSeconds(0.05));
  this.e((string) ((ContentControl) A_0).Content);
  bool flag = this.txtDial.Text.Length <= 0;
  int num = 1;
  while (true)
  {
    switch (num)
    {
      case 0:
        this.btnDelete.Visibility = Visibility.Visible;
        num = 2;
        continue;
      case 1:
        if (!flag)
        {
          num = 0;
          continue;
        }
        else
          goto label_7;
      case 2:
        goto label_5;
      default:
        goto label_2;
    }
  }
label_5:
  return;
label_7:;
}


Что же, и слава богу, разбираться в этом лучше и не начинать…
Казалось бы, тупик, однако оказывается, что всем управляет таймер, включающийся после каждого нажатия кнопки:

this.timer_btn.Tick += new EventHandler(this.ParseDial);

public void ParseDial(object sender, EventArgs e)
{
  if (1 == 0)
    ;
  this.timer_btn.Stop();
  this.GetEnumFromList(this.f(this.txtDial.Text));
}

public void GetEnumFromList(uint hashCode)
{
label_2:
  int A_0 = 0;
  int num = 12;
  bool flag;
  while (true)
  {
    switch (num)
    {
      case 0:
      case 16:
        num = 3;
        continue;
      case 1:
        if (!flag)
        {
          num = 10;
          continue;
        }
        else
        {
          flag = Convert.ToBoolean(this.iNVBlock);
          num = 14;
          continue;
        }
      case 2:
        this.TitleBrush.Color = Color.FromArgb(byte.MaxValue, (byte) 0, (byte) 80, byte.MaxValue);
        this.textBlockPageTitle.Foreground = (Brush) this.TitleBrush;
        this.NavigationService.Navigate(new Uri(string.Format(HiddenKeyTable.Current.hashTable[A_0].strPage, (object) HiddenKeyTable.Current.hashTable[A_0].iPageMode, (object) HiddenKeyTable.Current.hashTable[A_0].iHashcode), UriKind.Relative));
        num = 9;
        continue;
      case 3:
      case 9:
      case 15:
        num = 4;
        continue;
      case 4:
        if (1 == 0)
          ;
        ++A_0;
        num = 17;
        continue;
      case 5:
        if (flag)
        {
          this.TitleBrush.Color = Color.FromArgb(byte.MaxValue, byte.MaxValue, (byte) 0, (byte) 0);
          this.textBlockPageTitle.Foreground = (Brush) this.TitleBrush;
          num = 0;
          continue;
        }
        else
        {
          num = 8;
          continue;
        }
      case 6:
        if (!flag)
        {
          num = 13;
          continue;
        }
        else
          goto case 4;
      case 7:
        if (!flag)
        {
          num = 11;
          continue;
        }
        else
        {
          flag = HiddenKeyTable.Current.hashTable[A_0].iHash.CompareTo(hashCode) != 0;
          num = 6;
          continue;
        }
      case 8:
        this.TitleBrush.Color = Color.FromArgb(byte.MaxValue, byte.MaxValue, (byte) 127, (byte) 0);
        this.textBlockPageTitle.Foreground = (Brush) this.TitleBrush;
        this.NavigationService.Navigate(new Uri(string.Format(HiddenKeyTable.Current.hashTable[A_0].strPage, (object) HiddenKeyTable.Current.hashTable[A_0].iPageMode), UriKind.Relative));
        num = 16;
        continue;
      case 10:
        this.NavigationService.Navigate(new Uri(string.Format(HiddenKeyTable.Current.hashTable[A_0].strPage, (object) HiddenKeyTable.Current.hashTable[A_0].iPageMode, (object) HiddenKeyTable.Current.hashTable[A_0].iHashcode), UriKind.Relative));
        num = 15;
        continue;
      case 11:
        goto label_26;
      case 12:
      case 17:
        flag = A_0 < this.length;
        num = 7;
        continue;
      case 13:
        this.HybridInterface_FCRProxy.w(out this.iNVBlock);
        flag = !this.g(A_0);
        num = 1;
        continue;
      case 14:
        if (flag)
        {
          this.TitleBrush.Color = Color.FromArgb(byte.MaxValue, byte.MaxValue, (byte) 127, (byte) 0);
          this.textBlockPageTitle.Foreground = (Brush) this.TitleBrush;
          flag = this.d();
          num = 5;
          continue;
        }
        else
        {
          num = 2;
          continue;
        }
      default:
        goto label_2;
    }
  }
label_26:;
}


Господи, чтобы разобраться в этом коде нужно много алкоголя… Удаляем все, кроме навигации на страницы:

this.NavigationService.Navigate(new Uri(string.Format(HiddenKeyTable.Current.hashTable[A_0].strPage, (object) HiddenKeyTable.Current.hashTable[A_0].iPageMode, (object) HiddenKeyTable.Current.hashTable[A_0].iHashcode), UriKind.Relative));
this.NavigationService.Navigate(new Uri(string.Format(HiddenKeyTable.Current.hashTable[A_0].strPage, (object) HiddenKeyTable.Current.hashTable[A_0].iPageMode), UriKind.Relative));
this.NavigationService.Navigate(new Uri(string.Format(HiddenKeyTable.Current.hashTable[A_0].strPage, (object) HiddenKeyTable.Current.hashTable[A_0].iPageMode, (object) HiddenKeyTable.Current.hashTable[A_0].iHashcode), UriKind.Relative));


Уже лучше. Тут видно, что навигация идет через некую таблицу HiddenKeyTable, а в качестве ключа туда передается результат некой функции f(A_0), которая является хэшем введенного кода. Смотрим код функции:

private uint f(string A_0)
{
  switch (0) // Этот switch пуст, единственная ветвь в нем - default, можно смело убирать
  {
    default: // Это тоже
label_2: // Бесполезный label
      uint num1 = 0U;
      int index = 0;
      int num2 = 2;
      uint num3;
      bool flag; // А это вообще не скомпилируется, значение не установлено
      while (true)
      {
        switch (num2) // Запутывающий switch, его легко распутать через дебаггер
        {
          case 0:
            goto label_9;
          case 1:
          case 2:
            flag = index < A_0.Length;
            num2 = 4;
            continue;
          case 3:
            num3 = num1;
            num2 = 0;
            continue;
          case 4:
            if (!flag)
            {
              num2 = 3;
              continue;
            }
            else
            {
              num1 = (num1 << 5) + num1 + (uint) A_0[index]; // А вот то, что нам нужно
              ++index;
              if (1 == 0)
                ;
              num2 = 1;
              continue;
            }
          default:
            goto label_2;
        }
      }
label_9:
      return num3;
  }
}


Что же, придется разобраться без алкоголя. Убираем строки кода, которые просто сбивают с толка, такие как switch (0), лишние case, дебаггером проходимся по коду и смотрим, куда он приводит в конце концов (переделано в читаемый вид):

private static uint GetHashCode(string code)
{
  var hash = 0U;
  foreach (var ch in code)
    hash = (hash << 5) + hash + ch;
  return hash;
}


Найдя уже нерабочие коды, я обнаружил, что везде используется нечто вроде *#123#, т.е. числовой код начинается с *# и заканчиваются #. Сформируем строку:

const string format = "*#{0}#"


А теперь метод полного перебора, чтобы пробить хэш и сравнить его со значением из таблицы:

public static void Main()
{
  BruteHash("0{0}"); // Некоторые коды начинаются с 0, а некоторые - нет, поэтому используем оба случая
  BruteHash("{0}"); // Тут без нуля
}

private static void BruteHash(string f)
{
    const string format = "*#{0}#"; // Так выглядит код
    var thread = new Thread(() =>
    {
        var i = 0;
        while (true)
        {
            var code = string.Format(f, i);
            var codeStr = string.Format(format, code);
            var value = GetHashCode(codeStr); // Ищем хэш код строки вида *#123#
            if (IsIpHash(value)) // Сверяемся с хэшкодом
            {
                Console.Write(codeStr); // Выводим
                return;
            }
            i++;
        }
    });
    thread.Start();
}

private static uint GetHashCode(string code)
{
    var hash = 0U;
    foreach (var ch in code)
        hash = (hash << 5) + hash + ch;
    return hash;
}

private static bool IsIpHash(uint hash)
{
    return hash == 3974577854U;
}


Значение 3974577854U найти достаточно просто: в таблице HiddenKeyTable поиском IP я нашел ключ g_IPSetting, значением которого является именно это число. Осталось только запустить и буквально через секунду мы получаем результат — код *#232340#, вводим, и вуаля! Мы можем изменить IP адрес, маску подсети, шлюз, DNS. Работает только с Diagnosis 1109, Samsung Omnia W. На других не проверял, увы. Непонятны причины скрытия этого кода, правда. Покапавшись в таблице, я нашел и некоторые другие рабочие коды:

*#9900# — DEBUGDUMP
*#745# — RILDUMP (SLOG_DUMP, sth to file)
*#9990# — g_VERIFYCOMPARE (IMEI compare for PLS operation 8)
*#2470# — g_CAMERAUPDATE (CameraFWUpdate)
*#0589# !- g_LIGHTSENSORTEST
*#1111# — g_SWversionFTA (Test Mode FTA SW version)
*#2222# — g_HWversionFTA (Test Mode FTA HW version)
*#1234# — g_SWversionEx (Version of systems, wifi, bt, csc, pda, phone)
*#0228# — g_BATTERYINFO
*#0842# — g_DEVICETEST
*#0283# — g_PHONELOOPBACKTEST
*#7284# — g_USBPATHCHANGE
*#232337# — g_BLUETOOTH_MAC_VIEWER
*#232331# — g_BLUETOOTH_RF_TEST
*#232338# — g_WIFI_MAC_VIEWER
*#770# — g_VPHONE770
*#771# — g_VPHONE771
*#772# — g_VPHONE772
*#773# — g_VPHONE773
*#774# — g_VPHONE774
*#775# — g_VPHONE775
*#776# — g_VPHONE776
*#777# — g_VPHONE777
*#778# — g_VPHONE778
*#779# — g_VPHONE779
*#7451# — g_ERROR_REPORT_ON
*#6381# — g_RILNETLOG_ON
*#6380# — g_RILNETLOG_OFF
*#9908# — g_GUMITEST3G_CAB_INSTALL
*#9920# — g_CONNECTION_SETTING (connection string)
*#07# — g_VIEWHISTORYNW
*#2663# — g_TouchFirmare_2663 (touch screen version)
*#99785# — g_PVKKey
*#997856# — g_PVKFileName
*#86824# — g_TouchkeySensitivity (firmware deupgrade available! (from 09 to 08))
*#0123# — g_MPS (MPS viewer, connection string)
*#232340# — g_IPSetting (IP SETTINGS!!!)

Остальные помечены в таблице BlockType.none, я так и не понял, как их вызывать, там есть функция HybridInterface_FCRProxy.w(), которая что-то возвращает, но разобраться я так и не смог.

Ссылка на скачивание XAP архива. Извиняюсь, не знаю, куда правильно лить.

P.S. Если я написал или оформил что-то неправильно, просьба — укажите об этом в ЛС. Пока что не знаю точно, как оформлять. Спасибо.
Tags:
Hubs:
Total votes 69: ↑67 and ↓2+65
Comments2

Articles