{"id":20,"date":"2023-08-10T04:40:25","date_gmt":"2023-08-10T04:40:25","guid":{"rendered":"https:\/\/nocoffei.com\/?p=20"},"modified":"2023-09-17T16:41:40","modified_gmt":"2023-09-17T16:41:40","slug":"a-ctf-challenge-using-the-nintendo-3ds-unique-features","status":"publish","type":"post","link":"https:\/\/nocoffei.com\/?p=20","title":{"rendered":"A CTF Challenge using the Nintendo 3DS\u2019 unique features"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Or, how to make your CTF challenge <em>much<\/em> harder than intended. This long-form article describes an unusual challenge I wrote last year.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This blog post was originally written for <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/reswitched-weekly-reboot.github.io\/posts\/3ds-ctf\/\">ReSwitched Weekly Reboot<\/a>, but that site didn\u2019t take off.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you attended a certain hackathon late last year, you might have noticed a challenge listed like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"599\" height=\"662\" src=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Challenge_Title.png\" alt=\"\" class=\"wp-image-58\" srcset=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Challenge_Title.png 599w, https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Challenge_Title-271x300.png 271w\" sizes=\"auto, (max-width: 599px) 100vw, 599px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You\u2019re given an ELF file and a mysterious \u201cfile.bin\u201d. What wonders lie within?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Apparently, it\u2019s a stripped 32-bit ARM binary. Let\u2019s open it up in our favorite decompiler and\u2026<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"649\" height=\"679\" src=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Decompiled_View_Initial.png\" alt=\"An image of the decompiled main function as shown in Ghidras pseudocode view. It is intended to be interpreted as gibberish, at there are many functions called with no names attached to them.\" class=\"wp-image-59\" srcset=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Decompiled_View_Initial.png 649w, https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Decompiled_View_Initial-287x300.png 287w\" sizes=\"auto, (max-width: 649px) 100vw, 649px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">First Steps<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s revisit those hints:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>&gt; Looks like he left some logs at key intersections... Installing devkitARM may prove beneficial.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The challenges target platform has already been spoiled by the title, but if one looked it up they\u2019d find that devkitARM is a complete toolchain for Nintendo handheld consoles, including the GBA, DS, and 3DS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As for those logs\u2026 It\u2019s probably meant that \u201ckey intersections\u201d means critical sections of the program flow. For the first of these, we can immediately see that \u201cromfs:\/file.bin\u201d is an argument to a function. That means it\u2019s <code>fopen<\/code>, and that the return value is a file pointer\u2026 We\u2019ll come back to that shortly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>GX_DisplayTransfer<\/code> sounds like the name of the previous function. Plug that into our search engine and we get linked <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/libctru.devkitpro.org\/gx_8h.html\">directly to some documentation<\/a>. Looks like the function is from libctru, which is the <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/libctru.devkitpro.org\/index.html\">user-mode library for writing useful homebrew on the 3DS<\/a>. Now what\u2019s that function do\u2026?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>&gt; Initiates a display transfer.<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Well, that\u2019s not very helpful, but this is a \u201ckey intersection\u201d, so it\u2019s probably really important to understand this function.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">By finding an <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/SVatG\/SkateStation\/blob\/master\/EffectCoolCube2.c#L176\">example<\/a> online (I\u2019m linking to one I found randomly), and <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/www.3dbrew.org\/wiki\/GPU\/External_Registers#Transfer_Engine\">searching 3dbrew<\/a>, a knowledge base of 3DS reverse engineering, then reading between the lines, one can determine that this is a DMA copy function.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" style=\"font-style:normal;font-weight:600\">DMA on the 3DS<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Those familiar with computer architecture will know that a DMA engine is, in brief terms, an auxiliary device that can transfer large amounts of data without the CPUs involvement, freeing it up to perform more useful work. On the 3DS, the DMA engine is treated as part of the GPU, and is commonly used to upload textures to VRAM quickly.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With that in mind, we can revisit the documentation and example, and this \u201ckey intersection\u201d makes a little more sense. The code leading up to this block opens romfs:\/file.bin, which we can safely assume is the same file.bin we were provided. We can see that a value is read out of the file object, passed into another function, then that value is passed into another function twice. The results of those functions are then the input and output addresses of the DMA transfer, implying that everything just mentioned was memory allocation. Let\u2019s make some changes to the decompiled function with that in mind\u2026<\/p>\n\n\n\n<div class=\"wp-block-kadence-tabs alignnone\"><div class=\"kt-tabs-wrap kt-tabs-id20_78f624-21 kt-tabs-has-5-tabs kt-active-tab-5 kt-tabs-layout-tabs kt-tabs-tablet-layout-inherit kt-tabs-mobile-layout-inherit kt-tab-alignment-left \"><ul class=\"kt-tabs-title-list\"><li id=\"tab-tab1\" class=\"kt-title-item kt-title-item-1 kt-tabs-svg-show-always kt-tabs-icon-side-right kt-tab-title-inactive\"><a href=\"#tab-tab1\" data-tab=\"1\" class=\"kt-tab-title kt-tab-title-1 \"><span class=\"kt-title-text\">Tab 1<\/span><\/a><\/li><li id=\"tab-tab2\" class=\"kt-title-item kt-title-item-2 kt-tabs-svg-show-always kt-tabs-icon-side-right kt-tab-title-inactive\"><a href=\"#tab-tab2\" data-tab=\"2\" class=\"kt-tab-title kt-tab-title-2 \"><span class=\"kt-title-text\">Tab 2<\/span><\/a><\/li><li id=\"tab-tab3\" class=\"kt-title-item kt-title-item-3 kt-tabs-svg-show-always kt-tabs-icon-side-right kt-tab-title-inactive\"><a href=\"#tab-tab3\" data-tab=\"3\" class=\"kt-tab-title kt-tab-title-3 \"><span class=\"kt-title-text\">Tab 3<\/span><\/a><\/li><li id=\"tab-tab4\" class=\"kt-title-item kt-title-item-4 kt-tabs-svg-show-always kt-tabs-icon-side-right kt-tab-title-inactive\"><a href=\"#tab-tab4\" data-tab=\"4\" class=\"kt-tab-title kt-tab-title-4 \"><span class=\"kt-title-text\">Tab 4<\/span><\/a><\/li><li id=\"tab-tab5\" class=\"kt-title-item kt-title-item-5 kt-tabs-svg-show-always kt-tabs-icon-side-right kt-tab-title-active\"><a href=\"#tab-tab5\" data-tab=\"5\" class=\"kt-tab-title kt-tab-title-5 \"><span class=\"kt-title-text\">Tab 5<\/span><\/a><\/li><\/ul><div class=\"kt-tabs-content-wrap\">\n<div class=\"wp-block-kadence-tab kt-tab-inner-content kt-inner-tab-1 kt-inner-tab_3237e6-47\"><div class=\"kt-tab-inner-content-inner\">\n<pre class=\"wp-block-code has-small-font-size\"><code><code>    uStack_14 = FUN_0010d4ec(\"romfs:\/file.bin\",&amp;DAT_00125008);\n    FUN_0010dc10(uStack_14,0,2);\n    iStack_18 = FUN_0010e1d4(uStack_14);\n    FUN_0010dc10(uStack_14,0,0);\n    uVar3 = FUN_00101610(iStack_18);\n    uStack_1c = FUN_00101aac(uVar3);\n    uVar3 = FUN_00101610(iStack_18);\n    uStack_20 = FUN_00101aac(uVar3);\n    FUN_0010d7dc(uStack_1c,iStack_18,1,uStack_14);\n    FUN_0010cd88(uStack_14);\n    uVar3 = FUN_00101610(iStack_18);\n    FUN_0010612c(uStack_1c,uVar3);\n    uStack_24 = FUN_00104dac(uStack_1c,FUN_00100020,uStack_20,FUN_00100020,2);\n    FUN_0010f40c(\"GX_DisplayTransfer returned %d\\n\",uStack_24);<\/code><\/code><\/pre>\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-kadence-tab kt-tab-inner-content kt-inner-tab-2 kt-inner-tab_eeb266-62\"><div class=\"kt-tab-inner-content-inner\">\n<pre class=\"wp-block-code has-small-font-size\"><code>    fp = fopen(\"romfs:\/file.bin\",&amp;DAT_00125008);\n    FUN_0010dc10(fp,0,2);\n    local_18 = FUN_0010e1d4(fp);\n    FUN_0010dc10(fp,0,0);\n    uVar3 = FUN_00101610(local_18);\n    local_1c = FUN_00101aac(uVar3);\n    uVar3 = FUN_00101610(local_18);\n    local_20 = FUN_00101aac(uVar3);\n    FUN_0010d7dc(local_1c,local_18,1,fp);\n    FUN_0010cd88(fp);\n    uVar3 = FUN_00101610(local_18);\n    FUN_0010612c(local_1c,uVar3);\n    local_24 = FUN_00104dac(local_1c,FUN_00100020,local_20,FUN_00100020,2);\n    FUN_0010f40c(\"GX_DisplayTransfer returned %d\\n\",local_24);<\/code><\/pre>\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-kadence-tab kt-tab-inner-content kt-inner-tab-3 kt-inner-tab_3f69fd-df\"><div class=\"kt-tab-inner-content-inner\">\n<pre class=\"wp-block-code has-small-font-size\"><code>    fp = fopen(\"romfs:\/file.bin\",&amp;DAT_00125008);\n    FUN_0010dc10(fp,0,2);\n    local_18 = FUN_0010e1d4(fp);\n    FUN_0010dc10(fp,0,0);\n    size_of_alloc = some_size_fxn(local_18);\n    local_1c = some_alloc_fxn(size_of_alloc);\n    size_of_alloc = some_size_fxn(local_18);\n    local_20 = some_alloc_fxn(size_of_alloc);\n    FUN_0010d7dc(local_1c,local_18,1,fp);\n    FUN_0010cd88(fp);\n    size_of_alloc = some_size_fxn(local_18);\n    FUN_0010612c(local_1c,size_of_alloc);\n    local_24 = FUN_00104dac(local_1c,FUN_00100020,local_20,FUN_00100020,2);\n    FUN_0010f40c(\"GX_DisplayTransfer returned %d\\n\",local_24);<\/code><\/pre>\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-kadence-tab kt-tab-inner-content kt-inner-tab-4 kt-inner-tab_15d427-01\"><div class=\"kt-tab-inner-content-inner\">\n<pre class=\"wp-block-code has-small-font-size\"><code><code>    fp = fopen(\"romfs:\/file.bin\",&amp;DAT_00125008);\n    FUN_0010dc10(fp,0,2);\n    local_18 = FUN_0010e1d4(fp);\n    FUN_0010dc10(fp,0,0);\n    size_of_alloc = some_size_fxn(local_18);\n    input_buf = some_alloc_fxn(size_of_alloc);\n    size_of_alloc = some_size_fxn(local_18);\n    output_buf = some_alloc_fxn(size_of_alloc);\n    fread(input_buf,local_18,1,fp);\n    FUN_0010cd88(fp);\n    size_of_alloc = some_size_fxn(local_18);\n    FUN_0010612c(input_buf,size_of_alloc);\n    dt_result = GX_DisplayTransfer(input_buf,FUN_00100020,output_buf,FUN_00100020,2);\n    printf(\"GX_DisplayTransfer returned %d\\n\",dt_result);<\/code><\/code><\/pre>\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-kadence-tab kt-tab-inner-content kt-inner-tab-5 kt-inner-tab_7425d1-ed\"><div class=\"kt-tab-inner-content-inner\">\n<pre class=\"wp-block-code has-small-font-size\"><code>    fp = fopen(\"romfs:\/file.bin\",&amp;DAT_00125008);\n    FUN_0010dc10(fp,0,2);\n    local_18 = FUN_0010e1d4(fp);\n    FUN_0010dc10(fp,0,0);\n    size_of_alloc = some_size_fxn(local_18);\n    input_buf = some_alloc_fxn(size_of_alloc);\n    size_of_alloc = some_size_fxn(local_18);\n    output_buf = some_alloc_fxn(size_of_alloc);\n    fread(input_buf,local_18,1,fp);\n    FUN_0010cd88(fp);\n    size_of_alloc = some_size_fxn(local_18);\n    FUN_0010612c(input_buf,size_of_alloc);\n    dt_result = GX_DisplayTransfer(input_buf,0x100020,output_buf,0x100020,2);\n    printf(\"GX_DisplayTransfer returned %d\\n\",dt_result);<\/code><\/pre>\n<\/div><\/div>\n<\/div><\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">One noteworthy aspect of this, at least here, is that Ghidra mistakenly  types the \u201cdimension\u201d flag of the display transfer as a function  pointer. It\u2019s necessary to correct this issue; once it does, we can see  that the dimension is composed of two 16-bit integers packed together as  is seen in the documentation and example with the macro <code>GX_BUFFER_DIM<\/code>.  We need not worry too much about this, it\u2019s effectively a glorified  size parameter with a display transfer. (As an aside, this parameter  gets <em>much<\/em> more interesting with the GX_TextureCopy counterpart.) We also see a <code>flags<\/code> value of 2, which probably isn\u2019t important, right?<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Executable mapping?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The other \u201ckey intersection\u201d referred to in the logs is a bit further down:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>&gt; Executable mirroring of read-write page returned page %p\\n<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I intended for this segment of the code to be <em>completely ignored<\/em>, because it\u2019s a lot of function calls towards something that\u2019s meant to be largely overlooked. However, I believe that this was not made clear enough.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here\u2019s what that block looks like in the source code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    ctrdl_initCodeAllocator();\n    char* codeAlloc = ctrdl_allocateAligned(nextPow2(fsize));\n    memcpy(codeAlloc, display_transfer_result, nextPow2(fsize));\n\n    void* executable_addr = ctrdl_allocateCode(codeAlloc, fsize);\n\n    ctrdl_changePermission(\n            CUR_PROCESS_HANDLE, executable_addr,\n            ctrdl_alignSize(fsize),\n            MEMPERM_READEXECUTE);\n    printf(\"Executable mirroring of read-write page returned page %p\\n\", executable_addr);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">It\u2019s now time to explain what this does. The 3DS, like all contemporary operating systems, employs W^X (write or execute, but never both) memory protection, meaning that you can\u2019t execute a page that you\u2019ve written to and vice versa. One can get around this using <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/LumaTeam\/Luma3DS\">Luma3DS<\/a>, which relaxes enforcement of this policy; you can then have an allocated block of memory mapped as RW and a mirror of that memory mapped as RX. Ultimately, though, those technical details are unnecessary to understand what\u2019s important- this segment takes the result of the DMA operation and allows it to be executed.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/08\/lima3ds.png\" alt=\"A modified image of the Nintendo 3DS logo, which reads &quot;*new* Nintendo Lima 3DS XL&quot;.\" class=\"wp-image-60\" style=\"width:640px;height:60px\" width=\"640\" height=\"60\"\/><figcaption class=\"wp-element-caption\">It&#8217;s Luma3DS, not Lima3DS.<\/figcaption><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">It\u2019s important to remember while reading the rest of this article that <em>code is data<\/em>. For example, each single ARM instruction (add, subtract, multiply, compare, branch\u2026) is a series of four bytes, decoded and executed by the processor. As <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/en.wikipedia.org\/wiki\/Code_as_data\">Wikipedia<\/a> puts it:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>&gt; Code-as-data is also a principle of the Von Neumann architecture, since stored programs and data are both represented as bits in the same memory device. This architecture offers the ability to write self-modifying code. It also opens the security risk of disguising a malicious program as user data and then using an exploit to direct execution to the malicious program.<\/code><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Main Loop<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The main loop of the program is another subtle mistake, meant to be trivial to understand but in reality frustrating.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>    do {\n      iVar2 = FUN_001078e0();\n      if (iVar2 == 0) goto LAB_00101914;\n      FUN_00105e5c(2,1);\n      FUN_00104d08();\n      FUN_00107e68();\n      local_30 = FUN_0010810c();\n      param_3 = local_5c;\n      local_c = (*local_2c)(local_30,local_c);\n    } while (local_5c&#91;0] == 0);\n    FUN_0010f520(\"Congratulations! Your flag is:\");\n    printf(local_5c);<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">One thing we are able to see is that somehow, if the loop exits correctly, <code>local_5c<\/code> must contain the output flag. But what\u2019s all the rest?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you can figure out that <code>FUN_0010810c<\/code> is <code>hidKeysDown()<\/code>, the 3DS function which gets the buttons that were just pressed, this would have instantly become clear as day. However, ascertaining this is not a trivial task, which I\u2019ll discuss in more detail shortly. The main loop passes through the buttons pressed this frame, and somehow expects the executable code to spit out the flag if a certain button combination was pressed. Ghidra is actually mis-decompiling this code pointer call; the program does pass <code>flag_buffer<\/code> as a parameter, so I had to override the function signature to produce the correct output.<\/p>\n\n\n\n<div class=\"wp-block-kadence-tabs alignnone\"><div class=\"kt-tabs-wrap kt-tabs-id20_131c75-14 kt-tabs-has-2-tabs kt-active-tab-1 kt-tabs-layout-tabs kt-tabs-tablet-layout-inherit kt-tabs-mobile-layout-inherit kt-tab-alignment-left \"><ul class=\"kt-tabs-title-list\"><li id=\"tab-tab1\" class=\"kt-title-item kt-title-item-1 kt-tabs-svg-show-always kt-tabs-icon-side-right kt-tab-title-active\"><a href=\"#tab-tab1\" data-tab=\"1\" class=\"kt-tab-title kt-tab-title-1 \"><span class=\"kt-title-text\">Tab 1<\/span><\/a><\/li><li id=\"tab-tab2\" class=\"kt-title-item kt-title-item-2 kt-tabs-svg-show-always kt-tabs-icon-side-right kt-tab-title-inactive\"><a href=\"#tab-tab2\" data-tab=\"2\" class=\"kt-tab-title kt-tab-title-2 \"><span class=\"kt-title-text\">Tab 2<\/span><\/a><\/li><\/ul><div class=\"kt-tabs-content-wrap\">\n<div class=\"wp-block-kadence-tab kt-tab-inner-content kt-inner-tab-1 kt-inner-tab_53707f-54\"><div class=\"kt-tab-inner-content-inner\">\n<pre class=\"wp-block-code has-small-font-size\"><code>    do {\n      iVar2 = FUN_001078e0();\n      if (iVar2 == 0) goto LAB_00101914;\n      FUN_00105e5c(2,1);\n      FUN_00104d08();\n      FUN_00107e68();\n      uVar8 = hidKeysDown();\n      some_input_and_output = (*code_pointer)(uVar8,some_input_and_output);\n    } while (flag_buffer&#91;0] == 0);\n    FUN_0010f520(\"Congratulations! Your flag is:\");\n    printf(flag_buffer);<\/code><\/pre>\n<\/div><\/div>\n\n\n\n<div class=\"wp-block-kadence-tab kt-tab-inner-content kt-inner-tab-2 kt-inner-tab_d55bd0-62\"><div class=\"kt-tab-inner-content-inner\">\n<pre class=\"wp-block-code has-small-font-size\"><code>    do {\n      iVar2 = FUN_001078e0();\n      if (iVar2 == 0) goto LAB_00101914;\n      FUN_00105e5c(2,1);\n      FUN_00104d08();\n      FUN_00107e68();\n      uVar8 = hidKeysDown();\n      some_input_and_output = (*code_pointer)(uVar8,some_input_and_output,flag_buffer);\n    } while (flag_buffer&#91;0] == 0);\n    FUN_0010f520(\"Congratulations! Your flag is:\");\n    printf(flag_buffer);<\/code><\/pre>\n<\/div><\/div>\n<\/div><\/div><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s review what we\u2019ve learned so far:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The program opened <code>file.bin<\/code> and loaded it into memory.<\/li>\n\n\n\n<li>It copied it with <code>GX_DisplayTransfer<\/code> via the DMA hardware.<\/li>\n\n\n\n<li>It mapped it as executable.<\/li>\n\n\n\n<li>It then executed the file and expected it to spit the flag out after pressing some sort of button combination.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><em>So our flag is inside executable code in <code>file.bin<\/code>!<\/em> Let\u2019s disassemble that file\u2026<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>str fp, &#91;sp, #-4]!\nadd fp, sp, #0\nstr r1, &#91;fp, #-0x14]\nstr r2, &#91;fp, #-0x18]\nstr r3, &#91;fp, #-0x14]\nb #0x450\nbne #0x3c\nldr r3, &#91;fp, #-0x10]\nldr r3, &#91;fp, #-0x14]\ncmp r3, #2\nbic r3, r3, #0x80000000\nbic r3, r3, #0x80\n\n(...Useless garbage continues on)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Aw, nuts.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It couldn\u2019t be that easy, right? After the accidentally substantial reverse engineering effort, we\u2019re confronted with a bizarre puzzle. Somewhere along the line, this apparently executable code was transformed from gibberish into properly functioning code that will spit out our flag.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Intermission- An Accidental Odyssey<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This challenge was designed to have almost all of the information above available to you from the beginning. You were intended to learn that the file was opened, read in, fed to <code>GX_DisplayTransfer<\/code> (you were still intended to learn the meaning of that function on your own) and finally mapped as executable and run. Instead, it took careful, painful reverse engineering, with a lot of insight and guesswork. What went wrong?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The first major hurdle is that the meaning of <code>GX_DisplayTransfer<\/code>, which is not a mere search away. I spent quite a long time tinkering with 3DS homebrew, writing my own competent <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/Swiftloke\/ModMoon\/tree\/master\/source\/sdraw\">2D renderer<\/a> using the custom graphics API exposed with <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/devkitPro\/citro3d\">citro3d<\/a>, so of course <em>I<\/em> know what <code>GX_DisplayTransfer<\/code> does, but it takes a fair bit of reading to ascertain its meaning for yourself, and even then it might not be clear what the function is. I mentioned 3dbrew earlier; the average reverse engineer might not come across that treasure trove of information.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The next problem was with the RW-&gt;RX remapping. As aforementioned, I intended for that segment to be entirely ignored by the reverse engineer, but the way the logs were actually written meant that the hint was almost certainly not clear enough. Another hint along the lines of <code>Beginning to map DisplayTransfer result as executable...<\/code> may have significantly improved the situation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Another major problem was failing to properly explain that the unknown function in the main loop was <code>hidKeysDown()<\/code>. My challenge design revolved around people knowing that this was the function in question. I completely failed to elaborate on this, and the challenge was made much less clear as a result.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, this challenge stripped <em>symbols<\/em> from the binary, the strings that identify the meanings of a function. While designing this challenge, I was running up against the deadline to the CTF and needed a way to prevent the challenge from simply being run on a real 3DS. After all, since the code is clearly valid by the time it\u2019s executed, one could simply attach a debugger to the running process and dump the code at that time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Without any better ideas, I provided a stripped .ELF file as the binary. Without symbols, the devkitARM toolchain can\u2019t build it into an executable image. One of the more saavy people who attempted this challenge made many attempts to get it to run without progress; I figure at that rate, anyone who did manage to execute it deserves their flag (UPDATE: After the original posting of this article, one person successfully executed the program to retrieve the flag.) But at what cost? Recall that this was meant to be a \u201c6\/10\u201d challenge, <em>run as a side event during a hackathon<\/em>, with one neat twist to discover; instead, it required a mountain of reverse engineering to even get far enough to discover the twist. Only one person got far enough to even consider uncovering the secret, and only long after the CTF had ended when they continued grinding away at it.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">This One Weird Trick Will Scramble Your Data At The Hardware Level!<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">With my lament about accidentally creating a CTF challenge raised from hell, let\u2019s come back to how that nonsense code gets transformed into being executable.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s review the process of how the data flows:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Opened and read using the standard library functions.<\/li>\n\n\n\n<li>Copied using the GPUs DMA function.<\/li>\n\n\n\n<li>Mapped as executable using a library that I didn\u2019t write.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Where does that magical transformation happen?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No, seriously, engage your brain. Use some reading comprehension.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Perhaps the name of the challenge would help guide the way?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u2026<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If you guessed \u201cIt\u2019s the DMA function\u201d, congratulations! You\u2019ve been paying attention. If you skipped straight to the answer, well, I get it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The PICA200 GPU in the 3DS employs swizzling in its texturing pipeline. This is fairly confusing terminology, as this phrase has multiple other uses in the realm of graphics (see vertex swizzling, i.e. <code>vertex.xyz = vertex.zxy<\/code>). In this context, swizzling means that texture memory is physically stored differently than would appear logical to the human eye. This is done for the purposes of spatial locality- if you re-arrange the data, you\u2019re able increase your cache hit rate and therefore make fewer (and costlier) trips to main memory.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"159\" height=\"127\" src=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Normal_Versus_Swizzled.png\" alt=\"\" class=\"wp-image-61\"\/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">So what does this have to do with a DMA transfer? Well, earlier it was stated that<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>&gt; We also see a<\/code> <code>flags value of 2, which probably isn't important, right?<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Maybe it is important. Let\u2019s check that documentation a little more closely\u2026 We can see a <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/libctru.devkitpro.org\/gx_8h.html#a19b4c4ad91299256a7c285e9ba7673b9\">list of transfer flags<\/a> here (which you\u2019d be able to understand were transfer flags by looking at <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/SVatG\/SkateStation\/blob\/2818789c6d060a0144bb23af8de997d2ed9106b5\/source\/Tools.h#L54\">the example<\/a>). That <code>GX_TRANSFER_OUT_TILED(x)<\/code> flag is in fact referring to the very same form of tiling! The DMA engine on the 3DS has some functionality that allows it to, during the copy process, re-arrange data that is laid out in the way one expects into the way the GPU expects it. This can be used to load textures at runtime; think ports of games in which ahead-of-time translation isn\u2019t possible, or very bad code which decodes and loads PNG files at runtime instead of AoT conversion. (The latter was the de-facto standard while writing 3DS homebrew for a few years before the <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/devkitPro\/tex3ds\">tex3ds<\/a> project came out.)<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>GX_TRANSFER_OUT_TILED(1)<\/code> is equal to <code>(1&lt;&lt;1)<\/code> which is equal to 2. So that means that this DMA transfer is applying that tiling flag, and the bytes are re-arranged to fit the tiled format. That\u2019s how this data winds up being real code instead of garbage!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">At this point, there are two solutions to the problem. You\u2019ll need to tile this data to get the normal code back out. You can either:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Treat the tiling system as a black box, or<\/li>\n\n\n\n<li>Understand how the tiling function works<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">I\u2019ll go over both solutions, as well as some trivia about how the tiling system works that made development a <em>lot<\/em> easier.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Understand how the tiling function works<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The PICA200 follows the common <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/en.wikipedia.org\/wiki\/Z-order_curve\">Morton Order<\/a> tiling curve, also known as Z-Curve. As aforementioned, the goal of tiling is to increase <em>spatial locality<\/em> of texture accesses by re-arranging blocks. Morton Order is not particularly intuitive to me, and in fact I had little knowledge of it before writing this article; understanding that the 3DS has swizzling hardware without comprehending its inner workings was enough to write the challenge. I\u2019ll leave the Wikipedia link for those who seek to learn the idea for themselves, as they do a better job explaining it than I could.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Treat the tiling system as a black box<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This approach effectively means that you\u2019ll rewrite the challenge yourself, compile it, and execute it, either on real 3DS hardware or on an emulator. Luckily, you don\u2019t need to actually execute the code, you just need to dump it; that\u2019ll save you some headache.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Let\u2019s find another, more concise example of how display transfers work. I\u2019m going to dig up an ancient piece of history which inspired this challenge in the first place- the aforementioned piece of code that would decode and tile PNG files at runtime. This was copied around the internet many, many times over the course of 2016 through 2018 (when <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/citro2d.devkitpro.org\/\">citro2d<\/a> and tex3ds were released, superseding it). I too used this code, and <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/Swiftloke\/ModMoon\/blob\/bc32168ac060cca96ca45f075becce5118d1ba5f\/source\/sdraw.cpp#L92\">we can see it in an early branch of my renderer project<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>If you are interested in writing homebrew, do not use this code! It\u2019s dead for a reason! We have citro2d and tex3ds which do the same thing but faster and easier! See <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/devkitPro\/3ds-examples\/blob\/master\/graphics\/gpu\/gpusprites\/source\/main.c\">the current examples<\/a> for a modern lesson!<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As you can see, it\u2019s fairly straightforward; one needs to allocate enough memory using <code>linearAlloc<\/code> (a block of memory that\u2019s guaranteed to be readable by the GPU) first, then shove your data into it. Flush the cache so the GPU can read your data, then initiate the transfer and wait for the GPU to signal that it\u2019s done. (<code>C3D_SafeDisplayTransfer<\/code> is a citro3d function that allows usage of display transfers within a fully initialized C3D environment, such that the two don\u2019t trip over each other.) What\u2019s more important are the <code>TEXTURE_TRANSFER_FLAGS<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ Used to convert textures to 3DS tiled format\n\/\/ Note: vertical flip flag set so 0,0 is top left of texture\n#define TEXTURE_TRANSFER_FLAGS \\\n\t(GX_TRANSFER_FLIP_VERT(1) | GX_TRANSFER_OUT_TILED(1) | GX_TRANSFER_RAW_COPY(0) | \\\n\tGX_TRANSFER_IN_FORMAT(GX_TRANSFER_FMT_RGBA8) | GX_TRANSFER_OUT_FORMAT(GX_TRANSFER_FMT_RGBA8) | \\\n\tGX_TRANSFER_SCALING(GX_TRANSFER_SCALE_NO))<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">In this code, the vertical flip flag is set to enable a texturing convention; obviously don\u2019t do this, but do use the tiling flag.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">With a little bit of elbow grease, and an installation of devkitARM as the challenge description recommends, you can use this tutorial to dump the original code.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Aside: How to un-swizzle data<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Writing this challenge requiring that I convert the code using the inverse of the swizzling function first, since F(F'(x)) = x. I started out by attempting to use tex3ds\u2019 <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/devkitPro\/tex3ds\/blob\/master\/source\/swizzle.cpp#L28\">inverse swizzler<\/a>, but I couldn\u2019t get it quite right. While bashing my head against the wall, I inadventently nerd-sniped <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/github.com\/LiquidFenrir\">DeltaV<\/a> into performing a detailed, graphical series of tests on the GPU to see how flags affected the display transfer engine, and the results were quite interesting\u2026<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"770\" height=\"166\" src=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Display_Transfer_Experiments.png\" alt=\"\" class=\"wp-image-62\" srcset=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Display_Transfer_Experiments.png 770w, https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Display_Transfer_Experiments-300x65.png 300w, https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Display_Transfer_Experiments-768x166.png 768w\" sizes=\"auto, (max-width: 770px) 100vw, 770px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This image showcases the results of the outputs of each combination of flags with <code>GX_DisplayTransfer<\/code> and its close cousin <code>GX_TextureCopy<\/code> (which is another DMA function for slightly different purposes.) Input into each is a swizzled texture, now let\u2019s take a look right-to-left at the results\u2026<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Raw and tiled: Spits out the output with texture copy, hardware freeze with display transfer. (You\u2019re supposed to use texture copy for non-transformative DMAs.)<\/li>\n\n\n\n<li>Not raw and tiled: Tiles the output. This makes a double-tile from the input.<\/li>\n\n\n\n<li>Raw and not tiled: Same results as raw and tiled.<\/li>\n\n\n\n<li>Not raw and not tiled: <strong>The original image is returned.<\/strong><\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">That\u2019s right, in addition to supporting tiling, the 3DS hardware supports <strong>inverse tiling<\/strong>! This is implemented to make final output to a framebuffer easy. Thus, scrambling the challenge code was as simple as changing a one to a zero and dumping the results to a file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Climbing the Summit<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The reverse engineering nightmare overcome, the inscrutable DMA function understood and the obfuscated code un-scrambled, now at last the end is in sight\u2026<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Well, this is really not that complicated\u2026 <code>param_2<\/code> is obviously a state which is stored by the calling function, we\u2019ve already established that <code>param_1<\/code> is <code>hidKeysDown()<\/code> and <code>param_3<\/code> is the output flag. Let\u2019s update with that\u2026<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So that <code>state<\/code> value seems to rely on the current button inputs. If you click the correct sequence of buttons, it\u2019ll spit out the flag. If you went really far and <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/libctru.devkitpro.org\/hid_8h.html#a2f80701c36e79c0640d91c788feee0b3aa7b3a3d0503a46bec51c6e3da95747cc\">checked the bitmask of each button<\/a>, you\u2019d find that the function is actually searching for the Konami Code\u2026<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Anyway, let\u2019s move on to that flag. If the Konami Code is successfully input, the function will write some interesting values into the output buffer and then XOR those values with a constant (which is, as an Easter Egg, the <a href=\"https:\/\/web.archive.org\/web\/20230716171237\/https:\/\/en.wikipedia.org\/wiki\/Fast_inverse_square_root\">Quake inverse square root constant<\/a>) and spit that out. Do that XOR yourself and you\u2019ll have the flag.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">A last-minute disaster<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Having spent two Saturdays building this challenge, the morning before the CTF was set to start I got a message from a colleague:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Them:\n\nOh shit\nWhat's the flag\nBruh is it $THE_FLAG\nI just did ``strings file.bin``\n\nMe:\n\nIs my challenge actually that broken\nYes that's the flag<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">And sure enough:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ strings file.bin\nl1ng_1s_\nfUn!}\nhackD\nxi{SwiZz<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is how I learned not to leave your strings in plaintext. All of this incredibly complicated obfuscation couldn\u2019t stop people from discovering the flag, as four-byte segments had to still be present in the obfuscated file. I quickly added the XORing function and spent the next hour rebuilding the challenge (it shouldn\u2019t be too surprising that it\u2019s a <em>major<\/em> pain in the neck to build).<\/p>\n\n\n\n<div class=\"wp-site-blocks\"><main class=\"wp-block-group is-layout-flow\" id=\"wp--skip-link--target\"><div class=\"entry-content wp-block-post-content has-global-padding is-layout-constrained\"><h2 class=\"wp-block-heading\">Conclusion<\/h2><p>Although not my first CTF challenge to be widely distributed, this  was by far my most ambitious, and it shows along the way with the  stacking mistakes making it inaccessible to all but the most talented  reverse engineers. This was also my first long-form article since 2016,  in which I wrote about some concepts of 3DS security without having  formed a grasp on programming.<\/p><p>Reflecting on the value of writing, it\u2019s interesting that in terms of  pure time investment writing this article probably took me about as  long as making the challenge in the first place. Grabbing citations,  checking notes, reverse engineering the challenge myself and making  diagrams\u2026 It\u2019s tough work, but by doing it I get to share everything I  learned.<\/p><p>If you\u2019ve made it to the end of this article, I applaud your interest  (or possible insanity). There will be more articles to come soon on  some challenges I wrote earlier in 2022. In order to whet your appetite,  I\u2019ll share the following teaser:<\/p><\/div><\/main><\/div>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Intel_Itanium_logo.svg_.png\" alt=\"\" class=\"wp-image-63\" style=\"width:249px;height:303px\" width=\"249\" height=\"303\" srcset=\"https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Intel_Itanium_logo.svg_.png 394w, https:\/\/nocoffei.com\/wp-content\/uploads\/2023\/09\/Intel_Itanium_logo.svg_-246x300.png 246w\" sizes=\"auto, (max-width: 249px) 100vw, 249px\" \/><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Or, how to make your CTF challenge much harder than intended. This long-form article describes an unusual challenge I wrote last year.<\/p>\n","protected":false},"author":2,"featured_media":57,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-20","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/posts\/20","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=20"}],"version-history":[{"count":0,"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/posts\/20\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=\/wp\/v2\/media\/57"}],"wp:attachment":[{"href":"https:\/\/nocoffei.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=20"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=20"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nocoffei.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=20"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}