Dekompilacja aplikacji pisanych w .NET

Myślę, że o czymś takim jak dekompilacja wiele osób już słyszało. Jednak na wstępie przypomnę jeszcze czym właściwie ona jest. Jak wskazuje sama nazwa, jest to proces odwrotny do procesu kompilacji programu. Polega ona, więc na zamianie kodu maszynowego z powrotem na język wyższego poziomu. Należy przy tym zaznaczyć, że dekompiler podczas dekompilacji nie odtwarza oryginalnego kodu, jaki istniał przed kompilacją. Jednak odtwarza kod o takim samym działaniu jak ten źródłowy. W przypadku C# dekompilacja jest wyjątkowo prosta. Głównie z tego powodu, że programy nie są kompilowane bezpośrednio do kodu maszynowego, tylko do tzw. Common Intermediate Language (CLI). Kod ten zamieniany jest na kod maszynowy dopiero po uruchomieniu aplikacji na przeznaczonym do tego komputerze. Zapewnia to lepszą wydajność, oraz przenośność kompilowanych aplikacji. Jakie są tego konsekwencje? Takie, że właściwie każdy może odtworzyć oryginalny kod naszego programu, posiadając jedynie plik wykonywalny *.exe – a czasami możemy tego nie chcieć 😉

Narzędzia umożliwiające dekompilację programów opartych o .NET’a

Tego typu programów jest całkiem sporo. Do takich udało mi się dotrzeć:

  • dotPeek
  • ILSpy
  • JustDecompile
  • DisSharper
  • Mono Cecil
  • Kaliro
  • Dotnet IL Editor (DILE)
  • Common Compiler Infrastructure

Osobiście korzystam z dotPeek’a, innych rozwiązań nie testowałem. DotPeek działa i spełnia swoje zadanie, więc jak najbardziej mi wystarcza.

Przyjrzyjmy się sposobowi działania tego programu. Do testów przygotowałem prostą aplikację szyfrującą tekstową wiadomość. Wykorzystałem do tego celu klasę, którą opisywałem w tym wpisie.

Po kompilacji aplikacji i załadowania jej do dotPeeka uzyskujemy bez problemu dostęp do kodu, który jest identyczny, jak źródłowy:

Właściwie wszystko można skopiować do VS i uzyskamy w pełni działający program. Jak wiadomo nie wszystkie programy rozpowszechniane są na zasadzie open source. Kiedy ktoś coś napisze, może chcieć na tym zarobić. Kradzież kodu właściwe równa się z kradzieżą potencjalnego zarobku… Bo kto kupi program, który krąży w necie za free ze zdjętymi wszystkimi ograniczeniami? Wiadomo, każde zabezpieczenie da się złamać, jednak dostęp do niczym nie „zmąconego” kodu drastycznie ułatwia to zadanie. Czy to oznacza, że kod naszych programów jest narażony na kradzież? Nie koniecznie!

Zaciemnianie kodu sposobem na jego zabezpieczenie

Kod programu można zaciemnić. Co to konkretnie oznacza? Właściwie proces ten sprowadza się do zrobienia w kodzie jak największego bałaganu i ponownej kompilacji. Jednak w taki sposób, aby program cały czas działał tak jak powinien. Do tego zadania oczywiście istnieją specjalistyczne programy. Jeden z nich (w darmowej wersji) jest dołączony do Visual Studio Community. Nosi nazwę dotfuscator i można go odnaleźć w lokalizacji analogicznej do tej: C:\Program Files (x86)\Microsoft Visual Studio 14.0\PreEmptive Solutions\Dotfuscator and Analytics Community Edition\dotfuscator.exe

Okno tego narzędzia prezentuje się następująco:

Jego obsługa jest bardzo prosta. Sprowadza się do dodania pliku/plików projektu(*.exe,*.dll), oraz wybraniu folderu wyjściowego(Properties -> Build Settings -> Destination directory), w którym mają zostać zapisane „zaciemnione” pliki. Oprócz tego możemy zmienić bardziej szczegółowe ustawienia dotyczące procesu zaciemniania. Sugeruję zostawić wszystko na wartościach domyślnych, poza opcją „Library mode” – tą polecam odznaczyć. Zostawienie jej włączonej poskutkuje nie zaciemnieniem kodu publicznych klas/plików dll. Po ustawieniu wszystkiego możemy kliknąć na „Build -> Build project”. Po zakończeniu działania powinniśmy zostać poinformowani co konkretnie zostało zmienione w kodzie aplikacji:

Cóż… Sprawdźmy teraz jak wygląda kod po dekompilacji aplikacji w dotPeek’u:

Pierwsze co się rzuca w oczy to zmiana nazw klas na nazwy typu „a”, „b”, „c” itd, oraz zawartych w nich metod. Przyjrzyjmy się bliżej oryginalnemu kodowi:

Oraz temu zaciemnionemu:

O ile kod pozyskany po dekompilacji aplikacji, której kod nie został zaciemniony jest identyczny z oryginalnym, przez co można go dowolnie modyfikować i używać, to ten dostępny po dekompilacji kodu zaciemnionego jest… co najmniej dziwny. Nie dosyć, że wszystko nazywa się zupełnie nieintuicyjnie, to metody wewnątrz klas mają identyczne nazwy, co jest niedopuszczalne. Takiego kodu nie da się po prostu skopiować i uruchomić. Oczywiście można pokusić się o jego naprawienie, jednak w przypadku dużych projektów będzie to bardzo trudne i czasochłonne… Nawet tak bardzo, że bardziej będzie opłacało się pisać go od zera. To jest właśnie główny sens zacieniana kodu – zniechęcenie ewentualnego crackera/złodzieja do czytania kodu uzyskanego przez dekompilację.

Czy zaciemnianie kodu ma jakieś wady? Nie znam takich, poza jedną. W przypadku zagubienia kodu aplikacji nie jesteśmy w stanie w prosty sposób odtworzyć go poprzez dekompilację.

Jeżeli czegoś nie dopowiedziałem lub gdzieś się pomyliłem to jak zwykle czekam na Wasze komentarze 😉

1,992 total views, 4 views today

3 przemyślenia nt. „Dekompilacja aplikacji pisanych w .NET

  1. Wadą zaciemniania może być jeszcze nie możność użycia refleksji. Także jeżeli używamy np. WCF to mogą się pozmieniać interfejsy co może zmienić API aplikacji.

    Zastanawiam się też jak wygląda analiza zrzutów pamięci i wyjątków na produkcji w przypadku gdy dołączamy pdbki żeby miec więcej informacji.

    • Faktycznie, nie pomyślałem o tym. Refleksja po zacienieniu kodu nie będzie możliwa.
      Właśnie zaciemniłem kod swojego Cleaner’a i okazało się, że program nie ładuje pluginów(co jest logiczne akurat, bo używam tam typu dynamic). Jednak nie za bardzo wiem, dlaczego są też problemy z obsługą dodatkowych bibliotek (MvvmLight, oraz System.Windows.Interactivity). Niby program je ładuje, nie wywala przy tym wyjątków – jednak wszystko co jest z nimi związane po prostu nie działa. Szukanie przyczyny w zaciemnionym kodzie jest średnią przyjemnością 😉 Wychodzi na to, że coś tam nieźle zostało namieszane…

      Jednak zaciemnianie ma dużo więcej wad niż myślałem. Cóż, człowiek uczy się całe życie.

      Co do analizy zrzutów pamięci i wyjątków na produkcji… nie wiem jak to wygląda w praktyce, bo i nie mam skąd, ale na pewno zadania te będą przez zaciemniony kod utrudnione. Jednak pewnie i na to są jakieś sposoby. W ostateczności można zawsze wrzucić chwilowo na produkcję wersję niezaciemnioną 😉

      Pozdrawiam!

  2. Aby refleksja działała wszystkie obfuskatory mają możliwość dodania wykluczeń – jeśli część klas ma być użyta w refleksji trzeba je dodać do tej listy.

    Pozdrawiam

Możliwość komentowania jest wyłączona.