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

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

В корневой видим, что тут постарался DotObfuscator, что сильно усложняет процесс: лишние goto, пустые условия, циклы, однобуквенные названия переменных…
В DiagnosisApp уже интереснее. Видим тут класс, отвечающий за сам код страницы. Вся логика, в лучших традициях быдлокода, лежит не в ViewModel, а прямо в классе. Без всяких там partial. В начале класса видим переменные типа Button. То, что нужно! Это ведь панель набора кода:
Ищем событие Click:
Ведет оно в никуда, лишь управление кнопкой delete и вибрацией.
Что же, и слава богу, разбираться в этом лучше и не начинать…
Казалось бы, тупик, однако оказывается, что всем управляет таймер, включающийся после каждого нажатия кнопки:
Господи, чтобы разобраться в этом коде нужно много алкоголя… Удаляем все, кроме навигации на страницы:
Уже лучше. Тут видно, что навигация идет через некую таблицу HiddenKeyTable, а в качестве ключа туда передается результат некой функции f(A_0), которая является хэшем введенного кода. Смотрим код функции:
Что же, придется разобраться без алкоголя. Убираем строки кода, которые просто сбивают с толка, такие как switch (0), лишние case, дебаггером проходимся по коду и смотрим, куда он приводит в конце концов (переделано в читаемый вид):
Найдя уже нерабочие коды, я обнаружил, что везде используется нечто вроде *#123#, т.е. числовой код начинается с *# и заканчиваются #. Сформируем строку:
А теперь метод полного перебора, чтобы пробить хэш и сравнить его со значением из таблицы:
Значение 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. Если я написал или оформил что-то неправильно, просьба — укажите об этом в ЛС. Пока что не знаю точно, как оформлять. Спасибо.
Пошел я искать решение в гугл. Все, что я там нашел, было связано непосредственно с настройкой роутера или полностью рутованными телефонами (мой рут накрылся после обновления до Tango), поэтому данное решение мне также не подошло. И вот, в голову пришла гениальная идея — а что если ребята из Samsung уже потрудились, однако сообщить об этом забыли? Так неоднократно было с меню Diagnosis. Напомню, через дыру в него создавали полноценный рут, запускали exe файлы без подписей и многое-многое другое. В прошлом. Снова гуглим, но это ничего не дает — никто толком не знает рабочих кодов диагностики. Хорошо. Ищем xap меню диагностики. На удивление, нашел, т.к. обычно они внедрены глубоко в ядро. Ядро было написано на нативном коде, однако интерфейс — на C#. То, что нужно! Нашел, правда, в кэше китайского ресурса на последней странице, но не суть.
Что же, в путь! Открываем любимый dotPeek от JetBrains и видим следующую картину:

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

В корневой видим, что тут постарался 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. Если я написал или оформил что-то неправильно, просьба — укажите об этом в ЛС. Пока что не знаю точно, как оформлять. Спасибо.
