首页
社区
课程
招聘
[原创][RCTF2019]crack
发表于: 1小时前 46

[原创][RCTF2019]crack

1小时前
46
[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 &lt; 0 || (v9 = *(this + 52), rows &gt; *(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")

# 或者直接保存在当前目录(更简单)
# output_path = "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)
00000000010101000000000111100111111110100111100101001000101010010011101100111101011111111111111111001110111011011000000101110111001111100100011000000000000110001111110100000000001101110111010101011111000101110000011000111001110000000000000000000000011001000010000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000011100011111110000100111000000000000000000000000000000010000000000000001000001100000000000000101000000000100000010000000000000000010000000000000000000000

然后运行到这里

  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 ) 有对寄存器的约束,所以大胆猜测开了128int的虚拟寄存器,而且是从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了


[招生]科锐逆向工程师培训(2026年7月3日实地,远程教学同时开班, 第56期)!

收藏
免费 0
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回