KCTF 2021秋季赛 第七题 声名远扬
Contents
文章发布至看雪《KCTF 2021秋季赛 第七题 声名远扬》
起点
- 32位,无保护,ida搜索字符串没有看到错误输出,没有明显的函数处理过程
- 发现Duilib写的,下载源码,编译lib,https://github.com/duilib/duilib,pct,sigmake制作签名文件,应用之~
- 识别了500多个还可以了,阅读源码,跟踪按钮事件,CNotifyPump::NotifyPump->CNotifyPump::LoopDispatch,进而跟踪到按钮消息处理函数sub_89D2D0。
- 分析出大致流程如下:
int __userpurge sub_89D2D0@<eax>(int a1@<ecx>, int a2@<ebx>, int a3@<edi>, int a4@<esi>, int a5)
{
v13 = a1;
result = a1;
if ( *(_DWORD *)(a1 + 1384) == *(_DWORD *)(a5 + 136) )
{
//...
v10 = sub_89E530(a1a, a2a); //编码函数
//...
sub_89D600(&v15, a2, a3, a4, v7, (int)v16); //校验函数
(*(void (__thiscall **)(_DWORD, char *))(**(_DWORD **)(v13 + 1388) + 44))(*(_DWORD *)(v13 + 1388), v16); //结果提示
}
return result;
}
分析
- 关键代码1(编码函数),一个简单的类似base64的编码,这里我想的是直接通过ida脚本去把表提出来。
for ( i = 0; ; i += v11 )
{
v2 = get_len(a2);
v11 = v2 - i;
v27 = '@';
Src = *(_BYTE *)sub_89E970(v30, (int)&v27); //从编码表中替换
v26 = 64;
v32 = *(_BYTE *)sub_89E970(v30, (int)&v26);
v25 = 64;
v33 = *(_BYTE *)sub_89E970(v30, (int)&v25);
v24 = 0x40;
v34 = *(_BYTE *)sub_89E970(v30, (int)&v24);
if ( !v11 )
break;
if ( v11 == 1 )
{
v23 = ((int)*(unsigned __int8 *)char_at(i) >> 2) & 0x3F; //char_at是std::string.at
Src = *(_BYTE *)sub_89E970(v30, (int)&v23);
v22 = (16 * *(_BYTE *)char_at(i)) & 0x3F;
v32 = *(_BYTE *)sub_89E970(v30, (int)&v22);
v21 = 64;
v33 = *(_BYTE *)sub_89E970(v30, (int)&v21);
v20 = 64;
v34 = *(_BYTE *)sub_89E970(v30, (int)&v20);
}
else if ( v11 == 2 )
{
v19 = ((int)*(unsigned __int8 *)char_at(i) >> 2) & 0x3F;
Src = *(_BYTE *)sub_89E970(v30, (int)&v19);
v3 = 16 * *(_BYTE *)char_at(i);
v18 = (((int)*(unsigned __int8 *)char_at(i + 1) >> 4) | v3) & 0x3F;
v32 = *(_BYTE *)sub_89E970(v30, (int)&v18);
v17 = (4 * *(_BYTE *)char_at(i + 1)) & 0x3F;
v33 = *(_BYTE *)sub_89E970(v30, (int)&v17);
v16 = 64;
v34 = *(_BYTE *)sub_89E970(v30, (int)&v16);
}
else
{
v15 = ((int)*(unsigned __int8 *)char_at(i) >> 2) & 0x3F;
Src = *(_BYTE *)sub_89E970(v30, (int)&v15);
v4 = (16 * *(_BYTE *)char_at(i)) & 0x3F;
v14 = (((int)*(unsigned __int8 *)char_at(i + 1) >> 4) & 0x3F | v4) & 0x3F;
v32 = *(_BYTE *)sub_89E970(v30, (int)&v14);
v5 = 4 * *(_BYTE *)char_at(i + 1);
v13 = (((int)*(unsigned __int8 *)char_at(i + 2) >> 6) | v5) & 0x3F;
v33 = *(_BYTE *)sub_89E970(v30, (int)&v13);
v12 = *(_BYTE *)char_at(i + 2) & 0x3F;
v34 = *(_BYTE *)sub_89E970(v30, (int)&v12);
v11 = 3;
}
- 写个调试脚本如下:
//大致流程是这样,不过调试的时候连续执行step_over我再ida里一直没成功,没时间看了,有知道的兄弟请告知.
//因为本身循环次数不多,通过条件断点输出和多次run脚本把表打了出来
import time
for i in range(64):
edx = idc.get_reg_value("EBP")-0x39
idc.patch_dbg_byte(edx,i)
idc.set_reg_value(0x0089E5AF,'EIP')
idaapi.step_over()
idaapi.step_over()
idaapi.step_over()
idaapi.step_over()
time.sleep(0.1)
eax = idc.get_reg_value("EAX")
code = idc.read_dbg_byte(eax)
print(hex(code), end='')
//编码表[0x70,0x72,0x76,0x6f,0x39,0x43,0x48,0x53,0x4a,0x4f,0x63,0x50,0x49,0x62,0x36,0x78,0x52,0x56,0x55,0x58,0x51,0x7a,0x30,0x71,0x42,0x47,0x44,0x45,0x37,0x32,0x4c,0x4e,0x5a,0x64,0x75,0x61,0x65,0x66,0x59,0x54,0x35,0x4b,0x5f,0x38,0x2d,0x34,0x46,0x41,0x68,0x6c,0x69,0x6d,0x6a,0x6b,0x6e,0x67,0x74,0x31,0x79,0x4d,0x57,0x73,0x33,0x77,0x21]
- 关键代码2(校验函数),0089D8A2:call fword ptr [ebp+var_C] 校验代码放在了x64里执行,所以直接上windbg
- 看了看代码,一个简单的比较,bp 00895606 下断点,da rbp-30打印输出,直接拿最后的编码比较
- GYldGg-iIoJlPX9hPXpjPqfdEY21B01TBTzeGqfKNR!!
求解
- 写了个简单的z3求解
from z3 import *
table = [0x70,0x72,0x76,0x6f,0x39,0x43,0x48,0x53,0x4a,0x4f,0x63,0x50,0x49,0x62,0x36,0x78,0x52,0x56,0x55,0x58,0x51,0x7a,0x30,0x71,0x42,0x47,0x44,0x45,0x37,0x32,0x4c,0x4e,0x5a,0x64,0x75,0x61,0x65,0x66,0x59,0x54,0x35,0x4b,0x5f,0x38,0x2d,0x34,0x46,0x41,0x68,0x6c,0x69,0x6d,0x6a,0x6b,0x6e,0x67,0x74,0x31,0x79,0x4d,0x57,0x73,0x33,0x77,0x21]
A = Array('A', IntSort(), IntSort())
i = 0
for elem in table:
A = Store(A, i, elem)
i = i + 1
permute = lambda k:Select(A, BV2Int(k))
flag_len = 31
x = [BitVec('u%d'%i,8) for i in range(x_len)]
s = Solver()
i = 0
v = 0
xxx = []
while True:
k = x_len - i
if (k == 0):
break
if k == 1:
pos = (x[i] >> 2) & 0x3f
m0 = permute(pos)
pos = (16 * x[i]) & 0x3f
m1 = permute(pos)
pos = BitVecVal(0x40, 8)
m2 = permute(pos)
m3 = permute(pos)
elif k == 2:
pos = (x[i] >> 2) & 0x3f
m0 = permute(pos)
pos = ((x[i+1] >> 4) | (16 * x[i])) & 0x3f
m1 = permute(pos)
pos = (4 * x[i+1]) & 0x3f
m2 = permute(pos)
pos = BitVecVal(0x40, 8)
m3 = permute(pos)
else:
pos = (x[i] >> 2) & 0x3f
m0 = permute(pos)
pos = ((x[i+1] >> 4) & 0x3f | (16 * x[i])) & 0x3f
m1 = permute(pos)
pos = ((x[i+2] >> 6) | (4 * x[i+1])) & 0x3f
m2 = permute(pos)
pos = x[i+2] & 0x3f
m3 = permute(pos)
k = 3
xxx += [m0,m1,m2,m3]
i += k
cip = 'GYldGg-iIoJlPX9hPXpjPqfdEY21B01TBTzeGqfKNR!!'
j = 0
for t in xxx:
s.add(t==ord(cip[j]))
j+=1
res = s.check()
if res == z3.sat:
print(s.model())
model = s.model()
flag = ''
for i in range(0,flag_len):
flag += chr(model[x[i]].as_long().real)
print(flag)
else:
print('oh,no')
- 最后得到:flag{2021-10-04-yangyangbudeyi}