2019RCTF babyre

main函数校验flag长度是否为16位,若是则依次对flag加密后,而后若flag正确则输出常量"Bingo!"(此处有多解)

if ( v6 > 0x10 )                              // flag.len == 16
  {
    puts("input is too long!");
  }
  else if ( v6 == 16 )
  {
    v7 = sub_55A527C5BC00((unsigned __int64)&flag, 16, &ptr);// v7 == 8,ptr为flag 变换后的八个字符
    if ( v7
      && (v8 = sub_55A527C5C180(ptr, v7, (__int64)&::a3, 16, &dest), (v9 = (char *)v8) != 0LL)
      && dest > 0
      && (unsigned __int16)sub_55A527C5C3D0((__int64)v8, dest) == 0x69E2 )
    {
      for ( i = 0LL; dest > (signed int)i; ++i )
        v9[i] ^= 0x17u;
      puts(v9);
      if ( ptr )
        free(ptr);
      free(v9);
    }
    else
    {
      puts("input flag is wrong!");
    }
  }
  else
  {
    puts("input is too short!");
  }

第一个函数很简单,主要是对flag通过置换表进行置换,如 ’ab' 则 置换为 0xab
由于小端序,所以如果我们输入是'1234567890123456'(128bits)那么经过置换后变成一个64位的整数 0x5634129078563412(64bits)(由于置换表的缘故此处也存在多解,譬如 'f' 'F' '~' 均被置换为0xF)
第二个函数是对flag置换后形成的64位整数进行32轮的轮换加密形成一个新的64位整数X ,然后校验高8位是否小于4,并将BYTE(BYTE7(X))(X)置0
轮换加密函数很长,但是由于a2 < -1且 v27 <= 2 所以只需分析那一小部分即可

if ( a2 < -1 )
  {
    v24 = -a2;
    v25 = 0x9E3779B9 * (52 / v24 + 6);          // v24 == 2
    if ( v25 )
    {
      srch = &src[v24 - 1];
      v27 = ~v4;
      v44 = &src[~v4];
      v28 = ~v4 - 2 - ((~v4 - 3) & 0xFFFFFFFE);
      do
      {
        v29 = v25 >> 2;
        if ( v27 <= 2 )                         // true
        {
          v31 = v27;
        }
        else                                    // false
        {
              略
        }
        srcl_ = &src[v31];
        do
        {
          srcl = *(srcl_ - 1);
          --srcl_;
          srch_next = srcl_[1]
                    - (((srch_next ^ v25)
                      + (srcl ^ *(_DWORD *)(tables + 4LL * (((unsigned __int8)v29 ^ (unsigned __int8)v31) & 3)))) ^ (((srch_next >> 3) ^ 16 * srcl) + ((srcl >> 5) ^ 4 * srch_next)));
          srcl_[1] = srch_next;                 // 后八位0x56341290替换为 0x45d10f13 小端
          --v31;
        }
        while ( v31 );
        srcl_next = *src
                  - (((((unsigned int)*srch >> 5) ^ 4 * srch_next) + (16 * *srch ^ (srch_next >> 3))) ^ ((*(_DWORD *)(tables + 4LL * (v29 & 3)) ^ *srch) + (v25 ^ srch_next)));
        v42 = v25 == 0x9E3779B9;
        v25 += 0x61C88647;
        srch_next = srcl_next;
        *src = srcl_next;                       // 前四位0x78654321替换为  0x35158711 小端
      }
      while ( !v42 );
    }
    return 0LL;
  }
  return result;
}

第三个函数是一些位操作,并且验证返回值是否为 0x69E2,如果是则将X按字节从低到高异或0x17输出
题目提示输出'Bingo!',所以将其异或回去得到一个长度为6的byte数组,(由于小端序所以是64位整数的低48位)合理猜测高8位为0x02,所以只需爆破次高8位数据即可(由此构造一个64位数经前两轮解密而后动态调试验证第三个函数返回了0x69E2且输出'Bingo!')
题目提示 md5(rctf{your answer}) == 5f8243a662cf71bf31d2b2602638dc1d
由此可以爆破flag了,算了一下大概需要 0xff*(2^n+km)次运算(n取决于flag经过第一轮置换字母的数量,km取决了flag经过第一轮置换f的数量,k是常量)
权衡了一下先爆破第二轮加密过程的多解,第一轮默认为小写,且0xf均视为'f'
幸运的是爆破到第二个flag就出来了
rctf{05e8a376e4e0446e}
加解密脚本如下

#include<iostream>
#include<string>
#include <openssl/md5.h>
#include<cstdio>
#include<cstring>
using namespace std;
// -lcrypto 
string MD5(const string& src )
{
    MD5_CTX ctx;

    string md5_string;
    unsigned char md[16] = { 0 };
    char tmp[33] = { 0 };

    MD5_Init( &ctx );
    MD5_Update( &ctx, src.c_str(), src.size() );
    MD5_Final( md, &ctx );

    for( int i = 0; i < 16; ++i )
    {   
        memset( tmp, 0x00, sizeof( tmp ) );
        sprintf( tmp, "%02X", md[i] );
        md5_string += tmp;
    }   
    return md5_string;
}
// encrypt01
unsigned long long encrypt01 (string str)
{
    if (str.length() != 16) return 0;
    unsigned char byte_5626AE9EB620[55] = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
        0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
        0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
    };
    unsigned long long res = 0;
    unsigned long long ptr = 0;
    for (int i = 0; i < str.length(); i+=2)
    {
        unsigned char a = byte_5626AE9EB620[str[i] - 0x30];
        unsigned char b = byte_5626AE9EB620[str[i+1] - 0x30];
        ptr = (a << 4) + b;
        ptr <<= 4*i;
        res += ptr;
    }
    return res;
}

string decrypt01 (unsigned long long src)
{
    unsigned char byte_5626AE9EB620[55] = {
        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
        0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 
        0xFF, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F
    };
    char chars [16]={0};
    unsigned char dest = 0xff;
    for (int i = 0; i < 16; i+=2)
    {
        unsigned char ch = src & dest ;
        src >>= 8;
        if (((ch & 0xf0) >> 4) <= 9)
            chars[i] = ((ch & 0xf0) >> 4) + 0x30;
        else
            chars[i] = ((ch & 0xf0) >> 4) + 0x57;
        if ((ch & 0x0f) <= 9)
            chars[i+1] = (ch & 0x0f) + 0x30;
        else
            chars[i+1] = (ch & 0x0f) + 0x57;
    }
    return chars;
}

// encrypt02
unsigned long long  encrypt02 (unsigned long long src){
    unsigned int v25 = (0x9E3779B9 * (52 / 2 + 6) & 0xffffffff);
    unsigned int l = src & 0xffffffff;
    unsigned int r = (src >> 32) & 0xffffffff;
    int i = 0;
    bool flag = 0;
    do{
        int v31 = 1;
        unsigned int v29 = v25 >> 2;
        int table[] = {0xE0C7E0C7, 0xC6F1D3D7, 0xC6D3C6D3, 0xC4D0D2CE};
        r = r - (((l^v25) + (l^table[(v29^v31)&3])) ^ (((l>>3)^16*l) + ((l>>5)^4*l)));
        l = l - (((v25^r) + (r^table[(v29&3)])) ^ (((r>>5)^4*r) + (16*r^(r>>3))));
        flag = v25 == 0x9E3779B9;
        v25 += 0x61C88647;
    }while (!flag);
    unsigned long long res = ((unsigned long long)r << 32) + l;
    return res;
}


//decrypt02
unsigned long long decrypt02(unsigned long long dest){
    unsigned int v25 = 0x9E3779B9;
    unsigned int l = dest & 0xffffffff;
    unsigned int r = (dest >> 32) & 0xffffffff;
    bool flag = 0;
    do{ 
        int v31 = 1;
        unsigned int v29 = v25 >> 2;
        int table[] = {0xE0C7E0C7, 0xC6F1D3D7, 0xC6D3C6D3, 0xC4D0D2CE};
        l = l + (((v25^r) + (r^table[(v29&3)])) ^ (((r>>5)^4*r) + (16*r^(r>>3))));
        r = r + (((l^v25) + (l^table[(v29^v31)&3])) ^ (((l>>3)^16*l) + ((l>>5)^4*l)));
        flag = v25 ==(0x9E3779B9 * (52 / 2 + 6) & 0xffffffff);
        v25 -= 0x61C88647;
    }while(!flag);
    unsigned long long res = ((unsigned long long)r << 32) + l;
    return res;
}

int main(){
    //BYTE7(crack) == 02
    unsigned long long crack = (unsigned long long)0x02 << 56;
    string str = "Bingo!";
    int i = str.length();
    while(i--) crack += ((unsigned long long)(str[i]^0x17) << (i * 8));

    string flagmd5 = "5F8243A662CF71BF31D2B2602638DC1D";

    for (unsigned long long  i =  0x0; i <= 0xff; i+=0x1)
    {                          
        crack += (unsigned long long)0x1 << 48;
        cout << hex << crack << endl;
        string flag = "";
        flag += "rctf{";
        flag += decrypt01(decrypt02(crack));
        flag += "}";
        if (MD5(flag) == flagmd5)
        {
            cout << "success!" << endl;
            cout << flag << endl;
            return 0;
        }
    }

    //encrypt03
    // int v11 = r >> 24;
    // if (v11 <= 4)//true
    // {
    //  int v13 = 8 - v11;//v13 == 6
    //  //dest = v13
    //  //将src[v13]置0
    // }

    //encrypt04
    //i=0
    //v13 = 8 - 2
    //依次将v[i++]二进制逆序,循环进行下列level1算法直到v[v13-1],算出最终结果为0x4796

    //encrytp05
    //移位操作0x4796 -> 0x69e2

    return 0;
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,544评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,430评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,764评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,193评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,216评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,182评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,063评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,917评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,329评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,543评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,722评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,425评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,019评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,671评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,825评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,729评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,614评论 2 353

推荐阅读更多精彩内容