nbctf的难度对像我这样入坑逆向没多久的初学者来说绝对不算简单 但是同时又能学到不少新东西
email: okuusuku@gmail.com
Crisscross | pyre
给了一个python程序和它的输出:
import random
key1 = random.choices(range(256), k=20)
key2 = list(range(256))
random.shuffle(key2)
flag = open('flag.txt', 'rb').read()
def enc(n):
q = key2[n]
w = key1[q % 20]
n ^= q
return n, w
x = 0
for i, c in enumerate(flag):
x <<= 8
n, w = enc(c)
if i % 2:
n, w = w, n
x |= n
x |= w << ((2 * i + 1) * 8)
print(key1)
print(key2)
print(x)
#[127, 81, 241, 40, 222, 128, 45, 87, 27, 154, 66, 162, 73, 176, 172, 164, 106, 234, 77, 5]
#[155, 117, 124, 113, 104, 46, 151, 71, 144, 229, 152, 240, 199, 88, 103, 105, 245, 209, 13, 82, 166, 9, 201, 233, 228, 154, 19, 5, 30, 141, 81, 206, 246, 232, 107, 29, 208, 253, 187, 116, 98, 160, 60, 7, 220, 143, 80, 239, 52, 15, 94, 50, 149, 241, 57, 92, 230, 100, 31, 51, 36, 24, 39, 14, 25, 90, 101, 55, 194, 225, 157, 102, 2, 26, 148, 161, 180, 120, 223, 165, 32, 146, 185, 243, 119, 210, 172, 244, 1, 125, 44, 35, 169, 179, 188, 64, 207, 33, 137, 200, 142, 182, 250, 195, 28, 4, 79, 191, 86, 215, 96, 236, 91, 122, 196, 87, 118, 231, 126, 97, 147, 67, 132, 190, 234, 237, 43, 193, 252, 18, 212, 163, 56, 73, 123, 176, 162, 23, 192, 49, 21, 242, 171, 112, 153, 238, 203, 134, 167, 93, 115, 95, 8, 12, 65, 217, 248, 168, 219, 47, 211, 108, 76, 129, 145, 62, 156, 34, 218, 135, 48, 70, 75, 3, 249, 72, 202, 133, 183, 38, 37, 227, 164, 173, 159, 251, 0, 174, 54, 20, 136, 53, 138, 99, 226, 178, 42, 66, 150, 205, 204, 214, 197, 235, 110, 216, 63, 45, 184, 74, 41, 177, 27, 69, 130, 89, 61, 247, 255, 17, 254, 181, 131, 22, 224, 83, 189, 59, 114, 139, 111, 68, 6, 84, 11, 127, 221, 106, 77, 109, 158, 170, 16, 121, 222, 186, 10, 58, 175, 40, 128, 198, 78, 85, 213, 140]
#3449711664888782790334923396354433085218951813669043815144799745483347584183883892868078716490762334737115401929391994359609927294549975954045314661787321463018287415952
求出flag每一位对应的(w, n)
(需要注意的是w
和n
的排列顺序类似...n[2], w[1], n[0], w[0], n[1], w[1]...
) 再稍微改一下原程序爆破出flag:
key1 = [127, 81, 241, 40, 222, 128, 45, 87, 27, 154, 66, 162, 73, 176, 172, 164, 106, 234, 77, 5]
key2 = [155, 117, 124, 113, 104, 46, 151, 71, 144, 229, 152, 240, 199, 88, 103, 105, 245, 209, 13, 82, 166, 9, 201, 233, 228, 154, 19, 5, 30, 141, 81, 206, 246, 232, 107, 29, 208, 253, 187, 116, 98, 160, 60, 7, 220, 143, 80, 239, 52, 15, 94, 50, 149, 241, 57, 92, 230, 100, 31, 51, 36, 24, 39, 14, 25, 90, 101, 55, 194, 225, 157, 102, 2, 26, 148, 161, 180, 120, 223, 165, 32, 146, 185, 243, 119, 210, 172, 244, 1, 125, 44, 35, 169, 179, 188, 64, 207, 33, 137, 200, 142, 182, 250, 195, 28, 4, 79, 191, 86, 215, 96, 236, 91, 122, 196, 87, 118, 231, 126, 97, 147, 67, 132, 190, 234, 237, 43, 193, 252, 18, 212, 163, 56, 73, 123, 176, 162, 23, 192, 49, 21, 242, 171, 112, 153, 238, 203, 134, 167, 93, 115, 95, 8, 12, 65, 217, 248, 168, 219, 47, 211, 108, 76, 129, 145, 62, 156, 34, 218, 135, 48, 70, 75, 3, 249, 72, 202, 133, 183, 38, 37, 227, 164, 173, 159, 251, 0, 174, 54, 20, 136, 53, 138, 99, 226, 178, 42, 66, 150, 205, 204, 214, 197, 235, 110, 216, 63, 45, 184, 74, 41, 177, 27, 69, 130, 89, 61, 247, 255, 17, 254, 181, 131, 22, 224, 83, 189, 59, 114, 139, 111, 68, 6, 84, 11, 127, 221, 106, 77, 109, 158, 170, 16, 121, 222, 186, 10, 58, 175, 40, 128, 198, 78, 85, 213, 140]
x = 3449711664888782790334923396354433085218951813669043815144799745483347584183883892868078716490762334737115401929391994359609927294549975954045314661787321463018287415952
flag = []
while(x):
flag.append(x & 0xFF)
x >>= 8
w_o = []
n_o = []
for i in range(35):
n_o.append(flag[35 + (-1 if ((i + 1) % 2) else 1) * ((2 * int(i / 2) + 1))])
w_o.append(flag[35 + (-1 if (i % 2) else 1) * int((i + 1) / 2) * 2])
def enc(n):
q = key2[n]
w = key1[q % 20]
n ^= q
return n, w
i = 0
flag = ''
for _ in range(35):
for c in range(33, 125):
n, w = enc(c)
if n == n_o[i] and w == w_o[i]:
flag += chr(c)
i += 1
break
print(flag, end='')
print("}")
# nbctf{cr15s_cr0ss_str4wb3rry_s4uz3}
Shifty Sands | 进阶的迷宫题
用DIE打开附件 64位ELF无壳 IDA64打开 主函数结构非常简单 胜利的判断是走到L
失败的判定是走到S
唯一需要费点时间看的是加了点混淆(永真判断)的change()
函数:
__int64 __fastcall main(__int64 a1, char **a2, char **a3)
{
char input; // [rsp+Fh] [rbp-11h]
int row; // [rsp+10h] [rbp-10h] BYREF
unsigned int col; // [rsp+14h] [rbp-Ch] BYREF
unsigned __int64 v7; // [rsp+18h] [rbp-8h]
v7 = __readfsqword(0x28u);
row = 0;
col = 0;
do
{
do
input = getchar();
while ( input == 10 );
change(); // 从1开始算
// 走到4n 步时S向右扩散
// 走到4n + 3步时S向上扩散
// 走到4n + 2步时S向左扩散
// 走到4n + 1步时S向下扩散
++step;
check(input, &row, &col);
if ( step > 49 || maze[10 * row + col] == 'S' )
{
puts("ssssssssss");
return 1LL;
}
}
while ( maze[10 * row + col] != 'L' );
openflag();
return 0LL;
}
// change():
unsigned __int64 change()
{
unsigned __int64 result; // rax
int jj; // [rsp+0h] [rbp-10h]
int ii; // [rsp+0h] [rbp-10h]
int k; // [rsp+0h] [rbp-10h]
int j; // [rsp+0h] [rbp-10h]
int kk; // [rsp+4h] [rbp-Ch]
int n; // [rsp+4h] [rbp-Ch]
int m; // [rsp+4h] [rbp-Ch]
int i; // [rsp+4h] [rbp-Ch]
result = (step % 4);
if ( result == 3 )
{
for ( i = 9; i >= 0; --i )
{
for ( j = 0; j <= 9; ++j )
{
result = maze[10 * j + i];
if ( result == 'S' )
{
result = j;
if ( j >= 0 )
{
result = (i + 1);
if ( (result & 0x80000000) == 0LL )
{
result = (i + 1);
if ( result <= 9 )
{
result = maze[10 * j + 1 + i];
if ( result == '.' )
{
maze[10 * j + i] = '.';
result = &maze[10 * j + 1 + i];
*result = 'S';
}
}
}
}
}
}
}
}
else if ( result <= 3 )
{
if ( result == 2 )
{
for ( k = 0; k <= 9; ++k )
{
for ( m = 0; m <= 9; ++m )
{
result = maze[10 * k + m];
if ( result == 'S' )
{
result = (k - 1);
if ( (result & 0x80000000) == 0LL )
{
result = (k - 1);
if ( result <= 9 )
{
result = m;
if ( m >= 0 )
{
result = maze[10 * k - 10 + m];
if ( result == '.' )
{
maze[10 * k + m] = '.';
result = &maze[10 * k - 10 + m];
*result = 'S';
}
}
}
}
}
}
}
}
else if ( result )
{
if ( result == 1 )
{
for ( n = 0; n <= 9; ++n )
{
for ( ii = 0; ii <= 9; ++ii )
{
result = maze[10 * ii + n];
if ( result == 'S' )
{
result = ii;
if ( ii >= 0 )
{
result = (n - 1);
if ( (result & 0x80000000) == 0LL )
{
result = (n - 1);
if ( result <= 9 )
{
result = maze[10 * ii - 1 + n];
if ( result == '.' )
{
maze[10 * ii + n] = '.';
result = &maze[10 * ii - 1 + n];
*result = 'S';
}
}
}
}
}
}
}
}
}
else
{
for ( jj = 9; jj >= 0; --jj )
{
for ( kk = 0; kk <= 9; ++kk )
{
result = maze[10 * jj + kk];
if ( result == 'S' )
{
result = (jj + 1);
if ( (result & 0x80000000) == 0LL )
{
result = (jj + 1);
if ( result <= 9 )
{
result = kk;
if ( kk >= 0 )
{
result = maze[10 * jj + 10 + kk];
if ( result == '.' )
{
maze[10 * jj + kk] = '.';
result = &maze[10 * jj + 10 + kk];
*result = 'S';
}
}
}
}
}
}
}
}
}
return result;
}
既然是会动的迷宫 那盯着看原来的迷宫看是几乎不可能解出来的 这里提供一个python程序模仿迷宫的运作并记录步数:
def print_maze(maze, player_pos):
for i, row in enumerate(maze):
for j, cell in enumerate(row):
if (i, j) == player_pos:
print('$', end='')
else:
print(cell, end='')
print()
def move_player(maze, direction, player_pos):
row, col = player_pos
if direction == 'w' and row > 0 and maze[row - 1][col] != '#':
maze[row][col], maze[row - 1][col] = maze[row - 1][col], maze[row][col]
return (row - 1, col)
elif direction == 's' and row < len(maze) - 1 and maze[row + 1][col] != '#':
maze[row][col], maze[row + 1][col] = maze[row + 1][col], maze[row][col]
return (row + 1, col)
elif direction == 'a' and col > 0 and maze[row][col - 1] != '#':
maze[row][col], maze[row][col - 1] = maze[row][col - 1], maze[row][col]
return (row, col - 1)
elif direction == 'd' and col < len(maze[0]) - 1 and maze[row][col + 1] != '#':
maze[row][col], maze[row][col + 1] = maze[row][col + 1], maze[row][col]
return (row, col + 1)
return player_pos
def spread_sands(maze, step):
rows, cols = len(maze), len(maze[0])
if step % 4 == 0:
for i in range(rows - 1, -1, -1):
for j in range(cols):
if maze[i][j] == 'S':
if i < rows - 1 and maze[i + 1][j] == 'o':
maze[i][j], maze[i + 1][j] = maze[i + 1][j], maze[i][j]
elif step % 4 == 3:
for j in range(cols - 1, -1, -1):
for i in range(rows):
if maze[i][j] == 'S':
if j < cols - 1 and maze[i][j + 1] == 'o':
maze[i][j], maze[i][j + 1] = maze[i][j + 1], maze[i][j]
elif step % 4 == 2:
for i in range(rows):
for j in range(cols):
if maze[i][j] == 'S':
if i > 0 and maze[i - 1][j] == 'o':
maze[i][j], maze[i - 1][j] = maze[i - 1][j], maze[i][j]
elif step % 4 == 1:
for i in range(rows):
for j in range(cols):
if maze[i][j] == 'S':
if j > 0 and maze[i][j - 1] == 'o':
maze[i][j], maze[i][j - 1] = maze[i][j - 1], maze[i][j]
def main():
maze_str = """o###ooooo#
oo#So##oo#
#oS#o#SSo#
#oo#o#oo##
oSo#o#oSSo
o###o#oSoo
ooo#o#oooS
##o#o####o
oSoSo#ooSo
ooSoo#LSoo"""
maze = [list(row.strip()) for row in maze_str.split('\n')]
player_pos = (0, 0)
step = 0
while maze[player_pos[0]][player_pos[1]] != 'L':
print_maze(maze, player_pos)
print(f"Step: {step}")
print()
direction = input("Enter your move (w/a/s/d): ")
player_pos = move_player(maze, direction, player_pos)
spread_sands(maze, step)
step += 1
print("WIN")
if __name__ == "__main__":
main()
但是因为我的移动逻辑是交换 所以程序还是有点瑕疵(你不会赢 也不会似) 但是作为一个做出答案的辅助工具还是足够的
另外需要注意的是 附件中的迷宫运作逻辑是:
-
S
移动 -
玩家所在行, 列变化
-
判断玩家所在行, 列是否是
S
所以其实是可以实现类似居合和见切的效果的(玩太刀玩的) 不然也无法通关 e.g.
→ → ↓$ √ $o √ So √ ↑S ↑S ↑$
最后得到路线后nc到官方的端口并输入路线 得到flag:nbctf{5lowly_5huffl3d 5wa110wing_54nd5}
Itchy Scratchy | Scratch编程逆向
没有混淆逻辑很清晰的Scratch工程文件:
alpha
和enc
也都给出了 唯一的问题是最后需要解方程 这里用z3求解
from z3 import *
name = [29,26,7,7,6,0,10,32,4,3,21,10]
alpha = 'zvtwrca57n49u2by1jdqo6g0ksxfi8pelmh3'
enc = [902,764,141,454,207,51,532,1013,496,181,562,342]
j = []
for i in range(1, 13):
j.append((i * i + name[i - 1]) % len(name))
factor = []
for i in range(12):
factor.append(name[i] * name[j[i]])
enc = [enc[i] - factor[i] for i in range(12)]
vi = IntVector('vi', 12)
s = Solver()
for i in range(12):
s.add(vi[i] * vi[j[i]] == enc[i])
result = s.check()
if result == sat:
m = s.model()
vi_values = [m[vi[i]].as_long() for i in range(12)]
for each in vi_values:
print(each, end=', ')
else:
print('No solution')
# [17, 14, 33, 32, 3, 3, 36, 5, 28, 17, 11, 12]
intput = [17, 14, 33, 32, 3, 3, 36, 5, 28, 17, 11, 12]
print("\nnbctf{", end = '')
for each in intput:
print(alpha[each - 1], end = '')
print("}")
# nbctf{12lett3rf149}
Twostep | 构式加密
题如其名 用了类似两步舞的加密方式 有一种独特的美感(但是逆向的时候感觉不出来😡)
用DIE打开附件 64位ELF无壳
主函数的逻辑依然很简单:
__int64 __fastcall main(int a1, char **a2, char **a3)
{
__int64 v3; // rax
__int64 v4; // rax
__int64 v6; // rax
char input[40]; // [rsp+0h] [rbp-40h] BYREF
unsigned __int64 v9; // [rsp+28h] [rbp-18h]
v9 = __readfsqword(0x28u);
std::string::basic_string(input, a2, a3);
v3 = print(&std::cout, "C'mon y'all let's boogie!\n> ");
println(v3, &std::flush<char,std::char_traits<char>>);
std::operator>><char>(&std::cin, input);
if ( std::string::length(input) == 57 && (v4 = std::string::c_str(input), encrypto(v4)) )
v6 = print(&std::cout, "Yeehaw!");
else
v6 = print(&std::cout, "Aww shucks!");
println(v6, &std::endl<char,std::char_traits<char>>);
std::string::~string(input);
return 0LL;
}
但是打开encrypto()
时出现了IDA无法反编译的错误 发现是text.40168C处发生了call无法解析的错误我们去看看这个铸币是怎么回事
.text:000000000040168B FF D0 call rax
上下文也看不出rax存放的是固定的某个函数还是什么 不要紧我们先nop掉它 顺利反编译了encrypto()
发现它调用了encrypto2()
(这些都是我自己命名的) 打开encrypto2()
发现它调用了encrypto()
... 这就是"两步舞"的含义 不管 先分析两个函数
__int64 __fastcall encrypto(_BYTE *input_6)
{
__int64 result; // rax
char v2; // [rsp+19h] [rbp-A7h]
__int16 v3; // [rsp+1Ah] [rbp-A6h]
__int16 v4; // [rsp+1Ch] [rbp-A4h]
__int16 v5; // [rsp+1Eh] [rbp-A2h]
unsigned __int64 i; // [rsp+20h] [rbp-A0h]
unsigned __int64 k; // [rsp+20h] [rbp-A0h]
__int64 v8; // [rsp+20h] [rbp-A0h]
unsigned __int64 m; // [rsp+20h] [rbp-A0h]
unsigned __int64 j; // [rsp+28h] [rbp-98h]
_BYTE *v11; // [rsp+30h] [rbp-90h]
_BYTE *v12; // [rsp+30h] [rbp-90h]
_BYTE *v13; // [rsp+30h] [rbp-90h]
__int16 v14[4]; // [rsp+38h] [rbp-88h]
__int64 v15[6]; // [rsp+40h] [rbp-80h]
__int64 v16[8]; // [rsp+70h] [rbp-50h]
__int16 v17; // [rsp+B6h] [rbp-Ah]
unsigned __int64 v18; // [rsp+B8h] [rbp-8h]
v18 = __readfsqword(0x28u);
v14[0] = 18440;
v14[1] = 3136;
v14[2] = 18444;
v14[3] = 16524;
v16[0] = 1LL;
v16[1] = 16LL;
v16[2] = 32LL;
v16[3] = 512LL;
v16[4] = 1024LL;
v16[5] = 2048LL;
v16[6] = 0x2000LL;
v16[7] = 0x4000LL;
v15[0] = 177LL;
v15[1] = 166LL;
v15[2] = 183LL;
v15[3] = 182LL;
v15[4] = 177LL;
v15[5] = 173LL;
v17 = 170;
switch ( var )
{
case 0LL:
if ( *input_6 == 110
&& input_6[1] == 98
&& input_6[2] == 99
&& input_6[3] == 116
&& input_6[4] == 102
&& input_6[5] == 123 )
{
result = encrypto2(input_6 + 6);
}
else
{
result = 0LL;
}
break;
case 1LL:
v11 = check1_var_add(input_6);
for ( i = 0LL; i <= 3; ++i )
{
v2 = v11[i];
v4 = 0;
for ( j = 0LL; j <= 3; ++j )
{
v4 |= (v2 & 3) << (4 * j + 2);
v2 >>= 2;
}
if ( v4 != v14[i] )
return 0LL;
}
result = encrypto2(input_6);
break;
case 3LL:
v12 = check1_var_add(input_6);
for ( k = 0LL; k <= 4; ++k )
data[k] = v12[k];
result = encrypto2(input_6);
break;
case 6LL:
v3 = *check1_var_add(input_6);
v8 = 0LL;
while ( v3 )
{
v5 = v3 & -v3;
if ( v8 == 8 )
return 0LL;
if ( v16[v8] == v5 )
++v8;
v3 ^= v5;
}
result = v8 == 8 && encrypto2(input_6);
break;
case 8LL:
v13 = check1_var_add(input_6);
for ( m = 0LL; m <= 5; ++m )
LOBYTE(v17) = v13[m] ^ v15[m];
result = 1LL;
break;
default:
result = 0LL;
break;
}
return result;
}
//================================================
__int64 __fastcall encrypto2(_BYTE *input)
{
__int64 result; // rax
unsigned __int64 i; // [rsp+18h] [rbp-E8h]
unsigned __int64 j; // [rsp+18h] [rbp-E8h]
unsigned __int64 k; // [rsp+18h] [rbp-E8h]
__int64 v5; // [rsp+20h] [rbp-E0h]
_BYTE *v6; // [rsp+28h] [rbp-D8h]
_BYTE *v7; // [rsp+28h] [rbp-D8h]
_BYTE *v8; // [rsp+28h] [rbp-D8h]
_BYTE *v9; // [rsp+28h] [rbp-D8h]
__int64 v10[6]; // [rsp+30h] [rbp-D0h]
__int64 v11[6]; // [rsp+60h] [rbp-A0h]
__int64 v12[6]; // [rsp+90h] [rbp-70h]
__int64 v13[8]; // [rsp+C0h] [rbp-40h]
v13[7] = __readfsqword(0x28u);
v13[0] = 0xD4F3LL;
v13[1] = 0x3D49LL;
v13[2] = 0x107BLL;
v13[3] = 0xC479LL;
v13[4] = 0xAA84LL;
v13[5] = 0x9807LL;
v10[0] = 0x394CLL;
v10[1] = 24063LL;
v10[2] = 37349LL;
v10[3] = 50716LL;
v10[4] = 61563LL;
v11[0] = 1843061LL;
v11[1] = 222420LL;
v11[2] = 5184810LL;
v11[3] = 4590105LL;
v11[4] = 2184197LL;
v5 = 0LL;
v12[0] = 1073741834LL;
v12[1] = 2415919110LL;
v12[2] = 939524099LL;
v12[3] = 536870913LL;
v12[4] = 1845493760LL;
switch ( var )
{
case 0LL:
v6 = check1_var_add(input);
if ( (*v6 ^ 0x5F) == v6[1]
&& (v6[1] ^ 0x75) == v6[2]
&& (v6[2] ^ 0x12) == v6[3]
&& (v6[3] ^ 0x38) == *v6
&& *v6 == 0x6C )
{
result = encrypto(input);
}
else
{
result = 0LL;
}
break;
case 2LL:
v7 = check1_var_add(input);
for ( i = 0LL; i <= 4; ++i )
{
if ( v13[i] + v13[i + 1] * v7[i] != v11[i] )
return 0LL;
}
result = encrypto(input);
break;
case 4LL:
v8 = check1_var_add(input);
for ( j = 0LL; j <= 4; ++j )
{
v5 += data[j] + (v8[j] << 7);
if ( v5 != v10[j] )
return 0LL;
}
result = encrypto2(input);
break;
case 5LL:
v9 = check1_var_add(input);
for ( k = 0LL; k <= 4; ++k )
{
if ( v12[k] != ((v9[k] >> (k + 3)) | (v9[k] << (32 - (k + 3)))) )
return 0LL;
}
result = encrypto(input);
break;
case 7LL:
qword_404308 = *check1_var_add(input);
result = *&qword_404308 == 3.325947034342098e151 && encrypto(input);
break;
default:
result = 0LL;
break;
}
return result;
}
//================================================
_BYTE *__fastcall check1_var_add(_BYTE *input_6)
{
__int64 v1; // rax
__int64 v2; // rax
__int64 v5; // [rsp+18h] [rbp-8h]
v1 = var++;
v5 = word_404090[v1]; // 下划线数量
while ( v5 )
{
if ( *++input_6 == '}' || !*input_6 ) // *(input + 1) == '}' || !*input
// input++
{
v2 = print(&std::cout, "Aww shucks!");
println(v2, &MEMORY[0x7F456B738680]);
exit(1);
}
if ( *input_6 == '_' )
{
++input_6;
--v5;
}
}
return input_6;
}
两个加密函数都调用了的check1_var_add(_BYTE *input_6)
实际上是一个通过当前"步数"(var
对应每个case块的加密)来返回当前加密的单词在整个flag中的位置(flag中每个单词用_
分割) 动态调试时发现步数和位置的对应关系如下:
index: 4, 7, 2, 0, 3, 6, 5, 1, 8 var: 0, 1, 2, 3, 4, 5, 6, 7, 8
其实得到了这些信息后一步步攻破每个case块的加密即可 但看出check1_var_add(_BYTE *input_6)
的作用实际上也是一个 以下给出每个加密块对应的解密脚本:
# case 1:
flag = []
flag.append(0x6C)
flag.append(flag[0] ^ 0x5F)
flag.append(flag[1] ^ 0x75)
flag.append(flag[2] ^ 0x12)
flag.append(flag[3] ^ 0x38)
flag[0] = [chr(val) for val in flag]
# print(flag)
# l3FTl __5
# l3FT __5
# case 2:
flag = [None] * 4
flag[0] = 0x4808
flag[1] = 0xc40
flag[2] = 0x480c
flag[3] = 0x408c
flag = [val >> 2 for val in flag]
flag = [chr(val & 3 | ((val >> 4) & 3) << 2 | ((val >> 8) & 3) << 4 | ((val >> 12) & 3) << 6) for val in flag]
# print(flag)
# b4cK __8
# case 3:
flag = [None] * 5
flag[0] = 0x1c1f75
flag[1] = 0x364d4
flag[2] = 0x4f1d2a
flag[3] = 0x460a19
flag[4] = 0x215405
key = [None] * 6
key[0] = 0xD4F3
key[1] = 0x3D49
key[2] = 0x107B
key[3] = 0xC479
key[4] = 0xAA84
key[5] = 0x9807
flag = [chr(int((flag[i] - key[i]) / key[i + 1])) for i in range(5)]
# print(flag)
# r1gh7 __3
# case 4 & 5:
enc = [None] * 5
enc[0] = 0x394C
enc[1] = 24063
enc[2] = 37349
enc[3] = 50716
enc[4] = 61563
copy = [val for val in enc]
for i in range(5):
if i > 0:
enc[i] -= copy[i - 1]
print(chr(enc[i] >> 7), chr(enc[i] % 0x80))
# r L
# I 3
# g f
# h 7
# T _
# index:
# 4 1
# case 5:
flag = ''
enc = [None] * 100
enc[0] = 0x4000000a
enc[1] = 0x90000006
enc[2] = 0x38000003
enc[3] = 0x20000001
enc[4] = 0x6e000000
for i in range(5):
flag += chr(enc[i] >> (32 - (i + 3)) | enc[i] << (i + 3) & 0xFF)
# print(flag)
# RigH7 __7
# case 6:
enc[0] = 1
enc[1] = 0x10
enc[2] = 0x20
enc[3] = 0x200
enc[4] = 0x400
enc[5] = 0x800
enc[6] = 0x2000
enc[7] = 0x4000
# print(chr(0x31), chr(0x6E))
# 1n __6
# case 7:
x = 0x5F64523477723066
for i in range(8):
print(chr((x >> i * 8) & 0XFF), end = '')
# f0rw4Rd_ __2
最后问题来了 实际上call rax对应到伪代码就是
case 8LL:
v13 = check1_var_add(input_6);
for ( m = 0LL; m <= 5; ++m )
LOBYTE(v17) = v13[m] ^ v15[m];
(&v17)(); //调用v17
result = 1LL;
break;
那么其实rax是根据计算得到的不确定的值 但是原来的程序少了这一步貌似也是甚至就应该这样照常运行 那么什么时候call rax相当于什么都没做呢?
-
call $+5 ;花指令
-
call rax ;rax -> ret 调用后立即返回
第一种显然不太可能 那就是当rax == retn(C3)
时了! 于是写出最后一块解密脚本:
# case 8:
enc = [None] * 6
enc[0] = 0xb1
enc[1] = 0xa6
enc[2] = 0xb7
enc[3] = 0xb6
enc[4] = 0xb1
enc[5] = 0xad
for each in enc:
print(chr(each ^ 0xC3 & 0xFF), end='')
# return __9
最后组合起来得到最终flag:nbctf{L3f7_f0rw4Rd_r1gh7_rIghT_l3FT_1n_RigH7_b4cK_return}
(其实nbctf{L3f7_f0rw4Rd_r1gh7_rIghT_l3FTl_1n_RigH7_b4cK_return
也能让程序返回正确的提示 但显然是非预期解)
以上就是本人对能力所及题目的见解 若有不足还请各位师傅狠狠指正