yype.site2024-03-25T18:27:53+00:00https://yype.siteN1CTF2020 n1vault, Thoughts & Solutions2020-10-23T00:00:00+00:00https://yype.site/2020/10/23/n1vault<h2 id="intro">Intro</h2>
<p>I designed the RE challenge <em>n1vault</em> in the recent CTF <a href="https://ctftime.org/event/1099" target="_blank">N1CTF2020</a>. In this post, I will talk about details about this chal and offer some possible solutions.</p>
<p>The core part of this challenge is to craft a file’s CRC to an arbitrary value(zero) by modifying some specified bytes (of the same bit size as the CRC value) in the file.</p>
<p>As for the binary <code class="language-plaintext highlighter-rouge">n1vault</code>, it uses SHA256 to digest all the bytes inside the file(<code class="language-plaintext highlighter-rouge">credencial.png</code>) except for the even-numbered bytes in the last 25 bytes(some twists were added to the <em>sha256_update</em> function, paving the way for the backdoor). Once the file’s CRC is faked to 0, a secret logic(backdoor) will be triggered by an exception <em>FPE_INTDIV</em>, since the verification inside the function <code class="language-plaintext highlighter-rouge">main</code> has an unnecessary comparison <code class="language-plaintext highlighter-rouge">4764888639493207598 / (crc32_result | crc64_result) == 1</code>, which will trigger an <em>FPE_INTDIV</em> when both crc32_result and crc64_result are zero, and will be evaluated to <em>true</em> when given the original file <code class="language-plaintext highlighter-rouge">credential.png</code>. Players’ job is to to craft an input to trigger the backdoor, send the crafted bytes to the judging bot and receive the flag.</p>
<h2 id="solution">Solution</h2>
<p>The reverse engineering part of the binary program is quite easy. Some junk codes with fixed patterns are inserted into the main logic, which can be bypassed by a simple pattern searching & replacing. Afterward, the program logic is straightforward, and we only have to solve the math problem left.</p>
<p>CRC has a property that the final result can be viewed as the linear combination of the influences of each bit in the message and an initial offset, on $GF(2)$. This can be described as follows:</p>
\[f(x)=f(0) + \sum_{i=0}^{CRC\_SIZE-1} x_i \cdot influence_i,\]
<p>where $f(0)$ is the initial offset, specifically for this challenge, is the CRC of the credential with all the even-numbered bytes in the last 25 bytes set to zero. Given this property, if we have enough $x_i$ to control, we can easily construct a matrix and solve each $x_i$ using gaussian elimination. The trick here is we have to ensure that both $f(x)=CRC32(credential)$ and $g(x)=CRC64(credential)$ are equal to zero. In fact, if we let $h(x)=(f(x) < < 64)+g(x)$ and focus on making $h(x)=0$, it has the same effect as making both $f(x)$ and $g(x)$ zero.</p>
<p>I wrote a tool based on this interesting property of CRC, allowing us to arbitrarily craft a file’s CRC by specifying certain bits available for modification. It can output all the available solutions and allows for fewer available bits than the bit size of the CRC result. You can check the tool here:</p>
<blockquote>
<p><a href="https://github.com/yype/crcollider" target="_blank">https://github.com/yype/crcollider</a></p>
</blockquote>
<p>Using this tool we can easily solve the problem using the following Python code:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">crcollider</span> <span class="kn">import</span> <span class="n">collcrc</span>
<span class="kn">from</span> <span class="nn">crc_funcs</span> <span class="kn">import</span> <span class="n">crc64</span><span class="p">,</span> <span class="n">crc32</span>
<span class="k">def</span> <span class="nf">crc96</span><span class="p">(</span><span class="n">m</span><span class="p">):</span>
<span class="k">return</span> <span class="p">(</span><span class="n">crc32</span><span class="p">(</span><span class="n">m</span><span class="p">)</span> <span class="o"><<</span> <span class="mi">64</span><span class="p">)</span> <span class="o">+</span> <span class="n">crc64</span><span class="p">(</span><span class="n">m</span><span class="p">)</span>
<span class="k">def</span> <span class="nf">solve_chal</span><span class="p">():</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="s">'credential.png'</span><span class="p">,</span> <span class="s">'rb'</span><span class="p">)</span> <span class="k">as</span> <span class="n">f</span><span class="p">:</span>
<span class="n">org_img</span> <span class="o">=</span> <span class="n">f</span><span class="p">.</span><span class="n">read</span><span class="p">()</span>
<span class="n">rg</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">org_img</span><span class="p">)</span><span class="o">*</span><span class="mi">8</span><span class="p">))</span>
<span class="n">available_bits</span> <span class="o">=</span> <span class="p">[]</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">12</span><span class="p">):</span>
<span class="c1"># even bytes in the last 25 bytes
</span> <span class="n">available_bits</span> <span class="o">+=</span> <span class="n">rg</span><span class="p">[</span><span class="nb">len</span><span class="p">(</span><span class="n">rg</span><span class="p">)</span><span class="o">-</span><span class="mi">16</span><span class="o">*</span><span class="n">i</span><span class="o">-</span><span class="mi">16</span><span class="p">:</span><span class="nb">len</span><span class="p">(</span><span class="n">rg</span><span class="p">)</span><span class="o">-</span><span class="mi">16</span><span class="o">*</span><span class="n">i</span><span class="o">-</span><span class="mi">8</span><span class="p">]</span>
<span class="n">sol_num</span><span class="p">,</span> <span class="n">sols</span> <span class="o">=</span> <span class="n">collcrc</span><span class="p">(</span><span class="n">crc96</span><span class="p">,</span> <span class="mi">96</span><span class="p">,</span> <span class="n">org_img</span><span class="p">,</span> <span class="n">available_bits</span><span class="p">,</span> <span class="mh">0x0</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'</span><span class="si">{</span><span class="n">sol_num</span><span class="si">}</span><span class="s"> solution(s) found'</span><span class="p">)</span>
<span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">each</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">sols</span><span class="p">):</span>
<span class="n">file_out</span> <span class="o">=</span> <span class="sa">f</span><span class="s">'credential_sol</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s">.png'</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'Outputting sol</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s"> to </span><span class="si">{</span><span class="n">file_out</span><span class="si">}</span><span class="s">...'</span><span class="p">)</span>
<span class="k">with</span> <span class="nb">open</span><span class="p">(</span><span class="n">file_out</span><span class="p">,</span> <span class="s">'wb'</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="n">write</span><span class="p">(</span><span class="n">each</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">solve_chal</span><span class="p">()</span>
</code></pre></div></div>
<p>There are totally 4 solutions available for this challenge. One of them contains only visible characters, which is <code class="language-plaintext highlighter-rouge">n1vaultadmin</code>(intentionally crafted), while others are not. It might be better if I put some constraints to ensure that only one solution is available though.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>solution> python3 .\main.py
4 solution(s) found
Outputting sol0 to credential_sol0.png...
Outputting sol1 to credential_sol1.png...
Outputting sol2 to credential_sol2.png...
Outputting sol3 to credential_sol3.png...
</code></pre></div></div>
<p>The source code of this challenge and a duplicate of this post are uploaded to GitHub. Check them out at: <a href="https://github.com/Nu1LCTF/n1ctf-2020/tree/main/RE/n1vault" target="_blank">https://github.com/Nu1LCTF/n1ctf-2020/tree/main/RE/n1vault</a>.</p>
PlaidCTF2020 The Watness 2 Write-up2020-04-20T00:00:00+00:00https://yype.site/2020/04/20/Hypercard-Over-Windows<ul id="markdown-toc">
<li><a href="#intro" id="markdown-toc-intro">Intro</a></li>
<li><a href="#environment" id="markdown-toc-environment">Environment</a></li>
<li><a href="#challenge-solution" id="markdown-toc-challenge-solution">Challenge Solution</a></li>
</ul>
<h2 id="intro">Intro</h2>
<p>Recently in PlaidCTF2020 there was an RE challenge called <em>The Watness 2</em>. This is a game that requires <em>HyperCard</em> to run. Since I did not have a Macbook computer, I had been struggling figuring out ways to run this game over my Windows 10 laptop. Here is how I finally managed to do that.</p>
<h2 id="environment">Environment</h2>
<div class="photoswipe-gallery center">
<p><a class="photoswipe photo" href="/assets/HypercardOverWindows/1587369699146.png" target="_blank" data-cropped="true" data-pswp-width="803" data-pswp-height="640">
<img class="image" src="/assets/HypercardOverWindows/1587369699146.png" width="200" height="200" alt="Install StuffitExpander" />
<span class="badge">Install StuffitExpander</span>
</a></p>
<p><a class="photoswipe photo" href="/assets/HypercardOverWindows/1587369949569.png" target="_blank" data-cropped="true" data-pswp-width="932" data-pswp-height="656">
<img class="image" src="/assets/HypercardOverWindows/1587369949569.png" width="200" height="200" alt="Open the .rc1 file" />
<span class="badge">Open the .rc1 file</span>
</a></p>
</div>
<ul>
<li>Follow <a href="https://www.youtube.com/watch?v=TY3pjSGg1y4">this video tutorial</a></li>
<li>Download <a href="https://www.macintoshrepository.org/2475-stuffit-expander-and-dropstuff-5-5">StuffitExpander</a>, add it to the volumes’ list, install it inside the VM</li>
<li>Download <a href="https://macintoshgarden.org/apps/hypercard-241">HyperCard 2.4</a>, install it inside the VM as described above</li>
<li>Extract the .rc1 file from the .sit file and open it with a simple double-click</li>
</ul>
<h2 id="challenge-solution">Challenge Solution</h2>
<p>Extract the stack’s script code:</p>
<details>
<div class="photoswipe-gallery center">
<p><a class="photoswipe photo" href="/assets/HypercardOverWindows/1587389434804.png" target="_blank" data-cropped="true" data-pswp-width="892" data-pswp-height="729">
<img class="image" src="/assets/HypercardOverWindows/1587389434804.png" width="300" height="300" alt="Extract the stack’s script code" />
<span class="badge">Extract the stack’s script code</span>
</a></p>
</div>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>on openCard
Send colorMe to this card
pass openCard
end openCard
on closeCard
global prev_card
get the id of this cd
put it into prev_card
lock screen
pass closeCard
end closeCard
on colorMe
AddColor colorCard,stamp,0
end colorMe
on openStack
AddColor install
setupMenu
go to card "tun-1-n"
pass openStack
end openStack
on closeStack
AddColor remove
pass closeStack
end closeStack
on genPuzzle
send "doMenu New Button" to Hypercard
end genPuzzle
on initCard
answer prev_card
end initCard
on menuReset
setupMenu
pass menuReset
end menuReset
on setupMenu
if there is not a menu "Watness" then
create menu "Watness"
put "Generate Puzzle"&return&"Init Card"&return&"Set up Nav"&return&"Create Puzzle" into menu "Watness" with menuMsg genPuzzle,initCard,setupNav,constructPuzzle
end if
end setupMenu
on setupNav
ask "What is the name of this card"
set name of this cd to it
ask "Where should the left go?"
put it into left_loc
send "doMenu New Button" to Hypercard
set height of the last button to 342
set width of the last button to 100
set topleft of the last button to "0,0"
set style of the last button to "transparent"
set name of the last button to ""
put "on mouseUp"&return&"go to card "&quote&""&left_loc&quote&return&"end mouseUp" into left_script
set script of last button to left_script
ask "Where should the right go?"
put it into right_loc
send "doMenu New Button" to Hypercard
set height of the last button to 342
set width of the last button to 100
set topleft of the last button to "412,0"
set style of the last button to "transparent"
set name of the last button to ""
put "on mouseUp"&return&"go to card "&quote&""&right_loc&quote&return&"end mouseUp" into right_script
set script of last button to right_script
ask "Where should fwd go?"
put it into fwd_loc
send "doMenu New Button" to Hypercard
set height of the last button to 342
set width of the last button to 311
set the top of the last button to 0
set the left of the last button to 100
set style of the last button to "transparent"
set name of the last button to ""
put "on mouseUp"&return&"go to card "&quote&""&fwd_loc&quote&return&"end mouseUp" into fwd_script
set script of last button to fwd_script
get the script of this card
put it into cd_script
put cd_script&return into cd_script
put cd_script&"on arrowKey key"&return into cd_script
put cd_script&" if key = "&quote&"left"&quote&"then"&return into cd_script
put cd_script&" go to cd "&quote&left_loc&quote&return into cd_script
put cd_script&" end if"&return into cd_script
put cd_script&" if key = "&quote&"right"&quote&"then"&return into cd_script
put cd_script&" go to cd "&quote&right_loc&quote&return into cd_script
put cd_script&" end if"&return into cd_script
put cd_script&" if key = "&quote&"up"&quote&"then"&return into cd_script
put cd_script&" go to cd "&quote&fwd_loc&quote&return into cd_script
put cd_script&" end if"&return into cd_script
put cd_script&"end arrowKey"&return into cd_script
set the script of this cd to cd_script
end setupNav
on makeNode
global node
send "doMenu New Button" to Hypercard
put the id of the last button into node
end makeNode
on constructPuzzle
global node,constraints
ask "What are the constraints"
put it into constraints
get the script of this cd
put it into cd_script
put cd_script&return into cd_script
put cd_script&"on openCard"&return into cd_script
put cd_script&" global constraints,path,cursor_x,cursor_y," into cd_script
put 0 into i
repeat for 8
put 0 into j
repeat for 8
put cd_script&"active_"&i&"_"&j&"," into cd_script
put j+1 into j
end repeat
put i+1 into i
end repeat
put cd_script&"dummy"&return into cd_script
put cd_script&" colorme"&return into cd_script
put cd_script&" put -1 into cursor_x"&return into cd_script
put cd_script&" put 0 into cursor_y"&return into cd_script
put cd_script&" put "&quote&quote&" into path"&return into cd_script
put cd_script&" put "&quote&constraints&quote&" into constraints"&return into cd_script
put 1 into c_i
put 0 into i
repeat for 7
put 0 into j
repeat for 7
get char (j*7+i+1) of constraints
put it into letter
if letter <> " " then
makeNode
set the width of button id node to 10
set the height of button id node to 10
set the top of button id node to (76 + j * 30)
set the left of button id node to (161 + i * 30)
set the style of button id node to "opaque"
set showName of button id node to false
if letter = "r" then
put "65535,0,0" into node_color
end if
if letter = "g" then
put "0,65535,0" into node_color
end if
if letter = "b" then
put "0,0,65535" into node_color
end if
if letter <> " " then
addColor colorButton, cd, node, node_color
put cd_script&" addColor colorButton, cd, "&node&", "&quote&node_color&quote&return into cd_script
end if
end if
put j+1 into j
end repeat
put i+1 into i
end repeat
put 0 into i
repeat for 8
put 0 into j
repeat for 8
put cd_script&" put "&quote&quote&" into active_"&i&"_"&j&return into cd_script
put j+1 into j
end repeat
put i+1 into i
end repeat
makeNode
set the width of button id node to 10
set the height of button id node to 15
set the left of button id node to 356
set the top of button id node to 276
set the name of button id node to "path_extension"
set showName of button id node to false
set the style of button id node to opaque
addcolor colorButton, cd, node, "37632,30208,12288"
put "" into node_script
put node_script&"on checkYoSelf"&return into node_script
put node_script&" addcolor colorButton, cd, "&node&", "&quote&"65535,65535,30000"&quote&return into node_script
put node_script&"end checkYoSelf"&return into node_script
set the script of button id node to node_script
put cd_script&" addcolor colorButton, cd, "&node&", "&quote&"37632,30208,12288"&quote&return into cd_script
makeNode
set the width of button id node to 10
set the height of button id node to 10
set the left of button id node to 356
set the top of button id node to 286
set the name of button id node to "finale"
set showName of button id node to false
set the style of button id node to oval
addcolor colorButton, cd, node, "37632,30208,12288"
put "" into node_script
put node_script&"on mouseUp"&return into node_script
put node_script&" global cursor_x, cursor_y"&return into node_script
put node_script&" if (cursor_x = 7) and (cursor_y = 7) then"&return into node_script
put node_script&" addcolor colorButton, cd, "&node&", "&quote&"65535,65535,30000"&quote&return into node_script
put node_script&" send "&quote&"checkYoSelf"&quote&" to button path_extension"&return into node_script
put node_script&" send "&quote&"checkSolution"&quote&" to this cd"&return into node_script
put node_script&" end if"&return into node_script
put node_script&"end mouseUp"&return into node_script
set the script of button id node to node_script
put cd_script&" addcolor colorButton, cd, "&node&", "&quote&"37632,30208,12288"&quote&return into cd_script
put 0 into i
repeat for 7
put 0 into j
repeat for 8
makeNode
set the width of button id node to 30
set the height of button id node to 10
set the top of button id node to (61 + 30 * j)
set the left of button id node to (151 + 30 * i)
set the style of button id node to opaque
set the name of button id node to "h_path_"&i&"_"&j
set showName of button id node to false
addcolor colorButton, cd, node, "37632,30208,12288"
put cd_script&" addcolor colorButton, cd, "&node&", "&quote&"37632,30208,12288"&quote&return into cd_script
get the script of button id node
put it into node_script
put "active_"&i&"_"&j into f_node
put "active_"&(i+1)&"_"&j into s_node
put node_script&return into node_script
put node_script&"on checkYoSelf"&return into node_script
put node_script&" global "&f_node&","&s_node&return into node_script
put node_script&" if ("&f_node&" = true) and ("&s_node&" = true) then"&return into node_script
put node_script&" addcolor colorButton, cd, "&node&", "&quote&"65535,65535,30000"&quote&return into node_script
put node_script&" end if"&return into node_script
put node_script&"end checkYoSelf"&return into node_script
set the script of button id node to node_script
put j+1 into j
end repeat
put i+1 into i
end repeat
put 0 into i
repeat for 8
put 0 into j
repeat for 7
makeNode
set the width of button id node to 10
set the height of button id node to 30
set the top of button id node to (66 + 30 * j)
set the left of button id node to (146 + 30 * i)
set the style of button id node to opaque
set the name of button id node to "v_path_"&i&"_"&j
set showName of button id node to false
addcolor colorButton, cd, node, "37632,30208,12288"
put cd_script&" addcolor colorButton, cd, "&node&", "&quote&"37632,30208,12288"&quote&return into cd_script
get the script of button id node
put it into node_script
put "active_"&i&"_"&j into f_node
put "active_"&i&"_"&(j+1) into s_node
put node_script&return into node_script
put node_script&"on checkYoSelf"&return into node_script
put node_script&" global "&f_node&","&s_node&return into node_script
put node_script&" if ("&f_node&" = true) and ("&s_node&" = true) then"&return into node_script
put node_script&" addcolor colorButton, cd, "&node&", "&quote&"65535,65535,30000"&quote&return into node_script
put node_script&" end if"&return into node_script
put node_script&"end checkYoSelf"&return into node_script
set the script of button id node to node_script
put j+1 into j
end repeat
put i+1 into i
end repeat
put 0 into i
repeat for 8
put 0 into j
repeat for 8
makeNode
set the width of button id node to 10
set the height of button id node to 10
set the top of button id node to (61 + 30 * j)
set the left of button id node to (146 + 30 * i)
set the style of button id node to oval
set the name of button id node to "button_"&i&"_"&j
set showName of button id node to false
addcolor colorButton, cd, node, "37632,30208,12288"
put "active_"&i&"_"&j into v_name
put "" into node_script
put node_script&"on mouseUp"&return into node_script
put node_script&" global "&v_name&",cursor_x,cursor_y"&return into node_script
put node_script&" put cursor_x into prev_x"&return into node_script
put node_script&" put cursor_y into prev_y"&return into node_script
put node_script&" put abs(cursor_x-"&i&") into dx"&return into node_script
put node_script&" put abs(cursor_y-"&j&") into dy"&return into node_script
put node_script&" if ("&v_name&" = "&quote&quote&") and ((dx = 1 and dy = 0) or (dx = 0 and dy = 1)) then"&return into node_script
put node_script&" put true into "&v_name&return into node_script
put node_script&" send "&quote&"updateState "&i&","&j&quote&" to this cd"&return into node_script
put node_script&" addcolor colorButton, cd, "&node&", "&quote&"65535,65535,30000"&quote&return into node_script
put node_script&" end if"&return into node_script
put node_script&"end mouseUp"&return into node_script
set the script of button id node to node_script
put cd_script&" addcolor colorButton, cd, "&node&", "&quote&"37632,30208,12288"&quote&return into cd_script
put j+1 into j
end repeat
put i+1 into i
end repeat
set the width of button button_0_0 to 30
set the height of button button_0_0 to 30
set the top of button button_0_0 to 51
set the left of button button_0_0 to 136
get the id of button button_0_0
addColor colorButton, cd, it, "37632,30208,12288"
put cd_script&"end openCard"&return into cd_script
set the script of this cd to cd_script
end constructPuzzle
on checkSolution
global puzzle_id,path,constraints,flag_1,flag_2,flag_3
watnesssolver constraints,path
put the result into success
if success = "true" then
if puzzle_id = 1 then
decoder path,"clrtffxpry"
put the result into flag_1
end if
if puzzle_id = 2 then
decoder path,"nyghq7xksg"
put the result into flag_2
end if
if puzzle_id = 3 then
decoder path,"ppyyvn}1{7"
put the result into flag_3
end if
else
send opencard to this cd
end if
end checkSolution
on updateState i,j
global path,cursor_x,cursor_y
if (i <> 0) or (j <> 0) then
if (cursor_y = j+1) and (cursor_x = i) then
put path&"U" into path
end if
if (cursor_y = j) and (cursor_x = i - 1) then
put path&"R" into path
end if
if (cursor_y = j-1) and (cursor_x = i) then
put path&"D" into path
end if
if (cursor_y = j) and (cursor_x = i + 1) then
put path&"L" into path
end if
end if
if cursor_x >= 0 and cursor_y >= 0 then
put "h_path_"&min(cursor_x, i)&"_"&min(cursor_y, j) into h_path
put "v_path_"&min(cursor_x, i)&"_"&min(cursor_y, j) into v_path
if i = cursor_x then
send checkYoSelf to button v_path
end if
if j = cursor_y then
send checkYoSelf to button h_path
end if
end if
put i into cursor_x
put j into cursor_y
end updateState
</code></pre></div> </div>
</details>
<p>The card’s script code can also be extracted (puzzle 1):</p>
<details>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
on openCard
global puzzle_id,constraints,path,cursor_x,cursor_y,active_0_0,active_0_1,active_0_2,active_0_3,active_0_4,active_0_5,active_0_6,active_0_7,active_1_0,active_1_1,active_1_2,active_1_3,active_1_4,active_1_5,active_1_6,active_1_7,active_2_0,active_2_1,active_2_2,active_2_3,active_2_4,active_2_5,active_2_6,active_2_7,active_3_0,active_3_1,active_3_2,active_3_3,active_3_4,active_3_5,active_3_6,active_3_7,active_4_0,active_4_1,active_4_2,active_4_3,active_4_4,active_4_5,active_4_6,active_4_7,active_5_0,active_5_1,active_5_2,active_5_3,active_5_4,active_5_5,active_5_6,active_5_7,active_6_0,active_6_1,active_6_2,active_6_3,active_6_4,active_6_5,active_6_6,active_6_7,active_7_0,active_7_1,active_7_2,active_7_3,active_7_4,active_7_5,active_7_6,active_7_7,dummy
colorme
put 1 into puzzle_id
put -1 into cursor_x
put 0 into cursor_y
put "" into path
put "rbrr rgb rb r brgrbrgb grrgbbg grg bgrg bbgrbg" into constraints
addColor colorButton, cd, 1, "65535,0,0"
...
end openCard
on arrowKey key
if key = "left"then
go to cd "entry-3-n"
end if
if key = "right"then
go to cd "entry-3-n"
end if
if key = "up"then
go to cd ""
end if
end arrowKey
</code></pre></div> </div>
</details>
<p>Now we get the constraint string of this puzzle <code class="language-plaintext highlighter-rouge">"rbrr rgb rb r brgrbrgb grrgbbg grg bgrg bbgrbg"</code>, these constraints, along with the path that goes to the lower right corner, are passed into one thing called <em>XCMD</em> which checks the path’s correctness natively (it contains binary instructions that directly run over the 68k CPU). There are 2 XCMD binaries which can be extracted by <a href="https://github.com/PierreLorenzi/HyperCardPreview">this tool</a>, which seem can only run under MacOS. So I’m using the extracted binary from <a href="https://ctf.harrisongreen.me/2020/plaidctf/the_watness_2/">this</a> great post for now instead.</p>
<p>What’s more, I found that I can set breakpoints in the script and debug the game, I could even watch the variables on the fly:</p>
<div class="photoswipe-gallery center">
<p> </p>
<p><a class="photoswipe photo" href="/assets/HypercardOverWindows/1588514193878.png" target="_blank" data-cropped="true" data-pswp-width="1093" data-pswp-height="700">
<img class="image" src="/assets/HypercardOverWindows/1588514193878.png" width="300" height="300" alt="Debugging" />
<span class="badge">Debugging</span>
</a></p>
</div>
<p>As for the XCMD part, it’s basically just a few hours’ reverse engineering work. Since there are currently no reliable decompilers for the 68k architecture, I have to read the assembly. It wasn’t too hard, but I did spend several hours learning the basic concepts of 68k’s instruction set.</p>
<p>After the reverse engineering work, the watnesssolver’s checking methods can be rewritten in Python as:</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">build_automaton</span><span class="p">(</span><span class="n">constraints</span><span class="p">:</span> <span class="nb">str</span><span class="p">):</span>
<span class="n">trans</span> <span class="o">=</span> <span class="nb">str</span><span class="p">.</span><span class="n">maketrans</span><span class="p">(</span><span class="s">' rgb'</span><span class="p">,</span> <span class="s">'0123'</span><span class="p">)</span>
<span class="k">return</span> <span class="p">[</span><span class="nb">int</span><span class="p">(</span><span class="n">c</span><span class="p">)</span> <span class="k">for</span> <span class="n">c</span> <span class="ow">in</span> <span class="n">constraints</span><span class="p">.</span><span class="n">translate</span><span class="p">(</span><span class="n">trans</span><span class="p">)]</span>
<span class="k">def</span> <span class="nf">choose_empty</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">if</span> <span class="n">g</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">b</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="k">if</span> <span class="n">b</span> <span class="o"><</span> <span class="n">g</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">2</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">3</span>
<span class="k">def</span> <span class="nf">choose_red</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">if</span> <span class="n">r</span> <span class="o">!=</span> <span class="mi">2</span> <span class="ow">and</span> <span class="n">r</span> <span class="o">!=</span> <span class="mi">3</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="k">if</span> <span class="n">b</span> <span class="o">==</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">g</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="k">return</span> <span class="mi">1</span>
<span class="k">def</span> <span class="nf">choose_green</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">if</span> <span class="n">r</span> <span class="o"><=</span> <span class="mi">4</span><span class="p">:</span>
<span class="k">if</span> <span class="n">b</span> <span class="o"><=</span> <span class="mi">4</span><span class="p">:</span>
<span class="k">if</span> <span class="n">r</span> <span class="o">==</span> <span class="mi">2</span> <span class="ow">or</span> <span class="n">r</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">2</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">3</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">choose_blue</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">):</span>
<span class="k">if</span> <span class="n">r</span> <span class="o"><=</span> <span class="mi">4</span><span class="p">:</span>
<span class="k">if</span> <span class="n">g</span> <span class="o"><=</span> <span class="mi">4</span><span class="p">:</span>
<span class="k">if</span> <span class="n">r</span> <span class="o">==</span> <span class="mi">2</span> <span class="ow">or</span> <span class="n">r</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">1</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">3</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">2</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">return</span> <span class="mi">0</span>
<span class="k">def</span> <span class="nf">is_red</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">):</span>
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">x</span> <span class="o">>=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">x</span> <span class="o"><</span> <span class="mi">7</span> <span class="ow">and</span> <span class="n">y</span> <span class="o">>=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">y</span> <span class="o"><</span> <span class="mi">7</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">False</span>
<span class="k">return</span> <span class="n">constraints</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span>
<span class="k">def</span> <span class="nf">get_neighbors</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">color</span><span class="p">):</span>
<span class="nb">sum</span> <span class="o">=</span> <span class="mi">0</span>
<span class="k">for</span> <span class="n">bias_y</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span>
<span class="k">for</span> <span class="n">bias_x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">):</span>
<span class="k">if</span> <span class="p">(</span><span class="n">bias_x</span> <span class="o">!=</span> <span class="mi">0</span> <span class="ow">or</span> <span class="n">bias_y</span> <span class="o">!=</span> <span class="mi">0</span><span class="p">)</span> <span class="ow">and</span> <span class="p">(</span><span class="n">y</span> <span class="o">+</span> <span class="n">bias_y</span> <span class="o">>=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">y</span> <span class="o">+</span> <span class="n">bias_y</span> <span class="o"><</span> <span class="mi">7</span><span class="p">)</span> <span class="ow">and</span> \
<span class="p">(</span><span class="n">x</span> <span class="o">+</span> <span class="n">bias_x</span> <span class="o">>=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">x</span> <span class="o">+</span> <span class="n">bias_x</span> <span class="o"><</span> <span class="mi">7</span><span class="p">)</span> <span class="ow">and</span> <span class="n">constraints</span><span class="p">[(</span><span class="n">x</span><span class="o">+</span><span class="n">bias_x</span><span class="p">)</span><span class="o">+</span><span class="p">(</span><span class="n">y</span><span class="o">+</span><span class="n">bias_y</span><span class="p">)</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">==</span> <span class="n">color</span><span class="p">:</span>
<span class="nb">sum</span> <span class="o">+=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="nb">sum</span>
<span class="k">def</span> <span class="nf">step_automaton</span><span class="p">(</span><span class="n">constraints</span><span class="p">):</span>
<span class="n">new_constraints</span> <span class="o">=</span> <span class="n">constraints</span><span class="p">[:]</span>
<span class="k">for</span> <span class="n">y</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">7</span><span class="p">):</span>
<span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">7</span><span class="p">):</span>
<span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span> <span class="o">=</span> \
<span class="n">get_neighbors</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span> \
<span class="n">get_neighbors</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span> \
<span class="n">get_neighbors</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="mi">3</span><span class="p">)</span>
<span class="k">if</span> <span class="n">constraints</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="n">new_constraints</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">=</span> <span class="n">choose_empty</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">constraints</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="n">new_constraints</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">=</span> <span class="n">choose_red</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">constraints</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">==</span> <span class="mi">2</span><span class="p">:</span>
<span class="n">new_constraints</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">=</span> <span class="n">choose_green</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="k">elif</span> <span class="n">constraints</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">==</span> <span class="mi">3</span><span class="p">:</span>
<span class="n">new_constraints</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">y</span><span class="o">*</span><span class="mi">7</span><span class="p">]</span> <span class="o">=</span> <span class="n">choose_blue</span><span class="p">(</span><span class="n">r</span><span class="p">,</span> <span class="n">g</span><span class="p">,</span> <span class="n">b</span><span class="p">)</span>
<span class="k">return</span> <span class="n">new_constraints</span>
<span class="k">def</span> <span class="nf">perform_move</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">mem</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">d</span><span class="p">):</span>
<span class="n">bias_x</span><span class="p">,</span> <span class="n">bias_y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">0</span>
<span class="k">if</span> <span class="n">d</span> <span class="o">==</span> <span class="s">'U'</span><span class="p">:</span>
<span class="n">bias_x</span><span class="p">,</span> <span class="n">bias_y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="o">-</span><span class="mi">1</span>
<span class="k">elif</span> <span class="n">d</span> <span class="o">==</span> <span class="s">'D'</span><span class="p">:</span>
<span class="n">bias_x</span><span class="p">,</span> <span class="n">bias_y</span> <span class="o">=</span> <span class="mi">0</span><span class="p">,</span> <span class="mi">1</span>
<span class="k">elif</span> <span class="n">d</span> <span class="o">==</span> <span class="s">'L'</span><span class="p">:</span>
<span class="n">bias_x</span><span class="p">,</span> <span class="n">bias_y</span> <span class="o">=</span> <span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="mi">0</span>
<span class="k">elif</span> <span class="n">d</span> <span class="o">==</span> <span class="s">'R'</span><span class="p">:</span>
<span class="n">bias_x</span><span class="p">,</span> <span class="n">bias_y</span> <span class="o">=</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">0</span>
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">x</span><span class="o">+</span><span class="n">bias_x</span> <span class="o">>=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">x</span><span class="o">+</span><span class="n">bias_x</span> <span class="o"><</span> <span class="mi">8</span> <span class="ow">and</span> <span class="n">y</span><span class="o">+</span><span class="n">bias_y</span> <span class="o">>=</span> <span class="mi">0</span> <span class="ow">and</span> <span class="n">y</span><span class="o">+</span><span class="n">bias_y</span> <span class="o"><</span> <span class="mi">8</span><span class="p">):</span>
<span class="k">return</span> <span class="bp">False</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span>
<span class="n">min_x</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">x</span><span class="p">,</span> <span class="n">x</span><span class="o">+</span><span class="n">bias_x</span><span class="p">)</span>
<span class="n">min_y</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">y</span><span class="p">,</span> <span class="n">y</span><span class="o">+</span><span class="n">bias_y</span><span class="p">)</span>
<span class="k">if</span> <span class="n">bias_y</span> <span class="o">==</span> <span class="mi">0</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">is_red</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">min_x</span><span class="p">,</span> <span class="n">y</span><span class="p">)</span> <span class="ow">or</span> <span class="n">is_red</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">min_x</span><span class="p">,</span> <span class="n">y</span><span class="o">-</span><span class="mi">1</span><span class="p">)):</span>
<span class="k">return</span> <span class="bp">False</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span>
<span class="k">else</span><span class="p">:</span>
<span class="k">if</span> <span class="ow">not</span> <span class="p">(</span><span class="n">is_red</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">min_y</span><span class="p">)</span> <span class="ow">or</span> <span class="n">is_red</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">x</span><span class="o">-</span><span class="mi">1</span><span class="p">,</span> <span class="n">min_y</span><span class="p">)):</span>
<span class="k">return</span> <span class="bp">False</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span>
<span class="k">if</span> <span class="n">mem</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">bias_x</span><span class="p">][</span><span class="n">y</span><span class="o">+</span><span class="n">bias_y</span><span class="p">]</span> <span class="o">==</span> <span class="mi">1</span><span class="p">:</span>
<span class="k">return</span> <span class="bp">False</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span>
<span class="n">mem</span><span class="p">[</span><span class="n">x</span><span class="o">+</span><span class="n">bias_x</span><span class="p">][</span><span class="n">y</span><span class="o">+</span><span class="n">bias_y</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1</span>
<span class="k">return</span> <span class="bp">True</span><span class="p">,</span> <span class="n">x</span><span class="o">+</span><span class="n">bias_x</span><span class="p">,</span> <span class="n">y</span><span class="o">+</span><span class="n">bias_y</span>
<span class="k">def</span> <span class="nf">solver</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">constraints</span><span class="p">):</span>
<span class="n">x</span> <span class="o">=</span> <span class="n">y</span> <span class="o">=</span> <span class="mi">0</span>
<span class="n">constraints</span> <span class="o">=</span> <span class="n">build_automaton</span><span class="p">(</span><span class="n">constraints</span><span class="p">)</span>
<span class="n">mem</span> <span class="o">=</span> <span class="p">[[</span><span class="mi">0</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">)]</span> <span class="k">for</span> <span class="n">j</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">8</span><span class="p">)]</span> <span class="c1"># been to or not
</span> <span class="n">mem</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">=</span> <span class="mi">1</span>
<span class="k">for</span> <span class="n">each</span> <span class="ow">in</span> <span class="n">path</span><span class="p">:</span>
<span class="n">yes</span><span class="p">,</span> <span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span> <span class="o">=</span> <span class="n">perform_move</span><span class="p">(</span><span class="n">constraints</span><span class="p">,</span> <span class="n">mem</span><span class="p">,</span> <span class="n">x</span><span class="p">,</span> <span class="n">y</span><span class="p">,</span> <span class="n">each</span><span class="p">)</span>
<span class="k">if</span> <span class="n">yes</span><span class="p">:</span>
<span class="k">if</span> <span class="n">new_x</span> <span class="o">==</span> <span class="mi">7</span> <span class="ow">and</span> <span class="n">new_y</span> <span class="o">==</span> <span class="mi">7</span><span class="p">:</span>
<span class="c1"># print(f'Path `{path}` is great ' + '!' * 20)
</span> <span class="k">return</span> <span class="bp">True</span><span class="p">,</span> <span class="bp">True</span>
<span class="n">x</span><span class="p">,</span> <span class="n">y</span> <span class="o">=</span> <span class="n">new_x</span><span class="p">,</span> <span class="n">new_y</span>
<span class="n">constraints</span> <span class="o">=</span> <span class="n">step_automaton</span><span class="p">(</span><span class="n">constraints</span><span class="p">)</span>
<span class="k">else</span><span class="p">:</span>
<span class="c1">#print(f'Path `{path}` is bad')
</span> <span class="k">return</span> <span class="bp">False</span><span class="p">,</span> <span class="bp">False</span>
<span class="k">return</span> <span class="bp">True</span><span class="p">,</span> <span class="bp">False</span>
</code></pre></div></div>
<p>Looking around in the game, we’ll know that there are 3 puzzles we need to solve. So we can simply run 3 DFS searches on these contraints and three unique solutions will be printed out.</p>
<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">dfs</span><span class="p">(</span><span class="n">depth</span><span class="p">,</span> <span class="n">path</span><span class="p">,</span> <span class="n">constraints</span><span class="p">):</span>
<span class="k">if</span> <span class="n">depth</span> <span class="o">></span> <span class="mi">24</span><span class="p">:</span> <span class="c1"># figured out after multiple tests
</span> <span class="k">return</span>
<span class="n">mov</span><span class="p">,</span> <span class="n">end</span> <span class="o">=</span> <span class="n">solver</span><span class="p">(</span><span class="n">path</span><span class="p">,</span> <span class="n">constraints</span><span class="p">)</span>
<span class="k">if</span> <span class="n">end</span> <span class="o">==</span> <span class="bp">True</span><span class="p">:</span>
<span class="k">print</span><span class="p">(</span><span class="sa">f</span><span class="s">'Path </span><span class="si">{</span><span class="n">path</span><span class="si">}</span><span class="s"> is ok'</span><span class="p">)</span>
<span class="k">if</span> <span class="n">mov</span> <span class="o">==</span> <span class="bp">False</span><span class="p">:</span>
<span class="k">return</span>
<span class="k">for</span> <span class="n">d</span> <span class="ow">in</span> <span class="s">'LRUD'</span><span class="p">:</span>
<span class="n">n_path</span> <span class="o">=</span> <span class="n">path</span> <span class="o">+</span> <span class="n">d</span>
<span class="n">dfs</span><span class="p">(</span><span class="n">depth</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="n">n_path</span><span class="p">,</span> <span class="n">constraints</span><span class="p">)</span>
<span class="k">if</span> <span class="n">__name__</span> <span class="o">==</span> <span class="s">'__main__'</span><span class="p">:</span>
<span class="n">constraints_stage1</span> <span class="o">=</span> <span class="s">'rbrr rgb rb r brgrbrgb grrgbbg grg bgrg bbgrbg'</span>
<span class="n">constraints_stage2</span> <span class="o">=</span> <span class="s">'rbr bbggrgrggb bggbb b b bbrbbgg gbrrbgrbbb g'</span>
<span class="n">constraints_stage3</span> <span class="o">=</span> <span class="s">'rrbrb rg g bgrbgggr ggrgr gr rg brr b bggrbgbb'</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Stage1 solution:'</span><span class="p">)</span>
<span class="n">dfs</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s">''</span><span class="p">,</span> <span class="n">constraints_stage1</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Stage2 solution:'</span><span class="p">)</span>
<span class="n">dfs</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s">''</span><span class="p">,</span> <span class="n">constraints_stage2</span><span class="p">)</span>
<span class="k">print</span><span class="p">(</span><span class="s">'Stage3 solution:'</span><span class="p">)</span>
<span class="n">dfs</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="s">''</span><span class="p">,</span> <span class="n">constraints_stage3</span><span class="p">)</span>
</code></pre></div></div>
<p>Output:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Stage1 solution:
Path RDDDRURRRDLLDLDRRURRDDDR is ok
Stage2 solution:
Path RDDRURDDDRURULURRDDDDDRD is ok
Stage3 solution:
Path DRDDDDRUURRRULURRDDDDDDR is ok
</code></pre></div></div>
<p>Input these solutions to each puzzle, after that, we could go to the lock-like thing on the white gate to reveal the flag:</p>
<div class="photoswipe-gallery center">
<p> </p>
<p><a class="photoswipe photo" href="/assets/HypercardOverWindows/1588514105474.png" target="_blank" data-cropped="true" data-pswp-width="788" data-pswp-height="448">
<img class="image" src="/assets/HypercardOverWindows/1588514105474.png" width="300" height="300" alt="flag" />
<span class="badge">flag</span>
</a></p>
</div>
D^3CTF2019 Ancient Game V2, Thoughts & Solutions2020-02-20T00:00:00+00:00https://yype.site/2020/02/20/Ancient-Game-V2<h2 id="intro">Intro</h2>
<p>I designed the RE challenge <em>Ancient Game V2</em> in D^3CTF2019. This post talks about some designs behind this challenge along with its solution.</p>
<h2 id="challenge">Challenge</h2>
<p>The challenge uses a virtual architecture similar to OISC to implement a classic Sudoku verification algorithm. There are four types of instructions: input, output, jcc, and NAND. As a whole, they can be seen as a NAND OISC with two I/O interrupts.</p>
<p>All logical operations such as XOR, AND, and OR are implemented through combinations of NAND gates. For example:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>xor x,y =>
xor_tmp[0] = y NAND y
xor_tmp[1] = x NAND xor_tmp[0]
xor_tmp[2] = x NAND x
xor_tmp[3] = y NAND xor_tmp[2]
x = xor_tmp[1] NAND xor_tmp[3]
</code></pre></div></div>
<p>This is based on the fact that:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Q = A XOR B = [ B NAND ( A NAND A ) ] NAND [ A NAND ( B NAND B ) ]
</code></pre></div></div>
<p>The following is an excerpt of the Sudoku verification algorithm written in a custom DSL:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>welcome = mkstr("**************************\n** Welcome To D^3CTF **\n** Ancient Game V2 **\n**************************\n\nInput Flag:")
wrong = mkstr("\nSorry, please try again.\n")
correct = mkstr("\nCorrect.\n")
flag = new(50)
// distract = new(1000)
grid = new(81)
// initialize the puzzle
set(grid[0],9)
set(grid[5],8)
set(grid[9],1)
set(grid[10],3)
set(grid[14],9)
set(grid[16],7)
...
set(grid[71],6)
set(grid[75],9)
set(grid[80],1)
__code_start__
// print the welcome message
print(welcome)
// get input
input(flag[0])
input(flag[1])
input(flag[2])
input(flag[3])
input(flag[4])
input(flag[5])
...
input(flag[46])
input(flag[47])
input(flag[48])
input(flag[49])
// transfer chars in the flag into the grids
long_transfer(flag[0],grid[1])
long_transfer(flag[1],grid[2])
...
long_transfer(flag[47],grid[77])
long_transfer(flag[48],grid[78])
long_transfer(flag[49],grid[79])
// xor with xor_table, which is introduced
// for generating different flags to different teams
grid[1] = grid[1] ^ xor_table[0]
grid[2] = grid[2] ^ xor_table[1]
grid[3] = grid[3] ^ xor_table[2]
grid[4] = grid[4] ^ xor_table[3]
grid[6] = grid[6] ^ xor_table[4]
grid[7] = grid[7] ^ xor_table[5]
...
grid[77] = grid[77] ^ xor_table[47]
grid[78] = grid[78] ^ xor_table[48]
grid[79] = grid[79] ^ xor_table[49]
// verify the sudoku game
// rows
jmp _label_wrong if grid[4] == grid[5]
jmp _label_wrong if grid[4] == grid[6]
jmp _label_wrong if grid[4] == grid[7]
...
jmp _label_wrong if grid[3] == grid[7]
jmp _label_wrong if grid[3] == grid[8]
// columns
jmp _label_wrong if grid[0] == grid[9]
jmp _label_wrong if grid[0] == grid[18]
jmp _label_wrong if grid[0] == grid[27]
...
jmp _label_wrong if grid[62] == grid[80]
jmp _label_wrong if grid[71] == grid[80]
// subgrids
jmp _label_wrong if grid[0] == grid[1]
jmp _label_wrong if grid[0] == grid[2]
jmp _label_wrong if grid[0] == grid[9]
jmp _label_wrong if grid[0] == grid[10]
...
jmp _label_wrong if grid[78] == grid[79]
jmp _label_wrong if grid[78] == grid[80]
jmp _label_wrong if grid[79] == grid[80]
// check range
jmp _label_wrong if outofnumbers(grid[1])
jmp _label_wrong if outofnumbers(grid[2])
jmp _label_wrong if outofnumbers(grid[3])
jmp _label_wrong if outofnumbers(grid[4])
...
jmp _label_wrong if outofnumbers(grid[76])
jmp _label_wrong if outofnumbers(grid[77])
jmp _label_wrong if outofnumbers(grid[78])
jmp _label_wrong if outofnumbers(grid[79])
_label_correct:
print(correct)
return
_label_wrong:
print(wrong)
return
</code></pre></div></div>
<p>I wrote a compiler for this DSL, which was then used to compile the algorithm into an OISC program executable by the OISC VM. The OISC program is then packed together with the OISC VM as a standalone binary, which is delievered to the players.</p>
<p><strong>Multiple Solutions - Behind the Scenes</strong></p>
<p>During the competition, I was surprised to notice that multiple solutions exist. A quick debugging revealed the cause: the implementation of <code class="language-plaintext highlighter-rouge">outofnumbers (var)</code> in the compiler was incorrectly written in a way similar to <code class="language-plaintext highlighter-rouge">return var not in range [0...9]</code>. Since the Sudoku map is supposed to only contain 1 ~ 9, the correct implementation should be <code class="language-plaintext highlighter-rouge">return var not in range [1...9]</code>. Under such a scenario, multiple solutions exist because we allow the grids to be filled with 0.</p>
<p><strong>Sudoku Map</strong></p>
<div class="photoswipe-gallery center">
<p><a class="photoswipe photo" href="/assets/AncientGameV2/map.png" target="_blank" data-cropped="true" data-pswp-width="500" data-pswp-height="500">
<img class="image" src="/assets/AncientGameV2/map.png" width="250" height="250" alt="Sudoku Map" />
<span class="badge">Sudoku Map</span>
</a></p>
</div>
<p><strong>Solution</strong></p>
<p>To solve this challenge, there is no need to simplify all the logical operations. Since there is no complicated loop in the actual control flow, we can locate the conditions preventing the control flow from jumping to the part which outputs “Sorry” through simple control flow tracing and (manual/automated) symbolic analysis. Along the way, we extract the constraints for the non-Sorry branches. Finally, we can use an SMT solver to solve the constraints. (That’s how ThinerDAS solved this challenge.)</p>
<p>Flag: d3ctf{g5lk9t28zz47y3l6m2kosbajd2vk9e2dwghxgfktcki}</p>
<blockquote>
<p>Referenceable solution script: <a href="https://github.com/0h2o/D3CTF_Rev/blob/master/AncientGameV2/sol.py">sol.py</a> by <a href="https://github.com/byaidu">Byaidu</a></p>
</blockquote>
<p>The source code of this challenge (except for the compiler) and a duplicate of this post are uploaded to GitHub, check them out at: <a href="https://github.com/yype/D3CTF_Rev/tree/master/AncientGameV2" target="_blank">https://github.com/yype/D3CTF_Rev/tree/master/AncientGameV2</a>.</p>
<h2 id="more">More..</h2>
<p>I’ve always found OISC interesting. This challenge is just a demo of one of my ideas. I’m considering doing more interesting works related to OISC in the upcoming future.</p>