-
-
[原创] 看雪 2022 KCTF 秋季赛 第二题 盗贼作乱
-
发表于: 2022-11-17 03:19 8392
-
无混淆,好评(在程序逻辑公开的情况下设定谜题的阳谋比利用vmp之类的壳隐藏逻辑的阴谋更高一层)
IDA打开,观察了几个函数,感觉像大数运算
手动创建出下面的结构体:
其中,s
是大整数的字节,低位在前;len
是有效字节的个数
然后给各个函数改名称和类型:
可以写出bn_init_str的输入字符串和BN的实际数值之间的转换:
标记完成后的main函数如下:
这里的常量 n 解出后是 10000000000000000000 (10**19)
main函数的明逻辑很简单:给出两个小于n且不相等的整数x和y,统计 (x*loop_count - 1) % n == x
和 (y*loop_count + 1) % n == y
的次数,要求随着loop_count增大至少满足10次。
这两个等式稍微变形即可得到 x*(loop_count-1) = k1 * n + 1
和 y * (loop_count+1) = k1*n - 1
,因此 x 和 y 都要与 n 互质
如果想找到两个不同的 loop_count 满足同一个等式,即 x*(loop_count1-1) == 1 (mod n)
且 x*(loop_count2-1) == 1 (mod n)
,相减后得到 x * (loop_count2 - loop_count1) == 0 (mod n)
,即 n 整除 x * (loop_count2 - loop_count1)
。
由于 x 与 n 互质,所以只能是 n 整除 loop_count2 - loop_count1
,这意味着在满足一次等式条件后,需要至少再累加 n 次 x 才能再次满足等式,显然超过了限制,因此仅从表面逻辑看,题目似乎是无解的。
其实很多bn_函数内部都有一些奇怪的操作,例如很多无用的对tmp1和tmp2的操作;但是值得注意的是 bn_mul 和 bn_div 中有对 n 的引用,摘抄如下:
把常量运算带入到下面,发现玄机在于 n.s[tmp2.s[0]]
和 n.s[tmp1.s[0]]
两处的索引都越界了。看一下全局变量的地址:
这里 n.s[tmp2.s[0]]
访问的是 n.s[32]
,即 0x40A9D0+4+32 = 0x40A9F4,是 loop_count 变量;n.s[tmp1.s[0]]
访问的是 n.s[36]
,即 0x40A9D0+4+36 = 0x40A9F4,是 correctvar 变量。
所以,如果通过了 main 函数里明逻辑的等式使得 correctvar 加 1 时的 loop_count 等于 32,这部分暗逻辑会把 correctvar 再加上 4,所以才有可能达到明逻辑里 correctvar 等于 10 的最终要求。
如果限定 loop_count == 32,则等式约束变为了 x*(32-1) = k1*n + 1
和 y*(32+1) = k2*n - 1
。稍微遍历一下 k1 和 k2 的值使得 31 整除 k1*n + 1
和 33 整除 k2*n - 1
,得到 k1 = 12,k2 = 18,从而 x = 3870967741935483871,y = 6129032258064516129,且满足 x < y < n 的约束。
重新编码回去,得到程序正确的输入为 ZSxZerX4xb4-jyvP7x12lI7
。
(p.s. 利用越界写隐藏暗逻辑的思路很新颖,而且确实不太容易被发现。这种难度在往年估计都是中后段的题目了,今年竟然才到第二题)
(再p.s. 下一道题,看到出题战队是ArmVMP就很劝退)
struct BN {
int
len
;
char s[
32
];
}
struct BN {
int
len
;
char s[
32
];
}
sub_4014D0: bn_init_str,从字符串初始化BN。每个字符在字符表里的索引是每位数值,
62
进制,低位在前
sub_401630: bn_set_int,从整数初始化BN
sub_401660: bn_copy
sub_401690: bn_cmp
sub_401730: bn_add
sub_401820: bn_sub
sub_401920: bn_and
sub_401A80: bn_or
sub_401C00: bn_xor
sub_401CF0: bn_mul
sub_401F30: bn_div
sub_4021A0: bn_mod
sub_402360: do_bn_mod:计算mod,但是不修改BN
sub_4023A0: bn_shl
sub_402490: bn_shr
sub_4014D0: bn_init_str,从字符串初始化BN。每个字符在字符表里的索引是每位数值,
62
进制,低位在前
sub_401630: bn_set_int,从整数初始化BN
sub_401660: bn_copy
sub_401690: bn_cmp
sub_401730: bn_add
sub_401820: bn_sub
sub_401920: bn_and
sub_401A80: bn_or
sub_401C00: bn_xor
sub_401CF0: bn_mul
sub_401F30: bn_div
sub_4021A0: bn_mod
sub_402360: do_bn_mod:计算mod,但是不修改BN
sub_4023A0: bn_shl
sub_402490: bn_shr
def
bn_from(s):
table
=
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
tmp
=
[table.index(c)
for
c
in
s]
r
=
0
for
c
in
tmp[::
-
1
]:
r
=
r
*
len
(table)
+
c
return
r
def
bn_to(n):
table
=
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
tmp
=
[]
while
n:
t
=
n
%
len
(table)
tmp.append(table[t])
n
/
/
=
len
(table)
return
"".join(tmp)
def
bn_from(s):
table
=
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
tmp
=
[table.index(c)
for
c
in
s]
r
=
0
for
c
in
tmp[::
-
1
]:
r
=
r
*
len
(table)
+
c
return
r
def
bn_to(n):
table
=
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
tmp
=
[]
while
n:
t
=
n
%
len
(table)
tmp.append(table[t])
n
/
/
=
len
(table)
return
"".join(tmp)
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
int
v3;
/
/
edi
int
v4;
/
/
eax
int
v6;
/
/
esi
int
v7;
/
/
eax
char v9[
64
];
/
/
[esp
+
8h
] [ebp
-
40h
] BYREF
memset(v9,
0
, sizeof(v9));
printf(
"Input:"
);
gets(v9);
v3
=
-
1
;
v4
=
0
;
if
( !v9[
0
] )
goto LABEL_20;
do
{
if
( v4 >
=
64
)
break
;
if
( v9[v4]
=
=
'-'
)
v3
=
v4;
}
while
( v9[
+
+
v4] );
if
( v3 >
0
&& (v6
=
v4
-
v3, v4
-
v3 >
0
)
&& bn_init_str(&x, v9, v3, a0123456789abcd) >
0
&& bn_init_str(&y, &v9[v3
+
1
], v6
-
1
, a0123456789abcd) >
0
&& (bn_init_str(&n, aIrtzloz6iub, strlen(aIrtzloz6iub), a0123456789abcd),
bn_set_int(&v1,
0
),
bn_set_int(&v2,
0
),
bn_cmp(&x, &y) <
0
)
&& bn_cmp(&x, &n) <
0
&& bn_cmp(&y, &n) <
0
)
{
v7
=
0
;
while
(
1
)
{
loop_conut
=
v7
+
1
;
bn_add(&v1, &v1, &x);
bn_add(&v2, &v2, &y);
bn_mod(&v1, &v1, &n);
bn_mod(&v2, &v2, &n);
bn_set_int(&tmp1,
1
);
bn_sub(&tmp1, &v1, &tmp1);
if
( !bn_cmp(&tmp1, &x) )
{
+
+
correctvar;
bn_mul(&tmp1, &tmp1, &x);
}
bn_set_int(&tmp2,
1
);
bn_add(&tmp2, &v2, &tmp2);
if
( !bn_cmp(&tmp2, &y) )
{
+
+
correctvar;
bn_div(&tmp2, &n, &y);
}
if
( correctvar
=
=
10
)
break
;
v7
=
loop_conut;
if
( loop_conut >
=
0x200000
)
goto LABEL_20;
}
printf(
"Success!\n"
);
return
0
;
}
else
{
LABEL_20:
printf(
"Error.\n"
);
return
0
;
}
}
int
__cdecl main(
int
argc, const char
*
*
argv, const char
*
*
envp)
{
int
v3;
/
/
edi
int
v4;
/
/
eax
int
v6;
/
/
esi
int
v7;
/
/
eax
char v9[
64
];
/
/
[esp
+
8h
] [ebp
-
40h
] BYREF
memset(v9,
0
, sizeof(v9));
printf(
"Input:"
);
gets(v9);
v3
=
-
1
;
v4
=
0
;
if
( !v9[
0
] )
goto LABEL_20;
do
{
if
( v4 >
=
64
)
break
;
if
( v9[v4]
=
=
'-'
)
v3
=
v4;
}
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!