**Description**
> the format of flag is flag{.+}> > attachment:> > https://drive.google.com/open?id=1JoAvVnUJOO-E-E2C2qGH1QDVAVu6-qiS
**Solution**
Opened the executable in IDA, and many SSE instructions were found :\(
For SSE instructions, it is even more clear to read assembly than to read the F5 decompiler result. After changing some of the data structure of the variable, F5 gives these results:
```c  s[7] = -1LL;  s[8] = -1LL;  input[0] = 'UUUUUUUU';  input[1] = 'UUUUUUUU';  input[2] = 'UUUUUUUU';  input[3] = 'UUUUUUUU';  input[4] = 'UUUUUUUU';  input[5] = 'UUUUUUUU';  input[6] = 'UUUUUUUU';  input[7] = 'UUUUUUUU';  input[8] = 'UUUUUUUU';  input[9] = 'UUUUUUUU';  input[10] = 'UUUUUUUU';  input[11] = 'UUUUUUUU';  input[12] = 'UUUUUUUU';  input[13] = 'UUUUUUUU';  input[14] = 'UUUUUUUU';  input[15] = 'UUUUUUUU';  s[0] = ' emocleW'; // Welcome  s[9] = -1LL;  s[1] = ' FTCR ot'; // to RCTF  s[10] = -1LL;  s[2] = 'eH !8102'; // 2018! He  s[11] = -1LL;  s[3] = ' a si er'; // re is a  s[12] = -1LL;  s[13] = -1LL;  s[14] = -1LL;  s[15] = -1LL;  s[4] = 'c ERybaB'; // BabyRE c  s[5] = 'egnellah'; // hallenge  s[6] = 'uoy rof '; //  for you  LOWORD(s[7]) = '.';  puts((const char *)s);  __printf_chk(1LL, "Give me your flag: ");  __isoc99_scanf("%127s", input);/*  unsigned __int64 input[16]; // [rsp+0h] [rbp-198h]  unsigned __int64 s[16]; // [rsp+80h] [rbp-118h]  q_xmm_word result[8]; // [rsp+100h] [rbp-98h]  //result is regarded as uint64_t when assigning the result  //and regarded as uint128_t when checking the correctness of the result 00000000 q_xmm_word      union ; (sizeof=0x10, mappedto_16)00000000                                         ; XREF: main+1B8/w00000000                                         ; main+1E1/w ...00000000 qwords          two_qwords ?00000000 xmms            xmmword ?00000000 q_xmm_word      ends0000000000000000 ; ---------------------------------------------------------------------------0000000000000000 two_qwords      struc ; (sizeof=0x10, mappedto_14)00000000                                         ; XREF: q_xmm_word/r00000000 low             dq ?00000008 high            dq ?00000010 two_qwords      ends*/  result[0].qwords.low = sub_400BA0((q_xmm_word)(input[0] * (unsigned __int128)s[0]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[0].qwords.high = sub_400BA0((q_xmm_word)(input[1] * (unsigned __int128)s[1]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[1].qwords.low = sub_400BA0((q_xmm_word)(input[2] * (unsigned __int128)s[2]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[1].qwords.high = sub_400BA0((q_xmm_word)(input[3] * (unsigned __int128)s[3]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[2].qwords.low = sub_400BA0((q_xmm_word)(input[4] * (unsigned __int128)s[4]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[2].qwords.high = sub_400BA0((q_xmm_word)(input[5] * (unsigned __int128)s[5]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[3].qwords.low = sub_400BA0((q_xmm_word)(input[6] * (unsigned __int128)s[6]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[3].qwords.high = sub_400BA0((q_xmm_word)(input[7] * (unsigned __int128)s[7]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[4].qwords.low = sub_400BA0((q_xmm_word)(input[8] * (unsigned __int128)s[8]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[4].qwords.high = sub_400BA0((q_xmm_word)(input[9] * (unsigned __int128)s[9]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[5].qwords.low = sub_400BA0((q_xmm_word)(input[10] * (unsigned __int128)s[10]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[5].qwords.high = sub_400BA0((q_xmm_word)(input[11] * (unsigned __int128)s[11]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[6].qwords.low = sub_400BA0((q_xmm_word)(input[12] * (unsigned __int128)s[12]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[6].qwords.high = sub_400BA0((q_xmm_word)(input[13] * (unsigned __int128)s[13]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  result[7].qwords.low = sub_400BA0((q_xmm_word)(input[14] * (unsigned __int128)s[14]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  v3 = sub_400BA0((q_xmm_word)(input[15] * (unsigned __int128)s[15]), 0xFFFFFFFFFFFFFFC5LL, 0LL);  v4 = _mm_load_si128((const __m128i *)result);  result[7].qwords.high = v3;```
The logic is to regard 2 char arrays as arrays of `uint64_t`, multiply the input by `s` (the welcome message) and `0xFFFFFFFF` after that, then store the result in the result array.
Take a look at XMM instructions that follow:
```assemblymovdqa  xmm1, xmmword ptr [rsp+198h+result] ; regard result as array of uint128_tmov     qword ptr [rsp+198h+result+78h], raxmovdqa  xmm0, xmmword ptr cs:unk_602070pxor    xmm1, xmmword ptr cs:keys ; xmm1 = result[0] ^ keys[0]movdqa  xmm4, xmmword ptr [rsp+198h+result+20h]pxor    xmm0, xmmword ptr [rsp+198h+result+10h] ; xmm0 = keys[1] ^ result[1]movdqa  xmm3, xmmword ptr [rsp+198h+result+30h]pxor    xmm4, xmmword ptr cs:unk_602080 ; xmm4 = xor 2movdqa  xmm2, xmmword ptr [rsp+198h+result+40h]pxor    xmm3, xmmword ptr cs:unk_602090 ; xmm5 = xor3por     xmm1, xmm0      ; xmm1 = [0] | [1]pxor    xmm2, xmmword ptr cs:unk_6020A0 ; xmm2 = xor 4movdqa  xmm0, xmmword ptr [rsp+198h+result+60h]por     xmm4, xmm1      ; xmm4 = [0] | [1] | [2]movdqa  xmm1, xmmword ptr [rsp+198h+result+50h]pxor    xmm0, xmmword ptr cs:unk_6020C0 ; xmm0 = xor 6por     xmm3, xmm4      ; xmm3 = [0] | [1] | [2] | [3]pxor    xmm1, xmmword ptr cs:unk_6020B0 ; xmm1 = xor [5]por     xmm2, xmm3      ; xmm2 = [0] | [1] | [2] | [3] | [4]movdqa  xmm3, xmm2movdqa  xmm2, xmm1movdqa  xmm1, xmm0movdqa  xmm0, xmmword ptr cs:unk_6020D0por     xmm2, xmm3      ; xmm2 = [0] | [1] | [2] | [3] | [4] | [5]pxor    xmm0, xmmword ptr [rsp+198h+result+70h] ; xmm0 = xor7por     xmm1, xmm2por     xmm0, xmm1movdqa  xmm1, xmm0psrldq  xmm1, 8por     xmm0, xmm1movq    rax, xmm0test    rax, raxjz      short loc_400A86 ; jmp if correctmov     edi, offset s   ; "Incorrect."call    _puts```
The logic is, "xor" the result with the key, and "or" all of them together; if the final value obtained is 0, the answer is correct. This means that, all of the "xor" result must be 0, which means that result array from `sub_400BA0` must be same as key.
Interestingly, we can see the optimisation of the compiler, which generates the code that will be faster on out\-of\-order CPUs.
So take a look at `sub_400BA0`:
```cunsigned __int64 __fastcall sub_400BA0(q_xmm_word res, unsigned __int64 fc5, unsigned __int64 zero){  unsigned __int64 fc5_; // r10  unsigned __int64 result; // rax  unsigned __int64 v5; // rdx  __int64 v6; // rbp  int v7; // ebp  unsigned __int64 v8; // rbx  unsigned __int64 v9; // r10  unsigned __int64 v10; // r8  q_xmm_word v11; // tt  unsigned __int64 v12; // rsi  q_xmm_word v13; // ax  unsigned __int64 v14; // rcx  __int64 v15; // rdi  q_xmm_word v16; // ax  q_xmm_word tmp; // tt
  fc5_ = fc5;  result = res.qwords.low;  if ( zero )  { // can't reach here, zero is always 0, possibly obsfucation    if ( zero > res.qwords.high )    {      result = res.qwords.low;    }    else    {      _BitScanReverse64((unsigned __int64 *)&v6, zero);      v7 = v6 ^ 0x3F;      if ( v7 )      {        v8 = fc5 << v7;        v9 = (zero << v7) | (fc5 >> (64 - (unsigned __int8)v7));        v10 = res.qwords.low << v7;        v11.qwords.low = ((unsigned __int64)res.qwords.low >> (64 - (unsigned __int8)v7)) | (res.qwords.high << v7);        v11.qwords.high = res.qwords.high >> (64 - (unsigned __int8)v7);        v12 = v11.xmms % v9;        v13.xmms = (fc5 << v7) * (unsigned __int128)(unsigned __int64)(v11.xmms / v9);        v14 = v8 * (unsigned __int128)(unsigned __int64)(v11.xmms / v9) >> 64;        v15 = v8 * (v11.xmms / v9);        if ( v12 < v13.qwords.high || v12 == v13.qwords.high && v10 < v13.qwords.low )        {          v16.xmms = v13.xmms - __PAIR__(v9, v8);          v14 = v16.qwords.high;          v15 = v16.qwords.low;        }        result = ((v10 - v15) >> v7) | ((__PAIR__(v12, v10) - __PAIR__(v14, v15)) >> 64 << (64 - (unsigned __int8)v7));      }      else if ( zero < res.qwords.high || fc5 <= res.qwords.low )      {        result = res.qwords.low - fc5;      }    }  }  else  {    if ( fc5 <= res.qwords.high )    {      if ( !fc5 )        fc5_ = 1 / 0uLL;                        // never, fc5 is always a constant      tmp.qwords.low = res.qwords.low; // never, since for ret.high > fc5, the input is no longer readable      tmp.qwords.high = res.qwords.high % fc5_;      v5 = tmp.xmms % fc5_;    }    else    {//only useful part      v5 = res.xmms % fc5;    }    result = v5;  }  return result;}```
This function contains a lot useless code again: firstly, `zero` is always zero, so all the code in the first branch are useless; secondly, `fc5` is always non-zero, so the divivision by 0 exception can't occur; thirdly, `fc5 <= res.qwords.high` is always false, since for the result of multiplication to be larger than or equal to `0xFFFFFFFFFFFFFFC50000000000000000`, the input characters are not readable anymore, even if we took `0xFFFFFFFFFFFFFFFF` as the factor:
```python>>> hex(0xFFFFFFFFFFFFFFC50000000000000000 / 0xFFFFFFFFFFFFFFFF)'0xffffffffffffffc5L'```
Also, the bytes after the flag are `U` instead of `0xFF` as shown above; a number larger than `0xFFFFFFFFFFFFFFC5L` won't occur in the input array. So this means we need to solve the following equations:
```c(input[0] * 0x20656d6f636c6557) % 0xFFFFFFFFFFFFFFC5 == 0x2b7192452905e8fb(input[1] * 0x2046544352206f74) % 0xFFFFFFFFFFFFFFC5 == 0x7ba58f82bd898035(input[2] * 0x6548202138313032) % 0xFFFFFFFFFFFFFFC5 == 0xa3112746582e1434(input[3] * 0x2061207369206572) % 0xFFFFFFFFFFFFFFC5 == 0x163f756fcc221ab0(input[4] * 0x6320455279626142) % 0xFFFFFFFFFFFFFFC5 == 0xecc78e6fb9cba1fe(input[5] * 0x65676e656c6c6168) % 0xFFFFFFFFFFFFFFC5 == 0xdcdd8b49ea5d7e14(input[6] * 0x756f7920726f6620) % 0xFFFFFFFFFFFFFFC5 == 0xa2845fe0b3096f8e(input[7] * 0xffffffffffff002e) % 0xFFFFFFFFFFFFFFC5 == 0xaaaaaaaaaa975d1c(input[8] * 0xffffffffffffffff) % 0xFFFFFFFFFFFFFFC5 == 0x55555555555559a3(input[9] * 0xffffffffffffffff) % 0xFFFFFFFFFFFFFFC5 == 0x55555555555559a3(input[10] * 0xffffffffffffffff) % 0xFFFFFFFFFFFFFFC5 == 0x55555555555559a3(input[11] * 0xffffffffffffffff) % 0xFFFFFFFFFFFFFFC5 == 0x55555555555559a3(input[12] * 0xffffffffffffffff) % 0xFFFFFFFFFFFFFFC5 == 0x55555555555559a3(input[13] * 0xffffffffffffffff) % 0xFFFFFFFFFFFFFFC5 == 0x55555555555559a3(input[14] * 0xffffffffffffffff) % 0xFFFFFFFFFFFFFFC5 == 0x55555555555559a3(input[15] * 0xffffffffffffffff) % 0xFFFFFFFFFFFFFFC5 == 0x55555555555559a3```
These would take too long to brute-force (2^64-ish possibilities for each entry of the `input` array). But, multiply-modulo-compare is clearly just a [linear congruence](https://en.wikipedia.org/wiki/Chinese_remainder_theorem). A very fast algorithm to solve these is Euclid's extended algorithm.
([solver script](https://github.com/Aurel300/empirectf/blob/master/writeups/2018-05-19-RCTF/scripts/babyre2.py))
    666c61677b737461    795f7072696d655f    737461795f696e76    65727469626c655f    617761795f66726f    6d5f627275746566    6f7263657d005555    5555555555555555    5555555555555555    5555555555555555    5555555555555555    5555555555555555    5555555555555555    5555555555555555    5555555555555555    5555555555555555
And after decoding:
`flag{stay_prime_stay_invertible_away_from_bruteforce}`