چالش اندروید دوم - اگر می‌تونی لطفاً من رو نگیر

توضیحات

اگر واقعا دوست داری به پرچم برسی بی‌خیال مهندسی معکوس شو و از اندروید بهره ببر!

[دانلود فایل]

حل چالش

باز هم یک پرونده‌ی apk داریم و باز هم مراحل نصب آن را طی می‌کنیم (واقعا هم نیاز به نصب نیست همیشه، اگر دوست داشتید می‌تونید خودتون نصب کنید یا رایت‌آپ قبلی رو بخونید یا ویدئو‌های آموزشی رو ببنید!).

تصویر اول
تصویر اول
تصویر دوم
تصویر دوم

گام اول

همان‌طور که در ویدئو آموزشی دوم برای شما توضیح دادیم، این امکان وجود دارد که از کدهای Native در برنامه‌های اندروید استفاده شود. در این برنامه چهار تابع وجود دارند که از یک کتابخانه‌ به نام native-lib می‌آیند.
خروجی سه تابع رشته و تابع دیگری به نام flagValidator وجود دارد که خروجی آن درست/غلط است و همین تابع جایی است که بررسی می‌کند آیا رشته‌ای که کاربر وارد می‌کند درست است یا خیر.

گام دوم

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

$ r2 libnative-lib.so 
Cannot determine entrypoint, using 0x000348d8.
Warning: run r2 with -e io.cache=true to fix relocations in disassembly
 -- Finnished a beer
[0x000348d8]> aaa
[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] Finding xrefs in noncode section with anal.in=io.maps
[x] Analyze value pointers (aav)
[x] Value from 0x00000000 to 0x0007145d (aav)
[x] 0x00000000-0x0007145d in 0x0-0x7145d (aav)
[x] Emulate functions to find computed references (aaef)
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x00033640]> fs s
[0x00033640]> fs symbols 
[0x00033640]> f
Do you want to print 1695 lines? (y/N) n
[0x00033640]> f | grep -i java
0x00035140 296 sym.Java_io_parcham_donotreverseme_MainActivity_stringFromJNI
0x00035290 423 sym.Java_io_parcham_donotreverseme_Boomer_failFromJNI
0x00035460 296 sym.Java_io_parcham_donotreverseme_Boomer_winFromJNI
0x00035b80 139 sym.Java_io_parcham_donotreverseme_Boomer_flagValidator
 

از این داده استفاده می‌کنیم و به تابع Java_io_parcham_donotreverseme_Boomer_flagValidator که در آدرس 0x0003598c قرار دارد رفته و کد این تابع را دیکامپایل می کنیم:

[0x00033640]> s sym.Java_io_parcham_donotreverseme_Boomer_flagValidator 
[0x00035b80]> pdg 

uint32_t sym.Java_io_parcham_donotreverseme_Boomer_flagValidator
                   (int32_t arg_8h, undefined4 placeholder_1, int32_t arg_10h)
{
    undefined4 uVar1;
    uint32_t uVar2;
    uint32_t uVar3;
    int32_t in_GS_OFFSET;
    uint8_t auStack32 [8];
    undefined4 uStack24;
    int32_t iStack20;

    iStack20 = *(int32_t *)(in_GS_OFFSET + 0x14);
    uVar1 = (**(code **)(*(int32_t *)arg_8h + 0x2a4))(arg_8h, arg_10h, 0);
    fcn.00030390(auStack32, uVar1);
    uVar2 = fcn.00030480(auStack32);
    if ((auStack32[0] & 1) != 0) {
        uVar3 = fcn.000303f0(uStack24);
        uVar2 = uVar3 & 0xffffff00 | uVar2 & 0xff;
    }
    if (*(int32_t *)(in_GS_OFFSET + 0x14) == iStack20) {
        return uVar2;
    }
    // WARNING: Subroutine does not return
    sym.imp.__stack_chk_fail();
}
[0x00035b80]>
 

در این مرحله به دنبال تابع اصلی یعنی تابع fcn.000303f0 می‌باشیم، که آن را به شکل زیر پیدا می‌کنیم:

[0x00045820]> fs symbols
[0x00045820]> f | grep flag 
0x000474c0 1419 sym.check_flag_validity_std::__ndk1::basic_string_char__std::__ndk1::char_traits_char___std::__ndk1::allocator_char
0x00047b90 102 sym.Java_io_parcham_donotreverseme_Boomer_flagValidator
[0x00045820]> s sym.check_flag_validity_std::__ndk1::basic_string_char__std::__ndk1::char_traits_char___std::__ndk1::allocator_char
 

گام سوم

در نهایت به شکل زیر نسخه‌ی دی‌کامپایل شده‌ی کد را می‌سازیم:

[0x00045820]> s sym.check_flag_validity_std::__ndk1::basic_string_char__std::__ndk1::char_traits_char___std::__ndk1::allocator_char
[0x000474c0]> pdg 

// WARNING: Could not reconcile some variable overlaps

uint64_t 
         sym.check_flag_validity_std::__ndk1::basic_string_char__std::__ndk1::char_traits_char___std::__ndk1::allocator_char
                   (int64_t arg1)
{
    undefined uVar1;
    int32_t iVar2;
    uint64_t uVar3;
    undefined *puVar4;
    undefined *puVar5;
    unkbyte7 *pVar6;
    int64_t iVar7;
    uint8_t uVar8;
    uint32_t uVar9;
    int64_t iVar10;
    uint64_t uVar11;
    uint64_t uVar12;
    uint32_t uVar13;
    uint64_t uVar14;
    uint64_t uVar15;
    int64_t in_FS_OFFSET;
    undefined auStack168 [16];
    undefined *puStack152;
    undefined auStack136 [16];
    undefined *puStack120;
    undefined8 uStack104;
    undefined8 uStack96;
    undefined *puStack88;
    uint8_t uStack80;
    unkbyte7 Stack79;
    uint64_t uStack72;
    unkbyte7 *pStack64;
    int64_t iStack56;

    // check_flag_validity(std::__ndk1::basic_string<char, std::__ndk1::char_traits<char>, std::__ndk1::allocator<char>
    // >&)
    iStack56 = *(int64_t *)(in_FS_OFFSET + 0x28);
    if ((*(uint8_t *)arg1 & 1) == 0) {
        uVar15 = (uint64_t)(*(uint8_t *)arg1 >> 1);
    } else {
        uVar15 = *(uint64_t *)(arg1 + 8);
    }
    fcn.000424c0(&uStack80, 0xac460);
    fcn.000424c0(&uStack104, 0xac460);
    uVar3 = uVar15 >> 2;
    if (uVar3 != 0) {
        uVar9 = (uint32_t)uVar15 & 0x584 ^ 0x7f7f;
        iVar7 = SUB168(ZEXT816(uVar15) * ZEXT816(0x8618618618618619) >> 0x40, 0);
        uVar12 = (uVar15 - iVar7 >> 1) + iVar7 >> 4;
        iVar7 = 5;
        uVar14 = 0;
        do {
            uVar15 = uVar14 * uVar12;
            auStack136 = ZEXT816(0);
            puStack120 = (undefined *)0x0;
            uVar8 = *(uint8_t *)arg1;
            if ((uVar8 & 1) == 0) {
                uVar11 = (uVar8 >> 1) - uVar15;
                if (uVar15 <= uVar8 >> 1) goto code_r0x000475ef;
code_r0x00047a32:
                fcn.000425a0(auStack136);
code_r0x00047a3c:
                fcn.000424f0(auStack136);
                goto code_r0x00047a46;
            }
            uVar11 = *(uint64_t *)(arg1 + 8) - uVar15;
            if (*(uint64_t *)(arg1 + 8) < uVar15) goto code_r0x00047a32;
code_r0x000475ef:
            iVar10 = arg1 + 1;
            if ((uVar8 & 1) != 0) {
                iVar10 = *(int64_t *)(arg1 + 0x10);
            }
            if (uVar12 <= uVar11) {
                uVar11 = uVar12;
            }
            if (0xffffffffffffffef < uVar11) goto code_r0x00047a3c;
            if (uVar11 < 0x17) {
                auStack136 = CONCAT151(SUB1615(auStack136 >> 8, 0), (char)uVar11 * '\x02');
                puVar4 = auStack136 + 1;
                if (uVar11 != 0) goto code_r0x0004765f;
            } else {
                puVar4 = (undefined *)fcn.000424e0(uVar11 + 0x10 & 0x1ffffffffffffff0);
                auStack136 = CONCAT88(uVar11, uVar11 + 0x10) & (undefined  [16])0xffffffffffffffff |
                             (undefined  [16])0x1;
                puStack120 = puVar4;
code_r0x0004765f:
                sym.imp.memcpy(puVar4, iVar10 + uVar15, uVar11);
            }
            puVar4[uVar11] = 0;
            if ((uStack104 & 1) != 0) {
                *puStack88 = 0;
                uStack96 = 0;
                if ((uStack104 & 1) != 0) {
                    fcn.00042520(puStack88);
                }
            }
            puStack88 = puStack120;
            uStack104 = SUB168(auStack136, 0);
            uStack96 = SUB168(auStack136 >> 0x40, 0);
            if (uVar9 != 0xffffffff) {
                uVar13 = 0xffffffff;
                do {
                    puVar4 = puStack88;
                    uVar15 = uStack96;
                    auStack168 = ZEXT816(0);
                    puStack152 = (undefined *)0x0;
                    if ((uStack104 & 1) == 0) {
                        puStack152 = puStack88;
                        auStack168 = CONCAT412(uStack96._4_4_, CONCAT48((undefined4)uStack96, uStack104));
                    } else {
                        if (0xffffffffffffffef < uStack96) {
                            fcn.000424f0(auStack168);
                            goto code_r0x00047a32;
                        }
                        if (uStack96 < 0x17) {
                            auStack168 = CONCAT151(SUB1615(auStack168 >> 8, 0), (char)uStack96 * '\x02');
                            puVar5 = auStack168 + 1;
                            if (uStack96 != 0) goto code_r0x000477ba;
                        } else {
                            uVar11 = uStack96 + 0x10;
                            puVar5 = (undefined *)fcn.000424e0(uVar11 & 0xfffffffffffffff0);
                            auStack168 = CONCAT88(uVar15, uVar11) & (undefined  [16])0xffffffffffffffff |
                                         (undefined  [16])0x1;
                            puStack152 = puVar5;
code_r0x000477ba:
                            sym.imp.memcpy(puVar5, puVar4, uVar15);
                        }
                        puVar5[uVar15] = 0;
                    }
                    fcn.00042570(auStack136, auStack168);
                    if ((uStack104 & 1) != 0) {
                        *puStack88 = 0;
                        uStack96 = 0;
                        if ((uStack104 & 1) != 0) {
                            fcn.00042520(puStack88);
                        }
                    }
                    puStack88 = puStack120;
                    uStack104 = SUB168(auStack136, 0);
                    uStack96 = SUB168(auStack136 >> 0x40, 0);
                    auStack136 = ZEXT816(0);
                    puStack120 = (undefined *)0x0;
                    if ((auStack168 & (undefined  [16])0x1) != (undefined  [16])0x0) {
                        fcn.00042520(puStack152);
                    }
                    uVar13 = uVar13 + 1;
                } while (uVar9 != uVar13);
            }
            iVar10 = 0;
            do {
                puVar4 = (undefined *)((int64_t)&uStack104 + 1);
                if ((uStack104 & 1) != 0) {
                    puVar4 = puStack88;
                }
                uVar1 = puVar4[iVar10];
                if ((uStack80 & 1) == 0) {
                    uVar15 = (uint64_t)(uStack80 >> 1);
                    uVar11 = 0x16;
                    if (uVar15 == 0x16) goto code_r0x00047920;
code_r0x00047909:
                    if ((uStack80 & 1) != 0) goto code_r0x00047951;
code_r0x000478a0:
                    uStack80 = (char)uVar15 * '\x02' + 2;
                    pVar6 = &Stack79;
                } else {
                    uVar11 = (CONCAT71(Stack79, uStack80) & 0xfffffffffffffffe) - 1;
                    uVar15 = uStack72;
                    if (uStack72 != uVar11) goto code_r0x00047909;
code_r0x00047920:
                    fcn.00042580(&uStack80, uVar11, 1, uVar11, uVar11, 0, 0);
                    if ((uStack80 & 1) == 0) goto code_r0x000478a0;
code_r0x00047951:
                    uStack72 = uVar15 + 1;
                    pVar6 = pStack64;
                }
                *(undefined *)((int64_t)pVar6 + uVar15) = uVar1;
                *(undefined *)((int64_t)pVar6 + uVar15 + 1) = 0;
                iVar10 = iVar10 + 1;
            } while (iVar7 != iVar10);
            uVar9 = uVar9 + 0x539;
            uVar14 = uVar14 + 1;
            iVar7 = iVar7 + 1;
        } while (uVar14 != uVar3);
    }
    uVar8 = uStack80 & 1;
    uVar3 = uStack72;
    if (uVar8 == 0) {
        uVar3 = (uint64_t)(uStack80 >> 1);
    }
    if (uVar3 == 0x13b) {
        pVar6 = pStack64;
        if (uVar8 == 0) {
            pVar6 = &Stack79;
        }
        iVar2 = sym.imp.memcmp(pVar6, 
                               "7e23d580297beb2210b9d1c73d80f3efdd9968c4b565094177049a0d1f4ab1ab990a82da7be0e43cab29be9dd46689814c92eb2a1d0ea1a6cd2f84efe7b7151fc25105d06c14993182686980f02793444bc319781a03d342d784e65b985cbd183eebef5afa69d28c5026514ca88719edd4210779ea3f8061f058f78f584f6dfbe861534b861bb28aff420338f714dc9b89ce6f4b8f81b3bab6752f0007d"
                               , 0x13b);
        uVar15 = uVar15 & 0xffffffffffffff00 | (uint64_t)(iVar2 == 0);
    } else {
        uVar15 = 0;
    }
    if ((uStack104 & 1) != 0) {
        fcn.00042520(puStack88);
        uVar8 = uStack80 & 1;
    }
    if (uVar8 != 0) {
        fcn.00042520(pStack64);
    }
    if (*(int64_t *)(in_FS_OFFSET + 0x28) == iStack56) {
        return uVar15 & 0xffffffff;
    }
code_r0x00047a46:
    // WARNING: Subroutine does not return
    sym.imp.__stack_chk_fail();
}
 

و پس از ساده‌سازی به تابع زیر می‌رسیم:


    bool check_flag_validity(std::string &flag) {
        auto len = flag.length();
        unsigned s = 31337 | len & 2020 ^ 1399;
        std::string enc = "", segment = "";
        for (int i = 0; i < len / 4; i++) {
            segment = flag.substr(i * (len / 21), len / 21);
            for (int j = 0; j < s + 1; j++) {
                segment = md5(segment);
            }
            for (int j = 0 ; j < i + 5; j++) {
                enc += segment[j];
            }
            s += 1337;
        }
        if (enc == "7e23d580297beb2210b9d1c73d80f3efdd9968c4b565094177049a0d1f"
                   "4ab1ab990a82da7be0e43cab29be9dd46689814c92eb2a1d0ea1a6cd2f"
                   "84efe7b7151fc25105d06c14993182686980f02793444bc319781a03d3"
                   "42d784e65b985cbd183eebef5afa69d28c5026514ca88719edd4210779"
                   "ea3f8061f058f78f584f6dfbe861534b861bb28aff420338f714dc9b89"
                   "ce6f4b8f81b3bab6752f0007d")
            return true;
        else
            return false;
    }
 

البته این ساده‌سازی بسیار سخت بود، علت اصلی آن هم این است که همه‌ی توابع مورد استفاده در هنگام کامپایل در اندروید استدیو به صورت inline تبدیل شده‌اند.

گام چهارم

پس از استخراج الگوریتم در این مرحله با استفاده از یک برنامه‌ی CPP، پرچم را می‌سازیم. اما متاسفانه در این بخش ما یک کمی سخت گرفتیم و ظاهر امر این هست که پیدا کردن رشته‌ای که مورد نظر سوال است با دست کم روشی که ما قصد حل آن را داریم در بهترین حالت ۲ ماه زمان نیاز دارد! در ضمن به همین دلیل هم از کد C یا CPP استفاده کرده‌ایم چون تلاش کردیم به صورت بهینه‌تری کد این بخش را بزنیم و با پایتون کمی کندتر بود.

#include <string>
#include <array>
#include <iostream>
#include <cstdint>
#include <iomanip>
#include <cctype>

#include "md5.h"


// Constants for MD5Transform routine.
#define S11 7
#define S12 12
#define S13 17
#define S14 22
#define S21 5
#define S22 9
#define S23 14
#define S24 20
#define S31 4
#define S32 11
#define S33 16
#define S34 23
#define S41 6
#define S42 10
#define S43 15
#define S44 21

// F, G, H and I are basic MD5 functions.
inline MD5::uint4 MD5::F(uint4 x, uint4 y, uint4 z) {
    return x&y | ~x&z;
}

inline MD5::uint4 MD5::G(uint4 x, uint4 y, uint4 z) {
    return x&z | y&~z;
}

inline MD5::uint4 MD5::H(uint4 x, uint4 y, uint4 z) {
    return x^y^z;
}

inline MD5::uint4 MD5::I(uint4 x, uint4 y, uint4 z) {
    return y ^ (x | ~z);
}

// rotate_left rotates x left n bits.
inline MD5::uint4 MD5::rotate_left(uint4 x, int n) {
    return (x << n) | (x >> (32-n));
}

// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
// Rotation is separate from addition to prevent recomputation.
inline void MD5::FF(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) {
    a = rotate_left(a+ F(b,c,d) + x + ac, s) + b;
}

inline void MD5::GG(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) {
    a = rotate_left(a + G(b,c,d) + x + ac, s) + b;
}

inline void MD5::HH(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) {
    a = rotate_left(a + H(b,c,d) + x + ac, s) + b;
}

inline void MD5::II(uint4 &a, uint4 b, uint4 c, uint4 d, uint4 x, uint4 s, uint4 ac) {
    a = rotate_left(a + I(b,c,d) + x + ac, s) + b;
}

// default ctor, just initailize
MD5::MD5()
{
    init();
}

// nifty shortcut ctor, compute MD5 for string and finalize it right away
MD5::MD5(const std::string &text)
{
    init();
    update(text.c_str(), text.length());
    finalize();
}

void MD5::init()
{
    finalized=false;

    count[0] = 0;
    count[1] = 0;

    // load magic initialization constants.
    state[0] = 0x67452301;
    state[1] = 0xefcdab89;
    state[2] = 0x98badcfe;
    state[3] = 0x10325476;
}

// decodes input (unsigned char) into output (uint4). Assumes len is a multiple of 4.
void MD5::decode(uint4 output[], const uint1 input[], size_type len)
{
    for (unsigned int i = 0, j = 0; j < len; i++, j += 4)
        output[i] = ((uint4)input[j]) | (((uint4)input[j+1]) << 8) |
                    (((uint4)input[j+2]) << 16) | (((uint4)input[j+3]) << 24);
}

// encodes input (uint4) into output (unsigned char). Assumes len is
// a multiple of 4.
void MD5::encode(uint1 output[], const uint4 input[], size_type len)
{
    for (size_type i = 0, j = 0; j < len; i++, j += 4) {
        output[j] = input[i] & 0xff;
        output[j+1] = (input[i] >> 8) & 0xff;
        output[j+2] = (input[i] >> 16) & 0xff;
        output[j+3] = (input[i] >> 24) & 0xff;
    }
}

// apply MD5 algo on a block
void MD5::transform(const uint1 block[blocksize])
{
    uint4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
    decode (x, block, blocksize);

    /* Round 1 */
    FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
    FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
    FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
    FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
    FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
    FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
    FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
    FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
    FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
    FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
    FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
    FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
    FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
    FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
    FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
    FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */

    /* Round 2 */
    GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
    GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
    GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
    GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
    GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
    GG (d, a, b, c, x[10], S22,  0x2441453); /* 22 */
    GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
    GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
    GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
    GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
    GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
    GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
    GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
    GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
    GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
    GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */

    /* Round 3 */
    HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
    HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
    HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
    HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
    HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
    HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
    HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
    HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
    HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
    HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
    HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
    HH (b, c, d, a, x[ 6], S34,  0x4881d05); /* 44 */
    HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
    HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
    HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
    HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */

    /* Round 4 */
    II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
    II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
    II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
    II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
    II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
    II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
    II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
    II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
    II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
    II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
    II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
    II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
    II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
    II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
    II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
    II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */

    state[0] += a;
    state[1] += b;
    state[2] += c;
    state[3] += d;

    // Zeroize sensitive information.
    memset(x, 0, sizeof x);
}

// MD5 block update operation. Continues an MD5 message-digest
// operation, processing another message block
void MD5::update(const unsigned char input[], size_type length)
{
    // compute number of bytes mod 64
    size_type index = count[0] / 8 % blocksize;

    // Update number of bits
    if ((count[0] += (length << 3)) < (length << 3))
        count[1]++;
    count[1] += (length >> 29);

    // number of bytes we need to fill in buffer
    size_type firstpart = 64 - index;

    size_type i;

    // transform as many times as possible.
    if (length >= firstpart)
    {
        // fill buffer first, transform
        memcpy(&buffer[index], input, firstpart);
        transform(buffer);

        // transform chunks of blocksize (64 bytes)
        for (i = firstpart; i + blocksize <= length; i += blocksize)
            transform(&input[i]);

        index = 0;
    }
    else
        i = 0;

    // buffer remaining input
    memcpy(&buffer[index], &input[i], length-i);
}

// for convenience provide a verson with signed char
void MD5::update(const char input[], size_type length)
{
    update((const unsigned char*)input, length);
}

// MD5 finalization. Ends an MD5 message-digest operation, writing the
// the message digest and zeroizing the context.
MD5& MD5::finalize()
{
    static unsigned char padding[64] = {
            0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    };

    if (!finalized) {
        // Save number of bits
        unsigned char bits[8];
        encode(bits, count, 8);

        // pad out to 56 mod 64.
        size_type index = count[0] / 8 % 64;
        size_type padLen = (index < 56) ? (56 - index) : (120 - index);
        update(padding, padLen);

        // Append length (before padding)
        update(bits, 8);

        // Store state in digest
        encode(digest, state, 16);

        // Zeroize sensitive information.
        memset(buffer, 0, sizeof buffer);
        memset(count, 0, sizeof count);

        finalized=true;
    }

    return *this;
}

// return hex representation of digest as string
std::string MD5::hexdigest() const
{
    if (!finalized)
        return "";

    char buf[33];
    for (int i=0; i<16; i++)
        sprintf(buf+i*2, "%02x", digest[i]);
    buf[32]=0;

    return std::string(buf);
}

std::ostream& operator<<(std::ostream& out, MD5 md5)
{
    return out << md5.hexdigest();
}

std::string md5(const std::string str)
{
    MD5 md5 = MD5(str);

    return md5.hexdigest();
}


std::string enc_flag = "7e23d580297beb2210b9d1c73d80f3efdd9968c4b565094177049a0d1f"
               "4ab1ab990a82da7be0e43cab29be9dd46689814c92eb2a1d0ea1a6cd2f"
               "84efe7b7151fc25105d06c14993182686980f02793444bc319781a03d3"
               "42d784e65b985cbd183eebef5afa69d28c5026514ca88719edd4210779"
               "ea3f8061f058f78f584f6dfbe861534b861bb28aff420338f714dc9b89"
               "ce6f4b8f81b3bab6752f0007d";
std::string print = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!'_";



std::string brutal_test(unsigned offset, unsigned len, unsigned iter) {
  iter++;
  std::string test = "", res = "";
  for (auto &&c1 : print) {
    for (auto &&c2 : print) {
      for (auto &&c3 : print) {
        for (auto &&c4 : print) {
          test = "", res = "";
          test += c1;
          test += c2;
          test += c3;
          test += c4;
          res = test;
          for (int u = 0; u < iter; u++)
            test = md5(test);
          if (test.substr(0, len) == enc_flag.substr(offset, len))
            return res;
        }
      }
    }
  }
  return "FAIL";
}

int main() {

    std::string flag = "";
    for (int j = 5, i = 0, k = 32635; j < 26; j++, i += j, k += 1337) {
        flag += brutal_test(i, j, k);
        std::cerr << flag << std::endl; 
    }
    return 0;
}
 

که پس از احتمالا چند ماه به چنین پرچمی خواهید رسید!

parcham{__H3y__AndroiD_GuY5__!_W4nNA_h3aR_A_j0ke_aBoU7__priV4cY?_Y0U_wOn'T_93T_I7!!}