KCTF 看雪学院
专家点评
下面由深信服首席安全研究员:彭峙酿博士给大家带来第二道赛题的点评。
彭峙酿博士:第二题是一个有趣的迷宫吃豆游戏。根据用户输入让玩家从“S”开始走,要求走的过程每一步都要吃到豆子,走完后吃掉了迷宫中的所有豆子。选手通过逆向分析出程序原理,然后通过编程自动化完成游戏。难度不高,但是兼顾比赛性和游戏性,同时考察了大家的逆向分析和编程解决问题的能力。
出题团队简介
lelfei(HU1战队队长),crack爱好者。学生时代对电脑产生了浓厚的兴趣,经历了很长时间的游戏沉迷后,开始慢慢转向学习技术,工作后自学了ASM、VB、VC、HTML、ASP、Python等语言的入门。对单机的逆向分析、算法比较感兴趣。
赛题设计思路
设计说明:
运动方向的定义:
赛题解析
本赛题解析由看雪论坛 HHHso 给出:
【0x100】望
【0x200】切
【0x210】
int __cdecl main(int argc, const char **argv, const char **envp)
{
char lv_kc; // al
int lv_ki; // esi
int lv_kv; // ecx
int lv_ivm; // edx
int lv_iv; // eax
unsigned int lv_col; // ecx
int lv_opAB; // eax
int bsec; // edx
int lv_is_odd_row; // eax
char *lv_rAtRowCol; // eax
char *lv_xrow_p; // eax
int lv_sbz; // edx
char *lv_xrow_end; // ecx
int lv_is_even_row; // eax
int lv_is_even_row_; // eax
int lv_is_odd_row_; // eax
int lv_ivm_; // [esp+1Ch] [ebp-60h]
unsigned int lv_row; // [esp+20h] [ebp-5Ch]
unsigned int lv_cur_col; // [esp+24h] [ebp-58h]
char lv_tc; // [esp+2Bh] [ebp-51h]
int lv_radix; // [esp+2Ch] [ebp-50h]
char lv_key[76]; // [esp+30h] [ebp-4Ch] BYREF
Hi_init();
Hi_cout((int)&Hi_stdout, "Input your code: ");
Hi_cin((int)&Hi_stdin, lv_key);
if ( strlen(lv_key) <= 0x30 )
{
lv_kc = lv_key[0];
if ( lv_key[0] )
{
lv_ki = 0;
lv_row = 0;
lv_cur_col = 0;
lv_radix = Hi_radix;
lv_tc = Hi_RN_tbl[0];
lbl_con:
if ( lv_radix > 0 )
{
lv_kv = 0;
if ( lv_tc == lv_kc )
{
lbl_cv:
lv_ivm = (lv_ki + lv_kv / 6) % 6;
lv_iv = lv_kv + lv_ki;
lv_col = lv_cur_col;
lv_ivm_ = lv_ivm;
lv_opAB = 5 - lv_iv % 6;
for ( bsec = 0; ; bsec = 1 )
{
switch ( lv_opAB )
{
case 1:
++lv_col;
break;
case 2:
lv_is_even_row = (lv_row++ & 1) == 0;
lv_col += lv_is_even_row;
break;
case 3:
lv_is_odd_row = (lv_row++ & 1) != 0;
lv_col -= lv_is_odd_row;
break;
case 4:
--lv_col;
break;
case 5:
lv_is_odd_row_ = (lv_row-- & 1) != 0;
lv_col -= lv_is_odd_row_;
break;
default:
lv_is_even_row_ = (lv_row-- & 1) == 0;
lv_col += lv_is_even_row_;
break;
}
if ( lv_col > 9 )
break;
if ( lv_row > 8 )
break;
lv_rAtRowCol = &Hi_map[lv_row][lv_col];
if ( *lv_rAtRowCol )
break;
*lv_rAtRowCol = 1;
if ( bsec == 1 )
{
++lv_ki;
lv_cur_col = lv_col;
lv_kc = lv_key[lv_ki];
if ( lv_kc )
goto lbl_con;
goto lbl_end;
}
lv_opAB = lv_ivm_;
}
}
else
{
while ( lv_radix != ++lv_kv )
{
if ( Hi_RN_tbl[lv_kv] == lv_kc )
goto lbl_cv;
}
}
}
}
else
{
lbl_end:
lv_xrow_p = (char *)Hi_map;
lv_sbz = 0;
do
{
lv_xrow_end = lv_xrow_p + 10;
do
lv_sbz += *lv_xrow_p++ == 0;
while ( lv_xrow_end != lv_xrow_p );
}
while ( &Hi_map_end != lv_xrow_end );
if ( !lv_sbz )
{
Hi_cout_buf_len(&Hi_stdout, "Good job!", 9);
xdtor(&Hi_stdout);
return 0;
}
}
}
Hi_cout_buf_len(&Hi_stdout, "Try again...", 12);
xdtor(&Hi_stdout);
return 0;
}
【0x220】
#lv_key
RN36_tbl = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'
for lv_ki,lv_kc in enumerate(lv_key)
lv_kv = RN36_tbl.index(lv_kc)
import ida_bytes
def get_map(ea = 0x4B7080):
print("--"*20)
m = ida_bytes.get_bytes(ea,90)
m = m.replace(b'\x00',b'0').replace(b'\x01',b'1').decode()
for i in range(0,len(m),10):
print("{} {}".format((i//10)&1,m[i:i+10]))
get_map()
【0x230】未选择的路
在main代码中
opA = 5 - (ki+kv)%6
opB = (ki+kv/6) %6
所以知道了key[ki]对应产生的两个操作码opA,opB和ki就可以简单逆推得到kv,从而得到key[ki],这里原则上可以使用numpy的solve方法解方程。
但考虑kv取值为[0,36),直接枚举求解。
def get_kv(ki,a,b):
for x in range(36):
if 5-((x+ki)%6)==a and (ki+(x//6))%6==b:
return x
opName2opCode = {
"R":1,
"0DR":2,
"1D":2,
"0D":3,
"1DL":3,
"L":4,
"0U":5,
"1UL":5,
"0UR":0,
"1U":0
}
def get_kv(ki,a,b):
for x in range(36):
if 5-((x+ki)%6)==a and (ki+(x//6))%6==b:
return x
opABs = "R,0DR;1DL,L;0D,1D;R,0DR;1DL,L;0D,1D;R,R;0UR,R;1D,R;0UR,1U;0U,1U;0U,L;1DL,L;0U,1U;0U,1U;R,0DR;R,1U;R,0DR;R,1D;0D,L;1DL,0DR;1D,0D;1D,R"
opABs = opABs.split(";")
key = ''
for i,opAB in enumerate(opABs):
opA,opB = opAB.split(',')
kv = get_kv(i,opName2opCode[opA],opName2opCode[opB])
print(i,opAB,(opName2opCode[opA],opName2opCode[opB]),kv)
key += '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'[kv]
print(key)
GJ0V4LA4VKEVQZSVCNGJ00N
主办方
第三题正在火热进行中,
球分享
球点赞
球在看