چالش مهندسی معکوس دوم - دندان مار

توضیحات

دوش چون طاووس می‌نازیدم اندر باغ وصل
دیگر امروز از فراق یار می‌پیچم چو مار
قالب پرچم در این سوال به صورت parcham{some_l33t_string} است.

حل چالش

اول از هر چیز، نوع فایل را بررسی می‌کنیم:

$ file a.out 
a.out: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e3ecfce816610debcdf5b324bbff30a86b4dc4d0, stripped

وقتی که برنامه را با ltrace یا strace اجرا می‌کنیم، چیز خاصی عایدمان نمی‌شود. بنابراین با رادار۲ سراغش می‌رویم:

$ radare2 -AA a.out 
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[x] Finding function preludes
[x] Enable constraint types analysis for variables
[0x00001100]> s main
[0x000011e9]> pdgo
    0x000011e9    |undefined8 main(void)
                  |{
                  |    int32_t iVar1;
                  |    undefined8 uVar2;
                  |    int64_t iVar3;
                  |    int64_t *piVar4;
                  |    int64_t *piVar5;
                  |    int64_t var_480h;
                  |    int64_t var_40h;
                  |    int64_t var_38h;
                  |    int32_t var_30h;
                  |    undefined4 var_2ch;
                  |    int64_t var_28h;
                  |    int64_t var_20h;
                  |    int64_t var_18h;
                  |    int64_t var_10h;
                  |    int32_t var_8h;
                  |    int64_t var_4h;
                  |    
    0x00001206    |    iVar3 = 0x87;
    0x0000120e    |    piVar4 = (int64_t *)0x2010;
    0x0000120e    |    piVar5 = &var_480h;
    0x00001211    |    while (iVar3 != 0) {
    0x00001211    |        iVar3 = iVar3 + -1;
    0x00001211    |        *piVar5 = *piVar4;
    0x00001211    |        piVar4 = piVar4 + 1;
    0x00001211    |        piVar5 = piVar5 + 1;
                  |    }
    0x0000121c    |    *(undefined4 *)piVar5 = *(undefined4 *)piVar4;
    0x00001229    |    *(undefined *)((int64_t)piVar5 + 4) = *(undefined *)((int64_t)piVar4 + 4);
    0x0000123a    |    var_10h = (int64_t)fcn.0000134c;
    0x00001245    |    var_18h = (int64_t)fcn.00001882;
    0x00001250    |    var_20h = (int64_t)fcn.00001cca;
    0x0000125b    |    var_28h = (int64_t)fcn.0000188d;
    0x00001266    |    iVar1 = fcn.00001cd5((int64_t)fcn.0000134c);
    0x0000126e    |    if (iVar1 == -1) {
    0x00001270    |        uVar2 = 1;
                  |    } else {
    0x0000127a    |        fcn.0000134c();
    0x0000127f    |        var_2ch = 0x43d;
    0x00001286    |        var_4h._0_4_ = 0;
    0x000012bd    |        while ((int32_t)var_4h < 0x43d) {
    0x0000129c    |            var_40h = (int64_t)(undefined *)(var_10h + (int32_t)var_4h);
    0x000012b1    |            *(undefined *)(var_10h + (int32_t)var_4h) = *(undefined *)((int64_t)&var_480h + (int64_t)(int32_t)var_4h);
    0x000012b3    |            var_4h._0_4_ = (int32_t)var_4h + 1;
                  |        }
    0x000012c3    |        iVar1 = (int32_t)var_18h - (int32_t)var_10h;
    0x000012cc    |        var_30h = iVar1 + -0x43d;
    0x000012d3    |        var_8h = 0;
    0x00001309    |        while (var_8h < iVar1 + -0x440) {
    0x000012ee    |            var_38h = var_10h + (int64_t)var_8h + 0x43d;
    0x000012f9    |            *(undefined *)var_38h = 0x90;
    0x000012fc    |            var_8h = var_8h + 1;
                  |        }
    0x0000131e    |        sym.imp.printf(0x2008, var_20h);
    0x00001336    |        sym.imp.printf(0x2008, var_20h);
    0x00001340    |        sym.imp.putchar(10);
    0x00001345    |        uVar2 = 0;
                  |    }
    0x0000134b    |    return uVar2;
                  |}

تابع main بسیار کوتاه به نظر می‌رسد. در جایی تابع printf یا puts یا چیزهای مشابه مشاهده نمی‌شود که بتوان چاپ‌شدن مارپیچ‌ها را به آن منسوب کرد. پس احتمالا تابع fcn.0000134c که در خط 0x0000127a صدا زده شده، وظیفه‌ی چاپ مارپیچ‌ها را بر عهده دارد.
یک حرکت اشتباه، این است که همین ابتدا به بررسی این تابع بپردازیم. اصولا بایستی در مهندسی معکوس، توابعی که درون تابع فعلی صدا زده‌شده‌اند، به شکل جعبه‌سیاه ببینیم و تلاش کنیم که همین تابع فعلی را تا حد ممکن درک کنیم. پس به بررسی تابع main ادامه می‌دهیم.
یک شرط در 0x0000126e می‌بینیم که احتمالا بایستی درون else برویم. در غیر این صورت برنامه خیلی زود به پایان می‌رسد و بعید است که پرچم را تا آن لحظه ساخته باشد. پس به درون else می‌رویم و فرض می‌کنیم که شرط نادرست خواهد بود.
در همان ابتدای بلوک مربوط به else، یک حلقه در خط 0x000012bd مشاهده می‌شود. با کمی دقت و به سادگی، می‌توان فهمید که این حلقه مشغول به کپی‌کردن یک قسمت از حافظه درون یک قسمت دیگر است. در کجا کپی می‌کند؟ در var_10h که کمی بالاتر مقدارش برابر با آدرس تابع fcn.0000134c قرار داده است. از کجا کپی می‌کند؟ از بافر var_480h که به نظر می‌رسد مقداردهی نشده است. اما اگر بیشتر دقت کنیم، می‌بینیم که آدرس این بافر در خط 0x0000120e مورد استفاده قرار گرفته است و درون piVar5 ریخته شده است. سپس از حافظه‌ای که piVar4 به آن اشاره می‌کند، به داخل piVar5 کپی شده است.
پس بایستی سراغ حافظه‌ی مربوط به piVar4 برویم و ببینم آنجا چه چیزی نوشته شده است که بدین ترتیب کپی می‌شود. بنابراین انجامش می‌دهیم:

[0x000011e9]> s 0x2010
[0x00002010]> px
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x00002010  f30f 1efa 5548 89e5 4881 ecd0 0000 00c7  ....UH..H.......
0x00002020  45e4 b50c 0000 c745 fc01 0000 00c7 45e0  E......E......E.
0x00002030  0100 0000 c745 8070 0000 00c7 4584 7200  .....E.p....E.r.
0x00002040  0000 c745 8868 0000 00c7 458c 6d00 0000  ...E.h....E.m...
0x00002050  c745 9041 0000 00c7 4594 3300 0000 c745  .E.A....E.3....E
0x00002060  9830 0000 00c7 459c 3300 0000 c745 a034  .0....E.3....E.4
0x00002070  0000 00c7 45a4 7400 0000 c745 a85f 0000  ....E.t....E._..
0x00002080  00c7 45ac 3100 0000 c745 b034 0000 00c7  ..E.1....E.4....
0x00002090  45b4 5900 0000 c745 b870 0000 00c7 45bc  E.Y....E.p....E.
0x000020a0  5400 0000 c745 c048 0000 00c7 45c4 6e00  T....E.H....E.n.
0x000020b0  0000 c745 c87d 0000 00c7 8530 ffff ff61  ...E.}.....0...a
0x000020c0  0000 00c7 8534 ffff ff63 0000 00c7 8538  .....4...c.....8
0x000020d0  ffff ff61 0000 00c7 853c ffff ff7b 0000  ...a.....<...{..
0x000020e0  00c7 8540 ffff ff77 0000 00c7 8544 ffff  ...@...w.....D..
0x000020f0  ff73 0000 00c7 8548 ffff ff6d 0000 00c7  .s.....H...m....
0x00002100  854c ffff ff5f 0000 00c7 8550 ffff ff55  .L..._.....P...U

به نظر می‌رسد که محتوای این قسمت از حافظه، نظم خاصی دارد. مثلا حرف E را به تکرر بسیار می‌توان مشاهده کرد. از روی بایت‌های آن، می‌توان حدس زد که احتمالا در این قسمت، تعدادی دستور قرار داده شده است. پس حدس را امتحان می‌کنیم و دستور pd را اجرا می‌کنیم:

[0x00002010]> pd
            ; DATA XREF from main @ 0x11ff
            0x00002010      f30f1efa       endbr64
            0x00002014      55             push rbp
            0x00002015      4889e5         mov rbp, rsp
            0x00002018      4881ecd00000.  sub rsp, 0xd0
            0x0000201f      c745e4b50c00.  mov dword [rbp - 0x1c], 0xcb5 ; 3253
            0x00002026      c745fc010000.  mov dword [rbp - 4], 1
            0x0000202d      c745e0010000.  mov dword [rbp - 0x20], 1
            ; DATA XREF from fcn.00001d40 @ 0x1d5d
            0x00002034      c74580700000.  mov dword [rbp - 0x80], 0x70 ; 'p'
            0x0000203b      c74584720000.  mov dword [rbp - 0x7c], 0x72 ; 'r'
            0x00002042      c74588680000.  mov dword [rbp - 0x78], 0x68 ; 'h'
            0x00002049      c7458c6d0000.  mov dword [rbp - 0x74], 0x6d ; 'm'
            0x00002050      c74590410000.  mov dword [rbp - 0x70], 0x41 ; 'A'
            0x00002057      c74594330000.  mov dword [rbp - 0x6c], 0x33 ; '3'
            0x0000205e      c74598300000.  mov dword [rbp - 0x68], 0x30 ; '0'
            0x00002065      c7459c330000.  mov dword [rbp - 0x64], 0x33 ; '3'
            0x0000206c      c745a0340000.  mov dword [rbp - 0x60], 0x34 ; '4'
            0x00002073      c745a4740000.  mov dword [rbp - 0x5c], 0x74 ; 't'
            0x0000207a      c745a85f0000.  mov dword [rbp - 0x58], 0x5f ; '_'
            0x00002081      c745ac310000.  mov dword [rbp - 0x54], 0x31 ; '1'
            0x00002088      c745b0340000.  mov dword [rbp - 0x50], 0x34 ; '4'
            0x0000208f      c745b4590000.  mov dword [rbp - 0x4c], 0x59 ; 'Y'
            0x00002096      c745b8700000.  mov dword [rbp - 0x48], 0x70 ; 'p'
            0x0000209d      c745bc540000.  mov dword [rbp - 0x44], 0x54 ; 'T'
            0x000020a4      c745c0480000.  mov dword [rbp - 0x40], 0x48 ; 'H'
            0x000020ab      c745c46e0000.  mov dword [rbp - 0x3c], 0x6e ; 'n'
            0x000020b2      c745c87d0000.  mov dword [rbp - 0x38], 0x7d ; '}'
            0x000020b9      c78530ffffff.  mov dword [rbp - 0xd0], 0x61 ; 'a'
            0x000020c3      c78534ffffff.  mov dword [rbp - 0xcc], 0x63 ; 'c'
            0x000020cd      c78538ffffff.  mov dword [rbp - 0xc8], 0x61 ; 'a'
            0x000020d7      c7853cffffff.  mov dword [rbp - 0xc4], 0x7b ; '{'
            0x000020e1      c78540ffffff.  mov dword [rbp - 0xc0], 0x77 ; 'w'
            0x000020eb      c78544ffffff.  mov dword [rbp - 0xbc], 0x73 ; 's'
            0x000020f5      c78548ffffff.  mov dword [rbp - 0xb8], 0x6d ; 'm'
            0x000020ff      c7854cffffff.  mov dword [rbp - 0xb4], 0x5f ; '_'
            0x00002109      c78550ffffff.  mov dword [rbp - 0xb0], 0x55 ; 'U'
            0x00002113      c78554ffffff.  mov dword [rbp - 0xac], 0x30 ; '0'
            0x0000211d      c78558ffffff.  mov dword [rbp - 0xa8], 0x62 ; 'b'
            0x00002127      c7855cffffff.  mov dword [rbp - 0xa4], 0x4e ; 'N'
            0x00002131      c78560ffffff.  mov dword [rbp - 0xa0], 0x72 ; 'r'
            0x0000213b      c78564ffffff.  mov dword [rbp - 0x9c], 0x5f ; '_'
            0x00002145      c78568ffffff.  mov dword [rbp - 0x98], 0x34 ; '4'
            0x0000214f      c7856cffffff.  mov dword [rbp - 0x94], 0x63 ; 'c'
            0x00002159      c78570ffffff.  mov dword [rbp - 0x90], 0x31 ; '1'
            0x00002163      c78574ffffff.  mov dword [rbp - 0x8c], 0x47 ; 'G'
            0x0000216d      eb04           jmp 0x2173
            ; CODE XREF from section..rodata @ +0x17c
            0x0000216f      8345fc01       add dword [rbp - 4], 1
            ; CODE XREF from section..rodata @ +0x16d
            0x00002173      8b45fc         mov eax, dword [rbp - 4]
            0x00002176      0fafc0         imul eax, eax
            0x00002179      3945e4         cmp dword [rbp - 0x1c], eax
            0x0000217c      7ff1           jg 0x216f
            0x0000217e      8b45fc         mov eax, dword [rbp - 4]
            0x00002181      0fafc0         imul eax, eax
            0x00002184      3945e4         cmp dword [rbp - 0x1c], eax
            0x00002187      7404           je 0x218d
            0x00002189      8345fc01       add dword [rbp - 4], 1
            ; CODE XREF from section..rodata @ +0x187
            0x0000218d      8b45fc         mov eax, dword [rbp - 4]
            0x00002190      99             cdq
            0x00002191      c1ea1f         shr edx, 0x1f
            0x00002194      01d0           add eax, edx
            0x00002196      83e001         and eax, 1
            0x00002199      29d0           sub eax, edx
            0x0000219b      83f801         cmp eax, 1
            0x0000219e      7404           je 0x21a4
            0x000021a0      8345fc01       add dword [rbp - 4], 1

خروجی عجیبی به دست آوردیم. تعداد زیادی دستور mov می‌بینیم که نویسه‌های معتبر را جابجا می‌کنند. اگر کمی دقت کنیم، نظم ساده‌ای در این نویسه‌های می‌بینیم. در واقع پرچم، به شکل یکی در میان، دو قسمت شده و این قسمت‌ها پشت سر هم آورده شده‌اند.
بنابراین، پرچم به شکل زیر است:

parcham{Aw3s0m3_4Ut0_b1N4rY_p4TcH1nG}