<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.4.1">Jekyll</generator><link href="https://shomy.is-a.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://shomy.is-a.dev/" rel="alternate" type="text/html" /><updated>2026-07-06T17:12:06+00:00</updated><id>https://shomy.is-a.dev/feed.xml</id><title type="html">shomy</title><subtitle>Musician, developer.</subtitle><entry><title type="html">Hacking the Kyocera KY-42C</title><link href="https://shomy.is-a.dev/blog/article/unlocking-kyocera-ky42c" rel="alternate" type="text/html" title="Hacking the Kyocera KY-42C" /><published>2026-07-06T00:00:00+00:00</published><updated>2026-07-06T00:00:00+00:00</updated><id>https://shomy.is-a.dev/blog/article/unlocking-kyocera-ky42c</id><content type="html" xml:base="https://shomy.is-a.dev/blog/article/unlocking-kyocera-ky42c"><![CDATA[<h2 id="introduction">Introduction</h2>

<p>In this post, I will share my experience unlocking the bootloader and modding the <a href="https://garahowiki.com/phones:kyocera_digno_keitai_4:start">Kyocera KY-42C</a> (DIGNO Keitai 4), a Japanese feature phone. This device has been known by the community for being considered unrootable after a firmware update, but as always, that won’t stop someone from trying :).</p>

<p><img src="/media/posts/2026/ky42c/a202kc.jpg" alt="Kyocera A202KC, similar device to KY-42C" style="width: 50%;" />
<em>A Kyocera A202KC, almost same device as KY-42C, picture from <a href="https://garahowiki.com/phones:kyocera_digno_keitai_4:start">garahowiki</a></em></p>

<p>After my blog post about <a href="https://itssho.my/blog/article/serving-carbonara">Carbonara</a>, a MediaTek Download Agent exploit, a user known as <a href="https://github.com/760ceb3b9c0ba4872cadf3ce35a7a494">@760ceb3b9c0ba4872cadf3ce35a7a494
</a> left a comment asking for ideas on whether it was possible to unlock the bootloader of the KY-42C after the firmware update that supposedly made it unrootable. I was interested in this challenge, so I decided it was worth investigating.</p>

<h2 id="background">Background</h2>

<p>The KY-42C runs on a MediaTek MT6761 SoC, known by the market name of “Helio A22”. Being a MediaTek device (an old one too), some ideas already came to my mind. I had previously worked on unlocking MediaTek devices, so I was familiar with how to approach this.</p>

<p>Before diving into the specific details of the device, it’s worth mentioning some specific concepts about MediaTek devices.</p>

<p>MediaTek devices include a USB rescue mode for flashing firmware, found in <code class="language-plaintext highlighter-rouge">Preloader</code> and <code class="language-plaintext highlighter-rouge">BROM</code>.</p>

<p>Brom is the masked ROM in the chip, which is the first code that runs when the device is powered on.
It is responsible for initializing the basic hardware and loading the <code class="language-plaintext highlighter-rouge">Preloader</code>, a second stage bootloader whose job is to initialize the rest of the hardware (like DRAM) and load the main bootloader (LK), which then loads the Android OS.</p>

<p><img src="/media/posts/2026/ky42c/mtk_legacy_bootchain.png" alt="MediaTek Boot Process" /></p>

<p>Both BootROM and Preloader allow loading a <code class="language-plaintext highlighter-rouge">Download Agent</code> (DA), a binary that allow flashing firmware through specialised tools, such as <code class="language-plaintext highlighter-rouge">SP Flash Tool</code>, <code class="language-plaintext highlighter-rouge">Penumbra</code> or <code class="language-plaintext highlighter-rouge">mtkclient</code>.
For knowing more about Download Agents, I suggest reading <a href="https://blog.r0rt1z2.com/posts/exploiting-mediatek-datwo/#download-agents">R0rt1z2 blog post about heapb8</a> and <a href="https://penumbra.itssho.my/Mediatek/Common/DA/Download-Agent">penumbra documentation</a>.</p>

<p>Often OEMs don’t provide the DA needed for their devices, and if they do, they are usually locked to only allow flashing signed firmware.
Furthermore, because of BROM exploits such as <a href="https://penumbra.itssho.my/Mediatek/Exploits/Kamakiri">kamakiri</a> or linecode, OEMs have started to block BROM USBDL altogether, and only allow flashing through the Preloader. This is the case of the KY-42C, which has a locked BROM and no available DA for flashing firmware.</p>

<h2 id="investigation">Investigation</h2>

<p>In the case of the KY-42C, what we care is that the device, after the 1.090XX firmware update, locked BootROM USBDL.
This means that we cannot use any BROM exploits to boot an unsigned Download Agent, and we are limited to the Preloader download mode.</p>

<p>However, I had a feeling that BROM USBDL wasn’t really permanently locked, and that it was possible to access it temporarily.</p>

<p>MediaTek implemented two ways to block this mode: one is to disable it via an efuse, a permanent solution that cannot be reverted, and the other is to set a flag in the Preloader GFH header.</p>

<p>During boot up, the BootROM determines whether USBDL is allowed by checking a few flags:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
</pre></td><td class="rouge-code"><pre><span class="n">bool</span> <span class="nf">check_brom_cmd_disabled</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>

<span class="p">{</span>
  <span class="kt">int</span> <span class="n">iVar1</span><span class="p">;</span>
  <span class="n">GFH_BROM_SEC_CFG_v1</span> <span class="o">*</span><span class="n">brom_sec_cfg</span><span class="p">;</span>
  <span class="n">GFH_BROM_CFG_v3</span> <span class="o">*</span><span class="n">brom_cfg</span><span class="p">;</span>
  <span class="n">ushort</span> <span class="n">code2</span><span class="p">;</span>
  <span class="n">ushort</span> <span class="n">uVar2</span><span class="p">;</span>
  <span class="n">bool</span> <span class="n">locked</span><span class="p">;</span>
  
  <span class="n">code2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">uVar2</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="n">iVar1</span> <span class="o">=</span> <span class="n">efuse_is_brom_cmd_disabled</span><span class="p">();</span>
  <span class="n">locked</span> <span class="o">=</span> <span class="n">iVar1</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">locked</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">code2</span> <span class="o">=</span> <span class="mh">0x8000</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/* Preloader header is made of various structs, all under the common
   * name of GFH (General File Header).
   * Each GFH struct has a purpose, and the ones we care about are GFH_BROM_SEC_CFG and GFH_BROM_CFG.
   */</span>
  <span class="n">brom_sec_cfg</span> <span class="o">=</span> <span class="n">get_bl_gfh</span><span class="p">(</span><span class="n">GFH_BROM_SEC_CFG</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">((</span><span class="n">brom_sec_cfg</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">brom_sec_cfg</span><span class="o">-&gt;</span><span class="n">m_cmd_mode_permanent_dis</span> <span class="o">==</span> <span class="mh">0xc975e033</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">code2</span> <span class="o">=</span> <span class="n">code2</span> <span class="o">|</span> <span class="mh">0x4000</span><span class="p">;</span>
    <span class="n">locked</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="cm">/*
   * GFH_BROM_CFG allows to disable specific communication modes, in this case UART1 and USB.
   * This means that even if BROM USBDL is enabled, it can be allowed only through means other than USB.
   */</span>
  <span class="n">brom_cfg</span> <span class="o">=</span> <span class="n">get_bl_gfh</span><span class="p">(</span><span class="n">GFH_BROM_CFG</span><span class="p">);</span>
  <span class="k">if</span> <span class="p">((</span><span class="n">brom_cfg</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">brom_cfg</span><span class="o">-&gt;</span><span class="n">m_brom_cmd_via_uart1_disable_magic</span> <span class="o">==</span> <span class="sc">'R'</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">code2</span> <span class="o">=</span> <span class="n">code2</span> <span class="o">|</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">if</span> <span class="p">((</span><span class="n">brom_cfg</span> <span class="o">!=</span> <span class="nb">NULL</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">brom_cfg</span><span class="o">-&gt;</span><span class="n">m_brom_cmd_via_usb_disable_magic</span> <span class="o">==</span> <span class="sc">'U'</span><span class="p">))</span> <span class="p">{</span>
    <span class="n">code2</span> <span class="o">=</span> <span class="n">code2</span> <span class="o">|</span> <span class="mi">2</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="p">...</span>

  <span class="n">iVar1</span> <span class="o">=</span> <span class="n">efuse_is_brom_cmd_disabled</span><span class="p">();</span>
  <span class="k">if</span> <span class="p">(</span><span class="n">iVar1</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">code2</span> <span class="o">=</span> <span class="n">code2</span> <span class="o">|</span> <span class="mh">0x8000</span><span class="p">;</span>
    <span class="n">locked</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="n">cmd_dis_bitfield</span> <span class="o">=</span> <span class="n">code2</span> <span class="o">|</span> <span class="n">uVar2</span> <span class="o">|</span> <span class="n">cmd_dis_bitfield</span><span class="p">;</span>
  <span class="k">if</span> <span class="p">((</span><span class="n">cmd_dis_bitfield</span> <span class="o">&amp;</span> <span class="mh">0x3fff</span><span class="p">)</span> <span class="o">==</span> <span class="mi">3</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">cmd_dis_bitfield</span> <span class="o">=</span> <span class="n">cmd_dis_bitfield</span> <span class="o">&amp;</span> <span class="mh">0xfffe</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="p">...</span>
  
  <span class="k">return</span> <span class="n">locked</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>And later on the BootROM checks whether USBDL is allowed, and what communication mode is allowed.</p>

<p>My feeling was that the efuse itself was not blown, and instead download mode has been disabled through Preloader flags.
To confirm my theory, I asked 760ceb3b9c0ba4872cadf3ce35a7a494 to run a little script to dump fuses from Preloader USBDL (yes, MediaTek allows this).</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
</pre></td><td class="rouge-code"><pre><span class="c1"># The script is based on moto-experiments:
# https://github.com/R0rt1z2/moto-experiments
</span>    <span class="bp">...</span>
    
    <span class="n">device</span> <span class="o">=</span> <span class="nc">Device</span><span class="p">(</span><span class="n">args</span><span class="p">.</span><span class="n">port</span><span class="p">)</span>

    <span class="n">logging</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sh">'</span><span class="s">Waiting for device...</span><span class="sh">'</span><span class="p">)</span>

    <span class="n">device</span><span class="p">.</span><span class="nf">find_device</span><span class="p">()</span>

    <span class="k">if</span> <span class="ow">not</span> <span class="n">args</span><span class="p">.</span><span class="n">skip_handshake</span><span class="p">:</span>
        <span class="n">device</span><span class="p">.</span><span class="nf">handshake</span><span class="p">()</span>

    <span class="n">device</span><span class="p">.</span><span class="nf">identify</span><span class="p">()</span>
    <span class="n">hrid</span> <span class="o">=</span> <span class="n">device</span><span class="p">.</span><span class="nf">read32</span><span class="p">(</span><span class="mh">0x11c50000</span><span class="p">,</span> <span class="mh">0x1000</span><span class="p">)</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="sh">'</span><span class="s">efuse.bin</span><span class="sh">'</span><span class="p">,</span> <span class="sh">'</span><span class="s">wb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">f</span><span class="p">.</span><span class="nf">write</span><span class="p">(</span><span class="n">hrid</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Thanks to MTK whitelisting <code class="language-plaintext highlighter-rouge">devinfo</code> base in <code class="language-plaintext highlighter-rouge">read32</code> cmd, we were able to dump the whole efuse region, especially the <code class="language-plaintext highlighter-rouge">sec-ctrl</code> fuse, at offset <code class="language-plaintext highlighter-rouge">0x60</code>.</p>

<p><img src="/media/posts/2026/ky42c/sec_ctrl_fuse.png" alt="sec-ctrl fuse" /></p>

<p>By looking at the BootROM code, we can easily determine which bit is the one that disables BROM USBDL.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
</pre></td><td class="rouge-code"><pre><span class="n">uint</span> <span class="nf">efuse_is_brom_cmd_disabled</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
  <span class="k">return</span> <span class="p">(</span><span class="n">uint</span><span class="p">)(</span><span class="n">_DAT_11c50060</span><span class="p">)</span> <span class="o">&gt;&gt;</span> <span class="mi">8</span> <span class="o">&amp;</span> <span class="mi">1</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>So, the bit in question is bit 8, which in our case evaluates to 0!</p>

<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
</pre></td><td class="rouge-code"><pre><span class="o">&gt;&gt;&gt;</span> 0x46 <span class="o">&gt;&gt;</span> 8 &amp; 1
0
</pre></td></tr></tbody></table></code></pre></div></div>

<p>So, the efuse is not blown, meaning that we could in theory just erase Preloader or short the test point to be able to load an unsigned DA, thanks to <code class="language-plaintext highlighter-rouge">linecode</code> exploit.</p>

<p>Unfortunately, after many attempts to locate a test point, and without a way to erase Preloader because of the device locked down fastboot, only one option was left: to hopefully find a way to unlock the bootloader through software, preferrably through a Preloader or lk exploit.</p>

<h2 id="firmware-analysis">Firmware Analysis</h2>

<p>I initially started by analyzing lk, which 760ceb3b9c0ba4872cadf3ce35a7a494 had lying around from an old backup.</p>

<p>With the experience I got from analyzing lk images from occasionally contributing to <a href="https://github.com/R0rt1z2/kaeru">kaeru</a>, I was able to quickly find the <code class="language-plaintext highlighter-rouge">fastboot_init</code>, which is where all fastboot commands are registered. I was looking for some kind of unlock command, but it looks like Kyocera decided to remove it completely:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
</pre></td><td class="rouge-code"><pre><span class="p">...</span>
<span class="n">fastboot_register</span><span class="p">(</span><span class="s">"flash:"</span><span class="p">,</span><span class="mh">0x4802b461</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="n">iVar1</span> <span class="o">=</span> <span class="n">codecheck</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">iVar1</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"erase:"</span><span class="p">,</span><span class="mh">0x4802b1f1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"erase:"</span><span class="p">,</span><span class="mh">0x4802b1f1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem printk-ratelimit"</span><span class="p">,</span><span class="mh">0x48025175</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="n">iVar1</span> <span class="o">=</span> <span class="n">codecheck</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">iVar1</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"continue"</span><span class="p">,</span><span class="o">&amp;</span><span class="n">LAB_4802508c</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"continue"</span><span class="p">,</span><span class="o">&amp;</span><span class="n">LAB_4802508c</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">fastboot_register</span><span class="p">(</span><span class="s">"download:"</span><span class="p">,</span><span class="mh">0x4802b899</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="n">fastboot_publish</span><span class="p">(</span><span class="s">"max-download-size"</span><span class="p">,</span><span class="mh">0x480baa9c</span><span class="p">);</span>
<span class="n">FUN_48000188</span><span class="p">();</span>
<span class="n">iVar1</span> <span class="o">=</span> <span class="n">codecheck</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">iVar1</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem p2u"</span><span class="p">,</span><span class="mh">0x4802522d</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem p2u"</span><span class="p">,</span><span class="mh">0x4802522d</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem dump_pllk_log"</span><span class="p">,</span><span class="mh">0x480250fd</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem off-mode-charge"</span><span class="p">,</span><span class="o">&amp;</span><span class="n">LAB_48025388</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="n">iVar1</span> <span class="o">=</span> <span class="n">codecheck</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">iVar1</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem key"</span><span class="p">,</span><span class="mh">0x4802b905</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem key"</span><span class="p">,</span><span class="mh">0x4802b905</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">iVar1</span> <span class="o">=</span> <span class="n">codecheck</span><span class="p">();</span>
<span class="k">if</span> <span class="p">(</span><span class="n">iVar1</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem lks"</span><span class="p">,</span><span class="mh">0x4802b961</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span> <span class="p">{</span>
  <span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem lks"</span><span class="p">,</span><span class="mh">0x4802b961</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">0</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">...</span>

<span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem ultraflash:"</span><span class="p">,</span><span class="mh">0x4802b4cd</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
<span class="n">fastboot_register</span><span class="p">(</span><span class="s">"oem ultraflash_en"</span><span class="p">,</span><span class="mh">0x4802b5b9</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
<span class="n">fastboot_register</span><span class="p">(</span><span class="s">"ultraflash:"</span><span class="p">,</span><span class="mh">0x4802b4cd</span><span class="p">,</span><span class="mi">1</span><span class="p">,</span><span class="mi">1</span><span class="p">);</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>and unfortunately, most of the oem commands are gated behind a flag in the <code class="language-plaintext highlighter-rouge">chkcode</code> partition, making them inaccessible on secure devices.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
</pre></td><td class="rouge-code"><pre><span class="kt">int</span> <span class="nf">codecheck</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
  <span class="n">uint</span> <span class="n">uVar1</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">local_810</span><span class="p">;</span>
  <span class="kt">int</span> <span class="n">local_80c</span><span class="p">;</span>
  
  <span class="k">if</span> <span class="p">(</span><span class="n">ALLOWED</span><span class="o">?</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
    <span class="n">uVar1</span> <span class="o">=</span> <span class="n">partition_read</span><span class="p">(</span><span class="s">"chkcode"</span><span class="p">,</span><span class="mh">0x800</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="mi">0</span><span class="p">,</span><span class="o">&amp;</span><span class="n">local_810</span><span class="p">,</span><span class="mh">0x800</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(((</span><span class="n">uVar1</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span> <span class="o">||</span> <span class="p">(</span><span class="n">local_810</span> <span class="o">!=</span> <span class="mh">0x544f4f4c</span><span class="p">))</span> <span class="o">||</span> <span class="p">(</span><span class="n">local_80c</span> <span class="o">!=</span> <span class="mh">0x4b434642</span><span class="p">))</span> <span class="p">{</span>
      <span class="n">ALLOWED</span><span class="o">?</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
      <span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="n">ALLOWED</span><span class="o">?</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="k">return</span> <span class="n">ALLOWED</span><span class="o">?</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>This, combined with a strict sec policy, narrowed down the possibilities of an unlock exploit in lk (or at least, that I was willing to invest time into).</p>

<p>So, it was time to move on to the Preloader.</p>

<p>Getting a preloader image was quite a challenge, because the dump 760ceb3b9c0ba4872cadf3ce35a7a494 had didn’t include it. I waited for them to get me the required files to continue.
They were able to get both an old and new image from an OTA dump, which was perfect for diffing.</p>

<p>Having both images, I was able to, first of all, confirm my initial theory that the Preloader was indeed responsible for disabling BROM USBDL.</p>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
</pre></td><td class="rouge-code"><pre># Old preloader

  BROM_SEC_CFG (v1, 48 bytes)
    JTAG: disabled
    Debug: disabled
    BROM Cmd Perm Dis: no

# New preloader

  BROM_SEC_CFG (v1, 48 bytes)
    JTAG: disabled
    Debug: disabled
    BROM Cmd Perm Dis: yes
</pre></td></tr></tbody></table></code></pre></div></div>

<p>Then, I started loading the two images into Ghidra, and started looking for some exploitable paths.</p>

<h2 id="exploiting-preloader">Exploiting Preloader</h2>

<p>During USBDL in Preloader, the device will enter an infinite loop, waiting for a command from the host.
The command is sent through USB, where it will be matched in a switch statement and execute the corresponding function.</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
</pre></td><td class="rouge-code"><pre><span class="c1">// From `download.c`, found on GitHub</span>
<span class="kt">int</span> <span class="nf">usbdl_handler</span><span class="p">(</span><span class="k">struct</span> <span class="n">bldr_comport</span> <span class="o">*</span><span class="n">comport</span><span class="p">,</span> <span class="n">u32</span> <span class="n">hshk_tmo_ms</span><span class="p">)</span>
<span class="p">{</span>
	<span class="n">u8</span> <span class="n">cmd</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

  <span class="p">...</span>

<span class="cp">#if CFG_PRELOADER_AS_DA
</span>	<span class="n">usbdl_init_image</span><span class="p">();</span>
<span class="cp">#endif
</span>
	<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">platform_wdt_kick</span><span class="p">();</span>

		<span class="n">usbdl_get_byte</span><span class="p">(</span><span class="o">&amp;</span><span class="n">cmd</span><span class="p">);</span>
		<span class="k">if</span> <span class="p">(</span><span class="n">cmd</span> <span class="o">!=</span> <span class="n">CMD_GET_BL_VER</span><span class="p">)</span>
			<span class="n">usbdl_put_byte</span><span class="p">(</span><span class="n">cmd</span><span class="p">);</span>

		<span class="k">switch</span> <span class="p">(</span><span class="n">cmd</span><span class="p">)</span> <span class="p">{</span>
		  <span class="p">...</span>
			<span class="k">case</span> <span class="n">CMD_SEND_DA</span><span class="p">:</span>
				<span class="n">DBGMSG</span><span class="p">(</span><span class="s">"%s CMD_SEND_DA</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
				<span class="n">usbdl_send_da</span><span class="p">();</span>
				<span class="k">break</span><span class="p">;</span>
			<span class="k">case</span> <span class="n">CMD_JUMP_DA</span><span class="p">:</span>
				<span class="n">DBGMSG</span><span class="p">(</span><span class="s">"%s CMD_JUMP_DA</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
				<span class="n">usbdl_jump_da</span><span class="p">();</span>
				<span class="k">break</span><span class="p">;</span>
<span class="cp">#if CFG_PRELOADER_AS_DA
</span>			<span class="k">case</span> <span class="n">CMD_SEND_IMAGE</span><span class="p">:</span>
				<span class="n">DBGMSG</span><span class="p">(</span><span class="s">"%s CMD_SEND_IMAGE</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
				<span class="n">usbdl_send_image</span><span class="p">();</span>
				<span class="k">break</span><span class="p">;</span>
			<span class="k">case</span> <span class="n">CMD_BOOT_IMAGE</span><span class="p">:</span>
				<span class="n">DBGMSG</span><span class="p">(</span><span class="s">"%s CMD_BOOT_IMAGE</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
				<span class="n">usbdl_boot_image</span><span class="p">();</span>
				<span class="k">break</span><span class="p">;</span>
<span class="cp">#endif
</span>			<span class="k">case</span> <span class="n">CMD_READ32</span><span class="p">:</span>
				<span class="n">DBGMSG</span><span class="p">(</span><span class="s">"%s CMD_READ32</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
				<span class="n">usbdl_read32</span><span class="p">(</span><span class="n">FALSE</span><span class="p">);</span>
				<span class="k">break</span><span class="p">;</span>
			<span class="k">case</span> <span class="n">CMD_WRITE32</span><span class="p">:</span>
				<span class="n">DBGMSG</span><span class="p">(</span><span class="s">"%s CMD_WRITE32</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
				<span class="n">usbdl_write32</span><span class="p">(</span><span class="n">TRUE</span><span class="p">);</span>
				<span class="k">break</span><span class="p">;</span>
			<span class="p">...</span>
			<span class="nl">default:</span>
				<span class="n">DBGMSG</span><span class="p">(</span><span class="s">"%s Unhandled CMD 0x%x</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">,</span> <span class="n">cmd</span><span class="p">);</span>
				<span class="k">break</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">return</span> <span class="mi">0</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>While looking at the Preloader code and the one I have loaded in Ghidra, I got caught by the “CMD_SEND_IMAGE\n” debug message. This command is only available if <code class="language-plaintext highlighter-rouge">CFG_PRELOADER_AS_DA</code> is defined, which seems to be the case for KY-42C.</p>

<p>In my experience, this command and <code class="language-plaintext highlighter-rouge">CMD_BOOT_IMAGE</code> are not present on production preloaders.
So, I thought, if MTK doesn’t enable these commands by default, and decided to also remove them all together, perhaps they had some reason to.</p>

<p>Reading the code, I realized that, as I suspected, MediaTek forgot to implement a check for signature verification in the cmds:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
</pre></td><td class="rouge-code"><pre><span class="k">static</span> <span class="kt">void</span> <span class="nf">usbdl_send_image</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
	<span class="n">u32</span> <span class="n">img_addr</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">u32</span> <span class="n">img_len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">image_index_t</span> <span class="n">id</span><span class="p">;</span>
	<span class="n">u16</span> <span class="n">status</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">u8</span> <span class="n">img_name</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
	<span class="n">u32</span> <span class="n">checksum32</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">u32</span> <span class="n">my_checksum32</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
	<span class="n">u8</span> <span class="n">debug_buf</span><span class="p">[</span><span class="mi">32</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>

	<span class="n">usbdl_get_data</span><span class="p">(</span><span class="n">img_name</span><span class="p">,</span> <span class="mi">64</span><span class="p">);</span>
	<span class="n">usbdl_get_dword</span><span class="p">(</span><span class="o">&amp;</span><span class="n">img_len</span><span class="p">);</span>

	<span class="k">for</span> <span class="p">(</span><span class="n">id</span> <span class="o">=</span> <span class="n">IMAGE_LK_ID</span><span class="p">;</span> <span class="n">id</span> <span class="o">&lt;</span> <span class="n">IMAGE_MAX_NUM</span><span class="p">;</span> <span class="n">id</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
		<span class="k">if</span> <span class="p">((</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">img_name</span><span class="p">,</span> <span class="n">g_image_list</span><span class="p">[</span><span class="n">id</span><span class="p">].</span><span class="n">partition_name</span><span class="p">))</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">img_len</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="p">(</span><span class="n">img_len</span> <span class="o">&lt;=</span> <span class="n">g_image_list</span><span class="p">[</span><span class="n">id</span><span class="p">].</span><span class="n">length</span><span class="p">))</span> <span class="p">{</span>
			<span class="n">pal_log_info</span><span class="p">(</span><span class="s">"%s SEND: Verify PASS</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
			<span class="k">if</span><span class="p">(</span><span class="n">id</span> <span class="o">==</span> <span class="n">IMAGE_ATF_ID</span><span class="p">)</span>
				<span class="n">img_addr</span> <span class="o">=</span> <span class="p">(</span><span class="n">u32</span><span class="p">)</span><span class="n">tee1_buf</span><span class="p">;</span>
			<span class="k">else</span>
			<span class="n">img_addr</span> <span class="o">=</span> <span class="n">g_image_list</span><span class="p">[</span><span class="n">id</span><span class="p">].</span><span class="n">start_addr</span><span class="p">;</span>
			<span class="k">break</span><span class="p">;</span>
		<span class="p">}</span>
	<span class="p">}</span>
	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">img_addr</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">pal_log_err</span><span class="p">(</span><span class="s">"%s Unknown image</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
	<span class="p">}</span>

	<span class="n">usbdl_put_word</span><span class="p">(</span><span class="n">status</span><span class="p">);</span>

  <span class="cm">/* Here the device downloads the image data from the host, and
  * only performs a checksum verification, without ever verifying
  * the signature.
  */</span>
	<span class="n">usbdl_get_data</span><span class="p">((</span><span class="n">u8</span> <span class="o">*</span><span class="p">)</span><span class="n">img_addr</span><span class="p">,</span> <span class="n">img_len</span><span class="p">);</span>
	<span class="n">my_checksum32</span> <span class="o">=</span> <span class="n">checksum_plain</span><span class="p">((</span><span class="n">u8</span> <span class="o">*</span><span class="p">)</span><span class="n">img_addr</span><span class="p">,</span> <span class="n">img_len</span><span class="p">);</span>
	<span class="n">usbdl_get_dword</span><span class="p">(</span><span class="o">&amp;</span><span class="n">checksum32</span><span class="p">);</span>

	<span class="k">if</span> <span class="p">(</span><span class="n">my_checksum32</span> <span class="o">!=</span> <span class="n">checksum32</span><span class="p">)</span> <span class="p">{</span>
		<span class="n">pal_log_err</span><span class="p">(</span><span class="s">"%s checksum mismatch!</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
		<span class="k">return</span><span class="p">;</span>
	<span class="p">}</span>


  <span class="k">if</span><span class="p">(</span><span class="n">id</span> <span class="o">==</span> <span class="n">IMAGE_ATF_ID</span><span class="p">)</span> <span class="p">{</span>
      <span class="c1">// Relocate ATF and TEE</span>
  <span class="p">}</span>
<span class="p">}</span>

<span class="k">static</span> <span class="kt">void</span> <span class="nf">usbdl_boot_image</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>

	<span class="n">u8</span> <span class="n">img_name</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
	<span class="n">u32</span> <span class="n">jump_arg</span><span class="p">;</span>
	<span class="n">u16</span> <span class="n">status</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>

	<span class="n">usbdl_get_data</span><span class="p">(</span><span class="n">img_name</span><span class="p">,</span> <span class="mi">64</span><span class="p">);</span>

	<span class="n">trustzone_pre_init</span><span class="p">();</span>
	<span class="n">g_boot_mode</span> <span class="o">=</span> <span class="n">FASTBOOT</span><span class="p">;</span>
	<span class="n">platform_set_boot_args</span><span class="p">();</span>
	<span class="n">trustzone_post_init</span><span class="p">();</span>

	<span class="n">jump_arg</span> <span class="o">=</span> <span class="p">(</span><span class="n">u32</span><span class="p">)</span><span class="o">&amp;</span><span class="n">bootarg</span><span class="p">;</span>

	<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">img_name</span><span class="p">,</span> <span class="n">lk</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">usbdl_put_word</span><span class="p">(</span><span class="n">status</span><span class="p">);</span>
		<span class="n">pal_log_err</span><span class="p">(</span><span class="s">"%s Jump to LK</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
		<span class="cm">/* Hi MTK, thanks for this */</span>
		<span class="n">bldr_jump</span><span class="p">(</span><span class="n">g_image_list</span><span class="p">[</span><span class="n">IMAGE_LK_ID</span><span class="p">].</span><span class="n">start_addr</span> <span class="o">+</span> <span class="n">PART_HDR_BUF_SIZE</span><span class="p">,</span> <span class="n">jump_arg</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">boot_arg_t</span><span class="p">));</span>
	<span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="n">strcmp</span><span class="p">(</span><span class="n">img_name</span><span class="p">,</span> <span class="n">atf</span><span class="p">))</span> <span class="p">{</span>
		<span class="n">usbdl_put_word</span><span class="p">(</span><span class="n">status</span><span class="p">);</span>
		<span class="n">pal_log_err</span><span class="p">(</span><span class="s">"%s Jump to ATF</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
		<span class="n">bldr_jump64</span><span class="p">(</span><span class="n">g_image_list</span><span class="p">[</span><span class="n">IMAGE_LK_ID</span><span class="p">].</span><span class="n">start_addr</span> <span class="o">+</span> <span class="n">PART_HDR_BUF_SIZE</span><span class="p">,</span> <span class="n">jump_arg</span><span class="p">,</span> <span class="k">sizeof</span><span class="p">(</span><span class="n">boot_arg_t</span><span class="p">));</span>
	<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
		<span class="n">status</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
		<span class="n">usbdl_put_word</span><span class="p">(</span><span class="n">status</span><span class="p">);</span>
		<span class="n">pal_log_err</span><span class="p">(</span><span class="s">"%s Unknown Jump</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MOD</span><span class="p">);</span>
	<span class="p">}</span>
<span class="p">}</span>

</pre></td></tr></tbody></table></code></pre></div></div>

<p>This means that we can get <code class="language-plaintext highlighter-rouge">EL3</code> Arbitrary Code Execution with just a few lines of python code:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre>    <span class="n">path</span> <span class="o">=</span> <span class="sh">'</span><span class="s">bin/unlock.bin</span><span class="sh">'</span>
    <span class="k">with</span> <span class="nf">open</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="sh">'</span><span class="s">rb</span><span class="sh">'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
        <span class="n">data</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="nf">read</span><span class="p">()</span>
        
    <span class="n">device</span> <span class="o">=</span> <span class="nc">Device</span><span class="p">(</span><span class="bp">None</span><span class="p">)</span>
    
    <span class="n">logging</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sh">'</span><span class="s">Waiting for device...</span><span class="sh">'</span><span class="p">)</span>
    
    <span class="n">device</span><span class="p">.</span><span class="nf">find_device</span><span class="p">()</span>
    <span class="n">device</span><span class="p">.</span><span class="nf">handshake</span><span class="p">()</span>
    <span class="n">device</span><span class="p">.</span><span class="nf">identify</span><span class="p">()</span>
    
    <span class="n">device</span><span class="p">.</span><span class="nf">send_image</span><span class="p">(</span><span class="sh">'</span><span class="s">lk</span><span class="sh">'</span><span class="p">,</span> <span class="n">data</span><span class="p">)</span>
    <span class="n">device</span><span class="p">.</span><span class="nf">boot_image</span><span class="p">(</span><span class="sh">'</span><span class="s">lk</span><span class="sh">'</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><em>Worth mentioning that this exploit has already been used publicly in the past in <a href="https://github.com/arturkow2000/lgk10exploit">lgk10exploit</a>, so it’s nothing new, and has been patched for a few years by now. That said, it was still nice to figure this out by myself before discovering this project.</em></p>

<h2 id="unlocking-the-bootloader">Unlocking the bootloader</h2>

<p>Initially, we tried to boot a patched lk image, but unfortunately the device would crash after a few seconds, and we couldn’t figure out why.
Because of this, and because this lk didn’t include the <code class="language-plaintext highlighter-rouge">sec_set_lock_state</code> function at all, I decided to make my own payload, which uses the framework of my <a href="https://github.com/shomykohai/mtk-payloads">mtk-payloads</a> project.</p>

<p>You can see the full code for the payload <a href="https://github.com/shomykohai/kyocera-ky-42c-unlock/blob/main/payload/src/main.c">here</a>, but the main idea is to create a new seccfg, encrypt its hash and write it to the <code class="language-plaintext highlighter-rouge">seccfg</code> partition, and while at it, also unlock fastboot capabilities in <code class="language-plaintext highlighter-rouge">chkcode</code>:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
</pre></td><td class="rouge-code"><pre><span class="kt">int</span> <span class="nf">main</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span> <span class="p">{</span>
    <span class="p">...</span>

    <span class="n">seccfg</span><span class="p">.</span><span class="n">lock_state</span> <span class="o">=</span> <span class="n">LKS_UNLOCK</span><span class="p">;</span>
    <span class="n">seccfg</span><span class="p">.</span><span class="n">dm_verity</span> <span class="o">=</span> <span class="n">DM_VERITY_OK</span><span class="p">;</span>

    <span class="n">sha256_hash</span><span class="p">(</span><span class="n">hash</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">seccfg</span><span class="p">,</span> <span class="mh">0x1C</span><span class="p">);</span>

    <span class="n">params</span><span class="p">.</span><span class="n">length</span> <span class="o">=</span> <span class="n">HASH_SZ</span><span class="p">;</span>
    <span class="n">params</span><span class="p">.</span><span class="n">anti_clone</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>
    <span class="n">params</span><span class="p">.</span><span class="n">encrypt</span> <span class="o">=</span> <span class="nb">true</span><span class="p">;</span>

    <span class="n">sp_sej_enc</span><span class="p">(</span><span class="n">hash</span><span class="p">,</span> <span class="n">hash</span><span class="p">,</span> <span class="n">params</span><span class="p">);</span>

    <span class="n">memcpy</span><span class="p">(</span><span class="n">seccfg</span><span class="p">.</span><span class="n">hash</span><span class="p">,</span> <span class="n">hash</span><span class="p">,</span> <span class="n">HASH_SZ</span><span class="p">);</span>

    <span class="n">mmc_write_block</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g_mmc_dev</span><span class="p">,</span> <span class="n">seccfg_start</span><span class="p">,</span> <span class="o">&amp;</span><span class="n">seccfg</span><span class="p">);</span>

    <span class="n">u32</span> <span class="n">chkcode_block</span> <span class="o">=</span> <span class="n">gpt_get_start</span><span class="p">(</span><span class="o">&amp;</span><span class="n">gpt</span><span class="p">,</span> <span class="s">"chkcode"</span><span class="p">)</span> <span class="o">+</span> <span class="mi">4</span><span class="p">;</span>

    <span class="n">mmc_read_block</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g_mmc_dev</span><span class="p">,</span> <span class="n">chkcode_block</span><span class="p">,</span> <span class="n">chkcode_buf</span><span class="p">);</span>

    <span class="o">*</span><span class="p">(</span><span class="n">u32</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">chkcode_buf</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">=</span> <span class="n">CHKCODE_MAGIC1</span><span class="p">;</span>
    <span class="o">*</span><span class="p">(</span><span class="n">u32</span> <span class="o">*</span><span class="p">)</span><span class="o">&amp;</span><span class="n">chkcode_buf</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">=</span> <span class="n">CHKCODE_MAGIC2</span><span class="p">;</span>

    <span class="n">mmc_write_block</span><span class="p">(</span><span class="o">&amp;</span><span class="n">g_mmc_dev</span><span class="p">,</span> <span class="n">chkcode_block</span><span class="p">,</span> <span class="n">chkcode_buf</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>I sent the payload and python script to 760ceb3b9c0ba4872cadf3ce35a7a494, and they were able to successfully unlock the bootloader of their device!</p>

<table>
  <thead>
    <tr>
      <th> </th>
      <th> </th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><img src="/media/posts/2026/ky42c/orange_state.png" alt="orange_state" /></td>
      <td><img src="/media/posts/2026/ky42c/firmware_version.png" alt="firmware_version" /></td>
    </tr>
  </tbody>
</table>

<p>While at it, I also made a <a href="https://github.com/shomykohai/kyocera-ky-42c-unlock/blob/main/payload/src/patch.S">smaller payload</a> to patch Preloader security, jump back to it, and allow loading unsigned DAs, so that we could get a full dump of the device (available <a href="https://files.kaerulabs.com/k/android/firmwares/kyocera/ky-42c">here</a> with only firmware partitions).</p>

<h2 id="modding-attempt">Modding attempt</h2>

<p>A few days after unlocking the bootloader, a new cert exploit (similar to <code class="language-plaintext highlighter-rouge">CVE-2023-20696</code>) was made public, which would allow us to boot a patched lk image regardless of the Preloader sec policy.
Thanks to <code class="language-plaintext highlighter-rouge">lkpatcher</code>, I was able to quickly patch the lk image, and send it to 760ceb3b9c0ba4872cadf3ce35a7a494 to test it on their device.
Suprisingly, the device would not boot, and we were unable to grab any logs from the device.</p>

<p><img src="/media/posts/2026/ky42c/expdb_logs_zeroed.png" alt="expdb logs being all zeros" /></p>

<p>I suspect <code class="language-plaintext highlighter-rouge">expdb</code> logs were not being written during boot for some reason, and without UART access, we couldn’t figure out what was going on, so we gave up on this attempt for the time being.</p>

<h2 id="conclusions">Conclusions</h2>

<p>Investigating this device was a fun and unusual experience from my usual MediaTek reverse engineering work, and was a good opportunity to test both my knowledge and payload framework.</p>

<p>Thanks to <a href="https://760ceb3b9c0ba4872cadf3ce35a7a494.neocities.org">@760ceb3b9c0ba4872cadf3ce35a7a494</a> for taking the time to test all the payloads and scripts that I’ve sent them.
Thanks also to <a href="https://blog.r0rt1z2.com">@R0rt1z2</a> for some of the code snippets and scripts I used in my payload, as well for <code class="language-plaintext highlighter-rouge">lkpatcher</code>, which was a great help in the modding attempt.</p>

<p>All instructions for unlocking the bootloader are available in the <a href="https://github.com/shomykohai/kyocera-ky-42c-unlock">GitHub repository</a>!</p>

<p>I hope you enjoyed reading this post! See ya!</p>]]></content><author><name>Shomy</name></author><category term="posts" /><category term="Android Modding" /><category term="Bootloader Unlocking" /><category term="Reverse Engineering" /><summary type="html"><![CDATA[Introduction]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://shomy.is-a.dev/media/posts/2026/ky42c/a202kc.jpg" /><media:content medium="image" url="https://shomy.is-a.dev/media/posts/2026/ky42c/a202kc.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Carbonara: The MediaTek exploit nobody served</title><link href="https://shomy.is-a.dev/blog/article/serving-carbonara" rel="alternate" type="text/html" title="Carbonara: The MediaTek exploit nobody served" /><published>2026-01-11T00:00:00+00:00</published><updated>2026-01-11T00:00:00+00:00</updated><id>https://shomy.is-a.dev/blog/article/serving-carbonara</id><content type="html" xml:base="https://shomy.is-a.dev/blog/article/serving-carbonara"><![CDATA[<p>Imagine this:
You walk into a restaurant you only just discovered, the one rumored for serving the best dishes.
It quickly becomes your go-to place for every meal.
One day, something odd happens: a secret dish appears on the menu, <em>Carbonara</em>, no price, description, and most importantly, no way to order it.
Only few people know the secret code that makes the chef cook it.</p>

<p>I too wasn’t supposed to know, but that’s how every story begins, after all.</p>

<h2 id="how-it-all-began">How it all began</h2>

<p>Around february of 2025, I <a href="https://shomy.is-a.dev/blog/article/unlocking-the-motorola-g23">unlocked the Motorola G23</a> with the help of some people from the Motorola Helio G85 telegram group.
This was a great victory after almost 2 years from the device release date.</p>

<p>In 2024, the Motorola G24 got released, same SoC, same specs, but totally different bootloader!
After unlocking the G23, we tried to unlock the G24 as well, but the method that worked on it was not applicable on the G24.
For two weeks, we thought it was impossible to unlock.. until</p>

<p>milktoast56 enters the group, asking why a <em>known GSM tool</em> could unlock the bootloader, just for it to still claim to be locked.
Me and the other people were surprised and didn’t believe it until we saw that the bootloader unlock operation was indeed happening.</p>

<p>We suspected the bootloader was auto relocking itself, so <a href="https://github.com/R0rt1z2">Roger</a> (see previous blogpost) analyzed the bootloader (lk) in ghidra, to find, guess what, the relock function.</p>

<p><img src="/media/posts/2026/carbonara/relock_function.png" alt="tinno_commercial_device_force_lock" style="width: 60%;" /></p>

<p>So, we tested something.
Roger ported chouchou (the lk payload for G23) to G24, patching the relock function. To our surprise, that worked, and the G24 bootloader got unlocked!</p>

<p>Up until august, we relied on the paid tool to perform the unlock operation, until I decided to look deeper into the matter.</p>

<h2 id="what-is-going-on">What is going on?</h2>

<p>We already tested that mtkclient could not unlock the bootloader, so with Ryszard (another user from the group) we tried to understand what was going on with this tool.</p>

<p>We started with the easiest way: sniffing usb traffic.</p>

<p>Ryszard installed wireshark and started sending me usb traffic from the tool to the device.
For about a week, we thought this tool just had an engineering Download Agent.
I extracted the DA from the usb logs, and made a python script to parse it.
It looked different, but also too similar to the stock one.
I thought that it just had different patches as a consequence of being an engineering DA.</p>

<p>I made another script, to construct back a DA file + header so that mtkclient could then use it.</p>

<p><img src="/media/posts/2026/carbonara/mtkclient-failing.png" alt="MTKClient fails on uploading the extracted DA" style="width: 70%;" /></p>

<blockquote>
  <p>What??
The same binary was being sent, yet we get a verification error.</p>
</blockquote>

<p>This happens because DA1 verifies the integrity of DA2 before jumping to it, by hashing the received DA2 and comparing it to the expected hash embedded in DA1 itself.
This RoT works when DAA (Download Agent Authorization) is enabled, because this security measure allows only signed DA1 to be loaded.</p>

<p>Something was indeed happening in that tool, so for a few days I studied the XFlash protocol to finally get an answer</p>

<h3 id="two-boot_to-cmd-calls">Two boot_to cmd calls?</h3>

<p>The boot-to command is invoked to load the second stage DA (DA2) into DRAM, from the first stage DA (DA1), and then jump to it.
This command includes the DA2 size, load address and sha256 hash, so that DA1 can verify the integrity of the DA2 before jumping to it.</p>

<p><img src="/media/posts/2026/carbonara/wireshark-packet-2.png" alt="Wireshark packet sent by the host" style="width: 70%;" /></p>

<p>What is this weird string it’s sending?
I thought jokingly: “Imagine if it’s a sha256 hash”
So, i ran sha256sum on the da2 binary and..</p>

<blockquote>
  <p>The hashes match!! So, the tool is patching memory??</p>
</blockquote>

<p>But what does the payload it’s sending before mean?</p>

<p><img src="/media/posts/2026/carbonara/wireshark-packet.png" alt="Wireshark packet sent by the host" style="width: 70%;" /></p>

<p>So, me and Roger studied it, to conclude that all it does is locate the DA2 hash stored in DA1 memory, and overwrite it with the hash of the patched DA2.
This means that when DA1 receives the boot_to command the second time, it will verify the patched DA2 against the patched hash, and the verification will succeed!</p>

<h2 id="replicating-the-exploit-with-mtkclient">Replicating the exploit with mtkclient</h2>

<p>Now that I understood how the exploit worked, it was time to replicate it to try unlocking the G24 for free™.</p>

<p>I made a quick patch and sent it to the group to test on G24</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
</pre></td><td class="rouge-code"><pre><span class="c1"># xflash_lib.py, line 1226
</span><span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="nf">xsend</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">Cmd</span><span class="p">.</span><span class="n">BOOT_TO</span><span class="p">):</span>
    <span class="n">payload</span> <span class="o">=</span> <span class="nb">bytes</span><span class="p">.</span><span class="nf">fromhex</span><span class="p">(</span><span class="sh">'</span><span class="s">a4de2200000000002000000000000000</span><span class="sh">'</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="nf">xsend</span><span class="p">(</span><span class="n">payload</span><span class="p">):</span>
        <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="nf">status</span><span class="p">()</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
            <span class="kn">import</span> <span class="n">hashlib</span>
            <span class="n">da_hash</span> <span class="o">=</span> <span class="n">hashlib</span><span class="p">.</span><span class="nf">sha256</span><span class="p">(</span><span class="n">self</span><span class="p">.</span><span class="n">daconfig</span><span class="p">.</span><span class="n">da2</span><span class="p">).</span><span class="nf">digest</span><span class="p">()</span>
            <span class="k">if</span> <span class="n">self</span><span class="p">.</span><span class="nf">xsend</span><span class="p">(</span><span class="n">da_hash</span><span class="p">):</span>
                <span class="n">self</span><span class="p">.</span><span class="nf">status</span><span class="p">()</span>
                <span class="n">self</span><span class="p">.</span><span class="nf">info</span><span class="p">(</span><span class="sh">"</span><span class="s">All good!</span><span class="sh">"</span><span class="p">)</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><img src="/media/posts/2026/carbonara/mtkclient_unlock.jpg" alt="mtkclient unlocking the G24" style="width: 70%;" /></p>

<blockquote>
  <p>IT WORKED!</p>
</blockquote>

<p>So, now everyone could unlock their G24 for free, without relying on paid tools.
But, I wanted to go further.</p>

<h2 id="penumbra">Penumbra</h2>

<p>With the exploit replicated in mtkclient, I decided to take the opportunity to write my own MediaTek flashing tool.</p>

<p>I’d been wanting to learn Rust for a long time, so I chose this as my first real Rust project.
That’s how <a href="https://github.com/shomykohai/penumbra">Penumbra</a> was born!</p>

<p>What started as a small proof of concept quickly grew into a more complete tool.
At the time of writing, Penumbra has matured quite rapidly and now includes both a TUI and a CLI, called <strong>Antumbra</strong>.</p>

<p><img src="/media/posts/2026/carbonara/antumbra-tui.png" alt="Antumbra, a TUI powered by Penumbra" style="width: 70%;" /></p>

<p>The final goal of Penumbra is to be a reliable, free and open source tool for MediaTek devices.</p>

<p>More importantly, every payload and every patch is completely auditable and available for self-recompilation, meaning everyone can easily see and compile Penumbra for themselves without blindly trusting the compiled code.</p>

<h2 id="so-how-does-carbonara-actually-work">So, how does Carbonara actually work?</h2>

<p>Carbonara is, at its core, a surprisingly simple exploit.
Everything comes down to <strong>why loading a patched DA2 is possible in the first place</strong>.</p>

<p>On unpatched loaders, the DA2 load address and size are fully user controlled.
This means the host can, in fact, write to any memory region without checks, and free cache invalidation by the DA!</p>

<p>While all known tools implement Carbonara by replacing the DA2 hash to make DA1 accept the next stage, the exploit itself is in fact far more dangerous, allowing to load any arbitrary payload, malicious one included!</p>

<p>This is why all security researchers aware of this exploit decided to keep it private.
However, more and more paid tools started adding this exploit, which resulted in a higher risk of getting infected by rootkits or malware as a consequence.</p>

<p>More details about the exploit can be found in <a href="https://shomy.is-a.dev/penumbra/Mediatek/Exploits/Carbonara">Penumbra documentation’s</a>. and the source <a href="https://github.com/shomykohai/penumbra/blob/main/core/src/exploit/carbonara.rs">code itself</a></p>

<h2 id="conclusions">Conclusions</h2>

<p>So, now you know the story of how I (re)discovered Carbonara, the MediaTek exploit nobody served.<br />
I think this story is not just about an exploit or how unlocking bootloader is possible, but also about how many times we trust closed source tools without knowing what they do under the hood, and how important it is to have open source alternatives.</p>

<p>Special thanks to:</p>
<ul>
  <li><a href="https://github.com/R0rt1z2">Roger (R0rt1z2)</a> for all the help with reverse engineering and testing</li>
  <li>Ryszard for helping with usb traffic analysis</li>
  <li>milktoast56 for bringing up the initial suspicion that led to the discovery</li>
  <li>CXZa for the picture of mtkclient unlocking the G24</li>
  <li>The Motorola Helio G85 telegram group for the support and testing</li>
  <li><a href="https://github.com/bkerler">B. Kerler</a> for mtkclient, which helped me understand MTK protocols</li>
</ul>]]></content><author><name>Shomy</name></author><category term="posts" /><category term="Android Modding" /><category term="Bootloader Unlocking" /><category term="Reverse Engineering" /><summary type="html"><![CDATA[Imagine this: You walk into a restaurant you only just discovered, the one rumored for serving the best dishes. It quickly becomes your go-to place for every meal. One day, something odd happens: a secret dish appears on the menu, Carbonara, no price, description, and most importantly, no way to order it. Only few people know the secret code that makes the chef cook it.]]></summary></entry><entry><title type="html">Unlocking the Motorola G23 (and some words on Motorola)</title><link href="https://shomy.is-a.dev/blog/article/unlocking-the-motorola-g23" rel="alternate" type="text/html" title="Unlocking the Motorola G23 (and some words on Motorola)" /><published>2025-02-13T00:00:00+00:00</published><updated>2025-02-13T00:00:00+00:00</updated><id>https://shomy.is-a.dev/blog/article/unlocking-the-motorola-g23</id><content type="html" xml:base="https://shomy.is-a.dev/blog/article/unlocking-the-motorola-g23"><![CDATA[<p>Unlocking the Motorola G23 was not an easy task, but we finally did it!</p>

<p>In this article, I’ll explain how I and a team of enthusiasts managed to unlock the bootloader of the Motorola G23, a device that was previously thought to be unlockable.</p>

<h2 id="the-motorola-g23">The Motorola G23</h2>

<p>The Motorola Moto G23 is a smartphone that was released in January 2023.
Out of the box, the device might seem quite clean and minimal, but that all changed when Android 14 came.</p>

<p>I’ve been looking for to mod the device when Android 13 was still the latest version, but I couldn’t find any information on XDA developers, or any other forums.</p>

<p>So I started my own research.</p>

<h2 id="the-first-tries">The first tries</h2>

<p>I started by looking for known ways to unlock previous Motorola phones, starting by G22.<br /></p>

<p>Here, I found out about how <a href="https://github.com/bkerler/mtkclient">mtkclient</a> was used to unlock the bootloader on G22, so I thought giving it a try.<br />
Unfortunately, the G23 BROM was blocked compared to G22, and only the preloader port was accessible.<br />
This wouldn’t be a problem, if only the preloader wasn’t patched! Crash DA wasn’t available, and the device would just reboot into preloader mode.<br /><br /></p>

<p>Just like that, I was stuck.<br /><br /></p>

<p>So, I tried extracting the firmware from the official rescue software, RSA, and I was able to find both the stock firmware and the flash tool, the latter being an encrypted zip file.<br />
In the firmware I found a file named <code class="language-plaintext highlighter-rouge">MT6768_USER.bin</code>, which seemed to work at some extent. I tried using mtkclient’s <code class="language-plaintext highlighter-rouge">da unlock</code> command, and it failed.<br />
I knew RSA was able to flash stock firmware somehow, so tried getting the flash tool RSA used, and I was able to get the Flash Tool unencrypted by making RSA start a rescue, in which the tool had to be extracted beforehand.<br />
In the Flash Tool, I’ve found another Download Agent, which I used with mtkclient, and this one booted too! Unfortunately, it seemed like the DA wasn’t able to read partitions or flash anything.<br /><br /></p>

<p>Some months later, thanks to <a href="https://github.com/progzone122">@DiabloSat</a>, we were able to use this DA file to dump the firmware.<br /><br /></p>

<p>These tries were all made in early 2024, and I decided to just not bother with the device anymore, until Android 14 came.</p>

<h2 id="the-helio-g85-telegram-group">The Helio G85 telegram group</h2>

<p>In late 2024, I once again tried to look up for an unlocking method for the device, because of how laggy the device became with Android 14.
I searched for <code class="language-plaintext highlighter-rouge">penangf</code> (Moto G23 codename) on GitHub, and I found that someone was able to extract the Flash Tool from RSA, the same way I did.<br /><br /></p>

<p>I looked at the Flash Tool files once again, and thought about how I could use it to unlock the bootloader.<br />
Unfortunately, as already said before, the Download Agent was not able to read partitions or flash anything that wasn’t signed.<br />
I decided to look up on telegram, and I found a group where people were discussing on ways to unlock the bootloader, and found out that <a href="https://github.com/progzone122">DiabloSat</a>, the same person which uploaded the flash tool on GitHub, was on the group.<br /><br /></p>

<p>For some time, I occasionally checked the group to see if someone managed to unlock the bootloader. Unfortunately, no one did.<br /><br /></p>

<p>Near Novemeber 2024, I decided to open an issue on the Flash Tool repository, after I’ve found out we could use other flash tools to flash the firmware.<br /><br /></p>

<p>Unfortunately, this concluded nothing, and I was stuck once again, and decided to once again occasionally check the group.</p>

<h2 id="the-testpoints">The testpoints</h2>

<p>Around December 2024, I found another repository popup on GitHub with the schematics for the phone, and I found out that the device had testpoints, which could be used to force BROM.</p>

<p>I decided to open an issue on the schematics repository, and I was able to get in touch with DiabloSat, who uploaded the schematics.</p>

<p>From that day to until February 2025, me and DiabloSat teamed up to hopefully unlock the bootloader of the phone.<br />
We shared ideas, possible testpoints and more, but unfortunately, we couldn’t find a way to force BROM mode.</p>

<p>I started to document all the discoveries we made, and Diablo started contributing too. All the documentation can now be accessed <a href="https://penangf.fuckyoumoto.xyz">here</a>.</p>

<p>As a side quest to the other researches we were making, I started decompiling the bootloader (lk, which is responsible for integrating the fastboot protocol) and the preloader (first stage of boot after the BootROM), to find out how the device worked.<br />
Here I found out that the preloader has a trigger to reboot into DOWNLOAD_MODE (aka, what we wanted) with a specific combinationof keys:</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="rouge-code"><pre><span class="cp">#define KPDL1 KPCOL0 // 0
#define KPDL2 PWRKEY_HW // 8
#define KPDL3 HOMEKEY_RST // 17
</span>
<span class="n">bool</span> <span class="nf">are_dl_keys_pressed</span><span class="p">()</span>
<span class="p">{</span>
    <span class="k">if</span><span class="p">(</span><span class="n">mtk_detect_key</span><span class="p">(</span><span class="n">KPDL1</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">mtk_detect_key</span><span class="p">(</span><span class="n">KPDL2</span><span class="p">)</span> <span class="o">&amp;&amp;</span> <span class="n">mtk_detect_key</span><span class="p">(</span><span class="n">KPDL3</span><span class="p">))</span>
    <span class="p">{</span>
        <span class="n">pr_debug</span><span class="p">(</span><span class="s">"dl keys are pressed</span><span class="se">\n</span><span class="s">"</span><span class="p">);</span>
        <span class="k">return</span> <span class="nb">true</span><span class="p">;</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="nb">false</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="rouge-code"><pre><span class="cp">#define MODULE "[PLFM]"
</span>
<span class="kt">void</span> <span class="nf">platform_emergency_download</span><span class="p">(</span><span class="kt">int</span> <span class="n">timeout</span><span class="p">)</span>
<span class="p">{</span>
    <span class="n">pr_debug</span><span class="p">(</span><span class="s">"%S emergency download mode(timeout=%d)</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">MODULE</span><span class="p">,</span> <span class="n">timeout</span><span class="p">);</span>

    <span class="n">platform_safe_mode</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">timeout</span><span class="p">);</span>

    <span class="n">mtk_arch_reset</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span>

    <span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p>One of these keys is the KPCOL0 testpoint, which was tested thanks to DiabloSat.</p>

<p><img src="/media/posts/2025/penangf_mb_front_tp.png" alt="Moto G23 Testpoints" />
<em>Photo by DiabloSat</em></p>

<p>We were able to find the logs in the <code class="language-plaintext highlighter-rouge">expdb</code> partition, in which the phone seemed to indeed call the <code class="language-plaintext highlighter-rouge">platform_emergency_download</code> function, but unfortunately, the device would just reboot into preloader mode.</p>

<p>This, on a future retrospective, can be explained by the fact that the device is fused, which means the usb enumeration for download mode happens during preloder initialization, and not in BROM.</p>

<h2 id="decompiling-the-bootloader">Decompiling the bootloader</h2>

<p>While DiabloSat was testing more testpoints and trying to force mtkclient to send the device to BROM, I decided to decompile <strong>lk.img</strong>, the bootloader of the device.<br /></p>

<p>There I found something really interesting, our device was indeed able to be unlocked, compared to what official sources said.<br /></p>

<p>The problem was, the device needed a key to be unlocked.<br />
This obviously made things a lot harder, since it would have meant that we had to bruteforce the key or figure out the algorithm that checks the key.</p>

<p>One day Diablo found out about two important commands for us: <code class="language-plaintext highlighter-rouge">fastboot oem get_key</code> and <code class="language-plaintext highlighter-rouge">fastboot oem key &lt;KEY&gt;</code>.<br /></p>

<p>We had it, we thought.<br />
The key from the first command, unfortunately, was not the one we were looking for (or so, we thought at first).<br /><br /></p>

<p>I tried looking back at LK, and I found out the function that checks for the correct key, and, for a few days, I tried to reverse engineer the key, but I couldn’t.<br /></p>

<p>Then for another week or two, we focused on trying again to force BROM, and Diablo contacted <a href="https://github.com/R0rt1z2">R0rt1z2</a>, who apparently had experience with MediaTek devices.<br /></p>

<p>Roger bought a Moto G13, and he tried to try some of the testpoints we found, but unfortunately, BROM was apparently blocked by efuse, and unfortunately, his device broke.</p>

<p>We thought about exploiting a buffer overflow, but then..<br /><br /></p>

<p>I decided to finally put all my efforts on the only guaranteed to work method: reverse engineer how the key was generated from the phone.</p>

<p>Below, is the final algorithm I reverse engineered using Ghidra</p>

<div class="language-c highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
</pre></td><td class="rouge-code"><pre><span class="cp">#define UNLOCK_KEY_SIZE 32
#define SOC_ID "0123456789ABCDEF0123456789ABCDEF" // Generic SOC ID
</span>
<span class="kt">int</span> <span class="nf">fastboot_flashing_unlock_chk_key</span><span class="p">(</span><span class="kt">void</span><span class="p">)</span>
<span class="p">{</span>
    <span class="kt">char</span> <span class="n">unlock_key</span><span class="p">[</span><span class="n">UNLOCK_KEY_SIZE</span> <span class="o">+</span> <span class="mi">1</span><span class="p">];</span>
    <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">thing_to_hash</span><span class="p">[</span><span class="mi">65</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
    <span class="kt">unsigned</span> <span class="kt">char</span> <span class="n">hashed_value</span><span class="p">[</span><span class="mi">64</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span><span class="mi">0</span><span class="p">};</span>
    <span class="kt">int</span> <span class="n">len</span><span class="p">;</span>

    <span class="n">memset</span><span class="p">(</span><span class="n">thing_to_hash</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">65</span><span class="p">);</span>

    <span class="n">len</span> <span class="o">=</span> <span class="n">strlen</span><span class="p">(</span><span class="n">SOC_ID</span><span class="p">);</span>
    <span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o">==</span> <span class="n">UNLOCK_KEY_SIZE</span><span class="p">)</span> <span class="p">{</span>
        <span class="n">fastboot_info</span><span class="p">(</span><span class="n">SOC_ID</span><span class="p">);</span>
        <span class="n">mtk_memcpy</span><span class="p">(</span><span class="n">thing_to_hash</span><span class="p">,</span> <span class="n">SOC_ID</span><span class="p">,</span> <span class="n">UNLOCK_KEY_SIZE</span><span class="p">);</span>
        <span class="n">mtk_memcpy</span><span class="p">(</span><span class="n">thing_to_hash</span> <span class="o">+</span> <span class="mi">32</span><span class="p">,</span> <span class="n">thing_to_hash</span><span class="p">,</span> <span class="mi">32</span><span class="p">);</span>

        <span class="n">fastboot_info</span><span class="p">(</span><span class="s">"start fastboot unlock"</span><span class="p">);</span>
        <span class="n">fastboot_info</span><span class="p">(</span><span class="n">fb_unlock_key_str</span><span class="p">);</span>

        <span class="n">printf</span><span class="p">(</span><span class="s">"To hash: %s</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">thing_to_hash</span><span class="p">);</span>

        <span class="c1">// This calculates the hash of the SOC_ID and stores it in hashed_value</span>
        <span class="n">sha256</span><span class="p">(</span><span class="n">thing_to_hash</span><span class="p">,</span> <span class="mi">64</span><span class="p">,</span> <span class="n">hashed_value</span><span class="p">);</span>
        


        <span class="n">len</span> <span class="o">=</span> <span class="n">strncmp</span><span class="p">(</span><span class="n">fb_unlock_key_str</span><span class="p">,</span> <span class="p">(</span><span class="kt">char</span><span class="o">*</span><span class="p">)</span><span class="n">hashed_value</span><span class="p">,</span> <span class="n">UNLOCK_KEY_SIZE</span><span class="p">);</span>
        <span class="k">if</span> <span class="p">(</span><span class="n">len</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
            <span class="n">fastboot_fail</span><span class="p">(</span><span class="s">"Unlock key code is incorrect!"</span><span class="p">);</span>
            <span class="k">return</span> <span class="mh">0x7001</span><span class="p">;</span>
        <span class="p">}</span>

        <span class="n">fastboot_info</span><span class="p">(</span><span class="s">"Unlock Success"</span><span class="p">);</span>
        <span class="n">len</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">}</span>
    <span class="k">else</span> <span class="p">{</span>
        <span class="n">len</span> <span class="o">=</span> <span class="mh">0x7000</span><span class="p">;</span>
        <span class="n">fastboot_fail</span><span class="p">(</span><span class="s">"Unlock key length is incorrect!"</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="k">return</span> <span class="n">len</span><span class="p">;</span>
<span class="p">}</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<p><img src="/media/posts/2025/unlock_key_algorithm.png" style="width: 50%; heigth: 50%" /></p>

<p>And just like that, I made a python script and asked the other members of the team to try it out.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
</pre></td><td class="rouge-code"><pre>
<span class="k">def</span> <span class="nf">oem_keygen</span><span class="p">(</span><span class="n">key</span><span class="p">:</span> <span class="nb">str</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="nb">str</span><span class="p">:</span>
    <span class="n">to_hash</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="n">key</span> <span class="o">*</span> <span class="mi">2</span>

    <span class="nb">hash</span><span class="p">:</span> <span class="nb">str</span> <span class="o">=</span> <span class="nf">sha256</span><span class="p">(</span><span class="n">to_hash</span><span class="p">.</span><span class="nf">encode</span><span class="p">()).</span><span class="nf">hexdigest</span><span class="p">()</span>

    <span class="nf">print</span><span class="p">(</span><span class="sh">"</span><span class="s">Unlock key: %s</span><span class="sh">"</span> <span class="o">%</span> <span class="p">(</span><span class="nb">hash</span><span class="p">[:</span><span class="mi">32</span><span class="p">]))</span>
    <span class="k">return</span> <span class="nb">hash</span>
</pre></td></tr></tbody></table></code></pre></div></div>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
</pre></td><td class="rouge-code"><pre>➜  fuckyoumoto git:<span class="o">(</span>main<span class="o">)</span> ✗ python oem_keygen.py 061A757D042B2A378D9761E60C9D3FBC
Unlock key: 87f3aef774eb3edbcdef39e2e94d05c9

➜  fuckyoumoto git:<span class="o">(</span>main<span class="o">)</span> ✗ fastboot oem key 87f3aef774eb3edbcdef39e2e94d05c9 
<span class="o">(</span>bootloader<span class="o">)</span> open fastboot unlock
OKAY <span class="o">[</span>  0.000s]
Finished. Total <span class="nb">time</span>: 0.000s
➜  fuckyoumoto git:<span class="o">(</span>main<span class="o">)</span> ✗ fastboot flashing unlock
<span class="o">(</span>bootloader<span class="o">)</span> Start unlock flow

<span class="o">(</span>bootloader<span class="o">)</span> 061A757D042B2A378D9761E60C9D3FBC
<span class="o">(</span>bootloader<span class="o">)</span> start fastboot unlock
<span class="o">(</span>bootloader<span class="o">)</span> 87f3aef774eb3edbcdef39e2e94d05c9
<span class="o">(</span>bootloader<span class="o">)</span> Unlock Success
<span class="o">(</span>bootloader<span class="o">)</span> fastboot unlock success
OKAY <span class="o">[</span>  5.320s]
Finished. Total <span class="nb">time</span>: 5.320s
➜  fuckyoumoto git:<span class="o">(</span>main<span class="o">)</span> ✗ fastboot oem lks
<span class="o">(</span>bootloader<span class="o">)</span> lks <span class="o">=</span> 0
OKAY <span class="o">[</span>  0.005s]
Finished. Total <span class="nb">time</span>: 0.005s
</pre></td></tr></tbody></table></code></pre></div></div>

<blockquote>
  <p>We did it.<br />
The bootloader got unlocked!</p>
</blockquote>

<h2 id="moving-forward">Moving forward</h2>

<p>We were able to unlock the bootloader of the Motorola G23, and thanks to <a href="https://github.com/R0rt1z2">Roger</a>, who got another G13 with UART too, we were able to confirm that the same method worked on the G13.</p>

<p>The days later were spent on trying to find a way to boot TWRP (thanks <a href="https://github.com/GitFASTBOOT">@GitFASTBOOT</a> for making it), and testing GSIs.<br /></p>

<p><img src="/media/posts/2025/twrp_mainscreen.jpg" alt="TWRP main screen" style="width: 30%; heigth: 30%" /></p>

<p>The plans for the future are to try to port LineageOS, spread the word about the device and how it can be unlocked, and make it faster!</p>

<h2 id="chouchou-custom-bootloader">Chouchou (Custom bootloader)</h2>

<p>Now that the phone got unlocked, Roger decided to build a payload to inject code into LK, to be able to add new features to fastboot.</p>

<p><img src="/media/posts/2025/chouchou_injection.png" alt="chouchou injection" /></p>

<p>This payload protects the device from being relocked and blocks flashing of protected partitions that might hard brick the device.</p>

<p>I suggest using this custom bootloader for safety reasons, mainly avoiding malicious script (considering the default flash script in the firmware) to completely brick the phone.<br /><br /></p>

<p>To get the custom bootloader you can either <a href="https://github.com/R0rt1z2/chouchou">compile it from source</a>, or get pre-compiled and tested binaries <a href="https://penangf.fuckyoumoto.xyz/docs/modding/custom-bootloader">on the documentation</a>.</p>

<h2 id="video-guide">Video Guide</h2>

<p>This is a quick video guide made by DiabloSat showing off how to unlock the phone + the installation of the custom bootloader.</p>

<iframe height="315" src="https://www.youtube-nocookie.com/embed/3fHfiqM7UUg" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen=""></iframe>

<h2 id="making-the-phone-faster">Making the phone faster</h2>

<blockquote>
  <p>NOTE: This section was added after the original blog post, as I felt some of this information is important to mention.</p>
</blockquote>

<p>Diablo decided to make a debloated version of the firmware, and I helped with it.</p>

<p>While making the build, I (for some not so specific reason) decided to analyse network traffic in idle, to then discover how Moto has many system apps and services in its stock firmware that allows them to completely lock you out from using the device. Most notably PAKS (present in all Motorola devices) and SysDLL (known as <code class="language-plaintext highlighter-rouge">co.sitic.pp</code>).</p>

<p>The first is moto in house MDM system, which is completely integrated into SystemUI to force lock you out.</p>

<p>The latter is yet another MDM app, specific to South American carriers. This app in particular is what can be considered spyware. It’s specifically hidden within the application list (can be confirmed by decompiling the settings appnwith jadx) and every ~10-15 seconds it sends a POST request to various servers with the 1st IMEI to know if to lock the device.</p>

<p>We removed it in DebloatedMotoWeed, as well as removing bloatware, invasive ads and more from the firmware.</p>

<h2 id="some-words-on-motorola">Some words on Motorola</h2>

<p>It is now clear that Motorola doesn’t care of its customers, as it can be seen on how even flagships are full of ads, blaterare and more.</p>

<p>Moto g23 in particular is the perfect example of how low-mid range devices (mainky mediatek and unisoc) are not in their interest. This is also proven by the fact that these phones are not even produced by them, but by third party ODMs (Huaqin for g23/g13, Tinno for g24 and g15).</p>

<p>The moto g23 has an artificially inserted limitations for not allowing bootloader unlock, as Moto Agents confirm when affirming “Some devices are designed to not allow bootloader unlocking”. And a worse case is the moto g24, which can only be unlocked (recent discovery we made) by having an engineer download agent, and by manually patching the auto relocked feature Tinno put in the bootloader.</p>

<p>Furthermore, it appears Moto (Lenovo) imposes its ODMs to include referral code that gets injected into Android props (in g23 firmware I found referrals for Facebook Ads, Spotify and  some others).</p>

<p>Motorola is clearly only interested in shipping as many phones as possible, all while making it impossible to unlock on many models, or by removing your warranty on supported models just by getting (and not even using) the unlock code. Don’t buy a Motorola phone if you care about your freedom of having a phone that lasts more than a year and doesn’t artificially get slowed down.</p>

<h2 id="conclusion">Conclusion</h2>

<p>We finally were able to unlock the bootloader of the phone, after 2 years from its release date, finally making the device fully ours.</p>

<p>Special thanks to DiabloSat, R0rt1z2 (Roger) for helping me out with this project, GitFASTBOOT for making TWRP, and everyone else who helped us out.<br /><br /></p>

<p>Check <a href="https://penangf.fuckyoumoto.xyz">our documentation</a> for more information on the device!</p>]]></content><author><name>Shomy</name></author><category term="posts" /><category term="Android Modding" /><category term="Bootloader Unlocking" /><summary type="html"><![CDATA[Unlocking the Motorola G23 was not an easy task, but we finally did it!]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://shomy.is-a.dev/media/posts/2025/chouchou_injection.png" /><media:content medium="image" url="https://shomy.is-a.dev/media/posts/2025/chouchou_injection.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How I handle quests in godot</title><link href="https://shomy.is-a.dev/blog/article/how-i-handle-quests-in-godot" rel="alternate" type="text/html" title="How I handle quests in godot" /><published>2024-12-06T00:00:00+00:00</published><updated>2024-12-06T00:00:00+00:00</updated><id>https://shomy.is-a.dev/blog/article/how-i-handle-quests-in-godot</id><content type="html" xml:base="https://shomy.is-a.dev/blog/article/how-i-handle-quests-in-godot"><![CDATA[<p>For a long time, I’ve been maintaining a quest addon for Godot 4, <a href="https://github.com/shomykohai/quest-system">QuestSystem</a>, which I initially intended as a simple system for my projects.<br /><br />
<strong>But how do i use it?</strong></p>

<p><a href="https://shomy.is-a.dev/quest-system"><img src="/media/posts/2024/quest_system_banner.png" alt="QuestSystem Banner" /></a></p>

<h2 id="how-to-get-started-with-questsystem">How to get started with QuestSystem</h2>

<p>Before anything else, how do I install the addon in my projects?<br /></p>

<p>Thankfully, it’s as easy as opening Godot and searching it on the asset store!</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center">First step</th>
      <th style="text-align: center">Second step ⠀</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><img src="/media/posts/2024/godot_asset_lib_quest_system.png" alt="Godot Asset Lib (First step)" /></td>
      <td style="text-align: center"><img src="/media/posts/2024/godot_asset_lib_quest_system_download.png" alt="Quest System Download (Second step)" /></td>
    </tr>
  </tbody>
</table>

<p>Then you’re ready to start working with QuestSystem!</p>

<h2 id="how-i-design-the-quests-code">How I design the quests’ code</h2>

<p>Being QuestSystem a generalized system, it may be hard for newcomers to understand how to work with it, and they may prefer more user-friendly addons, like <a href="https://github.com/TheWalruzz/godot-questify">Questify</a> (which provides a Graph Node based approach).
<br /><br /></p>

<blockquote>
  <p>QuestSystem is intended to be easy, but <u>requires at least some coding knowledge.</u><br /></p>
</blockquote>

<p>While I prefer a code-based approach, it’s not scalable to some extent, and does not play well with Godot’s <strong>composition over inheritance</strong> philosophy.<br /></p>

<p>That’s why I designed my <a href="https://github.com/shomykohai/advanced-quest-system-example/blob/main/quests/scripts/base_quest.gd">quest script</a> to be just one file that handles all quest resources.</p>

<h3 id="breakdown-of-the-basequestresource-class">Breakdown of the BaseQuestResource class</h3>

<p>Here’s a breakdown of the symbols in the <code class="language-plaintext highlighter-rouge">BaseQuestResource</code> class</p>

<div class="language-gdscript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><table class="rouge-table"><tbody><tr><td class="rouge-gutter gl"><pre class="lineno">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
</pre></td><td class="rouge-code"><pre><span class="c1"># base_quest_resource.gd</span>
<span class="k">extends</span> <span class="n">Quest</span>
<span class="k">class_name</span> <span class="n">BaseQuestResource</span>

<span class="err">@</span><span class="k">export</span> <span class="k">var</span> <span class="n">steps</span><span class="p">:</span> <span class="kt">Array</span><span class="p">[</span><span class="n">QuestStep</span><span class="p">]</span>

<span class="c1"># Quest specific methods</span>

<span class="k">func</span> <span class="nf">start</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
    <span class="o">...</span>

<span class="k">func</span> <span class="nf">complete</span><span class="p">(</span><span class="o">...</span><span class="p">):</span>
    <span class="o">...</span>

<span class="k">func</span> <span class="nf">get_quest_step</span><span class="p">(</span><span class="n">idx</span><span class="p">:</span> <span class="kt">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">QuestStep</span><span class="p">:</span>
    <span class="o">...</span>

<span class="k">func</span> <span class="nf">complete_step</span><span class="p">(</span><span class="n">idx</span><span class="p">:</span> <span class="kt">int</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">Error</span><span class="p">:</span>
    <span class="o">...</span>

<span class="k">func</span> <span class="nf">get_first_uncompleted_step</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="n">QuestStep</span><span class="p">:</span>
    <span class="o">...</span>


<span class="c1"># Serialize and deserialize</span>

<span class="k">func</span> <span class="nf">serialize</span><span class="p">()</span> <span class="o">-&gt;</span> <span class="kt">Dictionary</span><span class="p">:</span>
    <span class="o">...</span>

<span class="k">func</span> <span class="nf">deserialize</span><span class="p">(</span><span class="n">data</span><span class="p">:</span> <span class="kt">Dictionary</span><span class="p">)</span> <span class="o">-&gt;</span> <span class="n">void</span><span class="p">:</span>
    <span class="o">...</span>

</pre></td></tr></tbody></table></code></pre></div></div>

<p>As you might have noticed, there’s a <code class="language-plaintext highlighter-rouge">QuestStep</code> class that has appeared.<br />
That’s exactly how I handle quests in my project: <strong>each</strong> quest has a collection of <strong>steps</strong> that <strong>need to be completed</strong> before the quest itself can be considered complete.</p>

<p>Here’s the code for <a href="https://github.com/shomykohai/advanced-quest-system-example/blob/main/quests/scripts/quest_step.gd"><code class="language-plaintext highlighter-rouge">QuestStep</code></a></p>

<h3 id="why-make-a-queststep-class">Why make a QuestStep class?</h3>

<p>QuestStep is a custom Resource that has to be extended to make more specific logic, and still allow for a composition approach.<br /></p>

<blockquote>
  <p>Making this new resource allows us to define a generalized <em>step</em> that can be reused for many different quests, and edit its properties directly in Godot’s Inspector.</p>
</blockquote>

<p>Here’s the example quest in the <a href="https://github.com/shomykohai/advanced-quest-system-example/">advanced-quest-system-example</a> repo:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center"><em>The inspector</em></th>
      <th style="text-align: center"><em>The quest step inspector</em></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center"><img src="/media/posts/2024/quest_system_example_inspector.png" alt="Quest System example inspector" /></td>
      <td style="text-align: center"><img src="/media/posts/2024/quest_system_example_inspector_step.png" alt="Quest Step inspector" /></td>
    </tr>
  </tbody>
</table>

<h2 id="the-power-of-a-modular-quest-system">The power of a modular quest system</h2>

<p>QuestSystem not only allows you to create custom quests, but also <strong>enables you to extend the manager (autoload) itself</strong>.</p>

<p>This is incredibly useful for iterating (or adapt) the quest system across different projects with different needs without needing to modify the addon itself.</p>

<p>To achieve this, you can create a new script that extends <code class="language-plaintext highlighter-rouge">QuestSystemManagerAPI</code>, implement new methods (or override existing ones), and update the autoload path in the project settings.</p>

<p><img src="/media/posts/2024/quest_system_settings_autoload.png" alt="Quest System Settings" /></p>

<h2 id="integrating-with-other-addons">Integrating with other addons</h2>

<p>In my projects, I often integrate QuestSystem with <a href="https://github.com/bitbrain/pandora">Pandora</a>, a fantastic addon to define RPG data and more.<br /></p>

<p>Furthermore, QuestSystem has been used with many other plugins, such as <a href="https://github.com/nathanhoad/godot_dialogue_manager">DialogueManager by Nathan Hoad</a>, <a href="https://github.com/dialogic-godot/dialogic">Dialogic by Emilio</a>, and has been used as template for <a href="https://github.com/Phazorknight/Cogito">Cogito’s QuestSystem</a>.</p>

<h2 id="useful-resources--projects-using-the-addon">Useful Resources &amp; projects using the addon</h2>

<p><strong>Projects &amp; Resources</strong></p>
<ul>
  <li><a href="https://shomy.is-a.dev/quest-system">QuestSystem documentation</a></li>
  <li><a href="https://github.com/shomykohai/quest-system">QuestSystem</a></li>
  <li><a href="https://github.com/shomykohai/advanced-quest-system-example">Advanced Quest System Example</a></li>
  <li><a href="https://github.com/Phazorknight/Cogito">Cogito</a></li>
</ul>

<p><strong>Videos using QuestSystem</strong></p>
<ul>
  <li>
    <table>
      <tbody>
        <tr>
          <td>Trobugno: [“What’s new? Quests and more</td>
          <td>Arkaruh’s Tale Devlog 2”](https://www.youtube.com/watch?v=xFB74hBJawA)</td>
        </tr>
      </tbody>
    </table>
  </li>
  <li>
    <table>
      <tbody>
        <tr>
          <td>LandonDevelops: [“Creating a quest system for my indie RPG game</td>
          <td>Godot devlog”](https://www.youtube.com/watch?v=D6X2Ex6m0vk)</td>
        </tr>
      </tbody>
    </table>
  </li>
</ul>

<h2 id="actually-designing-the-quests">Actually designing the quests</h2>

<p>To design the actual content of the quests, I use <a href="https://github.com/nathanhoad/godot_puzzle_dependencies">PuzzleDependencies</a> charts, which allow me to divide the quest flow into different logical steps.</p>

<p>Here’s a simple chart representing the <strong>“Help Nathan”</strong> quest in the Advanced Quest System Example project:
<img src="/media/posts/2024/help_nathan_quest_chart.png" alt="Help Nathan Chart" /></p>

<p>Doing this, it’s easy to see how the steps should be implemented and ordered.</p>

<p>Then I hop into Godot’s editor and make a new quest resource and fill in all necessary data (Quest name, description, etc).</p>

<p>I reuse the quest steps scripts (or make new ones if needed) and, again, fill the fields with the appropriate data (Name of the step, description, and specific data such as an Item to deliver or an NPC to interact with).</p>

<h2 id="conclusions">Conclusions</h2>

<p>This is just a small example of how I handle quests in my projects, but the addon is very flexible and can be used for many different purposes.<br /></p>]]></content><author><name>Shomy</name></author><category term="posts" /><category term="Gamedev" /><category term="Godot" /><category term="Addon" /><summary type="html"><![CDATA[For a long time, I’ve been maintaining a quest addon for Godot 4, QuestSystem, which I initially intended as a simple system for my projects. But how do i use it?]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://shomy.is-a.dev/media/posts/2024/quest_system_banner.png" /><media:content medium="image" url="https://shomy.is-a.dev/media/posts/2024/quest_system_banner.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">How do I get inspiration from music?</title><link href="https://shomy.is-a.dev/blog/article/how-do-i-get-inspiration-from-music" rel="alternate" type="text/html" title="How do I get inspiration from music?" /><published>2024-06-20T00:00:00+00:00</published><updated>2024-06-20T00:00:00+00:00</updated><id>https://shomy.is-a.dev/blog/article/how-do-i-get-inspiration-from-music</id><content type="html" xml:base="https://shomy.is-a.dev/blog/article/how-do-i-get-inspiration-from-music"><![CDATA[<p>Many times I find myself navigating through music streaming platforms in search of something to listen to while I do other stuff, such as playing around with Godot.
As a musician myself, when I find a song I find particularly good, I save it for later listening sessions.</p>

<h2 id="what-are-the-factors-i-take-into-account-when-analyzing-a-song">What are the factors I take into account when analyzing a song?</h2>
<h3 id="melody-and-drums">Melody and drums</h3>
<p>One of the first thing I notice in a song is surely the <strong>drums</strong> and the <strong>lead melody</strong>. Those two are what really make a song get stuck in your head.</p>

<p>Something I really like is when the drums go hard, making the whole song instruments follow that rhythm.</p>

<p>One of the artists which music influenced me most regarding drums is <a href="https://twitter.com/ironthawk">Iron T Hawk</a>, with their album <a href="https://www.youtube.com/watch?v=WU0XbGVV8E8">ToT</a>.</p>

<h3 id="harmony">Harmony</h3>
<p>Going further, I analyze the harmony of the song, trying to understand the chord progression and how each chord links to the others.<br />
This is really useful for me, because I can make use of the knowledge I gathered to make my own music.<br />
I listen to many genres of music, especially jazz, which comes with lots of complex chords compared to most of the music out there.<br />
Sometime I also analyze traditional music, like traditional japanese music.</p>

<h3 id="lyrics">Lyrics</h3>
<p>Music not only inspires me in composing, but also in writing stories.<br />
I’m always looking through many lyrics of many different songs.<br />
A song that really inspired me is <a href="https://www.youtube.com/watch?v=VJtoh7ggGDE">GATE OF STEINER</a> from Steins;Gate for its conception of time.</p>

<h2 id="some-music-i-like">Some music I like</h2>

<ul>
  <li><a href="https://www.youtube.com/@ironthawk">Iron T Hawk</a>’s albums for their energetic and mysterious sounds.</li>
  <li><a href="https://www.youtube.com/watch?v=IgeXLLAwNcs&amp;list=PLDOjCqYj3ys3TEe8HCR7_cYH7X7dU28_B">PaperLily OST</a></li>
  <li>Genshin Impact OST, as an inspiration for making ambience music.</li>
</ul>]]></content><author><name>Shomy</name></author><category term="posts" /><category term="Music" /><summary type="html"><![CDATA[Many times I find myself navigating through music streaming platforms in search of something to listen to while I do other stuff, such as playing around with Godot. As a musician myself, when I find a song I find particularly good, I save it for later listening sessions.]]></summary></entry></feed>