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