Hgame2020_Week3_Reverse

有点不知道做啥好,写写WP

Go-Master

flag说的很对,调试就完事了。

运行程序会要先输入localhost,之后提示The server is on。

找了一会发现是打开了本地的2333端口,可以nc localhost 2333来进行交互。

提示输入一个KEY,这里定位到main_handleRequest这个函数:

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
26
27
28
29
30
if ( (unsigned __int64)&retaddr <= *(_QWORD *)(__readfsqword(0xFFFFFFF8) + 16) )
runtime_morestack_noctxt();
runtime_newobject(a1, a2);
*(_QWORD *)v20 = 2334391967770110279LL;
*(_DWORD *)(v20 + 8) = 173622603;
(*(void (__cdecl **)(__int64))(a7 + 80))(a1);
runtime_makeslice(a1, a2, v10, v11);
v25 = 12LL;
(*(void (__cdecl **)(__int64, __int64, _QWORD, __int64))(a7 + 40))(a1, a2, *(_QWORD *)(a7 + 40), a7);
*(_QWORD *)&v12 = &v24;
*((_QWORD *)&v12 + 1) = 12LL;
runtime_slicebytetostring(a1, a2, v13, v14, v15, v12);
main_Encrypt(a1, a2, v16, *((__int64 *)&v22 + 1), v17, v22);
if ( v23 == 96 && (v21 = (char *)&unk_55790F, runtime_memequal(a1, a2, v18, (unsigned __int64)&unk_55790F), a10) )
{
runtime_newobject(a1, a2);
unk_55790F = 2334392307038315863LL;
*(_OWORD *)((char *)&unk_55790F + 7) = xmmword_556F17;
*(_OWORD *)((char *)&unk_55790F + 23) = xmmword_556F27;
*(_OWORD *)((char *)&unk_55790F + 39) = xmmword_556F37;
(*(void (__cdecl **)(__int64))(a7 + 80))(a1);
}
else
{
runtime_newobject(a1, a2);
*(_QWORD *)v21 = 7142757909132373843LL;
*(_OWORD *)(v21 + 8) = xmmword_552558;
(*(void (__cdecl **)(__int64))(a7 + 80))(a1);
}
return (*(__int64 (__cdecl **)(__int64))(a7 + 24))(a1);

基本上就是对输入的KEY加密之后和固定字符串比较。

先提取出最终比较的字符串55790F:

2ec69be41e5d095ad309036cc8cd70a7f0e90deebb6577fcde3298331888d46380a4abf840b3c18f2e57286716273164

一共96个字符,进入main_Encrypt函数:

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
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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
v9 = __readfsqword(0xFFFFFFF8);
if ( (unsigned __int64)&v42 <= *(_QWORD *)(v9 + 16) )
runtime_morestack_noctxt();
runtime_stringtoslicebyte(a1, a2, a3, v9, a5, a6);
v40 = v33;
*((_QWORD *)&v10 + 1) = v36;
*(_QWORD *)&v10 = v35;
crypto_des_NewCipher(a1, a2, v10, v11);
result = (_QWORD *)v37;
if ( !v36 )
{
v42 = v35;
v39 = v33;
(*(void (__cdecl **)(__int64))(v33 + 24))(a1);
v34 = a9;
main_ZeroPadding(a1, a2, v13, 0LL, v14, v15, v40);
if ( !a9 )
runtime_panicdivide(a1, a2);
if ( 0 % (signed __int64)a9 )
{
runtime_newobject();
result = v35;
v35[1] = 32LL;
*v35 = &unk_55421B;
}
else
{
runtime_makeslice(v37);
v16 = a9;
v44 = a9;
v17 = a9;
v18 = v39;
v19 = v36;
v20 = (__int64)v42;
v21 = (__int64)v35;
v22 = v37;
v23 = v36;
while ( (signed __int64)v19 > 0 )
{
if ( v17 > v22 )
runtime_panicSliceAcap(v21, v20);
v41 = v16;
v38 = v22;
v34 = v19;
(*(void (__cdecl **)(__int64, __int64, __int64))(v18 + 40))(v21, v20, v18);
if ( a9 > v19 )
runtime_panicSliceB(v21, v20);
v25 = v19 - a9;
if ( a9 > v19 )
runtime_panicSliceB(v38 - a9, v21 + (a9 & ((signed __int64)(a9 - v38) >> 63)));
v19 -= a9;
v24 = (a9 & (-(signed __int64)v25 >> 63)) + v41;
v17 = a9;
v18 = v39;
v23 = v36;
v22 = v38 - a9;
v21 += a9 & ((signed __int64)(a9 - v38) >> 63);
v16 = v24;
v20 = (__int64)v42;
}
v26 = 2 * v23;
runtime_makeslice(v21);
v43 = v34;
encoding_hex_Encode(v21, v20, v36, v26, v27, v28, v34, v26);
*(_QWORD *)&v29 = 0LL;
*((_QWORD *)&v29 + 1) = v43;
runtime_slicebytetostring(v21, v20, v30, v31, v32, v29);
result = (_QWORD *)v36;
}
}
return result;

看起来蛮复杂的,大致上应该就是用des对输入加密之后转hex。

调试的时候可以断在crypto_des_NewCipher(a1, a2, v10, v11);来看一下des的参数。

最终发现des加密的key(不是输入的KEY)是localhos,由此对之前提取出的固定字符串解密得到正确输入,即flag。

oooollvm

年轻人的第一个混淆,不禁想起了某次的奖品年轻人的第一个机械键盘2333

限制了输入长度是34,验证部分其实就一句话:

1
table2[v14] != (~s[v14] & (v14 + table1[v14]) | ~(v14+ table1[v14]) & s[v14]);

后面的运算等价于异或,所以可以替换为:

1
table2[v14] != s[v14] ^ ( table[v14] + v14 )

s即要求的输入,逆运算一下得到flag。

hidden

通过查找字符串可以定位到:

1
2
3
4
5
6
7
8
9
10
__int64 __fastcall sub_140001030(int a1)
{
__int64 v1; // rax

if ( a1 )
v1 = sub_1400015F0(std::cout, "ok you get the flag");
else
v1 = sub_1400015F0(std::cout, "error");
return std::basic_ostream<char,std::char_traits<char>>::operator<<(v1, sub_140001A80);
}

然后看一下sub_140001030的交叉引用:

1
2
3
4
5
Direction	Type	Address	Text
Down j sub_140001270+4C jmp sub_140001030
Down j sub_140001270+5D jmp sub_140001030
Down o sub_1400010C0+11F lea rdx, sub_140001030
Down o .pdata:ExceptionDir RUNTIME_FUNCTION <rva sub_140001030, rva algn_14000108C, \

这里先跟进sub_140001270看了一下,是要求输入40个字符,然后20个字符为一组进行CRC32,得到的结果和两个固定数进行比较。

这里再跟进sub_1400010C0,找到调用的地方:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
v3 = a1;
v4 = a3;
v5 = a2;
v6 = v21;
v7 = (unsigned int *)VirtualAlloc(0i64, 0x4000ui64, 0x3000u, 0x40u);
v8 = 0;
v9 = v7;
do
{
if ( v8 >= 256 )
{
*v9 = v6 ^ *(unsigned int *)((char *)v9 + &unk_140007000 - (_UNKNOWN *)v7 - 1024);
if ( v8 == 4095 )
sub_140001010(v5, sub_140001030, v7 + 256, sub_1400010A0);
}

可以发现用VirtualAlloc开辟了一块可以执行的空间,真正的代码就藏在了这里。

sub_140001010

1
.text:00007FF777B31010 sub_7FF777B31010 proc near
.text:00007FF777B31010
.text:00007FF777B31010 var_8= qword ptr -8
.text:00007FF777B31010 arg_FEF0= qword ptr  0FEF8h
.text:00007FF777B31010
.text:00007FF777B31010 push    r9
.text:00007FF777B31012 sub     rsp, 100h
.text:00007FF777B31019 call    r8
.text:00007FF777B3101C call    [rsp+108h+var_8]
.text:00007FF777B31023 mov     [rsp+108h+arg_FEF0], 0
.text:00007FF777B3102F int     3               ; Trap to Debugger
.text:00007FF777B3102F sub_7FF777B31010 endp ; sp-analysis failed

这里选择调试看一下call r8时调用的代码,运行到call r8这一行后f7步入,手动创建一个函数,f5得到关键部分:

1
__int64 __fastcall sub_2030CAA0400(__int64 a1, __int64 (__fastcall *a2)(_QWORD))
{
  int j; // [rsp+20h] [rbp-78h]
  int i; // [rsp+24h] [rbp-74h]
  int l; // [rsp+28h] [rbp-70h]
  int k; // [rsp+2Ch] [rbp-6Ch]
  unsigned int v7; // [rsp+30h] [rbp-68h]
  char v8[38]; // [rsp+38h] [rbp-60h]
  char v9; // [rsp+5Eh] [rbp-3Ah]
  char *v10; // [rsp+60h] [rbp-38h]
  __int64 v11; // [rsp+68h] [rbp-30h]
  __int64 v12; // [rsp+70h] [rbp-28h]
  __int64 v13; // [rsp+78h] [rbp-20h]
  __int64 v14; // [rsp+80h] [rbp-18h]
  __int64 v15; // [rsp+88h] [rbp-10h]

  for ( i = 0; i < 40; ++i )
    v8[i] = *(_BYTE *)(a1 + i);
  v10 = &v9;
  for ( j = 0; j < 19; ++j )
  {
    for ( k = 0; k < 2; ++k )
    {
      v8[j] ^= v8[j + 19];
      v8[j] += v10[k];
      v8[j + 19] -= 103;
      v8[j + 19] ^= v8[j];
    }
  }
  v7 = 1;
  for ( l = 0; l < 40; ++l )
  {
    v11 = 0x7B754B47758F8846i64;
    v12 = 0x48757A7B8A7F798Ei64;
    v13 = 0x4B7D87824B7B7B7Bi64;
    v14 = 0x81817350A79B885Di64;
    v15 = 0x7D65574F57FA729Ai64;
    if ( v8[l] != *((char *)&v11 + l) )
    {
      v7 = 0;
      return a2(v7);
    }
  }
  return a2(v7);
}

这部分即是真正的验证部分,逆运算一下得到flag。

文章目录
  1. 1. Go-Master
  2. 2. oooollvm
  3. 3. hidden
|