第一题:神秘来信
签到题,没什么好说的
if ( v4 < 7 && v14 == 51 && v13 == 53 && v12 == 51 && v11 + v10 + v9 == 149 )
判断成立即可
401353
第六题:消失的岛屿
main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
int v3; // eax
uint8_t bindata; // [esp+11h] [ebp-3Fh]
const char *v6; // [esp+48h] [ebp-8h]
char *v7; // [esp+4Ch] [ebp-4h]
__main();
printf("please enter Serial:");
scanf(" %s", &bindata);
if ( strlen((const char *)&bindata) > 0x31 )
puts("error");
v7 = (char *)calloc(1u, 0x400u);
v3 = strlen((const char *)&bindata);
base64_encode(&bindata, v7, v3);
v6 = "!NGV%,$h1f4S3%2P(hkQ94==";
if ( !strcmp("!NGV%,$h1f4S3%2P(hkQ94==", v7) )
puts("Success");
else
puts("Please Try Again");
free(v7);
system("pause");
return 0;
}
逻辑非常简单,将输入base64_encode之后与常量比较,看一眼base64_encode
没什么问题,很base64,进入charEncrypt
char __cdecl charEncrypt(int data)
{
int dataa; // [esp+18h] [ebp+8h]
dataa = aTuvwxtulmnopqr[data];
if ( dataa > 0x40 && dataa <= 0x5A )
return 0x9B - dataa;
if ( dataa > 0x60 && dataa <= 0x7A )
return dataa - 0x40;
if ( dataa > 0x2F && dataa <= 0x39 )
return dataa + 0x32;
if ( dataa == 0x2B )
return 0x77;
if ( dataa == 0x2F )
dataa = 0x79;
return dataa;
}
拿到table 'tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/'
看到table还经过了简单的变换,直接复制出来然后解base64就搞定了
import string
import base64
def fun(dataa):
if dataa > 0x40 and dataa <= 0x5A:
dataa = 0x9B - dataa
elif dataa > 0x60 and dataa <= 0x7A:
dataa = dataa - 0x40
elif dataa > 0x2F and dataa <= 0x39:
dataa = dataa + 0x32
elif dataa == 0x2B:
dataa = 0x77
elif dataa == 0x2F:
dataa = 0x79
return dataa
base64_table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
data = 'tuvwxTUlmnopqrs7YZabcdefghij8yz0123456VWXkABCDEFGHIJKLMNOPQRS9+/'
table = ''
cipher_text = '!NGV%,$h1f4S3%2P(hkQ94=='
for i in data:
table += chr(fun(ord(i)))
print table
key = base64.b64decode(cipher_text.translate(string.maketrans(table,base64_table)))
print key
KanXue2019ctf_st
第四题:达芬奇密码
ida32载入,找到入口函数
int __thiscall sub_401EA0(CWnd *this)
{
CWnd *v1; // esi
int v2; // eax
WCHAR String; // [esp+Ch] [ebp-310h]
char v5; // [esp+Eh] [ebp-30Eh]
char v6; // [esp+20Ch] [ebp-110h]
char v7; // [esp+20Dh] [ebp-10Fh]
DWORD v8; // [esp+30Ch] [ebp-10h]
CWnd *v9; // [esp+310h] [ebp-Ch]
int v10; // [esp+314h] [ebp-8h]
DWORD flOldProtect; // [esp+318h] [ebp-4h]
v1 = this;
v9 = this;
String = 0;
memset(&v5, 0, 0x1FEu);
v6 = 0;
memset(&v7, 0, 0xFFu);
CWnd::GetDlgItemTextW(v1, 1000, &String, 20);
if ( wcslen(&String) == 16 )
{
v2 = 0;
while ( !(*(&String + v2) & 0xFF00) )
{
*(&v6 + v2) = *((_BYTE *)&String + 2 * v2);
if ( ++v2 >= 16 )
{
v8 = 64;
flOldProtect = 0;
VirtualProtect(sub_4010E0, 0xD17u, 0x40u, &flOldProtect);
if ( GetLastError() )
return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
qmemcpy(sub_4010E0, &byte_5647B8, 0x330u);
VirtualProtect(sub_4010E0, 0xD17u, flOldProtect, &v8);
if ( !GetLastError() )
{
v10 = 0;
v10 = sub_4010E0();
if ( v10 == 1 )
return CWnd::MessageBoxW(v9, L"Congratulations! You are right!", 0, 0);
}
v1 = v9;
return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
}
}
}
return CWnd::MessageBoxW(v1, L"Wrong!", 0, 0);
}
程序逻辑十分简单,首先判断输入字符串长度是否为十六,之后将宽字符转换为字符然后调 VirtualProtect这个API,之后qmemcpy(sub_4010E0, &byte_5647B8, 0x330u);
将一段字符复制到sub_4010E0上然后继续用调 VirtualProtect这个API,进入if语句,调用sub_4010E0函数,返回值v10 = 1时正确
看似十分复杂,确实非常复杂,静态分析是不可能的,这辈子都不可能了,果断OD动态开调,发现真正对字符串加密就只有if语句中的sub_4010E0函数,即od中的TheDaVin.002A10E0
002A1FAA |. 8D85 F0FEFFFF lea eax,[local.68]
002A1FB0 50 push eax
002A1FB1 E8 2AF1FFFF call TheDaVin.002A10E0
002A1FB6 |. 8945 F8 mov [local.2],eax
菜鸡只能逐行看汇编代码,再转换为人类能看懂的语言
test = '???? ???? ???? ????'
assest (test.len == 16)
table = [0x16,0x96,0x8c,0xe3,0x81,0x98,0x6e,0x64,
0x84,0x08,0xdc,0x81,0xbe,0x4d,0x48,0x4f]
for i in range(8):
test[i] ^ table[i] -> [008FEFF4+i]
test[8+i] ^ table[8+i] -> [008EF00C+i]
&arr1 = 008FEFF4
&arr2 = 008EF00C
assest(arr1.len == 8 and arr2.len == 8)
008FF03C: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
ecx = 0
edi = 008FEFFC
edx = 008FF03C
bx = 0
for i in arr1:
for j in arr1:
ax = i*j
ax += bx
(ax & 0x00ff) -> [edi + ecx]
(ax >> 8) -> [edi + 0x08]
bx = [edi + 0x08]
ecx++
ecx = 0
eax = 0
for i in range(9):
eax += [edi + i] + [edx + i]
(eax & 0x00ff) -> [edx+i]
eax >>= 8
ecx = 0
edi++
edx++
ecx = 0
edi = 008FEFFC
edx = 008FF050
bx = 0
for i in arr2:
for j in arr2:
ax = i*j
ax += bx
(ax & 0x00ff) -> [edi + ecx]
(ax >> 8) -> [edi + 0x08]
bx = [edi + 0x08]
ecx++
ecx = 0
eax = 0
for i in range(9):
eax += [edi + i] + [edx + i]
(eax & 0x00ff) -> [edx+i]
eax >>= 8
ecx = 0
edi++
edx++
addr = 008FF050
ss = 008FF014
eax = 0
for i in range(0x11):
dx = [addr + i]
dx *= 0x07
eax += dx
(eax & 0x00ff) -> [ss + i]
eax >>= 8
eax -> [addr+0x11]
addl = 008FF03C
sa = 008FF028
dl = 0
for i in range(0x11):
cx = [ss + i]
si = [addl + i]
if i > 0:
assest(cx == si)
if i = 0:
assest(si == cx + 8)
# si -= cx
# #cx = dl
# #si -= cx
# ecx = si
# #cl -> [sa + i]
# (si & 0x0f) -> [sa + i]
# assest (ecx >= 0)
# if ecx < 0:
# dl = 1
# else:
# dl = 0
# assest ([sa] == 0x08)
# for i in range(1,11):
# assest ([sa+i] == 0x00)
仔细一看wtf,就是解一条方程
x^2 = 7 * y^2 + 8
嗯,双曲线方程,求64位整数解
将解和table异或一下就可以得到flag了(flag = x<<64 + y,小端序)
作为一个数学蒻鶸,这怎么解得出来呢!闲着没事干搜了一下椭圆曲线,发现可以根据小整数解组推出大整数解组?果断爆破
for i in range(0xffff):
for j in range(0xffff):
ax = i
bx = j
ax *= ax
bx *= bx
bx *= 7
if ax-bx == 8:
print hex(i),hex(j),hex(ax),hex(bx)
爆破得到这么几组整数解
0x6 0x2 0x24 0x1c
0x5a 0x22 0x1fa4 0x1f9c
0x59a 0x21e 0x1f60a4 0x1f609c
0x5946 0x21be 0x1f21bf24 0x1f21bf1c
蒻鶸怎么可能看得懂数学原理呢,自己动手找规律完事,依据两组小整数解组就可以理论上求出无穷大的整数解组
求解代码
x1 = 0x6
y1 = 0x2
x2 = 0x5a
y2 = 0x22
print hex(x1),hex(y1)
print hex(x2),hex(y2)
for i in range(3,17):
x3 = x2*0x10 - x1
y3 = y2*0x10 - y1
print hex(x3) , hex(y3)
x1,x2 = x2,x3
y1,y2 = y2,y3
可以得到63位和64位的解,和table异或一下
0x557f3b3b9e1a55a 0x2050988b2bd38de
0x552965d47892d506 0x20302760c38eb6fe
得到
0x61396b325a6d334c 0x4d4d443633613053
0x3147fd559b1e4310 0x4f484dbe81dc088d
排除第二组解,求出flag
'0x61396b325a6d334c'[2:].decode('hex')[::-1]+'0x4d4d443633613053'[2:].decode('hex')[::-1]
L3mZ2k9aS0a36DMM
ps:逆向方面不难,耐心点追踪汇编代码仿佛看源码,主要是最后的解方程