<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>Radare2 on Rebel Zhang&#39;s Blog</title>
        <link>https://rebel1725.codeberg.page/blog/en/tags/radare2/</link>
        <description>Recent content in Radare2 on Rebel Zhang&#39;s Blog</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>en-GB</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/en/tags/radare2/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>A Brief Introduction to Reverse Engineering</title>
            <link>https://rebel1725.codeberg.page/blog/en/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/en/post/a-brief-introduction-into-reverse-engineering/</guid>
            <description>&lt;p&gt;Over the past several weeks, I have been learning x86_64 assembly, AArch64 assembly, and various basics. Recently, I finally had my very first experience with reverse engineering. I am glad to share more details about my experience.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-is-reverse-engineering&#34;&gt;What is reverse engineering&#xA;&lt;/h2&gt;&lt;p&gt;A computer is really a rather dumb machine. It will not solve any problem on its own; it only executes the solution — specifically, it only executes instructions from humans one by one in order. To solve a problem with such a machine, we typically:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;analyse a &lt;em&gt;problem&lt;/em&gt;;&lt;/li&gt;&#xA;&lt;li&gt;find a &lt;em&gt;solution&lt;/em&gt; for that problem;&lt;/li&gt;&#xA;&lt;li&gt;implement the solution by designing an &lt;em&gt;algorithm&lt;/em&gt;;&lt;/li&gt;&#xA;&lt;li&gt;implement the algorithm by writing &lt;em&gt;code&lt;/em&gt;;&lt;/li&gt;&#xA;&lt;li&gt;produce the &lt;em&gt;executable&lt;/em&gt; from the code by compiling;&lt;/li&gt;&#xA;&lt;li&gt;feed that executable to that dumb computer to solve the problem.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This is a procedure to convert the &lt;em&gt;solution&lt;/em&gt; into &lt;em&gt;code&lt;/em&gt;, and then into &lt;em&gt;instructions&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Usually, we can easily understand what a program does by reading the code. This is the middle ground between the solution and the instructions. In the 1970s, code was usually shared among people, so everyone could study, improve, and share solutions within the community.&lt;/p&gt;&#xA;&lt;p&gt;However, things have changed since the emergence of those extremely evil proprietary software development monsters. They choose to deny the people access to the &lt;em&gt;code&lt;/em&gt;, take away users&amp;rsquo; control over their computing, implement all kinds of malicious functionality to exploit their users, and use all kinds of approaches to hide what their programs do — the &lt;em&gt;solution&lt;/em&gt;. They are building barriers. They are ruining sharing. They are creating disasters and despair. They do everything against the people, the community, and humanity. They do all this for &lt;strong&gt;their unethical and unjust profits&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Software should serve the people, not the capitalists&amp;rsquo; unethical and unjust profits. However, it is those unethical and unjust proprietary software developers, who should never exist and should be expunged from the world, that are making the world worse. But we cannot eliminate proprietary software all at once. &lt;strong&gt;We develop free software replacements for that, and take back control over computing gradually.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;Therefore, it is time to do the procedure above in reverse order. In other words, we:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;analyse the &lt;em&gt;executable&lt;/em&gt;;&lt;/li&gt;&#xA;&lt;li&gt;try to recover the &lt;em&gt;code&lt;/em&gt; that does what the executable does;&lt;/li&gt;&#xA;&lt;li&gt;find the &lt;em&gt;algorithm&lt;/em&gt; the code implements;&lt;/li&gt;&#xA;&lt;li&gt;reverse the &lt;em&gt;solution&lt;/em&gt; the algorithm implements;&lt;/li&gt;&#xA;&lt;li&gt;re-implement the solution to solve the &lt;em&gt;problem&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;This is the conversion from &lt;em&gt;instructions&lt;/em&gt; to &lt;em&gt;code&lt;/em&gt;, and then to &lt;em&gt;solution&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;p&gt;The practice of doing such a thing in reverse order is &lt;strong&gt;reverse engineering&lt;/strong&gt;, aka. &lt;strong&gt;RE&lt;/strong&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;why-reverse-engineering-matters&#34;&gt;Why reverse engineering matters&#xA;&lt;/h2&gt;&lt;p&gt;The most significant reason why reverse engineering matters is that it helps us to &lt;strong&gt;port free software operating systems&lt;/strong&gt;, such as postmarketOS and LineageOS, to various devices. This is also the main reason I am learning reverse engineering.&lt;/p&gt;&#xA;&lt;p&gt;On desktop PCs, software freedom is much easier to achieve, as we can usually run all kinds of free GNU/Linux distributions without severe problems. However, on mobile devices, such as smartphones or tablets, this is not the case.&lt;/p&gt;&#xA;&lt;p&gt;These devices often have many vendor-specific and device-specific tweaks, and it is almost impossible to find a one-size-fits-all solution to get a free OS running on these devices universally. Even worse, manufacturers are choosing to hide those technical details, and even Google chose not to release the device trees and vendor blobs anymore. Therefore, we now have to analyse the &lt;em&gt;binary&lt;/em&gt; firmware blobs and infer their logic in order to get postmarketOS, LineageOS, or other free software operating systems running on those devices.&lt;/p&gt;&#xA;&lt;p&gt;Actually, the Free Software Foundation is also working on this issue under the &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; project. This project cannot continue without reverse engineering.&lt;/p&gt;&#xA;&lt;p&gt;However, the application of reverse engineering is by no means limited to free OS porting. It also matters a lot in:&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;figuring out the details of proprietary protocols,&lt;/li&gt;&#xA;&lt;li&gt;finding evidence of malicious functionality in proprietary software,&lt;/li&gt;&#xA;&lt;li&gt;Digital Restrictions Management circumvention,&lt;/li&gt;&#xA;&lt;li&gt;and much more.&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;h2 id=&#34;how-to-do-reverse-engineering&#34;&gt;How to do reverse engineering&#xA;&lt;/h2&gt;&lt;p&gt;Typical reverse engineering consists of these two steps:&lt;/p&gt;&#xA;&lt;ol&gt;&#xA;&lt;li&gt;Convert the executable to assembly; this process is called &lt;em&gt;disassembling&lt;/em&gt;.&lt;/li&gt;&#xA;&lt;li&gt;Try to recover the ideas or rewrite equivalent code by reading the assembly code.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;p&gt;We can do this directly on the executable without running it, known as &lt;em&gt;static analysis&lt;/em&gt;, or while it is running, known as &lt;em&gt;dynamic analysis&lt;/em&gt;.&lt;/p&gt;&#xA;&lt;h2 id=&#34;static-analysis-example&#34;&gt;Static analysis example&#xA;&lt;/h2&gt;&lt;p&gt;Take this for example:&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 retries remaining.\nPassword: &amp;#34;, 3 - i);&#xA;        fgets(buf, sizeof buf, stdin);&#xA;        if (strncmp(PASS, buf, strlen(PASS)))&#xA;            puts(&amp;#34;Wrong password!\n&amp;#34;);&#xA;        else&#xA;        {&#xA;            puts(&amp;#34;Correct!\nWelcome to the world &amp;#34;&#xA;                 &amp;#34;of reverse engineering!\n&amp;#34;);&#xA;            break;&#xA;        }&#xA;    }&#xA;    return 0;&#xA;}&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Compile this with 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;Open the executable 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;You will see this:&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;Let&amp;rsquo;s analyse the executable:&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;List the functions:&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;We can now step into the main function and print the disassembly:&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 retries remaining.\nPassword: &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;Wrong password!\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;Correct!\nWelcome to the world of 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 will not only give us the assembly code, but also each instruction&amp;rsquo;s corresponding address and the calling relationships among them.&lt;/p&gt;&#xA;&lt;p&gt;However, it is still not so clear now. So let&amp;rsquo;s show the disassembly in a graph:&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;You will now see:&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;radare2 disassembly graph view&#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;In the graph, &lt;code&gt;t&lt;/code&gt; means jump when the condition is true. &lt;code&gt;f&lt;/code&gt; means jump when the condition is false. &lt;code&gt;v&lt;/code&gt; means unconditional jump.&lt;/p&gt;&#xA;&lt;p&gt;Now life becomes much easier. Radare2 has already figured out the logical relationships between the assembly code pieces, and you only need to figure out the assembly code pieces themselves.&lt;/p&gt;&#xA;&lt;p&gt;In the graph, we can easily find the password checking logic at the end of the &lt;code&gt;[0x93c]&lt;/code&gt; block:&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;Here it calls &lt;code&gt;sym.imp.strncmp&lt;/code&gt;, and then checks the value returned in &lt;code&gt;w0&lt;/code&gt; by that function. If it equals &lt;code&gt;0&lt;/code&gt;, it will jump to the &lt;code&gt;[0x9c0]&lt;/code&gt; block, indicating success. Otherwise it will continue to the &lt;code&gt;[0x9b0]&lt;/code&gt; block, indicating failure.&lt;/p&gt;&#xA;&lt;p&gt;According to the AArch64 calling convention, the pointers to the two strings to be compared are in the registers &lt;code&gt;x0&lt;/code&gt; and &lt;code&gt;x1&lt;/code&gt;, respectively, then we call &lt;code&gt;sym.imp.strncmp&lt;/code&gt;. The result is then stored in the &lt;code&gt;x0&lt;/code&gt; register (&lt;code&gt;w0&lt;/code&gt; is just the lower half of &lt;code&gt;x0&lt;/code&gt;), and if the two strings are identical, we get &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Now we scan backwards and see:&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;Then scan back further to find the instruction operating on &lt;code&gt;x19&lt;/code&gt;:&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 has already told us the top secret.&lt;/strong&gt; Let&amp;rsquo;s try it:&lt;/p&gt;&#xA;&lt;pre tabindex=&#34;0&#34;&gt;&lt;code&gt;$ ./demo&#xA;3 retries remaining.&#xA;Password: weakpasswd&#xA;Correct!&#xA;Welcome to the world of reverse engineering!&#xA;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That&amp;rsquo;s it.&lt;/p&gt;&#xA;&lt;p&gt;If we look more closely at the graph, we can learn more than just the password. For example, we can see a cycle in the graph: &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 a graph, a cycle often indicates loop control flow. This is a very important reverse engineering mindset.&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;In the &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;It loads the variable &lt;code&gt;var_6ch&lt;/code&gt; into a register and compares it with &lt;code&gt;2&lt;/code&gt;. If it is larger, it proceeds to the &lt;code&gt;[0x9e8]&lt;/code&gt; block and quits. Otherwise, it proceeds to the &lt;code&gt;[0x93c]&lt;/code&gt; block.&lt;/p&gt;&#xA;&lt;p&gt;Look at the end of the &lt;code&gt;[0x93c]&lt;/code&gt; block. We already know that if the password check succeeds, it will jump to the &lt;code&gt;[0x9c0]&lt;/code&gt; block, print the message indicating success, and then jump to the &lt;code&gt;[0x9e8]&lt;/code&gt; block, which ends the program.&lt;/p&gt;&#xA;&lt;p&gt;But what if the check fails? It will proceed to the &lt;code&gt;[0x9b0]&lt;/code&gt; block. It simply prints the error message, so there is nothing interesting here; let us skip to the &lt;code&gt;[0x9d0]&lt;/code&gt; block. Here we find something interesting:&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;It loads the variable &lt;code&gt;var_6ch&lt;/code&gt; into a register, increments the value by one, and stores the value back. Then it goes back to the &lt;code&gt;[0x9dc]&lt;/code&gt; block, which checks the value of &lt;code&gt;var_6ch&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Press &lt;code&gt;q&lt;/code&gt; twice to return to the prompt. Run &lt;code&gt;afv&lt;/code&gt; and &lt;code&gt;afvd&lt;/code&gt; to list variables and their information:&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;We can see that the value &lt;code&gt;var_6ch&lt;/code&gt; is an &lt;code&gt;int64_t&lt;/code&gt;, and its initial value is &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;&#xA;&lt;p&gt;Now we can infer that &lt;code&gt;var_6ch&lt;/code&gt; is a counter. Its initial value is 0. If a password check fails, the counter is incremented by one. Once it exceeds 2, the program will no longer ask for the password and will exit. Now we can tell that this corresponds to our C code &lt;code&gt;for (int i = 0; i &amp;lt; 3; i++)&lt;/code&gt;!&lt;/p&gt;&#xA;&lt;p&gt;However, this is a very simple example. In the real world, things become harder, as proprietary software developers often use anti-analysis and anti-debugging techniques.&lt;/p&gt;&#xA;&lt;h2 id=&#34;understanding-compiler-optimisations&#34;&gt;Understanding compiler optimisations&#xA;&lt;/h2&gt;&lt;p&gt;Now, let&amp;rsquo;s compile this even simpler 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;It loads an integer from the input, and outputs its modulo 65536.&lt;/p&gt;&#xA;&lt;p&gt;Disassemble its &lt;code&gt;main&lt;/code&gt; function:&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;You may be confused, because you cannot see anything about a modulo operation (&lt;code&gt;sdiv&lt;/code&gt;, &lt;code&gt;mul&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt;, etc.) here. Instead, you see:&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;This is because modern compilers are smart enough to detect some special computation patterns and optimise them into simpler instructions. 65536 is 2^16, so the binary number modulo 65536 is just its least significant 16 bits. A bitwise operation is enough; there is no need for adders or multipliers.&lt;/p&gt;&#xA;&lt;p&gt;However, if we change 65536 to something else, for example, 50000, that is no longer the case:&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;We can now see the &lt;code&gt;sdiv&lt;/code&gt;, &lt;code&gt;mul&lt;/code&gt;, and &lt;code&gt;sub&lt;/code&gt; instructions here, which are combined to complete a modulo operation.&lt;/p&gt;&#xA;&lt;h2 id=&#34;what-about-dynamic-analysis&#34;&gt;What about dynamic analysis?&#xA;&lt;/h2&gt;&lt;p&gt;For dynamic analysis, you will need GDB. With GDB, you can print disassembly, set breakpoints, inspect memory and registers, and even change the values of registers and variables while the program is running.&lt;/p&gt;&#xA;&lt;p&gt;I have not explored dynamic analysis in depth yet. I may write a new blog post to explain dynamic analysis in the future.&lt;/p&gt;&#xA;&lt;h2 id=&#34;recommended-resources&#34;&gt;Recommended resources&#xA;&lt;/h2&gt;&lt;p&gt;What I have explained is just the tip of the iceberg. To learn reverse engineering, you need to go deeper, practise more, and learn by doing. Below is a list of recommended resources:&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; is a good place to get started. You can learn x86_64 assembly in their starter dojo, and then proceed to the Reverse Engineering part in the Intro to Cybersecurity module.&lt;/li&gt;&#xA;&lt;li&gt;&lt;em&gt;Reverse Engineering for Beginners&lt;/em&gt; is a very good book on reverse engineering. It is free (as in freedom), licensed under CC BY-SA 4.0, and is available for download &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;here&lt;/a&gt;.&lt;/li&gt;&#xA;&lt;li&gt;ARM offers &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;official documentation&lt;/a&gt; for its AArch64 architecture.&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;Here&lt;/a&gt; you can download crackmes to practise your reverse engineering skills.&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;Here&lt;/a&gt; is a GitHub repository containing many resources for learning reverse engineering.&lt;/li&gt;&#xA;&lt;/ol&gt;&#xA;&lt;h2 id=&#34;disclaimer&#34;&gt;Disclaimer&#xA;&lt;/h2&gt;&lt;p&gt;This article is for educational purposes only. Please consult your local laws to learn what you may not do. I am not responsible for any of your actions.&lt;/p&gt;&#xA;</description>
        </item></channel>
</rss>
