【CTF reverse】[SCTF2019]babyre-除花指令+3道算法逆向

627 阅读14分钟

文件概况

Linux的file命令:

ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=b4876bc9fb492a1f16139a56b897b06f8e68d2c6, stripped

去除花指令

从798处开始就是一大堆被误认为数据的汇编代码。难道我的逆向生涯到此为止了吗?不!右键地址798处的第一个字节,左上角菜单Edit -> Code转化为代码。

作者:hans774882968以及hans774882968

此时我们肉眼可以找到4处花指令:

.text:000000000000098B                 jb      short near ptr loc_98F+1
.text:000000000000098D                 jnb     short near ptr loc_98F+1
.text:0000000000000AD8                 jb      short near ptr loc_ADC+1
.text:0000000000000ADA                 jnb     short near ptr loc_ADC+1
.text:0000000000000ADC loc_ADC:
.text:0000000000000ADC
.text:0000000000000ADC                 loop    near ptr loc_B24+2
.text:0000000000000B4F                 jb      short near ptr loc_B53+1
.text:0000000000000B51                 jnb     short near ptr loc_B53+1
.text:0000000000000B53
.text:0000000000000B53 loc_B53:
.text:0000000000000B53
.text:0000000000000B53                 jrcxz   near ptr loc_B9C+1
.text:0000000000000E0F                 jb      short near ptr loc_E13+1
.text:0000000000000E11                 jnb     short near ptr loc_E13+1
.text:0000000000000E13
.text:0000000000000E13 loc_E13:
.text:0000000000000E13
.text:0000000000000E13                 in      al, 0C7h

冷姿势:这几处花指令对应的二进制数据都是72 03 73 01 xx。把这5字节数据都用nop填充(换成90)即可。操作:左上角菜单Edit -> Patch program -> Change byte...

loc_798处右键,Create function,并改名为main。此时可以F5了。

第一关

int __cdecl main(int argc, const char **argv, const char **envp)
{
  int result; // eax@22
  __int64 v4; // rcx@29
  char v5; // [sp+Fh] [bp-151h]@3
  int v6; // [sp+10h] [bp-150h]@1
  signed int v7; // [sp+14h] [bp-14Ch]@1
  char *v8; // [sp+18h] [bp-148h]@1
  __int64 v9; // [sp+26h] [bp-13Ah]@1
  __int16 v10; // [sp+2Eh] [bp-132h]@1
  __int64 v11; // [sp+30h] [bp-130h]@1
  __int64 v12; // [sp+38h] [bp-128h]@1
  int v13; // [sp+40h] [bp-120h]@1
  __int64 v14; // [sp+50h] [bp-110h]@1
  __int64 v15; // [sp+58h] [bp-108h]@1
  __int64 v16; // [sp+60h] [bp-100h]@1
  char v17; // [sp+68h] [bp-F8h]@1
  __int64 v18; // [sp+70h] [bp-F0h]@1
  __int64 v19; // [sp+78h] [bp-E8h]@1
  __int64 v20; // [sp+80h] [bp-E0h]@1
  char v21; // [sp+88h] [bp-D8h]@1
  __int64 v22; // [sp+90h] [bp-D0h]@1
  __int64 v23; // [sp+98h] [bp-C8h]@1
  __int64 v24; // [sp+A0h] [bp-C0h]@1
  __int64 v25; // [sp+A8h] [bp-B8h]@1
  __int64 v26; // [sp+B0h] [bp-B0h]@1
  __int64 v27; // [sp+B8h] [bp-A8h]@1
  __int16 v28; // [sp+C0h] [bp-A0h]@1
  __int64 v29; // [sp+D0h] [bp-90h]@1
  __int64 v30; // [sp+D8h] [bp-88h]@1
  __int64 v31; // [sp+E0h] [bp-80h]@1
  __int64 v32; // [sp+E8h] [bp-78h]@1
  __int64 v33; // [sp+F0h] [bp-70h]@1
  __int64 v34; // [sp+F8h] [bp-68h]@1
  __int64 v35; // [sp+100h] [bp-60h]@1
  __int64 v36; // [sp+108h] [bp-58h]@1
  __int64 v37; // [sp+110h] [bp-50h]@1
  __int64 v38; // [sp+118h] [bp-48h]@1
  __int64 v39; // [sp+120h] [bp-40h]@1
  __int64 v40; // [sp+128h] [bp-38h]@1
  __int64 v41; // [sp+130h] [bp-30h]@1
  __int64 v42; // [sp+138h] [bp-28h]@1
  __int64 v43; // [sp+140h] [bp-20h]@1
  int v44; // [sp+148h] [bp-18h]@1
  __int16 v45; // [sp+14Ch] [bp-14h]@1
  __int64 v46; // [sp+158h] [bp-8h]@1

  v46 = *MK_FP(__FS__, 40LL);
  v6 = 0;
  v14 = 0LL;
  v15 = 0LL;
  v16 = 0LL;
  v17 = 0;
  v18 = 0LL;
  v19 = 0LL;
  v20 = 0LL;
  v21 = 0;
  v22 = 0LL;
  v23 = 0LL;
  v24 = 0LL;
  v25 = 0LL;
  v26 = 0LL;
  v27 = 0LL;
  v28 = 0;
  v29 = 3038287259199220266LL;
  v30 = 3039413159106062890LL;
  v31 = 3347065308717918762LL;
  v32 = 3038287259266591278LL;
  v33 = 3326517635351194154LL;
  v34 = 3038287259199220266LL;
  v35 = 3038287276446198314LL;
  v36 = 3036321349588626990LL;
  v37 = 3039417557152575022LL;
  v38 = 3038287259199482410LL;
  v39 = 3038287259199220266LL;
  v40 = 3326517635350932010LL;
  v41 = 3038287259266591274LL;
  v42 = 3326517652597910058LL;
  v43 = 3039413176353041966LL;
  v44 = 774515246;
  v45 = 42;
  v11 = 0LL;
  v12 = 0LL;
  v13 = 0;
  v8 = (char *)&v31 + 6;
  v9 = 3472619869582943091LL;
  v10 = 50;
  puts("plz tell me the shortest password1:");
  scanf("%s", &v22);
  v7 = 1;
  while ( v7 )
  {
    v5 = *((_BYTE *)&v22 + v6);
    switch ( v5 )
    {
      case 'w':
        v8 -= 5;
        break;
      case 's':
        v8 += 5;
        break;
      case 'd':
        ++v8;
        break;
      case 'a':
        --v8;
        break;
      case 'x':
        v8 += 25;
        break;
      case 'y':
        v8 -= 25;
        break;
      default:
        v7 = 0;
        break;
    }
    ++v6;
    if ( *v8 != '.' && *v8 != '#' )
      v7 = 0;
    if ( *v8 == '#' )
    {
      puts("good!you find the right way!\nBut there is another challenge!");
      break;
    }
  }
  if ( v7 )
  {
    puts("plz tell me the password2:");
    scanf("%s", &v14);
    sub_C22(&v14, &v18);
    if ( sub_F67(&v18, &v9) == 1 )
    {
      puts("Congratulation!");
      puts("Now,this is the last!");
      puts("plz tell me the password3:");
      scanf("%s", &v11);
      if ( sub_FFA(&v11) == 1 )
      {
        puts("Congratulation!Here is your flag!:");
        printf("sctf{%s-%s(%s)}", &v22, &v14, &v11);
      }
      else
      {
        printf("something srong...");
      }
      result = 0;
    }
    else
    {
      printf("sorry,somthing wrong...");
      result = 0;
    }
  }
  else
  {
    printf("sorry,is't not a right way...");
    result = 0;
  }
  v4 = *MK_FP(__FS__, 40LL) ^ v46;
  return result;
}

上面有些不明所以的v29 = 3038287259199220266LL之类的数据,让我深深怀疑花指令没除干净……它们的汇编长这样(只截取了一小部分)

; ...
.text:0000000000000857                 mov     rax, 2A2A2A2A2A2A2A2Ah
.text:0000000000000861                 mov     rdx, 2A2E2A2A2A2A2A2Ah
.text:000000000000086B                 mov     [rbp+var_90], rax
.text:0000000000000872                 mov     [rbp+var_88], rdx
.text:0000000000000879                 mov     rax, 2E732A2A2E2A2A2Ah
.text:0000000000000883                 mov     rdx, 2A2A2A2A2E2E2A2Eh
; ...

但随后我鼠标右键发现,可以转为字符串,这才意识到这玩意就是迷宫数据。在反汇编窗口右键数据,点击Char,即可看到迷宫:

  v29 = '********';
  v30 = '*.******';
  v31 = '.s**.***';
  v32 = '****..*.';
  v33 = '.****.**';
  v34 = '********';
  v35 = '***..***';
  v36 = '*#..**..';
  v37 = '*..***..';
  v38 = '*****.**';
  v39 = '********';
  v40 = '.*******';
  v41 = '****..**';
  v42 = '.**..***';
  v43 = '*.*..*..';
  v44 = '.**.';
  v45 = '*';

解迷宫代码如下:

def solve_maze(mp):
    delta = [
        [0,-1,"a"],[0,1,"d"],[1,0,"s"],
        [-1,0,"w"],[5,0,"x"],[-5,0,"y"]
    ]
    path = ""
    N,M = 25,5#迷宫行列数
    st = mp.index("s")
    ed = mp.index("#")
    ID = lambda x,y: x * M + y
    vis = [[False] * M for _ in range(N)]

    def dfs(x,y,cPath = ""):
        nonlocal path
        if x < 0 or x >= N or y < 0 or y >= M or vis[x][y] or mp[ID(x,y)] == "*":
            return False
        if ID(x,y) == ed:
            path = cPath
            return True
        vis[x][y] = True
        for i in range(6):
            fl = dfs(x + delta[i][0],y + delta[i][1],cPath + delta[i][2])
            if fl:
                vis[x][y] = False
                return True
        vis[x][y] = False
        return False

    dfs(st // M,st % M)
    return path

def main():
    maze_raw = '''
        v29 = '********';
        v30 = '*.******';
        v31 = '.s**.***';
        v32 = '****..*.';
        v33 = '.****.**';
        v34 = '********';
        v35 = '***..***';
        v36 = '*#..**..';
        v37 = '*..***..';
        v38 = '*****.**';
        v39 = '********';
        v40 = '.*******';
        v41 = '****..**';
        v42 = '.**..***';
        v43 = '*.*..*..';
        v44 = '.**.';
        v45 = '*';
    '''.split("\n")
    mp = ""
    for ln in maze_raw:
        ln = ln.strip()
        if not ln: continue
        mp += ln[ln.index("'")+1:-2][::-1]
    print(mp,len(mp))#dbg
    for i in range(25): print(mp[i*5:i*5+5])#dbg
    path = solve_maze(mp)
    print(path)#ddwwxxssxaxwwaasasyywwdd

if __name__ == "__main__":
    main()

值得注意的点:

  1. 这题没有特判,只接受字典序最小的路径:ddwwxxssxaxwwaasasyywwdd。虽然saxssd也是合法的,但它在buuoj里就不对。
  2. 如何求字典序最小路径?路径尝试顺序按字典序升序排序即可。

saxssd也是合法路径的证明:

// 在虚拟机(Ubuntu)中运行:
plz tell me the shortest password1:
saxssd
good!you find the right way!
But there is another challenge!
plz tell me the password2:
c2N0Zl85MTAy
Congratulation!
Now,this is the last!
plz tell me the password3:
fl4g_is_s0_ug1y!
Congratulation!Here is your flag!:
sctf{saxssd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)}

第二关

__int64 __fastcall sub_C22(char *a1, __int64 a2)
{
  signed __int64 v2; // rcx@1
  __int64 v3; // rdi@1
  _UNKNOWN *v4; // rsi@1
  bool v5; // al@12
  int v6; // eax@21
  int v7; // eax@23
  int v8; // eax@25
  __int64 out; // [sp+0h] [bp-260h]@1
  char *s; // [sp+8h] [bp-258h]@1
  int v12; // [sp+14h] [bp-24Ch]@4
  signed int v13; // [sp+18h] [bp-248h]@4
  int v14; // [sp+1Ch] [bp-244h]@17
  int v15; // [sp+20h] [bp-240h]@4
  int v16; // [sp+24h] [bp-23Ch]@4
  int sl; // [sp+28h] [bp-238h]@4
  int v18; // [sp+2Ch] [bp-234h]@5
  char *v19; // [sp+48h] [bp-218h]@4
  int v20[130]; // [sp+50h] [bp-210h]@1
  __int64 v21; // [sp+258h] [bp-8h]@1

  s = a1;
  out = a2;
  v21 = *MK_FP(__FS__, 40LL);
  v2 = 64LL;
  v3 = (__int64)v20;
  v4 = &unk_1740;
  // v20单纯地从unk_1740常量数组取值
  while ( v2 )
  {
    *(_QWORD *)v3 = *(_QWORD *)v4;
    v4 = (char *)v4 + 8;
    v3 += 8LL;
    --v2;
  }
  v13 = 3;
  v12 = 0;
  v15 = 0;
  v16 = 0;
  sl = strlen(s);
  v19 = s;
  while ( 1 )
  {
    v18 = 0;
    if ( v15 < sl )
      break;
LABEL_16:
    if ( v15 >= sl )
      goto LABEL_17;
  }
  do
  {
    if ( s[v15] != 25 )
      break;
    ++v15;
    ++v18;
  }
  while ( v15 < sl );
  if ( v15 != sl )
  {
    if ( sl - v15 > 1 )
    {
      v5 = v15 == 19 && s[20] == 16;
      s[v5];
    }
    ++v15;
    goto LABEL_16;
  }
LABEL_17:
  v14 = 0;
  // v19即输入串
  while ( sl > 0 )
  {
    v13 -= v20[*v19] == 0x40;
    v12 = v20[*v19] & 0x3F | (v12 << 6);
    ++v14;
    if ( v14 == 4 )
    {
      v14 = 0;
      if ( v13 )
      {
        v6 = v16++;
        *(_BYTE *)(v6 + out) = v12 >> 16;
      }
      if ( v13 > 1 )
      {
        v7 = v16++;
        *(_BYTE *)(v7 + out) = BYTE1(v12);
      }
      if ( v13 > 2 )
      {
        v8 = v16++;
        *(_BYTE *)(v8 + out) = v12;
      }
    }
    ++v19;
    --sl;
  }
  return *MK_FP(__FS__, 40LL) ^ v21;
}

sub_F67(&v18, &v9)就是判定v18v9字符串是否相等。把v18改名out,out等于字符串sctf_9102即可。分析过程如下:

  1. 别忘了低地址低位v9右键转为Char后记得反转。
  2. main函数的v10v9相邻,且类型为int16,初值为50(即'2'),高位的0构成了结尾字符'\0'

接下来看sub_C22的这段代码:

  int v20[130]; // [sp+50h] [bp-210h]@1
  v2 = 64LL;
  v3 = (__int64)v20;
  v4 = &unk_1740;
  // v20单纯地从unk_1740常量数组取值
  while ( v2 )
  {
    *(_QWORD *)v3 = *(_QWORD *)v4;
    v4 = (char *)v4 + 8;
    v3 += 8LL;
    --v2;
  }

这段代码实际上就是memcpy,但我误以为它是每隔8字节取一个值呜呜呜……

如何从unk_1740常量数组取值:选中512个字节,shift+E,然后写一小段python,每隔4字节取一个值。

sub_C22逆过程的python代码如下:

def main():
    dat = [127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 127, 62, 127, 127, 127, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 127, 127, 127, 64, 127, 127, 127, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 127, 127, 127, 127, 127, 127, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 127, 127, 127, 127, 127]
    s = "sctf_9102"
    ans = ""
    for i in range(0,9,3):
        v = (ord(s[i]) << 16) | (ord(s[i+1]) << 8) | ord(s[i+2])
        for j in range(18,-1,-6):
            ans += chr(dat.index(v >> j & 0x3f))
    print(ans)# c2N0Zl85MTAy

if __name__ == "__main__":
    main()

实际上,看到那段代码“每处理4个字符,输出3个字符”,也可以猜想是base64。并假定没有换码表,也得c2N0Zl85MTAy

第三关

signed __int64 __fastcall sub_FFA(__int64 a1)
{
  int v1; // ST24_4@1
  int v2; // ST28_4@1
  int v3; // ST2C_4@1
  signed __int64 result; // rax@6
  __int64 v5; // rcx@9
  signed int v6; // [sp+18h] [bp-158h]@1
  signed int i; // [sp+18h] [bp-158h]@3
  int v8; // [sp+1Ch] [bp-154h]@1
  int v9; // [sp+30h] [bp-140h]@1
  int v10; // [sp+34h] [bp-13Ch]@1
  int v11; // [sp+38h] [bp-138h]@1
  int v12; // [sp+3Ch] [bp-134h]@1
  int v13; // [sp+40h] [bp-130h]@1
  int v14; // [sp+44h] [bp-12Ch]@1
  int v15; // [sp+48h] [bp-128h]@1
  int v16; // [sp+4Ch] [bp-124h]@1
  int v17; // [sp+50h] [bp-120h]@1
  int v18; // [sp+54h] [bp-11Ch]@1
  int v19; // [sp+58h] [bp-118h]@1
  int v20; // [sp+5Ch] [bp-114h]@1
  int v21; // [sp+60h] [bp-110h]@1
  int v22; // [sp+64h] [bp-10Ch]@1
  int v23; // [sp+68h] [bp-108h]@1
  int v24; // [sp+6Ch] [bp-104h]@1
  unsigned int v25; // [sp+70h] [bp-100h]@3
  int v26; // [sp+74h] [bp-FCh]@3
  int v27; // [sp+78h] [bp-F8h]@3
  int v28; // [sp+7Ch] [bp-F4h]@3
  unsigned int v29; // [sp+80h] [bp-F0h]@3
  int v30; // [sp+84h] [bp-ECh]@3
  int v31; // [sp+88h] [bp-E8h]@3
  int v32; // [sp+8Ch] [bp-E4h]@3
  unsigned int v33; // [sp+90h] [bp-E0h]@3
  int v34; // [sp+94h] [bp-DCh]@3
  int v35; // [sp+98h] [bp-D8h]@3
  int v36; // [sp+9Ch] [bp-D4h]@3
  unsigned int v37; // [sp+A0h] [bp-D0h]@3
  int v38; // [sp+A4h] [bp-CCh]@3
  int v39; // [sp+A8h] [bp-C8h]@3
  int v40; // [sp+ACh] [bp-C4h]@3
  int v41; // [sp+B0h] [bp-C0h]@1
  int v42; // [sp+B4h] [bp-BCh]@1
  int v43; // [sp+B8h] [bp-B8h]@1
  int v44; // [sp+BCh] [bp-B4h]@1
  unsigned int v45; // [sp+118h] [bp-58h]@3
  unsigned int v46; // [sp+11Ch] [bp-54h]@3
  unsigned int v47; // [sp+120h] [bp-50h]@3
  unsigned int v48; // [sp+124h] [bp-4Ch]@3
  __int64 v49; // [sp+168h] [bp-8h]@1

  v49 = *MK_FP(__FS__, 40LL);
  v9 = 190;
  v10 = 4;
  v11 = 6;
  v12 = 128;
  v13 = 197;
  v14 = 175;
  v15 = 118;
  v16 = 71;
  v17 = 159;
  v18 = 204;
  v19 = 64;
  v20 = 31;
  v21 = 216;
  v22 = 191;
  v23 = 146;
  v24 = 239;
  v1 = (*(_BYTE *)(a1 + 6) << 8) | (*(_BYTE *)(a1 + 5) << 16) | (*(_BYTE *)(a1 + 4) << 24) | *(_BYTE *)(a1 + 7);
  v2 = (*(_BYTE *)(a1 + 10) << 8) | (*(_BYTE *)(a1 + 9) << 16) | (*(_BYTE *)(a1 + 8) << 24) | *(_BYTE *)(a1 + 11);
  v3 = (*(_BYTE *)(a1 + 14) << 8) | (*(_BYTE *)(a1 + 13) << 16) | (*(_BYTE *)(a1 + 12) << 24) | *(_BYTE *)(a1 + 15);
  v8 = 0;
  v6 = 4;
  v41 = sub_78A((*(_BYTE *)(a1 + 2) << 8) | (*(_BYTE *)(a1 + 1) << 16) | (*(_BYTE *)a1 << 24) | (unsigned int)*(_BYTE *)(a1 + 3));
  v42 = sub_78A((unsigned int)v1);
  v43 = sub_78A((unsigned int)v2);
  v44 = sub_78A((unsigned int)v3);
  // 低地址高位,再按字节颠倒,故以上代码相当于直接从输入串复制前16个字符,低地址低位。
  do
  {
    *(&v41 + v6) = sub_143B(
                     (unsigned int)*(&v41 + v8),
                     (unsigned int)*(&v41 + v8 + 1),
                     (unsigned int)*(&v41 + v8 + 2),
                     (unsigned int)*(&v41 + v8 + 3));
    ++v8;
    ++v6;
  }
  while ( v6 <= 29 );
  v25 = v45 >> 24;
  v26 = (unsigned __int8)(v45 >> 16);
  v27 = BYTE1(v45);
  v28 = (unsigned __int8)v45;
  v29 = v46 >> 24;
  v30 = (unsigned __int8)(v46 >> 16);
  v31 = BYTE1(v46);
  v32 = (unsigned __int8)v46;
  v33 = v47 >> 24;
  v34 = (unsigned __int8)(v47 >> 16);
  v35 = BYTE1(v47);
  v36 = (unsigned __int8)v47;
  v37 = v48 >> 24;
  v38 = (unsigned __int8)(v48 >> 16);
  v39 = BYTE1(v48);
  v40 = (unsigned __int8)v48;
  // v9和v25是两个整数数组
  for ( i = 0; i <= 15; ++i )
  {
    if ( *(&v25 + i) != *(&v9 + i) )
    {
      result = 0xFFFFFFFFLL;
      goto LABEL_9;
    }
  }
  result = 1LL;
LABEL_9:
  v5 = *MK_FP(__FS__, 40LL) ^ v49;
  return result;
}

__int64 __fastcall sub_143B(int a1, int a2, int a3, unsigned int a4)
{
  return a1 ^ (unsigned int)sub_1464(a2 ^ a3 ^ a4);
}

__int64 __fastcall sub_1464(unsigned int a1)
{
  signed __int64 v1; // rcx@1
  __int64 v2; // rdi@1
  _UNKNOWN *v3; // rsi@1
  int v4; // ST18_4@4
  int v5; // eax@4
  int v6; // edx@4
  int v7; // eax@4
  int v8; // edx@4
  int v9; // eax@4
  int v10; // edx@4
  unsigned int v11; // eax@4
  __int64 result; // rax@4
  __int64 v13; // rcx@4
  unsigned int v14; // [sp+Ch] [bp-4A4h]@1
  int v15[290]; // [sp+20h] [bp-490h]@1
  __int64 v16; // [sp+4A8h] [bp-8h]@1

  v14 = a1;
  v16 = *MK_FP(__FS__, 40LL);
  v1 = 144LL;
  v2 = (__int64)v15;
  v3 = &unk_1940;
  while ( v1 )
  {
    *(_QWORD *)v2 = *(_QWORD *)v3;
    v3 = (char *)v3 + 8;
    v2 += 8LL;
    --v1;
  }
  v4 = (v15[(unsigned __int64)(unsigned __int8)(v14 >> 16)] << 16) | v15[(unsigned __int64)(unsigned __int8)v14] | (v15[(unsigned __int64)BYTE1(v14)] << 8) | (v15[(unsigned __int64)(v14 >> 24)] << 24);
  v5 = __ROR4__(v4, 2);
  v6 = v5;
  v7 = __ROL4__(v4, 8);
  v8 = v7 ^ v6;
  v9 = __ROL4__(v4, 12);
  v10 = v9 ^ v8;
  v11 = __ROR4__(v4, 6);
  result = v10 ^ v11;
  v13 = *MK_FP(__FS__, 40LL) ^ v16;
  return result;
}

先看

  v1 = (*(_BYTE *)(a1 + 6) << 8) | (*(_BYTE *)(a1 + 5) << 16) | (*(_BYTE *)(a1 + 4) << 24) | *(_BYTE *)(a1 + 7);
  v2 = (*(_BYTE *)(a1 + 10) << 8) | (*(_BYTE *)(a1 + 9) << 16) | (*(_BYTE *)(a1 + 8) << 24) | *(_BYTE *)(a1 + 11);
  v3 = (*(_BYTE *)(a1 + 14) << 8) | (*(_BYTE *)(a1 + 13) << 16) | (*(_BYTE *)(a1 + 12) << 24) | *(_BYTE *)(a1 + 15);
  v8 = 0;
  v6 = 4;
  v41 = sub_78A((*(_BYTE *)(a1 + 2) << 8) | (*(_BYTE *)(a1 + 1) << 16) | (*(_BYTE *)a1 << 24) | (unsigned int)*(_BYTE *)(a1 + 3));
  v42 = sub_78A((unsigned int)v1);
  v43 = sub_78A((unsigned int)v2);
  v44 = sub_78A((unsigned int)v3);
  // 低地址高位,再按字节颠倒,故以上代码相当于直接从输入串复制前16个字符,低地址低位。

根据参考链接1,_byteswap_ulong(即sub_78A的返回值)以字节为单位颠倒。因此有以上注释的结论。

逆向思路

v25和v9是两个int数组,v41也是int数组,v45~v48正是v41数组的最后4个下标。我们已经知道了v45~v48,(记al = v41.size())而v41[al - 5] = v45 ^ (unsigned int)sub_1464(v46 ^ v47 ^ v48)故能推出v41[al - 5]。依此类推,倒序遍历即可获得v41~v44。于是可得输入串。

sub_1464很复杂,因此建议使用cpp来实现逆向过程。注意点:

  1. defs.h可以在IDA的安装路径中找到。
  2. 只有v14变量必须为unsigned int,因为需要保证下标为非负,并且int和unsigned int只有数据解释方式的差别。
#include <bits/stdc++.h>
#include "defs.h"
using namespace std;
typedef long long LL;
typedef unsigned int ui;
#define rep(i,a,b) for(int i = (a);i <= (b);++i)
#define re_(i,a,b) for(int i = (a);i < (b);++i)
#define dwn(i,a,b) for(int i = (a);i >= (b);--i)

void dbg() {
    puts ("");
}
template<typename T, typename... R>void dbg (const T &f, const R &... r) {
    cout << f << " ";
    dbg (r...);
}

LL sub_1464 (int a1) {
    LL v2; // rdi@1
    int v4; // ST18_4@4
    int v5; // eax@4
    int v6; // edx@4
    int v7; // eax@4
    int v8; // edx@4
    int v9; // eax@4
    int v10; // edx@4
    int v11; // eax@4
    LL result; // rax@4
    LL v13; // rcx@4
    ui v14; // [sp+Ch] [bp-4A4h]@1
    int v15[290] = {
        214, 144, 233, 254, 204, 225, 61, 183, 22, 182, 20, 194, 40, 251, 44, 5, 43, 103, 154, 118, 42, 190, 4, 195, 170, 68, 19, 38, 73, 134, 6, 153, 156, 66, 80, 244, 145, 239, 152, 122, 51, 84, 11, 67, 237, 207, 172, 98, 228, 179, 28, 169, 201, 8, 232, 149, 128, 223, 148, 250, 117, 143, 63, 166, 71, 7, 167, 252, 243, 115, 23, 186, 131, 89, 60, 25, 230, 133, 79, 168, 104, 107, 129, 178, 113, 100, 218, 139, 248, 235, 15, 75, 112, 86, 157, 53, 30, 36, 14, 94, 99, 88, 209, 162, 37, 34, 124, 59, 1, 33, 120, 135, 212, 0, 70, 87, 159, 211, 39, 82, 76, 54, 2, 231, 160, 196, 200, 158, 234, 191, 138, 210, 64, 199, 56, 181, 163, 247, 242, 206, 249, 97, 21, 161, 224, 174, 93, 164, 155, 52, 26, 85, 173, 147, 50, 48, 245, 140, 177, 227, 29, 246, 226, 46, 130, 102, 202, 96, 192, 41, 35, 171, 13, 83, 78, 111, 213, 219, 55, 69, 222, 253, 142, 47, 3, 255, 106, 114, 109, 108, 91, 81, 141, 27, 175, 146, 187, 221, 188, 127, 17, 217, 92, 65, 31, 16, 90, 216, 10, 193, 49, 136, 165, 205, 123, 189, 45, 116, 208, 18, 184, 229, 180, 176, 137, 105, 151, 74, 12, 150, 119, 126, 101, 185, 241, 9, 197, 110, 198, 132, 24, 240, 125, 236, 58, 220, 77, 32, 121, 238, 95, 62, 215, 203, 57, 72, 198, 186, 177, 163, 80, 51, 170, 86, 151, 145, 125, 103, 220, 34, 112, 178, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    }; // [sp+20h] [bp-490h]@1
    LL v16; // [sp+4A8h] [bp-8h]@1

    v14 = a1;
    v4 = (v15[ (unsigned __int8) (v14 >> 16)] << 16) | v15[ (unsigned __int8) v14] | (v15[BYTE1 (v14)] << 8) | (v15[ (v14 >> 24)] << 24);
    v5 = __ROR4__ (v4, 2);
    v6 = v5;
    v7 = __ROL4__ (v4, 8);
    v8 = v7 ^ v6;
    v9 = __ROL4__ (v4, 12);
    v10 = v9 ^ v8;
    v11 = __ROR4__ (v4, 6);
    result = v10 ^ v11;
    return result;
}

int getV (int v1, int v2, int v3, int v4) {
    return (v1 << 24) | (v2 << 16) | (v3 << 8) | v4;
}

int main() {
    vector<int> a = {
        getV (216, 191, 146, 239), getV (159, 204, 64, 31),
        getV (197, 175, 118, 71), getV (190, 4, 6, 128)
    };
    dwn (i, 25, 0) {
        int al = a.size();
        a.push_back (a[al - 4] ^ sub_1464 (a[al - 3] ^ a[al - 2] ^ a[al - 1]) );
    }
    int al = a.size();
    string ans;
    rep (i, 1, 4) {
        for (int j = 0; j < 32; j += 8)
            dbg (i, j, a[al - i] >> j & 0xff), ans += char (a[al - i] >> j & 0xff);
    }
    dbg (ans);
    return 0;
}

flag:flag{ddwwxxssxaxwwaasasyywwdd-c2N0Zl85MTAy(fl4g_is_s0_ug1y!)}

参考链接

  1. _byteswap_ulong:docs.microsoft.com/zh-cn/cpp/c…