CTF authors, please don't put PhD level math in reverse engineering challenges. thank you.
(好吧其实不是PhD level math,逃~)
题目附件:b3e67cd8a0f9ccc3bcfdf2deb8b0166dc6fb5fb745584426fb152e36c7abe56e
分析附件,不难发现为脚本文件。脚本文件执行Base85解码和LZMA解压释放文件。编写Python代码提取如下(节约篇幅,有删减)。
常规步骤,检查字符串。执行命令如下。
得到结果如下。
自然需要部署工具环境。执行命令如下。
根据脚本文件,不难给出可用的链接命令如下。不妨假设所有链接定义符号为零。
常规步骤,检查反汇编。执行命令如下。
不难发现直接提示错误,无额外逻辑。结合链接定义符号,怀疑重定位表覆写。检查重定位。执行命令如下。
关注到.rela.text.
这一部分内容过长,不符合常规。不难将这一部分导出为CSV表格文件。
不妨使用Frida计算文件偏移和有效地址之间的关系(JavaScript脚本略,可通过内存布局得到)。
得到文件偏移和有效地址偏差mem_delta = 0x01FFFBB5
。此外另从ELF结构读取文件偏移reloca_foff = 0x0000045B
和大小reloca_size = 0x00245940
。
LARCH重定位涉及基于栈的虚拟机。对于LARCH虚拟机,不用慌张,相较于传统VM逆向题目,各个操作功能及其参数是已知的。不妨转换为C代码并使用编译器优化。编写Python代码转换如下。需要注意的是,操作数可能被覆写,因此需要特殊处理。
执行脚本,生成C代码头文件。不妨配合以下C代码源文件使用。
不妨使用GCC编译并启用O2优化。命令如下。
此时我们已经将虚拟代码还原。使用常规工具反编译。关注到如下片段。
不妨发现为核心比较。
进一步观察,发现其中4个校验为相邻两部分的拼接经过运算的结果。具体拼接方式如下。
运算方式如下(节约篇幅,有删减)。大致为数域运算。
另外16个校验为每个部分两次经过运算的结果。运算方式大致为杂凑算法。
让我们来总结一下手里的东西。
不妨猜测:按照正常的逻辑,数域运算应当是可解的。我们应该得到每个flag_v?
的一半部分(4字节,32位),再暴力枚举剩余一半部分(4字节,32位)。
但是……参见“0x00 前言”。看不懂数学怎么办?z3?(数域运算大概率是非对称的,约束求解大概率是困难的)暴力!
由于直接暴力枚举杂凑算法开销过大,不妨枚举数域运算。然后再按照猜测的剩余逻辑求解。相信我,经过测试,不做优化的情况下单核单线程的CCF老年机也只需要万年就能全部枚举一遍。优化代码使用高性能计算机并采用并行计算,效率大幅提升。
但是为了节约用电,请不要这么做。不仅如此,这种方式可能无法让你在有限时间的比赛中提交答案。(狗头)
import
base64
import
lzma
O
=
b
"..."
with
open
(
"object"
,
"wb"
) as f:
f.write(lzma.decompress(base64.a85decode(O)))
import
base64
import
lzma
O
=
b
"..."
with
open
(
"object"
,
"wb"
) as f:
f.write(lzma.decompress(base64.a85decode(O)))
strings object |
grep
GCC
strings object |
grep
GCC
GCC: (LoongArch GNU toolchain rc1.
0
)
8.3
.
0
GCC: (LoongArch GNU toolchain rc1.
0
)
8.3
.
0
wget http:
//ftp
.loongnix.cn
/toolchain/gcc/release/loongarch/gcc8/toolchain-loongarch64-linux-gnu-cross-830-rc1
.0-2022-04-22.
tar
.xz
tar
-xf toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22.
tar
.xz
pushd
toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22
export
TC_HOME=`
pwd
`
pushd
bin
export
PATH=$PATH:`
pwd
`
popd
popd
wget http:
//ftp
.loongnix.cn
/toolchain/gcc/release/loongarch/gcc8/toolchain-loongarch64-linux-gnu-cross-830-rc1
.0-2022-04-22.
tar
.xz
tar
-xf toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22.
tar
.xz
pushd
toolchain-loongarch64-linux-gnu-cross-830-rc1.0-2022-04-22
export
TC_HOME=`
pwd
`
pushd
bin
export
PATH=$PATH:`
pwd
`
popd
popd
$TC_HOME
/loongarch64-linux-gnu/bin/ld
-plugin $TC_HOME
/libexec/gcc/loongarch64-linux-gnu/8
.3.0
/liblto_plugin
.so -plugin-opt=$TC_HOME
/libexec/gcc/loongarch64-linux-gnu/8
.3.0
/lto-wrapper
-plugin-opt=-fresolution=~
/resolution
.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --sysroot=$TC_HOME
/sysroot
-m elf64loongarch -static -o ~
/output
$TC_HOME
/sysroot/lib64/crt1
.o $TC_HOME
/sysroot/lib64/crti
.o $TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0
/crtbeginT
.o -L$TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0 -L$TC_HOME
/lib/gcc
-L$TC_HOME
/sysroot/lib64
-L$TC_HOME
/loongarch64-linux-gnu/lib
-L$TC_HOME
/sysroot/lib
~
/object
--defsym=v0=0 --defsym=v1=0 --defsym=v2=0 --defsym=v3=0 --defsym=v4=0 --defsym=v5=0 --defsym=v6=0 --defsym=v7=0 --start-group -lgcc -lgcc_eh -lc --end-group $TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0
/crtend
.o $TC_HOME
/sysroot/lib64/crtn
.o
$TC_HOME
/loongarch64-linux-gnu/bin/ld
-plugin $TC_HOME
/libexec/gcc/loongarch64-linux-gnu/8
.3.0
/liblto_plugin
.so -plugin-opt=$TC_HOME
/libexec/gcc/loongarch64-linux-gnu/8
.3.0
/lto-wrapper
-plugin-opt=-fresolution=~
/resolution
.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --sysroot=$TC_HOME
/sysroot
-m elf64loongarch -static -o ~
/output
$TC_HOME
/sysroot/lib64/crt1
.o $TC_HOME
/sysroot/lib64/crti
.o $TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0
/crtbeginT
.o -L$TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0 -L$TC_HOME
/lib/gcc
-L$TC_HOME
/sysroot/lib64
-L$TC_HOME
/loongarch64-linux-gnu/lib
-L$TC_HOME
/sysroot/lib
~
/object
--defsym=v0=0 --defsym=v1=0 --defsym=v2=0 --defsym=v3=0 --defsym=v4=0 --defsym=v5=0 --defsym=v6=0 --defsym=v7=0 --start-group -lgcc -lgcc_eh -lc --end-group $TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0
/crtend
.o $TC_HOME
/sysroot/lib64/crtn
.o
loongarch64-linux-gnu-objdump --disassemble object
loongarch64-linux-gnu-objdump --disassemble object
loongarch64-linux-gnu-objdump --reloc object
loongarch64-linux-gnu-objdump --reloc object
frida -f $TC_HOME
/loongarch64-linux-gnu/bin/ld
-- -plugin $TC_HOME
/libexec/gcc/loongarch64-linux-gnu/8
.3.0
/liblto_plugin
.so -plugin-opt=$TC_HOME
/libexec/gcc/loongarch64-linux-gnu/8
.3.0
/lto-wrapper
-plugin-opt=-fresolution=~
/resolution
.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --sysroot=$TC_HOME
/sysroot
-m elf64loongarch -static -o ~
/output
$TC_HOME
/sysroot/lib64/crt1
.o $TC_HOME
/sysroot/lib64/crti
.o $TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0
/crtbeginT
.o -L$TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0 -L$TC_HOME
/lib/gcc
-L$TC_HOME
/sysroot/lib64
-L$TC_HOME
/loongarch64-linux-gnu/lib
-L$TC_HOME
/sysroot/lib
~
/object
--defsym=v0=0 --defsym=v1=0 --defsym=v2=0 --defsym=v3=0 --defsym=v4=0 --defsym=v5=0 --defsym=v6=0 --defsym=v7=0 --start-group -lgcc -lgcc_eh -lc --end-group $TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0
/crtend
.o $TC_HOME
/sysroot/lib64/crtn
.o
frida -f $TC_HOME
/loongarch64-linux-gnu/bin/ld
-- -plugin $TC_HOME
/libexec/gcc/loongarch64-linux-gnu/8
.3.0
/liblto_plugin
.so -plugin-opt=$TC_HOME
/libexec/gcc/loongarch64-linux-gnu/8
.3.0
/lto-wrapper
-plugin-opt=-fresolution=~
/resolution
.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_eh -plugin-opt=-pass-through=-lc --sysroot=$TC_HOME
/sysroot
-m elf64loongarch -static -o ~
/output
$TC_HOME
/sysroot/lib64/crt1
.o $TC_HOME
/sysroot/lib64/crti
.o $TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0
/crtbeginT
.o -L$TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0 -L$TC_HOME
/lib/gcc
-L$TC_HOME
/sysroot/lib64
-L$TC_HOME
/loongarch64-linux-gnu/lib
-L$TC_HOME
/sysroot/lib
~
/object
--defsym=v0=0 --defsym=v1=0 --defsym=v2=0 --defsym=v3=0 --defsym=v4=0 --defsym=v5=0 --defsym=v6=0 --defsym=v7=0 --start-group -lgcc -lgcc_eh -lc --end-group $TC_HOME
/lib/gcc/loongarch64-linux-gnu/8
.3.0
/crtend
.o $TC_HOME
/sysroot/lib64/crtn
.o
import
csv
source
=
[]
mem_delta
=
0x01FFFBB5
reloca_foff
=
0x0000045B
reloca_size
=
0x00245940
reloca_start
=
mem_delta
+
reloca_foff
reloca_end
=
reloca_start
+
reloca_size
value_addrs
=
[]
addend_writable_addrs
=
[]
for
i
in
range
(reloca_start, reloca_end,
24
):
value_addrs.append(i
+
16
)
addend_writable_addrs.append(i
+
16
)
addend_writable_addrs.append(i
+
20
)
with
open
(
"reloc.csv"
) as f:
c
=
csv.reader(f)
c
=
list
(c)
assert
len
(c)
=
=
len
(value_addrs)
modified_value_addrs
=
[]
SP
=
0
for
(OFFSET,
TYPE
, VALUE), VALUE_ADDR
in
zip
(c, value_addrs):
if
TYPE
in
[
"R_LARCH_SOP_PUSH_PCREL"
,
"R_LARCH_SOP_PUSH_PLT_PCREL"
]:
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_PUSH_ABSOLUTE"
:
if
VALUE.startswith(
"*ABS*"
):
t1
=
VALUE_ADDR
t2
=
VALUE_ADDR
+
4
lpart
=
t1
in
modified_value_addrs
rpart
=
t2
in
modified_value_addrs
if
lpart
or
rpart:
assert
lpart
and
rpart
source.append(
f
"STK_U64_{SP} = ((uint64_t)LOC_U32_{t2:08X} << 32) | LOC_U32_{t1:08X};"
)
SP
+
=
1
else
:
if
VALUE
=
=
"*ABS*"
:
source.append(f
"STK_U64_{SP} = 0;"
)
SP
+
=
1
else
:
source.append(f
"STK_U64_{SP} = {VALUE[5:]};"
)
SP
+
=
1
elif
VALUE
in
[
"v0"
,
"v1"
,
"v2"
,
"v3"
,
"v4"
,
"v5"
,
"v6"
,
"v7"
]:
source.append(f
"STK_U64_{SP} = flag_{VALUE};"
)
SP
+
=
1
else
:
assert
False
, f
"Unknown VALUE {VALUE}"
elif
TYPE
=
=
"R_LARCH_SOP_SR"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} >> {opr2};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_SL"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} << {opr2};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_SUB"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} - {opr2};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_PUSH_DUP"
:
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1};"
)
SP
+
=
1
source.append(f
"STK_U64_{SP} = {opr1};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_AND"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} & {opr2};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_ADD"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} + {opr2};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_IF_ELSE"
:
SP
-
=
1
opr3
=
f
"STK_U64_{SP}"
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} ? {opr2} : {opr3};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_NOT"
:
SP
-
=
1
v
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = !{v};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_ASSERT"
:
SP
-
=
1
v
=
f
"STK_U64_{SP}"
source.append(f
"assert({v});"
)
elif
TYPE
in
[
"R_LARCH_SOP_POP_32_S_5_20"
,
"R_LARCH_SOP_POP_32_S_10_12"
,
"R_LARCH_SOP_POP_32_S_0_10_10_16_S2"
,
]:
SP
-
=
1
elif
TYPE
=
=
"R_LARCH_SOP_POP_32_U"
:
t
=
int
(OFFSET,
16
)
if
reloca_start <
=
t < reloca_end:
assert
t
in
addend_writable_addrs
modified_value_addrs.append(t)
SP
-
=
1
v
=
f
"STK_U64_{SP}"
source.append(f
"LOC_U32_{t:08X} = {v} & 0xffffffff;"
)
else
:
SP
-
=
1
elif
TYPE
=
=
"R_LARCH_ADD32"
:
pass
else
:
assert
False
, f
"Unknown TYPE {TYPE}"
for
i
in
range
(
4
):
print
(f
"uint64_t STK_U64_{i} = 0;"
)
print
()
for
i
in
modified_value_addrs:
print
(f
"uint32_t LOC_U32_{i:08X};"
)
print
()
for
i
in
source:
print
(i)
import
csv
source
=
[]
mem_delta
=
0x01FFFBB5
reloca_foff
=
0x0000045B
reloca_size
=
0x00245940
reloca_start
=
mem_delta
+
reloca_foff
reloca_end
=
reloca_start
+
reloca_size
value_addrs
=
[]
addend_writable_addrs
=
[]
for
i
in
range
(reloca_start, reloca_end,
24
):
value_addrs.append(i
+
16
)
addend_writable_addrs.append(i
+
16
)
addend_writable_addrs.append(i
+
20
)
with
open
(
"reloc.csv"
) as f:
c
=
csv.reader(f)
c
=
list
(c)
assert
len
(c)
=
=
len
(value_addrs)
modified_value_addrs
=
[]
SP
=
0
for
(OFFSET,
TYPE
, VALUE), VALUE_ADDR
in
zip
(c, value_addrs):
if
TYPE
in
[
"R_LARCH_SOP_PUSH_PCREL"
,
"R_LARCH_SOP_PUSH_PLT_PCREL"
]:
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_PUSH_ABSOLUTE"
:
if
VALUE.startswith(
"*ABS*"
):
t1
=
VALUE_ADDR
t2
=
VALUE_ADDR
+
4
lpart
=
t1
in
modified_value_addrs
rpart
=
t2
in
modified_value_addrs
if
lpart
or
rpart:
assert
lpart
and
rpart
source.append(
f
"STK_U64_{SP} = ((uint64_t)LOC_U32_{t2:08X} << 32) | LOC_U32_{t1:08X};"
)
SP
+
=
1
else
:
if
VALUE
=
=
"*ABS*"
:
source.append(f
"STK_U64_{SP} = 0;"
)
SP
+
=
1
else
:
source.append(f
"STK_U64_{SP} = {VALUE[5:]};"
)
SP
+
=
1
elif
VALUE
in
[
"v0"
,
"v1"
,
"v2"
,
"v3"
,
"v4"
,
"v5"
,
"v6"
,
"v7"
]:
source.append(f
"STK_U64_{SP} = flag_{VALUE};"
)
SP
+
=
1
else
:
assert
False
, f
"Unknown VALUE {VALUE}"
elif
TYPE
=
=
"R_LARCH_SOP_SR"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} >> {opr2};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_SL"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} << {opr2};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_SUB"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} - {opr2};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_PUSH_DUP"
:
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1};"
)
SP
+
=
1
source.append(f
"STK_U64_{SP} = {opr1};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_AND"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
source.append(f
"STK_U64_{SP} = {opr1} & {opr2};"
)
SP
+
=
1
elif
TYPE
=
=
"R_LARCH_SOP_ADD"
:
SP
-
=
1
opr2
=
f
"STK_U64_{SP}"
SP
-
=
1
opr1
=
f
"STK_U64_{SP}"
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!