REVERSE-如影随形
ISCC2026 WriteUp 提交模板
REVERSE-如影随形
解题思路
1.打开ida看函数
看main函数最后,有比较逻辑

是个硬编码

就是c&U.zG<uILF9m0 Y
2.第一关

// For each source permutation entry, finds its target position and calls the per-cell transform with horizontal/vertical distances.
void __fastcall apply_permutation_distance_transforms(unsigned __int8 (*a1)[4])
{
char *p_M; // r14
int n4; // r15d
__int64 i; // r13
char v4; // di
int n4_1; // esi
char *p_M_target; // rbx
__int64 v7; // r9
int v8; // r9d
int n4_2; // edx
p_M = (char *)&g_source_permutation_matrix;
n4 = 4;
do
{
for ( i = 0; i != 4; ++i )
{
v4 = p_M[i];
n4_1 = n4 - 4;
p_M_target = (char *)&g_target_identity_matrix;
LABEL_4:
while ( 2 )
{
v7 = 0;
while ( v4 != p_M_target[v7] )
{
if ( ++v7 == 4 )
{
++n4_1;
p_M_target += 4;
if ( n4 != n4_1 )
goto LABEL_4;
goto LABEL_8;
}
}
v8 = v7 - i;
n4_2 = -n4_1;
if ( n4_1 > 0 )
n4_2 = n4_1;
if ( v8 <= 0 )
v8 = -v8;
++n4_1;
p_M_target += 4;
transform_matrix_cell_by_distance(a1, 4 - n4, i, v8, n4_2);
if ( n4 != n4_1 )
continue;
break;
}
LABEL_8:
;
}
p_M += 4;
--n4;
}
while ( n4 );
}
会遍历两个 4x4 表:
M_target = 0 1 2 3
4 5 6 7
8 9 a b
c d e f
M = 7 8 c 9
e 3 5 2
0 f d b
a 4 1 6
它对 M 中每个元素,去 M_target 里找对应位置,然后得到当前位置与目标位置之间的横向距离和纵向距离,再调用 0x1400018A0 做一次“整串变换 + 单点变换”。
0x1400018A0 的逻辑分两段:
- 先把 4x4 矩阵按行拍平成 16 字节字符串。
- 对整个 16 字节做一次 printable 区间上的链式 mod95 位移。
公式可以写成:
y = (shift + x - 32) % 95 + 32
shift = y - 32
这里初始 shift 是刚才求出来的横向距离。
- 然后只修改某一个格子,修改次数等于纵向距离。
这里用了一个伪装得很像 flag 的字符串:
iscc_{this_is_not_flag}
它其实只是 key,不是答案。另一个表 cfg::kMapXor 会先整体异或 0x60 还原成一个 95 字节置换表,再配合这个 key 做单点替换。
这两个步骤都是可逆的,所以我们只要把 16 次操作倒着跑一遍就能拿回原串

ISCC{8,Fu}@V$s2sSt8y}
Exp
TARGET_BYTES = b"c&U.zG<uILF9m0 Y"
KEY_BYTES = b"iscc_{this_is_not_flag}"
TRANSFORM_STEPS = [
(0, 0, 3, 1), (0, 1, 1, 2), (0, 2, 2, 3), (0, 3, 2, 2),
(1, 0, 2, 2), (1, 1, 2, 1), (1, 2, 1, 0), (1, 3, 1, 1),
(2, 0, 0, 2), (2, 1, 2, 1), (2, 2, 1, 1), (2, 3, 0, 0),
(3, 0, 2, 1), (3, 1, 1, 2), (3, 2, 1, 3), (3, 3, 1, 2),
]
ENCODED_SBOX_BYTES = bytes([
0x43, 0x6F, 0x2E, 0x5E, 0x7F, 0x5F, 0x59, 0x39, 0x6D, 0x62, 0x73, 0x6B, 0x52, 0x51, 0x47, 0x27,
0x31, 0x45, 0x42, 0x24, 0x2F, 0x74, 0x58, 0x32, 0x75, 0x63, 0x5D, 0x7C, 0x71, 0x79, 0x2B, 0x29,
0x2A, 0x68, 0x22, 0x44, 0x23, 0x5A, 0x7B, 0x6E, 0x28, 0x7D, 0x67, 0x60, 0x3C, 0x65, 0x30, 0x21,
0x2C, 0x4F, 0x4A, 0x3B, 0x48, 0x6A, 0x55, 0x4D, 0x70, 0x41, 0x57, 0x49, 0x3D, 0x69, 0x20, 0x5B,
0x37, 0x36, 0x3E, 0x7A, 0x66, 0x26, 0x4E, 0x2D, 0x77, 0x54, 0x4B, 0x3A, 0x4C, 0x76, 0x7E, 0x61,
0x25, 0x50, 0x5C, 0x78, 0x34, 0x53, 0x38, 0x72, 0x33, 0x64, 0x56, 0x40, 0x35, 0x46, 0x6C,
])
decoded_sbox = bytes(value ^ 0x60 for value in ENCODED_SBOX_BYTES)
inverse_sbox = [0] * 95
for source_index, mapped_value in enumerate(decoded_sbox):
inverse_sbox[mapped_value] = source_index
def reverse_chain_shift(byte_values, initial_shift):
restored_values = []
rolling_shift = initial_shift % 95
for encoded_value in byte_values:
normalized_value = encoded_value - 32
original_value = (normalized_value - rolling_shift) % 95 + 32
restored_values.append(original_value)
rolling_shift = normalized_value
return restored_values
def reverse_cell_transform(byte_values, row_index, column_index, repeat_count):
if repeat_count == 0:
return byte_values
flat_index = row_index * 4 + column_index
current_value = byte_values[flat_index] - 32
key_offset = KEY_BYTES[repeat_count % len(KEY_BYTES)] % 95
for _ in range(repeat_count):
current_value = inverse_sbox[(current_value - key_offset) % 95]
byte_values[flat_index] = current_value + 32
return byte_values
working_values = list(TARGET_BYTES)
for row_index, column_index, horizontal_shift, vertical_shift in reversed(TRANSFORM_STEPS):
working_values = reverse_cell_transform(
working_values,
row_index,
column_index,
vertical_shift,
)
working_values = reverse_chain_shift(working_values, horizontal_shift)
print("ISCC{" + bytes(working_values).decode() + "}")

浙公网安备 33010602011771号