首页
论坛
课程
招聘
[原创] KCTF2021秋季赛 声名远扬
2021-12-2 02:15 11896

[原创] KCTF2021秋季赛 声名远扬

2021-12-2 02:15
11896

考察C++逆向。

 

总体思路:动态调试,黑盒测试。

 

Windows 32位程序,无壳。

 

注:以下分析如未作特殊说明,默认基址为0x251000

 

题目存在一些花指令,不多做阐述,nop修复即可。

 

比较多的虚函数,同时能看到关键词DuiLib

 

image-20211202012001688

 

有关DuiLib能够从网上找到N篇文章介绍切入点,随便挂一个

 

Dump微信PC端的界面Duilib文件-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com

 

通过虚函数跳转最终定位到按钮回调函数sub_26D2D0

 

简单调试分析后,能够发现首先是进行了变表的base64编码,具体位置在0x26E530,伪代码分析特征还是比较明显的

 

image-20211202014101147

 

当然最大的特征当属编码表,其特征位于函数0x26E250,伪代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
int __cdecl base64Maps(int a1)
{
  int *v1; // eax
  char v3[8]; // [esp+4h] [ebp-68h] BYREF
  int v4; // [esp+Ch] [ebp-60h]
  _BYTE base64[65]; // [esp+10h] [ebp-5Ch] BYREF
  unsigned int v6[2]; // [esp+51h] [ebp-1Bh] BYREF
  int v7; // [esp+68h] [ebp-4h]
 
  v4 = 0;
  sub_26D5D0((char *)v6 + 3, 8u);
  qmemcpy(base64, "prvo9CHSJOcPIb6xRVUXQz0qBGDE72LNZduaefYT5K_8-4FAhlimjkngt1yMWs3w!", sizeof(base64));
  v1 = (int *)__FrameHandler3::TryBlockMap::TryBlockMap(
                (__FrameHandler3::TryBlockMap *)v3,
                (const struct _s_FuncInfo *)base64,
                (unsigned int)v6);
  sub_26EA90((char *)v6 + 3, *v1, v1[1]);
  v7 = 0;
  sub_26EAD0((void *)a1, (int)v6 + 3);
  v4 |= 1u;
  v7 = -1;
  sub_26EA70();
  return a1;
}

下面进行验证

 

动态调试得到编码串

 

image-20211202014559436

 

尝试变表解密

1
2
3
4
5
6
7
8
import base64
 
baseMaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
newMaps = "prvo9CHSJOcPIb6xRVUXQz0qBGDE72LNZduaefYT5K_8-4FAhlimjkngt1yMWs3w!"
 
cipherText = "EHsnG0bjGT44BqIhETj!"
cipherText = cipherText.translate(cipherText.maketrans(newMaps, baseMaps))
print(base64.b64decode(cipherText.encode('utf-8')).decode())

解得lovectf{mas0n},验证成功

 

进入下一步

 

经过调试分析确定总体逻辑如下图

 

image-20211202015147080

 

单步调试过程中会发现在verify运行至一个段间跳转时出现异常。

 

image-20211202015328030

 

反复调试后,最终发现突破点在于其进行原跳转前,32位至64位模式的切换。

 

只不过在这里的表现形式与往常有所不同

 

image-20211202015704067

 

关于32位程序至64位程序的切换,可以参考文章

 

CTF中32位程序调用64位代码的逆向方法 - 安全客,安全资讯平台 (anquanke.com)

 

知晓其模式切换后,强制指定PE64标识

 

image-20211202020011428

 

放入IDA,为方便定位函数,Rebase Segment,基址设为0

 

image-20211202020136673

 

通过调试得到调用函数地址,减去基址后得到偏移量0x146f0

 

image-20211202020307140

 

定位函数,得到伪代码后,看到了函数调用

 

image-20211202020532995

 

汇编下形式为call rdi

 

image-20211202020620998

 

翻找之后能够知道rdi来自于指令mov rdi, [rsp+arg_0]

 

向上分析

 

image-20211202020820511

 

确定函数偏移为0x145AC

 

简单分析伪代码,结合代码复用,能够确定其check逻辑如下

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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
__int64 __fastcall sub_145AC(char *a1, __int64 a2)
{
  unsigned int v4; // edx
  char v5; // al
  __int64 v6; // rcx
  int v7; // edx
  char v8; // al
  char *v9; // rcx
  __int64 v10; // rax
  unsigned int v11; // edx
  char v12; // al
  __int64 v13; // rcx
  char v14; // cl
  __int64 v15; // r8
  int *v16; // rax
  char v17; // al
  __int64 v18; // rcx
  char v19; // cl
  __int64 v20; // r8
  int *v21; // rax
  int v23; // [rsp+4h] [rbp-3Ch] BYREF
  char v24; // [rsp+8h] [rbp-38h]
  __int16 v25; // [rsp+9h] [rbp-37h]
  char v26; // [rsp+Bh] [rbp-35h]
  unsigned int v27; // [rsp+Ch] [rbp-34h]
  char v28[48]; // [rsp+10h] [rbp-30h] BYREF
 
  v27 = 44;
  v4 = 0;
  *(__m128i *)v28 = _mm_load_si128((const __m128i *)&xmmword_14408);
  *(__m128i *)&v28[32] = _mm_load_si128((const __m128i *)&xmmword_143F8);
  *(__m128i *)&v28[16] = _mm_load_si128(xmmword_14418);
  do
  {
    v5 = v4 - 52;
    v6 = v4++;
    v28[v6] ^= v5;
  }
  while ( v4 < v27 );                           // 还原base64编码串
  v28[v27] = 0;
  v7 = 0;
  v8 = *a1;
  if ( *a1 )
  {
    v9 = a1;
    do
    {
      if ( v8 != v9[v28 - a1] )                 // strcmp
        break;
      ++v9;
      ++v7;
      v8 = *v9;
    }
    while ( *v9 );
  }
  v10 = v7;
  v11 = 0;
  v24 = -48;
  if ( a1[v10] == v28[v10] )                    // bingo
  {
    v23 = 0x78063019;                           // 正确
    v25 = 0;
    v26 = 0;
    do
    {
      v12 = v11 - 52;
      v13 = v11++;
      *((_BYTE *)&v23 + v13) ^= v12;
    }
    while ( v11 < 4 );
    v24 = 0;
    v14 = v23;
    if ( (_BYTE)v23 )
    {
      v15 = a2 - (_QWORD)&v23;
      v16 = &v23;
      do
      {
        *((_BYTE *)v16 + v15) = v14;
        v16 = (int *)((char *)v16 + 1);
        v14 = *(_BYTE *)v16;
      }
      while ( *(_BYTE *)v16 );
    }
  }
  else
  {
    v23 = 0x3C002078;                           // 错误
    v25 = 0;
    v26 = 0;
    do
    {
      v17 = v11 - 52;
      v18 = v11++;
      *((_BYTE *)&v23 + v18) ^= v17;
    }
    while ( v11 < 4 );
    v24 = 0;
    v19 = v23;
    if ( (_BYTE)v23 )
    {
      v20 = a2 - (_QWORD)&v23;
      v21 = &v23;
      do
      {
        *((_BYTE *)v21 + v20) = v19;
        v21 = (int *)((char *)v21 + 1);
        v19 = *(_BYTE *)v21;
      }
      while ( *(_BYTE *)v21 );
    }
  }
  return 0i64;
}

简单异或还原明文验证猜想

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
"""
v23 = 0x78063019;
    v25 = 0;
    v26 = 0;
    do
    {
      v12 = v11 - 52;
      v13 = v11++;
      *((_BYTE *)&v23 + v13) ^= v12;
    }
    while ( v11 < 4 );
"""
 
v11 = 0
v23 = bytearray(int.to_bytes(0x78063019, length=4, byteorder="little"))
 
while 1:
    v12 = v11 - 52
    v13 = v11
    v11 += 1
    v23[v13] ^= v12 & 0xff
 
    if v11 >= 4:
        break
print(v23.decode('gbk'))
# 正确

最终解题脚本如下

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
import base64
 
"""
  v27 = 44;
  v4 = 0;
  *(__m128i *)v28 = _mm_load_si128((const __m128i *)&xmmword_15408);
  *(__m128i *)&v28[32] = _mm_load_si128((const __m128i *)&xmmword_153F8);
  *(__m128i *)&v28[16] = _mm_load_si128(xmmword_15418);
  do
  {
    v5 = v4 - 52;
    v6 = v4++;
    v28[v6] ^= v5;
  }
  while ( v4 < v27 );
  v28[v27] = 0;
"""
 
xmmArr = [0x0B3E38188BB9CBA9DBAFFB697ABA2948B, 0x0BFDBD9AAD6D4BCA1878490B0B5AE858C, 0x0F8D6D7A7BAB89480B78A94B9AE]
v27 = 44
v28 = b''
for xmm in xmmArr:
    v28 += int.to_bytes(xmm, length=16, byteorder="little")
v28 = bytearray(v28)
 
v4 = 0
while 1:
    v5 = v4 - 52
    v6 = v4
    v4 += 1
    v28[v6] ^= v5 & 0xff
 
    if v4 >= v27:
        break
v28[v27] = 0
print(v28)
cipherText = v28.decode()
 
baseMaps = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
newMaps = "prvo9CHSJOcPIb6xRVUXQz0qBGDE72LNZduaefYT5K_8-4FAhlimjkngt1yMWs3w!"
 
cipherText = cipherText.translate(cipherText.maketrans(newMaps, baseMaps))
print(base64.b64decode(cipherText.encode('utf-8')).decode())

[2022夏季班]《安卓高级研修班(网课)》月薪两万班招生中~

最后于 2021-12-2 02:39 被sunfishi编辑 ,原因:
收藏
点赞3
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回