-
-
[原创] Blue Water CTF 2024 re OORM wp 模拟执行爆破+剪枝
-
发表于: 2024-10-14 16:03 5494
-
肝了一下午,拿了个一血,开心hh
如果patch或者插桩之类的方法,代替模拟执行的话,可能速度会更快,不过这题规模还能接受,就用模拟执行跑了,这个方便一点,切个片段出来就能直接执行
需要注意的是避免用unicorn的单步,指定until会快很多;然后unicorn不知道为什么emu_start多次后会报一个OS Error什么访问-1指针异常啥的,得隔一定次数重新new一个Uc
main:
输入100个hex
然后每一个hex,转int,接着按位拆分,存进QWORD[400]数组unk_214EE0,unk_214260中,低位在数组低地址
接着轮询800个函数,总共需要执行800次
最后需要dword_212940 == 40
那800个函数的定义:void (__fastcall *)(__int64 key, __int64 input_bit)
keyAs_2135E0、keyBs_212960初始化地方sub_6160,好像是什么PHT里引用了这个函数
keyAs、keyBs分别初始了20个非0元素,dump出这两个数组
funcs_A[0](key, input_bit)大致逻辑,根据keyAs[0]和input_bit[0],给keyAs[20]赋值:
funcs_A[1](key, input_bit)大致逻辑,根据keyAs[1]和input_bit[1],给keyAs[21]赋值:
funcs_A[399](key, input_bit)大致逻辑,根据keyAs[399]和input_bit[399],给dwCheck_212940赋值:
fA_0~fA_19
生成 fA_20~fA_39
的key
然后fA_20~fA_39
生成 fA_40~fA59
的key
直到fA_380~fA_399
是检验
整体大概是这么个顺序:
fA_0 ⇒ fA_20 … fA_360,fA_380校验
fA_1 ⇒ fA_21 … fA_361,fA_381校验
…
fB_0 ⇒ fB_1 … fB_18,fB_19校验
fB_20 ⇒ fB_21 … fB_38,fB_39校验
…
相当于输入是20*20的值只能为0、1的矩阵
fA系列函数是做列校验
fB系列函数是做行校验
使用fA_0、fA_20、...、fA_380爆破第一列,有很多结果:
模拟执行fA爆破列的脚本(使用fB爆破行的脚本也差不多):
爆破列校验发现有多解,检查列的所有解,如果某一位在该列的所有解中全为0或1可以确认该位为0或1,一次列验证确认结果如下:
然后用这个确认的结果,对爆破做剪枝加速,继续跑一次行验证、一次列验证、一次行验证(每次跑完验证都按前面的思路更新这个确认的结果),就出来完整结果了
times = 0;
do
{
for
( i = 0LL; i != 400; ++i )
{
keyA = keyAs_2135E0[i];
if
( runA )
{
++times;
funcs_A_211CA0[i](keyA, input_in_bits_A_214EE0[i]);
keyAs_2135E0[i] = 0LL;
}
keyB = keyBs_212960[i];
if
( keyB )
{
++times;
funcs_B_211020[i](keyB, input_in_bits_B_214260[i]);
keyBs_212960[i] = 0LL;
}
}
}
while
( times <= 799 );
times = 0;
do
{
for
( i = 0LL; i != 400; ++i )
{
keyA = keyAs_2135E0[i];
if
( runA )
{
++times;
funcs_A_211CA0[i](keyA, input_in_bits_A_214EE0[i]);
keyAs_2135E0[i] = 0LL;
}
keyB = keyBs_212960[i];
if
( keyB )
{
++times;
funcs_B_211020[i](keyB, input_in_bits_B_214260[i]);
keyBs_212960[i] = 0LL;
}
}
}
while
( times <= 799 );
void
funcs_A_0(
__int64
key,
__int64
input_bit) {
x = input_bit | (key<< 1);
y = hashA0(x);
// 48 89 3D B7 C5 mov cs:keyAs_2135E0+0A0h, rdi
keyAs[20] = y;
}
void
funcs_A_0(
__int64
key,
__int64
input_bit) {
x = input_bit | (key<< 1);
y = hashA0(x);
// 48 89 3D B7 C5 mov cs:keyAs_2135E0+0A0h, rdi
keyAs[20] = y;
}
void
funcs_A_1(
__int64
key,
__int64
input_bit) {
x = input_bit | (key<< 1);
y = hashA1(x);
// 48 89 3D FF BA mov cs:keyAs_2135E0+0A8h, rdi
keyAs[21] = y;
}
void
funcs_A_1(
__int64
key,
__int64
input_bit) {
x = input_bit | (key<< 1);
y = hashA1(x);
// 48 89 3D FF BA mov cs:keyAs_2135E0+0A8h, rdi
keyAs[21] = y;
}
void
funcs_A_399(
__int64
key,
__int64
input_bit) {
x = input_bit | (key<< 1);
y = hashA399(x);
if
( y == 21961 || y == 27098 )
++dwCheck_212940;
}
void
funcs_A_399(
__int64
key,
__int64
input_bit) {
x = input_bit | (key<< 1);
y = hashA399(x);
if
( y == 21961 || y == 27098 )
++dwCheck_212940;
}
[
0
,
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
0
]
3090
...
[
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
0
,
0
,
1
]
32766
[
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
0
,
1
,
0
]
3090
[
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
0
,
0
]
3090
...
[
0
,
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
0
,
1
,
1
,
1
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
0
,
1
]
32766
[
0
,
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
0
]
3090
...
[
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
0
,
0
,
1
]
32766
[
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
0
,
1
,
0
]
3090
[
1
,
0
,
1
,
0
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
1
,
0
,
1
,
1
,
1
,
0
,
1
,
0
,
0
]
3090
...
from
capstone
import
*
from
unicorn
import
*
from
unicorn.x86_const
import
*
from
elftools.elf.elffile
import
ELFFile
keyAs
=
[
9644
,
31494
,
15772
, ...,
0
,
0
,
0
, ...]
f
=
open
(
'main'
,
'rb'
)
elff
=
ELFFile(f)
def
rva_to_offset(elff, rva):
for
segment
in
elff.iter_sections():
if
rva >
=
segment[
'p_vaddr'
]
and
rva < segment[
'p_vaddr'
]
+
segment[
'p_memsz'
]:
return
rva
-
segment[
'p_vaddr'
]
+
segment[
'p_offset'
]
raise
ValueError(
'RVA not within any segment'
)
def
read_elf_content_by_rva(elff, rva, size):
for
segment
in
elff.iter_segments():
# 检查RVA是否在当前段的范围内
if
rva >
=
segment[
'p_vaddr'
]
and
rva < segment[
'p_vaddr'
]
+
segment[
'p_memsz'
]:
foa
=
rva
-
segment[
'p_vaddr'
]
content
=
segment.data()[foa : foa
+
size]
return
content
# 收集函数的地址
funcs_A
=
[
int
.from_bytes(read_elf_content_by_rva(elff,
0x211CA0
+
i
*
8
,
8
),
'little'
)
for
i
in
range
(
400
)]
funcs_A.append(
0x106430
)
endAs
=
[
28866
,
31618
,
34242
, ...]
tyAs
=
[
39
,
39
,
39
, ..., (
35
,
32766
,
3090
), (
35
,
6485
,
4159
), (
35
,
14535
,
24449
), ...]
def
sim2(uc: Uc, i, j, key, input_bit):
# func arg
idx
=
i
+
j
*
20
uc.reg_write(UC_X86_REG_RDI, key)
uc.reg_write(UC_X86_REG_RSI, input_bit)
uc.emu_start(funcs_A[idx], endAs[idx],
0
,
0
)
if
j !
=
19
:
return
uc.reg_read(tyAs[idx]),
0
,
0
else
:
return
uc.reg_read(tyAs[idx][
0
]), tyAs[idx][
1
], tyAs[idx][
2
]
uc
=
Uc(UC_ARCH_X86, UC_MODE_64)
# code
for
segment
in
elff.iter_segments():
if
segment[
'p_vaddr'
]
=
=
0x6000
:
uc.mem_map(segment[
'p_vaddr'
], segment[
'p_memsz'
]
/
/
0x1000
*
0x1000
+
0x1000
)
uc.mem_write(segment[
'p_vaddr'
], segment.data())
# 执行funcA[i + j * 20](key, 0) 和 funcA[i + j * 20](key, 1)
def
dfsA(i, j, key)
-
>
bool
:
global
path, uc
if
j
=
=
3
:
# unicron的bug,得隔一定次数重新创建一个Uc
uc
=
Uc(UC_ARCH_X86, UC_MODE_64)
for
segment
in
elff.iter_segments():
if
segment[
'p_vaddr'
]
=
=
0x6000
:
uc.mem_map(segment[
'p_vaddr'
], segment[
'p_memsz'
]
/
/
0x1000
*
0x1000
+
0x1000
)
uc.mem_write(segment[
'p_vaddr'
], segment.data())
if
j
=
=
6
:
print
(path)
if
j
=
=
19
:
new_key, t0, t1
=
sim2(uc, i, j, key,
0
)
if
new_key
=
=
t0
or
new_key
=
=
t1:
print
(
'path'
, path
+
[
0
], new_key)
new_key, t0, t1
=
sim2(uc, i, j, key,
1
)
if
new_key
=
=
t0
or
new_key
=
=
t1:
print
(
'path'
, path
+
[
1
], new_key)
else
:
new_key, _, _
=
sim2(uc, i, j, key,
0
)
path.append(
0
)
dfsA(i, j
+
1
, new_key)
path.pop()
new_key, _, _
=
sim2(uc, i, j, key,
1
)
path.append(
1
)
dfsA(i, j
+
1
, new_key)
path.pop()
path
=
[]
dfsA(
0
,
0
, keyAs[
0
])
f.close()
from
capstone
import
*
from
unicorn
import
*
from
unicorn.x86_const
import
*
from
elftools.elf.elffile
import
ELFFile
keyAs
=
[
9644
,
31494
,
15772
, ...,
0
,
0
,
0
, ...]
f
=
open
(
'main'
,
'rb'
)
elff
=
ELFFile(f)
def
rva_to_offset(elff, rva):
for
segment
in
elff.iter_sections():
if
rva >
=
segment[
'p_vaddr'
]
and
rva < segment[
'p_vaddr'
]
+
segment[
'p_memsz'
]:
return
rva
-
segment[
'p_vaddr'
]
+
segment[
'p_offset'
]
raise
ValueError(
'RVA not within any segment'
)
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!