 Mir wurde irgendwie ein Herald versprochen, aber es scheint niemand hier, also muss ich das selber machen. Ja, ich erzähle euch heute ein bisschen was zu automatischer Analyse von Binarys. Also wenn irgendwie ein Händler wieder mal keinen Source Code rausrückt, wie man da trotzdem irgendwie Analyse drauf machen kann, was für Analyse manche Tools schon einfach implizit machen und was es da für coolen aktuellen Kran gibt. Erst mal kurz so ein bisschen zu mir, ich schaffe bei ENW Research aus Heidelberg. In meiner Freizeit organisiere ich das Usage of DOS CTF Team aus Darmstadt, Twitter, Kontakt, FU, LA, obligatorische Firmenslide, das ist was wir so machen. Über was rede ich heute alles? Erst mal so ein bisschen was Programmanalyse überhaupt ist für die Leute, die es irgendwie noch nicht wissen oder nur irgendwie Grundlagen, formale Grundlagen 1 gehört haben oder so was. Was es da alles für Techniken gibt. Was es für Frameworks gibt, die diese Techniken bereits implementieren, was will man nicht unbedingt selber machen, was man damit alles für coole Sachen machen kann und am Ende so ein kurzes Recap. Erst mal was meine ich mit Automated Binary Analysis, im weitesten Sinne erst mal jegliche Formen von Binary Analysis, die irgendwie mit Algorithmen arbeitet, also wo man nicht als Mensch komplett draufstarrt, sondern das irgendwie Tools oder Algorithmen eben fährt. Wer in Informatik aufgepasst hat, erinnert sich vielleicht noch an das Halterproblem, dass es eigentlich so generell ganz schnell unmöglich wird, so was zu tun, da gibt es immer die erweiterte Form, Rises Theorem, das sagt im Grunde einfach nur, dass jede nicht triviale Eigenschaft eines Programms nicht von einem anderen Algorithmus im Allgemeinen rausgefunden werden kann, ja, aber aus irgendeinem Grund kann man das dann dort sehen. Das Problem, das man bei der Sache aber hat, wenn man da aus Vulnerability Research sich dran geht, ist nach was man eigentlich sucht. Crashings sind noch ziemlich einfach zu finden, weil das Programm crashed halt irgendwann, wenn man tatsächlich mal irgendein Inbrunt gefunden hat. Nerviger wird es bei so Dingen wie Memory Corruptions, die das Programm nicht crashen. Was ist eigentlich ein Memory Access, der so nicht intendiert war oder was könnten Memory Access sein, die nicht so intendiert sind? Noch mieser wird es bei Logikfehlern, da muss man sich halt irgendwie ein schönes Modell für bauen, dass man sieht, dass da irgendwas passieren kann, was nicht so beabsichtigt ist. Die Geschichte von dem ganzen Thema ist eigentlich ziemlich alt. Das erste Paper, das eine der wichtigsten Techniken vorgeschlagen hat, hat es jetzt 40 Jahre alt aus 1975. Aber das Problem an der Sache war, dass 1975 der größte Supercomputer, den sie hatten, so ein Tausendstel der Leistung hatte von so einem vier Jahre alten iPhone, das wir aktuell haben. Das ging damals einfach überhaupt nicht. Plus, es gab vor circa zehn Jahren noch mal ein paar Fortschritte bei einer anderen Technik, die die ganzen Algorithmen noch mal effizienter gebaut hat. Was das Ganze wirklich vorangebracht hat, ist die DARPA CVC, von der ich weiß nicht, wie viele Leute haben davon mitbekommen von der Sache, gar nicht mal so viele. Das hatte es nämlich soweit ich es gesehen habe, sogar in die Presse geschafft nach dem Motto der Kampf der Maschinen oder sowas. DARPA ist ein Institut in Amerika, das Grunde Gelder für Forschung an Leute gibt, die forschen in allgemeinsten Sinne und die diese Grand Challenges alle paar Jahre haben. Das gab es mal für automatisiertes Fahren und es gab eben diese Cyber Grand Challenge. Bei dir ging es darum, ein Cyber Reasoning System zu entwickeln, die genauen Kriterien sage ich dann noch. Und das war eigentlich der Punkt, wo die ganzen theoretischen Konzepte, die über die vielen Jahre in der Akademie entstanden sind und niemanden implementiert wurden oder niemanden so wirklich implementiert wurden, tatsächlich mal von Leuten gebaut wurden, weil sich dadurch Unis wirklich mal drangesetzt haben, Leute dafür abgestellt haben, baut mal so ein komplettes Framework, dass das kann. Wir wollen da gewinnen, weil das gab ordentlich Geld, also allein die Qualifikation, dass man sieben Teams haben, drei Viertelmillionen bekommen, das Gewinnerteam waren schon ordentlich zwei Millionen, da floss dann auch endlich mal Geld für die ganze Sache. Das Finale war auf der DevCon 2016, also nicht mal ein Jahr her. Deswegen, das ganze Konzept ist, also die Konzepte sind uralt, dass es mal Leute gebaut haben ist ziemlich neu, also dass es Leute vor allem außerhalb von dem Proof of Concept tatsächlich gebaut haben. Dieses Cyber Reasoning System musste diese drei Sachen tun, erstmal irgendwelche Schwachstellen finden, es musste die Möglichkeit haben, sie zu patchen und es musste Code drin haben, die tatsächlich teilweise zu exploiten, also da musste keine Shell rausfallen, aber man musste schon irgendwie zeigen, dass man zum Beispiel den EEP kontrollieren kann oder dass man irgendwie beliebige Daten lesen kann. Die HW auf der das lief ist, finde ich, immer noch super beeindruckend, wie er da lesen kann. Das waren 20 physische Kerne und Viertelterabyte an Arbeitsspeicher pro Note und jedes Team hatte 64 davon. Sie hatten also einen Cluster mit 16 Terabyte Arbeitsspeicher und ich will gar nicht ausrechnen, wie viele Kerne und Threads die damit machen konnten. Das Finale war dann auf der DevCon 2016 und das Gewinner Framework durfte dann beim DevCon CTF mitspielen. Auf allen sieben Maschinen gleichzeitig, also statt 64 Notes hatten sie dann 64 mal 7 Notes. Ja, es war an manchen Teilen des CTFs besser als ein anderes Team oder so was. Am Ende war es dann doch leider nur letzter, weil Menschen in der ganzen Sache immer noch irgendwie besser dran sind, aber zu irgendeinem Zeitpunkt besser als irgendein Team auf der DevCon zu sein, ist eigentlich immer noch ein Achievement. Das schafft auch nicht jedes Team aus Menschen. Das war das Team der CMU. Da habe ich, später wird das noch kurz erwähnt, genau. Es gibt so böse Zungen behaupten, sie hätten vor allem deswegen gewonnen, weil ihr Framework nach der Hälfte abgestürzt ist und das Scoring da so ein bisschen diesen Fall nicht bedacht hatte. Die haben dann halt volle Abtime-Punkte bekommen, weil das Framework hat nichts mehr getan. Aber die CMU hat auch Leute, die wirklich Ahnung haben, das Framework von denen ist wahrscheinlich ziemlich gut. Ja, was kann man da alles damit machen oder was sind Konzepte, wie man kennen sollte? Das erste, das Problem ist, das wir haben, ist, dass wir auf binary-Ebene arbeiten. Also wir haben nur das kompalte Programm und kein Source Code oder so was. Da keine einheitliche Sprache oder Repräsentationen erstmal. Und da man das Ganze ja am besten auf verschiedenen Architekturen machen will, braucht man sogenannte Intermediate Representations. Das ist im Grunde einfach nur eine vereinfachte Sprache, die ähnlich aussieht wie December, aber noch einmal bestimmte andere Eigenschaften hat, je nachdem, wie man sie entwickelt. Das heißt, man braucht einfach nur noch einen Lifter, der ein gegebenes Programm in diese Intermediate Sprache übersetzt und also in der Analyse macht man den dann eben auf diese Zwischensprache. Das Problem an der Sache ist, wer den XKCD kennt, es gibt viel zu viele Standards und einmal im Jahr kommt jemand her und sich die Standards sind alle blöd, es macht einen besseren und dann gibt es einen Standard mehr. Die bekanntesten sind die LVM Intermediate Representation, die das schon gemacht haben, bevor dieser Security Aspekt so stark reinkam. Die Wechselsprache wird gern verwendet, weil es da schon viele Libraries gibt, die Programme eben in diese Sprache übersetzen können und Binary Ninja ist ein bekanntes Tool, das da etwas komplett Eigenes entwickelt hat, das auch so ein bisschen darauf ausgelegt ist, dass man das als Mensch lesen kann und nicht nur guten Algorithmen darauf arbeitet. Das erste Konzept, was wahrscheinlich viele kennen, ist CFG Recovery. Wer schon mal irgendwie Aida oder Redarri oder so was offen hatte, hat gesehen, dass zeigt den Programmverlauf so schön in verschiedenen Blocks und Übergang von diesen Blocks ein. Macht das Ganze viel lesbarer. GDB ist, finde ich, deswegen allein schon fast nicht mehr benutzbar, weil man eben überhaupt keinen Überblick hat, wozu hell man gerade ist. Das Problem ist, in den meisten Fällen ist das noch ziemlich einfach zu bauen. Es läuft das Programm einfach durch. Und jeder conditional Jump ist den Übergang von einem Basic Block zu einem anderen Basic Block. Das modelliert man dann einfach als Grafen mit den Knoten als Basic Blocks und den Übergängen vom Kontrollfluss. Nervig wird das bei Dingen wie Jump EAX oder jeglicher Form von Subzionen, wo plötzlich zu einem Register gesprungen wird, weil, woher will man wissen, was eigentlich gerade in diesem Register drin steht. Das kann man mit so einem naiven Algorithmus eben nicht rausfinden. Und das passiert halt schon relativ häufig. Jump Tables kriegt Eider noch Resolved. So High Order Functions habe ich noch nie irgendwie gesehen in Eider. Bei OOP kann es halt auch schon ein bisschen hässlich werden. Wenn man diesen Graf hat vom Kontrollfluss, kann man doch schon so grundlegende Analyse drauf machen. So bestimmte Sachen kann man finden. Aber das ist im Grunde die Grundlage, auf der man dann später irgendwie weiterarbeiten kann. Dass man so ein Kontrollfluss-Graf irgendwo hat. Das nächste Konzept ist Datenflussanalyse oder Tainter Analysis, je nachdem wie genau man es macht. Das ist nichts anderes als eine Übertechnik, die einem am Ende wieder in den Grafen gibt, wo Daten eigentlich herkommen und wo sie am Ende hinfließen. Beziehungsweise welche Daten von welchen anderen Daten abhängig sind. Das kann praktisch sein, wenn man schnell rausfinden will, welche der 1.000 Funktionen in einem ggegenden Programm sich überhaupt mit User-Input beschäftigen, weil das sind dann die Interessanten, wenn man tatsächlich Schwachstellen finden will. Das kann aber auch interessant werden, wenn man da aus Privacy sich dran geht. Es gibt zum Beispiel ein Projekt für Android, das mit kompletten Datenflussanalyse Graf über eine beliebige App, solange sie nicht zu kompliziert ist, generiert. Und dann schaut, ob da irgendwo Daten herkommen und irgendwo hingehen, wo sie nicht sollten. Dann wird die Kontaktdaten ausgelesen werden und dann plötzlich im Netzwerk landen, während die App behauptet, dass sie die Kontaktdaten nicht exfiltrieren oder sowas. Das kann man dann zum Beispiel wieder gegen Policies checken, wenn man sie vorher baut und damit recht automatisch prüfen, ob irgendeine App Privacy Ansprüche stellt an sich selbst und sich dann nicht dran hält. Das nächste Konzept ist Slicing. Das basiert so ein bisschen auf Datenschlussanalyse. Das ist ein bisschen geschwollen von mir. Man reduziert alle Ausdrücke in dem Programm nur noch zu der Menge, die sich tatsächlich mit bestimmten Daten beschäftigt. Das wird bei den Beispiel dann relativ offensichtlich. Wenn man dann Datenfluss Graf vorher hat und damit eben diese Slicing-Sache anfängt, bleiben nur noch die Befehle übrig, die sich tatsächlich damit beschäftigen, wie die Summe generiert wird. Und der ganze Code, der mit dem Produkt arbeitet, ist komplett davon unabhängig. Bei dem Programm ist das noch ganz überschaubar, aber das kann dann eben auch bei größeren Mengen funktionieren. Wenn man dann zum Beispiel als Mensch dann nach drüber schauen will und nur noch wirklich sofort sehen will, was man sich anschauen muss, beziehungsweise dann brauchen spätere Algorithmen auch eventuell weniger Aufwand. Das Wichtigste von den ganzen Konzepten oder das sehr viel verwendet wird, nennt sich Symbolic Execution. Das bedeutet, dass man ein Programm nicht mal mit konkreten Werten ausführt, stattdessen alle Pfade im Programm durchläuft und dabei die Variablen und tatsächlich Variabel sind und nur noch sogenannte symbolische Werte. Das ist hier mit der Darstellung ein bisschen offensichtlicher. Das ist jetzt die Simpler-Pipen-Pseudocode. Das nimmt sich irgendwo User-Input rein und prüft den dann gegen verschiedene Bedingungen. Was bedeutet, nach dem ersten IF-Block gibt es zwei verschiedene Pfade, die das Programm nehmen kann. Das heißt, dass für den einen Pfad X auf jeden Fall kleiner 10 sein muss und für den anderen Pfad muss es auf jeden Fall größer oder gleich 10 sein, egal was der eingehende Wert ist. Das läuft dann einfach so weiter. Man hat jetzt einfach zwei Zustände und man fängt wieder weiter anzusuchen, wie die weitergehen können. Die nächste Bedingung kommt. Da ist jetzt wieder eine Branch, bei der der Wert unter 100 sein muss. Was bedeutet, bei der anderen Branch muss er über 100 sein. Was bedeutet, man hat jetzt irgendwie tatsächlich konkrete Bedingungen, um einen bestimmten Punkt im Programm zu erreichen, was man dann lösen kann. Man weiß zum Beispiel, dass wenn man zu der UIN-Funktion will, welche Bedingungen der Wert erfüllen muss und ein Beispielwert dafür ist 99. So was kann man jetzt noch im Kopf machen, aber das kann auch viel komplizierter werden wenn da komplexe Bedingungen geprüft werden. Da gibt es auch mal später ein Beispiel. Und da man das auch wieder nicht selber machen will, gibt es sogenanntes SMT und Constraint Solving. Constraint Solving bedeutet, dass man bestimmte Bedingungen hat und man versucht, ein Modell zu finden, dass all diese Bedingungen erfüllt. Das basiert auf Satzsolving im Grunde, was man eventuell mal irgendwie an der Uni gehört hat. SMT-Wandel, das im Hintergrund irgendwie in Satz um. Das dann effizient zu lösen, ist fürchterlich kompliziert und man könnte wahrscheinlich auch ein komplettes Semester damit zubringen, das zu erklären. Aber glücklicherweise gibt es da ein Tool, das einem das alles abnimmt. Was uns zum nächsten Teil bringt, zwar was es eigentlich alles so an Tools und Frameworks bei der Sache gibt. Bis dahin irgendwelche Fragen zu den Konzepten oder so was. Also die Konzepte sind vor allem relativ abstrakt. Die basieren dann eventuell noch mal auf irgendwelchen Algorithmen drunter. Aber viele der Frameworks implementieren die schon, dass man als Mensch oder mit anderen Programmen dann darauf irgendwie arbeiten kann. irgendwelche Fragen bis jetzt? Satisfiability Modulo Theories. Ich weiß nicht, was die genaue Erklärung davon ist, was das unterscheidet, aber im Grunde wandeln sie, man hat Theorien über bestimmte Domänen. Zum Beispiel die der realen Zahlen, die der ganzen Zahlen, was uns besonders interessiert, die über die Bitvektoren. Da wir das gelugt haben, dass bei einem konkreten Programm eine maximale Bitzahl hat meistens. 64 Bit auf aktuellen Systemen, also man rechnen wir mal mit Floats. Das macht manche Dinge noch mal ein bisschen einfacher. Das hat zum Beispiel aber auch irgendwie Theorien, wie Strings funktionieren. Zum Teil, glaube ich, Theorien, wie komplexere Datenstrukturen funktionieren. Also was eigentlich Eracent. Dass man das sich nicht mal selber zusammenbauen muss. Sondern sagen kann, es gibt jetzt eine Ray aus Werten, das im Speicher liegt. Darauf kann man dann wieder Dinge prüfen. Ja? Diese Theorien sind aber Menschen gemacht. Ja. Das ist schon mal die Slide. Das gibt dieses Z3-Tool dafür, das implementiert im Grunde und sammelt all diese Theorien. Das hat Microsoft mal geschrieben unter anderem, um ihre eigenen Programme zu verifizieren. Das hat Microsoft ein bisschen unnöblich. Das ist unser relativ offener Lizenz. Der QR-Code liegt komplett auf GitHub. Kann man sich durchlesen. Wenn man Lust darauf hat, sich für das Thema interessiert. Oder man kann einfach die komplette Library mit Python-Bindings verwenden. Und das Ganze als große, magische, schwarze Box betrachten. Die irgendwie in der Lage ist, solche Bedingungen zu verarbeiten und in Wert zurückzugeben, damit das Ganze lösen kann. Es gibt noch andere, aber die meisten Tools benutzen einfach diese Library. Weil man sonst Jahre wahrscheinlich damit zubringen würde, auf so einen Stand zu kommen. Es gibt nicht groß sind, das irgendwie selber zu schreiben. Sonst noch Fragen zu den vorherigen? Vielleicht eine Anmerkung. Es gibt auch eine SMT-Schwarte. Ja, genau. Richtig angemerkt. SMT-2-Lip, glaube ich. Da gab es mal eine Initiative, das zu standardisieren, dass jeder Solver, der irgendwie mit SMT arbeitet, diese gleiche Sprache spricht, dass man im Grunde jeden beliebigen verwenden kann. Z3 ist so mit der bekannteste. Es gibt noch ein paar andere, aber von dem habe ich irgendwie noch keinen, tatsächlich den beiden in der letztes Kontext gesehen. LLVM hat einen eigenen CLEE, den Sie irgendwie intern verwenden. Es gibt irgendwelche anderen Tools, die sich da dann irgendwie rein hocken, um den zu verwenden, aber das wird dann meistens ein bisschen frick gelegen. Das bekannteste Framework um all diese Techniken im Grunde zu implementieren, oder dass diese Techniken alle bereits implementiert hat, was man prima verwenden kann, ist Enger. Das wurde von den gleichen Leuten geschrieben. Also das Shellfish-CTF-Team für die Leute, denen das was sagt. Die hatten das vorher schon gebaut, aber das ist vor allem durch diese DARPA Challenge noch irgendwie Ziel-Energie und Geld reingeflossen. Die haben am Ende den dritten Platz geholt bei der DARPA CTC, sind das Best of Framework das komplett Open-Sources. Die haben danach all ihren Code, der für dieses Mecca-Fish-Framework das alte automatische Analyse macht. Das ist im Grunde die meisten Komponenten sind auf GitHub und sie versuchen alle anderen da auch noch irgendwie hochzuschieben. Und das ist im Grunde den großen Vorteil, dass es Doku hat. Es ist ganz gute Doku sogar. Es hat sogar integrierte Python-Doku und da ist es komplett in Python geschrieben, das kann man das auch ganz gut lesen. Das klingt irgendwie jetzt nicht so besonders, aber das gibt andere Tools, die nicht mal so grundlegende Sachen machen. Ein relativ bekanntes Tool hat bei Doku einfach Tudu drin stehen seit, ich weiß nicht wie vielen Jahren. Die Antwort von Montana ist halt auch so, dass es in, ja, nicht so schön im C geschrieben ist. Es gibt auch genug Paper von denen, die sind zur Zeit immer noch am Forschen in dem Bereich, so heißt wer sich mehr für die Theorie dahinter interessiert, kann zum Beispiel das State of the Art of War Paper lesen, wo die das alles noch ein bisschen ausführlich erklärt haben, was sie da tun, warum sie das tun und wie sie das implementiert haben. Das nächste Tool, das ich ganz gut finde auf Wende ist Triton. Das hat den Nachteil, dass das nur auf x86 funktioniert. Anger funktioniert mit Wechs. Es leistens unterstützt jede Sprache diese Wechs Intermediate Representation unterstützt, was die Gleiche ist, die von Valgrind verwendet wird. Also wenn jemand schon mal mit Valgrind gearbeitet hat, das unterstützt im Grunde die gleichen Sprachen. Triton leider nicht. Triton hat aber auch so ein bisschen andere Idee dahinter, weil es einer der wenigen Tools ist, die man sich in realen Kontexten verwenden kann. Es wird tatsächlich von der Firma maintained und hat nicht den Fokus darauf, dass man damit was komplett Automatisches schreibt, das einfach nur eine Binary nimmt und sich um den rest kümmert, sondern dass man das gut in andere Frameworks integrieren kann oder selber damit irgendwie rumspielt. Es hat wieder Pipenbindings, ganz okayer Dooku, könnte alles immer besser sein und bringt zum Beispiel auch einfach diese Lip Triton mit, die man dann aus beliebigen C-Programmen einfach aufrufen kann. Oder zum Beispiel, ein anderer Frameworks integrieren kann, die bereits C verwenden. Ja, das ist eigentlich das Wichtigste. Es gibt noch ein Haufen anderer Frameworks, die bekanntesten sind Bitblades, ist von der anderen kalifornischen Universität. BAP, die Binary Analysis Platform ist von den CMU Leuten, das sind die, die tatsächlich gewonnen haben. Das ist nicht komplett, also die Binary Analysis Platform selbst ist, glaube ich, Open Source, deren Mayhem Framework ist das nicht. Das Ganze ist in OCaml geschrieben. Ich weiß nicht, wie viele Leute hier irgendwie OCaml lesen können. Das ist ganz definitiv nicht. Und da das Ganze irgendwie jetzt im Grunde besitzt, dieser For All Secure Firma ist, kommt man da nicht so leicht dran. Die bieten das mehr als Dienstleistung an. Was es auch noch gibt, ist Miazum. Das ist das Tool, was ich erwähnt habe, ich hatte jetzt schon ein paar Mal den Fall, dass wir Leute erklärt haben, dass Miazum viel besser ist als Enger. Sie konnten bis heute nicht erklären, warum. Ausfinden kann man es auch schlecht. S2E ist ein Framework, das entwickelt wurde, um eine komplette 4M im Grunde laufen zu lassen und gegen bestimmte Bedingungen zu prüfen. Das war lange Zeit nicht mehr so aktiv maintained. Aber gerade sind sie dabei, so die Spire-Rewrite-Version zu veröffentlichen. Das haben sie nirgends wooffiziell angekündigt. Es gab plötzlich ein neues Gitter-Berepo namens S2E, wo langsam Code reinkommt. Das Problem an der Sache ist, das hat eine relativ restriktive Lizenz, das darf man z.B. nicht kommerziell irgendwie verwenden. Microsoft hat da auch wieder mal was Eigenes geschrieben. Und vor Kurzem hat Trail of Bits ihr Tool veröffentlicht namens Manticor. Mit dem konnte ich mich leider noch nicht viel beschäftigen. Das weniger Abstraktion als Enger ist auch wieder mit Python Bindings. Ein paar andere Features. Mir wurde gesagt, dass wenn man aus der Reverse Engineering-Ecke kommt, da man dann näher am Code tatsächlich arbeitet, sich da wohl leichter reinfindet. Ich hatte noch nicht die Gelegenheit zu, ich persönlich bin Fan von Enger, weil es einem viele Dinge erleichtert. Guten Zeit. Ja, was damit jetzt tatsächlich machen kann. Irgendwelche Fragen zu den Frameworks bis jetzt. Passt. Die Kornebender-Pass kommt nur zu diesem. Dass wir so 2-3 machen, gibt es z.B. bei diesem Team von der CMU irgendwie größere Teams? Bei der CMU hängt wahrscheinlich die Firma ein bisschen dahinter. Das hatte ich mir nicht so stark angeschaut, weil keine Ahnung von OCaml und nicht komplett öffentlich. Bei Triton da hängt halt eine Firma dahinter, die das entwickelt und da Ressourcen reinsteckt. Die machen halt auch so einfach ein bisschen Werbung damit, dass sie so ein Tool haben. Find ich aber persönlich nicht falsch. Da gibt es, glaube ich, 2 sehr aktive Developer. Enger hat Größenordnung so 5 Core-Developer. Die haben aber z.B. das Problem, dass die alle eben an der Uni arbeiten und primär dafür bezahlt werden, Paper zu schreiben und nicht irgendwelche Frameworks zu maintainen. Die sind also mehr damit beschäftigt, dass sie tatsächlich irgendwelche neuen Konzepte entwickeln, wie man so was analysiert, dann auch tatsächlich in Enger so weit implementieren. Aber das ist halt nicht der Hauptjob. Es gibt noch mal ein paar Tools für Enger, die halt irgendwie in der Freizeit von denen maintained werden, die dann halt Early Alpha State sind. Bei S2E weiß ich, dass da tatsächlich ein Team dahinter steckt. Die Uni hat tatsächlich Software-Entwickler angeheuert. Ich weiß nicht, wie viele Core-Developer sie sind, aber da sind auch tatsächlich Ressourcen dahinter. Na ja, Microsoft's Edge, da ist Microsoft dahinter. Bei Trail-of-Bits bin ich mir nicht sicher bei der Manticore-Framework. Ja, was macht man mit dem Ganzen? Das Problem an der Sache ist, das klingt alles fürchterlich cool, aber wird sehr schnell sehr teuer von den Berechnungen. Man hat ja vorhin die Maschinen gesehen, die bei der DAPA-Challenge gewendet wurden. Da bin ich für mich absolute Mengen an Rechenpower. Die braucht man halt irgendwo auch. Zum Beispiel Symbolic Execution hat halt schnell das Problem, wenn man irgendwie eine Schleife hat, wo es immer wieder eine Branch gibt und das irgendwie naiv durchgeht, dann steigen halt die Anzahl der möglichen Zustände relativ schnell exponentiell. Da reicht irgendwie all der Arbeitsspeiche, den man haben kann, auf 16 Terabyte irgendwann nicht mehr, wenn man das nicht intelligent angeht. Manchen Fällen hat man Glück und das ist einfacher, aber es kann halt ziemlich hässlich werden. Entweder man kombiniert das Ganze irgendwie mit einem Menschen, der das gezielt an bestimmten Stellen einsetzt oder man kombiniert das Ganze mit einem Fuzzer. Im Grunde hat jedes der Teams, die bei der DAPA-Challenge mitgemacht hat, nicht irgendein riesiges System beschrieben, das alles symbolisch ausführt und dadurch jegliche Wohlins findet, sondern sie haben alle AFL den Fuzzer verwendet und wenn der dann irgendwann nicht mehr weiterkam, haben sie an die Symbolik Execution Engine übergeben, um irgendwie neue Pfade zu finden und mit diesen neuen Pfaden dann wieder an den Fuzzer zurückzugeben und der hat von der Stelle einfach weitergefasst. Das Projekt von den Engerleuten zum Beispiel nennt sich Driller. Da gibt es auch wieder ein komplettes Paper drüber, wie sie das gemacht haben. Und aktuell gibt es auch irgendwie, versuchen Sie das in den benutzbaren Zustand zu kriegen, dass man sich das tatsächlich irgendwie mal verwenden kann für so eine nicht akademische Projekte oder dass man das außerhalb der ein komplettes Framework betreiben kann. Man kann das auch ein bisschen, das Zwischending ist mit Taint Analysis, so was zu prüfen, da symbolische Ausführung wieder relativ teuer ist, dass man zum Beispiel einfach nur prüft, welche Daten irgendwie für welche Branches zuständig sind und dass der Fuzzer sich dann darauf konzentrieren kann, dass er eben genau diese Daten ändert. So, Installing Enger. Ja, die Slide ist drin, weil das leider nicht so einfach ist. Enger ist noch das freundliche Tool zum Installieren. In dem Virtual Enf funktioniert das halbwegs. Mit PIP Install. Wenn man es auf seinem Hauptsystem betreibt, hat man relativ schnell das Problem, dass sie irgendwie geforkte Lips verwenden, die kollidieren, mal irgendwie die Python-Version nicht mehr mitspielt oder ähnliches. Also der beste Weg, die Tools zu betreiben, ist meistens leider im offiziellen Docker-Container, weil da garantieren Sie, dass es funktioniert. Bei Enger ist es in PIP und funktioniert ganz gut. Bei anderen Tools muss man schon eine Weile freckeln, bis sie überhaupt bauen. Ja, konkrete Beispiele. Was können wir mit dem Ganzen jetzt überhaupt tun? Der Code links ist ein Rapper um Melok. Versteht irgendjemand, was der Teil oben macht, die ganzen Bit-Operation. So jetzt auf Anhieb, wenn man sofort drauf schaut oder auch mit ein bisschen länger nachdenken. Wie war es auch nicht sofort ersichtlich? Ja, es ist eigentlich eine recht simple Operation, die es da macht. Ich hatte keine Lust, da irgendwie länger drüber nachzudenken. Ich dachte mir halt einfach, naja, wenn die irgendwas irgendwie Speicher alluzieren, dann ist das bestimmt fürchterlich blöd, wenn sie irgendwie versuchen, 100 bytes zu alluzieren. Aus irgendeinem Grund kommen dann weniger bytes raus. Dann kommt es garantiert im höheren Programmverlauf irgendwie zu einem Overflow. Und das ist ein Problem, dass man zum Beispiel jetzt gut mit irgendwie Constraint-Solver modellieren kann. Mit diesen grundlegenden Sachen. Page Size ist halt standard Page Size in dem Fall. Und man nimmt einfach an, dass wenn man es schafft, dass dieser S-Wert am Ende kleiner ist, als das, was man reingegeben hat, irgendwohin Dinge fürchterlich schiefgehen. Und das kann man jetzt tatsächlich mit Constraint-Solvern irgendwie prüfen. Man muss diesen Code irgendwie in Python kriegen, das Problem formulieren. Und dann gibt man das einfach an den Solver und der abspuckt einem dann entweder in Antwort aus oder sagt, nur gibt keinen Wert, wie diese Bedingungen erfüllt. Wie man das Ganze in Python bekommt. Enger bringt ein Foreign-Function-Interface mit. Das es einem erlaubt, beliebige Adressen aus irgendeinem gegebenen Programm zu nehmen. Und es gibt eine Funktion zurück, um das aufzurufen. Wenn man also die Start-Adresse von irgendeiner Funktion nimmt oder übergibt, hat man jetzt eine Python-Funktion, die Logik dieser Funktion aus der binary einfach in Python zur Verfügung stellt. Das funktioniert auch über verschiedene Betriebssysteme entweckten. Man kann zum Beispiel da drinnen DLL laden und einzelne Funktionen von der DLL aufrufen. Und was das nochmal von C-Types unterscheidet, das würde auch irgendwie mit C-Types gehen mit Python. Aber man kann das eben jetzt auch mit symbolischen Werten machen. Man kann sich also irgendeine Funktion nehmen aus einem Programm, die irgendwie komisch aussieht und damit einfach mal rumspielen und irgendwelche Sachen prüfen. Das ist relativ featurereich. Also man kann so auch so Dinge machen, wie dass man dem eine Python-Liste übergibt und der wandelt das dann intern um in einen Pointer zu einem Speicherbereich, wo dann eine Raiderin liegt. Structs kann man mit Tupeln in Python irgendwie bauen, weil man immer so konkret, also immer Werte bestimmt, der Länge hat funktioniert das dann auch. Der wandelt das im Hintergrund einfach um. Calling-Convention und so was und man spart sich irgendwie, selber irgendwie rum basteln zu müssen. Man kann das recht gleich in Python abstrahieren. Was aber auch meistens funktioniert, ist, dass man diese Logik einfach copy-pasted aus, entweder wenn man source code hat aus dem source code oder irgendwie aus dem Decompiler, wie einer. Die meisten dieser Operationen sind in der Z3-Absorption, die verwendet wird, schon implementiert. Und das funktioniert erstaunlich gut. Also ich hatte ziemlich oft die Situation, dass ich den Code einfach copy-pasted habe und es kamen das raus, was ich wollte und ich konnte es verwenden. Demo ist tatsächlich. Kann man so weit lesen alles. Also rechts ist einfach der Code, der das, was in den Slide Styles beschrieben war, implementiert. Setzt halt so ein paar Konstanten, Page Styles, Word Styles. Bei der man jetzt nicht unbedingt sofort verstanden hat, was sie tut. Beziehungsweise wo die Gegend, die man bestimmte Bedingungen prüfen will. Das ist jetzt einfach der copy-pasted Code. Einfach als Python Funktion definiert, mit der man später arbeiten kann. Dann jetzt. Man muss Clarify in dem Fall, sollte man erklären, ist eine Zwischen-Layer, die bei Enger verwendet wird zu beliebigen Sorvern. Man kann also auch was anderes verwenden als Z3 im Hintergrund. Aber wirklich support ist nur für Z3 da und das funktioniert eigentlich auch in den meisten Fällen. Falls irgendjemand schon mal mit Python und Z3 gearbeitet hat, die Funktionsaufrufe sind im Grunde die gleichen. Die Z3-Version, die Enger mitbringt, ist aber nochmal kleiner, dass sie weniger Features hat. Also man braucht halt nicht irgendwelche Theorien über die realen Zahlen, wenn man Beinwien alles macht. So instanziert man sich eine symbolische Variable. Der Teil hinten steht für Bitvector Symbolik, der übergibt man einfach nur den Namen, der intern verwendet wird und wie groß diese sein soll. Und das Einzige, was man hier wirklich, also die gedankliche Arbeit in dieser Problemlösung sind eigentlich nur die paar Zahlen. Das sind die Constraints. Also die Bedingungen, die gelten müssen, die man im Software übergibt. Das ist, dass dieser Sz Variable größer sein muss mit der Bit-Operation gar nicht erst getroffen. Man will prüfen, ob es irgendeinen Wert gibt, für den das Ergebnis dieser Funktion kleiner ist, als das, was man reingegeben hat. Und um irgendwie Error-Handling zu umgehen, schaut man auch, dass das Ergebnis nicht null ist. Da war in dem Code, in der längeren Version, haben sie nochmal geprüft, ob Melloc irgendwie nicht null returned oder sowas. Aber das war es dann eigentlich schon. Diese Constraints übergibt man dann einfach im Server und sagt, man will die ersten bis zu drei Werte zurückhaben, die all diese Bedingungen erfüllen. Und der Code hier unten ist eigentlich nur, um es schön irgendwie zu printen. Und ... Jetzt eine Frage. Diese Funktion F. Ist diese Mantel etwas anders als unter 10? Nein, das ist der Witz an der Sache. Weil diese ... Ja. Aber ... Wenn wir die aufrufen mit einem Python-Wert, dann verhält sie sich wie mit Python. Aber was im Hintergrund passiert, ist dieses X, das wir reingeben, wenn wir das hier unten aufrufen, so kann man das gleich beschossen, ist ... Ah, genau. Ist diese symbolische Variable. Ist diese symbolische Variable. Und bei der ist hier festgelegt, dass die eine bestimmte Größe hat. Soll heißen die Funktion oben, wird nicht mit einem Python BigInt ausgeführt, sondern mit so einem Objekt aus ClaryPy, ein BVS-Objekt, für das all diese Operationen definiert sind, so wie sie tatsächlich auf Bitvektoren stattfinden. Was hier passiert, ist tatsächlich, das wird nicht irgendwie dem Solver übergeben, sondern die Bibliothek selbst wandelt diese Bitvektoren mit diesen Operationen dann wieder in einen komplizierteren Bitvektor um, der intern die Repräsentation von diesen Operationen enthält und sich dann abstract syntax tree. Soll das dann den Solver übergeben. Das bedeutet zum Beispiel auch, dass man die Plus-Operation oder man kann das noch mit konkreten Werten machen, also so ein BVV, das implementiert dann auch tatsächlich Overflows und so was. Da ist dann irgendwie 0xFFFFFF plus 1 tatsächlich 0 und der wird nicht einfach größer gemacht von Python. Was man, wenn man halt irgendwie mit so Datentypen, wie sie tatsächlich im Speicher liegen, rumspielen will, sonst funktioniert das halt in Python nicht, sondern einfach größer. Mit C-Types würde das irgendwie auch gehen, aber der Spaß daran ist, dass man hier eben aus symbolische Werte verwenden kann. Wenn man das ausführt, spuckt es eben einfach sofort aus. Für diesen Wert von SZ ist das Ergebnis größer Page Size all diese Bedingungen gelten und man hat im Grunde einfach ein Overflow eben drin gehabt. Für ein bisschen kleinere Werte kommt da am Ende halt 0 raus, was auch noch irgendwie schiefgehen kann und das hilft aber eben nicht alle. Was in dem Fall schon heißt, irgendwie man hat irgendeine Vulnerability da drin, man kann dafür sorgen, dass dieser Buffer kleiner ist, als er sein sollte und dann wird im Programm darüber auf jeden Fall was schiefgehen. Als ich die Funktionen in Python hatte und damit rumgespielt habe, habe ich auch irgendwie auch sofort gesehen, natürlich, die leint einfach zu Page Size, aber da ich noch nicht irgendwie so viel mit Bits in C rumgespielt habe, war mir das am Anfang nicht direkt offen ersichtlich und viele Leute, denen man das irgendwie vorgelegt haben, haben auch irgendwie eine Weile gebraucht, bis sie da drauf kamen. Ja. So. Hier nochmal kurz der Code und rendert nicht, wie es soll, aber okay. Man definiert sich einfach diese Constraints und diese Funktion, schiebt das an den Server und der Server antwortet einfach mit Jo, hier ist der Wert, der all diese Bedingungen erfüllt. In dem Fall gibt es tatsächlich nur einen, weil er hat ja bis zu 3 irgendwie zurückgeben sollen. Ich habe noch eine Frage zu dem Code. Ja. Ja. Also die Frage war, ob das Maschinenunabhängig ist und ob man auch irgendwie X86 ARM anschauen kann. Ja, der Loader LED, also LED-Dibinary eben und die Wax-Library übersetzt das eben in diese Intermediate Representation und dieses komplette Tool arbeitet eigentlich auf diese Intermediate Representation, die einfach all diese Architekture-Eigenheiten also mit Enger relativ breiten Support für verschiedene Architekturen und das würde halt 1 zu 1 auch so funktionieren bzw. das arbeitet ja noch gar nicht direkt auf Code tatsächlich. Da haben wir noch mit Python Funktionen gearbeitet. Aber angenommen, wir hätten die so Funktionen aus dem Foreign Function Interface genommen, dann hätte die Funktion sich im Grunde genauso verhalten, wenn man diesen Code Teil genommen hätte und das wäre dann auch für X86 das gleiche gewesen, das Verhalten. Dann funktioniert es auch mit verschiedenen Architekturen. Hätte auch, wie gesagt, ein Windows-Binary oder sowas sein können. Da das alles in dem Emulator, also in das symbolischen Execution-Engine im Hintergrund läuft, ist das unabhängig vom Hostprozessor. Sonst fragen. Das nächste was man damit machen kann ist Funktionen invertieren. Die ist zu einem gewissen Grad. MD5-Reinwerfen funktioniert da leider nicht. Aber simplere Sachen, die sich mit SMT-Stirving noch modernieren lassen und weit jenseits von dem ist, was man als Mensch einfach mal schnell ausrechnen will, geht damit eben auch noch. Es funktioniert wieder genauso. Man braucht irgendwie eine Funktion, wenn man damit direkt arbeitet, die wieder aus dem Interface kommen kann und den man in Python baut. Das Beispiel für die Funktion, die ich jetzt hier genommen habe, ist einfach gründlich eine komplette mathematische Funktion würde ich jetzt auch nicht im Kopf machen wollen. Deswegen kann man das einfach wieder an den Solver auslagern. Wie das funktioniert, man setzt sich und dann implementiert die Funktion wieder in Python oder man verwendet sie irgendwie extra. Baut sich die Variablen zusammen bisschen printen, dass man sieht, was los ist. Und die Bedingung, die man dann einfach stellt, ist genau die offensichtliche. Mit einem symbolischen Wert und der Konstanten muss das Ergebnis das da sein. Das schiebt man wieder an den Solver rüber und lässt sich das Ganze einfach nur wieder ausgeben. Das sieht dann so aus und hier unten ist dann der interessante Teil die beiden möglichen Solutions. Man erkennt vielleicht, welche davon ich bei, als ich das geschrieben habe, intendiert hatte und welche mich etwas irritiert hatte. Wenn man nicht nur eine Lösung zurückgeben lässt, spuckt er nämlich die zweite aus. Und wenn man dann ein bisschen über nachdenkt, ist das relativ offensichtlich, weil das ist halt einfach ein Overflow. Und das ist für den Solver genauso valide wie alle anderen Formen von Operationen. So verhalten sich Variablen bzw. Bitvektoren im Speicher einfach. Dadurch wie z3 intern implementiert ist, ist das auch die erste Lösung, auf die das Ganze kommt. Und das Ergebnis ist halt einfach bei einem 64-Bit Register genau das gleiche. Das kann zum Beispiel auch was, was ich da tief gehen könnte. Man sieht, der Code geht da so ein bisschen rechts aus dem Bild. Ja, das ist auch noch der schönen Code zum schönen Pünden. Das ist halt auch irgendwie was man damit einfach schon prüfen kann. Das sind die beiden einzigen Solutions hier. Zu dem Code irgendwie fragen. Zwei verschiedene Lösungen. Für die CTF-Spieler wird jetzt der Teil interessant. Man kann damit bestimmte Challenges einfach in drei Zahlen Python lösen. Und Challenges, die man wirklich nicht per Hand lösen will. Wer das letzte Defcon CTF gespielt hat, die komplette Reversing-Kategorie war im Grunde darauf ausgelegt, dass man automatische Lösungen findet, weil man hat 100 von den Binarys ungefähr bekommen, musste das automatisieren. Das Konzept ist jetzt einfach das gleiche wie vorher. Das gibt am Ende die Bedingungen in den Server. Ich habe jetzt leider gerade nicht mehr anscheinend die Slide drin, wo man die Funktion gesehen hat. Im tatsächlichen Code war das für diese Demo so circa 50 Assembler-Instructions, die ganz viele Dinge mit Floats gemacht haben, Werte verglichen haben und am Ende einfach geprüft haben, ob ein Register, also das Ergebnis dieser Kalkulation den fixen Wert entspricht und dann halt entweder sich beendet oder es ging zum nächsten Check über. Von diesen Checks gab es 15 Stück. Mit dem Decompiler konnte man halbwegs sehen, was passiert, aber das wäre immer noch ziemlich aufwand gewesen. Das ist der Punkt, wo Enger extrem mächtig wird, weil der Crowd hier drüben, der eigentliche Lösevorgang sind diese beiden Zeilen hier. Was da oben passiert, ist, dass man die Binary eben lädt beziehungsweise die Libraries lädt. Hier die Umgebung aufsetzt, da die Binary in dem Fall UserInput über AGW entgegenimmt, muss man den Programmzustand erstmal aufsetzen, dass eben das erste Argument symbolisch ist und das Programm in Grunde in Abhängigkeit von diesem Wert ausgeführt werden soll. Generiert man den Zustand, das sind alles Dinge, die man in der Enger-Doku nochmal schnell nachschauen kann, wie man das macht, wenn man das Konzept einmal verstanden hat. Und der eigentliche Lösevorgang ist, dass man die Fahrtgruppe generiert und die dann einfach durchläuft und gegen Bedingungen prüft. Und das geht soweit, dass man sagen kann, dass man nicht mal mehr Adressen... Also eine Möglichkeit, das zu machen, ist, dass man konkrete Adressen angibt, die er finden soll und konkrete Adressen, die er vermeiden soll. Die müssten man sich dann unbedingt aus der Binary rauslesen. Was auch geht, ist, dass man ihn einfach während der Ausführung prüfen lässt, ob der Zustand des Programms bestimmte Bedingungen erfüllt. Oder steht da irgendwie Access denied, was man halt relativ schnell sieht, wenn man das einmal ausgeführt hat, wie sich das verhält. Das läuft dann ein bisschen, weil das relativ ineffizient ist, weil es jedes Mal den Zustand nachgucken muss. Aber nach so circa 8 Sekunden ein paar Warnungen spuckt es einem einfach die Flagge aus. Und dieser Input ist jetzt konkret der Input, der in den Gewinnzustand einen einfach bringt. Das heißt, man kann einfach solche simpleren Probleme aus CTF in paar Zeilen Python einfach lösen. Das funktioniert dann wieder auch für komplizitere Sachen. Aber da muss man dann wieder aufpassen, dass es nicht zu oft, wenn nicht wird und den Server überfordert. Weil man hat schon gesehen, es ist jetzt ein i54 drin und der braucht schon ein paar Sekunden, aber die Optimierung ist nochmal wieder ein komplett anderes Thema, wo auch dran gearbeitet wird. Aber da geht halt auch noch nicht irgendwie alles sofort, da es einfach kompliziert das Problem ist. Der Code hier unten ist nichts anderes als ein beliebigen String zurückgeben, um diesen Zustand zu erreichen für diesen Wert. Ist der Code soweit jedem ersichtlich, was der eigentliche, was die eigentliche Logik da drin ist. Sondern wie man so was verwenden wird und umschreiben. Ja, genau. Allgemeine Weg, wie man da dran geht ist das, was man sich anschauen will, symbolisch. Man fängt irgendwo an, das muss nicht am Beginn des Programms sein, das kann auch am Beginn einer Funktion sein, das kann am Beginn der Main Funktion sein, falls man sich vorher irgendwie Setup sparen will. Es kann mitten im Programmcode sein und dann lässt man das einfach so lange laufen, bis irgendeine Bedingung erfüllt ist und die Bedingungen können dann auch wieder irgendwie komplizierter sein. Das muss nicht heißen, dass es irgendwie was bestimmtes im Output steht oder dass zum Beispiel sondern auch das bestimmte Variable einen bestimmten Wert haben muss oder was für Exploiting sehr interessant ist, dass der Program Pointer also EEP plötzlich symbolisch ist. Weil das bedeutet in dem Fall, dass der Program Pointer vom User Info abhängt und wenn er dann komplett gar keine Bedingungen mehr hat, sondern heißt das so viel wie man hat das gerade geschafft und hat gerade in den Weg gefunden den Programmfluss komplett zu übernehmen nur mit Nutzerinput. Und da das Programm da Angular intern kein Verständnis hat für oder wenn die Algorithmen, es gibt Algorithmen für, um rauszufinden, wie groß eigentlich ein Buffer sein soll. Aber es ist nicht ganz so einfach rauszufinden, dass irgendwie eine Adresse noch zum bestimmten Array gehört. Es gibt es Techniken für, aber da gibt es wieder 20-seitige EEPer drüber, wie einfach nur dieser eine bestimmte Aspekt funktioniert und wie man den Effizient implementiert. Ja, es können solche Tools noch, vor allem Angular. Die Bugging geht damit ganz gut. Wer schon mal irgendwie mit Traces gearbeitet hat oder versucht halt irgendwie Programme zu tracen mit Angular kann man bei bestimmten Aktionen einfach einen Breakpoint setzen, bei dem dann eine bestimmte Python Funktion aufgerufen wird. Der Code rechts zum Beispiel tut nichts anderes als jedes Mal, wenn irgendwo in den Speicher geschrieben wird zeigt es an, was geschrieben wurde und wohin es geschrieben wurde. Was es auch noch gibt, sind Hux für bestimmte Funktionen. Da, wenn man versucht, irgendwelchen Lipsik-Code durchzulaufen mit symbolischer Ausführung, man auch relativ schnell an den Punkt ist, dass der Arbeitsspeicher einfach nicht mehr geht und es einfach viel zu viele Optimierung gibt und verschiedene Pfade, dass man das noch irgendwie symbolisch gut ausführen konnte. Aber, da man das ja relativ abstrakt an irgendwie in Python implementieren kann, gibt es für die Standard-Lipsi-Funktionen in dem Framework einfach schon was, dass zum Beispiel so was wie Gats oder Putz einfach direkt mit STD outarbeitet und nicht all diese internen Sachen emuliert werden müssen, die da verwendet werden. Was damit auch funktioniert, ist, wenn man irgendwelche Programme hat, die Anti-Debugging-Techniken drin haben und versuchen den Debugger zu detecten, weil Enger ist kein, oder solche Frameworks symbolische Execution Engine, ist kein Debugger im eigentlichen Sinne, sondern ein kompletter Prozessemulator oder ein kompletter CPU-Emulator. Manche der Detection Tricks funktionieren einfach nicht, manche andere machen das Framework auf unbeabsichtiger Wege kaputt, weil das noch nicht implementiert ist. Signal Handling zum Beispiel ist fürchterlich kompliziert, weil die möglichen Fahre des Programms oder das Verhalten noch mal um einiges komplizierter wird. Das ist zum Beispiel bei Enger noch ein offenes Problem, das geht bis zum gewissen Punkt, man kann das selber bauen, aber es geht leider noch nicht alles. Insgesamt, man kann zum Beispiel solche Crack-Me-Aufgaben mit Anti-Debug-Techniken oder auch irgendwelche konkreten Programme von irgendwelchen Händlern, die versuchen, dass man sich das als Security-Researcher nicht anschaut, einfach umgehen und damit trotzdem irgendwie weitermachen. Ja, warum ich überhaupt hier stehe und das ganze Zeug erzähle. Ich finde das Ganze fürchterlich interessant, weil Reverse Engineering für viele schon schwarze Magie ist. Nicht jeder versteht, wie der CPU drunter funktioniert und wie Zeug im Speicher funktioniert. Als wir dann irgendwann mal Leute erzählt haben, dass man dann auch fürchterlich komplizierte Mathe darauf werfen kann, das versteht wahrscheinlich kein Mensch und das ist fürchterlich kompliziert. Wenn man sich damals ja 3 anschaut, wird das ein endloses Loch aus Dingen, die man sich anschauen müsste, um alles zu verstehen. Der Trick einer Sache ist, aber dass man irgendwo die Grenze zieht und sich denkt, das ist jetzt eine magische schwarze Kiste, die funktioniert einfach. Z3 kann einem einfach irgendwelche Werte zurückgeben, die Bedingungen erfüllen und plötzlich hat man ein extrem mächtiges Werkzeug, das einem das Leben einfacher macht. Wer ist interessiert, man kann sich immer noch den ganzen Kram drunter anschauen, auch irgendwie einfach nur Libraries mit Python-Bindings mit relativ abstrakten Konzepten, mit denen man arbeiten kann. So etwas wie Slicing braucht ja verschiedene Analyse-Technik-Moten drunter noch, aber zum Beispiel Enger kann einem das einfach schon direkt abnehmen. Man kann sich einfach noch den Output anschauen und bekommt dadurch als Mensch schneller ein Verständnis für, was der Code tut. Für Leute, die sich für die ganze Theorie interessieren, am konkreten Beispiel Binary Analysis gibt es eine Reading List auf diesem Block, die viel zu viel Material umfasst. Also ich glaube, das sind allein in mir 10 PhD thesis, mehr als 100 Seiten, so groß wie eine PhD thesis werden kann drin. Aber auch irgendwie viele, einfach nur Konzepte, wo man sich mal umschauen kann, die Leute, die nur studieren, ob die Uni irgendwie sowas in die Richtung anbietet. Das basiert viel auf formale Informatik, theoretische Informatik, teilweise auch einfach viel Mathe. Aber für die Leute, die sich irgendwie das Ganze cool finden, aber nicht so sehr in die Theorie wollen, es gibt noch extrem viele Sachen in dem Bereich, die man mal machen müsste, aber irgendwie noch niemand getan hat einfach, weil die Leute, die vor allem diese Projekte bauen, akademischen Druck haben, dass sie Paper schreiben müssen und da Forschung machen. Es gibt zum Beispiel das Enger Management Projekt, was ein Interface ist für die Enger, für das Enger um das Ganze zu visualisieren. Das ist zur Zeit in der frühen Alpha-Phase, wird leider nicht so aktiv entwickelt, aber wer zum Beispiel irgendwie mit UIs umgehen kann und dann mal basteln wäre, kann man ja relativ schnell was bauen, was dann noch wahrscheinlich von Leuten verwendet wird. Die haben auch eine allgemeine Help Wanted List, falls man sich da mal anschauen will, was man alles machen kann. Das reicht von so müsste mal jemand sich irgendwie ein paar Tage dran setzen zu, könnte man mal eine PhD thesis drüber schreiben. Dokumentation ist auch immer ein fürchterliches Problem, weil die Leute, die das schreiben, kennen das schon und vergessen, dass es für andere Leute nicht so einfach ist, da anzufangen. Triton zum Beispiel hat leider nicht die coole Python-Doku. Also Python-Doku in dem Fall ich verwende Enger Prima von iPython aus und man kann dann zum Beispiel an beliebige Objekte einfach mal ein Fragezeichen hinten dran hängen und man kriegt schon sauber erklärt, was das eigentlich tut, ohne dass man irgendwie den Code lesen muss. Könnte man für Triton auch machen, müsste man halt jemand die offizielle Python zu Python übersetzen. Das ganze Zeug bauen fand ich auch fürchterlich nervig, Enger geht ganz gut. Bei Triton habe ich erst mal eine Weile gebraucht, bis es auf Archkompilter hat. Da die Entwickler gerne annehmen, dass Python standardmäßig zu Python 2 linked. Und wenn man dann mal Python 3 als Standard hat, Rechen an Obskuren stellen, irgendwelche Sachen und das kompiltert einfach nicht mehr. Nachdem man dann irgendwie alle Stellen gefixt hat, merkt man, das gibt dann noch mehr. Inzwischen baut Triton auf Arch, das PKG-Bild und das ist jetzt einfach nur der übliche Verlauf. Das werde ich irgendwie mal die nächsten Tage ins AOR und ins Arch-Thrike-Repository pushen, dass man das dann regulär einfach über den Paketmanager installieren kann. Wer da irgendwie mit rum spielt, man macht Leuten das Leben sehr viel leichter, wenn man einfach irgendwie direkt im Hintergrund den Compile-Formern fixt, dass andere Leute sich dann irgendwann dran setzen können, ohne mit irgendwie zu viel Hindernissen rumzuschlagen. Ja, das war es so weit eigentlich auch schon von mir. Hat irgendjemand noch Fragen um die ganzen Anregungen oder sonstiges? Ich habe den Anfang der Frage nicht verstanden gerade. Ja, das gibt ja. Das sind ja manchmal schüchelige Werte. Ja. Ich sag ja etwas, ich komme gleich in die Beschreibung eigentlich, damit ich ja auch meine Mäle, Mäle fremden auf eine Stelle, in die die Beschreibung existieren, und dann hier runter. Da gibt es Value-Set-Analysis. Das ist ein Ansatz, um dieses Problem zu lösen. Damit versucht man tatsächlich irgendwie Buffer zu rekonstruieren. Das kann man halbwegs... Ja, es ist schwierig, aber es gibt irgendwie Ansätze. Zum Beispiel, wenn man irgendwie merkt, man hat einen Pointer, der plus 200 ein möglicher Wert ist, dass man Element 200 exist, und das ist das Gleiche wie Element 0 von einem anderen Ray zu einem Pointer, dann merkt man schon, der läuft irgendwie was falsch. Aber ja, das ist eins der offenen Probleme. Da gibt es Research in dem Bereich, da gibt es irgendwie Paper zu, es ist teilweise ein Enger implementiert. Aber das ist dann der Punkt, wo es schon komplizierter werden kann. Ja. Wenn ich das richtig verstehe, dann kann man automated binary analysis tools auch als Ergänzung zum Tools für Formale der Defikation ansehen. Also, gibt es da irgendwelchen Burken zwischen, kann man solche Tools miteinander spielen lassen? Gleich. Die Frage war, inwieweit das mit formaler Verifizierung kombiniert werden kann. Im Grunde ist das formale Verifizierung nur rückwärts. Formale Verifizierung funktioniert meistens irgendwie mit Source Code. Gut, dass die alten Slides noch drin haben. LVM macht zum Beispiel, also die Compilerinfrastruktur von LVM macht all diesen Kramen irgendwie schon. Nur halt eben von Source Code zu Binary. Und wenn man eben nur noch die Binary selbst hat, hat man schlicht und einfach irgendwie neue Herausforderungen um darauf formale Verifizierung zu machen. Aber im Grunde ist es formale Verifizierung. Ja, ich denke jetzt in besonderem Fall, wo formale Verifikation extrem schnell sehr teuer wird. Einfach zu teuer wird und unkomfortabel und man an bestimmten Punkten diese formale Verifikation beenden kann und dem Rest quasi über so ordinary Binary analysis hinreichend pragmatisch verarbeiten. Tendenziell ist, das auf Binary-Ebene zu machen teurer als auf Source Code, da da einfach mehr komische Dinge passieren können. Ja, aber ordinary Binary analysis ist offenbar diesmal pragmatisch zu arbeiten. Das Problem ein bisschen runterzubrechen und mir bestimmte Dinge anzugucken und andere nicht. Und bei Formale Verifikation stelle ich mir das ein bisschen schwieriger vor. Ich habe noch nicht so viel mit formaler Verifizierung gemacht, aber ich würde irgendwie davon ausgehen, dass man bei formaler Verifizierung sich da auch nur einzelne Funktionen anschauen kann. Und dann irgendwie prüfen, ob die irgendwie mit anderen Code überhaupt oder mit anderen Speicherbereichen oder mit anderen Teilen des Programms überhaupt interagieren und die dann irgendwie für sich verifizieren. Also, das... Was möchtest du denn damit hinziehen? Also, ich muss so gehen, ich begreife das auch nur als Konzept, diese Formale der Verifikation, ich selber aber auch nicht zu tun. Aber ich habe mir das so vorgestellt, dass ich halt ein Programm die Komplexe, also auch über seinen Markt habe und ich überprüfe ob unter gewissen Bedingungen eben andere Bedingungen erfüllen sind, und zwar mit jeglichen Eingangsdaten. Das... Ja, so, das ist auch das Konzept von Formaler Verifizierung, das ich habe, aber das müsste ja auch auf einzelne Programmteile gehen, dann genauso. Also, die Konzepte sind im Grunde Formaler Verifizierung und Formale Binary Analysis eigentlich zum Großteil exakt die gleichen. Man arbeitet halt einfach nur auf einer anderen Sprache. Und mit einem komplizierteren Modell. Ich wüsste jetzt keinen Grund, warum irgendwie Formaler Verifizierung immer ein komplettes Programm... Also, es gibt auf jeden Fall mehr Sinn, sich ein komplettes Programm anzuschauen, aber einzelne Funktionen verifizieren müsste man genauso können. Und genauso kann man sich dann eben mit Automated Binary Analysis Tools im Respekt des Programms nur anschauen. Was ein Vorteil ist, wenn man irgendwie Sprachen hat, die tatsächlich durch den Compiler laufen, und dann am Ende des Semblers rauskommt, ist, dass Compiler manchmal irgendwelche Dinge optimieren und dadurch das Bugs reinkommen. Das gab man also nicht bei Xen, irgendwelche Bugs, da war der Source Code eigentlich absolut okay, wirkte nicht, als wäre dann Fehler drin. Der Compiler hat dann Dinge optimiert, und dann war da tatsächlich ein Problem im konkreten Assembler. Der Code hätte komplett Formal verifiziert sein können, der C-Code. Wenn der Compiler am Ende ein komisches Zeug macht, können immer noch irgendwie Bugs reinkommen. Die würde man damit dann mitkriegen. Aber tendenziell müsste eigentlich jegliche Analyse auf Binary eben noch mal schwerer sein, da man einfach irgendwie kompliziertere Konzepte hat. Ja. Ja. Ja. Ich glaube, wenn man alles Formal verifiziert, dann wäre ich arbeitslos, weil dann wäre IT Security Research kein Thema mehr. Ja. Ja. Wie heißt der? Konzert. Konzert. Nein, die man sich mal anschauen könnte. Ja. Sonst noch Fragen. Haben wir überhaupt irgendwie ein Mikro hier oder sowas? Dann hat man die Fragen jetzt leider nicht im Stream, oder auf der Aufnahme am Ende. Ja. Ich weiß nicht, ob es hier ein Audioengel oder ein Herald irgendwie gibt, falls überhaupt noch Fragen sind. Ich interpretiere das dann einfach mal als ein Ein- oder als generelle Verwirrung.