好友
阅读权限30
听众
最后登录1970-1-1
|
姚小宝
发表于 2018-11-25 22:34
本帖最后由 yechen123 于 2019-2-9 15:55 编辑
这两天比赛挺多了
利用空余时间看了下题目
发个帖子记录一下
上传题目 别的两道题目没有存有。。
nctf.rar
(56.01 KB, 下载次数: 14)
南邮杯的 题目大部分都不怎么难
0.后门后门后门
没啥技术含量
hereisyourflag(); 里边就是flag
1.基本操作
看代码
[Asm] 纯文本查看 复制代码 __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
__int64 result; // rax
puts("Input flag:");
__isoc99_scanf("%64s", &byte_601100);
dword_601064 = 0;
sub_400666(0LL);
if ( !strcmp(&s1, "bcec8d7dcda25d91ed3e0b720cbb6cf202b09fedbc3e017774273ef5d5581794") )
{
memset(&s1, 0, 0x80uLL);
dword_601064 = 0;
sub_4006BE(0LL, 0LL);
if ( !strcmp(&s1, "7d8dcdcaed592e1dcb07e02c36bcb2f0bf9e0bdcb0e13777237e25fd48515974") )
printf("TQL! TQL! flag: nctf{%s}\n", &byte_601100);
else
puts("Emmmm.....");
result = 0LL;
}
else
{
puts("GG!");
result = 0LL;
}
return result;
}
输入字符串后经过变换后
再跟字符串比较得到就是flag
看变换函数
[Asm] 纯文本查看 复制代码 __int64 __fastcall sub_400666(signed int a1)
{
int v1; // eax
__int64 result; // rax
if ( a1 <= 63 )
{
v1 = dword_601064++;
*(&s1 + v1) = byte_601100[a1];
sub_400666((unsigned int)(2 * a1 + 1));
result = sub_400666((unsigned int)(2 * (a1 + 1)));
}
return result;
}
懒得写脚本
直接按照ascii码生成一串有序字符串
然后再看如何变换
[Asm] 纯文本查看 复制代码 0123456789:;<=>?
@ABCDEFGHIJKLMNO
PQRSTUVWXYZ[\\]^
_`abcdefghijklmn
变换之后
[Asm] 纯文本查看 复制代码 0137?OnP@QR8ASTB
UV49CWXDYZ:E[\F\
]25;G^_H`a<IbcJd
e6=KfgLhi>MjkNlm
看变换之后的字符串
[Asm] 纯文本查看 复制代码 bcec8d7dcda25d91
ed3e0b720cbb6cf2
02b09fedbc3e0177
74273ef5d5581794
逆推得到flag
[Asm] 纯文本查看 复制代码 bc2e3b4c2eb03258c5102bf9de77f57dddad9edb70c6c20febc01773e5d81947
2.Some Boxes
[Asm] 纯文本查看 复制代码 __int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
signed int v3; // eax
int i; // [rsp+8h] [rbp-8h]
int len; // [rsp+Ch] [rbp-4h]
sub_4007D9();
read(0, buf, 1000uLL);
len = strlen(buf);
for ( i = 0; i < len; ++i )
{
v3 = buf[i];
if ( v3 == 52 ) // 4
{
sub_400C62(); //
goto LABEL_14;
}
if ( v3 > 52 )
{
if ( v3 == 53 ) // 5
{
sub_400D5D(); // 下
goto LABEL_14;
}
if ( v3 == 87 ) // W
{
sub_400B67(); // 上
goto LABEL_14;
}
}
else if ( v3 == 48 ) // 0
{
sub_400A6C(); // 右
goto LABEL_14;
}
puts("error!");
LABEL_14:
sub_400E58();
}
return 0LL;
}
题目显示play a game
题目会允许输入1000个字符
但是只能是4 5 W 0 是控制方向的 否则报错
先看看判断成功的函数
[Asm] 纯文本查看 复制代码 __int64 sub_400E58()
{
__int64 result; // rax
result = (unsigned __int8)byte_6020A0[16 * flag_one_hang + flag_one_lie];
if ( (_BYTE)result == 20 )
{
result = (unsigned __int8)byte_6020A0[16 * flag_two_hang + flag_two_lie];
if ( (_BYTE)result == 20 )
{
puts(&byte_4018EE);
system("cat flag");
exit(0);
}
}
return result;
}
最终要这两个值返回20
看一下迷宫
[Asm] 纯文本查看 复制代码 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08
08 08 00 00 08 08 08 08 08 08 08 08 00 00 08 08
08 08 00 00 00 00 00 08 00 00 00 00 00 00 08 08
08 08 00 08 08 00 00 00 00 00 08 08 08 00 00 08
08 08 00 08 08 00 08 08 00 00 08 14 00 00 00 08
08 08 00 00 08 00 00 00 00 00 08 08 08 08 08 08
08 08 00 00 08 08 08 08 00 08 08 08 08 08 08 08
08 08 00 08 08 08 08 08 00 08 08 00 00 00 08 08
08 00 00 08 14 08 00 08 00 00 00 00 08 00 08 08
08 00 00 00 00 00 00 00 00 08 08 08 08 00 08 08
08 00 00 00 00 08 08 08 00 08 00 00 00 00 08 08
08 08 08 08 08 08 08 08 08 08 08 08 08 08 08 08
在看一下方向控制函数 看看是一个什么游戏
[Asm] 纯文本查看 复制代码 __int64 sub_400C62()
{
__int64 result; // rax
result = (unsigned __int8)byte_6020A0[16 * hang - 1 + lie];
if ( (_BYTE)result != 8 ) // 如果是通路
{
if ( lie - 1 != flag_one_lie || hang != flag_one_hang )//
{
if ( lie - 1 == flag_two_lie && hang == flag_two_hang )// 需要和2冲突
{
if ( byte_6020A0[16 * flag_two_hang - 1 + flag_two_lie] == 8 )// 如果二左移撞墙
++lie; // 那主键就不移动
else
sub_400A1C(&flag_two_lie); // 否则二左移
}
}
else if ( byte_6020A0[16 * flag_one_hang - 1 + flag_one_lie] == 8 )// 如果此时一左移撞墙
{
++lie; // 主键不移动
}
else // 如果冲突了
{
sub_400A1C(&flag_one_lie); // one_lie左移
}
result = (unsigned int)(lie-- - 1);
}
return result;
}
说明 控制自己是主要的 另外还有两个值也在迷宫中
当主键移动时候 如果主键碰到强 也就是08 那就不移动 如果碰到另外两个值
如果另外两个值向着相同方向移动会碰墙 那就不移动
最终要使两个值推到0x14的方格中
那就是推箱子了
看看初始化位置
[Asm] 纯文本查看 复制代码 void sub_400796()
{
lie = 8;
hang = 5;
flag_one_lie = 5;
flag_one_hang = 2;
flag_two_lie = 8;
flag_two_hang = 7;
}
ok
4代表左 5是下 0是右 W是上
得到
WW44W444W45555555450050W0000WWWWW5444WW00050W4W0000W0550544
4.VM VM题目基本没怎么做过 只是了解一点加密过程 可能会有错误
一个vm程序
看主要代码[Asm] 纯文本查看 复制代码 __int64 sub_400DAB()
{
int v1; // [rsp+Ch] [rbp-54h]
void *liuchengbiao; // [rsp+10h] [rbp-50h]
char *v3; // [rsp+18h] [rbp-48h]
int *v4; // [rsp+20h] [rbp-40h]
int *v4_one; // [rsp+28h] [rbp-38h]
int *v4_two; // [rsp+30h] [rbp-30h]
int *v4_three; // [rsp+38h] [rbp-28h]
char *v8; // [rsp+40h] [rbp-20h]
char *v9; // [rsp+48h] [rbp-18h]
_DWORD *v10; // [rsp+50h] [rbp-10h]
unsigned __int64 v11; // [rsp+58h] [rbp-8h]
v11 = __readfsqword(0x28u);
v1 = 0; //
liuchengbiao = malloc(0x200uLL); // 1653010
v3 = (char *)malloc(0x1400uLL); // 1653220
memset(&v4, 0, 48uLL);
memcpy(liuchengbiao, &unk_6021C0, 0x190uLL);
v4 = (int *)malloc(0x38uLL); // 1654630
v4_one = v4 + 1; // 可能是寄存器
v4_two = v4 + 2;
v4_three = v4 + 3;
v8 = v3 + 5120;
v9 = v3 + 5120;
v10 = liuchengbiao;
LABEL_26:
while ( *v10 )
{
switch ( (char)*v10 )
{
case 8:
sub_40082B((&v4)[v10[1] - 1], v10[2], &v10);// 把v10[2]的值放到 第v10[1]-1块寄存器
goto LABEL_26;
case 9:
sub_40096D((&v4)[v10[1] - 1], (_DWORD **)&v8, &v10);// v8值赋值给寄存器
goto LABEL_26;
case 0xA:
sub_400927((&v4)[v10[1] - 1], &v8, &v10);// 寄存器值复制给v8 两个相差不远
goto LABEL_26;
case 0xB:
input_flag(v4, &v10); // flag存到寄存器中 每次就存一个字符
goto LABEL_26;
case 0xC:
sub_4009E5(v4, &v10);
goto LABEL_26;
case 0xD:
sub_400B5D(&v1, (&v4)[v10[1] - 1], (&v4)[v10[2] - 1], &v10);// 比较两个操作寄存器大小 并操作v1的值 下一个函数(F)会根据v1的值跳转
goto LABEL_26;
case 0xE:
sub_400A34(&v10, v10[1], (__int64)liuchengbiao);// 根据v10[1]决定跳转到liucjhengbiao哪个个值
goto LABEL_26;
case 0xF:
sub_400AAF(v1, (signed __int64 *)&v10, v10[1], (__int64)liuchengbiao);// 跳转
goto LABEL_26;
case 0x10:
sub_400A61(v1, (signed __int64 *)&v10, v10[1], (__int64)liuchengbiao);
goto LABEL_26;
case 0x11:
sub_400AFD((&v4)[v10[1] - 1], &v10); // 寄存器自增
goto LABEL_26;
case 0x12:
sub_400B2D((&v4)[v10[1] - 1], &v10);
goto LABEL_26;
case 0x13:
sub_400C0E((&v4)[v10[1] - 1], v10[2], &v10);// 让寄存器一加上流程表的值
goto LABEL_26;
case 0x14:
sub_400C43((&v4)[v10[1] - 1], (&v4)[v10[2] - 1], &v10);// 寄存器1减去寄存器2
goto LABEL_26;
case 0x15:
sub_400C7C((&v4)[v10[1] - 1], v10[2], &v10);// 让寄存器的值^上流程表的值
goto LABEL_26;
case 0x16:
sub_400CB1((&v4)[v10[1] - 1], (&v4)[v10[2] - 1], &v10);
goto LABEL_26;
case 0x17:
sub_400CEA((&v4)[v10[1] - 1], (&v4)[v10[2] - 1], &v10);
goto LABEL_26;
case 0x19:
sub_400858((&v4)[v10[1] - 1], (&v4)[v10[2] - 1], &v10);// 寄存器赋值
goto LABEL_26;
case 0x1A:
sub_400889((&v4)[v10[1] - 1], (unsigned __int64)(&v4)[v10[2] - 1], &v10);
goto LABEL_26;
case 0x1B:
sub_4008BA((&v4)[v10[1] - 1], (unsigned int *)(&v4)[v10[2] - 1], &v10);// 把寄存器二的值作为地址指向的值给寄存器1
goto LABEL_26;
case 0x1C:
sub_4008EF((unsigned int *)(&v4)[v10[1] - 1], (&v4)[v10[2] - 1], &v10);// 把寄存器一的值赋值给第二个寄存器所指向的地址
goto LABEL_26;
case 0x1D:
sub_400D23((&v4)[v10[1] - 1], v10[2], &v10);// 让第一个寄存器乘以v10[2]也就是流程表的值
goto LABEL_26;
case 0x64:
return sub_400D59((__int64)v9);
default:
sub_400A17(&v10);
break;
}
}
return 1LL;
}
memcpy(liuchengbiao, &unk_6021C0, 0x190uLL); 申请流程表 程序会根据里边的值加密程序
[Asm] 纯文本查看 复制代码 v4_one = v4 + 1; // 可能是寄存器
v4_two = v4 + 2;
v4_three = v4 + 3;
v8 = v3 + 5120;
v4_one 这几块内存 模拟 寄存器 其中v4_three会储存一个常数0x46 也就是输入的字符串长度
v8指的是一块内存 存储输入的字符串 一个字符一个字符的存 每次存完就会自动减四
看流程表
[Asm] 纯文本查看 复制代码 08 00 00 00 01 00 00 00 00 00 00 00 08 00 00 00
03 00 00 00 46 00 00 00 0E 00 00 00 15 00 00 00
0A 00 00 00 01 00 00 00 09 00 00 00 02 00 00 00
0B 00 00 00 0A 00 00 00 01 00 00 00 0A 00 00 00
02 00 00 00 09 00 00 00 01 00 00 00 11 00 00 00
01 00 00 00 0D 00 00 00 01 00 00 00 03 00 00 00
0F 00 00 00 08 00 00 00 08 00 00 00 01 00 00 00
00 00 00 00 08 00 00 00 03 00 00 00 47 00 00 00
0E 00 00 00 46 00 00 00 0A 00 00 00 01 00 00 00
1A 00 00 00 02 00 00 00 06 00 00 00 1D 00 00 00
01 00 00 00 04 00 00 00 14 00 00 00 02 00 00 00
01 00 00 00 19 00 00 00 01 00 00 00 02 00 00 00
1B 00 00 00 01 00 00 00 01 00 00 00 1D 00 00 00
01 00 00 00 6E 00 00 00 13 00 00 00 01 00 00 00
63 00 00 00 15 00 00 00 01 00 00 00 74 00 00 00
13 00 00 00 01 00 00 00 66 00 00 00 1C 00 00 00
02 00 00 00 01 00 00 00 09 00 00 00 01 00 00 00
11 00 00 00 01 00 00 00 0D 00 00 00 01 00 00 00
03 00 00 00 0F 00 00 00 22 00 00 00 64 00 00 00
其中比较重要的是
[Asm] 纯文本查看 复制代码 0D 00 00 00 01 00 00 00 03 00 00 00
这样的流程
0d表示比较寄存器
当字符计数寄存器大于另一个寄存器时候 跳转 也就是判断读取长度 要读够0x46个字符
然后后边的
0F 00 00 00 08 00 00 00
跳转
读取完了跳转到后边加密
当到了后边加密时候
会从内存中一个字符地读数据 放到最左边的寄存器中加密
加密完了在放回去 最右边的是一个计数寄存器 判断循环次数
而上边的内存就是存储输入的数据
再看看最后的判断代码
[Asm] 纯文本查看 复制代码 signed __int64 __fastcall sub_400D59(__int64 a1)
{
signed int i; // [rsp+14h] [rbp-4h]
for ( i = 0; i <= 69; ++i )
{
if ( dword_6020A0[i] != *(_DWORD *)(4LL * i - 280 + a1) )
return 0LL;
}
return 1LL;
}
最后就是比较了 读出最后加密过的代码
[Asm] 纯文本查看 复制代码 i = [14035, 11007, 10955, 11157, 11157, 11157, 5791, 6253, 6359, 5649, 6359, 11157, 11299, 11433, 5649, 5649, 6359, 11007, 6217, 6395, 10955, 10865, 5941, 6359, 5649, 10955, 5597, 6359, 11299, 5791, 5597, 11157, 5791, 5483, 6253, 11007, 5649, 5649, 5597, 11007, 11299, 10955, 5597, 5597, 6253, 6217, 11157, 5483, 5941, 6395, 6395, 10865, 11007, 5941, 11299, 5597, 6359, 10865, 6359, 6359, 11299, 11007, 5483, 11299, 5791, 13743, 11433, 12981, 11007, 12345]
刚上面的加密算法是
((输入的值*6e)+0x63)^0x74+0x66
不建议逆推 涉及小数点问题
直接爆破
[Asm] 纯文本查看 复制代码 i = [14035, 11007, 10955, 11157, 11157, 11157, 5791, 6253, 6359, 5649, 6359, 11157, 11299, 11433, 5649, 5649, 6359, 11007, 6217, 6395, 10955, 10865, 5941, 6359, 5649, 10955, 5597, 6359, 11299, 5791, 5597, 11157, 5791, 5483, 6253, 11007, 5649, 5649, 5597, 11007, 11299, 10955, 5597, 5597, 6253, 6217, 11157, 5483, 5941, 6395, 6395, 10865, 11007, 5941, 11299, 5597, 6359, 10865, 6359, 6359, 11299, 11007, 5483, 11299, 5791, 13743, 11433, 12981, 11007, 12345]
flags = ""
for q in range(len(i)):
for u in range(32, 126):
if (((((u*0x6e)+0x63)^0x74)+0x66)==i[q]):
flags += chr(u)
print flags
flag = ""
u = len(flags)
for u in range(len(flags)):
flag += flags[q-u]
print flag
得到最终flag
[Asm] 纯文本查看 复制代码 nctf{3e1ce77b70e4cb9941d6800aec022c813d03e70a274ba96c722fed72783dddac}
|
免费评分
-
查看全部评分
|