Samsung WB850F стала первой камерой, объединившей в себе DRIMeIII SoC и WiFi. В ней, как и в модели EX2F, используется прошивка, в которую инженеры Samsung любезно включили файл
partialImage.o.map
с полным дампом компоновщика и именами всех символов из ZIP-архива. Этот их «подарок» мы используем для реверс-инжиниринга основной прошивки SoC, чтобы обеспечить прохождение проверки при обнаружении точек доступа WiFi и возможность использовать для загрузки изображений samsung-nx-emailservice
.Эта публикация продолжает тему статьи, посвящённой камерам Samsung с поддержкой WiFi, и является частью серии, освещающей линейку Samsung NX.
▍ Содержание
WB850F_FW_210086.zip — внешний контейнер
Камера WB850F относится к немногим моделям, для которых компания Samsung продолжает выпускать прошивки и вспомогательные файлы, несмотря на прекращение обслуживания приложения iLauncher.
Архив WB850F_FW_210086.zip, который можно найти на её странице, несёт в себе несколько файлов (вывод получен с помощью
file
):GPS_FW/BASEBAND_FW_Flash.mbin: data
GPS_FW/BASEBAND_FW_Ram.mbin: data
GPS_FW/Config.BIN: data
GPS_FW/flashBurner.mbin: data
FWUP: ASCII text, with CRLF line terminators
partialImage.o.map: ASCII text
WB850-FW-SR-210086.bin: data
wb850f_adj.txt: ASCII text, with CRLF line terminators
Файл
FWUP
содержит только строку upgrade all
, представляющую скрипт модуля тестирования/автоматизации. Файл wb850f_adj.txt
аналогичен, но является более сложным скриптом для обновления прошивки GPS и удаления соответствующих файлов. Пока пропустим этот скрипт и каталог GPS_FW
.▍ partialImage.o.map — дамп компоновщика
Текстовый файл
partialImage.o.map
содержит более 300 000 строк, включая вывод компоновщика для partialImage.o
и всю карту памяти линкованного файла:output input virtual
section section address size file
.text 00000000 01301444
.text 00000000 000001a4 sysALib.o
$a 00000000 00000000
sysInit 00000000 00000000
L$_Good_Boot 00000090 00000000
archPwrDown 00000094 00000000
...
DevHTTPResponseStart 00321a84 000002a4
DevHTTPResponseData 00321d28 00000100
DevHTTPResponseEnd 00321e28 00000170
...
.data 00000000 004ed40c
.data 00000000 00000874 sysLib.o
sysBus 00000000 00000004
sysCpu 00000004 00000004
sysBootLine 00000008 00000004
Так продолжается очень долго, и это реальная карта сокровищ! Осталось лишь найти остров, для которого она составлена.
▍ WB850-FW-SR-210086.bin — анализ заголовков
Открыв
WB850-FW-SR-210086.bin
с помощью binwalk
, мы видим длинный список заголовков файлов (HTML, PNG, JPEG, ...), заголовок VxWorks
, множество путей Unix, но ничего похожего на разделы или файловые системы.Попробуем-ка тогда сделать шестнадцатеричный дамп первого килобайта:
00000000: 3231 3030 3836 0006 4657 5f55 502f 4f4e 210086..FW_UP/ON
00000010: 424c 312e 6269 6e00 0000 0000 0000 0000 BL1.bin.........
00000020: 0000 0000 0000 0000 c400 0000 0008 0000 ................
00000030: 4f4e 424c 3100 0000 0000 0000 0000 0000 ONBL1...........
00000040: 0000 0000 4657 5f55 502f 4f4e 424c 322e ....FW_UP/ONBL2.
00000050: 6269 6e00 0000 0000 0000 0000 0000 0000 bin.............
00000060: 0000 0000 30b6 0000 c408 0000 4f4e 424c ....0.......ONBL
00000070: 3200 0000 0000 0000 0000 0000 0000 0000 2...............
00000080: 5b57 4238 3530 5d44 5343 5f35 4b45 595f [WB850]DSC_5KEY_
00000090: 5742 3835 3000 0000 0000 0000 0000 0000 WB850...........
000000a0: 38f4 d101 f4be 0000 4d61 696e 5f49 6d61 8.......Main_Ima
000000b0: 6765 0000 0000 0000 0000 0000 526f 6d46 ge..........RomF
000000c0: 532f 5350 4944 2e52 6f6d 0000 0000 0000 S/SPID.Rom......
000000d0: 0000 0000 0000 0000 0000 0000 00ac f402 ................
000000e0: 2cb3 d201 5265 736f 7572 6365 0000 0000 ,...Resource....
000000f0: 0000 0000 0000 0000 4657 5f55 502f 5742 ........FW_UP/WB
00000100: 3835 302e 4845 5800 0000 0000 0000 0000 850.HEX.........
00000110: 0000 0000 0000 0000 864d 0000 2c5f c704 .........M..,_..
00000120: 4f49 5300 0000 0000 0000 0000 0000 0000 OIS.............
00000130: 0000 0000 4657 5f55 502f 736b 696e 2e62 ....FW_UP/skin.b
00000140: 696e 0000 0000 0000 0000 0000 0000 0000 in..............
00000150: 0000 0000 48d0 2f02 b2ac c704 534b 494e ....H./.....SKIN
00000160: 0000 0000 0000 0000 0000 0000 0000 0000 ................
*
000003f0: 0000 0000 0000 0000 0000 0000 5041 5254 ............PART
Очень интересно. Всё начинается с версии прошивки,
210086
. Потом идут 0x00 0x06
, сопровождаемые именем файла FW_UP/ONBL1.bin
, расположенного в смещении 0x008
. Следующее имя файла, FW_UP/ONBL2.bin
, находится в 0x044
, значит это, скорее всего, 60-байтовая запись «раздела»:00000008: 4657 5f55 502f 4f4e 424c 312e 6269 6e00 FW_UP/ONBL1.bin.
00000018: 0000 0000 0000 0000 0000 0000 0000 0000 ................
00000028: c400 0000 0008 0000 4f4e 424c 3100 0000 ........ONBL1...
00000038: 0000 0000 0000 0000 0000 0000 ............
После имени файла идёт много нулей (составляющих 32-байтовую строку, дополненную нулями), за которой следует два целых числа,
0xc4
и 0x800
, с прямым порядком байтов, сопровождаемые 20-байтовой строкой ONBL1
, тоже дополненной нулями, которая, по всей видимости, представляет имя соответствующего раздела. Далее идут другие записи с аналогичной структурой. Целыми числами во второй записи (ONBL2
) являются 0xb630
и 0x8c4
, значит, можно предположить, что первое является длиной, а второе отражает смещение в файле (смещение одной записи всегда равно «смещение + длина» предыдущей).Всего здесь шесть записей, значит,
0x00 0x06
между строкой версии и первой записью наверняка представляет завершающий или заполняющий байт версии прошивки и отражает количество разделов.Понимая это, можно воссоздать таблицу разделов следующим образом:
Имя файла | Размер | Смещение | Имя раздела |
FW_UP/ONBL1.bin | 196 (0xc4) | 0x0000800 | ONBL1 |
FW_UP/ONBL2.bin | 46 КБ (0xb630) | 0x00008c4 | ONBL2 |
[WB850]DSC_5KEY_WB850 | 30 МБ (0x1d1f438) | 0x000bef4 | Main_Image |
RomFS/SPID.Rom | 48 МБ (0x2f4ac00) | 0x1d2b32c | Resource |
FW_UP/WB850.HEX | 19 КБ (0x4d86) | 0x4c75f2c | OIS |
FW_UP/skin.bin | 36 МБ (0x22fd048) | 0x4c7acb2 | SKIN |
▍ WB850-FW-SR-210086.bin — разделы кода и данных
Этот инструмент извлекает разделы на основе их имён, добавляя в конце
.bin
. Выполнение file
в отношении вывода здесь не особо поможет:ONBL1.bin: data
ONBL2.bin: data
Main_Image.bin: OpenPGP Secret Key
Resource.bin: MIPSEB-LE MIPS-III ECOFF executable stripped - version 0.0
OIS.bin: data
SKIN.bin: data
-
ONBL1
иONBL2
, похоже, представляют стадии 1 и 3 загрузчика (что подтверждается строкой вMain_Image
:BootLoader(ONBL1, ONBL2) Update Done
). -
Main_Image
— это сама прошивка:OpenPGP Secret Key
здесь является ложным выводом;binwalk -A
показывает много прологов процедур ARM, находящихся в этом файле. -
Resource
иSKIN
— это крупные контейнеры, возможно, предоставленные производителем SoC для изменения скинов интерфейса камеры? -
OIS
— это не шестнадцатеричный файл, но он может являться прошивкой для отдельного оптического стабилизатора.
Из всего этого наибольший интерес представляет
Main_Image
.Загрузка кода в Ghidra
Три раздела,
ONBL1
, ONBL2
и Main_Image
, содержат фактический код ARM. Обычная прошивка ARM включает таблицу векторов сброса по адресу 0x0000000
(обычно начало флэш-памяти или ПЗУ), представляющую серию инструкций перехода. Однако все три двоичных файла в своём начале содержат фактический линейный код, значит, они наверняка должны повторно отображаться в пока неизвестный нам адрес.Чтобы понять, как и почему камера неверно определяет точку доступа, нужно:
- Найти правильный адрес памяти, в который должен отображаться
Main_Image
. - Загрузить имена символов из
partialImage.o.map
в Ghidra. - Найти и проанализировать функцию, которая ошибочно активирует авторизацию точки доступа.
▍ Загрузка и отображение Main_Image
По умолчанию Ghidra предположит, что двоичный файл выполняет загрузку по адресу
0x0000000
, и попытается проанализировать его именно таким образом. Чтобы получить корректный адрес памяти, нужно найти функцию, которая обращается к какому-то известному значению из исполняемого файла, используя абсолютный адрес. Учитывая, что всего в файле ~77 000 функций, можно начать с задачи под номером 3 и найти во вкладке Defined Strings все элементы, включающие yahoo
:Превосходно! Ghidra нашла несколько строк, которые напоминают выполнение измотанным разработчиком отладки при помощи
printf
. Возможно, они относятся к функции DevHTTPResponseStart()
, которая, по всей видимости, проверяет, может ли камера подключиться к Yahoo, Google или Samsung:0139f574 DevHTTPResponseStart: url=%s, handle=%x, status=%d\n, headers=%s\r\n
0139f5b8 DevHTTPResponseStart: This is YAHOO check !!!\r\n
0139f5f4 DevHTTPResponseStart: THIS IS GOOGLE/YAHOO/SAMSUNG PAGE!!!! 111\n\n\n
0139f638 DevHTTPResponseStart: 301/302/307! cannot find yahoo! safapi_is_browser_framebuffer_on : %d , safapi_is_browser_authed(): %d \r\n
Согласно
partialImage.o.map
, функция с таким именем существует по адресу 0x321a84
, и Ghidra тоже нашла функцию по адресу 0x321a84
. Между картой и бинарником совпадают и другие смещения функций, значит, можно предположить, что адреса .text
из файла карты фактически 1:1 соответствуют Main_Image
! Мы нашли остров, для которого составлялась наша карта!Вот начало той функции:
bool FUN_00321a84(undefined4 param_1,ushort param_2,int param_3,int param_4) {
/* удаление объявлений переменных */
FUN_0031daec(*(DAT_00321fd4 + 0x2c),DAT_00322034,param_3,param_1,param_2,param_4);
FUN_0031daec(*(DAT_00321fd4 + 0x2c),DAT_00322038);
FUN_00326f84(0x68);
Она начинается с двух вызовов к
FUN_0031daec()
с разным числом параметров — это вновь очень напоминает отладку с помощью printf
. Согласно карте памяти, это называется opd_printf()
! Первый параметр отражает некий контекст или точку назначения, а второй должен быть ссылкой на строку форматирования. Два значения DAT_
опознаются Ghidra как 32-битные undefined
:DAT_00322034:
74 35 3a c1 undefined4 C13A3574h
DAT_00322038:
b8 35 3a c1 undefined4 C13A35B8h
Тем не менее три последних соответствующих цифры имеют совпадение в ранее встреченных отладочных строках
DevHTTPResponseStart:
-
0xc13a3574
—0x0139f574
=0xc0004000
(первая строка форматирования с четырьмя параметрами). -
0xc13a35b8
—0x0139f5b8
=0xc0004000
(вторая строка форматирования без параметров).
Из этого можно сделать вывод, что
Main_Image
должен загружаться в память по адресу 0xc0004000
. Это нельзя изменить в Ghidra постфактум, поэтому нужно удалить исполняемый файл из проекта, заново его импортировать и установить желаемый базовый адрес:▍ Загрузка имён функций из partialImage.o.map
В Ghidra есть скрипт для импорта всех меток данных и имён функций из текстовой таблицы
ImportSymbolScript.py
. Он ожидает, что каждая строка будет содержать три переменных, разделённых произвольным объёмом пустого пространства (в соответствии с функцией Python string.split()
):- Имя символа.
- Шестнадцатеричный адрес.
-
f
— это function,l
— это label.
Карта символов имеет несколько разделов, но нас пока интересуют только функции, определённые в
.text
, которые в точности отображаются в адреса из Main_Image
. Помимо имён функций, карта также содержит пустые строки, смещения из объектного файла (с меткой в виде .text
), метки (с префиксом L$_
) и локальные символы (с префиксом $
).Нам нужно ограничиться символами из раздела
.text
(всё после .text
и до .debug_frame
), избавиться от пустых строк и не-функций, затем добавить к каждому адресу 0xc0004000
, чтобы обеспечить соответствие базовому адресу в Ghidra. Всё это можно в довольно непонятной форме проделать с помощью однострочной команды awk
:awk '/^\.text /{t=1;next}/^\.debug_frame /{t=0} ; !/[$.]/ { if (t && $1) { printf "%s %x f\n", $1, (strtonum("0x"$2)+0xc0004000) } }'
Или чуть понятнее при помощи более медленного цикла оболочки:
sed '1,/^\.text /d;/^\.debug_frame /,$d' | grep -v '^$' | grep -v '[.$]' | \
while read sym addr f ; do
printf "%s %x f\n" $sym $((0xc0004000 + 0x$addr))
done
В обоих случаях получится одинаковый вывод, который можно загрузить в Ghidra через Window / Script Manager / ImportSymbolsScript.py:
sysInit c0004000 f
archPwrDown c0004094 f
MMU_WriteControlReg c00040a4 f
MMU_WritePageTableBaseReg c00040b8 f
MMU_WriteDomainAccessReg c00040d0 f
...
▍ Реверс-инжиниринг DevHTTPResponseStart
Теперь, когда у нас есть имена функций, нужно вручную установить тип множества полей
DAT_
на pointer
, переименовать параметры в соответствии с отладочной строкой, после чего мы получим более-менее пригодный для использования вывод декомпилятора.Ниже показана версия с комментариями, отредактированная для лучшей читаемости. В ней были переписаны некоторые условные выражения, а также включены ссылки на строки.
bool DevHTTPResponseStart(undefined4 handle,ushort status,char *url,char *headers) {
bool result;
opd_printf(ctx,"DevHTTPResponseStart: url=%s, handle=%x, status=%d\n, headers=%s\r\n",
url,handle,status,headers);
opd_printf(ctx,"DevHTTPResponseStart: This is YAHOO check !!!\r\n");
safnotify_page_load_status(0x68);
if ((url == NULL) || (status != 301 && status != 302 && status != 307)) {
/* это не перенаправление по HTTP */
if (status == 200) {
/* HTTP 200 означает OK */
if (headers == NULL ||
(strstr(headers,"domain=.yahoo") == NULL &&
strstr(headers,"Domain=.yahoo") == NULL &&
strstr(headers,"domain=kr.yahoo") == NULL &&
strstr(headers,"Domain=kr.yahoo") == NULL)) {
/* при отсутствии заголовков ответа или куки yahoo проверка проваливается! */
result = true;
} else {
/* в заголовках обнаружен куки yahoo */
opd_printf(ctx,"DevHTTPResponseData: THIS IS GOOGLE/YAHOO PAGE!!!! 3333\n\n\n");
*p_request_ongoing = 0;
if (!safapi_is_browser_authed())
safnotify_auth_ap(0);
result = false;
}
} else if (status < 0) {
/* отрицательный статус = отмена? */
result = false;
} else {
/* положительный статус, не перенаправление, не "OK" */
result = !safapi_is_browser_framebuffer_on();
}
} else {
/* перенаправление по HTTP. */
char *match = strstr(url,"yahoo.");
if (match == NULL || match > (url+11)) {
opd_printf(ctx, "DevHTTPResponseStart: 301/302/307! cannot find yahoo! safapi_is_browser_framebuffer_on : %d , safapi_is_browser_authed(): %d \r\n",
safapi_is_browser_framebuffer_on(), safapi_is_browser_authed());
if (!safapi_is_browser_framebuffer_on() && !safapi_is_browser_authed()) {
opd_printf(ctx,"DevHTTPResponseStart: 302 auth failed!!! kSAFAPIAuthErrNotAuth!! \r\n");
safnotify_auth_ap(1);
}
result = false;
} else {
/* в URL-адресе найден элемент "yahoo." */
opd_printf(ctx, "DevHTTPResponseStart: THIS IS GOOGLE/YAHOO/SAMSUNG PAGE!!!! 111\n\n\n");
*p_request_ongoing = 0;
if (!safapi_is_browser_authed())
safnotify_auth_ap(0);
result = false;
}
}
return result;
}
▍ Интерпретация процесса обнаружения точки доступа
Итак, код в
DevHTTPResponseStart
проверяет выполнение одного из двух условий и вызывает safnotify_auth_ap(0)
, чтобы отметить точку доступа WiFi как аутентифицированную:- При HTTP-ответе
200 OK
сервер должен установить куки на домен.yahoo.something
либоkr.yahoo.something
- При получении кода, связанного с перенаправлением (
301
/302
/307
), в URL-адресе (вероятно, адресе перенаправления?) рядом с началом должна находиться подстрокаyahoo
.
Если вручную перейти по запрашиваемому URL,
http://www.yahoo.co.kr/
, он перенаправит нас на https://www.yahoo.com/
. Значит, всё в порядке?GET / HTTP/1.1
Host: www.yahoo.co.kr
HTTP/1.1 301 Moved Permanently
Location: https://www.yahoo.com/
Не совсем. Подстрока
yahoo.
находится в адресе https://www.yahoo.com/
в позиции 12, но код требует, чтобы она была в одной из первых 11 позиций. Эта проверка проваливается по вине протокола TLS.Чтобы пройти проверку точки доступа, нужно отмотать 10 лет повсеместного распространения HTTPS, либо направить запись DNS на другой сервер, который будет выполнять переадресацию на более yahoo-образное имя или устанавливать в куки домен
yahoo
. После
подобающего патча samsung-nx-emailservice
камера начнёт подключаться и загружать фото:▍ Итог: истинное сокровище
Этот проект позволил понять и обойти механизм подтверждения точек доступа в камере Samsung WB850F путём реверс-инжиниринга одной функции. В итоге получился крохотный патч, но найти обходной путь на основе одних только трейсов пакетов было невозможно из-за использованного инженерами Samsung «метода обнаружения» точек доступа. Когда стало ясно, что именно нужно искать, тот же обходной путь удалось применить к камерам, требовавшим подключения к MSN.com, тем самым добавив в список поддерживаемых моделей EX2F, ST200F, WB3xF и WB1100F.
Тем не менее истинное сокровище всё ещё ждёт своего искателя!
Main_Image
содержит более 77 000 функций, которых увлечённому охотнику за сокровищами будет предостаточно для того, чтобы лучше разобраться в принципе работы цифровых фотокамер.Telegram-канал со скидками, розыгрышами призов и новостями IT ?
dlinyj
Написанно криво (тут не к переводчику претензия, а к автору оригинальной статьи), но в целом весьма полезное чтиво для реверса.
datacompboy
Я сперва думал, что фикс был сделан для прошивки. Только в исходнике понял, что хак был на стороне эмулятора хотспота :)