warstwowe maskowanie: Taksonomia technik maskowania oprogramowania dla warstwowego bezpieczeństwa

Ta sekcja bada techniki maskowania dla określonych elementów kodu. Warstwa ta obejmuje większość publikacji w obszarze zaciemniania oprogramowania. Jak pokazano na Fig. 4, w zależności od tego, jakie elementy ma technika zaciemniania, dzielimy tę kategorię na pięć podkategorii: zaciemnianie układów, zaciemnianie kontroli, zaciemnianie danych funkcje zaciemniające i klasy zaciemniające.

Fig. 4
figurka4

techniki zaciemniania warstwy kodowo-elementowej

zaciemnianie układu

zaciemnianie układu koduje układ kodów lub instrukcji, zachowując nienaruszoną oryginalną składnię. W tej sekcji omówiono cztery strategie maskowania układów: bezsensowne klasyfikatory, usuwanie zbędnych symboli, oddzielanie powiązanych kodów i kody śmieciowe.

bezsensowne identyfikatory

to podejście jest również znane jako leksykalne zaciemnienie, które przekształca znaczące identyfikatory w bezsensowne. Dla większości języków programowania, przyjęcie znaczących i jednolitych zasad nazewnictwa (np. notacja węgierska (Simonyi 1999)) jest wymagane jako dobra praktyka programowania. Chociaż takie nazwy są określone w kodach źródłowych, niektóre domyślnie pozostaną w wydanym oprogramowaniu. Na przykład nazwy zmiennych globalnych i funkcji w C / C++ są przechowywane w plikach binarnych, a wszystkie nazwy Javy są zarezerwowane w kodach bajtowych. Ponieważ takie znaczące nazwy mogą ułatwić analizę programu, powinniśmy je zaszyfrować. Aby sprawić, że zaciemnione identyfikatory będą bardziej mylące, Chan and Yang (2004) zaproponowali celowo stosowanie tych samych nazw dla obiektów różnych typów lub w różnych domenach. Takie podejścia zostały przyjęte przez ProGuard (2016) jako domyślny schemat zaciemniania dla programów Java.

Usuwanie zbędnych symboli

ta strategia usuwa zbędne informacje symboliczne z opublikowanego oprogramowania, takie jak informacje debugowania dla większości propgramów (Low 1998). Poza tym istnieją inne nadmiarowe symbole dla poszczególnych formatów programów. Na przykład pliki ELF zawierają tabele symboli, które rejestrują pary identyfikatorów i adresów. Przy przyjmowaniu domyślnych opcji kompilacji do kompilacji programów C / C++, takich jak użycie LLVM (Lattner i Adve 2004), wygenerowane pliki binarne zawierają takie tabele symboli. Aby usunąć takie zbędne informacje, programiści mogą użyć narzędzia strip z Linuksa. Innym przykładem z nadmiarowymi informacjami są kody SMALI Android. Domyślnie wygenerowane kody smali zawierają informacje zaczynające się od .linia i .źródło, które można usunąć w celu zaciemnienia (Dalla Preda and Maggi 2017).

oddzielanie powiązanych kodów

program jest łatwiejszy do odczytania, jeśli jego logicznie powiązane kody są również fizycznie blisko (Collberg et al. 1997). Dlatego oddzielenie powiązanych kodów lub instrukcji może zwiększyć trudności w czytaniu. Ma zastosowanie zarówno do kodów źródłowych (np. zmiana kolejności zmiennych (Low 1998)), jak i kodów montażowych (np. instrukcje zmiany kolejności (Wróblewski 2002)). W praktyce zastosowanie bezwarunkowych skoków do przepisania programu jest popularnym podejściem do osiągnięcia tego celu. Na przykład programiści mogą tasować kody montażu, a następnie zatrudniać goto do rekonstrukcji oryginalnego przepływu sterowania (Ty i Yim 2010). Takie podejście jest popularne w przypadku kodów montażowych i bajtów Java z dostępnością instrukcji goto (Dalla Preda and Maggi 2017).

kody śmieci

ta strategia dodaje instrukcje śmieci, które nie działają. W przypadku plików binarnych możemy dodać instrukcje bez obsługi (NOP lub 0x00) (Dalla Preda and Maggi 2017; Marcelli et al. 2018). Poza tym możemy również dodawać niepotrzebne metody, takie jak dodawanie nieistniejących metod w kodach SMALI Androida (Dalla Preda and Maggi 2017). Niepotrzebne kody mogą zazwyczaj zmieniać sygnatury kodów, a tym samym uniemożliwiać statyczne rozpoznawanie wzorców.

ponieważ zaciemnienie układu nie ingeruje w oryginalną składnię kodu, jest mniej podatne na problemy ze zgodnością lub błędy. Dlatego takie techniki są najbardziej ulubione w praktyce. Ponadto techniki bezsensownych identyfikatorów i usuwania zbędnych symboli mogą zmniejszyć rozmiar programów, co dodatkowo czyni je atrakcyjnymi (ProGuard 2016). Jednak siła zaciemnienia układu jest ograniczona. Ma obiecującą odporność na ataki deobfuscation, ponieważ niektóre transformacje są jednokierunkowe, których nie można odwrócić. Jednak niektóre informacje o układzie nie mogą być zmieniane, takie jak identyfikatory metod z SDK Java. Takie szczątkowe informacje są niezbędne dla przeciwników, aby odzyskać zasłonięte informacje. Na przykład Bichsel et al. (2016) próbował deobfuscated ProGuard-zaciemnione aplikacje, i udało im się odzyskać około 80% nazw.

kontrolki zaciemniające

ten typ technik zaciemniania przekształca kontrolki kodów w celu zwiększenia złożoności programu. Można to osiągnąć za pomocą fałszywych przepływów kontrolnych, probabilistycznych przepływów kontrolnych, kontroli opartych na dyspozytorze i kontroli niejawnych.

fałszywe przepływy kontrolne

fałszywe przepływy kontrolne odnoszą się do przepływów kontrolnych, które są celowo dodawane do programu, ale nigdy nie zostaną wykonane. Może zwiększyć złożoność programu, np., w złożoności McCabe ’ a (McCabe 1976) czy Harrison metrics (Harrison and Magel 1981). Na przykład złożoność McCabe ’ a (McCabe 1976) jest obliczana jako liczba krawędzi na wykresie przepływu sterowania minus liczba węzłów, a następnie plus dwa razy połączonych elementów. Aby zwiększyć złożoność systemu, możemy albo wprowadzić nowe krawędzie, albo dodać zarówno nowe krawędzie, jak i węzły do połączonego komponentu.

aby zagwarantować nieosiągalność fałszywych przepływów kontrolnych, Collberg et al. (1997) sugerował zastosowanie nieprzezroczystych predykatów. Zdefiniowali nieprzezroczyste przewidywania jako predykat, którego wynik jest znany w czasie zaciemnienia, ale jest trudny do wydedukowania przez statyczną analizę programu. Ogólnie rzecz biorąc, nieprzezroczysty predykat może być stale prawdziwy (PT), stale fałszywy (PF) lub zależny od kontekstu (p?). Istnieją trzy metody tworzenia nieprzejrzystych predykatów: Schematy numeryczne, Schematy programowania i schematy kontekstowe.

Schematy liczbowe

Schematy liczbowe tworzą nieprzezroczyste predykaty z wyrażeniami matematycznymi. Na przykład, 7×2-1≠y2 jest stale prawdziwe dla wszystkich liczb całkowitych x i y. Możemy bezpośrednio zastosować takie nieprzejrzyste predykaty do wprowadzenia fałszywych przepływów kontrolnych. Rysunek 5a przedstawia przykład, w którym nieprzezroczysty predykat gwarantuje, że fałszywy przepływ sterowania (np. gałąź else) nie zostanie wykonany. Jednak atakujący mieliby większe szanse na ich wykrycie, jeśli często używamy tych samych nieprzezroczystych predykatów w programie zaciemnionym. Dlatego Arboit (2002) zaproponował automatyczne generowanie rodziny takich nieprzezroczystych predykatów, tak aby obfuscator mógł za każdym razem wybrać unikalny nieprzezroczysty predykat.

Fig. 5
figurka5

zaciemnienie przepływu kontrolnego z nieprzezroczystymi predykatami

innym matematycznym podejściem o wyższym bezpieczeństwie jest zastosowanie funkcji kryptograficznych, takich jak funkcja skrótu \(\mathcal {H}\) (Sharif et al. 2008) oraz szyfrowanie homomorficzne (Zhu and Thomborson 2005). Na przykład, możemy zastąpić predykat x = = c \(\mathcal {H} (x)==c_{hash}\), aby ukryć rozwiązanie x dla tego równania. Należy zauważyć, że takie podejście jest zwykle stosowane przez złośliwe oprogramowanie do unikania dynamicznej analizy programu. Możemy również użyć funkcji kryptograficznych do szyfrowania równań, które nie mogą być spełnione. Jednak takie nieprzejrzyste predykaty wiążą się z dużym obciążeniem.

aby skomponować nieprzezroczyste stałe odporne na analizę statyczną, Moser et al. (2007) zasugerował zastosowanie problemów 3-SAT, które są np-trudne. Jest to możliwe, ponieważ można mieć skuteczne algorytmy do komponowania takich trudnych problemów (Selman et al. 1996). Na przykład Tiella and Ceccato (2017) zademonstrowali, jak komponować takie nieprzejrzyste predykaty z problemami K-clique.

aby skomponować nieprzezroczyste stałe odporne na analizę dynamiczną, Wang et al. (2011) zaproponował skomponowanie nieprzezroczystych predykatów z formą nierozwiązanych przypuszczeń, które pętli na wiele razy. Ponieważ pętle są trudne dla analizy dynamicznej, podejście w przyrodzie powinno być odporne na analizę dynamiczną. Przykładami takich przypuszczeń są domysły Collatza, domysły 5x+1, domysły Matthewsa. Rysunek 5b pokazuje, jak wykorzystać przypuszczenie Collatza do wprowadzenia fałszywych przepływów kontrolnych. Bez względu na to, jak zainicjujemy x, program kończy się x=1, a originalCodes() zawsze może być wykonane.

Schematy programowania

ponieważ kontradyktoryjna analiza programów stanowi poważne zagrożenie dla nieprzezroczystych predykatów, możemy wykorzystać trudne problemy z analizą programów do skomponowania nieprzezroczystych predykatów. Collberg et al. zasugerował dwa klasyczne problemy, analizę wskaźników i programy współbieżne.

ogólnie rzecz biorąc, analiza wskaźników odnosi się do określenia, czy Dwa wskaźniki mogą lub mogą wskazywać na ten sam adres. Niektóre problemy z analizą wskaźników mogą być trudne do analizy statycznej, a nawet nierozstrzygalne (Landi and Ryder 1991). Kolejną zaletą jest to, że operacje na wskaźnikach są bardzo wydajne podczas wykonywania. Dlatego programiści mogą komponować odporne i wydajne nieprzezroczyste prognozy z dobrze zaprojektowanymi problemami analizy wskaźników, takimi jak utrzymywanie wskaźników do niektórych obiektów z dynamicznymi strukturami danych (Collberg et al. 1998a).

programy współbieżne lub programy równoległe to kolejny trudny problem. Ogólnie rzecz biorąc, równoległy obszar N poleceń ma n! różne sposoby egzekucji. Wykonanie jest nie tylko określone przez program, ale także przez stan runtime komputera hosta. Collberg et al. (1998a) zaproponował zastosowanie równoległych programów w celu wzmocnienia podejścia opartego na wskaźnikach poprzez jednoczesną aktualizację wskaźników. Majumdar and Thomborson (2006) zaproponowali zastosowanie rozproszonych programów równoległych do komponowania nieprzezroczystych predykatów.

poza tym niektóre podejścia tworzą nieprzezroczyste predykaty z sztuczkami programistycznymi, takimi jak wykorzystanie mechanizmów obsługi wyjątków. Na przykład Dolz and Parra (2008) zaproponował użycie mechanizmu try-catch do komponowania nieprzezroczystych predykatów dla.Net i Java. Wyjątki obejmują dzielenie przez zero, wskaźnik null, indeks poza zakresem, a nawet szczególne wyjątki sprzętowe (Chen et al. 2009). Oryginalna semantyka programu może być osiągnięta poprzez dopasowane Schematy obsługi wyjątków. Jednak takie nieprzejrzyste predykaty nie mają podstaw bezpieczeństwa i są podatne na zaawansowane ataki ręczne.

Schematy kontekstowe

Schematy kontekstowe mogą być stosowane do komponowania wariantów nieprzezroczystych predykatów(tj.}). Predykaty powinny posiadać pewne deterministyczne właściwości, takie, że mogą być stosowane do zaciemniania programów. Na przykład, zasłony i et al. (2009) zaproponowano skomponowanie takich nieprzezroczystych predykatów, które są niezmienne pod ograniczeniem kontekstowym, np. nieprzezroczysty predykat x mod3==1 jest stale prawdziwy, jeśli x mod3:1?X++: x=x+3. Palsberg et al. (2000) proposed dynamic opaque predicates, which include a sequence of correlated predicates. Wynik oceny każdego predykatu może się różnić w każdym przebiegu. Jednak dopóki predykaty są skorelowane, zachowanie programu jest deterministyczne. Rysunek 5c przedstawia przykład dynamicznych nieprzezroczystych predykatów. Bez względu na to, jak inicjalizujemy *p I * q, program jest równoważny y = x + 3, x = y+3.

opór fałszywych przepływów kontrolnych zależy głównie od bezpieczeństwa nieprzezroczystych predykatów. Idealną właściwością bezpieczeństwa dla nieprzejrzystych predykatów jest to, że wymagają one najgorszego wykładniczego czasu do złamania, ale tylko czasu wielomianowego do skonstruowania. Zauważ, że niektóre nieprzejrzyste predykaty są zaprojektowane z takimi problemami bezpieczeństwa, ale mogą być implementowane z wadami. Na przykład problemy 3-SAT zaproponowane przez Ogiso et al. (2003) są oparte na trywialnych Ustawieniach problemów, które można łatwo uprościć. Jeśli takie nieprzejrzyste predykaty zostaną właściwie wdrożone, będą one obiecujące, że będą odporne.

probabilistyczne przepływy kontrolne

fałszywe przepływy kontrolne mogą powodować problemy ze statyczną analizą programu. Są one jednak podatne na dynamiczną analizę programu, ponieważ fałszywe przepływy kontrolne są nieaktywne. Idea probabilistycznych przepływów kontrolnych przyjmuje inną strategię walki z zagrożeniem (Pawłowski et al. 2016). Wprowadza replikacje przepływów sterujących z tą samą semantyką, ale inną składnią. Po otrzymaniu tego samego wejścia kilka razy, program może zachowywać się inaczej przez różne czasy wykonania. Technika ta jest również przydatna do zwalczania ataków bocznych kanałów (Crane et al. 2015).

zauważ, że strategia probabilistycznych przepływów kontrolnych jest podobna do fałszywych przepływów kontrolnych z kontekstowymi nieprzejrzystymi predykatami. Ale mają one inny charakter, ponieważ kontekstowe nieprzejrzyste predykaty wprowadzają martwe ścieżki, chociaż nie wprowadzają kodów śmieciowych.

sterowanie oparte na Dyspozytorze

sterowanie oparte na dyspozytorze określa kolejne bloki kodów do wykonania w czasie wykonywania. Takie elementy sterujące są niezbędne do zaciemnienia przepływu sterowania, ponieważ mogą ukryć oryginalne przepływy sterujące przed statyczną analizą programu.

jednym z głównych metod zaciemniania opartych na dyspozytorze jest spłaszczanie przepływu sterowania, które przekształca kody głębokości w płytki o większej złożoności. Wang et al. (2000) po pierwsze zaproponował to podejście. Rysunek 6 pokazuje przykład z ich papieru, który przekształca pętlę while w inną formę za pomocą switch-case. Aby zrealizować taką transformację, pierwszym krokiem jest przekształcenie kodu w równoważną reprezentację za pomocą instrukcji if-then-goto, jak pokazano na Fig. 6; Następnie modyfikują instrukcje goto za pomocą instrukcji switch-case, jak pokazano na Fig. 6. W ten sposób oryginalna semantyka programu jest realizowana pośrednio poprzez sterowanie przepływem danych zmiennej switch. Ponieważ kolejność wykonywania bloków kodu jest określana dynamicznie przez zmienną, nie można znać przepływów sterujących bez wykonania programu. Cappaert and Preneel (2010) sformalizowali spłaszczenie przepływu sterowania jako wykorzystujące węzeł dyspozytorski (np. przełącznik), który kontroluje następny blok kodu, który ma zostać wykonany; po wykonaniu bloku kontrola jest przenoszona z powrotem do węzła dyspozytorskiego. Poza tym istnieje kilka ulepszeń spłaszczania przepływu kodu. Na przykład, aby zwiększyć odporność na statyczną analizę programu na zmiennej przełącznika, Wang et al. (2001) zaproponowano wprowadzenie problemów analizy wskaźników. Aby dodatkowo skomplikować program, Chow et al. (2001) zaproponował dodanie fałszywych bloków kodu.

Fig. 6
figurka6

podejście spłaszczania sterowania przepływem zaproponowane przez Wang et al. (2000)

László i Kiss (2009) zaproponowali mechanizm spłaszczania sterowania przepływem do obsługi określonej składni C++, takiej jak try-catch, while-do, continue. Mechanizm opiera się na abstrakcyjnym drzewie składni i wykorzystuje ustalony wzór układu. Dla każdego bloku kodu, który ma zostać zatuszowany, konstruuje instrukcję while W pętli zewnętrznej i związek typu switch-case wewnątrz pętli. Związek switch-case implementuje oryginalną semantykę programu, a zmienna switch jest również wykorzystywana do zakończenia zewnętrznej pętli. Cappaert and Preneel (2010) odkryli, że mechanizmy mogą być podatne na analizę lokalną, tj. zmienna przełącznika jest natychmiast przypisywana tak, że przeciwnicy mogą wywnioskować następny blok do wykonania, patrząc tylko na bieżący blok. Swvar = swVar+1) zamiast bezpośredniego przypisania (np. swVar=3), zastępując przypisanie za pomocą if-else jednolitym wyrażeniem przypisania i stosując funkcje jednokierunkowe w obliczaniu następcy podstawowego bloku.

oprócz spłaszczania przepływu sterowania, istnieje kilka innych badań zaciemniających opartych na dyspozytorach (np. (Linn and Debray 2003; GE et al. 2005; Zhang et al. 2010; Schrittwieser i Katzenbeisser 2011)). Linn and Debray (2003) zaproponowali zaciemnienie plików binarnych z funkcjami gałęzi, które kierują wykonaniem w oparciu o informacje o stosie. Podobnie Zhang et al. (2010) zaproponował zastosowanie funkcji gałęziowych do zaciemniania programów zorientowanych obiektowo, które definiują Ujednolicony styl wywoływania metod z pulą obiektów. W celu zwiększenia bezpieczeństwa takich mechanizmów, GE et al. (2005) zaproponował ukrycie informacji kontrolnych w innym samodzielnym procesie i zastosowanie komunikacji międzyprocesowej. Schrittwieser and Katzenbeisser (2011) zaproponowali zastosowanie zróżnicowanych bloków kodu, które implementują tę samą semantykę.

maskowanie oparte na Dyspozytorze jest odporne na analizę statyczną, ponieważ Ukrywa wykres przepływu sterowania programu. Jest jednak podatny na dynamiczną analizę programu lub podejścia hybrydowe. Na przykład, Udupa et al. (2005) zaproponował podejście hybrydowe w celu ujawnienia ukrytych przepływów kontrolnych za pomocą analizy statycznej i analizy dynamicznej.

Implicit controls

ta strategia przekształca jawne instrukcje kontroli na niejawne. Może to utrudniać inżynierom odwrotnym zajęcie się prawidłowymi przepływami sterującymi. Na przykład możemy zastąpić instrukcje sterowania kodami montażu (np. jmp i jne) kombinacją mov i innych instrukcji, które implementują tę samą semantykę sterowania (Balachandran and Emmanuel 2011).

zauważ, że wszystkie istniejące metody zaciemniania przepływu sterowania koncentrują się na transformacji na poziomie syntaktycznym, podczas gdy ochrona na poziomie semantycznym była rzadko omawiana. Chociaż mogą wykazywać pewną odporność na ataki, ich skuteczność maskowania w zakresie ochrony semantycznej pozostaje niejasna.

zaciemnianie danych

obecne techniki zaciemniania danych koncentrują się na typowych typach danych, takich jak liczby całkowite, ciągi znaków i tablice. Możemy przekształcać dane poprzez dzielenie, scalanie, proceduryzację, kodowanie itp.

dzielenie/scalanie danych

dzielenie danych rozkłada informacje o jednej zmiennej na kilka nowych zmiennych. Na przykład zmienna logiczna może być podzielona na dwie zmienne logiczne i wykonywanie na nich operacji logicznych może uzyskać pierwotną wartość.

scalanie danych, z drugiej strony, agreguje kilka zmiennych w jedną zmienną. Collberg et al. (1998b) zademonstrował przykład, który łączy dwie 32-bitowe liczby całkowite w jedną 64-bitową liczbę całkowitą. Ertaul i Venkatesh (2005) zaproponowali inną metodę, która pakuje kilka zmiennych w jedną przestrzeń z dyskretnymi logarytmami.

procedura danych

procedura danych zastępuje dane statyczne wywołaniami procedur. Collberg et al. (1998b) zaproponował zastąpienie łańcuchów funkcjami, które mogą wytwarzać wszystkie łańcuchy poprzez podanie wartości parametrów paticular. Zasłona i in. (2004) zaproponował kodowanie danych numerycznych za pomocą dwóch funkcji odwrotnych f I g. Aby przypisać wartość v do zmiennej i, przypisujemy ją do wstrzykiwanej zmiennej j jako j=f (v). Aby użyć i, wywołujemy zamiast tego g (j).

kodowanie danych

kodowanie danych koduje dane za pomocą funkcji matematycznych lub szyfrów. Ertaul and Venkatesh (2005) zaproponowali kodowanie ciągów za pomocą Szyfrów afinicznych (np. szyfr Casera) i stosowanie dyskretnych logarytmów do pakowania słów. Fukushima i in. (2008) zaproponował, aby zakodować jasne liczby z wyłącznością lub operacji, a następnie odszyfrować wynik obliczeń przed wyjściem. Kovacheva (2013) zaproponował szyfrowanie ciągów za pomocą szyfru RC4, a następnie odszyfrowywanie ich podczas działania.

transformacja tablicy

tablica jest najczęściej stosowaną strukturą danych. Aby zaciemnić tablice, Collberg et al. (1998b) omówił kilka przekształceń, takich jak dzielenie jednej tablicy na kilka podparć, łączenie kilku tablic w jedną tablicę, składanie tablicy w celu zwiększenia jej wymiaru lub spłaszczanie tablicy w celu zmniejszenia wymiaru. Ertaul i Venkatesh (2005) zasugerowali przekształcenie indeksów tablic za pomocą funkcji złożonych. Zhu i in. (2006); Zhu (2007) zaproponował zastosowanie homomorficznego szyfrowania do transformacji tablic, w tym zmiany indeksu, składania i schlebiania. Na przykład, możemy tasować elementy tablicy za pomocą i∗m mod n, gdzie i jest oryginalnym indeksem, N jest rozmiarem oryginalnej tablicy, A M I n są względnie pierwsze.

metody zaciemniające

metoda inline/outline

metoda jest niezależną procedurą, którą można wywołać za pomocą innych instrukcji programu. Metoda inline zastępuje oryginalne wywołanie proceduralne samym ciałem funkcji. Zarys metody działa w przeciwny sposób, który wyodrębnia sekwencję instrukcji i abstrahuje metodę. Są to dobre firmy, które mogą zaciemnić oryginalną abstrakcję procedur (Collberg et al. 1997).

klonowanie metody

jeśli metoda jest mocno wywoływana, możemy utworzyć replikacje metody i losowo wywołać jedną z nich. Aby zmylić przeciwstawną interpretację, każda wersja replikacji powinna być w jakiś sposób unikalna, na przykład poprzez przyjęcie różnych przekształceń zaciemnienia (Collberg et al. 1997) lub różne podpisy (Ertaul and Venkatesh 2004).

metoda agregacji / rozpraszania

idea jest podobna do zaciemniania danych. Możemy agregować nieistotne metody w jedną metodę lub rozpraszać metodę na kilka metod (Collberg et al. 1997; Low 1998).

metoda proxy

to podejście tworzy metody proxy, aby zmylić inżynierię odwrotną. Na przykład, możemy utworzyć Serwery Proxy jako publiczne statyczne metody z randomizowanymi identyfikatorami. Może istnieć kilka różnych proxy dla tej samej metody (Dalla Preda and Maggi 2017). Podejście jest niezwykle przydatne, gdy sygnatury metody nie mogą być zmienione (Protsenko and Muller 2013).

klasy zaciemniające

klasy zaciemniające mają podobne pomysły z metodami zaciemniającymi, takimi jak dzielenie i klonowanie (Collberg et al. 1998b). Ponieważ jednak Klasa istnieje tylko w obiektowych językach programowania, takich jak JAVA i.NET, omawiamy je jako unikalną kategorię. Poniżej prezentujemy główne strategie zaciemniania klas.

upuszczanie modyfikatorów

programy obiektowe zawierają modyfikatory (np., publiczne, prywatne) w celu ograniczenia dostępu do klas i członków klas. Upuszczanie modyfikatorów usuwa takie ograniczenia i upublicznia wszystkich członków (Protsenko and Muller 2013). Takie podejście może ułatwić implementację innych metod zaciemniania klas.

Klasa dzielenia/łączenia

ideą łączenia / dzielenia jest zaciemnienie intencji programistów podczas projektowania klas (Sosonkin et al. 2003). Podczas łączenia klas możemy przenieść zmienne lokalne lub lokalne grupy instrukcji do innej klasy (Fukushima et al. 2003).

spłaszczanie hierarchii klas

interfejs jest potężnym narzędziem dla programów obiektowych. Podobnie jak proxy metody, możemy tworzyć proxy dla klas z interfejsami (Sosonkin et al. 2003). Jednak silniejszym sposobem jest zerwanie pierwotnej relacji dziedziczenia między klasami z interfejsami. Pozwalając każdemu węzłowi podzbioru w hierarchii klas implementującej ten sam interfejs, możemy spłaszczyć hierarchię (Foket et al. 2012).

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.