-
-
[原创]看雪 2023 KCTF 年度赛 第六题 至暗时刻 解题过程(数独)
-
发表于: 2023-9-14 23:06 9475
-
直接扔调试器运行,运行后黑框框没有内容。换终端里运行,提示请输入。
意味着有反调试。
于是打开反反调插件,SyllaHide默认配置随便选第1个、第2个或者SharpOD全部打勾。
这时在调试器运行,能执行到提示请输入。
静态分析加载完,进去main就能看见入口sub被创建线程。
跟进去,四处查看,初步情况如下:
字符串异或加密,解密密钥带在解密函数(0x1400013B0)入参。
有一些重要函数是放了一些4字节常量然后走到syscall,且无法输出伪代码。
考虑到字符串不多,先对解密函数返回的ret指令(0x14000144E)下日志断点,输出明文字符串查看。
测试运行一次,字符串并不多:
将那个syscall指令的函数设置成__fastcall 4个参数,这时基本都正常输出伪代码。如果其他函数出现参数特别多的情况,也先给他降到4个。
进一步分析syscall涉及的函数:
考虑到涉及函数也不是很多,还是采用日志断点输出的方法。
在0x140002818:populate_syscall_index函数中,
下2个日志断点:
重来运行一次,日志中就得到了全部函数名与hash的对应。
复制日志到文本备用。
根据对应关系,重命名部分函数:
ZwAllocateVirtualMemory
ZwWriteVirtualMemory
ZwCreateThreadEx
ZwQueueApcThread_140002E4E
同时调整这些函数的参数数量,重点是ZwQueueApcThread,它有5个参数。
把syscall函数都重命名之后,流程变得清晰。
在ZwQueueApcThread pMem+500 call处(0x140001D9C)下断点,断下后对第2个参数即RDX寄存器跳转到反汇编窗口,对要执行的shellcode入口下断点,运行即断在shellcode入口。
移除入口处断点,单步步进一次,那个call很神奇,call到的自己的最后一个机器码。
把下一条指令地址压栈备用的同时,使得一般的反汇编器不能从call那个地方反汇编下去。
因而需要从call指令的最后一个字节开始反汇编。
此时用savedata保存shellcode单独分析,从pMem处开始,长度500+2347 = 0xb1f。
比如:
然后把sc.bin按dump时的pMem地址作为基址加载到分析工具。
后来发现在此处dump还不合适,加载之后发现call目标不再范围内。
单步研究后发现,紧接着代码还有一处循环自修改。
改为在循环之后dump,得到的shellcode即可全部正常加载出代码。
shellcode加载之后,发现有动态定位API的call。
shellcode需要动态定位API才能位置无关。
无需要跟进分析,步过call,根据返回值显示的函数名称信息,重命名局部函数指针即可。
感兴趣的可以参考很久以前写过的动态定位API文章:
https://bbs.kanxue.com/thread-203319.htm 。
重命名局部函数指针之后,逻辑就很清晰了。
checkall里面依次调用check1,check2,check3,且3个函数都调用同一个公共check函数。
分析后check原型大致如下:
后两个入参为十位、个位,返回百位为正确分支。
看了check1 2 3的检查逻辑之后,发现加起来刚好就是数独的规则。
代码还原如下:
并且通过对check的分析,得知先前被写到pMem开头的字符串结构与每段的处理:
现在就缺少60个百位,并且这60个百位与已知的21个百位,
按所在3位数的十位与个位作为二维索引,建立出一个数独。
求解出数独,再把求出的60个百位与各自对应的十位、个位组成3位数。即可得出flag。
如下python代码实现。
安装三方库后可直接运行,输出flag。
输出:
dec_str {utf8@cax}
dec_str {utf8@cax}
dec_str Please enter your key:
dec_str kctf
dec_str kernel32.dll
dec_str RtlFillMemory
dec_str Please enter your key:
dec_str kctf
dec_str kernel32.dll
dec_str RtlFillMemory
第
1
层:
0x140002A10
:call_syscall,通过syscall指令发起系统调用。
第
2
层:
0x1400029C4
:get_syscall_index 该函数入参为先前赋值的
4
字节常量,用来获取对应的系统调用号。
第
3
层:
0x140002818
:populate_syscall_index 动态从ntdll.dll建立好系统调用索引查询表。
第
1
层:
0x140002A10
:call_syscall,通过syscall指令发起系统调用。
第
2
层:
0x1400029C4
:get_syscall_index 该函数入参为先前赋值的
4
字节常量,用来获取对应的系统调用号。
第
3
层:
0x140002818
:populate_syscall_index 动态从ntdll.dll建立好系统调用索引查询表。
fname
=
{utf8@r9}
fname
=
{utf8@r9}
hash
=
0x
{x:ebx}
hash
=
0x
{x:ebx}
savedata c:\temp\sc.
bin
,
0x0000023676EB0000
,
0xb1f
savedata c:\temp\sc.
bin
,
0x0000023676EB0000
,
0xb1f
__int64 __fastcall check(char
*
Sz,
int
_shiwei,
int
__gewei)
__int64 __fastcall check(char
*
Sz,
int
_shiwei,
int
__gewei)
char __fastcall checkall(char
*
Sz)
{
unsigned
int
i;
int
j;
int
k;
i
=
0
;
while
( check1(Sz, i) && check2(Sz, i) )
{
if
( (
int
)
+
+
i >
=
9
)
{
j
=
0
;
label_cont:
k
=
0
;
while
( check3(Sz, (unsigned
int
)j, (unsigned
int
)k) )
{
k
+
=
3
;
if
( k >
=
9
)
{
j
+
=
3
;
if
( j <
9
)
goto label_cont;
return
1
;
}
}
return
0
;
}
}
return
0
;
}
char __fastcall check1(char
*
Input
, __int64 i)
{
int
j;
int
i_;
unsigned
int
oi;
unsigned
int
dws[
9
];
memset(dws,
0
, sizeof(dws));
j
=
0
;
i_
=
i;
while
(
1
)
{
oi
=
check(
Input
, i_, j)
-
1
;
if
( oi >
8
|| dws[oi] )
break
;
+
+
j;
dws[oi]
=
1
;
if
( j >
=
9
)
return
1
;
}
return
0
;
}
char __fastcall check2(char
*
Sz, __int64 i)
{
int
j;
int
i_;
unsigned
int
oi;
unsigned
int
dws[
9
];
memset(dws,
0
, sizeof(dws));
j
=
0
;
i_
=
i;
while
(
1
)
{
oi
=
check(Sz, j, i_)
-
1
;
if
( oi >
8
|| dws[oi] )
break
;
+
+
j;
dws[oi]
=
1
;
if
( j >
=
9
)
return
1
;
}
return
0
;
}
char __fastcall check3(char
*
Input
, __int64 j, __int64 k)
{
int
addj;
int
k_;
int
j_;
int
addk;
unsigned
int
oi;
unsigned
int
dws[
9
];
memset(dws,
0
, sizeof(dws));
addj
=
0
;
k_
=
k;
j_
=
j;
while
(
2
)
{
for
( addk
=
0
; addk <
3
;
+
+
addk )
{
oi
=
check(
Input
, addj
+
j_, addk
+
k_)
-
1
;
if
( oi >
8
|| dws[oi] )
return
0
;
dws[oi]
=
1
;
}
if
(
+
+
addj <
3
)
continue
;
break
;
}
return
1
;
}
char __fastcall checkall(char
*
Sz)
{
unsigned
int
i;
int
j;
int
k;
i
=
0
;
while
( check1(Sz, i) && check2(Sz, i) )
{
if
( (
int
)
+
+
i >
=
9
)
{
j
=
0
;
label_cont:
k
=
0
;
while
( check3(Sz, (unsigned
int
)j, (unsigned
int
)k) )
{
k
+
=
3
;
if
( k >
=
9
)
{
j
+
=
3
;
if
( j <
9
)
goto label_cont;
return
1
;
}
}
return
0
;
}
}
return
0
;
}
char __fastcall check1(char
*
Input
, __int64 i)
{
int
j;
int
i_;
unsigned
int
oi;
unsigned
int
dws[
9
];
memset(dws,
0
, sizeof(dws));
j
=
0
;
i_
=
i;
while
(
1
)
{
oi
=
check(
Input
, i_, j)
-
1
;
if
( oi >
8
|| dws[oi] )
break
;
+
+
j;
dws[oi]
=
1
;
if
( j >
=
9
)
return
1
;
}
return
0
;
}
char __fastcall check2(char
*
Sz, __int64 i)
{
int
j;
int
i_;
unsigned
int
oi;
unsigned
int
dws[
9
];
memset(dws,
0
, sizeof(dws));
j
=
0
;
i_
=
i;
while
(
1
)
{
oi
=
check(Sz, j, i_)
-
1
;
if
( oi >
8
|| dws[oi] )
break
;
+
+
j;
dws[oi]
=
1
;
if
( j >
=
9
)
return
1
;
}
return
0
;
}
char __fastcall check3(char
*
Input
, __int64 j, __int64 k)
{
int
addj;
int
k_;
int
j_;
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)