W wolnych chwilach pracowałem nad funkcjonalnością patchowania plików w moim projekcie strzykawki - syringe. Funkcja ta ukazała się pod postacią komendy patch
. Nie tylko potrafi ona nakładać IDA-owe pliki diff na standardowe binarki, ale także, co było dla mnie ważne, bezpośrednio na dane zapisane w formacie IntelHex. Takiej możliwości potrzebowałem, aby móc dokończyć całą zabawę z analizatorem logicznym Saleae Logic i finalnie zaaplikować wszystko do programu. A ostatecznie rownież wygenerować łatkę rozwiązującą problem.
Generowanie pliku diff dla firmware
Plik dif wygenerowany przez IDA-e, z modyfikacjami kodu firmware jakie wcześniej dokonałem (SaeLog #6 i SaeLog #7) mogę w łatwy sposób nałożyć na oryginalny kod zapisany w pliku hex. Wystarczy użyć polecenia patch
:
syringe patch -f IDA --target "Intel Hex" C:\SaeLog\firmware.hex C:\SaeLog\firmware.dif |
Porównując wynikowy plik zawierający zmodyfikowany kod z oryginalnym plikiem hex, przy pomocy jakiegoś rodzaju narzędzia typu diff/merge, będzie możliwe stworzenie typowego diff-a. Taki standardowy plik zmian w bardziej ludzki sposób przedstawi zmiany jakie zaszły w pliku hex, a także będzie służył wielką pomocą przy modyfikacji programu Logic.exe.
Mój diff prezentuje się następująco:
--- C:/SaeLog/firmware_org.hex Pt 2015-12-11 21:07:56 +++ C:/SaeLog/firmware.hex Pt 2015-12-11 21:08:22 @@ -323,13 +323,13 @@ :02159800D3225C :02159A00C3226A :100D74008F598D5A90E678E0FFEF4480FF90E67833 -:100D8400EFF090E67974A0F090E678E0FFEF30E0C1 -:100D9400F790E678E0FFEF20E11B90E678E0FFEFC4 +:100D8400EFF07400120B300090E678E0FFEF30E0F3 +:100D9400F790E678E0FFEF20E13B90E678E0FFEFA4 :100DA4004440FF90E678EFF090E678E0FFEF30E61D :100DB400C380F580BF90E679E55AF090E678E0FFCD -:100DC400EF30E0F790E678E0FFEF20E11B90E67863 -:100DD400E0FFEF4440FF90E678EFF090E678E0FF24 -:100DE400EF30E69080F5808C90E679E559F090E656 +:100DC400EF30E0F790E678E0FFEF30E1CDE55A70E0 +:100DD400178D5A80E090E678E030E405AD5A755AF4 +:100DE4000080D29080F5808C90E679E559F090E609 :100DF40078E0FFEF30E0F790E678E0FFEF20E11FC6 :100E040090E678E0FFEF4440FF90E678EFF090E65C :100E140078E0FFEF20E603020D7880F2020D78906F @@ -337,15 +337,15 @@ :0D0E3400E0FFEF30E60680F522020D782287 :0809FB008F428B438A448945B9 :100A0300AB43AA44A9458B478A48894990E678E0D5 -:100A1300FFEF4480FF90E678EFF090E67974A0F062 +:100A1300FFEF4480FF90E678EFF07400120B300094 :100A230090E678E0FFEF30E0F790E678E0FFEF2024 -:100A3300E11B90E678E0FFEF4440FF90E678EFF0AB +:100A3300E13B90E678E0FFEF4440FF90E678EFF08B :100A430090E678E0FFEF30E6C380F580BF90E6796B :100A5300E542F090E678E0FFEF30E0F790E678E0EB -:100A6300FFEF20E11B90E678E0FFEF4440FF90E6C4 -:100A730078EFF090E678E0FFEF30E69080F5808C39 -:100A830090E678E0FFEF4480FF90E678EFF090E6A1 -:100A93007974A1F090E678E0FFEF30E0F790E67824 +:100A6300FFEF30E1CDE542701A8B4280E090E678EB +:100A7300E030E405AB4275420080D29080F5808C73 +:100A830090E678E0FFEF4480FF90E678EFF07401A2 +:100A9300120B300090E678E0FFEF30E0F790E67855 :100AA300E0FFEF20E11F90E678E0FFEF4440FF9086 :100AB300E678EFF090E678E0FFEF20E603020A0F16 :100AC30080F2020A0F90E679E0FF8F4A90E678E021 @@ -354,9 +354,9 @@ :100AF30090E678EFF090E679E0FFAB47AA48A94982 :100B0300EF120FFE74012549F549E43548F5489085 :100B1300E678E0FFEF30E0F790E678E0FFEF20E1E2 -:100B230028E54614FFEF654A601F90E678E0FFEF83 -:100B33004440FF90E678EFF090E678E0FFEF20E6A0 -:100B430003020A0380F2020A03054A808B90E678C7 +:100B230028E54614FFEF654A601F020A3524A0FF3B +:100B330090E678E030E404EF2402FFEF90E679F0EA +:100B430022020A0380F2020A03054A808B90E678A8 :100B5300E0FFEF4440FF90E678EFF090E678E0FFA7 :0A0B6300EF30E60680F522020A0FCB :010B6D002265 |
Zmianie uległo tylko (a może aż 14) linii, ale gdy przyjrzymy się bliżej to będzie to tylko 91 bajtów, jakie zawierała oryginalna latka + zaktualizowane checksumy.
Generowanie patcha dla logic.exe
Wiem już które linie w reprezentacji IntelHex firmware ulegają zmianie, więc bez problemu mogę je zlokalizować w sekcji .rdata
pliku wykonywalnego aplikacji. Poniżej odpowiednio adres, offset w pliku i zmienione dane, jakie powinny znaleźć się pod podanym adresem:
00CE2518 008E0B18 :100D8400EFF07400120B300090E678E0FFEF30E0F3 00CE24EC 008E0AEC :100D9400F790E678E0FFEF20E13B90E678E0FFEFA4 00CE2468 008E0A68 :100DC400EF30E0F790E678E0FFEF30E1CDE55A70E0 00CE243C 008E0A3C :100DD400178D5A80E090E678E030E405AD5A755AF4 00CE2410 008E0A10 :100DE4000080D29080F5808C90E679E559F090E609 00CE22C4 008E08C4 :100A1300FFEF4480FF90E678EFF07400120B300094 00CE226C 008E086C :100A3300E13B90E678E0FFEF4440FF90E678EFF08B 00CE21E8 008E07E8 :100A6300FFEF30E1CDE542701A8B4280E090E678EB 00CE21BC 008E07BC :100A7300E030E405AB4275420080D29080F5808C73 00CE2190 008E0790 :100A830090E678E0FFEF4480FF90E678EFF07401A2 00CE2164 008E0764 :100A9300120B300090E678E0FFEF30E0F790E67855 00CE1FD8 008E05D8 :100B230028E54614FFEF654A601F020A3524A0FF3B 00CE1FAC 008E05AC :100B330090E678E030E404EF2402FFEF90E679F0EA 00CE1F80 008E0580 :100B430022020A0380F2020A03054A808B90E678A8 |
Takie dane są wystarczające do napisania prostego programu lub skryptu modyfikującego plik aplikacji. A jeśli syringe może aplikować patche z IDA, to najłatwiej byłoby pokusić się jednak o wygenerowanie takowych plików. Mogłoby to ułatwić zastosowanie finalnych modyfikacji zainteresowanym użytkownikom. Ale, że nie bardzo chciało mi się przyklepywać zmodyfikowane dane, to sobie walnąłem prosty skrypt w perlu:
#!/usr/bin/perl use strict; use warnings; my $source = 'logic.patch'; my $target = 'logic.exe'; open SH, '<', $source or die "Open file '$source' failed: $!"; open TH, '+<', $target or die "Open file '$target' failed: $!"; binmode(TH); while (<SH>) { my @rec = split(/\t/); my $pos = hex($rec[1]); chomp ($rec[2]); print "Writting data at $pos\n"; seek TH, $pos, 0 or die "Seek failed: $!"; print TH $rec[2] or die "Write failed: $!"; } close TH or die "Close file '$target' failed: $!"; close SH or die "Close file '$source' failed: $!"; |
Teraz wystarczy odpalić skrypt, aby zmiany zostały wprowadzone w pliku programu. Można pokusić się o wpakowanie jako tablicy zawartości pliku logic.patch
do skryptu, wtedy cały skrypt można traktować jako typowy patch.
Należy mieć na uwadze, że wszystkie zmiany jakie dokonywałem dotyczą wersji firmware z jaką moje urządzenie wchodziło w interakcje. Aplikacja ma zaszyte kilka wersji firmware, w którejś z części (chyba przy ekstrakcji kodu firmware z aplikacji) o tym pisałem.
Dzięki temu, że starałem się nie zwiększać kodu firmware, ale modyfikować tylko istniejący kod i upychać nowe instrukcje, aplikowanie do programu jest proste. Gdybym zwiększył kod urządzenia, wtedy sprawa byłaby trochę bardziej skomplikowana. Musiałbym gdzieś upchać nowe linie/dane kodu w IntelHex, np. w wolnych przestrzeniach, modyfikować funkcje związane z ładowaniem danych z .rdata
do pamięci i inne... mówiąc prosto, dużo więcej zabawy.
Zpatchowany program powinien działać bez zarzutu ;)