[RCTF2019]crack
水
直接弹窗字符串定位不说了
sub_94E558(this, 1 );
v2 = *(this + 52 );
if ( *(v2 - 4 ) > 1 )
sub_9413E0(this + 52 , *(v2 - 12 ));
v22 = 3 * *(this + 58 );
v21 = *(this + 56 );
*(this + 53 ) = *(this + 52 );
memset(v21, 0 , v22);
len = *(*(this + 52 ) - 12 ); // len
_04DE2020 = *(this + 54 ); // 04DE2020
cows = 0 ;
v6 = 0 ;
rows = 0 ;
v27 = 0x100758E540FLL;
len_1 = len ; // 输入长度
base = _04DE2020;
len_4 = 0 ;
if ( len <= 0 )
return MessageBoxA(nullptr, "Try Again!" , "tip" , MB_OK);
do
{
len_3 = *(this + 58 ); // 512
if ( rows >= len_3 )
break ;
if ( rows < 0 || (v9 = *(this + 52 ), rows > *(v9 - 12 )) )
sub_941560(0x80070057 );
n_0_ = *(v9 + 2 * rows); // input
if ( n_0_ != '0' && n_0_ != '1' )
return MessageBoxA(nullptr, "Input no accept!" , "tip" , MB_OK);
if ( n_0_ == '1' )
len_4 = ++cows;
v11 = *(base + 4 * (cows + rows * len_3));
v12 = v27 < v11;
LODWORD(v27) = v27 - v11;
cows = len_4;
_04DE2020 = base;
HIDWORD(v27) -= v12;
if ( rows > len_4 )
{
*(v6 + *(this + 56 )) = *(base + 4 * (rows + len_4 * len_3));
*(v6 + *(this + 56 ) + 1 ) = *(base + 4 * (rows + len_4 * *(this + 58 )) + 1 );
*(v6 + *(this + 56 ) + 2 ) = *(base + 4 * (rows + len_4 * *(this + 58 )) + 2 );
cows = len_4;
v6 += 3 ;
}
++rows;
}
while ( rows < len_1 );
if ( v27 > 0 )
return MessageBoxA(nullptr, "Try Again!" , "tip" , MB_OK);
根据这一行,并且对cows,和rows的限制可以知道base是一个512*512的二维矩阵,并且每一元素占4字节,只有当输入为1的时候cows才会增加,rows无条件增加,每次取出一个数,然后v27减去他,最后只有v27<=0的时候输入合法
v11 = *(base + 4 * (cows + rows * len_3));
所以现在让ai写一个脚本获取base数组
import idaapi
import idc
import os
start_address = 0x042F4020
count = 512 * 512
data = []
for i in range (count):
value = idaapi.get_wide_dword(start_address + i * 4 )
data.append(str (value))
desktop = os.path.expanduser("~/Desktop" )
output_path = os.path.join(desktop, "extracted_data.txt" )
with open (output_path, "w" ) as f:
f.write(" " .join(data))
print (f"已读取 {len (data)} 个int值,保存到 {output_path} " )
数组获取到了,然后根据输入的0或者1代表两种操作,0是往下走,1是往右下走,所以可以写个动态规划求解
if __name__ == '__main__' :
result = []
found = False
end_x, end_y = -1 , -1
TARGET = 0x100758E540F
N = 512
with open ("./extracted_data.txt" , "r" ) as f:
all_nums = [int (x) for x in f.read().split()]
all_len = len (all_nums)
if len (all_nums) != N * N:
print ("非法输入" )
exit(0 )
matrix = [all_nums[i * N:(i + 1 ) * N] for i in range (N)]
dp = [[0 ] * (N + 1 ) for j in range (N + 1 )]
choose = [[0 ] * (N + 1 ) for j in range (N + 1 )]
dp[0 ][0 ] = matrix[0 ][0 ]
for i in range (1 , N):
dp[i][0 ] = dp[i - 1 ][0 ] + matrix[i][0 ]
for i in range (1 , N):
dp[0 ][i] = dp[0 ][i - 1 ] + matrix[0 ][i]
for i in range (1 , N):
for j in range (i + 1 ):
current_val = matrix[i][j]
left_up = dp[i - 1 ][j - 1 ] + current_val
up = dp[i - 1 ][j] + current_val
if left_up > up:
choose[i][j] = 1
dp[i][j] = left_up
else :
choose[i][j] = 0
dp[i][j] = up
if dp[i][j] >= TARGET:
end_x, end_y = i, j
found = True
break
if found:
break
while end_x >= 0 :
value = choose[end_x][end_y]
result.append(str (value))
if value == 1 :
end_x -= 1
end_y -= 1
else :
end_x -= 1
input_str = "" .join(reversed (result))
print (input_str)
000000000 10101000000000111100111111110100111100101001000101010010011101100111101011111111111111111001110111011011000000101110111001111100100011000000000000110001111110100000000001101110111010101011111000101110000011000111001110000000000000000000000011001000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100011111110000100111000000000000000000000000000000010000000000000001000001100000000000000101000000000100000010000000000000000010000000000000000000000
然后运行到这里
v13(this, &v27, v23);
跟进去发现是一个vmp,n128_3很明显是一个pc指针
LODWORD(n128) 一会高字节,一会低字节很奇怪,看一下汇编,猜测可能是两个虚拟寄存器
HIDWORD(n128)
debug103:03CF44BA mov dword ptr [ebp+var_274], ecx
debug103:03CF428E mov dword ptr [ebp+var_274+4 ], eax
if ( n128 < 128 ) 有对寄存器的约束,所以大胆猜测开了128 个int 的虚拟寄存器,而且是从n128开始的
int __stdcall sub_3CF4020(_DWORD *a1, int a2, int a3)
{
while ( 1 )
{
result = n128_3 + a3;
if ( !*(n128_3 + a3) )
return result;
v21 += (*(n128_3 + a3) - 48 ) << (n128_3 % 6 );
result = n128_3 / 6 ;
if ( n128_3 % 6 != 5 )
goto LABEL_70;
switch ( v21 )
{
case 0 :
v21 = 0 ;
for ( i = 0 ; i < 24 ; ++i )
v21 += (*(++n128_3 + a3) - 48 ) << i;
LODWORD(n128) = v21;
goto LABEL_69;
case 1 :
v21 = 0 ;
for ( j = 0 ; j < 24 ; ++j )
v21 += (*(++n128_3 + a3) - 48 ) << j;
HIDWORD(n128) = v21;
goto LABEL_69;
case 2 :
LODWORD(n128) = *(v29 + 2 * v22++);
LABEL_69:
v21 = 0 ;
LABEL_70:
++n128_3;
break ;
case 3 :
HIDWORD(n128) = n128;
goto LABEL_69;
case 4 :
n128_2 = n128_1;
goto LABEL_69;
case 5 :
if ( n128 < 128 )
*(&n128 + n128) = HIDWORD(n128);
goto LABEL_69;
case 6 :
if ( SHIDWORD(n128) < 128 )
LODWORD(n128) = *(&n128 + HIDWORD(n128));
goto LABEL_69;
case 7 :
if ( n128_1 < 128 )
*(&n128 + n128_1) = n128_2;
goto LABEL_69;
case 8 :
if ( n128_2 < 128 )
n128_1 = *(&n128 + n128_2);
goto LABEL_69;
case 9 :
LODWORD(n128) = *(n128 + 4 * HIDWORD(n128));
goto LABEL_69;
case 10 :
*(n128 + 4 * HIDWORD(n128)) = v9;
goto LABEL_69;
case 11 :
LODWORD(n128) = HIDWORD(n128) + n128;
goto LABEL_69;
case 12 :
LODWORD(n128) = n128 - HIDWORD(n128);
goto LABEL_69;
case 13 :
LODWORD(n128) = HIDWORD(n128) * n128;
goto LABEL_69;
case 14 :
LODWORD(n128) = n128 / SHIDWORD(n128);
goto LABEL_69;
case 15 :
LODWORD(n128) = HIDWORD(n128) & n128;
goto LABEL_69;
case 16 :
LODWORD(n128) = HIDWORD(n128) | n128;
goto LABEL_69;
case 17 :
LODWORD(n128) = HIDWORD(n128) ^ n128;
goto LABEL_69;
case 18 :
LODWORD(n128) = n128 << SBYTE4(n128);
goto LABEL_69;
case 19 :
LODWORD(n128) = n128 >> SBYTE4(n128);
goto LABEL_69;
case 20 :
LODWORD(n128) = n128 > SHIDWORD(n128);
goto LABEL_69;
case 21 :
LODWORD(n128) = n128 < SHIDWORD(n128);
goto LABEL_69;
case 22 :
LODWORD(n128) = n128 == HIDWORD(n128);
goto LABEL_69;
case 23 :
LODWORD(n128) = n128 != HIDWORD(n128);
goto LABEL_69;
case 24 :
LODWORD(n128) = n128_3;
goto LABEL_69;
case 25 :
n128_3 = n128;
v21 = 0 ;
break ;
case 26 :
if ( n128 )
goto LABEL_69;
n128_3 = HIDWORD(n128);
v21 = 0 ;
break ;
default:
return result;
}
}
}
而且在堆栈窗口也有opcode,直接拿下来
001DF138 006D2764 sub_6D25E0+184 → cmp dword ptr [esp+28h+v27], ebx
001DF13C 001DF984 Stack[0000569C] → 008156C4 .rdata → 006DA342 sub_6DA342 → mov eax, offset off_7ECED8; "CDialogEx"
001DF140 001DF160 Stack[0000569C] → FFFFFFFF
001DF144 00814970 .rdata → "0000000101011001000000000000001100000100000101101000000000110000"
001DF148 00000111
001DF14C 00815638 .rdata → 006DA7EF sub_6DA7EF → mov eax, offset off_7ED0A4
001DF150 00000001
001DF154 00000090
001DF158 03BE1020 → 009C8171 → FFFFFFFF
然后写出解释器,附件提供
00 mov reg[0 ],0x26a
03 mov reg[1 ],reg[0 ]
02 mov reg[0 ],input [0 ]
01 mov reg[1 ],0x30
0c sub reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x3
05 mov reg[3 ],reg[1 ]
00 mov reg[0 ],0x0
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x2
05 mov reg[2 ],reg[1 ]
00 mov reg[0 ],0x7
03 mov reg[1 ],reg[0 ]
06 mov reg[0 ],reg[7 ]
03 mov reg[1 ],reg[0 ]
07 mov reg[0 ],reg[3 ]
12 shl reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x3
05 mov reg[3 ],reg[1 ]
00 mov reg[0 ],0x1
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x2
05 mov reg[2 ],reg[1 ]
00 mov reg[0 ],0x6
03 mov reg[1 ],reg[0 ]
06 mov reg[0 ],reg[6 ]
07 mov reg[1 ],reg[3 ]
0b add reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x6
05 mov reg[6 ],reg[1 ]
01 mov reg[1 ],0x7
06 mov reg[0 ],reg[7 ]
01 mov reg[1 ],0x1
0b add reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x7
05 mov reg[7 ],reg[1 ]
00 mov reg[0 ],0x0
19 jmp reg[0 ] 0x0
00 mov reg[0 ],0x26a
03 mov reg[1 ],reg[0 ]
02 mov reg[0 ],input [1 ]
01 mov reg[1 ],0x30
0c sub reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x3
05 mov reg[3 ],reg[1 ]
00 mov reg[0 ],0x0
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x2
05 mov reg[2 ],reg[1 ]
00 mov reg[0 ],0x7
03 mov reg[1 ],reg[0 ]
06 mov reg[0 ],reg[7 ]
03 mov reg[1 ],reg[0 ]
07 mov reg[0 ],reg[3 ]
12 shl reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x3
05 mov reg[3 ],reg[1 ]
00 mov reg[0 ],0x1
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x2
05 mov reg[2 ],reg[1 ]
00 mov reg[0 ],0x6
03 mov reg[1 ],reg[0 ]
06 mov reg[0 ],reg[6 ]
07 mov reg[1 ],reg[3 ]
0b add reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x6
05 mov reg[6 ],reg[1 ]
01 mov reg[1 ],0x7
06 mov reg[0 ],reg[7 ]
01 mov reg[1 ],0x1
0b add reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x7
05 mov reg[7 ],reg[1 ]
00 mov reg[0 ],0x0
19 jmp reg[0 ] 0x0
00 mov reg[0 ],0x26a
03 mov reg[1 ],reg[0 ]
02 mov reg[0 ],input [2 ]
01 mov reg[1 ],0x30
0c sub reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x3
05 mov reg[3 ],reg[1 ]
00 mov reg[0 ],0x0
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x2
05 mov reg[2 ],reg[1 ]
00 mov reg[0 ],0x7
03 mov reg[1 ],reg[0 ]
06 mov reg[0 ],reg[7 ]
03 mov reg[1 ],reg[0 ]
07 mov reg[0 ],reg[3 ]
12 shl reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x3
05 mov reg[3 ],reg[1 ]
00 mov reg[0 ],0x1
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x2
05 mov reg[2 ],reg[1 ]
00 mov reg[0 ],0x6
03 mov reg[1 ],reg[0 ]
06 mov reg[0 ],reg[6 ]
07 mov reg[1 ],reg[3 ]
0b add reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x6
05 mov reg[6 ],reg[1 ]
01 mov reg[1 ],0x7
06 mov reg[0 ],reg[7 ]
01 mov reg[1 ],0x1
0b add reg[0 ],reg[1 ]
03 mov reg[1 ],reg[0 ]
00 mov reg[0 ],0x7
05 mov reg[7 ],reg[1 ]
00 mov reg[0 ],0x0
19 jmp reg[0 ] 0x0
00 mov reg[0 ],0x26a
03 mov reg[1 ],reg[0 ]
02 mov reg[0 ],input [3 ]
1a jmp.jz 0x26a
01 mov reg[1 ],0x6
06 mov reg[0 ],reg[6 ]
01 mov reg[1 ],0x7
0d mul reg[0 ],reg[1 ]
01 mov reg[1 ],0xf423f
16 cmp reg[0 ],reg[1 ] reg[0 ]:0x126 reg[1 ]:0xf423f
还原一下算法
inp = "666"
result = 0
for i in range (len (inp)):
result += (ord (inp[i]) - 0x30 ) << i
print (hex (result*7 ))
然后用z3求解一下
from z3 import *
def solve_for_N (N ):
solver = Solver()
digits = [BitVec(f'd_{i} ' , 32 ) for i in range (N)]
for d in digits:
solver.add(And(d >= 0 , d <= 9 ))
if N > 0 :
solver.add(digits[N - 1 ] != 0 )
total = BitVecVal(0 , 32 )
for i in range (N):
total += digits[i] << i
solver.add(total * 7 == 0xf423f )
if solver.check() == sat:
m = solver.model()
return '' .join([str (m[d].as_long()) for d in digits])
return None
N = 1
while True :
result = solve_for_N(N)
if result:
print (f"N={N} : {result} " )
break
N += 1
结果拼接一下就拿到flag了