SaeLog #1: Intro
• tech • 1340 słów • 7 minut czytania
Ta notatka jest częścią serii Saleae Logic Hack. Zapoznaj się z pozostałymi wpisami.
Nowe wersje oprogramowania dla analizatora logicznego Saleae Logic mają problemy z obsługą nieoryginalnego sprzętu opartego na prostej aplikacji kostki CY7C68013A, w którym zastosowano większą pamięć adresowaną wordem. Napomknąłem o tym problemie w poprzedniej notce przy okazji testów płytki deweloperskiej z układem Cypressa w roli analizatora logicznego. Obiecałem sobie również, że spróbuje rozwiązać ten problem programowo, zamiast sprzętowo. Bez ingerencji lutownicą i wymiany pamięci EEPROM, ale za to ingerując w oprogramowanie. Zatem zabrałem się do tego celu, zaglądając do środka programu Logic, aby sprawdzić co się tam w środku dzieje.
W czasie walki z kodem software i firmware, przeprowadzając analizy i eksperymenty, tworząc już jakieś sprawozdania i nottaki z moich prac, uznałem, że nie ma sensu pisać jednej lub dwóch długawych notatek. Zamiast tego lepiej napisać kilka krótszych, skonkretyzowanych na danym zagadnieniu, fragmencie kodu lub temacie. Będzie to czytelniejsze, a także częściej pojawią się jakieś ciekawe informacje. Niestety jestem ostatnio zawalony robotą, przez co po pracy nie chce się patrzeć na kolejne wielkie połacie kodu binarnego czy asemblera, dlatego trochę czasu może upłynąć pomiędzy notatkami, nim dojdę do końca tej podróży. Ale na pewno będzie to szybsze niż czekanie i tworzenie wszystkiego w jednym wielkim kawałku. Dlatego przeredagowałem cześć tej notatki i następnej, aby zachować taką formę przekazu.
Wybrałem już identyfikator dla serii wpisów dotyczących zabaw z softem i sprzętem analizatora logicznego Saleae. Wszystkie wpisy będą oznaczone w tytule i w tagach słowem kluczem - SaeLog. Powinno to wyróżnić i ułatwić znalezienie innych notatek.
Oczywiście musi znależc się też mały disclaimer, aby nie powtarzać się w każdym wpisie. Wszystko co jest i będzie tutaj opisywane, w całej serii wpisów o Saleae Logic, prezentowane jest tylko i wyłącznie w celach czysto informacyjnych, edukacyjnych i technicznych. Nie ponoszę żadnej odpowiedzialności za ewentualne wszelkie problemy i szkody wynikłe z wykorzystania tych informacji.
~~WTF!?~~Jaki jest problem?
Po podłączeniu naszego urządzenia do portu USB i odpaleniu programu, zobaczymy ładnego crasha. Po ponownym uruchomieniu Error Report będzie chciał wysłać raport o zaistniałej sytuacji, w którego logach najważniejszą w tym momencie informacją będzie:
Error: LogicAnalyzerDevice::ReadEeprom failed after 3 tries.
Aplikacja w sytuacji wystąpienia błędu uniemożliwianego dalsze działanie zapisze niezbędne dane - log oraz zrzut pamięci procesu - umożliwiające zlokalizowanie przyczyny wystąpienia danej sytuacji i może to pomoc twórcom w naprawieniu problemu. Wszystkie te pliki raportu można znaleźć w katalogu z danymi aplikacji w katalogu profilu użytkownika:
%APPDATA%\Saleae LLC\Logic\Errors
Plik z logiem może powiedzieć nam trochę więcej o tym co się wydarzyło i co tak naprawdę było przyczyną problemu. Jego początek nie przedstawia nic ciekawego i nowego, ale pomijając nieistotne informacje o zasobach i dostępnych analizatorach, patrząc niżej, można zauważyć coś przypominającego szczątkowy callstack. Logi pokrywają się prawdopodobnie z kolejnymi ważniejszymi wywoływanymi funkcjami:
[ UsbDevice.cpp; UsbDevice::UsbDevice; 55 ]
[ UsbDevice.cpp; UsbDevice::IsFirmwareDownloaded; 96 ]
0 endpoints [ UsbDevice.cpp; UsbDevice::IsFirmwareDownloaded; 100 ]
[ UsbDevice.cpp; UsbDevice::DownloadFirmware; 123 ]
~LogicDevice() [ WindowsUsbDevice.cpp; WindowsUsbDevice::~WindowsUsbDevice; 57 ]
[ UsbDevice.cpp; UsbDevice::~UsbDevice; 60 ]
[ UsbDevice.cpp; UsbDevice::UsbDevice; 55 ]
[ UsbDevice.cpp; UsbDevice::IsFirmwareDownloaded; 96 ]
4 endpoints [ UsbDevice.cpp; UsbDevice::IsFirmwareDownloaded; 100 ]
[ LogicAnalyzerDevice.cpp; LogicAnalyzerDevice::LogicAnalyzerDevice; 21 ]
[ LogicAnalyzerDevice.cpp; LogicAnalyzerDevice::GetIdFromDevice; 227 ]
RecoverableException: WinUsb_ReadPipe failed; Pokrywająca się operacja We/Wy jest w toku.
[ WindowsUsbDevice.cpp; WindowsUsbDevice::Read; 140 ]
RecoverableException: WinUsb_ReadPipe failed; Pokrywająca się operacja We/Wy jest w toku.
[ WindowsUsbDevice.cpp; WindowsUsbDevice::Read; 140 ]
RecoverableException: WinUsb_ReadPipe failed; Pokrywająca się operacja We/Wy jest w toku.
[ WindowsUsbDevice.cpp; WindowsUsbDevice::Read; 140 ]
Z tego kawałka logu bardzo prosto można wywnioskować typowy scenariusz prowadzący do konfliktowej sytuacji. Po podłączeniu lub wykryciu urządzenia, ładowany (download) jest firmware do sprzętu. Oczywiście następuje to tylko jeśli jest to konieczne i wcześniej nie zostało wykonane. A później po utworzeniu obiektu reprezentującego urządzenie analizatora - LogicAnalyzerDevice
, następuje pobranie identyfikatora. Funkcja GetIdFromDevice
próbuje za pomocą ReadEeprom
odczytać z pamięci EEPROM wymagane dane. Urządzenie niestety nie odpowiada i syscall WinUsb_ReadPipe
zgłasza błąd we/wy. Po trzech takich próbach leci wyjątek RecoverableException
, którego nikt nie chce złapać i kończy się zabawa.
Analiza
Logi są bogate w informacje i nazwy funkcji. Jest to bardzo pomocne i przyspiesza analizę kodu aplikacji. Bez takich informacji, podobnie jak bez symboli debugowych (co zdarza się bardzo często) analiza zdeasemblowanego kodu jest trochę trudniejsza. Może nie trudna, ale wymagająca poświecenia trochę więcej czasu, analizowania ogromnych ilości kodu lub ślęczenia z debugerem, aby zlokalizować interesujące nas funkcje lub kod.
A tutaj mogę przejść od razu do działania, bo bez trudu, na podstawie stringów zawartych w kodzie i miejscu ich używania, mozna ustalić nazwy poszczególnych funkcji. A nazwy, prawie zawsze w jakimś stopniu odzwierciedlają to czym dana funkcja się zajmuje.
Tym sposobem bez problemu udało mi się zlokalizować kilka funkcji i klas, które wydawały mi się przydatne w dalszej pracy - LogicAnalyzerDevice
i jego potomne LogicDevice
i Logic16Device
, obiekty reprezentujące urządzenie USB - UsbDevice
i jego windowsowa implementacja WindowsUsbDevice
, które w większości opakowywują systemowe API dla obsługi USB - funkcje z rodziny WinUsb_*. To tylko mały zarys, ale skoro chcę wnikać jedynie w działanie blisko związane ze sprzętem, nie interesują mnie inne fragmenty oprogramowania. Przynajmniej na razie ;)
ReadEeprom
Od razu rzuciłem się do analizy kodu, zaczynając od funkcji związanych z obsługa pamięci EEPROM. Byłem pewny, że tam będzie leżał problem i ewentualne hacki w tym miejscu załatwią całą sprawę. W międzyczasie odpalając jakiś soft monitorujący wywołania systemowe, np. skądnikąd świetny API Monitor, można podpatrzeć komunikację lecącą po porcie USB. A raczej jednostronny monolog, jeśli chodzi o EEPROM.
Tutaj można łatwo zobaczyć, że aplikacja po wysyłaniu żądania do urządzenia - WinUsb_WritePipe
, próbuje odczytać odpowiedz - WinUsb_ReadPipe
z mizernym skutkiem. Widać również dane wysłane do portu, jest to 5 bajtów:
DF C6 B3 51 60
Te dane mogą obalać moją pierwszą tezę, a aplikacja może nie mieć żadnego pojęcia o pamięci EEPROM, działając wysokopoziomowo, a całą magią zajmuje się firmware na niskim poziomie. Ale mimo wszystko warto zajrzeć głębiej do kodu funkcji LogicAnalyzerDevice::ReadEeprom
i pochodnych. I o tym pewnie wkrótce napiszę, gdy tylko uda mi się całość okiełznać i przygotować materiał.
Firmware
Firmware ładowane do EZ-USB FX2LP jest kolejnym poziomem, o który chce zahaczyć. Skoro tam tkwi cała magia, to wskazane jest zajrzeć również do jego kodu. Jądro CY7C68013A oparto na wzmocnionej architekturze ‘51. Zatem rdzeń ten zmusza mnie do przypomnienia sobie, po ponad 10 latach, składni asemblera nieśmiertelnego intelowskiego procesora 8051.
Sam kod firmware jest zaszyty bezpośrednio w kodzie programu. Na próżno szukać osobnych plików binarnych z jego zawartością wśród pliko aplikacji. Przekonać się o tym możemy zaglądając do sekcji .rdata
pliku wykonywalnego.
Znajdziemy tam kod binarny firmware zapisany w postaci hexadecymalnej, a dokładniej to reprezentowany przez poszczególne linie kodu w formacie Intel HEX.
Po szybkiej analizie, mogę powiedzieć, że poszczególne linie ładowane są w jakieś bliżej nieokreślone struktury w pamięci odzwierciedlające intel-hex-owy plik. Ostatecznie po wykryciu urządzenia wykonywany jest download za pomocą funkcji UsbDevice::DownloadFirmware
. Iteruje ona po wektorze linii i korzystając z pomocniczych helperów w HexFileHelper
przewtaza to na odpowiednią postać binarną przesyłaną do układu.
Całość pewnie uda mi się dokładniej opisać po przeanalizowaniu tego fragmentu kodu. Na pewno będę to musiał zrobić, gdy będę szukał prostego mechanizmu do wyeksportowania kodu firmware z programu. W którymś z następnych wpisów będę poruszał ten temat.
Sam jeszcze nie wiem jak dokładnie będę chciał zmodyfikować kod firmware. Zmusić go tylko do działania z moją wersją pamięci EEPROM, czy może lepiej bardziej generycznie, obsługując 1- i 2- bajtowo adresowane kostki. To wszystko zależeć będzie od tego jak ten kod wygląda w środku. Zobaczymy co uda się zrobić.
To do dzieła!
Teraz nie pozostaje już nic innego jak zabrać do działania. Aktualnie mam dwie opcje, dwie drogi, którymi będę podążał. Jedna to próba obejścia problemu w software, a druga dotycząca tego samego ale w firmware. Podążając każdą z nich, myślę że pojawią się jakieś ciekawe i warte uwagi mechanizmy zaszyte w kodzie, więc o nich na pewno tez się cos pojawi.
Niestety jak wspomniałem wyżej, trochę ciężko to idzie, szczególnie, jak człowiek zawalony jest robotą. Mam nadzieje, że nowe informacje i raporty z mojego boju z software i firmware Saelae Logic będą się szybko i na bieżąco ukazywać. Niektóre elementy udało mi się w miarę szybko rozszyfrować, niektóre wymagają jeszcze trochę analizy, ale jak tylko uda mi się zebrać odpowiedni materiał to zostanie on opublikowany.
Komentarze (0)