<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ReverseEngineering on Rebel Zhangs Blog</title>
        <link>https://rebel1725.codeberg.page/blog/de/tags/reverseengineering/</link>
        <description>Recent content in ReverseEngineering on Rebel Zhangs Blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>de-DE</language>
        <managingEditor>rebel1725@tilde.club (Rebel Zhang)</managingEditor>
        <webMaster>rebel1725@tilde.club (Rebel Zhang)</webMaster>
        <lastBuildDate>Sat, 16 May 2026 19:37:05 +0800</lastBuildDate><atom:link href="https://rebel1725.codeberg.page/blog/de/tags/reverseengineering/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>Eine kurze Einführung in Reverse Engineering</title>
            <link>https://rebel1725.codeberg.page/blog/de/post/a-brief-introduction-into-reverse-engineering/</link>
            <pubDate>Sat, 16 May 2026 19:37:05 +0800</pubDate><author>rebel1725@tilde.club (Rebel Zhang)</author>
            <guid>https://rebel1725.codeberg.page/blog/de/post/a-brief-introduction-into-reverse-engineering/</guid>
            <description>&lt;p&gt;In den vergangenen Wochen habe ich x86_64-Assembler, AArch64-Assembler und verschiedene Grundlagen gelernt. Vor Kurzem hatte ich endlich meine allererste Erfahrung mit Reverse Engineering. Ich freue mich, mehr Einzelheiten über meine Erfahrung zu teilen.&lt;/p&gt;&#xA;&lt;h2 id=&#34;was-ist-reverse-engineering&#34;&gt;Was ist Reverse Engineering&#xA;&lt;/h2&gt;&lt;p&gt;Ein Computer ist in Wirklichkeit eine ziemlich dumme Maschine. Er löst kein Problem von selbst; er führt nur die Lösung aus — genauer gesagt führt er nur die Anweisungen von Menschen nacheinander und in der Reihenfolge aus. Um mit so einer Maschine ein Problem zu lösen, gehen wir typischerweise so vor:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;ein &lt;em&gt;Problem&lt;/em&gt; analysieren;&lt;/li&gt;&#xA;&lt;li&gt;eine &lt;em&gt;Lösung&lt;/em&gt; für dieses Problem finden;&lt;/li&gt;&#xA;&lt;li&gt;die Lösung durch das Entwerfen eines &lt;em&gt;Algorithmus&lt;/em&gt; umsetzen;&lt;/li&gt;&#xA;&lt;li&gt;den Algorithmus durch das Schreiben von &lt;em&gt;Code&lt;/em&gt; umsetzen;&lt;/li&gt;&#xA;&lt;li&gt;aus dem Code durch Kompilieren das &lt;em&gt;ausführbare Programm&lt;/em&gt; erzeugen;&lt;/li&gt;&#xA;&lt;li&gt;dieses ausführbare Programm der dummen Maschine übergeben, damit sie das Problem löst.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Das ist ein Verfahren, bei dem die &lt;em&gt;Lösung&lt;/em&gt; in &lt;em&gt;Code&lt;/em&gt; und dann in &lt;em&gt;Anweisungen&lt;/em&gt; umgewandelt wird.&lt;/p&gt;&#xA;&lt;p&gt;Gewöhnlich können wir leicht verstehen, was ein Programm tut, indem wir den Code lesen. Das ist der Mittelweg zwischen der Lösung und den Anweisungen. In den 1970er Jahren wurde Code meist unter Menschen geteilt, damit jede Person Lösungen innerhalb der Gemeinschaft studieren, verbessern und weitergeben konnte.&lt;/p&gt;&#xA;&lt;p&gt;Seit dem Auftauchen dieser äusserst bösen Monster der Entwicklung proprietärer Software hat sich das jedoch geändert. Sie verweigern dem Volk den Zugang zum &lt;em&gt;Code&lt;/em&gt;, nehmen den Nutzerinnen und Nutzern die Kontrolle über ihre Rechner ab, bauen allerlei bösartige Funktionen ein, um ihre Nutzer auszubeuten, und nutzen alle möglichen Methoden, um zu verbergen, was ihre Programme tun — die &lt;em&gt;Lösung&lt;/em&gt;. Sie errichten Barrieren. Sie zerstören das Teilen. Sie schaffen Katastrophen und Verzweiflung. Sie tun alles gegen das Volk, die Gemeinschaft und die Menschheit. All das tun sie für &lt;strong&gt;ihre unethischen und ungerechten Profite&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Software sollte dem Volk dienen, nicht den unethischen und ungerechten Profiten der Kapitalisten. Doch gerade diese unethischen und ungerechten Entwickler proprietärer Software, die niemals existieren sollten und aus der Welt ausgemerzt werden müssten, machen die Welt schlechter. Aber wir können proprietäre Software nicht auf einmal beseitigen. &lt;strong&gt;Wir entwickeln dafür freie Ersatzlösungen und nehmen die Kontrolle über das Rechnen schrittweise zurück.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Deshalb ist es an der Zeit, das oben beschriebene Verfahren in umgekehrter Reihenfolge durchzuführen. Mit anderen Worten:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;wir analysieren das &lt;em&gt;ausführbare Programm&lt;/em&gt;;&lt;/li&gt;&#xA;&lt;li&gt;wir versuchen, den &lt;em&gt;Code&lt;/em&gt; wiederherzustellen, den das ausführbare Programm ausführt;&lt;/li&gt;&#xA;&lt;li&gt;wir finden den &lt;em&gt;Algorithmus&lt;/em&gt;, den der Code umsetzt;&lt;/li&gt;&#xA;&lt;li&gt;wir kehren die &lt;em&gt;Lösung&lt;/em&gt; um, die der Algorithmus umsetzt;&lt;/li&gt;&#xA;&lt;li&gt;wir implementieren die Lösung neu, um das &lt;em&gt;Problem&lt;/em&gt; zu lösen.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;Das ist die Umwandlung von &lt;em&gt;Anweisungen&lt;/em&gt; in &lt;em&gt;Code&lt;/em&gt; und dann in &lt;em&gt;Lösung&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Die Praxis, so etwas in umgekehrter Reihenfolge zu tun, heisst &lt;strong&gt;Reverse Engineering&lt;/strong&gt;, auch &lt;strong&gt;RE&lt;/strong&gt; genannt.&lt;/p&gt;&#xA;&lt;h2 id=&#34;warum-reverse-engineering-wichtig-ist&#34;&gt;Warum Reverse Engineering wichtig ist&#xA;&lt;/h2&gt;&lt;p&gt;Der wichtigste Grund, warum Reverse Engineering wichtig ist, besteht darin, dass es uns hilft, &lt;strong&gt;freie Betriebssysteme&lt;/strong&gt; wie postmarketOS und LineageOS auf verschiedene Geräte zu portieren. Das ist auch der Hauptgrund, warum ich Reverse Engineering lerne.&lt;/p&gt;&#xA;&lt;p&gt;Auf Desktop-PCs ist Softwarefreiheit viel leichter zu erreichen, da wir normalerweise alle möglichen freien GNU/Linux-Distributionen ohne ernsthafte Probleme ausführen können. Auf Mobilgeräten wie Smartphones oder Tablets ist das jedoch nicht der Fall.&lt;/p&gt;&#xA;&lt;p&gt;Diese Geräte haben oft viele herstellerspezifische und gerätespezifische Anpassungen, und es ist fast unmöglich, eine Einheitslösung zu finden, um ein freies Betriebssystem universell auf solchen Geräten zum Laufen zu bringen. Noch schlimmer ist, dass die Hersteller diese technischen Details zu verbergen wählen, und selbst Google hat sich entschieden, die Gerätetrees und Hersteller-Binärblobs nicht mehr zu veröffentlichen. Daher müssen wir jetzt die &lt;em&gt;binären&lt;/em&gt; Firmware-Blobs analysieren und ihre Logik ableiten, um postmarketOS, LineageOS oder andere freie Betriebssysteme auf diesen Geräten zum Laufen zu bringen.&lt;/p&gt;&#xA;&lt;p&gt;Tatsächlich arbeitet auch die Free Software Foundation unter dem Projekt &lt;a class=&#34;link&#34; href=&#34;https://librephone.org/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Librephone&lt;/a&gt; an diesem Problem. Dieses Projekt kann ohne Reverse Engineering nicht fortgeführt werden.&lt;/p&gt;&#xA;&lt;p&gt;Die Anwendung von Reverse Engineering ist allerdings keineswegs auf das Portieren freier Betriebssysteme beschränkt. Es ist auch von grosser Bedeutung für:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;das Herausfinden der Einzelheiten proprietärer Protokolle,&lt;/li&gt;&#xA;&lt;li&gt;das Finden von Beweisen für bösartige Funktionen in proprietärer Software,&lt;/li&gt;&#xA;&lt;li&gt;das Umgehen von Digital Restrictions Management,&lt;/li&gt;&#xA;&lt;li&gt;und vieles mehr.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;wie-man-reverse-engineering-macht&#34;&gt;Wie man Reverse Engineering macht&#xA;&lt;/h2&gt;&lt;p&gt;Typisches Reverse Engineering besteht aus diesen zwei Schritten:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Das ausführbare Programm in Assembler umwandeln; dieser Prozess heisst &lt;em&gt;Disassemblieren&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Durch das Lesen des Assemblercodes versuchen, die Ideen wiederherzustellen oder gleichwertigen Code neu zu schreiben.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;Wir können dies direkt am ausführbaren Programm tun, ohne es auszuführen; das nennt man &lt;em&gt;statische Analyse&lt;/em&gt;, oder während es ausgeführt wird; das nennt man &lt;em&gt;dynamische Analyse&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;beispiel-für-statische-analyse&#34;&gt;Beispiel für statische Analyse&#xA;&lt;/h2&gt;&lt;p&gt;Nehmen wir dieses Beispiel:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;&#xA;#include &amp;lt;string.h&amp;gt;&#xA;&#xA;const char *PASS = &amp;#34;weakpasswd&amp;#34;;&#xA;&#xA;int main()&#xA;{&#xA;    char buf[64];&#xA;    for (int i = 0; i &amp;lt; 3; i++)&#xA;    {&#xA;        printf(&amp;#34;%d verbleibende Versuche.\nPasswort: &amp;#34;, 3 - i);&#xA;        fgets(buf, sizeof buf, stdin);&#xA;        if (strncmp(PASS, buf, strlen(PASS)))&#xA;            puts(&amp;#34;Falsches Passwort!\n&amp;#34;);&#xA;        else&#xA;        {&#xA;            puts(&amp;#34;Richtig!\nWillkommen in der Welt &amp;#34;&#xA;                 &amp;#34;des Reverse Engineering!\n&amp;#34;);&#xA;            break;&#xA;        }&#xA;    }&#xA;    return 0;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Kompilieren Sie dies mit GCC:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ gcc -o ./demo ./demo.c&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Öffnen Sie das ausführbare Programm in radare2:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ r2 ./demo&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sie werden dies sehen:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[0x00000800]&amp;gt; &#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Analysieren wir das ausführbare Programm:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[0x00000800]&amp;gt; aaa&#xA;INFO: Analyze all flags starting with sym. and entry0 (aa)&#xA;INFO: Analyze imports (af@@@i)&#xA;INFO: Analyze entrypoint (af@ entry0)&#xA;INFO: Analyze symbols (af@@@s)&#xA;INFO: Analyze all functions arguments/locals (afva@@F)&#xA;INFO: Analyze function calls (aac)&#xA;INFO: Analyze len bytes of instructions for references (aar)&#xA;INFO: Finding and parsing C++ vtables (avrr)&#xA;INFO: Analyzing methods (af @@ method.*)&#xA;INFO: Finding function preludes (aap)&#xA;INFO: Emulate functions to find computed references (aaef)&#xA;INFO: Recovering local variables (afva@@@F)&#xA;INFO: Type matching analysis for all functions (afft)&#xA;INFO: Propagate noreturn information (aanr)&#xA;INFO: Use -AA or aaaa to perform additional experimental analysis&#xA;INFO: Finding xrefs in noncode sections (e anal.in=io.maps.x; aav)&#xA;WARN: Skipping aav because base address is zero. Use -B 0x800000 or aav0&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Listen wir die Funktionen auf:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[0x00000800]&amp;gt; afl&#xA;0x00000750    1     16 sym.imp.strlen&#xA;0x00000760    1     16 sym.imp.__libc_start_main&#xA;0x00000770    1     16 sym.imp.__cxa_finalize&#xA;0x00000780    1     32 sym.imp.strncmp&#xA;0x000007a0    1     16 sym.imp.abort&#xA;0x000007b0    1     16 sym.imp.puts&#xA;0x000007c0    1     16 sym.imp.printf&#xA;0x000007d0    1     20 sym.imp.fgets&#xA;0x00000800    1     48 entry0&#xA;0x00000834    3     20 sym.call_weak_fn&#xA;0x00000860    4     48 sym.deregister_tm_clones&#xA;0x00000890    4     60 sym.register_tm_clones&#xA;0x000008cc    5     80 entry.fini0&#xA;0x00000920    1      8 entry.init0&#xA;0x000009f8    1     24 sym._fini&#xA;0x00000928    7    208 main&#xA;0x00000708    1     28 sym._init&#xA;0x00000730    1     32 fcn.00000730&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Jetzt können wir in die Funktion &lt;code&gt;main&lt;/code&gt; springen und den Disassemblierungscode ausgeben:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[0x00000800]&amp;gt; s main&#xA;[0x00000928]&amp;gt; pdf&#xA;            ; DATA XREF from entry0 @ 0x820(r)&#xA;            ; DATA XREF from entry.fini0 @ 0x8e0(r)&#xA;┌ 208: int main (int argc);&#xA;│ `- args(x0) vars(5:sp[0x4..0x70])&#xA;│           0x00000928      fd7bb9a9       stp x29, x30, [sp, -0x70]!&#xA;│           0x0000092c      fd030091       mov x29, sp&#xA;│           0x00000930      f30b00f9       str x19, [var_10h]&#xA;│           0x00000934      ff6f00b9       str wzr, [var_6ch]          ; argc&#xA;│       ┌─&amp;lt; 0x00000938      29000014       b 0x9dc&#xA;│       │   ; CODE XREF from main @ 0x9e4(x)&#xA;│      ┌──&amp;gt; 0x0000093c      61008052       mov w1, 3&#xA;│      ╎│   0x00000940      e06f40b9       ldr w0, [var_6ch]&#xA;│      ╎│   0x00000944      2000004b       sub w0, w1, w0&#xA;│      ╎│   0x00000948      e103002a       mov w1, w0&#xA;│      ╎│   0x0000094c      00000090       adrp x0, 0&#xA;│      ╎│   0x00000950      00a02891       add x0, x0, str._d_retries_remaining._nPassword: ; 0xa28 ; &amp;#34;%d verbleibende Versuche.\nPasswort: &amp;#34; ; const char *format&#xA;│      ╎│   0x00000954      9bffff97       bl sym.imp.printf           ; int printf(const char *format)&#xA;│      ╎│   0x00000958      e00000f0       adrp x0, 0x1f000&#xA;│      ╎│   0x0000095c      00e447f9       ldr x0, [x0, 0xfc8]         ; [0x1ffc8:4]=0&#xA;│      ╎│                                                              ; reloc.stdin&#xA;│      ╎│   0x00000960      010040f9       ldr x1, [x0]                ; int size&#xA;│      ╎│   0x00000964      e0a30091       add x0, sp, 0x28            ; char *s&#xA;│      ╎│   0x00000968      e20301aa       mov x2, x1                  ; FILE *stream&#xA;│      ╎│   0x0000096c      01088052       mov w1, 0x40&#xA;│      ╎│   0x00000970      98ffff97       bl sym.imp.fgets            ; char *fgets(char *s, int size, FILE *stream)&#xA;│      ╎│   0x00000974      00010090       adrp x0, reloc.strlen       ; 0x20000&#xA;│      ╎│   0x00000978      00600191       add x0, x0, 0x58&#xA;│      ╎│   0x0000097c      130040f9       ldr x19, [x0]               ; [0xa18:4]=0x6b616577 ; &amp;#34;weakpasswd&amp;#34;&#xA;│      ╎│   0x00000980      00010090       adrp x0, reloc.strlen       ; 0x20000&#xA;│      ╎│   0x00000984      00600191       add x0, x0, 0x58&#xA;│      ╎│   0x00000988      000040f9       ldr x0, [x0]                ; [0xa18:4]=0x6b616577 ; &amp;#34;weakpasswd&amp;#34; ; const char *s&#xA;│      ╎│   0x0000098c      71ffff97       bl sym.imp.strlen           ; size_t strlen(const char *s)&#xA;│      ╎│   0x00000990      e10300aa       mov x1, x0&#xA;│      ╎│   0x00000994      e0a30091       add x0, sp, 0x28&#xA;│      ╎│   0x00000998      e20301aa       mov x2, x1                  ; size_t n&#xA;│      ╎│   0x0000099c      e10300aa       mov x1, x0                  ; const char *s2&#xA;│      ╎│   0x000009a0      e00313aa       mov x0, x19                 ; const char *s1&#xA;│      ╎│   0x000009a4      77ffff97       bl sym.imp.strncmp          ; int strncmp(const char *s1, const char *s2, size_t n)&#xA;│      ╎│   0x000009a8      1f000071       cmp w0, 0&#xA;│     ┌───&amp;lt; 0x000009ac      a0000054       b.eq 0x9c0&#xA;│     │╎│   0x000009b0      00000090       adrp x0, 0&#xA;│     │╎│   0x000009b4      00402991       add x0, x0, str.Wrong_password__n ; 0xa50 ; &amp;#34;Falsches Passwort!\n&amp;#34; ; const char *s&#xA;│     │╎│   0x000009b8      7effff97       bl sym.imp.puts             ; int puts(const char *s)&#xA;│    ┌────&amp;lt; 0x000009bc      05000014       b 0x9d0&#xA;│    ││╎│   ; CODE XREF from main @ 0x9ac(x)&#xA;│    │└───&amp;gt; 0x000009c0      00000090       adrp x0, 0&#xA;│    │ ╎│   0x000009c4      00a02991       add x0, x0, str.Correct__nWelcome_to_the_world_of_reverse_engineering__n ; 0xa68 ; &amp;#34;Richtig!\nWillkommen in der Welt des Reverse Engineering!\n&amp;#34; ; const char *s&#xA;│    │ ╎│   0x000009c8      7affff97       bl sym.imp.puts             ; int puts(const char *s)&#xA;│    │┌───&amp;lt; 0x000009cc      07000014       b 0x9e8&#xA;│    ││╎│   ; CODE XREF from main @ 0x9bc(x)&#xA;│    └────&amp;gt; 0x000009d0      e06f40b9       ldr w0, [var_6ch]&#xA;│     │╎│   0x000009d4      00040011       add w0, w0, 1&#xA;│     │╎│   0x000009d8      e06f00b9       str w0, [var_6ch]&#xA;│     │╎│   ; CODE XREF from main @ 0x938(x)&#xA;│     │╎└─&amp;gt; 0x000009dc      e06f40b9       ldr w0, [var_6ch]&#xA;│     │╎    0x000009e0      1f080071       cmp w0, 2&#xA;│     │└──&amp;lt; 0x000009e4      cdfaff54       b.le 0x93c&#xA;│     │     ; CODE XREF from main @ 0x9cc(x)&#xA;│     └───&amp;gt; 0x000009e8      00008052       mov w0, 0&#xA;│           0x000009ec      f30b40f9       ldr x19, [var_10h]&#xA;│           0x000009f0      fd7bc7a8       ldp x29, x30, [sp], 0x70&#xA;└           0x000009f4      c0035fd6       ret&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Radare2 liefert uns nicht nur den Assemblercode, sondern auch die jeweilige Adresse jeder Anweisung und die Aufrufbeziehungen zwischen ihnen.&lt;/p&gt;&#xA;&lt;p&gt;Es ist jedoch noch nicht besonders klar. Zeigen wir also die Disassemblierung als Graph an:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[0x00000928]&amp;gt; VV&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nun werden Sie dies sehen:&lt;/p&gt;&#xA;&lt;p&gt;&lt;img src=&#34;https://rebel1725.codeberg.page/blog/pic_20260516_001_8405216816178737815.png&#34;&#xA;&#x9;width=&#34;1572&#34;&#xA;&#x9;height=&#34;2358&#34;&#xA;&#x9;loading=&#34;lazy&#34;&#xA;&#x9;&#xA;&#x9;&#x9;alt=&#34;Graphansicht der radare2-Disassemblierung&#34;&#xA;&#x9;&#xA;&#x9;&#xA;&#x9;&#x9;class=&#34;gallery-image&#34; &#xA;&#x9;&#x9;data-flex-grow=&#34;66&#34;&#xA;&#x9;&#x9;data-flex-basis=&#34;160px&#34;&#xA;&#x9;&#xA;&gt;&lt;/p&gt;&#xA;&lt;p&gt;Im Graphen bedeutet &lt;code&gt;t&lt;/code&gt;, dass bei wahrer Bedingung gesprungen wird. &lt;code&gt;f&lt;/code&gt; bedeutet, dass bei falscher Bedingung gesprungen wird. &lt;code&gt;v&lt;/code&gt; bedeutet einen unbedingten Sprung.&lt;/p&gt;&#xA;&lt;p&gt;Jetzt wird das Leben viel einfacher. Radare2 hat die logischen Beziehungen zwischen den Assemblercode-Blöcken bereits herausgefunden, und Sie müssen nur noch die Assemblercode-Blöcke selbst verstehen.&lt;/p&gt;&#xA;&lt;p&gt;Im Graphen können wir die Logik der Passwortprüfung am Ende des &lt;code&gt;[0x93c]&lt;/code&gt;-Blocks leicht finden:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;; int strncmp(const char *s1, const char *s2, size_t n)&#xA;bl sym.imp.strncmp&#xA;cmp w0, 0&#xA;b.eq 0x9c0&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hier wird &lt;code&gt;sym.imp.strncmp&lt;/code&gt; aufgerufen, und dann wird der von dieser Funktion in &lt;code&gt;w0&lt;/code&gt; zurückgegebene Wert überprüft. Wenn er gleich &lt;code&gt;0&lt;/code&gt; ist, springt das Programm zum &lt;code&gt;[0x9c0]&lt;/code&gt;-Block und signalisiert Erfolg. Andernfalls geht es zum &lt;code&gt;[0x9b0]&lt;/code&gt;-Block weiter, was einen Fehler bedeutet.&lt;/p&gt;&#xA;&lt;p&gt;Gemäss der AArch64-Aufrufkonvention befinden sich die Zeiger auf die beiden zu vergleichenden Zeichenketten in den Registern &lt;code&gt;x0&lt;/code&gt; und &lt;code&gt;x1&lt;/code&gt;; dann rufen wir &lt;code&gt;sym.imp.strncmp&lt;/code&gt; auf. Das Ergebnis wird anschliessend im Register &lt;code&gt;x0&lt;/code&gt; gespeichert (&lt;code&gt;w0&lt;/code&gt; ist einfach die untere Hälfte von &lt;code&gt;x0&lt;/code&gt;), und wenn die beiden Zeichenketten identisch sind, erhalten wir &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Nun schauen wir zurück und sehen:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;mov x0, x19&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Dann schauen wir weiter zurück, um die Anweisung zu finden, die &lt;code&gt;x19&lt;/code&gt; bearbeitet:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;; [0xa18:4]=0x6b616577&#xA;; &amp;#34;weakpasswd&amp;#34;&#xA;ldr x19, [x0]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Radare2 hat uns das Top-Secret bereits verraten.&lt;/strong&gt; Probieren wir es aus:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ./demo&#xA;3 verbleibende Versuche.&#xA;Passwort: weakpasswd&#xA;Richtig!&#xA;Willkommen in der Welt des Reverse Engineering!&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Das ist alles.&lt;/p&gt;&#xA;&lt;p&gt;Wenn wir den Graphen genauer betrachten, können wir mehr als nur das Passwort lernen. Zum Beispiel sehen wir im Graphen einen Zyklus: &lt;code&gt;[0x9dc]&lt;/code&gt; -&amp;gt; &lt;code&gt;[0x93c]&lt;/code&gt; -&amp;gt; &lt;code&gt;[0x9b0]&lt;/code&gt; -&amp;gt; &lt;code&gt;[0x9d0]&lt;/code&gt; -&amp;gt; &lt;code&gt;[0x9dc]&lt;/code&gt;. &lt;strong&gt;In einem Graphen deutet ein Zyklus oft auf Schleifensteuerfluss hin. Das ist eine sehr wichtige Denkweise beim Reverse Engineering.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Im &lt;code&gt;[0x9dc]&lt;/code&gt;-Block:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ldr w0, [var_6ch]&#xA;cmp w0, 2&#xA;b.le 0x93c&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Er lädt die Variable &lt;code&gt;var_6ch&lt;/code&gt; in ein Register und vergleicht sie mit &lt;code&gt;2&lt;/code&gt;. Wenn sie grösser ist, geht es zum &lt;code&gt;[0x9e8]&lt;/code&gt;-Block und das Programm endet. Andernfalls geht es zum &lt;code&gt;[0x93c]&lt;/code&gt;-Block.&lt;/p&gt;&#xA;&lt;p&gt;Schauen Sie sich das Ende des &lt;code&gt;[0x93c]&lt;/code&gt;-Blocks an. Wir wissen bereits, dass das Programm bei erfolgreicher Passwortprüfung zum &lt;code&gt;[0x9c0]&lt;/code&gt;-Block springt, die Erfolgsnachricht ausgibt und dann zum &lt;code&gt;[0x9e8]&lt;/code&gt;-Block springt, der das Programm beendet.&lt;/p&gt;&#xA;&lt;p&gt;Aber was geschieht, wenn die Prüfung fehlschlägt? Dann geht es zum &lt;code&gt;[0x9b0]&lt;/code&gt;-Block. Dort wird einfach die Fehlermeldung ausgegeben, also ist hier nichts Interessantes; gehen wir also zum &lt;code&gt;[0x9d0]&lt;/code&gt;-Block weiter. Dort finden wir etwas Interessantes:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;ldr w0, [var_6ch]&#xA;add w0, w0, 1&#xA;str w0, [var_6ch]&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Er lädt die Variable &lt;code&gt;var_6ch&lt;/code&gt; in ein Register, erhöht den Wert um eins und speichert den Wert wieder zurück. Dann springt er zurück zum &lt;code&gt;[0x9dc]&lt;/code&gt;-Block, der den Wert von &lt;code&gt;var_6ch&lt;/code&gt; überprüft.&lt;/p&gt;&#xA;&lt;p&gt;Drücken Sie zweimal &lt;code&gt;q&lt;/code&gt;, um zur Eingabeaufforderung zurückzukehren. Führen Sie &lt;code&gt;afv&lt;/code&gt; und &lt;code&gt;afvd&lt;/code&gt; aus, um die Variablen und ihre Informationen aufzulisten:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[0x000009b0]&amp;gt; afv&#xA;arg int argc @ x0&#xA;var int64_t var_70h @ sp+0x0&#xA;var int64_t var_70h_2 @ sp+0x8&#xA;var int64_t var_10h @ sp+0x10&#xA;var char * s2 @ sp+0x28&#xA;var int64_t var_6ch @ sp+0x6c&#xA;[0x000009b0]&amp;gt; afvd&#xA;arg argc = 0x00000000 0x00010102464c457f   .ELF.... @ pstate&#xA;var var_6ch = 0x0017806c = (qword)0x0000000000000000&#xA;var s2 = 0x00178028 = &amp;#34;&amp;#34;&#xA;var var_10h = 0x00178010 = (qword)0x0000000000000000&#xA;var var_70h = 0x00178000 = (qword)0x0000000000000000&#xA;var var_70h_2 = 0x00178008 = (qword)0x0000000000000000&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Wir sehen, dass der Wert &lt;code&gt;var_6ch&lt;/code&gt; ein &lt;code&gt;int64_t&lt;/code&gt; ist und sein Anfangswert &lt;code&gt;0&lt;/code&gt; beträgt.&lt;/p&gt;&#xA;&lt;p&gt;Nun können wir schliessen, dass &lt;code&gt;var_6ch&lt;/code&gt; ein Zähler ist. Sein Anfangswert ist 0. Wenn die Passwortprüfung fehlschlägt, wird der Zähler um eins erhöht. Sobald er grösser als 2 ist, fragt das Programm nicht mehr nach dem Passwort und beendet sich. Nun können wir sagen, dass dies unserem C-Code &lt;code&gt;for (int i = 0; i &amp;lt; 3; i++)&lt;/code&gt; entspricht!&lt;/p&gt;&#xA;&lt;p&gt;Das ist jedoch ein sehr einfaches Beispiel. In der realen Welt wird es schwieriger, da Entwickler proprietärer Software oft Anti-Analyse- und Anti-Debugging-Techniken einsetzen.&lt;/p&gt;&#xA;&lt;h2 id=&#34;compileroptimierungen-verstehen&#34;&gt;Compileroptimierungen verstehen&#xA;&lt;/h2&gt;&lt;p&gt;Kompilieren wir nun diesen noch einfacheren Code:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;#include &amp;lt;stdio.h&amp;gt;&#xA;&#xA;int main()&#xA;{&#xA;    int a;&#xA;    scanf(&amp;#34;%d&amp;#34;, &amp;amp;a);&#xA;    printf(&amp;#34;%d&amp;#34;, a % 65536);&#xA;    return 0;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Er liest eine ganze Zahl aus der Eingabe und gibt ihr Modulo 65536 aus.&lt;/p&gt;&#xA;&lt;p&gt;Disassemblieren wir seine &lt;code&gt;main&lt;/code&gt;-Funktion:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;[0x00000700]&amp;gt; aaa&#xA;INFO: Analyze all flags starting with sym. and entry0 (aa)&#xA;......&#xA;WARN: Skipping aav because base address is zero. Use -B 0x800000 or aav0&#xA;[0x00000700]&amp;gt; s main&#xA;[0x00000828]&amp;gt; pdf&#xA;            ; DATA XREF from entry0 @ 0x720(r)&#xA;            ; DATA XREF from entry.fini0 @ 0x7e0(r)&#xA;┌ 76: int main (int argc, char **argv, char **envp);&#xA;│ afv: vars(3:sp[0x4..0x20])&#xA;│           0x00000828      fd7bbea9       stp x29, x30, [sp, -0x20]!&#xA;│           0x0000082c      fd030091       mov x29, sp&#xA;│           0x00000830      e0730091       add x0, sp, 0x1c&#xA;│           0x00000834      e10300aa       mov x1, x0&#xA;│           0x00000838      00000090       adrp x0, 0&#xA;│           0x0000083c      00602291       add x0, x0, 0x898&#xA;│           0x00000840      94ffff97       bl sym.imp.__isoc23_scanf&#xA;│           0x00000844      e01f40b9       ldr w0, [var_1ch]&#xA;│           0x00000848      e103006b       negs w1, w0&#xA;│           0x0000084c      003c0012       and w0, w0, 0xffff&#xA;│           0x00000850      213c0012       and w1, w1, 0xffff&#xA;│           0x00000854      0044815a       csneg w0, w0, w1, mi&#xA;│           0x00000858      e103002a       mov w1, w0&#xA;│           0x0000085c      00000090       adrp x0, 0&#xA;│           0x00000860      00602291       add x0, x0, 0x898           ; const char *format&#xA;│           0x00000864      97ffff97       bl sym.imp.printf           ; int printf(const char *format)&#xA;│           0x00000868      00008052       mov w0, 0&#xA;│           0x0000086c      fd7bc2a8       ldp x29, x30, [sp], 0x20&#xA;└           0x00000870      c0035fd6       ret&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Sie könnten verwirrt sein, weil Sie hier nichts über eine Modulo-Operation sehen (&lt;code&gt;sdiv&lt;/code&gt;, &lt;code&gt;mul&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt; usw.). Stattdessen sehen Sie:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;and w0, w0, 0xffff&#xA;and w1, w1, 0xffff&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Das liegt daran, dass moderne Compiler klug genug sind, gewisse spezielle Berechnungsmuster zu erkennen und sie in einfachere Anweisungen zu optimieren. 65536 ist 2^16, also ist die binäre Zahl modulo 65536 einfach ihre niederwertigsten 16 Bits. Eine bitweise Operation reicht aus; es sind keine Addierer oder Multiplikatoren nötig.&lt;/p&gt;&#xA;&lt;p&gt;Wenn wir 65536 jedoch durch etwas anderes ersetzen, zum Beispiel 50000, ist das nicht mehr der Fall:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;            ; DATA XREF from entry0 @ 0x720(r)&#xA;            ; DATA XREF from entry.fini0 @ 0x7e0(r)&#xA;┌ 80: int main (int argc, char **argv, char **envp);&#xA;│ afv: vars(3:sp[0x4..0x20])&#xA;│           0x00000828      fd7bbea9       stp x29, x30, [sp, -0x20]!&#xA;│           0x0000082c      fd030091       mov x29, sp&#xA;│           0x00000830      e0730091       add x0, sp, 0x1c&#xA;│           0x00000834      e10300aa       mov x1, x0&#xA;│           0x00000838      00000090       adrp x0, 0&#xA;│           0x0000083c      00602291       add x0, x0, 0x898&#xA;│           0x00000840      94ffff97       bl sym.imp.__isoc23_scanf&#xA;│           0x00000844      e01f40b9       ldr w0, [var_1ch]&#xA;│           0x00000848      016a9852       mov w1, 0xc350&#xA;│           0x0000084c      020cc11a       sdiv w2, w0, w1&#xA;│           0x00000850      016a9852       mov w1, 0xc350&#xA;│           0x00000854      417c011b       mul w1, w2, w1&#xA;│           0x00000858      0000014b       sub w0, w0, w1&#xA;│           0x0000085c      e103002a       mov w1, w0&#xA;│           0x00000860      00000090       adrp x0, 0&#xA;│           0x00000864      00602291       add x0, x0, 0x898           ; const char *format&#xA;│           0x00000868      96ffff97       bl sym.imp.printf           ; int printf(const char *format)&#xA;│           0x0000086c      00008052       mov w0, 0&#xA;│           0x00000870      fd7bc2a8       ldp x29, x30, [sp], 0x20&#xA;└           0x00000874      c0035fd6       ret&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Nun sehen wir die Anweisungen &lt;code&gt;sdiv&lt;/code&gt;, &lt;code&gt;mul&lt;/code&gt; und &lt;code&gt;sub&lt;/code&gt;, die zusammen die Modulo-Operation ausführen.&lt;/p&gt;&#xA;&lt;h2 id=&#34;und-was-ist-mit-dynamischer-analyse&#34;&gt;Und was ist mit dynamischer Analyse?&#xA;&lt;/h2&gt;&lt;p&gt;Für die dynamische Analyse benötigen Sie GDB. Mit GDB können Sie Disassemblierung anzeigen, Haltepunkte setzen, Speicher und Register untersuchen und sogar die Werte von Registern und Variablen ändern, während das Programm läuft.&lt;/p&gt;&#xA;&lt;p&gt;Ich habe die dynamische Analyse bisher noch nicht im Detail untersucht. Vielleicht schreibe ich in Zukunft einen neuen Blogbeitrag, um die dynamische Analyse zu erklären.&lt;/p&gt;&#xA;&lt;h2 id=&#34;empfohlene-ressourcen&#34;&gt;Empfohlene Ressourcen&#xA;&lt;/h2&gt;&lt;p&gt;Was ich hier erklärt habe, ist nur die Spitze des Eisbergs. Um Reverse Engineering zu lernen, müssen Sie tiefer gehen, mehr üben und durch Tun lernen. Nachfolgend eine Liste empfohlener Ressourcen:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://pwn.college/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Pwn.college&lt;/a&gt; ist ein guter Ort für den Einstieg. Sie können dort im Einführungs-Dojo x86_64-Assembler lernen und anschliessend im Modul Intro to Cybersecurity zum Bereich Reverse Engineering übergehen.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;Reverse Engineering for Beginners&lt;/em&gt; ist ein sehr gutes Buch über Reverse Engineering. Es ist frei (im Sinne von Freiheit), unter der Lizenz CC BY-SA 4.0 veröffentlicht und &lt;a class=&#34;link&#34; href=&#34;https://github.com/Cactus-proj/RE-for-Beginners&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;hier&lt;/a&gt; zum Herunterladen verfügbar.&lt;/li&gt;&#xA;&lt;li&gt;ARM bietet &lt;a class=&#34;link&#34; href=&#34;https://www.arm.com/architecture/learn-the-architecture&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;offizielle Dokumentation&lt;/a&gt; für seine AArch64-Architektur an.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://crackmes.one/&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Hier&lt;/a&gt; können Sie &lt;em&gt;Crackmes&lt;/em&gt; herunterladen, um Ihre Fähigkeiten im Reverse Engineering zu üben.&lt;/li&gt;&#xA;&lt;li&gt;&lt;a class=&#34;link&#34; href=&#34;https://github.com/mytechnotalent/Reverse-Engineering&#34;  target=&#34;_blank&#34; rel=&#34;noopener&#34;&#xA;    &gt;Hier&lt;/a&gt; ist ein GitHub-Repository mit vielen Ressourcen zum Erlernen von Reverse Engineering.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;haftungsausschluss&#34;&gt;Haftungsausschluss&#xA;&lt;/h2&gt;&lt;p&gt;Dieser Artikel dient nur zu Bildungszwecken. Bitte konsultieren Sie Ihre örtlichen Gesetze, um zu erfahren, was Sie nicht tun dürfen. Ich bin nicht für irgendwelche Ihrer Handlungen verantwortlich.&lt;/p&gt;&#xA;</description>
        </item></channel>
</rss>
