You are on page 1of 115

Diplomarbeit

Universit at Siegen Naturwissenschaftlich-Technische Fakult at Department Elektrotechnik und Informatik

Denition und prototypische Implementierung einer berpru statischen Informationsussanalyse zur U fung des Datenschutzes von Java-basierten Programmen und insbesondere Apps

Julia Sigmund

Gutachter Prof. Dr. Roland Wism uller Dipl.-Inform. Simon Meurer Betriebssysteme und verteilte Systeme

31. Oktober 2012

Adavit Eidesstattliche Erkl arung

Ich versichere, dass ich die Arbeit ohne fremde Hilfe und ohne Benutzung anderer als der angegebenen Quellen angefertigt habe. Die Arbeit in gleicher oder ahnlicher Form wurde noch keiner anderen Pr ufungsbeh orde vorgelegt und von dieser als Teil einer Pr ufungsleistung angenommen. Alle Ausf uhrungen, die w ortlich oder sinngem a u bernommen wurden, sind als solche gekennzeichnet.

Siegen, den 31. Oktober 2012

ii

Danksagung

Es ist ein lobenswerter Brauch. Wer was Gutes bekommt, der bedankt sich auch. Wilhelm Busch

An dieser Stelle m ochte ich mich recht herzlich bei meinen beiden Betreuern, Herr Prof. Dr. Roland Wism uller und Herr Dipl. Inform. Simon Meurer, die mir w ahrend der Fertigstellung dieser Arbeit zur Seite standen, bedanken. Ohne die fachliche Betreuung, Motivation und Geduld w are diese Arbeit nicht m oglich gewesen. Weiterer Dank gilt allen, die mich w ahrend meines Studiums in Freundschaft begleitet haben. Nicht zu letzt gilt das gr ote Dankesch on meinen Eltern Gerd und Marion sowie meinem Freund Bastian. Die Unterst utzung, die ich von Euch in jeglicher Hinsicht erfahren durfte, hat mein Studium erst m oglich gemacht. Danke!

Julia Sigmund, Oktober 2012

iii

Inhaltsverzeichnis

Inhaltsverzeichnis
Einleitung 1 Grundlagen 1.1 Virtuelle Maschinen . . . . . . . . . . . . . . . . 1.2 Java Bytecode . . . . . . . . . . . . . . . . . . . 1.3 Dalvik Bytecode . . . . . . . . . . . . . . . . . 1.4 Datenussanalyse und Informationsussanalyse 1.5 Klassikation von Programmanalysen . . . . . . 2 Frameworks 2.1 Jif . . 2.2 Wala . 2.3 Soot . 1 10 10 15 17 19 28

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

zur Datenussanalyse 31 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 Soot . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 35 43 48

3 Datenuss- und Points-to-Analyse mit 3.1 Soot Bytecode Formate . . . . . . . 3.2 Aufrufgraphen . . . . . . . . . . . . 3.3 Points-to-Analyse . . . . . . . . . .

4 Realisierung 62 4.1 Informationsussanalyse . . . . . . . . . . . . . . . . . . . . . . . . 62 4.2 Points-to-Analyse . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5 Ergebnisse Fazit Literaturverzeichnis 90 99 103

iv

Einleitung

Einleitung
Mobile Endger ate sind heute aus dem Alltag vieler Menschen nicht mehr wegzudenken. Die Nutzung der vielseitigen Ger ate ist zu einer Selbstverst andlichkeit, zur Normalit at, geworden. Dabei steht jedoch nicht mehr die st andige Erreichbarkeit des Nutzers im Vordergrund, die einst der Kaufmagnet f ur potentielle Kunden war, heute geht es viel mehr um die Vereinigung von unterschiedlichsten Funktionen in einem kompakten Ger at, was man jeder Zeit mit sich f uhren kann. In den meisten F allen handelt es sich bei den mobilen Endger aten um Smartphones oder Tablet-PCs verschiedenster Hersteller, die schon bei der Auslieferung mit unterschiedlichen Betriebssystemen ausgestattet sind. Einige Programme bzw. Applikationen (im Folgenden auch: Apps) sind bereits installiert, damit die Standardfunktionalit at eines Ger ates gew ahrleistet werden kann (bpsw. Telefonie- und SMS-Dienste, Internetbrowser, Mediaplayer etc.). Die bereits vorhandenen Apps k onnen durch die Installation weiterer Programme von anderen Anbietern, so genannten third-party applications, erg anzt und teilweise auch ersetzt werden. Diese Apps werden mit Hilfe von Onlineplattformen vertrieben, die entweder direkt vom Entwickler der Betriebssysteme oder auch von alternativen Anbietern bereitgestellt werden. Die bekanntesten und erfolgreichsten Betriebssysteme f ur mobile Ger ate sind Apples iOS [4], das marktf uhrende OS Android, entwickelt von der Open Handset Alliance [2], Blackberrys Research In Motion [63] Nokias Symbian [52] und Microsofts Windows Phone / Mobile [49]. Die Verkaufszahlen und Marktverteilung f ur die Betriebssysteme werden in Tabelle 0.1 und Abbildung 0.1 dargestellt, dabei handelt es sich um Analysen der Agenturen Gartner (USA) und comScore f ur den BITKOM (Bundesverband Informationswirtschaft, Telekommunikation und neue Medien e.V.). Alle Anbieter erm oglichen ihren Kunden die Erweiterung des Systems durch die Installation von Apps, jedoch mit unterschiedlichen Rahmenbedingungen. Abgesehen von den eigenen Apps der Betriebssystemhersteller handelt es sich ausschlielich um Programme von Drittanbietern, die kleine und groe renommierte Firmen sein k onnen, aber auch Privatpersonen oder Institutionen.

Einleitung

Abbildung 0.1: Die Android System Architektur Quelle: BITKOM (Mai 2012), [11]

Der Aufwand, eine App zu entwickeln, ist je nach Betriebssystem und Funktionalit at nicht unbedingt hoch, so dass viele Personen mit Hilfe eines Entwickleraccounts Anwendungen programmieren k onnen. Leider entsteht durch die Vielzahl an Anbietern auch eine Vielzahl an Programmen, die als schadhaft klassiziert werden m ussen. Mit Hilfe von Apps lassen sich leicht viele Informationen der Nutzer kopieren, die dann verkauft werden k onnen. Je nach Zugrism oglichkeit auf das Ger at, k onnen Daten wie z.B. die Telefonnummer, Kontaktdaten oder die aktuelle Position ausgelesen und u a. ber eine Internetverbindung an einen Server o. gesendet werden. Ebenso existieren Programme, die kostenpichtige Dienste beinhalten, die, ohne dass der Nutzer es bemerkt, ausgef uhrt werden. Das Senden von Kurznachrichten oder der Aufbau einer Telefonverbindung an eine kostenpichtige Rufnummer kann in Form eines Hintergrundprozesses ausgef uhrt werden, ohne dass der Nutzer zu diesem Zeitpunkt davon in Kenntnis gesetzt wird. Um dem Missbrauch durch die Apps entgegen zu wirken, kontrolliert bspw. der Anbieter Apple jede App, die von einem Entwickler zum Verkauf angeboten werden soll, in dem er die Installation des Programms auf den Ger aten nur u ber die eigene Onlineplattform App Store erm oglicht und vor der Ver oentlichung dort die Apps nach bestimmten Kriterien u berpr u ft [70]. Trotz einer Uberpr ufung der Apps k onnen auch im App Store Programme, die Sicherheitsl ucken aufweisen, nicht g anzlich ausgeschlossen werden. Bspw. konnte 2011 ein sch adliches Programm namens Instastock, trotz der Kontrollen von Apple, durch den Sicherheitsexperten Charlie Miller in den App Store eingeschleust werden [53]. Weitere Angrie, die auf den Apple Store und damit iOS durchgef uhrt

Einleitung Operating System Android iOS Symbian Blackberry Bada Microsoft Others Total Operating System Android iOS Symbian Blackberry Bada Microsoft Others Total Q2 2012 Units 98,529.3 28,935.0 9,071.5 7,991.2 4,208.8 4,087.0 863.3 153,686.1 Q2 2011 Units 46,775.9 19,628.8 23,853.2 12,652.3 2,055.8 1,723.8 1,050.6 107,740.4 Q2 2012 Market Share 64.1% 18.8% 5.9% 5.2% 2.7% 2.7% 0.6% 100.0% Q2 2011 Market Share 43.4% 18.2% 22.1% 11.7% 1.9% 1.6% 1.0% 100.0%

Tabelle 0.1: Weltweite Marktverteilung mobiler Endger ate, sortiert nach Betriebssystem in 2Q12 und 2Q11 (Angabe u ate in 1000) ber Ger Quelle: Gartner (August 2012), [28]

wurde, sind in [24] aufgef uhrt. Um Programme auf ein mit Android, dem marktf uhrenden Betriebssystem, betriebenes Smartphone oder Tablet zu installieren, wird empfohlen, den Google Play Store zu nutzen. Dabei handelt es sich um die ozielle Downloadplattform von Google f ur die Android-Ger ate. Die Android Entwickler bzw. Google u berpr ufen die Apps nicht vor der Ver oentlichung im Play Store, es wurden jedoch Mechanismen wie z.B. der Malware Scanner Bouncer eingef uhrt, der die bereits vorhandenen Apps im Store und auch, je nach Einstellung des Ger ates, die bereits installierten Programme auf dem Smartphone u uft und in vielen F allen berpr schadhafte Software meldet.[13] Die Uberpr ufung der Apps wird bei Android teilweise auf den Nutzer umgelegt. Bei der Installation einer App werden dem Nutzer die Berechtigungen (engl. Permissions), die die App ben otigt, um ausgef uhrt zu werden, angezeigt. Dabei

Einleitung handelt es sich z.B. um die Berechtigung f ur Internetzugri oder die Bestimmung des aktuellen Ortes durch die Ortungsmechanismen des Ger ats. Wenn eine App keine expliziten Berechtigungen anfordert, darf sie standardisiert auf keine Daten oder das Internet etc. zugreifen. Erst, wenn der Nutzer der Installation und damit den geforderten Berechtigungen zustimmt, wird die App auf dem Ger at eingebunden und die geforderten Dienste werden ggf. genutzt. Das Smartphone-Betriebssystem Android wurde und wird durch die Open Handset Alliance, die aus groen Teilen aus Mitgliedern von Google besteht, entwickelt und vorangetrieben. Abgesehen vom Kernel, welcher unter der GPL 2 Lizenz steht, ist das Android System durch die Apache-Lizenz lizenziert. Android beruht auf einem Linux-Kernel, den (Core-) Libraries sowie einer Dalvik Virtual Machine, die im weiteren Verlauf dieser Arbeit noch genauer erl autert wird. Die oberste Ebene der Android-Architektur ist das Application Framework bzw. die Apps selber, wie Abbildung 0.2 zeigt. Um eine Anwendung f ur Android zu entwickeln, wird die Programmiersprache Java, XML und das Android SDK genutzt. Nach der Fertigstellung der Anwendung wird der Java-Code durch den Java-Compiler in .class-Dateien umgesetzt. Das im Android-SDK mitgelieferte Dalvik Cross Assembler-Tool wandelt daraufhin die .class-Dateien in Dalvik-Bytecode um, damit die Anweisungen an die Hardwareabstraktionsschicht weitergegeben werden k onnen. Eine Android-App besteht aus mehreren Komponenten, die in [61] n aher erl autert werden. Die Hauptkomponente ist dabei die AndroidManifest.xml-Datei, in der alle Einstellungen f ur die App, wie die Sprache oder die ben otigten Berechtigungen, zum Ausf uhren des Programms gespeichert sind. Die Manifestdatei kann und muss ggf. durch den Entwickler je nach Funktionalit at ge andert werden, da eine App pauschal ohne die Einforderung der Nutzung bestimmter Dienste durch das Android zu Grunde liegende Sandboxing, dem Abschirmen der App hinsichtlich der anderen Apps und dem Betriebssystem, nicht auf andere Funktionalit aten und Apps zugreifen kann. Eine Berechtigung zur Nutzung der App kann nach [34] innerhalb der Datei wie in Listing angefordert werden.
1 2 3 4 5

<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.app.myapp" > <uses-permission android:name="android.permission.RECEIVE_SMS" /> ... </manifest>

Die oben angeforderte Berechtigung erlaubt einer App, Kurznachrichten, SMS, zu empfangen.

Einleitung

Abbildung 0.2: Die Android System Architektur Quelle: Android Open Source Project [61]

Einleitung Abbildung 0.3 zeigt einen Teil der Berechtigungsanzeige auf einem Smartphone, bei der Installation einer App. Um die detaillierten Informationen u otigten ber die ben Berechtigungen zu erhalten, muss jede Berechtigung einzeln angeklickt werden.

Abbildung 0.3: Ansicht der Berechtigungen auf einem Smartphone Samsung Galaxy S II, Quelle: Google Play Store, [30]

Bei diesen ben otigten Berechtigungen handelt es sich um die App Whatsapp aus der Rubrik Kommunikation mit 50 - 100 Millionen get atigten Downloads bzw. Installationen (Stand 14.10.2012) im Google Play Store. Diese App fordert Berechtigungen ein, wie die Erlaubnis zur Einsicht in die Passw orter des Systems, zum Versenden von ggf. kostenpichtigen SMS bzw. zum Aufbau von kostenpichtigen Anrufen. Trotzdem wurde sie, wie die Downloadzahl zeigt, weit verbreitet und oft installiert. Dieses Downloadverhalten spiegelt den allgemeinen Umgang mit den Berechtigungen der Android-Apps wieder. Auch wenn die Berechtigungen direkt vor der Installation angezeigt werden, liegt die Vermutung nahe, dass in den meisten F allen trotzdem die Installation auf den Ger aten erfolgt. Ein Grund daf ur k onnte die, in die Suche nach der passenden App, vorangegangene investierte Zeit des Nutzers sein. Als ein weiterer Grund daf ur kann angenommen werden, dass die Benachrichtigungen u ur den Nutzer verst andlich ber die Berechtigungen nicht immer direkt f sind, da Formulierungen wie Informationen zur Authentizierung eines Kontos verwenden - Erm oglicht der App, Authentizierungs-Tokens anzufordern oder Uneingeschr ankter Internetzugri - Erm oglicht der App, Netzwerk-Sockets einzurichten verwendet werden.

Einleitung Vielen Nutzern werden dadurch die eigentlichen Folgen, die sie mit der Freigabe der Berechtigungen zulassen, nicht bewusst, da f ur die meisten Nutzer das Zusammenspiel der Berechtigungen nicht nachvollziehbar ist. Ohne eine Analyse des Programms kann der Nutzer in dem meisten F allen auch nicht gezielt wissen, ob und wenn ja, welche pers onlichen Daten durch die Zustimmung der eingeforderten Berechtigungen ausgelesen und z.B. an einen Server gesendet werden. Nicht alle Berechtigungen, die z.B. den uneingeschr ankten Zugri auf das Internet und das Auslesen von sensitiven Daten fordern, nutzen sie auch in einem schadhaften Zusammenhang. Um eine Aussage u ber die Nutzung der eingeforderten Daten treen zu k onnen, muss eine Analyse der App durchgef uhrt werden. Bei mittlerweile u ber 25 Milliarden Downloads im Google Play Store [12] liegt die Schlussfolgerung nahe, dass die Nutzung von Apps eine immer gr oere Bedeutung bekommen hat. Um das Nutzungsverhalten und das Bewusstsein der Personen, die die Apps downloaden, hinsichtlich m oglicher schadhafter Apps zu ver andern, wurde das Android PErmission Filter System, APEFS [48], entworfen. Im Folgenden wird genauer auf APEFS eingegangen sowie die daraus resultierende Problemstellung und Motivation dieser Arbeit erl autert.

Problemstellung und Motivation


Bei APEFS handelt es sich um eine Infrastruktur zur Filterung von Apps anhand ihrer Berechtigungen, d.h. es ist bisher m oglich, die Apps nach bestimmten Kategorien zu sortieren und anhand der ben otigten Berechtigungen zu ltern, bpsw. k onnen alle Apps angezeigt werden, die keinen Internetzugang ben otigen. In Abbildung 0.4 wird das Ergebnis einer Filterung gezeigt, bei der die Apps aus der Liste entfernt wurden, die pers onliche Daten abfragen wollen. Ebenso k onnen eigene Prole zur Filterung angelegt werden, was ebenfalls in Abb. 0.4 gezeigt wird. APEFS wird als eine eigenst andige App auf dem Smartphone oder in Verbindung mit einem Server zur Optimierung der Performanz angeboten. Doch APEFS soll nicht nur zur Filterung anhand von Berechtigungen dienen, es sollen ebenfalls Analysen u atigt ber den Informationsuss der Programme get werden. Viele Apps ben otigen nur eine Berechtigung f ur eine Internetverbindung, um das Programm mit Hilfe eingeblendeter Werbung, die aus dem Internet geladen wird, zu nanzieren. Jedoch ist bei diesen Programmen nicht ausgeschlossen, dass ggf. in Kombination mit anderen Berechtigungen eine Ubermittlung sensibler Daten an einen Server stattndet. Es kann aber nicht denitiv davon ausgegangen werden, dass es sich hier automatisch um eine schadhafte Software handelt. Daher erscheint es notwendig, den Nutzer u ber das Verhalten des Programms auf-

Einleitung zukl aren und die Apps ggf. zu klassizieren. Bei einer Informationsussanalyse der Programme ist es auf Grund der Resultate m oglich, den Nutzer u ogliche ber m Ausgaben der pers onlichen Daten der App (im Folgenden genannt Leak) zu informieren. Dazu wird der Bytecode der App in Java-Bytecode transformiert. Dieser Bytecode soll dann anhand einer Informationsussgleichung analysiert werden, damit der Informationsuss, also die eingehenden und ausgehenden Werte an einer Stelle im Programm, f ur jedes Codestatement der App u Im berwacht werden kann.

Abbildung 0.4: Links: Ansicht der Prole in APEFS, rechts: gelterte Ergebnisse der Apps. Quelle: [69]

Rahmen dieser Arbeit wird ein Prototyp f ur die Analyse von Java-Bytecode mit Hilfe eines unterst utzenden Frameworks zur Datenussanalyse erarbeitet. Dazu wird zun achst eine Informationsussanalyse, die den Fluss der sensitiven Daten innerhalb des Programms ermittelt, implementiert und im zweiten Schritt eine Points-to-Analyse, zur Ermittlung der Zeigerziele, erg anzt. Die Kombination dieser beiden Analysen soll die Falsch-Positiv-Rate der reinen Datenussanalyse minimieren. Bei der Falsch-Positiv-Rate handelt es sich hier um die Variablen, die f alschlicherweise als sensitiv markiert wurden, obwohl sie es gar nicht sind. Bei einer groen Falsch-Positiv-Rate spricht man von einer konservativen Analyse, was sich im Falle einer statischen Programmanalyse, die hier vorliegt, nicht vermeiden, jedoch optimieren l asst.

Einleitung

Gliederung der Arbeit


Kapitel 1 zeigt die Grundlagen der Arbeit auf. Zun achst werden die unterschiedlichen Typen von virtuellen Maschinen allgemein und dann speziell die Java und Dalvik Virtual Maschine erl autert. Daraufhin folgt eine Einf uhrung in die jeweiligen Bytecodeformate und in die grundlegenden Konzepte der Daten und Informationsussanalyse. Dazu geh oren die Vorstellung des Aufbau eines Kontrollussgraphens, verschiedene Analysewerkzeuge sowie die Klassikation von Programmanalysen. Kapitel 2 gibt einen kurzen Uberblick u ber drei vorhandene bekannte Frameworks zur Daten- und Informationsussanalyse von Java-Bytecode, Jif, WALA und Soot. Kapitel 3 beschreibt die verscheidenen Bytecode-Formate, die innerhalb von Soot genutzt werden k onnen, die M oglichkeiten der Aufrufgraphgenerierung sowie die Zeigeranalyse und Points-to-Analyse mit Soot. Dazu werden zuerst die vier Formate Baf, Jimple, Shimple und Grimp im Vergleich zu Java Bytecode gezeigt und erl autert. Es folgt ein allgemeiner Uberblick u ber die Erstellung von Aufrufgraphen sowie die Darstellung der verschiedenen Arten, nach denen Aufrufgraphen innerhalb von Soot mit Hilfe von SPARK generiert werden k onnen. Der letzte Teil dieses Kapitels befasst sich mit der Pointer- und Points-to-Analyse, indem verschiedene Algorithmen dargestellt und im Zusammenhang mit Soot gebracht werden. Kapitel 4 dokumentiert die Aspekte der Implementierung in zun achst formaler Art und darauf folgend mit Hilfe von Soot. Der erste Teil des Kapitels zeigt die Denitionen, die f ur die Durchf uhrung der Informationsussanalyse n otig sind und deren Umsetzung mit Soot. Im zweiten Teil werden die Denitionen f ur eine Informationsussanalyse im Zusammenhang mit einer Pointsto-Analyse gezeigt und erl autert. Hier wird ebenso die Umsetzung dieser Denitionen mit Soot, in Kombination mit SPARK, gezeigt. Kapitel 5 illustriert die nalen Ergebnisse der Arbeit sowie eine Bewertung dieser. Dazu werden zun achst mehrere Testf alle und die dazugeh origen AnalyseErgebnisse gezeigt und erl autert. Darauf folgt eine Zusammenfassung der Ergebnisse und eine Bewertung dieser. Zuletzt wird ein Fazit sowie ein Ausblick auf weitere Aufgabenfelder und Kombinationsm oglichkeiten gegeben.

1 Grundlagen

1 Grundlagen
Dieses Kapitel gibt einen Uberblick u ber die Grundlagen dieser Arbeit. Neben der Betrachtung von virtuellen Maschinen und Bytecodeformaten werden auch die Grundlagen der Datenussanalyse erl autert. Dazu geh oren verschiedene Datenussschemata sowie die Klassizierung von Datenussanalysen. Die Betrachtung virtueller Maschinen und Bytecode Formate ist f ur diese Arbeit genauso notwendig wie die Grundlagen der Datenussanalyse, da die Analysen der Programme auf Ebene des Bytecodes ablaufen. Durch die bereits vollzogene Transformation in Bytecode wird die Analyse der Programme erleichtert, da ihr Inhalt bereits w ahrend der Umwandlung optimiert und teilweise vereinheitlicht wurde. In diesem Kapitel werden die Java und Dalvik Bytecode Formate gezeigt, in Kapitel 3.1 erg anzend die verschiedenen Bytecode Formate des Java-BytecodeAnalyse Frameworks Soot.

1.1 Virtuelle Maschinen


Eine virtuelle Maschine [75] ist ein virtueller Computer, d.h. sie besteht i.d.R. nicht direkt aus Hardware, wie ein normaler Rechner, sondern wird virtuell, also als Software-Tool auf einem anderen Ger at, z.B. einem Server, installiert. Daher k onnen auf einem Computer neben dem eigentlichen Betriebssystem noch weitere unabh angige Installationen verschiedener Betriebssysteme oder Laufzeitumgebungen als virtuelle Maschine vorhanden sein. Virtuelle Maschinen werden unterteilt in Registermaschinen und Stapelmaschinen, also Maschinen, die auf einem Kellerautomaten basieren. Im Folgenden werden beide Typen genauer erl autert.

10

1.1 Virtuelle Maschinen

1.1.1 Registermaschinen
Im mathematischen Sinn besteht der Speicher einer Registermaschine aus einer abz ahlbar unendlichen Anzahl von Registern, die jeweils eine nat urliche Zahl aufnehmen k onnen [65]. Die Register sind durch fortlaufende Nummern, Indices, gekennzeichnet, beispielsweise r0, r1, r2, wobei r0 h aug den Akkumulator (Ergebnisspeicher) darstellt. Zu den Registern, die mit Eingangswerten initialisiert werden k onnen, kommen ggf. noch weitere Register, die als Hilfsregister genutzt werden sowie ein Befehlsz ahler. Die Grundoperationen auf den Registern sind das Inkrementieren und das Dekrementieren der Zahlen in den Registern sowie ggf. der NULL-Test. Um ein Programm abzuarbeiten, muss eine Folge von Operationen auf den Registern und Hilfsregistern durchgef uhrt werden. Das Ergebnis eines Programms wird am Ende ggf. in r0 geschrieben. Es gibt mehrere verschiedene Denitionen von Registermaschinen, jedoch ahneln sich diese sehr im Kern der Denition. Um die Funktionen einer Registermaschine zu verdeutlichen, werden die Operationen f ur eine unlimitierte Registermaschine im Folgenden dargestellt. Basisbefehle einer unlimitierten Registermaschine (URM): P(n):addiere 1 zur Zahl in Register n, z.B. (n) = (n) + 1. D(n): subtrahiere 1 von der Zahl in Register n, z.B. (n) = (n) - 1. O(n): l osche den Inhalt aus Register n, (n) = 0. C(m, n) kopiere von Register m nach n, z.B. (n) = (m). J[E1] springe zu exit 1. J(M)[E1] springe zu exit 1 wenn Register m leer ist.

Vereinfacht: F ur jede nat urliche Zahl N0 und jedes Programm P der URM existiert ein Programm mit dem selben Eekt wie P auf den Registern 1 . . . N0 welches nur aus den folgenden Befehlen zusammengestellt ist: PN (n) : (n ) = (n) + 1 DN (n) : (n ) = (n) 1 N(n)[E1] : jump to exit 1 if (n)! = 0 J

Ein Beispiel f ur eine virtuelle Maschine basierend auf einer Registermaschine ist die Dalvik Virtual Machine. Sie ist neben der Java Virtual Machine die Grundlage

11

1.1 Virtuelle Maschinen f ur die Umsetzung von Java-Programmcode in Dalvik-Bytecode - einen speziell f ur mobile Endger ate entwickelter Maschinencode. Die Java Virtual Machine sowie die Dalvik Virtual Maschine werden in 1.1.3 weitergehend er ortert.

1.1.2 Kellerautomaten
Ein Kellerautomat oder auch eine Stackmaschine wird in der theoretischen Informatik deniert und ist eine Erweiterung eines Automaten durch einen Speicher. Durch einen Kellerautomaten l asst sich eine kontextfreie Grammatik darstellen, d.h. zu jeder kontextfreien Grammatik geh ort auch ein Kellerautomat, der diese akzeptiert. Ein solcher Automat verf ugt neben einer Steuereinheit und einem Eingabeband u ber eine unbegrenzte Anzahl von Speicherzellen, den Keller (engl. Stack), was ihn von einem endlichen Automaten unterscheidet (s. [39]). Der Kellerspeicher ist vergleichbar mit einem unendlich langen Band von Speicherzellen, auf die nach dem last-in-rst-out (LiFo)-Prinzip zugegrien werden kann. D. h., das letzte Element, was dem Kellerspeicher zugef ugt wurde, ist auch das erste, was wieder ausgelesen werden kann. Neue Elemente werden durch den Befehl push in den Speicher gelegt, vorhandene durch pop wieder ausgelesen und je nach Implementierung entfernt. Ein Kellerautomat ben otigt kein Ausgabeband, da das Ergebnis durch den Wechsel zwischen dem Anfangszustand, dem ersten Zeichen auf dem Kellerspeicher und verschiedenen Zust anden abh angig ist und durch den Endzustand repr asentiert wird. Eine formale Denition eines nicht deterministischen Kellerautomatens kann folgendermaen aussehen: Ein Kellerautomat ist ein Tupel P = (V, Q, , q0 , F ), wobei V das Eingabealphabet, Q die endliche Menge der Zust ande, q0 Q der Anfangszustand, F Q die Menge der Endzust ande und eine endliche Relation zwischen Q+ (V {}) und Q , die Ubergangsrelation. kann man also betrachten als eine endliche partielle Funktion von Q+ (V {}) in die endlichen Teilmengen von Q . [74] Der Automat liest also in einem Zustand q Q ein Zeichen aus dem Eingabe alphabet V vom Eingabeband und die Ubergangsfunktion bestimmt die weitere

12

1.1 Virtuelle Maschinen Ausf uhrung, z.B. ob ein push oder pop auf dem Kellerspeicher ausgef uhrt werden soll. Anhand der Endzust ande kann dann ein Ergebnis ausgewertet werden. Der Kellerautomat ist Basis f ur verschiedene Modelle von virtuellen Maschinen. In diesem Zusammenhang wird besonders die Java Virtual Machine dargestellt. Ihre Aufgabe ist es, den durch den Compiler von Java-Programmcode transformierten Java-Bytecode (Maschinencode) w ahrend der Laufzeit zu interpretieren. So kann ein Java-Programm auf jedem Betriebssystem mit installierter Java-Laufzeitumgebung ausgef uhrt werden. Die Java Virtual Machine wird im folgenden Kapitel 1.1.3 weitergehend erl autert.

1.1.3 Die Java Virtual Machine und Dalvik Virtual Machine


Die Java Virtual Machine (im Folgenden JVM) ist neben den Java-APIs ein Bestandteil der Java-Laufzeitumgebung und ist daf ur zust andig, den durch den Compiler generierten Maschinencode zur Laufzeit zu interpretieren. Konkret bedeutet das, dass der durch den Entwickler geschriebene Java-Quellcode zun achst durch den Java Compiler in einen Maschinencode in einem typisierten Bin arformat u bersetzt wird. [44] Dieser Maschinencode wird als Java-Bytecode bezeichnet (s. 1.2) und hat die Dateiendung .class f ur Klassendatei, da es sich bei den transformierten Programmst ucken um Klassen handelt. F ur jede Java-Klasse wird eine .class Datei generiert, bei gr oeren Programmen werden sie h aug zu Java Archiven gruppiert. W ahrend der Programmausf uhrung in der Java Laufzeitumgebung wird dieser Bytecode von der JVM veriziert und interpretiert oder direkt durch einen JIT-Compiler wie HotSpot von Oracle ausgef uhrt [54]. Mittlerweile ist es m oglich, die JVM auch f ur andere Programmiersprachen zu nutzen. Bspw. ist eine Nutzung der JVM f ur Python in Form von Jython, einer auf Java basierenden Transformation von Python, m oglich. Die JVM basiert im Gegensatz zur Dalvik Virtual Machine, die innerhalb des Android-Betriebssystem zum Einsatz kommt, auf dem Modell eines Kellerautomatens (s. 1.1.2), d.h. sie verwendet statt Registern den Kellerspeicher. Die Dalvik VM, basierend auf dem Prinzip der Registermaschine, wurde entwickelt, um eine Umsetzung von Java-Programmcode f ur das Betriebssystem Android [31] von Google zu erm oglichen. Der Ablauf bei der Umsetzung des Programmcodes ist ahnlich dem von regul arem Java-Quellcode. Die Quelldateien in Java werden in Bytecode compiliert. Es erfolgt intern eine Weitergabe der .class Dateien an das dx Programm des Android SDKs, das die

13

1.1 Virtuelle Maschinen Dateien in das dex-Format (Dalvik Executable) umwandelt. Das dex-Format ist eine weitere Form des Bytecodes, der Dalvik Bytecode. Zur Laufzeit des Programms wird der Dalvik Bytecode ausgef uhrt. In Abbildung 1.1 wird der Aufbau eines Java Archivs (.jar) und einer Android

Abbildung 1.1: .class Dateien und Dalvik Executable Dateien im Vergleich Quelle: The Dalvik Virtual Machine Architecture, [23]

Application Package Datei (.apk) dargestellt. Da die Dalvik VM auf mobilen Endger aten ausgef uhrt wird, wurde bei ihrer Entwicklung besonders auf speicherezientes Arbeiten und die M oglichkeit, Daten w ahrend der Laufzeit zu teilen, geachtet. Bei Nutzung der JVM wird f ur jede Klasse eine .class Datei generiert, in der f ur jede einzelne Klasse u.a. eine private Sammlung von heterogenen Konstanten existiert sowie u.a. die Felder, Methoden und die Attribute der Klasse gesammelt werden. Ein .jar (Java-Archiv) wird dann aus mehreren .class Dateien zusammengesetzt.

14

1.2 Java Bytecode Im Dalvik Executable Format werden die Daten so zusammengestellt, dass alle Klassen in einer .dex Datei repr asentiert werden k onnen. Es existieren verschiedene Sammlungen f ur Konstanten, z.B. die Sammlung der String Konstanten oder die der Feld Konstanten, die dann bei entsprechendem Zugri durch eine Referenz erreicht werden k onnen. So wird vermieden, dass die Werte wiederholt gespeichert werden, um Speicherplatz einzusparen. Die dex-Datei wird dann in einer apk-Datei zusammengefasst und dabei durch ein Manifest im XML-Format erg anzt. Das Manifest beinhaltet u.a. Informationen zu den Berechtigungen, die das Programm zur Ausf uhrung ben otigt. [32] Durch die Installation der Android Application Package Datei auf dem Endger at kann eine App genutzt werden. Dabei erzeugt jede einzelne App eine eigene Instanz der Dalvik VM, um den Programmcode auszuf uhren. Daf ur wird jeder App ein eigener Prozess vom Betriebssystem bereitgestellt, ebenso wie ein eigener Speicherbereich, um Sandboxing zu erm oglichen. Damit trotz der eigenen Instanzen der Dalvik VM ein performantes Ergebnis gew ahrleistet werden kann, wird ein initialer Prozess beim Systemstart des Ger ates ausgef uhrt. Der Prozess nennt sich Zygote und ist daf ur verantwortlich, dass Anweisungen zwischen den Apps ausgetauscht werden und die virtuellen Maschinen schneller starten k onnen. Der schnellere Start der VMs wird dadurch erm oglicht, dass mit Hilfe von Zygote eine Dalvik VM initialisiert wird, die die Core Library Klassen f ur alle folgenden Dalvik VMs l adt und initialisiert. Bei einer JVM w urde jede Instanz die Core Library Klassen selber laden, hier u bernimmt das Zygote und wartet nach der Initialisierung auf die Anweisung, weitere VMs f ur die Apps zu starten. [23]

1.2 Java Bytecode


Der Java Bytecode entsteht, in dem der Java Compiler die Java Quelldateien in Bytecode transferiert. In Listing 1.1 wird ein einfaches Beispiel f ur eine Textausgabe in Java gezeigt. Listing 1.2 ist der Inhalt der dazugeh origen .class Datei, der Java Bytecode.

Listing 1.1: Java Code


1 2 3 4 5 6

public class HelloWorld { public static void main(String[] args) { String text = "Hallo Welt"; System.out.println(text);

15

1.2 Java Bytecode

7 8 9

} }

Im Folgenden werden die f ur diese Arbeit notwendigen Befehle des Java Bytecodes aufgezeigt, ein Maschinenbefehl (Opcode) hat hier eine L ange von 8 Bit und kann damit 256 Werte annehmen, aus denen die Anzahl der Opcodes f ur den Java-Bytecode resultiert. Die Datentypen im Java Bytecode sind Byte, Short, Integer, Long, Character, Float, Double und Reference. Boolsche Werte werden direkt durch Integer-Werte dargestellt.Konstanten k onnen implizit, also direkt im Code, explizit durch Hinzunahme eines Operanden oder durch Referenzen, die auf Konstanten im Constant Pool, der Konstanten Sammlung, zeigen, dargestellt werden. Lokale Variablen werden geladen und gespeichert wie bspw. in Listing 1.2 Zeile 5 und 6. Dort wird die Konstante Hallo Welt durch den Befehlt LDC als oberster Wert auf den Kellerspeicher gelegt, danach gelesen und die Variable mit der Nummer 1 gespeichert, in Zeile 11 wird eine Referenz von Variable 1 in den Kellerspeicher geladen. Methodenaufrufe werden mit INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEDYNAMIC und INVOKEINTERFACE, wie z.B. in Zeile 11, Listing 1.2. [44] Zur Generierung des Bytecodes wurde im Rahmen dieser Arbeit [45] genutzt.

Listing 1.2: Java Bytecode


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

// access flags 0x9 public static main([Ljava/lang/String;)V L0 LINENUMBER 4 L0 LDC "Hallo Welt" ASTORE 1 L1 LINENUMBER 5 L1 GETSTATIC java/lang/System.out : Ljava/io/PrintStream; ALOAD 1 INVOKEVIRTUAL java/io/PrintStream.println(Ljava/lang/String;)V L2 LINENUMBER 6 L2 RETURN L3 LOCALVARIABLE args [Ljava/lang/String; L0 L3 0 LOCALVARIABLE text Ljava/lang/String; L1 L3 1 MAXSTACK = 2 MAXLOCALS = 2

16

1.3 Dalvik Bytecode

1.3 Dalvik Bytecode


Der Dalvik Bytecode wurde so implementiert, dass er sich bestens an die vorhandene Hardware anpassen und somit ressourcensparend und optimiert arbeiten kann. 128MB RAM und 256 MB externer Flash Speicher ohne Swap reichen, um ein lau ahiges Androidsystem auf einem batteriebetriebenen System zu erhalten. [23] Die Opcodel ange betr agt hier 16 Bit, was im Vergleich zur JVM die doppelte Gr oe pro Ladevorgang bedeutet. Der Dalvik Bytecode ist der Bytecode, zugeh orig zu einer Android App. Im Folgenden wird eine kleine Beispiel App (Abb. 1.2) gezeigt und deren Bytecode mit Hilfe von Dedexer [57], einem Diassembler f ur .dex Dateien, dargestellt.

Abbildung 1.2: Android Beispiel App

Die App besteht aus mehreren Teilen, also einem ganzen Paket. Dazu geh oren u.a. mehrere .xml Dateien und Java-Code. Symbolisch f ur das Paket wird in Listing 1.3 der Java-Code dazu gezeigt.

Listing 1.3: Java Code einer Android App


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

package com.example.helloworld; import android.os.Bundle; import android.app.Activity; import android.view.Menu; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.activity_main, menu); return true;

17

1.3 Dalvik Bytecode

19 20

} }

Der Java-Code steht f ur die Initialisierung einer Activity durch den Aufbau der Klasse MainActivity, dem Kern dieser App. Eine Activity ist f ur die Interaktion mit dem Nutzer notwendig, da mit ihrer Hilfe die GUI Elemente innerhalb der App platziert werden. Die Methode onCreate() ordnet mit Hilfe von setContentView() der Activity einen so genannten View zu. Dabei handelt es sich um ein Layout f ur die App, was in Form einer XML-Datei deniert wird. Die Methode onCreateOptionsMenu() initialisiert das Men u innerhalb der App. Der dazugeh orige Dalvik-Bytecode wird in Listing 1.4 gezeigt.
Listing 1.4: Dalvik Bytecode einer Android App
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37

.class public com/example/helloworld/MainActivity .super android/app/Activity .source MainActivity.java

.method public <init>()V .limit registers 1 ; this: v0 (Lcom/example/helloworld/MainActivity;) .line 7 invoke-direct {v0},android/app/Activity/<init> ; <init>()V return-void .end method .method public onCreate(Landroid/os/Bundle;)V .limit registers 3 ; this: v1 (Lcom/example/helloworld/MainActivity;) ; parameter[0] : v2 (Landroid/os/Bundle;) .line 11 invoke-super {v1,v2},android/app/Activity/onCreate ; onCreate(Landroid/os/Bundle;)V .line 12 const/high16 v0,32515 invoke-virtual {v1,v0},com/example/helloworld/MainActivity/setContentView ; setContentView(I)V .line 13 return-void .end method .method public onCreateOptionsMenu(Landroid/view/Menu;)Z .limit registers 4 ; this: v2 (Lcom/example/helloworld/MainActivity;) ; parameter[0] : v3 (Landroid/view/Menu;) .line 17 invoke-virtual {v2},com/example/helloworld/MainActivity/getMenuInflater ; getMenuInflater()Landroid/view/MenuInflater ; move-result-object v0

18

1.4 Datenussanalyse und Informationsussanalyse

38 39 40 41 42 43 44

const/high16 v1,32518 invoke-virtual {v0,v1,v3},android/view/MenuInflater/inflate ; inflate(ILandroid/view/Menu;)V .line 18 const/4 v0,1 return v0 .end method

Zun achst ndet die Initialisierung der Klasse MainActivity in den Zeilen 113 statt. Dazu wird u.a. in Zeile 10 mit Hilfe eines invoke-direct Befehls eine nicht statische direkte Methode, hier der Konstruktor der Klasse, verarbeitet. Das Ergebnis des Aufrufs, wenn eins vorhanden ist, wird in v0 gespeichert. Zeile 15 - 28 stehen f ur die Umsetzung der Methode onCreate(). In Zeile 20 wird der super Aufruf aus Beispiel 1.3 in Zeile 11 mit Hilfe eines invoke-super Befehls dargestellt. Zeile 24 stellt den Aufruf von setContentView aus Bsp. 1.3 durch den Aufruf invoke-virtual, da es sich hier um eine virtuelle Methode handelt, dar. In den Zeilen 30 - 44 wird mit der Methode onCreateOptionsMenu ahnlich verfahren. Eine genaue Ubersicht der Befehle kann in [60] eingesehen werden. Nach [60] gilt allgemein f ur Dalvik-Bytecode eine Registergr oe von 32 Bits, wobei Werte mit einer Gr oe von 64 Bit mit Hilfe von Registerpaaren verarbeitet werden k onnen. Jedes Register kann jeden der folgenden Datentypen speichern. Die Basistypen des Dalvik-Bytecodes sind int (I), long (J), boolean (Z), double (D), oat (F), short (S), char (C) und void (V) als R uckgabetyp, wie z.B. in Zeile 6 in Listing 1.4. Erweitert werden diese Typen durch Klassen und Arrays. Methoden werden durch Meta-Informationen erg anzt und Informationen u ber die Signatur, Annotationen, Try-Catch Informationen, die Anzahl der genutzten Register usw. sind so aus dem Bytecode lesbar. Insgesamt existieren 220 Opcodes (s. [51]) innerhalb des Dalvik Bytecodes, die den Java-Bytecode weitestgehend optimiert zusammenfassen. Auch wenn es sich bei Dalvik-Bytecode, im Gegensatz zu Java-Bytecode, um registerbasierten Bytecode handelt, ist eine statische Analyse hier ebenso m oglich. Besonders durch die Kompaktheit der Opcodes wird die Analyse im Vergleich zu Java-Bytecode vereinfacht.

1.4 Datenussanalyse und Informationsussanalyse


Die Datenussanalyse [1] ist ein wesentlicher Bestandteil der statischen Programmanalyse und wird u.a. zur Codeoptimierung, Parallelisierung oder zur Sicherheitspr ufung von Programmen eingesetzt.

19

1.4 Datenussanalyse und Informationsussanalyse Eine Datenussanalyse betrachtet die Weitergabe von Daten in einem Programm entlang des Programmusses, in dem f ur jedes Statement ein Eingangsund Ausgangswert berechnet wird. Mit dem Ergebnis k onnen Abh angigkeiten von Ausdr ucken, ihre Gleichheit oder ggf. die Eliminierung eines Wertes ausgewertet werden, ohne dass das Programm ausgef uhrt wird. Da es sich um eine statische Analyseform handelt, wird die Analyse auf Basis des Codes ( ublicherweise Bytecodes) durchgef uhrt. Im Gegensatz zur reinen Datenussanalyse wird bei der Informationsussanalyse [66] auch der Inhalt der Daten betrachtet. Die Inhalte einer Methode oder eines Statements k onnen dadurch ausgelesen und optimiert bzw. ausgewertet werden. Die Informationsussanalyse wird meistens zur Uberpr ufung der Sicherheitsaspekte eines Programms genutzt, d.h. es wird u.a. kontrolliert, ob ein Programm sensitive Informationen z.B. des Nutzers in Form einer Sicherheitsl ucke (im folgenden auch: Leak) ausgibt. Dazu werden zun achst alle Variablen eines Programms als sensitive und nicht sensitive Daten klassiziert, bzw. als Daten mit einer niedrigen Sensitivit at L, bspw. ein Leak, und einer hohen Sensitivit at H, z.B. die Kontodaten eines Nutzers, eingestuft. Im n achsten Schritt wird dann ausgewertet, ob der Wert einer Variable der Klasse H an eine Variable der Klasse L weitergegeben wird und damit potentiell ausgegeben werden k onnte. Es existieren verschiedene Ans atze zur Implementierung einer Informationsussanalyse, bspw. [29] verwenden eine Kombination aus Boolschen Funktionen zur Darstellung der Informationsussbeziehung f ur die eingehenden und ausgehenden Werte f ur jedes Statement und der Generierung eines Kontrollussgraphens f ur vorliegenden Java-Bytecode. In dieser Arbeit wird auf Basis des Kontrollussgraphens f ur jedes Statement des Bytecodes eines Programms u uft, ob innerhalb eines Statements sensitive berpr Informationen generiert werden oder ein Leak durchgef uhrt werden k onnte. Wenn es sich um sensitive Informationen handelt, werden die Variablen in einer Menge critical gespeichert, handelt es sich um Leaks, werden die Variablen in einer Menge leak gesammelt. Diese Mengen k onnen innerhalb des Programms verglichen und ausgewertet werden. Im Folgenden ist mit dem Begri Datenussanalyse auch die Informationsussanalyse eingeschlossen, da es sich bei dieser Arbeit um eine Mischung aus beiden Methoden handelt, bzw. die Informationsussanalyse auch als Spezialfall der Datenussanalyse gilt. Bei der Datenussanalyse unterscheidet man zwischen einer Vorw artsanalyse,

20

1.4 Datenussanalyse und Informationsussanalyse R uckw artsanalyse und der bidirektionalen Flussanalyse. Da verschiedene Datenussprobleme bestehen, ist es bei der L osung bestimmter Datenussprobleme sinnvoller, die Daten vorw arts zu analysieren, in bestimmten r uckw arts oder gegebenenfalls in beide Richtungen [36]. Im Falle z.B. einer Konstantenpropagation (s. Kapitel 1.4.1) verwendet man eine Vorw artsanalyse, d.h. man analysiert die Informationen von einem Startpunkt zu einem Zielpunkt im Kontrollussgraphen (s.u.). Bei einer R uckw artsanalyse wird die entgegengesetzte Richtung analysiert, besonders im Fall der Analyse von so genannten lebendigen Variablen. Variablen gelten dann als lebendig, wenn ihr Wert im Programmablauf verwendet werden kann, im Gegensatz zu toten Variablen oder auch totem Code, die niemals f ur Berechnungen verwendet werden (s. [1]. G angige Methoden bzw. Schemata der Datenussanalyse sind u.a. die Kopierpropagation, die Dead-Code Elimination, die erreichende Denitionen oder auch die Konstantenpropagation. Bei der KopierPropagation werden gemeinsame Teilausdr ucke von Variablen durch Kopieranweisungen ersetzt. Bspw. k onnte in einem Programm statt x = y + z und w = y + z, t = y + z sowie x = t und w = t notiert werden. Dabei k onnte t f ur alle Berechnungen von y + z im Programm stehen. Die Eliminierung von totem Code (Dead-Code Elimination) wird eingesetzt, um Variablen, deren Werte nicht genutzt werden, zu eliminieren. Durch die Kopierpropagation entstehen oft tote Codesegmente, die eliminiert werden k onnen, was sich hinsichtlich einer Programmoptimierung als sinnvoll erweist. [74]. Die Konstantenpropagation sowie erreichende Denitionen werden in den folgenden Abschnitten 1.4.1 und 1.4.2 erkl art. Es existieren verschiedene Denitionen von Datenussproblemen. Im Folgenden wird ein formaler Aufbau einer Vorw arts- und R uckw artsussanalyse gezeigt. [1] beschreibt die Programmausf uhrreihenfolge, also den Programmuss eines Programms, als eine Hintereinanderreihung von Transformationen des Programmzustands bestehend aus allen Werten der in einem Programm vorhandenen Variablen. Wenn eine Anweisung ausgef uhrt wird, wird ein Eingabezustand durch die Ausf uhrung in einen Ausgabezustand transformiert. Der Eingabezustand eines Statements s wird hier durch IN[s] und der Ausga bezustand durch OUT[s] formal deniert. Der Ubergang von IN[s] zu OUT[s] h angt von einer Transferfunktion fb , die den Ubergang abh angig von der Semantik des Statements deniert und dem Kontrolluss ab. Ein Statement ist in der Datenussanalyse Bestandteil eines Blocks, wie Grak 1.3 zeigt. Bei der Analyse wird der Ubergang von IN[] zu OUT[] auf Basis dieser Bl ocke vollzogen. Um eine Transferfunktion f ur einen Ubergang zwischen den Statements eines

21

1.4 Datenussanalyse und Informationsussanalyse Blocks b zu denieren, wird eine Konkatenation der Transferfunktionen der Statements s1...n vollzogen: fb = fsn . . . fs2 fs1 . Die daraus resultierende Transferfunktion kann damit als OUT[b] = fb (IN[b]) beschrieben werden. [1] Abh angig vom Kontrolluss eines Programmblocks kann eine Datenussanalyse vorw artsgerichtet folgendermaen deniert werden: Davon ausgehend, dass b einen direkten Vorg anger p hat, kann man IN[b] denieren als: IN[b] = OUT[p],

ppred

wobei pred die Menge der Vorg anger von b darstellt. [35] Der Wert von OUT[b] kann von weiteren Faktoren innerhalb des Blocks, z.B. der Neuerzeugung oder Vernichtung einer Variable, abh angig sein. Beispielsweise kann formal die Neuerzeugung einer Variablen in b durch die Menge gen(b) beschrieben werden, die Vernichtung durch kill(b). Die Berechnung der Menge OUT[b] h angt von der gew ahlten Transferfunktion ab. Eine M oglichkeit w are z.B. die Menge gen(b) abzgl. der Menge kill(b) als OUT[b] zu denieren, was aussehen w urde, wie folgt: OUT[b] = gen(b) \ kill(b) Die in dieser Arbeit genutzte Transferfunktion wird in Kapitel 4 gezeigt.
Listing 1.5: Java Beispielcode fu r Datenussanalyse
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class Example { String id; String s;

public static void main(String[] args) { id = getId(); s = id; id = "text"; } }

Das idealisierte Beispiel 1.5 zeigt, dass anhand dieser Analyse die Variable id in Zeile 10 der Menge gen(b) zugeordnet w urde, falls durch den Aufruf der Funktion

22

1.4 Datenussanalyse und Informationsussanalyse getId() sensitive Informationen in id gespeichert werden. Zeile 11 werden die sensitiven Informationen der Variable id in s kopiert, was deutlich macht, dass innerhalb einer Transferfunktion eine Menge copy (b) ber ucksichtigt werden sollte. In der darauf folgenden Zeile wird die Variable id der kill-Menge hinzugef ugt, da sie durch die Zuweisung des Wertes "text" keine sensitiven Informationen mehr enth alt. Um diese Art von Datenussanalyse auf Basis von Bytecode durchf uhren zu k onnen, wird mit dem sogenannten Kontrollugraphen gearbeitet. Bei der Analyse von strukturierten Hochsprachen werden abstrakte Syntaxb aume genutzt [76]. In diesem Zusammenhang werden jedoch nur Flussgraphen betrachtet, da im vorliegenden Anwendungsfall eine Analyse auf Bytecode-Ebende stattndet. Ein Kontrollussgraph visualisiert den Ablauf eines Programms oder eines Programmteils, also alle m oglichen Pfade die w ahrend der Ausf uhrung des Programms durchlaufen werden k onnen. Dabei werden einzelne Anweisungen oder auch ggf. Gruppen von Anweisungen als Bl ocke deniert. Zu den gegebenen Bl ocken k onnen dann die Anfangs- und Endzust ande vor bzw. nach der Abarbeitung der Befehle ausgelesen und ausgewertet werden. Im Folgenden wird ein Java-Beispiel (Listing 1.6) und der dazugeh orige Kontrollussgraphen dargestellt:

Listing 1.6: Java Beispielcode Kontrollussgraph


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

int i = 2; int j = 10; boolean example; ... while( example != true){ j = j-i; if (j == 0){ example = true; } else{ example = false; } }

Die Initialisierung der Variablen i, j und example wird im Kontrollussgraphen in einem gemeinsamen Block dargestellt, da diese Anweisungssequenz keinen Einuss auf den Kontrolluss hat. Generell werden Anweisungen, die den Kontrolluss beeinussen, in einzelne Bl ocke ohne eine Gruppierung unterteilt, wie bspw. in Block 6: j = i+j aus Abb.1.3.

23

1.4 Datenussanalyse und Informationsussanalyse

Abbildung 1.3: Kontrollussgraph f ur ein Java Codebeispiel

1.4.1 Konstantenpropagation
Bei der Konstantenpropagation [1] ist das Ziel, herauszunden, ob eine Variable an einer bestimmten Stelle im Programm einen konstanten Wert besitzt. Ein konstanter Wert liegt dann vor, wenn die Variable bei jeder Ausf uhrung bis zu einem Programmpunkt den Wert der Variable an diesem Punkt besitzt. Wenn eine Variable einen konstanten Wert beh alt, kann zur Optimierung direkt ihr Wert in die Anweisung eingesetzt werden, ohne dass sie immer wieder ausgewertet werden muss. Das Beispiel 1.7 zeigt einen kleinen Programmausschnitt, f ur den anhand der Konstantenpropagation Optimierungen vorgenommen werden k onnen. In Zeile 10 aus Listing 1.7 wird der Wert der Variable x berechnet, der in den Zeilen 13 und 24 wieder genutzt wird. Mit Hilfe der Konstantenpropagation l asst sich dieser Wert f ur x ermitteln und direkt in den anderen F allen einsetzen, mit der Sicherheit, dass sich der Wert von x bis dahin nicht ver andert hat.

Listing 1.7: Java Beispielcode Konstantenpropagation

24

1.4 Datenussanalyse und Informationsussanalyse

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

public class Example { public static void main(String[] args) { int x = 0; int y = 1; int z = 2; //x hat den Wert 4 x = 2*z; //Hier koennte daher auch System.out.println(4) stehen, //da x eindeutig ist System.out.println(x); for(int i = 0; i < x; i++) { z = z - 1; //fuer y kann auf Grund der if-Anweisung keine //eindeutige Loesung gefunden werden if(z != 0) { //hier koennte fuer x auch wieder 4 stehen y = y + x; } else { y = 2; } } } }

Der Algorithmus zur Konstantenpropagation versucht, eine Liste von Variablen und deren vermeintlichen Werten zu ermitteln.Die Konstanten werden in diesem Algorithmus in drei Kategorien eingeteilt: Variablen, die einem konkreten konstanten Wert zugeteilt werden k onnen (N ), Variablen, die nicht konstant sind (N AC ) und Variablen, deren Wert nicht festgelegt werden kann, da keine Denition zu nden ist (U N DEF ) [1]. Es gilt nun, eine Funktion zu berechnen, die als Schl ussel die Variable enth alt und als Werte (N ) {N AC, U N DEF }. Die Durchf uhrung der Berechnung dieser Liste basiert auf einer Fixpunktiteration, dem wiederholten Durchlauf einer Berechnung ,bis ein bestimmter Fixpunkt, ein Endwert o. a. erreicht wird [64]. Dabei wird der Kontrollussgraph des Programms so lange traversiert, bis sich z.B. die berechneten Wertepaare nicht mehr ver andern. W ahrend der Traversierung wird dann f ur jedes Statement auf Basis der vorangegangenen Bl ocke und der Auswertung des aktuellen Blocks das Wertepaar gebildet [1] legt die Transferfunktionen zur Berechnung der Wertepaare wie folgt

25

1.4 Datenussanalyse und Informationsussanalyse fest: Sei fs die Transferfunktion eines Statemens s und m und m die Datenusswerte, so dass gilt: m = fs (m), wird folgende Fallunterscheidung getroen: 1. s ist keine Zuweisung, dann ist fs = s s 2. s ist eine Zuweisung zu eine Variable x, so gilt f ur die Variablen v m (v ) = m(v ) f ur alle Variablen v = x. m (x) wird deniert als: a) falls s: x = c, wobei c eine Konstante ist, ist m (x) = c b) falls s: x = y + z, m(y ) + m(z ), wenn m(y ) undm(y ) konstante Werte sind, gilt: m (x) = N AC, wenn entweder m(y ) oder m(z ) nicht konstant ist, U N DEF, andernfalls. c) wenn s : x = n, wobei n keine Konstante sondern ein Zeiger, Methodenaufruf o. a, ist, dann ist m (x) = N AC Auf Basis der Relationen von m und m wird oben die Transferfunktion beschrieben, die zur Klassizierung und Berechnung der Konstantenpropagation genutzt werden kann. Mit Hilfe der Konstantenpropagation k onnen die Ausdr ucke und Variablen mit gleichem Wert an einer Stelle im Programm durch eine Konstante ersetzt werden. Das Ergebnis ist dann ein kompakterer Programmcode und unerreichbare Programmteile k onnen bei den folgenden Abarbeitungen ausser acht gelassen werden.

1.4.2 Erreichende Denitionen


Das Datenussschema der erreichenden Denitionen [50] behandelt die Uberpr ufung der Verwendung von denierten Variablen. Variablen werden in einem Programm deniert, aber nicht alle Variablen werden letztendlich genutzt. Die Denition einer Variable ist eine Wertzuweisung, also z.B. int x = 10. Diese Denition kann aber ggf. durch einen kill zerst ort werden, in dem der Variable x erneut ein Wert zugewiesen wird, bevor sie letztendlich verwendet wird. Man spricht von einer erreichenden Denition, wenn der Wert der Variable, also die Denition, verwendet wird, bevor sie ggf. zerst ort wird.

26

1.4 Datenussanalyse und Informationsussanalyse Das folgende Beispiel 1.8 zeigt drei denierte Integervariablen i, j und k. Sie werden mit Werten belegt, die teilweise auch im weiteren Verlauf des Programms genutzt werden, wie bei i und j, oder deren denierter Wert zerst ort wird, siehe k in Zeile 10. Die Variablen i und j erreichen im Gegensatz zu k ihre Denition.

Listing 1.8: Java Beispielcode Erreichende Denitionen


1 2 3 4 5 6 7 8 9 10 11 12 13

public class Example { public static void main(String[] args) { // i, j und k werden definiert int i = 0; int j = 1; int k = 5; // i und j werden ohne kill genutzt, Definition erreicht // der definierte Wert von k wird zerstoert, Definition nicht erreicht k = i + j; } }

Formal wird der Weg zwischen der Denition und der weiteren Verwendung der Variable als Flusspfad bezeichnet. Wenn ein direkter Pfad zwischen der Denition und der Ausf uhrung der Variable, ohne einen kill der Variable, besteht, dann wird die Denition erreicht. [1] verdeutlicht das Datenussschema der erreichenden Denitionen durch einen iterativen Algorithmus. Dabei handelt es sich um eine Sch atzung der IN[b] und OUT[b] Mengen mit den vorher gegebenen Mengen kill(b) und gen(b): OUT[ENTRY] = ; for (jeder Grundblock b auer ENTRY) OUT[b] = ; while (ndert sich ein beliebiges Vorkommen von OUT for (jeder Grundblock b auer ENTRY) { IN[b] = ppred OUT[p] OUT[b] = gen(b) (IN[b] kill(b)) }

Dabei wird f ur jeden Block des Aufrufgraphens die Menge OUT[b] initialisiert und der Algorithmus so lange durchlaufen, bis sich keine OUT[b] Menge mehr ver andert. W ahrend der Durchl aufe wird IN[b] aus den Ergebnisse der Vorg angerbl ocke berechnet, OUT[b] auf der Basis der gen(b) vereinigt mit den Werten der IN[b] Menge abz uglich der in diesem Statement zerst orten Variablen, also

27

1.5 Klassikation von Programmanalysen abzgl. der kill(b) Menge.

1.5 Klassikation von Programmanalysen


Es gibt verschiedene Arten von Programmanalysen, die jeweils f ur einen bestimmten Anwendungsfall besonders gut oder deutlich weniger gut zutreen. In den folgenden Kapiteln werden die intraprozedurale und die interprozedurale, die kontextsensitive und kontextinsensitive sowie die usssensitive und ussinsensitive Analyseformen deniert.

1.5.1 Intraprozedurale und interprozedurale Analyse


Eine intraprozedurale Analyse eines Programmes ermittelt den Datenuss innerhalb einer Methode. Damit k onnen Methoden einzeln analysiert werden, indem man den Datenuss oder den Informationsuss ermittelt und auswertet. Die intraprozedurale Analyse reicht oft nicht aus, um bestimmte Probleme zu l osen. Dann muss auf eine interprozedurale Analyse zur uckgegrien werden, also eine Analyseform, bei der das komplette Programm betrachtet wird. Dabei werden die einzelnen Methoden analysiert und die Ergebnisse im Zusammenhang des kompletten Programms ausgewertet. Dazu ist es n otig, einen Aufrufgraphen (im Folgenden engl. Callgraph) f ur das Programm zu erstellen, damit es m oglich ist, abzusehen, welche Methoden andere Methoden aufrufen. So k onnen dann die intraprozeduralen Analysen zu einer interprozeduralen Analyse verkn upft werden. Weitere Details und verschiedene Formen von Callgraphen werden in Kapitel 3.2 dargestellt. Besonders bei Analysen wie der Points-to Analyse 3.3 ist es sinnvoll, auf eine interprozedurale Analyse zur uckzugreifen, da es meist nicht ausreicht, zu analysieren, wohin die Referenzen innerhalb einer Methode zeigen k onnen, sondern, wohin sie w ahrend des kompletten Programmablaufs zeigen k onnen.

1.5.2 Kontextinsensitive und kontextsensitive Analyse


Die Nutzung einer kontextinsensitive Analyse hat zur Folge, dass die Analyse unabh angig vom Verlauf der Methodenaufrufe durchgef uhrt wird und jedes Statement

28

1.5 Klassikation von Programmanalysen einzeln analysiert wird. Um die Ergebnisse einer Methode auszuwerten, ist es in vielen Analyseformen jedoch notwendig, den Inhalt der Methode bzw. die Berechnungen in Abh angigkeit vom Methodenaufruf zu analysieren. Die Ber ucksichtigung des Aufrufkontext ist der Unterschied zwischen einer kontextinsensitiven und einer kontextsensitiven Analyse. [1] Im folgenden Beispiel 1.9 beinhaltet die Methode eine einfache Berechnung.
Listing 1.9: Beispiel fu at / Kontextinsensitivit at r Kontextsensitivit
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

public class Example { public static void main(String[] args) { int i = 0; int j = 1; i = calculate(i); j = calculate(j); } private static int calculate(int k){ if(k > 0) { k = k + 2; } return k; } }

Die Arbeitsweise der Methode kann zwar schon vorab analysiert werden, jedoch sind mehrere Ergebnisse abh angig vom u bergebenen Parameter der Methode m oglich. Damit die Analyse genauer wird und nicht nur eine Absch atzung des Ergebnisses stattndet, wird die Methode in ihrem Aufrufkontext betrachtet. In Beispiel 1.9 werden f ur calculate(i) und calculate(j) nicht nur verschiedene Ergebnisse, n amlich i = 0 und j = 3 berechnet, sondert auch der Ablauf des Programms andert sich.

1.5.3 Flussinsensitive und usssensitive Analyse


Eine ussinsensitive Analyse betrachtet eine Anweisung unabh angig vom Ort und getrennt von den weiteren Anweisungen, also ohne den Zusammenhang des Programms. Das heit, es wird nur das einzelne Statement an sich ausgewertet. Eine usssensitive Analyse zieht auch in Betracht, ob mit diesem Statement, in dem z.B. eine lokale Variable verarbeitet wird, vorher oder nachher im Programmverlauf gearbeitet wird und sich z.B. der Variableninhalt andert.

29

1.5 Klassikation von Programmanalysen Das Beispiel 1.10 zeigt ein kleines Programm, in dem einer Stringvariable id ein Wert durch den Methodenaufruf getId() zugewiesen wird. Eine weitere Variable id2 erh alt den Wert "text". Darauf folgend wird der Variable id id2 zugewiesen und damit der urspr ungliche Wert von id vernichtet.

Listing 1.10: Beispiel fu at / Flussinsensitivit at r Flusssensitivit


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

public class Example { public static void main(String[] args) { String id = getId(); String id2 = "text"; id = id2; } private static String getId() { String id = "phoneId"; return id; } }

Im Rahmen einer usssensitiven Analyse w urde f ur die ganze Methode ermittelt, dass in Zeile 6 der Wert von id nicht mehr den Wert von getId(), sondern den von id2 besitzt und der ehemalige Wert nicht mehr vorhanden ist. Wenn man das Programm bspw. nun nach Aufrufen von getId() analysiert, da in diesem Zusammenhang z.B. eine Telefonnummer o. a. eines mobilen Endger ates gespeichert wird, w urde im Falle dieser Analyseform klar, dass der Aufruf von getId() vernachl assigt werden kann. In diesem Zusammenhang wird die Variable id ohne eine zwischenzeitliche Ver anderung u berschrieben. Wenn das gleiche Beispiel nun im Rahmen einer ussinsensitiven Analyse bearbeitet wird, w urde der Aufruf von getId() nicht vernachl assigt, da jede Zeile ohne den Zusammenhang analysiert wird. Das Ergebnis w urde in diesem Fall deutlich konservativer ausfallen, da man davon ausgehen w urde, dass die Telefonnummer, eine sensitive Information, gespeichert wird.

30

2 Frameworks zur Datenussanalyse

2 Frameworks
In diesem Kapitel werden die drei Frameworks zur Datenussanalyse von JavaProgrammen Jif, WALA und Soot vorgestellt. Diese Frameworks sind alle f ur eine Daten- bzw. Informationsussanalyse geeignet, jedoch verhalten sich die Verfahren dazu unterschiedlich. Daher wird im Folgenden f ur jedes Framework ein kleines Beispiel gegeben und die Entscheidung f ur bzw. gegen die Nutzung in dieser Arbeit erkl art.

2.1 Jif
Jif [20] steht f ur Java + Information Flow (Java und Informationsuss) und ist eine Programmiersprache aufbauend auf Java und Polyglot, einem Framework zur Erstellung von Java Erweiterungen (s. [21]), entwickelt vom Department of Computer Science der Cornell Universit at, USA. Die Besonderheit von Jif ist, dass durch das denierte Typsystem dieser Sprache von einer sicheren Sprache gesprochen werden kann, denn Jif beinhaltet eine Informationsuss- und Zugriskontrolle w ahrend der Kompilier- und Laufzeit. Die Programme, die mit Jif entwickelt werden, werden hinsichtlich des Informationusses durch den Compiler analysiert und in Java Programme umgesetzt. Durch einen regul aren Java Compiler werden die Java-Programme ausgef uhrt. Bei Jif handelt es sich um eine Erweiterung von Java-Quellcode, da hier Labels, also Bezeichnungen, in den Code eingef ugt werden, die die Nutzung der Variablen etc. denieren. In [20] wird folgendes Beispiel erl autert: int AliceBob x;. Der Unterschied von Jif zu Java liegt in der Erg anzung einer Bezeichnung bzgl. der Besitzer des Inhaltes von x, d.h. hier wird nicht nur die Variable x als ein Integer int deklariert, es wird noch zus atzlich deniert, wer die Inhalte von x lesen oder ver andern darf. In diesem Fall kann Alice komplett auf die Variable x zugreifen, sie ver andern

31

2.2 Wala und anderen zug anglich machen, wie in diesem Fall Bob. Bob darf die Inhalte aus x ebenfalls auslesen. Wenn die Zuweisung in entgegengesetzer Richtung dargestellt ist, also int AliceBob x;, bedeutet das, dass die Inhalte von x von Alice kontrolliert werden und Bob erlaubt wird, x zu beeinussen. Mit Hilfe dieser Erweiterung des Codes kann eine Informationsussanalyse erstellt werden. Ein Plugin f ur die Entwicklungsumgebung Eclipse (s. [27]) wird durch die Penn State University, USA, zur Verf ugung gestellt (s. [58]), um eine einfachere Handhandhabung und u oglichen. bersichtliche Vorgehensweise zu erm Die Nutzung von Jif bezieht sich jedoch mehr auf die Erstellung von Typregeln durch die Erweiterung von Java und ist nicht f ur die Analyse auf der Ebene von Bytecode entwickelt worden, weshalb in diesem Zusammenhang auf eine Nutzung von Jif zun achst verzichtet wird.

2.2 Wala
WALA [18] ist ein Open Source Framework zur statischen und dynamischen Analyse von Java Bytecode und JavaScript. WALA steht f ur T. J. Watson Libraries for Analysis. Seit 2006 wird WALA als Open Source Projekt entwickelt, wobei die urspr ungliche Entwicklung einem Forschungsprojekt des IBM T.J. Watson Research Center [62] zu Grunde liegt. Neben der Nutzung und Erweiterung des kompletten Frameworks wird hier ebenfalls ein Eclipse-Plugin angeboten. Die Hauptkomponenten von WALA liegen in der Class Hierarchy Analysis (s. 3.2.2), der interprozeduralen Datenussanalyse (s. 1.4) und der Pointer Analyse bzw. Aufrufgraphkonstruktion (s. 3.3 und 3.2). WALA bietet dazu verschiedene Instrumente und Datenstrukturen, die u.a. die Berechnung eines Kontrollussgraphens usw. erm oglichen sowie ein Toolkit namens Shrike [17] zum Lesen, Bearbeiten und Schreiben von Java-Bytecode. Im Zusammenhang mit Shrike wird eine Bibliothek Namens Dila [16] genutzt, die das Laden eigens angepasster Klassen zur Instrumentierung des Bytecodes vor der Ausf uhrung des Programms unterst utzt. Shrike beinhaltet f ur den Umgang mit Bytecode und dessen Manipulation zwei verschiedene Konzepte, einmal die ShrikeCT sowie die ShrikeBT Klassen. ShrikeCT wird zum Lesen und Schreiben von .class Dateien mit Hilfe der Klassen ClassReader und ClassWriter genutzt. Die Informationen u ber den Bytecode lassen sich mit Hilfe der Klasse MethodData, die Informationen u ber eine Methode zur uckliefert, generieren. Die Klasse MethodEditor erm oglicht die Ver anderung des Bytecodes einer Methode und die Klasse ClassInstrumenter

32

2.2 Wala einer kompletten Klasse, auf die mit Hilfe der o.g. Klassen ClassReader zugegrien und mit ClassWriter geschrieben werden kann. Die Nutzung der genannten Klassen wird in Listing 2.1 aus [22] gezeigt.

Listing 2.1: Beispiel fu r die Manipulation von Bytecode mit Shrike / WALA
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

//Methode zur Manipulation von Bytecode instrument(byte[] orig, int i) { // Hilfsklasse zur Manipulation von Bytecode ClassInstrumenter ci = new ClassInstrumenter(orig); // Speicherung von Methodeninformationen MethodData md = ci.visitMethod(i); doInstrumentation(md); // Schreibt Klasse in Java-Bytecode ClassWriter w = ci.emitClass(); byte[] modified = w.makeBytes(); } doInstrumentation(MethodData md) { // Veraendert die Methoden einer Klasse mit Hilfe von Patches MethodEditor me = new MethodEditor(md); me.beginPass(); // Fuegt Patches in die Methode ein me.insertAtStart(new Patch() {...}); me.insertBefore(j, new Patch() {...}); ... // Anwendung der Patches me.applyPatches(); me.endPass(); }

Mit Hilfe der Initialisierung der Klasse ClassInstrumenter in Zeile 5 wird der Zugri auf eine Vorhandene Klasse gegeben und durch die Erzeugung eines neuen MethodData Objekts in Zeile 7, alle Informationen der angefragten Methode geholt. Die Methode wird dann durch den Aufruf in Zeile 9 ver andert, indem ein neues MethodEditor Element erzeugt wird (Zeile 17) und mit Hilfe von Patches Ver anderungen in den Bytecode eingef ugt werden. Zuletzt wird die ver anderte .class Datei der Klasse durch den ClassWriter geschrieben. Bei WALA handelt es sich um ein Framework, welches, neben dem Java Bytecode, auf einer internen Bytecoderepr asentation arbeitet, die jedoch nicht ver andert werden kann. Die Arbeit auf direktem Java-Bytecode kann sich auf Grund der Vielzahl an Opcodes als un ubersichtlich herausstellen. Im Gegensatz dazu arbeitet das Framework Soot, dass im folgenden Kapitel 2.3 beschrieben wird, auf vier verschiedenen, optimierten Bytecode Repr asentationen, die individuell angepasst werden k onnen.

33

2.3 Soot

2.3 Soot
Soot [46] ist ein Framework zur statischen Analyse und Optimierung von Java Programmen. Verantwortlich sind die Entwickler der Sable Research Group an der McGill Universit at in Montreal, Kanada, die seit 1999 fortlaufend an der Weiterentwicklung des Frameworks arbeiten. Die aktuelle Version 2.5.0 stammt vom 22. Januar 2012. Soot ist speziell f ur Java entwickelt worden, das Framework selber wurde und wird ebenfalls in Java geschrieben. Es gibt mehrere M oglichkeiten, Soot zu verwenden, entweder indem das komplette Framework mit Hilfe von SVN heruntergeladen und kompiliert wird oder Soot als Standalone Tool, dass entweder vorkompiliert heruntergeladen oder als Eclipse Plugin installiert wird. Die Optimierung von Java-Bytecode [72] und die verschiedenen Analyseverfahren, wie u.a. Bausteine zur Datenussanalyse, Klassen zum Aufbau von Callgraphen, zur Points-to Analyse sowie zur Generierung abstrakter Kontrollussgraphen machen Soot zu einem vielf altigen Werkzeug. Detailiertere Informationen zur Erstellung einer Daten-(Informations-)ussanalyse und zur Points-to-Analyse mit Soot werden in 4.1.4 und 4.2.2 n aher erl autert und Beispiele gegeben. Die Vielfalt der Analysetechniken sowie die aktiven Forschungsarbeiten der Soot-Entwickler, besonders hinsichtlich der Analyse mobiler Systeme, war, neben der sehr aktiven Mailingliste, was auf eine groe Zahl an Nutzern von Soot zur uckschlieen l asst, ausschlaggebend f ur die Nutzung von Soot im Rahmen dieser Arbeit, um eine eigene Analyse zu implementieren. Die Analyse wird auf BytecodeEbene durchgef uhrt, dabei stellt Soot vier Repr asentationen f ur Bytecode zur Verf ugung, um m oglichst viele Analysetechniken zu vereinfachen. Die Darstellungen sind Baf, Grimp, Jimple und Shimple und werden im folgenden Kapitel Soot Bytecode Formate 3.1 weitergehend erl autert.

34

3 Datenuss- und Points-to-Analyse mit Soot

3 Datenuss- und Points-to-Analyse mit Soot


In diesem Kapitel wird die Datenussanalyse auf Basis von Soot dargestellt. Soot [72] ist ein Java Optimierungsframework, welches auf Basis mehrerer BytecodeRepr asentationen arbeitet. Zuerst werden die unterschiedlichen Bytecodeformate im Folgenden dargestellt, danach werden die verschiedenen Formen von Callgraphen sowie die Datenussanalyse mit Soot gezeigt. Zuletzt wird die Analyse von Zeigern sowie die Points-to-Analyse erl autert und neben g angigen Algorithmen eine Ubersicht im Umgang mit diesen Analyseformen unter Soot gegeben.

3.1 Soot Bytecode Formate


Durch Soot werden gleichzeitig mehrere Analyse- und Optimierungsmethoden gegeben. Damit diese Analyse besonders ezient verl auft, bietet Soot mehrere Repr asentationen von Bytecode an: Baf, Jimple, Grimple und Shimple. In den folgenden Abschnitten werden diese vier Bytecode-Formate erl autert und Beispiele gegeben, welche sich auf Beispiel 3.1 und Abwandlungen davon beziehen. Dabei wird ein kleiner Ausschnitt eines Java-Programms (links), in dem in Zeile 4 eine String-Variable erzeugt und mit dem Wert text belegt wird, gezeigt. Analog dazu wird im Bytecode (rechts) in Zeile 23 durch den Befehl LDC der String text auf den Kellerspeicher geschrieben. Zeile f unf des Java-Beispiels speichert den R uckgabewert der Funktion getId() in der Variable id, was im Bytecode in Zeile 28 zu nden ist. Dort wird der Methodenaufruf durch den Befehl INVOKESPECIAL dargestellt und in der darauolgenden Zeile eine Referenz auf id durch ASTORE gespeichert. Der Aufruf von System.out.println() wird durch den Befehl INVOKEVIRTUAL f ur den Aufruf einer virtuellen Methode im Bytecode in Zeile 34 umgesetzt.

35

3.1 Soot Bytecode Formate

Abbildung 3.1: Java Quellcode und Java Bytecode im Vergleich

36

3.1 Soot Bytecode Formate Die Methode getId(() aus Zeile 10 wird in Zeile 46 im Bytecode dargestellt, die Zeilen 49 und 50 zeigen hier den return Befehl aus Zeile 11 des Java-Beispiels. Die folgenden Abschnitte Baf, Jimple, Shimple und Grimp zeigen die Umsetzung dieses Beispiels in den internen Formaten des Soot Frameworks.

3.1.1 Baf
Die Baf-Repr asentation liegt nahe an der Darstellung von Java-Bytecode und basiert ebenfalls auf einem Kellerautomaten. Im Gegensatz zu Java-Bytecode abstrahiert Baf jedoch den Konstantenpool der im Java-Bytecode enthalten ist. Eine Analyse von kellerbasiertem Bytecode ist deutlich komplizierter als eine Analyse von registerbasiertem Code, jedoch l asst sie sich in bestimmten F allen nicht vermeiden, bspw. zur Peephole Optimierung (s. [47]). Daf ur wurde Baf als einfachere Version von Java-Bytecode entwickelt, ein Bytecode, der noch nah an der urspr unglichen Java-Version liegt, jedoch Optimierungen und innerhalb des Soot Frameworks noch andere Vorteile mit sich bringt. Die entscheidenden Unterschiede zu Java Bytecode und damit auch die Gr unde f ur die Kompaktheit und Ubersichtlichkeit von Baf sind die Einf uhrung von Typattributen, expliziten Namen f ur lokale Variablen sowie das Zusammenfassen von mehreren Befehlen in je einen Befehl, der f ur alle gilt. [71] Das Beispiel 3.1 zeigt den Baf-Bytecode f ur den obengenannten Java-Code bzw. Java-Bytecode.

Listing 3.1: Baf-Bytecode


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

public void <init>() { word r0; r0 := @this: Example; load.r r0; specialinvoke <java.lang.Object: void <init>()>; return; } public void main(java.lang.String[]) { word r0, r1; r0 := @this: Example; r1 := @parameter0: java.lang.String[]; push "text"; store.r r1; load.r r0; specialinvoke <Example: java.lang.String getId()>; store.r r1; staticget <java.lang.System: java.io.PrintStream out>; load.r r1; virtualinvoke <java.io.PrintStream: void println(java.lang.String)>;

37

3.1 Soot Bytecode Formate

22 23 24 25 26 27 28 29 30

return; } private java.lang.String getId() { word r0; r0 := @this: Example; push "secret"; return.r; }

Die lokalen Variablen werden hier zun achst vom Typ word initialisiert, und in den Zeilen 4, 12, 13 und 27 geladen. Die verschiedenen Formen, um innerhalb von Java-Bytecode Inhalte in die Variablen zu schreiben (in obigem Beispiel ist es der Aufruf LDC), werden hier durch den Befehl push in Zeile 14, der den Wert text auf den Kellerspeicher legt, zusammengefasst. Der Methodenaufruf von getId() wird in Zeile 17 durch den specialinvokeBefehl dargestellt. Der Baf-Bytecode ist insgesamt deutlich lesbarer und kompakter als der Java-Bytecode f ur das selbe Beispiel, auch die Namensgebung l asst den Code nachvollziehbarer erscheinen.

3.1.2 Jimple
Jimple [73] ist eine weitere Zwischenrepr asentation von Bytecode in Soot. Im Gegensatz zu Java-Bytecode und Baf handelt es sich bei dieser Darstellung um eine registerbasierte Schreibweise von Bytecode. Jimple ist eine getypte Drei-AdressRepr asentation, die aus Java bzw. Javabytecode erstellt oder direkt innerhalb von Soot geschrieben werden kann. Bei Drei-Adress-Code handelt es sich um einen Zwischencode, der Form Ergebnis = Operand1 Operator Operand2. Die Syntax eines Drei-Adress-Codes erlaubt nur einen Operator auf der rechten Seite der Zuweisung [1]. Berechnungen, die aus mehreren Operatoren bestehen werden in einzelne Teilberechnungen zerlegt. In Jimple existiert keine direkte Stackdarstellung, stattdessen wird hier mit expliziten Typzuweisungen f ur die Variablen gearbeitet. Eine groe Vereinfachung der Analyse bietet Jimple durch die Zusammenfassung der u ber 200 Opcodes des Java-Bytecodes in 15 Jimple-Statements. Die Statements bestehen u.a. aus CoreStatements, den Statements, die den intraprozeduralen und denen, die den interprozeduralen Kontrolluss beschreiben. Unter den Core-Statements versteht man Statements wie z.B.ein AssignStmt, also eine Zuweisung. Zu den Statements, die den intraprozeduralen Kontrolluss beschreiben, geh ort u.a. das IfStmt, was f ur eine If-Anweisung steht. Der interprozedurale Kontrolluss wird durch Statements wie z.B. das InvokeStmt dargestellt, welches f ur einen Methodenaufruf

38

3.1 Soot Bytecode Formate steht [25]. Das folgende Listing 3.2 zeigt die Jimple-Darstellung f ur das Beispiel in Abb. 3.1.

Listing 3.2: Jimple-Bytecode


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

public void <init>() { Example r0; r0 := @this: Example; specialinvoke r0.<java.lang.Object: void <init>()>(); return; } public void main(java.lang.String[]) { Example r0; java.lang.String[] r1; java.lang.String r2, r3; java.io.PrintStream $r4; r0 := @this: Example; r1 := @parameter0: java.lang.String[]; r2 = "text"; r3 = specialinvoke r0.<Example: java.lang.String getId()>(); $r4 = <java.lang.System: java.io.PrintStream out>; virtualinvoke $r4.<java.io.PrintStream:void println(java.lang.String)>(r3); return; } private java.lang.String getId() { Example r0; r0 := @this: Example; return "secret"; }

Die Initialisierung der Klasse Example wird zu Beginn in den Zeilen 1-8 vorgenommen wie auch zu Beginn der Methode main() alle Register in den Zeilen 12-17 initialisiert werden. In Zeile 18 wird r1 mit dem String-Array Parameter args der main() - Methode initialisiert. r2 steht f ur die String-Variable id des Beispiels 3.1 und wird mit dem Wert text belegt. In Zeile 20 wird der Methodenaufruf getId() durch das specialinvoke-Statement dargestellt und der R uckgabewert der Funktion in r3 gespeichert. Die Bildschirmausgabe von id wird in den Zeilen 21 und 22 dargestellt. Jimple ist im Vergleich zum Java-Bytecode, durch die Optimierungen, die Reduzierung der Statements sowie der einfacheren Lesbarkeit auf Grund der Anlehnung an den Java-Code eine Repr asentation von Bytecode, die eine deutlich einfachere Analyse vermuten l asst.

39

3.1 Soot Bytecode Formate Im folgenden Absatz 3.1.3 wird Shimple, eine weitere Form von Jimple, erl autert.

3.1.3 Shimple
Bei Shimple [25] handelt es sich um die Static Single Assignment Form von Jimple. Bei einer Static Single Assignment Form (im Folgenden: SSA) handelt es sich um eine Bytecoderepr asentation, bei der jede Variable, besonders bei einer Mehrfachnutzung, einen eindeutigen Namen erh alt [1]. D.h., bei einer Berechnung wie a = b + c und d = a + c w urden a und c im Rahmen einer SSA einen eindeutigen Wert erhalten und die Berechnung w urde folgendermaen aussehen: a1 = b + c1 und d = a2 + c2 . Um den Unterschied zwischen Jimple und Shimple deutlicher zu zeigen, wurde die Methode main() aus Abbildung 3.1 symbolisch durch eine while-Schleife und eine if-Anweisung erg anzt, s. Listing 3.3 Zeile 2-21.

Listing 3.3: Shimple-Bytecode


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

//Ergaenzung des Java-Beispiels: public static void main(String[] args) { int x = 0; String id = ""; while(x <= 1) { if(x == 0) { id = "text"; } else { id = getId(); } x++; } leak(id); } //Shimple Bytecode: public class Example extends java.lang.Object { public static void main(java.lang.String[]) { java.lang.String[] args; int x, temp$2, temp$3, x_1, x_2; java.lang.String id, temp$0, temp$1, id_1, id_2, id_3, id_4; args := @parameter0: java.lang.String[]; x = 0;

40

3.1 Soot Bytecode Formate

34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73

(0)

id = ""; label0: id_1 = Phi(id #0, id_4 #3); x_1 = Phi(x #0, x_2 #3); nop; if x_1 <= 1 goto label1; goto label5; label1: nop; if x_1 == 0 goto label2; goto label3; label2: nop; temp$0 = "text"; id_3 = temp$0; goto label4; label3: nop; temp$1 = staticinvoke <Example: java.lang.String getId()>(); id_2 = temp$1; label4: id_4 = Phi(id_3 #1, id_2 #2); nop; temp$2 = x_1; temp$3 = temp$2 + 1; x_2 = temp$3; goto label0; label5: nop; staticinvoke <Example: void leak(java.lang.String)>(id_1); return; }

(1)

(2)

(3)

Shimple ist prinzipiell identisch zu Jimple, der Bytecode wird jedoch durch zwei Eigenschaften unterschieden. Eine Eigenschaft ist die Verwendung mehrerer Variablennamen f ur die Mehrfachnutzung einer Variable um die Eindeutigkeit Variablen zu garantieren. Die andere ist die Erg anzung des Bytecodes um die Funktion Phi, die den Wert einer Variable abh angig vom Kontrolluss des Programms zur uckgibt. Der Bytecode in Listing 3.3 zeigt diese beiden Eigenschaften. In den Zeilen 29 und 30 werden die eindeutigen Variablennamen u.a. f ur die Variablen x und id durch x_1, x_2 sowie id_1, id_2, id_3, id_4 initialisiert. Die Funktionen Phi, die den R uckgabewert f ur x_1 bzw. id_1 zur ucklieferen, sind in den Zeilen 37, 38 und 62 deniert. Die verschiedenen Werte, die id annehmen kann, sind in den Zeilen 34, 53

41

3.1 Soot Bytecode Formate und 59 gegeben, abh angig vom Kontrolluss gibt die dazugeh orige Funktion Phi den passenden R uckgabewert an. Shimple kann damit einen expliziten Verlauf des Kontrollusses darstellen.

3.1.4 Grimp
Grimp [25] ist die vierte Bytecode-Darstellung innerhalb von Soot. Die Darstellung von Grimp ahnelt dem Jimple Code sehr, jedoch werden mehrere Anweisungen gruppiert dargestellt und die Anweisung new eingef uhrt, womit das gesamte Erscheinungsbild des Bytecodes sehr dem Java-Quellcode ahnelt. Da Grimp diese speziellen Eigenschaften besitzt, wurde auch dieses Beispiel erg anzt, in dem in den Zeilen 2-10 in Listing 3.4 die Erzeugung eines neuen Objektes umgesetzt wird.

Listing 3.4: Grimp-Bytecode


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

//Ergaenzung des Quellcode public static void main(String[] args) { String id; //Erzeugung eines neuen Objekts Example1 e = new Example1(); //Zuweisung des Rueckgabewerts von e.getId() an id id = e.getId(); leak(id); } //Grimp-Bytecode public class Example extends java.lang.Object { public static void main(java.lang.String[]) { java.lang.String[] args; java.lang.String id, temp$1; Example1 e, temp$0; args := @parameter0: java.lang.String[]; temp$0 = new Example1(); e = temp$0; //Aufruf von getId() in Grimp temp$1 = e.<Example1: java.lang.String getId()>(); id = temp$1; staticinvoke <Example: void leak(java.lang.String)>(id); return; } ... } //Aufruf von getId() in Jimple r3 = virtualinvoke r2.<Example1: java.lang.String getId()>();

42

3.2 Aufrufgraphen Das Ergebnis im Grimp-Bytecode ist in Zeile 23 zu sehen, dort wird der Befehl new Example 1() dargestellt. Eine Verk urzung der Anweisungen wird in Zeile 26 sichtbar. Im Vergleich zu Zeile 34, in der die selbe Java-Anweisung in Jimple dargestellt ist, wird hier auf den Befehl virtualinvoke verzichtet. Die Variablennamen id und e bleiben erhalten und die Register erhalten alle verst andliche Namen. Grimp ist die Bytecode-Darstellung, die, durch die Ann aherung an Java Quellcode, am Besten f ur eine manuelle bzw. menschliche Analyse des Kontrollusses, ohne die Nutzung eines Programms, geeignet ist. Im Rahmen dieser Arbeit wurde auf Basis des Jimple-Bytecodes auf Grund der zahlreichen Optimierungsm oglichkeiten gearbeitet.

3.2 Aufrufgraphen
Da Aufrufgraphen f ur die Erstellung einer interprozeduralen Analyse notwendig sind, wird in diesem Kapitel zun achst ein allgemeiner Einstieg in die Nutzung und Generierung von Aufrufgraphen gegeben und verschiedene Algorithmen vorgestellt. In Soot werden verschiedene Optionen zur Erstellung von Call Graphen gegeben, die hier vorgestellt werden. Neben einer Implementierung nach der Class Hierarchy Analyse kann das Soot Pointer Analysis Research Kit (im Folgenden SPARK) genutzt werden. Bei SPARK handelt es sich um ein Teil des Soot-Frameworks zur Analyse von Pointern und Pointerzielen sowie zur Erstellung von Aufrufgraphen. Neben einem On-the-Fly Aufrufgraphen, der w ahrend der Berechnung der Pointerziele erstellt wird, kann auch ein Graph anhand der Rapid und Variable Type Analysis erstellt werden. Diese M oglichkeiten und die dazugeh origen Algorithmen werden im Folgenden erl autert.

3.2.1 Allgemeines
Bei einem Aufrufgraphen (engl. Call Graph) handelt es sich um eine Darstellung der Methodenaufrufe eines Programms in Form eines gerichteten Graphens. Genutzt werden Aufrufgraphen zur visuellen Darstellung von Programmen, um ein besseres Verst andnis vom vorliegenden Programm zu erhalten, zur Optimierung des Programms oder zur Analyse.

43

3.2 Aufrufgraphen Es wird unterschieden zwischen einem statischen und einem dynamischen Aufrufgraphen, sowie zwischen intra- und interprozeduralen, kontext(in)sensitiven und uss(in)sensitiven Aufrufgraphen [33]. Der Unterschied wird durch die Methode zur Generierung des Graphens festgelegt. Ein statischer Graph repr asentiert alle m oglichen Methodenaufrufe anhand der Analyse des Programmcodes, ohne dass das Programm ausgef uhrt wird. Ein dynamischer Graph wird w ahrend der Laufzeit des Programms erstellt und basiert auf den Methodenaufrufen w ahrend der Ausf uhrung. Da sich die Analyse im Rahmen dieser Arbeit auf eine statische Analyse bezieht, wird im Folgenden der dynamische Aurfufgraph auer acht gelassen. N ahere Informationen bietet dazu u.a. [6]. Formal deniert, wird ein statischer Aufrufgraph Gp eines Programms p mit den Prozeduren {p1 . . . pn } durch Gp = (N, S, E, r). Dabei sind N = {p1 . . . pn } die Knoten, also die Prozeduren des Programms, S eine Markierung f ur die Aufrufstelle im Programm, E N N S die Menge aller Kanten und r N der Startknoten des Graphs. [50] Im Folgenden wird ein Java Beispiel in Listing 3.5 und die Darstellung f ur einen einfachen statischen Aufrufgraphen aufgezeigt.

Listing 3.5: Beispiel fu r einen Aufrufgraphen


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

public class CallGraph { public static void main(String[] args) { int result = method1(1); method2(result); } public int method1(int i) { method3(i); return i; } public void method2(int j) { j = j+1; method3(j); } public int method3(int k) { if( k < 0) { k = 5; } return k; } }

44

3.2 Aufrufgraphen Dabei ruft die Methode main() die Methoden method1() und method2() auf. Innerhalb dieser beiden Methoden wird jeweils die Methode method3() aufgerufen. Dieser Verlauf wird in Abbildung 3.2 gezeigt.

Abbildung 3.2: Beispiel f ur einen einfachen Aufrufgraphen

In Soot k onnen Aufrufgraphen mit Hilfe des Pointer Analyse Frameworks Paddle, SPARK sowie der Class Hierarchy Analysis (s.u.) generiert werden. Auf Paddle wird in diesem Zusammenhang verzichtet, da diese Arbeit auf der Basis von SPARK erstellt wurde. In Kapitel 3.3.5 wird jedoch die Points-to-Analyse mit Paddle erl autert, da sie f ur zuk unftige Arbeiten in Betracht gezogen werden k onnte. Innerhalb der Nutzung von SPARK sind verschiedene Implementierungen m oglich, welche in den n achsten Abschnitten gezeigt werden.

3.2.2 Aufrufgraph-Generierung mit SPARK


SPARK bietet anhand einer Vielzahl von Optionen mehrere M oglichkeiten, einen Aufrufgraphen zu generieren. Durch die Einstellungen, die man SPARK zu Beginn der Ausf uhrung u bergeben kann, kann man zwischen einem Class Hierarchy, Rapid Type Analysis, Variable Type Analysis oder einem On-the-FlyAufrufgraphen, der w ahrend der PointerAnalyse erstellt wird, w ahlen. In den folgenden Abschnitten werden die ersten drei Typen erl autert, der On the Fly Aufrufgraph wird im Abschnitt 3.3.5 erkl art.

45

3.2 Aufrufgraphen

Aufrufgraphgenerierung auf Basis der Class Hierarchy Analysis


Die Class Hierarchy Analyse (CHA) ist eine globale Analyse der Klassenhierarchie eines Programmes. [19] beschreibt die Vorgehensweise bei der CHA als basierend auf einer statischen intraprozeduralen Klassenanalyse. Das Ziel der Klassenanalyse ist, die dynamische Methodensuche f ur eine Klasse direkt durch Methodenaufrufe auszutauschen. Dabei werden f ur jede Variable einer Methode Mengen aus Klassen gebildet, die die Instanzen zusammenfasst, die in der Variable gespeichert sein k onnten. Bei der CHA handelt es sich konkret um die Menge Cone(C ). In Cone(C ) werden alle Subklassen C einer Klasse C inklusive C f ur eine Variable gespeichert. Die CHA liefert damit Informationen u ber die Unterklassen C sowie die in den C denierten bzw. u berschriebenen Methoden. Zum Ablauf der Analyse wird neben der Menge Cone(C ) f ur jede Methode ein applies to Set berechnet, in dem alle Klassen gespeichert werden, die diese Methodenimplementierung nutzen. Das applies to Set wird formal deniert als: applies to(C :: m) = Cone(C ) \
D { D | U nterklasse D berschreibt m }

Cone(D)

Im n achsten Schritt wird dann gepr uft, ob ein applies to Set f ur die zu analysierende Methode existiert, welches mindestens die Menge der m oglichen Empf anger der Methode enth alt. Ist das der Fall, kann diese Methodenimplementierung verwendet werden. [9] Um einen Aufrufgraphen unabh angig von SPARK zu nutzen, kann man in Soot mit Hilfe eines CHATransformers den Graphen f ur ein gegebenes Programm generieren. In SPARK wird die Generierung eines Aufrufgraphens mit Hilfe der CHA durchgef uhrt, in dem die Option On Fly Call Graph (on-fly-cg) auf false gesetzt wird (default ist true).

Rapid Type Analysis


Bei der Rapid Type Analysis [7] handelt es sich um ein ussinsensitives Verfahren zur Generierung von Aufrufgraphen. Um einen Call Graph nach der Rapid Type Analysis zu generieren, wird zuerst ein Aufrufgraph auf Basis der CHA gebildet. Damit die verschiedenen m oglichen Typen der Knotenelemente analysiert und in einer Menge zusammengefasst werden k onnen, wird der Graph, beginnend bei dem

46

3.2 Aufrufgraphen Wurzelknoten main, traversiert. Dabei werden virtuelle Aufrufe vorerst ignoriert und erst, wenn ein Konstruktor im Graphen gefunden wird, der im Programmverlauf aufgerufen, also genutzt wird, werden die virtuellen Methoden der zugeh origen Klasse analysiert und die dadurch entstehenden m oglichen Typen der Menge der instantiierten Typen hinzugef ugt. Ebenso wird ein Program Virtual Call Graph, gebildet, der neben den Funktionsaufrufen auch die Semantiken der virtuellen Funktionen ber ucksichtig. Dabei wird ebenfalls eine Menge der instantiierten Klassen gebildet, mit dem Unterschied, dass davon ausgegangen werden kann, dass diese Elemente denitiv genutzt werden. Diese beiden Mengen werden geschnitten, so dass nur noch die Typen aus lebendigen Aufrufen, die auch durch den CHA-Algorithmus gefunden wurden, u brig bleiben. Die Rapid Type Analysis vereinfacht die Analyse von Programmen, die groe Bibliotheken einbindet, da nur die genutzten Methoden analysiert werden. Diese Form von Analyse ist zwar ussinsensitiv und speichert keine Informationen u ur groe Programme, ber die analysierten Statements, dennoch ist sie, gerade f auf Grund ihrer schnellen Berechnung sinnvoll. In Zusammenhang mit SPARK kann die Emulation der Rapid Type Analysis durch das Setzen von soot.PhaseOptions.getBoolean( options, "rta") in Soot bezweckt werden. Allerdings wird in diesem Zusammenhang nur noch ein PointsToSet 4.2.2 f ur alle Variablen genutzt, was nur in bestimmten F allen sinnvoll ist.

3.2.3 Variable Type Analysis


Die in Soot genutzte Implementierung der ussinsensitiven Variable Type Analyse wird durch [68] beschrieben. Der Unterschied zu den vorhergegangenen Analysen ist, dass hier die Variablennamen als ihre Repr asentanten gelten. Da Jimple explizite Namen und Typen von lokalen Variablen beinhaltet und es nur drei verschiedene Variablentypen gibt (Ordinary, Field und Array Referenzen), kann diese Analyse im Rahmen von Soot sinnvoll umgesetzt werden. Dabei wird im ersten Schritt ein konservativer Call Graph auf Basis eines Worklist-Algorithmus und der CHA gebildet (s. [68]). Darauf aufbauend wird ein Typ-Propagations Graph erstellt, in dem zuerst f ur alle Felder f einer Klasse C innerhalb eines Programms P , die Referenzen sind, Knoten gebildet, die durch C.f markiert werden. Dann werden alle Methoden C.m, die der konservative Call Graph beinhaltet, gepr uft und analysiert indem:

47

3.3 Points-to-Analyse 1. f ur alle Parameter der Methode pi , die eine Objektreferenz sind, ein Knoten C.m.pi erstellt wird. 2. f ur alle lokalen Variablen li , die innerhalb einer Methode eine Objektreferenz sind, ein Knoten C.m.li angelegt wird und 3. f ur jedes this sowie jedes return innerhalb einer Methode ein Knoten C.m.this bzw. C.m.return konstruiert wird. Um die Knoten zu verbinden, werden nun f ur alle Zuweisungen zu Objekten, Kanten eingef ugt (dabei handelt es sich nicht nur um direkte Zuweisungen, sondern auch um Zuweisungen in Form von Methodenaufrufen bzw. R uckgabewerten).

Nachdem der Typ-Propagations Graph erstellt wurde, werden zwei weitere Phasen durchlaufen, die Initialisierung sowie die Typ-Propagation. Dabei wird zun achst f ur jede Zuweisung der Art var = new A() eine Menge der zu erreichenden Typen f ur var gebildet und A der Menge zugewiesen. Danach werden die stark zusammenh angenden Komponenten des Typ-Propagations Graphen in einem Oberknoten zusammengefasst, ebenso wie die zu jeder Komponente dazugeh orige Menge der erreichenden Typen f ur diesen Knoten vereint wird. Das Ergebnis dieser Vereinigung ist ein gerichteter Graph. Durch die Traversierung des Graphens von der Wurzel beginnend, k onnen die Typen der Knoten anhand ihrer Elternknoten im Graphen ermittelt werden. Um die Generierung des Aufrufgraphens mit Hilfe der Variable Type Analysis in Soot/SPARK zu nutzen, wird soot.PhaseOptions.getBoolean( options, "vta") gesetzt.

3.3 Points-to-Analyse
In diesem Kapitel werden zun achst die Unterschiede zwischen Zeigern und Referenzen erl autert. Es folgt eine Abschnitt zur Alias-Analyse, bei der gepr uft wird, ob zwei Zeiger bzw. Referenzen auf den selben Speicherbereich zeigen und ein weiterer Abschnitt, in dem die Points-to-Analyse zun achst allgemein erkl art wird. Im weiteren Verlauf werden zwei Verfahren zur Points-to-Analyse, der Steensgaard sowie der Andersen Points-to-Algorithmus, vorgestellt. Zuletzt wird die Points-to-Analyse innerhalb von Soot erl autert, die u.a. auf

48

3.3 Points-to-Analyse den beiden vorangestellten Algorithmen beruht.

3.3.1 Zeiger und Referenzen


Ein Zeiger [1] oder ein Pointer kann als eine Variable verstanden werden, in der eine Speicherplatzadresse gespeichert ist. An der Adresse im Speicher steht ein Objekt, eine Variable oder ggf. eine Anweisung. Es gibt verschiedene M oglichkeiten, auf Zeiger zuzugreifen und mit ihnen zu arbeiten, z.B. Berechnungen durchzuf uhren, ohne den Inhalt der Speicheradresse zu ver andern. Nicht jede Programmiersprache geht gleich oder u berhaupt mit Zeigern um. Obwohl es sich bei C++ und Java um zwei objektorientierte Hochsprachen handelt, wird in C++ ein direkter Zugri auf die Speicheradressen und eine dazugeh orige Zeigerarithmetik erm oglicht. Ebenso gilt dies f ur Objective-C, die Programmiersprache zur Entwicklung von Apps f ur iOS. Die M oglichkeit des direkten Zugris auf die Speicheradressen erschweren die statische Analyse dieser Programme enorm.

Abbildung 3.3: Darstellung des Java Heap und Stack. Zwei Variablen zeigen auf den selben Speicherbereich.

In Java wird nur mit Referenzen gearbeitet, ohne dass Zeiger oder Speicheradressen direkt ver andert werden k onnen.

49

3.3 Points-to-Analyse Referenzen sind Repr asentationen von Objekten oder Arrays, die im Speicher liegen. Ein neues Objekt wird in Java mit new [55] erzeugt: Example example1 = new Example();. Der Wert von example1 ist die Referenz auf das neue Objekt. Bei einer Kopie einer Referenz in eine andere Variable zeigen diese beiden Objekte auf den selben Speicherbereich, sind also Aliase. Abbildung 3.3 zeigt einen solchen Fall. Im folgenden Abschnitt wird die Analyse solcher Zusammenh ange erl autert.

3.3.2 Alias Analyse


Die Alias Analyse wird oft in ahnlichen Zusammenh angen genutzt wie die Pointsto-Analyse. Auch wenn sie oft als gleich dargestellt werden, gibt es einen Unterschied zwischen diesen beiden Analyse-Methoden. Mit der Alias Analyse kann ermittelt werden, ob zwei Zeiger bzw. Referenzen auf den gleichen Speicherbereich zeigen und wann dieser Fall eintritt, sie also Aliase sind. Dabei wird unterschieden zwischen einer Must- und May-Alias-Analyse. Bei einer Must-Alias Analyse wird gepr uft, ob zwei Zeiger denitiv auf den gleichen Speicherbereich zeigen, bei einer May-Alias Analyse ob die Zeiger m oglicherweise auf identische Speicherbereiche zeigen. Eine Alias Analyse kann wie in Kapitel 1.5 klassiziert werden. Dabei wird weiterhin noch zwischen einer typbasierten Analyse, die besonders eektive Ergebnisse bei typsicheren Sprachen wie Java liefert und einer ussbasierten Analyse, die f ur Programme in Sprachen mit Typecasts wie C++ genutzt wird, unterschieden. Eine Methode f ur eine Alias Analyse in Java wird von [77] aufgezeigt. Die im Folgenden dargestellte Points-to-Analyse kann dieses Problem ebenfalls l osen, jedoch wird in diesem Zusammenhang nicht gepr uft, ob die Pointer Aliase sind, sondern ob sich die Menge der Objekte, auf die ein Pointer zeigt, mit einer anderen solchen Menge schneidet.

3.3.3 Points-to-Analyse
Die Points-to-Analyse wird genutzt, um herauszunden, welche Zeiger auf welche Objekte im Speicher zeigen. Die Analyse wird anhand des Quellcodes bzw. des Bytecodes durchgef uhrt und geh ort damit zur Gruppe der statischen Programmanalysen. Es wird, wie bei der Alias-Analyse, zwischen der may- und der must Analyse unterschieden. Eine may-Analyse berechnet die Pointerziele, auf die ein

50

3.3 Points-to-Analyse Pointer w ahrend der Laufzeit des Programms zeigen kann, eine must-Analyse die Zeigerziele auf die ein Pointer denitiv zeigen wird. Das folgende Listing 3.6 soll demonstrieren, warum eine Pointer Analyse in dem Zusammenhang dieser Arbeit unverzichtbar ist:

Listing 3.6: Beispiel Referenzen


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public class PointsTo { public static void main(String[] args) { Example example1 = new Example(); Example example2 = new Example(); ... example2 = example1; example1.a = 1; System.out.println( example2.a ); example2.b = "Secret"; System.out.println( example1.b ); }

//Ausgabe = 1

//Ausgabe = Secret

Erkennbar ist hier, dass bei Zuweisungen nicht das vermeintlich zugewiesene Objekt kopiert wird, sondern die Referenz auf das Objekt, d.h. dass nun beide Variablen example1 und example2 auf den gleichen Bereich im Speicher zeigen. Auch wenn im weiteren Verlauf des Programms example1 nicht mehr existent w are, w are es weiterhin m oglich, durch example2 auf den ehemalig referenzierten Speicherbereich von example1 und damit auch auf den Inhalt, secret, zuzugreifen. Damit wird deutlich, dass im Zusammenhang mit der Analyse von AndroidApps eine Points-to-Analyse unumg anglich ist, da so verglichen werden kann, ob zwei Referenzen auf das gleiche Objekt zeigen. Sollte also die ID eines Smartphones oder andere sch utzenswerte Informationen anstelle von secret wie in Listing 3.6 weitergegeben werden, w urde es ohne eine Points-to-Analyse nicht bemerkt. Eine Points-to-Analyse ist jedoch, wie statische Analysen im Allgemeinen, unentscheidbar [38]. Es gibt verschiedene Ans atze f ur die Analyse von Zeigerzielen, zwei davon werden im Folgenden dargestellt.

51

3.3 Points-to-Analyse

3.3.4 Verfahren zur Points-to-Analyse


In diesem Abschnitt werden zwei Verfahren zur Berechnung von Points-to-Mengen vorgestellt. Zun achst wird der Steensgaard Algorithmus, dann der Andersen Algorithmus erl autert. Beide Algorithmen werden innerhalb von Soot in einer Kombination oder explizit genutzt.

Steensgaards Points-to-Analyse
Der Steensgard Algorithmus [67] geh ort zur Gruppe der ussinsensitiven interprozeduralen Analysen. Dennoch ist der entscheidende Vorteil gegen uber anderen Verfahren die Geschwindigkeit der Berechnung, die bei der Analyse groer Programme nicht zu vernachl assigen ist. Diese Analyse wurde urspr unglich f ur die Points-to-Analyse einer eigenen, C- ahnlichen, Sprache entwickelt. Der Algorithmus zur L osung des Points-To Problems und zur Erstellung des Points-To Graphens besteht aus zwei Stufen: Im ersten Schritt wird eine Typumgebung geliefert, die alle Variablen des Programms durch verschiedene Typvariablen repr asentiert, welche aus einer Union-Find-Datenstruktur bestehen. Der initiale Typ einer Typvariable ist ref ( ). Bei einer Union-Find-Datenstruktur [56] handelt es sich um eine Unterteilung einer Menge in drei Teilbereiche, Union, Find und MakeSet. Diese Unterteilung wird hier genutzt, um die Variablen in sogenannte Aquivalenzklassen einzuteilen, wobei M akeSet(e, i) f ur die Erstellung einer neuen Menge mit Namen i und Inhalt e zust andig ist, F ind(x) den Namen einer Menge zur uckgibt, deren Element x ist und U nion(i, j, k ) die Mengen i und j zu einer neuen Menge mit dem Namen k vereinigt. Im n achsten Schritt des Algorithmus wird jedes Statement einmal ausgef uhrt und die Typsicherheit sichergestellt, indem alle Typvariablen, wenn n otig, u berpr uft werden. Bei dieser Uberpr ufung werden, nach bestimmten Bedingungen, die Variablen, die dem gleichen Typen entsprechen, in einem Knoten des aus dem Algorithmus resultierenden Points-to Graphens zusammengefasst. Das folgende Beispiel aus [67] zeigt die Typdenition und einen Points-To Graphen eines gegeben Programms:

52

3.3 Points-to-Analyse
Listing 3.7: Beispiel fu r die Umsetzung des Steensgard Algorithmus
1 2 3 4 5 6 7 8

a = &x; b = &y; if p then y = &z; else y = &x; fi c = &y;

Aus den in [67] gegebenen Typregeln f ur die vorliegende Sprache ergeben sich folgende Typen f ur die Variablen:

a: 1 = ref (4 ) b: 2 = ref (5 ) c: 3 = ref (5 ) x: 4 = ref ( ) y: 5 = ref (4 ) z: 4 p: 6 = ref ( )

Abbildung 3.4: Typdenition f ur o.g. Programmcode und zugeh origer Graph

Aus den Typdenitionen in Abb. 3.4 ergibt sich der Points-To Graph. Die Variaben x und z werden auf Grund ihrer Typgleichheit in einem Knoten gruppiert. Das Ergebnis der Berechnung liegt zwar fast in linearer Zeit vor, aber der Graph ist unpr azise, da in diesem Algorithmus aus einer Zuweisung auch der Umkehrschluss gezogen wird, also eine bidirektionale Beziehung erstellt wird. Laut der Darstellung des Graphens in 1.1 kann a auch auf z zeigen, was anhand des vorliegenden Programmcodes so nicht m oglich ist. Dieser Algorithmus sollte daher als eine eher konservative N aherung der L osung des Points-to Problems angesehen werden.

Andersen Points-to-Analyse
Ein weiterer Algorithmus, der Andersen Points-to-Algorithmus zur Berechnung einer N aherung des Points-To Problems, urspr unglich entwickelt f ur die Sprache C,

53

3.3 Points-to-Analyse wird in [3] gezeigt. Bei diesem Algorithmus handelt es sich um ein kubisches Verfahren mit einer l angeren Berechnungsdauer, jedoch einem pr aziseren Ergebnis als dem Steensgard Verfahren. Die Gr unde daf ur sind u.a. die Ber ucksichtigung der Flussrichtung sowie die Betrachtung der einzelnen Zeigerziele ohne eine Vereinigung. Bei dieser Analyse handelt es sich um ein iteratives Verfahren auf der Basis einer Fixpunktiteration. Um den Points-to Graphen aufzubauen, wird jedes Statement des Programmcodes ausgewertet. Bei einer vorliegenden Points-to Beziehung w ahrend der Analyse wird der Graph aktualisiert. [26] stellt, wie in Abb. 3.5 bis Abb. 3.8, symbolisch sechs verschiedene Statements f ur die auftretenden F alle im Programmcode und deren Regeln zur Darstellung im Graphen wie folgt dar: Statement Statement Statement Statement Statement Statement 1: 2: 3: 4: 5: 6: p p p *p *p *p = = = = = = &a; q; *r; &a; q; *r;

Abbildung 3.5: In die Andersen Points-to-Analyse einbezogene Statements und Points-to Graph nach Andersen f ur Statement 1

Abb. 3.5 zeigt f ur Statement 1 eine Zuweisung von a nach p dargestellt in einem Points-to Graphen. Die Darstellung bedeutet, dass p m oglicherweise auf a zeigt. Bei einer Zuweisung der Form von Statement 2 werden alle Ziele, auf die q zeigt, auch Ziele f ur p. Die linke Abbildung in 3.6 verdeutlicht einen Graphen, bei dem p vorher auf a gezeigt hat, nun aber auch auf b zeigt, da alle Ziele von q mit ber ucksichtigt werden m ussen. Das gilt auch f ur Pointerziele, die sich erst w ahrend der weiteren Analyse als Ziel von q herausstellen (hier und im Folgenden dargestellt durch gestrichelte Kanten, z.B. zu c ), daher wird deutlich, dass ein iterativer Algorithmus hier sinnvoll ist. F ur das Statement 3 wird angenommen, dass eine Menge an Pointerzielen P existiert, auf die r zeigt. Des weiteren wird angenommen, dass die Ziele aus P auch auf weitere Ziele zeigen, die durch eine Menge P Z beschrieben wird. Um p = *r durch einen Points-to Graphen darzustellen, werden Kanten von p zu allen Zielen aus P Z gezogen. Die Zuweisung *p = &a, Statement 4, wird dargestellt, indem Kanten von a zu allen Pointerzielen von p gezogen werden. F ur Statement 5 m ussen alle Pointerziele von p mit allen Pointerzielen von q verkn upft werden. Wenn w ahrend der Analyse neue Ziele auftreten, m ussen auch deren Verkn upfungen aktualisiert werden. Bei Statement 6 wird eine ahnliche

54

3.3 Points-to-Analyse

Abbildung 3.6: Points-to Graph nach Andersen f ur Statement 2 und 3

Abbildung 3.7: Points-to Graph nach Andersen f ur Statement 4 und 5

Abbildung 3.8: Points-to Graph nach Andersen f ur Statement 6

55

3.3 Points-to-Analyse Regel angewendet wie bei Statement 3. Es wird angenommen, es existiert eine Menge P , auf die r zeigt sowie eine Menge P Z , auf die die Elemente aus P zeigen.Die Kanten werden hier nicht direkt von p zu den Elementen aus P Z gezogen, sondern von den Knoten, auf die p zeigt, zu den Zielen in P Z . Nach der Betrachtung beider Algorithmen liegt die Schlussfolgerung nahe, dass dieser Algorithmus pr aziser arbeitet als der Steensgard Algorithmus, jedoch auch langsamer. Bei groen Programmen wird dieser Algorithmus viel Zeit f ur die Berechnung der Points-to Mengen ben otigen, daher sollte im Vergleich der beiden Algorithmen abgewogen werden, ob eine schnelle oder pr azise Analyse im Vordergrund steht.

3.3.5 Points-to-Analyse mit Soot


In diesem Abschnitt wird die Points-to-Analyse mit Soot vorgestellt, indem die Pointer-Analyse-Framework Paddle und SPARK erl autert werden. Um mit Paddle zu arbeiten werden viele Installationsschritte ben otigt (s. [25] Abschnitt 8.2) und Teile der daf ur n otigen Programme sind nicht mehr auf einem aktuellen Stand. Da die Installation von Paddle als aufw andig zu bewerten ist und die Analyse sp ater auch innerhalb einer App stattnden soll, wurde im Rahmen dieser Arbeit nur mit SPARK gearbeitet. Der Algorithmus hinter der Points-toAnalyse mit Paddle wird trotzdem vorgestellt, da er f ur weitere Arbeiten nicht g anzlich ausser acht gelassen werden sollte.

Paddle
Bei Paddle handelt es sich um ein Werkzeug zur Pointer Analyse in Soot. Diese Analyse ist kontext- und usssensitiv und basiert auf der Verwendung von Teilmengen, vergleichbar mit dem Andersen Algorithmus, und bin aren Entscheidungsdiagrammen (engl. Binary Decision Diagrams, im Folgenden BDD), die im weiteren Verlauf erl autert werden. Eine aquivalenzklassenbasierte Analyse ist jedoch durch die Nutzung der unterschiedlichen Optionen in Paddle ebenso m oglich. Paddle wurde in der Sprache Jedd, einer Erg anzung zu Java, entwickelt. [25] Um die Flusssensitivit at zu garantieren, nutzt Paddle einen Hybrid-Ansatz. Zuerst wird das Programm in eine Zwischenrepr asentation transformiert, bei der die

56

3.3 Points-to-Analyse Kontrollussabh angigkeiten ausgewertet werden, dann folgt eine kontextinsensitive Analyse und die Vereinigung der Ergebnisse. Als Input kann Paddle Jimple oder Shimple Code verarbeiten. [41] Generell sind kontextsensitive Analysen teuer, d.h. sie ben otigen deutlich mehr Rechenaufwand als kontextinsensitive Analysen. Paddle auf Basis der BDDs ist eine, im Vergleich zu SPARK, deutlich speicheroptimiertere Analyse und bei gr oeren Problemen verh alt sich Paddle ann ahernd so schnell wie SPARK. Mit Binary Decision Diagramms k onnen ezient Mengen von bin aren Strings dargestellt werden. Ein BDD ist ein azyklischer, gerichteter Graph, der in der tiefsten Ebene Bl atter mit den Werten 0 und 1 besitzt. Dar uber stehen Bl atter, die den Weg von der Wurzel bis zu den konstanten Werten 0 und 1 bilden und somit die Strings beschreiben.

Abbildung 3.9: Beispiel f ur ein Binary Decision Diagramm

In diesem Graphen exisitieren zwei verschiedene Typen von Kanten, die niedrige Kante und die h ohere Kante, jeweils dargestellt durch eine gestrichelte (niedrig) oder eine durchgehende Linie (hoch). Anhand des Graphens kann ausgewertet werden, ob eine bestimmte Zeichenkette in der Menge der Zeichenketten vorhanden ist. [10] Das Beispiel aus Abb. 4.7 zeigt ein BDD f ur eine Zeichenkette mit der L ange drei: Die Werte {000, 001, 010, 100} sind nicht in der Menge enthalten, denn ihr Pfad endet in dem Blatt mit dem Wert 0, die Werte {011, 101, 110, 111} sind in der Menge enthalten, sie terminieren bei 1. Auf dieser Basis wurde der Algorithmus f ur Paddle entworfen. Der Algorithmus

57

3.3 Points-to-Analyse ist iterativ und terminiert, wenn ein Fixpunkt erreicht wird. Paddle ber ucksichtigt vier Typen von Pointer Statements, allocation ( a : l := newC ), simple assignment ( l2 := l1 ), eld store ( q.f := l ) und eld load ( l := p.f ). Nach den Regeln aus [10] f ur Points-to-Mengen und die assignment-edges wird ein Graph gebildet und die Statements mit Hilfe von [43] abgearbeitet.

SPARK
Die Implementierung der Call Graph Generierung mit SPARK basiert auf der Verwendung von Teilmengen, wie auch der Algorithmus von Andersen und bietet ebenso auch eine auf Aquivalenzklassen basierende Analyse, wie auch der Steensgard Algorithmus zur Pointer Analyse. Um die gew unschte Analysemethode zu nutzen, arbeitet SPARK mit verschiedenen Optionen, aus deren Kombinationen sich die gew unschte Analyse zusammensetzen l asst [25]. SPARK ist eine kontext-insensitive Analyse, d.h. die Points-to-Mengen werden w ahrend der Analyse auf Grund einer einmaligen Auswertung einer Methode gebildet und sind daher nicht exibel anwendbar. Die Points-to Menge einer Variable h angt damit nicht von der Position der Variable im Programm ab. SPARK besteht aus mehreren Komponenten, die aus dem vorliegenden JimpleCode die Analyse aufbauen. Nach [40] wird dazu zun achst mit Hilfe des Pointer Assignment Graph Builder der Pointer Assignment Graph f ur das Programm erstellt. Dadurch werden die n otigen Speicherbereiche des Programms gesammelt und in einem Graphen dargestellt, wobei zwischen vier verschiedenen Knotentypen unterschieden wird: Den Allocation Nodes, Variable Nodes, Field Reference Nodes sowie den Concrete Field Nodes. Die Allocation Nodes repr asentieren Objekte, auf die w ahrend der Laufzeit gezeigt werden kann und Variable Nodes die Variablen, also die Speicherbereiche, die m oglicherweise Pointer auf Objekte beinhalten, abh angig vom Typ der Variable. Field Reference Nodes beinhalten einen Variable Node als Basis und ein abstraktes Feld, in dem alle Objekte gesammelt werden, die auf die Basis zeigen k onnen. Im weiteren Verlauf der Analyse werden dem Graphen die sogenannten Concrete Field Nodes hinzugef ugt. Dabei handelt es sich um Knoten, die Points-to Sets f ur die Felder von Objekten, auf die konkret zugegrien wird, beinhalten. Die Kanten des Graphens werden durch vier verschiedene Kategorien deniert: Dazu z ahlen die Allocation Edges, also Kanten von Allocation Nodes zu Varia-

58

3.3 Points-to-Analyse ble Nodes, die eine Zuweisung eines Pointers auf ein Objekt mit der dazugeh origen Variable verbinden, ebenso die Assignment Edges, Kanten zwischen verschiedenen Variable Nodes, die Zuweisungen von Variablen wie r = s darstellen. Desweiteren geh oren Store Edges, also Kanten von Variable Nodes zu Field Nodes, die Anweisungen wie r.a = s gurieren, sowie Load Edges, Kanten von Field Reference Nodes zu Variable Nodes, die Anweisungen wie r = s.a darstellen, dazu. Der Pointer Assignment Graph wird dann letztlich aus dem Jimple-Code, einem Call Graphen und Simulationen der Funktionen von nativen Java-Funktionen erstellt. Eine Vereinfachung des fertigen Graphens kann dann u.a. durch das Zusammenf uhren mehrerer Variablen-Knoten mit gleichen Points-to Mengen durch u.a. Nutzung des Union-Find Algorithmus [56] geschehen. Anhand des aktualisierten Graphs k onnen nun mit Hilfe verschiedener Algorithmen die Points-to Mengen berechnet werden. SPARK bietet von Hause aus f unf Algorithmen, den Iterative Propagation Algorithm, den Worklist Propagation Algorithm, den Incremental Worklist Algorithm, den Alias Edge Propagation Algorithm sowie den Incremental Alias Edge Propagation Algorithm. In diesem Zusammenhang wird jedoch nur der Worklist Propagation Algorithm vorgestellt, da er auf Grund seiner guten Performanz in dieser Arbeit verwendet wird. Der Worklist Propagation Algorithm arbeitet auf der Basis von Listen, in denen abgearbeitete Knoten gespeichert werden. Jedes Mal wenn einer Points-to Menge einer Variable Node Points-to-Beziehungen hinzugef ugt werden, wird der Variable Node einer Worklist angef ugt. Im weiteren Verlauf werden die Listen dann genutzt und aktualisiert, um die Variable Nodes abzuarbeiten. Abb. 4.8 und 4.9 zeigen den Worklist Propagation Algorithmus. Die blau gekennzeichneten Nodes stehen f ur Allocation Nodes, gr une f ur Variable Nodes, rote f ur Field Reference Nodes und gelbe f ur Concrete Field Nodes. Der Algorithmus besteht aus zwei ineinander verschachtelten Schleifen, die die Worklist abarbeiten. Zun achst werden jedoch alle Allocation Nodes bearbeitet, indem die Zuweisungen den Points-to Mengen hinzugef ugt werden. In diesem Schritt wird die Worklist erstellt, da initial alle Ver anderungen der Points-to Beziehungen in die Liste eingetragen werden. Daraufhin wird, so lange noch Eintr age in der Worklist vorhanden sind, innerhalb von zwei Schleifen erst der oberste Eintrag der Liste entfernt, dann die Points-to Mengen der Assignment-Statements vereint und die Liste auf Grund von Ver anderungen der Points-to Mengen ggf. aktualisiert. Dann werden f ur jede Store Edge p q.f alle Allocation Nodes a, die Teil einer Points-to Menge von q sind, durchlaufen und die Points-to Menge f ur a.f mit der Points-to Menge f ur p vereinigt. Diese Prozedur wird zweimal hinterein-

59

3.3 Points-to-Analyse

Abbildung 3.10: SPARK Worklist Propagation Algorithmus, Quelle: [40]

ander durchlaufen, zuerst f ur die Edge p q.f zugeh orig zum aktuellen Node p und dann f ur die Edge q p.f mit p (bzw. p.f) als Ziel, da p im n achsten Durchlauf von der Worklist gel oscht wird. Zuletzt werden in dieser inneren Schleife die Load Edges, die in Zusammenhang mit p stehen, verarbeitet. Wenn sich dabei die Points-to menge f ur q ver andert hat, wird q der Worklist hinzugef ugt. Die innere Schleife des Algorithmus betrachtet nur die Nodes, die sich in der Worklist benden. Wenn sich nach der Verarbeitung von p jedoch die Points-to Menge f ur p andert, kann p nicht erneut ber ucksichtigt werden. Daher betrachtet die auere Schleife alle Load und Store Edges und aktualisiert somit die Worklist f ur alle Load Edges, um das Ergebnis der Points-to Mengen zu pr azisieren. Die Ergebnisse der Berechnungen werden durch die Implementierungen der abstrakten Klasse PointsToSet in Soot gespeichert. Um eine interprozedurale Analyse durchzuf uhren, wird ein Callgraph f ur das Programm generiert. Dabei lassen sich mehrere Arten von Callgraphen in Soot generieren (siehe 3.2), einen on-the-y Callgraph, einen nach der Class Hierarchy Analysis, einen Callgraph anhand der Rapid Type Analysis sowie einen nach der Variable Type Analysis [42]. SPARK l asst sich durch die Kombination unterschiedlicher Optionen vielf altig f ur verschiedene Analysen einsetzen, worauf in Kapitel 4.2 eingegangen wird.

60

3.3 Points-to-Analyse

Abbildung 3.11: SPARK Worklist Propagation Algorithmus, Quelle: [40]

61

4 Realisierung

4 Realisierung
Das Kapitel Realisierung stellt zun achst die reine Informationsussanalyse und dann die Erweiterung durch eine Points-to-Analyse dar, wie sie im Rahmen der Arbeit durchgef uhrt wurden. Einleitend wird f ur beide Analyseverfahren die Herleitung der Mengendenitionen gezeigt und anschlieend die Umsetzung des Prototypens mit Hilfe des Frameworks Soot.

4.1 Informationsussanalyse
Dieser Abschnitt zeigt erst die formale Denition der Informationsussanalyse in Form der Mengendenitionen f ur die Mengen gen(B ) , gen(M ) , kill(B ) , kill(M ), copy (B ), copy (M ), leak (B ) und leak (M ) und die daraus resultierende Berechnung der Mengen critical(B ) und critical(M ). Auf dieser Festlegung basierend, k onnen dann die Regeln zur Berechnung der ein und ausgehenden Mengen eines Blockes des Kontrollussgraphens bzw. einer Methode genin (B ) , genout (B ) , killin (B ) , killout (B ), copyin (B ), copyout (B ), leakin (B ), leakout (B ), criticalin (B ) und criticalout (B ) sowie f ur Mengen der Methoden M deniert werden. Im zweiten Teil des Abschnitts wird der Aufbau eines Programms, die Implementierung einer Flussanalyse sowie die konkrete Umsetzung der Denitionen in Soot dargestellt.

62

4.1 Informationsussanalyse

4.1.1 Denition
Als Basis f ur die Umsetzung dieser Arbeit wurden folgende Mengen zun achst formal deniert. B steht dabei f ur einen Codeblock eines Programms, der analysiert wird und M f ur eine Methode. Im Folgenden werden zun achst die Mengen f ur die Flussanalyse aufgezeigt, die Denitionen f ur die erg anzende Points-to-Analyse wird in Abschnitt 4.2 gezeigt. F ur alle Bl ocke B und die Variablen v innerhalb eines Kontrollussgraphens G gilt: gen(B ) := {v | B speichert in v m o glicherweise sensitive Inf ormationen direkt von der Quelle in v } {v | B speichert in v garantiert keine sensitiven Inf ormationen} {x v | B speichert mo glicherweise sensitive Inf ormationen aus x nach v } {v | B gibt m o glicherweise v aus}

kill(B )

:=

copy (B )

:=

leak (B )

:=

F ur jeden Codeblock des Graphens wird gepr uft, ob der Inhalt einer der obengenannten Mengen zugeordnet werden kann und wenn ja, welcher. Dabei repr asentiert die Menge gen(B ) die neu generierten Variablen innerhalb eines Blockes, deren Inhalt sensitiver Natur ist. Die Menge kill(B ) beinhaltet die Variablen, denen in diesem Block denitiv keine sensitiven Informationen zugewiesen wurden. Die Menge ist besonders wichtig, um eine zu konservative Analyse zu vermeiden, da nicht jede Variable, die ein Geheimnis enth alt, auch bis zum Erreichen des Leaks das Geheimnis gespeichert h alt. Wenn diese Variable mit nicht sensitiven Daten u urde im Falle eines Leaks nichts Geheimes berschrieben wird, w das Programm verlassen. Die Menge copy (B ) ist eine Abbildung von x nach v , d. h., falls ein Codeblock eine Zuweisung der Form v = x; enth alt, werden die sensitiven Daten aus x nach vu alt demnach auch sensitive Informationen. bertragen und v enth Falls innerhalb eines Blockes ein Leak, also ein Weg, Informationen auszugeben, implementiert wird, k onnte die Variable v ausgegeben werden. Die Variablen, die ausgegeben werden k onnten, werden in der Menge leak (B ) gespeichert, um zuletzt zu pr ufen, ob diese Variablen geheime Daten enthalten.

63

4.1 Informationsussanalyse F ur die interprozedurale Analyse k onnen aus den oben genannten Mengendenitionen, Denitionen f ur die Berechnung der Mengen f ur ganze Methoden gebildet werden. F ur alle Methoden eines Programmes P und alle Variablen v gilt: gen(M ) := {v | ein P f ad durch M, auf dem m o glicherweise sensitive Inf ormationen direkt von der Quelle in v gespeichert werden.} {v | P f ade durch M : v wird garantiert nicht mit sensitiven Inf ormationen u berschrieben} {x v | ein P f ad durch M, der mo glicherweise Inf ormationen aus x nach v speichert} {v | ein P f ad durch M, der m o glicherweise v ausgibt}

kill(M )

:=

copy (M )

:=

leak (M )

:=

Diese Mengen beschreiben die Analyse jeder Methode eines Programms. Je nach Ergebnis kann bspw. im Falle der Menge gen(M ) davon ausgegangen werden, dass ein Pfad innerhalb der Methode exisitert, auf dessen Verlauf innerhalb der Methode m oglicherweise sensitive Daten in die Variable v geschrieben werden. F ur die Menge kill(M ) bedeutet das, dass auf keinem der Pfade durch das Programm die Variable v mit sensitiven Informationen gef ullt wird. Die Menge copy (M ) beinhaltet die Abbildung von x nach v , falls innerhalb der Methode ein Pfad exisitiert, der m oglicherweise den Inhalt von x nach v speichert. Die Menge leak (M ) schliet die Variablen ein, die auf einem Pfad innerhalb der Methode ausgegeben werden k onnten. Um eine Informationsussanalyse durchzuf uhren, m ussen diese Mengen erweitert werden, da es f ur diesen Analysefall notwendig ist zu wissen, welche Werte in einen Codeblock oder eine Methode einieen und welche am Ende des Blocks bzw. der Methode weitergegeben werden. Daf ur wurden die folgenden Mengen f ur die Code-Bl ocke im Rahmen einer Vorw artsussanalyse deniert:

64

4.1 Informationsussanalyse genin (B ) genout (B ) killin (B ) killout (B ) copyin (B ) copyout (B ) := := := := := := genout (p)

ppred(B )

((genin (B ) kill(B )) gen(B )


ppred(B )

killout (p)

(kill(B )in gen(B ) kill(B )


ppred(B )

copyout (p)

{x v |((x y ) copyin (B ) y / kill(B )) ((x y ) copy (B )) (x z ) copyin (B ) : (z y ) copy (B )}

Diese Mengen beziehen sich wieder auf die einzelnen Bl ocke innerhalb des Kontrollussgraphens. Zu jedem Block lassen sich damit die Informationen analysieren, die in einen Block einieen und nach der Abarbeitung dessen im Programm wieder herausieen. Die Mengendenitionen genin (B ) und genout (B ) beschreiben, wie der Fluss f ur die neu generierten Variablen aussieht. Dabei werden in genin (B ) alle Variablen zusammengefasst, die nach der Analyse des Vorg angerblocks in der genout (B ) Menge stehen. Diese Informationen werden abh angig von der Denition von genout (B ) nach der Analyse des Statements in genout (B ) geschrieben oder ggf. nicht. Die Menge genout (B ) wird berechnet, indem von der Menge genin (B ) alle Elemente der Menge kill(B ) abgezogen werden. Das bedeutet, dass, falls die Variable v aus genin (B ) mit nicht-sensitiven Informationen u berschrieben wurde, sie nicht in der Menge genout (B ) stehen kann. Das Ergebnis wird dann mit den evtl. neu generierten Variablen eines Blocks vereinigt. Die Menge killin (B ) beinhaltet ebenfalls die Variablen aus der Menge genout (B ) des vorherigen Blockes, die Menge killout (B ) die Variablen, die in killin (B ) stehen, abz uglich derer, die ggf. in diesem Block neu generiert wurden, vereinigt mit den Variablen, die ggf. in diesem Block mit nicht sensitiven Daten u berschrieben wurden. F ur die Menge copyin (B ) gilt das selbe Prinzip, die Inhalte der Menge copyout (B ) des vorher ausgewerteten Blocks werden in copyin (B ) geschrieben. Die Denition der Menge copyout (B ) ist etwas umfangreicher: Die Abbildung x v wird hier gespeichert, wenn ein x nach y in copyin (B ) enthalten ist und y gleichzeitig nicht der Menge kill(B ) angeh ort, x nach y bereits copy (B ) angeh oren

65

4.1 Informationsussanalyse oder es eine Abbildung x nach z aus copyin (B ) gibt, deren Umkehrung z nach x bereits in copy (B ) stehen. Die Datenussgleichung f ur die Mengen criticalin (B ) und criticalout (B ), also der Kern der Informationsussanalyse, k onnen aus den vorher genannten Mengendenitionen hergeleitet werden. Hier werden sie deniert als: criticalin (B ) := criticalout (B ):=
ppred(B )

criticalout (p) gen(B )

(criticalin (B ) kill(B )) copy (B )(criticalin (B ))

Da es sich bei der Erstellung der Leak-Mengen um eine R uckw artsussanalyse handelt, werden sie wie folgt deniert: leakin (B ) leakout (B ) := := (leakout (B ) kill(B )) leak (B )
ssucc(B )

leakin (s)

Durch die R uckw artsussanalyse verhalten sich die Mengendenitionen hier quasi umgekehrt zu den obengenannten. Die Menge leakout (B ) ist w ahrend der Analyse gleich der Menge leakin (B ) des Nachfolgerblocks innerhalb des Kontrollussgraphens und die Menge leakin (B ) wird deniert als die Menge leakout (B ) abzgl. der Variablen, die denitiv keine sensitiven Informationen enthalten vereinigt mit den in diesem Block ausgehenden Variablen. Aus diesen Denitionen ergeben sich f ur die interprozedurale Analyse folgende Ergebnisse f ur die Methoden: genin (M ) killin (M ) copyin (M ) := := := genin (begin) killin (begin) copyin (begin) criticalin (begin) leakout (stmt)

criticalin (M ):= leakin (M ) :=

66

4.1 Informationsussanalyse genout (M ) killout (M ) copyout (M ) := := := genout (end) killout (end) copyout (end) criticalout (end) leakin (stmt)

criticalout (M ) := leakout (M ) :=

F ur die Mengen genin (M ), killin (M ), copyin (M ) und criticalin (M )gelten die jeweiligen in -Mengen des ersten Blocks innerhalb des Flussgraphens f ur eine Methode, f ur die Mengen genout (M ), killout (M ), copyout (M ) und criticalin (M )die Ergebnisse der jeweiligen out -Mengen des letzten Blocks. Im Falle von leakin (M ) und leakout (M ) wird der jeweilige Wert eines jeden Statements analysiert. Der letzte Schritt innerhalb der Analyse ist die Uberpr ufung, ob die Variablen aus criticalin (B ) eines Statements durch ein Leak ausgegeben werden, bzw. ob die gesamte Methode sensitive Daten ausgibt.

4.1.2 Programmaufbau mit Soot


Zum tieferen Verst andnis der Vorgehensweise werden zun achst die n otigen Komponenten des Soot-Frameworks [72] erl autert. Die Hauptkomponente in Soot stellt die Klasse Scene dar. Man kann sich die Scene wie eine Art Wurzel des Programms vorstellen, anhand dessen man die Analyse bzw. das Programm steuert. Mit Hilfe der Scene kann beispielsweise der Call Graph f ur ein Programm durch den Aufruf von Scene.v().getCallGraph() zur Analyse genutzt werden, oder auf alle zu analysierenden Klassen eines Programms via Scene.v().getApplicationClasses() zugegrien werden. Die zu analysierenden Klassen stammen vom Typ SootClass. Dabei handelt es sich um eine Repr asentation einer Klasse in Soot, wie die z.B. durch den Aufruf getApplicationClasses() zur uckgelieferten Elemente der Analyse. Die Methoden innerhalb einer SootClass werden als SootMethod und die Felder als SootField bezeichnet. Der Rumpf einer Methode wird in Soot mit der Klasse Body repr asentiert. Ein Methodenrumpf kann abh angig von der genutzten Bytecodedarstellung entweder

67

4.1 Informationsussanalyse ein JimpleBody, BafBody, ShimpleBody oder ein GrimpBody sein. Innerhalb der Methodenr umpfe werden die einzelnen Codeanweisungen, also die Statements innerhalb einer Methode, durch die Klasse Unit dargestellt. Anhand einer Unit lassen sich die Values innerhalb eines Statemens feststellen. Dabei handelt es sich um z.B. lokale Variablen, aber auch um Methodenaufrufe. Auf ein Value innerhalb einer Unit kann u ber die sogenannten VauleBoxen zugegrien werden, wobei unterschieden werden kann, ob es sich um genutzte oder neu denierte Values handelt. Detailiertere Informationen zur Struktur von Soot bietet [25]. In dieser Arbeit wird mit der Bytecodedarstellung Jimple gearbeitet, welche durch eine Instanz eines SceneTransformer, s. Listing 4.1, initialisiert wird. Dabei wird die Art der Analyse und damit auch die Art der Scene deniert. Soot bietet mehrere Pakete f ur bestimmte Analysemethoden. Diese Pakete beinhalten weitere so genannte Phasen, die mit Hilfe der Phase-Options angepasst werden k onnen.

Listing 4.1: Initialisierung des whole Jimple Transformation Packs


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

public class InitializeJimple { public static void main(String[] args) { //Initialisierung des whole-jimple-transformation-pack (wjtp) durch //Verwendung eines SceneTransformer PackManager.v().getPack("wjtp").add( new Transform("wjtp.myTransform", new SceneTransformer() { protected void internalTransform(String phaseName, Map options) { //hier wird der gewuenschte Programmcode eingefuegt ... } })); //Startpunkt fuer Soot soot.Main.main(args); } }

Standardm aig wendet Soot das Jimple Body Creation (jb)-Paket, dessen Funktionen im Folgenden gezeigt werden, auf jeden Methodenbody an, in dem entweder mit Hilfe von Co [37] .class-Dateien, oder mit Hilfe des in Soot vorhandenen Jimple-Parser .jimple-Dateien eingelesen werden k onnen. Dazu geh oren u.a. folgende Elemente, die schon standardm aig auf true gesetzt

68

4.1 Informationsussanalyse sind: Local Splitter (jb.ls) Trennt, wenn n otig, lokale Variablen in zwei eindeutige Variablen. Jimple Local Aggregator (jb.a) Entfernung ungenutzter Kopien von Variablen. Unused Local Eliminator (jb.ule) Entfernung ungenutzter Variablen. Type Assigner (jb.tr) Vergibt Typen an lokale Variablen w ahrend eines Methodendurchlaufs. Unsplit-originals Local Packer (jb.ulp) Das Gegenteil des Local Splitters, fasst Variablen innerhalb einer Methode zur Optimierung zusammen. Local Name Standardizer (jb.lns) Weist lokalen Variablen generische Namen zur Analyse zu. Copy Propagator (jb.cp) F uhrt eine Kopier-Propagation Optimierung durch. Dead Assignment Eliminator (jb.dae) Entfernt ungenutzte Zuweisungen zu lokalen Variablen. Post-copy propagation Unused Local Eliminator (jb.cp-ule) Entfernt alle Variablen, die nach der Kopier-Propagation ungenutzt sind. Im n achsten Schritt kann zur gesamten Analyse von Programmen bei unterschiedlicher Problemstellung zwischen den vier groen Whole-Program Paketen cg, wjtp, wjop und wjap gew ahlt werden, wobei cg f ur das Call Graph Paket, wjtp f ur das Whole Jimple Transformation Paket, wjop f ur das Whole Jimple Optimization Paket und wjap f ur Whole Jimple Annotation Paket steht. Ein weiteres Paket, das Whole Jimple Pre-processing Pack (wjpp), welches das Einf ugen von Pr aprozessoren, die vor der Konstruktion des Aufrufgraphens gestartet werden erlaubt, geh ort auch zu den interprozeduralen Analysemethoden, ist jedoch bisher noch nicht mit vielen Funktionen gef ullt.

69

4.1 Informationsussanalyse

Abbildung 4.1: Kombinationen und Zusammenh ange der Pakete und Phase-Options in Soot. Quelle: [15]

Die Nutzung des cg-Paketes berechnet abh angig von den verschiedenen Optionen einen Aufrufgraphen f ur das gesamte Programm, auf den innerhalb der Szene zugegrien werden kann. Das Paket beinhaltet folgende F alle f ur die Konstruktion eines Aufrufgraphens:

Class Hierarchy Analysis (cg.cha) Nutzung der Class Hierarchy Analyse Spark (cg.spark) Nutzung von SPARK Paddle (cg.paddle) Nutzung von Paddle Die Pakete wjtp, wjop und wjap stehen auf Grund des vorangestellten w f ur die interprozedurale Analyse des Programms. Bei der Angabe von wjtp wird jede zu analysierende Methode durch das Jimple Transformation Paket bearbeitet, bei wjop wird das Jimple Optimization Paket auf jeden in der Analyse vorhandenen Body vom Typ JimpleBody angewandt und bei wjap handelt es sich um das Jimple

70

4.1 Informationsussanalyse Annotation Paket, mit dem die JimpleBodies auf Grund der interprozeduralen Analyse annotiert werden. Zudem lassen sich zu jedem Paket (s. Abb. 4.1) noch die Pakete Jimple Transformation Pack (jtp), Jimple Optimization Pack (jop), Jimple Annotation Pack (jap), Tag Aggregator (tag) und das Baf Body Creation (bb) Paket einbinden. Die in den Paketen enthaltenen Elemente werden hier ausgelassen, sind aber unter [14] einsehbar. Die Analysekomponenten der einzelnen Methoden werden letztendlich erst durch die Ubergabe bestimmter Werte u ber die Kommandozeile oder innerhalb des Programms genutzt. Ohne die Initialisierung einer interprozeduralen Analyse wird Soot die passenden Pakete nicht nutzen k onnen. Eine Kombination aus verschiedenen Optionen l asst sich wie in Listing 4.2 bestimmen. Dort werden in Zeile 2-4 die verschiedenen Optionen, die f ur den Durchlauf mit Soot genutzt werden, gesetzt. Die Option in Zeile 3 bspw. versetzt Soot in einen Modus, mit dem das gesamte Programm analysiert wird, damit das dementsprechende Analysepaket f ur das gesamte Programm u berhaupt genutzt werden kann.
Listing 4.2: Ubergabe der Optionen an Soot
1 2 3 4 5

static { soot.options.Options.v().set_keep_line_number(true); soot.options.Options.v().set_whole_program(true); soot.options.Options.v().setPhaseOption("cg", "verbose:false"); }

In dieser Arbeit wird das Jimple Body Creation Paket (jb), das Whole-Jimple Transformation Pack (wjtp) sowie das Jimple Transformation Pack (jtp) genutzt. Um die Analyse des kompletten Programms zu garantieren, muss set whole program auf true gesetzt werden, sonst wird nur das jtp genutzt. Die Einbindung der zu analysierenden Klassen und die allgemeine Initialisierung wird in Listing 4.3 gezeigt. In Zeile 2 wird die Hauptklasse des zu analysierenden Programms geladen, in den Zeilen 5 und 6 werden allgemeine Initialisierungen von Soot vorgenommen. Zeile 9 setzt mit der Denition einer Soot-Methode den Startpunkt des zu analysierenden Programms, da sie mit der main()-Methode der Hauptklasse initialisiert wird.

Listing 4.3: Laden der n otigen Klassen etc.

71

4.1 Informationsussanalyse

1 2 3 4 5 6 7 8 9

//laden der Hauptklasse des zu analysierenden Programms SootClass sClass2 = loadClass("ExampleMainClass", true); //allgemeine Initialisierung soot.Scene.v().loadNecessaryClasses(); soot.Scene.v().setEntryPoints(EntryPoints.v().all()); //Startpunkt der Analyse SootMethod sootMethod = Scene.v().getMainClass().getMethodByName("main");

Damit sind die wichtigsten Einstellungen f ur die Flussanalyse gegeben. Im folgenden Kapitel wird die intraprozedurale Analyse mit Soot erl autert.

4.1.3 Intraprozedurale Analyse


Um die Mengenoperationen aus Abschnitt 4.1.1 umzusetzen, wurde zun achst eine intraprozedurale Analyse einzelnener Methoden erstellt. Diese Analyse kann mit Hilfe des Methodennamens gestartet werden und wertet f ur jede Methode die Menge criticalin und criticalout auf Basis der festgelegten Mengendenition aus. Dabei wird f ur jeden Body ein Kontrollussgraph durch den Aufruf UnitGraph graph = new ExceptionalUnitGraph(body) erstellt. Der ExceptionalUnitGraph ber ucksichtigt nicht nur die Units (Statements) eines Bodys, er beinhaltet auch die Exceptions, die ein Statement ausl osen k onnte. Der Graph wird auf der Grundlage der durch Body body = soMe.retrieveActiveBody() denierten JimpleBodys der Methoden erstellt. Nach der Erstellung des Graphens wird f ur jede Unit des Graphens die criticalin und criticalout Menge mit Hilfe einer eigenen Klasse zur Flussanalyse berechnet. Diese eigene Implementierung erbt dann von den Klassen BackwardFlow- bzw. ForwardFlowAnalysis.

4.1.4 Flussanalyse mit Soot


Die Flussanalyse in diesem Zusammenhang wird geteilt in eine Vorw arts- und eine R uckw artsussanalyse. Dabei wird im ersten Durchlauf die Vorw artsanalyse genutzt, um zun achst die Mengen gen, kill und copy f ur jeden Block (Unit des Graphens) zu bilden. Die Mengen criticalin und criticalout werden daraus dann

72

4.1 Informationsussanalyse f ur jeden Block berechnet. Im zweiten Durchlauf der Analyse wird die Menge leak gebildet, in dem jedes Statement danach u uft wird, ob Werte ausgegeben werden. Die Menge leak berpr beinhaltet dabei die Variablennamen, die in dem konkreten Fall ausgegeben w urden.

Vorw artsanalyse Um die o.g. Mengen zu bilden, wird der durch Soot generierte Jimple-Code f ur jedes Statement analysiert. Listing 4.4 verdeutlicht die Vorgehensweise bei der Analyse des Jimple-Codes.

Listing 4.4: Erzeugen der Mengen(B).


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

//Iteriere ueber alle Statements (units) for (Unit unit : graph) { ... //handelt es sich bei der Unit um eine Zuweisung (AssignmentStmt) if (unit instanceof JAssignStmt) { //caste die Unit und speichere den rechten Wert des Assignments ab JAssignStmt assignStmt = (JAssignStmt) unit; Value valRight = assignStmt.getRightOp(); //----- Erstellen der critical Menge -----// //handelt es sich bei dem rechten Value um einen Methodenaufruf if (valRight instanceof JVirtualInvokeExpr) { //caste Value und pruefe, ob der Name des Methodenaufrufs in der Liste //der Methoden steht, die geheime Daten erzeugen JVirtualInvokeExpr virtualInvokeExpr = (JVirtualInvokeExpr) valRight; if (criticalMethodList.contains(virtualInvokeExpr.getMethod().getName())) { if (assignStmt.getLeftOpBox().getValue() instanceof Local) //caste den linken Value als Lokale und speicher //sie in der critical Menge ab { Local local = (Local) assignStmt.getLeftOpBox().getValue(); criticalSet.add(local); ... } } } ... //----- Erstellen der copy Menge -----// for(ValueBox vb : unit.getUseBoxes()) { //pruefe, ob es sich bei beiden Values (links und rechts des //Gleichheitszeichen) um Lokalen handelt if (assignStmt.getLeftOpBox().getValue()

73

4.1 Informationsussanalyse

37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62

instanceof Local && vb.getValue() instanceof Local) { //speichere beide Lokalen in copySet ab Local local1 = (Local) vb.getValue(); Local local2 = (Local) assignStmt.getLeftOpBox() .getValue(); copySet.put(local1, local2); } } ... } ... //----- Erstellen der kill Menge -----// //gehe alle ValueBoxen der Unit durch, in denen neue Variablen definiert werden //damit sind die anderen Optionen ausgeschlossen bzw. wurden diese vorher abgeprueft //und sicher, dass in den Values definitiv keine sensitiven Informationen stehen for (ValueBox vb : unit.getDefBoxes()) { //handelt es sich bei den Values um Locals if (vb.getValue() instanceof Local) { //speichere den Wert im killSet Local local = (Local) vb.getValue(); killSet.add(local); } } ... }

F ur ein Jimple-Statement der Form: r3 = specialinvoke r0.<HelloWorld:java.lang.String getId() w urde hier eine Zuweisung erkannt und gepr uft, ob der rechte Teil der Zuweisung ein Methodenaufruf ist. Der Methodenname kann je nach Aufruftyp durch invokeExpr. getMethod(). getName() abgefragt werden. invokeExpr steht hier f ur JSpecialInvokeExpr, JVirtualInvokeExpr und JStaticInvokeExpr, wobei eine Verallgemeinerung der Methodenaufrufe ebenso m oglich ist. In den Zeilen 6 bis 21 in Listing 4.4 wird erst gepr uft, ob es sich bei der zu analysierenden Unit (dem Statement) um eine Zuweisung handelt. Wenn ja, wird gepr uft, ob es sich bei dem rechten Teil der Zuweisung um einen Methodenaufruf handelt. In Zeile 14 wird bspw. getestet, ob es sich hier um eine VirtualInvoke Expression, also einen virtuellen Methodenaufruf handelt. Im n achsten Schritt wird abgefragt, ob der Name der Methode in einer Liste von Methodennamen steht, die als R uckgabewerte sensitive Daten enthalten. Wenn eine Ubereinstimmung gefunden wurde, wird die Variable, in die die sensitiven Informationen gespeichert werden, der Menge criticalSet hinzugef ugt. Die Methode getId() geh ort in diesem Beispiel zu den Methoden, die sensitive Daten zur uckgeben. Daher wird hier die lokale Variable r3 f ur dieses Statement in die critical() Menge geschrieben. Die Menge copy () wird durch Statements wie r3 = r2 erzeugt. Es werden erst

74

4.1 Informationsussanalyse mit Hilfe von unit.getUseBoxes() alle genutzten Values geholt und dann gepr uft, ob es sich bei den Variablen links und rechts vom Gleichheitszeichen um lokale Variablen handelt. Beide Variablen werden dann in einer Map gespeichert, wie in den Zeilen 36 bis 44. Das Statement r2 = "text"; stellt die Initialisierung und direkte Zuweisung einer String-Variable dar. unit.getDefBoxes()) gibt r2 zur uck, da hier eine neue Variable generiert wird. Auch wenn es sich hier um eine neu erzeugte Variable und um eine Zuweisung handelt, wird die Variable der kill() Menge zugeordnet, siehe Zeile 53 bis 60. Es handelt sich hier um eine Wertzuweisung. Dabei ist der rechte Teil davon kein Methodenaufruf. In dieser Arbeit wird davon ausgegangen, dass Variablen, die die sensitiven Daten enthalten, nur u ber Methodenaufrufe generiert werden. Soot bietet zur Flussanalyse bereits u.a. die Klassen BackwardFlowAnalysis und ForwardFlowAnalysis des Frameworks zur Flussanalyse. Die Flussanalyse arbeitet auf Basis von Objekten der Klasse Flowset. Dabei handelt es sich um eine Darstellung einer Menge, in der bestimmte Informationen f ur die verschiedenen Arten der Analysen enthalten sind. In dieser Arbeit werden keine Flowsets, sondern Objekte der eigenen Klasse SecretSet genutzt, da hier gleichzeitig eine Points-to-Analyse durchgef uhrt wird. Konkretere Informationen zur Points-to-Analyse und Erl auterungen zu der Klasse SecretSet werden in Kapitel 4.2 gegeben. Das Framework zur Flussanalyse ist in Soot so gegeben, dass in den Klassen ForwardFlowAnalysis sowie BackwardFlowAnalysis der Analysevorgang bereits vorhanden ist und die Methoden newInitialFlow(), merge(inSet1, inSet2, outSet), copy(sourceSet, destSet) sowie die Methode, die die Transferfunktion f ur criticalout (B ) darstellt, flowThrough(inSet, s outSet) implementiert, bzw. u ussen. Listing 4.5 zeigt die berschrieben werden m Implementierung der genannten Funktionen, basierend auf den bereits genannten Objekten der Klasse SecretSet.

Listing 4.5: Vorw artsussanalyse.


1 2 3 4 5 6 7 8

@Override protected void flowThrough(Object inValue, Object unit, Object outValue) { //cast des eingehenden und ausgehenden Objekts zu SecretSets //urspruenglich wuerden hier FlowSets genutzt SecretSet in = (SecretSet) inValue; SecretSet out = (SecretSet) outValue;

75

4.1 Informationsussanalyse

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

//erstelle temporaeres Set SecretSet in_before = new SecretSet(); in.copySecretSets(in_before); //bilde Differenz zwischen eingehendem Set //und der kill Menge in.differenceSets(unitToKillSet.get(unit), in); //weitere Initialisierungen HashMap<Pair, Pair> copy = unitToCopySet.get(unit); SecretSet copy_of_unit = new SecretSet(); //iteriere ueber das key set der copy Menge for (Pair pair : copy.keySet()) { //pruefe ob das urspruengliche in Set bereits die lokale Variable //aus dem keySet enthaelt (damit werden die predecessors beschrieben) if (in_before.getPairList().contains(pair)) { //fuege dem copy_of_unit Set die lokale Variable hinzu copy_of_unit.getPairList().add(copy.get(pair)); } } //vereine das in Set mit dem out Set //(da diese Funktion mehrfach durchlaufen wird, enthaelt //die Menge out ggf. schon Informationen) out.unionSecretSets(in, out); //vereine das out Set mit dem copy Set out.unionSecretSets(copy_of_unit, out); //vereine das out Set mit dem critical Set out.unionSecretSets(unitToCritSet.get(unit), out); } @Override protected void merge(Object in1, Object in2, Object out) { //vereine die eingehenden Objekte als SecretSets und speichere //sie in out SecretSet inSet1 = (SecretSet) in1, inSet2 = (SecretSet) in2; SecretSet outSet = (SecretSet) out; inSet1.unionSecretSets(inSet2, outSet); } @Override protected //kopiere SecretSet SecretSet

void copy(Object source, Object dest) { das source Objekt als SecretSet in dest sourceSet = (SecretSet) source; destSet = (SecretSet) dest;

sourceSet.copySecretSets(destSet); }

In Listing 4.5 wird die Methode flowThrough u berschrieben, indem in den Zeilen 6 und 7 die Eingangs- und Ausgangswerte der Methode zu SecretSets gecastet werden, damit diese eigene Implementierung genutzt wird. In den Zeilen 10 und 11 wird eine tempor are Menge erstellt und eine Kopie der Eingangswerte

76

4.1 Informationsussanalyse darin gespeichert. Im n achsten Schritt wird die Dierenz zwischen der Eingangsmenge und der kill-Menge gebildet und in den Zeilen 22 bis 31 die Funktion copy f ur das aktuelle Statement berechnet. Zuletzt wird in den Zeilen 36 bis 40 die out-Menge mit der Eingangsmenge, der copy-Menge sowie der Menge der neu generierten sensitiven Variablen vereinigt. Die Methode merge() in Zeile 44 bis 50 u berschreibt die originale Methode zur Vereinigung der FlowSets mit der Vereinigung von SecretSets, ebenso wird in den Zeilen 53 bis 60 mit der Methode copy() verfahren. Die Analyse wird durch den Aufruf von doAnalysis() innerhalb des Konstruktors der eigenen Analyseklasse gestartet.

R uckw artsanalyse Die R uckw artsussanalyse funktioniert quasi analog zur Vorw artsanalyse, nur in umgekehrter Reihenfolge. Daher m ussen f ur diesen Fall nur die betroenen Mengen erstellt und die Methode flowThrough(inSet, s, outSet) angepasst werden. In diesem Fall wird diese Methode umgekehrt genutzt, also als flowThrough(outSet, s, inSet) . Listing 4.6 zeigt die Erstellung der Mengen und die Implementierung der Funktion flowThrough(). Dazu wird zun achst gepr uft, ob es sich bei dem betroenen Statement um einen Methodenaufruf handelt. Wenn ja, wird getestet, ob es sich bei der Methode um ein Leak handelt, s. Zeile 11. Falls es sich um den Aufruf der Methode leak() handelt, werden die Argumente, die der Methode u bergeben werden in Zeile 24 in die Menge leak geschrieben. Die Methode flowThrough f ur die R uckw artsanalyse wird wie deniert umgesetzt, in dem zun achst von leakout (B ) die Menge kill(B ) abgezogen und die Menge leakin (B ) mit der Ausgangsmenge vereinigt wird. Ebenso wird die Menge leakin (B ) mit den neu generierten leak-Werten des Statemenst vereinigt, s. Zeilen 38 bis 42.

Listing 4.6: Ru artsussanalyse. ckw


1 2 3 4 5 6 7 8 9

//Erstellung der Menge leak //Pruefung, ob Statement ein Methodenaufruf if(unit instanceof InvokeStmt) { InvokeStmt iStmt = (InvokeStmt) unit; InvokeExpr iExpr = iStmt.getInvokeExpr(); SootMethod soMethod = iExpr.getMethod();

77

4.1 Informationsussanalyse

10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43

//Pruefe, ob Methodennamen gleich leak() if(soMethod.getName().equals("leak")) { //Speichere die Anzahl der uebergebenen Elemente der Methode in i int i = iExpr.getArgCount(); //gehe alle Argumente durch for(int j = 0; j < i; j++ ) { Value val = iExpr.getArg(j); //wenn es sich um eine lokale Variable handelt, //speichere sie in der leak-Menge if(val instanceof Local) { Local loc = (Local) val; leakSet.add(loc); } } } } //Methode flowThrough fuer Rueckwaertsflussanalyse @Override protected void flowThrough(Object outValue, Object unit, Object inValue) { SecretSet out = (SecretSet) outValue; SecretSet in = (SecretSet) inValue; //leakout(B)-kill(B), schreibe Ergebnis in out out.differenceSets(unitToKillSet.get(unit), out); //Vereingung von out und in, Ergebnis steht in in in.unionSecretSets(out, in); //Vereingung von leak(B) und in, Ergebnis steht in in in.unionSecretSets(unitToLeakSet.get(unit), in); }

Nach der Erstellung der Menge leak () kann dann f ur jeden Block bzw. f ur jede Methode gepr uft werden, ob ein Leak vorhanden ist, indem der Schnitt zwischen der Menge critical() und der Menge leak () gebildet wird. Im Falle einer nicht leeren Schnittmenge ist ein Leak vorhanden.

4.1.5 Interprozedurale Analyse


Bei der interprozeduralen Analyse werden die in Abschnitt 4.1.1 denierten Mengen f ur Bl ocke f ur die Methoden eines Programms berechnet. Dabei werden die bisherigen intraprozedural erstellten Mengen f ur critical(B ), kill(B ) und copy (B ) genutzt. Aus diesen Mengen werden f ur das erste und das letzte Statement des Kontrollussgraphens einer Methode die Mengen critical(M ), kill(M ) und copy (M ) zusammengesetzt.

78

4.1 Informationsussanalyse F ur die Menge leak gilt der entgegengesetze Fall hinsichtlich der Reihenfolge, da es sich bei diesen Mengen um eine R uckw artsanalyse handelt. Um eine interprozedurale Analyse durchzuf uhren, gibt es mehrere M oglichkeiten in Soot einen Aufrufgraphen zu generieren. Da das Framework SPARK, welches hier zur Points-to-Analyse, s. 4.2 genutzt wird, die Erstellung eines Graphens mit sich bringt, wird in diesem Zusammenhang darauf zur uckgegrien. Da es verschiedene M oglichkeiten gibt, den Graphen zu erstellen, k onnen in SPARK mehrere Optionen gesetzt werden, die in Listing 4.7 gezeigt werden. Aus dieser Kombination wird ein On-the-Fly Graph, w ahrend der Generierung der Points-to-Mengen, erstellt. In Zeile 15 werden die Optionen an SPARK weitergegeben.

Listing 4.7: Erstellung des Callgraphens mit SPARK.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

//setzen der unterschiedlichen Optionen HashMap optionen = new HashMap(); //Nutzung der Variable Type Analysis (true / false) optionen.put("vta","false"); //Nutzung der Rapid Type Analysis (true / false) optionen.put("rta","false"); //Nutzung des Graphens, der waehrend der Analyse generiert wird (true / false) //wenn false, dann wird der Graph anhand der Class Hierarchy Analyse erstellt optionen.put("on-fly-cg","true"); //Uebergabe der Optionen an SPARK SparkTransformer.v().transform("", optionen); ... //Zugriff auf einen Callgraph CallGraph cg = Scene.v().getCallGraph()

Auf den Graphen kann dann im weiteren Verlauf wie in Zeile 20 zugegrien werden. Der Aufrufgraph enth alt, wenn man keine Anderungen bzw. Filterungen vornimmt, auch die nativen Java-Methoden. In diesem Zusammenhang werden diese nicht ber ucksichtigt und daher ein Filter ben otigt. Innerhalb von Soot l asst sich ein Filter durch die Klasse Filter implementieren, der ein Objekt der Klasse EdgesPredicate u bergeben wird. Listing 4.8 zeigt Optionen zur Filterung durch ein EdgePredicate-Objekt. Hier wurde anhand der Methoden-Namen bestimmt, welche Methoden ausser acht gelassen werden sollen, s. Zeile 12, 15 und 18.

79

4.1 Informationsussanalyse
Listing 4.8: Filtern des Aufrufgraphens.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

public class EdgesPredicateCgExample implements EdgePredicate{ @Override //Ueberschreiben von want stellt den eigentlichen Filter dar public boolean want(Edge edge) { //Definiere Target und Source Klassen String targetClass = edge.tgt().getDeclaringClass().toString(); String sourceClass = edge.src().getDeclaringClass().toString(); // Verschiedene Moeglichkeiten, die Filterung zu beeinflussen if(targetClass.startsWith("...")) return false; if(edge.tgt().toString().endsWith("...")) return false; if(sourceClass.startsWith("...")) return false; ... return true; } }

Die Denitionen aus dem EdgePredicate-Objekt werden an eine Instanz von Soot Filter u bergeben, siehe Zeile 6 in Listing 4.9. Dann wird ein Iterator u ber alle aus eine Methode ausgehenden Kanten initialisiert, mit dem ein neues Objekt der Klasse Target erzeugt wird. Dabei wird der Iterator u ber die erreichbaren Kanten in einen Iterator u andert, s. Zeile 11. ber die erreichbaren Methoden ver Um alle erreichbaren Methoden zu berechnen, wird in Zeile 13 mit diesem Iterator sowie dem Call Graph und dem Filter ein neues Objekt von Reachable Methods erstellt. Dabei handelt es sich um eine Queue von Objekten, in diesem Fall von Soot Methoden. Bei jedem erneuten Aufruf der Queue wird sie durch die in Zeile 17 und 18 in Listing 4.9 gezeigten Befehlte geupdatet. Zuletzt wird u uft, ob es sich bei der Methode um ber die Queue iteriert, gepr eine aus der zu analysierenden Klassen handelt und wenn ja, die Methode einer Menge hinzugef ugt, um sie wieder f ur die interprozedurale Analyse zu nutzen, siehe Zeilen 20 bis 29.

Listing 4.9: Erstellung einer Menge von erreichbaren Methoden.


1 2 3 4

private static void filterCallGraph(CallGraph cg, SootMethod sootMethod){ EdgesPredicateCgExample ep = new EdgesPredicateCgExample();

80

4.2 Points-to-Analyse

5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

//Uebergabe des Predicates an den Soot Filter Filter filter = new Filter(ep); //Iterator ueber die ausgehenden Kanten des Graphens aus einer gegebenen Methode Iterator edgesIt = cg.edgesOutOf(sootMethod); //Initialisiere Soot Targets Targets targetsReach = new Targets(edgesIt); //Berechne alle erreichbaren Methoden im Call Graph fuer die Targets und den Filter ReachableMethods reachableMethods = new ReachableMethods(cg, targetsReach, filter ); //Bei Veraenderungen der Abhaengigkeiten, fuehre ein //Update der erreichbaren Methoden durch reachableMethods.update(); QueueReader queueReader = reachableMethods.listener(); while (queueReader.hasNext()) { //gehe durch alle Methoden aus dem QueueReader: //wenn es sich nicht um eine Initialisierung innerhalb des Codes handelt, //speichere die Methode in der Mengeab SootMethod soMe = (SootMethod)queueReader.next(); if(!soMe.getName().contains("init") && !(soMe.getName().contains("clinit"))) { reachableMethodsSet.add(soMe); } } }

Uber die Menge der erreichbaren Methoden k onnen, von einer Methode ausgegangen, alle erreichbaren ausgelesen und analysiert werden, um dann R uckschl usse auf evtl. Leaks zu f uhren. Daf ur wird jede Methode des Sets einzeln analysiert, d.h. zuerst die Analyse u ber die Bl ocke und dann die Analyse u uhrt. Zuletzt kann ber die Methode ausgef f ur jede Methode ein Ergebnis ausgelesen werden und generell bestimmt werden, ob das vorliegende Programm eine Sicherheitsl ucke aufweist.

4.2 Points-to-Analyse
Dieser Abschnitt ist, a hnlich wie der vorhergegangene, unterteilt in zwei Teile: Im ersten Teil wird die formale Denition der Points-to-Analyse in Verbindung mit der Informationsussanalyse erl auert, im zweiten Teil die Implementierung der Analyse mit Hilfe von Soots SPARK. Dabei werden zun achst die oben genannten Denitionen der Methoden durch Points-to Mengen erg anzt, in denen die Zeigerziele der in den Mengen enthaltenen

81

4.2 Points-to-Analyse Variablen enthalten sind. Die Points-to Mengen werden im Rahmen der Implementierung durch PointsToSets ersetzt. Soot bietet das Interface PointsToSet und mehrere Implementierungen dieser. In diesem Zusammenhang werden die PointsToSets zusammen mit der urspr unglichen Variable in einer eigenen Menge gespeichert und damit die Informationsussanalyse erg anzt.

4.2.1 Denition
Die oben aufgef uhrten Mengen der Informationsussanalyse m ussen im Rahmen einer Points-to-Analyse erg anzt werden, da die Points-to Mengen mit in die Analyse einbezogen werden. Bei den Points-To Mengen handelt es sich um Zeigerziele von Pointern, konkreter, um andere Objekte, auf die eine Variable zeigen kann und ggf. mit ihnen kommunizieren k onnte. Da daf ur nicht mehr nur die lokale Variable innerhalb eines Statements bzw. Block betrachtet, sondern auch unterschieden wird, ob es sich dabei um eine einfache lokale Variable, eine lokale Zeigervariable oder um ein PointsToSet, handelt, werden die Mengen wie folgt erweitert (PTS steht dabei f ur die Points-To Menge): P T S (v ) := { die M enge aller Objekte, auf die v mglicherweise zeigt}

Die schon in Abschnitt 4.1.1 dargestellten Mengen werden nun durch die Menge P T S (v ) erg anzt, dabei gilt f ur alle Bl ocke B und die Variablen v innerhalb eines Kontrollussgraphens:

82

4.2 Points-to-Analyse gen(B ) := {v | B speichert in v m o glicherweise sensitive Inf ormationen direkt von der Quelle in v } {P T S (v ) | B speichert m o glicherweise sensitive Inf ormationen direkt von der Quelle in ein Objekt, auf das v zeigt} {v | B speichert in v garantiert keine sensitiven Inf ormationen} {x v | B speichert mo glicherweise sensitive Inf ormationen aus x nach v } {x P T S (v ) | f alls v ein Zeiger ist, speichert B m o glicherweise sensitive Inf ormationen aus x in ein Objekt, auf das x nach der Ausf u hrung zeigt.} {P T S (x) v | f alls x ein Zeiger ist, speichert B mo glicherweise sensitive Inf ormationen aus einem Objekt, auf das x m o glicherweise zeigen kann, in v.} {P T S (x) P T S (v ) | f alls x und y Zeiger sind, speichert B m o glicherweise sensitive Inf ormationen aus einem Objekt, auf das x zeigt in ein Objekt, auf das y zeigt} {v | B gibt m o glicherweise v aus} {P T S (v ) |B gibt m o glicherweise P T S (v ) aus}

kill(B )

:=

copy (B )

:=

leak (B )

:=

Die Menge gen(B ) wird durch die Vereinigung mit der Menge P T S (v ) neu deniert, da in einem Statement nicht nur lokale Variablen, sondern auch m oglicherweise Zeiger auf Objekte erzeugt werden, die durch die Menge P T S (v ) deniert werden. Im Bezug auf die Menge kill(B ) w urde man davon ausgehen, dass der Inhalt eines P T S (v ) leer ist, da nach der Ausf uhrung des Statements kein Objekt des PTS weiterhin vertrauliche Informationen erh alt. Da aber nur die Objekte, auf die v m oglicherweise zeigt, berechnet werden k onnen, kann nicht garantiert werden, dass kein Objekt mehr aus der Menge P T S (v ) sensitive Daten enth alt. Die Menge copy (B ) wird durch Vereinigung mit den Abbildungen x P T S (v ), P T S (v ) x sowie P T S (x) P T S (v ) erweitert. Dabei wird unterschieden, ob es sich bei den Kopierzielen bwz. Quellen um Zeiger handelt oder nicht. Die Mengendenition f ur leak (B ) werden ebenfalls erweitert, da auch hier nicht nur die Variable ausgegeben werden k onnte, sondern auch die Objekte, auf die v zeigt. Ebenso werden die Mengendenitionen f ur alle Methoden M eines Programmes P durch die Menge PTS(v) erweitert:

83

4.2 Points-to-Analyse

gen(M )

:=

{v | ein P f ad durch M, auf dem m o glicherweise sensitive Inf ormationen direkt von der Quelle in v gespeichert werden.} {P T S (v ) | ein P f ad durch M, auf dem m o glicherweise sensitive Inf ormationen direkt von der Quelle in P T S (v ) gespeichert werden.} {v | P f ade durch M : v wird garantiert nicht mit sensitiven Inf ormationen u berschrieben} {x v | ein P f ad durch M, der m o glicherweise Inf ormationen aus x nach v speichert} {x P T S (v ) | f alls v ein Zeiger ist, ein P f ad durch M, der m o glicherweise Inf ormationen aus x in ein Objekt speichert, auf das x nach der Ausf u hrung zeigt.} {P T S (x) v | f alls x ein Zeiger ist, ein P f ad durch M, der m o glicherweise Inf ormationen aus einem Objekt, auf das x m o glicherweise zeigen kann, in v speichert.} {P T S (x) P T S (v ) | f alls x und y Zeiger sind, ein P f ad durch M, der mo glicherweise Inf ormationen aus einem Objekt, auf das x zeigt in ein Objekt, auf das y zeigt speichert.}

kill(M ) copy (M )

:= :=

Die Menge leak (M ) sieht nun aus wie folgt: leak (M ) := {v | ein P f ad durch M, der m o glicherweise v ausgibt} {P T S (v ) | ein P f ad durch M, der m o glicherweise P T S (v ) ausgibt} Auch f ur die Mengendenitionen in diesem Fall ist es n otig, die Menge P T S (v ) mit einzubinden, da z.B. der R uckgabewert einer Methode auch ein Zeiger auf ein Objekt sein kann, was innerhalb der Menge gen(M ) beschrieben ist. Die Denition der Menge kill(M ) verh alt sich wie die der Menge kill(B ), da auch f ur eine Methode nicht garantiert werden kann, dass das P T S (v ) nach der Ausf uhrung der Methode keine sensitiven Daten mehr enth alt. Die Mengendenition f ur copy (M ) werden auch mit Hilfe der Menge P T S (v ) erweitert, um das Kopieren von und in Zeigerziele zu denierten. F ur leak (M ) ist die Denition ebenfalls ahnlich der von leak (B ), da deniert wird, dass nicht nur ein Pfad existiert, auf dem v m oglicherweise ausgegeben werden k onnte, sondern auch P T S (v ).

84

4.2 Points-to-Analyse Die Gleichungen f ur genin (B ),genout (B ), killin (B ), killout (B ), copyin (B ), copyout (B ) sowie leakin (B ) und leakout (B ) sind analog zur Informationsussanalyse deniert, allerdings werden die oben genannten und f ur die Beinhaltung der PTS(v) konzipierten Mengen ber ucksichtig. Ebenso verh alt es sich f ur die Mengendenitionen der in und out Mengen f ur die Methoden. Die Flussgleichungen der Informationsussanalyse werden wie folgt deniert: criticalin (B ) := criticalout (B ):=
ppred(B )

criticalout (p)

(criticalin (B ) kill(B )) gen(B ) copy (B )(criticalin (B ))

mit: x copy (B )(criticalin (B )) (a x) copy (B ) mit a criticalin (B ) oder a ist einP T S und hat einen nichtleeren Schnitt mit einem P T S aus criticalin (B ), wobei x eine V ariable oder ein P T S sein kann Wie auch bei der reinen Informationsussanalyse wird im letzten Analyseschritt gepr uft, ob die Variablen aus der Menge critical durch ein Leak ausgegeben werden. Dieses Mal wird nicht nur die Variable an sich ber ucksichtigt, sondern auch verglichen, ob die Points-to-Mengen der Variablen aus critical mit denen, die ausgegeben werden, gemeinsame Objekte haben, in dem die Schnittmenge der Mengen gebildet werden. Dadurch kann ebenfalls ein Leak gefunden werden, welches alleine durch eine Informationsussanalyse nicht entdeckt w urde. Im folgenden Kapitel wird die Umsetzung der Points-to-Analyse mit SPARK gezeigt.

4.2.2 Points-to-Analyse mit SPARK


Um eine noch ausf uhrlichere bzw. genauere und damit weniger konservative Analyse durchf uhren zu k onnen, wird zu dem bisher genannten Analyseverfahren eine Points-to-Analyse mit SPARK hinzugef ugt. Damit werden die Menge der Objekte, auf die eine Variable m oglicherweise zeigen kann, berechnet. Daf ur wird zun achst SPARK in die bisherige Implementierung eingef ugt, was Listing 4.10 zeigt. Mit Hilfe einer HashMap, werden die gesetzten Optionen an

85

4.2 Points-to-Analyse SPARK u bergeben und durch den Aufruf in Zeile 11 wird SPARK gestartet.

Listing 4.10: Einbindung von SPARK in die Analyse.


1 2 3 4 5 6 7 8 9 10 11 12

public class PointsToAnalysisSpark { static void sparkPointsToAnalysis() { //erzeuge Map mit Optionen HashMap opt = new HashMap(); opt.put("enabled","true"); opt.put("...", "..."); //Uebergebe die Optionen und starte Spark SparkTransformer.v().transform("", opt); }

SPARK bietet unterschiedliche Analyseformen an. Die in diesem Fall genutzte Kombination aus den Optionen wird im Folgenden dargestellt und zur besseren Ubersicht innerhalb von Listing 4.11 genauer erl autert.

Listing 4.11: Kombinationen der Optionen in SPARK.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31

//Map mit Optionen fuer SPARK HashMap opt = new HashMap(); //Nutzung von SPARK einschalten opt.put("enabled","true"); //Beachtung der verschiendenen deklarierten Typen //(daher false, sonst wuerden sie ignoriert) opt.put("ignore-types","false"); //Nutzung des garbage collectors waehrend der Analyse //hier false, da dadurch das Programm verlangsamt wird opt.put("force-gc","false"); //Spark Pointer Assignment Graph Building Option //Nutzung eines Call Graphens auf Basis //der Variable Type Analysis opt.put("vta","false"); //Nutzung eines Call Graphens auf Basis //der Rapid Type Analysis opt.put("rta","false"); //Feldsensitive Analyse ohne die Objekte //der Felder zu ignorieren (daher false) opt.put("field-based","false"); //Typen statt Speicherbereiche werden in der Analyse genutzt //wenn true opt.put("types-for-sites","false"); //Zusammenfassung aller java.lang.StringBuffer Objekte

86

4.2 Points-to-Analyse

32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

//wird durch setzen von false verhindert opt.put("merge-stringbuffer","false"); //Zusammenfuehrung der String Konstanten zu einem einzigen PTS //hier false, um das zu vermeiden opt.put("string-constants","true"); //Simulation der Auswirkungen der nativen Methoden //in Java opt.put("simulate-natives","true"); //durch setzen von true wuerde eine Analyse auf Basis //bidirektionaler Kanten, s. Steensgaard, durchgefuehrt opt.put("simple-edges-bidirectional","false"); //Nuzung des, waehrend der Analyse der Points-to Mengen, //generierten Graphens, bei false wird ein Graph nach //CHA genutzt opt.put("on-fly-cg","true"); //Optionen zur Vereinfachung des Points-to //Graphens wie in Kapitel 4.3.4 opt.put("simplify-offline","false"); opt.put("simplify-sccs","false"); opt.put("ignore-types-for-sccs","false"); //Wahl des Algorithmus opt.put("propagator","worklist"); //Wahl der Implementierung des PTS //Hier double, da es sich dabei um ein Paar fuer jedes PTS //handelt, bestehend aus den neu gefundenen Points-to Objekten und den alten, //die bereits im Algorihmus bearbeitet wurden opt.put("set-impl","double"); //Wahl der Implementierung des double Sets //Hybrid beinhaltet eine Liste fuer 16 Elemente //und wechselt wenn das Set groesser wird zu einem Bit-Vektor opt.put("double-set-old","hybrid"); opt.put("double-set-new","hybrid"); //Zugriff auf die Points-to-Analyse des Programms soot.PointsToAnalysis pta = Scene.v().getPointsToAnalysis(); //Zugriff auf die PTS einer lokalen Variable PointsToSet pts = pta.reachingObjects(local);

Die Ergebnisse der Points-to-Analyse mit SPARK werden wie in Zeile 74 und die Points-to-Mengen einer Variable wie in Zeile 77 des Listings 4.11 erreicht. Damit kann dann w ahrend der Bildung der Mengen innerhalb der Informationsussanalyse jeder Variable ein PointsToSet hinzugef ugt werden. Um die Variablen als Paar mit dem dazugeh origen PointsToSet zu kombinieren, wird hier eine eigene Klasse Pair erstellt, die in Listing 4.12 gezeigt wird. Neben den Konstruktoren der Klasse beinhaltet sie g angige Methoden wie clone(), hashCode(), equals sowie die Get- und Set-Methoden, zugeschnitten auf die

87

4.2 Points-to-Analyse Nutzung der Soot- Klassen Value und PointsToSet.

Listing 4.12: Klasse Pair.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45

public class Pair{ //Elemente des Paars: Das PointsToSet sowie der zugehoerige Value private PointsToSet pts; private Value val; //Konstruktoren public Pair() { pts = null; val = null; } public Pair(PointsToSet pts, Value val) { this.pts = pts; this.val = val; } private Pair(Pair other) { if (other == null) { pts = null; val = null; } else { pts = other.pts; val = other.val; } } //notwendige Methoden fuer Nutzung des Paars public Pair clone() {...} @Override public int hashCode() {...} @Override public boolean equals(Object obj) {...} public public public public } PointsToSet getPts() {...} void setPts(PointsToSet pts) {...} Value getVar() {...} void setVar(Value var) {...}

Dieses Paar wird dann in der Klasse SecretSet genutzt, um die Menge der Paare, die in den oben genannten Mengendenitionen erg anzt wurde, darzustellen. Dazu muss die Implementierung u otigen Mengenoperationen verf ugen, ber alle n wie Listing 4.13 zeigt. Dazu wurden, neben den g angigen Funktionen in Zeile 19 bis 33, die f ur die

88

4.2 Points-to-Analyse Analyse ben otigten Mengenoperationen f ur die Klasse SecretSet angepasst, so dass eine Vereinigung, das Kopieren, die Bildung der Dierenz sowie der Schnitt zweier Mengen mit einem SecretSet m oglich ist.

Listing 4.13: Klasse SecretSet.


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33

public class SecretSet { ArrayList<Pair> ptsValuePairList; //Konstruktor public SecretSet() { ptsValuePairList = new ArrayList<Pair>(); } //Umsetzung der Mengenoperationen, damit die Flussanalyse auch auf //dem SecretSet angewandt werden kann public void unionSecretSets(SecretSet other, SecretSet destination) {...} public void copySecretSets(SecretSet destination) {...} public void intersectPTS(SecretSet other, HashMap<Value, Value> destination) {...} public void differenceSets(SecretSet other, SecretSet destination) {...} //allgemeine Funktionen @Override public SecretSet clone() {...} public void addPair(PointsToSet pts, Value value) {...} public static SecretSet withoutDuplicates(ArrayList<Pair> arrayList) {...} @Override public int hashCode() {...} @Override public boolean equals(Object obj) {...} public ArrayList<Pair> getPairList() {...} public void setPairList(ArrayList<Pair> ptsValuePair) {...} @Override public String toString() {...} }

Durch Erg anzen der Klasse SecretSet wird die Points-to-Analyse der Informationsussanalyse hinzugef ugt und gibt Aufschluss u ber evtl. gemeinsame Pointerziele. Die Ergebnisse der Implementierung werden im folgenden Kapitel gezeigt.

89

5 Ergebnisse

5 Ergebnisse
Die Ergebnisse dieser Arbeit werden anhand von mehreren kleineren Testf allen dargestellt. Es werden verschiedene Beispiele und deren Ergebnisse gezeigt und eine Bewertung dieser vorgenommen. Daf ur wird zun achst ein einfaches Beispiel gegeben, bei dem sensitive Daten in einer Variable gespeichert und ausgegeben werden.

Einfache Ausgabe sensitiver Daten In diesem Beispiel 5.1 wird eine Variable in Zeile 5 erzeugt und ihr in Zeile 7 sensitive Informationen durch den Aufruf von getID() zugewiesen. Danach wird diese Variable durch die Funktion leak() in Zeile ausgegeben, die symbolisch f ur die Ausgabestr ome innerhalb des Programms steht.

Listing 5.1: Einfache Ausgabe


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20

public class Example { public static void main(String[] args) { String id = "text"; //id beinhaltet durch getId() sensitive Informationen id = getId(); leak(id); } public static void leak(String s) { System.out.println(s); }

private static String getId() { return "geheimnis"; } }

90

5 Ergebnisse Die Methoden des Programms werden anhand des Aufrufgraphens ermittelt und einzeln analysiert. Die Analyse erfolgt auf Basis des Kontrollussgraphens f ur jede Methode und dem Jimple Bytecode. Das komplette Ergebnis f ur die Berechnungen der in und out Mengen f ur Beispiel 5.1 sieht folgendermaen aus:

Listing 5.2: Analysebeispiel - Ergebnis


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51

--------------------------------------r0 := @parameter0: java.lang.String[] Crit in: {} Crit out: {} ----------------------------------------------------------------------------r1 = "text" Crit in: {} Crit out: {} --------------------------------------//Waehrend dieses Blockes wird id (r2) kritisch //daher ist r2 und das PointsToSet in Crit out --------------------------------------r2 = staticinvoke <Example: java.lang.String getId()>() Crit in: {} Crit out: {StringConstantNode 26 geheimnis,: r2: java.lang.String} --------------------------------------//Die Variable r2 wird hier geleakt --------------------------------------staticinvoke <Example: void leak(java.lang.String)>(r2) Crit in: {StringConstantNode 26 geheimnis,: r2: java.lang.String} Crit out: {StringConstantNode 26 geheimnis,: r2: java.lang.String} --------------------------------------//was anhand eines Abgleichs der beiden Mengen leak und Crit in //erkannt wird. Die Methode und die Zeilennummer des Originalcodes wird angezeigt ======================================= StringConstantNode 26 geheimnis, r2: LEAK in main Zeile: 9 ======================================= --------------------------------------return Crit in: {StringConstantNode 26 geheimnis,: r2: java.lang.String} Crit out: {StringConstantNode 26 geheimnis,: r2: java.lang.String} --------------------------------------======================================= //Analyse fuer getId() --------------------------------------CriticalAnalysis for getId --------------------------------------return "geheimnis" Crit in: {} Crit out: {} --------------------------------------======================================= //Analyse fuer leak() --------------------------------------CriticalAnalysis for leak ---------------------------------------

91

5 Ergebnisse

52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77

r0 := @parameter0: java.lang.String Crit in: {} Crit out: {} ----------------------------------------------------------------------------$r1 = <java.lang.System: java.io.PrintStream out> Crit in: {} Crit out: {} ----------------------------------------------------------------------------virtualinvoke $r1.<java.io.PrintStream: void println(java.lang.String)>(r0) Crit in: {} Crit out: {} ----------------------------------------------------------------------------return Crit in: {} Crit out: {} --------------------------------------======================================= LEAK: ======================================= StringConstantNode 26 geheimnis, r2: LEAK in main Zeile: 9 =======================================

Zeile 17 von Listing 5.2 zeigt, dass durch den Aufruf von getId() die Menge critical nun die Variable r2, also id im Quellcode, enth alt. In Zeile 22 wird die Funktion leak() ausgef uhrt und die Menge criticali n beinhaltet immer noch die Variable r2, die hier ausgegeben wird. Eine Meldung davon erfolgt in Zeile 30 der Ergebnisausgabe. Dort wird eine Ausgabe von r2, ihr Wert geheimnis und die Zeile 9 des originalen Java Codes, in der leak() aufgerufen wird, gemeldet.

Kopieren der sensitiven Daten Das folgenden Beispiele 5.3 zeigt die Initialisierung von zwei String-Variablen id und x in den Zeilen 6 und 7. Der Variable id werden daraufhin sensitive Informationen zugewiesen und ihr Wert der Variable x zugewiesen. Damit beinhaltet auch x sensitive Informationen. Der urspr ungliche Wert von id wird in Zeile 12 u berschrieben, so dass nur noch x sensitive Daten enth alt. x wird in Zeile 14 durch den Aufruf von leak() ausgegeben.

Listing 5.3: Kopieren der sensitiven Daten


1

public class Example {

92

5 Ergebnisse

2 3 4 5 6 7 8 9 10 11 12 13 14 15

public static void main(String[] args) { String id = "text"; String x = "text"; //id beinhaltet durch getId() sensitive Informationen id = getId(); x = id; //Ueberschreiben von id mit nicht sensitiven Daten id = "text"; //Ausgabe von x leak(x); } }

Trotz dass die sensitiven Variablenwerte in eine andere Variable kopiert wurden, zeigt das Ergebnis der Analyse ein Leak in Zeile 14 des Java-Quellcodes an.

Sensitive Daten in Objektattributen Listing 5.4 zeigt in den Zeilen 3 und 4 die Erzeugung zweier Objekte example1 und example2. Dem Attribut example1.id werden geheime Informationen durch getId() zugewiesen. In Zeile 7 wird example1 example2 zugewiesen und in Zeile 8 example2.id geleakt. Die sensitiven Informationen sind zu diesem Zeitpunkt in example2.id enthalten.

Listing 5.4: Sensitive Daten in Objektattributen


1 2 3 4 5 6 7 8 9 10 11 12 13 14

public static void main(String[] args) { //Erzeugung von zwei Objekten der Klasse Example Test example1 = new Test(); Test example2 = new Test(); //Zuweisung von sensitiven Informationen an das Attribut id example1.id = getId(); //Zuweisung von example1 and example2 example2 = example1; //Ausgabe von id durch example2 leak(example2.id); } }

In diesem Fall wird ebenfalls ein Leak angezeigt. Zeile 8 des Quellcodes gibt sensitive Daten aus, was von der Analyse erkannt wird. Um ein Gegenbeispiel zu zeigen, wird das folgende Listing 5.5 gezeigt. Die Funk-

93

5 Ergebnisse tionalit at ist die selbe wie in Listing 5.4, jedoch wird vor der Ausgabe der sensitiven Informationen aus example2.id in Zeile 9 example1.id mit nicht sensitiven Daten u berschrieben, womit auch in Zeile 10 example2.id keine sensitiven Daten mehr enth alt.
Listing 5.5: Sensitive Daten in Objektattributen
1 2 3 4 5 6 7 8 9 10 11

public static void main(String[] args) { Test example1 = new Test(); Test example2 = new Test(); example1.id = getId(); example2 = example1; //id wird in example1 vorher geloescht example1.id = "text"; leak(example2); }

Dieser Fall soll demonstrieren, dass die Rate der Falsch-Positiven nicht zu hoch ist, denn ein Leak wird hierf ur, wie erwartet, nicht angezeigt. Ein weiteres, ahnliches Beispiel wie in 5.4 wird in Listing 5.7 gezeigt. Hier wird in Zeile 17 ein neues Objekt einer Klasse Alias erzeugt und der Funktion set() u bergeben. Die Funktion set(), in Zeile 5, bekommt hier das Objekt example in Zeile 18 zugewiesen. Durch die Ubergabe an die Methode set(), wird example.s mit sensitive Informationen belegt, die in Zeile 8 durch den Aufruf von leak(this.s) ausgegeben werden.
Listing 5.6: Aliaserzeugung
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

public class Alias { String s; void set(Alias alias) { //Erzeugung eines Alias und Hinzufuegen sensitiver Daten alias.s = getId(); leak(this.s); } ... } public class Example1 { public static void main(String[] args) { Alias example = new Alias(); example.set(example); }

94

5 Ergebnisse

20

Auch diese Ausgabe wird angezeigt und damit das Leak in Zeile 8 gemeldet.

Das Singleton-Entwurfsmuster Das Beispiel in Listing 5.7 zeigt das Entwurfsmuster Singleton. Dabei handelt es sich um eine Kombination aus einer statischen Variable, die in Zeile 4 mit dem Namen instance deklariert wird und einer Methode, die, wenn bisher noch keine Instanz der Variable instance existiert, eine neue Instanz zur uckliefert. Existiert bereist eine Instanz der statischen Variable, wird diese zur uckgegeben. So wird garantiert, dass immer nur genau eine Instanz dieser Klasse existiert. In Zeile 16 wird ein neues Objekt x durch den Aufruf von getInstance() erzeugt. Das Attribut x.s wird daraufhin in Zeile 17 mit sensitiven Informationen belegt. In Zeile 18 wird erneut Single.getInstance() aufgerufen und das Ergebnis in y gespeichert. In Zeile 19 wird dann der Aufruf von leak() ausgef uhrt und dabei y.s u bergeben. Da nur ein Objekt der Klasse Single existiert, ist y jetzt die Instanz der Klasse und beinhaltet damit in y.s die sensitiven Daten, die vorher in x.s standen. Also werden auch so geheime Daten ausgegeben.

Listing 5.7: Singleton


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24

public final class Single { String s; private static Single instance; private Singleton() {} static Single getInstance() { if (instance == null) { instance = new Single(); } return instance; } public static void main(String[] args) { //Erzeugung einer Instanz von Single Single x = Single.getInstance(); x.s = getId(); //Erzeugung sensitiver Daten Single y = Single.getInstance(); //y ist jetzt Instanz von Single und s ist mit sensitiven Daten belegt leak(y.s);

95

5 Ergebnisse

25 26

} }

Die Analyse entdeckt auch hier das Leak in Zeile 19 und meldet die Ausgabe der Daten.

Arrays Im folgenden Beispiel 5.8 wird ein einfaches Stringarray erzeugt, s. Zeile 5. Das erste und zweite Element des Arrays wird direkt initialisiert und die Werte text1 sowie der R uckgabewert der Funktion getId() darin gespeichert. In Zeile 6 wird der Inhalt des Array-Elements an der Stelle 1 geleakt, also sensitive Daten ausgegeben.

Listing 5.8: Analysebeispiel 8


1 2 3 4 5 6 7 8 9 10

public class Example { public static void main(String[] args) { //Erzeugung eines String-Arrays mit u.a. sensitiven Daten String[] stringarray = {"text1", getId()}; //Ausgabe des 2. Elements leak(stringarray[1]); } }

Die Analyse erkennt hier die Ausgabe der sensitiven Informationen und meldet diese. An dieser Stelle wird jedoch das teilweise konservative Verhalten der Analyse deutlich, denn bei einer Ausgabe des kompletten Arrays wird ebenfalls ein Leak gemeldet, obwohl nicht alle Arrayelemente sensitive Daten enthalten. Das Beispiel im folgenden Abschnitt 5 verdeutlicht ebenfalls die Konservativit at der Analyse.

If-Anweisungen In diesem Beispiel 5.9 in Zeile 5 wird der Variable id der R uckgabewert der Funktion getId() und damit sensitive Informationen zugewiesen. In den Zeilen 8-11 ist wird eine if-Anweisung dargestellt, die, falls der zuf allig generierte Wert der

96

5 Ergebnisse Funktion Math.random gleich dem Wert von i, also 0,5 ist, die sensitiven Informationen in id u berschreibt. Je nach Programmablauf sind entweder noch sensitive Informationen in id enthalten oder nicht und werden dementsprechend in Zeile 12 ausgegeben oder nicht.

Listing 5.9: If-Anweisung


1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16

public class Example { public static void main(String[] args) { String id = getId(); double i = 0.5; //nur zur Laufzeit loesbar if(i == Math.random()) { id = "text"; } leak(id); } ... }

Die Analyse ist in der Lage, einfache if-Anweisungen, deren Verlauf absehbar ist, also der Inhalt der if- bzw. else-Bl ocke mit den vorgegebenen Variablen denitiv ausgef uhrt wird, auszuwerten. Jedoch in F allen wie in Listing 5.9, wo es nicht im Verlauf einer statischen Analyse klar ist, ob der innere Teil der Anweisung jemals durchlaufen wird oder nicht, sind die Ergebnisse konservativ, denn hier wird ein Leak angezeigt.

Benchmarks Die Nutzung von SPARK ist sehr speicher- und zeitintensiv. Die Analyse einer App auf einem Smartphone wird ggf. mehrere Minuten dauern, was die folgenden Zeitmessungen zeigen. Die Tests wurden auf einem Linux Rechner mit einer virtuellen Ubuntu 10.04 Distribution durchgef uhrt und das einfachste Testbeispiel aus Listing 5.1 analysiert. Die Prozessoren des PCs sind zwei Intel R CoreTM 2Duo CPU E7200 @ 2.53GHz Prozessoren und die System-RAM-Gr oe betr agt 5GiB. Bei dieser Konguration werden 64 Sekunden ben otigt.

97

5 Ergebnisse RAM 5 GiB 4 GiB 2 GiB 1 GiB 512MB Dauer 58 Sekunden 128 Sekunden 172 Sekunden 369 Sekunden k.a.

Tabelle 5.1: Benchmarkergebnisse f ur verschiedene Hauptspeichergr oen

Die restlichen Werte wurden mit Hilfe einer virtuellen Ubuntu-Distribution 12.10, 32-Bit, erstellt. Da eine Nutzung von APEFS jedoch nicht nur lokal, sondern durch eine Serveranbindung m oglich sein soll, k onnen daf ur deutlich bessere Werte angenommen werden.

Bewertung der Ergebnisse In den vorangegangenen Abschnitten wurden verschiedene Testf alle gezeigt. Die Ergebnisse sind so ausgefallen, wie sie erwartet wurden. F ur die einfache Ausgabe der sensitiven Daten wird ein Leak gemeldet, ebenso wie f ur die Daten, die vor der Ausgabe einer weiteren Variablen zugewiesen wurden. Es wurde gezeigt, dass sensitive Daten innerhalb von Objektattributen, bzw. bei der Erzeugung von Aliasen, ebenso erkannt werden, wie innerhalb von lokalen Variablen. Bei der Vernichtung der sensitiven Daten vor der Ausgabe, wie in Listing 5.5, wird dementsprechend auch keine Ausgabe von sensitiven Daten gemeldet. Auch bei der Analyse des Singleton-Entwurfmusters wird die Ausgabe der sensitiven Informationen erkannt. Bei der Analyse von Arrays wird ebenfalls ein Leak gemeldet, wenn das betroene Element ausgegeben wird, andernfalls verh alt sich die Analyse konservativ, ahnlich wie bei der Analyse einer if-Anweisung, deren Verlauf unklar ist, siehe Listing 5.9. Die vorliegenden Ergebnisse dieser Analyse zeigen bereits gute L osungen f ur die verschiedenen Beispiele, jedoch f allt auch auf, dass trotz einer Points-to Analyse die Ergebnisse teilweise auch noch konservativ ausfallen. Manche Probleme lassen sich erst zur Laufzeit auswerten und sind innerhalb einer statischen Analyse nicht l osbar, ohne ein zu konservatives Ergebnis anzuzeigen. Insgesamt ist es gelungen, mehrere F alle mit dieser statischen Analyse abzudecken und angemessen, bzw. wie erwartet, auszuwerten.

98

Fazit

Fazit
Es ist in dieser Arbeit gelungen, eine statische Informationsussanalyse f ur javabasierte Programme zu denieren und einen Prototypen f ur die Durchf uhrung der Analyse zu entwickeln. Der Prototyp ist in der Lage, diverse F alle und Beispiele korrekt zu analysieren, wie Kapitel 5 zeigt. Dabei wird die Generierung sensitiver Daten innerhalb eines Programms erkannt und der Inhalt der Variable, in der die Daten gespeichert werden, verfolgt. Es wird analysiert, ob die Variable im weiteren Verlauf die sensitiven Daten verliert, sie also u berschrieben wird, oder die Inhalte ggf. in eine andere Variable kopiert werden. Ebenso wird w ahrend eines Analysevorgangs mit Hilfe einer Points-to-Analyse gepr uft, ob weitere Referenzen innerhalb des Programms exisitieren, die auf den selben Speicherbereich zeigen wie die Variable, die die sensitiven Informationen enth alt. Zuletzt wird gepr uft und erkannt, ob die geheimen Daten in irgendeiner Form ausgegeben werden, also anderen Personen zug anglich gemacht werden k onnten, was dann dementsprechend gemeldet wird. Die Analyse von Programmen auf der Grundlage von Java kann mit Hilfe dieses Prototypens mit einer geringen Falsch-Positiv-Rate und nur noch bedingt konservativen Ergebnissen durchgef uhrt werden. In der vorliegenden Arbeit wurde dazu der Entwurfsprozess des Prototypens dokumientiert und diverse Analyseverfahren erl autert, in dem zun achst der Einstieg in die verschiedenen Bytecodeformate gegeben, drei bekannte Frameworks, Jif, Wala und Soot vorgestellt und die Entscheidung f ur Soot erkl art wurde. Um die Datenussanalyse mit Soot durchzuf uhren, wurden die n otigen Analyseverfahren erl autert sowie die verschiedenen Soot-Bytecode-Formate, die Generierung eines Aufrufgraphens in Soot und die Pointer und Points-to-Analyse. Darauf folgend wurde die Realisierung der Informationsussanalyse gezeigt, in dem erst die jeweiligen formalen Denitionen f ur die Informationsuss- sowie Points-to-Analyse gegeben und die zugeh orige Implementierung in Soot dargestellt wurde. Um die Ergebnisse zu verdeutlichen wurden einige Testf alle deniert und dargestellt sowie eine Bewertung der Ergebnisse gegeben.

99

Fazit Diese Arbeit wurde auf Grundlage der Idee zu APEFS [48] verfasst. Bisher wurde zu APEFS innerhalb des Lehrstuhls f ur Betriebssysteme und verteilte Systeme der Universit at Siegen im Rahmen einer Bachelorarbeit bereits eine Komponente zur Filterung der Android Apps auf Basis der Android Permissions entwickelt. D.h. es existiert nun eine App mit Serveranbindung, die die Apps ltert, indem die Top-Listen der Apps aus verschiedenen Kategorien geladen und die Ergebnisse anhand vorhandener oder manuell erstellter Prole ausgegeben werden. Diese Arbeit erweitert die vorhandenen Ergebnisse durch die Analyse des Informationsusses und der Points-To-Analyse. Viele Apps werden evtl. f alschlicher Weise herausgeltert, weil sie die Berechtigungskombination von Internetzugang und dem Auslesen sensitiver Daten anfordern. Diese Apps ohne eine Analyse als schadhaft zu denieren, w are zu konservativ, was durch die vorliegende Arbeit verhindert werden kann. Allerdings muss ber ucksichtigt werden, dass es sich bei dieser Analyseform um eine reine statische Codeanalyse handelt. Dabei sind die Berechnungen der Flusssowie der Points-to Mengen nur Absch atzungen und damit immer noch teilweise mehr oder weniger konservativ. Eine ausschliesslich statische Analyse wird keine denitive L osung anbieten k onnen, da z.B. die Variablenwerte w ahrend des Programmablaufs nur gesch atzt werden k onnen. Daher sollte f ur die folgende Arbeit mit diesem Ansatz eine Verbindung mit einer dynamischen Analyse des Programms w ahrend der Laufzeit gew ahlt werden. Ebenso ist es als sinnvoll zu betrachten, die Points-to-Analyse, f ur die weitere Arbeit mit diesem Prototypen, hinsichtlich der Kontext- und Flusssensitivit at zu erweitern bzw. zu verbessern, damit noch genauere und weniger konservative Ergebnisse erzielt werden k onnen. Eine Verbesserung der Performanz sollte gegebenenfalls in Betracht gezogen werden, dazu w are jedoch eine Ver anderung an SPARK n otig. Diese Analyse k onnte durch weitere Arbeiten sinnvoll erg anzt werden, bspw. mit Hilfe von Dexpler [8]. Bisher wurde innerhalb von APEFS der Dalvik-Bytecode zu Java-Bytecode und dann zu Jimple Code transformiert, um ihn dann zu analysieren. Mit Hilfe von Dexpler kann Dalvik Bytecode auf direktem Weg zu JimpleBytecode geschrieben werden und die Performanz so verbessert werden. Eine usssensitive und damit teilweise genauere Analyse k onnte durch die in [59] vorgestellte Implementierung f ur eine Erweiterung von Soots SPARK erreicht werden. Regul ar nutzt SPARK einen Graphen zur Darstellung der Points-to Beziehungen, bei dem jeweils nur ein Knoten f ur jede Variable und jeden Parameter einer Methode genutzt wird. Hier wird SPARK jedoch erg anzt, in dem der Graph durch die Zuweisung mehrerer Knoten zu jeder Variable, einen f ur jede Nutzung, erweitert wird.

100

Fazit Diese Ver anderung von SPARK erm oglicht eine weniger konservative Analyse und sollte in Betracht gezogen werden, da so an einem bestimmten Punkt innerhalb des Programms teilweise eine genauere Points-to Beziehung f ur die Referenzen ermittelt werden kann. Eine weitere Option ist die Erg anzung dieses Ansatzes durch ein anderes Tool zur Analyse von Android Apps, genannt Androguard [5]. Androguard transformiert die apk- und dex-Dateien einer App in Python Objekte und bietet u.a. an, auf dieser Basis eine eigene, selber denierte statische Programmanalyse durchzuf uhren. Dazu kommen weitere Optionen, wie u.a. die M oglichkeit, Apps auf Unterschiede hin zu vergleichen, Indikatoren f ur schadhafte Software zu lokalisieren und die Integration weiterer Compiler. Um Androguard mit dieser Analyse zu kombinieren, m usste die Analyse mit Hilfe von Python implementiert werden und w urde nicht mehr auf dem Bytecode der App durchgef uhrt werden. Eine Informationsussanalyse bzw. Points-To-Analyse in Verbindung mit einer dynamischen Analyse ist jedoch nicht nur bezogen auf mobile Apps sinnvoll. Da die Implementierung auf der Analyse von Bytecode beruht und daher allgemein f ur Java g ultig ist, kann sie auch zur Analyse f ur weitere Java-Programme genutzt werden, bspw. Open Source Software, die im Internet angeboten wird. Es existiert ein groes Angebot kostenloser Programme, jedoch ist nicht klar, ob und wie diese ggf. auf sensitive Daten zugreifen k onnen und wie sie damit umgehen. Auf Grund der umfangreichen und un ubersichtlichen Texte hinsichtlich der Lizensierung kann angenommen werden, dass viele Nutzer die Lizenzbedingungen nicht komplett bis zu diesem Punkt durchlesen. Da, ahnlich dem Verhalten mit dem Umgang hinsichtlich der Berechtigungen, die meisten Nutzer die Bedingungen dieser Programme vermutlich unwissend akzeptieren, k onnte hier auch im Vorfeld eine Analyse stattnden, die die Programme bewertet oder z.B. zertiziert. Alternativ k onnte anhand dieser Analyseformen ggf. jeder Nutzer selber seine Software auf dem eigenen PC pr ufen und einsehen, ob und welche Daten in welcher Form genutzt werden. Neben Java Programmen k onnten auch Java-Applets oder anderweitige Programme durch eine Abwandlung der Analyse gepr uft werden. Sicherlich sinnvoll w are eine Pr ufung von Apps innerhalb von Netzwerken, bei denen eine kritische Masse an sensitiven Daten vorhanden ist, wie bspw. ein soziales Netzwerk. Die App-Programmierung spielt auch dort eine Rolle. Das Prinzip der Nutzung ist ahnlich, die Mitglieder suchen nach einem Spiel oder einer App und werden erst bei der Installation auf die Weitergabe von Daten hingewiesen. In diesem Zusammenhang w are eine Klassizierung der Apps sowie die Verdeutlichung der Informationsweitergabe bereits im Voraus ebenfalls sinnvoll.

101

Fazit Zusammenfassend wurde das Ziel dieser Arbeit erreicht und ein erster Prototyp entwickelt und getestet. Unter Ber ucksichtigung der Vorschl age zur Verbesserung dieses Prototypens kann zuk unftig eine u.U. genauere und weniger konservative Analyse von java-basierten Programmen durchgef uhrt werden. Diese Analyse kann als Hilfestellung f ur die Nutzer dementsprechender Software fungieren, in dem sie auf Risiken und schadhafte Programme hinweist, ohne, dass tiefergehende Kenntnisse u otigt werden. ber den Aufbau und Ablauf der Software ben

102

Literaturverzeichnis

Literaturverzeichnis
[1] Alfred V. Aho, Monica S. Lam, Ravi Sethi, and Jerey D. Ullman. Compiler - Prinzipien, Techniken und Werkzeuge. Person Studium, Martin-KollarStrasse 10, 81829 M unchen, Deutschland, 2008. [2] Open Handset Alliance. Android. http://www.openhandsetalliance.com/, 2012. [Online; zuletzt besucht: 15. September 2012]. [3] L. O. Anderson. Program analysis and specialization for the c programming language. PhD thesis, DIKU, Universit at Kopenhagen, 1994. [4] Apple. iOS 6. http://www.apple.com/de/ios/, 2012. [Online; zuletzt besucht: 15. Oktober 2012]. [5] Axelle Apvrille, Yanick Fratantonio, and Craig Smith. Androguard. http://code.google.com/p/androguard/, 2012. [Online; zuletzt besucht: 20. Oktober 2012]. [6] Matthew Arnold and David Grove. Collecting and exploiting high-accuracy call graph proles in virtual machines. In Proceedings of the international symposium on Code generation and optimization, CGO 05, pages 5162, Washington, DC, USA, 2005. IEEE Computer Society. [7] David Francis Bacon. Fast and eective optimization of statically typed objectoriented languages. PhD thesis, University of California, Berkeley, 1997. AAI9828589. [8] Alexandre Bartel, Jacques Klein, Yves Le Traon, and Martin Monperrus. Dexpler: converting android dalvik bytecode to jimple for static analysis with soot. In Proceedings of the ACM SIGPLAN International Workshop on State

103

Literaturverzeichnis of the Art in Java Program analysis, SOAP 12, pages 2738, New York, NY, USA, 2012. ACM. [9] Bernhard Bauer and Riitta H ollerer. Ubersetzung objektorientierter Programmiersprachen. Springer (Lehrbuch), Berlin, Heidelberg, Deutschland, 1998. [10] Marc Berndl, Ond rej Lhot ak, Feng Qian, Laurie Hendren, and Navindra Umanee. Points-to analysis using bdds. In Proceedings of the ACM SIGPLAN 2003 Conference on Programming Language Design and Implementation, pages 103114. ACM Press, 2003. [11] BITKOM. Wettkampf der smartphone-plattformen. http://www.bitkom.org/de/presse/8477 72316.aspx, 2012. [Online; zuletzt besucht: 20. Oktober 2012]. [12] Android Ocial Blog. Google play hits 25 billion downloads. http://ocialandroid.blogspot.de/2012/09/google-play-hits-25-billiondownloads.html. [Online; zuletzt besucht: 14. Oktober 2012]. [13] Google Mobile Blog. Android and security. http://googlemobile.blogspot.de/2012/02/android-and-security.html. [Online; zuletzt besucht: 14. Oktober 2012]. [14] Eric Bodden. Packs and phases in soot. http://www.bodden.de/2008/11/26/soot-packs/. [Online; zuletzt besucht: 03. Oktober 2012]. [15] Eric Bodden. Packs and phases in soot. http://www.bodden.de/2008/11/26/soot-packs/, 2008. [Online; zuletzt besucht: 26. September 2012]. [16] IBM T.J. Watson Research Center. Dynamic Load-time Instrumentation Library for Java . http://wala.sourceforge.net/wiki/index.php/GettingStarted:wala.dila, 2012. [Online; zuletzt besucht: 25. Oktober 2012]. [17] IBM T.J. Watson Research Center. Shrike technical overview. http://wala.sourceforge.net/wiki/index.php/Shrike technical overview,

104

Literaturverzeichnis 2012. [Online; zuletzt besucht: 25. Oktober 2012]. [18] IBM T.J. Watson Research Center. T.J. Watson Libraries for Analysis (WALA). http://wala.sourceforge.net/, 2012. [Online; zuletzt besucht: 22. Oktober 2012]. [19] Jerey Dean, David Grove, and Craig Chambers. Optimization of objectoriented programs using static class hierarchy analysis. In Proceedings of the 9th European Conference on Object-Oriented Programming, ECOOP 95, pages 77101, London, UK, UK, 1995. Springer-Verlag. [20] USA Department of Computer Science der Cornell Universit at. Jif - Java + information ow. http://www.cs.cornell.edu/jif/, 2012. [Online; zuletzt besucht: 02. Oktober 2012]. [21] USA Department of Computer Science der Cornell Universit at. Polyglot. http://www.cs.cornell.edu/projects/polyglot/, 2012. [Online; zuletzt besucht: 22. Oktober 2012]. [22] Julian Dolby and Manu Sridharan. Static and Dynamic Program Analysis Using WALA. http://wala.sourceforge.net/les/PLDI WALA Tutorial.pdf, 2010. [Online; zuletzt besucht: 24. Oktober 2012]. [23] David Ehringer. The dalvik virtual machine architekture, M arz 2010. [24] Carsten Eilers. Sicher, sicherer, ios? Mobile Technology, 4:4551, 2012. [25] Arni Einarsson and Janus Dam Nielsen. A survivors guide to java program analysis with soot. 2008. [26] Charles N. Fischer. Cs 701 - andersen algorithm. pages.cs.wisc.edu/ scher/cs701.f08/lectures/Lecture26.pdf, 2008. [Online; zuletzt besucht: 10. Oktober 2012]. [27] The Eclipse Foundation. Eclipse.

105

Literaturverzeichnis http://www.eclipse.org/, 2012. [Online; zuletzt besucht: 24. Oktober 2012]. [28] Inc. USA Gartner. Gartner Says Worldwide Sales of Mobile Phones Declined 2.3 Percent in Second Quarter of 2012. http://www.gartner.com/it/page.jsp?id=2120015, 2012. [Online; zuletzt besucht: 14. Oktober 2012]. [29] Samir Genaim and Fausto Spoto. Information ow analysis for java bytecode. In Radhia Cousot, editor, Verication, Model Checking, and Abstract Interpretation, volume 3385 of Lecture Notes in Computer Science, pages 346362. Springer Berlin / Heidelberg, 2005. [30] Google. Whatsapp messenger. https://play.google.com/store/apps/details?id=com.whatsapp. [Online; zuletzt besucht: 19. Oktober 2012]. [31] Google. Android. http://www.android.com/, 2012. [Online; zuletzt besucht: 05. September 2012]. [32] Google. The AndroidManifest.xml File. http://developer.android.com/guide/topics/ manifest/manifest-intro.html, 2012. [Online; zuletzt besucht: 21. September 2012]. [33] David Grove, Greg DeFouw, Jerey Dean, and Craig Chambers. Call graph construction in object-oriented languages. In Proceedings of the 12th ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications, OOPSLA 97, pages 108124, New York, NY, USA, 1997. ACM. [34] Android Developers Guide. Permissions. http://developer.android.com/guide/topics/ security/permissions.html. [Online; zuletzt besucht: 13. Oktober 2012]. [35] Christoph A. Herrmann. Struktur und implementierung von programmiersprachen i.

106

Literaturverzeichnis http://www.infosun.m.uni-passau.de/cl/lehre/sips1-ss06/, 2006. [Online; zuletzt besucht: 28. September 2012]. [36] Uday P. Khedker and Dhananjay M. Dhamdhere. Bidirectional data ow analysis: myths and reality. SIGPLAN Not., 34(6):4757, June 1999. [37] Patrick Lam. Of graphs and co grounds: Decompiling java. http://www.sable.mcgill.ca/publications/techreports/, September 1998. [38] William Landi. Undecidability of static analysis. ACM Letters on Programming Languages and Systems, 1:323337, 1992. [39] Hans Werner Lang. Algorithmen in Java. Oldenbourg Verlag, M unchen, Deutschland, 2006. [40] Ond rej Lhot ak. Spark: A exible points-to analysis framework for Java. Masters thesis, McGill University, December 2002. [41] Ond rej Lhot ak. Program Analysis using Binary Decision Diagrams. PhD thesis, McGill University, January 2006. [42] Ond rej Lhot ak and Laurie Hendren. Scaling java points-to analysis using spark. In Proceedings of the 12th international conference on Compiler construction, CC03, pages 153169, Berlin, Heidelberg, 2003. Springer-Verlag. [43] Jrn Lind-Nielsen. BuDDy: A Binary Decision Diagram library. http:// buddy.sourceforge.net, 2010. [Online; zuletzt besucht: 26. September 2012]. [44] Tim Lindholm and Frank Yellin. The Java Virtual Machine Specication. Sun Microsystems Inc., Mountain View, CA, USA, 1997. [45] Andrey Loskutov. Bytecode Outline plugin for Eclipse. http://andrei.gmxhome.de/eclipse/, 2010. [Online; zuletzt besucht: 02. August 2012]. [46] Sable McGill. Soot: a Java Optimization Framework. http://www.sable.mcgill.ca/soot/, 2012. [Online; zuletzt besucht: 13. Oktober 2012].

107

Literaturverzeichnis [47] W. M. McKeeman. Peephole optimization. Commun. ACM, 8(7):443444, July 1965. [48] Simon Meurer and Roland Wism uller. Apefs: An infrastructure for permission-based ltering of android apps, 2012. [49] Microsoft. Windowsphone. http://www.windowsphone.com/, 2012. [Online; zuletzt besucht: 29. September 2012]. [50] Steven S. Muchnick. Advanced Compiler Design and Implementation. Morgan Kaufmann / Academic Press, San Francisco / San Diego, USA, 1997. [51] Carlo U. Nicola. Einblick in die dalvik virtual machine. Fokus Report, Fachhochschule Nordwestschweiz, pages 512, 2009. [52] Nokia. Nokia. http://www.nokia.com/, 2012. [Online; zuletzt besucht: 16. Oktober 2012]. [53] Heise Online. Charlie miller zeigt l ucke im ios-codesigning. http://www.heise.de/mac-and-i/meldung/Charlie-Miller -zeigt-Luecke-im-iOS-Codesigning-1374906.html. [Online; zuletzt besucht: 14. Oktober 2012]. [54] OpenJDK. The HotSpot Group. http://openjdk.java.net/groups/hotspot/, 2012. [Online; zuletzt besucht: 26. September 2012]. [55] Oracle. The Java Tutorials. http://docs.oracle.com/javase/ tutorial/java/javaOO/index.html, 2012. [Online; zuletzt besucht: 17. September 2012]. [56] Thomas Ottmann and Peter Widmayer. Algorithmen und Datenstrukturen. Spektrum Akademischer Verlag, Heidelberg, Deutschland, 2012. [57] Gabor Paller. Dedexer. http://dedexer.sourceforge.net/, 2011. [Online; zuletzt besucht: 23. Oktober 2012].

108

Literaturverzeichnis [58] Boniface Patrick Hicks Penn State University. Jifclipse. http://siis.cse.psu.edu/jifclipse/, 2006. [Online; zuletzt besucht: 24. Oktober 2012]. [59] Polyvios Pratikakis, Jaime Spacco, and Michael Hicks. Transparent proxies for java futures. In Proceedings of the 19th annual ACM SIGPLAN conference on Object-oriented programming, systems, languages, and applications, OOPSLA 04, pages 206223, New York, NY, USA, 2004. ACM. [60] The Android Open Source Project. Bytecode for the Dalvik VM. http://www.netmite.com/android/ mydroid/dalvik/docs/dalvik-bytecode.html, 2007. [Online; zuletzt besucht: 22. Oktober 2012]. [61] Android Open Source Projekt. Android security overview. http://source.android.com/tech/security. [Online; zuletzt besucht: 14. Oktober 2012]. [62] IBM Research. Watson research center. http://www.watson.ibm.com/index.shtml, 2012. [Online; zuletzt besucht: 25. Oktober 2012]. [63] Blackberry Research In Motion. Research In Motion. http://www.rim.com/, 2012. [Online; zuletzt besucht: 19. Oktober 2012]. [64] Mooly Sagiv and Noam Rinetzky. Program analysis. www.cs.berkeley.edu/ bodik/cs264/lectures/4-chaotic-notes.pdf, 2004. [Online; zuletzt besucht: 03. Oktober 2012]. [65] J. C. Shepherdsons and H. E. Sturgis. Computability of recursive functions. Journal of the ACM (JACM), 10(2):217255, april 1963. [66] Georey Smith. Principles of secure information ow analysis. In Mihai Christodorescu, Somesh Jha, Douglas Maughan, Dawn Song, and Cli Wang, editors, Malware Detection, volume 27 of Advances in Information Security, pages 291307. Springer US, 2007. [67] Bjarne Steensgaard. Points-to analysis in almost linear time. In Proceedings of

109

Literaturverzeichnis the 23rd ACM SIGPLAN-SIGACT symposium on Principles of programming languages, POPL 96, pages 3241, New York, NY, USA, 1996. ACM. [68] Vijay Sundaresan, Laurie J. Hendren, Chrislain Razamahefa, Raja Vall eeRai, Patrick Lam, Etienne Gagnon, and Charles Godin. Practical virtual method call resolution for java. In Conference on Object-Oriented Programming, Systems, Languages, and Applications (OOPSLA 00), pages 264280, 2000. [69] Daniel Szegedi and Andreas Wiebe. Entwicklung einer android-app zur lterung von apps mit unerw unschten berechtigungen im google play store zur verbesserung des datenschutzes, 2012. Universit at Siegen. [70] Apple Inc. USA. Apple Review. https://developer.apple.com/appstore/guidelines.html, 2012. [Online; zuletzt besucht: 13. Oktober 2012]. [71] Raja Vallee-Rai. Soot: a java bytecode optimization framework. Masters thesis, School of Computer Science, McGill University, Montreal, Kanada, 2000. [72] Raja Vall ee-Rai, Etienne Gagnon, Laurie J. Hendren, Patrick Lam, Patrice Pominville, and Vijay Sundaresan. Optimizing java bytecode using the soot framework: Is it feasible? In Proceedings of the 9th International Conference on Compiler Construction, CC 00, pages 1834, London, UK, UK, 2000. Springer-Verlag. [73] Raja Vallee-Rai and Laurie J. Hendren. Jimple: Simplifying java bytecode for analyses and transformations. Technical report, McGill University, Canada, 1998. [74] Reinhard Wilhelm and Dieter Maurer. Ubersetzerbau - Theorie, Konstruktion, Generierung. Springer Verlag, Berlin / Heidelberg, Deutschland, 1997. [75] Reinhard Wilhelm and Helmut Seidl. Ubersetzerbau - Virtuelle Maschinen. Springer Verlag, Berlin / Heidelberg, Deutschland, 2007. [76] Roland Wism uller. Quellsprachorientiertes Debugging von optimierten Programmen. Shaker Verlag, Aachen, Deutschland, 1995.

110

Literaturverzeichnis [77] J. Woo, I. Attali, D. Caromel, J.-L. Gaudiot, and A.L. Wendelborn. Alias analysis on type inference for class hierarchy in java. In Computer Science Conference, 2001. ACSC 2001. Proceedings. 24th Australasian, pages 206 214, 2001.

111