网鼎杯-2020-青龙组-jocker
萧禾财 Lv4

观察程序

用PE 探查一下

image-20220713160353522

是32位程序 无壳 用IDA 打开

这里如果你IDA版本较高 可以直接F5生成伪代码

此时版本为 IDA Pro 7.7

image-20220713163059797

但是有些较低的不行 所以我讲一下 无法直接F5的情况

这里无法生成伪代码是因为 栈不平衡

勾选上 栈指针 Options->Generral->Stack pointer

image-20220713163615156image-20220713163658449

就可以看到栈指针的情况了

image-20220713163904397

有两处栈不平衡情况

tips:在call函数最后 retn指令会使得栈指针恢复被调用前的数值

使用快捷键 Alt + k 修改新旧sp指针的差值为0

修改第一处:

image-20220713164446147

image-20220713164525939

修改第二处:

image-20220713164633571

image-20220713164700906

F5查看伪代码

看到一个VirtualProtect函数 没有遇到过 查一下

image-20220714100921528

VirtualProtect函数__virtualprotectex

是一个加密函数 加密encrypt函数 暂时还用不到 跳过

image-20220713165011508

有两个函数 wrong 和 omg 使用了 str

wrong:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
char *__cdecl wrong(char *a1)
{
char *result; // eax
int i; // [esp+Ch] [ebp-4h]

for ( i = 0; i <= 23; ++i )
{
result = &a1[i];
if ( (i & 1) != 0 )
a1[i] -= i;
else
a1[i] ^= i;
}
return result;

omg:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
int __cdecl omg(char *a1)
{
int v2[24]; // [esp+18h] [ebp-80h] BYREF
int i; // [esp+78h] [ebp-20h]
int v4; // [esp+7Ch] [ebp-1Ch]

v4 = 1;
qmemcpy(v2, &byte_4030C0, sizeof(v2));
for ( i = 0; i <= 23; ++i )
{
if ( a1[i] != v2[i] )
v4 = 0;
}
if ( v4 == 1 )
return puts("hahahaha_do_you_find_me?");
else
return puts("wrong ~~ But seems a little program");
}

写一下脚本

image-20220714091514207

错的flag 题目没有那么简单

再往后看

image-20220714091651997

有个for循环 循环了187次 有点像在还原程序

image-20220714091925228

encrypt函数打不开 报错 查看汇编代码 大多还是数据 基本上确定了for循环在进行解密

动态调试 去壳

这里用x32debug 动态调试

image-20220714092913356

一步一步调试 找到主函数下的for循环

按下F4 运行到for循环的下一步

按下F7进入encrypt函数内部

image-20220714093903949

image-20220714093635181

有正确的汇编代码

将解密完的程序dump 出来

image-20220714093223087

image-20220714093257374

将dump出的新程序拖进IDA

image-20220714094202011

入口变成了 start 这是dump程序默认更改的 点击start函数名 按X查看调用 易找到原来的main函数

image-20220714094518893

还原的encrypt函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int __cdecl start(int a1)
{
int v2[19]; // [esp+1Ch] [ebp-6Ch] BYREF
int v3; // [esp+68h] [ebp-20h]
int i; // [esp+6Ch] [ebp-1Ch]

v3 = 1;
qmemcpy(v2, &unk_403040, sizeof(v2));
for ( i = 0; i <= 18; ++i )
{
if ( (char)(*(_BYTE *)(i + a1) ^ aHahahahaDoYouF[i]) != v2[i] )
{
puts("wrong ~");
v3 = 0;
exit(0);
}
}
puts("come here");
return v3;
}

还原的finally函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int __cdecl sub_40159A(int a1)
{
unsigned int v1; // eax
char v3[9]; // [esp+13h] [ebp-15h] BYREF
int v4; // [esp+1Ch] [ebp-Ch]

strcpy(v3, "%tp&:");
v1 = time(0);
srand(v1);
v4 = rand() % 100;
v3[6] = 0;
*(_WORD *)&v3[7] = 0;
if ( (v3[(unsigned __int8)v3[5]] != *(_BYTE *)((unsigned __int8)v3[5] + a1)) == v4 )
return puts("Really??? Did you find it?OMG!!!");
else
return puts("I hide the last part, you will not succeed!!!");
}

encrypt函数加密思路

将a1的前19为与“hahahaha_do_you_find_me?”异或 其中a1就是我们的输入字符串

image-20220714095413106

finally函数的加密思路

image-20220714095754481

函数逻辑 看了很久不是很懂 再加上 还有随机数的干扰 这里就无法猜出它的解密方法

只能退一步 猜还是利用了encrypt函数的异或

因为 flag{…..} 最后一个必定是** }** 可以知道是 71 与 ”:” 异或

进一步猜 前四字符都是 和 71异或

解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
store = [0x0E,    0x0D,    0x09,
0x06, 0x13,
0x05, 0x58, 0x56, 0x3E, 0x06, 0x0C, 0x3C, 0x1F, 0x57, 0x14, 0x6B, 0x57, 0x59, 0x0D]
key = [0x68, 0x61, 0x68, 0x61, 0x68, 0x61, 0x68, 0x61, 0x5F, 0x64,
0x6F, 0x5F, 0x79, 0x6F, 0x75, 0x5F, 0x66, 0x69, 0x6E, 0x64,
0x5F, 0x6D, 0x65, 0x3F]
input1 = []
for i in range(len(store)):
input1.append(store[i] ^ key[i])
print(chr(input1[i]), end="")

key2 = [37, 116, 112, 38, 58]
for i in range(len(key2)):
print(chr(key2[i] ^ 71), end="")

#flag{d07abccf8a410cb37a}

参考WP:[(60条消息) 网鼎杯 2020 青龙组]jocker(详解)_ST4RBOY的博客-CSDN博客_网鼎杯jocker

  • 本文标题:网鼎杯-2020-青龙组-jocker
  • 本文作者:萧禾财
  • 创建时间:2022-07-13 16:02:21
  • 本文链接:https://ipartmentxhc.github.io/2022/07/13/青龙组-jocker/
  • 版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!