JustCTF 2025 WP

20th,re 4/5

baby-goes-re

go逆向,检查flag长度为53,然后main_CheckFlag gemini秒了

s = "..."
def solve_flag():
    """
    模拟 main.CheckFlag 的逻辑来重建正确的 Flag。
    """

    a3_data = s.encode()

    # 2. 初始化与汇编代码中完全相同的变量
    v10 = 0
    v11 = 0
    flag = []  # 用来存储我们生成的 Flag 字符

    # 3. 循环生成 Flag。我们不知道确切长度,所以先假设一个足够大的长度,
    #    例如 100。如果 Flag 更长,可以增加这个数字。
    for i in range(100):
        # 4. 完全按照汇编的逻辑计算偏移量
        # v16 = v11 + v10
        # v15 = v11 + v10 + 4919  (这是索引)
        offset = v11 + v10 + 4919

        # 检查偏移量是否超出了我们 dump 的数据范围
        if offset >= len(a3_data):
            print(f"计算出的偏移量 ({offset}) 超出数据块大小 ({len(a3_data)}),可能已到达 Flag 末尾。")
            break

        # 5. 从数据块中获取“正确的”字符
        # v18 = *(unsigned __int8 *)(v16 + a3 + 4919);
        expected_char_code = a3_data[offset]
        flag.append(chr(expected_char_code))

        # 6. 完全按照汇编的逻辑更新下一次循环的变量
        # v34 = v11 + v10 + 4920
        # v11 = v33 + 51  (v33 是 v11 的旧值)
        v10_old = v10
        v11_old = v11

        v10 = v11_old + v10_old + 4920
        v11 = v11_old + 51

    # 7. 打印结果
    final_flag = "".join(flag)
    print(f"成功生成 Flag: {final_flag}")
    print(f"Flag 长度: {len(final_flag)}")


if __name__ == "__main__":
    solve_flag()

justCTF{W3lc0m3_t0_R3v1NG!_Th4t_w45nt-s0-B4d-w45_1t?}

Satellite

给的流量包里出现了,post flag

image-20250805192746788.png

直接去elf里找关键字符串,定位到解密函数

int __fastcall sub_45EC(int a1)
{
  int v2; // r4
  int v3; // r6
  int v4; // r5
  int v5; // r7
  int i; // r8
  int j; // r9
  int v8; // r0
  char *v9; // r1
  _BYTE v11[8]; // [sp+0h] [bp-38h] BYREF
  int v12; // [sp+8h] [bp-30h]
  _DWORD v13[11]; // [sp+Ch] [bp-2Ch] BYREF

  sub_D9A4(&unk_20000854);
  sub_D9C4(&unk_20000854, "Decrypting hex data...");
  v2 = *(_DWORD *)(a1 + 8) >> 1;
  v3 = 0;
  v4 = sub_10964(v2);
  v5 = v4 - 1;
  while ( v3 < v2 )
  {
    sub_DCE4(v13, a1, 2 * v3, 2 * v3 + 2);
    *(_BYTE *)++v5 = sub_12594(v13[0], 0, 16);
    ++v3;
    sub_DABA(v13);
  }
  sub_DB50(v11, 85326);
  for ( i = 0; i < v2; i += 8 )
  {
    if ( i + 7 < v2 )
    {
      v13[0] = (*(unsigned __int8 *)(v4 + i + 2) << 16) | (*(unsigned __int8 *)(v4 + i + 1) << 8) | *(unsigned __int8 *)(v4 + i) | (*(unsigned __int8 *)(v4 + i + 3) << 24);
      v13[1] = (*(unsigned __int8 *)(v4 + i + 6) << 16) | (*(unsigned __int8 *)(v4 + i + 5) << 8) | *(unsigned __int8 *)(v4 + i + 4) | (*(unsigned __int8 *)(v4 + i + 7) << 24);
      sub_4594(v13, &dword_14B98[98]);
      for ( j = 0; j != 8; ++j )
      {
        if ( (unsigned __int8)(*((_BYTE *)v13 + j) - 32) <= 0x5Eu )
          sub_DC42(v11);
      }
    }
  }
  sub_10920(v4);
  if ( v12 )
  {
    sub_DD24(v11);
    sub_DC8A(v11, aSatellite, 100, 0);
    dword_200003BC = v12;
    dword_2000031C = 0;
    dword_20000320 = 0;
    sub_42F4(&dword_20000358);
    sub_DB50(v13, "Decrypted message: ");
    v8 = sub_DC58(v13, v11);
    sub_D9B0(&unk_20000854, v8);
    sub_DABA(v13);
    v9 = "Displaying on matrix...";
  }
  else
  {
    v9 = "Failed to decrypt data or no printable characters found.";
  }
  sub_D9C4(&unk_20000854, v9);
  return sub_DABA(v11);
}

sub_4594是TEA解密

__int64 *__fastcall sub_4594(__int64 *result, __int64 *a2)
{
  __int64 v2; // r2
  __int64 v3; // kr00_8
  __int64 v4; // kr08_8
  int v5; // r1
  int v6; // r4

  v2 = *result;
  v3 = a2[1];
  v4 = *a2;
  v5 = -957401312;
  do
  {
    HIDWORD(v2) -= (v3 + 16 * v2) ^ (HIDWORD(v3) + ((unsigned int)v2 >> 5)) ^ (v2 + v5);
    v6 = (v4 + 16 * HIDWORD(v2)) ^ (HIDWORD(v4) + (HIDWORD(v2) >> 5)) ^ (HIDWORD(v2) + v5);
    v5 += 1640531527;
    LODWORD(v2) = v2 - v6;
  }
  while ( v5 );
  *result = v2;
  return result;
}

直接脚本一把梭

from ctypes import c_uint32


def tea_decrypt(r, v, key, delta):
    v0, v1 = c_uint32(v[0]), c_uint32(v[1])
    total = c_uint32(0xC6EF3720)
    for i in range(r):
        v1.value -= ((v0.value << 4) + key[2]) ^ (v0.value + total.value) ^ ((v0.value >> 5) + key[3])
        v0.value -= ((v1.value << 4) + key[0]) ^ (v1.value + total.value) ^ ((v1.value >> 5) + key[1])
        total.value += delta
    return v0.value, v1.value

k = [0x12345678, 0x9ABCDEF0, 0x11111111, 0x22222222]
v = bytes.fromhex("5771D410CFFE844D24B50FCBBBDC1973A7A935E5C3468242950DFCCE94794B067F876A215D96EE09")
v = [int.from_bytes(v[i:i+4], byteorder="little") for i in range(0, len(v), 4)]
delta = 1640531527
for i in range(0, len(v), 2):
    v[i:i+2] = tea_decrypt(32, v[i:i+2], k, delta)
# print(list(map(hex, v)))
v = "".join([int.to_bytes(v[i], byteorder='little', length=4).decode() for i in range(len(v))])
print(v)

justCTF{TheConnection_w4s_interrupted}

slowrun

题目顾名思义,就是跑的慢。这题最快的做法就是一个个函数交给大模型,让他分析是做什么运算的

int __fastcall main(int argc, const char **argv, const char **envp)
{
  __int64 v4; // [rsp+18h] [rbp-18h]
  __int64 v5; // [rsp+20h] [rbp-10h]
  char *ptr; // [rsp+28h] [rbp-8h]

  if ( argc == 2 )
  {
    v4 = sub_1DEC(argv[1], argv, envp);
    if ( v4 )
    {
      if ( (int)sub_21E1(v4, 0LL) >= 0 )
      {
        puts("allocating memory... lots... of... memory...");
        sleep(3u);
        puts("warming up the CPU...");
        sleep(3u);
        puts("increasing fan speed...");
        sleep(3u);
        puts("calculating...");
        v5 = sub_1878(v4);
        ptr = (char *)sub_34CC(v5);
        printf("flag: %s\n", ptr);
        sub_1F42(v5);
        sub_1F42(v4);
        free(ptr);
        return 0;
      }
      else
      {
        fprintf(stderr, "Invalid number: %s\n", argv[1]);
        sub_1F42(v4);
        return 1;
      }
    }
    else
    {
      fprintf(stderr, "Invalid number: %s\n", argv[1]);
      return 1;
    }
  }
  else
  {
    fprintf(stderr, "Usage: %s <number>\n", *argv);
    return 1;
  }
}

由题目给的dockerfile可知传参13337

sub_1DEC是做一些初步处理,转为大整数的字符串形式

_BYTE *__fastcall sub_1DEC(char *a1)
{
  unsigned __int8 *v2; // rax
  _BYTE *v3; // rax
  char *s; // [rsp+8h] [rbp-28h]
  bool v5; // [rsp+12h] [rbp-1Eh]
  unsigned __int8 v6; // [rsp+13h] [rbp-1Dh]
  unsigned int size; // [rsp+14h] [rbp-1Ch]
  size_t size_4; // [rsp+18h] [rbp-18h]
  _BYTE *v9; // [rsp+20h] [rbp-10h]
  _BYTE *ptr; // [rsp+28h] [rbp-8h]

  s = a1;
  v5 = *a1 == 45;    // 负号
  if ( *a1 == 45 )
    s = a1 + 1;
  while ( *s == 48 )    // 开头的0忽略
    ++s;
  size = strlen(s);
  ptr = malloc(0x18uLL);
  if ( !ptr )
    return 0LL;
  ptr[16] = v5;
  *((_DWORD *)ptr + 3) = size;
  *(_QWORD *)ptr = malloc(size);
  if ( *(_QWORD *)ptr )
  {
    size_4 = (size_t)&s[size - 1];
    v9 = *(_BYTE **)ptr;
    while ( size_4 >= (unsigned __int64)s )
    {
      v2 = (unsigned __int8 *)size_4--;
      v6 = *v2;
      if ( *v2 <= 0x2Fu || v6 > 0x39u )        // 0x30-0x39 0-9
      {
        sub_1F42(ptr);
        *__errno_location() = 22;
        return 0LL;
      }
      v3 = v9++;
      *v3 = v6 - 48;
    }
    *((_DWORD *)ptr + 2) = (_DWORD)v9 - *(_DWORD *)ptr;
    return ptr;
  }
  else
  {
    free(ptr);
    return 0LL;
  }
}

sub_21E1结合动态调试和ai分析可知是个把大整数(字符串)转为int,用在作比较的地方,上面就是看是否大于等于0

接着直接看sub_1878,往里面不断分析可以发现多种运算,还原后如下

void **__fastcall sub_1878(__int64 a1)
{
  void **v2; // [rsp+10h] [rbp-20h]
  void **v3; // [rsp+18h] [rbp-18h]
  void **v4; // [rsp+20h] [rbp-10h]
  __int64 v5; // [rsp+28h] [rbp-8h]

  v2 = (void **)func1(a1);
  if ( (int)str2int(a1, 100) <= 0 )
    return v2;
  v3 = (void **)str2big(off_6010);
  v4 = (void **)str2big(off_6018);
  v5 = mod((__int64)v2, (__int64)v3);
  add(v5, v4);
  free1(v3);
  free1(v4);
  free1(v2);
  return (void **)v5;
}
__int64 __fastcall func1(__int64 a1)
{
  int v2; // eax
  __int64 v3; // [rsp+18h] [rbp-28h]
  __int64 v4; // [rsp+20h] [rbp-20h]
  __int64 v5; // [rsp+28h] [rbp-18h]
  __int64 v6; // [rsp+30h] [rbp-10h]
  __int64 v7; // [rsp+38h] [rbp-8h]

  if ( !(unsigned int)str2int(a1, 0) )
    return int2big(2LL);
  if ( (int)str2int(a1, 1) <= 0 )
    return int2big(1LL);
  v3 = int2big(0LL);
  v4 = int2big(73LL);
  mul(v4, a1);
  mul(v4, a1);
  mul(v4, a1);
  mul(v4, a1);
  mul(v4, a1);
  v6 = int2big(8LL);
  mul(v6, a1);
  mul(v6, a1);
  mul(v6, a1);
  v2 = sub_33A9(a1);
  v7 = sub_1D21(a1, (unsigned int)(v2 + 1));
  sub(v7, 1LL);
  v5 = func2(v7);
  add(v3, a1);
  sub(v3, 4LL);
  add(v3, v4);
  add(v3, v5);
  add(v3, v6);
  free1(v4);
  free1(v5);
  free1(v6);
  free1(v7);
  return v3;
}
__int64 __fastcall func2(__int64 a1)
{
  int v2; // eax
  int v3; // eax
  int v4; // eax
  __int64 v5; // [rsp+10h] [rbp-40h]
  __int64 v6; // [rsp+18h] [rbp-38h]
  __int64 v7; // [rsp+20h] [rbp-30h]
  __int64 v8; // [rsp+28h] [rbp-28h]
  __int64 v9; // [rsp+30h] [rbp-20h]
  __int64 v10; // [rsp+38h] [rbp-18h]
  __int64 v11; // [rsp+40h] [rbp-10h]
  __int64 v12; // [rsp+48h] [rbp-8h]

  if ( (int)str2int(a1, 1) <= 0 )
    return int2big(1LL);
  v5 = int2big(0LL);
  v2 = sub_33A9(a1);
  v10 = sub_1D21(a1, (unsigned int)(v2 + 1));
  sub(v10, 1LL);
  v6 = func1(v10);
  v3 = sub_33A9(a1);
  v11 = sub_1D21(a1, (unsigned int)(v3 + 1));
  sub(v11, 2LL);
  v7 = func1(v11);
  mul1(v7, 3LL);
  v4 = sub_33A9(a1);
  v12 = sub_1D21(a1, (unsigned int)(v4 + 1));
  sub(v12, 3LL);
  v8 = func1(v12);
  mul1(v8, 5LL);
  v9 = int2big(3LL);
  mul(v9, a1);
  mul(v9, a1);
  mul(v9, a1);
  mul(v9, a1);
  add(v5, v6);
  add(v5, v7);
  sub1(v5, v8);
  add(v5, v9);
  free1(v7);
  free1(v8);
  free1(v9);
  free1(v10);
  free1(v11);
  free1(v12);
  return v5;
}

总结如下:

$$ f(x)=73x^5+8x^3+g(x-1)+x-4 $$

$$ h(x)=f(x-1)+3f(x-2)-5f(x-3)+3x^4 $$

如果按照他的做法确实计算很长时间,要想加速可以利用缓存表(算法题思路),把每个计算好的func1(x)和func2(x)存储起来,下次调用直接查表

只需要计算出f(13337)再做个模运算和加法即可得到flag

import time


def solve_f_and_h(n_target):
    """
    使用迭代和记忆化(查表)来计算 f(n) 和 h(n)
    """
    # 初始化缓存,用于存储已计算的值
    f_cache = {-1: 1, 0: 2, 1: 1}
    h_cache = {0: 1, 1: 1}

    start_time = time.time()

    for k in range(2, n_target + 1):
        # 计算 f(k),它依赖 h(k-1)
        # f(k) = 73k^5 + 8k^3 + k - 4 + h(k-1)
        poly_f = 73 * (k ** 5) + 8 * (k ** 3) + k - 4
        f_cache[k] = poly_f + h_cache[k - 1]
        # 计算 h(k),它依赖 f(k-1), f(k-2), f(k-3)
        # h(k) = f(k-1) + 3*f(k-2) - 5*f(k-3) + 3k^4
        term1 = f_cache[k - 1]
        term2 = 3 * f_cache[k - 2]
        term3 = 5 * f_cache[k - 3]
        term4 = 3 * (k ** 4)
        h_cache[k] = term1 + term2 - term3 + term4

    end_time = time.time()
    print(f"计算耗时: {end_time - start_time:.4f} 秒")

    return f_cache[n_target]


# --- 主程序 ---
n = 13337
final_result = solve_f_and_h(n)

print("-" * 50)
print(f"f({n}) 的最终计算结果是:")
print(final_result)
print("-" * 50)

a = int("12871709638832864416674237492708808074465131233250468097567609804146306910998417223517320307084142930385333755674444057095681119233485961920941215894136808839080569675919567597231")
b = int("805129649450289111374098215345043938348341847793365469885914570440914675704049341968773123354333661444680237475120349087680072042981825910641377252873686258216120616639500404381")
final_result = (a+final_result % a)%a + b
from Crypto.Util.number import long_to_bytes
print(long_to_bytes(final_result).decode())

justCTF{1n_0rd3r_70_und3r574nd_r3cur510n_y0u_h4v3_t0_und3r574nd_r3cur510n}

6pack

MiscRE!不过感觉主要还是RE,给了个go流量过滤什么的,挺难读懂,最后从流量包ipv6数据包里提取出了一个带upx壳的exe

题目要求带两个参数,第一个是flag第二个是满足v13 >> 11 == 0xF的数(可知范围为0b111100000000000~0b1111000000000000,后者不包含)

int __fastcall main(int argc, const char **argv, const char **envp)
{
  int v3; // ebx
  void *v4; // rsi
  const char *v7; // rbp
  int v8; // eax
  void *v9; // rax
  int v10; // eax
  const char *v11; // rcx
  unsigned __int16 v13; // [rsp+2Eh] [rbp-4Ah] BYREF
  SIZE_T dwSize; // [rsp+30h] [rbp-48h] BYREF
  void *Src; // [rsp+38h] [rbp-40h]
  int v16; // [rsp+40h] [rbp-38h] BYREF
  unsigned __int16 *v17; // [rsp+48h] [rbp-30h]

  sub_7FF726F41550();
  if ( argc != 3 )
  {
LABEL_9:
    VirtualFree(v4, 0LL, 0x8000u);
    goto LABEL_11;
  }
  v7 = argv[1];
  v13 = atoi(argv[2]);
  dwSize = 0LL;
  Src = 0LL;
  v3 = sub_7FF726F42730(&dwSize);
  if ( !v3 )
  {
    v16 = 2;
    v17 = &v13;
    if ( v13 >> 11 == 0xF )
    {
      v8 = sub_7FF726F426E0(&dwSize, &v16);
      if ( !v8 )
      {
        v9 = VirtualAlloc(0LL, dwSize, 0x3000u, 0x40u);
        v4 = v9;
        if ( !v9 )
          goto LABEL_11;
        memcpy(v9, Src, dwSize);
        v10 = (v4)(v7);
        v11 = "correct";
        v3 = v10;
        if ( v10 )
          v11 = "nope";
        puts(v11);
        goto LABEL_9;
      }
      v3 = v8;
    }
  }
LABEL_11:
  if ( Src )
    free(Src);
  return v3;
}

其中sub_7FF726F42730函数读取6-pack里的内容

__int64 __fastcall sub_7FF726F42730(__int64 a1)
{
  FILE *v2; // rax
  FILE *v3; // rbx
  void *v4; // rsi
  int v5; // r14d
  void *v6; // rax
  int v7; // edx
  _BYTE Buffer[40]; // [rsp+20h] [rbp-F0h] BYREF
  int v10; // [rsp+48h] [rbp-C8h]
  unsigned __int16 v11; // [rsp+5Ah] [rbp-B6h]
  unsigned __int16 v12; // [rsp+5Ch] [rbp-B4h]
  unsigned __int16 v13; // [rsp+5Eh] [rbp-B2h]
  _BYTE v14[24]; // [rsp+60h] [rbp-B0h] BYREF
  int Offset; // [rsp+78h] [rbp-98h]
  size_t Size; // [rsp+80h] [rbp-90h]
  _DWORD v17[8]; // [rsp+A0h] [rbp-70h] BYREF
  size_t ElementSize; // [rsp+C0h] [rbp-50h]

  v2 = fopen("./6-pack", "rb");
  if ( !v2 )
    return 1LL;
  v3 = v2;
  fread(Buffer, 0x40uLL, 1uLL, v2);
  if ( fseek(v3, v10 + v11 * v13, 0) < 0 )
    goto LABEL_3;
  fread(v14, 0x40uLL, 1uLL, v3);
  v4 = malloc(Size);
  if ( fseek(v3, Offset, 0) < 0 )
    goto LABEL_3;
  v5 = 0;
  fread(v4, Size, 1uLL, v3);
  while ( 1 )
  {
    if ( v12 <= v5 )
      goto LABEL_12;
    if ( fseek(v3, v10 + v5 * v11, 0) < 0 )
      goto LABEL_3;
    fread(v17, 0x40uLL, 1uLL, v3);
    if ( !strcmp(v4 + v17[0], ".go.runtimeinfo") )
      break;
    ++v5;
  }
  v6 = malloc(ElementSize);
  v7 = v17[6];
  *(a1 + 8) = v6;
  if ( fseek(v3, v7, 0) < 0 )
LABEL_3:
    exit(1);
  fread(*(a1 + 8), ElementSize, 1uLL, v3);
  *a1 = ElementSize;
LABEL_12:
  free(v4);
  fclose(v3);
  return 0LL;
}

sub_7FF726F426E0调用了advapi32.dll的SystemFunction033,查询可知是一个RC4加密

__int64 __fastcall sub_7FF726F426E0(__int64 a1, __int64 a2)
{
  HMODULE LibraryA; // rax
  FARPROC SystemFunction033; // rax

  LibraryA = LoadLibraryA("advapi32.dll");
  if ( !LibraryA )
    return 1LL;
  SystemFunction033 = GetProcAddress(LibraryA, "SystemFunction033");
  if ( !SystemFunction033 )
    return 1LL;
  (SystemFunction033)(a1, a2);
  return 0LL;
}

(v4)(v7)可以知道必须输入正确的数字才能进入正常的函数,需要爆破,思路是逐个尝试如果能返回nop说明进入v4可以正常返回,但不一定是答案还需要结合动态调试逐一确认

import threading
import subprocess
import time  # 1. 导入 time 模块

# --- 共享资源 ---
executable = "./download.exe"
arg1 = "aaaa"
stop_event = threading.Event()
found_key = None


def brute_force_worker(key_list):
    """每个工作线程执行这个函数,并增加了计时功能"""
    global found_key

    for key in key_list:
        if stop_event.is_set():
            return

        command = [executable, arg1, str(key)]

        try:
            # 2. 记录命令执行前的开始时间
            start_time = time.monotonic()

            result = subprocess.run(command, capture_output=True, text=True, check=False, timeout=10)

            # 3. 记录命令执行完毕后的结束时间
            end_time = time.monotonic()

            # 4. 计算执行耗时
            duration = end_time - start_time

            # 将标准输出和标准错误合并检查
            output = result.stdout + result.stderr

            # 在打印信息中加入耗时
            print(f"[*] 线程 {threading.current_thread().name} 尝试密钥: {key} (耗时: {duration:.4f} 秒)")

            if "nope" in output and output.strip():
                print("\n" + "=" * 40)
                print(f"[!!!] 线程 {threading.current_thread().name} 成功!找到密钥: {key} (总耗时: {duration:.4f} 秒)")
                print(f"[*] 程序输出: \n{output}")
                print("=" * 40)

        except subprocess.TimeoutExpired:
            # 如果超时,也打印一下耗时信息
            duration = time.monotonic() - start_time
            print(f"[警告] 线程 {threading.current_thread().name} 尝试密钥: {key} 超时! (已运行超过 {duration:.2f} 秒)")
            continue
        except Exception as e:
            # print(f"尝试密钥 {key} 时出错: {e}")
            continue


# --- 主线程逻辑 (保持不变) ---
if __name__ == "__main__":
    keys_to_check = list(range(30720, 32768))
    num_threads = 16

    chunk_size = len(keys_to_check) // num_threads
    if len(keys_to_check) % num_threads != 0:
        chunk_size += 1  # 确保能覆盖所有密钥
    chunks = [keys_to_check[i:i + chunk_size] for i in range(0, len(keys_to_check), chunk_size)]

    threads = []
    for i, chunk in enumerate(chunks):
        thread = threading.Thread(target=brute_force_worker, args=(chunk,), name=f"Worker-{i + 1}")
        threads.append(thread)
        print(f"启动线程 Worker-{i + 1},负责 {len(chunk)} 个密钥。")
        thread.start()

    for thread in threads:
        thread.join()

    if found_key is None:
        print("\n所有密钥尝试完毕,未找到正确密钥。")

最后确认31337是正确的,动调跑进去看,可以看到获取了输入长度要求36

debug030:0000027357E40000 mov     r15, rcx
debug030:0000027357E40003 call    loc_27357E4002D
debug030:0000027357E40008 mov     r14, rax
debug030:0000027357E4000B cmp     r14, 24h ; '$'
debug030:0000027357E4000F jz      short loc_27357E40017
debug030:0000027357E40011 mov     eax, 1
debug030:0000027357E40016 retn
debug030:0000027357E40017 ; ---------------------------------------------------------------------------
debug030:0000027357E40017
debug030:0000027357E40017 loc_27357E40017:                        ; CODE XREF: debug030:0000027357E4000F↑j
debug030:0000027357E40017 call    loc_27357E40046
debug030:0000027357E4001C call    loc_27357E4006C
debug030:0000027357E40021 mov     rcx, r15
debug030:0000027357E40024 mov     rdx, r14
debug030:0000027357E40027 call    near ptr unk_27357E40100
debug030:0000027357E4002C retn
debug030:0000027357E4002D ; ---------------------------------------------------------------------------
debug030:0000027357E4002D
debug030:0000027357E4002D loc_27357E4002D:                        ; CODE XREF: debug030:0000027357E40003↑p
debug030:0000027357E4002D mov     rdi, rcx
debug030:0000027357E40030 xor     rax, rax
debug030:0000027357E40033 mov     ecx, 100h
debug030:0000027357E40038 repne scasb
debug030:0000027357E4003A mov     eax, 100h
debug030:0000027357E4003F sub     rax, rcx
debug030:0000027357E40042 dec     rax
debug030:0000027357E40045 retn
debug030:0000027357E40046 ; ---------------------------------------------------------------------------
debug030:0000027357E40046
debug030:0000027357E40046 loc_27357E40046:                        ; CODE XREF: debug030:loc_27357E40017↑p
debug030:0000027357E40046 lea     rdi, [r15]
debug030:0000027357E40049 lea     rsi, [r15+r14-1]
debug030:0000027357E4004E mov     rcx, r14
debug030:0000027357E40051 shr     rcx, 1
debug030:0000027357E40054
debug030:0000027357E40054 loc_27357E40054:                        ; CODE XREF: debug030:0000027357E40066↓j
debug030:0000027357E40054 movzx   rax, byte ptr [rdi]
debug030:0000027357E40058 movzx   rbx, byte ptr [rsi]
debug030:0000027357E4005C mov     [rsi], al
debug030:0000027357E4005E mov     [rdi], bl
debug030:0000027357E40060 inc     rdi
debug030:0000027357E40063 dec     rsi
debug030:0000027357E40066 loop    loc_27357E40054
debug030:0000027357E40068 xor     rax, rax
debug030:0000027357E4006B retn
debug030:0000027357E4006C ; ---------------------------------------------------------------------------
debug030:0000027357E4006C
debug030:0000027357E4006C loc_27357E4006C:                        ; CODE XREF: debug030:0000027357E4001C↑p
debug030:0000027357E4006C mov     ecx, 700h
debug030:0000027357E40071 lea     rdi, unk_27357E40100
debug030:0000027357E40078
debug030:0000027357E40078 loc_27357E40078:                        ; CODE XREF: debug030:0000027357E4007D↓j
debug030:0000027357E40078 xor     byte ptr [rdi+rcx-1], 17h
debug030:0000027357E4007D loop    loc_27357E40078
debug030:0000027357E4007F retn

然后跳转loc_27357E40017,调用loc_27357E40046,然后继续调用loc_27357E4006C,该地方对unk_27357E40100做了循环异或0x17,即SMC;结束后即可进入check函数

_BOOL8 __fastcall sub_27357E40100(__int64 a1, unsigned __int64 a2)
{
  void (__fastcall *v2)(char *); // rax
  __int64 v3; // rcx
  __int64 v4; // rax
  __int64 v6; // [rsp+28h] [rbp-48h]
  _QWORD v7[4]; // [rsp+30h] [rbp-40h] BYREF
  unsigned __int64 v8; // [rsp+50h] [rbp-20h]
  __int64 v9; // [rsp+58h] [rbp-18h]
  unsigned __int64 v10; // [rsp+60h] [rbp-10h]
  __int64 v11; // [rsp+68h] [rbp-8h]

  v11 = a1;
  v10 = a2;
  v9 = 0LL;
  v8 = 0LL;
  memset(v7, 0, sizeof(v7));
  v6 = 0LL;
  v2 = (sub_27357E401F8)(NtCurrentPeb()->Ldr->InMemoryOrderModuleList.Flink->Flink->Flink[2].Flink, dword_27357E40253);
  v2(aBcryptDll);
  v8 = v10 / 3;
  do
  {
    v3 = v11;
    v11 += 3LL;
    sub_27357E4026F(v3, v7);    // sha256
    v4 = v9;
    LOBYTE(v4) = memcmp(&unk_27357E403D0 + 8 * v9, v7, 0x20uLL) == 0;
    v6 += v4;
    v9 += 4LL;
    --v8;
  }
  while ( v8 );
  return v6 != 12;
}

稍微跟进可知对每三位进行sha256并和硬编码数据比较,直接写表爆破即可

import hashlib
import time


def create_reverse_hash_map():
    """
    创建从 SHA-256 哈希值到原始3字符的反向映射表。
    我们假设字符集为所有可见的ASCII字符(从空格到~)。
    """
    print("[-] 正在创建3字符的SHA-256哈希映射表,请稍候...")
    start_time = time.time()

    # 字符集:ASCII码从32 (空格) 到 126 (~)
    possible_chars = [chr(i) for i in range(32, 127)]

    hash_to_string_map = {}
    count = 0
    total = len(possible_chars) ** 3

    # 生成所有可能的3字符组合
    for c1 in possible_chars:
        for c2 in possible_chars:
            for c3 in possible_chars:
                original_string = c1 + c2 + c3
                # 字符串需要编码成字节才能进行哈希
                encoded_string = original_string.encode('ascii')
                # 计算SHA-256哈希值并转为十六进制字符串
                sha256_hash = hashlib.sha256(encoded_string).hexdigest()

                # 存入映射表
                hash_to_string_map[sha256_hash] = original_string

                count += 1
                if count % 100000 == 0:
                    print(f"    ...已生成 {count}/{total} 个哈希", end='\r')

    end_time = time.time()
    print(f"\n[+] 映射表创建完毕,共计 {len(hash_to_string_map)} 个条目,耗时 {end_time - start_time:.2f} 秒。")
    return hash_to_string_map


def find_original_string(target_hash_concat, hash_map):
    """
    根据拼接的哈希字符串和映射表,找出原始字符串。
    """
    print("[-] 正在解析并查找原始字符串...")
    if len(target_hash_concat) % 64 != 0:
        print("[错误] 目标哈希字符串的长度不是64的倍数。")
        return None

    # 将长字符串分割成单个SHA-256哈希值的列表
    target_hashes = [target_hash_concat[i:i + 64] for i in range(0, len(target_hash_concat), 64)]

    original_parts = []
    all_found = True

    for i, target_hash in enumerate(target_hashes):
        # 在映射表中查找
        original_chunk = hash_map.get(target_hash.lower())  # 将目标哈希转为小写以匹配

        if original_chunk:
            original_parts.append(original_chunk)
        else:
            print(f"[警告] 未能找到第 {i + 1} 个哈希值 '{target_hash[:16]}...' 对应的原始字符。")
            original_parts.append("???")  # 用'???'作为占位符
            all_found = False

    if all_found:
        print("[+] 所有哈希值均已成功匹配!")

    # 拼接所有找到的部分
    return "".join(original_parts)


# --- 主程序 ---
if __name__ == "__main__":
    # 您提供的拼接起来的哈希值表
    long_hash_string = "204001DE61EE2B59F771F53FEB640F350932C5CDE699E23B27662995D4BD041673FE4A5E486AE1F4D033C595C5AAC79E6AFEAE7A28B7829C3EA97F0C7475FF30A38DF01B42FE1009F1DA3C3BF7787BEACD5CE1743E6D19F6D46D096FD0BBA90A677562C4990949FB8E8C2F8166F80D8151A4A1C0E0679AC24D454633C1709C2916DB9AE9D8A6B5DC7616FB3B0B74E287E492E7D295961A0A14B9EE935C825D59AAC513EA306B59B9AA9AF987D390E38DF0928918B25656C180C235B9AF7BE296B7CF5A8D7EB93D4D825A400FA173020A0D5CD34A8C2F4F818D01CA2BD991C215487D3CFD82C3915466513E097C6B00593D8C677CE54B2A292FDE8B14F88CCB89933130B10131093C4463F7C372BA435C091A87A9B77803853F2C5AB5A3F33CAA547D5E2A740B5D7D7C94C9B9333C8183E13A858A528C8188FDDD2FE187A07CC07C5CD81D84046CAAC335C2AFD9673729BA0FBA09FB7CB225FEDB6499BE5EFC29EE340EA3ECF77D89247E264015C071D8BBD66B30FB49479A55C9225282B19287"

    # 1. 创建反向映射表
    reverse_map = create_reverse_hash_map()

    # 2. 查找原始字符串
    original_string = find_original_string(long_hash_string, reverse_map)

    if original_string:
        print("\n" + "=" * 50)
        print("还原后的原始字符串是:")
        print(original_string[::-1])
        print("=" * 50)

justCTF{ipv6_ch4t___h0w_b4d_1s_th4t}

java

what the fxxk is this?好吧菜的根本没法做,一直在试图找工具,但实际上需要你编译一个新java版本来debug;过于超模了,pass

最后修改:2025 年 08 月 05 日
如果觉得我的文章对你有用,请随意赞赏