Недавно понадобилось поставить новую wifi карточку стандарта ac в свой ноутбук Lenovo x230, в котором есть whitelist для wlan карточек. Ниже опишу свои изыскания по отключению whitelist'а.
Для начала нам надо слить дамп нашего биоса, сделать это можно с помощью утилиты FPT
Далее нам понадобится утилита PhoenixTool 2.52. Запускаем её, выбираем наш bios.rom и ждём, пока она его распакует, далее в поле Manufacturer выбираем Lenovo и жмём кнопку Advanced, там ставим галочки «Allow user to modify other modules» и «No SLIC»
Нажимаем «Done» и «Go», когда откроется окошко
ничего не нажимаем и идём в папку DUMP, куда распакован наш биос. Далее нужно найти файл отвечающий за whitelist. Ищем просто по тексту ошибки (в кодировке UTF-16), которая выводится на экран при вставленном модуле не из вайтлиста «1802: Unauthorized network card is plugged in»
Находим файл 79E0EDD7-9D1D-4F41-AE1A-F896169E5216_2207.ROM — это модуль биоса для вайтлиста.
Далее грузим его в IDA, смотрим код и ищем нашу строку «1802: Unauthorized network card is plugged in»
К этой строке идёт обращение в процедуре Sub_A0C. Т.е. процедура Sub_A0C предположительно занимается выводом ошибки на экран, смотрим откуда она вызывается (кликаем по имени процедуры и нажимем клавишу X на клавиатуре)
Видим что это процедура Sub_B20. Идём в эту процедуру и нажимаем пробел для перехода к схематичному представления кода.
Видим что процедура Sub_A0C вызывается из блока кода на метке Loc_BDD. Далее можно проследить условные переходы к этой метке и т.д. Но я решил, чтобы лучше понять код работы этой процедуры, пройти код от начала процедуры.
Итак, первое условное ветвление проверяет регистр edx на ноль
т.к. регистр edx выше в этой процедуре нигде не инициализируется, то понятно, что ему должно присваиваться значение перед вызовом процедуры Sub_B20. Это можно увидеть в коде
и
Что означает значение в регистре edx я не понял, поэтому пройдёмся по обоим веткам ветвления.
Пойдём для начала по правой веке, на Loc_C6E
В eax кладётся адрес данных qword_270, двойным щелчком по qword_270 переходим к просмотру этой записи, далее переходим на вкладку Hex View-A. Это и есть наш whitelist, он идёт с адреса 270h до 3FFh. С форматом тоже всё просто, каждая запись длинной в 16 байт (4 слова по 32bit) соответствует одному устройству: первое слово — какой-то флаг, принимающий значение 0, 1, 5 или 6, второе слово — это system id, третье — subsystem id, четвёртое — ещё один флаг, имеющий значение 0 или 1. Предполагаю что первый флаг определяет тип устройства: 0 — wifi карточка, 1 — модем, 5 — ?, 6 — конец списка.
Вернёмся к коду. Сравниваем eax с шестёркой, а шестёрка у нас — это конец вайтлиста, т.е. если вайтлист получается пустой, то идём сразу на Loc_BDD, которая у нас вызывает ошибку. Это условие нам не интересно, т.к. вайтлист у нас не пустой, и условие не выполнится. Далее кладём адрес qword_270 в регистр rdx, проверяем eax (первый флаг записи в вайтлисте) на ноль, если флаг отличный от нуля, переходим к ошибке. Это условие нас тоже не интересует. Это же условие является началом цикла обхода вайтлиста.
Дальше командами
Помещаем system id из вайтлиста в регистр ecx. Регистр r8 здесь выступает в роли инкремента в цикле, изначально он равен нулю.
Далее сравниваем ecx (system id из вайтлиста) и значение в памяти по адресу в регистре rdi.
Несложно догадаться, что по адресу в регистре rdi у нас лежит идентификатор нашей вставленной wifi карточки, а в начале процедуры в регистр rdi мы кладём r8, т.е. процедуре Sub_B20 в качестве параметра использется регистр r8, где должен лежать адрес ячейки памяли с идентификатором нашей карточки.
Если идентификатор не совпал, то идём на Loc_CBA, там мы увеличиваем инкрементный регистр r8 на 10h (для этого в регистре r13w у нас заранее должна лежать единица)
и проверяем, не в конце ли мы вайтлиста
Если в конце, то выходим из цикла и выдаём ошибку, если не в конце, то идём в начало цикла.
Понятно, что для обхода вайлиста, нам надо убрать условный переход при сравнении system id, т.е. заменить
Далее, если system id совпал, идёт аналогичный код для сравнения subsystem id
Если subsystem id не совпал, то перебираем по циклу идентификаторы дальше. Тут нам тоже необходимо поправить условные переход на безусловный
Дальше по коду уже нет переходов на ошибку, но есть интересный код, проверяющий второй флаг в записи вайтлиста, про который я писал ранее, он сравнивается с регистром r13d, и если совпадает, то выполняется некий дополнительный кусок кода. Что делает этот код понять трудно, по анализу вайтлиста можно заметить, что этот флаг стоит только у карточек фирмы Intel.
Так, с правой веткой разобрались, теперь пройдёмся по левой ветке.Там у нас код работы с вайтлистом начинается с метки Loc_C18. Аналогично проверяется, не пустой ли whitelist:
Далее сравнивается eax (первый флаг) и r13d (что за параметр лежит в r13d я так и не разобрался, в начале процедуры него кладётся значение по адресу [rbx+1]), далее в зависимости от выполнения этого условия, попадаем сразу на код проверки system id, либо выполняем интересное сравнение
Проверяем первый флаг в записи вайтлиста на 5, если условие выполняется, то пропускаем эту запись вайтлиста (т.е. игнорируем wifi карточку) и идём далее по циклу проверки.
Чтобы понять что же это за такой интересный флаг, надо понимать, что передаётся в процедуру Sub_B20. Если помните, в правой ветке кода, если этот флаг был отличный от нуля, то выдавалась ошибка о неподдерживаемой карте. Т.е. чтобы карточка с флагом 5 загрузилась, должны выполниться следующие условия вызова процедуры Sub_B20: регистр edx должен быть отличным от нуля, а регистр r13d не должен быть равен пяти (
Далее по коду, мы аналогично правой ветке, проверяем в цикле system id
Здесь нам надо также заменить условный переход
Что интересно, далее в левой ветке нет проверки subsystem id, т.е. достаточно совпадения system id. Из вышеизложенного можно предположить, что правая ветка — проверка wifi карточек, а левая проверка модемов. А в регистре edx при вызове функции содержится тип устройства: 0 — wifi, 1 — модем. Но тут тоже непонятка, т.к. каточка 0087:8086 с флагом 5 — это wifi+wimax, а не модем, да и в edx может быть больше единицы, т.к. из edx вычитается r13d, а потом сравнивается с четвёркой:
Собственно на этом анализ можно заканчивать. Нужные места для патча мы нашли.
Переводим команды в опкоды и делаем замены в hex редакторе, в итоге получаем такой патч:
Сохраняем пропатченый файл 79E0EDD7-9D1D-4F41-AE1A-F896169E5216_2207.ROM и нажимеам в окне PhoenixTool кнопку Ok.
PhoenixTool соберёт новый биос с именем bios_SLIC.rom.
Осталось дело за малым, необходимо прошить новый биос. В свежих ноутбуках Lenovo, включая мой x230, изменённый bios нельзя прошить программно, поэтому шьём его программатором. Фото процесса прошивки через программатор к сожалению не делал. Биос находится в микросхеме MX25L3206E рядом с разъёмом ExpressCard. Эта микросхема представляет из себя обычный SPI EEPROM 25ой серии, простой программатор для которой стоит 300р.
На этом всё, наслаждаемся работой новой wifi карточки.
Использовались материалы с форума bios-mods.com
www.bios-mods.com/forum/Thread-TUTORIAL-Lenovo-X230-Tablet-BIOS-Whitelist-Removal-Hardware-Flash
www.bios-mods.com/forum/Thread-REQUEST-Whitelist-wifi-removal-for-T430-got-hardware-flash-programmer?page=4
Для начала нам надо слить дамп нашего биоса, сделать это можно с помощью утилиты FPT
fpt -d bios.rom -BIOS
Далее нам понадобится утилита PhoenixTool 2.52. Запускаем её, выбираем наш bios.rom и ждём, пока она его распакует, далее в поле Manufacturer выбираем Lenovo и жмём кнопку Advanced, там ставим галочки «Allow user to modify other modules» и «No SLIC»
Нажимаем «Done» и «Go», когда откроется окошко
ничего не нажимаем и идём в папку DUMP, куда распакован наш биос. Далее нужно найти файл отвечающий за whitelist. Ищем просто по тексту ошибки (в кодировке UTF-16), которая выводится на экран при вставленном модуле не из вайтлиста «1802: Unauthorized network card is plugged in»
Находим файл 79E0EDD7-9D1D-4F41-AE1A-F896169E5216_2207.ROM — это модуль биоса для вайтлиста.
Далее грузим его в IDA, смотрим код и ищем нашу строку «1802: Unauthorized network card is plugged in»
К этой строке идёт обращение в процедуре Sub_A0C. Т.е. процедура Sub_A0C предположительно занимается выводом ошибки на экран, смотрим откуда она вызывается (кликаем по имени процедуры и нажимем клавишу X на клавиатуре)
Видим что это процедура Sub_B20. Идём в эту процедуру и нажимаем пробел для перехода к схематичному представления кода.
Видим что процедура Sub_A0C вызывается из блока кода на метке Loc_BDD. Далее можно проследить условные переходы к этой метке и т.д. Но я решил, чтобы лучше понять код работы этой процедуры, пройти код от начала процедуры.
Итак, первое условное ветвление проверяет регистр edx на ноль
test edx, edx
jz Loc_C6E
т.к. регистр edx выше в этой процедуре нигде не инициализируется, то понятно, что ему должно присваиваться значение перед вызовом процедуры Sub_B20. Это можно увидеть в коде
и
Что означает значение в регистре edx я не понял, поэтому пройдёмся по обоим веткам ветвления.
Пойдём для начала по правой веке, на Loc_C6E
В eax кладётся адрес данных qword_270, двойным щелчком по qword_270 переходим к просмотру этой записи, далее переходим на вкладку Hex View-A. Это и есть наш whitelist, он идёт с адреса 270h до 3FFh. С форматом тоже всё просто, каждая запись длинной в 16 байт (4 слова по 32bit) соответствует одному устройству: первое слово — какой-то флаг, принимающий значение 0, 1, 5 или 6, второе слово — это system id, третье — subsystem id, четвёртое — ещё один флаг, имеющий значение 0 или 1. Предполагаю что первый флаг определяет тип устройства: 0 — wifi карточка, 1 — модем, 5 — ?, 6 — конец списка.
Вернёмся к коду. Сравниваем eax с шестёркой, а шестёрка у нас — это конец вайтлиста, т.е. если вайтлист получается пустой, то идём сразу на Loc_BDD, которая у нас вызывает ошибку. Это условие нам не интересно, т.к. вайтлист у нас не пустой, и условие не выполнится. Далее кладём адрес qword_270 в регистр rdx, проверяем eax (первый флаг записи в вайтлисте) на ноль, если флаг отличный от нуля, переходим к ошибке. Это условие нас тоже не интересует. Это же условие является началом цикла обхода вайтлиста.
Дальше командами
movzx ecx, word ptr [r8+rdx+6]
movzx eax, word ptr [r8+rdx+4]
shl ecx, 10h
or ecx, eax
Помещаем system id из вайтлиста в регистр ecx. Регистр r8 здесь выступает в роли инкремента в цикле, изначально он равен нулю.
Далее сравниваем ecx (system id из вайтлиста) и значение в памяти по адресу в регистре rdi.
cmp [rdi], ecx
Несложно догадаться, что по адресу в регистре rdi у нас лежит идентификатор нашей вставленной wifi карточки, а в начале процедуры в регистр rdi мы кладём r8, т.е. процедуре Sub_B20 в качестве параметра использется регистр r8, где должен лежать адрес ячейки памяли с идентификатором нашей карточки.
Если идентификатор не совпал, то идём на Loc_CBA, там мы увеличиваем инкрементный регистр r8 на 10h (для этого в регистре r13w у нас заранее должна лежать единица)
loc_CBA:
add r9w, r13w
movzx r8d, r9w
shl r8, 4
и проверяем, не в конце ли мы вайтлиста
mov eax, [r8+rdx]
cmp eax, 6
jz loc_BDD
Если в конце, то выходим из цикла и выдаём ошибку, если не в конце, то идём в начало цикла.
Понятно, что для обхода вайлиста, нам надо убрать условный переход при сравнении system id, т.е. заменить
jnz short loc_CBA
на jmp $+2
.Далее, если system id совпал, идёт аналогичный код для сравнения subsystem id
movzx ecx, word ptr [r8+rdx+0Ah]
movzx eax, word ptr [r8+rdx+8]
shl ecx, 10h
or ecx, eax
cmp [rdi+4], ecx
jz short loc_CD5
Если subsystem id не совпал, то перебираем по циклу идентификаторы дальше. Тут нам тоже необходимо поправить условные переход на безусловный
jz short loc_CD5
на jmp short loc_CD5
.Дальше по коду уже нет переходов на ошибку, но есть интересный код, проверяющий второй флаг в записи вайтлиста, про который я писал ранее, он сравнивается с регистром r13d, и если совпадает, то выполняется некий дополнительный кусок кода. Что делает этот код понять трудно, по анализу вайтлиста можно заметить, что этот флаг стоит только у карточек фирмы Intel.
Так, с правой веткой разобрались, теперь пройдёмся по левой ветке.Там у нас код работы с вайтлистом начинается с метки Loc_C18. Аналогично проверяется, не пустой ли whitelist:
loc_C18:
mov eax, dword ptr cs:qword_270
xor r9w, r9w
cmp eax, 6
jz short loc_BDD
Далее сравнивается eax (первый флаг) и r13d (что за параметр лежит в r13d я так и не разобрался, в начале процедуры него кладётся значение по адресу [rbx+1]), далее в зависимости от выполнения этого условия, попадаем сразу на код проверки system id, либо выполняем интересное сравнение
cmp eax, 5
jnz short loc_C54
Проверяем первый флаг в записи вайтлиста на 5, если условие выполняется, то пропускаем эту запись вайтлиста (т.е. игнорируем wifi карточку) и идём далее по циклу проверки.
Чтобы понять что же это за такой интересный флаг, надо понимать, что передаётся в процедуру Sub_B20. Если помните, в правой ветке кода, если этот флаг был отличный от нуля, то выдавалась ошибка о неподдерживаемой карте. Т.е. чтобы карточка с флагом 5 загрузилась, должны выполниться следующие условия вызова процедуры Sub_B20: регистр edx должен быть отличным от нуля, а регистр r13d не должен быть равен пяти (
cmp eax, r13d
, а в eax у нас лежит наш флаг равный пяти). Можно лишь догадаться, что для запуска карточки с таким флагом, требуется ещё какое-то условие, кроме нахождения её в вайтлисте. Дальше выполнять анализ для меня было довольно сложно, поэтому, что же это за условие, узнать не удалось.Далее по коду, мы аналогично правой ветке, проверяем в цикле system id
loc_C3B:
movzx ecx, word ptr [r8+rdx+6]
movzx eax, word ptr [r8+rdx+4]
shl ecx, 10h
or ecx, eax
cmp [rdi], ecx
jz loc_DF3
Здесь нам надо также заменить условный переход
jz loc_DF3
на безусловный jmp loc_DF3
.Что интересно, далее в левой ветке нет проверки subsystem id, т.е. достаточно совпадения system id. Из вышеизложенного можно предположить, что правая ветка — проверка wifi карточек, а левая проверка модемов. А в регистре edx при вызове функции содержится тип устройства: 0 — wifi, 1 — модем. Но тут тоже непонятка, т.к. каточка 0087:8086 с флагом 5 — это wifi+wimax, а не модем, да и в edx может быть больше единицы, т.к. из edx вычитается r13d, а потом сравнивается с четвёркой:
sub edx, r13d
jz short loc_B4F
cmp edx, 4
jz short loc_B4F
Собственно на этом анализ можно заканчивать. Нужные места для патча мы нашли.
Переводим команды в опкоды и делаем замены в hex редакторе, в итоге получаем такой патч:
C4E: 0F84->90E9 (jz to jmp)
CA3: 16->00 (jnz loc_CBA to jnz $+2)
CB8: 74->EB (jnz to jmp)
Сохраняем пропатченый файл 79E0EDD7-9D1D-4F41-AE1A-F896169E5216_2207.ROM и нажимеам в окне PhoenixTool кнопку Ok.
PhoenixTool соберёт новый биос с именем bios_SLIC.rom.
Осталось дело за малым, необходимо прошить новый биос. В свежих ноутбуках Lenovo, включая мой x230, изменённый bios нельзя прошить программно, поэтому шьём его программатором. Фото процесса прошивки через программатор к сожалению не делал. Биос находится в микросхеме MX25L3206E рядом с разъёмом ExpressCard. Эта микросхема представляет из себя обычный SPI EEPROM 25ой серии, простой программатор для которой стоит 300р.
На этом всё, наслаждаемся работой новой wifi карточки.
Использовались материалы с форума bios-mods.com
www.bios-mods.com/forum/Thread-TUTORIAL-Lenovo-X230-Tablet-BIOS-Whitelist-Removal-Hardware-Flash
www.bios-mods.com/forum/Thread-REQUEST-Whitelist-wifi-removal-for-T430-got-hardware-flash-programmer?page=4